///|
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(_.to_string()).join(";")
}
///|
///|
///|
pub fn parse_cookie(cookie : StringView) -> Map[String, CookieItem] {
let res = Map::new()
let mut view = cookie
let mut last_cookie_name = ""
while view.length() > 0 {
// Skip whitespace using lexmatch
while true {
if view lexmatch? (" ", rest) {
view = rest
continue
}
if view lexmatch? ("\t", rest) {
view = rest
continue
}
break
}
if view.length() == 0 {
break
}
// Parse key
let mut key_end = 0
while key_end < view.length() &&
view[key_end] != '=' &&
view[key_end] != ';' {
key_end = key_end + 1
}
let key = view[0:key_end].trim_space().to_string() catch { _ => "" }
view = view[key_end:] catch { _ => view }
// Parse value
let mut val = ""
if view lexmatch? ("=", rest) {
view = rest
// Skip whitespace
while true {
if view lexmatch? (" ", rest) {
view = rest
continue
}
if view lexmatch? ("\t", rest) {
view = rest
continue
}
break
}
if view lexmatch? ("\"", rest) {
// Quoted value
view = rest
let mut q_end = 0
while q_end < view.length() && view[q_end] != '"' {
q_end = q_end + 1
}
val = view[0:q_end].to_string() catch { _ => "" }
// Skip closing quote if present
if q_end < view.length() {
view = view[q_end + 1:] catch {
_ => view[q_end:] catch { _ => view }
}
} else {
view = view[q_end:] catch { _ => view }
}
} else {
// Unquoted value
let mut v_end = 0
while v_end < view.length() && view[v_end] != ';' {
v_end = v_end + 1
}
val = view[0:v_end].trim_space().to_string() catch { _ => "" }
view = view[v_end:] catch { _ => view }
}
}
if key != "" {
let lower_key = key.to_lower()
if last_cookie_name != "" &&
(
lower_key == "path" ||
lower_key == "domain" ||
lower_key == "max-age" ||
lower_key == "secure" ||
lower_key == "httponly" ||
lower_key == "samesite"
) {
// It's an attribute for the last cookie
match res.get(last_cookie_name) {
Some(item) => {
let mut new_item : CookieItem = item
match lower_key {
"path" => new_item = { ..item, path: Some(val) }
"domain" => new_item = { ..item, domain: Some(val) }
"max-age" =>
new_item = {
..item,
max_age: Some(@strconv.parse_int(val)) catch {
_ => None
},
}
"secure" => new_item = { ..item, secure: Some(true) }
"httponly" => new_item = { ..item, http_only: Some(true) }
"samesite" => {
let same_site = match val.to_lower() {
"lax" => Some(Lax)
"strict" => Some(Strict)
"none" => Some(SameSiteNone)
_ => None
}
new_item = { ..item, same_site, }
}
_ => ()
}
res.set(last_cookie_name, new_item)
}
None => ()
}
} else {
// New cookie
let item : CookieItem = {
name: key,
value: val,
max_age: None,
path: None,
domain: None,
secure: None,
http_only: None,
same_site: None,
}
res.set(key, item)
last_cookie_name = key
}
}
// Skip until next semicolon
if view lexmatch? (";", rest) {
view = rest
} else {
// Skip invalid chars until ;
let mut i = 0
while i < view.length() && view[i] != ';' {
i = i + 1
}
if i < view.length() {
view = view[i + 1:] catch { _ => view }
} else {
view = view[i:] catch { _ => view }
}
}
}
res
}