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.
5 // Tests for package cgi
26 func newRequest(httpreq string) *http.Request {
27 buf := bufio.NewReader(strings.NewReader(httpreq))
28 req, err := http.ReadRequest(buf)
30 panic("cgi: bogus http request in test: " + httpreq)
32 req.RemoteAddr = "1.2.3.4"
36 func runCgiTest(t *testing.T, h *Handler, httpreq string, expectedMap map[string]string) *httptest.ResponseRecorder {
37 rw := httptest.NewRecorder()
38 req := newRequest(httpreq)
41 // Make a map to hold the test map that the CGI returns.
42 m := make(map[string]string)
46 line, err := rw.Body.ReadString('\n')
51 t.Fatalf("unexpected error reading from CGI: %v", err)
54 trimmedLine := strings.TrimRight(line, "\r\n")
55 split := strings.SplitN(trimmedLine, "=", 2)
57 t.Fatalf("Unexpected %d parts from invalid line number %v: %q; existing map=%v",
58 len(split), linesRead, line, m)
60 m[split[0]] = split[1]
63 for key, expected := range expectedMap {
64 if got := m[key]; got != expected {
65 t.Errorf("for key %q got %q; expected %q", key, got, expected)
74 func skipTest(t *testing.T) bool {
77 cgiWorks = exec.Command("./testdata/test.cgi").Run() == nil
80 // No Perl on Windows, needed by test.cgi
81 // TODO: make the child process be Go, not Perl.
82 t.Logf("Skipping test: test.cgi failed.")
88 func TestCGIBasicGet(t *testing.T) {
93 Path: "testdata/test.cgi",
96 expectedMap := map[string]string{
100 "env-GATEWAY_INTERFACE": "CGI/1.1",
101 "env-HTTP_HOST": "example.com",
103 "env-QUERY_STRING": "foo=bar&a=b",
104 "env-REMOTE_ADDR": "1.2.3.4",
105 "env-REMOTE_HOST": "1.2.3.4",
106 "env-REQUEST_METHOD": "GET",
107 "env-REQUEST_URI": "/test.cgi?foo=bar&a=b",
108 "env-SCRIPT_FILENAME": "testdata/test.cgi",
109 "env-SCRIPT_NAME": "/test.cgi",
110 "env-SERVER_NAME": "example.com",
111 "env-SERVER_PORT": "80",
112 "env-SERVER_SOFTWARE": "go",
114 replay := runCgiTest(t, h, "GET /test.cgi?foo=bar&a=b HTTP/1.0\nHost: example.com\n\n", expectedMap)
116 if expected, got := "text/html", replay.Header().Get("Content-Type"); got != expected {
117 t.Errorf("got a Content-Type of %q; expected %q", got, expected)
119 if expected, got := "X-Test-Value", replay.Header().Get("X-Test-Header"); got != expected {
120 t.Errorf("got a X-Test-Header of %q; expected %q", got, expected)
124 func TestCGIBasicGetAbsPath(t *testing.T) {
128 pwd, err := os.Getwd()
130 t.Fatalf("getwd error: %v", err)
133 Path: pwd + "/testdata/test.cgi",
136 expectedMap := map[string]string{
137 "env-REQUEST_URI": "/test.cgi?foo=bar&a=b",
138 "env-SCRIPT_FILENAME": pwd + "/testdata/test.cgi",
139 "env-SCRIPT_NAME": "/test.cgi",
141 runCgiTest(t, h, "GET /test.cgi?foo=bar&a=b HTTP/1.0\nHost: example.com\n\n", expectedMap)
144 func TestPathInfo(t *testing.T) {
149 Path: "testdata/test.cgi",
152 expectedMap := map[string]string{
154 "env-PATH_INFO": "/extrapath",
155 "env-QUERY_STRING": "a=b",
156 "env-REQUEST_URI": "/test.cgi/extrapath?a=b",
157 "env-SCRIPT_FILENAME": "testdata/test.cgi",
158 "env-SCRIPT_NAME": "/test.cgi",
160 runCgiTest(t, h, "GET /test.cgi/extrapath?a=b HTTP/1.0\nHost: example.com\n\n", expectedMap)
163 func TestPathInfoDirRoot(t *testing.T) {
168 Path: "testdata/test.cgi",
171 expectedMap := map[string]string{
172 "env-PATH_INFO": "bar",
173 "env-QUERY_STRING": "a=b",
174 "env-REQUEST_URI": "/myscript/bar?a=b",
175 "env-SCRIPT_FILENAME": "testdata/test.cgi",
176 "env-SCRIPT_NAME": "/myscript/",
178 runCgiTest(t, h, "GET /myscript/bar?a=b HTTP/1.0\nHost: example.com\n\n", expectedMap)
181 func TestDupHeaders(t *testing.T) {
186 Path: "testdata/test.cgi",
188 expectedMap := map[string]string{
189 "env-REQUEST_URI": "/myscript/bar?a=b",
190 "env-SCRIPT_FILENAME": "testdata/test.cgi",
191 "env-HTTP_COOKIE": "nom=NOM; yum=YUM",
192 "env-HTTP_X_FOO": "val1, val2",
194 runCgiTest(t, h, "GET /myscript/bar?a=b HTTP/1.0\n"+
199 "Host: example.com\n\n",
203 func TestPathInfoNoRoot(t *testing.T) {
208 Path: "testdata/test.cgi",
211 expectedMap := map[string]string{
212 "env-PATH_INFO": "/bar",
213 "env-QUERY_STRING": "a=b",
214 "env-REQUEST_URI": "/bar?a=b",
215 "env-SCRIPT_FILENAME": "testdata/test.cgi",
216 "env-SCRIPT_NAME": "/",
218 runCgiTest(t, h, "GET /bar?a=b HTTP/1.0\nHost: example.com\n\n", expectedMap)
221 func TestCGIBasicPost(t *testing.T) {
225 postReq := `POST /test.cgi?a=b HTTP/1.0
227 Content-Type: application/x-www-form-urlencoded
232 Path: "testdata/test.cgi",
235 expectedMap := map[string]string{
237 "param-postfoo": "postbar",
238 "env-REQUEST_METHOD": "POST",
239 "env-CONTENT_LENGTH": "15",
240 "env-REQUEST_URI": "/test.cgi?a=b",
242 runCgiTest(t, h, postReq, expectedMap)
245 func chunk(s string) string {
246 return fmt.Sprintf("%x\r\n%s\r\n", len(s), s)
249 // The CGI spec doesn't allow chunked requests.
250 func TestCGIPostChunked(t *testing.T) {
254 postReq := `POST /test.cgi?a=b HTTP/1.1
256 Content-Type: application/x-www-form-urlencoded
257 Transfer-Encoding: chunked
259 ` + chunk("postfoo") + chunk("=") + chunk("postbar") + chunk("")
262 Path: "testdata/test.cgi",
265 expectedMap := map[string]string{}
266 resp := runCgiTest(t, h, postReq, expectedMap)
267 if got, expected := resp.Code, http.StatusBadRequest; got != expected {
268 t.Fatalf("Expected %v response code from chunked request body; got %d",
273 func TestRedirect(t *testing.T) {
278 Path: "testdata/test.cgi",
281 rec := runCgiTest(t, h, "GET /test.cgi?loc=http://foo.com/ HTTP/1.0\nHost: example.com\n\n", nil)
282 if e, g := 302, rec.Code; e != g {
283 t.Errorf("expected status code %d; got %d", e, g)
285 if e, g := "http://foo.com/", rec.Header().Get("Location"); e != g {
286 t.Errorf("expected Location header of %q; got %q", e, g)
290 func TestInternalRedirect(t *testing.T) {
294 baseHandler := http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
295 fmt.Fprintf(rw, "basepath=%s\n", req.URL.Path)
296 fmt.Fprintf(rw, "remoteaddr=%s\n", req.RemoteAddr)
299 Path: "testdata/test.cgi",
301 PathLocationHandler: baseHandler,
303 expectedMap := map[string]string{
305 "remoteaddr": "1.2.3.4",
307 runCgiTest(t, h, "GET /test.cgi?loc=/foo HTTP/1.0\nHost: example.com\n\n", expectedMap)
310 // TestCopyError tests that we kill the process if there's an error copying
311 // its output. (for example, from the client having gone away)
312 func TestCopyError(t *testing.T) {
313 if skipTest(t) || runtime.GOOS == "windows" {
317 Path: "testdata/test.cgi",
320 ts := httptest.NewServer(h)
323 conn, err := net.Dial("tcp", ts.Listener.Addr().String())
327 req, _ := http.NewRequest("GET", "http://example.com/test.cgi?bigresponse=1", nil)
328 err = req.Write(conn)
330 t.Fatalf("Write: %v", err)
333 res, err := http.ReadResponse(bufio.NewReader(conn), req)
335 t.Fatalf("ReadResponse: %v", err)
338 pidstr := res.Header.Get("X-CGI-Pid")
340 t.Fatalf("expected an X-CGI-Pid header in response")
342 pid, err := strconv.Atoi(pidstr)
344 t.Fatalf("invalid X-CGI-Pid value")
348 n, err := io.ReadFull(res.Body, buf[:])
350 t.Fatalf("ReadFull: %d bytes, %v", n, err)
353 childRunning := func() bool {
354 p, err := os.FindProcess(pid)
358 return p.Signal(os.UnixSignal(0)) == nil
362 t.Fatalf("pre-conn.Close, expected child to be running")
366 if tries := 0; childRunning() {
367 for tries < 15 && childRunning() {
368 time.Sleep(50e6 * int64(tries))
372 t.Fatalf("post-conn.Close, expected child to be gone")
377 /* This test doesn't work in gccgo's testing environment.
379 func TestDirUnix(t *testing.T) {
380 if skipTest(t) || runtime.GOOS == "windows" {
386 Path: "testdata/test.cgi",
390 expectedMap := map[string]string{
393 runCgiTest(t, h, "GET /test.cgi HTTP/1.0\nHost: example.com\n\n", expectedMap)
396 cwd = filepath.Join(cwd, "testdata")
398 Path: "testdata/test.cgi",
401 expectedMap = map[string]string{
404 runCgiTest(t, h, "GET /test.cgi HTTP/1.0\nHost: example.com\n\n", expectedMap)
409 func TestDirWindows(t *testing.T) {
410 if skipTest(t) || runtime.GOOS != "windows" {
414 cgifile, _ := filepath.Abs("testdata/test.cgi")
418 perl, err = exec.LookPath("perl")
422 perl, _ = filepath.Abs(perl)
429 Args: []string{cgifile},
430 Env: []string{"SCRIPT_FILENAME=" + cgifile},
432 expectedMap := map[string]string{
435 runCgiTest(t, h, "GET /test.cgi HTTP/1.0\nHost: example.com\n\n", expectedMap)
437 // If not specify Dir on windows, working directory should be
438 // base directory of perl.
439 cwd, _ = filepath.Split(perl)
440 if cwd != "" && cwd[len(cwd)-1] == filepath.Separator {
441 cwd = cwd[:len(cwd)-1]
446 Args: []string{cgifile},
447 Env: []string{"SCRIPT_FILENAME=" + cgifile},
449 expectedMap = map[string]string{
452 runCgiTest(t, h, "GET /test.cgi HTTP/1.0\nHost: example.com\n\n", expectedMap)
455 func TestEnvOverride(t *testing.T) {
456 cgifile, _ := filepath.Abs("testdata/test.cgi")
460 perl, err = exec.LookPath("perl")
464 perl, _ = filepath.Abs(perl)
471 Args: []string{cgifile},
473 "SCRIPT_FILENAME=" + cgifile,
474 "REQUEST_URI=/foo/bar"},
476 expectedMap := map[string]string{
478 "env-SCRIPT_FILENAME": cgifile,
479 "env-REQUEST_URI": "/foo/bar",
481 runCgiTest(t, h, "GET /test.cgi HTTP/1.0\nHost: example.com\n\n", expectedMap)