Make the protocol transport-agnostic and use tokio-tungstenite
Replace the hand-rolled WebSocket implementation with a transport boundary (Frame over any Sink/Stream) plus adapters for tokio-tungstenite (server/client features) and axum (axum feature). The JSON wire format is unchanged, so existing peers keep working. Fixes: - unbounded frame lengths were allocated up front; the server now enforces message/frame size limits (1 MiB default) - responses arriving before `request` subscribed were lost - an accept() error ended `session_loop` - requests sent right after connecting could arrive before handlers were registered; the receiver now starts after `on_conn` returns - panics in the receive loop skipped `on_close` and leaked sessions; `on_close` now runs exactly once and handler panics fail only their request - unknown methods and invalid data got no reply; they now get an error response - slow handlers blocked pongs and responses Adds `on_notification`, `request_timeout`, `closed`, `is_closed`, `id`, `ServerConfig`, `from_transport`, `from_tungstenite` and `from_axum`, integration tests, and an axum example. Bumps to 0.2.0 since `Session::connect` now takes a URL and the `ws` module is removed. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
//! The same server as `examples/server.rs`, mounted in an axum router next to
|
||||
//! ordinary HTTP routes.
|
||||
|
||||
use axum::{Router, extract::WebSocketUpgrade, response::Response, routing::get};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use session_rs::{Method, Session};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
struct Data;
|
||||
|
||||
impl Method for Data {
|
||||
const NAME: &'static str = "data";
|
||||
type Request = String;
|
||||
type Response = String;
|
||||
type Error = String;
|
||||
}
|
||||
|
||||
async fn ws(upgrade: WebSocketUpgrade) -> Response {
|
||||
upgrade.max_message_size(1 << 20).on_upgrade(async |socket| {
|
||||
let session = Session::from_axum(socket);
|
||||
|
||||
session
|
||||
.on_request::<Data, _>(async |_, req| {
|
||||
println!("Msg from client: {req}");
|
||||
Ok(format!("Hello from axum, you said {req:?}"))
|
||||
})
|
||||
.await;
|
||||
|
||||
session.start_receiver();
|
||||
})
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> std::io::Result<()> {
|
||||
let app = Router::new()
|
||||
.route("/", get(ws))
|
||||
.route("/health", get(async || "ok"));
|
||||
|
||||
let listener = tokio::net::TcpListener::bind("127.0.0.1:8080").await?;
|
||||
axum::serve(listener, app).await
|
||||
}
|
||||
+2
-4
@@ -1,5 +1,5 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use session_rs::{Method, session::Session};
|
||||
use session_rs::{Method, Session};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
struct Data;
|
||||
@@ -13,7 +13,7 @@ impl Method for Data {
|
||||
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
async fn main() -> session_rs::Result<()> {
|
||||
let session = Session::connect("127.0.0.1:8080", "/").await?;
|
||||
let session = Session::connect("ws://127.0.0.1:8080").await?;
|
||||
|
||||
session.start_receiver();
|
||||
|
||||
@@ -31,8 +31,6 @@ async fn main() -> session_rs::Result<()> {
|
||||
.await?
|
||||
);
|
||||
|
||||
tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await;
|
||||
|
||||
session.close().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user