Reference

std/uri/uri

std/uri/src/uri.trb

Uri and UriReference: RFC 3986, read and normalized at construction, with Equals, Hash and Compare over the normalized form (docs/design/URI.md sections 3 to 5).

Both are capsules over one private record of the five parts, so the parser, the recomposition, the comparison and the resolution are written once. A Uri has a scheme; a UriReference may be relative, and resolving it against a Uri answers a Uri and cannot fail.

type Uri

type Uri with Show, Equals, Hash, Compare

A URI per RFC 3986 section 3: a scheme, and an authority, a path, a query and a fragment after it.

A Uri is a value, read from text with Uri.tryFrom and normalized on the way in - the scheme and a registered name lower case, escapes upper case and decoded where they name an unreserved character, dot segments gone, every character a URI cannot hold percent-encoded as UTF-8 - so ==, hash() and compare() are over the canonical form. What is a guess about a scheme rather than a fact about URIs, a default port, is Uri.normalized's.

It is a capsule: its field is private and has no default, so Uri.tryFrom is the one way in and Uri.text the way out, and the two are the conversion pair Encode and Decode go through - a URI in a JSON document is its text.

Examples

const uri: Uri = "HTTPS://Example.TEST/a/./b/../c?q=1#top"
print "{uri.scheme()} {uri.host()} {uri.path()}"

