github.com/mtsmfm/go/src@v0.0.0-20221020090648-44bdcb9f8fde/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 "crypto/rand" 12 "encoding/base64" 13 "fmt" 14 "io" 15 "math" 16 "mime/multipart" 17 . "net/http" 18 "net/url" 19 "os" 20 "reflect" 21 "regexp" 22 "strings" 23 "testing" 24 ) 25 26 func TestQuery(t *testing.T) { 27 req := &Request{Method: "GET"} 28 req.URL, _ = url.Parse("http://www.google.com/search?q=foo&q=bar") 29 if q := req.FormValue("q"); q != "foo" { 30 t.Errorf(`req.FormValue("q") = %q, want "foo"`, q) 31 } 32 } 33 34 // Issue #25192: Test that ParseForm fails but still parses the form when an URL 35 // containing a semicolon is provided. 36 func TestParseFormSemicolonSeparator(t *testing.T) { 37 for _, method := range []string{"POST", "PATCH", "PUT", "GET"} { 38 req, _ := NewRequest(method, "http://www.google.com/search?q=foo;q=bar&a=1", 39 strings.NewReader("q")) 40 err := req.ParseForm() 41 if err == nil { 42 t.Fatalf(`for method %s, ParseForm expected an error, got success`, method) 43 } 44 wantForm := url.Values{"a": []string{"1"}} 45 if !reflect.DeepEqual(req.Form, wantForm) { 46 t.Fatalf("for method %s, ParseForm expected req.Form = %v, want %v", method, req.Form, wantForm) 47 } 48 } 49 } 50 51 func TestParseFormQuery(t *testing.T) { 52 req, _ := NewRequest("POST", "http://www.google.com/search?q=foo&q=bar&both=x&prio=1&orphan=nope&empty=not", 53 strings.NewReader("z=post&both=y&prio=2&=nokey&orphan&empty=&")) 54 req.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value") 55 56 if q := req.FormValue("q"); q != "foo" { 57 t.Errorf(`req.FormValue("q") = %q, want "foo"`, q) 58 } 59 if z := req.FormValue("z"); z != "post" { 60 t.Errorf(`req.FormValue("z") = %q, want "post"`, z) 61 } 62 if bq, found := req.PostForm["q"]; found { 63 t.Errorf(`req.PostForm["q"] = %q, want no entry in map`, bq) 64 } 65 if bz := req.PostFormValue("z"); bz != "post" { 66 t.Errorf(`req.PostFormValue("z") = %q, want "post"`, bz) 67 } 68 if qs := req.Form["q"]; !reflect.DeepEqual(qs, []string{"foo", "bar"}) { 69 t.Errorf(`req.Form["q"] = %q, want ["foo", "bar"]`, qs) 70 } 71 if both := req.Form["both"]; !reflect.DeepEqual(both, []string{"y", "x"}) { 72 t.Errorf(`req.Form["both"] = %q, want ["y", "x"]`, both) 73 } 74 if prio := req.FormValue("prio"); prio != "2" { 75 t.Errorf(`req.FormValue("prio") = %q, want "2" (from body)`, prio) 76 } 77 if orphan := req.Form["orphan"]; !reflect.DeepEqual(orphan, []string{"", "nope"}) { 78 t.Errorf(`req.FormValue("orphan") = %q, want "" (from body)`, orphan) 79 } 80 if empty := req.Form["empty"]; !reflect.DeepEqual(empty, []string{"", "not"}) { 81 t.Errorf(`req.FormValue("empty") = %q, want "" (from body)`, empty) 82 } 83 if nokey := req.Form[""]; !reflect.DeepEqual(nokey, []string{"nokey"}) { 84 t.Errorf(`req.FormValue("nokey") = %q, want "nokey" (from body)`, nokey) 85 } 86 } 87 88 // Tests that we only parse the form automatically for certain methods. 89 func TestParseFormQueryMethods(t *testing.T) { 90 for _, method := range []string{"POST", "PATCH", "PUT", "FOO"} { 91 req, _ := NewRequest(method, "http://www.google.com/search", 92 strings.NewReader("foo=bar")) 93 req.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value") 94 want := "bar" 95 if method == "FOO" { 96 want = "" 97 } 98 if got := req.FormValue("foo"); got != want { 99 t.Errorf(`for method %s, FormValue("foo") = %q; want %q`, method, got, want) 100 } 101 } 102 } 103 104 func TestParseFormUnknownContentType(t *testing.T) { 105 for _, test := range []struct { 106 name string 107 wantErr string 108 contentType Header 109 }{ 110 {"text", "", Header{"Content-Type": {"text/plain"}}}, 111 // Empty content type is legal - may be treated as 112 // application/octet-stream (RFC 7231, section 3.1.1.5) 113 {"empty", "", Header{}}, 114 {"boundary", "mime: invalid media parameter", Header{"Content-Type": {"text/plain; boundary="}}}, 115 {"unknown", "", Header{"Content-Type": {"application/unknown"}}}, 116 } { 117 t.Run(test.name, 118 func(t *testing.T) { 119 req := &Request{ 120 Method: "POST", 121 Header: test.contentType, 122 Body: io.NopCloser(strings.NewReader("body")), 123 } 124 err := req.ParseForm() 125 switch { 126 case err == nil && test.wantErr != "": 127 t.Errorf("unexpected success; want error %q", test.wantErr) 128 case err != nil && test.wantErr == "": 129 t.Errorf("want success, got error: %v", err) 130 case test.wantErr != "" && test.wantErr != fmt.Sprint(err): 131 t.Errorf("got error %q; want %q", err, test.wantErr) 132 } 133 }, 134 ) 135 } 136 } 137 138 func TestParseFormInitializeOnError(t *testing.T) { 139 nilBody, _ := NewRequest("POST", "http://www.google.com/search?q=foo", nil) 140 tests := []*Request{ 141 nilBody, 142 {Method: "GET", URL: nil}, 143 } 144 for i, req := range tests { 145 err := req.ParseForm() 146 if req.Form == nil { 147 t.Errorf("%d. Form not initialized, error %v", i, err) 148 } 149 if req.PostForm == nil { 150 t.Errorf("%d. PostForm not initialized, error %v", i, err) 151 } 152 } 153 } 154 155 func TestMultipartReader(t *testing.T) { 156 tests := []struct { 157 shouldError bool 158 contentType string 159 }{ 160 {false, `multipart/form-data; boundary="foo123"`}, 161 {false, `multipart/mixed; boundary="foo123"`}, 162 {true, `text/plain`}, 163 } 164 165 for i, test := range tests { 166 req := &Request{ 167 Method: "POST", 168 Header: Header{"Content-Type": {test.contentType}}, 169 Body: io.NopCloser(new(bytes.Buffer)), 170 } 171 multipart, err := req.MultipartReader() 172 if test.shouldError { 173 if err == nil || multipart != nil { 174 t.Errorf("test %d: unexpectedly got nil-error (%v) or non-nil-multipart (%v)", i, err, multipart) 175 } 176 continue 177 } 178 if err != nil || multipart == nil { 179 t.Errorf("test %d: unexpectedly got error (%v) or nil-multipart (%v)", i, err, multipart) 180 } 181 } 182 } 183 184 // Issue 9305: ParseMultipartForm should populate PostForm too 185 func TestParseMultipartFormPopulatesPostForm(t *testing.T) { 186 postData := 187 `--xxx 188 Content-Disposition: form-data; name="field1" 189 190 value1 191 --xxx 192 Content-Disposition: form-data; name="field2" 193 194 value2 195 --xxx 196 Content-Disposition: form-data; name="file"; filename="file" 197 Content-Type: application/octet-stream 198 Content-Transfer-Encoding: binary 199 200 binary data 201 --xxx-- 202 ` 203 req := &Request{ 204 Method: "POST", 205 Header: Header{"Content-Type": {`multipart/form-data; boundary=xxx`}}, 206 Body: io.NopCloser(strings.NewReader(postData)), 207 } 208 209 initialFormItems := map[string]string{ 210 "language": "Go", 211 "name": "gopher", 212 "skill": "go-ing", 213 "field2": "initial-value2", 214 } 215 216 req.Form = make(url.Values) 217 for k, v := range initialFormItems { 218 req.Form.Add(k, v) 219 } 220 221 err := req.ParseMultipartForm(10000) 222 if err != nil { 223 t.Fatalf("unexpected multipart error %v", err) 224 } 225 226 wantForm := url.Values{ 227 "language": []string{"Go"}, 228 "name": []string{"gopher"}, 229 "skill": []string{"go-ing"}, 230 "field1": []string{"value1"}, 231 "field2": []string{"initial-value2", "value2"}, 232 } 233 if !reflect.DeepEqual(req.Form, wantForm) { 234 t.Fatalf("req.Form = %v, want %v", req.Form, wantForm) 235 } 236 237 wantPostForm := url.Values{ 238 "field1": []string{"value1"}, 239 "field2": []string{"value2"}, 240 } 241 if !reflect.DeepEqual(req.PostForm, wantPostForm) { 242 t.Fatalf("req.PostForm = %v, want %v", req.PostForm, wantPostForm) 243 } 244 } 245 246 func TestParseMultipartForm(t *testing.T) { 247 req := &Request{ 248 Method: "POST", 249 Header: Header{"Content-Type": {`multipart/form-data; boundary="foo123"`}}, 250 Body: io.NopCloser(new(bytes.Buffer)), 251 } 252 err := req.ParseMultipartForm(25) 253 if err == nil { 254 t.Error("expected multipart EOF, got nil") 255 } 256 257 req.Header = Header{"Content-Type": {"text/plain"}} 258 err = req.ParseMultipartForm(25) 259 if err != ErrNotMultipart { 260 t.Error("expected ErrNotMultipart for text/plain") 261 } 262 } 263 264 // Issue 45789: multipart form should not include directory path in filename 265 func TestParseMultipartFormFilename(t *testing.T) { 266 postData := 267 `--xxx 268 Content-Disposition: form-data; name="file"; filename="../usr/foobar.txt/" 269 Content-Type: text/plain 270 271 --xxx-- 272 ` 273 req := &Request{ 274 Method: "POST", 275 Header: Header{"Content-Type": {`multipart/form-data; boundary=xxx`}}, 276 Body: io.NopCloser(strings.NewReader(postData)), 277 } 278 _, hdr, err := req.FormFile("file") 279 if err != nil { 280 t.Fatal(err) 281 } 282 if hdr.Filename != "foobar.txt" { 283 t.Errorf("expected only the last element of the path, got %q", hdr.Filename) 284 } 285 } 286 287 // Issue #40430: Test that if maxMemory for ParseMultipartForm when combined with 288 // the payload size and the internal leeway buffer size of 10MiB overflows, that we 289 // correctly return an error. 290 func TestMaxInt64ForMultipartFormMaxMemoryOverflow(t *testing.T) { 291 run(t, testMaxInt64ForMultipartFormMaxMemoryOverflow) 292 } 293 func testMaxInt64ForMultipartFormMaxMemoryOverflow(t *testing.T, mode testMode) { 294 payloadSize := 1 << 10 295 cst := newClientServerTest(t, mode, HandlerFunc(func(rw ResponseWriter, req *Request) { 296 // The combination of: 297 // MaxInt64 + payloadSize + (internal spare of 10MiB) 298 // triggers the overflow. See issue https://golang.org/issue/40430/ 299 if err := req.ParseMultipartForm(math.MaxInt64); err != nil { 300 Error(rw, err.Error(), StatusBadRequest) 301 return 302 } 303 })).ts 304 fBuf := new(bytes.Buffer) 305 mw := multipart.NewWriter(fBuf) 306 mf, err := mw.CreateFormFile("file", "myfile.txt") 307 if err != nil { 308 t.Fatal(err) 309 } 310 if _, err := mf.Write(bytes.Repeat([]byte("abc"), payloadSize)); err != nil { 311 t.Fatal(err) 312 } 313 if err := mw.Close(); err != nil { 314 t.Fatal(err) 315 } 316 req, err := NewRequest("POST", cst.URL, fBuf) 317 if err != nil { 318 t.Fatal(err) 319 } 320 req.Header.Set("Content-Type", mw.FormDataContentType()) 321 res, err := cst.Client().Do(req) 322 if err != nil { 323 t.Fatal(err) 324 } 325 res.Body.Close() 326 if g, w := res.StatusCode, StatusOK; g != w { 327 t.Fatalf("Status code mismatch: got %d, want %d", g, w) 328 } 329 } 330 331 func TestRequestRedirect(t *testing.T) { run(t, testRequestRedirect) } 332 func testRequestRedirect(t *testing.T, mode testMode) { 333 cst := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) { 334 switch r.URL.Path { 335 case "/": 336 w.Header().Set("Location", "/foo/") 337 w.WriteHeader(StatusSeeOther) 338 case "/foo/": 339 fmt.Fprintf(w, "foo") 340 default: 341 w.WriteHeader(StatusBadRequest) 342 } 343 })) 344 345 var end = regexp.MustCompile("/foo/$") 346 r, err := cst.c.Get(cst.ts.URL) 347 if err != nil { 348 t.Fatal(err) 349 } 350 r.Body.Close() 351 url := r.Request.URL.String() 352 if r.StatusCode != 200 || !end.MatchString(url) { 353 t.Fatalf("Get got status %d at %q, want 200 matching /foo/$", r.StatusCode, url) 354 } 355 } 356 357 func TestSetBasicAuth(t *testing.T) { 358 r, _ := NewRequest("GET", "http://example.com/", nil) 359 r.SetBasicAuth("Aladdin", "open sesame") 360 if g, e := r.Header.Get("Authorization"), "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="; g != e { 361 t.Errorf("got header %q, want %q", g, e) 362 } 363 } 364 365 func TestMultipartRequest(t *testing.T) { 366 // Test that we can read the values and files of a 367 // multipart request with FormValue and FormFile, 368 // and that ParseMultipartForm can be called multiple times. 369 req := newTestMultipartRequest(t) 370 if err := req.ParseMultipartForm(25); err != nil { 371 t.Fatal("ParseMultipartForm first call:", err) 372 } 373 defer req.MultipartForm.RemoveAll() 374 validateTestMultipartContents(t, req, false) 375 if err := req.ParseMultipartForm(25); err != nil { 376 t.Fatal("ParseMultipartForm second call:", err) 377 } 378 validateTestMultipartContents(t, req, false) 379 } 380 381 // Issue #25192: Test that ParseMultipartForm fails but still parses the 382 // multi-part form when an URL containing a semicolon is provided. 383 func TestParseMultipartFormSemicolonSeparator(t *testing.T) { 384 req := newTestMultipartRequest(t) 385 req.URL = &url.URL{RawQuery: "q=foo;q=bar"} 386 if err := req.ParseMultipartForm(25); err == nil { 387 t.Fatal("ParseMultipartForm expected error due to invalid semicolon, got nil") 388 } 389 defer req.MultipartForm.RemoveAll() 390 validateTestMultipartContents(t, req, false) 391 } 392 393 func TestMultipartRequestAuto(t *testing.T) { 394 // Test that FormValue and FormFile automatically invoke 395 // ParseMultipartForm and return the right values. 396 req := newTestMultipartRequest(t) 397 defer func() { 398 if req.MultipartForm != nil { 399 req.MultipartForm.RemoveAll() 400 } 401 }() 402 validateTestMultipartContents(t, req, true) 403 } 404 405 func TestMissingFileMultipartRequest(t *testing.T) { 406 // Test that FormFile returns an error if 407 // the named file is missing. 408 req := newTestMultipartRequest(t) 409 testMissingFile(t, req) 410 } 411 412 // Test that FormValue invokes ParseMultipartForm. 413 func TestFormValueCallsParseMultipartForm(t *testing.T) { 414 req, _ := NewRequest("POST", "http://www.google.com/", strings.NewReader("z=post")) 415 req.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value") 416 if req.Form != nil { 417 t.Fatal("Unexpected request Form, want nil") 418 } 419 req.FormValue("z") 420 if req.Form == nil { 421 t.Fatal("ParseMultipartForm not called by FormValue") 422 } 423 } 424 425 // Test that FormFile invokes ParseMultipartForm. 426 func TestFormFileCallsParseMultipartForm(t *testing.T) { 427 req := newTestMultipartRequest(t) 428 if req.Form != nil { 429 t.Fatal("Unexpected request Form, want nil") 430 } 431 req.FormFile("") 432 if req.Form == nil { 433 t.Fatal("ParseMultipartForm not called by FormFile") 434 } 435 } 436 437 // Test that ParseMultipartForm errors if called 438 // after MultipartReader on the same request. 439 func TestParseMultipartFormOrder(t *testing.T) { 440 req := newTestMultipartRequest(t) 441 if _, err := req.MultipartReader(); err != nil { 442 t.Fatalf("MultipartReader: %v", err) 443 } 444 if err := req.ParseMultipartForm(1024); err == nil { 445 t.Fatal("expected an error from ParseMultipartForm after call to MultipartReader") 446 } 447 } 448 449 // Test that MultipartReader errors if called 450 // after ParseMultipartForm on the same request. 451 func TestMultipartReaderOrder(t *testing.T) { 452 req := newTestMultipartRequest(t) 453 if err := req.ParseMultipartForm(25); err != nil { 454 t.Fatalf("ParseMultipartForm: %v", err) 455 } 456 defer req.MultipartForm.RemoveAll() 457 if _, err := req.MultipartReader(); err == nil { 458 t.Fatal("expected an error from MultipartReader after call to ParseMultipartForm") 459 } 460 } 461 462 // Test that FormFile errors if called after 463 // MultipartReader on the same request. 464 func TestFormFileOrder(t *testing.T) { 465 req := newTestMultipartRequest(t) 466 if _, err := req.MultipartReader(); err != nil { 467 t.Fatalf("MultipartReader: %v", err) 468 } 469 if _, _, err := req.FormFile(""); err == nil { 470 t.Fatal("expected an error from FormFile after call to MultipartReader") 471 } 472 } 473 474 var readRequestErrorTests = []struct { 475 in string 476 err string 477 478 header Header 479 }{ 480 0: {"GET / HTTP/1.1\r\nheader:foo\r\n\r\n", "", Header{"Header": {"foo"}}}, 481 1: {"GET / HTTP/1.1\r\nheader:foo\r\n", io.ErrUnexpectedEOF.Error(), nil}, 482 2: {"", io.EOF.Error(), nil}, 483 3: { 484 in: "HEAD / HTTP/1.1\r\n\r\n", 485 header: Header{}, 486 }, 487 488 // Multiple Content-Length values should either be 489 // deduplicated if same or reject otherwise 490 // See Issue 16490. 491 4: { 492 in: "POST / HTTP/1.1\r\nContent-Length: 10\r\nContent-Length: 0\r\n\r\nGopher hey\r\n", 493 err: "cannot contain multiple Content-Length headers", 494 }, 495 5: { 496 in: "POST / HTTP/1.1\r\nContent-Length: 10\r\nContent-Length: 6\r\n\r\nGopher\r\n", 497 err: "cannot contain multiple Content-Length headers", 498 }, 499 6: { 500 in: "PUT / HTTP/1.1\r\nContent-Length: 6 \r\nContent-Length: 6\r\nContent-Length:6\r\n\r\nGopher\r\n", 501 err: "", 502 header: Header{"Content-Length": {"6"}}, 503 }, 504 7: { 505 in: "PUT / HTTP/1.1\r\nContent-Length: 1\r\nContent-Length: 6 \r\n\r\n", 506 err: "cannot contain multiple Content-Length headers", 507 }, 508 8: { 509 in: "POST / HTTP/1.1\r\nContent-Length:\r\nContent-Length: 3\r\n\r\n", 510 err: "cannot contain multiple Content-Length headers", 511 }, 512 9: { 513 in: "HEAD / HTTP/1.1\r\nContent-Length:0\r\nContent-Length: 0\r\n\r\n", 514 header: Header{"Content-Length": {"0"}}, 515 }, 516 10: { 517 in: "HEAD / HTTP/1.1\r\nHost: foo\r\nHost: bar\r\n\r\n\r\n\r\n", 518 err: "too many Host headers", 519 }, 520 } 521 522 func TestReadRequestErrors(t *testing.T) { 523 for i, tt := range readRequestErrorTests { 524 req, err := ReadRequest(bufio.NewReader(strings.NewReader(tt.in))) 525 if err == nil { 526 if tt.err != "" { 527 t.Errorf("#%d: got nil err; want %q", i, tt.err) 528 } 529 530 if !reflect.DeepEqual(tt.header, req.Header) { 531 t.Errorf("#%d: gotHeader: %q wantHeader: %q", i, req.Header, tt.header) 532 } 533 continue 534 } 535 536 if tt.err == "" || !strings.Contains(err.Error(), tt.err) { 537 t.Errorf("%d: got error = %v; want %v", i, err, tt.err) 538 } 539 } 540 } 541 542 var newRequestHostTests = []struct { 543 in, out string 544 }{ 545 {"http://www.example.com/", "www.example.com"}, 546 {"http://www.example.com:8080/", "www.example.com:8080"}, 547 548 {"http://192.168.0.1/", "192.168.0.1"}, 549 {"http://192.168.0.1:8080/", "192.168.0.1:8080"}, 550 {"http://192.168.0.1:/", "192.168.0.1"}, 551 552 {"http://[fe80::1]/", "[fe80::1]"}, 553 {"http://[fe80::1]:8080/", "[fe80::1]:8080"}, 554 {"http://[fe80::1%25en0]/", "[fe80::1%en0]"}, 555 {"http://[fe80::1%25en0]:8080/", "[fe80::1%en0]:8080"}, 556 {"http://[fe80::1%25en0]:/", "[fe80::1%en0]"}, 557 } 558 559 func TestNewRequestHost(t *testing.T) { 560 for i, tt := range newRequestHostTests { 561 req, err := NewRequest("GET", tt.in, nil) 562 if err != nil { 563 t.Errorf("#%v: %v", i, err) 564 continue 565 } 566 if req.Host != tt.out { 567 t.Errorf("got %q; want %q", req.Host, tt.out) 568 } 569 } 570 } 571 572 func TestRequestInvalidMethod(t *testing.T) { 573 _, err := NewRequest("bad method", "http://foo.com/", nil) 574 if err == nil { 575 t.Error("expected error from NewRequest with invalid method") 576 } 577 req, err := NewRequest("GET", "http://foo.example/", nil) 578 if err != nil { 579 t.Fatal(err) 580 } 581 req.Method = "bad method" 582 _, err = DefaultClient.Do(req) 583 if err == nil || !strings.Contains(err.Error(), "invalid method") { 584 t.Errorf("Transport error = %v; want invalid method", err) 585 } 586 587 req, err = NewRequest("", "http://foo.com/", nil) 588 if err != nil { 589 t.Errorf("NewRequest(empty method) = %v; want nil", err) 590 } else if req.Method != "GET" { 591 t.Errorf("NewRequest(empty method) has method %q; want GET", req.Method) 592 } 593 } 594 595 func TestNewRequestContentLength(t *testing.T) { 596 readByte := func(r io.Reader) io.Reader { 597 var b [1]byte 598 r.Read(b[:]) 599 return r 600 } 601 tests := []struct { 602 r io.Reader 603 want int64 604 }{ 605 {bytes.NewReader([]byte("123")), 3}, 606 {bytes.NewBuffer([]byte("1234")), 4}, 607 {strings.NewReader("12345"), 5}, 608 {strings.NewReader(""), 0}, 609 {NoBody, 0}, 610 611 // Not detected. During Go 1.8 we tried to make these set to -1, but 612 // due to Issue 18117, we keep these returning 0, even though they're 613 // unknown. 614 {struct{ io.Reader }{strings.NewReader("xyz")}, 0}, 615 {io.NewSectionReader(strings.NewReader("x"), 0, 6), 0}, 616 {readByte(io.NewSectionReader(strings.NewReader("xy"), 0, 6)), 0}, 617 } 618 for i, tt := range tests { 619 req, err := NewRequest("POST", "http://localhost/", tt.r) 620 if err != nil { 621 t.Fatal(err) 622 } 623 if req.ContentLength != tt.want { 624 t.Errorf("test[%d]: ContentLength(%T) = %d; want %d", i, tt.r, req.ContentLength, tt.want) 625 } 626 } 627 } 628 629 var parseHTTPVersionTests = []struct { 630 vers string 631 major, minor int 632 ok bool 633 }{ 634 {"HTTP/0.0", 0, 0, true}, 635 {"HTTP/0.9", 0, 9, true}, 636 {"HTTP/1.0", 1, 0, true}, 637 {"HTTP/1.1", 1, 1, true}, 638 639 {"HTTP", 0, 0, false}, 640 {"HTTP/one.one", 0, 0, false}, 641 {"HTTP/1.1/", 0, 0, false}, 642 {"HTTP/-1,0", 0, 0, false}, 643 {"HTTP/0,-1", 0, 0, false}, 644 {"HTTP/", 0, 0, false}, 645 {"HTTP/1,1", 0, 0, false}, 646 {"HTTP/+1.1", 0, 0, false}, 647 {"HTTP/1.+1", 0, 0, false}, 648 {"HTTP/0000000001.1", 0, 0, false}, 649 {"HTTP/1.0000000001", 0, 0, false}, 650 {"HTTP/3.14", 0, 0, false}, 651 {"HTTP/12.3", 0, 0, false}, 652 } 653 654 func TestParseHTTPVersion(t *testing.T) { 655 for _, tt := range parseHTTPVersionTests { 656 major, minor, ok := ParseHTTPVersion(tt.vers) 657 if ok != tt.ok || major != tt.major || minor != tt.minor { 658 type version struct { 659 major, minor int 660 ok bool 661 } 662 t.Errorf("failed to parse %q, expected: %#v, got %#v", tt.vers, version{tt.major, tt.minor, tt.ok}, version{major, minor, ok}) 663 } 664 } 665 } 666 667 type getBasicAuthTest struct { 668 username, password string 669 ok bool 670 } 671 672 type basicAuthCredentialsTest struct { 673 username, password string 674 } 675 676 var getBasicAuthTests = []struct { 677 username, password string 678 ok bool 679 }{ 680 {"Aladdin", "open sesame", true}, 681 {"Aladdin", "open:sesame", true}, 682 {"", "", true}, 683 } 684 685 func TestGetBasicAuth(t *testing.T) { 686 for _, tt := range getBasicAuthTests { 687 r, _ := NewRequest("GET", "http://example.com/", nil) 688 r.SetBasicAuth(tt.username, tt.password) 689 username, password, ok := r.BasicAuth() 690 if ok != tt.ok || username != tt.username || password != tt.password { 691 t.Errorf("BasicAuth() = %#v, want %#v", getBasicAuthTest{username, password, ok}, 692 getBasicAuthTest{tt.username, tt.password, tt.ok}) 693 } 694 } 695 // Unauthenticated request. 696 r, _ := NewRequest("GET", "http://example.com/", nil) 697 username, password, ok := r.BasicAuth() 698 if ok { 699 t.Errorf("expected false from BasicAuth when the request is unauthenticated") 700 } 701 want := basicAuthCredentialsTest{"", ""} 702 if username != want.username || password != want.password { 703 t.Errorf("expected credentials: %#v when the request is unauthenticated, got %#v", 704 want, basicAuthCredentialsTest{username, password}) 705 } 706 } 707 708 var parseBasicAuthTests = []struct { 709 header, username, password string 710 ok bool 711 }{ 712 {"Basic " + base64.StdEncoding.EncodeToString([]byte("Aladdin:open sesame")), "Aladdin", "open sesame", true}, 713 714 // Case doesn't matter: 715 {"BASIC " + base64.StdEncoding.EncodeToString([]byte("Aladdin:open sesame")), "Aladdin", "open sesame", true}, 716 {"basic " + base64.StdEncoding.EncodeToString([]byte("Aladdin:open sesame")), "Aladdin", "open sesame", true}, 717 718 {"Basic " + base64.StdEncoding.EncodeToString([]byte("Aladdin:open:sesame")), "Aladdin", "open:sesame", true}, 719 {"Basic " + base64.StdEncoding.EncodeToString([]byte(":")), "", "", true}, 720 {"Basic" + base64.StdEncoding.EncodeToString([]byte("Aladdin:open sesame")), "", "", false}, 721 {base64.StdEncoding.EncodeToString([]byte("Aladdin:open sesame")), "", "", false}, 722 {"Basic ", "", "", false}, 723 {"Basic Aladdin:open sesame", "", "", false}, 724 {`Digest username="Aladdin"`, "", "", false}, 725 } 726 727 func TestParseBasicAuth(t *testing.T) { 728 for _, tt := range parseBasicAuthTests { 729 r, _ := NewRequest("GET", "http://example.com/", nil) 730 r.Header.Set("Authorization", tt.header) 731 username, password, ok := r.BasicAuth() 732 if ok != tt.ok || username != tt.username || password != tt.password { 733 t.Errorf("BasicAuth() = %#v, want %#v", getBasicAuthTest{username, password, ok}, 734 getBasicAuthTest{tt.username, tt.password, tt.ok}) 735 } 736 } 737 } 738 739 type logWrites struct { 740 t *testing.T 741 dst *[]string 742 } 743 744 func (l logWrites) WriteByte(c byte) error { 745 l.t.Fatalf("unexpected WriteByte call") 746 return nil 747 } 748 749 func (l logWrites) Write(p []byte) (n int, err error) { 750 *l.dst = append(*l.dst, string(p)) 751 return len(p), nil 752 } 753 754 func TestRequestWriteBufferedWriter(t *testing.T) { 755 got := []string{} 756 req, _ := NewRequest("GET", "http://foo.com/", nil) 757 req.Write(logWrites{t, &got}) 758 want := []string{ 759 "GET / HTTP/1.1\r\n", 760 "Host: foo.com\r\n", 761 "User-Agent: " + DefaultUserAgent + "\r\n", 762 "\r\n", 763 } 764 if !reflect.DeepEqual(got, want) { 765 t.Errorf("Writes = %q\n Want = %q", got, want) 766 } 767 } 768 769 func TestRequestBadHost(t *testing.T) { 770 got := []string{} 771 req, err := NewRequest("GET", "http://foo/after", nil) 772 if err != nil { 773 t.Fatal(err) 774 } 775 req.Host = "foo.com with spaces" 776 req.URL.Host = "foo.com with spaces" 777 req.Write(logWrites{t, &got}) 778 want := []string{ 779 "GET /after HTTP/1.1\r\n", 780 "Host: foo.com\r\n", 781 "User-Agent: " + DefaultUserAgent + "\r\n", 782 "\r\n", 783 } 784 if !reflect.DeepEqual(got, want) { 785 t.Errorf("Writes = %q\n Want = %q", got, want) 786 } 787 } 788 789 func TestStarRequest(t *testing.T) { 790 req, err := ReadRequest(bufio.NewReader(strings.NewReader("M-SEARCH * HTTP/1.1\r\n\r\n"))) 791 if err != nil { 792 return 793 } 794 if req.ContentLength != 0 { 795 t.Errorf("ContentLength = %d; want 0", req.ContentLength) 796 } 797 if req.Body == nil { 798 t.Errorf("Body = nil; want non-nil") 799 } 800 801 // Request.Write has Client semantics for Body/ContentLength, 802 // where ContentLength 0 means unknown if Body is non-nil, and 803 // thus chunking will happen unless we change semantics and 804 // signal that we want to serialize it as exactly zero. The 805 // only way to do that for outbound requests is with a nil 806 // Body: 807 clientReq := *req 808 clientReq.Body = nil 809 810 var out strings.Builder 811 if err := clientReq.Write(&out); err != nil { 812 t.Fatal(err) 813 } 814 815 if strings.Contains(out.String(), "chunked") { 816 t.Error("wrote chunked request; want no body") 817 } 818 back, err := ReadRequest(bufio.NewReader(strings.NewReader(out.String()))) 819 if err != nil { 820 t.Fatal(err) 821 } 822 // Ignore the Headers (the User-Agent breaks the deep equal, 823 // but we don't care about it) 824 req.Header = nil 825 back.Header = nil 826 if !reflect.DeepEqual(req, back) { 827 t.Errorf("Original request doesn't match Request read back.") 828 t.Logf("Original: %#v", req) 829 t.Logf("Original.URL: %#v", req.URL) 830 t.Logf("Wrote: %s", out.String()) 831 t.Logf("Read back (doesn't match Original): %#v", back) 832 } 833 } 834 835 type responseWriterJustWriter struct { 836 io.Writer 837 } 838 839 func (responseWriterJustWriter) Header() Header { panic("should not be called") } 840 func (responseWriterJustWriter) WriteHeader(int) { panic("should not be called") } 841 842 // delayedEOFReader never returns (n > 0, io.EOF), instead putting 843 // off the io.EOF until a subsequent Read call. 844 type delayedEOFReader struct { 845 r io.Reader 846 } 847 848 func (dr delayedEOFReader) Read(p []byte) (n int, err error) { 849 n, err = dr.r.Read(p) 850 if n > 0 && err == io.EOF { 851 err = nil 852 } 853 return 854 } 855 856 func TestIssue10884_MaxBytesEOF(t *testing.T) { 857 dst := io.Discard 858 _, err := io.Copy(dst, MaxBytesReader( 859 responseWriterJustWriter{dst}, 860 io.NopCloser(delayedEOFReader{strings.NewReader("12345")}), 861 5)) 862 if err != nil { 863 t.Fatal(err) 864 } 865 } 866 867 // Issue 14981: MaxBytesReader's return error wasn't sticky. It 868 // doesn't technically need to be, but people expected it to be. 869 func TestMaxBytesReaderStickyError(t *testing.T) { 870 isSticky := func(r io.Reader) error { 871 var log bytes.Buffer 872 buf := make([]byte, 1000) 873 var firstErr error 874 for { 875 n, err := r.Read(buf) 876 fmt.Fprintf(&log, "Read(%d) = %d, %v\n", len(buf), n, err) 877 if err == nil { 878 continue 879 } 880 if firstErr == nil { 881 firstErr = err 882 continue 883 } 884 if !reflect.DeepEqual(err, firstErr) { 885 return fmt.Errorf("non-sticky error. got log:\n%s", log.Bytes()) 886 } 887 t.Logf("Got log: %s", log.Bytes()) 888 return nil 889 } 890 } 891 tests := [...]struct { 892 readable int 893 limit int64 894 }{ 895 0: {99, 100}, 896 1: {100, 100}, 897 2: {101, 100}, 898 } 899 for i, tt := range tests { 900 rc := MaxBytesReader(nil, io.NopCloser(bytes.NewReader(make([]byte, tt.readable))), tt.limit) 901 if err := isSticky(rc); err != nil { 902 t.Errorf("%d. error: %v", i, err) 903 } 904 } 905 } 906 907 // Issue 45101: maxBytesReader's Read panicked when n < -1. This test 908 // also ensures that Read treats negative limits as equivalent to 0. 909 func TestMaxBytesReaderDifferentLimits(t *testing.T) { 910 const testStr = "1234" 911 tests := [...]struct { 912 limit int64 913 lenP int 914 wantN int 915 wantErr bool 916 }{ 917 0: { 918 limit: -123, 919 lenP: 0, 920 wantN: 0, 921 wantErr: false, // Ensure we won't return an error when the limit is negative, but we don't need to read. 922 }, 923 1: { 924 limit: -100, 925 lenP: 32 * 1024, 926 wantN: 0, 927 wantErr: true, 928 }, 929 2: { 930 limit: -2, 931 lenP: 1, 932 wantN: 0, 933 wantErr: true, 934 }, 935 3: { 936 limit: -1, 937 lenP: 2, 938 wantN: 0, 939 wantErr: true, 940 }, 941 4: { 942 limit: 0, 943 lenP: 3, 944 wantN: 0, 945 wantErr: true, 946 }, 947 5: { 948 limit: 1, 949 lenP: 4, 950 wantN: 1, 951 wantErr: true, 952 }, 953 6: { 954 limit: 2, 955 lenP: 5, 956 wantN: 2, 957 wantErr: true, 958 }, 959 7: { 960 limit: 3, 961 lenP: 2, 962 wantN: 2, 963 wantErr: false, 964 }, 965 8: { 966 limit: int64(len(testStr)), 967 lenP: len(testStr), 968 wantN: len(testStr), 969 wantErr: false, 970 }, 971 9: { 972 limit: 100, 973 lenP: 6, 974 wantN: len(testStr), 975 wantErr: false, 976 }, 977 10: { /* Issue 54408 */ 978 limit: int64(1<<63 - 1), 979 lenP: len(testStr), 980 wantN: len(testStr), 981 wantErr: false, 982 }, 983 } 984 for i, tt := range tests { 985 rc := MaxBytesReader(nil, io.NopCloser(strings.NewReader(testStr)), tt.limit) 986 987 n, err := rc.Read(make([]byte, tt.lenP)) 988 989 if n != tt.wantN { 990 t.Errorf("%d. n: %d, want n: %d", i, n, tt.wantN) 991 } 992 993 if (err != nil) != tt.wantErr { 994 t.Errorf("%d. error: %v", i, err) 995 } 996 } 997 } 998 999 func TestWithContextNilURL(t *testing.T) { 1000 req, err := NewRequest("POST", "https://golang.org/", nil) 1001 if err != nil { 1002 t.Fatal(err) 1003 } 1004 1005 // Issue 20601 1006 req.URL = nil 1007 reqCopy := req.WithContext(context.Background()) 1008 if reqCopy.URL != nil { 1009 t.Error("expected nil URL in cloned request") 1010 } 1011 } 1012 1013 // Ensure that Request.Clone creates a deep copy of TransferEncoding. 1014 // See issue 41907. 1015 func TestRequestCloneTransferEncoding(t *testing.T) { 1016 body := strings.NewReader("body") 1017 req, _ := NewRequest("POST", "https://example.org/", body) 1018 req.TransferEncoding = []string{ 1019 "encoding1", 1020 } 1021 1022 clonedReq := req.Clone(context.Background()) 1023 // modify original after deep copy 1024 req.TransferEncoding[0] = "encoding2" 1025 1026 if req.TransferEncoding[0] != "encoding2" { 1027 t.Error("expected req.TransferEncoding to be changed") 1028 } 1029 if clonedReq.TransferEncoding[0] != "encoding1" { 1030 t.Error("expected clonedReq.TransferEncoding to be unchanged") 1031 } 1032 } 1033 1034 // Issue 34878: verify we don't panic when including basic auth (Go 1.13 regression) 1035 func TestNoPanicOnRoundTripWithBasicAuth(t *testing.T) { run(t, testNoPanicWithBasicAuth) } 1036 func testNoPanicWithBasicAuth(t *testing.T, mode testMode) { 1037 cst := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {})) 1038 1039 u, err := url.Parse(cst.ts.URL) 1040 if err != nil { 1041 t.Fatal(err) 1042 } 1043 u.User = url.UserPassword("foo", "bar") 1044 req := &Request{ 1045 URL: u, 1046 Method: "GET", 1047 } 1048 if _, err := cst.c.Do(req); err != nil { 1049 t.Fatalf("Unexpected error: %v", err) 1050 } 1051 } 1052 1053 // verify that NewRequest sets Request.GetBody and that it works 1054 func TestNewRequestGetBody(t *testing.T) { 1055 tests := []struct { 1056 r io.Reader 1057 }{ 1058 {r: strings.NewReader("hello")}, 1059 {r: bytes.NewReader([]byte("hello"))}, 1060 {r: bytes.NewBuffer([]byte("hello"))}, 1061 } 1062 for i, tt := range tests { 1063 req, err := NewRequest("POST", "http://foo.tld/", tt.r) 1064 if err != nil { 1065 t.Errorf("test[%d]: %v", i, err) 1066 continue 1067 } 1068 if req.Body == nil { 1069 t.Errorf("test[%d]: Body = nil", i) 1070 continue 1071 } 1072 if req.GetBody == nil { 1073 t.Errorf("test[%d]: GetBody = nil", i) 1074 continue 1075 } 1076 slurp1, err := io.ReadAll(req.Body) 1077 if err != nil { 1078 t.Errorf("test[%d]: ReadAll(Body) = %v", i, err) 1079 } 1080 newBody, err := req.GetBody() 1081 if err != nil { 1082 t.Errorf("test[%d]: GetBody = %v", i, err) 1083 } 1084 slurp2, err := io.ReadAll(newBody) 1085 if err != nil { 1086 t.Errorf("test[%d]: ReadAll(GetBody()) = %v", i, err) 1087 } 1088 if string(slurp1) != string(slurp2) { 1089 t.Errorf("test[%d]: Body %q != GetBody %q", i, slurp1, slurp2) 1090 } 1091 } 1092 } 1093 1094 func testMissingFile(t *testing.T, req *Request) { 1095 f, fh, err := req.FormFile("missing") 1096 if f != nil { 1097 t.Errorf("FormFile file = %v, want nil", f) 1098 } 1099 if fh != nil { 1100 t.Errorf("FormFile file header = %q, want nil", fh) 1101 } 1102 if err != ErrMissingFile { 1103 t.Errorf("FormFile err = %q, want ErrMissingFile", err) 1104 } 1105 } 1106 1107 func newTestMultipartRequest(t *testing.T) *Request { 1108 b := strings.NewReader(strings.ReplaceAll(message, "\n", "\r\n")) 1109 req, err := NewRequest("POST", "/", b) 1110 if err != nil { 1111 t.Fatal("NewRequest:", err) 1112 } 1113 ctype := fmt.Sprintf(`multipart/form-data; boundary="%s"`, boundary) 1114 req.Header.Set("Content-type", ctype) 1115 return req 1116 } 1117 1118 func validateTestMultipartContents(t *testing.T, req *Request, allMem bool) { 1119 if g, e := req.FormValue("texta"), textaValue; g != e { 1120 t.Errorf("texta value = %q, want %q", g, e) 1121 } 1122 if g, e := req.FormValue("textb"), textbValue; g != e { 1123 t.Errorf("textb value = %q, want %q", g, e) 1124 } 1125 if g := req.FormValue("missing"); g != "" { 1126 t.Errorf("missing value = %q, want empty string", g) 1127 } 1128 1129 assertMem := func(n string, fd multipart.File) { 1130 if _, ok := fd.(*os.File); ok { 1131 t.Error(n, " is *os.File, should not be") 1132 } 1133 } 1134 fda := testMultipartFile(t, req, "filea", "filea.txt", fileaContents) 1135 defer fda.Close() 1136 assertMem("filea", fda) 1137 fdb := testMultipartFile(t, req, "fileb", "fileb.txt", filebContents) 1138 defer fdb.Close() 1139 if allMem { 1140 assertMem("fileb", fdb) 1141 } else { 1142 if _, ok := fdb.(*os.File); !ok { 1143 t.Errorf("fileb has unexpected underlying type %T", fdb) 1144 } 1145 } 1146 1147 testMissingFile(t, req) 1148 } 1149 1150 func testMultipartFile(t *testing.T, req *Request, key, expectFilename, expectContent string) multipart.File { 1151 f, fh, err := req.FormFile(key) 1152 if err != nil { 1153 t.Fatalf("FormFile(%q): %q", key, err) 1154 } 1155 if fh.Filename != expectFilename { 1156 t.Errorf("filename = %q, want %q", fh.Filename, expectFilename) 1157 } 1158 var b strings.Builder 1159 _, err = io.Copy(&b, f) 1160 if err != nil { 1161 t.Fatal("copying contents:", err) 1162 } 1163 if g := b.String(); g != expectContent { 1164 t.Errorf("contents = %q, want %q", g, expectContent) 1165 } 1166 return f 1167 } 1168 1169 // Issue 53181: verify Request.Cookie return the correct Cookie. 1170 // Return ErrNoCookie instead of the first cookie when name is "". 1171 func TestRequestCookie(t *testing.T) { 1172 for _, tt := range []struct { 1173 name string 1174 value string 1175 expectedErr error 1176 }{ 1177 { 1178 name: "foo", 1179 value: "bar", 1180 expectedErr: nil, 1181 }, 1182 { 1183 name: "", 1184 expectedErr: ErrNoCookie, 1185 }, 1186 } { 1187 req, err := NewRequest("GET", "http://example.com/", nil) 1188 if err != nil { 1189 t.Fatal(err) 1190 } 1191 req.AddCookie(&Cookie{Name: tt.name, Value: tt.value}) 1192 c, err := req.Cookie(tt.name) 1193 if err != tt.expectedErr { 1194 t.Errorf("got %v, want %v", err, tt.expectedErr) 1195 } 1196 1197 // skip if error occurred. 1198 if err != nil { 1199 continue 1200 } 1201 if c.Value != tt.value { 1202 t.Errorf("got %v, want %v", c.Value, tt.value) 1203 } 1204 if c.Name != tt.name { 1205 t.Errorf("got %s, want %v", tt.name, c.Name) 1206 } 1207 } 1208 } 1209 1210 const ( 1211 fileaContents = "This is a test file." 1212 filebContents = "Another test file." 1213 textaValue = "foo" 1214 textbValue = "bar" 1215 boundary = `MyBoundary` 1216 ) 1217 1218 const message = ` 1219 --MyBoundary 1220 Content-Disposition: form-data; name="filea"; filename="filea.txt" 1221 Content-Type: text/plain 1222 1223 ` + fileaContents + ` 1224 --MyBoundary 1225 Content-Disposition: form-data; name="fileb"; filename="fileb.txt" 1226 Content-Type: text/plain 1227 1228 ` + filebContents + ` 1229 --MyBoundary 1230 Content-Disposition: form-data; name="texta" 1231 1232 ` + textaValue + ` 1233 --MyBoundary 1234 Content-Disposition: form-data; name="textb" 1235 1236 ` + textbValue + ` 1237 --MyBoundary-- 1238 ` 1239 1240 func benchmarkReadRequest(b *testing.B, request string) { 1241 request = request + "\n" // final \n 1242 request = strings.ReplaceAll(request, "\n", "\r\n") // expand \n to \r\n 1243 b.SetBytes(int64(len(request))) 1244 r := bufio.NewReader(&infiniteReader{buf: []byte(request)}) 1245 b.ReportAllocs() 1246 b.ResetTimer() 1247 for i := 0; i < b.N; i++ { 1248 _, err := ReadRequest(r) 1249 if err != nil { 1250 b.Fatalf("failed to read request: %v", err) 1251 } 1252 } 1253 } 1254 1255 // infiniteReader satisfies Read requests as if the contents of buf 1256 // loop indefinitely. 1257 type infiniteReader struct { 1258 buf []byte 1259 offset int 1260 } 1261 1262 func (r *infiniteReader) Read(b []byte) (int, error) { 1263 n := copy(b, r.buf[r.offset:]) 1264 r.offset = (r.offset + n) % len(r.buf) 1265 return n, nil 1266 } 1267 1268 func BenchmarkReadRequestChrome(b *testing.B) { 1269 // https://github.com/felixge/node-http-perf/blob/master/fixtures/get.http 1270 benchmarkReadRequest(b, `GET / HTTP/1.1 1271 Host: localhost:8080 1272 Connection: keep-alive 1273 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 1274 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 1275 Accept-Encoding: gzip,deflate,sdch 1276 Accept-Language: en-US,en;q=0.8 1277 Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3 1278 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 1279 `) 1280 } 1281 1282 func BenchmarkReadRequestCurl(b *testing.B) { 1283 // curl http://localhost:8080/ 1284 benchmarkReadRequest(b, `GET / HTTP/1.1 1285 User-Agent: curl/7.27.0 1286 Host: localhost:8080 1287 Accept: */* 1288 `) 1289 } 1290 1291 func BenchmarkReadRequestApachebench(b *testing.B) { 1292 // ab -n 1 -c 1 http://localhost:8080/ 1293 benchmarkReadRequest(b, `GET / HTTP/1.0 1294 Host: localhost:8080 1295 User-Agent: ApacheBench/2.3 1296 Accept: */* 1297 `) 1298 } 1299 1300 func BenchmarkReadRequestSiege(b *testing.B) { 1301 // siege -r 1 -c 1 http://localhost:8080/ 1302 benchmarkReadRequest(b, `GET / HTTP/1.1 1303 Host: localhost:8080 1304 Accept: */* 1305 Accept-Encoding: gzip 1306 User-Agent: JoeDog/1.00 [en] (X11; I; Siege 2.70) 1307 Connection: keep-alive 1308 `) 1309 } 1310 1311 func BenchmarkReadRequestWrk(b *testing.B) { 1312 // wrk -t 1 -r 1 -c 1 http://localhost:8080/ 1313 benchmarkReadRequest(b, `GET / HTTP/1.1 1314 Host: localhost:8080 1315 `) 1316 } 1317 1318 func BenchmarkFileAndServer_1KB(b *testing.B) { 1319 benchmarkFileAndServer(b, 1<<10) 1320 } 1321 1322 func BenchmarkFileAndServer_16MB(b *testing.B) { 1323 benchmarkFileAndServer(b, 1<<24) 1324 } 1325 1326 func BenchmarkFileAndServer_64MB(b *testing.B) { 1327 benchmarkFileAndServer(b, 1<<26) 1328 } 1329 1330 func benchmarkFileAndServer(b *testing.B, n int64) { 1331 f, err := os.CreateTemp(os.TempDir(), "go-bench-http-file-and-server") 1332 if err != nil { 1333 b.Fatalf("Failed to create temp file: %v", err) 1334 } 1335 1336 defer func() { 1337 f.Close() 1338 os.RemoveAll(f.Name()) 1339 }() 1340 1341 if _, err := io.CopyN(f, rand.Reader, n); err != nil { 1342 b.Fatalf("Failed to copy %d bytes: %v", n, err) 1343 } 1344 1345 run(b, func(b *testing.B, mode testMode) { 1346 runFileAndServerBenchmarks(b, mode, f, n) 1347 }, []testMode{http1Mode, https1Mode, http2Mode}) 1348 } 1349 1350 func runFileAndServerBenchmarks(b *testing.B, mode testMode, f *os.File, n int64) { 1351 handler := HandlerFunc(func(rw ResponseWriter, req *Request) { 1352 defer req.Body.Close() 1353 nc, err := io.Copy(io.Discard, req.Body) 1354 if err != nil { 1355 panic(err) 1356 } 1357 1358 if nc != n { 1359 panic(fmt.Errorf("Copied %d Wanted %d bytes", nc, n)) 1360 } 1361 }) 1362 1363 cst := newClientServerTest(b, mode, handler).ts 1364 1365 b.ResetTimer() 1366 for i := 0; i < b.N; i++ { 1367 // Perform some setup. 1368 b.StopTimer() 1369 if _, err := f.Seek(0, 0); err != nil { 1370 b.Fatalf("Failed to seek back to file: %v", err) 1371 } 1372 1373 b.StartTimer() 1374 req, err := NewRequest("PUT", cst.URL, io.NopCloser(f)) 1375 if err != nil { 1376 b.Fatal(err) 1377 } 1378 1379 req.ContentLength = n 1380 // Prevent mime sniffing by setting the Content-Type. 1381 req.Header.Set("Content-Type", "application/octet-stream") 1382 res, err := cst.Client().Do(req) 1383 if err != nil { 1384 b.Fatalf("Failed to make request to backend: %v", err) 1385 } 1386 1387 res.Body.Close() 1388 b.SetBytes(n) 1389 } 1390 }