diff --git a/Cargo.lock b/Cargo.lock index 4956aa5..48ce690 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3140,6 +3140,7 @@ dependencies = [ "futures", "futures-util", "governor", + "http", "hyper", "jsonrpc-http-server", "jsonrpc-pubsub", diff --git a/Cargo.toml b/Cargo.toml index eda845c..3cf5013 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,7 @@ chrono = "0.4.24" clap = { version = "4.1.1", features = ["derive"] } enumflags2 = "0.7.7" futures = "0.3.25" +http = "0.2.8" hyper = "0.14.23" log = "0.4.17" moka = { version = "0.11.0", features = ["future"] } diff --git a/benches/bench/main.rs b/benches/bench/main.rs index 8a97907..2be9aee 100644 --- a/benches/bench/main.rs +++ b/benches/bench/main.rs @@ -220,6 +220,7 @@ fn config() -> Config { max_connections: 1024 * 1024, request_timeout_seconds: 120, http_methods: Vec::new(), + cors: None, }), substrate_api: Some(SubstrateApiConfig { stale_timeout_seconds: 5_000, diff --git a/config.yml b/config.yml index 2cbe3cc..eb1629f 100644 --- a/config.yml +++ b/config.yml @@ -22,6 +22,7 @@ extensions: method: system_health - path: /liveness method: chain_getBlockHash + cors: all middlewares: methods: diff --git a/eth_config.yml b/eth_config.yml index 8a23a50..3363d77 100644 --- a/eth_config.yml +++ b/eth_config.yml @@ -14,6 +14,7 @@ extensions: port: 8545 listen_address: '0.0.0.0' max_connections: 2000 + cors: all middlewares: methods: diff --git a/src/extensions/server/mod.rs b/src/extensions/server/mod.rs index 65fd3dd..59ac30f 100644 --- a/src/extensions/server/mod.rs +++ b/src/extensions/server/mod.rs @@ -1,8 +1,10 @@ use std::{future::Future, net::SocketAddr}; use async_trait::async_trait; +use http::header::HeaderValue; use jsonrpsee::server::{RandomStringIdProvider, RpcModule, ServerBuilder, ServerHandle}; use serde::Deserialize; +use tower_http::cors::{AllowOrigin, CorsLayer}; use super::{Extension, ExtensionRegistry}; use proxy_get_request::ProxyGetRequestLayer; @@ -21,6 +23,22 @@ pub struct HttpMethodsConfig { pub method: String, } +#[derive(Deserialize, Debug, Clone)] +#[serde(untagged)] +pub enum ItemOrList { + Item(T), + List(Vec), +} + +impl ItemOrList { + fn into_list(self) -> Vec { + match self { + ItemOrList::Item(item) => vec![item], + ItemOrList::List(list) => list, + } + } +} + #[derive(Deserialize, Debug, Clone)] pub struct ServerConfig { pub port: u16, @@ -30,6 +48,8 @@ pub struct ServerConfig { pub http_methods: Vec, #[serde(default = "default_request_timeout_seconds")] pub request_timeout_seconds: u64, + #[serde(default)] + pub cors: Option>, } fn default_request_timeout_seconds() -> u64 { @@ -45,6 +65,22 @@ impl Extension for SubwayServerBuilder { } } +fn cors_layer(cors: Option>) -> anyhow::Result { + let origins = cors.map(|c| c.into_list()).unwrap_or_default(); + + match origins.as_slice() { + [] => Ok(CorsLayer::new()), + [origin] if origin == "*" || origin == "all" => Ok(CorsLayer::permissive()), + origins => { + let list = origins + .iter() + .map(|o| HeaderValue::from_str(o)) + .collect::, _>>()?; + Ok(CorsLayer::new().allow_origin(AllowOrigin::list(list))) + } + } +} + impl SubwayServerBuilder { pub fn new(config: ServerConfig) -> Self { Self { config } @@ -54,19 +90,21 @@ impl SubwayServerBuilder { &self, builder: impl FnOnce() -> Fut, ) -> anyhow::Result<(SocketAddr, ServerHandle)> { - let service_builder = tower::ServiceBuilder::new().layer( - ProxyGetRequestLayer::new( - self.config - .http_methods - .iter() - .map(|m| ProxyGetRequestMethod { - path: m.path.clone(), - method: m.method.clone(), - }) - .collect(), - ) - .expect("Invalid health config"), - ); + let service_builder = tower::ServiceBuilder::new() + .layer(cors_layer(self.config.cors.clone()).expect("Invalid CORS config")) + .layer( + ProxyGetRequestLayer::new( + self.config + .http_methods + .iter() + .map(|m| ProxyGetRequestMethod { + path: m.path.clone(), + method: m.method.clone(), + }) + .collect(), + ) + .expect("Invalid health config"), + ); let server = ServerBuilder::default() .set_middleware(service_builder) diff --git a/src/server.rs b/src/server.rs index eee7458..7600e94 100644 --- a/src/server.rs +++ b/src/server.rs @@ -221,6 +221,7 @@ mod tests { max_connections: 1024, request_timeout_seconds: request_timeout_seconds.unwrap_or(10), http_methods: Vec::new(), + cors: None, }), ..Default::default() }, diff --git a/src/tests/merge_subscription.rs b/src/tests/merge_subscription.rs index d64fc00..a41bb4b 100644 --- a/src/tests/merge_subscription.rs +++ b/src/tests/merge_subscription.rs @@ -56,6 +56,7 @@ async fn merge_subscription_works() { max_connections: 10, request_timeout_seconds: 120, http_methods: Vec::new(), + cors: None, }), merge_subscription: Some(MergeSubscriptionConfig { keep_alive_seconds: Some(1), diff --git a/src/tests/upstream.rs b/src/tests/upstream.rs index c61551b..84c38f5 100644 --- a/src/tests/upstream.rs +++ b/src/tests/upstream.rs @@ -38,6 +38,7 @@ async fn upstream_error_propagate() { max_connections: 10, request_timeout_seconds: 120, http_methods: Vec::new(), + cors: None, }), merge_subscription: Some(MergeSubscriptionConfig { keep_alive_seconds: Some(1),