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
Relevant source files
- src/rpc/rpc_dispatcher.rs
- src/rpc/rpc_internals/rpc_header.rs
- src/rpc/rpc_internals/rpc_respondable_session.rs
- src/rpc/rpc_request_response.rs
- tests/rpc_dispatcher_tests.rs
Purpose and Scope
This page documents the three core data structures used to represent RPC messages in the muxio core library: RpcRequest, RpcResponse, and RpcHeader. These types define the wire-level representation of remote procedure calls and their responses, serving as the primary interface between application code and the RPC dispatcher.
For information about how these types are processed by the dispatcher, see RPC Dispatcher. For details about the underlying binary framing protocol, see Binary Framing Protocol. For higher-level service definitions that encode/decode these structures, see Service Definitions.
Sources: src/rpc/rpc_request_response.rs:1-105 src/rpc/rpc_internals/rpc_header.rs:1-25
Type Hierarchy and Relationships
The three types form a layered abstraction: RpcRequest and RpcResponse are high-level types used by application code, while RpcHeader is an internal protocol-level type used for frame transmission.
Diagram: Type Conversion Flow
graph TB
subgraph "Application Layer"
RpcRequest["RpcRequest\n(client-side)"]
RpcResponse["RpcResponse\n(server-side)"]
end
subgraph "Protocol Layer"
RpcHeader["RpcHeader\n(wire format)"]
end
subgraph "Transport Layer"
BinaryFrames["Binary Frames\n(network transmission)"]
end
RpcRequest -->|RpcDispatcher::call| RpcHeader
RpcResponse -->|RpcDispatcher::respond| RpcHeader
RpcHeader -->|serialize| BinaryFrames
BinaryFrames -->|deserialize| RpcHeader
RpcHeader -->|queue processing| RpcRequest
RpcHeader -->|from_rpc_header| RpcResponse
The diagram shows how application-level types are converted to protocol-level types for transmission, and reconstructed on the receiving side. RpcDispatcher::call() converts RpcRequest to RpcHeader, while RpcDispatcher::respond() converts RpcResponse to RpcHeader. On the receive side, incoming headers are queued as RpcRequest structures or converted to RpcResponse via from_rpc_header().
Sources: src/rpc/rpc_dispatcher.rs:227-286 src/rpc/rpc_dispatcher.rs:298-337 src/rpc/rpc_request_response.rs:90-103
RpcRequest Structure
RpcRequest represents an outbound RPC call initiated by a client. It encapsulates the method identifier, encoded parameters, optional payload data, and a finalization flag.
Field Definitions
| Field | Type | Description |
|---|---|---|
rpc_method_id | u64 | Unique identifier for the remote method to invoke. Typically a hash of the method name. |
rpc_param_bytes | Option<Vec<u8>> | Optional encoded metadata (function parameters). Transmitted in RpcHeader.rpc_metadata_bytes. |
rpc_prebuffered_payload_bytes | Option<Vec<u8>> | Optional payload sent immediately after the header. Used for single-frame RPCs. |
is_finalized | bool | If true, the stream is closed immediately after sending header and payload. |
Sources: src/rpc/rpc_request_response.rs:10-33
Usage in RpcDispatcher::call()
When a client invokes RpcDispatcher::call(), the RpcRequest is converted to an internal RpcHeader:
rpc_method_idis copied directly toRpcHeader.rpc_method_idrpc_param_bytesis unwrapped (or defaults to empty) and stored inRpcHeader.rpc_metadata_bytes- A unique
rpc_request_idis generated and assigned toRpcHeader.rpc_request_id RpcMessageType::Callis set inRpcHeader.rpc_msg_type
If rpc_prebuffered_payload_bytes is present, it is immediately written to the stream encoder. If is_finalized is true, the stream is flushed and ended.
Sources: src/rpc/rpc_dispatcher.rs:239-283
Example Construction
Sources: tests/rpc_dispatcher_tests.rs:42-49
RpcResponse Structure
RpcResponse represents a reply to a prior RPC request. It contains the original request ID for correlation, a result status, and optional payload data.
Field Definitions
| Field | Type | Description |
|---|---|---|
rpc_request_id | u32 | The request header ID this response corresponds to. Must match the initiating request. |
rpc_method_id | u64 | The method ID associated with this response. Should match the original request. |
rpc_result_status | Option<u8> | Optional result status byte (e.g., 0 for success). Embedded in response metadata. |
rpc_prebuffered_payload_bytes | Option<Vec<u8>> | Optional payload to return with the response. |
is_finalized | bool | If true, the response stream is closed immediately after sending. |
Sources: src/rpc/rpc_request_response.rs:41-76
Usage in RpcDispatcher::respond()
When a server invokes RpcDispatcher::respond(), the RpcResponse is converted to an internal RpcHeader:
rpc_request_idis copied toRpcHeader.rpc_request_idfor correlationrpc_method_idis copied toRpcHeader.rpc_method_idrpc_result_statusis converted to a single-byte vector (or empty) and stored inRpcHeader.rpc_metadata_bytesRpcMessageType::Responseis set inRpcHeader.rpc_msg_type
If rpc_prebuffered_payload_bytes is present, it is immediately written to the stream encoder. If is_finalized is true, the stream is flushed and ended.
Sources: src/rpc/rpc_dispatcher.rs:307-335
Example Construction
Sources: tests/rpc_dispatcher_tests.rs:161-167
from_rpc_header() Factory Method
The RpcResponse::from_rpc_header() static method constructs a response from a received RpcHeader. This is typically called on the server side when processing a new request. The method interprets the first byte of rpc_metadata_bytes as the result status, if present.
Sources: src/rpc/rpc_request_response.rs:90-103
RpcHeader Structure
RpcHeader is an internal protocol-level type that represents the framed representation of an RPC message. It is not directly exposed to application code, but is created by RpcDispatcher when encoding RpcRequest or RpcResponse for transmission.
Field Definitions
| Field | Type | Description |
|---|---|---|
rpc_msg_type | RpcMessageType | The type of RPC message (Call or Response). |
rpc_request_id | u32 | Unique identifier for correlation. For calls, this is generated; for responses, this matches the request. |
rpc_method_id | u64 | Identifier (or hash) of the method being invoked. |
rpc_metadata_bytes | Vec<u8> | Schemaless metadata. For calls, contains encoded parameters; for responses, contains result status. |
Sources: src/rpc/rpc_internals/rpc_header.rs:5-24
Metadata Interpretation
The rpc_metadata_bytes field has different semantics depending on the message type:
- For Call messages: Contains the encoded function parameters (from
RpcRequest.rpc_param_bytes) - For Response messages: Contains a single-byte result status (from
RpcResponse.rpc_result_status), or is empty
This dual-purpose design allows the framing protocol to remain agnostic to the payload structure while providing a small metadata slot for control information.
Sources: src/rpc/rpc_dispatcher.rs:250-257 src/rpc/rpc_dispatcher.rs:313-319
sequenceDiagram
participant App as "Application Code"
participant Dispatcher as "RpcDispatcher"
participant Queue as "rpc_request_queue"
participant Wire as "Wire Protocol"
Note over App,Wire: Client-side Call
App->>Dispatcher: call(RpcRequest)
Dispatcher->>Dispatcher: Convert to RpcHeader\n(assign rpc_request_id)
Dispatcher->>Wire: Serialize RpcHeader\n+ payload bytes
Note over App,Wire: Server-side Receive
Wire->>Dispatcher: read_bytes()
Dispatcher->>Queue: Push (rpc_request_id, RpcRequest)
Queue->>App: Poll for finalized requests
Note over App,Wire: Server-side Respond
App->>Dispatcher: respond(RpcResponse)
Dispatcher->>Dispatcher: Convert to RpcHeader\n(copy rpc_request_id)
Dispatcher->>Wire: Serialize RpcHeader\n+ payload bytes
Note over App,Wire: Client-side Receive
Wire->>Dispatcher: read_bytes()
Dispatcher->>Dispatcher: Invoke response handler\n(match by rpc_request_id)
Dispatcher->>App: RpcStreamEvent callbacks
Data Flow and Transformations
The following diagram illustrates how data flows through the three types during a complete request-response cycle.
Diagram: Request-Response Data Flow
This sequence diagram shows the complete lifecycle of an RPC call:
- Client creates
RpcRequestand passes tocall() - Dispatcher converts to
RpcHeader, assigns uniquerpc_request_id, and serializes - Server receives bytes via
read_bytes(), reconstructs asRpcRequest, and queues byrpc_request_id - Server application polls queue, processes request, and creates
RpcResponse - Dispatcher converts to
RpcHeader(preservingrpc_request_id), and serializes - Client receives bytes, matches by
rpc_request_id, and invokes registered response handler
Sources: src/rpc/rpc_dispatcher.rs:227-286 src/rpc/rpc_dispatcher.rs:298-337 src/rpc/rpc_dispatcher.rs:362-374
graph LR
subgraph "RpcDispatcher"
ReadBytes["read_bytes()"]
Handler["catch_all_response_handler"]
Queue["rpc_request_queue\nVecDeque<(u32, RpcRequest)>"]
end
subgraph "Stream Events"
Header["RpcStreamEvent::Header"]
Chunk["RpcStreamEvent::PayloadChunk"]
End["RpcStreamEvent::End"]
end
ReadBytes -->|decode| Header
ReadBytes -->|decode| Chunk
ReadBytes -->|decode| End
Header -->|push_back| Queue
Chunk -->|append bytes| Queue
End -->|set is_finalized| Queue
Handler -->|processes| Header
Handler -->|processes| Chunk
Handler -->|processes| End
Request Queue Processing
On the receiving side, incoming RpcHeader frames are converted to RpcRequest structures and stored in a queue for processing. The dispatcher maintains this queue internally using a catch-all response handler.
Diagram: Request Queue Processing
The diagram shows how incoming stream events populate the request queue:
read_bytes()decodes incoming frames intoRpcStreamEventvariants- The catch-all handler processes each event type:
Header: Creates a newRpcRequestand pushes to queuePayloadChunk: Appends bytes to existing request'srpc_prebuffered_payload_bytesEnd: Setsis_finalizedtotrue
- Application code polls the queue using
get_rpc_request(),is_rpc_request_finalized(), anddelete_rpc_request()
Sources: src/rpc/rpc_dispatcher.rs:99-209
Queue Event Handling
The catch-all response handler installs a closure that processes incoming stream events and updates the queue:
- On Header event: Extracts
rpc_method_idandrpc_metadata_bytes(converted torpc_param_bytes), createsRpcRequestwithis_finalized: false, and pushes to queue - On PayloadChunk event: Finds matching request by
rpc_request_id, creates or extendsrpc_prebuffered_payload_bytes - On End event: Finds matching request by
rpc_request_id, setsis_finalized: true - On Error event: Logs error (future: may remove from queue or mark as errored)
Sources: src/rpc/rpc_dispatcher.rs:122-207
Field Mapping Reference
The following table shows how fields are mapped between types during conversion:
RpcRequest → RpcHeader (Client Call)
| RpcRequest Field | RpcHeader Field | Transformation |
|---|---|---|
rpc_method_id | rpc_method_id | Direct copy |
rpc_param_bytes | rpc_metadata_bytes | Unwrap or empty vector |
| N/A | rpc_request_id | Generated by dispatcher |
| N/A | rpc_msg_type | Set to RpcMessageType::Call |
Sources: src/rpc/rpc_dispatcher.rs:239-257
RpcHeader → RpcRequest (Server Receive)
| RpcHeader Field | RpcRequest Field | Transformation |
|---|---|---|
rpc_method_id | rpc_method_id | Direct copy |
rpc_metadata_bytes | rpc_param_bytes | Wrap in Some() if non-empty |
| N/A | rpc_prebuffered_payload_bytes | Initially None, populated by chunks |
| N/A | is_finalized | Initially false, set by End event |
Sources: src/rpc/rpc_dispatcher.rs:133-138
RpcResponse → RpcHeader (Server Respond)
| RpcResponse Field | RpcHeader Field | Transformation |
|---|---|---|
rpc_request_id | rpc_request_id | Direct copy |
rpc_method_id | rpc_method_id | Direct copy |
rpc_result_status | rpc_metadata_bytes | Convert to single-byte vector or empty |
| N/A | rpc_msg_type | Set to RpcMessageType::Response |
Sources: src/rpc/rpc_dispatcher.rs:307-319
RpcHeader → RpcResponse (Client Receive)
| RpcHeader Field | RpcResponse Field | Transformation |
|---|---|---|
rpc_request_id | rpc_request_id | Direct copy |
rpc_method_id | rpc_method_id | Direct copy |
rpc_metadata_bytes | rpc_result_status | First byte if non-empty, else None |
| N/A | rpc_prebuffered_payload_bytes | Initially None, populated by chunks |
| N/A | is_finalized | Hardcoded to false (non-determinable from header alone) |
Sources: src/rpc/rpc_request_response.rs:90-103
Prebuffered vs. Streaming Payloads
Both RpcRequest and RpcResponse support two modes of payload transmission:
-
Prebuffered mode: The entire payload is provided in
rpc_prebuffered_payload_bytesandis_finalizedis set totrue. The dispatcher sends the complete message in one operation. -
Streaming mode:
rpc_prebuffered_payload_bytesisNoneor partial, andis_finalizedisfalse. The dispatcher returns anRpcStreamEncoderthat allows incremental writes viawrite_bytes(), followed byflush()andend_stream().
Prebuffered Example
Sources: tests/rpc_dispatcher_tests.rs:42-49
Streaming Example
Sources: src/rpc/rpc_dispatcher.rs:298-337
Thread Safety and Poisoning
The rpc_request_queue is protected by a Mutex and shared via Arc. If the mutex becomes poisoned (due to a panic while holding the lock), the dispatcher will panic immediately rather than attempt recovery. This design choice ensures:
- Poisoned state is treated as a critical failure
- Inconsistent queue state does not lead to incorrect request routing
- Fast failure provides better debugging signals
The poisoning check occurs in:
- The catch-all response handler when updating the queue
read_bytes()when listing active request IDs- Queue accessor methods (
get_rpc_request(),is_rpc_request_finalized(),delete_rpc_request())
Sources: src/rpc/rpc_dispatcher.rs:104-118 src/rpc/rpc_dispatcher.rs:367-371
Related Types
The following types work in conjunction with RpcRequest, RpcResponse, and RpcHeader:
| Type | Purpose | Page Reference |
|---|---|---|
RpcMessageType | Enum distinguishing Call vs. Response | N/A |
RpcStreamEncoder | Incremental payload writer for streaming mode | Binary Framing Protocol |
RpcStreamEvent | Event-based callbacks for incoming streams | RPC Dispatcher |
RpcDispatcher | Main coordinator for request/response lifecycle | RPC Dispatcher |
RpcMethodPrebuffered | Trait for compile-time method definitions | Service Definitions |
Sources: src/rpc/rpc_request_response.rs:1-105 src/rpc/rpc_internals/rpc_header.rs:1-25
Dismiss
Refresh this wiki
Enter email to refresh