///|
/// The parts of a request URL the cookie algorithms read, taken from
/// `@http.Url` so that hosts, ports and paths are read as the rest of the
/// stack reads them.
priv struct RequestUrl {
  /// Lowercase scheme, without `:`.
  scheme : String
  /// The canonical host: lowercase, an IPv6 address in RFC 5952 form inside
  /// brackets. Never empty.
  host : String
  /// The normalised path, without query or fragment; `/` when it is empty.
  path : String
}

///|
/// Reads an absolute URL with a non-empty host, or returns `None`: a relative
/// reference, one without an authority, an empty host, or anything
/// `@http.Url::parse` refuses (a non-ASCII host among them).
fn parse_request_url(text : String) -> RequestUrl? {
  let url = @http.Url::parse(text) catch { _ => return None }
  guard url.scheme is Some(scheme) && url.host is Some(host) && !host.is_empty() else {
    return None
  }
  Some({
    scheme,
    host: if host.contains(":") {
      "[" + host + "]"
    } else {
      host
    },
    path: if url.path.is_empty() {
      "/"
    } else {
      url.path
    },
  })
}

///|
/// The canonical form of a `Domain` attribute that names an IPv6 literal, so
/// that it compares equal to the request host however it is written; any
/// other value is returned as it is.
fn canonical_ip_literal(domain : String) -> String {
  guard domain.has_prefix("[") &&
    domain.has_suffix("]") &&
    parse_request_url("http://" + domain) is Some(url) else {
    return domain
  }
  url.host
}

///|
/// Whether the scheme is one a Secure cookie may be set over or sent to.
fn RequestUrl::is_secure(self : RequestUrl) -> Bool {
  self.scheme == "https" || self.scheme == "wss"
}