Skip to content

Commit

Permalink
chore(volo-http): update service with wrapped Request, Response a…
Browse files Browse the repository at this point in the history
…nd `Infallible` (#272)

* chore(volo-http): re-export and organize imported types

* chore(volo-http): refactor for using `crate::{Request, Response}`

This commit refactor the `volo-http` for replacing
`http::Request<hyper::body::Incoming>` with `crate::Request` and
`http::Response<crate::RespBody>` with `crate::Response`.

* feat(volo-http): use `std::convert::Infallible` instead of `DynError`

---------

Signed-off-by: Yu Li <[email protected]>
  • Loading branch information
yukiiiteru authored Dec 6, 2023
1 parent d1c8b4c commit 5ecbed6
Show file tree
Hide file tree
Showing 10 changed files with 215 additions and 188 deletions.
1 change: 0 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion volo-http/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ maintenance = { status = "actively-developed" }
[dependencies]
volo = { version = "0.8", path = "../volo" }

http = "1"
http-body-util = "0.1"
hyper = { version = "1", features = ["server", "http1", "http2"] }
hyper-util = { version = "0.1", features = ["tokio"] }
Expand Down
33 changes: 11 additions & 22 deletions volo-http/src/extract.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,24 @@
use std::convert::Infallible;

use futures_util::Future;
use http::{Method, Response, Uri};
use hyper::http::{Method, Uri};
use volo::net::Address;

use crate::{response::IntoResponse, HttpContext, Params, State};
use crate::{HttpContext, Params, State};

pub trait FromContext<S>: Sized {
type Rejection: IntoResponse;
fn from_context(
context: &HttpContext,
state: &S,
) -> impl Future<Output = Result<Self, Self::Rejection>> + Send;
) -> impl Future<Output = Result<Self, Infallible>> + Send;
}

