chore: checkpoint before Python removal

This commit is contained in:
2026-03-26 22:33:59 +00:00
parent 683cec9307
commit e568ddf82a
29972 changed files with 11269302 additions and 2 deletions

View File

@@ -0,0 +1,9 @@
Examples
- [autobahn-client.rs](https://github.com/snapview/tokio-tungstenite/blob/master/examples/autobahn-client.rs)
- [autobahn-server.rs](https://github.com/snapview/tokio-tungstenite/blob/master/examples/autobahn-server.rs)
- [client.rs](https://github.com/snapview/tokio-tungstenite/blob/master/examples/client.rs)
- [echo-server.rs](https://github.com/snapview/tokio-tungstenite/blob/master/examples/echo-server.rs)
- [server.rs](https://github.com/snapview/tokio-tungstenite/blob/master/examples/server.rs)
- [server-headers.rs](https://github.com/snapview/tokio-tungstenite/blob/master/examples/server-headers.rs)
- [interval-server.rs](https://github.com/snapview/tokio-tungstenite/blob/master/examples/interval-server.rs)

View File

@@ -0,0 +1,54 @@
use futures_util::{SinkExt, StreamExt};
use log::*;
use tokio_tungstenite::{
connect_async,
tungstenite::{Error, Result},
};
const AGENT: &str = "Tungstenite";
async fn get_case_count() -> Result<u32> {
let (mut socket, _) = connect_async("ws://localhost:9001/getCaseCount").await?;
let msg = socket.next().await.expect("Can't fetch case count")?;
socket.close(None).await?;
Ok(msg.to_text()?.parse::<u32>().expect("Can't parse case count"))
}
async fn update_reports() -> Result<()> {
let (mut socket, _) =
connect_async(&format!("ws://localhost:9001/updateReports?agent={}", AGENT)).await?;
socket.close(None).await?;
Ok(())
}
async fn run_test(case: u32) -> Result<()> {
info!("Running test case {}", case);
let case_url = &format!("ws://localhost:9001/runCase?case={}&agent={}", case, AGENT);
let (mut ws_stream, _) = connect_async(case_url).await?;
while let Some(msg) = ws_stream.next().await {
let msg = msg?;
if msg.is_text() || msg.is_binary() {
ws_stream.send(msg).await?;
}
}
Ok(())
}
#[tokio::main]
async fn main() {
env_logger::init();
let total = get_case_count().await.expect("Error getting case count");
for case in 1..=total {
if let Err(e) = run_test(case).await {
match e {
Error::ConnectionClosed | Error::Protocol(_) | Error::Utf8 => (),
err => error!("Testcase failed: {}", err),
}
}
}
update_reports().await.expect("Error updating reports");
}

View File

@@ -0,0 +1,48 @@
use futures_util::{SinkExt, StreamExt};
use log::*;
use std::net::SocketAddr;
use tokio::net::{TcpListener, TcpStream};
use tokio_tungstenite::{
accept_async,
tungstenite::{Error, Result},
};
async fn accept_connection(peer: SocketAddr, stream: TcpStream) {
if let Err(e) = handle_connection(peer, stream).await {
match e {
Error::ConnectionClosed | Error::Protocol(_) | Error::Utf8 => (),
err => error!("Error processing connection: {}", err),
}
}
}
async fn handle_connection(peer: SocketAddr, stream: TcpStream) -> Result<()> {
let mut ws_stream = accept_async(stream).await.expect("Failed to accept");
info!("New WebSocket connection: {}", peer);
while let Some(msg) = ws_stream.next().await {
let msg = msg?;
if msg.is_text() || msg.is_binary() {
ws_stream.send(msg).await?;
}
}
Ok(())
}
#[tokio::main]
async fn main() {
env_logger::init();
let addr = "127.0.0.1:9002";
let listener = TcpListener::bind(&addr).await.expect("Can't listen");
info!("Listening on: {}", addr);
while let Ok((stream, _)) = listener.accept().await {
let peer = stream.peer_addr().expect("connected streams should have a peer address");
info!("Peer address: {}", peer);
tokio::spawn(accept_connection(peer, stream));
}
}

View File

@@ -0,0 +1,57 @@
//! A simple example of hooking up stdin/stdout to a WebSocket stream.
//!
//! This example will connect to a server specified in the argument list and
//! then forward all data read on stdin to the server, printing out all data
//! received on stdout.
//!
//! Note that this is not currently optimized for performance, especially around
//! buffer management. Rather it's intended to show an example of working with a
//! client.
//!
//! You can use this example together with the `server` example.
use std::env;
use futures_util::{future, pin_mut, StreamExt};
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio_tungstenite::{connect_async, tungstenite::protocol::Message};
#[tokio::main]
async fn main() {
let url =
env::args().nth(1).unwrap_or_else(|| panic!("this program requires at least one argument"));
let (stdin_tx, stdin_rx) = futures_channel::mpsc::unbounded();
tokio::spawn(read_stdin(stdin_tx));
let (ws_stream, _) = connect_async(&url).await.expect("Failed to connect");
println!("WebSocket handshake has been successfully completed");
let (write, read) = ws_stream.split();
let stdin_to_ws = stdin_rx.map(Ok).forward(write);
let ws_to_stdout = {
read.for_each(|message| async {
let data = message.unwrap().into_data();
tokio::io::stdout().write_all(&data).await.unwrap();
})
};
pin_mut!(stdin_to_ws, ws_to_stdout);
future::select(stdin_to_ws, ws_to_stdout).await;
}
// Our helper method which will read data from stdin and send it along the
// sender provided.
async fn read_stdin(tx: futures_channel::mpsc::UnboundedSender<Message>) {
let mut stdin = tokio::io::stdin();
loop {
let mut buf = vec![0; 1024];
let n = match stdin.read(&mut buf).await {
Err(_) | Ok(0) => break,
Ok(n) => n,
};
buf.truncate(n);
tx.unbounded_send(Message::binary(buf)).unwrap();
}
}

View File

@@ -0,0 +1,53 @@
//! A simple echo server.
//!
//! You can test this out by running:
//!
//! cargo run --example echo-server 127.0.0.1:12345
//!
//! And then in another window run:
//!
//! cargo run --example client ws://127.0.0.1:12345/
//!
//! Type a message into the client window, press enter to send it and
//! see it echoed back.
use std::{env, io::Error};
use futures_util::{future, StreamExt, TryStreamExt};
use log::info;
use tokio::net::{TcpListener, TcpStream};
#[tokio::main]
async fn main() -> Result<(), Error> {
let _ = env_logger::try_init();
let addr = env::args().nth(1).unwrap_or_else(|| "127.0.0.1:8080".to_string());
// Create the event loop and TCP listener we'll accept connections on.
let try_socket = TcpListener::bind(&addr).await;
let listener = try_socket.expect("Failed to bind");
info!("Listening on: {}", addr);
while let Ok((stream, _)) = listener.accept().await {
tokio::spawn(accept_connection(stream));
}
Ok(())
}
async fn accept_connection(stream: TcpStream) {
let addr = stream.peer_addr().expect("connected streams should have a peer address");
info!("Peer address: {}", addr);
let ws_stream = tokio_tungstenite::accept_async(stream)
.await
.expect("Error during the websocket handshake occurred");
info!("New WebSocket connection: {}", addr);
let (write, read) = ws_stream.split();
// We should not forward messages other than text or binary.
read.try_filter(|msg| future::ready(msg.is_text() || msg.is_binary()))
.forward(write)
.await
.expect("Failed to forward messages")
}

View File

@@ -0,0 +1,65 @@
use futures_util::{SinkExt, StreamExt};
use log::*;
use std::{net::SocketAddr, time::Duration};
use tokio::net::{TcpListener, TcpStream};
use tokio_tungstenite::{
accept_async,
tungstenite::{Error, Message, Result},
};
async fn accept_connection(peer: SocketAddr, stream: TcpStream) {
if let Err(e) = handle_connection(peer, stream).await {
match e {
Error::ConnectionClosed | Error::Protocol(_) | Error::Utf8 => (),
err => error!("Error processing connection: {}", err),
}
}
}
async fn handle_connection(peer: SocketAddr, stream: TcpStream) -> Result<()> {
let ws_stream = accept_async(stream).await.expect("Failed to accept");
info!("New WebSocket connection: {}", peer);
let (mut ws_sender, mut ws_receiver) = ws_stream.split();
let mut interval = tokio::time::interval(Duration::from_millis(1000));
// Echo incoming WebSocket messages and send a message periodically every second.
loop {
tokio::select! {
msg = ws_receiver.next() => {
match msg {
Some(msg) => {
let msg = msg?;
if msg.is_text() ||msg.is_binary() {
ws_sender.send(msg).await?;
} else if msg.is_close() {
break;
}
}
None => break,
}
}
_ = interval.tick() => {
ws_sender.send(Message::text("tick")).await?;
}
}
}
Ok(())
}
#[tokio::main]
async fn main() {
env_logger::init();
let addr = "127.0.0.1:9002";
let listener = TcpListener::bind(&addr).await.expect("Can't listen");
info!("Listening on: {}", addr);
while let Ok((stream, _)) = listener.accept().await {
let peer = stream.peer_addr().expect("connected streams should have a peer address");
info!("Peer address: {}", peer);
tokio::spawn(accept_connection(peer, stream));
}
}

View File

@@ -0,0 +1,183 @@
//! A chat server that broadcasts a message to all connections.
//!
//! This is a simple line-based server which accepts WebSocket connections,
//! reads lines from those connections, and broadcasts the lines to all other
//! connected clients.
//!
//! You can test this out by running:
//!
//! cargo run --example server 127.0.0.1:12345
//!
//! And then in another window run:
//!
//! cargo run --example client ws://127.0.0.1:12345/socket
//!
//! You can run the second command in multiple windows and then chat between the
//! two, seeing the messages from the other client as they're received. For all
//! connected clients they'll all join the same room and see everyone else's
//! messages.
use std::{
collections::HashMap,
convert::Infallible,
env,
net::SocketAddr,
sync::{Arc, Mutex},
};
use hyper::{
body::Incoming,
header::{
HeaderValue, CONNECTION, SEC_WEBSOCKET_ACCEPT, SEC_WEBSOCKET_KEY, SEC_WEBSOCKET_VERSION,
UPGRADE,
},
server::conn::http1,
service::service_fn,
upgrade::Upgraded,
Method, Request, Response, StatusCode, Version,
};
use hyper_util::rt::TokioIo;
use tokio::net::TcpListener;
use futures_channel::mpsc::{unbounded, UnboundedSender};
use futures_util::{future, pin_mut, stream::TryStreamExt, StreamExt};
use tokio_tungstenite::{
tungstenite::{
handshake::derive_accept_key,
protocol::{Message, Role},
},
WebSocketStream,
};
type Tx = UnboundedSender<Message>;
type PeerMap = Arc<Mutex<HashMap<SocketAddr, Tx>>>;
type Body = http_body_util::Full<hyper::body::Bytes>;
async fn handle_connection(
peer_map: PeerMap,
ws_stream: WebSocketStream<TokioIo<Upgraded>>,
addr: SocketAddr,
) {
println!("WebSocket connection established: {}", addr);
// Insert the write part of this peer to the peer map.
let (tx, rx) = unbounded();
peer_map.lock().unwrap().insert(addr, tx);
let (outgoing, incoming) = ws_stream.split();
let broadcast_incoming = incoming.try_for_each(|msg| {
println!("Received a message from {}: {}", addr, msg.to_text().unwrap());
let peers = peer_map.lock().unwrap();
// We want to broadcast the message to everyone except ourselves.
let broadcast_recipients =
peers.iter().filter(|(peer_addr, _)| peer_addr != &&addr).map(|(_, ws_sink)| ws_sink);
for recp in broadcast_recipients {
recp.unbounded_send(msg.clone()).unwrap();
}
future::ok(())
});
let receive_from_others = rx.map(Ok).forward(outgoing);
pin_mut!(broadcast_incoming, receive_from_others);
future::select(broadcast_incoming, receive_from_others).await;
println!("{} disconnected", &addr);
peer_map.lock().unwrap().remove(&addr);
}
async fn handle_request(
peer_map: PeerMap,
mut req: Request<Incoming>,
addr: SocketAddr,
) -> Result<Response<Body>, Infallible> {
println!("Received a new, potentially ws handshake");
println!("The request's path is: {}", req.uri().path());
println!("The request's headers are:");
for (ref header, _value) in req.headers() {
println!("* {}", header);
}
let upgrade = HeaderValue::from_static("Upgrade");
let websocket = HeaderValue::from_static("websocket");
let headers = req.headers();
let key = headers.get(SEC_WEBSOCKET_KEY);
let derived = key.map(|k| derive_accept_key(k.as_bytes()));
if req.method() != Method::GET
|| req.version() < Version::HTTP_11
|| !headers
.get(CONNECTION)
.and_then(|h| h.to_str().ok())
.map(|h| {
h.split(|c| c == ' ' || c == ',')
.any(|p| p.eq_ignore_ascii_case(upgrade.to_str().unwrap()))
})
.unwrap_or(false)
|| !headers
.get(UPGRADE)
.and_then(|h| h.to_str().ok())
.map(|h| h.eq_ignore_ascii_case("websocket"))
.unwrap_or(false)
|| !headers.get(SEC_WEBSOCKET_VERSION).map(|h| h == "13").unwrap_or(false)
|| key.is_none()
|| req.uri() != "/socket"
{
return Ok(Response::new(Body::from("Hello World!")));
}
let ver = req.version();
tokio::task::spawn(async move {
match hyper::upgrade::on(&mut req).await {
Ok(upgraded) => {
let upgraded = TokioIo::new(upgraded);
handle_connection(
peer_map,
WebSocketStream::from_raw_socket(upgraded, Role::Server, None).await,
addr,
)
.await;
}
Err(e) => println!("upgrade error: {}", e),
}
});
let mut res = Response::new(Body::default());
*res.status_mut() = StatusCode::SWITCHING_PROTOCOLS;
*res.version_mut() = ver;
res.headers_mut().append(CONNECTION, upgrade);
res.headers_mut().append(UPGRADE, websocket);
res.headers_mut().append(SEC_WEBSOCKET_ACCEPT, derived.unwrap().parse().unwrap());
// Let's add an additional header to our response to the client.
res.headers_mut().append("MyCustomHeader", ":)".parse().unwrap());
res.headers_mut().append("SOME_TUNGSTENITE_HEADER", "header_value".parse().unwrap());
Ok(res)
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let state = PeerMap::new(Mutex::new(HashMap::new()));
let addr =
env::args().nth(1).unwrap_or_else(|| "127.0.0.1:8080".to_string()).parse::<SocketAddr>()?;
let listener = TcpListener::bind(addr).await?;
loop {
let (stream, remote_addr) = listener.accept().await?;
let state = state.clone();
tokio::spawn(async move {
let io = TokioIo::new(stream);
let service = service_fn(move |req| handle_request(state.clone(), req, remote_addr));
let conn = http1::Builder::new().serve_connection(io, service).with_upgrades();
if let Err(err) = conn.await {
eprintln!("failed to serve connection: {err:?}");
}
});
}
}

View File

@@ -0,0 +1,84 @@
//! Read/Write headers on server example
//!
//! Run with logs:
//! Linux:
//! ```sh
//! RUST_LOG=debug cargo run --example server-headers
//! ```
//! Windows
//! ```sh
//! cmd /c "set RUST_LOG=debug && cargo run --example server-headers"
//! ```
use tokio::net::{TcpListener, TcpStream};
use tokio_tungstenite::{
accept_hdr_async,
tungstenite::{
connect,
handshake::server::{Request, Response},
Message,
},
};
#[macro_use]
extern crate log;
use futures_util::{SinkExt, StreamExt};
#[tokio::main]
async fn main() {
env_logger::builder().format_timestamp(None).init();
tokio::spawn(async move {
server().await;
});
client();
}
async fn server() {
let server = TcpListener::bind("127.0.0.1:8080").await.unwrap();
while let Ok((stream, _)) = server.accept().await {
tokio::spawn(accept_connection(stream));
}
}
async fn accept_connection(stream: TcpStream) {
let callback = |req: &Request, mut response: Response| {
debug!("Received a new ws handshake");
debug!("The request's path is: {}", req.uri().path());
debug!("The request's headers are:");
for (ref header, _value) in req.headers() {
debug!("* {}: {:?}", header, _value);
}
let headers = response.headers_mut();
headers.append("MyCustomHeader", ":)".parse().unwrap());
Ok(response)
};
let mut ws_stream = accept_hdr_async(stream, callback)
.await
.expect("Error during the websocket handshake occurred");
while let Some(msg) = ws_stream.next().await {
let msg = msg.unwrap();
if msg.is_text() || msg.is_binary() {
debug!("Server on message: {:?}", &msg);
ws_stream.send(msg).await.unwrap();
}
}
}
fn client() {
let (mut socket, response) = connect("ws://localhost:8080/socket").expect("Can't connect");
debug!("Connected to the server");
debug!("Response HTTP code: {}", response.status());
debug!("Response contains the following headers:");
for (ref header, _value) in response.headers() {
debug!("* {}: {:?}", header, _value);
}
socket.send(Message::Text("Hello WebSocket".into())).unwrap();
loop {
let msg = socket.read().expect("Error reading message");
debug!("Received: {}", msg);
}
}

View File

@@ -0,0 +1,92 @@
//! A chat server that broadcasts a message to all connections.
//!
//! This is a simple line-based server which accepts WebSocket connections,
//! reads lines from those connections, and broadcasts the lines to all other
//! connected clients.
//!
//! You can test this out by running:
//!
//! cargo run --example server 127.0.0.1:12345
//!
//! And then in another window run:
//!
//! cargo run --example client ws://127.0.0.1:12345/
//!
//! You can run the second command in multiple windows and then chat between the
//! two, seeing the messages from the other client as they're received. For all
//! connected clients they'll all join the same room and see everyone else's
//! messages.
use std::{
collections::HashMap,
env,
io::Error as IoError,
net::SocketAddr,
sync::{Arc, Mutex},
};
use futures_channel::mpsc::{unbounded, UnboundedSender};
use futures_util::{future, pin_mut, stream::TryStreamExt, StreamExt};
use tokio::net::{TcpListener, TcpStream};
use tokio_tungstenite::tungstenite::protocol::Message;
type Tx = UnboundedSender<Message>;
type PeerMap = Arc<Mutex<HashMap<SocketAddr, Tx>>>;
async fn handle_connection(peer_map: PeerMap, raw_stream: TcpStream, addr: SocketAddr) {
println!("Incoming TCP connection from: {}", addr);
let ws_stream = tokio_tungstenite::accept_async(raw_stream)
.await
.expect("Error during the websocket handshake occurred");
println!("WebSocket connection established: {}", addr);
// Insert the write part of this peer to the peer map.
let (tx, rx) = unbounded();
peer_map.lock().unwrap().insert(addr, tx);
let (outgoing, incoming) = ws_stream.split();
let broadcast_incoming = incoming.try_for_each(|msg| {
println!("Received a message from {}: {}", addr, msg.to_text().unwrap());
let peers = peer_map.lock().unwrap();
// We want to broadcast the message to everyone except ourselves.
let broadcast_recipients =
peers.iter().filter(|(peer_addr, _)| peer_addr != &&addr).map(|(_, ws_sink)| ws_sink);
for recp in broadcast_recipients {
recp.unbounded_send(msg.clone()).unwrap();
}
future::ok(())
});
let receive_from_others = rx.map(Ok).forward(outgoing);
pin_mut!(broadcast_incoming, receive_from_others);
future::select(broadcast_incoming, receive_from_others).await;
println!("{} disconnected", &addr);
peer_map.lock().unwrap().remove(&addr);
}
#[tokio::main]
async fn main() -> Result<(), IoError> {
let addr = env::args().nth(1).unwrap_or_else(|| "127.0.0.1:8080".to_string());
let state = PeerMap::new(Mutex::new(HashMap::new()));
// Create the event loop and TCP listener we'll accept connections on.
let try_socket = TcpListener::bind(&addr).await;
let listener = try_socket.expect("Failed to bind");
println!("Listening on: {}", addr);
// Let's spawn the handling of each connection in a separate task.
while let Ok((stream, addr)) = listener.accept().await {
tokio::spawn(handle_connection(state.clone(), stream, addr));
}
Ok(())
}