-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | KRPC remote procedure call protocol implementation. -- -- KRPC remote procedure call protocol implementation. -- -- -- -- @package krpc @version 0.1.0.0 -- | This module provides straightforward implementation of KRPC protocol. -- In many situations KRPC should be prefered since it gives more -- safe, convenient and high level api. -- --
--   See http://www.bittorrent.org/beps/bep_0005.html#krpc-protocol
--   
module Remote.KRPC.Protocol -- | Errors used to signal that some error occurred while processing a -- procedure call. Error may be send only from server to client but not -- in the opposite direction. -- -- Errors are encoded as bencoded dictionary: -- -- { "y" : "e", "e" : [error_code, -- human_readable_error_reason] } data KError -- | Some error doesn't fit in any other category. GenericError :: ByteString -> KError [errorMessage] :: KError -> ByteString -- | Occur when server fail to process procedure call. ServerError :: ByteString -> KError [errorMessage] :: KError -> ByteString -- | Malformed packet, invalid arguments or bad token. ProtocolError :: ByteString -> KError [errorMessage] :: KError -> ByteString -- | Occur when client trying to call method server don't know. MethodUnknown :: ByteString -> KError [errorMessage] :: KError -> ByteString type ErrorCode = Int errorCode :: KError -> ErrorCode mkKError :: ErrorCode -> ByteString -> KError -- | Query used to signal that caller want to make procedure call to callee -- and pass arguments in. Therefore query may be only sent from client to -- server but not in the opposite direction. -- -- Queries are encoded as bencoded dictionary: -- -- { "y" : "q", "q" : "method_name", "a" : [arg1, -- arg2, ...] } data KQuery type MethodName = ByteString type ParamName = ByteString kquery :: MethodName -> [(ParamName, BEncode)] -> KQuery -- | KResponse used to signal that callee successufully process a procedure -- call and to return values from procedure. KResponse should not be sent -- if error occurred during RPC. Thus KResponse may be only sent from -- server to client. -- -- Responses are encoded as bencoded dictionary: -- -- { "y" : "r", "r" : [val1, val2, ...] } data KResponse type ValName = ByteString kresponse :: [(ValName, BEncode)] -> KResponse sendMessage :: BEncodable msg => msg -> KRemoteAddr -> KRemote -> IO () recvResponse :: KRemote -> IO (Either KError KResponse) type KRemote = Socket type KRemoteAddr = (HostAddress, PortNumber) withRemote :: (MonadBaseControl IO m, MonadIO m) => (KRemote -> m a) -> m a -- | Run server using a given port. Method invocation should be done -- manually. remoteServer :: (MonadBaseControl IO remote, MonadIO remote) => PortNumber -> (KRemoteAddr -> KQuery -> remote (Either KError KResponse)) -> remote () encode :: BEncode -> ByteString encoded :: BEncodable a => a -> ByteString decode :: ByteString -> Result BEncode decoded :: BEncodable a => ByteString -> Result a toBEncode :: BEncodable a => a -> BEncode fromBEncode :: BEncodable a => BEncode -> Result a instance GHC.Classes.Ord Remote.KRPC.Protocol.KResponse instance GHC.Classes.Eq Remote.KRPC.Protocol.KResponse instance GHC.Read.Read Remote.KRPC.Protocol.KResponse instance GHC.Show.Show Remote.KRPC.Protocol.KResponse instance GHC.Classes.Ord Remote.KRPC.Protocol.KQuery instance GHC.Classes.Eq Remote.KRPC.Protocol.KQuery instance GHC.Read.Read Remote.KRPC.Protocol.KQuery instance GHC.Show.Show Remote.KRPC.Protocol.KQuery instance GHC.Classes.Ord Remote.KRPC.Protocol.KError instance GHC.Classes.Eq Remote.KRPC.Protocol.KError instance GHC.Read.Read Remote.KRPC.Protocol.KError instance GHC.Show.Show Remote.KRPC.Protocol.KError instance Data.BEncode.BEncodable Remote.KRPC.Protocol.KError instance Data.BEncode.BEncodable Remote.KRPC.Protocol.KQuery instance Data.BEncode.BEncodable Remote.KRPC.Protocol.KResponse -- | This module provides safe remote procedure call. One important point -- is exceptions and errors, so be able handle them properly we need to -- investigate a bit about how this all works. Internally, in order to -- make method invokation KRPC makes the following steps: -- -- -- -- If every other error occurred caller get the GenericError. All -- errors returned by callee are throwed as ordinary haskell exceptions -- at caller side. Make sure that both callee and caller uses the same -- method signatures and everything should be ok: this KRPC -- implementation provides some level of safety through types. Also note -- that both caller and callee use plain UDP, so KRPC is unreliable. -- -- Consider one tiny example. From now caller = client and -- callee = server or remote. -- -- Somewhere we have to define all procedure signatures. Imagine that -- this is a library shared between client and server: -- --
--   factorialMethod :: Method Int Int
--   factorialMethod = method "factorial" ["x"] ["y"]
--   
-- -- Otherwise you can define this code in both client and server of -- course. But in this case you might get into troubles: you can get -- MethodUnknown or ProtocolError if name or type of method -- will mismatch after not synced changes in client or server code. -- -- Now let's define our client-side: -- --
--   main = withRemote  $ \remote -> do
--      result <- call remote (0, 6000) factorialMethod 4
--      assert (result == 24) $ print "Success!"
--   
-- -- It basically open socket with withRemote and make all the other -- steps in call as describe above. And finally our server-side: -- --
--   factorialImpl :: Int -> Int
--   factorialImpl n = product [1..n]
--   
--   main = runServer [factorialMethod $ return . factorialImpl]
--   
-- -- Here we implement method signature from that shared lib and run server -- with runServer by passing method table in. -- -- For more examples see exsamples or tests -- directories. -- -- For protocol details see Protocol module. module Remote.KRPC -- | Method datatype used to describe name, parameters and return values of -- procedure. Client use a method to invoke, server -- implements the method to make the actual work. -- -- We use the following fantom types to ensure type-safiety: -- -- data Method param result Method :: MethodName -> [ParamName] -> [ValName] -> Method param result -- | Name used in query. [methodName] :: Method param result -> MethodName -- | Name of each parameter in right to left order. [methodParams] :: Method param result -> [ParamName] -- | Name of each return value in right to left order. [methodVals] :: Method param result -> [ValName] -- | Makes method signature. Note that order of parameters and return -- values are not important as long as corresponding names and types are -- match. For exsample this is the equal definitions: -- --
--   methodA : Method (Foo, Bar) (Baz, Quux)
--   methodA = method "mymethod" ["a", "b"] ["c", "d"]
--   
-- --
--   methodA : Method (Bar, Foo) (Quux, Baz)
--   methodB = method "mymethod" ["b", "a"] ["d", "c"]
--   
method :: MethodName -> [ParamName] -> [ValName] -> Method param result -- | Identity procedure signature. Could be used for echo servers. -- Implemented as: -- --
--   idM = method "id" ["x"] ["y"]
--   
idM :: Method a a -- | Address of remote can be called by client. type RemoteAddr = KRemoteAddr -- | Represent any error mentioned by protocol specification that -- call, await might throw. For more details see -- Protocol. data RPCException RPCException :: KError -> RPCException -- | Makes remote procedure call. Throws RPCException on any error -- occurred. call :: (MonadBaseControl IO host, MonadIO host) => (BEncodable param, BEncodable result) => RemoteAddr -> Method param result -> param -> host result -- | Asynchonous result typically get from async call. Used to defer -- return values transfer. data Async result -- | Query procedure call but not wait for its results. This function -- returns Async value which is handle to procedure result. Actual -- result might be obtained with await. Unable to throw -- RPCException, this might happen in await if at all. -- -- Note that sending multiple queries at the same time to the one remote -- is not recommended. For exsample in the following scenario: -- --
--   aa <- async theRemote ....
--   ab <- async theRemote ....
--   a  <- await ab
--   b  <- await ab
--   
-- -- it's likely that the a and b values will be mixed up. So -- in order to get correct results you need to make await before -- the next async. async :: MonadIO host => (BEncodable param, BEncodable result) => RemoteAddr -> Method param result -> param -> host (Async result) -- | Will wait until the callee finished processing of procedure call and -- return its results. Throws RPCException on any error occurred. await :: MonadIO host => Async result -> host result -- | Procedure signature and implementation binded up. type MethodHandler remote = (MethodName, HandlerBody remote) -- | Assign method implementation to the method signature. (==>) :: (BEncodable param, BEncodable result) => Monad remote => Method param result -> (param -> remote result) -> MethodHandler remote -- | Run RPC server on specified port by using list of handlers. Server -- will dispatch procedure specified by callee, but note that it will not -- create new thread for each connection. server :: (MonadBaseControl IO remote, MonadIO remote) => PortNumber -> [MethodHandler remote] -> remote () -- | The same as call but use already opened socket. call_ :: (MonadBaseControl IO host, MonadIO host) => (BEncodable param, BEncodable result) => Remote -> RemoteAddr -> Method param result -> param -> host result withRemote :: (MonadBaseControl IO m, MonadIO m) => (KRemote -> m a) -> m a instance GHC.Classes.Eq Remote.KRPC.RPCException instance GHC.Show.Show Remote.KRPC.RPCException instance GHC.Exception.Exception Remote.KRPC.RPCException -- | This module provides message scheme validation for core protocol -- messages from Procotol. This module should be used with -- Protocol, otherwise (if you are using KRPC) this module -- seems to be useless. module Remote.KRPC.Scheme -- | Used to validate any message by its scheme -- -- forall m. m validate scheme m class KMessage message scheme | message -> scheme where validate = (==) . scheme -- | Get a message scheme. scheme :: KMessage message scheme => message -> scheme -- | Check a message with a scheme. validate :: KMessage message scheme => message -> scheme -> Bool data KQueryScheme KQueryScheme :: MethodName -> Set ParamName -> KQueryScheme [qscMethod] :: KQueryScheme -> MethodName [qscParams] :: KQueryScheme -> Set ParamName methodQueryScheme :: Method a b -> KQueryScheme newtype KResponseScheme KResponseScheme :: Set ValName -> KResponseScheme [rscVals] :: KResponseScheme -> Set ValName methodRespScheme :: Method a b -> KResponseScheme instance GHC.Classes.Ord Remote.KRPC.Scheme.KResponseScheme instance GHC.Classes.Eq Remote.KRPC.Scheme.KResponseScheme instance GHC.Read.Read Remote.KRPC.Scheme.KResponseScheme instance GHC.Show.Show Remote.KRPC.Scheme.KResponseScheme instance GHC.Classes.Ord Remote.KRPC.Scheme.KQueryScheme instance GHC.Classes.Eq Remote.KRPC.Scheme.KQueryScheme instance GHC.Read.Read Remote.KRPC.Scheme.KQueryScheme instance GHC.Show.Show Remote.KRPC.Scheme.KQueryScheme instance Remote.KRPC.Scheme.KMessage Remote.KRPC.Protocol.KError Remote.KRPC.Protocol.ErrorCode instance Remote.KRPC.Scheme.KMessage Remote.KRPC.Protocol.KQuery Remote.KRPC.Scheme.KQueryScheme instance Remote.KRPC.Scheme.KMessage Remote.KRPC.Protocol.KResponse Remote.KRPC.Scheme.KResponseScheme