]> git.ipfire.org Git - thirdparty/gcc.git/blame - libgo/go/net/http/transfer_test.go
libgo: update to Go1.14beta1
[thirdparty/gcc.git] / libgo / go / net / http / transfer_test.go
CommitLineData
fabcaa8d
ILT
1// Copyright 2012 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package http
6
7import (
8 "bufio"
dd931d9b 9 "bytes"
5a8ea165 10 "compress/gzip"
aa8901e9
ILT
11 "crypto/rand"
12 "fmt"
6736ef96 13 "io"
dd931d9b 14 "io/ioutil"
aa8901e9
ILT
15 "os"
16 "reflect"
fabcaa8d
ILT
17 "strings"
18 "testing"
19)
20
21func TestBodyReadBadTrailer(t *testing.T) {
22 b := &body{
bae90c98
ILT
23 src: strings.NewReader("foobar"),
24 hdr: true, // force reading the trailer
25 r: bufio.NewReader(strings.NewReader("")),
fabcaa8d
ILT
26 }
27 buf := make([]byte, 7)
28 n, err := b.Read(buf[:3])
29 got := string(buf[:n])
30 if got != "foo" || err != nil {
31 t.Fatalf(`first Read = %d (%q), %v; want 3 ("foo")`, n, got, err)
32 }
33
34 n, err = b.Read(buf[:])
35 got = string(buf[:n])
36 if got != "bar" || err != nil {
37 t.Fatalf(`second Read = %d (%q), %v; want 3 ("bar")`, n, got, err)
38 }
39
40 n, err = b.Read(buf[:])
41 got = string(buf[:n])
42 if err == nil {
43 t.Errorf("final Read was successful (%q), expected error from trailer read", got)
44 }
45}
6736ef96
ILT
46
47func TestFinalChunkedBodyReadEOF(t *testing.T) {
48 res, err := ReadResponse(bufio.NewReader(strings.NewReader(
49 "HTTP/1.1 200 OK\r\n"+
50 "Transfer-Encoding: chunked\r\n"+
51 "\r\n"+
52 "0a\r\n"+
53 "Body here\n\r\n"+
54 "09\r\n"+
55 "continued\r\n"+
56 "0\r\n"+
57 "\r\n")), nil)
58 if err != nil {
59 t.Fatal(err)
60 }
61 want := "Body here\ncontinued"
62 buf := make([]byte, len(want))
63 n, err := res.Body.Read(buf)
64 if n != len(want) || err != io.EOF {
6736ef96
ILT
65 t.Errorf("Read = %v, %v; want %d, EOF", n, err, len(want))
66 }
67 if string(buf) != want {
68 t.Errorf("buf = %q; want %q", buf, want)
69 }
70}
dd931d9b
ILT
71
72func TestDetectInMemoryReaders(t *testing.T) {
73 pr, _ := io.Pipe()
74 tests := []struct {
75 r io.Reader
76 want bool
77 }{
78 {pr, false},
79
80 {bytes.NewReader(nil), true},
81 {bytes.NewBuffer(nil), true},
82 {strings.NewReader(""), true},
83
84 {ioutil.NopCloser(pr), false},
85
86 {ioutil.NopCloser(bytes.NewReader(nil)), true},
87 {ioutil.NopCloser(bytes.NewBuffer(nil)), true},
88 {ioutil.NopCloser(strings.NewReader("")), true},
89 }
90 for i, tt := range tests {
91 got := isKnownInMemoryReader(tt.r)
92 if got != tt.want {
93 t.Errorf("%d: got = %v; want %v", i, got, tt.want)
94 }
95 }
96}
aa8901e9
ILT
97
98type mockTransferWriter struct {
99 CalledReader io.Reader
100 WriteCalled bool
101}
102
103var _ io.ReaderFrom = (*mockTransferWriter)(nil)
104
105func (w *mockTransferWriter) ReadFrom(r io.Reader) (int64, error) {
106 w.CalledReader = r
107 return io.Copy(ioutil.Discard, r)
108}
109
110func (w *mockTransferWriter) Write(p []byte) (int, error) {
111 w.WriteCalled = true
112 return ioutil.Discard.Write(p)
113}
114
115func TestTransferWriterWriteBodyReaderTypes(t *testing.T) {
116 fileType := reflect.TypeOf(&os.File{})
117 bufferType := reflect.TypeOf(&bytes.Buffer{})
118
119 nBytes := int64(1 << 10)
120 newFileFunc := func() (r io.Reader, done func(), err error) {
121 f, err := ioutil.TempFile("", "net-http-newfilefunc")
122 if err != nil {
123 return nil, nil, err
124 }
125
126 // Write some bytes to the file to enable reading.
127 if _, err := io.CopyN(f, rand.Reader, nBytes); err != nil {
128 return nil, nil, fmt.Errorf("failed to write data to file: %v", err)
129 }
130 if _, err := f.Seek(0, 0); err != nil {
131 return nil, nil, fmt.Errorf("failed to seek to front: %v", err)
132 }
133
134 done = func() {
135 f.Close()
136 os.Remove(f.Name())
137 }
138
139 return f, done, nil
140 }
141
142 newBufferFunc := func() (io.Reader, func(), error) {
143 return bytes.NewBuffer(make([]byte, nBytes)), func() {}, nil
144 }
145
146 cases := []struct {
147 name string
148 bodyFunc func() (io.Reader, func(), error)
149 method string
150 contentLength int64
151 transferEncoding []string
152 limitedReader bool
153 expectedReader reflect.Type
154 expectedWrite bool
155 }{
156 {
157 name: "file, non-chunked, size set",
158 bodyFunc: newFileFunc,
159 method: "PUT",
160 contentLength: nBytes,
161 limitedReader: true,
162 expectedReader: fileType,
163 },
164 {
165 name: "file, non-chunked, size set, nopCloser wrapped",
166 method: "PUT",
167 bodyFunc: func() (io.Reader, func(), error) {
168 r, cleanup, err := newFileFunc()
169 return ioutil.NopCloser(r), cleanup, err
170 },
171 contentLength: nBytes,
172 limitedReader: true,
173 expectedReader: fileType,
174 },
175 {
176 name: "file, non-chunked, negative size",
177 method: "PUT",
178 bodyFunc: newFileFunc,
179 contentLength: -1,
180 expectedReader: fileType,
181 },
182 {
183 name: "file, non-chunked, CONNECT, negative size",
184 method: "CONNECT",
185 bodyFunc: newFileFunc,
186 contentLength: -1,
187 expectedReader: fileType,
188 },
189 {
190 name: "file, chunked",
191 method: "PUT",
192 bodyFunc: newFileFunc,
193 transferEncoding: []string{"chunked"},
194 expectedWrite: true,
195 },
196 {
197 name: "buffer, non-chunked, size set",
198 bodyFunc: newBufferFunc,
199 method: "PUT",
200 contentLength: nBytes,
201 limitedReader: true,
202 expectedReader: bufferType,
203 },
204 {
205 name: "buffer, non-chunked, size set, nopCloser wrapped",
206 method: "PUT",
207 bodyFunc: func() (io.Reader, func(), error) {
208 r, cleanup, err := newBufferFunc()
209 return ioutil.NopCloser(r), cleanup, err
210 },
211 contentLength: nBytes,
212 limitedReader: true,
213 expectedReader: bufferType,
214 },
215 {
216 name: "buffer, non-chunked, negative size",
217 method: "PUT",
218 bodyFunc: newBufferFunc,
219 contentLength: -1,
220 expectedWrite: true,
221 },
222 {
223 name: "buffer, non-chunked, CONNECT, negative size",
224 method: "CONNECT",
225 bodyFunc: newBufferFunc,
226 contentLength: -1,
227 expectedWrite: true,
228 },
229 {
230 name: "buffer, chunked",
231 method: "PUT",
232 bodyFunc: newBufferFunc,
233 transferEncoding: []string{"chunked"},
234 expectedWrite: true,
235 },
236 }
237
238 for _, tc := range cases {
239 t.Run(tc.name, func(t *testing.T) {
240 body, cleanup, err := tc.bodyFunc()
241 if err != nil {
242 t.Fatal(err)
243 }
244 defer cleanup()
245
246 mw := &mockTransferWriter{}
247 tw := &transferWriter{
248 Body: body,
249 ContentLength: tc.contentLength,
250 TransferEncoding: tc.transferEncoding,
251 }
252
253 if err := tw.writeBody(mw); err != nil {
254 t.Fatal(err)
255 }
256
257 if tc.expectedReader != nil {
258 if mw.CalledReader == nil {
259 t.Fatal("did not call ReadFrom")
260 }
261
262 var actualReader reflect.Type
263 lr, ok := mw.CalledReader.(*io.LimitedReader)
264 if ok && tc.limitedReader {
265 actualReader = reflect.TypeOf(lr.R)
266 } else {
267 actualReader = reflect.TypeOf(mw.CalledReader)
268 }
269
270 if tc.expectedReader != actualReader {
271 t.Fatalf("got reader %T want %T", actualReader, tc.expectedReader)
272 }
273 }
274
275 if tc.expectedWrite && !mw.WriteCalled {
276 t.Fatal("did not invoke Write")
277 }
278 })
279 }
280}
281
282func TestFixTransferEncoding(t *testing.T) {
283 tests := []struct {
284 hdr Header
285 wantErr error
286 }{
287 {
288 hdr: Header{"Transfer-Encoding": {"fugazi"}},
289 wantErr: &unsupportedTEError{`unsupported transfer encoding: "fugazi"`},
290 },
291 {
292 hdr: Header{"Transfer-Encoding": {"chunked, chunked", "identity", "chunked"}},
5a8ea165 293 wantErr: &badStringError{"chunked must be applied only once, as the last encoding", "chunked, chunked"},
aa8901e9
ILT
294 },
295 {
296 hdr: Header{"Transfer-Encoding": {"chunked"}},
297 wantErr: nil,
298 },
299 }
300
301 for i, tt := range tests {
302 tr := &transferReader{
303 Header: tt.hdr,
304 ProtoMajor: 1,
305 ProtoMinor: 1,
306 }
307 gotErr := tr.fixTransferEncoding()
308 if !reflect.DeepEqual(gotErr, tt.wantErr) {
309 t.Errorf("%d.\ngot error:\n%v\nwant error:\n%v\n\n", i, gotErr, tt.wantErr)
310 }
311 }
312}
5a8ea165
ILT
313
314func gzipIt(s string) string {
315 buf := new(bytes.Buffer)
316 gw := gzip.NewWriter(buf)
317 gw.Write([]byte(s))
318 gw.Close()
319 return buf.String()
320}
321
322func TestUnitTestProxyingReadCloserClosesBody(t *testing.T) {
323 var checker closeChecker
324 buf := new(bytes.Buffer)
325 buf.WriteString("Hello, Gophers!")
326 prc := &proxyingReadCloser{
327 Reader: buf,
328 Closer: &checker,
329 }
330 prc.Close()
331
332 read, err := ioutil.ReadAll(prc)
333 if err != nil {
334 t.Fatalf("Read error: %v", err)
335 }
336 if g, w := string(read), "Hello, Gophers!"; g != w {
337 t.Errorf("Read mismatch: got %q want %q", g, w)
338 }
339
340 if checker.closed != true {
341 t.Fatal("closeChecker.Close was never invoked")
342 }
343}
344
345func TestGzipTransferEncoding_request(t *testing.T) {
346 helloWorldGzipped := gzipIt("Hello, World!")
347
348 tests := []struct {
349 payload string
350 wantErr string
351 wantBody string
352 }{
353
354 {
355 // The case of "chunked" properly applied as the last encoding
356 // and a gzipped request payload that is streamed in 3 parts.
357 payload: `POST / HTTP/1.1
358Host: golang.org
359Transfer-Encoding: gzip, chunked
360Content-Type: text/html; charset=UTF-8
361
362` + fmt.Sprintf("%02x\r\n%s\r\n%02x\r\n%s\r\n%02x\r\n%s\r\n0\r\n\r\n",
363 3, helloWorldGzipped[:3],
364 5, helloWorldGzipped[3:8],
365 len(helloWorldGzipped)-8, helloWorldGzipped[8:]),
366 wantBody: `Hello, World!`,
367 },
368
369 {
370 // The request specifies "Transfer-Encoding: chunked" so its body must be left untouched.
371 payload: `PUT / HTTP/1.1
372Host: golang.org
373Transfer-Encoding: chunked
374Connection: close
375Content-Type: text/html; charset=UTF-8
376
377` + fmt.Sprintf("%0x\r\n%s\r\n0\r\n\r\n", len(helloWorldGzipped), helloWorldGzipped),
378 // We want that payload as it was sent.
379 wantBody: helloWorldGzipped,
380 },
381
382 {
383 // Valid request, the body doesn't have "Transfer-Encoding: chunked" but implicitly encoded
384 // for chunking as per the advisory from RFC 7230 3.3.1 which advises for cases where.
385 payload: `POST / HTTP/1.1
386Host: localhost
387Transfer-Encoding: gzip
388Content-Type: text/html; charset=UTF-8
389
390` + fmt.Sprintf("%0x\r\n%s\r\n0\r\n\r\n", len(helloWorldGzipped), helloWorldGzipped),
391 wantBody: `Hello, World!`,
392 },
393
394 {
395 // Invalid request, the body isn't chunked nor is the connection terminated immediately
396 // hence invalid as per the advisory from RFC 7230 3.3.1 which advises for cases where
397 // a Transfer-Encoding that isn't finally chunked is provided.
398 payload: `PUT / HTTP/1.1
399Host: golang.org
400Transfer-Encoding: gzip
401Content-Length: 0
402Connection: close
403Content-Type: text/html; charset=UTF-8
404
405`,
406 wantErr: `EOF`,
407 },
408
409 {
410 // The case of chunked applied before another encoding.
411 payload: `PUT / HTTP/1.1
412Location: golang.org
413Transfer-Encoding: chunked, gzip
414Content-Length: 0
415Connection: close
416Content-Type: text/html; charset=UTF-8
417
418`,
419 wantErr: `chunked must be applied only once, as the last encoding "chunked, gzip"`,
420 },
421
422 {
423 // The case of chunked properly applied as the
424 // last encoding BUT with a bad "Content-Length".
425 payload: `POST / HTTP/1.1
426Host: golang.org
427Transfer-Encoding: gzip, chunked
428Content-Length: 10
429Connection: close
430Content-Type: text/html; charset=UTF-8
431
432` + "0\r\n\r\n",
433 wantErr: "EOF",
434 },
435 }
436
437 for i, tt := range tests {
438 req, err := ReadRequest(bufio.NewReader(strings.NewReader(tt.payload)))
439 if tt.wantErr != "" {
440 if err == nil || !strings.Contains(err.Error(), tt.wantErr) {
441 t.Errorf("test %d. Error mismatch\nGot: %v\nWant: %s", i, err, tt.wantErr)
442 }
443 continue
444 }
445
446 if err != nil {
447 t.Errorf("test %d. Unexpected ReadRequest error: %v\nPayload:\n%s", i, err, tt.payload)
448 continue
449 }
450
451 got, err := ioutil.ReadAll(req.Body)
452 req.Body.Close()
453 if err != nil {
454 t.Errorf("test %d. Failed to read response body: %v", i, err)
455 }
456 if g, w := string(got), tt.wantBody; g != w {
457 t.Errorf("test %d. Request body mimsatch\nGot:\n%s\n\nWant:\n%s", i, g, w)
458 }
459 }
460}
461
462func TestGzipTransferEncoding_response(t *testing.T) {
463 helloWorldGzipped := gzipIt("Hello, World!")
464
465 tests := []struct {
466 payload string
467 wantErr string
468 wantBody string
469 }{
470
471 {
472 // The case of "chunked" properly applied as the last encoding
473 // and a gzipped payload that is streamed in 3 parts.
474 payload: `HTTP/1.1 302 Found
475Location: https://golang.org/
476Transfer-Encoding: gzip, chunked
477Connection: close
478Content-Type: text/html; charset=UTF-8
479
480` + fmt.Sprintf("%02x\r\n%s\r\n%02x\r\n%s\r\n%02x\r\n%s\r\n0\r\n\r\n",
481 3, helloWorldGzipped[:3],
482 5, helloWorldGzipped[3:8],
483 len(helloWorldGzipped)-8, helloWorldGzipped[8:]),
484 wantBody: `Hello, World!`,
485 },
486
487 {
488 // The response specifies "Transfer-Encoding: chunked" so response body must be left untouched.
489 payload: `HTTP/1.1 302 Found
490Location: https://golang.org/
491Transfer-Encoding: chunked
492Connection: close
493Content-Type: text/html; charset=UTF-8
494
495` + fmt.Sprintf("%0x\r\n%s\r\n0\r\n\r\n", len(helloWorldGzipped), helloWorldGzipped),
496 // We want that payload as it was sent.
497 wantBody: helloWorldGzipped,
498 },
499
500 {
501 // Valid response, the body doesn't have "Transfer-Encoding: chunked" but implicitly encoded
502 // for chunking as per the advisory from RFC 7230 3.3.1 which advises for cases where.
503 payload: `HTTP/1.1 302 Found
504Location: https://golang.org/
505Transfer-Encoding: gzip
506Connection: close
507Content-Type: text/html; charset=UTF-8
508
509` + fmt.Sprintf("%0x\r\n%s\r\n0\r\n\r\n", len(helloWorldGzipped), helloWorldGzipped),
510 wantBody: `Hello, World!`,
511 },
512
513 {
514 // Invalid response, the body isn't chunked nor is the connection terminated immediately
515 // hence invalid as per the advisory from RFC 7230 3.3.1 which advises for cases where
516 // a Transfer-Encoding that isn't finally chunked is provided.
517 payload: `HTTP/1.1 302 Found
518Location: https://golang.org/
519Transfer-Encoding: gzip
520Content-Length: 0
521Connection: close
522Content-Type: text/html; charset=UTF-8
523
524`,
525 wantErr: `EOF`,
526 },
527
528 {
529 // The case of chunked applied before another encoding.
530 payload: `HTTP/1.1 302 Found
531Location: https://golang.org/
532Transfer-Encoding: chunked, gzip
533Content-Length: 0
534Connection: close
535Content-Type: text/html; charset=UTF-8
536
537`,
538 wantErr: `chunked must be applied only once, as the last encoding "chunked, gzip"`,
539 },
540
541 {
542 // The case of chunked properly applied as the
543 // last encoding BUT with a bad "Content-Length".
544 payload: `HTTP/1.1 302 Found
545Location: https://golang.org/
546Transfer-Encoding: gzip, chunked
547Content-Length: 10
548Connection: close
549Content-Type: text/html; charset=UTF-8
550
551` + "0\r\n\r\n",
552 wantErr: "EOF",
553 },
554
555 {
556 // Including "identity" more than once.
557 payload: `HTTP/1.1 200 OK
558Location: https://golang.org/
559Transfer-Encoding: identity, identity
560Content-Length: 0
561Connection: close
562Content-Type: text/html; charset=UTF-8
563
564` + "0\r\n\r\n",
565 wantErr: `"identity" when present must be the only transfer encoding "identity, identity"`,
566 },
567 }
568
569 for i, tt := range tests {
570 res, err := ReadResponse(bufio.NewReader(strings.NewReader(tt.payload)), nil)
571 if tt.wantErr != "" {
572 if err == nil || !strings.Contains(err.Error(), tt.wantErr) {
573 t.Errorf("test %d. Error mismatch\nGot: %v\nWant: %s", i, err, tt.wantErr)
574 }
575 continue
576 }
577
578 if err != nil {
579 t.Errorf("test %d. Unexpected ReadResponse error: %v\nPayload:\n%s", i, err, tt.payload)
580 continue
581 }
582
583 got, err := ioutil.ReadAll(res.Body)
584 res.Body.Close()
585 if err != nil {
586 t.Errorf("test %d. Failed to read response body: %v", i, err)
587 }
588 if g, w := string(got), tt.wantBody; g != w {
589 t.Errorf("test %d. Response body mimsatch\nGot:\n%s\n\nWant:\n%s", i, g, w)
590 }
591 }
592}