libgo: update to Go 1.11
[official-gcc.git] / libgo / go / net / http / readrequest_test.go
blob18eed345a842238dcb83400542d255f3048ad9b6
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.
5 package http
7 import (
8 "bufio"
9 "bytes"
10 "fmt"
11 "io"
12 "io/ioutil"
13 "net/url"
14 "reflect"
15 "strings"
16 "testing"
19 type reqTest struct {
20 Raw string
21 Req *Request
22 Body string
23 Trailer Header
24 Error string
27 var noError = ""
28 var noBodyStr = ""
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" +
44 "abcdef\n???",
46 &Request{
47 Method: "GET",
48 URL: &url.URL{
49 Scheme: "http",
50 Host: "www.techcrunch.com",
51 Path: "/",
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"},
66 Close: false,
67 ContentLength: 7,
68 Host: "www.techcrunch.com",
69 RequestURI: "http://www.techcrunch.com/",
72 "abcdef\n",
74 noTrailer,
75 noError,
78 // GET request with no body (the normal case)
80 "GET / HTTP/1.1\r\n" +
81 "Host: foo.com\r\n\r\n",
83 &Request{
84 Method: "GET",
85 URL: &url.URL{
86 Path: "/",
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: "/",
98 noBodyStr,
99 noTrailer,
100 noError,
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",
109 &Request{
110 Method: "GET",
111 URL: &url.URL{
112 Path: "//user@host/is/actually/a/path/",
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/",
124 noBodyStr,
125 noTrailer,
126 noError,
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",
133 nil,
134 noBodyStr,
135 noTrailer,
136 "parse ../../../../etc/passwd: invalid URI for request",
139 // Tests missing URL:
141 "GET HTTP/1.1\r\n" +
142 "Host: test\r\n\r\n",
143 nil,
144 noBodyStr,
145 noTrailer,
146 "parse : empty url",
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" +
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: "/",
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: "/",
174 "foobar",
175 Header{
176 "Trailer-Key": {"Trailer-Value"},
178 noError,
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.
187 "3\r\nfoo\r\n" +
188 "3\r\nbar\r\n" +
189 "0\r\n" +
190 "\r\n",
191 &Request{
192 Method: "POST",
193 URL: &url.URL{
194 Path: "/",
196 TransferEncoding: []string{"chunked"},
197 Proto: "HTTP/1.1",
198 ProtoMajor: 1,
199 ProtoMinor: 1,
200 Header: Header{},
201 ContentLength: -1,
202 Host: "foo.com",
203 RequestURI: "/",
206 "foobar",
207 noTrailer,
208 noError,
211 // CONNECT request with domain name:
213 "CONNECT www.google.com:443 HTTP/1.1\r\n\r\n",
215 &Request{
216 Method: "CONNECT",
217 URL: &url.URL{
218 Host: "www.google.com:443",
220 Proto: "HTTP/1.1",
221 ProtoMajor: 1,
222 ProtoMinor: 1,
223 Header: Header{},
224 Close: false,
225 ContentLength: 0,
226 Host: "www.google.com:443",
227 RequestURI: "www.google.com:443",
230 noBodyStr,
231 noTrailer,
232 noError,
235 // CONNECT request with IP address:
237 "CONNECT 127.0.0.1:6060 HTTP/1.1\r\n\r\n",
239 &Request{
240 Method: "CONNECT",
241 URL: &url.URL{
242 Host: "127.0.0.1:6060",
244 Proto: "HTTP/1.1",
245 ProtoMajor: 1,
246 ProtoMinor: 1,
247 Header: Header{},
248 Close: false,
249 ContentLength: 0,
250 Host: "127.0.0.1:6060",
251 RequestURI: "127.0.0.1:6060",
254 noBodyStr,
255 noTrailer,
256 noError,
259 // CONNECT request for RPC:
261 "CONNECT /_goRPC_ HTTP/1.1\r\n\r\n",
263 &Request{
264 Method: "CONNECT",
265 URL: &url.URL{
266 Path: "/_goRPC_",
268 Proto: "HTTP/1.1",
269 ProtoMajor: 1,
270 ProtoMinor: 1,
271 Header: Header{},
272 Close: false,
273 ContentLength: 0,
274 Host: "",
275 RequestURI: "/_goRPC_",
278 noBodyStr,
279 noTrailer,
280 noError,
283 // SSDP Notify request. golang.org/issue/3692
285 "NOTIFY * HTTP/1.1\r\nServer: foo\r\n\r\n",
286 &Request{
287 Method: "NOTIFY",
288 URL: &url.URL{
289 Path: "*",
291 Proto: "HTTP/1.1",
292 ProtoMajor: 1,
293 ProtoMinor: 1,
294 Header: Header{
295 "Server": []string{"foo"},
297 Close: false,
298 ContentLength: 0,
299 RequestURI: "*",
302 noBodyStr,
303 noTrailer,
304 noError,
307 // OPTIONS request. Similar to golang.org/issue/3692
309 "OPTIONS * HTTP/1.1\r\nServer: foo\r\n\r\n",
310 &Request{
311 Method: "OPTIONS",
312 URL: &url.URL{
313 Path: "*",
315 Proto: "HTTP/1.1",
316 ProtoMajor: 1,
317 ProtoMinor: 1,
318 Header: Header{
319 "Server": []string{"foo"},
321 Close: false,
322 ContentLength: 0,
323 RequestURI: "*",
326 noBodyStr,
327 noTrailer,
328 noError,
331 // Connection: close. golang.org/issue/8261
333 "GET / HTTP/1.1\r\nHost: issue8261.com\r\nConnection: close\r\n\r\n",
334 &Request{
335 Method: "GET",
336 URL: &url.URL{
337 Path: "/",
339 Header: Header{
340 // This wasn't removed from Go 1.0 to
341 // Go 1.3, so locking it in that we
342 // keep this:
343 "Connection": []string{"close"},
345 Host: "issue8261.com",
346 Proto: "HTTP/1.1",
347 ProtoMajor: 1,
348 ProtoMinor: 1,
349 Close: true,
350 RequestURI: "/",
353 noBodyStr,
354 noTrailer,
355 noError,
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",
362 &Request{
363 Method: "HEAD",
364 URL: &url.URL{
365 Path: "/",
367 Header: Header{
368 "Connection": []string{"close"},
369 "Content-Length": []string{"0"},
371 Host: "issue8261.com",
372 Proto: "HTTP/1.1",
373 ProtoMajor: 1,
374 ProtoMinor: 1,
375 Close: true,
376 RequestURI: "/",
379 noBodyStr,
380 noTrailer,
381 noError,
384 // http2 client preface:
386 "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n",
387 &Request{
388 Method: "PRI",
389 URL: &url.URL{
390 Path: "*",
392 Header: Header{},
393 Proto: "HTTP/2.0",
394 ProtoMajor: 2,
395 ProtoMinor: 0,
396 RequestURI: "*",
397 ContentLength: -1,
398 Close: true,
400 noBodyStr,
401 noTrailer,
402 noError,
406 func TestReadRequest(t *testing.T) {
407 for i := range reqTests {
408 tt := &reqTests[i]
409 req, err := ReadRequest(bufio.NewReader(strings.NewReader(tt.Raw)))
410 if err != nil {
411 if err.Error() != tt.Error {
412 t.Errorf("#%d: error %q, want error %q", i, err.Error(), tt.Error)
414 continue
416 rbody := req.Body
417 req.Body = nil
418 testName := fmt.Sprintf("Test %d (%q)", i, tt.Raw)
419 diff(t, testName, req, tt.Req)
420 var bout bytes.Buffer
421 if rbody != nil {
422 _, err := io.Copy(&bout, rbody)
423 if err != nil {
424 t.Fatalf("%s: copying body: %v", testName, err)
426 rbody.Close()
428 body := bout.String()
429 if body != tt.Body {
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 {
445 name string
446 req []byte
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
450 Content-Length: 3
451 Content-Length: 4
453 abc`)},
454 {"smuggle_content_len_head", reqBytes(`HEAD / HTTP/1.1
455 Host: foo
456 Content-Length: 5`)},
458 // golang.org/issue/22464
459 {"leading_space_in_header", reqBytes(`HEAD / HTTP/1.1
460 Host: foo
461 Content-Length: 5`)},
462 {"leading_tab_in_header", reqBytes(`HEAD / HTTP/1.1
463 \tHost: foo
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)))
470 if err == nil {
471 all, err := ioutil.ReadAll(got.Body)
472 t.Errorf("%s: got unexpected request = %#v\n Body = %q, %v", tt.name, got, all, err)