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 poll_fn(|cx| {
101 let Ok(mut driver) = self.driver.try_borrow_mut() else {
102 cx.waker().wake_by_ref();
104 return Poll::Pending;
105 };
106 if let Some(event) = driver.events.pop_front() {
107 return Poll::Ready(Some(event));
108 }
109 if driver.stopped {
110 return Poll::Ready(None);
111 }
112 driver.waker = Some(cx.waker().clone());
113 Poll::Pending
114 })
115 .await
116 }
117
118 pub fn close(self) {
120 drop(self);
121 }
122
123 fn with<T>(&self, act: impl FnOnce(&mut Driver) -> Result<T, FeedError>) -> Result<T, FeedError> {
126 let mut driver = self.driver.try_borrow_mut().map_err(|_| FeedError::Stopped)?;
127 if driver.stopped {
128 return Err(FeedError::Stopped);
129 }
130 act(&mut driver)
131 }
132}
133
134impl Drop for Feed {
135 fn drop(&mut self) {
136 if let Ok(mut driver) = self.driver.try_borrow_mut() {
137 driver.stop();
138 }
139 }
140}
141
142struct Driver {
145 url: String,
146 session: Session,
147 global: JsValue,
148 timers: Option<(Function, Function)>,
150 socket: Option<WebSocket>,
152 timer: Option<JsValue>,
155 calls: Calls,
156 events: VecDeque<Event>,
157 waker: Option<Waker>,
158 stopped: bool,
159}
160
161impl Driver {
162 fn dial(&mut self) {
165 match connect(&self.global, &self.url) {
166 Ok(socket) => {
167 socket.set_onopen(Some(self.calls.open.as_ref().unchecked_ref()));
168 socket.set_onmessage(Some(self.calls.message.as_ref().unchecked_ref()));
169 socket.set_onclose(Some(self.calls.close.as_ref().unchecked_ref()));
170 socket.set_onerror(Some(self.calls.error.as_ref().unchecked_ref()));
171 self.socket = Some(socket);
172 }
173 Err(reason) => self.lost(reason),
174 }
175 }
176
177 fn opened(&mut self) {
178 for frame in self.session.opened(clock()) {
179 if let Err(reason) = self.send(&frame) {
180 return self.lost(reason);
181 }
182 }
183 self.events.push_back(Event::Opened);
184 self.arm_tick();
185 }
186
187 fn received(&mut self, message: &MessageEvent) {
188 let Some(text) = message.data().as_string() else { return };
190 let event = match self.session.received(clock(), &text) {
191 Ok(frame) => Event::Frame(Box::new(frame)),
192 Err(bad) => Event::Unreadable(bad),
193 };
194 self.events.push_back(event);
195 }
196
197 fn woke(&mut self) {
199 self.timer = None;
200 if self.session.is_open() {
201 match self.session.tick(clock()) {
202 Tick::Wait => {}
203 Tick::Ping => {
204 if let Err(reason) = self.send(PING) {
205 return self.lost(reason);
206 }
207 }
208 Tick::Reconnect => return self.lost("nothing arrived for too long".to_owned()),
209 }
210 self.arm_tick();
211 } else if self.socket.is_none() {
212 self.dial();
213 }
214 }
215
216 fn carry(&mut self, frame: &str) {
218 if let Err(reason) = self.send(frame) {
219 self.lost(reason);
220 }
221 }
222
223 fn send(&self, frame: &str) -> Result<(), String> {
224 let Some(socket) = &self.socket else { return Err("no socket to send on".to_owned()) };
225 socket.send_with_str(frame).map_err(|error| said(&error).unwrap_or_else(|| "the send failed".to_owned()))
226 }
227
228 fn lost(&mut self, reason: String) {
231 self.hang_up();
232 let retry = self.session.closed(clock(), random());
233 let after = retry.after_ms;
234 self.events.push_back(Event::Closed { reason, retry });
235 self.arm(after);
236 }
237
238 fn hang_up(&mut self) {
242 if let Some(socket) = self.socket.take() {
243 socket.set_onopen(None);
244 socket.set_onmessage(None);
245 socket.set_onclose(None);
246 socket.set_onerror(None);
247 let _ = socket.close();
248 }
249 }
250
251 fn stop(&mut self) {
252 self.hang_up();
253 self.disarm();
254 self.stopped = true;
255 }
256
257 fn arm_tick(&mut self) {
259 if let Some(at) = self.session.due() {
260 self.arm(at.saturating_sub(clock()));
261 }
262 }
263
264 fn arm(&mut self, ms: u64) {
267 self.disarm();
268 let Some((set_timeout, _)) = &self.timers else { return self.stop() };
269 let delay = JsValue::from(u32::try_from(ms).map_or(LONGEST_DELAY, |ms| ms.min(LONGEST_DELAY)));
270 match set_timeout.call2(&self.global, self.calls.wake.as_ref(), &delay) {
271 Ok(timer) => self.timer = Some(timer),
272 Err(_) => self.stop(),
273 }
274 }
275
276 fn disarm(&mut self) {
277 if let (Some(timer), Some((_, clear_timeout))) = (self.timer.take(), &self.timers) {
278 let _ = clear_timeout.call1(&self.global, &timer);
279 }
280 }
281
282 fn wakeable(&mut self) -> Option<Waker> {
284 if self.events.is_empty() && !self.stopped { None } else { self.waker.take() }
285 }
286}
287
288const LONGEST_DELAY: u32 = 0x7fff_ffff;
290
291#[cfg(test)]
292#[cfg(target_arch = "wasm32")]
293#[path = "tests/feed.rs"]
294mod tests;