{-# OPTIONS_HADDOCK hide #-} module SecondTransfer.MainLoop.Framer( readNextChunk ,readNextChunkAndContinue ,readLength ,Framer ,LengthCallback ) where import Control.Monad.Trans.Class (lift) import Control.Monad.IO.Class (MonadIO -- , liftIO ) import qualified Data.ByteString as B import qualified Data.ByteString.Lazy as LB import Data.Conduit import Data.Monoid (mappend, mempty) type Framer m = LB.ByteString -- Input left overs -> m B.ByteString -- Generator -> Maybe Int -- Length to read, if we know now -> m (LB.ByteString, LB.ByteString) -- To yield, left-overs... -- * Doing it by parts type LengthCallback = B.ByteString -> Maybe Int readNextChunk :: Monad m => LengthCallback -- ^ How to know if we can split somewhere -> B.ByteString -- ^ Input left-overs -> m B.ByteString -- ^ Generator action -> Source m B.ByteString -- ^ Packet and leftovers, if we could get them readNextChunk length_callback input_leftovers gen = do let maybe_length = length_callback input_leftovers readUpTo_ lo the_length | (B.length lo) >= the_length = return $ B.splitAt the_length lo readUpTo_ lo the_length = do frag <- lift gen readUpTo_ (lo `mappend` frag) the_length case maybe_length of Just the_length -> do -- Just need to read the rest .... (package_bytes, newnewleftovers) <- readUpTo_ input_leftovers the_length yield package_bytes readNextChunk length_callback newnewleftovers gen Nothing -> do -- Read a bit more new_fragment <- lift gen let new_leftovers = input_leftovers `mappend` new_fragment readNextChunk length_callback new_leftovers gen -- readNextChunkAndContinue :: Monad m => LengthCallback -- ^ How to know if we can split somewhere -> B.ByteString -- ^ Input left-overs -> m B.ByteString -- ^ Generator action -> m (B.ByteString, B.ByteString) -- ^ Packet bytes and left-overs. readNextChunkAndContinue length_callback input_leftovers gen = do let maybe_length = length_callback input_leftovers case maybe_length of Just the_length -> do -- Just need to read the rest .... (package_bytes, newnewleftovers) <- readUpTo gen input_leftovers the_length return (package_bytes, newnewleftovers) Nothing -> do -- Read a bit more new_fragment <- gen let new_leftovers = input_leftovers `mappend` new_fragment readNextChunkAndContinue length_callback new_leftovers gen readUpTo :: Monad m => m B.ByteString -> B.ByteString -> Int -> m (B.ByteString, B.ByteString) readUpTo _ lo the_length | (B.length lo) >= the_length = return $ B.splitAt the_length lo readUpTo gen lo the_length = do frag <- gen readUpTo gen (lo `mappend` frag) the_length -- Some protocols, e.g., http/2, have the client transmit a fixed-length -- prefix. This function reads both that prefix and returns whatever get's -- trapped up there.... readLength :: MonadIO m => Int -> m B.ByteString -> m (B.ByteString, B.ByteString) readLength the_length gen = readUpTo_ mempty where readUpTo_ lo | (B.length lo) >= the_length = do -- liftIO $ putStrLn "Full read" return $ B.splitAt the_length lo | otherwise = do -- liftIO $ putStrLn $ "fragment read " ++ (show lo) frag <- gen readUpTo_ (lo `mappend` frag)