Skip to content

juniper_warp::make_graphql_filter improperly returns 4xx on some context_extractor 5xx errors #1177

Closed
@scottlamb

Description

@scottlamb

Describe the bug
with juniper_warp = "0.7.0", this sequence can happen:

  1. request comes in with valid application/json body.
  2. post_json_filter runs; context_extractor fails for some transient reason (database error in my case), returns some Rejection that should produce a 5xx error.
  3. then post_graphql_filter runs. context_extractor happens to succeed this time. post_graphql_filter returns a 4xx rejection about a parse error because the body's actually in json format.

To Reproduce
Steps to reproduce the behavior:
(code example preferred)

failing unit test diff
diff --git a/juniper_warp/src/lib.rs b/juniper_warp/src/lib.rs
index 7b524d2a..7567062f 100644
--- a/juniper_warp/src/lib.rs
+++ b/juniper_warp/src/lib.rs
@@ -449,7 +449,7 @@ pub mod subscriptions {
 #[cfg(test)]
 mod tests {
     use super::*;
-    use warp::{http, test::request};
+    use warp::{http, test::request, reject::Reject};
 
     #[test]
     fn graphiql_response_does_not_panic() {
@@ -642,6 +642,52 @@ mod tests {
 
         assert!(result.is_err());
     }
+
+    #[tokio::test]
+    async fn context_extractor_failure() {
+        use juniper::{
+            tests::fixtures::starwars::schema::{Database, Query},
+            EmptyMutation, EmptySubscription, RootNode,
+        };
+
+        type Schema =
+            juniper::RootNode<'static, Query, EmptyMutation<Database>, EmptySubscription<Database>>;
+
+        let schema: Schema = RootNode::new(
+            Query,
+            EmptyMutation::<Database>::new(),
+            EmptySubscription::<Database>::new(),
+        );
+
+        let called = Arc::new(std::sync::atomic::AtomicBool::new(false));
+        #[derive(Debug)]
+        struct MyRejection;
+        impl Reject for MyRejection {}
+        let context_extractor: warp::filters::BoxedFilter<(Database,)> = warp::any().and_then(move || {
+            std::future::ready(if called.swap(true, std::sync::atomic::Ordering::Relaxed) {
+                println!("second call");
+                Ok(Database::new())
+            } else {
+                println!("first call");
+                Err(warp::reject::custom(MyRejection))
+            })
+        }).boxed();
+        let filter = warp::path("graphql").and(make_graphql_filter(schema, context_extractor));
+        let response = request()
+            .method("POST")
+            .path("/graphql")
+            .header("accept", "application/json")
+            .header("content-type", "application/json")
+            .body(
+                r##"[
+                     { "variables": null, "query": "{ hero(episode: NEW_HOPE) { name } }" },
+                     { "variables": null, "query": "{ hero(episode: EMPIRE) { id name } }" }
+                 ]"##,
+            )
+            .reply(&filter)
+            .await;
+        assert_eq!(response.status(), http::StatusCode::INTERNAL_SERVER_ERROR);
+    }
 }
 
 #[cfg(test)]

Expected behavior
It should run my context_extractor only once per request. Any Rejections returned from the context_extractor should be returned faithfully from the combined filter returned from make_graphql_filter, rather than effectively turning a 5xx error into a 4xx error.

Additional context
Add any other context about the problem here.

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingk::integrationRelated to integration with third-party libraries or systemslib::warpRelated to `warp` crate integration

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions