summaryrefslogtreecommitdiffstats
path: root/libgo/go/net/http
diff options
context:
space:
mode:
authorian <ian@138bc75d-0d04-0410-961f-82ee72b054a4>2012-02-01 19:26:59 +0000
committerian <ian@138bc75d-0d04-0410-961f-82ee72b054a4>2012-02-01 19:26:59 +0000
commit86babf4bfda7c2c2e9be4abc20e4d8073e05e5be (patch)
tree7e7e6083ebe59999943a211a17f8ef6f07f17c2f /libgo/go/net/http
parentdf9ff8bf53f716508a120d15cc144e628fd2e9b5 (diff)
downloadppe42-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.go4
-rw-r--r--libgo/go/net/http/client_test.go12
-rw-r--r--libgo/go/net/http/readrequest_test.go76
-rw-r--r--libgo/go/net/http/request.go36
-rw-r--r--libgo/go/net/http/request_test.go19
-rw-r--r--libgo/go/net/http/transport.go18
-rw-r--r--libgo/go/net/http/transport_test.go56
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) {
OpenPOWER on IntegriCloud