github.com/zach-klippenstein/go@v0.0.0-20150108044943-fcfbeb3adf58/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 }