{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE OverloadedStrings #-} module Network.Wai.Handler.Warp.Header where import Data.Array import Data.Array.ST import qualified Data.ByteString as BS import Data.CaseInsensitive (foldedCase) import Network.HTTP.Types import Network.Wai.Handler.Warp.Types ---------------------------------------------------------------- -- | Array for a set of HTTP headers. type IndexedHeader = Array Int (Maybe HeaderValue) ---------------------------------------------------------------- indexRequestHeader :: RequestHeaders -> IndexedHeader indexRequestHeader hdr = traverseHeader hdr requestMaxIndex requestKeyIndex data RequestHeaderIndex = ReqContentLength | ReqTransferEncoding | ReqExpect | ReqConnection | ReqRange | ReqHost | ReqIfModifiedSince | ReqIfUnmodifiedSince | ReqIfRange | ReqReferer | ReqUserAgent | ReqIfMatch | ReqIfNoneMatch deriving (Enum, Bounded) -- | The size for 'IndexedHeader' for HTTP Request. -- From 0 to this corresponds to: -- -- - \"Content-Length\" -- - \"Transfer-Encoding\" -- - \"Expect\" -- - \"Connection\" -- - \"Range\" -- - \"Host\" -- - \"If-Modified-Since\" -- - \"If-Unmodified-Since\" -- - \"If-Range\" -- - \"Referer\" -- - \"User-Agent\" -- - \"If-Match\" -- - \"If-None-Match\" requestMaxIndex :: Int requestMaxIndex = fromEnum (maxBound :: RequestHeaderIndex) requestKeyIndex :: HeaderName -> Int requestKeyIndex hn = case BS.length bs of 4 | bs == "host" -> fromEnum ReqHost 5 | bs == "range" -> fromEnum ReqRange 6 | bs == "expect" -> fromEnum ReqExpect 7 | bs == "referer" -> fromEnum ReqReferer 8 | bs == "if-range" -> fromEnum ReqIfRange | bs == "if-match" -> fromEnum ReqIfMatch 10 | bs == "user-agent" -> fromEnum ReqUserAgent | bs == "connection" -> fromEnum ReqConnection 13 | bs == "if-none-match" -> fromEnum ReqIfNoneMatch 14 | bs == "content-length" -> fromEnum ReqContentLength 17 | bs == "transfer-encoding" -> fromEnum ReqTransferEncoding | bs == "if-modified-since" -> fromEnum ReqIfModifiedSince 19 | bs == "if-unmodified-since" -> fromEnum ReqIfUnmodifiedSince _ -> -1 where bs = foldedCase hn defaultIndexRequestHeader :: IndexedHeader defaultIndexRequestHeader = array (0, requestMaxIndex) [(i, Nothing) | i <- [0 .. requestMaxIndex]] ---------------------------------------------------------------- indexResponseHeader :: ResponseHeaders -> IndexedHeader indexResponseHeader hdr = traverseHeader hdr responseMaxIndex responseKeyIndex data ResponseHeaderIndex = ResContentLength | ResServer | ResDate | ResLastModified deriving (Enum, Bounded) -- | The size for 'IndexedHeader' for HTTP Response. responseMaxIndex :: Int responseMaxIndex = fromEnum (maxBound :: ResponseHeaderIndex) responseKeyIndex :: HeaderName -> Int responseKeyIndex hn = case BS.length bs of 4 | bs == "date" -> fromEnum ResDate 6 | bs == "server" -> fromEnum ResServer 13 | bs == "last-modified" -> fromEnum ResLastModified 14 | bs == "content-length" -> fromEnum ResContentLength _ -> -1 where bs = foldedCase hn ---------------------------------------------------------------- traverseHeader :: [Header] -> Int -> (HeaderName -> Int) -> IndexedHeader traverseHeader hdr maxidx getIndex = runSTArray $ do arr <- newArray (0, maxidx) Nothing mapM_ (insert arr) hdr return arr where insert arr (key, val) | idx == -1 = return () | otherwise = writeArray arr idx (Just val) where idx = getIndex key