use worker::{Response, Result as WorkerResult, WebSocket, WebSocketPair};
pub fn accept_websocket() -> WorkerResult<(WebSocket, Response)> {
let pair = WebSocketPair::new()?;
let server = pair.server;
let client = pair.client;
let mut resp = Response::from_websocket(client)?;
resp.headers_mut()
.set("Access-Control-Allow-Origin", "*")
.map_err(|_| "header error")?;
Ok((server, resp))
}
pub fn send_binary(ws: &WebSocket, data: &[u8]) -> WorkerResult<()> {
ws.send_with_bytes(data.to_vec())
}
pub fn send_text(ws: &WebSocket, text: &str) -> WorkerResult<()> {
ws.send_with_str(text)
}
pub fn close_socket(ws: &WebSocket, code: Option<u16>, reason: Option<&str>) {
let _ = ws.close(code, reason);
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum SignalRHandshake {
#[default]
ExpectHandshake,
Connected,
}
#[derive(Default)]
pub struct SignalrHandshaker {
state: SignalRHandshake,
}
impl SignalrHandshaker {
#[must_use]
pub fn new() -> Self {
Self {
state: SignalRHandshake::ExpectHandshake,
}
}
#[must_use]
pub fn state(&self) -> SignalRHandshake {
self.state
}
#[must_use]
pub fn is_connected(&self) -> bool {
self.state == SignalRHandshake::Connected
}
pub fn process(
&mut self,
ws: &WebSocket,
text: &str,
) -> WorkerResult<Option<SignalRHandshake>> {
if self.state != SignalRHandshake::ExpectHandshake {
return Ok(None);
}
let trimmed = text.trim_end_matches('\x1E');
if trimmed.contains("messagepack") || trimmed.contains("protocol") {
ws.send_with_str("{}\x1E")?;
self.state = SignalRHandshake::Connected;
return Ok(Some(SignalRHandshake::Connected));
}
Ok(Some(SignalRHandshake::ExpectHandshake))
}
pub fn reset(&mut self) {
self.state = SignalRHandshake::ExpectHandshake;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn handshaker_starts_expecting() {
let h = SignalrHandshaker::new();
assert!(!h.is_connected());
assert_eq!(h.state(), SignalRHandshake::ExpectHandshake);
}
#[test]
fn handshaker_reset() {
let mut h = SignalrHandshaker::new();
h.state = SignalRHandshake::Connected;
h.reset();
assert_eq!(h.state(), SignalRHandshake::ExpectHandshake);
}
#[test]
fn handshaker_skips_when_connected() {
let mut h = SignalrHandshaker::new();
h.state = SignalRHandshake::Connected;
}
}