pub struct UserAgent { /* private fields */ }Expand description
A user agent bound to a transport endpoint.
Implementations§
Source§impl UserAgent
impl UserAgent
Sourcepub fn new(endpoint: Handle, config: Config) -> Self
pub fn new(endpoint: Handle, config: Config) -> Self
A user agent that will send through endpoint.
Sourcepub fn path(&self) -> &PathSet
pub fn path(&self) -> &PathSet
The path the registrar recorded for this binding (RFC 3327).
Empty until a registration succeeds, and empty afterwards if no proxy on the way put itself on the path. This is reported rather than routed on: §5.1 says “the general operation of the UA is to ignore the Path header field in the response”, because the vector exists so that requests arriving at the registrar can be steered back toward a UA behind a NAT. What §5.1 does offer it for is inspection — seeing a proxy that has “inappropriately added” itself — and that is only possible if the value survives.
Sourcepub fn service_route(&self) -> &ServiceRoute
pub fn service_route(&self) -> &ServiceRoute
The route the registrar dictated for requests this UA sends (RFC 3608).
The opposite direction from UserAgent::path, and the one a UA is meant to act on:
§6.1 has it used “as a preloaded Route header field in outgoing initial requests”. sipx
does not preload it behind the caller’s back — a Route set silently attached to every
request is the kind of thing that is impossible to debug from the outside — so this is
handed to whoever builds the request, via DialOptions::with_service_route for a call.
Empty until a registration succeeds, and empty again after any 2xx that carries no
Service-Route.
Sourcepub const fn registration_observation(&self) -> &RegistrationObservation
pub const fn registration_observation(&self) -> &RegistrationObservation
What the registrar’s top response Via reported for the last successful registration.
RegistrationObservation::NotRegistered means no registration has succeeded yet;
RegistrationObservation::Absent is different: a success carried a valid top Via but
no observation parameters.
This does not authorize rewriting Contact, routing, GRUU, Outbound, push, SDP or media
addresses. RegistrationObservation::Invalid still accompanies a successful lease.
Sourcepub const fn observed_registration_address(&self) -> Option<SocketAddr>
pub const fn observed_registration_address(&self) -> Option<SocketAddr>
The registrar-observed address, when one was reported unambiguously.
This convenience accessor deliberately returns no fallback for absent or invalid data. Use
Self::registration_observation when the distinction matters.
Sourcepub fn flow_accepted(&self) -> bool
pub fn flow_accepted(&self) -> bool
Whether the registrar reported performing an Outbound registration (RFC 5626 §6).
False until a registration succeeds, and false afterwards if the registrar did not put the
option tag in Require — which is the case for every registrar that does not implement
RFC 5626 at all. Asking for Outbound and not getting it is not an error: the binding is an
ordinary one, and the only thing that changes is that there is no flow to keep alive.
Sourcepub fn keepalive_after(&self, power: Power) -> Option<Duration>
pub fn keepalive_after(&self, power: Power) -> Option<Duration>
How long to wait before the next keep-alive on this flow, if it is one (RFC 5626 §4.4).
None when the registrar did not perform an Outbound registration — there is no flow, so
pinging would be traffic with nothing at the far end that cares. Re-drawn on every call,
because §4.4.1 requires a fresh random interval for each ping: a fleet on a fixed period
synchronises after any shared outage and arrives back as one spike.
Sourcepub async fn keepalive(&mut self) -> Result<()>
pub async fn keepalive(&mut self) -> Result<()>
Send one keep-alive on this flow and judge the answer (RFC 5626 §4.4).
Three ways this reports a failed flow, and §4.4 makes each of them one:
- no answer within
outbound::PONG_TIMEOUT(§4.4.1), - a STUN Binding Error Response (§4.4.2),
- a reflexive address different from the last one (§4.4.2).
The third is the one that is easy to leave out and the reason STUN is the UDP technique at all. The socket still works; what has changed is that the NAT rebound, so the mapping the registrar holds for this flow no longer reaches it. A keep-alive that only asked “did anything come back” would call that flow healthy right up until a call failed to arrive.
Ok(()) on a flow the registrar did not accept: there is no flow, so there is nothing to
keep alive and nothing has failed.
Sourcepub fn reflexive_address(&self) -> Option<SocketAddr>
pub fn reflexive_address(&self) -> Option<SocketAddr>
The reflexive address the last keep-alive reported, if one did (RFC 5626 §4.4.2).
Sourcepub fn gruus(&self) -> &Gruus
pub fn gruus(&self) -> &Gruus
The GRUUs the registrar issued for this instance (RFC 5627 §4.2).
Empty until a registration succeeds, empty afterwards if GRUU was not asked for, and empty again if it was and the registrar issued nothing — §4.2 requires a UA to be ready for one, both or neither, and a registrar that does not implement RFC 5627 answers a REGISTER perfectly well and attaches none.
Sourcepub fn sent_to_our_gruu(&self, request: &Request) -> bool
pub fn sent_to_our_gruu(&self, request: &Request) -> bool
Whether a request that arrived was sent to one of this instance’s GRUUs (RFC 5627 §4.5).
This is the question the mechanism exists to make answerable, and it is not the question
“is this request for me”: an address of record reaches every device the user registered,
and RFC 5627 §5.4 notes that a public GRUU “will always be equivalent to the AOR based on
URI equality rules”. A true here means the sender addressed this instance and nothing
else — which is what a transfer target or a callback is relying on.
Sourcepub fn dialog_contact(&self) -> String
pub fn dialog_contact(&self) -> String
The Contact to put on a dialog-forming or target-refresh request (RFC 5627 §4.4,
RFC 5626 §4.3).
Three answers, in the order the RFCs put them:
- The GRUU, when one is known. §4.4: “A UA SHOULD use a GRUU when populating the Contact header field of dialog-forming and target refresh requests and responses.” It is an address that survives this flow, this NAT mapping and this registration, which is more than either of the others can say.
- The contact with
ob, when this is an accepted flow and no GRUU is known. RFC 5626 §4.3 makes that a MUST in the absence of a GRUU, and it tells the far end that mid-dialog requests belong on this flow rather than at the address in the URI — behind a NAT, the difference between a re-INVITE arriving and vanishing. - The plain contact, when neither applies.
A caller that asked for a temporary GRUU and did not get one lands in the second or third case, never the first: the public GRUU is not a substitute for an unlinkable address, and quietly publishing the device’s permanent name to a peer that was promised otherwise is a worse outcome than publishing the contact. It is logged, because the caller asked for something it did not get.
Sourcepub fn push_support(&self) -> &Support
pub fn push_support(&self) -> &Support
What the registrar said about push notifications (RFC 8599 §8.2).
Empty until a registration succeeds, and empty afterwards when the registrar implements
nothing of RFC 8599 — which is not a refusal, just silence. The question to ask it is
Support::supports with the provider this side
registered: a registrar that answered 200 while naming a different push service has
recorded a binding nothing will ever wake, and this is the only place that says so.
Sourcepub async fn woken(&mut self) -> Result<Pending>
pub async fn woken(&mut self) -> Result<Pending>
A push notification arrived: refresh the binding, and only then expect the request (RFC 8599 §4.1.3).
§4.1.3: “When a UA receives a push notification, the UA MUST send a binding-refresh
REGISTER request.” The push is not the call — it is permission to go and get a flow,
and the request the push was sent for arrives down the flow this REGISTER creates. A
client that skips this and waits for the INVITE is waiting on a path that does not
exist yet, which is why the Pending that licenses the wait
comes from here and nowhere else.
sipx neither sends nor receives the push itself: the service is behind
crate::push::PushService, and when to call this is the application’s — it is
whatever “the notification fired” means on its platform.
Sourcepub async fn register(&mut self) -> Result<Lease>
pub async fn register(&mut self) -> Result<Lease>
Register, answering a challenge if one comes.
One retry, not a loop. A second challenge after credentials were supplied means the credentials are wrong — unless the server says the nonce was merely stale, which is a different thing and is retried. Looping on a genuine rejection is how a client locks out the account it is trying to use.
Sourcepub async fn keep_registered(&mut self) -> Result<Infallible>
pub async fn keep_registered(&mut self) -> Result<Infallible>
Register and keep registering, refreshing before each lease expires.
A failed refresh is retried once inside the margin left by the last granted lease. The
registrar’s grant defines both deadlines: the first attempt starts at refresh_after, and
the retry divides what remains before granted. A second failure returns rather than
creating an unbounded retry loop.
Sourcepub async fn answer(&self, incoming: &Incoming) -> Result<bool>
pub async fn answer(&self, incoming: &Incoming) -> Result<bool>
Answer a request that arrived, when the user-agent layer owns the decision.
Handles what a user agent must answer to be a good citizen on the network, and refuses an initial INVITE addressed to a GRUU this registered instance does not own. Anything else is left to the caller, which is why this returns whether it acted.
The GRUU guard applies only after this agent has learned at least one GRUU. An unregistered
agent cannot prove that an unfamiliar value belongs to somebody else, while one holding its
registrar-issued values can. RFC 5627 §4.5 says gr identifies an instance-specific URI;
§6.1 gives an unresolved GRUU the ordinary 404 Not Found response. Applying that response
at the last hop keeps a proxy’s accidental misrouting from turning an instance address back
into an address-of-record fan-out.
Trait Implementations§
Auto Trait Implementations§
impl !Freeze for UserAgent
impl !RefUnwindSafe for UserAgent
impl Send for UserAgent
impl Sync for UserAgent
impl Unpin for UserAgent
impl UnsafeUnpin for UserAgent
impl !UnwindSafe for UserAgent
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more