Source file
src/net/http/http1_server_test.go
1
2
3
4
5 package http_test
6
7 import (
8 "bufio"
9 "bytes"
10 "errors"
11 "internal/nettest"
12 "io"
13 "net/http"
14 "net/http/httptest"
15 "slices"
16 "strings"
17 "sync"
18 "testing"
19 "testing/synctest"
20 )
21
22 func TestHTTP1ServerInvalidTrailers(t *testing.T) {
23 for _, test := range []struct {
24 name string
25 request string
26 }{{
27 name: "invalid trailer",
28 request: joinCRLF(
29 "POST / HTTP/1.1",
30 "Host: example.tld",
31 "Trailer: Park",
32 "Transfer-Encoding: chunked",
33 "",
34 "3",
35 "xxx",
36 "0",
37 "I'm not a valid trailer",
38 "GET /smuggled HTTP/1.1",
39 "Host: example.tld",
40 "Content-Length: 0",
41 "",
42 ),
43 }, {
44 name: "trailer section ends with bare LF",
45 request: joinCRLF(
46 "POST / HTTP/1.1",
47 "Host: example.tld",
48 "Transfer-Encoding: chunked",
49 "",
50 "3",
51 "xxx",
52 "0",
53 "\nGET /smuggled HTTP/1.1",
54 "Host: example.tld",
55 "Content-Length: 0",
56 "",
57 ),
58 }, {
59 name: "trailer line ends with bare LF",
60 request: joinCRLF(
61 "POST / HTTP/1.1",
62 "Host: example.tld",
63 "Transfer-Encoding: chunked",
64 "",
65 "3",
66 "xxx",
67 "0",
68 "A: 1\nB: 2",
69 "",
70 ),
71 }, {
72 name: "bare CR before end of trailers",
73 request: joinCRLF(
74 "POST / HTTP/1.1",
75 "Host: example.tld",
76 "Transfer-Encoding: chunked",
77 "",
78 "3",
79 "xxx",
80 "0",
81 "Foo: bar\r\r\n\r\n",
82 ),
83 }} {
84 synctest.Subtest(t, test.name, func(t *testing.T) {
85 handler := newTestHandler(t)
86 st := newHTTP1ServerTest(t, handler.ServeHTTP)
87 defer handler.Close()
88
89 conn := st.dial()
90 conn.writeMessage(test.request)
91
92 call := handler.nextCall()
93 http.NewResponseController(call.w).EnableFullDuplex()
94 n, err := io.Copy(io.Discard, call.req.Body)
95 if err == nil {
96 t.Errorf("read %v request data bytes without error; want error", n)
97 }
98 call.exit()
99
100
101 conn.wantResponse("HTTP/1.1 200 OK", nil)
102 conn.wantClosed()
103 })
104 }
105 }
106
107
108
109 type http1ServerTest struct {
110 t *testing.T
111 ts *httptest.Server
112 }
113
114 func newHTTP1ServerTest(t *testing.T, h http.HandlerFunc) *http1ServerTest {
115 if h == nil {
116 h = func(w http.ResponseWriter, req *http.Request) {}
117 }
118 st := &http1ServerTest{
119 t: t,
120 ts: httptest.NewTestServer(t, h),
121 }
122 return st
123 }
124
125
126 func (st *http1ServerTest) client() *http.Client {
127 return st.ts.Client()
128 }
129
130
131 func (st *http1ServerTest) transport() *http.Transport {
132 return st.ts.Client().Transport.(*http.Transport)
133 }
134
135
136 func (st *http1ServerTest) dial() *http1TestConn {
137 t := st.t
138 t.Helper()
139 nc, err := st.transport().DialContext(st.t.Context(), "tcp", "example.tld")
140 if err != nil {
141 t.Fatal(err)
142 }
143 t.Cleanup(func() {
144 nc.Close()
145 })
146 conn := nc.(*nettest.Conn)
147 conn.SetReadError(errWouldBlock)
148 return &http1TestConn{
149 t: st.t,
150 conn: conn,
151 bufr: bufio.NewReader(conn),
152 }
153 }
154
155 var errWouldBlock = errors.New("would block")
156
157 type http1TestConn struct {
158 t *testing.T
159 conn *nettest.Conn
160 bufr *bufio.Reader
161 }
162
163
164 func (tc *http1TestConn) writeMessage(lines ...string) {
165 t := tc.t
166 t.Helper()
167 if _, err := tc.conn.Write([]byte(strings.Join(lines, "\r\n") + "\r\n")); err != nil {
168 t.Fatalf("conn write: %v", err)
169 }
170 }
171
172
173 func (tc *http1TestConn) readRequest() *http.Request {
174 t := tc.t
175 t.Helper()
176 synctest.Wait()
177 req, err := http.ReadRequest(tc.bufr)
178 if err != nil {
179 t.Fatalf("ReadRequest: %v", err)
180 }
181 return req
182 }
183
184
185 func (tc *http1TestConn) readResponse() *http.Response {
186 t := tc.t
187 t.Helper()
188 synctest.Wait()
189 resp, err := http.ReadResponse(tc.bufr, nil)
190 if err != nil {
191 t.Fatalf("ReadResponse: %v", err)
192 }
193 return resp
194 }
195
196 func (tc *http1TestConn) wantResponse(wantStart string, wantHeaders http.Header) {
197 t := tc.t
198 t.Helper()
199 synctest.Wait()
200 gotStart, err := tc.bufr.ReadString('\n')
201 if err != nil {
202 t.Fatalf("read from conn: %q, %v; want start line %q", gotStart, err, wantStart)
203 }
204 if got, want := gotStart, wantStart+"\r\n"; got != want {
205 t.Fatalf("read start line:\n%q\nwant:\n%q", got, want)
206 }
207 gotHeaders := make(http.Header)
208 for {
209 line, err := tc.bufr.ReadString('\n')
210 if err != nil {
211 t.Fatalf("read from conn: %v (want header)", err)
212 }
213 line, ok := strings.CutSuffix(line, "\r\n")
214 if !ok {
215 t.Fatalf("header line has no CRLF suffix: %q", line)
216 }
217 if line == "" {
218 break
219 }
220 k, v, ok := strings.Cut(line, ": ")
221 if !ok {
222 t.Fatalf("invalid header line: %q", line)
223 }
224 gotHeaders[k] = append(gotHeaders[k], v)
225 }
226 for k, wantv := range wantHeaders {
227 gotv := gotHeaders[k]
228 if !slices.Equal(gotv, wantv) {
229 t.Errorf("header %v = %q, want %q", k, gotv, wantv)
230 }
231 }
232 if t.Failed() {
233 t.FailNow()
234 }
235 }
236
237
238 func (tc *http1TestConn) wantBytes(want []byte) {
239 t := tc.t
240 t.Helper()
241 synctest.Wait()
242 got := make([]byte, len(want))
243 n, err := io.ReadFull(tc.bufr, got)
244 got = got[:n]
245 if err != nil || !bytes.Equal(want, got) {
246 t.Fatalf("want bytes %q, got %q and error %v", want, got, err)
247 }
248 }
249
250
251 func (tc *http1TestConn) wantIdle() {
252 t := tc.t
253 t.Helper()
254 synctest.Wait()
255 if got, err := tc.bufr.Peek(32); len(got) != 0 || !errors.Is(err, errWouldBlock) {
256 t.Fatalf("read from conn: %q, %v; expect conn to be idle", got, err)
257 }
258 }
259
260
261 func (tc *http1TestConn) wantClosed() {
262 t := tc.t
263 t.Helper()
264 synctest.Wait()
265 if got, err := tc.bufr.Peek(32); len(got) != 0 || err != io.EOF {
266 t.Fatalf("read from conn: %q; expect conn to be closed", got)
267 }
268 }
269
270 type testHandler struct {
271 t *testing.T
272 mu sync.Mutex
273 calls []*testHandlerCall
274 closed bool
275 }
276
277 func newTestHandler(t *testing.T) *testHandler {
278 h := &testHandler{t: t}
279 t.Cleanup(func() {
280
281
282 if !h.closed {
283 t.Errorf("testHandler.Close not called")
284 }
285 })
286 return h
287 }
288
289 func (h *testHandler) Close() {
290 h.t.Helper()
291 synctest.Wait()
292 h.mu.Lock()
293 defer h.mu.Unlock()
294 if len(h.calls) > 0 {
295 h.t.Errorf("test finished with %v handler calls unhandled", len(h.calls))
296 }
297 for _, call := range h.calls {
298 call.exit()
299 }
300 h.calls = nil
301 h.closed = true
302 }
303
304 func (h *testHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
305 call := &testHandlerCall{
306 w: w,
307 req: req,
308 ch: make(chan func()),
309 }
310 h.mu.Lock()
311 if h.closed {
312 h.t.Errorf("test handler called after close")
313 }
314 h.calls = append(h.calls, call)
315 h.mu.Unlock()
316 for f := range call.ch {
317 f()
318 }
319 }
320
321 func (h *testHandler) nextCall() *testHandlerCall {
322 h.t.Helper()
323 synctest.Wait()
324 h.mu.Lock()
325 defer h.mu.Unlock()
326 if len(h.calls) == 0 {
327 h.t.Fatal("expected server handler call, got none")
328 }
329 call := h.calls[0]
330 h.calls = h.calls[1:]
331 h.t.Cleanup(call.exit)
332 return call
333 }
334
335
336 type testHandlerCall struct {
337 w http.ResponseWriter
338 req *http.Request
339 closeOnce sync.Once
340 ch chan func()
341 }
342
343
344 func (call *testHandlerCall) do(f func(http.ResponseWriter, *http.Request)) {
345 donec := make(chan struct{})
346 call.ch <- func() {
347 defer close(donec)
348 f(call.w, call.req)
349 }
350 <-donec
351 }
352
353
354 func (call *testHandlerCall) exit() {
355 call.closeOnce.Do(func() {
356 close(call.ch)
357 })
358 }
359
360 func joinCRLF(s ...string) string {
361 return strings.Join(s, "\r\n")
362 }
363
View as plain text