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 +30/−0
- Setup.hs +2/−0
- network-simple-sockaddr.cabal +34/−0
- src/Network/Simple/SockAddr.hs +175/−0
+ 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 #-}