Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bradleybeddoes/698dc5212a32a919d1950800ac6402fa to your computer and use it in GitHub Desktop.
Save bradleybeddoes/698dc5212a32a919d1950800ac6402fa to your computer and use it in GitHub Desktop.
support sub router delegation
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