///|
/// A domain that cannot identify a registrable site.
pub(all) enum SiteError {
InvalidSiteDomain(DomainError)
NoRegistrableDomain(String)
InvalidSiteScheme(String)
InvalidSiteHost(String, String)
} derive(Eq, Debug)
///|
pub fn SiteError::message(self : SiteError) -> String {
match self {
InvalidSiteDomain(error) => error.message()
NoRegistrableDomain(domain) =>
"domain '\{domain}' is a public suffix and has no registrable site"
InvalidSiteScheme(scheme) =>
"unsupported site scheme '\{scheme}': expected http or https"
InvalidSiteHost(host, reason) =>
"invalid site hostname '\{host}': \{reason}"
}
}
///|
/// The scheme and registrable domain of a DNS-backed web site.
///
/// Ports and paths do not affect the site identity. The hostname must already
/// be in ASCII/A-label form; use the IDNA adapter before constructing a site
/// from a Unicode hostname.
pub struct SchemefulSite {
scheme_ : String
registrable_domain_ : String
}
///|
pub fn SchemefulSite::scheme(self : SchemefulSite) -> String {
self.scheme_
}
///|
pub fn SchemefulSite::registrable_domain(self : SchemefulSite) -> String {
self.registrable_domain_
}
///|
pub fn SchemefulSite::key(self : SchemefulSite) -> String {
self.scheme_ + "://" + self.registrable_domain_
}
///|
pub fn SchemefulSite::same_site(
self : SchemefulSite,
other : SchemefulSite,
) -> Bool {
self.scheme_ == other.scheme_ &&
self.registrable_domain_ == other.registrable_domain_
}
///|
/// Construct a DNS-backed web site using browser-style PSL semantics.
pub fn SuffixList::schemeful_site(
self : SuffixList,
scheme : String,
hostname : String,
) -> Result[SchemefulSite, SiteError] {
self.schemeful_site_with_options(
scheme,
hostname,
LookupOptions::browser_default(),
)
}
///|
/// Construct a web site under an explicit PSL section and fallback policy.
/// Scheme is case-insensitive; only HTTP and HTTPS DNS hosts are supported.
pub fn SuffixList::schemeful_site_with_options(
self : SuffixList,
scheme : String,
hostname : String,
options : LookupOptions,
) -> Result[SchemefulSite, SiteError] {
let normalized_scheme = scheme.to_lower()
if normalized_scheme != "http" && normalized_scheme != "https" {
return Err(InvalidSiteScheme(scheme))
}
let host = remove_trailing_root_dot(hostname)
let ascii_host = match canonical_ascii_dns_name(host) {
Ok(value) => value
Err(reason) => return Err(InvalidSiteHost(hostname, reason))
}
match self.site_key_with_options(ascii_host, options) {
Ok(domain) =>
Ok({ scheme_: normalized_scheme, registrable_domain_: domain, })
Err(error) => Err(error)
}
}
///|
fn remove_trailing_root_dot(domain : String) -> String {
if domain.has_suffix(".") {
domain[:domain.length() - 1].to_owned()
} else {
domain
}
}
///|
/// Return the canonical registrable site key using browser-style PSL policy.
///
/// The key is the registrable domain without a trailing DNS root dot. A public
/// suffix by itself cannot identify an independently controlled site.
pub fn SuffixList::site_key(
self : SuffixList,
domain : String,
) -> Result[String, SiteError] {
self.site_key_with_options(domain, LookupOptions::browser_default())
}
///|
/// Return the canonical registrable site key under an explicit lookup policy.
pub fn SuffixList::site_key_with_options(
self : SuffixList,
domain : String,
options : LookupOptions,
) -> Result[String, SiteError] {
match self.lookup_with_options(domain, options) {
Err(error) => Err(InvalidSiteDomain(error))
Ok(result) =>
match result.registrable_domain() {
Some(site) => Ok(remove_trailing_root_dot(site))
None => Err(NoRegistrableDomain(result.normalized_domain()))
}
}
}
///|
/// Test whether two hostnames have the same registrable site key using the
/// browser-style PSL policy.
///
/// This compares hostname boundaries only. Schemeful same-site callers must
/// additionally compare their already-parsed URL schemes.
pub fn SuffixList::same_registrable_site(
self : SuffixList,
left : String,
right : String,
) -> Result[Bool, SiteError] {
self.same_registrable_site_with_options(
left,
right,
LookupOptions::browser_default(),
)
}
///|
/// Test whether two hostnames have the same registrable site key under an
/// explicit lookup policy. The left hostname is validated first.
pub fn SuffixList::same_registrable_site_with_options(
self : SuffixList,
left : String,
right : String,
options : LookupOptions,
) -> Result[Bool, SiteError] {
let left_site = match self.site_key_with_options(left, options) {
Ok(site) => site
Err(error) => return Err(error)
}
let right_site = match self.site_key_with_options(right, options) {
Ok(site) => site
Err(error) => return Err(error)
}
Ok(left_site == right_site)
}