Skip to content
Snippets Groups Projects
Commit e748f978 authored by codecraft's avatar codecraft :crocodile:
Browse files

FromStr for Cookie, Change Cookie Path to be Uri

- Finish implementing FromStr trait for Cookie
- Change the path variable of the Cookie struct to be a Uri
- Update docs of CookieBuilder to compile
parent 15a864bb
No related branches found
No related tags found
1 merge request!1Initial feature merge
use std::{error::Error, str::FromStr, time::Duration};
use std::{collections::HashMap, str::FromStr, time::Duration};
use crate::{handling::response::CookieBuilder, utils::urlencoded::DeCodable};
use crate::{
handling::response::{cookie_management::error_types::CookieError, CookieBuilder},
utils::{url_utils::Uri, urlencoded::DeCodable},
};
macro_rules! update_map {
($map:expr, $key:expr) => {
*$map.get_mut($key).unwrap() = true;
};
}
use super::error_types::{ParseCookieError, ParseSameSiteError, SameSiteError};
/// Structure representing a Cookie
/// # Creating a Cookie:
......@@ -17,20 +28,20 @@ pub struct Cookie {
pub(crate) cookie_string: Option<String>,
pub(crate) name: String,
pub(crate) value: String,
// expires: Option<Tm>,
pub(crate) max_age: Option<Duration>,
/// The cookie's domain, if any.
pub(crate) domain: Option<String>,
/// The cookie's path domain, if any.
pub(crate) path: Option<String>,
/// Whether this cookie was marked Secure.
pub(crate) secure: bool,
/// Whether this cookie was marked HttpOnly.
pub(crate) http_only: bool,
pub(crate) partitioned: bool,
// expires: Option<Tm>,
pub(crate) max_age: Option<Duration>,
/// The draft `SameSite` attribute.
pub(crate) same_site: Option<SameSite>,
/// The cookie's domain, if any.
pub(crate) domain: Option<String>,
/// The cookie's path domain, if any.
pub(crate) path: Option<Uri>,
pub(crate) expires: Option<String>,
pub(crate) partitioned: bool,
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
......@@ -52,6 +63,21 @@ impl std::fmt::Display for SameSite {
}
}
impl FromStr for SameSite {
type Err = ParseSameSiteError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"None" => Ok(SameSite::None),
"Lax" => Ok(SameSite::Lax),
"Strict" => Ok(SameSite::Strict),
_ => Err(Self::Err {
inner: SameSiteError::NotAValidVariant,
}),
}
}
}
impl std::fmt::Display for Cookie {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut appendix = String::from("");
......@@ -86,31 +112,24 @@ impl std::fmt::Display for Cookie {
}
}
impl Error for ParseCookieError {}
#[derive(Debug)]
pub struct ParseCookieError {
inner: CookieError,
}
#[derive(Debug, PartialEq, PartialOrd, Eq, Ord)]
pub enum CookieError {
MissingEqual,
InvalidAttribute,
}
impl std::fmt::Display for ParseCookieError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "ParseCookieError {{ error: {:?} }}", self.inner)
}
}
impl FromStr for Cookie {
type Err = ParseCookieError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut map = HashMap::with_capacity(8);
map.insert("Partitioned", false);
map.insert("HttpOnly", false);
map.insert("Secure", false);
map.insert("SameSite", false);
map.insert("Max-Age", false);
map.insert("Domain", false);
map.insert("Path", false);
map.insert("Expires", false);
let mut final_result = CookieBuilder::build("", "");
let mut first = true;
for part in s.split(';') {
let stripped = s.strip_prefix("Set-Cookie: ").ok_or(Self::Err {
inner: CookieError::NoSetCookieHeader,
})?;
for part in stripped.split(';') {
let trimmed_part = part.trim();
if first {
let Some(name_val) = part.split_once('=') else {
......@@ -131,20 +150,74 @@ impl FromStr for Cookie {
);
}
first = false;
break;
continue;
}
final_result = match trimmed_part {
"Secure" => final_result.secure(true),
"HttpOnly" => final_result.http_only(true),
"Partitioned" => final_result.partitioned(true),
_ => {
return Err(Self::Err {
inner: CookieError::InvalidAttribute,
});
}
if !map.get("Max-Age").unwrap() && trimmed_part.starts_with("Max-Age=") {
final_result = final_result.max_age(Duration::from_secs(
trimmed_part
.strip_prefix("Max-Age=")
.unwrap()
.parse()
.map_err(|_| Self::Err {
inner: CookieError::InvalidMaxAge,
})?,
));
update_map!(map, "Max-Age");
continue;
}
if !map.get("Expires").unwrap() && trimmed_part.starts_with("Expires=") {
final_result = final_result.expires(trimmed_part.strip_prefix("Expires=").unwrap());
update_map!(map, "Expires");
continue;
}
if !map.get("HttpOnly").unwrap() && trimmed_part == "HttpOnly" {
final_result = final_result.http_only(true);
update_map!(map, "HttpOnly");
continue;
}
if !map.get("SameSite").unwrap() && trimmed_part.starts_with("SameSite=") {
final_result = final_result.same_site(
trimmed_part
.strip_prefix("SameSite=")
.unwrap()
.parse::<SameSite>()
.map_err(|err| Self::Err {
inner: CookieError::InvilidSameSite(err.inner),
})?,
);
update_map!(map, "SameSite");
continue;
}
if !map.get("Path").unwrap() && trimmed_part.starts_with("Path=") {
final_result = final_result.path(
trimmed_part
.strip_prefix("Path=")
.unwrap()
.parse::<Uri>()
.map_err(|err| Self::Err {
inner: CookieError::PathError(err.error),
})?,
);
update_map!(map, "Path");
continue;
}
if !map.get("Domain").unwrap() && trimmed_part.starts_with("Domain=") {
final_result = final_result.domain(trimmed_part.strip_prefix("Domain=").unwrap());
update_map!(map, "Domain");
continue;
}
if !map.get("Secure").unwrap() && trimmed_part == "Secure" {
final_result = final_result.secure(true);
update_map!(map, "Secure");
continue;
}
if !map.get("Partitioned").unwrap() && trimmed_part == "Partitioned" {
final_result = final_result.partitioned(true);
update_map!(map, "Partitioned");
continue;
}
}
println!("{:?}", final_result);
Ok(final_result.finish())
}
}
......@@ -153,7 +226,7 @@ impl FromStr for Cookie {
mod test {
use std::time::Duration;
use crate::handling::response::CookieBuilder;
use crate::handling::response::{Cookie, CookieBuilder};
use super::SameSite;
......@@ -169,19 +242,32 @@ mod test {
let test_cookie3 = CookieBuilder::build("ab", "ss")
.max_age(Duration::from_secs(24))
.domain("codecraft.com")
.path("/")
.path("/".parse().unwrap())
.same_site(SameSite::None)
.http_only(true)
.partitioned(true)
.expires("Monday")
.finish();
let test_cookie3_res = "Set-Cookie: ab=ss; HttpOnly; Partitioned; \
Max-Age=24; Domain=codecraft.com; Path=/; SameSite=None; Secure; Expires=Monday";
let test_cookie3_res = "Set-Cookie: ab=ss; Secure; HttpOnly; Partitioned; \
Max-Age=24; Domain=codecraft.com; Path=/; SameSite=None; Expires=Monday";
assert_eq!(test_cookie1_res, test_cookie1);
assert_eq!(test_cookie2_res, test_cookie2);
assert_eq!(test_cookie3_res, test_cookie3.to_string());
}
#[test]
fn cooki_from_string() {}
fn cookie_from_string() {
let test_cookie3_res = "Set-Cookie: ab=ss; HttpOnly; Partitioned; \
Max-Age=24; Domain=codecraft.com; Path=/; SameSite=None; Secure; Expires=Monday";
let test_cookie3 = CookieBuilder::build("ab", "ss")
.max_age(Duration::from_secs(24))
.domain("codecraft.com")
.path("/".parse().unwrap())
.same_site(SameSite::None)
.http_only(true)
.partitioned(true)
.expires("Monday")
.finish();
assert_eq!(test_cookie3, test_cookie3_res.parse::<Cookie>().unwrap());
}
}
use std::time::Duration;
use crate::utils::urlencoded::EnCodable;
use crate::utils::{url_utils::Uri, urlencoded::EnCodable};
use super::{Cookie, SameSite};
......@@ -11,7 +11,7 @@ use super::{Cookie, SameSite};
/// use http::handling::response::Cookie;
/// use http::handling::response::CookieBuilder;
///
/// let cookie = CookieBuilder::build("name", "value").path("/").finish();
/// let cookie = CookieBuilder::build("name", "value").path("/".parse().unwrap()).finish();
/// ```
#[derive(Debug)]
pub struct CookieBuilder {
......@@ -49,8 +49,8 @@ impl CookieBuilder {
self.inner.domain = Some(domain.encode());
self
}
pub fn path(mut self, path: &str) -> Self {
self.inner.path = Some(path.encode());
pub fn path(mut self, path: Uri) -> Self {
self.inner.path = Some(path);
self
}
pub fn secure(mut self, secure: bool) -> Self {
......@@ -62,6 +62,9 @@ impl CookieBuilder {
self
}
pub fn same_site(mut self, same_site: SameSite) -> Self {
if same_site == SameSite::None {
self.inner.secure = true;
}
self.inner.same_site = Some(same_site);
self
}
......
use std::error::Error;
use crate::utils::url_utils::UriError;
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum SameSiteError {
NotAValidVariant,
}
#[derive(Debug)]
pub struct ParseSameSiteError {
pub inner: SameSiteError,
}
impl Error for ParseSameSiteError {}
impl std::fmt::Display for ParseSameSiteError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}
impl Error for ParseCookieError {}
#[derive(Debug)]
pub struct ParseCookieError {
pub inner: CookieError,
}
#[derive(Debug, PartialEq, PartialOrd, Eq, Ord)]
pub enum CookieError {
MissingEqual,
InvalidAttribute,
InvalidMaxAge,
NoSetCookieHeader,
InvilidSameSite(SameSiteError),
PathError(UriError),
}
impl std::fmt::Display for ParseCookieError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "ParseCookieError {{ error: {:?} }}", self.inner)
}
}
mod cookie;
mod cookie_builder;
mod error_types;
pub use cookie::Cookie;
pub use cookie::SameSite;
pub use cookie_builder::CookieBuilder;
pub use error_types::CookieError;
pub use error_types::ParseCookieError;
pub use error_types::ParseSameSiteError;
pub use error_types::SameSiteError;
......@@ -20,14 +20,14 @@ pub enum RawUriElement {
Name(UrlEncodeData),
}
#[derive(Debug)]
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum UriError {
InvalidUriEncoding,
}
#[derive(Debug)]
pub struct ParseUriError {
error: UriError,
pub error: UriError,
}
impl std::fmt::Display for ParseUriError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment