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 Options::additional_headers and subprotocols #27

Merged
merged 30 commits into from
Oct 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
7baa740
fix: add Origin Header in native app
Its-Just-Nans Feb 13, 2024
39c1600
correct cargo.toml
Its-Just-Nans Feb 13, 2024
722c5f3
Merge branch 'main' into origin-header
Its-Just-Nans May 13, 2024
6629215
update
Its-Just-Nans May 13, 2024
f6d612e
add trunk
Its-Just-Nans May 13, 2024
e79b007
rm manifest
Its-Just-Nans May 13, 2024
bf3e070
rm
Its-Just-Nans May 13, 2024
a45012a
Merge branch 'main' into origin-header
Its-Just-Nans Jun 2, 2024
164d67d
bump version and clippy
Its-Just-Nans Jun 2, 2024
68c2b0a
bump echo_server and TODO
Its-Just-Nans Jun 2, 2024
aa122db
todo done
Its-Just-Nans Jun 2, 2024
a1c33bb
correct if
Its-Just-Nans Jun 2, 2024
1f1fe14
docs
Its-Just-Nans Jun 2, 2024
2154c81
bump to 0.23 !
Its-Just-Nans Jun 2, 2024
66a3534
docs
Its-Just-Nans Jun 2, 2024
f8abaa7
Update main.rs
Its-Just-Nans Jun 2, 2024
34fba9d
remove useless
Its-Just-Nans Jun 2, 2024
c89118e
Merge branch 'origin-header' of github.com:Its-Just-Nans/ewebsock int…
Its-Just-Nans Jun 2, 2024
d1545f3
typos
Its-Just-Nans Jun 3, 2024
d1d5c09
solve problems
Its-Just-Nans Jun 5, 2024
a60d82c
Merge branch 'main' into origin-header
Its-Just-Nans Jun 5, 2024
0ac302f
add for native tokio
Its-Just-Nans Jun 6, 2024
4443a06
clean
Its-Just-Nans Jun 6, 2024
c70c985
add links
Its-Just-Nans Jun 6, 2024
baa5bbb
add back
Its-Just-Nans Jun 6, 2024
8462ebf
Merge branch 'main' into origin-header
emilk Oct 10, 2024
b58c796
Revert Cargo.lock
emilk Oct 10, 2024
4eb7f00
Write which URL was bad in error message
emilk Oct 10, 2024
bf27223
Better URL parse error message
emilk Oct 10, 2024
19ad569
document that this is native-only
emilk Oct 10, 2024
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
19 changes: 17 additions & 2 deletions ewebsock/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ pub type Result<T> = std::result::Result<T, Error>;
pub(crate) type EventHandler = Box<dyn Send + Fn(WsEvent) -> 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 @@ -134,6 +134,19 @@ pub struct Options {
/// Ignored on Web.
pub max_incoming_frame_size: usize,

/// Additional Request headers.
///
/// Currently only supported on native.
pub additional_headers: Vec<(String, String)>,

/// Additional subprotocols.
///
/// <https://www.iana.org/assignments/websocket/websocket.xml>
/// <https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_servers#miscellaneous>
///
/// Currently only supported on native.
pub subprotocols: Vec<String>,

/// Delay blocking in ms - default 10ms
pub delay_blocking: std::time::Duration,
}
Expand All @@ -142,7 +155,9 @@ impl Default for Options {
fn default() -> Self {
Self {
max_incoming_frame_size: 64 * 1024 * 1024,
delay_blocking: std::time::Duration::from_millis(10),
additional_headers: vec![],
subprotocols: vec![],
delay_blocking: std::time::Duration::from_millis(10), // default value 10ms,
}
}
}
Expand Down
57 changes: 39 additions & 18 deletions ewebsock/src/native_tungstenite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use std::{
sync::mpsc::{Receiver, TryRecvError},
};

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

