github.com/apernet/quic-go@v0.43.1-0.20240515053213-5e9e635fd9f0/http3/request_writer_test.go (about) 1 package http3 2 3 import ( 4 "bytes" 5 "io" 6 "net/http" 7 8 mockquic "github.com/apernet/quic-go/internal/mocks/quic" 9 10 "github.com/quic-go/qpack" 11 "go.uber.org/mock/gomock" 12 13 . "github.com/onsi/ginkgo/v2" 14 . "github.com/onsi/gomega" 15 ) 16 17 var _ = Describe("Request Writer", func() { 18 var ( 19 rw *requestWriter 20 str *mockquic.MockStream 21 strBuf *bytes.Buffer 22 ) 23 24 decode := func(str io.Reader) map[string]string { 25 frame, err := parseNextFrame(str, nil) 26 ExpectWithOffset(1, err).ToNot(HaveOccurred()) 27 ExpectWithOffset(1, frame).To(BeAssignableToTypeOf(&headersFrame{})) 28 headersFrame := frame.(*headersFrame) 29 data := make([]byte, headersFrame.Length) 30 _, err = io.ReadFull(str, data) 31 ExpectWithOffset(1, err).ToNot(HaveOccurred()) 32 decoder := qpack.NewDecoder(nil) 33 hfs, err := decoder.DecodeFull(data) 34 ExpectWithOffset(1, err).ToNot(HaveOccurred()) 35 values := make(map[string]string) 36 for _, hf := range hfs { 37 values[hf.Name] = hf.Value 38 } 39 return values 40 } 41 42 BeforeEach(func() { 43 rw = newRequestWriter() 44 strBuf = &bytes.Buffer{} 45 str = mockquic.NewMockStream(mockCtrl) 46 str.EXPECT().Write(gomock.Any()).DoAndReturn(strBuf.Write).AnyTimes() 47 }) 48 49 It("writes a GET request", func() { 50 req, err := http.NewRequest(http.MethodGet, "https://quic.clemente.io/index.html?foo=bar", nil) 51 Expect(err).ToNot(HaveOccurred()) 52 Expect(rw.WriteRequestHeader(str, req, false)).To(Succeed()) 53 headerFields := decode(strBuf) 54 Expect(headerFields).To(HaveKeyWithValue(":authority", "quic.clemente.io")) 55 Expect(headerFields).To(HaveKeyWithValue(":method", "GET")) 56 Expect(headerFields).To(HaveKeyWithValue(":path", "/index.html?foo=bar")) 57 Expect(headerFields).To(HaveKeyWithValue(":scheme", "https")) 58 Expect(headerFields).ToNot(HaveKey("accept-encoding")) 59 }) 60 61 It("rejects invalid host headers", func() { 62 req, err := http.NewRequest(http.MethodGet, "https://quic.clemente.io/index.html?foo=bar", nil) 63 Expect(err).ToNot(HaveOccurred()) 64 req.Host = "foo@bar" // @ is invalid 65 Expect(rw.WriteRequestHeader(str, req, false)).To(MatchError("http3: invalid Host header")) 66 }) 67 68 It("sends cookies", func() { 69 req, err := http.NewRequest(http.MethodGet, "https://quic.clemente.io/", nil) 70 Expect(err).ToNot(HaveOccurred()) 71 cookie1 := &http.Cookie{ 72 Name: "Cookie #1", 73 Value: "Value #1", 74 } 75 cookie2 := &http.Cookie{ 76 Name: "Cookie #2", 77 Value: "Value #2", 78 } 79 req.AddCookie(cookie1) 80 req.AddCookie(cookie2) 81 Expect(rw.WriteRequestHeader(str, req, false)).To(Succeed()) 82 headerFields := decode(strBuf) 83 Expect(headerFields).To(HaveKeyWithValue("cookie", `Cookie #1="Value #1"; Cookie #2="Value #2"`)) 84 }) 85 86 It("adds the header for gzip support", func() { 87 req, err := http.NewRequest(http.MethodGet, "https://quic.clemente.io/", nil) 88 Expect(err).ToNot(HaveOccurred()) 89 Expect(rw.WriteRequestHeader(str, req, true)).To(Succeed()) 90 headerFields := decode(strBuf) 91 Expect(headerFields).To(HaveKeyWithValue("accept-encoding", "gzip")) 92 }) 93 94 It("writes a CONNECT request", func() { 95 req, err := http.NewRequest(http.MethodConnect, "https://quic.clemente.io/", nil) 96 Expect(err).ToNot(HaveOccurred()) 97 Expect(rw.WriteRequestHeader(str, req, false)).To(Succeed()) 98 headerFields := decode(strBuf) 99 Expect(headerFields).To(HaveKeyWithValue(":method", "CONNECT")) 100 Expect(headerFields).To(HaveKeyWithValue(":authority", "quic.clemente.io")) 101 Expect(headerFields).ToNot(HaveKey(":path")) 102 Expect(headerFields).ToNot(HaveKey(":scheme")) 103 Expect(headerFields).ToNot(HaveKey(":protocol")) 104 }) 105 106 It("writes an Extended CONNECT request", func() { 107 req, err := http.NewRequest(http.MethodConnect, "https://quic.clemente.io/foobar", nil) 108 Expect(err).ToNot(HaveOccurred()) 109 req.Proto = "webtransport" 110 Expect(rw.WriteRequestHeader(str, req, false)).To(Succeed()) 111 headerFields := decode(strBuf) 112 Expect(headerFields).To(HaveKeyWithValue(":authority", "quic.clemente.io")) 113 Expect(headerFields).To(HaveKeyWithValue(":method", "CONNECT")) 114 Expect(headerFields).To(HaveKeyWithValue(":path", "/foobar")) 115 Expect(headerFields).To(HaveKeyWithValue(":scheme", "https")) 116 Expect(headerFields).To(HaveKeyWithValue(":protocol", "webtransport")) 117 }) 118 })