quic-simple-0.1.0.0: src/Network/QUIC/Simple/Stream.hs
module Network.QUIC.Simple.Stream
( MessageQueues
, streamSerialise
, streamCodec
) where
import Codec.Serialise (Serialise, serialise, deserialiseIncremental)
import Codec.Serialise qualified as IDecode (IDecode(..))
import Control.Concurrent (forkIO)
import Control.Concurrent.Async (race_)
import Control.Concurrent.STM
import Control.Exception (throwIO)
import Control.Monad.ST (stToIO)
import Data.ByteString qualified as BS
import Data.ByteString.Lazy qualified as BSL
import Data.IORef
import Network.QUIC qualified as QUIC
type MessageQueues sendMsg recvMsg = (TBQueue sendMsg, TBQueue recvMsg)
streamSerialise
:: forall sendMsg recvMsg
. (Serialise sendMsg, Serialise recvMsg)
=> QUIC.Stream
-> IO (MessageQueues sendMsg recvMsg)
streamSerialise stream = do
initial <- stToIO $ deserialiseIncremental @recvMsg
state <- newIORef initial
let
decode starting chunk = do
decoder <- readIORef state
case decoder of
IDecode.Fail _leftovers _offset err ->
throwIO err -- crash writer (thus the stream, and the reader/writer etc)
IDecode.Done leftovers _consumed msg -> do
stToIO deserialiseIncremental >>= writeIORef state -- restart decoder
pure (leftovers, Just msg)
IDecode.Partial consume -> do
-- want more data (initial state?)
stToIO (consume $ Just chunk) >>= writeIORef state -- step decoder
if starting then
-- re-check if done
decode False ""
else
-- suspend and wait for next chunk
pure ("", Nothing)
streamCodec serialise (decode True) stream
streamCodec
:: (sendMsg -> BSL.ByteString)
-> (BS.ByteString -> IO (BS.ByteString, Maybe recvMsg))
-> QUIC.Stream
-> IO (MessageQueues sendMsg recvMsg)
streamCodec encode decode stream = do
readQ <- newTBQueueIO 1024
writeQ <- newTBQueueIO 1024
_tid <- forkIO $ race_ (reader "" readQ) (writer writeQ)
pure (writeQ, readQ)
where
reader leftovers readQ = do
chunk <-
if BS.null leftovers then
QUIC.recvStream stream 4096
else
pure leftovers
(leftovers', message_) <- decode chunk
mapM_ (atomically . writeTBQueue readQ) message_
reader leftovers' readQ
writer writeQ = do
message <- atomically $ readTBQueue writeQ
let chunks = BSL.toChunks $ encode message
QUIC.sendStreamMany stream chunks
writer writeQ