{-# LANGUAGE DeriveDataTypeable #-}
module Network.Wai.Request
( appearsSecure
, guessApproot
, RequestSizeException(..)
, requestSizeCheck
) where
import Data.ByteString (ByteString)
import Data.Maybe (fromMaybe)
import Network.HTTP.Types (HeaderName)
import Network.Wai
import qualified Data.ByteString as S
import qualified Data.ByteString.Char8 as C
import Control.Exception (Exception, throwIO)
import Data.Typeable (Typeable)
import Data.Word (Word64)
import Data.IORef (atomicModifyIORef', newIORef)
appearsSecure :: Request -> Bool
appearsSecure request = isSecure request || any (uncurry matchHeader)
[ ("HTTPS" , (== "on"))
, ("HTTP_X_FORWARDED_SSL" , (== "on"))
, ("HTTP_X_FORWARDED_SCHEME", (== "https"))
, ("HTTP_X_FORWARDED_PROTO" , ((== ["https"]) . take 1 . C.split ','))
, ("X-Forwarded-Proto" , (== "https"))
]
where
matchHeader :: HeaderName -> (ByteString -> Bool) -> Bool
matchHeader h f = maybe False f $ lookup h $ requestHeaders request
guessApproot :: Request -> ByteString
guessApproot req =
(if appearsSecure req then "https://" else "http://") `S.append`
(fromMaybe "localhost" $ requestHeaderHost req)
data RequestSizeException
= RequestSizeException Word64
deriving (Eq, Ord, Typeable)
instance Exception RequestSizeException
instance Show RequestSizeException where
showsPrec p (RequestSizeException limit) =
showString ("Request Body is larger than ") . showsPrec p limit . showString " bytes."
requestSizeCheck :: Word64 -> Request -> IO Request
requestSizeCheck maxSize req =
case requestBodyLength req of
KnownLength len ->
if len > maxSize
then return $ req { requestBody = throwIO (RequestSizeException maxSize) }
else return req
ChunkedBody -> do
currentSize <- newIORef 0
return $ req
{ requestBody = do
bs <- requestBody req
total <-
atomicModifyIORef' currentSize $ \sz ->
let nextSize = sz + fromIntegral (S.length bs)
in (nextSize, nextSize)
if total > maxSize
then throwIO (RequestSizeException maxSize)
else return bs
}