diff --git a/proxy/http/request.go b/proxy/http/request.go index 84f3fe4..6a6b75a 100644 --- a/proxy/http/request.go +++ b/proxy/http/request.go @@ -4,6 +4,7 @@ import ( "bufio" "bytes" "fmt" + "net" "net/textproto" "net/url" "strings" @@ -78,24 +79,19 @@ func parseRequest(r *bufio.Reader) (*request, error) { uri = uri[7:] } - tgt := u.Host - if !strings.Contains(tgt, ":") { - tgt += ":80" - } - req := &request{ method: method, uri: uri, proto: proto, auth: auth, header: header, - target: tgt, } if u.IsAbs() { req.absuri = u.String() - u.Scheme, u.Host = "", "" - req.ruri = u.String() + relative := *u + relative.Scheme, relative.Host = "", "" + req.ruri = relative.String() } else { req.ruri = u.String() base, err := url.Parse("http://" + header.Get("Host")) @@ -106,6 +102,12 @@ func parseRequest(r *bufio.Reader) (*request, error) { req.absuri = u.String() } + port := u.Port() + if port == "" { + port = "80" + } + req.target = net.JoinHostPort(u.Hostname(), port) + return req, nil } diff --git a/proxy/http/request_test.go b/proxy/http/request_test.go new file mode 100644 index 0000000..fc5e923 --- /dev/null +++ b/proxy/http/request_test.go @@ -0,0 +1,54 @@ +package http + +import ( + "bufio" + "strings" + "testing" +) + +func TestParseRequestTarget(t *testing.T) { + for _, tt := range []struct { + name string + uri string + host string + target string + }{ + {"absolute hostname", "http://example.com/path", "other.example", "example.com:80"}, + {"absolute hostname port", "http://example.com:8080/path", "other.example", "example.com:8080"}, + {"absolute IPv6", "http://[2001:db8::1]/path", "other.example", "[2001:db8::1]:80"}, + {"absolute IPv6 port", "http://[2001:db8::1]:8080/path", "other.example", "[2001:db8::1]:8080"}, + {"origin hostname", "/path", "example.com", "example.com:80"}, + {"origin hostname port", "/path", "example.com:8080", "example.com:8080"}, + {"origin IPv6", "/path", "[2001:db8::1]", "[2001:db8::1]:80"}, + {"origin IPv6 port", "/path", "[2001:db8::1]:8080", "[2001:db8::1]:8080"}, + } { + t.Run(tt.name, func(t *testing.T) { + raw := "GET " + tt.uri + " HTTP/1.1\r\nHost: " + tt.host + "\r\n\r\n" + req, err := parseRequest(bufio.NewReader(strings.NewReader(raw))) + if err != nil { + t.Fatal(err) + } + if req.target != tt.target { + t.Errorf("target = %q, want %q", req.target, tt.target) + } + if req.ruri != "/path" { + t.Errorf("relative URI = %q, want /path", req.ruri) + } + }) + } +} + +func TestParseConnectAuthority(t *testing.T) { + for _, authority := range []string{"example.com:443", "[2001:db8::1]:8443"} { + t.Run(authority, func(t *testing.T) { + raw := "CONNECT " + authority + " HTTP/1.1\r\nHost: other.example:8080\r\n\r\n" + req, err := parseRequest(bufio.NewReader(strings.NewReader(raw))) + if err != nil { + t.Fatal(err) + } + if req.uri != authority || req.target != authority { + t.Errorf("CONNECT uri=%q target=%q, want %q", req.uri, req.target, authority) + } + }) + } +}