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}