network-enumerator 0.1.1 → 0.1.2
raw patch · 2 files changed
+87/−32 lines, 2 files
Files
- Network/Socket/Enumerator.hs +86/−31
- network-enumerator.cabal +1/−1
Network/Socket/Enumerator.hs view
@@ -11,33 +11,35 @@ module Network.Socket.Enumerator ( enumSocket , enumSocketFrom+ , enumSocketTimed , iterSocket , iterSocketTo+ , iterSocketTimed ) where import qualified Control.Exception as Exc-import Control.Monad.IO.Class (MonadIO, liftIO)+import Control.Monad.IO.Class (MonadIO, liftIO) import qualified Data.ByteString as B-import Data.Enumerator ((>>==))+import Data.Enumerator ((>>==)) import qualified Data.Enumerator as E import qualified Network.Socket as S-import Network.Socket.ByteString (sendMany, sendManyTo, recv, recvFrom)+import Network.Socket.ByteString (sendMany, sendManyTo, recv, recvFrom)+import System.Timeout (timeout) -- | Enumerate binary data from a 'S.Socket', using 'recv'. The socket -- must be connected.+--+-- The buffer size should be a small power of 2, such as 4096. enumSocket :: MonadIO m => Integer -- ^ Buffer size -> S.Socket -> E.Enumerator B.ByteString m b-enumSocket bufferSize sock = loop where- intSize = fromInteger bufferSize- - loop (E.Continue k) = do- bytes <- try (recv sock intSize)- if B.null bytes- then E.continue k- else k (E.Chunks [bytes]) >>== loop+enumSocket bufferSize sock = enumLoop $ \loop k -> do+ let intSize = fromInteger bufferSize - loop step = E.returnI step+ bytes <- try (recv sock intSize)+ if B.null bytes+ then E.continue k+ else k (E.Chunks [bytes]) >>== loop -- | Enumerate binary data from a 'S.Socket', using 'recvFrom'. The socket -- does not have to be connected. Each chunk of data received will be paired@@ -46,40 +48,93 @@ => Integer -- ^ Buffer size -> S.Socket -> E.Enumerator (B.ByteString, S.SockAddr) m b-enumSocketFrom bufferSize sock = loop where- intSize = fromInteger bufferSize+enumSocketFrom bufferSize sock = enumLoop $ \loop k -> do+ let intSize = fromInteger bufferSize - loop (E.Continue k) = do- (bytes, addr) <- try (recvFrom sock intSize)- if B.null bytes- then E.continue k- else k (E.Chunks [(bytes, addr)]) >>== loop+ (bytes, addr) <- try (recvFrom sock intSize)+ if B.null bytes+ then E.continue k+ else k (E.Chunks [(bytes, addr)]) >>== loop++-- | Enumerate binary data from a 'Socket', using 'recv'. The socket must+-- be connected.+--+-- The buffer size should be a small power of 2, such as 4096.+--+-- If any call to 'recv' takes longer than the timeout, 'enumSocketTimed'+-- will throw an error. To add a timeout for the entire session, wrap the+-- call to 'E.run' in 'timeout'.+--+-- Since: 0.1.2+enumSocketTimed :: MonadIO m+ => Integer -- ^ Buffer size+ -> Integer -- ^ Timeout, in microseconds+ -> S.Socket+ -> E.Enumerator B.ByteString m b+enumSocketTimed bufferSize maxWait sock = enumLoop $ \loop k -> do+ let intSize = fromInteger bufferSize+ intWait = fromInteger maxWait+ timedOut = Exc.ErrorCall "enumSocketTimed: timeout exceeded" - loop step = E.returnI step+ tried <- try (timeout intWait (recv sock intSize))+ case tried of+ Nothing -> E.throwError timedOut+ Just bytes -> if B.null bytes+ then E.continue k+ else k (E.Chunks [bytes]) >>== loop -- | Write data to a 'S.Socket', using 'sendMany'. The socket must be connected. iterSocket :: MonadIO m => S.Socket -> E.Iteratee B.ByteString m ()-iterSocket sock = E.continue step where- step E.EOF = E.yield () E.EOF- step (E.Chunks []) = E.continue step- step (E.Chunks xs) = try (sendMany sock xs) >> E.continue step+iterSocket sock = foldMany (\xs -> try (sendMany sock xs)) -- | Write data to a 'S.Socket', using 'sendManyTo'. The socket does not -- have to be connected. iterSocketTo :: MonadIO m- => S.Socket- -> S.SockAddr- -> E.Iteratee B.ByteString m ()-iterSocketTo sock addr = E.continue step where- step E.EOF = E.yield () E.EOF- step (E.Chunks []) = E.continue step- step (E.Chunks xs) = try (sendManyTo sock xs addr) >> E.continue step+ => S.Socket+ -> S.SockAddr+ -> E.Iteratee B.ByteString m ()+iterSocketTo sock addr = foldMany (\xs -> try (sendManyTo sock xs addr)) +-- | Write data to a 'S.Socket', using 'sendMany'. The socket must be connected.+--+-- If any call to 'sendMany' takes longer than the timeout, 'iterSocketTimed'+-- will throw an error. To add a timeout for the entire session, wrap the+-- call to 'E.run' in 'timeout'.+--+-- Since: 0.1.2+iterSocketTimed :: MonadIO m+ => Integer -- ^ Timeout, in microseconds+ -> S.Socket+ -> E.Iteratee B.ByteString m ()+iterSocketTimed maxWait sock = foldMany $ \xs -> do+ let intWait = fromInteger maxWait+ timedOut = Exc.ErrorCall "iterSocketTimed: timeout exceeded"+ + tried <- try (timeout intWait (sendMany sock xs))+ case tried of+ Nothing -> E.throwError timedOut+ Just _ -> return ()+ try :: MonadIO m => IO b -> E.Iteratee a m b try io = do tried <- liftIO (Exc.try io) case tried of Left err -> E.throwError (err :: Exc.SomeException) Right b -> return b++enumLoop :: Monad m+ => ((E.Step a m b -> E.Iteratee a m b)+ -> (E.Stream a -> E.Iteratee a m b)+ -> E.Iteratee a m b)+ -> E.Enumerator a m b+enumLoop iter = loop where+ loop (E.Continue k) = iter loop k+ loop step = E.returnI step++foldMany :: Monad m => ([a] -> E.Iteratee a m b) -> E.Iteratee a m ()+foldMany f = E.continue step where+ step E.EOF = E.yield () E.EOF+ step (E.Chunks []) = E.continue step+ step (E.Chunks xs) = f xs >> E.continue step
network-enumerator.cabal view
@@ -1,5 +1,5 @@ name: network-enumerator-version: 0.1.1+version: 0.1.2 synopsis: Enumerators for network sockets license: MIT license-file: license.txt