Portability | portable |
---|---|
Stability | experimental |
Maintainer | pxqr.sta@gmail.com |
Safe Haskell | None |
This module provides safe remote procedure call. One important point is exceptions and errors, so to 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:
- Caller serialize arguments to bencoded bytestrings;
- Caller send bytestring data over UDP to the callee;
- Callee receive and decode arguments to the method and method
name. If it can't decode then it send
ProtocolError
back to the caller; - Callee search for the
method name
in the method table. If it not present in the table then callee sendMethodUnknown
back to the caller; - Callee check if argument names match. If not it send
ProtocolError
back; - Callee make the actuall call to the plain old haskell
function. If the function throw exception then callee send
ServerError
back. - Callee serialize result of the function to bencoded bytestring.
- Callee encode result to bencoded bytestring and send it back to the caller.
- Caller check if return values names match with the signature it called in the first step.
- Caller extracts results and finally return results of the procedure call as ordinary haskell values.
If every other error occurred then caller get the
GenericError
. All errors returned by callee are throwed as
ordinary haskell exceptions at caller side. Also note that both
caller and callee use plain UDP, so KRPC is unreliable.
For async query
use async
package.
For protocol details see Network.KRPC.Message module.
- data Method param result
- class (Typeable req, BEncode req, Typeable resp, BEncode resp) => KRPC req resp | req -> resp where
- data QueryFailure
- query :: forall h m a b. (MonadKRPC h m, KRPC a b) => SockAddr -> a -> m b
- getQueryCount :: MonadKRPC h m => m Int
- data HandlerFailure
- type Handler h = (MethodName, HandlerBody h)
- handler :: forall h a b. (KRPC a b, Monad h) => (SockAddr -> a -> h b) -> Handler h
- class (MonadBaseControl IO m, MonadLogger m, MonadIO m) => MonadKRPC h m | m -> h where
- getManager :: m (Manager h)
- liftHandler :: h a -> m a
- data Options = Options {
- optSeedTransaction :: !Int
- optQueryTimeout :: !Int
- optMaxMsgSize :: !Int
- def :: Default a => a
- data Manager h
- newManager :: Options -> SockAddr -> [Handler h] -> IO (Manager h)
- closeManager :: Manager m -> IO ()
- withManager :: Options -> SockAddr -> [Handler h] -> (Manager h -> IO a) -> IO a
- listen :: MonadKRPC h m => m ()
- data ErrorCode
- data SockAddr
Methods
data Method param result Source
Method datatype used to describe method 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:
- param: Type of method parameters.
- result: Type of return value of the method.
class (Typeable req, BEncode req, Typeable resp, BEncode resp) => KRPC req resp | req -> resp whereSource
RPC
Query
data QueryFailure Source
Used to signal query
errors.
SendFailed | unable to send query; |
QueryFailed ErrorCode Text | remote node return error; |
TimeoutExpired | remote node not responding. |
query :: forall h m a b. (MonadKRPC h m, KRPC a b) => SockAddr -> a -> m bSource
Enqueue query to the given node.
This function should throw QueryFailure
exception if quered node
respond with error
message or the query timeout expires.
getQueryCount :: MonadKRPC h m => m IntSource
How many times query
call have been performed.
Handler
data HandlerFailure Source
Used to signal protocol errors.
BadAddress | for e.g.: node calls herself; |
InvalidParameter Text | for e.g.: bad session token. |
type Handler h = (MethodName, HandlerBody h)Source
Handler is a function which will be invoked then some remote node querying this node.
handler :: forall h a b. (KRPC a b, Monad h) => (SockAddr -> a -> h b) -> Handler hSource
Make handler from handler function. Any thrown exception will be supressed and send over the wire back to the querying node.
If the handler make some query
normally it should handle
corresponding QueryFailure
s.
Manager
class (MonadBaseControl IO m, MonadLogger m, MonadIO m) => MonadKRPC h m | m -> h whereSource
A monad which can perform or handle queries.
getManager :: m (Manager h)Source
Ask for manager.
liftHandler :: h a -> m aSource
Can be used to add logging for instance.
RPC manager options.
Options | |
|
Keep track pending queries made by this node and handle queries made by remote nodes.
:: Options | various protocol options; |
-> SockAddr | address to listen on; |
-> [Handler h] | handlers to run on incoming queries. |
-> IO (Manager h) | new rpc manager. |
Bind socket to the specified address. To enable query handling
run listen
.
closeManager :: Manager m -> IO ()Source
Unblock all pending calls and close socket.
withManager :: Options -> SockAddr -> [Handler h] -> (Manager h -> IO a) -> IO aSource
Normally you should use Control.Monad.Trans.Resource.allocate function.
listen :: MonadKRPC h m => m ()Source
Should be run before any query
, otherwise they will never
succeed.
Re-exports
Types of RPC errors.
GenericError | Some error doesn't fit in any other category. |
ServerError | Occur when server fail to process procedure call. |
ProtocolError | Malformed packet, invalid arguments or bad token. |
MethodUnknown | Occur when client trying to call method server don't know. |