warp-3.4.15: Network/Wai/Handler/Warp/Header.hs
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}
module Network.Wai.Handler.Warp.Header (
IndexedRequestHeader (..),
ResponseHeaderPresence (..),
indexRequestHeader,
defaultIndexRequestHeader,
indexResponseHeader,
) where
import qualified Data.ByteString as BS
import Data.CaseInsensitive (foldedCase)
import Data.List as L (foldl')
import Network.HTTP.Types
import Network.Wai.Handler.Warp.Types
----------------------------------------------------------------
-- | Strict record of the request headers that Warp inspects,
-- one field per header.
data IndexedRequestHeader = IndexedRequestHeader
{ reqidxContentLength :: Maybe HeaderValue
, reqidxTransferEncoding :: Maybe HeaderValue
, reqidxExpect :: Maybe HeaderValue
, reqidxConnection :: Maybe HeaderValue
, reqidxRange :: Maybe HeaderValue
, reqidxHost :: Maybe HeaderValue
, reqidxIfModifiedSince :: Maybe HeaderValue
, reqidxIfUnmodifiedSince :: Maybe HeaderValue
, reqidxIfRange :: Maybe HeaderValue
, reqidxReferer :: Maybe HeaderValue
, reqidxUserAgent :: Maybe HeaderValue
, reqidxIfMatch :: Maybe HeaderValue
, reqidxIfNoneMatch :: Maybe HeaderValue
}
indexRequestHeader :: RequestHeaders -> IndexedRequestHeader
indexRequestHeader = L.foldl' insert defaultIndexRequestHeader
where
insert ix (key, val) = case BS.length bs of
4 | bs == "host" -> ix{reqidxHost = Just val}
5 | bs == "range" -> ix{reqidxRange = Just val}
6 | bs == "expect" -> ix{reqidxExpect = Just val}
7 | bs == "referer" -> ix{reqidxReferer = Just val}
8
| bs == "if-range" -> ix{reqidxIfRange = Just val}
| bs == "if-match" -> ix{reqidxIfMatch = Just val}
10
| bs == "user-agent" -> ix{reqidxUserAgent = Just val}
| bs == "connection" -> ix{reqidxConnection = Just val}
13 | bs == "if-none-match" -> ix{reqidxIfNoneMatch = Just val}
14 | bs == "content-length" -> ix{reqidxContentLength = Just val}
17
| bs == "transfer-encoding" -> ix{reqidxTransferEncoding = Just val}
| bs == "if-modified-since" -> ix{reqidxIfModifiedSince = Just val}
19 | bs == "if-unmodified-since" -> ix{reqidxIfUnmodifiedSince = Just val}
_ -> ix
where
bs = foldedCase key
-- | 'IndexedRequestHeader' with no headers set.
defaultIndexRequestHeader :: IndexedRequestHeader
defaultIndexRequestHeader =
IndexedRequestHeader
{ reqidxContentLength = Nothing
, reqidxTransferEncoding = Nothing
, reqidxExpect = Nothing
, reqidxConnection = Nothing
, reqidxRange = Nothing
, reqidxHost = Nothing
, reqidxIfModifiedSince = Nothing
, reqidxIfUnmodifiedSince = Nothing
, reqidxIfRange = Nothing
, reqidxReferer = Nothing
, reqidxUserAgent = Nothing
, reqidxIfMatch = Nothing
, reqidxIfNoneMatch = Nothing
}
----------------------------------------------------------------
-- | Presence of the response headers Warp itself consults.
-- Only these four headers are ever looked up on the response side, and
-- only their presence, never their value, so a flat record of strict
-- 'Bool's built in a single traversal beats a boxed array.
data ResponseHeaderPresence = ResponseHeaderPresence
{ hasContentLength :: Bool
, hasServer :: Bool
, hasDate :: Bool
, hasLastModified :: Bool
, hasTransferEncoding :: Maybe HeaderValue
, hasConnection :: Maybe HeaderValue
}
indexResponseHeader :: ResponseHeaders -> ResponseHeaderPresence
indexResponseHeader = go emptyResponseHeaderPresence
where
go ix [] = ix
go ix (tup : rest) = go (insert ix tup) rest
insert ix (key, val) = case BS.length bs of
4 | bs == "date" -> ix{hasDate = True}
6 | bs == "server" -> ix{hasServer = True}
10 | bs == "connection" -> ix{hasConnection = Just val}
13 | bs == "last-modified" -> ix{hasLastModified = True}
14 | bs == "content-length" -> ix{hasContentLength = True}
17 | bs == "transfer-encoding" -> ix{hasTransferEncoding = Just val}
_ -> ix
where
bs = foldedCase key
emptyResponseHeaderPresence :: ResponseHeaderPresence
emptyResponseHeaderPresence =
ResponseHeaderPresence
{ hasContentLength = False
, hasServer = False
, hasDate = False
, hasLastModified = False
, hasTransferEncoding = Nothing
, hasConnection = Nothing
}