///|
/// Where a request goes, from its absolute `http` or `https` URL.
priv struct Target {
/// The host to dial: a name, an IPv4 address, or an IPv6 address without
/// brackets.
host : String
port : Int32
secure : Bool
/// `host:port` with the port always written and IPv6 in brackets: the
/// target of a CONNECT that opens a tunnel.
authority : String
/// The `Host` field: the authority without the scheme's default port.
host_field : String
/// `scheme://host:port` with the port always written: the key that parks
/// and counts connections.
origin : String
/// The request target in origin form, or the authority for CONNECT.
path : String
}
///|
/// Validates `request` and parses its URL. A relative URL, a scheme other
/// than http and https, a URL without a host or with userinfo is refused
/// with `Invalid` in phase Pool. The fragment is never sent.
fn target(
request : @mttp_core.Request,
) -> Target raise @mttp_core.TransportError {
request.validate()
let url = @mttp_core.Url::parse(request.url) catch {
_ => raise invalid("invalid URL")
}
guard url.scheme is Some("http" | "https" as scheme) &&
url.host is Some(host) &&
!host.is_empty() &&
url.userinfo is None &&
url.port_or_default() is Some(port) else {
raise invalid(
"the client requires an absolute http(s) URL without userinfo",
)
}
let name = if host.contains(":") { "[" + host + "]" } else { host }
let authority = name + ":" + port.to_string()
let default_port = if scheme == "https" { 443 } else { 80 }
let path = if request.meth == Connect {
authority
} else {
(if url.path.is_empty() { "/" } else { url.path }) +
(match url.query {
Some(q) => "?" + q
None => ""
})
}
{
host,
port,
secure: scheme == "https",
authority,
// A CONNECT names the authority with its port, Host included.
host_field: if port == default_port && request.meth != Connect {
name
} else {
authority
},
origin: scheme + "://" + authority,
path,
}
}