From: Andrew Balholm Date: Wed, 25 Jan 2012 00:42:00 +0000 (+1100) Subject: net/http: parse CONNECT requests X-Git-Tag: weekly.2012-01-27~61 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=c3b9650caa7715c8961dcb5d7503b90b6dbae7cb;p=gostls13.git net/http: parse CONNECT requests Fixes #2755 R=dsymonds, rsc CC=golang-dev https://golang.org/cl/5571052 --- diff --git a/src/pkg/net/http/readrequest_test.go b/src/pkg/net/http/readrequest_test.go index da3e4050fe..df2f5aba99 100644 --- a/src/pkg/net/http/readrequest_test.go +++ b/src/pkg/net/http/readrequest_test.go @@ -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) { diff --git a/src/pkg/net/http/request.go b/src/pkg/net/http/request.go index 5a4e739073..554ad26b2c 100644 --- a/src/pkg/net/http/request.go +++ b/src/pkg/net/http/request.go @@ -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 {