1
2
3
4
5 package http3
6
7 import (
8 "bytes"
9 "errors"
10 "fmt"
11 "io"
12 "net"
13 "net/http"
14 "testing"
15 )
16
17
18 func TestReadData(t *testing.T) {
19
20
21
22 type (
23
24 receiveHeaders struct {
25 contentLength int64
26 }
27
28 receiveDataHeader struct {
29 size int64
30 }
31
32 receiveData struct {
33 size int64
34 }
35
36 receiveTrailers struct{}
37
38 receiveFrame struct {
39 ftype frameType
40 data []byte
41 }
42
43 receiveEOF struct{}
44
45 wantBody struct {
46 size int64
47 eof bool
48 }
49
50 wantError struct {
51
52 err error
53 }
54
55 closeBody struct{}
56 )
57 for _, test := range []struct {
58 name string
59 respHeader http.Header
60 steps []any
61 wantError bool
62 }{{
63 name: "no content length",
64 steps: []any{
65 receiveHeaders{contentLength: -1},
66 receiveDataHeader{size: 10},
67 receiveData{size: 10},
68 receiveEOF{},
69 wantBody{size: 10, eof: true},
70 },
71 }, {
72 name: "valid content length",
73 steps: []any{
74 receiveHeaders{contentLength: 10},
75 receiveDataHeader{size: 10},
76 receiveData{size: 10},
77 receiveEOF{},
78 wantBody{size: 10, eof: true},
79 },
80 }, {
81 name: "data frame exceeds content length",
82 steps: []any{
83 receiveHeaders{contentLength: 5},
84 receiveDataHeader{size: 10},
85 receiveData{size: 10},
86 wantError{},
87 },
88 }, {
89 name: "data frame after all content read",
90 steps: []any{
91 receiveHeaders{contentLength: 5},
92 receiveDataHeader{size: 5},
93 receiveData{size: 5},
94 wantBody{size: 5},
95 receiveDataHeader{size: 1},
96 receiveData{size: 1},
97 wantError{},
98 },
99 }, {
100 name: "content length too long",
101 steps: []any{
102 receiveHeaders{contentLength: 10},
103 receiveDataHeader{size: 5},
104 receiveData{size: 5},
105 receiveEOF{},
106 wantBody{size: 5},
107 wantError{},
108 },
109 }, {
110 name: "stream ended by trailers",
111 steps: []any{
112 receiveHeaders{contentLength: -1},
113 receiveDataHeader{size: 5},
114 receiveData{size: 5},
115 receiveTrailers{},
116 wantBody{size: 5, eof: true},
117 },
118 }, {
119 name: "trailers and content length too long",
120 steps: []any{
121 receiveHeaders{contentLength: 10},
122 receiveDataHeader{size: 5},
123 receiveData{size: 5},
124 wantBody{size: 5},
125 receiveTrailers{},
126 wantError{},
127 },
128 }, {
129 name: "unknown frame before headers",
130 steps: []any{
131 receiveFrame{
132 ftype: 0x1f + 0x21,
133 data: []byte{1, 2, 3, 4},
134 },
135 receiveHeaders{contentLength: -1},
136 receiveDataHeader{size: 10},
137 receiveData{size: 10},
138 wantBody{size: 10},
139 },
140 }, {
141 name: "unknown frame after headers",
142 steps: []any{
143 receiveHeaders{contentLength: -1},
144 receiveFrame{
145 ftype: 0x1f + 0x21,
146 data: []byte{1, 2, 3, 4},
147 },
148 receiveDataHeader{size: 10},
149 receiveData{size: 10},
150 wantBody{size: 10},
151 },
152 }, {
153 name: "invalid frame",
154 steps: []any{
155 receiveHeaders{contentLength: -1},
156 receiveFrame{
157 ftype: frameTypeSettings,
158 data: []byte{1, 2, 3, 4},
159 },
160 wantError{},
161 },
162 }, {
163 name: "data frame consumed by several reads",
164 steps: []any{
165 receiveHeaders{contentLength: -1},
166 receiveDataHeader{size: 16},
167 receiveData{size: 16},
168 wantBody{size: 2},
169 wantBody{size: 4},
170 wantBody{size: 8},
171 wantBody{size: 2},
172 },
173 }, {
174 name: "read multiple frames",
175 steps: []any{
176 receiveHeaders{contentLength: -1},
177 receiveDataHeader{size: 2},
178 receiveData{size: 2},
179 receiveDataHeader{size: 4},
180 receiveData{size: 4},
181 receiveDataHeader{size: 8},
182 receiveData{size: 8},
183 wantBody{size: 2},
184 wantBody{size: 4},
185 wantBody{size: 8},
186 },
187 }, {
188 name: "read after body close",
189 steps: []any{
190 receiveHeaders{contentLength: -1},
191 receiveDataHeader{size: 2},
192 receiveData{size: 2},
193 receiveDataHeader{size: 4},
194 receiveData{size: 4},
195 receiveDataHeader{size: 8},
196 receiveData{size: 8},
197 wantBody{size: 2},
198 wantBody{size: 4},
199 closeBody{},
200 wantError{err: net.ErrClosed},
201 },
202 }} {
203
204 runTest := func(t testing.TB, h http.Header, st *testQUICStream, body func() io.ReadCloser) {
205 var (
206 bytesSent int
207 bytesReceived int
208 )
209 for _, step := range test.steps {
210 switch step := step.(type) {
211 case receiveHeaders:
212 header := h.Clone()
213 if step.contentLength != -1 {
214 header["content-length"] = []string{
215 fmt.Sprint(step.contentLength),
216 }
217 }
218 st.writeHeaders(header)
219 case receiveDataHeader:
220 t.Logf("receive DATA frame header: size=%v", step.size)
221 st.writeVarint(int64(frameTypeData))
222 st.writeVarint(step.size)
223 st.Flush()
224 case receiveData:
225 t.Logf("receive DATA frame content: size=%v", step.size)
226 for range step.size {
227 st.WriteByte(byte(bytesSent))
228 bytesSent++
229 }
230 st.Flush()
231 case receiveTrailers:
232 st.writeHeaders(http.Header{
233 "x-trailer": []string{"trailer"},
234 })
235 case receiveFrame:
236 st.writeVarint(int64(step.ftype))
237 st.writeVarint(int64(len(step.data)))
238 st.Write(step.data)
239 st.Flush()
240 case receiveEOF:
241 t.Logf("receive EOF on request stream")
242 st.CloseWrite()
243 case wantBody:
244 t.Logf("read %v bytes from response body", step.size)
245 want := make([]byte, step.size)
246 for i := range want {
247 want[i] = byte(bytesReceived)
248 bytesReceived++
249 }
250 got := make([]byte, step.size)
251 n, err := body().Read(got)
252 got = got[:n]
253 if !bytes.Equal(got, want) {
254 t.Errorf("resp.Body.Read:")
255 t.Errorf(" got: {%x}", got)
256 t.Fatalf(" want: {%x}", want)
257 }
258 if err != nil {
259 if step.eof && err == io.EOF {
260 continue
261 }
262 t.Fatalf("resp.Body.Read: unexpected error %v", err)
263 }
264 if step.eof {
265 if n, err := body().Read([]byte{0}); n != 0 || err != io.EOF {
266 t.Fatalf("resp.Body.Read() = %v, %v; want io.EOF", n, err)
267 }
268 }
269 case wantError:
270 n, err := body().Read([]byte{0})
271 if n != 0 || err == nil || err == io.EOF {
272 t.Fatalf("resp.Body.Read() = %v, %v; want error", n, err)
273 }
274 if step.err != nil && !errors.Is(step.err, err) {
275 t.Fatalf("resp.Body.Read() = %v, %v; want %v error", n, err, step.err)
276 }
277 case closeBody:
278 if err := body().Close(); err != nil {
279 t.Fatalf("resp.Body.Close() = %v, want nil", err)
280 }
281 default:
282 t.Fatalf("unknown test step %T", step)
283 }
284 }
285
286 }
287
288 synctestSubtest(t, test.name+"/client", func(t *testing.T) {
289 tc := newTestClientConn(t)
290 tc.greet()
291
292 req, _ := http.NewRequest("GET", "https://example.tld/", nil)
293 rt := tc.roundTrip(req)
294 st := tc.wantStream(streamTypeRequest)
295 st.wantHeaders(nil)
296
297 header := http.Header{
298 ":status": []string{"200"},
299 }
300 runTest(t, header, st, func() io.ReadCloser {
301 return rt.response().Body
302 })
303 })
304 }
305 }
306
View as plain text