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.