Expand description
sod::Service implementations to interact with tungstenite websockets.
§Service Impls
All Services are Retryable and are able to be blocking or non-blocking.
WsSessionis aMutServicethat wraps atungstenite::WebSocket, acceptingWsSessionEventto send or receive messages.WsSession::into_splitcan split aWsSessioninto aWsReader,WsWriter, andWsFlusher.WsReaderis aServicethat wraps aMutex<tungstenite::WebSocket>, accepting a()as input and producingtungstenite::Messageas output.WsWriteris aServicethat wraps aMutex<tungstenite::WebSocket>, accepting atungstenite::Messageas input.WsFlusheris aServicethat wraps aMutex<tungstenite::WebSocket>, accepting a()as input.WsServeris aServicethat that listens on a TCP port, accepting a()as input and producing aWsSessionas output.
§Features
native-tlsto enable Native TLS__rustls-tlsto enable Rustls TLS`
§Blocking Example
use sod::{idle::backoff, MaybeProcessService, MutService, RetryService, Service, ServiceChain};
use sod_tungstenite::{UninitializedWsSession, WsServer, WsSession, WsSessionEvent};
use std::{sync::atomic::Ordering, thread::spawn};
use tungstenite::{http::StatusCode, Message};
use url::Url;
// server session logic to add `"pong: "` in front of text payload
struct PongService;
impl Service for PongService {
type Input = Message;
type Output = Option<Message>;
type Error = ();
fn process(&self, input: Message) -> Result<Self::Output, Self::Error> {
match input {
Message::Text(text) => Ok(Some(Message::Text(format!("pong: {text}")))),
_ => Ok(None),
}
}
}
// wires session logic and spawns in new thread
struct SessionSpawner;
impl Service for SessionSpawner {
type Input = UninitializedWsSession;
type Output = ();
type Error = ();
fn process(&self, input: UninitializedWsSession) -> Result<Self::Output, Self::Error> {
spawn(|| {
let (r, w, f) = input.handshake().unwrap().into_split();
let chain = ServiceChain::start(r)
.next(PongService)
.next(MaybeProcessService::new(w))
.next(MaybeProcessService::new(f))
.end();
sod::thread::spawn_loop(chain, |err| {
println!("Session: {err:?}");
Err(err) // stop thread on error
});
});
Ok(())
}
}
// start a blocking server that creates blocking sessions
let server = WsServer::bind("127.0.0.1:48490").unwrap();
// spawn a thread to start accepting new server sessions
let handle = sod::thread::spawn_loop(
ServiceChain::start(server).next(SessionSpawner).end(),
|err| {
println!("Server: {err:?}");
Err(err) // stop thread on error
},
);
// connect a client to the server
let (mut client, _) =
WsSession::connect(Url::parse("ws://127.0.0.1:48490/socket").unwrap()).unwrap();
// client writes `"hello world"` payload
client
.process(WsSessionEvent::WriteMessage(Message::Text(
"hello world!".to_owned(),
)))
.unwrap();
// client receives `"pong: hello world"` payload
println!(
"Received: {:?}",
client.process(WsSessionEvent::ReadMessage).unwrap()
);
// join until server crashes
handle.join().unwrap();§Non-Blocking Example
use sod::{idle::backoff, MaybeProcessService, MutService, RetryService, Service, ServiceChain};
use sod_tungstenite::{UninitializedWsSession, WsServer, WsSession, WsSessionEvent};
use std::{sync::atomic::Ordering, thread::spawn};
use tungstenite::{http::StatusCode, Message};
use url::Url;
// server session logic to add `"pong: "` in front of text payload
struct PongService;
impl Service for PongService {
type Input = Message;
type Output = Option<Message>;
type Error = ();
fn process(&self, input: Message) -> Result<Self::Output, Self::Error> {
match input {
Message::Text(text) => Ok(Some(Message::Text(format!("pong: {text}")))),
_ => Ok(None),
}
}
}
// wires session logic and spawns in new thread
struct SessionSpawner;
impl Service for SessionSpawner {
type Input = UninitializedWsSession;
type Output = ();
type Error = ();
fn process(&self, input: UninitializedWsSession) -> Result<Self::Output, Self::Error> {
spawn(|| {
let (r, w, f) = input.handshake().unwrap().into_split();
let chain = ServiceChain::start(RetryService::new(r, backoff))
.next(PongService)
.next(MaybeProcessService::new(RetryService::new(w, backoff)))
.next(MaybeProcessService::new(f))
.end();
sod::thread::spawn_loop(chain, |err| {
println!("Session: {err:?}");
Err(err) // stop thread on error
});
});
Ok(())
}
}
// start a non-blocking server that creates non-blocking sessions
let server = WsServer::bind("127.0.0.1:48490")
.unwrap()
.with_nonblocking_sessions(true)
.with_nonblocking_server(true)
.unwrap();
// spawn a thread to start accepting new server sessions
let handle = sod::thread::spawn_loop(
ServiceChain::start(RetryService::new(server, backoff))
.next(SessionSpawner)
.end(),
|err| {
println!("Server: {err:?}");
Err(err) // stop thread on error
},
);
// connect a client to the server
let (mut client, response) =
WsSession::connect(Url::parse("ws://127.0.0.1:48490/socket").unwrap()).unwrap();
assert_eq!(response.status(), StatusCode::SWITCHING_PROTOCOLS);
// client writes `"hello world"` payload
client
.process(WsSessionEvent::WriteMessage(Message::Text(
"hello world!".to_owned(),
)))
.unwrap();
// client receives `"pong: hello world"` payload
assert_eq!(
client.process(WsSessionEvent::ReadMessage).unwrap(),
Some(Message::Text("pong: hello world!".to_owned()))
);
// stop the server
sod::idle::KEEP_RUNNING.store(false, Ordering::Release);
handle.join().unwrap();Re-exports§
pub extern crate tungstenite;
Structs§
- Uninitialized
WsSession - A
WsSessionthat has yet to complete its handshake. - WsFlusher
- The flush-side of a split
WsSession. - WsReader
- The read-side of a split
WsSession. - WsServer
- A TCP Server that produces
UninitializedWsSessionas output. - WsSession
- A
MutServicethat wraps atungstenite::WebSocket, processing aWsSessionEvent, producing aSome(Message)when aMessageis read, and producingNoneotherwise. - WsWriter
- The write-side of a split
WsSession.
Enums§
- Tls
- Used to configure if and how TLS is used for a
WsServer. - WsSession
Event - An input event for
WsSession, which can be a read or write.