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

Relevant source files

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

FieldTypeDescription
rpc_method_idu64Unique identifier for the remote method to invoke. Typically a hash of the method name.
rpc_param_bytesOption<Vec<u8>>Optional encoded metadata (function parameters). Transmitted in RpcHeader.rpc_metadata_bytes.
rpc_prebuffered_payload_bytesOption<Vec<u8>>Optional payload sent immediately after the header. Used for single-frame RPCs.
is_finalizedboolIf 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:

  1. rpc_method_id is copied directly to RpcHeader.rpc_method_id
  2. rpc_param_bytes is unwrapped (or defaults to empty) and stored in RpcHeader.rpc_metadata_bytes
  3. A unique rpc_request_id is generated and assigned to RpcHeader.rpc_request_id
  4. RpcMessageType::Call is set in RpcHeader.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

FieldTypeDescription
rpc_request_idu32The request header ID this response corresponds to. Must match the initiating request.
rpc_method_idu64The method ID associated with this response. Should match the original request.
rpc_result_statusOption<u8>Optional result status byte (e.g., 0 for success). Embedded in response metadata.
rpc_prebuffered_payload_bytesOption<Vec<u8>>Optional payload to return with the response.
is_finalizedboolIf 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:

  1. rpc_request_id is copied to RpcHeader.rpc_request_id for correlation
  2. rpc_method_id is copied to RpcHeader.rpc_method_id
  3. rpc_result_status is converted to a single-byte vector (or empty) and stored in RpcHeader.rpc_metadata_bytes
  4. RpcMessageType::Response is set in RpcHeader.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

FieldTypeDescription
rpc_msg_typeRpcMessageTypeThe type of RPC message (Call or Response).
rpc_request_idu32Unique identifier for correlation. For calls, this is generated; for responses, this matches the request.
rpc_method_idu64Identifier (or hash) of the method being invoked.
rpc_metadata_bytesVec<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:

  1. Client creates RpcRequest and passes to call()
  2. Dispatcher converts to RpcHeader, assigns unique rpc_request_id, and serializes
  3. Server receives bytes via read_bytes(), reconstructs as RpcRequest, and queues by rpc_request_id
  4. Server application polls queue, processes request, and creates RpcResponse
  5. Dispatcher converts to RpcHeader (preserving rpc_request_id), and serializes
  6. 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&lt;(u32, RpcRequest)&gt;"]
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:

  1. read_bytes() decodes incoming frames into RpcStreamEvent variants
  2. The catch-all handler processes each event type:
    • Header: Creates a new RpcRequest and pushes to queue
    • PayloadChunk: Appends bytes to existing request's rpc_prebuffered_payload_bytes
    • End: Sets is_finalized to true
  3. Application code polls the queue using get_rpc_request(), is_rpc_request_finalized(), and delete_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_id and rpc_metadata_bytes (converted to rpc_param_bytes), creates RpcRequest with is_finalized: false, and pushes to queue
  • On PayloadChunk event: Finds matching request by rpc_request_id, creates or extends rpc_prebuffered_payload_bytes
  • On End event: Finds matching request by rpc_request_id, sets is_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 FieldRpcHeader FieldTransformation
rpc_method_idrpc_method_idDirect copy
rpc_param_bytesrpc_metadata_bytesUnwrap or empty vector
N/Arpc_request_idGenerated by dispatcher
N/Arpc_msg_typeSet to RpcMessageType::Call

Sources: src/rpc/rpc_dispatcher.rs:239-257

RpcHeader → RpcRequest (Server Receive)

RpcHeader FieldRpcRequest FieldTransformation
rpc_method_idrpc_method_idDirect copy
rpc_metadata_bytesrpc_param_bytesWrap in Some() if non-empty
N/Arpc_prebuffered_payload_bytesInitially None, populated by chunks
N/Ais_finalizedInitially false, set by End event

Sources: src/rpc/rpc_dispatcher.rs:133-138

RpcResponse → RpcHeader (Server Respond)

RpcResponse FieldRpcHeader FieldTransformation
rpc_request_idrpc_request_idDirect copy
rpc_method_idrpc_method_idDirect copy
rpc_result_statusrpc_metadata_bytesConvert to single-byte vector or empty
N/Arpc_msg_typeSet to RpcMessageType::Response

Sources: src/rpc/rpc_dispatcher.rs:307-319

RpcHeader → RpcResponse (Client Receive)

RpcHeader FieldRpcResponse FieldTransformation
rpc_request_idrpc_request_idDirect copy
rpc_method_idrpc_method_idDirect copy
rpc_metadata_bytesrpc_result_statusFirst byte if non-empty, else None
N/Arpc_prebuffered_payload_bytesInitially None, populated by chunks
N/Ais_finalizedHardcoded 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:

  1. Prebuffered mode: The entire payload is provided in rpc_prebuffered_payload_bytes and is_finalized is set to true. The dispatcher sends the complete message in one operation.

  2. Streaming mode: rpc_prebuffered_payload_bytes is None or partial, and is_finalized is false. The dispatcher returns an RpcStreamEncoder that allows incremental writes via write_bytes(), followed by flush() and end_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


The following types work in conjunction with RpcRequest, RpcResponse, and RpcHeader:

TypePurposePage Reference
RpcMessageTypeEnum distinguishing Call vs. ResponseN/A
RpcStreamEncoderIncremental payload writer for streaming modeBinary Framing Protocol
RpcStreamEventEvent-based callbacks for incoming streamsRPC Dispatcher
RpcDispatcherMain coordinator for request/response lifecycleRPC Dispatcher
RpcMethodPrebufferedTrait for compile-time method definitionsService 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