///|
/// Whether a canonical (lowercase) host is an IP address rather than a
/// domain name: a bracketed IPv6 literal, or a host whose last label is a
/// number (the WHATWG URL "ends in a number" rule, which also catches
/// shorthand IPv4 forms such as `127.1` and `0x7f.1`).
fn is_ip_host(host : String) -> Bool {
if host.has_prefix("[") {
return true
}
let host = host.strip_suffix(".").unwrap_or(host[:])
let last = match host.rev_find(".") {
Some(dot) => host[dot + 1:]
None => host
}
if last.is_empty() {
return false
}
let digits = if last.has_prefix("0x") || last.has_prefix("0X") {
last[2:]
} else {
last
}
for c in digits {
guard c.is_ascii_digit() ||
(digits.length() < last.length() && c.is_ascii_hexdigit()) else {
return false
}
}
true
}
///|
/// RFC 6265 ยง5.1.3 domain-match: `host` is `domain`, or ends with `.` then
/// `domain` and is a name rather than an IP address.
fn domain_match(host : String, domain : String) -> Bool {
host == domain || (host.has_suffix("." + domain) && !is_ip_host(host))
}
///|
/// The one public-suffix rule this package applies without a list: a
/// single-label domain (`com`, `localhost`, a bracketed IPv6 literal) is a
/// public suffix. Under the Public Suffix List every single label is one too
/// (its default rule is `*`), so a list only adds to this rule.
fn is_single_label(domain : String) -> Bool {
!domain.contains(".")
}
///|
/// The domains whose cookies can match a request to `host`: the host itself
/// and, unless it is an IP address, each parent domain.
fn candidate_domains(host : String) -> Array[String] {
let domains = [host]
if !is_ip_host(host) {
let mut rest = host[:]
while rest.find(".") is Some(dot) {
rest = rest[dot + 1:]
if !rest.is_empty() {
domains.push(rest.to_owned())
}
}
}
domains
}