vibe.web.rpc

Web based, bi-directional, concurrent RPC implementation.

This module implements a generic RPC mechanism that allows transparently calling remote functions over an HTTP based network connection. The current implementation is based on a WebSocket based protocol, serializing method arguments and return types as BSON.

The RPC API is defined using interfaces, very similar to the system in vibe.web.rest. It supports methods with or without a return value, normal, ref and out parameters, exceptions, properties returning interfaces, and properties returning vibe.web.rest.Collection!I.

Authorization and authentication is supported via the vibe.web.auth framework. When using it, the authenticate method should be defined as @noRoute T authenticate(ref const WebRPCPerrInfo), where T is the type passed to the @requiresAuth UDA.

Any remote function calls can execute concurrently, so that the connection never gets blocked by an unfinished function call.

Note that this system will establish a bi-directional communication facility, allowing both, the client and the server, to initiate calls. This effectively forms a peer-to-peer connection instead of a client-server connection. The advantage of using HTTP as the basis is that this makes it easy to establish P2P connections where one of the peers is behind a firewall or NAT layer, but the other peer can be reached through a public port or through a (reverse) proxy server.

Defining_a_simple_RPC_interface:

The API for the interface is defined as a normal D interface:

interface ExampleAPI {
	void performSomeAction();
	int getSomeInformation();
}

An implementation of this interface is required on both, the server and the client side:

class ExampleAPIImplementation : ExampleAPI {
	void performSomeAction() { ... }
	int getSomeInformation() { return ...; }
}

With this defined, this is the basic code needed to set up the server side:

void handleIncomingPeer(WebRPCPeer!ExampleAPI peer)
@safe nothrow {
	// this gets executed for any client that connects to the server
	try {
		peer.performSomeAction();
	} catch (Exception e) {
		logException(e, "Failed to perform peer action");
	}
}

auto r = new URLRouter;
r.registerWebRPC!ExampleAPI(r, "/rpc", new ExampleAPIImplementation, &handlePeer);
// could register other routes here, such as for a web or REST interface

auto l = listenHTTP("127.0.0.1:1234", r);

A client can now connect to the server and access the API as well:

auto peer = connectWebRPC(URL("http://127.0.0.1:1234/rpc"),
	new ExampleAPIImplementation);

peer.performSomeAction();

Types 9

aliasWebRPCPeerCallback(I) = void delegate(WebRPCPeer!I peer) @safe nothrow

Provides information about a peer;

Fields
NetworkAddress address
TLSCertificateInformation certificate
structWebRPCPeer(I)

Reference counted type used to access a peer's API.

This struct defines an alias this to its implementation property in order to provide an interface implementation of I. Any calls on the methods of this implementation will be forwarded to the remote peer.

Note that the WebRPC connection will be closed as soon as the last instance of a connected WebRPCPeer gets destroyed.

Fields
private WebRPCPeerImpl!(I, I, "") m_impl
Methods
const(WebRPCPeerInfo) peerInformation() @property ref @safe constProvides information about the remote peer.
inout(I) implementation() @property @safe inoutAccesses the remote peer's API interface.
Constructors
this(WebRPCPeerImpl!(I, I, "") impl)
Destructors
classWebRPCPeerImpl(I, RootI, string method_prefix) : I if (is(I == interface) && is(RootI == interface))
Fields
private WebSocketHandler!RootI m_handler
private staticMap!(SubPeerImpl, Info.SubInterfaceFunctions) m_subInterfaces
Methods
private ReturnType!method performCall(alias method, PARAMS...)(auto ref PARAMS params) @safe
Constructors
this(WebSocketHandler!RootI handler)
private classWebSocketHandler(I)
Fields
private I m_impl
private WebRPCPeerInfo m_peerInfo
private int m_refCount
private WebSocket m_socket
private TaskMutex m_sendMutex
private ulong m_sequence
private Res[ulong] m_availableResponses
private LocalManualEvent m_responseEvent
Methods
void addRef() @safe
void releaseRef() @safe
ulong sendCall(string method, Bson arguments) @safe
void sendResponse(ulong sequence, Bson result) @safe
void sendErrorResponse(ulong sequence, string error_message) @safe
Bson waitForResponse(ulong sequence) @safe
private void terminateConnection() @safe nothrow
void runReadLoop() @safe nothrow
private Bson invokeMethod(string name, Bson arguments) @safe
private Bson invokeMethodF(SI, alias method, string qualified_name)(Bson arguments) @safe
private auto resolveImpl(string qualified_name, RI)(RI base, Bson arguments) if (is(RI == interface)) @safe
private void resolveArguments(alias method, SI)(SI impl, Bson arguments, out typeof(ParameterTypeTuple!method.init) args) @safe
Constructors
this(return WebSocket ws, I impl, ref const(WebRPCPeerInfo) peer_info)
Nested Templates
Res
private enumWebRPCMessageType : ubyte
call = 1
response = 2
errorResponse = 3
private structWebRPCCallPacket
Fields
ulong sequence
string method
Bson arguments
private structWebRPCResponsePacket
Fields
ulong sequence
Bson result
Fields
ulong sequence
string message

Functions 5

fnvoid registerWebRPC(I)(URLRouter router, string path, I implementation, WebRPCPeerCallback!I peer_callback) if (is(I == interface))Registers a route for handling incoming WebRPC requests.
fnWebRPCPeer!I connectWebRPC(I)(URL url, I implementation) if (is(I == interface))Connects to a WebRPC endpoint.
private fnvoid handleWebRPC(I)(I implementation, WebRPCPeerCallback!I peer_callback, scope HTTPServerRequest req, scope HTTPServerResponse res)
private fnstring generateWebRPCPeerMethods(I)()
private fnstring generateModuleImports(I)()

Templates 2

tmplrefOutParameterIndices(alias fun)
tmplrecursiveInterfaceFunctions(I, string method_prefix)