upgrade axum to 0.8.9 (and side deps) (#6808)
* upgrade axum to 0.8.9 (and side deps) Bumps axum 0.7.5 → 0.8.9, axum-extra 0.9.4 → 0.12.6, axum-client-ip 0.6.1 → 1.3.1, axum-test 16.2.0 → 20.0.0, utoipa-swagger-ui 8.1 → 9.0.2. * warn upon using fallback ip Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * chore: replace use of deprecated try_next() * update console-subscriber to ensure single version of axum in the lock file * removed unused axum-test dev-dep --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
6b0a904d10
commit
28b22f6b22
Generated
+363
-469
File diff suppressed because it is too large
Load Diff
+6
-6
@@ -222,10 +222,10 @@ anyhow = "1.0.98"
|
|||||||
arc-swap = "1.7.1"
|
arc-swap = "1.7.1"
|
||||||
argon2 = "0.5.0"
|
argon2 = "0.5.0"
|
||||||
async-trait = "0.1.88"
|
async-trait = "0.1.88"
|
||||||
axum = "0.7.5"
|
axum = "0.8.9"
|
||||||
axum-client-ip = "0.6.1"
|
axum-client-ip = "1.3.1"
|
||||||
axum-extra = "0.9.4"
|
axum-extra = "0.12.6"
|
||||||
axum-test = "16.2.0"
|
axum-test = "20.0.0"
|
||||||
base64 = "0.22.1"
|
base64 = "0.22.1"
|
||||||
base85rs = "0.1.3"
|
base85rs = "0.1.3"
|
||||||
bincode = "1.3.3"
|
bincode = "1.3.3"
|
||||||
@@ -250,7 +250,7 @@ clap_complete_fig = "4.5"
|
|||||||
colored = "2.2"
|
colored = "2.2"
|
||||||
comfy-table = "7.1.4"
|
comfy-table = "7.1.4"
|
||||||
console = "0.16.0"
|
console = "0.16.0"
|
||||||
console-subscriber = "0.4.1"
|
console-subscriber = "0.5.0"
|
||||||
console_error_panic_hook = "0.1"
|
console_error_panic_hook = "0.1"
|
||||||
const-str = "0.5.6"
|
const-str = "0.5.6"
|
||||||
const_format = "0.2.34"
|
const_format = "0.2.34"
|
||||||
@@ -392,7 +392,7 @@ uniffi = "0.29.2"
|
|||||||
uniffi_build = "0.29.0"
|
uniffi_build = "0.29.0"
|
||||||
url = "2.5"
|
url = "2.5"
|
||||||
utoipa = "5.2"
|
utoipa = "5.2"
|
||||||
utoipa-swagger-ui = "8.1"
|
utoipa-swagger-ui = "9.0.2"
|
||||||
utoipauto = "0.2"
|
utoipauto = "0.2"
|
||||||
uuid = "1.19.0"
|
uuid = "1.19.0"
|
||||||
vergen = { version = "=8.3.1", default-features = false }
|
vergen = { version = "=8.3.1", default-features = false }
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
// Copyright 2026 - Nym Technologies SA <contact@nymtech.net>
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use axum::extract::{ConnectInfo, FromRequestParts};
|
||||||
|
use axum::http::request::Parts;
|
||||||
|
use axum_client_ip::RightmostXForwardedFor;
|
||||||
|
use std::convert::Infallible;
|
||||||
|
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
||||||
|
use tracing::warn;
|
||||||
|
|
||||||
|
/// Best-effort client IP extractor.
|
||||||
|
///
|
||||||
|
/// Prefers the rightmost entry of `X-Forwarded-For` (set by a trusted reverse
|
||||||
|
/// proxy); falls back to the TCP peer address when the header is absent, and to
|
||||||
|
/// the unspecified address when neither is available (tests).
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct ClientIpAddr(pub IpAddr);
|
||||||
|
|
||||||
|
impl<S> FromRequestParts<S> for ClientIpAddr
|
||||||
|
where
|
||||||
|
S: Send + Sync,
|
||||||
|
{
|
||||||
|
type Rejection = Infallible;
|
||||||
|
|
||||||
|
async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
|
||||||
|
if let Ok(RightmostXForwardedFor(ip)) =
|
||||||
|
RightmostXForwardedFor::from_request_parts(parts, state).await
|
||||||
|
{
|
||||||
|
return Ok(ClientIpAddr(ip));
|
||||||
|
}
|
||||||
|
if let Ok(ConnectInfo(addr)) =
|
||||||
|
ConnectInfo::<SocketAddr>::from_request_parts(parts, state).await
|
||||||
|
{
|
||||||
|
return Ok(ClientIpAddr(addr.ip()));
|
||||||
|
}
|
||||||
|
warn!("ClientIpAddr: no X-Forwarded-For or ConnectInfo found; using 0.0.0.0 fallback");
|
||||||
|
Ok(ClientIpAddr(IpAddr::V4(Ipv4Addr::UNSPECIFIED)))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2024 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
use crate::middleware::client_ip::ClientIpAddr;
|
||||||
use axum::extract::Request;
|
use axum::extract::Request;
|
||||||
use axum::http::HeaderValue;
|
use axum::http::HeaderValue;
|
||||||
use axum::http::header::{HOST, USER_AGENT};
|
use axum::http::header::{HOST, USER_AGENT};
|
||||||
use axum::middleware::Next;
|
use axum::middleware::Next;
|
||||||
use axum::response::IntoResponse;
|
use axum::response::IntoResponse;
|
||||||
use axum_client_ip::InsecureClientIp;
|
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use tracing::{debug, info};
|
use tracing::{debug, info};
|
||||||
@@ -17,24 +17,24 @@ enum LogLevel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn log_request_info(
|
pub async fn log_request_info(
|
||||||
insecure_client_ip: InsecureClientIp,
|
client_ip: ClientIpAddr,
|
||||||
request: Request,
|
request: Request,
|
||||||
next: Next,
|
next: Next,
|
||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
log_request(insecure_client_ip, request, next, LogLevel::Info).await
|
log_request(client_ip, request, next, LogLevel::Info).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn log_request_debug(
|
pub async fn log_request_debug(
|
||||||
insecure_client_ip: InsecureClientIp,
|
client_ip: ClientIpAddr,
|
||||||
request: Request,
|
request: Request,
|
||||||
next: Next,
|
next: Next,
|
||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
log_request(insecure_client_ip, request, next, LogLevel::Debug).await
|
log_request(client_ip, request, next, LogLevel::Debug).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Simple logger for requests
|
/// Simple logger for requests
|
||||||
async fn log_request(
|
async fn log_request(
|
||||||
InsecureClientIp(addr): InsecureClientIp,
|
ClientIpAddr(addr): ClientIpAddr,
|
||||||
request: Request,
|
request: Request,
|
||||||
next: Next,
|
next: Next,
|
||||||
level: LogLevel,
|
level: LogLevel,
|
||||||
|
|||||||
@@ -2,4 +2,5 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
pub mod bearer_auth;
|
pub mod bearer_auth;
|
||||||
|
pub mod client_ip;
|
||||||
pub mod logging;
|
pub mod logging;
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
// Copyright 2025 - Nym Technologies SA <contact@nymtech.net>
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
use async_trait::async_trait;
|
|
||||||
use axum::extract::FromRequestParts;
|
use axum::extract::FromRequestParts;
|
||||||
use axum::http::Request;
|
use axum::http::Request;
|
||||||
use axum::http::request::Parts;
|
use axum::http::request::Parts;
|
||||||
@@ -56,7 +55,6 @@ impl DummyConnectInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl<S> FromRequestParts<S> for DummyConnectInfo
|
impl<S> FromRequestParts<S> for DummyConnectInfo
|
||||||
where
|
where
|
||||||
S: Send + Sync,
|
S: Send + Sync,
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ pub(crate) mod test {
|
|||||||
|
|
||||||
async fn handle_check_request(&mut self, polled_request: CheckRequest) {
|
async fn handle_check_request(&mut self, polled_request: CheckRequest) {
|
||||||
let mut requests = vec![polled_request];
|
let mut requests = vec![polled_request];
|
||||||
while let Ok(Some(queued_up)) = self.check_request_receiver.try_next() {
|
while let Ok(queued_up) = self.check_request_receiver.try_recv() {
|
||||||
requests.push(queued_up);
|
requests.push(queued_up);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ impl UpgradeModeWatcher {
|
|||||||
|
|
||||||
async fn handle_check_request(&mut self, polled_request: CheckRequest) {
|
async fn handle_check_request(&mut self, polled_request: CheckRequest) {
|
||||||
let mut requests = vec![polled_request];
|
let mut requests = vec![polled_request];
|
||||||
while let Ok(Some(queued_up)) = self.check_request_receiver.try_next() {
|
while let Ok(queued_up) = self.check_request_receiver.try_recv() {
|
||||||
requests.push(queued_up);
|
requests.push(queued_up);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,15 +32,15 @@ pub(crate) fn issued_routes() -> Router<AppState> {
|
|||||||
axum::routing::get(issued_ticketbooks_count),
|
axum::routing::get(issued_ticketbooks_count),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/issued-ticketbooks-for-count/:expiration_date",
|
"/issued-ticketbooks-for-count/{expiration_date}",
|
||||||
axum::routing::get(issued_ticketbooks_for_count),
|
axum::routing::get(issued_ticketbooks_for_count),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/issued-ticketbooks-on-count/:issuance_date",
|
"/issued-ticketbooks-on-count/{issuance_date}",
|
||||||
axum::routing::get(issued_ticketbooks_on_count),
|
axum::routing::get(issued_ticketbooks_on_count),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/issued-ticketbooks-for/:expiration_date",
|
"/issued-ticketbooks-for/{expiration_date}",
|
||||||
axum::routing::get(issued_ticketbooks_for),
|
axum::routing::get(issued_ticketbooks_for),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
|
|||||||
@@ -1369,8 +1369,7 @@ impl TestFixture {
|
|||||||
Router::new()
|
Router::new()
|
||||||
.nest("/v1/ecash", ecash_routes())
|
.nest("/v1/ecash", ecash_routes())
|
||||||
.with_state(app_state),
|
.with_state(app_state),
|
||||||
)
|
),
|
||||||
.unwrap(),
|
|
||||||
storage,
|
storage,
|
||||||
chain_state: bundle.chain_state,
|
chain_state: bundle.chain_state,
|
||||||
epoch: bundle.epoch,
|
epoch: bundle.epoch,
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ const MAX_FAMILIES_PAGE_SIZE: u32 = 200;
|
|||||||
pub(crate) fn routes() -> Router<AppState> {
|
pub(crate) fn routes() -> Router<AppState> {
|
||||||
Router::new()
|
Router::new()
|
||||||
.route("/", get(get_families))
|
.route("/", get(get_families))
|
||||||
.route("/:family_id", get(get_family_by_id))
|
.route("/{family_id}", get(get_family_by_id))
|
||||||
.route("/by-node/:node_id", get(get_family_for_node))
|
.route("/by-node/{node_id}", get(get_family_for_node))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[utoipa::path(
|
#[utoipa::path(
|
||||||
|
|||||||
@@ -52,8 +52,7 @@ impl NodeFamiliesTestFixture {
|
|||||||
Router::new()
|
Router::new()
|
||||||
.nest("/v1/node-families", handlers::routes())
|
.nest("/v1/node-families", handlers::routes())
|
||||||
.with_state(app_state),
|
.with_state(app_state),
|
||||||
)
|
);
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
NodeFamiliesTestFixture {
|
NodeFamiliesTestFixture {
|
||||||
axum: server,
|
axum: server,
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ use utoipa::IntoParams;
|
|||||||
pub(super) fn network_monitor_routes() -> Router<AppState> {
|
pub(super) fn network_monitor_routes() -> Router<AppState> {
|
||||||
Router::new()
|
Router::new()
|
||||||
.nest(
|
.nest(
|
||||||
"/gateway/:identity",
|
"/gateway/{identity}",
|
||||||
Router::new()
|
Router::new()
|
||||||
.route("/history", axum::routing::get(gateway_uptime_history))
|
.route("/history", axum::routing::get(gateway_uptime_history))
|
||||||
.route(
|
.route(
|
||||||
@@ -34,7 +34,7 @@ pub(super) fn network_monitor_routes() -> Router<AppState> {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
.nest(
|
.nest(
|
||||||
"/mixnode/:mix_id",
|
"/mixnode/{mix_id}",
|
||||||
Router::new()
|
Router::new()
|
||||||
.route("/history", axum::routing::get(mixnode_uptime_history))
|
.route("/history", axum::routing::get(mixnode_uptime_history))
|
||||||
.route(
|
.route(
|
||||||
@@ -45,14 +45,14 @@ pub(super) fn network_monitor_routes() -> Router<AppState> {
|
|||||||
.nest(
|
.nest(
|
||||||
"/mixnodes",
|
"/mixnodes",
|
||||||
Router::new().route(
|
Router::new().route(
|
||||||
"/unstable/:mix_id/test-results",
|
"/unstable/{mix_id}/test-results",
|
||||||
axum::routing::get(unstable::mixnode_test_results),
|
axum::routing::get(unstable::mixnode_test_results),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.nest(
|
.nest(
|
||||||
"/gateways",
|
"/gateways",
|
||||||
Router::new().route(
|
Router::new().route(
|
||||||
"/unstable/:gateway_identity/test-results",
|
"/unstable/{gateway_identity}/test-results",
|
||||||
axum::routing::get(unstable::gateway_test_results),
|
axum::routing::get(unstable::gateway_test_results),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -60,7 +60,7 @@ pub(super) fn network_monitor_routes() -> Router<AppState> {
|
|||||||
"/network-monitor/unstable",
|
"/network-monitor/unstable",
|
||||||
Router::new()
|
Router::new()
|
||||||
.route(
|
.route(
|
||||||
"/run/:monitor_run_id/details",
|
"/run/{monitor_run_id}/details",
|
||||||
axum::routing::get(monitor_run_report),
|
axum::routing::get(monitor_run_report),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
|
|||||||
@@ -30,19 +30,22 @@ pub(crate) fn routes() -> Router<AppState> {
|
|||||||
.route("/noise", get(nodes_noise))
|
.route("/noise", get(nodes_noise))
|
||||||
.route("/bonded", get(get_bonded_nodes))
|
.route("/bonded", get(get_bonded_nodes))
|
||||||
.route("/described", get(get_described_nodes))
|
.route("/described", get(get_described_nodes))
|
||||||
.route("/annotation/:node_id", get(get_node_annotation))
|
.route("/annotation/{node_id}", get(get_node_annotation))
|
||||||
.route("/performance/:node_id", get(get_current_node_performance))
|
.route("/performance/{node_id}", get(get_current_node_performance))
|
||||||
.route("/stake-saturation/:node_id", get(get_node_stake_saturation))
|
|
||||||
.route(
|
.route(
|
||||||
"/historical-performance/:node_id",
|
"/stake-saturation/{node_id}",
|
||||||
|
get(get_node_stake_saturation),
|
||||||
|
)
|
||||||
|
.route(
|
||||||
|
"/historical-performance/{node_id}",
|
||||||
get(get_historical_performance),
|
get(get_historical_performance),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/performance-history/:node_id",
|
"/performance-history/{node_id}",
|
||||||
get(get_node_performance_history),
|
get(get_node_performance_history),
|
||||||
)
|
)
|
||||||
// to make it compatible with all the explorers that were used to using 0-100 values
|
// to make it compatible with all the explorers that were used to using 0-100 values
|
||||||
.route("/uptime-history/:node_id", get(get_node_uptime_history))
|
.route("/uptime-history/{node_id}", get(get_node_uptime_history))
|
||||||
.route("/rewarded-set", get(rewarded_set))
|
.route("/rewarded-set", get(rewarded_set))
|
||||||
.layer(CompressionLayer::new())
|
.layer(CompressionLayer::new())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ pub(crate) mod data_collector;
|
|||||||
pub(crate) mod models;
|
pub(crate) mod models;
|
||||||
|
|
||||||
pub(crate) fn routes() -> Router<AppState> {
|
pub(crate) fn routes() -> Router<AppState> {
|
||||||
Router::new().route("/:address", get(address))
|
Router::new().route("/{address}", get(address))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema, utoipa::IntoParams)]
|
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema, utoipa::IntoParams)]
|
||||||
|
|||||||
@@ -62,9 +62,9 @@ pub mod routes {
|
|||||||
pub mod shares {
|
pub mod shares {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
pub const SHARE_BY_ID: &str = "/:share_id";
|
pub const SHARE_BY_ID: &str = "/{share_id}";
|
||||||
pub const SHARE_BY_DEVICE_AND_CREDENTIAL_ID: &str =
|
pub const SHARE_BY_DEVICE_AND_CREDENTIAL_ID: &str =
|
||||||
"/device/:device_id/credential/:credential_id";
|
"/device/{device_id}/credential/{credential_id}";
|
||||||
|
|
||||||
absolute_route!(share_by_id_absolute, shares_absolute(), SHARE_BY_ID);
|
absolute_route!(share_by_id_absolute, shares_absolute(), SHARE_BY_ID);
|
||||||
absolute_route!(
|
absolute_route!(
|
||||||
|
|||||||
@@ -77,3 +77,13 @@ fn setup_cors() -> CorsLayer {
|
|||||||
.allow_headers(tower_http::cors::Any)
|
.allow_headers(tower_http::cors::Any)
|
||||||
.allow_credentials(false)
|
.allow_credentials(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn router_constructs_without_panic() {
|
||||||
|
let _ = RouterBuilder::with_default_routes().finalize_routes();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -58,6 +58,21 @@ impl AppState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn build_router(state: AppState) -> Router {
|
||||||
|
Router::new()
|
||||||
|
.route("/v1/send", post(send_handler).with_state(state))
|
||||||
|
.merge(SwaggerUi::new("/v1/ui").url("/v1/docs/openapi.json", ApiDoc::openapi()))
|
||||||
|
.route("/v1/accounting", get(accounting_handler))
|
||||||
|
.route("/v1/sent", get(sent_handler))
|
||||||
|
.route("/v1/dot/{mix_id}", get(mix_dot_handler))
|
||||||
|
.route("/v1/dot", get(graph_handler))
|
||||||
|
.route("/v1/mermaid", get(mermaid_handler))
|
||||||
|
.route("/v1/stats", get(stats_handler))
|
||||||
|
.route("/v1/node_stats/{mix_id}", get(node_stats_handler))
|
||||||
|
.route("/v1/node_stats", get(all_nodes_stats_handler))
|
||||||
|
.route("/v1/received", get(recv_handler))
|
||||||
|
}
|
||||||
|
|
||||||
impl HttpServer {
|
impl HttpServer {
|
||||||
pub fn new(listener: SocketAddr, cancel: CancellationToken) -> Self {
|
pub fn new(listener: SocketAddr, cancel: CancellationToken) -> Self {
|
||||||
HttpServer { listener, cancel }
|
HttpServer { listener, cancel }
|
||||||
@@ -66,18 +81,7 @@ impl HttpServer {
|
|||||||
pub async fn run(self, clients: ClientsWrapper) -> anyhow::Result<()> {
|
pub async fn run(self, clients: ClientsWrapper) -> anyhow::Result<()> {
|
||||||
let n_clients = clients.read().await.len();
|
let n_clients = clients.read().await.len();
|
||||||
let state = AppState { clients };
|
let state = AppState { clients };
|
||||||
let app = Router::new()
|
let app = build_router(state);
|
||||||
.route("/v1/send", post(send_handler).with_state(state))
|
|
||||||
.merge(SwaggerUi::new("/v1/ui").url("/v1/docs/openapi.json", ApiDoc::openapi()))
|
|
||||||
.route("/v1/accounting", get(accounting_handler))
|
|
||||||
.route("/v1/sent", get(sent_handler))
|
|
||||||
.route("/v1/dot/:mix_id", get(mix_dot_handler))
|
|
||||||
.route("/v1/dot", get(graph_handler))
|
|
||||||
.route("/v1/mermaid", get(mermaid_handler))
|
|
||||||
.route("/v1/stats", get(stats_handler))
|
|
||||||
.route("/v1/node_stats/:mix_id", get(node_stats_handler))
|
|
||||||
.route("/v1/node_stats", get(all_nodes_stats_handler))
|
|
||||||
.route("/v1/received", get(recv_handler));
|
|
||||||
let listener = tokio::net::TcpListener::bind(self.listener).await?;
|
let listener = tokio::net::TcpListener::bind(self.listener).await?;
|
||||||
|
|
||||||
let server_future =
|
let server_future =
|
||||||
@@ -98,3 +102,17 @@ impl HttpServer {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tokio::sync::RwLock;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn router_constructs_without_panic() {
|
||||||
|
let clients: ClientsWrapper = Arc::new(RwLock::new(VecDeque::new()));
|
||||||
|
let _ = build_router(AppState { clients });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -84,6 +84,5 @@ sqlx = { workspace = true, features = [
|
|||||||
] }
|
] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
axum-test = { workspace = true }
|
|
||||||
time = { workspace = true, features = ["macros"] }
|
time = { workspace = true, features = ["macros"] }
|
||||||
nym-gateway-probe = { workspace = true, features = ["test-utils"] }
|
nym-gateway-probe = { workspace = true, features = ["test-utils"] }
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ use crate::http::{error::HttpResult, state::AppState};
|
|||||||
pub(crate) fn routes() -> Router<AppState> {
|
pub(crate) fn routes() -> Router<AppState> {
|
||||||
Router::new()
|
Router::new()
|
||||||
.route(
|
.route(
|
||||||
"/country/:two_letter_country_code",
|
"/country/{two_letter_country_code}",
|
||||||
axum::routing::get(get_gateways_by_country),
|
axum::routing::get(get_gateways_by_country),
|
||||||
)
|
)
|
||||||
.route("/countries", axum::routing::get(get_gateway_countries))
|
.route("/countries", axum::routing::get(get_gateway_countries))
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ pub(crate) fn routes() -> Router<AppState> {
|
|||||||
axum::routing::get(get_entry_gateway_countries),
|
axum::routing::get(get_entry_gateway_countries),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/entry/country/:two_letter_country_code",
|
"/entry/country/{two_letter_country_code}",
|
||||||
axum::routing::get(get_entry_gateways_by_country),
|
axum::routing::get(get_entry_gateways_by_country),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ pub(crate) fn routes() -> Router<AppState> {
|
|||||||
axum::routing::get(get_entry_gateway_countries),
|
axum::routing::get(get_entry_gateway_countries),
|
||||||
)
|
)
|
||||||
.route(
|
.route(
|
||||||
"/exit/country/:two_letter_country_code",
|
"/exit/country/{two_letter_country_code}",
|
||||||
axum::routing::get(get_exit_gateways_by_country),
|
axum::routing::get(get_exit_gateways_by_country),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ pub(crate) fn routes() -> Router<AppState> {
|
|||||||
Router::new()
|
Router::new()
|
||||||
.route("/", axum::routing::get(gateways))
|
.route("/", axum::routing::get(gateways))
|
||||||
.route("/skinny", axum::routing::get(gateways_skinny))
|
.route("/skinny", axum::routing::get(gateways_skinny))
|
||||||
.route("/:identity_key", axum::routing::get(get_gateway))
|
.route("/{identity_key}", axum::routing::get(get_gateway))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[utoipa::path(
|
#[utoipa::path(
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ use crate::http::{
|
|||||||
|
|
||||||
pub(crate) fn routes() -> Router<AppState> {
|
pub(crate) fn routes() -> Router<AppState> {
|
||||||
Router::new().route("/", axum::routing::get(get_all_sessions))
|
Router::new().route("/", axum::routing::get(get_all_sessions))
|
||||||
// .route("/:node_id", axum::routing::get(get_node_sessions))
|
// .route("/{node_id}", axum::routing::get(get_node_sessions))
|
||||||
// .route("/:day", axum::routing::get(get_daily_sessions))
|
// .route("/{day}", axum::routing::get(get_daily_sessions))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, utoipa::IntoParams)]
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, utoipa::IntoParams)]
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ pub(crate) fn routes() -> Router<AppState> {
|
|||||||
Router::new()
|
Router::new()
|
||||||
.route("/", axum::routing::get(nym_nodes))
|
.route("/", axum::routing::get(nym_nodes))
|
||||||
.route(
|
.route(
|
||||||
"/:node_id/delegations",
|
"/{node_id}/delegations",
|
||||||
axum::routing::get(node_delegations),
|
axum::routing::get(node_delegations),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,8 +26,8 @@ use tracing::error;
|
|||||||
pub(crate) fn routes() -> Router<AppState> {
|
pub(crate) fn routes() -> Router<AppState> {
|
||||||
Router::new()
|
Router::new()
|
||||||
.route("/", axum::routing::get(request_testrun))
|
.route("/", axum::routing::get(request_testrun))
|
||||||
.route("/:testrun_id", axum::routing::post(submit_testrun))
|
.route("/{testrun_id}", axum::routing::post(submit_testrun))
|
||||||
.route("/:testrun_id/v2", axum::routing::post(submit_testrun_v2))
|
.route("/{testrun_id}/v2", axum::routing::post(submit_testrun_v2))
|
||||||
.layer(DefaultBodyLimit::max(1024 * 1024 * 5))
|
.layer(DefaultBodyLimit::max(1024 * 1024 * 5))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,10 @@ use axum::Router;
|
|||||||
use axum::extract::ConnectInfo;
|
use axum::extract::ConnectInfo;
|
||||||
use axum::extract::connect_info::IntoMakeServiceWithConnectInfo;
|
use axum::extract::connect_info::IntoMakeServiceWithConnectInfo;
|
||||||
use axum::middleware::AddExtension;
|
use axum::middleware::AddExtension;
|
||||||
use axum::serve::Serve;
|
use axum::serve::WithGracefulShutdown;
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
|
use tokio::net::TcpListener;
|
||||||
|
use tokio_util::sync::WaitForCancellationFutureOwned;
|
||||||
|
|
||||||
pub use router::{HttpServerConfig, NymNodeRouter, api};
|
pub use router::{HttpServerConfig, NymNodeRouter, api};
|
||||||
|
|
||||||
@@ -15,6 +17,7 @@ pub mod helpers;
|
|||||||
pub mod router;
|
pub mod router;
|
||||||
pub mod state;
|
pub mod state;
|
||||||
|
|
||||||
type InnerService = IntoMakeServiceWithConnectInfo<Router, SocketAddr>;
|
type MakeService = IntoMakeServiceWithConnectInfo<Router, SocketAddr>;
|
||||||
type ConnectInfoExt = AddExtension<Router, ConnectInfo<SocketAddr>>;
|
type InnerService = AddExtension<Router, ConnectInfo<SocketAddr>>;
|
||||||
pub type NymNodeHttpServer = Serve<InnerService, ConnectInfoExt>;
|
pub type NymNodeHttpServer =
|
||||||
|
WithGracefulShutdown<TcpListener, MakeService, InnerService, WaitForCancellationFutureOwned>;
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ pub struct Config {
|
|||||||
|
|
||||||
pub(super) fn routes<S: Send + Sync + 'static + Clone>(config: Config) -> Router<S> {
|
pub(super) fn routes<S: Send + Sync + 'static + Clone>(config: Config) -> Router<S> {
|
||||||
if let Some(assets) = config.assets_path {
|
if let Some(assets) = config.assets_path {
|
||||||
Router::new().nest_service("/", ServeDir::new(assets))
|
Router::new().fallback_service(ServeDir::new(assets))
|
||||||
} else {
|
} else {
|
||||||
Router::new().route("/", get(default))
|
Router::new().route("/", get(default))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ use nym_node_requests::routes;
|
|||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use tokio_util::sync::WaitForCancellationFutureOwned;
|
||||||
use zeroize::Zeroizing;
|
use zeroize::Zeroizing;
|
||||||
|
|
||||||
pub mod api;
|
pub mod api;
|
||||||
@@ -176,7 +177,7 @@ impl NymNodeRouter {
|
|||||||
Redirect::to(&routes::api::v1::metrics::prometheus_absolute())
|
Redirect::to(&routes::api::v1::metrics::prometheus_absolute())
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.nest(routes::LANDING_PAGE, landing_page::routes(config.landing))
|
.merge(landing_page::routes(config.landing))
|
||||||
.nest(routes::API, api::routes(config.api))
|
.nest(routes::API, api::routes(config.api))
|
||||||
.layer(axum::middleware::from_fn(logging::log_request_info))
|
.layer(axum::middleware::from_fn(logging::log_request_info))
|
||||||
.with_state(state),
|
.with_state(state),
|
||||||
@@ -186,6 +187,7 @@ impl NymNodeRouter {
|
|||||||
pub async fn build_server(
|
pub async fn build_server(
|
||||||
self,
|
self,
|
||||||
bind_address: &SocketAddr,
|
bind_address: &SocketAddr,
|
||||||
|
shutdown: WaitForCancellationFutureOwned,
|
||||||
) -> Result<NymNodeHttpServer, NymNodeHttpError> {
|
) -> Result<NymNodeHttpServer, NymNodeHttpError> {
|
||||||
let listener = tokio::net::TcpListener::bind(bind_address)
|
let listener = tokio::net::TcpListener::bind(bind_address)
|
||||||
.await
|
.await
|
||||||
@@ -198,6 +200,28 @@ impl NymNodeRouter {
|
|||||||
listener,
|
listener,
|
||||||
self.inner
|
self.inner
|
||||||
.into_make_service_with_connect_info::<SocketAddr>(),
|
.into_make_service_with_connect_info::<SocketAddr>(),
|
||||||
))
|
)
|
||||||
|
.with_graceful_shutdown(shutdown))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use nym_crypto::asymmetric::{ed25519, x25519};
|
||||||
|
use nym_node_requests::api::SignedData;
|
||||||
|
use nym_node_requests::api::v1::lewes_protocol::models::LewesProtocol;
|
||||||
|
use nym_test_utils::helpers::deterministic_rng;
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn router_constructs_without_panic() {
|
||||||
|
let mut rng = deterministic_rng();
|
||||||
|
let signing = ed25519::KeyPair::new(&mut rng);
|
||||||
|
let x25519_pub: x25519::DHPublicKey = x25519::PrivateKey::new(&mut rng).public_key().into();
|
||||||
|
let lp = LewesProtocol::new(false, 0, 0, x25519_pub, BTreeMap::new());
|
||||||
|
let signed = SignedData::new(lp, signing.private_key()).unwrap();
|
||||||
|
let config = HttpServerConfig::new(signed);
|
||||||
|
let _ = NymNodeRouter::new(config, AppState::dummy());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,4 +73,30 @@ impl AppState {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub(crate) fn dummy() -> Self {
|
||||||
|
use crate::node::key_rotation::key::SphinxPrivateKey;
|
||||||
|
use rand::rngs::OsRng;
|
||||||
|
|
||||||
|
let ed25519_keys = ed25519::KeyPair::new(&mut OsRng);
|
||||||
|
let attester_pk = *ed25519_keys.public_key();
|
||||||
|
let static_information = StaticNodeInformation {
|
||||||
|
ed25519_identity_keys: Arc::new(ed25519_keys),
|
||||||
|
x25519_versioned_noise_key: None,
|
||||||
|
ip_addresses: vec![],
|
||||||
|
hostname: None,
|
||||||
|
};
|
||||||
|
let active_sphinx = ActiveSphinxKeys::new_fresh(SphinxPrivateKey::new(&mut OsRng, 0));
|
||||||
|
|
||||||
|
AppState::new(
|
||||||
|
static_information,
|
||||||
|
active_sphinx,
|
||||||
|
NymNodeMetrics::new(),
|
||||||
|
SharedVerlocStats::default(),
|
||||||
|
Url::parse("https://attestation.test").unwrap(),
|
||||||
|
UpgradeModeState::new(attester_pk),
|
||||||
|
Duration::from_secs(60),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ use std::ops::Deref;
|
|||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
|
use tokio_util::sync::WaitForCancellationFutureOwned;
|
||||||
use tracing::{debug, info, trace};
|
use tracing::{debug, info, trace};
|
||||||
use zeroize::Zeroizing;
|
use zeroize::Zeroizing;
|
||||||
|
|
||||||
@@ -872,7 +873,10 @@ impl NymNode {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn build_http_server(&self) -> Result<NymNodeHttpServer, NymNodeError> {
|
pub(crate) async fn build_http_server(
|
||||||
|
&self,
|
||||||
|
shutdown: WaitForCancellationFutureOwned,
|
||||||
|
) -> Result<NymNodeHttpServer, NymNodeError> {
|
||||||
let auxiliary_details = api_requests::v1::node::models::AuxiliaryDetails {
|
let auxiliary_details = api_requests::v1::node::models::AuxiliaryDetails {
|
||||||
location: self.config.host.location,
|
location: self.config.host.location,
|
||||||
announce_ports: AnnouncePorts {
|
announce_ports: AnnouncePorts {
|
||||||
@@ -1023,7 +1027,7 @@ impl NymNode {
|
|||||||
);
|
);
|
||||||
|
|
||||||
Ok(NymNodeRouter::new(config, app_state)
|
Ok(NymNodeRouter::new(config, app_state)
|
||||||
.build_server(&self.config.http.bind_address)
|
.build_server(&self.config.http.bind_address, shutdown)
|
||||||
.await?)
|
.await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1342,16 +1346,17 @@ impl NymNode {
|
|||||||
);
|
);
|
||||||
debug!("config: {:#?}", self.config);
|
debug!("config: {:#?}", self.config);
|
||||||
|
|
||||||
let http_server = self.build_http_server().await?;
|
|
||||||
let bind_address = self.config.http.bind_address;
|
let bind_address = self.config.http.bind_address;
|
||||||
let server_shutdown = self.shutdown_manager.clone_shutdown_token();
|
let shutdown = self
|
||||||
|
.shutdown_manager
|
||||||
|
.clone_shutdown_token()
|
||||||
|
.cancelled_owned();
|
||||||
|
let http_server = self.build_http_server(shutdown).await?;
|
||||||
|
|
||||||
self.shutdown_manager.try_spawn_named(
|
self.shutdown_manager.try_spawn_named(
|
||||||
async move {
|
async move {
|
||||||
info!("starting NymNodeHTTPServer on {bind_address}");
|
info!("starting NymNodeHTTPServer on {bind_address}");
|
||||||
http_server
|
http_server.await
|
||||||
.with_graceful_shutdown(async move { server_shutdown.cancelled().await })
|
|
||||||
.await
|
|
||||||
},
|
},
|
||||||
"HttpApi",
|
"HttpApi",
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use axum::{Json, Router, extract::State};
|
use axum::{Json, Router, extract::State};
|
||||||
use axum_client_ip::InsecureClientIp;
|
|
||||||
use axum_extra::{TypedHeader, headers::UserAgent};
|
use axum_extra::{TypedHeader, headers::UserAgent};
|
||||||
|
use nym_http_api_common::middleware::client_ip::ClientIpAddr;
|
||||||
use nym_statistics_common::report::vpn_client::{
|
use nym_statistics_common::report::vpn_client::{
|
||||||
ActiveDeviceReport, StaticInformationReport, VpnClientStatsReport, VpnClientStatsReportV2,
|
ActiveDeviceReport, StaticInformationReport, VpnClientStatsReport, VpnClientStatsReportV2,
|
||||||
};
|
};
|
||||||
@@ -35,15 +35,12 @@ pub(crate) fn routes() -> Router<AppState> {
|
|||||||
async fn submit_stats_report(
|
async fn submit_stats_report(
|
||||||
State(mut state): State<AppState>,
|
State(mut state): State<AppState>,
|
||||||
TypedHeader(user_agent): TypedHeader<UserAgent>,
|
TypedHeader(user_agent): TypedHeader<UserAgent>,
|
||||||
insecure_ip_addr: InsecureClientIp,
|
ClientIpAddr(client_ip): ClientIpAddr,
|
||||||
Json(report): Json<VpnClientStatsReport>,
|
Json(report): Json<VpnClientStatsReport>,
|
||||||
) -> HttpResult<Json<()>> {
|
) -> HttpResult<Json<()>> {
|
||||||
let now = time::OffsetDateTime::now_utc();
|
let now = time::OffsetDateTime::now_utc();
|
||||||
|
|
||||||
let gateway_record = state
|
let gateway_record = state.network_view().get_country_by_ip(&client_ip).await;
|
||||||
.network_view()
|
|
||||||
.get_country_by_ip(&insecure_ip_addr.0)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
let from_mixnet = gateway_record.is_some();
|
let from_mixnet = gateway_record.is_some();
|
||||||
let maybe_location = gateway_record.unwrap_or_default();
|
let maybe_location = gateway_record.unwrap_or_default();
|
||||||
@@ -60,7 +57,7 @@ async fn submit_stats_report(
|
|||||||
&report,
|
&report,
|
||||||
user_agent,
|
user_agent,
|
||||||
from_mixnet,
|
from_mixnet,
|
||||||
insecure_ip_addr.0,
|
client_ip,
|
||||||
maybe_location,
|
maybe_location,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -93,15 +90,12 @@ async fn submit_stats_report(
|
|||||||
async fn submit_active_device(
|
async fn submit_active_device(
|
||||||
State(mut state): State<AppState>,
|
State(mut state): State<AppState>,
|
||||||
TypedHeader(user_agent): TypedHeader<UserAgent>,
|
TypedHeader(user_agent): TypedHeader<UserAgent>,
|
||||||
insecure_ip_addr: InsecureClientIp,
|
ClientIpAddr(client_ip): ClientIpAddr,
|
||||||
Json(report): Json<ActiveDeviceReport>,
|
Json(report): Json<ActiveDeviceReport>,
|
||||||
) -> HttpResult<Json<()>> {
|
) -> HttpResult<Json<()>> {
|
||||||
let now = time::OffsetDateTime::now_utc();
|
let now = time::OffsetDateTime::now_utc();
|
||||||
|
|
||||||
let gateway_record = state
|
let gateway_record = state.network_view().get_country_by_ip(&client_ip).await;
|
||||||
.network_view()
|
|
||||||
.get_country_by_ip(&insecure_ip_addr.0)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
let from_mixnet = gateway_record.is_some();
|
let from_mixnet = gateway_record.is_some();
|
||||||
|
|
||||||
@@ -140,7 +134,7 @@ async fn submit_active_device(
|
|||||||
async fn submit_session_report(
|
async fn submit_session_report(
|
||||||
State(mut state): State<AppState>,
|
State(mut state): State<AppState>,
|
||||||
TypedHeader(user_agent): TypedHeader<UserAgent>,
|
TypedHeader(user_agent): TypedHeader<UserAgent>,
|
||||||
insecure_ip_addr: InsecureClientIp, // This is the reverse proxy IP for now, but maybe in the future?
|
ClientIpAddr(client_ip): ClientIpAddr,
|
||||||
Json(report): Json<VpnClientStatsReportV2>,
|
Json(report): Json<VpnClientStatsReportV2>,
|
||||||
) -> HttpResult<Json<()>> {
|
) -> HttpResult<Json<()>> {
|
||||||
let now = time::OffsetDateTime::now_utc();
|
let now = time::OffsetDateTime::now_utc();
|
||||||
@@ -164,7 +158,7 @@ async fn submit_session_report(
|
|||||||
&report,
|
&report,
|
||||||
user_agent,
|
user_agent,
|
||||||
from_mixnet,
|
from_mixnet,
|
||||||
insecure_ip_addr.0,
|
client_ip,
|
||||||
maybe_location,
|
maybe_location,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -79,3 +79,13 @@ fn setup_cors() -> CorsLayer {
|
|||||||
.allow_headers(tower_http::cors::Any)
|
.allow_headers(tower_http::cors::Any)
|
||||||
.allow_credentials(false)
|
.allow_credentials(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn router_constructs_without_panic() {
|
||||||
|
let _ = RouterBuilder::with_default_routes().finalize_routes();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user