Introduction

Overview

Client perform connection to the server. When connection is successfully initialized client or server might calls remote methods.

@startuml

title Client and Server communication


participant Client as client
participant Server as server

client -> server: Initializing connection
server -> server: authorize client

alt client was not authenticated
    server -> client: 403 Forbidden
    server ->x client: connection closed
else client authenticated
    server -> client: 101 Switching Protocols

    ...

    client -> server: RPC request 1
    ...
    client -> server: RPC request 2
    server -> client: RPC response 2
    ...
    client -> server: RPC request 3
    server -> client: RPC response 3
    ...
    server -> client: RPC response 1

    client -> server: Close connection
    server ->x client: connection closed
end

@enduml

Socket instance

Server or client has a similar interface based on wsrpc_aiohttp.WSRPCBase.

That’s means the two sides has wsrpc_aiohttp.WSRPCBase.call() and method wsrpc_aiohttp.WSRPCBase.proxy

Routes

It’s an abstraction for exposing local functions to the remote side. Route it’s an alias for function.

Function based routes

Lets write simple function:

def multiply(socket, *, x, y):
    return x * y

Then register this function on the client side:

client = WSRPCClient("ws://127.0.0.1:8000/ws/", loop=loop)
client.add_route('multiply', multiply)

await client.connect()

And create the function on the server side:

async def multiply_loopback(socket: WebSocketAsync):
    return await socket.proxy.multiply(x=10, y=20)

Register to the server-side socket.

WebSocketAsync.add_route('multiply_loopback', multiply_loopback)

Class based routes

The route can be a based on class wsrpc_aiohttp.WebSocketRoute. In this case socket instance will store an initialized instance of this class and instance appears after first route call.

class Storage(WebSocketRoute):
    async def init(self):
        """ method which will be called after the first route call """
        self._internal = dict()

    async def get(self, key, default=None):
        return self._internal.get(key, default)

    async def set(self, key, value):
        self._internal[key] = value
        return True

WebSocketAsync.add_route('kv', Storage)

Python client code:

client = WSRPCClient("ws://127.0.0.1:8000/ws/", loop=loop)
await client.connect()

assert await client.proxy.kv.set(key='foo', value='bar')
# The `init` will be called on the server side before `set`

assert await client.proxy.kv.get(key='foo')

Javascript client code:

RPC = WSRPC("ws://127.0.0.1:8000/ws/");
RPC.connect();

RPC.call('kw.set', {'key': 'foo', 'value': 'bar'}).then(function (result) {
    console.log('Key foo was set');
}).then(function () {
    RPC.call('kw.get', {'key': 'foo'}).then(function (result) {
        console.log('Key foo is', result);
    });
});

Client to server calls

Let’s define another route on the server side for demonstrate simple case.

async def subtract(socket: WebSocketAsync, *, a, b):
    return a - b

WebSocketAsync.add_route('subtract', subtract)

After server initialization any client can call it.

client = WSRPCClient("ws://127.0.0.1:8000/ws/", loop=loop)
await client.connect()

assert await client.proxy.subtract(a=1, b=9) == -8

Server to client calls

The wsrpc_aiohttp.WSRPCBase instance will be passed to the route function. That’s means you can call function on the remote side inside of route function.

Code of the server side:

async def multiply_loopback(socket: WebSocketAsync):
    # multiply will be called on the client side
    return await socket.proxy.multiply(x=10, y=20)

...

WebSocketAsync.add_route('multiply_loopback', multiply_loopback)

The client-side code:

def multiply(socket, *, x, y):
    return x * y

...

client = WSRPCClient("ws://127.0.0.1:8000/ws/", loop=loop)
client.add_route('multiply', multiply)
await client.connect()

await client.proxy.multiply_loopback()

The sequence diagram for this case:

@startuml

title Client and Server communication


participant Client as client
participant Server as server

client --> server: Initializing connection
server --> client: 101 Switching Protocols

...

client -> server: [Request #0] <b>await client.proxy.multiply_loopback()</b>
server --> server: <b>multiply_loopback()</b>
server -> client: [Request #1] <b>await socket.proxy.multiply(x=10, y=20)</b>
client --> client: <b>multiply(socket, x=10, y=20)</b>
client -> server: [Response #1] <b>return 200</b>
server -> client: [Response #0] <b>return None</b>
...

@enduml