Skip to main content

PhantomSession

Struct PhantomSession 

Source
pub struct PhantomSession { /* private fields */ }
Available on crate feature std only.
Expand description

Client-first session — instant construction, non-blocking send().

§Design

The real entry point is connect_with_transport (NOT the inert legacy connect() constructor — see its doc): it returns instantly and runs the handshake + data pump in the background, so sends issued before the handshake finishes are buffered and auto-flushed once the channel is up.

  // instant — spawns the background handshake + pump:
  let session = PhantomSession::connect_with_transport(addr, transport, pinned_key);
  session.send(data).await;   // queued until handshake completes
  session.send(data2).await;  // also queued
  // ... handshake completes in background ...
  // queued data auto-flushed, new sends go directly

The session progresses through states: Connecting → ClassicalReady → PqcUpgrading → PqcReady → Connected

Implementations§

Source§

impl PhantomSession

Source

pub fn connect_with_transport<T: SessionTransport>( peer_addr: &str, transport: T, expected_server_key: HybridVerifyingKey, ) -> Self

Create a new session and start the background handshake task.

Requires expected_server_key for MITM resistance — the client will abort the handshake unless the server presents this exact verifying key. Callers obtain this key out-of-band (e.g. from PhantomListener::verifying_key_bytes).

The handshake runs in the background:

  1. Exchange hybrid PQC ClientHello/ServerHello.
  2. Verify server identity against expected_server_key.
  3. Derive AEAD keys; flush queued sends as encrypted packets.

All network I/O goes through the provided SessionTransport. The task that drives the handshake + data pump runs on the default TokioRuntime; use connect_with_transport_with_runtime to substitute a different Runtime.

Source

pub fn connect_with_transport_with_runtime<T: SessionTransport>( peer_addr: &str, transport: T, expected_server_key: HybridVerifyingKey, runtime: Arc<dyn Runtime>, ) -> Self

Like connect_with_transport but runs the background task on the supplied Runtime. Intended for WASM / embedded / test backends that don’t drive tokio::spawn.

Source

pub fn connect_with_resumption<T: SessionTransport>( peer_addr: &str, transport: T, expected_server_key: HybridVerifyingKey, resumption_hint: ([u8; 32], [u8; 32]), early_data: Vec<u8>, ) -> Result<Self, CoreError>

Connect with a 0-RTT resumption attempt.

resumption_hint is the (session_id, resumption_secret) tuple from a prior session’s PhantomSession::resumption_hint. early_data (≤ EARLY_DATA_MAX_LEN bytes) is sealed and carried inside the resuming ClientHello so it reaches the server on the very first flight — saving a round-trip versus 1-RTT.

Acceptance is best-effort: a stale/unknown ticket or an AEAD failure leaves early_data_accepted at Some(false) and the handshake completes as a normal 1-RTT exchange — the caller must then send that payload over the normal channel. Returns Err only when early_data exceeds the cap.

Runs on the default TokioRuntime.

Source§

impl PhantomSession

Source

pub fn observability(&self) -> Arc<Observability>

Session observability handle (Rust-only — Observability is not a UniFFI type). For a server-accepted session this is the PhantomListener’s shared instance; for a client it is the session’s own. Read .snapshot() for the lock-free metric counters.

Source§

impl PhantomSession

Source

pub fn connect(peer_addr: String) -> Arc<Self>

Create a placeholder session — returns instantly and performs no handshake.

§⚠️ This does not connect

Despite the name, this constructor never opens a transport, never runs the PQC handshake, and never spawns the background data pump. It returns an inert shell stuck in ConnectionState::Connecting: any send() only queues into an in-memory buffer that is never flushed, and recv() never yields application bytes. No bytes ever reach the network. It exists only as a pre-handshake placeholder from an earlier API shape.

Deprecated — use a real entry point instead:

§Why no #[deprecated] attribute (T5.7)

A #[deprecated] attribute would be the natural way to flag this, but it cannot be applied here: this constructor is #[uniffi::constructor], and UniFFI 0.31 emits FFI scaffolding that calls Self::connect() from generated code in this same crate. That generated call would trip the deprecated lint, which CI promotes to a hard error under clippy --lib -D warnings — and no item-scoped #[allow(deprecated)] reaches the macro-generated call site (only a module-wide #![allow(deprecated)] would, which would silently mask every future genuine deprecation across this module). So the deprecation is documented loudly here instead. UniFFI copies this doc-comment into the generated Python / Swift / Kotlin docstrings (the C header carries no docstrings), so they were regenerated and committed alongside this change — the bindings drift CI job stays green. See tests::deprecated_connect_is_inert_and_sends_no_bytes for the regression pinning the inert behaviour.

