{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
module Network.HTTP2.Server.HPACK (
hpackEncodeHeader
, hpackEncodeHeaderLoop
, hpackDecodeHeader
, hpackDecodeTrailer
, just
, fixHeaders
) where
import qualified Control.Exception as E
import Data.ByteString.Internal (unsafeCreate)
import Foreign.Ptr (plusPtr)
import Foreign.Storable (poke)
import Network.ByteOrder
import qualified Network.HTTP.Types as H
import Imports
import Network.HPACK
import Network.HPACK.Token
import Network.HTTP2
import Network.HTTP2.Server.Context
fixHeaders :: H.Status -> H.ResponseHeaders -> H.ResponseHeaders
fixHeaders st hdr = (":status", packStatus st) : deleteUnnecessaryHeaders hdr
packStatus :: H.Status -> ByteString
packStatus status = unsafeCreate 3 $ \p -> do
poke p (toW8 r2)
poke (p `plusPtr` 1) (toW8 r1)
poke (p `plusPtr` 2) (toW8 r0)
where
toW8 :: Int -> Word8
toW8 n = 48 + fromIntegral n
!s = fromIntegral $ H.statusCode status
(!q0,!r0) = s `divMod` 10
(!q1,!r1) = q0 `divMod` 10
!r2 = q1 `mod` 10
deleteUnnecessaryHeaders :: H.ResponseHeaders -> H.ResponseHeaders
deleteUnnecessaryHeaders hdr = filter del hdr
where
del (k,_) = k `notElem` headersToBeRemoved
headersToBeRemoved :: [H.HeaderName]
headersToBeRemoved = [ H.hConnection
, "Transfer-Encoding"
]
strategy :: EncodeStrategy
strategy = EncodeStrategy { compressionAlgo = Linear, useHuffman = False }
hpackEncodeHeader :: Context -> Buffer -> BufferSize
-> TokenHeaderList
-> IO (TokenHeaderList, Int)
hpackEncodeHeader Context{..} buf siz ths =
encodeTokenHeader buf siz strategy True encodeDynamicTable ths
hpackEncodeHeaderLoop :: Context -> Buffer -> BufferSize
-> TokenHeaderList
-> IO (TokenHeaderList, Int)
hpackEncodeHeaderLoop Context{..} buf siz hs =
encodeTokenHeader buf siz strategy False encodeDynamicTable hs
hpackDecodeHeader :: HeaderBlockFragment -> Context -> IO HeaderTable
hpackDecodeHeader hdrblk ctx = do
tbl@(_,vt) <- hpackDecodeTrailer hdrblk ctx
if checkRequestHeader vt then
return tbl
else
E.throwIO $ ConnectionError ProtocolError "the header key is illegal"
hpackDecodeTrailer :: HeaderBlockFragment -> Context -> IO HeaderTable
hpackDecodeTrailer hdrblk Context{..} = decodeTokenHeader decodeDynamicTable hdrblk `E.catch` handl
where
handl IllegalHeaderName =
E.throwIO $ ConnectionError ProtocolError "the header key is illegal"
handl _ =
E.throwIO $ ConnectionError CompressionError "cannot decompress the header"
{-# INLINE checkRequestHeader #-}
checkRequestHeader :: ValueTable -> Bool
checkRequestHeader reqvt
| just mMethod (== "CONNECT") = isNothing mPath && isNothing mScheme
| isJust mStatus = False
| isNothing mMethod = False
| isNothing mScheme = False
| isNothing mPath = False
| mPath == Just "" = False
| isJust mConnection = False
| just mTE (/= "trailers") = False
| otherwise = True
where
mStatus = getHeaderValue tokenStatus reqvt
mScheme = getHeaderValue tokenScheme reqvt
mPath = getHeaderValue tokenPath reqvt
mMethod = getHeaderValue tokenMethod reqvt
mConnection = getHeaderValue tokenConnection reqvt
mTE = getHeaderValue tokenTE reqvt
{-# INLINE just #-}
just :: Maybe a -> (a -> Bool) -> Bool
just Nothing _ = False
just (Just x) p
| p x = True
| otherwise = False