Breakup routing module (#424)

The routing module had gotten quite hard to navigate so this breaks it
into smaller modules. No user facing changes.
This commit is contained in:
David Pedersen 2021-10-26 19:51:22 +02:00 committed by GitHub
parent 91981db8c7
commit 302a720271
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 278 additions and 243 deletions

View file

@ -1,19 +1,15 @@
//! Future types.
use crate::{body::BoxBody, clone_box_service::CloneBoxService};
use crate::body::BoxBody;
use futures_util::future::Either;
use http::{Request, Response};
use pin_project_lite::pin_project;
use std::{
convert::Infallible,
future::{ready, Future},
pin::Pin,
task::{Context, Poll},
};
use std::{convert::Infallible, future::ready};
use tower::util::Oneshot;
use tower_service::Service;
pub use super::method_not_allowed::MethodNotAllowedFuture;
pub use super::{
into_make_service::IntoMakeService, method_not_allowed::MethodNotAllowedFuture,
route::RouteFuture,
};
opaque_future! {
/// Response future for [`Router`](super::Router).
@ -33,62 +29,3 @@ impl<B> RouterFuture<B> {
RouterFuture::new(Either::Right(ready(Ok(response))))
}
}
pin_project! {
/// Response future for [`Route`](super::Route).
pub struct RouteFuture<B> {
#[pin]
future: Oneshot<
CloneBoxService<Request<B>, Response<BoxBody>, Infallible>,
Request<B>,
>
}
}
impl<B> RouteFuture<B> {
pub(crate) fn new(
future: Oneshot<CloneBoxService<Request<B>, Response<BoxBody>, Infallible>, Request<B>>,
) -> Self {
RouteFuture { future }
}
}
impl<B> Future for RouteFuture<B> {
type Output = Result<Response<BoxBody>, Infallible>;
#[inline]
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
self.project().future.poll(cx)
}
}
pin_project! {
/// The response future for [`Nested`](super::Nested).
#[derive(Debug)]
pub(crate) struct NestedFuture<S, B>
where
S: Service<Request<B>>,
{
#[pin]
pub(super) inner: Oneshot<S, Request<B>>
}
}
impl<S, B> Future for NestedFuture<S, B>
where
S: Service<Request<B>, Response = Response<BoxBody>, Error = Infallible>,
B: Send + Sync + 'static,
{
type Output = Result<Response<BoxBody>, Infallible>;
#[inline]
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
self.project().inner.poll(cx)
}
}
opaque_future! {
/// Response future from [`MakeRouteService`] services.
pub type MakeRouteServiceFuture<S> =
std::future::Ready<Result<S, Infallible>>;
}

View file

@ -0,0 +1,57 @@
use std::{
convert::Infallible,
future::ready,
task::{Context, Poll},
};
use tower_service::Service;
/// A [`MakeService`] that produces axum router services.
///
/// [`MakeService`]: tower::make::MakeService
#[derive(Debug, Clone)]
pub struct IntoMakeService<S> {
service: S,
}
impl<S> IntoMakeService<S> {
pub(super) fn new(service: S) -> Self {
Self { service }
}
}
impl<S, T> Service<T> for IntoMakeService<S>
where
S: Clone,
{
type Response = S;
type Error = Infallible;
type Future = MakeRouteServiceFuture<S>;
#[inline]
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, _target: T) -> Self::Future {
MakeRouteServiceFuture::new(ready(Ok(self.service.clone())))
}
}
opaque_future! {
/// Response future from [`MakeRouteService`] services.
pub type MakeRouteServiceFuture<S> =
std::future::Ready<Result<S, Infallible>>;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn traits() {
use crate::tests::*;
assert_send::<IntoMakeService<()>>();
assert_sync::<IntoMakeService<()>>();
}
}

View file

@ -67,3 +67,16 @@ opaque_future! {
pub type MethodNotAllowedFuture<E> =
std::future::Ready<Result<Response<BoxBody>, E>>;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn traits() {
use crate::tests::*;
assert_send::<MethodNotAllowed<NotSendSync>>();
assert_sync::<MethodNotAllowed<NotSendSync>>();
}
}

View file