Source

pub fn open_stream(&self) -> Arc<PhantomStream>

Open a new multiplexed stream

Source

pub async fn send(&self, data: Vec<u8>) -> Result<(), CoreError>

Send data through the session.

  • If the session is connected: sends immediately
  • If still handshaking: queues the data for auto-flush later
Source

pub async fn recv(&self) -> Result<Vec<u8>, CoreError>

Receive data from the session.

Internally the recv pipeline keeps payloads as Bytes to avoid the per-packet Vec clone that used to fan out to the stream demux. The FFI surface still hands callers a Vec<u8>; if this is the last refcount the Vec is moved out of the underlying buffer, otherwise Bytes::to_vec copies.

Source

pub fn connection_state(&self) -> ConnectionState

Get the current connection state (lock-free).

Source

pub fn is_data_ready(&self) -> bool

Whether the session is ready for data transmission.

Source

pub fn is_pqc_ready(&self) -> bool

Whether the session has full PQC protection.

Source

pub async fn flush_queue(&self) -> Result<u32, CoreError>

Flush all queued messages (called when handshake completes).

Source

pub async fn queued_count(&self) -> u32

Number of messages queued (waiting for handshake).

Source

pub fn id(&self) -> String

Session identifier.

Source

pub fn peer_addr(&self) -> String

Target peer address.

Source

pub async fn early_data_accepted(&self) -> Option<bool>

The 0-RTT verdict for this session.

  • None — still handshaking, the handshake failed, or the client sent no early-data on this connect.
  • Some(true) — the server consumed the 0-RTT early-data.
  • Some(false) — the client sent early-data and the server rejected it (stale/unknown ticket, oversized blob, or AEAD failure). The caller must re-send that payload over the normal channel.
Source

pub async fn resumption_hint(&self) -> Option<ResumptionHint>

Extract a ResumptionHint for a future 0-RTT reconnect.

Returns Some after a successful handshake; None while still handshaking, after a failure, or before the inner session has been published.

Store the hint alongside the pinned HybridVerifyingKey of the server it was negotiated against and feed it back to connect_pinned_with_resumption. Reusing a hint across servers is a configuration bug — the resumption_secret is server-pinned.

Source

pub async fn current_epoch(&self) -> Option<u8>

Current rekey epoch of the established session (None while still connecting). Rust-only — used by soak / integration tests to confirm that automatic mid-session rekey (C1) advanced the epoch.

Source

pub async fn set_rekey_threshold(&self, n: u64) -> bool

Override the automatic-rekey send-invocation high-watermark on the established session (default REKEY_SOFT_LIMIT, currently 2^32). Returns false if the session is still connecting. Rust-only — primarily for soak/load harnesses that need to exercise mid-session rekey without sending 2^32 packets.

Source

pub async fn set_traffic_shaping(&self, config: TrafficShapingConfig) -> bool

