Skip to content

Commit 4b961ea

Browse files
committed
feat: ipc serving example
1 parent bda33c7 commit 4b961ea

File tree

6 files changed

+124
-21
lines changed

6 files changed

+124
-21
lines changed

Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,3 +81,8 @@ inherits = "dev"
8181
strip = true
8282
debug = false
8383
incremental = false
84+
85+
86+
[[example]]
87+
name = "ipc"
88+
required-features = ["ipc"]

examples/cors.rs

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//! Basic example of using ajj with CORS.
1+
//! Basic example of using ajj with CORS via axum.
22
//!
33
//! This example demonstrates how to set up a simple HTTP server using `axum`
44
//! and `tower_http` for CORS support.
@@ -17,6 +17,32 @@ use eyre::{ensure, Context};
1717
use std::{future::IntoFuture, net::SocketAddr};
1818
use tower_http::cors::{AllowOrigin, Any, CorsLayer};
1919

20+
#[tokio::main]
21+
async fn main() -> eyre::Result<()> {
22+
let cors = std::env::args().nth(1).unwrap_or("*".to_string());
23+
24+
let router = make_router()
25+
// Convert to an axum router
26+
.into_axum("/")
27+
// And then layer on your CORS settings
28+
.layer(make_cors(&cors)?);
29+
30+
// Now we can serve the router on a TCP listener
31+
let addr = SocketAddr::from(([127, 0, 0, 1], 0));
32+
let listener = tokio::net::TcpListener::bind(addr).await?;
33+
34+
println!("Listening on {}", listener.local_addr()?);
35+
println!("CORS allowed for: {}", cors);
36+
if cors == "*" {
37+
println!("(specify cors domains as a comma-separated list to restrict origins)");
38+
}
39+
println!("use Ctrl-C to stop");
40+
axum::serve(listener, router)
41+
.into_future()
42+
.await
43+
.map_err(Into::into)
44+
}
45+
2046
fn get_allowed(cors: &str) -> eyre::Result<AllowOrigin> {
2147
// Wildcard `*` means any origin is allowed.
2248
if cors == "*" {
@@ -53,12 +79,10 @@ fn make_cors(cors: &str) -> eyre::Result<CorsLayer> {
5379
.allow_headers(Any))
5480
}
5581

56-
#[tokio::main]
57-
async fn main() -> eyre::Result<()> {
58-
let cors = std::env::args().nth(1).unwrap_or("*".to_string());
59-
82+
// Setting up an AJJ router is easy and fun!
83+
fn make_router() -> ajj::Router<()> {
6084
// Setting up an AJJ router is easy and fun!
61-
let router = ajj::Router::<()>::new()
85+
ajj::Router::<()>::new()
6286
.route("helloWorld", || async {
6387
tracing::info!("serving hello world");
6488
Ok::<_, ()>("Hello, world!")
@@ -67,15 +91,4 @@ async fn main() -> eyre::Result<()> {
6791
tracing::info!("serving addNumbers");
6892
Ok::<_, ()>(a + b)
6993
})
70-
// Convert to an axum router
71-
.into_axum("/")
72-
// And then layer on your CORS settings
73-
.layer(make_cors(&cors)?);
74-
75-
// Now we can serve the router on a TCP listener
76-
let addr = SocketAddr::from(([127, 0, 0, 1], 0));
77-
let listener = tokio::net::TcpListener::bind(addr).await?;
78-
79-
axum::serve(listener, router).into_future().await?;
80-
Ok(())
8194
}

examples/ipc.rs

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
//! Basic example of running a JSON-RPC server over IPC using ajj
2+
//!
3+
//! This example demonstrates how to set up a simple IPC server using `ajj`.
4+
5+
use ajj::{
6+
pubsub::{ipc::local_socket, Connect},
7+
HandlerCtx, Router,
8+
};
9+
use tempfile::NamedTempFile;
10+
11+
#[tokio::main]
12+
async fn main() -> eyre::Result<()> {
13+
let router = make_router();
14+
15+
// Create a temporary file for the IPC socket.
16+
let tempfile = NamedTempFile::new()?;
17+
let name = to_name(tempfile.path().as_os_str()).expect("invalid name");
18+
19+
println!("Serving IPC on socket: {:?}", tempfile.path());
20+
println!("use Ctrl-C to stop");
21+
22+
// The guard keeps the server running until dropped.
23+
let guard = ajj::pubsub::ipc::ListenerOptions::new()
24+
.name(name)
25+
.serve(router)
26+
.await?;
27+
28+
// Shut down on Ctrl-C
29+
tokio::signal::ctrl_c().await?;
30+
drop(guard);
31+
32+
Ok(())
33+
}
34+
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+
43+
// Setting up an AJJ router is easy and fun!
44+
fn make_router() -> Router<()> {
45+
Router::<()>::new()
46+
.route("helloWorld", || async {
47+
tracing::info!("serving hello world");
48+
Ok::<_, ()>("Hello, world!")
49+
})
50+
.route("addNumbers", |(a, b): (u32, u32)| async move {
51+
tracing::info!("serving addNumbers");
52+
Ok::<_, ()>(a + b)
53+
})
54+
.route("notify", |ctx: HandlerCtx| async move {
55+
// Check if notifications are enabled for the connection.
56+
if !ctx.notifications_enabled() {
57+
// This error will appear in the ResponsePayload's `data` field.
58+
return Err("notifications are disabled");
59+
}
60+
61+
let req_id = 15u8;
62+
63+
// Spawn a task to send the notification after a short delay.
64+
ctx.spawn_with_ctx(|ctx| async move {
65+
// something expensive goes here
66+
let result = 100_000_000;
67+
let _ = ctx
68+
.notify(&serde_json::json!({
69+
"req_id": req_id,
70+
"result": result,
71+
}))
72+
.await;
73+
});
74+
75+
// Return the request ID immediately.
76+
Ok(req_id)
77+
})
78+
}

src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,13 @@
3131
//!
3232
//! let req_id = 15u8;
3333
//!
34-
//! tokio::task::spawn_blocking(move || {
34+
//! ctx.spawn_with_ctx(|ctx| async move {
3535
//! // something expensive goes here
3636
//! let result = 100_000_000;
3737
//! let _ = ctx.notify(&serde_json::json!({
3838
//! "req_id": req_id,
3939
//! "result": result,
40-
//! }));
40+
//! })).await;
4141
//! });
4242
//! Ok(req_id)
4343
//! })
File renamed without changes.

src/pubsub/mod.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,10 +89,17 @@
8989
//! [`HandlerCtx`]: crate::HandlerCtx
9090
9191
#[cfg(feature = "ipc")]
92-
mod ipc;
92+
mod ipc_inner;
9393
#[cfg(feature = "ipc")]
9494
#[doc(hidden)]
95-
pub use ipc::ReadJsonStream;
95+
// Re-exported for use in tests
96+
pub use ipc_inner::ReadJsonStream;
97+
98+
/// IPC support via interprocess local sockets.
99+
#[cfg(feature = "ipc")]
100+
pub mod ipc {
101+
pub use interprocess::local_socket::{self as local_socket, Listener, ListenerOptions, Name};
102+
}
96103

97104
mod shared;
98105
pub(crate) use shared::WriteItem;

0 commit comments

Comments
 (0)