github.com/sean-/go@v0.0.0-20151219100004-97f854cd7bb6/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 "encoding/base64" 11 "fmt" 12 "io" 13 "io/ioutil" 14 "mime/multipart" 15 . "net/http" 16 "net/url" 17 "os" 18 "reflect" 19 "regexp" 20 "strings" 21 "testing" 22 ) 23 24 func TestQuery(t *testing.T) { 25 req := &Request{Method: "GET"} 26 req.URL, _ = url.Parse("http://www.google.com/search?q=foo&q=bar") 27 if q := req.FormValue("q"); q != "foo" { 28 t.Errorf(`req.FormValue("q") = %q, want "foo"`, q) 29 } 30 } 31 32 func TestPostQuery(t *testing.T) { 33 req, _ := NewRequest("POST", "http://www.google.com/search?q=foo&q=bar&both=x&prio=1&empty=not", 34 strings.NewReader("z=post&both=y&prio=2&empty=")) 35 req.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value") 36 37 if q := req.FormValue("q"); q != "foo" { 38 t.Errorf(`req.FormValue("q") = %q, want "foo"`, q) 39 } 40 if z := req.FormValue("z"); z != "post" { 41 t.Errorf(`req.FormValue("z") = %q, want "post"`, z) 42 } 43 if bq, found := req.PostForm["q"]; found { 44 t.Errorf(`req.PostForm["q"] = %q, want no entry in map`, bq) 45 } 46 if bz := req.PostFormValue("z"); bz != "post" { 47 t.Errorf(`req.PostFormValue("z") = %q, want "post"`, bz) 48 } 49 if qs := req.Form["q"]; !reflect.DeepEqual(qs, []string{"foo", "bar"}) { 50 t.Errorf(`req.Form["q"] = %q, want ["foo", "bar"]`, qs) 51 } 52 if both := req.Form["both"]; !reflect.DeepEqual(both, []string{"y", "x"}) { 53 t.Errorf(`req.Form["both"] = %q, want ["y", "x"]`, both) 54 } 55 if prio := req.FormValue("prio"); prio != "2" { 56 t.Errorf(`req.FormValue("prio") = %q, want "2" (from body)`, prio) 57 } 58 if empty := req.FormValue("empty"); empty != "" { 59 t.Errorf(`req.FormValue("empty") = %q, want "" (from body)`, empty) 60 } 61 } 62 63 func TestPatchQuery(t *testing.T) { 64 req, _ := NewRequest("PATCH", "http://www.google.com/search?q=foo&q=bar&both=x&prio=1&empty=not", 65 strings.NewReader("z=post&both=y&prio=2&empty=")) 66 req.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value") 67 68 if q := req.FormValue("q"); q != "foo" { 69 t.Errorf(`req.FormValue("q") = %q, want "foo"`, q) 70 } 71 if z := req.FormValue("z"); z != "post" { 72 t.Errorf(`req.FormValue("z") = %q, want "post"`, z) 73 } 74 if bq, found := req.PostForm["q"]; found { 75 t.Errorf(`req.PostForm["q"] = %q, want no entry in map`, bq) 76 } 77 if bz := req.PostFormValue("z"); bz != "post" { 78 t.Errorf(`req.PostFormValue("z") = %q, want "post"`, bz) 79 } 80 if qs := req.Form["q"]; !reflect.DeepEqual(qs, []string{"foo", "bar"}) { 81 t.Errorf(`req.Form["q"] = %q, want ["foo", "bar"]`, qs) 82 } 83 if both := req.Form["both"]; !reflect.DeepEqual(both, []string{"y", "x"}) { 84 t.Errorf(`req.Form["both"] = %q, want ["y", "x"]`, both) 85 } 86 if prio := req.FormValue("prio"); prio != "2" { 87 t.Errorf(`req.FormValue("prio") = %q, want "2" (from body)`, prio) 88 } 89 if empty := req.FormValue("empty"); empty != "" { 90 t.Errorf(`req.FormValue("empty") = %q, want "" (from body)`, empty) 91 } 92 } 93 94 type stringMap map[string][]string 95 type parseContentTypeTest struct { 96 shouldError bool 97 contentType stringMap 98 } 99 100 var parseContentTypeTests = []parseContentTypeTest{ 101 {false, stringMap{"Content-Type": {"text/plain"}}}, 102 // Empty content type is legal - shoult be treated as 103 // application/octet-stream (RFC 2616, section 7.2.1) 104 {false, stringMap{}}, 105 {true, stringMap{"Content-Type": {"text/plain; boundary="}}}, 106 {false, stringMap{"Content-Type": {"application/unknown"}}}, 107 } 108 109 func TestParseFormUnknownContentType(t *testing.T) { 110 for i, test := range parseContentTypeTests { 111 req := &Request{ 112 Method: "POST", 113 Header: Header(test.contentType), 114 Body: ioutil.NopCloser(strings.NewReader("body")), 115 } 116 err := req.ParseForm() 117 switch { 118 case err == nil && test.shouldError: 119 t.Errorf("test %d should have returned error", i) 120 case err != nil && !test.shouldError: 121 t.Errorf("test %d should not have returned error, got %v", i, err) 122 } 123 } 124 } 125 126 func TestParseFormInitializeOnError(t *testing.T) { 127 nilBody, _ := NewRequest("POST", "http://www.google.com/search?q=foo", nil) 128 tests := []*Request{ 129 nilBody, 130 {Method: "GET", URL: nil}, 131 } 132 for i, req := range tests { 133 err := req.ParseForm() 134 if req.Form == nil { 135 t.Errorf("%d. Form not initialized, error %v", i, err) 136 } 137 if req.PostForm == nil { 138 t.Errorf("%d. PostForm not initialized, error %v", i, err) 139 } 140 } 141 } 142 143 func TestMultipartReader(t *testing.T) { 144 req := &Request{ 145 Method: "POST", 146 Header: Header{"Content-Type": {`multipart/form-data; boundary="foo123"`}}, 147 Body: ioutil.NopCloser(new(bytes.Buffer)), 148 } 149 multipart, err := req.MultipartReader() 150 if multipart == nil { 151 t.Errorf("expected multipart; error: %v", err) 152 } 153 154 req.Header = Header{"Content-Type": {"text/plain"}} 155 multipart, err = req.MultipartReader() 156 if multipart != nil { 157 t.Error("unexpected multipart for text/plain") 158 } 159 } 160 161 func TestParseMultipartForm(t *testing.T) { 162 req := &Request{ 163 Method: "POST", 164 Header: Header{"Content-Type": {`multipart/form-data; boundary="foo123"`}}, 165 Body: ioutil.NopCloser(new(bytes.Buffer)), 166 } 167 err := req.ParseMultipartForm(25) 168 if err == nil { 169 t.Error("expected multipart EOF, got nil") 170 } 171 172 req.Header = Header{"Content-Type": {"text/plain"}} 173 err = req.ParseMultipartForm(25) 174 if err != ErrNotMultipart { 175 t.Error("expected ErrNotMultipart for text/plain") 176 } 177 } 178 179 func TestRedirect_h1(t *testing.T) { testRedirect(t, h1Mode) } 180 func TestRedirect_h2(t *testing.T) { testRedirect(t, h2Mode) } 181 func testRedirect(t *testing.T, h2 bool) { 182 defer afterTest(t) 183 cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) { 184 switch r.URL.Path { 185 case "/": 186 w.Header().Set("Location", "/foo/") 187 w.WriteHeader(StatusSeeOther) 188 case "/foo/": 189 fmt.Fprintf(w, "foo") 190 default: 191 w.WriteHeader(StatusBadRequest) 192 } 193 })) 194 defer cst.close() 195 196 var end = regexp.MustCompile("/foo/$") 197 r, err := cst.c.Get(cst.ts.URL) 198 if err != nil { 199 t.Fatal(err) 200 } 201 r.Body.Close() 202 url := r.Request.URL.String() 203 if r.StatusCode != 200 || !end.MatchString(url) { 204 t.Fatalf("Get got status %d at %q, want 200 matching /foo/$", r.StatusCode, url) 205 } 206 } 207 208 func TestSetBasicAuth(t *testing.T) { 209 r, _ := NewRequest("GET", "http://example.com/", nil) 210 r.SetBasicAuth("Aladdin", "open sesame") 211 if g, e := r.Header.Get("Authorization"), "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="; g != e { 212 t.Errorf("got header %q, want %q", g, e) 213 } 214 } 215 216 func TestMultipartRequest(t *testing.T) { 217 // Test that we can read the values and files of a 218 // multipart request with FormValue and FormFile, 219 // and that ParseMultipartForm can be called multiple times. 220 req := newTestMultipartRequest(t) 221 if err := req.ParseMultipartForm(25); err != nil { 222 t.Fatal("ParseMultipartForm first call:", err) 223 } 224 defer req.MultipartForm.RemoveAll() 225 validateTestMultipartContents(t, req, false) 226 if err := req.ParseMultipartForm(25); err != nil { 227 t.Fatal("ParseMultipartForm second call:", err) 228 } 229 validateTestMultipartContents(t, req, false) 230 } 231 232 func TestMultipartRequestAuto(t *testing.T) { 233 // Test that FormValue and FormFile automatically invoke 234 // ParseMultipartForm and return the right values. 235 req := newTestMultipartRequest(t) 236 defer func() { 237 if req.MultipartForm != nil { 238 req.MultipartForm.RemoveAll() 239 } 240 }() 241 validateTestMultipartContents(t, req, true) 242 } 243 244 func TestMissingFileMultipartRequest(t *testing.T) { 245 // Test that FormFile returns an error if 246 // the named file is missing. 247 req := newTestMultipartRequest(t) 248 testMissingFile(t, req) 249 } 250 251 // Test that FormValue invokes ParseMultipartForm. 252 func TestFormValueCallsParseMultipartForm(t *testing.T) { 253 req, _ := NewRequest("POST", "http://www.google.com/", strings.NewReader("z=post")) 254 req.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value") 255 if req.Form != nil { 256 t.Fatal("Unexpected request Form, want nil") 257 } 258 req.FormValue("z") 259 if req.Form == nil { 260 t.Fatal("ParseMultipartForm not called by FormValue") 261 } 262 } 263 264 // Test that FormFile invokes ParseMultipartForm. 265 func TestFormFileCallsParseMultipartForm(t *testing.T) { 266 req := newTestMultipartRequest(t) 267 if req.Form != nil { 268 t.Fatal("Unexpected request Form, want nil") 269 } 270 req.FormFile("") 271 if req.Form == nil { 272 t.Fatal("ParseMultipartForm not called by FormFile") 273 } 274 } 275 276 // Test that ParseMultipartForm errors if called 277 // after MultipartReader on the same request. 278 func TestParseMultipartFormOrder(t *testing.T) { 279 req := newTestMultipartRequest(t) 280 if _, err := req.MultipartReader(); err != nil { 281 t.Fatalf("MultipartReader: %v", err) 282 } 283 if err := req.ParseMultipartForm(1024); err == nil { 284 t.Fatal("expected an error from ParseMultipartForm after call to MultipartReader") 285 } 286 } 287 288 // Test that MultipartReader errors if called 289 // after ParseMultipartForm on the same request. 290 func TestMultipartReaderOrder(t *testing.T) { 291 req := newTestMultipartRequest(t) 292 if err := req.ParseMultipartForm(25); err != nil { 293 t.Fatalf("ParseMultipartForm: %v", err) 294 } 295 defer req.MultipartForm.RemoveAll() 296 if _, err := req.MultipartReader(); err == nil { 297 t.Fatal("expected an error from MultipartReader after call to ParseMultipartForm") 298 } 299 } 300 301 // Test that FormFile errors if called after 302 // MultipartReader on the same request. 303 func TestFormFileOrder(t *testing.T) { 304 req := newTestMultipartRequest(t) 305 if _, err := req.MultipartReader(); err != nil { 306 t.Fatalf("MultipartReader: %v", err) 307 } 308 if _, _, err := req.FormFile(""); err == nil { 309 t.Fatal("expected an error from FormFile after call to MultipartReader") 310 } 311 } 312 313 var readRequestErrorTests = []struct { 314 in string 315 err error 316 }{ 317 {"GET / HTTP/1.1\r\nheader:foo\r\n\r\n", nil}, 318 {"GET / HTTP/1.1\r\nheader:foo\r\n", io.ErrUnexpectedEOF}, 319 {"", io.EOF}, 320 } 321 322 func TestReadRequestErrors(t *testing.T) { 323 for i, tt := range readRequestErrorTests { 324 _, err := ReadRequest(bufio.NewReader(strings.NewReader(tt.in))) 325 if err != tt.err { 326 t.Errorf("%d. got error = %v; want %v", i, err, tt.err) 327 } 328 } 329 } 330 331 var newRequestHostTests = []struct { 332 in, out string 333 }{ 334 {"http://www.example.com/", "www.example.com"}, 335 {"http://www.example.com:8080/", "www.example.com:8080"}, 336 337 {"http://192.168.0.1/", "192.168.0.1"}, 338 {"http://192.168.0.1:8080/", "192.168.0.1:8080"}, 339 340 {"http://[fe80::1]/", "[fe80::1]"}, 341 {"http://[fe80::1]:8080/", "[fe80::1]:8080"}, 342 {"http://[fe80::1%25en0]/", "[fe80::1%en0]"}, 343 {"http://[fe80::1%25en0]:8080/", "[fe80::1%en0]:8080"}, 344 } 345 346 func TestNewRequestHost(t *testing.T) { 347 for i, tt := range newRequestHostTests { 348 req, err := NewRequest("GET", tt.in, nil) 349 if err != nil { 350 t.Errorf("#%v: %v", i, err) 351 continue 352 } 353 if req.Host != tt.out { 354 t.Errorf("got %q; want %q", req.Host, tt.out) 355 } 356 } 357 } 358 359 func TestRequestInvalidMethod(t *testing.T) { 360 _, err := NewRequest("bad method", "http://foo.com/", nil) 361 if err == nil { 362 t.Error("expected error from NewRequest with invalid method") 363 } 364 req, err := NewRequest("GET", "http://foo.example/", nil) 365 if err != nil { 366 t.Fatal(err) 367 } 368 req.Method = "bad method" 369 _, err = DefaultClient.Do(req) 370 if err == nil || !strings.Contains(err.Error(), "invalid method") { 371 t.Errorf("Transport error = %v; want invalid method", err) 372 } 373 374 req, err = NewRequest("", "http://foo.com/", nil) 375 if err != nil { 376 t.Errorf("NewRequest(empty method) = %v; want nil", err) 377 } else if req.Method != "GET" { 378 t.Errorf("NewRequest(empty method) has method %q; want GET", req.Method) 379 } 380 } 381 382 func TestNewRequestContentLength(t *testing.T) { 383 readByte := func(r io.Reader) io.Reader { 384 var b [1]byte 385 r.Read(b[:]) 386 return r 387 } 388 tests := []struct { 389 r io.Reader 390 want int64 391 }{ 392 {bytes.NewReader([]byte("123")), 3}, 393 {bytes.NewBuffer([]byte("1234")), 4}, 394 {strings.NewReader("12345"), 5}, 395 // Not detected: 396 {struct{ io.Reader }{strings.NewReader("xyz")}, 0}, 397 {io.NewSectionReader(strings.NewReader("x"), 0, 6), 0}, 398 {readByte(io.NewSectionReader(strings.NewReader("xy"), 0, 6)), 0}, 399 } 400 for _, tt := range tests { 401 req, err := NewRequest("POST", "http://localhost/", tt.r) 402 if err != nil { 403 t.Fatal(err) 404 } 405 if req.ContentLength != tt.want { 406 t.Errorf("ContentLength(%T) = %d; want %d", tt.r, req.ContentLength, tt.want) 407 } 408 } 409 } 410 411 var parseHTTPVersionTests = []struct { 412 vers string 413 major, minor int 414 ok bool 415 }{ 416 {"HTTP/0.9", 0, 9, true}, 417 {"HTTP/1.0", 1, 0, true}, 418 {"HTTP/1.1", 1, 1, true}, 419 {"HTTP/3.14", 3, 14, true}, 420 421 {"HTTP", 0, 0, false}, 422 {"HTTP/one.one", 0, 0, false}, 423 {"HTTP/1.1/", 0, 0, false}, 424 {"HTTP/-1,0", 0, 0, false}, 425 {"HTTP/0,-1", 0, 0, false}, 426 {"HTTP/", 0, 0, false}, 427 {"HTTP/1,1", 0, 0, false}, 428 } 429 430 func TestParseHTTPVersion(t *testing.T) { 431 for _, tt := range parseHTTPVersionTests { 432 major, minor, ok := ParseHTTPVersion(tt.vers) 433 if ok != tt.ok || major != tt.major || minor != tt.minor { 434 type version struct { 435 major, minor int 436 ok bool 437 } 438 t.Errorf("failed to parse %q, expected: %#v, got %#v", tt.vers, version{tt.major, tt.minor, tt.ok}, version{major, minor, ok}) 439 } 440 } 441 } 442 443 type getBasicAuthTest struct { 444 username, password string 445 ok bool 446 } 447 448 type basicAuthCredentialsTest struct { 449 username, password string 450 } 451 452 var getBasicAuthTests = []struct { 453 username, password string 454 ok bool 455 }{ 456 {"Aladdin", "open sesame", true}, 457 {"Aladdin", "open:sesame", true}, 458 {"", "", true}, 459 } 460 461 func TestGetBasicAuth(t *testing.T) { 462 for _, tt := range getBasicAuthTests { 463 r, _ := NewRequest("GET", "http://example.com/", nil) 464 r.SetBasicAuth(tt.username, tt.password) 465 username, password, ok := r.BasicAuth() 466 if ok != tt.ok || username != tt.username || password != tt.password { 467 t.Errorf("BasicAuth() = %#v, want %#v", getBasicAuthTest{username, password, ok}, 468 getBasicAuthTest{tt.username, tt.password, tt.ok}) 469 } 470 } 471 // Unauthenticated request. 472 r, _ := NewRequest("GET", "http://example.com/", nil) 473 username, password, ok := r.BasicAuth() 474 if ok { 475 t.Errorf("expected false from BasicAuth when the request is unauthenticated") 476 } 477 want := basicAuthCredentialsTest{"", ""} 478 if username != want.username || password != want.password { 479 t.Errorf("expected credentials: %#v when the request is unauthenticated, got %#v", 480 want, basicAuthCredentialsTest{username, password}) 481 } 482 } 483 484 var parseBasicAuthTests = []struct { 485 header, username, password string 486 ok bool 487 }{ 488 {"Basic " + base64.StdEncoding.EncodeToString([]byte("Aladdin:open sesame")), "Aladdin", "open sesame", true}, 489 {"Basic " + base64.StdEncoding.EncodeToString([]byte("Aladdin:open:sesame")), "Aladdin", "open:sesame", true}, 490 {"Basic " + base64.StdEncoding.EncodeToString([]byte(":")), "", "", true}, 491 {"Basic" + base64.StdEncoding.EncodeToString([]byte("Aladdin:open sesame")), "", "", false}, 492 {base64.StdEncoding.EncodeToString([]byte("Aladdin:open sesame")), "", "", false}, 493 {"Basic ", "", "", false}, 494 {"Basic Aladdin:open sesame", "", "", false}, 495 {`Digest username="Aladdin"`, "", "", false}, 496 } 497 498 func TestParseBasicAuth(t *testing.T) { 499 for _, tt := range parseBasicAuthTests { 500 r, _ := NewRequest("GET", "http://example.com/", nil) 501 r.Header.Set("Authorization", tt.header) 502 username, password, ok := r.BasicAuth() 503 if ok != tt.ok || username != tt.username || password != tt.password { 504 t.Errorf("BasicAuth() = %#v, want %#v", getBasicAuthTest{username, password, ok}, 505 getBasicAuthTest{tt.username, tt.password, tt.ok}) 506 } 507 } 508 } 509 510 type logWrites struct { 511 t *testing.T 512 dst *[]string 513 } 514 515 func (l logWrites) WriteByte(c byte) error { 516 l.t.Fatalf("unexpected WriteByte call") 517 return nil 518 } 519 520 func (l logWrites) Write(p []byte) (n int, err error) { 521 *l.dst = append(*l.dst, string(p)) 522 return len(p), nil 523 } 524 525 func TestRequestWriteBufferedWriter(t *testing.T) { 526 got := []string{} 527 req, _ := NewRequest("GET", "http://foo.com/", nil) 528 req.Write(logWrites{t, &got}) 529 want := []string{ 530 "GET / HTTP/1.1\r\n", 531 "Host: foo.com\r\n", 532 "User-Agent: " + DefaultUserAgent + "\r\n", 533 "\r\n", 534 } 535 if !reflect.DeepEqual(got, want) { 536 t.Errorf("Writes = %q\n Want = %q", got, want) 537 } 538 } 539 540 func TestRequestBadHost(t *testing.T) { 541 got := []string{} 542 req, err := NewRequest("GET", "http://foo/after", nil) 543 if err != nil { 544 t.Fatal(err) 545 } 546 req.Host = "foo.com with spaces" 547 req.URL.Host = "foo.com with spaces" 548 req.Write(logWrites{t, &got}) 549 want := []string{ 550 "GET /after HTTP/1.1\r\n", 551 "Host: foo.com\r\n", 552 "User-Agent: " + DefaultUserAgent + "\r\n", 553 "\r\n", 554 } 555 if !reflect.DeepEqual(got, want) { 556 t.Errorf("Writes = %q\n Want = %q", got, want) 557 } 558 } 559 560 func TestStarRequest(t *testing.T) { 561 req, err := ReadRequest(bufio.NewReader(strings.NewReader("M-SEARCH * HTTP/1.1\r\n\r\n"))) 562 if err != nil { 563 return 564 } 565 var out bytes.Buffer 566 if err := req.Write(&out); err != nil { 567 t.Fatal(err) 568 } 569 back, err := ReadRequest(bufio.NewReader(&out)) 570 if err != nil { 571 t.Fatal(err) 572 } 573 // Ignore the Headers (the User-Agent breaks the deep equal, 574 // but we don't care about it) 575 req.Header = nil 576 back.Header = nil 577 if !reflect.DeepEqual(req, back) { 578 t.Errorf("Original request doesn't match Request read back.") 579 t.Logf("Original: %#v", req) 580 t.Logf("Original.URL: %#v", req.URL) 581 t.Logf("Wrote: %s", out.Bytes()) 582 t.Logf("Read back (doesn't match Original): %#v", back) 583 } 584 } 585 586 type responseWriterJustWriter struct { 587 io.Writer 588 } 589 590 func (responseWriterJustWriter) Header() Header { panic("should not be called") } 591 func (responseWriterJustWriter) WriteHeader(int) { panic("should not be called") } 592 593 // delayedEOFReader never returns (n > 0, io.EOF), instead putting 594 // off the io.EOF until a subsequent Read call. 595 type delayedEOFReader struct { 596 r io.Reader 597 } 598 599 func (dr delayedEOFReader) Read(p []byte) (n int, err error) { 600 n, err = dr.r.Read(p) 601 if n > 0 && err == io.EOF { 602 err = nil 603 } 604 return 605 } 606 607 func TestIssue10884_MaxBytesEOF(t *testing.T) { 608 dst := ioutil.Discard 609 _, err := io.Copy(dst, MaxBytesReader( 610 responseWriterJustWriter{dst}, 611 ioutil.NopCloser(delayedEOFReader{strings.NewReader("12345")}), 612 5)) 613 if err != nil { 614 t.Fatal(err) 615 } 616 } 617 618 func testMissingFile(t *testing.T, req *Request) { 619 f, fh, err := req.FormFile("missing") 620 if f != nil { 621 t.Errorf("FormFile file = %v, want nil", f) 622 } 623 if fh != nil { 624 t.Errorf("FormFile file header = %q, want nil", fh) 625 } 626 if err != ErrMissingFile { 627 t.Errorf("FormFile err = %q, want ErrMissingFile", err) 628 } 629 } 630 631 func newTestMultipartRequest(t *testing.T) *Request { 632 b := strings.NewReader(strings.Replace(message, "\n", "\r\n", -1)) 633 req, err := NewRequest("POST", "/", b) 634 if err != nil { 635 t.Fatal("NewRequest:", err) 636 } 637 ctype := fmt.Sprintf(`multipart/form-data; boundary="%s"`, boundary) 638 req.Header.Set("Content-type", ctype) 639 return req 640 } 641 642 func validateTestMultipartContents(t *testing.T, req *Request, allMem bool) { 643 if g, e := req.FormValue("texta"), textaValue; g != e { 644 t.Errorf("texta value = %q, want %q", g, e) 645 } 646 if g, e := req.FormValue("textb"), textbValue; g != e { 647 t.Errorf("textb value = %q, want %q", g, e) 648 } 649 if g := req.FormValue("missing"); g != "" { 650 t.Errorf("missing value = %q, want empty string", g) 651 } 652 653 assertMem := func(n string, fd multipart.File) { 654 if _, ok := fd.(*os.File); ok { 655 t.Error(n, " is *os.File, should not be") 656 } 657 } 658 fda := testMultipartFile(t, req, "filea", "filea.txt", fileaContents) 659 defer fda.Close() 660 assertMem("filea", fda) 661 fdb := testMultipartFile(t, req, "fileb", "fileb.txt", filebContents) 662 defer fdb.Close() 663 if allMem { 664 assertMem("fileb", fdb) 665 } else { 666 if _, ok := fdb.(*os.File); !ok { 667 t.Errorf("fileb has unexpected underlying type %T", fdb) 668 } 669 } 670 671 testMissingFile(t, req) 672 } 673 674 func testMultipartFile(t *testing.T, req *Request, key, expectFilename, expectContent string) multipart.File { 675 f, fh, err := req.FormFile(key) 676 if err != nil { 677 t.Fatalf("FormFile(%q): %q", key, err) 678 } 679 if fh.Filename != expectFilename { 680 t.Errorf("filename = %q, want %q", fh.Filename, expectFilename) 681 } 682 var b bytes.Buffer 683 _, err = io.Copy(&b, f) 684 if err != nil { 685 t.Fatal("copying contents:", err) 686 } 687 if g := b.String(); g != expectContent { 688 t.Errorf("contents = %q, want %q", g, expectContent) 689 } 690 return f 691 } 692 693 const ( 694 fileaContents = "This is a test file." 695 filebContents = "Another test file." 696 textaValue = "foo" 697 textbValue = "bar" 698 boundary = `MyBoundary` 699 ) 700 701 const message = ` 702 --MyBoundary 703 Content-Disposition: form-data; name="filea"; filename="filea.txt" 704 Content-Type: text/plain 705 706 ` + fileaContents + ` 707 --MyBoundary 708 Content-Disposition: form-data; name="fileb"; filename="fileb.txt" 709 Content-Type: text/plain 710 711 ` + filebContents + ` 712 --MyBoundary 713 Content-Disposition: form-data; name="texta" 714 715 ` + textaValue + ` 716 --MyBoundary 717 Content-Disposition: form-data; name="textb" 718 719 ` + textbValue + ` 720 --MyBoundary-- 721 ` 722 723 func benchmarkReadRequest(b *testing.B, request string) { 724 request = request + "\n" // final \n 725 request = strings.Replace(request, "\n", "\r\n", -1) // expand \n to \r\n 726 b.SetBytes(int64(len(request))) 727 r := bufio.NewReader(&infiniteReader{buf: []byte(request)}) 728 b.ReportAllocs() 729 b.ResetTimer() 730 for i := 0; i < b.N; i++ { 731 _, err := ReadRequest(r) 732 if err != nil { 733 b.Fatalf("failed to read request: %v", err) 734 } 735 } 736 } 737 738 // infiniteReader satisfies Read requests as if the contents of buf 739 // loop indefinitely. 740 type infiniteReader struct { 741 buf []byte 742 offset int 743 } 744 745 func (r *infiniteReader) Read(b []byte) (int, error) { 746 n := copy(b, r.buf[r.offset:]) 747 r.offset = (r.offset + n) % len(r.buf) 748 return n, nil 749 } 750 751 func BenchmarkReadRequestChrome(b *testing.B) { 752 // https://github.com/felixge/node-http-perf/blob/master/fixtures/get.http 753 benchmarkReadRequest(b, `GET / HTTP/1.1 754 Host: localhost:8080 755 Connection: keep-alive 756 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 757 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 758 Accept-Encoding: gzip,deflate,sdch 759 Accept-Language: en-US,en;q=0.8 760 Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3 761 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 762 `) 763 } 764 765 func BenchmarkReadRequestCurl(b *testing.B) { 766 // curl http://localhost:8080/ 767 benchmarkReadRequest(b, `GET / HTTP/1.1 768 User-Agent: curl/7.27.0 769 Host: localhost:8080 770 Accept: */* 771 `) 772 } 773 774 func BenchmarkReadRequestApachebench(b *testing.B) { 775 // ab -n 1 -c 1 http://localhost:8080/ 776 benchmarkReadRequest(b, `GET / HTTP/1.0 777 Host: localhost:8080 778 User-Agent: ApacheBench/2.3 779 Accept: */* 780 `) 781 } 782 783 func BenchmarkReadRequestSiege(b *testing.B) { 784 // siege -r 1 -c 1 http://localhost:8080/ 785 benchmarkReadRequest(b, `GET / HTTP/1.1 786 Host: localhost:8080 787 Accept: */* 788 Accept-Encoding: gzip 789 User-Agent: JoeDog/1.00 [en] (X11; I; Siege 2.70) 790 Connection: keep-alive 791 `) 792 } 793 794 func BenchmarkReadRequestWrk(b *testing.B) { 795 // wrk -t 1 -r 1 -c 1 http://localhost:8080/ 796 benchmarkReadRequest(b, `GET / HTTP/1.1 797 Host: localhost:8080 798 `) 799 }