Reading body of http.Request without modifying request state?

I have a type implementing the http.Handler interface where, in its ServeHTTP method, incoming HTTP requests are inspected, some action is taken, and then the requests are forwarded to a reverse proxy handler (httputil.NewSingleHostReverseProxy).

This works fine, so long as I'm only inspecting the basic request properties, such as the URL or headers.

When I want to inspect the body of an incoming POST request, e.g. by calling req.ParseForm() and then using the req.Form property, I run into an error once the request is passed onto the reverse proxy:

http: proxy error: http: Request.ContentLength=687 with Body length 0

I imagine this happens because looking at the body of the HTTP request causes the req.Body.Reader stream to be drained, meaning it cannot be read again by the proxy handler.

I've been playing with things like io.Copy() and bufio.Peek(), but I'm not really getting anywhere.

Is there a way to peek at the HTTP request body (and use the built-in parsing of req.ParseForm etc.), while leaving the original request object in its original state, so that it can be passed to the reverse proxy?


Solution 1:

Try reading into a buffer and then using the buffer to back two new readers, one for you to use, and one for subsequent consumers to use. For example, imagine that we want to modify the following code:

doStuff(r.Body) // r is an http.Request

We could do:

buf, _ := ioutil.ReadAll(r.Body)
rdr1 := ioutil.NopCloser(bytes.NewBuffer(buf))
rdr2 := ioutil.NopCloser(bytes.NewBuffer(buf))

doStuff(rdr1)
r.Body = rdr2 // OK since rdr2 implements the io.ReadCloser interface

// Now the program can continue oblivious to the fact that
// r.Body was ever touched.

Note that *bytes.Buffer does not have a Close() error method, so it doesn't implement the io.ReadCloser interface. Thus, we have to wrap our *bytes.Buffer values in calls to ioutil.NopCloser.