1
2
3
4
5 package http3
6
7 import (
8 "errors"
9 "fmt"
10 "io"
11 "maps"
12 "net/http"
13 "net/netip"
14 "net/url"
15 "os"
16 "reflect"
17 "slices"
18 "strconv"
19 "strings"
20 "sync"
21 "testing"
22 "testing/synctest"
23 "time"
24
25 "golang.org/x/net/quic"
26 )
27
28
29
30
31
32
33
34 func requestHeader(h http.Header) http.Header {
35 minimalHeader := http.Header{
36 ":method": {"GET"},
37 ":scheme": {"https"},
38 ":path": {"/"},
39 }
40 maps.Copy(minimalHeader, h)
41 return minimalHeader
42 }
43
44 func TestServerReceivePushStream(t *testing.T) {
45
46
47
48 synctest.Test(t, func(t *testing.T) {
49 ts := newTestServer(t, nil)
50 tc := ts.connect()
51 tc.newStream(streamTypePush)
52 tc.wantClosed("invalid client-created push stream", errH3StreamCreationError)
53 })
54 }
55
56 func TestServerCancelPushForUnsentPromise(t *testing.T) {
57 synctest.Test(t, func(t *testing.T) {
58 ts := newTestServer(t, nil)
59 tc := ts.connect()
60 tc.greet()
61
62 const pushID = 100
63 tc.control.writeVarint(int64(frameTypeCancelPush))
64 tc.control.writeVarint(int64(sizeVarint(pushID)))
65 tc.control.writeVarint(pushID)
66 tc.control.Flush()
67
68 tc.wantClosed("client canceled never-sent push ID", errH3IDError)
69 })
70 }
71
72 func TestServerHeader(t *testing.T) {
73 synctest.Test(t, func(t *testing.T) {
74 ts := newTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
75 header := w.Header()
76 for key, values := range r.Header {
77 for _, value := range values {
78 header.Add(key, value)
79 }
80 }
81 w.WriteHeader(204)
82 }))
83 tc := ts.connect()
84 tc.greet()
85
86 reqStream := tc.newStream(streamTypeRequest)
87 reqStream.writeHeaders(requestHeader(http.Header{
88 "header-from-client": {"that", "should", "be", "echoed"},
89 }))
90 reqStream.wantSomeHeaders(http.Header{
91 ":status": {"204"},
92 "Header-From-Client": {"that", "should", "be", "echoed"},
93 })
94 reqStream.wantClosed("request is complete")
95 })
96 }
97
98 func TestServerHeaderSnapshot(t *testing.T) {
99 synctest.Test(t, func(t *testing.T) {
100 ts := newTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
101 w.Header().Set("X-Test-Header", "original")
102 w.WriteHeader(200)
103 w.Header().Set("X-Test-Header", "modified")
104 w.Write([]byte("body"))
105 }))
106 tc := ts.connect()
107 tc.greet()
108
109 reqStream := tc.newStream(streamTypeRequest)
110 reqStream.writeHeaders(requestHeader(nil))
111 reqStream.wantSomeHeaders(http.Header{
112 ":status": {"200"},
113 "X-Test-Header": {"original"},
114 })
115 reqStream.wantData([]byte("body"))
116 reqStream.wantClosed("request is complete")
117 })
118 }
119
120 func TestServerHeaderInvalid(t *testing.T) {
121 tests := []struct {
122 name string
123 header http.Header
124 wantError bool
125 }{
126 {
127 name: "header name with control character",
128 header: http.Header{"name\nevilinjection": {"Value"}},
129 wantError: true,
130 },
131 {
132 name: "header name with uppercase character",
133 header: http.Header{"nAme": {"Value"}},
134 wantError: true,
135 },
136 {
137 name: "pseudo-header name with control character",
138 header: http.Header{":path\nevilinjection": {"Value"}},
139 wantError: true,
140 },
141 {
142 name: "pseudo-header name with uppercase character",
143 header: http.Header{":meThod": {"Value"}},
144 wantError: true,
145 },
146 {
147 name: "header value with control character",
148 header: http.Header{"name": {"Value\nEvilInjection"}},
149 wantError: true,
150 },
151 {
152 name: "pseudo-header value with control character",
153 header: http.Header{":method": {"Value\nEvilInjection"}},
154 wantError: true,
155 },
156 {
157 name: "connection header name",
158 header: http.Header{"connection": {"foo"}},
159 wantError: true,
160 },
161 {
162 name: "keep-alive header name",
163 header: http.Header{"Keep-Alive": {"foo"}},
164 wantError: true,
165 },
166 {
167 name: "proxy-connection header name",
168 header: http.Header{"proxy-connection": {"foo"}},
169 wantError: true,
170 },
171 {
172 name: "transfer-encoding header name",
173 header: http.Header{"transfer-encoding": {"foo"}},
174 wantError: true,
175 },
176 {
177 name: "upgrade header name",
178 header: http.Header{"upgrade": {"foo"}},
179 wantError: true,
180 },
181 {
182 name: "te header name",
183 header: http.Header{"te": {"foo"}},
184 wantError: true,
185 },
186 {
187 name: "te header name with trailers value",
188 header: http.Header{"te": {"trailers"}},
189 wantError: false,
190 },
191 }
192 for _, tt := range tests {
193 synctestSubtest(t, tt.name, func(t *testing.T) {
194 body := []byte("some data")
195 ts := newTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
196 w.Write(body)
197 }))
198 tc := ts.connect()
199 tc.greet()
200
201 reqStream := tc.newStream(streamTypeRequest)
202 reqStream.writeHeadersRaw(requestHeader(tt.header))
203
204 if tt.wantError {
205 reqStream.wantError(quic.StreamError(errH3MessageError))
206 } else {
207 reqStream.wantHeaders(nil)
208 reqStream.wantData(body)
209 reqStream.wantClosed("request is complete")
210 }
211 })
212 }
213 }
214
215 func TestServerPseudoHeader(t *testing.T) {
216 synctest.Test(t, func(t *testing.T) {
217 ts := newTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
218
219
220 if len(r.Header) != 0 {
221 t.Errorf("got %v, want request header to be empty", r.Header)
222 }
223 if r.Method != "GET" {
224 t.Errorf("got %v, want GET method", r.Method)
225 }
226 if r.Host != "fake.tld:1234" {
227 t.Errorf("got %v, want fake.tld:1234", r.Host)
228 }
229 wantURL := &url.URL{
230 Path: "/some/path",
231 RawQuery: "query=value&query2=value2#fragment",
232 }
233 if !reflect.DeepEqual(r.URL, wantURL) {
234 t.Errorf("got %v, want URL to be %v", r.URL, wantURL)
235 }
236
237
238
239 header := w.Header()
240 header.Add(":status", "123")
241 w.WriteHeader(321)
242 }))
243 tc := ts.connect()
244 tc.greet()
245
246 reqStream := tc.newStream(streamTypeRequest)
247 reqStream.writeHeaders(http.Header{
248 ":method": {"GET"},
249 ":authority": {"fake.tld:1234"},
250 ":scheme": {"https"},
251 ":path": {"/some/path?query=value&query2=value2#fragment"},
252 })
253 reqStream.wantSomeHeaders(http.Header{":status": {"321"}})
254 reqStream.wantClosed("request is complete")
255
256 reqStream = tc.newStream(streamTypeRequest)
257 reqStream.writeHeaders(http.Header{})
258 reqStream.wantError(quic.StreamError(errH3MessageError))
259 })
260 }
261
262 func TestServerPseudoHeaderCount(t *testing.T) {
263 tests := []struct {
264 name string
265 header http.Header
266 wantError bool
267 }{
268 {
269 name: "missing method pseudo-header",
270 header: http.Header{
271 ":scheme": {"https"},
272 ":path": {"/"},
273 ":authority": {"fake.tld:1234"},
274 },
275 wantError: true,
276 },
277 {
278 name: "valid pseudo-headers for non-CONNECT request",
279 header: http.Header{
280 ":method": {"GET"},
281 ":scheme": {"https"},
282 ":path": {"/"},
283 },
284 wantError: false,
285 },
286 {
287 name: "extraneous pseudo-headers for non-CONNECT request",
288 header: http.Header{
289 ":method": {"GET", "GET"},
290 ":scheme": {"https"},
291 ":path": {"/"},
292 },
293 wantError: true,
294 },
295 {
296 name: "missing pseudo-headers for non-CONNECT request",
297 header: http.Header{
298 ":method": {"GET", "GET"},
299 ":path": {"/"},
300 },
301 wantError: true,
302 },
303 {
304 name: "valid pseudo-headers for CONNECT request",
305 header: http.Header{
306 ":method": {"CONNECT"},
307 ":authority": {"fake.tld:1234"},
308 },
309 wantError: false,
310 },
311 {
312 name: "extraneous pseudo-headers for CONNECT request",
313 header: http.Header{
314 ":method": {"CONNECT"},
315 ":authority": {"fake.tld:1234"},
316 ":path": {"/"},
317 },
318 wantError: true,
319 },
320 {
321 name: "missing pseudo-headers for CONNECT request",
322 header: http.Header{
323 ":method": {"CONNECT"},
324 },
325 wantError: true,
326 },
327 }
328 for _, tt := range tests {
329 synctestSubtest(t, tt.name, func(t *testing.T) {
330 body := []byte("some data")
331 ts := newTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
332 w.Write(body)
333 }))
334 tc := ts.connect()
335 tc.greet()
336
337 reqStream := tc.newStream(streamTypeRequest)
338 reqStream.writeHeaders(tt.header)
339
340 if tt.wantError {
341 reqStream.wantError(quic.StreamError(errH3MessageError))
342 } else {
343 reqStream.wantHeaders(nil)
344 reqStream.wantData(body)
345 reqStream.wantClosed("request is complete")
346 }
347 })
348 }
349 }
350
351 func TestServerInvalidHeader(t *testing.T) {
352 synctest.Test(t, func(t *testing.T) {
353 ts := newTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
354 w.Header().Add("valid-name", "valid value")
355
356 w.Header().Add("invalid name with spaces", "some value")
357 w.Header().Add("some-name", "invalid value with \n")
358 w.Header().Add("valid-name-2", "valid value 2")
359 w.WriteHeader(200)
360 }))
361 tc := ts.connect()
362 tc.greet()
363
364 reqStream := tc.newStream(streamTypeRequest)
365 reqStream.writeHeaders(requestHeader(nil))
366 reqStream.wantSomeHeaders(http.Header{
367 ":status": {"200"},
368 "Valid-Name": {"valid value"},
369 "Valid-Name-2": {"valid value 2"},
370 })
371 reqStream.wantClosed("request is complete")
372 })
373 }
374
375 func TestServerAuthorityAndHostHeader(t *testing.T) {
376 for _, test := range []struct {
377 name string
378 h http.Header
379 valid bool
380 wantHost string
381 }{{
382 name: "authority host mismatch",
383 h: http.Header{
384 ":authority": {"example.tld"},
385 "host": {"other.tld"},
386 },
387 }, {
388
389
390
391 name: "authority host case differs",
392 h: http.Header{
393 ":authority": {"example.tld"},
394 "host": {"EXAMPLE.TLD"},
395 },
396 }, {
397 name: "authority and multiple host",
398 h: http.Header{
399 ":authority": {"example.tld"},
400 "host": {"example.tld", "example.tld"},
401 },
402 }, {
403 name: "multiple host only",
404 h: http.Header{
405 "host": {"example.tld", "example.tld"},
406 },
407 }, {
408 name: "multiple authority only",
409 h: http.Header{
410 ":authority": {"example.tld", "example.tld"},
411 },
412 }, {
413 name: "empty authority",
414 h: http.Header{
415 ":authority": {""},
416 },
417 }, {
418 name: "invalid authority",
419 h: http.Header{
420 ":authority": {"example . tld"},
421 },
422 }, {
423 name: "invalid host",
424 h: http.Header{
425 "host": {"example . tld"},
426 },
427 }, {
428 name: "authority only",
429 h: http.Header{
430 ":authority": {"example.tld"},
431 },
432 valid: true,
433 wantHost: "example.tld",
434 }, {
435 name: "host only",
436 h: http.Header{
437 "host": {"example.tld"},
438 },
439 valid: true,
440 wantHost: "example.tld",
441 }, {
442 name: "authority host match",
443 h: http.Header{
444 ":authority": {"example.tld"},
445 "host": {"example.tld"},
446 },
447 valid: true,
448 wantHost: "example.tld",
449 }, {
450 name: "authority host match with port",
451 h: http.Header{
452 ":authority": {"example.tld:443"},
453 "host": {"example.tld:443"},
454 },
455 valid: true,
456 wantHost: "example.tld:443",
457 }, {
458 name: "authority host mismatch with port",
459 h: http.Header{
460 ":authority": {"example.tld:80"},
461 "host": {"example.tld:443"},
462 },
463 }, {
464 name: "userinfo in authority",
465 h: http.Header{
466 ":authority": {"user:pass@example.tld"},
467 },
468 }, {
469 name: "userinfo in host",
470 h: http.Header{
471 "host": {"user:pass@example.tld"},
472 },
473 }, {
474 name: "neither authority nor host",
475 h: http.Header{},
476 valid: true,
477 wantHost: "",
478 }} {
479 synctestSubtest(t, test.name, func(t *testing.T) {
480 ts := newTestServer(t, nil)
481 tc := ts.connect()
482 tc.greet()
483
484 reqStream := tc.newStream(streamTypeRequest)
485 reqStream.writeHeaders(requestHeader(test.h))
486 if test.valid {
487 call := tc.nextHandlerCall()
488 if call == nil {
489 t.Fatal("no server handler call; want one")
490 }
491 if got, want := call.req.Host, test.wantHost; got != want {
492 t.Errorf("handler got Host %q, want %q", got, want)
493 }
494 if h, ok := call.req.Header["Host"]; ok {
495 t.Errorf(`handler got Header["Host"] = %q, want unset`, h)
496 }
497 } else {
498 reqStream.wantError(quic.StreamError(errH3MessageError))
499 }
500 })
501 }
502 }
503
504 func TestServerInvalidStatus(t *testing.T) {
505 synctest.Test(t, func(t *testing.T) {
506 gotpanic := make(chan bool)
507 ts := newTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
508 defer close(gotpanic)
509 defer func() {
510 if e := recover(); e != nil {
511 got := fmt.Sprintf("%T, %v", e, e)
512 want := "string, invalid WriteHeader code 0"
513 if got != want {
514 t.Errorf("unexpected panic value:\n got: %v\nwant: %v\n", got, want)
515 }
516 gotpanic <- true
517
518
519
520 w.WriteHeader(503)
521
522
523
524 w.WriteHeader(0)
525 }
526 }()
527 w.WriteHeader(0)
528 }))
529 tc := ts.connect()
530 tc.greet()
531
532 reqStream := tc.newStream(streamTypeRequest)
533 reqStream.writeHeaders(requestHeader(nil))
534 if !<-gotpanic {
535 t.Error("expected panic in handler")
536 }
537 synctest.Wait()
538 reqStream.wantSomeHeaders(http.Header{
539 ":status": {"503"},
540 })
541 reqStream.wantClosed("request is complete")
542 })
543 }
544
545 func TestServerHeaderLimits(t *testing.T) {
546 for _, test := range []struct {
547 name string
548 h http.Header
549 valid bool
550 maxHeaderBytes int
551 maxHeaderValueCount int
552 }{{
553 name: "within limits",
554 h: http.Header{
555 "x-foo": {strings.Repeat("x", 1000)},
556 },
557 maxHeaderBytes: 1500,
558 valid: true,
559 }, {
560 name: "too many header bytes",
561 h: http.Header{
562 "x-foo": {strings.Repeat("x", 1000)},
563 "x-bar": {strings.Repeat("x", 1000)},
564 },
565 maxHeaderBytes: 1500,
566 }, {
567 name: "field count within limit",
568 h: http.Header{
569
570 "x-foo": {"4"},
571 "x-bar": {"5"},
572 },
573 maxHeaderBytes: 1500,
574 maxHeaderValueCount: 5,
575 valid: true,
576 }, {
577 name: "field count over limit",
578 h: http.Header{
579
580 "x-foo": {"4"},
581 "x-bar": {"5"},
582 },
583 maxHeaderBytes: 1500,
584 maxHeaderValueCount: 4,
585 }} {
586 synctestSubtest(t, test.name, func(t *testing.T) {
587 if test.maxHeaderValueCount != 0 {
588 t.Skip("TODO: when we support only go1.27")
589 }
590 ts := newTestServer(t, nil)
591 ts.s.srv1.MaxHeaderBytes = test.maxHeaderBytes
592
593
594 tc := ts.connect()
595 tc.greet()
596
597 reqStream := tc.newStream(streamTypeRequest)
598 reqStream.writeHeaders(requestHeader(test.h))
599 if test.valid {
600 call := tc.nextHandlerCall()
601 if call == nil {
602 t.Fatal("no server handler call; want one")
603 }
604 } else {
605 reqStream.wantError(quic.StreamError(errH3RequestRejected))
606 }
607 })
608 }
609 }
610
611 func TestServerBody(t *testing.T) {
612 synctest.Test(t, func(t *testing.T) {
613 ts := newTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
614 body, err := io.ReadAll(r.Body)
615 if err != nil {
616 t.Fatal(err)
617 }
618 w.Write([]byte(r.URL.Path))
619 w.Write(body)
620 }))
621 tc := ts.connect()
622 tc.greet()
623
624 reqStream := tc.newStream(streamTypeRequest)
625 reqStream.writeHeaders(requestHeader(nil))
626 bodyContent := []byte("some body content that should be echoed")
627 reqStream.writeData(bodyContent)
628 reqStream.CloseWrite()
629 reqStream.wantSomeHeaders(http.Header{":status": {"200"}})
630
631 reqStream.wantData(append([]byte("/"), bodyContent...))
632 reqStream.wantClosed("request is complete")
633 })
634 }
635
636 func TestServerHeadResponseNoBody(t *testing.T) {
637 bodyContent := []byte("response body that will not be sent for HEAD requests")
638 synctest.Test(t, func(t *testing.T) {
639 ts := newTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
640 w.Write(bodyContent)
641 }))
642 tc := ts.connect()
643 tc.greet()
644
645 reqStream := tc.newStream(streamTypeRequest)
646 reqStream.writeHeaders(requestHeader(nil))
647 reqStream.wantSomeHeaders(http.Header{":status": {"200"}})
648 reqStream.wantData(bodyContent)
649 reqStream.wantClosed("request is complete")
650
651 reqStream = tc.newStream(streamTypeRequest)
652 reqStream.writeHeaders(requestHeader(http.Header{":method": {http.MethodHead}}))
653 reqStream.wantSomeHeaders(http.Header{":status": {"200"}})
654 reqStream.wantClosed("request is complete")
655 })
656 }
657
658 func TestServerShutdownGoaway(t *testing.T) {
659 synctest.Test(t, func(t *testing.T) {
660 ts := newTestServer(t, nil)
661
662 tc := ts.connect()
663 tc.greet()
664 tc.wantNotClosed("after initial connection handshake")
665
666 requestCount := int64(5)
667 for range requestCount {
668 tc.newStream(streamTypeRequest).writeHeaders(requestHeader(nil))
669 }
670
671 control := tc.wantStream(streamTypeControl)
672 control.wantSettings(nil)
673
674 shutdownComplete := make(chan any)
675 go func() {
676 ts.s.shutdown(t.Context())
677 shutdownComplete <- struct{}{}
678 }()
679 control.wantGoaway((requestCount - 1) * 4)
680 <-shutdownComplete
681 })
682 }
683
684 func TestServerHandlerEmpty(t *testing.T) {
685 synctest.Test(t, func(t *testing.T) {
686 ts := newTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
687
688 }))
689 tc := ts.connect()
690 tc.greet()
691
692 reqStream := tc.newStream(streamTypeRequest)
693 reqStream.writeHeaders(requestHeader(nil))
694 reqStream.wantSomeHeaders(http.Header{":status": {"200"}})
695 reqStream.wantClosed("request is complete")
696 })
697 }
698
699 func TestServerHandlerFlushing(t *testing.T) {
700 synctest.Test(t, func(t *testing.T) {
701 ts := newTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
702 time.Sleep(time.Second)
703 w.Write([]byte("first"))
704
705 time.Sleep(time.Second)
706 w.Write([]byte("second"))
707 w.(http.Flusher).Flush()
708
709 time.Sleep(time.Second)
710 w.Write([]byte("third"))
711 }))
712 tc := ts.connect()
713 tc.greet()
714
715 reqStream := tc.newStream(streamTypeRequest)
716 reqStream.writeHeaders(requestHeader(nil))
717 respBody := make([]byte, 100)
718
719 time.Sleep(time.Second)
720 synctest.Wait()
721 if n, err := reqStream.Read(respBody); err == nil {
722 t.Errorf("got %v bytes read, want no message yet", n)
723 }
724
725 time.Sleep(time.Second)
726 synctest.Wait()
727 if _, err := reqStream.Read(respBody); err != nil {
728 t.Errorf("failed to read partial response from server, got err: %v", err)
729 }
730
731 time.Sleep(time.Second)
732 synctest.Wait()
733 if _, err := reqStream.Read(respBody); err != io.EOF {
734 t.Errorf("got err %v, want EOF", err)
735 }
736 reqStream.wantClosed("request is complete")
737 })
738 }
739
740 func TestServerHandlerStreaming(t *testing.T) {
741 synctest.Test(t, func(t *testing.T) {
742 stream := make(chan string)
743 ts := newTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
744
745
746 w.(http.Flusher).Flush()
747 for str := range stream {
748 w.Write([]byte(str))
749 w.(http.Flusher).Flush()
750 }
751 }))
752 tc := ts.connect()
753 tc.greet()
754
755 reqStream := tc.newStream(streamTypeRequest)
756 reqStream.writeHeaders(requestHeader(nil))
757 reqStream.wantSomeHeaders(http.Header{":status": {"200"}})
758
759 for _, data := range []string{"a", "bunch", "of", "things", "to", "stream"} {
760 stream <- data
761 reqStream.wantData([]byte(data))
762 }
763 close(stream)
764 reqStream.wantClosed("request is complete")
765 })
766 }
767
768 func TestServerHandlerDeclaresContentLength(t *testing.T) {
769 tests := []struct {
770 name string
771 contentLen string
772 actualContentLen int
773 wantWrittenLen int
774 wantTrimmed bool
775 wantCLHeader bool
776 }{
777 {
778 name: "accurate content length",
779 contentLen: "100",
780 actualContentLen: 100,
781 wantWrittenLen: 100,
782 wantCLHeader: true,
783 },
784 {
785 name: "larger content length",
786 contentLen: "100",
787 actualContentLen: 10,
788 wantWrittenLen: 10,
789 wantCLHeader: true,
790 },
791 {
792 name: "smaller content length",
793 contentLen: "10",
794 actualContentLen: 100,
795 wantWrittenLen: 10,
796 wantTrimmed: true,
797 wantCLHeader: true,
798 },
799 {
800 name: "non-numeric string",
801 contentLen: "intentional gibberish",
802 actualContentLen: 100,
803 wantWrittenLen: 100,
804 },
805 {
806 name: "negative number",
807 contentLen: "-10",
808 actualContentLen: 100,
809 wantWrittenLen: 100,
810 },
811 {
812 name: "plus sign",
813 contentLen: "+10",
814 actualContentLen: 100,
815 wantWrittenLen: 100,
816 },
817 {
818 name: "empty",
819 contentLen: "",
820 actualContentLen: 100,
821 wantWrittenLen: 100,
822 },
823 {
824 name: "large valid content length",
825 contentLen: "3000000000",
826 actualContentLen: 100,
827 wantWrittenLen: 100,
828 wantCLHeader: true,
829 },
830 {
831 name: "content length overflowing int64",
832 contentLen: "9223372036854775808",
833 actualContentLen: 100,
834 wantWrittenLen: 100,
835 },
836 }
837
838 for _, tt := range tests {
839 synctestSubtest(t, tt.name, func(t *testing.T) {
840 ts := newTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
841 w.Header().Set("Content-Length", tt.contentLen)
842 var written int
843 var lastErr error
844 for range tt.actualContentLen {
845 n, err := w.Write([]byte("a"))
846 written += n
847 lastErr = err
848 }
849 if tt.wantTrimmed != (lastErr != nil) {
850 t.Errorf("got %v error when writing response body, even though wantTrimmed is %v", lastErr, tt.wantTrimmed)
851 }
852 if written != tt.wantWrittenLen {
853 t.Errorf("got %v bytes written by the server, want %v bytes", written, tt.wantWrittenLen)
854 }
855 }))
856 tc := ts.connect()
857 tc.greet()
858
859 reqStream := tc.newStream(streamTypeRequest)
860 reqStream.writeHeaders(requestHeader(nil))
861 expectedHeaders := http.Header{
862 ":status": {"200"},
863 "Content-Type": {"text/plain; charset=utf-8"},
864 "Date": {"Sat, 01 Jan 2000 00:00:00 GMT"},
865 }
866 if tt.wantCLHeader {
867 expectedHeaders.Set("Content-Length", tt.contentLen)
868 }
869 reqStream.wantHeaders(expectedHeaders)
870 reqStream.wantData(slices.Repeat([]byte("a"), tt.wantWrittenLen))
871 reqStream.wantClosed("request is complete")
872 })
873 }
874 }
875
876 func TestServerExpect100Continue(t *testing.T) {
877 synctest.Test(t, func(t *testing.T) {
878 streamIdle := make(chan bool)
879 ts := newTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
880
881
882 if len(r.Header) > 0 {
883 t.Errorf("got %v, want request header to be empty", r.Header)
884 }
885
886 <-streamIdle
887 body, err := io.ReadAll(r.Body)
888 if err != nil {
889 t.Fatal(err)
890 }
891
892
893 w.Write(body)
894 }))
895 tc := ts.connect()
896 tc.greet()
897
898
899 reqStream := tc.newStream(streamTypeRequest)
900 reqStream.writeHeaders(requestHeader(http.Header{
901 "expect": {"100-continue"},
902 }))
903
904 reqStream.wantIdle("stream is idle until server sends an HTTP 100 status")
905 streamIdle <- true
906
907
908 reqStream.wantSomeHeaders(http.Header{":status": {"100"}})
909 body := []byte("body that will be echoed back if we get status 100")
910 reqStream.writeData(body)
911 reqStream.CloseWrite()
912
913
914 reqStream.wantSomeHeaders(http.Header{":status": {"200"}})
915 reqStream.wantData(body)
916 reqStream.wantClosed("request is complete")
917 })
918 }
919
920 func TestServerExpect100ContinueSentManually(t *testing.T) {
921 synctest.Test(t, func(t *testing.T) {
922 ts := newTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
923 w.WriteHeader(100)
924 body, err := io.ReadAll(r.Body)
925 if err != nil {
926 t.Fatal(err)
927 }
928 w.Write(body)
929 }))
930 tc := ts.connect()
931 tc.greet()
932
933
934 reqStream := tc.newStream(streamTypeRequest)
935 reqStream.writeHeaders(requestHeader(http.Header{
936 "expect": {"100-continue"},
937 }))
938
939
940 reqStream.wantSomeHeaders(http.Header{":status": {"100"}})
941 body := []byte("body that will be echoed back")
942 reqStream.writeData(body)
943 reqStream.CloseWrite()
944
945
946 reqStream.wantSomeHeaders(http.Header{":status": {"200"}})
947 reqStream.wantData(body)
948 reqStream.wantClosed("request is complete")
949 })
950 }
951
952 func TestServerExpect100ContinueRejected(t *testing.T) {
953 synctest.Test(t, func(t *testing.T) {
954 rejectBody := []byte("not allowed")
955 ts := newTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
956 w.WriteHeader(403)
957 w.Write(rejectBody)
958 }))
959 tc := ts.connect()
960 tc.greet()
961
962
963 reqStream := tc.newStream(streamTypeRequest)
964 reqStream.writeHeaders(requestHeader(http.Header{
965 "expect": {"100-continue"},
966 }))
967
968
969 reqStream.wantSomeHeaders(http.Header{":status": {"403"}})
970 reqStream.wantData(rejectBody)
971 reqStream.wantClosed("request is complete")
972 })
973 }
974
975 func TestServer100ContinueBodyReadAfterFinalResponse(t *testing.T) {
976 synctest.Test(t, func(t *testing.T) {
977 ts := newTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
978 w.WriteHeader(200)
979 w.(http.Flusher).Flush()
980
981
982
983 if _, err := io.ReadAll(r.Body); err == nil {
984 t.Errorf("got %v, want an error", err)
985 }
986 }))
987 tc := ts.connect()
988 tc.greet()
989
990
991 reqStream := tc.newStream(streamTypeRequest)
992 reqStream.writeHeaders(requestHeader(http.Header{
993 "expect": {"100-continue"},
994 }))
995
996
997 reqStream.wantSomeHeaders(http.Header{":status": {"200"}})
998 reqStream.wantClosed("request is complete")
999 })
1000 }
1001
1002 func TestServer100ContinueBodyReadAfter100AndFinalResponse(t *testing.T) {
1003 synctest.Test(t, func(t *testing.T) {
1004 body := []byte("client body")
1005 ts := newTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1006 w.WriteHeader(100)
1007 w.WriteHeader(200)
1008 w.(http.Flusher).Flush()
1009
1010 if gotBody, err := io.ReadAll(r.Body); err != nil || string(gotBody) != string(body) {
1011 t.Errorf("io.ReadAll(r.Body) = %v, %v; want %v, nil", gotBody, err, body)
1012 }
1013 }))
1014 tc := ts.connect()
1015 tc.greet()
1016
1017
1018 reqStream := tc.newStream(streamTypeRequest)
1019 reqStream.writeHeaders(requestHeader(http.Header{
1020 "expect": {"100-continue"},
1021 }))
1022
1023
1024 reqStream.wantSomeHeaders(http.Header{":status": {"100"}})
1025 reqStream.writeData(body)
1026 reqStream.CloseWrite()
1027 reqStream.wantSomeHeaders(http.Header{":status": {"200"}})
1028 reqStream.wantClosed("request is complete")
1029 })
1030 }
1031
1032 func TestServerHandlerReadReqWithNoBody(t *testing.T) {
1033 synctest.Test(t, func(t *testing.T) {
1034 serverBody := []byte("hello from server!")
1035 ts := newTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1036 if _, err := io.ReadAll(r.Body); err != nil {
1037 t.Errorf("got %v err when reading from an empty request body, want nil", err)
1038 }
1039 w.Write(serverBody)
1040 }))
1041 tc := ts.connect()
1042 tc.greet()
1043
1044
1045
1046 reqStream := tc.newStream(streamTypeRequest)
1047 reqStream.writeHeaders(requestHeader(nil))
1048 reqStream.CloseWrite()
1049 reqStream.wantSomeHeaders(http.Header{":status": {"200"}})
1050 reqStream.wantData(serverBody)
1051 reqStream.wantClosed("request is complete")
1052
1053
1054
1055 reqStream = tc.newStream(streamTypeRequest)
1056 reqStream.writeHeaders(requestHeader(http.Header{
1057 "content-length": {"0"},
1058 }))
1059 reqStream.wantSomeHeaders(http.Header{":status": {"200"}})
1060 reqStream.wantData(serverBody)
1061 reqStream.wantClosed("request is complete")
1062 })
1063 }
1064
1065 func TestServerHandlerReadTrailer(t *testing.T) {
1066 synctest.Test(t, func(t *testing.T) {
1067 body := []byte("some body")
1068 ts := newTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1069 wantTrailer := http.Header{
1070 "Client-Trailer-A": nil,
1071 "Client-Trailer-B": nil,
1072 }
1073 if !reflect.DeepEqual(r.Trailer, wantTrailer) {
1074 t.Errorf("got %v; want trailer to be %v before reading the body", r.Trailer, wantTrailer)
1075 }
1076 if _, err := io.ReadAll(r.Body); err != nil {
1077 t.Fatal(err)
1078 }
1079 wantTrailer = http.Header{
1080 "Client-Trailer-A": {"valuea"},
1081 "Client-Trailer-B": {"valueb"},
1082 }
1083 if !reflect.DeepEqual(r.Trailer, wantTrailer) {
1084 t.Errorf("got %v; want trailer to be %v after reading the body", r.Trailer, wantTrailer)
1085 }
1086 w.WriteHeader(200)
1087 }))
1088 tc := ts.connect()
1089 tc.greet()
1090
1091 reqStream := tc.newStream(streamTypeRequest)
1092 reqStream.writeHeaders(requestHeader(http.Header{
1093 "trailer": {"Client-Trailer-A, Client-Trailer-B"},
1094 }))
1095 reqStream.writeData(body)
1096 reqStream.writeHeaders(http.Header{
1097 "Client-Trailer-A": {"valuea"},
1098 "Client-Trailer-B": {"valueb"},
1099
1100 "Undeclared-Trailer": {"undeclared"},
1101 })
1102 reqStream.wantHeaders(nil)
1103 reqStream.wantClosed("request is complete")
1104 })
1105 }
1106
1107 func TestServerHandlerReadTrailerNoBody(t *testing.T) {
1108 synctest.Test(t, func(t *testing.T) {
1109 ts := newTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1110 wantTrailer := http.Header{
1111 "Client-Trailer-A": nil,
1112 "Client-Trailer-B": nil,
1113 }
1114 if !reflect.DeepEqual(r.Trailer, wantTrailer) {
1115 t.Errorf("got %v; want trailer to be %v before reading the body", r.Trailer, wantTrailer)
1116 }
1117 if _, err := io.ReadAll(r.Body); err != nil {
1118 t.Fatal(err)
1119 }
1120 wantTrailer = http.Header{
1121 "Client-Trailer-A": {"valuea"},
1122 "Client-Trailer-B": {"valueb"},
1123 }
1124 if !reflect.DeepEqual(r.Trailer, wantTrailer) {
1125 t.Errorf("got %v; want trailer to be %v after reading the body", r.Trailer, wantTrailer)
1126 }
1127 w.WriteHeader(200)
1128 }))
1129 tc := ts.connect()
1130 tc.greet()
1131
1132 reqStream := tc.newStream(streamTypeRequest)
1133 reqStream.writeHeaders(requestHeader(http.Header{
1134 "trailer": {"Client-Trailer-A, Client-Trailer-B"},
1135 "content-length": {"0"},
1136 }))
1137 reqStream.writeHeaders(http.Header{
1138 "Client-Trailer-A": {"valuea"},
1139 "Client-Trailer-B": {"valueb"},
1140
1141 "Undeclared-Trailer": {"undeclared"},
1142 })
1143 reqStream.wantHeaders(nil)
1144 reqStream.wantClosed("request is complete")
1145 })
1146 }
1147
1148 func TestServerHandlerWriteTrailer(t *testing.T) {
1149 synctest.Test(t, func(t *testing.T) {
1150 body := []byte("some body")
1151 ts := newTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1152 w.Header().Set("Trailer", "server-trailer-a, server-trailer-b")
1153 w.Header().Add("Trailer", "Server-Trailer-C")
1154
1155 w.Write(body)
1156
1157 w.Header().Set("server-trailer-a", "valuea")
1158 w.Header().Set("Server-Trailer-C", "valuec")
1159
1160
1161 w.Header().Set("Server-Trailer-Not-Declared", "should be omitted")
1162 w.Header().Set("Trailer:Undeclared-Trailer-Exception", "should be sent")
1163 }))
1164 tc := ts.connect()
1165 tc.greet()
1166
1167 reqStream := tc.newStream(streamTypeRequest)
1168 reqStream.writeHeaders(requestHeader(nil))
1169 reqStream.wantSomeHeaders(http.Header{
1170 ":status": {"200"},
1171 "Trailer": {"Server-Trailer-A, Server-Trailer-B, Server-Trailer-C"},
1172 })
1173 reqStream.wantData(body)
1174 reqStream.wantSomeHeaders(http.Header{
1175 "Server-Trailer-A": {"valuea"},
1176 "Server-Trailer-C": {"valuec"},
1177 "Undeclared-Trailer-Exception": {"should be sent"},
1178 })
1179 reqStream.wantClosed("request is complete")
1180 })
1181 }
1182
1183 func TestServerHandlerWriteTrailerNoBody(t *testing.T) {
1184 synctest.Test(t, func(t *testing.T) {
1185 ts := newTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1186 w.Header().Set("Trailer", "server-trailer-a, server-trailer-b")
1187 w.Header().Add("Trailer", "Server-Trailer-C")
1188
1189 w.(http.Flusher).Flush()
1190
1191 w.Header().Set("server-trailer-a", "valuea")
1192 w.Header().Set("Server-Trailer-C", "valuec")
1193
1194
1195 w.Header().Set("Server-Trailer-Not-Declared", "should be omitted")
1196 w.Header().Set("Trailer:undeclared-trailer-exception", "should be sent")
1197 }))
1198 tc := ts.connect()
1199 tc.greet()
1200
1201 reqStream := tc.newStream(streamTypeRequest)
1202 reqStream.writeHeaders(requestHeader(nil))
1203 reqStream.wantSomeHeaders(http.Header{
1204 ":status": {"200"},
1205 "Trailer": {"Server-Trailer-A, Server-Trailer-B, Server-Trailer-C"},
1206 })
1207 reqStream.wantSomeHeaders(http.Header{
1208 "Server-Trailer-A": {"valuea"},
1209 "Server-Trailer-C": {"valuec"},
1210 "Undeclared-Trailer-Exception": {"should be sent"},
1211 })
1212 reqStream.wantClosed("request is complete")
1213 })
1214 }
1215
1216 func TestServerInfersHeaders(t *testing.T) {
1217 tests := []struct {
1218 name string
1219 flushedEarly bool
1220 responseStatus int
1221 does100Continue bool
1222 declaredHeader http.Header
1223 want http.Header
1224 }{
1225 {
1226 name: "infers undeclared headers",
1227 responseStatus: 200,
1228 declaredHeader: http.Header{
1229 "Some-Other-Header": {"some value"},
1230 },
1231 want: http.Header{
1232 "Date": {"Sat, 01 Jan 2000 00:00:00 GMT"},
1233 "Content-Type": {"text/html; charset=utf-8"},
1234 "Some-Other-Header": {"some value"},
1235 },
1236 },
1237 {
1238 name: "does not write over declared header",
1239 responseStatus: 200,
1240 declaredHeader: http.Header{
1241 "Date": {"some date"},
1242 "Content-Type": {"some content type"},
1243 "Some-Other-Header": {"some value"},
1244 },
1245 want: http.Header{
1246 "Date": {"some date"},
1247 "Content-Type": {"some content type"},
1248 "Some-Other-Header": {"some value"},
1249 },
1250 },
1251 {
1252 name: "does not infer content type for response with no body",
1253 responseStatus: 304,
1254 declaredHeader: http.Header{
1255 "Some-Other-Header": {"some value"},
1256 },
1257 want: http.Header{
1258 "Date": {"Sat, 01 Jan 2000 00:00:00 GMT"},
1259 "Some-Other-Header": {"some value"},
1260 },
1261 },
1262 {
1263
1264 name: "does not infer content type for response with declared content encoding",
1265 responseStatus: 200,
1266 declaredHeader: http.Header{
1267 "Content-Encoding": {"some encoding"},
1268 "Some-Other-Header": {"some value"},
1269 },
1270 want: http.Header{
1271 "Date": {"Sat, 01 Jan 2000 00:00:00 GMT"},
1272 "Content-Encoding": {"some encoding"},
1273 "Some-Other-Header": {"some value"},
1274 },
1275 },
1276 {
1277 name: "infers content type for response with empty content encoding",
1278 responseStatus: 200,
1279 declaredHeader: http.Header{
1280 "Content-Encoding": {""},
1281 "Some-Other-Header": {"some value"},
1282 },
1283 want: http.Header{
1284 "Date": {"Sat, 01 Jan 2000 00:00:00 GMT"},
1285 "Content-Encoding": {""},
1286 "Content-Type": {"text/html; charset=utf-8"},
1287 "Some-Other-Header": {"some value"},
1288 },
1289 },
1290 {
1291 name: "does not infer content type when header is flushed before body is written",
1292 responseStatus: 200,
1293 flushedEarly: true,
1294 declaredHeader: http.Header{
1295 "Some-Other-Header": {"some value"},
1296 },
1297 want: http.Header{
1298 "Date": {"Sat, 01 Jan 2000 00:00:00 GMT"},
1299 "Some-Other-Header": {"some value"},
1300 },
1301 },
1302 {
1303 name: "infers header for the header that comes after 100 continue",
1304 responseStatus: 200,
1305 does100Continue: true,
1306 declaredHeader: http.Header{
1307 "Some-Other-Header": {"some value"},
1308 },
1309 want: http.Header{
1310 "Date": {"Sat, 01 Jan 2000 00:00:00 GMT"},
1311 "Content-Type": {"text/html; charset=utf-8"},
1312 "Some-Other-Header": {"some value"},
1313 },
1314 },
1315 }
1316
1317 for _, tt := range tests {
1318 synctestSubtest(t, tt.name, func(t *testing.T) {
1319 body := []byte("<html>some html content</html>")
1320 streamIdle := make(chan bool)
1321 ts := newTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1322 if tt.does100Continue {
1323 <-streamIdle
1324 io.ReadAll(r.Body)
1325 }
1326 for name, values := range tt.declaredHeader {
1327 for _, value := range values {
1328 w.Header().Add(name, value)
1329 }
1330 }
1331 w.WriteHeader(tt.responseStatus)
1332 if tt.flushedEarly {
1333 w.(http.Flusher).Flush()
1334 }
1335
1336
1337
1338 for _, b := range body {
1339 w.Write([]byte{b})
1340 }
1341 }))
1342 tc := ts.connect()
1343 tc.greet()
1344
1345 reqStream := tc.newStream(streamTypeRequest)
1346
1347 if tt.does100Continue {
1348 reqStream.writeHeaders(requestHeader(http.Header{
1349 "expect": {"100-continue"},
1350 }))
1351 reqStream.wantIdle("stream is idle until server sends an HTTP 100 status")
1352 streamIdle <- true
1353 reqStream.wantHeaders(http.Header{":status": {"100"}})
1354 }
1355
1356 reqStream.writeHeaders(requestHeader(nil))
1357 tt.want.Add(":status", strconv.Itoa(tt.responseStatus))
1358 reqStream.wantHeaders(tt.want)
1359 if responseCanHaveBody(tt.responseStatus) {
1360 reqStream.wantData(body)
1361 }
1362 reqStream.wantClosed("request is complete")
1363 })
1364 }
1365 }
1366
1367 func TestServerBuffersBodyWrite(t *testing.T) {
1368 tests := []struct {
1369 name string
1370 bodyLen int
1371 writeSize int
1372 flushes bool
1373 }{
1374 {
1375 name: "buffers small body content",
1376 bodyLen: defaultBodyBufferCap * 10,
1377 writeSize: 5,
1378 flushes: false,
1379 },
1380 {
1381 name: "does not buffer large body content",
1382 bodyLen: defaultBodyBufferCap * 10,
1383 writeSize: defaultBodyBufferCap * 2,
1384 flushes: false,
1385 },
1386 {
1387 name: "does not buffer flushed body content",
1388 bodyLen: defaultBodyBufferCap * 10,
1389 writeSize: 10,
1390 flushes: true,
1391 },
1392 }
1393 for _, tt := range tests {
1394 synctestSubtest(t, tt.name, func(t *testing.T) {
1395 ts := newTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1396 for n := 0; n < tt.bodyLen; n += tt.writeSize {
1397 data := slices.Repeat([]byte("a"), min(tt.writeSize, tt.bodyLen-n))
1398 n, err := w.Write(data)
1399 if err != nil {
1400 t.Fatal(err)
1401 }
1402 if n != len(data) {
1403 t.Errorf("got %v bytes when writing in server handler, want %v", n, len(data))
1404 }
1405 if tt.flushes {
1406 w.(http.Flusher).Flush()
1407 }
1408 }
1409 }))
1410 tc := ts.connect()
1411 tc.greet()
1412
1413 reqStream := tc.newStream(streamTypeRequest)
1414 reqStream.writeHeaders(requestHeader(nil))
1415 reqStream.wantHeaders(nil)
1416 switch {
1417 case tt.writeSize > defaultBodyBufferCap:
1418
1419
1420 for n := 0; n < tt.bodyLen; n += tt.writeSize {
1421 reqStream.wantData(slices.Repeat([]byte("a"), min(tt.writeSize, tt.bodyLen-n)))
1422 }
1423 case tt.flushes:
1424 for n := 0; n < tt.bodyLen; n += tt.writeSize {
1425 reqStream.wantData(slices.Repeat([]byte("a"), min(tt.writeSize, tt.bodyLen-n)))
1426 }
1427 case tt.writeSize <= defaultBodyBufferCap:
1428 dataLen := defaultBodyBufferCap + tt.writeSize - (defaultBodyBufferCap % tt.writeSize)
1429 for n := 0; n < tt.bodyLen; n += dataLen {
1430 reqStream.wantData(slices.Repeat([]byte("a"), min(dataLen, tt.bodyLen-n)))
1431 }
1432 }
1433 reqStream.wantClosed("request is complete")
1434 })
1435 }
1436 }
1437
1438 func TestServer103EarlyHints(t *testing.T) {
1439 synctest.Test(t, func(t *testing.T) {
1440 body := []byte("some body")
1441 ts := newTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1442 h := w.Header()
1443
1444 h.Add("Content-Length", "123")
1445 h.Add("Link", "</style.css>; rel=preload; as=style")
1446 h.Add("Link", "</script.js>; rel=preload; as=script")
1447 w.WriteHeader(http.StatusEarlyHints)
1448
1449 h.Add("Link", "</foo.js>; rel=preload; as=script")
1450 w.WriteHeader(http.StatusEarlyHints)
1451
1452 w.Write(body)
1453 w.WriteHeader(http.StatusEarlyHints)
1454 }))
1455 tc := ts.connect()
1456 tc.greet()
1457
1458 reqStream := tc.newStream(streamTypeRequest)
1459 reqStream.writeHeaders(requestHeader(nil))
1460 reqStream.wantHeaders(http.Header{
1461 ":status": {"103"},
1462 "Link": {
1463 "</style.css>; rel=preload; as=style",
1464 "</script.js>; rel=preload; as=script",
1465 },
1466 })
1467 reqStream.wantHeaders(http.Header{
1468 ":status": {"103"},
1469 "Link": {
1470 "</style.css>; rel=preload; as=style",
1471 "</script.js>; rel=preload; as=script",
1472 "</foo.js>; rel=preload; as=script",
1473 },
1474 })
1475 reqStream.wantSomeHeaders(http.Header{
1476 ":status": {"200"},
1477 "Content-Length": {"123"},
1478 })
1479 reqStream.wantData(body)
1480 reqStream.wantClosed("request is complete")
1481 })
1482 }
1483
1484 func TestServer304NotModified(t *testing.T) {
1485 synctest.Test(t, func(t *testing.T) {
1486 ts := newTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1487 w.WriteHeader(http.StatusNotModified)
1488 if _, err := w.Write([]byte("body should not be allowed")); !errors.Is(err, http.ErrBodyNotAllowed) {
1489 t.Errorf("got %v error when calling Write after WriteHeader(304), want %v error", err, http.ErrBodyNotAllowed)
1490 }
1491 }))
1492 tc := ts.connect()
1493 tc.greet()
1494
1495 reqStream := tc.newStream(streamTypeRequest)
1496 reqStream.writeHeaders(requestHeader(nil))
1497 reqStream.wantSomeHeaders(http.Header{":status": {"304"}})
1498 reqStream.wantClosed("request is complete")
1499 })
1500 }
1501
1502 func TestServerInvalidPathHeader(t *testing.T) {
1503 for _, test := range []struct {
1504 name string
1505 path string
1506 }{{
1507 name: "empty",
1508 path: "",
1509 }, {
1510 name: "invalid char",
1511 path: "\x00",
1512 }, {
1513 name: "absolute url",
1514 path: "https://example.com/",
1515 }} {
1516 synctestSubtest(t, test.name, func(t *testing.T) {
1517 ts := newTestServer(t, nil)
1518 tc := ts.connect()
1519 tc.greet()
1520
1521 reqStream := tc.newStream(streamTypeRequest)
1522 reqStream.writeHeaders(requestHeader(http.Header{
1523 ":path": []string{test.path},
1524 }))
1525 reqStream.wantError(quic.StreamError(errH3MessageError))
1526 })
1527 }
1528 }
1529
1530 func TestServerOptionsMethod(t *testing.T) {
1531 synctest.Test(t, func(t *testing.T) {
1532 ts := newTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
1533 tc := ts.connect()
1534 tc.greet()
1535
1536 reqStream := tc.newStream(streamTypeRequest)
1537 reqStream.writeHeaders(requestHeader(http.Header{
1538 ":method": []string{"OPTIONS"},
1539 ":path": []string{"*"},
1540 }))
1541 reqStream.wantSomeHeaders(http.Header{
1542 ":status": {"200"},
1543 })
1544 })
1545 }
1546
1547 func TestServerPastWriteDeadline(t *testing.T) {
1548 synctest.Test(t, func(t *testing.T) {
1549 ts := newTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1550 ctl := http.NewResponseController(w)
1551 io.WriteString(w, "one")
1552 if err := ctl.Flush(); err != nil {
1553 t.Errorf("Flush() = %v, want nil", err)
1554 }
1555 time.Sleep(time.Second)
1556
1557 if err := ctl.SetWriteDeadline(time.Now().Add(-10 * time.Second)); err != nil {
1558 t.Errorf("SetWriteDeadline() = %v, want nil", err)
1559 }
1560 var err error
1561 _, err = io.WriteString(w, "x")
1562 if err == nil {
1563 err = ctl.Flush()
1564 }
1565 if !errors.Is(err, os.ErrDeadlineExceeded) {
1566 t.Errorf("got write err %v, want %v", err, os.ErrDeadlineExceeded)
1567 }
1568
1569
1570 if err := ctl.SetWriteDeadline(time.Now().Add(10 * time.Second)); err != nil {
1571 t.Errorf("SetWriteDeadline() = %v, want nil", err)
1572 }
1573 _, err = io.WriteString(w, "x")
1574 if err == nil {
1575 err = ctl.Flush()
1576 }
1577 if !errors.Is(err, os.ErrDeadlineExceeded) {
1578 t.Errorf("got write err %v (after extend), want %v", err, os.ErrDeadlineExceeded)
1579 }
1580 }))
1581 tc := ts.connect()
1582 tc.greet()
1583
1584 reqStream := tc.newStream(streamTypeRequest)
1585 reqStream.writeHeaders(requestHeader(nil))
1586 reqStream.wantSomeHeaders(http.Header{":status": {"200"}})
1587 reqStream.wantData([]byte("one"))
1588 time.Sleep(2 * time.Second)
1589 synctest.Wait()
1590 reqStream.wantError(quic.StreamError(errH3RequestCancelled))
1591 })
1592 }
1593
1594 func TestServerFutureWriteDeadline(t *testing.T) {
1595 synctest.Test(t, func(t *testing.T) {
1596 ts := newTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1597 ctl := http.NewResponseController(w)
1598 io.WriteString(w, "one")
1599 if err := ctl.Flush(); err != nil {
1600 t.Errorf("Flush() = %v, want nil", err)
1601 }
1602
1603
1604 if err := ctl.SetWriteDeadline(time.Now().Add(time.Second)); err != nil {
1605 t.Errorf("SetWriteDeadline() = %v, want nil", err)
1606 }
1607 io.WriteString(w, "two")
1608 if err := ctl.Flush(); err != nil {
1609 t.Errorf("Flush() = %v, want nil", err)
1610 }
1611
1612
1613 if err := ctl.SetWriteDeadline(time.Now().Add(3 * time.Second)); err != nil {
1614 t.Errorf("SetWriteDeadline() = %v, want nil", err)
1615 }
1616
1617 time.Sleep(2 * time.Second)
1618 io.WriteString(w, "three")
1619 if err := ctl.Flush(); err != nil {
1620 t.Errorf("Flush() = %v, want nil", err)
1621 }
1622
1623
1624 time.Sleep(2 * time.Second)
1625 var err error
1626 _, err = io.WriteString(w, "x")
1627 if err == nil {
1628 err = ctl.Flush()
1629 }
1630 if !errors.Is(err, os.ErrDeadlineExceeded) {
1631 t.Errorf("got write err %v, want %v", err, os.ErrDeadlineExceeded)
1632 }
1633
1634
1635 if err := ctl.SetWriteDeadline(time.Time{}); err != nil {
1636 t.Errorf("SetWriteDeadline() = %v, want nil", err)
1637 }
1638 _, err = io.WriteString(w, "x")
1639 if err == nil {
1640 err = ctl.Flush()
1641 }
1642 if !errors.Is(err, os.ErrDeadlineExceeded) {
1643 t.Errorf("got write err %v (after extend), want %v", err, os.ErrDeadlineExceeded)
1644 }
1645 }))
1646 tc := ts.connect()
1647 tc.greet()
1648
1649 reqStream := tc.newStream(streamTypeRequest)
1650 reqStream.writeHeaders(requestHeader(nil))
1651 reqStream.wantSomeHeaders(http.Header{":status": {"200"}})
1652 reqStream.wantData([]byte("one"))
1653 reqStream.wantData([]byte("two"))
1654 time.Sleep(3 * time.Second)
1655 reqStream.wantData([]byte("three"))
1656 time.Sleep(3 * time.Second)
1657 reqStream.wantError(quic.StreamError(errH3RequestCancelled))
1658 })
1659 }
1660
1661 func TestServerPastReadDeadline(t *testing.T) {
1662 synctest.Test(t, func(t *testing.T) {
1663 ts := newTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1664 ctl := http.NewResponseController(w)
1665 b := make([]byte, 3)
1666 if _, err := io.ReadFull(r.Body, b); err != nil || string(b) != "one" {
1667 t.Errorf("Read() got (%q, %v), want (%q, nil)", b, err, "one")
1668 }
1669
1670 if err := ctl.SetReadDeadline(time.Now().Add(-10 * time.Second)); err != nil {
1671 t.Errorf("SetReadDeadline() = %v, want nil", err)
1672 }
1673 _, err := io.ReadAll(r.Body)
1674 if !errors.Is(err, os.ErrDeadlineExceeded) {
1675 t.Errorf("got read err %v, want %v", err, os.ErrDeadlineExceeded)
1676 }
1677
1678
1679 if err := ctl.SetReadDeadline(time.Now().Add(10 * time.Second)); err != nil {
1680 t.Errorf("SetReadDeadline() = %v, want nil", err)
1681 }
1682 _, err = io.ReadAll(r.Body)
1683 if !errors.Is(err, os.ErrDeadlineExceeded) {
1684 t.Errorf("got read err %v (after extend), want %v", err, os.ErrDeadlineExceeded)
1685 }
1686 }))
1687 tc := ts.connect()
1688 tc.greet()
1689
1690 reqStream := tc.newStream(streamTypeRequest)
1691 reqStream.writeHeaders(requestHeader(nil))
1692 reqStream.writeData([]byte("one"))
1693 synctest.Wait()
1694 })
1695 }
1696
1697 func TestServerFutureReadDeadline(t *testing.T) {
1698 synctest.Test(t, func(t *testing.T) {
1699 ts := newTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1700 ctl := http.NewResponseController(w)
1701 b := make([]byte, 3)
1702 if _, err := io.ReadFull(r.Body, b); err != nil || string(b) != "one" {
1703 t.Errorf("Read() got (%q, %v), want (%q, nil)", b, err, "one")
1704 }
1705
1706
1707 if err := ctl.SetReadDeadline(time.Now().Add(2 * time.Second)); err != nil {
1708 t.Errorf("SetReadDeadline() = %v, want nil", err)
1709 }
1710 b2 := make([]byte, 3)
1711 if _, err := io.ReadFull(r.Body, b2); err != nil || string(b2) != "two" {
1712 t.Errorf("Read() got (%q, %v), want (%q, nil)", b2, err, "two")
1713 }
1714
1715
1716 if err := ctl.SetReadDeadline(time.Now().Add(4 * time.Second)); err != nil {
1717 t.Errorf("SetReadDeadline() = %v, want nil", err)
1718 }
1719
1720 time.Sleep(2 * time.Second)
1721 b3 := make([]byte, 5)
1722 if _, err := io.ReadFull(r.Body, b3); err != nil || string(b3) != "three" {
1723 t.Errorf("Read() got (%q, %v), want (%q, nil)", b3, err, "three")
1724 }
1725
1726
1727 time.Sleep(3 * time.Second)
1728 _, err := io.ReadAll(r.Body)
1729 if !errors.Is(err, os.ErrDeadlineExceeded) {
1730 t.Errorf("got read err %v, want %v", err, os.ErrDeadlineExceeded)
1731 }
1732
1733
1734 if err := ctl.SetReadDeadline(time.Time{}); err != nil {
1735 t.Errorf("SetReadDeadline() = %v, want nil", err)
1736 }
1737 _, err = io.ReadAll(r.Body)
1738 if !errors.Is(err, os.ErrDeadlineExceeded) {
1739 t.Errorf("got read err %v (after extend), want %v", err, os.ErrDeadlineExceeded)
1740 }
1741 }))
1742 tc := ts.connect()
1743 tc.greet()
1744
1745 reqStream := tc.newStream(streamTypeRequest)
1746 reqStream.writeHeaders(requestHeader(nil))
1747 reqStream.writeData([]byte("one"))
1748
1749 time.Sleep(time.Second)
1750 reqStream.writeData([]byte("two"))
1751 synctest.Wait()
1752
1753 time.Sleep(time.Second)
1754 reqStream.writeData([]byte("three"))
1755 synctest.Wait()
1756
1757 time.Sleep(4 * time.Second)
1758 })
1759 }
1760
1761 func TestServerReadHeaderTimeout(t *testing.T) {
1762 synctest.Test(t, func(t *testing.T) {
1763 timeout := 10 * time.Second
1764 ts := newTestServer(t, nil)
1765 ts.s.srv1.ReadHeaderTimeout = timeout
1766 tc := ts.connect()
1767 tc.greet()
1768
1769
1770 reqStream := tc.newStream(streamTypeRequest)
1771 reqStream.writeVarint(int64(frameTypeHeaders))
1772 if err := reqStream.Flush(); err != nil {
1773 t.Fatalf("Flush() = %v, want nil", err)
1774 }
1775
1776
1777
1778 time.Sleep(timeout - 1)
1779 reqStream.wantIdle("timeout has not been reached")
1780 time.Sleep(1)
1781 reqStream.wantError(quic.StreamError(errH3RequestRejected))
1782 if tc.nextHandlerCall() != nil {
1783 t.Error("server handler should not be called")
1784 }
1785 })
1786 }
1787
1788 func TestServerReadTimeout(t *testing.T) {
1789 synctest.Test(t, func(t *testing.T) {
1790 timeout := 10 * time.Second
1791 ts := newTestServer(t, nil)
1792 ts.s.srv1.ReadTimeout = timeout
1793 tc := ts.connect()
1794 tc.greet()
1795
1796 reqStream := tc.newStream(streamTypeRequest)
1797 reqStream.writeHeaders(requestHeader(nil))
1798 reqStream.writeData([]byte("some body"))
1799 call := tc.nextHandlerCall()
1800
1801
1802 time.Sleep(timeout - 1)
1803 synctest.Wait()
1804 if _, err := call.req.Body.Read(make([]byte, 1)); err != nil {
1805 t.Errorf("Read() before timeout = %v, want nil", err)
1806 }
1807
1808
1809
1810
1811
1812 time.Sleep(1)
1813 synctest.Wait()
1814 if _, err := call.req.Body.Read(make([]byte, 1)); !errors.Is(err, os.ErrDeadlineExceeded) {
1815 t.Errorf("Read() after timeout = %v, want os.ErrDeadlineExceeded", err)
1816 }
1817 call.w.Write([]byte("some body"))
1818 call.exit()
1819 reqStream.wantSomeHeaders(http.Header{":status": {"200"}})
1820 reqStream.wantData([]byte("some body"))
1821 reqStream.wantClosed("clean close expected")
1822 })
1823 }
1824
1825 func TestServerReadTimeoutInProgress(t *testing.T) {
1826 synctest.Test(t, func(t *testing.T) {
1827 timeout := 10 * time.Second
1828 ts := newTestServer(t, nil)
1829 ts.s.srv1.ReadTimeout = timeout
1830 tc := ts.connect()
1831 tc.greet()
1832
1833 reqStream := tc.newStream(streamTypeRequest)
1834 reqStream.writeHeaders(requestHeader(nil))
1835 reqStream.Flush()
1836 call := tc.nextHandlerCall()
1837
1838
1839
1840 start := time.Now()
1841 _, err := call.req.Body.Read(make([]byte, 1))
1842 if !errors.Is(err, os.ErrDeadlineExceeded) {
1843 t.Errorf("Read error = %v, want os.ErrDeadlineExceeded", err)
1844 }
1845 if got, want := time.Since(start), timeout; got != want {
1846 t.Errorf("Read blocked for %v, want %v", got, want)
1847 }
1848 call.exit()
1849 })
1850 }
1851
1852 func TestServerWriteTimeout(t *testing.T) {
1853 synctest.Test(t, func(t *testing.T) {
1854 timeout := 10 * time.Second
1855 ts := newTestServer(t, nil)
1856 ts.s.srv1.WriteTimeout = timeout
1857 tc := ts.connect()
1858 tc.greet()
1859
1860 reqStream := tc.newStream(streamTypeRequest)
1861 reqStream.writeHeaders(requestHeader(nil))
1862 call := tc.nextHandlerCall()
1863 body := make([]byte, defaultBodyBufferCap+1)
1864
1865
1866 time.Sleep(timeout - 1)
1867 synctest.Wait()
1868 if _, err := call.w.Write(body); err != nil {
1869 t.Errorf("Write() before timeout = %v, want nil", err)
1870 }
1871 call.w.(http.Flusher).Flush()
1872 reqStream.wantSomeHeaders(http.Header{":status": {"200"}})
1873 reqStream.wantData(body)
1874 reqStream.wantIdle("timeout has not been reached")
1875
1876
1877
1878 time.Sleep(1)
1879 synctest.Wait()
1880 if _, err := call.w.Write(body); !errors.Is(err, os.ErrDeadlineExceeded) {
1881 t.Errorf("Write() after timeout = %v, want os.ErrDeadlineExceeded", err)
1882 }
1883 call.exit()
1884 reqStream.wantError(quic.StreamError(errH3RequestCancelled))
1885 })
1886 }
1887
1888 func TestServerWriteTimeoutInProgress(t *testing.T) {
1889 synctest.Test(t, func(t *testing.T) {
1890 timeout := 10 * time.Second
1891 ts := newTestServer(t, nil)
1892 ts.s.srv1.WriteTimeout = timeout
1893 tc := ts.connect()
1894 tc.greet()
1895
1896 reqStream := tc.newStream(streamTypeRequest)
1897 reqStream.writeHeaders(requestHeader(nil))
1898 reqStream.Flush()
1899 call := tc.nextHandlerCall()
1900
1901
1902
1903 start := time.Now()
1904 var err error
1905 for err == nil {
1906 _, err = call.w.Write([]byte("endless body"))
1907 }
1908 if !errors.Is(err, os.ErrDeadlineExceeded) {
1909 t.Errorf("Write error = %v, want os.ErrDeadlineExceeded", err)
1910 }
1911 if got, want := time.Since(start), timeout; got != want {
1912 t.Errorf("Write blocked for %v, want %v", got, want)
1913 }
1914 call.exit()
1915 })
1916 }
1917
1918 type testServer struct {
1919 t testing.TB
1920 s *server
1921 tn testNet
1922 testHandler *testServerHandler
1923 *testQUICEndpoint
1924
1925 addr netip.AddrPort
1926 }
1927
1928 type testQUICEndpoint struct {
1929 t testing.TB
1930 e *quic.Endpoint
1931 }
1932
1933 type testServerConn struct {
1934 ts *testServer
1935
1936 *testQUICConn
1937 control *testQUICStream
1938 localAddr netip.AddrPort
1939 }
1940
1941 type testServerHandler struct {
1942 ts *testServer
1943 callsMu sync.Mutex
1944 calls []*serverHandlerCall
1945 }
1946
1947
1948 type serverHandlerCall struct {
1949 w http.ResponseWriter
1950 req *http.Request
1951 closeOnce sync.Once
1952 ch chan func()
1953 }
1954
1955 func newTestServer(t testing.TB, handler http.Handler) *testServer {
1956 t.Helper()
1957 ts := &testServer{
1958 t: t,
1959 }
1960 if handler == nil {
1961 ts.testHandler = &testServerHandler{
1962 ts: ts,
1963 calls: []*serverHandlerCall{},
1964 }
1965 handler = ts.testHandler
1966 }
1967 ts.s = &server{
1968 srv1: &http.Server{},
1969 }
1970 e := ts.tn.newQUICEndpoint(t, &quic.Config{
1971 TLSConfig: testTLSConfig,
1972 })
1973 ts.addr = e.LocalAddr()
1974 go ts.s.serve(t.Context(), e, handler)
1975 return ts
1976 }
1977
1978 func (ts *testServer) connect() *testServerConn {
1979 ts.t.Helper()
1980 config := &quic.Config{TLSConfig: testTLSConfig}
1981 e := ts.tn.newQUICEndpoint(ts.t, nil)
1982 qconn, err := e.Dial(ts.t.Context(), "udp", ts.addr.String(), config)
1983 if err != nil {
1984 ts.t.Fatal(err)
1985 }
1986 tc := &testServerConn{
1987 ts: ts,
1988 testQUICConn: newTestQUICConn(ts.t, qconn),
1989 localAddr: e.LocalAddr(),
1990 }
1991 synctest.Wait()
1992 return tc
1993 }
1994
1995
1996 func (tc *testServerConn) greet() {
1997
1998 tc.control = tc.newStream(streamTypeControl)
1999 tc.control.writeVarint(int64(frameTypeSettings))
2000 tc.control.writeVarint(0)
2001 tc.control.Flush()
2002 synctest.Wait()
2003 }
2004
2005
2006
2007 func (tc *testServerConn) nextHandlerCall() *serverHandlerCall {
2008 h := tc.ts.testHandler
2009 if h == nil {
2010 tc.t.Fatal("nextHandlerCall is called for a testServer with non-nil handler")
2011 }
2012 tc.t.Helper()
2013 synctest.Wait()
2014 h.callsMu.Lock()
2015 defer h.callsMu.Unlock()
2016 for i, call := range h.calls {
2017 if call.req.RemoteAddr == tc.localAddr.String() {
2018 h.calls = append(h.calls[:i], h.calls[i+1:]...)
2019 return call
2020 }
2021 }
2022 return nil
2023 }
2024
2025 func (h *testServerHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
2026 call := &serverHandlerCall{
2027 w: w,
2028 req: req,
2029 ch: make(chan func()),
2030 }
2031 h.ts.t.Cleanup(call.exit)
2032 h.callsMu.Lock()
2033 h.calls = append(h.calls, call)
2034 h.callsMu.Unlock()
2035 for f := range call.ch {
2036 f()
2037 }
2038 }
2039
2040
2041 func (call *serverHandlerCall) do(f func(http.ResponseWriter, *http.Request)) {
2042 donec := make(chan struct{})
2043 call.ch <- func() {
2044 defer close(donec)
2045 f(call.w, call.req)
2046 }
2047 <-donec
2048 }
2049
2050
2051 func (call *serverHandlerCall) exit() {
2052 call.closeOnce.Do(func() {
2053 close(call.ch)
2054 })
2055 }
2056
View as plain text