github.com/varialus/godfly@v0.0.0-20130904042352-1934f9f095ab/src/pkg/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 "fmt" 11 "io" 12 "io/ioutil" 13 "mime/multipart" 14 . "net/http" 15 "net/http/httptest" 16 "net/url" 17 "os" 18 "reflect" 19 "regexp" 20 "strings" 21 "testing" 22 ) 23 24 func TestQuery(t *testing.T) { 25 req := &Request{Method: "GET"} 26 req.URL, _ = url.Parse("http://www.google.com/search?q=foo&q=bar") 27 if q := req.FormValue("q"); q != "foo" { 28 t.Errorf(`req.FormValue("q") = %q, want "foo"`, q) 29 } 30 } 31 32 func TestPostQuery(t *testing.T) { 33 req, _ := NewRequest("POST", "http://www.google.com/search?q=foo&q=bar&both=x&prio=1&empty=not", 34 strings.NewReader("z=post&both=y&prio=2&empty=")) 35 req.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value") 36 37 if q := req.FormValue("q"); q != "foo" { 38 t.Errorf(`req.FormValue("q") = %q, want "foo"`, q) 39 } 40 if z := req.FormValue("z"); z != "post" { 41 t.Errorf(`req.FormValue("z") = %q, want "post"`, z) 42 } 43 if bq, found := req.PostForm["q"]; found { 44 t.Errorf(`req.PostForm["q"] = %q, want no entry in map`, bq) 45 } 46 if bz := req.PostFormValue("z"); bz != "post" { 47 t.Errorf(`req.PostFormValue("z") = %q, want "post"`, bz) 48 } 49 if qs := req.Form["q"]; !reflect.DeepEqual(qs, []string{"foo", "bar"}) { 50 t.Errorf(`req.Form["q"] = %q, want ["foo", "bar"]`, qs) 51 } 52 if both := req.Form["both"]; !reflect.DeepEqual(both, []string{"y", "x"}) { 53 t.Errorf(`req.Form["both"] = %q, want ["y", "x"]`, both) 54 } 55 if prio := req.FormValue("prio"); prio != "2" { 56 t.Errorf(`req.FormValue("prio") = %q, want "2" (from body)`, prio) 57 } 58 if empty := req.FormValue("empty"); empty != "" { 59 t.Errorf(`req.FormValue("empty") = %q, want "" (from body)`, empty) 60 } 61 } 62 63 type stringMap map[string][]string 64 type parseContentTypeTest struct { 65 shouldError bool 66 contentType stringMap 67 } 68 69 var parseContentTypeTests = []parseContentTypeTest{ 70 {false, stringMap{"Content-Type": {"text/plain"}}}, 71 // Non-existent keys are not placed. The value nil is illegal. 72 {true, stringMap{}}, 73 {true, stringMap{"Content-Type": {"text/plain; boundary="}}}, 74 {false, stringMap{"Content-Type": {"application/unknown"}}}, 75 } 76 77 func TestParseFormUnknownContentType(t *testing.T) { 78 for i, test := range parseContentTypeTests { 79 req := &Request{ 80 Method: "POST", 81 Header: Header(test.contentType), 82 Body: ioutil.NopCloser(bytes.NewBufferString("body")), 83 } 84 err := req.ParseForm() 85 switch { 86 case err == nil && test.shouldError: 87 t.Errorf("test %d should have returned error", i) 88 case err != nil && !test.shouldError: 89 t.Errorf("test %d should not have returned error, got %v", i, err) 90 } 91 } 92 } 93 94 func TestParseFormInitializeOnError(t *testing.T) { 95 nilBody, _ := NewRequest("POST", "http://www.google.com/search?q=foo", nil) 96 tests := []*Request{ 97 nilBody, 98 {Method: "GET", URL: nil}, 99 } 100 for i, req := range tests { 101 err := req.ParseForm() 102 if req.Form == nil { 103 t.Errorf("%d. Form not initialized, error %v", i, err) 104 } 105 if req.PostForm == nil { 106 t.Errorf("%d. PostForm not initialized, error %v", i, err) 107 } 108 } 109 } 110 111 func TestMultipartReader(t *testing.T) { 112 req := &Request{ 113 Method: "POST", 114 Header: Header{"Content-Type": {`multipart/form-data; boundary="foo123"`}}, 115 Body: ioutil.NopCloser(new(bytes.Buffer)), 116 } 117 multipart, err := req.MultipartReader() 118 if multipart == nil { 119 t.Errorf("expected multipart; error: %v", err) 120 } 121 122 req.Header = Header{"Content-Type": {"text/plain"}} 123 multipart, err = req.MultipartReader() 124 if multipart != nil { 125 t.Errorf("unexpected multipart for text/plain") 126 } 127 } 128 129 func TestRedirect(t *testing.T) { 130 ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { 131 switch r.URL.Path { 132 case "/": 133 w.Header().Set("Location", "/foo/") 134 w.WriteHeader(StatusSeeOther) 135 case "/foo/": 136 fmt.Fprintf(w, "foo") 137 default: 138 w.WriteHeader(StatusBadRequest) 139 } 140 })) 141 defer ts.Close() 142 143 var end = regexp.MustCompile("/foo/$") 144 r, err := Get(ts.URL) 145 if err != nil { 146 t.Fatal(err) 147 } 148 r.Body.Close() 149 url := r.Request.URL.String() 150 if r.StatusCode != 200 || !end.MatchString(url) { 151 t.Fatalf("Get got status %d at %q, want 200 matching /foo/$", r.StatusCode, url) 152 } 153 } 154 155 func TestSetBasicAuth(t *testing.T) { 156 r, _ := NewRequest("GET", "http://example.com/", nil) 157 r.SetBasicAuth("Aladdin", "open sesame") 158 if g, e := r.Header.Get("Authorization"), "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="; g != e { 159 t.Errorf("got header %q, want %q", g, e) 160 } 161 } 162 163 func TestMultipartRequest(t *testing.T) { 164 // Test that we can read the values and files of a 165 // multipart request with FormValue and FormFile, 166 // and that ParseMultipartForm can be called multiple times. 167 req := newTestMultipartRequest(t) 168 if err := req.ParseMultipartForm(25); err != nil { 169 t.Fatal("ParseMultipartForm first call:", err) 170 } 171 defer req.MultipartForm.RemoveAll() 172 validateTestMultipartContents(t, req, false) 173 if err := req.ParseMultipartForm(25); err != nil { 174 t.Fatal("ParseMultipartForm second call:", err) 175 } 176 validateTestMultipartContents(t, req, false) 177 } 178 179 func TestMultipartRequestAuto(t *testing.T) { 180 // Test that FormValue and FormFile automatically invoke 181 // ParseMultipartForm and return the right values. 182 req := newTestMultipartRequest(t) 183 defer func() { 184 if req.MultipartForm != nil { 185 req.MultipartForm.RemoveAll() 186 } 187 }() 188 validateTestMultipartContents(t, req, true) 189 } 190 191 func TestEmptyMultipartRequest(t *testing.T) { 192 // Test that FormValue and FormFile automatically invoke 193 // ParseMultipartForm and return the right values. 194 req, err := NewRequest("GET", "/", nil) 195 if err != nil { 196 t.Errorf("NewRequest err = %q", err) 197 } 198 testMissingFile(t, req) 199 } 200 201 func TestRequestMultipartCallOrder(t *testing.T) { 202 req := newTestMultipartRequest(t) 203 _, err := req.MultipartReader() 204 if err != nil { 205 t.Fatalf("MultipartReader: %v", err) 206 } 207 err = req.ParseMultipartForm(1024) 208 if err == nil { 209 t.Errorf("expected an error from ParseMultipartForm after call to MultipartReader") 210 } 211 } 212 213 var readRequestErrorTests = []struct { 214 in string 215 err error 216 }{ 217 {"GET / HTTP/1.1\r\nheader:foo\r\n\r\n", nil}, 218 {"GET / HTTP/1.1\r\nheader:foo\r\n", io.ErrUnexpectedEOF}, 219 {"", io.EOF}, 220 } 221 222 func TestReadRequestErrors(t *testing.T) { 223 for i, tt := range readRequestErrorTests { 224 _, err := ReadRequest(bufio.NewReader(strings.NewReader(tt.in))) 225 if err != tt.err { 226 t.Errorf("%d. got error = %v; want %v", i, err, tt.err) 227 } 228 } 229 } 230 231 func TestNewRequestHost(t *testing.T) { 232 req, err := NewRequest("GET", "http://localhost:1234/", nil) 233 if err != nil { 234 t.Fatal(err) 235 } 236 if req.Host != "localhost:1234" { 237 t.Errorf("Host = %q; want localhost:1234", req.Host) 238 } 239 } 240 241 func TestNewRequestContentLength(t *testing.T) { 242 readByte := func(r io.Reader) io.Reader { 243 var b [1]byte 244 r.Read(b[:]) 245 return r 246 } 247 tests := []struct { 248 r io.Reader 249 want int64 250 }{ 251 {bytes.NewReader([]byte("123")), 3}, 252 {bytes.NewBuffer([]byte("1234")), 4}, 253 {strings.NewReader("12345"), 5}, 254 // Not detected: 255 {struct{ io.Reader }{strings.NewReader("xyz")}, 0}, 256 {io.NewSectionReader(strings.NewReader("x"), 0, 6), 0}, 257 {readByte(io.NewSectionReader(strings.NewReader("xy"), 0, 6)), 0}, 258 } 259 for _, tt := range tests { 260 req, err := NewRequest("POST", "http://localhost/", tt.r) 261 if err != nil { 262 t.Fatal(err) 263 } 264 if req.ContentLength != tt.want { 265 t.Errorf("ContentLength(%T) = %d; want %d", tt.r, req.ContentLength, tt.want) 266 } 267 } 268 } 269 270 var parseHTTPVersionTests = []struct { 271 vers string 272 major, minor int 273 ok bool 274 }{ 275 {"HTTP/0.9", 0, 9, true}, 276 {"HTTP/1.0", 1, 0, true}, 277 {"HTTP/1.1", 1, 1, true}, 278 {"HTTP/3.14", 3, 14, true}, 279 280 {"HTTP", 0, 0, false}, 281 {"HTTP/one.one", 0, 0, false}, 282 {"HTTP/1.1/", 0, 0, false}, 283 {"HTTP/-1,0", 0, 0, false}, 284 {"HTTP/0,-1", 0, 0, false}, 285 {"HTTP/", 0, 0, false}, 286 {"HTTP/1,1", 0, 0, false}, 287 } 288 289 func TestParseHTTPVersion(t *testing.T) { 290 for _, tt := range parseHTTPVersionTests { 291 major, minor, ok := ParseHTTPVersion(tt.vers) 292 if ok != tt.ok || major != tt.major || minor != tt.minor { 293 type version struct { 294 major, minor int 295 ok bool 296 } 297 t.Errorf("failed to parse %q, expected: %#v, got %#v", tt.vers, version{tt.major, tt.minor, tt.ok}, version{major, minor, ok}) 298 } 299 } 300 } 301 302 type logWrites struct { 303 t *testing.T 304 dst *[]string 305 } 306 307 func (l logWrites) WriteByte(c byte) error { 308 l.t.Fatalf("unexpected WriteByte call") 309 return nil 310 } 311 312 func (l logWrites) Write(p []byte) (n int, err error) { 313 *l.dst = append(*l.dst, string(p)) 314 return len(p), nil 315 } 316 317 func TestRequestWriteBufferedWriter(t *testing.T) { 318 got := []string{} 319 req, _ := NewRequest("GET", "http://foo.com/", nil) 320 req.Write(logWrites{t, &got}) 321 want := []string{ 322 "GET / HTTP/1.1\r\n", 323 "Host: foo.com\r\n", 324 "User-Agent: " + DefaultUserAgent + "\r\n", 325 "\r\n", 326 } 327 if !reflect.DeepEqual(got, want) { 328 t.Errorf("Writes = %q\n Want = %q", got, want) 329 } 330 } 331 332 func testMissingFile(t *testing.T, req *Request) { 333 f, fh, err := req.FormFile("missing") 334 if f != nil { 335 t.Errorf("FormFile file = %q, want nil", f) 336 } 337 if fh != nil { 338 t.Errorf("FormFile file header = %q, want nil", fh) 339 } 340 if err != ErrMissingFile { 341 t.Errorf("FormFile err = %q, want ErrMissingFile", err) 342 } 343 } 344 345 func newTestMultipartRequest(t *testing.T) *Request { 346 b := bytes.NewBufferString(strings.Replace(message, "\n", "\r\n", -1)) 347 req, err := NewRequest("POST", "/", b) 348 if err != nil { 349 t.Fatal("NewRequest:", err) 350 } 351 ctype := fmt.Sprintf(`multipart/form-data; boundary="%s"`, boundary) 352 req.Header.Set("Content-type", ctype) 353 return req 354 } 355 356 func validateTestMultipartContents(t *testing.T, req *Request, allMem bool) { 357 if g, e := req.FormValue("texta"), textaValue; g != e { 358 t.Errorf("texta value = %q, want %q", g, e) 359 } 360 if g, e := req.FormValue("textb"), textbValue; g != e { 361 t.Errorf("textb value = %q, want %q", g, e) 362 } 363 if g := req.FormValue("missing"); g != "" { 364 t.Errorf("missing value = %q, want empty string", g) 365 } 366 367 assertMem := func(n string, fd multipart.File) { 368 if _, ok := fd.(*os.File); ok { 369 t.Error(n, " is *os.File, should not be") 370 } 371 } 372 fda := testMultipartFile(t, req, "filea", "filea.txt", fileaContents) 373 defer fda.Close() 374 assertMem("filea", fda) 375 fdb := testMultipartFile(t, req, "fileb", "fileb.txt", filebContents) 376 defer fdb.Close() 377 if allMem { 378 assertMem("fileb", fdb) 379 } else { 380 if _, ok := fdb.(*os.File); !ok { 381 t.Errorf("fileb has unexpected underlying type %T", fdb) 382 } 383 } 384 385 testMissingFile(t, req) 386 } 387 388 func testMultipartFile(t *testing.T, req *Request, key, expectFilename, expectContent string) multipart.File { 389 f, fh, err := req.FormFile(key) 390 if err != nil { 391 t.Fatalf("FormFile(%q): %q", key, err) 392 } 393 if fh.Filename != expectFilename { 394 t.Errorf("filename = %q, want %q", fh.Filename, expectFilename) 395 } 396 var b bytes.Buffer 397 _, err = io.Copy(&b, f) 398 if err != nil { 399 t.Fatal("copying contents:", err) 400 } 401 if g := b.String(); g != expectContent { 402 t.Errorf("contents = %q, want %q", g, expectContent) 403 } 404 return f 405 } 406 407 const ( 408 fileaContents = "This is a test file." 409 filebContents = "Another test file." 410 textaValue = "foo" 411 textbValue = "bar" 412 boundary = `MyBoundary` 413 ) 414 415 const message = ` 416 --MyBoundary 417 Content-Disposition: form-data; name="filea"; filename="filea.txt" 418 Content-Type: text/plain 419 420 ` + fileaContents + ` 421 --MyBoundary 422 Content-Disposition: form-data; name="fileb"; filename="fileb.txt" 423 Content-Type: text/plain 424 425 ` + filebContents + ` 426 --MyBoundary 427 Content-Disposition: form-data; name="texta" 428 429 ` + textaValue + ` 430 --MyBoundary 431 Content-Disposition: form-data; name="textb" 432 433 ` + textbValue + ` 434 --MyBoundary-- 435 ` 436 437 func benchmarkReadRequest(b *testing.B, request string) { 438 request = request + "\n" // final \n 439 request = strings.Replace(request, "\n", "\r\n", -1) // expand \n to \r\n 440 b.SetBytes(int64(len(request))) 441 r := bufio.NewReader(&infiniteReader{buf: []byte(request)}) 442 b.ReportAllocs() 443 b.ResetTimer() 444 for i := 0; i < b.N; i++ { 445 _, err := ReadRequest(r) 446 if err != nil { 447 b.Fatalf("failed to read request: %v", err) 448 } 449 } 450 } 451 452 // infiniteReader satisfies Read requests as if the contents of buf 453 // loop indefinitely. 454 type infiniteReader struct { 455 buf []byte 456 offset int 457 } 458 459 func (r *infiniteReader) Read(b []byte) (int, error) { 460 n := copy(b, r.buf[r.offset:]) 461 r.offset = (r.offset + n) % len(r.buf) 462 return n, nil 463 } 464 465 func BenchmarkReadRequestChrome(b *testing.B) { 466 // https://github.com/felixge/node-http-perf/blob/master/fixtures/get.http 467 benchmarkReadRequest(b, `GET / HTTP/1.1 468 Host: localhost:8080 469 Connection: keep-alive 470 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 471 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 472 Accept-Encoding: gzip,deflate,sdch 473 Accept-Language: en-US,en;q=0.8 474 Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3 475 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 476 `) 477 } 478 479 func BenchmarkReadRequestCurl(b *testing.B) { 480 // curl http://localhost:8080/ 481 benchmarkReadRequest(b, `GET / HTTP/1.1 482 User-Agent: curl/7.27.0 483 Host: localhost:8080 484 Accept: */* 485 `) 486 } 487 488 func BenchmarkReadRequestApachebench(b *testing.B) { 489 // ab -n 1 -c 1 http://localhost:8080/ 490 benchmarkReadRequest(b, `GET / HTTP/1.0 491 Host: localhost:8080 492 User-Agent: ApacheBench/2.3 493 Accept: */* 494 `) 495 } 496 497 func BenchmarkReadRequestSiege(b *testing.B) { 498 // siege -r 1 -c 1 http://localhost:8080/ 499 benchmarkReadRequest(b, `GET / HTTP/1.1 500 Host: localhost:8080 501 Accept: */* 502 Accept-Encoding: gzip 503 User-Agent: JoeDog/1.00 [en] (X11; I; Siege 2.70) 504 Connection: keep-alive 505 `) 506 } 507 508 func BenchmarkReadRequestWrk(b *testing.B) { 509 // wrk -t 1 -r 1 -c 1 http://localhost:8080/ 510 benchmarkReadRequest(b, `GET / HTTP/1.1 511 Host: localhost:8080 512 `) 513 }