@ -1,10 +1,9 @@
//! Routing between [`Service`]s and handlers.
use self::future::{NestedFuture, RouteFuture, RouterFuture};
use self::future::RouterFuture;
use self::not_found::NotFound;
use crate::{
body::{box_body, Body, BoxBody},
clone_box_service::CloneBoxService,
extract::{
connect_info::{Connected, IntoMakeServiceWithConnectInfo},
OriginalUri,
@ -19,7 +18,6 @@ use std::{
collections::HashMap,
convert::Infallible,
fmt,
future::ready,
sync::Arc,
task::{Context, Poll},
};
@ -32,12 +30,15 @@ pub mod future;
pub mod handler_method_router;
pub mod service_method_router;
mod into_make_service;
mod method_filter;
mod method_not_allowed;
mod nested;
mod not_found;
mod route;
pub use self::method_filter::MethodFilter;
pub(crate) use self::method_not_allowed::MethodNotAllowed;
pub use self::{into_make_service::IntoMakeService, method_filter::MethodFilter, route::Route};
#[doc(no_inline)]
pub use self::handler_method_router::{
@ -353,7 +354,7 @@ where
panic!("Invalid route: {}", err);
}
self.routes.insert(id, Route::new(Nested { svc }));
self.routes.insert(id, Route::new(nested::Nested { svc }));
self
}
@ -754,7 +755,8 @@ where
.collect::<Vec<_>>();
if let Some(tail) = match_.params.get(NEST_TAIL_PARAM) {
req.extensions_mut().insert(NestMatchTail(tail.to_string()));
req.extensions_mut()
.insert(nested::NestMatchTail(tail.to_string()));
}
insert_url_params(&mut req, params);
@ -769,9 +771,6 @@ where
}
}
#[derive(Clone)]
struct NestMatchTail(String);
impl<B> Service<Request<B>> for Router<B>
where
B: Send + Sync + 'static,
@ -824,18 +823,33 @@ where
}
}
pub(crate) struct UriStack(Vec<Uri>);
impl UriStack {
fn push<B>(req: &mut Request<B>) {
let uri = req.uri().clone();
if let Some(stack) = req.extensions_mut().get_mut::<Self>() {
stack.0.push(uri);
fn with_path(uri: &Uri, new_path: &str) -> Uri {
let path_and_query = if let Some(path_and_query) = uri.path_and_query() {
let new_path = if new_path.starts_with('/') {
Cow::Borrowed(new_path)
} else {
req.extensions_mut().insert(Self(vec![uri]));
Cow::Owned(format!("/{}", new_path))
};
if let Some(query) = path_and_query.query() {
Some(
format!("{}?{}", new_path, query)
.parse::<http::uri::PathAndQuery>()
.unwrap(),
)
} else {
Some(new_path.parse().unwrap())
}
}
} else {
None
};
let mut parts = http::uri::Parts::default();
parts.scheme = uri.scheme().cloned();
parts.authority = uri.authority().cloned();
parts.path_and_query = path_and_query;
Uri::from_parts(parts).unwrap()
}
// we store the potential error here such that users can handle invalid path
@ -881,150 +895,6 @@ pub(crate) struct InvalidUtf8InPathParam {
pub(crate) key: ByteStr,
}
/// A [`Service`] that has been nested inside a router at some path.
///
/// Created with [`Router::nest`].
#[derive(Debug, Clone)]
struct Nested<S> {
svc: S,
}
impl<B, S> Service<Request<B>> for Nested<S>
where
S: Service<Request<B>, Response = Response<BoxBody>, Error = Infallible> + Clone,
B: Send + Sync + 'static,
{
type Response = Response<BoxBody>;
type Error = Infallible;
type Future = NestedFuture<S, B>;
#[inline]
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, mut req: Request<B>) -> Self::Future {
// strip the prefix from the URI just before calling the inner service
// such that any surrounding middleware still see the full path
if let Some(tail) = req.extensions_mut().remove::<NestMatchTail>() {
UriStack::push(&mut req);
let new_uri = with_path(req.uri(), &tail.0);
*req.uri_mut() = new_uri;
}
NestedFuture {
inner: self.svc.clone().oneshot(req),
}
}
}
fn with_path(uri: &Uri, new_path: &str) -> Uri {
let path_and_query = if let Some(path_and_query) = uri.path_and_query() {
let new_path = if new_path.starts_with('/') {
Cow::Borrowed(new_path)
} else {
Cow::Owned(format!("/{}", new_path))
};
if let Some(query) = path_and_query.query() {
Some(
format!("{}?{}", new_path, query)
.parse::<http::uri::PathAndQuery>()
.unwrap(),
)
} else {
Some(new_path.parse().unwrap())
}
} else {
None
};
let mut parts = http::uri::Parts::default();
parts.scheme = uri.scheme().cloned();
parts.authority = uri.authority().cloned();
parts.path_and_query = path_and_query;
Uri::from_parts(parts).unwrap()
}
/// A [`MakeService`] that produces axum router services.
///
/// [`MakeService`]: tower::make::MakeService
#[derive(Debug, Clone)]
pub struct IntoMakeService<S> {
service: S,
}
impl<S> IntoMakeService<S> {
fn new(service: S) -> Self {
Self { service }
}
}
impl<S, T> Service<T> for IntoMakeService<S>
where
S: Clone,
{
type Response = S;
type Error = Infallible;
type Future = future::MakeRouteServiceFuture<S>;
#[inline]
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, _target: T) -> Self::Future {
future::MakeRouteServiceFuture::new(ready(Ok(self.service.clone())))
}
}
/// How routes are stored inside a [`Router`].
///
/// You normally shouldn't need to care about this type.
pub struct Route<B = Body>(CloneBoxService<Request<B>, Response<BoxBody>, Infallible>);
impl<B> Route<B> {
fn new<T>(svc: T) -> Self
where
T: Service<Request<B>, Response = Response<BoxBody>, Error = Infallible>
+ Clone
+ Send
+ 'static,
T::Future: Send + 'static,
{
Self(CloneBoxService::new(svc))
}
}
impl<ReqBody> Clone for Route<ReqBody> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<ReqBody> fmt::Debug for Route<ReqBody> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Route").finish()
}
}
impl<B> Service<Request<B>> for Route<B> {
type Response = Response<BoxBody>;
type Error = Infallible;
type Future = RouteFuture<B>;
#[inline]
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
#[inline]
fn call(&mut self, req: Request<B>) -> Self::Future {
RouteFuture::new(self.0.clone().oneshot(req))
}
}
/// Wrapper around `matchit::Node` that supports merging two `Node`s.
#[derive(Clone, Default)]
struct Node {
@ -1109,16 +979,5 @@ mod tests {
use crate::tests::*;
assert_send::<Router<()>>();
assert_send::<Route<()>>();
assert_send::<MethodNotAllowed<NotSendSync>>();
assert_sync::<MethodNotAllowed<NotSendSync>>();
assert_send::<Nested<()>>();
assert_sync::<Nested<()>>();
assert_send::<IntoMakeService<()>>();
assert_sync::<IntoMakeService<()>>();
}
}