/// This is how you send [`WsMessage`]s to the server.
Expand Down Expand Up @@ -64,21 +65,27 @@ pub(crate) fn ws_receive_impl(url: String, options: Options, on_event: EventHand

/// Connect and call the given event handler on each received event.
///
/// Blocking version of [`ws_receive`], only available on native.
/// Blocking version of [`crate::ws_receive`], only available on native.
///
/// # 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 uri: tungstenite::http::Uri = url
.parse()
.map_err(|err| format!("Failed to parse URL {url:?}: {err}"))?;
let config = tungstenite::protocol::WebSocketConfig::from(options.clone());
let max_redirects = 3; // tungstenite default

let (mut socket, response) =
match tungstenite::client::connect_with_config(url, Some(config), max_redirects) {
Ok(result) => result,
Err(err) => {
return Err(format!("Connect: {err}"));
}
};
let (mut socket, response) = match tungstenite::client::connect_with_config(
into_requester(uri, options),
Some(config),
max_redirects,
) {
Ok(result) => result,
Err(err) => {
return Err(format!("Connect: {err}"));
}
};

log::debug!("WebSocket HTTP response code: {}", response.status());
log::trace!(
Expand Down Expand Up @@ -166,15 +173,21 @@ pub fn ws_connect_blocking(
rx: &Receiver<WsMessage>,
) -> Result<()> {
let delay = options.delay_blocking;
let config = tungstenite::protocol::WebSocketConfig::from(options);
let config = tungstenite::protocol::WebSocketConfig::from(options.clone());
let max_redirects = 3; // tungstenite default
let (mut socket, response) =
match tungstenite::client::connect_with_config(url, Some(config), max_redirects) {
Ok(result) => result,
Err(err) => {
return Err(format!("Connect: {err}"));
}
};
let uri: tungstenite::http::Uri = url
.parse()
.map_err(|err| format!("Failed to parse URL {url:?}: {err}"))?;
let (mut socket, response) = match tungstenite::client::connect_with_config(
into_requester(uri, options),
Some(config),
max_redirects,
) {
Ok(result) => result,
Err(err) => {
return Err(format!("Connect: {err}"));
}
};

log::debug!("WebSocket HTTP response code: {}", response.status());
log::trace!(
Expand Down Expand Up @@ -217,7 +230,7 @@ pub fn ws_connect_blocking(
WsMessage::Pong(data) => tungstenite::protocol::Message::Pong(data),
WsMessage::Unknown(_) => panic!("You cannot send WsMessage::Unknown"),
};
if let Err(err) = socket.write(outgoing_message) {
if let Err(err) = socket.send(outgoing_message) {
socket.close(None).ok();
socket.flush().ok();
return Err(format!("send: {err}"));
Expand Down Expand Up @@ -278,3 +291,11 @@ pub fn ws_connect_blocking(
}
}
}

#[test]
fn test_connect() {
let options = crate::Options::default();
// see documentation for more options
let (mut sender, _receiver) = crate::connect("ws://example.com", options).unwrap();
sender.send(crate::WsMessage::Text("Hello!".into()));
}
30 changes: 27 additions & 3 deletions ewebsock/src/native_tungstenite_tokio.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::ops::ControlFlow;

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

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

let config = tungstenite::protocol::WebSocketConfig::from(options);
let uri: tungstenite::http::Uri = match url.parse() {
Ok(uri) => uri,
Err(err) => {
on_event(WsEvent::Error(format!(
"Failed to parse URL {url:?}: {err}"
)));
return;
}
};
let config = tungstenite::protocol::WebSocketConfig::from(options.clone());
let disable_nagle = false; // God damn everyone who adds negations to the names of their variables
let (ws_stream, _response) = match tokio_tungstenite::connect_async_with_config(
url,
into_requester(uri, options),
Some(config),
disable_nagle,
)
Expand Down Expand Up @@ -146,3 +155,18 @@ fn ws_connect_native(url: String, options: Options, on_event: EventHandler) -> W
pub(crate) fn ws_receive_impl(url: String, options: Options, on_event: EventHandler) -> Result<()> {
ws_connect_impl(url, options, on_event).map(|sender| sender.forget())
}

#[cfg(feature = "tokio")]
#[test]
fn test_connect_tokio() {
tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap()
.block_on(async {
let options = crate::Options::default();
// see documentation for more options
let (mut sender, _receiver) = crate::connect("ws://example.com", options).unwrap();
sender.send(crate::WsMessage::Text("Hello!".into()));
});
}
15 changes: 15 additions & 0 deletions ewebsock/src/tungstenite_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,18 @@ impl From<crate::Options> for tungstenite::protocol::WebSocketConfig {
}
}
}

/// transform uri and options into a request builder
pub fn into_requester(
uri: tungstenite::http::Uri,
options: crate::Options,
) -> tungstenite::client::ClientRequestBuilder {
let mut client_request = tungstenite::client::ClientRequestBuilder::new(uri);
for (key, value) in options.additional_headers {
client_request = client_request.with_header(key, value);
}
for subprotocol in options.subprotocols {
client_request = client_request.with_sub_protocol(subprotocol);
}
client_request
}
2 changes: 1 addition & 1 deletion example_app/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

<head>
<!-- change this to your project name -->
<title>example_app websocket template</title>
<title>ewebsock demo</title>

<!-- config for our rust wasm binary. go to https://trunkrs.dev/assets/#rust for more customization -->
<link data-trunk rel="rust" data-wasm-opt="2" />
Expand Down
Loading