impl<T, S> FromContext<S> for Option<T>
where
T: FromContext<S>,
S: Send + Sync,
{
type Rejection = Response<()>; // Infallible

async fn from_context(context: &HttpContext, state: &S) -> Result<Self, Self::Rejection> {
async fn from_context(context: &HttpContext, state: &S) -> Result<Self, Infallible> {
Ok(T::from_context(context, state).await.ok())
}
}
Expand All @@ -28,9 +27,7 @@ impl<S> FromContext<S> for Address
where
S: Send + Sync,
{
type Rejection = Response<()>; // Infallible

async fn from_context(context: &HttpContext, _state: &S) -> Result<Address, Self::Rejection> {
async fn from_context(context: &HttpContext, _state: &S) -> Result<Address, Infallible> {
Ok(context.peer.clone())
}
}
Expand All @@ -39,9 +36,7 @@ impl<S> FromContext<S> for Uri
where
S: Send + Sync,
{
type Rejection = Response<()>; // Infallible

async fn from_context(context: &HttpContext, _state: &S) -> Result<Uri, Self::Rejection> {
async fn from_context(context: &HttpContext, _state: &S) -> Result<Uri, Infallible> {
Ok(context.uri.clone())
}
}
Expand All @@ -50,9 +45,7 @@ impl<S> FromContext<S> for Method
where
S: Send + Sync,
{
type Rejection = Response<()>; // Infallible

async fn from_context(context: &HttpContext, _state: &S) -> Result<Method, Self::Rejection> {
async fn from_context(context: &HttpContext, _state: &S) -> Result<Method, Infallible> {
Ok(context.method.clone())
}
}
Expand All @@ -61,9 +54,7 @@ impl<S> FromContext<S> for Params
where
S: Send + Sync,
{
type Rejection = Response<()>; // Infallible

async fn from_context(context: &HttpContext, _state: &S) -> Result<Params, Self::Rejection> {
async fn from_context(context: &HttpContext, _state: &S) -> Result<Params, Infallible> {
Ok(context.params.clone())
}
}
Expand All @@ -72,9 +63,7 @@ impl<S> FromContext<S> for State<S>
where
S: Clone + Send + Sync,
{
type Rejection = Response<()>; // Infallible

async fn from_context(_context: &HttpContext, state: &S) -> Result<Self, Self::Rejection> {
async fn from_context(_context: &HttpContext, state: &S) -> Result<Self, Infallible> {
Ok(State(state.clone()))
}
}
30 changes: 12 additions & 18 deletions volo-http/src/handler.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
use std::{future::Future, marker::PhantomData};
use std::{convert::Infallible, future::Future, marker::PhantomData};

use http::Response;
use hyper::body::Incoming;
use motore::Service;

use crate::{
extract::FromContext,
macros::{all_the_tuples, all_the_tuples_no_last_special_case},
request::FromRequest,
response::{IntoResponse, RespBody},
DynError, DynService, HttpContext,
response::{IntoResponse, Response},
DynService, HttpContext,
};

pub trait Handler<T, S>: Sized {
Expand All @@ -18,7 +17,7 @@ pub trait Handler<T, S>: Sized {
context: &mut HttpContext,
req: Incoming,
state: &S,
) -> impl Future<Output = Response<RespBody>> + Send;
) -> impl Future<Output = Response> + Send;

fn with_state(self, state: S) -> HandlerService<Self, S, T>
where
Expand All @@ -39,12 +38,7 @@ where
Res: IntoResponse,
S: Send + Sync,
{
async fn call(
self,
_context: &mut HttpContext,
_req: Incoming,
_state: &S,
) -> Response<RespBody> {
async fn call(self, _context: &mut HttpContext, _req: Incoming, _state: &S) -> Response {
self().await.into_response()
}
}
Expand All @@ -63,7 +57,7 @@ macro_rules! impl_handler {
$( for<'r> $ty: FromContext<S> + Send + 'r, )*
for<'r> $last: FromRequest<S, M> + Send + 'r,
{
async fn call(self, context: &mut HttpContext, req: Incoming, state: &S) -> Response<RespBody> {
async fn call(self, context: &mut HttpContext, req: Incoming, state: &S) -> Response {
$(
let $ty = match $ty::from_context(context, state).await {
Ok(value) => value,
Expand Down Expand Up @@ -132,7 +126,7 @@ where
cx: &mut HttpContext,
req: Incoming,
state: S,
) -> Result<Response<RespBody>, DynError> {
) -> Result<Response, Infallible> {
self.0.into_route(state).call(cx, req).await
}
}
Expand Down Expand Up @@ -238,8 +232,8 @@ where
for<'r> H: Handler<T, S> + Clone + Send + Sync + 'r,
S: Sync,
{
type Response = Response<RespBody>;
type Error = DynError;
type Response = Response;
type Error = Infallible;

async fn call<'s, 'cx>(
&'s self,
Expand All @@ -251,15 +245,15 @@ where
}

pub trait HandlerWithoutRequest<T>: Sized {
fn call(self, context: &HttpContext) -> impl Future<Output = Response<RespBody>> + Send;
fn call(self, context: &HttpContext) -> impl Future<Output = Response> + Send;
}

impl<F, Res> HandlerWithoutRequest<()> for F
where
F: FnOnce() -> Res + Clone + Send,
Res: IntoResponse,
{
async fn call(self, _context: &HttpContext) -> Response<RespBody> {
async fn call(self, _context: &HttpContext) -> Response {
self().into_response()
}
}
Expand All @@ -275,7 +269,7 @@ macro_rules! impl_handler_without_request {
Res: IntoResponse,
$( for<'r> $ty: FromContext<()> + Send + 'r, )*
{
async fn call(self, context: &HttpContext) -> Response<RespBody> {
async fn call(self, context: &HttpContext) -> Response {
$(
let $ty = match $ty::from_context(context, &()).await {
Ok(value) => value,
Expand Down
59 changes: 25 additions & 34 deletions volo-http/src/layer.rs
Original file line number Diff line number Diff line change
@@ -1,39 +1,39 @@
use std::{marker::PhantomData, time::Duration};

use http::{Method, Request, Response, StatusCode};
use http_body_util::Full;
use hyper::body::{Bytes, Incoming};
use hyper::{
body::Incoming,
http::{Method, StatusCode},
};
use motore::{layer::Layer, service::Service};

use crate::{
handler::HandlerWithoutRequest,
response::{IntoResponse, RespBody},
request::Request,
response::{IntoResponse, Response},
HttpContext,
};

pub trait LayerExt {
fn method(
self,
method: Method,
) -> FilterLayer<Box<dyn Fn(&mut HttpContext, &Request<Incoming>) -> Result<(), StatusCode>>>
) -> FilterLayer<Box<dyn Fn(&mut HttpContext, &Request) -> Result<(), StatusCode>>>

Check warning on line 20 in volo-http/src/layer.rs

View workflow job for this annotation

GitHub Actions / clippy

very complex type used. Consider factoring parts into `type` definitions

warning: very complex type used. Consider factoring parts into `type` definitions --> volo-http/src/layer.rs:20:10 | 20 | ) -> FilterLayer<Box<dyn Fn(&mut HttpContext, &Request) -> Result<(), StatusCode>>> | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#type_complexity = note: `#[warn(clippy::type_complexity)]` on by default
where
Self: Sized,
{
self.filter(Box::new(
move |cx: &mut HttpContext, _: &Request<Incoming>| {
if cx.method == method {
Ok(())
} else {
Err(StatusCode::METHOD_NOT_ALLOWED)
}
},
))
self.filter(Box::new(move |cx: &mut HttpContext, _: &Request| {
if cx.method == method {
Ok(())
} else {
Err(StatusCode::METHOD_NOT_ALLOWED)
}
}))
}

fn filter<F>(self, f: F) -> FilterLayer<F>
where
Self: Sized,
F: Fn(&mut HttpContext, &Request<Incoming>) -> Result<(), StatusCode>,
F: Fn(&mut HttpContext, &Request) -> Result<(), StatusCode>,
{
FilterLayer { f }
}
Expand All @@ -45,11 +45,8 @@ pub struct FilterLayer<F> {

impl<S, F> Layer<S> for FilterLayer<F>
where
S: Service<HttpContext, Request<Incoming>, Response = Response<Full<Bytes>>>
+ Send
+ Sync
+ 'static,
F: Fn(&mut HttpContext, &Request<Incoming>) -> Result<(), StatusCode> + Send + Sync,
S: Service<HttpContext, Request, Response = Response> + Send + Sync + 'static,
F: Fn(&mut HttpContext, &Request) -> Result<(), StatusCode> + Send + Sync,
{
type Service = Filter<S, F>;

Expand All @@ -66,13 +63,10 @@ pub struct Filter<S, F> {
f: F,
}

impl<S, F> Service<HttpContext, Request<Incoming>> for Filter<S, F>
impl<S, F> Service<HttpContext, Request> for Filter<S, F>
where
S: Service<HttpContext, Request<Incoming>, Response = Response<Full<Bytes>>>
+ Send
+ Sync
+ 'static,
F: Fn(&mut HttpContext, &Request<Incoming>) -> Result<(), StatusCode> + Send + Sync,
S: Service<HttpContext, Request, Response = Response> + Send + Sync + 'static,
F: Fn(&mut HttpContext, &Request) -> Result<(), StatusCode> + Send + Sync,
{
type Response = S::Response;

Expand All @@ -81,13 +75,10 @@ where
async fn call<'s, 'cx>(
&'s self,
cx: &'cx mut HttpContext,
req: Request<Incoming>,
req: Request,
) -> Result<Self::Response, Self::Error> {
if let Err(status) = (self.f)(cx, &req) {
return Ok(Response::builder()
.status(status)
.body(Full::new(Bytes::new()))
.unwrap());
return Ok(status.into_response());
}
self.service.call(cx, req).await
}
Expand Down Expand Up @@ -115,7 +106,7 @@ impl<H, T> TimeoutLayer<H, T> {

impl<S, H, T> Layer<S> for TimeoutLayer<H, T>
where
S: Service<HttpContext, Incoming, Response = Response<RespBody>> + Send + Sync + 'static,
S: Service<HttpContext, Incoming, Response = Response> + Send + Sync + 'static,
H: HandlerWithoutRequest<T> + Clone + Send + Sync + 'static,
T: Sync,
{
Expand All @@ -141,7 +132,7 @@ pub struct Timeout<S, H, T> {

impl<S, H, T> Service<HttpContext, Incoming> for Timeout<S, H, T>
where
S: Service<HttpContext, Incoming, Response = Response<RespBody>> + Send + Sync + 'static,
S: Service<HttpContext, Incoming, Response = Response> + Send + Sync + 'static,
S::Error: Send,
H: HandlerWithoutRequest<T> + Clone + Send + Sync + 'static,
T: Sync,
Expand All @@ -161,7 +152,7 @@ where
tokio::select! {
resp = fut_service => resp,
_ = fut_timeout => {
Ok(self.handler.clone().call(cx).await.into_response())
Ok(self.handler.clone().call(cx).await)
},
}
}
Expand Down
28 changes: 13 additions & 15 deletions volo-http/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,23 @@ pub mod server;

mod macros;

use std::convert::Infallible;

pub use bytes::Bytes;
use http::{Extensions, HeaderMap, HeaderValue, Version};
pub use http::{Method, StatusCode, Uri};
use hyper::{body::Incoming, Response};
pub use hyper::{
body::Incoming,
http::{Extensions, HeaderMap, HeaderName, HeaderValue, Method, StatusCode, Uri, Version},
};
pub use volo::net::Address;

pub use crate::{param::Params, request::Json, server::Server};

mod private {
#[derive(Debug, Clone, Copy)]
pub enum ViaContext {}

#[derive(Debug, Clone, Copy)]
pub enum ViaRequest {}
}
pub use crate::{
param::Params,
request::{Json, Request},
response::Response,
server::Server,
};

pub type DynService =
motore::BoxCloneService<HttpContext, Incoming, Response<response::RespBody>, DynError>;
pub type DynError = Box<dyn std::error::Error + Send + Sync>;
pub type DynService = motore::BoxCloneService<HttpContext, Incoming, Response, Infallible>;

#[derive(Debug, Default, Clone, Copy)]
pub struct State<S>(pub S);
Expand Down
Loading

0 comments on commit 5ecbed6

Please sign in to comment.