Skip to content

Commit 257bc69

Browse files
elcharitasLegNeatotyranron
authored
Make juniper_hyper generic over hyper::body::Body (#1263, #1102)
Co-authored-by: Christian Legnitto <[email protected]> Co-authored-by: Kai Ren <[email protected]>
1 parent 86297a4 commit 257bc69

File tree

5 files changed

+130
-53
lines changed

5 files changed

+130
-53
lines changed

.github/workflows/ci.yml

+5-5
Original file line numberDiff line numberDiff line change
@@ -153,17 +153,17 @@ jobs:
153153
- juniper_graphql_ws
154154
- juniper_actix
155155
- juniper_axum
156-
- juniper_hyper
156+
#- juniper_hyper
157157
- juniper_rocket
158158
- juniper_warp
159159
os:
160160
- ubuntu
161161
- macOS
162162
- windows
163-
#include:
164-
# - { msrv: "1.75.0", crate: "juniper_actix", os: "ubuntu" }
165-
# - { msrv: "1.75.0", crate: "juniper_actix", os: "macOS" }
166-
# - { msrv: "1.75.0", crate: "juniper_actix", os: "windows" }
163+
include:
164+
- { msrv: "1.79.0", crate: "juniper_hyper", os: "ubuntu" }
165+
- { msrv: "1.79.0", crate: "juniper_hyper", os: "macOS" }
166+
- { msrv: "1.79.0", crate: "juniper_hyper", os: "windows" }
167167
runs-on: ${{ matrix.os }}-latest
168168
steps:
169169
- uses: actions/checkout@v4

juniper_hyper/CHANGELOG.md

+4-2
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@ All user visible changes to `juniper_hyper` crate will be documented in this fil
1010

1111
### BC Breaks
1212

13-
- Bumped up [MSRV] to 1.75. ([#1272])
13+
- Bumped up [MSRV] to 1.79. ([#1263])
14+
- Made `hyper::Request` in `graphql()` and `graphql_sync()` functions generic over `B: hyper::body::Body`. ([#1263], [#1102])
1415

15-
[#1272]: /../../pull/1272
16+
[#1102]: /../../issues/1102
17+
[#1263]: /../../pull/1263
1618

1719

1820

juniper_hyper/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
name = "juniper_hyper"
33
version = "0.9.0"
44
edition = "2021"
5-
rust-version = "1.75"
5+
rust-version = "1.79"
66
description = "`juniper` GraphQL integration with `hyper`."
77
license = "BSD-2-Clause"
88
authors = [

juniper_hyper/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
[![Crates.io](https://img.shields.io/crates/v/juniper_hyper.svg?maxAge=2592000)](https://crates.io/crates/juniper_hyper)
55
[![Documentation](https://docs.rs/juniper_hyper/badge.svg)](https://docs.rs/juniper_hyper)
66
[![CI](https://github.com/graphql-rust/juniper/workflows/CI/badge.svg?branch=master "CI")](https://github.com/graphql-rust/juniper/actions?query=workflow%3ACI+branch%3Amaster)
7-
[![Rust 1.75+](https://img.shields.io/badge/rustc-1.75+-lightgray.svg "Rust 1.75+")](https://blog.rust-lang.org/2023/12/28/Rust-1.75.0.html)
7+
[![Rust 1.79+](https://img.shields.io/badge/rustc-1.79+-lightgray.svg "Rust 1.79+")](https://blog.rust-lang.org/2024/06/13/Rust-1.79.0.html)
88

99
- [Changelog](https://github.com/graphql-rust/juniper/blob/juniper_hyper-v0.9.0/juniper_hyper/CHANGELOG.md)
1010

juniper_hyper/src/lib.rs

+119-44
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::{error::Error, fmt, string::FromUtf8Error, sync::Arc};
44

55
use http_body_util::BodyExt as _;
66
use hyper::{
7-
body,
7+
body::Body,
88
header::{self, HeaderValue},
99
Method, Request, Response, StatusCode,
1010
};
@@ -15,10 +15,10 @@ use juniper::{
1515
use serde_json::error::Error as SerdeError;
1616
use url::form_urlencoded;
1717

18-
pub async fn graphql_sync<CtxT, QueryT, MutationT, SubscriptionT, S>(
18+
pub async fn graphql_sync<CtxT, QueryT, MutationT, SubscriptionT, S, B>(
1919
root_node: Arc<RootNode<'static, QueryT, MutationT, SubscriptionT, S>>,
2020
context: Arc<CtxT>,
21-
req: Request<body::Incoming>,
21+
req: Request<B>,
2222
) -> Response<String>
2323
where
2424
QueryT: GraphQLType<S, Context = CtxT>,
@@ -29,17 +29,18 @@ where
2929
SubscriptionT::TypeInfo: Sync,
3030
CtxT: Sync,
3131
S: ScalarValue + Send + Sync,
32+
B: Body<Error: fmt::Display>,
3233
{
3334
match parse_req(req).await {
3435
Ok(req) => execute_request_sync(root_node, context, req).await,
3536
Err(resp) => resp,
3637
}
3738
}
3839

39-
pub async fn graphql<CtxT, QueryT, MutationT, SubscriptionT, S>(
40+
pub async fn graphql<CtxT, QueryT, MutationT, SubscriptionT, S, B>(
4041
root_node: Arc<RootNode<'static, QueryT, MutationT, SubscriptionT, S>>,
4142
context: Arc<CtxT>,
42-
req: Request<body::Incoming>,
43+
req: Request<B>,
4344
) -> Response<String>
4445
where
4546
QueryT: GraphQLTypeAsync<S, Context = CtxT>,
@@ -50,16 +51,19 @@ where
5051
SubscriptionT::TypeInfo: Sync,
5152
CtxT: Sync,
5253
S: ScalarValue + Send + Sync,
54+
B: Body<Error: fmt::Display>,
5355
{
5456
match parse_req(req).await {
5557
Ok(req) => execute_request(root_node, context, req).await,
5658
Err(resp) => resp,
5759
}
5860
}
5961

60-
async fn parse_req<S: ScalarValue>(
61-
req: Request<body::Incoming>,
62-
) -> Result<GraphQLBatchRequest<S>, Response<String>> {
62+
async fn parse_req<S, B>(req: Request<B>) -> Result<GraphQLBatchRequest<S>, Response<String>>
63+
where
64+
S: ScalarValue,
65+
B: Body<Error: fmt::Display>,
66+
{
6367
match *req.method() {
6468
Method::GET => parse_get_req(req),
6569
Method::POST => {
@@ -78,9 +82,11 @@ async fn parse_req<S: ScalarValue>(
7882
.map_err(render_error)
7983
}
8084

81-
fn parse_get_req<S: ScalarValue>(
82-
req: Request<body::Incoming>,
83-
) -> Result<GraphQLBatchRequest<S>, GraphQLRequestError> {
85+
fn parse_get_req<S, B>(req: Request<B>) -> Result<GraphQLBatchRequest<S>, GraphQLRequestError<B>>
86+
where
87+
S: ScalarValue,
88+
B: Body,
89+
{
8490
req.uri()
8591
.query()
8692
.map(|q| gql_request_from_get(q).map(GraphQLBatchRequest::Single))
@@ -91,9 +97,13 @@ fn parse_get_req<S: ScalarValue>(
9197
})
9298
}
9399

94-
async fn parse_post_json_req<S: ScalarValue>(
95-
body: body::Incoming,
96-
) -> Result<GraphQLBatchRequest<S>, GraphQLRequestError> {
100+
async fn parse_post_json_req<S, B>(
101+
body: B,
102+
) -> Result<GraphQLBatchRequest<S>, GraphQLRequestError<B>>
103+
where
104+
S: ScalarValue,
105+
B: Body,
106+
{
97107
let chunk = body
98108
.collect()
99109
.await
@@ -106,9 +116,13 @@ async fn parse_post_json_req<S: ScalarValue>(
106116
.map_err(GraphQLRequestError::BodyJSONError)
107117
}
108118

109-
async fn parse_post_graphql_req<S: ScalarValue>(
110-
body: body::Incoming,
111-
) -> Result<GraphQLBatchRequest<S>, GraphQLRequestError> {
119+
async fn parse_post_graphql_req<S, B>(
120+
body: B,
121+
) -> Result<GraphQLBatchRequest<S>, GraphQLRequestError<B>>
122+
where
123+
S: ScalarValue,
124+
B: Body,
125+
{
112126
let chunk = body
113127
.collect()
114128
.await
@@ -143,7 +157,10 @@ pub async fn playground(
143157
resp
144158
}
145159

146-
fn render_error(err: GraphQLRequestError) -> Response<String> {
160+
fn render_error<B>(err: GraphQLRequestError<B>) -> Response<String>
161+
where
162+
B: Body<Error: fmt::Display>,
163+
{
147164
let mut resp = new_response(StatusCode::BAD_REQUEST);
148165
*resp.body_mut() = err.to_string();
149166
resp
@@ -211,9 +228,12 @@ where
211228
resp
212229
}
213230

214-
fn gql_request_from_get<S>(input: &str) -> Result<JuniperGraphQLRequest<S>, GraphQLRequestError>
231+
fn gql_request_from_get<S, B>(
232+
input: &str,
233+
) -> Result<JuniperGraphQLRequest<S>, GraphQLRequestError<B>>
215234
where
216235
S: ScalarValue,
236+
B: Body,
217237
{
218238
let mut query = None;
219239
let mut operation_name = None;
@@ -254,7 +274,7 @@ where
254274
}
255275
}
256276

257-
fn invalid_err(parameter_name: &str) -> GraphQLRequestError {
277+
fn invalid_err<B: Body>(parameter_name: &str) -> GraphQLRequestError<B> {
258278
GraphQLRequestError::Invalid(format!(
259279
"`{parameter_name}` parameter is specified multiple times",
260280
))
@@ -275,35 +295,57 @@ fn new_html_response(code: StatusCode) -> Response<String> {
275295
resp
276296
}
277297

278-
#[derive(Debug)]
279-
enum GraphQLRequestError {
280-
BodyHyper(hyper::Error),
298+
enum GraphQLRequestError<B: Body> {
299+
BodyHyper(B::Error),
281300
BodyUtf8(FromUtf8Error),
282301
BodyJSONError(SerdeError),
283302
Variables(SerdeError),
284303
Invalid(String),
285304
}
286305

287-
impl fmt::Display for GraphQLRequestError {
306+
// NOTE: Manual implementation instead of `#[derive(Debug)]` is used to omit imposing unnecessary
307+
// `B: Debug` bound on the implementation.
308+
impl<B> fmt::Debug for GraphQLRequestError<B>
309+
where
310+
B: Body<Error: fmt::Debug>,
311+
{
288312
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
289313
match self {
290-
GraphQLRequestError::BodyHyper(err) => fmt::Display::fmt(err, f),
291-
GraphQLRequestError::BodyUtf8(err) => fmt::Display::fmt(err, f),
292-
GraphQLRequestError::BodyJSONError(err) => fmt::Display::fmt(err, f),
293-
GraphQLRequestError::Variables(err) => fmt::Display::fmt(err, f),
294-
GraphQLRequestError::Invalid(err) => fmt::Display::fmt(err, f),
314+
Self::BodyHyper(e) => fmt::Debug::fmt(e, f),
315+
Self::BodyUtf8(e) => fmt::Debug::fmt(e, f),
316+
Self::BodyJSONError(e) => fmt::Debug::fmt(e, f),
317+
Self::Variables(e) => fmt::Debug::fmt(e, f),
318+
Self::Invalid(e) => fmt::Debug::fmt(e, f),
295319
}
296320
}
297321
}
298322

299-
impl Error for GraphQLRequestError {
323+
impl<B> fmt::Display for GraphQLRequestError<B>
324+
where
325+
B: Body<Error: fmt::Display>,
326+
{
327+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
328+
match self {
329+
Self::BodyHyper(e) => fmt::Display::fmt(e, f),
330+
Self::BodyUtf8(e) => fmt::Display::fmt(e, f),
331+
Self::BodyJSONError(e) => fmt::Display::fmt(e, f),
332+
Self::Variables(e) => fmt::Display::fmt(e, f),
333+
Self::Invalid(e) => fmt::Display::fmt(e, f),
334+
}
335+
}
336+
}
337+
338+
impl<B> Error for GraphQLRequestError<B>
339+
where
340+
B: Body<Error: Error + 'static>,
341+
{
300342
fn source(&self) -> Option<&(dyn Error + 'static)> {
301343
match self {
302-
GraphQLRequestError::BodyHyper(err) => Some(err),
303-
GraphQLRequestError::BodyUtf8(err) => Some(err),
304-
GraphQLRequestError::BodyJSONError(err) => Some(err),
305-
GraphQLRequestError::Variables(err) => Some(err),
306-
GraphQLRequestError::Invalid(_) => None,
344+
Self::BodyHyper(e) => Some(e),
345+
Self::BodyUtf8(e) => Some(e),
346+
Self::BodyJSONError(e) => Some(e),
347+
Self::Variables(e) => Some(e),
348+
Self::Invalid(_) => None,
307349
}
308350
}
309351
}
@@ -314,7 +356,11 @@ mod tests {
314356
convert::Infallible, error::Error, net::SocketAddr, panic, sync::Arc, time::Duration,
315357
};
316358

317-
use hyper::{server::conn::http1, service::service_fn, Method, Response, StatusCode};
359+
use http_body_util::BodyExt as _;
360+
use hyper::{
361+
body::Incoming, server::conn::http1, service::service_fn, Method, Request, Response,
362+
StatusCode,
363+
};
318364
use hyper_util::rt::TokioIo;
319365
use juniper::{
320366
http::tests as http_tests,
@@ -376,8 +422,7 @@ mod tests {
376422
}
377423
}
378424

379-
async fn run_hyper_integration(is_sync: bool) {
380-
let port = if is_sync { 3002 } else { 3001 };
425+
async fn run_hyper_integration(port: u16, is_sync: bool, is_custom_type: bool) {
381426
let addr = SocketAddr::from(([127, 0, 0, 1], port));
382427

383428
let db = Arc::new(Database::new());
@@ -405,7 +450,7 @@ mod tests {
405450
if let Err(e) = http1::Builder::new()
406451
.serve_connection(
407452
io,
408-
service_fn(move |req| {
453+
service_fn(move |req: Request<Incoming>| {
409454
let root_node = root_node.clone();
410455
let db = db.clone();
411456
let matches = {
@@ -419,10 +464,30 @@ mod tests {
419464
};
420465
async move {
421466
Ok::<_, Infallible>(if matches {
422-
if is_sync {
423-
super::graphql_sync(root_node, db, req).await
467+
if is_custom_type {
468+
let (parts, mut body) = req.into_parts();
469+
let body = {
470+
let mut buf = String::new();
471+
if let Some(Ok(frame)) = body.frame().await {
472+
if let Ok(bytes) = frame.into_data() {
473+
buf = String::from_utf8_lossy(&bytes)
474+
.to_string();
475+
}
476+
}
477+
buf
478+
};
479+
let req = Request::from_parts(parts, body);
480+
if is_sync {
481+
super::graphql_sync(root_node, db, req).await
482+
} else {
483+
super::graphql(root_node, db, req).await
484+
}
424485
} else {
425-
super::graphql(root_node, db, req).await
486+
if is_sync {
487+
super::graphql_sync(root_node, db, req).await
488+
} else {
489+
super::graphql(root_node, db, req).await
490+
}
426491
}
427492
} else {
428493
let mut resp = Response::new(String::new());
@@ -460,11 +525,21 @@ mod tests {
460525

461526
#[tokio::test]
462527
async fn test_hyper_integration() {
463-
run_hyper_integration(false).await
528+
run_hyper_integration(3000, false, false).await
464529
}
465530

466531
#[tokio::test]
467532
async fn test_sync_hyper_integration() {
468-
run_hyper_integration(true).await
533+
run_hyper_integration(3001, true, false).await
534+
}
535+
536+
#[tokio::test]
537+
async fn test_custom_request_hyper_integration() {
538+
run_hyper_integration(3002, false, false).await
539+
}
540+
541+
#[tokio::test]
542+
async fn test_custom_request_sync_hyper_integration() {
543+
run_hyper_integration(3003, true, true).await
469544
}
470545
}

0 commit comments

Comments
 (0)