Upgrade juniper_hyper to 1.0 hyper (#1217)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Kai Ren <tyranron@gmail.com>
This commit is contained in:
dependabot[bot] 2023-11-17 13:04:37 +00:00 committed by GitHub
parent 9f776fd0a1
commit 813c0d1210
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 137 additions and 105 deletions

View file

@ -11,10 +11,12 @@ All user visible changes to `juniper_hyper` crate will be documented in this fil
### BC Breaks ### BC Breaks
- Switched to 0.16 version of [`juniper` crate]. - Switched to 0.16 version of [`juniper` crate].
- Switched to 1 version of [`hyper` crate]. ([#1217])
- Changed return type of all functions from `Response<Body>` to `Response<String>`. ([#1101], [#1096]) - Changed return type of all functions from `Response<Body>` to `Response<String>`. ([#1101], [#1096])
[#1096]: /../../issues/1096 [#1096]: /../../issues/1096
[#1101]: /../../pull/1101 [#1101]: /../../pull/1101
[#1217]: /../../pull/1217
@ -27,4 +29,5 @@ See [old CHANGELOG](/../../blob/juniper_hyper-v0.8.0/juniper_hyper/CHANGELOG.md)
[`juniper` crate]: https://docs.rs/juniper [`juniper` crate]: https://docs.rs/juniper
[`hyper` crate]: https://docs.rs/hyper
[Semantic Versioning 2.0.0]: https://semver.org [Semantic Versioning 2.0.0]: https://semver.org

View file

@ -16,18 +16,18 @@ exclude = ["/examples/", "/release.toml"]
[dependencies] [dependencies]
futures = "0.3.22" futures = "0.3.22"
hyper = { version = "0.14.7", features = ["server", "runtime"] } http-body-util = "0.1"
hyper = { version = "1.0", features = ["server"] }
juniper = { version = "0.16.0-dev", path = "../juniper", default-features = false } juniper = { version = "0.16.0-dev", path = "../juniper", default-features = false }
serde_json = "1.0.18" serde_json = "1.0.18"
tokio = "1.0" tokio = "1.0"
url = "2.0" url = "2.0"
# Fixes for `minimal-versions` check.
# TODO: Try remove on upgrade of `hyper` crate.
http-body = "0.4.5"
[dev-dependencies] [dev-dependencies]
hyper = { version = "1.0", features = ["http1"] }
hyper-util = { version = "0.1", features = ["tokio"] }
juniper = { version = "0.16.0-dev", path = "../juniper", features = ["expose-test-schema"] } juniper = { version = "0.16.0-dev", path = "../juniper", features = ["expose-test-schema"] }
log = "0.4"
pretty_env_logger = "0.5" pretty_env_logger = "0.5"
reqwest = { version = "0.11", features = ["blocking", "rustls-tls"], default-features = false } reqwest = { version = "0.11", features = ["blocking", "rustls-tls"], default-features = false }
tokio = { version = "1.0", features = ["macros", "rt-multi-thread"] } tokio = { version = "1.0", features = ["macros", "net", "rt-multi-thread"] }

View file

@ -1,21 +1,19 @@
use std::{convert::Infallible, sync::Arc}; use std::{convert::Infallible, env, error::Error, net::SocketAddr, sync::Arc};
use hyper::{ use hyper::{server::conn::http1, service::service_fn, Method, Response, StatusCode};
server::Server, use hyper_util::rt::TokioIo;
service::{make_service_fn, service_fn},
Method, Response, StatusCode,
};
use juniper::{ use juniper::{
tests::fixtures::starwars::schema::{Database, Query}, tests::fixtures::starwars::schema::{Database, Query},
EmptyMutation, EmptySubscription, RootNode, EmptyMutation, EmptySubscription, RootNode,
}; };
use juniper_hyper::{graphiql, graphql, playground};
use tokio::net::TcpListener;
#[tokio::main] #[tokio::main]
async fn main() { async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
env::set_var("RUST_LOG", "info");
pretty_env_logger::init(); pretty_env_logger::init();
let addr = ([127, 0, 0, 1], 3000).into();
let db = Arc::new(Database::new()); let db = Arc::new(Database::new());
let root_node = Arc::new(RootNode::new( let root_node = Arc::new(RootNode::new(
Query, Query,
@ -23,35 +21,46 @@ async fn main() {
EmptySubscription::<Database>::new(), EmptySubscription::<Database>::new(),
)); ));
let new_service = make_service_fn(move |_| { let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
let root_node = root_node.clone(); let listener = TcpListener::bind(addr).await?;
let ctx = db.clone(); log::info!("Listening on http://{addr}");
loop {
let (stream, _) = listener.accept().await?;
let io = TokioIo::new(stream);
async {
Ok::<_, hyper::Error>(service_fn(move |req| {
let root_node = root_node.clone(); let root_node = root_node.clone();
let ctx = ctx.clone(); let db = db.clone();
tokio::spawn(async move {
let root_node = root_node.clone();
let db = db.clone();
if let Err(e) = http1::Builder::new()
.serve_connection(
io,
service_fn(move |req| {
let root_node = root_node.clone();
let db = db.clone();
async { async {
Ok::<_, Infallible>(match (req.method(), req.uri().path()) { Ok::<_, Infallible>(match (req.method(), req.uri().path()) {
(&Method::GET, "/") => juniper_hyper::graphiql("/graphql", None).await,
(&Method::GET, "/graphql") | (&Method::POST, "/graphql") => { (&Method::GET, "/graphql") | (&Method::POST, "/graphql") => {
juniper_hyper::graphql(root_node, ctx, req).await graphql(root_node, db, req).await
} }
(&Method::GET, "/graphiql") => graphiql("/graphql", None).await,
(&Method::GET, "/playground") => playground("/graphql", None).await,
_ => { _ => {
let mut response = Response::new(String::new()); let mut resp = Response::new(String::new());
*response.status_mut() = StatusCode::NOT_FOUND; *resp.status_mut() = StatusCode::NOT_FOUND;
response resp
} }
}) })
} }
})) }),
)
.await
{
log::error!("Error serving connection: {e}");
} }
}); });
let server = Server::bind(&addr).serve(new_service);
println!("Listening on http://{addr}");
if let Err(e) = server.await {
eprintln!("server error: {e}")
} }
} }

View file

@ -2,9 +2,11 @@
use std::{error::Error, fmt, string::FromUtf8Error, sync::Arc}; use std::{error::Error, fmt, string::FromUtf8Error, sync::Arc};
use http_body_util::BodyExt as _;
use hyper::{ use hyper::{
body,
header::{self, HeaderValue}, header::{self, HeaderValue},
Body, Method, Request, Response, StatusCode, Method, Request, Response, StatusCode,
}; };
use juniper::{ use juniper::{
http::{GraphQLBatchRequest, GraphQLRequest as JuniperGraphQLRequest, GraphQLRequest}, http::{GraphQLBatchRequest, GraphQLRequest as JuniperGraphQLRequest, GraphQLRequest},
@ -16,7 +18,7 @@ use url::form_urlencoded;
pub async fn graphql_sync<CtxT, QueryT, MutationT, SubscriptionT, S>( pub async fn graphql_sync<CtxT, QueryT, MutationT, SubscriptionT, S>(
root_node: Arc<RootNode<'static, QueryT, MutationT, SubscriptionT, S>>, root_node: Arc<RootNode<'static, QueryT, MutationT, SubscriptionT, S>>,
context: Arc<CtxT>, context: Arc<CtxT>,
req: Request<Body>, req: Request<body::Incoming>,
) -> Response<String> ) -> Response<String>
where where
QueryT: GraphQLType<S, Context = CtxT>, QueryT: GraphQLType<S, Context = CtxT>,
@ -37,7 +39,7 @@ where
pub async fn graphql<CtxT, QueryT, MutationT, SubscriptionT, S>( pub async fn graphql<CtxT, QueryT, MutationT, SubscriptionT, S>(
root_node: Arc<RootNode<'static, QueryT, MutationT, SubscriptionT, S>>, root_node: Arc<RootNode<'static, QueryT, MutationT, SubscriptionT, S>>,
context: Arc<CtxT>, context: Arc<CtxT>,
req: Request<Body>, req: Request<body::Incoming>,
) -> Response<String> ) -> Response<String>
where where
QueryT: GraphQLTypeAsync<S, Context = CtxT>, QueryT: GraphQLTypeAsync<S, Context = CtxT>,
@ -56,7 +58,7 @@ where
} }
async fn parse_req<S: ScalarValue>( async fn parse_req<S: ScalarValue>(
req: Request<Body>, req: Request<body::Incoming>,
) -> Result<GraphQLBatchRequest<S>, Response<String>> { ) -> Result<GraphQLBatchRequest<S>, Response<String>> {
match *req.method() { match *req.method() {
Method::GET => parse_get_req(req), Method::GET => parse_get_req(req),
@ -77,7 +79,7 @@ async fn parse_req<S: ScalarValue>(
} }
fn parse_get_req<S: ScalarValue>( fn parse_get_req<S: ScalarValue>(
req: Request<Body>, req: Request<body::Incoming>,
) -> Result<GraphQLBatchRequest<S>, GraphQLRequestError> { ) -> Result<GraphQLBatchRequest<S>, GraphQLRequestError> {
req.uri() req.uri()
.query() .query()
@ -90,13 +92,14 @@ fn parse_get_req<S: ScalarValue>(
} }
async fn parse_post_json_req<S: ScalarValue>( async fn parse_post_json_req<S: ScalarValue>(
body: Body, body: body::Incoming,
) -> Result<GraphQLBatchRequest<S>, GraphQLRequestError> { ) -> Result<GraphQLBatchRequest<S>, GraphQLRequestError> {
let chunk = hyper::body::to_bytes(body) let chunk = body
.collect()
.await .await
.map_err(GraphQLRequestError::BodyHyper)?; .map_err(GraphQLRequestError::BodyHyper)?;
let input = String::from_utf8(chunk.iter().cloned().collect()) let input = String::from_utf8(chunk.to_bytes().iter().cloned().collect())
.map_err(GraphQLRequestError::BodyUtf8)?; .map_err(GraphQLRequestError::BodyUtf8)?;
serde_json::from_str::<GraphQLBatchRequest<S>>(&input) serde_json::from_str::<GraphQLBatchRequest<S>>(&input)
@ -104,13 +107,14 @@ async fn parse_post_json_req<S: ScalarValue>(
} }
async fn parse_post_graphql_req<S: ScalarValue>( async fn parse_post_graphql_req<S: ScalarValue>(
body: Body, body: body::Incoming,
) -> Result<GraphQLBatchRequest<S>, GraphQLRequestError> { ) -> Result<GraphQLBatchRequest<S>, GraphQLRequestError> {
let chunk = hyper::body::to_bytes(body) let chunk = body
.collect()
.await .await
.map_err(GraphQLRequestError::BodyHyper)?; .map_err(GraphQLRequestError::BodyHyper)?;
let query = String::from_utf8(chunk.iter().cloned().collect()) let query = String::from_utf8(chunk.to_bytes().iter().cloned().collect())
.map_err(GraphQLRequestError::BodyUtf8)?; .map_err(GraphQLRequestError::BodyUtf8)?;
Ok(GraphQLBatchRequest::Single(GraphQLRequest::new( Ok(GraphQLBatchRequest::Single(GraphQLRequest::new(
@ -306,18 +310,19 @@ impl Error for GraphQLRequestError {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use hyper::{ use std::{
server::Server, convert::Infallible, error::Error, net::SocketAddr, panic, sync::Arc, time::Duration,
service::{make_service_fn, service_fn},
Method, Response, StatusCode,
}; };
use hyper::{server::conn::http1, service::service_fn, Method, Response, StatusCode};
use hyper_util::rt::TokioIo;
use juniper::{ use juniper::{
http::tests as http_tests, http::tests as http_tests,
tests::fixtures::starwars::schema::{Database, Query}, tests::fixtures::starwars::schema::{Database, Query},
EmptyMutation, EmptySubscription, RootNode, EmptyMutation, EmptySubscription, RootNode,
}; };
use reqwest::{self, blocking::Response as ReqwestResponse}; use reqwest::blocking::Response as ReqwestResponse;
use std::{convert::Infallible, net::SocketAddr, sync::Arc, thread, time::Duration}; use tokio::{net::TcpListener, task, time::sleep};
struct TestHyperIntegration { struct TestHyperIntegration {
port: u16, port: u16,
@ -373,7 +378,7 @@ mod tests {
async fn run_hyper_integration(is_sync: bool) { async fn run_hyper_integration(is_sync: bool) {
let port = if is_sync { 3002 } else { 3001 }; let port = if is_sync { 3002 } else { 3001 };
let addr: SocketAddr = ([127, 0, 0, 1], port).into(); let addr = SocketAddr::from(([127, 0, 0, 1], port));
let db = Arc::new(Database::new()); let db = Arc::new(Database::new());
let root_node = Arc::new(RootNode::new( let root_node = Arc::new(RootNode::new(
@ -382,14 +387,27 @@ mod tests {
EmptySubscription::<Database>::new(), EmptySubscription::<Database>::new(),
)); ));
let new_service = make_service_fn(move |_| { let server: task::JoinHandle<Result<(), Box<dyn Error + Send + Sync>>> =
let root_node = root_node.clone(); task::spawn(async move {
let ctx = db.clone(); let listener = TcpListener::bind(addr).await?;
loop {
let (stream, _) = listener.accept().await?;
let io = TokioIo::new(stream);
async move {
Ok::<_, hyper::Error>(service_fn(move |req| {
let root_node = root_node.clone(); let root_node = root_node.clone();
let ctx = ctx.clone(); let db = db.clone();
_ = task::spawn(async move {
let root_node = root_node.clone();
let db = db.clone();
if let Err(e) = http1::Builder::new()
.serve_connection(
io,
service_fn(move |req| {
let root_node = root_node.clone();
let db = db.clone();
let matches = { let matches = {
let path = req.uri().path(); let path = req.uri().path();
match req.method() { match req.method() {
@ -402,9 +420,9 @@ mod tests {
async move { async move {
Ok::<_, Infallible>(if matches { Ok::<_, Infallible>(if matches {
if is_sync { if is_sync {
super::graphql_sync(root_node, ctx, req).await super::graphql_sync(root_node, db, req).await
} else { } else {
super::graphql(root_node, ctx, req).await super::graphql(root_node, db, req).await
} }
} else { } else {
let mut resp = Response::new(String::new()); let mut resp = Response::new(String::new());
@ -412,29 +430,31 @@ mod tests {
resp resp
}) })
} }
})) }),
)
.await
{
eprintln!("server error: {e}");
}
});
} }
}); });
let (shutdown_fut, shutdown) = futures::future::abortable(async { sleep(Duration::from_secs(10)).await; // wait 10ms for `server` to bind
tokio::time::sleep(Duration::from_secs(60)).await;
});
let server = Server::bind(&addr) match task::spawn_blocking(move || {
.serve(new_service)
.with_graceful_shutdown(async {
shutdown_fut.await.unwrap_err();
});
tokio::task::spawn_blocking(move || {
thread::sleep(Duration::from_millis(10)); // wait 10ms for server to bind
let integration = TestHyperIntegration { port }; let integration = TestHyperIntegration { port };
http_tests::run_http_test_suite(&integration); http_tests::run_http_test_suite(&integration);
shutdown.abort(); })
}); .await
{
Err(f) if f.is_panic() => panic::resume_unwind(f.into_panic()),
Ok(()) | Err(_) => {}
}
if let Err(e) = server.await { server.abort();
eprintln!("server error: {e}"); if let Ok(Err(e)) = server.await {
panic!("server failed: {e}");
} }
} }