http: consume request bodies before replying

This fixes our http behavior (even if Handlers forget to
consume a request body, we do it for them before we send
their response header), fixes the racy TestServerExpect,
and adds TestServerConsumesRequestBody.

With GOMAXPROCS>1, the http tests now seem race-free.

R=rsc
CC=golang-dev
https://golang.org/cl/4419042
This commit is contained in:
Brad Fitzpatrick 2011-04-14 10:40:23 -07:00
parent e0533b044d
commit e27702545a
2 changed files with 62 additions and 1 deletions

View File

@ -588,7 +588,7 @@ func TestServerExpect(t *testing.T) {
sendf := func(format string, args ...interface{}) {
_, err := fmt.Fprintf(conn, format, args...)
if err != nil {
t.Fatalf("Error writing %q: %v", format, err)
t.Fatalf("On test %#v, error writing %q: %v", test, format, err)
}
}
go func() {
@ -616,3 +616,49 @@ func TestServerExpect(t *testing.T) {
runTest(test)
}
}
func TestServerConsumesRequestBody(t *testing.T) {
log := make(chan string, 100)
ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
log <- "got_request"
w.WriteHeader(StatusOK)
log <- "wrote_header"
}))
defer ts.Close()
conn, err := net.Dial("tcp", ts.Listener.Addr().String())
if err != nil {
t.Fatalf("Dial: %v", err)
}
defer conn.Close()
bufr := bufio.NewReader(conn)
gotres := make(chan bool)
go func() {
line, err := bufr.ReadString('\n')
if err != nil {
t.Fatal(err)
}
log <- line
gotres <- true
}()
size := 1 << 20
log <- "writing_request"
fmt.Fprintf(conn, "POST / HTTP/1.0\r\nContent-Length: %d\r\n\r\n", size)
time.Sleep(25e6) // give server chance to misbehave & speak out of turn
log <- "slept_after_req_headers"
conn.Write([]byte(strings.Repeat("a", size)))
<-gotres
expected := []string{
"writing_request", "got_request",
"slept_after_req_headers", "wrote_header",
"HTTP/1.0 200 OK\r\n"}
for step, e := range expected {
if g := <-log; e != g {
t.Errorf("on step %d expected %q, got %q", step, e, g)
}
}
}

View File

@ -141,9 +141,13 @@ func newConn(rwc net.Conn, handler Handler) (c *conn, err os.Error) {
type expectContinueReader struct {
resp *response
readCloser io.ReadCloser
closed bool
}
func (ecr *expectContinueReader) Read(p []byte) (n int, err os.Error) {
if ecr.closed {
return 0, os.NewError("http: Read after Close on request Body")
}
if !ecr.resp.wroteContinue && !ecr.resp.conn.hijacked {
ecr.resp.wroteContinue = true
io.WriteString(ecr.resp.conn.buf, "HTTP/1.1 100 Continue\r\n\r\n")
@ -153,6 +157,7 @@ func (ecr *expectContinueReader) Read(p []byte) (n int, err os.Error) {
}
func (ecr *expectContinueReader) Close() os.Error {
ecr.closed = true
return ecr.readCloser.Close()
}
@ -196,6 +201,16 @@ func (w *response) WriteHeader(code int) {
log.Print("http: multiple response.WriteHeader calls")
return
}
// Per RFC 2616, we should consume the request body before
// replying, if the handler hasn't already done so.
if w.req.ContentLength != 0 {
ecr, isExpecter := w.req.Body.(*expectContinueReader)
if !isExpecter || ecr.resp.wroteContinue {
w.req.Body.Close()
}
}
w.wroteHeader = true
w.status = code
if code == StatusNotModified {