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 req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
1753 rt := tc.roundTrip(req)
1754 tc.wantFrameType(FrameHeaders)
1755
1756
1757 const windowIncrease = 1000
1758 tc.writeWindowUpdate(rt.streamID(), windowIncrease)
1759 tc.wantIdle()
1760
1761
1762 const maxWindowSize = (1 << 31) - 1
1763 const maxInitialWindowSize = maxWindowSize - windowIncrease
1764 tc.writeSettings(Setting{SettingInitialWindowSize, maxInitialWindowSize + 1})
1765 tc.wantGoAway(0, ErrCodeFlowControl)
1766 }
1767
1768
1769
1770 func TestTransportSettingsFlowControlUpdateWithinLimit(t *testing.T) {
1771 synctest.Test(t, testTransportSettingsFlowControlUpdateWithinLimit)
1772 }
1773 func testTransportSettingsFlowControlUpdateWithinLimit(t *testing.T) {
1774 tc := newTestClientConn(t)
1775 tc.greet()
1776
1777 req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
1778 rt := tc.roundTrip(req)
1779 tc.wantFrameType(FrameHeaders)
1780
1781
1782 const windowIncrease = 1000
1783 tc.writeWindowUpdate(rt.streamID(), windowIncrease)
1784 tc.wantIdle()
1785
1786
1787 const maxWindowSize = (1 << 31) - 1
1788 const maxInitialWindowSize = maxWindowSize - windowIncrease
1789 tc.writeSettings(Setting{SettingInitialWindowSize, maxInitialWindowSize})
1790 tc.wantSettingsAck()
1791 tc.wantIdle()
1792 }
1793
1794
1795 func TestTransportWindowUpdateBeyondLimit(t *testing.T) {
1796 synctest.Test(t, testTransportWindowUpdateBeyondLimit)
1797 }
1798 func testTransportWindowUpdateBeyondLimit(t *testing.T) {
1799 const windowIncrease uint32 = (1 << 31) - 1
1800 tc := newTestClientConn(t)
1801 tc.greet()
1802
1803 req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
1804 rt := tc.roundTrip(req)
1805 tc.wantHeaders(wantHeader{
1806 streamID: rt.streamID(),
1807 endStream: true,
1808 })
1809
1810 tc.writeWindowUpdate(rt.streamID(), windowIncrease)
1811 tc.wantRSTStream(rt.streamID(), ErrCodeFlowControl)
1812
1813 tc.writeWindowUpdate(0, windowIncrease)
1814 tc.wantClosed()
1815 }
1816
1817 func TestTransportDisableCompression(t *testing.T) {
1818 const body = "sup"
1819 ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) {
1820 want := http.Header{
1821 "User-Agent": []string{"Go-http-client/2.0"},
1822 }
1823 if !reflect.DeepEqual(r.Header, want) {
1824 t.Errorf("request headers = %v; want %v", r.Header, want)
1825 }
1826 })
1827
1828 tr := newTransport(t)
1829 tr.DisableCompression = true
1830
1831 req, err := http.NewRequest("GET", ts.URL, nil)
1832 if err != nil {
1833 t.Fatal(err)
1834 }
1835 res, err := tr.RoundTrip(req)
1836 if err != nil {
1837 t.Fatal(err)
1838 }
1839 defer res.Body.Close()
1840 }
1841
1842
1843 func TestTransportRejectsConnHeaders(t *testing.T) {
1844 ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) {
1845 var got []string
1846 for k := range r.Header {
1847 got = append(got, k)
1848 }
1849 sort.Strings(got)
1850 w.Header().Set("Got-Header", strings.Join(got, ","))
1851 })
1852
1853 tr := newTransport(t)
1854
1855 tests := []struct {
1856 key string
1857 value []string
1858 want string
1859 }{
1860 {
1861 key: "Upgrade",
1862 value: []string{"anything"},
1863 want: "ERROR: http2: invalid Upgrade request header: [\"anything\"]",
1864 },
1865 {
1866 key: "Connection",
1867 value: []string{"foo"},
1868 want: "ERROR: http2: invalid Connection request header: [\"foo\"]",
1869 },
1870 {
1871 key: "Connection",
1872 value: []string{"close"},
1873 want: "Accept-Encoding,User-Agent",
1874 },
1875 {
1876 key: "Connection",
1877 value: []string{"CLoSe"},
1878 want: "Accept-Encoding,User-Agent",
1879 },
1880 {
1881 key: "Connection",
1882 value: []string{"close", "something-else"},
1883 want: "ERROR: http2: invalid Connection request header: [\"close\" \"something-else\"]",
1884 },
1885 {
1886 key: "Connection",
1887 value: []string{"keep-alive"},
1888 want: "Accept-Encoding,User-Agent",
1889 },
1890 {
1891 key: "Connection",
1892 value: []string{"Keep-ALIVE"},
1893 want: "Accept-Encoding,User-Agent",
1894 },
1895 {
1896 key: "Proxy-Connection",
1897 value: []string{"keep-alive"},
1898 want: "Accept-Encoding,User-Agent",
1899 },
1900 {
1901 key: "Transfer-Encoding",
1902 value: []string{""},
1903 want: "Accept-Encoding,User-Agent",
1904 },
1905 {
1906 key: "Transfer-Encoding",
1907 value: []string{"foo"},
1908 want: "ERROR: http2: invalid Transfer-Encoding request header: [\"foo\"]",
1909 },
1910 {
1911 key: "Transfer-Encoding",
1912 value: []string{"chunked"},
1913 want: "Accept-Encoding,User-Agent",
1914 },
1915 {
1916 key: "Transfer-Encoding",
1917 value: []string{"chunKed"},
1918 want: "ERROR: http2: invalid Transfer-Encoding request header: [\"chunKed\"]",
1919 },
1920 {
1921 key: "Transfer-Encoding",
1922 value: []string{"chunked", "other"},
1923 want: "ERROR: http2: invalid Transfer-Encoding request header: [\"chunked\" \"other\"]",
1924 },
1925 {
1926 key: "Content-Length",
1927 value: []string{"123"},
1928 want: "Accept-Encoding,User-Agent",
1929 },
1930 {
1931 key: "Keep-Alive",
1932 value: []string{"doop"},
1933 want: "Accept-Encoding,User-Agent",
1934 },
1935 }
1936
1937 for _, tt := range tests {
1938 req, _ := http.NewRequest("GET", ts.URL, nil)
1939 req.Header[tt.key] = tt.value
1940 res, err := tr.RoundTrip(req)
1941 var got string
1942 if err != nil {
1943 got = fmt.Sprintf("ERROR: %v", err)
1944 } else {
1945 got = res.Header.Get("Got-Header")
1946 res.Body.Close()
1947 }
1948 if got != tt.want {
1949 t.Errorf("For key %q, value %q, got = %q; want %q", tt.key, tt.value, got, tt.want)
1950 }
1951 }
1952 }
1953
1954
1955
1956 func TestTransportRejectsContentLengthWithSign(t *testing.T) {
1957 tests := []struct {
1958 name string
1959 cl []string
1960 wantCL string
1961 }{
1962 {
1963 name: "proper content-length",
1964 cl: []string{"3"},
1965 wantCL: "3",
1966 },
1967 {
1968 name: "ignore cl with plus sign",
1969 cl: []string{"+3"},
1970 wantCL: "",
1971 },
1972 {
1973 name: "ignore cl with minus sign",
1974 cl: []string{"-3"},
1975 wantCL: "",
1976 },
1977 {
1978 name: "max int64, for safe uint64->int64 conversion",
1979 cl: []string{"9223372036854775807"},
1980 wantCL: "9223372036854775807",
1981 },
1982 {
1983 name: "overflows int64, so ignored",
1984 cl: []string{"9223372036854775808"},
1985 wantCL: "",
1986 },
1987 }
1988
1989 for _, tt := range tests {
1990 t.Run(tt.name, func(t *testing.T) {
1991 ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) {
1992 w.Header().Set("Content-Length", tt.cl[0])
1993 })
1994 tr := newTransport(t)
1995
1996 req, _ := http.NewRequest("HEAD", ts.URL, nil)
1997 res, err := tr.RoundTrip(req)
1998
1999 var got string
2000 if err != nil {
2001 got = fmt.Sprintf("ERROR: %v", err)
2002 } else {
2003 got = res.Header.Get("Content-Length")
2004 res.Body.Close()
2005 }
2006
2007 if got != tt.wantCL {
2008 t.Fatalf("Got: %q\nWant: %q", got, tt.wantCL)
2009 }
2010 })
2011 }
2012 }
2013
2014
2015
2016
2017 func TestTransportResponseContentLength(t *testing.T) {
2018 tests := []struct {
2019 name string
2020 clValues []string
2021 wantLen int
2022 }{
2023 {
2024 name: "single value",
2025 clValues: []string{"3"},
2026 wantLen: 3,
2027 },
2028 {
2029 name: "identical duplicate values",
2030 clValues: []string{"3", "3", "3"},
2031 wantLen: 3,
2032 },
2033 {
2034 name: "different duplicate values",
2035 clValues: []string{"3", "1", "3"},
2036 wantLen: -1,
2037 },
2038 {
2039 name: "extraneous whitespace",
2040 clValues: []string{" 3"},
2041 wantLen: -1,
2042 },
2043 {
2044 name: "identical duplicate values with extraneous whitespace",
2045 clValues: []string{"3", "3", " 3"},
2046 wantLen: -1,
2047 },
2048 {
2049 name: "plus sign",
2050 clValues: []string{"+3"},
2051 wantLen: -1,
2052 },
2053 {
2054 name: "non-numeric",
2055 clValues: []string{"abc"},
2056 wantLen: -1,
2057 },
2058 {
2059 name: "empty value",
2060 clValues: []string{""},
2061 wantLen: -1,
2062 },
2063 {
2064 name: "no header",
2065 wantLen: -1,
2066 },
2067 }
2068 for _, tt := range tests {
2069 synctestSubtest(t, tt.name, func(t *testing.T) {
2070 tc := newTestClientConn(t)
2071 tc.greet()
2072
2073 req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
2074 rt := tc.roundTrip(req)
2075
2076 headers := []string{":status", "200"}
2077 for _, val := range tt.clValues {
2078 headers = append(headers, "content-length", val)
2079 }
2080 tc.wantFrameType(FrameHeaders)
2081 tc.writeHeaders(HeadersFrameParam{
2082 StreamID: rt.streamID(),
2083 EndHeaders: true,
2084 BlockFragment: tc.makeHeaderBlockFragment(headers...),
2085 })
2086 body := slices.Repeat([]byte("a"), max(tt.wantLen, 1))
2087 tc.writeData(rt.streamID(), true, body)
2088
2089 res := rt.response()
2090 rt.wantBody(body)
2091
2092 if res.ContentLength != int64(tt.wantLen) {
2093 t.Errorf("got ContentLength = %d, want %d", res.ContentLength, tt.wantLen)
2094 }
2095 var wantHeader []string
2096 if tt.wantLen >= 0 {
2097 wantHeader = []string{strconv.FormatInt(int64(tt.wantLen), 10)}
2098 }
2099 if got := res.Header["Content-Length"]; !slices.Equal(got, wantHeader) {
2100 t.Errorf("got Header[%q] = %q, want %q", "Content-Length", got, wantHeader)
2101 }
2102 })
2103 }
2104 }
2105
2106
2107
2108
2109 func TestTransportResponseConnHeaders(t *testing.T) {
2110 tests := []struct {
2111 name string
2112 fields []string
2113 wantHeader http.Header
2114 }{
2115 {
2116 name: "unaffected header",
2117 fields: []string{"content-type", "text/plain"},
2118 wantHeader: http.Header{"Content-Type": {"text/plain"}},
2119 },
2120 {
2121 name: "transfer-encoding",
2122 fields: []string{"transfer-encoding", "chunked"},
2123 },
2124 {
2125 name: "transfer-encoding alongside content-length",
2126 fields: []string{"content-length", "-1", "transfer-encoding", "chunked"},
2127 },
2128 {
2129 name: "connection and keep-alive",
2130 fields: []string{"connection", "keep-alive", "keep-alive", "timeout=5"},
2131 },
2132 {
2133 name: "proxy-connection",
2134 fields: []string{"proxy-connection", "keep-alive"},
2135 },
2136 {
2137 name: "upgrade",
2138 fields: []string{"upgrade", "websocket"},
2139 },
2140 }
2141 for _, tt := range tests {
2142 synctestSubtest(t, tt.name, func(t *testing.T) {
2143 tc := newTestClientConn(t)
2144 tc.greet()
2145
2146 req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
2147 rt := tc.roundTrip(req)
2148
2149 headers := []string{":status", "200"}
2150 headers = append(headers, tt.fields...)
2151 tc.wantFrameType(FrameHeaders)
2152 tc.writeHeaders(HeadersFrameParam{
2153 StreamID: rt.streamID(),
2154 EndHeaders: true,
2155 BlockFragment: tc.makeHeaderBlockFragment(headers...),
2156 })
2157 tc.writeData(rt.streamID(), true, []byte("body"))
2158
2159 res := rt.response()
2160 rt.wantBody([]byte("body"))
2161
2162 if !maps.EqualFunc(res.Header, tt.wantHeader, slices.Equal) {
2163 t.Errorf("got Header = %q, want %q", res.Header, tt.wantHeader)
2164 }
2165 })
2166 }
2167 }
2168
2169
2170
2171 func TestTransportFailsOnInvalidHeadersAndTrailers(t *testing.T) {
2172 ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) {
2173 var got []string
2174 for k := range r.Header {
2175 got = append(got, k)
2176 }
2177 sort.Strings(got)
2178 w.Header().Set("Got-Header", strings.Join(got, ","))
2179 })
2180
2181 tests := [...]struct {
2182 h http.Header
2183 t http.Header
2184 wantErr string
2185 }{
2186 0: {
2187 h: http.Header{"with space": {"foo"}},
2188 wantErr: `net/http: invalid header field name "with space"`,
2189 },
2190 1: {
2191 h: http.Header{"name": {"Брэд"}},
2192 wantErr: "",
2193 },
2194 2: {
2195 h: http.Header{"имя": {"Brad"}},
2196 wantErr: `net/http: invalid header field name "имя"`,
2197 },
2198 3: {
2199 h: http.Header{"foo": {"foo\x01bar"}},
2200 wantErr: `net/http: invalid header field value for "foo"`,
2201 },
2202 4: {
2203 t: http.Header{"foo": {"foo\x01bar"}},
2204 wantErr: `net/http: invalid trailer field value for "foo"`,
2205 },
2206 5: {
2207 t: http.Header{"x-\r\nda": {"foo\x01bar"}},
2208 wantErr: `net/http: invalid trailer field name "x-\r\nda"`,
2209 },
2210 }
2211
2212 tr := newTransport(t)
2213
2214 for i, tt := range tests {
2215 req, _ := http.NewRequest("GET", ts.URL, nil)
2216 req.Header = tt.h
2217 if req.Header == nil {
2218 req.Header = http.Header{}
2219 }
2220 req.Trailer = tt.t
2221 res, err := tr.RoundTrip(req)
2222 var bad bool
2223 if tt.wantErr == "" {
2224 if err != nil {
2225 bad = true
2226 t.Errorf("case %d: error = %v; want no error", i, err)
2227 }
2228 } else {
2229 if !strings.Contains(fmt.Sprint(err), tt.wantErr) {
2230 bad = true
2231 t.Errorf("case %d: error = %v; want error %q", i, err, tt.wantErr)
2232 }
2233 }
2234 if err == nil {
2235 if bad {
2236 t.Logf("case %d: server got headers %q", i, res.Header.Get("Got-Header"))
2237 }
2238 res.Body.Close()
2239 }
2240 }
2241 }
2242
2243
2244
2245
2246 func TestTransportReadHeadResponse(t *testing.T) { synctest.Test(t, testTransportReadHeadResponse) }
2247 func testTransportReadHeadResponse(t *testing.T) {
2248 tc := newTestClientConn(t)
2249 tc.greet()
2250
2251 req, _ := http.NewRequest("HEAD", "https://dummy.tld/", nil)
2252 rt := tc.roundTrip(req)
2253
2254 tc.wantFrameType(FrameHeaders)
2255 tc.writeHeaders(HeadersFrameParam{
2256 StreamID: rt.streamID(),
2257 EndHeaders: true,
2258 EndStream: false,
2259 BlockFragment: tc.makeHeaderBlockFragment(
2260 ":status", "200",
2261 "content-length", "123",
2262 ),
2263 })
2264 tc.writeData(rt.streamID(), true, nil)
2265
2266 res := rt.response()
2267 if res.ContentLength != 123 {
2268 t.Fatalf("Content-Length = %d; want 123", res.ContentLength)
2269 }
2270 rt.wantBody(nil)
2271 }
2272
2273 func TestTransportReadHeadResponseWithBody(t *testing.T) {
2274 synctest.Test(t, testTransportReadHeadResponseWithBody)
2275 }
2276 func testTransportReadHeadResponseWithBody(t *testing.T) {
2277
2278
2279 log.SetOutput(io.Discard)
2280 defer log.SetOutput(os.Stderr)
2281
2282 response := "redirecting to /elsewhere"
2283 tc := newTestClientConn(t)
2284 tc.greet()
2285
2286 req, _ := http.NewRequest("HEAD", "https://dummy.tld/", nil)
2287 rt := tc.roundTrip(req)
2288
2289 tc.wantFrameType(FrameHeaders)
2290 tc.writeHeaders(HeadersFrameParam{
2291 StreamID: rt.streamID(),
2292 EndHeaders: true,
2293 EndStream: false,
2294 BlockFragment: tc.makeHeaderBlockFragment(
2295 ":status", "200",
2296 "content-length", strconv.Itoa(len(response)),
2297 ),
2298 })
2299 tc.writeData(rt.streamID(), true, []byte(response))
2300
2301 res := rt.response()
2302 if res.ContentLength != int64(len(response)) {
2303 t.Fatalf("Content-Length = %d; want %d", res.ContentLength, len(response))
2304 }
2305 rt.wantBody(nil)
2306 }
2307
2308 type neverEnding byte
2309
2310 func (b neverEnding) Read(p []byte) (int, error) {
2311 for i := range p {
2312 p[i] = byte(b)
2313 }
2314 return len(p), nil
2315 }
2316
2317
2318
2319 func TestTransportStreamEndsWhileBodyIsBeingWritten(t *testing.T) {
2320 synctest.Test(t, testTransportStreamEndsWhileBodyIsBeingWritten)
2321 }
2322 func testTransportStreamEndsWhileBodyIsBeingWritten(t *testing.T) {
2323 body := "this is the client request body"
2324 const windowSize = 10
2325
2326 tc := newTestClientConn(t)
2327 tc.greet(Setting{SettingInitialWindowSize, windowSize})
2328
2329
2330 req, _ := http.NewRequest("PUT", "https://dummy.tld/", strings.NewReader(body))
2331 rt := tc.roundTrip(req)
2332 tc.wantFrameType(FrameHeaders)
2333 tc.wantData(wantData{
2334 streamID: rt.streamID(),
2335 endStream: false,
2336 size: windowSize,
2337 })
2338
2339
2340 tc.writeHeaders(HeadersFrameParam{
2341 StreamID: rt.streamID(),
2342 EndHeaders: true,
2343 EndStream: true,
2344 BlockFragment: tc.makeHeaderBlockFragment(
2345 ":status", "413",
2346 ),
2347 })
2348 rt.wantStatus(413)
2349 }
2350
2351 func TestTransportFlowControl(t *testing.T) { synctest.Test(t, testTransportFlowControl) }
2352 func testTransportFlowControl(t *testing.T) {
2353 const maxBuffer = 64 << 10
2354 tc := newTestClientConn(t, func(tr *http.Transport) {
2355 tr.HTTP2 = &http.HTTP2Config{
2356 MaxReceiveBufferPerConnection: maxBuffer,
2357 MaxReceiveBufferPerStream: maxBuffer,
2358 MaxReadFrameSize: 16 << 20,
2359 }
2360 })
2361 tc.greet()
2362
2363 req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
2364 rt := tc.roundTrip(req)
2365 tc.wantFrameType(FrameHeaders)
2366
2367 tc.writeHeaders(HeadersFrameParam{
2368 StreamID: rt.streamID(),
2369 EndHeaders: true,
2370 EndStream: false,
2371 BlockFragment: tc.makeHeaderBlockFragment(
2372 ":status", "200",
2373 ),
2374 })
2375 rt.wantStatus(200)
2376
2377
2378
2379
2380 tc.writeData(rt.streamID(), false, make([]byte, maxBuffer))
2381 tc.wantIdle()
2382
2383
2384
2385 resp := rt.response()
2386 if _, err := io.ReadFull(resp.Body, make([]byte, maxBuffer)); err != nil {
2387 t.Fatalf("io.Body.Read: %v", err)
2388 }
2389 var connTokens, streamTokens uint32
2390 for {
2391 f := tc.readFrame()
2392 if f == nil {
2393 break
2394 }
2395 wu, ok := f.(*WindowUpdateFrame)
2396 if !ok {
2397 t.Fatalf("received unexpected frame %T (want WINDOW_UPDATE)", f)
2398 }
2399 switch wu.StreamID {
2400 case 0:
2401 connTokens += wu.Increment
2402 case wu.StreamID:
2403 streamTokens += wu.Increment
2404 default:
2405 t.Fatalf("received unexpected WINDOW_UPDATE for stream %v", wu.StreamID)
2406 }
2407 }
2408 if got, want := connTokens, uint32(maxBuffer); got != want {
2409 t.Errorf("transport provided %v bytes of connection WINDOW_UPDATE, want %v", got, want)
2410 }
2411 if got, want := streamTokens, uint32(maxBuffer); got != want {
2412 t.Errorf("transport provided %v bytes of stream WINDOW_UPDATE, want %v", got, want)
2413 }
2414 }
2415
2416
2417
2418
2419
2420
2421 func TestTransportUsesGoAwayDebugError_RoundTrip(t *testing.T) {
2422 synctest.Test(t, func(t *testing.T) {
2423 testTransportUsesGoAwayDebugError(t, false)
2424 })
2425 }
2426
2427 func TestTransportUsesGoAwayDebugError_Body(t *testing.T) {
2428 synctest.Test(t, func(t *testing.T) {
2429 testTransportUsesGoAwayDebugError(t, true)
2430 })
2431 }
2432
2433 func testTransportUsesGoAwayDebugError(t *testing.T, failMidBody bool) {
2434 tc := newTestClientConn(t)
2435 tc.greet()
2436
2437 const goAwayErrCode = ErrCodeHTTP11Required
2438 const goAwayDebugData = "some debug data"
2439
2440 req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
2441 rt := tc.roundTrip(req)
2442
2443 tc.wantFrameType(FrameHeaders)
2444
2445 if failMidBody {
2446 tc.writeHeaders(HeadersFrameParam{
2447 StreamID: rt.streamID(),
2448 EndHeaders: true,
2449 EndStream: false,
2450 BlockFragment: tc.makeHeaderBlockFragment(
2451 ":status", "200",
2452 "content-length", "123",
2453 ),
2454 })
2455 }
2456
2457
2458
2459 tc.writeGoAway(5, ErrCodeNo, []byte(goAwayDebugData))
2460 tc.writeGoAway(5, goAwayErrCode, nil)
2461 tc.closeWrite()
2462
2463 res, err := rt.result()
2464 whence := "RoundTrip"
2465 if failMidBody {
2466 whence = "Body.Read"
2467 if err != nil {
2468 t.Fatalf("RoundTrip error = %v, want success", err)
2469 }
2470 _, err = res.Body.Read(make([]byte, 1))
2471 }
2472
2473 want := GoAwayError{
2474 LastStreamID: 5,
2475 ErrCode: goAwayErrCode,
2476 DebugData: goAwayDebugData,
2477 }
2478 if !reflect.DeepEqual(err, want) {
2479 t.Errorf("%v error = %T: %#v, want %T (%#v)", whence, err, err, want, want)
2480 }
2481 }
2482
2483
2484
2485 func TestTransportGoAwayWithNoConns(t *testing.T) { synctest.Test(t, testTransportGoAwayWithNoConns) }
2486 func testTransportGoAwayWithNoConns(t *testing.T) {
2487 tt := newTestTransportWithUnusedConn(t)
2488 tc := tt.getConn()
2489 tc.greet()
2490 tc.writeGoAway(1, ErrCodeNo, nil)
2491 tc.wantClosed()
2492 }
2493
2494 func testTransportReturnsUnusedFlowControl(t *testing.T, oneDataFrame bool) {
2495 tc := newTestClientConn(t)
2496 tc.greet()
2497
2498 req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
2499 rt := tc.roundTrip(req)
2500
2501 tc.wantFrameType(FrameHeaders)
2502 tc.writeHeaders(HeadersFrameParam{
2503 StreamID: rt.streamID(),
2504 EndHeaders: true,
2505 EndStream: false,
2506 BlockFragment: tc.makeHeaderBlockFragment(
2507 ":status", "200",
2508 "content-length", "5000",
2509 ),
2510 })
2511 initialInflow := tc.inflowWindow(0)
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523 const streamNotEnded = false
2524 if oneDataFrame {
2525 tc.writeData(rt.streamID(), streamNotEnded, make([]byte, 5000))
2526 } else {
2527 tc.writeData(rt.streamID(), streamNotEnded, make([]byte, 1))
2528 }
2529
2530 res := rt.response()
2531 if n, err := res.Body.Read(make([]byte, 1)); err != nil || n != 1 {
2532 t.Fatalf("body read = %v, %v; want 1, nil", n, err)
2533 }
2534 res.Body.Close()
2535 synctest.Wait()
2536
2537 sentAdditionalData := false
2538 tc.wantUnorderedFrames(
2539 func(f *RSTStreamFrame) bool {
2540 if f.ErrCode != ErrCodeCancel {
2541 t.Fatalf("Expected a RSTStreamFrame with code cancel; got %v", SummarizeFrame(f))
2542 }
2543 if !oneDataFrame {
2544
2545 tc.writeData(rt.streamID(), streamNotEnded, make([]byte, 4999))
2546 sentAdditionalData = true
2547 }
2548 return true
2549 },
2550 func(f *WindowUpdateFrame) bool {
2551 if !oneDataFrame && !sentAdditionalData {
2552 t.Fatalf("Got WindowUpdateFrame, don't expect one yet")
2553 }
2554 if f.Increment != 5000 {
2555 t.Fatalf("Expected WindowUpdateFrames for 5000 bytes; got %v", SummarizeFrame(f))
2556 }
2557 return true
2558 },
2559 )
2560
2561 if got, want := tc.inflowWindow(0), initialInflow; got != want {
2562 t.Fatalf("connection flow tokens = %v, want %v", got, want)
2563 }
2564 }
2565
2566
2567 func TestTransportReturnsUnusedFlowControlSingleWrite(t *testing.T) {
2568 synctest.Test(t, func(t *testing.T) {
2569 testTransportReturnsUnusedFlowControl(t, true)
2570 })
2571 }
2572
2573
2574 func TestTransportReturnsUnusedFlowControlMultipleWrites(t *testing.T) {
2575 synctest.Test(t, func(t *testing.T) {
2576 testTransportReturnsUnusedFlowControl(t, false)
2577 })
2578 }
2579
2580
2581
2582 func TestTransportAdjustsFlowControl(t *testing.T) { synctest.Test(t, testTransportAdjustsFlowControl) }
2583 func testTransportAdjustsFlowControl(t *testing.T) {
2584 const bodySize = 1 << 20
2585
2586 tc := newTestClientConn(t)
2587 tc.wantFrameType(FrameSettings)
2588 tc.wantFrameType(FrameWindowUpdate)
2589
2590
2591 body := tc.newRequestBody()
2592 body.writeBytes(bodySize)
2593 body.closeWithError(io.EOF)
2594
2595 req, _ := http.NewRequest("POST", "https://dummy.tld/", body)
2596 rt := tc.roundTrip(req)
2597
2598 tc.wantFrameType(FrameHeaders)
2599
2600 gotBytes := int64(0)
2601 for {
2602 f := readFrame[*DataFrame](t, tc)
2603 gotBytes += int64(len(f.Data()))
2604
2605
2606 if gotBytes >= InitialWindowSize/2 {
2607 break
2608 }
2609 }
2610
2611 tc.writeSettings(Setting{ID: SettingInitialWindowSize, Val: bodySize})
2612 tc.writeWindowUpdate(0, bodySize)
2613 tc.writeSettingsAck()
2614
2615 tc.wantUnorderedFrames(
2616 func(f *SettingsFrame) bool { return true },
2617 func(f *DataFrame) bool {
2618 gotBytes += int64(len(f.Data()))
2619 return f.StreamEnded()
2620 },
2621 )
2622
2623 if gotBytes != bodySize {
2624 t.Fatalf("server received %v bytes of body, want %v", gotBytes, bodySize)
2625 }
2626
2627 tc.writeHeaders(HeadersFrameParam{
2628 StreamID: rt.streamID(),
2629 EndHeaders: true,
2630 EndStream: true,
2631 BlockFragment: tc.makeHeaderBlockFragment(
2632 ":status", "200",
2633 ),
2634 })
2635 rt.wantStatus(200)
2636 }
2637
2638
2639 func TestTransportReturnsDataPaddingFlowControl(t *testing.T) {
2640 synctest.Test(t, testTransportReturnsDataPaddingFlowControl)
2641 }
2642 func testTransportReturnsDataPaddingFlowControl(t *testing.T) {
2643 tc := newTestClientConn(t)
2644 tc.greet()
2645
2646 req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
2647 rt := tc.roundTrip(req)
2648
2649 tc.wantFrameType(FrameHeaders)
2650 tc.writeHeaders(HeadersFrameParam{
2651 StreamID: rt.streamID(),
2652 EndHeaders: true,
2653 EndStream: false,
2654 BlockFragment: tc.makeHeaderBlockFragment(
2655 ":status", "200",
2656 "content-length", "5000",
2657 ),
2658 })
2659
2660 initialConnWindow := tc.inflowWindow(0)
2661 initialStreamWindow := tc.inflowWindow(rt.streamID())
2662
2663 pad := make([]byte, 5)
2664 tc.writeDataPadded(rt.streamID(), false, make([]byte, 5000), pad)
2665
2666
2667 synctest.Wait()
2668 if got, want := tc.inflowWindow(0), initialConnWindow-5000; got != want {
2669 t.Errorf("conn inflow window = %v, want %v", got, want)
2670 }
2671 if got, want := tc.inflowWindow(rt.streamID()), initialStreamWindow-5000; got != want {
2672 t.Errorf("stream inflow window = %v, want %v", got, want)
2673 }
2674 }
2675
2676
2677
2678 func TestTransportReturnsErrorOnBadResponseHeaders(t *testing.T) {
2679 synctest.Test(t, testTransportReturnsErrorOnBadResponseHeaders)
2680 }
2681 func testTransportReturnsErrorOnBadResponseHeaders(t *testing.T) {
2682 tc := newTestClientConn(t)
2683 tc.greet()
2684
2685 req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
2686 rt := tc.roundTrip(req)
2687
2688 tc.wantFrameType(FrameHeaders)
2689 tc.writeHeaders(HeadersFrameParam{
2690 StreamID: rt.streamID(),
2691 EndHeaders: true,
2692 EndStream: false,
2693 BlockFragment: tc.makeHeaderBlockFragment(
2694 ":status", "200",
2695 " content-type", "bogus",
2696 ),
2697 })
2698
2699 err := rt.err()
2700 want := StreamError{1, ErrCodeProtocol, HeaderFieldNameError(" content-type")}
2701 if !reflect.DeepEqual(err, want) {
2702 t.Fatalf("RoundTrip error = %#v; want %#v", err, want)
2703 }
2704
2705 fr := readFrame[*RSTStreamFrame](t, tc)
2706 if fr.StreamID != 1 || fr.ErrCode != ErrCodeProtocol {
2707 t.Errorf("Frame = %v; want RST_STREAM for stream 1 with ErrCodeProtocol", SummarizeFrame(fr))
2708 }
2709 }
2710
2711
2712
2713 type byteAndEOFReader byte
2714
2715 func (b byteAndEOFReader) Read(p []byte) (n int, err error) {
2716 if len(p) == 0 {
2717 panic("unexpected useless call")
2718 }
2719 p[0] = byte(b)
2720 return 1, io.EOF
2721 }
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732 func TestTransportBodyDoubleEndStream(t *testing.T) {
2733 synctest.Test(t, testTransportBodyDoubleEndStream)
2734 }
2735 func testTransportBodyDoubleEndStream(t *testing.T) {
2736 ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) {
2737
2738 })
2739
2740 tr := newTransport(t)
2741
2742 for i := range 2 {
2743 req, _ := http.NewRequest("POST", ts.URL, byteAndEOFReader('a'))
2744 req.ContentLength = 1
2745 res, err := tr.RoundTrip(req)
2746 if err != nil {
2747 t.Fatalf("failure on req %d: %v", i+1, err)
2748 }
2749 defer res.Body.Close()
2750 }
2751 }
2752
2753
2754 func TestTransportRequestPathPseudo(t *testing.T) {
2755 type result struct {
2756 path string
2757 err string
2758 }
2759 tests := []struct {
2760 req *http.Request
2761 want result
2762 }{
2763 0: {
2764 req: &http.Request{
2765 Method: "GET",
2766 URL: &url.URL{
2767 Host: "foo.com",
2768 Path: "/foo",
2769 },
2770 },
2771 want: result{path: "/foo"},
2772 },
2773
2774
2775
2776 1: {
2777 req: &http.Request{
2778 Method: "GET",
2779 URL: &url.URL{
2780 Host: "foo.com",
2781 Path: "//foo",
2782 },
2783 },
2784 want: result{path: "//foo"},
2785 },
2786
2787
2788 2: {
2789 req: &http.Request{
2790 Method: "GET",
2791 URL: &url.URL{
2792 Scheme: "https",
2793 Opaque: "//foo.com/path",
2794 Host: "foo.com",
2795 Path: "/ignored",
2796 },
2797 },
2798 want: result{path: "/path"},
2799 },
2800
2801
2802 3: {
2803 req: &http.Request{
2804 Method: "GET",
2805 Host: "bar.com",
2806 URL: &url.URL{
2807 Scheme: "https",
2808 Opaque: "//bar.com/path",
2809 Host: "foo.com",
2810 Path: "/ignored",
2811 },
2812 },
2813 want: result{path: "/path"},
2814 },
2815
2816
2817 4: {
2818 req: &http.Request{
2819 Method: "GET",
2820 URL: &url.URL{
2821 Opaque: "/path",
2822 Host: "foo.com",
2823 Path: "/ignored",
2824 },
2825 },
2826 want: result{path: "/path"},
2827 },
2828
2829
2830 5: {
2831 req: &http.Request{
2832 Method: "GET",
2833 URL: &url.URL{
2834 Scheme: "https",
2835 Opaque: "//unknown_host/path",
2836 Host: "foo.com",
2837 Path: "/ignored",
2838 },
2839 },
2840 want: result{err: `invalid request :path "https://unknown_host/path" from URL.Opaque = "//unknown_host/path"`},
2841 },
2842
2843
2844 6: {
2845 req: &http.Request{
2846 Method: "CONNECT",
2847 URL: &url.URL{
2848 Host: "foo.com",
2849 },
2850 },
2851 want: result{},
2852 },
2853 }
2854 for i, tt := range tests {
2855 hbuf := &bytes.Buffer{}
2856 henc := hpack.NewEncoder(hbuf)
2857 _, err := httpcommon.EncodeHeaders(context.Background(), httpcommon.EncodeHeadersParam{
2858 Request: httpcommon.Request{
2859 Header: tt.req.Header,
2860 Trailer: tt.req.Trailer,
2861 URL: tt.req.URL,
2862 Host: tt.req.Host,
2863 Method: tt.req.Method,
2864 ActualContentLength: tt.req.ContentLength,
2865 },
2866 AddGzipHeader: false,
2867 PeerMaxHeaderListSize: 0xffffffffffffffff,
2868 }, func(name, value string) {
2869 henc.WriteField(hpack.HeaderField{Name: name, Value: value})
2870 })
2871 hdrs := hbuf.Bytes()
2872 var got result
2873 hpackDec := hpack.NewDecoder(InitialHeaderTableSize, func(f hpack.HeaderField) {
2874 if f.Name == ":path" {
2875 got.path = f.Value
2876 }
2877 })
2878 if err != nil {
2879 got.err = err.Error()
2880 } else if len(hdrs) > 0 {
2881 if _, err := hpackDec.Write(hdrs); err != nil {
2882 t.Errorf("%d. bogus hpack: %v", i, err)
2883 continue
2884 }
2885 }
2886 if got != tt.want {
2887 t.Errorf("%d. got %+v; want %+v", i, got, tt.want)
2888 }
2889
2890 }
2891
2892 }
2893
2894
2895
2896 func TestRoundTripDoesntConsumeRequestBodyEarly(t *testing.T) {
2897 synctest.Test(t, testRoundTripDoesntConsumeRequestBodyEarly)
2898 }
2899 func testRoundTripDoesntConsumeRequestBodyEarly(t *testing.T) {
2900 tc := newTestClientConn(t)
2901 tc.greet()
2902 tc.closeWrite()
2903 synctest.Wait()
2904
2905 const body = "foo"
2906 req, _ := http.NewRequest("POST", "http://foo.com/", io.NopCloser(strings.NewReader(body)))
2907 rt := tc.roundTrip(req)
2908 if err := rt.err(); err != ErrClientConnNotEstablished {
2909 t.Fatalf("RoundTrip = %v; want errClientConnNotEstablished", err)
2910 }
2911
2912 slurp, err := io.ReadAll(req.Body)
2913 if err != nil {
2914 t.Errorf("ReadAll = %v", err)
2915 }
2916 if string(slurp) != body {
2917 t.Errorf("Body = %q; want %q", slurp, body)
2918 }
2919 }
2920
2921
2922
2923
2924
2925 func TestTransportCancelDataResponseRace(t *testing.T) {
2926 cancel := make(chan struct{})
2927 clientGotResponse := make(chan bool, 1)
2928
2929 const msg = "Hello."
2930 ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) {
2931 if strings.Contains(r.URL.Path, "/hello") {
2932 time.Sleep(50 * time.Millisecond)
2933 io.WriteString(w, msg)
2934 return
2935 }
2936 for i := range 50 {
2937 io.WriteString(w, "Some data.")
2938 w.(http.Flusher).Flush()
2939 if i == 2 {
2940 <-clientGotResponse
2941 close(cancel)
2942 }
2943 time.Sleep(10 * time.Millisecond)
2944 }
2945 })
2946
2947 tr := newTransport(t)
2948
2949 c := &http.Client{Transport: tr}
2950 req, _ := http.NewRequest("GET", ts.URL, nil)
2951 req.Cancel = cancel
2952 res, err := c.Do(req)
2953 clientGotResponse <- true
2954 if err != nil {
2955 t.Fatal(err)
2956 }
2957 if _, err = io.Copy(io.Discard, res.Body); err == nil {
2958 t.Fatal("unexpected success")
2959 }
2960
2961 res, err = c.Get(ts.URL + "/hello")
2962 if err != nil {
2963 t.Fatal(err)
2964 }
2965 slurp, err := io.ReadAll(res.Body)
2966 if err != nil {
2967 t.Fatal(err)
2968 }
2969 if string(slurp) != msg {
2970 t.Errorf("Got = %q; want %q", slurp, msg)
2971 }
2972 }
2973
2974
2975
2976 func TestTransportNoRaceOnRequestObjectAfterRequestComplete(t *testing.T) {
2977 ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) {
2978 w.WriteHeader(200)
2979 io.WriteString(w, "body")
2980 })
2981
2982 tr := newTransport(t)
2983
2984 req, _ := http.NewRequest("GET", ts.URL, nil)
2985 resp, err := tr.RoundTrip(req)
2986 if err != nil {
2987 t.Fatal(err)
2988 }
2989 if _, err = io.Copy(io.Discard, resp.Body); err != nil {
2990 t.Fatalf("error reading response body: %v", err)
2991 }
2992 if err := resp.Body.Close(); err != nil {
2993 t.Fatalf("error closing response body: %v", err)
2994 }
2995
2996
2997 req.Header = http.Header{}
2998 }
2999
3000 func TestTransportCloseAfterLostPing(t *testing.T) { synctest.Test(t, testTransportCloseAfterLostPing) }
3001 func testTransportCloseAfterLostPing(t *testing.T) {
3002 tc := newTestClientConn(t, func(h2 *http.HTTP2Config) {
3003 h2.PingTimeout = 1 * time.Second
3004 h2.SendPingTimeout = 1 * time.Second
3005 })
3006 tc.greet()
3007
3008 req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
3009 rt := tc.roundTrip(req)
3010 tc.wantFrameType(FrameHeaders)
3011
3012 time.Sleep(1 * time.Second)
3013 tc.wantFrameType(FramePing)
3014
3015 time.Sleep(1 * time.Second)
3016 err := rt.err()
3017 if err == nil || !strings.Contains(err.Error(), "client connection lost") {
3018 t.Fatalf("expected to get error about \"connection lost\", got %v", err)
3019 }
3020 }
3021
3022 func TestTransportPingWriteBlocks(t *testing.T) {
3023 ts := newTestServer(t,
3024 func(w http.ResponseWriter, r *http.Request) {},
3025 )
3026 tr := newTransport(t)
3027 tr.Dial = func(network, addr string) (net.Conn, error) {
3028 s, c := net.Pipe()
3029 go func() {
3030 srv := tls.Server(s, tlsConfigInsecure)
3031 srv.Handshake()
3032
3033
3034
3035
3036 var buf [1024]byte
3037 s.Read(buf[:])
3038 }()
3039 return c, nil
3040 }
3041 tr.HTTP2.PingTimeout = 1 * time.Millisecond
3042 tr.HTTP2.SendPingTimeout = 1 * time.Millisecond
3043 c := &http.Client{Transport: tr}
3044 _, err := c.Get(ts.URL)
3045 if err == nil {
3046 t.Fatalf("Get = nil, want error")
3047 }
3048 }
3049
3050 func TestTransportPingWhenReadingMultiplePings(t *testing.T) {
3051 synctest.Test(t, testTransportPingWhenReadingMultiplePings)
3052 }
3053 func testTransportPingWhenReadingMultiplePings(t *testing.T) {
3054 tc := newTestClientConn(t, func(h2 *http.HTTP2Config) {
3055 h2.SendPingTimeout = 1000 * time.Millisecond
3056 })
3057 tc.greet()
3058
3059 ctx, cancel := context.WithCancel(context.Background())
3060 req, _ := http.NewRequestWithContext(ctx, "GET", "https://dummy.tld/", nil)
3061 rt := tc.roundTrip(req)
3062
3063 tc.wantFrameType(FrameHeaders)
3064 tc.writeHeaders(HeadersFrameParam{
3065 StreamID: rt.streamID(),
3066 EndHeaders: true,
3067 EndStream: false,
3068 BlockFragment: tc.makeHeaderBlockFragment(
3069 ":status", "200",
3070 ),
3071 })
3072
3073 for range 5 {
3074
3075 time.Sleep(999 * time.Millisecond)
3076 if f := tc.readFrame(); f != nil {
3077 t.Fatalf("unexpected frame: %v", f)
3078 }
3079
3080
3081 time.Sleep(1 * time.Millisecond)
3082 f := readFrame[*PingFrame](t, tc)
3083 tc.writePing(true, f.Data)
3084 }
3085
3086
3087 cancel()
3088 synctest.Wait()
3089
3090 tc.wantFrameType(FrameRSTStream)
3091 _, err := rt.readBody()
3092 if err == nil {
3093 t.Fatalf("Response.Body.Read() = %v, want error", err)
3094 }
3095 }
3096
3097 func TestTransportPingWhenReadingPingDisabled(t *testing.T) {
3098 synctest.Test(t, testTransportPingWhenReadingPingDisabled)
3099 }
3100 func testTransportPingWhenReadingPingDisabled(t *testing.T) {
3101 tc := newTestClientConn(t, func(h2 *http.HTTP2Config) {
3102 h2.SendPingTimeout = 0
3103 })
3104 tc.greet()
3105
3106 req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
3107 rt := tc.roundTrip(req)
3108
3109 tc.wantFrameType(FrameHeaders)
3110 tc.writeHeaders(HeadersFrameParam{
3111 StreamID: rt.streamID(),
3112 EndHeaders: true,
3113 EndStream: false,
3114 BlockFragment: tc.makeHeaderBlockFragment(
3115 ":status", "200",
3116 ),
3117 })
3118
3119
3120 time.Sleep(1 * time.Minute)
3121 if f := tc.readFrame(); f != nil {
3122 t.Fatalf("unexpected frame: %v", f)
3123 }
3124 }
3125
3126 func TestTransportRetryAfterGOAWAYNoRetry(t *testing.T) {
3127 synctest.Test(t, testTransportRetryAfterGOAWAYNoRetry)
3128 }
3129 func testTransportRetryAfterGOAWAYNoRetry(t *testing.T) {
3130 tt := newTestTransport(t)
3131
3132 req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
3133 rt := tt.roundTrip(req)
3134
3135
3136
3137
3138
3139 tc := tt.getConn()
3140 tc.wantFrameType(FrameSettings)
3141 tc.wantFrameType(FrameWindowUpdate)
3142 tc.wantHeaders(wantHeader{
3143 streamID: 1,
3144 endStream: true,
3145 })
3146 tc.writeSettings()
3147 tc.writeGoAway(0 , ErrCodeInternal, nil)
3148 if rt.err() == nil {
3149 t.Fatalf("after GOAWAY, RoundTrip is not done, want error")
3150 }
3151 }
3152
3153 func TestTransportRetryAfterGOAWAYRetry(t *testing.T) {
3154 synctest.Test(t, testTransportRetryAfterGOAWAYRetry)
3155 }
3156 func testTransportRetryAfterGOAWAYRetry(t *testing.T) {
3157 tt := newTestTransport(t)
3158
3159 req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
3160 rt := tt.roundTrip(req)
3161
3162
3163
3164
3165
3166 tc := tt.getConn()
3167 tc.wantFrameType(FrameSettings)
3168 tc.wantFrameType(FrameWindowUpdate)
3169 tc.wantHeaders(wantHeader{
3170 streamID: 1,
3171 endStream: true,
3172 })
3173 tc.writeSettings()
3174 tc.writeGoAway(0 , ErrCodeNo, nil)
3175 if rt.done() {
3176 t.Fatalf("after GOAWAY, RoundTrip is done; want it to be retrying")
3177 }
3178
3179
3180 tc = tt.getConn()
3181 tc.wantFrameType(FrameSettings)
3182 tc.wantFrameType(FrameWindowUpdate)
3183 tc.wantHeaders(wantHeader{
3184 streamID: 1,
3185 endStream: true,
3186 })
3187 tc.writeSettings()
3188 tc.writeHeaders(HeadersFrameParam{
3189 StreamID: 1,
3190 EndHeaders: true,
3191 EndStream: true,
3192 BlockFragment: tc.makeHeaderBlockFragment(
3193 ":status", "200",
3194 ),
3195 })
3196
3197 rt.wantStatus(200)
3198 }
3199
3200 func TestTransportRetryAfterGOAWAYSecondRequest(t *testing.T) {
3201 synctest.Test(t, testTransportRetryAfterGOAWAYSecondRequest)
3202 }
3203 func testTransportRetryAfterGOAWAYSecondRequest(t *testing.T) {
3204 tt := newTestTransport(t)
3205
3206
3207 req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
3208 rt1 := tt.roundTrip(req)
3209 tc := tt.getConn()
3210 tc.wantFrameType(FrameSettings)
3211 tc.wantFrameType(FrameWindowUpdate)
3212 tc.wantHeaders(wantHeader{
3213 streamID: 1,
3214 endStream: true,
3215 })
3216 tc.writeSettings()
3217 tc.wantFrameType(FrameSettings)
3218 tc.writeHeaders(HeadersFrameParam{
3219 StreamID: 1,
3220 EndHeaders: true,
3221 EndStream: true,
3222 BlockFragment: tc.makeHeaderBlockFragment(
3223 ":status", "200",
3224 ),
3225 })
3226 rt1.wantStatus(200)
3227
3228
3229
3230
3231
3232 req, _ = http.NewRequest("GET", "https://dummy.tld/", nil)
3233 rt2 := tt.roundTrip(req)
3234
3235
3236 tc.wantHeaders(wantHeader{
3237 streamID: 3,
3238 endStream: true,
3239 })
3240 tc.writeSettings()
3241 tc.writeGoAway(1 , ErrCodeProtocol, nil)
3242 if rt2.done() {
3243 t.Fatalf("after GOAWAY, RoundTrip is done; want it to be retrying")
3244 }
3245
3246
3247 tc = tt.getConn()
3248 tc.wantFrameType(FrameSettings)
3249 tc.wantFrameType(FrameWindowUpdate)
3250 tc.wantHeaders(wantHeader{
3251 streamID: 1,
3252 endStream: true,
3253 })
3254 tc.writeSettings()
3255 tc.writeHeaders(HeadersFrameParam{
3256 StreamID: 1,
3257 EndHeaders: true,
3258 EndStream: true,
3259 BlockFragment: tc.makeHeaderBlockFragment(
3260 ":status", "200",
3261 ),
3262 })
3263 rt2.wantStatus(200)
3264 }
3265
3266 func TestTransportRetryAfterRefusedStream(t *testing.T) {
3267 synctest.Test(t, testTransportRetryAfterRefusedStream)
3268 }
3269 func testTransportRetryAfterRefusedStream(t *testing.T) {
3270 tt := newTestTransport(t)
3271
3272 req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
3273 rt := tt.roundTrip(req)
3274
3275
3276 tc := tt.getConn()
3277 tc.wantFrameType(FrameSettings)
3278 tc.wantFrameType(FrameWindowUpdate)
3279 tc.wantHeaders(wantHeader{
3280 streamID: 1,
3281 endStream: true,
3282 })
3283 tc.writeSettings()
3284 tc.wantFrameType(FrameSettings)
3285 tc.writeRSTStream(1, ErrCodeRefusedStream)
3286 if rt.done() {
3287 t.Fatalf("after RST_STREAM, RoundTrip is done; want it to be retrying")
3288 }
3289
3290
3291 tc.wantHeaders(wantHeader{
3292 streamID: 3,
3293 endStream: true,
3294 })
3295 tc.writeSettings()
3296 tc.writeHeaders(HeadersFrameParam{
3297 StreamID: 3,
3298 EndHeaders: true,
3299 EndStream: true,
3300 BlockFragment: tc.makeHeaderBlockFragment(
3301 ":status", "204",
3302 ),
3303 })
3304
3305 rt.wantStatus(204)
3306 }
3307
3308 func TestTransportRetryHasLimit(t *testing.T) { synctest.Test(t, testTransportRetryHasLimit) }
3309 func testTransportRetryHasLimit(t *testing.T) {
3310 tt := newTestTransport(t)
3311
3312 req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
3313 rt := tt.roundTrip(req)
3314
3315 tc := tt.getConn()
3316 tc.netconn.SetReadDeadline(time.Time{})
3317 tc.wantFrameType(FrameSettings)
3318 tc.wantFrameType(FrameWindowUpdate)
3319
3320 count := 0
3321 start := time.Now()
3322 for streamID := uint32(1); !rt.done(); streamID += 2 {
3323 count++
3324 tc.wantHeaders(wantHeader{
3325 streamID: streamID,
3326 endStream: true,
3327 })
3328 if streamID == 1 {
3329 tc.writeSettings()
3330 tc.wantFrameType(FrameSettings)
3331 }
3332 tc.writeRSTStream(streamID, ErrCodeRefusedStream)
3333
3334 if totalDelay := time.Since(start); totalDelay > 5*time.Minute {
3335 t.Fatalf("RoundTrip still retrying after %v, should have given up", totalDelay)
3336 }
3337 synctest.Wait()
3338 }
3339 if got, want := count, 5; got < count {
3340 t.Errorf("RoundTrip made %v attempts, want at least %v", got, want)
3341 }
3342 if rt.err() == nil {
3343 t.Errorf("RoundTrip succeeded, want error")
3344 }
3345 }
3346
3347 func TestTransportResponseDataBeforeHeaders(t *testing.T) {
3348 synctest.Test(t, testTransportResponseDataBeforeHeaders)
3349 }
3350 func testTransportResponseDataBeforeHeaders(t *testing.T) {
3351
3352 log.SetOutput(io.Discard)
3353 t.Cleanup(func() { log.SetOutput(os.Stderr) })
3354
3355 tc := newTestClientConn(t)
3356 tc.greet()
3357
3358
3359 req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
3360 rt1 := tc.roundTrip(req)
3361 tc.wantFrameType(FrameHeaders)
3362 tc.writeHeaders(HeadersFrameParam{
3363 StreamID: rt1.streamID(),
3364 EndHeaders: true,
3365 EndStream: true,
3366 BlockFragment: tc.makeHeaderBlockFragment(
3367 ":status", "200",
3368 ),
3369 })
3370 rt1.wantStatus(200)
3371
3372
3373 rt2 := tc.roundTrip(req)
3374 tc.wantFrameType(FrameHeaders)
3375 tc.writeData(rt2.streamID(), true, []byte("payload"))
3376 if err, ok := rt2.err().(StreamError); !ok || err.Code != ErrCodeProtocol {
3377 t.Fatalf("expected stream PROTOCOL_ERROR, got: %v", err)
3378 }
3379 }
3380
3381 func TestTransportMaxFrameReadSize(t *testing.T) {
3382 for _, test := range []struct {
3383 maxReadFrameSize uint32
3384 want uint32
3385 }{{
3386 maxReadFrameSize: 64000,
3387 want: 64000,
3388 }, {
3389 maxReadFrameSize: 1024,
3390
3391
3392
3393
3394
3395
3396
3397
3398 want: DefaultMaxReadFrameSize,
3399 }} {
3400 synctestSubtest(t, fmt.Sprint(test.maxReadFrameSize), func(t *testing.T) {
3401 tc := newTestClientConn(t, func(h2 *http.HTTP2Config) {
3402 h2.MaxReadFrameSize = int(test.maxReadFrameSize)
3403 })
3404
3405 fr := readFrame[*SettingsFrame](t, tc)
3406 got, ok := fr.Value(SettingMaxFrameSize)
3407 if !ok {
3408 t.Errorf("Transport.MaxReadFrameSize = %v; server got no setting, want %v", test.maxReadFrameSize, test.want)
3409 } else if got != test.want {
3410 t.Errorf("Transport.MaxReadFrameSize = %v; server got %v, want %v", test.maxReadFrameSize, got, test.want)
3411 }
3412 })
3413 }
3414 }
3415
3416 func TestTransportRequestsLowServerLimit(t *testing.T) {
3417 ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) {
3418 }, func(h2 *http.HTTP2Config) {
3419 h2.MaxConcurrentStreams = 1
3420 })
3421
3422 var (
3423 connCountMu sync.Mutex
3424 connCount int
3425 )
3426 tr := newTransport(t)
3427 tr.DialTLS = func(network, addr string) (net.Conn, error) {
3428 connCountMu.Lock()
3429 defer connCountMu.Unlock()
3430 connCount++
3431 return tls.Dial(network, addr, tlsConfigInsecure)
3432 }
3433
3434 const reqCount = 3
3435 for range reqCount {
3436 req, err := http.NewRequest("GET", ts.URL, nil)
3437 if err != nil {
3438 t.Fatal(err)
3439 }
3440 res, err := tr.RoundTrip(req)
3441 if err != nil {
3442 t.Fatal(err)
3443 }
3444 if got, want := res.StatusCode, 200; got != want {
3445 t.Errorf("StatusCode = %v; want %v", got, want)
3446 }
3447 if res != nil && res.Body != nil {
3448 res.Body.Close()
3449 }
3450 }
3451
3452 if connCount != 1 {
3453 t.Errorf("created %v connections for %v requests, want 1", connCount, reqCount)
3454 }
3455 }
3456
3457
3458 func TestTransportRequestsStallAtServerLimit(t *testing.T) {
3459 synctest.Test(t, testTransportRequestsStallAtServerLimit)
3460 }
3461 func testTransportRequestsStallAtServerLimit(t *testing.T) {
3462 const maxConcurrent = 2
3463
3464 tc := newTestClientConn(t, func(h2 *http.HTTP2Config) {
3465 h2.StrictMaxConcurrentRequests = true
3466 })
3467 tc.greet(Setting{SettingMaxConcurrentStreams, maxConcurrent})
3468
3469 cancelClientRequest := make(chan struct{})
3470
3471
3472
3473 var rts []*testRoundTrip
3474 for k := range maxConcurrent + 2 {
3475 req, _ := http.NewRequest("GET", fmt.Sprintf("https://dummy.tld/%d", k), nil)
3476 if k == maxConcurrent {
3477 req.Cancel = cancelClientRequest
3478 }
3479 rt := tc.roundTrip(req)
3480 rts = append(rts, rt)
3481
3482 if k < maxConcurrent {
3483
3484 tc.wantHeaders(wantHeader{
3485 streamID: rt.streamID(),
3486 endStream: true,
3487 header: http.Header{
3488 ":authority": []string{"dummy.tld"},
3489 ":method": []string{"GET"},
3490 ":path": []string{fmt.Sprintf("/%d", k)},
3491 },
3492 })
3493 } else {
3494
3495
3496 if fr := tc.readFrame(); fr != nil {
3497 t.Fatalf("after making new request while at stream limit, got unexpected frame: %v", fr)
3498 }
3499 }
3500
3501 if rt.done() {
3502 t.Fatalf("rt %v done", k)
3503 }
3504 }
3505
3506
3507
3508 close(cancelClientRequest)
3509 synctest.Wait()
3510 if err := rts[maxConcurrent].err(); err == nil {
3511 t.Fatalf("RoundTrip(%d) should have failed due to cancel, did not", maxConcurrent)
3512 }
3513
3514
3515 for i, rt := range rts {
3516 if i != maxConcurrent && rt.done() {
3517 t.Fatalf("RoundTrip(%d) is done, but should not be", i)
3518 }
3519 }
3520
3521
3522 tc.writeHeaders(HeadersFrameParam{
3523 StreamID: rts[0].streamID(),
3524 EndHeaders: true,
3525 EndStream: true,
3526 BlockFragment: tc.makeHeaderBlockFragment(
3527 ":status", "200",
3528 ),
3529 })
3530 synctest.Wait()
3531 tc.wantHeaders(wantHeader{
3532 streamID: rts[maxConcurrent+1].streamID(),
3533 endStream: true,
3534 header: http.Header{
3535 ":authority": []string{"dummy.tld"},
3536 ":method": []string{"GET"},
3537 ":path": []string{fmt.Sprintf("/%d", maxConcurrent+1)},
3538 },
3539 })
3540 rts[0].wantStatus(200)
3541 }
3542
3543 func TestTransportMaxDecoderHeaderTableSize(t *testing.T) {
3544 synctest.Test(t, testTransportMaxDecoderHeaderTableSize)
3545 }
3546 func testTransportMaxDecoderHeaderTableSize(t *testing.T) {
3547 var reqSize, resSize uint32 = 8192, 16384
3548 tc := newTestClientConn(t, func(h2 *http.HTTP2Config) {
3549 h2.MaxDecoderHeaderTableSize = int(reqSize)
3550 })
3551
3552 fr := readFrame[*SettingsFrame](t, tc)
3553 if v, ok := fr.Value(SettingHeaderTableSize); !ok {
3554 t.Fatalf("missing SETTINGS_HEADER_TABLE_SIZE setting")
3555 } else if v != reqSize {
3556 t.Fatalf("received SETTINGS_HEADER_TABLE_SIZE = %d, want %d", v, reqSize)
3557 }
3558
3559 tc.writeSettings(Setting{SettingHeaderTableSize, resSize})
3560 synctest.Wait()
3561 if got, want := tc.cc.TestPeerMaxHeaderTableSize(), resSize; got != want {
3562 t.Fatalf("peerHeaderTableSize = %d, want %d", got, want)
3563 }
3564 }
3565
3566 func TestTransportMaxEncoderHeaderTableSize(t *testing.T) {
3567 synctest.Test(t, testTransportMaxEncoderHeaderTableSize)
3568 }
3569 func testTransportMaxEncoderHeaderTableSize(t *testing.T) {
3570 var peerAdvertisedMaxHeaderTableSize uint32 = 16384
3571 const wantMaxEncoderHeaderTableSize = 8192
3572 tc := newTestClientConn(t, func(h2 *http.HTTP2Config) {
3573 h2.MaxEncoderHeaderTableSize = wantMaxEncoderHeaderTableSize
3574 })
3575 tc.greet(Setting{SettingHeaderTableSize, peerAdvertisedMaxHeaderTableSize})
3576
3577 if got, want := tc.cc.TestHPACKEncoder().MaxDynamicTableSize(), uint32(wantMaxEncoderHeaderTableSize); got != want {
3578 t.Fatalf("henc.MaxDynamicTableSize() = %d, want %d", got, want)
3579 }
3580 }
3581
3582
3583
3584 func TestTransportAllocationsAfterResponseBodyClose(t *testing.T) {
3585 synctest.Test(t, testTransportAllocationsAfterResponseBodyClose)
3586 }
3587 func testTransportAllocationsAfterResponseBodyClose(t *testing.T) {
3588 tc := newTestClientConn(t)
3589 tc.greet()
3590
3591
3592 req, _ := http.NewRequest("PUT", "https://dummy.tld/", nil)
3593 rt := tc.roundTrip(req)
3594 tc.wantFrameType(FrameHeaders)
3595
3596
3597 tc.writeHeaders(HeadersFrameParam{
3598 StreamID: rt.streamID(),
3599 EndHeaders: true,
3600 EndStream: false,
3601 BlockFragment: tc.makeHeaderBlockFragment(
3602 ":status", "200",
3603 ),
3604 })
3605 tc.writeData(rt.streamID(), false, make([]byte, 64))
3606 tc.wantIdle()
3607
3608
3609 respBody := rt.response().Body
3610 var buf [1]byte
3611 if _, err := respBody.Read(buf[:]); err != nil {
3612 t.Error(err)
3613 }
3614 if err := respBody.Close(); err != nil {
3615 t.Error(err)
3616 }
3617 tc.wantFrameType(FrameRSTStream)
3618
3619
3620 tc.writeData(rt.streamID(), false, make([]byte, 64))
3621
3622 if _, err := respBody.Read(buf[:]); err == nil {
3623 t.Error("read from closed body unexpectedly succeeded")
3624 }
3625 }
3626
3627
3628
3629 func TestTransportNoBodyMeansNoDATA(t *testing.T) { synctest.Test(t, testTransportNoBodyMeansNoDATA) }
3630 func testTransportNoBodyMeansNoDATA(t *testing.T) {
3631 tc := newTestClientConn(t)
3632 tc.greet()
3633
3634 req, _ := http.NewRequest("GET", "https://dummy.tld/", http.NoBody)
3635 rt := tc.roundTrip(req)
3636
3637 tc.wantHeaders(wantHeader{
3638 streamID: rt.streamID(),
3639 endStream: true,
3640 header: http.Header{
3641 ":authority": []string{"dummy.tld"},
3642 ":method": []string{"GET"},
3643 ":path": []string{"/"},
3644 },
3645 })
3646 if fr := tc.readFrame(); fr != nil {
3647 t.Fatalf("unexpected frame after headers: %v", fr)
3648 }
3649 }
3650
3651 func benchSimpleRoundTrip(b *testing.B, nReqHeaders, nResHeader int) {
3652 DisableGoroutineTracking(b)
3653 b.ReportAllocs()
3654 ts := newTestServer(b,
3655 func(w http.ResponseWriter, r *http.Request) {
3656 for i := range nResHeader {
3657 name := fmt.Sprint("A-", i)
3658 w.Header().Set(name, "*")
3659 }
3660 },
3661 optQuiet,
3662 )
3663
3664
3665
3666
3667 ts.Config.MaxHeaderValueCount = 30 + nReqHeaders
3668
3669 tr := newTransport(b)
3670
3671 req, err := http.NewRequest("GET", ts.URL, nil)
3672 if err != nil {
3673 b.Fatal(err)
3674 }
3675
3676 for i := range nReqHeaders {
3677 name := fmt.Sprint("A-", i)
3678 req.Header.Set(name, "*")
3679 }
3680
3681 b.ResetTimer()
3682
3683 for i := 0; i < b.N; i++ {
3684 res, err := tr.RoundTrip(req)
3685 if err != nil {
3686 if res != nil {
3687 res.Body.Close()
3688 }
3689 b.Fatalf("RoundTrip err = %v; want nil", err)
3690 }
3691 res.Body.Close()
3692 if res.StatusCode != http.StatusOK {
3693 b.Fatalf("Response code = %v; want %v", res.StatusCode, http.StatusOK)
3694 }
3695 }
3696 }
3697
3698 type infiniteReader struct{}
3699
3700 func (r infiniteReader) Read(b []byte) (int, error) {
3701 return len(b), nil
3702 }
3703
3704
3705
3706 func TestTransportResponseAndResetWithoutConsumingBodyRace(t *testing.T) {
3707 ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) {
3708 w.WriteHeader(http.StatusOK)
3709 })
3710
3711 tr := newTransport(t)
3712
3713
3714 req, _ := http.NewRequest("PUT", ts.URL, infiniteReader{})
3715 res, err := tr.RoundTrip(req)
3716 if err != nil {
3717 t.Fatal(err)
3718 }
3719 if res.StatusCode != http.StatusOK {
3720 t.Fatalf("Response code = %v; want %v", res.StatusCode, http.StatusOK)
3721 }
3722 }
3723
3724
3725
3726 func TestTransportHandlesInvalidStatuslessResponse(t *testing.T) {
3727 synctest.Test(t, testTransportHandlesInvalidStatuslessResponse)
3728 }
3729 func testTransportHandlesInvalidStatuslessResponse(t *testing.T) {
3730 tc := newTestClientConn(t)
3731 tc.greet()
3732
3733 req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
3734 rt := tc.roundTrip(req)
3735
3736 tc.wantFrameType(FrameHeaders)
3737 tc.writeHeaders(HeadersFrameParam{
3738 StreamID: rt.streamID(),
3739 EndHeaders: true,
3740 EndStream: false,
3741 BlockFragment: tc.makeHeaderBlockFragment(
3742 "content-type", "text/html",
3743 ),
3744 })
3745 tc.writeData(rt.streamID(), true, []byte("payload"))
3746 }
3747
3748 func BenchmarkClientRequestHeaders(b *testing.B) {
3749 b.Run(" 0 Headers", func(b *testing.B) { benchSimpleRoundTrip(b, 0, 0) })
3750 b.Run(" 10 Headers", func(b *testing.B) { benchSimpleRoundTrip(b, 10, 0) })
3751 b.Run(" 100 Headers", func(b *testing.B) { benchSimpleRoundTrip(b, 100, 0) })
3752 b.Run("1000 Headers", func(b *testing.B) { benchSimpleRoundTrip(b, 1000, 0) })
3753 }
3754
3755 func BenchmarkClientResponseHeaders(b *testing.B) {
3756 b.Run(" 0 Headers", func(b *testing.B) { benchSimpleRoundTrip(b, 0, 0) })
3757 b.Run(" 10 Headers", func(b *testing.B) { benchSimpleRoundTrip(b, 0, 10) })
3758 b.Run(" 100 Headers", func(b *testing.B) { benchSimpleRoundTrip(b, 0, 100) })
3759 b.Run("1000 Headers", func(b *testing.B) { benchSimpleRoundTrip(b, 0, 1000) })
3760 }
3761
3762 func BenchmarkDownloadFrameSize(b *testing.B) {
3763 b.Run(" 16k Frame", func(b *testing.B) { benchLargeDownloadRoundTrip(b, 16*1024) })
3764 b.Run(" 64k Frame", func(b *testing.B) { benchLargeDownloadRoundTrip(b, 64*1024) })
3765 b.Run("128k Frame", func(b *testing.B) { benchLargeDownloadRoundTrip(b, 128*1024) })
3766 b.Run("256k Frame", func(b *testing.B) { benchLargeDownloadRoundTrip(b, 256*1024) })
3767 b.Run("512k Frame", func(b *testing.B) { benchLargeDownloadRoundTrip(b, 512*1024) })
3768 }
3769 func benchLargeDownloadRoundTrip(b *testing.B, frameSize uint32) {
3770 DisableGoroutineTracking(b)
3771 const transferSize = 1024 * 1024 * 1024
3772 b.ReportAllocs()
3773 ts := newTestServer(b,
3774 func(w http.ResponseWriter, r *http.Request) {
3775
3776 w.Header().Set("Content-Length", strconv.Itoa(transferSize))
3777 w.Header().Set("Content-Transfer-Encoding", "binary")
3778 var data [1024 * 1024]byte
3779 for range transferSize / (1024 * 1024) {
3780 w.Write(data[:])
3781 }
3782 }, optQuiet,
3783 )
3784
3785 tr := newTransport(b)
3786 tr.HTTP2.MaxReadFrameSize = int(frameSize)
3787
3788 req, err := http.NewRequest("GET", ts.URL, nil)
3789 if err != nil {
3790 b.Fatal(err)
3791 }
3792
3793 b.N = 3
3794 b.SetBytes(transferSize)
3795 b.ResetTimer()
3796
3797 for i := 0; i < b.N; i++ {
3798 res, err := tr.RoundTrip(req)
3799 if err != nil {
3800 if res != nil {
3801 res.Body.Close()
3802 }
3803 b.Fatalf("RoundTrip err = %v; want nil", err)
3804 }
3805 data, _ := io.ReadAll(res.Body)
3806 if len(data) != transferSize {
3807 b.Fatalf("Response length invalid")
3808 }
3809 res.Body.Close()
3810 if res.StatusCode != http.StatusOK {
3811 b.Fatalf("Response code = %v; want %v", res.StatusCode, http.StatusOK)
3812 }
3813 }
3814 }
3815
3816 func BenchmarkClientGzip(b *testing.B) {
3817 DisableGoroutineTracking(b)
3818 b.ReportAllocs()
3819
3820 const responseSize = 1024 * 1024
3821
3822 var buf bytes.Buffer
3823 gz := gzip.NewWriter(&buf)
3824 if _, err := io.CopyN(gz, crand.Reader, responseSize); err != nil {
3825 b.Fatal(err)
3826 }
3827 gz.Close()
3828
3829 data := buf.Bytes()
3830 ts := newTestServer(b,
3831 func(w http.ResponseWriter, r *http.Request) {
3832 w.Header().Set("Content-Encoding", "gzip")
3833 w.Write(data)
3834 },
3835 optQuiet,
3836 )
3837
3838 tr := newTransport(b)
3839
3840 req, err := http.NewRequest("GET", ts.URL, nil)
3841 if err != nil {
3842 b.Fatal(err)
3843 }
3844
3845 b.ResetTimer()
3846
3847 for i := 0; i < b.N; i++ {
3848 res, err := tr.RoundTrip(req)
3849 if err != nil {
3850 b.Fatalf("RoundTrip err = %v; want nil", err)
3851 }
3852 if res.StatusCode != http.StatusOK {
3853 b.Fatalf("Response code = %v; want %v", res.StatusCode, http.StatusOK)
3854 }
3855 n, err := io.Copy(io.Discard, res.Body)
3856 res.Body.Close()
3857 if err != nil {
3858 b.Fatalf("RoundTrip err = %v; want nil", err)
3859 }
3860 if n != responseSize {
3861 b.Fatalf("RoundTrip expected %d bytes, got %d", responseSize, n)
3862 }
3863 }
3864 }
3865
3866
3867
3868
3869 func TestClientConnCloseAtHeaders(t *testing.T) { synctest.Test(t, testClientConnCloseAtHeaders) }
3870 func testClientConnCloseAtHeaders(t *testing.T) {
3871 tc := newTestClientConn(t)
3872 tc.greet()
3873
3874 req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
3875 rt := tc.roundTrip(req)
3876 tc.wantFrameType(FrameHeaders)
3877
3878 tc.cc.Close()
3879 synctest.Wait()
3880 if err := rt.err(); err != ErrClientConnForceClosed {
3881 t.Fatalf("RoundTrip error = %v, want errClientConnForceClosed", err)
3882 }
3883 }
3884
3885
3886
3887 func TestClientConnCloseAtBody(t *testing.T) { synctest.Test(t, testClientConnCloseAtBody) }
3888 func testClientConnCloseAtBody(t *testing.T) {
3889 tc := newTestClientConn(t)
3890 tc.greet()
3891
3892 req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
3893 rt := tc.roundTrip(req)
3894 tc.wantFrameType(FrameHeaders)
3895
3896 tc.writeHeaders(HeadersFrameParam{
3897 StreamID: rt.streamID(),
3898 EndHeaders: true,
3899 EndStream: false,
3900 BlockFragment: tc.makeHeaderBlockFragment(
3901 ":status", "200",
3902 ),
3903 })
3904 tc.writeData(rt.streamID(), false, make([]byte, 64))
3905 resp := rt.response()
3906 tc.cc.Close()
3907 synctest.Wait()
3908
3909 if _, err := io.Copy(io.Discard, resp.Body); err == nil {
3910 t.Error("expected a Copy error, got nil")
3911 }
3912 }
3913
3914
3915
3916 func TestClientConnShutdown(t *testing.T) { synctest.Test(t, testClientConnShutdown) }
3917 func testClientConnShutdown(t *testing.T) {
3918 tc := newTestClientConn(t)
3919 tc.greet()
3920
3921 req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
3922 rt := tc.roundTrip(req)
3923 tc.wantFrameType(FrameHeaders)
3924
3925 go tc.cc.Shutdown(context.Background())
3926 synctest.Wait()
3927
3928 tc.wantFrameType(FrameGoAway)
3929 tc.wantIdle()
3930 body := []byte("body")
3931 tc.writeHeaders(HeadersFrameParam{
3932 StreamID: rt.streamID(),
3933 EndHeaders: true,
3934 EndStream: false,
3935 BlockFragment: tc.makeHeaderBlockFragment(
3936 ":status", "200",
3937 ),
3938 })
3939 tc.writeData(rt.streamID(), true, body)
3940
3941 rt.wantStatus(200)
3942 rt.wantBody(body)
3943
3944
3945 tc.wantClosed()
3946 }
3947
3948
3949
3950
3951 func TestClientConnShutdownCancel(t *testing.T) { synctest.Test(t, testClientConnShutdownCancel) }
3952 func testClientConnShutdownCancel(t *testing.T) {
3953 tc := newTestClientConn(t)
3954 tc.greet()
3955
3956 req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
3957 rt := tc.roundTrip(req)
3958 tc.wantFrameType(FrameHeaders)
3959
3960 ctx, cancel := context.WithCancel(t.Context())
3961 var shutdownErr error
3962 go func() {
3963 shutdownErr = tc.cc.Shutdown(ctx)
3964 }()
3965 synctest.Wait()
3966
3967 tc.wantFrameType(FrameGoAway)
3968 tc.wantIdle()
3969
3970 cancel()
3971 synctest.Wait()
3972
3973 if shutdownErr != context.Canceled {
3974 t.Fatalf("ClientConn.Shutdown(ctx) did not return context.Canceled after cancelling context")
3975 }
3976
3977
3978
3979
3980
3981
3982
3983
3984
3985
3986 if rt.done() {
3987 t.Fatal("RoundTrip unexpectedly returned during shutdown")
3988 }
3989 }
3990
3991 type errReader struct {
3992 body []byte
3993 err error
3994 }
3995
3996 func (r *errReader) Read(p []byte) (int, error) {
3997 if len(r.body) > 0 {
3998 n := copy(p, r.body)
3999 r.body = r.body[n:]
4000 return n, nil
4001 }
4002 return 0, r.err
4003 }
4004
4005 func testTransportBodyReadError(t *testing.T, body []byte) {
4006 synctest.Test(t, func(t *testing.T) {
4007 testTransportBodyReadErrorBubble(t, body)
4008 })
4009 }
4010 func testTransportBodyReadErrorBubble(t *testing.T, body []byte) {
4011 tc := newTestClientConn(t)
4012 tc.greet()
4013
4014 bodyReadError := errors.New("body read error")
4015 b := tc.newRequestBody()
4016 b.Write(body)
4017 b.closeWithError(bodyReadError)
4018 req, _ := http.NewRequest("PUT", "https://dummy.tld/", b)
4019 rt := tc.roundTrip(req)
4020
4021 tc.wantFrameType(FrameHeaders)
4022 var receivedBody []byte
4023 readFrames:
4024 for {
4025 switch f := tc.readFrame().(type) {
4026 case *DataFrame:
4027 receivedBody = append(receivedBody, f.Data()...)
4028 case *RSTStreamFrame:
4029 break readFrames
4030 default:
4031 t.Fatalf("unexpected frame: %v", f)
4032 case nil:
4033 t.Fatalf("transport is idle, want RST_STREAM")
4034 }
4035 }
4036 if !bytes.Equal(receivedBody, body) {
4037 t.Fatalf("body: %q; expected %q", receivedBody, body)
4038 }
4039
4040 if err := rt.err(); err != bodyReadError {
4041 t.Fatalf("err = %v; want %v", err, bodyReadError)
4042 }
4043 }
4044
4045 func TestTransportBodyReadError_Immediately(t *testing.T) { testTransportBodyReadError(t, nil) }
4046 func TestTransportBodyReadError_Some(t *testing.T) { testTransportBodyReadError(t, []byte("123")) }
4047
4048
4049
4050
4051 func TestTransportBodyEagerEndStream(t *testing.T) { synctest.Test(t, testTransportBodyEagerEndStream) }
4052 func testTransportBodyEagerEndStream(t *testing.T) {
4053 const reqBody = "some request body"
4054 const resBody = "some response body"
4055
4056 tc := newTestClientConn(t)
4057 tc.greet()
4058
4059 body := strings.NewReader(reqBody)
4060 req, _ := http.NewRequest("PUT", "https://dummy.tld/", body)
4061 tc.roundTrip(req)
4062
4063 tc.wantFrameType(FrameHeaders)
4064 f := readFrame[*DataFrame](t, tc)
4065 if !f.StreamEnded() {
4066 t.Fatalf("data frame without END_STREAM %v", f)
4067 }
4068 }
4069
4070 type chunkReader struct {
4071 chunks [][]byte
4072 }
4073
4074 func (r *chunkReader) Read(p []byte) (int, error) {
4075 if len(r.chunks) > 0 {
4076 n := copy(p, r.chunks[0])
4077 r.chunks = r.chunks[1:]
4078 return n, nil
4079 }
4080 panic("shouldn't read this many times")
4081 }
4082
4083
4084
4085
4086
4087
4088
4089
4090
4091 func TestTransportBodyLargerThanSpecifiedContentLength_len3(t *testing.T) {
4092 body := &chunkReader{[][]byte{
4093 []byte("123"),
4094 []byte("456"),
4095 }}
4096 synctest.Test(t, func(t *testing.T) {
4097 testTransportBodyLargerThanSpecifiedContentLength(t, body, 3)
4098 })
4099 }
4100
4101 func TestTransportBodyLargerThanSpecifiedContentLength_len2(t *testing.T) {
4102 body := &chunkReader{[][]byte{
4103 []byte("123"),
4104 }}
4105 synctest.Test(t, func(t *testing.T) {
4106 testTransportBodyLargerThanSpecifiedContentLength(t, body, 2)
4107 })
4108 }
4109
4110 func testTransportBodyLargerThanSpecifiedContentLength(t *testing.T, body *chunkReader, contentLen int64) {
4111 ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) {
4112 r.Body.Read(make([]byte, 6))
4113 })
4114
4115 tr := newTransport(t)
4116
4117 req, _ := http.NewRequest("POST", ts.URL, body)
4118 req.ContentLength = contentLen
4119 _, err := tr.RoundTrip(req)
4120 if err != ErrReqBodyTooLong {
4121 t.Fatalf("expected %v, got %v", ErrReqBodyTooLong, err)
4122 }
4123 }
4124
4125
4126 func TestTransportNewClientConnCloseOnWriteError(t *testing.T) {
4127 synctest.Test(t, testTransportNewClientConnCloseOnWriteError)
4128 }
4129 func testTransportNewClientConnCloseOnWriteError(t *testing.T) {
4130
4131
4132
4133
4134
4135
4136
4137 t.Skip("TODO: test fails because write errors don't cause the conn to close")
4138
4139 tc := newTestClientConn(t)
4140
4141 synctest.Wait()
4142 writeErr := errors.New("write error")
4143 tc.netconn.loc.setWriteError(writeErr)
4144
4145 tc.writeSettings()
4146 tc.wantIdle()
4147
4148
4149 tc.wantFrameType(FrameSettings)
4150 tc.wantFrameType(FrameWindowUpdate)
4151 tc.wantIdle()
4152
4153 synctest.Wait()
4154 if !tc.netconn.IsClosedByPeer() {
4155 t.Error("expected closed conn")
4156 }
4157 }
4158
4159 func TestTransportRoundtripCloseOnWriteError(t *testing.T) {
4160 synctest.Test(t, testTransportRoundtripCloseOnWriteError)
4161 }
4162 func testTransportRoundtripCloseOnWriteError(t *testing.T) {
4163 tc := newTestClientConn(t)
4164 tc.greet()
4165
4166 body := tc.newRequestBody()
4167 body.writeBytes(1)
4168 req, _ := http.NewRequest("GET", "https://dummy.tld/", body)
4169 rt := tc.roundTrip(req)
4170
4171 writeErr := errors.New("write error")
4172 tc.closeWriteWithError(writeErr)
4173
4174 body.writeBytes(1)
4175 if err := rt.err(); err != writeErr {
4176 t.Fatalf("RoundTrip error %v, want %v", err, writeErr)
4177 }
4178
4179 rt2 := tc.roundTrip(req)
4180 if err := rt2.err(); err != ErrClientConnUnusable {
4181 t.Fatalf("RoundTrip error %v, want errClientConnUnusable", err)
4182 }
4183 }
4184
4185
4186
4187
4188 func TestTransportBodyRewindRace(t *testing.T) {
4189 ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) {
4190 w.Header().Set("Connection", "close")
4191 w.WriteHeader(http.StatusOK)
4192 return
4193 })
4194
4195 tr := newTransport(t)
4196 tr.MaxConnsPerHost = 1
4197 client := &http.Client{
4198 Transport: tr,
4199 }
4200
4201 const clients = 50
4202
4203 var wg sync.WaitGroup
4204 wg.Add(clients)
4205 for range clients {
4206 req, err := http.NewRequest("POST", ts.URL, bytes.NewBufferString("abcdef"))
4207 if err != nil {
4208 t.Fatalf("unexpected new request error: %v", err)
4209 }
4210
4211 go func() {
4212 defer wg.Done()
4213 res, err := client.Do(req)
4214 if err == nil {
4215 res.Body.Close()
4216 }
4217 }()
4218 }
4219
4220 wg.Wait()
4221 }
4222
4223 type errorReader struct{ err error }
4224
4225 func (r errorReader) Read(p []byte) (int, error) { return 0, r.err }
4226
4227
4228
4229 func TestTransportServerResetStreamAtHeaders(t *testing.T) {
4230 ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) {
4231 w.WriteHeader(http.StatusUnauthorized)
4232 return
4233 })
4234
4235 tr := newTransport(t)
4236 tr.MaxConnsPerHost = 1
4237 tr.ExpectContinueTimeout = 10 * time.Second
4238
4239 client := &http.Client{
4240 Transport: tr,
4241 }
4242
4243 req, err := http.NewRequest("POST", ts.URL, errorReader{io.EOF})
4244 if err != nil {
4245 t.Fatalf("unexpected new request error: %v", err)
4246 }
4247 req.ContentLength = 0
4248 req.Header.Set("Expect", "100-continue")
4249 res, err := client.Do(req)
4250 if err != nil {
4251 t.Fatal(err)
4252 }
4253 res.Body.Close()
4254 }
4255
4256 type trackingReader struct {
4257 rdr io.Reader
4258 wasRead uint32
4259 }
4260
4261 func (tr *trackingReader) Read(p []byte) (int, error) {
4262 atomic.StoreUint32(&tr.wasRead, 1)
4263 return tr.rdr.Read(p)
4264 }
4265
4266 func (tr *trackingReader) WasRead() bool {
4267 return atomic.LoadUint32(&tr.wasRead) != 0
4268 }
4269
4270 func TestTransportExpectContinue(t *testing.T) {
4271 ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) {
4272 switch r.URL.Path {
4273 case "/reject":
4274 w.WriteHeader(403)
4275 default:
4276 io.Copy(io.Discard, r.Body)
4277 }
4278 })
4279
4280 tr := newTransport(t)
4281 tr.MaxConnsPerHost = 1
4282 tr.ExpectContinueTimeout = 10 * time.Second
4283
4284 client := &http.Client{
4285 Transport: tr,
4286 }
4287
4288 testCases := []struct {
4289 Name string
4290 Path string
4291 Body *trackingReader
4292 ExpectedCode int
4293 ShouldRead bool
4294 }{
4295 {
4296 Name: "read-all",
4297 Path: "/",
4298 Body: &trackingReader{rdr: strings.NewReader("hello")},
4299 ExpectedCode: 200,
4300 ShouldRead: true,
4301 },
4302 {
4303 Name: "reject",
4304 Path: "/reject",
4305 Body: &trackingReader{rdr: strings.NewReader("hello")},
4306 ExpectedCode: 403,
4307 ShouldRead: false,
4308 },
4309 }
4310
4311 for _, tc := range testCases {
4312 t.Run(tc.Name, func(t *testing.T) {
4313 startTime := time.Now()
4314
4315 req, err := http.NewRequest("POST", ts.URL+tc.Path, tc.Body)
4316 if err != nil {
4317 t.Fatal(err)
4318 }
4319 req.Header.Set("Expect", "100-continue")
4320 res, err := client.Do(req)
4321 if err != nil {
4322 t.Fatal(err)
4323 }
4324 res.Body.Close()
4325
4326 if delta := time.Since(startTime); delta >= tr.ExpectContinueTimeout {
4327 t.Error("Request didn't finish before expect continue timeout")
4328 }
4329 if res.StatusCode != tc.ExpectedCode {
4330 t.Errorf("Unexpected status code, got %d, expected %d", res.StatusCode, tc.ExpectedCode)
4331 }
4332 if tc.Body.WasRead() != tc.ShouldRead {
4333 t.Errorf("Unexpected read status, got %v, expected %v", tc.Body.WasRead(), tc.ShouldRead)
4334 }
4335 })
4336 }
4337 }
4338
4339 type closeChecker struct {
4340 io.ReadCloser
4341 closed chan struct{}
4342 }
4343
4344 func newCloseChecker(r io.ReadCloser) *closeChecker {
4345 return &closeChecker{r, make(chan struct{})}
4346 }
4347
4348 func newStaticCloseChecker(body string) *closeChecker {
4349 return newCloseChecker(io.NopCloser(strings.NewReader("body")))
4350 }
4351
4352 func (rc *closeChecker) Read(b []byte) (n int, err error) {
4353 select {
4354 default:
4355 case <-rc.closed:
4356
4357
4358
4359 return 0, errors.New("read after Body.Close")
4360 }
4361 return rc.ReadCloser.Read(b)
4362 }
4363
4364 func (rc *closeChecker) Close() error {
4365 close(rc.closed)
4366 return rc.ReadCloser.Close()
4367 }
4368
4369 func (rc *closeChecker) isClosed() error {
4370
4371
4372
4373 timeout := time.Duration(10 * time.Second)
4374 select {
4375 case <-rc.closed:
4376 case <-time.After(timeout):
4377 return fmt.Errorf("body not closed after %v", timeout)
4378 }
4379 return nil
4380 }
4381
4382
4383 type blockingWriteConn struct {
4384 net.Conn
4385 writeOnce sync.Once
4386 writec chan struct{}
4387 unblockc chan struct{}
4388 count, limit int
4389 }
4390
4391 func newBlockingWriteConn(conn net.Conn, limit int) *blockingWriteConn {
4392 return &blockingWriteConn{
4393 Conn: conn,
4394 limit: limit,
4395 writec: make(chan struct{}),
4396 unblockc: make(chan struct{}),
4397 }
4398 }
4399
4400
4401 func (c *blockingWriteConn) wait() {
4402 <-c.writec
4403 }
4404
4405
4406 func (c *blockingWriteConn) unblock() {
4407 close(c.unblockc)
4408 }
4409
4410 func (c *blockingWriteConn) Write(b []byte) (n int, err error) {
4411 if c.count+len(b) > c.limit {
4412 c.writeOnce.Do(func() {
4413 close(c.writec)
4414 })
4415 <-c.unblockc
4416 }
4417 n, err = c.Conn.Write(b)
4418 c.count += n
4419 return n, err
4420 }
4421
4422
4423
4424 func TestTransportFrameBufferReuse(t *testing.T) {
4425 filler := hex.EncodeToString([]byte(randString(2048)))
4426
4427 ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) {
4428 if got, want := r.Header.Get("Big"), filler; got != want {
4429 t.Errorf(`r.Header.Get("Big") = %q, want %q`, got, want)
4430 }
4431 b, err := io.ReadAll(r.Body)
4432 if err != nil {
4433 t.Errorf("error reading request body: %v", err)
4434 }
4435 if got, want := string(b), filler; got != want {
4436 t.Errorf("request body = %q, want %q", got, want)
4437 }
4438 if got, want := r.Trailer.Get("Big"), filler; got != want {
4439 t.Errorf(`r.Trailer.Get("Big") = %q, want %q`, got, want)
4440 }
4441 })
4442
4443 tr := newTransport(t)
4444
4445 var wg sync.WaitGroup
4446 defer wg.Wait()
4447 for range 10 {
4448 wg.Go(func() {
4449 req, err := http.NewRequest("POST", ts.URL, strings.NewReader(filler))
4450 if err != nil {
4451 t.Error(err)
4452 return
4453 }
4454 req.Header.Set("Big", filler)
4455 req.Trailer = make(http.Header)
4456 req.Trailer.Set("Big", filler)
4457 res, err := tr.RoundTrip(req)
4458 if err != nil {
4459 t.Error(err)
4460 return
4461 }
4462 if got, want := res.StatusCode, 200; got != want {
4463 t.Errorf("StatusCode = %v; want %v", got, want)
4464 }
4465 if res != nil && res.Body != nil {
4466 res.Body.Close()
4467 }
4468 })
4469 }
4470
4471 }
4472
4473
4474
4475
4476
4477 func TestTransportBlockingRequestWrite(t *testing.T) {
4478 filler := hex.EncodeToString([]byte(randString(2048)))
4479 for _, test := range []struct {
4480 name string
4481 req *http.Request
4482 }{{
4483 name: "headers",
4484 req: func() *http.Request {
4485 req, _ := http.NewRequest("POST", "https://dummy.tld/", nil)
4486 req.Header.Set("Big", filler)
4487 return req
4488 }(),
4489 }, {
4490 name: "body",
4491 req: func() *http.Request {
4492 req, _ := http.NewRequest("POST", "https://dummy.tld/", strings.NewReader(filler))
4493 return req
4494 }(),
4495 }, {
4496 name: "trailer",
4497 req: func() *http.Request {
4498 req, _ := http.NewRequest("POST", "https://dummy.tld/", strings.NewReader("body"))
4499 req.Trailer = make(http.Header)
4500 req.Trailer.Set("Big", filler)
4501 return req
4502 }(),
4503 }} {
4504 t.Run(test.name, func(t *testing.T) {
4505 synctest.Test(t, func(t *testing.T) {
4506 testTransportBlockingRequestWrite(t, test.req)
4507 })
4508 })
4509 }
4510 }
4511 func testTransportBlockingRequestWrite(t *testing.T, req2 *http.Request) {
4512 tt := newTestTransport(t)
4513
4514 smallReq := func() *http.Request {
4515 req, _ := http.NewRequest("GET", req2.URL.String(), nil)
4516 return req
4517 }
4518
4519
4520 rt1 := tt.roundTrip(smallReq())
4521 tc1 := tt.getConn()
4522 tc1.wantFrameType(FrameSettings)
4523 tc1.wantFrameType(FrameWindowUpdate)
4524 tc1.wantHeaders(wantHeader{
4525 streamID: 1,
4526 endStream: true,
4527 })
4528 tc1.writeSettings(Setting{SettingMaxConcurrentStreams, 1})
4529 tc1.writeHeaders(HeadersFrameParam{
4530 StreamID: 1,
4531 EndHeaders: true,
4532 EndStream: true,
4533 BlockFragment: tc1.makeHeaderBlockFragment(
4534 ":status", "200",
4535 ),
4536 })
4537 rt1.wantStatus(200)
4538 tc1.wantFrameType(FrameSettings)
4539
4540
4541 tc1.netconn.SetReadBufferSize(1024)
4542 rt2 := tt.roundTrip(req2)
4543
4544
4545
4546 rt3 := tt.roundTrip(smallReq())
4547 tc2 := tt.getConn()
4548 tc2.wantFrameType(FrameSettings)
4549 tc2.wantFrameType(FrameWindowUpdate)
4550 tc2.wantHeaders(wantHeader{
4551 streamID: 1,
4552 endStream: true,
4553 })
4554 tc2.writeSettings()
4555 tc2.writeHeaders(HeadersFrameParam{
4556 StreamID: 1,
4557 EndHeaders: true,
4558 EndStream: true,
4559 BlockFragment: tc1.makeHeaderBlockFragment(
4560 ":status", "200",
4561 ),
4562 })
4563 rt3.wantStatus(200)
4564 tc2.wantFrameType(FrameSettings)
4565
4566 if rt2.done() {
4567 t.Errorf("RoundTrip 2 is done, expect it to be still pending")
4568 }
4569 }
4570
4571 func TestTransportCloseRequestBody(t *testing.T) {
4572 var statusCode int
4573 ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) {
4574 w.WriteHeader(statusCode)
4575 })
4576
4577 tr := newTransport(t)
4578 ctx := context.Background()
4579 cc, err := tr.NewClientConn(ctx, "https", ts.Listener.Addr().String())
4580 if err != nil {
4581 t.Fatal(err)
4582 }
4583 defer cc.Close()
4584
4585 for _, status := range []int{200, 401} {
4586 t.Run(fmt.Sprintf("status=%d", status), func(t *testing.T) {
4587 statusCode = status
4588 pr, pw := io.Pipe()
4589 body := newCloseChecker(pr)
4590 req, err := http.NewRequest("PUT", "https://dummy.tld/", body)
4591 if err != nil {
4592 t.Fatal(err)
4593 }
4594 res, err := cc.RoundTrip(req)
4595 if err != nil {
4596 t.Fatal(err)
4597 }
4598 res.Body.Close()
4599 pw.Close()
4600 if err := body.isClosed(); err != nil {
4601 t.Fatal(err)
4602 }
4603 })
4604 }
4605 }
4606
4607 func TestTransportNoRetryOnStreamProtocolError(t *testing.T) {
4608 synctest.Test(t, testTransportNoRetryOnStreamProtocolError)
4609 }
4610 func testTransportNoRetryOnStreamProtocolError(t *testing.T) {
4611
4612
4613
4614
4615
4616 tt := newTestTransport(t)
4617
4618
4619
4620
4621
4622
4623 req1, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
4624 rt1 := tt.roundTrip(req1)
4625 tc1 := tt.getConn()
4626 tc1.wantFrameType(FrameSettings)
4627 tc1.wantFrameType(FrameWindowUpdate)
4628 tc1.wantHeaders(wantHeader{
4629 streamID: 1,
4630 endStream: true,
4631 })
4632 tc1.writeSettings()
4633 tc1.wantFrameType(FrameSettings)
4634
4635
4636 req2, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
4637 rt2 := tt.roundTrip(req2)
4638 tc1.wantHeaders(wantHeader{
4639 streamID: 3,
4640 endStream: true,
4641 })
4642
4643
4644 tc1.writeRSTStream(3, ErrCodeProtocol)
4645 if rt1.done() {
4646 t.Fatalf("After protocol error on RoundTrip #2, RoundTrip #1 is done; want still in progress")
4647 }
4648 if !rt2.done() {
4649 t.Fatalf("After protocol error on RoundTrip #2, RoundTrip #2 is in progress; want done")
4650 }
4651
4652 if tt.hasConn() {
4653 t.Fatalf("After protocol error on RoundTrip #2, RoundTrip #2 is unexpectedly retried")
4654 }
4655
4656
4657 tc1.writeHeaders(HeadersFrameParam{
4658 StreamID: 1,
4659 EndHeaders: true,
4660 EndStream: true,
4661 BlockFragment: tc1.makeHeaderBlockFragment(
4662 ":status", "200",
4663 ),
4664 })
4665 rt1.wantStatus(200)
4666 }
4667
4668 func TestClientConnReservations(t *testing.T) { synctest.Test(t, testClientConnReservations) }
4669 func testClientConnReservations(t *testing.T) {
4670 tc := newTestClientConn(t)
4671 tc.greet(
4672 Setting{ID: SettingMaxConcurrentStreams, Val: InitialMaxConcurrentStreams},
4673 )
4674
4675 doRoundTrip := func() {
4676 req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
4677 rt := tc.roundTrip(req)
4678 tc.wantFrameType(FrameHeaders)
4679 tc.writeHeaders(HeadersFrameParam{
4680 StreamID: rt.streamID(),
4681 EndHeaders: true,
4682 EndStream: true,
4683 BlockFragment: tc.makeHeaderBlockFragment(
4684 ":status", "200",
4685 ),
4686 })
4687 rt.wantStatus(200)
4688 }
4689
4690 n := 0
4691 for n <= InitialMaxConcurrentStreams && tc.cc.ReserveNewRequest() {
4692 n++
4693 }
4694 if n != InitialMaxConcurrentStreams {
4695 t.Errorf("did %v reservations; want %v", n, InitialMaxConcurrentStreams)
4696 }
4697 doRoundTrip()
4698 n2 := 0
4699 for n2 <= 5 && tc.cc.ReserveNewRequest() {
4700 n2++
4701 }
4702 if n2 != 1 {
4703 t.Fatalf("after one RoundTrip, did %v reservations; want 1", n2)
4704 }
4705
4706
4707 for i := 0; i < n; i++ {
4708 doRoundTrip()
4709 }
4710
4711 n2 = 0
4712 for n2 <= InitialMaxConcurrentStreams && tc.cc.ReserveNewRequest() {
4713 n2++
4714 }
4715 if n2 != n {
4716 t.Errorf("after reset, reservations = %v; want %v", n2, n)
4717 }
4718 }
4719
4720 func TestTransportTimeoutServerHangs(t *testing.T) { synctest.Test(t, testTransportTimeoutServerHangs) }
4721 func testTransportTimeoutServerHangs(t *testing.T) {
4722 tc := newTestClientConn(t)
4723 tc.greet()
4724
4725 ctx, cancel := context.WithCancel(context.Background())
4726 req, _ := http.NewRequestWithContext(ctx, "PUT", "https://dummy.tld/", nil)
4727 rt := tc.roundTrip(req)
4728
4729 tc.wantFrameType(FrameHeaders)
4730 time.Sleep(5 * time.Second)
4731 if f := tc.readFrame(); f != nil {
4732 t.Fatalf("unexpected frame: %v", f)
4733 }
4734 if rt.done() {
4735 t.Fatalf("after 5 seconds with no response, RoundTrip unexpectedly returned")
4736 }
4737
4738 cancel()
4739 synctest.Wait()
4740 if rt.err() != context.Canceled {
4741 t.Fatalf("RoundTrip error: %v; want context.Canceled", rt.err())
4742 }
4743 }
4744
4745 func TestTransportContentLengthWithoutBody(t *testing.T) {
4746 for _, test := range []struct {
4747 name string
4748 contentLength string
4749 wantBody string
4750 wantErr error
4751 wantContentLength int64
4752 }{
4753 {
4754 name: "non-zero content length",
4755 contentLength: "42",
4756 wantErr: io.ErrUnexpectedEOF,
4757 wantContentLength: 42,
4758 },
4759 {
4760 name: "zero content length",
4761 contentLength: "0",
4762 wantErr: nil,
4763 wantContentLength: 0,
4764 },
4765 } {
4766 synctestSubtest(t, test.name, func(t *testing.T) {
4767 contentLength := ""
4768 ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) {
4769 w.Header().Set("Content-Length", contentLength)
4770 })
4771 tr := newTransport(t)
4772
4773 contentLength = test.contentLength
4774
4775 req, _ := http.NewRequest("GET", ts.URL, nil)
4776 res, err := tr.RoundTrip(req)
4777 if err != nil {
4778 t.Fatal(err)
4779 }
4780 defer res.Body.Close()
4781 body, err := io.ReadAll(res.Body)
4782
4783 if err != test.wantErr {
4784 t.Errorf("Expected error %v, got: %v", test.wantErr, err)
4785 }
4786 if len(body) > 0 {
4787 t.Errorf("Expected empty body, got: %v", body)
4788 }
4789 if res.ContentLength != test.wantContentLength {
4790 t.Errorf("Expected content length %d, got: %d", test.wantContentLength, res.ContentLength)
4791 }
4792 })
4793 }
4794 }
4795
4796 func TestTransportCloseResponseBodyWhileRequestBodyHangs(t *testing.T) {
4797 synctest.Test(t, testTransportCloseResponseBodyWhileRequestBodyHangs)
4798 }
4799 func testTransportCloseResponseBodyWhileRequestBodyHangs(t *testing.T) {
4800 ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) {
4801 w.WriteHeader(200)
4802 w.(http.Flusher).Flush()
4803 io.Copy(io.Discard, r.Body)
4804 })
4805
4806 tr := newTransport(t)
4807
4808 pr, pw := net.Pipe()
4809 req, err := http.NewRequest("GET", ts.URL, pr)
4810 if err != nil {
4811 t.Fatal(err)
4812 }
4813 res, err := tr.RoundTrip(req)
4814 if err != nil {
4815 t.Fatal(err)
4816 }
4817
4818 res.Body.Close()
4819 pw.Close()
4820 }
4821
4822 func TestTransport300ResponseBody(t *testing.T) { synctest.Test(t, testTransport300ResponseBody) }
4823 func testTransport300ResponseBody(t *testing.T) {
4824 reqc := make(chan struct{})
4825 body := []byte("response body")
4826 ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) {
4827 w.WriteHeader(300)
4828 w.(http.Flusher).Flush()
4829 <-reqc
4830 w.Write(body)
4831 })
4832
4833 tr := newTransport(t)
4834
4835 pr, pw := net.Pipe()
4836 req, err := http.NewRequest("GET", ts.URL, pr)
4837 if err != nil {
4838 t.Fatal(err)
4839 }
4840 res, err := tr.RoundTrip(req)
4841 if err != nil {
4842 t.Fatal(err)
4843 }
4844 close(reqc)
4845 got, err := io.ReadAll(res.Body)
4846 if err != nil {
4847 t.Fatalf("error reading response body: %v", err)
4848 }
4849 if !bytes.Equal(got, body) {
4850 t.Errorf("got response body %q, want %q", string(got), string(body))
4851 }
4852 res.Body.Close()
4853 pw.Close()
4854 }
4855
4856 func TestTransportWriteByteTimeout(t *testing.T) {
4857 ts := newTestServer(t, nil, func(s *http.Server) {
4858 s.Protocols = protocols("h2c")
4859 })
4860 tr := newTransport(t)
4861 tr.Protocols = protocols("h2c")
4862 tr.Dial = func(network, addr string) (net.Conn, error) {
4863 _, c := net.Pipe()
4864 return c, nil
4865 }
4866 tr.HTTP2.WriteByteTimeout = 1 * time.Millisecond
4867 defer tr.CloseIdleConnections()
4868 c := &http.Client{Transport: tr}
4869
4870 _, err := c.Get(ts.URL)
4871 if !errors.Is(err, os.ErrDeadlineExceeded) {
4872 t.Fatalf("Get on unresponsive connection: got %q; want ErrDeadlineExceeded", err)
4873 }
4874 }
4875
4876 type slowWriteConn struct {
4877 net.Conn
4878 hasWriteDeadline bool
4879 }
4880
4881 func (c *slowWriteConn) SetWriteDeadline(t time.Time) error {
4882 c.hasWriteDeadline = !t.IsZero()
4883 return nil
4884 }
4885
4886 func (c *slowWriteConn) Write(b []byte) (n int, err error) {
4887 if c.hasWriteDeadline && len(b) > 1 {
4888 n, err = c.Conn.Write(b[:1])
4889 if err != nil {
4890 return n, err
4891 }
4892 return n, fmt.Errorf("slow write: %w", os.ErrDeadlineExceeded)
4893 }
4894 return c.Conn.Write(b)
4895 }
4896
4897 func TestTransportSlowWrites(t *testing.T) { synctest.Test(t, testTransportSlowWrites) }
4898 func testTransportSlowWrites(t *testing.T) {
4899 ts := newTestServer(t, nil, func(s *http.Server) {
4900 s.Protocols = protocols("h2c")
4901 })
4902 tr := newTransport(t)
4903 tr.Protocols = protocols("h2c")
4904 tr.Dial = func(network, addr string) (net.Conn, error) {
4905 c, err := net.Dial(network, addr)
4906 return &slowWriteConn{Conn: c}, err
4907 }
4908 tr.HTTP2.WriteByteTimeout = 1 * time.Millisecond
4909 c := &http.Client{Transport: tr}
4910
4911 const bodySize = 1 << 20
4912 resp, err := c.Post(ts.URL, "text/foo", io.LimitReader(neverEnding('A'), bodySize))
4913 if err != nil {
4914 t.Fatal(err)
4915 }
4916 resp.Body.Close()
4917 }
4918
4919 func TestTransportClosesConnAfterGoAwayNoStreams(t *testing.T) {
4920 synctest.Test(t, func(t *testing.T) {
4921 testTransportClosesConnAfterGoAway(t, 0)
4922 })
4923 }
4924 func TestTransportClosesConnAfterGoAwayLastStream(t *testing.T) {
4925 synctest.Test(t, func(t *testing.T) {
4926 testTransportClosesConnAfterGoAway(t, 1)
4927 })
4928 }
4929
4930
4931
4932
4933
4934
4935
4936 func testTransportClosesConnAfterGoAway(t *testing.T, lastStream uint32) {
4937 tc := newTestClientConn(t)
4938 tc.greet()
4939
4940 req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
4941 rt := tc.roundTrip(req)
4942
4943 tc.wantFrameType(FrameHeaders)
4944 tc.writeGoAway(lastStream, ErrCodeNo, nil)
4945
4946 if lastStream > 0 {
4947
4948 tc.writeHeaders(HeadersFrameParam{
4949 StreamID: rt.streamID(),
4950 EndHeaders: true,
4951 EndStream: true,
4952 BlockFragment: tc.makeHeaderBlockFragment(
4953 ":status", "200",
4954 ),
4955 })
4956 }
4957
4958 tc.closeWrite()
4959 err := rt.err()
4960 if gotErr, wantErr := err != nil, lastStream == 0; gotErr != wantErr {
4961 t.Errorf("RoundTrip got error %v (want error: %v)", err, wantErr)
4962 }
4963 if !tc.isClosed() {
4964 t.Errorf("ClientConn did not close its net.Conn, expected it to")
4965 }
4966 }
4967
4968 type slowCloser struct {
4969 closing chan struct{}
4970 closed chan struct{}
4971 }
4972
4973 func (r *slowCloser) Read([]byte) (int, error) {
4974 return 0, io.EOF
4975 }
4976
4977 func (r *slowCloser) Close() error {
4978 close(r.closing)
4979 <-r.closed
4980 return nil
4981 }
4982
4983 func TestTransportSlowClose(t *testing.T) {
4984 ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) {
4985 })
4986
4987 client := ts.Client()
4988 body := &slowCloser{
4989 closing: make(chan struct{}),
4990 closed: make(chan struct{}),
4991 }
4992
4993 reqc := make(chan struct{})
4994 go func() {
4995 defer close(reqc)
4996 res, err := client.Post(ts.URL, "text/plain", body)
4997 if err != nil {
4998 t.Error(err)
4999 }
5000 res.Body.Close()
5001 }()
5002 defer func() {
5003 close(body.closed)
5004 <-reqc
5005 }()
5006
5007 <-body.closing
5008
5009 res, err := client.Get(ts.URL)
5010 if err != nil {
5011 t.Fatal(err)
5012 }
5013 res.Body.Close()
5014 }
5015
5016 func TestTransportDialTLSContext(t *testing.T) {
5017 blockCh := make(chan struct{})
5018 serverTLSConfigFunc := func(ts *httptest.Server) {
5019 ts.Config.TLSConfig = &tls.Config{
5020
5021
5022 ClientAuth: tls.RequestClientCert,
5023 }
5024 }
5025 ts := newTestServer(t,
5026 func(w http.ResponseWriter, r *http.Request) {},
5027 serverTLSConfigFunc,
5028 )
5029 tr := newTransport(t)
5030 tr.TLSClientConfig = &tls.Config{
5031 GetClientCertificate: func(cri *tls.CertificateRequestInfo) (*tls.Certificate, error) {
5032
5033
5034 close(blockCh)
5035 <-cri.Context().Done()
5036 return nil, cri.Context().Err()
5037 },
5038 InsecureSkipVerify: true,
5039 }
5040 req, err := http.NewRequest(http.MethodGet, ts.URL, nil)
5041 if err != nil {
5042 t.Fatal(err)
5043 }
5044 ctx, cancel := context.WithCancel(context.Background())
5045 defer cancel()
5046 req = req.WithContext(ctx)
5047 errCh := make(chan error)
5048 go func() {
5049 defer close(errCh)
5050 res, err := tr.RoundTrip(req)
5051 if err != nil {
5052 errCh <- err
5053 return
5054 }
5055 res.Body.Close()
5056 }()
5057
5058 <-blockCh
5059
5060 cancel()
5061
5062 err = <-errCh
5063 if err == nil {
5064 t.Fatal("cancelling context during client certificate fetch did not error as expected")
5065 return
5066 }
5067 if !errors.Is(err, context.Canceled) {
5068 t.Fatalf("unexpected error returned after cancellation: %v", err)
5069 }
5070 }
5071
5072
5073
5074
5075
5076 func TestDialRaceResumesDial(t *testing.T) {
5077 t.Skip("https://go.dev/issue/77908: test fails when using an http.Transport")
5078 blockCh := make(chan struct{})
5079 serverTLSConfigFunc := func(ts *httptest.Server) {
5080 ts.Config.TLSConfig = &tls.Config{
5081
5082
5083 ClientAuth: tls.RequestClientCert,
5084 }
5085 }
5086 ts := newTestServer(t,
5087 func(w http.ResponseWriter, r *http.Request) {},
5088 serverTLSConfigFunc,
5089 )
5090 tr := newTransport(t)
5091 tr.TLSClientConfig = &tls.Config{
5092 GetClientCertificate: func(cri *tls.CertificateRequestInfo) (*tls.Certificate, error) {
5093 select {
5094 case <-blockCh:
5095
5096 return &tls.Certificate{}, nil
5097 default:
5098 }
5099 close(blockCh)
5100 <-cri.Context().Done()
5101 return nil, cri.Context().Err()
5102 },
5103 InsecureSkipVerify: true,
5104 }
5105 req, err := http.NewRequest(http.MethodGet, ts.URL, nil)
5106 if err != nil {
5107 t.Fatal(err)
5108 }
5109
5110 ctx1, cancel1 := context.WithCancel(context.Background())
5111 defer cancel1()
5112 req1 := req.WithContext(ctx1)
5113 ctx2 := t.Context()
5114 req2 := req.WithContext(ctx2)
5115 errCh := make(chan error)
5116 go func() {
5117 res, err := tr.RoundTrip(req1)
5118 if err != nil {
5119 errCh <- err
5120 return
5121 }
5122 res.Body.Close()
5123 }()
5124 successCh := make(chan struct{})
5125 go func() {
5126
5127
5128 <-blockCh
5129 res, err := tr.RoundTrip(req2)
5130 if err != nil {
5131 errCh <- err
5132 return
5133 }
5134 res.Body.Close()
5135
5136
5137 close(successCh)
5138 }()
5139
5140 <-blockCh
5141
5142 cancel1()
5143
5144 err = <-errCh
5145 if err == nil {
5146 t.Fatal("cancelling context during client certificate fetch did not error as expected")
5147 return
5148 }
5149 if !errors.Is(err, context.Canceled) {
5150 t.Fatalf("unexpected error returned after cancellation: %v", err)
5151 }
5152 select {
5153 case err := <-errCh:
5154 t.Fatalf("unexpected second error: %v", err)
5155 case <-successCh:
5156 }
5157 }
5158
5159 func TestTransportDataAfter1xxHeader(t *testing.T) { synctest.Test(t, testTransportDataAfter1xxHeader) }
5160 func testTransportDataAfter1xxHeader(t *testing.T) {
5161
5162 log.SetOutput(io.Discard)
5163 defer log.SetOutput(os.Stderr)
5164
5165
5166 tc := newTestClientConn(t)
5167 tc.greet()
5168
5169 req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
5170 rt := tc.roundTrip(req)
5171
5172 tc.wantFrameType(FrameHeaders)
5173 tc.writeHeaders(HeadersFrameParam{
5174 StreamID: rt.streamID(),
5175 EndHeaders: true,
5176 EndStream: false,
5177 BlockFragment: tc.makeHeaderBlockFragment(
5178 ":status", "100",
5179 ),
5180 })
5181 tc.writeData(rt.streamID(), true, []byte{0})
5182 err := rt.err()
5183 if err, ok := err.(StreamError); !ok || err.Code != ErrCodeProtocol {
5184 t.Errorf("RoundTrip error: %v; want ErrCodeProtocol", err)
5185 }
5186 tc.wantFrameType(FrameRSTStream)
5187 }
5188
5189 func TestIssue66763Race(t *testing.T) {
5190 ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) {},
5191 func(s *http.Server) {
5192 s.Protocols = protocols("h2c")
5193 })
5194 tr := newTransport(t)
5195 tr.IdleConnTimeout = 1 * time.Nanosecond
5196 tr.Protocols = protocols("h2c")
5197
5198 donec := make(chan struct{})
5199 go func() {
5200
5201
5202
5203 conn, err := tr.NewClientConn(t.Context(), "http", ts.URL)
5204 close(donec)
5205 if err == nil {
5206 conn.Close()
5207 }
5208 }()
5209
5210
5211
5212 <-donec
5213 }
5214
5215
5216
5217 func TestIssue67671(t *testing.T) {
5218 ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) {},
5219 func(s *http.Server) {
5220 s.Protocols = protocols("h2c")
5221 })
5222 tr := newTransport(t)
5223 tr.Protocols = protocols("h2c")
5224 req, _ := http.NewRequest("GET", ts.URL, nil)
5225 req.Close = true
5226 for range 2 {
5227 res, err := tr.RoundTrip(req)
5228 if err != nil {
5229 t.Fatal(err)
5230 }
5231 res.Body.Close()
5232 }
5233 }
5234
5235 func TestTransport1xxLimits(t *testing.T) {
5236 for _, test := range []struct {
5237 name string
5238 opt any
5239 ctxfn func(context.Context) context.Context
5240 hcount int
5241 limited bool
5242 }{{
5243 name: "default",
5244 hcount: 10,
5245 limited: false,
5246 }, {
5247 name: "MaxResponseHeaderBytes",
5248 opt: func(tr *http.Transport) {
5249 tr.MaxResponseHeaderBytes = 10000
5250 },
5251 hcount: 10,
5252 limited: true,
5253 }, {
5254 name: "limit by client trace",
5255 ctxfn: func(ctx context.Context) context.Context {
5256 count := 0
5257 return httptrace.WithClientTrace(ctx, &httptrace.ClientTrace{
5258 Got1xxResponse: func(code int, header textproto.MIMEHeader) error {
5259 count++
5260 if count >= 10 {
5261 return errors.New("too many 1xx")
5262 }
5263 return nil
5264 },
5265 })
5266 },
5267 hcount: 10,
5268 limited: true,
5269 }, {
5270 name: "limit disabled by client trace",
5271 opt: func(tr *http.Transport) {
5272 tr.MaxResponseHeaderBytes = 10000
5273 },
5274 ctxfn: func(ctx context.Context) context.Context {
5275 return httptrace.WithClientTrace(ctx, &httptrace.ClientTrace{
5276 Got1xxResponse: func(code int, header textproto.MIMEHeader) error {
5277 return nil
5278 },
5279 })
5280 },
5281 hcount: 20,
5282 limited: false,
5283 }} {
5284 synctestSubtest(t, test.name, func(t *testing.T) {
5285 tc := newTestClientConn(t, test.opt)
5286 tc.greet()
5287
5288 ctx := context.Background()
5289 if test.ctxfn != nil {
5290 ctx = test.ctxfn(ctx)
5291 }
5292 req, _ := http.NewRequestWithContext(ctx, "GET", "https://dummy.tld/", nil)
5293 rt := tc.roundTrip(req)
5294 tc.wantFrameType(FrameHeaders)
5295
5296 for i := 0; i < test.hcount; i++ {
5297 if fr, err := tc.fr.ReadFrame(); err != os.ErrDeadlineExceeded {
5298 t.Fatalf("after writing %v 1xx headers: read %v, %v; want idle", i, fr, err)
5299 }
5300 tc.writeHeaders(HeadersFrameParam{
5301 StreamID: rt.streamID(),
5302 EndHeaders: true,
5303 EndStream: false,
5304 BlockFragment: tc.makeHeaderBlockFragment(
5305 ":status", "103",
5306 "x-field", strings.Repeat("a", 1000),
5307 ),
5308 })
5309 }
5310 if test.limited {
5311 tc.wantFrameType(FrameRSTStream)
5312 } else {
5313 tc.wantIdle()
5314 }
5315 })
5316 }
5317 }
5318
5319
5320
5321 func TestTransportSendPingWithReset(t *testing.T) { synctest.Test(t, testTransportSendPingWithReset) }
5322 func testTransportSendPingWithReset(t *testing.T) {
5323 tc := newTestClientConn(t, func(h2 *http.HTTP2Config) {
5324 h2.StrictMaxConcurrentRequests = true
5325 })
5326
5327 const maxConcurrent = 3
5328 tc.greet(Setting{SettingMaxConcurrentStreams, maxConcurrent})
5329
5330
5331 var rts []*testRoundTrip
5332 for i := range maxConcurrent + 1 {
5333 req := Must(http.NewRequest("GET", "https://dummy.tld/", nil))
5334 rt := tc.roundTrip(req)
5335 if i >= maxConcurrent {
5336 tc.wantIdle()
5337 continue
5338 }
5339 tc.wantFrameType(FrameHeaders)
5340 rts = append(rts, rt)
5341 }
5342
5343
5344 rts[0].cancel()
5345 tc.wantRSTStream(rts[0].streamID(), ErrCodeCancel)
5346 pf := readFrame[*PingFrame](t, tc)
5347 tc.wantIdle()
5348
5349
5350 rts[1].cancel()
5351 tc.wantRSTStream(rts[1].streamID(), ErrCodeCancel)
5352 tc.wantIdle()
5353
5354
5355
5356 tc.writePing(true, pf.Data)
5357 tc.wantFrameType(FrameHeaders)
5358 tc.wantIdle()
5359 }
5360
5361
5362
5363
5364
5365 func TestTransportNoPingAfterResetWithFrames(t *testing.T) {
5366 synctest.Test(t, testTransportNoPingAfterResetWithFrames)
5367 }
5368 func testTransportNoPingAfterResetWithFrames(t *testing.T) {
5369 tc := newTestClientConn(t, func(h2 *http.HTTP2Config) {
5370 h2.StrictMaxConcurrentRequests = true
5371 })
5372
5373 const maxConcurrent = 1
5374 tc.greet(Setting{SettingMaxConcurrentStreams, maxConcurrent})
5375
5376
5377
5378 req1 := Must(http.NewRequest("GET", "https://dummy.tld/", nil))
5379 rt1 := tc.roundTrip(req1)
5380 tc.wantFrameType(FrameHeaders)
5381 tc.writeHeaders(HeadersFrameParam{
5382 StreamID: rt1.streamID(),
5383 EndHeaders: true,
5384 BlockFragment: tc.makeHeaderBlockFragment(
5385 ":status", "200",
5386 ),
5387 })
5388 rt1.wantStatus(200)
5389
5390
5391
5392 req2 := Must(http.NewRequest("GET", "https://dummy.tld/", nil))
5393 rt2 := tc.roundTrip(req2)
5394 tc.wantIdle()
5395
5396
5397
5398 rt1.cancel()
5399 tc.wantRSTStream(rt1.streamID(), ErrCodeCancel)
5400 tc.wantFrameType(FrameHeaders)
5401
5402
5403
5404
5405 rt2.cancel()
5406 tc.wantRSTStream(rt2.streamID(), ErrCodeCancel)
5407 tc.wantFrameType(FramePing)
5408 }
5409
5410
5411
5412 func TestTransportSendNoMoreThanOnePingWithReset(t *testing.T) {
5413 synctest.Test(t, testTransportSendNoMoreThanOnePingWithReset)
5414 }
5415 func testTransportSendNoMoreThanOnePingWithReset(t *testing.T) {
5416 tc := newTestClientConn(t)
5417 tc.greet()
5418
5419 makeAndResetRequest := func() {
5420 t.Helper()
5421 ctx, cancel := context.WithCancel(context.Background())
5422 req := Must(http.NewRequestWithContext(ctx, "GET", "https://dummy.tld/", nil))
5423 rt := tc.roundTrip(req)
5424 tc.wantFrameType(FrameHeaders)
5425 cancel()
5426 tc.wantRSTStream(rt.streamID(), ErrCodeCancel)
5427 }
5428
5429
5430
5431 makeAndResetRequest()
5432 pf1 := readFrame[*PingFrame](t, tc)
5433 tc.wantIdle()
5434
5435
5436
5437
5438
5439 makeAndResetRequest()
5440 tc.wantIdle()
5441
5442
5443
5444 tc.writeHeaders(HeadersFrameParam{
5445 StreamID: 1,
5446 EndHeaders: true,
5447 EndStream: true,
5448 BlockFragment: tc.makeHeaderBlockFragment(
5449 ":status", "200",
5450 ),
5451 })
5452 tc.wantIdle()
5453
5454
5455
5456
5457 makeAndResetRequest()
5458 tc.wantIdle()
5459
5460
5461 tc.writePing(true, pf1.Data)
5462 tc.wantIdle()
5463
5464
5465
5466
5467 makeAndResetRequest()
5468 tc.wantIdle()
5469
5470
5471 tc.writeHeaders(HeadersFrameParam{
5472 StreamID: 3,
5473 EndHeaders: true,
5474 EndStream: true,
5475 BlockFragment: tc.makeHeaderBlockFragment(
5476 ":status", "200",
5477 ),
5478 })
5479 tc.wantIdle()
5480
5481
5482
5483 makeAndResetRequest()
5484 tc.wantFrameType(FramePing)
5485 }
5486
5487 func TestTransportConnBecomesUnresponsive(t *testing.T) {
5488 synctest.Test(t, testTransportConnBecomesUnresponsive)
5489 }
5490 func testTransportConnBecomesUnresponsive(t *testing.T) {
5491
5492
5493
5494 tt := newTestTransport(t)
5495
5496 const maxConcurrent = 3
5497
5498 t.Logf("first request opens a new connection and succeeds")
5499 req1 := Must(http.NewRequest("GET", "https://dummy.tld/", nil))
5500 rt1 := tt.roundTrip(req1)
5501 tc1 := tt.getConn()
5502 tc1.wantFrameType(FrameSettings)
5503 tc1.wantFrameType(FrameWindowUpdate)
5504 hf1 := readFrame[*HeadersFrame](t, tc1)
5505 tc1.writeSettings(Setting{SettingMaxConcurrentStreams, maxConcurrent})
5506 tc1.wantFrameType(FrameSettings)
5507 tc1.writeHeaders(HeadersFrameParam{
5508 StreamID: hf1.StreamID,
5509 EndHeaders: true,
5510 EndStream: true,
5511 BlockFragment: tc1.makeHeaderBlockFragment(
5512 ":status", "200",
5513 ),
5514 })
5515 rt1.wantStatus(200)
5516 rt1.response().Body.Close()
5517
5518
5519
5520
5521 for i := range maxConcurrent {
5522 t.Logf("request %v receives no response and is canceled", i)
5523 ctx, cancel := context.WithCancel(context.Background())
5524 req := Must(http.NewRequestWithContext(ctx, "GET", "https://dummy.tld/", nil))
5525 tt.roundTrip(req)
5526 if tt.hasConn() {
5527 t.Fatalf("new connection created; expect existing conn to be reused")
5528 }
5529 tc1.wantFrameType(FrameHeaders)
5530 cancel()
5531 tc1.wantFrameType(FrameRSTStream)
5532 if i == 0 {
5533 tc1.wantFrameType(FramePing)
5534 }
5535 tc1.wantIdle()
5536 }
5537
5538
5539
5540 req2 := Must(http.NewRequest("GET", "https://dummy.tld/", nil))
5541 rt2 := tt.roundTrip(req2)
5542 tc2 := tt.getConn()
5543 tc2.wantFrameType(FrameSettings)
5544 tc2.wantFrameType(FrameWindowUpdate)
5545 hf := readFrame[*HeadersFrame](t, tc2)
5546 tc2.writeSettings(Setting{SettingMaxConcurrentStreams, maxConcurrent})
5547 tc2.wantFrameType(FrameSettings)
5548 tc2.writeHeaders(HeadersFrameParam{
5549 StreamID: hf.StreamID,
5550 EndHeaders: true,
5551 EndStream: true,
5552 BlockFragment: tc2.makeHeaderBlockFragment(
5553 ":status", "200",
5554 ),
5555 })
5556 rt2.wantStatus(200)
5557 rt2.response().Body.Close()
5558 }
5559
5560
5561
5562
5563
5564
5565 func newTestTransportWithUnusedConn(t *testing.T, opts ...any) *testTransport {
5566 tt := newTestTransport(t, opts...)
5567
5568 waitc := make(chan struct{})
5569 dialContext := tt.tr1.DialContext
5570 tt.tr1.DialContext = func(ctx context.Context, network, address string) (net.Conn, error) {
5571 <-waitc
5572 return dialContext(ctx, network, address)
5573 }
5574
5575 req := Must(http.NewRequest("GET", "https://dummy.tld/", nil))
5576 rt := tt.roundTrip(req)
5577 rt.cancel()
5578 if rt.err() == nil {
5579 t.Fatalf("RoundTrip still running after request is canceled")
5580 }
5581
5582 close(waitc)
5583 synctest.Wait()
5584 return tt
5585 }
5586
5587
5588 func TestTransportUnusedConnOK(t *testing.T) { synctest.Test(t, testTransportUnusedConnOK) }
5589 func testTransportUnusedConnOK(t *testing.T) {
5590 tt := newTestTransportWithUnusedConn(t)
5591
5592 req := Must(http.NewRequest("GET", "https://dummy.tld/", nil))
5593 tc := tt.getConn()
5594 tc.wantFrameType(FrameSettings)
5595 tc.wantFrameType(FrameWindowUpdate)
5596
5597
5598
5599 rt := tt.roundTrip(req)
5600 tc.wantHeaders(wantHeader{
5601 streamID: 1,
5602 endStream: true,
5603 header: http.Header{
5604 ":authority": []string{"dummy.tld"},
5605 ":method": []string{"GET"},
5606 ":path": []string{"/"},
5607 },
5608 })
5609
5610 tc.writeSettings()
5611 tc.writeSettingsAck()
5612 tc.wantFrameType(FrameSettings)
5613
5614 tc.writeHeaders(HeadersFrameParam{
5615 StreamID: 1,
5616 EndHeaders: true,
5617 EndStream: true,
5618 BlockFragment: tc.makeHeaderBlockFragment(
5619 ":status", "200",
5620 ),
5621 })
5622 rt.wantStatus(200)
5623 rt.wantBody(nil)
5624 }
5625
5626
5627 func TestTransportUnusedConnImmediateFailureUsed(t *testing.T) {
5628 synctest.Test(t, testTransportUnusedConnImmediateFailureUsed)
5629 }
5630 func testTransportUnusedConnImmediateFailureUsed(t *testing.T) {
5631 tt := newTestTransportWithUnusedConn(t)
5632
5633
5634 tc1 := tt.getConn()
5635 tc1.closeWrite()
5636
5637
5638
5639
5640 req := Must(http.NewRequest("GET", "https://dummy.tld/", nil))
5641 rt := tt.roundTrip(req)
5642 if err := rt.err(); err == nil || errors.Is(err, ErrNoCachedConn) {
5643 t.Fatalf("RoundTrip with broken conn: got %v, want an error other than ErrNoCachedConn", err)
5644 }
5645
5646
5647
5648
5649 _ = tt.roundTrip(req)
5650 tc2 := tt.getConn()
5651 tc2.wantFrameType(FrameSettings)
5652 tc2.wantFrameType(FrameWindowUpdate)
5653 tc2.wantFrameType(FrameHeaders)
5654 }
5655
5656
5657 func TestTransportUnusedConnIdleTimoutBeforeUse(t *testing.T) {
5658 synctest.Test(t, testTransportUnusedConnIdleTimoutBeforeUse)
5659 }
5660 func testTransportUnusedConnIdleTimoutBeforeUse(t *testing.T) {
5661 tt := newTestTransportWithUnusedConn(t, func(t1 *http.Transport) {
5662 t1.IdleConnTimeout = 1 * time.Second
5663 })
5664
5665 _ = tt.getConn()
5666
5667
5668 time.Sleep(2 * time.Second)
5669 synctest.Wait()
5670
5671
5672
5673
5674
5675 req := Must(http.NewRequest("GET", "https://dummy.tld/", nil))
5676 _ = tt.roundTrip(req)
5677 tc2 := tt.getConn()
5678 tc2.wantFrameType(FrameSettings)
5679 tc2.wantFrameType(FrameWindowUpdate)
5680 tc2.wantFrameType(FrameHeaders)
5681 }
5682
5683
5684
5685 func TestTransportTLSNextProtoConnImmediateFailureUnused(t *testing.T) {
5686 synctest.Test(t, testTransportTLSNextProtoConnImmediateFailureUnused)
5687 }
5688 func testTransportTLSNextProtoConnImmediateFailureUnused(t *testing.T) {
5689 tt := newTestTransportWithUnusedConn(t, func(t1 *http.Transport) {
5690 t1.IdleConnTimeout = 1 * time.Second
5691 })
5692
5693
5694 tc1 := tt.getConn()
5695 tc1.closeWrite()
5696
5697
5698
5699 time.Sleep(10 * time.Second)
5700
5701
5702
5703
5704 req := Must(http.NewRequest("GET", "https://dummy.tld/", nil))
5705 _ = tt.roundTrip(req)
5706 tc2 := tt.getConn()
5707 tc2.wantFrameType(FrameSettings)
5708 tc2.wantFrameType(FrameWindowUpdate)
5709 tc2.wantFrameType(FrameHeaders)
5710 }
5711
5712 func TestTransportDoNotHangOnZeroMaxFrameSize(t *testing.T) {
5713 synctest.Test(t, testTransportDoNotHangOnZeroMaxFrameSize)
5714 }
5715 func testTransportDoNotHangOnZeroMaxFrameSize(t *testing.T) {
5716 tc := newTestClientConn(t)
5717 tc.writeSettings(Setting{ID: SettingMaxFrameSize, Val: 0})
5718 tc.wantFrameType(FrameSettings)
5719
5720 req, _ := http.NewRequest("POST", "https://dummy.tld/", strings.NewReader("body"))
5721 tc.roundTrip(req)
5722
5723 }
5724
5725 func TestExtendedConnectClientWithServerSupport(t *testing.T) {
5726 t.Skip("https://go.dev/issue/53208 -- net/http needs to support the :protocol header")
5727 SetDisableExtendedConnectProtocol(t, false)
5728 ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) {
5729 if r.Header.Get(":protocol") != "extended-connect" {
5730 t.Fatalf("unexpected :protocol header received")
5731 }
5732 t.Log(io.Copy(w, r.Body))
5733 })
5734 tr := newTransport(t)
5735 pr, pw := io.Pipe()
5736 pwDone := make(chan struct{})
5737 req, _ := http.NewRequest("CONNECT", ts.URL, pr)
5738 req.Header.Set(":protocol", "extended-connect")
5739 req.Header.Set("X-A", "A")
5740 req.Header.Set("X-B", "B")
5741 req.Header.Set("X-C", "C")
5742 go func() {
5743 pw.Write([]byte("hello, extended connect"))
5744 pw.Close()
5745 close(pwDone)
5746 }()
5747
5748 res, err := tr.RoundTrip(req)
5749 if err != nil {
5750 t.Fatal(err)
5751 }
5752 body, err := io.ReadAll(res.Body)
5753 if err != nil {
5754 t.Fatal(err)
5755 }
5756 if !bytes.Equal(body, []byte("hello, extended connect")) {
5757 t.Fatal("unexpected body received")
5758 }
5759 }
5760
5761 func TestExtendedConnectClientWithoutServerSupport(t *testing.T) {
5762 t.Skip("https://go.dev/issue/53208 -- net/http needs to support the :protocol header")
5763 SetDisableExtendedConnectProtocol(t, true)
5764 ts := newTestServer(t, func(w http.ResponseWriter, r *http.Request) {
5765 io.Copy(w, r.Body)
5766 })
5767 tr := newTransport(t)
5768 pr, pw := io.Pipe()
5769 pwDone := make(chan struct{})
5770 req, _ := http.NewRequest("CONNECT", ts.URL, pr)
5771 req.Header.Set(":protocol", "extended-connect")
5772 req.Header.Set("X-A", "A")
5773 req.Header.Set("X-B", "B")
5774 req.Header.Set("X-C", "C")
5775 go func() {
5776 pw.Write([]byte("hello, extended connect"))
5777 pw.Close()
5778 close(pwDone)
5779 }()
5780
5781 _, err := tr.RoundTrip(req)
5782 if !errors.Is(err, ErrExtendedConnectNotSupported) {
5783 t.Fatalf("expected error errExtendedConnectNotSupported, got: %v", err)
5784 }
5785 }
5786
5787
5788
5789 func TestExtendedConnectReadFrameError(t *testing.T) {
5790 synctest.Test(t, testExtendedConnectReadFrameError)
5791 }
5792 func testExtendedConnectReadFrameError(t *testing.T) {
5793 t.Skip("https://go.dev/issue/53208 -- net/http needs to support the :protocol header")
5794 tc := newTestClientConn(t)
5795 tc.wantFrameType(FrameSettings)
5796 tc.wantFrameType(FrameWindowUpdate)
5797
5798 req, _ := http.NewRequest("CONNECT", "https://dummy.tld/", nil)
5799 req.Header.Set(":protocol", "extended-connect")
5800 rt := tc.roundTrip(req)
5801 tc.wantIdle()
5802
5803 tc.closeWrite()
5804 if !rt.done() {
5805 t.Fatalf("after connection closed: RoundTrip still running; want done")
5806 }
5807 if rt.err() == nil {
5808 t.Fatalf("after connection closed: RoundTrip succeeded; want error")
5809 }
5810 }
5811
View as plain text