1
0
mirror of https://github.com/golang/go synced 2024-11-25 11:48:04 -07:00

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
This commit is contained in:
Brad Fitzpatrick 2011-06-09 18:10:21 -07:00
parent aac6afbb11
commit 5e8b9c614b
2 changed files with 18 additions and 4 deletions

View File

@ -12,6 +12,7 @@ import (
"fmt" "fmt"
. "http" . "http"
"http/httptest" "http/httptest"
"io"
"io/ioutil" "io/ioutil"
"log" "log"
"os" "os"
@ -495,6 +496,12 @@ func TestHeadResponses(t *testing.T) {
if err != ErrBodyNotAllowed { if err != ErrBodyNotAllowed {
t.Errorf("on Write, expected ErrBodyNotAllowed, got %v", err) 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() defer ts.Close()
res, err := Head(ts.URL) res, err := Head(ts.URL)

View File

@ -129,7 +129,7 @@ func (r *response) ReadFrom(src io.Reader) (n int64, err os.Error) {
// WriteHeader if it hasn't been called yet, and WriteHeader // WriteHeader if it hasn't been called yet, and WriteHeader
// is what sets r.chunking. // is what sets r.chunking.
r.Flush() r.Flush()
if !r.chunking { if !r.chunking && r.bodyAllowed() {
if rf, ok := r.conn.rwc.(io.ReaderFrom); ok { if rf, ok := r.conn.rwc.(io.ReaderFrom); ok {
n, err = rf.ReadFrom(src) n, err = rf.ReadFrom(src)
r.written += n r.written += n
@ -335,6 +335,15 @@ func (w *response) WriteHeader(code int) {
io.WriteString(w.conn.buf, "\r\n") 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) { func (w *response) Write(data []byte) (n int, err os.Error) {
if w.conn.hijacked { if w.conn.hijacked {
log.Print("http: response.Write on hijacked connection") log.Print("http: response.Write on hijacked connection")
@ -346,9 +355,7 @@ func (w *response) Write(data []byte) (n int, err os.Error) {
if len(data) == 0 { if len(data) == 0 {
return 0, nil return 0, nil
} }
if !w.bodyAllowed() {
if w.status == StatusNotModified || w.req.Method == "HEAD" {
// Must not have body.
return 0, ErrBodyNotAllowed return 0, ErrBodyNotAllowed
} }