package http import ( "bufio" "errors" "fmt" "io" "net" "strconv" "strings" ) func Do(method string, url string) (*Response, error) { path := "/" arr := strings.Split(url, "/") if len(arr) == 2 { path = arr[1] } if !strings.HasPrefix(path, "/") { path = "/" + path } conn, err := net.Dial("tcp", arr[0]) if err != nil { return nil, errors.New("connect " + arr[0]) } defer conn.Close() WriteRequest(conn, method, path, "HTTP/1.0") return ReadResponse(conn) } func WriteRequest(w io.Writer, method string, path string, httpVersion string) { fmt.Fprintf(w, "%s %s %s\r\n\r\n", method, path, httpVersion) } func ReadResponse(r io.Reader) (*Response, error) { response := &Response{} b := bufio.NewReader(r) status, err := b.ReadString('\n') if err != nil { return response, errors.New("read status") } arr := strings.Split(status, " ") response.StatusCode, err = strconv.Atoi(arr[1]) if err != nil { return response, errors.New("read status code") } headerStr := "" for { headerStr, err = b.ReadString('\n') if err != nil { return response, errors.New("read header") } if headerStr == "\r\n" { break } arr := strings.Split(headerStr, ": ") value := strings.TrimSpace(arr[1]) response.Headers = append(response.Headers, &Header{Name: arr[0], Value: value}) } contentTypeHeader, err := response.GetHeader("Content-Length") if err != nil { return response, errors.New("get Content-Length") } length, err := strconv.Atoi(contentTypeHeader.Value) if err != nil { return response, errors.New("read Content-Length") } body := make([]byte, 0, length) for i := 0; i < length; i++ { r, err := b.ReadByte() if err != nil { return response, errors.New("read body") } body = append(body, r) } response.Body = body return response, nil }