Skip to content

Commit fa89f57

Browse files
authored
feat: cors example (#29)
* feat: cors example * fix: clippy and feature checks * fix: better corsing * chore: make arg optional * refactor: improve the example and comment
1 parent fdcc44c commit fa89f57

File tree

3 files changed

+87
-1
lines changed

3 files changed

+87
-1
lines changed

Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,15 @@ tokio-tungstenite = { version = "0.26.1", features = ["rustls-tls-webpki-roots"]
3939
futures-util = { version = "0.3.31", optional = true }
4040

4141
[dev-dependencies]
42+
ajj = { path = "./", features = ["axum", "ws", "ipc"] }
43+
4244
tempfile = "3.15.0"
4345
tracing-subscriber = "0.3.19"
4446
axum = { version = "0.8.1", features = ["macros"] }
47+
tower-http = { version = "0.6.2", features = ["cors"] }
48+
tokio = { version = "1.43.0", features = ["full"] }
49+
eyre = "0.6.12"
50+
4551

4652
[features]
4753
default = ["axum", "ws", "ipc"]

examples/cors.rs

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
//! Basic example of using ajj with CORS.
2+
//!
3+
//! This example demonstrates how to set up a simple HTTP server using `axum`
4+
//! and `tower_http` for CORS support.
5+
//!
6+
//! ## Inovking this example
7+
//!
8+
//! ```ignore
9+
//! // Invoking with cors domains
10+
//! cargo run --example cors -- "https://example.com,https://example.org"
11+
//! // Invoking with wildcard
12+
//! cargo run --example cors
13+
//! ```
14+
15+
use axum::http::{HeaderValue, Method};
16+
use eyre::{ensure, Context};
17+
use std::{future::IntoFuture, net::SocketAddr};
18+
use tower_http::cors::{AllowOrigin, Any, CorsLayer};
19+
20+
fn get_allowed(cors: &str) -> eyre::Result<AllowOrigin> {
21+
// Wildcard `*` means any origin is allowed.
22+
if cors == "*" {
23+
return Ok(AllowOrigin::any());
24+
}
25+
26+
// Check if the cors string contains a wildcard
27+
ensure!(
28+
!cors.split(',').any(|o| o == "*"),
29+
"Wildcard '*' is not allowed in CORS domains"
30+
);
31+
32+
// Split the cors string by commas and parse each domain into a HeaderValue
33+
// Then convert the Vec<HeaderValue> into AllowOrigin
34+
cors.split(',')
35+
.map(|domain| {
36+
// Parse each substring into a HeaderValue
37+
// We print parsing errors to stderr, because this is an example :)
38+
domain
39+
.parse::<HeaderValue>()
40+
.inspect_err(|e| eprintln!("Failed to parse domain {}: {}", domain, e))
41+
.wrap_err_with(|| format!("Invalid CORS domain: {}", domain))
42+
})
43+
.collect::<Result<Vec<_>, _>>()
44+
.map(Into::into)
45+
}
46+
47+
fn make_cors(cors: &str) -> eyre::Result<CorsLayer> {
48+
let origins = get_allowed(cors)?;
49+
50+
Ok(CorsLayer::new()
51+
.allow_methods([Method::GET, Method::POST])
52+
.allow_origin(origins)
53+
.allow_headers(Any))
54+
}
55+
56+
#[tokio::main]
57+
async fn main() -> eyre::Result<()> {
58+
let cors = std::env::args().nth(1).unwrap_or("*".to_string());
59+
60+
// Setting up an AJJ router is easy and fun!
61+
let router = ajj::Router::<()>::new()
62+
.route("helloWorld", || async {
63+
tracing::info!("serving hello world");
64+
Ok::<_, ()>("Hello, world!")
65+
})
66+
.route("addNumbers", |(a, b): (u32, u32)| async move {
67+
tracing::info!("serving addNumbers");
68+
Ok::<_, ()>(a + b)
69+
})
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(())
81+
}

src/lib.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,6 @@
136136
//!
137137
//! [`axum`]: https://docs.rs/axum/latest/axum/index.html
138138
//! [`axum::Router`]: https://docs.rs/axum/latest/axum/routing/struct.Router.html
139-
//! [`ResponsePayload`]: alloy::rpc::json_rpc::ResponsePayload
140139
//! [`interprocess::local_socket::ListenerOptions`]: https://docs.rs/interprocess/latest/interprocess/local_socket/struct.ListenerOptions.html
141140
142141
#![warn(

0 commit comments

Comments
 (0)