1 // Copyright 2010 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.
21 type respTest
struct {
27 func dummyReq(method
string) *Request
{
28 return &Request
{Method
: method
}
31 var respTests
= []respTest
{
32 // Unchunked response without Content-Length.
34 "HTTP/1.0 200 OK\r\n" +
35 "Connection: close\r\n" +
45 Request
: dummyReq("GET"),
47 "Connection": {"close"}, // TODO(rsc): Delete?
56 // Unchunked HTTP/1.1 response without Content-Length or
57 // Connection headers.
59 "HTTP/1.1 200 OK\r\n" +
70 Request
: dummyReq("GET"),
78 // Unchunked HTTP/1.1 204 response without Content-Length.
80 "HTTP/1.1 204 No Content\r\n" +
82 "Body should not be read!\n",
85 Status
: "204 No Content",
91 Request
: dummyReq("GET"),
99 // Unchunked response with Content-Length.
101 "HTTP/1.0 200 OK\r\n" +
102 "Content-Length: 10\r\n" +
103 "Connection: close\r\n" +
113 Request
: dummyReq("GET"),
115 "Connection": {"close"},
116 "Content-Length": {"10"},
125 // Chunked response without Content-Length.
127 "HTTP/1.1 200 OK\r\n" +
128 "Transfer-Encoding: chunked\r\n" +
143 Request
: dummyReq("GET"),
147 TransferEncoding
: []string{"chunked"},
150 "Body here\ncontinued",
153 // Chunked response with Content-Length.
155 "HTTP/1.1 200 OK\r\n" +
156 "Transfer-Encoding: chunked\r\n" +
157 "Content-Length: 10\r\n" +
170 Request
: dummyReq("GET"),
174 TransferEncoding
: []string{"chunked"},
180 // Chunked response in response to a HEAD request
182 "HTTP/1.1 200 OK\r\n" +
183 "Transfer-Encoding: chunked\r\n" +
192 Request
: dummyReq("HEAD"),
194 TransferEncoding
: []string{"chunked"},
202 // Content-Length in response to a HEAD request
204 "HTTP/1.0 200 OK\r\n" +
205 "Content-Length: 256\r\n" +
214 Request
: dummyReq("HEAD"),
215 Header
: Header
{"Content-Length": {"256"}},
216 TransferEncoding
: nil,
224 // Content-Length in response to a HEAD request with HTTP/1.1
226 "HTTP/1.1 200 OK\r\n" +
227 "Content-Length: 256\r\n" +
236 Request
: dummyReq("HEAD"),
237 Header
: Header
{"Content-Length": {"256"}},
238 TransferEncoding
: nil,
246 // No Content-Length or Chunked in response to a HEAD request
248 "HTTP/1.0 200 OK\r\n" +
257 Request
: dummyReq("HEAD"),
259 TransferEncoding
: nil,
267 // explicit Content-Length of 0.
269 "HTTP/1.1 200 OK\r\n" +
270 "Content-Length: 0\r\n" +
279 Request
: dummyReq("GET"),
281 "Content-Length": {"0"},
290 // Status line without a Reason-Phrase, but trailing space.
291 // (permitted by RFC 2616)
293 "HTTP/1.0 303 \r\n\r\n",
300 Request
: dummyReq("GET"),
309 // Status line without a Reason-Phrase, and no trailing space.
310 // (not permitted by RFC 2616, but we'll accept it anyway)
312 "HTTP/1.0 303\r\n\r\n",
319 Request
: dummyReq("GET"),
328 // golang.org/issue/4767: don't special-case multipart/byteranges responses
330 `HTTP/1.1 206 Partial Content
332 Content-Type: multipart/byteranges; boundary=18a75608c8f47cef
336 Status
: "206 Partial Content",
341 Request
: dummyReq("GET"),
343 "Content-Type": []string{"multipart/byteranges; boundary=18a75608c8f47cef"},
353 func TestReadResponse(t
*testing
.T
) {
354 for i
, tt
:= range respTests
{
355 resp
, err
:= ReadResponse(bufio
.NewReader(strings
.NewReader(tt
.Raw
)), tt
.Resp
.Request
)
357 t
.Errorf("#%d: %v", i
, err
)
362 diff(t
, fmt
.Sprintf("#%d Response", i
), resp
, &tt
.Resp
)
363 var bout bytes
.Buffer
365 _
, err
= io
.Copy(&bout
, rbody
)
367 t
.Errorf("#%d: %v", i
, err
)
372 body
:= bout
.String()
374 t
.Errorf("#%d: Body = %q want %q", i
, body
, tt
.Body
)
379 func TestWriteResponse(t
*testing
.T
) {
380 for i
, tt
:= range respTests
{
381 resp
, err
:= ReadResponse(bufio
.NewReader(strings
.NewReader(tt
.Raw
)), tt
.Resp
.Request
)
383 t
.Errorf("#%d: %v", i
, err
)
386 bout
:= bytes
.NewBuffer(nil)
387 err
= resp
.Write(bout
)
389 t
.Errorf("#%d: %v", i
, err
)
395 var readResponseCloseInMiddleTests
= []struct {
396 chunked
, compressed
bool
403 // TestReadResponseCloseInMiddle tests that closing a body after
404 // reading only part of its contents advances the read to the end of
405 // the request, right up until the next request.
406 func TestReadResponseCloseInMiddle(t
*testing
.T
) {
407 for _
, test
:= range readResponseCloseInMiddleTests
{
408 fatalf
:= func(format
string, args
...interface{}) {
409 args
= append([]interface{}{test
.chunked
, test
.compressed
}, args
...)
410 t
.Fatalf("on test chunked=%v, compressed=%v: "+format
, args
...)
412 checkErr
:= func(err error
, msg
string) {
416 fatalf(msg
+": %v", err
)
419 buf
.WriteString("HTTP/1.1 200 OK\r\n")
421 buf
.WriteString("Transfer-Encoding: chunked\r\n")
423 buf
.WriteString("Content-Length: 1000000\r\n")
425 var wr io
.Writer
= &buf
427 wr
= newChunkedWriter(wr
)
430 buf
.WriteString("Content-Encoding: gzip\r\n")
431 wr
= gzip
.NewWriter(wr
)
433 buf
.WriteString("\r\n")
435 chunk
:= bytes
.Repeat([]byte{'x'}, 1000)
436 for i
:= 0; i
< 1000; i
++ {
438 // Otherwise this compresses too well.
439 _
, err
:= io
.ReadFull(rand
.Reader
, chunk
)
440 checkErr(err
, "rand.Reader ReadFull")
445 err
:= wr
.(*gzip
.Writer
).Close()
446 checkErr(err
, "compressor close")
449 buf
.WriteString("0\r\n\r\n")
451 buf
.WriteString("Next Request Here")
453 bufr
:= bufio
.NewReader(&buf
)
454 resp
, err
:= ReadResponse(bufr
, dummyReq("GET"))
455 checkErr(err
, "ReadResponse")
456 expectedLength
:= int64(-1)
458 expectedLength
= 1000000
460 if resp
.ContentLength
!= expectedLength
{
461 fatalf("expected response length %d, got %d", expectedLength
, resp
.ContentLength
)
463 if resp
.Body
== nil {
467 gzReader
, err
:= gzip
.NewReader(resp
.Body
)
468 checkErr(err
, "gzip.NewReader")
469 resp
.Body
= &readerAndCloser
{gzReader
, resp
.Body
}
472 rbuf
:= make([]byte, 2500)
473 n
, err
:= io
.ReadFull(resp
.Body
, rbuf
)
474 checkErr(err
, "2500 byte ReadFull")
476 fatalf("ReadFull only read %d bytes", n
)
478 if test
.compressed
== false && !bytes
.Equal(bytes
.Repeat([]byte{'x'}, 2500), rbuf
) {
479 fatalf("ReadFull didn't read 2500 'x'; got %q", string(rbuf
))
483 rest
, err
:= ioutil
.ReadAll(bufr
)
484 checkErr(err
, "ReadAll on remainder")
485 if e
, g
:= "Next Request Here", string(rest
); e
!= g
{
486 fatalf("remainder = %q, expected %q", g
, e
)
491 func diff(t
*testing
.T
, prefix
string, have
, want
interface{}) {
492 hv
:= reflect
.ValueOf(have
).Elem()
493 wv
:= reflect
.ValueOf(want
).Elem()
494 if hv
.Type() != wv
.Type() {
495 t
.Errorf("%s: type mismatch %v want %v", prefix
, hv
.Type(), wv
.Type())
497 for i
:= 0; i
< hv
.NumField(); i
++ {
498 hf
:= hv
.Field(i
).Interface()
499 wf
:= wv
.Field(i
).Interface()
500 if !reflect
.DeepEqual(hf
, wf
) {
501 t
.Errorf("%s: %s = %v want %v", prefix
, hv
.Type().Field(i
).Name
, hf
, wf
)
506 type responseLocationTest
struct {
507 location
string // Response's Location header or ""
508 requrl
string // Response.Request.URL or ""
513 var responseLocationTests
= []responseLocationTest
{
514 {"/foo", "http://bar.com/baz", "http://bar.com/foo", nil},
515 {"http://foo.com/", "http://bar.com/baz", "http://foo.com/", nil},
516 {"", "http://bar.com/baz", "", ErrNoLocation
},
519 func TestLocationResponse(t
*testing
.T
) {
520 for i
, tt
:= range responseLocationTests
{
522 res
.Header
= make(Header
)
523 res
.Header
.Set("Location", tt
.location
)
525 res
.Request
= &Request
{}
527 res
.Request
.URL
, err
= url
.Parse(tt
.requrl
)
529 t
.Fatalf("bad test URL %q: %v", tt
.requrl
, err
)
533 got
, err
:= res
.Location()
534 if tt
.wantErr
!= nil {
536 t
.Errorf("%d. err=nil; want %q", i
, tt
.wantErr
)
539 if g
, e
:= err
.Error(), tt
.wantErr
.Error(); g
!= e
{
540 t
.Errorf("%d. err=%q; want %q", i
, g
, e
)
546 t
.Errorf("%d. err=%q", i
, err
)
549 if g
, e
:= got
.String(), tt
.want
; g
!= e
{
550 t
.Errorf("%d. Location=%q; want %q", i
, g
, e
)
555 func TestResponseStatusStutter(t
*testing
.T
) {
557 Status
: "123 some status",
564 if strings
.Contains(buf
.String(), "123 123") {
565 t
.Errorf("stutter in status: %s", buf
.String())