1
0
mirror of https://github.com/actix/actix-extras.git synced 2024-11-28 01:32:57 +01:00

Merge branch 'master' into ws-trait

This commit is contained in:
Nikolay Kim 2018-05-03 08:57:45 -07:00 committed by GitHub
commit bb61dd41af
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 421 additions and 245 deletions

View File

@ -12,6 +12,10 @@
* Allow to access Error's backtrace object
* Allow to override files listing renderer for `StaticFiles` #203
* Various extractor usability improvements #207
## 0.5.6 (2018-04-24)

View File

@ -15,6 +15,20 @@
* `FromRequest::Result` has to implement `Into<Reply<Self>>`
* `HttpRequest::query()` is deprecated. Use `Query` extractor.
```rust
fn index(q: Query<HashMap<String, String>>) -> Result<..> {
...
}
```
or
```rust
let q = Query::<HashMap<String, String>>::extract(req);
```
## Migration from 0.4 to 0.5

View File

@ -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());
}

View File

@ -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;
@ -99,13 +100,12 @@ impl<T> Path<T> {
impl<T, S> FromRequest<S> for Path<T>
where
T: DeserializeOwned,
S: 'static,
{
type Config = ();
type Result = Result<Self, Error>;
#[inline]
fn from_request(req: &mut HttpRequest<S>, _: &Self::Config) -> Self::Result {
fn from_request(req: &HttpRequest<S>, _: &Self::Config) -> Self::Result {
let req = req.clone();
de::Deserialize::deserialize(PathDeserializer::new(&req))
.map_err(|e| e.into())
@ -167,13 +167,12 @@ impl<T> Query<T> {
impl<T, S> FromRequest<S> for Query<T>
where
T: de::DeserializeOwned,
S: 'static,
{
type Config = ();
type Result = Result<Self, Error>;
#[inline]
fn from_request(req: &mut HttpRequest<S>, _: &Self::Config) -> Self::Result {
fn from_request(req: &HttpRequest<S>, _: &Self::Config) -> Self::Result {
let req = req.clone();
serde_urlencoded::from_str::<T>(req.query_string())
.map_err(|e| e.into())
@ -241,7 +240,7 @@ where
type Result = Box<Future<Item = Self, Error = Error>>;
#[inline]
fn from_request(req: &mut HttpRequest<S>, cfg: &Self::Config) -> Self::Result {
fn from_request(req: &HttpRequest<S>, cfg: &Self::Config) -> Self::Result {
Box::new(
UrlEncoded::new(req.clone())
.limit(cfg.limit)
@ -326,7 +325,7 @@ impl<S: 'static> FromRequest<S> for Bytes {
type Result = Result<Box<Future<Item = Self, Error = Error>>, Error>;
#[inline]
fn from_request(req: &mut HttpRequest<S>, cfg: &Self::Config) -> Self::Result {
fn from_request(req: &HttpRequest<S>, cfg: &Self::Config) -> Self::Result {
// check content-type
cfg.check_mimetype(req)?;
@ -370,7 +369,7 @@ impl<S: 'static> FromRequest<S> for String {
type Result = Result<Box<Future<Item = String, Error = Error>>, Error>;
#[inline]
fn from_request(req: &mut HttpRequest<S>, cfg: &Self::Config) -> Self::Result {
fn from_request(req: &HttpRequest<S>, cfg: &Self::Config) -> Self::Result {
// check content-type
cfg.check_mimetype(req)?;
@ -447,6 +446,123 @@ impl Default for PayloadConfig {
}
}
macro_rules! tuple_from_req ({$fut_type:ident, $(($n:tt, $T:ident)),+} => {
/// FromRequest implementation for tuple
impl<S, $($T: FromRequest<S> + 'static),+> FromRequest<S> for ($($T,)+)
where
S: 'static,
{
type Config = ($($T::Config,)+);
type Result = Box<Future<Item = ($($T,)+), Error = Error>>;
fn from_request(req: &HttpRequest<S>, 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<S, $($T: FromRequest<S>),+>
where
S: 'static,
{
s: PhantomData<S>,
items: ($(Option<$T>,)+),
futs: ($(Option<Reply<$T>>,)+),
}
impl<S, $($T: FromRequest<S>),+> Future for $fut_type<S, $($T),+>
where
S: 'static,
{
type Item = ($($T,)+);
type Error = Error;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
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::*;
@ -471,7 +587,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 +606,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 +630,7 @@ mod tests {
let mut cfg = FormConfig::default();
cfg.limit(4096);
match Form::<Info>::from_request(&mut req, &cfg)
.poll()
.unwrap()
{
match Form::<Info>::from_request(&req, &cfg).poll().unwrap() {
Async::Ready(s) => {
assert_eq!(s.hello, "world");
}
@ -574,29 +687,29 @@ mod tests {
let (router, _) = Router::new("", ServerSettings::default(), routes);
assert!(router.recognize(&mut req).is_some());
let s = Path::<MyStruct>::from_request(&mut req, &()).unwrap();
let s = Path::<MyStruct>::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::<Id>::from_request(&mut req, &()).unwrap();
let s = Query::<Id>::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::<Test2>::from_request(&mut req, &()).unwrap();
let s = Path::<Test2>::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::<Vec<String>>::from_request(&mut req, &()).unwrap();
let res = Path::<Vec<String>>::extract(&req).unwrap();
assert_eq!(res[0], "name".to_owned());
assert_eq!(res[1], "32".to_owned());
}
@ -614,4 +727,37 @@ mod tests {
assert_eq!(*Path::<i8>::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");
}
}

120
src/fs.rs
View File

@ -365,11 +365,14 @@ impl Stream for ChunkedReadFile {
}
}
type DirectoryRenderer<S> =
Fn(&Directory, &HttpRequest<S>) -> Result<HttpResponse, io::Error>;
/// 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<DirEntry>) -> bool {
pub fn is_visible(&self, entry: &io::Result<DirEntry>) -> 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<S>(
dir: &Directory, req: &HttpRequest<S>,
) -> Result<HttpResponse, io::Error> {
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<HttpResponse, io::Error> {
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,
"<li><a href=\"{}\">{}/</a></li>",
file_url,
entry.file_name().to_string_lossy()
);
} else {
let _ = write!(
body,
"<li><a href=\"{}\">{}</a></li>",
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,
"<li><a href=\"{}\">{}/</a></li>",
file_url,
entry.file_name().to_string_lossy()
);
} else {
continue;
let _ = write!(
body,
"<li><a href=\"{}\">{}</a></li>",
file_url,
entry.file_name().to_string_lossy()
);
}
} else {
continue;
}
}
let html = format!(
"<html>\
<head><title>{}</title></head>\
<body><h1>{}</h1>\
<ul>\
{}\
</ul></body>\n</html>",
index_of, index_of, body
);
Ok(HttpResponse::Ok()
.content_type("text/html; charset=utf-8")
.body(html))
}
let html = format!(
"<html>\
<head><title>{}</title></head>\
<body><h1>{}</h1>\
<ul>\
{}\
</ul></body>\n</html>",
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<S> {
show_index: bool,
cpu_pool: CpuPool,
default: Box<RouteHandler<S>>,
renderer: Box<DirectoryRenderer<S>>,
_chunk_size: usize,
_follow_symlinks: bool,
}
@ -535,6 +536,7 @@ impl<S: 'static> StaticFiles<S> {
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<S: 'static> StaticFiles<S> {
self
}
/// Set custom directory renderer
pub fn files_listing_renderer<F>(mut self, f: F) -> Self
where
for<'r, 's> F: Fn(&'r Directory, &'s HttpRequest<S>)
-> Result<HttpResponse, io::Error>
+ 'static,
{
self.renderer = Box::new(f);
self
}
/// Set index file
///
/// Redirects to specific index file for directory "/" instead of
@ -601,9 +614,8 @@ impl<S: 'static> Handler<S> for StaticFiles<S> {
.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))
}

View File

@ -1,5 +1,4 @@
use std::marker::PhantomData;
use std::mem;
use std::ops::Deref;
use futures::future::{err, ok, Future};
@ -36,10 +35,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<S>: Sized
where
S: 'static,
{
pub trait FromRequest<S>: Sized {
/// Configuration for conversion process
type Config: Default;
@ -47,7 +43,14 @@ where
type Result: Into<Reply<Self>>;
/// Convert request to a Self
fn from_request(req: &mut HttpRequest<S>, cfg: &Self::Config) -> Self::Result;
fn from_request(req: &HttpRequest<S>, cfg: &Self::Config) -> Self::Result;
/// Convert request to a Self
///
/// This method uses default extractor configuration
fn extract(req: &HttpRequest<S>) -> Self::Result {
Self::from_request(req, &Self::Config::default())
}
}
/// Combines two different responder types into a single type
@ -185,77 +188,74 @@ where
/// * Message(T) - ready item
/// * Error(Error) - error happen during reply process
/// * Future<T, Error> - reply process completes in the future
pub struct Reply<T>(ReplyItem<T>);
pub struct Reply<I, E = Error>(Option<ReplyResult<I, E>>);
impl<T> Future for Reply<T> {
type Item = T;
type Error = Error;
impl<I, E> Future for Reply<I, E> {
type Item = I;
type Error = E;
fn poll(&mut self) -> Poll<T, Error> {
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<I, E> {
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<T> {
None,
Error(Error),
Message(T),
Future(Box<Future<Item = T, Error = Error>>),
pub(crate) enum ReplyResult<I, E> {
Ok(I),
Err(E),
Future(Box<Future<Item = I, Error = E>>),
}
impl<T> Reply<T> {
impl<I, E> Reply<I, E> {
/// Create async response
#[inline]
pub fn async<F>(fut: F) -> Reply<T>
pub fn async<F>(fut: F) -> Reply<I, E>
where
F: Future<Item = T, Error = Error> + 'static,
F: Future<Item = I, Error = E> + 'static,
{
Reply(ReplyItem::Future(Box::new(fut)))
Reply(Some(ReplyResult::Future(Box::new(fut))))
}
/// Send response
#[inline]
pub fn response<R: Into<T>>(response: R) -> Reply<T> {
Reply(ReplyItem::Message(response.into()))
pub fn response<R: Into<I>>(response: R) -> Reply<I, E> {
Reply(Some(ReplyResult::Ok(response.into())))
}
/// Send error
#[inline]
pub fn error<R: Into<Error>>(err: R) -> Reply<T> {
Reply(ReplyItem::Error(err.into()))
pub fn error<R: Into<E>>(err: R) -> Reply<I, E> {
Reply(Some(ReplyResult::Err(err.into())))
}
#[inline]
pub(crate) fn into(self) -> ReplyItem<T> {
self.0
pub(crate) fn into(self) -> ReplyResult<I, E> {
self.0.expect("use after resolve")
}
#[cfg(test)]
pub(crate) fn as_msg(&self) -> &T {
match self.0 {
ReplyItem::Message(ref resp) => resp,
pub(crate) fn as_msg(&self) -> &I {
match self.0.as_ref().unwrap() {
&ReplyResult::Ok(ref resp) => resp,
_ => panic!(),
}
}
#[cfg(test)]
pub(crate) fn as_err(&self) -> Option<&Error> {
match self.0 {
ReplyItem::Error(ref err) => Some(err),
pub(crate) fn as_err(&self) -> Option<&E> {
match self.0.as_ref().unwrap() {
&ReplyResult::Err(ref err) => Some(err),
_ => None,
}
}
@ -276,14 +276,14 @@ impl Responder for HttpResponse {
#[inline]
fn respond_to(self, _: HttpRequest) -> Result<Reply<HttpResponse>, Error> {
Ok(Reply(ReplyItem::Message(self)))
Ok(Reply(Some(ReplyResult::Ok(self))))
}
}
impl<T> From<T> for Reply<T> {
#[inline]
fn from(resp: T) -> Reply<T> {
Reply(ReplyItem::Message(resp))
Reply(Some(ReplyResult::Ok(resp)))
}
}
@ -307,7 +307,7 @@ impl<T, E: Into<Error>> From<Result<Reply<T>, E>> for Reply<T> {
fn from(res: Result<Reply<T>, E>) -> Self {
match res {
Ok(val) => val,
Err(err) => Reply(ReplyItem::Error(err.into())),
Err(err) => Reply(Some(ReplyResult::Err(err.into()))),
}
}
}
@ -316,8 +316,8 @@ impl<T, E: Into<Error>> From<Result<T, E>> for Reply<T> {
#[inline]
fn from(res: Result<T, E>) -> 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()))),
}
}
}
@ -328,8 +328,8 @@ impl<T, E: Into<Error>> From<Result<Box<Future<Item = T, Error = Error>>, E>>
#[inline]
fn from(res: Result<Box<Future<Item = T, Error = Error>>, 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()))),
}
}
}
@ -337,7 +337,7 @@ impl<T, E: Into<Error>> From<Result<Box<Future<Item = T, Error = Error>>, E>>
impl<T> From<Box<Future<Item = T, Error = Error>>> for Reply<T> {
#[inline]
fn from(fut: Box<Future<Item = T, Error = Error>>) -> Reply<T> {
Reply(ReplyItem::Future(fut))
Reply(Some(ReplyResult::Future(fut)))
}
}
@ -356,8 +356,8 @@ where
fn respond_to(self, req: HttpRequest) -> Result<Reply<HttpResponse>, 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),
@ -452,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),
@ -505,12 +505,12 @@ impl<S> Deref for State<S> {
}
}
impl<S: 'static> FromRequest<S> for State<S> {
impl<S> FromRequest<S> for State<S> {
type Config = ();
type Result = State<S>;
#[inline]
fn from_request(req: &mut HttpRequest<S>, _: &Self::Config) -> Self::Result {
State(req.clone()).into()
fn from_request(req: &HttpRequest<S>, _: &Self::Config) -> Self::Result {
State(req.clone())
}
}

View File

@ -500,12 +500,12 @@ impl<S> Clone for HttpRequest<S> {
}
}
impl<S: 'static> FromRequest<S> for HttpRequest<S> {
impl<S> FromRequest<S> for HttpRequest<S> {
type Config = ();
type Result = Self;
#[inline]
fn from_request(req: &mut HttpRequest<S>, _: &Self::Config) -> Self::Result {
fn from_request(req: &HttpRequest<S>, _: &Self::Config) -> Self::Result {
req.clone()
}
}

View File

@ -136,7 +136,7 @@ where
type Result = Box<Future<Item = Self, Error = Error>>;
#[inline]
fn from_request(req: &mut HttpRequest<S>, cfg: &Self::Config) -> Self::Result {
fn from_request(req: &HttpRequest<S>, cfg: &Self::Config) -> Self::Result {
let req = req.clone();
let err = Rc::clone(&cfg.ehandler);
Box::new(

View File

@ -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.

View File

@ -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::<i32>("counter")? {
//! println!("SESSION value: {}", count);
@ -525,3 +525,30 @@ impl<S> SessionBackend<S> 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());
}
}

View File

@ -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<S: 'static, H> WaitingResponse<S, H> {
info: &mut PipelineInfo<S>, reply: Reply<HttpResponse>,
) -> PipelineState<S, H> {
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"),
}
}

View File

@ -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<S: 'static> WaitingResponse<S> {
#[inline]
fn init(info: &mut ComposeInfo<S>, reply: Reply<HttpResponse>) -> ComposeState<S> {
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"),
}
}

View File

@ -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<S: 'static> WaitingResponse<S> {
#[inline]
fn init(info: &mut ComposeInfo<S>, reply: Reply<HttpResponse>) -> ComposeState<S> {
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"),
}
}

View File

@ -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<S> TestRequest<S> {
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<S> TestRequest<S> {
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),

View File

@ -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;
@ -134,15 +134,14 @@ 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,
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"),
}
}
}
@ -268,39 +266,36 @@ 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,
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(&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,
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()),
}
@ -309,29 +304,26 @@ 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,
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()),
}
@ -354,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()
@ -480,52 +471,48 @@ 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,
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(&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,
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(&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,
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()),
}
@ -536,42 +523,37 @@ 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,
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(&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,
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()),
}
@ -584,30 +566,27 @@ 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,
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()),
}
@ -633,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()