use core::cell::{Cell, RefCell};
use core::future::Future;
use core::hash::Hash;
use core::marker::PhantomData;
use core::mem;
use core::ops::Deref;
use core::pin::Pin;
use core::ptr::NonNull;
use core::task::{Context, Poll, Waker};
use core::{any, fmt};
use std::collections::VecDeque;
use alloc::boxed::Box;
use alloc::format;
use alloc::rc::Rc;
use alloc::rc::Weak;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use std::collections::hash_map::{Entry, HashMap};
use slab::Slab;
use crate::api::{self, ChannelId, DecodeBody, Event, Format, MessageId, Mode};
use crate::format;
#[cfg(test)]
mod tests;
const MAX_CAPACITY: usize = 1048576;
#[non_exhaustive]
pub struct EmptyBody;
#[non_exhaustive]
pub struct EmptyCallback;
trait RequestCallback {
fn error(&self, error: Error);
fn as_request(&self) -> Option<&(dyn Callback<Result<RawPacket, Error>> + 'static)> {
None
}
fn as_channel(&self) -> Option<&(dyn Fn(Result<ChannelId, Error>) + 'static)> {
None
}
fn as_negotiate(&self) -> Option<(Format, Mode)> {
None
}
}
struct NegotiateCallback(Format, Mode);
impl RequestCallback for NegotiateCallback {
#[inline]
fn as_negotiate(&self) -> Option<(Format, Mode)> {
Some((self.0, self.1))
}
#[inline]
fn error(&self, error: Error) {
tracing::debug!("Format negotiation failed: {error}");
}
}
type StateListeners = Slab<Rc<dyn Callback<State>>>;
type Broadcasts = HashMap<MessageId, Slab<Rc<dyn Callback<Result<RawPacket>>>>>;
type Buffers = VecDeque<Box<BufData>>;
type PendingRequests = HashMap<u32, Box<Pending<dyn RequestCallback>>>;
#[doc(hidden)]
pub struct Location {
pub(crate) protocol: String,
pub(crate) host: String,
pub(crate) port: String,
}
pub(crate) mod sealed_socket {
pub trait Sealed {}
}
pub(crate) trait SocketImpl
where
Self: Sized + self::sealed_socket::Sealed,
{
#[doc(hidden)]
type Handles;
#[doc(hidden)]
fn new(url: &str, handles: &Self::Handles) -> Result<Self, Error>;
#[doc(hidden)]
fn send(&self, mode: Mode, data: &[u8]) -> Result<(), Error>;
#[doc(hidden)]
fn close(self) -> Result<(), Error>;
}
pub(crate) mod sealed_window {
pub trait Sealed {}
}
pub(crate) trait WindowImpl
where
Self: Sized + self::sealed_window::Sealed,
{
#[doc(hidden)]
type Timeout;
#[doc(hidden)]
type OnBeforeUnload;
#[doc(hidden)]
fn new() -> Result<Self, Error>;
#[doc(hidden)]
fn location(&self) -> Result<Location, Error>;
#[doc(hidden)]
fn set_timeout(
&self,
millis: u32,
callback: impl Fn() + 'static,
) -> Result<Self::Timeout, Error>;
#[doc(hidden)]
fn onbeforeunload(&self, callback: impl Fn() + 'static) -> Result<Self::OnBeforeUnload, Error>;
}
pub(crate) mod sealed_web {
pub trait Sealed {}
}
pub trait WebImpl
where
Self: 'static + Copy + Sized + self::sealed_web::Sealed,
{
#[doc(hidden)]
#[allow(private_bounds)]
type Window: WindowImpl;
#[doc(hidden)]
type Handles;
#[doc(hidden)]
#[allow(private_bounds)]
type Socket: SocketImpl<Handles = Self::Handles>;
#[doc(hidden)]
#[allow(private_interfaces)]
fn handles(shared: &Weak<Shared<Self>>) -> Self::Handles;
#[doc(hidden)]
fn random(range: u32) -> u32;
}
pub fn connect<H>(connect: Connect) -> ServiceBuilder<H, EmptyCallback>
where
H: WebImpl,
{
ServiceBuilder {
connect,
on_error: EmptyCallback,
close_before_unload: false,
format: Format::DEFAULT,
mode: Mode::DEFAULT,
app: 0,
_marker: PhantomData,
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[non_exhaustive]
pub enum State {
Open,
Closed,
Incompatible {
server: u32,
client: u32,
},
}
impl State {
pub fn is_open(&self) -> bool {
matches!(self, Self::Open)
}
pub fn is_incompatible(&self) -> bool {
matches!(self, Self::Incompatible { .. })
}
}
#[derive(Debug)]
pub struct Error {
kind: ErrorKind,
}
impl Error {
#[inline]
fn new(kind: ErrorKind) -> Self {
Self { kind }
}
#[inline]
pub fn is_empty_packet(&self) -> bool {
matches!(self.kind, ErrorKind::EmptyPacket)
}
#[inline]
pub fn is_not_connected(&self) -> bool {
matches!(self.kind, ErrorKind::NotConnected(..))
}
#[inline]
pub fn is_connection_lost(&self) -> bool {
matches!(self.kind, ErrorKind::ConnectionLost(..))
}
#[inline]
pub fn is_cancelled(&self) -> bool {
matches!(self.kind, ErrorKind::Cancelled)
}
#[inline]
pub fn as_server_error(&self) -> Option<&str> {
match &self.kind {
ErrorKind::Server(error) => Some(&error.message),
_ => None,
}
}
pub fn server_error<'de, T>(&'de self) -> Result<Option<T>>
where
T: DecodeBody<'de>,
{
let ErrorKind::Server(error) = &self.kind else {
return Ok(None);
};
let Some((format, payload)) = &error.payload else {
return Ok(None);
};
let value = format
.decode(payload, &mut 0)
.map_err(Error::decode_error_message)?;
Ok(Some(value))
}
#[inline]
pub fn is_decode(&self) -> bool {
matches!(
self.kind,
ErrorKind::EmptyPacket
| ErrorKind::UnknownFormat(..)
| ErrorKind::DecodeResponseHeader(..)
| ErrorKind::DecodeErrorMessage(..)
| ErrorKind::DecodePacket(..)
)
}
#[inline]
pub fn message(message: impl fmt::Display) -> Self {
Self::new(ErrorKind::Message(message.to_string()))
}
#[inline]
fn server(error: ServerError) -> Self {
Self::new(ErrorKind::Server(error))
}
#[inline]
pub(crate) fn decode_response_header(error: format::Error) -> Self {
Self::new(ErrorKind::DecodeResponseHeader(error))
}
#[inline]
pub(crate) fn decode_error_message(error: format::Error) -> Self {
Self::new(ErrorKind::DecodeErrorMessage(error))
}
#[inline]
pub(crate) fn decode_packet(error: format::Error) -> Self {
Self::new(ErrorKind::DecodePacket(error))
}
#[inline]
pub(crate) fn encoding_header(error: format::Error) -> Self {
Self::new(ErrorKind::EncodingHeader(error))
}
#[inline]
pub(crate) fn encoding_body(error: format::Error) -> Self {
Self::new(ErrorKind::EncodingBody(error))
}
}
#[derive(Debug)]
enum ErrorKind {
EmptyPacket,
NotConnected(&'static str),
ConnectionLost(&'static str),
Cancelled,
Message(String),
Server(ServerError),
UnknownFormat(u8),
DecodeResponseHeader(format::Error),
DecodeErrorMessage(format::Error),
DecodePacket(format::Error),
EncodingHeader(format::Error),
EncodingBody(format::Error),
}
impl fmt::Display for Error {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.kind {
ErrorKind::EmptyPacket => write!(f, "Packet is empty"),
ErrorKind::NotConnected(message) => write!(f, "{message}"),
ErrorKind::ConnectionLost(message) => write!(f, "{message}"),
ErrorKind::Cancelled => write!(f, "Request cancelled"),
ErrorKind::Message(message) => write!(f, "{message}"),
ErrorKind::Server(error) => write!(f, "Server error: {}", error.message),
ErrorKind::UnknownFormat(format) => write!(
f,
"Server used unknown format id {format} for a message body"
),
ErrorKind::DecodeResponseHeader(..) => {
write!(f, "Encoding error when decoding response header")
}
ErrorKind::DecodeErrorMessage(..) => {
write!(f, "Encoding error when decoding error response")
}
ErrorKind::DecodePacket(..) => write!(f, "Encoding error when decoding packet"),
ErrorKind::EncodingHeader(..) => write!(f, "Encoding error when encoding header"),
ErrorKind::EncodingBody(..) => write!(f, "Encoding error when encoding body"),
}
}
}
impl core::error::Error for Error {
#[inline]
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
match &self.kind {
ErrorKind::DecodeResponseHeader(error) => Some(error),
ErrorKind::DecodeErrorMessage(error) => Some(error),
ErrorKind::DecodePacket(error) => Some(error),
ErrorKind::EncodingHeader(error) => Some(error),
ErrorKind::EncodingBody(error) => Some(error),
_ => None,
}
}
}
#[cfg(feature = "wasm_bindgen02")]
impl From<wasm_bindgen02::JsValue> for Error {
#[inline]
fn from(error: wasm_bindgen02::JsValue) -> Self {
Self::new(ErrorKind::Message(format!("{error:?}")))
}
}
#[derive(Debug, Clone)]
struct ServerError {
message: String,
payload: Option<(Format, Box<[u8]>)>,
}
impl ServerError {
#[inline]
fn message(message: &str) -> Self {
Self {
message: String::from(message),
payload: None,
}
}
}
type Result<T, E = Error> = core::result::Result<T, E>;
const INITIAL_TIMEOUT: u32 = 250;
const MAX_TIMEOUT: u32 = 4000;
#[derive(Debug)]
enum ConnectKind {
Location { path: String },
Url { url: String },
}
pub struct Connect {
kind: ConnectKind,
}
impl Connect {
#[inline]
pub fn location(path: impl AsRef<str>) -> Self {
Self {
kind: ConnectKind::Location {
path: String::from(path.as_ref()),
},
}
}
#[inline]
pub fn url(url: String) -> Self {
Self {
kind: ConnectKind::Url { url },
}
}
}
impl fmt::Debug for Connect {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.kind.fmt(f)
}
}
struct Generic {
state_listeners: RefCell<StateListeners>,
generation: Cell<u64>,
requests: RefCell<PendingRequests>,
broadcasts: RefCell<Broadcasts>,
buffers: RefCell<Buffers>,
}
pub(crate) struct Shared<H>
where
H: WebImpl,
{
connect: Connect,
state: Cell<State>,
should_be: Cell<State>,
connecting: Cell<bool>,
format: Cell<Format>,
requested: Format,
mode: Cell<Mode>,
requested_mode: Mode,
app: u32,
handle: Handle<H>,
pub(crate) on_error: Box<dyn Callback<Error>>,
window: H::Window,
handles: H::Handles,
serial: Cell<u32>,
defer_broadcasts: RefCell<VecDeque<Weak<dyn Callback<Result<RawPacket>>>>>,
defer_state_listeners: RefCell<VecDeque<Weak<dyn Callback<State>>>>,
pub(crate) socket: RefCell<Option<H::Socket>>,
output: RefCell<Vec<u8>>,
current_timeout: Cell<u32>,
reconnect_timeout: RefCell<Option<<H::Window as WindowImpl>::Timeout>>,
_window_onbeforeunload: Option<<H::Window as WindowImpl>::OnBeforeUnload>,
g: Rc<Generic>,
}
impl<H> Drop for Shared<H>
where
H: WebImpl,
{
fn drop(&mut self) {
if let Some(s) = self.socket.take() {
tracing::debug!("Closing connection during drop");
if let Err(error) = s.close() {
self.on_error.call(error);
}
}
let state_listeners = mem::take(&mut *self.g.state_listeners.borrow_mut());
for (_, listener) in state_listeners {
listener.call(State::Closed);
}
let requests = mem::take(&mut *self.g.requests.borrow_mut());
for (_, p) in requests {
p.callback.error(Error::new(ErrorKind::ConnectionLost(
"Websocket service closed",
)));
}
}
}
pub struct ServiceBuilder<H, E>
where
H: WebImpl,
{
connect: Connect,
on_error: E,
close_before_unload: bool,
format: Format,
mode: Mode,
app: u32,
_marker: PhantomData<H>,
}
impl<H, E> ServiceBuilder<H, E>
where
H: WebImpl,
E: Callback<Error>,
{
pub fn on_error<U>(self, on_error: U) -> ServiceBuilder<H, U>
where
U: Callback<Error>,
{
ServiceBuilder {
connect: self.connect,
on_error,
close_before_unload: self.close_before_unload,
format: self.format,
mode: self.mode,
app: self.app,
_marker: self._marker,
}
}
#[inline]
pub fn format(mut self, format: Format) -> ServiceBuilder<H, E> {
self.format = format;
self
}
#[inline]
pub fn mode(mut self, mode: Mode) -> ServiceBuilder<H, E> {
self.mode = mode;
self
}
#[inline]
pub fn app_version(mut self, version: u32) -> Self {
self.app = version;
self
}
#[inline]
pub fn close_before_unload(mut self) -> Self {
self.close_before_unload = true;
self
}
pub fn build(self) -> Service<H> {
let window = match H::Window::new() {
Ok(window) => window,
Err(error) => {
panic!("{error}")
}
};
let shared = Rc::<Shared<H>>::new_cyclic(move |shared| {
let window_onbeforeunload = if self.close_before_unload {
let shared = shared.clone();
let result = window.onbeforeunload(move || {
tracing::debug!("Trying to close WebSocket connection on page unload");
if let Some(shared) = shared.upgrade() {
shared.close();
}
});
match result {
Ok(onbeforeunload) => Some(onbeforeunload),
Err(error) => {
self.on_error.call(Error::message(format_args!(
"Failed to set onbeforeunload: {error}"
)));
None
}
}
} else {
None
};
Shared {
connect: self.connect,
state: Cell::new(State::Closed),
should_be: Cell::new(State::Closed),
connecting: Cell::new(false),
format: Cell::new(self.format),
requested: self.format,
mode: Cell::new(self.mode),
requested_mode: self.mode,
app: self.app,
handle: Handle {
shared: shared.clone(),
},
on_error: Box::new(self.on_error),
window,
handles: H::handles(shared),
serial: Cell::new(0),
defer_broadcasts: RefCell::new(VecDeque::new()),
defer_state_listeners: RefCell::new(VecDeque::new()),
socket: RefCell::new(None),
output: RefCell::new(Vec::new()),
current_timeout: Cell::new(INITIAL_TIMEOUT),
reconnect_timeout: RefCell::new(None),
_window_onbeforeunload: window_onbeforeunload,
g: Rc::new(Generic {
state_listeners: RefCell::new(Slab::new()),
generation: Cell::new(0),
broadcasts: RefCell::new(HashMap::new()),
requests: RefCell::new(PendingRequests::new()),
buffers: RefCell::new(VecDeque::new()),
}),
}
});
let service = Service { shared };
service.open();
service
}
}
pub struct Service<H>
where
H: WebImpl,
{
shared: Rc<Shared<H>>,
}
impl<H> Service<H>
where
H: WebImpl,
{
#[deprecated(since = "0.3.0", note = "Use `open` instead")]
pub fn connect(&self) {
self.shared.open()
}
pub fn open(&self) {
self.shared.open()
}
pub fn close(&self) {
self.shared.close();
}
pub fn handle(&self) -> &Handle<H> {
&self.shared.handle
}
}
impl<H> Clone for Service<H>
where
H: WebImpl,
{
#[inline]
fn clone(&self) -> Self {
Self {
shared: self.shared.clone(),
}
}
}
impl<H> Shared<H>
where
H: WebImpl,
{
fn send_client_request<T>(&self, serial: u32, channel: ChannelId, body: &T) -> Result<()>
where
T: api::Request,
{
let Some(ref socket) = *self.socket.borrow() else {
return Err(Error::new(ErrorKind::NotConnected(
"Socket is not connected",
)));
};
let format = self.format.get();
let mode = self.mode.get();
let header = api::RequestHeader {
version: api::VERSION,
serial,
id: <T::Endpoint as api::Endpoint>::ID.get(),
format: format.to_u8(),
channel,
app: 0,
};
let out = &mut *self.output.borrow_mut();
format::encode_envelope(mode, &mut *out, &header).map_err(Error::encoding_header)?;
format
.encode(&mut *out, body)
.map_err(Error::encoding_body)?;
tracing::debug!(
header.serial,
?header.id,
len = out.len(),
"Sending request"
);
socket.send(mode, out.as_slice())?;
out.clear();
out.shrink_to(MAX_CAPACITY);
Ok(())
}
fn send_connect(&self, serial: u32) -> Result<()> {
let Some(ref socket) = *self.socket.borrow() else {
return Err(Error::new(ErrorKind::NotConnected(
"Socket is not connected",
)));
};
let mode = self.mode.get();
let header = api::RequestHeader {
version: api::VERSION,
serial,
id: MessageId::CONNECT.get(),
format: 0,
channel: ChannelId::NONE,
app: 0,
};
let out = &mut *self.output.borrow_mut();
format::encode_envelope(mode, &mut *out, &header).map_err(Error::encoding_header)?;
tracing::debug!(
header.serial,
?header.id,
len = out.len(),
"Sending request"
);
socket.send(mode, out.as_slice())?;
out.clear();
out.shrink_to(MAX_CAPACITY);
Ok(())
}
fn send_negotiate(self: &Rc<Self>) -> Result<()> {
let format = self.requested;
let mode = self.requested_mode;
let serial = self.serial.get();
self.serial.set(serial.wrapping_add(1));
{
let Some(ref socket) = *self.socket.borrow() else {
return Err(Error::new(ErrorKind::NotConnected(
"Socket is not connected",
)));
};
let header = api::RequestHeader {
version: api::VERSION,
serial,
id: MessageId::NEGOTIATE.get(),
format: format.to_u8(),
channel: ChannelId::NONE,
app: self.app,
};
let out = &mut *self.output.borrow_mut();
format::encode_envelope(mode, &mut *out, &header).map_err(Error::encoding_header)?;
tracing::debug!(?format, ?mode, "Requesting format");
socket.send(mode, out.as_slice())?;
out.clear();
out.shrink_to(MAX_CAPACITY);
}
let pending = Pending {
id: MessageId::NEGOTIATE,
serial,
callback: NegotiateCallback(format, mode),
};
let existing = self
.g
.requests
.borrow_mut()
.insert(serial, Box::new(pending));
if let Some(p) = existing {
p.callback.error(Error::new(ErrorKind::Cancelled));
}
Ok(())
}
fn remove_channel(&self, channel: ChannelId) {
if let Err(error) = self._send_disconnect(channel) {
self.on_error.call(error);
}
}
fn _send_disconnect(&self, channel: ChannelId) -> Result<()> {
let Some(ref socket) = *self.socket.borrow() else {
return Err(Error::new(ErrorKind::NotConnected(
"Socket is not connected",
)));
};
let mode = self.mode.get();
let header = api::RequestHeader {
version: api::VERSION,
serial: 0,
id: MessageId::DISCONNECT.get(),
format: 0,
channel,
app: 0,
};
let out = &mut *self.output.borrow_mut();
format::encode_envelope(mode, &mut *out, &header).map_err(Error::encoding_header)?;
tracing::debug!(
header.serial,
?header.id,
len = out.len(),
"Sending request"
);
socket.send(mode, out.as_slice())?;
out.clear();
out.shrink_to(MAX_CAPACITY);
Ok(())
}
pub(crate) fn next_buffer(self: &Rc<Self>, needed: usize) -> Box<BufData> {
match self.g.buffers.borrow_mut().pop_front() {
Some(mut buf) => {
debug_assert!(buf.data.is_empty());
buf.data.reserve(needed);
buf
}
None => Box::new(BufData::with_capacity(Rc::downgrade(&self.g), needed)),
}
}
pub(crate) fn text_message(self: &Rc<Self>, text: &str) -> Result<()> {
let bytes = text.as_bytes();
let mut buf = self.next_buffer(bytes.len());
buf.data.extend_from_slice(bytes);
self.message(Mode::Text, buf)
}
fn body_format(header: &api::ResponseHeader) -> Result<Format> {
let Some(format) = Format::from_u8(header.format) else {
return Err(Error::new(ErrorKind::UnknownFormat(header.format)));
};
Ok(format)
}
fn server_error(
id: MessageId,
header: &api::ResponseHeader,
buf: &[u8],
mut at: usize,
unsupported: &str,
) -> Result<ServerError> {
let typed = match id {
MessageId::ERROR_MESSAGE => false,
MessageId::TYPED_ERROR_MESSAGE => true,
_ => return Ok(ServerError::message(unsupported)),
};
let format = Self::body_format(header)?;
let error: api::ErrorMessage<'_> = format
.decode(buf, &mut at)
.map_err(Error::decode_error_message)?;
let payload = if typed {
Some((format, Box::from(buf.get(at..).unwrap_or_default())))
} else {
None
};
Ok(ServerError {
message: String::from(error.message),
payload,
})
}
pub(crate) fn message(self: &Rc<Self>, mode: Mode, buf: Box<BufData>) -> Result<()> {
let buf = BufRc::new(buf);
let mut at = 0;
let header: api::ResponseHeader = match format::decode_envelope(mode, &buf, &mut at) {
Ok(header) => header,
Err(error) if error.unsupported_version().is_some() => {
self.on_error.call(Error::decode_response_header(error));
return self.close_and_reconnect();
}
Err(error) => return Err(Error::decode_response_header(error)),
};
if let Some(broadcast) = MessageId::new(header.broadcast) {
tracing::debug!(?header, "Got broadcast");
if broadcast == MessageId::SERVER_HELLO {
tracing::debug!("Server hello, negotiating format");
self.send_negotiate()?;
if self.app != 0 && header.app != 0 && header.app != self.app {
self.incompatible(header.app);
}
return Ok(());
}
if !self.defer_broadcasts(broadcast) {
return Ok(());
};
if let Some(id) = MessageId::new(header.error) {
let error = Self::server_error(id, &header, &buf, at, "Unsupported broadcast")?;
while let Some(callback) = self.defer_broadcasts.borrow_mut().pop_front() {
if let Some(callback) = callback.upgrade() {
callback.call(Err(Error::server(error.clone())));
}
}
return Ok(());
}
let format = Self::body_format(&header)?;
let packet = RawPacket {
buf: Some(buf.clone()),
at: Cell::new(at),
id: broadcast,
format,
channel: header.channel,
};
while let Some(callback) = self.defer_broadcasts.borrow_mut().pop_front() {
if let Some(callback) = callback.upgrade() {
callback.call(Ok(packet.clone()));
}
}
} else {
tracing::debug!(?header, "Got response");
let p = self.g.requests.borrow_mut().remove(&header.serial);
if let Some(p) = p {
if let Some(id) = MessageId::new(header.error) {
let error =
match Self::server_error(id, &header, &buf, at, "Unsupported request") {
Ok(error) => error,
Err(error) if p.callback.as_negotiate().is_none() => {
p.callback.error(error);
return Ok(());
}
Err(error) => return Err(error),
};
if let Some((format, requested)) = p.callback.as_negotiate() {
self.on_error.call(Error::message(format_args!(
"Server rejected format `{format}` in `{requested}` mode ({}), falling back to `{}`",
error.message,
Format::DEFAULT
)));
self.format.set(Format::DEFAULT);
self.mode.set(mode);
self.emit_state_change(State::Open);
return Ok(());
}
p.callback.error(Error::server(error));
return Ok(());
}
match p.id {
MessageId::NEGOTIATE => {
let Some((requested, _)) = p.callback.as_negotiate() else {
p.callback
.error(Error::message("Unexpected negotiate response"));
return Ok(());
};
let accepted = Format::from_u8(header.format).unwrap_or(requested);
tracing::debug!(?accepted, ?mode, "Format negotiated");
self.format.set(accepted);
self.mode.set(mode);
self.emit_state_change(State::Open);
return Ok(());
}
MessageId::CONNECT => {
let Some(callback) = &p.callback.as_channel() else {
p.callback
.error(Error::message("Unexpected channel response"));
return Ok(());
};
callback(Ok(header.channel));
return Ok(());
}
_ => {
let Some(callback) = p.callback.as_request() else {
p.callback
.error(Error::message("Unexpected channel response"));
return Ok(());
};
let format = Self::body_format(&header)?;
let packet = RawPacket {
id: p.id,
buf: Some(buf),
at: Cell::new(at),
format,
channel: header.channel,
};
callback.call(Ok(packet));
return Ok(());
}
}
}
tracing::trace!(?header.serial, "Got message with unknown serial");
}
Ok(())
}
fn defer_broadcasts(self: &Rc<Self>, kind: MessageId) -> bool {
let mut defer = self.defer_broadcasts.borrow_mut();
let broadcasts = self.g.broadcasts.borrow();
let Some(broadcasts) = broadcasts.get(&kind) else {
return false;
};
for (_, callback) in broadcasts.iter() {
defer.push_back(Rc::downgrade(callback));
}
!defer.is_empty()
}
pub(crate) fn close_and_reconnect(self: &Rc<Self>) -> Result<(), Error> {
tracing::debug!("Closing and reconnecting");
let shared = Rc::downgrade(self);
if self.state.get() == State::Closed {
let current_timeout = self.current_timeout.get();
if current_timeout < MAX_TIMEOUT {
let fuzz = H::random(50);
self.current_timeout.set(
current_timeout
.saturating_mul(2)
.saturating_add(fuzz)
.min(MAX_TIMEOUT),
);
}
} else {
self.current_timeout.set(INITIAL_TIMEOUT);
self.close_once();
}
let timeout = self.current_timeout.get();
tracing::debug!(?timeout, "Setting");
let timeout = self.window.set_timeout(timeout, move || {
if let Some(shared) = shared.upgrade() {
Self::try_once(&shared);
}
})?;
drop(self.reconnect_timeout.borrow_mut().replace(timeout));
self.connecting.set(false);
Ok(())
}
fn incompatible(self: &Rc<Self>, server: u32) {
let client = self.app;
tracing::debug!(server, client, "Incompatible server");
self.should_be.set(State::Closed);
let timeout = self.reconnect_timeout.borrow_mut().take();
drop(timeout);
self.current_timeout.set(INITIAL_TIMEOUT);
self.connecting.set(false);
if let Some(s) = self.socket.take() {
if let Err(error) = s.close() {
self.on_error.call(error);
}
}
self.close_pending();
self.emit_state_change(State::Incompatible { server, client });
}
fn fail_request(&self, serial: u32, error: Error) {
let p = self.g.requests.borrow_mut().remove(&serial);
if let Some(p) = p {
p.callback.error(error);
}
}
fn close_pending(self: &Rc<Self>) {
loop {
let Some(serial) = self.g.requests.borrow().keys().next().copied() else {
break;
};
let p = {
let mut requests = self.g.requests.borrow_mut();
let Some(p) = requests.remove(&serial) else {
break;
};
p
};
p.callback
.error(Error::new(ErrorKind::ConnectionLost("Connection closed")));
}
}
fn emit_state_change(&self, state: State) {
if self.state.get() == state {
return;
}
self.state.set(state);
if state == State::Open {
let generation = self.g.generation.get().wrapping_add(1);
self.g.generation.set(generation);
}
{
let mut defer = self.defer_state_listeners.borrow_mut();
for (_, callback) in self.g.state_listeners.borrow().iter() {
defer.push_back(Rc::downgrade(callback));
}
if defer.is_empty() {
return;
}
}
while let Some(callback) = self.defer_state_listeners.borrow_mut().pop_front() {
if let Some(callback) = callback.upgrade() {
callback.call(state);
}
}
}
#[inline]
fn open(self: &Rc<Self>) {
self.should_be.set(State::Open);
self.try_once();
}
fn close(self: &Rc<Self>) {
self.should_be.set(State::Closed);
let timeout = self.reconnect_timeout.borrow_mut().take();
drop(timeout);
self.current_timeout.set(INITIAL_TIMEOUT);
self.close_once();
}
fn try_once(self: &Rc<Self>) {
if self.should_be.get() == State::Open && !self.connecting.get() {
tracing::debug!("Trying to open connection");
self.connect_once();
}
}
fn close_once(self: &Rc<Self>) {
self.connecting.set(false);
self.emit_state_change(State::Closed);
if let Some(s) = self.socket.take() {
tracing::debug!("Closing old socket once");
if let Err(error) = s.close() {
self.on_error.call(error);
}
}
self.close_pending();
}
fn connect_once(self: &Rc<Self>) {
self.connecting.set(true);
if let Err(error) = self.try_connect() {
self.on_error.call(error);
self.should_be.set(State::Closed);
self.connecting.set(false);
}
}
fn try_connect(self: &Rc<Self>) -> Result<()> {
let url = match &self.connect.kind {
ConnectKind::Location { path } => {
let location = match WindowImpl::location(&self.window) {
Ok(location) => location,
Err(e) => {
return Err(Error::message(format_args!("Could not get location: {e}")));
}
};
let Location {
protocol,
host,
port,
} = location;
let protocol = match protocol.as_str() {
"https:" => "wss:",
"http:" => "ws:",
other => {
return Err(Error::message(format_args!(
"Unsupported protocol `{other}` for same host connection"
)));
}
};
let path = ForcePrefix(path, '/');
format!("{protocol}//{host}:{port}{path}")
}
ConnectKind::Url { url } => url.clone(),
};
if let Some(s) = self.socket.borrow_mut().take() {
tracing::debug!("Closing old socket");
if let Err(error) = s.close() {
self.on_error.call(error);
}
}
let ws = SocketImpl::new(&url, &self.handles)?;
*self.socket.borrow_mut() = Some(ws);
Ok(())
}
}
pub trait Callback<I>
where
Self: 'static,
{
fn call(&self, input: I);
}
impl<I> Callback<I> for EmptyCallback {
#[inline]
fn call(&self, _: I) {}
}
impl<F, I> Callback<I> for F
where
F: 'static + Fn(I),
{
#[inline]
fn call(&self, input: I) {
self(input)
}
}
pub struct ChannelBuilder<'a, H, C>
where
H: WebImpl,
{
shared: &'a Weak<Shared<H>>,
callback: C,
}
impl<'a, H, C> ChannelBuilder<'a, H, C>
where
H: WebImpl,
{
pub fn on_change<U>(self, callback: U) -> ChannelBuilder<'a, H, U>
where
U: Callback<ChannelEvent>,
{
ChannelBuilder {
shared: self.shared,
callback,
}
}
}
impl<'a, H, C> ChannelBuilder<'a, H, C>
where
C: Callback<ChannelEvent>,
H: WebImpl,
{
pub fn open(self) -> Channel<H> {
let inner = Rc::new(ChannelInner {
handle: Handle {
shared: self.shared.clone(),
},
id: Cell::new(ChannelId::NONE),
generation: Cell::new(0),
on_change: Box::new(self.callback),
queue: RefCell::new(VecDeque::new()),
connect: RefCell::new(Request::new()),
state: RefCell::new(StateListener::new()),
});
let (state, listener) = inner.handle.on_state_change({
let inner = Rc::downgrade(&inner);
move |state: State| {
if let Some(inner) = inner.upgrade() {
inner.on_state(state);
}
}
});
*inner.state.borrow_mut() = listener;
if state.is_open() {
inner.connect();
}
Channel { inner }
}
}
fn connect_channel<H>(
shared: &Weak<Shared<H>>,
callback: impl Fn(Result<ChannelId, Error>) + 'static,
) -> Request
where
H: WebImpl,
{
struct RequestCallbackImpl<C>(C);
impl<C> RequestCallback for RequestCallbackImpl<C>
where
C: Fn(Result<ChannelId, Error>) + 'static,
{
#[inline]
fn as_channel(&self) -> Option<&(dyn Fn(Result<ChannelId, Error>) + 'static)> {
Some(&self.0)
}
#[inline]
fn error(&self, error: Error) {
(self.0)(Err(error));
}
}
let Some(shared) = shared.upgrade() else {
callback(Err(Error::new(ErrorKind::NotConnected(
"WebSocket service is down",
))));
return Request::new();
};
if shared.state.get() != State::Open {
callback(Err(Error::new(ErrorKind::NotConnected(
"WebSocket is not connected",
))));
return Request::new();
}
let serial = shared.serial.get();
shared.serial.set(serial.wrapping_add(1));
let pending = Pending {
id: MessageId::CONNECT,
serial,
callback: RequestCallbackImpl(callback),
};
let existing = shared
.g
.requests
.borrow_mut()
.insert(serial, Box::new(pending));
if let Some(p) = existing {
p.callback.error(Error::new(ErrorKind::Cancelled));
}
if let Err(error) = shared.send_connect(serial) {
shared.fail_request(serial, error);
return Request::new();
}
Request {
serial,
g: Rc::downgrade(&shared.g),
}
}
pub struct RequestBuilder<'a, H, B, C>
where
H: WebImpl,
{
shared: &'a Weak<Shared<H>>,
channel: Option<ChannelId>,
body: B,
callback: C,
}
impl<'a, H, B, C> RequestBuilder<'a, H, B, C>
where
H: WebImpl,
{
#[inline]
pub fn body<U>(self, body: U) -> RequestBuilder<'a, H, U, C>
where
U: api::Request,
{
RequestBuilder {
shared: self.shared,
channel: self.channel,
body,
callback: self.callback,
}
}
pub fn on_packet<E>(
self,
callback: impl Callback<Result<Packet<E>>>,
) -> RequestBuilder<'a, H, B, impl Callback<Result<RawPacket>>>
where
E: api::Endpoint,
{
self.on_raw_packet(move |result: Result<RawPacket>| match result {
Ok(ok) => callback.call(Ok(Packet::new(ok))),
Err(err) => callback.call(Err(err)),
})
}
pub fn on_raw_packet<U>(self, callback: U) -> RequestBuilder<'a, H, B, U>
where
U: Callback<Result<RawPacket, Error>>,
{
RequestBuilder {
shared: self.shared,
channel: self.channel,
body: self.body,
callback,
}
}
}
impl<'a, H, B, C> RequestBuilder<'a, H, B, C>
where
B: api::Request,
C: Callback<Result<RawPacket>>,
H: WebImpl,
{
pub fn send(self) -> Request {
struct RequestCallbackImpl<C>(C);
impl<C> RequestCallback for RequestCallbackImpl<C>
where
C: Callback<Result<RawPacket>>,
{
#[inline]
fn as_request(&self) -> Option<&(dyn Callback<Result<RawPacket>> + 'static)> {
Some(&self.0)
}
#[inline]
fn error(&self, error: Error) {
self.0.call(Err(error));
}
}
let Some(shared) = self.shared.upgrade() else {
self.callback.call(Err(Error::new(ErrorKind::NotConnected(
"WebSocket service is down",
))));
return Request::new();
};
if shared.state.get() != State::Open {
self.callback.call(Err(Error::new(ErrorKind::NotConnected(
"WebSocket is not connected",
))));
return Request::new();
}
let Some(channel) = self.channel else {
self.callback.call(Err(Error::new(ErrorKind::NotConnected(
"WebSocket request over closed channel",
))));
return Request::new();
};
let serial = shared.serial.get();
shared.serial.set(serial.wrapping_add(1));
let pending = Pending {
id: <B::Endpoint as api::Endpoint>::ID,
serial,
callback: RequestCallbackImpl(self.callback),
};
let existing = shared
.g
.requests
.borrow_mut()
.insert(serial, Box::new(pending));
if let Some(p) = existing {
p.callback.error(Error::new(ErrorKind::Cancelled));
}
if let Err(error) = shared.send_client_request(serial, channel, &self.body) {
shared.fail_request(serial, error);
return Request::new();
}
Request {
serial,
g: Rc::downgrade(&shared.g),
}
}
}
pub struct Request {
serial: u32,
g: Weak<Generic>,
}
impl Request {
#[inline]
pub const fn new() -> Self {
Self {
serial: 0,
g: Weak::new(),
}
}
pub fn clear(&mut self) {
let removed = {
let serial = mem::take(&mut self.serial);
let Some(g) = self.g.upgrade() else {
return;
};
self.g = Weak::new();
let Some(p) = g.requests.borrow_mut().remove(&serial) else {
return;
};
p
};
drop(removed);
}
#[inline]
pub fn is_pending(&self) -> bool {
let Some(g) = self.g.upgrade() else {
return false;
};
g.requests.borrow().contains_key(&self.serial)
}
}
impl Default for Request {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl Drop for Request {
#[inline]
fn drop(&mut self) {
self.clear();
}
}
#[must_use = "Futures do nothing unless polled, and dropping this cancels the request"]
pub struct Call<T> {
slot: Rc<CallSlot>,
request: Request,
_marker: PhantomData<fn() -> T>,
}
#[derive(Default)]
struct CallSlot {
result: Cell<Option<Result<RawPacket>>>,
waker: Cell<Option<Waker>>,
}
impl<T> Call<T> {
fn send<H, B>(builder: RequestBuilder<'_, H, B, EmptyCallback>) -> Self
where
H: WebImpl,
B: api::Request,
{
let slot = Rc::new(CallSlot::default());
let request = builder
.on_raw_packet({
let slot = slot.clone();
move |result: Result<RawPacket>| {
slot.result.set(Some(result));
if let Some(waker) = slot.waker.take() {
waker.wake();
}
}
})
.send();
Self {
slot,
request,
_marker: PhantomData,
}
}
#[inline]
pub fn is_pending(&self) -> bool {
self.request.is_pending()
}
}
impl<T> Future for Call<T>
where
for<'de> T: DecodeBody<'de>,
{
type Output = Result<T>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.get_mut();
let Some(result) = this.slot.result.take() else {
this.slot.waker.set(Some(cx.waker().clone()));
return Poll::Pending;
};
Poll::Ready(result.and_then(|packet| packet.decode()))
}
}
impl<T> fmt::Debug for Call<T> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Call")
.field("type", &any::type_name::<T>())
.field("is_pending", &self.is_pending())
.finish()
}
}
#[derive(Default)]
pub struct Requests {
requests: Vec<Request>,
}
impl Requests {
#[inline]
pub const fn new() -> Self {
Self {
requests: Vec::new(),
}
}
pub fn push(&mut self, request: Request) {
if !request.is_pending() {
return;
}
if self.requests.len() == self.requests.capacity() {
self.requests.retain(Request::is_pending);
}
self.requests.push(request);
}
pub fn len(&self) -> usize {
self.requests.iter().filter(|r| r.is_pending()).count()
}
pub fn is_empty(&self) -> bool {
!self.requests.iter().any(Request::is_pending)
}
pub fn clear(&mut self) {
let requests = mem::take(&mut self.requests);
drop(requests);
}
}
impl fmt::Debug for Requests {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Requests")
.field("len", &self.len())
.finish()
}
}
impl Extend<Request> for Requests {
fn extend<I>(&mut self, iter: I)
where
I: IntoIterator<Item = Request>,
{
for request in iter {
self.push(request);
}
}
}
pub struct KeyedRequests<K> {
requests: HashMap<K, Request>,
}
impl<K> KeyedRequests<K> {
#[inline]
pub fn new() -> Self {
Self {
requests: HashMap::new(),
}
}
pub fn len(&self) -> usize {
self.requests.values().filter(|r| r.is_pending()).count()
}
pub fn is_empty(&self) -> bool {
!self.requests.values().any(Request::is_pending)
}
pub fn keys(&self) -> impl Iterator<Item = &K> {
self.requests
.iter()
.filter(|(_, r)| r.is_pending())
.map(|(k, _)| k)
}
pub fn clear(&mut self) {
let requests = mem::take(&mut self.requests);
drop(requests);
}
}
impl<K> KeyedRequests<K>
where
K: Eq + Hash,
{
pub fn insert(&mut self, key: K, request: Request) {
if !request.is_pending() {
let old = self.requests.remove(&key);
drop(old);
return;
}
if self.requests.len() == self.requests.capacity() {
self.requests.retain(|_, r| r.is_pending());
}
let old = self.requests.insert(key, request);
drop(old);
}
pub fn remove<Q>(&mut self, key: &Q) -> bool
where
K: core::borrow::Borrow<Q>,
Q: ?Sized + Eq + Hash,
{
let Some(request) = self.requests.remove(key) else {
return false;
};
let pending = request.is_pending();
drop(request);
pending
}
pub fn is_pending<Q>(&self, key: &Q) -> bool
where
K: core::borrow::Borrow<Q>,
Q: ?Sized + Eq + Hash,
{
self.requests.get(key).is_some_and(Request::is_pending)
}
}
impl<K> Default for KeyedRequests<K> {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl<K> fmt::Debug for KeyedRequests<K>
where
K: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_set().entries(self.keys()).finish()
}
}
pub struct Listener {
kind: Option<MessageId>,
index: usize,
g: Weak<Generic>,
}
impl Listener {
#[inline]
pub const fn new() -> Self {
Self {
kind: None,
index: 0,
g: Weak::new(),
}
}
#[inline]
pub(crate) const fn empty_with_kind(kind: MessageId) -> Self {
Self {
kind: Some(kind),
index: 0,
g: Weak::new(),
}
}
pub fn clear(&mut self) {
let removed;
let removed_value;
{
let Some(g) = self.g.upgrade() else {
return;
};
self.g = Weak::new();
let index = mem::take(&mut self.index);
let Some(kind) = self.kind.take() else {
return;
};
let mut broadcasts = g.broadcasts.borrow_mut();
let Entry::Occupied(mut e) = broadcasts.entry(kind) else {
return;
};
removed = e.get_mut().try_remove(index);
if e.get().is_empty() {
removed_value = Some(e.remove());
} else {
removed_value = None;
}
}
drop(removed);
drop(removed_value);
}
}
impl Default for Listener {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl Drop for Listener {
#[inline]
fn drop(&mut self) {
self.clear();
}
}
pub struct StateListener {
index: usize,
g: Weak<Generic>,
}
impl StateListener {
#[inline]
pub const fn new() -> Self {
Self {
index: 0,
g: Weak::new(),
}
}
pub fn clear(&mut self) {
let removed = {
let Some(g) = self.g.upgrade() else {
return;
};
self.g = Weak::new();
g.state_listeners.borrow_mut().try_remove(self.index)
};
drop(removed);
}
}
impl Default for StateListener {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl Drop for StateListener {
#[inline]
fn drop(&mut self) {
self.clear();
}
}
pub(crate) struct BufData {
pub(crate) data: Vec<u8>,
strong: Cell<usize>,
g: Weak<Generic>,
}
impl BufData {
fn with_capacity(g: Weak<Generic>, capacity: usize) -> Self {
Self {
data: Vec::with_capacity(capacity),
strong: Cell::new(0),
g,
}
}
unsafe fn dec(ptr: NonNull<BufData>) {
unsafe {
let count = ptr.as_ref().strong.get().wrapping_sub(1);
ptr.as_ref().strong.set(count);
if count > 0 {
return;
}
let mut buf = Box::from_raw(ptr.as_ptr());
let Some(g) = buf.as_ref().g.upgrade() else {
return;
};
let mut buffers = g.buffers.borrow_mut();
buf.data.clear();
buf.data.shrink_to(MAX_CAPACITY);
buffers.push_back(buf);
}
}
unsafe fn inc(ptr: NonNull<BufData>) {
unsafe {
let count = ptr.as_ref().strong.get().wrapping_add(1);
if count == 0 {
std::process::abort();
}
ptr.as_ref().strong.set(count);
}
}
}
struct BufRc {
data: NonNull<BufData>,
}
impl BufRc {
fn new(data: Box<BufData>) -> Self {
let data = NonNull::from(Box::leak(data));
unsafe {
BufData::inc(data);
}
Self { data }
}
}
impl Deref for BufRc {
type Target = [u8];
fn deref(&self) -> &Self::Target {
unsafe { &(*self.data.as_ptr()).data }
}
}
impl Clone for BufRc {
fn clone(&self) -> Self {
unsafe {
BufData::inc(self.data);
}
Self { data: self.data }
}
}
impl Drop for BufRc {
fn drop(&mut self) {
unsafe {
BufData::dec(self.data);
}
}
}
#[derive(Clone)]
pub struct RawPacket {
id: MessageId,
buf: Option<BufRc>,
at: Cell<usize>,
format: Format,
channel: ChannelId,
}
impl RawPacket {
pub const fn empty() -> Self {
Self {
id: MessageId::EMPTY,
buf: None,
at: Cell::new(0),
format: Format::DEFAULT,
channel: ChannelId::NONE,
}
}
#[inline]
pub fn format(&self) -> Format {
self.format
}
#[inline]
pub fn channel(&self) -> ChannelId {
self.channel
}
#[inline]
pub fn is_from(&self, channel: ChannelId) -> bool {
channel != ChannelId::NONE && self.channel == channel
}
pub fn decode<'this, T>(&'this self) -> Result<T>
where
T: DecodeBody<'this>,
{
if self.id == MessageId::EMPTY {
return Err(Error::new(ErrorKind::EmptyPacket));
}
let mut at = self.at.get();
match self.format.decode(self.as_slice(), &mut at) {
Ok(value) => {
self.at.set(at);
Ok(value)
}
Err(error) => {
self.at.set(self.len());
Err(Error::decode_packet(error))
}
}
}
pub fn as_slice(&self) -> &[u8] {
match &self.buf {
Some(buf) => buf.as_ref(),
None => &[],
}
}
pub fn remaining(&self) -> usize {
self.as_slice().len().saturating_sub(self.at.get())
}
pub fn len(&self) -> usize {
match &self.buf {
Some(buf) => buf.len(),
None => 0,
}
}
pub fn is_empty(&self) -> bool {
self.at.get() >= self.len()
}
pub fn id(&self) -> MessageId {
self.id
}
}
impl fmt::Debug for RawPacket {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("RawPacket")
.field("remaining", &self.remaining())
.finish()
}
}
pub struct Packet<T> {
raw: RawPacket,
_marker: PhantomData<T>,
}
impl<T> Packet<T> {
pub const fn empty() -> Self {
Self {
raw: RawPacket::empty(),
_marker: PhantomData,
}
}
#[inline]
pub fn channel(&self) -> ChannelId {
self.raw.channel()
}
#[inline]
pub fn is_from(&self, channel: ChannelId) -> bool {
self.raw.is_from(channel)
}
#[inline]
pub fn format(&self) -> Format {
self.raw.format()
}
#[inline]
pub fn new(raw: RawPacket) -> Self {
Self {
raw,
_marker: PhantomData,
}
}
pub fn into_raw(self) -> RawPacket {
self.raw
}
pub fn remaining(&self) -> usize {
self.raw.remaining()
}
pub fn is_empty(&self) -> bool {
self.raw.is_empty()
}
pub fn id(&self) -> MessageId {
self.raw.id()
}
}
impl<T> Packet<T>
where
T: api::Decodable,
{
pub fn decode(&self) -> Result<T::Type<'_>> {
self.decode_any()
}
pub fn decode_any<'de, R>(&'de self) -> Result<R>
where
R: DecodeBody<'de>,
{
self.raw.decode()
}
}
impl<T> Packet<T>
where
T: api::Endpoint,
{
pub fn decode_response(&self) -> Result<T::Response<'_>> {
self.decode_any_response()
}
pub fn decode_any_response<'de, R>(&'de self) -> Result<R>
where
R: DecodeBody<'de>,
{
self.raw.decode()
}
}
impl<T> Packet<T>
where
T: api::Broadcast,
{
pub fn decode_event<'de>(&'de self) -> Result<T::Event<'de>>
where
T: api::BroadcastWithEvent,
{
self.decode_event_any()
}
pub fn decode_event_any<'de, E>(&'de self) -> Result<E>
where
E: Event<Broadcast = T> + DecodeBody<'de>,
{
self.raw.decode()
}
}
impl<T> Clone for Packet<T> {
#[inline]
fn clone(&self) -> Self {
Self {
raw: self.raw.clone(),
_marker: PhantomData,
}
}
}
impl<T> fmt::Debug for Packet<T> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Packet")
.field("type", &any::type_name::<T>())
.field("remaining", &self.remaining())
.finish()
}
}
pub struct Handle<H>
where
H: WebImpl,
{
shared: Weak<Shared<H>>,
}
impl<H> Handle<H>
where
H: WebImpl,
{
pub fn format(&self) -> Format {
let Some(shared) = self.shared.upgrade() else {
return Format::DEFAULT;
};
shared.format.get()
}
pub fn mode(&self) -> Mode {
let Some(shared) = self.shared.upgrade() else {
return Mode::DEFAULT;
};
shared.mode.get()
}
pub fn generation(&self) -> u64 {
let Some(shared) = self.shared.upgrade() else {
return 0;
};
shared.g.generation.get()
}
pub fn channel(&self) -> ChannelBuilder<'_, H, EmptyCallback> {
ChannelBuilder {
shared: &self.shared,
callback: EmptyCallback,
}
}
pub fn request(&self) -> RequestBuilder<'_, H, EmptyBody, EmptyCallback> {
RequestBuilder {
shared: &self.shared,
channel: Some(ChannelId::NONE),
body: EmptyBody,
callback: EmptyCallback,
}
}
pub fn call<B>(&self, body: B) -> Call<<B::Endpoint as api::Endpoint>::Response<'static>>
where
B: api::Request,
for<'de> <B::Endpoint as api::Endpoint>::Response<'static>: DecodeBody<'de>,
{
Call::send(self.request().body(body))
}
pub fn on_broadcast<T>(&self, callback: impl Callback<Result<Packet<T>>>) -> Listener
where
T: api::Broadcast,
{
self.on_raw_broadcast::<T>(move |result| match result {
Ok(packet) => callback.call(Ok(Packet::new(packet))),
Err(error) => callback.call(Err(error)),
})
}
pub fn on_raw_broadcast<T>(&self, callback: impl Callback<Result<RawPacket>>) -> Listener
where
T: api::Broadcast,
{
let Some(shared) = self.shared.upgrade() else {
return Listener::empty_with_kind(T::ID);
};
let index = {
let mut broadcasts = shared.g.broadcasts.borrow_mut();
let slots = broadcasts.entry(T::ID).or_default();
slots.insert(Rc::new(callback))
};
Listener {
kind: Some(T::ID),
index,
g: Rc::downgrade(&shared.g),
}
}
pub fn on_state_change(&self, callback: impl Callback<State>) -> (State, StateListener) {
let Some(shared) = self.shared.upgrade() else {
return (
State::Closed,
StateListener {
index: 0,
g: Weak::new(),
},
);
};
let (state, index) = {
let index = shared
.g
.state_listeners
.borrow_mut()
.insert(Rc::new(callback));
(shared.state.get(), index)
};
let listener = StateListener {
index,
g: Rc::downgrade(&shared.g),
};
(state, listener)
}
pub fn on_open(&self, callback: impl Callback<u64>) -> (Option<u64>, StateListener) {
let Some(shared) = self.shared.upgrade() else {
return (None, StateListener::new());
};
let callback = {
let g = Rc::downgrade(&shared.g);
move |state: State| {
if state != State::Open {
return;
}
if let Some(g) = g.upgrade() {
callback.call(g.generation.get());
}
}
};
let index = shared
.g
.state_listeners
.borrow_mut()
.insert(Rc::new(callback));
let generation = shared
.state
.get()
.is_open()
.then(|| shared.g.generation.get());
let listener = StateListener {
index,
g: Rc::downgrade(&shared.g),
};
(generation, listener)
}
}
impl<H> Clone for Handle<H>
where
H: WebImpl,
{
#[inline]
fn clone(&self) -> Self {
Self {
shared: self.shared.clone(),
}
}
}
impl<H> Default for Handle<H>
where
H: WebImpl,
{
#[inline]
fn default() -> Self {
Self {
shared: Weak::new(),
}
}
}
impl<H> PartialEq for Handle<H>
where
H: WebImpl,
{
#[inline]
fn eq(&self, other: &Self) -> bool {
Weak::ptr_eq(&self.shared, &other.shared)
}
}
impl<H> fmt::Debug for Handle<H>
where
H: WebImpl,
{
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut f = f.debug_struct("Handle");
if let Some(shared) = self.shared.upgrade() {
f.field("connect", &shared.connect);
f.field("state", &shared.state.get());
}
f.finish()
}
}
#[derive(Debug)]
#[non_exhaustive]
pub enum ChannelEvent {
Opened(ChannelId),
Closed,
Failed(Error),
}
pub struct Channel<H>
where
H: WebImpl,
{
inner: Rc<ChannelInner<H>>,
}
struct ChannelInner<H>
where
H: WebImpl,
{
handle: Handle<H>,
id: Cell<ChannelId>,
generation: Cell<u64>,
on_change: Box<dyn Callback<ChannelEvent>>,
queue: RefCell<VecDeque<Box<dyn FnOnce(&Channel<H>)>>>,
connect: RefCell<Request>,
state: RefCell<StateListener>,
}
impl<H> ChannelInner<H>
where
H: WebImpl,
{
fn is_open(&self) -> bool {
let id = self.id.get();
if id == ChannelId::NONE {
return false;
}
let Some(shared) = self.handle.shared.upgrade() else {
return false;
};
shared.state.get().is_open() && shared.g.generation.get() == self.generation.get()
}
fn on_state(self: &Rc<Self>, state: State) {
match state {
State::Open => {
self.connect();
}
State::Closed | State::Incompatible { .. } => {
let connect = mem::take(&mut *self.connect.borrow_mut());
drop(connect);
if self.id.replace(ChannelId::NONE) != ChannelId::NONE {
self.on_change.call(ChannelEvent::Closed);
}
}
}
}
fn connect(self: &Rc<Self>) {
let request = connect_channel(&self.handle.shared, {
let inner = Rc::downgrade(self);
move |result| {
if let Some(inner) = inner.upgrade() {
inner.connected(result);
}
}
});
let old = mem::replace(&mut *self.connect.borrow_mut(), request);
drop(old);
}
fn connected(self: &Rc<Self>, result: Result<ChannelId>) {
let id = match result {
Ok(id) => id,
Err(error) => {
if !(error.is_connection_lost() || error.is_not_connected() || error.is_cancelled())
{
self.on_change.call(ChannelEvent::Failed(error));
}
return;
}
};
self.id.set(id);
self.generation.set(self.handle.generation());
let channel = Channel {
inner: self.clone(),
};
while channel.is_open() {
let Some(f) = self.queue.borrow_mut().pop_front() else {
break;
};
f(&channel);
}
if channel.is_open() && self.id.get() == id {
self.on_change.call(ChannelEvent::Opened(id));
}
}
}
impl<H> Drop for ChannelInner<H>
where
H: WebImpl,
{
#[inline]
fn drop(&mut self) {
if self.is_open() {
if let Some(shared) = self.handle.shared.upgrade() {
shared.remove_channel(self.id.get());
}
}
}
}
impl<H> Channel<H>
where
H: WebImpl,
{
#[inline]
pub fn id(&self) -> ChannelId {
if self.inner.is_open() {
self.inner.id.get()
} else {
ChannelId::NONE
}
}
#[inline]
pub fn generation(&self) -> u64 {
self.inner.generation.get()
}
#[inline]
pub fn is_open(&self) -> bool {
self.inner.is_open()
}
#[inline]
pub fn handle(&self) -> &Handle<H> {
&self.inner.handle
}
pub fn request(&self) -> RequestBuilder<'_, H, EmptyBody, EmptyCallback> {
RequestBuilder {
shared: &self.inner.handle.shared,
channel: self.inner.is_open().then(|| self.inner.id.get()),
body: EmptyBody,
callback: EmptyCallback,
}
}
pub fn call<B>(&self, body: B) -> Call<<B::Endpoint as api::Endpoint>::Response<'static>>
where
B: api::Request,
for<'de> <B::Endpoint as api::Endpoint>::Response<'static>: DecodeBody<'de>,
{
Call::send(self.request().body(body))
}
pub fn when_open(&self, f: impl FnOnce(&Channel<H>) + 'static) {
if self.inner.is_open() {
f(self);
return;
}
self.inner.queue.borrow_mut().push_back(Box::new(f));
}
}
impl<H> Clone for Channel<H>
where
H: WebImpl,
{
#[inline]
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
}
}
}
impl<H> Default for Channel<H>
where
H: WebImpl,
{
#[inline]
fn default() -> Self {
Self {
inner: Rc::new(ChannelInner {
handle: Handle::default(),
id: Cell::new(ChannelId::NONE),
generation: Cell::new(0),
on_change: Box::new(EmptyCallback),
queue: RefCell::new(VecDeque::new()),
connect: RefCell::new(Request::new()),
state: RefCell::new(StateListener::new()),
}),
}
}
}
impl<H> PartialEq for Channel<H>
where
H: WebImpl,
{
#[inline]
fn eq(&self, other: &Self) -> bool {
Weak::ptr_eq(&self.inner.handle.shared, &other.inner.handle.shared)
&& self.id() == other.id()
}
}
impl<H> fmt::Debug for Channel<H>
where
H: WebImpl,
{
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut f = f.debug_struct("Channel");
if let Some(shared) = self.inner.handle.shared.upgrade() {
f.field("connect", &shared.connect);
f.field("state", &shared.state.get());
}
f.field("id", &self.id());
f.field("generation", &self.generation());
f.finish()
}
}
struct Pending<C>
where
C: ?Sized,
{
id: MessageId,
serial: u32,
callback: C,
}
impl<C> fmt::Debug for Pending<C> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Pending")
.field("serial", &self.serial)
.field("id", &self.id)
.finish_non_exhaustive()
}
}
struct ForcePrefix<'a>(&'a str, char);
impl fmt::Display for ForcePrefix<'_> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self(string, prefix) = *self;
prefix.fmt(f)?;
string.trim_start_matches(prefix).fmt(f)?;
Ok(())
}
}