Skip to content

Commit

Permalink
Add support for delegating to another router
Browse files Browse the repository at this point in the history
As suggested in review of #70 this was missing from the initial version
of the router builder. The approach here keeps the delegated route as a
separate piece of API to avoid going via the normal route builder API
which allows path/query extractors and route matchers to be added.
  • Loading branch information
smangelsdorf committed Dec 28, 2017
1 parent 685e0d5 commit 5ab9c86
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 14 deletions.
55 changes: 49 additions & 6 deletions src/router/builder/draw.rs
@@ -1,13 +1,13 @@
use std::marker::PhantomData;
use std::panic::RefUnwindSafe;

use hyper::Method;

use router::route::Delegation;
use router::route::dispatch::{PipelineHandleChain, PipelineSet};
use router::route::matcher::MethodOnlyRouteMatcher;
use router::request::path::NoopPathExtractor;
use router::request::query_string::NoopQueryStringExtractor;
use router::builder::{SingleRouteBuilder, RouterBuilder, ScopeBuilder};
use router::builder::{SingleRouteBuilder, RouterBuilder, ScopeBuilder, DelegateRouteBuilder};
use router::tree::node::{SegmentType, NodeBuilder};
use router::tree::regex::ConstrainedSegmentRegex;

Expand All @@ -28,7 +28,7 @@ pub type DefaultSingleRouteBuilder<'a, C, P> = SingleRouteBuilder<
pub trait DrawRoutes<C, P>
where
C: PipelineHandleChain<P> + Copy + Send + Sync + 'static,
P: Send + Sync + 'static,
P: RefUnwindSafe + Send + Sync + 'static,
{
/// Creates a route which matches `GET` and `HEAD` requests to the given path.
///
Expand Down Expand Up @@ -157,7 +157,6 @@ where
node_builder,
pipeline_chain: *pipeline_chain,
pipelines: pipelines.clone(),
delegation: Delegation::Internal,
phantom: PhantomData,
}
}
Expand Down Expand Up @@ -217,6 +216,50 @@ where
f(&mut scope_builder)
}

/// Begins delegating a subpath of the tree.
///
/// # Examples
///
/// ```rust
/// # extern crate gotham;
/// # extern crate hyper;
/// # 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};
/// #
/// fn admin_router() -> Router {
/// // Implementation elided
/// # build_simple_router(|_route| {})
/// }
///
/// # 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.delegate("/admin").to_router(admin_router());
/// })
/// # }
/// # fn main() { router(); }
/// ```
fn delegate<'b>(&'b mut self, path: &str) -> DelegateRouteBuilder<'b, C, P> {
let (node_builder, pipeline_chain, pipelines) = self.component_refs();
let node_builder = descend(node_builder, path);

DelegateRouteBuilder {
node_builder,
pipeline_chain: *pipeline_chain,
pipelines: pipelines.clone(),
}
}

