diff --git a/lib/Cargo.toml b/lib/Cargo.toml index ee152d3d..a635ae2f 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -38,7 +38,7 @@ isatty = "0.1" [dependencies.cookie] git = "https://github.com/alexcrichton/cookie-rs" -rev = "b8298e" +rev = "f4e6930" features = ["percent-encode", "secure"] [dev-dependencies] diff --git a/lib/src/http/cookies.rs b/lib/src/http/cookies.rs index 33211d5f..2ed9bfcc 100644 --- a/lib/src/http/cookies.rs +++ b/lib/src/http/cookies.rs @@ -231,26 +231,46 @@ impl<'a> Cookies<'a> { /// ``` pub fn add_private(&mut self, mut cookie: Cookie<'static>) { if let Cookies::Jarred(ref mut jar, key) = *self { - if cookie.path().is_none() { - cookie.set_path("/"); - } - - if cookie.http_only().is_none() { - cookie.set_http_only(true); - } - - if cookie.expires().is_none() { - cookie.set_expires(::time::now() + ::time::Duration::weeks(1)); - } - - if cookie.same_site().is_none() { - cookie.set_same_site(SameSite::Strict); - } - + Cookies::set_private_defaults(&mut cookie); jar.private(key).add(cookie) } } + /// Adds an original, private `cookie` to the collection. + pub(crate) fn add_original_private(&mut self, mut cookie: Cookie<'static>) { + if let Cookies::Jarred(ref mut jar, key) = *self { + Cookies::set_private_defaults(&mut cookie); + jar.private(key).add_original(cookie) + } + } + + /// For each property mentioned below, this method checks + /// if there is provided value and if there is none, set default value. + /// Default values are: + /// + /// * `path`: `"/"` + /// * `SameSite`: `Strict` + /// * `HttpOnly`: `true` + /// * `Expires`: 1 week from now + /// + fn set_private_defaults(cookie: &mut Cookie<'static>) { + if cookie.path().is_none() { + cookie.set_path("/"); + } + + if cookie.http_only().is_none() { + cookie.set_http_only(true); + } + + if cookie.expires().is_none() { + cookie.set_expires(::time::now() + ::time::Duration::weeks(1)); + } + + if cookie.same_site().is_none() { + cookie.set_same_site(SameSite::Strict); + } + } + /// Removes `cookie` from this collection and generates a "removal" cookies /// to send to the client on response. For correctness, `cookie` must /// contain the same `path` and `domain` as the cookie that was initially diff --git a/lib/src/local/request.rs b/lib/src/local/request.rs index 66695f3d..1a6c7c03 100644 --- a/lib/src/local/request.rs +++ b/lib/src/local/request.rs @@ -189,6 +189,28 @@ impl<'c> LocalRequest<'c> { self } + /// Add a [private cookie] to this request. + /// + /// [private cookie]: /rocket/http/enum.Cookies.html#private-cookies + /// + /// # Examples + /// + /// Add `user_id` as a private cookie: + /// + /// ```rust + /// use rocket::local::Client; + /// use rocket::http::Cookie; + /// + /// let client = Client::new(rocket::ignite()).unwrap(); + /// # #[allow(unused_variables)] + /// let req = client.get("/").private_cookie(Cookie::new("user_id", "sb")); + /// ``` + #[inline] + pub fn private_cookie(self, cookie: Cookie<'static>) -> Self { + self.request.cookies().add_original_private(cookie); + self + } + // TODO: For CGI, we want to be able to set the body to be stdin without // actually reading everything into a vector. Can we allow that here while // keeping the simplicity? Looks like it would require us to reintroduce a diff --git a/lib/tests/local_request_private_cookie-issue-368.rs b/lib/tests/local_request_private_cookie-issue-368.rs new file mode 100644 index 00000000..4bdd8041 --- /dev/null +++ b/lib/tests/local_request_private_cookie-issue-368.rs @@ -0,0 +1,44 @@ +#![feature(plugin, decl_macro)] +#![plugin(rocket_codegen)] + +extern crate rocket; + +use rocket::http::Cookies; + +#[get("/")] +fn return_private_cookie(mut cookies: Cookies) -> Option { + match cookies.get_private("cookie_name") { + Some(cookie) => Some(cookie.value().into()), + None => None, + } +} + +mod tests { + use super::*; + use rocket::local::Client; + use rocket::http::Cookie; + use rocket::http::Status; + + #[test] + fn private_cookie_is_returned() { + let rocket = rocket::ignite().mount("/", routes![return_private_cookie]); + + let client = Client::new(rocket).unwrap(); + let req = client.get("/").private_cookie(Cookie::new("cookie_name", "cookie_value")); + let mut response = req.dispatch(); + + assert_eq!(response.body_string(), Some("cookie_value".into())); + assert_eq!(response.headers().get_one("Set-Cookie"), None); + } + + #[test] + fn regular_cookie_is_not_returned() { + let rocket = rocket::ignite().mount("/", routes![return_private_cookie]); + + let client = Client::new(rocket).unwrap(); + let req = client.get("/").cookie(Cookie::new("cookie_name", "cookie_value")); + let response = req.dispatch(); + + assert_eq!(response.status(), Status::NotFound); + } +} \ No newline at end of file