"io"
"net/http"
"net/http/httptest"
+ "net/url"
"os"
+ "strings"
"testing"
- "time"
)
// This test is a CGI host (testing host.go) that runs its own binary
h := &Handler{
Path: os.Args[0],
Root: "/test.go",
- Args: []string{"-test.run=TestBeChildCGIProcess"},
}
expectedMap := map[string]string{
"test": "Hello CGI-in-CGI",
}
replay := runCgiTest(t, h, "GET /test.go?foo=bar&a=b HTTP/1.0\nHost: example.com\n\n", expectedMap)
- if expected, got := "text/html; charset=utf-8", replay.Header().Get("Content-Type"); got != expected {
+ if expected, got := "text/plain; charset=utf-8", replay.Header().Get("Content-Type"); got != expected {
t.Errorf("got a Content-Type of %q; expected %q", got, expected)
}
if expected, got := "X-Test-Value", replay.Header().Get("X-Test-Header"); got != expected {
func TestKillChildAfterCopyError(t *testing.T) {
testenv.MustHaveExec(t)
- defer func() { testHookStartProcess = nil }()
- proc := make(chan *os.Process, 1)
- testHookStartProcess = func(p *os.Process) {
- proc <- p
- }
-
h := &Handler{
Path: os.Args[0],
Root: "/test.go",
- Args: []string{"-test.run=TestBeChildCGIProcess"},
}
- req, _ := http.NewRequest("GET", "http://example.com/test.cgi?write-forever=1", nil)
+ req, _ := http.NewRequest("GET", "http://example.com/test.go?write-forever=1", nil)
rec := httptest.NewRecorder()
var out bytes.Buffer
const writeLen = 50 << 10
rw := &customWriterRecorder{&limitWriter{&out, writeLen}, rec}
- donec := make(chan bool, 1)
- go func() {
- h.ServeHTTP(rw, req)
- donec <- true
- }()
-
- select {
- case <-donec:
- if out.Len() != writeLen || out.Bytes()[0] != 'a' {
- t.Errorf("unexpected output: %q", out.Bytes())
- }
- case <-time.After(5 * time.Second):
- t.Errorf("timeout. ServeHTTP hung and didn't kill the child process?")
- select {
- case p := <-proc:
- p.Kill()
- t.Logf("killed process")
- default:
- t.Logf("didn't kill process")
- }
+ h.ServeHTTP(rw, req)
+ if out.Len() != writeLen || out.Bytes()[0] != 'a' {
+ t.Errorf("unexpected output: %q", out.Bytes())
}
}
h := &Handler{
Path: os.Args[0],
Root: "/test.go",
- Args: []string{"-test.run=TestBeChildCGIProcess"},
}
expectedMap := map[string]string{
"_body": "",
}
}
+// Test that a child handler does not receive a nil Request Body.
+// golang.org/issue/39190
+func TestNilRequestBody(t *testing.T) {
+ testenv.MustHaveExec(t)
+
+ h := &Handler{
+ Path: os.Args[0],
+ Root: "/test.go",
+ }
+ expectedMap := map[string]string{
+ "nil-request-body": "false",
+ }
+ _ = runCgiTest(t, h, "POST /test.go?nil-request-body=1 HTTP/1.0\nHost: example.com\n\n", expectedMap)
+ _ = runCgiTest(t, h, "POST /test.go?nil-request-body=1 HTTP/1.0\nHost: example.com\nContent-Length: 0\n\n", expectedMap)
+}
+
+func TestChildContentType(t *testing.T) {
+ testenv.MustHaveExec(t)
+
+ h := &Handler{
+ Path: os.Args[0],
+ Root: "/test.go",
+ }
+ var tests = []struct {
+ name string
+ body string
+ wantCT string
+ }{
+ {
+ name: "no body",
+ wantCT: "text/plain; charset=utf-8",
+ },
+ {
+ name: "html",
+ body: "<html><head><title>test page</title></head><body>This is a body</body></html>",
+ wantCT: "text/html; charset=utf-8",
+ },
+ {
+ name: "text",
+ body: strings.Repeat("gopher", 86),
+ wantCT: "text/plain; charset=utf-8",
+ },
+ {
+ name: "jpg",
+ body: "\xFF\xD8\xFF" + strings.Repeat("B", 1024),
+ wantCT: "image/jpeg",
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ expectedMap := map[string]string{"_body": tt.body}
+ req := fmt.Sprintf("GET /test.go?exact-body=%s HTTP/1.0\nHost: example.com\n\n", url.QueryEscape(tt.body))
+ replay := runCgiTest(t, h, req, expectedMap)
+ if got := replay.Header().Get("Content-Type"); got != tt.wantCT {
+ t.Errorf("got a Content-Type of %q; expected it to start with %q", got, tt.wantCT)
+ }
+ })
+ }
+}
+
// golang.org/issue/7198
func Test500WithNoHeaders(t *testing.T) { want500Test(t, "/immediate-disconnect") }
func Test500WithNoContentType(t *testing.T) { want500Test(t, "/no-content-type") }
h := &Handler{
Path: os.Args[0],
Root: "/test.go",
- Args: []string{"-test.run=TestBeChildCGIProcess"},
}
expectedMap := map[string]string{
"_body": "",
t.Errorf("Got code %d; want 500", replay.Code)
}
}
-
-type neverEnding byte
-
-func (b neverEnding) Read(p []byte) (n int, err error) {
- for i := range p {
- p[i] = byte(b)
- }
- return len(p), nil
-}
-
-// Note: not actually a test.
-func TestBeChildCGIProcess(t *testing.T) {
- if os.Getenv("REQUEST_METHOD") == "" {
- // Not in a CGI environment; skipping test.
- return
- }
- switch os.Getenv("REQUEST_URI") {
- case "/immediate-disconnect":
- os.Exit(0)
- case "/no-content-type":
- fmt.Printf("Content-Length: 6\n\nHello\n")
- os.Exit(0)
- case "/empty-headers":
- fmt.Printf("\nHello")
- os.Exit(0)
- }
- Serve(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
- rw.Header().Set("X-Test-Header", "X-Test-Value")
- req.ParseForm()
- if req.FormValue("no-body") == "1" {
- return
- }
- if req.FormValue("write-forever") == "1" {
- io.Copy(rw, neverEnding('a'))
- for {
- time.Sleep(5 * time.Second) // hang forever, until killed
- }
- }
- fmt.Fprintf(rw, "test=Hello CGI-in-CGI\n")
- for k, vv := range req.Form {
- for _, v := range vv {
- fmt.Fprintf(rw, "param-%s=%s\n", k, v)
- }
- }
- for _, kv := range os.Environ() {
- fmt.Fprintf(rw, "env-%s\n", kv)
- }
- }))
- os.Exit(0)
-}