Skip to content

Commit

Permalink
full revamp of CORS
Browse files Browse the repository at this point in the history
  • Loading branch information
itegulov committed Dec 18, 2024
1 parent 2fe56af commit d7f6147
Show file tree
Hide file tree
Showing 10 changed files with 267 additions and 145 deletions.
4 changes: 2 additions & 2 deletions crates/cli/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -249,8 +249,8 @@ pub struct Cli {
pub allow_origin: String,

/// Disable CORS.
#[arg(long, default_missing_value = "true", num_args(0..=1), help_heading = "Server options")]
pub no_cors: Option<bool>,
#[arg(long, conflicts_with = "allow_origin", help_heading = "Server options")]
pub no_cors: bool,
}

#[derive(Debug, Subcommand, Clone)]
Expand Down
32 changes: 13 additions & 19 deletions crates/cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@ use futures::{
FutureExt,
};
use http::Method;
use jsonrpsee::server::middleware::http::{HostFilterLayer, ProxyGetRequestLayer};
use jsonrpsee::server::middleware::http::ProxyGetRequestLayer;
use jsonrpsee::server::{RpcServiceBuilder, ServerBuilder};
use std::fs::File;
use std::{env, net::SocketAddr, str::FromStr};
use tower_http::cors::{AllowOrigin, Any, CorsLayer};
use tower_http::cors::{AllowOrigin, CorsLayer};
use tracing_subscriber::filter::LevelFilter;
use zksync_types::fee_model::{FeeModelConfigV2, FeeParams};
use zksync_types::H160;
Expand All @@ -53,7 +53,7 @@ async fn build_json_http(
node: InMemoryNode,
enable_health_api: bool,
cors_allow_origin: String,
disable_cors: bool,
enable_cors: bool,
) -> tokio::task::JoinHandle<()> {
let (sender, recv) = oneshot::channel::<()>();

Expand All @@ -76,25 +76,19 @@ async fn build_json_http(
.unwrap();
rpc.merge(Web3Namespace.into_rpc()).unwrap();

let cors_layers = tower::util::option_layer(if !disable_cors {
// `CorsLayer` adds CORS-specific headers to responses but does not do filtering by itself
let cors_layers = tower::util::option_layer(enable_cors.then(|| {
// `CorsLayer` adds CORS-specific headers to responses but does not do filtering by itself.
// CORS relies on browsers respecting server's access list response headers.
// See [`tower_http::cors`](https://docs.rs/tower-http/latest/tower_http/cors/index.html)
// for more details.
let cors_layer = CorsLayer::new()
.allow_origin(AllowOrigin::exact(
cors_allow_origin.parse().expect("malformed allow origin"),
))
.allow_headers([http::header::CONTENT_TYPE])
.allow_methods([Method::GET, Method::POST])
.expose_headers(Any);
// `HostFilterLayer` filters requests based on "Host" header
let host_filter_layer = if cors_allow_origin == "*" {
HostFilterLayer::disable()
} else {
HostFilterLayer::new([cors_allow_origin]).expect("malformed allow origin")
};
Some((cors_layer, host_filter_layer))
} else {
None
});
.allow_methods([Method::GET, Method::POST]);
cors_layer
}));
let health_api_layer = tower::util::option_layer(if enable_health_api {
Some(ProxyGetRequestLayer::new("/health", "web3_clientVersion").unwrap())
} else {
Expand All @@ -107,7 +101,7 @@ async fn build_json_http(
.layer(cors_layers)
.layer(health_api_layer),
)
.set_rpc_middleware(RpcServiceBuilder::new().rpc_logger(100));
.set_rpc_middleware(RpcServiceBuilder::new().rpc_logger(1024));

let server = server_builder.build(addr).await.unwrap();

Expand Down Expand Up @@ -352,7 +346,7 @@ async fn main() -> anyhow::Result<()> {
node.clone(),
config.health_check_endpoint,
config.allow_origin.clone(),
config.no_cors,
!config.no_cors,
)
}))
.await;
Expand Down
6 changes: 2 additions & 4 deletions crates/config/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -888,10 +888,8 @@ impl TestNodeConfig {

// Enable or disable CORS
#[must_use]
pub fn with_no_cors(mut self, no_cors: Option<bool>) -> Self {
if let Some(no_cors) = no_cors {
self.no_cors = no_cors;
}
pub fn with_no_cors(mut self, no_cors: bool) -> Self {
self.no_cors = no_cors;
self
}
}
21 changes: 21 additions & 0 deletions e2e-tests-rust/Cargo.lock

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

7 changes: 6 additions & 1 deletion e2e-tests-rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,18 @@ publish = false

[dependencies]
alloy-zksync = { git = "https://github.com/itegulov/alloy-zksync.git", rev = "692c5c2ca5defc88ac542f420d97c6756dadf9df" }
alloy = { version = "0.6", features = ["full", "rlp", "serde", "sol-types", "getrandom", "provider-anvil-api"] }
alloy = { version = "0.6", features = ["full", "rlp", "serde", "sol-types", "getrandom", "provider-anvil-api", "json-rpc"] }
anyhow = "1.0"
fs2 = "0.4.3"
tokio = { version = "1", features = ["time", "rt", "process"] }
futures = "0.3.31"
itertools = "0.13.0"
async-trait = "0.1.83"
reqwest = "0.12.9"
reqwest-middleware = { version = "0.4", features = ["json"] }
serde_json = "1"
tower = "0.5"
http = "1.1.0"

[dev-dependencies]

Expand Down
71 changes: 71 additions & 0 deletions e2e-tests-rust/src/http_middleware.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/// This file is a an adapted copy of [`alloy::transports::http::Http`] that can work with
/// [`reqwest_middleware`].
// TODO: Consider upstreaming support to alloy
use alloy::rpc::json_rpc::{RequestPacket, ResponsePacket};
use alloy::transports::{TransportError, TransportErrorKind, TransportFut};
use reqwest_middleware::ClientWithMiddleware;
use std::task::{Context, Poll};
use tower::Service;

#[derive(Clone)]
pub struct HttpWithMiddleware {
client: ClientWithMiddleware,
url: reqwest::Url,
}

impl HttpWithMiddleware {
/// Create a new [`HttpWithMiddleware`] transport with a custom client.
pub const fn with_client(client: ClientWithMiddleware, url: reqwest::Url) -> Self {
Self { client, url }
}

/// Make a request.
fn request_reqwest(&self, req: RequestPacket) -> TransportFut<'static> {
let this = self.clone();
Box::pin(async move {
let resp = this
.client
.post(this.url)
.json(&req)
.send()
.await
.map_err(TransportErrorKind::custom)?;
let status = resp.status();

// Unpack data from the response body. We do this regardless of
// the status code, as we want to return the error in the body
// if there is one.
let body = resp.bytes().await.map_err(TransportErrorKind::custom)?;

if status != reqwest::StatusCode::OK {
return Err(TransportErrorKind::http_error(
status.as_u16(),
String::from_utf8_lossy(&body).into_owned(),
));
}

// Deserialize a Box<RawValue> from the body. If deserialization fails, return
// the body as a string in the error. The conversion to String
// is lossy and may not cover all the bytes in the body.
serde_json::from_slice(&body)
.map_err(|err| TransportError::deser_err(err, String::from_utf8_lossy(&body)))
})
}
}

impl Service<RequestPacket> for HttpWithMiddleware {
type Response = ResponsePacket;
type Error = TransportError;
type Future = TransportFut<'static>;

#[inline]
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
// reqwest always returns ok
Poll::Ready(Ok(()))
}

#[inline]
fn call(&mut self, req: RequestPacket) -> Self::Future {
self.request_reqwest(req)
}
}
6 changes: 5 additions & 1 deletion e2e-tests-rust/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
#![allow(async_fn_in_trait)]

mod ext;
mod http_middleware;
mod provider;
mod utils;

pub use ext::{ReceiptExt, ZksyncWalletProviderExt};
pub use provider::{init_testing_provider, init_testing_provider_with_http_headers, AnvilZKsyncApi, TestingProvider, DEFAULT_TX_VALUE};
pub use provider::{
init_testing_provider, init_testing_provider_with_client, AnvilZKsyncApi, TestingProvider,
DEFAULT_TX_VALUE,
};
4 changes: 3 additions & 1 deletion e2e-tests-rust/src/provider/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@ mod anvil_zksync;
mod testing;

pub use anvil_zksync::AnvilZKsyncApi;
pub use testing::{init_testing_provider, init_testing_provider_with_http_headers, TestingProvider, DEFAULT_TX_VALUE};
pub use testing::{
init_testing_provider, init_testing_provider_with_client, TestingProvider, DEFAULT_TX_VALUE,
};
Loading

0 comments on commit d7f6147

Please sign in to comment.