///|
pub fn HttpResponse::set_cookie(
self : HttpResponse,
name : String,
value : String,
max_age? : Int,
path? : String,
domain? : String,
secure? : Bool,
http_only? : Bool,
same_site? : SameSiteOption,
) -> Unit {
let item : CookieItem = {
name,
value,
max_age,
path,
domain,
secure,
http_only,
same_site,
}
self.cookies.set(name, item)
}
///|
pub fn HttpRequest::get_cookie(
self : HttpRequest,
name : String,
) -> CookieItem? {
if self.headers.get("Cookie") is Some(cookie) {
parse_cookie(cookie).get(name)
} else if self.headers.get("cookie") is Some(cookie) {
parse_cookie(cookie).get(name)
} else {
None
}
}
///|
pub fn HttpResponse::delete_cookie(self : HttpResponse, key : String) -> Unit {
self.set_cookie(key, "", max_age=0)
}
///|
pub fn cookie_to_string(cookie : Array[CookieItem]) -> String {
cookie.map(x => x.to_string()).join(";")
}
///|
pub fn parse_cookie(cookie : StringView) -> Map[String, CookieItem] {
fn dequote(value : StringView) -> StringView {
if value is ['"', .. rest, '"'] {
rest
} else {
value
}
}
let res = Map([])
let mut last_cookie_item : (String, CookieItem)? = None
fn on_key_value(key : StringView, value : StringView) -> Unit {
let key = key.to_owned()
let value = dequote(value).to_owned()
if last_cookie_item is Some((name, item)) {
if key =~ re"^(?i:path)$" {
let new_item = { ..item, path: Some(value) }
res.set(name, new_item)
last_cookie_item = Some((name, new_item))
return
} else if key =~ re"^(?i:domain)$" {
let new_item = { ..item, domain: Some(value) }
res.set(name, new_item)
last_cookie_item = Some((name, new_item))
return
} else if key =~ re"^(?i:max-age)$" {
let new_item = {
..item,
max_age: try @string.parse_int(value) catch {
_ => None
} noraise {
x => Some(x)
},
}
res.set(name, new_item)
last_cookie_item = Some((name, new_item))
return
} else if key =~ re"^(?i:secure)$" {
let new_item = { ..item, secure: Some(true) }
res.set(name, new_item)
last_cookie_item = Some((name, new_item))
return
} else if key =~ re"^(?i:httponly)$" {
let new_item = { ..item, http_only: Some(true) }
res.set(name, new_item)
last_cookie_item = Some((name, new_item))
return
} else if key =~ re"^(?i:samesite)$" {
let same_site = if value =~ re"^(?i:lax)$" {
Some(Lax)
} else if value =~ re"^(?i:strict)$" {
Some(Strict)
} else if value =~ re"^(?i:none)$" {
Some(SameSiteNone)
} else {
None
}
let new_item = { ..item, same_site, }
res.set(name, new_item)
last_cookie_item = Some((name, new_item))
return
}
}
let item = CookieItem::{
name: key,
value,
max_age: None,
path: None,
domain: None,
secure: None,
http_only: None,
same_site: None,
}
res.set(key, item)
last_cookie_item = Some((key, item))
}
for curr = cookie {
// TODO: more spec compliance
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#attributes
if curr =~ (re"^[ \t]+", after=rest) {
continue rest
} else if curr =~ (
re"^" +
(re"[^ \t=;]([ \t]*[^ \t=;])*" as key) +
re"[ \t]*=[ \t]*" +
(re"[^ \t;]([ \t]*[^ \t;])*" as value) +
re"[ \t]*;?",
after=rest,
) {
on_key_value(key, value)
continue rest
} else if curr =~ (
re"^" +
(re"[^ \t=;]([ \t]*[^ \t=;])*" as key) +
re"[ \t]*(=[ \t]*)?;?",
after=rest,
) {
on_key_value(key, "")
continue rest
} else {
break
}
}
res
}