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.
29 var noTrailer Header
= nil
31 var reqTests
= []reqTest
{
32 // Baseline test; All Request fields included for template use
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" +
50 Host
: "www.techcrunch.com",
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"},
68 Host
: "www.techcrunch.com",
69 RequestURI
: "http://www.techcrunch.com/",
78 // GET request with no body (the normal case)
80 "GET / HTTP/1.1\r\n" +
81 "Host: foo.com\r\n\r\n",
103 // Tests that we don't parse a path that looks like a
104 // scheme-relative URI as a scheme-relative URI.
106 "GET //user@host/is/actually/a/path/ HTTP/1.1\r\n" +
107 "Host: test\r\n\r\n",
112 Path
: "//user@host/is/actually/a/path/",
121 RequestURI
: "//user@host/is/actually/a/path/",
129 // Tests a bogus absolute-path on the Request-Line (RFC 7230 section 5.3.1)
131 "GET ../../../../etc/passwd HTTP/1.1\r\n" +
132 "Host: test\r\n\r\n",
136 "parse ../../../../etc/passwd: invalid URI for request",
139 // Tests missing URL:
142 "Host: test\r\n\r\n",
149 // Tests chunked body with trailer:
151 "POST / HTTP/1.1\r\n" +
152 "Host: foo.com\r\n" +
153 "Transfer-Encoding: chunked\r\n\r\n" +
157 "Trailer-Key: Trailer-Value\r\n" +
164 TransferEncoding
: []string{"chunked"},
176 "Trailer-Key": {"Trailer-Value"},
181 // Tests chunked body and a bogus Content-Length which should be deleted.
183 "POST / HTTP/1.1\r\n" +
184 "Host: foo.com\r\n" +
185 "Transfer-Encoding: chunked\r\n" +
186 "Content-Length: 9999\r\n\r\n" + // to be removed.
196 TransferEncoding
: []string{"chunked"},
211 // CONNECT request with domain name:
213 "CONNECT www.google.com:443 HTTP/1.1\r\n\r\n",
218 Host
: "www.google.com:443",
226 Host
: "www.google.com:443",
227 RequestURI
: "www.google.com:443",
235 // CONNECT request with IP address:
237 "CONNECT 127.0.0.1:6060 HTTP/1.1\r\n\r\n",
242 Host
: "127.0.0.1:6060",
250 Host
: "127.0.0.1:6060",
251 RequestURI
: "127.0.0.1:6060",
259 // CONNECT request for RPC:
261 "CONNECT /_goRPC_ HTTP/1.1\r\n\r\n",
275 RequestURI
: "/_goRPC_",
283 // SSDP Notify request. golang.org/issue/3692
285 "NOTIFY * HTTP/1.1\r\nServer: foo\r\n\r\n",
295 "Server": []string{"foo"},
307 // OPTIONS request. Similar to golang.org/issue/3692
309 "OPTIONS * HTTP/1.1\r\nServer: foo\r\n\r\n",
319 "Server": []string{"foo"},
331 // Connection: close. golang.org/issue/8261
333 "GET / HTTP/1.1\r\nHost: issue8261.com\r\nConnection: close\r\n\r\n",
340 // This wasn't removed from Go 1.0 to
341 // Go 1.3, so locking it in that we
343 "Connection": []string{"close"},
345 Host
: "issue8261.com",
358 // HEAD with Content-Length 0. Make sure this is permitted,
359 // since I think we used to send it.
361 "HEAD / HTTP/1.1\r\nHost: issue8261.com\r\nConnection: close\r\nContent-Length: 0\r\n\r\n",
368 "Connection": []string{"close"},
369 "Content-Length": []string{"0"},
371 Host
: "issue8261.com",
384 // http2 client preface:
386 "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n",
406 func TestReadRequest(t
*testing
.T
) {
407 for i
:= range reqTests
{
409 req
, err
:= ReadRequest(bufio
.NewReader(strings
.NewReader(tt
.Raw
)))
411 if err
.Error() != tt
.Error
{
412 t
.Errorf("#%d: error %q, want error %q", i
, err
.Error(), tt
.Error
)
418 testName
:= fmt
.Sprintf("Test %d (%q)", i
, tt
.Raw
)
419 diff(t
, testName
, req
, tt
.Req
)
420 var bout bytes
.Buffer
422 _
, err
:= io
.Copy(&bout
, rbody
)
424 t
.Fatalf("%s: copying body: %v", testName
, err
)
428 body
:= bout
.String()
430 t
.Errorf("%s: Body = %q want %q", testName
, body
, tt
.Body
)
432 if !reflect
.DeepEqual(tt
.Trailer
, req
.Trailer
) {
433 t
.Errorf("%s: Trailers differ.\n got: %v\nwant: %v", testName
, req
.Trailer
, tt
.Trailer
)
438 // reqBytes treats req as a request (with \n delimiters) and returns it with \r\n delimiters,
439 // ending in \r\n\r\n
440 func reqBytes(req
string) []byte {
441 return []byte(strings
.Replace(strings
.TrimSpace(req
), "\n", "\r\n", -1) + "\r\n\r\n")
444 var badRequestTests
= []struct {
448 {"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")},
449 {"smuggle_two_contentlen", reqBytes(`POST / HTTP/1.1
454 {"smuggle_content_len_head", reqBytes(`HEAD / HTTP/1.1
456 Content-Length: 5`)},
458 // golang.org/issue/22464
459 {"leading_space_in_header", reqBytes(`HEAD / HTTP/1.1
461 Content-Length: 5`)},
462 {"leading_tab_in_header", reqBytes(`HEAD / HTTP/1.1
464 Content-Length: 5`)},
467 func TestReadRequest_Bad(t
*testing
.T
) {
468 for _
, tt
:= range badRequestTests
{
469 got
, err
:= ReadRequest(bufio
.NewReader(bytes
.NewReader(tt
.req
)))
471 all
, err
:= ioutil
.ReadAll(got
.Body
)
472 t
.Errorf("%s: got unexpected request = %#v\n Body = %q, %v", tt
.name
, got
, all
, err
)