Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions client/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,11 @@ impl ClientBuilder {
///
/// // trait implement for the logic of middleware. most of the types are boilerplate
/// // that can be copy/pasted. the real logic goes into `async fn call`
/// impl<'r, 'c> Service<ServiceRequest<'r, 'c>> for MyMiddleware {
/// impl<'c> Service<ServiceRequest<'c>> for MyMiddleware {
/// type Response = Response;
/// type Error = Error;
///
/// async fn call(&self, req: ServiceRequest<'r, 'c>) -> Result<Self::Response, Self::Error> {
/// async fn call(&self, req: ServiceRequest<'c>) -> Result<Self::Response, Self::Error> {
/// // my middleware receive ServiceRequest and can do pre-process before passing it to
/// // HttpService. in this case we just print out the HTTP method of request.
/// println!("request method is: {}", req.req.method());
Expand Down Expand Up @@ -115,7 +115,7 @@ impl ClientBuilder {
pub fn middleware<F, S>(mut self, func: F) -> Self
where
F: FnOnce(HttpService) -> S,
S: for<'r, 'c> Service<ServiceRequest<'r, 'c>, Response = Response, Error = Error> + Send + Sync + 'static,
S: for<'c> Service<ServiceRequest<'c>, Response = Response, Error = Error> + Send + Sync + 'static,
{
self.service = Box::new(func(self.service));
self
Expand Down
4 changes: 2 additions & 2 deletions client/src/h1/proto/dispatcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use super::context::Context;
pub(crate) async fn send<S, B, E>(
stream: &mut S,
date: DateTimeHandle<'_>,
req: &mut Request<B>,
mut req: Request<B>,
) -> Result<(Response<()>, BytesMut, TransferCoding, bool), Error>
where
S: AsyncIo + Unpin,
Expand Down Expand Up @@ -72,7 +72,7 @@ where
let mut ctx = Context::<128>::new(&date);

// encode request head and return transfer encoding for request body
let encoder = ctx.encode_head(&mut buf, req)?;
let encoder = ctx.encode_head(&mut buf, &mut req)?;

// it's important to call set_head_method after encode_head. Context would remove http body it encodes/decodes
// for head http method.
Expand Down
6 changes: 3 additions & 3 deletions client/src/middleware/decompress.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@ impl<S> Decompress<S> {
}
}

impl<'r, 'c, S> Service<ServiceRequest<'r, 'c>> for Decompress<S>
impl<'c, S> Service<ServiceRequest<'c>> for Decompress<S>
where
S: for<'r2, 'c2> Service<ServiceRequest<'r2, 'c2>, Response = Response, Error = Error> + Send + Sync,
S: for<'c2> Service<ServiceRequest<'c2>, Response = Response, Error = Error> + Send + Sync,
{
type Response = Response;
type Error = Error;

async fn call(&self, req: ServiceRequest<'r, 'c>) -> Result<Self::Response, Self::Error> {
async fn call(&self, mut req: ServiceRequest<'c>) -> Result<Self::Response, Self::Error> {
req.req
.headers_mut()
.insert(ACCEPT_ENCODING, HeaderValue::from_static("gzip, deflate, br"));
Expand Down
49 changes: 20 additions & 29 deletions client/src/middleware/redirect.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use xitca_http::Request;
use crate::{
body::BoxBody,
error::{Error, InvalidUri},
http::{
header::{
Expand Down Expand Up @@ -41,22 +41,21 @@ impl<S, const MAX: usize> FollowRedirect<S, MAX> {
}
}

impl<'r, 'c, S, const MAX: usize> Service<ServiceRequest<'r, 'c>> for FollowRedirect<S, MAX>
impl<'c, S, const MAX: usize> Service<ServiceRequest<'c>> for FollowRedirect<S, MAX>
where
S: for<'r2, 'c2> Service<ServiceRequest<'r2, 'c2>, Response = Response, Error = Error> + Send + Sync,
S: for<'c2> Service<ServiceRequest<'c2>, Response = Response, Error = Error> + Send + Sync,
{
type Response = Response;
type Error = Error;

async fn call(&self, req: ServiceRequest<'r, 'c>) -> Result<Self::Response, Self::Error> {
async fn call(&self, req: ServiceRequest<'c>) -> Result<Self::Response, Self::Error> {
let ServiceRequest { req, client, timeout } = req;
let mut headers = req.headers().clone();
let mut method = req.method().clone();
let mut uri = req.uri().clone();
let ext = req.extensions().clone();
let (mut head, mut body) = req.into_parts();
let mut count = 0;

loop {
let body = core::mem::take(&mut body);
let req = Request::from_parts(head.clone(), body);
let mut res = self.service.call(ServiceRequest { req, client, timeout }).await?;

if count == MAX {
Expand All @@ -65,14 +64,12 @@ where

match res.status() {
StatusCode::MOVED_PERMANENTLY | StatusCode::FOUND | StatusCode::SEE_OTHER => {
if method != Method::HEAD {
method = Method::GET;
if head.method != Method::HEAD {
head.method = Method::GET;
}

*req.body_mut() = BoxBody::default();

for header in &[TRANSFER_ENCODING, CONTENT_ENCODING, CONTENT_TYPE, CONTENT_LENGTH] {
headers.remove(header);
head.headers.remove(header);
}
}
StatusCode::TEMPORARY_REDIRECT | StatusCode::PERMANENT_REDIRECT => {}
Expand All @@ -83,7 +80,7 @@ where
return Ok(res);
};

let parts = uri.into_parts();
let parts = core::mem::take(&mut head.uri).into_parts();

let parts_location = location
.to_str()
Expand All @@ -93,9 +90,9 @@ where

// remove authenticated headers when redirected to different scheme/authority
if parts_location.scheme != parts.scheme || parts_location.authority != parts.authority {
headers.remove(AUTHORIZATION);
headers.remove(PROXY_AUTHORIZATION);
headers.remove(COOKIE);
head.headers.remove(AUTHORIZATION);
head.headers.remove(PROXY_AUTHORIZATION);
head.headers.remove(COOKIE);
}

let mut uri_builder = Uri::builder();
Expand All @@ -109,12 +106,7 @@ where
}

let path = parts_location.path_and_query.ok_or(InvalidUri::MissingPathQuery)?;
uri = uri_builder.path_and_query(path).build().unwrap();

*req.uri_mut() = uri.clone();
*req.method_mut() = method.clone();
*req.headers_mut() = headers.clone();
*req.extensions_mut() = ext.clone();
head.uri = uri_builder.path_and_query(path).build().unwrap();

count += 1;
}
Expand All @@ -124,11 +116,10 @@ where
#[cfg(test)]
mod test {
use crate::{
body::ResponseBody,
body::{BoxBody, ResponseBody},
http,
service::{mock_service, Service},
};

use super::*;

#[tokio::test]
Expand All @@ -155,21 +146,21 @@ mod test {
p => panic!("unexpected uri path: {p}"),
};

let mut req = http::Request::builder()
let req = http::Request::builder()
.uri("http://foo.bar/foo")
.body(Default::default())
.unwrap();

let req = handle.mock(&mut req, handler);
let req = handle.mock(req, handler);
let res = redirect.call(req).await.unwrap();
assert_eq!(res.status(), StatusCode::IM_A_TEAPOT);

let mut req = http::Request::builder()
let req = http::Request::builder()
.uri("http://foo.bar/fur")
.body(Default::default())
.unwrap();

let req = handle.mock(&mut req, handler);
let req = handle.mock(req, handler);
let res = redirect.call(req).await.unwrap();
assert_eq!(res.status(), StatusCode::SEE_OTHER);
assert_eq!(res.headers().get(LOCATION).unwrap().to_str().unwrap(), "/bar");
Expand Down
11 changes: 2 additions & 9 deletions client/src/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ impl<'a, M> RequestBuilder<'a, M> {
// send request to server
pub(crate) async fn _send(self) -> Result<Response, Error> {
let Self {
mut req,
req,
err,
client,
timeout,
Expand All @@ -132,14 +132,7 @@ impl<'a, M> RequestBuilder<'a, M> {
return Err(err.into());
}

client
.service
.call(ServiceRequest {
req: &mut req,
client,
timeout,
})
.await
client.service.call(ServiceRequest { req, client, timeout }).await
}

pub(crate) fn push_error(&mut self, e: Error) {
Expand Down
37 changes: 19 additions & 18 deletions client/src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,28 +64,32 @@ where
/// It's similar to [RequestBuilder] type but with additional side effect enabled.
///
/// [RequestBuilder]: crate::request::RequestBuilder
pub struct ServiceRequest<'r, 'c> {
pub req: &'r mut Request<BoxBody>,
pub struct ServiceRequest<'c> {
pub req: Request<BoxBody>,
pub client: &'c Client,
pub timeout: Duration,
}

/// type alias for object safe wrapper of type implement [Service] trait.
pub type HttpService =
Box<dyn for<'r, 'c> ServiceDyn<ServiceRequest<'r, 'c>, Response = Response, Error = Error> + Send + Sync>;
Box<dyn for<'c> ServiceDyn<ServiceRequest<'c>, Response = Response, Error = Error> + Send + Sync>;

pub(crate) fn base_service() -> HttpService {
struct HttpService;

impl<'r, 'c> Service<ServiceRequest<'r, 'c>> for HttpService {
impl<'c> Service<ServiceRequest<'c>> for HttpService {
type Response = Response;
type Error = Error;

async fn call(&self, req: ServiceRequest<'r, 'c>) -> Result<Self::Response, Self::Error> {
async fn call(&self, req: ServiceRequest<'c>) -> Result<Self::Response, Self::Error> {
#[cfg(any(feature = "http1", feature = "http2", feature = "http3"))]
use crate::{error::TimeoutError, timeout::Timeout};

let ServiceRequest { req, client, timeout } = req;
let ServiceRequest {
mut req,
client,
timeout,
} = req;

let uri = Uri::try_parse(req.uri())?;

Expand All @@ -108,10 +112,7 @@ pub(crate) fn base_service() -> HttpService {
return match _conn.conn {
#[cfg(feature = "http2")]
crate::connection::ConnectionShared::H2(ref mut conn) => {
match crate::h2::proto::send(conn, _date, core::mem::take(req))
.timeout(_timer.as_mut())
.await
{
match crate::h2::proto::send(conn, _date, req).timeout(_timer.as_mut()).await {
Ok(Ok(res)) => {
let timeout = client.timeout_config.response_timeout;
Ok(Response::new(res, _timer, timeout))
Expand All @@ -128,7 +129,7 @@ pub(crate) fn base_service() -> HttpService {
}
#[cfg(feature = "http3")]
crate::connection::ConnectionShared::H3(ref mut conn) => {
let res = crate::h3::proto::send(conn, _date, core::mem::take(req))
let res = crate::h3::proto::send(conn, _date, req)
.timeout(_timer.as_mut())
.await
.map_err(|_| TimeoutError::Request)??;
Expand Down Expand Up @@ -297,11 +298,11 @@ mod test {

impl HttpServiceMockHandle {
/// compose a service request with given http request and it's mocked server side handler function
pub(crate) fn mock<'r, 'c>(
&'c self,
req: &'r mut Request<BoxBody>,
pub(crate) fn mock(
&self,
mut req: Request<BoxBody>,
handler: impl Fn(Request<BoxBody>) -> Result<http::Response<ResponseBody>, Error> + Send + Sync + 'static,
) -> ServiceRequest<'r, 'c> {
) -> ServiceRequest<'_> {
req.extensions_mut().insert(Arc::new(handler) as HandlerFn);
ServiceRequest {
req,
Expand All @@ -311,17 +312,17 @@ mod test {
}
}

impl<'r, 'c> Service<ServiceRequest<'r, 'c>> for HttpServiceMock {
impl<'c> Service<ServiceRequest<'c>> for HttpServiceMock {
type Response = Response;
type Error = Error;

async fn call(
&self,
ServiceRequest { req, timeout, .. }: ServiceRequest<'r, 'c>,
ServiceRequest { req, timeout, .. }: ServiceRequest<'c>,
) -> Result<Self::Response, Self::Error> {
let handler = req.extensions().get::<HandlerFn>().unwrap().clone();

let res = handler(core::mem::take(req))?;
let res = handler(req)?;

Ok(Response::new(
res,
Expand Down