Pitfalls

  • show() hides a password (postgres://ada:***@host/db), and text() is the whole text: the one a request, a file or an encoder needs.
  • http://example.test:80/ and http://example.test/ are two values until Uri.normalized drops the default port.

Related

fn scheme

fn scheme(): String

The scheme, lower case: https.

fn authority

fn authority(): Authority?

The authority, where the URI has one. None and an empty host are different things.

fn host

fn host(): Host?

The host of the authority, where there is one.

fn port

fn port(): Int?

The port that is written out. It is not the port the scheme defaults to: Uri.defaultPort is.

fn defaultPort

fn defaultPort(): Int?

The port the scheme defaults to, for the five schemes IANA fixes one for: http, https, ws, wss, ftp.

fn socketAddress

fn socketAddress(): SocketAddress?

Where to connect without a resolver: the host where it is an IP literal, with the written port or the scheme's default. None for a registered name, which std/network resolves.

fn path

fn path(): String

The path. Always there, and "" where the URI has none.

fn segments

fn segments(): List<String>

The path split at its /, still percent-encoded, without the empty segment in front of an absolute path: / has none, /a/b/ has a, b and "".

fn query

fn query(): String?

Everything between the first ? and the #, as RFC 3986 leaves it: one opaque string.

fn fragment

fn fragment(): String?

Everything after the first #.

fn isUrl

fn isUrl(): Bool

Whether the URI has an authority: the question a Url type would have been for.

fn isUrn

fn isUrn(): Bool

Whether the scheme is urn. Urn.tryFrom(uri) reads the parts of one.

fn queryParameters

fn queryParameters(): Map<String, List<String>>

The query read as application/x-www-form-urlencoded: every value of every name, in order.

fn queryParameter

fn queryParameter(name: String): String?

The first value of a query parameter, which is what almost every caller of Uri.queryParameters wants.

fn withQueryParameters

fn withQueryParameters(parameters: Map<String, List<String>>): Uri

The same URI with its query written from parameters, and with none where parameters is empty.

fn withFragment

fn withFragment(fragment: String?): Uri

The same URI with its fragment replaced by fragment, and with none where it is None. The text is written as it is: every character a fragment cannot hold is percent-encoded, % among them.

fn joined

fn joined(relative: String): Uri

relative below this URI's path: split at /, each segment percent-encoded, . and empty segments dropped, and .. removing only a segment relative itself added - so the answer is never above this URI's path, whatever relative is. The query and the fragment belong to the old resource and are dropped.

Examples

const base: Uri = "https://example.test/users"
print base.joined("Ada Lovelace/posts")

fn normalized

fn normalized(): Uri

RFC 3986 section 6.2.3, scheme-based normalization: a port that is the scheme's default is dropped and an empty path under an authority becomes /. It is a member and not part of construction, because the default port of a scheme is a fact about five schemes and not a fact about URIs.

fn relativeTo

fn relativeTo(base: Uri): UriReference

This URI as seen from base: the shortest reference that UriReference.resolved turns back into this one. Where the schemes differ it is the whole URI, and where the authorities differ it starts with //.

fn text

fn text(): String

The canonical text: the recomposition of RFC 3986 section 5.3, with every part of it. A password in the user information is in this text, and the name is what makes that greppable.

fn iriText

fn iriText(): String

The IRI this URI stands for (RFC 3987 section 3.2): every escape of a UTF-8 character outside ASCII decoded, every A-label of the host shown as its U-label (https://münchen.test/ for https://xn--mnchen-3ya.test/), and everything else as Uri.text has it, so Uri.tryFrom(uri.iriText()) is the same URI again. It carries the password, as text() does.

fn show

fn show(): String

The canonical text with a password in the user information replaced by ***. Uri.text is the whole one.

fn compare

fn compare(other: Uri): Ordering

Scheme, then authority, then path, then query, then fragment, each None before every Some.

extend Uri with TryFrom<String, UriError>

extend Uri with TryFrom<String, UriError>

A text read as a URI and normalized on the way in.

Examples

print Uri.tryFrom("HTTP://Example.COM:80/a/./b/../c?q=1#top")

Errors

fn tryFrom

static fn tryFrom(value: String): Result<Uri, UriError>

extend String with From<Uri>

extend String with From<Uri>

The way back out, and with Uri.tryFrom the one conversion pair of the capsule. It is Uri.text and not Uri.show, because this is the direction Encode is derived through, and a configuration that round trips without its password is a configuration that stopped working.

fn from

static fn from(value: Uri): String

type UriReference

type UriReference with Show, Equals, Hash, Compare

A URI reference per RFC 3986 section 4.1: a Uri, or a relative reference - ../a, /b?c, //host/d, #top - that means something only against a base. UriReference.resolved is that meaning, and it answers a Uri.

It is normalized as a Uri is, except that the dot segments of a relative path (../a) are kept: they mean something only once a base is known.

Examples

const base: Uri = "https://example.test/guide/start.html"
const link: UriReference = "../reference/index.html#top"
print link.resolved(against: base)

Related

fn scheme

fn scheme(): String?

The scheme, lower case. None for a relative reference.

fn authority

fn authority(): Authority?

The authority, where the reference has one.

fn host

fn host(): Host?

The host of the authority, where there is one.

fn port

fn port(): Int?

The port that is written out.

fn path

fn path(): String

The path. Always there, and "" where the reference has none.

fn segments

fn segments(): List<String>

The path split at its /, still percent-encoded, without the empty segment in front of an absolute path.

fn query

fn query(): String?

Everything between the first ? and the #.

fn fragment

fn fragment(): String?

Everything after the first #.

fn isAbsolute

fn isAbsolute(): Bool

Whether the reference has a scheme. Its opposite is UriReference.isRelative.

fn isRelative

fn isRelative(): Bool

Whether the reference has no scheme, so that it means something only against a base.

fn uri

fn uri(): Uri?

The reference as a Uri, where it has a scheme.

fn resolved

fn resolved(against: Uri): Uri

This reference resolved against base, per RFC 3986 section 5.2 (the strict reading: a reference with a scheme is itself). It cannot fail, because a Uri always has the scheme section 5.2.1 asks of a base.

fn queryParameters

fn queryParameters(): Map<String, List<String>>

The query read as application/x-www-form-urlencoded: every value of every name, in order.

fn queryParameter

fn queryParameter(name: String): String?

The first value of a query parameter.

fn withFragment

fn withFragment(fragment: String?): UriReference

The same reference with its fragment replaced by fragment, written as it is, and with none where it is None.

fn text

fn text(): String

The canonical text, with every part of it.

fn iriText

fn iriText(): String

The IRI this reference stands for (RFC 3987 section 3.2).

fn show

fn show(): String

The canonical text with a password in the user information replaced by ***.

fn compare

fn compare(other: UriReference): Ordering

Scheme, then authority, then path, then query, then fragment, each None before every Some.

extend UriReference with TryFrom<String, UriError>

extend UriReference with TryFrom<String, UriError>

A text read as a URI reference - a URI or a relative reference - and normalized on the way in.

Errors

fn tryFrom

static fn tryFrom(value: String): Result<UriReference, UriError>

extend UriReference with From<Uri>

extend UriReference with From<Uri>

Every URI is a reference. The way back is UriReference.uri, and deliberately not a TryFrom on Uri.

fn from

static fn from(value: Uri): UriReference

extend String with From<UriReference>

extend String with From<UriReference>

The way back out of UriReference, and with UriReference.tryFrom its conversion pair.

fn from

static fn from(value: UriReference): String

extend UriReference with TryFrom<Path, UriError>

extend UriReference with TryFrom<Path, UriError>

A Path written as a URI reference: an absolute path as a file: URI - the root becomes the authority or the first segment - and a relative path as a relative reference. Every component is percent-encoded as UTF-8.

Errors

fn tryFrom

static fn tryFrom(value: Path): Result<UriReference, UriError>

extend Uri with TryFrom<Path, UriError>

extend Uri with TryFrom<Path, UriError>

A Path written as a file: URI.

Errors

fn tryFrom

static fn tryFrom(value: Path): Result<Uri, UriError>

extend Path with TryFrom<Uri, UriError>

extend Path with TryFrom<Uri, UriError>

A file: URI read as a Path, in every form RFC 8089 and its appendices know: file:///x, file:/x, file://localhost/x, file:///C:/x, file:///C|/x, file:C:/x and file://server/share/x. A fragment points into the document and is ignored.

Errors

  • UriError.NotAFile for a URI whose scheme is not file, for a query, for an escape that names a separator and for a path that is not absolute.
  • UriError.NotText for escapes whose bytes are not UTF-8.

fn tryFrom

static fn tryFrom(value: Uri): Result<Path, UriError>

extend Path with TryFrom<UriReference, UriError>

extend Path with TryFrom<UriReference, UriError>

A URI reference read as a Path: a file: URI as Path.tryFrom of a Uri reads it, and a relative-path reference as the relative path it spells.

Errors

  • UriError.NotAFile for another scheme, a network-path reference (//server/x), a query and an escape that names a separator.
  • UriError.NotText for escapes whose bytes are not UTF-8.

fn tryFrom

static fn tryFrom(value: UriReference): Result<Path, UriError>