1 // Copyright 2009 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.
25 func TestQuery(t
*testing
.T
) {
26 req
:= &Request
{Method
: "GET"}
27 req
.URL
, _
= url
.Parse("http://www.google.com/search?q=foo&q=bar")
28 if q
:= req
.FormValue("q"); q
!= "foo" {
29 t
.Errorf(`req.FormValue("q") = %q, want "foo"`, q
)
33 func TestParseFormQuery(t
*testing
.T
) {
34 req
, _
:= NewRequest("POST", "http://www.google.com/search?q=foo&q=bar&both=x&prio=1&orphan=nope&empty=not",
35 strings
.NewReader("z=post&both=y&prio=2&=nokey&orphan;empty=&"))
36 req
.Header
.Set("Content-Type", "application/x-www-form-urlencoded; param=value")
38 if q
:= req
.FormValue("q"); q
!= "foo" {
39 t
.Errorf(`req.FormValue("q") = %q, want "foo"`, q
)
41 if z
:= req
.FormValue("z"); z
!= "post" {
42 t
.Errorf(`req.FormValue("z") = %q, want "post"`, z
)
44 if bq
, found
:= req
.PostForm
["q"]; found
{
45 t
.Errorf(`req.PostForm["q"] = %q, want no entry in map`, bq
)
47 if bz
:= req
.PostFormValue("z"); bz
!= "post" {
48 t
.Errorf(`req.PostFormValue("z") = %q, want "post"`, bz
)
50 if qs
:= req
.Form
["q"]; !reflect
.DeepEqual(qs
, []string{"foo", "bar"}) {
51 t
.Errorf(`req.Form["q"] = %q, want ["foo", "bar"]`, qs
)
53 if both
:= req
.Form
["both"]; !reflect
.DeepEqual(both
, []string{"y", "x"}) {
54 t
.Errorf(`req.Form["both"] = %q, want ["y", "x"]`, both
)
56 if prio
:= req
.FormValue("prio"); prio
!= "2" {
57 t
.Errorf(`req.FormValue("prio") = %q, want "2" (from body)`, prio
)
59 if orphan
:= req
.Form
["orphan"]; !reflect
.DeepEqual(orphan
, []string{"", "nope"}) {
60 t
.Errorf(`req.FormValue("orphan") = %q, want "" (from body)`, orphan
)
62 if empty
:= req
.Form
["empty"]; !reflect
.DeepEqual(empty
, []string{"", "not"}) {
63 t
.Errorf(`req.FormValue("empty") = %q, want "" (from body)`, empty
)
65 if nokey
:= req
.Form
[""]; !reflect
.DeepEqual(nokey
, []string{"nokey"}) {
66 t
.Errorf(`req.FormValue("nokey") = %q, want "nokey" (from body)`, nokey
)
70 // Tests that we only parse the form automatically for certain methods.
71 func TestParseFormQueryMethods(t
*testing
.T
) {
72 for _
, method
:= range []string{"POST", "PATCH", "PUT", "FOO"} {
73 req
, _
:= NewRequest(method
, "http://www.google.com/search",
74 strings
.NewReader("foo=bar"))
75 req
.Header
.Set("Content-Type", "application/x-www-form-urlencoded; param=value")
80 if got
:= req
.FormValue("foo"); got
!= want
{
81 t
.Errorf(`for method %s, FormValue("foo") = %q; want %q`, method
, got
, want
)
86 type stringMap
map[string][]string
87 type parseContentTypeTest
struct {
92 var parseContentTypeTests
= []parseContentTypeTest
{
93 {false, stringMap
{"Content-Type": {"text/plain"}}},
94 // Empty content type is legal - may be treated as
95 // application/octet-stream (RFC 7231, section 3.1.1.5)
97 {true, stringMap
{"Content-Type": {"text/plain; boundary="}}},
98 {false, stringMap
{"Content-Type": {"application/unknown"}}},
101 func TestParseFormUnknownContentType(t
*testing
.T
) {
102 for i
, test
:= range parseContentTypeTests
{
105 Header
: Header(test
.contentType
),
106 Body
: ioutil
.NopCloser(strings
.NewReader("body")),
108 err
:= req
.ParseForm()
110 case err
== nil && test
.shouldError
:
111 t
.Errorf("test %d should have returned error", i
)
112 case err
!= nil && !test
.shouldError
:
113 t
.Errorf("test %d should not have returned error, got %v", i
, err
)
118 func TestParseFormInitializeOnError(t
*testing
.T
) {
119 nilBody
, _
:= NewRequest("POST", "http://www.google.com/search?q=foo", nil)
122 {Method
: "GET", URL
: nil},
124 for i
, req
:= range tests
{
125 err
:= req
.ParseForm()
127 t
.Errorf("%d. Form not initialized, error %v", i
, err
)
129 if req
.PostForm
== nil {
130 t
.Errorf("%d. PostForm not initialized, error %v", i
, err
)
135 func TestMultipartReader(t
*testing
.T
) {
138 Header
: Header
{"Content-Type": {`multipart/form-data; boundary="foo123"`}},
139 Body
: ioutil
.NopCloser(new(bytes
.Buffer
)),
141 multipart
, err
:= req
.MultipartReader()
142 if multipart
== nil {
143 t
.Errorf("expected multipart; error: %v", err
)
148 Header
: Header
{"Content-Type": {`multipart/mixed; boundary="foo123"`}},
149 Body
: ioutil
.NopCloser(new(bytes
.Buffer
)),
151 multipart
, err
= req
.MultipartReader()
152 if multipart
== nil {
153 t
.Errorf("expected multipart; error: %v", err
)
156 req
.Header
= Header
{"Content-Type": {"text/plain"}}
157 multipart
, err
= req
.MultipartReader()
158 if multipart
!= nil {
159 t
.Error("unexpected multipart for text/plain")
163 // Issue 9305: ParseMultipartForm should populate PostForm too
164 func TestParseMultipartFormPopulatesPostForm(t
*testing
.T
) {
167 Content-Disposition: form-data; name="field1"
171 Content-Disposition: form-data; name="field2"
175 Content-Disposition: form-data; name="file"; filename="file"
176 Content-Type: application/octet-stream
177 Content-Transfer-Encoding: binary
184 Header
: Header
{"Content-Type": {`multipart/form-data; boundary=xxx`}},
185 Body
: ioutil
.NopCloser(strings
.NewReader(postData
)),
188 initialFormItems
:= map[string]string{
192 "field2": "initial-value2",
195 req
.Form
= make(url
.Values
)
196 for k
, v
:= range initialFormItems
{
200 err
:= req
.ParseMultipartForm(10000)
202 t
.Fatalf("unexpected multipart error %v", err
)
205 wantForm
:= url
.Values
{
206 "language": []string{"Go"},
207 "name": []string{"gopher"},
208 "skill": []string{"go-ing"},
209 "field1": []string{"value1"},
210 "field2": []string{"initial-value2", "value2"},
212 if !reflect
.DeepEqual(req
.Form
, wantForm
) {
213 t
.Fatalf("req.Form = %v, want %v", req
.Form
, wantForm
)
216 wantPostForm
:= url
.Values
{
217 "field1": []string{"value1"},
218 "field2": []string{"value2"},
220 if !reflect
.DeepEqual(req
.PostForm
, wantPostForm
) {
221 t
.Fatalf("req.PostForm = %v, want %v", req
.PostForm
, wantPostForm
)
225 func TestParseMultipartForm(t
*testing
.T
) {
228 Header
: Header
{"Content-Type": {`multipart/form-data; boundary="foo123"`}},
229 Body
: ioutil
.NopCloser(new(bytes
.Buffer
)),
231 err
:= req
.ParseMultipartForm(25)
233 t
.Error("expected multipart EOF, got nil")
236 req
.Header
= Header
{"Content-Type": {"text/plain"}}
237 err
= req
.ParseMultipartForm(25)
238 if err
!= ErrNotMultipart
{
239 t
.Error("expected ErrNotMultipart for text/plain")
243 func TestRedirect_h1(t
*testing
.T
) { testRedirect(t
, h1Mode
) }
244 func TestRedirect_h2(t
*testing
.T
) { testRedirect(t
, h2Mode
) }
245 func testRedirect(t
*testing
.T
, h2
bool) {
247 cst
:= newClientServerTest(t
, h2
, HandlerFunc(func(w ResponseWriter
, r
*Request
) {
250 w
.Header().Set("Location", "/foo/")
251 w
.WriteHeader(StatusSeeOther
)
253 fmt
.Fprintf(w
, "foo")
255 w
.WriteHeader(StatusBadRequest
)
260 var end
= regexp
.MustCompile("/foo/$")
261 r
, err
:= cst
.c
.Get(cst
.ts
.URL
)
266 url
:= r
.Request
.URL
.String()
267 if r
.StatusCode
!= 200 ||
!end
.MatchString(url
) {
268 t
.Fatalf("Get got status %d at %q, want 200 matching /foo/$", r
.StatusCode
, url
)
272 func TestSetBasicAuth(t
*testing
.T
) {
273 r
, _
:= NewRequest("GET", "http://example.com/", nil)
274 r
.SetBasicAuth("Aladdin", "open sesame")
275 if g
, e
:= r
.Header
.Get("Authorization"), "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="; g
!= e
{
276 t
.Errorf("got header %q, want %q", g
, e
)
280 func TestMultipartRequest(t
*testing
.T
) {
281 // Test that we can read the values and files of a
282 // multipart request with FormValue and FormFile,
283 // and that ParseMultipartForm can be called multiple times.
284 req
:= newTestMultipartRequest(t
)
285 if err
:= req
.ParseMultipartForm(25); err
!= nil {
286 t
.Fatal("ParseMultipartForm first call:", err
)
288 defer req
.MultipartForm
.RemoveAll()
289 validateTestMultipartContents(t
, req
, false)
290 if err
:= req
.ParseMultipartForm(25); err
!= nil {
291 t
.Fatal("ParseMultipartForm second call:", err
)
293 validateTestMultipartContents(t
, req
, false)
296 func TestMultipartRequestAuto(t
*testing
.T
) {
297 // Test that FormValue and FormFile automatically invoke
298 // ParseMultipartForm and return the right values.
299 req
:= newTestMultipartRequest(t
)
301 if req
.MultipartForm
!= nil {
302 req
.MultipartForm
.RemoveAll()
305 validateTestMultipartContents(t
, req
, true)
308 func TestMissingFileMultipartRequest(t
*testing
.T
) {
309 // Test that FormFile returns an error if
310 // the named file is missing.
311 req
:= newTestMultipartRequest(t
)
312 testMissingFile(t
, req
)
315 // Test that FormValue invokes ParseMultipartForm.
316 func TestFormValueCallsParseMultipartForm(t
*testing
.T
) {
317 req
, _
:= NewRequest("POST", "http://www.google.com/", strings
.NewReader("z=post"))
318 req
.Header
.Set("Content-Type", "application/x-www-form-urlencoded; param=value")
320 t
.Fatal("Unexpected request Form, want nil")
324 t
.Fatal("ParseMultipartForm not called by FormValue")
328 // Test that FormFile invokes ParseMultipartForm.
329 func TestFormFileCallsParseMultipartForm(t
*testing
.T
) {
330 req
:= newTestMultipartRequest(t
)
332 t
.Fatal("Unexpected request Form, want nil")
336 t
.Fatal("ParseMultipartForm not called by FormFile")
340 // Test that ParseMultipartForm errors if called
341 // after MultipartReader on the same request.
342 func TestParseMultipartFormOrder(t
*testing
.T
) {
343 req
:= newTestMultipartRequest(t
)
344 if _
, err
:= req
.MultipartReader(); err
!= nil {
345 t
.Fatalf("MultipartReader: %v", err
)
347 if err
:= req
.ParseMultipartForm(1024); err
== nil {
348 t
.Fatal("expected an error from ParseMultipartForm after call to MultipartReader")
352 // Test that MultipartReader errors if called
353 // after ParseMultipartForm on the same request.
354 func TestMultipartReaderOrder(t
*testing
.T
) {
355 req
:= newTestMultipartRequest(t
)
356 if err
:= req
.ParseMultipartForm(25); err
!= nil {
357 t
.Fatalf("ParseMultipartForm: %v", err
)
359 defer req
.MultipartForm
.RemoveAll()
360 if _
, err
:= req
.MultipartReader(); err
== nil {
361 t
.Fatal("expected an error from MultipartReader after call to ParseMultipartForm")
365 // Test that FormFile errors if called after
366 // MultipartReader on the same request.
367 func TestFormFileOrder(t
*testing
.T
) {
368 req
:= newTestMultipartRequest(t
)
369 if _
, err
:= req
.MultipartReader(); err
!= nil {
370 t
.Fatalf("MultipartReader: %v", err
)
372 if _
, _
, err
:= req
.FormFile(""); err
== nil {
373 t
.Fatal("expected an error from FormFile after call to MultipartReader")
377 var readRequestErrorTests
= []struct {
383 0: {"GET / HTTP/1.1\r\nheader:foo\r\n\r\n", "", Header
{"Header": {"foo"}}},
384 1: {"GET / HTTP/1.1\r\nheader:foo\r\n", io
.ErrUnexpectedEOF
.Error(), nil},
385 2: {"", io
.EOF
.Error(), nil},
387 in
: "HEAD / HTTP/1.1\r\nContent-Length:4\r\n\r\n",
388 err
: "http: method cannot contain a Content-Length",
391 in
: "HEAD / HTTP/1.1\r\n\r\n",
395 // Multiple Content-Length values should either be
396 // deduplicated if same or reject otherwise
399 in
: "POST / HTTP/1.1\r\nContent-Length: 10\r\nContent-Length: 0\r\n\r\nGopher hey\r\n",
400 err
: "cannot contain multiple Content-Length headers",
403 in
: "POST / HTTP/1.1\r\nContent-Length: 10\r\nContent-Length: 6\r\n\r\nGopher\r\n",
404 err
: "cannot contain multiple Content-Length headers",
407 in
: "PUT / HTTP/1.1\r\nContent-Length: 6 \r\nContent-Length: 6\r\nContent-Length:6\r\n\r\nGopher\r\n",
409 header
: Header
{"Content-Length": {"6"}},
412 in
: "PUT / HTTP/1.1\r\nContent-Length: 1\r\nContent-Length: 6 \r\n\r\n",
413 err
: "cannot contain multiple Content-Length headers",
416 in
: "POST / HTTP/1.1\r\nContent-Length:\r\nContent-Length: 3\r\n\r\n",
417 err
: "cannot contain multiple Content-Length headers",
420 in
: "HEAD / HTTP/1.1\r\nContent-Length:0\r\nContent-Length: 0\r\n\r\n",
421 header
: Header
{"Content-Length": {"0"}},
425 func TestReadRequestErrors(t
*testing
.T
) {
426 for i
, tt
:= range readRequestErrorTests
{
427 req
, err
:= ReadRequest(bufio
.NewReader(strings
.NewReader(tt
.in
)))
430 t
.Errorf("#%d: got nil err; want %q", i
, tt
.err
)
433 if !reflect
.DeepEqual(tt
.header
, req
.Header
) {
434 t
.Errorf("#%d: gotHeader: %q wantHeader: %q", i
, req
.Header
, tt
.header
)
439 if tt
.err
== "" ||
!strings
.Contains(err
.Error(), tt
.err
) {
440 t
.Errorf("%d: got error = %v; want %v", i
, err
, tt
.err
)
445 var newRequestHostTests
= []struct {
448 {"http://www.example.com/", "www.example.com"},
449 {"http://www.example.com:8080/", "www.example.com:8080"},
451 {"http://192.168.0.1/", "192.168.0.1"},
452 {"http://192.168.0.1:8080/", "192.168.0.1:8080"},
453 {"http://192.168.0.1:/", "192.168.0.1"},
455 {"http://[fe80::1]/", "[fe80::1]"},
456 {"http://[fe80::1]:8080/", "[fe80::1]:8080"},
457 {"http://[fe80::1%25en0]/", "[fe80::1%en0]"},
458 {"http://[fe80::1%25en0]:8080/", "[fe80::1%en0]:8080"},
459 {"http://[fe80::1%25en0]:/", "[fe80::1%en0]"},
462 func TestNewRequestHost(t
*testing
.T
) {
463 for i
, tt
:= range newRequestHostTests
{
464 req
, err
:= NewRequest("GET", tt
.in
, nil)
466 t
.Errorf("#%v: %v", i
, err
)
469 if req
.Host
!= tt
.out
{
470 t
.Errorf("got %q; want %q", req
.Host
, tt
.out
)
475 func TestRequestInvalidMethod(t
*testing
.T
) {
476 _
, err
:= NewRequest("bad method", "http://foo.com/", nil)
478 t
.Error("expected error from NewRequest with invalid method")
480 req
, err
:= NewRequest("GET", "http://foo.example/", nil)
484 req
.Method
= "bad method"
485 _
, err
= DefaultClient
.Do(req
)
486 if err
== nil ||
!strings
.Contains(err
.Error(), "invalid method") {
487 t
.Errorf("Transport error = %v; want invalid method", err
)
490 req
, err
= NewRequest("", "http://foo.com/", nil)
492 t
.Errorf("NewRequest(empty method) = %v; want nil", err
)
493 } else if req
.Method
!= "GET" {
494 t
.Errorf("NewRequest(empty method) has method %q; want GET", req
.Method
)
498 func TestNewRequestContentLength(t
*testing
.T
) {
499 readByte
:= func(r io
.Reader
) io
.Reader
{
508 {bytes
.NewReader([]byte("123")), 3},
509 {bytes
.NewBuffer([]byte("1234")), 4},
510 {strings
.NewReader("12345"), 5},
511 {strings
.NewReader(""), 0},
514 // Not detected. During Go 1.8 we tried to make these set to -1, but
515 // due to Issue 18117, we keep these returning 0, even though they're
517 {struct{ io
.Reader
}{strings
.NewReader("xyz")}, 0},
518 {io
.NewSectionReader(strings
.NewReader("x"), 0, 6), 0},
519 {readByte(io
.NewSectionReader(strings
.NewReader("xy"), 0, 6)), 0},
521 for i
, tt
:= range tests
{
522 req
, err
:= NewRequest("POST", "http://localhost/", tt
.r
)
526 if req
.ContentLength
!= tt
.want
{
527 t
.Errorf("test[%d]: ContentLength(%T) = %d; want %d", i
, tt
.r
, req
.ContentLength
, tt
.want
)
532 var parseHTTPVersionTests
= []struct {
537 {"HTTP/0.9", 0, 9, true},
538 {"HTTP/1.0", 1, 0, true},
539 {"HTTP/1.1", 1, 1, true},
540 {"HTTP/3.14", 3, 14, true},
542 {"HTTP", 0, 0, false},
543 {"HTTP/one.one", 0, 0, false},
544 {"HTTP/1.1/", 0, 0, false},
545 {"HTTP/-1,0", 0, 0, false},
546 {"HTTP/0,-1", 0, 0, false},
547 {"HTTP/", 0, 0, false},
548 {"HTTP/1,1", 0, 0, false},
551 func TestParseHTTPVersion(t
*testing
.T
) {
552 for _
, tt
:= range parseHTTPVersionTests
{
553 major
, minor
, ok
:= ParseHTTPVersion(tt
.vers
)
554 if ok
!= tt
.ok || major
!= tt
.major || minor
!= tt
.minor
{
555 type version
struct {
559 t
.Errorf("failed to parse %q, expected: %#v, got %#v", tt
.vers
, version
{tt
.major
, tt
.minor
, tt
.ok
}, version
{major
, minor
, ok
})
564 type getBasicAuthTest
struct {
565 username
, password
string
569 type basicAuthCredentialsTest
struct {
570 username
, password
string
573 var getBasicAuthTests
= []struct {
574 username
, password
string
577 {"Aladdin", "open sesame", true},
578 {"Aladdin", "open:sesame", true},
582 func TestGetBasicAuth(t
*testing
.T
) {
583 for _
, tt
:= range getBasicAuthTests
{
584 r
, _
:= NewRequest("GET", "http://example.com/", nil)
585 r
.SetBasicAuth(tt
.username
, tt
.password
)
586 username
, password
, ok
:= r
.BasicAuth()
587 if ok
!= tt
.ok || username
!= tt
.username || password
!= tt
.password
{
588 t
.Errorf("BasicAuth() = %#v, want %#v", getBasicAuthTest
{username
, password
, ok
},
589 getBasicAuthTest
{tt
.username
, tt
.password
, tt
.ok
})
592 // Unauthenticated request.
593 r
, _
:= NewRequest("GET", "http://example.com/", nil)
594 username
, password
, ok
:= r
.BasicAuth()
596 t
.Errorf("expected false from BasicAuth when the request is unauthenticated")
598 want
:= basicAuthCredentialsTest
{"", ""}
599 if username
!= want
.username || password
!= want
.password
{
600 t
.Errorf("expected credentials: %#v when the request is unauthenticated, got %#v",
601 want
, basicAuthCredentialsTest
{username
, password
})
605 var parseBasicAuthTests
= []struct {
606 header
, username
, password
string
609 {"Basic " + base64
.StdEncoding
.EncodeToString([]byte("Aladdin:open sesame")), "Aladdin", "open sesame", true},
611 // Case doesn't matter:
612 {"BASIC " + base64
.StdEncoding
.EncodeToString([]byte("Aladdin:open sesame")), "Aladdin", "open sesame", true},
613 {"basic " + base64
.StdEncoding
.EncodeToString([]byte("Aladdin:open sesame")), "Aladdin", "open sesame", true},
615 {"Basic " + base64
.StdEncoding
.EncodeToString([]byte("Aladdin:open:sesame")), "Aladdin", "open:sesame", true},
616 {"Basic " + base64
.StdEncoding
.EncodeToString([]byte(":")), "", "", true},
617 {"Basic" + base64
.StdEncoding
.EncodeToString([]byte("Aladdin:open sesame")), "", "", false},
618 {base64
.StdEncoding
.EncodeToString([]byte("Aladdin:open sesame")), "", "", false},
619 {"Basic ", "", "", false},
620 {"Basic Aladdin:open sesame", "", "", false},
621 {`Digest username="Aladdin"`, "", "", false},
624 func TestParseBasicAuth(t
*testing
.T
) {
625 for _
, tt
:= range parseBasicAuthTests
{
626 r
, _
:= NewRequest("GET", "http://example.com/", nil)
627 r
.Header
.Set("Authorization", tt
.header
)
628 username
, password
, ok
:= r
.BasicAuth()
629 if ok
!= tt
.ok || username
!= tt
.username || password
!= tt
.password
{
630 t
.Errorf("BasicAuth() = %#v, want %#v", getBasicAuthTest
{username
, password
, ok
},
631 getBasicAuthTest
{tt
.username
, tt
.password
, tt
.ok
})
636 type logWrites
struct {
641 func (l logWrites
) WriteByte(c
byte) error
{
642 l
.t
.Fatalf("unexpected WriteByte call")
646 func (l logWrites
) Write(p
[]byte) (n
int, err error
) {
647 *l
.dst
= append(*l
.dst
, string(p
))
651 func TestRequestWriteBufferedWriter(t
*testing
.T
) {
653 req
, _
:= NewRequest("GET", "http://foo.com/", nil)
654 req
.Write(logWrites
{t
, &got
})
656 "GET / HTTP/1.1\r\n",
658 "User-Agent: " + DefaultUserAgent
+ "\r\n",
661 if !reflect
.DeepEqual(got
, want
) {
662 t
.Errorf("Writes = %q\n Want = %q", got
, want
)
666 func TestRequestBadHost(t
*testing
.T
) {
668 req
, err
:= NewRequest("GET", "http://foo/after", nil)
672 req
.Host
= "foo.com with spaces"
673 req
.URL
.Host
= "foo.com with spaces"
674 req
.Write(logWrites
{t
, &got
})
676 "GET /after HTTP/1.1\r\n",
678 "User-Agent: " + DefaultUserAgent
+ "\r\n",
681 if !reflect
.DeepEqual(got
, want
) {
682 t
.Errorf("Writes = %q\n Want = %q", got
, want
)
686 func TestStarRequest(t
*testing
.T
) {
687 req
, err
:= ReadRequest(bufio
.NewReader(strings
.NewReader("M-SEARCH * HTTP/1.1\r\n\r\n")))
691 if req
.ContentLength
!= 0 {
692 t
.Errorf("ContentLength = %d; want 0", req
.ContentLength
)
695 t
.Errorf("Body = nil; want non-nil")
698 // Request.Write has Client semantics for Body/ContentLength,
699 // where ContentLength 0 means unknown if Body is non-nil, and
700 // thus chunking will happen unless we change semantics and
701 // signal that we want to serialize it as exactly zero. The
702 // only way to do that for outbound requests is with a nil
708 if err
:= clientReq
.Write(&out
); err
!= nil {
712 if strings
.Contains(out
.String(), "chunked") {
713 t
.Error("wrote chunked request; want no body")
715 back
, err
:= ReadRequest(bufio
.NewReader(bytes
.NewReader(out
.Bytes())))
719 // Ignore the Headers (the User-Agent breaks the deep equal,
720 // but we don't care about it)
723 if !reflect
.DeepEqual(req
, back
) {
724 t
.Errorf("Original request doesn't match Request read back.")
725 t
.Logf("Original: %#v", req
)
726 t
.Logf("Original.URL: %#v", req
.URL
)
727 t
.Logf("Wrote: %s", out
.Bytes())
728 t
.Logf("Read back (doesn't match Original): %#v", back
)
732 type responseWriterJustWriter
struct {
736 func (responseWriterJustWriter
) Header() Header
{ panic("should not be called") }
737 func (responseWriterJustWriter
) WriteHeader(int) { panic("should not be called") }
739 // delayedEOFReader never returns (n > 0, io.EOF), instead putting
740 // off the io.EOF until a subsequent Read call.
741 type delayedEOFReader
struct {
745 func (dr delayedEOFReader
) Read(p
[]byte) (n
int, err error
) {
746 n
, err
= dr
.r
.Read(p
)
747 if n
> 0 && err
== io
.EOF
{
753 func TestIssue10884_MaxBytesEOF(t
*testing
.T
) {
754 dst
:= ioutil
.Discard
755 _
, err
:= io
.Copy(dst
, MaxBytesReader(
756 responseWriterJustWriter
{dst
},
757 ioutil
.NopCloser(delayedEOFReader
{strings
.NewReader("12345")}),
764 // Issue 14981: MaxBytesReader's return error wasn't sticky. It
765 // doesn't technically need to be, but people expected it to be.
766 func TestMaxBytesReaderStickyError(t
*testing
.T
) {
767 isSticky
:= func(r io
.Reader
) error
{
769 buf
:= make([]byte, 1000)
772 n
, err
:= r
.Read(buf
)
773 fmt
.Fprintf(&log
, "Read(%d) = %d, %v\n", len(buf
), n
, err
)
781 if !reflect
.DeepEqual(err
, firstErr
) {
782 return fmt
.Errorf("non-sticky error. got log:\n%s", log
.Bytes())
784 t
.Logf("Got log: %s", log
.Bytes())
788 tests
:= [...]struct {
796 for i
, tt
:= range tests
{
797 rc
:= MaxBytesReader(nil, ioutil
.NopCloser(bytes
.NewReader(make([]byte, tt
.readable
))), tt
.limit
)
798 if err
:= isSticky(rc
); err
!= nil {
799 t
.Errorf("%d. error: %v", i
, err
)
804 func TestWithContextDeepCopiesURL(t
*testing
.T
) {
805 req
, err
:= NewRequest("POST", "https://golang.org/", nil)
810 reqCopy
:= req
.WithContext(context
.Background())
811 reqCopy
.URL
.Scheme
= "http"
813 firstURL
, secondURL
:= req
.URL
.String(), reqCopy
.URL
.String()
814 if firstURL
== secondURL
{
815 t
.Errorf("unexpected change to original request's URL")
818 // And also check we don't crash on nil (Issue 20601)
820 reqCopy
= req
.WithContext(context
.Background())
821 if reqCopy
.URL
!= nil {
822 t
.Error("expected nil URL in cloned request")
826 // verify that NewRequest sets Request.GetBody and that it works
827 func TestNewRequestGetBody(t
*testing
.T
) {
831 {r
: strings
.NewReader("hello")},
832 {r
: bytes
.NewReader([]byte("hello"))},
833 {r
: bytes
.NewBuffer([]byte("hello"))},
835 for i
, tt
:= range tests
{
836 req
, err
:= NewRequest("POST", "http://foo.tld/", tt
.r
)
838 t
.Errorf("test[%d]: %v", i
, err
)
842 t
.Errorf("test[%d]: Body = nil", i
)
845 if req
.GetBody
== nil {
846 t
.Errorf("test[%d]: GetBody = nil", i
)
849 slurp1
, err
:= ioutil
.ReadAll(req
.Body
)
851 t
.Errorf("test[%d]: ReadAll(Body) = %v", i
, err
)
853 newBody
, err
:= req
.GetBody()
855 t
.Errorf("test[%d]: GetBody = %v", i
, err
)
857 slurp2
, err
:= ioutil
.ReadAll(newBody
)
859 t
.Errorf("test[%d]: ReadAll(GetBody()) = %v", i
, err
)
861 if string(slurp1
) != string(slurp2
) {
862 t
.Errorf("test[%d]: Body %q != GetBody %q", i
, slurp1
, slurp2
)
867 func testMissingFile(t
*testing
.T
, req
*Request
) {
868 f
, fh
, err
:= req
.FormFile("missing")
870 t
.Errorf("FormFile file = %v, want nil", f
)
873 t
.Errorf("FormFile file header = %q, want nil", fh
)
875 if err
!= ErrMissingFile
{
876 t
.Errorf("FormFile err = %q, want ErrMissingFile", err
)
880 func newTestMultipartRequest(t
*testing
.T
) *Request
{
881 b
:= strings
.NewReader(strings
.Replace(message
, "\n", "\r\n", -1))
882 req
, err
:= NewRequest("POST", "/", b
)
884 t
.Fatal("NewRequest:", err
)
886 ctype
:= fmt
.Sprintf(`multipart/form-data; boundary="%s"`, boundary
)
887 req
.Header
.Set("Content-type", ctype
)
891 func validateTestMultipartContents(t
*testing
.T
, req
*Request
, allMem
bool) {
892 if g
, e
:= req
.FormValue("texta"), textaValue
; g
!= e
{
893 t
.Errorf("texta value = %q, want %q", g
, e
)
895 if g
, e
:= req
.FormValue("textb"), textbValue
; g
!= e
{
896 t
.Errorf("textb value = %q, want %q", g
, e
)
898 if g
:= req
.FormValue("missing"); g
!= "" {
899 t
.Errorf("missing value = %q, want empty string", g
)
902 assertMem
:= func(n
string, fd multipart
.File
) {
903 if _
, ok
:= fd
.(*os
.File
); ok
{
904 t
.Error(n
, " is *os.File, should not be")
907 fda
:= testMultipartFile(t
, req
, "filea", "filea.txt", fileaContents
)
909 assertMem("filea", fda
)
910 fdb
:= testMultipartFile(t
, req
, "fileb", "fileb.txt", filebContents
)
913 assertMem("fileb", fdb
)
915 if _
, ok
:= fdb
.(*os
.File
); !ok
{
916 t
.Errorf("fileb has unexpected underlying type %T", fdb
)
920 testMissingFile(t
, req
)
923 func testMultipartFile(t
*testing
.T
, req
*Request
, key
, expectFilename
, expectContent
string) multipart
.File
{
924 f
, fh
, err
:= req
.FormFile(key
)
926 t
.Fatalf("FormFile(%q): %q", key
, err
)
928 if fh
.Filename
!= expectFilename
{
929 t
.Errorf("filename = %q, want %q", fh
.Filename
, expectFilename
)
932 _
, err
= io
.Copy(&b
, f
)
934 t
.Fatal("copying contents:", err
)
936 if g
:= b
.String(); g
!= expectContent
{
937 t
.Errorf("contents = %q, want %q", g
, expectContent
)
943 fileaContents
= "This is a test file."
944 filebContents
= "Another test file."
947 boundary
= `MyBoundary`
952 Content-Disposition: form-data; name="filea"; filename="filea.txt"
953 Content-Type: text/plain
955 ` + fileaContents
+ `
957 Content-Disposition: form-data; name="fileb"; filename="fileb.txt"
958 Content-Type: text/plain
960 ` + filebContents
+ `
962 Content-Disposition: form-data; name="texta"
966 Content-Disposition: form-data; name="textb"
972 func benchmarkReadRequest(b
*testing
.B
, request
string) {
973 request
= request
+ "\n" // final \n
974 request
= strings
.Replace(request
, "\n", "\r\n", -1) // expand \n to \r\n
975 b
.SetBytes(int64(len(request
)))
976 r
:= bufio
.NewReader(&infiniteReader
{buf
: []byte(request
)})
979 for i
:= 0; i
< b
.N
; i
++ {
980 _
, err
:= ReadRequest(r
)
982 b
.Fatalf("failed to read request: %v", err
)
987 // infiniteReader satisfies Read requests as if the contents of buf
988 // loop indefinitely.
989 type infiniteReader
struct {
994 func (r
*infiniteReader
) Read(b
[]byte) (int, error
) {
995 n
:= copy(b
, r
.buf
[r
.offset
:])
996 r
.offset
= (r
.offset
+ n
) % len(r
.buf
)
1000 func BenchmarkReadRequestChrome(b
*testing
.B
) {
1001 // https://github.com/felixge/node-http-perf/blob/master/fixtures/get.http
1002 benchmarkReadRequest(b
, `GET / HTTP/1.1
1003 Host: localhost:8080
1004 Connection: keep-alive
1005 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
1006 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17
1007 Accept-Encoding: gzip,deflate,sdch
1008 Accept-Language: en-US,en;q=0.8
1009 Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
1010 Cookie: __utma=1.1978842379.1323102373.1323102373.1323102373.1; EPi:NumberOfVisits=1,2012-02-28T13:42:18; CrmSession=5b707226b9563e1bc69084d07a107c98; plushContainerWidth=100%25; plushNoTopMenu=0; hudson_auto_refresh=false
1014 func BenchmarkReadRequestCurl(b
*testing
.B
) {
1015 // curl http://localhost:8080/
1016 benchmarkReadRequest(b
, `GET / HTTP/1.1
1017 User-Agent: curl/7.27.0
1018 Host: localhost:8080
1023 func BenchmarkReadRequestApachebench(b
*testing
.B
) {
1024 // ab -n 1 -c 1 http://localhost:8080/
1025 benchmarkReadRequest(b
, `GET / HTTP/1.0
1026 Host: localhost:8080
1027 User-Agent: ApacheBench/2.3
1032 func BenchmarkReadRequestSiege(b
*testing
.B
) {
1033 // siege -r 1 -c 1 http://localhost:8080/
1034 benchmarkReadRequest(b
, `GET / HTTP/1.1
1035 Host: localhost:8080
1037 Accept-Encoding: gzip
1038 User-Agent: JoeDog/1.00 [en] (X11; I; Siege 2.70)
1039 Connection: keep-alive
1043 func BenchmarkReadRequestWrk(b
*testing
.B
) {
1044 // wrk -t 1 -r 1 -c 1 http://localhost:8080/
1045 benchmarkReadRequest(b
, `GET / HTTP/1.1
1046 Host: localhost:8080