package http import ( "bufio" "bytes" "errors" "fmt" "io" "net" "strconv" "strings" ) type HttpTransport struct{} func (t *HttpTransport) RoundTrip(req *Request) (*Response, error) { conn, err := net.Dial("tcp", req.ConnectPath) // conn, err := tls.Dial("tcp", connectPath, nil) if err != nil { return nil, errors.New("connect " + req.ConnectPath) } defer conn.Close() debugWrap(conn, req, writeRequest) return readResponse(conn) } func writeRequest(w io.Writer, req *Request) { fmt.Fprintf(w, "%s %s %s\r\n", req.Method, req.Path, req.Protocol) for _, header := range req.Headers { fmt.Fprintf(w, "%s: %s\r\n", header.Name, header.Value) } fmt.Fprintf(w, "\r\n") if len(req.Body) > 0 { fmt.Fprintf(w, "%s\r\n", req.Body) } } func debugWrap(w io.Writer, req *Request, f func(w io.Writer, req *Request)) { buffer := &bytes.Buffer{} f(buffer, req) fmt.Println("----- Debug Info -----") fmt.Println(buffer.String()) fmt.Println("----- Debug End Info -----") w.Write(buffer.Bytes()) } func readResponse(r io.Reader) (*Response, error) { b := bufio.NewReader(r) if _, err := b.ReadString(' '); err != nil { return nil, errors.New("read httpVersion") } statusCodeStr, err := b.ReadString(' ') if err != nil { return nil, errors.New("read statusCode") } if _, err := b.ReadString('\r'); err != nil { return nil, errors.New("read statusName") } if _, err := b.ReadString('\n'); err != nil { return nil, errors.New("read LF") } statusCode, err := strconv.Atoi(statusCodeStr[:len(statusCodeStr)-1]) if err != nil { return nil, errors.New("read statusCode") } headerStr := "" contentLengthStr := "" headers := []Header{} for { headerStr, err = b.ReadString('\n') if err != nil { return nil, errors.New("read header") } if headerStr == "\r\n" { break } arr := strings.Split(headerStr, ": ") value := strings.TrimSpace(arr[1]) header := Header{Name: arr[0], Value: value} if header.Name == "Content-Length" { contentLengthStr = header.Value } headers = append(headers, header) } length, err := strconv.Atoi(contentLengthStr) if err != nil { return nil, errors.New("read Content-Length") } body := make([]byte, 0, length) for i := 0; i < length; i++ { r, err := b.ReadByte() if err != nil { return nil, errors.New("read body") } body = append(body, r) } return &Response{ StatusCode: statusCode, Headers: headers, Body: body, }, nil }