mirror of
https://github.com/tokio-rs/axum.git
synced 2025-03-30 11:19:20 +02:00
Write a few docs
This commit is contained in:
parent
46f74895ce
commit
21c96e0aa1
9 changed files with 441 additions and 72 deletions
|
@ -14,7 +14,7 @@ use tower_http::{
|
|||
use tower_web::{
|
||||
body::Body,
|
||||
extract::{BytesMaxLength, Extension, UrlParams},
|
||||
get, route, Handler,
|
||||
prelude::*,
|
||||
};
|
||||
|
||||
#[tokio::main]
|
||||
|
|
|
@ -77,6 +77,10 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// A boxed error trait object that implements [`std::error::Error`].
|
||||
///
|
||||
/// This is necessary for compatibility with middleware that changes the error
|
||||
/// type of the response body.
|
||||
// work around for `BoxError` not implementing `std::error::Error`
|
||||
//
|
||||
// This is currently required since tower-http's Compression middleware's body type's
|
||||
|
|
142
src/handler.rs
142
src/handler.rs
|
@ -2,7 +2,7 @@ use crate::{
|
|||
body::{Body, BoxBody},
|
||||
extract::FromRequest,
|
||||
response::IntoResponse,
|
||||
routing::{BoxResponseBody, EmptyRouter, MethodFilter},
|
||||
routing::{BoxResponseBody, EmptyRouter, MethodFilter, RouteFuture},
|
||||
service::HandleError,
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
|
@ -15,7 +15,28 @@ use std::{
|
|||
marker::PhantomData,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
use tower::{util::Oneshot, BoxError, Layer, Service, ServiceExt};
|
||||
use tower::{BoxError, Layer, Service, ServiceExt};
|
||||
|
||||
pub fn any<H, T>(handler: H) -> OnMethod<IntoService<H, T>, EmptyRouter>
|
||||
where
|
||||
H: Handler<T>,
|
||||
{
|
||||
on(MethodFilter::Any, handler)
|
||||
}
|
||||
|
||||
pub fn connect<H, T>(handler: H) -> OnMethod<IntoService<H, T>, EmptyRouter>
|
||||
where
|
||||
H: Handler<T>,
|
||||
{
|
||||
on(MethodFilter::Connect, handler)
|
||||
}
|
||||
|
||||
pub fn delete<H, T>(handler: H) -> OnMethod<IntoService<H, T>, EmptyRouter>
|
||||
where
|
||||
H: Handler<T>,
|
||||
{
|
||||
on(MethodFilter::Delete, handler)
|
||||
}
|
||||
|
||||
pub fn get<H, T>(handler: H) -> OnMethod<IntoService<H, T>, EmptyRouter>
|
||||
where
|
||||
|
@ -24,6 +45,27 @@ where
|
|||
on(MethodFilter::Get, handler)
|
||||
}
|
||||
|
||||
pub fn head<H, T>(handler: H) -> OnMethod<IntoService<H, T>, EmptyRouter>
|
||||
where
|
||||
H: Handler<T>,
|
||||
{
|
||||
on(MethodFilter::Head, handler)
|
||||
}
|
||||
|
||||
pub fn options<H, T>(handler: H) -> OnMethod<IntoService<H, T>, EmptyRouter>
|
||||
where
|
||||
H: Handler<T>,
|
||||
{
|
||||
on(MethodFilter::Options, handler)
|
||||
}
|
||||
|
||||
pub fn patch<H, T>(handler: H) -> OnMethod<IntoService<H, T>, EmptyRouter>
|
||||
where
|
||||
H: Handler<T>,
|
||||
{
|
||||
on(MethodFilter::Patch, handler)
|
||||
}
|
||||
|
||||
pub fn post<H, T>(handler: H) -> OnMethod<IntoService<H, T>, EmptyRouter>
|
||||
where
|
||||
H: Handler<T>,
|
||||
|
@ -31,6 +73,20 @@ where
|
|||
on(MethodFilter::Post, handler)
|
||||
}
|
||||
|
||||
pub fn put<H, T>(handler: H) -> OnMethod<IntoService<H, T>, EmptyRouter>
|
||||
where
|
||||
H: Handler<T>,
|
||||
{
|
||||
on(MethodFilter::Put, handler)
|
||||
}
|
||||
|
||||
pub fn trace<H, T>(handler: H) -> OnMethod<IntoService<H, T>, EmptyRouter>
|
||||
where
|
||||
H: Handler<T>,
|
||||
{
|
||||
on(MethodFilter::Trace, handler)
|
||||
}
|
||||
|
||||
pub fn on<H, T>(method: MethodFilter, handler: H) -> OnMethod<IntoService<H, T>, EmptyRouter>
|
||||
where
|
||||
H: Handler<T>,
|
||||
|
@ -216,7 +272,7 @@ where
|
|||
{
|
||||
type Response = Response<BoxBody>;
|
||||
type Error = Infallible;
|
||||
type Future = future::BoxFuture<'static, Result<Self::Response, Self::Error>>;
|
||||
type Future = IntoServiceFuture;
|
||||
|
||||
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
// `IntoService` can only be constructed from async functions which are always ready, or from
|
||||
|
@ -227,13 +283,19 @@ where
|
|||
|
||||
fn call(&mut self, req: Request<Body>) -> Self::Future {
|
||||
let handler = self.handler.clone();
|
||||
Box::pin(async move {
|
||||
let future = Box::pin(async move {
|
||||
let res = Handler::call(handler, req).await;
|
||||
Ok(res)
|
||||
})
|
||||
});
|
||||
IntoServiceFuture(future)
|
||||
}
|
||||
}
|
||||
|
||||
opaque_future! {
|
||||
pub type IntoServiceFuture =
|
||||
future::BoxFuture<'static, Result<Response<BoxBody>, Infallible>>;
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct OnMethod<S, F> {
|
||||
pub(crate) method: MethodFilter,
|
||||
|
@ -242,6 +304,27 @@ pub struct OnMethod<S, F> {
|
|||
}
|
||||
|
||||
impl<S, F> OnMethod<S, F> {
|
||||
pub fn any<H, T>(self, handler: H) -> OnMethod<IntoService<H, T>, Self>
|
||||
where
|
||||
H: Handler<T>,
|
||||
{
|
||||
self.on(MethodFilter::Any, handler)
|
||||
}
|
||||
|
||||
pub fn connect<H, T>(self, handler: H) -> OnMethod<IntoService<H, T>, Self>
|
||||
where
|
||||
H: Handler<T>,
|
||||
{
|
||||
self.on(MethodFilter::Connect, handler)
|
||||
}
|
||||
|
||||
pub fn delete<H, T>(self, handler: H) -> OnMethod<IntoService<H, T>, Self>
|
||||
where
|
||||
H: Handler<T>,
|
||||
{
|
||||
self.on(MethodFilter::Delete, handler)
|
||||
}
|
||||
|
||||
pub fn get<H, T>(self, handler: H) -> OnMethod<IntoService<H, T>, Self>
|
||||
where
|
||||
H: Handler<T>,
|
||||
|
@ -249,6 +332,27 @@ impl<S, F> OnMethod<S, F> {
|
|||
self.on(MethodFilter::Get, handler)
|
||||
}
|
||||
|
||||
pub fn head<H, T>(self, handler: H) -> OnMethod<IntoService<H, T>, Self>
|
||||
where
|
||||
H: Handler<T>,
|
||||
{
|
||||
self.on(MethodFilter::Head, handler)
|
||||
}
|
||||
|
||||
pub fn options<H, T>(self, handler: H) -> OnMethod<IntoService<H, T>, Self>
|
||||
where
|
||||
H: Handler<T>,
|
||||
{
|
||||
self.on(MethodFilter::Options, handler)
|
||||
}
|
||||
|
||||
pub fn patch<H, T>(self, handler: H) -> OnMethod<IntoService<H, T>, Self>
|
||||
where
|
||||
H: Handler<T>,
|
||||
{
|
||||
self.on(MethodFilter::Patch, handler)
|
||||
}
|
||||
|
||||
pub fn post<H, T>(self, handler: H) -> OnMethod<IntoService<H, T>, Self>
|
||||
where
|
||||
H: Handler<T>,
|
||||
|
@ -256,6 +360,20 @@ impl<S, F> OnMethod<S, F> {
|
|||
self.on(MethodFilter::Post, handler)
|
||||
}
|
||||
|
||||
pub fn put<H, T>(self, handler: H) -> OnMethod<IntoService<H, T>, Self>
|
||||
where
|
||||
H: Handler<T>,
|
||||
{
|
||||
self.on(MethodFilter::Put, handler)
|
||||
}
|
||||
|
||||
pub fn trace<H, T>(self, handler: H) -> OnMethod<IntoService<H, T>, Self>
|
||||
where
|
||||
H: Handler<T>,
|
||||
{
|
||||
self.on(MethodFilter::Trace, handler)
|
||||
}
|
||||
|
||||
pub fn on<H, T>(self, method: MethodFilter, handler: H) -> OnMethod<IntoService<H, T>, Self>
|
||||
where
|
||||
H: Handler<T>,
|
||||
|
@ -268,8 +386,6 @@ impl<S, F> OnMethod<S, F> {
|
|||
}
|
||||
}
|
||||
|
||||
// this is identical to `routing::OnMethod`'s implementation. Would be nice to find a way to clean
|
||||
// that up, but not sure its possible.
|
||||
impl<S, F, SB, FB> Service<Request<Body>> for OnMethod<S, F>
|
||||
where
|
||||
S: Service<Request<Body>, Response = Response<SB>, Error = Infallible> + Clone,
|
||||
|
@ -282,24 +398,20 @@ where
|
|||
{
|
||||
type Response = Response<BoxBody>;
|
||||
type Error = Infallible;
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
type Future = future::Either<
|
||||
BoxResponseBody<Oneshot<S, Request<Body>>>,
|
||||
BoxResponseBody<Oneshot<F, Request<Body>>>,
|
||||
>;
|
||||
type Future = RouteFuture<S, F>;
|
||||
|
||||
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
fn call(&mut self, req: Request<Body>) -> Self::Future {
|
||||
if self.method.matches(req.method()) {
|
||||
let f = if self.method.matches(req.method()) {
|
||||
let response_future = self.svc.clone().oneshot(req);
|
||||
future::Either::Left(BoxResponseBody(response_future))
|
||||
} else {
|
||||
let response_future = self.fallback.clone().oneshot(req);
|
||||
future::Either::Right(BoxResponseBody(response_future))
|
||||
}
|
||||
};
|
||||
RouteFuture(f)
|
||||
}
|
||||
}
|
||||
|
|
106
src/lib.rs
106
src/lib.rs
|
@ -500,7 +500,6 @@
|
|||
//! use tower_http::services::ServeFile;
|
||||
//! use http::Response;
|
||||
//! use std::convert::Infallible;
|
||||
//! use tower::{service_fn, BoxError};
|
||||
//!
|
||||
//! fn api_routes() -> BoxRoute<BoxBody> {
|
||||
//! route("/users", get(|_: Request<Body>| async { /* ... */ })).boxed()
|
||||
|
@ -571,7 +570,7 @@
|
|||
rust_2018_idioms,
|
||||
future_incompatible,
|
||||
nonstandard_style,
|
||||
// missing_docs
|
||||
missing_docs
|
||||
)]
|
||||
#![deny(unreachable_pub, broken_intra_doc_links, private_in_public)]
|
||||
#![allow(
|
||||
|
@ -592,6 +591,9 @@ use routing::{EmptyRouter, Route};
|
|||
use std::convert::Infallible;
|
||||
use tower::{BoxError, Service};
|
||||
|
||||
#[macro_use]
|
||||
pub(crate) mod macros;
|
||||
|
||||
pub mod body;
|
||||
pub mod extract;
|
||||
pub mod handler;
|
||||
|
@ -602,12 +604,6 @@ pub mod service;
|
|||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
#[doc(inline)]
|
||||
pub use self::{
|
||||
handler::{get, on, post, Handler},
|
||||
routing::RoutingDsl,
|
||||
};
|
||||
|
||||
pub use async_trait::async_trait;
|
||||
pub use tower_http::add_extension::{AddExtension, AddExtensionLayer};
|
||||
|
||||
|
@ -615,24 +611,102 @@ pub mod prelude {
|
|||
//! Re-exports of important traits, types, and functions used with tower-web. Meant to be glob
|
||||
//! imported.
|
||||
|
||||
pub use crate::{
|
||||
body::Body,
|
||||
extract,
|
||||
handler::{get, on, post, Handler},
|
||||
response, route,
|
||||
routing::RoutingDsl,
|
||||
pub use crate::body::Body;
|
||||
pub use crate::extract;
|
||||
pub use crate::handler::{
|
||||
any, connect, delete, get, head, options, patch, post, put, trace, Handler,
|
||||
};
|
||||
pub use crate::response;
|
||||
pub use crate::route;
|
||||
pub use crate::routing::RoutingDsl;
|
||||
pub use http::Request;
|
||||
}
|
||||
|
||||
pub fn route<S>(spec: &str, svc: S) -> Route<S, EmptyRouter>
|
||||
/// Create a route.
|
||||
///
|
||||
/// `description` is a string of path segments separated by `/`. Each segment
|
||||
/// can be either concrete or a capture:
|
||||
///
|
||||
/// - `/foo/bar/baz` will only match requests where the path is `/foo/bar/bar`.
|
||||
/// - `/:foo` will match any route with exactly one segment _and_ it will
|
||||
/// capture the first segment and store it only the key `foo`.
|
||||
///
|
||||
/// `service` is the [`Service`] that should receive the request if the path
|
||||
/// matches `description`.
|
||||
///
|
||||
/// Note that `service`'s error type must be [`Infallible`] meaning you must
|
||||
/// handle all errors. If you're creating handlers from async functions that is
|
||||
/// handled automatically but if you're routing to some other [`Service`] you
|
||||
/// might need to use [`handle_error`](ServiceExt::handle_error) to map errors
|
||||
/// into responses.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use tower_web::prelude::*;
|
||||
/// # use std::convert::Infallible;
|
||||
/// # use http::Response;
|
||||
/// # let service = tower::service_fn(|_: Request<Body>| async {
|
||||
/// # Ok::<Response<Body>, Infallible>(Response::new(Body::empty()))
|
||||
/// # });
|
||||
///
|
||||
/// route("/", service);
|
||||
/// route("/users", service);
|
||||
/// route("/users/:id", service);
|
||||
/// route("/api/:version/users/:id/action", service);
|
||||
/// ```
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if `description` doesn't start with `/`.
|
||||
pub fn route<S>(description: &str, service: S) -> Route<S, EmptyRouter>
|
||||
where
|
||||
S: Service<Request<Body>, Error = Infallible> + Clone,
|
||||
{
|
||||
routing::EmptyRouter.route(spec, svc)
|
||||
use routing::RoutingDsl;
|
||||
|
||||
routing::EmptyRouter.route(description, service)
|
||||
}
|
||||
|
||||
/// Extension trait that adds additional methods to [`Service`].
|
||||
pub trait ServiceExt<B>: Service<Request<Body>, Response = Response<B>> {
|
||||
/// Handle errors from a service.
|
||||
///
|
||||
/// tower-web requires all handles to never return errors. If you route to
|
||||
/// [`Service`], not created by tower-web, who's error isn't `Infallible`
|
||||
/// you can use this combinator to handle the error.
|
||||
///
|
||||
/// `handle_error` takes a closure that will map errors from the service
|
||||
/// into responses. The closure's return type must implement
|
||||
/// [`IntoResponse`].
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// use tower_web::{
|
||||
/// service, prelude::*,
|
||||
/// ServiceExt,
|
||||
/// };
|
||||
/// use http::Response;
|
||||
/// use tower::{service_fn, BoxError};
|
||||
///
|
||||
/// // A service that might fail with `std::io::Error`
|
||||
/// let service = service_fn(|_: Request<Body>| async {
|
||||
/// let res = Response::new(Body::empty());
|
||||
/// Ok::<_, std::io::Error>(res)
|
||||
/// });
|
||||
///
|
||||
/// let app = route(
|
||||
/// "/",
|
||||
/// service.handle_error(|error: std::io::Error| {
|
||||
/// // Handle error by returning something that implements `IntoResponse`
|
||||
/// }),
|
||||
/// );
|
||||
/// #
|
||||
/// # async {
|
||||
/// # hyper::Server::bind(&"".parse().unwrap()).serve(tower::make::Shared::new(app)).await;
|
||||
/// # };
|
||||
/// ```
|
||||
fn handle_error<F, Res>(self, f: F) -> service::HandleError<Self, F>
|
||||
where
|
||||
Self: Sized,
|
||||
|
|
34
src/macros.rs
Normal file
34
src/macros.rs
Normal file
|
@ -0,0 +1,34 @@
|
|||
//! Internal macros
|
||||
|
||||
macro_rules! opaque_future {
|
||||
($(#[$m:meta])* pub type $name:ident = $actual:ty;) => {
|
||||
opaque_future! {
|
||||
$(#[$m])*
|
||||
pub type $name<> = $actual;
|
||||
}
|
||||
};
|
||||
|
||||
($(#[$m:meta])* pub type $name:ident<$($param:ident),*> = $actual:ty;) => {
|
||||
#[pin_project::pin_project]
|
||||
$(#[$m])*
|
||||
pub struct $name<$($param),*>(#[pin] pub(crate) $actual)
|
||||
where;
|
||||
|
||||
impl<$($param),*> std::fmt::Debug for $name<$($param),*> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_tuple(stringify!($name)).field(&format_args!("...")).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<$($param),*> std::future::Future for $name<$($param),*>
|
||||
where
|
||||
$actual: std::future::Future,
|
||||
{
|
||||
type Output = <$actual as std::future::Future>::Output;
|
||||
#[inline]
|
||||
fn poll(self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> std::task::Poll<Self::Output> {
|
||||
self.project().0.poll(cx)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
|
@ -5,7 +5,11 @@ use serde::Serialize;
|
|||
use std::{borrow::Cow, convert::Infallible};
|
||||
use tower::util::Either;
|
||||
|
||||
/// Trait for generating responses.
|
||||
///
|
||||
/// Types that implement `IntoResponse` can be returned from handlers.
|
||||
pub trait IntoResponse {
|
||||
/// Create a response.
|
||||
fn into_response(self) -> Response<Body>;
|
||||
}
|
||||
|
||||
|
@ -153,6 +157,9 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// An HTML response.
|
||||
///
|
||||
/// Will automatically get `Content-Type: text/html`.
|
||||
pub struct Html<T>(pub T);
|
||||
|
||||
impl<T> IntoResponse for Html<T>
|
||||
|
@ -167,6 +174,30 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// A JSON response.
|
||||
///
|
||||
/// Can be created from any type that implements [`serde::Serialize`].
|
||||
///
|
||||
/// Will automatically get `Content-Type: application/json`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use serde_json::json;
|
||||
/// use tower_web::{body::Body, response::{Json, IntoResponse}};
|
||||
/// use http::{Response, header::CONTENT_TYPE};
|
||||
///
|
||||
/// let json = json!({
|
||||
/// "data": 42,
|
||||
/// });
|
||||
///
|
||||
/// let response: Response<Body> = Json(json).into_response();
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// response.headers().get(CONTENT_TYPE).unwrap(),
|
||||
/// "application/json",
|
||||
/// );
|
||||
/// ```
|
||||
pub struct Json<T>(pub T);
|
||||
|
||||
impl<T> IntoResponse for Json<T>
|
||||
|
|
|
@ -64,23 +64,23 @@ pub struct Route<S, F> {
|
|||
}
|
||||
|
||||
pub trait RoutingDsl: Sized {
|
||||
fn route<T>(self, spec: &str, svc: T) -> Route<T, Self>
|
||||
fn route<T>(self, description: &str, svc: T) -> Route<T, Self>
|
||||
where
|
||||
T: Service<Request<Body>, Error = Infallible> + Clone,
|
||||
{
|
||||
Route {
|
||||
pattern: PathPattern::new(spec),
|
||||
pattern: PathPattern::new(description),
|
||||
svc,
|
||||
fallback: self,
|
||||
}
|
||||
}
|
||||
|
||||
fn nest<T>(self, spec: &str, svc: T) -> Nested<T, Self>
|
||||
fn nest<T>(self, description: &str, svc: T) -> Nested<T, Self>
|
||||
where
|
||||
T: Service<Request<Body>, Error = Infallible> + Clone,
|
||||
{
|
||||
Nested {
|
||||
pattern: PathPattern::new(spec),
|
||||
pattern: PathPattern::new(description),
|
||||
svc,
|
||||
fallback: self,
|
||||
}
|
||||
|
@ -125,26 +125,50 @@ where
|
|||
{
|
||||
type Response = Response<BoxBody>;
|
||||
type Error = Infallible;
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
type Future = future::Either<
|
||||
BoxResponseBody<Oneshot<S, Request<Body>>>,
|
||||
BoxResponseBody<Oneshot<F, Request<Body>>>,
|
||||
>;
|
||||
type Future = RouteFuture<S, F>;
|
||||
|
||||
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
fn call(&mut self, mut req: Request<Body>) -> Self::Future {
|
||||
if let Some(captures) = self.pattern.full_match(req.uri().path()) {
|
||||
let f = if let Some(captures) = self.pattern.full_match(req.uri().path()) {
|
||||
insert_url_params(&mut req, captures);
|
||||
let response_future = self.svc.clone().oneshot(req);
|
||||
future::Either::Left(BoxResponseBody(response_future))
|
||||
} else {
|
||||
let response_future = self.fallback.clone().oneshot(req);
|
||||
future::Either::Right(BoxResponseBody(response_future))
|
||||
}
|
||||
};
|
||||
RouteFuture(f)
|
||||
}
|
||||
}
|
||||
|
||||
#[pin_project]
|
||||
pub struct RouteFuture<S, F>(
|
||||
#[pin]
|
||||
pub(crate) future::Either<
|
||||
BoxResponseBody<Oneshot<S, Request<Body>>>,
|
||||
BoxResponseBody<Oneshot<F, Request<Body>>>,
|
||||
>,
|
||||
)
|
||||
where
|
||||
S: Service<Request<Body>>,
|
||||
F: Service<Request<Body>>;
|
||||
|
||||
impl<S, F, SB, FB> Future for RouteFuture<S, F>
|
||||
where
|
||||
S: Service<Request<Body>, Response = Response<SB>, Error = Infallible>,
|
||||
SB: http_body::Body<Data = Bytes> + Send + Sync + 'static,
|
||||
SB::Error: Into<BoxError>,
|
||||
F: Service<Request<Body>, Response = Response<FB>, Error = Infallible>,
|
||||
FB: http_body::Body<Data = Bytes> + Send + Sync + 'static,
|
||||
FB::Error: Into<BoxError>,
|
||||
{
|
||||
type Output = Result<Response<BoxBody>, Infallible>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
self.project().0.poll(cx)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -187,22 +211,27 @@ pub struct EmptyRouter;
|
|||
|
||||
impl RoutingDsl for EmptyRouter {}
|
||||
|
||||
impl<R> Service<R> for EmptyRouter {
|
||||
impl Service<Request<Body>> for EmptyRouter {
|
||||
type Response = Response<Body>;
|
||||
type Error = Infallible;
|
||||
type Future = future::Ready<Result<Self::Response, Self::Error>>;
|
||||
type Future = EmptyRouterFuture;
|
||||
|
||||
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
fn call(&mut self, _req: R) -> Self::Future {
|
||||
fn call(&mut self, _req: Request<Body>) -> Self::Future {
|
||||
let mut res = Response::new(Body::empty());
|
||||
*res.status_mut() = StatusCode::NOT_FOUND;
|
||||
future::ok(res)
|
||||
EmptyRouterFuture(future::ok(res))
|
||||
}
|
||||
}
|
||||
|
||||
opaque_future! {
|
||||
pub type EmptyRouterFuture =
|
||||
future::Ready<Result<Response<Body>, Infallible>>;
|
||||
}
|
||||
|
||||
// ===== PathPattern =====
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -216,6 +245,11 @@ struct Inner {
|
|||
|
||||
impl PathPattern {
|
||||
pub(crate) fn new(pattern: &str) -> Self {
|
||||
assert!(
|
||||
pattern.starts_with('/'),
|
||||
"Route description must start with a `/`"
|
||||
);
|
||||
|
||||
let mut capture_group_names = Vec::new();
|
||||
|
||||
let pattern = pattern
|
||||
|
@ -309,7 +343,7 @@ where
|
|||
{
|
||||
type Response = Response<BoxBody>;
|
||||
type Error = Infallible;
|
||||
type Future = BoxRouteResponseFuture<B>;
|
||||
type Future = BoxRouteFuture<B>;
|
||||
|
||||
#[inline]
|
||||
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
|
@ -318,19 +352,19 @@ where
|
|||
|
||||
#[inline]
|
||||
fn call(&mut self, req: Request<Body>) -> Self::Future {
|
||||
BoxRouteResponseFuture(self.0.clone().oneshot(req))
|
||||
BoxRouteFuture(self.0.clone().oneshot(req))
|
||||
}
|
||||
}
|
||||
|
||||
#[pin_project]
|
||||
pub struct BoxRouteResponseFuture<B>(#[pin] InnerFuture<B>);
|
||||
pub struct BoxRouteFuture<B>(#[pin] InnerFuture<B>);
|
||||
|
||||
type InnerFuture<B> = Oneshot<
|
||||
Buffer<BoxService<Request<Body>, Response<B>, Infallible>, Request<Body>>,
|
||||
Request<Body>,
|
||||
>;
|
||||
|
||||
impl<B> Future for BoxRouteResponseFuture<B>
|
||||
impl<B> Future for BoxRouteFuture<B>
|
||||
where
|
||||
B: http_body::Body<Data = Bytes> + Send + Sync + 'static,
|
||||
B::Error: Into<BoxError> + Send + Sync + 'static,
|
||||
|
@ -418,12 +452,12 @@ where
|
|||
|
||||
// ===== nesting =====
|
||||
|
||||
pub fn nest<S>(spec: &str, svc: S) -> Nested<S, EmptyRouter>
|
||||
pub fn nest<S>(description: &str, svc: S) -> Nested<S, EmptyRouter>
|
||||
where
|
||||
S: Service<Request<Body>, Error = Infallible> + Clone,
|
||||
{
|
||||
Nested {
|
||||
pattern: PathPattern::new(spec),
|
||||
pattern: PathPattern::new(description),
|
||||
svc,
|
||||
fallback: EmptyRouter,
|
||||
}
|
||||
|
@ -450,19 +484,14 @@ where
|
|||
{
|
||||
type Response = Response<BoxBody>;
|
||||
type Error = Infallible;
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
type Future = future::Either<
|
||||
BoxResponseBody<Oneshot<S, Request<Body>>>,
|
||||
BoxResponseBody<Oneshot<F, Request<Body>>>,
|
||||
>;
|
||||
type Future = RouteFuture<S, F>;
|
||||
|
||||
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
fn call(&mut self, mut req: Request<Body>) -> Self::Future {
|
||||
if let Some((prefix, captures)) = self.pattern.prefix_match(req.uri().path()) {
|
||||
let f = if let Some((prefix, captures)) = self.pattern.prefix_match(req.uri().path()) {
|
||||
let without_prefix = strip_prefix(req.uri(), prefix);
|
||||
*req.uri_mut() = without_prefix;
|
||||
|
||||
|
@ -472,7 +501,8 @@ where
|
|||
} else {
|
||||
let response_future = self.fallback.clone().oneshot(req);
|
||||
future::Either::Right(BoxResponseBody(response_future))
|
||||
}
|
||||
};
|
||||
RouteFuture(f)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
102
src/service.rs
102
src/service.rs
|
@ -1,7 +1,7 @@
|
|||
use crate::{
|
||||
body::{Body, BoxBody},
|
||||
response::IntoResponse,
|
||||
routing::{BoxResponseBody, EmptyRouter, MethodFilter},
|
||||
routing::{BoxResponseBody, EmptyRouter, MethodFilter, RouteFuture},
|
||||
};
|
||||
use bytes::Bytes;
|
||||
use futures_util::future;
|
||||
|
@ -17,14 +17,46 @@ use std::{
|
|||
};
|
||||
use tower::{util::Oneshot, BoxError, Service, ServiceExt as _};
|
||||
|
||||
pub fn any<S>(svc: S) -> OnMethod<S, EmptyRouter> {
|
||||
on(MethodFilter::Any, svc)
|
||||
}
|
||||
|
||||
pub fn connect<S>(svc: S) -> OnMethod<S, EmptyRouter> {
|
||||
on(MethodFilter::Connect, svc)
|
||||
}
|
||||
|
||||
pub fn delete<S>(svc: S) -> OnMethod<S, EmptyRouter> {
|
||||
on(MethodFilter::Delete, svc)
|
||||
}
|
||||
|
||||
pub fn get<S>(svc: S) -> OnMethod<S, EmptyRouter> {
|
||||
on(MethodFilter::Get, svc)
|
||||
}
|
||||
|
||||
pub fn head<S>(svc: S) -> OnMethod<S, EmptyRouter> {
|
||||
on(MethodFilter::Head, svc)
|
||||
}
|
||||
|
||||
pub fn options<S>(svc: S) -> OnMethod<S, EmptyRouter> {
|
||||
on(MethodFilter::Options, svc)
|
||||
}
|
||||
|
||||
pub fn patch<S>(svc: S) -> OnMethod<S, EmptyRouter> {
|
||||
on(MethodFilter::Patch, svc)
|
||||
}
|
||||
|
||||
pub fn post<S>(svc: S) -> OnMethod<S, EmptyRouter> {
|
||||
on(MethodFilter::Post, svc)
|
||||
}
|
||||
|
||||
pub fn put<S>(svc: S) -> OnMethod<S, EmptyRouter> {
|
||||
on(MethodFilter::Put, svc)
|
||||
}
|
||||
|
||||
pub fn trace<S>(svc: S) -> OnMethod<S, EmptyRouter> {
|
||||
on(MethodFilter::Trace, svc)
|
||||
}
|
||||
|
||||
pub fn on<S>(method: MethodFilter, svc: S) -> OnMethod<S, EmptyRouter> {
|
||||
OnMethod {
|
||||
method,
|
||||
|
@ -41,6 +73,27 @@ pub struct OnMethod<S, F> {
|
|||
}
|
||||
|
||||
impl<S, F> OnMethod<S, F> {
|
||||
pub fn any<T>(self, svc: T) -> OnMethod<T, Self>
|
||||
where
|
||||
T: Service<Request<Body>> + Clone,
|
||||
{
|
||||
self.on(MethodFilter::Any, svc)
|
||||
}
|
||||
|
||||
pub fn connect<T>(self, svc: T) -> OnMethod<T, Self>
|
||||
where
|
||||
T: Service<Request<Body>> + Clone,
|
||||
{
|
||||
self.on(MethodFilter::Connect, svc)
|
||||
}
|
||||
|
||||
pub fn delete<T>(self, svc: T) -> OnMethod<T, Self>
|
||||
where
|
||||
T: Service<Request<Body>> + Clone,
|
||||
{
|
||||
self.on(MethodFilter::Delete, svc)
|
||||
}
|
||||
|
||||
pub fn get<T>(self, svc: T) -> OnMethod<T, Self>
|
||||
where
|
||||
T: Service<Request<Body>> + Clone,
|
||||
|
@ -48,6 +101,27 @@ impl<S, F> OnMethod<S, F> {
|
|||
self.on(MethodFilter::Get, svc)
|
||||
}
|
||||
|
||||
pub fn head<T>(self, svc: T) -> OnMethod<T, Self>
|
||||
where
|
||||
T: Service<Request<Body>> + Clone,
|
||||
{
|
||||
self.on(MethodFilter::Head, svc)
|
||||
}
|
||||
|
||||
pub fn options<T>(self, svc: T) -> OnMethod<T, Self>
|
||||
where
|
||||
T: Service<Request<Body>> + Clone,
|
||||
{
|
||||
self.on(MethodFilter::Options, svc)
|
||||
}
|
||||
|
||||
pub fn patch<T>(self, svc: T) -> OnMethod<T, Self>
|
||||
where
|
||||
T: Service<Request<Body>> + Clone,
|
||||
{
|
||||
self.on(MethodFilter::Patch, svc)
|
||||
}
|
||||
|
||||
pub fn post<T>(self, svc: T) -> OnMethod<T, Self>
|
||||
where
|
||||
T: Service<Request<Body>> + Clone,
|
||||
|
@ -55,6 +129,20 @@ impl<S, F> OnMethod<S, F> {
|
|||
self.on(MethodFilter::Post, svc)
|
||||
}
|
||||
|
||||
pub fn put<T>(self, svc: T) -> OnMethod<T, Self>
|
||||
where
|
||||
T: Service<Request<Body>> + Clone,
|
||||
{
|
||||
self.on(MethodFilter::Put, svc)
|
||||
}
|
||||
|
||||
pub fn trace<T>(self, svc: T) -> OnMethod<T, Self>
|
||||
where
|
||||
T: Service<Request<Body>> + Clone,
|
||||
{
|
||||
self.on(MethodFilter::Trace, svc)
|
||||
}
|
||||
|
||||
pub fn on<T>(self, method: MethodFilter, svc: T) -> OnMethod<T, Self>
|
||||
where
|
||||
T: Service<Request<Body>> + Clone,
|
||||
|
@ -81,25 +169,21 @@ where
|
|||
{
|
||||
type Response = Response<BoxBody>;
|
||||
type Error = Infallible;
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
type Future = future::Either<
|
||||
BoxResponseBody<Oneshot<S, Request<Body>>>,
|
||||
BoxResponseBody<Oneshot<F, Request<Body>>>,
|
||||
>;
|
||||
type Future = RouteFuture<S, F>;
|
||||
|
||||
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
fn call(&mut self, req: Request<Body>) -> Self::Future {
|
||||
if self.method.matches(req.method()) {
|
||||
let f = if self.method.matches(req.method()) {
|
||||
let response_future = self.svc.clone().oneshot(req);
|
||||
future::Either::Left(BoxResponseBody(response_future))
|
||||
} else {
|
||||
let response_future = self.fallback.clone().oneshot(req);
|
||||
future::Either::Right(BoxResponseBody(response_future))
|
||||
}
|
||||
};
|
||||
RouteFuture(f)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::{extract, get, on, post, route, routing::MethodFilter, service, Handler, RoutingDsl};
|
||||
use crate::{handler::on, prelude::*, routing::MethodFilter, service};
|
||||
use http::{Request, Response, StatusCode};
|
||||
use hyper::{Body, Server};
|
||||
use serde::Deserialize;
|
||||
|
|
Loading…
Add table
Reference in a new issue