2022-06-05 22:58:47 +03:00
|
|
|
mod errors;
|
|
|
|
mod http;
|
|
|
|
mod http_proxy;
|
2022-08-07 23:44:56 +03:00
|
|
|
mod rate_limit;
|
2022-07-14 00:49:57 +03:00
|
|
|
mod users;
|
2022-06-05 22:58:47 +03:00
|
|
|
mod ws_proxy;
|
2022-07-07 06:22:09 +03:00
|
|
|
|
2022-08-16 22:29:00 +03:00
|
|
|
use crate::app::Web3ProxyApp;
|
2022-08-21 12:44:53 +03:00
|
|
|
use ::http::Request;
|
2022-06-05 22:58:47 +03:00
|
|
|
use axum::{
|
2022-08-16 03:33:26 +03:00
|
|
|
body::Body,
|
2022-06-05 22:58:47 +03:00
|
|
|
handler::Handler,
|
|
|
|
routing::{get, post},
|
|
|
|
Extension, Router,
|
|
|
|
};
|
2022-08-07 23:44:56 +03:00
|
|
|
use std::net::SocketAddr;
|
2022-06-05 22:58:47 +03:00
|
|
|
use std::sync::Arc;
|
2022-08-16 03:33:26 +03:00
|
|
|
use tower_http::trace::TraceLayer;
|
|
|
|
use tower_request_id::{RequestId, RequestIdLayer};
|
|
|
|
use tracing::{error_span, info};
|
2022-06-15 04:02:26 +03:00
|
|
|
|
2022-08-16 03:33:26 +03:00
|
|
|
/// http and websocket frontend for customers
|
2022-08-10 09:22:49 +03:00
|
|
|
pub async fn serve(port: u16, proxy_app: Arc<Web3ProxyApp>) -> anyhow::Result<()> {
|
2022-08-19 23:18:12 +03:00
|
|
|
// create a tracing span for each request with a random request id and the method
|
|
|
|
// GET: websocket or static pages
|
|
|
|
// POST: http rpc or login
|
2022-08-16 03:33:26 +03:00
|
|
|
let request_tracing_layer =
|
|
|
|
TraceLayer::new_for_http().make_span_with(|request: &Request<Body>| {
|
|
|
|
// We get the request id from the extensions
|
|
|
|
let request_id = request
|
|
|
|
.extensions()
|
|
|
|
.get::<RequestId>()
|
|
|
|
.map(ToString::to_string)
|
|
|
|
.unwrap_or_else(|| "unknown".into());
|
|
|
|
// And then we put it along with other information into the `request` span
|
|
|
|
error_span!(
|
|
|
|
"http_request",
|
|
|
|
id = %request_id,
|
|
|
|
// TODO: do we want these?
|
|
|
|
method = %request.method(),
|
|
|
|
// uri = %request.uri(),
|
|
|
|
)
|
|
|
|
});
|
|
|
|
|
2022-08-21 11:18:57 +03:00
|
|
|
// build our axum Router
|
2022-06-05 22:58:47 +03:00
|
|
|
let app = Router::new()
|
2022-08-21 11:18:57 +03:00
|
|
|
// routes should be order most to least common
|
2022-08-05 22:22:23 +03:00
|
|
|
.route("/", post(http_proxy::public_proxy_web3_rpc))
|
|
|
|
.route("/", get(ws_proxy::public_websocket_handler))
|
2022-08-06 04:17:25 +03:00
|
|
|
.route("/u/:user_key", post(http_proxy::user_proxy_web3_rpc))
|
|
|
|
.route("/u/:user_key", get(ws_proxy::user_websocket_handler))
|
2022-06-29 21:22:53 +03:00
|
|
|
.route("/health", get(http::health))
|
2022-08-21 11:18:57 +03:00
|
|
|
// TODO: we probably want to remove /status in favor of the separate prometheus thread
|
2022-06-05 22:58:47 +03:00
|
|
|
.route("/status", get(http::status))
|
2022-08-17 01:52:12 +03:00
|
|
|
.route("/login/:user_address", get(users::get_login))
|
2022-08-19 23:18:12 +03:00
|
|
|
.route("/login/:user_address/:message_eip", get(users::get_login))
|
2022-08-21 12:44:53 +03:00
|
|
|
.route("/login", post(users::post_login))
|
2022-08-21 11:18:57 +03:00
|
|
|
.route("/users", post(users::post_user))
|
|
|
|
// layers are ordered bottom up
|
|
|
|
// the last layer is first for requests and last for responses
|
2022-08-07 22:35:24 +03:00
|
|
|
.layer(Extension(proxy_app))
|
2022-08-21 11:18:57 +03:00
|
|
|
// add the request id to our tracing logs
|
2022-08-16 03:33:26 +03:00
|
|
|
.layer(request_tracing_layer)
|
2022-08-21 11:18:57 +03:00
|
|
|
// create a unique id for each request
|
2022-08-16 03:33:26 +03:00
|
|
|
.layer(RequestIdLayer)
|
2022-08-07 22:35:24 +03:00
|
|
|
// 404 for any unknown routes
|
|
|
|
.fallback(errors::handler_404.into_service());
|
2022-06-05 22:58:47 +03:00
|
|
|
|
|
|
|
// run our app with hyper
|
2022-08-16 03:33:26 +03:00
|
|
|
// TODO: allow only listening on localhost? top_config.app.host.parse()?
|
2022-06-05 22:58:47 +03:00
|
|
|
let addr = SocketAddr::from(([0, 0, 0, 0], port));
|
2022-08-06 04:17:25 +03:00
|
|
|
info!("listening on port {}", port);
|
2022-07-07 06:22:09 +03:00
|
|
|
// TODO: into_make_service is enough if we always run behind a proxy. make into_make_service_with_connect_info optional?
|
2022-08-11 05:57:01 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
It sequentially looks for an IP in:
|
|
|
|
- x-forwarded-for header (de-facto standard)
|
|
|
|
- x-real-ip header
|
|
|
|
- forwarded header (new standard)
|
|
|
|
- axum::extract::ConnectInfo (if not behind proxy)
|
|
|
|
|
|
|
|
So we probably won't need into_make_service_with_connect_info, but it shouldn't hurt
|
|
|
|
*/
|
|
|
|
let service = app.into_make_service_with_connect_info::<SocketAddr>();
|
|
|
|
// let service = app.into_make_service();
|
|
|
|
|
2022-08-13 00:00:26 +03:00
|
|
|
// `axum::Server` is a re-export of `hyper::Server`
|
2022-06-05 22:58:47 +03:00
|
|
|
axum::Server::bind(&addr)
|
2022-08-07 09:48:57 +03:00
|
|
|
// TODO: option to use with_connect_info. we want it in dev, but not when running behind a proxy, but not
|
2022-08-11 05:57:01 +03:00
|
|
|
.serve(service)
|
2022-08-13 00:00:26 +03:00
|
|
|
.with_graceful_shutdown(signal_shutdown())
|
2022-06-05 22:58:47 +03:00
|
|
|
.await
|
|
|
|
.map_err(Into::into)
|
|
|
|
}
|
2022-08-07 22:33:16 +03:00
|
|
|
|
|
|
|
/// Tokio signal handler that will wait for a user to press CTRL+C.
|
|
|
|
/// We use this in our hyper `Server` method `with_graceful_shutdown`.
|
|
|
|
async fn signal_shutdown() {
|
2022-08-10 09:22:49 +03:00
|
|
|
info!("ctrl-c to quit");
|
2022-08-07 22:33:16 +03:00
|
|
|
tokio::signal::ctrl_c()
|
|
|
|
.await
|
|
|
|
.expect("expect tokio signal ctrl-c");
|
|
|
|
info!("signal shutdown");
|
|
|
|
}
|