Source file
src/net/http/request_test.go
1
2
3
4
5 package http_test
6
7 import (
8 "bufio"
9 "bytes"
10 "context"
11 "crypto/rand"
12 "encoding/base64"
13 "encoding/json"
14 "errors"
15 "fmt"
16 "io"
17 "math"
18 "mime/multipart"
19 "net/http"
20 . "net/http"
21 "net/http/httptest"
22 "net/url"
23 "os"
24 "reflect"
25 "regexp"
26 "slices"
27 "strings"
28 "testing"
29 )
30
31 func TestQuery(t *testing.T) {
32 req := &Request{Method: "GET"}
33 req.URL, _ = url.Parse("http://www.google.com/search?q=foo&q=bar")
34 if q := req.FormValue("q"); q != "foo" {
35 t.Errorf(`req.FormValue("q") = %q, want "foo"`, q)
36 }
37 }
38
39
40
41 func TestParseFormSemicolonSeparator(t *testing.T) {
42 for _, method := range []string{"POST", "PATCH", "PUT", "GET"} {
43 req, _ := NewRequest(method, "http://www.google.com/search?q=foo;q=bar&a=1",
44 strings.NewReader("q"))
45 err := req.ParseForm()
46 if err == nil {
47 t.Fatalf(`for method %s, ParseForm expected an error, got success`, method)
48 }
49 wantForm := url.Values{"a": []string{"1"}}
50 if !reflect.DeepEqual(req.Form, wantForm) {
51 t.Fatalf("for method %s, ParseForm expected req.Form = %v, want %v", method, req.Form, wantForm)
52 }
53 }
54 }
55
56 func TestParseFormQuery(t *testing.T) {
57 req, _ := NewRequest("POST", "http://www.google.com/search?q=foo&q=bar&both=x&prio=1&orphan=nope&empty=not",
58 strings.NewReader("z=post&both=y&prio=2&=nokey&orphan&empty=&"))
59 req.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value")
60
61 if q := req.FormValue("q"); q != "foo" {
62 t.Errorf(`req.FormValue("q") = %q, want "foo"`, q)
63 }
64 if z := req.FormValue("z"); z != "post" {
65 t.Errorf(`req.FormValue("z") = %q, want "post"`, z)
66 }
67 if bq, found := req.PostForm["q"]; found {
68 t.Errorf(`req.PostForm["q"] = %q, want no entry in map`, bq)
69 }
70 if bz := req.PostFormValue("z"); bz != "post" {
71 t.Errorf(`req.PostFormValue("z") = %q, want "post"`, bz)
72 }
73 if qs := req.Form["q"]; !slices.Equal(qs, []string{"foo", "bar"}) {
74 t.Errorf(`req.Form["q"] = %q, want ["foo", "bar"]`, qs)
75 }
76 if both := req.Form["both"]; !slices.Equal(both, []string{"y", "x"}) {
77 t.Errorf(`req.Form["both"] = %q, want ["y", "x"]`, both)
78 }
79 if prio := req.FormValue("prio"); prio != "2" {
80 t.Errorf(`req.FormValue("prio") = %q, want "2" (from body)`, prio)
81 }
82 if orphan := req.Form["orphan"]; !slices.Equal(orphan, []string{"", "nope"}) {
83 t.Errorf(`req.FormValue("orphan") = %q, want "" (from body)`, orphan)
84 }
85 if empty := req.Form["empty"]; !slices.Equal(empty, []string{"", "not"}) {
86 t.Errorf(`req.FormValue("empty") = %q, want "" (from body)`, empty)
87 }
88 if nokey := req.Form[""]; !slices.Equal(nokey, []string{"nokey"}) {
89 t.Errorf(`req.FormValue("nokey") = %q, want "nokey" (from body)`, nokey)
90 }
91 }
92
93
94 func TestParseFormQueryMethods(t *testing.T) {
95 for _, method := range []string{"POST", "PATCH", "PUT", "FOO"} {
96 req, _ := NewRequest(method, "http://www.google.com/search",
97 strings.NewReader("foo=bar"))
98 req.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value")
99 want := "bar"
100 if method == "FOO" {
101 want = ""
102 }
103 if got := req.FormValue("foo"); got != want {
104 t.Errorf(`for method %s, FormValue("foo") = %q; want %q`, method, got, want)
105 }
106 }
107 }
108
109 func TestParseFormUnknownContentType(t *testing.T) {
110 for _, test := range []struct {
111 name string
112 wantErr string
113 contentType Header
114 }{
115 {"text", "", Header{"Content-Type": {"text/plain"}}},
116
117
118 {"empty", "", Header{}},
119 {"boundary", "mime: invalid media parameter", Header{"Content-Type": {"text/plain; boundary="}}},
120 {"unknown", "", Header{"Content-Type": {"application/unknown"}}},
121 } {
122 t.Run(test.name,
123 func(t *testing.T) {
124 req := &Request{
125 Method: "POST",
126 Header: test.contentType,
127 Body: io.NopCloser(strings.NewReader("body")),
128 }
129 err := req.ParseForm()
130 switch {
131 case err == nil && test.wantErr != "":
132 t.Errorf("unexpected success; want error %q", test.wantErr)
133 case err != nil && test.wantErr == "":
134 t.Errorf("want success, got error: %v", err)
135 case test.wantErr != "" && test.wantErr != fmt.Sprint(err):
136 t.Errorf("got error %q; want %q", err, test.wantErr)
137 }
138 },
139 )
140 }
141 }
142
143 func TestParseFormInitializeOnError(t *testing.T) {
144 nilBody, _ := NewRequest("POST", "http://www.google.com/search?q=foo", nil)
145 tests := []*Request{
146 nilBody,
147 {Method: "GET", URL: nil},
148 }
149 for i, req := range tests {
150 err := req.ParseForm()
151 if req.Form == nil {
152 t.Errorf("%d. Form not initialized, error %v", i, err)
153 }
154 if req.PostForm == nil {
155 t.Errorf("%d. PostForm not initialized, error %v", i, err)
156 }
157 }
158 }
159
160 func TestMultipartReader(t *testing.T) {
161 tests := []struct {
162 shouldError bool
163 contentType string
164 }{
165 {false, `multipart/form-data; boundary="foo123"`},
166 {false, `multipart/mixed; boundary="foo123"`},
167 {true, `text/plain`},
168 }
169
170 for i, test := range tests {
171 req := &Request{
172 Method: "POST",
173 Header: Header{"Content-Type": {test.contentType}},
174 Body: io.NopCloser(new(bytes.Buffer)),
175 }
176 multipart, err := req.MultipartReader()
177 if test.shouldError {
178 if err == nil || multipart != nil {
179 t.Errorf("test %d: unexpectedly got nil-error (%v) or non-nil-multipart (%v)", i, err, multipart)
180 }
181 continue
182 }
183 if err != nil || multipart == nil {
184 t.Errorf("test %d: unexpectedly got error (%v) or nil-multipart (%v)", i, err, multipart)
185 }
186 }
187 }
188
189
190 func TestParseMultipartFormPopulatesPostForm(t *testing.T) {
191 postData :=
192 `--xxx
193 Content-Disposition: form-data; name="field1"
194
195 value1
196 --xxx
197 Content-Disposition: form-data; name="field2"
198
199 value2
200 --xxx
201 Content-Disposition: form-data; name="file"; filename="file"
202 Content-Type: application/octet-stream
203 Content-Transfer-Encoding: binary
204
205 binary data
206 --xxx--
207 `
208 req := &Request{
209 Method: "POST",
210 Header: Header{"Content-Type": {`multipart/form-data; boundary=xxx`}},
211 Body: io.NopCloser(strings.NewReader(postData)),
212 }
213
214 initialFormItems := map[string]string{
215 "language": "Go",
216 "name": "gopher",
217 "skill": "go-ing",
218 "field2": "initial-value2",
219 }
220
221 req.Form = make(url.Values)
222 for k, v := range initialFormItems {
223 req.Form.Add(k, v)
224 }
225
226 err := req.ParseMultipartForm(10000)
227 if err != nil {
228 t.Fatalf("unexpected multipart error %v", err)
229 }
230
231 wantForm := url.Values{
232 "language": []string{"Go"},
233 "name": []string{"gopher"},
234 "skill": []string{"go-ing"},
235 "field1": []string{"value1"},
236 "field2": []string{"initial-value2", "value2"},
237 }
238 if !reflect.DeepEqual(req.Form, wantForm) {
239 t.Fatalf("req.Form = %v, want %v", req.Form, wantForm)
240 }
241
242 wantPostForm := url.Values{
243 "field1": []string{"value1"},
244 "field2": []string{"value2"},
245 }
246 if !reflect.DeepEqual(req.PostForm, wantPostForm) {
247 t.Fatalf("req.PostForm = %v, want %v", req.PostForm, wantPostForm)
248 }
249 }
250
251 func TestParseMultipartForm(t *testing.T) {
252 req := &Request{
253 Method: "POST",
254 Header: Header{"Content-Type": {`multipart/form-data; boundary="foo123"`}},
255 Body: io.NopCloser(new(bytes.Buffer)),
256 }
257 err := req.ParseMultipartForm(25)
258 if err == nil {
259 t.Error("expected multipart EOF, got nil")
260 }
261
262 req.Header = Header{"Content-Type": {"text/plain"}}
263 err = req.ParseMultipartForm(25)
264 if err != ErrNotMultipart {
265 t.Error("expected ErrNotMultipart for text/plain")
266 }
267 }
268
269
270 func TestParseMultipartFormFilename(t *testing.T) {
271 postData :=
272 `--xxx
273 Content-Disposition: form-data; name="file"; filename="../usr/foobar.txt/"
274 Content-Type: text/plain
275
276 --xxx--
277 `
278 req := &Request{
279 Method: "POST",
280 Header: Header{"Content-Type": {`multipart/form-data; boundary=xxx`}},
281 Body: io.NopCloser(strings.NewReader(postData)),
282 }
283 _, hdr, err := req.FormFile("file")
284 if err != nil {
285 t.Fatal(err)
286 }
287 if hdr.Filename != "foobar.txt" {
288 t.Errorf("expected only the last element of the path, got %q", hdr.Filename)
289 }
290 }
291
292
293
294
295 func TestMaxInt64ForMultipartFormMaxMemoryOverflow(t *testing.T) {
296 run(t, testMaxInt64ForMultipartFormMaxMemoryOverflow)
297 }
298 func testMaxInt64ForMultipartFormMaxMemoryOverflow(t *testing.T, mode testMode) {
299 payloadSize := 1 << 10
300 cst := newClientServerTest(t, mode, HandlerFunc(func(rw ResponseWriter, req *Request) {
301
302
303
304 if err := req.ParseMultipartForm(math.MaxInt64); err != nil {
305 Error(rw, err.Error(), StatusBadRequest)
306 return
307 }
308 })).ts
309 fBuf := new(bytes.Buffer)
310 mw := multipart.NewWriter(fBuf)
311 mf, err := mw.CreateFormFile("file", "myfile.txt")
312 if err != nil {
313 t.Fatal(err)
314 }
315 if _, err := mf.Write(bytes.Repeat([]byte("abc"), payloadSize)); err != nil {
316 t.Fatal(err)
317 }
318 if err := mw.Close(); err != nil {
319 t.Fatal(err)
320 }
321 req, err := NewRequest("POST", cst.URL, fBuf)
322 if err != nil {
323 t.Fatal(err)
324 }
325 req.Header.Set("Content-Type", mw.FormDataContentType())
326 res, err := cst.Client().Do(req)
327 if err != nil {
328 t.Fatal(err)
329 }
330 res.Body.Close()
331 if g, w := res.StatusCode, StatusOK; g != w {
332 t.Fatalf("Status code mismatch: got %d, want %d", g, w)
333 }
334 }
335
336 func TestRequestRedirect(t *testing.T) { run(t, testRequestRedirect) }
337 func testRequestRedirect(t *testing.T, mode testMode) {
338 cst := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
339 switch r.URL.Path {
340 case "/":
341 w.Header().Set("Location", "/foo/")
342 w.WriteHeader(StatusSeeOther)
343 case "/foo/":
344 fmt.Fprintf(w, "foo")
345 default:
346 w.WriteHeader(StatusBadRequest)
347 }
348 }))
349
350 var end = regexp.MustCompile("/foo/$")
351 r, err := cst.c.Get(cst.ts.URL)
352 if err != nil {
353 t.Fatal(err)
354 }
355 r.Body.Close()
356 url := r.Request.URL.String()
357 if r.StatusCode != 200 || !end.MatchString(url) {
358 t.Fatalf("Get got status %d at %q, want 200 matching /foo/$", r.StatusCode, url)
359 }
360 }
361
362 func TestSetBasicAuth(t *testing.T) {
363 r, _ := NewRequest("GET", "http://example.com/", nil)
364 r.SetBasicAuth("Aladdin", "open sesame")
365 if g, e := r.Header.Get("Authorization"), "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="; g != e {
366 t.Errorf("got header %q, want %q", g, e)
367 }
368 }
369
370 func TestMultipartRequest(t *testing.T) {
371
372
373
374 req := newTestMultipartRequest(t)
375 if err := req.ParseMultipartForm(25); err != nil {
376 t.Fatal("ParseMultipartForm first call:", err)
377 }
378 defer req.MultipartForm.RemoveAll()
379 validateTestMultipartContents(t, req, false)
380 if err := req.ParseMultipartForm(25); err != nil {
381 t.Fatal("ParseMultipartForm second call:", err)
382 }
383 validateTestMultipartContents(t, req, false)
384 }
385
386
387
388 func TestParseMultipartFormSemicolonSeparator(t *testing.T) {
389 req := newTestMultipartRequest(t)
390 req.URL = &url.URL{RawQuery: "q=foo;q=bar"}
391 if err := req.ParseMultipartForm(25); err == nil {
392 t.Fatal("ParseMultipartForm expected error due to invalid semicolon, got nil")
393 }
394 defer req.MultipartForm.RemoveAll()
395 validateTestMultipartContents(t, req, false)
396 }
397
398 func TestMultipartRequestAuto(t *testing.T) {
399
400
401 req := newTestMultipartRequest(t)
402 defer func() {
403 if req.MultipartForm != nil {
404 req.MultipartForm.RemoveAll()
405 }
406 }()
407 validateTestMultipartContents(t, req, true)
408 }
409
410 func TestMissingFileMultipartRequest(t *testing.T) {
411
412
413 req := newTestMultipartRequest(t)
414 testMissingFile(t, req)
415 }
416
417
418 func TestFormValueCallsParseMultipartForm(t *testing.T) {
419 req, _ := NewRequest("POST", "http://www.google.com/", strings.NewReader("z=post"))
420 req.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value")
421 if req.Form != nil {
422 t.Fatal("Unexpected request Form, want nil")
423 }
424 req.FormValue("z")
425 if req.Form == nil {
426 t.Fatal("ParseMultipartForm not called by FormValue")
427 }
428 }
429
430
431 func TestFormFileCallsParseMultipartForm(t *testing.T) {
432 req := newTestMultipartRequest(t)
433 if req.Form != nil {
434 t.Fatal("Unexpected request Form, want nil")
435 }
436 req.FormFile("")
437 if req.Form == nil {
438 t.Fatal("ParseMultipartForm not called by FormFile")
439 }
440 }
441
442
443
444 func TestParseMultipartFormOrder(t *testing.T) {
445 req := newTestMultipartRequest(t)
446 if _, err := req.MultipartReader(); err != nil {
447 t.Fatalf("MultipartReader: %v", err)
448 }
449 if err := req.ParseMultipartForm(1024); err == nil {
450 t.Fatal("expected an error from ParseMultipartForm after call to MultipartReader")
451 }
452 }
453
454
455
456 func TestMultipartReaderOrder(t *testing.T) {
457 req := newTestMultipartRequest(t)
458 if err := req.ParseMultipartForm(25); err != nil {
459 t.Fatalf("ParseMultipartForm: %v", err)
460 }
461 defer req.MultipartForm.RemoveAll()
462 if _, err := req.MultipartReader(); err == nil {
463 t.Fatal("expected an error from MultipartReader after call to ParseMultipartForm")
464 }
465 }
466
467
468
469 func TestFormFileOrder(t *testing.T) {
470 req := newTestMultipartRequest(t)
471 if _, err := req.MultipartReader(); err != nil {
472 t.Fatalf("MultipartReader: %v", err)
473 }
474 if _, _, err := req.FormFile(""); err == nil {
475 t.Fatal("expected an error from FormFile after call to MultipartReader")
476 }
477 }
478
479 var readRequestErrorTests = []struct {
480 in string
481 err string
482
483 header Header
484 }{
485 0: {"GET / HTTP/1.1\r\nheader:foo\r\n\r\n", "", Header{"Header": {"foo"}}},
486 1: {"GET / HTTP/1.1\r\nheader:foo\r\n", io.ErrUnexpectedEOF.Error(), nil},
487 2: {"", io.EOF.Error(), nil},
488 3: {
489 in: "HEAD / HTTP/1.1\r\n\r\n",
490 header: Header{},
491 },
492
493
494
495
496 4: {
497 in: "POST / HTTP/1.1\r\nContent-Length: 10\r\nContent-Length: 0\r\n\r\nGopher hey\r\n",
498 err: "cannot contain multiple Content-Length headers",
499 },
500 5: {
501 in: "POST / HTTP/1.1\r\nContent-Length: 10\r\nContent-Length: 6\r\n\r\nGopher\r\n",
502 err: "cannot contain multiple Content-Length headers",
503 },
504 6: {
505 in: "PUT / HTTP/1.1\r\nContent-Length: 6 \r\nContent-Length: 6\r\nContent-Length:6\r\n\r\nGopher\r\n",
506 err: "",
507 header: Header{"Content-Length": {"6"}},
508 },
509 7: {
510 in: "PUT / HTTP/1.1\r\nContent-Length: 1\r\nContent-Length: 6 \r\n\r\n",
511 err: "cannot contain multiple Content-Length headers",
512 },
513 8: {
514 in: "POST / HTTP/1.1\r\nContent-Length:\r\nContent-Length: 3\r\n\r\n",
515 err: "cannot contain multiple Content-Length headers",
516 },
517 9: {
518 in: "HEAD / HTTP/1.1\r\nContent-Length:0\r\nContent-Length: 0\r\n\r\n",
519 header: Header{"Content-Length": {"0"}},
520 },
521 10: {
522 in: "HEAD / HTTP/1.1\r\nHost: foo\r\nHost: bar\r\n\r\n\r\n\r\n",
523 err: "too many Host headers",
524 },
525 }
526
527 func TestReadRequestErrors(t *testing.T) {
528 for i, tt := range readRequestErrorTests {
529 req, err := ReadRequest(bufio.NewReader(strings.NewReader(tt.in)))
530 if err == nil {
531 if tt.err != "" {
532 t.Errorf("#%d: got nil err; want %q", i, tt.err)
533 }
534
535 if !reflect.DeepEqual(tt.header, req.Header) {
536 t.Errorf("#%d: gotHeader: %q wantHeader: %q", i, req.Header, tt.header)
537 }
538 continue
539 }
540
541 if tt.err == "" || !strings.Contains(err.Error(), tt.err) {
542 t.Errorf("%d: got error = %v; want %v", i, err, tt.err)
543 }
544 }
545 }
546
547 var newRequestHostTests = []struct {
548 in, out string
549 }{
550 {"http://www.example.com/", "www.example.com"},
551 {"http://www.example.com:8080/", "www.example.com:8080"},
552
553 {"http://192.168.0.1/", "192.168.0.1"},
554 {"http://192.168.0.1:8080/", "192.168.0.1:8080"},
555 {"http://192.168.0.1:/", "192.168.0.1"},
556
557 {"http://[fe80::1]/", "[fe80::1]"},
558 {"http://[fe80::1]:8080/", "[fe80::1]:8080"},
559 {"http://[fe80::1%25en0]/", "[fe80::1%en0]"},
560 {"http://[fe80::1%25en0]:8080/", "[fe80::1%en0]:8080"},
561 {"http://[fe80::1%25en0]:/", "[fe80::1%en0]"},
562 }
563
564 func TestNewRequestHost(t *testing.T) {
565 for i, tt := range newRequestHostTests {
566 req, err := NewRequest("GET", tt.in, nil)
567 if err != nil {
568 t.Errorf("#%v: %v", i, err)
569 continue
570 }
571 if req.Host != tt.out {
572 t.Errorf("got %q; want %q", req.Host, tt.out)
573 }
574 }
575 }
576
577 func TestRequestInvalidMethod(t *testing.T) {
578 _, err := NewRequest("bad method", "http://foo.com/", nil)
579 if err == nil {
580 t.Error("expected error from NewRequest with invalid method")
581 }
582 req, err := NewRequest("GET", "http://foo.example/", nil)
583 if err != nil {
584 t.Fatal(err)
585 }
586 req.Method = "bad method"
587 _, err = DefaultClient.Do(req)
588 if err == nil || !strings.Contains(err.Error(), "invalid method") {
589 t.Errorf("Transport error = %v; want invalid method", err)
590 }
591
592 req, err = NewRequest("", "http://foo.com/", nil)
593 if err != nil {
594 t.Errorf("NewRequest(empty method) = %v; want nil", err)
595 } else if req.Method != "GET" {
596 t.Errorf("NewRequest(empty method) has method %q; want GET", req.Method)
597 }
598 }
599
600 func TestNewRequestContentLength(t *testing.T) {
601 readByte := func(r io.Reader) io.Reader {
602 var b [1]byte
603 r.Read(b[:])
604 return r
605 }
606 tests := []struct {
607 r io.Reader
608 want int64
609 }{
610 {bytes.NewReader([]byte("123")), 3},
611 {bytes.NewBuffer([]byte("1234")), 4},
612 {strings.NewReader("12345"), 5},
613 {strings.NewReader(""), 0},
614 {NoBody, 0},
615
616
617
618
619 {struct{ io.Reader }{strings.NewReader("xyz")}, 0},
620 {io.NewSectionReader(strings.NewReader("x"), 0, 6), 0},
621 {readByte(io.NewSectionReader(strings.NewReader("xy"), 0, 6)), 0},
622 }
623 for i, tt := range tests {
624 req, err := NewRequest("POST", "http://localhost/", tt.r)
625 if err != nil {
626 t.Fatal(err)
627 }
628 if req.ContentLength != tt.want {
629 t.Errorf("test[%d]: ContentLength(%T) = %d; want %d", i, tt.r, req.ContentLength, tt.want)
630 }
631 }
632 }
633
634 var parseHTTPVersionTests = []struct {
635 vers string
636 major, minor int
637 ok bool
638 }{
639 {"HTTP/0.0", 0, 0, true},
640 {"HTTP/0.9", 0, 9, true},
641 {"HTTP/1.0", 1, 0, true},
642 {"HTTP/1.1", 1, 1, true},
643
644 {"HTTP", 0, 0, false},
645 {"HTTP/one.one", 0, 0, false},
646 {"HTTP/1.1/", 0, 0, false},
647 {"HTTP/-1,0", 0, 0, false},
648 {"HTTP/0,-1", 0, 0, false},
649 {"HTTP/", 0, 0, false},
650 {"HTTP/1,1", 0, 0, false},
651 {"HTTP/+1.1", 0, 0, false},
652 {"HTTP/1.+1", 0, 0, false},
653 {"HTTP/0000000001.1", 0, 0, false},
654 {"HTTP/1.0000000001", 0, 0, false},
655 {"HTTP/3.14", 0, 0, false},
656 {"HTTP/12.3", 0, 0, false},
657 }
658
659 func TestParseHTTPVersion(t *testing.T) {
660 for _, tt := range parseHTTPVersionTests {
661 major, minor, ok := ParseHTTPVersion(tt.vers)
662 if ok != tt.ok || major != tt.major || minor != tt.minor {
663 type version struct {
664 major, minor int
665 ok bool
666 }
667 t.Errorf("failed to parse %q, expected: %#v, got %#v", tt.vers, version{tt.major, tt.minor, tt.ok}, version{major, minor, ok})
668 }
669 }
670 }
671
672 type getBasicAuthTest struct {
673 username, password string
674 ok bool
675 }
676
677 type basicAuthCredentialsTest struct {
678 username, password string
679 }
680
681 var getBasicAuthTests = []struct {
682 username, password string
683 ok bool
684 }{
685 {"Aladdin", "open sesame", true},
686 {"Aladdin", "open:sesame", true},
687 {"", "", true},
688 }
689
690 func TestGetBasicAuth(t *testing.T) {
691 for _, tt := range getBasicAuthTests {
692 r, _ := NewRequest("GET", "http://example.com/", nil)
693 r.SetBasicAuth(tt.username, tt.password)
694 username, password, ok := r.BasicAuth()
695 if ok != tt.ok || username != tt.username || password != tt.password {
696 t.Errorf("BasicAuth() = %#v, want %#v", getBasicAuthTest{username, password, ok},
697 getBasicAuthTest{tt.username, tt.password, tt.ok})
698 }
699 }
700
701 r, _ := NewRequest("GET", "http://example.com/", nil)
702 username, password, ok := r.BasicAuth()
703 if ok {
704 t.Errorf("expected false from BasicAuth when the request is unauthenticated")
705 }
706 want := basicAuthCredentialsTest{"", ""}
707 if username != want.username || password != want.password {
708 t.Errorf("expected credentials: %#v when the request is unauthenticated, got %#v",
709 want, basicAuthCredentialsTest{username, password})
710 }
711 }
712
713 var parseBasicAuthTests = []struct {
714 header, username, password string
715 ok bool
716 }{
717 {"Basic " + base64.StdEncoding.EncodeToString([]byte("Aladdin:open sesame")), "Aladdin", "open sesame", true},
718
719
720 {"BASIC " + base64.StdEncoding.EncodeToString([]byte("Aladdin:open sesame")), "Aladdin", "open sesame", true},
721 {"basic " + base64.StdEncoding.EncodeToString([]byte("Aladdin:open sesame")), "Aladdin", "open sesame", true},
722
723 {"Basic " + base64.StdEncoding.EncodeToString([]byte("Aladdin:open:sesame")), "Aladdin", "open:sesame", true},
724 {"Basic " + base64.StdEncoding.EncodeToString([]byte(":")), "", "", true},
725 {"Basic" + base64.StdEncoding.EncodeToString([]byte("Aladdin:open sesame")), "", "", false},
726 {base64.StdEncoding.EncodeToString([]byte("Aladdin:open sesame")), "", "", false},
727 {"Basic ", "", "", false},
728 {"Basic Aladdin:open sesame", "", "", false},
729 {`Digest username="Aladdin"`, "", "", false},
730 }
731
732 func TestParseBasicAuth(t *testing.T) {
733 for _, tt := range parseBasicAuthTests {
734 r, _ := NewRequest("GET", "http://example.com/", nil)
735 r.Header.Set("Authorization", tt.header)
736 username, password, ok := r.BasicAuth()
737 if ok != tt.ok || username != tt.username || password != tt.password {
738 t.Errorf("BasicAuth() = %#v, want %#v", getBasicAuthTest{username, password, ok},
739 getBasicAuthTest{tt.username, tt.password, tt.ok})
740 }
741 }
742 }
743
744 type logWrites struct {
745 t *testing.T
746 dst *[]string
747 }
748
749 func (l logWrites) WriteByte(c byte) error {
750 l.t.Fatalf("unexpected WriteByte call")
751 return nil
752 }
753
754 func (l logWrites) Write(p []byte) (n int, err error) {
755 *l.dst = append(*l.dst, string(p))
756 return len(p), nil
757 }
758
759 func TestRequestWriteBufferedWriter(t *testing.T) {
760 got := []string{}
761 req, _ := NewRequest("GET", "http://foo.com/", nil)
762 req.Write(logWrites{t, &got})
763 want := []string{
764 "GET / HTTP/1.1\r\n",
765 "Host: foo.com\r\n",
766 "User-Agent: " + DefaultUserAgent + "\r\n",
767 "\r\n",
768 }
769 if !slices.Equal(got, want) {
770 t.Errorf("Writes = %q\n Want = %q", got, want)
771 }
772 }
773
774 func TestRequestBadHostHeader(t *testing.T) {
775 got := []string{}
776 req, err := NewRequest("GET", "http://foo/after", nil)
777 if err != nil {
778 t.Fatal(err)
779 }
780 req.Host = "foo.com\nnewline"
781 req.URL.Host = "foo.com\nnewline"
782 req.Write(logWrites{t, &got})
783 want := []string{
784 "GET /after HTTP/1.1\r\n",
785 "Host: \r\n",
786 "User-Agent: " + DefaultUserAgent + "\r\n",
787 "\r\n",
788 }
789 if !slices.Equal(got, want) {
790 t.Errorf("Writes = %q\n Want = %q", got, want)
791 }
792 }
793
794 func TestRequestBadUserAgent(t *testing.T) {
795 got := []string{}
796 req, err := NewRequest("GET", "http://foo/after", nil)
797 if err != nil {
798 t.Fatal(err)
799 }
800 req.Header.Set("User-Agent", "evil\r\nX-Evil: evil")
801 req.Write(logWrites{t, &got})
802 want := []string{
803 "GET /after HTTP/1.1\r\n",
804 "Host: foo\r\n",
805 "User-Agent: evil X-Evil: evil\r\n",
806 "\r\n",
807 }
808 if !slices.Equal(got, want) {
809 t.Errorf("Writes = %q\n Want = %q", got, want)
810 }
811 }
812
813 func TestStarRequest(t *testing.T) {
814 req, err := ReadRequest(bufio.NewReader(strings.NewReader("M-SEARCH * HTTP/1.1\r\n\r\n")))
815 if err != nil {
816 return
817 }
818 if req.ContentLength != 0 {
819 t.Errorf("ContentLength = %d; want 0", req.ContentLength)
820 }
821 if req.Body == nil {
822 t.Errorf("Body = nil; want non-nil")
823 }
824
825
826
827
828
829
830
831 clientReq := *req
832 clientReq.Body = nil
833
834 var out strings.Builder
835 if err := clientReq.Write(&out); err != nil {
836 t.Fatal(err)
837 }
838
839 if strings.Contains(out.String(), "chunked") {
840 t.Error("wrote chunked request; want no body")
841 }
842 back, err := ReadRequest(bufio.NewReader(strings.NewReader(out.String())))
843 if err != nil {
844 t.Fatal(err)
845 }
846
847
848 req.Header = nil
849 back.Header = nil
850 if !reflect.DeepEqual(req, back) {
851 t.Errorf("Original request doesn't match Request read back.")
852 t.Logf("Original: %#v", req)
853 t.Logf("Original.URL: %#v", req.URL)
854 t.Logf("Wrote: %s", out.String())
855 t.Logf("Read back (doesn't match Original): %#v", back)
856 }
857 }
858
859 type responseWriterJustWriter struct {
860 io.Writer
861 }
862
863 func (responseWriterJustWriter) Header() Header { panic("should not be called") }
864 func (responseWriterJustWriter) WriteHeader(int) { panic("should not be called") }
865
866
867
868 type delayedEOFReader struct {
869 r io.Reader
870 }
871
872 func (dr delayedEOFReader) Read(p []byte) (n int, err error) {
873 n, err = dr.r.Read(p)
874 if n > 0 && err == io.EOF {
875 err = nil
876 }
877 return
878 }
879
880 func TestIssue10884_MaxBytesEOF(t *testing.T) {
881 dst := io.Discard
882 _, err := io.Copy(dst, MaxBytesReader(
883 responseWriterJustWriter{dst},
884 io.NopCloser(delayedEOFReader{strings.NewReader("12345")}),
885 5))
886 if err != nil {
887 t.Fatal(err)
888 }
889 }
890
891
892
893 func TestMaxBytesReaderStickyError(t *testing.T) {
894 isSticky := func(r io.Reader) error {
895 var log bytes.Buffer
896 buf := make([]byte, 1000)
897 var firstErr error
898 for {
899 n, err := r.Read(buf)
900 fmt.Fprintf(&log, "Read(%d) = %d, %v\n", len(buf), n, err)
901 if err == nil {
902 continue
903 }
904 if firstErr == nil {
905 firstErr = err
906 continue
907 }
908 if !reflect.DeepEqual(err, firstErr) {
909 return fmt.Errorf("non-sticky error. got log:\n%s", log.Bytes())
910 }
911 t.Logf("Got log: %s", log.Bytes())
912 return nil
913 }
914 }
915 tests := [...]struct {
916 readable int
917 limit int64
918 }{
919 0: {99, 100},
920 1: {100, 100},
921 2: {101, 100},
922 }
923 for i, tt := range tests {
924 rc := MaxBytesReader(nil, io.NopCloser(bytes.NewReader(make([]byte, tt.readable))), tt.limit)
925 if err := isSticky(rc); err != nil {
926 t.Errorf("%d. error: %v", i, err)
927 }
928 }
929 }
930
931
932
933 func TestMaxBytesReaderDifferentLimits(t *testing.T) {
934 const testStr = "1234"
935 tests := [...]struct {
936 limit int64
937 lenP int
938 wantN int
939 wantErr bool
940 }{
941 0: {
942 limit: -123,
943 lenP: 0,
944 wantN: 0,
945 wantErr: false,
946 },
947 1: {
948 limit: -100,
949 lenP: 32 * 1024,
950 wantN: 0,
951 wantErr: true,
952 },
953 2: {
954 limit: -2,
955 lenP: 1,
956 wantN: 0,
957 wantErr: true,
958 },
959 3: {
960 limit: -1,
961 lenP: 2,
962 wantN: 0,
963 wantErr: true,
964 },
965 4: {
966 limit: 0,
967 lenP: 3,
968 wantN: 0,
969 wantErr: true,
970 },
971 5: {
972 limit: 1,
973 lenP: 4,
974 wantN: 1,
975 wantErr: true,
976 },
977 6: {
978 limit: 2,
979 lenP: 5,
980 wantN: 2,
981 wantErr: true,
982 },
983 7: {
984 limit: 3,
985 lenP: 2,
986 wantN: 2,
987 wantErr: false,
988 },
989 8: {
990 limit: int64(len(testStr)),
991 lenP: len(testStr),
992 wantN: len(testStr),
993 wantErr: false,
994 },
995 9: {
996 limit: 100,
997 lenP: 6,
998 wantN: len(testStr),
999 wantErr: false,
1000 },
1001 10: {
1002 limit: int64(1<<63 - 1),
1003 lenP: len(testStr),
1004 wantN: len(testStr),
1005 wantErr: false,
1006 },
1007 }
1008 for i, tt := range tests {
1009 rc := MaxBytesReader(nil, io.NopCloser(strings.NewReader(testStr)), tt.limit)
1010
1011 n, err := rc.Read(make([]byte, tt.lenP))
1012
1013 if n != tt.wantN {
1014 t.Errorf("%d. n: %d, want n: %d", i, n, tt.wantN)
1015 }
1016
1017 if (err != nil) != tt.wantErr {
1018 t.Errorf("%d. error: %v", i, err)
1019 }
1020 }
1021 }
1022
1023 func TestWithContextNilURL(t *testing.T) {
1024 req, err := NewRequest("POST", "https://golang.org/", nil)
1025 if err != nil {
1026 t.Fatal(err)
1027 }
1028
1029
1030 req.URL = nil
1031 reqCopy := req.WithContext(context.Background())
1032 if reqCopy.URL != nil {
1033 t.Error("expected nil URL in cloned request")
1034 }
1035 }
1036
1037
1038
1039 func TestRequestCloneTransferEncoding(t *testing.T) {
1040 body := strings.NewReader("body")
1041 req, _ := NewRequest("POST", "https://example.org/", body)
1042 req.TransferEncoding = []string{
1043 "encoding1",
1044 }
1045
1046 clonedReq := req.Clone(context.Background())
1047
1048 req.TransferEncoding[0] = "encoding2"
1049
1050 if req.TransferEncoding[0] != "encoding2" {
1051 t.Error("expected req.TransferEncoding to be changed")
1052 }
1053 if clonedReq.TransferEncoding[0] != "encoding1" {
1054 t.Error("expected clonedReq.TransferEncoding to be unchanged")
1055 }
1056 }
1057
1058
1059
1060 func TestRequestClonePathValue(t *testing.T) {
1061 req, _ := http.NewRequest("GET", "https://example.org/", nil)
1062 req.SetPathValue("p1", "orig")
1063
1064 clonedReq := req.Clone(context.Background())
1065 clonedReq.SetPathValue("p2", "copy")
1066
1067
1068
1069 if g, w := req.PathValue("p2"), ""; g != w {
1070 t.Fatalf("p2 mismatch got %q, want %q", g, w)
1071 }
1072 if g, w := req.PathValue("p1"), "orig"; g != w {
1073 t.Fatalf("p1 mismatch got %q, want %q", g, w)
1074 }
1075
1076
1077 if g, w := clonedReq.PathValue("p1"), "orig"; g != w {
1078 t.Fatalf("p1 mismatch got %q, want %q", g, w)
1079 }
1080 if g, w := clonedReq.PathValue("p2"), "copy"; g != w {
1081 t.Fatalf("p2 mismatch got %q, want %q", g, w)
1082 }
1083 }
1084
1085
1086 func TestNoPanicOnRoundTripWithBasicAuth(t *testing.T) { run(t, testNoPanicWithBasicAuth) }
1087 func testNoPanicWithBasicAuth(t *testing.T, mode testMode) {
1088 cst := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {}))
1089
1090 u, err := url.Parse(cst.ts.URL)
1091 if err != nil {
1092 t.Fatal(err)
1093 }
1094 u.User = url.UserPassword("foo", "bar")
1095 req := &Request{
1096 URL: u,
1097 Method: "GET",
1098 }
1099 if _, err := cst.c.Do(req); err != nil {
1100 t.Fatalf("Unexpected error: %v", err)
1101 }
1102 }
1103
1104
1105 func TestNewRequestGetBody(t *testing.T) {
1106 tests := []struct {
1107 r io.Reader
1108 }{
1109 {r: strings.NewReader("hello")},
1110 {r: bytes.NewReader([]byte("hello"))},
1111 {r: bytes.NewBuffer([]byte("hello"))},
1112 }
1113 for i, tt := range tests {
1114 req, err := NewRequest("POST", "http://foo.tld/", tt.r)
1115 if err != nil {
1116 t.Errorf("test[%d]: %v", i, err)
1117 continue
1118 }
1119 if req.Body == nil {
1120 t.Errorf("test[%d]: Body = nil", i)
1121 continue
1122 }
1123 if req.GetBody == nil {
1124 t.Errorf("test[%d]: GetBody = nil", i)
1125 continue
1126 }
1127 slurp1, err := io.ReadAll(req.Body)
1128 if err != nil {
1129 t.Errorf("test[%d]: ReadAll(Body) = %v", i, err)
1130 }
1131 newBody, err := req.GetBody()
1132 if err != nil {
1133 t.Errorf("test[%d]: GetBody = %v", i, err)
1134 }
1135 slurp2, err := io.ReadAll(newBody)
1136 if err != nil {
1137 t.Errorf("test[%d]: ReadAll(GetBody()) = %v", i, err)
1138 }
1139 if string(slurp1) != string(slurp2) {
1140 t.Errorf("test[%d]: Body %q != GetBody %q", i, slurp1, slurp2)
1141 }
1142 }
1143 }
1144
1145 func testMissingFile(t *testing.T, req *Request) {
1146 f, fh, err := req.FormFile("missing")
1147 if f != nil {
1148 t.Errorf("FormFile file = %v, want nil", f)
1149 }
1150 if fh != nil {
1151 t.Errorf("FormFile file header = %v, want nil", fh)
1152 }
1153 if err != ErrMissingFile {
1154 t.Errorf("FormFile err = %q, want ErrMissingFile", err)
1155 }
1156 }
1157
1158 func newTestMultipartRequest(t *testing.T) *Request {
1159 b := strings.NewReader(strings.ReplaceAll(message, "\n", "\r\n"))
1160 req, err := NewRequest("POST", "/", b)
1161 if err != nil {
1162 t.Fatal("NewRequest:", err)
1163 }
1164 ctype := fmt.Sprintf(`multipart/form-data; boundary="%s"`, boundary)
1165 req.Header.Set("Content-type", ctype)
1166 return req
1167 }
1168
1169 func validateTestMultipartContents(t *testing.T, req *Request, allMem bool) {
1170 if g, e := req.FormValue("texta"), textaValue; g != e {
1171 t.Errorf("texta value = %q, want %q", g, e)
1172 }
1173 if g, e := req.FormValue("textb"), textbValue; g != e {
1174 t.Errorf("textb value = %q, want %q", g, e)
1175 }
1176 if g := req.FormValue("missing"); g != "" {
1177 t.Errorf("missing value = %q, want empty string", g)
1178 }
1179
1180 assertMem := func(n string, fd multipart.File) {
1181 if _, ok := fd.(*os.File); ok {
1182 t.Error(n, " is *os.File, should not be")
1183 }
1184 }
1185 fda := testMultipartFile(t, req, "filea", "filea.txt", fileaContents)
1186 defer fda.Close()
1187 assertMem("filea", fda)
1188 fdb := testMultipartFile(t, req, "fileb", "fileb.txt", filebContents)
1189 defer fdb.Close()
1190 if allMem {
1191 assertMem("fileb", fdb)
1192 } else {
1193 if _, ok := fdb.(*os.File); !ok {
1194 t.Errorf("fileb has unexpected underlying type %T", fdb)
1195 }
1196 }
1197
1198 testMissingFile(t, req)
1199 }
1200
1201 func testMultipartFile(t *testing.T, req *Request, key, expectFilename, expectContent string) multipart.File {
1202 f, fh, err := req.FormFile(key)
1203 if err != nil {
1204 t.Fatalf("FormFile(%q): %q", key, err)
1205 }
1206 if fh.Filename != expectFilename {
1207 t.Errorf("filename = %q, want %q", fh.Filename, expectFilename)
1208 }
1209 var b strings.Builder
1210 _, err = io.Copy(&b, f)
1211 if err != nil {
1212 t.Fatal("copying contents:", err)
1213 }
1214 if g := b.String(); g != expectContent {
1215 t.Errorf("contents = %q, want %q", g, expectContent)
1216 }
1217 return f
1218 }
1219
1220
1221
1222 func TestRequestCookie(t *testing.T) {
1223 for _, tt := range []struct {
1224 name string
1225 value string
1226 expectedErr error
1227 }{
1228 {
1229 name: "foo",
1230 value: "bar",
1231 expectedErr: nil,
1232 },
1233 {
1234 name: "",
1235 expectedErr: ErrNoCookie,
1236 },
1237 } {
1238 req, err := NewRequest("GET", "http://example.com/", nil)
1239 if err != nil {
1240 t.Fatal(err)
1241 }
1242 req.AddCookie(&Cookie{Name: tt.name, Value: tt.value})
1243 c, err := req.Cookie(tt.name)
1244 if err != tt.expectedErr {
1245 t.Errorf("got %v, want %v", err, tt.expectedErr)
1246 }
1247
1248
1249 if err != nil {
1250 continue
1251 }
1252 if c.Value != tt.value {
1253 t.Errorf("got %v, want %v", c.Value, tt.value)
1254 }
1255 if c.Name != tt.name {
1256 t.Errorf("got %s, want %v", tt.name, c.Name)
1257 }
1258 }
1259 }
1260
1261 func TestRequestCookiesByName(t *testing.T) {
1262 tests := []struct {
1263 in []*Cookie
1264 filter string
1265 want []*Cookie
1266 }{
1267 {
1268 in: []*Cookie{
1269 {Name: "foo", Value: "foo-1"},
1270 {Name: "bar", Value: "bar"},
1271 },
1272 filter: "foo",
1273 want: []*Cookie{{Name: "foo", Value: "foo-1"}},
1274 },
1275 {
1276 in: []*Cookie{
1277 {Name: "foo", Value: "foo-1"},
1278 {Name: "foo", Value: "foo-2"},
1279 {Name: "bar", Value: "bar"},
1280 },
1281 filter: "foo",
1282 want: []*Cookie{
1283 {Name: "foo", Value: "foo-1"},
1284 {Name: "foo", Value: "foo-2"},
1285 },
1286 },
1287 {
1288 in: []*Cookie{
1289 {Name: "bar", Value: "bar"},
1290 },
1291 filter: "foo",
1292 want: []*Cookie{},
1293 },
1294 {
1295 in: []*Cookie{
1296 {Name: "bar", Value: "bar"},
1297 },
1298 filter: "",
1299 want: []*Cookie{},
1300 },
1301 {
1302 in: []*Cookie{},
1303 filter: "foo",
1304 want: []*Cookie{},
1305 },
1306 }
1307
1308 for _, tt := range tests {
1309 t.Run(tt.filter, func(t *testing.T) {
1310 req, err := NewRequest("GET", "http://example.com/", nil)
1311 if err != nil {
1312 t.Fatal(err)
1313 }
1314 for _, c := range tt.in {
1315 req.AddCookie(c)
1316 }
1317
1318 got := req.CookiesNamed(tt.filter)
1319
1320 if !reflect.DeepEqual(got, tt.want) {
1321 asStr := func(v any) string {
1322 blob, _ := json.MarshalIndent(v, "", " ")
1323 return string(blob)
1324 }
1325 t.Fatalf("Result mismatch\n\tGot: %s\n\tWant: %s", asStr(got), asStr(tt.want))
1326 }
1327 })
1328 }
1329 }
1330
1331 const (
1332 fileaContents = "This is a test file."
1333 filebContents = "Another test file."
1334 textaValue = "foo"
1335 textbValue = "bar"
1336 boundary = `MyBoundary`
1337 )
1338
1339 const message = `
1340 --MyBoundary
1341 Content-Disposition: form-data; name="filea"; filename="filea.txt"
1342 Content-Type: text/plain
1343
1344 ` + fileaContents + `
1345 --MyBoundary
1346 Content-Disposition: form-data; name="fileb"; filename="fileb.txt"
1347 Content-Type: text/plain
1348
1349 ` + filebContents + `
1350 --MyBoundary
1351 Content-Disposition: form-data; name="texta"
1352
1353 ` + textaValue + `
1354 --MyBoundary
1355 Content-Disposition: form-data; name="textb"
1356
1357 ` + textbValue + `
1358 --MyBoundary--
1359 `
1360
1361 func benchmarkReadRequest(b *testing.B, request string) {
1362 request = request + "\n"
1363 request = strings.ReplaceAll(request, "\n", "\r\n")
1364 b.SetBytes(int64(len(request)))
1365 r := bufio.NewReader(&infiniteReader{buf: []byte(request)})
1366 b.ReportAllocs()
1367 b.ResetTimer()
1368 for i := 0; i < b.N; i++ {
1369 _, err := ReadRequest(r)
1370 if err != nil {
1371 b.Fatalf("failed to read request: %v", err)
1372 }
1373 }
1374 }
1375
1376
1377
1378 type infiniteReader struct {
1379 buf []byte
1380 offset int
1381 }
1382
1383 func (r *infiniteReader) Read(b []byte) (int, error) {
1384 n := copy(b, r.buf[r.offset:])
1385 r.offset = (r.offset + n) % len(r.buf)
1386 return n, nil
1387 }
1388
1389 func BenchmarkReadRequestChrome(b *testing.B) {
1390
1391 benchmarkReadRequest(b, `GET / HTTP/1.1
1392 Host: localhost:8080
1393 Connection: keep-alive
1394 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
1395 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
1396 Accept-Encoding: gzip,deflate,sdch
1397 Accept-Language: en-US,en;q=0.8
1398 Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
1399 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
1400 `)
1401 }
1402
1403 func BenchmarkReadRequestCurl(b *testing.B) {
1404
1405 benchmarkReadRequest(b, `GET / HTTP/1.1
1406 User-Agent: curl/7.27.0
1407 Host: localhost:8080
1408 Accept: */*
1409 `)
1410 }
1411
1412 func BenchmarkReadRequestApachebench(b *testing.B) {
1413
1414 benchmarkReadRequest(b, `GET / HTTP/1.0
1415 Host: localhost:8080
1416 User-Agent: ApacheBench/2.3
1417 Accept: */*
1418 `)
1419 }
1420
1421 func BenchmarkReadRequestSiege(b *testing.B) {
1422
1423 benchmarkReadRequest(b, `GET / HTTP/1.1
1424 Host: localhost:8080
1425 Accept: */*
1426 Accept-Encoding: gzip
1427 User-Agent: JoeDog/1.00 [en] (X11; I; Siege 2.70)
1428 Connection: keep-alive
1429 `)
1430 }
1431
1432 func BenchmarkReadRequestWrk(b *testing.B) {
1433
1434 benchmarkReadRequest(b, `GET / HTTP/1.1
1435 Host: localhost:8080
1436 `)
1437 }
1438
1439 func BenchmarkFileAndServer_1KB(b *testing.B) {
1440 benchmarkFileAndServer(b, 1<<10)
1441 }
1442
1443 func BenchmarkFileAndServer_16MB(b *testing.B) {
1444 benchmarkFileAndServer(b, 1<<24)
1445 }
1446
1447 func BenchmarkFileAndServer_64MB(b *testing.B) {
1448 benchmarkFileAndServer(b, 1<<26)
1449 }
1450
1451 func benchmarkFileAndServer(b *testing.B, n int64) {
1452 f, err := os.CreateTemp(os.TempDir(), "go-bench-http-file-and-server")
1453 if err != nil {
1454 b.Fatalf("Failed to create temp file: %v", err)
1455 }
1456
1457 defer func() {
1458 f.Close()
1459 os.RemoveAll(f.Name())
1460 }()
1461
1462 if _, err := io.CopyN(f, rand.Reader, n); err != nil {
1463 b.Fatalf("Failed to copy %d bytes: %v", n, err)
1464 }
1465
1466 run(b, func(b *testing.B, mode testMode) {
1467 runFileAndServerBenchmarks(b, mode, f, n)
1468 }, []testMode{http1Mode, https1Mode, http2Mode})
1469 }
1470
1471 func runFileAndServerBenchmarks(b *testing.B, mode testMode, f *os.File, n int64) {
1472 handler := HandlerFunc(func(rw ResponseWriter, req *Request) {
1473 defer req.Body.Close()
1474 nc, err := io.Copy(io.Discard, req.Body)
1475 if err != nil {
1476 panic(err)
1477 }
1478
1479 if nc != n {
1480 panic(fmt.Errorf("Copied %d Wanted %d bytes", nc, n))
1481 }
1482 })
1483
1484 cst := newClientServerTest(b, mode, handler).ts
1485
1486 b.ResetTimer()
1487 for i := 0; i < b.N; i++ {
1488
1489 b.StopTimer()
1490 if _, err := f.Seek(0, 0); err != nil {
1491 b.Fatalf("Failed to seek back to file: %v", err)
1492 }
1493
1494 b.StartTimer()
1495 req, err := NewRequest("PUT", cst.URL, io.NopCloser(f))
1496 if err != nil {
1497 b.Fatal(err)
1498 }
1499
1500 req.ContentLength = n
1501
1502 req.Header.Set("Content-Type", "application/octet-stream")
1503 res, err := cst.Client().Do(req)
1504 if err != nil {
1505 b.Fatalf("Failed to make request to backend: %v", err)
1506 }
1507
1508 res.Body.Close()
1509 b.SetBytes(n)
1510 }
1511 }
1512
1513 func TestErrNotSupported(t *testing.T) {
1514 if !errors.Is(ErrNotSupported, errors.ErrUnsupported) {
1515 t.Error("errors.Is(ErrNotSupported, errors.ErrUnsupported) failed")
1516 }
1517 }
1518
1519 func TestPathValueNoMatch(t *testing.T) {
1520
1521 var r Request
1522 if g, w := r.PathValue("x"), ""; g != w {
1523 t.Errorf("got %q, want %q", g, w)
1524 }
1525 r.SetPathValue("x", "a")
1526 if g, w := r.PathValue("x"), "a"; g != w {
1527 t.Errorf("got %q, want %q", g, w)
1528 }
1529 }
1530
1531 func TestPathValueAndPattern(t *testing.T) {
1532 for _, test := range []struct {
1533 pattern string
1534 url string
1535 want map[string]string
1536 }{
1537 {
1538 "/{a}/is/{b}/{c...}",
1539 "/now/is/the/time/for/all",
1540 map[string]string{
1541 "a": "now",
1542 "b": "the",
1543 "c": "time/for/all",
1544 "d": "",
1545 },
1546 },
1547 {
1548 "/names/{name}/{other...}",
1549 "/names/%2fjohn/address",
1550 map[string]string{
1551 "name": "/john",
1552 "other": "address",
1553 },
1554 },
1555 {
1556 "/names/{name}/{other...}",
1557 "/names/john%2Fdoe/there/is%2F/more",
1558 map[string]string{
1559 "name": "john/doe",
1560 "other": "there/is//more",
1561 },
1562 },
1563 {
1564 "/names/{name}/{other...}",
1565 "/names/n/*",
1566 map[string]string{
1567 "name": "n",
1568 "other": "*",
1569 },
1570 },
1571 } {
1572 mux := NewServeMux()
1573 mux.HandleFunc(test.pattern, func(w ResponseWriter, r *Request) {
1574 for name, want := range test.want {
1575 got := r.PathValue(name)
1576 if got != want {
1577 t.Errorf("%q, %q: got %q, want %q", test.pattern, name, got, want)
1578 }
1579 }
1580 if r.Pattern != test.pattern {
1581 t.Errorf("pattern: got %s, want %s", r.Pattern, test.pattern)
1582 }
1583 })
1584 server := httptest.NewServer(mux)
1585 defer server.Close()
1586 res, err := Get(server.URL + test.url)
1587 if err != nil {
1588 t.Fatal(err)
1589 }
1590 res.Body.Close()
1591 }
1592 }
1593
1594 func TestSetPathValue(t *testing.T) {
1595 mux := NewServeMux()
1596 mux.HandleFunc("/a/{b}/c/{d...}", func(_ ResponseWriter, r *Request) {
1597 kvs := map[string]string{
1598 "b": "X",
1599 "d": "Y",
1600 "a": "Z",
1601 }
1602 for k, v := range kvs {
1603 r.SetPathValue(k, v)
1604 }
1605 for k, w := range kvs {
1606 if g := r.PathValue(k); g != w {
1607 t.Errorf("got %q, want %q", g, w)
1608 }
1609 }
1610 })
1611 server := httptest.NewServer(mux)
1612 defer server.Close()
1613 res, err := Get(server.URL + "/a/b/c/d/e")
1614 if err != nil {
1615 t.Fatal(err)
1616 }
1617 res.Body.Close()
1618 }
1619
1620 func TestStatus(t *testing.T) {
1621
1622 h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
1623 mux := NewServeMux()
1624 mux.Handle("GET /g", h)
1625 mux.Handle("POST /p", h)
1626 mux.Handle("PATCH /p", h)
1627 mux.Handle("PUT /r", h)
1628 mux.Handle("GET /r/", h)
1629 server := httptest.NewServer(mux)
1630 defer server.Close()
1631
1632 for _, test := range []struct {
1633 method, path string
1634 wantStatus int
1635 wantAllow string
1636 }{
1637 {"GET", "/g", 200, ""},
1638 {"HEAD", "/g", 200, ""},
1639 {"POST", "/g", 405, "GET, HEAD"},
1640 {"GET", "/x", 404, ""},
1641 {"GET", "/p", 405, "PATCH, POST"},
1642 {"GET", "/./p", 405, "PATCH, POST"},
1643 {"GET", "/r/", 200, ""},
1644 {"GET", "/r", 200, ""},
1645 {"HEAD", "/r/", 200, ""},
1646 {"HEAD", "/r", 200, ""},
1647 {"PUT", "/r/", 405, "GET, HEAD"},
1648 {"PUT", "/r", 200, ""},
1649 } {
1650 req, err := http.NewRequest(test.method, server.URL+test.path, nil)
1651 if err != nil {
1652 t.Fatal(err)
1653 }
1654 res, err := http.DefaultClient.Do(req)
1655 if err != nil {
1656 t.Fatal(err)
1657 }
1658 res.Body.Close()
1659 if g, w := res.StatusCode, test.wantStatus; g != w {
1660 t.Errorf("%s %s: got %d, want %d", test.method, test.path, g, w)
1661 }
1662 if g, w := res.Header.Get("Allow"), test.wantAllow; g != w {
1663 t.Errorf("%s %s, Allow: got %q, want %q", test.method, test.path, g, w)
1664 }
1665 }
1666 }
1667
View as plain text