{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE CPP #-} module Data.Conduit.Network ( -- * Basic utilities sourceSocket , sinkSocket -- * Simple TCP server/client interface. , SN.AppData , appSource , appSink , SN.appSockAddr , SN.appLocalAddr -- ** Server , SN.ServerSettings , serverSettings , SN.runTCPServer , SN.runTCPServerWithHandle , forkTCPServer , runGeneralTCPServer -- ** Client , SN.ClientSettings , clientSettings , SN.runTCPClient , runGeneralTCPClient -- ** Getters , SN.getPort , SN.getHost , SN.getAfterBind , SN.getNeedLocalAddr -- ** Setters , SN.setPort , SN.setHost , SN.setAfterBind , SN.setNeedLocalAddr -- * Types , SN.HostPreference ) where import Prelude hiding (catch) import Data.Conduit import qualified Network.Socket as NS import Network.Socket (Socket) import Network.Socket.ByteString (sendAll, recv) import Data.ByteString (ByteString) import qualified GHC.Conc as Conc (yield) import qualified Data.ByteString as S import qualified Data.ByteString.Char8 as S8 import Control.Monad.IO.Class (MonadIO (liftIO)) import Control.Exception (throwIO, SomeException, try, finally, bracket, IOException, catch) import Control.Monad (unless, void) import Control.Monad.Trans.Control (MonadBaseControl, control, liftBaseWith) import Control.Monad.Trans.Class (lift) import Control.Concurrent (forkIO, newEmptyMVar, putMVar, takeMVar, MVar, ThreadId) import qualified Data.Streaming.Network as SN -- | Stream data from the socket. -- -- This function does /not/ automatically close the socket. -- -- Since 0.0.0 sourceSocket :: MonadIO m => Socket -> Producer m ByteString sourceSocket socket = loop where loop = do bs <- lift $ liftIO $ SN.safeRecv socket 4096 if S.null bs then return () else yield bs >> loop -- | Stream data to the socket. -- -- This function does /not/ automatically close the socket. -- -- Since 0.0.0 sinkSocket :: MonadIO m => Socket -> Consumer ByteString m () sinkSocket socket = loop where loop = await >>= maybe (return ()) (\bs -> lift (liftIO $ sendAll socket bs) >> loop) serverSettings = SN.serverSettingsTCP clientSettings = SN.clientSettingsTCP appSource :: (SN.HasReadWrite ad, MonadIO m) => ad -> Producer m ByteString appSource ad = loop where read' = SN.appRead ad loop = do bs <- liftIO read' unless (S.null bs) $ do yield bs loop appSink :: (SN.HasReadWrite ad, MonadIO m) => ad -> Consumer ByteString m () appSink ad = awaitForever $ \d -> liftIO $ SN.appWrite ad d >> Conc.yield addBoundSignal::MVar ()-> SN.ServerSettings -> SN.ServerSettings addBoundSignal isBound set = SN.setAfterBind ( \socket -> originalAfterBind socket >> signalBound socket) set where originalAfterBind :: Socket -> IO () originalAfterBind = SN.getAfterBind set signalBound :: Socket -> IO () signalBound _socket = putMVar isBound () -- | Fork a TCP Server -- -- Will fork the runGeneralTCPServer function but will only return from -- this call when the server is bound to the port and accepting incoming -- connections. Will return the thread id of the server -- -- Since 1.1.4 forkTCPServer :: MonadBaseControl IO m => SN.ServerSettings -> (SN.AppData -> m ()) -> m ThreadId forkTCPServer set f = liftBaseWith $ \run -> do isBound <- newEmptyMVar let setWithWaitForBind = addBoundSignal isBound set threadId <- forkIO . void . run $ runGeneralTCPServer setWithWaitForBind f takeMVar isBound return threadId -- | Run a general TCP server -- -- Same as 'SN.runTCPServer', except monad can be any instance of -- 'MonadBaseControl' 'IO'. -- -- Note that any changes to the monadic state performed by individual -- client handlers will be discarded. If you have mutable state you want -- to share among multiple handlers, you need to use some kind of mutable -- variables. -- -- Since 1.1.3 runGeneralTCPServer :: MonadBaseControl IO m => SN.ServerSettings -> (SN.AppData -> m ()) -> m a runGeneralTCPServer set f = liftBaseWith $ \run -> SN.runTCPServer set $ void . run . f -- | Run a general TCP client -- -- Same as 'SN.runTCPClient', except monad can be any instance of 'MonadBaseControl' 'IO'. -- -- Since 1.1.3 runGeneralTCPClient :: MonadBaseControl IO m => SN.ClientSettings -> (SN.AppData -> m a) -> m a runGeneralTCPClient set f = control $ \run -> SN.runTCPClient set $ run . f