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.

Request and Response Types

Loading…

Request and Response Types

Relevant source files

Purpose and Scope

This document defines the core data structures used to represent RPC requests and responses in the muxio framework: RpcRequest, RpcResponse, and RpcHeader. These types provide a runtime-agnostic, schemaless representation of method invocations and their corresponding replies. They serve as the fundamental building blocks for all RPC communication in the system.

For information about how these types are processed and routed, see RPC Dispatcher. For information about how they are serialized and transmitted at the binary level, see Binary Framing Protocol.


Type Hierarchy and Relationships

The request-response system is built on three primary types that work together to enable method invocation and correlation:

Sources:

graph TB
    subgraph "Application Layer"
        USER["User Code\nService Definitions"]
end
    
    subgraph "RPC Protocol Layer Types"
        REQ["RpcRequest\nsrc/rpc/rpc_request_response.rs"]
RESP["RpcResponse\nsrc/rpc/rpc_request_response.rs"]
HDR["RpcHeader\nsrc/rpc/rpc_internals/rpc_header.rs"]
end
    
    subgraph "Processing Components"
        DISP["RpcDispatcher::call()\nRpcDispatcher::respond()"]
SESSION["RpcRespondableSession"]
end
    
 
   USER -->|constructs| REQ
 
   USER -->|constructs| RESP
    
 
   REQ -->|converted to| HDR
 
   RESP -->|converted to| HDR
    
 
   HDR -->|can be converted back to| RESP
    
 
   DISP -->|processes| REQ
 
   DISP -->|processes| RESP
    
 
   DISP -->|delegates to| SESSION
    
    style REQ fill:#f9f9f9
    style RESP fill:#f9f9f9
    style HDR fill:#f9f9f9

RpcHeader Structure

RpcHeader is the internal protocol-level representation of RPC metadata transmitted in frame headers. It contains all information necessary to route and identify an RPC message.

Field Description

FieldTypePurpose
rpc_msg_typeRpcMessageTypeDiscriminates between Call, Response, Event, etc.
rpc_request_idu32Unique identifier for request-response correlation
rpc_method_idu64Identifier (typically hash) of the method being invoked
rpc_metadata_bytesVec<u8>Schemaless encoded parameters or status information

Type Definition

The complete structure is defined at src/rpc/rpc_internals/rpc_header.rs:3-24:

Metadata Semantics

The rpc_metadata_bytes field has different interpretations depending on the message type:

  • For Calls : Contains serialized method parameters (e.g., bitcode-encoded arguments)
  • For Responses : Contains a single-byte result status code, or is empty if no status is provided
  • Empty Vector : Valid and indicates no metadata is present

Sources:


RpcRequest Structure

RpcRequest represents an outbound RPC call initiated by a client. It is constructed by user code and passed to RpcDispatcher::call() for transmission.

Field Description

FieldTypePurpose
rpc_method_idu64Unique method identifier (typically xxhash of method name)
rpc_param_bytesOption<Vec<u8>>Serialized method parameters (None if no parameters)
rpc_prebuffered_payload_bytesOption<Vec<u8>>Optional payload to send after header
is_finalizedboolIf true, stream ends immediately after header+payload

Type Definition

Defined at src/rpc/rpc_request_response.rs:9-33:

Usage Patterns

Finalized Request (Single-Frame RPC)

When the entire request is known upfront:

This pattern is demonstrated at tests/rpc_dispatcher_tests.rs:42-49

Streaming Request (Multi-Frame RPC)

When payload will be written incrementally:

Conversion to RpcHeader

When RpcDispatcher::call() is invoked, the RpcRequest is converted to an RpcHeader at src/rpc/rpc_dispatcher.rs:249-257:

Sources:


RpcResponse Structure

RpcResponse represents the reply to a prior RPC request. It is constructed on the server side and passed to RpcDispatcher::respond() for transmission back to the client.

Field Description

FieldTypePurpose
rpc_request_idu32Must match the original request’s rpc_request_id for correlation
rpc_method_idu64Should match the original request’s rpc_method_id
rpc_result_statusOption<u8>Optional status byte (by convention, 0 = success)
rpc_prebuffered_payload_bytesOption<Vec<u8>>Serialized response payload
is_finalizedboolIf true, stream ends immediately after header+payload

Type Definition

Defined at src/rpc/rpc_request_response.rs:40-76:

Construction from RpcHeader

The from_rpc_header() method provides a convenience constructor for server-side response creation at src/rpc/rpc_request_response.rs:90-103:

Usage Example

Server-side response construction from tests/rpc_dispatcher_tests.rs:161-167:

Conversion to RpcHeader

When RpcDispatcher::respond() is invoked, the RpcResponse is converted to an RpcHeader at src/rpc/rpc_dispatcher.rs:307-319:

Sources:


sequenceDiagram
    participant Client as "Client Code"
    participant Disp as "RpcDispatcher"
    participant IDGen as "next_rpc_request_id\n(u32 counter)"
    participant Queue as "rpc_request_queue\nArc<Mutex<VecDeque>>"
    
    Note over Client,Queue: Request Path
    Client->>Disp: call(RpcRequest)
    Disp->>IDGen: Allocate ID
    IDGen-->>Disp: rpc_request_id = N
    Disp->>Disp: Build RpcHeader with\nrpc_request_id=N
    Note over Disp: Transmit request frames...
    
    Note over Client,Queue: Response Path
    Disp->>Disp: Receive response frames
    Disp->>Disp: Parse RpcHeader from frames
    Disp->>Queue: Find request by rpc_request_id=N
    Queue-->>Disp: Matched RpcRequest entry
    Disp->>Disp: Invoke response handler
    Disp->>Queue: Remove entry on End/Error

