]> Cypherpunks.ru repositories - gostls13.git/blob - src/net/http/responsecontroller_test.go
0dca7332b796704c90d5e935f46efa3d9a8abe79
[gostls13.git] / 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                 // Connection errors are sticky, so resetting the deadline does not permit
95                 // making more progress. We might want to change this in the future, but verify
96                 // the current behavior for now. If we do change this, we'll want to make sure
97                 // to do so only for writing the response body, not headers.
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 // don't set the deadline until the client reads response headers
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 // io.Copy error
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         cst := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
160                 ctl := NewResponseController(w)
161                 b := make([]byte, 3)
162                 n, err := io.ReadFull(r.Body, b)
163                 b = b[:n]
164                 if err != nil || string(b) != "one" {
165                         t.Errorf("before setting read deadline: Read = %v, %q, want nil, %q", err, string(b), "one")
166                         return
167                 }
168                 if err := ctl.SetReadDeadline(time.Now()); err != nil {
169                         t.Errorf("ctl.SetReadDeadline() = %v, want nil", err)
170                         return
171                 }
172                 b, err = io.ReadAll(r.Body)
173                 if err == nil || string(b) != "" {
174                         t.Errorf("after setting read deadline: Read = %q, nil, want error", string(b))
175                 }
176                 close(readc)
177                 // Connection errors are sticky, so resetting the deadline does not permit
178                 // making more progress. We might want to change this in the future, but verify
179                 // the current behavior for now.
180                 if err := ctl.SetReadDeadline(time.Time{}); err != nil {
181                         t.Errorf("ctl.SetReadDeadline() = %v, want nil", err)
182                         return
183                 }
184                 b, err = io.ReadAll(r.Body)
185                 if err == nil {
186                         t.Errorf("after resetting read deadline: Read = %q, nil, want error", string(b))
187                 }
188         }))
189
190         pr, pw := io.Pipe()
191         var wg sync.WaitGroup
192         wg.Add(1)
193         go func() {
194                 defer wg.Done()
195                 pw.Write([]byte("one"))
196                 <-readc
197                 pw.Write([]byte("two"))
198                 pw.Close()
199         }()
200         defer wg.Wait()
201         res, err := cst.c.Post(cst.ts.URL, "text/foo", pr)
202         if err == nil {
203                 defer res.Body.Close()
204         }
205 }
206
207 func TestResponseControllerSetFutureReadDeadline(t *testing.T) {
208         run(t, testResponseControllerSetFutureReadDeadline)
209 }
210 func testResponseControllerSetFutureReadDeadline(t *testing.T, mode testMode) {
211         respBody := "response body"
212         cst := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, req *Request) {
213                 ctl := NewResponseController(w)
214                 if err := ctl.SetReadDeadline(time.Now().Add(1 * time.Millisecond)); err != nil {
215                         t.Errorf("ctl.SetReadDeadline() = %v, want nil", err)
216                 }
217                 _, err := io.Copy(io.Discard, req.Body)
218                 if !errors.Is(err, os.ErrDeadlineExceeded) {
219                         t.Errorf("server timed out reading request body: got err %v; want os.ErrDeadlineExceeded", err)
220                 }
221                 w.Write([]byte(respBody))
222         }))
223         pr, pw := io.Pipe()
224         res, err := cst.c.Post(cst.ts.URL, "text/apocryphal", pr)
225         if err != nil {
226                 t.Fatal(err)
227         }
228         defer res.Body.Close()
229         got, err := io.ReadAll(res.Body)
230         if string(got) != respBody || err != nil {
231                 t.Errorf("client read response body: %q, %v; want %q, nil", string(got), err, respBody)
232         }
233         pw.Close()
234 }
235
236 type wrapWriter struct {
237         ResponseWriter
238 }
239
240 func (w wrapWriter) Unwrap() ResponseWriter {
241         return w.ResponseWriter
242 }
243
244 func TestWrappedResponseController(t *testing.T) { run(t, testWrappedResponseController) }
245 func testWrappedResponseController(t *testing.T, mode testMode) {
246         cst := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
247                 w = wrapWriter{w}
248                 ctl := NewResponseController(w)
249                 if err := ctl.Flush(); err != nil {
250                         t.Errorf("ctl.Flush() = %v, want nil", err)
251                 }
252                 if err := ctl.SetReadDeadline(time.Time{}); err != nil {
253                         t.Errorf("ctl.SetReadDeadline() = %v, want nil", err)
254                 }
255                 if err := ctl.SetWriteDeadline(time.Time{}); err != nil {
256                         t.Errorf("ctl.SetWriteDeadline() = %v, want nil", err)
257                 }
258         }))
259         res, err := cst.c.Get(cst.ts.URL)
260         if err != nil {
261                 t.Fatalf("unexpected connection error: %v", err)
262         }
263         io.Copy(io.Discard, res.Body)
264         defer res.Body.Close()
265 }