Skip to content

Commit

Permalink
Fix content-header parsing in juniper_axum (#1289)
Browse files Browse the repository at this point in the history
* Fix content-header parsing in juniper_axum

Check media type using `starts_with` instead of full string matching.

Fixes #1288

Signed-off-by: Joe Grund <jgrund@whamcloud.io>

* Add changelog and tests

---------

Signed-off-by: Joe Grund <jgrund@whamcloud.io>
  • Loading branch information
jgrund authored Oct 26, 2024
1 parent 257bc69 commit 3c1a561
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 11 deletions.
4 changes: 4 additions & 0 deletions juniper_axum/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ All user visible changes to `juniper_axum` crate will be documented in this file

- Building on `wasm32-unknown-unknown` and `wasm32-wasi` targets. ([#1283], [#1282])

### Fixed

- `Content-Type` header reading full value instead of just the media type. ([#1288])

[#1272]: /../../pull/1272
[#1282]: /../../issues/1282
[#1283]: /../../pull/1283
Expand Down
56 changes: 45 additions & 11 deletions juniper_axum/src/extract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use axum::{
async_trait,
body::Body,
extract::{FromRequest, FromRequestParts, Query},
http::{HeaderValue, Method, Request, StatusCode},
http::{header, HeaderValue, Method, Request, StatusCode},
response::{IntoResponse as _, Response},
Json, RequestExt as _,
};
Expand Down Expand Up @@ -85,7 +85,7 @@ where
async fn from_request(mut req: Request<Body>, state: &State) -> Result<Self, Self::Rejection> {
let content_type = req
.headers()
.get("content-type")
.get(header::CONTENT_TYPE)
.map(HeaderValue::to_str)
.transpose()
.map_err(|_| {
Expand Down Expand Up @@ -122,22 +122,24 @@ where
.into_response()
})
}),
(&Method::POST, Some("application/json")) => {
(&Method::POST, Some(x)) if x.starts_with("application/json") => {
Json::<GraphQLBatchRequest<S>>::from_request(req, state)
.await
.map(|req| Self(req.0))
.map_err(|e| {
(StatusCode::BAD_REQUEST, format!("Invalid JSON body: {e}")).into_response()
})
}
(&Method::POST, Some("application/graphql")) => String::from_request(req, state)
.await
.map(|body| {
Self(GraphQLBatchRequest::Single(GraphQLRequest::new(
body, None, None,
)))
})
.map_err(|_| (StatusCode::BAD_REQUEST, "Not valid UTF-8 body").into_response()),
(&Method::POST, Some(x)) if x.starts_with("application/graphql") => {
String::from_request(req, state)
.await
.map(|body| {
Self(GraphQLBatchRequest::Single(GraphQLRequest::new(
body, None, None,
)))
})
.map_err(|_| (StatusCode::BAD_REQUEST, "Not valid UTF-8 body").into_response())
}
(&Method::POST, _) => Err((
StatusCode::UNSUPPORTED_MEDIA_TYPE,
"`Content-Type` header is expected to be either `application/json` or \
Expand Down Expand Up @@ -246,6 +248,22 @@ mod juniper_request_tests {
assert_eq!(do_from_request(req).await, expected);
}

#[tokio::test]
async fn from_json_post_request_with_charset() {
let req = Request::post("/")
.header("content-type", "application/json; charset=utf-8")
.body(Body::from(r#"{"query": "{ add(a: 2, b: 3) }"}"#))
.unwrap_or_else(|e| panic!("cannot build `Request`: {e}"));

let expected = JuniperRequest(GraphQLBatchRequest::Single(GraphQLRequest::new(
"{ add(a: 2, b: 3) }".to_string(),
None,
None,
)));

assert_eq!(do_from_request(req).await, expected);
}

#[tokio::test]
async fn from_graphql_post_request() {
let req = Request::post("/")
Expand All @@ -262,6 +280,22 @@ mod juniper_request_tests {
assert_eq!(do_from_request(req).await, expected);
}

#[tokio::test]
async fn from_graphql_post_request_with_charset() {
let req = Request::post("/")
.header("content-type", "application/graphql; charset=utf-8")
.body(Body::from(r#"{ add(a: 2, b: 3) }"#))
.unwrap_or_else(|e| panic!("cannot build `Request`: {e}"));

let expected = JuniperRequest(GraphQLBatchRequest::Single(GraphQLRequest::new(
"{ add(a: 2, b: 3) }".to_string(),
None,
None,
)));

assert_eq!(do_from_request(req).await, expected);
}

/// Performs [`JuniperRequest::from_request()`].
async fn do_from_request(req: Request<Body>) -> JuniperRequest {
match JuniperRequest::from_request(req, &()).await {
Expand Down

0 comments on commit 3c1a561

Please sign in to comment.