OSDN Git Service

Update Go library to r60.
[pf3gnuchains/gcc-fork.git] / libgo / go / http / cgi / host_test.go
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         "exec"
12         "fmt"
13         "http"
14         "http/httptest"
15         "io"
16         "os"
17         "net"
18         "path/filepath"
19         "strconv"
20         "strings"
21         "testing"
22         "time"
23         "runtime"
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         linesRead := 0
44 readlines:
45         for {
46                 line, err := rw.Body.ReadString('\n')
47                 switch {
48                 case err == os.EOF:
49                         break readlines
50                 case err != nil:
51                         t.Fatalf("unexpected error reading from CGI: %v", err)
52                 }
53                 linesRead++
54                 trimmedLine := strings.TrimRight(line, "\r\n")
55                 split := strings.SplitN(trimmedLine, "=", 2)
56                 if len(split) != 2 {
57                         t.Fatalf("Unexpected %d parts from invalid line number %v: %q; existing map=%v",
58                                 len(split), linesRead, line, m)
59                 }
60                 m[split[0]] = split[1]
61         }
62
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)
66                 }
67         }
68         return rw
69 }
70
71 var cgiTested = false
72 var cgiWorks bool
73
74 func skipTest(t *testing.T) bool {
75         if !cgiTested {
76                 cgiTested = true
77                 cgiWorks = exec.Command("./testdata/test.cgi").Run() == nil
78         }
79         if !cgiWorks {
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.")
83                 return true
84         }
85         return false
86 }
87
88 func TestCGIBasicGet(t *testing.T) {
89         if skipTest(t) {
90                 return
91         }
92         h := &Handler{
93                 Path: "testdata/test.cgi",
94                 Root: "/test.cgi",
95         }
96         expectedMap := map[string]string{
97                 "test":                  "Hello CGI",
98                 "param-a":               "b",
99                 "param-foo":             "bar",
100                 "env-GATEWAY_INTERFACE": "CGI/1.1",
101                 "env-HTTP_HOST":         "example.com",
102                 "env-PATH_INFO":         "",
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",
113         }
114         replay := runCgiTest(t, h, "GET /test.cgi?foo=bar&a=b HTTP/1.0\nHost: example.com\n\n", expectedMap)
115
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)
118         }
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)
121         }
122 }
123
124 func TestCGIBasicGetAbsPath(t *testing.T) {
125         if skipTest(t) {
126                 return
127         }
128         pwd, err := os.Getwd()
129         if err != nil {
130                 t.Fatalf("getwd error: %v", err)
131         }
132         h := &Handler{
133                 Path: pwd + "/testdata/test.cgi",
134                 Root: "/test.cgi",
135         }
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",
140         }
141         runCgiTest(t, h, "GET /test.cgi?foo=bar&a=b HTTP/1.0\nHost: example.com\n\n", expectedMap)
142 }
143
144 func TestPathInfo(t *testing.T) {
145         if skipTest(t) {
146                 return
147         }
148         h := &Handler{
149                 Path: "testdata/test.cgi",
150                 Root: "/test.cgi",
151         }
152         expectedMap := map[string]string{
153                 "param-a":             "b",
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",
159         }
160         runCgiTest(t, h, "GET /test.cgi/extrapath?a=b HTTP/1.0\nHost: example.com\n\n", expectedMap)
161 }
162
163 func TestPathInfoDirRoot(t *testing.T) {
164         if skipTest(t) {
165                 return
166         }
167         h := &Handler{
168                 Path: "testdata/test.cgi",
169                 Root: "/myscript/",
170         }
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/",
177         }
178         runCgiTest(t, h, "GET /myscript/bar?a=b HTTP/1.0\nHost: example.com\n\n", expectedMap)
179 }
180
181 func TestDupHeaders(t *testing.T) {
182         if skipTest(t) {
183                 return
184         }
185         h := &Handler{
186                 Path: "testdata/test.cgi",
187         }
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",
193         }
194         runCgiTest(t, h, "GET /myscript/bar?a=b HTTP/1.0\n"+
195                 "Cookie: nom=NOM\n"+
196                 "Cookie: yum=YUM\n"+
197                 "X-Foo: val1\n"+
198                 "X-Foo: val2\n"+
199                 "Host: example.com\n\n",
200                 expectedMap)
201 }
202
203 func TestPathInfoNoRoot(t *testing.T) {
204         if skipTest(t) {
205                 return
206         }
207         h := &Handler{
208                 Path: "testdata/test.cgi",
209                 Root: "",
210         }
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":     "/",
217         }
218         runCgiTest(t, h, "GET /bar?a=b HTTP/1.0\nHost: example.com\n\n", expectedMap)
219 }
220
221 func TestCGIBasicPost(t *testing.T) {
222         if skipTest(t) {
223                 return
224         }
225         postReq := `POST /test.cgi?a=b HTTP/1.0
226 Host: example.com
227 Content-Type: application/x-www-form-urlencoded
228 Content-Length: 15
229
230 postfoo=postbar`
231         h := &Handler{
232                 Path: "testdata/test.cgi",
233                 Root: "/test.cgi",
234         }
235         expectedMap := map[string]string{
236                 "test":               "Hello CGI",
237                 "param-postfoo":      "postbar",
238                 "env-REQUEST_METHOD": "POST",
239                 "env-CONTENT_LENGTH": "15",
240                 "env-REQUEST_URI":    "/test.cgi?a=b",
241         }
242         runCgiTest(t, h, postReq, expectedMap)
243 }
244
245 func chunk(s string) string {
246         return fmt.Sprintf("%x\r\n%s\r\n", len(s), s)
247 }
248
249 // The CGI spec doesn't allow chunked requests.
250 func TestCGIPostChunked(t *testing.T) {
251         if skipTest(t) {
252                 return
253         }
254         postReq := `POST /test.cgi?a=b HTTP/1.1
255 Host: example.com
256 Content-Type: application/x-www-form-urlencoded
257 Transfer-Encoding: chunked
258
259 ` + chunk("postfoo") + chunk("=") + chunk("postbar") + chunk("")
260
261         h := &Handler{
262                 Path: "testdata/test.cgi",
263                 Root: "/test.cgi",
264         }
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",
269                         expected, got)
270         }
271 }
272
273 func TestRedirect(t *testing.T) {
274         if skipTest(t) {
275                 return
276         }
277         h := &Handler{
278                 Path: "testdata/test.cgi",
279                 Root: "/test.cgi",
280         }
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)
284         }
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)
287         }
288 }
289
290 func TestInternalRedirect(t *testing.T) {
291         if skipTest(t) {
292                 return
293         }
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)
297         })
298         h := &Handler{
299                 Path:                "testdata/test.cgi",
300                 Root:                "/test.cgi",
301                 PathLocationHandler: baseHandler,
302         }
303         expectedMap := map[string]string{
304                 "basepath":   "/foo",
305                 "remoteaddr": "1.2.3.4",
306         }
307         runCgiTest(t, h, "GET /test.cgi?loc=/foo HTTP/1.0\nHost: example.com\n\n", expectedMap)
308 }
309
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" {
314                 return
315         }
316         h := &Handler{
317                 Path: "testdata/test.cgi",
318                 Root: "/test.cgi",
319         }
320         ts := httptest.NewServer(h)
321         defer ts.Close()
322
323         conn, err := net.Dial("tcp", ts.Listener.Addr().String())
324         if err != nil {
325                 t.Fatal(err)
326         }
327         req, _ := http.NewRequest("GET", "http://example.com/test.cgi?bigresponse=1", nil)
328         err = req.Write(conn)
329         if err != nil {
330                 t.Fatalf("Write: %v", err)
331         }
332
333         res, err := http.ReadResponse(bufio.NewReader(conn), req)
334         if err != nil {
335                 t.Fatalf("ReadResponse: %v", err)
336         }
337
338         pidstr := res.Header.Get("X-CGI-Pid")
339         if pidstr == "" {
340                 t.Fatalf("expected an X-CGI-Pid header in response")
341         }
342         pid, err := strconv.Atoi(pidstr)
343         if err != nil {
344                 t.Fatalf("invalid X-CGI-Pid value")
345         }
346
347         var buf [5000]byte
348         n, err := io.ReadFull(res.Body, buf[:])
349         if err != nil {
350                 t.Fatalf("ReadFull: %d bytes, %v", n, err)
351         }
352
353         childRunning := func() bool {
354                 p, err := os.FindProcess(pid)
355                 if err != nil {
356                         return false
357                 }
358                 return p.Signal(os.UnixSignal(0)) == nil
359         }
360
361         if !childRunning() {
362                 t.Fatalf("pre-conn.Close, expected child to be running")
363         }
364         conn.Close()
365
366         if tries := 0; childRunning() {
367                 for tries < 15 && childRunning() {
368                         time.Sleep(50e6 * int64(tries))
369                         tries++
370                 }
371                 if childRunning() {
372                         t.Fatalf("post-conn.Close, expected child to be gone")
373                 }
374         }
375 }
376
377 func TestDirUnix(t *testing.T) {
378         if skipTest(t) || runtime.GOOS == "windows" {
379                 return
380         }
381
382         cwd, _ := os.Getwd()
383         h := &Handler{
384                 Path: "testdata/test.cgi",
385                 Root: "/test.cgi",
386                 Dir:  cwd,
387         }
388         expectedMap := map[string]string{
389                 "cwd": cwd,
390         }
391         runCgiTest(t, h, "GET /test.cgi HTTP/1.0\nHost: example.com\n\n", expectedMap)
392
393         cwd, _ = os.Getwd()
394         cwd = filepath.Join(cwd, "testdata")
395         h = &Handler{
396                 Path: "testdata/test.cgi",
397                 Root: "/test.cgi",
398         }
399         expectedMap = map[string]string{
400                 "cwd": cwd,
401         }
402         runCgiTest(t, h, "GET /test.cgi HTTP/1.0\nHost: example.com\n\n", expectedMap)
403 }
404
405 func TestDirWindows(t *testing.T) {
406         if skipTest(t) || runtime.GOOS != "windows" {
407                 return
408         }
409
410         cgifile, _ := filepath.Abs("testdata/test.cgi")
411
412         var perl string
413         var err os.Error
414         perl, err = exec.LookPath("perl")
415         if err != nil {
416                 return
417         }
418         perl, _ = filepath.Abs(perl)
419
420         cwd, _ := os.Getwd()
421         h := &Handler{
422                 Path: perl,
423                 Root: "/test.cgi",
424                 Dir:  cwd,
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         // If not specify Dir on windows, working directory should be
434         // base directory of perl.
435         cwd, _ = filepath.Split(perl)
436         if cwd != "" && cwd[len(cwd)-1] == filepath.Separator {
437                 cwd = cwd[:len(cwd)-1]
438         }
439         h = &Handler{
440                 Path: perl,
441                 Root: "/test.cgi",
442                 Args: []string{cgifile},
443                 Env:  []string{"SCRIPT_FILENAME=" + cgifile},
444         }
445         expectedMap = map[string]string{
446                 "cwd": cwd,
447         }
448         runCgiTest(t, h, "GET /test.cgi HTTP/1.0\nHost: example.com\n\n", expectedMap)
449 }