github.com/metacubex/quic-go@v0.44.1-0.20240520163451-20b689a59136/http3/http_stream_test.go (about) 1 package http3 2 3 import ( 4 "bytes" 5 "io" 6 "math" 7 "net/http" 8 9 mockquic "github.com/metacubex/quic-go/internal/mocks/quic" 10 "github.com/metacubex/quic-go/internal/protocol" 11 "github.com/metacubex/quic-go/internal/qerr" 12 13 "github.com/quic-go/qpack" 14 15 . "github.com/onsi/ginkgo/v2" 16 . "github.com/onsi/gomega" 17 "go.uber.org/mock/gomock" 18 ) 19 20 func getDataFrame(data []byte) []byte { 21 b := (&dataFrame{Length: uint64(len(data))}).Append(nil) 22 return append(b, data...) 23 } 24 25 var _ = Describe("Stream", func() { 26 Context("reading", func() { 27 var ( 28 str Stream 29 qstr *mockquic.MockStream 30 buf *bytes.Buffer 31 errorCbCalled bool 32 ) 33 34 BeforeEach(func() { 35 buf = &bytes.Buffer{} 36 errorCbCalled = false 37 qstr = mockquic.NewMockStream(mockCtrl) 38 qstr.EXPECT().Write(gomock.Any()).DoAndReturn(buf.Write).AnyTimes() 39 qstr.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes() 40 conn := mockquic.NewMockEarlyConnection(mockCtrl) 41 conn.EXPECT().CloseWithError(gomock.Any(), gomock.Any()).Do(func(qerr.ApplicationErrorCode, string) error { 42 errorCbCalled = true 43 return nil 44 }).AnyTimes() 45 str = newStream(qstr, newConnection(conn, false, protocol.PerspectiveClient, nil), nil) 46 }) 47 48 It("reads DATA frames in a single run", func() { 49 buf.Write(getDataFrame([]byte("foobar"))) 50 b := make([]byte, 6) 51 n, err := str.Read(b) 52 Expect(err).ToNot(HaveOccurred()) 53 Expect(n).To(Equal(6)) 54 Expect(b).To(Equal([]byte("foobar"))) 55 }) 56 57 It("reads DATA frames in multiple runs", func() { 58 buf.Write(getDataFrame([]byte("foobar"))) 59 b := make([]byte, 3) 60 n, err := str.Read(b) 61 Expect(err).ToNot(HaveOccurred()) 62 Expect(n).To(Equal(3)) 63 Expect(b).To(Equal([]byte("foo"))) 64 n, err = str.Read(b) 65 Expect(err).ToNot(HaveOccurred()) 66 Expect(n).To(Equal(3)) 67 Expect(b).To(Equal([]byte("bar"))) 68 }) 69 70 It("reads DATA frames into too large buffers", func() { 71 buf.Write(getDataFrame([]byte("foobar"))) 72 b := make([]byte, 10) 73 n, err := str.Read(b) 74 Expect(err).ToNot(HaveOccurred()) 75 Expect(n).To(Equal(6)) 76 Expect(b[:n]).To(Equal([]byte("foobar"))) 77 }) 78 79 It("reads DATA frames into too large buffers, in multiple runs", func() { 80 buf.Write(getDataFrame([]byte("foobar"))) 81 b := make([]byte, 4) 82 n, err := str.Read(b) 83 Expect(err).ToNot(HaveOccurred()) 84 Expect(n).To(Equal(4)) 85 Expect(b).To(Equal([]byte("foob"))) 86 n, err = str.Read(b) 87 Expect(err).ToNot(HaveOccurred()) 88 Expect(n).To(Equal(2)) 89 Expect(b[:n]).To(Equal([]byte("ar"))) 90 }) 91 92 It("reads multiple DATA frames", func() { 93 buf.Write(getDataFrame([]byte("foo"))) 94 buf.Write(getDataFrame([]byte("bar"))) 95 b := make([]byte, 6) 96 n, err := str.Read(b) 97 Expect(err).ToNot(HaveOccurred()) 98 Expect(n).To(Equal(3)) 99 Expect(b[:n]).To(Equal([]byte("foo"))) 100 n, err = str.Read(b) 101 Expect(err).ToNot(HaveOccurred()) 102 Expect(n).To(Equal(3)) 103 Expect(b[:n]).To(Equal([]byte("bar"))) 104 }) 105 106 It("skips HEADERS frames", func() { 107 b := getDataFrame([]byte("foo")) 108 b = (&headersFrame{Length: 10}).Append(b) 109 b = append(b, make([]byte, 10)...) 110 b = append(b, getDataFrame([]byte("bar"))...) 111 buf.Write(b) 112 r := make([]byte, 6) 113 n, err := io.ReadFull(str, r) 114 Expect(err).ToNot(HaveOccurred()) 115 Expect(n).To(Equal(6)) 116 Expect(r).To(Equal([]byte("foobar"))) 117 }) 118 119 It("errors when it can't parse the frame", func() { 120 buf.Write([]byte("invalid")) 121 _, err := str.Read([]byte{0}) 122 Expect(err).To(HaveOccurred()) 123 }) 124 125 It("errors on unexpected frames, and calls the error callback", func() { 126 b := (&settingsFrame{}).Append(nil) 127 buf.Write(b) 128 _, err := str.Read([]byte{0}) 129 Expect(err).To(MatchError("peer sent an unexpected frame: *http3.settingsFrame")) 130 Expect(errorCbCalled).To(BeTrue()) 131 }) 132 }) 133 134 Context("writing", func() { 135 It("writes data frames", func() { 136 buf := &bytes.Buffer{} 137 qstr := mockquic.NewMockStream(mockCtrl) 138 qstr.EXPECT().Write(gomock.Any()).DoAndReturn(buf.Write).AnyTimes() 139 str := newStream(qstr, nil, nil) 140 str.Write([]byte("foo")) 141 str.Write([]byte("foobar")) 142 143 fp := frameParser{r: buf} 144 f, err := fp.ParseNext() 145 Expect(err).ToNot(HaveOccurred()) 146 Expect(f).To(Equal(&dataFrame{Length: 3})) 147 b := make([]byte, 3) 148 _, err = io.ReadFull(buf, b) 149 Expect(err).ToNot(HaveOccurred()) 150 Expect(b).To(Equal([]byte("foo"))) 151 152 fp = frameParser{r: buf} 153 f, err = fp.ParseNext() 154 Expect(err).ToNot(HaveOccurred()) 155 Expect(f).To(Equal(&dataFrame{Length: 6})) 156 b = make([]byte, 6) 157 _, err = io.ReadFull(buf, b) 158 Expect(err).ToNot(HaveOccurred()) 159 Expect(b).To(Equal([]byte("foobar"))) 160 }) 161 }) 162 }) 163 164 var _ = Describe("Request Stream", func() { 165 var str *requestStream 166 var qstr *mockquic.MockStream 167 168 BeforeEach(func() { 169 qstr = mockquic.NewMockStream(mockCtrl) 170 requestWriter := newRequestWriter() 171 conn := mockquic.NewMockEarlyConnection(mockCtrl) 172 str = newRequestStream( 173 newStream(qstr, newConnection(conn, false, protocol.PerspectiveClient, nil), nil), 174 requestWriter, 175 make(chan struct{}), 176 qpack.NewDecoder(func(qpack.HeaderField) {}), 177 true, 178 math.MaxUint64, 179 ) 180 }) 181 182 It("refuses to read before having read the response", func() { 183 _, err := str.Read(make([]byte, 100)) 184 Expect(err).To(MatchError("http3: invalid use of RequestStream.Read: need to call ReadResponse first")) 185 }) 186 187 It("prevents duplicate calls to SendRequestHeader", func() { 188 req, err := http.NewRequest(http.MethodGet, "https://quic-go.net", nil) 189 Expect(err).ToNot(HaveOccurred()) 190 qstr.EXPECT().Write(gomock.Any()).AnyTimes() 191 Expect(str.SendRequestHeader(req)).To(Succeed()) 192 Expect(str.SendRequestHeader(req)).To(MatchError("http3: invalid duplicate use of SendRequestHeader")) 193 }) 194 195 It("reads after the response", func() { 196 req, err := http.NewRequest(http.MethodGet, "https://quic-go.net", nil) 197 Expect(err).ToNot(HaveOccurred()) 198 qstr.EXPECT().Write(gomock.Any()).AnyTimes() 199 Expect(str.SendRequestHeader(req)).To(Succeed()) 200 201 buf := bytes.NewBuffer(encodeResponse(200)) 202 buf.Write((&dataFrame{Length: 6}).Append(nil)) 203 buf.Write([]byte("foobar")) 204 qstr.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes() 205 rsp, err := str.ReadResponse() 206 Expect(err).ToNot(HaveOccurred()) 207 Expect(rsp.StatusCode).To(Equal(200)) 208 b := make([]byte, 10) 209 n, err := str.Read(b) 210 Expect(err).ToNot(HaveOccurred()) 211 Expect(n).To(Equal(6)) 212 Expect(b[:n]).To(Equal([]byte("foobar"))) 213 }) 214 })