Skip to content

Commit 0c20d51

Browse files
committed
feat: axum w/ websockets
1 parent 4b961ea commit 0c20d51

File tree

5 files changed

+92
-24
lines changed

5 files changed

+92
-24
lines changed

Cargo.toml

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,16 +73,20 @@ inherits = "release"
7373
debug = 2
7474
strip = false
7575

76-
[profile.bench]
77-
inherits = "profiling"
78-
7976
[profile.ci-rust]
8077
inherits = "dev"
8178
strip = true
8279
debug = false
8380
incremental = false
8481

85-
8682
[[example]]
8783
name = "ipc"
88-
required-features = ["ipc"]
84+
required-features = ["ipc"]
85+
86+
[[example]]
87+
name = "axum"
88+
required-features = ["axum", "pubsub"]
89+
90+
[[example]]
91+
name = "cors"
92+
required-features = ["axum"]

examples/axum.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
use ajj::HandlerCtx;
2+
use std::net::SocketAddr;
3+
4+
#[tokio::main]
5+
async fn main() -> eyre::Result<()> {
6+
let router = make_router();
7+
8+
// Serve via `POST` on `/` and Websockets on `/ws`
9+
let axum = router.clone().into_axum_with_ws("/", "/ws");
10+
11+
// Now we can serve the router on a TCP listener
12+
let addr = SocketAddr::from(([127, 0, 0, 1], 0));
13+
let listener = tokio::net::TcpListener::bind(addr).await?;
14+
15+
println!("Listening for POST on {}/", listener.local_addr()?);
16+
println!("Listening for WS on {}/ws", listener.local_addr()?);
17+
18+
println!("use Ctrl-C to stop");
19+
axum::serve(listener, axum).await.map_err(Into::into)
20+
}
21+
22+
fn make_router() -> ajj::Router<()> {
23+
ajj::Router::<()>::new()
24+
.route("helloWorld", || async {
25+
tracing::info!("serving hello world");
26+
Ok::<_, ()>("Hello, world!")
27+
})
28+
.route("addNumbers", |(a, b): (u32, u32)| async move {
29+
tracing::info!("serving addNumbers");
30+
Ok::<_, ()>(a + b)
31+
})
32+
.route("notify", |ctx: HandlerCtx| async move {
33+
// Check if notifications are enabled for the connection.
34+
if !ctx.notifications_enabled() {
35+
// This error will appear in the ResponsePayload's `data` field.
36+
return Err("notifications are disabled");
37+
}
38+
39+
let req_id = 15u8;
40+
41+
// Spawn a task to send the notification after a short delay.
42+
ctx.spawn_with_ctx(|ctx| async move {
43+
// something expensive goes here
44+
let result = 100_000_000;
45+
let _ = ctx
46+
.notify(&serde_json::json!({
47+
"req_id": req_id,
48+
"result": result,
49+
}))
50+
.await;
51+
});
52+
53+
// Return the request ID immediately.
54+
Ok(req_id)
55+
})
56+
}

examples/cors.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
1515
use axum::http::{HeaderValue, Method};
1616
use eyre::{ensure, Context};
17-
use std::{future::IntoFuture, net::SocketAddr};
17+
use std::net::SocketAddr;
1818
use tower_http::cors::{AllowOrigin, Any, CorsLayer};
1919

2020
#[tokio::main]
@@ -37,10 +37,7 @@ async fn main() -> eyre::Result<()> {
3737
println!("(specify cors domains as a comma-separated list to restrict origins)");
3838
}
3939
println!("use Ctrl-C to stop");
40-
axum::serve(listener, router)
41-
.into_future()
42-
.await
43-
.map_err(Into::into)
40+
axum::serve(listener, router).await.map_err(Into::into)
4441
}
4542

4643
fn get_allowed(cors: &str) -> eyre::Result<AllowOrigin> {

examples/ipc.rs

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,19 @@
11
//! Basic example of running a JSON-RPC server over IPC using ajj
22
//!
3-
//! This example demonstrates how to set up a simple IPC server using `ajj`.
3+
//! This example demonstrates how to set up a simple IPC server using `ajj`, and
4+
//! defines a few basic RPC methods.
5+
//!
6+
//! The `make_router` function sets up a router with three routes:
7+
//! - `helloWorld`: Returns a greeting string.
8+
//! - `addNumbers`: Takes two numbers as parameters and returns their sum.
9+
//! - `notify`: Sends a notification after a short delay, demonstrating the use
10+
//! of notifications in the RPC context.
411
512
use ajj::{
6-
pubsub::{ipc::local_socket, Connect},
13+
pubsub::{
14+
ipc::{to_name, ListenerOptions},
15+
Connect,
16+
},
717
HandlerCtx, Router,
818
};
919
use tempfile::NamedTempFile;
@@ -20,10 +30,7 @@ async fn main() -> eyre::Result<()> {
2030
println!("use Ctrl-C to stop");
2131

2232
// The guard keeps the server running until dropped.
23-
let guard = ajj::pubsub::ipc::ListenerOptions::new()
24-
.name(name)
25-
.serve(router)
26-
.await?;
33+
let guard = ListenerOptions::new().name(name).serve(router).await?;
2734

2835
// Shut down on Ctrl-C
2936
tokio::signal::ctrl_c().await?;
@@ -32,14 +39,6 @@ async fn main() -> eyre::Result<()> {
3239
Ok(())
3340
}
3441

35-
fn to_name(path: &std::ffi::OsStr) -> std::io::Result<local_socket::Name<'_>> {
36-
if cfg!(windows) && !path.as_encoded_bytes().starts_with(br"\\.\pipe\") {
37-
local_socket::ToNsName::to_ns_name::<local_socket::GenericNamespaced>(path)
38-
} else {
39-
local_socket::ToFsName::to_fs_name::<local_socket::GenericFilePath>(path)
40-
}
41-
}
42-
4342
// Setting up an AJJ router is easy and fun!
4443
fn make_router() -> Router<()> {
4544
Router::<()>::new()

src/pubsub/mod.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,19 @@ pub use ipc_inner::ReadJsonStream;
9898
/// IPC support via interprocess local sockets.
9999
#[cfg(feature = "ipc")]
100100
pub mod ipc {
101+
use std::ffi::OsStr;
102+
101103
pub use interprocess::local_socket::{self as local_socket, Listener, ListenerOptions, Name};
104+
105+
/// Convenience function to convert an [`OsStr`] to a local socket [`Name`]
106+
/// in a platform-safe way.
107+
pub fn to_name(path: &OsStr) -> std::io::Result<local_socket::Name<'_>> {
108+
if cfg!(windows) && !path.as_encoded_bytes().starts_with(br"\\.\pipe\") {
109+
local_socket::ToNsName::to_ns_name::<local_socket::GenericNamespaced>(path)
110+
} else {
111+
local_socket::ToFsName::to_fs_name::<local_socket::GenericFilePath>(path)
112+
}
113+
}
102114
}
103115

104116
mod shared;

0 commit comments

Comments
 (0)