1 // Copyright 2011 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
5 // Tests for transport.go
30 // TODO: test 5 pipelined requests with responses: 1) OK, 2) OK, Connection: Close
31 // and then verify that the final 2 responses get errors back.
33 // hostPortHandler writes back the client's "host:port".
34 var hostPortHandler
= HandlerFunc(func(w ResponseWriter
, r
*Request
) {
35 if r
.FormValue("close") == "true" {
36 w
.Header().Set("Connection", "close")
38 w
.Write([]byte(r
.RemoteAddr
))
41 // testCloseConn is a net.Conn tracked by a testConnSet.
42 type testCloseConn
struct {
47 func (c
*testCloseConn
) Close() error
{
52 // testConnSet tracks a set of TCP connections and whether they've
54 type testConnSet
struct {
56 closed map[net
.Conn
]bool
57 list
[]net
.Conn
// in order created
61 func (tcs
*testConnSet
) insert(c net
.Conn
) {
63 defer tcs
.mutex
.Unlock()
65 tcs
.list
= append(tcs
.list
, c
)
68 func (tcs
*testConnSet
) remove(c net
.Conn
) {
70 defer tcs
.mutex
.Unlock()
74 // some tests use this to manage raw tcp connections for later inspection
75 func makeTestDial(t
*testing
.T
) (*testConnSet
, func(n
, addr
string) (net
.Conn
, error
)) {
76 connSet
:= &testConnSet
{
78 closed: make(map[net
.Conn
]bool),
80 dial
:= func(n
, addr
string) (net
.Conn
, error
) {
81 c
, err
:= net
.Dial(n
, addr
)
85 tc
:= &testCloseConn
{c
, connSet
}
92 func (tcs
*testConnSet
) check(t
*testing
.T
) {
94 defer tcs
.mutex
.Unlock()
96 for i
, c
:= range tcs
.list
{
98 t
.Errorf("TCP connection #%d, %p (of %d total) was not closed", i
+1, c
, len(tcs
.list
))
103 // Two subsequent requests and verify their response is the same.
104 // The response from the server is our own IP:port
105 func TestTransportKeepAlives(t
*testing
.T
) {
107 ts
:= httptest
.NewServer(hostPortHandler
)
110 for _
, disableKeepAlive
:= range []bool{false, true} {
111 tr
:= &Transport
{DisableKeepAlives
: disableKeepAlive
}
112 defer tr
.CloseIdleConnections()
113 c
:= &Client
{Transport
: tr
}
115 fetch
:= func(n
int) string {
116 res
, err
:= c
.Get(ts
.URL
)
118 t
.Fatalf("error in disableKeepAlive=%v, req #%d, GET: %v", disableKeepAlive
, n
, err
)
120 body
, err
:= ioutil
.ReadAll(res
.Body
)
122 t
.Fatalf("error in disableKeepAlive=%v, req #%d, ReadAll: %v", disableKeepAlive
, n
, err
)
130 bodiesDiffer
:= body1
!= body2
131 if bodiesDiffer
!= disableKeepAlive
{
132 t
.Errorf("error in disableKeepAlive=%v. unexpected bodiesDiffer=%v; body1=%q; body2=%q",
133 disableKeepAlive
, bodiesDiffer
, body1
, body2
)
138 func TestTransportConnectionCloseOnResponse(t
*testing
.T
) {
140 ts
:= httptest
.NewServer(hostPortHandler
)
143 connSet
, testDial
:= makeTestDial(t
)
145 for _
, connectionClose
:= range []bool{false, true} {
149 c
:= &Client
{Transport
: tr
}
151 fetch
:= func(n
int) string {
154 req
.URL
, err
= url
.Parse(ts
.URL
+ fmt
.Sprintf("/?close=%v", connectionClose
))
156 t
.Fatalf("URL parse error: %v", err
)
159 req
.Proto
= "HTTP/1.1"
163 res
, err
:= c
.Do(req
)
165 t
.Fatalf("error in connectionClose=%v, req #%d, Do: %v", connectionClose
, n
, err
)
167 defer res
.Body
.Close()
168 body
, err
:= ioutil
.ReadAll(res
.Body
)
170 t
.Fatalf("error in connectionClose=%v, req #%d, ReadAll: %v", connectionClose
, n
, err
)
177 bodiesDiffer
:= body1
!= body2
178 if bodiesDiffer
!= connectionClose
{
179 t
.Errorf("error in connectionClose=%v. unexpected bodiesDiffer=%v; body1=%q; body2=%q",
180 connectionClose
, bodiesDiffer
, body1
, body2
)
183 tr
.CloseIdleConnections()
189 func TestTransportConnectionCloseOnRequest(t
*testing
.T
) {
191 ts
:= httptest
.NewServer(hostPortHandler
)
194 connSet
, testDial
:= makeTestDial(t
)
196 for _
, connectionClose
:= range []bool{false, true} {
200 c
:= &Client
{Transport
: tr
}
202 fetch
:= func(n
int) string {
205 req
.URL
, err
= url
.Parse(ts
.URL
)
207 t
.Fatalf("URL parse error: %v", err
)
210 req
.Proto
= "HTTP/1.1"
213 req
.Close
= connectionClose
215 res
, err
:= c
.Do(req
)
217 t
.Fatalf("error in connectionClose=%v, req #%d, Do: %v", connectionClose
, n
, err
)
219 body
, err
:= ioutil
.ReadAll(res
.Body
)
221 t
.Fatalf("error in connectionClose=%v, req #%d, ReadAll: %v", connectionClose
, n
, err
)
228 bodiesDiffer
:= body1
!= body2
229 if bodiesDiffer
!= connectionClose
{
230 t
.Errorf("error in connectionClose=%v. unexpected bodiesDiffer=%v; body1=%q; body2=%q",
231 connectionClose
, bodiesDiffer
, body1
, body2
)
234 tr
.CloseIdleConnections()
240 func TestTransportIdleCacheKeys(t
*testing
.T
) {
242 ts
:= httptest
.NewServer(hostPortHandler
)
245 tr
:= &Transport
{DisableKeepAlives
: false}
246 c
:= &Client
{Transport
: tr
}
248 if e
, g
:= 0, len(tr
.IdleConnKeysForTesting()); e
!= g
{
249 t
.Errorf("After CloseIdleConnections expected %d idle conn cache keys; got %d", e
, g
)
252 resp
, err
:= c
.Get(ts
.URL
)
256 ioutil
.ReadAll(resp
.Body
)
258 keys
:= tr
.IdleConnKeysForTesting()
259 if e
, g
:= 1, len(keys
); e
!= g
{
260 t
.Fatalf("After Get expected %d idle conn cache keys; got %d", e
, g
)
263 if e
:= "|http|" + ts
.Listener
.Addr().String(); keys
[0] != e
{
264 t
.Errorf("Expected idle cache key %q; got %q", e
, keys
[0])
267 tr
.CloseIdleConnections()
268 if e
, g
:= 0, len(tr
.IdleConnKeysForTesting()); e
!= g
{
269 t
.Errorf("After CloseIdleConnections expected %d idle conn cache keys; got %d", e
, g
)
273 func TestTransportMaxPerHostIdleConns(t
*testing
.T
) {
275 resch
:= make(chan string)
276 gotReq
:= make(chan bool)
277 ts
:= httptest
.NewServer(HandlerFunc(func(w ResponseWriter
, r
*Request
) {
280 _
, err
:= w
.Write([]byte(msg
))
282 t
.Fatalf("Write: %v", err
)
287 tr
:= &Transport
{DisableKeepAlives
: false, MaxIdleConnsPerHost
: maxIdleConns
}
288 c
:= &Client
{Transport
: tr
}
290 // Start 3 outstanding requests and wait for the server to get them.
291 // Their responses will hang until we write to resch, though.
292 donech
:= make(chan bool)
294 resp
, err
:= c
.Get(ts
.URL
)
298 _
, err
= ioutil
.ReadAll(resp
.Body
)
300 t
.Fatalf("ReadAll: %v", err
)
311 if e
, g
:= 0, len(tr
.IdleConnKeysForTesting()); e
!= g
{
312 t
.Fatalf("Before writes, expected %d idle conn cache keys; got %d", e
, g
)
317 keys
:= tr
.IdleConnKeysForTesting()
318 if e
, g
:= 1, len(keys
); e
!= g
{
319 t
.Fatalf("after first response, expected %d idle conn cache keys; got %d", e
, g
)
321 cacheKey
:= "|http|" + ts
.Listener
.Addr().String()
322 if keys
[0] != cacheKey
{
323 t
.Fatalf("Expected idle cache key %q; got %q", cacheKey
, keys
[0])
325 if e
, g
:= 1, tr
.IdleConnCountForTesting(cacheKey
); e
!= g
{
326 t
.Errorf("after first response, expected %d idle conns; got %d", e
, g
)
331 if e
, g
:= 2, tr
.IdleConnCountForTesting(cacheKey
); e
!= g
{
332 t
.Errorf("after second response, expected %d idle conns; got %d", e
, g
)
337 if e
, g
:= maxIdleConns
, tr
.IdleConnCountForTesting(cacheKey
); e
!= g
{
338 t
.Errorf("after third response, still expected %d idle conns; got %d", e
, g
)
342 func TestTransportServerClosingUnexpectedly(t
*testing
.T
) {
344 ts
:= httptest
.NewServer(hostPortHandler
)
348 c
:= &Client
{Transport
: tr
}
350 fetch
:= func(n
, retries
int) string {
351 condFatalf
:= func(format
string, arg
...interface{}) {
353 t
.Fatalf(format
, arg
...)
355 t
.Logf("retrying shortly after expected error: "+format
, arg
...)
356 time
.Sleep(time
.Second
/ time
.Duration(retries
))
360 res
, err
:= c
.Get(ts
.URL
)
362 condFatalf("error in req #%d, GET: %v", n
, err
)
365 body
, err
:= ioutil
.ReadAll(res
.Body
)
367 condFatalf("error in req #%d, ReadAll: %v", n
, err
)
379 ts
.CloseClientConnections() // surprise!
381 // This test has an expected race. Sleeping for 25 ms prevents
382 // it on most fast machines, causing the next fetch() call to
383 // succeed quickly. But if we do get errors, fetch() will retry 5
384 // times with some delays between.
385 time
.Sleep(25 * time
.Millisecond
)
390 t
.Errorf("expected body1 and body2 to be equal")
393 t
.Errorf("expected body2 and body3 to be different")
397 // Test for http://golang.org/issue/2616 (appropriate issue number)
398 // This fails pretty reliably with GOMAXPROCS=100 or something high.
399 func TestStressSurpriseServerCloses(t
*testing
.T
) {
402 t
.Skip("skipping test in short mode")
404 ts
:= httptest
.NewServer(HandlerFunc(func(w ResponseWriter
, r
*Request
) {
405 w
.Header().Set("Content-Length", "5")
406 w
.Header().Set("Content-Type", "text/plain")
407 w
.Write([]byte("Hello"))
409 conn
, buf
, _
:= w
.(Hijacker
).Hijack()
415 tr
:= &Transport
{DisableKeepAlives
: false}
416 c
:= &Client
{Transport
: tr
}
418 // Do a bunch of traffic from different goroutines. Send to activityc
419 // after each request completes, regardless of whether it failed.
424 activityc
:= make(chan bool)
425 for i
:= 0; i
< numClients
; i
++ {
427 for i
:= 0; i
< reqsPerClient
; i
++ {
428 res
, err
:= c
.Get(ts
.URL
)
430 // We expect errors since the server is
431 // hanging up on us after telling us to
432 // send more requests, so we don't
433 // actually care what the error is.
434 // But we want to close the body in cases
435 // where we won the race.
443 // Make sure all the request come back, one way or another.
444 for i
:= 0; i
< numClients
*reqsPerClient
; i
++ {
447 case <-time
.After(5 * time
.Second
):
448 t
.Fatalf("presumed deadlock; no HTTP client activity seen in awhile")
453 // TestTransportHeadResponses verifies that we deal with Content-Lengths
454 // with no bodies properly
455 func TestTransportHeadResponses(t
*testing
.T
) {
457 ts
:= httptest
.NewServer(HandlerFunc(func(w ResponseWriter
, r
*Request
) {
458 if r
.Method
!= "HEAD" {
459 panic("expected HEAD; got " + r
.Method
)
461 w
.Header().Set("Content-Length", "123")
466 tr
:= &Transport
{DisableKeepAlives
: false}
467 c
:= &Client
{Transport
: tr
}
468 for i
:= 0; i
< 2; i
++ {
469 res
, err
:= c
.Head(ts
.URL
)
471 t
.Errorf("error on loop %d: %v", i
, err
)
473 if e
, g
:= "123", res
.Header
.Get("Content-Length"); e
!= g
{
474 t
.Errorf("loop %d: expected Content-Length header of %q, got %q", i
, e
, g
)
476 if e
, g
:= int64(123), res
.ContentLength
; e
!= g
{
477 t
.Errorf("loop %d: expected res.ContentLength of %v, got %v", i
, e
, g
)
482 // TestTransportHeadChunkedResponse verifies that we ignore chunked transfer-encoding
483 // on responses to HEAD requests.
484 func TestTransportHeadChunkedResponse(t
*testing
.T
) {
486 ts
:= httptest
.NewServer(HandlerFunc(func(w ResponseWriter
, r
*Request
) {
487 if r
.Method
!= "HEAD" {
488 panic("expected HEAD; got " + r
.Method
)
490 w
.Header().Set("Transfer-Encoding", "chunked") // client should ignore
491 w
.Header().Set("x-client-ipport", r
.RemoteAddr
)
496 tr
:= &Transport
{DisableKeepAlives
: false}
497 c
:= &Client
{Transport
: tr
}
499 res1
, err
:= c
.Head(ts
.URL
)
501 t
.Fatalf("request 1 error: %v", err
)
503 res2
, err
:= c
.Head(ts
.URL
)
505 t
.Fatalf("request 2 error: %v", err
)
507 if v1
, v2
:= res1
.Header
.Get("x-client-ipport"), res2
.Header
.Get("x-client-ipport"); v1
!= v2
{
508 t
.Errorf("ip/ports differed between head requests: %q vs %q", v1
, v2
)
512 var roundTripTests
= []struct {
517 // Requests with no accept-encoding header use transparent compression
519 // Requests with other accept-encoding should pass through unmodified
520 {"foo", "foo", false},
521 // Requests with accept-encoding == gzip should be passed through
522 {"gzip", "gzip", true},
525 // Test that the modification made to the Request by the RoundTripper is cleaned up
526 func TestRoundTripGzip(t
*testing
.T
) {
528 const responseBody
= "test response body"
529 ts
:= httptest
.NewServer(HandlerFunc(func(rw ResponseWriter
, req
*Request
) {
530 accept
:= req
.Header
.Get("Accept-Encoding")
531 if expect
:= req
.FormValue("expect_accept"); accept
!= expect
{
532 t
.Errorf("in handler, test %v: Accept-Encoding = %q, want %q",
533 req
.FormValue("testnum"), accept
, expect
)
535 if accept
== "gzip" {
536 rw
.Header().Set("Content-Encoding", "gzip")
537 gz
:= gzip
.NewWriter(rw
)
538 gz
.Write([]byte(responseBody
))
541 rw
.Header().Set("Content-Encoding", accept
)
542 rw
.Write([]byte(responseBody
))
547 for i
, test
:= range roundTripTests
{
548 // Test basic request (no accept-encoding)
549 req
, _
:= NewRequest("GET", fmt
.Sprintf("%s/?testnum=%d&expect_accept=%s", ts
.URL
, i
, test
.expectAccept
), nil)
550 if test
.accept
!= "" {
551 req
.Header
.Set("Accept-Encoding", test
.accept
)
553 res
, err
:= DefaultTransport
.RoundTrip(req
)
556 gzip
, err
:= gzip
.NewReader(res
.Body
)
558 t
.Errorf("%d. gzip NewReader: %v", i
, err
)
561 body
, err
= ioutil
.ReadAll(gzip
)
564 body
, err
= ioutil
.ReadAll(res
.Body
)
567 t
.Errorf("%d. Error: %q", i
, err
)
570 if g
, e
:= string(body
), responseBody
; g
!= e
{
571 t
.Errorf("%d. body = %q; want %q", i
, g
, e
)
573 if g
, e
:= req
.Header
.Get("Accept-Encoding"), test
.accept
; g
!= e
{
574 t
.Errorf("%d. Accept-Encoding = %q; want %q (it was mutated, in violation of RoundTrip contract)", i
, g
, e
)
576 if g
, e
:= res
.Header
.Get("Content-Encoding"), test
.accept
; g
!= e
{
577 t
.Errorf("%d. Content-Encoding = %q; want %q", i
, g
, e
)
583 func TestTransportGzip(t
*testing
.T
) {
585 const testString
= "The test string aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
586 const nRandBytes
= 1024 * 1024
587 ts
:= httptest
.NewServer(HandlerFunc(func(rw ResponseWriter
, req
*Request
) {
588 if g
, e
:= req
.Header
.Get("Accept-Encoding"), "gzip"; g
!= e
{
589 t
.Errorf("Accept-Encoding = %q, want %q", g
, e
)
591 rw
.Header().Set("Content-Encoding", "gzip")
592 if req
.Method
== "HEAD" {
598 if req
.FormValue("chunked") == "0" {
600 defer io
.Copy(rw
, &buf
)
602 rw
.Header().Set("Content-Length", strconv
.Itoa(buf
.Len()))
605 gz
:= gzip
.NewWriter(w
)
606 gz
.Write([]byte(testString
))
607 if req
.FormValue("body") == "large" {
608 io
.CopyN(gz
, rand
.Reader
, nRandBytes
)
614 for _
, chunked
:= range []string{"1", "0"} {
615 c
:= &Client
{Transport
: &Transport
{}}
617 // First fetch something large, but only read some of it.
618 res
, err
:= c
.Get(ts
.URL
+ "/?body=large&chunked=" + chunked
)
620 t
.Fatalf("large get: %v", err
)
622 buf
:= make([]byte, len(testString
))
623 n
, err
:= io
.ReadFull(res
.Body
, buf
)
625 t
.Fatalf("partial read of large response: size=%d, %v", n
, err
)
627 if e
, g
:= testString
, string(buf
); e
!= g
{
628 t
.Errorf("partial read got %q, expected %q", g
, e
)
631 // Read on the body, even though it's closed
632 n
, err
= res
.Body
.Read(buf
)
633 if n
!= 0 || err
== nil {
634 t
.Errorf("expected error post-closed large Read; got = %d, %v", n
, err
)
637 // Then something small.
638 res
, err
= c
.Get(ts
.URL
+ "/?chunked=" + chunked
)
642 body
, err
:= ioutil
.ReadAll(res
.Body
)
646 if g
, e
:= string(body
), testString
; g
!= e
{
647 t
.Fatalf("body = %q; want %q", g
, e
)
649 if g
, e
:= res
.Header
.Get("Content-Encoding"), ""; g
!= e
{
650 t
.Fatalf("Content-Encoding = %q; want %q", g
, e
)
653 // Read on the body after it's been fully read:
654 n
, err
= res
.Body
.Read(buf
)
655 if n
!= 0 || err
== nil {
656 t
.Errorf("expected Read error after exhausted reads; got %d, %v", n
, err
)
659 n
, err
= res
.Body
.Read(buf
)
660 if n
!= 0 || err
== nil {
661 t
.Errorf("expected Read error after Close; got %d, %v", n
, err
)
665 // And a HEAD request too, because they're always weird.
666 c
:= &Client
{Transport
: &Transport
{}}
667 res
, err
:= c
.Head(ts
.URL
)
669 t
.Fatalf("Head: %v", err
)
671 if res
.StatusCode
!= 200 {
672 t
.Errorf("Head status=%d; want=200", res
.StatusCode
)
676 func TestTransportProxy(t
*testing
.T
) {
678 ch
:= make(chan string, 1)
679 ts
:= httptest
.NewServer(HandlerFunc(func(w ResponseWriter
, r
*Request
) {
683 proxy
:= httptest
.NewServer(HandlerFunc(func(w ResponseWriter
, r
*Request
) {
684 ch
<- "proxy for " + r
.URL
.String()
688 pu
, err
:= url
.Parse(proxy
.URL
)
692 c
:= &Client
{Transport
: &Transport
{Proxy
: ProxyURL(pu
)}}
695 want
:= "proxy for " + ts
.URL
+ "/"
697 t
.Errorf("want %q, got %q", want
, got
)
701 // TestTransportGzipRecursive sends a gzip quine and checks that the
702 // client gets the same value back. This is more cute than anything,
703 // but checks that we don't recurse forever, and checks that
704 // Content-Encoding is removed.
705 func TestTransportGzipRecursive(t
*testing
.T
) {
707 ts
:= httptest
.NewServer(HandlerFunc(func(w ResponseWriter
, r
*Request
) {
708 w
.Header().Set("Content-Encoding", "gzip")
713 c
:= &Client
{Transport
: &Transport
{}}
714 res
, err
:= c
.Get(ts
.URL
)
718 body
, err
:= ioutil
.ReadAll(res
.Body
)
722 if !bytes
.Equal(body
, rgz
) {
723 t
.Fatalf("Incorrect result from recursive gz:\nhave=%x\nwant=%x",
726 if g
, e
:= res
.Header
.Get("Content-Encoding"), ""; g
!= e
{
727 t
.Fatalf("Content-Encoding = %q; want %q", g
, e
)
731 // tests that persistent goroutine connections shut down when no longer desired.
732 func TestTransportPersistConnLeak(t
*testing
.T
) {
734 gotReqCh
:= make(chan bool)
735 unblockCh
:= make(chan bool)
736 ts
:= httptest
.NewServer(HandlerFunc(func(w ResponseWriter
, r
*Request
) {
739 w
.Header().Set("Content-Length", "0")
745 c
:= &Client
{Transport
: tr
}
747 n0
:= runtime
.NumGoroutine()
750 didReqCh
:= make(chan bool)
751 for i
:= 0; i
< numReq
; i
++ {
753 res
, err
:= c
.Get(ts
.URL
)
756 t
.Errorf("client fetch error: %v", err
)
763 // Wait for all goroutines to be stuck in the Handler.
764 for i
:= 0; i
< numReq
; i
++ {
768 nhigh
:= runtime
.NumGoroutine()
770 // Tell all handlers to unblock and reply.
771 for i
:= 0; i
< numReq
; i
++ {
775 // Wait for all HTTP clients to be done.
776 for i
:= 0; i
< numReq
; i
++ {
780 tr
.CloseIdleConnections()
781 time
.Sleep(100 * time
.Millisecond
)
783 runtime
.GC() // even more.
784 nfinal
:= runtime
.NumGoroutine()
786 growth
:= nfinal
- n0
788 // We expect 0 or 1 extra goroutine, empirically. Allow up to 5.
789 // Previously we were leaking one per numReq.
790 t
.Logf("goroutine growth: %d -> %d -> %d (delta: %d)", n0
, nhigh
, nfinal
, growth
)
792 t
.Error("too many new goroutines")
796 // golang.org/issue/4531: Transport leaks goroutines when
797 // request.ContentLength is explicitly short
798 func TestTransportPersistConnLeakShortBody(t
*testing
.T
) {
800 ts
:= httptest
.NewServer(HandlerFunc(func(w ResponseWriter
, r
*Request
) {
805 c
:= &Client
{Transport
: tr
}
807 n0
:= runtime
.NumGoroutine()
808 body
:= []byte("Hello")
809 for i
:= 0; i
< 20; i
++ {
810 req
, err
:= NewRequest("POST", ts
.URL
, bytes
.NewReader(body
))
814 req
.ContentLength
= int64(len(body
) - 2) // explicitly short
817 t
.Fatal("Expect an error from writing too long of a body.")
820 nhigh
:= runtime
.NumGoroutine()
821 tr
.CloseIdleConnections()
822 time
.Sleep(50 * time
.Millisecond
)
824 nfinal
:= runtime
.NumGoroutine()
826 growth
:= nfinal
- n0
828 // We expect 0 or 1 extra goroutine, empirically. Allow up to 5.
829 // Previously we were leaking one per numReq.
830 t
.Logf("goroutine growth: %d -> %d -> %d (delta: %d)", n0
, nhigh
, nfinal
, growth
)
832 t
.Error("too many new goroutines")
836 // This used to crash; http://golang.org/issue/3266
837 func TestTransportIdleConnCrash(t
*testing
.T
) {
840 c
:= &Client
{Transport
: tr
}
842 unblockCh
:= make(chan bool, 1)
843 ts
:= httptest
.NewServer(HandlerFunc(func(w ResponseWriter
, r
*Request
) {
845 tr
.CloseIdleConnections()
849 didreq
:= make(chan bool)
851 res
, err
:= c
.Get(ts
.URL
)
855 res
.Body
.Close() // returns idle conn
863 // Test that the transport doesn't close the TCP connection early,
864 // before the response body has been read. This was a regression
865 // which sadly lacked a triggering test. The large response body made
866 // the old race easier to trigger.
867 func TestIssue3644(t
*testing
.T
) {
870 ts
:= httptest
.NewServer(HandlerFunc(func(w ResponseWriter
, r
*Request
) {
871 w
.Header().Set("Connection", "close")
872 for i
:= 0; i
< numFoos
; i
++ {
873 w
.Write([]byte("foo "))
878 c
:= &Client
{Transport
: tr
}
879 res
, err
:= c
.Get(ts
.URL
)
883 defer res
.Body
.Close()
884 bs
, err
:= ioutil
.ReadAll(res
.Body
)
888 if len(bs
) != numFoos
*len("foo ") {
889 t
.Errorf("unexpected response length")
893 // Test that a client receives a server's reply, even if the server doesn't read
894 // the entire request body.
895 func TestIssue3595(t
*testing
.T
) {
897 const deniedMsg
= "sorry, denied."
898 ts
:= httptest
.NewServer(HandlerFunc(func(w ResponseWriter
, r
*Request
) {
899 Error(w
, deniedMsg
, StatusUnauthorized
)
903 c
:= &Client
{Transport
: tr
}
904 res
, err
:= c
.Post(ts
.URL
, "application/octet-stream", neverEnding('a'))
906 t
.Errorf("Post: %v", err
)
909 got
, err
:= ioutil
.ReadAll(res
.Body
)
911 t
.Fatalf("Body ReadAll: %v", err
)
913 if !strings
.Contains(string(got
), deniedMsg
) {
914 t
.Errorf("Known bug: response %q does not contain %q", got
, deniedMsg
)
918 // From http://golang.org/issue/4454 ,
919 // "client fails to handle requests with no body and chunked encoding"
920 func TestChunkedNoContent(t
*testing
.T
) {
922 ts
:= httptest
.NewServer(HandlerFunc(func(w ResponseWriter
, r
*Request
) {
923 w
.WriteHeader(StatusNoContent
)
927 for _
, closeBody
:= range []bool{true, false} {
928 c
:= &Client
{Transport
: &Transport
{}}
930 for i
:= 1; i
<= n
; i
++ {
931 res
, err
:= c
.Get(ts
.URL
)
933 t
.Errorf("closingBody=%v, req %d/%d: %v", closeBody
, i
, n
, err
)
943 func TestTransportConcurrency(t
*testing
.T
) {
945 maxProcs
, numReqs
:= 16, 500
947 maxProcs
, numReqs
= 4, 50
949 defer runtime
.GOMAXPROCS(runtime
.GOMAXPROCS(maxProcs
))
950 ts
:= httptest
.NewServer(HandlerFunc(func(w ResponseWriter
, r
*Request
) {
951 fmt
.Fprintf(w
, "%v", r
.FormValue("echo"))
955 var wg sync
.WaitGroup
959 Dial
: func(netw
, addr
string) (c net
.Conn
, err error
) {
960 // Due to the Transport's "socket late
961 // binding" (see idleConnCh in transport.go),
962 // the numReqs HTTP requests below can finish
963 // with a dial still outstanding. So count
964 // our dials as work too so the leak checker
965 // doesn't complain at us.
968 return net
.Dial(netw
, addr
)
971 defer tr
.CloseIdleConnections()
972 c
:= &Client
{Transport
: tr
}
973 reqs
:= make(chan string)
976 for i
:= 0; i
< maxProcs
*2; i
++ {
978 for req
:= range reqs
{
979 res
, err
:= c
.Get(ts
.URL
+ "/?echo=" + req
)
981 t
.Errorf("error on req %s: %v", req
, err
)
985 all
, err
:= ioutil
.ReadAll(res
.Body
)
987 t
.Errorf("read error on req %s: %v", req
, err
)
991 if string(all
) != req
{
992 t
.Errorf("body of req %s = %q; want %q", req
, all
, req
)
999 for i
:= 0; i
< numReqs
; i
++ {
1000 reqs
<- fmt
.Sprintf("request-%d", i
)
1005 func TestIssue4191_InfiniteGetTimeout(t
*testing
.T
) {
1008 mux
:= NewServeMux()
1009 mux
.HandleFunc("/get", func(w ResponseWriter
, r
*Request
) {
1010 io
.Copy(w
, neverEnding('a'))
1012 ts
:= httptest
.NewServer(mux
)
1013 timeout
:= 100 * time
.Millisecond
1016 Transport
: &Transport
{
1017 Dial
: func(n
, addr
string) (net
.Conn
, error
) {
1018 conn
, err
:= net
.Dial(n
, addr
)
1022 conn
.SetDeadline(time
.Now().Add(timeout
))
1024 conn
= NewLoggingConn("client", conn
)
1028 DisableKeepAlives
: true,
1034 if testing
.Short() {
1037 for i
:= 0; i
< nRuns
; i
++ {
1039 println("run", i
+1, "of", nRuns
)
1041 sres
, err
:= client
.Get(ts
.URL
+ "/get")
1044 // Make the timeout longer, once.
1046 t
.Logf("increasing timeout")
1051 t
.Errorf("Error issuing GET: %v", err
)
1054 _
, err
= io
.Copy(ioutil
.Discard
, sres
.Body
)
1056 t
.Errorf("Unexpected successful copy")
1061 println("tests complete; waiting for handlers to finish")
1066 func TestIssue4191_InfiniteGetToPutTimeout(t
*testing
.T
) {
1069 mux
:= NewServeMux()
1070 mux
.HandleFunc("/get", func(w ResponseWriter
, r
*Request
) {
1071 io
.Copy(w
, neverEnding('a'))
1073 mux
.HandleFunc("/put", func(w ResponseWriter
, r
*Request
) {
1074 defer r
.Body
.Close()
1075 io
.Copy(ioutil
.Discard
, r
.Body
)
1077 ts
:= httptest
.NewServer(mux
)
1078 timeout
:= 100 * time
.Millisecond
1081 Transport
: &Transport
{
1082 Dial
: func(n
, addr
string) (net
.Conn
, error
) {
1083 conn
, err
:= net
.Dial(n
, addr
)
1087 conn
.SetDeadline(time
.Now().Add(timeout
))
1089 conn
= NewLoggingConn("client", conn
)
1093 DisableKeepAlives
: true,
1099 if testing
.Short() {
1102 for i
:= 0; i
< nRuns
; i
++ {
1104 println("run", i
+1, "of", nRuns
)
1106 sres
, err
:= client
.Get(ts
.URL
+ "/get")
1109 // Make the timeout longer, once.
1111 t
.Logf("increasing timeout")
1116 t
.Errorf("Error issuing GET: %v", err
)
1119 req
, _
:= NewRequest("PUT", ts
.URL
+"/put", sres
.Body
)
1120 _
, err
= client
.Do(req
)
1123 t
.Errorf("Unexpected successful PUT")
1129 println("tests complete; waiting for handlers to finish")
1134 func TestTransportResponseHeaderTimeout(t
*testing
.T
) {
1136 if testing
.Short() {
1137 t
.Skip("skipping timeout test in -short mode")
1139 mux
:= NewServeMux()
1140 mux
.HandleFunc("/fast", func(w ResponseWriter
, r
*Request
) {})
1141 mux
.HandleFunc("/slow", func(w ResponseWriter
, r
*Request
) {
1142 time
.Sleep(2 * time
.Second
)
1144 ts
:= httptest
.NewServer(mux
)
1148 ResponseHeaderTimeout
: 500 * time
.Millisecond
,
1150 defer tr
.CloseIdleConnections()
1151 c
:= &Client
{Transport
: tr
}
1158 {path
: "/fast", want
: 200},
1159 {path
: "/slow", wantErr
: "timeout awaiting response headers"},
1160 {path
: "/fast", want
: 200},
1162 for i
, tt
:= range tests
{
1163 res
, err
:= c
.Get(ts
.URL
+ tt
.path
)
1165 if strings
.Contains(err
.Error(), tt
.wantErr
) {
1168 t
.Errorf("%d. unexpected error: %v", i
, err
)
1171 if tt
.wantErr
!= "" {
1172 t
.Errorf("%d. no error. expected error: %v", i
, tt
.wantErr
)
1175 if res
.StatusCode
!= tt
.want
{
1176 t
.Errorf("%d for path %q status = %d; want %d", i
, tt
.path
, res
.StatusCode
, tt
.want
)
1181 func TestTransportCancelRequest(t
*testing
.T
) {
1183 if testing
.Short() {
1184 t
.Skip("skipping test in -short mode")
1186 unblockc
:= make(chan bool)
1187 ts
:= httptest
.NewServer(HandlerFunc(func(w ResponseWriter
, r
*Request
) {
1188 fmt
.Fprintf(w
, "Hello")
1189 w
.(Flusher
).Flush() // send headers and some body
1193 defer close(unblockc
)
1196 defer tr
.CloseIdleConnections()
1197 c
:= &Client
{Transport
: tr
}
1199 req
, _
:= NewRequest("GET", ts
.URL
, nil)
1200 res
, err
:= c
.Do(req
)
1205 time
.Sleep(1 * time
.Second
)
1206 tr
.CancelRequest(req
)
1209 body
, err
:= ioutil
.ReadAll(res
.Body
)
1213 t
.Error("expected an error reading the body")
1215 if string(body
) != "Hello" {
1216 t
.Errorf("Body = %q; want Hello", body
)
1218 if d
< 500*time
.Millisecond
{
1219 t
.Errorf("expected ~1 second delay; got %v", d
)
1221 // Verify no outstanding requests after readLoop/writeLoop
1222 // goroutines shut down.
1223 for tries
:= 3; tries
> 0; tries
-- {
1224 n
:= tr
.NumPendingRequestsForTesting()
1228 time
.Sleep(100 * time
.Millisecond
)
1230 t
.Errorf("pending requests = %d; want 0", n
)
1235 // golang.org/issue/3672 -- Client can't close HTTP stream
1236 // Calling Close on a Response.Body used to just read until EOF.
1237 // Now it actually closes the TCP connection.
1238 func TestTransportCloseResponseBody(t
*testing
.T
) {
1240 writeErr
:= make(chan error
, 1)
1241 msg
:= []byte("young\n")
1242 ts
:= httptest
.NewServer(HandlerFunc(func(w ResponseWriter
, r
*Request
) {
1244 _
, err
:= w
.Write(msg
)
1255 defer tr
.CloseIdleConnections()
1256 c
:= &Client
{Transport
: tr
}
1258 req
, _
:= NewRequest("GET", ts
.URL
, nil)
1259 defer tr
.CancelRequest(req
)
1261 res
, err
:= c
.Do(req
)
1267 buf
:= make([]byte, len(msg
)*repeats
)
1268 want
:= bytes
.Repeat(msg
, repeats
)
1270 _
, err
= io
.ReadFull(res
.Body
, buf
)
1274 if !bytes
.Equal(buf
, want
) {
1275 t
.Errorf("read %q; want %q", buf
, want
)
1277 didClose
:= make(chan error
, 1)
1279 didClose
<- res
.Body
.Close()
1282 case err
:= <-didClose
:
1284 t
.Errorf("Close = %v", err
)
1286 case <-time
.After(10 * time
.Second
):
1287 t
.Fatal("too long waiting for close")
1290 case err
:= <-writeErr
:
1292 t
.Errorf("expected non-nil write error")
1294 case <-time
.After(10 * time
.Second
):
1295 t
.Fatal("too long waiting for write error")
1299 type fooProto
struct{}
1301 func (fooProto
) RoundTrip(req
*Request
) (*Response
, error
) {
1305 Header
: make(Header
),
1306 Body
: ioutil
.NopCloser(strings
.NewReader("You wanted " + req
.URL
.String())),
1311 func TestTransportAltProto(t
*testing
.T
) {
1314 c
:= &Client
{Transport
: tr
}
1315 tr
.RegisterProtocol("foo", fooProto
{})
1316 res
, err
:= c
.Get("foo://bar.com/path")
1320 bodyb
, err
:= ioutil
.ReadAll(res
.Body
)
1324 body
:= string(bodyb
)
1325 if e
:= "You wanted foo://bar.com/path"; body
!= e
{
1326 t
.Errorf("got response %q, want %q", body
, e
)
1330 func TestTransportNoHost(t
*testing
.T
) {
1333 _
, err
:= tr
.RoundTrip(&Request
{
1334 Header
: make(Header
),
1339 want
:= "http: no Host in request URL"
1340 if got
:= fmt
.Sprint(err
); got
!= want
{
1341 t
.Errorf("error = %v; want %q", err
, want
)
1345 func TestTransportSocketLateBinding(t
*testing
.T
) {
1348 mux
:= NewServeMux()
1349 fooGate
:= make(chan bool, 1)
1350 mux
.HandleFunc("/foo", func(w ResponseWriter
, r
*Request
) {
1351 w
.Header().Set("foo-ipport", r
.RemoteAddr
)
1355 mux
.HandleFunc("/bar", func(w ResponseWriter
, r
*Request
) {
1356 w
.Header().Set("bar-ipport", r
.RemoteAddr
)
1358 ts
:= httptest
.NewServer(mux
)
1361 dialGate
:= make(chan bool, 1)
1363 Dial
: func(n
, addr
string) (net
.Conn
, error
) {
1365 return net
.Dial(n
, addr
)
1367 DisableKeepAlives
: false,
1369 defer tr
.CloseIdleConnections()
1374 dialGate
<- true // only allow one dial
1375 fooRes
, err
:= c
.Get(ts
.URL
+ "/foo")
1379 fooAddr
:= fooRes
.Header
.Get("foo-ipport")
1381 t
.Fatal("No addr on /foo request")
1383 time
.AfterFunc(200*time
.Millisecond
, func() {
1384 // let the foo response finish so we can use its
1385 // connection for /bar
1387 io
.Copy(ioutil
.Discard
, fooRes
.Body
)
1391 barRes
, err
:= c
.Get(ts
.URL
+ "/bar")
1395 barAddr
:= barRes
.Header
.Get("bar-ipport")
1396 if barAddr
!= fooAddr
{
1397 t
.Fatalf("/foo came from conn %q; /bar came from %q instead", fooAddr
, barAddr
)
1404 func TestTransportReading100Continue(t
*testing
.T
) {
1408 reqBody
:= func(n
int) string { return fmt
.Sprintf("request body %d", n
) }
1409 reqID
:= func(n
int) string { return fmt
.Sprintf("REQ-ID-%d", n
) }
1411 send100Response
:= func(w
*io
.PipeWriter
, r
*io
.PipeReader
) {
1414 br
:= bufio
.NewReader(r
)
1418 req
, err
:= ReadRequest(br
)
1426 slurp
, err
:= ioutil
.ReadAll(req
.Body
)
1428 t
.Errorf("Server request body slurp: %v", err
)
1431 id
:= req
.Header
.Get("Request-Id")
1432 resCode
:= req
.Header
.Get("X-Want-Response-Code")
1434 resCode
= "100 Continue"
1435 if string(slurp
) != reqBody(n
) {
1436 t
.Errorf("Server got %q, %v; want %q", slurp
, err
, reqBody(n
))
1439 body
:= fmt
.Sprintf("Response number %d", n
)
1440 v
:= []byte(strings
.Replace(fmt
.Sprintf(`HTTP/1.1 %s
1441 Date: Thu, 28 Feb 2013 17:55:41 GMT
1444 Content-Type: text/html
1448 %s`, resCode
, id
, len(body
), body
), "\n", "\r\n", -1))
1450 if id
== reqID(numReqs
) {
1458 Dial
: func(n
, addr
string) (net
.Conn
, error
) {
1459 sr
, sw
:= io
.Pipe() // server read/write
1460 cr
, cw
:= io
.Pipe() // client read/write
1461 conn
:= &rwTestConn
{
1464 closeFunc
: func() error
{
1470 go send100Response(cw
, sr
)
1473 DisableKeepAlives
: false,
1475 defer tr
.CloseIdleConnections()
1476 c
:= &Client
{Transport
: tr
}
1478 testResponse
:= func(req
*Request
, name
string, wantCode
int) {
1479 res
, err
:= c
.Do(req
)
1481 t
.Fatalf("%s: Do: %v", name
, err
)
1483 if res
.StatusCode
!= wantCode
{
1484 t
.Fatalf("%s: Response Statuscode=%d; want %d", name
, res
.StatusCode
, wantCode
)
1486 if id
, idBack
:= req
.Header
.Get("Request-Id"), res
.Header
.Get("Echo-Request-Id"); id
!= "" && id
!= idBack
{
1487 t
.Errorf("%s: response id %q != request id %q", name
, idBack
, id
)
1489 _
, err
= ioutil
.ReadAll(res
.Body
)
1491 t
.Fatalf("%s: Slurp error: %v", name
, err
)
1495 // Few 100 responses, making sure we're not off-by-one.
1496 for i
:= 1; i
<= numReqs
; i
++ {
1497 req
, _
:= NewRequest("POST", "http://dummy.tld/", strings
.NewReader(reqBody(i
)))
1498 req
.Header
.Set("Request-Id", reqID(i
))
1499 testResponse(req
, fmt
.Sprintf("100, %d/%d", i
, numReqs
), 200)
1502 // And some other informational 1xx but non-100 responses, to test
1503 // we return them but don't re-use the connection.
1504 for i
:= 1; i
<= numReqs
; i
++ {
1505 req
, _
:= NewRequest("POST", "http://other.tld/", strings
.NewReader(reqBody(i
)))
1506 req
.Header
.Set("X-Want-Response-Code", "123 Sesame Street")
1507 testResponse(req
, fmt
.Sprintf("123, %d/%d", i
, numReqs
), 123)
1511 type proxyFromEnvTest
struct {
1512 req
string // URL to fetch; blank means "http://example.com"
1519 func (t proxyFromEnvTest
) String() string {
1520 var buf bytes
.Buffer
1522 fmt
.Fprintf(&buf
, "http_proxy=%q", t
.env
)
1525 fmt
.Fprintf(&buf
, " no_proxy=%q", t
.noenv
)
1527 req
:= "http://example.com"
1531 fmt
.Fprintf(&buf
, " req=%q", req
)
1532 return strings
.TrimSpace(buf
.String())
1535 var proxyFromEnvTests
= []proxyFromEnvTest
{
1536 {env
: "127.0.0.1:8080", want
: "http://127.0.0.1:8080"},
1537 {env
: "cache.corp.example.com:1234", want
: "http://cache.corp.example.com:1234"},
1538 {env
: "cache.corp.example.com", want
: "http://cache.corp.example.com"},
1539 {env
: "https://cache.corp.example.com", want
: "https://cache.corp.example.com"},
1540 {env
: "http://127.0.0.1:8080", want
: "http://127.0.0.1:8080"},
1541 {env
: "https://127.0.0.1:8080", want
: "https://127.0.0.1:8080"},
1543 {noenv
: "example.com", req
: "http://example.com/", env
: "proxy", want
: "<nil>"},
1544 {noenv
: ".example.com", req
: "http://example.com/", env
: "proxy", want
: "<nil>"},
1545 {noenv
: "ample.com", req
: "http://example.com/", env
: "proxy", want
: "http://proxy"},
1546 {noenv
: "example.com", req
: "http://foo.example.com/", env
: "proxy", want
: "<nil>"},
1547 {noenv
: ".foo.com", req
: "http://example.com/", env
: "proxy", want
: "http://proxy"},
1550 func TestProxyFromEnvironment(t
*testing
.T
) {
1551 os
.Setenv("HTTP_PROXY", "")
1552 os
.Setenv("http_proxy", "")
1553 os
.Setenv("NO_PROXY", "")
1554 os
.Setenv("no_proxy", "")
1555 for _
, tt
:= range proxyFromEnvTests
{
1556 os
.Setenv("HTTP_PROXY", tt
.env
)
1557 os
.Setenv("NO_PROXY", tt
.noenv
)
1560 reqURL
= "http://example.com"
1562 req
, _
:= NewRequest("GET", reqURL
, nil)
1563 url
, err
:= ProxyFromEnvironment(req
)
1564 if g
, e
:= fmt
.Sprintf("%v", err
), fmt
.Sprintf("%v", tt
.wanterr
); g
!= e
{
1565 t
.Errorf("%v: got error = %q, want %q", tt
, g
, e
)
1568 if got
:= fmt
.Sprintf("%s", url
); got
!= tt
.want
{
1569 t
.Errorf("%v: got URL = %q, want %q", tt
, url
, tt
.want
)
1574 // rgz is a gzip quine that uncompresses to itself.
1576 0x1f, 0x8b, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00,
1577 0x00, 0x00, 0x72, 0x65, 0x63, 0x75, 0x72, 0x73,
1578 0x69, 0x76, 0x65, 0x00, 0x92, 0xef, 0xe6, 0xe0,
1579 0x60, 0x00, 0x83, 0xa2, 0xd4, 0xe4, 0xd2, 0xa2,
1580 0xe2, 0xcc, 0xb2, 0x54, 0x06, 0x00, 0x00, 0x17,
1581 0x00, 0xe8, 0xff, 0x92, 0xef, 0xe6, 0xe0, 0x60,
1582 0x00, 0x83, 0xa2, 0xd4, 0xe4, 0xd2, 0xa2, 0xe2,
1583 0xcc, 0xb2, 0x54, 0x06, 0x00, 0x00, 0x17, 0x00,
1584 0xe8, 0xff, 0x42, 0x12, 0x46, 0x16, 0x06, 0x00,
1585 0x05, 0x00, 0xfa, 0xff, 0x42, 0x12, 0x46, 0x16,
1586 0x06, 0x00, 0x05, 0x00, 0xfa, 0xff, 0x00, 0x05,
1587 0x00, 0xfa, 0xff, 0x00, 0x14, 0x00, 0xeb, 0xff,
1588 0x42, 0x12, 0x46, 0x16, 0x06, 0x00, 0x05, 0x00,
1589 0xfa, 0xff, 0x00, 0x05, 0x00, 0xfa, 0xff, 0x00,
1590 0x14, 0x00, 0xeb, 0xff, 0x42, 0x88, 0x21, 0xc4,
1591 0x00, 0x00, 0x14, 0x00, 0xeb, 0xff, 0x42, 0x88,
1592 0x21, 0xc4, 0x00, 0x00, 0x14, 0x00, 0xeb, 0xff,
1593 0x42, 0x88, 0x21, 0xc4, 0x00, 0x00, 0x14, 0x00,
1594 0xeb, 0xff, 0x42, 0x88, 0x21, 0xc4, 0x00, 0x00,
1595 0x14, 0x00, 0xeb, 0xff, 0x42, 0x88, 0x21, 0xc4,
1596 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00,
1597 0x00, 0xff, 0xff, 0x00, 0x17, 0x00, 0xe8, 0xff,
1598 0x42, 0x88, 0x21, 0xc4, 0x00, 0x00, 0x00, 0x00,
1599 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00,
1600 0x17, 0x00, 0xe8, 0xff, 0x42, 0x12, 0x46, 0x16,
1601 0x06, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x08,
1602 0x00, 0xf7, 0xff, 0x3d, 0xb1, 0x20, 0x85, 0xfa,
1603 0x00, 0x00, 0x00, 0x42, 0x12, 0x46, 0x16, 0x06,
1604 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x08, 0x00,
1605 0xf7, 0xff, 0x3d, 0xb1, 0x20, 0x85, 0xfa, 0x00,
1606 0x00, 0x00, 0x3d, 0xb1, 0x20, 0x85, 0xfa, 0x00,