package http import ( "bufio" "bytes" "crypto/tls" "errors" "fmt" "io" "net" "strconv" "strings" ) type HttpTransport struct{} func (t *HttpTransport) RoundTrip(req *Request) (*Response, error) { var err error var conn net.Conn switch req.URL.Scheme { case "http": conn, err = net.Dial("tcp", req.ConnectPath) case "https": conn, err = tls.Dial("tcp", req.ConnectPath, nil) default: panic("scheme not support") } if err != nil { return nil, errors.New("connect " + req.ConnectPath) } defer conn.Close() WriteRequestDebugWrap(conn, req, WriteRequest) return ReadResponseDebugWrap(conn, ReadResponse) } func WriteRequest(w io.Writer, req *Request) { fmt.Fprintf(w, "%s %s %s\r\n", req.Method, req.URL.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 WriteRequestDebugWrap(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) protocol, err := b.ReadString(' ') if err != nil { return nil, errors.New("read protocol") } protocol = protocol[:len(protocol)-1] statusCode, err := b.ReadString(' ') if err != nil { return nil, errors.New("read statusCode") } statusCode = statusCode[:len(statusCode)-1] statusMessage, err := b.ReadString('\r') if err != nil { return nil, errors.New("read statusMessage") } statusMessage = statusMessage[:len(statusMessage)-1] if _, err := b.ReadString('\n'); err != nil { return nil, errors.New("read LF") } 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{ Protocol: protocol, StatusCode: statusCode, StatusMessage: statusMessage, Headers: headers, Body: body, }, nil } func ReadResponseDebugWrap(r io.Reader, f func(r io.Reader) (*Response, error)) (*Response, error) { resp, err := f(r) if err != nil { return nil, err } fmt.Println("----- Debug Info -----") fmt.Printf("%s %s %s\n", resp.Protocol, resp.StatusCode, resp.StatusMessage) for _, header := range resp.Headers { fmt.Printf("%s: %s\n", header.Name, header.Value) } if len(resp.Body) > 0 { fmt.Println(string(resp.Body)) } fmt.Println("----- Debug End Info -----") return resp, nil }