1pub mod dispatch;
8pub mod matcher;
9
10use std::marker::PhantomData;
11use std::panic::RefUnwindSafe;
12use std::pin::Pin;
13
14use hyper::{Body, Response, Uri};
15use log::debug;
16
17use crate::extractor::{self, PathExtractor, QueryStringExtractor};
18use crate::handler::HandlerFuture;
19use crate::helpers::http::request::query_string;
20use crate::router::non_match::RouteNonMatch;
21use crate::router::route::dispatch::Dispatcher;
22use crate::router::route::matcher::RouteMatcher;
23use crate::router::tree::segment::SegmentMapping;
24use crate::state::{request_id, State};
25
26#[derive(Clone, Copy, Eq, PartialEq)]
27pub enum Delegation {
31 Internal,
34
35 External,
38}
39
40pub trait Route: RefUnwindSafe {
54 type ResBody;
57 fn is_match(&self, state: &State) -> Result<(), RouteNonMatch>;
59
60 fn delegation(&self) -> Delegation;
62
63 fn extract_request_path<'a>(
65 &self,
66 state: &mut State,
67 params: SegmentMapping<'a>,
68 ) -> Result<(), ExtractorFailed>;
69
70 fn extend_response_on_path_error(&self, state: &mut State, res: &mut Response<Self::ResBody>);
72
73 fn extract_query_string(&self, state: &mut State) -> Result<(), ExtractorFailed>;
75
76 fn extend_response_on_query_string_error(
78 &self,
79 state: &mut State,
80 res: &mut Response<Self::ResBody>,
81 );
82
83 fn dispatch(&self, state: State) -> Pin<Box<HandlerFuture>>;
86}
87
88pub struct ExtractorFailed;
91
92pub struct RouteImpl<RM, PE, QSE>
95where
96 RM: RouteMatcher,
97 PE: PathExtractor<Body>,
98 QSE: QueryStringExtractor<Body>,
99{
100 matcher: RM,
101 dispatcher: Box<dyn Dispatcher + Send + Sync>,
102 _extractors: Extractors<PE, QSE>,
103 delegation: Delegation,
104}
105
106pub struct Extractors<PE, QSE>
109where
110 PE: PathExtractor<Body>,
111 QSE: QueryStringExtractor<Body>,
112{
113 rpe_phantom: PhantomData<PE>,
114 qse_phantom: PhantomData<QSE>,
115}
116
117impl<RM, PE, QSE> RouteImpl<RM, PE, QSE>
118where
119 RM: RouteMatcher,
120 PE: PathExtractor<Body>,
121 QSE: QueryStringExtractor<Body>,
122{
123 pub fn new(
125 matcher: RM,
126 dispatcher: Box<dyn Dispatcher + Send + Sync>,
127 _extractors: Extractors<PE, QSE>,
128 delegation: Delegation,
129 ) -> Self {
130 RouteImpl {
131 matcher,
132 dispatcher,
133 _extractors,
134 delegation,
135 }
136 }
137}
138
139impl<PE, QSE> Extractors<PE, QSE>
140where
141 PE: PathExtractor<Body>,
142 QSE: QueryStringExtractor<Body>,
143{
144 pub fn new() -> Self {
146 Extractors {
147 rpe_phantom: PhantomData,
148 qse_phantom: PhantomData,
149 }
150 }
151}
152
153impl<RM, PE, QSE> Route for RouteImpl<RM, PE, QSE>
154where
155 RM: RouteMatcher,
156 PE: PathExtractor<Body>,
157 QSE: QueryStringExtractor<Body>,
158{
159 type ResBody = Body;
160
161 fn is_match(&self, state: &State) -> Result<(), RouteNonMatch> {
162 self.matcher.is_match(state)
163 }
164
165 fn delegation(&self) -> Delegation {
166 self.delegation
167 }
168
169 fn dispatch(&self, state: State) -> Pin<Box<HandlerFuture>> {
170 self.dispatcher.dispatch(state)
171 }
172
173 fn extract_request_path<'a>(
174 &self,
175 state: &mut State,
176 params: SegmentMapping<'a>,
177 ) -> Result<(), ExtractorFailed> {
178 match extractor::internal::from_segment_mapping::<PE>(params) {
179 Ok(val) => Ok(state.put(val)),
180 Err(e) => {
181 debug!("[{}] path extractor failed: {}", request_id(state), e);
182 Err(ExtractorFailed)
183 }
184 }
185 }
186
187 fn extend_response_on_path_error(&self, state: &mut State, res: &mut Response<Self::ResBody>) {
188 PE::extend(state, res)
189 }
190
191 fn extract_query_string(&self, state: &mut State) -> Result<(), ExtractorFailed> {
192 let result: Result<QSE, _> = {
193 let uri = state.borrow::<Uri>();
194 let query_string_mapping = query_string::split(uri.query());
195 extractor::internal::from_query_string_mapping(&query_string_mapping)
196 };
197
198 match result {
199 Ok(val) => Ok(state.put(val)),
200 Err(e) => {
201 debug!(
202 "[{}] query string extractor failed: {}",
203 request_id(state),
204 e
205 );
206 Err(ExtractorFailed)
207 }
208 }
209 }
210
211 fn extend_response_on_query_string_error(
212 &self,
213 state: &mut State,
214 res: &mut Response<Self::ResBody>,
215 ) {
216 QSE::extend(state, res)
217 }
218}
219
220#[cfg(test)]
221mod tests {
222 use super::*;
223
224 use futures_util::FutureExt;
225 use hyper::{HeaderMap, Method, StatusCode, Uri};
226 use std::str::FromStr;
227
228 use crate::extractor::{NoopPathExtractor, NoopQueryStringExtractor};
229 use crate::helpers::http::request::path::RequestPathSegments;
230 use crate::helpers::http::response::create_empty_response;
231 use crate::pipeline::{finalize_pipeline_set, new_pipeline_set};
232 use crate::router::builder::*;
233 use crate::router::route::dispatch::DispatcherImpl;
234 use crate::router::route::matcher::MethodOnlyRouteMatcher;
235 use crate::state::set_request_id;
236
237 #[test]
238 fn internal_route_tests() {
239 fn handler(state: State) -> (State, Response<Body>) {
240 let res = create_empty_response(&state, StatusCode::ACCEPTED);
241 (state, res)
242 }
243
244 let pipeline_set = finalize_pipeline_set(new_pipeline_set());
245 let methods = vec![Method::GET];
246 let matcher = MethodOnlyRouteMatcher::new(methods);
247 let dispatcher = Box::new(DispatcherImpl::new(|| Ok(handler), (), pipeline_set));
248 let extractors: Extractors<NoopPathExtractor, NoopQueryStringExtractor> = Extractors::new();
249 let route = RouteImpl::new(matcher, dispatcher, extractors, Delegation::Internal);
250
251 let mut state = State::new();
252 state.put(HeaderMap::new());
253 state.put(Method::GET);
254 set_request_id(&mut state);
255
256 match route.dispatch(state).now_or_never() {
257 Some(Ok((_state, response))) => assert_eq!(response.status(), StatusCode::ACCEPTED),
258 Some(Err((_state, e))) => panic!("error polling future: {:?}", e),
259 None => panic!("expected future to be completed already"),
260 }
261 }
262
263 #[test]
264 fn external_route_tests() {
265 fn handler(state: State) -> (State, Response<Body>) {
266 let res = create_empty_response(&state, StatusCode::ACCEPTED);
267 (state, res)
268 }
269
270 let secondary_router = build_simple_router(|route| {
271 route.get("/").to(handler);
272 });
273
274 let pipeline_set = finalize_pipeline_set(new_pipeline_set());
275 let methods = vec![Method::GET];
276 let matcher = MethodOnlyRouteMatcher::new(methods);
277 let dispatcher = Box::new(DispatcherImpl::new(secondary_router, (), pipeline_set));
278 let extractors: Extractors<NoopPathExtractor, NoopQueryStringExtractor> = Extractors::new();
279 let route = RouteImpl::new(matcher, dispatcher, extractors, Delegation::External);
280
281 let mut state = State::new();
282 state.put(Method::GET);
283 state.put(Uri::from_str("https://example.com/").unwrap());
284 state.put(HeaderMap::new());
285 state.put(RequestPathSegments::new("/"));
286 set_request_id(&mut state);
287
288 match route.dispatch(state).now_or_never() {
289 Some(Ok((_state, response))) => assert_eq!(response.status(), StatusCode::ACCEPTED),
290 Some(Err((_state, e))) => panic!("error polling future: {:?}", e),
291 None => panic!("expected future to be completed already"),
292 }
293 }
294}