Source file
src/net/http/responsecontroller_test.go
1 package http_test
2
3 import (
4 "errors"
5 "fmt"
6 "io"
7 . "net/http"
8 "os"
9 "sync"
10 "testing"
11 "time"
12 )
13
14 func TestResponseControllerFlush(t *testing.T) { run(t, testResponseControllerFlush) }
15 func testResponseControllerFlush(t *testing.T, mode testMode) {
16 continuec := make(chan struct{})
17 cst := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
18 ctl := NewResponseController(w)
19 w.Write([]byte("one"))
20 if err := ctl.Flush(); err != nil {
21 t.Errorf("ctl.Flush() = %v, want nil", err)
22 return
23 }
24 <-continuec
25 w.Write([]byte("two"))
26 }))
27
28 res, err := cst.c.Get(cst.ts.URL)
29 if err != nil {
30 t.Fatalf("unexpected connection error: %v", err)
31 }
32 defer res.Body.Close()
33
34 buf := make([]byte, 16)
35 n, err := res.Body.Read(buf)
36 close(continuec)
37 if err != nil || string(buf[:n]) != "one" {
38 t.Fatalf("Body.Read = %q, %v, want %q, nil", string(buf[:n]), err, "one")
39 }
40
41 got, err := io.ReadAll(res.Body)
42 if err != nil || string(got) != "two" {
43 t.Fatalf("Body.Read = %q, %v, want %q, nil", string(got), err, "two")
44 }
45 }
46
47 func TestResponseControllerHijack(t *testing.T) { run(t, testResponseControllerHijack) }
48 func testResponseControllerHijack(t *testing.T, mode testMode) {
49 const header = "X-Header"
50 const value = "set"
51 cst := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
52 ctl := NewResponseController(w)
53 c, _, err := ctl.Hijack()
54 if mode == http2Mode {
55 if err == nil {
56 t.Errorf("ctl.Hijack = nil, want error")
57 }
58 w.Header().Set(header, value)
59 return
60 }
61 if err != nil {
62 t.Errorf("ctl.Hijack = _, _, %v, want _, _, nil", err)
63 return
64 }
65 fmt.Fprintf(c, "HTTP/1.0 200 OK\r\n%v: %v\r\nContent-Length: 0\r\n\r\n", header, value)
66 }))
67 res, err := cst.c.Get(cst.ts.URL)
68 if err != nil {
69 t.Fatal(err)
70 }
71 if got, want := res.Header.Get(header), value; got != want {
72 t.Errorf("response header %q = %q, want %q", header, got, want)
73 }
74 }
75
76 func TestResponseControllerSetPastWriteDeadline(t *testing.T) {
77 run(t, testResponseControllerSetPastWriteDeadline)
78 }
79 func testResponseControllerSetPastWriteDeadline(t *testing.T, mode testMode) {
80 cst := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
81 ctl := NewResponseController(w)
82 w.Write([]byte("one"))
83 if err := ctl.Flush(); err != nil {
84 t.Errorf("before setting deadline: ctl.Flush() = %v, want nil", err)
85 }
86 if err := ctl.SetWriteDeadline(time.Now().Add(-10 * time.Second)); err != nil {
87 t.Errorf("ctl.SetWriteDeadline() = %v, want nil", err)
88 }
89
90 w.Write([]byte("two"))
91 if err := ctl.Flush(); err == nil {
92 t.Errorf("after setting deadline: ctl.Flush() = nil, want non-nil")
93 }
94
95
96
97
98 if err := ctl.SetWriteDeadline(time.Now().Add(1 * time.Hour)); err != nil {
99 t.Errorf("ctl.SetWriteDeadline() = %v, want nil", err)
100 }
101 w.Write([]byte("three"))
102 if err := ctl.Flush(); err == nil {
103 t.Errorf("after resetting deadline: ctl.Flush() = nil, want non-nil")
104 }
105 }))
106
107 res, err := cst.c.Get(cst.ts.URL)
108 if err != nil {
109 t.Fatalf("unexpected connection error: %v", err)
110 }
111 defer res.Body.Close()
112 b, _ := io.ReadAll(res.Body)
113 if string(b) != "one" {
114 t.Errorf("unexpected body: %q", string(b))
115 }
116 }
117
118 func TestResponseControllerSetFutureWriteDeadline(t *testing.T) {
119 run(t, testResponseControllerSetFutureWriteDeadline)
120 }
121 func testResponseControllerSetFutureWriteDeadline(t *testing.T, mode testMode) {
122 errc := make(chan error, 1)
123 startwritec := make(chan struct{})
124 cst := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
125 ctl := NewResponseController(w)
126 w.WriteHeader(200)
127 if err := ctl.Flush(); err != nil {
128 t.Errorf("ctl.Flush() = %v, want nil", err)
129 }
130 <-startwritec
131 if err := ctl.SetWriteDeadline(time.Now().Add(1 * time.Millisecond)); err != nil {
132 t.Errorf("ctl.SetWriteDeadline() = %v, want nil", err)
133 }
134 _, err := io.Copy(w, neverEnding('a'))
135 errc <- err
136 }))
137
138 res, err := cst.c.Get(cst.ts.URL)
139 close(startwritec)
140 if err != nil {
141 t.Fatalf("unexpected connection error: %v", err)
142 }
143 defer res.Body.Close()
144 _, err = io.Copy(io.Discard, res.Body)
145 if err == nil {
146 t.Errorf("client reading from truncated request body: got nil error, want non-nil")
147 }
148 err = <-errc
149 if !errors.Is(err, os.ErrDeadlineExceeded) {
150 t.Errorf("server timed out writing request body: got err %v; want os.ErrDeadlineExceeded", err)
151 }
152 }
153
154 func TestResponseControllerSetPastReadDeadline(t *testing.T) {
155 run(t, testResponseControllerSetPastReadDeadline)
156 }
157 func testResponseControllerSetPastReadDeadline(t *testing.T, mode testMode) {
158 readc := make(chan struct{})
159 donec := make(chan struct{})
160 cst := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
161 defer close(donec)
162 ctl := NewResponseController(w)
163 b := make([]byte, 3)
164 n, err := io.ReadFull(r.Body, b)
165 b = b[:n]
166 if err != nil || string(b) != "one" {
167 t.Errorf("before setting read deadline: Read = %v, %q, want nil, %q", err, string(b), "one")
168 return
169 }
170 if err := ctl.SetReadDeadline(time.Now()); err != nil {
171 t.Errorf("ctl.SetReadDeadline() = %v, want nil", err)
172 return
173 }
174 b, err = io.ReadAll(r.Body)
175 if err == nil || string(b) != "" {
176 t.Errorf("after setting read deadline: Read = %q, nil, want error", string(b))
177 }
178 close(readc)
179
180
181
182 if err := ctl.SetReadDeadline(time.Time{}); err != nil {
183 t.Errorf("ctl.SetReadDeadline() = %v, want nil", err)
184 return
185 }
186 b, err = io.ReadAll(r.Body)
187 if err == nil {
188 t.Errorf("after resetting read deadline: Read = %q, nil, want error", string(b))
189 }
190 }))
191
192 pr, pw := io.Pipe()
193 var wg sync.WaitGroup
194 wg.Add(1)
195 go func() {
196 defer wg.Done()
197 defer pw.Close()
198 pw.Write([]byte("one"))
199 select {
200 case <-readc:
201 case <-donec:
202 select {
203 case <-readc:
204 default:
205 t.Errorf("server handler unexpectedly exited without closing readc")
206 return
207 }
208 }
209 pw.Write([]byte("two"))
210 }()
211 defer wg.Wait()
212 res, err := cst.c.Post(cst.ts.URL, "text/foo", pr)
213 if err == nil {
214 defer res.Body.Close()
215 }
216 }
217
218 func TestResponseControllerSetFutureReadDeadline(t *testing.T) {
219 run(t, testResponseControllerSetFutureReadDeadline)
220 }
221 func testResponseControllerSetFutureReadDeadline(t *testing.T, mode testMode) {
222 respBody := "response body"
223 cst := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, req *Request) {
224 ctl := NewResponseController(w)
225 if err := ctl.SetReadDeadline(time.Now().Add(1 * time.Millisecond)); err != nil {
226 t.Errorf("ctl.SetReadDeadline() = %v, want nil", err)
227 }
228 _, err := io.Copy(io.Discard, req.Body)
229 if !errors.Is(err, os.ErrDeadlineExceeded) {
230 t.Errorf("server timed out reading request body: got err %v; want os.ErrDeadlineExceeded", err)
231 }
232 w.Write([]byte(respBody))
233 }))
234 pr, pw := io.Pipe()
235 res, err := cst.c.Post(cst.ts.URL, "text/apocryphal", pr)
236 if err != nil {
237 t.Fatal(err)
238 }
239 defer res.Body.Close()
240 got, err := io.ReadAll(res.Body)
241 if string(got) != respBody || err != nil {
242 t.Errorf("client read response body: %q, %v; want %q, nil", string(got), err, respBody)
243 }
244 pw.Close()
245 }
246
247 type wrapWriter struct {
248 ResponseWriter
249 }
250
251 func (w wrapWriter) Unwrap() ResponseWriter {
252 return w.ResponseWriter
253 }
254
255 func TestWrappedResponseController(t *testing.T) { run(t, testWrappedResponseController) }
256 func testWrappedResponseController(t *testing.T, mode testMode) {
257 cst := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
258 w = wrapWriter{w}
259 ctl := NewResponseController(w)
260 if err := ctl.Flush(); err != nil {
261 t.Errorf("ctl.Flush() = %v, want nil", err)
262 }
263 if err := ctl.SetReadDeadline(time.Time{}); err != nil {
264 t.Errorf("ctl.SetReadDeadline() = %v, want nil", err)
265 }
266 if err := ctl.SetWriteDeadline(time.Time{}); err != nil {
267 t.Errorf("ctl.SetWriteDeadline() = %v, want nil", err)
268 }
269 }))
270 res, err := cst.c.Get(cst.ts.URL)
271 if err != nil {
272 t.Fatalf("unexpected connection error: %v", err)
273 }
274 io.Copy(io.Discard, res.Body)
275 defer res.Body.Close()
276 }
277
278 func TestResponseControllerEnableFullDuplex(t *testing.T) {
279 run(t, testResponseControllerEnableFullDuplex)
280 }
281 func testResponseControllerEnableFullDuplex(t *testing.T, mode testMode) {
282 cst := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, req *Request) {
283 ctl := NewResponseController(w)
284 if err := ctl.EnableFullDuplex(); err != nil {
285
286
287
288
289 if mode != http2Mode {
290 t.Errorf("ctl.EnableFullDuplex() = %v, want nil", err)
291 }
292 }
293 w.WriteHeader(200)
294 ctl.Flush()
295 for {
296 var buf [1]byte
297 n, err := req.Body.Read(buf[:])
298 if n != 1 || err != nil {
299 break
300 }
301 w.Write(buf[:])
302 ctl.Flush()
303 }
304 }))
305 pr, pw := io.Pipe()
306 res, err := cst.c.Post(cst.ts.URL, "text/apocryphal", pr)
307 if err != nil {
308 t.Fatal(err)
309 }
310 defer res.Body.Close()
311 for i := byte(0); i < 10; i++ {
312 if _, err := pw.Write([]byte{i}); err != nil {
313 t.Fatalf("Write: %v", err)
314 }
315 var buf [1]byte
316 if n, err := res.Body.Read(buf[:]); n != 1 || err != nil {
317 t.Fatalf("Read: %v, %v", n, err)
318 }
319 if buf[0] != i {
320 t.Fatalf("read byte %v, want %v", buf[0], i)
321 }
322 }
323 pw.Close()
324 }
325
View as plain text