Files
continuwuity/src/router/request.rs
T

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

127 lines
3.2 KiB
Rust
Raw Normal View History

2025-02-01 23:41:05 +00:00
use std::{
fmt::Debug,
sync::{Arc, atomic::Ordering},
2025-02-02 10:43:02 +00:00
time::Duration,
2025-02-01 23:41:05 +00:00
};
2024-05-09 15:59:08 -07:00
use axum::{
extract::State,
response::{IntoResponse, Response},
};
use conduwuit::{Result, debug, debug_error, debug_warn, err, error, trace};
2024-12-14 21:58:01 -05:00
use conduwuit_service::Services;
2025-02-02 10:43:02 +00:00
use futures::FutureExt;
2024-05-09 15:59:08 -07:00
use http::{Method, StatusCode, Uri};
2025-02-02 10:43:02 +00:00
use tokio::time::sleep;
2025-02-02 10:07:00 +00:00
use tracing::Span;
2024-05-09 15:59:08 -07:00
2025-02-02 23:19:35 +00:00
#[tracing::instrument(name = "request", level = "debug", skip_all)]
2024-05-09 15:59:08 -07:00
pub(crate) async fn handle(
State(services): State<Arc<Services>>,
req: http::Request<axum::body::Body>,
next: axum::middleware::Next,
) -> Result<Response, StatusCode> {
2025-01-30 09:28:34 +00:00
if !services.server.running() {
2024-05-09 15:59:08 -07:00
debug_warn!(
method = %req.method(),
uri = %req.uri(),
"unavailable pending shutdown"
);
return Err(StatusCode::SERVICE_UNAVAILABLE);
}
2025-01-30 09:28:34 +00:00
let uri = req.uri().clone();
2025-02-01 23:41:05 +00:00
let method = req.method().clone();
let services_ = services.clone();
2025-02-02 10:07:00 +00:00
let parent = Span::current();
let task = services.server.runtime().spawn(async move {
tokio::select! {
response = execute(&services_, req, next, &parent) => response,
2025-02-02 10:43:02 +00:00
response = services_.server.until_shutdown()
.then(|()| {
let timeout = services_.server.config.client_shutdown_timeout;
let timeout = Duration::from_secs(timeout);
sleep(timeout)
})
.map(|()| StatusCode::SERVICE_UNAVAILABLE)
.map(IntoResponse::into_response) => response,
2025-02-02 10:07:00 +00:00
}
});
2025-02-01 23:41:05 +00:00
task.await
.map_err(unhandled)
.and_then(move |result| handle_result(&method, &uri, result))
}
2025-02-02 10:07:00 +00:00
#[tracing::instrument(
name = "handle",
level = "debug",
parent = parent,
skip_all,
2025-02-02 23:19:35 +00:00
fields(
active = %services
.server
.metrics
.requests_handle_active
.fetch_add(1, Ordering::Relaxed),
handled = %services
.server
.metrics
.requests_handle_finished
.load(Ordering::Relaxed),
)
2025-02-02 10:07:00 +00:00
)]
2025-02-01 23:41:05 +00:00
async fn execute(
// we made a safety contract that Services will not go out of scope
// during the request; this ensures a reference is accounted for at
// the base frame of the task regardless of its detachment.
2025-02-02 23:19:35 +00:00
services: &Arc<Services>,
2025-02-01 23:41:05 +00:00
req: http::Request<axum::body::Body>,
next: axum::middleware::Next,
parent: &Span,
2025-02-01 23:41:05 +00:00
) -> Response {
2025-02-02 23:19:35 +00:00
#[cfg(debug_assertions)]
conduwuit::defer! {{
_ = services.server
.metrics
.requests_handle_finished
.fetch_add(1, Ordering::Relaxed);
_ = services.server
.metrics
.requests_handle_active
.fetch_sub(1, Ordering::Relaxed);
}};
2025-02-01 23:41:05 +00:00
next.run(req).await
2024-05-09 15:59:08 -07:00
}
fn handle_result(method: &Method, uri: &Uri, result: Response) -> Result<Response, StatusCode> {
2024-05-09 15:59:08 -07:00
let status = result.status();
let code = status.as_u16();
let reason = status.canonical_reason().unwrap_or("Unknown Reason");
2025-02-01 23:41:05 +00:00
2024-05-09 15:59:08 -07:00
if status.is_server_error() {
2026-01-04 03:04:37 +00:00
error!(%method, %uri, "{code} {reason}");
2024-05-09 15:59:08 -07:00
} else if status.is_client_error() {
2026-01-04 03:04:37 +00:00
debug_error!(%method, %uri, "{code} {reason}");
2024-05-09 15:59:08 -07:00
} else if status.is_redirection() {
2026-01-04 03:04:37 +00:00
debug!(%method, %uri, "{code} {reason}");
2024-05-09 15:59:08 -07:00
} else {
2026-01-04 03:04:37 +00:00
trace!(%method, %uri, "{code} {reason}");
2024-05-09 15:59:08 -07:00
}
if status == StatusCode::METHOD_NOT_ALLOWED {
return Ok(err!(Request(Unrecognized("Method Not Allowed"))).into_response());
}
Ok(result)
2024-05-09 15:59:08 -07:00
}
2025-02-01 23:41:05 +00:00
#[cold]
fn unhandled<Error: Debug>(e: Error) -> StatusCode {
error!("unhandled error or panic during request: {e:?}");
StatusCode::INTERNAL_SERVER_ERROR
}