packages feed

network-simple-sockaddr (empty) → 0.1

raw patch · 4 files changed

+241/−0 lines, 4 filesdep +basedep +bytestringdep +directorysetup-changed

Dependencies added: base, bytestring, directory, exceptions, network, transformers

Files

+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2014, Danny Navarro++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Danny Navarro nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ network-simple-sockaddr.cabal view
@@ -0,0 +1,34 @@+name:                network-simple-sockaddr+version:             0.1+cabal-version:       >=1.10+tested-with:         GHC == 7.6.3+build-type:          Simple+license:             BSD3+license-file:        LICENSE+author:              Danny Navarro+maintainer:          j@dannynavarro.net+category:            Network+homepage:            https://github.com/jdnavarro/network-simple-sockaddr+bug-reports:         https://github.com/jdnavarro/network-simple-sockaddr/issues+synopsis:            network-simple for resolved addresses+description:+  @<http://hackage.haskell.org/package/network-simple-0.3.0 network-simple>@+  works by resolving 'HostName's. This packages offers a similar API but+  working with unresolved addresses in the form of 'SockAddr'. In addition to+  'IPv4' addresses, 'IPv6' and @Unix Domain Sockets@ are also supported.++source-repository head+  type: git+  location: git@github.com:jdnavarro/network-simple-sockaddr.git++library+  exposed-modules:     Network.Simple.SockAddr+  build-depends:       base >=4.6 && <4.8,+                       bytestring,+                       directory,+                       transformers >=0.2,+                       network >=2.4,+                       exceptions >=0.3+  hs-source-dirs:      src+  default-language:    Haskell2010+  ghc-options:         -Wall
+ src/Network/Simple/SockAddr.hs view
@@ -0,0 +1,175 @@+{-# LANGUAGE RankNTypes #-}++{-| This is the same API as @network-simple@ with the difference+    of working on 'SockAddr' instead of @'HostName's@.++    For a more detailed explanation check+    @<http://hackage.haskell.org/package/network-simple-0.3.0/docs/Network-Simple-TCP.html Network.Simple.TCP>@+-}++module Network.Simple.SockAddr+  (+  -- * Client side+    connect+  , connectFork+  -- * Server side+  , serve+  , listen+  , bind+  , acceptFork+  -- * Utils+  , send+  , recv+  , close+  -- * Re-exported from @Network.Socket@+  , Socket+  , SockAddr+  ) where++import Control.Monad (forever, when)+import Control.Concurrent (ThreadId, forkIO, forkFinally)+import Data.ByteString (ByteString)+import Control.Monad.IO.Class (MonadIO, liftIO)+import System.Directory (removeFile)+import qualified Network.Socket as NS+import Network.Socket+  ( SockAddr(SockAddrInet, SockAddrInet6, SockAddrUnix)+  , SocketType(Stream)+  , Family(AF_INET, AF_INET6, AF_UNIX)+  , Socket+  , defaultProtocol+  )+import qualified Network.Socket.ByteString as NSB+import Control.Monad.Catch (MonadCatch, bracket, bracketOnError, throwM)++-- * Client side++{-| Connect to a server and use the connection.++    The connection socket is closed when done or in case of exceptions.+-}+connect :: (MonadIO m, MonadCatch m)+        => SockAddr+        -- ^ Server address.+        -> (Socket -> m r)+        -- ^ Computation taking the socket connection socket.+        -> m r+connect addr = bracket connect' (liftIO . NS.close)+  where+    connect' = do sock <- newSocket addr+                  liftIO $ NS.connect sock addr+                  return sock++-- | Like 'connect' but fork the connection in a different thread.+connectFork :: MonadIO m+            => SockAddr+            -- ^ Server address.+            -> (forall m' . (Functor m', MonadIO m', MonadCatch m')+                         => Socket -> m' ())+            -- ^ Computation taking the socket connection socket.+            -> m ThreadId+connectFork addr k = liftIO . forkIO $ connect addr k+{-# INLINABLE connectFork #-}++-- * Server side++{-| Start a server that accepts incoming connections and handles them+    concurrently in different threads.++    Any acquired network resources are properly closed and discarded when done+    or in case of exceptions.+-}+serve :: (MonadIO m, MonadCatch m)+      => SockAddr+      -- ^ Address to bind to.+      -> (forall m' . (Functor m', MonadIO m', MonadCatch m')+                   => SockAddr -> Socket -> m' ())+      -- ^ Computation to run in a different thread+      --   once an incoming connection is accepted. Takes the+      --   the remote end address and the connection socket.+      -> m ()+serve addr k = listen addr $ \sock -> forever $ acceptFork sock k++{-| Bind a listening socket and use it.++    The listening socket is closed when done or in case of exceptions.+-}+listen :: (MonadIO m, MonadCatch m)+       => SockAddr+       -- ^ Address to bind to.+       -> (Socket -> m r)+       -- ^ Computation taking the listening socket.+       -> m r+listen addr = bracket listen' (close addr)+  where+    listen' = liftIO $ do sock <- bind addr+                          NS.listen sock $ max 2048 NS.maxListenQueue+                          return sock+{-| Obtain a 'Socket' bound to the given 'SockAddr'.++   The obtained 'Socket' should be closed manually using 'close' when it's not+   needed anymore.++   Prefer to use 'listen' if you will be listening on this socket and using it+   within a limited scope, and would like it to be closed immediately after its+   usage or in case of exceptions.+-}+bind :: (MonadIO m, MonadCatch m) => SockAddr -> m Socket+bind addr = bracketOnError (newSocket addr) (close addr)+          $ \sock -> liftIO $ do+                let set so n = when (NS.isSupportedSocketOption so)+                                    (NS.setSocketOption sock so n)+                when (isTCP addr) (set NS.NoDelay 1)+                set NS.ReuseAddr 1+                NS.bindSocket sock addr+                return sock+  where+    isTCP (SockAddrUnix {}) = False+    isTCP _                 = True++{-| Accept a single incoming connection and use it in a different thread.++    The connection socket is closed when done or in case of exceptions.+-}+acceptFork :: (MonadIO m, MonadCatch m)+           => Socket+           -- ^ Listening and bound socket.+           -> (forall m' . (Functor m', MonadIO m', MonadCatch m')+                        => SockAddr -> Socket -> m' ())+           -- ^ Computation to run in a different thread+           --   once an incoming connection is accepted. Takes the+           --   remote end address and connection socket.+           -> m ThreadId+acceptFork lsock k = liftIO $ do+    (csock,caddr) <- NS.accept lsock+    forkFinally (k caddr csock)+                (\ea -> NS.close csock >> either throwM return ea)+{-# INLINABLE acceptFork #-}++-- * Utils++-- | Writes the given bytes to the socket.+send :: MonadIO m => Socket -> ByteString -> m ()+send sock bs = liftIO $ NSB.sendAll sock bs+{-# INLINE send #-}++-- | Read up to a limited number of bytes from a socket.+recv :: MonadIO m => Socket -> Int -> m ByteString+recv sock n = liftIO $ NSB.recv sock n+{-# INLINE recv #-}++-- | Close the 'Socket' and unlinks the 'SockAddr' for Unix sockets.+close :: MonadIO m => SockAddr -> Socket -> m ()+close (SockAddrUnix path) sock = liftIO $ NS.close sock >> removeFile path+close _                   sock = liftIO $ NS.close sock+{-# INLINE close #-}++-- * Internal++newSocket :: MonadIO m => SockAddr -> m Socket+newSocket addr = liftIO $ NS.socket (fam addr) Stream defaultProtocol+  where+    fam (SockAddrInet  {}) = AF_INET+    fam (SockAddrInet6 {}) = AF_INET6+    fam (SockAddrUnix  {}) = AF_UNIX+{-# INLINABLE newSocket #-}