sqd
sqd

Reputation: 1535

Haskell conduit: How to send data downstream while producing a monadic value

I want to write a proxy, which would receive a header that indicates the destination IP, port, etc.

So I have something like this:

getHeader = do
    Just x <- await
    let (a, rest) = splitAt headerLen x
    return $ parseHeader a
    -- how to send the rest data downstream ??

(resume, header) <- clientSource $$+ getHeader
-- do something according to the header

The problem is that, sometimes the header and the subsequent data are sent together, and therefore getHeader consumes the subsequent data, which is supposed to be consumed by later conduits. So how can I send rest downstream?

Upvotes: 2

Views: 122

Answers (1)

Petr
Petr

Reputation: 63359

You could also consider to use a parser wrapped into a conduit, for example Data.Conduit.Attoparsec in conduit-extra. The wrapper takes care of requesting as many input pieces as needed (in your case it can happen that the first piece is shorter than headerLen), and also of taking care of leftovers:

import Control.Monad.Catch
import qualified Data.Attoparsec.ByteString as P
import Data.Attoparsec.Types
import qualified Data.ByteString as BS
import Data.Conduit
import Data.Conduit.Attoparsec


parseHeader :: Parser BS.ByteString BS.ByteString
parseHeader = P.take headerLen -- do whatever parsing you need to get the header
  where 
    headerLen = 42

consumerHeader :: (MonadThrow m) => Consumer BS.ByteString m BS.ByteString
consumerHeader = sinkParser parseHeader

Consumer is defined as

type Consumer i m r = forall o. ConduitM i o m r

so consumerHeader takes ByteString as input, is polymorphic in the output as well as the monad, and returns the parsed ByteString.

Upvotes: 1

Related Questions