Request-Response Correlation Mechanism

The system correlates requests and responses using the rpc_request_id field, which is managed by the dispatcher’s monotonic ID generator.

ID Generation

The dispatcher maintains a monotonic counter at src/rpc/rpc_dispatcher.rs42:

Each call increments this counter using increment_u32_id() at src/rpc/rpc_dispatcher.rs:241-242:

Request Queue Management

Inbound requests (responses from the remote peer) are tracked in a shared queue at src/rpc/rpc_dispatcher.rs50:

The queue is populated by the catch-all response handler at src/rpc/rpc_dispatcher.rs:122-141:

  • Header Event : Creates new RpcRequest entry with rpc_request_id
  • PayloadChunk Event : Appends bytes to matching request’s payload
  • End Event : Marks request as finalized

Requests can be retrieved and removed using:

Sources:


graph LR
    subgraph "Client Side - Outbound Call"
        C1["User constructs\nRpcRequest"]
C2["RpcDispatcher::call()"]
C3["Convert to RpcHeader\nrpc_msg_type=Call"]
C4["RpcStreamEncoder\nSerialize + Frame"]
C5["Bytes on wire"]
end
    
    subgraph "Server Side - Inbound Call"
        S1["Bytes received"]
S2["RpcSession::read_bytes()"]
S3["Decode to RpcHeader"]
S4["RpcStreamEvent::Header\n+PayloadChunk\n+End"]
S5["Reconstruct RpcRequest\nin rpc_request_queue"]
S6["User retrieves\ndelete_rpc_request()"]
end
    
    subgraph "Server Side - Outbound Response"
        R1["User constructs\nRpcResponse"]
R2["RpcDispatcher::respond()"]
R3["Convert to RpcHeader\nrpc_msg_type=Response"]
R4["RpcStreamEncoder\nSerialize + Frame"]
R5["Bytes on wire"]
end
    
    subgraph "Client Side - Inbound Response"
        R6["Bytes received"]
R7["RpcSession::read_bytes()"]
R8["Decode to RpcHeader"]
R9["RpcStreamEvent fired\nto response_handler"]
R10["User processes\nresponse payload"]
end
    
 
   C1 --> C2
 
   C2 --> C3
 
   C3 --> C4
 
   C4 --> C5
    
    C5 -.network.-> S1
 
   S1 --> S2
 
   S2 --> S3
 
   S3 --> S4
 
   S4 --> S5
 
   S5 --> S6
    
 
   R1 --> R2
 
   R2 --> R3
 
   R3 --> R4
 
   R4 --> R5
    
    R5 -.network.-> R6
 
   R6 --> R7
 
   R7 --> R8
 
   R8 --> R9
 
   R9 --> R10

Data Flow Through Type Transformations

The following diagram illustrates how request and response data flows through type transformations:

Sources:


Field Semantics and Special Cases

Prebuffered Payloads

Both RpcRequest and RpcResponse support rpc_prebuffered_payload_bytes:

  • Purpose : Allows sending the entire payload in a single transmission without manual streaming
  • Transmission : Sent immediately after header via encoder.write_bytes() at src/rpc/rpc_dispatcher.rs:270-276 and src/rpc/rpc_dispatcher.rs:327-329
  • Use Case : Suitable for small to medium-sized payloads where chunking overhead is undesirable

Finalization Flag

The is_finalized field controls stream lifecycle:

Result Status Conventions

While the core library does not enforce semantics for rpc_result_status, the following conventions are commonly used:

ValueMeaning
Some(0)Success
Some(1)Generic error
Some(2+)Custom error codes
NoneNo status information

This convention is referenced in the documentation at src/rpc/rpc_request_response.rs:61-62

Sources:


Complete Request-Response Lifecycle Example

The following table illustrates a complete request-response cycle from the test suite at tests/rpc_dispatcher_tests.rs:42-198:

StepLocationActionData Structure
1ClientConstruct requestRpcRequest { method_id: ADD_METHOD_ID, param_bytes: Some(encoded), prebuffered_payload: None, is_finalized: true }
2ClientCall dispatcherclient_dispatcher.call(rpc_request, 4, on_emit, on_response, true)
3ClientConvert to headerRpcHeader { msg_type: Call, request_id: 1, method_id: ADD_METHOD_ID, metadata: encoded_params }
4TransportTransmit bytesBinary frames written to outgoing_buf
5ServerReceive bytesserver_dispatcher.read_bytes(chunk)
6ServerDecode to eventsRpcStreamEvent::Header, RpcStreamEvent::End
7ServerReconstruct requestEntry added to rpc_request_queue with (request_id, RpcRequest)
8ServerRetrieve requestserver_dispatcher.delete_rpc_request(request_id)
9ServerProcess and respondRpcResponse { request_id: 1, method_id: ADD_METHOD_ID, result_status: Some(0), payload: encoded_result, is_finalized: true }
10ServerConvert to headerRpcHeader { msg_type: Response, request_id: 1, method_id: ADD_METHOD_ID, metadata: [0] }
11TransportTransmit responseBinary frames via server_dispatcher.respond()
12ClientReceive responseclient_dispatcher.read_bytes() routes to on_response handler
13ClientProcess resultUser code decodes payload from RpcStreamEvent::PayloadChunk

Sources: