github.com/corona10/go@v0.0.0-20180224231303-7a218942be57/src/net/http/request_test.go (about) 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. 4 5 package http_test 6 7 import ( 8 "bufio" 9 "bytes" 10 "context" 11 "encoding/base64" 12 "fmt" 13 "io" 14 "io/ioutil" 15 "mime/multipart" 16 . "net/http" 17 "net/url" 18 "os" 19 "reflect" 20 "regexp" 21 "strings" 22 "testing" 23 ) 24 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) 30 } 31 } 32 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") 37 38 if q := req.FormValue("q"); q != "foo" { 39 t.Errorf(`req.FormValue("q") = %q, want "foo"`, q) 40 } 41 if z := req.FormValue("z"); z != "post" { 42 t.Errorf(`req.FormValue("z") = %q, want "post"`, z) 43 } 44 if bq, found := req.PostForm["q"]; found { 45 t.Errorf(`req.PostForm["q"] = %q, want no entry in map`, bq) 46 } 47 if bz := req.PostFormValue("z"); bz != "post" { 48 t.Errorf(`req.PostFormValue("z") = %q, want "post"`, bz) 49 } 50 if qs := req.Form["q"]; !reflect.DeepEqual(qs, []string{"foo", "bar"}) { 51 t.Errorf(`req.Form["q"] = %q, want ["foo", "bar"]`, qs) 52 } 53 if both := req.Form["both"]; !reflect.DeepEqual(both, []string{"y", "x"}) { 54 t.Errorf(`req.Form["both"] = %q, want ["y", "x"]`, both) 55 } 56 if prio := req.FormValue("prio"); prio != "2" { 57 t.Errorf(`req.FormValue("prio") = %q, want "2" (from body)`, prio) 58 } 59 if orphan := req.Form["orphan"]; !reflect.DeepEqual(orphan, []string{"", "nope"}) { 60 t.Errorf(`req.FormValue("orphan") = %q, want "" (from body)`, orphan) 61 } 62 if empty := req.Form["empty"]; !reflect.DeepEqual(empty, []string{"", "not"}) { 63 t.Errorf(`req.FormValue("empty") = %q, want "" (from body)`, empty) 64 } 65 if nokey := req.Form[""]; !reflect.DeepEqual(nokey, []string{"nokey"}) { 66 t.Errorf(`req.FormValue("nokey") = %q, want "nokey" (from body)`, nokey) 67 } 68 } 69 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") 76 want := "bar" 77 if method == "FOO" { 78 want = "" 79 } 80 if got := req.FormValue("foo"); got != want { 81 t.Errorf(`for method %s, FormValue("foo") = %q; want %q`, method, got, want) 82 } 83 } 84 } 85 86 type stringMap map[string][]string 87 type parseContentTypeTest struct { 88 shouldError bool 89 contentType stringMap 90 } 91 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) 96 {false, stringMap{}}, 97 {true, stringMap{"Content-Type": {"text/plain; boundary="}}}, 98 {false, stringMap{"Content-Type": {"application/unknown"}}}, 99 } 100 101 func TestParseFormUnknownContentType(t *testing.T) { 102 for i, test := range parseContentTypeTests { 103 req := &Request{ 104 Method: "POST", 105 Header: Header(test.contentType), 106 Body: ioutil.NopCloser(strings.NewReader("body")), 107 } 108 err := req.ParseForm() 109 switch { 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) 114 } 115 } 116 } 117 118 func TestParseFormInitializeOnError(t *testing.T) { 119 nilBody, _ := NewRequest("POST", "http://www.google.com/search?q=foo", nil) 120 tests := []*Request{ 121 nilBody, 122 {Method: "GET", URL: nil}, 123 } 124 for i, req := range tests { 125 err := req.ParseForm() 126 if req.Form == nil { 127 t.Errorf("%d. Form not initialized, error %v", i, err) 128 } 129 if req.PostForm == nil { 130 t.Errorf("%d. PostForm not initialized, error %v", i, err) 131 } 132 } 133 } 134 135 func TestMultipartReader(t *testing.T) { 136 req := &Request{ 137 Method: "POST", 138 Header: Header{"Content-Type": {`multipart/form-data; boundary="foo123"`}}, 139 Body: ioutil.NopCloser(new(bytes.Buffer)), 140 } 141 multipart, err := req.MultipartReader() 142 if multipart == nil { 143 t.Errorf("expected multipart; error: %v", err) 144 } 145 146 req = &Request{ 147 Method: "POST", 148 Header: Header{"Content-Type": {`multipart/mixed; boundary="foo123"`}}, 149 Body: ioutil.NopCloser(new(bytes.Buffer)), 150 } 151 multipart, err = req.MultipartReader() 152 if multipart == nil { 153 t.Errorf("expected multipart; error: %v", err) 154 } 155 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") 160 } 161 } 162 163 // Issue 9305: ParseMultipartForm should populate PostForm too 164 func TestParseMultipartFormPopulatesPostForm(t *testing.T) { 165 postData := 166 `--xxx 167 Content-Disposition: form-data; name="field1" 168 169 value1 170 --xxx 171 Content-Disposition: form-data; name="field2" 172 173 value2 174 --xxx 175 Content-Disposition: form-data; name="file"; filename="file" 176 Content-Type: application/octet-stream 177 Content-Transfer-Encoding: binary 178 179 binary data 180 --xxx-- 181 ` 182 req := &Request{ 183 Method: "POST", 184 Header: Header{"Content-Type": {`multipart/form-data; boundary=xxx`}}, 185 Body: ioutil.NopCloser(strings.NewReader(postData)), 186 } 187 188 initialFormItems := map[string]string{ 189 "language": "Go", 190 "name": "gopher", 191 "skill": "go-ing", 192 "field2": "initial-value2", 193 } 194 195 req.Form = make(url.Values) 196 for k, v := range initialFormItems { 197 req.Form.Add(k, v) 198 } 199 200 err := req.ParseMultipartForm(10000) 201 if err != nil { 202 t.Fatalf("unexpected multipart error %v", err) 203 } 204 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"}, 211 } 212 if !reflect.DeepEqual(req.Form, wantForm) { 213 t.Fatalf("req.Form = %v, want %v", req.Form, wantForm) 214 } 215 216 wantPostForm := url.Values{ 217 "field1": []string{"value1"}, 218 "field2": []string{"value2"}, 219 } 220 if !reflect.DeepEqual(req.PostForm, wantPostForm) { 221 t.Fatalf("req.PostForm = %v, want %v", req.PostForm, wantPostForm) 222 } 223 } 224 225 func TestParseMultipartForm(t *testing.T) { 226 req := &Request{ 227 Method: "POST", 228 Header: Header{"Content-Type": {`multipart/form-data; boundary="foo123"`}}, 229 Body: ioutil.NopCloser(new(bytes.Buffer)), 230 } 231 err := req.ParseMultipartForm(25) 232 if err == nil { 233 t.Error("expected multipart EOF, got nil") 234 } 235 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") 240 } 241 } 242 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) { 246 defer afterTest(t) 247 cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) { 248 switch r.URL.Path { 249 case "/": 250 w.Header().Set("Location", "/foo/") 251 w.WriteHeader(StatusSeeOther) 252 case "/foo/": 253 fmt.Fprintf(w, "foo") 254 default: 255 w.WriteHeader(StatusBadRequest) 256 } 257 })) 258 defer cst.close() 259 260 var end = regexp.MustCompile("/foo/$") 261 r, err := cst.c.Get(cst.ts.URL) 262 if err != nil { 263 t.Fatal(err) 264 } 265 r.Body.Close() 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) 269 } 270 } 271 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) 277 } 278 } 279 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) 287 } 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) 292 } 293 validateTestMultipartContents(t, req, false) 294 } 295 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) 300 defer func() { 301 if req.MultipartForm != nil { 302 req.MultipartForm.RemoveAll() 303 } 304 }() 305 validateTestMultipartContents(t, req, true) 306 } 307 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) 313 } 314 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") 319 if req.Form != nil { 320 t.Fatal("Unexpected request Form, want nil") 321 } 322 req.FormValue("z") 323 if req.Form == nil { 324 t.Fatal("ParseMultipartForm not called by FormValue") 325 } 326 } 327 328 // Test that FormFile invokes ParseMultipartForm. 329 func TestFormFileCallsParseMultipartForm(t *testing.T) { 330 req := newTestMultipartRequest(t) 331 if req.Form != nil { 332 t.Fatal("Unexpected request Form, want nil") 333 } 334 req.FormFile("") 335 if req.Form == nil { 336 t.Fatal("ParseMultipartForm not called by FormFile") 337 } 338 } 339 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) 346 } 347 if err := req.ParseMultipartForm(1024); err == nil { 348 t.Fatal("expected an error from ParseMultipartForm after call to MultipartReader") 349 } 350 } 351 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) 358 } 359 defer req.MultipartForm.RemoveAll() 360 if _, err := req.MultipartReader(); err == nil { 361 t.Fatal("expected an error from MultipartReader after call to ParseMultipartForm") 362 } 363 } 364 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) 371 } 372 if _, _, err := req.FormFile(""); err == nil { 373 t.Fatal("expected an error from FormFile after call to MultipartReader") 374 } 375 } 376 377 var readRequestErrorTests = []struct { 378 in string 379 err string 380 381 header Header 382 }{ 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}, 386 3: { 387 in: "HEAD / HTTP/1.1\r\nContent-Length:4\r\n\r\n", 388 err: "http: method cannot contain a Content-Length", 389 }, 390 4: { 391 in: "HEAD / HTTP/1.1\r\n\r\n", 392 header: Header{}, 393 }, 394 395 // Multiple Content-Length values should either be 396 // deduplicated if same or reject otherwise 397 // See Issue 16490. 398 5: { 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", 401 }, 402 6: { 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", 405 }, 406 7: { 407 in: "PUT / HTTP/1.1\r\nContent-Length: 6 \r\nContent-Length: 6\r\nContent-Length:6\r\n\r\nGopher\r\n", 408 err: "", 409 header: Header{"Content-Length": {"6"}}, 410 }, 411 8: { 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", 414 }, 415 9: { 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", 418 }, 419 10: { 420 in: "HEAD / HTTP/1.1\r\nContent-Length:0\r\nContent-Length: 0\r\n\r\n", 421 header: Header{"Content-Length": {"0"}}, 422 }, 423 } 424 425 func TestReadRequestErrors(t *testing.T) { 426 for i, tt := range readRequestErrorTests { 427 req, err := ReadRequest(bufio.NewReader(strings.NewReader(tt.in))) 428 if err == nil { 429 if tt.err != "" { 430 t.Errorf("#%d: got nil err; want %q", i, tt.err) 431 } 432 433 if !reflect.DeepEqual(tt.header, req.Header) { 434 t.Errorf("#%d: gotHeader: %q wantHeader: %q", i, req.Header, tt.header) 435 } 436 continue 437 } 438 439 if tt.err == "" || !strings.Contains(err.Error(), tt.err) { 440 t.Errorf("%d: got error = %v; want %v", i, err, tt.err) 441 } 442 } 443 } 444 445 var newRequestHostTests = []struct { 446 in, out string 447 }{ 448 {"http://www.example.com/", "www.example.com"}, 449 {"http://www.example.com:8080/", "www.example.com:8080"}, 450 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"}, 454 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]"}, 460 } 461 462 func TestNewRequestHost(t *testing.T) { 463 for i, tt := range newRequestHostTests { 464 req, err := NewRequest("GET", tt.in, nil) 465 if err != nil { 466 t.Errorf("#%v: %v", i, err) 467 continue 468 } 469 if req.Host != tt.out { 470 t.Errorf("got %q; want %q", req.Host, tt.out) 471 } 472 } 473 } 474 475 func TestRequestInvalidMethod(t *testing.T) { 476 _, err := NewRequest("bad method", "http://foo.com/", nil) 477 if err == nil { 478 t.Error("expected error from NewRequest with invalid method") 479 } 480 req, err := NewRequest("GET", "http://foo.example/", nil) 481 if err != nil { 482 t.Fatal(err) 483 } 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) 488 } 489 490 req, err = NewRequest("", "http://foo.com/", nil) 491 if err != 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) 495 } 496 } 497 498 func TestNewRequestContentLength(t *testing.T) { 499 readByte := func(r io.Reader) io.Reader { 500 var b [1]byte 501 r.Read(b[:]) 502 return r 503 } 504 tests := []struct { 505 r io.Reader 506 want int64 507 }{ 508 {bytes.NewReader([]byte("123")), 3}, 509 {bytes.NewBuffer([]byte("1234")), 4}, 510 {strings.NewReader("12345"), 5}, 511 {strings.NewReader(""), 0}, 512 {NoBody, 0}, 513 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 516 // unknown. 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}, 520 } 521 for i, tt := range tests { 522 req, err := NewRequest("POST", "http://localhost/", tt.r) 523 if err != nil { 524 t.Fatal(err) 525 } 526 if req.ContentLength != tt.want { 527 t.Errorf("test[%d]: ContentLength(%T) = %d; want %d", i, tt.r, req.ContentLength, tt.want) 528 } 529 } 530 } 531 532 var parseHTTPVersionTests = []struct { 533 vers string 534 major, minor int 535 ok bool 536 }{ 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}, 541 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}, 549 } 550 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 { 556 major, minor int 557 ok bool 558 } 559 t.Errorf("failed to parse %q, expected: %#v, got %#v", tt.vers, version{tt.major, tt.minor, tt.ok}, version{major, minor, ok}) 560 } 561 } 562 } 563 564 type getBasicAuthTest struct { 565 username, password string 566 ok bool 567 } 568 569 type basicAuthCredentialsTest struct { 570 username, password string 571 } 572 573 var getBasicAuthTests = []struct { 574 username, password string 575 ok bool 576 }{ 577 {"Aladdin", "open sesame", true}, 578 {"Aladdin", "open:sesame", true}, 579 {"", "", true}, 580 } 581 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}) 590 } 591 } 592 // Unauthenticated request. 593 r, _ := NewRequest("GET", "http://example.com/", nil) 594 username, password, ok := r.BasicAuth() 595 if ok { 596 t.Errorf("expected false from BasicAuth when the request is unauthenticated") 597 } 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}) 602 } 603 } 604 605 var parseBasicAuthTests = []struct { 606 header, username, password string 607 ok bool 608 }{ 609 {"Basic " + base64.StdEncoding.EncodeToString([]byte("Aladdin:open sesame")), "Aladdin", "open sesame", true}, 610 {"Basic " + base64.StdEncoding.EncodeToString([]byte("Aladdin:open:sesame")), "Aladdin", "open:sesame", true}, 611 {"Basic " + base64.StdEncoding.EncodeToString([]byte(":")), "", "", true}, 612 {"Basic" + base64.StdEncoding.EncodeToString([]byte("Aladdin:open sesame")), "", "", false}, 613 {base64.StdEncoding.EncodeToString([]byte("Aladdin:open sesame")), "", "", false}, 614 {"Basic ", "", "", false}, 615 {"Basic Aladdin:open sesame", "", "", false}, 616 {`Digest username="Aladdin"`, "", "", false}, 617 } 618 619 func TestParseBasicAuth(t *testing.T) { 620 for _, tt := range parseBasicAuthTests { 621 r, _ := NewRequest("GET", "http://example.com/", nil) 622 r.Header.Set("Authorization", tt.header) 623 username, password, ok := r.BasicAuth() 624 if ok != tt.ok || username != tt.username || password != tt.password { 625 t.Errorf("BasicAuth() = %#v, want %#v", getBasicAuthTest{username, password, ok}, 626 getBasicAuthTest{tt.username, tt.password, tt.ok}) 627 } 628 } 629 } 630 631 type logWrites struct { 632 t *testing.T 633 dst *[]string 634 } 635 636 func (l logWrites) WriteByte(c byte) error { 637 l.t.Fatalf("unexpected WriteByte call") 638 return nil 639 } 640 641 func (l logWrites) Write(p []byte) (n int, err error) { 642 *l.dst = append(*l.dst, string(p)) 643 return len(p), nil 644 } 645 646 func TestRequestWriteBufferedWriter(t *testing.T) { 647 got := []string{} 648 req, _ := NewRequest("GET", "http://foo.com/", nil) 649 req.Write(logWrites{t, &got}) 650 want := []string{ 651 "GET / HTTP/1.1\r\n", 652 "Host: foo.com\r\n", 653 "User-Agent: " + DefaultUserAgent + "\r\n", 654 "\r\n", 655 } 656 if !reflect.DeepEqual(got, want) { 657 t.Errorf("Writes = %q\n Want = %q", got, want) 658 } 659 } 660 661 func TestRequestBadHost(t *testing.T) { 662 got := []string{} 663 req, err := NewRequest("GET", "http://foo/after", nil) 664 if err != nil { 665 t.Fatal(err) 666 } 667 req.Host = "foo.com with spaces" 668 req.URL.Host = "foo.com with spaces" 669 req.Write(logWrites{t, &got}) 670 want := []string{ 671 "GET /after HTTP/1.1\r\n", 672 "Host: foo.com\r\n", 673 "User-Agent: " + DefaultUserAgent + "\r\n", 674 "\r\n", 675 } 676 if !reflect.DeepEqual(got, want) { 677 t.Errorf("Writes = %q\n Want = %q", got, want) 678 } 679 } 680 681 func TestStarRequest(t *testing.T) { 682 req, err := ReadRequest(bufio.NewReader(strings.NewReader("M-SEARCH * HTTP/1.1\r\n\r\n"))) 683 if err != nil { 684 return 685 } 686 if req.ContentLength != 0 { 687 t.Errorf("ContentLength = %d; want 0", req.ContentLength) 688 } 689 if req.Body == nil { 690 t.Errorf("Body = nil; want non-nil") 691 } 692 693 // Request.Write has Client semantics for Body/ContentLength, 694 // where ContentLength 0 means unknown if Body is non-nil, and 695 // thus chunking will happen unless we change semantics and 696 // signal that we want to serialize it as exactly zero. The 697 // only way to do that for outbound requests is with a nil 698 // Body: 699 clientReq := *req 700 clientReq.Body = nil 701 702 var out bytes.Buffer 703 if err := clientReq.Write(&out); err != nil { 704 t.Fatal(err) 705 } 706 707 if strings.Contains(out.String(), "chunked") { 708 t.Error("wrote chunked request; want no body") 709 } 710 back, err := ReadRequest(bufio.NewReader(bytes.NewReader(out.Bytes()))) 711 if err != nil { 712 t.Fatal(err) 713 } 714 // Ignore the Headers (the User-Agent breaks the deep equal, 715 // but we don't care about it) 716 req.Header = nil 717 back.Header = nil 718 if !reflect.DeepEqual(req, back) { 719 t.Errorf("Original request doesn't match Request read back.") 720 t.Logf("Original: %#v", req) 721 t.Logf("Original.URL: %#v", req.URL) 722 t.Logf("Wrote: %s", out.Bytes()) 723 t.Logf("Read back (doesn't match Original): %#v", back) 724 } 725 } 726 727 type responseWriterJustWriter struct { 728 io.Writer 729 } 730 731 func (responseWriterJustWriter) Header() Header { panic("should not be called") } 732 func (responseWriterJustWriter) WriteHeader(int) { panic("should not be called") } 733 734 // delayedEOFReader never returns (n > 0, io.EOF), instead putting 735 // off the io.EOF until a subsequent Read call. 736 type delayedEOFReader struct { 737 r io.Reader 738 } 739 740 func (dr delayedEOFReader) Read(p []byte) (n int, err error) { 741 n, err = dr.r.Read(p) 742 if n > 0 && err == io.EOF { 743 err = nil 744 } 745 return 746 } 747 748 func TestIssue10884_MaxBytesEOF(t *testing.T) { 749 dst := ioutil.Discard 750 _, err := io.Copy(dst, MaxBytesReader( 751 responseWriterJustWriter{dst}, 752 ioutil.NopCloser(delayedEOFReader{strings.NewReader("12345")}), 753 5)) 754 if err != nil { 755 t.Fatal(err) 756 } 757 } 758 759 // Issue 14981: MaxBytesReader's return error wasn't sticky. It 760 // doesn't technically need to be, but people expected it to be. 761 func TestMaxBytesReaderStickyError(t *testing.T) { 762 isSticky := func(r io.Reader) error { 763 var log bytes.Buffer 764 buf := make([]byte, 1000) 765 var firstErr error 766 for { 767 n, err := r.Read(buf) 768 fmt.Fprintf(&log, "Read(%d) = %d, %v\n", len(buf), n, err) 769 if err == nil { 770 continue 771 } 772 if firstErr == nil { 773 firstErr = err 774 continue 775 } 776 if !reflect.DeepEqual(err, firstErr) { 777 return fmt.Errorf("non-sticky error. got log:\n%s", log.Bytes()) 778 } 779 t.Logf("Got log: %s", log.Bytes()) 780 return nil 781 } 782 } 783 tests := [...]struct { 784 readable int 785 limit int64 786 }{ 787 0: {99, 100}, 788 1: {100, 100}, 789 2: {101, 100}, 790 } 791 for i, tt := range tests { 792 rc := MaxBytesReader(nil, ioutil.NopCloser(bytes.NewReader(make([]byte, tt.readable))), tt.limit) 793 if err := isSticky(rc); err != nil { 794 t.Errorf("%d. error: %v", i, err) 795 } 796 } 797 } 798 799 func TestWithContextDeepCopiesURL(t *testing.T) { 800 req, err := NewRequest("POST", "https://golang.org/", nil) 801 if err != nil { 802 t.Fatal(err) 803 } 804 805 reqCopy := req.WithContext(context.Background()) 806 reqCopy.URL.Scheme = "http" 807 808 firstURL, secondURL := req.URL.String(), reqCopy.URL.String() 809 if firstURL == secondURL { 810 t.Errorf("unexpected change to original request's URL") 811 } 812 813 // And also check we don't crash on nil (Issue 20601) 814 req.URL = nil 815 reqCopy = req.WithContext(context.Background()) 816 if reqCopy.URL != nil { 817 t.Error("expected nil URL in cloned request") 818 } 819 } 820 821 // verify that NewRequest sets Request.GetBody and that it works 822 func TestNewRequestGetBody(t *testing.T) { 823 tests := []struct { 824 r io.Reader 825 }{ 826 {r: strings.NewReader("hello")}, 827 {r: bytes.NewReader([]byte("hello"))}, 828 {r: bytes.NewBuffer([]byte("hello"))}, 829 } 830 for i, tt := range tests { 831 req, err := NewRequest("POST", "http://foo.tld/", tt.r) 832 if err != nil { 833 t.Errorf("test[%d]: %v", i, err) 834 continue 835 } 836 if req.Body == nil { 837 t.Errorf("test[%d]: Body = nil", i) 838 continue 839 } 840 if req.GetBody == nil { 841 t.Errorf("test[%d]: GetBody = nil", i) 842 continue 843 } 844 slurp1, err := ioutil.ReadAll(req.Body) 845 if err != nil { 846 t.Errorf("test[%d]: ReadAll(Body) = %v", i, err) 847 } 848 newBody, err := req.GetBody() 849 if err != nil { 850 t.Errorf("test[%d]: GetBody = %v", i, err) 851 } 852 slurp2, err := ioutil.ReadAll(newBody) 853 if err != nil { 854 t.Errorf("test[%d]: ReadAll(GetBody()) = %v", i, err) 855 } 856 if string(slurp1) != string(slurp2) { 857 t.Errorf("test[%d]: Body %q != GetBody %q", i, slurp1, slurp2) 858 } 859 } 860 } 861 862 func testMissingFile(t *testing.T, req *Request) { 863 f, fh, err := req.FormFile("missing") 864 if f != nil { 865 t.Errorf("FormFile file = %v, want nil", f) 866 } 867 if fh != nil { 868 t.Errorf("FormFile file header = %q, want nil", fh) 869 } 870 if err != ErrMissingFile { 871 t.Errorf("FormFile err = %q, want ErrMissingFile", err) 872 } 873 } 874 875 func newTestMultipartRequest(t *testing.T) *Request { 876 b := strings.NewReader(strings.Replace(message, "\n", "\r\n", -1)) 877 req, err := NewRequest("POST", "/", b) 878 if err != nil { 879 t.Fatal("NewRequest:", err) 880 } 881 ctype := fmt.Sprintf(`multipart/form-data; boundary="%s"`, boundary) 882 req.Header.Set("Content-type", ctype) 883 return req 884 } 885 886 func validateTestMultipartContents(t *testing.T, req *Request, allMem bool) { 887 if g, e := req.FormValue("texta"), textaValue; g != e { 888 t.Errorf("texta value = %q, want %q", g, e) 889 } 890 if g, e := req.FormValue("textb"), textbValue; g != e { 891 t.Errorf("textb value = %q, want %q", g, e) 892 } 893 if g := req.FormValue("missing"); g != "" { 894 t.Errorf("missing value = %q, want empty string", g) 895 } 896 897 assertMem := func(n string, fd multipart.File) { 898 if _, ok := fd.(*os.File); ok { 899 t.Error(n, " is *os.File, should not be") 900 } 901 } 902 fda := testMultipartFile(t, req, "filea", "filea.txt", fileaContents) 903 defer fda.Close() 904 assertMem("filea", fda) 905 fdb := testMultipartFile(t, req, "fileb", "fileb.txt", filebContents) 906 defer fdb.Close() 907 if allMem { 908 assertMem("fileb", fdb) 909 } else { 910 if _, ok := fdb.(*os.File); !ok { 911 t.Errorf("fileb has unexpected underlying type %T", fdb) 912 } 913 } 914 915 testMissingFile(t, req) 916 } 917 918 func testMultipartFile(t *testing.T, req *Request, key, expectFilename, expectContent string) multipart.File { 919 f, fh, err := req.FormFile(key) 920 if err != nil { 921 t.Fatalf("FormFile(%q): %q", key, err) 922 } 923 if fh.Filename != expectFilename { 924 t.Errorf("filename = %q, want %q", fh.Filename, expectFilename) 925 } 926 var b bytes.Buffer 927 _, err = io.Copy(&b, f) 928 if err != nil { 929 t.Fatal("copying contents:", err) 930 } 931 if g := b.String(); g != expectContent { 932 t.Errorf("contents = %q, want %q", g, expectContent) 933 } 934 return f 935 } 936 937 const ( 938 fileaContents = "This is a test file." 939 filebContents = "Another test file." 940 textaValue = "foo" 941 textbValue = "bar" 942 boundary = `MyBoundary` 943 ) 944 945 const message = ` 946 --MyBoundary 947 Content-Disposition: form-data; name="filea"; filename="filea.txt" 948 Content-Type: text/plain 949 950 ` + fileaContents + ` 951 --MyBoundary 952 Content-Disposition: form-data; name="fileb"; filename="fileb.txt" 953 Content-Type: text/plain 954 955 ` + filebContents + ` 956 --MyBoundary 957 Content-Disposition: form-data; name="texta" 958 959 ` + textaValue + ` 960 --MyBoundary 961 Content-Disposition: form-data; name="textb" 962 963 ` + textbValue + ` 964 --MyBoundary-- 965 ` 966 967 func benchmarkReadRequest(b *testing.B, request string) { 968 request = request + "\n" // final \n 969 request = strings.Replace(request, "\n", "\r\n", -1) // expand \n to \r\n 970 b.SetBytes(int64(len(request))) 971 r := bufio.NewReader(&infiniteReader{buf: []byte(request)}) 972 b.ReportAllocs() 973 b.ResetTimer() 974 for i := 0; i < b.N; i++ { 975 _, err := ReadRequest(r) 976 if err != nil { 977 b.Fatalf("failed to read request: %v", err) 978 } 979 } 980 } 981 982 // infiniteReader satisfies Read requests as if the contents of buf 983 // loop indefinitely. 984 type infiniteReader struct { 985 buf []byte 986 offset int 987 } 988 989 func (r *infiniteReader) Read(b []byte) (int, error) { 990 n := copy(b, r.buf[r.offset:]) 991 r.offset = (r.offset + n) % len(r.buf) 992 return n, nil 993 } 994 995 func BenchmarkReadRequestChrome(b *testing.B) { 996 // https://github.com/felixge/node-http-perf/blob/master/fixtures/get.http 997 benchmarkReadRequest(b, `GET / HTTP/1.1 998 Host: localhost:8080 999 Connection: keep-alive 1000 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 1001 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 1002 Accept-Encoding: gzip,deflate,sdch 1003 Accept-Language: en-US,en;q=0.8 1004 Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3 1005 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 1006 `) 1007 } 1008 1009 func BenchmarkReadRequestCurl(b *testing.B) { 1010 // curl http://localhost:8080/ 1011 benchmarkReadRequest(b, `GET / HTTP/1.1 1012 User-Agent: curl/7.27.0 1013 Host: localhost:8080 1014 Accept: */* 1015 `) 1016 } 1017 1018 func BenchmarkReadRequestApachebench(b *testing.B) { 1019 // ab -n 1 -c 1 http://localhost:8080/ 1020 benchmarkReadRequest(b, `GET / HTTP/1.0 1021 Host: localhost:8080 1022 User-Agent: ApacheBench/2.3 1023 Accept: */* 1024 `) 1025 } 1026 1027 func BenchmarkReadRequestSiege(b *testing.B) { 1028 // siege -r 1 -c 1 http://localhost:8080/ 1029 benchmarkReadRequest(b, `GET / HTTP/1.1 1030 Host: localhost:8080 1031 Accept: */* 1032 Accept-Encoding: gzip 1033 User-Agent: JoeDog/1.00 [en] (X11; I; Siege 2.70) 1034 Connection: keep-alive 1035 `) 1036 } 1037 1038 func BenchmarkReadRequestWrk(b *testing.B) { 1039 // wrk -t 1 -r 1 -c 1 http://localhost:8080/ 1040 benchmarkReadRequest(b, `GET / HTTP/1.1 1041 Host: localhost:8080 1042 `) 1043 }