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