From b367f07d56e8f4a7cb5e7bb5af35d03bf9479925 Mon Sep 17 00:00:00 2001 From: Denis Kolodin Date: Mon, 23 Jul 2018 12:29:25 +0300 Subject: [PATCH 1/7] Add http_only flag to CookieSessionBackend --- CHANGES.md | 1 + src/middleware/session.rs | 10 +++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index d83736eb5..04c004fa7 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -6,6 +6,7 @@ * Fixed default_resource 'not yet implemented' panic #410 +* Add `CookieSessionBackend::http_only` method to set `HttpOnly` directive of cookies ## [0.7.0] - 2018-07-21 diff --git a/src/middleware/session.rs b/src/middleware/session.rs index 40ba0f4dd..cc7aab6b4 100644 --- a/src/middleware/session.rs +++ b/src/middleware/session.rs @@ -358,6 +358,7 @@ struct CookieSessionInner { path: String, domain: Option, secure: bool, + http_only: bool, max_age: Option, same_site: Option, } @@ -371,6 +372,7 @@ impl CookieSessionInner { path: "/".to_owned(), domain: None, secure: true, + http_only: true, max_age: None, same_site: None, } @@ -388,7 +390,7 @@ impl CookieSessionInner { let mut cookie = Cookie::new(self.name.clone(), value); cookie.set_path(self.path.clone()); cookie.set_secure(self.secure); - cookie.set_http_only(true); + cookie.set_http_only(self.http_only); if let Some(ref domain) = self.domain { cookie.set_domain(domain.clone()); @@ -532,6 +534,12 @@ impl CookieSessionBackend { self } + /// Sets the `http_only` field in the session cookie being built. + pub fn http_only(mut self, value: bool) -> CookieSessionBackend { + Rc::get_mut(&mut self.0).unwrap().http_only = value; + self + } + /// Sets the `same_site` field in the session cookie being built. pub fn same_site(mut self, value: SameSite) -> CookieSessionBackend { Rc::get_mut(&mut self.0).unwrap().same_site = Some(value); From c352a69d5433592101790dac0e4e34d1bc7880d8 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Mon, 23 Jul 2018 13:22:16 -0700 Subject: [PATCH 2/7] fix dead links --- README.md | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/README.md b/README.md index 632a33dc9..ec8c439ef 100644 --- a/README.md +++ b/README.md @@ -12,12 +12,7 @@ Actix web is a simple, pragmatic and extremely fast web framework for Rust. * Multipart streams * Static assets * SSL support with OpenSSL or `native-tls` -* Middlewares ([Logger](https://actix.rs/book/actix-web/sec-9-middlewares.html#logging), - [Session](https://actix.rs/book/actix-web/sec-9-middlewares.html#user-sessions), - [Redis sessions](https://github.com/actix/actix-redis), - [DefaultHeaders](https://actix.rs/book/actix-web/sec-9-middlewares.html#default-headers), - [CORS](https://actix.rs/actix-web/actix_web/middleware/cors/index.html), - [CSRF](https://actix.rs/actix-web/actix_web/middleware/csrf/index.html)) +* Middlewares ([Logger,Session,CORS,CSRF,etc](https://actix.rs/docs/middleware/)) * Includes an asynchronous [HTTP client](https://actix.rs/actix-web/actix_web/client/index.html) * Built on top of [Actix actor framework](https://github.com/actix/actix) From 0099091e9664bc6d6e803ff5c1e1e236ce234a5e Mon Sep 17 00:00:00 2001 From: Akos Vandra Date: Mon, 23 Jul 2018 15:07:54 +0200 Subject: [PATCH 3/7] remove unnecessary use --- src/application.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/application.rs b/src/application.rs index 72ecff3da..f36adf69e 100644 --- a/src/application.rs +++ b/src/application.rs @@ -610,7 +610,6 @@ impl Iterator for App { mod tests { use super::*; use body::{Binary, Body}; - use fs; use http::StatusCode; use httprequest::HttpRequest; use httpresponse::HttpResponse; From f4bb7efa89d4f21dfc6c7217fcb280f2b4d322d4 Mon Sep 17 00:00:00 2001 From: Akos Vandra Date: Mon, 23 Jul 2018 15:10:30 +0200 Subject: [PATCH 4/7] add partialeq, eq, partialord and ord dervie to Path, Form and Query --- src/extractor.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/extractor.rs b/src/extractor.rs index 8e4745f86..7696e9920 100644 --- a/src/extractor.rs +++ b/src/extractor.rs @@ -16,7 +16,10 @@ use error::{Error, ErrorBadRequest, ErrorNotFound, UrlencodedError}; use handler::{AsyncResult, FromRequest}; use httpmessage::{HttpMessage, MessageBody, UrlEncoded}; use httprequest::HttpRequest; +use Result; +use futures::future; +#[derive(PartialEq, Eq, PartialOrd, Ord)] /// Extract typed information from the request's path. /// /// ## Example @@ -128,6 +131,7 @@ impl fmt::Display for Path { } } +#[derive(PartialEq, Eq, PartialOrd, Ord)] /// Extract typed information from from the request's query. /// /// ## Example @@ -215,6 +219,7 @@ impl fmt::Display for Query { } } +#[derive(PartialEq, Eq, PartialOrd, Ord)] /// Extract typed information from the request's body. /// /// To extract typed information from request's body, the type `T` must From 1079c5c56202449a449b23ef39880b3816b2a385 Mon Sep 17 00:00:00 2001 From: Akos Vandra Date: Mon, 23 Jul 2018 15:19:04 +0200 Subject: [PATCH 5/7] Add FromRequest implementation for Result and Option where T:FromRequest --- src/extractor.rs | 218 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 218 insertions(+) diff --git a/src/extractor.rs b/src/extractor.rs index 7696e9920..458b7f1a7 100644 --- a/src/extractor.rs +++ b/src/extractor.rs @@ -460,6 +460,126 @@ impl FromRequest for String { } } +/// Optionally extract a field from the request +/// +/// If the FromRequest for T fails, return None rather than returning an error response +/// +/// ## Example +/// +/// ```rust +/// # extern crate actix_web; +/// extern crate rand; +/// #[macro_use] extern crate serde_derive; +/// use actix_web::{http, App, Result, HttpRequest, Error, FromRequest}; +/// use actix_web::error::ErrorBadRequest; +/// +/// #[derive(Debug, Deserialize)] +/// struct Thing { name: String } +/// +/// impl FromRequest for Thing { +/// type Config = (); +/// type Result = Result; +/// +/// #[inline] +/// fn from_request(req: &HttpRequest, _cfg: &Self::Config) -> Self::Result { +/// if rand::random() { +/// Ok(Thing { name: "thingy".into() }) +/// } else { +/// Err(ErrorBadRequest("no luck")) +/// } +/// +/// } +/// } +/// +/// /// extract text data from request +/// fn index(supplied_thing: Option) -> Result { +/// match supplied_thing { +/// // Puns not intended +/// Some(thing) => Ok(format!("Got something: {:?}", thing)), +/// None => Ok(format!("No thing!")) +/// } +/// } +/// +/// fn main() { +/// let app = App::new().resource("/users/:first", |r| { +/// r.method(http::Method::POST).with(index) +/// }); +/// } +/// ``` +impl FromRequest for Option where T: FromRequest { + type Config = T::Config; + type Result = Box, Error = Error>>; + + #[inline] + fn from_request(req: &HttpRequest, cfg: &Self::Config) -> Self::Result { + Box::new(T::from_request(req, cfg).into().then( |r| { + match r { + Ok(v) => future::ok(Some(v)), + Err(e) => { +// if true { panic!("{:?}", e.as_response_error()); } + + future::ok(None) + } + } + })) + } +} + +/// Optionally extract a field from the request or extract the Error if unsuccessful +/// +/// If the FromRequest for T fails, inject Err into handler rather than returning an error response +/// +/// ## Example +/// +/// ```rust +/// # extern crate actix_web; +/// extern crate rand; +/// #[macro_use] extern crate serde_derive; +/// use actix_web::{http, App, Result, HttpRequest, Error, FromRequest}; +/// use actix_web::error::ErrorBadRequest; +/// +/// #[derive(Debug, Deserialize)] +/// struct Thing { name: String } +/// +/// impl FromRequest for Thing { +/// type Config = (); +/// type Result = Result; +/// +/// #[inline] +/// fn from_request(req: &HttpRequest, _cfg: &Self::Config) -> Self::Result { +/// if rand::random() { +/// Ok(Thing { name: "thingy".into() }) +/// } else { +/// Err(ErrorBadRequest("no luck")) +/// } +/// +/// } +/// } +/// +/// /// extract text data from request +/// fn index(supplied_thing: Result) -> Result { +/// match supplied_thing { +/// Ok(thing) => Ok(format!("Got thing: {:?}", thing)), +/// Err(e) => Ok(format!("Error extracting thing: {}", e)) +/// } +/// } +/// +/// fn main() { +/// let app = App::new().resource("/users/:first", |r| { +/// r.method(http::Method::POST).with(index) +/// }); +/// } +/// ``` +impl FromRequest for Result where T: FromRequest{ + type Config = T::Config; + type Result = Box, Error = Error>>; + + #[inline] + fn from_request(req: &HttpRequest, cfg: &Self::Config) -> Self::Result { + Box::new(T::from_request(req, cfg).into().then( |r| { future::ok(r) })) + } +} + /// Payload configuration for request's payload. pub struct PayloadConfig { limit: usize, @@ -685,6 +805,75 @@ mod tests { } } + #[test] + fn test_option() { + let req = TestRequest::with_header( + header::CONTENT_TYPE, + "application/x-www-form-urlencoded", + ).finish(); + + let mut cfg = FormConfig::default(); + cfg.limit(4096); + + match Option::>::from_request(&req, &cfg).poll().unwrap() { + Async::Ready(r) => assert_eq!(r, None), + _ => unreachable!(), + } + + let req = TestRequest::with_header( + header::CONTENT_TYPE, + "application/x-www-form-urlencoded", + ).header(header::CONTENT_LENGTH, "9") + .set_payload(Bytes::from_static(b"hello=world")) + .finish(); + + match Option::>::from_request(&req, &cfg).poll().unwrap() { + Async::Ready(r) => assert_eq!(r, Some(Form(Info { hello: "world".into() }))), + _ => unreachable!(), + } + + let req = TestRequest::with_header( + header::CONTENT_TYPE, + "application/x-www-form-urlencoded", + ).header(header::CONTENT_LENGTH, "9") + .set_payload(Bytes::from_static(b"bye=world")) + .finish(); + + match Option::>::from_request(&req, &cfg).poll().unwrap() { + Async::Ready(r) => assert_eq!(r, None), + _ => unreachable!(), + } + } + + #[test] + fn test_result() { + let req = TestRequest::with_header( + header::CONTENT_TYPE, + "application/x-www-form-urlencoded", + ).header(header::CONTENT_LENGTH, "11") + .set_payload(Bytes::from_static(b"hello=world")) + .finish(); + + match Result::>::from_request(&req, &FormConfig::default()).poll().unwrap() { + Async::Ready(Ok(r)) => assert_eq!(r, Form(Info { hello: "world".into() })), + _ => unreachable!(), + } + + let req = TestRequest::with_header( + header::CONTENT_TYPE, + "application/x-www-form-urlencoded", + ).header(header::CONTENT_LENGTH, "9") + .set_payload(Bytes::from_static(b"bye=world")) + .finish(); + + match Result::>::from_request(&req, &FormConfig::default()).poll().unwrap() { + Async::Ready(r) => assert!(r.is_err()), + _ => unreachable!(), + } + } + + + #[test] fn test_payload_config() { let req = TestRequest::default().finish(); @@ -797,4 +986,33 @@ mod tests { assert_eq!((res.1).0, "name"); assert_eq!((res.1).1, "user1"); } + +// #[test] +// fn test_tuple_optional() { +// let mut router = Router::<()>::new(); +// router.register_resource(Resource::new(ResourceDef::new("/{key}/{value}/"))); +// +// let req = TestRequest::with_uri("/name/?id=test").finish(); +// let info = router.recognize(&req, &(), 0); +// let req = req.with_route_info(info); +// +// let res = match <(Path<(Option, Option)>,)>::extract(&req).wait() { +// Ok(res) => res, +// e => panic!("error {:?}", e), +// }; +// assert_eq!((res.0).0, Some("name".into())); +// assert_eq!((res.0).1, None); +// +// let req = TestRequest::with_uri("/user/?id=test").finish(); +// let info = router.recognize(&req, &(), 0); +// let req = req.with_route_info(info); +// +// let res = match <(Path<(Option, Option)>,)>::extract(&req).wait() { +// Ok(res) => res, +// _ => panic!("error"), +// }; +// assert_eq!((res.0).0, None); +// assert_eq!((res.0).1, Some("user".into())); +// } + } From 35b754a3ab816637013436016bc801ac490c38f7 Mon Sep 17 00:00:00 2001 From: Akos Vandra Date: Tue, 24 Jul 2018 09:39:27 +0200 Subject: [PATCH 6/7] pr fixes --- src/extractor.rs | 43 +++++-------------------------------------- 1 file changed, 5 insertions(+), 38 deletions(-) diff --git a/src/extractor.rs b/src/extractor.rs index 458b7f1a7..768edfb76 100644 --- a/src/extractor.rs +++ b/src/extractor.rs @@ -6,7 +6,7 @@ use std::{fmt, str}; use bytes::Bytes; use encoding::all::UTF_8; use encoding::types::{DecoderTrap, Encoding}; -use futures::{Async, Future, Poll}; +use futures::{Async, Future, Poll, future}; use mime::Mime; use serde::de::{self, DeserializeOwned}; use serde_urlencoded; @@ -16,8 +16,6 @@ use error::{Error, ErrorBadRequest, ErrorNotFound, UrlencodedError}; use handler::{AsyncResult, FromRequest}; use httpmessage::{HttpMessage, MessageBody, UrlEncoded}; use httprequest::HttpRequest; -use Result; -use futures::future; #[derive(PartialEq, Eq, PartialOrd, Ord)] /// Extract typed information from the request's path. @@ -516,8 +514,6 @@ impl FromRequest for Option where T: FromRequest future::ok(Some(v)), Err(e) => { -// if true { panic!("{:?}", e.as_response_error()); } - future::ok(None) } } @@ -570,9 +566,9 @@ impl FromRequest for Option where T: FromRequest FromRequest for Result where T: FromRequest{ +impl FromRequest for Result where T: FromRequest{ type Config = T::Config; - type Result = Box, Error = Error>>; + type Result = Box, Error = Error>>; #[inline] fn from_request(req: &HttpRequest, cfg: &Self::Config) -> Self::Result { @@ -854,7 +850,7 @@ mod tests { .set_payload(Bytes::from_static(b"hello=world")) .finish(); - match Result::>::from_request(&req, &FormConfig::default()).poll().unwrap() { + match Result::, Error>::from_request(&req, &FormConfig::default()).poll().unwrap() { Async::Ready(Ok(r)) => assert_eq!(r, Form(Info { hello: "world".into() })), _ => unreachable!(), } @@ -866,7 +862,7 @@ mod tests { .set_payload(Bytes::from_static(b"bye=world")) .finish(); - match Result::>::from_request(&req, &FormConfig::default()).poll().unwrap() { + match Result::, Error>::from_request(&req, &FormConfig::default()).poll().unwrap() { Async::Ready(r) => assert!(r.is_err()), _ => unreachable!(), } @@ -986,33 +982,4 @@ mod tests { assert_eq!((res.1).0, "name"); assert_eq!((res.1).1, "user1"); } - -// #[test] -// fn test_tuple_optional() { -// let mut router = Router::<()>::new(); -// router.register_resource(Resource::new(ResourceDef::new("/{key}/{value}/"))); -// -// let req = TestRequest::with_uri("/name/?id=test").finish(); -// let info = router.recognize(&req, &(), 0); -// let req = req.with_route_info(info); -// -// let res = match <(Path<(Option, Option)>,)>::extract(&req).wait() { -// Ok(res) => res, -// e => panic!("error {:?}", e), -// }; -// assert_eq!((res.0).0, Some("name".into())); -// assert_eq!((res.0).1, None); -// -// let req = TestRequest::with_uri("/user/?id=test").finish(); -// let info = router.recognize(&req, &(), 0); -// let req = req.with_route_info(info); -// -// let res = match <(Path<(Option, Option)>,)>::extract(&req).wait() { -// Ok(res) => res, -// _ => panic!("error"), -// }; -// assert_eq!((res.0).0, None); -// assert_eq!((res.0).1, Some("user".into())); -// } - } From b48a2d4d7b36410297d3c3b3a6e3dffb665715d1 Mon Sep 17 00:00:00 2001 From: Akos Vandra Date: Tue, 24 Jul 2018 22:25:48 +0200 Subject: [PATCH 7/7] add changes to CHANGES.md --- CHANGES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 04c004fa7..15786fb69 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,6 +2,10 @@ ## [0.7.1] - 2018-07-21 +### Added + + * Add implementation of `FromRequest` for `Option` and `Result` + ### Fixed * Fixed default_resource 'not yet implemented' panic #410