Search code examples
clojurering

Reading Ring request body when already read


My question is, how can I idiomatically read the body of a Ring request if it has already been read?

Here's the background. I'm writing an error handler for a Ring app. When an error occurs, I want to log the error, including all relevant information that I might need to reproduce and fix the error. One important piece of information is the body of the request. However, the statefulness of the :body value (because it is a type of java.io.InputStream object) causes problems.

Specifically, what happens is that some middleware (the ring.middleware.json/wrap-json-body middleware in my case) does a slurp on the body InputStream object, which changes the internal state of the object such that future calls to slurp return an empty string. Thus, the [content of the] body is effectively lost from the request map.

The only solution I can think of is to preemptively copy the body InputStream object before the body can be read, just in case I might need it later. I don't like this approach because it seems clumsy to do some work on every request just in case there might be an error later. Is there a better approach?


Solution

  • I have a lib that sucks up the body, replaces it with a stream with identical contents, and stores the original so that it can be deflated later.

    groundhog

    This is not adequate for indefinitely open streams, and is a bad idea if the body is the upload of some large object. But it helps for testing, and recreating error conditions as a part of the debugging process.

    If all you need is a duplicate of the stream, you can use the tee-stream function from groundhog as the basis for your own middleware.