1
2
3
4
5 package http2_test
6
7 import (
8 "bufio"
9 "bytes"
10 "compress/gzip"
11 "context"
12 crand "crypto/rand"
13 "crypto/tls"
14 "encoding/hex"
15 "errors"
16 "flag"
17 "fmt"
18 "io"
19 "log"
20 "maps"
21 "math/rand"
22 "net"
23 "net/http"
24 "net/http/httptest"
25 "net/http/httptrace"
26 "net/textproto"
27 "net/url"
28 "os"
29 "reflect"
30 "slices"
31 "sort"
32 "strconv"
33 "strings"
34 "sync"
35 "sync/atomic"
36 "testing"
37 "testing/synctest"
38 "time"
39
40 . "net/http/internal/http2"
41 "net/http/internal/httpcommon"
42
43 "golang.org/x/net/http2/hpack"
44 )
45
46 var (
47 extNet = flag.Bool("extnet", false, "do external network tests")
48 transportHost = flag.String("transporthost", "go.dev", "hostname to use for TestTransport")
49 )
50
51 var tlsConfigInsecure = &tls.Config{InsecureSkipVerify: true}
52
53 var canceledCtx context.Context
54
55 func init() {
56 ctx, cancel := context.WithCancel(context.Background())
57 cancel()
58 canceledCtx = ctx
59 }
60
61
62 func newTransport(t testing.TB, opts ...any) *http.Transport {
63 tr1 := &http.Transport{
64 TLSClientConfig: tlsConfigInsecure,
65 Protocols: protocols("h2"),
66 HTTP2: &http.HTTP2Config{},
67 }
68 for _, o := range opts {
69 switch o := o.(type) {
70 case func(*http.Transport):
71 o(tr1)
72 case func(*http.HTTP2Config):
73 o(tr1.HTTP2)
74 default:
75 t.Fatalf("unknown newTransport option %T", o)
76 }
77 }
78 t.Cleanup(tr1.CloseIdleConnections)
79 return tr1
80 }
81
82 func TestTransportExternal(t *testing.T) {
83 if !*extNet {
84 t.Skip("skipping external network test")
85 }
86 req, _ := http.NewRequest("GET", "https://"+*transportHost+"/", nil)
87 rt := newTransport(t)
88 res, err := rt.RoundTrip(req)
89 if err != nil {
90 t.Fatalf("%v", err)
91 }
92 res.Write(os.Stdout)
93 }
94
95 func TestIdleConnTimeout(t *testing.T) {
96 for _, test := range []struct {
97 name string
98 idleConnTimeout time.Duration
99 wait time.Duration
100 baseTransport *http.Transport
101 wantNewConn bool
102 }{{
103 name: "NoExpiry",
104 idleConnTimeout: 2 * time.Second,
105 wait: 1 * time.Second,
106 baseTransport: nil,
107 wantNewConn: false,
108 }, {
109 name: "H2TransportTimeoutExpires",
110 idleConnTimeout: 1 * time.Second,
111 wait: 2 * time.Second,
112 baseTransport: nil,
113 wantNewConn: true,
114 }, {
115 name: "H1TransportTimeoutExpires",
116 idleConnTimeout: 0 * time.Second,
117 wait: 1 * time.Second,
118 baseTransport: newTransport(t, func(tr1 *http.Transport) {
119 tr1.IdleConnTimeout = 2 * time.Second
120 }),
121 wantNewConn: false,
122 }} {
123 synctestSubtest(t, test.name, func(t *testing.T) {
124 tt := newTestTransport(t, func(tr *http.Transport) {
125 tr.IdleConnTimeout = test.idleConnTimeout
126 })
127 var tc *testClientConn
128 for i := range 3 {
129 req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
130 rt := tt.roundTrip(req)
131
132
133
134
135 wantConn := i == 0 || test.wantNewConn
136 if has := tt.hasConn(); has != wantConn {
137 t.Fatalf("request %v: hasConn=%v, want %v", i, has, wantConn)
138 }
139 if wantConn {
140 tc = tt.getConn()
141
142
143 tc.wantFrameType(FrameSettings)
144 tc.wantFrameType(FrameWindowUpdate)
145 tc.writeSettings()
146 }
147 if tt.hasConn() {
148 t.Fatalf("request %v: Transport has more than one conn", i)
149 }
150
151
152 hf := readFrame[*HeadersFrame](t, tc)
153 tc.writeHeaders(HeadersFrameParam{
154 StreamID: hf.StreamID,
155 EndHeaders: true,
156 EndStream: true,
157 BlockFragment: tc.makeHeaderBlockFragment(
158 ":status", "200",
159 ),
160 })
161 rt.wantStatus(200)
162
163
164 if wantConn {
165 tc.wantFrameType(FrameSettings)
166 }
167
168 time.Sleep(test.wait)
169 if got, want := tc.isClosed(), test.wantNewConn; got != want {
170 t.Fatalf("after waiting %v, conn closed=%v; want %v", test.wait, got, want)
171 }
172 }
173 })
174 }
175 }
176
177 func TestTransportH2c(t *testing.T) {
178 ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) {
179 fmt.Fprintf(w, "Hello, %v, http: %v", r.URL.Path, r.TLS == nil)
180 }, func(s *http.Server) {
181 s.Protocols = protocols("h2c")
182 })
183 req, err := http.NewRequest("GET", ts.URL+"/foobar", nil)
184 if err != nil {
185 t.Fatal(err)
186 }
187 var gotConnCnt int32
188 trace := &httptrace.ClientTrace{
189 GotConn: func(connInfo httptrace.GotConnInfo) {
190 if !connInfo.Reused {
191 atomic.AddInt32(&gotConnCnt, 1)
192 }
193 },
194 }
195 req = req.WithContext(httptrace.WithClientTrace(req.Context(), trace))
196 tr := newTransport(t)
197 tr.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
198 return net.Dial(network, addr)
199 }
200 tr.Protocols = protocols("h2c")
201 res, err := tr.RoundTrip(req)
202 if err != nil {
203 t.Fatal(err)
204 }
205 if res.ProtoMajor != 2 {
206 t.Fatal("proto not h2c")
207 }
208 body, err := io.ReadAll(res.Body)
209 if err != nil {
210 t.Fatal(err)
211 }
212 if got, want := string(body), "Hello, /foobar, http: true"; got != want {
213 t.Fatalf("response got %v, want %v", got, want)
214 }
215 if got, want := gotConnCnt, int32(1); got != want {
216 t.Errorf("Too many got connections: %d", gotConnCnt)
217 }
218 }
219
220 func TestTransport(t *testing.T) {
221 const body = "sup"
222 ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) {
223 io.WriteString(w, body)
224 })
225
226 tr := ts.Client().Transport.(*http.Transport)
227 defer tr.CloseIdleConnections()
228
229 u, err := url.Parse(ts.URL)
230 if err != nil {
231 t.Fatal(err)
232 }
233 for i, m := range []string{"GET", ""} {
234 req := &http.Request{
235 Method: m,
236 URL: u,
237 Header: http.Header{},
238 }
239 res, err := tr.RoundTrip(req)
240 if err != nil {
241 t.Fatalf("%d: %s", i, err)
242 }
243
244 t.Logf("%d: Got res: %+v", i, res)
245 if g, w := res.StatusCode, 200; g != w {
246 t.Errorf("%d: StatusCode = %v; want %v", i, g, w)
247 }
248 if g, w := res.Status, "200 OK"; g != w {
249 t.Errorf("%d: Status = %q; want %q", i, g, w)
250 }
251 wantHeader := http.Header{
252 "Content-Length": []string{"3"},
253 "Content-Type": []string{"text/plain; charset=utf-8"},
254 "Date": []string{"XXX"},
255 }
256
257 if d := res.Header["Date"]; len(d) == 1 {
258 d[0] = "XXX"
259 }
260 if !reflect.DeepEqual(res.Header, wantHeader) {
261 t.Errorf("%d: res Header = %v; want %v", i, res.Header, wantHeader)
262 }
263 if res.Request != req {
264 t.Errorf("%d: Response.Request = %p; want %p", i, res.Request, req)
265 }
266 if res.TLS == nil {
267 t.Errorf("%d: Response.TLS = nil; want non-nil", i)
268 }
269 slurp, err := io.ReadAll(res.Body)
270 if err != nil {
271 t.Errorf("%d: Body read: %v", i, err)
272 } else if string(slurp) != body {
273 t.Errorf("%d: Body = %q; want %q", i, slurp, body)
274 }
275 res.Body.Close()
276 }
277 }
278
279 func TestTransportFailureErrorForHTTP1Response(t *testing.T) {
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301 t.Skip("test is racy")
302
303 const expectedHTTP1PayloadHint = "frame header looked like an HTTP/1.1 header"
304
305 ts := httptest.NewServer(http.NewServeMux())
306 t.Cleanup(ts.Close)
307
308 for _, tc := range []struct {
309 name string
310 maxFrameSize uint32
311 expectedErrorIs error
312 }{
313 {
314 name: "with default max frame size",
315 maxFrameSize: 0,
316 },
317 {
318 name: "with enough frame size to start reading",
319 maxFrameSize: InvalidHTTP1LookingFrameHeader().Length + 1,
320 },
321 } {
322 t.Run(tc.name, func(t *testing.T) {
323 tr := newTransport(t)
324 tr.HTTP2.MaxReadFrameSize = int(tc.maxFrameSize)
325 tr.Protocols = protocols("h2c")
326
327 req, err := http.NewRequest("GET", ts.URL, nil)
328 if err != nil {
329 t.Fatal(err)
330 }
331
332 _, err = tr.RoundTrip(req)
333 if err == nil || !strings.Contains(err.Error(), expectedHTTP1PayloadHint) {
334 t.Errorf("expected error to contain %q, got %v", expectedHTTP1PayloadHint, err)
335 }
336 })
337 }
338 }
339
340 func testTransportReusesConns(t *testing.T, wantSame bool, modReq func(*http.Request)) {
341 ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) {
342 io.WriteString(w, r.RemoteAddr)
343 }, func(ts *httptest.Server) {
344 ts.Config.ConnState = func(c net.Conn, st http.ConnState) {
345 t.Logf("conn %v is now state %v", c.RemoteAddr(), st)
346 }
347 })
348 tr := newTransport(t)
349 get := func() string {
350 req, err := http.NewRequest("GET", ts.URL, nil)
351 if err != nil {
352 t.Fatal(err)
353 }
354 modReq(req)
355 res, err := tr.RoundTrip(req)
356 if err != nil {
357 t.Fatal(err)
358 }
359 defer res.Body.Close()
360 slurp, err := io.ReadAll(res.Body)
361 if err != nil {
362 t.Fatalf("Body read: %v", err)
363 }
364 addr := strings.TrimSpace(string(slurp))
365 if addr == "" {
366 t.Fatalf("didn't get an addr in response")
367 }
368 return addr
369 }
370 first := get()
371 second := get()
372 if got := first == second; got != wantSame {
373 t.Errorf("first and second responses on same connection: %v; want %v", got, wantSame)
374 }
375 }
376
377 func TestTransportReusesConns(t *testing.T) {
378 for _, test := range []struct {
379 name string
380 modReq func(*http.Request)
381 wantSame bool
382 }{{
383 name: "ReuseConn",
384 modReq: func(*http.Request) {},
385 wantSame: true,
386 }, {
387 name: "RequestClose",
388 modReq: func(r *http.Request) { r.Close = true },
389 wantSame: false,
390 }, {
391 name: "ConnClose",
392 modReq: func(r *http.Request) { r.Header.Set("Connection", "close") },
393 wantSame: false,
394 }} {
395 t.Run(test.name, func(t *testing.T) {
396 testTransportReusesConns(t, test.wantSame, test.modReq)
397 })
398 }
399 }
400
401 func TestTransportGetGotConnHooks_HTTP2Transport(t *testing.T) {
402 testTransportGetGotConnHooks(t, false)
403 }
404 func TestTransportGetGotConnHooks_Client(t *testing.T) { testTransportGetGotConnHooks(t, true) }
405
406 func testTransportGetGotConnHooks(t *testing.T, useClient bool) {
407 ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) {
408 io.WriteString(w, r.RemoteAddr)
409 })
410
411 tr := newTransport(t)
412 client := ts.Client()
413
414 var (
415 getConns int32
416 gotConns int32
417 )
418 for i := range 2 {
419 trace := &httptrace.ClientTrace{
420 GetConn: func(hostport string) {
421 atomic.AddInt32(&getConns, 1)
422 },
423 GotConn: func(connInfo httptrace.GotConnInfo) {
424 got := atomic.AddInt32(&gotConns, 1)
425 wantReused, wantWasIdle := false, false
426 if got > 1 {
427 wantReused, wantWasIdle = true, true
428 }
429 if connInfo.Reused != wantReused || connInfo.WasIdle != wantWasIdle {
430 t.Errorf("GotConn %v: Reused=%v (want %v), WasIdle=%v (want %v)", i, connInfo.Reused, wantReused, connInfo.WasIdle, wantWasIdle)
431 }
432 },
433 }
434 req, err := http.NewRequest("GET", ts.URL, nil)
435 if err != nil {
436 t.Fatal(err)
437 }
438 req = req.WithContext(httptrace.WithClientTrace(req.Context(), trace))
439
440 var res *http.Response
441 if useClient {
442 res, err = client.Do(req)
443 } else {
444 res, err = tr.RoundTrip(req)
445 }
446 if err != nil {
447 t.Fatal(err)
448 }
449 res.Body.Close()
450 if get := atomic.LoadInt32(&getConns); get != int32(i+1) {
451 t.Errorf("after request %v, %v calls to GetConns: want %v", i, get, i+1)
452 }
453 if got := atomic.LoadInt32(&gotConns); got != int32(i+1) {
454 t.Errorf("after request %v, %v calls to GotConns: want %v", i, got, i+1)
455 }
456 }
457 }
458
459 func TestTransportAbortClosesPipes(t *testing.T) {
460 shutdown := make(chan struct{})
461 ts := newTestServer(t,
462 func(w http.ResponseWriter, r *http.Request) {
463 w.(http.Flusher).Flush()
464 <-shutdown
465 },
466 )
467 defer close(shutdown)
468
469 errCh := make(chan error)
470 go func() {
471 defer close(errCh)
472 tr := newTransport(t)
473 req, err := http.NewRequest("GET", ts.URL, nil)
474 if err != nil {
475 errCh <- err
476 return
477 }
478 res, err := tr.RoundTrip(req)
479 if err != nil {
480 errCh <- err
481 return
482 }
483 defer res.Body.Close()
484 ts.CloseClientConnections()
485 _, err = io.ReadAll(res.Body)
486 if err == nil {
487 errCh <- errors.New("expected error from res.Body.Read")
488 return
489 }
490 }()
491
492 select {
493 case err := <-errCh:
494 if err != nil {
495 t.Fatal(err)
496 }
497
498 case <-time.After(3 * time.Second):
499 t.Fatal("timeout")
500 }
501 }
502
503
504
505 func TestTransportPath(t *testing.T) {
506 gotc := make(chan *url.URL, 1)
507 ts := newTestServer(t,
508 func(w http.ResponseWriter, r *http.Request) {
509 gotc <- r.URL
510 },
511 )
512
513 tr := newTransport(t)
514 const (
515 path = "/testpath"
516 query = "q=1"
517 )
518 surl := ts.URL + path + "?" + query
519 req, err := http.NewRequest("POST", surl, nil)
520 if err != nil {
521 t.Fatal(err)
522 }
523 c := &http.Client{Transport: tr}
524 res, err := c.Do(req)
525 if err != nil {
526 t.Fatal(err)
527 }
528 defer res.Body.Close()
529 got := <-gotc
530 if got.Path != path {
531 t.Errorf("Read Path = %q; want %q", got.Path, path)
532 }
533 if got.RawQuery != query {
534 t.Errorf("Read RawQuery = %q; want %q", got.RawQuery, query)
535 }
536 }
537
538 func randString(n int) string {
539 rnd := rand.New(rand.NewSource(int64(n)))
540 b := make([]byte, n)
541 for i := range b {
542 b[i] = byte(rnd.Intn(256))
543 }
544 return string(b)
545 }
546
547 func TestTransportBody(t *testing.T) {
548 bodyTests := []struct {
549 body string
550 noContentLen bool
551 }{
552 {body: "some message"},
553 {body: "some message", noContentLen: true},
554 {body: strings.Repeat("a", 1<<20), noContentLen: true},
555 {body: strings.Repeat("a", 1<<20)},
556 {body: randString(16<<10 - 1)},
557 {body: randString(16 << 10)},
558 {body: randString(16<<10 + 1)},
559 {body: randString(512<<10 - 1)},
560 {body: randString(512 << 10)},
561 {body: randString(512<<10 + 1)},
562 {body: randString(1<<20 - 1)},
563 {body: randString(1 << 20)},
564 {body: randString(1<<20 + 2)},
565 }
566
567 type reqInfo struct {
568 req *http.Request
569 slurp []byte
570 err error
571 }
572 gotc := make(chan reqInfo, 1)
573 ts := newTestServer(t,
574 func(w http.ResponseWriter, r *http.Request) {
575 slurp, err := io.ReadAll(r.Body)
576 if err != nil {
577 gotc <- reqInfo{err: err}
578 } else {
579 gotc <- reqInfo{req: r, slurp: slurp}
580 }
581 },
582 )
583
584 for i, tt := range bodyTests {
585 tr := newTransport(t)
586
587 var body io.Reader = strings.NewReader(tt.body)
588 if tt.noContentLen {
589 body = struct{ io.Reader }{body}
590 }
591 req, err := http.NewRequest("POST", ts.URL, body)
592 if err != nil {
593 t.Fatalf("#%d: %v", i, err)
594 }
595 c := &http.Client{Transport: tr}
596 res, err := c.Do(req)
597 if err != nil {
598 t.Fatalf("#%d: %v", i, err)
599 }
600 defer res.Body.Close()
601 ri := <-gotc
602 if ri.err != nil {
603 t.Errorf("#%d: read error: %v", i, ri.err)
604 continue
605 }
606 if got := string(ri.slurp); got != tt.body {
607 t.Errorf("#%d: Read body mismatch.\n got: %q (len %d)\nwant: %q (len %d)", i, shortString(got), len(got), shortString(tt.body), len(tt.body))
608 }
609 wantLen := int64(len(tt.body))
610 if tt.noContentLen && tt.body != "" {
611 wantLen = -1
612 }
613 if ri.req.ContentLength != wantLen {
614 t.Errorf("#%d. handler got ContentLength = %v; want %v", i, ri.req.ContentLength, wantLen)
615 }
616 }
617 }
618
619 func shortString(v string) string {
620 const maxLen = 100
621 if len(v) <= maxLen {
622 return v
623 }
624 return fmt.Sprintf("%v[...%d bytes omitted...]%v", v[:maxLen/2], len(v)-maxLen, v[len(v)-maxLen/2:])
625 }
626
627 type capitalizeReader struct {
628 r io.Reader
629 }
630
631 func (cr capitalizeReader) Read(p []byte) (n int, err error) {
632 n, err = cr.r.Read(p)
633 for i, b := range p[:n] {
634 if b >= 'a' && b <= 'z' {
635 p[i] = b - ('a' - 'A')
636 }
637 }
638 return
639 }
640
641 type flushWriter struct {
642 w io.Writer
643 }
644
645 func (fw flushWriter) Write(p []byte) (n int, err error) {
646 n, err = fw.w.Write(p)
647 if f, ok := fw.w.(http.Flusher); ok {
648 f.Flush()
649 }
650 return
651 }
652
653 func newLocalListener(t *testing.T) net.Listener {
654 ln, err := net.Listen("tcp4", "127.0.0.1:0")
655 if err == nil {
656 return ln
657 }
658 ln, err = net.Listen("tcp6", "[::1]:0")
659 if err != nil {
660 t.Fatal(err)
661 }
662 return ln
663 }
664
665 func TestTransportReqBodyAfterResponse_200(t *testing.T) {
666 synctest.Test(t, func(t *testing.T) {
667 testTransportReqBodyAfterResponse(t, 200)
668 })
669 }
670 func TestTransportReqBodyAfterResponse_403(t *testing.T) {
671 synctest.Test(t, func(t *testing.T) {
672 testTransportReqBodyAfterResponse(t, 403)
673 })
674 }
675
676 func testTransportReqBodyAfterResponse(t *testing.T, status int) {
677 const bodySize = 1 << 10
678
679 tc := newTestClientConn(t)
680 tc.greet()
681
682 body := tc.newRequestBody()
683 body.writeBytes(bodySize / 2)
684 req, _ := http.NewRequest("PUT", "https://dummy.tld/", body)
685 rt := tc.roundTrip(req)
686
687 tc.wantHeaders(wantHeader{
688 streamID: rt.streamID(),
689 endStream: false,
690 header: http.Header{
691 ":authority": []string{"dummy.tld"},
692 ":method": []string{"PUT"},
693 ":path": []string{"/"},
694 },
695 })
696
697
698 tc.writeWindowUpdate(0, bodySize)
699 tc.writeWindowUpdate(rt.streamID(), bodySize)
700
701 tc.wantData(wantData{
702 streamID: rt.streamID(),
703 endStream: false,
704 size: bodySize / 2,
705 })
706
707 tc.writeHeaders(HeadersFrameParam{
708 StreamID: rt.streamID(),
709 EndHeaders: true,
710 EndStream: true,
711 BlockFragment: tc.makeHeaderBlockFragment(
712 ":status", strconv.Itoa(status),
713 ),
714 })
715
716 res := rt.response()
717 if res.StatusCode != status {
718 t.Fatalf("status code = %v; want %v", res.StatusCode, status)
719 }
720
721 body.writeBytes(bodySize / 2)
722 body.closeWithError(io.EOF)
723
724 if status == 200 {
725
726 tc.wantData(wantData{
727 streamID: rt.streamID(),
728 endStream: true,
729 size: bodySize / 2,
730 multiple: true,
731 })
732 } else {
733
734 tc.wantFrameType(FrameRSTStream)
735 }
736
737 rt.wantBody(nil)
738 }
739
740
741 func TestTransportFullDuplex(t *testing.T) {
742 ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) {
743 w.WriteHeader(200)
744 w.(http.Flusher).Flush()
745 io.Copy(flushWriter{w}, capitalizeReader{r.Body})
746 fmt.Fprintf(w, "bye.\n")
747 })
748
749 tr := newTransport(t)
750 c := &http.Client{Transport: tr}
751
752 pr, pw := io.Pipe()
753 req, err := http.NewRequest("PUT", ts.URL, io.NopCloser(pr))
754 if err != nil {
755 t.Fatal(err)
756 }
757 req.ContentLength = -1
758 res, err := c.Do(req)
759 if err != nil {
760 t.Fatal(err)
761 }
762 defer res.Body.Close()
763 if res.StatusCode != 200 {
764 t.Fatalf("StatusCode = %v; want %v", res.StatusCode, 200)
765 }
766 bs := bufio.NewScanner(res.Body)
767 want := func(v string) {
768 if !bs.Scan() {
769 t.Fatalf("wanted to read %q but Scan() = false, err = %v", v, bs.Err())
770 }
771 }
772 write := func(v string) {
773 _, err := io.WriteString(pw, v)
774 if err != nil {
775 t.Fatalf("pipe write: %v", err)
776 }
777 }
778 write("foo\n")
779 want("FOO")
780 write("bar\n")
781 want("BAR")
782 pw.Close()
783 want("bye.")
784 if err := bs.Err(); err != nil {
785 t.Fatal(err)
786 }
787 }
788
789 func TestTransportConnectRequest(t *testing.T) {
790 gotc := make(chan *http.Request, 1)
791 ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) {
792 gotc <- r
793 })
794
795 u, err := url.Parse(ts.URL)
796 if err != nil {
797 t.Fatal(err)
798 }
799
800 tr := newTransport(t)
801 c := &http.Client{Transport: tr}
802
803 tests := []struct {
804 req *http.Request
805 want string
806 }{
807 {
808 req: &http.Request{
809 Method: "CONNECT",
810 Header: http.Header{},
811 URL: u,
812 },
813 want: u.Host,
814 },
815 {
816 req: &http.Request{
817 Method: "CONNECT",
818 Header: http.Header{},
819 URL: u,
820 Host: "example.com:123",
821 },
822 want: "example.com:123",
823 },
824 }
825
826 for i, tt := range tests {
827 res, err := c.Do(tt.req)
828 if err != nil {
829 t.Errorf("%d. RoundTrip = %v", i, err)
830 continue
831 }
832 res.Body.Close()
833 req := <-gotc
834 if req.Method != "CONNECT" {
835 t.Errorf("method = %q; want CONNECT", req.Method)
836 }
837 if req.Host != tt.want {
838 t.Errorf("Host = %q; want %q", req.Host, tt.want)
839 }
840 if req.URL.Host != tt.want {
841 t.Errorf("URL.Host = %q; want %q", req.URL.Host, tt.want)
842 }
843 }
844 }
845
846 type headerType int
847
848 const (
849 noHeader headerType = iota
850 oneHeader
851 splitHeader
852 )
853
854 const (
855 f0 = noHeader
856 f1 = oneHeader
857 f2 = splitHeader
858 d0 = false
859 d1 = true
860 )
861
862
863
864
865
866
867 func TestTransportResPattern_c0h1d0t0(t *testing.T) { testTransportResPattern(t, f0, f1, d0, f0) }
868 func TestTransportResPattern_c0h1d0t1(t *testing.T) { testTransportResPattern(t, f0, f1, d0, f1) }
869 func TestTransportResPattern_c0h1d0t2(t *testing.T) { testTransportResPattern(t, f0, f1, d0, f2) }
870 func TestTransportResPattern_c0h1d1t0(t *testing.T) { testTransportResPattern(t, f0, f1, d1, f0) }
871 func TestTransportResPattern_c0h1d1t1(t *testing.T) { testTransportResPattern(t, f0, f1, d1, f1) }
872 func TestTransportResPattern_c0h1d1t2(t *testing.T) { testTransportResPattern(t, f0, f1, d1, f2) }
873 func TestTransportResPattern_c0h2d0t0(t *testing.T) { testTransportResPattern(t, f0, f2, d0, f0) }
874 func TestTransportResPattern_c0h2d0t1(t *testing.T) { testTransportResPattern(t, f0, f2, d0, f1) }
875 func TestTransportResPattern_c0h2d0t2(t *testing.T) { testTransportResPattern(t, f0, f2, d0, f2) }
876 func TestTransportResPattern_c0h2d1t0(t *testing.T) { testTransportResPattern(t, f0, f2, d1, f0) }
877 func TestTransportResPattern_c0h2d1t1(t *testing.T) { testTransportResPattern(t, f0, f2, d1, f1) }
878 func TestTransportResPattern_c0h2d1t2(t *testing.T) { testTransportResPattern(t, f0, f2, d1, f2) }
879 func TestTransportResPattern_c1h1d0t0(t *testing.T) { testTransportResPattern(t, f1, f1, d0, f0) }
880 func TestTransportResPattern_c1h1d0t1(t *testing.T) { testTransportResPattern(t, f1, f1, d0, f1) }
881 func TestTransportResPattern_c1h1d0t2(t *testing.T) { testTransportResPattern(t, f1, f1, d0, f2) }
882 func TestTransportResPattern_c1h1d1t0(t *testing.T) { testTransportResPattern(t, f1, f1, d1, f0) }
883 func TestTransportResPattern_c1h1d1t1(t *testing.T) { testTransportResPattern(t, f1, f1, d1, f1) }
884 func TestTransportResPattern_c1h1d1t2(t *testing.T) { testTransportResPattern(t, f1, f1, d1, f2) }
885 func TestTransportResPattern_c1h2d0t0(t *testing.T) { testTransportResPattern(t, f1, f2, d0, f0) }
886 func TestTransportResPattern_c1h2d0t1(t *testing.T) { testTransportResPattern(t, f1, f2, d0, f1) }
887 func TestTransportResPattern_c1h2d0t2(t *testing.T) { testTransportResPattern(t, f1, f2, d0, f2) }
888 func TestTransportResPattern_c1h2d1t0(t *testing.T) { testTransportResPattern(t, f1, f2, d1, f0) }
889 func TestTransportResPattern_c1h2d1t1(t *testing.T) { testTransportResPattern(t, f1, f2, d1, f1) }
890 func TestTransportResPattern_c1h2d1t2(t *testing.T) { testTransportResPattern(t, f1, f2, d1, f2) }
891 func TestTransportResPattern_c2h1d0t0(t *testing.T) { testTransportResPattern(t, f2, f1, d0, f0) }
892 func TestTransportResPattern_c2h1d0t1(t *testing.T) { testTransportResPattern(t, f2, f1, d0, f1) }
893 func TestTransportResPattern_c2h1d0t2(t *testing.T) { testTransportResPattern(t, f2, f1, d0, f2) }
894 func TestTransportResPattern_c2h1d1t0(t *testing.T) { testTransportResPattern(t, f2, f1, d1, f0) }
895 func TestTransportResPattern_c2h1d1t1(t *testing.T) { testTransportResPattern(t, f2, f1, d1, f1) }
896 func TestTransportResPattern_c2h1d1t2(t *testing.T) { testTransportResPattern(t, f2, f1, d1, f2) }
897 func TestTransportResPattern_c2h2d0t0(t *testing.T) { testTransportResPattern(t, f2, f2, d0, f0) }
898 func TestTransportResPattern_c2h2d0t1(t *testing.T) { testTransportResPattern(t, f2, f2, d0, f1) }
899 func TestTransportResPattern_c2h2d0t2(t *testing.T) { testTransportResPattern(t, f2, f2, d0, f2) }
900 func TestTransportResPattern_c2h2d1t0(t *testing.T) { testTransportResPattern(t, f2, f2, d1, f0) }
901 func TestTransportResPattern_c2h2d1t1(t *testing.T) { testTransportResPattern(t, f2, f2, d1, f1) }
902 func TestTransportResPattern_c2h2d1t2(t *testing.T) { testTransportResPattern(t, f2, f2, d1, f2) }
903
904 func testTransportResPattern(t *testing.T, expect100Continue, resHeader headerType, withData bool, trailers headerType) {
905 synctest.Test(t, func(t *testing.T) {
906 testTransportResPatternBubble(t, expect100Continue, resHeader, withData, trailers)
907 })
908 }
909 func testTransportResPatternBubble(t *testing.T, expect100Continue, resHeader headerType, withData bool, trailers headerType) {
910 const reqBody = "some request body"
911 const resBody = "some response body"
912
913 if resHeader == noHeader {
914
915
916 panic("invalid combination")
917 }
918
919 tc := newTestClientConn(t)
920 tc.greet()
921
922 req, _ := http.NewRequest("POST", "https://dummy.tld/", strings.NewReader(reqBody))
923 if expect100Continue != noHeader {
924 req.Header.Set("Expect", "100-continue")
925 }
926 rt := tc.roundTrip(req)
927
928 tc.wantFrameType(FrameHeaders)
929
930
931 tc.writeHeadersMode(expect100Continue, HeadersFrameParam{
932 StreamID: rt.streamID(),
933 EndHeaders: true,
934 EndStream: false,
935 BlockFragment: tc.makeHeaderBlockFragment(
936 ":status", "100",
937 ),
938 })
939
940
941 tc.wantData(wantData{
942 streamID: rt.streamID(),
943 endStream: true,
944 size: len(reqBody),
945 })
946
947 hdr := []string{
948 ":status", "200",
949 "x-foo", "blah",
950 "x-bar", "more",
951 }
952 if trailers != noHeader {
953 hdr = append(hdr, "trailer", "some-trailer")
954 }
955 tc.writeHeadersMode(resHeader, HeadersFrameParam{
956 StreamID: rt.streamID(),
957 EndHeaders: true,
958 EndStream: withData == false && trailers == noHeader,
959 BlockFragment: tc.makeHeaderBlockFragment(hdr...),
960 })
961 if withData {
962 endStream := trailers == noHeader
963 tc.writeData(rt.streamID(), endStream, []byte(resBody))
964 }
965 tc.writeHeadersMode(trailers, HeadersFrameParam{
966 StreamID: rt.streamID(),
967 EndHeaders: true,
968 EndStream: true,
969 BlockFragment: tc.makeHeaderBlockFragment(
970 "some-trailer", "some-value",
971 ),
972 })
973
974 rt.wantStatus(200)
975 if !withData {
976 rt.wantBody(nil)
977 } else {
978 rt.wantBody([]byte(resBody))
979 }
980 if trailers == noHeader {
981 rt.wantTrailers(nil)
982 } else {
983 rt.wantTrailers(http.Header{
984 "Some-Trailer": {"some-value"},
985 })
986 }
987 }
988
989
990 func TestTransportUnknown1xx(t *testing.T) { synctest.Test(t, testTransportUnknown1xx) }
991 func testTransportUnknown1xx(t *testing.T) {
992 var buf bytes.Buffer
993 SetTestHookGot1xx(t, func(code int, header textproto.MIMEHeader) error {
994 fmt.Fprintf(&buf, "code=%d header=%v\n", code, header)
995 return nil
996 })
997
998 tc := newTestClientConn(t)
999 tc.greet()
1000
1001 req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
1002 rt := tc.roundTrip(req)
1003
1004 for i := 110; i <= 114; i++ {
1005 tc.writeHeaders(HeadersFrameParam{
1006 StreamID: rt.streamID(),
1007 EndHeaders: true,
1008 EndStream: false,
1009 BlockFragment: tc.makeHeaderBlockFragment(
1010 ":status", fmt.Sprint(i),
1011 "foo-bar", fmt.Sprint(i),
1012 ),
1013 })
1014 }
1015 tc.writeHeaders(HeadersFrameParam{
1016 StreamID: rt.streamID(),
1017 EndHeaders: true,
1018 EndStream: true,
1019 BlockFragment: tc.makeHeaderBlockFragment(
1020 ":status", "204",
1021 ),
1022 })
1023
1024 res := rt.response()
1025 if res.StatusCode != 204 {
1026 t.Fatalf("status code = %v; want 204", res.StatusCode)
1027 }
1028 want := `code=110 header=map[Foo-Bar:[110]]
1029 code=111 header=map[Foo-Bar:[111]]
1030 code=112 header=map[Foo-Bar:[112]]
1031 code=113 header=map[Foo-Bar:[113]]
1032 code=114 header=map[Foo-Bar:[114]]
1033 `
1034 if got := buf.String(); got != want {
1035 t.Errorf("Got trace:\n%s\nWant:\n%s", got, want)
1036 }
1037 }
1038
1039 func TestTransportReceiveUndeclaredTrailer(t *testing.T) {
1040 synctest.Test(t, testTransportReceiveUndeclaredTrailer)
1041 }
1042 func testTransportReceiveUndeclaredTrailer(t *testing.T) {
1043 tc := newTestClientConn(t)
1044 tc.greet()
1045
1046 req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
1047 rt := tc.roundTrip(req)
1048
1049 tc.writeHeaders(HeadersFrameParam{
1050 StreamID: rt.streamID(),
1051 EndHeaders: true,
1052 EndStream: false,
1053 BlockFragment: tc.makeHeaderBlockFragment(
1054 ":status", "200",
1055 ),
1056 })
1057 tc.writeHeaders(HeadersFrameParam{
1058 StreamID: rt.streamID(),
1059 EndHeaders: true,
1060 EndStream: true,
1061 BlockFragment: tc.makeHeaderBlockFragment(
1062 "some-trailer", "I'm an undeclared Trailer!",
1063 ),
1064 })
1065
1066 rt.wantStatus(200)
1067 rt.wantBody(nil)
1068 rt.wantTrailers(http.Header{
1069 "Some-Trailer": []string{"I'm an undeclared Trailer!"},
1070 })
1071 }
1072
1073 func TestTransportInvalidTrailer_Pseudo1(t *testing.T) {
1074 testTransportInvalidTrailer_Pseudo(t, oneHeader)
1075 }
1076 func TestTransportInvalidTrailer_Pseudo2(t *testing.T) {
1077 testTransportInvalidTrailer_Pseudo(t, splitHeader)
1078 }
1079 func testTransportInvalidTrailer_Pseudo(t *testing.T, trailers headerType) {
1080 testInvalidTrailer(t, trailers, PseudoHeaderError(":colon"),
1081 ":colon", "foo",
1082 "foo", "bar",
1083 )
1084 }
1085
1086 func TestTransportInvalidTrailer_Capital1(t *testing.T) {
1087 testTransportInvalidTrailer_Capital(t, oneHeader)
1088 }
1089 func TestTransportInvalidTrailer_Capital2(t *testing.T) {
1090 testTransportInvalidTrailer_Capital(t, splitHeader)
1091 }
1092 func testTransportInvalidTrailer_Capital(t *testing.T, trailers headerType) {
1093 testInvalidTrailer(t, trailers, HeaderFieldNameError("Capital"),
1094 "foo", "bar",
1095 "Capital", "bad",
1096 )
1097 }
1098 func TestTransportInvalidTrailer_EmptyFieldName(t *testing.T) {
1099 testInvalidTrailer(t, oneHeader, HeaderFieldNameError(""),
1100 "", "bad",
1101 )
1102 }
1103 func TestTransportInvalidTrailer_BinaryFieldValue(t *testing.T) {
1104 testInvalidTrailer(t, oneHeader, HeaderFieldValueError("x"),
1105 "x", "has\nnewline",
1106 )
1107 }
1108
1109 func testInvalidTrailer(t *testing.T, mode headerType, wantErr error, trailers ...string) {
1110 synctest.Test(t, func(t *testing.T) {
1111 testInvalidTrailerBubble(t, mode, wantErr, trailers...)
1112 })
1113 }
1114 func testInvalidTrailerBubble(t *testing.T, mode headerType, wantErr error, trailers ...string) {
1115 tc := newTestClientConn(t)
1116 tc.greet()
1117
1118 req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
1119 rt := tc.roundTrip(req)
1120
1121 tc.writeHeaders(HeadersFrameParam{
1122 StreamID: rt.streamID(),
1123 EndHeaders: true,
1124 EndStream: false,
1125 BlockFragment: tc.makeHeaderBlockFragment(
1126 ":status", "200",
1127 "trailer", "declared",
1128 ),
1129 })
1130 tc.writeHeadersMode(mode, HeadersFrameParam{
1131 StreamID: rt.streamID(),
1132 EndHeaders: true,
1133 EndStream: true,
1134 BlockFragment: tc.makeHeaderBlockFragment(trailers...),
1135 })
1136
1137 rt.wantStatus(200)
1138 body, err := rt.readBody()
1139 se, ok := err.(StreamError)
1140 if !ok || se.Cause != wantErr {
1141 t.Fatalf("res.Body ReadAll error = %q, %#v; want StreamError with cause %T, %#v", body, err, wantErr, wantErr)
1142 }
1143 if len(body) > 0 {
1144 t.Fatalf("body = %q; want nothing", body)
1145 }
1146 }
1147
1148
1149
1150
1151
1152 func headerListSize(h http.Header) (size uint32) {
1153 for k, vv := range h {
1154 for _, v := range vv {
1155 hf := hpack.HeaderField{Name: k, Value: v}
1156 size += hf.Size()
1157 }
1158 }
1159 return size
1160 }
1161
1162
1163
1164
1165
1166
1167
1168
1169 func padHeaders(t *testing.T, h http.Header, limit uint64, filler string) {
1170 if limit > 0xffffffff {
1171 t.Fatalf("padHeaders: refusing to pad to more than 2^32-1 bytes. limit = %v", limit)
1172 }
1173 hf := hpack.HeaderField{Name: "Pad-Headers", Value: ""}
1174 minPadding := uint64(hf.Size())
1175 size := uint64(headerListSize(h))
1176
1177 minlimit := size + minPadding
1178 if limit < minlimit {
1179 t.Fatalf("padHeaders: limit %v < %v", limit, minlimit)
1180 }
1181
1182
1183
1184 nameFmt := "Pad-Headers-%06d"
1185 hf = hpack.HeaderField{Name: fmt.Sprintf(nameFmt, 1), Value: filler}
1186 fieldSize := uint64(hf.Size())
1187
1188
1189
1190 limit = limit - minPadding
1191 for i := 0; size+fieldSize < limit; i++ {
1192 name := fmt.Sprintf(nameFmt, i)
1193 h.Add(name, filler)
1194 size += fieldSize
1195 }
1196
1197
1198 remain := limit - size
1199 lastValue := strings.Repeat("*", int(remain))
1200 h.Add("Pad-Headers", lastValue)
1201 }
1202
1203 func TestPadHeaders(t *testing.T) {
1204 check := func(h http.Header, limit uint32, fillerLen int) {
1205 if h == nil {
1206 h = make(http.Header)
1207 }
1208 filler := strings.Repeat("f", fillerLen)
1209 padHeaders(t, h, uint64(limit), filler)
1210 gotSize := headerListSize(h)
1211 if gotSize != limit {
1212 t.Errorf("Got size = %v; want %v", gotSize, limit)
1213 }
1214 }
1215
1216 hf := hpack.HeaderField{Name: "Pad-Headers", Value: ""}
1217 minLimit := hf.Size()
1218 for limit := minLimit; limit <= 128; limit++ {
1219 for fillerLen := 0; uint32(fillerLen) <= limit; fillerLen++ {
1220 check(nil, limit, fillerLen)
1221 }
1222 }
1223
1224
1225
1226
1227
1228
1229 tests := []struct {
1230 fillerLen int
1231 limit uint32
1232 }{
1233 {
1234 fillerLen: 64,
1235 limit: 1024,
1236 },
1237 {
1238 fillerLen: 1024,
1239 limit: 1286,
1240 },
1241 {
1242 fillerLen: 256,
1243 limit: 2048,
1244 },
1245 {
1246 fillerLen: 1024,
1247 limit: 10 * 1024,
1248 },
1249 {
1250 fillerLen: 1023,
1251 limit: 11 * 1024,
1252 },
1253 }
1254 h := make(http.Header)
1255 for _, tc := range tests {
1256 check(nil, tc.limit, tc.fillerLen)
1257 check(h, tc.limit, tc.fillerLen)
1258 }
1259 }
1260
1261 func TestTransportChecksRequestHeaderListSize(t *testing.T) {
1262 synctest.Test(t, testTransportChecksRequestHeaderListSize)
1263 }
1264 func testTransportChecksRequestHeaderListSize(t *testing.T) {
1265 const peerSize = 16 << 10
1266
1267 tc := newTestClientConn(t)
1268 tc.greet(Setting{SettingMaxHeaderListSize, peerSize})
1269
1270 checkRoundTrip := func(req *http.Request, wantErr error, desc string) {
1271 t.Helper()
1272 rt := tc.roundTrip(req)
1273 if wantErr != nil {
1274 if err := rt.err(); !errors.Is(err, wantErr) {
1275 t.Errorf("%v: RoundTrip err = %v; want %v", desc, err, wantErr)
1276 }
1277 return
1278 }
1279
1280 tc.wantFrameType(FrameHeaders)
1281 tc.writeHeaders(HeadersFrameParam{
1282 StreamID: rt.streamID(),
1283 EndHeaders: true,
1284 EndStream: true,
1285 BlockFragment: tc.makeHeaderBlockFragment(
1286 ":status", "200",
1287 ),
1288 })
1289
1290 rt.wantStatus(http.StatusOK)
1291 }
1292 headerListSizeForRequest := func(req *http.Request) (size uint64) {
1293 _, err := httpcommon.EncodeHeaders(context.Background(), httpcommon.EncodeHeadersParam{
1294 Request: httpcommon.Request{
1295 Header: req.Header,
1296 Trailer: req.Trailer,
1297 URL: req.URL,
1298 Host: req.Host,
1299 Method: req.Method,
1300 ActualContentLength: req.ContentLength,
1301 },
1302 AddGzipHeader: true,
1303 PeerMaxHeaderListSize: 0xffffffffffffffff,
1304 }, func(name, value string) {
1305 hf := hpack.HeaderField{Name: name, Value: value}
1306 size += uint64(hf.Size())
1307 })
1308 if err != nil {
1309 t.Fatal(err)
1310 }
1311 return size
1312 }
1313
1314
1315
1316 newRequest := func() *http.Request {
1317
1318 const bodytext = "hello"
1319 body := strings.NewReader(bodytext)
1320 req, err := http.NewRequest("POST", "https://example.tld/", body)
1321 if err != nil {
1322 t.Fatalf("newRequest: NewRequest: %v", err)
1323 }
1324 req.ContentLength = int64(len(bodytext))
1325 req.Header = http.Header{"User-Agent": nil}
1326 return req
1327 }
1328
1329
1330 req := newRequest()
1331 req.Trailer = make(http.Header)
1332 filler := strings.Repeat("*", 1024)
1333 padHeaders(t, req.Trailer, peerSize, filler)
1334
1335
1336 defaultBytes := headerListSizeForRequest(req)
1337 padHeaders(t, req.Header, peerSize-defaultBytes, filler)
1338 checkRoundTrip(req, nil, "Headers & Trailers under limit")
1339
1340
1341 req = newRequest()
1342 padHeaders(t, req.Header, peerSize, filler)
1343 checkRoundTrip(req, ErrRequestHeaderListSize, "Headers over limit")
1344
1345
1346 req = newRequest()
1347 req.Trailer = make(http.Header)
1348 padHeaders(t, req.Trailer, peerSize+1, filler)
1349 checkRoundTrip(req, ErrRequestHeaderListSize, "Trailers over limit")
1350
1351
1352 req = newRequest()
1353 filler = strings.Repeat("*", int(peerSize))
1354 req.Header.Set("Big", filler)
1355 checkRoundTrip(req, ErrRequestHeaderListSize, "Single large header")
1356
1357
1358 req = newRequest()
1359 req.Trailer = make(http.Header)
1360 req.Trailer.Set("Big", filler)
1361 checkRoundTrip(req, ErrRequestHeaderListSize, "Single large trailer")
1362 }
1363
1364 func TestTransportChecksResponseHeaderListSize(t *testing.T) {
1365 t.Run("headers", func(t *testing.T) {
1366 synctest.Test(t, testTransportChecksResponseHeaderListSize)
1367 })
1368 t.Run("trailers", func(t *testing.T) {
1369 synctest.Test(t, testTransportChecksResponseTrailerHeaderListSize)
1370 })
1371 }
1372
1373 func testTransportChecksResponseHeaderListSize(t *testing.T) {
1374 tc := newTestClientConn(t)
1375 tc.greet()
1376
1377 req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
1378 rt := tc.roundTrip(req)
1379
1380 tc.wantFrameType(FrameHeaders)
1381
1382 hdr := []string{":status", "200"}
1383 large := strings.Repeat("a", 1<<10)
1384 for range 5042 {
1385 hdr = append(hdr, large, large)
1386 }
1387 hbf := tc.makeHeaderBlockFragment(hdr...)
1388
1389
1390
1391 if size, want := len(hbf), 6329; size != want {
1392 t.Fatalf("encoding over 10MB of duplicate keypairs took %d bytes; expected %d", size, want)
1393 }
1394 tc.writeHeaders(HeadersFrameParam{
1395 StreamID: rt.streamID(),
1396 EndHeaders: true,
1397 EndStream: true,
1398 BlockFragment: hbf,
1399 })
1400
1401 res, err := rt.result()
1402 if e, ok := err.(StreamError); ok {
1403 err = e.Cause
1404 }
1405 if err != ErrResponseHeaderListSize {
1406 size := int64(0)
1407 if res != nil {
1408 res.Body.Close()
1409 for k, vv := range res.Header {
1410 for _, v := range vv {
1411 size += int64(len(k)) + int64(len(v)) + 32
1412 }
1413 }
1414 }
1415 t.Fatalf("RoundTrip Error = %v (and %d bytes of response headers); want errResponseHeaderListSize", err, size)
1416 }
1417 }
1418
1419 func testTransportChecksResponseTrailerHeaderListSize(t *testing.T) {
1420 tc := newTestClientConn(t)
1421 tc.greet()
1422
1423 req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
1424 rt := tc.roundTrip(req)
1425
1426 tc.wantFrameType(FrameHeaders)
1427 tc.writeHeaders(HeadersFrameParam{
1428 StreamID: rt.streamID(),
1429 EndHeaders: true,
1430 EndStream: false,
1431 BlockFragment: tc.makeHeaderBlockFragment(
1432 ":status", "200",
1433 "trailer", "x-trailer",
1434 ),
1435 })
1436 rt.wantStatus(200)
1437
1438 var hdr []string
1439 large := strings.Repeat("a", 1<<10)
1440 for range 5042 {
1441 hdr = append(hdr, large, large)
1442 }
1443 hbf := tc.makeHeaderBlockFragment(hdr...)
1444
1445 if size, want := len(hbf), 6328; size != want {
1446 t.Fatalf("encoding over 10MB of duplicate keypairs took %d bytes; expected %d", size, want)
1447 }
1448 tc.writeHeaders(HeadersFrameParam{
1449 StreamID: rt.streamID(),
1450 EndHeaders: true,
1451 EndStream: true,
1452 BlockFragment: hbf,
1453 })
1454
1455 _, err := rt.readBody()
1456 if e, ok := err.(StreamError); ok {
1457 err = e.Cause
1458 }
1459 if err != ErrResponseHeaderListSize {
1460 t.Errorf("Read = %v, want %v", err, ErrResponseHeaderListSize)
1461 }
1462
1463
1464 tc.wantFrameType(FrameRSTStream)
1465 tc.wantIdle()
1466 }
1467
1468 func TestTransportCookieHeaderSplit(t *testing.T) { synctest.Test(t, testTransportCookieHeaderSplit) }
1469 func testTransportCookieHeaderSplit(t *testing.T) {
1470 tc := newTestClientConn(t)
1471 tc.greet()
1472
1473 req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
1474 req.Header.Add("Cookie", "a=b;c=d; e=f;")
1475 req.Header.Add("Cookie", "e=f;g=h; ")
1476 req.Header.Add("Cookie", "i=j")
1477 rt := tc.roundTrip(req)
1478
1479 tc.wantHeaders(wantHeader{
1480 streamID: rt.streamID(),
1481 endStream: true,
1482 header: http.Header{
1483 "cookie": []string{"a=b", "c=d", "e=f", "e=f", "g=h", "i=j"},
1484 },
1485 })
1486 tc.writeHeaders(HeadersFrameParam{
1487 StreamID: rt.streamID(),
1488 EndHeaders: true,
1489 EndStream: true,
1490 BlockFragment: tc.makeHeaderBlockFragment(
1491 ":status", "204",
1492 ),
1493 })
1494
1495 if err := rt.err(); err != nil {
1496 t.Fatalf("RoundTrip = %v, want success", err)
1497 }
1498 }
1499
1500
1501
1502
1503 func TestTransportBodyReadErrorType(t *testing.T) {
1504 doPanic := make(chan bool, 1)
1505 ts := newTestServer(t,
1506 func(w http.ResponseWriter, r *http.Request) {
1507 w.(http.Flusher).Flush()
1508 <-doPanic
1509 panic("boom")
1510 },
1511 optQuiet,
1512 )
1513
1514 tr := newTransport(t)
1515 c := &http.Client{Transport: tr}
1516
1517 res, err := c.Get(ts.URL)
1518 if err != nil {
1519 t.Fatal(err)
1520 }
1521 defer res.Body.Close()
1522 doPanic <- true
1523 buf := make([]byte, 100)
1524 n, err := res.Body.Read(buf)
1525 got, ok := err.(StreamError)
1526 want := StreamError{StreamID: 0x1, Code: 0x2}
1527 if !ok || got.StreamID != want.StreamID || got.Code != want.Code {
1528 t.Errorf("Read = %v, %#v; want error %#v", n, err, want)
1529 }
1530 }
1531
1532
1533
1534
1535 func TestTransportDoubleCloseOnWriteError(t *testing.T) {
1536 var (
1537 mu sync.Mutex
1538 conn net.Conn
1539 )
1540
1541 ts := newTestServer(t,
1542 func(w http.ResponseWriter, r *http.Request) {
1543 mu.Lock()
1544 defer mu.Unlock()
1545 if conn != nil {
1546 conn.Close()
1547 }
1548 },
1549 )
1550
1551 tr := newTransport(t)
1552 tr.DialTLS = func(network, addr string) (net.Conn, error) {
1553 tc, err := tls.Dial(network, addr, tlsConfigInsecure)
1554 if err != nil {
1555 return nil, err
1556 }
1557 mu.Lock()
1558 defer mu.Unlock()
1559 conn = tc
1560 return tc, nil
1561 }
1562 c := &http.Client{Transport: tr}
1563 c.Get(ts.URL)
1564 }
1565
1566
1567
1568
1569 func TestTransportDisableKeepAlives(t *testing.T) {
1570 ts := newTestServer(t,
1571 func(w http.ResponseWriter, r *http.Request) {
1572 io.WriteString(w, "hi")
1573 },
1574 )
1575
1576 connClosed := make(chan struct{})
1577 tr := newTransport(t)
1578 tr.Dial = func(network, addr string) (net.Conn, error) {
1579 tc, err := net.Dial(network, addr)
1580 if err != nil {
1581 return nil, err
1582 }
1583 return ¬eCloseConn{Conn: tc, closefn: func() { close(connClosed) }}, nil
1584 }
1585 tr.DisableKeepAlives = true
1586 c := &http.Client{Transport: tr}
1587 res, err := c.Get(ts.URL)
1588 if err != nil {
1589 t.Fatal(err)
1590 }
1591 if _, err := io.ReadAll(res.Body); err != nil {
1592 t.Fatal(err)
1593 }
1594 defer res.Body.Close()
1595
1596 select {
1597 case <-connClosed:
1598 case <-time.After(1 * time.Second):
1599 t.Errorf("timeout")
1600 }
1601
1602 }
1603
1604
1605
1606 func TestTransportDisableKeepAlives_Concurrency(t *testing.T) {
1607 const D = 25 * time.Millisecond
1608 ts := newTestServer(t,
1609 func(w http.ResponseWriter, r *http.Request) {
1610 time.Sleep(D)
1611 io.WriteString(w, "hi")
1612 },
1613 )
1614
1615 var dials int32
1616 var conns sync.WaitGroup
1617 tr := newTransport(t)
1618 tr.Dial = func(network, addr string) (net.Conn, error) {
1619 tc, err := net.Dial(network, addr)
1620 if err != nil {
1621 return nil, err
1622 }
1623 atomic.AddInt32(&dials, 1)
1624 conns.Add(1)
1625 return ¬eCloseConn{Conn: tc, closefn: func() { conns.Done() }}, nil
1626 }
1627 tr.DisableKeepAlives = true
1628 c := &http.Client{Transport: tr}
1629 var reqs sync.WaitGroup
1630 const N = 20
1631 for i := range N {
1632 reqs.Add(1)
1633 if i == N-1 {
1634
1635
1636
1637
1638
1639
1640 time.Sleep(D * 2)
1641 }
1642 go func() {
1643 defer reqs.Done()
1644 res, err := c.Get(ts.URL)
1645 if err != nil {
1646 t.Error(err)
1647 return
1648 }
1649 if _, err := io.ReadAll(res.Body); err != nil {
1650 t.Error(err)
1651 return
1652 }
1653 res.Body.Close()
1654 }()
1655 }
1656 reqs.Wait()
1657 conns.Wait()
1658 t.Logf("did %d dials, %d requests", atomic.LoadInt32(&dials), N)
1659 }
1660
1661 type noteCloseConn struct {
1662 net.Conn
1663 onceClose sync.Once
1664 closefn func()
1665 }
1666
1667 func (c *noteCloseConn) Close() error {
1668 c.onceClose.Do(c.closefn)
1669 return c.Conn.Close()
1670 }
1671
1672 func isTimeout(err error) bool {
1673 switch err := err.(type) {
1674 case nil:
1675 return false
1676 case *url.Error:
1677 return isTimeout(err.Err)
1678 case net.Error:
1679 return err.Timeout()
1680 }
1681 return false
1682 }
1683
1684
1685 func TestTransportResponseHeaderTimeout_NoBody(t *testing.T) {
1686 synctest.Test(t, func(t *testing.T) {
1687 testTransportResponseHeaderTimeout(t, false)
1688 })
1689 }
1690 func TestTransportResponseHeaderTimeout_Body(t *testing.T) {
1691 synctest.Test(t, func(t *testing.T) {
1692 testTransportResponseHeaderTimeout(t, true)
1693 })
1694 }
1695
1696 func testTransportResponseHeaderTimeout(t *testing.T, body bool) {
1697 const bodySize = 4 << 20
1698 tc := newTestClientConn(t, func(t1 *http.Transport) {
1699 t1.ResponseHeaderTimeout = 5 * time.Millisecond
1700 })
1701 tc.greet()
1702
1703 var req *http.Request
1704 var reqBody *testRequestBody
1705 if body {
1706 reqBody = tc.newRequestBody()
1707 reqBody.writeBytes(bodySize)
1708 reqBody.closeWithError(io.EOF)
1709 req, _ = http.NewRequest("POST", "https://dummy.tld/", reqBody)
1710 req.Header.Set("Content-Type", "text/foo")
1711 } else {
1712 req, _ = http.NewRequest("GET", "https://dummy.tld/", nil)
1713 }
1714
1715 rt := tc.roundTrip(req)
1716
1717 tc.wantFrameType(FrameHeaders)
1718
1719 tc.writeWindowUpdate(0, bodySize)
1720 tc.writeWindowUpdate(rt.streamID(), bodySize)
1721
1722 if body {
1723 tc.wantData(wantData{
1724 endStream: true,
1725 size: bodySize,
1726 multiple: true,
1727 })
1728 }
1729
1730 time.Sleep(4 * time.Millisecond)
1731 if rt.done() {
1732 t.Fatalf("RoundTrip is done after 4ms; want still waiting")
1733 }
1734 time.Sleep(1 * time.Millisecond)
1735
1736 if err := rt.err(); !isTimeout(err) {
1737 t.Fatalf("RoundTrip error: %v; want timeout error", err)
1738 }
1739 }
1740
1741
1742
1743
1744
1745 func TestTransportSettingsFlowControlUpdateBeyondLimit(t *testing.T) {
1746 synctest.Test(t, testTransportSettingsFlowControlUpdateBeyondLimit)
1747 }
1748 func testTransportSettingsFlowControlUpdateBeyondLimit(t *testing.T) {
1749 tc := newTestClientConn(t)
1750 tc.greet()
1751
1752 body := tc.newRequestBody()
1753 req, _ := http.NewRequest("GET", "https://dummy.tld/", body)
1754 rt := tc.roundTrip(req)
1755 tc.wantFrameType(FrameHeaders)
1756
1757
1758 const windowIncrease = 1000
1759 tc.writeWindowUpdate(rt.streamID(), windowIncrease)
1760 tc.wantIdle()
1761
1762
1763 const maxWindowSize = (1 << 31) - 1
1764 const maxInitialWindowSize = maxWindowSize - windowIncrease
1765 tc.writeSettings(Setting{SettingInitialWindowSize, maxInitialWindowSize + 1})
1766 tc.wantSettingsAck()
1767
1768
1769 body.writeBytes(1)
1770 body.closeWithError(io.EOF)
1771
1772 tc.wantGoAway(0, ErrCodeFlowControl)
1773 }
1774
1775
1776
1777 func TestTransportSettingsFlowControlUpdateWithinLimit(t *testing.T) {
1778 synctest.Test(t, testTransportSettingsFlowControlUpdateWithinLimit)
1779 }
1780 func testTransportSettingsFlowControlUpdateWithinLimit(t *testing.T) {
1781 tc := newTestClientConn(t)
1782 tc.greet()
1783
1784 body := tc.newRequestBody()
1785 req, _ := http.NewRequest("GET", "https://dummy.tld/", body)
1786 rt := tc.roundTrip(req)
1787 tc.wantFrameType(FrameHeaders)
1788
1789
1790 const windowIncrease = 1000
1791 tc.writeWindowUpdate(rt.streamID(), windowIncrease)
1792 tc.wantIdle()
1793
1794
1795 const maxWindowSize = (1 << 31) - 1
1796 const maxInitialWindowSize = maxWindowSize - windowIncrease
1797 tc.writeSettings(Setting{SettingInitialWindowSize, maxInitialWindowSize})
1798 tc.wantSettingsAck()
1799
1800 body.writeBytes(1)
1801 body.closeWithError(io.EOF)
1802 tc.wantData(wantData{
1803 streamID: 1,
1804 endStream: true,
1805 size: 1,
1806 multiple: true,
1807 })
1808 tc.wantIdle()
1809 }
1810
1811
1812 func TestTransportWindowUpdateBeyondLimit(t *testing.T) {
1813 synctest.Test(t, testTransportWindowUpdateBeyondLimit)
1814 }
1815 func testTransportWindowUpdateBeyondLimit(t *testing.T) {
1816 const windowIncrease uint32 = (1 << 31) - 1
1817 tc := newTestClientConn(t)
1818 tc.greet()
1819
1820 req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
1821 rt := tc.roundTrip(req)
1822 tc.wantHeaders(wantHeader{
1823 streamID: rt.streamID(),
1824 endStream: true,
1825 })
1826
1827 tc.writeWindowUpdate(rt.streamID(), windowIncrease)
1828 tc.wantRSTStream(rt.streamID(), ErrCodeFlowControl)
1829
1830 tc.writeWindowUpdate(0, windowIncrease)
1831 tc.wantClosed()
1832 }
1833
1834 func TestTransportDisableCompression(t *testing.T) {
1835 const body = "sup"
1836 ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) {
1837 want := http.Header{
1838 "User-Agent": []string{"Go-http-client/2.0"},
1839 }
1840 if !reflect.DeepEqual(r.Header, want) {
1841 t.Errorf("request headers = %v; want %v", r.Header, want)
1842 }
1843 })
1844
1845 tr := newTransport(t)
1846 tr.DisableCompression = true
1847
1848 req, err := http.NewRequest("GET", ts.URL, nil)
1849 if err != nil {
1850 t.Fatal(err)
1851 }
1852 res, err := tr.RoundTrip(req)
1853 if err != nil {
1854 t.Fatal(err)
1855 }
1856 defer res.Body.Close()
1857 }
1858
1859
1860 func TestTransportRejectsConnHeaders(t *testing.T) {
1861 ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) {
1862 var got []string
1863 for k := range r.Header {
1864 got = append(got, k)
1865 }
1866 sort.Strings(got)
1867 w.Header().Set("Got-Header", strings.Join(got, ","))
1868 })
1869
1870 tr := newTransport(t)
1871
1872 tests := []struct {
1873 key string
1874 value []string
1875 want string
1876 }{
1877 {
1878 key: "Upgrade",
1879 value: []string{"anything"},
1880 want: "ERROR: http2: invalid Upgrade request header: [\"anything\"]",
1881 },
1882 {
1883 key: "Connection",
1884 value: []string{"foo"},
1885 want: "ERROR: http2: invalid Connection request header: [\"foo\"]",
1886 },
1887 {
1888 key: "Connection",
1889 value: []string{"close"},
1890 want: "Accept-Encoding,User-Agent",
1891 },
1892 {
1893 key: "Connection",
1894 value: []string{"CLoSe"},
1895 want: "Accept-Encoding,User-Agent",
1896 },
1897 {
1898 key: "Connection",
1899 value: []string{"close", "something-else"},
1900 want: "ERROR: http2: invalid Connection request header: [\"close\" \"something-else\"]",
1901 },
1902 {
1903 key: "Connection",
1904 value: []string{"keep-alive"},
1905 want: "Accept-Encoding,User-Agent",
1906 },
1907 {
1908 key: "Connection",
1909 value: []string{"Keep-ALIVE"},
1910 want: "Accept-Encoding,User-Agent",
1911 },
1912 {
1913 key: "Proxy-Connection",
1914 value: []string{"keep-alive"},
1915 want: "Accept-Encoding,User-Agent",
1916 },
1917 {
1918 key: "Transfer-Encoding",
1919 value: []string{""},
1920 want: "Accept-Encoding,User-Agent",
1921 },
1922 {
1923 key: "Transfer-Encoding",
1924 value: []string{"foo"},
1925 want: "ERROR: http2: invalid Transfer-Encoding request header: [\"foo\"]",
1926 },
1927 {
1928 key: "Transfer-Encoding",
1929 value: []string{"chunked"},
1930 want: "Accept-Encoding,User-Agent",
1931 },
1932 {
1933 key: "Transfer-Encoding",
1934 value: []string{"chunKed"},
1935 want: "ERROR: http2: invalid Transfer-Encoding request header: [\"chunKed\"]",
1936 },
1937 {
1938 key: "Transfer-Encoding",
1939 value: []string{"chunked", "other"},
1940 want: "ERROR: http2: invalid Transfer-Encoding request header: [\"chunked\" \"other\"]",
1941 },
1942 {
1943 key: "Content-Length",
1944 value: []string{"123"},
1945 want: "Accept-Encoding,User-Agent",
1946 },
1947 {
1948 key: "Keep-Alive",
1949 value: []string{"doop"},
1950 want: "Accept-Encoding,User-Agent",
1951 },
1952 }
1953
1954 for _, tt := range tests {
1955 req, _ := http.NewRequest("GET", ts.URL, nil)
1956 req.Header[tt.key] = tt.value
1957 res, err := tr.RoundTrip(req)
1958 var got string
1959 if err != nil {
1960 got = fmt.Sprintf("ERROR: %v", err)
1961 } else {
1962 got = res.Header.Get("Got-Header")
1963 res.Body.Close()
1964 }
1965 if got != tt.want {
1966 t.Errorf("For key %q, value %q, got = %q; want %q", tt.key, tt.value, got, tt.want)
1967 }
1968 }
1969 }
1970
1971
1972
1973 func TestTransportRejectsContentLengthWithSign(t *testing.T) {
1974 tests := []struct {
1975 name string
1976 cl []string
1977 wantCL string
1978 }{
1979 {
1980 name: "proper content-length",
1981 cl: []string{"3"},
1982 wantCL: "3",
1983 },
1984 {
1985 name: "ignore cl with plus sign",
1986 cl: []string{"+3"},
1987 wantCL: "",
1988 },
1989 {
1990 name: "ignore cl with minus sign",
1991 cl: []string{"-3"},
1992 wantCL: "",
1993 },
1994 {
1995 name: "max int64, for safe uint64->int64 conversion",
1996 cl: []string{"9223372036854775807"},
1997 wantCL: "9223372036854775807",
1998 },
1999 {
2000 name: "overflows int64, so ignored",
2001 cl: []string{"9223372036854775808"},
2002 wantCL: "",
2003 },
2004 }
2005
2006 for _, tt := range tests {
2007 t.Run(tt.name, func(t *testing.T) {
2008 ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) {
2009 w.Header().Set("Content-Length", tt.cl[0])
2010 })
2011 tr := newTransport(t)
2012
2013 req, _ := http.NewRequest("HEAD", ts.URL, nil)
2014 res, err := tr.RoundTrip(req)
2015
2016 var got string
2017 if err != nil {
2018 got = fmt.Sprintf("ERROR: %v", err)
2019 } else {
2020 got = res.Header.Get("Content-Length")
2021 res.Body.Close()
2022 }
2023
2024 if got != tt.wantCL {
2025 t.Fatalf("Got: %q\nWant: %q", got, tt.wantCL)
2026 }
2027 })
2028 }
2029 }
2030
2031
2032
2033
2034 func TestTransportResponseContentLength(t *testing.T) {
2035 tests := []struct {
2036 name string
2037 clValues []string
2038 wantLen int
2039 }{
2040 {
2041 name: "single value",
2042 clValues: []string{"3"},
2043 wantLen: 3,
2044 },
2045 {
2046 name: "identical duplicate values",
2047 clValues: []string{"3", "3", "3"},
2048 wantLen: 3,
2049 },
2050 {
2051 name: "different duplicate values",
2052 clValues: []string{"3", "1", "3"},
2053 wantLen: -1,
2054 },
2055 {
2056 name: "extraneous whitespace",
2057 clValues: []string{" 3"},
2058 wantLen: -1,
2059 },
2060 {
2061 name: "identical duplicate values with extraneous whitespace",
2062 clValues: []string{"3", "3", " 3"},
2063 wantLen: -1,
2064 },
2065 {
2066 name: "plus sign",
2067 clValues: []string{"+3"},
2068 wantLen: -1,
2069 },
2070 {
2071 name: "non-numeric",
2072 clValues: []string{"abc"},
2073 wantLen: -1,
2074 },
2075 {
2076 name: "empty value",
2077 clValues: []string{""},
2078 wantLen: -1,
2079 },
2080 {
2081 name: "no header",
2082 wantLen: -1,
2083 },
2084 }
2085 for _, tt := range tests {
2086 synctestSubtest(t, tt.name, func(t *testing.T) {
2087 tc := newTestClientConn(t)
2088 tc.greet()
2089
2090 req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
2091 rt := tc.roundTrip(req)
2092
2093 headers := []string{":status", "200"}
2094 for _, val := range tt.clValues {
2095 headers = append(headers, "content-length", val)
2096 }
2097 tc.wantFrameType(FrameHeaders)
2098 tc.writeHeaders(HeadersFrameParam{
2099 StreamID: rt.streamID(),
2100 EndHeaders: true,
2101 BlockFragment: tc.makeHeaderBlockFragment(headers...),
2102 })
2103 body := slices.Repeat([]byte("a"), max(tt.wantLen, 1))
2104 tc.writeData(rt.streamID(), true, body)
2105
2106 res := rt.response()
2107 rt.wantBody(body)
2108
2109 if res.ContentLength != int64(tt.wantLen) {
2110 t.Errorf("got ContentLength = %d, want %d", res.ContentLength, tt.wantLen)
2111 }
2112 var wantHeader []string
2113 if tt.wantLen >= 0 {
2114 wantHeader = []string{strconv.FormatInt(int64(tt.wantLen), 10)}
2115 }
2116 if got := res.Header["Content-Length"]; !slices.Equal(got, wantHeader) {
2117 t.Errorf("got Header[%q] = %q, want %q", "Content-Length", got, wantHeader)
2118 }
2119 })
2120 }
2121 }
2122
2123
2124
2125
2126 func TestTransportResponseConnHeaders(t *testing.T) {
2127 tests := []struct {
2128 name string
2129 fields []string
2130 wantHeader http.Header
2131 }{
2132 {
2133 name: "unaffected header",
2134 fields: []string{"content-type", "text/plain"},
2135 wantHeader: http.Header{"Content-Type": {"text/plain"}},
2136 },
2137 {
2138 name: "transfer-encoding",
2139 fields: []string{"transfer-encoding", "chunked"},
2140 },
2141 {
2142 name: "transfer-encoding alongside content-length",
2143 fields: []string{"content-length", "-1", "transfer-encoding", "chunked"},
2144 },
2145 {
2146 name: "connection and keep-alive",
2147 fields: []string{"connection", "keep-alive", "keep-alive", "timeout=5"},
2148 },
2149 {
2150 name: "proxy-connection",
2151 fields: []string{"proxy-connection", "keep-alive"},
2152 },
2153 {
2154 name: "upgrade",
2155 fields: []string{"upgrade", "websocket"},
2156 },
2157 }
2158 for _, tt := range tests {
2159 synctestSubtest(t, tt.name, func(t *testing.T) {
2160 tc := newTestClientConn(t)
2161 tc.greet()
2162
2163 req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
2164 rt := tc.roundTrip(req)
2165
2166 headers := []string{":status", "200"}
2167 headers = append(headers, tt.fields...)
2168 tc.wantFrameType(FrameHeaders)
2169 tc.writeHeaders(HeadersFrameParam{
2170 StreamID: rt.streamID(),
2171 EndHeaders: true,
2172 BlockFragment: tc.makeHeaderBlockFragment(headers...),
2173 })
2174 tc.writeData(rt.streamID(), true, []byte("body"))
2175
2176 res := rt.response()
2177 rt.wantBody([]byte("body"))
2178
2179 if !maps.EqualFunc(res.Header, tt.wantHeader, slices.Equal) {
2180 t.Errorf("got Header = %q, want %q", res.Header, tt.wantHeader)
2181 }
2182 })
2183 }
2184 }
2185
2186
2187
2188 func TestTransportFailsOnInvalidHeadersAndTrailers(t *testing.T) {
2189 ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) {
2190 var got []string
2191 for k := range r.Header {
2192 got = append(got, k)
2193 }
2194 sort.Strings(got)
2195 w.Header().Set("Got-Header", strings.Join(got, ","))
2196 })
2197
2198 tests := [...]struct {
2199 h http.Header
2200 t http.Header
2201 wantErr string
2202 }{
2203 0: {
2204 h: http.Header{"with space": {"foo"}},
2205 wantErr: `net/http: invalid header field name "with space"`,
2206 },
2207 1: {
2208 h: http.Header{"name": {"Брэд"}},
2209 wantErr: "",
2210 },
2211 2: {
2212 h: http.Header{"имя": {"Brad"}},
2213 wantErr: `net/http: invalid header field name "имя"`,
2214 },
2215 3: {
2216 h: http.Header{"foo": {"foo\x01bar"}},
2217 wantErr: `net/http: invalid header field value for "foo"`,
2218 },
2219 4: {
2220 t: http.Header{"foo": {"foo\x01bar"}},
2221 wantErr: `net/http: invalid trailer field value for "foo"`,
2222 },
2223 5: {
2224 t: http.Header{"x-\r\nda": {"foo\x01bar"}},
2225 wantErr: `net/http: invalid trailer field name "x-\r\nda"`,
2226 },
2227 }
2228
2229 tr := newTransport(t)
2230
2231 for i, tt := range tests {
2232 req, _ := http.NewRequest("GET", ts.URL, nil)
2233 req.Header = tt.h
2234 if req.Header == nil {
2235 req.Header = http.Header{}
2236 }
2237 req.Trailer = tt.t
2238 res, err := tr.RoundTrip(req)
2239 var bad bool
2240 if tt.wantErr == "" {
2241 if err != nil {
2242 bad = true
2243 t.Errorf("case %d: error = %v; want no error", i, err)
2244 }
2245 } else {
2246 if !strings.Contains(fmt.Sprint(err), tt.wantErr) {
2247 bad = true
2248 t.Errorf("case %d: error = %v; want error %q", i, err, tt.wantErr)
2249 }
2250 }
2251 if err == nil {
2252 if bad {
2253 t.Logf("case %d: server got headers %q", i, res.Header.Get("Got-Header"))
2254 }
2255 res.Body.Close()
2256 }
2257 }
2258 }
2259
2260
2261
2262
2263 func TestTransportReadHeadResponse(t *testing.T) { synctest.Test(t, testTransportReadHeadResponse) }
2264 func testTransportReadHeadResponse(t *testing.T) {
2265 tc := newTestClientConn(t)
2266 tc.greet()
2267
2268 req, _ := http.NewRequest("HEAD", "https://dummy.tld/", nil)
2269 rt := tc.roundTrip(req)
2270
2271 tc.wantFrameType(FrameHeaders)
2272 tc.writeHeaders(HeadersFrameParam{
2273 StreamID: rt.streamID(),
2274 EndHeaders: true,
2275 EndStream: false,
2276 BlockFragment: tc.makeHeaderBlockFragment(
2277 ":status", "200",
2278 "content-length", "123",
2279 ),
2280 })
2281 tc.writeData(rt.streamID(), true, nil)
2282
2283 res := rt.response()
2284 if res.ContentLength != 123 {
2285 t.Fatalf("Content-Length = %d; want 123", res.ContentLength)
2286 }
2287 rt.wantBody(nil)
2288 }
2289
2290 func TestTransportReadHeadResponseWithBody(t *testing.T) {
2291 synctest.Test(t, testTransportReadHeadResponseWithBody)
2292 }
2293 func testTransportReadHeadResponseWithBody(t *testing.T) {
2294
2295
2296 log.SetOutput(io.Discard)
2297 defer log.SetOutput(os.Stderr)
2298
2299 response := "redirecting to /elsewhere"
2300 tc := newTestClientConn(t)
2301 tc.greet()
2302
2303 req, _ := http.NewRequest("HEAD", "https://dummy.tld/", nil)
2304 rt := tc.roundTrip(req)
2305
2306 tc.wantFrameType(FrameHeaders)
2307 tc.writeHeaders(HeadersFrameParam{
2308 StreamID: rt.streamID(),
2309 EndHeaders: true,
2310 EndStream: false,
2311 BlockFragment: tc.makeHeaderBlockFragment(
2312 ":status", "200",
2313 "content-length", strconv.Itoa(len(response)),
2314 ),
2315 })
2316 tc.writeData(rt.streamID(), true, []byte(response))
2317
2318 res := rt.response()
2319 if res.ContentLength != int64(len(response)) {
2320 t.Fatalf("Content-Length = %d; want %d", res.ContentLength, len(response))
2321 }
2322 rt.wantBody(nil)
2323 }
2324
2325 type neverEnding byte
2326
2327 func (b neverEnding) Read(p []byte) (int, error) {
2328 for i := range p {
2329 p[i] = byte(b)
2330 }
2331 return len(p), nil
2332 }
2333
2334
2335
2336 func TestTransportStreamEndsWhileBodyIsBeingWritten(t *testing.T) {
2337 synctest.Test(t, testTransportStreamEndsWhileBodyIsBeingWritten)
2338 }
2339 func testTransportStreamEndsWhileBodyIsBeingWritten(t *testing.T) {
2340 body := "this is the client request body"
2341 const windowSize = 10
2342
2343 tc := newTestClientConn(t)
2344 tc.greet(Setting{SettingInitialWindowSize, windowSize})
2345
2346
2347 req, _ := http.NewRequest("PUT", "https://dummy.tld/", strings.NewReader(body))
2348 rt := tc.roundTrip(req)
2349 tc.wantFrameType(FrameHeaders)
2350 tc.wantData(wantData{
2351 streamID: rt.streamID(),
2352 endStream: false,
2353 size: windowSize,
2354 })
2355
2356
2357 tc.writeHeaders(HeadersFrameParam{
2358 StreamID: rt.streamID(),
2359 EndHeaders: true,
2360 EndStream: true,
2361 BlockFragment: tc.makeHeaderBlockFragment(
2362 ":status", "413",
2363 ),
2364 })
2365 rt.wantStatus(413)
2366 }
2367
2368 func TestTransportFlowControl(t *testing.T) { synctest.Test(t, testTransportFlowControl) }
2369 func testTransportFlowControl(t *testing.T) {
2370 const maxBuffer = 64 << 10
2371 tc := newTestClientConn(t, func(tr *http.Transport) {
2372 tr.HTTP2 = &http.HTTP2Config{
2373 MaxReceiveBufferPerConnection: maxBuffer,
2374 MaxReceiveBufferPerStream: maxBuffer,
2375 MaxReadFrameSize: 16 << 20,
2376 }
2377 })
2378 tc.greet()
2379
2380 req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
2381 rt := tc.roundTrip(req)
2382 tc.wantFrameType(FrameHeaders)
2383
2384 tc.writeHeaders(HeadersFrameParam{
2385 StreamID: rt.streamID(),
2386 EndHeaders: true,
2387 EndStream: false,
2388 BlockFragment: tc.makeHeaderBlockFragment(
2389 ":status", "200",
2390 ),
2391 })
2392 rt.wantStatus(200)
2393
2394
2395
2396
2397 tc.writeData(rt.streamID(), false, make([]byte, maxBuffer))
2398 tc.wantIdle()
2399
2400
2401
2402 resp := rt.response()
2403 if _, err := io.ReadFull(resp.Body, make([]byte, maxBuffer)); err != nil {
2404 t.Fatalf("io.Body.Read: %v", err)
2405 }
2406 var connTokens, streamTokens uint32
2407 for {
2408 f := tc.readFrame()
2409 if f == nil {
2410 break
2411 }
2412 wu, ok := f.(*WindowUpdateFrame)
2413 if !ok {
2414 t.Fatalf("received unexpected frame %T (want WINDOW_UPDATE)", f)
2415 }
2416 switch wu.StreamID {
2417 case 0:
2418 connTokens += wu.Increment
2419 case wu.StreamID:
2420 streamTokens += wu.Increment
2421 default:
2422 t.Fatalf("received unexpected WINDOW_UPDATE for stream %v", wu.StreamID)
2423 }
2424 }
2425 if got, want := connTokens, uint32(maxBuffer); got != want {
2426 t.Errorf("transport provided %v bytes of connection WINDOW_UPDATE, want %v", got, want)
2427 }
2428 if got, want := streamTokens, uint32(maxBuffer); got != want {
2429 t.Errorf("transport provided %v bytes of stream WINDOW_UPDATE, want %v", got, want)
2430 }
2431 }
2432
2433
2434
2435
2436
2437
2438 func TestTransportUsesGoAwayDebugError_RoundTrip(t *testing.T) {
2439 synctest.Test(t, func(t *testing.T) {
2440 testTransportUsesGoAwayDebugError(t, false)
2441 })
2442 }
2443
2444 func TestTransportUsesGoAwayDebugError_Body(t *testing.T) {
2445 synctest.Test(t, func(t *testing.T) {
2446 testTransportUsesGoAwayDebugError(t, true)
2447 })
2448 }
2449
2450 func testTransportUsesGoAwayDebugError(t *testing.T, failMidBody bool) {
2451 tc := newTestClientConn(t)
2452 tc.greet()
2453
2454 const goAwayErrCode = ErrCodeHTTP11Required
2455 const goAwayDebugData = "some debug data"
2456
2457 req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
2458 rt := tc.roundTrip(req)
2459
2460 tc.wantFrameType(FrameHeaders)
2461
2462 if failMidBody {
2463 tc.writeHeaders(HeadersFrameParam{
2464 StreamID: rt.streamID(),
2465 EndHeaders: true,
2466 EndStream: false,
2467 BlockFragment: tc.makeHeaderBlockFragment(
2468 ":status", "200",
2469 "content-length", "123",
2470 ),
2471 })
2472 }
2473
2474
2475
2476 tc.writeGoAway(5, ErrCodeNo, []byte(goAwayDebugData))
2477 tc.writeGoAway(5, goAwayErrCode, nil)
2478 tc.closeWrite()
2479
2480 res, err := rt.result()
2481 whence := "RoundTrip"
2482 if failMidBody {
2483 whence = "Body.Read"
2484 if err != nil {
2485 t.Fatalf("RoundTrip error = %v, want success", err)
2486 }
2487 _, err = res.Body.Read(make([]byte, 1))
2488 }
2489
2490 want := GoAwayError{
2491 LastStreamID: 5,
2492 ErrCode: goAwayErrCode,
2493 DebugData: goAwayDebugData,
2494 }
2495 if !reflect.DeepEqual(err, want) {
2496 t.Errorf("%v error = %T: %#v, want %T (%#v)", whence, err, err, want, want)
2497 }
2498 }
2499
2500
2501
2502 func TestTransportGoAwayWithNoConns(t *testing.T) { synctest.Test(t, testTransportGoAwayWithNoConns) }
2503 func testTransportGoAwayWithNoConns(t *testing.T) {
2504 tt := newTestTransportWithUnusedConn(t)
2505 tc := tt.getConn()
2506 tc.greet()
2507 tc.writeGoAway(1, ErrCodeNo, nil)
2508 tc.wantClosed()
2509 }
2510
2511 func testTransportReturnsUnusedFlowControl(t *testing.T, oneDataFrame bool) {
2512 tc := newTestClientConn(t)
2513 tc.greet()
2514
2515 req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
2516 rt := tc.roundTrip(req)
2517
2518 tc.wantFrameType(FrameHeaders)
2519 tc.writeHeaders(HeadersFrameParam{
2520 StreamID: rt.streamID(),
2521 EndHeaders: true,
2522 EndStream: false,
2523 BlockFragment: tc.makeHeaderBlockFragment(
2524 ":status", "200",
2525 "content-length", "5000",
2526 ),
2527 })
2528 initialInflow := tc.inflowWindow(0)
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540 const streamNotEnded = false
2541 if oneDataFrame {
2542 tc.writeData(rt.streamID(), streamNotEnded, make([]byte, 5000))
2543 } else {
2544 tc.writeData(rt.streamID(), streamNotEnded, make([]byte, 1))
2545 }
2546
2547 res := rt.response()
2548 if n, err := res.Body.Read(make([]byte, 1)); err != nil || n != 1 {
2549 t.Fatalf("body read = %v, %v; want 1, nil", n, err)
2550 }
2551 res.Body.Close()
2552 synctest.Wait()
2553
2554 sentAdditionalData := false
2555 tc.wantUnorderedFrames(
2556 func(f *RSTStreamFrame) bool {
2557 if f.ErrCode != ErrCodeCancel {
2558 t.Fatalf("Expected a RSTStreamFrame with code cancel; got %v", SummarizeFrame(f))
2559 }
2560 if !oneDataFrame {
2561
2562 tc.writeData(rt.streamID(), streamNotEnded, make([]byte, 4999))
2563 sentAdditionalData = true
2564 }
2565 return true
2566 },
2567 func(f *WindowUpdateFrame) bool {
2568 if !oneDataFrame && !sentAdditionalData {
2569 t.Fatalf("Got WindowUpdateFrame, don't expect one yet")
2570 }
2571 if f.Increment != 5000 {
2572 t.Fatalf("Expected WindowUpdateFrames for 5000 bytes; got %v", SummarizeFrame(f))
2573 }
2574 return true
2575 },
2576 )
2577
2578 if got, want := tc.inflowWindow(0), initialInflow; got != want {
2579 t.Fatalf("connection flow tokens = %v, want %v", got, want)
2580 }
2581 }
2582
2583
2584 func TestTransportReturnsUnusedFlowControlSingleWrite(t *testing.T) {
2585 synctest.Test(t, func(t *testing.T) {
2586 testTransportReturnsUnusedFlowControl(t, true)
2587 })
2588 }
2589
2590
2591 func TestTransportReturnsUnusedFlowControlMultipleWrites(t *testing.T) {
2592 synctest.Test(t, func(t *testing.T) {
2593 testTransportReturnsUnusedFlowControl(t, false)
2594 })
2595 }
2596
2597
2598
2599 func TestTransportAdjustsFlowControl(t *testing.T) { synctest.Test(t, testTransportAdjustsFlowControl) }
2600 func testTransportAdjustsFlowControl(t *testing.T) {
2601 const bodySize = 1 << 20
2602
2603 tc := newTestClientConn(t)
2604 tc.wantFrameType(FrameSettings)
2605 tc.wantFrameType(FrameWindowUpdate)
2606
2607
2608 body := tc.newRequestBody()
2609 body.writeBytes(bodySize)
2610 body.closeWithError(io.EOF)
2611
2612 req, _ := http.NewRequest("POST", "https://dummy.tld/", body)
2613 rt := tc.roundTrip(req)
2614
2615 tc.wantFrameType(FrameHeaders)
2616
2617 gotBytes := int64(0)
2618 for {
2619 f := readFrame[*DataFrame](t, tc)
2620 gotBytes += int64(len(f.Data()))
2621
2622
2623 if gotBytes >= InitialWindowSize/2 {
2624 break
2625 }
2626 }
2627
2628 tc.writeSettings(Setting{ID: SettingInitialWindowSize, Val: bodySize})
2629 tc.writeWindowUpdate(0, bodySize)
2630 tc.writeSettingsAck()
2631
2632 tc.wantUnorderedFrames(
2633 func(f *SettingsFrame) bool { return true },
2634 func(f *DataFrame) bool {
2635 gotBytes += int64(len(f.Data()))
2636 return f.StreamEnded()
2637 },
2638 )
2639
2640 if gotBytes != bodySize {
2641 t.Fatalf("server received %v bytes of body, want %v", gotBytes, bodySize)
2642 }
2643
2644 tc.writeHeaders(HeadersFrameParam{
2645 StreamID: rt.streamID(),
2646 EndHeaders: true,
2647 EndStream: true,
2648 BlockFragment: tc.makeHeaderBlockFragment(
2649 ":status", "200",
2650 ),
2651 })
2652 rt.wantStatus(200)
2653 }
2654
2655
2656 func TestTransportReturnsDataPaddingFlowControl(t *testing.T) {
2657 synctest.Test(t, testTransportReturnsDataPaddingFlowControl)
2658 }
2659 func testTransportReturnsDataPaddingFlowControl(t *testing.T) {
2660 tc := newTestClientConn(t)
2661 tc.greet()
2662
2663 req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
2664 rt := tc.roundTrip(req)
2665
2666 tc.wantFrameType(FrameHeaders)
2667 tc.writeHeaders(HeadersFrameParam{
2668 StreamID: rt.streamID(),
2669 EndHeaders: true,
2670 EndStream: false,
2671 BlockFragment: tc.makeHeaderBlockFragment(
2672 ":status", "200",
2673 "content-length", "5000",
2674 ),
2675 })
2676
2677 initialConnWindow := tc.inflowWindow(0)
2678 initialStreamWindow := tc.inflowWindow(rt.streamID())
2679
2680 pad := make([]byte, 5)
2681 tc.writeDataPadded(rt.streamID(), false, make([]byte, 5000), pad)
2682
2683
2684 synctest.Wait()
2685 if got, want := tc.inflowWindow(0), initialConnWindow-5000; got != want {
2686 t.Errorf("conn inflow window = %v, want %v", got, want)
2687 }
2688 if got, want := tc.inflowWindow(rt.streamID()), initialStreamWindow-5000; got != want {
2689 t.Errorf("stream inflow window = %v, want %v", got, want)
2690 }
2691 }
2692
2693
2694
2695 func TestTransportReturnsErrorOnBadResponseHeaders(t *testing.T) {
2696 synctest.Test(t, testTransportReturnsErrorOnBadResponseHeaders)
2697 }
2698 func testTransportReturnsErrorOnBadResponseHeaders(t *testing.T) {
2699 tc := newTestClientConn(t)
2700 tc.greet()
2701
2702 req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
2703 rt := tc.roundTrip(req)
2704
2705 tc.wantFrameType(FrameHeaders)
2706 tc.writeHeaders(HeadersFrameParam{
2707 StreamID: rt.streamID(),
2708 EndHeaders: true,
2709 EndStream: false,
2710 BlockFragment: tc.makeHeaderBlockFragment(
2711 ":status", "200",
2712 " content-type", "bogus",
2713 ),
2714 })
2715
2716 err := rt.err()
2717 want := StreamError{1, ErrCodeProtocol, HeaderFieldNameError(" content-type")}
2718 if !reflect.DeepEqual(err, want) {
2719 t.Fatalf("RoundTrip error = %#v; want %#v", err, want)
2720 }
2721
2722 fr := readFrame[*RSTStreamFrame](t, tc)
2723 if fr.StreamID != 1 || fr.ErrCode != ErrCodeProtocol {
2724 t.Errorf("Frame = %v; want RST_STREAM for stream 1 with ErrCodeProtocol", SummarizeFrame(fr))
2725 }
2726 }
2727
2728
2729
2730 type byteAndEOFReader byte
2731
2732 func (b byteAndEOFReader) Read(p []byte) (n int, err error) {
2733 if len(p) == 0 {
2734 panic("unexpected useless call")
2735 }
2736 p[0] = byte(b)
2737 return 1, io.EOF
2738 }
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749 func TestTransportBodyDoubleEndStream(t *testing.T) {
2750 synctest.Test(t, testTransportBodyDoubleEndStream)
2751 }
2752 func testTransportBodyDoubleEndStream(t *testing.T) {
2753 ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) {
2754
2755 })
2756
2757 tr := newTransport(t)
2758
2759 for i := range 2 {
2760 req, _ := http.NewRequest("POST", ts.URL, byteAndEOFReader('a'))
2761 req.ContentLength = 1
2762 res, err := tr.RoundTrip(req)
2763 if err != nil {
2764 t.Fatalf("failure on req %d: %v", i+1, err)
2765 }
2766 defer res.Body.Close()
2767 }
2768 }
2769
2770
2771 func TestTransportRequestPathPseudo(t *testing.T) {
2772 type result struct {
2773 path string
2774 err string
2775 }
2776 tests := []struct {
2777 req *http.Request
2778 want result
2779 }{
2780 0: {
2781 req: &http.Request{
2782 Method: "GET",
2783 URL: &url.URL{
2784 Host: "foo.com",
2785 Path: "/foo",
2786 },
2787 },
2788 want: result{path: "/foo"},
2789 },
2790
2791
2792
2793 1: {
2794 req: &http.Request{
2795 Method: "GET",
2796 URL: &url.URL{
2797 Host: "foo.com",
2798 Path: "//foo",
2799 },
2800 },
2801 want: result{path: "//foo"},
2802 },
2803
2804
2805 2: {
2806 req: &http.Request{
2807 Method: "GET",
2808 URL: &url.URL{
2809 Scheme: "https",
2810 Opaque: "//foo.com/path",
2811 Host: "foo.com",
2812 Path: "/ignored",
2813 },
2814 },
2815 want: result{path: "/path"},
2816 },
2817
2818
2819 3: {
2820 req: &http.Request{
2821 Method: "GET",
2822 Host: "bar.com",
2823 URL: &url.URL{
2824 Scheme: "https",
2825 Opaque: "//bar.com/path",
2826 Host: "foo.com",
2827 Path: "/ignored",
2828 },
2829 },
2830 want: result{path: "/path"},
2831 },
2832
2833
2834 4: {
2835 req: &http.Request{
2836 Method: "GET",
2837 URL: &url.URL{
2838 Opaque: "/path",
2839 Host: "foo.com",
2840 Path: "/ignored",
2841 },
2842 },
2843 want: result{path: "/path"},
2844 },
2845
2846
2847 5: {
2848 req: &http.Request{
2849 Method: "GET",
2850 URL: &url.URL{
2851 Scheme: "https",
2852 Opaque: "//unknown_host/path",
2853 Host: "foo.com",
2854 Path: "/ignored",
2855 },
2856 },
2857 want: result{err: `invalid request :path "https://unknown_host/path" from URL.Opaque = "//unknown_host/path"`},
2858 },
2859
2860
2861 6: {
2862 req: &http.Request{
2863 Method: "CONNECT",
2864 URL: &url.URL{
2865 Host: "foo.com",
2866 },
2867 },
2868 want: result{},
2869 },
2870 }
2871 for i, tt := range tests {
2872 hbuf := &bytes.Buffer{}
2873 henc := hpack.NewEncoder(hbuf)
2874 _, err := httpcommon.EncodeHeaders(context.Background(), httpcommon.EncodeHeadersParam{
2875 Request: httpcommon.Request{
2876 Header: tt.req.Header,
2877 Trailer: tt.req.Trailer,
2878 URL: tt.req.URL,
2879 Host: tt.req.Host,
2880 Method: tt.req.Method,
2881 ActualContentLength: tt.req.ContentLength,
2882 },
2883 AddGzipHeader: false,
2884 PeerMaxHeaderListSize: 0xffffffffffffffff,
2885 }, func(name, value string) {
2886 henc.WriteField(hpack.HeaderField{Name: name, Value: value})
2887 })
2888 hdrs := hbuf.Bytes()
2889 var got result
2890 hpackDec := hpack.NewDecoder(InitialHeaderTableSize, func(f hpack.HeaderField) {
2891 if f.Name == ":path" {
2892 got.path = f.Value
2893 }
2894 })
2895 if err != nil {
2896 got.err = err.Error()
2897 } else if len(hdrs) > 0 {
2898 if _, err := hpackDec.Write(hdrs); err != nil {
2899 t.Errorf("%d. bogus hpack: %v", i, err)
2900 continue
2901 }
2902 }
2903 if got != tt.want {
2904 t.Errorf("%d. got %+v; want %+v", i, got, tt.want)
2905 }
2906
2907 }
2908
2909 }
2910
2911
2912
2913 func TestRoundTripDoesntConsumeRequestBodyEarly(t *testing.T) {
2914 synctest.Test(t, testRoundTripDoesntConsumeRequestBodyEarly)
2915 }
2916 func testRoundTripDoesntConsumeRequestBodyEarly(t *testing.T) {
2917 tc := newTestClientConn(t)
2918 tc.greet()
2919 tc.closeWrite()
2920 synctest.Wait()
2921
2922 const body = "foo"
2923 req, _ := http.NewRequest("POST", "http://foo.com/", io.NopCloser(strings.NewReader(body)))
2924 rt := tc.roundTrip(req)
2925 if err := rt.err(); err != ErrClientConnNotEstablished {
2926 t.Fatalf("RoundTrip = %v; want errClientConnNotEstablished", err)
2927 }
2928
2929 slurp, err := io.ReadAll(req.Body)
2930 if err != nil {
2931 t.Errorf("ReadAll = %v", err)
2932 }
2933 if string(slurp) != body {
2934 t.Errorf("Body = %q; want %q", slurp, body)
2935 }
2936 }
2937
2938
2939
2940
2941
2942 func TestTransportCancelDataResponseRace(t *testing.T) {
2943 cancel := make(chan struct{})
2944 clientGotResponse := make(chan bool, 1)
2945
2946 const msg = "Hello."
2947 ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) {
2948 if strings.Contains(r.URL.Path, "/hello") {
2949 time.Sleep(50 * time.Millisecond)
2950 io.WriteString(w, msg)
2951 return
2952 }
2953 for i := range 50 {
2954 io.WriteString(w, "Some data.")
2955 w.(http.Flusher).Flush()
2956 if i == 2 {
2957 <-clientGotResponse
2958 close(cancel)
2959 }
2960 time.Sleep(10 * time.Millisecond)
2961 }
2962 })
2963
2964 tr := newTransport(t)
2965
2966 c := &http.Client{Transport: tr}
2967 req, _ := http.NewRequest("GET", ts.URL, nil)
2968 req.Cancel = cancel
2969 res, err := c.Do(req)
2970 clientGotResponse <- true
2971 if err != nil {
2972 t.Fatal(err)
2973 }
2974 if _, err = io.Copy(io.Discard, res.Body); err == nil {
2975 t.Fatal("unexpected success")
2976 }
2977
2978 res, err = c.Get(ts.URL + "/hello")
2979 if err != nil {
2980 t.Fatal(err)
2981 }
2982 slurp, err := io.ReadAll(res.Body)
2983 if err != nil {
2984 t.Fatal(err)
2985 }
2986 if string(slurp) != msg {
2987 t.Errorf("Got = %q; want %q", slurp, msg)
2988 }
2989 }
2990
2991
2992
2993 func TestTransportNoRaceOnRequestObjectAfterRequestComplete(t *testing.T) {
2994 ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) {
2995 w.WriteHeader(200)
2996 io.WriteString(w, "body")
2997 })
2998
2999 tr := newTransport(t)
3000
3001 req, _ := http.NewRequest("GET", ts.URL, nil)
3002 resp, err := tr.RoundTrip(req)
3003 if err != nil {
3004 t.Fatal(err)
3005 }
3006 if _, err = io.Copy(io.Discard, resp.Body); err != nil {
3007 t.Fatalf("error reading response body: %v", err)
3008 }
3009 if err := resp.Body.Close(); err != nil {
3010 t.Fatalf("error closing response body: %v", err)
3011 }
3012
3013
3014 req.Header = http.Header{}
3015 }
3016
3017 func TestTransportCloseAfterLostPing(t *testing.T) { synctest.Test(t, testTransportCloseAfterLostPing) }
3018 func testTransportCloseAfterLostPing(t *testing.T) {
3019 tc := newTestClientConn(t, func(h2 *http.HTTP2Config) {
3020 h2.PingTimeout = 1 * time.Second
3021 h2.SendPingTimeout = 1 * time.Second
3022 })
3023 tc.greet()
3024
3025 req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
3026 rt := tc.roundTrip(req)
3027 tc.wantFrameType(FrameHeaders)
3028
3029 time.Sleep(1 * time.Second)
3030 tc.wantFrameType(FramePing)
3031
3032 time.Sleep(1 * time.Second)
3033 err := rt.err()
3034 if err == nil || !strings.Contains(err.Error(), "client connection lost") {
3035 t.Fatalf("expected to get error about \"connection lost\", got %v", err)
3036 }
3037 }
3038
3039 func TestTransportPingWriteBlocks(t *testing.T) {
3040 ts := newTestServer(t,
3041 func(w http.ResponseWriter, r *http.Request) {},
3042 )
3043 tr := newTransport(t)
3044 tr.Dial = func(network, addr string) (net.Conn, error) {
3045 s, c := net.Pipe()
3046 go func() {
3047 srv := tls.Server(s, tlsConfigInsecure)
3048 srv.Handshake()
3049
3050
3051
3052
3053 var buf [1024]byte
3054 s.Read(buf[:])
3055 }()
3056 return c, nil
3057 }
3058 tr.HTTP2.PingTimeout = 1 * time.Millisecond
3059 tr.HTTP2.SendPingTimeout = 1 * time.Millisecond
3060 c := &http.Client{Transport: tr}
3061 _, err := c.Get(ts.URL)
3062 if err == nil {
3063 t.Fatalf("Get = nil, want error")
3064 }
3065 }
3066
3067 func TestTransportPingWhenReadingMultiplePings(t *testing.T) {
3068 synctest.Test(t, testTransportPingWhenReadingMultiplePings)
3069 }
3070 func testTransportPingWhenReadingMultiplePings(t *testing.T) {
3071 tc := newTestClientConn(t, func(h2 *http.HTTP2Config) {
3072 h2.SendPingTimeout = 1000 * time.Millisecond
3073 })
3074 tc.greet()
3075
3076 ctx, cancel := context.WithCancel(context.Background())
3077 req, _ := http.NewRequestWithContext(ctx, "GET", "https://dummy.tld/", nil)
3078 rt := tc.roundTrip(req)
3079
3080 tc.wantFrameType(FrameHeaders)
3081 tc.writeHeaders(HeadersFrameParam{
3082 StreamID: rt.streamID(),
3083 EndHeaders: true,
3084 EndStream: false,
3085 BlockFragment: tc.makeHeaderBlockFragment(
3086 ":status", "200",
3087 ),
3088 })
3089
3090 for range 5 {
3091
3092 time.Sleep(999 * time.Millisecond)
3093 if f := tc.readFrame(); f != nil {
3094 t.Fatalf("unexpected frame: %v", f)
3095 }
3096
3097
3098 time.Sleep(1 * time.Millisecond)
3099 f := readFrame[*PingFrame](t, tc)
3100 tc.writePing(true, f.Data)
3101 }
3102
3103
3104 cancel()
3105 synctest.Wait()
3106
3107 tc.wantFrameType(FrameRSTStream)
3108 _, err := rt.readBody()
3109 if err == nil {
3110 t.Fatalf("Response.Body.Read() = %v, want error", err)
3111 }
3112 }
3113
3114 func TestTransportPingWhenReadingPingDisabled(t *testing.T) {
3115 synctest.Test(t, testTransportPingWhenReadingPingDisabled)
3116 }
3117 func testTransportPingWhenReadingPingDisabled(t *testing.T) {
3118 tc := newTestClientConn(t, func(h2 *http.HTTP2Config) {
3119 h2.SendPingTimeout = 0
3120 })
3121 tc.greet()
3122
3123 req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
3124 rt := tc.roundTrip(req)
3125
3126 tc.wantFrameType(FrameHeaders)
3127 tc.writeHeaders(HeadersFrameParam{
3128 StreamID: rt.streamID(),
3129 EndHeaders: true,
3130 EndStream: false,
3131 BlockFragment: tc.makeHeaderBlockFragment(
3132 ":status", "200",
3133 ),
3134 })
3135
3136
3137 time.Sleep(1 * time.Minute)
3138 if f := tc.readFrame(); f != nil {
3139 t.Fatalf("unexpected frame: %v", f)
3140 }
3141 }
3142
3143 func TestTransportRetryAfterGOAWAYNoRetry(t *testing.T) {
3144 synctest.Test(t, testTransportRetryAfterGOAWAYNoRetry)
3145 }
3146 func testTransportRetryAfterGOAWAYNoRetry(t *testing.T) {
3147 tt := newTestTransport(t)
3148
3149 req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
3150 rt := tt.roundTrip(req)
3151
3152
3153
3154
3155
3156 tc := tt.getConn()
3157 tc.wantFrameType(FrameSettings)
3158 tc.wantFrameType(FrameWindowUpdate)
3159 tc.wantHeaders(wantHeader{
3160 streamID: 1,
3161 endStream: true,
3162 })
3163 tc.writeSettings()
3164 tc.writeGoAway(0 , ErrCodeInternal, nil)
3165 if rt.err() == nil {
3166 t.Fatalf("after GOAWAY, RoundTrip is not done, want error")
3167 }
3168 }
3169
3170 func TestTransportRetryAfterGOAWAYRetry(t *testing.T) {
3171 synctest.Test(t, testTransportRetryAfterGOAWAYRetry)
3172 }
3173 func testTransportRetryAfterGOAWAYRetry(t *testing.T) {
3174 tt := newTestTransport(t)
3175
3176 req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
3177 rt := tt.roundTrip(req)
3178
3179
3180
3181
3182
3183 tc := tt.getConn()
3184 tc.wantFrameType(FrameSettings)
3185 tc.wantFrameType(FrameWindowUpdate)
3186 tc.wantHeaders(wantHeader{
3187 streamID: 1,
3188 endStream: true,
3189 })
3190 tc.writeSettings()
3191 tc.writeGoAway(0 , ErrCodeNo, nil)
3192 if rt.done() {
3193 t.Fatalf("after GOAWAY, RoundTrip is done; want it to be retrying")
3194 }
3195
3196
3197 tc = tt.getConn()
3198 tc.wantFrameType(FrameSettings)
3199 tc.wantFrameType(FrameWindowUpdate)
3200 tc.wantHeaders(wantHeader{
3201 streamID: 1,
3202 endStream: true,
3203 })
3204 tc.writeSettings()
3205 tc.writeHeaders(HeadersFrameParam{
3206 StreamID: 1,
3207 EndHeaders: true,
3208 EndStream: true,
3209 BlockFragment: tc.makeHeaderBlockFragment(
3210 ":status", "200",
3211 ),
3212 })
3213
3214 rt.wantStatus(200)
3215 }
3216
3217 func TestTransportRetryAfterGOAWAYSecondRequest(t *testing.T) {
3218 synctest.Test(t, testTransportRetryAfterGOAWAYSecondRequest)
3219 }
3220 func testTransportRetryAfterGOAWAYSecondRequest(t *testing.T) {
3221 tt := newTestTransport(t)
3222
3223
3224 req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
3225 rt1 := tt.roundTrip(req)
3226 tc := tt.getConn()
3227 tc.wantFrameType(FrameSettings)
3228 tc.wantFrameType(FrameWindowUpdate)
3229 tc.wantHeaders(wantHeader{
3230 streamID: 1,
3231 endStream: true,
3232 })
3233 tc.writeSettings()
3234 tc.wantFrameType(FrameSettings)
3235 tc.writeHeaders(HeadersFrameParam{
3236 StreamID: 1,
3237 EndHeaders: true,
3238 EndStream: true,
3239 BlockFragment: tc.makeHeaderBlockFragment(
3240 ":status", "200",
3241 ),
3242 })
3243 rt1.wantStatus(200)
3244
3245
3246
3247
3248
3249 req, _ = http.NewRequest("GET", "https://dummy.tld/", nil)
3250 rt2 := tt.roundTrip(req)
3251
3252
3253 tc.wantHeaders(wantHeader{
3254 streamID: 3,
3255 endStream: true,
3256 })
3257 tc.writeSettings()
3258 tc.writeGoAway(1 , ErrCodeProtocol, nil)
3259 if rt2.done() {
3260 t.Fatalf("after GOAWAY, RoundTrip is done; want it to be retrying")
3261 }
3262
3263
3264 tc = tt.getConn()
3265 tc.wantFrameType(FrameSettings)
3266 tc.wantFrameType(FrameWindowUpdate)
3267 tc.wantHeaders(wantHeader{
3268 streamID: 1,
3269 endStream: true,
3270 })
3271 tc.writeSettings()
3272 tc.writeHeaders(HeadersFrameParam{
3273 StreamID: 1,
3274 EndHeaders: true,
3275 EndStream: true,
3276 BlockFragment: tc.makeHeaderBlockFragment(
3277 ":status", "200",
3278 ),
3279 })
3280 rt2.wantStatus(200)
3281 }
3282
3283 func TestTransportRetryAfterRefusedStream(t *testing.T) {
3284 synctest.Test(t, testTransportRetryAfterRefusedStream)
3285 }
3286 func testTransportRetryAfterRefusedStream(t *testing.T) {
3287 tt := newTestTransport(t)
3288
3289 req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
3290 rt := tt.roundTrip(req)
3291
3292
3293 tc := tt.getConn()
3294 tc.wantFrameType(FrameSettings)
3295 tc.wantFrameType(FrameWindowUpdate)
3296 tc.wantHeaders(wantHeader{
3297 streamID: 1,
3298 endStream: true,
3299 })
3300 tc.writeSettings()
3301 tc.wantFrameType(FrameSettings)
3302 tc.writeRSTStream(1, ErrCodeRefusedStream)
3303 if rt.done() {
3304 t.Fatalf("after RST_STREAM, RoundTrip is done; want it to be retrying")
3305 }
3306
3307
3308 tc.wantHeaders(wantHeader{
3309 streamID: 3,
3310 endStream: true,
3311 })
3312 tc.writeSettings()
3313 tc.writeHeaders(HeadersFrameParam{
3314 StreamID: 3,
3315 EndHeaders: true,
3316 EndStream: true,
3317 BlockFragment: tc.makeHeaderBlockFragment(
3318 ":status", "204",
3319 ),
3320 })
3321
3322 rt.wantStatus(204)
3323 }
3324
3325 func TestTransportRetryHasLimit(t *testing.T) { synctest.Test(t, testTransportRetryHasLimit) }
3326 func testTransportRetryHasLimit(t *testing.T) {
3327 tt := newTestTransport(t)
3328
3329 req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
3330 rt := tt.roundTrip(req)
3331
3332 tc := tt.getConn()
3333 tc.netconn.SetReadDeadline(time.Time{})
3334 tc.wantFrameType(FrameSettings)
3335 tc.wantFrameType(FrameWindowUpdate)
3336
3337 count := 0
3338 start := time.Now()
3339 for streamID := uint32(1); !rt.done(); streamID += 2 {
3340 count++
3341 tc.wantHeaders(wantHeader{
3342 streamID: streamID,
3343 endStream: true,
3344 })
3345 if streamID == 1 {
3346 tc.writeSettings()
3347 tc.wantFrameType(FrameSettings)
3348 }
3349 tc.writeRSTStream(streamID, ErrCodeRefusedStream)
3350
3351 if totalDelay := time.Since(start); totalDelay > 5*time.Minute {
3352 t.Fatalf("RoundTrip still retrying after %v, should have given up", totalDelay)
3353 }
3354 synctest.Wait()
3355 }
3356 if got, want := count, 5; got < count {
3357 t.Errorf("RoundTrip made %v attempts, want at least %v", got, want)
3358 }
3359 if rt.err() == nil {
3360 t.Errorf("RoundTrip succeeded, want error")
3361 }
3362 }
3363
3364 func TestTransportResponseDataBeforeHeaders(t *testing.T) {
3365 synctest.Test(t, testTransportResponseDataBeforeHeaders)
3366 }
3367 func testTransportResponseDataBeforeHeaders(t *testing.T) {
3368
3369 log.SetOutput(io.Discard)
3370 t.Cleanup(func() { log.SetOutput(os.Stderr) })
3371
3372 tc := newTestClientConn(t)
3373 tc.greet()
3374
3375
3376 req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
3377 rt1 := tc.roundTrip(req)
3378 tc.wantFrameType(FrameHeaders)
3379 tc.writeHeaders(HeadersFrameParam{
3380 StreamID: rt1.streamID(),
3381 EndHeaders: true,
3382 EndStream: true,
3383 BlockFragment: tc.makeHeaderBlockFragment(
3384 ":status", "200",
3385 ),
3386 })
3387 rt1.wantStatus(200)
3388
3389
3390 rt2 := tc.roundTrip(req)
3391 tc.wantFrameType(FrameHeaders)
3392 tc.writeData(rt2.streamID(), true, []byte("payload"))
3393 if err, ok := rt2.err().(StreamError); !ok || err.Code != ErrCodeProtocol {
3394 t.Fatalf("expected stream PROTOCOL_ERROR, got: %v", err)
3395 }
3396 }
3397
3398 func TestTransportMaxFrameReadSize(t *testing.T) {
3399 for _, test := range []struct {
3400 maxReadFrameSize uint32
3401 want uint32
3402 }{{
3403 maxReadFrameSize: 64000,
3404 want: 64000,
3405 }, {
3406 maxReadFrameSize: 1024,
3407
3408
3409
3410
3411
3412
3413
3414
3415 want: DefaultMaxReadFrameSize,
3416 }} {
3417 synctestSubtest(t, fmt.Sprint(test.maxReadFrameSize), func(t *testing.T) {
3418 tc := newTestClientConn(t, func(h2 *http.HTTP2Config) {
3419 h2.MaxReadFrameSize = int(test.maxReadFrameSize)
3420 })
3421
3422 fr := readFrame[*SettingsFrame](t, tc)
3423 got, ok := fr.Value(SettingMaxFrameSize)
3424 if !ok {
3425 t.Errorf("Transport.MaxReadFrameSize = %v; server got no setting, want %v", test.maxReadFrameSize, test.want)
3426 } else if got != test.want {
3427 t.Errorf("Transport.MaxReadFrameSize = %v; server got %v, want %v", test.maxReadFrameSize, got, test.want)
3428 }
3429 })
3430 }
3431 }
3432
3433 func TestTransportRequestsLowServerLimit(t *testing.T) {
3434 ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) {
3435 }, func(h2 *http.HTTP2Config) {
3436 h2.MaxConcurrentStreams = 1
3437 })
3438
3439 var (
3440 connCountMu sync.Mutex
3441 connCount int
3442 )
3443 tr := newTransport(t)
3444 tr.DialTLS = func(network, addr string) (net.Conn, error) {
3445 connCountMu.Lock()
3446 defer connCountMu.Unlock()
3447 connCount++
3448 return tls.Dial(network, addr, tlsConfigInsecure)
3449 }
3450
3451 const reqCount = 3
3452 for range reqCount {
3453 req, err := http.NewRequest("GET", ts.URL, nil)
3454 if err != nil {
3455 t.Fatal(err)
3456 }
3457 res, err := tr.RoundTrip(req)
3458 if err != nil {
3459 t.Fatal(err)
3460 }
3461 if got, want := res.StatusCode, 200; got != want {
3462 t.Errorf("StatusCode = %v; want %v", got, want)
3463 }
3464 if res != nil && res.Body != nil {
3465 res.Body.Close()
3466 }
3467 }
3468
3469 if connCount != 1 {
3470 t.Errorf("created %v connections for %v requests, want 1", connCount, reqCount)
3471 }
3472 }
3473
3474
3475 func TestTransportRequestsStallAtServerLimit(t *testing.T) {
3476 synctest.Test(t, testTransportRequestsStallAtServerLimit)
3477 }
3478 func testTransportRequestsStallAtServerLimit(t *testing.T) {
3479 const maxConcurrent = 2
3480
3481 tc := newTestClientConn(t, func(h2 *http.HTTP2Config) {
3482 h2.StrictMaxConcurrentRequests = true
3483 })
3484 tc.greet(Setting{SettingMaxConcurrentStreams, maxConcurrent})
3485
3486 cancelClientRequest := make(chan struct{})
3487
3488
3489
3490 var rts []*testRoundTrip
3491 for k := range maxConcurrent + 2 {
3492 req, _ := http.NewRequest("GET", fmt.Sprintf("https://dummy.tld/%d", k), nil)
3493 if k == maxConcurrent {
3494 req.Cancel = cancelClientRequest
3495 }
3496 rt := tc.roundTrip(req)
3497 rts = append(rts, rt)
3498
3499 if k < maxConcurrent {
3500
3501 tc.wantHeaders(wantHeader{
3502 streamID: rt.streamID(),
3503 endStream: true,
3504 header: http.Header{
3505 ":authority": []string{"dummy.tld"},
3506 ":method": []string{"GET"},
3507 ":path": []string{fmt.Sprintf("/%d", k)},
3508 },
3509 })
3510 } else {
3511
3512
3513 if fr := tc.readFrame(); fr != nil {
3514 t.Fatalf("after making new request while at stream limit, got unexpected frame: %v", fr)
3515 }
3516 }
3517
3518 if rt.done() {
3519 t.Fatalf("rt %v done", k)
3520 }
3521 }
3522
3523
3524
3525 close(cancelClientRequest)
3526 synctest.Wait()
3527 if err := rts[maxConcurrent].err(); err == nil {
3528 t.Fatalf("RoundTrip(%d) should have failed due to cancel, did not", maxConcurrent)
3529 }
3530
3531
3532 for i, rt := range rts {
3533 if i != maxConcurrent && rt.done() {
3534 t.Fatalf("RoundTrip(%d) is done, but should not be", i)
3535 }
3536 }
3537
3538
3539 tc.writeHeaders(HeadersFrameParam{
3540 StreamID: rts[0].streamID(),
3541 EndHeaders: true,
3542 EndStream: true,
3543 BlockFragment: tc.makeHeaderBlockFragment(
3544 ":status", "200",
3545 ),
3546 })
3547 synctest.Wait()
3548 tc.wantHeaders(wantHeader{
3549 streamID: rts[maxConcurrent+1].streamID(),
3550 endStream: true,
3551 header: http.Header{
3552 ":authority": []string{"dummy.tld"},
3553 ":method": []string{"GET"},
3554 ":path": []string{fmt.Sprintf("/%d", maxConcurrent+1)},
3555 },
3556 })
3557 rts[0].wantStatus(200)
3558 }
3559
3560 func TestTransportMaxDecoderHeaderTableSize(t *testing.T) {
3561 synctest.Test(t, testTransportMaxDecoderHeaderTableSize)
3562 }
3563 func testTransportMaxDecoderHeaderTableSize(t *testing.T) {
3564 var reqSize, resSize uint32 = 8192, 16384
3565 tc := newTestClientConn(t, func(h2 *http.HTTP2Config) {
3566 h2.MaxDecoderHeaderTableSize = int(reqSize)
3567 })
3568
3569 fr := readFrame[*SettingsFrame](t, tc)
3570 if v, ok := fr.Value(SettingHeaderTableSize); !ok {
3571 t.Fatalf("missing SETTINGS_HEADER_TABLE_SIZE setting")
3572 } else if v != reqSize {
3573 t.Fatalf("received SETTINGS_HEADER_TABLE_SIZE = %d, want %d", v, reqSize)
3574 }
3575
3576 tc.writeSettings(Setting{SettingHeaderTableSize, resSize})
3577 synctest.Wait()
3578 if got, want := tc.cc.TestPeerMaxHeaderTableSize(), resSize; got != want {
3579 t.Fatalf("peerHeaderTableSize = %d, want %d", got, want)
3580 }
3581 }
3582
3583 func TestTransportMaxEncoderHeaderTableSize(t *testing.T) {
3584 synctest.Test(t, testTransportMaxEncoderHeaderTableSize)
3585 }
3586 func testTransportMaxEncoderHeaderTableSize(t *testing.T) {
3587 var peerAdvertisedMaxHeaderTableSize uint32 = 16384
3588 const wantMaxEncoderHeaderTableSize = 8192
3589 tc := newTestClientConn(t, func(h2 *http.HTTP2Config) {
3590 h2.MaxEncoderHeaderTableSize = wantMaxEncoderHeaderTableSize
3591 })
3592 tc.greet(Setting{SettingHeaderTableSize, peerAdvertisedMaxHeaderTableSize})
3593
3594 if got, want := tc.cc.TestHPACKEncoder().MaxDynamicTableSize(), uint32(wantMaxEncoderHeaderTableSize); got != want {
3595 t.Fatalf("henc.MaxDynamicTableSize() = %d, want %d", got, want)
3596 }
3597 }
3598
3599
3600
3601 func TestTransportAllocationsAfterResponseBodyClose(t *testing.T) {
3602 synctest.Test(t, testTransportAllocationsAfterResponseBodyClose)
3603 }
3604 func testTransportAllocationsAfterResponseBodyClose(t *testing.T) {
3605 tc := newTestClientConn(t)
3606 tc.greet()
3607
3608
3609 req, _ := http.NewRequest("PUT", "https://dummy.tld/", nil)
3610 rt := tc.roundTrip(req)
3611 tc.wantFrameType(FrameHeaders)
3612
3613
3614 tc.writeHeaders(HeadersFrameParam{
3615 StreamID: rt.streamID(),
3616 EndHeaders: true,
3617 EndStream: false,
3618 BlockFragment: tc.makeHeaderBlockFragment(
3619 ":status", "200",
3620 ),
3621 })
3622 tc.writeData(rt.streamID(), false, make([]byte, 64))
3623 tc.wantIdle()
3624
3625
3626 respBody := rt.response().Body
3627 var buf [1]byte
3628 if _, err := respBody.Read(buf[:]); err != nil {
3629 t.Error(err)
3630 }
3631 if err := respBody.Close(); err != nil {
3632 t.Error(err)
3633 }
3634 tc.wantFrameType(FrameRSTStream)
3635
3636
3637 tc.writeData(rt.streamID(), false, make([]byte, 64))
3638
3639 if _, err := respBody.Read(buf[:]); err == nil {
3640 t.Error("read from closed body unexpectedly succeeded")
3641 }
3642 }
3643
3644
3645
3646 func TestTransportNoBodyMeansNoDATA(t *testing.T) { synctest.Test(t, testTransportNoBodyMeansNoDATA) }
3647 func testTransportNoBodyMeansNoDATA(t *testing.T) {
3648 tc := newTestClientConn(t)
3649 tc.greet()
3650
3651 req, _ := http.NewRequest("GET", "https://dummy.tld/", http.NoBody)
3652 rt := tc.roundTrip(req)
3653
3654 tc.wantHeaders(wantHeader{
3655 streamID: rt.streamID(),
3656 endStream: true,
3657 header: http.Header{
3658 ":authority": []string{"dummy.tld"},
3659 ":method": []string{"GET"},
3660 ":path": []string{"/"},
3661 },
3662 })
3663 if fr := tc.readFrame(); fr != nil {
3664 t.Fatalf("unexpected frame after headers: %v", fr)
3665 }
3666 }
3667
3668 func benchSimpleRoundTrip(b *testing.B, nReqHeaders, nResHeader int) {
3669 DisableGoroutineTracking(b)
3670 b.ReportAllocs()
3671 ts := newTestServer(b,
3672 func(w http.ResponseWriter, r *http.Request) {
3673 for i := range nResHeader {
3674 name := fmt.Sprint("A-", i)
3675 w.Header().Set(name, "*")
3676 }
3677 },
3678 optQuiet,
3679 )
3680
3681
3682
3683
3684 ts.Config.MaxHeaderValueCount = 30 + nReqHeaders
3685
3686 tr := newTransport(b)
3687
3688 req, err := http.NewRequest("GET", ts.URL, nil)
3689 if err != nil {
3690 b.Fatal(err)
3691 }
3692
3693 for i := range nReqHeaders {
3694 name := fmt.Sprint("A-", i)
3695 req.Header.Set(name, "*")
3696 }
3697
3698 b.ResetTimer()
3699
3700 for i := 0; i < b.N; i++ {
3701 res, err := tr.RoundTrip(req)
3702 if err != nil {
3703 if res != nil {
3704 res.Body.Close()
3705 }
3706 b.Fatalf("RoundTrip err = %v; want nil", err)
3707 }
3708 res.Body.Close()
3709 if res.StatusCode != http.StatusOK {
3710 b.Fatalf("Response code = %v; want %v", res.StatusCode, http.StatusOK)
3711 }
3712 }
3713 }
3714
3715 type infiniteReader struct{}
3716
3717 func (r infiniteReader) Read(b []byte) (int, error) {
3718 return len(b), nil
3719 }
3720
3721
3722
3723 func TestTransportResponseAndResetWithoutConsumingBodyRace(t *testing.T) {
3724 ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) {
3725 w.WriteHeader(http.StatusOK)
3726 })
3727
3728 tr := newTransport(t)
3729
3730
3731 req, _ := http.NewRequest("PUT", ts.URL, infiniteReader{})
3732 res, err := tr.RoundTrip(req)
3733 if err != nil {
3734 t.Fatal(err)
3735 }
3736 if res.StatusCode != http.StatusOK {
3737 t.Fatalf("Response code = %v; want %v", res.StatusCode, http.StatusOK)
3738 }
3739 }
3740
3741
3742
3743 func TestTransportHandlesInvalidStatuslessResponse(t *testing.T) {
3744 synctest.Test(t, testTransportHandlesInvalidStatuslessResponse)
3745 }
3746 func testTransportHandlesInvalidStatuslessResponse(t *testing.T) {
3747 tc := newTestClientConn(t)
3748 tc.greet()
3749
3750 req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
3751 rt := tc.roundTrip(req)
3752
3753 tc.wantFrameType(FrameHeaders)
3754 tc.writeHeaders(HeadersFrameParam{
3755 StreamID: rt.streamID(),
3756 EndHeaders: true,
3757 EndStream: false,
3758 BlockFragment: tc.makeHeaderBlockFragment(
3759 "content-type", "text/html",
3760 ),
3761 })
3762 tc.writeData(rt.streamID(), true, []byte("payload"))
3763 }
3764
3765 func BenchmarkClientRequestHeaders(b *testing.B) {
3766 b.Run(" 0 Headers", func(b *testing.B) { benchSimpleRoundTrip(b, 0, 0) })
3767 b.Run(" 10 Headers", func(b *testing.B) { benchSimpleRoundTrip(b, 10, 0) })
3768 b.Run(" 100 Headers", func(b *testing.B) { benchSimpleRoundTrip(b, 100, 0) })
3769 b.Run("1000 Headers", func(b *testing.B) { benchSimpleRoundTrip(b, 1000, 0) })
3770 }
3771
3772 func BenchmarkClientResponseHeaders(b *testing.B) {
3773 b.Run(" 0 Headers", func(b *testing.B) { benchSimpleRoundTrip(b, 0, 0) })
3774 b.Run(" 10 Headers", func(b *testing.B) { benchSimpleRoundTrip(b, 0, 10) })
3775 b.Run(" 100 Headers", func(b *testing.B) { benchSimpleRoundTrip(b, 0, 100) })
3776 b.Run("1000 Headers", func(b *testing.B) { benchSimpleRoundTrip(b, 0, 1000) })
3777 }
3778
3779 func BenchmarkDownloadFrameSize(b *testing.B) {
3780 b.Run(" 16k Frame", func(b *testing.B) { benchLargeDownloadRoundTrip(b, 16*1024) })
3781 b.Run(" 64k Frame", func(b *testing.B) { benchLargeDownloadRoundTrip(b, 64*1024) })
3782 b.Run("128k Frame", func(b *testing.B) { benchLargeDownloadRoundTrip(b, 128*1024) })
3783 b.Run("256k Frame", func(b *testing.B) { benchLargeDownloadRoundTrip(b, 256*1024) })
3784 b.Run("512k Frame", func(b *testing.B) { benchLargeDownloadRoundTrip(b, 512*1024) })
3785 }
3786 func benchLargeDownloadRoundTrip(b *testing.B, frameSize uint32) {
3787 DisableGoroutineTracking(b)
3788 const transferSize = 1024 * 1024 * 1024
3789 b.ReportAllocs()
3790 ts := newTestServer(b,
3791 func(w http.ResponseWriter, r *http.Request) {
3792
3793 w.Header().Set("Content-Length", strconv.Itoa(transferSize))
3794 w.Header().Set("Content-Transfer-Encoding", "binary")
3795 var data [1024 * 1024]byte
3796 for range transferSize / (1024 * 1024) {
3797 w.Write(data[:])
3798 }
3799 }, optQuiet,
3800 )
3801
3802 tr := newTransport(b)
3803 tr.HTTP2.MaxReadFrameSize = int(frameSize)
3804
3805 req, err := http.NewRequest("GET", ts.URL, nil)
3806 if err != nil {
3807 b.Fatal(err)
3808 }
3809
3810 b.N = 3
3811 b.SetBytes(transferSize)
3812 b.ResetTimer()
3813
3814 for i := 0; i < b.N; i++ {
3815 res, err := tr.RoundTrip(req)
3816 if err != nil {
3817 if res != nil {
3818 res.Body.Close()
3819 }
3820 b.Fatalf("RoundTrip err = %v; want nil", err)
3821 }
3822 data, _ := io.ReadAll(res.Body)
3823 if len(data) != transferSize {
3824 b.Fatalf("Response length invalid")
3825 }
3826 res.Body.Close()
3827 if res.StatusCode != http.StatusOK {
3828 b.Fatalf("Response code = %v; want %v", res.StatusCode, http.StatusOK)
3829 }
3830 }
3831 }
3832
3833 func BenchmarkClientGzip(b *testing.B) {
3834 DisableGoroutineTracking(b)
3835 b.ReportAllocs()
3836
3837 const responseSize = 1024 * 1024
3838
3839 var buf bytes.Buffer
3840 gz := gzip.NewWriter(&buf)
3841 if _, err := io.CopyN(gz, crand.Reader, responseSize); err != nil {
3842 b.Fatal(err)
3843 }
3844 gz.Close()
3845
3846 data := buf.Bytes()
3847 ts := newTestServer(b,
3848 func(w http.ResponseWriter, r *http.Request) {
3849 w.Header().Set("Content-Encoding", "gzip")
3850 w.Write(data)
3851 },
3852 optQuiet,
3853 )
3854
3855 tr := newTransport(b)
3856
3857 req, err := http.NewRequest("GET", ts.URL, nil)
3858 if err != nil {
3859 b.Fatal(err)
3860 }
3861
3862 b.ResetTimer()
3863
3864 for i := 0; i < b.N; i++ {
3865 res, err := tr.RoundTrip(req)
3866 if err != nil {
3867 b.Fatalf("RoundTrip err = %v; want nil", err)
3868 }
3869 if res.StatusCode != http.StatusOK {
3870 b.Fatalf("Response code = %v; want %v", res.StatusCode, http.StatusOK)
3871 }
3872 n, err := io.Copy(io.Discard, res.Body)
3873 res.Body.Close()
3874 if err != nil {
3875 b.Fatalf("RoundTrip err = %v; want nil", err)
3876 }
3877 if n != responseSize {
3878 b.Fatalf("RoundTrip expected %d bytes, got %d", responseSize, n)
3879 }
3880 }
3881 }
3882
3883
3884
3885
3886 func TestClientConnCloseAtHeaders(t *testing.T) { synctest.Test(t, testClientConnCloseAtHeaders) }
3887 func testClientConnCloseAtHeaders(t *testing.T) {
3888 tc := newTestClientConn(t)
3889 tc.greet()
3890
3891 req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
3892 rt := tc.roundTrip(req)
3893 tc.wantFrameType(FrameHeaders)
3894
3895 tc.cc.Close()
3896 synctest.Wait()
3897 if err := rt.err(); err != ErrClientConnForceClosed {
3898 t.Fatalf("RoundTrip error = %v, want errClientConnForceClosed", err)
3899 }
3900 }
3901
3902
3903
3904 func TestClientConnCloseAtBody(t *testing.T) { synctest.Test(t, testClientConnCloseAtBody) }
3905 func testClientConnCloseAtBody(t *testing.T) {
3906 tc := newTestClientConn(t)
3907 tc.greet()
3908
3909 req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
3910 rt := tc.roundTrip(req)
3911 tc.wantFrameType(FrameHeaders)
3912
3913 tc.writeHeaders(HeadersFrameParam{
3914 StreamID: rt.streamID(),
3915 EndHeaders: true,
3916 EndStream: false,
3917 BlockFragment: tc.makeHeaderBlockFragment(
3918 ":status", "200",
3919 ),
3920 })
3921 tc.writeData(rt.streamID(), false, make([]byte, 64))
3922 resp := rt.response()
3923 tc.cc.Close()
3924 synctest.Wait()
3925
3926 if _, err := io.Copy(io.Discard, resp.Body); err == nil {
3927 t.Error("expected a Copy error, got nil")
3928 }
3929 }
3930
3931
3932
3933 func TestClientConnShutdown(t *testing.T) { synctest.Test(t, testClientConnShutdown) }
3934 func testClientConnShutdown(t *testing.T) {
3935 tc := newTestClientConn(t)
3936 tc.greet()
3937
3938 req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
3939 rt := tc.roundTrip(req)
3940 tc.wantFrameType(FrameHeaders)
3941
3942 go tc.cc.Shutdown(context.Background())
3943 synctest.Wait()
3944
3945 tc.wantFrameType(FrameGoAway)
3946 tc.wantIdle()
3947 body := []byte("body")
3948 tc.writeHeaders(HeadersFrameParam{
3949 StreamID: rt.streamID(),
3950 EndHeaders: true,
3951 EndStream: false,
3952 BlockFragment: tc.makeHeaderBlockFragment(
3953 ":status", "200",
3954 ),
3955 })
3956 tc.writeData(rt.streamID(), true, body)
3957
3958 rt.wantStatus(200)
3959 rt.wantBody(body)
3960
3961
3962 tc.wantClosed()
3963 }
3964
3965
3966
3967
3968 func TestClientConnShutdownCancel(t *testing.T) { synctest.Test(t, testClientConnShutdownCancel) }
3969 func testClientConnShutdownCancel(t *testing.T) {
3970 tc := newTestClientConn(t)
3971 tc.greet()
3972
3973 req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
3974 rt := tc.roundTrip(req)
3975 tc.wantFrameType(FrameHeaders)
3976
3977 ctx, cancel := context.WithCancel(t.Context())
3978 var shutdownErr error
3979 go func() {
3980 shutdownErr = tc.cc.Shutdown(ctx)
3981 }()
3982 synctest.Wait()
3983
3984 tc.wantFrameType(FrameGoAway)
3985 tc.wantIdle()
3986
3987 cancel()
3988 synctest.Wait()
3989
3990 if shutdownErr != context.Canceled {
3991 t.Fatalf("ClientConn.Shutdown(ctx) did not return context.Canceled after cancelling context")
3992 }
3993
3994
3995
3996
3997
3998
3999
4000
4001
4002
4003 if rt.done() {
4004 t.Fatal("RoundTrip unexpectedly returned during shutdown")
4005 }
4006 }
4007
4008 type errReader struct {
4009 body []byte
4010 err error
4011 }
4012
4013 func (r *errReader) Read(p []byte) (int, error) {
4014 if len(r.body) > 0 {
4015 n := copy(p, r.body)
4016 r.body = r.body[n:]
4017 return n, nil
4018 }
4019 return 0, r.err
4020 }
4021
4022 func testTransportBodyReadError(t *testing.T, body []byte) {
4023 synctest.Test(t, func(t *testing.T) {
4024 testTransportBodyReadErrorBubble(t, body)
4025 })
4026 }
4027 func testTransportBodyReadErrorBubble(t *testing.T, body []byte) {
4028 tc := newTestClientConn(t)
4029 tc.greet()
4030
4031 bodyReadError := errors.New("body read error")
4032 b := tc.newRequestBody()
4033 b.Write(body)
4034 b.closeWithError(bodyReadError)
4035 req, _ := http.NewRequest("PUT", "https://dummy.tld/", b)
4036 rt := tc.roundTrip(req)
4037
4038 tc.wantFrameType(FrameHeaders)
4039 var receivedBody []byte
4040 readFrames:
4041 for {
4042 switch f := tc.readFrame().(type) {
4043 case *DataFrame:
4044 receivedBody = append(receivedBody, f.Data()...)
4045 case *RSTStreamFrame:
4046 break readFrames
4047 default:
4048 t.Fatalf("unexpected frame: %v", f)
4049 case nil:
4050 t.Fatalf("transport is idle, want RST_STREAM")
4051 }
4052 }
4053 if !bytes.Equal(receivedBody, body) {
4054 t.Fatalf("body: %q; expected %q", receivedBody, body)
4055 }
4056
4057 if err := rt.err(); err != bodyReadError {
4058 t.Fatalf("err = %v; want %v", err, bodyReadError)
4059 }
4060 }
4061
4062 func TestTransportBodyReadError_Immediately(t *testing.T) { testTransportBodyReadError(t, nil) }
4063 func TestTransportBodyReadError_Some(t *testing.T) { testTransportBodyReadError(t, []byte("123")) }
4064
4065
4066
4067
4068 func TestTransportBodyEagerEndStream(t *testing.T) { synctest.Test(t, testTransportBodyEagerEndStream) }
4069 func testTransportBodyEagerEndStream(t *testing.T) {
4070 const reqBody = "some request body"
4071 const resBody = "some response body"
4072
4073 tc := newTestClientConn(t)
4074 tc.greet()
4075
4076 body := strings.NewReader(reqBody)
4077 req, _ := http.NewRequest("PUT", "https://dummy.tld/", body)
4078 tc.roundTrip(req)
4079
4080 tc.wantFrameType(FrameHeaders)
4081 f := readFrame[*DataFrame](t, tc)
4082 if !f.StreamEnded() {
4083 t.Fatalf("data frame without END_STREAM %v", f)
4084 }
4085 }
4086
4087 type chunkReader struct {
4088 chunks [][]byte
4089 }
4090
4091 func (r *chunkReader) Read(p []byte) (int, error) {
4092 if len(r.chunks) > 0 {
4093 n := copy(p, r.chunks[0])
4094 r.chunks = r.chunks[1:]
4095 return n, nil
4096 }
4097 panic("shouldn't read this many times")
4098 }
4099
4100
4101
4102
4103
4104
4105
4106
4107
4108 func TestTransportBodyLargerThanSpecifiedContentLength_len3(t *testing.T) {
4109 body := &chunkReader{[][]byte{
4110 []byte("123"),
4111 []byte("456"),
4112 }}
4113 synctest.Test(t, func(t *testing.T) {
4114 testTransportBodyLargerThanSpecifiedContentLength(t, body, 3)
4115 })
4116 }
4117
4118 func TestTransportBodyLargerThanSpecifiedContentLength_len2(t *testing.T) {
4119 body := &chunkReader{[][]byte{
4120 []byte("123"),
4121 }}
4122 synctest.Test(t, func(t *testing.T) {
4123 testTransportBodyLargerThanSpecifiedContentLength(t, body, 2)
4124 })
4125 }
4126
4127 func testTransportBodyLargerThanSpecifiedContentLength(t *testing.T, body *chunkReader, contentLen int64) {
4128 ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) {
4129 r.Body.Read(make([]byte, 6))
4130 })
4131
4132 tr := newTransport(t)
4133
4134 req, _ := http.NewRequest("POST", ts.URL, body)
4135 req.ContentLength = contentLen
4136 _, err := tr.RoundTrip(req)
4137 if err != ErrReqBodyTooLong {
4138 t.Fatalf("expected %v, got %v", ErrReqBodyTooLong, err)
4139 }
4140 }
4141
4142
4143 func TestTransportNewClientConnCloseOnWriteError(t *testing.T) {
4144 synctest.Test(t, testTransportNewClientConnCloseOnWriteError)
4145 }
4146 func testTransportNewClientConnCloseOnWriteError(t *testing.T) {
4147
4148
4149
4150
4151
4152
4153
4154 t.Skip("TODO: test fails because write errors don't cause the conn to close")
4155
4156 tc := newTestClientConn(t)
4157
4158 synctest.Wait()
4159 writeErr := errors.New("write error")
4160 tc.netconn.loc.setWriteError(writeErr)
4161
4162 tc.writeSettings()
4163 tc.wantIdle()
4164
4165
4166 tc.wantFrameType(FrameSettings)
4167 tc.wantFrameType(FrameWindowUpdate)
4168 tc.wantIdle()
4169
4170 synctest.Wait()
4171 if !tc.netconn.IsClosedByPeer() {
4172 t.Error("expected closed conn")
4173 }
4174 }
4175
4176 func TestTransportRoundtripCloseOnWriteError(t *testing.T) {
4177 synctest.Test(t, testTransportRoundtripCloseOnWriteError)
4178 }
4179 func testTransportRoundtripCloseOnWriteError(t *testing.T) {
4180 tc := newTestClientConn(t)
4181 tc.greet()
4182
4183 body := tc.newRequestBody()
4184 body.writeBytes(1)
4185 req, _ := http.NewRequest("GET", "https://dummy.tld/", body)
4186 rt := tc.roundTrip(req)
4187
4188 writeErr := errors.New("write error")
4189 tc.closeWriteWithError(writeErr)
4190
4191 body.writeBytes(1)
4192 if err := rt.err(); err != writeErr {
4193 t.Fatalf("RoundTrip error %v, want %v", err, writeErr)
4194 }
4195
4196 rt2 := tc.roundTrip(req)
4197 if err := rt2.err(); err != ErrClientConnUnusable {
4198 t.Fatalf("RoundTrip error %v, want errClientConnUnusable", err)
4199 }
4200 }
4201
4202
4203
4204
4205 func TestTransportBodyRewindRace(t *testing.T) {
4206 ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) {
4207 w.Header().Set("Connection", "close")
4208 w.WriteHeader(http.StatusOK)
4209 return
4210 })
4211
4212 tr := newTransport(t)
4213 tr.MaxConnsPerHost = 1
4214 client := &http.Client{
4215 Transport: tr,
4216 }
4217
4218 const clients = 50
4219
4220 var wg sync.WaitGroup
4221 wg.Add(clients)
4222 for range clients {
4223 req, err := http.NewRequest("POST", ts.URL, bytes.NewBufferString("abcdef"))
4224 if err != nil {
4225 t.Fatalf("unexpected new request error: %v", err)
4226 }
4227
4228 go func() {
4229 defer wg.Done()
4230 res, err := client.Do(req)
4231 if err == nil {
4232 res.Body.Close()
4233 }
4234 }()
4235 }
4236
4237 wg.Wait()
4238 }
4239
4240 type errorReader struct{ err error }
4241
4242 func (r errorReader) Read(p []byte) (int, error) { return 0, r.err }
4243
4244
4245
4246 func TestTransportServerResetStreamAtHeaders(t *testing.T) {
4247 ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) {
4248 w.WriteHeader(http.StatusUnauthorized)
4249 return
4250 })
4251
4252 tr := newTransport(t)
4253 tr.MaxConnsPerHost = 1
4254 tr.ExpectContinueTimeout = 10 * time.Second
4255
4256 client := &http.Client{
4257 Transport: tr,
4258 }
4259
4260 req, err := http.NewRequest("POST", ts.URL, errorReader{io.EOF})
4261 if err != nil {
4262 t.Fatalf("unexpected new request error: %v", err)
4263 }
4264 req.ContentLength = 0
4265 req.Header.Set("Expect", "100-continue")
4266 res, err := client.Do(req)
4267 if err != nil {
4268 t.Fatal(err)
4269 }
4270 res.Body.Close()
4271 }
4272
4273 type trackingReader struct {
4274 rdr io.Reader
4275 wasRead uint32
4276 }
4277
4278 func (tr *trackingReader) Read(p []byte) (int, error) {
4279 atomic.StoreUint32(&tr.wasRead, 1)
4280 return tr.rdr.Read(p)
4281 }
4282
4283 func (tr *trackingReader) WasRead() bool {
4284 return atomic.LoadUint32(&tr.wasRead) != 0
4285 }
4286
4287 func TestTransportExpectContinue(t *testing.T) {
4288 ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) {
4289 switch r.URL.Path {
4290 case "/reject":
4291 w.WriteHeader(403)
4292 default:
4293 io.Copy(io.Discard, r.Body)
4294 }
4295 })
4296
4297 tr := newTransport(t)
4298 tr.MaxConnsPerHost = 1
4299 tr.ExpectContinueTimeout = 10 * time.Second
4300
4301 client := &http.Client{
4302 Transport: tr,
4303 }
4304
4305 testCases := []struct {
4306 Name string
4307 Path string
4308 Body *trackingReader
4309 ExpectedCode int
4310 ShouldRead bool
4311 }{
4312 {
4313 Name: "read-all",
4314 Path: "/",
4315 Body: &trackingReader{rdr: strings.NewReader("hello")},
4316 ExpectedCode: 200,
4317 ShouldRead: true,
4318 },
4319 {
4320 Name: "reject",
4321 Path: "/reject",
4322 Body: &trackingReader{rdr: strings.NewReader("hello")},
4323 ExpectedCode: 403,
4324 ShouldRead: false,
4325 },
4326 }
4327
4328 for _, tc := range testCases {
4329 t.Run(tc.Name, func(t *testing.T) {
4330 startTime := time.Now()
4331
4332 req, err := http.NewRequest("POST", ts.URL+tc.Path, tc.Body)
4333 if err != nil {
4334 t.Fatal(err)
4335 }
4336 req.Header.Set("Expect", "100-continue")
4337 res, err := client.Do(req)
4338 if err != nil {
4339 t.Fatal(err)
4340 }
4341 res.Body.Close()
4342
4343 if delta := time.Since(startTime); delta >= tr.ExpectContinueTimeout {
4344 t.Error("Request didn't finish before expect continue timeout")
4345 }
4346 if res.StatusCode != tc.ExpectedCode {
4347 t.Errorf("Unexpected status code, got %d, expected %d", res.StatusCode, tc.ExpectedCode)
4348 }
4349 if tc.Body.WasRead() != tc.ShouldRead {
4350 t.Errorf("Unexpected read status, got %v, expected %v", tc.Body.WasRead(), tc.ShouldRead)
4351 }
4352 })
4353 }
4354 }
4355
4356 type closeChecker struct {
4357 io.ReadCloser
4358 closed chan struct{}
4359 }
4360
4361 func newCloseChecker(r io.ReadCloser) *closeChecker {
4362 return &closeChecker{r, make(chan struct{})}
4363 }
4364
4365 func newStaticCloseChecker(body string) *closeChecker {
4366 return newCloseChecker(io.NopCloser(strings.NewReader("body")))
4367 }
4368
4369 func (rc *closeChecker) Read(b []byte) (n int, err error) {
4370 select {
4371 default:
4372 case <-rc.closed:
4373
4374
4375
4376 return 0, errors.New("read after Body.Close")
4377 }
4378 return rc.ReadCloser.Read(b)
4379 }
4380
4381 func (rc *closeChecker) Close() error {
4382 close(rc.closed)
4383 return rc.ReadCloser.Close()
4384 }
4385
4386 func (rc *closeChecker) isClosed() error {
4387
4388
4389
4390 timeout := time.Duration(10 * time.Second)
4391 select {
4392 case <-rc.closed:
4393 case <-time.After(timeout):
4394 return fmt.Errorf("body not closed after %v", timeout)
4395 }
4396 return nil
4397 }
4398
4399
4400 type blockingWriteConn struct {
4401 net.Conn
4402 writeOnce sync.Once
4403 writec chan struct{}
4404 unblockc chan struct{}
4405 count, limit int
4406 }
4407
4408 func newBlockingWriteConn(conn net.Conn, limit int) *blockingWriteConn {
4409 return &blockingWriteConn{
4410 Conn: conn,
4411 limit: limit,
4412 writec: make(chan struct{}),
4413 unblockc: make(chan struct{}),
4414 }
4415 }
4416
4417
4418 func (c *blockingWriteConn) wait() {
4419 <-c.writec
4420 }
4421
4422
4423 func (c *blockingWriteConn) unblock() {
4424 close(c.unblockc)
4425 }
4426
4427 func (c *blockingWriteConn) Write(b []byte) (n int, err error) {
4428 if c.count+len(b) > c.limit {
4429 c.writeOnce.Do(func() {
4430 close(c.writec)
4431 })
4432 <-c.unblockc
4433 }
4434 n, err = c.Conn.Write(b)
4435 c.count += n
4436 return n, err
4437 }
4438
4439
4440
4441 func TestTransportFrameBufferReuse(t *testing.T) {
4442 filler := hex.EncodeToString([]byte(randString(2048)))
4443
4444 ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) {
4445 if got, want := r.Header.Get("Big"), filler; got != want {
4446 t.Errorf(`r.Header.Get("Big") = %q, want %q`, got, want)
4447 }
4448 b, err := io.ReadAll(r.Body)
4449 if err != nil {
4450 t.Errorf("error reading request body: %v", err)
4451 }
4452 if got, want := string(b), filler; got != want {
4453 t.Errorf("request body = %q, want %q", got, want)
4454 }
4455 if got, want := r.Trailer.Get("Big"), filler; got != want {
4456 t.Errorf(`r.Trailer.Get("Big") = %q, want %q`, got, want)
4457 }
4458 })
4459
4460 tr := newTransport(t)
4461
4462 var wg sync.WaitGroup
4463 defer wg.Wait()
4464 for range 10 {
4465 wg.Go(func() {
4466 req, err := http.NewRequest("POST", ts.URL, strings.NewReader(filler))
4467 if err != nil {
4468 t.Error(err)
4469 return
4470 }
4471 req.Header.Set("Big", filler)
4472 req.Trailer = make(http.Header)
4473 req.Trailer.Set("Big", filler)
4474 res, err := tr.RoundTrip(req)
4475 if err != nil {
4476 t.Error(err)
4477 return
4478 }
4479 if got, want := res.StatusCode, 200; got != want {
4480 t.Errorf("StatusCode = %v; want %v", got, want)
4481 }
4482 if res != nil && res.Body != nil {
4483 res.Body.Close()
4484 }
4485 })
4486 }
4487
4488 }
4489
4490
4491
4492
4493
4494 func TestTransportBlockingRequestWrite(t *testing.T) {
4495 filler := hex.EncodeToString([]byte(randString(2048)))
4496 for _, test := range []struct {
4497 name string
4498 req *http.Request
4499 }{{
4500 name: "headers",
4501 req: func() *http.Request {
4502 req, _ := http.NewRequest("POST", "https://dummy.tld/", nil)
4503 req.Header.Set("Big", filler)
4504 return req
4505 }(),
4506 }, {
4507 name: "body",
4508 req: func() *http.Request {
4509 req, _ := http.NewRequest("POST", "https://dummy.tld/", strings.NewReader(filler))
4510 return req
4511 }(),
4512 }, {
4513 name: "trailer",
4514 req: func() *http.Request {
4515 req, _ := http.NewRequest("POST", "https://dummy.tld/", strings.NewReader("body"))
4516 req.Trailer = make(http.Header)
4517 req.Trailer.Set("Big", filler)
4518 return req
4519 }(),
4520 }} {
4521 t.Run(test.name, func(t *testing.T) {
4522 synctest.Test(t, func(t *testing.T) {
4523 testTransportBlockingRequestWrite(t, test.req)
4524 })
4525 })
4526 }
4527 }
4528 func testTransportBlockingRequestWrite(t *testing.T, req2 *http.Request) {
4529 tt := newTestTransport(t)
4530
4531 smallReq := func() *http.Request {
4532 req, _ := http.NewRequest("GET", req2.URL.String(), nil)
4533 return req
4534 }
4535
4536
4537 rt1 := tt.roundTrip(smallReq())
4538 tc1 := tt.getConn()
4539 tc1.wantFrameType(FrameSettings)
4540 tc1.wantFrameType(FrameWindowUpdate)
4541 tc1.wantHeaders(wantHeader{
4542 streamID: 1,
4543 endStream: true,
4544 })
4545 tc1.writeSettings(Setting{SettingMaxConcurrentStreams, 1})
4546 tc1.writeHeaders(HeadersFrameParam{
4547 StreamID: 1,
4548 EndHeaders: true,
4549 EndStream: true,
4550 BlockFragment: tc1.makeHeaderBlockFragment(
4551 ":status", "200",
4552 ),
4553 })
4554 rt1.wantStatus(200)
4555 tc1.wantFrameType(FrameSettings)
4556
4557
4558 tc1.netconn.SetReadBufferSize(1024)
4559 rt2 := tt.roundTrip(req2)
4560
4561
4562
4563 rt3 := tt.roundTrip(smallReq())
4564 tc2 := tt.getConn()
4565 tc2.wantFrameType(FrameSettings)
4566 tc2.wantFrameType(FrameWindowUpdate)
4567 tc2.wantHeaders(wantHeader{
4568 streamID: 1,
4569 endStream: true,
4570 })
4571 tc2.writeSettings()
4572 tc2.writeHeaders(HeadersFrameParam{
4573 StreamID: 1,
4574 EndHeaders: true,
4575 EndStream: true,
4576 BlockFragment: tc1.makeHeaderBlockFragment(
4577 ":status", "200",
4578 ),
4579 })
4580 rt3.wantStatus(200)
4581 tc2.wantFrameType(FrameSettings)
4582
4583 if rt2.done() {
4584 t.Errorf("RoundTrip 2 is done, expect it to be still pending")
4585 }
4586 }
4587
4588 func TestTransportCloseRequestBody(t *testing.T) {
4589 var statusCode int
4590 ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) {
4591 w.WriteHeader(statusCode)
4592 })
4593
4594 tr := newTransport(t)
4595 ctx := context.Background()
4596 cc, err := tr.NewClientConn(ctx, "https", ts.Listener.Addr().String())
4597 if err != nil {
4598 t.Fatal(err)
4599 }
4600 defer cc.Close()
4601
4602 for _, status := range []int{200, 401} {
4603 t.Run(fmt.Sprintf("status=%d", status), func(t *testing.T) {
4604 statusCode = status
4605 pr, pw := io.Pipe()
4606 body := newCloseChecker(pr)
4607 req, err := http.NewRequest("PUT", "https://dummy.tld/", body)
4608 if err != nil {
4609 t.Fatal(err)
4610 }
4611 res, err := cc.RoundTrip(req)
4612 if err != nil {
4613 t.Fatal(err)
4614 }
4615 res.Body.Close()
4616 pw.Close()
4617 if err := body.isClosed(); err != nil {
4618 t.Fatal(err)
4619 }
4620 })
4621 }
4622 }
4623
4624 func TestTransportNoRetryOnStreamProtocolError(t *testing.T) {
4625 synctest.Test(t, testTransportNoRetryOnStreamProtocolError)
4626 }
4627 func testTransportNoRetryOnStreamProtocolError(t *testing.T) {
4628
4629
4630
4631
4632
4633 tt := newTestTransport(t)
4634
4635
4636
4637
4638
4639
4640 req1, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
4641 rt1 := tt.roundTrip(req1)
4642 tc1 := tt.getConn()
4643 tc1.wantFrameType(FrameSettings)
4644 tc1.wantFrameType(FrameWindowUpdate)
4645 tc1.wantHeaders(wantHeader{
4646 streamID: 1,
4647 endStream: true,
4648 })
4649 tc1.writeSettings()
4650 tc1.wantFrameType(FrameSettings)
4651
4652
4653 req2, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
4654 rt2 := tt.roundTrip(req2)
4655 tc1.wantHeaders(wantHeader{
4656 streamID: 3,
4657 endStream: true,
4658 })
4659
4660
4661 tc1.writeRSTStream(3, ErrCodeProtocol)
4662 if rt1.done() {
4663 t.Fatalf("After protocol error on RoundTrip #2, RoundTrip #1 is done; want still in progress")
4664 }
4665 if !rt2.done() {
4666 t.Fatalf("After protocol error on RoundTrip #2, RoundTrip #2 is in progress; want done")
4667 }
4668
4669 if tt.hasConn() {
4670 t.Fatalf("After protocol error on RoundTrip #2, RoundTrip #2 is unexpectedly retried")
4671 }
4672
4673
4674 tc1.writeHeaders(HeadersFrameParam{
4675 StreamID: 1,
4676 EndHeaders: true,
4677 EndStream: true,
4678 BlockFragment: tc1.makeHeaderBlockFragment(
4679 ":status", "200",
4680 ),
4681 })
4682 rt1.wantStatus(200)
4683 }
4684
4685 func TestClientConnReservations(t *testing.T) { synctest.Test(t, testClientConnReservations) }
4686 func testClientConnReservations(t *testing.T) {
4687 tc := newTestClientConn(t)
4688 tc.greet(
4689 Setting{ID: SettingMaxConcurrentStreams, Val: InitialMaxConcurrentStreams},
4690 )
4691
4692 doRoundTrip := func() {
4693 req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
4694 rt := tc.roundTrip(req)
4695 tc.wantFrameType(FrameHeaders)
4696 tc.writeHeaders(HeadersFrameParam{
4697 StreamID: rt.streamID(),
4698 EndHeaders: true,
4699 EndStream: true,
4700 BlockFragment: tc.makeHeaderBlockFragment(
4701 ":status", "200",
4702 ),
4703 })
4704 rt.wantStatus(200)
4705 }
4706
4707 n := 0
4708 for n <= InitialMaxConcurrentStreams && tc.cc.ReserveNewRequest() {
4709 n++
4710 }
4711 if n != InitialMaxConcurrentStreams {
4712 t.Errorf("did %v reservations; want %v", n, InitialMaxConcurrentStreams)
4713 }
4714 doRoundTrip()
4715 n2 := 0
4716 for n2 <= 5 && tc.cc.ReserveNewRequest() {
4717 n2++
4718 }
4719 if n2 != 1 {
4720 t.Fatalf("after one RoundTrip, did %v reservations; want 1", n2)
4721 }
4722
4723
4724 for i := 0; i < n; i++ {
4725 doRoundTrip()
4726 }
4727
4728 n2 = 0
4729 for n2 <= InitialMaxConcurrentStreams && tc.cc.ReserveNewRequest() {
4730 n2++
4731 }
4732 if n2 != n {
4733 t.Errorf("after reset, reservations = %v; want %v", n2, n)
4734 }
4735 }
4736
4737 func TestTransportTimeoutServerHangs(t *testing.T) { synctest.Test(t, testTransportTimeoutServerHangs) }
4738 func testTransportTimeoutServerHangs(t *testing.T) {
4739 tc := newTestClientConn(t)
4740 tc.greet()
4741
4742 ctx, cancel := context.WithCancel(context.Background())
4743 req, _ := http.NewRequestWithContext(ctx, "PUT", "https://dummy.tld/", nil)
4744 rt := tc.roundTrip(req)
4745
4746 tc.wantFrameType(FrameHeaders)
4747 time.Sleep(5 * time.Second)
4748 if f := tc.readFrame(); f != nil {
4749 t.Fatalf("unexpected frame: %v", f)
4750 }
4751 if rt.done() {
4752 t.Fatalf("after 5 seconds with no response, RoundTrip unexpectedly returned")
4753 }
4754
4755 cancel()
4756 synctest.Wait()
4757 if rt.err() != context.Canceled {
4758 t.Fatalf("RoundTrip error: %v; want context.Canceled", rt.err())
4759 }
4760 }
4761
4762 func TestTransportContentLengthWithoutBody(t *testing.T) {
4763 for _, test := range []struct {
4764 name string
4765 contentLength string
4766 wantBody string
4767 wantErr error
4768 wantContentLength int64
4769 }{
4770 {
4771 name: "non-zero content length",
4772 contentLength: "42",
4773 wantErr: io.ErrUnexpectedEOF,
4774 wantContentLength: 42,
4775 },
4776 {
4777 name: "zero content length",
4778 contentLength: "0",
4779 wantErr: nil,
4780 wantContentLength: 0,
4781 },
4782 } {
4783 synctestSubtest(t, test.name, func(t *testing.T) {
4784 contentLength := ""
4785 ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) {
4786 w.Header().Set("Content-Length", contentLength)
4787 })
4788 tr := newTransport(t)
4789
4790 contentLength = test.contentLength
4791
4792 req, _ := http.NewRequest("GET", ts.URL, nil)
4793 res, err := tr.RoundTrip(req)
4794 if err != nil {
4795 t.Fatal(err)
4796 }
4797 defer res.Body.Close()
4798 body, err := io.ReadAll(res.Body)
4799
4800 if err != test.wantErr {
4801 t.Errorf("Expected error %v, got: %v", test.wantErr, err)
4802 }
4803 if len(body) > 0 {
4804 t.Errorf("Expected empty body, got: %v", body)
4805 }
4806 if res.ContentLength != test.wantContentLength {
4807 t.Errorf("Expected content length %d, got: %d", test.wantContentLength, res.ContentLength)
4808 }
4809 })
4810 }
4811 }
4812
4813 func TestTransportCloseResponseBodyWhileRequestBodyHangs(t *testing.T) {
4814 synctest.Test(t, testTransportCloseResponseBodyWhileRequestBodyHangs)
4815 }
4816 func testTransportCloseResponseBodyWhileRequestBodyHangs(t *testing.T) {
4817 ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) {
4818 w.WriteHeader(200)
4819 w.(http.Flusher).Flush()
4820 io.Copy(io.Discard, r.Body)
4821 })
4822
4823 tr := newTransport(t)
4824
4825 pr, pw := net.Pipe()
4826 req, err := http.NewRequest("GET", ts.URL, pr)
4827 if err != nil {
4828 t.Fatal(err)
4829 }
4830 res, err := tr.RoundTrip(req)
4831 if err != nil {
4832 t.Fatal(err)
4833 }
4834
4835 res.Body.Close()
4836 pw.Close()
4837 }
4838
4839 func TestTransport300ResponseBody(t *testing.T) { synctest.Test(t, testTransport300ResponseBody) }
4840 func testTransport300ResponseBody(t *testing.T) {
4841 reqc := make(chan struct{})
4842 body := []byte("response body")
4843 ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) {
4844 w.WriteHeader(300)
4845 w.(http.Flusher).Flush()
4846 <-reqc
4847 w.Write(body)
4848 })
4849
4850 tr := newTransport(t)
4851
4852 pr, pw := net.Pipe()
4853 req, err := http.NewRequest("GET", ts.URL, pr)
4854 if err != nil {
4855 t.Fatal(err)
4856 }
4857 res, err := tr.RoundTrip(req)
4858 if err != nil {
4859 t.Fatal(err)
4860 }
4861 close(reqc)
4862 got, err := io.ReadAll(res.Body)
4863 if err != nil {
4864 t.Fatalf("error reading response body: %v", err)
4865 }
4866 if !bytes.Equal(got, body) {
4867 t.Errorf("got response body %q, want %q", string(got), string(body))
4868 }
4869 res.Body.Close()
4870 pw.Close()
4871 }
4872
4873 func TestTransportWriteByteTimeout(t *testing.T) {
4874 ts := newTestServer(t, nil, func(s *http.Server) {
4875 s.Protocols = protocols("h2c")
4876 })
4877 tr := newTransport(t)
4878 tr.Protocols = protocols("h2c")
4879 tr.Dial = func(network, addr string) (net.Conn, error) {
4880 _, c := net.Pipe()
4881 return c, nil
4882 }
4883 tr.HTTP2.WriteByteTimeout = 1 * time.Millisecond
4884 defer tr.CloseIdleConnections()
4885 c := &http.Client{Transport: tr}
4886
4887 _, err := c.Get(ts.URL)
4888 if !errors.Is(err, os.ErrDeadlineExceeded) {
4889 t.Fatalf("Get on unresponsive connection: got %q; want ErrDeadlineExceeded", err)
4890 }
4891 }
4892
4893 type slowWriteConn struct {
4894 net.Conn
4895 hasWriteDeadline bool
4896 }
4897
4898 func (c *slowWriteConn) SetWriteDeadline(t time.Time) error {
4899 c.hasWriteDeadline = !t.IsZero()
4900 return nil
4901 }
4902
4903 func (c *slowWriteConn) Write(b []byte) (n int, err error) {
4904 if c.hasWriteDeadline && len(b) > 1 {
4905 n, err = c.Conn.Write(b[:1])
4906 if err != nil {
4907 return n, err
4908 }
4909 return n, fmt.Errorf("slow write: %w", os.ErrDeadlineExceeded)
4910 }
4911 return c.Conn.Write(b)
4912 }
4913
4914 func TestTransportSlowWrites(t *testing.T) { synctest.Test(t, testTransportSlowWrites) }
4915 func testTransportSlowWrites(t *testing.T) {
4916 ts := newTestServer(t, nil, func(s *http.Server) {
4917 s.Protocols = protocols("h2c")
4918 })
4919 tr := newTransport(t)
4920 tr.Protocols = protocols("h2c")
4921 tr.Dial = func(network, addr string) (net.Conn, error) {
4922 c, err := net.Dial(network, addr)
4923 return &slowWriteConn{Conn: c}, err
4924 }
4925 tr.HTTP2.WriteByteTimeout = 1 * time.Millisecond
4926 c := &http.Client{Transport: tr}
4927
4928 const bodySize = 1 << 20
4929 resp, err := c.Post(ts.URL, "text/foo", io.LimitReader(neverEnding('A'), bodySize))
4930 if err != nil {
4931 t.Fatal(err)
4932 }
4933 resp.Body.Close()
4934 }
4935
4936 func TestTransportClosesConnAfterGoAwayNoStreams(t *testing.T) {
4937 synctest.Test(t, func(t *testing.T) {
4938 testTransportClosesConnAfterGoAway(t, 0)
4939 })
4940 }
4941 func TestTransportClosesConnAfterGoAwayLastStream(t *testing.T) {
4942 synctest.Test(t, func(t *testing.T) {
4943 testTransportClosesConnAfterGoAway(t, 1)
4944 })
4945 }
4946
4947
4948
4949
4950
4951
4952
4953 func testTransportClosesConnAfterGoAway(t *testing.T, lastStream uint32) {
4954 tc := newTestClientConn(t)
4955 tc.greet()
4956
4957 req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
4958 rt := tc.roundTrip(req)
4959
4960 tc.wantFrameType(FrameHeaders)
4961 tc.writeGoAway(lastStream, ErrCodeNo, nil)
4962
4963 if lastStream > 0 {
4964
4965 tc.writeHeaders(HeadersFrameParam{
4966 StreamID: rt.streamID(),
4967 EndHeaders: true,
4968 EndStream: true,
4969 BlockFragment: tc.makeHeaderBlockFragment(
4970 ":status", "200",
4971 ),
4972 })
4973 }
4974
4975 tc.closeWrite()
4976 err := rt.err()
4977 if gotErr, wantErr := err != nil, lastStream == 0; gotErr != wantErr {
4978 t.Errorf("RoundTrip got error %v (want error: %v)", err, wantErr)
4979 }
4980 if !tc.isClosed() {
4981 t.Errorf("ClientConn did not close its net.Conn, expected it to")
4982 }
4983 }
4984
4985 type slowCloser struct {
4986 closing chan struct{}
4987 closed chan struct{}
4988 }
4989
4990 func (r *slowCloser) Read([]byte) (int, error) {
4991 return 0, io.EOF
4992 }
4993
4994 func (r *slowCloser) Close() error {
4995 close(r.closing)
4996 <-r.closed
4997 return nil
4998 }
4999
5000 func TestTransportSlowClose(t *testing.T) {
5001 ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) {
5002 })
5003
5004 client := ts.Client()
5005 body := &slowCloser{
5006 closing: make(chan struct{}),
5007 closed: make(chan struct{}),
5008 }
5009
5010 reqc := make(chan struct{})
5011 go func() {
5012 defer close(reqc)
5013 res, err := client.Post(ts.URL, "text/plain", body)
5014 if err != nil {
5015 t.Error(err)
5016 }
5017 res.Body.Close()
5018 }()
5019 defer func() {
5020 close(body.closed)
5021 <-reqc
5022 }()
5023
5024 <-body.closing
5025
5026 res, err := client.Get(ts.URL)
5027 if err != nil {
5028 t.Fatal(err)
5029 }
5030 res.Body.Close()
5031 }
5032
5033 func TestTransportDialTLSContext(t *testing.T) {
5034 blockCh := make(chan struct{})
5035 serverTLSConfigFunc := func(ts *httptest.Server) {
5036 ts.Config.TLSConfig = &tls.Config{
5037
5038
5039 ClientAuth: tls.RequestClientCert,
5040 }
5041 }
5042 ts := newTestServer(t,
5043 func(w http.ResponseWriter, r *http.Request) {},
5044 serverTLSConfigFunc,
5045 )
5046 tr := newTransport(t)
5047 tr.TLSClientConfig = &tls.Config{
5048 GetClientCertificate: func(cri *tls.CertificateRequestInfo) (*tls.Certificate, error) {
5049
5050
5051 close(blockCh)
5052 <-cri.Context().Done()
5053 return nil, cri.Context().Err()
5054 },
5055 InsecureSkipVerify: true,
5056 }
5057 req, err := http.NewRequest(http.MethodGet, ts.URL, nil)
5058 if err != nil {
5059 t.Fatal(err)
5060 }
5061 ctx, cancel := context.WithCancel(context.Background())
5062 defer cancel()
5063 req = req.WithContext(ctx)
5064 errCh := make(chan error)
5065 go func() {
5066 defer close(errCh)
5067 res, err := tr.RoundTrip(req)
5068 if err != nil {
5069 errCh <- err
5070 return
5071 }
5072 res.Body.Close()
5073 }()
5074
5075 <-blockCh
5076
5077 cancel()
5078
5079 err = <-errCh
5080 if err == nil {
5081 t.Fatal("cancelling context during client certificate fetch did not error as expected")
5082 return
5083 }
5084 if !errors.Is(err, context.Canceled) {
5085 t.Fatalf("unexpected error returned after cancellation: %v", err)
5086 }
5087 }
5088
5089
5090
5091
5092
5093 func TestDialRaceResumesDial(t *testing.T) {
5094 t.Skip("https://go.dev/issue/77908: test fails when using an http.Transport")
5095 blockCh := make(chan struct{})
5096 serverTLSConfigFunc := func(ts *httptest.Server) {
5097 ts.Config.TLSConfig = &tls.Config{
5098
5099
5100 ClientAuth: tls.RequestClientCert,
5101 }
5102 }
5103 ts := newTestServer(t,
5104 func(w http.ResponseWriter, r *http.Request) {},
5105 serverTLSConfigFunc,
5106 )
5107 tr := newTransport(t)
5108 tr.TLSClientConfig = &tls.Config{
5109 GetClientCertificate: func(cri *tls.CertificateRequestInfo) (*tls.Certificate, error) {
5110 select {
5111 case <-blockCh:
5112
5113 return &tls.Certificate{}, nil
5114 default:
5115 }
5116 close(blockCh)
5117 <-cri.Context().Done()
5118 return nil, cri.Context().Err()
5119 },
5120 InsecureSkipVerify: true,
5121 }
5122 req, err := http.NewRequest(http.MethodGet, ts.URL, nil)
5123 if err != nil {
5124 t.Fatal(err)
5125 }
5126
5127 ctx1, cancel1 := context.WithCancel(context.Background())
5128 defer cancel1()
5129 req1 := req.WithContext(ctx1)
5130 ctx2 := t.Context()
5131 req2 := req.WithContext(ctx2)
5132 errCh := make(chan error)
5133 go func() {
5134 res, err := tr.RoundTrip(req1)
5135 if err != nil {
5136 errCh <- err
5137 return
5138 }
5139 res.Body.Close()
5140 }()
5141 successCh := make(chan struct{})
5142 go func() {
5143
5144
5145 <-blockCh
5146 res, err := tr.RoundTrip(req2)
5147 if err != nil {
5148 errCh <- err
5149 return
5150 }
5151 res.Body.Close()
5152
5153
5154 close(successCh)
5155 }()
5156
5157 <-blockCh
5158
5159 cancel1()
5160
5161 err = <-errCh
5162 if err == nil {
5163 t.Fatal("cancelling context during client certificate fetch did not error as expected")
5164 return
5165 }
5166 if !errors.Is(err, context.Canceled) {
5167 t.Fatalf("unexpected error returned after cancellation: %v", err)
5168 }
5169 select {
5170 case err := <-errCh:
5171 t.Fatalf("unexpected second error: %v", err)
5172 case <-successCh:
5173 }
5174 }
5175
5176 func TestTransportDataAfter1xxHeader(t *testing.T) { synctest.Test(t, testTransportDataAfter1xxHeader) }
5177 func testTransportDataAfter1xxHeader(t *testing.T) {
5178
5179 log.SetOutput(io.Discard)
5180 defer log.SetOutput(os.Stderr)
5181
5182
5183 tc := newTestClientConn(t)
5184 tc.greet()
5185
5186 req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
5187 rt := tc.roundTrip(req)
5188
5189 tc.wantFrameType(FrameHeaders)
5190 tc.writeHeaders(HeadersFrameParam{
5191 StreamID: rt.streamID(),
5192 EndHeaders: true,
5193 EndStream: false,
5194 BlockFragment: tc.makeHeaderBlockFragment(
5195 ":status", "100",
5196 ),
5197 })
5198 tc.writeData(rt.streamID(), true, []byte{0})
5199 err := rt.err()
5200 if err, ok := err.(StreamError); !ok || err.Code != ErrCodeProtocol {
5201 t.Errorf("RoundTrip error: %v; want ErrCodeProtocol", err)
5202 }
5203 tc.wantFrameType(FrameRSTStream)
5204 }
5205
5206 func TestIssue66763Race(t *testing.T) {
5207 ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) {},
5208 func(s *http.Server) {
5209 s.Protocols = protocols("h2c")
5210 })
5211 tr := newTransport(t)
5212 tr.IdleConnTimeout = 1 * time.Nanosecond
5213 tr.Protocols = protocols("h2c")
5214
5215 donec := make(chan struct{})
5216 go func() {
5217
5218
5219
5220 conn, err := tr.NewClientConn(t.Context(), "http", ts.URL)
5221 close(donec)
5222 if err == nil {
5223 conn.Close()
5224 }
5225 }()
5226
5227
5228
5229 <-donec
5230 }
5231
5232
5233
5234 func TestIssue67671(t *testing.T) {
5235 ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) {},
5236 func(s *http.Server) {
5237 s.Protocols = protocols("h2c")
5238 })
5239 tr := newTransport(t)
5240 tr.Protocols = protocols("h2c")
5241 req, _ := http.NewRequest("GET", ts.URL, nil)
5242 req.Close = true
5243 for range 2 {
5244 res, err := tr.RoundTrip(req)
5245 if err != nil {
5246 t.Fatal(err)
5247 }
5248 res.Body.Close()
5249 }
5250 }
5251
5252 func TestTransport1xxLimits(t *testing.T) {
5253 for _, test := range []struct {
5254 name string
5255 opt any
5256 ctxfn func(context.Context) context.Context
5257 hcount int
5258 limited bool
5259 }{{
5260 name: "default",
5261 hcount: 10,
5262 limited: false,
5263 }, {
5264 name: "MaxResponseHeaderBytes",
5265 opt: func(tr *http.Transport) {
5266 tr.MaxResponseHeaderBytes = 10000
5267 },
5268 hcount: 10,
5269 limited: true,
5270 }, {
5271 name: "limit by client trace",
5272 ctxfn: func(ctx context.Context) context.Context {
5273 count := 0
5274 return httptrace.WithClientTrace(ctx, &httptrace.ClientTrace{
5275 Got1xxResponse: func(code int, header textproto.MIMEHeader) error {
5276 count++
5277 if count >= 10 {
5278 return errors.New("too many 1xx")
5279 }
5280 return nil
5281 },
5282 })
5283 },
5284 hcount: 10,
5285 limited: true,
5286 }, {
5287 name: "limit disabled by client trace",
5288 opt: func(tr *http.Transport) {
5289 tr.MaxResponseHeaderBytes = 10000
5290 },
5291 ctxfn: func(ctx context.Context) context.Context {
5292 return httptrace.WithClientTrace(ctx, &httptrace.ClientTrace{
5293 Got1xxResponse: func(code int, header textproto.MIMEHeader) error {
5294 return nil
5295 },
5296 })
5297 },
5298 hcount: 20,
5299 limited: false,
5300 }} {
5301 synctestSubtest(t, test.name, func(t *testing.T) {
5302 tc := newTestClientConn(t, test.opt)
5303 tc.greet()
5304
5305 ctx := context.Background()
5306 if test.ctxfn != nil {
5307 ctx = test.ctxfn(ctx)
5308 }
5309 req, _ := http.NewRequestWithContext(ctx, "GET", "https://dummy.tld/", nil)
5310 rt := tc.roundTrip(req)
5311 tc.wantFrameType(FrameHeaders)
5312
5313 for i := 0; i < test.hcount; i++ {
5314 if fr, err := tc.fr.ReadFrame(); err != os.ErrDeadlineExceeded {
5315 t.Fatalf("after writing %v 1xx headers: read %v, %v; want idle", i, fr, err)
5316 }
5317 tc.writeHeaders(HeadersFrameParam{
5318 StreamID: rt.streamID(),
5319 EndHeaders: true,
5320 EndStream: false,
5321 BlockFragment: tc.makeHeaderBlockFragment(
5322 ":status", "103",
5323 "x-field", strings.Repeat("a", 1000),
5324 ),
5325 })
5326 }
5327 if test.limited {
5328 tc.wantFrameType(FrameRSTStream)
5329 } else {
5330 tc.wantIdle()
5331 }
5332 })
5333 }
5334 }
5335
5336
5337
5338 func TestTransportSendPingWithReset(t *testing.T) { synctest.Test(t, testTransportSendPingWithReset) }
5339 func testTransportSendPingWithReset(t *testing.T) {
5340 tc := newTestClientConn(t, func(h2 *http.HTTP2Config) {
5341 h2.StrictMaxConcurrentRequests = true
5342 })
5343
5344 const maxConcurrent = 3
5345 tc.greet(Setting{SettingMaxConcurrentStreams, maxConcurrent})
5346
5347
5348 var rts []*testRoundTrip
5349 for i := range maxConcurrent + 1 {
5350 req := Must(http.NewRequest("GET", "https://dummy.tld/", nil))
5351 rt := tc.roundTrip(req)
5352 if i >= maxConcurrent {
5353 tc.wantIdle()
5354 continue
5355 }
5356 tc.wantFrameType(FrameHeaders)
5357 rts = append(rts, rt)
5358 }
5359
5360
5361 rts[0].cancel()
5362 tc.wantRSTStream(rts[0].streamID(), ErrCodeCancel)
5363 pf := readFrame[*PingFrame](t, tc)
5364 tc.wantIdle()
5365
5366
5367 rts[1].cancel()
5368 tc.wantRSTStream(rts[1].streamID(), ErrCodeCancel)
5369 tc.wantIdle()
5370
5371
5372
5373 tc.writePing(true, pf.Data)
5374 tc.wantFrameType(FrameHeaders)
5375 tc.wantIdle()
5376 }
5377
5378
5379
5380
5381
5382 func TestTransportNoPingAfterResetWithFrames(t *testing.T) {
5383 synctest.Test(t, testTransportNoPingAfterResetWithFrames)
5384 }
5385 func testTransportNoPingAfterResetWithFrames(t *testing.T) {
5386 tc := newTestClientConn(t, func(h2 *http.HTTP2Config) {
5387 h2.StrictMaxConcurrentRequests = true
5388 })
5389
5390 const maxConcurrent = 1
5391 tc.greet(Setting{SettingMaxConcurrentStreams, maxConcurrent})
5392
5393
5394
5395 req1 := Must(http.NewRequest("GET", "https://dummy.tld/", nil))
5396 rt1 := tc.roundTrip(req1)
5397 tc.wantFrameType(FrameHeaders)
5398 tc.writeHeaders(HeadersFrameParam{
5399 StreamID: rt1.streamID(),
5400 EndHeaders: true,
5401 BlockFragment: tc.makeHeaderBlockFragment(
5402 ":status", "200",
5403 ),
5404 })
5405 rt1.wantStatus(200)
5406
5407
5408
5409 req2 := Must(http.NewRequest("GET", "https://dummy.tld/", nil))
5410 rt2 := tc.roundTrip(req2)
5411 tc.wantIdle()
5412
5413
5414
5415 rt1.cancel()
5416 tc.wantRSTStream(rt1.streamID(), ErrCodeCancel)
5417 tc.wantFrameType(FrameHeaders)
5418
5419
5420
5421
5422 rt2.cancel()
5423 tc.wantRSTStream(rt2.streamID(), ErrCodeCancel)
5424 tc.wantFrameType(FramePing)
5425 }
5426
5427
5428
5429 func TestTransportSendNoMoreThanOnePingWithReset(t *testing.T) {
5430 synctest.Test(t, testTransportSendNoMoreThanOnePingWithReset)
5431 }
5432 func testTransportSendNoMoreThanOnePingWithReset(t *testing.T) {
5433 tc := newTestClientConn(t)
5434 tc.greet()
5435
5436 makeAndResetRequest := func() {
5437 t.Helper()
5438 ctx, cancel := context.WithCancel(context.Background())
5439 req := Must(http.NewRequestWithContext(ctx, "GET", "https://dummy.tld/", nil))
5440 rt := tc.roundTrip(req)
5441 tc.wantFrameType(FrameHeaders)
5442 cancel()
5443 tc.wantRSTStream(rt.streamID(), ErrCodeCancel)
5444 }
5445
5446
5447
5448 makeAndResetRequest()
5449 pf1 := readFrame[*PingFrame](t, tc)
5450 tc.wantIdle()
5451
5452
5453
5454
5455
5456 makeAndResetRequest()
5457 tc.wantIdle()
5458
5459
5460
5461 tc.writeHeaders(HeadersFrameParam{
5462 StreamID: 1,
5463 EndHeaders: true,
5464 EndStream: true,
5465 BlockFragment: tc.makeHeaderBlockFragment(
5466 ":status", "200",
5467 ),
5468 })
5469 tc.wantIdle()
5470
5471
5472
5473
5474 makeAndResetRequest()
5475 tc.wantIdle()
5476
5477
5478 tc.writePing(true, pf1.Data)
5479 tc.wantIdle()
5480
5481
5482
5483
5484 makeAndResetRequest()
5485 tc.wantIdle()
5486
5487
5488 tc.writeHeaders(HeadersFrameParam{
5489 StreamID: 3,
5490 EndHeaders: true,
5491 EndStream: true,
5492 BlockFragment: tc.makeHeaderBlockFragment(
5493 ":status", "200",
5494 ),
5495 })
5496 tc.wantIdle()
5497
5498
5499
5500 makeAndResetRequest()
5501 tc.wantFrameType(FramePing)
5502 }
5503
5504 func TestTransportConnBecomesUnresponsive(t *testing.T) {
5505 synctest.Test(t, testTransportConnBecomesUnresponsive)
5506 }
5507 func testTransportConnBecomesUnresponsive(t *testing.T) {
5508
5509
5510
5511 tt := newTestTransport(t)
5512
5513 const maxConcurrent = 3
5514
5515 t.Logf("first request opens a new connection and succeeds")
5516 req1 := Must(http.NewRequest("GET", "https://dummy.tld/", nil))
5517 rt1 := tt.roundTrip(req1)
5518 tc1 := tt.getConn()
5519 tc1.wantFrameType(FrameSettings)
5520 tc1.wantFrameType(FrameWindowUpdate)
5521 hf1 := readFrame[*HeadersFrame](t, tc1)
5522 tc1.writeSettings(Setting{SettingMaxConcurrentStreams, maxConcurrent})
5523 tc1.wantFrameType(FrameSettings)
5524 tc1.writeHeaders(HeadersFrameParam{
5525 StreamID: hf1.StreamID,
5526 EndHeaders: true,
5527 EndStream: true,
5528 BlockFragment: tc1.makeHeaderBlockFragment(
5529 ":status", "200",
5530 ),
5531 })
5532 rt1.wantStatus(200)
5533 rt1.response().Body.Close()
5534
5535
5536
5537
5538 for i := range maxConcurrent {
5539 t.Logf("request %v receives no response and is canceled", i)
5540 ctx, cancel := context.WithCancel(context.Background())
5541 req := Must(http.NewRequestWithContext(ctx, "GET", "https://dummy.tld/", nil))
5542 tt.roundTrip(req)
5543 if tt.hasConn() {
5544 t.Fatalf("new connection created; expect existing conn to be reused")
5545 }
5546 tc1.wantFrameType(FrameHeaders)
5547 cancel()
5548 tc1.wantFrameType(FrameRSTStream)
5549 if i == 0 {
5550 tc1.wantFrameType(FramePing)
5551 }
5552 tc1.wantIdle()
5553 }
5554
5555
5556
5557 req2 := Must(http.NewRequest("GET", "https://dummy.tld/", nil))
5558 rt2 := tt.roundTrip(req2)
5559 tc2 := tt.getConn()
5560 tc2.wantFrameType(FrameSettings)
5561 tc2.wantFrameType(FrameWindowUpdate)
5562 hf := readFrame[*HeadersFrame](t, tc2)
5563 tc2.writeSettings(Setting{SettingMaxConcurrentStreams, maxConcurrent})
5564 tc2.wantFrameType(FrameSettings)
5565 tc2.writeHeaders(HeadersFrameParam{
5566 StreamID: hf.StreamID,
5567 EndHeaders: true,
5568 EndStream: true,
5569 BlockFragment: tc2.makeHeaderBlockFragment(
5570 ":status", "200",
5571 ),
5572 })
5573 rt2.wantStatus(200)
5574 rt2.response().Body.Close()
5575 }
5576
5577
5578
5579
5580
5581
5582 func newTestTransportWithUnusedConn(t *testing.T, opts ...any) *testTransport {
5583 tt := newTestTransport(t, opts...)
5584
5585 waitc := make(chan struct{})
5586 dialContext := tt.tr1.DialContext
5587 tt.tr1.DialContext = func(ctx context.Context, network, address string) (net.Conn, error) {
5588 <-waitc
5589 return dialContext(ctx, network, address)
5590 }
5591
5592 req := Must(http.NewRequest("GET", "https://dummy.tld/", nil))
5593 rt := tt.roundTrip(req)
5594 rt.cancel()
5595 if rt.err() == nil {
5596 t.Fatalf("RoundTrip still running after request is canceled")
5597 }
5598
5599 close(waitc)
5600 synctest.Wait()
5601 return tt
5602 }
5603
5604
5605 func TestTransportUnusedConnOK(t *testing.T) { synctest.Test(t, testTransportUnusedConnOK) }
5606 func testTransportUnusedConnOK(t *testing.T) {
5607 tt := newTestTransportWithUnusedConn(t)
5608
5609 req := Must(http.NewRequest("GET", "https://dummy.tld/", nil))
5610 tc := tt.getConn()
5611 tc.wantFrameType(FrameSettings)
5612 tc.wantFrameType(FrameWindowUpdate)
5613
5614
5615
5616 rt := tt.roundTrip(req)
5617 tc.wantHeaders(wantHeader{
5618 streamID: 1,
5619 endStream: true,
5620 header: http.Header{
5621 ":authority": []string{"dummy.tld"},
5622 ":method": []string{"GET"},
5623 ":path": []string{"/"},
5624 },
5625 })
5626
5627 tc.writeSettings()
5628 tc.writeSettingsAck()
5629 tc.wantFrameType(FrameSettings)
5630
5631 tc.writeHeaders(HeadersFrameParam{
5632 StreamID: 1,
5633 EndHeaders: true,
5634 EndStream: true,
5635 BlockFragment: tc.makeHeaderBlockFragment(
5636 ":status", "200",
5637 ),
5638 })
5639 rt.wantStatus(200)
5640 rt.wantBody(nil)
5641 }
5642
5643
5644 func TestTransportUnusedConnImmediateFailureUsed(t *testing.T) {
5645 synctest.Test(t, testTransportUnusedConnImmediateFailureUsed)
5646 }
5647 func testTransportUnusedConnImmediateFailureUsed(t *testing.T) {
5648 tt := newTestTransportWithUnusedConn(t)
5649
5650
5651 tc1 := tt.getConn()
5652 tc1.closeWrite()
5653
5654
5655
5656
5657 req := Must(http.NewRequest("GET", "https://dummy.tld/", nil))
5658 rt := tt.roundTrip(req)
5659 if err := rt.err(); err == nil || errors.Is(err, ErrNoCachedConn) {
5660 t.Fatalf("RoundTrip with broken conn: got %v, want an error other than ErrNoCachedConn", err)
5661 }
5662
5663
5664
5665
5666 _ = tt.roundTrip(req)
5667 tc2 := tt.getConn()
5668 tc2.wantFrameType(FrameSettings)
5669 tc2.wantFrameType(FrameWindowUpdate)
5670 tc2.wantFrameType(FrameHeaders)
5671 }
5672
5673
5674 func TestTransportUnusedConnIdleTimoutBeforeUse(t *testing.T) {
5675 synctest.Test(t, testTransportUnusedConnIdleTimoutBeforeUse)
5676 }
5677 func testTransportUnusedConnIdleTimoutBeforeUse(t *testing.T) {
5678 tt := newTestTransportWithUnusedConn(t, func(t1 *http.Transport) {
5679 t1.IdleConnTimeout = 1 * time.Second
5680 })
5681
5682 _ = tt.getConn()
5683
5684
5685 time.Sleep(2 * time.Second)
5686 synctest.Wait()
5687
5688
5689
5690
5691
5692 req := Must(http.NewRequest("GET", "https://dummy.tld/", nil))
5693 _ = tt.roundTrip(req)
5694 tc2 := tt.getConn()
5695 tc2.wantFrameType(FrameSettings)
5696 tc2.wantFrameType(FrameWindowUpdate)
5697 tc2.wantFrameType(FrameHeaders)
5698 }
5699
5700
5701
5702 func TestTransportTLSNextProtoConnImmediateFailureUnused(t *testing.T) {
5703 synctest.Test(t, testTransportTLSNextProtoConnImmediateFailureUnused)
5704 }
5705 func testTransportTLSNextProtoConnImmediateFailureUnused(t *testing.T) {
5706 tt := newTestTransportWithUnusedConn(t, func(t1 *http.Transport) {
5707 t1.IdleConnTimeout = 1 * time.Second
5708 })
5709
5710
5711 tc1 := tt.getConn()
5712 tc1.closeWrite()
5713
5714
5715
5716 time.Sleep(10 * time.Second)
5717
5718
5719
5720
5721 req := Must(http.NewRequest("GET", "https://dummy.tld/", nil))
5722 _ = tt.roundTrip(req)
5723 tc2 := tt.getConn()
5724 tc2.wantFrameType(FrameSettings)
5725 tc2.wantFrameType(FrameWindowUpdate)
5726 tc2.wantFrameType(FrameHeaders)
5727 }
5728
5729 func TestTransportDoNotHangOnZeroMaxFrameSize(t *testing.T) {
5730 synctest.Test(t, testTransportDoNotHangOnZeroMaxFrameSize)
5731 }
5732 func testTransportDoNotHangOnZeroMaxFrameSize(t *testing.T) {
5733 tc := newTestClientConn(t)
5734 tc.writeSettings(Setting{ID: SettingMaxFrameSize, Val: 0})
5735 tc.wantFrameType(FrameSettings)
5736
5737 req, _ := http.NewRequest("POST", "https://dummy.tld/", strings.NewReader("body"))
5738 tc.roundTrip(req)
5739
5740 }
5741
5742 func TestExtendedConnectClientWithServerSupport(t *testing.T) {
5743 t.Skip("https://go.dev/issue/53208 -- net/http needs to support the :protocol header")
5744 SetDisableExtendedConnectProtocol(t, false)
5745 ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) {
5746 if r.Header.Get(":protocol") != "extended-connect" {
5747 t.Fatalf("unexpected :protocol header received")
5748 }
5749 t.Log(io.Copy(w, r.Body))
5750 })
5751 tr := newTransport(t)
5752 pr, pw := io.Pipe()
5753 pwDone := make(chan struct{})
5754 req, _ := http.NewRequest("CONNECT", ts.URL, pr)
5755 req.Header.Set(":protocol", "extended-connect")
5756 req.Header.Set("X-A", "A")
5757 req.Header.Set("X-B", "B")
5758 req.Header.Set("X-C", "C")
5759 go func() {
5760 pw.Write([]byte("hello, extended connect"))
5761 pw.Close()
5762 close(pwDone)
5763 }()
5764
5765 res, err := tr.RoundTrip(req)
5766 if err != nil {
5767 t.Fatal(err)
5768 }
5769 body, err := io.ReadAll(res.Body)
5770 if err != nil {
5771 t.Fatal(err)
5772 }
5773 if !bytes.Equal(body, []byte("hello, extended connect")) {
5774 t.Fatal("unexpected body received")
5775 }
5776 }
5777
5778 func TestExtendedConnectClientWithoutServerSupport(t *testing.T) {
5779 t.Skip("https://go.dev/issue/53208 -- net/http needs to support the :protocol header")
5780 SetDisableExtendedConnectProtocol(t, true)
5781 ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) {
5782 io.Copy(w, r.Body)
5783 })
5784 tr := newTransport(t)
5785 pr, pw := io.Pipe()
5786 pwDone := make(chan struct{})
5787 req, _ := http.NewRequest("CONNECT", ts.URL, pr)
5788 req.Header.Set(":protocol", "extended-connect")
5789 req.Header.Set("X-A", "A")
5790 req.Header.Set("X-B", "B")
5791 req.Header.Set("X-C", "C")
5792 go func() {
5793 pw.Write([]byte("hello, extended connect"))
5794 pw.Close()
5795 close(pwDone)
5796 }()
5797
5798 _, err := tr.RoundTrip(req)
5799 if !errors.Is(err, ErrExtendedConnectNotSupported) {
5800 t.Fatalf("expected error errExtendedConnectNotSupported, got: %v", err)
5801 }
5802 }
5803
5804
5805
5806 func TestExtendedConnectReadFrameError(t *testing.T) {
5807 synctest.Test(t, testExtendedConnectReadFrameError)
5808 }
5809 func testExtendedConnectReadFrameError(t *testing.T) {
5810 t.Skip("https://go.dev/issue/53208 -- net/http needs to support the :protocol header")
5811 tc := newTestClientConn(t)
5812 tc.wantFrameType(FrameSettings)
5813 tc.wantFrameType(FrameWindowUpdate)
5814
5815 req, _ := http.NewRequest("CONNECT", "https://dummy.tld/", nil)
5816 req.Header.Set(":protocol", "extended-connect")
5817 rt := tc.roundTrip(req)
5818 tc.wantIdle()
5819
5820 tc.closeWrite()
5821 if !rt.done() {
5822 t.Fatalf("after connection closed: RoundTrip still running; want done")
5823 }
5824 if rt.err() == nil {
5825 t.Fatalf("after connection closed: RoundTrip succeeded; want error")
5826 }
5827 }
5828
View as plain text