Skip to content

Commit 2275246

Browse files
committed
reply: implement redirect
1 parent 7ab0a71 commit 2275246

File tree

2 files changed

+44
-36
lines changed

2 files changed

+44
-36
lines changed

src/lib.rs

Lines changed: 17 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -69,18 +69,16 @@ use combinators::{
6969
Add2, Base, End, Inject, InjectAll, Link, Map, MapErr, MapErrs, Path, Then, TryMap, TryThen,
7070
};
7171
use handler::{Chain, Handler, NotFound};
72-
use reply::Reply;
72+
use reply::{redirect, Reply};
7373
use tree::{Cluster, Node, Params, Parser, PathSpec, Route, Segment};
7474

7575
use frunk_core::{
7676
hlist::{HList, HNil},
7777
indices::Here,
7878
};
7979
use futures::future::{ready, BoxFuture, FutureExt, NeverError, Ready};
80-
use http::{Extensions, HeaderMap, HeaderValue, Uri, Version};
81-
use hyper::{
82-
header::LOCATION, server::conn::AddrStream, service::Service, Body, Method, Request, StatusCode,
83-
};
80+
use http::{Extensions, HeaderMap, Uri, Version};
81+
use hyper::{server::conn::AddrStream, service::Service, Body, Method, Request};
8482
use std::{
8583
borrow::Cow,
8684
collections::HashMap,
@@ -152,15 +150,7 @@ impl Service<Request<Body>> for AppService {
152150
}
153151

154152
/// The set of request scoped state that contexts are initialized with.
155-
pub type Init = R![
156-
Body,
157-
Method,
158-
Uri,
159-
Version,
160-
HeaderMap<HeaderValue>,
161-
Extensions,
162-
SocketAddr,
163-
];
153+
pub type Init = R![Body, Method, Uri, Version, HeaderMap, Extensions, SocketAddr];
164154

165155
/// Contains routes and handlers for a given http application.
166156
///
@@ -321,11 +311,12 @@ impl App {
321311
}
322312
}
323313

324-
fn lookup_route(&self, method: &Method, path: &str) -> Option<Route<'_, dyn Handler>> {
314+
fn lookup_route(&self, method: &Method, path: &str) -> Route<'_, dyn Handler> {
325315
method_idx(method)
326316
.map(|i| &self.common[i])
327317
.or_else(|| self.custom.get(method))
328318
.map(|n| n.lookup(path))
319+
.unwrap_or_default()
329320
}
330321

331322
fn dispatch(&self, req: Request<Body>, addr: SocketAddr) -> BoxFuture<'static, Response> {
@@ -341,30 +332,21 @@ impl App {
341332
Cow::Owned(s)
342333
}
343334

344-
let method = req.method();
345335
let path = req.uri().path();
336+
let route = self.lookup_route(req.method(), path);
346337

347-
match self.lookup_route(method, path) {
348-
Some(Route { entry: Some(h), .. }) => h.handle(req, addr),
349-
350-
Some(Route { tsr: true, .. }) if method != Method::CONNECT && path != "/" => {
351-
let code = if let Method::GET = *method {
352-
StatusCode::MOVED_PERMANENTLY
353-
} else {
354-
StatusCode::PERMANENT_REDIRECT
355-
};
356-
357-
let resp = hyper::Response::builder()
358-
.status(code)
359-
.header(LOCATION, &*swap_trailing_slash(path))
360-
.body(Body::empty())
361-
.unwrap();
362-
363-
Box::pin(async { resp })
364-
}
338+
// there's a handler matching this uri
339+
if let Some(h) = route.entry {
340+
return h.handle(req, addr);
341+
}
365342

366-
_ => self.not_found.handle(req, addr),
343+
// there's a handler matching this uri if the trailing slash is swapped
344+
if route.tsr && req.method() != Method::CONNECT && path != "/" {
345+
return ready(redirect(false, swap_trailing_slash(path))).boxed();
367346
}
347+
348+
// there's no handler matching this uri
349+
self.not_found.handle(req, addr)
368350
}
369351

370352
/// Create a test client for this app.

src/reply/mod.rs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ use super::{field::IsoEncode, Response, R};
33
use frunk_core::coproduct::{CNil, Coproduct};
44
use headers::{Header, HeaderMapExt};
55
use http::{HeaderMap, Method, StatusCode, Uri};
6-
use hyper::header::{CONTENT_TYPE, X_CONTENT_TYPE_OPTIONS};
6+
use hyper::{
7+
header::{CONTENT_TYPE, LOCATION, X_CONTENT_TYPE_OPTIONS},
8+
Body,
9+
};
710
use hyper_staticfile::{resolve_path, ResponseBuilder};
811
use serde::Serialize;
912
use std::{
@@ -166,6 +169,7 @@ pub fn json<T: Serialize>(value: &T) -> Response {
166169

167170
/// Returns a json [Response] from an hlist of [Serialize] named fields.
168171
///
172+
/// # Examples
169173
/// ```
170174
/// use hyperbole::{r, reply};
171175
///
@@ -281,3 +285,25 @@ async fn fs_inner(path: PathBuf, m: Method, u: UriRes, h: HeaderMap) -> Result<R
281285
.build(resolve_path(path, uri.path()).await?)
282286
.map_err(|e| e.into())
283287
}
288+
289+
/// Returns a redirect response to the provided `path`.
290+
///
291+
/// # Examples
292+
/// ```
293+
/// use hyperbole::{reply, uri, Ctx, R};
294+
///
295+
/// let _ctx = Ctx::default().get(uri!["go-home"], |cx: R![]| async {
296+
/// reply::redirect(true, "/home")
297+
/// });
298+
/// ```
299+
pub fn redirect<P: AsRef<str>>(permanent: bool, path: P) -> Response {
300+
hyper::Response::builder()
301+
.status(if permanent {
302+
StatusCode::PERMANENT_REDIRECT
303+
} else {
304+
StatusCode::TEMPORARY_REDIRECT
305+
})
306+
.header(LOCATION, path.as_ref())
307+
.body(Body::empty())
308+
.unwrap()
309+
}

0 commit comments

Comments
 (0)