diff options
Diffstat (limited to 'libgo/go/net/http/serve_test.go')
-rw-r--r-- | libgo/go/net/http/serve_test.go | 950 |
1 files changed, 803 insertions, 147 deletions
diff --git a/libgo/go/net/http/serve_test.go b/libgo/go/net/http/serve_test.go index d51417e..f8cad80 100644 --- a/libgo/go/net/http/serve_test.go +++ b/libgo/go/net/http/serve_test.go @@ -12,6 +12,7 @@ import ( "crypto/tls" "errors" "fmt" + "internal/testenv" "io" "io/ioutil" "log" @@ -26,6 +27,8 @@ import ( "os/exec" "reflect" "runtime" + "runtime/debug" + "sort" "strconv" "strings" "sync" @@ -96,6 +99,7 @@ func (c *rwTestConn) Close() error { } type testConn struct { + readMu sync.Mutex // for TestHandlerBodyClose readBuf bytes.Buffer writeBuf bytes.Buffer closec chan bool // if non-nil, send value to it on close @@ -103,6 +107,8 @@ type testConn struct { } func (c *testConn) Read(b []byte) (int, error) { + c.readMu.Lock() + defer c.readMu.Unlock() return c.readBuf.Read(b) } @@ -450,6 +456,7 @@ func TestServerTimeouts(t *testing.T) { if runtime.GOOS == "plan9" { t.Skip("skipping test; see https://golang.org/issue/7237") } + setParallel(t) defer afterTest(t) reqNum := 0 ts := httptest.NewUnstartedServer(HandlerFunc(func(res ResponseWriter, req *Request) { @@ -734,14 +741,17 @@ func TestHandlersCanSetConnectionClose10(t *testing.T) { })) } -func TestSetsRemoteAddr(t *testing.T) { +func TestSetsRemoteAddr_h1(t *testing.T) { testSetsRemoteAddr(t, h1Mode) } +func TestSetsRemoteAddr_h2(t *testing.T) { testSetsRemoteAddr(t, h2Mode) } + +func testSetsRemoteAddr(t *testing.T, h2 bool) { defer afterTest(t) - ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { + cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) { fmt.Fprintf(w, "%s", r.RemoteAddr) })) - defer ts.Close() + defer cst.close() - res, err := Get(ts.URL) + res, err := cst.c.Get(cst.ts.URL) if err != nil { t.Fatalf("Get error: %v", err) } @@ -755,34 +765,106 @@ func TestSetsRemoteAddr(t *testing.T) { } } -func TestChunkedResponseHeaders(t *testing.T) { - defer afterTest(t) - log.SetOutput(ioutil.Discard) // is noisy otherwise - defer log.SetOutput(os.Stderr) +type blockingRemoteAddrListener struct { + net.Listener + conns chan<- net.Conn +} - ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { - w.Header().Set("Content-Length", "intentional gibberish") // we check that this is deleted - w.(Flusher).Flush() - fmt.Fprintf(w, "I am a chunked response.") +func (l *blockingRemoteAddrListener) Accept() (net.Conn, error) { + c, err := l.Listener.Accept() + if err != nil { + return nil, err + } + brac := &blockingRemoteAddrConn{ + Conn: c, + addrs: make(chan net.Addr, 1), + } + l.conns <- brac + return brac, nil +} + +type blockingRemoteAddrConn struct { + net.Conn + addrs chan net.Addr +} + +func (c *blockingRemoteAddrConn) RemoteAddr() net.Addr { + return <-c.addrs +} + +// Issue 12943 +func TestServerAllowsBlockingRemoteAddr(t *testing.T) { + defer afterTest(t) + ts := httptest.NewUnstartedServer(HandlerFunc(func(w ResponseWriter, r *Request) { + fmt.Fprintf(w, "RA:%s", r.RemoteAddr) })) + conns := make(chan net.Conn) + ts.Listener = &blockingRemoteAddrListener{ + Listener: ts.Listener, + conns: conns, + } + ts.Start() defer ts.Close() - res, err := Get(ts.URL) - if err != nil { - t.Fatalf("Get error: %v", err) + tr := &Transport{DisableKeepAlives: true} + defer tr.CloseIdleConnections() + c := &Client{Transport: tr, Timeout: time.Second} + + fetch := func(response chan string) { + resp, err := c.Get(ts.URL) + if err != nil { + t.Error(err) + response <- "" + return + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + t.Error(err) + response <- "" + return + } + response <- string(body) } - defer res.Body.Close() - if g, e := res.ContentLength, int64(-1); g != e { - t.Errorf("expected ContentLength of %d; got %d", e, g) + + // Start a request. The server will block on getting conn.RemoteAddr. + response1c := make(chan string, 1) + go fetch(response1c) + + // Wait for the server to accept it; grab the connection. + conn1 := <-conns + + // Start another request and grab its connection + response2c := make(chan string, 1) + go fetch(response2c) + var conn2 net.Conn + + select { + case conn2 = <-conns: + case <-time.After(time.Second): + t.Fatal("Second Accept didn't happen") } - if g, e := res.TransferEncoding, []string{"chunked"}; !reflect.DeepEqual(g, e) { - t.Errorf("expected TransferEncoding of %v; got %v", e, g) + + // Send a response on connection 2. + conn2.(*blockingRemoteAddrConn).addrs <- &net.TCPAddr{ + IP: net.ParseIP("12.12.12.12"), Port: 12} + + // ... and see it + response2 := <-response2c + if g, e := response2, "RA:12.12.12.12:12"; g != e { + t.Fatalf("response 2 addr = %q; want %q", g, e) } - if _, haveCL := res.Header["Content-Length"]; haveCL { - t.Errorf("Unexpected Content-Length") + + // Finish the first response. + conn1.(*blockingRemoteAddrConn).addrs <- &net.TCPAddr{ + IP: net.ParseIP("21.21.21.21"), Port: 21} + + // ... and see it + response1 := <-response1c + if g, e := response1, "RA:21.21.21.21:21"; g != e { + t.Fatalf("response 1 addr = %q; want %q", g, e) } } - func TestIdentityResponseHeaders(t *testing.T) { defer afterTest(t) log.SetOutput(ioutil.Discard) // is noisy otherwise @@ -812,40 +894,14 @@ func TestIdentityResponseHeaders(t *testing.T) { } } -// Test304Responses verifies that 304s don't declare that they're -// chunking in their response headers and aren't allowed to produce -// output. -func Test304Responses(t *testing.T) { - defer afterTest(t) - ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { - w.WriteHeader(StatusNotModified) - _, err := w.Write([]byte("illegal body")) - if err != ErrBodyNotAllowed { - t.Errorf("on Write, expected ErrBodyNotAllowed, got %v", err) - } - })) - defer ts.Close() - res, err := Get(ts.URL) - if err != nil { - t.Error(err) - } - if len(res.TransferEncoding) > 0 { - t.Errorf("expected no TransferEncoding; got %v", res.TransferEncoding) - } - body, err := ioutil.ReadAll(res.Body) - if err != nil { - t.Error(err) - } - if len(body) > 0 { - t.Errorf("got unexpected body %q", string(body)) - } -} - // TestHeadResponses verifies that all MIME type sniffing and Content-Length // counting of GET requests also happens on HEAD requests. -func TestHeadResponses(t *testing.T) { +func TestHeadResponses_h1(t *testing.T) { testHeadResponses(t, h1Mode) } +func TestHeadResponses_h2(t *testing.T) { testHeadResponses(t, h2Mode) } + +func testHeadResponses(t *testing.T, h2 bool) { defer afterTest(t) - ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { + cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) { _, err := w.Write([]byte("<html>")) if err != nil { t.Errorf("ResponseWriter.Write: %v", err) @@ -857,8 +913,8 @@ func TestHeadResponses(t *testing.T) { t.Errorf("Copy(ResponseWriter, ...): %v", err) } })) - defer ts.Close() - res, err := Head(ts.URL) + defer cst.close() + res, err := cst.c.Head(cst.ts.URL) if err != nil { t.Error(err) } @@ -884,6 +940,7 @@ func TestTLSHandshakeTimeout(t *testing.T) { if runtime.GOOS == "plan9" { t.Skip("skipping test; see https://golang.org/issue/7237") } + setParallel(t) defer afterTest(t) ts := httptest.NewUnstartedServer(HandlerFunc(func(w ResponseWriter, r *Request) {})) errc := make(chanWriter, 10) // but only expecting 1 @@ -967,6 +1024,79 @@ func TestTLSServer(t *testing.T) { }) } +func TestAutomaticHTTP2_Serve(t *testing.T) { + defer afterTest(t) + ln := newLocalListener(t) + ln.Close() // immediately (not a defer!) + var s Server + if err := s.Serve(ln); err == nil { + t.Fatal("expected an error") + } + on := s.TLSNextProto["h2"] != nil + if !on { + t.Errorf("http2 wasn't automatically enabled") + } +} + +func TestAutomaticHTTP2_ListenAndServe(t *testing.T) { + defer afterTest(t) + defer SetTestHookServerServe(nil) + cert, err := tls.X509KeyPair(internal.LocalhostCert, internal.LocalhostKey) + if err != nil { + t.Fatal(err) + } + var ok bool + var s *Server + const maxTries = 5 + var ln net.Listener +Try: + for try := 0; try < maxTries; try++ { + ln = newLocalListener(t) + addr := ln.Addr().String() + ln.Close() + t.Logf("Got %v", addr) + lnc := make(chan net.Listener, 1) + SetTestHookServerServe(func(s *Server, ln net.Listener) { + lnc <- ln + }) + s = &Server{ + Addr: addr, + TLSConfig: &tls.Config{ + Certificates: []tls.Certificate{cert}, + }, + } + errc := make(chan error, 1) + go func() { errc <- s.ListenAndServeTLS("", "") }() + select { + case err := <-errc: + t.Logf("On try #%v: %v", try+1, err) + continue + case ln = <-lnc: + ok = true + t.Logf("Listening on %v", ln.Addr().String()) + break Try + } + } + if !ok { + t.Fatalf("Failed to start up after %d tries", maxTries) + } + defer ln.Close() + c, err := tls.Dial("tcp", ln.Addr().String(), &tls.Config{ + InsecureSkipVerify: true, + NextProtos: []string{"h2", "http/1.1"}, + }) + if err != nil { + t.Fatal(err) + } + defer c.Close() + if got, want := c.ConnectionState().NegotiatedProtocol, "h2"; got != want { + t.Errorf("NegotiatedProtocol = %q; want %q", got, want) + } + if got, want := c.ConnectionState().NegotiatedProtocolIsMutual, true; got != want { + t.Errorf("NegotiatedProtocolIsMutual = %v; want %v", got, want) + } +} + type serverExpectTest struct { contentLength int // of request body chunked bool @@ -1016,6 +1146,7 @@ var serverExpectTests = []serverExpectTest{ // Tests that the server responds to the "Expect" request header // correctly. +// http2 test: TestServer_Response_Automatic100Continue func TestServerExpect(t *testing.T) { defer afterTest(t) ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { @@ -1122,15 +1253,21 @@ func TestServerUnreadRequestBodyLittle(t *testing.T) { done := make(chan bool) + readBufLen := func() int { + conn.readMu.Lock() + defer conn.readMu.Unlock() + return conn.readBuf.Len() + } + ls := &oneConnListener{conn} go Serve(ls, HandlerFunc(func(rw ResponseWriter, req *Request) { defer close(done) - if conn.readBuf.Len() < len(body)/2 { - t.Errorf("on request, read buffer length is %d; expected about 100 KB", conn.readBuf.Len()) + if bufLen := readBufLen(); bufLen < len(body)/2 { + t.Errorf("on request, read buffer length is %d; expected about 100 KB", bufLen) } rw.WriteHeader(200) rw.(Flusher).Flush() - if g, e := conn.readBuf.Len(), 0; g != e { + if g, e := readBufLen(), 0; g != e { t.Errorf("after WriteHeader, read buffer length is %d; want %d", g, e) } if c := rw.Header().Get("Connection"); c != "" { @@ -1144,6 +1281,9 @@ func TestServerUnreadRequestBodyLittle(t *testing.T) { // should ignore client request bodies that a handler didn't read // and close the connection. func TestServerUnreadRequestBodyLarge(t *testing.T) { + if testing.Short() && testenv.Builder() == "" { + t.Log("skipping in short mode") + } conn := new(testConn) body := strings.Repeat("x", 1<<20) conn.readBuf.Write([]byte(fmt.Sprintf( @@ -1274,6 +1414,9 @@ var handlerBodyCloseTests = [...]handlerBodyCloseTest{ } func TestHandlerBodyClose(t *testing.T) { + if testing.Short() && testenv.Builder() == "" { + t.Skip("skipping in -short mode") + } for i, tt := range handlerBodyCloseTests { testHandlerBodyClose(t, i, tt) } @@ -1306,15 +1449,21 @@ func testHandlerBodyClose(t *testing.T, i int, tt handlerBodyCloseTest) { } conn.closec = make(chan bool, 1) + readBufLen := func() int { + conn.readMu.Lock() + defer conn.readMu.Unlock() + return conn.readBuf.Len() + } + ls := &oneConnListener{conn} var numReqs int var size0, size1 int go Serve(ls, HandlerFunc(func(rw ResponseWriter, req *Request) { numReqs++ if numReqs == 1 { - size0 = conn.readBuf.Len() + size0 = readBufLen() req.Body.Close() - size1 = conn.readBuf.Len() + size1 = readBufLen() } })) <-conn.closec @@ -1414,7 +1563,9 @@ type slowTestConn struct { // over multiple calls to Read, time.Durations are slept, strings are read. script []interface{} closec chan bool - rd, wd time.Time // read, write deadline + + mu sync.Mutex // guards rd/wd + rd, wd time.Time // read, write deadline noopConn } @@ -1425,16 +1576,22 @@ func (c *slowTestConn) SetDeadline(t time.Time) error { } func (c *slowTestConn) SetReadDeadline(t time.Time) error { + c.mu.Lock() + defer c.mu.Unlock() c.rd = t return nil } func (c *slowTestConn) SetWriteDeadline(t time.Time) error { + c.mu.Lock() + defer c.mu.Unlock() c.wd = t return nil } func (c *slowTestConn) Read(b []byte) (n int, err error) { + c.mu.Lock() + defer c.mu.Unlock() restart: if !c.rd.IsZero() && time.Now().After(c.rd) { return 0, syscall.ETIMEDOUT @@ -1531,7 +1688,9 @@ func TestRequestBodyTimeoutClosesConnection(t *testing.T) { } } -func TestTimeoutHandler(t *testing.T) { +func TestTimeoutHandler_h1(t *testing.T) { testTimeoutHandler(t, h1Mode) } +func TestTimeoutHandler_h2(t *testing.T) { testTimeoutHandler(t, h2Mode) } +func testTimeoutHandler(t *testing.T, h2 bool) { defer afterTest(t) sendHi := make(chan bool, 1) writeErrors := make(chan error, 1) @@ -1541,12 +1700,12 @@ func TestTimeoutHandler(t *testing.T) { writeErrors <- werr }) timeout := make(chan time.Time, 1) // write to this to force timeouts - ts := httptest.NewServer(NewTestTimeoutHandler(sayHi, timeout)) - defer ts.Close() + cst := newClientServerTest(t, h2, NewTestTimeoutHandler(sayHi, timeout)) + defer cst.close() // Succeed without timing out: sendHi <- true - res, err := Get(ts.URL) + res, err := cst.c.Get(cst.ts.URL) if err != nil { t.Error(err) } @@ -1563,7 +1722,7 @@ func TestTimeoutHandler(t *testing.T) { // Times out: timeout <- time.Time{} - res, err = Get(ts.URL) + res, err = cst.c.Get(cst.ts.URL) if err != nil { t.Error(err) } @@ -1659,6 +1818,60 @@ func TestTimeoutHandlerRaceHeader(t *testing.T) { wg.Wait() } +// Issue 9162 +func TestTimeoutHandlerRaceHeaderTimeout(t *testing.T) { + defer afterTest(t) + sendHi := make(chan bool, 1) + writeErrors := make(chan error, 1) + sayHi := HandlerFunc(func(w ResponseWriter, r *Request) { + w.Header().Set("Content-Type", "text/plain") + <-sendHi + _, werr := w.Write([]byte("hi")) + writeErrors <- werr + }) + timeout := make(chan time.Time, 1) // write to this to force timeouts + cst := newClientServerTest(t, h1Mode, NewTestTimeoutHandler(sayHi, timeout)) + defer cst.close() + + // Succeed without timing out: + sendHi <- true + res, err := cst.c.Get(cst.ts.URL) + if err != nil { + t.Error(err) + } + if g, e := res.StatusCode, StatusOK; g != e { + t.Errorf("got res.StatusCode %d; expected %d", g, e) + } + body, _ := ioutil.ReadAll(res.Body) + if g, e := string(body), "hi"; g != e { + t.Errorf("got body %q; expected %q", g, e) + } + if g := <-writeErrors; g != nil { + t.Errorf("got unexpected Write error on first request: %v", g) + } + + // Times out: + timeout <- time.Time{} + res, err = cst.c.Get(cst.ts.URL) + if err != nil { + t.Error(err) + } + if g, e := res.StatusCode, StatusServiceUnavailable; g != e { + t.Errorf("got res.StatusCode %d; expected %d", g, e) + } + body, _ = ioutil.ReadAll(res.Body) + if !strings.Contains(string(body), "<title>Timeout</title>") { + t.Errorf("expected timeout body; got %q", string(body)) + } + + // Now make the previously-timed out handler speak again, + // which verifies the panic is handled: + sendHi <- true + if g, e := <-writeErrors, ErrHandlerTimeout; g != e { + t.Errorf("expected Write error of %v; got %v", e, g) + } +} + // Verifies we don't path.Clean() on the wrong parts in redirects. func TestRedirectMunging(t *testing.T) { req, _ := NewRequest("GET", "http://example.com/", nil) @@ -1693,15 +1906,57 @@ func TestRedirectBadPath(t *testing.T) { } } +// Test different URL formats and schemes +func TestRedirectURLFormat(t *testing.T) { + req, _ := NewRequest("GET", "http://example.com/qux/", nil) + + var tests = []struct { + in string + want string + }{ + // normal http + {"http://foobar.com/baz", "http://foobar.com/baz"}, + // normal https + {"https://foobar.com/baz", "https://foobar.com/baz"}, + // custom scheme + {"test://foobar.com/baz", "test://foobar.com/baz"}, + // schemeless + {"//foobar.com/baz", "//foobar.com/baz"}, + // relative to the root + {"/foobar.com/baz", "/foobar.com/baz"}, + // relative to the current path + {"foobar.com/baz", "/qux/foobar.com/baz"}, + // relative to the current path (+ going upwards) + {"../quux/foobar.com/baz", "/quux/foobar.com/baz"}, + // incorrect number of slashes + {"///foobar.com/baz", "/foobar.com/baz"}, + } + + for _, tt := range tests { + rec := httptest.NewRecorder() + Redirect(rec, req, tt.in, 302) + if got := rec.Header().Get("Location"); got != tt.want { + t.Errorf("Redirect(%q) generated Location header %q; want %q", tt.in, got, tt.want) + } + } +} + // TestZeroLengthPostAndResponse exercises an optimization done by the Transport: // when there is no body (either because the method doesn't permit a body, or an // explicit Content-Length of zero is present), then the transport can re-use the // connection immediately. But when it re-uses the connection, it typically closes // the previous request's body, which is not optimal for zero-lengthed bodies, // as the client would then see http.ErrBodyReadAfterClose and not 0, io.EOF. -func TestZeroLengthPostAndResponse(t *testing.T) { +func TestZeroLengthPostAndResponse_h1(t *testing.T) { + testZeroLengthPostAndResponse(t, h1Mode) +} +func TestZeroLengthPostAndResponse_h2(t *testing.T) { + testZeroLengthPostAndResponse(t, h2Mode) +} + +func testZeroLengthPostAndResponse(t *testing.T, h2 bool) { defer afterTest(t) - ts := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, r *Request) { + cst := newClientServerTest(t, h2, HandlerFunc(func(rw ResponseWriter, r *Request) { all, err := ioutil.ReadAll(r.Body) if err != nil { t.Fatalf("handler ReadAll: %v", err) @@ -1711,9 +1966,9 @@ func TestZeroLengthPostAndResponse(t *testing.T) { } rw.Header().Set("Content-Length", "0") })) - defer ts.Close() + defer cst.close() - req, err := NewRequest("POST", ts.URL, strings.NewReader("")) + req, err := NewRequest("POST", cst.ts.URL, strings.NewReader("")) if err != nil { t.Fatal(err) } @@ -1721,7 +1976,7 @@ func TestZeroLengthPostAndResponse(t *testing.T) { var resp [5]*Response for i := range resp { - resp[i], err = DefaultClient.Do(req) + resp[i], err = cst.c.Do(req) if err != nil { t.Fatalf("client post #%d: %v", i, err) } @@ -1738,19 +1993,22 @@ func TestZeroLengthPostAndResponse(t *testing.T) { } } -func TestHandlerPanicNil(t *testing.T) { - testHandlerPanic(t, false, nil) -} +func TestHandlerPanicNil_h1(t *testing.T) { testHandlerPanic(t, false, h1Mode, nil) } +func TestHandlerPanicNil_h2(t *testing.T) { testHandlerPanic(t, false, h2Mode, nil) } -func TestHandlerPanic(t *testing.T) { - testHandlerPanic(t, false, "intentional death for testing") +func TestHandlerPanic_h1(t *testing.T) { + testHandlerPanic(t, false, h1Mode, "intentional death for testing") +} +func TestHandlerPanic_h2(t *testing.T) { + testHandlerPanic(t, false, h2Mode, "intentional death for testing") } func TestHandlerPanicWithHijack(t *testing.T) { - testHandlerPanic(t, true, "intentional death for testing") + // Only testing HTTP/1, and our http2 server doesn't support hijacking. + testHandlerPanic(t, true, h1Mode, "intentional death for testing") } -func testHandlerPanic(t *testing.T, withHijack bool, panicValue interface{}) { +func testHandlerPanic(t *testing.T, withHijack, h2 bool, panicValue interface{}) { defer afterTest(t) // Unlike the other tests that set the log output to ioutil.Discard // to quiet the output, this test uses a pipe. The pipe serves three @@ -1773,7 +2031,7 @@ func testHandlerPanic(t *testing.T, withHijack bool, panicValue interface{}) { defer log.SetOutput(os.Stderr) defer pw.Close() - ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { + cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) { if withHijack { rwc, _, err := w.(Hijacker).Hijack() if err != nil { @@ -1783,7 +2041,7 @@ func testHandlerPanic(t *testing.T, withHijack bool, panicValue interface{}) { } panic(panicValue) })) - defer ts.Close() + defer cst.close() // Do a blocking read on the log output pipe so its logging // doesn't bleed into the next test. But wait only 5 seconds @@ -1799,7 +2057,7 @@ func testHandlerPanic(t *testing.T, withHijack bool, panicValue interface{}) { done <- true }() - _, err := Get(ts.URL) + _, err := cst.c.Get(cst.ts.URL) if err == nil { t.Logf("expected an error") } @@ -1816,17 +2074,19 @@ func testHandlerPanic(t *testing.T, withHijack bool, panicValue interface{}) { } } -func TestServerNoDate(t *testing.T) { testServerNoHeader(t, "Date") } -func TestServerNoContentType(t *testing.T) { testServerNoHeader(t, "Content-Type") } +func TestServerNoDate_h1(t *testing.T) { testServerNoHeader(t, h1Mode, "Date") } +func TestServerNoDate_h2(t *testing.T) { testServerNoHeader(t, h2Mode, "Date") } +func TestServerNoContentType_h1(t *testing.T) { testServerNoHeader(t, h1Mode, "Content-Type") } +func TestServerNoContentType_h2(t *testing.T) { testServerNoHeader(t, h2Mode, "Content-Type") } -func testServerNoHeader(t *testing.T, header string) { +func testServerNoHeader(t *testing.T, h2 bool, header string) { defer afterTest(t) - ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { + cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) { w.Header()[header] = nil io.WriteString(w, "<html>foo</html>") // non-empty })) - defer ts.Close() - res, err := Get(ts.URL) + defer cst.close() + res, err := cst.c.Get(cst.ts.URL) if err != nil { t.Fatal(err) } @@ -1863,18 +2123,20 @@ func TestStripPrefix(t *testing.T) { res.Body.Close() } -func TestRequestLimit(t *testing.T) { +func TestRequestLimit_h1(t *testing.T) { testRequestLimit(t, h1Mode) } +func TestRequestLimit_h2(t *testing.T) { testRequestLimit(t, h2Mode) } +func testRequestLimit(t *testing.T, h2 bool) { defer afterTest(t) - ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { + cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) { t.Fatalf("didn't expect to get request in Handler") })) - defer ts.Close() - req, _ := NewRequest("GET", ts.URL, nil) + defer cst.close() + req, _ := NewRequest("GET", cst.ts.URL, nil) var bytesPerHeader = len("header12345: val12345\r\n") for i := 0; i < ((DefaultMaxHeaderBytes+4096)/bytesPerHeader)+1; i++ { req.Header.Set(fmt.Sprintf("header%05d", i), fmt.Sprintf("val%05d", i)) } - res, err := DefaultClient.Do(req) + res, err := cst.c.Do(req) if err != nil { // Some HTTP clients may fail on this undefined behavior (server replying and // closing the connection while the request is still being written), but @@ -1882,8 +2144,8 @@ func TestRequestLimit(t *testing.T) { t.Fatalf("Do: %v", err) } defer res.Body.Close() - if res.StatusCode != 413 { - t.Fatalf("expected 413 response status; got: %d %s", res.StatusCode, res.Status) + if res.StatusCode != 431 { + t.Fatalf("expected 431 response status; got: %d %s", res.StatusCode, res.Status) } } @@ -1907,10 +2169,12 @@ func (cr countReader) Read(p []byte) (n int, err error) { return } -func TestRequestBodyLimit(t *testing.T) { +func TestRequestBodyLimit_h1(t *testing.T) { testRequestBodyLimit(t, h1Mode) } +func TestRequestBodyLimit_h2(t *testing.T) { testRequestBodyLimit(t, h2Mode) } +func testRequestBodyLimit(t *testing.T, h2 bool) { defer afterTest(t) const limit = 1 << 20 - ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { + cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) { r.Body = MaxBytesReader(w, r.Body, limit) n, err := io.Copy(ioutil.Discard, r.Body) if err == nil { @@ -1920,10 +2184,10 @@ func TestRequestBodyLimit(t *testing.T) { t.Errorf("io.Copy = %d, want %d", n, limit) } })) - defer ts.Close() + defer cst.close() nWritten := new(int64) - req, _ := NewRequest("POST", ts.URL, io.LimitReader(countReader{neverEnding('a'), nWritten}, limit*200)) + req, _ := NewRequest("POST", cst.ts.URL, io.LimitReader(countReader{neverEnding('a'), nWritten}, limit*200)) // Send the POST, but don't care it succeeds or not. The // remote side is going to reply and then close the TCP @@ -1934,7 +2198,7 @@ func TestRequestBodyLimit(t *testing.T) { // // But that's okay, since what we're really testing is that // the remote side hung up on us before we wrote too much. - _, _ = DefaultClient.Do(req) + _, _ = cst.c.Do(req) if atomic.LoadInt64(nWritten) > limit*100 { t.Errorf("handler restricted the request body to %d bytes, but client managed to write %d", @@ -1982,7 +2246,7 @@ func TestClientWriteShutdown(t *testing.T) { // buffered before chunk headers are added, not after chunk headers. func TestServerBufferedChunking(t *testing.T) { conn := new(testConn) - conn.readBuf.Write([]byte("GET / HTTP/1.1\r\n\r\n")) + conn.readBuf.Write([]byte("GET / HTTP/1.1\r\nHost: foo\r\n\r\n")) conn.closec = make(chan bool, 1) ls := &oneConnListener{conn} go Serve(ls, HandlerFunc(func(rw ResponseWriter, req *Request) { @@ -2045,20 +2309,23 @@ func TestServerGracefulClose(t *testing.T) { <-writeErr } -func TestCaseSensitiveMethod(t *testing.T) { +func TestCaseSensitiveMethod_h1(t *testing.T) { testCaseSensitiveMethod(t, h1Mode) } +func TestCaseSensitiveMethod_h2(t *testing.T) { testCaseSensitiveMethod(t, h2Mode) } +func testCaseSensitiveMethod(t *testing.T, h2 bool) { defer afterTest(t) - ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { + cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) { if r.Method != "get" { t.Errorf(`Got method %q; want "get"`, r.Method) } })) - defer ts.Close() - req, _ := NewRequest("get", ts.URL, nil) - res, err := DefaultClient.Do(req) + defer cst.close() + req, _ := NewRequest("get", cst.ts.URL, nil) + res, err := cst.c.Do(req) if err != nil { t.Error(err) return } + res.Body.Close() } @@ -2131,6 +2398,49 @@ For: ts.Close() } +// Tests that a pipelined request causes the first request's Handler's CloseNotify +// channel to fire. Previously it deadlocked. +// +// Issue 13165 +func TestCloseNotifierPipelined(t *testing.T) { + defer afterTest(t) + gotReq := make(chan bool, 2) + sawClose := make(chan bool, 2) + ts := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, req *Request) { + gotReq <- true + cc := rw.(CloseNotifier).CloseNotify() + <-cc + sawClose <- true + })) + conn, err := net.Dial("tcp", ts.Listener.Addr().String()) + if err != nil { + t.Fatalf("error dialing: %v", err) + } + diec := make(chan bool, 2) + go func() { + const req = "GET / HTTP/1.1\r\nConnection: keep-alive\r\nHost: foo\r\n\r\n" + _, err = io.WriteString(conn, req+req) // two requests + if err != nil { + t.Fatal(err) + } + <-diec + conn.Close() + }() +For: + for { + select { + case <-gotReq: + diec <- true + case <-sawClose: + break For + case <-time.After(5 * time.Second): + ts.CloseClientConnections() + t.Fatal("timeout") + } + } + ts.Close() +} + func TestCloseNotifierChanLeak(t *testing.T) { defer afterTest(t) req := reqBytes("GET / HTTP/1.0\nHost: golang.org") @@ -2153,6 +2463,114 @@ func TestCloseNotifierChanLeak(t *testing.T) { } } +// Tests that we can use CloseNotifier in one request, and later call Hijack +// on a second request on the same connection. +// +// It also tests that the connReader stitches together its background +// 1-byte read for CloseNotifier when CloseNotifier doesn't fire with +// the rest of the second HTTP later. +// +// Issue 9763. +// HTTP/1-only test. (http2 doesn't have Hijack) +func TestHijackAfterCloseNotifier(t *testing.T) { + defer afterTest(t) + script := make(chan string, 2) + script <- "closenotify" + script <- "hijack" + close(script) + ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { + plan := <-script + switch plan { + default: + panic("bogus plan; too many requests") + case "closenotify": + w.(CloseNotifier).CloseNotify() // discard result + w.Header().Set("X-Addr", r.RemoteAddr) + case "hijack": + c, _, err := w.(Hijacker).Hijack() + if err != nil { + t.Errorf("Hijack in Handler: %v", err) + return + } + if _, ok := c.(*net.TCPConn); !ok { + // Verify it's not wrapped in some type. + // Not strictly a go1 compat issue, but in practice it probably is. + t.Errorf("type of hijacked conn is %T; want *net.TCPConn", c) + } + fmt.Fprintf(c, "HTTP/1.0 200 OK\r\nX-Addr: %v\r\nContent-Length: 0\r\n\r\n", r.RemoteAddr) + c.Close() + return + } + })) + defer ts.Close() + res1, err := Get(ts.URL) + if err != nil { + log.Fatal(err) + } + res2, err := Get(ts.URL) + if err != nil { + log.Fatal(err) + } + addr1 := res1.Header.Get("X-Addr") + addr2 := res2.Header.Get("X-Addr") + if addr1 == "" || addr1 != addr2 { + t.Errorf("addr1, addr2 = %q, %q; want same", addr1, addr2) + } +} + +func TestHijackBeforeRequestBodyRead(t *testing.T) { + defer afterTest(t) + var requestBody = bytes.Repeat([]byte("a"), 1<<20) + bodyOkay := make(chan bool, 1) + gotCloseNotify := make(chan bool, 1) + ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { + defer close(bodyOkay) // caller will read false if nothing else + + reqBody := r.Body + r.Body = nil // to test that server.go doesn't use this value. + + gone := w.(CloseNotifier).CloseNotify() + slurp, err := ioutil.ReadAll(reqBody) + if err != nil { + t.Errorf("Body read: %v", err) + return + } + if len(slurp) != len(requestBody) { + t.Errorf("Backend read %d request body bytes; want %d", len(slurp), len(requestBody)) + return + } + if !bytes.Equal(slurp, requestBody) { + t.Error("Backend read wrong request body.") // 1MB; omitting details + return + } + bodyOkay <- true + select { + case <-gone: + gotCloseNotify <- true + case <-time.After(5 * time.Second): + gotCloseNotify <- false + } + })) + defer ts.Close() + + conn, err := net.Dial("tcp", ts.Listener.Addr().String()) + if err != nil { + t.Fatal(err) + } + defer conn.Close() + + fmt.Fprintf(conn, "POST / HTTP/1.1\r\nHost: foo\r\nContent-Length: %d\r\n\r\n%s", + len(requestBody), requestBody) + if !<-bodyOkay { + // already failed. + return + } + conn.Close() + if !<-gotCloseNotify { + t.Error("timeout waiting for CloseNotify") + } +} + func TestOptions(t *testing.T) { uric := make(chan string, 2) // only expect 1, but leave space for 2 mux := NewServeMux() @@ -2230,7 +2648,7 @@ func TestHeaderToWire(t *testing.T) { return errors.New("no content-length") } if !strings.Contains(got, "Content-Type: text/plain") { - return errors.New("no content-length") + return errors.New("no content-type") } return nil }, @@ -2302,7 +2720,7 @@ func TestHeaderToWire(t *testing.T) { return errors.New("header appeared from after WriteHeader") } if !strings.Contains(got, "Content-Type: some/type") { - return errors.New("wrong content-length") + return errors.New("wrong content-type") } return nil }, @@ -2315,7 +2733,7 @@ func TestHeaderToWire(t *testing.T) { }, check: func(got string) error { if !strings.Contains(got, "Content-Type: text/html") { - return errors.New("wrong content-length; want html") + return errors.New("wrong content-type; want html") } return nil }, @@ -2328,7 +2746,7 @@ func TestHeaderToWire(t *testing.T) { }, check: func(got string) error { if !strings.Contains(got, "Content-Type: some/type") { - return errors.New("wrong content-length; want html") + return errors.New("wrong content-type; want html") } return nil }, @@ -2339,7 +2757,7 @@ func TestHeaderToWire(t *testing.T) { }, check: func(got string) error { if !strings.Contains(got, "Content-Type: text/plain") { - return errors.New("wrong content-length; want text/plain") + return errors.New("wrong content-type; want text/plain") } if !strings.Contains(got, "Content-Length: 0") { return errors.New("want 0 content-length") @@ -2369,7 +2787,7 @@ func TestHeaderToWire(t *testing.T) { if !strings.Contains(got, "404") { return errors.New("wrong status") } - if strings.Contains(got, "Some-Header") { + if strings.Contains(got, "Too-Late") { return errors.New("shouldn't have seen Too-Late") } return nil @@ -2503,7 +2921,7 @@ func TestHTTP10ConnectionHeader(t *testing.T) { defer afterTest(t) mux := NewServeMux() - mux.Handle("/", HandlerFunc(func(resp ResponseWriter, req *Request) {})) + mux.Handle("/", HandlerFunc(func(ResponseWriter, *Request) {})) ts := httptest.NewServer(mux) defer ts.Close() @@ -2552,11 +2970,13 @@ func TestHTTP10ConnectionHeader(t *testing.T) { } // See golang.org/issue/5660 -func TestServerReaderFromOrder(t *testing.T) { +func TestServerReaderFromOrder_h1(t *testing.T) { testServerReaderFromOrder(t, h1Mode) } +func TestServerReaderFromOrder_h2(t *testing.T) { testServerReaderFromOrder(t, h2Mode) } +func testServerReaderFromOrder(t *testing.T, h2 bool) { defer afterTest(t) pr, pw := io.Pipe() const size = 3 << 20 - ts := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, req *Request) { + cst := newClientServerTest(t, h2, HandlerFunc(func(rw ResponseWriter, req *Request) { rw.Header().Set("Content-Type", "text/plain") // prevent sniffing path done := make(chan bool) go func() { @@ -2576,13 +2996,13 @@ func TestServerReaderFromOrder(t *testing.T) { pw.Close() <-done })) - defer ts.Close() + defer cst.close() - req, err := NewRequest("POST", ts.URL, io.LimitReader(neverEnding('a'), size)) + req, err := NewRequest("POST", cst.ts.URL, io.LimitReader(neverEnding('a'), size)) if err != nil { t.Fatal(err) } - res, err := DefaultClient.Do(req) + res, err := cst.c.Do(req) if err != nil { t.Fatal(err) } @@ -2612,9 +3032,9 @@ func TestCodesPreventingContentTypeAndBody(t *testing.T) { "GET / HTTP/1.0", "GET /header HTTP/1.0", "GET /more HTTP/1.0", - "GET / HTTP/1.1", - "GET /header HTTP/1.1", - "GET /more HTTP/1.1", + "GET / HTTP/1.1\nHost: foo", + "GET /header HTTP/1.1\nHost: foo", + "GET /more HTTP/1.1\nHost: foo", } { got := ht.rawResponse(req) wantStatus := fmt.Sprintf("%d %s", code, StatusText(code)) @@ -2635,7 +3055,7 @@ func TestContentTypeOkayOn204(t *testing.T) { w.Header().Set("Content-Type", "foo/bar") w.WriteHeader(204) })) - got := ht.rawResponse("GET / HTTP/1.1") + got := ht.rawResponse("GET / HTTP/1.1\nHost: foo") if !strings.Contains(got, "Content-Type: foo/bar") { t.Errorf("Response = %q; want Content-Type: foo/bar", got) } @@ -2650,45 +3070,101 @@ func TestContentTypeOkayOn204(t *testing.T) { // proxy). So then two people own that Request.Body (both the server // and the http client), and both think they can close it on failure. // Therefore, all incoming server requests Bodies need to be thread-safe. -func TestTransportAndServerSharedBodyRace(t *testing.T) { +func TestTransportAndServerSharedBodyRace_h1(t *testing.T) { + testTransportAndServerSharedBodyRace(t, h1Mode) +} +func TestTransportAndServerSharedBodyRace_h2(t *testing.T) { + testTransportAndServerSharedBodyRace(t, h2Mode) +} +func testTransportAndServerSharedBodyRace(t *testing.T, h2 bool) { defer afterTest(t) const bodySize = 1 << 20 + // errorf is like t.Errorf, but also writes to println. When + // this test fails, it hangs. This helps debugging and I've + // added this enough times "temporarily". It now gets added + // full time. + errorf := func(format string, args ...interface{}) { + v := fmt.Sprintf(format, args...) + println(v) + t.Error(v) + } + unblockBackend := make(chan bool) - backend := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, req *Request) { - io.CopyN(rw, req.Body, bodySize) + backend := newClientServerTest(t, h2, HandlerFunc(func(rw ResponseWriter, req *Request) { + gone := rw.(CloseNotifier).CloseNotify() + didCopy := make(chan interface{}) + go func() { + n, err := io.CopyN(rw, req.Body, bodySize) + didCopy <- []interface{}{n, err} + }() + isGone := false + Loop: + for { + select { + case <-didCopy: + break Loop + case <-gone: + isGone = true + case <-time.After(time.Second): + println("1 second passes in backend, proxygone=", isGone) + } + } <-unblockBackend })) - defer backend.Close() + var quitTimer *time.Timer + defer func() { quitTimer.Stop() }() + defer backend.close() backendRespc := make(chan *Response, 1) - proxy := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, req *Request) { - req2, _ := NewRequest("POST", backend.URL, req.Body) + var proxy *clientServerTest + proxy = newClientServerTest(t, h2, HandlerFunc(func(rw ResponseWriter, req *Request) { + req2, _ := NewRequest("POST", backend.ts.URL, req.Body) req2.ContentLength = bodySize + cancel := make(chan struct{}) + req2.Cancel = cancel - bresp, err := DefaultClient.Do(req2) + bresp, err := proxy.c.Do(req2) if err != nil { - t.Errorf("Proxy outbound request: %v", err) + errorf("Proxy outbound request: %v", err) return } _, err = io.CopyN(ioutil.Discard, bresp.Body, bodySize/2) if err != nil { - t.Errorf("Proxy copy error: %v", err) + errorf("Proxy copy error: %v", err) return } backendRespc <- bresp // to close later - // Try to cause a race: Both the DefaultTransport and the proxy handler's Server + // Try to cause a race: Both the Transport and the proxy handler's Server // will try to read/close req.Body (aka req2.Body) - DefaultTransport.(*Transport).CancelRequest(req2) + if h2 { + close(cancel) + } else { + proxy.c.Transport.(*Transport).CancelRequest(req2) + } rw.Write([]byte("OK")) })) - defer proxy.Close() + defer proxy.close() + defer func() { + // Before we shut down our two httptest.Servers, start a timer. + // We choose 7 seconds because httptest.Server starts logging + // warnings to stderr at 5 seconds. If we don't disarm this bomb + // in 7 seconds (after the two httptest.Server.Close calls above), + // then we explode with stacks. + quitTimer = time.AfterFunc(7*time.Second, func() { + debug.SetTraceback("ALL") + stacks := make([]byte, 1<<20) + stacks = stacks[:runtime.Stack(stacks, true)] + fmt.Fprintf(os.Stderr, "%s", stacks) + log.Fatalf("Timeout.") + }) + }() defer close(unblockBackend) - req, _ := NewRequest("POST", proxy.URL, io.LimitReader(neverEnding('a'), bodySize)) - res, err := DefaultClient.Do(req) + req, _ := NewRequest("POST", proxy.ts.URL, io.LimitReader(neverEnding('a'), bodySize)) + res, err := proxy.c.Do(req) if err != nil { t.Fatalf("Original request: %v", err) } @@ -2699,7 +3175,7 @@ func TestTransportAndServerSharedBodyRace(t *testing.T) { case res := <-backendRespc: res.Body.Close() default: - // We failed earlier. (e.g. on DefaultClient.Do(req2)) + // We failed earlier. (e.g. on proxy.c.Do(req2)) } } @@ -2863,6 +3339,7 @@ func TestServerConnState(t *testing.T) { if _, err := io.WriteString(c, "BOGUS REQUEST\r\n\r\n"); err != nil { t.Fatal(err) } + c.Read(make([]byte, 1)) // block until server hangs up on us c.Close() } @@ -2896,9 +3373,14 @@ func TestServerConnState(t *testing.T) { } logString := func(m map[int][]ConnState) string { var b bytes.Buffer - for id, l := range m { + var keys []int + for id := range m { + keys = append(keys, id) + } + sort.Ints(keys) + for _, id := range keys { fmt.Fprintf(&b, "Conn %d: ", id) - for _, s := range l { + for _, s := range m[id] { fmt.Fprintf(&b, "%s ", s) } b.WriteString("\n") @@ -2959,20 +3441,22 @@ func TestServerKeepAlivesEnabled(t *testing.T) { } // golang.org/issue/7856 -func TestServerEmptyBodyRace(t *testing.T) { +func TestServerEmptyBodyRace_h1(t *testing.T) { testServerEmptyBodyRace(t, h1Mode) } +func TestServerEmptyBodyRace_h2(t *testing.T) { testServerEmptyBodyRace(t, h2Mode) } +func testServerEmptyBodyRace(t *testing.T, h2 bool) { defer afterTest(t) var n int32 - ts := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, req *Request) { + cst := newClientServerTest(t, h2, HandlerFunc(func(rw ResponseWriter, req *Request) { atomic.AddInt32(&n, 1) })) - defer ts.Close() + defer cst.close() var wg sync.WaitGroup const reqs = 20 for i := 0; i < reqs; i++ { wg.Add(1) go func() { defer wg.Done() - res, err := Get(ts.URL) + res, err := cst.c.Get(cst.ts.URL) if err != nil { t.Error(err) return @@ -3025,10 +3509,7 @@ func (c *closeWriteTestConn) CloseWrite() error { func TestCloseWrite(t *testing.T) { var srv Server var testConn closeWriteTestConn - c, err := ExportServerNewConn(&srv, &testConn) - if err != nil { - t.Fatal(err) - } + c := ExportServerNewConn(&srv, &testConn) ExportCloseWriteAndWait(c) if !testConn.didCloseWrite { t.Error("didn't see CloseWrite call") @@ -3193,6 +3674,33 @@ func TestTolerateCRLFBeforeRequestLine(t *testing.T) { } } +func TestIssue13893_Expect100(t *testing.T) { + // test that the Server doesn't filter out Expect headers. + req := reqBytes(`PUT /readbody HTTP/1.1 +User-Agent: PycURL/7.22.0 +Host: 127.0.0.1:9000 +Accept: */* +Expect: 100-continue +Content-Length: 10 + +HelloWorld + +`) + var buf bytes.Buffer + conn := &rwTestConn{ + Reader: bytes.NewReader(req), + Writer: &buf, + closec: make(chan bool, 1), + } + ln := &oneConnListener{conn: conn} + go Serve(ln, HandlerFunc(func(w ResponseWriter, r *Request) { + if _, ok := r.Header["Expect"]; !ok { + t.Error("Expect header should not be filtered out") + } + })) + <-conn.closec +} + func TestIssue11549_Expect100(t *testing.T) { req := reqBytes(`PUT /readbody HTTP/1.1 User-Agent: PycURL/7.22.0 @@ -3260,6 +3768,122 @@ func TestHandlerFinishSkipBigContentLengthRead(t *testing.T) { } } +func TestHandlerSetsBodyNil_h1(t *testing.T) { testHandlerSetsBodyNil(t, h1Mode) } +func TestHandlerSetsBodyNil_h2(t *testing.T) { testHandlerSetsBodyNil(t, h2Mode) } +func testHandlerSetsBodyNil(t *testing.T, h2 bool) { + defer afterTest(t) + cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) { + r.Body = nil + fmt.Fprintf(w, "%v", r.RemoteAddr) + })) + defer cst.close() + get := func() string { + res, err := cst.c.Get(cst.ts.URL) + if err != nil { + t.Fatal(err) + } + defer res.Body.Close() + slurp, err := ioutil.ReadAll(res.Body) + if err != nil { + t.Fatal(err) + } + return string(slurp) + } + a, b := get(), get() + if a != b { + t.Errorf("Failed to reuse connections between requests: %v vs %v", a, b) + } +} + +// Test that we validate the Host header. +// Issue 11206 (invalid bytes in Host) and 13624 (Host present in HTTP/1.1) +func TestServerValidatesHostHeader(t *testing.T) { + tests := []struct { + proto string + host string + want int + }{ + {"HTTP/1.1", "", 400}, + {"HTTP/1.1", "Host: \r\n", 200}, + {"HTTP/1.1", "Host: 1.2.3.4\r\n", 200}, + {"HTTP/1.1", "Host: foo.com\r\n", 200}, + {"HTTP/1.1", "Host: foo-bar_baz.com\r\n", 200}, + {"HTTP/1.1", "Host: foo.com:80\r\n", 200}, + {"HTTP/1.1", "Host: ::1\r\n", 200}, + {"HTTP/1.1", "Host: [::1]\r\n", 200}, // questionable without port, but accept it + {"HTTP/1.1", "Host: [::1]:80\r\n", 200}, + {"HTTP/1.1", "Host: [::1%25en0]:80\r\n", 200}, + {"HTTP/1.1", "Host: 1.2.3.4\r\n", 200}, + {"HTTP/1.1", "Host: \x06\r\n", 400}, + {"HTTP/1.1", "Host: \xff\r\n", 400}, + {"HTTP/1.1", "Host: {\r\n", 400}, + {"HTTP/1.1", "Host: }\r\n", 400}, + {"HTTP/1.1", "Host: first\r\nHost: second\r\n", 400}, + + // HTTP/1.0 can lack a host header, but if present + // must play by the rules too: + {"HTTP/1.0", "", 200}, + {"HTTP/1.0", "Host: first\r\nHost: second\r\n", 400}, + {"HTTP/1.0", "Host: \xff\r\n", 400}, + } + for _, tt := range tests { + conn := &testConn{closec: make(chan bool, 1)} + io.WriteString(&conn.readBuf, "GET / "+tt.proto+"\r\n"+tt.host+"\r\n") + + ln := &oneConnListener{conn} + go Serve(ln, HandlerFunc(func(ResponseWriter, *Request) {})) + <-conn.closec + res, err := ReadResponse(bufio.NewReader(&conn.writeBuf), nil) + if err != nil { + t.Errorf("For %s %q, ReadResponse: %v", tt.proto, tt.host, res) + continue + } + if res.StatusCode != tt.want { + t.Errorf("For %s %q, Status = %d; want %d", tt.proto, tt.host, res.StatusCode, tt.want) + } + } +} + +// Test that we validate the valid bytes in HTTP/1 headers. +// Issue 11207. +func TestServerValidatesHeaders(t *testing.T) { + tests := []struct { + header string + want int + }{ + {"", 200}, + {"Foo: bar\r\n", 200}, + {"X-Foo: bar\r\n", 200}, + {"Foo: a space\r\n", 200}, + + {"A space: foo\r\n", 400}, // space in header + {"foo\xffbar: foo\r\n", 400}, // binary in header + {"foo\x00bar: foo\r\n", 400}, // binary in header + + {"foo: foo foo\r\n", 200}, // LWS space is okay + {"foo: foo\tfoo\r\n", 200}, // LWS tab is okay + {"foo: foo\x00foo\r\n", 400}, // CTL 0x00 in value is bad + {"foo: foo\x7ffoo\r\n", 400}, // CTL 0x7f in value is bad + {"foo: foo\xfffoo\r\n", 200}, // non-ASCII high octets in value are fine + } + for _, tt := range tests { + conn := &testConn{closec: make(chan bool, 1)} + io.WriteString(&conn.readBuf, "GET / HTTP/1.1\r\nHost: foo\r\n"+tt.header+"\r\n") + + ln := &oneConnListener{conn} + go Serve(ln, HandlerFunc(func(ResponseWriter, *Request) {})) + <-conn.closec + res, err := ReadResponse(bufio.NewReader(&conn.writeBuf), nil) + if err != nil { + t.Errorf("For %q, ReadResponse: %v", tt.header, res) + continue + } + if res.StatusCode != tt.want { + t.Errorf("For %q, Status = %d; want %d", tt.header, res.StatusCode, tt.want) + } + } +} + func BenchmarkClientServer(b *testing.B) { b.ReportAllocs() b.StopTimer() @@ -3685,3 +4309,35 @@ Host: golang.org <-conn.closec } } + +func BenchmarkCloseNotifier(b *testing.B) { + b.ReportAllocs() + b.StopTimer() + sawClose := make(chan bool) + ts := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, req *Request) { + <-rw.(CloseNotifier).CloseNotify() + sawClose <- true + })) + defer ts.Close() + tot := time.NewTimer(5 * time.Second) + defer tot.Stop() + b.StartTimer() + for i := 0; i < b.N; i++ { + conn, err := net.Dial("tcp", ts.Listener.Addr().String()) + if err != nil { + b.Fatalf("error dialing: %v", err) + } + _, err = fmt.Fprintf(conn, "GET / HTTP/1.1\r\nConnection: keep-alive\r\nHost: foo\r\n\r\n") + if err != nil { + b.Fatal(err) + } + conn.Close() + tot.Reset(5 * time.Second) + select { + case <-sawClose: + case <-tot.C: + b.Fatal("timeout") + } + } + b.StopTimer() +} |