-
Notifications
You must be signed in to change notification settings - Fork 276
Middleware to record request and response sizes #1034
Copy link
Copy link
Open
Description
I am interested in having WAI middleware that records the request body size and the response body size (in my case to prometheus). I don't think this exists today.
I haven't tested it, but I got this compiling which I think is roughly that. Would WAI be interested in a PR with something similar to this?
-- Largely modelled off of requestSizeCheck from wai-extra
recordRequestSize :: (Word64 -> IO ()) -> Wai.Request -> IO Wai.Request
recordRequestSize recordLength req =
case Wai.requestBodyLength req of
Wai.KnownLength len -> do
recordLength len
pure req
Wai.ChunkedBody -> do
currentSizeRef <- newIORef (0, False)
let yieldNextBodyChunk :: IO ByteString
yieldNextBodyChunk = do
bs <- Wai.getRequestBodyChunk req
if BS.length bs == 0 -- Out of chunks
then do
(currentSize, hasReported :: Bool) <- readIORef currentSizeRef
-- (Just in case someone repeatedly calls for the next chunk when there is none)
unless hasReported do
atomicModifyIORef' currentSizeRef (\(prevSize, _) -> ((prevSize, True), ()))
recordLength currentSize
else do
atomicModifyIORef' currentSizeRef $ \(prevSize, reported) ->
let nextSize = prevSize + fromIntegral (BS.length bs)
in ((nextSize, reported), ())
pure ()
pure bs
-- Each time a caller downstream gets the next chunk, it will call yieldNextBodyChunk
-- Which gives + increments the counter on the request size, until it's out of chunks
pure $ Wai.setRequestBodyChunks yieldNextBodyChunk req
recordResponseBody :: IORef Int64 -> Wai.Response -> IO Wai.Response
recordResponseBody outgoingRef response = case response of
Wai.ResponseStream status headers streamingBody -> do
let wrappedBody :: (Builder.Builder -> IO ()) -> IO () -> IO ()
wrappedBody originalSend flush =
let modifiedSend builder = do
let chunkSize = LBS.length (Builder.toLazyByteString builder)
atomicModifyIORef' outgoingRef (\x -> (x + chunkSize, ()))
originalSend builder
in streamingBody modifiedSend flush
pure $ Wai.ResponseStream status headers wrappedBody
Wai.ResponseBuilder _ _ builder -> do
let total = LBS.length (Builder.toLazyByteString builder)
atomicWriteIORef outgoingRef total
pure response
Wai.ResponseRaw _ _ -> pure response -- Not sure if it's doable to measure raw responses
Wai.ResponseFile _ _ filePath _ -> do
size <- getFileSize filePath
atomicWriteIORef outgoingRef (fromIntegral size)
pure responseReactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels