Exth.Rpc.MessageHandler (Exth v0.4.2)
View SourceHandles JSON-RPC message correlation between requests and responses.
This module provides a mechanism for tracking JSON-RPC messages and their corresponding callers. Each client/transport gets its own handler instance to ensure proper isolation of message handling.
Features
- Per-client message handling
- Automatic request/response correlation
- Batch request support
- Process crash resilience
- Efficient message routing
- Support for any transport implementation
- Subscription handling
Usage
# Create a new handler for a client
{:ok, handler} = MessageHandler.new("wss://eth-mainnet.example.com")
# Send a request and wait for response
{:ok, response} = MessageHandler.call(handler, request, transport)
# Handle incoming responses
:ok = MessageHandler.handle_response(handler, encoded_response)
Message Flow
- Client creates its own handler instance with a unique name
- Client sends a request through
call/4
:- Determines request type (RPC vs Subscription)
- Registers request ID with caller's PID
- Sends request through transport
- Waits for response
- Cleans up registration
- Transport receives response and calls
handle_response/2
:- Deserializes response
- Looks up registered caller
- Sends response to caller
- Caller receives response and continues execution
Error Handling
The handler handles several error cases:
- Process crashes (automatic cleanup via Registry)
- Orphaned responses (no registered caller)
- Timeouts (configurable per call)
- Transport errors (propagated to caller)
Transport Implementation
Any transport implementation can be used with the handler as long as it:
- Implements the
Exth.Transport.Transportable
protocol - Handles asynchronous communication
- Calls
handle_response/2
with encoded responses
Performance Considerations
- Uses Registry for efficient process lookup
- Automatic cleanup of registrations
- No shared state between calls
- Configurable timeouts per call
Summary
Functions
Sends a request through the handler and waits for a response.
Handles an incoming response by routing it to the appropriate caller.
Creates a new handler instance for a client.
Types
@type handler() :: Registry.registry()
@type request_id() :: pos_integer() | String.t()
@type request_type() :: :rpc | :subscription
Functions
@spec call( handler(), [Exth.Rpc.Request.t()], Exth.Transport.Transportable.t(), timeout() ) :: {:ok, [Exth.Rpc.Response.t()]} | {:error, term()}
Sends a request through the handler and waits for a response.
This function handles the full request/response cycle:
- Determines request type (RPC vs Subscription)
- Registers the request ID with the caller's PID
- Sends the request through the transport
- Waits for the response
- Cleans up the registration
Parameters
handler
- The handler instance to userequests
- The request(s) to sendtransport
- The transport to usetimeout
- Optional timeout in milliseconds (default: 5000)
Returns
{:ok, response}
- Successful response{:error, term()}
- Request failed
Example
{:ok, response} = MessageHandler.call(handler, request, transport)
Handles an incoming response by routing it to the appropriate caller.
This function is called by the transport when a response is received. It:
- Deserializes the response
- Looks up the registered caller
- Sends the response to the caller
For subscription events, it:
- Extracts the subscription ID from the notification
- Looks up the process that owns the subscription
- Delivers the event to that process
Parameters
handler
- The handler instance to useencoded_response
- The encoded JSON-RPC response
Returns
:ok
- Response was handled:error
- Response could not be handled
Example
:ok = MessageHandler.handle_response(handler, encoded_response)
Creates a new handler instance for a client.
Each client should have its own handler instance to ensure proper isolation of message handling. The handler is identified by a unique name derived from the RPC URL.
Parameters
rpc_url
- The RPC URL to use as the base for the handler name
Returns
{:ok, handler()}
- Successfully created handler{:error, term()}
- Failed to create handler
Example
{:ok, handler} = MessageHandler.new("wss://eth-mainnet.example.com")