diff options
| author | ian <ian@138bc75d-0d04-0410-961f-82ee72b054a4> | 2012-02-01 19:26:59 +0000 |
|---|---|---|
| committer | ian <ian@138bc75d-0d04-0410-961f-82ee72b054a4> | 2012-02-01 19:26:59 +0000 |
| commit | 86babf4bfda7c2c2e9be4abc20e4d8073e05e5be (patch) | |
| tree | 7e7e6083ebe59999943a211a17f8ef6f07f17c2f /libgo/go/net/http | |
| parent | df9ff8bf53f716508a120d15cc144e628fd2e9b5 (diff) | |
| download | ppe42-gcc-86babf4bfda7c2c2e9be4abc20e4d8073e05e5be.tar.gz ppe42-gcc-86babf4bfda7c2c2e9be4abc20e4d8073e05e5be.zip | |
libgo: Update to weekly.2012-01-27.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@183810 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'libgo/go/net/http')
| -rw-r--r-- | libgo/go/net/http/client.go | 4 | ||||
| -rw-r--r-- | libgo/go/net/http/client_test.go | 12 | ||||
| -rw-r--r-- | libgo/go/net/http/readrequest_test.go | 76 | ||||
| -rw-r--r-- | libgo/go/net/http/request.go | 36 | ||||
| -rw-r--r-- | libgo/go/net/http/request_test.go | 19 | ||||
| -rw-r--r-- | libgo/go/net/http/transport.go | 18 | ||||
| -rw-r--r-- | libgo/go/net/http/transport_test.go | 56 |
7 files changed, 200 insertions, 21 deletions
diff --git a/libgo/go/net/http/client.go b/libgo/go/net/http/client.go index 1d70672695c..c9f02401757 100644 --- a/libgo/go/net/http/client.go +++ b/libgo/go/net/http/client.go @@ -116,6 +116,10 @@ func send(req *Request, t RoundTripper) (resp *Response, err error) { return nil, errors.New("http: nil Request.URL") } + if req.RequestURI != "" { + return nil, errors.New("http: Request.RequestURI can't be set in client requests.") + } + // Most the callers of send (Get, Post, et al) don't need // Headers, leaving it uninitialized. We guarantee to the // Transport that this has been initialized, though. diff --git a/libgo/go/net/http/client_test.go b/libgo/go/net/http/client_test.go index c74611011a8..aa0bf4be676 100644 --- a/libgo/go/net/http/client_test.go +++ b/libgo/go/net/http/client_test.go @@ -428,3 +428,15 @@ func TestClientInsecureTransport(t *testing.T) { } } } + +func TestClientErrorWithRequestURI(t *testing.T) { + req, _ := NewRequest("GET", "http://localhost:1234/", nil) + req.RequestURI = "/this/field/is/illegal/and/should/error/" + _, err := DefaultClient.Do(req) + if err == nil { + t.Fatalf("expected an error") + } + if !strings.Contains(err.Error(), "RequestURI") { + t.Errorf("wanted error mentioning RequestURI; got error: %v", err) + } +} diff --git a/libgo/go/net/http/readrequest_test.go b/libgo/go/net/http/readrequest_test.go index da3e4050fe1..2e03c658aa9 100644 --- a/libgo/go/net/http/readrequest_test.go +++ b/libgo/go/net/http/readrequest_test.go @@ -64,6 +64,7 @@ var reqTests = []reqTest{ Close: false, ContentLength: 7, Host: "www.techcrunch.com", + RequestURI: "http://www.techcrunch.com/", }, "abcdef\n", @@ -89,6 +90,7 @@ var reqTests = []reqTest{ Close: false, ContentLength: 0, Host: "foo.com", + RequestURI: "/", }, noBody, @@ -114,6 +116,7 @@ var reqTests = []reqTest{ Close: false, ContentLength: 0, Host: "test", + RequestURI: "//user@host/is/actually/a/path/", }, noBody, @@ -163,6 +166,7 @@ var reqTests = []reqTest{ Header: Header{}, ContentLength: -1, Host: "foo.com", + RequestURI: "/", }, "foobar", @@ -171,6 +175,78 @@ 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", + RequestURI: "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", + RequestURI: "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: "", + RequestURI: "/_goRPC_", + }, + + noBody, + noTrailer, + noError, + }, } func TestReadRequest(t *testing.T) { diff --git a/libgo/go/net/http/request.go b/libgo/go/net/http/request.go index 5a4e739073a..5f8c00086be 100644 --- a/libgo/go/net/http/request.go +++ b/libgo/go/net/http/request.go @@ -153,6 +153,12 @@ type Request struct { // This field is ignored by the HTTP client. RemoteAddr string + // RequestURI is the unmodified Request-URI of the + // Request-Line (RFC 2616, Section 5.1) as sent by the client + // to a server. Usually the URL field should be used instead. + // It is an error to set this field in an HTTP client request. + RequestURI string + // TLS allows HTTP servers and other software to record // information about the TLS connection on which the request // was received. This field is not filled in by ReadRequest. @@ -305,6 +311,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? @@ -456,17 +465,36 @@ func ReadRequest(b *bufio.Reader) (req *Request, err error) { if f = strings.SplitN(s, " ", 3); len(f) < 3 { return nil, &badStringError{"malformed HTTP request", s} } - var rawurl string - req.Method, rawurl, req.Proto = f[0], f[1], f[2] + req.Method, req.RequestURI, req.Proto = f[0], f[1], f[2] + rawurl := req.RequestURI var ok bool if req.ProtoMajor, req.ProtoMinor, ok = ParseHTTPVersion(req.Proto); !ok { 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 { @@ -584,7 +612,7 @@ func (r *Request) ParseForm() (err error) { return errors.New("missing form body") } ct := r.Header.Get("Content-Type") - ct, _, err := mime.ParseMediaType(ct) + ct, _, err = mime.ParseMediaType(ct) switch { case ct == "application/x-www-form-urlencoded": var reader io.Reader = r.Body @@ -624,8 +652,6 @@ func (r *Request) ParseForm() (err error) { // Clean this up and write more tests. // request_test.go contains the start of this, // in TestRequestMultipartCallOrder. - default: - return &badStringError{"unknown Content-Type", ct} } } return err diff --git a/libgo/go/net/http/request_test.go b/libgo/go/net/http/request_test.go index 7b78645169e..7a3556d0367 100644 --- a/libgo/go/net/http/request_test.go +++ b/libgo/go/net/http/request_test.go @@ -46,19 +46,19 @@ func TestPostQuery(t *testing.T) { type stringMap map[string][]string type parseContentTypeTest struct { + shouldError bool contentType stringMap } var parseContentTypeTests = []parseContentTypeTest{ - {contentType: stringMap{"Content-Type": {"text/plain"}}}, - {contentType: stringMap{}}, // Non-existent keys are not placed. The value nil is illegal. - {contentType: stringMap{"Content-Type": {"text/plain; boundary="}}}, - { - contentType: stringMap{"Content-Type": {"application/unknown"}}, - }, + {false, stringMap{"Content-Type": {"text/plain"}}}, + // Non-existent keys are not placed. The value nil is illegal. + {true, stringMap{}}, + {true, stringMap{"Content-Type": {"text/plain; boundary="}}}, + {false, stringMap{"Content-Type": {"application/unknown"}}}, } -func TestParseFormBadContentType(t *testing.T) { +func TestParseFormUnknownContentType(t *testing.T) { for i, test := range parseContentTypeTests { req := &Request{ Method: "POST", @@ -66,8 +66,11 @@ func TestParseFormBadContentType(t *testing.T) { Body: ioutil.NopCloser(bytes.NewBufferString("body")), } err := req.ParseForm() - if err == nil { + switch { + case err == nil && test.shouldError: t.Errorf("test %d should have returned error", i) + case err != nil && !test.shouldError: + t.Errorf("test %d should not have returned error, got %v", i, err) } } } diff --git a/libgo/go/net/http/transport.go b/libgo/go/net/http/transport.go index 1b9ad1b85c5..4de070f01f1 100644 --- a/libgo/go/net/http/transport.go +++ b/libgo/go/net/http/transport.go @@ -494,12 +494,6 @@ func (pc *persistConn) isBroken() bool { return pc.broken } -func (pc *persistConn) expectingResponse() bool { - pc.lk.Lock() - defer pc.lk.Unlock() - return pc.numExpectedResponses > 0 -} - var remoteSideClosedFunc func(error) bool // or nil to use default func remoteSideClosed(err error) bool { @@ -518,14 +512,18 @@ func (pc *persistConn) readLoop() { for alive { pb, err := pc.br.Peek(1) - if !pc.expectingResponse() { + + pc.lk.Lock() + if pc.numExpectedResponses == 0 { + pc.closeLocked() + pc.lk.Unlock() if len(pb) > 0 { log.Printf("Unsolicited response received on idle HTTP channel starting with %q; err=%v", string(pb), err) } - pc.close() return } + pc.lk.Unlock() rc := <-pc.reqch @@ -649,6 +647,10 @@ func (pc *persistConn) roundTrip(req *transportRequest) (resp *Response, err err func (pc *persistConn) close() { pc.lk.Lock() defer pc.lk.Unlock() + pc.closeLocked() +} + +func (pc *persistConn) closeLocked() { pc.broken = true pc.conn.Close() pc.mutateHeaderFunc = nil diff --git a/libgo/go/net/http/transport_test.go b/libgo/go/net/http/transport_test.go index ff12fa2d014..321da52e278 100644 --- a/libgo/go/net/http/transport_test.go +++ b/libgo/go/net/http/transport_test.go @@ -304,6 +304,62 @@ func TestTransportServerClosingUnexpectedly(t *testing.T) { } } +// Test for http://golang.org/issue/2616 (appropriate issue number) +// This fails pretty reliably with GOMAXPROCS=100 or something high. +func TestStressSurpriseServerCloses(t *testing.T) { + if testing.Short() { + t.Logf("skipping test in short mode") + return + } + ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { + w.Header().Set("Content-Length", "5") + w.Header().Set("Content-Type", "text/plain") + w.Write([]byte("Hello")) + w.(Flusher).Flush() + conn, buf, _ := w.(Hijacker).Hijack() + buf.Flush() + conn.Close() + })) + defer ts.Close() + + tr := &Transport{DisableKeepAlives: false} + c := &Client{Transport: tr} + + // Do a bunch of traffic from different goroutines. Send to activityc + // after each request completes, regardless of whether it failed. + const ( + numClients = 50 + reqsPerClient = 250 + ) + activityc := make(chan bool) + for i := 0; i < numClients; i++ { + go func() { + for i := 0; i < reqsPerClient; i++ { + res, err := c.Get(ts.URL) + if err == nil { + // We expect errors since the server is + // hanging up on us after telling us to + // send more requests, so we don't + // actually care what the error is. + // But we want to close the body in cases + // where we won the race. + res.Body.Close() + } + activityc <- true + } + }() + } + + // Make sure all the request come back, one way or another. + for i := 0; i < numClients*reqsPerClient; i++ { + select { + case <-activityc: + case <-time.After(5 * time.Second): + t.Fatalf("presumed deadlock; no HTTP client activity seen in awhile") + } + } +} + // TestTransportHeadResponses verifies that we deal with Content-Lengths // with no bodies properly func TestTransportHeadResponses(t *testing.T) { |

