116 lines
		
	
	
		
			2.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			116 lines
		
	
	
		
			2.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
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
 | 
						|
}
 |