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.

Extending the Framework

Loading…

Extending the Framework

Relevant source files

Purpose and Scope

This document provides guidance for extending the muxio framework with custom transport implementations, runtime-specific client and server implementations, and platform-specific adaptations. It covers the architectural extension points, required trait implementations, and patterns for integrating new runtime environments.

For information about deploying existing implementations across platforms, see Cross-Platform Deployment. For details about the existing Tokio and WASM implementations, see Platform Implementations.

Sources: Cargo.toml:19-31 extensions/muxio-rpc-service-endpoint/Cargo.toml:1-33


Extension Points in the Architecture

The muxio framework provides several well-defined extension points that enable custom implementations without modifying core logic. The layered architecture isolates platform-specific concerns from runtime-agnostic abstractions.

Diagram 1: Framework Extension Points

graph TB
    subgraph "Core Layer - No Extensions Needed"
        CORE["muxio Core"]
DISPATCHER["RpcDispatcher"]
SESSION["RpcSession"]
FRAME["Binary Framing Protocol"]
end
    
    subgraph "Framework Layer - Extension Points"
        CALLER["RpcServiceCallerInterface\nTrait for Client Logic"]
ENDPOINT["RpcServiceEndpointInterface\nTrait for Server Logic"]
SERVICE["RpcMethodPrebuffered\nService Definitions"]
end
    
    subgraph "Platform Layer - Your Extensions"
        CUSTOM_CLIENT["Custom RPC Client\nYour Transport"]
CUSTOM_SERVER["Custom RPC Server\nYour Runtime"]
CUSTOM_TRANSPORT["Custom Transport Layer\nYour Protocol"]
end
    
 
   CORE --> DISPATCHER
 
   CORE --> SESSION
 
   CORE --> FRAME
    
 
   DISPATCHER --> CALLER
 
   DISPATCHER --> ENDPOINT
 
   SESSION --> CALLER
 
   SESSION --> ENDPOINT
    
 
   CALLER --> SERVICE
 
   ENDPOINT --> SERVICE
    
    CUSTOM_CLIENT -.implements.-> CALLER
    CUSTOM_SERVER -.implements.-> ENDPOINT
    CUSTOM_TRANSPORT -.uses.-> CORE
    
 
   CUSTOM_CLIENT --> CUSTOM_TRANSPORT
 
   CUSTOM_SERVER --> CUSTOM_TRANSPORT

Key Extension Points

Extension PointLocationPurposeRequired Traits
Client ImplementationCustom cratePlatform-specific RPC clientRpcServiceCallerInterface
Server ImplementationCustom cratePlatform-specific RPC serverRpcServiceEndpointInterface
Transport LayerAnyCustom wire protocol or runtimeNone (callback-driven)
Service DefinitionsShared crateBusiness logic contractsRpcMethodPrebuffered
Feature FlagsCargo.tomlConditional compilationN/A

Sources: Cargo.toml:20-31 extensions/muxio-rpc-service-endpoint/Cargo.toml:23-27


Creating Custom Transports

Custom transports wrap the RpcDispatcher and provide platform-specific I/O mechanisms. The core dispatcher is runtime-agnostic and callback-driven, enabling integration with any execution model.

Diagram 2: Custom Transport Integration Pattern

graph LR
    subgraph "Your Custom Transport"
        INIT["Initialize Transport\nCustom I/O Setup"]
WRITE["Write Callback\nfn(Vec<u8>)"]
READ["Read Loop\nPlatform-Specific"]
LIFECYCLE["Connection Lifecycle\nState Management"]
end
    
    subgraph "Core muxio Components"
        DISPATCHER["RpcDispatcher"]
SESSION["RpcSession"]
end
    
 
   INIT --> DISPATCHER
 
   DISPATCHER --> WRITE
 
   READ --> DISPATCHER
 
   LIFECYCLE --> DISPATCHER
    
 
   DISPATCHER --> SESSION

Transport Implementation Requirements

  1. InitializeRpcDispatcher with a write callback that sends binary frames via your transport
  2. Implement read loop that feeds received bytes to RpcDispatcher::read()
  3. Handle lifecycle events such as connection, disconnection, and errors
  4. Manage concurrency model appropriate for your runtime (async, sync, thread-per-connection, etc.)

Example Transport Structure

