pub struct Client { /* private fields */ }Expand description
A connection to one host of a service. One request in flight at a time;
open a second Client for parallel calls. Dropping it closes the
connection, which the host treats as a disconnect (jobs started with
cancel_on_disconnect are cancelled).
Implementations§
Source§impl Client
impl Client
Sourcepub fn connect(
suite: &Suite,
service: &str,
app: &str,
) -> Result<Client, ServiceError>
pub fn connect( suite: &Suite, service: &str, app: &str, ) -> Result<Client, ServiceError>
Discover the live host of service on suite and say hello as app,
bounded by DEFAULT_DEADLINE.
The host is not authenticated. Discovery is a file (service.json) and a socket path
that any program running as the same user can write or bind, and suite itself may have
come from an environment variable. The transport proves only that whatever answered runs
as the current user, so the hello and every request, including any secret in one, go to
that program. A client that sends secrets, or that acts on what the host answers, must use
Client::connect_expecting instead.
Sourcepub fn connect_with(
suite: &Suite,
service: &str,
app: &str,
deadline: Option<Duration>,
) -> Result<Client, ServiceError>
pub fn connect_with( suite: &Suite, service: &str, app: &str, deadline: Option<Duration>, ) -> Result<Client, ServiceError>
Like Client::connect with an explicit deadline for connect + hello,
which also becomes the client’s per-request deadline (None = unbounded).
The host is not authenticated: see Client::connect.
Sourcepub fn connect_config(config: &ClientConfig) -> Result<Client, ServiceError>
pub fn connect_config(config: &ClientConfig) -> Result<Client, ServiceError>
Connect through a ClientConfig: the suite and deadline come from the configuration,
and ClientConfig::ignore_env_overrides is honoured. The host is still not
authenticated (see Client::connect); this hardens only where the client looks.
Sourcepub fn connect_expecting(
config: &ClientConfig,
expect: &HostExpectation,
) -> Result<Client, ConnectError>
pub fn connect_expecting( config: &ClientConfig, expect: &HostExpectation, ) -> Result<Client, ConnectError>
Discover the host of the configured service, verify it is the program expect names, and
only then say hello.
The OS is asked who is on the listening end of the connected socket: its uid must equal
this process’s, and its executable (read from the kernel for that process, canonicalised)
must be one of expect’s. The process id is proven stable across the read (start time,
and on macOS the audit token, before and after, and the pid re-read from the socket). A
caller-supplied HostExpectation::with_verifier check then runs. Platforms: macOS
(getpeereid, LOCAL_PEERPID, proc_pidpath), Linux (SO_PEERCRED, /proc/<pid>/exe),
Windows (the pipe’s server process: its token user, a held process handle,
QueryFullProcessImageNameW); elsewhere the refusal is
HostIdentityReason::Unverified and nothing is
sent. See the README, “Verifying the host”.
Order. The check runs after the kernel has completed the socket connect and before
anything is written. Nothing application-level precedes it: no hello, no request, no
length prefix. The only thing the other side can observe of a refused attempt is a
connection that closes without a byte. The deadline in config bounds the whole attempt.
A refusal is ConnectError::HostIdentity, distinct from ConnectError::Service
(no record, unreachable, timeout, hello refused). It is not retryable. The check is made
once, at connect, for the life of the connection.
Sourcepub fn verified_host(&self) -> Option<&HostProcess>
pub fn verified_host(&self) -> Option<&HostProcess>
The host process the OS identified and this client verified, for a client opened with
Client::connect_expecting; None for one opened with Client::connect or
Client::connect_with, whose host was never checked.
Sourcepub fn set_deadline(&self, deadline: Option<Duration>)
pub fn set_deadline(&self, deadline: Option<Duration>)
Set the per-request deadline for later calls (None = unbounded).
pub fn record(&self) -> &ServiceRecord
Sourcepub fn hello(&self) -> &Value
pub fn hello(&self) -> &Value
The host’s hello: service, role, rank, version, and the methods this app may call.
Sourcepub fn abort(&self)
pub fn abort(&self)
Close the connection from any thread. A request blocked in
Client::call returns connection_closed; the host sees the disconnect.
pub fn call(&self, method: &str, params: Value) -> Result<Value, ServiceError>
Sourcepub fn call_events(
&self,
method: &str,
params: Value,
on_event: &mut dyn FnMut(&Value),
) -> Result<Value, ServiceError>
pub fn call_events( &self, method: &str, params: Value, on_event: &mut dyn FnMut(&Value), ) -> Result<Value, ServiceError>
Like Client::call, streaming the request’s event frames to on_event.
The per-request deadline starts when this method is entered and covers waiting for the connection (another call in flight), the write, and the reply: one monotonic deadline.
Sourcepub fn call_events_until(
&self,
method: &str,
params: Value,
deadline: Option<Instant>,
on_event: &mut dyn FnMut(&Value),
) -> Result<Value, ServiceError>
pub fn call_events_until( &self, method: &str, params: Value, deadline: Option<Instant>, on_event: &mut dyn FnMut(&Value), ) -> Result<Value, ServiceError>
Like Client::call_events, bounded by an absolute monotonic deadline (None =
unbounded) that the caller fixed earlier. Waiting for a request already in flight on this
connection counts against it: when it passes first, this returns the typed timeout without
aborting the connection, because the request that holds it is not this one’s.
Source§impl Client
impl Client
Sourcepub fn call_large(
&self,
method: &str,
params: Value,
on_event: &mut dyn FnMut(&Value),
) -> Result<Value, ServiceError>
pub fn call_large( &self, method: &str, params: Value, on_event: &mut dyn FnMut(&Value), ) -> Result<Value, ServiceError>
Like Client::call_events for a request or a result that may be larger than one frame
(issue #294). The frame limit stays the cap on every frame; a message above it is cut into
pieces and assembled by the other side under the host’s separate message bound
(HostOptions::max_message), as the chunk module
describes.
- Params that fit in half the host’s frame go as one ordinary request, and a result that does not fit one frame comes back in pieces (this call opts in to that).
- Larger params are uploaded in pieces through the reserved
rk.uploadmethod; the host runsmethodonce the last piece arrives, and its response answers this call. A host that predates chunking (nomax_messagein itshello) or has it off refuses withrequest_too_large, and so does a message beyond the host’s bound, before any piece is sent.
Each frame is its own request with its own deadline (the per-request deadline set on the
client), so a long transfer is not cut by one deadline for the whole. A failure part-way
leaves nothing in the host after the next call or the end of the connection; resubmitting
the whole call is safe for the transfer itself, but whether method may run twice is the
caller’s call, as for Client::call. Events of the call (on_event) arrive with the
final upload piece’s request.
Source§impl Client
impl Client
Sourcepub fn call_retrying(
suite: &Suite,
service: &str,
app: &str,
method: &str,
params: Value,
policy: RetryPolicy,
on_event: &mut dyn FnMut(&Value),
) -> Result<Value, ServiceError>
pub fn call_retrying( suite: &Suite, service: &str, app: &str, method: &str, params: Value, policy: RetryPolicy, on_event: &mut dyn FnMut(&Value), ) -> Result<Value, ServiceError>
Connect, call, and on a retryable error (host handoff, closed connection,
service unavailable) re-read discovery and resubmit the whole request.
The host is not authenticated: see Client::connect.
Sourcepub fn call_retrying_expecting(
config: &ClientConfig,
expect: &HostExpectation,
method: &str,
params: Value,
policy: RetryPolicy,
on_event: &mut dyn FnMut(&Value),
) -> Result<Value, ServiceError>
pub fn call_retrying_expecting( config: &ClientConfig, expect: &HostExpectation, method: &str, params: Value, policy: RetryPolicy, on_event: &mut dyn FnMut(&Value), ) -> Result<Value, ServiceError>
Client::call_retrying that verifies the host on every attempt, as
Client::connect_expecting does. A host identity refusal is not retryable and is
returned at once as a ServiceError with code host_identity
(ERROR_HOST_IDENTITY); it is never replayed against another attempt. The per-attempt
deadline is policy.request_deadline, not the configuration’s.
Sourcepub fn call_retrying_over<F>(
connect: F,
method: &str,
params: Value,
policy: RetryPolicy,
on_event: &mut dyn FnMut(&Value),
) -> Result<Value, ServiceError>
pub fn call_retrying_over<F>( connect: F, method: &str, params: Value, policy: RetryPolicy, on_event: &mut dyn FnMut(&Value), ) -> Result<Value, ServiceError>
Retry over a connection source the caller owns, instead of a fresh connection per call:
connect returns a client for each attempt (a held client, a pool, or a closure that
connects with its own verification). The request goes through Client::call_large, so a
request or result larger than one frame works, and the same retry rules as
Client::call_retrying apply (see RetryPolicy::idempotent). connect gets the
per-attempt deadline from policy.request_deadline.
Sourcepub fn call_retrying_until<F>(
connect: F,
method: &str,
params: Value,
policy: RetryPolicy,
stop: &(dyn Fn() -> bool + Sync),
on_event: &mut dyn FnMut(&Value),
) -> Result<Value, ServiceError>
pub fn call_retrying_until<F>( connect: F, method: &str, params: Value, policy: RetryPolicy, stop: &(dyn Fn() -> bool + Sync), on_event: &mut dyn FnMut(&Value), ) -> Result<Value, ServiceError>
Client::call_retrying_over for a call that runs until it is stopped, such as a long event
subscription. stop is checked before each attempt, during the backoff, and while an
attempt is in flight (a watcher thread aborts the connection once it returns true, which
ends that attempt with connection_closed and no further attempt is made). For a call
with no end, also set policy.give_up_after to Duration::MAX: the give-up limit counts
from the first attempt and would otherwise end it.