http3-0.0.24: Network/HTTP3/Recv.hs
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
module Network.HTTP3.Recv (
Source,
newSource,
readSource,
readSource',
recvHeader,
recvBody,
) where
import qualified Data.ByteString as BS
import Data.IORef
import Network.QUIC
import Imports
import Network.HTTP3.Context
import Network.HTTP3.Error
import Network.HTTP3.Frame
data Source = Source
{ sourceRead :: IO ByteString
, sourcePending :: IORef (Maybe ByteString)
}
newSource :: Stream -> IO Source
newSource strm = Source (recvStream strm 1024) <$> newIORef Nothing
readSource :: Source -> IO ByteString
readSource Source{..} = do
mx <- readIORef sourcePending
case mx of
Nothing -> sourceRead
Just x -> do
writeIORef sourcePending Nothing
return x
readSource' :: Source -> IO (ByteString, Bool)
readSource' src = do
x <- readSource src
return $ if x == "" then (x, True) else (x, False)
pushbackSource :: Source -> ByteString -> IO ()
pushbackSource _ "" = return ()
pushbackSource Source{..} bs = writeIORef sourcePending $ Just bs
recvHeader :: Context -> StreamId -> Source -> IO (Maybe TokenHeaderTable)
recvHeader ctx sid src = loop IInit
where
loop st = do
bs <- readSource src
if bs == ""
then return Nothing
else case parseH3Frame st bs of
IDone typ payload leftover
| typ == H3FrameHeaders -> do
pushbackSource src leftover
Just <$> qpackDecode ctx sid payload
| typ == H3FrameData -> do
abort ctx H3FrameUnexpected
loop IInit -- dummy
| permittedInRequestStream typ -> do
pushbackSource src leftover
loop IInit
| otherwise -> do
abort ctx H3FrameUnexpected
loop IInit -- dummy
st' -> loop st'
recvBody
:: Context
-> StreamId
-> Source
-> IORef IFrame
-> IORef (Maybe TokenHeaderTable)
-> IO (ByteString, Bool)
recvBody ctx sid src refI refH = do
st <- readIORef refI
loop st
where
loop st = do
bs <- readSource src
if bs == ""
then return ("", True)
else case parseH3Frame st bs of
IPay H3FrameData siz received bss -> do
let st' = IPay H3FrameData siz received []
if null bss
then loop st'
else do
writeIORef refI st'
let ret = BS.concat $ reverse bss
return (ret, False)
IDone typ payload leftover
| typ == H3FrameHeaders -> do
writeIORef refI IInit
-- pushbackSource src leftover -- fixme
hdr <- qpackDecode ctx sid payload
writeIORef refH $ Just hdr
return ("", True)
| typ == H3FrameData -> do
writeIORef refI IInit
pushbackSource src leftover
return (payload, False)
| permittedInRequestStream typ -> do
pushbackSource src leftover
loop IInit
| otherwise -> do
abort ctx H3FrameUnexpected
return (payload, False) -- dummy
st' -> loop st'