github.com/gocuntian/go@v0.0.0-20160610041250-fee02d270bf8/src/net/http/cgi/host_test.go (about) 1 // Copyright 2011 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 // Tests for package cgi 6 7 package cgi 8 9 import ( 10 "bufio" 11 "bytes" 12 "fmt" 13 "io" 14 "net" 15 "net/http" 16 "net/http/httptest" 17 "os" 18 "os/exec" 19 "path/filepath" 20 "reflect" 21 "runtime" 22 "strconv" 23 "strings" 24 "testing" 25 "time" 26 ) 27 28 func newRequest(httpreq string) *http.Request { 29 buf := bufio.NewReader(strings.NewReader(httpreq)) 30 req, err := http.ReadRequest(buf) 31 if err != nil { 32 panic("cgi: bogus http request in test: " + httpreq) 33 } 34 req.RemoteAddr = "1.2.3.4:1234" 35 return req 36 } 37 38 func runCgiTest(t *testing.T, h *Handler, httpreq string, expectedMap map[string]string) *httptest.ResponseRecorder { 39 rw := httptest.NewRecorder() 40 req := newRequest(httpreq) 41 h.ServeHTTP(rw, req) 42 runResponseChecks(t, rw, expectedMap) 43 return rw 44 } 45 46 func runResponseChecks(t *testing.T, rw *httptest.ResponseRecorder, expectedMap map[string]string) { 47 // Make a map to hold the test map that the CGI returns. 48 m := make(map[string]string) 49 m["_body"] = rw.Body.String() 50 linesRead := 0 51 readlines: 52 for { 53 line, err := rw.Body.ReadString('\n') 54 switch { 55 case err == io.EOF: 56 break readlines 57 case err != nil: 58 t.Fatalf("unexpected error reading from CGI: %v", err) 59 } 60 linesRead++ 61 trimmedLine := strings.TrimRight(line, "\r\n") 62 split := strings.SplitN(trimmedLine, "=", 2) 63 if len(split) != 2 { 64 t.Fatalf("Unexpected %d parts from invalid line number %v: %q; existing map=%v", 65 len(split), linesRead, line, m) 66 } 67 m[split[0]] = split[1] 68 } 69 70 for key, expected := range expectedMap { 71 got := m[key] 72 if key == "cwd" { 73 // For Windows. golang.org/issue/4645. 74 fi1, _ := os.Stat(got) 75 fi2, _ := os.Stat(expected) 76 if os.SameFile(fi1, fi2) { 77 got = expected 78 } 79 } 80 if got != expected { 81 t.Errorf("for key %q got %q; expected %q", key, got, expected) 82 } 83 } 84 } 85 86 var cgiTested, cgiWorks bool 87 88 func check(t *testing.T) { 89 if !cgiTested { 90 cgiTested = true 91 cgiWorks = exec.Command("./testdata/test.cgi").Run() == nil 92 } 93 if !cgiWorks { 94 // No Perl on Windows, needed by test.cgi 95 // TODO: make the child process be Go, not Perl. 96 t.Skip("Skipping test: test.cgi failed.") 97 } 98 } 99 100 func TestCGIBasicGet(t *testing.T) { 101 check(t) 102 h := &Handler{ 103 Path: "testdata/test.cgi", 104 Root: "/test.cgi", 105 } 106 expectedMap := map[string]string{ 107 "test": "Hello CGI", 108 "param-a": "b", 109 "param-foo": "bar", 110 "env-GATEWAY_INTERFACE": "CGI/1.1", 111 "env-HTTP_HOST": "example.com", 112 "env-PATH_INFO": "", 113 "env-QUERY_STRING": "foo=bar&a=b", 114 "env-REMOTE_ADDR": "1.2.3.4", 115 "env-REMOTE_HOST": "1.2.3.4", 116 "env-REMOTE_PORT": "1234", 117 "env-REQUEST_METHOD": "GET", 118 "env-REQUEST_URI": "/test.cgi?foo=bar&a=b", 119 "env-SCRIPT_FILENAME": "testdata/test.cgi", 120 "env-SCRIPT_NAME": "/test.cgi", 121 "env-SERVER_NAME": "example.com", 122 "env-SERVER_PORT": "80", 123 "env-SERVER_SOFTWARE": "go", 124 } 125 replay := runCgiTest(t, h, "GET /test.cgi?foo=bar&a=b HTTP/1.0\nHost: example.com\n\n", expectedMap) 126 127 if expected, got := "text/html", replay.Header().Get("Content-Type"); got != expected { 128 t.Errorf("got a Content-Type of %q; expected %q", got, expected) 129 } 130 if expected, got := "X-Test-Value", replay.Header().Get("X-Test-Header"); got != expected { 131 t.Errorf("got a X-Test-Header of %q; expected %q", got, expected) 132 } 133 } 134 135 func TestCGIEnvIPv6(t *testing.T) { 136 check(t) 137 h := &Handler{ 138 Path: "testdata/test.cgi", 139 Root: "/test.cgi", 140 } 141 expectedMap := map[string]string{ 142 "test": "Hello CGI", 143 "param-a": "b", 144 "param-foo": "bar", 145 "env-GATEWAY_INTERFACE": "CGI/1.1", 146 "env-HTTP_HOST": "example.com", 147 "env-PATH_INFO": "", 148 "env-QUERY_STRING": "foo=bar&a=b", 149 "env-REMOTE_ADDR": "2000::3000", 150 "env-REMOTE_HOST": "2000::3000", 151 "env-REMOTE_PORT": "12345", 152 "env-REQUEST_METHOD": "GET", 153 "env-REQUEST_URI": "/test.cgi?foo=bar&a=b", 154 "env-SCRIPT_FILENAME": "testdata/test.cgi", 155 "env-SCRIPT_NAME": "/test.cgi", 156 "env-SERVER_NAME": "example.com", 157 "env-SERVER_PORT": "80", 158 "env-SERVER_SOFTWARE": "go", 159 } 160 161 rw := httptest.NewRecorder() 162 req := newRequest("GET /test.cgi?foo=bar&a=b HTTP/1.0\nHost: example.com\n\n") 163 req.RemoteAddr = "[2000::3000]:12345" 164 h.ServeHTTP(rw, req) 165 runResponseChecks(t, rw, expectedMap) 166 } 167 168 func TestCGIBasicGetAbsPath(t *testing.T) { 169 check(t) 170 pwd, err := os.Getwd() 171 if err != nil { 172 t.Fatalf("getwd error: %v", err) 173 } 174 h := &Handler{ 175 Path: pwd + "/testdata/test.cgi", 176 Root: "/test.cgi", 177 } 178 expectedMap := map[string]string{ 179 "env-REQUEST_URI": "/test.cgi?foo=bar&a=b", 180 "env-SCRIPT_FILENAME": pwd + "/testdata/test.cgi", 181 "env-SCRIPT_NAME": "/test.cgi", 182 } 183 runCgiTest(t, h, "GET /test.cgi?foo=bar&a=b HTTP/1.0\nHost: example.com\n\n", expectedMap) 184 } 185 186 func TestPathInfo(t *testing.T) { 187 check(t) 188 h := &Handler{ 189 Path: "testdata/test.cgi", 190 Root: "/test.cgi", 191 } 192 expectedMap := map[string]string{ 193 "param-a": "b", 194 "env-PATH_INFO": "/extrapath", 195 "env-QUERY_STRING": "a=b", 196 "env-REQUEST_URI": "/test.cgi/extrapath?a=b", 197 "env-SCRIPT_FILENAME": "testdata/test.cgi", 198 "env-SCRIPT_NAME": "/test.cgi", 199 } 200 runCgiTest(t, h, "GET /test.cgi/extrapath?a=b HTTP/1.0\nHost: example.com\n\n", expectedMap) 201 } 202 203 func TestPathInfoDirRoot(t *testing.T) { 204 check(t) 205 h := &Handler{ 206 Path: "testdata/test.cgi", 207 Root: "/myscript/", 208 } 209 expectedMap := map[string]string{ 210 "env-PATH_INFO": "bar", 211 "env-QUERY_STRING": "a=b", 212 "env-REQUEST_URI": "/myscript/bar?a=b", 213 "env-SCRIPT_FILENAME": "testdata/test.cgi", 214 "env-SCRIPT_NAME": "/myscript/", 215 } 216 runCgiTest(t, h, "GET /myscript/bar?a=b HTTP/1.0\nHost: example.com\n\n", expectedMap) 217 } 218 219 func TestDupHeaders(t *testing.T) { 220 check(t) 221 h := &Handler{ 222 Path: "testdata/test.cgi", 223 } 224 expectedMap := map[string]string{ 225 "env-REQUEST_URI": "/myscript/bar?a=b", 226 "env-SCRIPT_FILENAME": "testdata/test.cgi", 227 "env-HTTP_COOKIE": "nom=NOM; yum=YUM", 228 "env-HTTP_X_FOO": "val1, val2", 229 } 230 runCgiTest(t, h, "GET /myscript/bar?a=b HTTP/1.0\n"+ 231 "Cookie: nom=NOM\n"+ 232 "Cookie: yum=YUM\n"+ 233 "X-Foo: val1\n"+ 234 "X-Foo: val2\n"+ 235 "Host: example.com\n\n", 236 expectedMap) 237 } 238 239 func TestPathInfoNoRoot(t *testing.T) { 240 check(t) 241 h := &Handler{ 242 Path: "testdata/test.cgi", 243 Root: "", 244 } 245 expectedMap := map[string]string{ 246 "env-PATH_INFO": "/bar", 247 "env-QUERY_STRING": "a=b", 248 "env-REQUEST_URI": "/bar?a=b", 249 "env-SCRIPT_FILENAME": "testdata/test.cgi", 250 "env-SCRIPT_NAME": "/", 251 } 252 runCgiTest(t, h, "GET /bar?a=b HTTP/1.0\nHost: example.com\n\n", expectedMap) 253 } 254 255 func TestCGIBasicPost(t *testing.T) { 256 check(t) 257 postReq := `POST /test.cgi?a=b HTTP/1.0 258 Host: example.com 259 Content-Type: application/x-www-form-urlencoded 260 Content-Length: 15 261 262 postfoo=postbar` 263 h := &Handler{ 264 Path: "testdata/test.cgi", 265 Root: "/test.cgi", 266 } 267 expectedMap := map[string]string{ 268 "test": "Hello CGI", 269 "param-postfoo": "postbar", 270 "env-REQUEST_METHOD": "POST", 271 "env-CONTENT_LENGTH": "15", 272 "env-REQUEST_URI": "/test.cgi?a=b", 273 } 274 runCgiTest(t, h, postReq, expectedMap) 275 } 276 277 func chunk(s string) string { 278 return fmt.Sprintf("%x\r\n%s\r\n", len(s), s) 279 } 280 281 // The CGI spec doesn't allow chunked requests. 282 func TestCGIPostChunked(t *testing.T) { 283 check(t) 284 postReq := `POST /test.cgi?a=b HTTP/1.1 285 Host: example.com 286 Content-Type: application/x-www-form-urlencoded 287 Transfer-Encoding: chunked 288 289 ` + chunk("postfoo") + chunk("=") + chunk("postbar") + chunk("") 290 291 h := &Handler{ 292 Path: "testdata/test.cgi", 293 Root: "/test.cgi", 294 } 295 expectedMap := map[string]string{} 296 resp := runCgiTest(t, h, postReq, expectedMap) 297 if got, expected := resp.Code, http.StatusBadRequest; got != expected { 298 t.Fatalf("Expected %v response code from chunked request body; got %d", 299 expected, got) 300 } 301 } 302 303 func TestRedirect(t *testing.T) { 304 check(t) 305 h := &Handler{ 306 Path: "testdata/test.cgi", 307 Root: "/test.cgi", 308 } 309 rec := runCgiTest(t, h, "GET /test.cgi?loc=http://foo.com/ HTTP/1.0\nHost: example.com\n\n", nil) 310 if e, g := 302, rec.Code; e != g { 311 t.Errorf("expected status code %d; got %d", e, g) 312 } 313 if e, g := "http://foo.com/", rec.Header().Get("Location"); e != g { 314 t.Errorf("expected Location header of %q; got %q", e, g) 315 } 316 } 317 318 func TestInternalRedirect(t *testing.T) { 319 check(t) 320 baseHandler := http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { 321 fmt.Fprintf(rw, "basepath=%s\n", req.URL.Path) 322 fmt.Fprintf(rw, "remoteaddr=%s\n", req.RemoteAddr) 323 }) 324 h := &Handler{ 325 Path: "testdata/test.cgi", 326 Root: "/test.cgi", 327 PathLocationHandler: baseHandler, 328 } 329 expectedMap := map[string]string{ 330 "basepath": "/foo", 331 "remoteaddr": "1.2.3.4:1234", 332 } 333 runCgiTest(t, h, "GET /test.cgi?loc=/foo HTTP/1.0\nHost: example.com\n\n", expectedMap) 334 } 335 336 // TestCopyError tests that we kill the process if there's an error copying 337 // its output. (for example, from the client having gone away) 338 func TestCopyError(t *testing.T) { 339 check(t) 340 if runtime.GOOS == "windows" { 341 t.Skipf("skipping test on %q", runtime.GOOS) 342 } 343 h := &Handler{ 344 Path: "testdata/test.cgi", 345 Root: "/test.cgi", 346 } 347 ts := httptest.NewServer(h) 348 defer ts.Close() 349 350 conn, err := net.Dial("tcp", ts.Listener.Addr().String()) 351 if err != nil { 352 t.Fatal(err) 353 } 354 req, _ := http.NewRequest("GET", "http://example.com/test.cgi?bigresponse=1", nil) 355 err = req.Write(conn) 356 if err != nil { 357 t.Fatalf("Write: %v", err) 358 } 359 360 res, err := http.ReadResponse(bufio.NewReader(conn), req) 361 if err != nil { 362 t.Fatalf("ReadResponse: %v", err) 363 } 364 365 pidstr := res.Header.Get("X-CGI-Pid") 366 if pidstr == "" { 367 t.Fatalf("expected an X-CGI-Pid header in response") 368 } 369 pid, err := strconv.Atoi(pidstr) 370 if err != nil { 371 t.Fatalf("invalid X-CGI-Pid value") 372 } 373 374 var buf [5000]byte 375 n, err := io.ReadFull(res.Body, buf[:]) 376 if err != nil { 377 t.Fatalf("ReadFull: %d bytes, %v", n, err) 378 } 379 380 childRunning := func() bool { 381 return isProcessRunning(t, pid) 382 } 383 384 if !childRunning() { 385 t.Fatalf("pre-conn.Close, expected child to be running") 386 } 387 conn.Close() 388 389 tries := 0 390 for tries < 25 && childRunning() { 391 time.Sleep(50 * time.Millisecond * time.Duration(tries)) 392 tries++ 393 } 394 if childRunning() { 395 t.Fatalf("post-conn.Close, expected child to be gone") 396 } 397 } 398 399 func TestDirUnix(t *testing.T) { 400 check(t) 401 if runtime.GOOS == "windows" { 402 t.Skipf("skipping test on %q", runtime.GOOS) 403 } 404 cwd, _ := os.Getwd() 405 h := &Handler{ 406 Path: "testdata/test.cgi", 407 Root: "/test.cgi", 408 Dir: cwd, 409 } 410 expectedMap := map[string]string{ 411 "cwd": cwd, 412 } 413 runCgiTest(t, h, "GET /test.cgi HTTP/1.0\nHost: example.com\n\n", expectedMap) 414 415 cwd, _ = os.Getwd() 416 cwd = filepath.Join(cwd, "testdata") 417 h = &Handler{ 418 Path: "testdata/test.cgi", 419 Root: "/test.cgi", 420 } 421 expectedMap = map[string]string{ 422 "cwd": cwd, 423 } 424 runCgiTest(t, h, "GET /test.cgi HTTP/1.0\nHost: example.com\n\n", expectedMap) 425 } 426 427 func TestDirWindows(t *testing.T) { 428 if runtime.GOOS != "windows" { 429 t.Skip("Skipping windows specific test.") 430 } 431 432 cgifile, _ := filepath.Abs("testdata/test.cgi") 433 434 var perl string 435 var err error 436 perl, err = exec.LookPath("perl") 437 if err != nil { 438 t.Skip("Skipping test: perl not found.") 439 } 440 perl, _ = filepath.Abs(perl) 441 442 cwd, _ := os.Getwd() 443 h := &Handler{ 444 Path: perl, 445 Root: "/test.cgi", 446 Dir: cwd, 447 Args: []string{cgifile}, 448 Env: []string{"SCRIPT_FILENAME=" + cgifile}, 449 } 450 expectedMap := map[string]string{ 451 "cwd": cwd, 452 } 453 runCgiTest(t, h, "GET /test.cgi HTTP/1.0\nHost: example.com\n\n", expectedMap) 454 455 // If not specify Dir on windows, working directory should be 456 // base directory of perl. 457 cwd, _ = filepath.Split(perl) 458 if cwd != "" && cwd[len(cwd)-1] == filepath.Separator { 459 cwd = cwd[:len(cwd)-1] 460 } 461 h = &Handler{ 462 Path: perl, 463 Root: "/test.cgi", 464 Args: []string{cgifile}, 465 Env: []string{"SCRIPT_FILENAME=" + cgifile}, 466 } 467 expectedMap = map[string]string{ 468 "cwd": cwd, 469 } 470 runCgiTest(t, h, "GET /test.cgi HTTP/1.0\nHost: example.com\n\n", expectedMap) 471 } 472 473 func TestEnvOverride(t *testing.T) { 474 cgifile, _ := filepath.Abs("testdata/test.cgi") 475 476 var perl string 477 var err error 478 perl, err = exec.LookPath("perl") 479 if err != nil { 480 t.Skipf("Skipping test: perl not found.") 481 } 482 perl, _ = filepath.Abs(perl) 483 484 cwd, _ := os.Getwd() 485 h := &Handler{ 486 Path: perl, 487 Root: "/test.cgi", 488 Dir: cwd, 489 Args: []string{cgifile}, 490 Env: []string{ 491 "SCRIPT_FILENAME=" + cgifile, 492 "REQUEST_URI=/foo/bar", 493 "PATH=/wibble"}, 494 } 495 expectedMap := map[string]string{ 496 "cwd": cwd, 497 "env-SCRIPT_FILENAME": cgifile, 498 "env-REQUEST_URI": "/foo/bar", 499 "env-PATH": "/wibble", 500 } 501 runCgiTest(t, h, "GET /test.cgi HTTP/1.0\nHost: example.com\n\n", expectedMap) 502 } 503 504 func TestHandlerStderr(t *testing.T) { 505 check(t) 506 var stderr bytes.Buffer 507 h := &Handler{ 508 Path: "testdata/test.cgi", 509 Root: "/test.cgi", 510 Stderr: &stderr, 511 } 512 513 rw := httptest.NewRecorder() 514 req := newRequest("GET /test.cgi?writestderr=1 HTTP/1.0\nHost: example.com\n\n") 515 h.ServeHTTP(rw, req) 516 if got, want := stderr.String(), "Hello, stderr!\n"; got != want { 517 t.Errorf("Stderr = %q; want %q", got, want) 518 } 519 } 520 521 func TestRemoveLeadingDuplicates(t *testing.T) { 522 tests := []struct { 523 env []string 524 want []string 525 }{ 526 { 527 env: []string{"a=b", "b=c", "a=b2"}, 528 want: []string{"b=c", "a=b2"}, 529 }, 530 { 531 env: []string{"a=b", "b=c", "d", "e=f"}, 532 want: []string{"a=b", "b=c", "d", "e=f"}, 533 }, 534 } 535 for _, tt := range tests { 536 got := removeLeadingDuplicates(tt.env) 537 if !reflect.DeepEqual(got, tt.want) { 538 t.Errorf("removeLeadingDuplicates(%q) = %q; want %q", tt.env, got, tt.want) 539 } 540 } 541 }