msgpack-rpc 0.6.1.2 → 0.7.0
raw patch · 6 files changed
+283/−301 lines, 6 filesdep +attoparsec-conduitdep +conduitdep +textdep −attoparsec-enumeratordep −enumeratordep ~deepseqdep ~msgpack
Dependencies added: attoparsec-conduit, conduit, text
Dependencies removed: attoparsec-enumerator, enumerator
Dependency ranges changed: deepseq, msgpack
Files
- Network/MessagePackRpc/Client.hs +139/−0
- Network/MessagePackRpc/Server.hs +118/−0
- msgpack-rpc.cabal +23/−27
- src/Network/MessagePackRpc/Client.hs +0/−144
- src/Network/MessagePackRpc/Server.hs +0/−123
- test/Cli.hs +3/−7
+ Network/MessagePackRpc/Client.hs view
@@ -0,0 +1,139 @@+{-# Language DeriveDataTypeable #-}++-------------------------------------------------------------------+-- |+-- Module : Network.MessagePackRpc.Client+-- Copyright : (c) Hideyuki Tanaka, 2010+-- License : BSD3+--+-- Maintainer: tanaka.hideyuki@gmail.com+-- Stability : experimental+-- Portability: portable+--+-- This module is client library of MessagePack-RPC.+-- The specification of MessagePack-RPC is at <http://redmine.msgpack.org/projects/msgpack/wiki/RPCProtocolSpec>.+--+-- A simple example:+--+-- >import Network.MessagePackRpc.Client+-- >+-- >add :: RpcMethod (Int -> Int -> IO Int)+-- >add = method "add"+-- >+-- >main = do+-- > conn <- connect "127.0.0.1" 1234+-- > print =<< add conn 123 456+--+--------------------------------------------------------------------++module Network.MessagePackRpc.Client (+ -- * RPC connection+ Connection,+ connect,+ disconnect,+ + -- * RPC error+ RpcError(..),+ + -- * Call RPC method+ RpcMethod,+ call,+ method,+ ) where++import Control.Concurrent.MVar+import Control.Exception+import Control.Monad+import qualified Data.ByteString.Lazy as BL+import qualified Data.Conduit as C+import qualified Data.Conduit.Binary as CB+import qualified Data.Conduit.Attoparsec as CA+import Data.Functor+import Data.MessagePack+import Data.Typeable+import Network+import System.IO+import System.Random++-- | RPC connection type+data Connection+ = Connection+ { connHandle :: MVar Handle }++-- | Connect to RPC server+connect :: String -- ^ Host name+ -> Int -- ^ Port number+ -> IO Connection -- ^ Connection+connect addr port = withSocketsDo $ do+ h <- connectTo addr (PortNumber $ fromIntegral port)+ mh <- newMVar h+ return $ Connection+ { connHandle = mh+ }++-- | Disconnect a connection+disconnect :: Connection -> IO ()+disconnect Connection { connHandle = mh } =+ hClose =<< takeMVar mh++-- | RPC error type+data RpcError+ = ServerError Object -- ^ Server error+ | ResultTypeError String -- ^ Result type mismatch+ | ProtocolError String -- ^ Protocol error+ deriving (Eq, Ord, Typeable)++instance Exception RpcError++instance Show RpcError where+ show (ServerError err) =+ "server error: " ++ show err+ show (ResultTypeError err) =+ "result type error: " ++ err+ show (ProtocolError err) =+ "protocol error: " ++ err++class RpcType r where+ rpcc :: Connection -> String -> [Object] -> r++fromObject' :: OBJECT o => Object -> o+fromObject' o =+ case tryFromObject o of+ Left err -> throw $ ResultTypeError err+ Right r -> r++instance OBJECT o => RpcType (IO o) where+ rpcc c m args = return . fromObject' =<< rpcCall c m (reverse args)++instance (OBJECT o, RpcType r) => RpcType (o -> r) where+ rpcc c m args arg = rpcc c m (toObject arg:args)++rpcCall :: Connection -> String -> [Object] -> IO Object+rpcCall Connection{ connHandle = mh } m args = withMVar mh $ \h -> do+ msgid <- (`mod`2^(30::Int)) <$> randomIO :: IO Int+ BL.hPutStr h $ pack (0 ::Int, msgid, m, args)+ hFlush h+ C.runResourceT $ CB.sourceHandle h C.$$ do+ (rtype, rmsgid, rerror, rresult) <- CA.sinkParser get+ when (rtype /= (1 :: Int)) $+ throw $ ProtocolError $ "response type is not 1 (got " ++ show rtype ++ ")"+ when (rmsgid /= msgid) $+ throw $ ProtocolError $ "message id mismatch: expect " ++ show msgid ++ ", but got " ++ show rmsgid+ case tryFromObject rerror of+ Left _ ->+ throw $ ServerError rerror+ Right () ->+ return rresult++-- | Call an RPC Method+call :: RpcType a =>+ Connection -- ^ Connection+ -> String -- ^ Method name+ -> a+call c m = rpcc c m []++-- | Create an RPC Method (call c m == method m c)+method :: RpcType a => String -> RpcMethod a+method c m = call m c++type RpcMethod a = Connection -> a
+ Network/MessagePackRpc/Server.hs view
@@ -0,0 +1,118 @@+-------------------------------------------------------------------+-- |+-- Module : Network.MessagePackRpc.Server+-- Copyright : (c) Hideyuki Tanaka, 2010+-- License : BSD3+--+-- Maintainer: tanaka.hideyuki@gmail.com+-- Stability : experimental+-- Portability: portable+--+-- This module is server library of MessagePack-RPC.+-- The specification of MessagePack-RPC is at <http://redmine.msgpack.org/projects/msgpack/wiki/RPCProtocolSpec>.+--+-- A simple example:+--+-- >import Network.MessagePackRpc.Server+-- >+-- >add :: Int -> Int -> IO Int+-- >add x y = return $ x + y+-- >+-- >main =+-- > serve 1234 [("add", fun add)]+--+--------------------------------------------------------------------++module Network.MessagePackRpc.Server (+ -- * RPC method types+ RpcMethod,+ RpcMethodType(..),+ -- * Create RPC method+ fun,+ -- * Start RPC server+ serve,+ ) where++import Control.Applicative+import Control.Concurrent+import Control.DeepSeq+import Control.Exception as E+import Control.Monad+import Control.Monad.Trans+import qualified Data.ByteString.Lazy as BL+import qualified Data.Conduit as C+import qualified Data.Conduit.Binary as CB+import qualified Data.Conduit.Attoparsec as CA+import Data.Maybe+import Data.MessagePack+import Network+import System.IO++import Prelude hiding (catch)++type RpcMethod = [Object] -> IO Object++class RpcMethodType f where+ toRpcMethod :: f -> RpcMethod++instance OBJECT o => RpcMethodType (IO o) where+ toRpcMethod m = \[] -> toObject <$> m++instance (OBJECT o, RpcMethodType r) => RpcMethodType (o -> r) where+ toRpcMethod f = \(x:xs) -> toRpcMethod (f $! fromObject' x) xs++fromObject' :: OBJECT o => Object -> o+fromObject' o =+ case tryFromObject o of+ Left err -> error $ "argument type error: " ++ err+ Right r -> r++-- | Create a RPC method from a Haskell function.+fun :: RpcMethodType f => f -> RpcMethod+fun = toRpcMethod++-- | Start RPC server with a set of RPC methods.+serve :: Int -- ^ Port number+ -> [(String, RpcMethod)] -- ^ list of (method name, RPC method)+ -> IO ()+serve port methods = withSocketsDo $ do+ sock <- listenOn (PortNumber $ fromIntegral port)+ forever $ do+ (h, host, hostport) <- accept sock+ forkIO $+ (processRequests h `finally` hClose h) `catches`+ [ Handler $ \e ->+ case e of+ CA.ParseError ["demandInput"] _ -> return ()+ _ -> hPutStrLn stderr $ host ++ ":" ++ show hostport ++ ": " ++ show e+ , Handler $ \e ->+ hPutStrLn stderr $ host ++ ":" ++ show hostport ++ ": " ++ show (e :: SomeException)]++ where+ processRequests h =+ C.runResourceT $ CB.sourceHandle h C.$$ forever $ processRequest h+ + processRequest h = do+ (rtype, msgid, method, args) <- CA.sinkParser get+ liftIO $ do+ resp <- try $ getResponse rtype method args+ case resp of+ Left err ->+ BL.hPutStr h $ pack (1 :: Int, msgid :: Int, show (err :: SomeException), ())+ Right ret ->+ BL.hPutStr h $ pack (1 :: Int, msgid :: Int, (), ret)+ hFlush h++ getResponse rtype method args = do+ when (rtype /= (0 :: Int)) $+ fail "request type is not 0"+ + r <- callMethod (method :: String) (args :: [Object])+ r `deepseq` return r+ + callMethod methodName args =+ case lookup methodName methods of+ Nothing ->+ fail $ "method '" ++ methodName ++ "' not found"+ Just method ->+ method args
msgpack-rpc.cabal view
@@ -1,42 +1,38 @@ Name: msgpack-rpc-Version: 0.6.1.2+Version: 0.7.0 Synopsis: A MessagePack-RPC Implementation-Description:- A MessagePack-RPC Implementation <http://msgpack.org/>-+Description: A MessagePack-RPC Implementation <http://msgpack.org/>+Homepage: http://msgpack.org/ License: BSD3 License-file: LICENSE-Copyright: Copyright (c) 2010, Hideyuki Tanaka-Category: Network Author: Hideyuki Tanaka Maintainer: Hideyuki Tanaka <tanaka.hideyuki@gmail.com>-Homepage: http://github.com/msgpack/msgpack-rpc-Stability: Experimental-Cabal-version: >=1.6+Copyright: Copyright (c) 2010-2011, Hideyuki Tanaka+Category: Network+Stability: Experimental+Cabal-version: >=1.8 Build-type: Simple -Extra-source-files:- test/Serv.hs- test/Cli.hs+Extra-source-files: test/Serv.hs+ test/Cli.hs Library- Build-depends: base >= 4 && < 5,- bytestring >= 0.9 && < 0.10,- network >= 2.2 && < 2.4,- random >= 1.0 && < 1.1,- enumerator >= 0.4 && < 0.5,- attoparsec-enumerator >= 0.2 && < 0.3,- msgpack >= 0.6 && < 0.7,- mtl >= 2.0 && < 2.1,- deepseq >= 1.1 && < 1.3+ Build-depends: base == 4.*+ , bytestring == 0.9.*+ , text == 0.11.*+ , network >= 2.2 && < 2.4+ , random == 1.0.*+ , mtl == 2.0.*+ , conduit == 0.2.*+ , attoparsec-conduit == 0.2.*+ , deepseq >= 1.1 && < 1.4+ , msgpack == 0.7.* Ghc-options: -Wall- Hs-source-dirs: src - Exposed-modules:- Network.MessagePackRpc.Server- Network.MessagePackRpc.Client+ Exposed-modules: Network.MessagePackRpc.Server+ Network.MessagePackRpc.Client Source-repository head- Type: git- Location: git://github.com/msgpack/msgpack-rpc.git+ Type: git+ Location: git://github.com/msgpack/msgpack-haskell.git
− src/Network/MessagePackRpc/Client.hs
@@ -1,144 +0,0 @@-{-# Language DeriveDataTypeable #-}------------------------------------------------------------------------ |--- Module : Network.MessagePackRpc.Client--- Copyright : (c) Hideyuki Tanaka, 2010--- License : BSD3------ Maintainer: tanaka.hideyuki@gmail.com--- Stability : experimental--- Portability: portable------ This module is client library of MessagePack-RPC.--- The specification of MessagePack-RPC is at <http://redmine.msgpack.org/projects/msgpack/wiki/RPCProtocolSpec>.------ A simple example:------ >import Network.MessagePackRpc.Client--- >--- >add :: RpcMethod (Int -> Int -> IO Int)--- >add = method "add"--- >--- >main = do--- > conn <- connect "127.0.0.1" 1234--- > print =<< add conn 123 456--------------------------------------------------------------------------module Network.MessagePackRpc.Client (- -- * RPC connection- Connection,- connect,- disconnect,- - -- * RPC error- RpcError(..),- - -- * Call RPC method- RpcMethod,- call,- method,- ) where--import Control.Concurrent.MVar-import Control.Exception-import Control.Monad-import Data.Attoparsec.Enumerator-import qualified Data.ByteString.Lazy as BL-import Data.Enumerator-import Data.Enumerator.Binary-import Data.Functor-import Data.MessagePack-import Data.Typeable-import Network-import System.IO-import System.Random--bufferSize :: Integer-bufferSize = 4096---- | RPC connection type-data Connection- = Connection- { connHandle :: MVar Handle }---- | Connect to RPC server-connect :: String -- ^ Host name- -> Int -- ^ Port number- -> IO Connection -- ^ Connection-connect addr port = withSocketsDo $ do- h <- connectTo addr (PortNumber $ fromIntegral port)- mh <- newMVar h- return $ Connection- { connHandle = mh- }---- | Disconnect a connection-disconnect :: Connection -> IO ()-disconnect Connection { connHandle = mh } =- hClose =<< takeMVar mh---- | RPC error type-data RpcError- = ServerError Object -- ^ Server error- | ResultTypeError String -- ^ Result type mismatch- | ProtocolError String -- ^ Protocol error- deriving (Eq, Ord, Typeable)--instance Exception RpcError--instance Show RpcError where- show (ServerError err) =- "server error: " ++ show err- show (ResultTypeError err) =- "result type error: " ++ err- show (ProtocolError err) =- "protocol error: " ++ err--class RpcType r where- rpcc :: Connection -> String -> [Object] -> r--fromObject' :: OBJECT o => Object -> o-fromObject' o =- case tryFromObject o of- Left err -> throw $ ResultTypeError err- Right r -> r--instance OBJECT o => RpcType (IO o) where- rpcc c m args = return . fromObject' =<< rpcCall c m (reverse args)--instance (OBJECT o, RpcType r) => RpcType (o -> r) where- rpcc c m args arg = rpcc c m (toObject arg:args)--rpcCall :: Connection -> String -> [Object] -> IO Object-rpcCall Connection{ connHandle = mh } m args = withMVar mh $ \h -> do- msgid <- (`mod`2^(30::Int)) <$> randomIO :: IO Int- BL.hPutStr h $ pack (0 ::Int, msgid, m, args)- hFlush h- run_ $ enumHandle bufferSize h $$ do- (rtype, rmsgid, rerror, rresult) <- iterParser get- when (rtype /= (1 :: Int)) $- throw $ ProtocolError $ "response type is not 1 (got " ++ show rtype ++ ")"- when (rmsgid /= msgid) $- throw $ ProtocolError $ "message id mismatch: expect " ++ show msgid ++ ", but got " ++ show rmsgid- case tryFromObject rerror of- Left _ ->- throw $ ServerError rerror- Right () ->- return rresult-------- | Call an RPC Method-call :: RpcType a =>- Connection -- ^ Connection- -> String -- ^ Method name- -> a-call c m = rpcc c m []---- | Create an RPC Method (call c m == method m c)-method :: RpcType a => String -> RpcMethod a-method c m = call m c--type RpcMethod a = Connection -> a
− src/Network/MessagePackRpc/Server.hs
@@ -1,123 +0,0 @@----------------------------------------------------------------------- |--- Module : Network.MessagePackRpc.Server--- Copyright : (c) Hideyuki Tanaka, 2010--- License : BSD3------ Maintainer: tanaka.hideyuki@gmail.com--- Stability : experimental--- Portability: portable------ This module is server library of MessagePack-RPC.--- The specification of MessagePack-RPC is at <http://redmine.msgpack.org/projects/msgpack/wiki/RPCProtocolSpec>.------ A simple example:------ >import Network.MessagePackRpc.Server--- >--- >add :: Int -> Int -> IO Int--- >add x y = return $ x + y--- >--- >main =--- > serve 1234 [("add", fun add)]--------------------------------------------------------------------------module Network.MessagePackRpc.Server (- -- * RPC method types- RpcMethod,- RpcMethodType(..),- -- * Create RPC method- fun,- -- * Start RPC server- serve,- ) where--import Control.Applicative-import Control.Concurrent-import Control.DeepSeq-import Control.Exception as E-import Data.Enumerator-import Data.Enumerator.Binary-import Control.Monad-import Control.Monad.Trans-import Data.Attoparsec.Enumerator-import qualified Data.ByteString.Lazy as BL-import Data.Maybe-import Data.MessagePack-import Network-import System.IO--import Prelude hiding (catch)--bufferSize :: Integer-bufferSize = 4096--type RpcMethod = [Object] -> IO Object--class RpcMethodType f where- toRpcMethod :: f -> RpcMethod--instance OBJECT o => RpcMethodType (IO o) where- toRpcMethod m = \[] -> toObject <$> m--instance (OBJECT o, RpcMethodType r) => RpcMethodType (o -> r) where- toRpcMethod f = \(x:xs) -> toRpcMethod (f $! fromObject' x) xs--fromObject' :: OBJECT o => Object -> o-fromObject' o =- case tryFromObject o of- Left err -> error $ "argument type error: " ++ err- Right r -> r-------- | Create a RPC method from a Haskell function.-fun :: RpcMethodType f => f -> RpcMethod-fun = toRpcMethod---- | Start RPC server with a set of RPC methods.-serve :: Int -- ^ Port number- -> [(String, RpcMethod)] -- ^ list of (method name, RPC method)- -> IO ()-serve port methods = withSocketsDo $ do- sock <- listenOn (PortNumber $ fromIntegral port)- forever $ do- (h, host, hostport) <- accept sock- forkIO $- (processRequests h `finally` hClose h) `catches`- [ Handler $ \e ->- case e of- ParseError ["demandInput"] _ -> return ()- _ -> hPutStrLn stderr $ host ++ ":" ++ show hostport ++ ": " ++ show e- , Handler $ \e ->- hPutStrLn stderr $ host ++ ":" ++ show hostport ++ ": " ++ show (e :: SomeException)]-- where- processRequests h =- run_ $ enumHandle bufferSize h $$ forever $ processRequest h- - processRequest h = do- (rtype, msgid, method, args) <- iterParser get- liftIO $ do- resp <- try $ getResponse rtype method args- case resp of- Left err ->- BL.hPutStr h $ pack (1 :: Int, msgid :: Int, show (err :: SomeException), ())- Right ret ->- BL.hPutStr h $ pack (1 :: Int, msgid :: Int, (), ret)- hFlush h-- getResponse rtype method args = do- when (rtype /= (0 :: Int)) $- fail "request type is not 0"- - r <- callMethod (method :: String) (args :: [Object])- r `deepseq` return r- - callMethod methodName args =- case lookup methodName methods of- Nothing ->- fail $ "method '" ++ methodName ++ "' not found"- Just method ->- method args
test/Cli.hs view
@@ -1,8 +1,5 @@ import Network.MessagePackRpc.Client -import Control.Concurrent-import Control.Monad- add :: RpcMethod (Int -> Int -> IO Int) add = method "add" @@ -11,7 +8,6 @@ main :: IO () main = do- conn <- connect "127.0.0.1" 8081- forM_ [0..10000] $ \i -> do- print =<< add conn i (i*2)- print =<< add conn (i*2) (i*3)+ conn <- connect "localhost" 8081+ print =<< add conn 123 456+ print =<< echo conn "hoge"