Apply an anti-fingerprint traffic-shaping configuration to the established session (WIRE v6, direction #4). Returns false if the session is still connecting. All shaping is opt-in (default: none); enabling size padding (PaddingPolicy::Padme) makes outbound packets pad up to a PADÉ bucket so the datagram size no longer tracks the payload size, at a bounded (≈ ≤12% worst-case) bandwidth cost. FFI-exported so mobile / other embedders can tune it.

Source

pub async fn traffic_shaping(&self) -> Option<TrafficShapingConfig>

Read back the traffic-shaping config currently applied to the established session (#9). None while still connecting (the session is not installed yet — the pending config set via set_traffic_shaping will apply on install). FFI-exported.

Source

pub async fn migrate(&self, local_addr: String) -> Result<(), CoreError>

Migrate the session to a new local network address (Phase 4 — embedder- triggered connection migration). The embedder calls this when the OS reports a network change (Wi-Fi↔cellular, NAT rebind); local_addr is the new local bind address (e.g. "0.0.0.0:0" to let the OS pick an ephemeral port on the new interface).

Best-effort and non-blocking on validation. It hands the request to the background pump, which rebinds the transport (keeping the old socket for the overlap) and bumps the send path_id; the path validation + server-side peer switch then complete asynchronously. The keys and session persist — no re-handshake. A failed rebind never tears the session down: it keeps running on the existing socket (broken-rebind safety). Err here means only that the session was already closed (the command channel is gone).

Source

pub async fn disconnect(&self) -> Result<(), CoreError>

Send the graceful close frame and shut the session down.

Named disconnect rather than close because UniFFI’s Kotlin generator unconditionally adds AutoCloseable.close() to every object, and a Rust-side close here would conflict with it.

Source§

impl PhantomSession

Source

pub fn demux(&self) -> Arc<StreamDemultiplexer>

Get the stream demultiplexer (internal use, not exposed to UniFFI)

Source

pub async fn set_liveness_config(&self, cfg: LivenessConfig) -> bool

Override the path-liveness thresholds on the established session (Phase 4 / P4.3). Returns false if the session is still connecting. Rust-only (the LivenessConfig type is not on the UniFFI surface) — for tests / advanced embedders that want a faster or slower path-down / migration-idle timeout than the default.

Source

pub async fn migrate_server(&self, local_addr: String) -> Result<(), CoreError>

Migrate the server side of this session to a new local send address (the server-side mirror of migrate). Intended for an accepted server session whose network path changes (failover, multi-homing, an egress NAT rebind): the server rebinds its send socket to local_addr and rotates its server→client path_id + connection-ID in lock-step, so the peer follows the fresh s2c source (its unconnected socket hears it) and an observer cannot relink the session by the s2c ConnId across the move. The keys and session persist — no re-handshake.

The server keeps RECEIVING client→server traffic on the established (listen) address through the overlap, so the session stays bidirectional immediately. Best-effort: a failed rebind leaves the session on the old send socket and never tears it down. Err here means only that the session was already closed.

Rust-only (deliberately not on the UniFFI/FFI surface): server migration is a native-deployment operation, not a mobile-client one. The peer’s symmetric c2s follow (switching its send target to the new server address once the old one is unreachable) and the matching c2s CID rotation are added in the follow-up security-core change.

Trait Implementations§

Source§

impl Debug for PhantomSession

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<UT> LiftRef<UT> for PhantomSession

Source§

impl<UT> LowerError<UT> for PhantomSession

Source§

fn lower_error(obj: Self) -> RustBuffer

Lower this value for scaffolding function return Read more
Source§

impl<UT> LowerReturn<UT> for PhantomSession

Source§

type ReturnType = <Arc<PhantomSession> as LowerReturn<UniFfiTag>>::ReturnType

The type that should be returned by scaffolding functions for this type. Read more
Source§

fn lower_return(obj: Self) -> Result<Self::ReturnType, RustCallError>

Lower the return value from an scaffolding call Read more
Source§

fn handle_failed_lift( error: LiftArgsError, ) -> Result<Self::ReturnType, RustCallError>

Lower the return value for failed argument lifts Read more
Source§

impl<UT> TypeId<UT> for PhantomSession

Auto Trait Implementations§

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<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> CompatExt for T

Source§

fn compat(self) -> Compat<T>
where T: Sized,

Applies the Compat adapter by value. Read more
Source§

fn compat_ref(&self) -> Compat<&T>

Applies the Compat adapter by shared reference. Read more
Source§

fn compat_mut(&mut self) -> Compat<&mut T>

Applies the Compat adapter by mutable reference. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> FutureExt for T

Source§

fn with_context(self, otel_cx: Context) -> WithContext<Self>

Attaches the provided Context to this type, returning a WithContext wrapper. Read more
Source§

fn with_current_context(self) -> WithContext<Self>

Attaches the current Context to this type, returning a WithContext wrapper. Read more
Source§

impl<T, UT> HandleAlloc<UT> for T
where T: Send + Sync,

Source§

fn new_handle(value: Arc<T>) -> Handle

Create a new handle for an Arc value Read more
Source§

unsafe fn clone_handle(handle: Handle) -> Handle

Clone a handle Read more
Source§

unsafe fn consume_handle(handle: Handle) -> Arc<T>

Consume a handle, getting back the initial Arc<> Read more
Source§

unsafe fn get_arc(handle: Handle) -> Arc<Self>

Get a clone of the Arc<> using a “borrowed” handle. Read more
Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
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> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

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

Source§

type Error = Infallible

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

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

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.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more