http: fix regression permitting io.Copy on HEAD response
With the ReadFrom change in the sendfile CL, it became
possible to illegally send a response to a HEAD request if you
did it via io.Copy.
Fixes #1939
R=rsc
CC=golang-dev
https://golang.org/cl/4584049
diff --git a/src/pkg/http/serve_test.go b/src/pkg/http/serve_test.go
index 1054d47..dc4594a 100644
--- a/src/pkg/http/serve_test.go
+++ b/src/pkg/http/serve_test.go
@@ -12,6 +12,7 @@
"fmt"
. "http"
"http/httptest"
+ "io"
"io/ioutil"
"log"
"os"
@@ -495,6 +496,12 @@
if err != ErrBodyNotAllowed {
t.Errorf("on Write, expected ErrBodyNotAllowed, got %v", err)
}
+
+ // Also exercise the ReaderFrom path
+ _, err = io.Copy(w, strings.NewReader("Ignored body"))
+ if err != ErrBodyNotAllowed {
+ t.Errorf("on Copy, expected ErrBodyNotAllowed, got %v", err)
+ }
}))
defer ts.Close()
res, err := Head(ts.URL)
diff --git a/src/pkg/http/server.go b/src/pkg/http/server.go
index 4063fad..d4638f1 100644
--- a/src/pkg/http/server.go
+++ b/src/pkg/http/server.go
@@ -129,7 +129,7 @@
// WriteHeader if it hasn't been called yet, and WriteHeader
// is what sets r.chunking.
r.Flush()
- if !r.chunking {
+ if !r.chunking && r.bodyAllowed() {
if rf, ok := r.conn.rwc.(io.ReaderFrom); ok {
n, err = rf.ReadFrom(src)
r.written += n
@@ -335,6 +335,15 @@
io.WriteString(w.conn.buf, "\r\n")
}
+// bodyAllowed returns true if a Write is allowed for this response type.
+// It's illegal to call this before the header has been flushed.
+func (w *response) bodyAllowed() bool {
+ if !w.wroteHeader {
+ panic("")
+ }
+ return w.status != StatusNotModified && w.req.Method != "HEAD"
+}
+
func (w *response) Write(data []byte) (n int, err os.Error) {
if w.conn.hijacked {
log.Print("http: response.Write on hijacked connection")
@@ -346,9 +355,7 @@
if len(data) == 0 {
return 0, nil
}
-
- if w.status == StatusNotModified || w.req.Method == "HEAD" {
- // Must not have body.
+ if !w.bodyAllowed() {
return 0, ErrBodyNotAllowed
}