wai-extra-3.1.14: Network/Wai/Middleware/RequestSizeLimit.hs
{-# LANGUAGE CPP #-}
-- | The functions in this module allow you to limit the total size of incoming request bodies.
--
-- Limiting incoming request body size helps protect your server against denial-of-service (DOS) attacks,
-- in which an attacker sends huge bodies to your server.
module Network.Wai.Middleware.RequestSizeLimit (
-- * Middleware
requestSizeLimitMiddleware,
-- * Constructing 'RequestSizeLimitSettings'
defaultRequestSizeLimitSettings,
-- * 'RequestSizeLimitSettings' and accessors
RequestSizeLimitSettings,
setMaxLengthForRequest,
setOnLengthExceeded,
) where
import Control.Exception (catch, try)
import qualified Data.ByteString.Lazy as BSL
import qualified Data.ByteString.Lazy.Char8 as LS8
#if __GLASGOW_HASKELL__ < 804
import Data.Monoid ((<>))
#endif
import Data.Word (Word64)
import Network.HTTP.Types.Status (requestEntityTooLarge413)
import Network.Wai
import Network.Wai.Middleware.RequestSizeLimit.Internal (
RequestSizeLimitSettings (..),
setMaxLengthForRequest,
setOnLengthExceeded,
)
import Network.Wai.Request
-- | Create a 'RequestSizeLimitSettings' with these settings:
--
-- * 2MB size limit for all requests
-- * When the limit is exceeded, return a plain text response describing the error, with a 413 status code.
--
-- @since 3.1.1
defaultRequestSizeLimitSettings :: RequestSizeLimitSettings
defaultRequestSizeLimitSettings =
RequestSizeLimitSettings
{ maxLengthForRequest = \_req -> pure $ Just $ 2 * 1024 * 1024
, onLengthExceeded = \maxLen _app req sendResponse -> sendResponse (tooLargeResponse maxLen (requestBodyLength req))
}
-- | Middleware to limit request bodies to a certain size.
--
-- This uses 'requestSizeCheck' under the hood; see that function for details.
--
-- @since 3.1.1
requestSizeLimitMiddleware :: RequestSizeLimitSettings -> Middleware
requestSizeLimitMiddleware settings app req sendResponse = do
maybeMaxLen <- maxLengthForRequest settings req
case maybeMaxLen of
Nothing -> app req sendResponse
Just maxLen -> do
eitherSizeExceptionOrNewReq <- try (requestSizeCheck maxLen req)
case eitherSizeExceptionOrNewReq of
-- In the case of a known-length request, RequestSizeException will be thrown immediately
Left (RequestSizeException _maxLen) -> handleLengthExceeded maxLen
-- In the case of a chunked request (unknown length), RequestSizeException will be thrown during the processing of a body
Right newReq ->
app newReq sendResponse `catch` \(RequestSizeException _maxLen) -> handleLengthExceeded maxLen
where
handleLengthExceeded maxLen = onLengthExceeded settings maxLen app req sendResponse
tooLargeResponse :: Word64 -> RequestBodyLength -> Response
tooLargeResponse maxLen bodyLen =
responseLBS
requestEntityTooLarge413
[("Content-Type", "text/plain")]
( BSL.concat
[ "Request body too large to be processed. The maximum size is "
, LS8.pack (show maxLen)
, " bytes; your request body was "
, case bodyLen of
KnownLength knownLen -> LS8.pack (show knownLen) <> " bytes."
ChunkedBody -> "split into chunks, whose total size is unknown, but exceeded the limit."
, " If you're the developer of this site, you can configure the maximum length with `requestSizeLimitMiddleware`."
]
)