1
0
Fork 0
mirror of https://github.com/actix/actix-web.git synced 2024-06-02 21:39:26 +00:00

fix client cookies parsing

This commit is contained in:
Nikolay Kim 2019-03-26 21:31:18 -07:00
parent ab597dd98a
commit 5703bd8160
3 changed files with 91 additions and 66 deletions

View file

@ -242,7 +242,7 @@ impl ClientRequest {
self.header(header::CONTENT_LENGTH, wrt.get_mut().take().freeze())
}
/// Set HTTP basic authorization
/// Set HTTP basic authorization header
pub fn basic_auth<U, P>(self, username: U, password: Option<P>) -> Self
where
U: fmt::Display,
@ -258,7 +258,7 @@ impl ClientRequest {
)
}
/// Set HTTP bearer authentication
/// Set HTTP bearer authentication header
pub fn bearer_auth<T>(self, token: T) -> Self
where
T: fmt::Display,

View file

@ -5,10 +5,15 @@ use bytes::{Bytes, BytesMut};
use futures::{Future, Poll, Stream};
use actix_http::error::PayloadError;
use actix_http::http::header::CONTENT_LENGTH;
use actix_http::http::header::{CONTENT_LENGTH, SET_COOKIE};
use actix_http::http::{HeaderMap, StatusCode, Version};
use actix_http::{Extensions, Head, HttpMessage, Payload, PayloadStream, ResponseHead};
#[cfg(feature = "cookies")]
use actix_http::error::CookieParseError;
#[cfg(feature = "cookies")]
use cookie::Cookie;
/// Client Response
pub struct ClientResponse<S = PayloadStream> {
pub(crate) head: ResponseHead,
@ -33,6 +38,26 @@ impl<S> HttpMessage for ClientResponse<S> {
fn take_payload(&mut self) -> Payload<S> {
std::mem::replace(&mut self.payload, Payload::None)
}
/// Load request cookies.
#[inline]
#[cfg(feature = "cookies")]
fn cookies(&self) -> Result<Ref<Vec<Cookie<'static>>>, CookieParseError> {
struct Cookies(Vec<Cookie<'static>>);
if self.extensions().get::<Cookies>().is_none() {
let mut cookies = Vec::new();
for hdr in self.headers().get_all(SET_COOKIE) {
let s = std::str::from_utf8(hdr.as_bytes())
.map_err(CookieParseError::from)?;
cookies.push(Cookie::parse_encoded(s)?.into_owned());
}
self.extensions_mut().insert(Cookies(cookies));
}
Ok(Ref::map(self.extensions(), |ext| {
&ext.get::<Cookies>().unwrap().0
}))
}
}
impl<S> ClientResponse<S> {

View file

@ -8,7 +8,7 @@ use rand::Rng;
use actix_http::HttpService;
use actix_http_test::TestServer;
use actix_web::{http::header, web, App, HttpMessage, HttpRequest, HttpResponse};
use actix_web::{http::header, web, App, Error, HttpMessage, HttpRequest, HttpResponse};
const STR: &str = "Hello World Hello World Hello World Hello World Hello World \
Hello World Hello World Hello World Hello World Hello World \
@ -352,69 +352,69 @@ fn test_client_brotli_encoding() {
// assert_eq!(bytes, Bytes::from_static(STR.as_ref()));
// }
// #[test]
// fn test_client_cookie_handling() {
// use actix_web::http::Cookie;
// fn err() -> Error {
// use std::io::{Error as IoError, ErrorKind};
// // stub some generic error
// Error::from(IoError::from(ErrorKind::NotFound))
// }
// let cookie1 = Cookie::build("cookie1", "value1").finish();
// let cookie2 = Cookie::build("cookie2", "value2")
// .domain("www.example.org")
// .path("/")
// .secure(true)
// .http_only(true)
// .finish();
// // Q: are all these clones really necessary? A: Yes, possibly
// let cookie1b = cookie1.clone();
// let cookie2b = cookie2.clone();
// let mut srv = test::TestServer::new(move |app| {
// let cookie1 = cookie1b.clone();
// let cookie2 = cookie2b.clone();
// app.handler(move |req: &HttpRequest| {
// // Check cookies were sent correctly
// req.cookie("cookie1")
// .ok_or_else(err)
// .and_then(|c1| {
// if c1.value() == "value1" {
// Ok(())
// } else {
// Err(err())
// }
// })
// .and_then(|()| req.cookie("cookie2").ok_or_else(err))
// .and_then(|c2| {
// if c2.value() == "value2" {
// Ok(())
// } else {
// Err(err())
// }
// })
// // Send some cookies back
// .map(|_| {
// HttpResponse::Ok()
// .cookie(cookie1.clone())
// .cookie(cookie2.clone())
// .finish()
// })
// })
// });
#[test]
fn test_client_cookie_handling() {
use actix_web::http::Cookie;
fn err() -> Error {
use std::io::{Error as IoError, ErrorKind};
// stub some generic error
Error::from(IoError::from(ErrorKind::NotFound))
}
let cookie1 = Cookie::build("cookie1", "value1").finish();
let cookie2 = Cookie::build("cookie2", "value2")
.domain("www.example.org")
.path("/")
.secure(true)
.http_only(true)
.finish();
// Q: are all these clones really necessary? A: Yes, possibly
let cookie1b = cookie1.clone();
let cookie2b = cookie2.clone();
// let request = srv
// .get()
// .cookie(cookie1.clone())
// .cookie(cookie2.clone())
// .finish()
// .unwrap();
// let response = srv.execute(request.send()).unwrap();
// assert!(response.status().is_success());
// let c1 = response.cookie("cookie1").expect("Missing cookie1");
// assert_eq!(c1, cookie1);
// let c2 = response.cookie("cookie2").expect("Missing cookie2");
// assert_eq!(c2, cookie2);
// }
let mut srv = TestServer::new(move || {
let cookie1 = cookie1b.clone();
let cookie2 = cookie2b.clone();
HttpService::new(App::new().route(
"/",
web::to(move |req: HttpRequest| {
// Check cookies were sent correctly
req.cookie("cookie1")
.ok_or_else(err)
.and_then(|c1| {
if c1.value() == "value1" {
Ok(())
} else {
Err(err())
}
})
.and_then(|()| req.cookie("cookie2").ok_or_else(err))
.and_then(|c2| {
if c2.value() == "value2" {
Ok(())
} else {
Err(err())
}
})
// Send some cookies back
.map(|_| {
HttpResponse::Ok()
.cookie(cookie1.clone())
.cookie(cookie2.clone())
.finish()
})
}),
))
});
let request = srv.get().cookie(cookie1.clone()).cookie(cookie2.clone());
let response = srv.block_on(request.send()).unwrap();
assert!(response.status().is_success());
let c1 = response.cookie("cookie1").expect("Missing cookie1");
assert_eq!(c1, cookie1);
let c2 = response.cookie("cookie2").expect("Missing cookie2");
assert_eq!(c2, cookie2);
}
// #[test]
// fn test_default_headers() {