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  }