-
Notifications
You must be signed in to change notification settings - Fork 91
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
ref(server): Use multer directly (#3978)
Switches to direct use of `multer` instead of `axum`'s wrapper. This allows us to use constraints to configure the maximum body size and maximum field size directly on the `Multipart` instance instead of handling this manually. In order to keep using the multipart type as an extractor, we define `Remote` which is a shallow transparent wrapper around any remote type we would like to implement `FromRequest`, `FromRequestParts`, or `IntoResponse` for. This way, we can use the `Multipart` and `multer::Error` types directly in our signatures. Usage of this type is documented on the type.
- Loading branch information
Showing
10 changed files
with
149 additions
and
100 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,49 +1,43 @@ | ||
use axum::extract::{DefaultBodyLimit, Multipart, Path}; | ||
use axum::extract::Path; | ||
use axum::http::StatusCode; | ||
use axum::response::IntoResponse; | ||
use axum::routing::{post, MethodRouter}; | ||
use relay_config::Config; | ||
use multer::Multipart; | ||
use relay_event_schema::protocol::EventId; | ||
use serde::Deserialize; | ||
|
||
use crate::endpoints::common::{self, BadStoreRequest}; | ||
use crate::envelope::{AttachmentType, Envelope}; | ||
use crate::extractors::RequestMeta; | ||
use crate::extractors::{Remote, RequestMeta}; | ||
use crate::service::ServiceState; | ||
use crate::utils; | ||
|
||
#[derive(Debug, Deserialize)] | ||
struct AttachmentPath { | ||
pub struct AttachmentPath { | ||
event_id: EventId, | ||
} | ||
|
||
async fn extract_envelope( | ||
config: &Config, | ||
meta: RequestMeta, | ||
path: AttachmentPath, | ||
multipart: Multipart, | ||
multipart: Multipart<'static>, | ||
) -> Result<Box<Envelope>, BadStoreRequest> { | ||
let max_size = config.max_attachment_size(); | ||
let items = utils::multipart_items(multipart, max_size, |_| AttachmentType::default()).await?; | ||
let items = utils::multipart_items(multipart, |_| AttachmentType::default()).await?; | ||
|
||
let mut envelope = Envelope::from_request(Some(path.event_id), meta); | ||
for item in items { | ||
envelope.add_item(item); | ||
} | ||
|
||
Ok(envelope) | ||
} | ||
|
||
async fn handle( | ||
pub async fn handle( | ||
state: ServiceState, | ||
meta: RequestMeta, | ||
Path(path): Path<AttachmentPath>, | ||
multipart: Multipart, | ||
Remote(multipart): Remote<Multipart<'static>>, | ||
) -> Result<impl IntoResponse, BadStoreRequest> { | ||
let envelope = extract_envelope(state.config(), meta, path, multipart).await?; | ||
let envelope = extract_envelope(meta, path, multipart).await?; | ||
common::handle_envelope(&state, envelope).await?; | ||
Ok(StatusCode::CREATED) | ||
} | ||
|
||
pub fn route(config: &Config) -> MethodRouter<ServiceState> { | ||
post(handle).route_layer(DefaultBodyLimit::max(config.max_attachments_size())) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,15 @@ | ||
mod content_type; | ||
mod forwarded_for; | ||
mod mime; | ||
mod remote; | ||
mod request_meta; | ||
mod signed_json; | ||
mod start_time; | ||
|
||
pub use self::content_type::*; | ||
pub use self::forwarded_for::*; | ||
pub use self::mime::*; | ||
pub use self::remote::*; | ||
pub use self::request_meta::*; | ||
pub use self::signed_json::*; | ||
pub use self::start_time::*; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
//! Extractors for types from other crates via [`Remote`]. | ||
|
||
use axum::extract::{FromRequest, Request}; | ||
use axum::http::StatusCode; | ||
use axum::response::{IntoResponse, Response}; | ||
use multer::Multipart; | ||
|
||
use crate::service::ServiceState; | ||
use crate::utils::{self, ApiErrorResponse}; | ||
|
||
/// A transparent wrapper around a remote type that implements [`FromRequest`] or [`IntoResponse`]. | ||
/// | ||
/// # Example | ||
/// | ||
/// ```ignore | ||
/// use std::convert::Infallible; | ||
/// | ||
/// use axum::extract::{FromRequest, Request}; | ||
/// use axum::response::IntoResponse; | ||
/// | ||
/// use crate::extractors::Remote; | ||
/// | ||
/// // Derive `FromRequest` for `bool` for illustration purposes: | ||
/// #[axum::async_trait] | ||
/// impl<S> axum::extract::FromRequest<S> for Remote<bool> { | ||
/// type Rejection = Remote<Infallible>; | ||
/// | ||
/// async fn from_request(request: Request) -> Result<Self, Self::Rejection> { | ||
/// Ok(Remote(true)) | ||
/// } | ||
/// } | ||
/// | ||
/// impl IntoResponse for Remote<Infallible> { | ||
/// fn into_response(self) -> axum::response::Response { | ||
/// match self.0 {} | ||
/// } | ||
/// } | ||
/// ``` | ||
#[derive(Debug)] | ||
pub struct Remote<T>(pub T); | ||
|
||
impl<T> From<T> for Remote<T> { | ||
fn from(inner: T) -> Self { | ||
Self(inner) | ||
} | ||
} | ||
|
||
#[axum::async_trait] | ||
impl FromRequest<ServiceState> for Remote<Multipart<'static>> { | ||
type Rejection = Remote<multer::Error>; | ||
|
||
async fn from_request(request: Request, state: &ServiceState) -> Result<Self, Self::Rejection> { | ||
utils::multipart_from_request(request, state.config()) | ||
.map(Remote) | ||
.map_err(Remote) | ||
} | ||
} | ||
|
||
impl IntoResponse for Remote<multer::Error> { | ||
fn into_response(self) -> Response { | ||
let Self(ref error) = self; | ||
|
||
let status_code = match error { | ||
multer::Error::FieldSizeExceeded { .. } => StatusCode::PAYLOAD_TOO_LARGE, | ||
multer::Error::StreamSizeExceeded { .. } => StatusCode::PAYLOAD_TOO_LARGE, | ||
_ => StatusCode::BAD_REQUEST, | ||
}; | ||
|
||
(status_code, ApiErrorResponse::from_error(error)).into_response() | ||
} | ||
} |
Oops, something went wrong.