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
23
24 type http1ServerTest struct {
25 t *testing.T
26 ts *httptest.Server
27 }
28
29 func newHTTP1ServerTest(t *testing.T, h http.HandlerFunc) *http1ServerTest {
30 if h == nil {
31 h = func(w http.ResponseWriter, req *http.Request) {}
32 }
33 st := &http1ServerTest{
34 t: t,
35 ts: httptest.NewTestServer(t, h),
36 }
37 return st
38 }
39
40
41 func (st *http1ServerTest) client() *http.Client {
42 return st.ts.Client()
43 }
44
45
46 func (st *http1ServerTest) transport() *http.Transport {
47 return st.ts.Client().Transport.(*http.Transport)
48 }
49
50
51 func (st *http1ServerTest) dial() *http1TestConn {
52 t := st.t
53 t.Helper()
54 nc, err := st.transport().DialContext(st.t.Context(), "tcp", "example.tld")
55 if err != nil {
56 t.Fatal(err)
57 }
58 t.Cleanup(func() {
59 nc.Close()
60 })
61 conn := nc.(*nettest.Conn)
62 conn.SetReadError(errWouldBlock)
63 return &http1TestConn{
64 t: st.t,
65 conn: conn,
66 bufr: bufio.NewReader(conn),
67 }
68 }
69
70 var errWouldBlock = errors.New("would block")
71
72 type http1TestConn struct {
73 t *testing.T
74 conn *nettest.Conn
75 bufr *bufio.Reader
76 }
77
78
79 func (tc *http1TestConn) writeMessage(lines ...string) {
80 t := tc.t
81 t.Helper()
82 if _, err := tc.conn.Write([]byte(strings.Join(lines, "\r\n") + "\r\n")); err != nil {
83 t.Fatalf("conn write: %v", err)
84 }
85 }
86
87
88 func (tc *http1TestConn) readRequest() *http.Request {
89 t := tc.t
90 t.Helper()
91 synctest.Wait()
92 req, err := http.ReadRequest(tc.bufr)
93 if err != nil {
94 t.Fatalf("ReadRequest: %v", err)
95 }
96 return req
97 }
98
99
100 func (tc *http1TestConn) readResponse() *http.Response {
101 t := tc.t
102 t.Helper()
103 synctest.Wait()
104 resp, err := http.ReadResponse(tc.bufr, nil)
105 if err != nil {
106 t.Fatalf("ReadResponse: %v", err)
107 }
108 return resp
109 }
110
111 func (tc *http1TestConn) wantResponse(wantStart string, wantHeaders http.Header) {
112 t := tc.t
113 t.Helper()
114 synctest.Wait()
115 gotStart, err := tc.bufr.ReadString('\n')
116 if err != nil {
117 t.Fatalf("read from conn: %q, %v; want start line %q", gotStart, err, wantStart)
118 }
119 if got, want := gotStart, wantStart+"\r\n"; got != want {
120 t.Fatalf("read start line:\n%q\nwant:\n%q", got, want)
121 }
122 gotHeaders := make(http.Header)
123 for {
124 line, err := tc.bufr.ReadString('\n')
125 if err != nil {
126 t.Fatalf("read from conn: %v (want header)", err)
127 }
128 line, ok := strings.CutSuffix(line, "\r\n")
129 if !ok {
130 t.Fatalf("header line has no CRLF suffix: %q", line)
131 }
132 if line == "" {
133 break
134 }
135 k, v, ok := strings.Cut(line, ": ")
136 if !ok {
137 t.Fatalf("invalid header line: %q", line)
138 }
139 gotHeaders[k] = append(gotHeaders[k], v)
140 }
141 for k, wantv := range wantHeaders {
142 gotv := gotHeaders[k]
143 if !slices.Equal(gotv, wantv) {
144 t.Errorf("header %v = %q, want %q", k, gotv, wantv)
145 }
146 }
147 if t.Failed() {
148 t.FailNow()
149 }
150 }
151
152
153 func (tc *http1TestConn) wantBytes(want []byte) {
154 t := tc.t
155 t.Helper()
156 synctest.Wait()
157 got := make([]byte, len(want))
158 n, err := io.ReadFull(tc.bufr, got)
159 got = got[:n]
160 if err != nil || !bytes.Equal(want, got) {
161 t.Fatalf("want bytes %q, got %q and error %v", want, got, err)
162 }
163 }
164
165
166 func (tc *http1TestConn) wantIdle() {
167 t := tc.t
168 t.Helper()
169 synctest.Wait()
170 if got, err := tc.bufr.Peek(32); len(got) != 0 || !errors.Is(err, errWouldBlock) {
171 t.Fatalf("read from conn: %q, %v; expect conn to be idle", got, err)
172 }
173 }
174
175
176 func (tc *http1TestConn) wantClosed() {
177 t := tc.t
178 t.Helper()
179 synctest.Wait()
180 if got, err := tc.bufr.Peek(32); len(got) != 0 || err != io.EOF {
181 t.Fatalf("read from conn: %q; expect conn to be closed", got)
182 }
183 }
184
185 type testHandler struct {
186 t *testing.T
187 mu sync.Mutex
188 calls []*testHandlerCall
189 closed bool
190 }
191
192 func newTestHandler(t *testing.T) *testHandler {
193 h := &testHandler{t: t}
194 t.Cleanup(func() {
195
196
197 if !h.closed {
198 t.Errorf("testHandler.Close not called")
199 }
200 })
201 return h
202 }
203
204 func (h *testHandler) Close() {
205 h.t.Helper()
206 synctest.Wait()
207 h.mu.Lock()
208 defer h.mu.Unlock()
209 if len(h.calls) > 0 {
210 h.t.Errorf("test finished with %v handler calls unhandled", len(h.calls))
211 }
212 for _, call := range h.calls {
213 call.exit()
214 }
215 h.calls = nil
216 h.closed = true
217 }
218
219 func (h *testHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
220 call := &testHandlerCall{
221 w: w,
222 req: req,
223 ch: make(chan func()),
224 }
225 h.mu.Lock()
226 if h.closed {
227 h.t.Errorf("test handler called after close")
228 }
229 h.calls = append(h.calls, call)
230 h.mu.Unlock()
231 for f := range call.ch {
232 f()
233 }
234 }
235
236 func (h *testHandler) nextCall() *testHandlerCall {
237 h.t.Helper()
238 synctest.Wait()
239 h.mu.Lock()
240 defer h.mu.Unlock()
241 if len(h.calls) == 0 {
242 h.t.Fatal("expected server handler call, got none")
243 }
244 call := h.calls[0]
245 h.calls = h.calls[1:]
246 h.t.Cleanup(call.exit)
247 return call
248 }
249
250
251 type testHandlerCall struct {
252 w http.ResponseWriter
253 req *http.Request
254 closeOnce sync.Once
255 ch chan func()
256 }
257
258
259 func (call *testHandlerCall) do(f func(http.ResponseWriter, *http.Request)) {
260 donec := make(chan struct{})
261 call.ch <- func() {
262 defer close(donec)
263 f(call.w, call.req)
264 }
265 <-donec
266 }
267
268
269 func (call *testHandlerCall) exit() {
270 call.closeOnce.Do(func() {
271 close(call.ch)
272 })
273 }
274
275 func joinCRLF(s ...string) string {
276 return strings.Join(s, "\r\n")
277 }
278
View as plain text