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