Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion crates/driver/src/domain/competition/pre_processing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,11 @@ impl Utilities {
observe::metrics::metrics().on_auction_overhead_start("driver", "parse_dto");
// deserialization takes tens of milliseconds so run it on a blocking task
tokio::task::spawn_blocking(move || {
serde_json::from_slice(&solve_request).context("could not parse solve request")
serde_json::from_slice(&solve_request)
.inspect_err(|err| {
tracing::warn!(?err, "failed to parse /solve request body");
})
.context("could not parse solve request")
})
.await
.context("failed to await blocking task")??
Expand Down
65 changes: 65 additions & 0 deletions crates/driver/src/infra/api/extract.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
//! Axum extractors that emit a `warn` log when request deserialization
//! fails, then delegate to the stock extractor's rejection so the HTTP
//! response shape is unchanged.

use {
axum::{
extract::{
FromRequest,
FromRequestParts,
Request,
rejection::{JsonRejection, QueryRejection},
},
http::request::Parts,
},
serde::de::DeserializeOwned,
};

pub struct LoggingJson<T>(pub T);
Comment thread
squadgazzz marked this conversation as resolved.

impl<S, T> FromRequest<S> for LoggingJson<T>
where
S: Send + Sync,
T: DeserializeOwned,
{
type Rejection = JsonRejection;

async fn from_request(req: Request, state: &S) -> Result<Self, Self::Rejection> {
match axum::Json::<T>::from_request(req, state).await {
Ok(axum::Json(value)) => Ok(Self(value)),
Err(rejection) => {
tracing::warn!(
err = %rejection,
target = std::any::type_name::<T>(),
"failed to deserialize JSON request body",
);
Err(rejection)
}
}
}
}

pub struct LoggingQuery<T>(pub T);
Comment thread
squadgazzz marked this conversation as resolved.

impl<S, T> FromRequestParts<S> for LoggingQuery<T>
where
S: Send + Sync,
T: DeserializeOwned,
{
type Rejection = QueryRejection;

async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
match axum::extract::Query::<T>::from_request_parts(parts, state).await {
Ok(axum::extract::Query(value)) => Ok(Self(value)),
Err(rejection) => {
tracing::warn!(
err = %rejection,
target = std::any::type_name::<T>(),
query = parts.uri.query().unwrap_or_default(),
"failed to deserialize query string",
);
Err(rejection)
}
}
}
}
1 change: 1 addition & 0 deletions crates/driver/src/infra/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ use {
};

mod error;
mod extract;
pub mod routes;

pub struct Api {
Expand Down
6 changes: 3 additions & 3 deletions crates/driver/src/infra/api/routes/quote/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use {
crate::infra::{
api::{Error, State},
api::{Error, State, extract::LoggingQuery},
observe,
},
tracing::Instrument,
Expand All @@ -16,10 +16,10 @@ pub(in crate::infra::api) fn quote(router: axum::Router<State>) -> axum::Router<

async fn route(
state: axum::extract::State<State>,
order: axum::extract::Query<dto::Order>,
LoggingQuery(order): LoggingQuery<dto::Order>,
) -> Result<axum::Json<dto::Quote>, (axum::http::StatusCode, axum::Json<Error>)> {
let handle_request = async {
let order = order.0.into_domain();
let order = order.into_domain();
observe::quoting(&order);
let quote = order
.quote(
Expand Down
4 changes: 2 additions & 2 deletions crates/driver/src/infra/api/routes/reveal/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use {
crate::{
domain::competition::auction,
infra::{
api::{self, Error, State},
api::{self, Error, State, extract::LoggingJson},
observe,
},
},
Expand All @@ -17,7 +17,7 @@ pub(in crate::infra::api) fn reveal(router: axum::Router<State>) -> axum::Router

async fn route(
state: axum::extract::State<State>,
req: axum::Json<dto::RevealRequest>,
LoggingJson(req): LoggingJson<dto::RevealRequest>,
) -> Result<axum::Json<dto::RevealResponse>, (axum::http::StatusCode, axum::Json<Error>)> {
let auction_id =
auction::Id::try_from(req.auction_id).map_err(api::routes::AuctionError::from)?;
Expand Down
4 changes: 2 additions & 2 deletions crates/driver/src/infra/api/routes/settle/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use {
crate::{
domain::competition::auction,
infra::{
api::{self, Error, State},
api::{self, Error, State, extract::LoggingJson},
observe,
},
},
Expand All @@ -17,7 +17,7 @@ pub(in crate::infra::api) fn settle(router: axum::Router<State>) -> axum::Router

async fn route(
state: axum::extract::State<State>,
req: axum::Json<dto::SettleRequest>,
LoggingJson(req): LoggingJson<dto::SettleRequest>,
) -> Result<(), (axum::http::StatusCode, axum::Json<Error>)> {
let auction_id =
auction::Id::try_from(req.auction_id).map_err(api::routes::AuctionError::from)?;
Expand Down
Loading