module Network.HPACK.HeaderBlock.Decode (
fromByteStream
) where
import Data.Bits (testBit, clearBit, (.&.))
import Data.ByteString (ByteString)
import qualified Data.ByteString as BS
import Data.Word (Word8)
import Network.HPACK.Builder
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
fromByteStream :: HuffmanDecoding -> ByteStream
-> Either DecodeError HeaderBlock
fromByteStream hd inp = go inp empty
where
go bs builder
| BS.null bs = Right $ run builder
| otherwise = do
(hf, bs') <- toHeaderField hd bs
go bs' (builder << hf)
toHeaderField :: HuffmanDecoding -> ByteString
-> Either DecodeError (HeaderField, ByteString)
toHeaderField hd bs
| BS.null bs = Left EmptyBlock
| w `testBit` 7 = Right $ indexed w bs'
| w `testBit` 6 = withoutIndexing hd w bs'
| otherwise = incrementalIndexing hd w bs'
where
w = BS.head bs
bs' = BS.tail bs
indexed :: Word8 -> ByteString -> (HeaderField, ByteString)
indexed w ws = (Indexed idx , ws')
where
w' = clearBit w 7
(idx, ws') = I.parseInteger 7 w' ws
withoutIndexing :: HuffmanDecoding -> Word8 -> ByteString
-> Either DecodeError (HeaderField, ByteString)
withoutIndexing hd w ws
| isIndexedName w = indexedName NotAdd hd w ws
| otherwise = newName NotAdd hd ws
incrementalIndexing :: HuffmanDecoding -> Word8 -> ByteString
-> Either DecodeError (HeaderField, ByteString)
incrementalIndexing hd w ws
| isIndexedName w = indexedName Add hd w ws
| otherwise = newName Add hd ws
indexedName :: Indexing -> HuffmanDecoding -> Word8 -> ByteString
-> Either DecodeError (HeaderField, ByteString)
indexedName indexing hd w ws = do
(val,ws'') <- headerStuff hd ws'
let hf = Literal indexing (Idx idx) val
return (hf, ws'')
where
p = mask6 w
(idx,ws') = I.parseInteger 6 p ws
newName :: Indexing -> HuffmanDecoding -> ByteString
-> Either DecodeError (HeaderField, ByteString)
newName indexing hd ws = do
(key,ws') <- headerStuff hd ws
(val,ws'') <- headerStuff hd ws'
let hf = Literal indexing (Lit key) val
return (hf, ws'')
headerStuff :: HuffmanDecoding -> ByteString
-> Either DecodeError (HeaderStuff, ByteString)
headerStuff hd bs
| BS.null bs = Left EmptyEncodedString
| otherwise = S.parseString hd huff len bs''
where
w = BS.head bs
bs' = BS.tail bs
p = dropHuffman w
huff = isHuffman w
(len, bs'') = I.parseInteger 7 p bs'
mask6 :: Word8 -> Word8
mask6 w = w .&. 63
isIndexedName :: Word8 -> Bool
isIndexedName w = mask6 w /= 0
isHuffman :: Word8 -> Bool
isHuffman w = w `testBit` 7
dropHuffman :: Word8 -> Word8
dropHuffman w = w `clearBit` 7