I’m trying to rebuild a consumed request body in a WAI middleware.
When I call
getRequestBodyChunk :: Request -> IO ByteString
the body content is consumed, so the downstream application receives an empty body. To forward the original request body again, it needs to be preserved and then restored before passing the request onward.
I found that WAI provides this function:
setRequestBodyChunks :: IO ByteString -> Request -> Request
which seems to allow restoring the body. However, I’m not sure how to construct the required IO ByteString argument.
The documentation says:
The supplied IO action should return the next chunk of the body each time it is called and empty when it has been fully consumed.
Could someone show an example of how to implement such an action, or explain the recommended approach for reusing the request body within middleware?
Thanks!
Something like this:
bodyChunksRef <- newIORef []
...
chunk <- getRequestBodyChunk request
modifyIORef' bodyChunksRef (chunk :)
...
modifyIORef' bodyChunksRef reverse
let request' = flip setRequestBodyChunks request $ do
chunks <- readIORef bodyChunksRef
case chunks of
[] -> pure mempty
(chunk:rest) -> do
writeIORef bodyChunksRef rest
pure chunk
Essentially, instead of reading a body chunk from the network, we’re reading it from a mutable variable. You’ll want to track the body chunks as you get them. It’s a bit easier to just prepend the chunks as they come in, and then reverse them before passing them downstream.
If you need to pass the partial body along, you’ll want to use something like Data.Sequence instead, and you’ll need to think about concurrency.
1 Like
Thank you for your help! Using the hint from your code, I was able to solve it as follows:
makeAction :: L.ByteString -> IO (IO B.ByteString)
makeAction bs = do
let chunks = L.toChunks bs
ref <- newIORef chunks
return $ do
chunk <- readIORef ref
case chunk of
[] -> return B.empty
(x:xs) -> do
writeIORef ref xs
return x
I really appreciate it!
2 Likes