func TestReverseProxy(t *testing.T) { const backendResponse = "I am the backend" const backendStatus = 404 backend := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { if r.Header.Get("X-Forwarded-For") == "" { t.Errorf("didn't get X-Forwarded-For header") } w.Header().Set("X-Foo", "bar") w.WriteHeader(backendStatus) w.Write([]byte(backendResponse)) })) defer backend.Close() backendURL, err := ParseURL(backend.URL) if err != nil { t.Fatal(err) } proxyHandler := NewSingleHostReverseProxy(backendURL) frontend := httptest.NewServer(proxyHandler) defer frontend.Close() res, _, err := Get(frontend.URL) if err != nil { t.Fatalf("Get: %v", err) } if g, e := res.StatusCode, backendStatus; g != e { t.Errorf("got res.StatusCode %d; expected %d", g, e) } if g, e := res.Header.Get("X-Foo"), "bar"; g != e { t.Errorf("got X-Foo %q; expected %q", g, e) } bodyBytes, _ := ioutil.ReadAll(res.Body) if g, e := string(bodyBytes), backendResponse; g != e { t.Errorf("got body %q; expected %q", g, e) } }
func TestReverseProxy(t *testing.T) { const backendResponse = "I am the backend" const backendStatus = 404 backend := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { if len(r.TransferEncoding) > 0 { t.Errorf("backend got unexpected TransferEncoding: %v", r.TransferEncoding) } if r.Header.Get("X-Forwarded-For") == "" { t.Errorf("didn't get X-Forwarded-For header") } if c := r.Header.Get("Connection"); c != "" { t.Errorf("handler got Connection header value %q", c) } if g, e := r.Host, "some-name"; g != e { t.Errorf("backend got Host header %q, want %q", g, e) } w.Header().Set("X-Foo", "bar") SetCookie(w, &Cookie{Name: "flavor", Value: "chocolateChip"}) w.WriteHeader(backendStatus) w.Write([]byte(backendResponse)) })) defer backend.Close() backendURL, err := url.Parse(backend.URL) if err != nil { t.Fatal(err) } proxyHandler := NewSingleHostReverseProxy(backendURL) frontend := httptest.NewServer(proxyHandler) defer frontend.Close() getReq, _ := NewRequest("GET", frontend.URL, nil) getReq.Host = "some-name" getReq.Header.Set("Connection", "close") getReq.Close = true res, err := DefaultClient.Do(getReq) if err != nil { t.Fatalf("Get: %v", err) } if g, e := res.StatusCode, backendStatus; g != e { t.Errorf("got res.StatusCode %d; expected %d", g, e) } if g, e := res.Header.Get("X-Foo"), "bar"; g != e { t.Errorf("got X-Foo %q; expected %q", g, e) } if g, e := len(res.Header["Set-Cookie"]), 1; g != e { t.Fatalf("got %d SetCookies, want %d", g, e) } if cookie := res.Cookies()[0]; cookie.Name != "flavor" { t.Errorf("unexpected cookie %q", cookie.Name) } bodyBytes, _ := ioutil.ReadAll(res.Body) if g, e := string(bodyBytes), backendResponse; g != e { t.Errorf("got body %q; expected %q", g, e) } }
func TestTransportServerClosingUnexpectedly(t *testing.T) { ts := httptest.NewServer(hostPortHandler) defer ts.Close() tr := &Transport{} c := &Client{Transport: tr} fetch := func(n int) string { res, _, err := c.Get(ts.URL) if err != nil { t.Fatalf("error in req #%d, GET: %v", n, err) } body, err := ioutil.ReadAll(res.Body) if err != nil { t.Fatalf("error in req #%d, ReadAll: %v", n, err) } res.Body.Close() return string(body) } body1 := fetch(1) body2 := fetch(2) ts.CloseClientConnections() // surprise! time.Sleep(25e6) // idle for a bit (test is inherently racey, but expectedly) body3 := fetch(3) if body1 != body2 { t.Errorf("expected body1 and body2 to be equal") } if body2 == body3 { t.Errorf("expected body2 and body3 to be different") } }
// TestTransportHeadResponses verifies that we deal with Content-Lengths // with no bodies properly func TestTransportHeadResponses(t *testing.T) { ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { if r.Method != "HEAD" { panic("expected HEAD; got " + r.Method) } w.Header().Set("Content-Length", "123") w.WriteHeader(200) })) defer ts.Close() tr := &Transport{DisableKeepAlives: false} c := &Client{Transport: tr} for i := 0; i < 2; i++ { res, err := c.Head(ts.URL) if err != nil { t.Errorf("error on loop %d: %v", i, err) } if e, g := "123", res.Header.Get("Content-Length"); e != g { t.Errorf("loop %d: expected Content-Length header of %q, got %q", i, e, g) } if e, g := int64(0), res.ContentLength; e != g { t.Errorf("loop %d: expected res.ContentLength of %v, got %v", i, e, g) } } }
func TestServerContentType(t *testing.T) { ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { i, _ := strconv.Atoi(r.FormValue("i")) tt := sniffTests[i] n, err := w.Write(tt.data) if n != len(tt.data) || err != nil { log.Fatalf("%v: Write(%q) = %v, %v want %d, nil", tt.desc, tt.data, n, err, len(tt.data)) } })) defer ts.Close() for i, tt := range sniffTests { resp, err := Get(ts.URL + "/?i=" + strconv.Itoa(i)) if err != nil { t.Errorf("%v: %v", tt.desc, err) continue } if ct := resp.Header.Get("Content-Type"); ct != tt.contentType { t.Errorf("%v: Content-Type = %q, want %q", tt.desc, ct, tt.contentType) } data, err := ioutil.ReadAll(resp.Body) if err != nil { t.Errorf("%v: reading body: %v", tt.desc, err) } else if !bytes.Equal(data, tt.data) { t.Errorf("%v: data is %q, want %q", tt.desc, data, tt.data) } resp.Body.Close() } }
func startServer() { http.Handle("/echo", Handler(echoServer)) http.Handle("/echoDraft75", Draft75Handler(echoServer)) server := httptest.NewServer(nil) serverAddr = server.Listener.Addr().String() log.Print("Test WebSocket server listening on ", serverAddr) }
// TestTransportHeadChunkedResponse verifies that we ignore chunked transfer-encoding // on responses to HEAD requests. func TestTransportHeadChunkedResponse(t *testing.T) { ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { if r.Method != "HEAD" { panic("expected HEAD; got " + r.Method) } w.Header().Set("Transfer-Encoding", "chunked") // client should ignore w.Header().Set("x-client-ipport", r.RemoteAddr) w.WriteHeader(200) })) defer ts.Close() tr := &Transport{DisableKeepAlives: false} c := &Client{Transport: tr} res1, err := c.Head(ts.URL) if err != nil { t.Fatalf("request 1 error: %v", err) } res2, err := c.Head(ts.URL) if err != nil { t.Fatalf("request 2 error: %v", err) } if v1, v2 := res1.Header.Get("x-client-ipport"), res2.Header.Get("x-client-ipport"); v1 != v2 { t.Errorf("ip/ports differed between head requests: %q vs %q", v1, v2) } }
func TestTransportIdleCacheKeys(t *testing.T) { ts := httptest.NewServer(hostPortHandler) defer ts.Close() tr := &Transport{DisableKeepAlives: false} c := &Client{Transport: tr} if e, g := 0, len(tr.IdleConnKeysForTesting()); e != g { t.Errorf("After CloseIdleConnections expected %d idle conn cache keys; got %d", e, g) } resp, _, err := c.Get(ts.URL) if err != nil { t.Error(err) } ioutil.ReadAll(resp.Body) keys := tr.IdleConnKeysForTesting() if e, g := 1, len(keys); e != g { t.Fatalf("After Get expected %d idle conn cache keys; got %d", e, g) } if e := "|http|" + ts.Listener.Addr().String(); keys[0] != e { t.Errorf("Expected idle cache key %q; got %q", e, keys[0]) } tr.CloseIdleConnections() if e, g := 0, len(tr.IdleConnKeysForTesting()); e != g { t.Errorf("After CloseIdleConnections expected %d idle conn cache keys; got %d", e, g) } }
// TestHeadResponses verifies that responses to HEAD requests don't // declare that they're chunking in their response headers and aren't // allowed to produce output. func TestHeadResponses(t *testing.T) { ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { _, err := w.Write([]byte("Ignored body")) if err != ErrBodyNotAllowed { t.Errorf("on Write, expected ErrBodyNotAllowed, got %v", err) } // Also exercise the ReaderFrom path _, err = io.Copy(w, strings.NewReader("Ignored body")) if err != ErrBodyNotAllowed { t.Errorf("on Copy, expected ErrBodyNotAllowed, got %v", err) } })) defer ts.Close() res, err := Head(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)) } }
// TestClientWrites verifies that client requests are buffered and we // don't send a TCP packet per line of the http request + body. func TestClientWrites(t *testing.T) { ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { })) defer ts.Close() writes := 0 dialer := func(netz string, addr string) (net.Conn, os.Error) { c, err := net.Dial(netz, addr) if err == nil { c = &writeCountingConn{c, &writes} } return c, err } c := &Client{Transport: &Transport{Dial: dialer}} _, err := c.Get(ts.URL) if err != nil { t.Fatal(err) } if writes != 1 { t.Errorf("Get request did %d Write calls, want 1", writes) } writes = 0 _, err = c.PostForm(ts.URL, url.Values{"foo": {"bar"}}) if err != nil { t.Fatal(err) } if writes != 1 { t.Errorf("Post request did %d Write calls, want 1", writes) } }
func TestRequestBodyLimit(t *testing.T) { const limit = 1 << 20 ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { r.Body = MaxBytesReader(w, r.Body, limit) n, err := io.Copy(ioutil.Discard, r.Body) if err == nil { t.Errorf("expected error from io.Copy") } if n != limit { t.Errorf("io.Copy = %d, want %d", n, limit) } })) defer ts.Close() nWritten := int64(0) req, _ := NewRequest("POST", 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 // connection, and HTTP doesn't really define if that's // 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. // // 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) if nWritten > limit*100 { t.Errorf("handler restricted the request body to %d bytes, but client managed to write %d", limit, nWritten) } }
// TestClientWriteShutdown tests that if the client shuts down the write // side of their TCP connection, the server doesn't send a 400 Bad Request. func TestClientWriteShutdown(t *testing.T) { ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {})) defer ts.Close() conn, err := net.Dial("tcp", ts.Listener.Addr().String()) if err != nil { t.Fatalf("Dial: %v", err) } err = conn.(*net.TCPConn).CloseWrite() if err != nil { t.Fatalf("Dial: %v", err) } donec := make(chan bool) go func() { defer close(donec) bs, err := ioutil.ReadAll(conn) if err != nil { t.Fatalf("ReadAll: %v", err) } got := string(bs) if got != "" { t.Errorf("read %q from server; want nothing", got) } }() select { case <-donec: case <-time.After(10e9): t.Fatalf("timeout") } }
func TestRedirect(t *testing.T) { ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { switch r.URL.Path { case "/": w.Header().Set("Location", "/foo/") w.WriteHeader(StatusSeeOther) case "/foo/": fmt.Fprintf(w, "foo") default: w.WriteHeader(StatusBadRequest) } })) defer ts.Close() var end = regexp.MustCompile("/foo/$") r, err := Get(ts.URL) if err != nil { t.Fatal(err) } r.Body.Close() url := r.Request.URL.String() if r.StatusCode != 200 || !end.MatchString(url) { t.Fatalf("Get got status %d at %q, want 200 matching /foo/$", r.StatusCode, url) } }
func BenchmarkClientServer(b *testing.B) { b.StopTimer() ts := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, r *Request) { fmt.Fprintf(rw, "Hello world.\n") })) defer ts.Close() b.StartTimer() for i := 0; i < b.N; i++ { res, err := Get(ts.URL) if err != nil { panic("Get: " + err.String()) } all, err := ioutil.ReadAll(res.Body) if err != nil { panic("ReadAll: " + err.String()) } body := string(all) if body != "Hello world.\n" { panic("Got body: " + body) } } b.StopTimer() }
func TestHostHandlers(t *testing.T) { for _, h := range handlers { Handle(h.pattern, stringHandler(h.msg)) } ts := httptest.NewServer(nil) defer ts.Close() conn, err := net.Dial("tcp", ts.Listener.Addr().String()) if err != nil { t.Fatal(err) } defer conn.Close() cc := NewClientConn(conn, nil) for _, vt := range vtests { var r *Response var req Request if req.URL, err = ParseURL(vt.url); err != nil { t.Errorf("cannot parse url: %v", err) continue } if err := cc.Write(&req); err != nil { t.Errorf("writing request: %v", err) continue } r, err := cc.Read(&req) if err != nil { t.Errorf("reading response: %v", err) continue } s := r.Header.Get("Result") if s != vt.expected { t.Errorf("Get(%q) = %q, want %q", vt.url, s, vt.expected) } } }
func TestFileServerImplicitLeadingSlash(t *testing.T) { tempDir, err := ioutil.TempDir("", "") if err != nil { t.Fatalf("TempDir: %v", err) } defer os.RemoveAll(tempDir) if err := ioutil.WriteFile(filepath.Join(tempDir, "foo.txt"), []byte("Hello world"), 0644); err != nil { t.Fatalf("WriteFile: %v", err) } ts := httptest.NewServer(StripPrefix("/bar/", FileServer(Dir(tempDir)))) defer ts.Close() get := func(suffix string) string { res, err := Get(ts.URL + suffix) if err != nil { t.Fatalf("Get %s: %v", suffix, err) } b, err := ioutil.ReadAll(res.Body) if err != nil { t.Fatalf("ReadAll %s: %v", suffix, err) } return string(b) } if s := get("/bar/"); !strings.Contains(s, ">foo.txt<") { t.Logf("expected a directory listing with foo.txt, got %q", s) } if s := get("/bar/foo.txt"); s != "Hello world" { t.Logf("expected %q, got %q", "Hello world", s) } }
func TestTransportNilURL(t *testing.T) { ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { fmt.Fprintf(w, "Hi") })) defer ts.Close() req := new(Request) req.URL = nil // what we're actually testing req.Method = "GET" req.RawURL = ts.URL req.Proto = "HTTP/1.1" req.ProtoMajor = 1 req.ProtoMinor = 1 req.Header = make(Header) tr := &Transport{} res, err := tr.RoundTrip(req) if err != nil { t.Fatalf("unexpected RoundTrip error: %v", err) } body, err := ioutil.ReadAll(res.Body) if g, e := string(body), "Hi"; g != e { t.Fatalf("Expected response body of %q; got %q", e, g) } }
// Two subsequent requests and verify their response is the same. // The response from the server is our own IP:port func TestTransportKeepAlives(t *testing.T) { ts := httptest.NewServer(hostPortHandler) defer ts.Close() for _, disableKeepAlive := range []bool{false, true} { tr := &Transport{DisableKeepAlives: disableKeepAlive} c := &Client{Transport: tr} fetch := func(n int) string { res, _, err := c.Get(ts.URL) if err != nil { t.Fatalf("error in disableKeepAlive=%v, req #%d, GET: %v", disableKeepAlive, n, err) } body, err := ioutil.ReadAll(res.Body) if err != nil { t.Fatalf("error in disableKeepAlive=%v, req #%d, ReadAll: %v", disableKeepAlive, n, err) } return string(body) } body1 := fetch(1) body2 := fetch(2) bodiesDiffer := body1 != body2 if bodiesDiffer != disableKeepAlive { t.Errorf("error in disableKeepAlive=%v. unexpected bodiesDiffer=%v; body1=%q; body2=%q", disableKeepAlive, bodiesDiffer, body1, body2) } } }
func testHandlerPanic(t *testing.T, withHijack bool) { // 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 // purposes: // // 1) The log.Print from the http server (generated by the caught // panic) will go to the pipe instead of stderr, making the // output quiet. // // 2) We read from the pipe to verify that the handler // actually caught the panic and logged something. // // 3) The blocking Read call prevents this TestHandlerPanic // function from exiting before the HTTP server handler // finishes crashing. If this text function exited too // early (and its defer log.SetOutput(os.Stderr) ran), // then the crash output could spill into the next test. pr, pw := io.Pipe() log.SetOutput(pw) defer log.SetOutput(os.Stderr) ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { if withHijack { rwc, _, err := w.(Hijacker).Hijack() if err != nil { t.Logf("unexpected error: %v", err) } defer rwc.Close() } panic("intentional death for testing") })) defer ts.Close() _, err := Get(ts.URL) if err == nil { t.Logf("expected an error") } // Do a blocking read on the log output pipe so its logging // doesn't bleed into the next test. But wait only 5 seconds // for it. done := make(chan bool) go func() { buf := make([]byte, 1024) _, err := pr.Read(buf) pr.Close() if err != nil { t.Fatal(err) } done <- true }() select { case <-done: return case <-time.After(5e9): t.Fatal("expected server handler to log an error") } }
func TestTransportMaxPerHostIdleConns(t *testing.T) { ch := make(chan string) ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { fmt.Fprintf(w, "%s", <-ch) })) defer ts.Close() maxIdleConns := 2 tr := &Transport{DisableKeepAlives: false, MaxIdleConnsPerHost: maxIdleConns} c := &Client{Transport: tr} // Start 3 outstanding requests (will hang until we write to // ch) donech := make(chan bool) doReq := func() { resp, _, err := c.Get(ts.URL) if err != nil { t.Error(err) } ioutil.ReadAll(resp.Body) donech <- true } go doReq() go doReq() go doReq() if e, g := 0, len(tr.IdleConnKeysForTesting()); e != g { t.Fatalf("Before writes, expected %d idle conn cache keys; got %d", e, g) } ch <- "res1" <-donech keys := tr.IdleConnKeysForTesting() if e, g := 1, len(keys); e != g { t.Fatalf("after first response, expected %d idle conn cache keys; got %d", e, g) } cacheKey := "|http|" + ts.Listener.Addr().String() if keys[0] != cacheKey { t.Fatalf("Expected idle cache key %q; got %q", cacheKey, keys[0]) } if e, g := 1, tr.IdleConnCountForTesting(cacheKey); e != g { t.Errorf("after first response, expected %d idle conns; got %d", e, g) } ch <- "res2" <-donech if e, g := 2, tr.IdleConnCountForTesting(cacheKey); e != g { t.Errorf("after second response, expected %d idle conns; got %d", e, g) } ch <- "res3" <-donech if e, g := maxIdleConns, tr.IdleConnCountForTesting(cacheKey); e != g { t.Errorf("after third response, still expected %d idle conns; got %d", e, g) } }
// Create a new server for use in unit tests. When done, be sure to call // Close(). func NewTestServer() *TestServer { handler := newHandler() server := httptest.NewServer(handler) url, err := url.Parse(server.URL) if err != nil { panic(err) } handler.registry.RegisterThisServer(url.Host) return &TestServer{handler: handler, server: server} }
func TestTransportServerClosingUnexpectedly(t *testing.T) { ts := httptest.NewServer(hostPortHandler) defer ts.Close() tr := &Transport{} c := &Client{Transport: tr} fetch := func(n, retries int) string { condFatalf := func(format string, arg ...interface{}) { if retries <= 0 { t.Fatalf(format, arg...) } t.Logf("retrying shortly after expected error: "+format, arg...) time.Sleep(1e9 / int64(retries)) } for retries >= 0 { retries-- res, _, err := c.Get(ts.URL) if err != nil { condFatalf("error in req #%d, GET: %v", n, err) continue } body, err := ioutil.ReadAll(res.Body) if err != nil { condFatalf("error in req #%d, ReadAll: %v", n, err) continue } res.Body.Close() return string(body) } panic("unreachable") } body1 := fetch(1, 0) body2 := fetch(2, 0) ts.CloseClientConnections() // surprise! // This test has an expected race. Sleeping for 25 ms prevents // it on most fast machines, causing the next fetch() call to // succeed quickly. But if we do get errors, fetch() will retry 5 // times with some delays between. time.Sleep(25e6) body3 := fetch(3, 5) if body1 != body2 { t.Errorf("expected body1 and body2 to be equal") } if body2 == body3 { t.Errorf("expected body2 and body3 to be different") } }
func TestClientHead(t *testing.T) { ts := httptest.NewServer(robotsTxtHandler) defer ts.Close() r, err := Head(ts.URL) if err != nil { t.Fatal(err) } if _, ok := r.Header["Last-Modified"]; !ok { t.Error("Last-Modified header not found.") } }
func TestServeFile(t *testing.T) { ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { ServeFile(w, r, "testdata/file") })) defer ts.Close() var err os.Error file, err := ioutil.ReadFile(testFile) if err != nil { t.Fatal("reading file:", err) } // set up the Request (re-used for all tests) var req Request req.Header = make(Header) if req.URL, err = ParseURL(ts.URL); err != nil { t.Fatal("ParseURL:", err) } req.Method = "GET" // straight GET _, body := getBody(t, req) if !equal(body, file) { t.Fatalf("body mismatch: got %q, want %q", body, file) } // Range tests for _, rt := range ServeFileRangeTests { req.Header.Set("Range", "bytes="+rt.r) if rt.r == "" { req.Header["Range"] = nil } r, body := getBody(t, req) if r.StatusCode != rt.code { t.Errorf("range=%q: StatusCode=%d, want %d", rt.r, r.StatusCode, rt.code) } if rt.code == StatusRequestedRangeNotSatisfiable { continue } h := fmt.Sprintf("bytes %d-%d/%d", rt.start, rt.end-1, testFileLength) if rt.r == "" { h = "" } cr := r.Header.Get("Content-Range") if cr != h { t.Errorf("header mismatch: range=%q: got %q, want %q", rt.r, cr, h) } if !equal(body, file[rt.start:rt.end]) { t.Errorf("body mismatch: range=%q: got %q, want %q", rt.r, body, file[rt.start:rt.end]) } } }
func TestHandlerPanic(t *testing.T) { log.SetOutput(ioutil.Discard) // is noisy otherwise defer log.SetOutput(os.Stderr) ts := httptest.NewServer(HandlerFunc(func(ResponseWriter, *Request) { panic("intentional death for testing") })) defer ts.Close() _, err := Get(ts.URL) if err == nil { t.Logf("expected an error") } }
// Tests that the server responds to the "Expect" request header // correctly. func TestServerExpect(t *testing.T) { ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { // Note using r.FormValue("readbody") because for POST // requests that would read from r.Body, which we only // conditionally want to do. if strings.Contains(r.URL.RawPath, "readbody=true") { ioutil.ReadAll(r.Body) w.Write([]byte("Hi")) } else { w.WriteHeader(StatusUnauthorized) } })) defer ts.Close() runTest := func(test serverExpectTest) { conn, err := net.Dial("tcp", ts.Listener.Addr().String()) if err != nil { t.Fatalf("Dial: %v", err) } defer conn.Close() sendf := func(format string, args ...interface{}) { _, err := fmt.Fprintf(conn, format, args...) if err != nil { t.Fatalf("On test %#v, error writing %q: %v", test, format, err) } } go func() { sendf("POST /?readbody=%v HTTP/1.1\r\n"+ "Connection: close\r\n"+ "Content-Length: %d\r\n"+ "Expect: %s\r\nHost: foo\r\n\r\n", test.readBody, test.contentLength, test.expectation) if test.contentLength > 0 && strings.ToLower(test.expectation) != "100-continue" { body := strings.Repeat("A", test.contentLength) sendf(body) } }() bufr := bufio.NewReader(conn) line, err := bufr.ReadString('\n') if err != nil { t.Fatalf("ReadString: %v", err) } if !strings.Contains(line, test.expectedResponse) { t.Errorf("for test %#v got first line=%q", test, line) } } for _, test := range serverExpectTests { runTest(test) } }
// Test that the modification made to the Request by the RoundTripper is cleaned up func TestRoundTripGzip(t *testing.T) { const responseBody = "test response body" ts := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, req *Request) { accept := req.Header.Get("Accept-Encoding") if expect := req.FormValue("expect_accept"); accept != expect { t.Errorf("in handler, test %v: Accept-Encoding = %q, want %q", req.FormValue("testnum"), accept, expect) } if accept == "gzip" { rw.Header().Set("Content-Encoding", "gzip") gz, _ := gzip.NewWriter(rw) gz.Write([]byte(responseBody)) gz.Close() } else { rw.Header().Set("Content-Encoding", accept) rw.Write([]byte(responseBody)) } })) defer ts.Close() for i, test := range roundTripTests { // Test basic request (no accept-encoding) req, _ := NewRequest("GET", fmt.Sprintf("%s/?testnum=%d&expect_accept=%s", ts.URL, i, test.expectAccept), nil) if test.accept != "" { req.Header.Set("Accept-Encoding", test.accept) } res, err := DefaultTransport.RoundTrip(req) var body []byte if test.compressed { gzip, _ := gzip.NewReader(res.Body) body, err = ioutil.ReadAll(gzip) res.Body.Close() } else { body, err = ioutil.ReadAll(res.Body) } if err != nil { t.Errorf("%d. Error: %q", i, err) continue } if g, e := string(body), responseBody; g != e { t.Errorf("%d. body = %q; want %q", i, g, e) } if g, e := req.Header.Get("Accept-Encoding"), test.accept; g != e { t.Errorf("%d. Accept-Encoding = %q; want %q (it was mutated, in violation of RoundTrip contract)", i, g, e) } if g, e := res.Header.Get("Content-Encoding"), test.accept; g != e { t.Errorf("%d. Content-Encoding = %q; want %q", i, g, e) } } }
func TestTimeoutHandler(t *testing.T) { sendHi := make(chan bool, 1) writeErrors := make(chan os.Error, 1) sayHi := HandlerFunc(func(w ResponseWriter, r *Request) { <-sendHi _, werr := w.Write([]byte("hi")) writeErrors <- werr }) timeout := make(chan int64, 1) // write to this to force timeouts ts := httptest.NewServer(NewTestTimeoutHandler(sayHi, timeout)) defer ts.Close() // Succeed without timing out: sendHi <- true res, err := Get(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 <- 1 res, err = Get(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) } }
func TestTransportProxy(t *testing.T) { ch := make(chan string, 1) ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { ch <- "real server" })) defer ts.Close() proxy := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { ch <- "proxy for " + r.URL.String() })) defer proxy.Close() pu, err := url.Parse(proxy.URL) if err != nil { t.Fatal(err) } c := &Client{Transport: &Transport{Proxy: ProxyURL(pu)}} c.Head(ts.URL) got := <-ch want := "proxy for " + ts.URL + "/" if got != want { t.Errorf("want %q, got %q", want, got) } }
func TestNoDate(t *testing.T) { ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { w.Header()["Date"] = nil })) defer ts.Close() res, err := Get(ts.URL) if err != nil { t.Fatal(err) } _, present := res.Header["Date"] if present { t.Fatalf("Expected no Date header; got %v", res.Header["Date"]) } }