Source file src/net/http/cgi/integration_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 a Go CGI program running under a Go CGI host process.
     6  // Further, the two programs are the same binary, just checking
     7  // their environment to figure out what mode to run in.
     8  
     9  package cgi
    10  
    11  import (
    12  	"bytes"
    13  	"errors"
    14  	"fmt"
    15  	"internal/testenv"
    16  	"io"
    17  	"net/http"
    18  	"net/http/httptest"
    19  	"net/url"
    20  	"os"
    21  	"strings"
    22  	"testing"
    23  )
    24  
    25  // This test is a CGI host (testing host.go) that runs its own binary
    26  // as a child process testing the other half of CGI (child.go).
    27  func TestHostingOurselves(t *testing.T) {
    28  	testenv.MustHaveExec(t)
    29  
    30  	h := &Handler{
    31  		Path: os.Args[0],
    32  		Root: "/test.go",
    33  	}
    34  	expectedMap := map[string]string{
    35  		"test":                  "Hello CGI-in-CGI",
    36  		"param-a":               "b",
    37  		"param-foo":             "bar",
    38  		"env-GATEWAY_INTERFACE": "CGI/1.1",
    39  		"env-HTTP_HOST":         "example.com",
    40  		"env-PATH_INFO":         "",
    41  		"env-QUERY_STRING":      "foo=bar&a=b",
    42  		"env-REMOTE_ADDR":       "1.2.3.4",
    43  		"env-REMOTE_HOST":       "1.2.3.4",
    44  		"env-REMOTE_PORT":       "1234",
    45  		"env-REQUEST_METHOD":    "GET",
    46  		"env-REQUEST_URI":       "/test.go?foo=bar&a=b",
    47  		"env-SCRIPT_FILENAME":   os.Args[0],
    48  		"env-SCRIPT_NAME":       "/test.go",
    49  		"env-SERVER_NAME":       "example.com",
    50  		"env-SERVER_PORT":       "80",
    51  		"env-SERVER_SOFTWARE":   "go",
    52  	}
    53  	replay := runCgiTest(t, h, "GET /test.go?foo=bar&a=b HTTP/1.0\nHost: example.com\n\n", expectedMap)
    54  
    55  	if expected, got := "text/plain; charset=utf-8", replay.Header().Get("Content-Type"); got != expected {
    56  		t.Errorf("got a Content-Type of %q; expected %q", got, expected)
    57  	}
    58  	if expected, got := "X-Test-Value", replay.Header().Get("X-Test-Header"); got != expected {
    59  		t.Errorf("got a X-Test-Header of %q; expected %q", got, expected)
    60  	}
    61  }
    62  
    63  type customWriterRecorder struct {
    64  	w io.Writer
    65  	*httptest.ResponseRecorder
    66  }
    67  
    68  func (r *customWriterRecorder) Write(p []byte) (n int, err error) {
    69  	return r.w.Write(p)
    70  }
    71  
    72  type limitWriter struct {
    73  	w io.Writer
    74  	n int
    75  }
    76  
    77  func (w *limitWriter) Write(p []byte) (n int, err error) {
    78  	if len(p) > w.n {
    79  		p = p[:w.n]
    80  	}
    81  	if len(p) > 0 {
    82  		n, err = w.w.Write(p)
    83  		w.n -= n
    84  	}
    85  	if w.n == 0 {
    86  		err = errors.New("past write limit")
    87  	}
    88  	return
    89  }
    90  
    91  // If there's an error copying the child's output to the parent, test
    92  // that we kill the child.
    93  func TestKillChildAfterCopyError(t *testing.T) {
    94  	testenv.MustHaveExec(t)
    95  
    96  	h := &Handler{
    97  		Path: os.Args[0],
    98  		Root: "/test.go",
    99  	}
   100  	req, _ := http.NewRequest("GET", "http://example.com/test.go?write-forever=1", nil)
   101  	rec := httptest.NewRecorder()
   102  	var out bytes.Buffer
   103  	const writeLen = 50 << 10
   104  	rw := &customWriterRecorder{&limitWriter{&out, writeLen}, rec}
   105  
   106  	h.ServeHTTP(rw, req)
   107  	if out.Len() != writeLen || out.Bytes()[0] != 'a' {
   108  		t.Errorf("unexpected output: %q", out.Bytes())
   109  	}
   110  }
   111  
   112  // Test that a child handler writing only headers works.
   113  // golang.org/issue/7196
   114  func TestChildOnlyHeaders(t *testing.T) {
   115  	testenv.MustHaveExec(t)
   116  
   117  	h := &Handler{
   118  		Path: os.Args[0],
   119  		Root: "/test.go",
   120  	}
   121  	expectedMap := map[string]string{
   122  		"_body": "",
   123  	}
   124  	replay := runCgiTest(t, h, "GET /test.go?no-body=1 HTTP/1.0\nHost: example.com\n\n", expectedMap)
   125  	if expected, got := "X-Test-Value", replay.Header().Get("X-Test-Header"); got != expected {
   126  		t.Errorf("got a X-Test-Header of %q; expected %q", got, expected)
   127  	}
   128  }
   129  
   130  // Test that a child handler does not receive a nil Request Body.
   131  // golang.org/issue/39190
   132  func TestNilRequestBody(t *testing.T) {
   133  	testenv.MustHaveExec(t)
   134  
   135  	h := &Handler{
   136  		Path: os.Args[0],
   137  		Root: "/test.go",
   138  	}
   139  	expectedMap := map[string]string{
   140  		"nil-request-body": "false",
   141  	}
   142  	_ = runCgiTest(t, h, "POST /test.go?nil-request-body=1 HTTP/1.0\nHost: example.com\n\n", expectedMap)
   143  	_ = runCgiTest(t, h, "POST /test.go?nil-request-body=1 HTTP/1.0\nHost: example.com\nContent-Length: 0\n\n", expectedMap)
   144  }
   145  
   146  func TestChildContentType(t *testing.T) {
   147  	testenv.MustHaveExec(t)
   148  
   149  	h := &Handler{
   150  		Path: os.Args[0],
   151  		Root: "/test.go",
   152  	}
   153  	var tests = []struct {
   154  		name   string
   155  		body   string
   156  		wantCT string
   157  	}{
   158  		{
   159  			name:   "no body",
   160  			wantCT: "text/plain; charset=utf-8",
   161  		},
   162  		{
   163  			name:   "html",
   164  			body:   "<html><head><title>test page</title></head><body>This is a body</body></html>",
   165  			wantCT: "text/html; charset=utf-8",
   166  		},
   167  		{
   168  			name:   "text",
   169  			body:   strings.Repeat("gopher", 86),
   170  			wantCT: "text/plain; charset=utf-8",
   171  		},
   172  		{
   173  			name:   "jpg",
   174  			body:   "\xFF\xD8\xFF" + strings.Repeat("B", 1024),
   175  			wantCT: "image/jpeg",
   176  		},
   177  	}
   178  	for _, tt := range tests {
   179  		t.Run(tt.name, func(t *testing.T) {
   180  			expectedMap := map[string]string{"_body": tt.body}
   181  			req := fmt.Sprintf("GET /test.go?exact-body=%s HTTP/1.0\nHost: example.com\n\n", url.QueryEscape(tt.body))
   182  			replay := runCgiTest(t, h, req, expectedMap)
   183  			if got := replay.Header().Get("Content-Type"); got != tt.wantCT {
   184  				t.Errorf("got a Content-Type of %q; expected it to start with %q", got, tt.wantCT)
   185  			}
   186  		})
   187  	}
   188  }
   189  
   190  // golang.org/issue/7198
   191  func Test500WithNoHeaders(t *testing.T)     { want500Test(t, "/immediate-disconnect") }
   192  func Test500WithNoContentType(t *testing.T) { want500Test(t, "/no-content-type") }
   193  func Test500WithEmptyHeaders(t *testing.T)  { want500Test(t, "/empty-headers") }
   194  
   195  func want500Test(t *testing.T, path string) {
   196  	h := &Handler{
   197  		Path: os.Args[0],
   198  		Root: "/test.go",
   199  	}
   200  	expectedMap := map[string]string{
   201  		"_body": "",
   202  	}
   203  	replay := runCgiTest(t, h, "GET "+path+" HTTP/1.0\nHost: example.com\n\n", expectedMap)
   204  	if replay.Code != 500 {
   205  		t.Errorf("Got code %d; want 500", replay.Code)
   206  	}
   207  }
   208  

View as plain text