Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

GitHub

This documentation is part of the "Projects with Books" initiative at zenOSmosis.

The source code for this project is available on GitHub.

Examples and Tutorials

Relevant source files

This page provides an overview of practical examples demonstrating rust-muxio usage patterns. It covers the core example application, common testing patterns, and basic usage workflows. For a detailed walkthrough of the WebSocket RPC application, see WebSocket RPC Application. For a step-by-step tutorial on building services, see Simple Calculator Service.

The examples in this system demonstrate:

  • Service definition patterns using RpcMethodPrebuffered
  • Server-side handler registration with RpcServiceEndpointInterface
  • Client-side invocation using RpcServiceCallerInterface
  • Cross-platform testing with native Tokio and WASM clients
  • Error handling and large payload streaming

Sources: README.md:1-166 extensions/README.md:1-4


Available Example Resources

The codebase includes several resources demonstrating different aspects of the system:

ResourceLocationPurpose
Main Example Applicationexamples/example-muxio-ws-rpc-app/Complete WebSocket RPC server and client
Shared Service Definitionsexamples/example-muxio-rpc-service-definition/Reusable service contracts
Tokio Client Testsextensions/muxio-tokio-rpc-client/tests/Native client integration tests
WASM Client Testsextensions/muxio-wasm-rpc-client/tests/WASM client integration tests

Sources: README.md:67-68 extensions/muxio-tokio-rpc-client/tests/prebuffered_integration_tests.rs:1-241 extensions/muxio-wasm-rpc-client/tests/prebuffered_integration_tests.rs:1-313


Service Definition to Client Call Flow

This diagram maps the complete flow from defining a service method to executing it on a client, using actual type and function names from the codebase:

Sources: README.md:69-161 extensions/muxio-rpc-service-caller/src/prebuffered/traits.rs:1-99 extensions/muxio-tokio-rpc-client/tests/prebuffered_integration_tests.rs:34-61

graph TB
    subgraph "Service Definition Crate"
        AddStruct["Add struct"]
RpcMethodPrebuffered["impl RpcMethodPrebuffered"]
METHOD_ID["Add::METHOD_ID"]
encode_request["Add::encode_request()"]
decode_response["Add::decode_response()"]
end
    
    subgraph "Server Registration"
        endpoint["RpcServiceEndpointInterface"]
register_prebuffered["endpoint.register_prebuffered()"]
handler["Handler closure"]
decode_req["Add::decode_request()"]
encode_resp["Add::encode_response()"]
end
    
    subgraph "Client Invocation"
        RpcClient["RpcClient or RpcWasmClient"]
RpcCallPrebuffered["RpcCallPrebuffered trait"]
call_method["Add::call()"]
call_rpc_buffered["client.call_rpc_buffered()"]
end
    
 
   AddStruct --> RpcMethodPrebuffered
 
   RpcMethodPrebuffered --> METHOD_ID
 
   RpcMethodPrebuffered --> encode_request
 
   RpcMethodPrebuffered --> decode_response
    
 
   METHOD_ID --> register_prebuffered
 
   register_prebuffered --> handler
 
   handler --> decode_req
 
   handler --> encode_resp
    
 
   encode_request --> call_method
 
   decode_response --> call_method
 
   call_method --> RpcCallPrebuffered
 
   RpcCallPrebuffered --> call_rpc_buffered
 
   call_rpc_buffered --> RpcClient

Basic Server Setup Pattern

The standard pattern for creating and configuring an RPC server involves these steps:

sequenceDiagram
    participant App as "Application Code"
    participant TcpListener as "TcpListener"
    participant RpcServer as "RpcServer::new()"
    participant Endpoint as "server.endpoint()"
    participant Task as "tokio::spawn()"
    
    App->>TcpListener: bind("127.0.0.1:0")
    App->>RpcServer: Create with optional config
    App->>Endpoint: Get endpoint handle
    
    loop For each RPC method
        Endpoint->>Endpoint: register_prebuffered(METHOD_ID, handler)
    end
    
    App->>Task: Spawn server task
    Task->>RpcServer: serve_with_listener(listener)
    
    Note over RpcServer: Server now accepting\nWebSocket connections

The server is wrapped in Arc<RpcServer> to allow sharing between the registration code and the spawned task. The endpoint() method returns a handle implementing RpcServiceEndpointInterface for registering handlers before the server starts.

Sources: README.md:86-128 extensions/muxio-tokio-rpc-client/tests/prebuffered_integration_tests.rs:21-71


Handler Registration Code Pattern

Handler registration uses register_prebuffered() with the compile-time generated METHOD_ID and an async closure:

graph LR
    subgraph "Handler Components"
        METHOD_ID["Add::METHOD_ID\n(compile-time hash)"]
closure["Async closure:\n/request_bytes, _ctx/"]
decode["Add::decode_request()"]
logic["Business logic:\nrequest_params.iter().sum()"]
encode["Add::encode_response()"]
end
    
 
   METHOD_ID --> register["endpoint.register_prebuffered()"]
closure --> register
    
 
   closure --> decode
 
   decode --> logic
 
   logic --> encode
 
   encode --> return["Ok(response_bytes)"]

Example handler registration from the tests:

  • Decode request bytes using Add::decode_request(&request_bytes)?
  • Execute business logic (e.g., request_params.iter().sum())
  • Encode response using Add::encode_response(result)?
  • Return Ok(response_bytes) or Err(...) for errors

Sources: README.md:100-117 extensions/muxio-tokio-rpc-client/tests/prebuffered_integration_tests.rs:35-60


Client Connection and Call Pattern

Client setup and RPC invocation follows this structure:

The client implements RpcServiceCallerInterface, which is used by the RpcCallPrebuffered trait to handle all encoding, transmission, and decoding automatically.

