Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add protocols option #31

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions ewebsock/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,12 +117,12 @@ impl WsReceiver {
pub type Error = String;

/// Short for `Result<T, ewebsock::Error>`.
pub type Result<T> = std::result::Result<T, Error>;
pub type Result<T, E = Error> = std::result::Result<T, E>;

pub(crate) type EventHandler = Box<dyn Send + Fn(WsEvent) -> std::ops::ControlFlow<()>>;

/// Options for a connection.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Options {
/// The maximum size of a single incoming message frame, in bytes.
///
Expand All @@ -131,12 +131,17 @@ pub struct Options {
///
/// Ignored on Web.
pub max_incoming_frame_size: usize,
/// The sub-protocols requested to the server.
///
/// This is sent via the `Sec-WebSocket-Protocol` header.
pub protocols: Vec<String>,
}

impl Default for Options {
fn default() -> Self {
Self {
max_incoming_frame_size: 64 * 1024 * 1024,
protocols: Vec::new(),
}
}
}
Expand Down
10 changes: 6 additions & 4 deletions ewebsock/src/native_tungstenite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

use std::sync::mpsc::{Receiver, TryRecvError};

use crate::tungstenite_common::tungstenite_options;
use crate::{EventHandler, Options, Result, WsEvent, WsMessage};

/// This is how you send [`WsMessage`]s to the server.
Expand Down Expand Up @@ -69,11 +70,11 @@ pub(crate) fn ws_receive_impl(url: String, options: Options, on_event: EventHand
/// # Errors
/// All errors are returned to the caller, and NOT reported via `on_event`.
pub fn ws_receiver_blocking(url: &str, options: Options, on_event: &EventHandler) -> Result<()> {
let config = tungstenite::protocol::WebSocketConfig::from(options);
let (config, request) = tungstenite_options(url, options);
let max_redirects = 3; // tungstenite default

let (mut socket, response) =
match tungstenite::client::connect_with_config(url, Some(config), max_redirects) {
match tungstenite::client::connect_with_config(request, Some(config), max_redirects) {
Ok(result) => result,
Err(err) => {
return Err(format!("Connect: {err}"));
Expand Down Expand Up @@ -152,10 +153,11 @@ pub fn ws_connect_blocking(
on_event: &EventHandler,
rx: &Receiver<WsMessage>,
) -> Result<()> {
let config = tungstenite::protocol::WebSocketConfig::from(options);
let (config, request) = tungstenite_options(url, options);
let max_redirects = 3; // tungstenite default

let (mut socket, response) =
match tungstenite::client::connect_with_config(url, Some(config), max_redirects) {
match tungstenite::client::connect_with_config(request, Some(config), max_redirects) {
Ok(result) => result,
Err(err) => {
return Err(format!("Connect: {err}"));
Expand Down
27 changes: 13 additions & 14 deletions ewebsock/src/native_tungstenite_tokio.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::tungstenite_common::tungstenite_options;
use crate::{EventHandler, Options, Result, WsEvent, WsMessage};

/// This is how you send [`WsMessage`]s to the server.
Expand Down Expand Up @@ -51,21 +52,19 @@ async fn ws_connect_async(
) {
use futures::StreamExt as _;

let config = tungstenite::protocol::WebSocketConfig::from(options);
let (config, request) = tungstenite_options(&url, options);

let disable_nagle = false; // God damn everyone who adds negations to the names of their variables
let (ws_stream, _) = match tokio_tungstenite::connect_async_with_config(
url,
Some(config),
disable_nagle,
)
.await
{
Ok(result) => result,
Err(err) => {
on_event(WsEvent::Error(err.to_string()));
return;
}
};
let (ws_stream, _) =
match tokio_tungstenite::connect_async_with_config(request, Some(config), disable_nagle)
.await
{
Ok(result) => result,
Err(err) => {
on_event(WsEvent::Error(err.to_string()));
return;
}
};

log::info!("WebSocket handshake has been successfully completed");
on_event(WsEvent::Opened);
Expand Down
34 changes: 21 additions & 13 deletions ewebsock/src/tungstenite_common.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
impl From<crate::Options> for tungstenite::protocol::WebSocketConfig {
fn from(options: crate::Options) -> Self {
let crate::Options {
max_incoming_frame_size,
} = options;
use tungstenite::client::IntoClientRequest;
use tungstenite::handshake::client::Request;
use tungstenite::protocol::WebSocketConfig;

tungstenite::protocol::WebSocketConfig {
max_frame_size: if max_incoming_frame_size == usize::MAX {
None
} else {
Some(max_incoming_frame_size)
},
..Default::default()
}
pub fn tungstenite_options(url: &str, options: crate::Options) -> (WebSocketConfig, Request) {

Check failure on line 5 in ewebsock/src/tungstenite_common.rs

View workflow job for this annotation

GitHub Actions / Rust format, cranky, check, test, doc

this argument is passed by value, but not consumed in the function body
let mut request = url.into_client_request().unwrap();
if !options.protocols.is_empty() {
let protocols = options.protocols.join(", ").try_into().unwrap();
request
.headers_mut()
.insert("Sec-WebSocket-Protocol", protocols);
}

let max_frame_size =
(options.max_incoming_frame_size != usize::MAX).then_some(options.max_incoming_frame_size);

(
WebSocketConfig {
max_frame_size,
..Default::default()
},
request,
)
}
20 changes: 15 additions & 5 deletions ewebsock/src/web.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use wasm_bindgen::prelude::*;

use crate::{EventHandler, Options, Result, WsEvent, WsMessage};

#[allow(clippy::needless_pass_by_value)]
fn string_from_js_value(s: wasm_bindgen::JsValue) -> String {
fn string_from_js_value(s: JsValue) -> String {
s.as_string().unwrap_or(format!("{:#?}", s))
}

Expand Down Expand Up @@ -69,16 +71,24 @@ pub(crate) fn ws_receive_impl(url: String, options: Options, on_event: EventHand

pub(crate) fn ws_connect_impl(
url: String,
_ignored_options: Options,
options: Options,
on_event: EventHandler,
) -> Result<WsSender> {
// Based on https://rustwasm.github.io/wasm-bindgen/examples/websockets.html

use wasm_bindgen::closure::Closure;
use wasm_bindgen::JsCast as _;
// Custom binding because `WebSocket::new_with_str_sequence` takes &JsValue??
#[wasm_bindgen]
extern "C" {
type WebSocket;

#[wasm_bindgen(catch, constructor, js_class = "WebSocket")]
fn connect(url: &str, protocols: Vec<String>) -> Result<WebSocket, JsValue>;
}

// Connect to an server
let ws = web_sys::WebSocket::new(&url).map_err(string_from_js_value)?;
let ws = WebSocket::connect(&url, options.protocols)
.map_err(string_from_js_value)?
.unchecked_into::<web_sys::WebSocket>();

// For small binary messages, like CBOR, Arraybuffer is more efficient than Blob handling
ws.set_binary_type(web_sys::BinaryType::Arraybuffer);
Expand Down
Loading