...

Source file src/net/http/cgi/integration_test.go

Documentation: net/http/cgi

		 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  	"time"
		24  )
		25  
		26  // This test is a CGI host (testing host.go) that runs its own binary
		27  // as a child process testing the other half of CGI (child.go).
		28  func TestHostingOurselves(t *testing.T) {
		29  	testenv.MustHaveExec(t)
		30  
		31  	h := &Handler{
		32  		Path: os.Args[0],
		33  		Root: "/test.go",
		34  		Args: []string{"-test.run=TestBeChildCGIProcess"},
		35  	}
		36  	expectedMap := map[string]string{
		37  		"test":									"Hello CGI-in-CGI",
		38  		"param-a":							 "b",
		39  		"param-foo":						 "bar",
		40  		"env-GATEWAY_INTERFACE": "CGI/1.1",
		41  		"env-HTTP_HOST":				 "example.com",
		42  		"env-PATH_INFO":				 "",
		43  		"env-QUERY_STRING":			"foo=bar&a=b",
		44  		"env-REMOTE_ADDR":			 "1.2.3.4",
		45  		"env-REMOTE_HOST":			 "1.2.3.4",
		46  		"env-REMOTE_PORT":			 "1234",
		47  		"env-REQUEST_METHOD":		"GET",
		48  		"env-REQUEST_URI":			 "/test.go?foo=bar&a=b",
		49  		"env-SCRIPT_FILENAME":	 os.Args[0],
		50  		"env-SCRIPT_NAME":			 "/test.go",
		51  		"env-SERVER_NAME":			 "example.com",
		52  		"env-SERVER_PORT":			 "80",
		53  		"env-SERVER_SOFTWARE":	 "go",
		54  	}
		55  	replay := runCgiTest(t, h, "GET /test.go?foo=bar&a=b HTTP/1.0\nHost: example.com\n\n", expectedMap)
		56  
		57  	if expected, got := "text/plain; charset=utf-8", replay.Header().Get("Content-Type"); got != expected {
		58  		t.Errorf("got a Content-Type of %q; expected %q", got, expected)
		59  	}
		60  	if expected, got := "X-Test-Value", replay.Header().Get("X-Test-Header"); got != expected {
		61  		t.Errorf("got a X-Test-Header of %q; expected %q", got, expected)
		62  	}
		63  }
		64  
		65  type customWriterRecorder struct {
		66  	w io.Writer
		67  	*httptest.ResponseRecorder
		68  }
		69  
		70  func (r *customWriterRecorder) Write(p []byte) (n int, err error) {
		71  	return r.w.Write(p)
		72  }
		73  
		74  type limitWriter struct {
		75  	w io.Writer
		76  	n int
		77  }
		78  
		79  func (w *limitWriter) Write(p []byte) (n int, err error) {
		80  	if len(p) > w.n {
		81  		p = p[:w.n]
		82  	}
		83  	if len(p) > 0 {
		84  		n, err = w.w.Write(p)
		85  		w.n -= n
		86  	}
		87  	if w.n == 0 {
		88  		err = errors.New("past write limit")
		89  	}
		90  	return
		91  }
		92  
		93  // If there's an error copying the child's output to the parent, test
		94  // that we kill the child.
		95  func TestKillChildAfterCopyError(t *testing.T) {
		96  	testenv.MustHaveExec(t)
		97  
		98  	h := &Handler{
		99  		Path: os.Args[0],
	 100  		Root: "/test.go",
	 101  		Args: []string{"-test.run=TestBeChildCGIProcess"},
	 102  	}
	 103  	req, _ := http.NewRequest("GET", "http://example.com/test.cgi?write-forever=1", nil)
	 104  	rec := httptest.NewRecorder()
	 105  	var out bytes.Buffer
	 106  	const writeLen = 50 << 10
	 107  	rw := &customWriterRecorder{&limitWriter{&out, writeLen}, rec}
	 108  
	 109  	h.ServeHTTP(rw, req)
	 110  	if out.Len() != writeLen || out.Bytes()[0] != 'a' {
	 111  		t.Errorf("unexpected output: %q", out.Bytes())
	 112  	}
	 113  }
	 114  
	 115  // Test that a child handler writing only headers works.
	 116  // golang.org/issue/7196
	 117  func TestChildOnlyHeaders(t *testing.T) {
	 118  	testenv.MustHaveExec(t)
	 119  
	 120  	h := &Handler{
	 121  		Path: os.Args[0],
	 122  		Root: "/test.go",
	 123  		Args: []string{"-test.run=TestBeChildCGIProcess"},
	 124  	}
	 125  	expectedMap := map[string]string{
	 126  		"_body": "",
	 127  	}
	 128  	replay := runCgiTest(t, h, "GET /test.go?no-body=1 HTTP/1.0\nHost: example.com\n\n", expectedMap)
	 129  	if expected, got := "X-Test-Value", replay.Header().Get("X-Test-Header"); got != expected {
	 130  		t.Errorf("got a X-Test-Header of %q; expected %q", got, expected)
	 131  	}
	 132  }
	 133  
	 134  // Test that a child handler does not receive a nil Request Body.
	 135  // golang.org/issue/39190
	 136  func TestNilRequestBody(t *testing.T) {
	 137  	testenv.MustHaveExec(t)
	 138  
	 139  	h := &Handler{
	 140  		Path: os.Args[0],
	 141  		Root: "/test.go",
	 142  		Args: []string{"-test.run=TestBeChildCGIProcess"},
	 143  	}
	 144  	expectedMap := map[string]string{
	 145  		"nil-request-body": "false",
	 146  	}
	 147  	_ = runCgiTest(t, h, "POST /test.go?nil-request-body=1 HTTP/1.0\nHost: example.com\n\n", expectedMap)
	 148  	_ = runCgiTest(t, h, "POST /test.go?nil-request-body=1 HTTP/1.0\nHost: example.com\nContent-Length: 0\n\n", expectedMap)
	 149  }
	 150  
	 151  func TestChildContentType(t *testing.T) {
	 152  	testenv.MustHaveExec(t)
	 153  
	 154  	h := &Handler{
	 155  		Path: os.Args[0],
	 156  		Root: "/test.go",
	 157  		Args: []string{"-test.run=TestBeChildCGIProcess"},
	 158  	}
	 159  	var tests = []struct {
	 160  		name	 string
	 161  		body	 string
	 162  		wantCT string
	 163  	}{
	 164  		{
	 165  			name:	 "no body",
	 166  			wantCT: "text/plain; charset=utf-8",
	 167  		},
	 168  		{
	 169  			name:	 "html",
	 170  			body:	 "<html><head><title>test page</title></head><body>This is a body</body></html>",
	 171  			wantCT: "text/html; charset=utf-8",
	 172  		},
	 173  		{
	 174  			name:	 "text",
	 175  			body:	 strings.Repeat("gopher", 86),
	 176  			wantCT: "text/plain; charset=utf-8",
	 177  		},
	 178  		{
	 179  			name:	 "jpg",
	 180  			body:	 "\xFF\xD8\xFF" + strings.Repeat("B", 1024),
	 181  			wantCT: "image/jpeg",
	 182  		},
	 183  	}
	 184  	for _, tt := range tests {
	 185  		t.Run(tt.name, func(t *testing.T) {
	 186  			expectedMap := map[string]string{"_body": tt.body}
	 187  			req := fmt.Sprintf("GET /test.go?exact-body=%s HTTP/1.0\nHost: example.com\n\n", url.QueryEscape(tt.body))
	 188  			replay := runCgiTest(t, h, req, expectedMap)
	 189  			if got := replay.Header().Get("Content-Type"); got != tt.wantCT {
	 190  				t.Errorf("got a Content-Type of %q; expected it to start with %q", got, tt.wantCT)
	 191  			}
	 192  		})
	 193  	}
	 194  }
	 195  
	 196  // golang.org/issue/7198
	 197  func Test500WithNoHeaders(t *testing.T)		 { want500Test(t, "/immediate-disconnect") }
	 198  func Test500WithNoContentType(t *testing.T) { want500Test(t, "/no-content-type") }
	 199  func Test500WithEmptyHeaders(t *testing.T)	{ want500Test(t, "/empty-headers") }
	 200  
	 201  func want500Test(t *testing.T, path string) {
	 202  	h := &Handler{
	 203  		Path: os.Args[0],
	 204  		Root: "/test.go",
	 205  		Args: []string{"-test.run=TestBeChildCGIProcess"},
	 206  	}
	 207  	expectedMap := map[string]string{
	 208  		"_body": "",
	 209  	}
	 210  	replay := runCgiTest(t, h, "GET "+path+" HTTP/1.0\nHost: example.com\n\n", expectedMap)
	 211  	if replay.Code != 500 {
	 212  		t.Errorf("Got code %d; want 500", replay.Code)
	 213  	}
	 214  }
	 215  
	 216  type neverEnding byte
	 217  
	 218  func (b neverEnding) Read(p []byte) (n int, err error) {
	 219  	for i := range p {
	 220  		p[i] = byte(b)
	 221  	}
	 222  	return len(p), nil
	 223  }
	 224  
	 225  // Note: not actually a test.
	 226  func TestBeChildCGIProcess(t *testing.T) {
	 227  	if os.Getenv("REQUEST_METHOD") == "" {
	 228  		// Not in a CGI environment; skipping test.
	 229  		return
	 230  	}
	 231  	switch os.Getenv("REQUEST_URI") {
	 232  	case "/immediate-disconnect":
	 233  		os.Exit(0)
	 234  	case "/no-content-type":
	 235  		fmt.Printf("Content-Length: 6\n\nHello\n")
	 236  		os.Exit(0)
	 237  	case "/empty-headers":
	 238  		fmt.Printf("\nHello")
	 239  		os.Exit(0)
	 240  	}
	 241  	Serve(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
	 242  		if req.FormValue("nil-request-body") == "1" {
	 243  			fmt.Fprintf(rw, "nil-request-body=%v\n", req.Body == nil)
	 244  			return
	 245  		}
	 246  		rw.Header().Set("X-Test-Header", "X-Test-Value")
	 247  		req.ParseForm()
	 248  		if req.FormValue("no-body") == "1" {
	 249  			return
	 250  		}
	 251  		if eb, ok := req.Form["exact-body"]; ok {
	 252  			io.WriteString(rw, eb[0])
	 253  			return
	 254  		}
	 255  		if req.FormValue("write-forever") == "1" {
	 256  			io.Copy(rw, neverEnding('a'))
	 257  			for {
	 258  				time.Sleep(5 * time.Second) // hang forever, until killed
	 259  			}
	 260  		}
	 261  		fmt.Fprintf(rw, "test=Hello CGI-in-CGI\n")
	 262  		for k, vv := range req.Form {
	 263  			for _, v := range vv {
	 264  				fmt.Fprintf(rw, "param-%s=%s\n", k, v)
	 265  			}
	 266  		}
	 267  		for _, kv := range os.Environ() {
	 268  			fmt.Fprintf(rw, "env-%s\n", kv)
	 269  		}
	 270  	}))
	 271  	os.Exit(0)
	 272  }
	 273  

View as plain text