// 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.Headers.Get("x-client-ipport"), res2.Headers.Get("x-client-ipport"); v1 != v2 { t.Errorf("ip/ports differed between head requests: %q vs %q", v1, v2) } }
// 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.Headers.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) } } }
// 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 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 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) } }
func TestFileServerImplicitLeadingSlash(t *testing.T) { tempDir, err := ioutil.TempDir("", "") if err != nil { t.Fatalf("TempDir: %v", err) } defer mustRemoveAll(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) } res.Body.Close() 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 TestContentTypeWithCopy(t *testing.T) { const ( input = "\n<html>\n\t<head>\n" expected = "text/html; charset=utf-8" ) ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { // Use io.Copy from a bytes.Buffer to trigger ReadFrom. buf := bytes.NewBuffer([]byte(input)) n, err := io.Copy(w, buf) if int(n) != len(input) || err != nil { t.Errorf("io.Copy(w, %q) = %v, %v want %d, nil", input, n, err, len(input)) } })) defer ts.Close() resp, err := Get(ts.URL) if err != nil { t.Fatalf("Get: %v", err) } if ct := resp.Headers.Get("Content-Type"); ct != expected { t.Errorf("Content-Type = %q, want %q", ct, expected) } data, err := ioutil.ReadAll(resp.Body) if err != nil { t.Errorf("reading body: %v", err) } else if !bytes.Equal(data, []byte(input)) { t.Errorf("data is %q, want %q", data, input) } resp.Body.Close() }
// 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, 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 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.Headers.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() } }
// 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.Headers.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.Headers.Set("Accept-Encoding", test.accept) } res, err := DefaultTransport.RoundTrip(req) var body []byte if test.compressed { gzip, err := gzip.NewReader(res.Body) if err != nil { t.Errorf("%d. gzip NewReader: %v", i, err) continue } 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.Headers.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.Headers.Get("Content-Encoding"), test.accept; g != e { t.Errorf("%d. Content-Encoding = %q; want %q", i, g, e) } } }
func TestRedirectCookiesJar(t *testing.T) { var ts *httptest.Server ts = httptest.NewServer(echoCookiesRedirectHandler) defer ts.Close() c := &Client{} c.Jar = &TestJar{perURL: make(map[string][]*Cookie)} u, _ := url.Parse(ts.URL) c.Jar.SetCookies(u, []*Cookie{expectedCookies[0]}) resp, _ := c.Get(ts.URL) matchReturnedCookies(t, expectedCookies, resp.Cookies()) }
// 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") } } }
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(time.Second / time.Duration(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(25 * time.Millisecond) 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.Headers["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 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.Headers = make(Header) if req.URL, err = url.Parse(ts.URL); err != nil { t.Fatal("ParseURL:", err) } req.Method = "GET" // straight GET _, body := getBody(t, "straight get", req) if !equal(body, file) { t.Fatalf("body mismatch: got %q, want %q", body, file) } // Range tests for i, rt := range ServeFileRangeTests { req.Headers.Set("Range", "bytes="+rt.r) if rt.r == "" { req.Headers["Range"] = nil } r, body := getBody(t, fmt.Sprintf("test %d", i), 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.Headers.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 TestServeFileFromCWD(t *testing.T) { ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { ServeFile(w, r, "fs_test.go") })) defer ts.Close() r, err := Get(ts.URL) if err != nil { t.Fatal(err) } if r.StatusCode != 200 { t.Fatalf("expected 200 OK, got %s", r.Status) } }
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 TestServeFileWithContentEncoding(t *testing.T) { ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { w.Header().Set("Content-Encoding", "foo") ServeFile(w, r, "testdata/file") })) defer ts.Close() resp, err := Get(ts.URL) if err != nil { t.Fatal(err) } if g, e := resp.ContentLength, int64(-1); g != e { t.Errorf("Content-Length mismatch: got %d, want %d", g, e) } }
func TestServeFileMimeType(t *testing.T) { ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { ServeFile(w, r, "testdata/style.css") })) defer ts.Close() resp, err := Get(ts.URL) if err != nil { t.Fatal(err) } want := "text/css; charset=utf-8" if h := resp.Headers.Get("Content-Type"); h != want { t.Errorf("Content-Type mismatch: got %q, want %q", h, want) } }
func TestFSRedirect(t *testing.T) { ts := httptest.NewServer(StripPrefix("/test", FileServer(Dir(".")))) defer ts.Close() for _, data := range fsRedirectTestData { res, err := Get(ts.URL + data.original) if err != nil { t.Fatal(err) } res.Body.Close() if g, e := res.Request.URL.Path, data.redirect; g != e { t.Errorf("redirect from %s: got %s, want %s", data.original, g, e) } } }
func TestRedirectCookiesOnRequest(t *testing.T) { var ts *httptest.Server ts = httptest.NewServer(echoCookiesRedirectHandler) defer ts.Close() c := &Client{} req, _ := NewRequest("GET", ts.URL, nil) req.AddCookie(expectedCookies[0]) // TODO: Uncomment when an implementation of a RFC6265 cookie jar lands. _ = c // resp, _ := c.Do(req) // matchReturnedCookies(t, expectedCookies, resp.Cookies()) req, _ = NewRequest("GET", ts.URL, nil) // resp, _ = c.Do(req) // matchReturnedCookies(t, expectedCookies[1:], resp.Cookies()) }
func TestClient(t *testing.T) { ts := httptest.NewServer(robotsTxtHandler) defer ts.Close() r, err := Get(ts.URL) var b []byte if err == nil { b, err = pedanticReadAll(r.Body) r.Body.Close() } if err != nil { t.Error(err) } else if s := string(b); !strings.HasPrefix(s, "User-agent:") { t.Errorf("Incorrect page body (did not begin with User-agent): %q", s) } }
func TestServeContent(t *testing.T) { type req struct { name string modtime time.Time content io.ReadSeeker } ch := make(chan req, 1) ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { p := <-ch ServeContent(w, r, p.name, p.modtime, p.content) })) defer ts.Close() css, err := os.Open("testdata/style.css") if err != nil { t.Fatal(err) } defer css.Close() ch <- req{"style.css", time.Time{}, css} res, err := Get(ts.URL) if err != nil { t.Fatal(err) } if g, e := res.Headers.Get("Content-Type"), "text/css; charset=utf-8"; g != e { t.Errorf("style.css: content type = %q, want %q", g, e) } if g := res.Headers.Get("Last-Modified"); g != "" { t.Errorf("want empty Last-Modified; got %q", g) } fi, err := css.Stat() if err != nil { t.Fatal(err) } ch <- req{"style.html", fi.ModTime(), css} res, err = Get(ts.URL) if err != nil { t.Fatal(err) } if g, e := res.Headers.Get("Content-Type"), "text/html; charset=utf-8"; g != e { t.Errorf("style.html: content type = %q, want %q", g, e) } if g := res.Headers.Get("Last-Modified"); g == "" { t.Errorf("want non-empty last-modified") } }
func ExampleServer() { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "Hello, client") })) defer ts.Close() res, err := http.Get(ts.URL) if err != nil { log.Fatal(err) } greeting, err := ioutil.ReadAll(res.Body) res.Body.Close() if err != nil { log.Fatal(err) } fmt.Printf("%s", greeting) // Output: Hello, client }
func TestTransportConnectionCloseOnResponse(t *testing.T) { ts := httptest.NewServer(hostPortHandler) defer ts.Close() for _, connectionClose := range []bool{false, true} { tr := &Transport{} c := &Client{Transport: tr} fetch := func(n int) string { req := new(Request) var err error req.URL, err = url.Parse(ts.URL + fmt.Sprintf("/?close=%v", connectionClose)) if err != nil { t.Fatalf("URL parse error: %v", err) } req.Method = "GET" req.Protocol = *(new(Protocol)) req.Protocol.Name = "HTTP/1.1" req.Protocol.Major = 1 req.Protocol.Minor = 1 res, err := c.Do(req) if err != nil { t.Fatalf("error in connectionClose=%v, req #%d, Do: %v", connectionClose, n, err) } body, err := ioutil.ReadAll(res.Body) defer res.Body.Close() if err != nil { t.Fatalf("error in connectionClose=%v, req #%d, ReadAll: %v", connectionClose, n, err) } return string(body) } body1 := fetch(1) body2 := fetch(2) bodiesDiffer := body1 != body2 if bodiesDiffer != connectionClose { t.Errorf("error in connectionClose=%v. unexpected bodiesDiffer=%v; body1=%q; body2=%q", connectionClose, bodiesDiffer, body1, body2) } } }
func TestServeIndexHtml(t *testing.T) { const want = "index.html says hello\n" ts := httptest.NewServer(FileServer(Dir("."))) defer ts.Close() for _, path := range []string{"/testdata/", "/testdata/index.html"} { res, err := Get(ts.URL + path) if err != nil { t.Fatal(err) } b, err := ioutil.ReadAll(res.Body) if err != nil { t.Fatal("reading Body:", err) } if s := string(b); s != want { t.Errorf("for path %q got %q, want %q", path, s, want) } res.Body.Close() } }
func TestServeFileContentType(t *testing.T) { const ctype = "icecream/chocolate" ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { if r.FormValue("override") == "1" { w.Header().Set("Content-Type", ctype) } ServeFile(w, r, "testdata/file") })) defer ts.Close() get := func(override, want string) { resp, err := Get(ts.URL + "?override=" + override) if err != nil { t.Fatal(err) } if h := resp.Headers.Get("Content-Type"); h != want { t.Errorf("Content-Type mismatch: got %q, want %q", h, want) } } get("0", "text/plain; charset=utf-8") get("1", ctype) }
func TestSniffWriteSize(t *testing.T) { ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { size, _ := strconv.Atoi(r.FormValue("size")) written, err := io.WriteString(w, strings.Repeat("a", size)) if err != nil { t.Errorf("write of %d bytes: %v", size, err) return } if written != size { t.Errorf("write of %d bytes wrote %d bytes", size, written) } })) defer ts.Close() for _, size := range []int{0, 1, 200, 600, 999, 1000, 1023, 1024, 512 << 10, 1 << 20} { res, err := Get(fmt.Sprintf("%s/?size=%d", ts.URL, size)) if err != nil { t.Fatalf("size %d: %v", size, err) } res.Body.Close() } }
func TestStreamingGet(t *testing.T) { say := make(chan string) ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { w.(Flusher).Flush() for str := range say { w.Write([]byte(str)) w.(Flusher).Flush() } })) defer ts.Close() c := &Client{} res, err := c.Get(ts.URL) if err != nil { t.Fatal(err) } var buf [10]byte for _, str := range []string{"i", "am", "also", "known", "as", "comet"} { say <- str n, err := io.ReadFull(res.Body, buf[0:len(str)]) if err != nil { t.Fatalf("ReadFull on %q: %v", str, err) } if n != len(str) { t.Fatalf("Receiving %q, only read %d bytes", str, n) } got := string(buf[0:n]) if got != str { t.Fatalf("Expected %q, got %q", str, got) } } close(say) _, err = io.ReadFull(res.Body, buf[0:1]) if err != io.EOF { t.Fatalf("at end expected EOF, got %v", err) } }
// This used to crash; http://golang.org/issue/3266 func TestTransportIdleConnCrash(t *testing.T) { tr := &Transport{} c := &Client{Transport: tr} unblockCh := make(chan bool, 1) ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { <-unblockCh tr.CloseIdleConnections() })) defer ts.Close() didreq := make(chan bool) go func() { res, err := c.Get(ts.URL) if err != nil { t.Error(err) } else { res.Body.Close() // returns idle conn } didreq <- true }() unblockCh <- true <-didreq }