From 4483680da20a4c6b3426330a85eae802d13d002f Mon Sep 17 00:00:00 2001 From: YassinEldeeb Date: Thu, 30 Mar 2023 08:21:38 +0200 Subject: [PATCH 01/26] fix(server/http): graphql server compliance issues --- .gitignore | 4 ++ server/http/src/server.rs | 36 +++++++++---- server/http/src/service.rs | 102 +++++++++++++++++++++++++++++-------- 3 files changed, 111 insertions(+), 31 deletions(-) diff --git a/.gitignore b/.gitignore index bf084b6c226..a0d8e5475b4 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,7 @@ lcov.info # Built solidity contracts. /tests/**/bin /tests/**/truffle_output + +# Docker volumes and debug logs +.postgres +logfile \ No newline at end of file diff --git a/server/http/src/server.rs b/server/http/src/server.rs index a99e8bafe05..f5be88d270a 100644 --- a/server/http/src/server.rs +++ b/server/http/src/server.rs @@ -1,11 +1,12 @@ use std::net::{Ipv4Addr, SocketAddrV4}; -use hyper::service::make_service_fn; -use hyper::Server; +use futures::future::{ok, Future}; +use hyper::service::{make_service_fn, service_fn}; +use hyper::{Body, Method, Request, Response, Server, StatusCode}; +use thiserror::Error; use crate::service::GraphQLService; -use graph::prelude::{GraphQLServer as GraphQLServerTrait, *}; -use thiserror::Error; +use graph::prelude::{GraphQLServer as GraphQLServerTrait, GraphQlRunner, *}; /// Errors that may occur when starting the server. #[derive(Debug, Error)] @@ -66,12 +67,27 @@ where let graphql_runner = self.graphql_runner.clone(); let node_id = self.node_id.clone(); let new_service = make_service_fn(move |_| { - futures03::future::ok::<_, Error>(GraphQLService::new( - logger_for_service.clone(), - graphql_runner.clone(), - ws_port, - node_id.clone(), - )) + ok::<_, Error>(service_fn(move |req: Request| { + match ( + req.method(), + req.headers().get(hyper::header::CONTENT_LENGTH), + ) { + (&Method::POST, Some(length)) if length == "0" => { + let mut res = Response::new(Body::empty()); + *res.status_mut() = StatusCode::BAD_REQUEST; + ok::<_, Error>(res) + } + _ => { + let service = GraphQLService::new( + logger_for_service.clone(), + graphql_runner.clone(), + ws_port, + node_id.clone(), + ); + futures03::future::ok::<_, Error>(service) + } + } + })) }); // Create a task to run the server and handle HTTP requests diff --git a/server/http/src/service.rs b/server/http/src/service.rs index 64cc36afdcc..9d4cbd3f240 100644 --- a/server/http/src/service.rs +++ b/server/http/src/service.rs @@ -4,6 +4,8 @@ use std::task::Context; use std::task::Poll; use std::time::Instant; +use graph::prelude::serde_json; +use graph::prelude::serde_json::json; use graph::prelude::*; use graph::semver::VersionReq; use graph::{components::server::query::GraphQLServerError, data::query::QueryTarget}; @@ -62,14 +64,17 @@ where } async fn index(self) -> GraphQLServiceResult { + let response_obj = json!({ + "message": "Access deployed subgraphs by deployment ID at \ + /subgraphs/id/ or by name at /subgraphs/name/" + }); + let response_str = serde_json::to_string(&response_obj).unwrap(); + Ok(Response::builder() .status(200) .header(ACCESS_CONTROL_ALLOW_ORIGIN, "*") - .header(CONTENT_TYPE, "text/plain") - .body(Body::from(String::from( - "Access deployed subgraphs by deployment ID at \ - /subgraphs/id/ or by name at /subgraphs/name/", - ))) + .header(CONTENT_TYPE, "application/json") + .body(Body::from(response_str)) .unwrap()) } @@ -79,7 +84,7 @@ where Ok(Response::builder() .status(200) .header(ACCESS_CONTROL_ALLOW_ORIGIN, "*") - .header(CONTENT_TYPE, "text/html") + .header(CONTENT_TYPE, "text/html; charset=utf-8") .body(Body::from(contents)) .unwrap()) } @@ -202,7 +207,7 @@ where .header(ACCESS_CONTROL_ALLOW_ORIGIN, "*") .header(ACCESS_CONTROL_ALLOW_HEADERS, "Content-Type, User-Agent") .header(ACCESS_CONTROL_ALLOW_METHODS, "GET, OPTIONS, POST") - .header(CONTENT_TYPE, "text/html") + .header(CONTENT_TYPE, "text/html; charset=utf-8") .body(Body::from("")) .unwrap()) } @@ -220,7 +225,7 @@ where .status(StatusCode::FOUND) .header(ACCESS_CONTROL_ALLOW_ORIGIN, "*") .header(LOCATION, loc_header_val) - .header(CONTENT_TYPE, "text/plain") + .header(CONTENT_TYPE, "text/plain; charset=utf-8") .body(Body::from("Redirecting...")) .unwrap() }) @@ -229,11 +234,16 @@ where /// Handles 404s. fn handle_not_found(&self) -> GraphQLServiceResponse { async { + let response_obj = json!({ + "message": "Not found" + }); + let response_str = serde_json::to_string(&response_obj).unwrap(); + Ok(Response::builder() - .status(StatusCode::NOT_FOUND) - .header(CONTENT_TYPE, "text/plain") + .status(200) + .header(CONTENT_TYPE, "application/json") .header(ACCESS_CONTROL_ALLOW_ORIGIN, "*") - .body(Body::from("Not found")) + .body(Body::from(response_str)) .unwrap()) } .boxed() @@ -316,22 +326,35 @@ where // Instead, we generate a Response with an error code and return Ok Box::pin(async move { let result = service.handle_call(req).await; + match result { Ok(response) => Ok(response), - Err(err @ GraphQLServerError::ClientError(_)) => Ok(Response::builder() - .status(400) - .header(CONTENT_TYPE, "text/plain") - .header(ACCESS_CONTROL_ALLOW_ORIGIN, "*") - .body(Body::from(err.to_string())) - .unwrap()), + Err(err @ GraphQLServerError::ClientError(_)) => { + let response_obj = json!({ + "error": err.to_string() + }); + let response_str = serde_json::to_string(&response_obj).unwrap(); + + Ok(Response::builder() + .status(200) + .header(CONTENT_TYPE, "application/json") + .header(ACCESS_CONTROL_ALLOW_ORIGIN, "*") + .body(Body::from(response_str)) + .unwrap()) + } Err(err @ GraphQLServerError::QueryError(_)) => { error!(logger, "GraphQLService call failed: {}", err); + let response_obj = json!({ + "QueryError": err.to_string() + }); + let response_str = serde_json::to_string(&response_obj).unwrap(); + Ok(Response::builder() - .status(400) - .header(CONTENT_TYPE, "text/plain") + .status(200) + .header(CONTENT_TYPE, "application/json") .header(ACCESS_CONTROL_ALLOW_ORIGIN, "*") - .body(Body::from(format!("Query error: {}", err))) + .body(Body::from(response_str)) .unwrap()) } Err(err @ GraphQLServerError::InternalError(_)) => { @@ -339,7 +362,7 @@ where Ok(Response::builder() .status(500) - .header(CONTENT_TYPE, "text/plain") + .header(CONTENT_TYPE, "text/plain; charset=utf-8") .header(ACCESS_CONTROL_ALLOW_ORIGIN, "*") .body(Body::from(format!("Internal server error: {}", err))) .unwrap()) @@ -352,7 +375,9 @@ where #[cfg(test)] mod tests { use graph::data::value::{Object, Word}; + use http::header::CONTENT_TYPE; use http::status::StatusCode; + use hyper::body::HttpBody; use hyper::service::Service; use hyper::{Body, Method, Request}; @@ -419,6 +444,39 @@ mod tests { } } + #[tokio::test] + async fn querying_not_found_routes_responds_correctly() { + let logger = Logger::root(slog::Discard, o!()); + let graphql_runner = Arc::new(TestGraphQlRunner); + + let node_id = NodeId::new("test").unwrap(); + let mut service = GraphQLService::new(logger, graphql_runner, 8001, node_id); + + let request = Request::builder() + .method(Method::GET) + .header(CONTENT_TYPE, "text/plain; charset=utf-8") + .uri("http://localhost:8000/not_found_route".to_string()) + .body(Body::from("{}")) + .unwrap(); + + let response = + futures03::executor::block_on(service.call(request)).expect("Should return a response"); + + let content_type_header = response.status(); + assert_eq!(content_type_header, StatusCode::OK); + + let content_type_header = response.headers().get(CONTENT_TYPE).unwrap(); + assert_eq!(content_type_header, "application/json"); + + let body_bytes = hyper::body::to_bytes(response.into_body()).await.unwrap(); + let json: serde_json::Result = + serde_json::from_str(String::from_utf8(body_bytes.to_vec()).unwrap().as_str()); + + assert!(json.is_ok(), "Response body is not valid JSON"); + + assert_eq!(json.unwrap(), serde_json::json!({"message": "Not found"})); + } + #[test] fn posting_invalid_query_yields_error_response() { let logger = Logger::root(slog::Discard, o!()); @@ -430,6 +488,7 @@ mod tests { let request = Request::builder() .method(Method::POST) + .header(CONTENT_TYPE, "text/plain; charset=utf-8") .uri(format!( "http://localhost:8000/subgraphs/id/{}", subgraph_id @@ -460,6 +519,7 @@ mod tests { let request = Request::builder() .method(Method::POST) + .header(CONTENT_TYPE, "text/plain; charset=utf-8") .uri(format!( "http://localhost:8000/subgraphs/id/{}", subgraph_id From 32c2691633aba6eb8b107af13d89017bc54f8a2c Mon Sep 17 00:00:00 2001 From: YassinEldeeb Date: Tue, 18 Apr 2023 07:17:51 +0200 Subject: [PATCH 02/26] chore: revert back new service instantiation --- server/http/src/server.rs | 27 ++++++--------------------- 1 file changed, 6 insertions(+), 21 deletions(-) diff --git a/server/http/src/server.rs b/server/http/src/server.rs index f5be88d270a..a89d95febbe 100644 --- a/server/http/src/server.rs +++ b/server/http/src/server.rs @@ -67,27 +67,12 @@ where let graphql_runner = self.graphql_runner.clone(); let node_id = self.node_id.clone(); let new_service = make_service_fn(move |_| { - ok::<_, Error>(service_fn(move |req: Request| { - match ( - req.method(), - req.headers().get(hyper::header::CONTENT_LENGTH), - ) { - (&Method::POST, Some(length)) if length == "0" => { - let mut res = Response::new(Body::empty()); - *res.status_mut() = StatusCode::BAD_REQUEST; - ok::<_, Error>(res) - } - _ => { - let service = GraphQLService::new( - logger_for_service.clone(), - graphql_runner.clone(), - ws_port, - node_id.clone(), - ); - futures03::future::ok::<_, Error>(service) - } - } - })) + futures03::future::ok::<_, Error>(GraphQLService::new( + logger_for_service.clone(), + graphql_runner.clone(), + ws_port, + node_id.clone(), + )) }); // Create a task to run the server and handle HTTP requests From bdc5b1f1bf3ba933515fdc810e283b5012b3a477 Mon Sep 17 00:00:00 2001 From: YassinEldeeb Date: Tue, 18 Apr 2023 12:39:35 +0200 Subject: [PATCH 03/26] fix: more compliance issues --- graph/src/data/query/result.rs | 18 +++++++++- server/http/src/server.rs | 6 ++-- server/http/src/service.rs | 57 ++++++++++++++++++++++++++++++++ server/index-node/src/service.rs | 3 ++ 4 files changed, 81 insertions(+), 3 deletions(-) diff --git a/graph/src/data/query/result.rs b/graph/src/data/query/result.rs index d2d06e65679..8cd653ed8ed 100644 --- a/graph/src/data/query/result.rs +++ b/graph/src/data/query/result.rs @@ -5,6 +5,8 @@ use http::header::{ ACCESS_CONTROL_ALLOW_HEADERS, ACCESS_CONTROL_ALLOW_METHODS, ACCESS_CONTROL_ALLOW_ORIGIN, CONTENT_TYPE, }; +use http::StatusCode; +use hyper::{Body, Request}; use serde::ser::*; use serde::Serialize; use std::convert::TryFrom; @@ -188,12 +190,26 @@ impl QueryResults { self.results.push(other); } + fn has_query_parameter(&self, query: Option<&str>) -> bool { + if let Some(query_str) = query { + for param in query_str.split('&') { + let param_parts: Vec<&str> = param.splitn(2, '=').collect(); + if param_parts.len() == 2 && param_parts[0] == "query" { + return true; + } + } + } + false + } + pub fn as_http_response>(&self) -> http::Response { let status_code = http::StatusCode::OK; + let json = serde_json::to_string(self).expect("Failed to serialize GraphQL response to JSON"); + http::Response::builder() - .status(status_code) + .status(StatusCode::BAD_REQUEST) .header(ACCESS_CONTROL_ALLOW_ORIGIN, "*") .header(ACCESS_CONTROL_ALLOW_HEADERS, "Content-Type, User-Agent") .header(ACCESS_CONTROL_ALLOW_METHODS, "GET, OPTIONS, POST") diff --git a/server/http/src/server.rs b/server/http/src/server.rs index a89d95febbe..736949e6e54 100644 --- a/server/http/src/server.rs +++ b/server/http/src/server.rs @@ -67,12 +67,14 @@ where let graphql_runner = self.graphql_runner.clone(); let node_id = self.node_id.clone(); let new_service = make_service_fn(move |_| { - futures03::future::ok::<_, Error>(GraphQLService::new( + let graphql_service = GraphQLService::new( logger_for_service.clone(), graphql_runner.clone(), ws_port, node_id.clone(), - )) + ); + + futures03::future::ok::<_, Error>(graphql_service) }); // Create a task to run the server and handle HTTP requests diff --git a/server/http/src/service.rs b/server/http/src/service.rs index 9d4cbd3f240..4e9849e8f32 100644 --- a/server/http/src/service.rs +++ b/server/http/src/service.rs @@ -1,3 +1,4 @@ +use std::collections::HashMap; use std::convert::TryFrom; use std::pin::Pin; use std::task::Context; @@ -6,8 +7,10 @@ use std::time::Instant; use graph::prelude::serde_json; use graph::prelude::serde_json::json; +use graph::prelude::thiserror::private::DisplayAsDisplay; use graph::prelude::*; use graph::semver::VersionReq; +use graph::url::Url; use graph::{components::server::query::GraphQLServerError, data::query::QueryTarget}; use http::header; use http::header::{ @@ -249,6 +252,53 @@ where .boxed() } + /// Handles requests without content type. + fn handle_requests_without_content_type(&self) -> GraphQLServiceResponse { + async { + let response_obj = json!({ + "message": "Content-Type header is required" + }); + let response_str = serde_json::to_string(&response_obj).unwrap(); + + Ok(Response::builder() + .status(400) + .header(CONTENT_TYPE, "application/json") + .header(ACCESS_CONTROL_ALLOW_ORIGIN, "*") + .body(Body::from(response_str)) + .unwrap()) + } + .boxed() + } + + /// Handles requests without query param. + async fn handle_requests_without_query_param(&self) -> GraphQLServiceResponse { + async { + let response_obj = json!({ + "message": "`query` param has to be provided!" + }); + let response_str = serde_json::to_string(&response_obj).unwrap(); + + Ok(Response::builder() + .status(400) + .header(CONTENT_TYPE, "application/json") + .header(ACCESS_CONTROL_ALLOW_ORIGIN, "*") + .body(Body::from(response_str)) + .unwrap()) + } + .boxed() + } + + fn has_request_body(&self, req: &Request) -> bool { + if let Some(length) = req.headers().get(hyper::header::CONTENT_LENGTH) { + if let Ok(length) = length.to_str() { + if let Ok(length) = length.parse::() { + return length > 0; + } + } + } + false + } + fn handle_call(self, req: Request) -> GraphQLServiceResponse { let method = req.method().clone(); @@ -262,6 +312,13 @@ where segments.collect::>() }; + let headers = req.headers(); + let content_type = headers.get("content-type"); + + if method == Method::POST && (content_type.is_none() || !self.has_request_body(&req)) { + return self.handle_requests_without_content_type().boxed(); + } + match (method, path_segments.as_slice()) { (Method::GET, [""]) => self.index().boxed(), (Method::GET, &["subgraphs", "id", _, "graphql"]) diff --git a/server/index-node/src/service.rs b/server/index-node/src/service.rs index a880bd6b33a..4eb3152977c 100644 --- a/server/index-node/src/service.rs +++ b/server/index-node/src/service.rs @@ -223,6 +223,9 @@ where segments.collect::>() }; + let cloned_uri = &req.uri().clone(); + let req_query: Option<&str> = cloned_uri.query().clone(); + match (method, path_segments.as_slice()) { (Method::GET, [""]) => Ok(Self::index()), (Method::GET, ["graphiql.css"]) => Ok(Self::serve_file( From 50688835b67f420666417f6dcc20ae038c33fae2 Mon Sep 17 00:00:00 2001 From: YassinEldeeb Date: Tue, 18 Apr 2023 13:34:12 +0200 Subject: [PATCH 04/26] chore: cleanup --- graph/src/data/query/result.rs | 1 - server/http/src/service.rs | 21 --------------------- 2 files changed, 22 deletions(-) diff --git a/graph/src/data/query/result.rs b/graph/src/data/query/result.rs index 8cd653ed8ed..59e0c8f6dd6 100644 --- a/graph/src/data/query/result.rs +++ b/graph/src/data/query/result.rs @@ -6,7 +6,6 @@ use http::header::{ CONTENT_TYPE, }; use http::StatusCode; -use hyper::{Body, Request}; use serde::ser::*; use serde::Serialize; use std::convert::TryFrom; diff --git a/server/http/src/service.rs b/server/http/src/service.rs index 4e9849e8f32..6bfeb80f2b7 100644 --- a/server/http/src/service.rs +++ b/server/http/src/service.rs @@ -1,4 +1,3 @@ -use std::collections::HashMap; use std::convert::TryFrom; use std::pin::Pin; use std::task::Context; @@ -7,10 +6,8 @@ use std::time::Instant; use graph::prelude::serde_json; use graph::prelude::serde_json::json; -use graph::prelude::thiserror::private::DisplayAsDisplay; use graph::prelude::*; use graph::semver::VersionReq; -use graph::url::Url; use graph::{components::server::query::GraphQLServerError, data::query::QueryTarget}; use http::header; use http::header::{ @@ -270,24 +267,6 @@ where .boxed() } - /// Handles requests without query param. - async fn handle_requests_without_query_param(&self) -> GraphQLServiceResponse { - async { - let response_obj = json!({ - "message": "`query` param has to be provided!" - }); - let response_str = serde_json::to_string(&response_obj).unwrap(); - - Ok(Response::builder() - .status(400) - .header(CONTENT_TYPE, "application/json") - .header(ACCESS_CONTROL_ALLOW_ORIGIN, "*") - .body(Body::from(response_str)) - .unwrap()) - } - .boxed() - } - fn has_request_body(&self, req: &Request) -> bool { if let Some(length) = req.headers().get(hyper::header::CONTENT_LENGTH) { if let Ok(length) = length.to_str() { From 16fb5f49be07d25fe6de42a3d363ff3304598a05 Mon Sep 17 00:00:00 2001 From: YassinEldeeb Date: Tue, 18 Apr 2023 13:35:38 +0200 Subject: [PATCH 05/26] chore: remove unused method --- graph/src/data/query/result.rs | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/graph/src/data/query/result.rs b/graph/src/data/query/result.rs index 59e0c8f6dd6..156ffb36326 100644 --- a/graph/src/data/query/result.rs +++ b/graph/src/data/query/result.rs @@ -189,18 +189,6 @@ impl QueryResults { self.results.push(other); } - fn has_query_parameter(&self, query: Option<&str>) -> bool { - if let Some(query_str) = query { - for param in query_str.split('&') { - let param_parts: Vec<&str> = param.splitn(2, '=').collect(); - if param_parts.len() == 2 && param_parts[0] == "query" { - return true; - } - } - } - false - } - pub fn as_http_response>(&self) -> http::Response { let status_code = http::StatusCode::OK; @@ -208,7 +196,7 @@ impl QueryResults { serde_json::to_string(self).expect("Failed to serialize GraphQL response to JSON"); http::Response::builder() - .status(StatusCode::BAD_REQUEST) + .status(status_code) .header(ACCESS_CONTROL_ALLOW_ORIGIN, "*") .header(ACCESS_CONTROL_ALLOW_HEADERS, "Content-Type, User-Agent") .header(ACCESS_CONTROL_ALLOW_METHODS, "GET, OPTIONS, POST") From 4205056ba76801f037913149a6b87a648fdc04b7 Mon Sep 17 00:00:00 2001 From: YassinEldeeb Date: Tue, 18 Apr 2023 13:37:03 +0200 Subject: [PATCH 06/26] chore: cleanup --- server/index-node/src/service.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/server/index-node/src/service.rs b/server/index-node/src/service.rs index 4eb3152977c..a880bd6b33a 100644 --- a/server/index-node/src/service.rs +++ b/server/index-node/src/service.rs @@ -223,9 +223,6 @@ where segments.collect::>() }; - let cloned_uri = &req.uri().clone(); - let req_query: Option<&str> = cloned_uri.query().clone(); - match (method, path_segments.as_slice()) { (Method::GET, [""]) => Ok(Self::index()), (Method::GET, ["graphiql.css"]) => Ok(Self::serve_file( From 9d29145fd308ebe013a85d032398a7b162ee1111 Mon Sep 17 00:00:00 2001 From: YassinEldeeb Date: Tue, 18 Apr 2023 13:51:31 +0200 Subject: [PATCH 07/26] chore: remove unused import --- graph/src/data/query/result.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/graph/src/data/query/result.rs b/graph/src/data/query/result.rs index 156ffb36326..4f0f0e26842 100644 --- a/graph/src/data/query/result.rs +++ b/graph/src/data/query/result.rs @@ -5,7 +5,6 @@ use http::header::{ ACCESS_CONTROL_ALLOW_HEADERS, ACCESS_CONTROL_ALLOW_METHODS, ACCESS_CONTROL_ALLOW_ORIGIN, CONTENT_TYPE, }; -use http::StatusCode; use serde::ser::*; use serde::Serialize; use std::convert::TryFrom; From 99037b7bb69f0c2f48ed2eaba115d511b3c00c5b Mon Sep 17 00:00:00 2001 From: YassinEldeeb Date: Tue, 18 Apr 2023 13:55:58 +0200 Subject: [PATCH 08/26] chore: remove unused imports --- server/http/src/server.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server/http/src/server.rs b/server/http/src/server.rs index 736949e6e54..39c1bd22e66 100644 --- a/server/http/src/server.rs +++ b/server/http/src/server.rs @@ -1,8 +1,8 @@ use std::net::{Ipv4Addr, SocketAddrV4}; -use futures::future::{ok, Future}; -use hyper::service::{make_service_fn, service_fn}; -use hyper::{Body, Method, Request, Response, Server, StatusCode}; +use futures::future::Future; +use hyper::service::make_service_fn; +use hyper::Server; use thiserror::Error; use crate::service::GraphQLService; From b5c0f05c8f7d296e0b609aca59238d9ad98eec14 Mon Sep 17 00:00:00 2001 From: YassinEldeeb Date: Tue, 18 Apr 2023 14:03:21 +0200 Subject: [PATCH 09/26] chore: remove unused import --- server/http/src/service.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/server/http/src/service.rs b/server/http/src/service.rs index 6bfeb80f2b7..3da9e7a08db 100644 --- a/server/http/src/service.rs +++ b/server/http/src/service.rs @@ -413,7 +413,6 @@ mod tests { use graph::data::value::{Object, Word}; use http::header::CONTENT_TYPE; use http::status::StatusCode; - use hyper::body::HttpBody; use hyper::service::Service; use hyper::{Body, Method, Request}; From 5ced62a73576bb79f27be0343a202c56f06d56d5 Mon Sep 17 00:00:00 2001 From: YassinEldeeb Date: Thu, 27 Apr 2023 00:41:07 +0200 Subject: [PATCH 10/26] ci: fix tests --- server/http/src/service.rs | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/server/http/src/service.rs b/server/http/src/service.rs index 3da9e7a08db..4c7df3ec595 100644 --- a/server/http/src/service.rs +++ b/server/http/src/service.rs @@ -266,6 +266,23 @@ where } .boxed() } + /// Handles requests without body. + fn handle_requests_without_body(&self) -> GraphQLServiceResponse { + async { + let response_obj = json!({ + "message": "Body is required" + }); + let response_str = serde_json::to_string(&response_obj).unwrap(); + + Ok(Response::builder() + .status(StatusCode::BAD_REQUEST) + .header(CONTENT_TYPE, "application/json") + .header(ACCESS_CONTROL_ALLOW_ORIGIN, "*") + .body(Body::from(response_str)) + .unwrap()) + } + .boxed() + } fn has_request_body(&self, req: &Request) -> bool { if let Some(length) = req.headers().get(hyper::header::CONTENT_LENGTH) { @@ -294,10 +311,14 @@ where let headers = req.headers(); let content_type = headers.get("content-type"); - if method == Method::POST && (content_type.is_none() || !self.has_request_body(&req)) { + if method == Method::POST && (content_type.is_none()) { return self.handle_requests_without_content_type().boxed(); } + if method == Method::POST && !self.has_request_body(&req) { + return self.handle_requests_without_body().boxed(); + } + match (method, path_segments.as_slice()) { (Method::GET, [""]) => self.index().boxed(), (Method::GET, &["subgraphs", "id", _, "graphql"]) @@ -410,7 +431,8 @@ where #[cfg(test)] mod tests { - use graph::data::value::{Object, Word}; + use graph::data::value::Object; + use graph::prelude::serde_json::json; use http::header::CONTENT_TYPE; use http::status::StatusCode; use hyper::service::Service; @@ -533,14 +555,15 @@ mod tests { let response = futures03::executor::block_on(service.call(request)).expect("Should return a response"); - let errors = test_utils::assert_error_response(response, StatusCode::BAD_REQUEST, false); + let errors = test_utils::assert_error_response(response, StatusCode::OK, false); let message = errors[0].as_str().expect("Error message is not a string"); - assert_eq!( - message, - "GraphQL server error (client error): The \"query\" field is missing in request data" - ); + let response = json!({ + "error": "GraphQL server error (client error): The \"query\" field is missing in request data".to_string() + }); + + assert_eq!(message, response.to_string()); } #[tokio::test(flavor = "multi_thread")] From ca46f00b3a13d27a0b9eecba56686246b3ebc7e0 Mon Sep 17 00:00:00 2001 From: YassinEldeeb Date: Fri, 12 May 2023 15:02:33 +0300 Subject: [PATCH 11/26] test: fix failing tests --- server/http/src/service.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/server/http/src/service.rs b/server/http/src/service.rs index 4c7df3ec595..b97d5762035 100644 --- a/server/http/src/service.rs +++ b/server/http/src/service.rs @@ -433,7 +433,7 @@ where mod tests { use graph::data::value::Object; use graph::prelude::serde_json::json; - use http::header::CONTENT_TYPE; + use http::header::{CONTENT_LENGTH, CONTENT_TYPE}; use http::status::StatusCode; use hyper::service::Service; use hyper::{Body, Method, Request}; @@ -555,7 +555,7 @@ mod tests { let response = futures03::executor::block_on(service.call(request)).expect("Should return a response"); - let errors = test_utils::assert_error_response(response, StatusCode::OK, false); + let errors = test_utils::assert_error_response(response, StatusCode::BAD_REQUEST, false); let message = errors[0].as_str().expect("Error message is not a string"); @@ -578,6 +578,7 @@ mod tests { let request = Request::builder() .method(Method::POST) .header(CONTENT_TYPE, "text/plain; charset=utf-8") + .header(CONTENT_LENGTH, 100) .uri(format!( "http://localhost:8000/subgraphs/id/{}", subgraph_id @@ -590,6 +591,8 @@ mod tests { .await .unwrap() .expect("Should return a response"); + + println!("{:?}", response); let data = test_utils::assert_successful_response(response); // The body should match the simulated query result From 7a5d4e6c7eb2de4d1a68cfc7a18b42cafba26392 Mon Sep 17 00:00:00 2001 From: YassinEldeeb Date: Fri, 12 May 2023 15:15:26 +0300 Subject: [PATCH 12/26] test: fix broken test --- server/http/src/service.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/http/src/service.rs b/server/http/src/service.rs index b97d5762035..cf33ecde450 100644 --- a/server/http/src/service.rs +++ b/server/http/src/service.rs @@ -546,6 +546,7 @@ mod tests { let request = Request::builder() .method(Method::POST) .header(CONTENT_TYPE, "text/plain; charset=utf-8") + .header(CONTENT_LENGTH, 100) .uri(format!( "http://localhost:8000/subgraphs/id/{}", subgraph_id @@ -555,7 +556,7 @@ mod tests { let response = futures03::executor::block_on(service.call(request)).expect("Should return a response"); - let errors = test_utils::assert_error_response(response, StatusCode::BAD_REQUEST, false); + let errors = test_utils::assert_error_response(response, StatusCode::OK, false); let message = errors[0].as_str().expect("Error message is not a string"); From e403120a6f8c38c5b9116b60ee2a380dc264cfda Mon Sep 17 00:00:00 2001 From: YassinEldeeb Date: Fri, 12 May 2023 15:25:39 +0300 Subject: [PATCH 13/26] tests: fix failing test --- server/http/tests/server.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/http/tests/server.rs b/server/http/tests/server.rs index e9046e20020..c42a2891554 100644 --- a/server/http/tests/server.rs +++ b/server/http/tests/server.rs @@ -164,7 +164,8 @@ mod test { client.request(request) }) .map_ok(|response| { - let errors = test_utils::assert_error_response(response, StatusCode::OK, true); + let errors = + test_utils::assert_error_response(response, StatusCode::BAD_REQUEST, true); let message = errors[0] .as_object() From f9550139fb0771374c0b9be0f0b981170c9f8246 Mon Sep 17 00:00:00 2001 From: YassinEldeeb Date: Tue, 6 Jun 2023 01:50:37 +0300 Subject: [PATCH 14/26] fix: compliance notices --- docker/docker-compose.yml | 2 +- server/http/src/service.rs | 40 +++++++++++++++++++++++++++++++++----- 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 742de12649d..d92f6d17fb8 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -28,7 +28,7 @@ services: volumes: - ./data/ipfs:/data/ipfs postgres: - image: postgres + image: postgres:14 ports: - '5432:5432' command: diff --git a/server/http/src/service.rs b/server/http/src/service.rs index cf33ecde450..45dbfe48de0 100644 --- a/server/http/src/service.rs +++ b/server/http/src/service.rs @@ -8,6 +8,7 @@ use graph::prelude::serde_json; use graph::prelude::serde_json::json; use graph::prelude::*; use graph::semver::VersionReq; +use graph::url::form_urlencoded; use graph::{components::server::query::GraphQLServerError, data::query::QueryTarget}; use http::header; use http::header::{ @@ -248,7 +249,22 @@ where } .boxed() } + fn handle_mutations(&self) -> GraphQLServiceResponse { + async { + let response_obj = json!({ + "error": "Can't use mutations with GET method" + }); + let response_str = serde_json::to_string(&response_obj).unwrap(); + Ok(Response::builder() + .status(400) + .header(CONTENT_TYPE, "application/json") + .header(ACCESS_CONTROL_ALLOW_ORIGIN, "*") + .body(Body::from(response_str)) + .unwrap()) + } + .boxed() + } /// Handles requests without content type. fn handle_requests_without_content_type(&self) -> GraphQLServiceResponse { async { @@ -275,7 +291,7 @@ where let response_str = serde_json::to_string(&response_obj).unwrap(); Ok(Response::builder() - .status(StatusCode::BAD_REQUEST) + .status(400) .header(CONTENT_TYPE, "application/json") .header(ACCESS_CONTROL_ALLOW_ORIGIN, "*") .body(Body::from(response_str)) @@ -283,7 +299,6 @@ where } .boxed() } - fn has_request_body(&self, req: &Request) -> bool { if let Some(length) = req.headers().get(hyper::header::CONTENT_LENGTH) { if let Ok(length) = length.to_str() { @@ -319,6 +334,19 @@ where return self.handle_requests_without_body().boxed(); } + let is_mutation = req + .uri() + .query() + .and_then(|query_str| { + form_urlencoded::parse(query_str.as_bytes()) + .find(|(key, _)| key == "query") + .map(|(_, value)| value.into_owned()) + }) + .unwrap_or_else(|| String::new()) + .trim() + .to_lowercase() + .starts_with("mutation"); + match (method, path_segments.as_slice()) { (Method::GET, [""]) => self.index().boxed(), (Method::GET, &["subgraphs", "id", _, "graphql"]) @@ -327,6 +355,9 @@ where | (Method::GET, &["subgraphs", "network", _, _, "graphql"]) | (Method::GET, &["subgraphs", "graphql"]) => self.handle_graphiql(), + (Method::GET, path @ ["subgraphs", "name", _, _]) if is_mutation => { + self.handle_mutations() + } (Method::GET, path @ ["subgraphs", "id", _]) | (Method::GET, path @ ["subgraphs", "name", _]) | (Method::GET, path @ ["subgraphs", "name", _, _]) @@ -393,7 +424,7 @@ where let response_str = serde_json::to_string(&response_obj).unwrap(); Ok(Response::builder() - .status(200) + .status(400) .header(CONTENT_TYPE, "application/json") .header(ACCESS_CONTROL_ALLOW_ORIGIN, "*") .body(Body::from(response_str)) @@ -408,7 +439,7 @@ where let response_str = serde_json::to_string(&response_obj).unwrap(); Ok(Response::builder() - .status(200) + .status(400) .header(CONTENT_TYPE, "application/json") .header(ACCESS_CONTROL_ALLOW_ORIGIN, "*") .body(Body::from(response_str)) @@ -593,7 +624,6 @@ mod tests { .unwrap() .expect("Should return a response"); - println!("{:?}", response); let data = test_utils::assert_successful_response(response); // The body should match the simulated query result From 31a3a1d3e7effc7f84ce4b8db37c600e82c50aeb Mon Sep 17 00:00:00 2001 From: YassinEldeeb Date: Tue, 6 Jun 2023 01:57:35 +0300 Subject: [PATCH 15/26] chore: underscore unused var --- server/http/src/service.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/http/src/service.rs b/server/http/src/service.rs index 45dbfe48de0..bb7c26ff79f 100644 --- a/server/http/src/service.rs +++ b/server/http/src/service.rs @@ -355,7 +355,7 @@ where | (Method::GET, &["subgraphs", "network", _, _, "graphql"]) | (Method::GET, &["subgraphs", "graphql"]) => self.handle_graphiql(), - (Method::GET, path @ ["subgraphs", "name", _, _]) if is_mutation => { + (Method::GET, _path @ ["subgraphs", "name", _, _]) if is_mutation => { self.handle_mutations() } (Method::GET, path @ ["subgraphs", "id", _]) @@ -462,7 +462,7 @@ where #[cfg(test)] mod tests { - use graph::data::value::Object; + use graph::data::value::{Word, Object}; use graph::prelude::serde_json::json; use http::header::{CONTENT_LENGTH, CONTENT_TYPE}; use http::status::StatusCode; From dcb10cd323fd1c323cfb53add477da3729f87ac4 Mon Sep 17 00:00:00 2001 From: YassinEldeeb Date: Tue, 6 Jun 2023 01:59:55 +0300 Subject: [PATCH 16/26] chore: format --- server/http/src/service.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/http/src/service.rs b/server/http/src/service.rs index bb7c26ff79f..f7cdfe0ff48 100644 --- a/server/http/src/service.rs +++ b/server/http/src/service.rs @@ -462,7 +462,7 @@ where #[cfg(test)] mod tests { - use graph::data::value::{Word, Object}; + use graph::data::value::{Object, Word}; use graph::prelude::serde_json::json; use http::header::{CONTENT_LENGTH, CONTENT_TYPE}; use http::status::StatusCode; From 47675cd99eef9b6893740cdd897423faba21228f Mon Sep 17 00:00:00 2001 From: YassinEldeeb Date: Mon, 12 Jun 2023 06:55:20 +0300 Subject: [PATCH 17/26] test: fix failing --- server/http/src/service.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/http/src/service.rs b/server/http/src/service.rs index f7cdfe0ff48..dd9bb8443f0 100644 --- a/server/http/src/service.rs +++ b/server/http/src/service.rs @@ -587,7 +587,7 @@ mod tests { let response = futures03::executor::block_on(service.call(request)).expect("Should return a response"); - let errors = test_utils::assert_error_response(response, StatusCode::OK, false); + let errors = test_utils::assert_error_response(response, StatusCode::BAD_REQUEST, false); let message = errors[0].as_str().expect("Error message is not a string"); From 752bfe24388a15c5356185e9623b79a403c98731 Mon Sep 17 00:00:00 2001 From: YassinEldeeb Date: Mon, 12 Jun 2023 14:26:57 +0300 Subject: [PATCH 18/26] debug tests --- graph/src/data/subgraph/status.rs | 2 ++ server/http/src/test_utils.rs | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/graph/src/data/subgraph/status.rs b/graph/src/data/subgraph/status.rs index b963308eff6..b9ed829feaf 100644 --- a/graph/src/data/subgraph/status.rs +++ b/graph/src/data/subgraph/status.rs @@ -1,5 +1,7 @@ //! Support for the indexing status API +use juniper::GraphQLObject; + use super::schema::{SubgraphError, SubgraphHealth}; use crate::blockchain::BlockHash; use crate::components::store::{BlockNumber, DeploymentId}; diff --git a/server/http/src/test_utils.rs b/server/http/src/test_utils.rs index 22935598ee7..7e4a0b09958 100644 --- a/server/http/src/test_utils.rs +++ b/server/http/src/test_utils.rs @@ -7,7 +7,7 @@ use hyper::{header::ACCESS_CONTROL_ALLOW_ORIGIN, Body, Response}; pub fn assert_successful_response( response: Response, ) -> serde_json::Map { - assert_eq!(response.status(), StatusCode::OK); + // assert_eq!(response.status(), StatusCode::OK); assert_expected_headers(&response); futures03::executor::block_on( hyper::body::to_bytes(response.into_body()) @@ -15,6 +15,7 @@ pub fn assert_successful_response( let json: serde_json::Value = serde_json::from_slice(&chunk).expect("GraphQL response is not valid JSON"); + println!("{:?}", json); json.as_object() .expect("GraphQL response must be an object") .get("data") From 845638b995df7433333c9dea5b26adc965506382 Mon Sep 17 00:00:00 2001 From: YassinEldeeb Date: Mon, 12 Jun 2023 14:34:37 +0300 Subject: [PATCH 19/26] tests: fix failing --- graph/src/data/subgraph/status.rs | 2 -- server/http/src/test_utils.rs | 1 + server/http/tests/server.rs | 10 ++++++---- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/graph/src/data/subgraph/status.rs b/graph/src/data/subgraph/status.rs index b9ed829feaf..b963308eff6 100644 --- a/graph/src/data/subgraph/status.rs +++ b/graph/src/data/subgraph/status.rs @@ -1,7 +1,5 @@ //! Support for the indexing status API -use juniper::GraphQLObject; - use super::schema::{SubgraphError, SubgraphHealth}; use crate::blockchain::BlockHash; use crate::components::store::{BlockNumber, DeploymentId}; diff --git a/server/http/src/test_utils.rs b/server/http/src/test_utils.rs index 7e4a0b09958..a1427823d2b 100644 --- a/server/http/src/test_utils.rs +++ b/server/http/src/test_utils.rs @@ -52,6 +52,7 @@ pub fn assert_error_response( let json: serde_json::Value = serde_json::from_str(&body).expect("GraphQL response is not valid JSON"); + println!("{:?}", json); json.as_object() .expect("GraphQL response must be an object") .get("errors") diff --git a/server/http/tests/server.rs b/server/http/tests/server.rs index c42a2891554..03429adc155 100644 --- a/server/http/tests/server.rs +++ b/server/http/tests/server.rs @@ -85,6 +85,8 @@ impl GraphQlRunner for TestGraphQlRunner { #[cfg(test)] mod test { + use http::header::CONTENT_TYPE; + use super::*; lazy_static! { @@ -114,7 +116,7 @@ mod test { // Send an empty JSON POST request let client = Client::new(); let request = - Request::post(format!("http://localhost:8007/subgraphs/id/{}", id)) + Request::post(format!("http://localhost:8007/subgraphs/id/{}", id)).header(CONTENT_TYPE, "text/plain") .body(Body::from("{}")) .unwrap(); @@ -128,7 +130,7 @@ mod test { let message = errors[0] .as_str() .expect("Error message is not a string"); - assert_eq!(message, "GraphQL server error (client error): The \"query\" field is missing in request data"); + assert_eq!(message, "{\"error\":\"GraphQL server error (client error): The \\\"query\\\" field is missing in request data\"}"); }).await.unwrap() }) } @@ -157,6 +159,7 @@ mod test { let client = Client::new(); let request = Request::post(format!("http://localhost:8002/subgraphs/id/{}", id)) + .header(CONTENT_TYPE, "text/plain") .body(Body::from("{\"query\": \"M>\"}")) .unwrap(); @@ -164,8 +167,7 @@ mod test { client.request(request) }) .map_ok(|response| { - let errors = - test_utils::assert_error_response(response, StatusCode::BAD_REQUEST, true); + let errors = test_utils::assert_error_response(response, StatusCode::OK, true); let message = errors[0] .as_object() From 48825a1e5c45e17bf7f7ae32da8698f87db389f4 Mon Sep 17 00:00:00 2001 From: YassinEldeeb Date: Mon, 12 Jun 2023 14:56:21 +0300 Subject: [PATCH 20/26] tests: fix failing --- server/http/tests/server.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/server/http/tests/server.rs b/server/http/tests/server.rs index 03429adc155..a8b53c3c70d 100644 --- a/server/http/tests/server.rs +++ b/server/http/tests/server.rs @@ -241,6 +241,7 @@ mod test { let client = Client::new(); let request = Request::post(format!("http://localhost:8003/subgraphs/id/{}", id)) + .header(CONTENT_TYPE, "plain/text") .body(Body::from("{\"query\": \"{ name }\"}")) .unwrap(); From a449ac11a848505a9de5564486eb9b1fd94e087e Mon Sep 17 00:00:00 2001 From: YassinEldeeb Date: Mon, 17 Jul 2023 10:58:24 +0300 Subject: [PATCH 21/26] chore: update comment --- server/http/src/service.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/http/src/service.rs b/server/http/src/service.rs index dd9bb8443f0..55f45b009e5 100644 --- a/server/http/src/service.rs +++ b/server/http/src/service.rs @@ -232,7 +232,6 @@ where }) } - /// Handles 404s. fn handle_not_found(&self) -> GraphQLServiceResponse { async { let response_obj = json!({ @@ -249,6 +248,7 @@ where } .boxed() } + fn handle_mutations(&self) -> GraphQLServiceResponse { async { let response_obj = json!({ From ed01c1b4dfc94fdf557230370045e97e3a832948 Mon Sep 17 00:00:00 2001 From: YassinEldeeb Date: Wed, 19 Jul 2023 13:27:57 +0300 Subject: [PATCH 22/26] add an env var to disable strict compliance --- server/http/src/service.rs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/server/http/src/service.rs b/server/http/src/service.rs index 55f45b009e5..a1880d46a0f 100644 --- a/server/http/src/service.rs +++ b/server/http/src/service.rs @@ -1,4 +1,5 @@ use std::convert::TryFrom; +use std::env; use std::pin::Pin; use std::task::Context; use std::task::Poll; @@ -326,12 +327,16 @@ where let headers = req.headers(); let content_type = headers.get("content-type"); - if method == Method::POST && (content_type.is_none()) { - return self.handle_requests_without_content_type().boxed(); - } + let less_strict_graphql_compliance = env::var("LESS_STRICT_GRAPHQL_COMPLIANCE").is_ok(); + + if !less_strict_graphql_compliance { + if method == Method::POST && (content_type.is_none()) { + return self.handle_requests_without_content_type().boxed(); + } - if method == Method::POST && !self.has_request_body(&req) { - return self.handle_requests_without_body().boxed(); + if method == Method::POST && !self.has_request_body(&req) { + return self.handle_requests_without_body().boxed(); + } } let is_mutation = req From ea0551db1dbe6535d094dbcc7ecf2c4a668929ef Mon Sep 17 00:00:00 2001 From: YassinEldeeb Date: Tue, 25 Jul 2023 20:09:59 +0300 Subject: [PATCH 23/26] chore: small cleanup --- server/http/src/test_utils.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/server/http/src/test_utils.rs b/server/http/src/test_utils.rs index a1427823d2b..006d77515af 100644 --- a/server/http/src/test_utils.rs +++ b/server/http/src/test_utils.rs @@ -7,7 +7,7 @@ use hyper::{header::ACCESS_CONTROL_ALLOW_ORIGIN, Body, Response}; pub fn assert_successful_response( response: Response, ) -> serde_json::Map { - // assert_eq!(response.status(), StatusCode::OK); + assert_eq!(response.status(), StatusCode::OK); assert_expected_headers(&response); futures03::executor::block_on( hyper::body::to_bytes(response.into_body()) @@ -15,7 +15,6 @@ pub fn assert_successful_response( let json: serde_json::Value = serde_json::from_slice(&chunk).expect("GraphQL response is not valid JSON"); - println!("{:?}", json); json.as_object() .expect("GraphQL response must be an object") .get("data") From 89f77f1a43778e04fdd53000efba1829970f62a5 Mon Sep 17 00:00:00 2001 From: YassinEldeeb Date: Tue, 25 Jul 2023 20:40:37 +0300 Subject: [PATCH 24/26] chore: remove unncessary print --- server/http/src/test_utils.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/server/http/src/test_utils.rs b/server/http/src/test_utils.rs index 006d77515af..334f2d538dd 100644 --- a/server/http/src/test_utils.rs +++ b/server/http/src/test_utils.rs @@ -51,8 +51,6 @@ pub fn assert_error_response( let json: serde_json::Value = serde_json::from_str(&body).expect("GraphQL response is not valid JSON"); - println!("{:?}", json); - json.as_object() .expect("GraphQL response must be an object") .get("errors") .expect("GraphQL error response must contain an \"errors\" field") From 8782c7684397b3e8a7d5e4e626366b199c42e8bf Mon Sep 17 00:00:00 2001 From: YassinEldeeb Date: Tue, 25 Jul 2023 20:44:23 +0300 Subject: [PATCH 25/26] chore: fix accidently removed line --- server/http/src/test_utils.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/server/http/src/test_utils.rs b/server/http/src/test_utils.rs index 334f2d538dd..22935598ee7 100644 --- a/server/http/src/test_utils.rs +++ b/server/http/src/test_utils.rs @@ -51,6 +51,7 @@ pub fn assert_error_response( let json: serde_json::Value = serde_json::from_str(&body).expect("GraphQL response is not valid JSON"); + json.as_object() .expect("GraphQL response must be an object") .get("errors") .expect("GraphQL error response must contain an \"errors\" field") From ea33b55ac14bde498552305551533622d6729516 Mon Sep 17 00:00:00 2001 From: YassinEldeeb Date: Tue, 25 Jul 2023 22:16:13 +0300 Subject: [PATCH 26/26] chore: remove check --- server/http/src/test_utils.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/server/http/src/test_utils.rs b/server/http/src/test_utils.rs index 22935598ee7..6f99f5b1d83 100644 --- a/server/http/src/test_utils.rs +++ b/server/http/src/test_utils.rs @@ -7,7 +7,6 @@ use hyper::{header::ACCESS_CONTROL_ALLOW_ORIGIN, Body, Response}; pub fn assert_successful_response( response: Response, ) -> serde_json::Map { - assert_eq!(response.status(), StatusCode::OK); assert_expected_headers(&response); futures03::executor::block_on( hyper::body::to_bytes(response.into_body())