Skip to main content

Client

Struct Client 

Source
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

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

pub fn set_deadline(&self, deadline: Option<Duration>)

Set the per-request deadline for later calls (None = unbounded).

Source

pub fn record(&self) -> &ServiceRecord

Source

pub fn hello(&self) -> &Value

The host’s hello: service, role, rank, version, and the methods this app may call.

Source

pub fn abort(&self)

Close the connection from any thread. A request blocked in Client::call returns connection_closed; the host sees the disconnect.

Source

pub fn call(&self, method: &str, params: Value) -> Result<Value, ServiceError>

Source

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.

Source

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

Source

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.upload method; the host runs method once the last piece arrives, and its response answers this call. A host that predates chunking (no max_message in its hello) or has it off refuses with request_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

Source

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.

Source

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.

Source

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.

Source

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.

Auto Trait Implementations§

§

impl !Freeze for Client

§

impl !RefUnwindSafe for Client

§

impl !UnwindSafe for Client

§

impl Send for Client

§

impl Sync for Client

§

impl Unpin for Client

§

impl UnsafeUnpin for Client

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.