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