aboutsummaryrefslogtreecommitdiff
path: root/src/pkg/http
diff options
context:
space:
mode:
authorBrad Fitzpatrick <bradfitz@golang.org>2011-07-11 15:59:27 -0700
committerBrad Fitzpatrick <bradfitz@golang.org>2011-07-11 15:59:27 -0700
commit1722ec22cd6a34dcef717cc242e3e1ac24366069 (patch)
tree453d0f03ca07f9796b02d9313624caa20278db35 /src/pkg/http
parentd53385fd0c986c33c60e4d6fce6b39b6bd779a72 (diff)
downloadgo-1722ec22cd6a34dcef717cc242e3e1ac24366069.tar.xz
cgi: close stdout reader pipe when finished
This causes the child, if still writing, to get an error or SIGPIPE and most likely exit so our subsequent wait can finish. A more guaranteed fix would be putting a time limit on the child's overall execution, but this fixes the problem I was having. Fixes #2059 R=rsc CC=golang-dev https://golang.org/cl/4675081
Diffstat (limited to 'src/pkg/http')
-rw-r--r--src/pkg/http/cgi/host.go1
-rw-r--r--src/pkg/http/cgi/host_test.go76
-rwxr-xr-xsrc/pkg/http/cgi/testdata/test.cgi8
3 files changed, 83 insertions, 2 deletions
diff --git a/src/pkg/http/cgi/host.go b/src/pkg/http/cgi/host.go
index 059fc758e3..93825b3919 100644
--- a/src/pkg/http/cgi/host.go
+++ b/src/pkg/http/cgi/host.go
@@ -187,6 +187,7 @@ func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
return
}
defer cmd.Wait()
+ defer stdoutRead.Close()
linebody, _ := bufio.NewReaderSize(stdoutRead, 1024)
headers := make(http.Header)
diff --git a/src/pkg/http/cgi/host_test.go b/src/pkg/http/cgi/host_test.go
index b08d8bbf68..250324a512 100644
--- a/src/pkg/http/cgi/host_test.go
+++ b/src/pkg/http/cgi/host_test.go
@@ -12,10 +12,14 @@ import (
"fmt"
"http"
"http/httptest"
+ "io"
"os"
+ "net"
"path/filepath"
+ "strconv"
"strings"
"testing"
+ "time"
"runtime"
)
@@ -304,8 +308,76 @@ func TestInternalRedirect(t *testing.T) {
runCgiTest(t, h, "GET /test.cgi?loc=/foo HTTP/1.0\nHost: example.com\n\n", expectedMap)
}
+// TestCopyError tests that we kill the process if there's an error copying
+// its output. (for example, from the client having gone away)
+func TestCopyError(t *testing.T) {
+ if skipTest(t) || runtime.GOOS == "windows" {
+ return
+ }
+ h := &Handler{
+ Path: "testdata/test.cgi",
+ Root: "/test.cgi",
+ }
+ ts := httptest.NewServer(h)
+ defer ts.Close()
+
+ conn, err := net.Dial("tcp", ts.Listener.Addr().String())
+ if err != nil {
+ t.Fatal(err)
+ }
+ req, _ := http.NewRequest("GET", "http://example.com/test.cgi?bigresponse=1", nil)
+ err = req.Write(conn)
+ if err != nil {
+ t.Fatalf("Write: %v", err)
+ }
+
+ res, err := http.ReadResponse(bufio.NewReader(conn), req)
+ if err != nil {
+ t.Fatalf("ReadResponse: %v", err)
+ }
+
+ pidstr := res.Header.Get("X-CGI-Pid")
+ if pidstr == "" {
+ t.Fatalf("expected an X-CGI-Pid header in response")
+ }
+ pid, err := strconv.Atoi(pidstr)
+ if err != nil {
+ t.Fatalf("invalid X-CGI-Pid value")
+ }
+
+ var buf [5000]byte
+ n, err := io.ReadFull(res.Body, buf[:])
+ if err != nil {
+ t.Fatalf("ReadFull: %d bytes, %v", n, err)
+ }
+
+ childRunning := func() bool {
+ p, err := os.FindProcess(pid)
+ if err != nil {
+ return false
+ }
+ return p.Signal(os.UnixSignal(0)) == nil
+ }
+
+ if !childRunning() {
+ t.Fatalf("pre-conn.Close, expected child to be running")
+ }
+ conn.Close()
+
+ if tries := 0; childRunning() {
+ for tries < 5 && childRunning() {
+ time.Sleep(50e6 * int64(tries))
+ tries++
+ }
+ if childRunning() {
+ t.Fatalf("post-conn.Close, expected child to be gone")
+ }
+ }
+}
+
+
func TestDirUnix(t *testing.T) {
- if runtime.GOOS == "windows" {
+ if skipTest(t) || runtime.GOOS == "windows" {
return
}
@@ -333,7 +405,7 @@ func TestDirUnix(t *testing.T) {
}
func TestDirWindows(t *testing.T) {
- if runtime.GOOS != "windows" {
+ if skipTest(t) || runtime.GOOS != "windows" {
return
}
diff --git a/src/pkg/http/cgi/testdata/test.cgi b/src/pkg/http/cgi/testdata/test.cgi
index 36c107f76b..b46b1330f3 100755
--- a/src/pkg/http/cgi/testdata/test.cgi
+++ b/src/pkg/http/cgi/testdata/test.cgi
@@ -25,9 +25,17 @@ my $p = sub {
# With carriage returns
$p->("Content-Type: text/html");
+$p->("X-CGI-Pid: $$");
$p->("X-Test-Header: X-Test-Value");
$p->("");
+if ($params->{"bigresponse"}) {
+ for (1..1024) {
+ print "A" x 1024, "\n";
+ }
+ exit 0;
+}
+
print "test=Hello CGI\n";
foreach my $k (sort keys %$params) {