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 }