]> Cypherpunks repositories - gostls13.git/commitdiff
net/http: parse CONNECT requests
authorAndrew Balholm <andybalholm@gmail.com>
Wed, 25 Jan 2012 00:42:00 +0000 (11:42 +1100)
committerDavid Symonds <dsymonds@golang.org>
Wed, 25 Jan 2012 00:42:00 +0000 (11:42 +1100)
Fixes #2755

R=dsymonds, rsc
CC=golang-dev
https://golang.org/cl/5571052

src/pkg/net/http/readrequest_test.go
src/pkg/net/http/request.go

index da3e4050fe11368b830df4b70bc62506507e2259..df2f5aba99117782f1e06fb64d7e345ee8646438 100644 (file)
@@ -171,6 +171,75 @@ var reqTests = []reqTest{
                },
                noError,
        },
+
+       // CONNECT request with domain name:
+       {
+               "CONNECT www.google.com:443 HTTP/1.1\r\n\r\n",
+
+               &Request{
+                       Method: "CONNECT",
+                       URL: &url.URL{
+                               Host: "www.google.com:443",
+                       },
+                       Proto:         "HTTP/1.1",
+                       ProtoMajor:    1,
+                       ProtoMinor:    1,
+                       Header:        Header{},
+                       Close:         false,
+                       ContentLength: 0,
+                       Host:          "www.google.com:443",
+               },
+
+               noBody,
+               noTrailer,
+               noError,
+       },
+
+       // CONNECT request with IP address:
+       {
+               "CONNECT 127.0.0.1:6060 HTTP/1.1\r\n\r\n",
+
+               &Request{
+                       Method: "CONNECT",
+                       URL: &url.URL{
+                               Host: "127.0.0.1:6060",
+                       },
+                       Proto:         "HTTP/1.1",
+                       ProtoMajor:    1,
+                       ProtoMinor:    1,
+                       Header:        Header{},
+                       Close:         false,
+                       ContentLength: 0,
+                       Host:          "127.0.0.1:6060",
+               },
+
+               noBody,
+               noTrailer,
+               noError,
+       },
+
+       // CONNECT request for RPC:
+       {
+               "CONNECT /_goRPC_ HTTP/1.1\r\n\r\n",
+
+               &Request{
+                       Method: "CONNECT",
+                       URL: &url.URL{
+                               Path: "/_goRPC_",
+                       },
+                       Proto:         "HTTP/1.1",
+                       ProtoMajor:    1,
+                       ProtoMinor:    1,
+                       Header:        Header{},
+                       Close:         false,
+                       ContentLength: 0,
+                       Host:          "",
+               },
+
+               noBody,
+               noTrailer,
+               noError,
+       },
 }
 
 func TestReadRequest(t *testing.T) {
index 5a4e739073ac9278ef481c11d77bc6a37ddad9a6..554ad26b2c58aea8845a691ee12fbc8bc9aa6255 100644 (file)
@@ -305,6 +305,9 @@ func (req *Request) write(w io.Writer, usingProxy bool, extraHeaders Header) err
        ruri := req.URL.RequestURI()
        if usingProxy && req.URL.Scheme != "" && req.URL.Opaque == "" {
                ruri = req.URL.Scheme + "://" + host + ruri
+       } else if req.Method == "CONNECT" && req.URL.Path == "" {
+               // CONNECT requests normally give just the host and port, not a full URL.
+               ruri = host
        }
        // TODO(bradfitz): escape at least newlines in ruri?
 
@@ -463,10 +466,29 @@ func ReadRequest(b *bufio.Reader) (req *Request, err error) {
                return nil, &badStringError{"malformed HTTP version", req.Proto}
        }
 
+       // CONNECT requests are used two different ways, and neither uses a full URL:
+       // The standard use is to tunnel HTTPS through an HTTP proxy.
+       // It looks like "CONNECT www.google.com:443 HTTP/1.1", and the parameter is
+       // just the authority section of a URL. This information should go in req.URL.Host.
+       //
+       // The net/rpc package also uses CONNECT, but there the parameter is a path
+       // that starts with a slash. It can be parsed with the regular URL parser,
+       // and the path will end up in req.URL.Path, where it needs to be in order for
+       // RPC to work.
+       justAuthority := req.Method == "CONNECT" && !strings.HasPrefix(rawurl, "/")
+       if justAuthority {
+               rawurl = "http://" + rawurl
+       }
+
        if req.URL, err = url.ParseRequest(rawurl); err != nil {
                return nil, err
        }
 
+       if justAuthority {
+               // Strip the bogus "http://" back off.
+               req.URL.Scheme = ""
+       }
+
        // Subsequent lines: Key: value.
        mimeHeader, err := tp.ReadMIMEHeader()
        if err != nil {