Skip to content

Commit

Permalink
net/http/httputil: permit nil request body in ReverseProxy
Browse files Browse the repository at this point in the history
Accepting a request with a nil body was never explicitly supported but
happened to work in the past.

This doesn't happen in most cases because usually people pass
a Server's incoming Request to the ReverseProxy's ServeHTTP method,
and incoming server requests are guaranteed to have non-nil bodies.

Still, it's a regression, so fix.

Fixes golang#12344

Change-Id: Id9a5a47aea3f2875d195b66c9a5f8581c4ca2aed
Reviewed-on: https://go-review.googlesource.com/13935
Reviewed-by: Ian Lance Taylor <[email protected]>
Run-TryBot: Brad Fitzpatrick <[email protected]>
TryBot-Result: Gobot Gobot <[email protected]>
  • Loading branch information
bradfitz committed Aug 26, 2015
1 parent b1b3243 commit a34b8cb
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 1 deletion.
5 changes: 4 additions & 1 deletion src/net/http/httputil/reverseproxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ type requestCanceler interface {
}

type runOnFirstRead struct {
io.Reader
io.Reader // optional; nil means empty body

fn func() // Run before first Read, then set to nil
}
Expand All @@ -115,6 +115,9 @@ func (c *runOnFirstRead) Read(bs []byte) (int, error) {
c.fn()
c.fn = nil
}
if c.Reader == nil {
return 0, io.EOF
}
return c.Reader.Read(bs)
}

Expand Down
39 changes: 39 additions & 0 deletions src/net/http/httputil/reverseproxy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
package httputil

import (
"bufio"
"io/ioutil"
"log"
"net/http"
Expand Down Expand Up @@ -281,3 +282,41 @@ func TestReverseProxyCancellation(t *testing.T) {
t.Fatal("DefaultClient.Do() returned nil error")
}
}

func req(t *testing.T, v string) *http.Request {
req, err := http.ReadRequest(bufio.NewReader(strings.NewReader(v)))
if err != nil {
t.Fatal(err)
}
return req
}

// Issue 12344
func TestNilBody(t *testing.T) {
backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("hi"))
}))
defer backend.Close()

frontend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
backURL, _ := url.Parse(backend.URL)
rp := NewSingleHostReverseProxy(backURL)
r := req(t, "GET / HTTP/1.0\r\n\r\n")
r.Body = nil // this accidentally worked in Go 1.4 and below, so keep it working
rp.ServeHTTP(w, r)
}))
defer frontend.Close()

res, err := http.Get(frontend.URL)
if err != nil {
t.Fatal(err)
}
defer res.Body.Close()
slurp, err := ioutil.ReadAll(res.Body)
if err != nil {
t.Fatal(err)
}
if string(slurp) != "hi" {
t.Errorf("Got %q; want %q", slurp, "hi")
}
}

0 comments on commit a34b8cb

Please sign in to comment.