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-09-24 07:31:06 +03:00
|
|
|
use super::authorization::{bearer_is_authorized, ip_is_authorized, key_is_authorized};
|
2022-08-21 12:44:53 +03:00
|
|
|
use super::errors::FrontendResult;
|
2022-08-16 22:29:00 +03:00
|
|
|
use crate::{app::Web3ProxyApp, jsonrpc::JsonRpcRequestEnum};
|
2022-09-22 22:57:21 +03:00
|
|
|
use axum::extract::Path;
|
2022-09-24 07:31:06 +03:00
|
|
|
use axum::headers::authorization::Bearer;
|
|
|
|
use axum::headers::{Authorization, 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-06-05 22:58:47 +03:00
|
|
|
use std::sync::Arc;
|
2022-09-22 22:57:21 +03:00
|
|
|
use tracing::{error_span, 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-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>>,
|
2022-09-24 07:31:06 +03:00
|
|
|
bearer: Option<TypedHeader<Authorization<Bearer>>>,
|
2022-07-07 06:22:09 +03:00
|
|
|
ClientIp(ip): ClientIp,
|
2022-09-09 00:01:36 +03:00
|
|
|
Json(payload): Json<JsonRpcRequestEnum>,
|
2022-09-24 07:31:06 +03:00
|
|
|
origin: Option<TypedHeader<Origin>>,
|
2022-09-09 00:01:36 +03:00
|
|
|
referer: Option<TypedHeader<Referer>>,
|
|
|
|
user_agent: Option<TypedHeader<UserAgent>>,
|
2022-08-21 12:39:38 +03:00
|
|
|
) -> FrontendResult {
|
2022-09-22 02:50:55 +03:00
|
|
|
let request_span = error_span!("request", %ip, ?referer, ?user_agent);
|
2022-08-16 07:56:01 +03:00
|
|
|
|
2022-09-27 05:01:45 +03:00
|
|
|
let (authorized_request, _semaphore) = if let Some(TypedHeader(Authorization(bearer))) = bearer
|
|
|
|
{
|
2022-09-24 07:31:06 +03:00
|
|
|
let origin = origin.map(|x| x.0);
|
|
|
|
let referer = referer.map(|x| x.0);
|
|
|
|
let user_agent = user_agent.map(|x| x.0);
|
|
|
|
|
|
|
|
bearer_is_authorized(&app, bearer, ip, origin, referer, user_agent)
|
|
|
|
.instrument(request_span.clone())
|
|
|
|
.await?
|
|
|
|
} else {
|
|
|
|
ip_is_authorized(&app, ip)
|
|
|
|
.instrument(request_span.clone())
|
|
|
|
.await?
|
|
|
|
};
|
2022-08-13 01:12:46 +03:00
|
|
|
|
2022-09-24 08:53:45 +03:00
|
|
|
let request_span = error_span!("request", ?authorized_request);
|
2022-09-22 23:27:14 +03:00
|
|
|
|
2022-09-24 08:53:45 +03:00
|
|
|
let authorized_request = Arc::new(authorized_request);
|
2022-09-22 23:27:14 +03:00
|
|
|
|
2022-10-18 00:47:58 +03:00
|
|
|
// TODO: spawn earlier?
|
2022-09-22 23:27:14 +03:00
|
|
|
let f = tokio::spawn(async move {
|
2022-10-10 07:15:07 +03:00
|
|
|
app.proxy_web3_rpc(authorized_request, payload)
|
2022-09-22 23:27:14 +03:00
|
|
|
.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-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>,
|
2022-09-23 08:22:33 +03:00
|
|
|
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-09-24 08:53:45 +03:00
|
|
|
Path(user_key): Path<String>,
|
2022-08-21 12:39:38 +03:00
|
|
|
) -> FrontendResult {
|
2022-09-24 08:53:45 +03:00
|
|
|
let user_key = user_key.parse()?;
|
|
|
|
|
2022-09-22 22:57:21 +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(
|
2022-09-22 22:57:21 +03:00
|
|
|
&app,
|
|
|
|
user_key,
|
|
|
|
ip,
|
2022-09-23 08:22:33 +03:00
|
|
|
origin.map(|x| x.0),
|
2022-09-22 22:57:21 +03:00
|
|
|
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-08-16 07:56:01 +03:00
|
|
|
|
2022-09-24 08:53:45 +03:00
|
|
|
let authorized_request = Arc::new(authorized_request);
|
2022-09-22 23:27:14 +03:00
|
|
|
|
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?
|
2022-09-22 23:27:14 +03:00
|
|
|
let f = tokio::spawn(async move {
|
2022-10-10 07:15:07 +03:00
|
|
|
app.proxy_web3_rpc(authorized_request, payload)
|
2022-09-22 23:27:14 +03:00
|
|
|
.instrument(request_span)
|
|
|
|
.await
|
|
|
|
});
|
2022-08-16 07:56:01 +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-06-05 22:58:47 +03:00
|
|
|
}
|