Source file
src/net/http/transport_test.go
1
2
3
4
5
6
7
8
9
10 package http_test
11
12 import (
13 "bufio"
14 "bytes"
15 "compress/gzip"
16 "context"
17 "crypto/rand"
18 "crypto/tls"
19 "crypto/x509"
20 "encoding/binary"
21 "errors"
22 "fmt"
23 "go/token"
24 "internal/nettest"
25 "internal/nettrace"
26 "io"
27 "log"
28 mrand "math/rand"
29 "net"
30 "net/http"
31 . "net/http"
32 "net/http/httptest"
33 "net/http/httptrace"
34 "net/http/httputil"
35 "net/http/internal/testcert"
36 "net/textproto"
37 "net/url"
38 "os"
39 "reflect"
40 "runtime"
41 "slices"
42 "strconv"
43 "strings"
44 "sync"
45 "sync/atomic"
46 "testing"
47 "testing/iotest"
48 "testing/synctest"
49 "time"
50
51 "golang.org/x/net/http/httpguts"
52 )
53
54
55
56
57
58 var hostPortHandler = HandlerFunc(func(w ResponseWriter, r *Request) {
59 if r.FormValue("close") == "true" {
60 w.Header().Set("Connection", "close")
61 }
62 w.Header().Set("X-Saw-Close", fmt.Sprint(r.Close))
63 w.Write([]byte(r.RemoteAddr))
64
65
66
67 if c, ok := ResponseWriterConnForTesting(w); ok {
68 fmt.Fprintf(w, ", %T %p", c, c)
69 }
70 })
71
72
73 type testCloseConn struct {
74 net.Conn
75 set *testConnSet
76 }
77
78 func (c *testCloseConn) Close() error {
79 c.set.remove(c)
80 return c.Conn.Close()
81 }
82
83
84
85 type testConnSet struct {
86 t *testing.T
87 mu sync.Mutex
88 closed map[net.Conn]bool
89 list []net.Conn
90 }
91
92 func (tcs *testConnSet) insert(c net.Conn) {
93 tcs.mu.Lock()
94 defer tcs.mu.Unlock()
95 tcs.closed[c] = false
96 tcs.list = append(tcs.list, c)
97 }
98
99 func (tcs *testConnSet) remove(c net.Conn) {
100 tcs.mu.Lock()
101 defer tcs.mu.Unlock()
102 tcs.closed[c] = true
103 }
104
105
106 func makeTestDial(t *testing.T) (*testConnSet, func(n, addr string) (net.Conn, error)) {
107 connSet := &testConnSet{
108 t: t,
109 closed: make(map[net.Conn]bool),
110 }
111 dial := func(n, addr string) (net.Conn, error) {
112 c, err := net.Dial(n, addr)
113 if err != nil {
114 return nil, err
115 }
116 tc := &testCloseConn{c, connSet}
117 connSet.insert(tc)
118 return tc, nil
119 }
120 return connSet, dial
121 }
122
123 func (tcs *testConnSet) check(t *testing.T) {
124 tcs.mu.Lock()
125 defer tcs.mu.Unlock()
126 for i := 4; i >= 0; i-- {
127 for i, c := range tcs.list {
128 if tcs.closed[c] {
129 continue
130 }
131 if i != 0 {
132
133
134 tcs.mu.Unlock()
135 time.Sleep(50 * time.Millisecond)
136 tcs.mu.Lock()
137 continue
138 }
139 t.Errorf("TCP connection #%d, %p (of %d total) was not closed", i+1, c, len(tcs.list))
140 }
141 }
142 }
143
144 func TestReuseRequest(t *testing.T) { run(t, testReuseRequest) }
145 func testReuseRequest(t *testing.T, mode testMode) {
146 ts := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
147 w.Write([]byte("{}"))
148 })).ts
149
150 c := ts.Client()
151 req, _ := NewRequest("GET", ts.URL, nil)
152 res, err := c.Do(req)
153 if err != nil {
154 t.Fatal(err)
155 }
156 err = res.Body.Close()
157 if err != nil {
158 t.Fatal(err)
159 }
160
161 res, err = c.Do(req)
162 if err != nil {
163 t.Fatal(err)
164 }
165 err = res.Body.Close()
166 if err != nil {
167 t.Fatal(err)
168 }
169 }
170
171
172
173 func TestTransportKeepAlives(t *testing.T) { run(t, testTransportKeepAlives, []testMode{http1Mode}) }
174 func testTransportKeepAlives(t *testing.T, mode testMode) {
175 ts := newClientServerTest(t, mode, hostPortHandler).ts
176
177 c := ts.Client()
178 for _, disableKeepAlive := range []bool{false, true} {
179 c.Transport.(*Transport).DisableKeepAlives = disableKeepAlive
180 fetch := func(n int) string {
181 res, err := c.Get(ts.URL)
182 if err != nil {
183 t.Fatalf("error in disableKeepAlive=%v, req #%d, GET: %v", disableKeepAlive, n, err)
184 }
185 body, err := io.ReadAll(res.Body)
186 if err != nil {
187 t.Fatalf("error in disableKeepAlive=%v, req #%d, ReadAll: %v", disableKeepAlive, n, err)
188 }
189 return string(body)
190 }
191
192 body1 := fetch(1)
193 body2 := fetch(2)
194
195 bodiesDiffer := body1 != body2
196 if bodiesDiffer != disableKeepAlive {
197 t.Errorf("error in disableKeepAlive=%v. unexpected bodiesDiffer=%v; body1=%q; body2=%q",
198 disableKeepAlive, bodiesDiffer, body1, body2)
199 }
200 }
201 }
202
203 func TestTransportConnectionCloseOnResponse(t *testing.T) {
204 run(t, testTransportConnectionCloseOnResponse, http3SkippedMode)
205 }
206 func testTransportConnectionCloseOnResponse(t *testing.T, mode testMode) {
207 ts := newClientServerTest(t, mode, hostPortHandler, optRealNet).ts
208
209 connSet, testDial := makeTestDial(t)
210
211 c := ts.Client()
212 tr := c.Transport.(*Transport)
213 tr.Dial = testDial
214
215 for _, connectionClose := range []bool{false, true} {
216 fetch := func(n int) string {
217 req := new(Request)
218 var err error
219 req.URL, err = url.Parse(ts.URL + fmt.Sprintf("/?close=%v", connectionClose))
220 if err != nil {
221 t.Fatalf("URL parse error: %v", err)
222 }
223 req.Method = "GET"
224 req.Proto = "HTTP/1.1"
225 req.ProtoMajor = 1
226 req.ProtoMinor = 1
227
228 res, err := c.Do(req)
229 if err != nil {
230 t.Fatalf("error in connectionClose=%v, req #%d, Do: %v", connectionClose, n, err)
231 }
232 defer res.Body.Close()
233 body, err := io.ReadAll(res.Body)
234 if err != nil {
235 t.Fatalf("error in connectionClose=%v, req #%d, ReadAll: %v", connectionClose, n, err)
236 }
237 return string(body)
238 }
239
240 body1 := fetch(1)
241 body2 := fetch(2)
242 bodiesDiffer := body1 != body2
243 if bodiesDiffer != connectionClose {
244 t.Errorf("error in connectionClose=%v. unexpected bodiesDiffer=%v; body1=%q; body2=%q",
245 connectionClose, bodiesDiffer, body1, body2)
246 }
247
248 tr.CloseIdleConnections()
249 }
250
251 connSet.check(t)
252 }
253
254
255
256
257
258
259
260 func TestTransportConnectionCloseOnRequest(t *testing.T) {
261 run(t, testTransportConnectionCloseOnRequest, []testMode{http1Mode})
262 }
263 func testTransportConnectionCloseOnRequest(t *testing.T, mode testMode) {
264 ts := newClientServerTest(t, mode, hostPortHandler, optRealNet).ts
265
266 connSet, testDial := makeTestDial(t)
267
268 c := ts.Client()
269 tr := c.Transport.(*Transport)
270 tr.Dial = testDial
271 for _, reqClose := range []bool{false, true} {
272 fetch := func(n int) string {
273 req := new(Request)
274 var err error
275 req.URL, err = url.Parse(ts.URL)
276 if err != nil {
277 t.Fatalf("URL parse error: %v", err)
278 }
279 req.Method = "GET"
280 req.Proto = "HTTP/1.1"
281 req.ProtoMajor = 1
282 req.ProtoMinor = 1
283 req.Close = reqClose
284
285 res, err := c.Do(req)
286 if err != nil {
287 t.Fatalf("error in Request.Close=%v, req #%d, Do: %v", reqClose, n, err)
288 }
289 if got, want := res.Header.Get("X-Saw-Close"), fmt.Sprint(reqClose); got != want {
290 t.Errorf("for Request.Close = %v; handler's X-Saw-Close was %v; want %v",
291 reqClose, got, !reqClose)
292 }
293 body, err := io.ReadAll(res.Body)
294 if err != nil {
295 t.Fatalf("for Request.Close=%v, on request %v/2: ReadAll: %v", reqClose, n, err)
296 }
297 return string(body)
298 }
299
300 body1 := fetch(1)
301 body2 := fetch(2)
302
303 got := 1
304 if body1 != body2 {
305 got++
306 }
307 want := 1
308 if reqClose {
309 want = 2
310 }
311 if got != want {
312 t.Errorf("for Request.Close=%v: server saw %v unique connections, wanted %v\n\nbodies were: %q and %q",
313 reqClose, got, want, body1, body2)
314 }
315
316 tr.CloseIdleConnections()
317 }
318
319 connSet.check(t)
320 }
321
322
323
324
325 func TestTransportConnectionCloseOnRequestDisableKeepAlive(t *testing.T) {
326 run(t, testTransportConnectionCloseOnRequestDisableKeepAlive, []testMode{http1Mode})
327 }
328 func testTransportConnectionCloseOnRequestDisableKeepAlive(t *testing.T, mode testMode) {
329 ts := newClientServerTest(t, mode, hostPortHandler).ts
330
331 c := ts.Client()
332 c.Transport.(*Transport).DisableKeepAlives = true
333
334 res, err := c.Get(ts.URL)
335 if err != nil {
336 t.Fatal(err)
337 }
338 res.Body.Close()
339 if res.Header.Get("X-Saw-Close") != "true" {
340 t.Errorf("handler didn't see Connection: close ")
341 }
342 }
343
344
345
346 func TestTransportRespectRequestWantsClose(t *testing.T) {
347 run(t, testTransportRespectRequestWantsClose, []testMode{http1Mode})
348 }
349 func testTransportRespectRequestWantsClose(t *testing.T, mode testMode) {
350 tests := []struct {
351 disableKeepAlives bool
352 close bool
353 }{
354 {disableKeepAlives: false, close: false},
355 {disableKeepAlives: false, close: true},
356 {disableKeepAlives: true, close: false},
357 {disableKeepAlives: true, close: true},
358 }
359
360 for _, tc := range tests {
361 t.Run(fmt.Sprintf("DisableKeepAlive=%v,RequestClose=%v", tc.disableKeepAlives, tc.close),
362 func(t *testing.T) {
363 ts := newClientServerTest(t, mode, hostPortHandler).ts
364
365 c := ts.Client()
366 c.Transport.(*Transport).DisableKeepAlives = tc.disableKeepAlives
367 req, err := NewRequest("GET", ts.URL, nil)
368 if err != nil {
369 t.Fatal(err)
370 }
371 count := 0
372 trace := &httptrace.ClientTrace{
373 WroteHeaderField: func(key string, field []string) {
374 if key != "Connection" {
375 return
376 }
377 if httpguts.HeaderValuesContainsToken(field, "close") {
378 count += 1
379 }
380 },
381 }
382 req = req.WithContext(httptrace.WithClientTrace(req.Context(), trace))
383 req.Close = tc.close
384 res, err := c.Do(req)
385 if err != nil {
386 t.Fatal(err)
387 }
388 defer res.Body.Close()
389 if want := tc.disableKeepAlives || tc.close; count > 1 || (count == 1) != want {
390 t.Errorf("expecting want:%v, got 'Connection: close':%d", want, count)
391 }
392 })
393 }
394
395 }
396
397 func TestTransportIdleCacheKeys(t *testing.T) {
398 run(t, testTransportIdleCacheKeys, []testMode{http1Mode})
399 }
400 func testTransportIdleCacheKeys(t *testing.T, mode testMode) {
401 ts := newClientServerTest(t, mode, hostPortHandler, optRealNet).ts
402 c := ts.Client()
403 tr := c.Transport.(*Transport)
404
405 if e, g := 0, len(tr.IdleConnKeysForTesting()); e != g {
406 t.Errorf("After CloseIdleConnections expected %d idle conn cache keys; got %d", e, g)
407 }
408
409 resp, err := c.Get(ts.URL)
410 if err != nil {
411 t.Error(err)
412 }
413 io.ReadAll(resp.Body)
414
415 keys := tr.IdleConnKeysForTesting()
416 if e, g := 1, len(keys); e != g {
417 t.Fatalf("After Get expected %d idle conn cache keys; got %d", e, g)
418 }
419
420 if e := "|http|" + ts.Listener.Addr().String(); keys[0] != e {
421 t.Errorf("Expected idle cache key %q; got %q", e, keys[0])
422 }
423
424 tr.CloseIdleConnections()
425 if e, g := 0, len(tr.IdleConnKeysForTesting()); e != g {
426 t.Errorf("After CloseIdleConnections expected %d idle conn cache keys; got %d", e, g)
427 }
428 }
429
430
431
432 func TestTransportReadToEndReusesConn(t *testing.T) { run(t, testTransportReadToEndReusesConn) }
433 func testTransportReadToEndReusesConn(t *testing.T, mode testMode) {
434 const msg = "foobar"
435 ts := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
436 w.Header().Set("Remote-Addr", r.RemoteAddr)
437 if r.URL.Path == "/chunked/" {
438 w.WriteHeader(200)
439 w.(Flusher).Flush()
440 } else {
441 w.Header().Set("Content-Length", strconv.Itoa(len(msg)))
442 w.WriteHeader(200)
443 }
444 w.Write([]byte(msg))
445 })).ts
446
447 for pi, path := range []string{"/content-length/", "/chunked/"} {
448 wantLen := []int{len(msg), -1}[pi]
449 addrSeen := make(map[string]int)
450 for range 3 {
451 res, err := ts.Client().Get(ts.URL + path)
452 if err != nil {
453 t.Errorf("Get %s: %v", path, err)
454 continue
455 }
456 defer res.Body.Close()
457
458 if res.ContentLength != int64(wantLen) {
459 t.Errorf("%s res.ContentLength = %d; want %d", path, res.ContentLength, wantLen)
460 }
461 got, err := io.ReadAll(res.Body)
462 if string(got) != msg || err != nil {
463 t.Errorf("%s ReadAll(Body) = %q, %v; want %q, nil", path, string(got), err, msg)
464 }
465 addrSeen[res.Header.Get("Remote-Addr")]++
466 }
467 if len(addrSeen) != 1 {
468 t.Errorf("for %s, server saw %d distinct client addresses; want 1", path, len(addrSeen))
469 }
470 }
471 }
472
473
474
475
476
477 func TestTransportNotReadToEndConnectionReuse(t *testing.T) {
478 run(t, testTransportNotReadToEndConnectionReuse, []testMode{http1Mode, https1Mode})
479 }
480 func testTransportNotReadToEndConnectionReuse(t *testing.T, mode testMode) {
481 tests := []struct {
482 name string
483 bodyLen int
484 contentLenKnown bool
485 headRequest bool
486 timeBetweenReqs time.Duration
487 responseTime time.Duration
488 wantReuse bool
489 }{
490 {
491 name: "unconsumed body within drain limit",
492 bodyLen: 200 * 1024,
493 timeBetweenReqs: http.MaxPostCloseReadTime,
494 wantReuse: true,
495 },
496 {
497 name: "unconsumed body within drain limit with known length",
498 bodyLen: 200 * 1024,
499 contentLenKnown: true,
500 timeBetweenReqs: http.MaxPostCloseReadTime,
501 wantReuse: true,
502 },
503 {
504 name: "unconsumed body larger than drain limit",
505 bodyLen: 500 * 1024,
506 timeBetweenReqs: http.MaxPostCloseReadTime,
507 wantReuse: false,
508 },
509 {
510 name: "unconsumed body larger than drain limit with known length",
511 bodyLen: 500 * 1024,
512 contentLenKnown: true,
513 timeBetweenReqs: http.MaxPostCloseReadTime,
514 wantReuse: false,
515 },
516 {
517 name: "new requests start before drain for old requests are finished",
518 bodyLen: 200 * 1024,
519 timeBetweenReqs: 0,
520 responseTime: time.Minute,
521 wantReuse: false,
522 },
523 {
524
525
526 name: "unconsumed body larger than drain limit for HEAD request",
527 bodyLen: 500 * 1024,
528 headRequest: true,
529 wantReuse: true,
530 },
531 }
532
533 for _, tc := range tests {
534 subtest := func(t *testing.T) {
535 addrSeen := make(map[string]int)
536 ts := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
537 addrSeen[r.RemoteAddr]++
538 time.Sleep(tc.responseTime)
539 if tc.contentLenKnown {
540 w.Header().Add("Content-Length", strconv.Itoa(tc.bodyLen))
541 }
542 w.Write(slices.Repeat([]byte("a"), tc.bodyLen))
543 })).ts
544
545 var wg sync.WaitGroup
546 for range 10 {
547 wg.Go(func() {
548 method := http.MethodGet
549 if tc.headRequest {
550 method = http.MethodHead
551 }
552 ctx, cancel := context.WithCancel(context.Background())
553 req, err := http.NewRequestWithContext(ctx, method, ts.URL, nil)
554 if err != nil {
555 log.Fatal(err)
556 }
557 resp, err := ts.Client().Do(req)
558 if err != nil {
559 t.Fatal(err)
560 }
561 if resp.StatusCode != http.StatusOK {
562 t.Errorf("expected HTTP 200, got: %v", resp.StatusCode)
563 }
564 resp.Body.Close()
565
566
567 cancel()
568 if n, err := resp.Body.Read([]byte{}); n != 0 || err == nil {
569 t.Errorf("read after body has been closed should not succeed, but read %v byte with %v error", n, err)
570 }
571 })
572 time.Sleep(tc.timeBetweenReqs)
573 synctest.Wait()
574 }
575 wg.Wait()
576 if (len(addrSeen) == 1) != tc.wantReuse {
577 t.Errorf("want connection reuse to be %v, but %v connections were created", tc.wantReuse, len(addrSeen))
578 }
579 }
580 t.Run(tc.name, func(t *testing.T) {
581 synctest.Test(t, subtest)
582 })
583 }
584 }
585
586 func TestTransportMaxPerHostIdleConns(t *testing.T) {
587 run(t, testTransportMaxPerHostIdleConns, []testMode{http1Mode})
588 }
589 func testTransportMaxPerHostIdleConns(t *testing.T, mode testMode) {
590 stop := make(chan struct{})
591 defer close(stop)
592
593 resch := make(chan string)
594 gotReq := make(chan bool)
595 ts := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
596 gotReq <- true
597 var msg string
598 select {
599 case <-stop:
600 return
601 case msg = <-resch:
602 }
603 _, err := w.Write([]byte(msg))
604 if err != nil {
605 t.Errorf("Write: %v", err)
606 return
607 }
608 }), optRealNet).ts
609
610 c := ts.Client()
611 tr := c.Transport.(*Transport)
612 maxIdleConnsPerHost := 2
613 tr.MaxIdleConnsPerHost = maxIdleConnsPerHost
614
615
616
617 donech := make(chan bool)
618 doReq := func() {
619 defer func() {
620 select {
621 case <-stop:
622 return
623 case donech <- t.Failed():
624 }
625 }()
626 resp, err := c.Get(ts.URL)
627 if err != nil {
628 t.Error(err)
629 return
630 }
631 if _, err := io.ReadAll(resp.Body); err != nil {
632 t.Errorf("ReadAll: %v", err)
633 return
634 }
635 }
636 go doReq()
637 <-gotReq
638 go doReq()
639 <-gotReq
640 go doReq()
641 <-gotReq
642
643 if e, g := 0, len(tr.IdleConnKeysForTesting()); e != g {
644 t.Fatalf("Before writes, expected %d idle conn cache keys; got %d", e, g)
645 }
646
647 resch <- "res1"
648 <-donech
649 keys := tr.IdleConnKeysForTesting()
650 if e, g := 1, len(keys); e != g {
651 t.Fatalf("after first response, expected %d idle conn cache keys; got %d", e, g)
652 }
653 addr := ts.Listener.Addr().String()
654 cacheKey := "|http|" + addr
655 if keys[0] != cacheKey {
656 t.Fatalf("Expected idle cache key %q; got %q", cacheKey, keys[0])
657 }
658 if e, g := 1, tr.IdleConnCountForTesting("http", addr); e != g {
659 t.Errorf("after first response, expected %d idle conns; got %d", e, g)
660 }
661
662 resch <- "res2"
663 <-donech
664 if g, w := tr.IdleConnCountForTesting("http", addr), 2; g != w {
665 t.Errorf("after second response, idle conns = %d; want %d", g, w)
666 }
667
668 resch <- "res3"
669 <-donech
670 if g, w := tr.IdleConnCountForTesting("http", addr), maxIdleConnsPerHost; g != w {
671 t.Errorf("after third response, idle conns = %d; want %d", g, w)
672 }
673 }
674
675 func TestTransportMaxConnsPerHostIncludeDialInProgress(t *testing.T) {
676 run(t, testTransportMaxConnsPerHostIncludeDialInProgress, http3SkippedMode)
677 }
678 func testTransportMaxConnsPerHostIncludeDialInProgress(t *testing.T, mode testMode) {
679 ts := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
680 _, err := w.Write([]byte("foo"))
681 if err != nil {
682 t.Fatalf("Write: %v", err)
683 }
684 }), optRealNet).ts
685 c := ts.Client()
686 tr := c.Transport.(*Transport)
687 dialStarted := make(chan struct{})
688 stallDial := make(chan struct{})
689 tr.Dial = func(network, addr string) (net.Conn, error) {
690 dialStarted <- struct{}{}
691 <-stallDial
692 return net.Dial(network, addr)
693 }
694
695 tr.DisableKeepAlives = true
696 tr.MaxConnsPerHost = 1
697
698 preDial := make(chan struct{})
699 reqComplete := make(chan struct{})
700 doReq := func(reqId string) {
701 req, _ := NewRequest("GET", ts.URL, nil)
702 trace := &httptrace.ClientTrace{
703 GetConn: func(hostPort string) {
704 preDial <- struct{}{}
705 },
706 }
707 req = req.WithContext(httptrace.WithClientTrace(req.Context(), trace))
708 resp, err := tr.RoundTrip(req)
709 if err != nil {
710 t.Errorf("unexpected error for request %s: %v", reqId, err)
711 }
712 _, err = io.ReadAll(resp.Body)
713 if err != nil {
714 t.Errorf("unexpected error for request %s: %v", reqId, err)
715 }
716 reqComplete <- struct{}{}
717 }
718
719 go doReq("req1")
720 <-preDial
721 <-dialStarted
722
723
724 go doReq("req2")
725 <-preDial
726 select {
727 case <-dialStarted:
728 t.Error("req2 dial started while req1 dial in progress")
729 return
730 default:
731 }
732
733
734 stallDial <- struct{}{}
735 <-reqComplete
736
737
738 <-dialStarted
739 stallDial <- struct{}{}
740 <-reqComplete
741 }
742
743 func TestTransportMaxConnsPerHost(t *testing.T) {
744 run(t, testTransportMaxConnsPerHost, []testMode{http1Mode, https1Mode, http2Mode})
745 }
746 func testTransportMaxConnsPerHost(t *testing.T, mode testMode) {
747 CondSkipHTTP2(t)
748
749 h := HandlerFunc(func(w ResponseWriter, r *Request) {
750 _, err := w.Write([]byte("foo"))
751 if err != nil {
752 t.Fatalf("Write: %v", err)
753 }
754 })
755
756 ts := newClientServerTest(t, mode, h, optRealNet).ts
757 c := ts.Client()
758 tr := c.Transport.(*Transport)
759 tr.MaxConnsPerHost = 1
760
761 mu := sync.Mutex{}
762 var conns []net.Conn
763 var dialCnt, gotConnCnt, tlsHandshakeCnt int32
764 tr.Dial = func(network, addr string) (net.Conn, error) {
765 atomic.AddInt32(&dialCnt, 1)
766 c, err := net.Dial(network, addr)
767 mu.Lock()
768 defer mu.Unlock()
769 conns = append(conns, c)
770 return c, err
771 }
772
773 doReq := func() {
774 trace := &httptrace.ClientTrace{
775 GotConn: func(connInfo httptrace.GotConnInfo) {
776 if !connInfo.Reused {
777 atomic.AddInt32(&gotConnCnt, 1)
778 }
779 },
780 TLSHandshakeStart: func() {
781 atomic.AddInt32(&tlsHandshakeCnt, 1)
782 },
783 }
784 req, _ := NewRequest("GET", ts.URL, nil)
785 req = req.WithContext(httptrace.WithClientTrace(req.Context(), trace))
786
787 resp, err := c.Do(req)
788 if err != nil {
789 t.Fatalf("request failed: %v", err)
790 }
791 defer resp.Body.Close()
792 _, err = io.ReadAll(resp.Body)
793 if err != nil {
794 t.Fatalf("read body failed: %v", err)
795 }
796 }
797
798 wg := sync.WaitGroup{}
799 for i := 0; i < 10; i++ {
800 wg.Add(1)
801 go func() {
802 defer wg.Done()
803 doReq()
804 }()
805 }
806 wg.Wait()
807
808 expected := int32(tr.MaxConnsPerHost)
809 if dialCnt != expected {
810 t.Errorf("round 1: too many dials: %d != %d", dialCnt, expected)
811 }
812 if gotConnCnt != expected {
813 t.Errorf("round 1: too many get connections: %d != %d", gotConnCnt, expected)
814 }
815 if ts.TLS != nil && tlsHandshakeCnt != expected {
816 t.Errorf("round 1: too many tls handshakes: %d != %d", tlsHandshakeCnt, expected)
817 }
818
819 if t.Failed() {
820 t.FailNow()
821 }
822
823 mu.Lock()
824 for _, c := range conns {
825 c.Close()
826 }
827 conns = nil
828 mu.Unlock()
829 tr.CloseIdleConnections()
830
831 doReq()
832 expected++
833 if dialCnt != expected {
834 t.Errorf("round 2: too many dials: %d", dialCnt)
835 }
836 if gotConnCnt != expected {
837 t.Errorf("round 2: too many get connections: %d != %d", gotConnCnt, expected)
838 }
839 if ts.TLS != nil && tlsHandshakeCnt != expected {
840 t.Errorf("round 2: too many tls handshakes: %d != %d", tlsHandshakeCnt, expected)
841 }
842 }
843
844 func TestTransportMaxConnsPerHostDialCancellation(t *testing.T) {
845 run(t, testTransportMaxConnsPerHostDialCancellation,
846 testNotParallel,
847 []testMode{http1Mode, https1Mode, http2Mode},
848 )
849 }
850
851 func testTransportMaxConnsPerHostDialCancellation(t *testing.T, mode testMode) {
852 CondSkipHTTP2(t)
853
854 h := HandlerFunc(func(w ResponseWriter, r *Request) {
855 _, err := w.Write([]byte("foo"))
856 if err != nil {
857 t.Fatalf("Write: %v", err)
858 }
859 })
860
861 cst := newClientServerTest(t, mode, h)
862 defer cst.close()
863 ts := cst.ts
864 c := ts.Client()
865 tr := c.Transport.(*Transport)
866 tr.MaxConnsPerHost = 1
867
868
869 ctx, cancel := context.WithCancel(context.Background())
870 defer cancel()
871 SetPendingDialHooks(cancel, nil)
872 defer SetPendingDialHooks(nil, nil)
873
874 req, _ := NewRequestWithContext(ctx, "GET", ts.URL, nil)
875 _, err := c.Do(req)
876 if !errors.Is(err, context.Canceled) {
877 t.Errorf("expected error %v, got %v", context.Canceled, err)
878 }
879
880
881 SetPendingDialHooks(nil, nil)
882 req, _ = NewRequest("GET", ts.URL, nil)
883 resp, err := c.Do(req)
884 if err != nil {
885 t.Fatalf("request failed: %v", err)
886 }
887 defer resp.Body.Close()
888 _, err = io.ReadAll(resp.Body)
889 if err != nil {
890 t.Fatalf("read body failed: %v", err)
891 }
892 }
893
894 func TestTransportRemovesDeadIdleConnections(t *testing.T) {
895 run(t, testTransportRemovesDeadIdleConnections, []testMode{http1Mode})
896 }
897 func testTransportRemovesDeadIdleConnections(t *testing.T, mode testMode) {
898 ts := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
899 io.WriteString(w, r.RemoteAddr)
900 })).ts
901
902 c := ts.Client()
903 tr := c.Transport.(*Transport)
904
905 doReq := func(name string) {
906
907
908 res, err := c.Post(ts.URL, "", nil)
909 if err != nil {
910 t.Fatalf("%s: %v", name, err)
911 }
912 if res.StatusCode != 200 {
913 t.Fatalf("%s: %v", name, res.Status)
914 }
915 defer res.Body.Close()
916 slurp, err := io.ReadAll(res.Body)
917 if err != nil {
918 t.Fatalf("%s: %v", name, err)
919 }
920 t.Logf("%s: ok (%q)", name, slurp)
921 }
922
923 doReq("first")
924 keys1 := tr.IdleConnKeysForTesting()
925
926 ts.CloseClientConnections()
927
928 var keys2 []string
929 waitCondition(t, 10*time.Millisecond, func(d time.Duration) bool {
930 keys2 = tr.IdleConnKeysForTesting()
931 if len(keys2) != 0 {
932 if d > 0 {
933 t.Logf("Transport hasn't noticed idle connection's death in %v.\nbefore: %q\n after: %q\n", d, keys1, keys2)
934 }
935 return false
936 }
937 return true
938 })
939
940 doReq("second")
941 }
942
943
944
945 func TestTransportServerClosingUnexpectedly(t *testing.T) {
946 run(t, testTransportServerClosingUnexpectedly, []testMode{http1Mode})
947 }
948 func testTransportServerClosingUnexpectedly(t *testing.T, mode testMode) {
949 ts := newClientServerTest(t, mode, hostPortHandler).ts
950 c := ts.Client()
951
952 fetch := func(n, retries int) string {
953 condFatalf := func(format string, arg ...any) {
954 if retries <= 0 {
955 t.Fatalf(format, arg...)
956 }
957 t.Logf("retrying shortly after expected error: "+format, arg...)
958 time.Sleep(time.Second / time.Duration(retries))
959 }
960 for retries >= 0 {
961 retries--
962 res, err := c.Get(ts.URL)
963 if err != nil {
964 condFatalf("error in req #%d, GET: %v", n, err)
965 continue
966 }
967 body, err := io.ReadAll(res.Body)
968 if err != nil {
969 condFatalf("error in req #%d, ReadAll: %v", n, err)
970 continue
971 }
972 res.Body.Close()
973 return string(body)
974 }
975 panic("unreachable")
976 }
977
978 body1 := fetch(1, 0)
979 body2 := fetch(2, 0)
980
981
982
983
984
985
986
987
988 ExportCloseTransportConnsAbruptly(c.Transport.(*Transport))
989
990 body3 := fetch(3, 5)
991
992 if body1 != body2 {
993 t.Errorf("expected body1 and body2 to be equal")
994 }
995 if body2 == body3 {
996 t.Errorf("expected body2 and body3 to be different")
997 }
998 }
999
1000
1001
1002 func TestStressSurpriseServerCloses(t *testing.T) {
1003 run(t, testStressSurpriseServerCloses, []testMode{http1Mode})
1004 }
1005 func testStressSurpriseServerCloses(t *testing.T, mode testMode) {
1006 if testing.Short() {
1007 t.Skip("skipping test in short mode")
1008 }
1009 ts := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
1010 w.Header().Set("Content-Length", "5")
1011 w.Header().Set("Content-Type", "text/plain")
1012 w.Write([]byte("Hello"))
1013 w.(Flusher).Flush()
1014 conn, buf, _ := w.(Hijacker).Hijack()
1015 buf.Flush()
1016 conn.Close()
1017 })).ts
1018 c := ts.Client()
1019
1020
1021
1022
1023
1024
1025
1026 const (
1027 numClients = 20
1028 reqsPerClient = 25
1029 )
1030 var wg sync.WaitGroup
1031 wg.Add(numClients * reqsPerClient)
1032 for i := 0; i < numClients; i++ {
1033 go func() {
1034 for i := 0; i < reqsPerClient; i++ {
1035 res, err := c.Get(ts.URL)
1036 if err == nil {
1037
1038
1039
1040
1041
1042
1043 res.Body.Close()
1044 }
1045 wg.Done()
1046 }
1047 }()
1048 }
1049
1050
1051 wg.Wait()
1052 }
1053
1054
1055
1056 func TestTransportHeadResponses(t *testing.T) { run(t, testTransportHeadResponses) }
1057 func testTransportHeadResponses(t *testing.T, mode testMode) {
1058 ts := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
1059 if r.Method != "HEAD" {
1060 panic("expected HEAD; got " + r.Method)
1061 }
1062 w.Header().Set("Content-Length", "123")
1063 w.WriteHeader(200)
1064 })).ts
1065 c := ts.Client()
1066
1067 for i := 0; i < 2; i++ {
1068 res, err := c.Head(ts.URL)
1069 if err != nil {
1070 t.Errorf("error on loop %d: %v", i, err)
1071 continue
1072 }
1073 if e, g := "123", res.Header.Get("Content-Length"); e != g {
1074 t.Errorf("loop %d: expected Content-Length header of %q, got %q", i, e, g)
1075 }
1076 if e, g := int64(123), res.ContentLength; e != g {
1077 t.Errorf("loop %d: expected res.ContentLength of %v, got %v", i, e, g)
1078 }
1079 if all, err := io.ReadAll(res.Body); err != nil {
1080 t.Errorf("loop %d: Body ReadAll: %v", i, err)
1081 } else if len(all) != 0 {
1082 t.Errorf("Bogus body %q", all)
1083 }
1084 }
1085 }
1086
1087
1088
1089 func TestTransportHeadChunkedResponse(t *testing.T) {
1090 run(t, testTransportHeadChunkedResponse, []testMode{http1Mode}, testNotParallel)
1091 }
1092 func testTransportHeadChunkedResponse(t *testing.T, mode testMode) {
1093 ts := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
1094 if r.Method != "HEAD" {
1095 panic("expected HEAD; got " + r.Method)
1096 }
1097 w.Header().Set("Transfer-Encoding", "chunked")
1098 w.Header().Set("x-client-ipport", r.RemoteAddr)
1099 w.WriteHeader(200)
1100 })).ts
1101 c := ts.Client()
1102
1103
1104
1105 didRead := make(chan bool)
1106 SetReadLoopBeforeNextReadHook(func() { didRead <- true })
1107 defer SetReadLoopBeforeNextReadHook(nil)
1108
1109 res1, err := c.Head(ts.URL)
1110 <-didRead
1111
1112 if err != nil {
1113 t.Fatalf("request 1 error: %v", err)
1114 }
1115
1116 res2, err := c.Head(ts.URL)
1117 <-didRead
1118
1119 if err != nil {
1120 t.Fatalf("request 2 error: %v", err)
1121 }
1122 if v1, v2 := res1.Header.Get("x-client-ipport"), res2.Header.Get("x-client-ipport"); v1 != v2 {
1123 t.Errorf("ip/ports differed between head requests: %q vs %q", v1, v2)
1124 }
1125 }
1126
1127 var roundTripTests = []struct {
1128 accept string
1129 expectAccept string
1130 compressed bool
1131 }{
1132
1133 {"", "gzip", false},
1134
1135 {"foo", "foo", false},
1136
1137 {"gzip", "gzip", true},
1138 }
1139
1140
1141 func TestRoundTripGzip(t *testing.T) { run(t, testRoundTripGzip) }
1142 func testRoundTripGzip(t *testing.T, mode testMode) {
1143 const responseBody = "test response body"
1144 ts := newClientServerTest(t, mode, HandlerFunc(func(rw ResponseWriter, req *Request) {
1145 accept := req.Header.Get("Accept-Encoding")
1146 if expect := req.FormValue("expect_accept"); accept != expect {
1147 t.Errorf("in handler, test %v: Accept-Encoding = %q, want %q",
1148 req.FormValue("testnum"), accept, expect)
1149 }
1150 if accept == "gzip" {
1151 rw.Header().Set("Content-Encoding", "gzip")
1152 gz := gzip.NewWriter(rw)
1153 gz.Write([]byte(responseBody))
1154 gz.Close()
1155 } else {
1156 rw.Header().Set("Content-Encoding", accept)
1157 rw.Write([]byte(responseBody))
1158 }
1159 })).ts
1160 tr := ts.Client().Transport.(*Transport)
1161
1162 for i, test := range roundTripTests {
1163
1164 req, _ := NewRequest("GET", fmt.Sprintf("%s/?testnum=%d&expect_accept=%s", ts.URL, i, test.expectAccept), nil)
1165 if test.accept != "" {
1166 req.Header.Set("Accept-Encoding", test.accept)
1167 }
1168 res, err := tr.RoundTrip(req)
1169 if err != nil {
1170 t.Errorf("%d. RoundTrip: %v", i, err)
1171 continue
1172 }
1173 var body []byte
1174 if test.compressed {
1175 var r *gzip.Reader
1176 r, err = gzip.NewReader(res.Body)
1177 if err != nil {
1178 t.Errorf("%d. gzip NewReader: %v", i, err)
1179 continue
1180 }
1181 body, err = io.ReadAll(r)
1182 res.Body.Close()
1183 } else {
1184 body, err = io.ReadAll(res.Body)
1185 }
1186 if err != nil {
1187 t.Errorf("%d. Error: %q", i, err)
1188 continue
1189 }
1190 if g, e := string(body), responseBody; g != e {
1191 t.Errorf("%d. body = %q; want %q", i, g, e)
1192 }
1193 if g, e := req.Header.Get("Accept-Encoding"), test.accept; g != e {
1194 t.Errorf("%d. Accept-Encoding = %q; want %q (it was mutated, in violation of RoundTrip contract)", i, g, e)
1195 }
1196 if g, e := res.Header.Get("Content-Encoding"), test.accept; g != e {
1197 t.Errorf("%d. Content-Encoding = %q; want %q", i, g, e)
1198 }
1199 }
1200
1201 }
1202
1203 func TestTransportGzip(t *testing.T) { run(t, testTransportGzip) }
1204 func testTransportGzip(t *testing.T, mode testMode) {
1205 const testString = "The test string aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
1206 const nRandBytes = 1024 * 1024
1207 ts := newClientServerTest(t, mode, HandlerFunc(func(rw ResponseWriter, req *Request) {
1208 if req.Method == "HEAD" {
1209 if g := req.Header.Get("Accept-Encoding"); g != "" {
1210 t.Errorf("HEAD request sent with Accept-Encoding of %q; want none", g)
1211 }
1212 return
1213 }
1214 if g, e := req.Header.Get("Accept-Encoding"), "gzip"; g != e {
1215 t.Errorf("Accept-Encoding = %q, want %q", g, e)
1216 }
1217 rw.Header().Set("Content-Encoding", "gzip")
1218
1219 var w io.Writer = rw
1220 var buf bytes.Buffer
1221 if req.FormValue("chunked") == "0" {
1222 w = &buf
1223 defer io.Copy(rw, &buf)
1224 defer func() {
1225 rw.Header().Set("Content-Length", strconv.Itoa(buf.Len()))
1226 }()
1227 }
1228 gz := gzip.NewWriter(w)
1229 gz.Write([]byte(testString))
1230 if req.FormValue("body") == "large" {
1231 io.CopyN(gz, rand.Reader, nRandBytes)
1232 }
1233 gz.Close()
1234 })).ts
1235 c := ts.Client()
1236
1237 for _, chunked := range []string{"1", "0"} {
1238
1239 res, err := c.Get(ts.URL + "/?body=large&chunked=" + chunked)
1240 if err != nil {
1241 t.Fatalf("large get: %v", err)
1242 }
1243 buf := make([]byte, len(testString))
1244 n, err := io.ReadFull(res.Body, buf)
1245 if err != nil {
1246 t.Fatalf("partial read of large response: size=%d, %v", n, err)
1247 }
1248 if e, g := testString, string(buf); e != g {
1249 t.Errorf("partial read got %q, expected %q", g, e)
1250 }
1251 res.Body.Close()
1252
1253 n, err = res.Body.Read(buf)
1254 if n != 0 || err == nil {
1255 t.Errorf("expected error post-closed large Read; got = %d, %v", n, err)
1256 }
1257
1258
1259 res, err = c.Get(ts.URL + "/?chunked=" + chunked)
1260 if err != nil {
1261 t.Fatal(err)
1262 }
1263 body, err := io.ReadAll(res.Body)
1264 if err != nil {
1265 t.Fatal(err)
1266 }
1267 if g, e := string(body), testString; g != e {
1268 t.Fatalf("body = %q; want %q", g, e)
1269 }
1270 if g, e := res.Header.Get("Content-Encoding"), ""; g != e {
1271 t.Fatalf("Content-Encoding = %q; want %q", g, e)
1272 }
1273
1274
1275 n, err = res.Body.Read(buf)
1276 if n != 0 || err == nil {
1277 t.Errorf("expected Read error after exhausted reads; got %d, %v", n, err)
1278 }
1279 res.Body.Close()
1280 n, err = res.Body.Read(buf)
1281 if n != 0 || err == nil {
1282 t.Errorf("expected Read error after Close; got %d, %v", n, err)
1283 }
1284 }
1285
1286
1287 res, err := c.Head(ts.URL)
1288 if err != nil {
1289 t.Fatalf("Head: %v", err)
1290 }
1291 if res.StatusCode != 200 {
1292 t.Errorf("Head status=%d; want=200", res.StatusCode)
1293 }
1294 }
1295
1296
1297
1298 type transport100ContinueTest struct {
1299 t *testing.T
1300
1301 reqdone chan struct{}
1302 resp *Response
1303 respErr error
1304
1305 conn net.Conn
1306 reader *bufio.Reader
1307 }
1308
1309 const transport100ContinueTestBody = "request body"
1310
1311
1312
1313 func newTransport100ContinueTest(t *testing.T, timeout time.Duration) *transport100ContinueTest {
1314 ln := newLocalListener(t)
1315 defer ln.Close()
1316
1317 test := &transport100ContinueTest{
1318 t: t,
1319 reqdone: make(chan struct{}),
1320 }
1321
1322 tr := &Transport{
1323 ExpectContinueTimeout: timeout,
1324 }
1325 go func() {
1326 defer close(test.reqdone)
1327 body := strings.NewReader(transport100ContinueTestBody)
1328 req, _ := NewRequest("PUT", "http://"+ln.Addr().String(), body)
1329 req.Header.Set("Expect", "100-continue")
1330 req.ContentLength = int64(len(transport100ContinueTestBody))
1331 test.resp, test.respErr = tr.RoundTrip(req)
1332 test.resp.Body.Close()
1333 }()
1334
1335 c, err := ln.Accept()
1336 if err != nil {
1337 t.Fatalf("Accept: %v", err)
1338 }
1339 t.Cleanup(func() {
1340 c.Close()
1341 })
1342 br := bufio.NewReader(c)
1343 _, err = ReadRequest(br)
1344 if err != nil {
1345 t.Fatalf("ReadRequest: %v", err)
1346 }
1347 test.conn = c
1348 test.reader = br
1349 t.Cleanup(func() {
1350 <-test.reqdone
1351 tr.CloseIdleConnections()
1352 got, _ := io.ReadAll(test.reader)
1353 if len(got) > 0 {
1354 t.Fatalf("Transport sent unexpected bytes: %q", got)
1355 }
1356 })
1357
1358 return test
1359 }
1360
1361
1362 func (test *transport100ContinueTest) respond(lines ...string) {
1363 for _, line := range lines {
1364 if _, err := test.conn.Write([]byte(line + "\r\n")); err != nil {
1365 test.t.Fatalf("Write: %v", err)
1366 }
1367 }
1368 if _, err := test.conn.Write([]byte("\r\n")); err != nil {
1369 test.t.Fatalf("Write: %v", err)
1370 }
1371 }
1372
1373
1374 func (test *transport100ContinueTest) wantBodySent() {
1375 got, err := io.ReadAll(io.LimitReader(test.reader, int64(len(transport100ContinueTestBody))))
1376 if err != nil {
1377 test.t.Fatalf("unexpected error reading body: %v", err)
1378 }
1379 if got, want := string(got), transport100ContinueTestBody; got != want {
1380 test.t.Fatalf("unexpected body: got %q, want %q", got, want)
1381 }
1382 }
1383
1384
1385 func (test *transport100ContinueTest) wantRequestDone(want int) {
1386 <-test.reqdone
1387 if test.respErr != nil {
1388 test.t.Fatalf("unexpected RoundTrip error: %v", test.respErr)
1389 }
1390 if got := test.resp.StatusCode; got != want {
1391 test.t.Fatalf("unexpected response code: got %v, want %v", got, want)
1392 }
1393 }
1394
1395 func TestTransportExpect100ContinueSent(t *testing.T) {
1396 test := newTransport100ContinueTest(t, 1*time.Hour)
1397
1398 test.respond("HTTP/1.1 100 Continue")
1399 test.wantBodySent()
1400 test.respond("HTTP/1.1 200", "Content-Length: 0")
1401 test.wantRequestDone(200)
1402 }
1403
1404 func TestTransportExpect100Continue200ResponseNoConnClose(t *testing.T) {
1405 test := newTransport100ContinueTest(t, 1*time.Hour)
1406
1407 test.respond("HTTP/1.1 200", "Content-Length: 0")
1408 test.wantBodySent()
1409 test.wantRequestDone(200)
1410 }
1411
1412 func TestTransportExpect100Continue200ResponseWithConnClose(t *testing.T) {
1413 test := newTransport100ContinueTest(t, 1*time.Hour)
1414
1415 test.respond("HTTP/1.1 200", "Connection: close", "Content-Length: 0")
1416 test.wantRequestDone(200)
1417 }
1418
1419 func TestTransportExpect100Continue500ResponseNoConnClose(t *testing.T) {
1420 test := newTransport100ContinueTest(t, 1*time.Hour)
1421
1422 test.respond("HTTP/1.1 500", "Content-Length: 0")
1423 test.wantBodySent()
1424 test.wantRequestDone(500)
1425 }
1426
1427 func TestTransportExpect100Continue500ResponseTimeout(t *testing.T) {
1428 test := newTransport100ContinueTest(t, 5*time.Millisecond)
1429 test.wantBodySent()
1430 test.respond("HTTP/1.1 200", "Content-Length: 0")
1431 test.wantRequestDone(200)
1432 }
1433
1434 func TestSOCKS5Proxy(t *testing.T) {
1435 run(t, testSOCKS5Proxy, []testMode{http1Mode, https1Mode, http2Mode})
1436 }
1437 func testSOCKS5Proxy(t *testing.T, mode testMode) {
1438 ch := make(chan string, 1)
1439 l := newLocalListener(t)
1440 defer l.Close()
1441 defer close(ch)
1442 proxy := func(t *testing.T) {
1443 s, err := l.Accept()
1444 if err != nil {
1445 t.Errorf("socks5 proxy Accept(): %v", err)
1446 return
1447 }
1448 defer s.Close()
1449 var buf [22]byte
1450 if _, err := io.ReadFull(s, buf[:3]); err != nil {
1451 t.Errorf("socks5 proxy initial read: %v", err)
1452 return
1453 }
1454 if want := []byte{5, 1, 0}; !bytes.Equal(buf[:3], want) {
1455 t.Errorf("socks5 proxy initial read: got %v, want %v", buf[:3], want)
1456 return
1457 }
1458 if _, err := s.Write([]byte{5, 0}); err != nil {
1459 t.Errorf("socks5 proxy initial write: %v", err)
1460 return
1461 }
1462 if _, err := io.ReadFull(s, buf[:4]); err != nil {
1463 t.Errorf("socks5 proxy second read: %v", err)
1464 return
1465 }
1466 if want := []byte{5, 1, 0}; !bytes.Equal(buf[:3], want) {
1467 t.Errorf("socks5 proxy second read: got %v, want %v", buf[:3], want)
1468 return
1469 }
1470 var ipLen int
1471 switch buf[3] {
1472 case 1:
1473 ipLen = net.IPv4len
1474 case 4:
1475 ipLen = net.IPv6len
1476 default:
1477 t.Errorf("socks5 proxy second read: unexpected address type %v", buf[4])
1478 return
1479 }
1480 if _, err := io.ReadFull(s, buf[4:ipLen+6]); err != nil {
1481 t.Errorf("socks5 proxy address read: %v", err)
1482 return
1483 }
1484 ip := net.IP(buf[4 : ipLen+4])
1485 port := binary.BigEndian.Uint16(buf[ipLen+4 : ipLen+6])
1486 copy(buf[:3], []byte{5, 0, 0})
1487 if _, err := s.Write(buf[:ipLen+6]); err != nil {
1488 t.Errorf("socks5 proxy connect write: %v", err)
1489 return
1490 }
1491 ch <- fmt.Sprintf("proxy for %s:%d", ip, port)
1492
1493
1494 targetHost := net.JoinHostPort(ip.String(), strconv.Itoa(int(port)))
1495 targetConn, err := net.Dial("tcp", targetHost)
1496 if err != nil {
1497 t.Errorf("net.Dial failed")
1498 return
1499 }
1500 go io.Copy(targetConn, s)
1501 io.Copy(s, targetConn)
1502 targetConn.Close()
1503 }
1504
1505 pu, err := url.Parse("socks5://" + l.Addr().String())
1506 if err != nil {
1507 t.Fatal(err)
1508 }
1509
1510 sentinelHeader := "X-Sentinel"
1511 sentinelValue := "12345"
1512 h := HandlerFunc(func(w ResponseWriter, r *Request) {
1513 w.Header().Set(sentinelHeader, sentinelValue)
1514 })
1515 for _, useTLS := range []bool{false, true} {
1516 t.Run(fmt.Sprintf("useTLS=%v", useTLS), func(t *testing.T) {
1517 ts := newClientServerTest(t, mode, h, optRealNet).ts
1518 go proxy(t)
1519 c := ts.Client()
1520 c.Transport.(*Transport).Proxy = ProxyURL(pu)
1521 r, err := c.Head(ts.URL)
1522 if err != nil {
1523 t.Fatal(err)
1524 }
1525 if r.Header.Get(sentinelHeader) != sentinelValue {
1526 t.Errorf("Failed to retrieve sentinel value")
1527 }
1528 got := <-ch
1529 ts.Close()
1530 tsu, err := url.Parse(ts.URL)
1531 if err != nil {
1532 t.Fatal(err)
1533 }
1534 want := "proxy for " + tsu.Host
1535 if got != want {
1536 t.Errorf("got %q, want %q", got, want)
1537 }
1538 })
1539 }
1540 }
1541
1542 func TestTransportProxy(t *testing.T) {
1543 defer afterTest(t)
1544 testCases := []struct{ siteMode, proxyMode testMode }{
1545 {http1Mode, http1Mode},
1546 {http1Mode, https1Mode},
1547 {https1Mode, http1Mode},
1548 {https1Mode, https1Mode},
1549 }
1550 for _, testCase := range testCases {
1551 siteMode := testCase.siteMode
1552 proxyMode := testCase.proxyMode
1553 t.Run(fmt.Sprintf("site=%v/proxy=%v", siteMode, proxyMode), func(t *testing.T) {
1554 siteCh := make(chan *Request, 1)
1555 h1 := HandlerFunc(func(w ResponseWriter, r *Request) {
1556 siteCh <- r
1557 })
1558 proxyCh := make(chan *Request, 1)
1559 h2 := HandlerFunc(func(w ResponseWriter, r *Request) {
1560 proxyCh <- r
1561
1562 if r.Method == "CONNECT" {
1563 hijacker, ok := w.(Hijacker)
1564 if !ok {
1565 t.Errorf("hijack not allowed")
1566 return
1567 }
1568 clientConn, _, err := hijacker.Hijack()
1569 if err != nil {
1570 t.Errorf("hijacking failed")
1571 return
1572 }
1573 res := &Response{
1574 StatusCode: StatusOK,
1575 Proto: "HTTP/1.1",
1576 ProtoMajor: 1,
1577 ProtoMinor: 1,
1578 Header: make(Header),
1579 }
1580
1581 targetConn, err := net.Dial("tcp", r.URL.Host)
1582 if err != nil {
1583 t.Errorf("net.Dial(%q) failed: %v", r.URL.Host, err)
1584 return
1585 }
1586
1587 if err := res.Write(clientConn); err != nil {
1588 t.Errorf("Writing 200 OK failed: %v", err)
1589 return
1590 }
1591
1592 go io.Copy(targetConn, clientConn)
1593 go func() {
1594 io.Copy(clientConn, targetConn)
1595 targetConn.Close()
1596 }()
1597 }
1598 })
1599 ts := newClientServerTest(t, siteMode, h1, optRealNet).ts
1600 proxy := newClientServerTest(t, proxyMode, h2, optRealNet).ts
1601
1602 pu, err := url.Parse(proxy.URL)
1603 if err != nil {
1604 t.Fatal(err)
1605 }
1606
1607
1608
1609
1610 c := proxy.Client()
1611 if siteMode == https1Mode {
1612 c = ts.Client()
1613 }
1614
1615 c.Transport.(*Transport).Proxy = ProxyURL(pu)
1616 if _, err := c.Head(ts.URL); err != nil {
1617 t.Error(err)
1618 }
1619 got := <-proxyCh
1620 c.Transport.(*Transport).CloseIdleConnections()
1621 ts.Close()
1622 proxy.Close()
1623 if siteMode == https1Mode {
1624
1625 if got.Method != "CONNECT" {
1626 t.Errorf("Wrong method for secure proxying: %q", got.Method)
1627 }
1628 gotHost := got.URL.Host
1629 pu, err := url.Parse(ts.URL)
1630 if err != nil {
1631 t.Fatal("Invalid site URL")
1632 }
1633 if wantHost := pu.Host; gotHost != wantHost {
1634 t.Errorf("Got CONNECT host %q, want %q", gotHost, wantHost)
1635 }
1636
1637
1638 next := <-siteCh
1639 if next.Method != "HEAD" {
1640 t.Errorf("Wrong method at destination: %s", next.Method)
1641 }
1642 if nextURL := next.URL.String(); nextURL != "/" {
1643 t.Errorf("Wrong URL at destination: %s", nextURL)
1644 }
1645 } else {
1646 if got.Method != "HEAD" {
1647 t.Errorf("Wrong method for destination: %q", got.Method)
1648 }
1649 gotURL := got.URL.String()
1650 wantURL := ts.URL + "/"
1651 if gotURL != wantURL {
1652 t.Errorf("Got URL %q, want %q", gotURL, wantURL)
1653 }
1654 }
1655 })
1656 }
1657 }
1658
1659
1660
1661
1662 func TestProxyWithInfiniteHeader(t *testing.T) {
1663 defer afterTest(t)
1664
1665 ln := newLocalListener(t)
1666 defer ln.Close()
1667 cancelc := make(chan struct{})
1668 defer close(cancelc)
1669
1670
1671
1672 go func() {
1673 c, err := ln.Accept()
1674 if err != nil {
1675 t.Errorf("Accept: %v", err)
1676 return
1677 }
1678 defer c.Close()
1679
1680 br := bufio.NewReader(c)
1681 cr, err := ReadRequest(br)
1682 if err != nil {
1683 t.Errorf("proxy server failed to read CONNECT request")
1684 return
1685 }
1686 if cr.Method != "CONNECT" {
1687 t.Errorf("unexpected method %q", cr.Method)
1688 return
1689 }
1690
1691
1692 for {
1693
1694
1695
1696 runtime.Gosched()
1697 select {
1698 case <-cancelc:
1699 return
1700 default:
1701 c.Write([]byte("infinite stream of bytes"))
1702 }
1703 }
1704 }()
1705
1706 c := &Client{
1707 Transport: &Transport{
1708 Proxy: func(*Request) (*url.URL, error) {
1709 return url.Parse("http://" + ln.Addr().String())
1710 },
1711
1712 MaxResponseHeaderBytes: 1024,
1713 },
1714 }
1715 req, err := NewRequest("GET", "https://golang.fake.tld/", nil)
1716 if err != nil {
1717 t.Fatal(err)
1718 }
1719 _, err = c.Do(req)
1720 if err == nil {
1721 t.Errorf("unexpected Get success")
1722 }
1723 }
1724
1725 func TestOnProxyConnectResponse(t *testing.T) {
1726
1727 var tcases = []struct {
1728 proxyStatusCode int
1729 err error
1730 }{
1731 {
1732 StatusOK,
1733 nil,
1734 },
1735 {
1736 StatusForbidden,
1737 errors.New("403"),
1738 },
1739 }
1740 for _, tcase := range tcases {
1741 h1 := HandlerFunc(func(w ResponseWriter, r *Request) {
1742
1743 })
1744
1745 h2 := HandlerFunc(func(w ResponseWriter, r *Request) {
1746
1747 if r.Method == "CONNECT" {
1748 if tcase.proxyStatusCode != StatusOK {
1749 w.WriteHeader(tcase.proxyStatusCode)
1750 return
1751 }
1752 hijacker, ok := w.(Hijacker)
1753 if !ok {
1754 t.Errorf("hijack not allowed")
1755 return
1756 }
1757 clientConn, _, err := hijacker.Hijack()
1758 if err != nil {
1759 t.Errorf("hijacking failed")
1760 return
1761 }
1762 res := &Response{
1763 StatusCode: StatusOK,
1764 Proto: "HTTP/1.1",
1765 ProtoMajor: 1,
1766 ProtoMinor: 1,
1767 Header: make(Header),
1768 }
1769
1770 targetConn, err := net.Dial("tcp", r.URL.Host)
1771 if err != nil {
1772 t.Errorf("net.Dial(%q) failed: %v", r.URL.Host, err)
1773 return
1774 }
1775
1776 if err := res.Write(clientConn); err != nil {
1777 t.Errorf("Writing 200 OK failed: %v", err)
1778 return
1779 }
1780
1781 go io.Copy(targetConn, clientConn)
1782 go func() {
1783 io.Copy(clientConn, targetConn)
1784 targetConn.Close()
1785 }()
1786 }
1787 })
1788 ts := newClientServerTest(t, https1Mode, h1, optRealNet).ts
1789 proxy := newClientServerTest(t, https1Mode, h2, optRealNet).ts
1790
1791 pu, err := url.Parse(proxy.URL)
1792 if err != nil {
1793 t.Fatal(err)
1794 }
1795
1796 c := proxy.Client()
1797
1798 var (
1799 dials atomic.Int32
1800 closes atomic.Int32
1801 )
1802 c.Transport.(*Transport).DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
1803 conn, err := net.Dial(network, addr)
1804 if err != nil {
1805 return nil, err
1806 }
1807 dials.Add(1)
1808 return noteCloseConn{
1809 Conn: conn,
1810 closeFunc: func() {
1811 closes.Add(1)
1812 },
1813 }, nil
1814 }
1815
1816 c.Transport.(*Transport).Proxy = ProxyURL(pu)
1817 c.Transport.(*Transport).OnProxyConnectResponse = func(ctx context.Context, proxyURL *url.URL, connectReq *Request, connectRes *Response) error {
1818 if proxyURL.String() != pu.String() {
1819 t.Errorf("proxy url got %s, want %s", proxyURL, pu)
1820 }
1821
1822 if "https://"+connectReq.URL.String() != ts.URL {
1823 t.Errorf("connect url got %s, want %s", connectReq.URL, ts.URL)
1824 }
1825 return tcase.err
1826 }
1827 wantCloses := int32(0)
1828 if _, err := c.Head(ts.URL); err != nil {
1829 wantCloses = 1
1830 if tcase.err != nil && !strings.Contains(err.Error(), tcase.err.Error()) {
1831 t.Errorf("got %v, want %v", err, tcase.err)
1832 }
1833 } else {
1834 if tcase.err != nil {
1835 t.Errorf("got %v, want nil", err)
1836 }
1837 }
1838 if got, want := dials.Load(), int32(1); got != want {
1839 t.Errorf("got %v dials, want %v", got, want)
1840 }
1841
1842 if got, want := closes.Load(), wantCloses; got != want {
1843 t.Errorf("got %v closes, want %v", got, want)
1844 }
1845 }
1846 }
1847
1848
1849
1850 func TestTransportProxyHTTPSConnectLeak(t *testing.T) {
1851 cancelc := make(chan struct{})
1852 SetTestHookProxyConnectTimeout(t, func(ctx context.Context, timeout time.Duration) (context.Context, context.CancelFunc) {
1853 ctx, cancel := context.WithCancel(ctx)
1854 go func() {
1855 select {
1856 case <-cancelc:
1857 case <-ctx.Done():
1858 }
1859 cancel()
1860 }()
1861 return ctx, cancel
1862 })
1863
1864 defer afterTest(t)
1865
1866 ln := newLocalListener(t)
1867 defer ln.Close()
1868 listenerDone := make(chan struct{})
1869 go func() {
1870 defer close(listenerDone)
1871 c, err := ln.Accept()
1872 if err != nil {
1873 t.Errorf("Accept: %v", err)
1874 return
1875 }
1876 defer c.Close()
1877
1878 br := bufio.NewReader(c)
1879 cr, err := ReadRequest(br)
1880 if err != nil {
1881 t.Errorf("proxy server failed to read CONNECT request")
1882 return
1883 }
1884 if cr.Method != "CONNECT" {
1885 t.Errorf("unexpected method %q", cr.Method)
1886 return
1887 }
1888
1889
1890
1891
1892 close(cancelc)
1893 var buf [1]byte
1894 _, err = br.Read(buf[:])
1895 if err != io.EOF {
1896 t.Errorf("proxy server Read err = %v; want EOF", err)
1897 }
1898 return
1899 }()
1900
1901 c := &Client{
1902 Transport: &Transport{
1903 Proxy: func(*Request) (*url.URL, error) {
1904 return url.Parse("http://" + ln.Addr().String())
1905 },
1906 },
1907 }
1908 req, err := NewRequest("GET", "https://golang.fake.tld/", nil)
1909 if err != nil {
1910 t.Fatal(err)
1911 }
1912 _, err = c.Do(req)
1913 if err == nil {
1914 t.Errorf("unexpected Get success")
1915 }
1916
1917
1918
1919
1920 <-listenerDone
1921 }
1922
1923
1924 func TestTransportDialPreservesNetOpProxyError(t *testing.T) {
1925 defer afterTest(t)
1926
1927 var errDial = errors.New("some dial error")
1928
1929 tr := &Transport{
1930 Proxy: func(*Request) (*url.URL, error) {
1931 return url.Parse("http://proxy.fake.tld/")
1932 },
1933 Dial: func(string, string) (net.Conn, error) {
1934 return nil, errDial
1935 },
1936 }
1937 defer tr.CloseIdleConnections()
1938
1939 c := &Client{Transport: tr}
1940 req, _ := NewRequest("GET", "http://fake.tld", nil)
1941 res, err := c.Do(req)
1942 if err == nil {
1943 res.Body.Close()
1944 t.Fatal("wanted a non-nil error")
1945 }
1946
1947 uerr, ok := err.(*url.Error)
1948 if !ok {
1949 t.Fatalf("got %T, want *url.Error", err)
1950 }
1951 oe, ok := uerr.Err.(*net.OpError)
1952 if !ok {
1953 t.Fatalf("url.Error.Err = %T; want *net.OpError", uerr.Err)
1954 }
1955 want := &net.OpError{
1956 Op: "proxyconnect",
1957 Net: "tcp",
1958 Err: errDial,
1959 }
1960 if !reflect.DeepEqual(oe, want) {
1961 t.Errorf("Got error %#v; want %#v", oe, want)
1962 }
1963 }
1964
1965
1966
1967
1968
1969 func TestTransportProxyDialDoesNotMutateProxyConnectHeader(t *testing.T) {
1970 run(t, testTransportProxyDialDoesNotMutateProxyConnectHeader, http3SkippedMode)
1971 }
1972 func testTransportProxyDialDoesNotMutateProxyConnectHeader(t *testing.T, mode testMode) {
1973 proxy := newClientServerTest(t, mode, NotFoundHandler()).ts
1974 defer proxy.Close()
1975 c := proxy.Client()
1976
1977 tr := c.Transport.(*Transport)
1978 tr.Proxy = func(*Request) (*url.URL, error) {
1979 u, _ := url.Parse(proxy.URL)
1980 u.User = url.UserPassword("aladdin", "opensesame")
1981 return u, nil
1982 }
1983 h := tr.ProxyConnectHeader
1984 if h == nil {
1985 h = make(Header)
1986 }
1987 tr.ProxyConnectHeader = h.Clone()
1988
1989 req, err := NewRequest("GET", "https://golang.fake.tld/", nil)
1990 if err != nil {
1991 t.Fatal(err)
1992 }
1993 _, err = c.Do(req)
1994 if err == nil {
1995 t.Errorf("unexpected Get success")
1996 }
1997
1998 if !reflect.DeepEqual(tr.ProxyConnectHeader, h) {
1999 t.Errorf("tr.ProxyConnectHeader = %v; want %v", tr.ProxyConnectHeader, h)
2000 }
2001 }
2002
2003
2004
2005
2006
2007 func TestTransportGzipRecursive(t *testing.T) { run(t, testTransportGzipRecursive) }
2008 func testTransportGzipRecursive(t *testing.T, mode testMode) {
2009 ts := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
2010 w.Header().Set("Content-Encoding", "gzip")
2011 w.Write(rgz)
2012 })).ts
2013
2014 c := ts.Client()
2015 res, err := c.Get(ts.URL)
2016 if err != nil {
2017 t.Fatal(err)
2018 }
2019 body, err := io.ReadAll(res.Body)
2020 if err != nil {
2021 t.Fatal(err)
2022 }
2023 if !bytes.Equal(body, rgz) {
2024 t.Fatalf("Incorrect result from recursive gz:\nhave=%x\nwant=%x",
2025 body, rgz)
2026 }
2027 if g, e := res.Header.Get("Content-Encoding"), ""; g != e {
2028 t.Fatalf("Content-Encoding = %q; want %q", g, e)
2029 }
2030 }
2031
2032
2033
2034 func TestTransportGzipShort(t *testing.T) { run(t, testTransportGzipShort) }
2035 func testTransportGzipShort(t *testing.T, mode testMode) {
2036 ts := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
2037 w.Header().Set("Content-Encoding", "gzip")
2038 w.Write([]byte{0x1f, 0x8b})
2039 })).ts
2040
2041 c := ts.Client()
2042 res, err := c.Get(ts.URL)
2043 if err != nil {
2044 t.Fatal(err)
2045 }
2046 defer res.Body.Close()
2047 _, err = io.ReadAll(res.Body)
2048 if err == nil {
2049 t.Fatal("Expect an error from reading a body.")
2050 }
2051 if err != io.ErrUnexpectedEOF {
2052 t.Errorf("ReadAll error = %v; want io.ErrUnexpectedEOF", err)
2053 }
2054 }
2055
2056
2057 func waitNumGoroutine(nmax int) int {
2058 nfinal := runtime.NumGoroutine()
2059 for ntries := 10; ntries > 0 && nfinal > nmax; ntries-- {
2060 time.Sleep(50 * time.Millisecond)
2061 runtime.GC()
2062 nfinal = runtime.NumGoroutine()
2063 }
2064 return nfinal
2065 }
2066
2067
2068 func TestTransportPersistConnLeak(t *testing.T) {
2069 run(t, testTransportPersistConnLeak, testNotParallel)
2070 }
2071 func testTransportPersistConnLeak(t *testing.T, mode testMode) {
2072 if mode == http2Mode || mode == http3Mode {
2073 t.Skip("flaky in HTTP/2 and HTTP/3")
2074 }
2075
2076
2077 const numReq = 25
2078 gotReqCh := make(chan bool, numReq)
2079 unblockCh := make(chan bool, numReq)
2080 ts := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
2081 gotReqCh <- true
2082 <-unblockCh
2083 w.Header().Set("Content-Length", "0")
2084 w.WriteHeader(204)
2085 })).ts
2086 c := ts.Client()
2087 tr := c.Transport.(*Transport)
2088
2089 n0 := runtime.NumGoroutine()
2090
2091 didReqCh := make(chan bool, numReq)
2092 failed := make(chan bool, numReq)
2093 for i := 0; i < numReq; i++ {
2094 go func() {
2095 res, err := c.Get(ts.URL)
2096 didReqCh <- true
2097 if err != nil {
2098 t.Logf("client fetch error: %v", err)
2099 failed <- true
2100 return
2101 }
2102 res.Body.Close()
2103 }()
2104 }
2105
2106
2107 for i := 0; i < numReq; i++ {
2108 select {
2109 case <-gotReqCh:
2110
2111 case <-failed:
2112
2113
2114 }
2115 }
2116
2117 nhigh := runtime.NumGoroutine()
2118
2119
2120 close(unblockCh)
2121
2122
2123 for i := 0; i < numReq; i++ {
2124 <-didReqCh
2125 }
2126
2127 tr.CloseIdleConnections()
2128 nfinal := waitNumGoroutine(n0 + 5)
2129
2130 growth := nfinal - n0
2131
2132
2133
2134 if int(growth) > 5 {
2135 t.Logf("goroutine growth: %d -> %d -> %d (delta: %d)", n0, nhigh, nfinal, growth)
2136 t.Error("too many new goroutines")
2137 }
2138 }
2139
2140
2141
2142 func TestTransportPersistConnLeakShortBody(t *testing.T) {
2143 run(t, testTransportPersistConnLeakShortBody, testNotParallel)
2144 }
2145 func testTransportPersistConnLeakShortBody(t *testing.T, mode testMode) {
2146 if mode == http2Mode || mode == http3Mode {
2147 t.Skip("flaky in HTTP/2 and HTTP/3")
2148 }
2149
2150
2151 ts := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
2152 })).ts
2153 c := ts.Client()
2154 tr := c.Transport.(*Transport)
2155
2156 n0 := runtime.NumGoroutine()
2157 body := []byte("Hello")
2158 for i := 0; i < 20; i++ {
2159 req, err := NewRequest("POST", ts.URL, bytes.NewReader(body))
2160 if err != nil {
2161 t.Fatal(err)
2162 }
2163 req.ContentLength = int64(len(body) - 2)
2164 _, err = c.Do(req)
2165 if err == nil {
2166 t.Fatal("Expect an error from writing too long of a body.")
2167 }
2168 }
2169 nhigh := runtime.NumGoroutine()
2170 tr.CloseIdleConnections()
2171 nfinal := waitNumGoroutine(n0 + 5)
2172
2173 growth := nfinal - n0
2174
2175
2176
2177 t.Logf("goroutine growth: %d -> %d -> %d (delta: %d)", n0, nhigh, nfinal, growth)
2178 if int(growth) > 5 {
2179 t.Error("too many new goroutines")
2180 }
2181 }
2182
2183
2184 type countedConn struct {
2185 net.Conn
2186 }
2187
2188
2189 type countingDialer struct {
2190 dialer net.Dialer
2191 mu sync.Mutex
2192 total, live int64
2193 }
2194
2195 func (d *countingDialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
2196 conn, err := d.dialer.DialContext(ctx, network, address)
2197 if err != nil {
2198 return nil, err
2199 }
2200
2201 counted := new(countedConn)
2202 counted.Conn = conn
2203
2204 d.mu.Lock()
2205 defer d.mu.Unlock()
2206 d.total++
2207 d.live++
2208
2209 runtime.AddCleanup(counted, func(dd *countingDialer) { dd.decrement(nil) }, d)
2210 return counted, nil
2211 }
2212
2213 func (d *countingDialer) decrement(*countedConn) {
2214 d.mu.Lock()
2215 defer d.mu.Unlock()
2216 d.live--
2217 }
2218
2219 func (d *countingDialer) Read() (total, live int64) {
2220 d.mu.Lock()
2221 defer d.mu.Unlock()
2222 return d.total, d.live
2223 }
2224
2225 func TestTransportPersistConnLeakNeverIdle(t *testing.T) {
2226 run(t, testTransportPersistConnLeakNeverIdle, []testMode{http1Mode})
2227 }
2228 func testTransportPersistConnLeakNeverIdle(t *testing.T, mode testMode) {
2229 ts := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
2230
2231 conn, _, err := w.(Hijacker).Hijack()
2232 if err != nil {
2233 t.Errorf("Hijack failed unexpectedly: %v", err)
2234 return
2235 }
2236 conn.Close()
2237 }), optRealNet).ts
2238
2239 var d countingDialer
2240 c := ts.Client()
2241 c.Transport.(*Transport).DialContext = d.DialContext
2242
2243 body := []byte("Hello")
2244 for i := 0; ; i++ {
2245 total, live := d.Read()
2246 if live < total {
2247 break
2248 }
2249 if i >= 1<<12 {
2250 t.Fatalf("Count of live client net.Conns (%d) not lower than total (%d) after %d Do / GC iterations.", live, total, i)
2251 }
2252
2253 req, err := NewRequest("POST", ts.URL, bytes.NewReader(body))
2254 if err != nil {
2255 t.Fatal(err)
2256 }
2257 _, err = c.Do(req)
2258 if err == nil {
2259 t.Fatal("expected broken connection")
2260 }
2261
2262 runtime.GC()
2263 }
2264 }
2265
2266 type countedContext struct {
2267 context.Context
2268 }
2269
2270 type contextCounter struct {
2271 mu sync.Mutex
2272 live int64
2273 }
2274
2275 func (cc *contextCounter) Track(ctx context.Context) context.Context {
2276 counted := new(countedContext)
2277 counted.Context = ctx
2278 cc.mu.Lock()
2279 defer cc.mu.Unlock()
2280 cc.live++
2281 runtime.AddCleanup(counted, func(c *contextCounter) { cc.decrement(nil) }, cc)
2282 return counted
2283 }
2284
2285 func (cc *contextCounter) decrement(*countedContext) {
2286 cc.mu.Lock()
2287 defer cc.mu.Unlock()
2288 cc.live--
2289 }
2290
2291 func (cc *contextCounter) Read() (live int64) {
2292 cc.mu.Lock()
2293 defer cc.mu.Unlock()
2294 return cc.live
2295 }
2296
2297 func TestTransportPersistConnContextLeakMaxConnsPerHost(t *testing.T) {
2298 run(t, testTransportPersistConnContextLeakMaxConnsPerHost, http3SkippedMode)
2299 }
2300 func testTransportPersistConnContextLeakMaxConnsPerHost(t *testing.T, mode testMode) {
2301 if mode == http2Mode {
2302 t.Skip("https://go.dev/issue/56021")
2303 }
2304
2305 ts := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
2306 runtime.Gosched()
2307 w.WriteHeader(StatusOK)
2308 })).ts
2309
2310 c := ts.Client()
2311 c.Transport.(*Transport).MaxConnsPerHost = 1
2312
2313 ctx := context.Background()
2314 body := []byte("Hello")
2315 doPosts := func(cc *contextCounter) {
2316 var wg sync.WaitGroup
2317 for n := 64; n > 0; n-- {
2318 wg.Add(1)
2319 go func() {
2320 defer wg.Done()
2321
2322 ctx := cc.Track(ctx)
2323 req, err := NewRequest("POST", ts.URL, bytes.NewReader(body))
2324 if err != nil {
2325 t.Error(err)
2326 }
2327
2328 _, err = c.Do(req.WithContext(ctx))
2329 if err != nil {
2330 t.Errorf("Do failed with error: %v", err)
2331 }
2332 }()
2333 }
2334 wg.Wait()
2335 }
2336
2337 var initialCC contextCounter
2338 doPosts(&initialCC)
2339
2340
2341
2342
2343 var flushCC contextCounter
2344 for i := 0; ; i++ {
2345 live := initialCC.Read()
2346 if live == 0 {
2347 break
2348 }
2349 if i >= 100 {
2350 t.Fatalf("%d Contexts still not finalized after %d GC cycles.", live, i)
2351 }
2352 doPosts(&flushCC)
2353 runtime.GC()
2354 }
2355 }
2356
2357
2358 func TestTransportIdleConnCrash(t *testing.T) { run(t, testTransportIdleConnCrash, http3SkippedMode) }
2359 func testTransportIdleConnCrash(t *testing.T, mode testMode) {
2360 var tr *Transport
2361
2362 unblockCh := make(chan bool, 1)
2363 ts := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
2364 <-unblockCh
2365 tr.CloseIdleConnections()
2366 })).ts
2367 c := ts.Client()
2368 tr = c.Transport.(*Transport)
2369
2370 didreq := make(chan bool)
2371 go func() {
2372 res, err := c.Get(ts.URL)
2373 if err != nil {
2374 t.Error(err)
2375 } else {
2376 res.Body.Close()
2377 }
2378 didreq <- true
2379 }()
2380 unblockCh <- true
2381 <-didreq
2382 }
2383
2384
2385
2386
2387
2388 func TestIssue3644(t *testing.T) { run(t, testIssue3644) }
2389 func testIssue3644(t *testing.T, mode testMode) {
2390 const numFoos = 5000
2391 ts := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
2392 w.Header().Set("Connection", "close")
2393 for i := 0; i < numFoos; i++ {
2394 w.Write([]byte("foo "))
2395 }
2396 })).ts
2397 c := ts.Client()
2398 res, err := c.Get(ts.URL)
2399 if err != nil {
2400 t.Fatal(err)
2401 }
2402 defer res.Body.Close()
2403 bs, err := io.ReadAll(res.Body)
2404 if err != nil {
2405 t.Fatal(err)
2406 }
2407 if len(bs) != numFoos*len("foo ") {
2408 t.Errorf("unexpected response length")
2409 }
2410 }
2411
2412
2413
2414 func TestIssue3595(t *testing.T) { run(t, testIssue3595, testNotParallel) }
2415 func testIssue3595(t *testing.T, mode testMode) {
2416 runTimeSensitiveTest(t, []time.Duration{
2417 1 * time.Millisecond,
2418 5 * time.Millisecond,
2419 10 * time.Millisecond,
2420 50 * time.Millisecond,
2421 100 * time.Millisecond,
2422 500 * time.Millisecond,
2423 time.Second,
2424 5 * time.Second,
2425 }, func(t *testing.T, timeout time.Duration) error {
2426 SetRSTAvoidanceDelay(t, timeout)
2427 t.Logf("set RST avoidance delay to %v", timeout)
2428
2429 const deniedMsg = "sorry, denied."
2430 cst := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
2431 Error(w, deniedMsg, StatusUnauthorized)
2432 }), optRealNet)
2433
2434
2435 defer cst.close()
2436 ts := cst.ts
2437 c := ts.Client()
2438
2439 res, err := c.Post(ts.URL, "application/octet-stream", neverEnding('a'))
2440 if err != nil {
2441 return fmt.Errorf("Post: %v", err)
2442 }
2443 got, err := io.ReadAll(res.Body)
2444 if err != nil {
2445 return fmt.Errorf("Body ReadAll: %v", err)
2446 }
2447 t.Logf("server response:\n%s", got)
2448 if !strings.Contains(string(got), deniedMsg) {
2449
2450
2451 t.Errorf("Known bug: response %q does not contain %q", got, deniedMsg)
2452 }
2453 return nil
2454 })
2455 }
2456
2457
2458
2459 func TestChunkedNoContent(t *testing.T) { run(t, testChunkedNoContent) }
2460 func testChunkedNoContent(t *testing.T, mode testMode) {
2461 ts := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
2462 w.WriteHeader(StatusNoContent)
2463 })).ts
2464
2465 c := ts.Client()
2466 for _, closeBody := range []bool{true, false} {
2467 const n = 4
2468 for i := 1; i <= n; i++ {
2469 res, err := c.Get(ts.URL)
2470 if err != nil {
2471 t.Errorf("closingBody=%v, req %d/%d: %v", closeBody, i, n, err)
2472 } else {
2473 if closeBody {
2474 res.Body.Close()
2475 }
2476 }
2477 }
2478 }
2479 }
2480
2481 func TestTransportConcurrency(t *testing.T) {
2482 run(t, testTransportConcurrency, testNotParallel, []testMode{http1Mode})
2483 }
2484 func testTransportConcurrency(t *testing.T, mode testMode) {
2485
2486 maxProcs, numReqs := 16, 500
2487 if testing.Short() {
2488 maxProcs, numReqs = 4, 50
2489 }
2490 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(maxProcs))
2491 ts := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
2492 fmt.Fprintf(w, "%v", r.FormValue("echo"))
2493 })).ts
2494
2495 var wg sync.WaitGroup
2496 wg.Add(numReqs)
2497
2498
2499
2500
2501
2502
2503
2504 SetPendingDialHooks(func() { wg.Add(1) }, wg.Done)
2505 defer SetPendingDialHooks(nil, nil)
2506
2507 c := ts.Client()
2508 reqs := make(chan string)
2509 defer close(reqs)
2510
2511 for i := 0; i < maxProcs*2; i++ {
2512 go func() {
2513 for req := range reqs {
2514 res, err := c.Get(ts.URL + "/?echo=" + req)
2515 if err != nil {
2516 if runtime.GOOS == "netbsd" && strings.HasSuffix(err.Error(), ": connection reset by peer") {
2517
2518
2519 t.Logf("error on req %s: %v", req, err)
2520 t.Logf("(see https://go.dev/issue/52168)")
2521 } else {
2522 t.Errorf("error on req %s: %v", req, err)
2523 }
2524 wg.Done()
2525 continue
2526 }
2527 all, err := io.ReadAll(res.Body)
2528 if err != nil {
2529 t.Errorf("read error on req %s: %v", req, err)
2530 } else if string(all) != req {
2531 t.Errorf("body of req %s = %q; want %q", req, all, req)
2532 }
2533 res.Body.Close()
2534 wg.Done()
2535 }
2536 }()
2537 }
2538 for i := 0; i < numReqs; i++ {
2539 reqs <- fmt.Sprintf("request-%d", i)
2540 }
2541 wg.Wait()
2542 }
2543
2544 func TestIssue4191_InfiniteGetTimeout(t *testing.T) {
2545 run(t, testIssue4191_InfiniteGetTimeout, http3SkippedMode)
2546 }
2547 func testIssue4191_InfiniteGetTimeout(t *testing.T, mode testMode) {
2548 mux := NewServeMux()
2549 mux.HandleFunc("/get", func(w ResponseWriter, r *Request) {
2550 io.Copy(w, neverEnding('a'))
2551 })
2552 ts := newClientServerTest(t, mode, mux, optRealNet).ts
2553
2554 connc := make(chan net.Conn, 1)
2555 c := ts.Client()
2556 c.Transport.(*Transport).Dial = func(n, addr string) (net.Conn, error) {
2557 conn, err := net.Dial(n, addr)
2558 if err != nil {
2559 return nil, err
2560 }
2561 select {
2562 case connc <- conn:
2563 default:
2564 }
2565 return conn, nil
2566 }
2567
2568 res, err := c.Get(ts.URL + "/get")
2569 if err != nil {
2570 t.Fatalf("Error issuing GET: %v", err)
2571 }
2572 defer res.Body.Close()
2573
2574 conn := <-connc
2575 conn.SetDeadline(time.Now().Add(1 * time.Millisecond))
2576 _, err = io.Copy(io.Discard, res.Body)
2577 if err == nil {
2578 t.Errorf("Unexpected successful copy")
2579 }
2580 }
2581
2582 func TestIssue4191_InfiniteGetToPutTimeout(t *testing.T) {
2583 run(t, testIssue4191_InfiniteGetToPutTimeout, []testMode{http1Mode})
2584 }
2585 func testIssue4191_InfiniteGetToPutTimeout(t *testing.T, mode testMode) {
2586 const debug = false
2587 mux := NewServeMux()
2588 mux.HandleFunc("/get", func(w ResponseWriter, r *Request) {
2589 io.Copy(w, neverEnding('a'))
2590 })
2591 mux.HandleFunc("/put", func(w ResponseWriter, r *Request) {
2592 defer r.Body.Close()
2593 io.Copy(io.Discard, r.Body)
2594 })
2595 ts := newClientServerTest(t, mode, mux, optRealNet).ts
2596 timeout := 100 * time.Millisecond
2597
2598 c := ts.Client()
2599 c.Transport.(*Transport).Dial = func(n, addr string) (net.Conn, error) {
2600 conn, err := net.Dial(n, addr)
2601 if err != nil {
2602 return nil, err
2603 }
2604 conn.SetDeadline(time.Now().Add(timeout))
2605 if debug {
2606 conn = NewLoggingConn("client", conn)
2607 }
2608 return conn, nil
2609 }
2610
2611 getFailed := false
2612 nRuns := 5
2613 if testing.Short() {
2614 nRuns = 1
2615 }
2616 for i := 0; i < nRuns; i++ {
2617 if debug {
2618 println("run", i+1, "of", nRuns)
2619 }
2620 sres, err := c.Get(ts.URL + "/get")
2621 if err != nil {
2622 if !getFailed {
2623
2624 getFailed = true
2625 t.Logf("increasing timeout")
2626 i--
2627 timeout *= 10
2628 continue
2629 }
2630 t.Errorf("Error issuing GET: %v", err)
2631 break
2632 }
2633 req, _ := NewRequest("PUT", ts.URL+"/put", sres.Body)
2634 _, err = c.Do(req)
2635 if err == nil {
2636 sres.Body.Close()
2637 t.Errorf("Unexpected successful PUT")
2638 break
2639 }
2640 sres.Body.Close()
2641 }
2642 if debug {
2643 println("tests complete; waiting for handlers to finish")
2644 }
2645 ts.Close()
2646 }
2647
2648 func TestTransportResponseHeaderTimeout(t *testing.T) {
2649 run(t, testTransportResponseHeaderTimeout, http3SkippedMode)
2650 }
2651 func testTransportResponseHeaderTimeout(t *testing.T, mode testMode) {
2652 if testing.Short() {
2653 t.Skip("skipping timeout test in -short mode")
2654 }
2655
2656 timeout := 2 * time.Millisecond
2657 retry := true
2658 for retry && !t.Failed() {
2659 var srvWG sync.WaitGroup
2660 inHandler := make(chan bool, 1)
2661 mux := NewServeMux()
2662 mux.HandleFunc("/fast", func(w ResponseWriter, r *Request) {
2663 inHandler <- true
2664 srvWG.Done()
2665 })
2666 mux.HandleFunc("/slow", func(w ResponseWriter, r *Request) {
2667 inHandler <- true
2668 <-r.Context().Done()
2669 srvWG.Done()
2670 })
2671 ts := newClientServerTest(t, mode, mux).ts
2672
2673 c := ts.Client()
2674 c.Transport.(*Transport).ResponseHeaderTimeout = timeout
2675
2676 retry = false
2677 srvWG.Add(3)
2678 tests := []struct {
2679 path string
2680 wantTimeout bool
2681 }{
2682 {path: "/fast"},
2683 {path: "/slow", wantTimeout: true},
2684 {path: "/fast"},
2685 }
2686 for i, tt := range tests {
2687 req, _ := NewRequest("GET", ts.URL+tt.path, nil)
2688 req = req.WithT(t)
2689 res, err := c.Do(req)
2690 <-inHandler
2691 if err != nil {
2692 uerr, ok := err.(*url.Error)
2693 if !ok {
2694 t.Errorf("error is not a url.Error; got: %#v", err)
2695 continue
2696 }
2697 nerr, ok := uerr.Err.(net.Error)
2698 if !ok {
2699 t.Errorf("error does not satisfy net.Error interface; got: %#v", err)
2700 continue
2701 }
2702 if !nerr.Timeout() {
2703 t.Errorf("want timeout error; got: %q", nerr)
2704 continue
2705 }
2706 if !tt.wantTimeout {
2707 if !retry {
2708
2709 t.Logf("unexpected timeout for path %q after %v; retrying with longer timeout", tt.path, timeout)
2710 timeout *= 2
2711 retry = true
2712 }
2713 }
2714 if !strings.Contains(err.Error(), "timeout awaiting response headers") {
2715 t.Errorf("%d. unexpected error: %v", i, err)
2716 }
2717 continue
2718 }
2719 if tt.wantTimeout {
2720 t.Errorf(`no error for path %q; expected "timeout awaiting response headers"`, tt.path)
2721 continue
2722 }
2723 if res.StatusCode != 200 {
2724 t.Errorf("%d for path %q status = %d; want 200", i, tt.path, res.StatusCode)
2725 }
2726 }
2727
2728 srvWG.Wait()
2729 ts.Close()
2730 }
2731 }
2732
2733
2734 type cancelTest struct {
2735 mode testMode
2736 newReq func(req *Request) *Request
2737 cancel func(tr *Transport, req *Request)
2738 checkErr func(when string, err error)
2739 }
2740
2741
2742 func runCancelTestChannel(t *testing.T, mode testMode, f func(t *testing.T, test cancelTest)) {
2743 cancelc := make(chan struct{})
2744 cancelOnce := sync.OnceFunc(func() { close(cancelc) })
2745 f(t, cancelTest{
2746 mode: mode,
2747 newReq: func(req *Request) *Request {
2748 req.Cancel = cancelc
2749 return req
2750 },
2751 cancel: func(tr *Transport, req *Request) {
2752 cancelOnce()
2753 },
2754 checkErr: func(when string, err error) {
2755 if !errors.Is(err, ExportErrRequestCanceled) && !errors.Is(err, ExportErrRequestCanceledConn) {
2756 t.Errorf("%v error = %v, want errRequestCanceled or errRequestCanceledConn", when, err)
2757 }
2758 },
2759 })
2760 }
2761
2762
2763 func runCancelTestContext(t *testing.T, mode testMode, f func(t *testing.T, test cancelTest)) {
2764 ctx, cancel := context.WithCancel(context.Background())
2765 f(t, cancelTest{
2766 mode: mode,
2767 newReq: func(req *Request) *Request {
2768 return req.WithContext(ctx)
2769 },
2770 cancel: func(tr *Transport, req *Request) {
2771 cancel()
2772 },
2773 checkErr: func(when string, err error) {
2774 if !errors.Is(err, context.Canceled) {
2775 t.Errorf("%v error = %v, want context.Canceled", when, err)
2776 }
2777 },
2778 })
2779 }
2780
2781 func runCancelTest(t *testing.T, f func(t *testing.T, test cancelTest), opts ...any) {
2782 run(t, func(t *testing.T, mode testMode) {
2783 t.Run("RequestCancel", func(t *testing.T) {
2784 if mode == http3Mode {
2785 t.Skip("Request.Cancel not supported for HTTP/3")
2786 }
2787 synctest.Test(t, func(t *testing.T) {
2788 runCancelTestChannel(t, mode, f)
2789 })
2790 })
2791 t.Run("ContextCancel", func(t *testing.T) {
2792 synctest.Test(t, func(t *testing.T) {
2793 runCancelTestContext(t, mode, f)
2794 })
2795 })
2796 }, opts...)
2797 }
2798
2799 func TestTransportCancelRequest(t *testing.T) {
2800 runCancelTest(t, testTransportCancelRequest, http3SkippedMode)
2801 }
2802 func testTransportCancelRequest(t *testing.T, test cancelTest) {
2803 if testing.Short() {
2804 t.Skip("skipping test in -short mode")
2805 }
2806
2807 const msg = "Hello"
2808 unblockc := make(chan bool)
2809 cst := newClientServerTest(t, test.mode, HandlerFunc(func(w ResponseWriter, r *Request) {
2810 io.WriteString(w, msg)
2811 w.(Flusher).Flush()
2812 <-unblockc
2813 }))
2814 defer close(unblockc)
2815
2816 conn, err := cst.tr.NewClientConn(t.Context(), test.mode.Scheme(), "example.tld:80")
2817 if err != nil {
2818 t.Fatal(err)
2819 }
2820
2821 req, _ := NewRequest("GET", cst.ts.URL, nil)
2822 req = test.newReq(req)
2823 res, err := conn.RoundTrip(req)
2824 if err != nil {
2825 t.Fatal(err)
2826 }
2827 body := make([]byte, len(msg))
2828 n, _ := io.ReadFull(res.Body, body)
2829 if n != len(body) || !bytes.Equal(body, []byte(msg)) {
2830 t.Errorf("Body = %q; want %q", body[:n], msg)
2831 }
2832 synctest.Wait()
2833 if got, want := conn.InFlight(), 1; got != want {
2834 t.Fatalf("Before cancel: InFlight = %v, want %v", got, want)
2835 }
2836 test.cancel(cst.tr, req)
2837
2838 tail, err := io.ReadAll(res.Body)
2839 res.Body.Close()
2840 test.checkErr("Body.Read", err)
2841 if len(tail) > 0 {
2842 t.Errorf("Spurious bytes from Body.Read: %q", tail)
2843 }
2844
2845 synctest.Wait()
2846 if got, want := conn.InFlight(), 0; got != want {
2847 t.Fatalf("After cancel: InFlight = %v, want %v", got, want)
2848 }
2849 }
2850
2851 func testTransportCancelRequestInDo(t *testing.T, test cancelTest, body io.Reader) {
2852 if testing.Short() {
2853 t.Skip("skipping test in -short mode")
2854 }
2855 unblockc := make(chan bool)
2856 ts := newClientServerTest(t, test.mode, HandlerFunc(func(w ResponseWriter, r *Request) {
2857 <-unblockc
2858 })).ts
2859 defer close(unblockc)
2860
2861 c := ts.Client()
2862 tr := c.Transport.(*Transport)
2863
2864 req, _ := NewRequest("GET", ts.URL, body)
2865 req = test.newReq(req)
2866 done := false
2867 go func() {
2868 c.Do(req)
2869 done = true
2870 }()
2871 synctest.Wait()
2872 test.cancel(tr, req)
2873 synctest.Wait()
2874 if !done {
2875 t.Errorf("Do of canceled request has not returned")
2876 }
2877 }
2878
2879 func TestTransportCancelRequestInDo(t *testing.T) {
2880 runCancelTest(t, func(t *testing.T, test cancelTest) {
2881 testTransportCancelRequestInDo(t, test, nil)
2882 })
2883 }
2884
2885 func TestTransportCancelRequestWithBodyInDo(t *testing.T) {
2886 runCancelTest(t, func(t *testing.T, test cancelTest) {
2887 testTransportCancelRequestInDo(t, test, bytes.NewBuffer([]byte{0}))
2888 })
2889 }
2890
2891 func TestTransportCancelRequestInDial(t *testing.T) {
2892 runCancelTest(t, testTransportCancelRequestInDial)
2893 }
2894 func testTransportCancelRequestInDial(t *testing.T, test cancelTest) {
2895 defer afterTest(t)
2896 if testing.Short() {
2897 t.Skip("skipping test in -short mode")
2898 }
2899 var logbuf strings.Builder
2900 eventLog := log.New(&logbuf, "", 0)
2901
2902 unblockDial := make(chan bool)
2903 defer close(unblockDial)
2904
2905 inDial := make(chan bool)
2906 tr := &Transport{
2907 Dial: func(network, addr string) (net.Conn, error) {
2908 eventLog.Println("dial: blocking")
2909 if !<-inDial {
2910 return nil, errors.New("main Test goroutine exited")
2911 }
2912 <-unblockDial
2913 return nil, errors.New("nope")
2914 },
2915 }
2916 cl := &Client{Transport: tr}
2917 gotres := false
2918 req, _ := NewRequest("GET", "http://something.no-network.tld/", nil)
2919 req = test.newReq(req)
2920 go func() {
2921 _, err := cl.Do(req)
2922 eventLog.Printf("Get error = %v", err != nil)
2923 test.checkErr("Get", err)
2924 gotres = true
2925 }()
2926
2927 inDial <- true
2928
2929 eventLog.Printf("canceling")
2930 test.cancel(tr, req)
2931 test.cancel(tr, req)
2932
2933 synctest.Wait()
2934 if !gotres {
2935 t.Errorf("after cancel, Do has not returned")
2936 }
2937 got := logbuf.String()
2938 want := `dial: blocking
2939 canceling
2940 Get error = true
2941 `
2942 if got != want {
2943 t.Errorf("Got events:\n%s\nWant:\n%s", got, want)
2944 }
2945 }
2946
2947
2948 func TestTransportCancelRequestWithBody(t *testing.T) {
2949 runCancelTest(t, testTransportCancelRequestWithBody, http3SkippedMode)
2950 }
2951 func testTransportCancelRequestWithBody(t *testing.T, test cancelTest) {
2952 if testing.Short() {
2953 t.Skip("skipping test in -short mode")
2954 }
2955
2956 const msg = "Hello"
2957 unblockc := make(chan struct{})
2958 cst := newClientServerTest(t, test.mode, HandlerFunc(func(w ResponseWriter, r *Request) {
2959 io.WriteString(w, msg)
2960 w.(Flusher).Flush()
2961 <-unblockc
2962 }))
2963 defer close(unblockc)
2964
2965 conn, err := cst.tr.NewClientConn(t.Context(), test.mode.Scheme(), "example.tld:80")
2966 if err != nil {
2967 t.Fatal(err)
2968 }
2969
2970 req, _ := NewRequest("POST", cst.ts.URL, strings.NewReader("withbody"))
2971 req = test.newReq(req)
2972
2973 res, err := conn.RoundTrip(req)
2974 if err != nil {
2975 t.Fatal(err)
2976 }
2977 body := make([]byte, len(msg))
2978 n, _ := io.ReadFull(res.Body, body)
2979 if n != len(body) || !bytes.Equal(body, []byte(msg)) {
2980 t.Errorf("Body = %q; want %q", body[:n], msg)
2981 }
2982 synctest.Wait()
2983 if got, want := conn.InFlight(), 1; got != want {
2984 t.Fatalf("Before cancel: InFlight = %v, want %v", got, want)
2985 }
2986 test.cancel(cst.tr, req)
2987
2988 tail, err := io.ReadAll(res.Body)
2989 res.Body.Close()
2990 test.checkErr("Body.Read", err)
2991 if len(tail) > 0 {
2992 t.Errorf("Spurious bytes from Body.Read: %q", tail)
2993 }
2994
2995 synctest.Wait()
2996 if got, want := conn.InFlight(), 0; got != want {
2997 t.Fatalf("After cancel: InFlight = %v, want %v", got, want)
2998 }
2999 }
3000
3001 func TestTransportCancelRequestBeforeDo(t *testing.T) {
3002 runCancelTest(t, testTransportCancelRequestBeforeDo, http3SkippedMode)
3003 }
3004 func testTransportCancelRequestBeforeDo(t *testing.T, test cancelTest) {
3005 unblockc := make(chan bool)
3006 cst := newClientServerTest(t, test.mode, HandlerFunc(func(w ResponseWriter, r *Request) {
3007 <-unblockc
3008 }))
3009 defer close(unblockc)
3010
3011 c := cst.ts.Client()
3012
3013 req, _ := NewRequest("GET", cst.ts.URL, nil)
3014 req = test.newReq(req)
3015 test.cancel(cst.tr, req)
3016
3017 _, err := c.Do(req)
3018 test.checkErr("Do", err)
3019 }
3020
3021
3022 func TestTransportCancelRequestBeforeResponseHeaders(t *testing.T) {
3023 runCancelTest(t, testTransportCancelRequestBeforeResponseHeaders, []testMode{http1Mode})
3024 }
3025 func testTransportCancelRequestBeforeResponseHeaders(t *testing.T, test cancelTest) {
3026 defer afterTest(t)
3027
3028 serverConnCh := make(chan net.Conn, 1)
3029 tr := &Transport{
3030 Dial: func(network, addr string) (net.Conn, error) {
3031 cc, sc := net.Pipe()
3032 serverConnCh <- sc
3033 return cc, nil
3034 },
3035 }
3036 defer tr.CloseIdleConnections()
3037 errc := make(chan error, 1)
3038 req, _ := NewRequest("GET", "http://example.com/", nil)
3039 req = test.newReq(req)
3040 go func() {
3041 _, err := tr.RoundTrip(req)
3042 errc <- err
3043 }()
3044
3045 sc := <-serverConnCh
3046 verb := make([]byte, 3)
3047 if _, err := io.ReadFull(sc, verb); err != nil {
3048 t.Errorf("Error reading HTTP verb from server: %v", err)
3049 }
3050 if string(verb) != "GET" {
3051 t.Errorf("server received %q; want GET", verb)
3052 }
3053 defer sc.Close()
3054
3055 test.cancel(tr, req)
3056
3057 err := <-errc
3058 if err == nil {
3059 t.Fatalf("unexpected success from RoundTrip")
3060 }
3061 test.checkErr("RoundTrip", err)
3062 }
3063
3064
3065
3066
3067 func TestTransportCloseResponseBody(t *testing.T) {
3068 run(t, testTransportCloseResponseBody, http3SkippedMode)
3069 }
3070 func testTransportCloseResponseBody(t *testing.T, mode testMode) {
3071 writeErr := make(chan error, 1)
3072 msg := []byte("young\n")
3073 cst := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
3074 for {
3075 _, err := w.Write(msg)
3076 if err != nil {
3077 writeErr <- err
3078 return
3079 }
3080 w.(Flusher).Flush()
3081 }
3082 }))
3083 ts := cst.ts
3084 cst.setDialNettestHook(func(nc *nettest.Conn) {
3085 nc.SetReadBufferSize(1024)
3086 })
3087
3088 c := ts.Client()
3089 req, _ := NewRequestWithContext(t.Context(), "GET", ts.URL, nil)
3090 res, err := c.Do(req)
3091 if err != nil {
3092 t.Fatal(err)
3093 }
3094
3095 const repeats = 3
3096 buf := make([]byte, len(msg)*repeats)
3097 want := bytes.Repeat(msg, repeats)
3098
3099 _, err = io.ReadFull(res.Body, buf)
3100 if err != nil {
3101 t.Fatal(err)
3102 }
3103 if !bytes.Equal(buf, want) {
3104 t.Fatalf("read %q; want %q", buf, want)
3105 }
3106
3107 if err := res.Body.Close(); err != nil {
3108 t.Errorf("Close = %v", err)
3109 }
3110
3111 if err := <-writeErr; err == nil {
3112 t.Errorf("expected non-nil write error")
3113 }
3114 }
3115
3116 type fooProto struct{}
3117
3118 func (fooProto) RoundTrip(req *Request) (*Response, error) {
3119 res := &Response{
3120 Status: "200 OK",
3121 StatusCode: 200,
3122 Header: make(Header),
3123 Body: io.NopCloser(strings.NewReader("You wanted " + req.URL.String())),
3124 }
3125 return res, nil
3126 }
3127
3128 func TestTransportAltProto(t *testing.T) {
3129 defer afterTest(t)
3130 tr := &Transport{}
3131 c := &Client{Transport: tr}
3132 tr.RegisterProtocol("foo", fooProto{})
3133 res, err := c.Get("foo://bar.com/path")
3134 if err != nil {
3135 t.Fatal(err)
3136 }
3137 bodyb, err := io.ReadAll(res.Body)
3138 if err != nil {
3139 t.Fatal(err)
3140 }
3141 body := string(bodyb)
3142 if e := "You wanted foo://bar.com/path"; body != e {
3143 t.Errorf("got response %q, want %q", body, e)
3144 }
3145 }
3146
3147 func TestTransportNoHost(t *testing.T) {
3148 defer afterTest(t)
3149 tr := &Transport{}
3150 _, err := tr.RoundTrip(&Request{
3151 Header: make(Header),
3152 URL: &url.URL{
3153 Scheme: "http",
3154 },
3155 })
3156 want := "http: no Host in request URL"
3157 if got := fmt.Sprint(err); got != want {
3158 t.Errorf("error = %v; want %q", err, want)
3159 }
3160 }
3161
3162
3163 func TestTransportEmptyMethod(t *testing.T) {
3164 req, _ := NewRequest("GET", "http://foo.com/", nil)
3165 req.Method = ""
3166 got, err := httputil.DumpRequestOut(req, false)
3167 if err != nil {
3168 t.Fatal(err)
3169 }
3170 if !strings.Contains(string(got), "GET ") {
3171 t.Fatalf("expected substring 'GET '; got: %s", got)
3172 }
3173 }
3174
3175 func TestTransportSocketLateBinding(t *testing.T) {
3176 run(t, testTransportSocketLateBinding, http3SkippedMode)
3177 }
3178 func testTransportSocketLateBinding(t *testing.T, mode testMode) {
3179 mux := NewServeMux()
3180 fooGate := make(chan bool, 1)
3181 mux.HandleFunc("/foo", func(w ResponseWriter, r *Request) {
3182 w.Header().Set("foo-ipport", r.RemoteAddr)
3183 w.(Flusher).Flush()
3184 <-fooGate
3185 })
3186 mux.HandleFunc("/bar", func(w ResponseWriter, r *Request) {
3187 w.Header().Set("bar-ipport", r.RemoteAddr)
3188 })
3189 ts := newClientServerTest(t, mode, mux, optRealNet).ts
3190
3191 dialGate := make(chan bool, 1)
3192 dialing := make(chan bool)
3193 c := ts.Client()
3194 c.Transport.(*Transport).Dial = func(n, addr string) (net.Conn, error) {
3195 for {
3196 select {
3197 case ok := <-dialGate:
3198 if !ok {
3199 return nil, errors.New("manually closed")
3200 }
3201 return net.Dial(n, addr)
3202 case dialing <- true:
3203 }
3204 }
3205 }
3206 defer close(dialGate)
3207
3208 dialGate <- true
3209 fooRes, err := c.Get(ts.URL + "/foo")
3210 if err != nil {
3211 t.Fatal(err)
3212 }
3213 fooAddr := fooRes.Header.Get("foo-ipport")
3214 if fooAddr == "" {
3215 t.Fatal("No addr on /foo request")
3216 }
3217
3218 fooDone := make(chan struct{})
3219 go func() {
3220
3221
3222
3223
3224 if mode == http2Mode {
3225
3226
3227
3228
3229 select {
3230 case <-dialing:
3231 t.Errorf("unexpected second Dial in HTTP/2 mode")
3232 case <-time.After(10 * time.Millisecond):
3233 }
3234 } else {
3235 <-dialing
3236 }
3237 fooGate <- true
3238 io.Copy(io.Discard, fooRes.Body)
3239 fooRes.Body.Close()
3240 close(fooDone)
3241 }()
3242 defer func() {
3243 <-fooDone
3244 }()
3245
3246 barRes, err := c.Get(ts.URL + "/bar")
3247 if err != nil {
3248 t.Fatal(err)
3249 }
3250 barAddr := barRes.Header.Get("bar-ipport")
3251 if barAddr != fooAddr {
3252 t.Fatalf("/foo came from conn %q; /bar came from %q instead", fooAddr, barAddr)
3253 }
3254 barRes.Body.Close()
3255 }
3256
3257
3258 func TestTransportReading100Continue(t *testing.T) {
3259 defer afterTest(t)
3260
3261 const numReqs = 5
3262 reqBody := func(n int) string { return fmt.Sprintf("request body %d", n) }
3263 reqID := func(n int) string { return fmt.Sprintf("REQ-ID-%d", n) }
3264
3265 send100Response := func(w *io.PipeWriter, r *io.PipeReader) {
3266 defer w.Close()
3267 defer r.Close()
3268 br := bufio.NewReader(r)
3269 n := 0
3270 for {
3271 n++
3272 req, err := ReadRequest(br)
3273 if err == io.EOF {
3274 return
3275 }
3276 if err != nil {
3277 t.Error(err)
3278 return
3279 }
3280 slurp, err := io.ReadAll(req.Body)
3281 if err != nil {
3282 t.Errorf("Server request body slurp: %v", err)
3283 return
3284 }
3285 id := req.Header.Get("Request-Id")
3286 resCode := req.Header.Get("X-Want-Response-Code")
3287 if resCode == "" {
3288 resCode = "100 Continue"
3289 if string(slurp) != reqBody(n) {
3290 t.Errorf("Server got %q, %v; want %q", slurp, err, reqBody(n))
3291 }
3292 }
3293 body := fmt.Sprintf("Response number %d", n)
3294 v := []byte(strings.Replace(fmt.Sprintf(`HTTP/1.1 %s
3295 Date: Thu, 28 Feb 2013 17:55:41 GMT
3296
3297 HTTP/1.1 200 OK
3298 Content-Type: text/html
3299 Echo-Request-Id: %s
3300 Content-Length: %d
3301
3302 %s`, resCode, id, len(body), body), "\n", "\r\n", -1))
3303 w.Write(v)
3304 if id == reqID(numReqs) {
3305 return
3306 }
3307 }
3308
3309 }
3310
3311 tr := &Transport{
3312 Dial: func(n, addr string) (net.Conn, error) {
3313 sr, sw := io.Pipe()
3314 cr, cw := io.Pipe()
3315 conn := &rwTestConn{
3316 Reader: cr,
3317 Writer: sw,
3318 closeFunc: func() error {
3319 sw.Close()
3320 cw.Close()
3321 return nil
3322 },
3323 }
3324 go send100Response(cw, sr)
3325 return conn, nil
3326 },
3327 DisableKeepAlives: false,
3328 }
3329 defer tr.CloseIdleConnections()
3330 c := &Client{Transport: tr}
3331
3332 testResponse := func(req *Request, name string, wantCode int) {
3333 t.Helper()
3334 res, err := c.Do(req)
3335 if err != nil {
3336 t.Fatalf("%s: Do: %v", name, err)
3337 }
3338 if res.StatusCode != wantCode {
3339 t.Fatalf("%s: Response Statuscode=%d; want %d", name, res.StatusCode, wantCode)
3340 }
3341 if id, idBack := req.Header.Get("Request-Id"), res.Header.Get("Echo-Request-Id"); id != "" && id != idBack {
3342 t.Errorf("%s: response id %q != request id %q", name, idBack, id)
3343 }
3344 _, err = io.ReadAll(res.Body)
3345 if err != nil {
3346 t.Fatalf("%s: Slurp error: %v", name, err)
3347 }
3348 }
3349
3350
3351 for i := 1; i <= numReqs; i++ {
3352 req, _ := NewRequest("POST", "http://dummy.tld/", strings.NewReader(reqBody(i)))
3353 req.Header.Set("Request-Id", reqID(i))
3354 testResponse(req, fmt.Sprintf("100, %d/%d", i, numReqs), 200)
3355 }
3356 }
3357
3358
3359
3360 func TestTransportIgnore1xxResponses(t *testing.T) {
3361 run(t, testTransportIgnore1xxResponses, []testMode{http1Mode})
3362 }
3363 func testTransportIgnore1xxResponses(t *testing.T, mode testMode) {
3364 cst := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
3365 conn, buf, _ := w.(Hijacker).Hijack()
3366 buf.Write([]byte("HTTP/1.1 123 OneTwoThree\r\nFoo: bar\r\n\r\nHTTP/1.1 200 OK\r\nBar: baz\r\nContent-Length: 5\r\n\r\nHello"))
3367 buf.Flush()
3368 conn.Close()
3369 }))
3370 cst.tr.DisableKeepAlives = true
3371
3372 var got strings.Builder
3373
3374 req, _ := NewRequest("GET", cst.ts.URL, nil)
3375 req = req.WithContext(httptrace.WithClientTrace(context.Background(), &httptrace.ClientTrace{
3376 Got1xxResponse: func(code int, header textproto.MIMEHeader) error {
3377 fmt.Fprintf(&got, "1xx: code=%v, header=%v\n", code, header)
3378 return nil
3379 },
3380 }))
3381 res, err := cst.c.Do(req)
3382 if err != nil {
3383 t.Fatal(err)
3384 }
3385 defer res.Body.Close()
3386
3387 res.Write(&got)
3388 want := "1xx: code=123, header=map[Foo:[bar]]\nHTTP/1.1 200 OK\r\nContent-Length: 5\r\nBar: baz\r\n\r\nHello"
3389 if got.String() != want {
3390 t.Errorf(" got: %q\nwant: %q\n", got.String(), want)
3391 }
3392 }
3393
3394 func TestTransportLimits1xxResponses(t *testing.T) {
3395 run(t, testTransportLimits1xxResponses, http3SkippedMode)
3396 }
3397 func testTransportLimits1xxResponses(t *testing.T, mode testMode) {
3398 cst := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
3399 w.Header().Add("X-Header", strings.Repeat("a", 100))
3400 for i := 0; i < 10; i++ {
3401 w.WriteHeader(123)
3402 }
3403 w.WriteHeader(204)
3404 }))
3405 cst.tr.DisableKeepAlives = true
3406 cst.tr.MaxResponseHeaderBytes = 1000
3407
3408 res, err := cst.c.Get(cst.ts.URL)
3409 if err == nil {
3410 res.Body.Close()
3411 t.Fatalf("RoundTrip succeeded; want error")
3412 }
3413 for _, want := range []string{
3414 "response headers exceeded",
3415 "too many 1xx",
3416 "header list too large",
3417 } {
3418 if strings.Contains(err.Error(), want) {
3419 return
3420 }
3421 }
3422 t.Errorf(`got error %q; want "response headers exceeded" or "too many 1xx"`, err)
3423 }
3424
3425 func TestTransportDoesNotLimitDelivered1xxResponses(t *testing.T) {
3426 run(t, testTransportDoesNotLimitDelivered1xxResponses, http3SkippedMode)
3427 }
3428 func testTransportDoesNotLimitDelivered1xxResponses(t *testing.T, mode testMode) {
3429 if mode == http2Mode {
3430 t.Skip("skip until x/net/http2 updated")
3431 }
3432 const num1xx = 10
3433 cst := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
3434 w.Header().Add("X-Header", strings.Repeat("a", 100))
3435 for i := 0; i < 10; i++ {
3436 w.WriteHeader(123)
3437 }
3438 w.WriteHeader(204)
3439 }))
3440 cst.tr.DisableKeepAlives = true
3441 cst.tr.MaxResponseHeaderBytes = 1000
3442
3443 got1xx := 0
3444 ctx := httptrace.WithClientTrace(context.Background(), &httptrace.ClientTrace{
3445 Got1xxResponse: func(code int, header textproto.MIMEHeader) error {
3446 got1xx++
3447 return nil
3448 },
3449 })
3450 req, _ := NewRequestWithContext(ctx, "GET", cst.ts.URL, nil)
3451 res, err := cst.c.Do(req)
3452 if err != nil {
3453 t.Fatal(err)
3454 }
3455 res.Body.Close()
3456 if got1xx != num1xx {
3457 t.Errorf("Got %v 1xx responses, want %x", got1xx, num1xx)
3458 }
3459 }
3460
3461
3462
3463 func TestTransportTreat101Terminal(t *testing.T) {
3464 run(t, testTransportTreat101Terminal, []testMode{http1Mode})
3465 }
3466 func testTransportTreat101Terminal(t *testing.T, mode testMode) {
3467 cst := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
3468 conn, buf, _ := w.(Hijacker).Hijack()
3469 buf.Write([]byte("HTTP/1.1 101 Switching Protocols\r\n\r\n"))
3470 buf.Write([]byte("HTTP/1.1 204 No Content\r\n\r\n"))
3471 buf.Flush()
3472 conn.Close()
3473 }))
3474 res, err := cst.c.Get(cst.ts.URL)
3475 if err != nil {
3476 t.Fatal(err)
3477 }
3478 defer res.Body.Close()
3479 if res.StatusCode != StatusSwitchingProtocols {
3480 t.Errorf("StatusCode = %v; want 101 Switching Protocols", res.StatusCode)
3481 }
3482 }
3483
3484 type proxyFromEnvTest struct {
3485 req string
3486
3487 env string
3488 httpsenv string
3489 noenv string
3490 reqmeth string
3491
3492 want string
3493 wanterr error
3494 }
3495
3496 func (t proxyFromEnvTest) String() string {
3497 var buf strings.Builder
3498 space := func() {
3499 if buf.Len() > 0 {
3500 buf.WriteByte(' ')
3501 }
3502 }
3503 if t.env != "" {
3504 fmt.Fprintf(&buf, "http_proxy=%q", t.env)
3505 }
3506 if t.httpsenv != "" {
3507 space()
3508 fmt.Fprintf(&buf, "https_proxy=%q", t.httpsenv)
3509 }
3510 if t.noenv != "" {
3511 space()
3512 fmt.Fprintf(&buf, "no_proxy=%q", t.noenv)
3513 }
3514 if t.reqmeth != "" {
3515 space()
3516 fmt.Fprintf(&buf, "request_method=%q", t.reqmeth)
3517 }
3518 req := "http://example.com"
3519 if t.req != "" {
3520 req = t.req
3521 }
3522 space()
3523 fmt.Fprintf(&buf, "req=%q", req)
3524 return strings.TrimSpace(buf.String())
3525 }
3526
3527 var proxyFromEnvTests = []proxyFromEnvTest{
3528 {env: "127.0.0.1:8080", want: "http://127.0.0.1:8080"},
3529 {env: "cache.corp.example.com:1234", want: "http://cache.corp.example.com:1234"},
3530 {env: "cache.corp.example.com", want: "http://cache.corp.example.com"},
3531 {env: "https://cache.corp.example.com", want: "https://cache.corp.example.com"},
3532 {env: "http://127.0.0.1:8080", want: "http://127.0.0.1:8080"},
3533 {env: "https://127.0.0.1:8080", want: "https://127.0.0.1:8080"},
3534 {env: "socks5://127.0.0.1", want: "socks5://127.0.0.1"},
3535 {env: "socks5h://127.0.0.1", want: "socks5h://127.0.0.1"},
3536
3537
3538 {req: "http://insecure.tld/", env: "http.proxy.tld", httpsenv: "secure.proxy.tld", want: "http://http.proxy.tld"},
3539
3540 {req: "https://secure.tld/", env: "http.proxy.tld", httpsenv: "secure.proxy.tld", want: "http://secure.proxy.tld"},
3541 {req: "https://secure.tld/", env: "http.proxy.tld", httpsenv: "https://secure.proxy.tld", want: "https://secure.proxy.tld"},
3542
3543
3544
3545 {env: "http://10.1.2.3:8080", reqmeth: "POST",
3546 want: "<nil>",
3547 wanterr: errors.New("refusing to use HTTP_PROXY value in CGI environment; see golang.org/s/cgihttpproxy")},
3548
3549 {want: "<nil>"},
3550
3551 {noenv: "example.com", req: "http://example.com/", env: "proxy", want: "<nil>"},
3552 {noenv: ".example.com", req: "http://example.com/", env: "proxy", want: "http://proxy"},
3553 {noenv: "ample.com", req: "http://example.com/", env: "proxy", want: "http://proxy"},
3554 {noenv: "example.com", req: "http://foo.example.com/", env: "proxy", want: "<nil>"},
3555 {noenv: ".foo.com", req: "http://example.com/", env: "proxy", want: "http://proxy"},
3556 }
3557
3558 func testProxyForRequest(t *testing.T, tt proxyFromEnvTest, proxyForRequest func(req *Request) (*url.URL, error)) {
3559 t.Helper()
3560 reqURL := tt.req
3561 if reqURL == "" {
3562 reqURL = "http://example.com"
3563 }
3564 req, _ := NewRequest("GET", reqURL, nil)
3565 url, err := proxyForRequest(req)
3566 if g, e := fmt.Sprintf("%v", err), fmt.Sprintf("%v", tt.wanterr); g != e {
3567 t.Errorf("%v: got error = %q, want %q", tt, g, e)
3568 return
3569 }
3570 if got := fmt.Sprintf("%s", url); got != tt.want {
3571 t.Errorf("%v: got URL = %q, want %q", tt, url, tt.want)
3572 }
3573 }
3574
3575 func TestProxyFromEnvironment(t *testing.T) {
3576 ResetProxyEnv()
3577 defer ResetProxyEnv()
3578 for _, tt := range proxyFromEnvTests {
3579 testProxyForRequest(t, tt, func(req *Request) (*url.URL, error) {
3580 os.Setenv("HTTP_PROXY", tt.env)
3581 os.Setenv("HTTPS_PROXY", tt.httpsenv)
3582 os.Setenv("NO_PROXY", tt.noenv)
3583 os.Setenv("REQUEST_METHOD", tt.reqmeth)
3584 ResetCachedEnvironment()
3585 return ProxyFromEnvironment(req)
3586 })
3587 }
3588 }
3589
3590 func TestProxyFromEnvironmentLowerCase(t *testing.T) {
3591 ResetProxyEnv()
3592 defer ResetProxyEnv()
3593 for _, tt := range proxyFromEnvTests {
3594 testProxyForRequest(t, tt, func(req *Request) (*url.URL, error) {
3595 os.Setenv("http_proxy", tt.env)
3596 os.Setenv("https_proxy", tt.httpsenv)
3597 os.Setenv("no_proxy", tt.noenv)
3598 os.Setenv("REQUEST_METHOD", tt.reqmeth)
3599 ResetCachedEnvironment()
3600 return ProxyFromEnvironment(req)
3601 })
3602 }
3603 }
3604
3605 func TestIdleConnChannelLeak(t *testing.T) {
3606 run(t, testIdleConnChannelLeak, []testMode{http1Mode}, testNotParallel)
3607 }
3608 func testIdleConnChannelLeak(t *testing.T, mode testMode) {
3609
3610 var mu sync.Mutex
3611 var n int
3612
3613 ts := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
3614 mu.Lock()
3615 n++
3616 mu.Unlock()
3617 }), optRealNet).ts
3618
3619 const nReqs = 5
3620 didRead := make(chan bool, nReqs)
3621 SetReadLoopBeforeNextReadHook(func() { didRead <- true })
3622 defer SetReadLoopBeforeNextReadHook(nil)
3623
3624 c := ts.Client()
3625 tr := c.Transport.(*Transport)
3626 tr.Dial = func(netw, addr string) (net.Conn, error) {
3627 return net.Dial(netw, ts.Listener.Addr().String())
3628 }
3629
3630
3631 for _, disableKeep := range []bool{true, false} {
3632 tr.DisableKeepAlives = disableKeep
3633 for i := 0; i < nReqs; i++ {
3634 _, err := c.Get(fmt.Sprintf("http://foo-host-%d.tld/", i))
3635 if err != nil {
3636 t.Fatal(err)
3637 }
3638
3639
3640
3641
3642
3643 }
3644
3645
3646
3647
3648
3649
3650
3651 for i := 0; i < nReqs; i++ {
3652 <-didRead
3653 }
3654
3655 if got := tr.IdleConnWaitMapSizeForTesting(); got != 0 {
3656 t.Fatalf("for DisableKeepAlives = %v, map size = %d; want 0", disableKeep, got)
3657 }
3658 }
3659 }
3660
3661
3662
3663
3664 func TestTransportClosesRequestBody(t *testing.T) {
3665 run(t, testTransportClosesRequestBody, []testMode{http1Mode})
3666 }
3667 func testTransportClosesRequestBody(t *testing.T, mode testMode) {
3668 ts := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
3669 io.Copy(io.Discard, r.Body)
3670 })).ts
3671
3672 c := ts.Client()
3673
3674 closes := 0
3675
3676 res, err := c.Post(ts.URL, "text/plain", countCloseReader{&closes, strings.NewReader("hello")})
3677 if err != nil {
3678 t.Fatal(err)
3679 }
3680 res.Body.Close()
3681 if closes != 1 {
3682 t.Errorf("closes = %d; want 1", closes)
3683 }
3684 }
3685
3686 func TestTransportTLSHandshakeTimeout(t *testing.T) {
3687 defer afterTest(t)
3688 if testing.Short() {
3689 t.Skip("skipping in short mode")
3690 }
3691 ln := newLocalListener(t)
3692 defer ln.Close()
3693 testdonec := make(chan struct{})
3694 defer close(testdonec)
3695
3696 go func() {
3697 c, err := ln.Accept()
3698 if err != nil {
3699 t.Error(err)
3700 return
3701 }
3702 <-testdonec
3703 c.Close()
3704 }()
3705
3706 tr := &Transport{
3707 Dial: func(_, _ string) (net.Conn, error) {
3708 return net.Dial("tcp", ln.Addr().String())
3709 },
3710 TLSHandshakeTimeout: 250 * time.Millisecond,
3711 }
3712 cl := &Client{Transport: tr}
3713 _, err := cl.Get("https://dummy.tld/")
3714 if err == nil {
3715 t.Error("expected error")
3716 return
3717 }
3718 ue, ok := err.(*url.Error)
3719 if !ok {
3720 t.Errorf("expected url.Error; got %#v", err)
3721 return
3722 }
3723 ne, ok := ue.Err.(net.Error)
3724 if !ok {
3725 t.Errorf("expected net.Error; got %#v", err)
3726 return
3727 }
3728 if !ne.Timeout() {
3729 t.Errorf("expected timeout error; got %v", err)
3730 }
3731 if !strings.Contains(err.Error(), "handshake timeout") {
3732 t.Errorf("expected 'handshake timeout' in error; got %v", err)
3733 }
3734 }
3735
3736
3737 func TestTLSServerClosesConnection(t *testing.T) {
3738 run(t, testTLSServerClosesConnection, []testMode{https1Mode})
3739 }
3740 func testTLSServerClosesConnection(t *testing.T, mode testMode) {
3741 closedc := make(chan bool, 1)
3742 ts := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
3743 if strings.Contains(r.URL.Path, "/keep-alive-then-die") {
3744 conn, _, _ := w.(Hijacker).Hijack()
3745 conn.Write([]byte("HTTP/1.1 200 OK\r\nContent-Length: 3\r\n\r\nfoo"))
3746 conn.Close()
3747 closedc <- true
3748 return
3749 }
3750 fmt.Fprintf(w, "hello")
3751 })).ts
3752
3753 c := ts.Client()
3754 tr := c.Transport.(*Transport)
3755
3756 var nSuccess = 0
3757 var errs []error
3758 const trials = 20
3759 for i := 0; i < trials; i++ {
3760 tr.CloseIdleConnections()
3761 res, err := c.Get(ts.URL + "/keep-alive-then-die")
3762 if err != nil {
3763 t.Fatal(err)
3764 }
3765 <-closedc
3766 slurp, err := io.ReadAll(res.Body)
3767 if err != nil {
3768 t.Fatal(err)
3769 }
3770 if string(slurp) != "foo" {
3771 t.Errorf("Got %q, want foo", slurp)
3772 }
3773
3774
3775
3776 res, err = c.Get(ts.URL + "/")
3777 if err != nil {
3778 errs = append(errs, err)
3779 continue
3780 }
3781 slurp, err = io.ReadAll(res.Body)
3782 if err != nil {
3783 errs = append(errs, err)
3784 continue
3785 }
3786 nSuccess++
3787 }
3788 if nSuccess > 0 {
3789 t.Logf("successes = %d of %d", nSuccess, trials)
3790 } else {
3791 t.Errorf("All runs failed:")
3792 }
3793 for _, err := range errs {
3794 t.Logf(" err: %v", err)
3795 }
3796 }
3797
3798
3799
3800
3801 type byteFromChanReader chan byte
3802
3803 func (c byteFromChanReader) Read(p []byte) (n int, err error) {
3804 if len(p) == 0 {
3805 return
3806 }
3807 b, ok := <-c
3808 if !ok {
3809 return 0, io.EOF
3810 }
3811 p[0] = b
3812 return 1, nil
3813 }
3814
3815
3816
3817
3818
3819
3820
3821 func TestTransportNoReuseAfterEarlyResponse(t *testing.T) {
3822 run(t, testTransportNoReuseAfterEarlyResponse, []testMode{http1Mode}, testNotParallel)
3823 }
3824 func testTransportNoReuseAfterEarlyResponse(t *testing.T, mode testMode) {
3825 defer func(d time.Duration) {
3826 *MaxWriteWaitBeforeConnReuse = d
3827 }(*MaxWriteWaitBeforeConnReuse)
3828 *MaxWriteWaitBeforeConnReuse = 10 * time.Millisecond
3829 var sconn struct {
3830 sync.Mutex
3831 c net.Conn
3832 }
3833 var getOkay bool
3834 var willCopy sync.WaitGroup
3835 closeConn := func() {
3836 sconn.Lock()
3837 defer sconn.Unlock()
3838 if sconn.c != nil {
3839 sconn.c.Close()
3840 sconn.c = nil
3841 if !getOkay {
3842 t.Logf("Closed server connection")
3843 }
3844 }
3845 }
3846 defer func() {
3847 closeConn()
3848 willCopy.Wait()
3849 }()
3850
3851 ts := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
3852 if r.Method == "GET" {
3853 io.WriteString(w, "bar")
3854 return
3855 }
3856 conn, _, _ := w.(Hijacker).Hijack()
3857 sconn.Lock()
3858 sconn.c = conn
3859 sconn.Unlock()
3860
3861 willCopy.Add(1)
3862 conn.Write([]byte("HTTP/1.1 200 OK\r\nContent-Length: 3\r\n\r\nfoo"))
3863 go func() {
3864 io.Copy(io.Discard, conn)
3865 willCopy.Done()
3866 }()
3867 })).ts
3868 c := ts.Client()
3869
3870 const bodySize = 256 << 10
3871 finalBit := make(byteFromChanReader, 1)
3872 req, _ := NewRequest("POST", ts.URL, io.MultiReader(io.LimitReader(neverEnding('x'), bodySize-1), finalBit))
3873 req.ContentLength = bodySize
3874 res, err := c.Do(req)
3875 if err := wantBody(res, err, "foo"); err != nil {
3876 t.Errorf("POST response: %v", err)
3877 }
3878
3879 res, err = c.Get(ts.URL)
3880 if err := wantBody(res, err, "bar"); err != nil {
3881 t.Errorf("GET response: %v", err)
3882 return
3883 }
3884 getOkay = true
3885 finalBit <- 'x'
3886 close(finalBit)
3887 }
3888
3889
3890
3891 func TestTransportIssue10457(t *testing.T) { run(t, testTransportIssue10457, []testMode{http1Mode}) }
3892 func testTransportIssue10457(t *testing.T, mode testMode) {
3893 ts := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
3894
3895
3896
3897
3898
3899 conn, _, _ := w.(Hijacker).Hijack()
3900 conn.Write([]byte("HTTP/1.1 200 OK\r\nFoo: Bar\r\nContent-Length: 0\r\n\r\n"))
3901 conn.Close()
3902 })).ts
3903 c := ts.Client()
3904
3905 res, err := c.Get(ts.URL)
3906 if err != nil {
3907 t.Fatalf("Get: %v", err)
3908 }
3909 defer res.Body.Close()
3910
3911
3912
3913
3914 if got, want := res.Header.Get("Foo"), "Bar"; got != want {
3915 t.Errorf("Foo header = %q; want %q", got, want)
3916 }
3917 }
3918
3919 type closerFunc func() error
3920
3921 func (f closerFunc) Close() error { return f() }
3922
3923 type writerFuncConn struct {
3924 net.Conn
3925 write func(p []byte) (n int, err error)
3926 }
3927
3928 func (c writerFuncConn) Write(p []byte) (n int, err error) { return c.write(p) }
3929
3930
3931
3932
3933
3934
3935
3936
3937
3938
3939
3940
3941
3942 func TestRetryRequestsOnError(t *testing.T) {
3943 run(t, testRetryRequestsOnError, testNotParallel, []testMode{http1Mode})
3944 }
3945 func testRetryRequestsOnError(t *testing.T, mode testMode) {
3946 newRequest := func(method, urlStr string, body io.Reader) *Request {
3947 req, err := NewRequest(method, urlStr, body)
3948 if err != nil {
3949 t.Fatal(err)
3950 }
3951 return req
3952 }
3953
3954 testCases := []struct {
3955 name string
3956 failureN int
3957 failureErr error
3958
3959
3960
3961 req func() *Request
3962 reqString string
3963 }{
3964 {
3965 name: "IdempotentNoBodySomeWritten",
3966
3967
3968 failureN: 1,
3969
3970 failureErr: ExportErrServerClosedIdle,
3971 req: func() *Request {
3972 return newRequest("GET", "http://fake.golang", nil)
3973 },
3974 reqString: `GET / HTTP/1.1\r\nHost: fake.golang\r\nUser-Agent: Go-http-client/1.1\r\nAccept-Encoding: gzip\r\n\r\n`,
3975 },
3976 {
3977 name: "IdempotentGetBodySomeWritten",
3978
3979
3980 failureN: 1,
3981
3982 failureErr: ExportErrServerClosedIdle,
3983 req: func() *Request {
3984 return newRequest("GET", "http://fake.golang", strings.NewReader("foo\n"))
3985 },
3986 reqString: `GET / HTTP/1.1\r\nHost: fake.golang\r\nUser-Agent: Go-http-client/1.1\r\nContent-Length: 4\r\nAccept-Encoding: gzip\r\n\r\nfoo\n`,
3987 },
3988 {
3989 name: "NothingWrittenNoBody",
3990
3991
3992 failureN: 0,
3993 failureErr: errors.New("second write fails"),
3994 req: func() *Request {
3995 return newRequest("DELETE", "http://fake.golang", nil)
3996 },
3997 reqString: `DELETE / HTTP/1.1\r\nHost: fake.golang\r\nUser-Agent: Go-http-client/1.1\r\nAccept-Encoding: gzip\r\n\r\n`,
3998 },
3999 {
4000 name: "NothingWrittenGetBody",
4001
4002
4003 failureN: 0,
4004 failureErr: errors.New("second write fails"),
4005
4006
4007 req: func() *Request {
4008 return newRequest("POST", "http://fake.golang", strings.NewReader("foo\n"))
4009 },
4010 reqString: `POST / HTTP/1.1\r\nHost: fake.golang\r\nUser-Agent: Go-http-client/1.1\r\nContent-Length: 4\r\nAccept-Encoding: gzip\r\n\r\nfoo\n`,
4011 },
4012 }
4013
4014 for _, tc := range testCases {
4015 t.Run(tc.name, func(t *testing.T) {
4016 var (
4017 mu sync.Mutex
4018 logbuf strings.Builder
4019 )
4020 logf := func(format string, args ...any) {
4021 mu.Lock()
4022 defer mu.Unlock()
4023 fmt.Fprintf(&logbuf, format, args...)
4024 logbuf.WriteByte('\n')
4025 }
4026
4027 ts := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
4028 logf("Handler")
4029 w.Header().Set("X-Status", "ok")
4030 }), optRealNet).ts
4031
4032 var writeNumAtomic int32
4033 c := ts.Client()
4034 c.Transport.(*Transport).Dial = func(network, addr string) (net.Conn, error) {
4035 logf("Dial")
4036 c, err := net.Dial(network, ts.Listener.Addr().String())
4037 if err != nil {
4038 logf("Dial error: %v", err)
4039 return nil, err
4040 }
4041 return &writerFuncConn{
4042 Conn: c,
4043 write: func(p []byte) (n int, err error) {
4044 if atomic.AddInt32(&writeNumAtomic, 1) == 2 {
4045 logf("intentional write failure")
4046 return tc.failureN, tc.failureErr
4047 }
4048 logf("Write(%q)", p)
4049 return c.Write(p)
4050 },
4051 }, nil
4052 }
4053
4054 SetRoundTripRetried(func() {
4055 logf("Retried.")
4056 })
4057 defer SetRoundTripRetried(nil)
4058
4059 for i := 0; i < 3; i++ {
4060 t0 := time.Now()
4061 req := tc.req()
4062 res, err := c.Do(req)
4063 if err != nil {
4064 if time.Since(t0) < *MaxWriteWaitBeforeConnReuse/2 {
4065 mu.Lock()
4066 got := logbuf.String()
4067 mu.Unlock()
4068 t.Fatalf("i=%d: Do = %v; log:\n%s", i, err, got)
4069 }
4070 t.Skipf("connection likely wasn't recycled within %d, interfering with actual test; skipping", *MaxWriteWaitBeforeConnReuse)
4071 }
4072 res.Body.Close()
4073 if res.Request != req {
4074 t.Errorf("Response.Request != original request; want identical Request")
4075 }
4076 }
4077
4078 mu.Lock()
4079 got := logbuf.String()
4080 mu.Unlock()
4081 want := fmt.Sprintf(`Dial
4082 Write("%s")
4083 Handler
4084 intentional write failure
4085 Retried.
4086 Dial
4087 Write("%s")
4088 Handler
4089 Write("%s")
4090 Handler
4091 `, tc.reqString, tc.reqString, tc.reqString)
4092 if got != want {
4093 t.Errorf("Log of events differs. Got:\n%s\nWant:\n%s", got, want)
4094 }
4095 })
4096 }
4097 }
4098
4099
4100 func TestTransportClosesBodyOnError(t *testing.T) { run(t, testTransportClosesBodyOnError) }
4101 func testTransportClosesBodyOnError(t *testing.T, mode testMode) {
4102 readBody := make(chan error, 1)
4103 ts := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
4104 _, err := io.ReadAll(r.Body)
4105 readBody <- err
4106 })).ts
4107 c := ts.Client()
4108 fakeErr := errors.New("fake error")
4109 didClose := make(chan bool, 1)
4110 req, _ := NewRequest("POST", ts.URL, struct {
4111 io.Reader
4112 io.Closer
4113 }{
4114 io.MultiReader(io.LimitReader(neverEnding('x'), 1<<20), iotest.ErrReader(fakeErr)),
4115 closerFunc(func() error {
4116 select {
4117 case didClose <- true:
4118 default:
4119 }
4120 return nil
4121 }),
4122 })
4123
4124
4125
4126
4127
4128
4129
4130 req.Header.Set("Expect", "100-continue")
4131 res, err := c.Do(req)
4132 if res != nil {
4133 defer res.Body.Close()
4134 }
4135 if err == nil || !strings.Contains(err.Error(), fakeErr.Error()) {
4136 t.Fatalf("Do error = %v; want something containing %q", err, fakeErr.Error())
4137 }
4138 if err := <-readBody; err == nil {
4139 t.Errorf("Unexpected success reading request body from handler; want 'unexpected EOF reading trailer'")
4140 }
4141 select {
4142 case <-didClose:
4143 default:
4144 t.Errorf("didn't see Body.Close")
4145 }
4146 }
4147
4148 func TestTransportDialTLS(t *testing.T) {
4149 run(t, testTransportDialTLS, []testMode{https1Mode, http2Mode})
4150 }
4151 func testTransportDialTLS(t *testing.T, mode testMode) {
4152 var mu sync.Mutex
4153 var gotReq, didDial bool
4154
4155 ts := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
4156 mu.Lock()
4157 gotReq = true
4158 mu.Unlock()
4159 }), optRealNet).ts
4160 c := ts.Client()
4161 c.Transport.(*Transport).DialTLS = func(netw, addr string) (net.Conn, error) {
4162 mu.Lock()
4163 didDial = true
4164 mu.Unlock()
4165 c, err := tls.Dial(netw, addr, c.Transport.(*Transport).TLSClientConfig)
4166 if err != nil {
4167 return nil, err
4168 }
4169 return c, c.Handshake()
4170 }
4171
4172 res, err := c.Get(ts.URL)
4173 if err != nil {
4174 t.Fatal(err)
4175 }
4176 res.Body.Close()
4177 mu.Lock()
4178 if !gotReq {
4179 t.Error("didn't get request")
4180 }
4181 if !didDial {
4182 t.Error("didn't use dial hook")
4183 }
4184 }
4185
4186 func TestTransportDialContext(t *testing.T) { run(t, testTransportDialContext, http3SkippedMode) }
4187 func testTransportDialContext(t *testing.T, mode testMode) {
4188 ctxKey := "some-key"
4189 ctxValue := "some-value"
4190 var (
4191 mu sync.Mutex
4192 gotReq bool
4193 gotCtxValue any
4194 )
4195
4196 ts := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
4197 mu.Lock()
4198 gotReq = true
4199 mu.Unlock()
4200 }), optRealNet).ts
4201 c := ts.Client()
4202 c.Transport.(*Transport).DialContext = func(ctx context.Context, netw, addr string) (net.Conn, error) {
4203 mu.Lock()
4204 gotCtxValue = ctx.Value(ctxKey)
4205 mu.Unlock()
4206 return net.Dial(netw, addr)
4207 }
4208
4209 req, err := NewRequest("GET", ts.URL, nil)
4210 if err != nil {
4211 t.Fatal(err)
4212 }
4213 ctx := context.WithValue(context.Background(), ctxKey, ctxValue)
4214 res, err := c.Do(req.WithContext(ctx))
4215 if err != nil {
4216 t.Fatal(err)
4217 }
4218 res.Body.Close()
4219 mu.Lock()
4220 if !gotReq {
4221 t.Error("didn't get request")
4222 }
4223 if got, want := gotCtxValue, ctxValue; got != want {
4224 t.Errorf("got context with value %v, want %v", got, want)
4225 }
4226 }
4227
4228 func TestTransportDialTLSContext(t *testing.T) {
4229 run(t, testTransportDialTLSContext, []testMode{https1Mode, http2Mode})
4230 }
4231 func testTransportDialTLSContext(t *testing.T, mode testMode) {
4232 ctxKey := "some-key"
4233 ctxValue := "some-value"
4234 var (
4235 mu sync.Mutex
4236 gotReq bool
4237 gotCtxValue any
4238 )
4239
4240 ts := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
4241 mu.Lock()
4242 gotReq = true
4243 mu.Unlock()
4244 }), optRealNet).ts
4245 c := ts.Client()
4246 c.Transport.(*Transport).DialTLSContext = func(ctx context.Context, netw, addr string) (net.Conn, error) {
4247 mu.Lock()
4248 gotCtxValue = ctx.Value(ctxKey)
4249 mu.Unlock()
4250 c, err := tls.Dial(netw, addr, c.Transport.(*Transport).TLSClientConfig)
4251 if err != nil {
4252 return nil, err
4253 }
4254 return c, c.HandshakeContext(ctx)
4255 }
4256
4257 req, err := NewRequest("GET", ts.URL, nil)
4258 if err != nil {
4259 t.Fatal(err)
4260 }
4261 ctx := context.WithValue(context.Background(), ctxKey, ctxValue)
4262 res, err := c.Do(req.WithContext(ctx))
4263 if err != nil {
4264 t.Fatal(err)
4265 }
4266 res.Body.Close()
4267 mu.Lock()
4268 if !gotReq {
4269 t.Error("didn't get request")
4270 }
4271 if got, want := gotCtxValue, ctxValue; got != want {
4272 t.Errorf("got context with value %v, want %v", got, want)
4273 }
4274 }
4275
4276
4277
4278 func TestRoundTripReturnsProxyError(t *testing.T) {
4279 badProxy := func(*Request) (*url.URL, error) {
4280 return nil, errors.New("errorMessage")
4281 }
4282
4283 tr := &Transport{Proxy: badProxy}
4284
4285 req, _ := NewRequest("GET", "http://example.com", nil)
4286
4287 _, err := tr.RoundTrip(req)
4288
4289 if err == nil {
4290 t.Error("Expected proxy error to be returned by RoundTrip")
4291 }
4292 }
4293
4294
4295 func TestTransportCloseIdleConnsThenReturn(t *testing.T) {
4296 tr := &Transport{}
4297 wantIdle := func(when string, n int) bool {
4298 got := tr.IdleConnCountForTesting("http", "example.com")
4299 if got == n {
4300 return true
4301 }
4302 t.Errorf("%s: idle conns = %d; want %d", when, got, n)
4303 return false
4304 }
4305 wantIdle("start", 0)
4306 if !tr.PutIdleTestConn("http", "example.com") {
4307 t.Fatal("put failed")
4308 }
4309 if !tr.PutIdleTestConn("http", "example.com") {
4310 t.Fatal("second put failed")
4311 }
4312 wantIdle("after put", 2)
4313 tr.CloseIdleConnections()
4314 if !tr.IsIdleForTesting() {
4315 t.Error("should be idle after CloseIdleConnections")
4316 }
4317 wantIdle("after close idle", 0)
4318 if tr.PutIdleTestConn("http", "example.com") {
4319 t.Fatal("put didn't fail")
4320 }
4321 wantIdle("after second put", 0)
4322
4323 tr.QueueForIdleConnForTesting()
4324 if tr.IsIdleForTesting() {
4325 t.Error("shouldn't be idle after QueueForIdleConnForTesting")
4326 }
4327 if !tr.PutIdleTestConn("http", "example.com") {
4328 t.Fatal("after re-activation")
4329 }
4330 wantIdle("after final put", 1)
4331 }
4332
4333
4334
4335 func TestTransportTraceGotConnH2IdleConns(t *testing.T) {
4336 tr := &Transport{}
4337 wantIdle := func(when string, n int) bool {
4338 got := tr.IdleConnCountForTesting("https", "example.com:443")
4339 if got == n {
4340 return true
4341 }
4342 t.Errorf("%s: idle conns = %d; want %d", when, got, n)
4343 return false
4344 }
4345 wantIdle("start", 0)
4346 alt := funcRoundTripper(func() {})
4347 if !tr.PutIdleTestConnH2("https", "example.com:443", alt) {
4348 t.Fatal("put failed")
4349 }
4350 wantIdle("after put", 1)
4351 ctx := httptrace.WithClientTrace(context.Background(), &httptrace.ClientTrace{
4352 GotConn: func(httptrace.GotConnInfo) {
4353
4354 t.Error("GotConn called")
4355 },
4356 })
4357 req, _ := NewRequestWithContext(ctx, MethodGet, "https://example.com", nil)
4358 _, err := tr.RoundTrip(req)
4359 if err != errFakeRoundTrip {
4360 t.Errorf("got error: %v; want %q", err, errFakeRoundTrip)
4361 }
4362 wantIdle("after round trip", 1)
4363 }
4364
4365
4366
4367
4368
4369
4370 func TestTransportIdleConnRacesRequest(t *testing.T) {
4371
4372
4373 runSynctest(t, testTransportIdleConnRacesRequest, []testMode{http1Mode, http2UnencryptedMode})
4374 }
4375 func testTransportIdleConnRacesRequest(t *testing.T, mode testMode) {
4376 timeout := 1 * time.Millisecond
4377 trFunc := func(tr *Transport) {
4378 tr.IdleConnTimeout = timeout
4379 }
4380 cst := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
4381 }), trFunc)
4382
4383 dialsBlocked := make(chan struct{})
4384 closeBlocked := make(chan struct{})
4385
4386 dialContext := cst.tr.DialContext
4387 cst.tr.DialContext = func(ctx context.Context, network, address string) (net.Conn, error) {
4388 <-dialsBlocked
4389 c, err := dialContext(ctx, network, address)
4390 if err != nil {
4391 return nil, err
4392 }
4393 return &blockCloseConn{closeBlocked, c}, nil
4394 }
4395
4396
4397
4398
4399
4400 ctx, cancel := context.WithCancel(t.Context())
4401 go func() {
4402 req, _ := NewRequestWithContext(ctx, "GET", cst.ts.URL, nil)
4403 resp, err := cst.c.Do(req)
4404 if err == nil {
4405 t.Errorf("expected request to fail, but it succeeded")
4406 resp.Body.Close()
4407 }
4408 }()
4409
4410 synctest.Wait()
4411
4412 cancel()
4413 synctest.Wait()
4414
4415 close(dialsBlocked)
4416
4417
4418
4419
4420
4421
4422
4423
4424 synctest.Wait()
4425 synctest.Sleep(timeout)
4426
4427 go func() {
4428 resp, err := cst.c.Get(cst.ts.URL)
4429 if err != nil {
4430 t.Errorf("expected second request to succeed, but it failed: %v", err)
4431 return
4432 }
4433 resp.Body.Close()
4434 }()
4435
4436
4437 close(closeBlocked)
4438 synctest.Wait()
4439 }
4440
4441 type blockCloseConn struct {
4442 closeBlocked chan struct{}
4443 net.Conn
4444 }
4445
4446 func (c *blockCloseConn) Close() error {
4447 <-c.closeBlocked
4448 return c.Conn.Close()
4449 }
4450
4451 func TestTransportRemovesConnsAfterIdle(t *testing.T) {
4452 runSynctest(t, testTransportRemovesConnsAfterIdle, http3SkippedMode)
4453 }
4454 func testTransportRemovesConnsAfterIdle(t *testing.T, mode testMode) {
4455 if testing.Short() {
4456 t.Skip("skipping in short mode")
4457 }
4458
4459 timeout := 1 * time.Second
4460 trFunc := func(tr *Transport) {
4461 tr.MaxConnsPerHost = 1
4462 tr.MaxIdleConnsPerHost = 1
4463 tr.IdleConnTimeout = timeout
4464 }
4465 cst := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
4466 w.Header().Set("X-Addr", r.RemoteAddr)
4467 }), trFunc)
4468
4469
4470
4471 makeRequest := func() string {
4472 resp, err := cst.c.Get(cst.ts.URL)
4473 if err != nil {
4474 t.Fatalf("got error: %s", err)
4475 }
4476 resp.Body.Close()
4477 return resp.Header.Get("X-Addr")
4478 }
4479
4480 addr1 := makeRequest()
4481
4482 time.Sleep(timeout / 2)
4483 synctest.Wait()
4484 addr2 := makeRequest()
4485 if addr1 != addr2 {
4486 t.Fatalf("two requests made within IdleConnTimeout should have used the same conn, but used %v, %v", addr1, addr2)
4487 }
4488
4489 time.Sleep(timeout)
4490 synctest.Wait()
4491 addr3 := makeRequest()
4492 if addr1 == addr3 {
4493 t.Fatalf("two requests made more than IdleConnTimeout apart should have used different conns, but used %v, %v", addr1, addr3)
4494 }
4495 }
4496
4497 func TestTransportRemovesConnsAfterBroken(t *testing.T) {
4498 runSynctest(t, testTransportRemovesConnsAfterBroken, http3SkippedMode)
4499 }
4500 func testTransportRemovesConnsAfterBroken(t *testing.T, mode testMode) {
4501 if testing.Short() {
4502 t.Skip("skipping in short mode")
4503 }
4504
4505 trFunc := func(tr *Transport) {
4506 tr.MaxConnsPerHost = 1
4507 tr.MaxIdleConnsPerHost = 1
4508 }
4509 cst := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
4510 w.Header().Set("X-Addr", r.RemoteAddr)
4511 }), trFunc)
4512
4513 var conns []*nettest.Conn
4514 cst.setDialNettestHook(func(c *nettest.Conn) {
4515 conns = append(conns, c)
4516 })
4517
4518
4519
4520 makeRequest := func() string {
4521 resp, err := cst.c.Get(cst.ts.URL)
4522 if err != nil {
4523 t.Fatalf("got error: %s", err)
4524 }
4525 resp.Body.Close()
4526 return resp.Header.Get("X-Addr")
4527 }
4528
4529 addr1 := makeRequest()
4530 addr2 := makeRequest()
4531 if addr1 != addr2 {
4532 t.Fatalf("successive requests should have used the same conn, but used %v, %v", addr1, addr2)
4533 }
4534
4535
4536 synctest.Wait()
4537 conns[0].Peer().Close()
4538 synctest.Wait()
4539 addr3 := makeRequest()
4540 if addr1 == addr3 {
4541 t.Fatalf("successive requests made with conn broken between should have used different conns, but used %v, %v", addr1, addr3)
4542 }
4543 }
4544
4545
4546
4547
4548
4549 func TestTransportRangeAndGzip(t *testing.T) { run(t, testTransportRangeAndGzip) }
4550 func testTransportRangeAndGzip(t *testing.T, mode testMode) {
4551 ts := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
4552 if strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
4553 t.Error("Transport advertised gzip support in the Accept header")
4554 }
4555 if r.Header.Get("Range") == "" {
4556 t.Error("no Range in request")
4557 }
4558 })).ts
4559 c := ts.Client()
4560
4561 req, _ := NewRequest("GET", ts.URL, nil)
4562 req.Header.Set("Range", "bytes=7-11")
4563 res, err := c.Do(req)
4564 if err != nil {
4565 t.Fatal(err)
4566 }
4567 res.Body.Close()
4568 }
4569
4570
4571 func TestTransportResponseCancelRace(t *testing.T) { run(t, testTransportResponseCancelRace) }
4572 func testTransportResponseCancelRace(t *testing.T, mode testMode) {
4573 ts := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
4574
4575 var b [1024]byte
4576 w.Write(b[:])
4577 })).ts
4578 tr := ts.Client().Transport.(*Transport)
4579
4580 ctx, cancel := context.WithCancel(t.Context())
4581 req, err := NewRequestWithContext(ctx, "GET", ts.URL, nil)
4582 if err != nil {
4583 t.Fatal(err)
4584 }
4585 res, err := tr.RoundTrip(req)
4586 if err != nil {
4587 t.Fatal(err)
4588 }
4589
4590
4591
4592 if _, err := io.Copy(io.Discard, res.Body); err != nil {
4593 t.Fatal(err)
4594 }
4595
4596 req2, err := NewRequest("GET", ts.URL, nil)
4597 if err != nil {
4598 t.Fatal(err)
4599 }
4600 cancel()
4601 res, err = tr.RoundTrip(req2)
4602 if err != nil {
4603 t.Fatal(err)
4604 }
4605 res.Body.Close()
4606 }
4607
4608
4609 func TestTransportContentEncodingCaseInsensitive(t *testing.T) {
4610 run(t, testTransportContentEncodingCaseInsensitive)
4611 }
4612 func testTransportContentEncodingCaseInsensitive(t *testing.T, mode testMode) {
4613 for _, ce := range []string{"gzip", "GZIP"} {
4614 t.Run(ce, func(t *testing.T) {
4615 const encodedString = "Hello Gopher"
4616 ts := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
4617 w.Header().Set("Content-Encoding", ce)
4618 gz := gzip.NewWriter(w)
4619 gz.Write([]byte(encodedString))
4620 gz.Close()
4621 })).ts
4622
4623 res, err := ts.Client().Get(ts.URL)
4624 if err != nil {
4625 t.Fatal(err)
4626 }
4627
4628 body, err := io.ReadAll(res.Body)
4629 res.Body.Close()
4630 if err != nil {
4631 t.Fatal(err)
4632 }
4633
4634 if string(body) != encodedString {
4635 t.Fatalf("Expected body %q, got: %q\n", encodedString, string(body))
4636 }
4637 })
4638 }
4639 }
4640
4641
4642 func TestConnClosedBeforeRequestIsWritten(t *testing.T) {
4643 run(t, testConnClosedBeforeRequestIsWritten, testNotParallel, []testMode{http1Mode})
4644 }
4645 func testConnClosedBeforeRequestIsWritten(t *testing.T, mode testMode) {
4646 ts := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {}),
4647 func(tr *Transport) {
4648 tr.DialContext = func(_ context.Context, network, addr string) (net.Conn, error) {
4649
4650 return &funcConn{
4651 read: func([]byte) (int, error) {
4652 return 0, errors.New("error")
4653 },
4654 write: func([]byte) (int, error) {
4655 return 0, errors.New("error")
4656 },
4657 }, nil
4658 }
4659 },
4660 optRealNet,
4661 ).ts
4662
4663
4664
4665
4666
4667 SetEnterRoundTripHook(func() {
4668 time.Sleep(1 * time.Millisecond)
4669 })
4670 defer SetEnterRoundTripHook(nil)
4671 var closes int
4672 _, err := ts.Client().Post(ts.URL, "text/plain", countCloseReader{&closes, strings.NewReader("hello")})
4673 if err == nil {
4674 t.Fatalf("expected request to fail, but it did not")
4675 }
4676 if closes != 1 {
4677 t.Errorf("after RoundTrip, request body was closed %v times; want 1", closes)
4678 }
4679 }
4680
4681
4682
4683
4684 type logWritesConn struct {
4685 net.Conn
4686
4687 w io.Writer
4688
4689 rch <-chan io.Reader
4690 r io.Reader
4691
4692 mu sync.Mutex
4693 writes []string
4694 }
4695
4696 func (c *logWritesConn) Write(p []byte) (n int, err error) {
4697 c.mu.Lock()
4698 defer c.mu.Unlock()
4699 c.writes = append(c.writes, string(p))
4700 return c.w.Write(p)
4701 }
4702
4703 func (c *logWritesConn) Read(p []byte) (n int, err error) {
4704 if c.r == nil {
4705 c.r = <-c.rch
4706 }
4707 return c.r.Read(p)
4708 }
4709
4710 func (c *logWritesConn) Close() error { return nil }
4711
4712
4713 func TestTransportFlushesBodyChunks(t *testing.T) {
4714 defer afterTest(t)
4715 resBody := make(chan io.Reader, 1)
4716 connr, connw := io.Pipe()
4717 lw := &logWritesConn{
4718 rch: resBody,
4719 w: connw,
4720 }
4721 tr := &Transport{
4722 Dial: func(network, addr string) (net.Conn, error) {
4723 return lw, nil
4724 },
4725 }
4726 bodyr, bodyw := io.Pipe()
4727 go func() {
4728 defer bodyw.Close()
4729 for i := 0; i < 3; i++ {
4730 fmt.Fprintf(bodyw, "num%d\n", i)
4731 }
4732 }()
4733 resc := make(chan *Response)
4734 go func() {
4735 req, _ := NewRequest("POST", "http://localhost:8080", bodyr)
4736 req.Header.Set("User-Agent", "x")
4737 res, err := tr.RoundTrip(req)
4738 if err != nil {
4739 t.Errorf("RoundTrip: %v", err)
4740 close(resc)
4741 return
4742 }
4743 resc <- res
4744
4745 }()
4746
4747 req, err := ReadRequest(bufio.NewReader(connr))
4748 if err != nil {
4749 t.Fatal(err)
4750 }
4751 io.Copy(io.Discard, req.Body)
4752
4753
4754 resBody <- strings.NewReader("HTTP/1.1 204 No Content\r\nConnection: close\r\n\r\n")
4755 res, ok := <-resc
4756 if !ok {
4757 return
4758 }
4759 defer res.Body.Close()
4760
4761 want := []string{
4762 "POST / HTTP/1.1\r\nHost: localhost:8080\r\nUser-Agent: x\r\nTransfer-Encoding: chunked\r\nAccept-Encoding: gzip\r\n\r\n",
4763 "5\r\nnum0\n\r\n",
4764 "5\r\nnum1\n\r\n",
4765 "5\r\nnum2\n\r\n",
4766 "0\r\n\r\n",
4767 }
4768 if !slices.Equal(lw.writes, want) {
4769 t.Errorf("Writes differed.\n Got: %q\nWant: %q\n", lw.writes, want)
4770 }
4771 }
4772
4773
4774 func TestTransportFlushesRequestHeader(t *testing.T) { run(t, testTransportFlushesRequestHeader) }
4775 func testTransportFlushesRequestHeader(t *testing.T, mode testMode) {
4776 gotReq := make(chan struct{})
4777 cst := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
4778 close(gotReq)
4779 }))
4780
4781 pr, pw := io.Pipe()
4782 req, err := NewRequest("POST", cst.ts.URL, pr)
4783 if err != nil {
4784 t.Fatal(err)
4785 }
4786 gotRes := make(chan struct{})
4787 go func() {
4788 defer close(gotRes)
4789 res, err := cst.tr.RoundTrip(req)
4790 if err != nil {
4791 t.Error(err)
4792 return
4793 }
4794 res.Body.Close()
4795 }()
4796
4797 <-gotReq
4798 pw.Close()
4799 <-gotRes
4800 }
4801
4802 type wgReadCloser struct {
4803 io.Reader
4804 wg *sync.WaitGroup
4805 closed bool
4806 }
4807
4808 func (c *wgReadCloser) Close() error {
4809 if c.closed {
4810 return net.ErrClosed
4811 }
4812 c.closed = true
4813 c.wg.Done()
4814 return nil
4815 }
4816
4817
4818 func TestTransportPrefersResponseOverWriteError(t *testing.T) {
4819
4820 run(t, testTransportPrefersResponseOverWriteError, testNotParallel)
4821 }
4822 func testTransportPrefersResponseOverWriteError(t *testing.T, mode testMode) {
4823 if testing.Short() {
4824 t.Skip("skipping in short mode")
4825 }
4826
4827 runTimeSensitiveTest(t, []time.Duration{
4828 1 * time.Millisecond,
4829 5 * time.Millisecond,
4830 10 * time.Millisecond,
4831 50 * time.Millisecond,
4832 100 * time.Millisecond,
4833 500 * time.Millisecond,
4834 time.Second,
4835 5 * time.Second,
4836 }, func(t *testing.T, timeout time.Duration) error {
4837 SetRSTAvoidanceDelay(t, timeout)
4838 t.Logf("set RST avoidance delay to %v", timeout)
4839
4840 const contentLengthLimit = 1024 * 1024
4841 cst := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
4842 if r.ContentLength >= contentLengthLimit {
4843 w.WriteHeader(StatusBadRequest)
4844 r.Body.Close()
4845 return
4846 }
4847 w.WriteHeader(StatusOK)
4848 }))
4849
4850
4851 defer cst.close()
4852 ts := cst.ts
4853 c := ts.Client()
4854
4855 count := 100
4856
4857 bigBody := strings.Repeat("a", contentLengthLimit*2)
4858 var wg sync.WaitGroup
4859 defer wg.Wait()
4860 getBody := func() (io.ReadCloser, error) {
4861 wg.Add(1)
4862 body := &wgReadCloser{
4863 Reader: strings.NewReader(bigBody),
4864 wg: &wg,
4865 }
4866 return body, nil
4867 }
4868
4869 for i := 0; i < count; i++ {
4870 reqBody, _ := getBody()
4871 req, err := NewRequest("PUT", ts.URL, reqBody)
4872 if err != nil {
4873 reqBody.Close()
4874 t.Fatal(err)
4875 }
4876 req.ContentLength = int64(len(bigBody))
4877 req.GetBody = getBody
4878
4879 resp, err := c.Do(req)
4880 if err != nil {
4881 return fmt.Errorf("Do %d: %v", i, err)
4882 } else {
4883 resp.Body.Close()
4884 if resp.StatusCode != 400 {
4885 t.Errorf("Expected status code 400, got %v", resp.Status)
4886 }
4887 }
4888 }
4889 return nil
4890 })
4891 }
4892
4893 func TestTransportAutomaticHTTP2(t *testing.T) {
4894 testTransportAutoHTTP(t, &Transport{}, true)
4895 }
4896
4897 func TestTransportAutomaticHTTP2_DialerAndTLSConfigSupportsHTTP2AndTLSConfig(t *testing.T) {
4898 testTransportAutoHTTP(t, &Transport{
4899 ForceAttemptHTTP2: true,
4900 TLSClientConfig: new(tls.Config),
4901 }, true)
4902 }
4903
4904
4905 func TestTransportAutomaticHTTP2_DefaultTransport(t *testing.T) {
4906 testTransportAutoHTTP(t, DefaultTransport.(*Transport), true)
4907 }
4908
4909 func TestTransportAutomaticHTTP2_TLSNextProto(t *testing.T) {
4910 testTransportAutoHTTP(t, &Transport{
4911 TLSNextProto: make(map[string]func(string, *tls.Conn) RoundTripper),
4912 }, false)
4913 }
4914
4915 func TestTransportAutomaticHTTP2_TLSConfig(t *testing.T) {
4916 testTransportAutoHTTP(t, &Transport{
4917 TLSClientConfig: new(tls.Config),
4918 }, false)
4919 }
4920
4921 func TestTransportAutomaticHTTP2_ExpectContinueTimeout(t *testing.T) {
4922 testTransportAutoHTTP(t, &Transport{
4923 ExpectContinueTimeout: 1 * time.Second,
4924 }, true)
4925 }
4926
4927 func TestTransportAutomaticHTTP2_Dial(t *testing.T) {
4928 var d net.Dialer
4929 testTransportAutoHTTP(t, &Transport{
4930 Dial: d.Dial,
4931 }, false)
4932 }
4933
4934 func TestTransportAutomaticHTTP2_DialContext(t *testing.T) {
4935 var d net.Dialer
4936 testTransportAutoHTTP(t, &Transport{
4937 DialContext: d.DialContext,
4938 }, false)
4939 }
4940
4941 func TestTransportAutomaticHTTP2_DialTLS(t *testing.T) {
4942 testTransportAutoHTTP(t, &Transport{
4943 DialTLS: func(network, addr string) (net.Conn, error) {
4944 panic("unused")
4945 },
4946 }, false)
4947 }
4948
4949 func testTransportAutoHTTP(t *testing.T, tr *Transport, wantH2 bool) {
4950 CondSkipHTTP2(t)
4951 _, err := tr.RoundTrip(new(Request))
4952 if err == nil {
4953 t.Error("expected error from RoundTrip")
4954 }
4955 if reg := tr.TLSNextProto["h2"] != nil; reg != wantH2 {
4956 t.Errorf("HTTP/2 registered = %v; want %v", reg, wantH2)
4957 }
4958 }
4959
4960
4961
4962
4963
4964
4965
4966
4967 func TestTransportReuseConnEmptyResponseBody(t *testing.T) {
4968 run(t, testTransportReuseConnEmptyResponseBody)
4969 }
4970 func testTransportReuseConnEmptyResponseBody(t *testing.T, mode testMode) {
4971 cst := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
4972 w.Header().Set("X-Addr", r.RemoteAddr)
4973
4974 }))
4975 n := 100
4976 if testing.Short() {
4977 n = 10
4978 }
4979 var firstAddr string
4980 for i := 0; i < n; i++ {
4981 res, err := cst.c.Get(cst.ts.URL)
4982 if err != nil {
4983 log.Fatal(err)
4984 }
4985 addr := res.Header.Get("X-Addr")
4986 if i == 0 {
4987 firstAddr = addr
4988 } else if addr != firstAddr {
4989 t.Fatalf("On request %d, addr %q != original addr %q", i+1, addr, firstAddr)
4990 }
4991 res.Body.Close()
4992 }
4993 }
4994
4995
4996 func TestNoCrashReturningTransportAltConn(t *testing.T) {
4997 cert, err := tls.X509KeyPair(testcert.LocalhostCert, testcert.LocalhostKey)
4998 if err != nil {
4999 t.Fatal(err)
5000 }
5001 ln := newLocalListener(t)
5002 defer ln.Close()
5003
5004 var wg sync.WaitGroup
5005 SetPendingDialHooks(func() { wg.Add(1) }, wg.Done)
5006 defer SetPendingDialHooks(nil, nil)
5007
5008 testDone := make(chan struct{})
5009 defer close(testDone)
5010 go func() {
5011 tln := tls.NewListener(ln, &tls.Config{
5012 NextProtos: []string{"foo"},
5013 Certificates: []tls.Certificate{cert},
5014 })
5015 sc, err := tln.Accept()
5016 if err != nil {
5017 t.Error(err)
5018 return
5019 }
5020 if err := sc.(*tls.Conn).Handshake(); err != nil {
5021 t.Error(err)
5022 return
5023 }
5024 <-testDone
5025 sc.Close()
5026 }()
5027
5028 addr := ln.Addr().String()
5029
5030 req, _ := NewRequest("GET", "https://fake.tld/", nil)
5031 cancel := make(chan struct{})
5032 req.Cancel = cancel
5033
5034 doReturned := make(chan bool, 1)
5035 madeRoundTripper := make(chan bool, 1)
5036
5037 tr := &Transport{
5038 DisableKeepAlives: true,
5039 TLSNextProto: map[string]func(string, *tls.Conn) RoundTripper{
5040 "foo": func(authority string, c *tls.Conn) RoundTripper {
5041 madeRoundTripper <- true
5042 return funcRoundTripper(func() {
5043 t.Error("foo RoundTripper should not be called")
5044 })
5045 },
5046 },
5047 Dial: func(_, _ string) (net.Conn, error) {
5048 panic("shouldn't be called")
5049 },
5050 DialTLS: func(_, _ string) (net.Conn, error) {
5051 tc, err := tls.Dial("tcp", addr, &tls.Config{
5052 InsecureSkipVerify: true,
5053 NextProtos: []string{"foo"},
5054 })
5055 if err != nil {
5056 return nil, err
5057 }
5058 if err := tc.Handshake(); err != nil {
5059 return nil, err
5060 }
5061 close(cancel)
5062 <-doReturned
5063 return tc, nil
5064 },
5065 }
5066 c := &Client{Transport: tr}
5067
5068 _, err = c.Do(req)
5069 if ue, ok := err.(*url.Error); !ok || ue.Err != ExportErrRequestCanceledConn {
5070 t.Fatalf("Do error = %v; want url.Error with errRequestCanceledConn", err)
5071 }
5072
5073 doReturned <- true
5074 <-madeRoundTripper
5075 wg.Wait()
5076 }
5077
5078 func TestTransportReuseConnection_Gzip_Chunked(t *testing.T) {
5079 run(t, func(t *testing.T, mode testMode) {
5080 testTransportReuseConnection_Gzip(t, mode, true)
5081 })
5082 }
5083
5084 func TestTransportReuseConnection_Gzip_ContentLength(t *testing.T) {
5085 run(t, func(t *testing.T, mode testMode) {
5086 testTransportReuseConnection_Gzip(t, mode, false)
5087 })
5088 }
5089
5090
5091 func testTransportReuseConnection_Gzip(t *testing.T, mode testMode, chunked bool) {
5092 addr := make(chan string, 2)
5093 ts := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
5094 addr <- r.RemoteAddr
5095 w.Header().Set("Content-Encoding", "gzip")
5096 if chunked {
5097 w.(Flusher).Flush()
5098 }
5099 w.Write(rgz)
5100 })).ts
5101 c := ts.Client()
5102
5103 trace := &httptrace.ClientTrace{
5104 GetConn: func(hostPort string) { t.Logf("GetConn(%q)", hostPort) },
5105 GotConn: func(ci httptrace.GotConnInfo) { t.Logf("GotConn(%+v)", ci) },
5106 PutIdleConn: func(err error) { t.Logf("PutIdleConn(%v)", err) },
5107 ConnectStart: func(network, addr string) { t.Logf("ConnectStart(%q, %q)", network, addr) },
5108 ConnectDone: func(network, addr string, err error) { t.Logf("ConnectDone(%q, %q, %v)", network, addr, err) },
5109 }
5110 ctx := httptrace.WithClientTrace(context.Background(), trace)
5111
5112 for i := 0; i < 2; i++ {
5113 req, _ := NewRequest("GET", ts.URL, nil)
5114 req = req.WithContext(ctx)
5115 res, err := c.Do(req)
5116 if err != nil {
5117 t.Fatal(err)
5118 }
5119 buf := make([]byte, len(rgz))
5120 if n, err := io.ReadFull(res.Body, buf); err != nil {
5121 t.Errorf("%d. ReadFull = %v, %v", i, n, err)
5122 }
5123
5124
5125
5126 }
5127 a1, a2 := <-addr, <-addr
5128 if a1 != a2 {
5129 t.Fatalf("didn't reuse connection")
5130 }
5131 }
5132
5133 func TestTransportResponseHeaderLength(t *testing.T) {
5134 run(t, testTransportResponseHeaderLength, http3SkippedMode)
5135 }
5136 func testTransportResponseHeaderLength(t *testing.T, mode testMode) {
5137 if mode == http2Mode {
5138 t.Skip("HTTP/2 Transport doesn't support MaxResponseHeaderBytes")
5139 }
5140 ts := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
5141 if r.URL.Path == "/long" {
5142 w.Header().Set("Long", strings.Repeat("a", 1<<20))
5143 }
5144 })).ts
5145 c := ts.Client()
5146 c.Transport.(*Transport).MaxResponseHeaderBytes = 512 << 10
5147
5148 if res, err := c.Get(ts.URL); err != nil {
5149 t.Fatal(err)
5150 } else {
5151 res.Body.Close()
5152 }
5153
5154 res, err := c.Get(ts.URL + "/long")
5155 if err == nil {
5156 defer res.Body.Close()
5157 var n int64
5158 for k, vv := range res.Header {
5159 for _, v := range vv {
5160 n += int64(len(k)) + int64(len(v))
5161 }
5162 }
5163 t.Fatalf("Unexpected success. Got %v and %d bytes of response headers", res.Status, n)
5164 }
5165 if want := "server response headers exceeded 524288 bytes"; !strings.Contains(err.Error(), want) {
5166 t.Errorf("got error: %v; want %q", err, want)
5167 }
5168 }
5169
5170 func TestTransportEventTrace(t *testing.T) {
5171 run(t, func(t *testing.T, mode testMode) {
5172 testTransportEventTrace(t, mode, false)
5173 }, testNotParallel, http3SkippedMode)
5174 }
5175
5176
5177 func TestTransportEventTrace_NoHooks(t *testing.T) {
5178 run(t, func(t *testing.T, mode testMode) {
5179 testTransportEventTrace(t, mode, true)
5180 }, testNotParallel, http3SkippedMode)
5181 }
5182
5183 func testTransportEventTrace(t *testing.T, mode testMode, noHooks bool) {
5184 const resBody = "some body"
5185 gotWroteReqEvent := make(chan struct{}, 500)
5186 cst := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
5187 if r.Method == "GET" {
5188
5189 return
5190 }
5191 if _, err := io.ReadAll(r.Body); err != nil {
5192 t.Error(err)
5193 }
5194 if !noHooks {
5195 <-gotWroteReqEvent
5196 }
5197 io.WriteString(w, resBody)
5198 }), func(tr *Transport) {
5199 if tr.TLSClientConfig != nil {
5200 tr.TLSClientConfig.InsecureSkipVerify = true
5201 }
5202 }, optRealNet)
5203 defer cst.close()
5204
5205 cst.tr.ExpectContinueTimeout = 1 * time.Second
5206
5207 var mu sync.Mutex
5208 var buf strings.Builder
5209 logf := func(format string, args ...any) {
5210 mu.Lock()
5211 defer mu.Unlock()
5212 fmt.Fprintf(&buf, format, args...)
5213 buf.WriteByte('\n')
5214 }
5215
5216 addrStr := cst.ts.Listener.Addr().String()
5217 ip, port, err := net.SplitHostPort(addrStr)
5218 if err != nil {
5219 t.Fatal(err)
5220 }
5221
5222
5223 ctx := context.WithValue(context.Background(), nettrace.LookupIPAltResolverKey{}, func(ctx context.Context, network, host string) ([]net.IPAddr, error) {
5224 if host != "dns-is-faked.golang" {
5225 t.Errorf("unexpected DNS host lookup for %q/%q", network, host)
5226 return nil, nil
5227 }
5228 return []net.IPAddr{{IP: net.ParseIP(ip)}}, nil
5229 })
5230
5231 body := "some body"
5232 req, _ := NewRequest("POST", cst.scheme()+"://dns-is-faked.golang:"+port, strings.NewReader(body))
5233 req.Header["X-Foo-Multiple-Vals"] = []string{"bar", "baz"}
5234 trace := &httptrace.ClientTrace{
5235 GetConn: func(hostPort string) { logf("Getting conn for %v ...", hostPort) },
5236 GotConn: func(ci httptrace.GotConnInfo) { logf("got conn: %+v", ci) },
5237 GotFirstResponseByte: func() { logf("first response byte") },
5238 PutIdleConn: func(err error) { logf("PutIdleConn = %v", err) },
5239 DNSStart: func(e httptrace.DNSStartInfo) { logf("DNS start: %+v", e) },
5240 DNSDone: func(e httptrace.DNSDoneInfo) { logf("DNS done: %+v", e) },
5241 ConnectStart: func(network, addr string) { logf("ConnectStart: Connecting to %s %s ...", network, addr) },
5242 ConnectDone: func(network, addr string, err error) {
5243 if err != nil {
5244 t.Errorf("ConnectDone: %v", err)
5245 }
5246 logf("ConnectDone: connected to %s %s = %v", network, addr, err)
5247 },
5248 WroteHeaderField: func(key string, value []string) {
5249 logf("WroteHeaderField: %s: %v", key, value)
5250 },
5251 WroteHeaders: func() {
5252 logf("WroteHeaders")
5253 },
5254 Wait100Continue: func() { logf("Wait100Continue") },
5255 Got100Continue: func() { logf("Got100Continue") },
5256 WroteRequest: func(e httptrace.WroteRequestInfo) {
5257 logf("WroteRequest: %+v", e)
5258 gotWroteReqEvent <- struct{}{}
5259 },
5260 }
5261 if mode == http2Mode {
5262 trace.TLSHandshakeStart = func() { logf("tls handshake start") }
5263 trace.TLSHandshakeDone = func(s tls.ConnectionState, err error) {
5264 logf("tls handshake done. ConnectionState = %v \n err = %v", s, err)
5265 }
5266 }
5267 if noHooks {
5268
5269 *trace = httptrace.ClientTrace{}
5270 }
5271 req = req.WithContext(httptrace.WithClientTrace(ctx, trace))
5272
5273 req.Header.Set("Expect", "100-continue")
5274 res, err := cst.c.Do(req)
5275 if err != nil {
5276 t.Fatal(err)
5277 }
5278 logf("got roundtrip.response")
5279 slurp, err := io.ReadAll(res.Body)
5280 if err != nil {
5281 t.Fatal(err)
5282 }
5283 logf("consumed body")
5284 if string(slurp) != resBody || res.StatusCode != 200 {
5285 t.Fatalf("Got %q, %v; want %q, 200 OK", slurp, res.Status, resBody)
5286 }
5287 res.Body.Close()
5288
5289 if noHooks {
5290
5291
5292
5293 return
5294 }
5295
5296 mu.Lock()
5297 got := buf.String()
5298 mu.Unlock()
5299
5300 wantOnce := func(sub string) {
5301 if strings.Count(got, sub) != 1 {
5302 t.Errorf("expected substring %q exactly once in output.", sub)
5303 }
5304 }
5305 wantOnceOrMore := func(sub string) {
5306 if strings.Count(got, sub) == 0 {
5307 t.Errorf("expected substring %q at least once in output.", sub)
5308 }
5309 }
5310 wantOnce("Getting conn for dns-is-faked.golang:" + port)
5311 wantOnce("DNS start: {Host:dns-is-faked.golang}")
5312 wantOnce("DNS done: {Addrs:[{IP:" + ip + " Zone:}] Err:<nil> Coalesced:false}")
5313 wantOnce("got conn: {")
5314 wantOnceOrMore("Connecting to tcp " + addrStr)
5315 wantOnceOrMore("connected to tcp " + addrStr + " = <nil>")
5316 wantOnce("Reused:false WasIdle:false IdleTime:0s")
5317 wantOnce("first response byte")
5318 if mode == http2Mode {
5319 wantOnce("tls handshake start")
5320 wantOnce("tls handshake done")
5321 } else {
5322 wantOnce("PutIdleConn = <nil>")
5323 wantOnce("WroteHeaderField: User-Agent: [Go-http-client/1.1]")
5324
5325
5326 wantOnce(fmt.Sprintf("WroteHeaderField: Host: [dns-is-faked.golang:%s]", port))
5327 wantOnce(fmt.Sprintf("WroteHeaderField: Content-Length: [%d]", len(body)))
5328 wantOnce("WroteHeaderField: X-Foo-Multiple-Vals: [bar baz]")
5329 wantOnce("WroteHeaderField: Accept-Encoding: [gzip]")
5330 }
5331 wantOnce("WroteHeaders")
5332 wantOnce("Wait100Continue")
5333 wantOnce("Got100Continue")
5334 wantOnce("WroteRequest: {Err:<nil>}")
5335 if strings.Contains(got, " to udp ") {
5336 t.Errorf("should not see UDP (DNS) connections")
5337 }
5338 if t.Failed() {
5339 t.Errorf("Output:\n%s", got)
5340 }
5341
5342
5343 req, _ = NewRequest("GET", cst.scheme()+"://dns-is-faked.golang:"+port, nil)
5344 req = req.WithContext(httptrace.WithClientTrace(ctx, trace))
5345 res, err = cst.c.Do(req)
5346 if err != nil {
5347 t.Fatal(err)
5348 }
5349 if res.StatusCode != 200 {
5350 t.Fatal(res.Status)
5351 }
5352 res.Body.Close()
5353
5354 mu.Lock()
5355 got = buf.String()
5356 mu.Unlock()
5357
5358 sub := "Getting conn for dns-is-faked.golang:"
5359 if gotn, want := strings.Count(got, sub), 2; gotn != want {
5360 t.Errorf("substring %q appeared %d times; want %d. Log:\n%s", sub, gotn, want, got)
5361 }
5362
5363 }
5364
5365 func TestTransportEventTraceTLSVerify(t *testing.T) {
5366 run(t, testTransportEventTraceTLSVerify, []testMode{https1Mode, http2Mode})
5367 }
5368 func testTransportEventTraceTLSVerify(t *testing.T, mode testMode) {
5369 var mu sync.Mutex
5370 var buf strings.Builder
5371 logf := func(format string, args ...any) {
5372 mu.Lock()
5373 defer mu.Unlock()
5374 fmt.Fprintf(&buf, format, args...)
5375 buf.WriteByte('\n')
5376 }
5377
5378 ts := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
5379 t.Error("Unexpected request")
5380 }), func(ts *httptest.Server) {
5381 ts.Config.ErrorLog = log.New(funcWriter(func(p []byte) (int, error) {
5382 logf("%s", p)
5383 return len(p), nil
5384 }), "", 0)
5385 }, optRealNet).ts
5386
5387 certpool := x509.NewCertPool()
5388 certpool.AddCert(ts.Certificate())
5389
5390 c := &Client{Transport: &Transport{
5391 TLSClientConfig: &tls.Config{
5392 ServerName: "dns-is-faked.golang",
5393 RootCAs: certpool,
5394 },
5395 }}
5396
5397 trace := &httptrace.ClientTrace{
5398 TLSHandshakeStart: func() { logf("TLSHandshakeStart") },
5399 TLSHandshakeDone: func(s tls.ConnectionState, err error) {
5400 logf("TLSHandshakeDone: ConnectionState = %v \n err = %v", s, err)
5401 },
5402 }
5403
5404 req, _ := NewRequest("GET", ts.URL, nil)
5405 req = req.WithContext(httptrace.WithClientTrace(context.Background(), trace))
5406 _, err := c.Do(req)
5407 if err == nil {
5408 t.Error("Expected request to fail TLS verification")
5409 }
5410
5411 mu.Lock()
5412 got := buf.String()
5413 mu.Unlock()
5414
5415 wantOnce := func(sub string) {
5416 if strings.Count(got, sub) != 1 {
5417 t.Errorf("expected substring %q exactly once in output.", sub)
5418 }
5419 }
5420
5421 wantOnce("TLSHandshakeStart")
5422 wantOnce("TLSHandshakeDone")
5423 wantOnce("err = tls: failed to verify certificate: x509: certificate is valid for example.com")
5424
5425 if t.Failed() {
5426 t.Errorf("Output:\n%s", got)
5427 }
5428 }
5429
5430 var isDNSHijacked = sync.OnceValue(func() bool {
5431 addrs, _ := net.LookupHost("dns-should-not-resolve.golang")
5432 return len(addrs) != 0
5433 })
5434
5435 func skipIfDNSHijacked(t *testing.T) {
5436
5437
5438
5439 if isDNSHijacked() {
5440 t.Skip("skipping; test requires non-hijacking DNS server")
5441 }
5442 }
5443
5444 func TestTransportEventTraceRealDNS(t *testing.T) {
5445 skipIfDNSHijacked(t)
5446 defer afterTest(t)
5447 tr := &Transport{}
5448 defer tr.CloseIdleConnections()
5449 c := &Client{Transport: tr}
5450
5451 var mu sync.Mutex
5452 var buf strings.Builder
5453 logf := func(format string, args ...any) {
5454 mu.Lock()
5455 defer mu.Unlock()
5456 fmt.Fprintf(&buf, format, args...)
5457 buf.WriteByte('\n')
5458 }
5459
5460 req, _ := NewRequest("GET", "http://dns-should-not-resolve.golang:80", nil)
5461 trace := &httptrace.ClientTrace{
5462 DNSStart: func(e httptrace.DNSStartInfo) { logf("DNSStart: %+v", e) },
5463 DNSDone: func(e httptrace.DNSDoneInfo) { logf("DNSDone: %+v", e) },
5464 ConnectStart: func(network, addr string) { logf("ConnectStart: %s %s", network, addr) },
5465 ConnectDone: func(network, addr string, err error) { logf("ConnectDone: %s %s %v", network, addr, err) },
5466 }
5467 req = req.WithContext(httptrace.WithClientTrace(context.Background(), trace))
5468
5469 resp, err := c.Do(req)
5470 if err == nil {
5471 resp.Body.Close()
5472 t.Fatal("expected error during DNS lookup")
5473 }
5474
5475 mu.Lock()
5476 got := buf.String()
5477 mu.Unlock()
5478
5479 wantSub := func(sub string) {
5480 if !strings.Contains(got, sub) {
5481 t.Errorf("expected substring %q in output.", sub)
5482 }
5483 }
5484 wantSub("DNSStart: {Host:dns-should-not-resolve.golang}")
5485 wantSub("DNSDone: {Addrs:[] Err:")
5486 if strings.Contains(got, "ConnectStart") || strings.Contains(got, "ConnectDone") {
5487 t.Errorf("should not see Connect events")
5488 }
5489 if t.Failed() {
5490 t.Errorf("Output:\n%s", got)
5491 }
5492 }
5493
5494
5495 func TestTransportRejectsAlphaPort(t *testing.T) {
5496 res, err := Get("http://dummy.tld:123foo/bar")
5497 if err == nil {
5498 res.Body.Close()
5499 t.Fatal("unexpected success")
5500 }
5501 ue, ok := err.(*url.Error)
5502 if !ok {
5503 t.Fatalf("got %#v; want *url.Error", err)
5504 }
5505 got := ue.Err.Error()
5506 want := `invalid port ":123foo" after host`
5507 if got != want {
5508 t.Errorf("got error %q; want %q", got, want)
5509 }
5510 }
5511
5512
5513
5514 func TestTLSHandshakeTrace(t *testing.T) {
5515 run(t, testTLSHandshakeTrace, []testMode{https1Mode, http2Mode})
5516 }
5517 func testTLSHandshakeTrace(t *testing.T, mode testMode) {
5518 ts := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {}), optRealNet).ts
5519
5520 var mu sync.Mutex
5521 var start, done bool
5522 trace := &httptrace.ClientTrace{
5523 TLSHandshakeStart: func() {
5524 mu.Lock()
5525 defer mu.Unlock()
5526 start = true
5527 },
5528 TLSHandshakeDone: func(s tls.ConnectionState, err error) {
5529 mu.Lock()
5530 defer mu.Unlock()
5531 done = true
5532 if err != nil {
5533 t.Fatal("Expected error to be nil but was:", err)
5534 }
5535 },
5536 }
5537
5538 c := ts.Client()
5539 req, err := NewRequest("GET", ts.URL, nil)
5540 if err != nil {
5541 t.Fatal("Unable to construct test request:", err)
5542 }
5543 req = req.WithContext(httptrace.WithClientTrace(req.Context(), trace))
5544
5545 r, err := c.Do(req)
5546 if err != nil {
5547 t.Fatal("Unexpected error making request:", err)
5548 }
5549 r.Body.Close()
5550 mu.Lock()
5551 defer mu.Unlock()
5552 if !start {
5553 t.Fatal("Expected TLSHandshakeStart to be called, but wasn't")
5554 }
5555 if !done {
5556 t.Fatal("Expected TLSHandshakeDone to be called, but wasn't")
5557 }
5558 }
5559
5560 func TestTransportMaxIdleConns(t *testing.T) {
5561 run(t, testTransportMaxIdleConns, []testMode{http1Mode})
5562 }
5563 func testTransportMaxIdleConns(t *testing.T, mode testMode) {
5564 ts := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
5565
5566 }), optRealNet).ts
5567 c := ts.Client()
5568 tr := c.Transport.(*Transport)
5569 tr.MaxIdleConns = 4
5570
5571 ip, port, err := net.SplitHostPort(ts.Listener.Addr().String())
5572 if err != nil {
5573 t.Fatal(err)
5574 }
5575 ctx := context.WithValue(context.Background(), nettrace.LookupIPAltResolverKey{}, func(ctx context.Context, _, host string) ([]net.IPAddr, error) {
5576 return []net.IPAddr{{IP: net.ParseIP(ip)}}, nil
5577 })
5578
5579 hitHost := func(n int) {
5580 req, _ := NewRequest("GET", fmt.Sprintf("http://host-%d.dns-is-faked.golang:"+port, n), nil)
5581 req = req.WithContext(ctx)
5582 res, err := c.Do(req)
5583 if err != nil {
5584 t.Fatal(err)
5585 }
5586 res.Body.Close()
5587 }
5588 for i := 0; i < 4; i++ {
5589 hitHost(i)
5590 }
5591 want := []string{
5592 "|http|host-0.dns-is-faked.golang:" + port,
5593 "|http|host-1.dns-is-faked.golang:" + port,
5594 "|http|host-2.dns-is-faked.golang:" + port,
5595 "|http|host-3.dns-is-faked.golang:" + port,
5596 }
5597 if got := tr.IdleConnKeysForTesting(); !slices.Equal(got, want) {
5598 t.Fatalf("idle conn keys mismatch.\n got: %q\nwant: %q\n", got, want)
5599 }
5600
5601
5602 hitHost(4)
5603 want = []string{
5604 "|http|host-1.dns-is-faked.golang:" + port,
5605 "|http|host-2.dns-is-faked.golang:" + port,
5606 "|http|host-3.dns-is-faked.golang:" + port,
5607 "|http|host-4.dns-is-faked.golang:" + port,
5608 }
5609 if got := tr.IdleConnKeysForTesting(); !slices.Equal(got, want) {
5610 t.Fatalf("idle conn keys mismatch after 5th host.\n got: %q\nwant: %q\n", got, want)
5611 }
5612 }
5613
5614 func TestTransportIdleConnTimeout(t *testing.T) {
5615 run(t, testTransportIdleConnTimeout, http3SkippedMode)
5616 }
5617 func testTransportIdleConnTimeout(t *testing.T, mode testMode) {
5618 if testing.Short() {
5619 t.Skip("skipping in short mode")
5620 }
5621
5622 timeout := 1 * time.Millisecond
5623 timeoutLoop:
5624 for {
5625 cst := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
5626
5627 }))
5628 tr := cst.tr
5629 tr.IdleConnTimeout = timeout
5630 defer tr.CloseIdleConnections()
5631 c := &Client{Transport: tr}
5632
5633 idleConns := func() []string {
5634 return tr.IdleConnStrsForTesting()
5635 }
5636
5637 var conn string
5638 doReq := func(n int) (timeoutOk bool) {
5639 req, _ := NewRequest("GET", cst.ts.URL, nil)
5640 req = req.WithContext(httptrace.WithClientTrace(context.Background(), &httptrace.ClientTrace{
5641 PutIdleConn: func(err error) {
5642 if err != nil {
5643 t.Errorf("failed to keep idle conn: %v", err)
5644 }
5645 },
5646 }))
5647 res, err := c.Do(req)
5648 if err != nil {
5649 if strings.Contains(err.Error(), "use of closed network connection") {
5650 t.Logf("req %v: connection closed prematurely", n)
5651 return false
5652 }
5653 }
5654 if err == nil {
5655 res.Body.Close()
5656 }
5657 conns := idleConns()
5658 if len(conns) != 1 {
5659 if len(conns) == 0 {
5660 t.Logf("req %v: no idle conns", n)
5661 return false
5662 }
5663 t.Fatalf("req %v: unexpected number of idle conns: %q", n, conns)
5664 }
5665 if conn == "" {
5666 conn = conns[0]
5667 }
5668 if conn != conns[0] {
5669 t.Logf("req %v: cached connection changed; expected the same one throughout the test", n)
5670 return false
5671 }
5672 return true
5673 }
5674 for i := 0; i < 3; i++ {
5675 if !doReq(i) {
5676 t.Logf("idle conn timeout %v appears to be too short; retrying with longer", timeout)
5677 timeout *= 2
5678 cst.close()
5679 continue timeoutLoop
5680 }
5681 time.Sleep(timeout / 2)
5682 }
5683
5684 waitCondition(t, timeout/2, func(d time.Duration) bool {
5685 if got := idleConns(); len(got) != 0 {
5686 if d >= timeout*3/2 {
5687 t.Logf("after %v, idle conns = %q", d, got)
5688 }
5689 return false
5690 }
5691 return true
5692 })
5693 break
5694 }
5695 }
5696
5697
5698
5699
5700
5701
5702
5703
5704
5705
5706
5707
5708 func TestIdleConnH2Crash(t *testing.T) { run(t, testIdleConnH2Crash, []testMode{http2Mode}) }
5709 func testIdleConnH2Crash(t *testing.T, mode testMode) {
5710 cst := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
5711
5712 }), optRealNet)
5713
5714 ctx, cancel := context.WithCancel(context.Background())
5715 defer cancel()
5716
5717 sawDoErr := make(chan bool, 1)
5718 testDone := make(chan struct{})
5719 defer close(testDone)
5720
5721 cst.tr.IdleConnTimeout = 5 * time.Millisecond
5722 cst.tr.DialTLS = func(network, addr string) (net.Conn, error) {
5723 c, err := tls.Dial(network, addr, &tls.Config{
5724 InsecureSkipVerify: true,
5725 NextProtos: []string{"h2"},
5726 })
5727 if err != nil {
5728 t.Error(err)
5729 return nil, err
5730 }
5731 if cs := c.ConnectionState(); cs.NegotiatedProtocol != "h2" {
5732 t.Errorf("protocol = %q; want %q", cs.NegotiatedProtocol, "h2")
5733 c.Close()
5734 return nil, errors.New("bogus")
5735 }
5736
5737 cancel()
5738
5739 select {
5740 case <-sawDoErr:
5741 case <-testDone:
5742 }
5743 return c, nil
5744 }
5745
5746 req, _ := NewRequest("GET", cst.ts.URL, nil)
5747 req = req.WithContext(ctx)
5748 res, err := cst.c.Do(req)
5749 if err == nil {
5750 res.Body.Close()
5751 t.Fatal("unexpected success")
5752 }
5753 sawDoErr <- true
5754
5755
5756 time.Sleep(cst.tr.IdleConnTimeout * 10)
5757 }
5758
5759 type funcConn struct {
5760 net.Conn
5761 read func([]byte) (int, error)
5762 write func([]byte) (int, error)
5763 }
5764
5765 func (c funcConn) Read(p []byte) (int, error) { return c.read(p) }
5766 func (c funcConn) Write(p []byte) (int, error) { return c.write(p) }
5767 func (c funcConn) Close() error { return nil }
5768
5769
5770
5771 func TestTransportReturnsPeekError(t *testing.T) {
5772 errValue := errors.New("specific error value")
5773
5774 wrote := make(chan struct{})
5775 wroteOnce := sync.OnceFunc(func() { close(wrote) })
5776
5777 tr := &Transport{
5778 Dial: func(network, addr string) (net.Conn, error) {
5779 c := funcConn{
5780 read: func([]byte) (int, error) {
5781 <-wrote
5782 return 0, errValue
5783 },
5784 write: func(p []byte) (int, error) {
5785 wroteOnce()
5786 return len(p), nil
5787 },
5788 }
5789 return c, nil
5790 },
5791 }
5792 _, err := tr.RoundTrip(httptest.NewRequest("GET", "http://fake.tld/", nil))
5793 if err != errValue {
5794 t.Errorf("error = %#v; want %v", err, errValue)
5795 }
5796 }
5797
5798
5799 func TestTransportIDNA(t *testing.T) { run(t, testTransportIDNA, http3SkippedMode) }
5800 func testTransportIDNA(t *testing.T, mode testMode) {
5801 const uniDomain = "гофер.го"
5802 const punyDomain = "xn--c1ae0ajs.xn--c1aw"
5803
5804 var port string
5805 cst := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
5806 want := punyDomain + ":" + port
5807 if r.Host != want {
5808 t.Errorf("Host header = %q; want %q", r.Host, want)
5809 }
5810 if mode == http2Mode {
5811 if r.TLS == nil {
5812 t.Errorf("r.TLS == nil")
5813 } else if r.TLS.ServerName != punyDomain {
5814 t.Errorf("TLS.ServerName = %q; want %q", r.TLS.ServerName, punyDomain)
5815 }
5816 }
5817 w.Header().Set("Hit-Handler", "1")
5818 }), func(tr *Transport) {
5819 if tr.TLSClientConfig != nil {
5820 tr.TLSClientConfig.InsecureSkipVerify = true
5821 }
5822 }, optRealNet)
5823
5824 ip, port, err := net.SplitHostPort(cst.ts.Listener.Addr().String())
5825 if err != nil {
5826 t.Fatal(err)
5827 }
5828
5829
5830 ctx := context.WithValue(context.Background(), nettrace.LookupIPAltResolverKey{}, func(ctx context.Context, network, host string) ([]net.IPAddr, error) {
5831 if host != punyDomain {
5832 t.Errorf("got DNS host lookup for %q/%q; want %q", network, host, punyDomain)
5833 return nil, nil
5834 }
5835 return []net.IPAddr{{IP: net.ParseIP(ip)}}, nil
5836 })
5837
5838 req, _ := NewRequest("GET", cst.scheme()+"://"+uniDomain+":"+port, nil)
5839 trace := &httptrace.ClientTrace{
5840 GetConn: func(hostPort string) {
5841 want := net.JoinHostPort(punyDomain, port)
5842 if hostPort != want {
5843 t.Errorf("getting conn for %q; want %q", hostPort, want)
5844 }
5845 },
5846 DNSStart: func(e httptrace.DNSStartInfo) {
5847 if e.Host != punyDomain {
5848 t.Errorf("DNSStart Host = %q; want %q", e.Host, punyDomain)
5849 }
5850 },
5851 }
5852 req = req.WithContext(httptrace.WithClientTrace(ctx, trace))
5853
5854 res, err := cst.tr.RoundTrip(req)
5855 if err != nil {
5856 t.Fatal(err)
5857 }
5858 defer res.Body.Close()
5859 if res.Header.Get("Hit-Handler") != "1" {
5860 out, err := httputil.DumpResponse(res, true)
5861 if err != nil {
5862 t.Fatal(err)
5863 }
5864 t.Errorf("Response body wasn't from Handler. Got:\n%s\n", out)
5865 }
5866 }
5867
5868
5869 func TestTransportProxyConnectHeader(t *testing.T) {
5870 run(t, testTransportProxyConnectHeader, []testMode{http1Mode})
5871 }
5872 func testTransportProxyConnectHeader(t *testing.T, mode testMode) {
5873 reqc := make(chan *Request, 1)
5874 ts := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
5875 if r.Method != "CONNECT" {
5876 t.Errorf("method = %q; want CONNECT", r.Method)
5877 }
5878 reqc <- r
5879 c, _, err := w.(Hijacker).Hijack()
5880 if err != nil {
5881 t.Errorf("Hijack: %v", err)
5882 return
5883 }
5884 c.Close()
5885 })).ts
5886
5887 c := ts.Client()
5888 c.Transport.(*Transport).Proxy = func(r *Request) (*url.URL, error) {
5889 return url.Parse(ts.URL)
5890 }
5891 c.Transport.(*Transport).ProxyConnectHeader = Header{
5892 "User-Agent": {"foo"},
5893 "Other": {"bar"},
5894 }
5895
5896 res, err := c.Get("https://dummy.tld/")
5897 if err == nil {
5898 res.Body.Close()
5899 t.Errorf("unexpected success")
5900 }
5901
5902 r := <-reqc
5903 if got, want := r.Header.Get("User-Agent"), "foo"; got != want {
5904 t.Errorf("CONNECT request User-Agent = %q; want %q", got, want)
5905 }
5906 if got, want := r.Header.Get("Other"), "bar"; got != want {
5907 t.Errorf("CONNECT request Other = %q; want %q", got, want)
5908 }
5909 }
5910
5911 func TestTransportProxyGetConnectHeader(t *testing.T) {
5912 run(t, testTransportProxyGetConnectHeader, []testMode{http1Mode})
5913 }
5914 func testTransportProxyGetConnectHeader(t *testing.T, mode testMode) {
5915 reqc := make(chan *Request, 1)
5916 ts := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
5917 if r.Method != "CONNECT" {
5918 t.Errorf("method = %q; want CONNECT", r.Method)
5919 }
5920 reqc <- r
5921 c, _, err := w.(Hijacker).Hijack()
5922 if err != nil {
5923 t.Errorf("Hijack: %v", err)
5924 return
5925 }
5926 c.Close()
5927 })).ts
5928
5929 c := ts.Client()
5930 c.Transport.(*Transport).Proxy = func(r *Request) (*url.URL, error) {
5931 return url.Parse(ts.URL)
5932 }
5933
5934 c.Transport.(*Transport).ProxyConnectHeader = Header{
5935 "User-Agent": {"foo"},
5936 "Other": {"bar"},
5937 }
5938 c.Transport.(*Transport).GetProxyConnectHeader = func(ctx context.Context, proxyURL *url.URL, target string) (Header, error) {
5939 return Header{
5940 "User-Agent": {"foo2"},
5941 "Other": {"bar2"},
5942 }, nil
5943 }
5944
5945 res, err := c.Get("https://dummy.tld/")
5946 if err == nil {
5947 res.Body.Close()
5948 t.Errorf("unexpected success")
5949 }
5950
5951 r := <-reqc
5952 if got, want := r.Header.Get("User-Agent"), "foo2"; got != want {
5953 t.Errorf("CONNECT request User-Agent = %q; want %q", got, want)
5954 }
5955 if got, want := r.Header.Get("Other"), "bar2"; got != want {
5956 t.Errorf("CONNECT request Other = %q; want %q", got, want)
5957 }
5958 }
5959
5960 var errFakeRoundTrip = errors.New("fake roundtrip")
5961
5962 type funcRoundTripper func()
5963
5964 func (fn funcRoundTripper) RoundTrip(*Request) (*Response, error) {
5965 fn()
5966 return nil, errFakeRoundTrip
5967 }
5968
5969 func wantBody(res *Response, err error, want string) error {
5970 if err != nil {
5971 return err
5972 }
5973 slurp, err := io.ReadAll(res.Body)
5974 if err != nil {
5975 return fmt.Errorf("error reading body: %v", err)
5976 }
5977 if string(slurp) != want {
5978 return fmt.Errorf("body = %q; want %q", slurp, want)
5979 }
5980 if err := res.Body.Close(); err != nil {
5981 return fmt.Errorf("body Close = %v", err)
5982 }
5983 return nil
5984 }
5985
5986 func newLocalListener(t *testing.T) net.Listener {
5987 ln, err := net.Listen("tcp", "127.0.0.1:0")
5988 if err != nil {
5989 ln, err = net.Listen("tcp6", "[::1]:0")
5990 }
5991 if err != nil {
5992 t.Fatal(err)
5993 }
5994 return ln
5995 }
5996
5997 type countCloseReader struct {
5998 n *int
5999 io.Reader
6000 }
6001
6002 func (cr countCloseReader) Close() error {
6003 (*cr.n)++
6004 return nil
6005 }
6006
6007
6008 var rgz = []byte{
6009 0x1f, 0x8b, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00,
6010 0x00, 0x00, 0x72, 0x65, 0x63, 0x75, 0x72, 0x73,
6011 0x69, 0x76, 0x65, 0x00, 0x92, 0xef, 0xe6, 0xe0,
6012 0x60, 0x00, 0x83, 0xa2, 0xd4, 0xe4, 0xd2, 0xa2,
6013 0xe2, 0xcc, 0xb2, 0x54, 0x06, 0x00, 0x00, 0x17,
6014 0x00, 0xe8, 0xff, 0x92, 0xef, 0xe6, 0xe0, 0x60,
6015 0x00, 0x83, 0xa2, 0xd4, 0xe4, 0xd2, 0xa2, 0xe2,
6016 0xcc, 0xb2, 0x54, 0x06, 0x00, 0x00, 0x17, 0x00,
6017 0xe8, 0xff, 0x42, 0x12, 0x46, 0x16, 0x06, 0x00,
6018 0x05, 0x00, 0xfa, 0xff, 0x42, 0x12, 0x46, 0x16,
6019 0x06, 0x00, 0x05, 0x00, 0xfa, 0xff, 0x00, 0x05,
6020 0x00, 0xfa, 0xff, 0x00, 0x14, 0x00, 0xeb, 0xff,
6021 0x42, 0x12, 0x46, 0x16, 0x06, 0x00, 0x05, 0x00,
6022 0xfa, 0xff, 0x00, 0x05, 0x00, 0xfa, 0xff, 0x00,
6023 0x14, 0x00, 0xeb, 0xff, 0x42, 0x88, 0x21, 0xc4,
6024 0x00, 0x00, 0x14, 0x00, 0xeb, 0xff, 0x42, 0x88,
6025 0x21, 0xc4, 0x00, 0x00, 0x14, 0x00, 0xeb, 0xff,
6026 0x42, 0x88, 0x21, 0xc4, 0x00, 0x00, 0x14, 0x00,
6027 0xeb, 0xff, 0x42, 0x88, 0x21, 0xc4, 0x00, 0x00,
6028 0x14, 0x00, 0xeb, 0xff, 0x42, 0x88, 0x21, 0xc4,
6029 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00,
6030 0x00, 0xff, 0xff, 0x00, 0x17, 0x00, 0xe8, 0xff,
6031 0x42, 0x88, 0x21, 0xc4, 0x00, 0x00, 0x00, 0x00,
6032 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00,
6033 0x17, 0x00, 0xe8, 0xff, 0x42, 0x12, 0x46, 0x16,
6034 0x06, 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x08,
6035 0x00, 0xf7, 0xff, 0x3d, 0xb1, 0x20, 0x85, 0xfa,
6036 0x00, 0x00, 0x00, 0x42, 0x12, 0x46, 0x16, 0x06,
6037 0x00, 0x00, 0x00, 0xff, 0xff, 0x01, 0x08, 0x00,
6038 0xf7, 0xff, 0x3d, 0xb1, 0x20, 0x85, 0xfa, 0x00,
6039 0x00, 0x00, 0x3d, 0xb1, 0x20, 0x85, 0xfa, 0x00,
6040 0x00, 0x00,
6041 }
6042
6043
6044
6045 func TestMissingStatusNoPanic(t *testing.T) {
6046 t.Parallel()
6047
6048 const want = "unknown status code"
6049
6050 ln := newLocalListener(t)
6051 addr := ln.Addr().String()
6052 done := make(chan bool)
6053 fullAddrURL := fmt.Sprintf("http://%s", addr)
6054 raw := "HTTP/1.1 400\r\n" +
6055 "Date: Wed, 30 Aug 2017 19:09:27 GMT\r\n" +
6056 "Content-Type: text/html; charset=utf-8\r\n" +
6057 "Content-Length: 10\r\n" +
6058 "Last-Modified: Wed, 30 Aug 2017 19:02:02 GMT\r\n" +
6059 "Vary: Accept-Encoding\r\n\r\n" +
6060 "Aloha Olaa"
6061
6062 go func() {
6063 defer close(done)
6064
6065 conn, _ := ln.Accept()
6066 if conn != nil {
6067 io.WriteString(conn, raw)
6068 io.ReadAll(conn)
6069 conn.Close()
6070 }
6071 }()
6072
6073 proxyURL, err := url.Parse(fullAddrURL)
6074 if err != nil {
6075 t.Fatalf("proxyURL: %v", err)
6076 }
6077
6078 tr := &Transport{Proxy: ProxyURL(proxyURL)}
6079
6080 req, _ := NewRequest("GET", "https://golang.org/", nil)
6081 res, err, panicked := doFetchCheckPanic(tr, req)
6082 if panicked {
6083 t.Error("panicked, expecting an error")
6084 }
6085 if res != nil && res.Body != nil {
6086 io.Copy(io.Discard, res.Body)
6087 res.Body.Close()
6088 }
6089
6090 if err == nil || !strings.Contains(err.Error(), want) {
6091 t.Errorf("got=%v want=%q", err, want)
6092 }
6093
6094 ln.Close()
6095 <-done
6096 }
6097
6098 func doFetchCheckPanic(tr *Transport, req *Request) (res *Response, err error, panicked bool) {
6099 defer func() {
6100 if r := recover(); r != nil {
6101 panicked = true
6102 }
6103 }()
6104 res, err = tr.RoundTrip(req)
6105 return
6106 }
6107
6108
6109
6110 func TestNoBodyOnChunked304Response(t *testing.T) {
6111 run(t, testNoBodyOnChunked304Response, []testMode{http1Mode})
6112 }
6113 func testNoBodyOnChunked304Response(t *testing.T, mode testMode) {
6114 cst := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
6115 conn, buf, _ := w.(Hijacker).Hijack()
6116 buf.Write([]byte("HTTP/1.1 304 NOT MODIFIED\r\nTransfer-Encoding: chunked\r\n\r\n0\r\n\r\n"))
6117 buf.Flush()
6118 conn.Close()
6119 }))
6120
6121
6122
6123
6124
6125 cst.tr.DisableKeepAlives = true
6126
6127 res, err := cst.c.Get(cst.ts.URL)
6128 if err != nil {
6129 t.Fatal(err)
6130 }
6131
6132 if res.Body != NoBody {
6133 t.Errorf("Unexpected body on 304 response")
6134 }
6135 }
6136
6137 type funcWriter func([]byte) (int, error)
6138
6139 func (f funcWriter) Write(p []byte) (int, error) { return f(p) }
6140
6141 type doneContext struct {
6142 context.Context
6143 err error
6144 }
6145
6146 func (doneContext) Done() <-chan struct{} {
6147 c := make(chan struct{})
6148 close(c)
6149 return c
6150 }
6151
6152 func (d doneContext) Err() error { return d.err }
6153
6154
6155 func TestTransportCheckContextDoneEarly(t *testing.T) {
6156 tr := &Transport{}
6157 req, _ := NewRequest("GET", "http://fake.example/", nil)
6158 wantErr := errors.New("some error")
6159 req = req.WithContext(doneContext{context.Background(), wantErr})
6160 _, err := tr.RoundTrip(req)
6161 if err != wantErr {
6162 t.Errorf("error = %v; want %v", err, wantErr)
6163 }
6164 }
6165
6166
6167
6168
6169
6170
6171 func TestClientTimeoutKillsConn_BeforeHeaders(t *testing.T) {
6172 run(t, testClientTimeoutKillsConn_BeforeHeaders, []testMode{http1Mode})
6173 }
6174 func testClientTimeoutKillsConn_BeforeHeaders(t *testing.T, mode testMode) {
6175 timeout := 1 * time.Millisecond
6176 for {
6177 inHandler := make(chan bool)
6178 cancelHandler := make(chan struct{})
6179 handlerDone := make(chan bool)
6180 cst := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
6181 <-r.Context().Done()
6182
6183 select {
6184 case <-cancelHandler:
6185 return
6186 case inHandler <- true:
6187 }
6188 defer func() { handlerDone <- true }()
6189
6190
6191 conn, _, err := w.(Hijacker).Hijack()
6192 if err != nil {
6193 t.Error(err)
6194 return
6195 }
6196 n, err := conn.Read([]byte{0})
6197 if n != 0 || err != io.EOF {
6198 t.Errorf("unexpected Read result: %v, %v", n, err)
6199 }
6200 conn.Close()
6201 }))
6202
6203 cst.c.Timeout = timeout
6204
6205 _, err := cst.c.Get(cst.ts.URL)
6206 if err == nil {
6207 close(cancelHandler)
6208 t.Fatal("unexpected Get success")
6209 }
6210
6211 tooSlow := time.NewTimer(timeout * 10)
6212 select {
6213 case <-tooSlow.C:
6214
6215
6216
6217 t.Logf("no handler seen in %v; retrying with longer timeout", timeout)
6218 close(cancelHandler)
6219 cst.close()
6220 timeout *= 2
6221 continue
6222 case <-inHandler:
6223 tooSlow.Stop()
6224 <-handlerDone
6225 }
6226 break
6227 }
6228 }
6229
6230
6231
6232
6233
6234
6235 func TestClientTimeoutKillsConn_AfterHeaders(t *testing.T) {
6236 run(t, testClientTimeoutKillsConn_AfterHeaders, []testMode{http1Mode})
6237 }
6238 func testClientTimeoutKillsConn_AfterHeaders(t *testing.T, mode testMode) {
6239 inHandler := make(chan bool)
6240 cancelHandler := make(chan struct{})
6241 handlerDone := make(chan bool)
6242 cst := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
6243 w.Header().Set("Content-Length", "100")
6244 w.(Flusher).Flush()
6245
6246 select {
6247 case <-cancelHandler:
6248 return
6249 case inHandler <- true:
6250 }
6251 defer func() { handlerDone <- true }()
6252
6253 conn, _, err := w.(Hijacker).Hijack()
6254 if err != nil {
6255 t.Error(err)
6256 return
6257 }
6258 conn.Write([]byte("foo"))
6259
6260 n, err := conn.Read([]byte{0})
6261
6262
6263
6264
6265
6266 if n != 0 || err == nil {
6267 t.Errorf("unexpected Read result: %v, %v", n, err)
6268 }
6269 conn.Close()
6270 }))
6271
6272
6273
6274
6275
6276 cst.c.Timeout = 24 * time.Hour
6277 req, _ := NewRequest("GET", cst.ts.URL, nil)
6278 cancelReq := make(chan struct{})
6279 req.Cancel = cancelReq
6280
6281 res, err := cst.c.Do(req)
6282 if err != nil {
6283 close(cancelHandler)
6284 t.Fatalf("Get error: %v", err)
6285 }
6286
6287
6288
6289
6290 close(cancelReq)
6291 got, err := io.ReadAll(res.Body)
6292 if err == nil {
6293 t.Errorf("unexpected success; read %q, nil", got)
6294 }
6295
6296
6297 <-inHandler
6298 <-handlerDone
6299 }
6300
6301 func TestTransportResponseBodyWritableOnProtocolSwitch(t *testing.T) {
6302 run(t, testTransportResponseBodyWritableOnProtocolSwitch, []testMode{http1Mode})
6303 }
6304 func testTransportResponseBodyWritableOnProtocolSwitch(t *testing.T, mode testMode) {
6305 done := make(chan struct{})
6306 defer close(done)
6307 cst := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
6308 conn, _, err := w.(Hijacker).Hijack()
6309 if err != nil {
6310 t.Error(err)
6311 return
6312 }
6313 defer conn.Close()
6314 io.WriteString(conn, "HTTP/1.1 101 Switching Protocols Hi\r\nConnection: upgRADe\r\nUpgrade: foo\r\n\r\nSome buffered data\n")
6315 bs := bufio.NewScanner(conn)
6316 bs.Scan()
6317 fmt.Fprintf(conn, "%s\n", strings.ToUpper(bs.Text()))
6318 <-done
6319 }))
6320
6321 req, _ := NewRequest("GET", cst.ts.URL, nil)
6322 req.Header.Set("Upgrade", "foo")
6323 req.Header.Set("Connection", "upgrade")
6324 res, err := cst.c.Do(req)
6325 if err != nil {
6326 t.Fatal(err)
6327 }
6328 if res.StatusCode != 101 {
6329 t.Fatalf("expected 101 switching protocols; got %v, %v", res.Status, res.Header)
6330 }
6331 rwc, ok := res.Body.(io.ReadWriteCloser)
6332 if !ok {
6333 t.Fatalf("expected a ReadWriteCloser; got a %T", res.Body)
6334 }
6335 defer rwc.Close()
6336 bs := bufio.NewScanner(rwc)
6337 if !bs.Scan() {
6338 t.Fatalf("expected readable input")
6339 }
6340 if got, want := bs.Text(), "Some buffered data"; got != want {
6341 t.Errorf("read %q; want %q", got, want)
6342 }
6343 io.WriteString(rwc, "echo\n")
6344 if !bs.Scan() {
6345 t.Fatalf("expected another line")
6346 }
6347 if got, want := bs.Text(), "ECHO"; got != want {
6348 t.Errorf("read %q; want %q", got, want)
6349 }
6350 }
6351
6352 func TestTransportCONNECTBidi(t *testing.T) { run(t, testTransportCONNECTBidi, []testMode{http1Mode}) }
6353 func testTransportCONNECTBidi(t *testing.T, mode testMode) {
6354 const target = "backend:443"
6355 cst := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
6356 if r.Method != "CONNECT" {
6357 t.Errorf("unexpected method %q", r.Method)
6358 w.WriteHeader(500)
6359 return
6360 }
6361 if r.RequestURI != target {
6362 t.Errorf("unexpected CONNECT target %q", r.RequestURI)
6363 w.WriteHeader(500)
6364 return
6365 }
6366 nc, brw, err := w.(Hijacker).Hijack()
6367 if err != nil {
6368 t.Error(err)
6369 return
6370 }
6371 defer nc.Close()
6372 nc.Write([]byte("HTTP/1.1 200 OK\r\n\r\n"))
6373
6374 for {
6375 line, err := brw.ReadString('\n')
6376 if err != nil {
6377 if err != io.EOF {
6378 t.Error(err)
6379 }
6380 return
6381 }
6382 io.WriteString(brw, strings.ToUpper(line))
6383 brw.Flush()
6384 }
6385 }))
6386 pr, pw := io.Pipe()
6387 defer pw.Close()
6388 req, err := NewRequest("CONNECT", cst.ts.URL, pr)
6389 if err != nil {
6390 t.Fatal(err)
6391 }
6392 req.URL.Opaque = target
6393 res, err := cst.c.Do(req)
6394 if err != nil {
6395 t.Fatal(err)
6396 }
6397 defer res.Body.Close()
6398 if res.StatusCode != 200 {
6399 t.Fatalf("status code = %d; want 200", res.StatusCode)
6400 }
6401 br := bufio.NewReader(res.Body)
6402 for _, str := range []string{"foo", "bar", "baz"} {
6403 fmt.Fprintf(pw, "%s\n", str)
6404 got, err := br.ReadString('\n')
6405 if err != nil {
6406 t.Fatal(err)
6407 }
6408 got = strings.TrimSpace(got)
6409 want := strings.ToUpper(str)
6410 if got != want {
6411 t.Fatalf("got %q; want %q", got, want)
6412 }
6413 }
6414 }
6415
6416 func TestTransportRequestReplayable(t *testing.T) {
6417 someBody := io.NopCloser(strings.NewReader(""))
6418 tests := []struct {
6419 name string
6420 req *Request
6421 want bool
6422 }{
6423 {
6424 name: "GET",
6425 req: &Request{Method: "GET"},
6426 want: true,
6427 },
6428 {
6429 name: "GET_http.NoBody",
6430 req: &Request{Method: "GET", Body: NoBody},
6431 want: true,
6432 },
6433 {
6434 name: "GET_body",
6435 req: &Request{Method: "GET", Body: someBody},
6436 want: false,
6437 },
6438 {
6439 name: "POST",
6440 req: &Request{Method: "POST"},
6441 want: false,
6442 },
6443 {
6444 name: "POST_idempotency-key",
6445 req: &Request{Method: "POST", Header: Header{"Idempotency-Key": {"x"}}},
6446 want: true,
6447 },
6448 {
6449 name: "POST_x-idempotency-key",
6450 req: &Request{Method: "POST", Header: Header{"X-Idempotency-Key": {"x"}}},
6451 want: true,
6452 },
6453 {
6454 name: "POST_body",
6455 req: &Request{Method: "POST", Header: Header{"Idempotency-Key": {"x"}}, Body: someBody},
6456 want: false,
6457 },
6458 }
6459 for _, tt := range tests {
6460 t.Run(tt.name, func(t *testing.T) {
6461 got := tt.req.ExportIsReplayable()
6462 if got != tt.want {
6463 t.Errorf("replyable = %v; want %v", got, tt.want)
6464 }
6465 })
6466 }
6467 }
6468
6469
6470
6471 type testMockTCPConn struct {
6472 *net.TCPConn
6473
6474 ReadFromCalled bool
6475 }
6476
6477 func (c *testMockTCPConn) ReadFrom(r io.Reader) (int64, error) {
6478 c.ReadFromCalled = true
6479 return c.TCPConn.ReadFrom(r)
6480 }
6481
6482 func TestTransportRequestWriteRoundTrip(t *testing.T) { run(t, testTransportRequestWriteRoundTrip) }
6483 func testTransportRequestWriteRoundTrip(t *testing.T, mode testMode) {
6484 nBytes := int64(1 << 10)
6485 newFileFunc := func() (r io.Reader, done func(), err error) {
6486 f, err := os.CreateTemp("", "net-http-newfilefunc")
6487 if err != nil {
6488 return nil, nil, err
6489 }
6490
6491
6492 if _, err := io.CopyN(f, rand.Reader, nBytes); err != nil {
6493 return nil, nil, fmt.Errorf("failed to write data to file: %v", err)
6494 }
6495 if _, err := f.Seek(0, 0); err != nil {
6496 return nil, nil, fmt.Errorf("failed to seek to front: %v", err)
6497 }
6498
6499 done = func() {
6500 f.Close()
6501 os.Remove(f.Name())
6502 }
6503
6504 return f, done, nil
6505 }
6506
6507 newBufferFunc := func() (io.Reader, func(), error) {
6508 return bytes.NewBuffer(make([]byte, nBytes)), func() {}, nil
6509 }
6510
6511 cases := []struct {
6512 name string
6513 readerFunc func() (io.Reader, func(), error)
6514 contentLength int64
6515 expectedReadFrom bool
6516 }{
6517 {
6518 name: "file, length",
6519 readerFunc: newFileFunc,
6520 contentLength: nBytes,
6521 expectedReadFrom: true,
6522 },
6523 {
6524 name: "file, no length",
6525 readerFunc: newFileFunc,
6526 },
6527 {
6528 name: "file, negative length",
6529 readerFunc: newFileFunc,
6530 contentLength: -1,
6531 },
6532 {
6533 name: "buffer",
6534 contentLength: nBytes,
6535 readerFunc: newBufferFunc,
6536 },
6537 {
6538 name: "buffer, no length",
6539 readerFunc: newBufferFunc,
6540 },
6541 {
6542 name: "buffer, length -1",
6543 contentLength: -1,
6544 readerFunc: newBufferFunc,
6545 },
6546 }
6547
6548 for _, tc := range cases {
6549 t.Run(tc.name, func(t *testing.T) {
6550 r, cleanup, err := tc.readerFunc()
6551 if err != nil {
6552 t.Fatal(err)
6553 }
6554 defer cleanup()
6555
6556 tConn := &testMockTCPConn{}
6557 trFunc := func(tr *Transport) {
6558 tr.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
6559 var d net.Dialer
6560 conn, err := d.DialContext(ctx, network, addr)
6561 if err != nil {
6562 return nil, err
6563 }
6564
6565 tcpConn, ok := conn.(*net.TCPConn)
6566 if !ok {
6567 return nil, fmt.Errorf("%s/%s does not provide a *net.TCPConn", network, addr)
6568 }
6569
6570 tConn.TCPConn = tcpConn
6571 return tConn, nil
6572 }
6573 }
6574
6575 cst := newClientServerTest(
6576 t,
6577 mode,
6578 HandlerFunc(func(w ResponseWriter, r *Request) {
6579 io.Copy(io.Discard, r.Body)
6580 r.Body.Close()
6581 w.WriteHeader(200)
6582 }),
6583 trFunc,
6584 optRealNet,
6585 )
6586
6587 req, err := NewRequest("PUT", cst.ts.URL, r)
6588 if err != nil {
6589 t.Fatal(err)
6590 }
6591 req.ContentLength = tc.contentLength
6592 req.Header.Set("Content-Type", "application/octet-stream")
6593 resp, err := cst.c.Do(req)
6594 if err != nil {
6595 t.Fatal(err)
6596 }
6597 defer resp.Body.Close()
6598 if resp.StatusCode != 200 {
6599 t.Fatalf("status code = %d; want 200", resp.StatusCode)
6600 }
6601
6602 expectedReadFrom := tc.expectedReadFrom
6603 if mode != http1Mode {
6604 expectedReadFrom = false
6605 }
6606 if !tConn.ReadFromCalled && expectedReadFrom {
6607 t.Fatalf("did not call ReadFrom")
6608 }
6609
6610 if tConn.ReadFromCalled && !expectedReadFrom {
6611 t.Fatalf("ReadFrom was unexpectedly invoked")
6612 }
6613 })
6614 }
6615 }
6616
6617 func TestTransportClone(t *testing.T) {
6618 tr := &Transport{
6619 Proxy: func(*Request) (*url.URL, error) { panic("") },
6620 OnProxyConnectResponse: func(ctx context.Context, proxyURL *url.URL, connectReq *Request, connectRes *Response) error {
6621 return nil
6622 },
6623 DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { panic("") },
6624 Dial: func(network, addr string) (net.Conn, error) { panic("") },
6625 DialTLS: func(network, addr string) (net.Conn, error) { panic("") },
6626 DialTLSContext: func(ctx context.Context, network, addr string) (net.Conn, error) { panic("") },
6627 TLSClientConfig: new(tls.Config),
6628 TLSHandshakeTimeout: time.Second,
6629 DisableKeepAlives: true,
6630 DisableCompression: true,
6631 MaxIdleConns: 1,
6632 MaxIdleConnsPerHost: 1,
6633 MaxConnsPerHost: 1,
6634 IdleConnTimeout: time.Second,
6635 ResponseHeaderTimeout: time.Second,
6636 ExpectContinueTimeout: time.Second,
6637 ProxyConnectHeader: Header{},
6638 GetProxyConnectHeader: func(context.Context, *url.URL, string) (Header, error) { return nil, nil },
6639 MaxResponseHeaderBytes: 1,
6640 ForceAttemptHTTP2: true,
6641 HTTP2: &HTTP2Config{MaxConcurrentStreams: 1},
6642 Protocols: &Protocols{},
6643 TLSNextProto: map[string]func(authority string, c *tls.Conn) RoundTripper{
6644 "foo": func(authority string, c *tls.Conn) RoundTripper { panic("") },
6645 },
6646 ReadBufferSize: 1,
6647 WriteBufferSize: 1,
6648 }
6649 tr.Protocols.SetHTTP1(true)
6650 tr.Protocols.SetHTTP2(true)
6651 tr2 := tr.Clone()
6652 rv := reflect.ValueOf(tr2).Elem()
6653 rt := rv.Type()
6654 for i := 0; i < rt.NumField(); i++ {
6655 sf := rt.Field(i)
6656 if !token.IsExported(sf.Name) {
6657 continue
6658 }
6659 if rv.Field(i).IsZero() {
6660 t.Errorf("cloned field t2.%s is zero", sf.Name)
6661 }
6662 }
6663
6664 if _, ok := tr2.TLSNextProto["foo"]; !ok {
6665 t.Errorf("cloned Transport lacked TLSNextProto 'foo' key")
6666 }
6667
6668
6669 tr = new(Transport)
6670 tr2 = tr.Clone()
6671 if tr2.TLSNextProto != nil {
6672 t.Errorf("Transport.TLSNextProto unexpected non-nil")
6673 }
6674 }
6675
6676 func TestIs408(t *testing.T) {
6677 tests := []struct {
6678 in string
6679 want bool
6680 }{
6681 {"HTTP/1.0 408", true},
6682 {"HTTP/1.1 408", true},
6683 {"HTTP/1.8 408", true},
6684 {"HTTP/2.0 408", false},
6685 {"HTTP/1.1 408 ", true},
6686 {"HTTP/1.1 40", false},
6687 {"http/1.0 408", false},
6688 {"HTTP/1-1 408", false},
6689 }
6690 for _, tt := range tests {
6691 if got := Export_is408Message([]byte(tt.in)); got != tt.want {
6692 t.Errorf("is408Message(%q) = %v; want %v", tt.in, got, tt.want)
6693 }
6694 }
6695 }
6696
6697 func TestTransportIgnores408(t *testing.T) {
6698 run(t, testTransportIgnores408, []testMode{http1Mode}, testNotParallel)
6699 }
6700 func testTransportIgnores408(t *testing.T, mode testMode) {
6701
6702 defer log.SetOutput(log.Writer())
6703
6704 var logout strings.Builder
6705 log.SetOutput(&logout)
6706
6707 const target = "backend:443"
6708
6709 cst := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
6710 nc, _, err := w.(Hijacker).Hijack()
6711 if err != nil {
6712 t.Error(err)
6713 return
6714 }
6715 defer nc.Close()
6716 nc.Write([]byte("HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nok"))
6717 nc.Write([]byte("HTTP/1.1 408 bye\r\n"))
6718 }))
6719 req, err := NewRequest("GET", cst.ts.URL, nil)
6720 if err != nil {
6721 t.Fatal(err)
6722 }
6723 res, err := cst.c.Do(req)
6724 if err != nil {
6725 t.Fatal(err)
6726 }
6727 slurp, err := io.ReadAll(res.Body)
6728 if err != nil {
6729 t.Fatal(err)
6730 }
6731 if err != nil {
6732 t.Fatal(err)
6733 }
6734 if string(slurp) != "ok" {
6735 t.Fatalf("got %q; want ok", slurp)
6736 }
6737
6738 waitCondition(t, 1*time.Millisecond, func(d time.Duration) bool {
6739 if n := cst.tr.IdleConnKeyCountForTesting(); n != 0 {
6740 if d > 0 {
6741 t.Logf("%v idle conns still present after %v", n, d)
6742 }
6743 return false
6744 }
6745 return true
6746 })
6747 if got := logout.String(); got != "" {
6748 t.Fatalf("expected no log output; got: %s", got)
6749 }
6750 }
6751
6752 func TestInvalidHeaderResponse(t *testing.T) {
6753 run(t, testInvalidHeaderResponse, []testMode{http1Mode})
6754 }
6755 func testInvalidHeaderResponse(t *testing.T, mode testMode) {
6756 cst := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
6757 conn, buf, _ := w.(Hijacker).Hijack()
6758 buf.Write([]byte("HTTP/1.1 200 OK\r\n" +
6759 "Date: Wed, 30 Aug 2017 19:09:27 GMT\r\n" +
6760 "Content-Type: text/html; charset=utf-8\r\n" +
6761 "Content-Length: 0\r\n" +
6762 "Foo : bar\r\n\r\n"))
6763 buf.Flush()
6764 conn.Close()
6765 }))
6766 res, err := cst.c.Get(cst.ts.URL)
6767 if err != nil {
6768 t.Fatal(err)
6769 }
6770 defer res.Body.Close()
6771 if v := res.Header.Get("Foo"); v != "" {
6772 t.Errorf(`unexpected "Foo" header: %q`, v)
6773 }
6774 if v := res.Header.Get("Foo "); v != "bar" {
6775 t.Errorf(`bad "Foo " header value: %q, want %q`, v, "bar")
6776 }
6777 }
6778
6779 type bodyCloser bool
6780
6781 func (bc *bodyCloser) Close() error {
6782 *bc = true
6783 return nil
6784 }
6785 func (bc *bodyCloser) Read(b []byte) (n int, err error) {
6786 return 0, io.EOF
6787 }
6788
6789
6790
6791 func TestTransportClosesBodyOnInvalidRequests(t *testing.T) {
6792 run(t, testTransportClosesBodyOnInvalidRequests)
6793 }
6794 func testTransportClosesBodyOnInvalidRequests(t *testing.T, mode testMode) {
6795 cst := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
6796 t.Errorf("Should not have been invoked")
6797 })).ts
6798
6799 u, _ := url.Parse(cst.URL)
6800
6801 tests := []struct {
6802 name string
6803 req *Request
6804 wantErr string
6805 }{
6806 {
6807 name: "invalid method",
6808 req: &Request{
6809 Method: " ",
6810 URL: u,
6811 },
6812 wantErr: `invalid method " "`,
6813 },
6814 {
6815 name: "nil URL",
6816 req: &Request{
6817 Method: "GET",
6818 },
6819 wantErr: `nil Request.URL`,
6820 },
6821 {
6822 name: "invalid header key",
6823 req: &Request{
6824 Method: "GET",
6825 Header: Header{"💡": {"emoji"}},
6826 URL: u,
6827 },
6828 wantErr: `invalid header field name "💡"`,
6829 },
6830 {
6831 name: "invalid header value",
6832 req: &Request{
6833 Method: "POST",
6834 Header: Header{"key": {"\x19"}},
6835 URL: u,
6836 },
6837 wantErr: `invalid header field value for "key"`,
6838 },
6839 {
6840 name: "non HTTP(s) scheme",
6841 req: &Request{
6842 Method: "POST",
6843 URL: &url.URL{Scheme: "faux"},
6844 },
6845 wantErr: `unsupported protocol scheme "faux"`,
6846 },
6847 {
6848 name: "no Host in URL",
6849 req: &Request{
6850 Method: "POST",
6851 URL: &url.URL{Scheme: "http"},
6852 },
6853 wantErr: `no Host in request URL`,
6854 },
6855 }
6856
6857 for _, tt := range tests {
6858 t.Run(tt.name, func(t *testing.T) {
6859 var bc bodyCloser
6860 req := tt.req
6861 req.Body = &bc
6862 _, err := cst.Client().Do(tt.req)
6863 if err == nil {
6864 t.Fatal("Expected an error")
6865 }
6866 if !bc {
6867 t.Fatal("Expected body to have been closed")
6868 }
6869 if g, w := err.Error(), tt.wantErr; !strings.HasSuffix(g, w) {
6870 t.Fatalf("Error mismatch: %q does not end with %q", g, w)
6871 }
6872 })
6873 }
6874 }
6875
6876
6877
6878 type breakableConn struct {
6879 net.Conn
6880 *brokenState
6881 }
6882
6883 type brokenState struct {
6884 sync.Mutex
6885 broken bool
6886 }
6887
6888 func (w *breakableConn) Write(b []byte) (n int, err error) {
6889 w.Lock()
6890 defer w.Unlock()
6891 if w.broken {
6892 return 0, errors.New("some write error")
6893 }
6894 return w.Conn.Write(b)
6895 }
6896
6897
6898 func TestDontCacheBrokenHTTP2Conn(t *testing.T) {
6899 run(t, testDontCacheBrokenHTTP2Conn, []testMode{http2Mode})
6900 }
6901 func testDontCacheBrokenHTTP2Conn(t *testing.T, mode testMode) {
6902 cst := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {}), optQuietLog, optRealNet)
6903
6904 var brokenState brokenState
6905
6906 const numReqs = 5
6907 var numDials, gotConns uint32
6908
6909 cst.tr.Dial = func(netw, addr string) (net.Conn, error) {
6910 atomic.AddUint32(&numDials, 1)
6911 c, err := net.Dial(netw, addr)
6912 if err != nil {
6913 t.Errorf("unexpected Dial error: %v", err)
6914 return nil, err
6915 }
6916 return &breakableConn{c, &brokenState}, err
6917 }
6918
6919 for i := 1; i <= numReqs; i++ {
6920 brokenState.Lock()
6921 brokenState.broken = false
6922 brokenState.Unlock()
6923
6924
6925
6926
6927 doBreak := i != numReqs
6928
6929 ctx := httptrace.WithClientTrace(context.Background(), &httptrace.ClientTrace{
6930 GotConn: func(info httptrace.GotConnInfo) {
6931 t.Logf("got conn: %v, reused=%v, wasIdle=%v, idleTime=%v", info.Conn.LocalAddr(), info.Reused, info.WasIdle, info.IdleTime)
6932 atomic.AddUint32(&gotConns, 1)
6933 },
6934 TLSHandshakeDone: func(cfg tls.ConnectionState, err error) {
6935 brokenState.Lock()
6936 defer brokenState.Unlock()
6937 if doBreak {
6938 brokenState.broken = true
6939 }
6940 },
6941 })
6942 req, err := NewRequestWithContext(ctx, "GET", cst.ts.URL, nil)
6943 if err != nil {
6944 t.Fatal(err)
6945 }
6946 _, err = cst.c.Do(req)
6947 if doBreak != (err != nil) {
6948 t.Errorf("for iteration %d, doBreak=%v; unexpected error %v", i, doBreak, err)
6949 }
6950 }
6951 if got, want := atomic.LoadUint32(&gotConns), 1; int(got) != want {
6952 t.Errorf("GotConn calls = %v; want %v", got, want)
6953 }
6954 if got, want := atomic.LoadUint32(&numDials), numReqs; int(got) != want {
6955 t.Errorf("Dials = %v; want %v", got, want)
6956 }
6957 }
6958
6959
6960
6961
6962
6963 func TestTransportDecrementConnWhenIdleConnRemoved(t *testing.T) {
6964 run(t, testTransportDecrementConnWhenIdleConnRemoved, []testMode{http2Mode})
6965 }
6966 func testTransportDecrementConnWhenIdleConnRemoved(t *testing.T, mode testMode) {
6967 CondSkipHTTP2(t)
6968
6969 h := HandlerFunc(func(w ResponseWriter, r *Request) {
6970 _, err := w.Write([]byte("foo"))
6971 if err != nil {
6972 t.Fatalf("Write: %v", err)
6973 }
6974 })
6975
6976 ts := newClientServerTest(t, mode, h).ts
6977
6978 c := ts.Client()
6979 tr := c.Transport.(*Transport)
6980 tr.MaxConnsPerHost = 1
6981
6982 errCh := make(chan error, 300)
6983 doReq := func() {
6984 resp, err := c.Get(ts.URL)
6985 if err != nil {
6986 errCh <- fmt.Errorf("request failed: %v", err)
6987 return
6988 }
6989 defer resp.Body.Close()
6990 _, err = io.ReadAll(resp.Body)
6991 if err != nil {
6992 errCh <- fmt.Errorf("read body failed: %v", err)
6993 }
6994 }
6995
6996 var wg sync.WaitGroup
6997 for i := 0; i < 300; i++ {
6998 wg.Add(1)
6999 go func() {
7000 defer wg.Done()
7001 doReq()
7002 }()
7003 }
7004 wg.Wait()
7005 close(errCh)
7006
7007 for err := range errCh {
7008 t.Errorf("error occurred: %v", err)
7009 }
7010 }
7011
7012
7013
7014
7015 func TestAltProtoCancellation(t *testing.T) {
7016 defer afterTest(t)
7017 tr := &Transport{}
7018 c := &Client{
7019 Transport: tr,
7020 Timeout: time.Millisecond,
7021 }
7022 tr.RegisterProtocol("cancel", cancelProto{})
7023 _, err := c.Get("cancel://bar.com/path")
7024 if err == nil {
7025 t.Error("request unexpectedly succeeded")
7026 } else if !strings.Contains(err.Error(), errCancelProto.Error()) {
7027 t.Errorf("got error %q, does not contain expected string %q", err, errCancelProto)
7028 }
7029 }
7030
7031 var errCancelProto = errors.New("canceled as expected")
7032
7033 type cancelProto struct{}
7034
7035 func (cancelProto) RoundTrip(req *Request) (*Response, error) {
7036 <-req.Cancel
7037 return nil, errCancelProto
7038 }
7039
7040 type roundTripFunc func(r *Request) (*Response, error)
7041
7042 func (f roundTripFunc) RoundTrip(r *Request) (*Response, error) { return f(r) }
7043
7044
7045 func TestIssue32441(t *testing.T) { run(t, testIssue32441, []testMode{http1Mode}) }
7046 func testIssue32441(t *testing.T, mode testMode) {
7047 ts := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
7048 if n, _ := io.Copy(io.Discard, r.Body); n == 0 {
7049 t.Error("body length is zero")
7050 }
7051 })).ts
7052 c := ts.Client()
7053 c.Transport.(*Transport).RegisterProtocol("http", roundTripFunc(func(r *Request) (*Response, error) {
7054
7055 if n, _ := io.Copy(io.Discard, r.Body); n == 0 {
7056 t.Error("body length is zero during round trip")
7057 }
7058 return nil, ErrSkipAltProtocol
7059 }))
7060 if _, err := c.Post(ts.URL, "application/octet-stream", bytes.NewBufferString("data")); err != nil {
7061 t.Error(err)
7062 }
7063 }
7064
7065
7066
7067 func TestTransportRejectsSignInContentLength(t *testing.T) {
7068 run(t, testTransportRejectsSignInContentLength, []testMode{http1Mode})
7069 }
7070 func testTransportRejectsSignInContentLength(t *testing.T, mode testMode) {
7071 cst := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
7072 w.Header().Set("Content-Length", "+3")
7073 w.Write([]byte("abc"))
7074 })).ts
7075
7076 c := cst.Client()
7077 res, err := c.Get(cst.URL)
7078 if err == nil || res != nil {
7079 t.Fatal("Expected a non-nil error and a nil http.Response")
7080 }
7081 if got, want := err.Error(), `bad Content-Length "+3"`; !strings.Contains(got, want) {
7082 t.Fatalf("Error mismatch\nGot: %q\nWanted substring: %q", got, want)
7083 }
7084 }
7085
7086
7087 type dumpConn struct {
7088 io.Writer
7089 io.Reader
7090 }
7091
7092 func (c *dumpConn) Close() error { return nil }
7093 func (c *dumpConn) LocalAddr() net.Addr { return nil }
7094 func (c *dumpConn) RemoteAddr() net.Addr { return nil }
7095 func (c *dumpConn) SetDeadline(t time.Time) error { return nil }
7096 func (c *dumpConn) SetReadDeadline(t time.Time) error { return nil }
7097 func (c *dumpConn) SetWriteDeadline(t time.Time) error { return nil }
7098
7099
7100
7101 type delegateReader struct {
7102 c chan io.Reader
7103 r io.Reader
7104 }
7105
7106 func (r *delegateReader) Read(p []byte) (int, error) {
7107 if r.r == nil {
7108 var ok bool
7109 if r.r, ok = <-r.c; !ok {
7110 return 0, errors.New("delegate closed")
7111 }
7112 }
7113 return r.r.Read(p)
7114 }
7115
7116 func testTransportRace(req *Request) {
7117 save := req.Body
7118 pr, pw := io.Pipe()
7119 defer pr.Close()
7120 defer pw.Close()
7121 dr := &delegateReader{c: make(chan io.Reader)}
7122
7123 t := &Transport{
7124 Dial: func(net, addr string) (net.Conn, error) {
7125 return &dumpConn{pw, dr}, nil
7126 },
7127 }
7128 defer t.CloseIdleConnections()
7129
7130 quitReadCh := make(chan struct{})
7131
7132 go func() {
7133 defer close(quitReadCh)
7134
7135 req, err := ReadRequest(bufio.NewReader(pr))
7136 if err == nil {
7137
7138
7139 io.Copy(io.Discard, req.Body)
7140 req.Body.Close()
7141 }
7142 select {
7143 case dr.c <- strings.NewReader("HTTP/1.1 204 No Content\r\nConnection: close\r\n\r\n"):
7144 case quitReadCh <- struct{}{}:
7145
7146 close(dr.c)
7147 }
7148 }()
7149
7150 t.RoundTrip(req)
7151
7152
7153
7154 pw.Close()
7155 <-quitReadCh
7156
7157 req.Body = save
7158 }
7159
7160
7161
7162
7163
7164 func TestErrorWriteLoopRace(t *testing.T) {
7165 if testing.Short() {
7166 return
7167 }
7168 t.Parallel()
7169 for i := 0; i < 1000; i++ {
7170 delay := time.Duration(mrand.Intn(5)) * time.Millisecond
7171 ctx, cancel := context.WithTimeout(context.Background(), delay)
7172 defer cancel()
7173
7174 r := bytes.NewBuffer(make([]byte, 10000))
7175 req, err := NewRequestWithContext(ctx, MethodPost, "http://example.com", r)
7176 if err != nil {
7177 t.Fatal(err)
7178 }
7179
7180 testTransportRace(req)
7181 }
7182 }
7183
7184
7185
7186
7187 func TestCancelRequestWhenSharingConnection(t *testing.T) {
7188 run(t, testCancelRequestWhenSharingConnection, []testMode{http1Mode})
7189 }
7190 func testCancelRequestWhenSharingConnection(t *testing.T, mode testMode) {
7191 reqc := make(chan chan struct{}, 2)
7192 ts := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, req *Request) {
7193 ch := make(chan struct{}, 1)
7194 reqc <- ch
7195 <-ch
7196 w.Header().Add("Content-Length", "0")
7197 })).ts
7198
7199 client := ts.Client()
7200 transport := client.Transport.(*Transport)
7201 transport.MaxIdleConns = 1
7202 transport.MaxConnsPerHost = 1
7203
7204 var wg sync.WaitGroup
7205
7206 wg.Add(1)
7207 putidlec := make(chan chan struct{}, 1)
7208 reqerrc := make(chan error, 1)
7209 go func() {
7210 defer wg.Done()
7211 ctx := httptrace.WithClientTrace(context.Background(), &httptrace.ClientTrace{
7212 PutIdleConn: func(error) {
7213
7214
7215 ch := make(chan struct{})
7216 putidlec <- ch
7217 close(putidlec)
7218 <-ch
7219 },
7220 })
7221 req, _ := NewRequestWithContext(ctx, "GET", ts.URL, nil)
7222 res, err := client.Do(req)
7223 if err != nil {
7224 reqerrc <- err
7225 } else {
7226 res.Body.Close()
7227 }
7228 }()
7229
7230
7231
7232 select {
7233 case err := <-reqerrc:
7234 t.Fatalf("request 1: got err %v, want nil", err)
7235 case r1c := <-reqc:
7236 close(r1c)
7237 }
7238 var idlec chan struct{}
7239 select {
7240 case err := <-reqerrc:
7241 t.Fatalf("request 1: got err %v, want nil", err)
7242 case idlec = <-putidlec:
7243 }
7244
7245 wg.Add(1)
7246 cancelctx, cancel := context.WithCancel(context.Background())
7247 go func() {
7248 defer wg.Done()
7249 req, _ := NewRequestWithContext(cancelctx, "GET", ts.URL, nil)
7250 res, err := client.Do(req)
7251 if err == nil {
7252 res.Body.Close()
7253 }
7254 if !errors.Is(err, context.Canceled) {
7255 t.Errorf("request 2: got err %v, want Canceled", err)
7256 }
7257
7258
7259 close(idlec)
7260 }()
7261
7262
7263
7264 r2c := <-reqc
7265 cancel()
7266
7267 <-idlec
7268
7269 close(r2c)
7270 wg.Wait()
7271 }
7272
7273 func TestHandlerAbortRacesBodyRead(t *testing.T) {
7274 run(t, testHandlerAbortRacesBodyRead, http3SkippedMode)
7275 }
7276 func testHandlerAbortRacesBodyRead(t *testing.T, mode testMode) {
7277 ts := newClientServerTest(t, mode, HandlerFunc(func(rw ResponseWriter, req *Request) {
7278 go io.Copy(io.Discard, req.Body)
7279 panic(ErrAbortHandler)
7280 })).ts
7281
7282 var wg sync.WaitGroup
7283 for i := 0; i < 2; i++ {
7284 wg.Add(1)
7285 go func() {
7286 defer wg.Done()
7287 for j := 0; j < 10; j++ {
7288 const reqLen = 6 * 1024 * 1024
7289 req, _ := NewRequest("POST", ts.URL, &io.LimitedReader{R: neverEnding('x'), N: reqLen})
7290 req.ContentLength = reqLen
7291 resp, _ := ts.Client().Transport.RoundTrip(req)
7292 if resp != nil {
7293 resp.Body.Close()
7294 }
7295 }
7296 }()
7297 }
7298 wg.Wait()
7299 }
7300
7301 func TestRequestSanitization(t *testing.T) { run(t, testRequestSanitization) }
7302 func testRequestSanitization(t *testing.T, mode testMode) {
7303 if mode == http2Mode {
7304
7305 t.Skip("https://go.dev/issue/60374 test fails when run with HTTP/2")
7306 }
7307 ts := newClientServerTest(t, mode, HandlerFunc(func(rw ResponseWriter, req *Request) {
7308 if h, ok := req.Header["X-Evil"]; ok {
7309 t.Errorf("request has X-Evil header: %q", h)
7310 }
7311 })).ts
7312 req, _ := NewRequest("GET", ts.URL, nil)
7313 req.Host = "go.dev\r\nX-Evil:evil"
7314 resp, _ := ts.Client().Do(req)
7315 if resp != nil {
7316 resp.Body.Close()
7317 }
7318 }
7319
7320 func TestProxyAuthHeader(t *testing.T) {
7321
7322 run(t, testProxyAuthHeader, []testMode{http1Mode}, testNotParallel)
7323 }
7324 func testProxyAuthHeader(t *testing.T, mode testMode) {
7325 const username = "u"
7326 const password = "@/?!"
7327 cst := newClientServerTest(t, mode, HandlerFunc(func(rw ResponseWriter, req *Request) {
7328
7329
7330 var r2 Request
7331 r2.Header = Header{
7332 "Authorization": req.Header["Proxy-Authorization"],
7333 }
7334 gotuser, gotpass, ok := r2.BasicAuth()
7335 if !ok || gotuser != username || gotpass != password {
7336 t.Errorf("req.BasicAuth() = %q, %q, %v; want %q, %q, true", gotuser, gotpass, ok, username, password)
7337 }
7338 }))
7339 u, err := url.Parse(cst.ts.URL)
7340 if err != nil {
7341 t.Fatal(err)
7342 }
7343 u.User = url.UserPassword(username, password)
7344 t.Setenv("HTTP_PROXY", u.String())
7345 cst.tr.Proxy = ProxyURL(u)
7346 resp, err := cst.c.Get("http://_/")
7347 if err != nil {
7348 t.Fatal(err)
7349 }
7350 resp.Body.Close()
7351 }
7352
7353
7354 func TestTransportReqCancelerCleanupOnRequestBodyWriteError(t *testing.T) {
7355 synctest.Test(t, func(t *testing.T) {
7356 tt := newHTTP1TransportTest(t)
7357
7358 clientConn, netConn := tt.newClientConn("http", "example.tld:80")
7359
7360
7361
7362 bodyr, bodyw := io.Pipe()
7363 t.Cleanup(func() {
7364 bodyr.Close()
7365 bodyw.Close()
7366 })
7367
7368
7369 sentReq, _ := NewRequest("POST", "http://example.tld/", bodyr)
7370 sentReq.ContentLength = 1000
7371 sentReq.Header.Set("Content-Type", "text/plain")
7372 rt := newTestRoundTrip(t, clientConn, sentReq)
7373 _ = netConn.readRequest()
7374
7375
7376 body1 := []byte("1234")
7377 go bodyw.Write(body1)
7378 synctest.Wait()
7379 netConn.wantBytes(body1)
7380 if got, want := clientConn.InFlight(), 1; got != want {
7381 t.Fatalf("with body being written: InFlight = %v, want %v", got, want)
7382 }
7383
7384
7385 netConn.conn.Peer().SetWriteError(errors.New("write error"))
7386 go bodyw.Write([]byte("write fails with an error"))
7387 synctest.Wait()
7388 if got, want := clientConn.InFlight(), 0; got != want {
7389 t.Fatalf("after body write error: InFlight = %v, want %v", got, want)
7390 }
7391 if err := rt.err(); err == nil {
7392 t.Fatalf("after body write error: RoundTrip = %v, want error", err)
7393 }
7394 })
7395 }
7396
7397 func TestTransportResponseBodyDrainReadAndClose(t *testing.T) {
7398 tests := []struct {
7399 name string
7400 read bool
7401 closeEarly bool
7402 closeAfterRead bool
7403 serverTruncate bool
7404 wantReadErr error
7405 wantCloseErr error
7406 wantReuse bool
7407 }{
7408
7409 {
7410 name: "concurrent early close and read to eof",
7411 read: true,
7412 closeEarly: true,
7413 wantReadErr: io.EOF,
7414 wantReuse: true,
7415 },
7416 {
7417 name: "concurrent early close and unexpected read error",
7418 read: true,
7419 closeEarly: true,
7420 serverTruncate: true,
7421 wantReadErr: io.ErrUnexpectedEOF,
7422 wantReuse: false,
7423 },
7424 {
7425 name: "unexpected read error without any close",
7426 read: true,
7427 serverTruncate: true,
7428 wantReadErr: io.ErrUnexpectedEOF,
7429 wantReuse: false,
7430 },
7431 {
7432 name: "unexpected read error followed by close",
7433 read: true,
7434 closeAfterRead: true,
7435 serverTruncate: true,
7436 wantReadErr: io.ErrUnexpectedEOF,
7437 wantCloseErr: io.ErrUnexpectedEOF,
7438 wantReuse: false,
7439 },
7440 {
7441 name: "early close without any read",
7442 read: false,
7443 closeEarly: true,
7444 wantReuse: true,
7445 },
7446 {
7447 name: "read all then close",
7448 read: true,
7449 closeAfterRead: true,
7450 wantReadErr: io.EOF,
7451 wantReuse: true,
7452 },
7453 }
7454
7455 for _, tc := range tests {
7456 synctest.Subtest(t, tc.name, func(t *testing.T) {
7457 tt := newHTTP1TransportTest(t)
7458 req, _ := NewRequest("GET", "http://example.tld/", nil)
7459 rt := tt.roundTrip(req)
7460 conn := tt.wantDial("tcp", "example.tld:80").connect()
7461 conn.readRequest()
7462 conn.writeMessage(
7463 "HTTP/1.1 200 OK",
7464 "Transfer-Encoding: chunked",
7465 "",
7466 )
7467 res := rt.response()
7468
7469 readErr := make(chan error, 1)
7470 if tc.read {
7471 go func() {
7472 var buf [1]byte
7473 _, err := res.Body.Read(buf[:])
7474 readErr <- err
7475 }()
7476
7477 synctest.Wait()
7478 }
7479
7480 if tc.closeEarly {
7481 if err := res.Body.Close(); !errors.Is(err, tc.wantCloseErr) {
7482 t.Fatalf("Close = %v, want %v", err, tc.wantCloseErr)
7483 }
7484 }
7485
7486 if tc.serverTruncate {
7487 conn.conn.CloseWrite()
7488 } else {
7489 conn.writeMessage(
7490 "0",
7491 "",
7492 )
7493 }
7494
7495 if tc.read {
7496 synctest.Wait()
7497 if err := <-readErr; !errors.Is(err, tc.wantReadErr) {
7498 t.Fatalf("Read = %v, want %v", err, tc.wantReadErr)
7499 }
7500 }
7501
7502 if tc.closeAfterRead {
7503 if err := res.Body.Close(); !errors.Is(err, tc.wantCloseErr) {
7504 t.Fatalf("Close = %v, want %v", err, tc.wantCloseErr)
7505 }
7506 }
7507
7508 if !tc.wantReuse {
7509 conn.wantClosed()
7510 } else {
7511 synctest.Wait()
7512 req, _ = NewRequest("GET", "http://example.tld/next", nil)
7513 rt = tt.roundTrip(req)
7514 if got := conn.readRequest().URL.Path; got != "/next" {
7515 t.Fatalf("request path = %q, want /next", got)
7516 }
7517 conn.writeMessage(
7518 "HTTP/1.1 200 OK",
7519 "Content-Length: 0",
7520 "",
7521 )
7522 rt.wantStatus(200)
7523 }
7524 })
7525 }
7526 }
7527
7528 func TestValidateClientRequestTrailers(t *testing.T) {
7529 run(t, testValidateClientRequestTrailers)
7530 }
7531
7532 func testValidateClientRequestTrailers(t *testing.T, mode testMode) {
7533 cst := newClientServerTest(t, mode, HandlerFunc(func(rw ResponseWriter, req *Request) {
7534 rw.Write([]byte("Hello"))
7535 })).ts
7536
7537 cases := []struct {
7538 trailer Header
7539 wantErr string
7540 }{
7541 {Header{"Trx": {"x\r\nX-Another-One"}}, `invalid trailer field value for "Trx"`},
7542 {Header{"\r\nTrx": {"X-Another-One"}}, `invalid trailer field name "\r\nTrx"`},
7543 }
7544
7545 for i, tt := range cases {
7546 testName := fmt.Sprintf("%s%d", mode, i)
7547 t.Run(testName, func(t *testing.T) {
7548 req, err := NewRequest("GET", cst.URL, nil)
7549 if err != nil {
7550 t.Fatal(err)
7551 }
7552 req.Trailer = tt.trailer
7553 res, err := cst.Client().Do(req)
7554 if err == nil {
7555 t.Fatal("Expected an error")
7556 }
7557 if g, w := err.Error(), tt.wantErr; !strings.Contains(g, w) {
7558 t.Fatalf("Mismatched error\n\t%q\ndoes not contain\n\t%q", g, w)
7559 }
7560 if res != nil {
7561 t.Fatal("Unexpected non-nil response")
7562 }
7563 })
7564 }
7565 }
7566
7567 func TestTransportServerProtocols(t *testing.T) {
7568 CondSkipHTTP2(t)
7569 DefaultTransport.(*Transport).CloseIdleConnections()
7570
7571 cert, err := tls.X509KeyPair(testcert.LocalhostCert, testcert.LocalhostKey)
7572 if err != nil {
7573 t.Fatal(err)
7574 }
7575 leafCert, err := x509.ParseCertificate(cert.Certificate[0])
7576 if err != nil {
7577 t.Fatal(err)
7578 }
7579 certpool := x509.NewCertPool()
7580 certpool.AddCert(leafCert)
7581
7582 for _, test := range []struct {
7583 name string
7584 scheme string
7585 setup func(t *testing.T)
7586 transport func(*Transport)
7587 server func(*Server)
7588 want string
7589 }{{
7590 name: "http default",
7591 scheme: "http",
7592 want: "HTTP/1.1",
7593 }, {
7594 name: "https default",
7595 scheme: "https",
7596 transport: func(tr *Transport) {
7597
7598 },
7599 want: "HTTP/1.1",
7600 }, {
7601 name: "https transport protocols include HTTP2",
7602 scheme: "https",
7603 transport: func(tr *Transport) {
7604
7605
7606 tr.Protocols = &Protocols{}
7607 tr.Protocols.SetHTTP1(true)
7608 tr.Protocols.SetHTTP2(true)
7609 },
7610 want: "HTTP/2.0",
7611 }, {
7612 name: "https transport protocols only include HTTP1",
7613 scheme: "https",
7614 transport: func(tr *Transport) {
7615
7616 tr.Protocols = &Protocols{}
7617 tr.Protocols.SetHTTP1(true)
7618 },
7619 want: "HTTP/1.1",
7620 }, {
7621 name: "https transport ForceAttemptHTTP2",
7622 scheme: "https",
7623 transport: func(tr *Transport) {
7624
7625 tr.ForceAttemptHTTP2 = true
7626 },
7627 want: "HTTP/2.0",
7628 }, {
7629 name: "https transport protocols override TLSNextProto",
7630 scheme: "https",
7631 transport: func(tr *Transport) {
7632
7633
7634
7635 tr.Protocols = &Protocols{}
7636 tr.Protocols.SetHTTP1(true)
7637 tr.Protocols.SetHTTP2(true)
7638 tr.TLSNextProto = map[string]func(string, *tls.Conn) RoundTripper{}
7639 },
7640 want: "HTTP/2.0",
7641 }, {
7642 name: "https server disables HTTP2 with TLSNextProto",
7643 scheme: "https",
7644 server: func(srv *Server) {
7645
7646
7647 srv.TLSNextProto = map[string]func(*Server, *tls.Conn, Handler){}
7648 },
7649 want: "HTTP/1.1",
7650 }, {
7651 name: "https server Protocols overrides empty TLSNextProto",
7652 scheme: "https",
7653 server: func(srv *Server) {
7654
7655
7656 srv.Protocols = &Protocols{}
7657 srv.Protocols.SetHTTP1(true)
7658 srv.Protocols.SetHTTP2(true)
7659 srv.TLSNextProto = map[string]func(*Server, *tls.Conn, Handler){}
7660 },
7661 want: "HTTP/2.0",
7662 }, {
7663 name: "https server protocols only include HTTP1",
7664 scheme: "https",
7665 server: func(srv *Server) {
7666 srv.Protocols = &Protocols{}
7667 srv.Protocols.SetHTTP1(true)
7668 },
7669 want: "HTTP/1.1",
7670 }, {
7671 name: "https server protocols include HTTP2",
7672 scheme: "https",
7673 server: func(srv *Server) {
7674 srv.Protocols = &Protocols{}
7675 srv.Protocols.SetHTTP1(true)
7676 srv.Protocols.SetHTTP2(true)
7677 },
7678 want: "HTTP/2.0",
7679 }, {
7680 name: "GODEBUG disables HTTP2 client",
7681 scheme: "https",
7682 setup: func(t *testing.T) {
7683 t.Setenv("GODEBUG", "http2client=0")
7684 },
7685 transport: func(tr *Transport) {
7686
7687
7688 tr.Protocols = &Protocols{}
7689 tr.Protocols.SetHTTP1(true)
7690 tr.Protocols.SetHTTP2(true)
7691 },
7692 want: "HTTP/1.1",
7693 }, {
7694 name: "GODEBUG disables HTTP2 server",
7695 scheme: "https",
7696 setup: func(t *testing.T) {
7697 t.Setenv("GODEBUG", "http2server=0")
7698 },
7699 transport: func(tr *Transport) {
7700
7701
7702 tr.Protocols = &Protocols{}
7703 tr.Protocols.SetHTTP1(true)
7704 tr.Protocols.SetHTTP2(true)
7705 },
7706 want: "HTTP/1.1",
7707 }, {
7708 name: "unencrypted HTTP2 with prior knowledge",
7709 scheme: "http",
7710 transport: func(tr *Transport) {
7711 tr.Protocols = &Protocols{}
7712 tr.Protocols.SetUnencryptedHTTP2(true)
7713 },
7714 server: func(srv *Server) {
7715 srv.Protocols = &Protocols{}
7716 srv.Protocols.SetHTTP1(true)
7717 srv.Protocols.SetUnencryptedHTTP2(true)
7718 },
7719 want: "HTTP/2.0",
7720 }, {
7721 name: "unencrypted HTTP2 only on server",
7722 scheme: "http",
7723 transport: func(tr *Transport) {
7724 tr.Protocols = &Protocols{}
7725 tr.Protocols.SetUnencryptedHTTP2(true)
7726 },
7727 server: func(srv *Server) {
7728 srv.Protocols = &Protocols{}
7729 srv.Protocols.SetUnencryptedHTTP2(true)
7730 },
7731 want: "HTTP/2.0",
7732 }, {
7733 name: "unencrypted HTTP2 with no server support",
7734 scheme: "http",
7735 transport: func(tr *Transport) {
7736 tr.Protocols = &Protocols{}
7737 tr.Protocols.SetUnencryptedHTTP2(true)
7738 },
7739 server: func(srv *Server) {
7740 srv.Protocols = &Protocols{}
7741 srv.Protocols.SetHTTP1(true)
7742 },
7743 want: "error",
7744 }, {
7745 name: "HTTP1 with no server support",
7746 scheme: "http",
7747 transport: func(tr *Transport) {
7748 tr.Protocols = &Protocols{}
7749 tr.Protocols.SetHTTP1(true)
7750 },
7751 server: func(srv *Server) {
7752 srv.Protocols = &Protocols{}
7753 srv.Protocols.SetUnencryptedHTTP2(true)
7754 },
7755 want: "error",
7756 }, {
7757 name: "HTTPS1 with no server support",
7758 scheme: "https",
7759 transport: func(tr *Transport) {
7760 tr.Protocols = &Protocols{}
7761 tr.Protocols.SetHTTP1(true)
7762 },
7763 server: func(srv *Server) {
7764 srv.Protocols = &Protocols{}
7765 srv.Protocols.SetHTTP2(true)
7766 },
7767 want: "error",
7768 }, {
7769
7770 name: "ConfigureServer updates TLSNextProto",
7771 scheme: "https",
7772 transport: func(tr *Transport) {
7773 tr.Protocols = &Protocols{}
7774 tr.Protocols.SetHTTP2(true)
7775 },
7776 server: func(srv *Server) {
7777 srv.TLSNextProto = map[string]func(*http.Server, *tls.Conn, http.Handler){}
7778 testHTTP2ConfigureServer(srv)
7779 },
7780 want: "HTTP/2.0",
7781 }} {
7782 t.Run(test.name, func(t *testing.T) {
7783
7784
7785 srv := &Server{
7786 TLSConfig: &tls.Config{
7787 Certificates: []tls.Certificate{cert},
7788 },
7789 Handler: HandlerFunc(func(w ResponseWriter, req *Request) {
7790 w.Header().Set("X-Proto", req.Proto)
7791 }),
7792 }
7793 tr := &Transport{
7794 TLSClientConfig: &tls.Config{
7795 RootCAs: certpool,
7796 },
7797 }
7798
7799 if test.setup != nil {
7800 test.setup(t)
7801 }
7802 if test.server != nil {
7803 test.server(srv)
7804 }
7805 if test.transport != nil {
7806 test.transport(tr)
7807 } else {
7808 tr.Protocols = &Protocols{}
7809 tr.Protocols.SetHTTP1(true)
7810 tr.Protocols.SetHTTP2(true)
7811 }
7812
7813 listener := newLocalListener(t)
7814 srvc := make(chan error, 1)
7815 go func() {
7816 switch test.scheme {
7817 case "http":
7818 srvc <- srv.Serve(listener)
7819 case "https":
7820 srvc <- srv.ServeTLS(listener, "", "")
7821 }
7822 }()
7823 t.Cleanup(func() {
7824 srv.Close()
7825 <-srvc
7826 })
7827
7828 client := &Client{Transport: tr}
7829 resp, err := client.Get(test.scheme + "://" + listener.Addr().String())
7830 if err != nil {
7831 if test.want == "error" {
7832 return
7833 }
7834 t.Fatal(err)
7835 }
7836 if got := resp.Header.Get("X-Proto"); got != test.want {
7837 t.Fatalf("request proto %q, want %q", got, test.want)
7838 }
7839 })
7840 }
7841 }
7842
7843
7844 func testHTTP2ConfigureServer(s *Server) {
7845 s.Serve(testHTTP2ServerConfig{})
7846 }
7847
7848 type testHTTP2ServerConfig struct {
7849 net.Listener
7850 }
7851
7852 func (testHTTP2ServerConfig) HTTP2Config() HTTP2Config {
7853 return HTTP2Config{}
7854 }
7855 func (testHTTP2ServerConfig) IdleTimeout() time.Duration {
7856 return 0
7857 }
7858 func (testHTTP2ServerConfig) ServeConnFunc(func(ctx context.Context, nc net.Conn, h Handler, sawClientPreface bool, upgradeReq *Request, settings []byte)) {
7859 }
7860
7861 func TestIssue61474(t *testing.T) {
7862 run(t, testIssue61474, []testMode{http2Mode})
7863 }
7864 func testIssue61474(t *testing.T, mode testMode) {
7865 if testing.Short() {
7866 return
7867 }
7868
7869
7870
7871
7872 t.Skip("test is too large")
7873
7874 cst := newClientServerTest(t, mode, HandlerFunc(func(rw ResponseWriter, req *Request) {
7875 }), func(tr *Transport) {
7876 tr.MaxConnsPerHost = 1
7877 })
7878 var wg sync.WaitGroup
7879 defer wg.Wait()
7880 for range 100000 {
7881 wg.Go(func() {
7882 ctx, cancel := context.WithTimeout(t.Context(), 1*time.Millisecond)
7883 defer cancel()
7884 req, _ := NewRequestWithContext(ctx, "GET", cst.ts.URL, nil)
7885 resp, err := cst.c.Do(req)
7886 if err == nil {
7887 resp.Body.Close()
7888 }
7889 })
7890 }
7891 }
7892
View as plain text