packages feed

network-conduit 0.2.0 → 0.2.1

raw patch · 2 files changed

+123/−2 lines, 2 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

+ Data.Conduit.Network: ClientSettings :: Int -> String -> ClientSettings
+ Data.Conduit.Network: ServerSettings :: Int -> Maybe String -> ServerSettings
+ Data.Conduit.Network: bindPort :: Maybe String -> Int -> IO Socket
+ Data.Conduit.Network: clientHost :: ClientSettings -> String
+ Data.Conduit.Network: clientPort :: ClientSettings -> Int
+ Data.Conduit.Network: data ClientSettings
+ Data.Conduit.Network: data ServerSettings
+ Data.Conduit.Network: getSocket :: String -> Int -> IO Socket
+ Data.Conduit.Network: runTCPClient :: ClientSettings -> Application -> IO ()
+ Data.Conduit.Network: runTCPServer :: ServerSettings -> Application -> IO ()
+ Data.Conduit.Network: serverHost :: ServerSettings -> Maybe String
+ Data.Conduit.Network: serverPort :: ServerSettings -> Int
+ Data.Conduit.Network: type Application = Source IO ByteString -> Sink ByteString IO () -> ResourceT IO ()

Files

Data/Conduit/Network.hs view
@@ -1,14 +1,31 @@+{-# LANGUAGE ScopedTypeVariables #-} module Data.Conduit.Network-    ( sourceSocket+    ( -- * Basic utilities+      sourceSocket     , sinkSocket+      -- * Simple TCP server/client interface.+    , Application+      -- ** Server+    , ServerSettings (..)+    , runTCPServer+      -- ** Client+    , ClientSettings (..)+    , runTCPClient+      -- * Helper utilities+    , bindPort+    , getSocket     ) where  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 Data.ByteString as S import Control.Monad.IO.Class (liftIO)+import Control.Exception (bracketOnError, IOException, bracket, throwIO, SomeException, try)+import Control.Monad (forever)+import Control.Concurrent (forkIO)  -- | Stream data from the socket. --@@ -39,3 +56,107 @@         liftIO (sendAll socket bs)         return (Processing push close)     close = return ()++-- | A simple TCP application. It takes two arguments: the @Source@ to read+-- input data from, and the @Sink@ to send output data to.+--+-- Since 0.2.1+type Application = Source IO ByteString+                -> Sink ByteString IO ()+                -> ResourceT IO ()++-- | Settings for a TCP server. It takes a port to listen on, and an optional+-- hostname to bind to.+--+-- Since 0.2.1+data ServerSettings = ServerSettings+    { serverPort :: Int+    , serverHost :: Maybe String -- ^ 'Nothing' indicates no preference+    }++-- | Run an @Application@ with the given settings. This function will create a+-- new listening socket, accept connections on it, and spawn a new thread for+-- each connection.+--+-- Since 0.2.1+runTCPServer :: ServerSettings -> Application -> IO ()+runTCPServer (ServerSettings port host) app = bracket+    (bindPort host port)+    NS.sClose+    (forever . serve)+  where+    serve lsocket = do+        (socket, _addr) <- NS.accept lsocket+        forkIO $ runResourceT $ app (sourceSocket socket) (sinkSocket socket)++-- | Settings for a TCP client, specifying how to connect to the server.+data ClientSettings = ClientSettings+    { clientPort :: Int+    , clientHost :: String+    }++-- | Run an @Application@ by connecting to the specified server.+--+-- Since 0.2.1+runTCPClient :: ClientSettings -> Application -> IO ()+runTCPClient (ClientSettings port host) app = do+    socket <- getSocket host port+    runResourceT $ app (sourceSocket socket) (sinkSocket socket)++-- | Attempt to connect to the given host/port.+--+-- Since 0.2.1+getSocket :: String -> Int -> IO NS.Socket+getSocket host' port' = do+    let hints = NS.defaultHints {+                          NS.addrFlags = [NS.AI_ADDRCONFIG]+                        , NS.addrSocketType = NS.Stream+                        }+    (addr:_) <- NS.getAddrInfo (Just hints) (Just host') (Just $ show port')+    sock <- NS.socket (NS.addrFamily addr) (NS.addrSocketType addr)+                      (NS.addrProtocol addr)+    ee <- try' $ NS.connect sock (NS.addrAddress addr)+    case ee of+        Left e -> NS.sClose sock >> throwIO e+        Right () -> return sock+  where+    try' :: IO a -> IO (Either SomeException a)+    try' = try++-- | Attempt to bind a listening @Socket@ on the given host/port. If no host is+-- given, will use the first address available.+--+-- Since 0.2.1+bindPort :: Maybe String -> Int -> IO Socket+bindPort host p = do+    let hints = NS.defaultHints+            { NS.addrFlags =+                [ NS.AI_PASSIVE+                , NS.AI_NUMERICSERV+                , NS.AI_NUMERICHOST+                ]+            , NS.addrSocketType = NS.Stream+            }+        port = Just . show $ p+    addrs <- NS.getAddrInfo (Just hints) host port+    let+        tryAddrs (addr1:rest@(_:_)) =+                                      catch+                                      (theBody addr1)+                                      (\(_ :: IOException) -> tryAddrs rest)+        tryAddrs (addr1:[])         = theBody addr1+        tryAddrs _                  = error "bindPort: addrs is empty"+        theBody addr =+          bracketOnError+          (NS.socket+            (NS.addrFamily addr)+            (NS.addrSocketType addr)+            (NS.addrProtocol addr))+          NS.sClose+          (\sock -> do+              NS.setSocketOption sock NS.ReuseAddr 1+              NS.bindSocket sock (NS.addrAddress addr)+              NS.listen sock NS.maxListenQueue+              return sock+          )+    tryAddrs addrs
network-conduit.cabal view
@@ -1,5 +1,5 @@ Name:                network-conduit-Version:             0.2.0+Version:             0.2.1 Synopsis:            Stream socket data using conduits. Description:         Stream socket data using conduits. License:             BSD3