diff --git a/go.mod b/go.mod index e3bf4c4..7295709 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,7 @@ module git.3crabs.ru/VLADIMIR/net go 1.20 -require github.com/stretchr/testify v1.8.4 +require github.com/stretchr/testify v1.11.1 require ( github.com/davecgh/go-spew v1.1.1 // indirect diff --git a/go.sum b/go.sum index 8cf6655..cc8b3f4 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/http/client.go b/http/client.go index 3d52324..59cd8a2 100644 --- a/http/client.go +++ b/http/client.go @@ -1,12 +1,7 @@ package http import ( - "strings" -) - -var ( - CRLF = []byte("\r\n") - SP = []byte(" ") + "git.3crabs.ru/VLADIMIR/net/url" ) type Client struct { @@ -14,22 +9,16 @@ type Client struct { Transport RoundTripper } -func (c *Client) Do(method string, url string, headers []Header) (*Response, error) { - url = strings.TrimPrefix(url, "http://") - url = strings.TrimPrefix(url, "https://") - path := "/" - arr := strings.Split(url, "/") - if len(arr) == 2 { - path = arr[1] - } - if !strings.HasPrefix(path, "/") { - path = "/" + path +func (c *Client) Do(method string, rawURl string, headers []Header) (*Response, error) { + u, err := url.Parse(rawURl) + if err != nil { + return nil, err } connectPath := "" if c.DNS != nil { var err error - connectPath, err = c.DNS.GetIP(arr[0]) + connectPath, err = c.DNS.GetIP(u.Host) if err != nil { return nil, err } @@ -39,7 +28,7 @@ func (c *Client) Do(method string, url string, headers []Header) (*Response, err &Request{ ConnectPath: connectPath, Method: method, - Path: path, + URL: u, Protocol: "HTTP/1.0", Headers: headers, }, diff --git a/http/header.go b/http/header.go deleted file mode 100644 index e4928ba..0000000 --- a/http/header.go +++ /dev/null @@ -1,6 +0,0 @@ -package http - -type Header struct { - Name string - Value string -} diff --git a/http/models.go b/http/models.go new file mode 100644 index 0000000..f2d202d --- /dev/null +++ b/http/models.go @@ -0,0 +1,30 @@ +package http + +import "git.3crabs.ru/VLADIMIR/net/url" + +type Request struct { + ConnectPath string // ip + + Method string + URL *url.URL + Protocol string + + Headers []Header + + Body []byte +} + +type Response struct { + Protocol string + StatusCode string + StatusMessage string + + Headers []Header + + Body []byte +} + +type Header struct { + Name string + Value string +} diff --git a/http/request.go b/http/request.go deleted file mode 100644 index 07dd303..0000000 --- a/http/request.go +++ /dev/null @@ -1,13 +0,0 @@ -package http - -type Request struct { - ConnectPath string // ip - - Method string - Path string - Protocol string - - Headers []Header - - Body string -} diff --git a/http/response.go b/http/response.go deleted file mode 100644 index 8db8786..0000000 --- a/http/response.go +++ /dev/null @@ -1,24 +0,0 @@ -package http - -import ( - "errors" -) - -var ( - ErrHeaderNotFound = errors.New("header not found") -) - -type Response struct { - StatusCode int - Headers []Header - Body []byte -} - -func (r *Response) GetHeader(name string) (Header, error) { - for _, h := range r.Headers { - if h.Name == name { - return h, nil - } - } - return Header{}, ErrHeaderNotFound -} diff --git a/http/transport.go b/http/transport.go index 8ea5526..33847d7 100644 --- a/http/transport.go +++ b/http/transport.go @@ -3,6 +3,7 @@ package http import ( "bufio" "bytes" + "crypto/tls" "errors" "fmt" "io" @@ -14,19 +15,28 @@ import ( 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) + 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() - debugWrap(conn, req, writeRequest) - return readResponse(conn) + 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.Path, req.Protocol) +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) @@ -38,7 +48,7 @@ func writeRequest(w io.Writer, req *Request) { } } -func debugWrap(w io.Writer, req *Request, f func(w io.Writer, req *Request)) { +func WriteRequestDebugWrap(w io.Writer, req *Request, f func(w io.Writer, req *Request)) { buffer := &bytes.Buffer{} f(buffer, req) @@ -50,28 +60,31 @@ func debugWrap(w io.Writer, req *Request, f func(w io.Writer, req *Request)) { w.Write(buffer.Bytes()) } -func readResponse(r io.Reader) (*Response, error) { +func ReadResponse(r io.Reader) (*Response, error) { b := bufio.NewReader(r) - if _, err := b.ReadString(' '); err != nil { - return nil, errors.New("read httpVersion") + protocol, err := b.ReadString(' ') + if err != nil { + return nil, errors.New("read protocol") } - statusCodeStr, err := b.ReadString(' ') + protocol = protocol[:len(protocol)-1] + + statusCode, 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") + 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") } - statusCode, err := strconv.Atoi(statusCodeStr[:len(statusCodeStr)-1]) - if err != nil { - return nil, errors.New("read statusCode") - } - headerStr := "" contentLengthStr := "" headers := []Header{} @@ -108,8 +121,27 @@ func readResponse(r io.Reader) (*Response, error) { } return &Response{ - StatusCode: statusCode, - Headers: headers, - Body: body, + 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 +} diff --git a/http/transport_test.go b/http/transport_test.go index 6008fc7..cd39e0f 100644 --- a/http/transport_test.go +++ b/http/transport_test.go @@ -5,17 +5,20 @@ import ( "strings" "testing" + "git.3crabs.ru/VLADIMIR/net/url" "github.com/stretchr/testify/assert" ) func Test_WriteRequest(t *testing.T) { b := &strings.Builder{} - writeRequest( + WriteRequest( b, &Request{ - Method: "GET", - Path: "/", + Method: "GET", + URL: &url.URL{ + Path: "/", + }, Protocol: "HTTP/1.0", }, ) @@ -28,10 +31,10 @@ func Test_ReadResponse(t *testing.T) { []byte("HTTP/1.1 200 OK\r\nDate: Sat, 03 Feb 2024 16:40:29 GMT\r\nContent-Length: 12\r\nContent-Type: text/plain; charset=utf-8\r\n\r\nHello World!"), ) - r, err := readResponse(b) + r, err := ReadResponse(b) assert.Nil(t, err) - assert.Equal(t, 200, r.StatusCode) + assert.Equal(t, "200", r.StatusCode) assert.Equal(t, "Date", r.Headers[0].Name) assert.Equal(t, "Sat, 03 Feb 2024 16:40:29 GMT", r.Headers[0].Value) diff --git a/test_client/main.go b/test_client/main.go index c77f163..c414e48 100644 --- a/test_client/main.go +++ b/test_client/main.go @@ -2,7 +2,6 @@ package main import ( "errors" - "fmt" "git.3crabs.ru/VLADIMIR/net/http" ) @@ -21,9 +20,9 @@ func main() { DNS: &CustomDNS{}, Transport: &http.HttpTransport{}, } - r, err := client.Do( + _, err := client.Do( "GET", - "http://test.ru", + "http://test.ru/", []http.Header{ {Name: "Host", Value: "3crabs.ru"}, {Name: "User-Agent", Value: "3crabs/0.0.1"}, @@ -33,9 +32,4 @@ func main() { if err != nil { panic(err) } - fmt.Println(r.StatusCode) - for _, header := range r.Headers { - fmt.Println(header) - } - fmt.Println(string(r.Body)) } diff --git a/url/examples/main.go b/url/examples/main.go new file mode 100644 index 0000000..4aa9c33 --- /dev/null +++ b/url/examples/main.go @@ -0,0 +1,15 @@ +package main + +import ( + "fmt" + + "git.3crabs.ru/VLADIMIR/net/url" +) + +func main() { + u, err := url.Parse("http://test.ru/") + if err != nil { + panic(err) + } + fmt.Printf("%+v", u) +} diff --git a/url/url.go b/url/url.go new file mode 100644 index 0000000..7b31ac8 --- /dev/null +++ b/url/url.go @@ -0,0 +1,31 @@ +package url + +type URL struct { + Scheme string + Host string + Path string +} + +func Parse(rawURL string) (*URL, error) { + schemeIndex := -1 + hostStartIndex := -1 + hostEndIndex := -1 + for i := 0; i < len(rawURL); i++ { + if schemeIndex == -1 && rawURL[i] == ':' { + schemeIndex = i + hostStartIndex = i + 3 + i += 3 + continue + } + if rawURL[i] == '/' { + hostEndIndex = i + break + } + } + + return &URL{ + Scheme: rawURL[:schemeIndex], + Host: rawURL[hostStartIndex:hostEndIndex], + Path: rawURL[hostEndIndex:], + }, nil +}