1
0
mirror of https://github.com/fafhrd91/actix-web synced 2025-01-18 13:51:50 +01:00

add cookies support for response

This commit is contained in:
Nikolay Kim 2017-10-14 10:40:58 -07:00
parent 5c9f813d28
commit f0531793b4
3 changed files with 71 additions and 7 deletions

View File

@ -2,12 +2,13 @@
use std::{io, mem, str}; use std::{io, mem, str};
use std::convert::Into; use std::convert::Into;
use cookie; use cookie::CookieJar;
use bytes::Bytes; use bytes::Bytes;
use http::{Method, StatusCode, Version, Uri, HeaderMap, HttpTryFrom, Error}; use http::{Method, StatusCode, Version, Uri, HeaderMap, HttpTryFrom, Error};
use http::header::{self, HeaderName, HeaderValue}; use http::header::{self, HeaderName, HeaderValue};
use Params; use Params;
use {Cookie, CookieParseError};
#[derive(Copy, Clone, PartialEq, Debug)] #[derive(Copy, Clone, PartialEq, Debug)]
pub enum ConnectionType { pub enum ConnectionType {
@ -24,7 +25,7 @@ pub struct HttpRequest {
uri: Uri, uri: Uri,
headers: HeaderMap, headers: HeaderMap,
params: Params, params: Params,
cookies: Vec<cookie::Cookie<'static>>, cookies: Vec<Cookie<'static>>,
} }
impl HttpRequest { impl HttpRequest {
@ -81,18 +82,18 @@ impl HttpRequest {
} }
/// Return request cookies. /// Return request cookies.
pub fn cookies(&mut self) -> &Vec<cookie::Cookie<'static>> { pub fn cookies(&mut self) -> &Vec<Cookie<'static>> {
&self.cookies &self.cookies
} }
/// Load cookies /// Load cookies
pub fn load_cookies(&mut self) -> Result<&Vec<cookie::Cookie>, cookie::ParseError> pub fn load_cookies(&mut self) -> Result<&Vec<Cookie>, CookieParseError>
{ {
if let Some(val) = self.headers.get(header::COOKIE) { if let Some(val) = self.headers.get(header::COOKIE) {
let s = str::from_utf8(val.as_bytes()) let s = str::from_utf8(val.as_bytes())
.map_err(cookie::ParseError::from)?; .map_err(CookieParseError::from)?;
for cookie in s.split("; ") { 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) Ok(&self.cookies)
@ -344,6 +345,7 @@ struct Parts {
reason: Option<&'static str>, reason: Option<&'static str>,
chunked: bool, chunked: bool,
connection_type: Option<ConnectionType>, connection_type: Option<ConnectionType>,
cookies: CookieJar,
} }
impl Parts { impl Parts {
@ -355,6 +357,7 @@ impl Parts {
reason: None, reason: None,
chunked: false, chunked: false,
connection_type: None, connection_type: None,
cookies: CookieJar::new(),
} }
} }
} }
@ -470,12 +473,35 @@ impl Builder {
self 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 /// Set a body
pub fn body<B: Into<Body>>(&mut self, body: B) -> Result<HttpResponse, Error> { pub fn body<B: Into<Body>>(&mut self, body: B) -> Result<HttpResponse, Error> {
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() { if let Some(e) = self.err.take() {
return Err(e) return Err(e)
} }
for cookie in parts.cookies.delta() {
parts.headers.append(
header::SET_COOKIE,
HeaderValue::from_str(&cookie.to_string())?);
}
Ok(HttpResponse { Ok(HttpResponse {
version: parts.version, version: parts.version,
headers: parts.headers, headers: parts.headers,

View File

@ -49,4 +49,8 @@ pub use resource::{Reply, Resource};
pub use route::{Route, RouteFactory, RouteHandler}; pub use route::{Route, RouteFactory, RouteHandler};
pub use server::HttpServer; pub use server::HttpServer;
pub use context::HttpContext; pub use context::HttpContext;
// re-exports
pub use cookie::{Cookie, CookieBuilder};
pub use cookie::{ParseError as CookieParseError};
pub use route_recognizer::Params; pub use route_recognizer::Params;

View File

@ -1,7 +1,9 @@
extern crate actix_web; extern crate actix_web;
extern crate http; extern crate http;
extern crate time;
use actix_web::*; use actix_web::*;
use time::Duration;
use http::{header, Method, Uri, Version, HeaderMap, HttpTryFrom}; 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].name(), "cookie2");
assert_eq!(cookies[1].value(), "value2"); 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");
}