summaryrefslogtreecommitdiff
path: root/libgo/go/net/http/serve_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/net/http/serve_test.go')
-rw-r--r--libgo/go/net/http/serve_test.go373
1 files changed, 361 insertions, 12 deletions
diff --git a/libgo/go/net/http/serve_test.go b/libgo/go/net/http/serve_test.go
index 384b453ce0a..139ce3eafc7 100644
--- a/libgo/go/net/http/serve_test.go
+++ b/libgo/go/net/http/serve_test.go
@@ -9,7 +9,10 @@ package http_test
import (
"bufio"
"bytes"
+ "compress/gzip"
+ "context"
"crypto/tls"
+ "encoding/json"
"errors"
"fmt"
"internal/testenv"
@@ -617,7 +620,7 @@ func TestIdentityResponse(t *testing.T) {
defer ts.Close()
// Note: this relies on the assumption (which is true) that
- // Get sends HTTP/1.1 or greater requests. Otherwise the
+ // Get sends HTTP/1.1 or greater requests. Otherwise the
// server wouldn't have the choice to send back chunked
// responses.
for _, te := range []string{"", "identity"} {
@@ -713,6 +716,31 @@ func testTCPConnectionCloses(t *testing.T, req string, h Handler) {
}
}
+func testTCPConnectionStaysOpen(t *testing.T, req string, handler Handler) {
+ defer afterTest(t)
+ ts := httptest.NewServer(handler)
+ defer ts.Close()
+ conn, err := net.Dial("tcp", ts.Listener.Addr().String())
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer conn.Close()
+ br := bufio.NewReader(conn)
+ for i := 0; i < 2; i++ {
+ if _, err := io.WriteString(conn, req); err != nil {
+ t.Fatal(err)
+ }
+ res, err := ReadResponse(br, nil)
+ if err != nil {
+ t.Fatalf("res %d: %v", i+1, err)
+ }
+ if _, err := io.Copy(ioutil.Discard, res.Body); err != nil {
+ t.Fatalf("res %d body copy: %v", i+1, err)
+ }
+ res.Body.Close()
+ }
+}
+
// TestServeHTTP10Close verifies that HTTP/1.0 requests won't be kept alive.
func TestServeHTTP10Close(t *testing.T) {
testTCPConnectionCloses(t, "GET / HTTP/1.0\r\n\r\n", HandlerFunc(func(w ResponseWriter, r *Request) {
@@ -741,6 +769,61 @@ func TestHandlersCanSetConnectionClose10(t *testing.T) {
}))
}
+func TestHTTP2UpgradeClosesConnection(t *testing.T) {
+ testTCPConnectionCloses(t, "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", HandlerFunc(func(w ResponseWriter, r *Request) {
+ // Nothing. (if not hijacked, the server should close the connection
+ // afterwards)
+ }))
+}
+
+func send204(w ResponseWriter, r *Request) { w.WriteHeader(204) }
+func send304(w ResponseWriter, r *Request) { w.WriteHeader(304) }
+
+// Issue 15647: 204 responses can't have bodies, so HTTP/1.0 keep-alive conns should stay open.
+func TestHTTP10KeepAlive204Response(t *testing.T) {
+ testTCPConnectionStaysOpen(t, "GET / HTTP/1.0\r\nConnection: keep-alive\r\n\r\n", HandlerFunc(send204))
+}
+
+func TestHTTP11KeepAlive204Response(t *testing.T) {
+ testTCPConnectionStaysOpen(t, "GET / HTTP/1.1\r\nHost: foo\r\n\r\n", HandlerFunc(send204))
+}
+
+func TestHTTP10KeepAlive304Response(t *testing.T) {
+ testTCPConnectionStaysOpen(t,
+ "GET / HTTP/1.0\r\nConnection: keep-alive\r\nIf-Modified-Since: Mon, 02 Jan 2006 15:04:05 GMT\r\n\r\n",
+ HandlerFunc(send304))
+}
+
+// Issue 15703
+func TestKeepAliveFinalChunkWithEOF(t *testing.T) {
+ defer afterTest(t)
+ cst := newClientServerTest(t, false /* h1 */, HandlerFunc(func(w ResponseWriter, r *Request) {
+ w.(Flusher).Flush() // force chunked encoding
+ w.Write([]byte("{\"Addr\": \"" + r.RemoteAddr + "\"}"))
+ }))
+ defer cst.close()
+ type data struct {
+ Addr string
+ }
+ var addrs [2]data
+ for i := range addrs {
+ res, err := cst.c.Get(cst.ts.URL)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if err := json.NewDecoder(res.Body).Decode(&addrs[i]); err != nil {
+ t.Fatal(err)
+ }
+ if addrs[i].Addr == "" {
+ t.Fatal("no address")
+ }
+ res.Body.Close()
+ }
+ if addrs[0] != addrs[1] {
+ t.Fatalf("connection not reused")
+ }
+}
+
func TestSetsRemoteAddr_h1(t *testing.T) { testSetsRemoteAddr(t, h1Mode) }
func TestSetsRemoteAddr_h2(t *testing.T) { testSetsRemoteAddr(t, h2Mode) }
@@ -984,7 +1067,7 @@ func TestTLSServer(t *testing.T) {
defer ts.Close()
// Connect an idle TCP connection to this server before we run
- // our real tests. This idle connection used to block forever
+ // our real tests. This idle connection used to block forever
// in the TLS handshake, preventing future connections from
// being accepted. It may prevent future accidental blocking
// in newConn.
@@ -1024,11 +1107,44 @@ func TestTLSServer(t *testing.T) {
})
}
-func TestAutomaticHTTP2_Serve(t *testing.T) {
+// Issue 15908
+func TestAutomaticHTTP2_Serve_NoTLSConfig(t *testing.T) {
+ testAutomaticHTTP2_Serve(t, nil, true)
+}
+
+func TestAutomaticHTTP2_Serve_NonH2TLSConfig(t *testing.T) {
+ testAutomaticHTTP2_Serve(t, &tls.Config{}, false)
+}
+
+func TestAutomaticHTTP2_Serve_H2TLSConfig(t *testing.T) {
+ testAutomaticHTTP2_Serve(t, &tls.Config{NextProtos: []string{"h2"}}, true)
+}
+
+func testAutomaticHTTP2_Serve(t *testing.T, tlsConf *tls.Config, wantH2 bool) {
defer afterTest(t)
ln := newLocalListener(t)
ln.Close() // immediately (not a defer!)
var s Server
+ s.TLSConfig = tlsConf
+ if err := s.Serve(ln); err == nil {
+ t.Fatal("expected an error")
+ }
+ gotH2 := s.TLSNextProto["h2"] != nil
+ if gotH2 != wantH2 {
+ t.Errorf("http2 configured = %v; want %v", gotH2, wantH2)
+ }
+}
+
+func TestAutomaticHTTP2_Serve_WithTLSConfig(t *testing.T) {
+ defer afterTest(t)
+ ln := newLocalListener(t)
+ ln.Close() // immediately (not a defer!)
+ var s Server
+ // Set the TLSConfig. In reality, this would be the
+ // *tls.Config given to tls.NewListener.
+ s.TLSConfig = &tls.Config{
+ NextProtos: []string{"h2"},
+ }
if err := s.Serve(ln); err == nil {
t.Fatal("expected an error")
}
@@ -1888,6 +2004,52 @@ func TestTimeoutHandlerRaceHeaderTimeout(t *testing.T) {
}
}
+// Issue 14568.
+func TestTimeoutHandlerStartTimerWhenServing(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping sleeping test in -short mode")
+ }
+ defer afterTest(t)
+ var handler HandlerFunc = func(w ResponseWriter, _ *Request) {
+ w.WriteHeader(StatusNoContent)
+ }
+ timeout := 300 * time.Millisecond
+ ts := httptest.NewServer(TimeoutHandler(handler, timeout, ""))
+ defer ts.Close()
+ // Issue was caused by the timeout handler starting the timer when
+ // was created, not when the request. So wait for more than the timeout
+ // to ensure that's not the case.
+ time.Sleep(2 * timeout)
+ res, err := Get(ts.URL)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer res.Body.Close()
+ if res.StatusCode != StatusNoContent {
+ t.Errorf("got res.StatusCode %d, want %v", res.StatusCode, StatusNoContent)
+ }
+}
+
+// https://golang.org/issue/15948
+func TestTimeoutHandlerEmptyResponse(t *testing.T) {
+ defer afterTest(t)
+ var handler HandlerFunc = func(w ResponseWriter, _ *Request) {
+ // No response.
+ }
+ timeout := 300 * time.Millisecond
+ ts := httptest.NewServer(TimeoutHandler(handler, timeout, ""))
+ defer ts.Close()
+
+ res, err := Get(ts.URL)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer res.Body.Close()
+ if res.StatusCode != StatusOK {
+ t.Errorf("got res.StatusCode %d, want %v", res.StatusCode, StatusOK)
+ }
+}
+
// Verifies we don't path.Clean() on the wrong parts in redirects.
func TestRedirectMunging(t *testing.T) {
req, _ := NewRequest("GET", "http://example.com/", nil)
@@ -2027,7 +2189,7 @@ func TestHandlerPanicWithHijack(t *testing.T) {
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
+ // to quiet the output, this test uses a pipe. The pipe serves three
// purposes:
//
// 1) The log.Print from the http server (generated by the caught
@@ -2060,7 +2222,7 @@ func testHandlerPanic(t *testing.T, withHijack, h2 bool, panicValue interface{})
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
+ // doesn't bleed into the next test. But wait only 5 seconds
// for it.
done := make(chan bool, 1)
go func() {
@@ -2205,10 +2367,10 @@ func testRequestBodyLimit(t *testing.T, h2 bool) {
nWritten := new(int64)
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
+ // Send the POST, but don't care it succeeds or not. The
// remote side is going to reply and then close the TCP
// connection, and HTTP doesn't really define if that's
- // allowed or not. Some HTTP clients will get the response
+ // allowed or not. Some HTTP clients will get the response
// and some (like ours, currently) will complain that the
// request write failed, without reading the response.
//
@@ -2650,7 +2812,7 @@ func TestOptions(t *testing.T) {
}
// Tests regarding the ordering of Write, WriteHeader, Header, and
-// Flush calls. In Go 1.0, rw.WriteHeader immediately flushed the
+// Flush calls. In Go 1.0, rw.WriteHeader immediately flushed the
// (*response).header to the wire. In Go 1.1, the actual wire flush is
// delayed, so we could maybe tack on a Content-Length and better
// Content-Type after we see more (or all) of the output. To preserve
@@ -3107,7 +3269,7 @@ func testTransportAndServerSharedBodyRace(t *testing.T, h2 bool) {
const bodySize = 1 << 20
- // errorf is like t.Errorf, but also writes to println. When
+ // 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.
@@ -3829,6 +3991,8 @@ func TestServerValidatesHostHeader(t *testing.T) {
host string
want int
}{
+ {"HTTP/0.9", "", 400},
+
{"HTTP/1.1", "", 400},
{"HTTP/1.1", "Host: \r\n", 200},
{"HTTP/1.1", "Host: 1.2.3.4\r\n", 200},
@@ -3851,10 +4015,22 @@ func TestServerValidatesHostHeader(t *testing.T) {
{"HTTP/1.0", "", 200},
{"HTTP/1.0", "Host: first\r\nHost: second\r\n", 400},
{"HTTP/1.0", "Host: \xff\r\n", 400},
+
+ // Make an exception for HTTP upgrade requests:
+ {"PRI * HTTP/2.0", "", 200},
+
+ // But not other HTTP/2 stuff:
+ {"PRI / HTTP/2.0", "", 400},
+ {"GET / HTTP/2.0", "", 400},
+ {"GET / HTTP/3.0", "", 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")
+ methodTarget := "GET / "
+ if !strings.HasPrefix(tt.proto, "HTTP/") {
+ methodTarget = ""
+ }
+ io.WriteString(&conn.readBuf, methodTarget+tt.proto+"\r\n"+tt.host+"\r\n")
ln := &oneConnListener{conn}
go Serve(ln, HandlerFunc(func(ResponseWriter, *Request) {}))
@@ -3870,6 +4046,45 @@ func TestServerValidatesHostHeader(t *testing.T) {
}
}
+func TestServerHandlersCanHandleH2PRI(t *testing.T) {
+ const upgradeResponse = "upgrade here"
+ defer afterTest(t)
+ ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ conn, br, err := w.(Hijacker).Hijack()
+ defer conn.Close()
+ if r.Method != "PRI" || r.RequestURI != "*" {
+ t.Errorf("Got method/target %q %q; want PRI *", r.Method, r.RequestURI)
+ return
+ }
+ if !r.Close {
+ t.Errorf("Request.Close = true; want false")
+ }
+ const want = "SM\r\n\r\n"
+ buf := make([]byte, len(want))
+ n, err := io.ReadFull(br, buf)
+ if err != nil || string(buf[:n]) != want {
+ t.Errorf("Read = %v, %v (%q), want %q", n, err, buf[:n], want)
+ return
+ }
+ io.WriteString(conn, upgradeResponse)
+ }))
+ defer ts.Close()
+
+ c, err := net.Dial("tcp", ts.Listener.Addr().String())
+ if err != nil {
+ t.Fatalf("Dial: %v", err)
+ }
+ defer c.Close()
+ io.WriteString(c, "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n")
+ slurp, err := ioutil.ReadAll(c)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if string(slurp) != upgradeResponse {
+ t.Errorf("Handler response = %q; want %q", slurp, upgradeResponse)
+ }
+}
+
// Test that we validate the valid bytes in HTTP/1 headers.
// Issue 11207.
func TestServerValidatesHeaders(t *testing.T) {
@@ -3910,6 +4125,140 @@ func TestServerValidatesHeaders(t *testing.T) {
}
}
+func TestServerRequestContextCancel_ServeHTTPDone_h1(t *testing.T) {
+ testServerRequestContextCancel_ServeHTTPDone(t, h1Mode)
+}
+func TestServerRequestContextCancel_ServeHTTPDone_h2(t *testing.T) {
+ testServerRequestContextCancel_ServeHTTPDone(t, h2Mode)
+}
+func testServerRequestContextCancel_ServeHTTPDone(t *testing.T, h2 bool) {
+ defer afterTest(t)
+ ctxc := make(chan context.Context, 1)
+ cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) {
+ ctx := r.Context()
+ select {
+ case <-ctx.Done():
+ t.Error("should not be Done in ServeHTTP")
+ default:
+ }
+ ctxc <- ctx
+ }))
+ defer cst.close()
+ res, err := cst.c.Get(cst.ts.URL)
+ if err != nil {
+ t.Fatal(err)
+ }
+ res.Body.Close()
+ ctx := <-ctxc
+ select {
+ case <-ctx.Done():
+ default:
+ t.Error("context should be done after ServeHTTP completes")
+ }
+}
+
+func TestServerRequestContextCancel_ConnClose(t *testing.T) {
+ // Currently the context is not canceled when the connection
+ // is closed because we're not reading from the connection
+ // until after ServeHTTP for the previous handler is done.
+ // Until the server code is modified to always be in a read
+ // (Issue 15224), this test doesn't work yet.
+ t.Skip("TODO(bradfitz): this test doesn't yet work; golang.org/issue/15224")
+ defer afterTest(t)
+ inHandler := make(chan struct{})
+ handlerDone := make(chan struct{})
+ ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+ close(inHandler)
+ select {
+ case <-r.Context().Done():
+ case <-time.After(3 * time.Second):
+ t.Errorf("timeout waiting for context to be done")
+ }
+ close(handlerDone)
+ }))
+ defer ts.Close()
+ c, err := net.Dial("tcp", ts.Listener.Addr().String())
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer c.Close()
+ io.WriteString(c, "GET / HTTP/1.1\r\nHost: foo\r\n\r\n")
+ select {
+ case <-inHandler:
+ case <-time.After(3 * time.Second):
+ t.Fatalf("timeout waiting to see ServeHTTP get called")
+ }
+ c.Close() // this should trigger the context being done
+
+ select {
+ case <-handlerDone:
+ case <-time.After(3 * time.Second):
+ t.Fatalf("timeout waiting to see ServeHTTP exit")
+ }
+}
+
+func TestServerContext_ServerContextKey_h1(t *testing.T) {
+ testServerContext_ServerContextKey(t, h1Mode)
+}
+func TestServerContext_ServerContextKey_h2(t *testing.T) {
+ testServerContext_ServerContextKey(t, h2Mode)
+}
+func testServerContext_ServerContextKey(t *testing.T, h2 bool) {
+ defer afterTest(t)
+ cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) {
+ ctx := r.Context()
+ got := ctx.Value(ServerContextKey)
+ if _, ok := got.(*Server); !ok {
+ t.Errorf("context value = %T; want *http.Server", got)
+ }
+
+ got = ctx.Value(LocalAddrContextKey)
+ if addr, ok := got.(net.Addr); !ok {
+ t.Errorf("local addr value = %T; want net.Addr", got)
+ } else if fmt.Sprint(addr) != r.Host {
+ t.Errorf("local addr = %v; want %v", addr, r.Host)
+ }
+ }))
+ defer cst.close()
+ res, err := cst.c.Get(cst.ts.URL)
+ if err != nil {
+ t.Fatal(err)
+ }
+ res.Body.Close()
+}
+
+// https://golang.org/issue/15960
+func TestHandlerSetTransferEncodingChunked(t *testing.T) {
+ defer afterTest(t)
+ ht := newHandlerTest(HandlerFunc(func(w ResponseWriter, r *Request) {
+ w.Header().Set("Transfer-Encoding", "chunked")
+ w.Write([]byte("hello"))
+ }))
+ resp := ht.rawResponse("GET / HTTP/1.1\nHost: foo")
+ const hdr = "Transfer-Encoding: chunked"
+ if n := strings.Count(resp, hdr); n != 1 {
+ t.Errorf("want 1 occurrence of %q in response, got %v\nresponse: %v", hdr, n, resp)
+ }
+}
+
+// https://golang.org/issue/16063
+func TestHandlerSetTransferEncodingGzip(t *testing.T) {
+ defer afterTest(t)
+ ht := newHandlerTest(HandlerFunc(func(w ResponseWriter, r *Request) {
+ w.Header().Set("Transfer-Encoding", "gzip")
+ gz := gzip.NewWriter(w)
+ gz.Write([]byte("hello"))
+ gz.Close()
+ }))
+ resp := ht.rawResponse("GET / HTTP/1.1\nHost: foo")
+ for _, v := range []string{"gzip", "chunked"} {
+ hdr := "Transfer-Encoding: " + v
+ if n := strings.Count(resp, hdr); n != 1 {
+ t.Errorf("want 1 occurrence of %q in response, got %v\nresponse: %v", hdr, n, resp)
+ }
+ }
+}
+
func BenchmarkClientServer(b *testing.B) {
b.ReportAllocs()
b.StopTimer()
@@ -4100,7 +4449,7 @@ func BenchmarkClient(b *testing.B) {
// Wait for the server process to respond.
url := "http://localhost:" + port + "/"
for i := 0; i < 100; i++ {
- time.Sleep(50 * time.Millisecond)
+ time.Sleep(100 * time.Millisecond)
if _, err := getNoBody(url); err == nil {
break
}
@@ -4121,7 +4470,7 @@ func BenchmarkClient(b *testing.B) {
if err != nil {
b.Fatalf("ReadAll: %v", err)
}
- if bytes.Compare(body, data) != 0 {
+ if !bytes.Equal(body, data) {
b.Fatalf("Got body: %q", body)
}
}