Created
December 21, 2017 05:31
-
-
Save bradleybeddoes/698dc5212a32a919d1950800ac6402fa to your computer and use it in GitHub Desktop.
support sub router delegation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
From d99ddd76b053a7c7e3bdf86d635c456816846231 Mon Sep 17 00:00:00 2001 | |
From: Bradley Beddoes <bradleybeddoes@gmail.com> | |
Date: Thu, 21 Dec 2017 15:16:45 +1000 | |
Subject: [PATCH 1/1] Support sub-router delegation in router builder | |
--- | |
src/router/builder/draw.rs | 2 - | |
src/router/builder/mod.rs | 91 +++++++++++++------------------- | |
src/router/builder/single.rs | 120 ++++++++++++++++++++++++++++++++++--------- | |
3 files changed, 132 insertions(+), 81 deletions(-) | |
diff --git a/src/router/builder/draw.rs b/src/router/builder/draw.rs | |
index 53aa09e..c7128e6 100644 | |
--- a/src/router/builder/draw.rs | |
+++ b/src/router/builder/draw.rs | |
@@ -2,7 +2,6 @@ use std::marker::PhantomData; | |
use hyper::Method; | |
-use router::route::Delegation; | |
use router::route::dispatch::{PipelineHandleChain, PipelineSet}; | |
use router::route::matcher::MethodOnlyRouteMatcher; | |
use router::request::path::NoopPathExtractor; | |
@@ -157,7 +156,6 @@ where | |
node_builder, | |
pipeline_chain: *pipeline_chain, | |
pipelines: pipelines.clone(), | |
- delegation: Delegation::Internal, | |
phantom: PhantomData, | |
} | |
} | |
diff --git a/src/router/builder/mod.rs b/src/router/builder/mod.rs | |
index a8f008b..249f179 100644 | |
--- a/src/router/builder/mod.rs | |
+++ b/src/router/builder/mod.rs | |
@@ -9,19 +9,18 @@ use std::marker::PhantomData; | |
use hyper::StatusCode; | |
use router::Router; | |
-use router::tree::TreeBuilder; | |
+use router::request::path::PathExtractor; | |
+use router::request::query_string::QueryStringExtractor; | |
use router::response::extender::ResponseExtender; | |
use router::response::finalizer::ResponseFinalizerBuilder; | |
-use router::route::Delegation; | |
-use router::route::matcher::RouteMatcher; | |
-use router::route::dispatch::{new_pipeline_set, finalize_pipeline_set, PipelineHandleChain, | |
+use router::route::dispatch::{finalize_pipeline_set, new_pipeline_set, PipelineHandleChain, | |
PipelineSet}; | |
-use router::request::path::PathExtractor; | |
-use router::request::query_string::QueryStringExtractor; | |
+use router::route::matcher::RouteMatcher; | |
+use router::tree::TreeBuilder; | |
use router::tree::node::NodeBuilder; | |
+pub use self::draw::{DefaultSingleRouteBuilder, DrawRoutes}; | |
pub use self::single::DefineSingleRoute; | |
-pub use self::draw::{DrawRoutes, DefaultSingleRouteBuilder}; | |
/// Builds a `Router` using the provided closure. Routes are defined using the `RouterBuilder` | |
/// value passed to the closure, and the `Router` is constructed before returning. | |
@@ -171,10 +170,8 @@ where | |
where | |
E: ResponseExtender + Send + Sync + 'static, | |
{ | |
- self.response_finalizer_builder.add( | |
- status_code, | |
- Box::new(extender), | |
- ) | |
+ self.response_finalizer_builder | |
+ .add(status_code, Box::new(extender)) | |
} | |
} | |
@@ -204,30 +201,17 @@ where | |
matcher: M, | |
pipeline_chain: C, | |
pipelines: PipelineSet<P>, | |
- delegation: Delegation, | |
phantom: PhantomData<(PE, QSE)>, | |
} | |
// Trait impls live with the traits. | |
impl<'a, M, C, P, PE, QSE> SingleRouteBuilder<'a, M, C, P, PE, QSE> | |
where | |
- M: RouteMatcher | |
- + Send | |
- + Sync | |
- + 'static, | |
- C: PipelineHandleChain<P> | |
- + Send | |
- + Sync | |
- + 'static, | |
+ M: RouteMatcher + Send + Sync + 'static, | |
+ C: PipelineHandleChain<P> + Send + Sync + 'static, | |
P: Send + Sync + 'static, | |
- PE: PathExtractor | |
- + Send | |
- + Sync | |
- + 'static, | |
- QSE: QueryStringExtractor | |
- + Send | |
- + Sync | |
- + 'static, | |
+ PE: PathExtractor + Send + Sync + 'static, | |
+ QSE: QueryStringExtractor + Send + Sync + 'static, | |
{ | |
/// Coerces the type of the internal `PhantomData`, to replace an extractor by changing the | |
/// type parameter without changing anything else. | |
@@ -241,7 +225,6 @@ where | |
matcher: self.matcher, | |
pipeline_chain: self.pipeline_chain, | |
pipelines: self.pipelines, | |
- delegation: self.delegation, | |
phantom: PhantomData, | |
} | |
} | |
@@ -253,19 +236,19 @@ mod tests { | |
use std::str::FromStr; | |
- use hyper::{Request, Response, StatusCode, Method, Uri}; | |
- use hyper::server::{NewService, Service}; | |
use futures::{Future, Stream}; | |
+ use hyper::{Method, Request, Response, StatusCode, Uri}; | |
+ use hyper::server::{NewService, Service}; | |
+ use handler::NewHandlerService; | |
+ use http::FormUrlDecoded; | |
+ use http::request::query_string; | |
use middleware::pipeline::new_pipeline; | |
use middleware::session::NewSessionMiddleware; | |
- use state::{State, StateData, FromState}; | |
- use handler::NewHandlerService; | |
- use router::route::dispatch::{new_pipeline_set, finalize_pipeline_set}; | |
use router::response::extender::StaticResponseExtender; | |
+ use router::route::dispatch::{finalize_pipeline_set, new_pipeline_set}; | |
use router::tree::SegmentMapping; | |
- use http::FormUrlDecoded; | |
- use http::request::query_string; | |
+ use state::{FromState, State, StateData}; | |
struct SalutationParams { | |
name: String, | |
@@ -339,43 +322,37 @@ mod tests { | |
pub fn hello(mut state: State) -> (State, Response) { | |
let params = state.take::<SalutationParams>(); | |
- let response = Response::new().with_status(StatusCode::Ok).with_body( | |
- format!( | |
- "Hello, {}!", | |
- params.name | |
- ), | |
- ); | |
+ let response = Response::new() | |
+ .with_status(StatusCode::Ok) | |
+ .with_body(format!("Hello, {}!", params.name)); | |
(state, response) | |
} | |
pub fn globbed(state: State) -> (State, Response) { | |
- let response = Response::new().with_status(StatusCode::Ok).with_body( | |
- "Globbed", | |
- ); | |
+ let response = Response::new() | |
+ .with_status(StatusCode::Ok) | |
+ .with_body("Globbed"); | |
(state, response) | |
} | |
pub fn goodbye(mut state: State) -> (State, Response) { | |
let params = state.take::<SalutationParams>(); | |
- let response = Response::new().with_status(StatusCode::Ok).with_body( | |
- format!( | |
- "Goodbye, {}!", | |
- params.name | |
- ), | |
- ); | |
+ let response = Response::new() | |
+ .with_status(StatusCode::Ok) | |
+ .with_body(format!("Goodbye, {}!", params.name)); | |
(state, response) | |
} | |
pub fn add(mut state: State) -> (State, Response) { | |
let params = state.take::<AddParams>(); | |
- let response = Response::new().with_status(StatusCode::Ok).with_body( | |
- format!( | |
+ let response = Response::new() | |
+ .with_status(StatusCode::Ok) | |
+ .with_body(format!( | |
"{} + {} = {}", | |
params.x, | |
params.y, | |
params.x + params.y, | |
- ), | |
- ); | |
+ )); | |
(state, response) | |
} | |
} | |
@@ -422,7 +399,9 @@ mod tests { | |
route.get(r"/literal/\:param/\*").to(welcome::literal); | |
- route.scope("/api", |route| { route.post("/submit").to(api::submit); }); | |
+ route.scope("/api", |route| { | |
+ route.post("/submit").to(api::submit); | |
+ }); | |
}); | |
let new_service = NewHandlerService::new(router); | |
diff --git a/src/router/builder/single.rs b/src/router/builder/single.rs | |
index 697877e..314c679 100644 | |
--- a/src/router/builder/single.rs | |
+++ b/src/router/builder/single.rs | |
@@ -1,13 +1,15 @@ | |
use std::panic::RefUnwindSafe; | |
-use router::request::path::PathExtractor; | |
-use router::request::query_string::QueryStringExtractor; | |
+use handler::{Handler, NewHandler}; | |
+use router::Router; | |
use router::builder::SingleRouteBuilder; | |
use router::builder::replace::{ReplacePathExtractor, ReplaceQueryStringExtractor}; | |
+use router::request::path::PathExtractor; | |
+use router::request::query_string::QueryStringExtractor; | |
use router::route::{Extractors, RouteImpl}; | |
+use router::route::Delegation; | |
+use router::route::dispatch::{DispatcherImpl, PipelineHandleChain}; | |
use router::route::matcher::RouteMatcher; | |
-use router::route::dispatch::{PipelineHandleChain, DispatcherImpl}; | |
-use handler::{Handler, NewHandler}; | |
/// Describes the API for defining a single route, after determining which request paths will be | |
/// dispatched here. The API here uses chained function calls to build and add the route into the | |
@@ -140,6 +142,78 @@ pub trait DefineSingleRoute { | |
where | |
NH: NewHandler + 'static; | |
+ /// Delegates the request to a secondary router instance. | |
+ /// | |
+ /// This allows Gotham apps to support the modular application structure which are more | |
+ /// structured applications with a top-level router that dispatches requests to sub-routers that | |
+ /// in turn handle the set of requests defined by that module. | |
+ /// | |
+ /// # Examples | |
+ /// | |
+ /// ```rust | |
+ /// # extern crate gotham; | |
+ /// # extern crate hyper; | |
+ /// # use std::io; | |
+ /// # use gotham::handler::{Handler, HandlerFuture, NewHandler}; | |
+ /// # use gotham::state::State; | |
+ /// # use gotham::router::Router; | |
+ /// # use gotham::router::builder::*; | |
+ /// # use gotham::middleware::pipeline::new_pipeline; | |
+ /// # use gotham::middleware::session::NewSessionMiddleware; | |
+ /// # use gotham::router::route::dispatch::{new_pipeline_set, finalize_pipeline_set}; | |
+ /// | |
+ /// mod abc { | |
+ /// use super::*; | |
+ /// | |
+ /// struct MyNewHandler; | |
+ /// struct MyHandler; | |
+ /// | |
+ /// impl NewHandler for MyNewHandler { | |
+ /// type Instance = MyHandler; | |
+ /// | |
+ /// fn new_handler(&self) -> io::Result<Self::Instance> { | |
+ /// Ok(MyHandler) | |
+ /// } | |
+ /// } | |
+ /// | |
+ /// impl Handler for MyHandler { | |
+ /// fn handle(self, _state: State) -> Box<HandlerFuture> { | |
+ /// // Handler implementation elided. | |
+ /// # unimplemented!() | |
+ /// } | |
+ /// } | |
+ /// # | |
+ /// pub fn sub_router() -> Router { | |
+ /// # let pipelines = new_pipeline_set(); | |
+ /// # let (pipelines, default) = | |
+ /// # pipelines.add(new_pipeline().add(NewSessionMiddleware::default()).build()); | |
+ /// # | |
+ /// # let pipelines = finalize_pipeline_set(pipelines); | |
+ /// # | |
+ /// # let default_pipeline_chain = (default, ()); | |
+ /// | |
+ /// build_router(default_pipeline_chain, pipelines, |route| { | |
+ /// route.get("/path").to_new_handler(MyNewHandler); | |
+ /// }) | |
+ /// # } | |
+ /// } | |
+ /// | |
+ /// fn router() -> Router { | |
+ /// # let pipelines = new_pipeline_set(); | |
+ /// # let (pipelines, default) = | |
+ /// # pipelines.add(new_pipeline().add(NewSessionMiddleware::default()).build()); | |
+ /// # | |
+ /// # let pipelines = finalize_pipeline_set(pipelines); | |
+ /// # | |
+ /// # let default_pipeline_chain = (default, ()); | |
+ /// build_router(default_pipeline_chain, pipelines, |route| { | |
+ /// route.get("/abc").to_router(abc::sub_router()); | |
+ /// }) | |
+ /// } | |
+ /// # fn main() { router(); } | |
+ /// ``` | |
+ fn to_router(self, router: Router); | |
+ | |
/// Applies a `PathExtractor` type to the current route, to extract path parameters into | |
/// `State` with the given type. | |
/// | |
@@ -257,23 +331,11 @@ pub trait DefineSingleRoute { | |
impl<'a, M, C, P, PE, QSE> DefineSingleRoute for SingleRouteBuilder<'a, M, C, P, PE, QSE> | |
where | |
- M: RouteMatcher | |
- + Send | |
- + Sync | |
- + 'static, | |
- C: PipelineHandleChain<P> | |
- + Send | |
- + Sync | |
- + 'static, | |
+ M: RouteMatcher + Send + Sync + 'static, | |
+ C: PipelineHandleChain<P> + Send + Sync + 'static, | |
P: RefUnwindSafe + Send + Sync + 'static, | |
- PE: PathExtractor | |
- + Send | |
- + Sync | |
- + 'static, | |
- QSE: QueryStringExtractor | |
- + Send | |
- + Sync | |
- + 'static, | |
+ PE: PathExtractor + Send + Sync + 'static, | |
+ QSE: QueryStringExtractor + Send + Sync + 'static, | |
{ | |
fn to<H>(self, handler: H) | |
where | |
@@ -291,7 +353,18 @@ where | |
self.matcher, | |
Box::new(dispatcher), | |
Extractors::new(), | |
- self.delegation, | |
+ Delegation::Internal, | |
+ ); | |
+ self.node_builder.add_route(Box::new(route)); | |
+ } | |
+ | |
+ fn to_router(self, router: Router) { | |
+ let dispatcher = DispatcherImpl::new(router, self.pipeline_chain, self.pipelines); | |
+ let route: RouteImpl<M, PE, QSE> = RouteImpl::new( | |
+ self.matcher, | |
+ Box::new(dispatcher), | |
+ Extractors::new(), | |
+ Delegation::External, | |
); | |
self.node_builder.add_route(Box::new(route)); | |
} | |
@@ -303,8 +376,9 @@ where | |
self.replace_path_extractor() | |
} | |
- fn with_query_string_extractor<NQSE>(self) | |
- -> <Self as ReplaceQueryStringExtractor<NQSE>>::Output | |
+ fn with_query_string_extractor<NQSE>( | |
+ self, | |
+ ) -> <Self as ReplaceQueryStringExtractor<NQSE>>::Output | |
where | |
NQSE: QueryStringExtractor + Send + Sync + 'static, | |
{ | |
-- | |
2.13.0 | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment