{-# LANGUAGE BangPatterns #-} module Network.HPACK.Context ( -- * Types HeaderSet -- re-exporting , Context , newContext , DecodeError(..) , printContext -- * Initialization and final results , clearHeaderSet , getHeaderSet , emitNotEmitted -- * Processing , clearRefSets , removeRef , newEntry , pushRef , emitOnly -- * Auxiliary functions , isPresentIn , getEntry -- * Table , whichTable , lookupHeader ) where import Control.Applicative ((<$>)) import Network.HPACK.Context.HeaderSet import Network.HPACK.Context.ReferenceSet import Network.HPACK.Table import Network.HPACK.Types ---------------------------------------------------------------- -- | Context for encoding/decoding. data Context = Context { headerTable :: !HeaderTable -- ^ A cache of headers , oldReferenceSet :: ReferenceSet -- ^ References for not emitted , newReferenceSet :: ReferenceSet -- ^ References for already mitted , headerSet :: HeaderSet -- ^ Emitted header set. -- Encode: the previous ones. -- Decode: the results. } -- | Printing 'Context' printContext :: Context -> IO () printContext (Context hdrtbl oldref newref hdrset) = do putStrLn "<<
>>" printHeaderTable hdrtbl putStr "\n" putStrLn "<<>>" print $ getIndices oldref putStr "\n" putStrLn "<<>>" print $ getIndices newref putStr "\n" putStrLn "<<>>" printHeaderSet hdrset ---------------------------------------------------------------- -- | Creating a new 'Context'. -- The first argument is the size of 'HeaderTable'. newContext :: Size -> IO Context newContext maxsiz = do hdrtbl <- newHeaderTable maxsiz return $ Context hdrtbl emptyReferenceSet emptyReferenceSet emptyHeaderSet ---------------------------------------------------------------- -- | The reference set is emptied. clearRefSets :: Context -> IO Context clearRefSets ctx = return ctx { oldReferenceSet = emptyReferenceSet , newReferenceSet = emptyReferenceSet } -- | The entry is removed from the reference set. removeRef :: Context -> Index -> IO Context removeRef (Context hdrtbl oldref newref hdrset) idx = return ctx where oldref' = removeIndex idx oldref newref' = removeIndex idx newref ctx = Context hdrtbl oldref' newref' hdrset -- | The header field is emitted. -- The header field is inserted at the beginning of the header table. -- A reference to the new entry is added to the reference set. newEntry :: Context -> Entry -> IO Context newEntry (Context hdrtbl oldref newref hdrset) e = do (hdrtbl', is) <- insertEntry e hdrtbl let oldref' = removeIndices is $ adjustReferenceSet oldref newref' = addIndex 1 $ removeIndices is $ adjustReferenceSet newref hdrset' = insertHeader (fromEntry e) hdrset return $ Context hdrtbl' oldref' newref' hdrset' -- | The header field corresponding to the referenced entry is emitted. -- The referenced header table entry is added to the reference set. pushRef :: Context -> Index -> Entry -> IO Context pushRef (Context hdrtbl oldref newref hdrset) idx e = return ctx where hdrset' = insertHeader (fromEntry e) hdrset -- isPresentIn ensures that idx does not exist in -- newref and oldref. newref' = addIndex idx newref ctx = Context hdrtbl oldref newref' hdrset' -- | The header field is emitted. emitOnly :: Context -> Header -> IO Context emitOnly (Context hdrtbl oldref newref hdrset) h = return ctx where hdrset' = insertHeader h hdrset ctx = Context hdrtbl oldref newref hdrset' ---------------------------------------------------------------- -- | Emitting non-emitted headers. emitNotEmitted :: Context -> IO Context emitNotEmitted ctx = emit ctx <$> getNotEmitted ctx -- | Emit non-emitted headers. emit :: Context -> HeaderSet -> Context emit (Context hdrtbl oldref newref hdrset) notEmitted = ctx where hdrset' = meregeHeaderSet hdrset notEmitted oldref' = mergeReferenceSet newref oldref ctx = Context hdrtbl oldref' emptyReferenceSet hdrset' getNotEmitted :: Context -> IO HeaderSet getNotEmitted ctx = do let is = getIndices $ oldReferenceSet ctx hdrtbl = headerTable ctx map (fromEntry . snd) <$> mapM (which hdrtbl) is ---------------------------------------------------------------- -- | Is 'Index' present in the reference set? isPresentIn :: Index -> Context -> Bool isPresentIn idx ctx = idx `isMember` oldref || idx `isMember` newref where oldref = oldReferenceSet ctx newref = newReferenceSet ctx ---------------------------------------------------------------- -- | Which table does 'Index' refer to? whichTable :: Index -> Context -> IO (WhichTable, Entry) whichTable idx ctx = which hdrtbl idx where hdrtbl = headerTable ctx -- | Which table contains 'Header'? lookupHeader :: Header -> Context -> IO HeaderCache lookupHeader h ctx = lookupTable h (headerTable ctx) ---------------------------------------------------------------- -- | Getting 'Entry' by 'Index'. getEntry :: Index -> Context -> IO Entry getEntry idx ctx = snd <$> whichTable idx ctx ---------------------------------------------------------------- -- | Clearing 'HeaderSet' in 'Context' for the next decode. clearHeaderSet :: Context -> Context clearHeaderSet ctx = ctx { headerSet = emptyHeaderSet } -- | Getting 'HeaderSet' as emitted headers. getHeaderSet :: Context -> HeaderSet getHeaderSet = headerSet