69
src/routing/nested.rs Normal file
View file

@ -0,0 +1,69 @@
use crate::body::BoxBody;
use http::{Request, Response, Uri};
use std::{
convert::Infallible,
task::{Context, Poll},
};
use tower::util::Oneshot;
use tower::ServiceExt;
use tower_service::Service;
/// A [`Service`] that has been nested inside a router at some path.
///
/// Created with [`Router::nest`].
#[derive(Debug, Clone)]
pub(super) struct Nested<S> {
pub(super) svc: S,
}
impl<B, S> Service<Request<B>> for Nested<S>
where
S: Service<Request<B>, Response = Response<BoxBody>, Error = Infallible> + Clone,
B: Send + Sync + 'static,
{
type Response = Response<BoxBody>;
type Error = Infallible;
type Future = Oneshot<S, Request<B>>;
#[inline]
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, mut req: Request<B>) -> Self::Future {
// strip the prefix from the URI just before calling the inner service
// such that any surrounding middleware still see the full path
if let Some(tail) = req.extensions_mut().remove::<NestMatchTail>() {
UriStack::push(&mut req);
let new_uri = super::with_path(req.uri(), &tail.0);
*req.uri_mut() = new_uri;
}
self.svc.clone().oneshot(req)
}
}
pub(crate) struct UriStack(Vec<Uri>);
impl UriStack {
fn push<B>(req: &mut Request<B>) {
let uri = req.uri().clone();
if let Some(stack) = req.extensions_mut().get_mut::<Self>() {
stack.0.push(uri);
} else {
req.extensions_mut().insert(Self(vec![uri]));
}
}
}
#[derive(Clone)]
pub(super) struct NestMatchTail(pub(super) String);
#[test]
fn traits() {
use crate::tests::*;
assert_send::<Nested<()>>();
assert_sync::<Nested<()>>();
}

100
src/routing/route.rs Normal file
View file

@ -0,0 +1,100 @@
use crate::{
body::{Body, BoxBody},
clone_box_service::CloneBoxService,
};
use http::{Request, Response};
use pin_project_lite::pin_project;
use std::{
convert::Infallible,
fmt,
future::Future,
pin::Pin,
task::{Context, Poll},
};
use tower::{util::Oneshot, ServiceExt};
use tower_service::Service;
/// How routes are stored inside a [`Router`](super::Router).
///
/// You normally shouldn't need to care about this type.
pub struct Route<B = Body>(CloneBoxService<Request<B>, Response<BoxBody>, Infallible>);
impl<B> Route<B> {
pub(super) fn new<T>(svc: T) -> Self
where
T: Service<Request<B>, Response = Response<BoxBody>, Error = Infallible>
+ Clone
+ Send
+ 'static,
T::Future: Send + 'static,
{
Self(CloneBoxService::new(svc))
}
}
impl<ReqBody> Clone for Route<ReqBody> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<ReqBody> fmt::Debug for Route<ReqBody> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Route").finish()
}
}
impl<B> Service<Request<B>> for Route<B> {
type Response = Response<BoxBody>;
type Error = Infallible;
type Future = RouteFuture<B>;
#[inline]
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
#[inline]
fn call(&mut self, req: Request<B>) -> Self::Future {
RouteFuture::new(self.0.clone().oneshot(req))
}
}
pin_project! {
/// Response future for [`Route`].
pub struct RouteFuture<B> {
#[pin]
future: Oneshot<
CloneBoxService<Request<B>, Response<BoxBody>, Infallible>,
Request<B>,
>
}
}
impl<B> RouteFuture<B> {
pub(crate) fn new(
future: Oneshot<CloneBoxService<Request<B>, Response<BoxBody>, Infallible>, Request<B>>,
) -> Self {
RouteFuture { future }
}
}
impl<B> Future for RouteFuture<B> {
type Output = Result<Response<BoxBody>, Infallible>;
#[inline]
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
self.project().future.poll(cx)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn traits() {
use crate::tests::*;
assert_send::<Route<()>>();
}
}