Skip to main content

irtt_client/
error.rs

1use std::{io, time::Duration};
2
3use thiserror::Error;
4
5/// Failure reported while configuring, opening, running, receiving, closing, or
6/// joining an IRTT client session.
7///
8/// Configuration validation errors usually require changing
9/// [`ClientConfig`](crate::ClientConfig) or related options before retrying.
10/// Network, protocol, and runtime errors may be transient, but can also reflect
11/// peer incompatibility or an invalid session state.
12#[derive(Debug, Error)]
13#[non_exhaustive]
14pub enum ClientError {
15    /// The configured server address could not be resolved to a socket address.
16    ///
17    /// Check the host, port, address-family restrictions, and name service
18    /// configuration before retrying.
19    #[error("failed to resolve server address {addr:?}")]
20    Resolve { addr: String },
21    /// A UDP socket operation failed while binding, connecting, sending, or
22    /// receiving.
23    ///
24    /// This can be a transient network or OS error, but may also reflect local
25    /// permissions, routing, or socket configuration.
26    #[error("socket error: {0}")]
27    Socket(#[from] io::Error),
28    /// Applying or reading a socket option failed.
29    ///
30    /// This is usually caused by an unsupported option, an invalid local value,
31    /// or a platform-specific socket limitation. Changing socket configuration
32    /// is more likely to help than retrying unchanged.
33    #[error("socket option error while trying to {operation} for {remote}: {source}")]
34    SocketOption {
35        operation: &'static str,
36        remote: std::net::SocketAddr,
37        source: io::Error,
38    },
39    /// Restoring the configured read timeout after open negotiation failed.
40    ///
41    /// The session may have opened successfully, but the socket could not be
42    /// returned to the caller's requested receive-timeout behavior.
43    #[error("failed to restore configured socket read timeout after open negotiation: {source}")]
44    ReadTimeoutRestore { source: io::Error },
45    /// A packet failed IRTT protocol parsing or authentication.
46    ///
47    /// Likely causes include malformed input, unsupported protocol data, or an
48    /// HMAC mismatch between client and server.
49    #[error("protocol error: {0}")]
50    Protocol(#[from] irtt_proto::ProtoError),
51    /// No valid open reply was received before all configured open attempts
52    /// timed out.
53    ///
54    /// Retrying can succeed when the server or network is temporarily
55    /// unavailable. Persistent failures usually require changing the address,
56    /// authentication key, server configuration, or timeout settings.
57    #[error("all open requests timed out")]
58    OpenTimeout,
59    /// An open-attempt timeout is below the client's supported minimum.
60    ///
61    /// Increase the configured timeout before retrying.
62    #[error("open timeout {timeout:?} is below the minimum {minimum:?}")]
63    OpenTimeoutTooSmall {
64        timeout: Duration,
65        minimum: Duration,
66    },
67    /// The configured open-attempt timeout list is empty.
68    ///
69    /// Provide at least one timeout before opening the session.
70    #[error("open_timeouts must not be empty")]
71    NoOpenTimeouts,
72    /// The server explicitly rejected the open request.
73    ///
74    /// Retrying unchanged is unlikely to help unless the server configuration or
75    /// load has changed.
76    #[error("server rejected the open request")]
77    ServerRejected,
78    /// A no-test open request received a reply that did not have no-test close
79    /// semantics.
80    ///
81    /// This indicates an incompatible or unexpected peer response.
82    #[error("unexpected no-test open reply")]
83    UnexpectedNoTestReply,
84    /// A no-test open reply included a connection token.
85    ///
86    /// No-test negotiation must not establish a probe session, so a non-zero
87    /// token indicates an incompatible or unexpected peer response.
88    #[error("no-test reply included a non-zero connection token: {token}")]
89    NonZeroNoTestToken { token: u64 },
90    /// The peer replied with a protocol version different from the requested
91    /// version.
92    ///
93    /// This is a protocol compatibility error and requires compatible client
94    /// and server versions.
95    #[error("protocol version mismatch: requested {requested}, received {received}")]
96    ProtocolVersionMismatch { requested: i64, received: i64 },
97    /// The server returned a zero connection token for a normal test session.
98    ///
99    /// A normal session requires a non-zero token to correlate echo traffic, so
100    /// this indicates an invalid peer response.
101    #[error("server returned a zero connection token")]
102    ZeroToken,
103    /// Negotiated parameters were rejected by the configured negotiation policy.
104    ///
105    /// Use a less restrictive configuration or
106    /// [`NegotiationPolicy::Loose`](crate::NegotiationPolicy::Loose) when the
107    /// caller accepts documented server restrictions.
108    #[error("strict negotiation rejected changed params: {reason}")]
109    NegotiationRejected { reason: String },
110    /// The requested operation requires an open session, but the client is only
111    /// connected.
112    #[error("client is not open")]
113    NotOpen,
114    /// The session is already open.
115    ///
116    /// Continue running, receiving, or closing the existing session instead of
117    /// opening it again.
118    #[error("client session is already open")]
119    AlreadyOpen,
120    /// The no-test session has already completed.
121    ///
122    /// Create a new client session to perform another negotiation.
123    #[error("client session is already completed")]
124    AlreadyCompleted,
125    /// The session is already closed.
126    ///
127    /// Create a new client session before opening or running another test.
128    #[error("client session is already closed")]
129    AlreadyClosed,
130    /// A duration could not be represented as nanoseconds for scheduling or
131    /// protocol encoding.
132    ///
133    /// Use smaller configured or negotiated durations.
134    #[error("duration is too large to encode as nanoseconds")]
135    DurationOverflow,
136    /// A client-maintained counter would overflow.
137    ///
138    /// This is a runtime limit rather than a network failure. Start a new
139    /// session if the test duration or send volume reaches this limit.
140    #[error("counter {counter} overflowed")]
141    CounterOverflow { counter: &'static str },
142    /// Sending another probe would exceed the configured pending-probe limit.
143    ///
144    /// Increase [`ClientConfig::max_pending_probes`](crate::ClientConfig::max_pending_probes),
145    /// reduce send rate, or receive replies and timeouts more often before
146    /// sending again.
147    #[error("pending probe limit exceeded ({limit})")]
148    PendingLimitExceeded { limit: usize },
149    /// Client configuration failed validation before the requested operation.
150    ///
151    /// The reported reason names a value that must be changed before retrying.
152    #[error("invalid configuration: {reason}")]
153    InvalidConfig { reason: String },
154    /// A managed-session worker thread panicked before producing an outcome.
155    ///
156    /// This is a runtime failure from joining the managed session, not a
157    /// protocol response from the server.
158    #[error("managed client worker thread panicked")]
159    WorkerPanicked,
160}
161
162/// Failure reported while setting up or receiving from an event subscription.
163///
164/// Zero-capacity subscriber queues are rejected when subscriptions are created.
165/// Once a subscription exists, this error type reports delivery failures observed
166/// by [`EventSubscription`](crate::EventSubscription).
167#[derive(Debug, Error, Clone, Copy, PartialEq, Eq)]
168pub enum EventSubscriptionError {
169    /// The subscription can no longer receive new events.
170    ///
171    /// Already queued events are delivered first. After the queue is drained,
172    /// `recv` and `try_recv` return this error when the hub disconnects the
173    /// subscriber, the hub is shut down, or the subscription is disconnected by
174    /// its overflow policy.
175    #[error("event subscription is disconnected")]
176    Disconnected,
177}