{-# LANGUAGE BangPatterns, OverloadedStrings, CPP #-} module Network.HPACK.HeaderBlock.To ( toHeaderBlock ) where #if __GLASGOW_HASKELL__ < 709 import Control.Applicative ((<$>)) #endif import Control.Arrow (second) import Network.HPACK.Builder import Network.HPACK.HeaderBlock.HeaderField import Network.HPACK.Table import Network.HPACK.Types type Ctx = (DynamicTable, Builder HeaderField) type Step = Ctx -> Header -> IO Ctx -- | Encoding 'HeaderList' to 'HeaderBlock'. toHeaderBlock :: CompressionAlgo -> DynamicTable -> HeaderList -> IO (DynamicTable, HeaderBlock) toHeaderBlock algo !dyntbl hs = do msiz <- needChangeTableSize dyntbl (dyntbl', op) <- case msiz of Keep -> do return (dyntbl, id) Change lim -> do tbl <- renewDynamicTable lim dyntbl return (tbl, (ChangeTableSize lim :)) Ignore lim -> do resetLimitForEncoding dyntbl return (dyntbl, (ChangeTableSize lim :)) second op <$> toHeaderBlock' algo dyntbl' hs toHeaderBlock' :: CompressionAlgo -> DynamicTable -> HeaderList -> IO (DynamicTable, HeaderBlock) toHeaderBlock' Naive !dyntbl hs = encodeLoop naiveStep hs (dyntbl,empty) toHeaderBlock' Static !dyntbl hs = encodeLoop staticStep hs (dyntbl,empty) toHeaderBlock' Linear !dyntbl hs = encodeLoop linearStep hs (dyntbl,empty) ---------------------------------------------------------------- encodeFinal :: Ctx -> IO (DynamicTable, HeaderBlock) encodeFinal (!dyntbl, !builder) = return (dyntbl, run builder) encodeLoop :: Step -> HeaderList -> Ctx -> IO (DynamicTable, HeaderBlock) encodeLoop step (h:hs) !dyntbl = step dyntbl h >>= encodeLoop step hs encodeLoop _ [] !dyntbl = encodeFinal dyntbl ---------------------------------------------------------------- naiveStep :: Step naiveStep (!dyntbl,!builder) (k,v) = do let builder' = builder << Literal NotAdd (Lit k) v return (dyntbl, builder') ---------------------------------------------------------------- staticStep :: Step staticStep (!dyntbl,!builder) h@(k,v) = return (dyntbl, builder') where b = case lookupTable h dyntbl of None -> Literal NotAdd (Lit k) v KeyOnly InStaticTable i -> Literal NotAdd (Idx i) v KeyOnly InDynamicTable _ -> Literal NotAdd (Lit k) v KeyValue InStaticTable i -> Literal NotAdd (Idx i) v KeyValue InDynamicTable _ -> Literal NotAdd (Lit k) v builder' = builder << b ---------------------------------------------------------------- -- A simple encoding strategy to reset the reference set first -- by 'Index 0' and uses indexing as much as possible. linearStep :: Step linearStep cb@(!dyntbl,!builder) h = smartStep linear cb h where linear i = return (dyntbl,builder << Indexed i) ---------------------------------------------------------------- smartStep :: (Index -> IO Ctx) -> Step smartStep func cb@(!dyntbl,!builder) h@(k,_) = do let cache = lookupTable h dyntbl case cache of None -> check cb h (Lit k) KeyOnly InStaticTable i -> check cb h (Idx i) KeyOnly InDynamicTable i -> check cb h (Idx i) KeyValue InStaticTable i -> return (dyntbl, builder << Indexed i) KeyValue InDynamicTable i -> func i check :: Ctx -> Header -> Naming -> IO Ctx check (dyntbl,builder) h@(k,v) x | k `elem` headersNotToIndex = do let builder' = builder << Literal NotAdd x v return (dyntbl, builder') | otherwise = do let e = toEntry h dyntbl' <- insertEntry e dyntbl let builder' = builder << Literal Add x v return (dyntbl', builder') headersNotToIndex :: [HeaderName] headersNotToIndex = [ ":path" , "content-length" , "location" , "etag" , "set-cookie" ]