/// Return the components that comprise this builder. For internal use only.
#[doc(hidden)]
fn component_refs(&mut self) -> (&mut NodeBuilder, &mut C, &PipelineSet<P>);
Expand Down Expand Up @@ -285,7 +328,7 @@ where
+ Send
+ Sync
+ 'static,
P: Send + Sync + 'static,
P: RefUnwindSafe + Send + Sync + 'static,
{
fn component_refs(&mut self) -> (&mut NodeBuilder, &mut C, &PipelineSet<P>) {
(
Expand All @@ -303,7 +346,7 @@ where
+ Send
+ Sync
+ 'static,
P: Send + Sync + 'static,
P: RefUnwindSafe + Send + Sync + 'static,
{
fn component_refs(&mut self) -> (&mut NodeBuilder, &mut C, &PipelineSet<P>) {
(
Expand Down
62 changes: 56 additions & 6 deletions src/router/builder/mod.rs
Expand Up @@ -5,19 +5,21 @@ mod single;
mod replace;

use std::marker::PhantomData;
use std::panic::RefUnwindSafe;

use hyper::StatusCode;

use router::Router;
use router::tree::TreeBuilder;
use router::response::extender::ResponseExtender;
use router::response::finalizer::ResponseFinalizerBuilder;
use router::route::Delegation;
use router::route::{Delegation, Extractors, RouteImpl};
use router::route::matcher::RouteMatcher;
use router::route::matcher::any::AnyRouteMatcher;
use router::route::dispatch::{new_pipeline_set, finalize_pipeline_set, PipelineHandleChain,
PipelineSet};
use router::request::path::PathExtractor;
use router::request::query_string::QueryStringExtractor;
PipelineSet, DispatcherImpl};
use router::request::path::{PathExtractor, NoopPathExtractor};
use router::request::query_string::{QueryStringExtractor, NoopQueryStringExtractor};
use router::tree::node::NodeBuilder;

pub use self::single::DefineSingleRoute;
Expand Down Expand Up @@ -190,6 +192,39 @@ where
pipelines: PipelineSet<P>,
}

/// A delegated builder, which is created by `DrawRoutes::delegate` and returned. See the
/// `DrawRoutes` trait for usage.
pub struct DelegateRouteBuilder<'a, C, P>
where
C: PipelineHandleChain<P> + Copy + Send + Sync + 'static,
P: Send + Sync + 'static,
{
node_builder: &'a mut NodeBuilder,
pipeline_chain: C,
pipelines: PipelineSet<P>,
}

type DelegatedRoute = RouteImpl<AnyRouteMatcher, NoopPathExtractor, NoopQueryStringExtractor>;

impl<'a, C, P> DelegateRouteBuilder<'a, C, P>
where
C: PipelineHandleChain<P> + Copy + Send + Sync + 'static,
P: RefUnwindSafe + Send + Sync + 'static,
{
/// Directs the delegated route to the given `Router`.
pub fn to_router(self, router: Router) {
let dispatcher = DispatcherImpl::new(router, self.pipeline_chain, self.pipelines);
let route: DelegatedRoute = DelegatedRoute::new(
AnyRouteMatcher::new(),
Box::new(dispatcher),
Extractors::new(),
Delegation::External,
);

self.node_builder.add_route(Box::new(route));
}
}

/// Implements the traits required to define a single route, after determining which request paths
/// will be dispatched here. The `DefineSingleRoute` trait has documentation for using this type.
pub struct SingleRouteBuilder<'a, M, C, P, PE, QSE>
Expand All @@ -204,7 +239,6 @@ where
matcher: M,
pipeline_chain: C,
pipelines: PipelineSet<P>,
delegation: Delegation,
phantom: PhantomData<(PE, QSE)>,
}

Expand Down Expand Up @@ -241,7 +275,6 @@ where
matcher: self.matcher,
pipeline_chain: self.pipeline_chain,
pipelines: self.pipelines,
delegation: self.delegation,
phantom: PhantomData,
}
}
Expand Down Expand Up @@ -355,6 +388,13 @@ mod tests {
(state, response)
}

pub fn delegated(state: State) -> (State, Response) {
let response = Response::new().with_status(StatusCode::Ok).with_body(
"Delegated",
);
(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(
Expand Down Expand Up @@ -397,6 +437,9 @@ mod tests {

let default_pipeline_chain = (default, ());

let delegated_router =
build_simple_router(|route| { route.get("/b").to(welcome::delegated); });

let router = build_router(default_pipeline_chain, pipelines, |route| {
route.get("/").to(welcome::index);

Expand All @@ -423,6 +466,8 @@ mod tests {
route.get(r"/literal/\:param/\*").to(welcome::literal);

route.scope("/api", |route| { route.post("/submit").to(api::submit); });

route.delegate("/delegated").to_router(delegated_router);
});

let new_service = NewHandlerService::new(router);
Expand Down Expand Up @@ -453,6 +498,11 @@ mod tests {
let response_bytes = response.body().concat2().wait().unwrap().to_vec();
assert_eq!(&String::from_utf8(response_bytes).unwrap(), "Globbed");

let response = call(Request::new(Method::Get, "/delegated/b".parse().unwrap()));
assert_eq!(response.status(), StatusCode::Ok);
let response_bytes = response.body().concat2().wait().unwrap().to_vec();
assert_eq!(&String::from_utf8(response_bytes).unwrap(), "Delegated");

let response = call(Request::new(Method::Get, "/goodbye/world".parse().unwrap()));
assert_eq!(response.status(), StatusCode::Ok);
let response_bytes = response.body().concat2().wait().unwrap().to_vec();
Expand Down
4 changes: 2 additions & 2 deletions src/router/builder/single.rs
Expand Up @@ -4,7 +4,7 @@ use router::request::path::PathExtractor;
use router::request::query_string::QueryStringExtractor;
use router::builder::SingleRouteBuilder;
use router::builder::replace::{ReplacePathExtractor, ReplaceQueryStringExtractor};
use router::route::{Extractors, RouteImpl};
use router::route::{Delegation, Extractors, RouteImpl};
use router::route::matcher::RouteMatcher;
use router::route::dispatch::{PipelineHandleChain, DispatcherImpl};
use handler::{Handler, NewHandler};
Expand Down Expand Up @@ -291,7 +291,7 @@ where
self.matcher,
Box::new(dispatcher),
Extractors::new(),
self.delegation,
Delegation::Internal,
);
self.node_builder.add_route(Box::new(route));
}
Expand Down

0 comments on commit 5ab9c86

Please sign in to comment.