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