mirror of
https://github.com/tokio-rs/axum.git
synced 2025-01-01 08:56:15 +01:00
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:
parent
91981db8c7
commit
302a720271
6 changed files with 278 additions and 243 deletions
|
@ -1,19 +1,15 @@
|
||||||
//! Future types.
|
//! Future types.
|
||||||
|
|
||||||
use crate::{body::BoxBody, clone_box_service::CloneBoxService};
|
use crate::body::BoxBody;
|
||||||
use futures_util::future::Either;
|
use futures_util::future::Either;
|
||||||
use http::{Request, Response};
|
use http::{Request, Response};
|
||||||
use pin_project_lite::pin_project;
|
use std::{convert::Infallible, future::ready};
|
||||||
use std::{
|
|
||||||
convert::Infallible,
|
|
||||||
future::{ready, Future},
|
|
||||||
pin::Pin,
|
|
||||||
task::{Context, Poll},
|
|
||||||
};
|
|
||||||
use tower::util::Oneshot;
|
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! {
|
opaque_future! {
|
||||||
/// Response future for [`Router`](super::Router).
|
/// Response future for [`Router`](super::Router).
|
||||||
|
@ -33,62 +29,3 @@ impl<B> RouterFuture<B> {
|
||||||
RouterFuture::new(Either::Right(ready(Ok(response))))
|
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>>;
|
|
||||||
}
|
|
||||||
|
|
57
src/routing/into_make_service.rs
Normal file
57
src/routing/into_make_service.rs
Normal 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<()>>();
|
||||||
|
}
|
||||||
|
}
|
|
@ -67,3 +67,16 @@ opaque_future! {
|
||||||
pub type MethodNotAllowedFuture<E> =
|
pub type MethodNotAllowedFuture<E> =
|
||||||
std::future::Ready<Result<Response<BoxBody>, 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>>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
//! Routing between [`Service`]s and handlers.
|
//! Routing between [`Service`]s and handlers.
|
||||||
|
|
||||||
use self::future::{NestedFuture, RouteFuture, RouterFuture};
|
use self::future::RouterFuture;
|
||||||
use self::not_found::NotFound;
|
use self::not_found::NotFound;
|
||||||
use crate::{
|
use crate::{
|
||||||
body::{box_body, Body, BoxBody},
|
body::{box_body, Body, BoxBody},
|
||||||
clone_box_service::CloneBoxService,
|
|
||||||
extract::{
|
extract::{
|
||||||
connect_info::{Connected, IntoMakeServiceWithConnectInfo},
|
connect_info::{Connected, IntoMakeServiceWithConnectInfo},
|
||||||
OriginalUri,
|
OriginalUri,
|
||||||
|
@ -19,7 +18,6 @@ use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
convert::Infallible,
|
convert::Infallible,
|
||||||
fmt,
|
fmt,
|
||||||
future::ready,
|
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
task::{Context, Poll},
|
task::{Context, Poll},
|
||||||
};
|
};
|
||||||
|
@ -32,12 +30,15 @@ pub mod future;
|
||||||
pub mod handler_method_router;
|
pub mod handler_method_router;
|
||||||
pub mod service_method_router;
|
pub mod service_method_router;
|
||||||
|
|
||||||
|
mod into_make_service;
|
||||||
mod method_filter;
|
mod method_filter;
|
||||||
mod method_not_allowed;
|
mod method_not_allowed;
|
||||||
|
mod nested;
|
||||||
mod not_found;
|
mod not_found;
|
||||||
|
mod route;
|
||||||
|
|
||||||
pub use self::method_filter::MethodFilter;
|
|
||||||
pub(crate) use self::method_not_allowed::MethodNotAllowed;
|
pub(crate) use self::method_not_allowed::MethodNotAllowed;
|
||||||
|
pub use self::{into_make_service::IntoMakeService, method_filter::MethodFilter, route::Route};
|
||||||
|
|
||||||
#[doc(no_inline)]
|
#[doc(no_inline)]
|
||||||
pub use self::handler_method_router::{
|
pub use self::handler_method_router::{
|
||||||
|
@ -353,7 +354,7 @@ where
|
||||||
panic!("Invalid route: {}", err);
|
panic!("Invalid route: {}", err);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.routes.insert(id, Route::new(Nested { svc }));
|
self.routes.insert(id, Route::new(nested::Nested { svc }));
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
@ -754,7 +755,8 @@ where
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
if let Some(tail) = match_.params.get(NEST_TAIL_PARAM) {
|
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);
|
insert_url_params(&mut req, params);
|
||||||
|
@ -769,9 +771,6 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
struct NestMatchTail(String);
|
|
||||||
|
|
||||||
impl<B> Service<Request<B>> for Router<B>
|
impl<B> Service<Request<B>> for Router<B>
|
||||||
where
|
where
|
||||||
B: Send + Sync + 'static,
|
B: Send + Sync + 'static,
|
||||||
|
@ -824,18 +823,33 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct UriStack(Vec<Uri>);
|
fn with_path(uri: &Uri, new_path: &str) -> Uri {
|
||||||
|
let path_and_query = if let Some(path_and_query) = uri.path_and_query() {
|
||||||
impl UriStack {
|
let new_path = if new_path.starts_with('/') {
|
||||||
fn push<B>(req: &mut Request<B>) {
|
Cow::Borrowed(new_path)
|
||||||
let uri = req.uri().clone();
|
|
||||||
|
|
||||||
if let Some(stack) = req.extensions_mut().get_mut::<Self>() {
|
|
||||||
stack.0.push(uri);
|
|
||||||
} else {
|
} 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
|
// 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,
|
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.
|
/// Wrapper around `matchit::Node` that supports merging two `Node`s.
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
struct Node {
|
struct Node {
|
||||||
|
@ -1109,16 +979,5 @@ mod tests {
|
||||||
use crate::tests::*;
|
use crate::tests::*;
|
||||||
|
|
||||||
assert_send::<Router<()>>();
|
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
69
src/routing/nested.rs
Normal 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
100
src/routing/route.rs
Normal 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<()>>();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue