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
diff --git a/src/pkg/exec/exec.go b/src/pkg/exec/exec.go
index 4ddefae..3b20f20 100644
--- a/src/pkg/exec/exec.go
+++ b/src/pkg/exec/exec.go
@@ -338,7 +338,8 @@
 
 // StdoutPipe returns a pipe that will be connected to the command's
 // standard output when the command starts.
-func (c *Cmd) StdoutPipe() (io.Reader, os.Error) {
+// The pipe will be closed automatically after Wait sees the command exit.
+func (c *Cmd) StdoutPipe() (io.ReadCloser, os.Error) {
 	if c.Stdout != nil {
 		return nil, os.NewError("exec: Stdout already set")
 	}
@@ -357,7 +358,8 @@
 
 // StderrPipe returns a pipe that will be connected to the command's
 // standard error when the command starts.
-func (c *Cmd) StderrPipe() (io.Reader, os.Error) {
+// The pipe will be closed automatically after Wait sees the command exit.
+func (c *Cmd) StderrPipe() (io.ReadCloser, os.Error) {
 	if c.Stderr != nil {
 		return nil, os.NewError("exec: Stderr already set")
 	}
diff --git a/src/pkg/http/cgi/host.go b/src/pkg/http/cgi/host.go
index 059fc75..93825b3 100644
--- a/src/pkg/http/cgi/host.go
+++ b/src/pkg/http/cgi/host.go
@@ -187,6 +187,7 @@
 		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 b08d8bb..250324a 100644
--- a/src/pkg/http/cgi/host_test.go
+++ b/src/pkg/http/cgi/host_test.go
@@ -12,10 +12,14 @@
 	"fmt"
 	"http"
 	"http/httptest"
+	"io"
 	"os"
+	"net"
 	"path/filepath"
+	"strconv"
 	"strings"
 	"testing"
+	"time"
 	"runtime"
 )
 
@@ -304,8 +308,76 @@
 	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 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 36c107f..b46b133 100755
--- a/src/pkg/http/cgi/testdata/test.cgi
+++ b/src/pkg/http/cgi/testdata/test.cgi
@@ -25,9 +25,17 @@
 
 # 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) {