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