OSDN Git Service

Update Go compiler, library, and testsuite on gcc 4.7 branch.
[pf3gnuchains/gcc-fork.git] / libgo / go / net / http / transport_test.go
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.
4
5 // Tests for transport.go
6
7 package http_test
8
9 import (
10         "bytes"
11         "compress/gzip"
12         "crypto/rand"
13         "fmt"
14         "io"
15         "io/ioutil"
16         . "net/http"
17         "net/http/httptest"
18         "net/url"
19         "os"
20         "runtime"
21         "strconv"
22         "strings"
23         "testing"
24         "time"
25 )
26
27 // TODO: test 5 pipelined requests with responses: 1) OK, 2) OK, Connection: Close
28 //       and then verify that the final 2 responses get errors back.
29
30 // hostPortHandler writes back the client's "host:port".
31 var hostPortHandler = HandlerFunc(func(w ResponseWriter, r *Request) {
32         if r.FormValue("close") == "true" {
33                 w.Header().Set("Connection", "close")
34         }
35         w.Write([]byte(r.RemoteAddr))
36 })
37
38 // Two subsequent requests and verify their response is the same.
39 // The response from the server is our own IP:port
40 func TestTransportKeepAlives(t *testing.T) {
41         ts := httptest.NewServer(hostPortHandler)
42         defer ts.Close()
43
44         for _, disableKeepAlive := range []bool{false, true} {
45                 tr := &Transport{DisableKeepAlives: disableKeepAlive}
46                 c := &Client{Transport: tr}
47
48                 fetch := func(n int) string {
49                         res, err := c.Get(ts.URL)
50                         if err != nil {
51                                 t.Fatalf("error in disableKeepAlive=%v, req #%d, GET: %v", disableKeepAlive, n, err)
52                         }
53                         body, err := ioutil.ReadAll(res.Body)
54                         if err != nil {
55                                 t.Fatalf("error in disableKeepAlive=%v, req #%d, ReadAll: %v", disableKeepAlive, n, err)
56                         }
57                         return string(body)
58                 }
59
60                 body1 := fetch(1)
61                 body2 := fetch(2)
62
63                 bodiesDiffer := body1 != body2
64                 if bodiesDiffer != disableKeepAlive {
65                         t.Errorf("error in disableKeepAlive=%v. unexpected bodiesDiffer=%v; body1=%q; body2=%q",
66                                 disableKeepAlive, bodiesDiffer, body1, body2)
67                 }
68         }
69 }
70
71 func TestTransportConnectionCloseOnResponse(t *testing.T) {
72         ts := httptest.NewServer(hostPortHandler)
73         defer ts.Close()
74
75         for _, connectionClose := range []bool{false, true} {
76                 tr := &Transport{}
77                 c := &Client{Transport: tr}
78
79                 fetch := func(n int) string {
80                         req := new(Request)
81                         var err error
82                         req.URL, err = url.Parse(ts.URL + fmt.Sprintf("/?close=%v", connectionClose))
83                         if err != nil {
84                                 t.Fatalf("URL parse error: %v", err)
85                         }
86                         req.Method = "GET"
87                         req.Proto = "HTTP/1.1"
88                         req.ProtoMajor = 1
89                         req.ProtoMinor = 1
90
91                         res, err := c.Do(req)
92                         if err != nil {
93                                 t.Fatalf("error in connectionClose=%v, req #%d, Do: %v", connectionClose, n, err)
94                         }
95                         body, err := ioutil.ReadAll(res.Body)
96                         defer res.Body.Close()
97                         if err != nil {
98                                 t.Fatalf("error in connectionClose=%v, req #%d, ReadAll: %v", connectionClose, n, err)
99                         }
100                         return string(body)
101                 }
102
103                 body1 := fetch(1)
104                 body2 := fetch(2)
105                 bodiesDiffer := body1 != body2
106                 if bodiesDiffer != connectionClose {
107                         t.Errorf("error in connectionClose=%v. unexpected bodiesDiffer=%v; body1=%q; body2=%q",
108                                 connectionClose, bodiesDiffer, body1, body2)
109                 }
110         }
111 }
112
113 func TestTransportConnectionCloseOnRequest(t *testing.T) {
114         ts := httptest.NewServer(hostPortHandler)
115         defer ts.Close()
116
117         for _, connectionClose := range []bool{false, true} {
118                 tr := &Transport{}
119                 c := &Client{Transport: tr}
120
121                 fetch := func(n int) string {
122                         req := new(Request)
123                         var err error
124                         req.URL, err = url.Parse(ts.URL)
125                         if err != nil {
126                                 t.Fatalf("URL parse error: %v", err)
127                         }
128                         req.Method = "GET"
129                         req.Proto = "HTTP/1.1"
130                         req.ProtoMajor = 1
131                         req.ProtoMinor = 1
132                         req.Close = connectionClose
133
134                         res, err := c.Do(req)
135                         if err != nil {
136                                 t.Fatalf("error in connectionClose=%v, req #%d, Do: %v", connectionClose, n, err)
137                         }
138                         body, err := ioutil.ReadAll(res.Body)
139                         if err != nil {
140                                 t.Fatalf("error in connectionClose=%v, req #%d, ReadAll: %v", connectionClose, n, err)
141                         }
142                         return string(body)
143                 }
144
145                 body1 := fetch(1)
146                 body2 := fetch(2)
147                 bodiesDiffer := body1 != body2
148                 if bodiesDiffer != connectionClose {
149                         t.Errorf("error in connectionClose=%v. unexpected bodiesDiffer=%v; body1=%q; body2=%q",
150                                 connectionClose, bodiesDiffer, body1, body2)
151                 }
152         }
153 }
154
155 func TestTransportIdleCacheKeys(t *testing.T) {
156         ts := httptest.NewServer(hostPortHandler)
157         defer ts.Close()
158
159         tr := &Transport{DisableKeepAlives: false}
160         c := &Client{Transport: tr}
161
162         if e, g := 0, len(tr.IdleConnKeysForTesting()); e != g {
163                 t.Errorf("After CloseIdleConnections expected %d idle conn cache keys; got %d", e, g)
164         }
165
166         resp, err := c.Get(ts.URL)
167         if err != nil {
168                 t.Error(err)
169         }
170         ioutil.ReadAll(resp.Body)
171
172         keys := tr.IdleConnKeysForTesting()
173         if e, g := 1, len(keys); e != g {
174                 t.Fatalf("After Get expected %d idle conn cache keys; got %d", e, g)
175         }
176
177         if e := "|http|" + ts.Listener.Addr().String(); keys[0] != e {
178                 t.Errorf("Expected idle cache key %q; got %q", e, keys[0])
179         }
180
181         tr.CloseIdleConnections()
182         if e, g := 0, len(tr.IdleConnKeysForTesting()); e != g {
183                 t.Errorf("After CloseIdleConnections expected %d idle conn cache keys; got %d", e, g)
184         }
185 }
186
187 func TestTransportMaxPerHostIdleConns(t *testing.T) {
188         resch := make(chan string)
189         gotReq := make(chan bool)
190         ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
191                 gotReq <- true
192                 msg := <-resch
193                 _, err := w.Write([]byte(msg))
194                 if err != nil {
195                         t.Fatalf("Write: %v", err)
196                 }
197         }))
198         defer ts.Close()
199         maxIdleConns := 2
200         tr := &Transport{DisableKeepAlives: false, MaxIdleConnsPerHost: maxIdleConns}
201         c := &Client{Transport: tr}
202
203         // Start 3 outstanding requests and wait for the server to get them.
204         // Their responses will hang until we we write to resch, though.
205         donech := make(chan bool)
206         doReq := func() {
207                 resp, err := c.Get(ts.URL)
208                 if err != nil {
209                         t.Error(err)
210                 }
211                 _, err = ioutil.ReadAll(resp.Body)
212                 if err != nil {
213                         t.Fatalf("ReadAll: %v", err)
214                 }
215                 donech <- true
216         }
217         go doReq()
218         <-gotReq
219         go doReq()
220         <-gotReq
221         go doReq()
222         <-gotReq
223
224         if e, g := 0, len(tr.IdleConnKeysForTesting()); e != g {
225                 t.Fatalf("Before writes, expected %d idle conn cache keys; got %d", e, g)
226         }
227
228         resch <- "res1"
229         <-donech
230         keys := tr.IdleConnKeysForTesting()
231         if e, g := 1, len(keys); e != g {
232                 t.Fatalf("after first response, expected %d idle conn cache keys; got %d", e, g)
233         }
234         cacheKey := "|http|" + ts.Listener.Addr().String()
235         if keys[0] != cacheKey {
236                 t.Fatalf("Expected idle cache key %q; got %q", cacheKey, keys[0])
237         }
238         if e, g := 1, tr.IdleConnCountForTesting(cacheKey); e != g {
239                 t.Errorf("after first response, expected %d idle conns; got %d", e, g)
240         }
241
242         resch <- "res2"
243         <-donech
244         if e, g := 2, tr.IdleConnCountForTesting(cacheKey); e != g {
245                 t.Errorf("after second response, expected %d idle conns; got %d", e, g)
246         }
247
248         resch <- "res3"
249         <-donech
250         if e, g := maxIdleConns, tr.IdleConnCountForTesting(cacheKey); e != g {
251                 t.Errorf("after third response, still expected %d idle conns; got %d", e, g)
252         }
253 }
254
255 func TestTransportServerClosingUnexpectedly(t *testing.T) {
256         ts := httptest.NewServer(hostPortHandler)
257         defer ts.Close()
258
259         tr := &Transport{}
260         c := &Client{Transport: tr}
261
262         fetch := func(n, retries int) string {
263                 condFatalf := func(format string, arg ...interface{}) {
264                         if retries <= 0 {
265                                 t.Fatalf(format, arg...)
266                         }
267                         t.Logf("retrying shortly after expected error: "+format, arg...)
268                         time.Sleep(time.Second / time.Duration(retries))
269                 }
270                 for retries >= 0 {
271                         retries--
272                         res, err := c.Get(ts.URL)
273                         if err != nil {
274                                 condFatalf("error in req #%d, GET: %v", n, err)
275                                 continue
276                         }
277                         body, err := ioutil.ReadAll(res.Body)
278                         if err != nil {
279                                 condFatalf("error in req #%d, ReadAll: %v", n, err)
280                                 continue
281                         }
282                         res.Body.Close()
283                         return string(body)
284                 }
285                 panic("unreachable")
286         }
287
288         body1 := fetch(1, 0)
289         body2 := fetch(2, 0)
290
291         ts.CloseClientConnections() // surprise!
292
293         // This test has an expected race. Sleeping for 25 ms prevents
294         // it on most fast machines, causing the next fetch() call to
295         // succeed quickly.  But if we do get errors, fetch() will retry 5
296         // times with some delays between.
297         time.Sleep(25 * time.Millisecond)
298
299         body3 := fetch(3, 5)
300
301         if body1 != body2 {
302                 t.Errorf("expected body1 and body2 to be equal")
303         }
304         if body2 == body3 {
305                 t.Errorf("expected body2 and body3 to be different")
306         }
307 }
308
309 // Test for http://golang.org/issue/2616 (appropriate issue number)
310 // This fails pretty reliably with GOMAXPROCS=100 or something high.
311 func TestStressSurpriseServerCloses(t *testing.T) {
312         if testing.Short() {
313                 t.Logf("skipping test in short mode")
314                 return
315         }
316         ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
317                 w.Header().Set("Content-Length", "5")
318                 w.Header().Set("Content-Type", "text/plain")
319                 w.Write([]byte("Hello"))
320                 w.(Flusher).Flush()
321                 conn, buf, _ := w.(Hijacker).Hijack()
322                 buf.Flush()
323                 conn.Close()
324         }))
325         defer ts.Close()
326
327         tr := &Transport{DisableKeepAlives: false}
328         c := &Client{Transport: tr}
329
330         // Do a bunch of traffic from different goroutines. Send to activityc
331         // after each request completes, regardless of whether it failed.
332         const (
333                 numClients    = 50
334                 reqsPerClient = 250
335         )
336         activityc := make(chan bool)
337         for i := 0; i < numClients; i++ {
338                 go func() {
339                         for i := 0; i < reqsPerClient; i++ {
340                                 res, err := c.Get(ts.URL)
341                                 if err == nil {
342                                         // We expect errors since the server is
343                                         // hanging up on us after telling us to
344                                         // send more requests, so we don't
345                                         // actually care what the error is.
346                                         // But we want to close the body in cases
347                                         // where we won the race.
348                                         res.Body.Close()
349                                 }
350                                 activityc <- true
351                         }
352                 }()
353         }
354
355         // Make sure all the request come back, one way or another.
356         for i := 0; i < numClients*reqsPerClient; i++ {
357                 select {
358                 case <-activityc:
359                 case <-time.After(5 * time.Second):
360                         t.Fatalf("presumed deadlock; no HTTP client activity seen in awhile")
361                 }
362         }
363 }
364
365 // TestTransportHeadResponses verifies that we deal with Content-Lengths
366 // with no bodies properly
367 func TestTransportHeadResponses(t *testing.T) {
368         ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
369                 if r.Method != "HEAD" {
370                         panic("expected HEAD; got " + r.Method)
371                 }
372                 w.Header().Set("Content-Length", "123")
373                 w.WriteHeader(200)
374         }))
375         defer ts.Close()
376
377         tr := &Transport{DisableKeepAlives: false}
378         c := &Client{Transport: tr}
379         for i := 0; i < 2; i++ {
380                 res, err := c.Head(ts.URL)
381                 if err != nil {
382                         t.Errorf("error on loop %d: %v", i, err)
383                 }
384                 if e, g := "123", res.Header.Get("Content-Length"); e != g {
385                         t.Errorf("loop %d: expected Content-Length header of %q, got %q", i, e, g)
386                 }
387                 if e, g := int64(0), res.ContentLength; e != g {
388                         t.Errorf("loop %d: expected res.ContentLength of %v, got %v", i, e, g)
389                 }
390         }
391 }
392
393 // TestTransportHeadChunkedResponse verifies that we ignore chunked transfer-encoding
394 // on responses to HEAD requests.
395 func TestTransportHeadChunkedResponse(t *testing.T) {
396         ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
397                 if r.Method != "HEAD" {
398                         panic("expected HEAD; got " + r.Method)
399                 }
400                 w.Header().Set("Transfer-Encoding", "chunked") // client should ignore
401                 w.Header().Set("x-client-ipport", r.RemoteAddr)
402                 w.WriteHeader(200)
403         }))
404         defer ts.Close()
405
406         tr := &Transport{DisableKeepAlives: false}
407         c := &Client{Transport: tr}
408
409         res1, err := c.Head(ts.URL)
410         if err != nil {
411                 t.Fatalf("request 1 error: %v", err)
412         }
413         res2, err := c.Head(ts.URL)
414         if err != nil {
415                 t.Fatalf("request 2 error: %v", err)
416         }
417         if v1, v2 := res1.Header.Get("x-client-ipport"), res2.Header.Get("x-client-ipport"); v1 != v2 {
418                 t.Errorf("ip/ports differed between head requests: %q vs %q", v1, v2)
419         }
420 }
421
422 var roundTripTests = []struct {
423         accept       string
424         expectAccept string
425         compressed   bool
426 }{
427         // Requests with no accept-encoding header use transparent compression
428         {"", "gzip", false},
429         // Requests with other accept-encoding should pass through unmodified
430         {"foo", "foo", false},
431         // Requests with accept-encoding == gzip should be passed through
432         {"gzip", "gzip", true},
433 }
434
435 // Test that the modification made to the Request by the RoundTripper is cleaned up
436 func TestRoundTripGzip(t *testing.T) {
437         const responseBody = "test response body"
438         ts := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, req *Request) {
439                 accept := req.Header.Get("Accept-Encoding")
440                 if expect := req.FormValue("expect_accept"); accept != expect {
441                         t.Errorf("in handler, test %v: Accept-Encoding = %q, want %q",
442                                 req.FormValue("testnum"), accept, expect)
443                 }
444                 if accept == "gzip" {
445                         rw.Header().Set("Content-Encoding", "gzip")
446                         gz := gzip.NewWriter(rw)
447                         gz.Write([]byte(responseBody))
448                         gz.Close()
449                 } else {
450                         rw.Header().Set("Content-Encoding", accept)
451                         rw.Write([]byte(responseBody))
452                 }
453         }))
454         defer ts.Close()
455
456         for i, test := range roundTripTests {
457                 // Test basic request (no accept-encoding)
458                 req, _ := NewRequest("GET", fmt.Sprintf("%s/?testnum=%d&expect_accept=%s", ts.URL, i, test.expectAccept), nil)
459                 if test.accept != "" {
460                         req.Header.Set("Accept-Encoding", test.accept)
461                 }
462                 res, err := DefaultTransport.RoundTrip(req)
463                 var body []byte
464                 if test.compressed {
465                         gzip, err := gzip.NewReader(res.Body)
466                         if err != nil {
467                                 t.Errorf("%d. gzip NewReader: %v", i, err)
468                                 continue
469                         }
470                         body, err = ioutil.ReadAll(gzip)
471                         res.Body.Close()
472                 } else {
473                         body, err = ioutil.ReadAll(res.Body)
474                 }
475                 if err != nil {
476                         t.Errorf("%d. Error: %q", i, err)
477                         continue
478                 }
479                 if g, e := string(body), responseBody; g != e {
480                         t.Errorf("%d. body = %q; want %q", i, g, e)
481                 }
482                 if g, e := req.Header.Get("Accept-Encoding"), test.accept; g != e {
483                         t.Errorf("%d. Accept-Encoding = %q; want %q (it was mutated, in violation of RoundTrip contract)", i, g, e)
484                 }
485                 if g, e := res.Header.Get("Content-Encoding"), test.accept; g != e {
486                         t.Errorf("%d. Content-Encoding = %q; want %q", i, g, e)
487                 }
488         }
489
490 }
491
492 func TestTransportGzip(t *testing.T) {
493         const testString = "The test string aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
494         const nRandBytes = 1024 * 1024
495         ts := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, req *Request) {
496                 if g, e := req.Header.Get("Accept-Encoding"), "gzip"; g != e {
497                         t.Errorf("Accept-Encoding = %q, want %q", g, e)
498                 }
499                 rw.Header().Set("Content-Encoding", "gzip")
500                 if req.Method == "HEAD" {
501                         return
502                 }
503
504                 var w io.Writer = rw
505                 var buf bytes.Buffer
506                 if req.FormValue("chunked") == "0" {
507                         w = &buf
508                         defer io.Copy(rw, &buf)
509                         defer func() {
510                                 rw.Header().Set("Content-Length", strconv.Itoa(buf.Len()))
511                         }()
512                 }
513                 gz := gzip.NewWriter(w)
514                 gz.Write([]byte(testString))
515                 if req.FormValue("body") == "large" {
516                         io.CopyN(gz, rand.Reader, nRandBytes)
517                 }
518                 gz.Close()
519         }))
520         defer ts.Close()
521
522         for _, chunked := range []string{"1", "0"} {
523                 c := &Client{Transport: &Transport{}}
524
525                 // First fetch something large, but only read some of it.
526                 res, err := c.Get(ts.URL + "/?body=large&chunked=" + chunked)
527                 if err != nil {
528                         t.Fatalf("large get: %v", err)
529                 }
530                 buf := make([]byte, len(testString))
531                 n, err := io.ReadFull(res.Body, buf)
532                 if err != nil {
533                         t.Fatalf("partial read of large response: size=%d, %v", n, err)
534                 }
535                 if e, g := testString, string(buf); e != g {
536                         t.Errorf("partial read got %q, expected %q", g, e)
537                 }
538                 res.Body.Close()
539                 // Read on the body, even though it's closed
540                 n, err = res.Body.Read(buf)
541                 if n != 0 || err == nil {
542                         t.Errorf("expected error post-closed large Read; got = %d, %v", n, err)
543                 }
544
545                 // Then something small.
546                 res, err = c.Get(ts.URL + "/?chunked=" + chunked)
547                 if err != nil {
548                         t.Fatal(err)
549                 }
550                 body, err := ioutil.ReadAll(res.Body)
551                 if err != nil {
552                         t.Fatal(err)
553                 }
554                 if g, e := string(body), testString; g != e {
555                         t.Fatalf("body = %q; want %q", g, e)
556                 }
557                 if g, e := res.Header.Get("Content-Encoding"), ""; g != e {
558                         t.Fatalf("Content-Encoding = %q; want %q", g, e)
559                 }
560
561                 // Read on the body after it's been fully read:
562                 n, err = res.Body.Read(buf)
563                 if n != 0 || err == nil {
564                         t.Errorf("expected Read error after exhausted reads; got %d, %v", n, err)
565                 }
566                 res.Body.Close()
567                 n, err = res.Body.Read(buf)
568                 if n != 0 || err == nil {
569                         t.Errorf("expected Read error after Close; got %d, %v", n, err)
570                 }
571         }
572
573         // And a HEAD request too, because they're always weird.
574         c := &Client{Transport: &Transport{}}
575         res, err := c.Head(ts.URL)
576         if err != nil {
577                 t.Fatalf("Head: %v", err)
578         }
579         if res.StatusCode != 200 {
580                 t.Errorf("Head status=%d; want=200", res.StatusCode)
581         }
582 }
583
584 func TestTransportProxy(t *testing.T) {
585         ch := make(chan string, 1)
586         ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
587                 ch <- "real server"
588         }))
589         defer ts.Close()
590         proxy := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
591                 ch <- "proxy for " + r.URL.String()
592         }))
593         defer proxy.Close()
594
595         pu, err := url.Parse(proxy.URL)
596         if err != nil {
597                 t.Fatal(err)
598         }
599         c := &Client{Transport: &Transport{Proxy: ProxyURL(pu)}}
600         c.Head(ts.URL)
601         got := <-ch
602         want := "proxy for " + ts.URL + "/"
603         if got != want {
604                 t.Errorf("want %q, got %q", want, got)
605         }
606 }
607
608 // TestTransportGzipRecursive sends a gzip quine and checks that the
609 // client gets the same value back. This is more cute than anything,
610 // but checks that we don't recurse forever, and checks that
611 // Content-Encoding is removed.
612 func TestTransportGzipRecursive(t *testing.T) {
613         ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
614                 w.Header().Set("Content-Encoding", "gzip")
615                 w.Write(rgz)
616         }))
617         defer ts.Close()
618
619         c := &Client{Transport: &Transport{}}
620         res, err := c.Get(ts.URL)
621         if err != nil {
622                 t.Fatal(err)
623         }
624         body, err := ioutil.ReadAll(res.Body)
625         if err != nil {
626                 t.Fatal(err)
627         }
628         if !bytes.Equal(body, rgz) {
629                 t.Fatalf("Incorrect result from recursive gz:\nhave=%x\nwant=%x",
630                         body, rgz)
631         }
632         if g, e := res.Header.Get("Content-Encoding"), ""; g != e {
633                 t.Fatalf("Content-Encoding = %q; want %q", g, e)
634         }
635 }
636
637 // tests that persistent goroutine connections shut down when no longer desired.
638 func TestTransportPersistConnLeak(t *testing.T) {
639         gotReqCh := make(chan bool)
640         unblockCh := make(chan bool)
641         ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
642                 gotReqCh <- true
643                 <-unblockCh
644                 w.Header().Set("Content-Length", "0")
645                 w.WriteHeader(204)
646         }))
647         defer ts.Close()
648
649         tr := &Transport{}
650         c := &Client{Transport: tr}
651
652         n0 := runtime.NumGoroutine()
653
654         const numReq = 25
655         didReqCh := make(chan bool)
656         for i := 0; i < numReq; i++ {
657                 go func() {
658                         res, err := c.Get(ts.URL)
659                         didReqCh <- true
660                         if err != nil {
661                                 t.Errorf("client fetch error: %v", err)
662                                 return
663                         }
664                         res.Body.Close()
665                 }()
666         }
667
668         // Wait for all goroutines to be stuck in the Handler.
669         for i := 0; i < numReq; i++ {
670                 <-gotReqCh
671         }
672
673         nhigh := runtime.NumGoroutine()
674
675         // Tell all handlers to unblock and reply.
676         for i := 0; i < numReq; i++ {
677                 unblockCh <- true
678         }
679
680         // Wait for all HTTP clients to be done.
681         for i := 0; i < numReq; i++ {
682                 <-didReqCh
683         }
684
685         tr.CloseIdleConnections()
686         time.Sleep(100 * time.Millisecond)
687         runtime.GC()
688         runtime.GC() // even more.
689         nfinal := runtime.NumGoroutine()
690
691         growth := nfinal - n0
692
693         // We expect 0 or 1 extra goroutine, empirically.  Allow up to 5.
694         // Previously we were leaking one per numReq.
695         t.Logf("goroutine growth: %d -> %d -> %d (delta: %d)", n0, nhigh, nfinal, growth)
696         if int(growth) > 5 {
697                 t.Error("too many new goroutines")
698         }
699 }
700
701 type fooProto struct{}
702
703 func (fooProto) RoundTrip(req *Request) (*Response, error) {
704         res := &Response{
705                 Status:     "200 OK",
706                 StatusCode: 200,
707                 Header:     make(Header),
708                 Body:       ioutil.NopCloser(strings.NewReader("You wanted " + req.URL.String())),
709         }
710         return res, nil
711 }
712
713 func TestTransportAltProto(t *testing.T) {
714         tr := &Transport{}
715         c := &Client{Transport: tr}
716         tr.RegisterProtocol("foo", fooProto{})
717         res, err := c.Get("foo://bar.com/path")
718         if err != nil {
719                 t.Fatal(err)
720         }
721         bodyb, err := ioutil.ReadAll(res.Body)
722         if err != nil {
723                 t.Fatal(err)
724         }
725         body := string(bodyb)
726         if e := "You wanted foo://bar.com/path"; body != e {
727                 t.Errorf("got response %q, want %q", body, e)
728         }
729 }
730
731 var proxyFromEnvTests = []struct {
732         env     string
733         wanturl string
734         wanterr error
735 }{
736         {"127.0.0.1:8080", "http://127.0.0.1:8080", nil},
737         {"http://127.0.0.1:8080", "http://127.0.0.1:8080", nil},
738         {"https://127.0.0.1:8080", "https://127.0.0.1:8080", nil},
739         {"", "<nil>", nil},
740 }
741
742 func TestProxyFromEnvironment(t *testing.T) {
743         os.Setenv("HTTP_PROXY", "")
744         os.Setenv("http_proxy", "")
745         os.Setenv("NO_PROXY", "")
746         os.Setenv("no_proxy", "")
747         for i, tt := range proxyFromEnvTests {
748                 os.Setenv("HTTP_PROXY", tt.env)
749                 req, _ := NewRequest("GET", "http://example.com", nil)
750                 url, err := ProxyFromEnvironment(req)
751                 if g, e := fmt.Sprintf("%v", err), fmt.Sprintf("%v", tt.wanterr); g != e {
752                         t.Errorf("%d. got error = %q, want %q", i, g, e)
753                         continue
754                 }
755                 if got := fmt.Sprintf("%s", url); got != tt.wanturl {
756                         t.Errorf("%d. got URL = %q, want %q", i, url, tt.wanturl)
757                 }
758         }
759 }
760
761 // rgz is a gzip quine that uncompresses to itself.
762 var rgz = []byte{
763         0x1f, 0x8b, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00,
764         0x00, 0x00, 0x72, 0x65, 0x63, 0x75, 0x72, 0x73,
765         0x69, 0x76, 0x65, 0x00, 0x92, 0xef, 0xe6, 0xe0,
766         0x60, 0x00, 0x83, 0xa2, 0xd4, 0xe4, 0xd2, 0xa2,
767         0xe2, 0xcc, 0xb2, 0x54, 0x06, 0x00, 0x00, 0x17,
768         0x00, 0xe8, 0xff, 0x92, 0xef, 0xe6, 0xe0, 0x60,
769         0x00, 0x83, 0xa2, 0xd4, 0xe4, 0xd2, 0xa2, 0xe2,
770         0xcc, 0xb2, 0x54, 0x06, 0x00, 0x00, 0x17, 0x00,
771         0xe8, 0xff, 0x42, 0x12, 0x46, 0x16, 0x06, 0x00,
772         0x05, 0x00, 0xfa, 0xff, 0x42, 0x12, 0x46, 0x16,
773         0x06, 0x00, 0x05, 0x00, 0xfa, 0xff, 0x00, 0x05,
774         0x00, 0xfa, 0xff, 0x00, 0x14, 0x00, 0xeb, 0xff,
775         0x42, 0x12, 0x46, 0x16, 0x06, 0x00, 0x05, 0x00,
776         0xfa, 0xff, 0x00, 0x05, 0x00, 0xfa, 0xff, 0x00,
777         0x14, 0x00, 0xeb, 0xff, 0x42, 0x88, 0x21, 0xc4,
778         0x00, 0x00, 0x14, 0x00, 0xeb, 0xff, 0x42, 0x88,
779         0x21, 0xc4, 0x00, 0x00, 0x14, 0x00, 0xeb, 0xff,
780         0x42, 0x88, 0x21, 0xc4, 0x00, 0x00, 0x14, 0x00,
781         0xeb, 0xff, 0x42, 0x88, 0x21, 0xc4, 0x00, 0x00,
782         0x14, 0x00, 0xeb, 0xff, 0x42, 0x88, 0x21, 0xc4,
783         0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00,
784         0x00, 0xff, 0xff, 0x00, 0x17, 0x00, 0xe8, 0xff,
785         0x42, 0x88, 0x21, 0xc4, 0x00, 0x00, 0x00, 0x00,
786         0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00,
787         0x17, 0x00, 0xe8, 0xff, 0x42, 0x12, 0x46, 0x16,
788         0x06, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x08,
789         0x00, 0xf7, 0xff, 0x3d, 0xb1, 0x20, 0x85, 0xfa,
790         0x00, 0x00, 0x00, 0x42, 0x12, 0x46, 0x16, 0x06,
791         0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x08, 0x00,
792         0xf7, 0xff, 0x3d, 0xb1, 0x20, 0x85, 0xfa, 0x00,
793         0x00, 0x00, 0x3d, 0xb1, 0x20, 0x85, 0xfa, 0x00,
794         0x00, 0x00,
795 }