Contributor-facing documentation for the hyper-client crate -- the connection layer
between hyperapi (high-level API) and hyper-protocol (wire protocol).
For user-facing documentation, see README.md.
hyper-client provides two transport families with both sync and async variants:
| Transport | Protocol | Capabilities | Variants |
|---|---|---|---|
| TCP | PostgreSQL wire protocol (v3) | Full read/write, DDL, COPY | Client, AsyncClient |
| gRPC | Protobuf over HTTP/2 | Read-only (SELECT), Arrow IPC results | GrpcClient, GrpcClientSync |
src/
lib.rs # Crate root, re-exports
config.rs # Config builder for TCP connections
client.rs # Sync TCP client (Client, CopyInWriter, QueryStream)
async_client.rs # Async TCP client (AsyncClient, AsyncCopyInWriter)
connection.rs # RawConnection<S> -- sync wire protocol engine
async_connection.rs # AsyncRawConnection<S> -- async wire protocol engine
auth.rs # Authentication: cleartext, MD5, SCRAM-SHA-256
tls.rs # TLS config and rustls integration
cancel.rs # Cancellable trait (transport-agnostic cancel)
endpoint.rs # ConnectionEndpoint (TCP, Unix, Named Pipe)
sync_stream.rs # SyncStream (TCP/Unix/Pipe wrapper for sync I/O)
async_stream.rs # AsyncStream (TCP/Unix/Pipe wrapper for async I/O)
error.rs # Error types
notice.rs # Server notice/warning handling
row.rs # Row, StreamRow, BatchRow
statement.rs # Column metadata, ColumnFormat
prepare.rs # Prepared statement support
grpc/
mod.rs # gRPC module root, re-exports
client.rs # GrpcClient, GrpcClientSync
config.rs # GrpcConfig builder
executor.rs # GrpcQueryExecutor state machine
params.rs # QueryParameters, ParameterStyle
result.rs # GrpcQueryResult, GrpcResultChunk
error.rs # gRPC error conversion
proto.rs # Generated protobuf types
authenticated_client.rs # AuthenticatedGrpcClient (salesforce-auth feature)
tests/
common/mod.rs # TestServer helper
client_tests.rs # Sync client integration tests
copy_tests.rs # COPY protocol tests
prepared_statement_tests.rs
tls_tests.rs # TLS with self-signed certs (rcgen)
async_copy_cancel_tests.rs
- TCP connect --
TcpStream::connect(or Unix/Named Pipe equivalent) - Startup -- Send
StartupMessagewith user, database, and protocol version - Authentication -- Server selects method; client responds (see
auth.rsmodule docs) - Parameter exchange -- Server sends
ParameterStatusmessages (server_version, etc.) - Ready -- Server sends
ReadyForQuery('I'), connection is usable
The startup and auth handshake is implemented in RawConnection::startup() and
AsyncRawConnection::startup().
The TCP client supports three query modes. Implementation details (protocol messages,
format codes, memory behavior) are documented in client.rs module-level rustdoc.
| Mode | Method | Format | Memory Model |
|---|---|---|---|
| Simple Query | query() |
Text | All rows materialized in Vec<Row> |
| Extended Query | query_fast() |
HyperBinary (format code 2, LE binary) | All rows in Vec<StreamRow> |
| Streaming | query_streaming() |
HyperBinary | Chunked via QueryStream iterator |
COPY IN uses the PostgreSQL COPY subprotocol:
- Client sends
Query("COPY ... FROM STDIN WITH (FORMAT ...)") - Server responds with
CopyInResponse - Client sends
CopyDatamessages (buffered inCopyInWriter) - Client sends
CopyDone(success) orCopyFail(abort) - Server responds with
CommandComplete+ReadyForQuery
Supported formats: HYPERBINARY (default), ARROWSTREAM, CSV.
The higher-level hyperapi::Inserter handles binary encoding; CopyInWriter is
a transport-level data pump.
RawConnection tracks a desynchronized flag. Once set (e.g., a bounded drain
exceeded its budget before seeing ReadyForQuery), all operations fast-fail.
Recovery requires dropping the connection and opening a new one.
Key methods:
ensure_healthy()-- checked before every new requestis_healthy()-- used by pool layers during recycledrain_until_ready_bounded(cap)-- used byQueryStream::dropand error recoveryconsume_error()-- drains throughReadyForQueryafter anErrorResponse
PG wire cancellation opens a separate TCP connection and sends a 16-byte
CancelRequest packet (process ID + secret key). This design allows cancellation
from any thread without acquiring the connection mutex.
See cancel.rs for the Cancellable trait and Client::cancel() for the
user-facing fallible API. QueryStream::drop uses Cancellable to stop
in-flight streaming queries.
GrpcQueryExecutor (in grpc/executor.rs) drives query execution through a state
machine that handles all three transfer modes. The state transitions and per-mode
paths are documented in that module's rustdoc. Key states:
ReadInitialResults -> RequestStatus -> ReadStatus -> RequestResults -> ReadResults -> Finished
SYNC-mode queries go directly from ReadInitialResults to Finished.
ADAPTIVE-mode queries may take either path depending on result size.
Unlike PG wire, gRPC cancels travel as a regular CancelQuery RPC multiplexed
over the existing HTTP/2 channel. The cancel carries x-hyperdb-query-id metadata
for server-side routing. See GrpcClient::cancel_query() rustdoc for the full
design discussion, including why GrpcClient does not implement Cancellable.
The build.rs script compiles .proto files (from the crate-local protos/
directory) into Rust types via tonic-build. The generated code lives in
grpc/proto.rs (included via include!).
To regenerate after proto changes:
cargo build -p hyper-clientAuthentication is server-directed: the server sends an AuthenticationRequest
message indicating which method to use. The client responds accordingly.
The supported methods, their wire formats, and the SCRAM-SHA-256 multi-step
handshake are documented in auth.rs module-level rustdoc.
- All intermediate SCRAM key material uses
zeroize::Zeroizing<Vec<u8>> AuthStateis consumed (moved) byscram_verify_server, ensuring keys are dropped after verification- MD5 uses a double-hash scheme:
MD5(MD5(password + user) + salt)
TLS is implemented via rustls (pure Rust, no OpenSSL dependency). The
implementation lives in tls.rs and tls::rustls_impl.
- Root certificates: system roots via
webpki-roots+ optional custom CA - Client certificates: optional mutual TLS (mTLS)
- Key formats: PEM (PKCS#8 or PKCS#1 for private keys)
- TLS modes:
Disable,Prefer,Require,VerifyCA,VerifyFull
gRPC TLS is handled separately by tonic's built-in TLS support, configured
via GrpcConfig (auto-detected from https:// endpoints).
Integration tests require a running Hyper server. The tests/common/mod.rs module
provides TestServer, which:
- Starts a
HyperProcess(viahyperapidev-dependency) - Creates a temporary database
- Provides
ConfigandClienthelpers for the test - Cleans up on drop
use crate::common::TestServer;
#[test]
fn test_something() -> hyperapi::Result<()> {
let server = TestServer::new()?;
let client = server.connect()?;
// ... test with real server ...
Ok(())
}Test output (databases, logs) goes to hyper-client/test_results/.
# All hyper-client tests (requires hyperd on PATH or HYPERD_PATH set)
cargo test -p hyper-client
# Unit tests only (no server needed)
cargo test -p hyper-client --lib
# Specific test file
cargo test -p hyper-client --test client_teststls_tests.rs generates self-signed certificates at runtime using rcgen
(dev-dependency) to test TLS handshake, mTLS, and certificate verification
without requiring pre-generated certificates.
- Use
TestServer::new()for tests that need a database - Use
TestServer::without_database()for tests that manage databases explicitly - Use
#[test]for sync tests,#[tokio::test]for async tests - Keep test databases in
test_results/(auto-managed byTestServer) - gRPC tests live in
hyperapi/tests/since they exercise the full stack
| Feature | Dependencies Added | What It Enables |
|---|---|---|
salesforce-auth |
hyperapi-salesforce, chrono, arrow |
AuthenticatedGrpcClient, with_data_cloud_token() on GrpcConfig |
Everything else (TCP clients, gRPC clients, TLS, auth) is always available.
The sync Client uses std::net::TcpStream + std::sync::Mutex for zero
async runtime overhead. The async AsyncClient uses tokio::net::TcpStream +
tokio::sync::Mutex. They share the same wire protocol logic via
RawConnection<S> / AsyncRawConnection<S>, parameterized over the stream type.
GrpcClientSync creates an internal tokio::runtime::Runtime and blocks on
the async GrpcClient. This avoids duplicating the gRPC logic but adds ~1ms
runtime creation overhead. For sync-heavy workloads, create one GrpcClientSync
and reuse it.
Standard PostgreSQL binary format (code 1) uses big-endian. Hyper extends the
protocol with format code 2 for little-endian binary (HyperBinary), which
avoids byte-swapping on x86/ARM and enables zero-copy field access in
StreamRow. This is a Hyper-specific extension not supported by standard
PostgreSQL servers.
The Cancellable trait is intentionally minimal (fn cancel(&self) with no
return value and no arguments) to serve as an internal cleanup abstraction
usable from Drop impls. User-facing cancel APIs (Client::cancel() -> Result,
GrpcClient::cancel_query(id) -> Result) are separate and fallible.
See cancel.rs for the full rationale.
GrpcClientduplicates query-building logic betweenexecute_query_with_optionsandexecute_query_with_params_and_options-- should be unifiedAsyncClientlacks a streaming query mode equivalent toQueryStream- Connection pooling exists as
deadpoolintegration but is not yet exposed as a first-class API in this crate TlsConfig/TlsModeare defined but not yet wired intoConfig's builder (TLS is configured at a lower level currently)
- Root DEVELOPMENT.md -- workspace-wide build, test, CI
- hyper-protocol README -- wire protocol details
- hyper-types README -- type system and binary formats
- hyperapi README -- high-level API built on this crate