df_hyperliquid_core/client/web/socket/
mod.rs1mod js;
10
11use std::{
12 cell::RefCell,
13 collections::VecDeque,
14 future::poll_fn,
15 rc::Rc,
16 task::{Poll, Waker},
17};
18
19use crate::client::web::clock;
20use crate::client::web::socket::js::{Calls, connect, function, random, said};
21use crate::live::{Event, FeedError, PING, Post, Session, Subscription, Tick};
22use js_sys::Function;
23use wasm_bindgen::{JsCast, JsValue};
24use web_sys::{MessageEvent, WebSocket};
25
26pub struct Feed {
28 driver: Rc<RefCell<Driver>>,
29}
30
31impl Feed {
32 #[must_use]
34 pub fn start(session: Session) -> Feed {
35 let url = session.url();
36 Feed::start_at(url, session)
37 }
38
39 #[must_use]
41 pub fn start_at(url: impl Into<String>, session: Session) -> Feed {
42 let global: JsValue = js_sys::global().into();
43 let timers = function(&global, "setTimeout").zip(function(&global, "clearTimeout"));
44 let driver = Rc::new_cyclic(|driver| {
45 RefCell::new(Driver {
46 url: url.into(),
47 session,
48 global,
49 timers,
50 socket: None,
51 timer: None,
52 calls: Calls::new(driver),
53 events: VecDeque::new(),
54 waker: None,
55 stopped: false,
56 })
57 });
58 if let Ok(mut driver) = driver.try_borrow_mut() {
59 if driver.timers.is_some() {
60 driver.dial();
61 } else {
62 driver.stop();
63 }
64 }
65 Feed { driver }
66 }
67
68 pub async fn subscribe(&self, subscription: Subscription) -> Result<(), FeedError> {
71 self.with(|driver| {
72 if let Some(frame) = driver.session.subscribe(subscription)? {
73 driver.carry(&frame);
74 }
75 Ok(())
76 })
77 }
78
79 pub fn unsubscribe(&self, subscription: Subscription) -> Result<(), FeedError> {
80 self.with(|driver| {
81 if let Some(frame) = driver.session.unsubscribe(&subscription) {
82 driver.carry(&frame);
83 }
84 Ok(())
85 })
86 }
87
88 pub async fn post(&self, request: Post) -> Result<u64, FeedError> {
91 self.with(|driver| {
92 let (id, frame) = driver.session.post(&request)?;
93 driver.carry(&frame);
94 Ok(id)
95 })
96 }
97
98 pub async fn next(&mut self) -> Option<Event> {
100 self.next_with_text().await.map(|(event, _)| event)
101 }
102
103 pub async fn next_with_text(&mut self) -> Option<(Event, Option<String>)> {
106 poll_fn(|cx| {
107 let Ok(mut driver) = self.driver.try_borrow_mut() else {
108 cx.waker().wake_by_ref();
110 return Poll::Pending;
111 };
112 if let Some(delivered) = driver.events.pop_front() {
113 return Poll::Ready(Some(delivered));
114 }
115 if driver.stopped {
116 return Poll::Ready(None);
117 }
118 driver.waker = Some(cx.waker().clone());
119 Poll::Pending
120 })
121 .await
122 }
123
124 pub fn close(self) {
126 drop(self);
127 }
128
129 fn with<T>(&self, act: impl FnOnce(&mut Driver) -> Result<T, FeedError>) -> Result<T, FeedError> {
132 let mut driver = self.driver.try_borrow_mut().map_err(|_| FeedError::Stopped)?;
133 if driver.stopped {
134 return Err(FeedError::Stopped);
135 }
136 act(&mut driver)
137 }
138}
139
140impl Drop for Feed {
141 fn drop(&mut self) {
142 if let Ok(mut driver) = self.driver.try_borrow_mut() {
143 driver.stop();
144 }
145 }
146}
147
148struct Driver {
151 url: String,
152 session: Session,
153 global: JsValue,
154 timers: Option<(Function, Function)>,
156 socket: Option<WebSocket>,
158 timer: Option<JsValue>,
161 calls: Calls,
162 events: VecDeque<(Event, Option<String>)>,
164 waker: Option<Waker>,
165 stopped: bool,
166}
167
168impl Driver {
169 fn dial(&mut self) {
172 match connect(&self.global, &self.url) {
173 Ok(socket) => {
174 socket.set_onopen(Some(self.calls.open.as_ref().unchecked_ref()));
175 socket.set_onmessage(Some(self.calls.message.as_ref().unchecked_ref()));
176 socket.set_onclose(Some(self.calls.close.as_ref().unchecked_ref()));
177 socket.set_onerror(Some(self.calls.error.as_ref().unchecked_ref()));
178 self.socket = Some(socket);
179 }
180 Err(reason) => self.lost(reason),
181 }
182 }
183
184 fn opened(&mut self) {
185 for frame in self.session.opened(clock()) {
186 if let Err(reason) = self.send(&frame) {
187 return self.lost(reason);
188 }
189 }
190 self.events.push_back((Event::Opened, None));
191 self.arm_tick();
192 }
193
194 fn received(&mut self, message: &MessageEvent) {
195 let Some(text) = message.data().as_string() else { return };
197 let event = match self.session.received(clock(), &text) {
198 Ok(frame) => Event::Frame(Box::new(frame)),
199 Err(bad) => Event::Unreadable(bad),
200 };
201 self.events.push_back((event, Some(text)));
202 }
203
204 fn woke(&mut self) {
206 self.timer = None;
207 if self.session.is_open() {
208 match self.session.tick(clock()) {
209 Tick::Wait => {}
210 Tick::Ping => {
211 if let Err(reason) = self.send(PING) {
212 return self.lost(reason);
213 }
214 }
215 Tick::Reconnect => return self.lost("nothing arrived for too long".to_owned()),
216 }
217 self.arm_tick();
218 } else if self.socket.is_none() {
219 self.dial();
220 }
221 }
222
223 fn carry(&mut self, frame: &str) {
225 if let Err(reason) = self.send(frame) {
226 self.lost(reason);
227 }
228 }
229
230 fn send(&self, frame: &str) -> Result<(), String> {
231 let Some(socket) = &self.socket else { return Err("no socket to send on".to_owned()) };
232 socket.send_with_str(frame).map_err(|error| said(&error).unwrap_or_else(|| "the send failed".to_owned()))
233 }
234
235 fn lost(&mut self, reason: String) {
238 self.hang_up();
239 let retry = self.session.closed(clock(), random());
240 let after = retry.after_ms;
241 self.events.push_back((Event::Closed { reason, retry }, None));
242 self.arm(after);
243 }
244
245 fn hang_up(&mut self) {
249 if let Some(socket) = self.socket.take() {
250 socket.set_onopen(None);
251 socket.set_onmessage(None);
252 socket.set_onclose(None);
253 socket.set_onerror(None);
254 let _ = socket.close();
255 }
256 }
257
258 fn stop(&mut self) {
259 self.hang_up();
260 self.disarm();
261 self.stopped = true;
262 }
263
264 fn arm_tick(&mut self) {
266 if let Some(at) = self.session.due() {
267 self.arm(at.saturating_sub(clock()));
268 }
269 }
270
271 fn arm(&mut self, ms: u64) {
274 self.disarm();
275 let Some((set_timeout, _)) = &self.timers else { return self.stop() };
276 let delay = JsValue::from(u32::try_from(ms).map_or(LONGEST_DELAY, |ms| ms.min(LONGEST_DELAY)));
277 match set_timeout.call2(&self.global, self.calls.wake.as_ref(), &delay) {
278 Ok(timer) => self.timer = Some(timer),
279 Err(_) => self.stop(),
280 }
281 }
282
283 fn disarm(&mut self) {
284 if let (Some(timer), Some((_, clear_timeout))) = (self.timer.take(), &self.timers) {
285 let _ = clear_timeout.call1(&self.global, &timer);
286 }
287 }
288
289 fn wakeable(&mut self) -> Option<Waker> {
291 if self.events.is_empty() && !self.stopped { None } else { self.waker.take() }
292 }
293}
294
295const LONGEST_DELAY: u32 = 0x7fff_ffff;
297
298#[cfg(test)]
299#[cfg(target_arch = "wasm32")]
300#[path = "tests/feed.rs"]
301mod tests;