module Network.HPACK.HeaderBlock.Encode (
toByteStream
) where
import Blaze.ByteString.Builder (Builder)
import qualified Blaze.ByteString.Builder as BB
import Data.Bits (setBit)
import qualified Data.ByteString as BS
import Data.List (foldl')
import Data.Monoid ((<>), mempty)
import Data.Word (Word8)
import Network.HPACK.HeaderBlock.HeaderField
import qualified Network.HPACK.HeaderBlock.Integer as I
import qualified Network.HPACK.HeaderBlock.String as S
import Network.HPACK.Huffman
import Network.HPACK.Types
toByteStream :: HuffmanEncoding -> Bool -> HeaderBlock -> ByteStream
toByteStream he huff hbs = BB.toByteString $ foldl' (<>) mempty $ map toBB hbs
where
toBB = fromHeaderField he huff
fromHeaderField :: HuffmanEncoding -> Bool -> HeaderField -> Builder
fromHeaderField _ _ (Indexed idx) = index idx
fromHeaderField he huff (Literal NotAdd (Idx idx) v) = indexedName he huff set01 idx v
fromHeaderField he huff (Literal NotAdd (Lit key) v) = newName he huff set01 key v
fromHeaderField he huff (Literal Add (Idx idx) v) = indexedName he huff set00 idx v
fromHeaderField he huff (Literal Add (Lit key) v) = newName he huff set00 key v
index :: Int -> Builder
index i = BB.fromWord8s (w':ws)
where
(w:ws) = I.encode 7 i
w' = set1 w
indexedName :: HuffmanEncoding -> Bool -> Setter -> Int -> HeaderValue -> Builder
indexedName he huff set idx v = pre <> vlen <> val
where
(p:ps) = I.encode 6 idx
pre = BB.fromWord8s $ set p : ps
value = S.encode he v
valueLen = BS.length value
vlen
| huff = BB.fromWord8s $ setH $ I.encode 7 valueLen
| otherwise = BB.fromWord8s $ I.encode 7 valueLen
val = BB.fromByteString value
newName :: HuffmanEncoding -> Bool -> Setter -> HeaderName -> HeaderValue -> Builder
newName he huff set k v = pre <> klen <> key <> vlen <> val
where
pre = BB.fromWord8 $ set 0
key0 = S.encode he k
keyLen = BS.length key0
value = S.encode he v
valueLen = BS.length value
klen
| huff = BB.fromWord8s $ setH $ I.encode 7 keyLen
| otherwise = BB.fromWord8s $ I.encode 7 keyLen
vlen
| huff = BB.fromWord8s $ setH $ I.encode 7 valueLen
| otherwise = BB.fromWord8s $ I.encode 7 valueLen
key = BB.fromByteString key0
val = BB.fromByteString value
type Setter = Word8 -> Word8
set1, set01, set00 :: Setter
set1 x = x `setBit` 7
set01 x = x `setBit` 6
set00 = id
setH :: [Word8] -> [Word8]
setH [] = error "setH"
setH (x:xs) = (x `setBit` 7) : xs