github.com/ader1990/go@v0.0.0-20140630135419-8c24447fa791/src/pkg/net/http/readrequest_test.go (about)

     1  // Copyright 2010 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
     6  
     7  import (
     8  	"bufio"
     9  	"bytes"
    10  	"fmt"
    11  	"io"
    12  	"net/url"
    13  	"reflect"
    14  	"testing"
    15  )
    16  
    17  type reqTest struct {
    18  	Raw     string
    19  	Req     *Request
    20  	Body    string
    21  	Trailer Header
    22  	Error   string
    23  }
    24  
    25  var noError = ""
    26  var noBody = ""
    27  var noTrailer Header = nil
    28  
    29  var reqTests = []reqTest{
    30  	// Baseline test; All Request fields included for template use
    31  	{
    32  		"GET http://www.techcrunch.com/ HTTP/1.1\r\n" +
    33  			"Host: www.techcrunch.com\r\n" +
    34  			"User-Agent: Fake\r\n" +
    35  			"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n" +
    36  			"Accept-Language: en-us,en;q=0.5\r\n" +
    37  			"Accept-Encoding: gzip,deflate\r\n" +
    38  			"Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n" +
    39  			"Keep-Alive: 300\r\n" +
    40  			"Content-Length: 7\r\n" +
    41  			"Proxy-Connection: keep-alive\r\n\r\n" +
    42  			"abcdef\n???",
    43  
    44  		&Request{
    45  			Method: "GET",
    46  			URL: &url.URL{
    47  				Scheme: "http",
    48  				Host:   "www.techcrunch.com",
    49  				Path:   "/",
    50  			},
    51  			Proto:      "HTTP/1.1",
    52  			ProtoMajor: 1,
    53  			ProtoMinor: 1,
    54  			Header: Header{
    55  				"Accept":           {"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"},
    56  				"Accept-Language":  {"en-us,en;q=0.5"},
    57  				"Accept-Encoding":  {"gzip,deflate"},
    58  				"Accept-Charset":   {"ISO-8859-1,utf-8;q=0.7,*;q=0.7"},
    59  				"Keep-Alive":       {"300"},
    60  				"Proxy-Connection": {"keep-alive"},
    61  				"Content-Length":   {"7"},
    62  				"User-Agent":       {"Fake"},
    63  			},
    64  			Close:         false,
    65  			ContentLength: 7,
    66  			Host:          "www.techcrunch.com",
    67  			RequestURI:    "http://www.techcrunch.com/",
    68  		},
    69  
    70  		"abcdef\n",
    71  
    72  		noTrailer,
    73  		noError,
    74  	},
    75  
    76  	// GET request with no body (the normal case)
    77  	{
    78  		"GET / HTTP/1.1\r\n" +
    79  			"Host: foo.com\r\n\r\n",
    80  
    81  		&Request{
    82  			Method: "GET",
    83  			URL: &url.URL{
    84  				Path: "/",
    85  			},
    86  			Proto:         "HTTP/1.1",
    87  			ProtoMajor:    1,
    88  			ProtoMinor:    1,
    89  			Header:        Header{},
    90  			Close:         false,
    91  			ContentLength: 0,
    92  			Host:          "foo.com",
    93  			RequestURI:    "/",
    94  		},
    95  
    96  		noBody,
    97  		noTrailer,
    98  		noError,
    99  	},
   100  
   101  	// Tests that we don't parse a path that looks like a
   102  	// scheme-relative URI as a scheme-relative URI.
   103  	{
   104  		"GET //user@host/is/actually/a/path/ HTTP/1.1\r\n" +
   105  			"Host: test\r\n\r\n",
   106  
   107  		&Request{
   108  			Method: "GET",
   109  			URL: &url.URL{
   110  				Path: "//user@host/is/actually/a/path/",
   111  			},
   112  			Proto:         "HTTP/1.1",
   113  			ProtoMajor:    1,
   114  			ProtoMinor:    1,
   115  			Header:        Header{},
   116  			Close:         false,
   117  			ContentLength: 0,
   118  			Host:          "test",
   119  			RequestURI:    "//user@host/is/actually/a/path/",
   120  		},
   121  
   122  		noBody,
   123  		noTrailer,
   124  		noError,
   125  	},
   126  
   127  	// Tests a bogus abs_path on the Request-Line (RFC 2616 section 5.1.2)
   128  	{
   129  		"GET ../../../../etc/passwd HTTP/1.1\r\n" +
   130  			"Host: test\r\n\r\n",
   131  		nil,
   132  		noBody,
   133  		noTrailer,
   134  		"parse ../../../../etc/passwd: invalid URI for request",
   135  	},
   136  
   137  	// Tests missing URL:
   138  	{
   139  		"GET  HTTP/1.1\r\n" +
   140  			"Host: test\r\n\r\n",
   141  		nil,
   142  		noBody,
   143  		noTrailer,
   144  		"parse : empty url",
   145  	},
   146  
   147  	// Tests chunked body with trailer:
   148  	{
   149  		"POST / HTTP/1.1\r\n" +
   150  			"Host: foo.com\r\n" +
   151  			"Transfer-Encoding: chunked\r\n\r\n" +
   152  			"3\r\nfoo\r\n" +
   153  			"3\r\nbar\r\n" +
   154  			"0\r\n" +
   155  			"Trailer-Key: Trailer-Value\r\n" +
   156  			"\r\n",
   157  		&Request{
   158  			Method: "POST",
   159  			URL: &url.URL{
   160  				Path: "/",
   161  			},
   162  			TransferEncoding: []string{"chunked"},
   163  			Proto:            "HTTP/1.1",
   164  			ProtoMajor:       1,
   165  			ProtoMinor:       1,
   166  			Header:           Header{},
   167  			ContentLength:    -1,
   168  			Host:             "foo.com",
   169  			RequestURI:       "/",
   170  		},
   171  
   172  		"foobar",
   173  		Header{
   174  			"Trailer-Key": {"Trailer-Value"},
   175  		},
   176  		noError,
   177  	},
   178  
   179  	// CONNECT request with domain name:
   180  	{
   181  		"CONNECT www.google.com:443 HTTP/1.1\r\n\r\n",
   182  
   183  		&Request{
   184  			Method: "CONNECT",
   185  			URL: &url.URL{
   186  				Host: "www.google.com:443",
   187  			},
   188  			Proto:         "HTTP/1.1",
   189  			ProtoMajor:    1,
   190  			ProtoMinor:    1,
   191  			Header:        Header{},
   192  			Close:         false,
   193  			ContentLength: 0,
   194  			Host:          "www.google.com:443",
   195  			RequestURI:    "www.google.com:443",
   196  		},
   197  
   198  		noBody,
   199  		noTrailer,
   200  		noError,
   201  	},
   202  
   203  	// CONNECT request with IP address:
   204  	{
   205  		"CONNECT 127.0.0.1:6060 HTTP/1.1\r\n\r\n",
   206  
   207  		&Request{
   208  			Method: "CONNECT",
   209  			URL: &url.URL{
   210  				Host: "127.0.0.1:6060",
   211  			},
   212  			Proto:         "HTTP/1.1",
   213  			ProtoMajor:    1,
   214  			ProtoMinor:    1,
   215  			Header:        Header{},
   216  			Close:         false,
   217  			ContentLength: 0,
   218  			Host:          "127.0.0.1:6060",
   219  			RequestURI:    "127.0.0.1:6060",
   220  		},
   221  
   222  		noBody,
   223  		noTrailer,
   224  		noError,
   225  	},
   226  
   227  	// CONNECT request for RPC:
   228  	{
   229  		"CONNECT /_goRPC_ HTTP/1.1\r\n\r\n",
   230  
   231  		&Request{
   232  			Method: "CONNECT",
   233  			URL: &url.URL{
   234  				Path: "/_goRPC_",
   235  			},
   236  			Proto:         "HTTP/1.1",
   237  			ProtoMajor:    1,
   238  			ProtoMinor:    1,
   239  			Header:        Header{},
   240  			Close:         false,
   241  			ContentLength: 0,
   242  			Host:          "",
   243  			RequestURI:    "/_goRPC_",
   244  		},
   245  
   246  		noBody,
   247  		noTrailer,
   248  		noError,
   249  	},
   250  
   251  	// SSDP Notify request. golang.org/issue/3692
   252  	{
   253  		"NOTIFY * HTTP/1.1\r\nServer: foo\r\n\r\n",
   254  		&Request{
   255  			Method: "NOTIFY",
   256  			URL: &url.URL{
   257  				Path: "*",
   258  			},
   259  			Proto:      "HTTP/1.1",
   260  			ProtoMajor: 1,
   261  			ProtoMinor: 1,
   262  			Header: Header{
   263  				"Server": []string{"foo"},
   264  			},
   265  			Close:         false,
   266  			ContentLength: 0,
   267  			RequestURI:    "*",
   268  		},
   269  
   270  		noBody,
   271  		noTrailer,
   272  		noError,
   273  	},
   274  
   275  	// OPTIONS request. Similar to golang.org/issue/3692
   276  	{
   277  		"OPTIONS * HTTP/1.1\r\nServer: foo\r\n\r\n",
   278  		&Request{
   279  			Method: "OPTIONS",
   280  			URL: &url.URL{
   281  				Path: "*",
   282  			},
   283  			Proto:      "HTTP/1.1",
   284  			ProtoMajor: 1,
   285  			ProtoMinor: 1,
   286  			Header: Header{
   287  				"Server": []string{"foo"},
   288  			},
   289  			Close:         false,
   290  			ContentLength: 0,
   291  			RequestURI:    "*",
   292  		},
   293  
   294  		noBody,
   295  		noTrailer,
   296  		noError,
   297  	},
   298  }
   299  
   300  func TestReadRequest(t *testing.T) {
   301  	for i := range reqTests {
   302  		tt := &reqTests[i]
   303  		var braw bytes.Buffer
   304  		braw.WriteString(tt.Raw)
   305  		req, err := ReadRequest(bufio.NewReader(&braw))
   306  		if err != nil {
   307  			if err.Error() != tt.Error {
   308  				t.Errorf("#%d: error %q, want error %q", i, err.Error(), tt.Error)
   309  			}
   310  			continue
   311  		}
   312  		rbody := req.Body
   313  		req.Body = nil
   314  		diff(t, fmt.Sprintf("#%d Request", i), req, tt.Req)
   315  		var bout bytes.Buffer
   316  		if rbody != nil {
   317  			_, err := io.Copy(&bout, rbody)
   318  			if err != nil {
   319  				t.Fatalf("#%d. copying body: %v", i, err)
   320  			}
   321  			rbody.Close()
   322  		}
   323  		body := bout.String()
   324  		if body != tt.Body {
   325  			t.Errorf("#%d: Body = %q want %q", i, body, tt.Body)
   326  		}
   327  		if !reflect.DeepEqual(tt.Trailer, req.Trailer) {
   328  			t.Errorf("#%d. Trailers differ.\n got: %v\nwant: %v", i, req.Trailer, tt.Trailer)
   329  		}
   330  	}
   331  }