Asynchronous RPC protocols for mudb
See src/example for a contrived demo which will be explained below. To run the demo
npm i mudosrc/exampletsc ./*.tsmudo --socket websocketA mudb instance can have multiple RPC protocols for different sets of behaviors. Naturally each RPC protocol consistes of two sides, one for server and one for client, plus a schema to describe the RPC interfaces (the argument and return).
So the first step to define an RPC protocol is to specify a protocol schema using muschema.
Like the protocol to be created, the schema always consists of two sides, the server side and the client side. Each side in turn contains schemas for the RPC interfaces that they implement repectively, under the name of the corresponding function. RPC schemas are created using the MuRPC function, which takes the schemas of the argument and return.
const IntegerSetSchema = new MuArray(new MuInt8());
const TotalSchema = new MuInt32();
const SecretSchema = new MuString();
const DigestSchema = new MuFixedASCII(128);
export const RPCSchema = {
client: {
// schema for a function that sums up all integers in a set
// which takes an array of int8 and returns an int32
sum: MuRPC(IntegerSetSchema, TotalSchema),
},
server: {
// schema for a function that hashes a secret string
// which takes a string and always returns an ASCII of length 128
hash: MuRPC(SecretSchema, DigestSchema),
},
};
You can define the server side of the RPC protocol by creating and then configuring an instance of MuRPCServer.
export = function (server:MuServer) {
const protocol = new MuRPCServer(server, RPCSchema);
// content of the server side of the protocol
protocol.configure({
rpc: {
// a hash function to be called by clients
hash: (secret, next) => {
// hash `secret`
const digest = createHash('sha512').update(secret).digest('hex');
// pass the digest back to client
next(undefined, digest);
},
},
// set up server to do something when a client connects
connect: (client) => {
const set = [1, 2, 3, 4];
// call `sum()` on the client to get the total of all numbers in `set`
client.rpc.sum(set, (total) => {
console.log(`sum of ${set}: ${total}`);
});
},
});
server.start();
};
The last missing piece is the client side of the RPC protocol. Similarly, you can define it through an instance of MuRPCClient.
export = function (client:MuClient) {
const protocol = new MuRPCClient(client, RPCSchema);
// content of the client side of the protocol
protocol.configure({
rpc: {
// a function to be calle by server
sum: (set, next) => {
// calculate the total of all numbers in `set`
const total = set.reduce((acc, num) => acc + num);
// pass result back to server
next(undefined, total);
},
},
// set up client to do something when it is ready to handle messages
ready: () => {
// generate a secret string
const secret = Math.random().toString(36).substr(2, 11);
// call `hash()` on server to get the digest of `secret`
protocol.server.rpc.hash(secret, (digest) => {
console.log('secret digest:', digest);
});
},
});
client.start();
};
npm i murpc
Purely instructive types used to describle the API:
TableOf<T>: { [name:string]:T } | {}RPCSchema: [ AnyMuSchema, AnyMuSchema ]RPCProtocolSchema: { server:TableOf<RPCSchema>, client:TableOf<RPCSchema> }NextFn: (errorMessage:string|undefined, returnValue?) => undefinedServerRPCHandler: (rpcArgument, next:NextFn, client?:MuRemoteRPCClient) => undefinedClientRPCHandler: (rpcArgument, next:NextFn) => undefinedCallbackFn: (returnValue) => undefinedRPCCaller: (rpcArgument, callback:CallbackFn) => undefinedExported from murpc/rpc, used when creating the RPC protocol schema, to define RPC interfaces in terms of the argument and the return:
argumentSchema:AnyMuSchema the data type of the argumentreturnSchema:AnyMuSchema the data type of the return valueUsed to define the server side of an RPC protocol. Exported from murpc/server, it takes these arguments:
server:MuServerschema:RPCProtocolSchema the RPC protocol schemaMocks of all connected clients, used to initiate RPCs to a specific client
The underlying MuServer object
The RPC protocol schema
Registers event handlers specifed in spec
spec:{ rpc, ready?, connect?, disconnect?, close? }
rpc:TableOf<ServerRPCHandler> an object containing the implementations of the functions to be called by clientsready() called when the server is launchedconnect(client:MuRemoteRPCClient) called when a client connectsdisconnect(client:MuRemoteRPCClient) called when a client disconnectsclose() called when the server is shut downA MuRemoteRPCClient is the server-side mock of a connected client
A unique session id identifying the client
A table of RPC initiators each under the name of the corresponding remote function on a client
Used to define the client side of an RPC protocol. Exported from murpc/client, it takes these arguments:
client:MuClientschema:ProtocolSche4ma the RPC protocol schemaA unique session id identifying the client
A mock used to initiate RPCs to the remote server
The underlying MuClient object
The RPC protocol schema
Registers event handlers specified in spec
spec:{ rpc, ready?, close? }
rpc:TableOf<ClientRPCHandler> an object containing the implementations of the functions to be called by the serverready() called when client is ready to handle messagesclose() called when the client is disconnectedA MuRemoteRPCServer is the client-side mock of the server
A table of RPC initiators each under the name of the corresponding remote function on the server
Copyright (c) 2017 Mikola Lysenko, Shenzhen Dianmao Technology Company Limited