Sources: README.md:130-161 extensions/muxio-tokio-rpc-client/tests/prebuffered_integration_tests.rs:75-96


Complete Integration Test Structure

Integration tests demonstrate the full client-server setup in a single test function:

graph TB
    subgraph "Test Setup Phase"
        bind["TcpListener::bind()"]
create_server["Arc::new(RpcServer::new())"]
get_endpoint["server.endpoint()"]
register["Register all handlers"]
spawn_server["tokio::spawn(server.serve())"]
end
    
    subgraph "Test Execution Phase"
        sleep["tokio::time::sleep()"]
create_client["RpcClient::new()"]
make_calls["Execute RPC calls with join!()"]
assertions["Assert results"]
end
    
 
   bind --> create_server
 
   create_server --> get_endpoint
 
   get_endpoint --> register
 
   register --> spawn_server
 
   spawn_server --> sleep
 
   sleep --> create_client
 
   create_client --> make_calls
 
   make_calls --> assertions

The join! macro enables concurrent RPC calls over a single connection, demonstrating multiplexing in action. All requests are sent and responses are awaited in parallel.

Sources: extensions/muxio-tokio-rpc-client/tests/prebuffered_integration_tests.rs:16-97


Example: Concurrent RPC Calls

The test files demonstrate making multiple concurrent RPC calls using tokio::join!:

All six calls execute concurrently over the same WebSocket connection. The RpcDispatcher assigns unique request IDs and correlates responses back to the correct futures.

Sources: extensions/muxio-tokio-rpc-client/tests/prebuffered_integration_tests.rs:81-96 README.md:144-151


Example: Error Handling

The integration tests show how RPC errors propagate from server to client:

Example error handling code from tests:

Sources: extensions/muxio-tokio-rpc-client/tests/prebuffered_integration_tests.rs:99-152


Example: Large Payload Handling

The system automatically chunks large payloads. The integration tests verify this with payloads 200x larger than the chunk size:

Test ScenarioPayload SizeMechanism
Small request< DEFAULT_SERVICE_MAX_CHUNK_SIZESent in rpc_param_bytes field
Large requestDEFAULT_SERVICE_MAX_CHUNK_SIZESent via rpc_prebuffered_payload_bytes with automatic chunking
Large responseAny sizeAutomatically chunked by RpcDispatcher

Test code creates a payload of DEFAULT_SERVICE_MAX_CHUNK_SIZE * 200 (approximately 12.8 MB) and verifies it round-trips correctly:

The chunking and reassembly happen transparently in the RpcDispatcher layer.

Sources: extensions/muxio-tokio-rpc-client/tests/prebuffered_integration_tests.rs:154-203 extensions/muxio-rpc-service-caller/src/prebuffered/traits.rs:30-72


WASM Client Testing Pattern

WASM client tests use a bridge pattern to connect the client to a real server:

The bridge creates an unbounded_channel to receive bytes from the WASM client's output callback, then forwards them through a real WebSocket connection. Responses flow back through spawn_blocking to avoid blocking the async runtime when calling the synchronous dispatcher.blocking_lock().

Sources: extensions/muxio-wasm-rpc-client/tests/prebuffered_integration_tests.rs:1-142


Example: Method Not Found Error

When a client calls a method that hasn't been registered on the server, the system returns RpcServiceErrorCode::NotFound:

This demonstrates the system's ability to distinguish between different error types: NotFound for missing handlers versus System for handler execution failures.

Sources: extensions/muxio-tokio-rpc-client/tests/prebuffered_integration_tests.rs:205-240


State Change Callbacks

Clients can register callbacks to monitor connection state changes:

Example from the README:

Sources: README.md:138-141


Service Definition Module Structure

Service definitions are typically organized in a separate crate shared by both client and server:

ComponentPurposeExample
RpcMethodPrebuffered implDefines method contractimpl RpcMethodPrebuffered for Add
METHOD_ID constantCompile-time generated hashAdd::METHOD_ID
Input typeRequest parameters typeVec<f64>
Output typeResponse result typef64
encode_request()Serializes inputUses bitcode::encode()
decode_request()Deserializes inputUses bitcode::decode()
encode_response()Serializes outputUses bitcode::encode()
decode_response()Deserializes outputUses bitcode::decode()

This shared definition ensures compile-time type safety between client and server implementations.

Sources: README.md:49-50 extensions/muxio-tokio-rpc-client/tests/prebuffered_integration_tests.rs:1-6


Testing Utilities

The muxio-ext-test crate provides utilities for testing, though the primary testing pattern uses real server and client instances as shown in the integration tests. The tests demonstrate:

  • Using TcpListener::bind("127.0.0.1:0") for random available ports
  • Extracting host and port with tcp_listener_to_host_port()
  • Using tokio::time::sleep() for synchronization
  • Spawning server tasks with tokio::spawn()
  • Making concurrent calls with tokio::join!()

Sources: extensions/muxio-tokio-rpc-client/tests/prebuffered_integration_tests.rs:9-14


Common Patterns Summary

PatternImplementationUse Case
Server setupArc::new(RpcServer::new())Share between registration and serving
Handler registrationendpoint.register_prebuffered()Before calling serve_with_listener()
Client creationRpcClient::new(host, port)Tokio-based native client
WASM clientRpcWasmClient::new(callback)Browser/WASM environment
RPC invocationMethod::call(&client, params)Type-safe method calls
Concurrent callstokio::join!(...)Multiple simultaneous requests
Error handlingmatch RpcServiceErrorDistinguish error types
Large payloadsAutomatic chunkingTransparent for > 64KB data

Sources: README.md:69-161 extensions/muxio-tokio-rpc-client/tests/prebuffered_integration_tests.rs:18-97

Dismiss

Refresh this wiki

Enter email to refresh