mirror of https://github.com/fafhrd91/actix-web
feat(web): add `cookie_raw` API (#4013)
This commit is contained in:
parent
ad7cbef1f9
commit
3556ae0b4f
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
|
- Add `HttpRequest::{cookies_raw,cookie_raw}` and `ServiceRequest::{cookies_raw,cookie_raw}` for reading request cookies without percent-decoding names and values. [#3542]
|
||||||
- Enable dual-stack IPv6 sockets on Windows when possible so that Actix-created listeners bound to `[::]` also accept IPv4 connections.
|
- Enable dual-stack IPv6 sockets on Windows when possible so that Actix-created listeners bound to `[::]` also accept IPv4 connections.
|
||||||
- Panic when calling `Route::to()` or `Route::service()` after `Route::wrap()` to prevent silently dropping route middleware. [#3944]
|
- Panic when calling `Route::to()` or `Route::service()` after `Route::wrap()` to prevent silently dropping route middleware. [#3944]
|
||||||
- Fix `HttpRequest::{match_pattern,match_name}` reporting path-only matches when route guards disambiguate overlapping resources. [#3346]
|
- Fix `HttpRequest::{match_pattern,match_name}` reporting path-only matches when route guards disambiguate overlapping resources. [#3346]
|
||||||
|
|
@ -9,6 +10,7 @@
|
||||||
|
|
||||||
[#3944]: https://github.com/actix/actix-web/pull/3944
|
[#3944]: https://github.com/actix/actix-web/pull/3944
|
||||||
[#3346]: https://github.com/actix/actix-web/issues/3346
|
[#3346]: https://github.com/actix/actix-web/issues/3346
|
||||||
|
[#3542]: https://github.com/actix/actix-web/issues/3542
|
||||||
|
|
||||||
## 4.13.0
|
## 4.13.0
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,9 @@ use crate::{
|
||||||
#[cfg(feature = "cookies")]
|
#[cfg(feature = "cookies")]
|
||||||
struct Cookies(Vec<Cookie<'static>>);
|
struct Cookies(Vec<Cookie<'static>>);
|
||||||
|
|
||||||
|
#[cfg(feature = "cookies")]
|
||||||
|
struct RawCookies(Vec<Cookie<'static>>);
|
||||||
|
|
||||||
/// An incoming request.
|
/// An incoming request.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct HttpRequest {
|
pub struct HttpRequest {
|
||||||
|
|
@ -457,6 +460,8 @@ impl HttpRequest {
|
||||||
|
|
||||||
/// Load request cookies.
|
/// Load request cookies.
|
||||||
///
|
///
|
||||||
|
/// The names and values of cookies are percent-decoded.
|
||||||
|
///
|
||||||
/// Any cookie that cannot be parsed is omitted from the result.
|
/// Any cookie that cannot be parsed is omitted from the result.
|
||||||
/// This includes cookies with an empty name (e.g. `document.cookie = "=value"`).
|
/// This includes cookies with an empty name (e.g. `document.cookie = "=value"`).
|
||||||
#[cfg(feature = "cookies")]
|
#[cfg(feature = "cookies")]
|
||||||
|
|
@ -481,16 +486,49 @@ impl HttpRequest {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Load request cookies **without** percent-decoding their names and values.
|
||||||
|
///
|
||||||
|
/// Any cookie that cannot be parsed is omitted from the result.
|
||||||
|
/// This includes cookies with an empty name (e.g. `document.cookie = "=value"`).
|
||||||
|
#[cfg(feature = "cookies")]
|
||||||
|
pub fn cookies_raw(&self) -> Result<Ref<'_, Vec<Cookie<'static>>>, CookieParseError> {
|
||||||
|
use actix_http::header::COOKIE;
|
||||||
|
|
||||||
|
if self.extensions().get::<RawCookies>().is_none() {
|
||||||
|
let mut cookies = Vec::new();
|
||||||
|
for hdr in self.headers().get_all(COOKIE) {
|
||||||
|
let s = str::from_utf8(hdr.as_bytes()).map_err(CookieParseError::from)?;
|
||||||
|
for cookie_str in s.split(';').map(|s| s.trim()).filter(|s| !s.is_empty()) {
|
||||||
|
if let Ok(cookie) = Cookie::parse(cookie_str) {
|
||||||
|
cookies.push(cookie.into_owned());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.extensions_mut().insert(RawCookies(cookies));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Ref::map(self.extensions(), |ext| {
|
||||||
|
&ext.get::<RawCookies>().unwrap().0
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
/// Return request cookie.
|
/// Return request cookie.
|
||||||
#[cfg(feature = "cookies")]
|
#[cfg(feature = "cookies")]
|
||||||
pub fn cookie(&self, name: &str) -> Option<Cookie<'static>> {
|
pub fn cookie(&self, name: &str) -> Option<Cookie<'static>> {
|
||||||
if let Ok(cookies) = self.cookies() {
|
if let Ok(cookies) = self.cookies() {
|
||||||
for cookie in cookies.iter() {
|
return cookies.iter().find(|cookie| cookie.name() == name).cloned();
|
||||||
if cookie.name() == name {
|
|
||||||
return Some(cookie.to_owned());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return request cookie **without** percent-decoding its name and value.
|
||||||
|
#[cfg(feature = "cookies")]
|
||||||
|
pub fn cookie_raw(&self, name: &str) -> Option<Cookie<'static>> {
|
||||||
|
if let Ok(cookies) = self.cookies_raw() {
|
||||||
|
return cookies.iter().find(|cookie| cookie.name() == name).cloned();
|
||||||
|
}
|
||||||
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -723,6 +761,49 @@ mod tests {
|
||||||
assert!(cookie.is_none());
|
assert!(cookie.is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(feature = "cookies")]
|
||||||
|
fn test_request_cookies_raw() {
|
||||||
|
let req = TestRequest::default()
|
||||||
|
.append_header((header::COOKIE, "cookie1=hello%20world"))
|
||||||
|
.append_header((header::COOKIE, "cookie2=%db"))
|
||||||
|
.to_http_request();
|
||||||
|
{
|
||||||
|
let cookies = req.cookies_raw().unwrap();
|
||||||
|
assert_eq!(cookies.len(), 2);
|
||||||
|
assert_eq!(cookies[0].name(), "cookie1");
|
||||||
|
assert_eq!(cookies[0].value(), "hello%20world");
|
||||||
|
assert_eq!(cookies[1].name(), "cookie2");
|
||||||
|
assert_eq!(cookies[1].value(), "%db");
|
||||||
|
}
|
||||||
|
|
||||||
|
let cookie = req.cookie_raw("cookie1");
|
||||||
|
assert!(cookie.is_some());
|
||||||
|
let cookie = cookie.unwrap();
|
||||||
|
assert_eq!(cookie.name(), "cookie1");
|
||||||
|
assert_eq!(cookie.value(), "hello%20world");
|
||||||
|
|
||||||
|
let cookie = req.cookie_raw("cookie2");
|
||||||
|
assert!(cookie.is_some());
|
||||||
|
let cookie = cookie.unwrap();
|
||||||
|
assert_eq!(cookie.name(), "cookie2");
|
||||||
|
assert_eq!(cookie.value(), "%db");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(feature = "cookies")]
|
||||||
|
fn test_request_cookies_raw_is_independent_from_encoded_cookies() {
|
||||||
|
let req = TestRequest::default()
|
||||||
|
.append_header((header::COOKIE, "cookie=%20"))
|
||||||
|
.to_http_request();
|
||||||
|
|
||||||
|
let cookie = req.cookie("cookie").unwrap();
|
||||||
|
assert_eq!(cookie.value(), " ");
|
||||||
|
|
||||||
|
let raw_cookie = req.cookie_raw("cookie").unwrap();
|
||||||
|
assert_eq!(raw_cookie.value(), "%20");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(feature = "cookies")]
|
#[cfg(feature = "cookies")]
|
||||||
fn test_empty_key() {
|
fn test_empty_key() {
|
||||||
|
|
|
||||||
|
|
@ -297,6 +297,13 @@ impl ServiceRequest {
|
||||||
self.req.cookies()
|
self.req.cookies()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return request cookies **without** percent-decoding their names and values.
|
||||||
|
#[cfg(feature = "cookies")]
|
||||||
|
#[inline]
|
||||||
|
pub fn cookies_raw(&self) -> Result<Ref<'_, Vec<Cookie<'static>>>, CookieParseError> {
|
||||||
|
self.req.cookies_raw()
|
||||||
|
}
|
||||||
|
|
||||||
/// Return request cookie.
|
/// Return request cookie.
|
||||||
#[cfg(feature = "cookies")]
|
#[cfg(feature = "cookies")]
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
@ -304,6 +311,13 @@ impl ServiceRequest {
|
||||||
self.req.cookie(name)
|
self.req.cookie(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return request cookie **without** percent-decoding its name and value.
|
||||||
|
#[cfg(feature = "cookies")]
|
||||||
|
#[inline]
|
||||||
|
pub fn cookie_raw(&self, name: &str) -> Option<Cookie<'static>> {
|
||||||
|
self.req.cookie_raw(name)
|
||||||
|
}
|
||||||
|
|
||||||
/// Set request payload.
|
/// Set request payload.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_payload(&mut self, payload: Payload) {
|
pub fn set_payload(&mut self, payload: Payload) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue