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