web3-proxy/web3_proxy/src/frontend/rpc_proxy_http.rs

94 lines
3.2 KiB
Rust
Raw Normal View History

2022-10-18 00:47:58 +03:00
//! Take a user's HTTP JSON-RPC requests and either respond from local data or proxy the request to a backend rpc server.
2022-10-20 23:26:14 +03:00
use super::authorization::{ip_is_authorized, key_is_authorized};
use super::errors::FrontendResult;
2022-08-16 22:29:00 +03:00
use crate::{app::Web3ProxyApp, jsonrpc::JsonRpcRequestEnum};
use axum::extract::Path;
2022-10-20 23:26:14 +03:00
use axum::headers::{Origin, Referer, UserAgent};
2022-09-09 00:01:36 +03:00
use axum::TypedHeader;
2022-09-07 06:54:16 +03:00
use axum::{response::IntoResponse, Extension, Json};
2022-07-07 06:22:09 +03:00
use axum_client_ip::ClientIp;
2022-10-20 01:26:33 +03:00
use axum_macros::debug_handler;
2022-06-05 22:58:47 +03:00
use std::sync::Arc;
2022-10-29 01:52:47 +03:00
use tracing::{error_span, instrument, Instrument};
2022-06-05 22:58:47 +03:00
2022-10-18 00:47:58 +03:00
/// POST /rpc -- Public entrypoint for HTTP JSON-RPC requests. Web3 wallets use this.
/// Defaults to rate limiting by IP address, but can also read the Authorization header for a bearer token.
/// If possible, please use a WebSocket instead.
2022-10-20 01:26:33 +03:00
#[debug_handler]
2022-10-29 01:52:47 +03:00
#[instrument(level = "trace")]
2022-09-24 07:31:06 +03:00
pub async fn proxy_web3_rpc(
2022-07-07 06:22:09 +03:00
Extension(app): Extension<Arc<Web3ProxyApp>>,
ClientIp(ip): ClientIp,
2022-10-21 23:59:05 +03:00
origin: Option<TypedHeader<Origin>>,
2022-09-09 00:01:36 +03:00
Json(payload): Json<JsonRpcRequestEnum>,
2022-08-21 12:39:38 +03:00
) -> FrontendResult {
2022-10-20 23:26:14 +03:00
let request_span = error_span!("request", %ip);
2022-09-24 07:31:06 +03:00
2022-10-21 23:59:05 +03:00
let (authorized_request, _semaphore) = ip_is_authorized(&app, ip, origin)
.instrument(request_span)
.await?;
2022-09-24 08:53:45 +03:00
let request_span = error_span!("request", ?authorized_request);
2022-09-24 08:53:45 +03:00
let authorized_request = Arc::new(authorized_request);
2022-10-27 03:12:42 +03:00
// TODO: spawn earlier? i think we want ip_is_authorized in this future
let f = tokio::spawn(async move {
2022-10-10 07:15:07 +03:00
app.proxy_web3_rpc(authorized_request, payload)
.instrument(request_span)
.await
});
2022-08-16 03:33:26 +03:00
2022-09-30 07:18:18 +03:00
let response = f.await.expect("joinhandle should always work")?;
2022-08-21 12:39:38 +03:00
2022-09-07 06:54:16 +03:00
Ok(Json(&response).into_response())
2022-08-04 04:10:27 +03:00
}
2022-07-07 06:22:09 +03:00
2022-10-18 00:47:58 +03:00
/// Authenticated entrypoint for HTTP JSON-RPC requests. Web3 wallets use this.
/// Rate limit and billing based on the api key in the url.
/// Can optionally authorized based on origin, referer, or user agent.
/// If possible, please use a WebSocket instead.
2022-10-20 01:26:33 +03:00
#[debug_handler]
2022-10-29 01:52:47 +03:00
#[instrument(level = "trace")]
2022-09-24 07:31:06 +03:00
pub async fn proxy_web3_rpc_with_key(
2022-08-04 04:10:27 +03:00
Extension(app): Extension<Arc<Web3ProxyApp>>,
2022-09-22 02:50:55 +03:00
ClientIp(ip): ClientIp,
2022-09-09 00:01:36 +03:00
Json(payload): Json<JsonRpcRequestEnum>,
origin: Option<TypedHeader<Origin>>,
2022-09-22 02:50:55 +03:00
referer: Option<TypedHeader<Referer>>,
2022-09-09 00:01:36 +03:00
user_agent: Option<TypedHeader<UserAgent>>,
2022-10-27 03:12:42 +03:00
Path(rpc_key): Path<String>,
2022-08-21 12:39:38 +03:00
) -> FrontendResult {
2022-10-27 03:12:42 +03:00
let rpc_key = rpc_key.parse()?;
2022-09-24 08:53:45 +03:00
let request_span = error_span!("request", %ip, ?referer, ?user_agent);
2022-09-09 00:01:36 +03:00
2022-09-27 05:01:45 +03:00
let (authorized_request, _semaphore) = key_is_authorized(
&app,
2022-10-27 03:12:42 +03:00
rpc_key,
ip,
origin.map(|x| x.0),
referer.map(|x| x.0),
user_agent.map(|x| x.0),
)
.instrument(request_span.clone())
.await?;
2022-09-24 08:53:45 +03:00
let request_span = error_span!("request", ?authorized_request);
2022-09-24 08:53:45 +03:00
let authorized_request = Arc::new(authorized_request);
2022-10-18 00:47:58 +03:00
// the request can take a while, so we spawn so that we can start serving another request
// TODO: spawn even earlier?
let f = tokio::spawn(async move {
2022-10-10 07:15:07 +03:00
app.proxy_web3_rpc(authorized_request, payload)
.instrument(request_span)
.await
});
2022-09-30 07:18:18 +03:00
let response = f.await.expect("JoinHandle should always work")?;
2022-08-21 12:39:38 +03:00
2022-09-07 06:54:16 +03:00
Ok(Json(&response).into_response())
2022-06-05 22:58:47 +03:00
}