Connection Lifecycle
Every TrevRPC runtime exposes a long-lived application Channel or RpcChannel. Reconnection is part of the channel contract, not an optional wrapper or feature flag. Applications create one channel, share it across generated clients, and close it during shutdown.
Raw transports that own exactly one connection or WebTransport session are advanced-only. They exist for integrations, benchmarks, diagnostics, and deterministic transport tests, not routine application use.
Call Contract
Each ready connection is a generation. A new unary or streaming RPC snapshots exactly one ready generation when it starts.
- The RPC remains attached to that generation for its entire lifetime.
- If the generation dies, every in-flight RPC on it fails through the runtime's normal transport error path.
- TrevRPC never retries, replays, resumes, or moves a call to a later generation.
- The channel reconnects in the background for future calls.
- A new call made while the channel is connecting or reconnecting fails immediately with
Unavailableor the runtime's equivalent. - Applications call the runtime readiness API before issuing work when waiting is intentional.
- A remote RPC
Unavailablestatus never authorizes reconnecting or replaying that RPC. - Explicit channel close is terminal and stops reconnection.
Applications own RPC retry policy. This keeps idempotency, duplicate side effects, and request replay decisions above the connection lifecycle.
Routine APIs
| Runtime | Application API | Coverage | Readiness |
|---|---|---|---|
| Rust | trevrpc::client::Channel::connect |
Native Quinn QUIC | wait_until_ready |
| Rust | trevrpc::client::Channel::connect_webtransport |
WebTransport | wait_until_ready |
| Go | trevrpc.Dial(ctx, target, trevrpc.DialOptions) returning *trevrpc.Channel |
Address targets use native QUIC; https:// targets use WebTransport |
WaitUntilReady |
| C | trevrpc_channel_connect returning trevrpc_channel* |
Native MsQuic QUIC | trevrpc_channel_wait_ready |
| Kotlin | NettyRpcChannel.nativeQuic(config) returning RpcChannel |
Netty native QUIC | awaitReady |
| Kotlin | NettyRpcChannel.http3(config) returning RpcChannel |
Netty ordinary HTTP/3 | awaitReady |
| Kotlin | CronetRpcChannel.create(...) returning RpcChannel |
Provider-owned Cronet HTTP/3 | awaitReady |
| Browser JavaScript | connect(url, options) from trevrpc-js, returning Channel |
Browser WebTransport | waitUntilReady |
| Node JavaScript | Root connect(...) or Channel from trevrpc-js/node |
Native MsQuic | waitUntilReady |
Generated clients accept these channels directly. Generated C methods directly accept trevrpc_channel*; there are no separate channel-specific method variants.
Rust connect, Go Dial, C trevrpc_channel_connect, and JavaScript connect wait for the first ready generation; bound initial setup with the runtime's normal future, context, timeout, cancellation, or constructor option. Kotlin Netty factories return a channel whose initial state may still be connecting, so call awaitReady before initial work when waiting is intentional. After creation, calls fail fast during a reconnect unless application code explicitly waits for readiness.
Cronet owns its physical connection pool and reconnection behavior. CronetRpcChannel provides the same long-lived application surface, but TrevRPC does not claim control over Cronet's internal connection generations, path migration, or TLS ticket policy. TrevRPC still never retries or replays an RPC.
Reconnect Backoff
Every TrevRPC-controlled reconnect loop uses the same fixed reconnect defaults:
- Initial delay: 100 ms.
- Multiplier: 2.
- Jitter: 20 percent.
- Maximum delay: 30 seconds.
Backoff resets after a generation becomes ready. Routine constructors do not expose reconnect-policy tuning. Advanced and runtime-test paths may override it when deterministic timing or specialized integration behavior requires that control.
Lifecycle state and generation are observable where the runtime exposes them. Runtime event hooks may report connection loss, reconnect failure, readiness, and close. Keep callbacks non-blocking; bounded event queues may coalesce or drop detail for a persistently slow observer while the latest channel state remains authoritative.
Advanced APIs
| Runtime | Advanced API | Purpose |
|---|---|---|
| Rust | trevrpc::advanced::RawQuinnTransport |
One established Quinn connection |
| Rust | trevrpc::advanced::RawWebTransport |
One established WebTransport session |
| Rust | trevrpc::advanced::ChannelOperations |
Quinn endpoint address and rebind operations |
| Rust | trevrpc::advanced::ChannelConfigOperations |
Reconnect-policy tuning for specialized and test paths |
| Rust | trevrpc::advanced::connect_webtransport_channel_with_request |
Channel creation with a custom WebTransport CONNECT request |
| Go | trevrpc.Advanced.NewRawQUICClient |
One caller-owned quic-go connection |
| Go | trevrpc.Advanced.DialRawWebTransport |
Dial one WebTransport session |
| Go | trevrpc.Advanced.NewRawWebTransportClient |
One caller-owned WebTransport session |
| Go | trevrpc.Advanced.Channel(channel).AddPath |
Path operations on the current native QUIC generation |
| Go | trevrpc.Advanced.Channel(channel).RawWebTransportSession |
Inspect the current WebTransport session |
| C | trevrpc_raw_client APIs from trevrpc_raw.h |
Single-connection native QUIC and WebTransport operations |
| C | trevrpc_channel_options_set_backoff from trevrpc_raw.h |
Advanced channel backoff tuning |
| Kotlin | zip.trev.trevrpc.netty.advanced.RawNettyQuicRpcTransport |
One Netty native QUIC connection |
| Kotlin | zip.trev.trevrpc.netty.advanced.RawNettyHttp3RpcTransport |
One Netty HTTP/3 connection |
| Browser JavaScript | RawWebTransport from trevrpc-js/advanced |
One browser WebTransport session |
| Node JavaScript | RawNodeTransport from trevrpc-js/node/advanced |
One native MsQuic connection |
Raw transports do not reconnect. Code using one must own connection establishment, failure detection, and shutdown explicitly.
TLS Resumption And 0-RTT
TLS session resumption only optimizes a later connection handshake. It does not recover an RPC or application session, and it never changes generation pinning.
Where TrevRPC controls QUIC setup, 0-RTT application data remains disabled. A missing, expired, rejected, or incompatible resumption ticket falls back to a full handshake without changing RPC semantics. Browser, Cronet, and other provider-controlled stacks may retain internal TLS state, but TrevRPC does not expose that behavior as a correctness guarantee.
Path Migration
Path migration preserves a living QUIC connection; reconnection replaces a dead connection. A successful path change keeps the same generation and may preserve in-flight streams.
| Runtime | Migration behavior |
|---|---|
| Rust / Quinn | Import trevrpc::advanced::ChannelOperations and call channel.rebind_quinn(socket). Quinn rebinding is endpoint-wide and affects every connection sharing that endpoint. |
| Go / quic-go | Call trevrpc.Advanced.Channel(channel).AddPath(transport), then probe and switch the returned quic-go path. |
| C / MsQuic | MsQuic handles supported migration and NAT rebinding; channel lifecycle events report address changes. |
| Browser WebTransport | The browser owns path changes. JavaScript can observe channel state but cannot control QUIC paths. |
| Kotlin / Cronet | Netty or Cronet owns path behavior; TrevRPC exposes no portable migration operation. |
Migration depends on peer transport parameters, routing, NAT behavior, and load-balancer connection-ID support. A failed migration may eventually close the generation and enter normal channel reconnection.
Shutdown
Closing a channel stops future generations and closes its current connection. Existing runtime cancellation rules apply to calls still attached to that generation.
The C API deliberately separates shutdown and lifetime release. Call trevrpc_channel_close(channel) first, then trevrpc_channel_release(channel). Close is idempotent and callback-safe. Final release joins workers and frees the handle; no new channel API call may begin concurrently with release.
Testing Guidance
Test connection loss independently from RPC status failures:
- Start every RPC shape on one generation.
- Terminate the connection after the server receives at least one request.
- Verify every affected RPC fails exactly once and no request appears on the next generation.
- Verify new calls fail immediately during reconnection.
- Wait for readiness and verify a newly issued RPC succeeds on the later generation.
- Test path migration without expecting a generation change.
- Test resumed and full handshakes as equivalent correctness paths, with 0-RTT application data disabled where TrevRPC controls it.