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