network-service (empty) → 0.1.0.0
raw patch · 6 files changed
+248/−0 lines, 6 filesdep +basedep +base64-bytestringdep +bytestringsetup-changed
Dependencies added: base, base64-bytestring, bytestring, hscolour, network, network-simple
Files
- Example/Echo.hs +77/−0
- LICENSE +20/−0
- Network/Service.hs +42/−0
- Network/Transport/Encoding/Base64.hs +46/−0
- Setup.hs +2/−0
- network-service.cabal +61/−0
+ Example/Echo.hs view
@@ -0,0 +1,77 @@+{-# LANGUAGE Rank2Types #-}+--------------------------------------------------------------------------------+-- |+-- Module : Example.Echo+-- Copyright : (c) Moritz Angermann 2014+-- License : MIT+--+-- Maintainer : moritz@lichtzwerge.de+-- Stability : stable+-- Portability : portable+--+-- An echo service on port 2000+--+-- To start the server run+-- > $ echo server+--+-- You can then run the client+-- > $ echo client+--+-- Or connect to the server with telnet+-- > $ telnet localhost 2000+--+--------------------------------------------------------------------------------+module Main (main) where+import Network.Simple.TCP+import Data.ByteString.Char8 ( pack, unpack )+import System.Environment ( getProgName, getArgs )+import Control.Monad ( unless )++import Network.Service+import Network.Transport.Encoding.Base64 (mkService)++-- | Make a service server running on the executing host.+mkServiceServer :: ServiceMessage a+ => ServiceName -- ^ the port+ -> ServiceHandler a -- ^ a handler to handle connections.+ -> IO ()+mkServiceServer port shandler = serve HostAny port handler+ where handler :: (Socket, SockAddr) -> IO ()+ handler x = mkService x >>= shandler++-- | Make a service client+mkServiceClient :: ServiceMessage a+ => HostName -- ^ The hostname that provides a 'Service a'.+ -> ServiceName -- ^ The port the service runs at.+ -> IO (Service a) -- ^ The client. (Service interface)+mkServiceClient h p = connect h p mkService++-- | We use a simple data type for communication.+-- All it holds is a simple string.+data Message = Msg String++-- | Turning a String message into a bytestring.+-- Trivially through pack and unpack.+instance ServiceMessage Message where+ toBS (Msg s) = pack s+ fromBS = Msg . unpack++main :: IO ()+main = do+ args <- getArgs+ prog <- getProgName+ + case args of+ ["server"] -> mkServiceServer "2000" handler+ ["client"] -> do+ service <- mkServiceClient "localhost" "2000"+ let msg = "Hello World!"+ putStrLn $ "[Client] Sending: " ++ msg+ sSend service $ Msg msg+ Msg resp <- sRecv service+ putStrLn $ "[Client] Received: " ++ resp+ sTerm service+ _ -> putStrLn $ "Usage: " ++ prog ++ " (server|client)"++ where handler :: ServiceHandler Message+ handler service = sRecv service >>= sSend service >> sDone service >>= flip unless (handler service)
+ LICENSE view
@@ -0,0 +1,20 @@+Copyright (c) 2014 Moritz Angermann++Permission is hereby granted, free of charge, to any person obtaining+a copy of this software and associated documentation files (the+"Software"), to deal in the Software without restriction, including+without limitation the rights to use, copy, modify, merge, publish,+distribute, sublicense, and/or sell copies of the Software, and to+permit persons to whom the Software is furnished to do so, subject to+the following conditions:++The above copyright notice and this permission notice shall be included+in all copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ Network/Service.hs view
@@ -0,0 +1,42 @@+{-# LANGUAGE Rank2Types #-}+--------------------------------------------------------------------------------+-- |+-- Module : Network.Service+-- Copyright : (c) Moritz Angermann 2014+-- License : MIT+--+-- Maintainer : moritz@lichtzwerge.de+-- Stability : stable+-- Portability : portable+--+-- A service is an endpoint that can receive and send messages.+--+--------------------------------------------------------------------------------+module Network.Service where++import Data.ByteString (ByteString)++-- | Services operate on Messages, the ServiceMessage class abstracts+-- the serialization.+class ServiceMessage a where+ toBS :: a -> ByteString -- ^ serialization to ByteString representation+ fromBS :: ByteString -> a -- ^ deserialization from ByteString representation++-- | A Service of a certain Message data.+data Service a = Service+ { sDone :: IO Bool -- ^ tells whether the service is done or not.+ , sRecv :: IO a -- ^ receive a message.+ , sSend :: a -> IO () -- ^ send a message.+ , sTerm :: IO () -- ^ terminate the service.+ }+-- | A handler takes a service and returns an IO action.+-- The idea is that a service handler will 'run' a service+--+-- a simple echo handler might look like the following:+--+-- > handler service = do { msg <- sRecv service+-- > ; sSend service msg+-- > ; isDone <- sDone service+-- > ; unless isDone (handler service)+-- > }+type ServiceHandler a = Service a -> IO ()
+ Network/Transport/Encoding/Base64.hs view
@@ -0,0 +1,46 @@+--------------------------------------------------------------------------------+-- |+-- Module : Network.Transport.Encoding.Base64+-- Copyright : (c) Moritz Angermann 2014+-- License : MIT+--+-- Maintainer : moritz@lichtzwerge.de+-- Stability : stable+-- Portability : portable+--+-- A trivial service that uses base64 as encoding for the+-- messages and newlines for message separation.+--------------------------------------------------------------------------------+module Network.Transport.Encoding.Base64 (mkService) where+import Network.Socket (Socket, SockAddr, socketToHandle)+import System.IO (hSetBuffering, hGetContents, hPutStrLn, hClose+ ,IOMode( ReadWriteMode ), BufferMode( LineBuffering ))+import Control.Concurrent (newMVar, modifyMVar)+import Data.ByteString.Base64 (encode, decode)+import Data.ByteString.Char8 (pack, unpack)+import Data.ByteString (ByteString)++import Network.Service++-- hack.+decode' :: ByteString -> ByteString+decode' bs = let Right res = decode bs in res++-- | Builds a simple service, that uses base64 as the base+-- encoding for the messages. Messages are separated by+-- newlines.+mkService :: ServiceMessage a+ => (Socket, SockAddr) -- ^ The socket and socket address to the service is bound on.+ -> IO (Service a) -- ^ The service to be used.+mkService (sock, addr) = do+ hdl <- socketToHandle sock ReadWriteMode+ hSetBuffering hdl LineBuffering+ messages <- lines `fmap` (hGetContents hdl)+ messageMVar <- newMVar messages+ return $ Service { sDone = modifyMVar messageMVar+ (\ms -> return (ms, ms == []))+ , sRecv = modifyMVar messageMVar+ (\ms -> return (tail ms, fromBS . decode' . pack $ head ms))+ , sSend = hPutStrLn hdl . unpack . encode . toBS+ , sTerm = hClose hdl+ }
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ network-service.cabal view
@@ -0,0 +1,61 @@+-- Initial network-service.cabal generated by cabal init. For further +-- documentation, see http://haskell.org/cabal/users-guide/++name: network-service+version: 0.1.0.0+synopsis: Provide a service at the data type level.+description: Thin very layer over network-simple, that allows to write+ a service that can be communicated with at the data level.+ Makes it trivial to build a services upon a data type,+ provided a ByteString serialization can be given.+homepage: https://github.com/angerman/network-service+license: MIT+license-file: LICENSE+author: Moritz Angermann+maintainer: moritz@lichtzwerge.de+copyright: 2014 Moritz Angermann+category: Network+build-type: Simple+-- extra-source-files: +cabal-version: >=1.10++flag example+ description: Build example executables to showcase the library+ default: False++flag documentation+ default: False++source-repository head+ type: git+ location: https://github.com/angerman/network-service.git++library+ if flag(documentation)+ build-depends: hscolour+ exposed-modules: Network.Service, Network.Transport.Encoding.Base64+ -- other-modules: + other-extensions: Rank2Types+ build-depends: base >=4.7 && <4.8+ , network >=2.6 && <2.7+ , bytestring >=0.10 && <0.11+ , base64-bytestring >=1.0 && <1.1+ -- hs-source-dirs: + default-language: Haskell2010++executable echo+ if flag(example)+ Buildable: True+ else+ Buildable: False+ + main-is: Example/Echo.hs+ other-modules: Network.Service, Network.Transport.Encoding.Base64+ -- other-extensions:+ build-depends: base >=4.7 && <4.8+ , network >=2.6 && <2.7+ , network-simple >= 0.4 && <0.5+ , bytestring >=0.10 && <0.11+ , base64-bytestring >=1.0 && <1.1+ -- hs-source-dirs:+ default-language: Haskell2010