1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
//! Connection state machine and event types.
//!
//! Runtime-free: this module depends only on `std::sync::mpsc` and
//! `std::time::Duration`. It is shared by both the sync `WebSocketClient`
//! (always compiled) and the async `aio::WebSocketClient` (behind the
//! `tokio-comp` feature).
//!
//! # Backpressure policy
//!
//! Events flow over `std::sync::mpsc::sync_channel(N)` where `N` is the
//! per-client `event_buffer` (default
//! [`DEFAULT_EVENT_BUFFER`](crate::websocket::DEFAULT_EVENT_BUFFER)). The
//! channel is **drop-newest**: when full, `emit_event` (internal) discards
//! the incoming event rather than blocking the network task. This is the only
//! safe choice because `std::sync::mpsc` does not expose receiver-side
//! access to the sender; switching to a primitive that does (e.g.
//! `tokio::sync::broadcast`) would break the `events()` /
//! `state_events()` API surface that bindings depend on.
//!
//! Drops are surfaced via:
//! - the per-client
//! [`messages_dropped_total`](crate::aio::WebSocketClient::messages_dropped_total)
//! counter (for the inbound *message* channel; the *event* channel
//! shares the same drop-newest discipline but is small enough that
//! saturation is rare); and
//! - a `tracing::warn!` at the saturation site when the `tracing` feature
//! is enabled.
//!
//! Saturation is itself the bug signal — a healthy consumer never
//! approaches the configured cap.
use mpsc;
use Duration;
/// Who initiated the disconnect captured by
/// [`ConnectionEvent::Disconnected`] / [`ConnectionState::Closed`].
///
/// Lets consumers branch on the cause without string-matching the
/// `reason` field.
/// WebSocket connection state machine
/// Events emitted by WebSocket connection.
///
/// Consumers attribute events to their source client via the
/// [`events()`](crate::aio::WebSocketClient::events) /
/// [`state_events()`](crate::aio::WebSocketClient::state_events)
/// `Receiver` they were yielded from — `tokio::select!` arms naturally
/// label by source, and code that merges streams from multiple clients
/// is expected to wrap with its own labeling adapter (3 lines via
/// `tokio_stream::StreamExt::map`). The SDK does not pre-empt that
/// decision by stuffing a label on every event.
/// Emit a [`ConnectionEvent`] on the bounded event channel.
///
/// See the module-level documentation for the drop-newest backpressure
/// policy and how saturation is surfaced. The `dropped` counter is
/// incremented once per drop so consumers can observe saturation via
/// [`crate::WebSocketClient::events_dropped_total`] /
/// [`crate::aio::WebSocketClient::events_dropped_total`]. When the
/// `metrics` feature is enabled, the increment also bumps the
/// `fugle_marketdata_ws_events_dropped_total` counter on the active
/// `metrics` recorder.
pub