Key patterns:

  • Write Callback : Closure or function that writes bytes to transport [see extensions/muxio-tokio-rpc-client/src/rpc_client.rs100-226](https://github.com/jzombie/rust-muxio/blob/30450c98/see extensions/muxio-tokio-rpc-client/src/rpc_client.rs#L100-L226)
  • Read Integration : Feed incoming bytes to RpcDispatcher::read() [see src/rpc/rpc_dispatcher.rs130-264](https://github.com/jzombie/rust-muxio/blob/30450c98/see src/rpc/rpc_dispatcher.rs#L130-L264)
  • State Management : Track connection lifecycle with callbacks [see extensions/muxio-tokio-rpc-client/src/rpc_client.rs30-38](https://github.com/jzombie/rust-muxio/blob/30450c98/see extensions/muxio-tokio-rpc-client/src/rpc_client.rs#L30-L38)

Sources: extensions/muxio-tokio-rpc-client/src/rpc_client.rs:30-226 src/rpc/rpc_dispatcher.rs:130-264


Implementing Platform-Specific Clients

Platform-specific clients implement the RpcServiceCallerInterface trait to provide type-safe RPC invocation. The trait is defined in muxio-rpc-service-caller.

Diagram 3: Client Implementation Architecture

graph TB
    subgraph "Your Client Implementation"
        CLIENT["CustomRpcClient\nPlatform-Specific Lifecycle"]
CALLER_IMPL["impl RpcServiceCallerInterface"]
end
    
    subgraph "Required Integration"
        DISPATCHER["RpcDispatcher\nOwned or Arc Reference"]
MUTEX["Sync Primitive\nMutex, TokioMutex, etc."]
WRITE["write_callback\nPlatform I/O"]
READ["read_loop\nBackground Task/Thread"]
end
    
    subgraph "Trait Methods to Implement"
        CALL["async fn call()\nRpcRequest -> RpcResponse"]
GET_DISP["fn get_dispatcher()\nAccess to Dispatcher"]
end
    
 
   CLIENT --> CALLER_IMPL
 
   CLIENT --> DISPATCHER
 
   CLIENT --> MUTEX
 
   CLIENT --> WRITE
 
   CLIENT --> READ
    
    CALLER_IMPL -.implements.-> CALL
    CALLER_IMPL -.implements.-> GET_DISP
    
 
   CALL --> GET_DISP
 
   GET_DISP --> DISPATCHER

Required Trait Implementation

The RpcServiceCallerInterface trait requires implementing two core methods:

MethodSignaturePurpose
callasync fn call(&self, request: RpcRequest) -> Result<RpcResponse>Send RPC request and await response
get_dispatcherfn get_dispatcher(&self) -> Arc<...>Provide access to underlying dispatcher

Client Lifecycle Considerations

  1. Construction : Initialize RpcDispatcher with write callback
  2. Connection : Establish transport and start read loop
  3. Operation : Handle call() invocations by delegating to dispatcher
  4. Cleanup : Close connection and stop background tasks on drop

Reference Implementation Pattern

For Tokio-based clients, see extensions/muxio-tokio-rpc-client/src/rpc_client.rs:30-226 which demonstrates:

  • Arc-based shared ownership of RpcDispatcher
  • TokioMutex for async-safe access
  • Background task for read loop using tokio::spawn
  • Connection state tracking with callbacks

For WASM-based clients, see extensions/muxio-wasm-rpc-client/src/rpc_wasm_client.rs:15-32 which demonstrates:

  • Single-threaded execution model
  • JavaScript bridge integration
  • Static singleton pattern for WASM constraints

Sources: extensions/muxio-rpc-service-caller/ extensions/muxio-tokio-rpc-client/src/rpc_client.rs:30-226 extensions/muxio-wasm-rpc-client/src/rpc_wasm_client.rs:15-32


graph TB
    subgraph "Your Server Implementation"
        SERVER["CustomRpcServer\nPlatform-Specific Server"]
ENDPOINT_IMPL["impl RpcServiceEndpointInterface"]
HANDLER_REG["Handler Registration\nregister_handler()"]
end
    
    subgraph "Request Processing Pipeline"
        ACCEPT["Accept Connection\nPlatform-Specific"]
DISPATCHER["RpcDispatcher\nPer-Connection"]
READ["Read Loop\nFeed to Dispatcher"]
ENDPOINT["RpcServiceEndpoint\nHandler Registry"]
end
    
    subgraph "Handler Execution"
        DECODE["Decode RpcRequest\nDeserialize Parameters"]
DISPATCH["Dispatch to Handler\nMatch method_id"]
EXECUTE["Execute Handler\nUser Business Logic"]
RESPOND["Encode RpcResponse\nSend via Dispatcher"]
end
    
 
   SERVER --> ENDPOINT_IMPL
 
   SERVER --> HANDLER_REG
    
 
   ACCEPT --> DISPATCHER
 
   DISPATCHER --> READ
 
   READ --> ENDPOINT
    
 
   ENDPOINT --> DECODE
 
   DECODE --> DISPATCH
 
   DISPATCH --> EXECUTE
 
   EXECUTE --> RESPOND
 
   RESPOND --> DISPATCHER

Implementing Platform-Specific Servers

Platform-specific servers implement the RpcServiceEndpointInterface trait to handle incoming RPC requests and dispatch them to registered handlers. The trait is defined in muxio-rpc-service-endpoint.

Diagram 4: Server Implementation Flow

Required Trait Implementation

The RpcServiceEndpointInterface trait provides default implementations but allows customization:

MethodDefault ImplementationOverride When
register_handlerRegisters handler in internal mapCustom dispatch logic needed
handle_finalized_requestDeserializes and invokes handlerCustom request processing required
handle_stream_openRoutes streamed requestsCustom stream handling needed

Server Architecture Patterns

Connection Management:

  • Create new RpcDispatcher instance per connection
  • Create new RpcServiceEndpoint instance per connection
  • Share handler registrations across connections (using Arc)

Handler Registration:

  • Register handlers at server startup or connection time
  • Use RpcServiceEndpoint::register_handler() with closure
  • Handlers receive deserialized parameters and return typed results

Reference Implementation Pattern

For Tokio-based servers, see extensions/muxio-tokio-rpc-server/ which demonstrates:

  • Axum framework integration for HTTP/WebSocket serving
  • Per-connection RpcDispatcher and RpcServiceEndpoint
  • Handler registration with async closures
  • Graceful shutdown handling

Sources: extensions/muxio-rpc-service-endpoint/src/endpoint_interface.rs:65-137 extensions/muxio-tokio-rpc-server/


Feature Flags and Conditional Compilation

The muxio ecosystem uses Cargo feature flags to enable optional dependencies and platform-specific code. This pattern allows extensions to remain lightweight while supporting multiple runtimes.

Diagram 5: Feature Flag Pattern

graph LR
    subgraph "Extension Crate Cargo.toml"
        DEFAULT["default = []\nMinimal Dependencies"]
FEATURE1["tokio_support\ndep:tokio"]
FEATURE2["async_std_support\ndep:async-std"]
FEATURE3["custom_feature\nYour Feature"]
end
    
    subgraph "Conditional Code"
        CFG1["#[cfg(feature = tokio_support)]\nTokio-Specific Impl"]
CFG2["#[cfg(feature = async_std_support)]\nasync-std Impl"]
CFG3["#[cfg(not(any(...)))]\nFallback Impl"]
end
    
 
   DEFAULT --> CFG3
 
   FEATURE1 --> CFG1
 
   FEATURE2 --> CFG2
 
   FEATURE3 --> CFG1

Feature Flag Best Practices

InCargo.toml:

In source code:

Example: muxio-rpc-service-endpoint

The muxio-rpc-service-endpoint crate demonstrates this pattern at extensions/muxio-rpc-service-endpoint/Cargo.toml:23-27:

This enables Tokio-specific mutex types while maintaining compatibility with synchronous code.

Sources: extensions/muxio-rpc-service-endpoint/Cargo.toml:23-27 Cargo.toml:39-65


Integration Patterns

When creating a new platform extension, follow these patterns to ensure compatibility with the muxio ecosystem.

Workspace Integration

Directory Structure:

rust-muxio/
├── extensions/
│   ├── muxio-custom-client/
│   │   ├── Cargo.toml
│   │   └── src/
│   │       └── lib.rs
│   └── muxio-custom-server/
│       ├── Cargo.toml
│       └── src/
│           └── lib.rs

Add to workspace: Cargo.toml:19-31

Dependency Configuration

Workspace Dependencies: Cargo.toml:39-48

Extension Crate Dependencies:

Metadata Inheritance

Use workspace metadata inheritance to maintain consistency Cargo.toml:1-17:

Testing Integration

Create dev dependencies for integration testing:

Reference the example service definitions for end-to-end tests, as demonstrated in extensions/muxio-rpc-service-endpoint/Cargo.toml:29-33

Sources: Cargo.toml:1-71 extensions/muxio-rpc-service-endpoint/Cargo.toml:1-33


Extension Checklist

When implementing a new platform extension, ensure the following:

For Client Extensions

  • Implement RpcServiceCallerInterface trait
  • Create RpcDispatcher with platform-specific write callback
  • Start read loop that feeds bytes to RpcDispatcher::read()
  • Manage connection lifecycle with state tracking
  • Provide async or sync API appropriate for runtime
  • Handle connection errors and cleanup
  • Add feature flags for optional dependencies
  • Document platform-specific requirements

For Server Extensions

  • Implement RpcServiceEndpointInterface trait
  • Accept connections and create per-connection RpcDispatcher
  • Create per-connection RpcServiceEndpoint
  • Register handlers from shared definitions
  • Implement request routing and execution
  • Send responses via RpcDispatcher
  • Handle graceful shutdown
  • Document server setup and configuration

For Transport Extensions

  • Define platform-specific connection type
  • Implement write callback for outgoing frames
  • Implement read mechanism for incoming frames
  • Handle connection establishment and teardown
  • Provide error handling and recovery
  • Document transport-specific limitations

Sources: extensions/muxio-tokio-rpc-client/ extensions/muxio-tokio-rpc-server/ extensions/muxio-wasm-rpc-client/