Skip to main content

Request routing

The chat application will have various functionality (e.g. private messages and channels). Each request to the server will be identified by a route (similar to paths in an HTTP URL). To implement this we will use the RequestRouter and RoutingRequestHandler classes.

See resulting code on GitHub

Server side

We will modify the example from the previous step into a routed request response.

Routing request handler

The handler_factory method below replaces the Handler class from the previous step:

from typing import Awaitable

from rsocket.frame_helpers import ensure_bytes
from rsocket.helpers import utf8_decode, create_response
from rsocket.payload import Payload
from rsocket.routing.request_router import RequestRouter
from rsocket.routing.routing_request_handler import RoutingRequestHandler

def handler_factory() -> RoutingRequestHandler:
router = RequestRouter()

@router.response('login')
async def login(payload: Payload) -> Awaitable[Payload]:
username = utf8_decode(payload.data)
return create_response(ensure_bytes(f'Welcome to chat, {username}'))

return RoutingRequestHandler(router)

Line 10 instantiates the RequestRouter. This helper is used as a method decorator to register the route of each request (it is similar to Flask and Quart syntax).

Lines 12-15 define the login method and attach it to a request-response with the login route. The method name does not have to match the route.

All methods decorated by the request router accept two optional arguments, payload and composite_metadata. For now, we only pass the payload. It is ann instance of a Payload class which contains the data and metadata of the request. This argument must appear with the exact names as above, as that is how the decorator detects where to pass the payload. The argument type hints are optional.

Line 17 returns the actual request handler, an instance of RoutingRequestHandler, which uses the request router instance.

Use the routing request handler

Modify the RSocketServer instantiation from the previous example and pass the handler_factory method as the handler_factory parameter:

from rsocket.rsocket_server import RSocketServer
from rsocket.transports.tcp import TransportTCP

async def run_server():
def session(*connection):
RSocketServer(TransportTCP(*connection), handler_factory=handler_factory)

Client side

Let's modify the client side to call this new routed request. For readability and maintainability, we will create a ChatClient which will wrap the RSocket client and provide the methods for interacting with the server.

ChatClient class

Below is the complete code for the new client.py module:

from rsocket.extensions.helpers import composite, route
from rsocket.frame_helpers import ensure_bytes
from rsocket.helpers import utf8_decode
from rsocket.payload import Payload
from rsocket.rsocket_client import RSocketClient

class ChatClient:
def __init__(self, rsocket: RSocketClient):
self._rsocket = rsocket

async def login(self, username: str):
payload = Payload(ensure_bytes(username), composite(route('login')))
response = await self._rsocket.request_response(payload)
print(f'Server response: {utf8_decode(response.data)}')

Lines 7-14 define our new ChatClient which will encapsulate the methods used to interact with the chat server.

Lines 11-14 define a login method. It uses the composite and route helper methods to create the metadata which will ensure the payload is routed to the method registered on the server side in the previous step.

Use the new ChatClient class

Let's modify the client module to test our new ChatClient:

from rsocket.extensions.mimetypes import WellKnownMimeTypes
from rsocket.helpers import single_transport_provider
from rsocket.rsocket_client import RSocketClient
from rsocket.transports.tcp import TransportTCP

async def main():
...
async with RSocketClient(single_transport_provider(TransportTCP(*connection)),
metadata_encoding=WellKnownMimeTypes.MESSAGE_RSOCKET_COMPOSITE_METADATA) as client:
user = ChatClient(client)
await user.login('George')

Line 9 changes the metadata_encoding type to be COMPOSITE_METADATA. This is required for routing support.

Lines 10-11 instantiate a ChatClient and call the login method.