From 76b644365fd6fa74e1617dc16f6a36c1eeb85a0d Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Wed, 2 May 2018 06:07:30 -0700 Subject: [PATCH 01/10] use read only ref for FromRequest; remove unnecessary static --- src/error.rs | 3 +-- src/extractor.rs | 33 ++++++++++++++------------------- src/handler.rs | 20 ++++++++++++-------- src/httprequest.rs | 4 ++-- src/json.rs | 2 +- src/with.rs | 24 ++++++++++-------------- 6 files changed, 40 insertions(+), 46 deletions(-) diff --git a/src/error.rs b/src/error.rs index 37dc3d89f..963abd3b9 100644 --- a/src/error.rs +++ b/src/error.rs @@ -805,8 +805,7 @@ mod tests { #[test] fn test_backtrace() { - let orig = ErrorBadRequest("err"); - let e: Error = orig.into(); + let e = ErrorBadRequest("err"); assert!(e.backtrace().is_some()); } diff --git a/src/extractor.rs b/src/extractor.rs index 4ed88489e..aff15c46d 100644 --- a/src/extractor.rs +++ b/src/extractor.rs @@ -99,13 +99,12 @@ impl Path { impl FromRequest for Path where T: DeserializeOwned, - S: 'static, { type Config = (); type Result = Result; #[inline] - fn from_request(req: &mut HttpRequest, _: &Self::Config) -> Self::Result { + fn from_request(req: &HttpRequest, _: &Self::Config) -> Self::Result { let req = req.clone(); de::Deserialize::deserialize(PathDeserializer::new(&req)) .map_err(|e| e.into()) @@ -167,13 +166,12 @@ impl Query { impl FromRequest for Query where T: de::DeserializeOwned, - S: 'static, { type Config = (); type Result = Result; #[inline] - fn from_request(req: &mut HttpRequest, _: &Self::Config) -> Self::Result { + fn from_request(req: &HttpRequest, _: &Self::Config) -> Self::Result { let req = req.clone(); serde_urlencoded::from_str::(req.query_string()) .map_err(|e| e.into()) @@ -241,7 +239,7 @@ where type Result = Box>; #[inline] - fn from_request(req: &mut HttpRequest, cfg: &Self::Config) -> Self::Result { + fn from_request(req: &HttpRequest, cfg: &Self::Config) -> Self::Result { Box::new( UrlEncoded::new(req.clone()) .limit(cfg.limit) @@ -326,7 +324,7 @@ impl FromRequest for Bytes { type Result = Result>, Error>; #[inline] - fn from_request(req: &mut HttpRequest, cfg: &Self::Config) -> Self::Result { + fn from_request(req: &HttpRequest, cfg: &Self::Config) -> Self::Result { // check content-type cfg.check_mimetype(req)?; @@ -370,7 +368,7 @@ impl FromRequest for String { type Result = Result>, Error>; #[inline] - fn from_request(req: &mut HttpRequest, cfg: &Self::Config) -> Self::Result { + fn from_request(req: &HttpRequest, cfg: &Self::Config) -> Self::Result { // check content-type cfg.check_mimetype(req)?; @@ -471,7 +469,7 @@ mod tests { req.payload_mut() .unread_data(Bytes::from_static(b"hello=world")); - match Bytes::from_request(&mut req, &cfg) + match Bytes::from_request(&req, &cfg) .unwrap() .poll() .unwrap() @@ -490,7 +488,7 @@ mod tests { req.payload_mut() .unread_data(Bytes::from_static(b"hello=world")); - match String::from_request(&mut req, &cfg) + match String::from_request(&req, &cfg) .unwrap() .poll() .unwrap() @@ -514,10 +512,7 @@ mod tests { let mut cfg = FormConfig::default(); cfg.limit(4096); - match Form::::from_request(&mut req, &cfg) - .poll() - .unwrap() - { + match Form::::from_request(&req, &cfg).poll().unwrap() { Async::Ready(s) => { assert_eq!(s.hello, "world"); } @@ -574,29 +569,29 @@ mod tests { let (router, _) = Router::new("", ServerSettings::default(), routes); assert!(router.recognize(&mut req).is_some()); - let s = Path::::from_request(&mut req, &()).unwrap(); + let s = Path::::from_request(&req, &()).unwrap(); assert_eq!(s.key, "name"); assert_eq!(s.value, "user1"); - let s = Path::<(String, String)>::from_request(&mut req, &()).unwrap(); + let s = Path::<(String, String)>::from_request(&req, &()).unwrap(); assert_eq!(s.0, "name"); assert_eq!(s.1, "user1"); - let s = Query::::from_request(&mut req, &()).unwrap(); + let s = Query::::from_request(&req, &()).unwrap(); assert_eq!(s.id, "test"); let mut req = TestRequest::with_uri("/name/32/").finish(); assert!(router.recognize(&mut req).is_some()); - let s = Path::::from_request(&mut req, &()).unwrap(); + let s = Path::::from_request(&req, &()).unwrap(); assert_eq!(s.as_ref().key, "name"); assert_eq!(s.value, 32); - let s = Path::<(String, u8)>::from_request(&mut req, &()).unwrap(); + let s = Path::<(String, u8)>::from_request(&req, &()).unwrap(); assert_eq!(s.0, "name"); assert_eq!(s.1, 32); - let res = Path::>::from_request(&mut req, &()).unwrap(); + let res = Path::>::from_default(&req).unwrap(); assert_eq!(res[0], "name".to_owned()); assert_eq!(res[1], "32".to_owned()); } diff --git a/src/handler.rs b/src/handler.rs index 15f975b6e..216faa29b 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -36,10 +36,7 @@ pub trait Responder { /// Trait implemented by types that can be extracted from request. /// /// Types that implement this trait can be used with `Route::with()` method. -pub trait FromRequest: Sized -where - S: 'static, -{ +pub trait FromRequest: Sized { /// Configuration for conversion process type Config: Default; @@ -47,7 +44,14 @@ where type Result: Into>; /// Convert request to a Self - fn from_request(req: &mut HttpRequest, cfg: &Self::Config) -> Self::Result; + fn from_request(req: &HttpRequest, cfg: &Self::Config) -> Self::Result; + + /// Convert request to a Self + /// + /// This method uses default extractor configuration + fn from_default(req: &HttpRequest) -> Self::Result { + Self::from_request(req, &Self::Config::default()) + } } /// Combines two different responder types into a single type @@ -505,12 +509,12 @@ impl Deref for State { } } -impl FromRequest for State { +impl FromRequest for State { type Config = (); type Result = State; #[inline] - fn from_request(req: &mut HttpRequest, _: &Self::Config) -> Self::Result { - State(req.clone()).into() + fn from_request(req: &HttpRequest, _: &Self::Config) -> Self::Result { + State(req.clone()) } } diff --git a/src/httprequest.rs b/src/httprequest.rs index 2225b4bb8..12e5da1d6 100644 --- a/src/httprequest.rs +++ b/src/httprequest.rs @@ -500,12 +500,12 @@ impl Clone for HttpRequest { } } -impl FromRequest for HttpRequest { +impl FromRequest for HttpRequest { type Config = (); type Result = Self; #[inline] - fn from_request(req: &mut HttpRequest, _: &Self::Config) -> Self::Result { + fn from_request(req: &HttpRequest, _: &Self::Config) -> Self::Result { req.clone() } } diff --git a/src/json.rs b/src/json.rs index 24d1c9c4b..27c99c649 100644 --- a/src/json.rs +++ b/src/json.rs @@ -136,7 +136,7 @@ where type Result = Box>; #[inline] - fn from_request(req: &mut HttpRequest, cfg: &Self::Config) -> Self::Result { + fn from_request(req: &HttpRequest, cfg: &Self::Config) -> Self::Result { let req = req.clone(); let err = Rc::clone(&cfg.ehandler); Box::new( diff --git a/src/with.rs b/src/with.rs index bf0d77d2a..e522d97ec 100644 --- a/src/with.rs +++ b/src/with.rs @@ -134,7 +134,7 @@ where let item = if !self.started { self.started = true; - let reply = T::from_request(&mut self.req, self.cfg.as_ref()).into(); + let reply = T::from_request(&self.req, self.cfg.as_ref()).into(); match reply.into() { ReplyItem::Error(err) => return Err(err), ReplyItem::Message(msg) => msg, @@ -268,7 +268,7 @@ where if !self.started { self.started = true; - let reply = T1::from_request(&mut self.req, self.cfg1.as_ref()).into(); + let reply = T1::from_request(&self.req, self.cfg1.as_ref()).into(); let item1 = match reply.into() { ReplyItem::Error(err) => return Err(err), ReplyItem::Message(msg) => msg, @@ -279,7 +279,7 @@ where ReplyItem::None => panic!("use after resolve"), }; - let reply = T2::from_request(&mut self.req, self.cfg2.as_ref()).into(); + let reply = T2::from_request(&self.req, self.cfg2.as_ref()).into(); let item2 = match reply.into() { ReplyItem::Error(err) => return Err(err), ReplyItem::Message(msg) => msg, @@ -309,8 +309,7 @@ where if self.fut1.is_some() { match self.fut1.as_mut().unwrap().poll()? { Async::Ready(item) => { - let reply = - T2::from_request(&mut self.req, self.cfg2.as_ref()).into(); + let reply = T2::from_request(&self.req, self.cfg2.as_ref()).into(); let item2 = match reply.into() { ReplyItem::Error(err) => return Err(err), ReplyItem::Message(msg) => msg, @@ -480,7 +479,7 @@ where if !self.started { self.started = true; - let reply = T1::from_request(&mut self.req, self.cfg1.as_ref()).into(); + let reply = T1::from_request(&self.req, self.cfg1.as_ref()).into(); let item1 = match reply.into() { ReplyItem::Error(err) => return Err(err), ReplyItem::Message(msg) => msg, @@ -491,7 +490,7 @@ where ReplyItem::None => panic!("use after resolve"), }; - let reply = T2::from_request(&mut self.req, self.cfg2.as_ref()).into(); + let reply = T2::from_request(&self.req, self.cfg2.as_ref()).into(); let item2 = match reply.into() { ReplyItem::Error(err) => return Err(err), ReplyItem::Message(msg) => msg, @@ -503,7 +502,7 @@ where ReplyItem::None => panic!("use after resolve"), }; - let reply = T3::from_request(&mut self.req, self.cfg3.as_ref()).into(); + let reply = T3::from_request(&self.req, self.cfg3.as_ref()).into(); let item3 = match reply.into() { ReplyItem::Error(err) => return Err(err), ReplyItem::Message(msg) => msg, @@ -536,8 +535,7 @@ where Async::Ready(item) => { self.item1 = Some(item); self.fut1.take(); - let reply = - T2::from_request(&mut self.req, self.cfg2.as_ref()).into(); + let reply = T2::from_request(&self.req, self.cfg2.as_ref()).into(); let item2 = match reply.into() { ReplyItem::Error(err) => return Err(err), ReplyItem::Message(msg) => msg, @@ -548,8 +546,7 @@ where ReplyItem::None => panic!("use after resolve"), }; - let reply = - T3::from_request(&mut self.req, self.cfg3.as_ref()).into(); + let reply = T3::from_request(&self.req, self.cfg3.as_ref()).into(); let item3 = match reply.into() { ReplyItem::Error(err) => return Err(err), ReplyItem::Message(msg) => msg, @@ -584,8 +581,7 @@ where match self.fut2.as_mut().unwrap().poll()? { Async::Ready(item) => { self.fut2.take(); - let reply = - T3::from_request(&mut self.req, self.cfg3.as_ref()).into(); + let reply = T3::from_request(&self.req, self.cfg3.as_ref()).into(); let item3 = match reply.into() { ReplyItem::Error(err) => return Err(err), ReplyItem::Message(msg) => msg, From 1aadfee6f705211bbd20debc1b8e3df15fb27661 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Wed, 2 May 2018 06:09:50 -0700 Subject: [PATCH 02/10] rename from_default to extract --- src/extractor.rs | 2 +- src/handler.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/extractor.rs b/src/extractor.rs index aff15c46d..12ae9e981 100644 --- a/src/extractor.rs +++ b/src/extractor.rs @@ -591,7 +591,7 @@ mod tests { assert_eq!(s.0, "name"); assert_eq!(s.1, 32); - let res = Path::>::from_default(&req).unwrap(); + let res = Path::>::extract(&req).unwrap(); assert_eq!(res[0], "name".to_owned()); assert_eq!(res[1], "32".to_owned()); } diff --git a/src/handler.rs b/src/handler.rs index 216faa29b..07281b6b3 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -49,7 +49,7 @@ pub trait FromRequest: Sized { /// Convert request to a Self /// /// This method uses default extractor configuration - fn from_default(req: &HttpRequest) -> Self::Result { + fn extract(req: &HttpRequest) -> Self::Result { Self::from_request(req, &Self::Config::default()) } } From 31e23d4ab173711ac66c78241a8fbb52187228cb Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Wed, 2 May 2018 06:28:38 -0700 Subject: [PATCH 03/10] add query deprecation info --- MIGRATION.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/MIGRATION.md b/MIGRATION.md index f343028b5..791a6f125 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -15,6 +15,12 @@ * `FromRequest::Result` has to implement `Into>` +* `HttpRequest::query()` is deprecated. Use `Query` extractor. + + ```rust + let q = Query::>::extract(req); + ``` + ## Migration from 0.4 to 0.5 From a38acb41e5c01197b6a3a948be228d8d95f1206c Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Wed, 2 May 2018 06:30:06 -0700 Subject: [PATCH 04/10] better query example --- MIGRATION.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/MIGRATION.md b/MIGRATION.md index 791a6f125..4ecff7b2f 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -17,6 +17,14 @@ * `HttpRequest::query()` is deprecated. Use `Query` extractor. + ```rust + fn index(q: Query>) -> Result<..> { + ... + } + ``` + + or + ```rust let q = Query::>::extract(req); ``` From 4ca5d8bcfc7e5b32caeb8a585ec206f16f64329f Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Wed, 2 May 2018 13:38:25 -0700 Subject: [PATCH 05/10] add FromRequest impl for tuples of various length --- src/extractor.rs | 155 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 153 insertions(+), 2 deletions(-) diff --git a/src/extractor.rs b/src/extractor.rs index 12ae9e981..9d707e22f 100644 --- a/src/extractor.rs +++ b/src/extractor.rs @@ -1,17 +1,18 @@ +use std::marker::PhantomData; use std::ops::{Deref, DerefMut}; use std::str; use bytes::Bytes; use encoding::all::UTF_8; use encoding::types::{DecoderTrap, Encoding}; -use futures::future::Future; +use futures::{Async, Future, Poll}; use mime::Mime; use serde::de::{self, DeserializeOwned}; use serde_urlencoded; use de::PathDeserializer; use error::{Error, ErrorBadRequest}; -use handler::FromRequest; +use handler::{FromRequest, Reply}; use httpmessage::{HttpMessage, MessageBody, UrlEncoded}; use httprequest::HttpRequest; @@ -445,6 +446,123 @@ impl Default for PayloadConfig { } } +macro_rules! tuple_from_req ({$fut_type:ident, $(($n:tt, $T:ident)),+} => { + + /// FromRequest implementation for tuple + impl + 'static),+> FromRequest for ($($T,)+) + where + S: 'static, + { + type Config = ($($T::Config,)+); + type Result = Box>; + + fn from_request(req: &HttpRequest, cfg: &Self::Config) -> Self::Result { + Box::new($fut_type { + s: PhantomData, + items: <($(Option<$T>,)+)>::default(), + futs: ($(Some($T::from_request(req, &cfg.$n).into()),)+), + }) + } + } + + struct $fut_type),+> + where + S: 'static, + { + s: PhantomData, + items: ($(Option<$T>,)+), + futs: ($(Option>,)+), + } + + impl),+> Future for $fut_type + where + S: 'static, + { + type Item = ($($T,)+); + type Error = Error; + + fn poll(&mut self) -> Poll { + let mut ready = true; + + $( + if self.futs.$n.is_some() { + match self.futs.$n.as_mut().unwrap().poll() { + Ok(Async::Ready(item)) => { + self.items.$n = Some(item); + self.futs.$n.take(); + } + Ok(Async::NotReady) => ready = false, + Err(e) => return Err(e), + } + } + )+ + + if ready { + Ok(Async::Ready( + ($(self.items.$n.take().unwrap(),)+) + )) + } else { + Ok(Async::NotReady) + } + } + } +}); + +tuple_from_req!(TupleFromRequest1, (0, A)); +tuple_from_req!(TupleFromRequest2, (0, A), (1, B)); +tuple_from_req!(TupleFromRequest3, (0, A), (1, B), (2, C)); +tuple_from_req!(TupleFromRequest4, (0, A), (1, B), (2, C), (3, D)); +tuple_from_req!( + TupleFromRequest5, + (0, A), + (1, B), + (2, C), + (3, D), + (4, E) +); +tuple_from_req!( + TupleFromRequest6, + (0, A), + (1, B), + (2, C), + (3, D), + (4, E), + (5, F) +); +tuple_from_req!( + TupleFromRequest7, + (0, A), + (1, B), + (2, C), + (3, D), + (4, E), + (5, F), + (6, G) +); +tuple_from_req!( + TupleFromRequest8, + (0, A), + (1, B), + (2, C), + (3, D), + (4, E), + (5, F), + (6, G), + (7, H) +); +tuple_from_req!( + TupleFromRequest9, + (0, A), + (1, B), + (2, C), + (3, D), + (4, E), + (5, F), + (6, G), + (7, H), + (8, I) +); + #[cfg(test)] mod tests { use super::*; @@ -609,4 +727,37 @@ mod tests { assert_eq!(*Path::::from_request(&mut req, &()).unwrap(), 32); } + + #[test] + fn test_tuple_extract() { + let mut req = TestRequest::with_uri("/name/user1/?id=test").finish(); + + let mut resource = ResourceHandler::<()>::default(); + resource.name("index"); + let mut routes = Vec::new(); + routes.push(( + Resource::new("index", "/{key}/{value}/"), + Some(resource), + )); + let (router, _) = Router::new("", ServerSettings::default(), routes); + assert!(router.recognize(&mut req).is_some()); + + let res = match <(Path<(String, String)>,)>::extract(&req).poll() { + Ok(Async::Ready(res)) => res, + _ => panic!("error"), + }; + assert_eq!((res.0).0, "name"); + assert_eq!((res.0).1, "user1"); + + let res = match <(Path<(String, String)>, Path<(String, String)>)>::extract(&req) + .poll() + { + Ok(Async::Ready(res)) => res, + _ => panic!("error"), + }; + assert_eq!((res.0).0, "name"); + assert_eq!((res.0).1, "user1"); + assert_eq!((res.1).0, "name"); + assert_eq!((res.1).1, "user1"); + } } From 35a4078434596b956aa0141af509f793d33d4ff6 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Wed, 2 May 2018 13:43:51 -0700 Subject: [PATCH 06/10] update changelog --- CHANGES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index ddc9dc846..7a5631535 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -12,6 +12,8 @@ * Allow to access Error's backtrace object +* Various extractor usability improvements #207 + ## 0.5.6 (2018-04-24) From 32a28664492ce9ebd3265ba90092362469b6f32b Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Wed, 2 May 2018 15:53:07 -0700 Subject: [PATCH 07/10] Allow to override files listing renderer for #203 --- CHANGES.md | 2 + src/fs.rs | 120 +++++++++++++++++++++++------------------- src/middleware/mod.rs | 2 +- 3 files changed, 69 insertions(+), 55 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 7a5631535..3955251d7 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -12,6 +12,8 @@ * Allow to access Error's backtrace object +* Allow to override files listing renderer for `StaticFiles` #203 + * Various extractor usability improvements #207 diff --git a/src/fs.rs b/src/fs.rs index 007e97f6b..9d2ea3015 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -365,11 +365,14 @@ impl Stream for ChunkedReadFile { } } +type DirectoryRenderer = + Fn(&Directory, &HttpRequest) -> Result; + /// A directory; responds with the generated directory listing. #[derive(Debug)] pub struct Directory { - base: PathBuf, - path: PathBuf, + pub base: PathBuf, + pub path: PathBuf, } impl Directory { @@ -377,7 +380,7 @@ impl Directory { Directory { base, path } } - fn can_list(&self, entry: &io::Result) -> bool { + pub fn is_visible(&self, entry: &io::Result) -> bool { if let Ok(ref entry) = *entry { if let Some(name) = entry.file_name().to_str() { if name.starts_with('.') { @@ -393,61 +396,58 @@ impl Directory { } } -impl Responder for Directory { - type Item = HttpResponse; - type Error = io::Error; +fn directory_listing( + dir: &Directory, req: &HttpRequest, +) -> Result { + let index_of = format!("Index of {}", req.path()); + let mut body = String::new(); + let base = Path::new(req.path()); - fn respond_to(self, req: HttpRequest) -> Result { - let index_of = format!("Index of {}", req.path()); - let mut body = String::new(); - let base = Path::new(req.path()); + for entry in dir.path.read_dir()? { + if dir.is_visible(&entry) { + let entry = entry.unwrap(); + let p = match entry.path().strip_prefix(&dir.path) { + Ok(p) => base.join(p), + Err(_) => continue, + }; + // show file url as relative to static path + let file_url = format!("{}", p.to_string_lossy()); - for entry in self.path.read_dir()? { - if self.can_list(&entry) { - let entry = entry.unwrap(); - let p = match entry.path().strip_prefix(&self.path) { - Ok(p) => base.join(p), - Err(_) => continue, - }; - // show file url as relative to static path - let file_url = format!("{}", p.to_string_lossy()); - - // if file is a directory, add '/' to the end of the name - if let Ok(metadata) = entry.metadata() { - if metadata.is_dir() { - let _ = write!( - body, - "
  • {}/
  • ", - file_url, - entry.file_name().to_string_lossy() - ); - } else { - let _ = write!( - body, - "
  • {}
  • ", - file_url, - entry.file_name().to_string_lossy() - ); - } + // if file is a directory, add '/' to the end of the name + if let Ok(metadata) = entry.metadata() { + if metadata.is_dir() { + let _ = write!( + body, + "
  • {}/
  • ", + file_url, + entry.file_name().to_string_lossy() + ); } else { - continue; + let _ = write!( + body, + "
  • {}
  • ", + file_url, + entry.file_name().to_string_lossy() + ); } + } else { + continue; } } - - let html = format!( - "\ - {}\ -

    {}

    \ -
      \ - {}\ -
    \n", - index_of, index_of, body - ); - Ok(HttpResponse::Ok() - .content_type("text/html; charset=utf-8") - .body(html)) } + + let html = format!( + "\ + {}\ +

    {}

    \ +
      \ + {}\ +
    \n", + index_of, index_of, body + ); + Ok(HttpResponse::Ok() + .content_type("text/html; charset=utf-8") + .body(html)) } /// Static files handling @@ -472,6 +472,7 @@ pub struct StaticFiles { show_index: bool, cpu_pool: CpuPool, default: Box>, + renderer: Box>, _chunk_size: usize, _follow_symlinks: bool, } @@ -535,6 +536,7 @@ impl StaticFiles { default: Box::new(WrapHandler::new(|_| { HttpResponse::new(StatusCode::NOT_FOUND) })), + renderer: Box::new(directory_listing), _chunk_size: 0, _follow_symlinks: false, } @@ -548,6 +550,17 @@ impl StaticFiles { self } + /// Set custom directory renderer + pub fn files_listing_renderer(mut self, f: F) -> Self + where + for<'r, 's> F: Fn(&'r Directory, &'s HttpRequest) + -> Result + + 'static, + { + self.renderer = Box::new(f); + self + } + /// Set index file /// /// Redirects to specific index file for directory "/" instead of @@ -601,9 +614,8 @@ impl Handler for StaticFiles { .finish() .respond_to(req.drop_state()) } else if self.show_index { - Directory::new(self.directory.clone(), path) - .respond_to(req.drop_state())? - .respond_to(req.drop_state()) + let dir = Directory::new(self.directory.clone(), path); + Ok((*self.renderer)(&dir, &req)?.into()) } else { Ok(self.default.handle(req)) } diff --git a/src/middleware/mod.rs b/src/middleware/mod.rs index f097484f4..2551ded15 100644 --- a/src/middleware/mod.rs +++ b/src/middleware/mod.rs @@ -21,7 +21,7 @@ pub use self::logger::Logger; /// Middleware start result pub enum Started { - /// Execution completed + /// Middleware is completed, continue to next middleware Done, /// New http response got generated. If middleware generates response /// handler execution halts. From 7036656ae4b63385b36d2a5207b0566647b17121 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Wed, 2 May 2018 16:33:29 -0700 Subject: [PATCH 08/10] make Reply generic over error too --- src/handler.rs | 84 +++++++++++++++---------------- src/pipeline.rs | 9 ++-- src/route.rs | 9 ++-- src/scope.rs | 9 ++-- src/test.rs | 11 ++--- src/with.rs | 128 +++++++++++++++++++++--------------------------- 6 files changed, 112 insertions(+), 138 deletions(-) diff --git a/src/handler.rs b/src/handler.rs index 07281b6b3..a6a7eadb8 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -1,5 +1,4 @@ use std::marker::PhantomData; -use std::mem; use std::ops::Deref; use futures::future::{err, ok, Future}; @@ -189,77 +188,74 @@ where /// * Message(T) - ready item /// * Error(Error) - error happen during reply process /// * Future - reply process completes in the future -pub struct Reply(ReplyItem); +pub struct Reply(Option>); -impl Future for Reply { - type Item = T; - type Error = Error; +impl Future for Reply { + type Item = I; + type Error = E; - fn poll(&mut self) -> Poll { - let item = mem::replace(&mut self.0, ReplyItem::None); - - match item { - ReplyItem::Error(err) => Err(err), - ReplyItem::Message(msg) => Ok(Async::Ready(msg)), - ReplyItem::Future(mut fut) => match fut.poll() { + fn poll(&mut self) -> Poll { + let res = self.0.take().expect("use after resolve"); + match res { + ReplyResult::Ok(msg) => Ok(Async::Ready(msg)), + ReplyResult::Err(err) => Err(err), + ReplyResult::Future(mut fut) => match fut.poll() { Ok(Async::NotReady) => { - self.0 = ReplyItem::Future(fut); + self.0 = Some(ReplyResult::Future(fut)); Ok(Async::NotReady) } Ok(Async::Ready(msg)) => Ok(Async::Ready(msg)), Err(err) => Err(err), }, - ReplyItem::None => panic!("use after resolve"), } } } -pub(crate) enum ReplyItem { - None, - Error(Error), - Message(T), - Future(Box>), +pub(crate) enum ReplyResult { + Ok(I), + Err(E), + Future(Box>), } -impl Reply { +impl Reply { /// Create async response #[inline] - pub fn async(fut: F) -> Reply + pub fn async(fut: F) -> Reply where - F: Future + 'static, + F: Future + 'static, { - Reply(ReplyItem::Future(Box::new(fut))) + Reply(Some(ReplyResult::Future(Box::new(fut)))) } /// Send response #[inline] - pub fn response>(response: R) -> Reply { - Reply(ReplyItem::Message(response.into())) + pub fn response>(response: R) -> Reply { + Reply(Some(ReplyResult::Ok(response.into()))) } /// Send error #[inline] - pub fn error>(err: R) -> Reply { - Reply(ReplyItem::Error(err.into())) + pub fn error>(err: R) -> Reply { + Reply(Some(ReplyResult::Err(err.into()))) } #[inline] - pub(crate) fn into(self) -> ReplyItem { - self.0 + pub(crate) fn into(self) -> ReplyResult { + self.0.expect("use after resolve") } #[cfg(test)] pub(crate) fn as_msg(&self) -> &T { match self.0 { - ReplyItem::Message(ref resp) => resp, + ReplyResult::Ok(ref resp) => resp, _ => panic!(), } } #[cfg(test)] - pub(crate) fn as_err(&self) -> Option<&Error> { + pub(crate) fn as_err(&self) -> Option<&E> { match self.0 { - ReplyItem::Error(ref err) => Some(err), + ReplyResult::Err(ref err) => Some(err), _ => None, } } @@ -280,14 +276,14 @@ impl Responder for HttpResponse { #[inline] fn respond_to(self, _: HttpRequest) -> Result, Error> { - Ok(Reply(ReplyItem::Message(self))) + Ok(Reply(Some(ReplyResult::Ok(self)))) } } impl From for Reply { #[inline] fn from(resp: T) -> Reply { - Reply(ReplyItem::Message(resp)) + Reply(Some(ReplyResult::Ok(resp))) } } @@ -311,7 +307,7 @@ impl> From, E>> for Reply { fn from(res: Result, E>) -> Self { match res { Ok(val) => val, - Err(err) => Reply(ReplyItem::Error(err.into())), + Err(err) => Reply(Some(ReplyResult::Err(err.into()))), } } } @@ -320,8 +316,8 @@ impl> From> for Reply { #[inline] fn from(res: Result) -> Self { match res { - Ok(val) => Reply(ReplyItem::Message(val)), - Err(err) => Reply(ReplyItem::Error(err.into())), + Ok(val) => Reply(Some(ReplyResult::Ok(val))), + Err(err) => Reply(Some(ReplyResult::Err(err.into()))), } } } @@ -332,8 +328,8 @@ impl> From>, E>> #[inline] fn from(res: Result>, E>) -> Self { match res { - Ok(fut) => Reply(ReplyItem::Future(fut)), - Err(err) => Reply(ReplyItem::Error(err.into())), + Ok(fut) => Reply(Some(ReplyResult::Future(fut))), + Err(err) => Reply(Some(ReplyResult::Err(err.into()))), } } } @@ -341,7 +337,7 @@ impl> From>, E>> impl From>> for Reply { #[inline] fn from(fut: Box>) -> Reply { - Reply(ReplyItem::Future(fut)) + Reply(Some(ReplyResult::Future(fut))) } } @@ -360,8 +356,8 @@ where fn respond_to(self, req: HttpRequest) -> Result, Error> { let fut = self.map_err(|e| e.into()) .then(move |r| match r.respond_to(req) { - Ok(reply) => match reply.into().0 { - ReplyItem::Message(resp) => ok(resp), + Ok(reply) => match reply.into().into() { + ReplyResult::Ok(resp) => ok(resp), _ => panic!("Nested async replies are not supported"), }, Err(e) => err(e), @@ -456,8 +452,8 @@ where let req2 = req.drop_state(); let fut = (self.h)(req).map_err(|e| e.into()).then(move |r| { match r.respond_to(req2) { - Ok(reply) => match reply.into().0 { - ReplyItem::Message(resp) => ok(resp), + Ok(reply) => match reply.into().into() { + ReplyResult::Ok(resp) => ok(resp), _ => panic!("Nested async replies are not supported"), }, Err(e) => err(e), diff --git a/src/pipeline.rs b/src/pipeline.rs index 398738e61..b6ddfff3f 100644 --- a/src/pipeline.rs +++ b/src/pipeline.rs @@ -11,7 +11,7 @@ use application::Inner; use body::{Body, BodyStream}; use context::{ActorHttpContext, Frame}; use error::Error; -use handler::{Reply, ReplyItem}; +use handler::{Reply, ReplyResult}; use header::ContentEncoding; use httprequest::HttpRequest; use httpresponse::HttpResponse; @@ -324,14 +324,13 @@ impl WaitingResponse { info: &mut PipelineInfo, reply: Reply, ) -> PipelineState { match reply.into() { - ReplyItem::Error(err) => RunMiddlewares::init(info, err.into()), - ReplyItem::Message(resp) => RunMiddlewares::init(info, resp), - ReplyItem::Future(fut) => PipelineState::Handler(WaitingResponse { + ReplyResult::Err(err) => RunMiddlewares::init(info, err.into()), + ReplyResult::Ok(resp) => RunMiddlewares::init(info, resp), + ReplyResult::Future(fut) => PipelineState::Handler(WaitingResponse { fut, _s: PhantomData, _h: PhantomData, }), - ReplyItem::None => panic!("use after resolve"), } } diff --git a/src/route.rs b/src/route.rs index d5137c575..7dda988ae 100644 --- a/src/route.rs +++ b/src/route.rs @@ -5,7 +5,7 @@ use std::rc::Rc; use futures::{Async, Future, Poll}; use error::Error; -use handler::{AsyncHandler, FromRequest, Handler, Reply, ReplyItem, Responder, +use handler::{AsyncHandler, FromRequest, Handler, Reply, ReplyResult, Responder, RouteHandler, WrapHandler}; use http::StatusCode; use httprequest::HttpRequest; @@ -417,13 +417,12 @@ impl WaitingResponse { #[inline] fn init(info: &mut ComposeInfo, reply: Reply) -> ComposeState { match reply.into() { - ReplyItem::Error(err) => RunMiddlewares::init(info, err.into()), - ReplyItem::Message(resp) => RunMiddlewares::init(info, resp), - ReplyItem::Future(fut) => ComposeState::Handler(WaitingResponse { + ReplyResult::Err(err) => RunMiddlewares::init(info, err.into()), + ReplyResult::Ok(resp) => RunMiddlewares::init(info, resp), + ReplyResult::Future(fut) => ComposeState::Handler(WaitingResponse { fut, _s: PhantomData, }), - ReplyItem::None => panic!("use after resolve"), } } diff --git a/src/scope.rs b/src/scope.rs index 23c95e682..f48308f2a 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -5,7 +5,7 @@ use std::rc::Rc; use futures::{Async, Future, Poll}; use error::Error; -use handler::{FromRequest, Reply, ReplyItem, Responder, RouteHandler}; +use handler::{FromRequest, Reply, ReplyResult, Responder, RouteHandler}; use http::Method; use httprequest::HttpRequest; use httpresponse::HttpResponse; @@ -523,13 +523,12 @@ impl WaitingResponse { #[inline] fn init(info: &mut ComposeInfo, reply: Reply) -> ComposeState { match reply.into() { - ReplyItem::Message(resp) => RunMiddlewares::init(info, resp), - ReplyItem::Error(err) => RunMiddlewares::init(info, err.into()), - ReplyItem::Future(fut) => ComposeState::Handler(WaitingResponse { + ReplyResult::Ok(resp) => RunMiddlewares::init(info, resp), + ReplyResult::Err(err) => RunMiddlewares::init(info, err.into()), + ReplyResult::Future(fut) => ComposeState::Handler(WaitingResponse { fut, _s: PhantomData, }), - ReplyItem::None => panic!("use after resolve"), } } diff --git a/src/test.rs b/src/test.rs index 6b62a5ceb..08d05ba8d 100644 --- a/src/test.rs +++ b/src/test.rs @@ -21,7 +21,7 @@ use application::{App, HttpApplication}; use body::Binary; use client::{ClientConnector, ClientRequest, ClientRequestBuilder}; use error::Error; -use handler::{Handler, ReplyItem, Responder}; +use handler::{Handler, ReplyResult, Responder}; use header::{Header, IntoHeaderValue}; use httprequest::HttpRequest; use httpresponse::HttpResponse; @@ -601,10 +601,9 @@ impl TestRequest { match resp.respond_to(req.drop_state()) { Ok(resp) => match resp.into().into() { - ReplyItem::Message(resp) => Ok(resp), - ReplyItem::Error(err) => Ok(err.into()), - ReplyItem::Future(_) => panic!("Async handler is not supported."), - ReplyItem::None => panic!("use after resolve"), + ReplyResult::Ok(resp) => Ok(resp), + ReplyResult::Err(err) => Ok(err.into()), + ReplyResult::Future(_) => panic!("Async handler is not supported."), }, Err(err) => Err(err), } @@ -628,7 +627,7 @@ impl TestRequest { match core.run(fut) { Ok(r) => match r.respond_to(req.drop_state()) { Ok(reply) => match reply.into().into() { - ReplyItem::Message(resp) => Ok(resp), + ReplyResult::Ok(resp) => Ok(resp), _ => panic!("Nested async replies are not supported"), }, Err(e) => Err(e), diff --git a/src/with.rs b/src/with.rs index e522d97ec..3ee9a6a10 100644 --- a/src/with.rs +++ b/src/with.rs @@ -5,7 +5,7 @@ use std::ops::{Deref, DerefMut}; use std::rc::Rc; use error::Error; -use handler::{FromRequest, Handler, Reply, ReplyItem, Responder}; +use handler::{FromRequest, Handler, Reply, ReplyResult, Responder}; use httprequest::HttpRequest; use httpresponse::HttpResponse; @@ -136,13 +136,12 @@ where self.started = true; let reply = T::from_request(&self.req, self.cfg.as_ref()).into(); match reply.into() { - ReplyItem::Error(err) => return Err(err), - ReplyItem::Message(msg) => msg, - ReplyItem::Future(fut) => { + ReplyResult::Err(err) => return Err(err), + ReplyResult::Ok(msg) => msg, + ReplyResult::Future(fut) => { self.fut1 = Some(fut); return self.poll(); } - ReplyItem::None => panic!("use after resolve"), } } else { match self.fut1.as_mut().unwrap().poll()? { @@ -158,13 +157,12 @@ where }; match item.into() { - ReplyItem::Error(err) => Err(err), - ReplyItem::Message(resp) => Ok(Async::Ready(resp)), - ReplyItem::Future(fut) => { + ReplyResult::Err(err) => Err(err), + ReplyResult::Ok(resp) => Ok(Async::Ready(resp)), + ReplyResult::Future(fut) => { self.fut2 = Some(fut); self.poll() } - ReplyItem::None => panic!("use after resolve"), } } } @@ -270,37 +268,34 @@ where self.started = true; let reply = T1::from_request(&self.req, self.cfg1.as_ref()).into(); let item1 = match reply.into() { - ReplyItem::Error(err) => return Err(err), - ReplyItem::Message(msg) => msg, - ReplyItem::Future(fut) => { + ReplyResult::Err(err) => return Err(err), + ReplyResult::Ok(msg) => msg, + ReplyResult::Future(fut) => { self.fut1 = Some(fut); return self.poll(); } - ReplyItem::None => panic!("use after resolve"), }; let reply = T2::from_request(&self.req, self.cfg2.as_ref()).into(); let item2 = match reply.into() { - ReplyItem::Error(err) => return Err(err), - ReplyItem::Message(msg) => msg, - ReplyItem::Future(fut) => { + ReplyResult::Err(err) => return Err(err), + ReplyResult::Ok(msg) => msg, + ReplyResult::Future(fut) => { self.item = Some(item1); self.fut2 = Some(fut); return self.poll(); } - ReplyItem::None => panic!("use after resolve"), }; let hnd: &mut F = unsafe { &mut *self.hnd.get() }; match (*hnd)(item1, item2).respond_to(self.req.drop_state()) { Ok(item) => match item.into().into() { - ReplyItem::Error(err) => return Err(err), - ReplyItem::Message(resp) => return Ok(Async::Ready(resp)), - ReplyItem::Future(fut) => { + ReplyResult::Err(err) => return Err(err), + ReplyResult::Ok(resp) => return Ok(Async::Ready(resp)), + ReplyResult::Future(fut) => { self.fut3 = Some(fut); return self.poll(); } - ReplyItem::None => panic!("use after resolve"), }, Err(e) => return Err(e.into()), } @@ -311,26 +306,24 @@ where Async::Ready(item) => { let reply = T2::from_request(&self.req, self.cfg2.as_ref()).into(); let item2 = match reply.into() { - ReplyItem::Error(err) => return Err(err), - ReplyItem::Message(msg) => msg, - ReplyItem::Future(fut) => { + ReplyResult::Err(err) => return Err(err), + ReplyResult::Ok(msg) => msg, + ReplyResult::Future(fut) => { self.item = Some(item); self.fut2 = Some(fut); return self.poll(); } - ReplyItem::None => panic!("use after resolve"), }; let hnd: &mut F = unsafe { &mut *self.hnd.get() }; match (*hnd)(item, item2).respond_to(self.req.drop_state()) { Ok(item) => match item.into().into() { - ReplyItem::Error(err) => return Err(err), - ReplyItem::Message(resp) => return Ok(Async::Ready(resp)), - ReplyItem::Future(fut) => { + ReplyResult::Err(err) => return Err(err), + ReplyResult::Ok(resp) => return Ok(Async::Ready(resp)), + ReplyResult::Future(fut) => { self.fut3 = Some(fut); return self.poll(); } - ReplyItem::None => panic!("use after resolve"), }, Err(e) => return Err(e.into()), } @@ -353,10 +346,9 @@ where }; match item.into() { - ReplyItem::Error(err) => return Err(err), - ReplyItem::Message(resp) => return Ok(Async::Ready(resp)), - ReplyItem::Future(fut) => self.fut3 = Some(fut), - ReplyItem::None => panic!("use after resolve"), + ReplyResult::Err(err) => return Err(err), + ReplyResult::Ok(resp) => return Ok(Async::Ready(resp)), + ReplyResult::Future(fut) => self.fut3 = Some(fut), } self.poll() @@ -481,50 +473,46 @@ where self.started = true; let reply = T1::from_request(&self.req, self.cfg1.as_ref()).into(); let item1 = match reply.into() { - ReplyItem::Error(err) => return Err(err), - ReplyItem::Message(msg) => msg, - ReplyItem::Future(fut) => { + ReplyResult::Err(err) => return Err(err), + ReplyResult::Ok(msg) => msg, + ReplyResult::Future(fut) => { self.fut1 = Some(fut); return self.poll(); } - ReplyItem::None => panic!("use after resolve"), }; let reply = T2::from_request(&self.req, self.cfg2.as_ref()).into(); let item2 = match reply.into() { - ReplyItem::Error(err) => return Err(err), - ReplyItem::Message(msg) => msg, - ReplyItem::Future(fut) => { + ReplyResult::Err(err) => return Err(err), + ReplyResult::Ok(msg) => msg, + ReplyResult::Future(fut) => { self.item1 = Some(item1); self.fut2 = Some(fut); return self.poll(); } - ReplyItem::None => panic!("use after resolve"), }; let reply = T3::from_request(&self.req, self.cfg3.as_ref()).into(); let item3 = match reply.into() { - ReplyItem::Error(err) => return Err(err), - ReplyItem::Message(msg) => msg, - ReplyItem::Future(fut) => { + ReplyResult::Err(err) => return Err(err), + ReplyResult::Ok(msg) => msg, + ReplyResult::Future(fut) => { self.item1 = Some(item1); self.item2 = Some(item2); self.fut3 = Some(fut); return self.poll(); } - ReplyItem::None => panic!("use after resolve"), }; let hnd: &mut F = unsafe { &mut *self.hnd.get() }; match (*hnd)(item1, item2, item3).respond_to(self.req.drop_state()) { Ok(item) => match item.into().into() { - ReplyItem::Error(err) => return Err(err), - ReplyItem::Message(resp) => return Ok(Async::Ready(resp)), - ReplyItem::Future(fut) => { + ReplyResult::Err(err) => return Err(err), + ReplyResult::Ok(resp) => return Ok(Async::Ready(resp)), + ReplyResult::Future(fut) => { self.fut4 = Some(fut); return self.poll(); } - ReplyItem::None => panic!("use after resolve"), }, Err(e) => return Err(e.into()), } @@ -537,38 +525,35 @@ where self.fut1.take(); let reply = T2::from_request(&self.req, self.cfg2.as_ref()).into(); let item2 = match reply.into() { - ReplyItem::Error(err) => return Err(err), - ReplyItem::Message(msg) => msg, - ReplyItem::Future(fut) => { + ReplyResult::Err(err) => return Err(err), + ReplyResult::Ok(msg) => msg, + ReplyResult::Future(fut) => { self.fut2 = Some(fut); return self.poll(); } - ReplyItem::None => panic!("use after resolve"), }; let reply = T3::from_request(&self.req, self.cfg3.as_ref()).into(); let item3 = match reply.into() { - ReplyItem::Error(err) => return Err(err), - ReplyItem::Message(msg) => msg, - ReplyItem::Future(fut) => { + ReplyResult::Err(err) => return Err(err), + ReplyResult::Ok(msg) => msg, + ReplyResult::Future(fut) => { self.item2 = Some(item2); self.fut3 = Some(fut); return self.poll(); } - ReplyItem::None => panic!("use after resolve"), }; let hnd: &mut F = unsafe { &mut *self.hnd.get() }; match (*hnd)(self.item1.take().unwrap(), item2, item3) .respond_to(self.req.drop_state()) { Ok(item) => match item.into().into() { - ReplyItem::Error(err) => return Err(err), - ReplyItem::Message(resp) => return Ok(Async::Ready(resp)), - ReplyItem::Future(fut) => { + ReplyResult::Err(err) => return Err(err), + ReplyResult::Ok(resp) => return Ok(Async::Ready(resp)), + ReplyResult::Future(fut) => { self.fut4 = Some(fut); return self.poll(); } - ReplyItem::None => panic!("use after resolve"), }, Err(e) => return Err(e.into()), } @@ -583,27 +568,25 @@ where self.fut2.take(); let reply = T3::from_request(&self.req, self.cfg3.as_ref()).into(); let item3 = match reply.into() { - ReplyItem::Error(err) => return Err(err), - ReplyItem::Message(msg) => msg, - ReplyItem::Future(fut) => { + ReplyResult::Err(err) => return Err(err), + ReplyResult::Ok(msg) => msg, + ReplyResult::Future(fut) => { self.item2 = Some(item); self.fut3 = Some(fut); return self.poll(); } - ReplyItem::None => panic!("use after resolve"), }; let hnd: &mut F = unsafe { &mut *self.hnd.get() }; match (*hnd)(self.item1.take().unwrap(), item, item3) .respond_to(self.req.drop_state()) { Ok(item) => match item.into().into() { - ReplyItem::Error(err) => return Err(err), - ReplyItem::Message(resp) => return Ok(Async::Ready(resp)), - ReplyItem::Future(fut) => { + ReplyResult::Err(err) => return Err(err), + ReplyResult::Ok(resp) => return Ok(Async::Ready(resp)), + ReplyResult::Future(fut) => { self.fut4 = Some(fut); return self.poll(); } - ReplyItem::None => panic!("use after resolve"), }, Err(e) => return Err(e.into()), } @@ -629,10 +612,9 @@ where }; match item.into() { - ReplyItem::Error(err) => return Ok(Async::Ready(err.into())), - ReplyItem::Message(resp) => return Ok(Async::Ready(resp)), - ReplyItem::Future(fut) => self.fut4 = Some(fut), - ReplyItem::None => panic!("use after resolve"), + ReplyResult::Err(err) => return Ok(Async::Ready(err.into())), + ReplyResult::Ok(resp) => return Ok(Async::Ready(resp)), + ReplyResult::Future(fut) => self.fut4 = Some(fut), } self.poll() From 3623383e8389bbd8ef8aa9c5edffdd96fa10d8d2 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Wed, 2 May 2018 16:48:42 -0700 Subject: [PATCH 09/10] fix tests --- src/handler.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/handler.rs b/src/handler.rs index a6a7eadb8..cafb90867 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -245,8 +245,8 @@ impl Reply { } #[cfg(test)] - pub(crate) fn as_msg(&self) -> &T { - match self.0 { + pub(crate) fn as_msg(&self) -> &I { + match self.0.as_ref().unwrap() { ReplyResult::Ok(ref resp) => resp, _ => panic!(), } @@ -254,7 +254,7 @@ impl Reply { #[cfg(test)] pub(crate) fn as_err(&self) -> Option<&E> { - match self.0 { + match self.0.as_ref().unwrap() { ReplyResult::Err(ref err) => Some(err), _ => None, } From 58079b5bbe5d808554a56e3383168ba51ffa6548 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Wed, 2 May 2018 19:11:44 -0700 Subject: [PATCH 10/10] add session test --- src/handler.rs | 4 ++-- src/middleware/session.rs | 29 ++++++++++++++++++++++++++++- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/handler.rs b/src/handler.rs index cafb90867..0a1bd9f59 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -247,7 +247,7 @@ impl Reply { #[cfg(test)] pub(crate) fn as_msg(&self) -> &I { match self.0.as_ref().unwrap() { - ReplyResult::Ok(ref resp) => resp, + &ReplyResult::Ok(ref resp) => resp, _ => panic!(), } } @@ -255,7 +255,7 @@ impl Reply { #[cfg(test)] pub(crate) fn as_err(&self) -> Option<&E> { match self.0.as_ref().unwrap() { - ReplyResult::Err(ref err) => Some(err), + &ReplyResult::Err(ref err) => Some(err), _ => None, } } diff --git a/src/middleware/session.rs b/src/middleware/session.rs index 13948ac2a..b926842f5 100644 --- a/src/middleware/session.rs +++ b/src/middleware/session.rs @@ -37,7 +37,7 @@ //! use actix_web::{server, App, HttpRequest, Result}; //! use actix_web::middleware::session::{RequestSession, SessionStorage, CookieSessionBackend}; //! -//! fn index(mut req: HttpRequest) -> Result<&'static str> { +//! fn index(req: HttpRequest) -> Result<&'static str> { //! // access session data //! if let Some(count) = req.session().get::("counter")? { //! println!("SESSION value: {}", count); @@ -525,3 +525,30 @@ impl SessionBackend for CookieSessionBackend { }) } } + +#[cfg(test)] +mod tests { + use super::*; + use application::App; + use test; + + #[test] + fn cookie_session() { + let mut srv = test::TestServer::with_factory(|| { + App::new() + .middleware(SessionStorage::new( + CookieSessionBackend::signed(&[0; 32]).secure(false), + )) + .resource("/", |r| { + r.f(|req| { + let _ = req.session().set("counter", 100); + "test" + }) + }) + }); + + let request = srv.get().uri(srv.url("/")).finish().unwrap(); + let response = srv.execute(request.send()).unwrap(); + assert!(response.cookie("actix-session").is_some()); + } +}