Skip to content
Merged
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
35 changes: 35 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ repository = "https://github.com/zheylmun/socketeer"

[features]
mocking = []
msgpack = ["dep:rmp-serde"]
tracing = ["dep:tracing"]

[dependencies]
bytes = "1"
futures = "0.3"
futures-util = "0.3"
rmp-serde = { version = "1", optional = true }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
thiserror = "2"
Expand Down
42 changes: 31 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@
`socketeer` is a simplified async WebSocket client built on tokio-tungstenite. It manages the underlying connection and exposes a clean API for sending and receiving messages, with support for:

- Automatic connection management with configurable keepalive
- Type-safe JSON message serialization/deserialization via serde
- Raw message support for non-JSON protocols
- Pluggable codec for typed messages (`JsonCodec`, `MsgPackCodec`, `RawCodec`, or your own)
- Custom HTTP headers on the WebSocket upgrade request
- Connection lifecycle hooks for auth handshakes and subscriptions
- Transparent handling of WebSocket protocol messages (ping/pong/close)
Expand All @@ -20,7 +19,7 @@
### Simple JSON messages

```rust no_run
use socketeer::Socketeer;
use socketeer::{JsonCodec, Socketeer};

#[derive(Debug, serde::Serialize, serde::Deserialize)]
struct SocketMessage {
Expand All @@ -29,7 +28,7 @@ struct SocketMessage {

#[tokio::main]
async fn main() {
let mut socketeer: Socketeer<SocketMessage, SocketMessage> =
let mut socketeer: Socketeer<JsonCodec<SocketMessage, SocketMessage>> =
Socketeer::connect("ws://127.0.0.1:80")
.await
.unwrap();
Expand All @@ -45,10 +44,30 @@ async fn main() {
}
```

### `MessagePack`

Enable the `msgpack` feature and use [`MsgPackCodec`] in place of [`JsonCodec`]:

```rust no_run
# #[cfg(feature = "msgpack")]
# {
use socketeer::{MsgPackCodec, Socketeer};

#[derive(Debug, serde::Serialize, serde::Deserialize)]
struct SocketMessage { message: String }

# #[tokio::main]
# async fn main() {
let mut socketeer: Socketeer<MsgPackCodec<SocketMessage, SocketMessage>> =
Socketeer::connect("ws://127.0.0.1:80").await.unwrap();
# }
# }
```

### Custom headers and connection options

```rust no_run
use socketeer::{Socketeer, ConnectOptions};
use socketeer::{ConnectOptions, JsonCodec, Socketeer};
use std::time::Duration;

# #[derive(Debug, serde::Serialize, serde::Deserialize)]
Expand All @@ -59,7 +78,7 @@ let mut options = ConnectOptions::default();
options.extra_headers.insert("Authorization", "Bearer my-token".parse().unwrap());
options.keepalive_interval = Some(Duration::from_secs(10));

let socketeer: Socketeer<Msg, Msg> =
let socketeer: Socketeer<JsonCodec<Msg, Msg>> =
Socketeer::connect_with("wss://api.example.com/ws", options)
.await
.unwrap();
Expand All @@ -69,12 +88,12 @@ let socketeer: Socketeer<Msg, Msg> =
### Connection lifecycle hooks

```rust no_run
use socketeer::{Socketeer, ConnectOptions, ConnectionHandler, HandshakeContext, Error};
use socketeer::{Codec, ConnectOptions, ConnectionHandler, Error, HandshakeContext, JsonCodec, Socketeer};

struct MyAuthHandler { api_key: String }

impl ConnectionHandler for MyAuthHandler {
async fn on_connected(&mut self, ctx: &mut HandshakeContext<'_>) -> Result<(), Error> {
impl<C: Codec> ConnectionHandler<C> for MyAuthHandler {
async fn on_connected(&mut self, ctx: &mut HandshakeContext<'_, C>) -> Result<(), Error> {
ctx.send_text(&format!(r#"{{"action":"auth","key":"{}"}}"#, self.api_key)).await?;
let _response = ctx.recv_text().await?;
Ok(())
Expand All @@ -86,10 +105,11 @@ impl ConnectionHandler for MyAuthHandler {
# #[tokio::main]
# async fn main() {
let handler = MyAuthHandler { api_key: "secret".into() };
let socketeer: Socketeer<Msg, Msg, MyAuthHandler> =
Socketeer::connect_with_handler(
let socketeer: Socketeer<JsonCodec<Msg, Msg>, MyAuthHandler> =
Socketeer::connect_with_codec(
"wss://stream.example.com",
ConnectOptions::default(),
JsonCodec::new(),
handler,
)
.await
Expand Down
5 changes: 3 additions & 2 deletions examples/echo_chat.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use socketeer::{EchoControlMessage, Socketeer, echo_server, get_mock_address};
use socketeer::{EchoControlMessage, JsonCodec, Socketeer, echo_server, get_mock_address};
use tracing_subscriber::fmt::Subscriber;

#[tokio::main]
Expand All @@ -14,7 +14,8 @@ async fn main() {
let server_address = get_mock_address(echo_server).await;

// Next, we create a Socketeer instance that connects to the mock server.
let mut socketeer: Socketeer<EchoControlMessage, EchoControlMessage> =
// The codec parameter declares both the wire format and the message types.
let mut socketeer: Socketeer<JsonCodec<EchoControlMessage, EchoControlMessage>> =
Socketeer::connect(&format!("ws://{server_address}",))
.await
.unwrap();
Expand Down
Loading
Loading