diff --git a/src/httpmessage.rs b/src/httpmessage.rs index 1fa01a141..6b59413ad 100644 --- a/src/httpmessage.rs +++ b/src/httpmessage.rs @@ -2,12 +2,13 @@ use std::{io, mem, str}; use std::convert::Into; -use cookie; +use cookie::CookieJar; use bytes::Bytes; use http::{Method, StatusCode, Version, Uri, HeaderMap, HttpTryFrom, Error}; use http::header::{self, HeaderName, HeaderValue}; use Params; +use {Cookie, CookieParseError}; #[derive(Copy, Clone, PartialEq, Debug)] pub enum ConnectionType { @@ -24,7 +25,7 @@ pub struct HttpRequest { uri: Uri, headers: HeaderMap, params: Params, - cookies: Vec>, + cookies: Vec>, } impl HttpRequest { @@ -81,18 +82,18 @@ impl HttpRequest { } /// Return request cookies. - pub fn cookies(&mut self) -> &Vec> { + pub fn cookies(&mut self) -> &Vec> { &self.cookies } /// Load cookies - pub fn load_cookies(&mut self) -> Result<&Vec, cookie::ParseError> + pub fn load_cookies(&mut self) -> Result<&Vec, CookieParseError> { if let Some(val) = self.headers.get(header::COOKIE) { let s = str::from_utf8(val.as_bytes()) - .map_err(cookie::ParseError::from)?; + .map_err(CookieParseError::from)?; for cookie in s.split("; ") { - self.cookies.push(cookie::Cookie::parse_encoded(cookie)?.into_owned()); + self.cookies.push(Cookie::parse_encoded(cookie)?.into_owned()); } } Ok(&self.cookies) @@ -344,6 +345,7 @@ struct Parts { reason: Option<&'static str>, chunked: bool, connection_type: Option, + cookies: CookieJar, } impl Parts { @@ -355,6 +357,7 @@ impl Parts { reason: None, chunked: false, connection_type: None, + cookies: CookieJar::new(), } } } @@ -470,12 +473,35 @@ impl Builder { self }*/ + /// Set a cookie + pub fn cookie<'c>(&mut self, cookie: Cookie<'c>) -> &mut Self { + if let Some(parts) = parts(&mut self.parts, &self.err) { + parts.cookies.add(cookie.into_owned()); + } + self + } + + /// Remote cookie, cookie has to be cookie from `HttpRequest::cookies()` method. + pub fn del_cookie<'a>(&mut self, cookie: &Cookie<'a>) -> &mut Self { + if let Some(parts) = parts(&mut self.parts, &self.err) { + let cookie = cookie.clone().into_owned(); + parts.cookies.add_original(cookie.clone()); + parts.cookies.remove(cookie); + } + self + } + /// Set a body pub fn body>(&mut self, body: B) -> Result { - let parts = self.parts.take().expect("cannot reuse response builder"); + let mut parts = self.parts.take().expect("cannot reuse response builder"); if let Some(e) = self.err.take() { return Err(e) } + for cookie in parts.cookies.delta() { + parts.headers.append( + header::SET_COOKIE, + HeaderValue::from_str(&cookie.to_string())?); + } Ok(HttpResponse { version: parts.version, headers: parts.headers, diff --git a/src/lib.rs b/src/lib.rs index 956346c87..c2f7d69fd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -49,4 +49,8 @@ pub use resource::{Reply, Resource}; pub use route::{Route, RouteFactory, RouteHandler}; pub use server::HttpServer; pub use context::HttpContext; + +// re-exports +pub use cookie::{Cookie, CookieBuilder}; +pub use cookie::{ParseError as CookieParseError}; pub use route_recognizer::Params; diff --git a/tests/test_httpmessage.rs b/tests/test_httpmessage.rs index be220488e..0ed07013c 100644 --- a/tests/test_httpmessage.rs +++ b/tests/test_httpmessage.rs @@ -1,7 +1,9 @@ extern crate actix_web; extern crate http; +extern crate time; use actix_web::*; +use time::Duration; use http::{header, Method, Uri, Version, HeaderMap, HttpTryFrom}; @@ -30,3 +32,35 @@ fn test_request_cookies() { assert_eq!(cookies[1].name(), "cookie2"); assert_eq!(cookies[1].value(), "value2"); } + +#[test] +fn test_response_cookies() { + let mut headers = HeaderMap::new(); + headers.insert(header::COOKIE, + header::HeaderValue::from_static("cookie1=value1; cookie2=value2")); + + let mut req = HttpRequest::new( + Method::GET, Uri::try_from("/").unwrap(), Version::HTTP_11, headers); + let cookies = req.load_cookies().unwrap(); + + let resp = httpcodes::HTTPOk + .builder() + .cookie(Cookie::build("name", "value") + .domain("www.rust-lang.org") + .path("/test") + .http_only(true) + .max_age(Duration::days(1)) + .finish()) + .del_cookie(&cookies[0]) + .body(Body::Empty); + + assert!(resp.is_ok()); + let resp = resp.unwrap(); + + let mut val: Vec<_> = resp.headers().get_all("Set-Cookie") + .iter().map(|v| v.to_str().unwrap().to_owned()).collect(); + val.sort(); + assert!(val[0].starts_with("cookie1=; Max-Age=0;")); + assert_eq!( + val[1],"name=value; HttpOnly; Path=/test; Domain=www.rust-lang.org; Max-Age=86400"); +}