logo

const class

sys::Uri

sys::Obj
  sys::Uri

Uri is used to immutably represent a Universal Resource Identifier according to RFC 3986. The generic format for a URI is:

<uri>       := [<scheme> ":"] <body>
<body>      := ["//" <auth>] ["/" <path>] ["?" <query>] ["#" <frag>]
<auth>      := [<userInfo> "@"] <host> [":" <port>]
<path>      := <name> ("/" <name>)*
<name>      := <basename> ["." <ext>]
<query>     := <queryPair> (<querySep> <queryPair>)*
<querySep>  := "&" | ";"
<queryPair> := <queryKey> ["=" <queryVal>]

Uris are expressed in either encoded or decoded form. In encoded form RFC 3986 defines a strict set of rules for the characters allowed in each section of the URI (scheme, userInfo, host, path, query, and fragment). Any character outside of the allowed set is UTF-8 encoded into octets and %HH percent encoded.

In decoded form the full range of Unicode characters is allowed in all sections except the general delimiters which separate sections. For example ? is barred in any section before the query, buf is permissible in the query string itself or the fragment identifier. The scheme must be strictly defined in terms of ASCII alphanumeric, ".", "+", or "-".

The Uri API is designed to work with the decoded format of the Uri. Access methods like host, pathStr, or queryStr all return the decoded format of the URI. To summarize different ways of working with Uri:

  • Uri.fromStr: parses a string from its decoded format
  • Uri.decode: parses a string from percent encoded format
  • Uri.encode: translate into percent encoded format

Uri can be used to model either absolute URIs or relative references. The plus and minus shortcut operators can be used to resolve and relativize relative references against a base URI.

Slots

auth

Str auth()

The authority represents a network endpoint in the format:

[<userInfo> "@"] host [":" <port>]

Examples:

`http://user@host:99/`.auth -> "user@host:99"
`http://host/`.auth -> "host"
`/dir/file.txt`.auth -> null
basename

Str basename()

Return file name without the extension (everything up to the last dot) or null if name is null.

Examples:

`/`.basename -> null
`/a/file.txt`.basename -> "file"
`/a/file`.basename -> "file"
`/a/file.`.basename -> "file"
`..`.basename -> ".."
checkName

static Void checkName(Str name)

If the specified string is not a valid name according to the isName method, then throw NameErr.

decode

static Uri decode(Str s)

Parse an ASCII percent encoded string into a Uri according to RFC 3986. All %HH escape sequences are translated into octects, and then the octect sequence is UTF-8 decoded into a Str. The + character in the query section is unescaped into a space. ParseErr if the string is a malformed URI or if not encoded correctly. Refer to fromStr for normalization rules.

decodeQuery

static Str:Str decodeQuery(Str s)

Decode a map of query parameters which are URL encoded according to the "application/x-www-form-urlencoded" MIME type. This method will unescape % percent encoding and + into space. The parameters are parsed into map using the same semantics as Uri.query. Throw ArgErr is the string is malformed. See encodeQuery.

encode

Str encode()

Return the percent encoded string for this Uri according to RFC 3986. Each section of the Uri is UTF-8 encoded into octects and then percent encoded according to its valid character set. Spaces in the query section are encoded as +.

encodeQuery

static Str encodeQuery(Str:Str q)

Encode a map of query parameters into URL percent encoding according to the "application/x-www-form-urlencoded" MIME type. See decodeQuery.

equals

override Bool equals(Obj that)

Two Uris are equal if they have same string normalized representation.

ext

Str ext()

Return file name extension (everything after the last dot) or null if name is null or name has no dot.

Examples:

`/`.ext -> null
`/a/file.txt`.ext -> "txt"
`/Foo.Bar`.ext -> "Bar"
`/a/file`.ext-> null
`/a/file.`.ext-> ""
`..`.ext -> null
frag

Str frag()

Return the fragment component of the Uri which is everything after the "#". Return null if no fragment specified.

Examples:

`http://host/path?query#frag`.frag -> "frag"
`http://host/path` -> null
`#h1` -> "h1"
fromStr

static Uri fromStr(Str s)

Parse the specified string into a Uri. Throw ParseErr if the string is a malformed URI. This method parses an decoded Unicode string into its generic parts. It does not unescape % or + and handles normal Unicode characters in the string.

All Uris are automatically normalized as follows:

  • Replacing "." and ".." segments in the middle of a path
  • Scheme always normalizes to lowercase
  • If http then port 80 normalizes to null
  • If http then a null path normalizes to /
get

Obj get(Obj base := def)

Resolve this Uri to it's target object. If this Uri is absolute then base may be null (it is ignored), otherwise the Uri is resolved against the base specified. Throw ArgErr is base is null and this Uri is relative. Return null if the Uri cannot be resolved. Steps for resolution:

1) if absolute, map scheme to factory class to get base (TODO)
2) route to base.trapUri

TODO: how should this method be with Resources and resolve???

hash

override Int hash()

Return a hash code based on the normalized string representation.

host

Str host()

Return the host address of the URI or null if not available. The host is in the format of a DNS name, IPv4 address, or IPv6 address surrounded by square brackets. Return null if the uri is not absolute.

Examples:

`ftp://there:78/file`.host -> "there"
`http://www.cool.com/`.host -> "www.cool.com"
`http://user@10.162.255.4/index`.host -> "10.162.255.4"
`http://[::192.9.5.5]/`.host -> "[::192.9.5.5]"
`//foo/bar`.host -> "foo"
`/bar`.host -> null
isAbs

Bool isAbs()

Return if an absolute Uri which means it has a nonnull scheme.

isDir

Bool isDir()

A Uri represents a directory if it has a non-null path which ends with a "/" slash. Directories are joined with other Uris relative to themselves versus non-directories which are joined relative to their parent.

Examples:

`/a/b`.isDir -> false
`/a/b/`.isDir -> true
isName

static Bool isName(Str name)

Return if the specified string is an valid name segment to use in an unencoded URI. The name must be at least one char long and can never be "." or "..". The legal characters are defined by as follows from RFC 3986:

unreserved  =  ALPHA / DIGIT / "-" / "." / "_" / "~"
ALPHA       =  %x41-5A / %x61-7A   ; A-Z / a-z
DIGIT       =  %x30-39 ; 0-9

Although RFC 3986 does allow path segments to contain other special characters such as sub-delims, Fan takes a strict approach to names to be used in URIs.

isPathAbs

Bool isPathAbs()

Return if the path starts with a leading slash. If pathStr is null, then return false.

Examples:

`http://foo/`.isPathAbs   -> true
`/dir/f.txt`.isPathAbs    -> true
`dir/f.txt`.isPathAbs     -> false
`../index.html`.isPathAbs -> false
isRel

Bool isRel()

Return if a relative Uri which means it has a null scheme.

minus

Uri minus(Uri toRelativize)

Relativize this uri against the specified base.

Examples:

`http://foo/a/b/c` - `http://foo/a/b/c`  -> ``
`http://foo/a/b/c` - `http://foo/a/b`  -> `c`
`//foo/a/b/c` - `http://foo/` -> `a/b/c`
`/a/b/c` - `/a` ->  `b/c`
name

Str name()

Return simple file name which is path.last or null if the path is empty.

Examples:

`/`.name -> null
`/a/file.txt`.name -> "file.txt"
`/a/file`.name -> "file"
parent

Uri parent()

Return the parent directory of this Uri or null if a parent path cannot be computed from this Uri.

Examples:

`http://foo/a/b/c?q#f`.parent -> `http://foo/a/b/`
`/a/b/c/`.parent -> `/a/b/`)
`a/b/c`.parent   -> `a/b/`
`/a`.parent      ->  `/`
`/`.parent       ->  null
`a.txt`.parent   ->  null
path

Str[] path()

Return the path parsed into a list of simple names or an empty list if the pathStr is "" or "/".

Examples:

`mailto:me@there.com` -> null
`http://host`.path -> Str[,]
`http://foo/`.path -> Str[,]
`/`.path -> Str[,]
`/a`.path -> ["a"]
`/a/b`.path -> ["a", "b"]
`../a/b`.path -> ["..", "a", "b"]
pathStr

Str pathStr()

Return the path component of the Uri.

Examples:

`mailto:me@there.com` -> "me@there.com"
`http://host` -> ""
`http://foo/`.pathStr -> "/"
`/a`.pathStr -> "/a"
`/a/b`.pathStr -> "/a/b"
`../a/b`.pathStr -> "../a/b"
plus

Uri plus(Uri toAppend)

Return a new Uri with the specified Uri appended to this Uri.

Examples:

`http://foo/path` + `http://bar/` -> `http://bar/`
`http://foo/path?q#f` + `newpath` -> `http://foo/newpath`
`http://foo/path/?q#f` + `newpath` -> `http://foo/path/newpath`
`a/b/c`  + `d` -> `a/b/d`
`a/b/c/` + `d` -> `a/b/c/d`
`a/b/c`  + `../../d` -> `d`
`a/b/c/` + `../../d` -> `a/d`
`a/b/c`  + `../../../d` -> `../d`
`a/b/c/` + `../../../d` -> `d`
port

Int port()

Return the IP port of the host for the network end point. It is optionally embedded in the authority using the ":" character. If unspecified then return null.

Examples:

`http://foo:81/'.port -> 81
`http://www.cool.com/`.port -> null
query

Str:Str query()

Return the query parsed as a map of key/value pairs. If no query string was specified return an empty map (this method will never return null). The query is parsed such that pairs are separated by the "&" or ";" characters. If a pair contains the "=", then it is split into a key and value, otherwise the value defaults to "true".

Examples:

`http://host/path?query`.query -> ["query":"true"]
`http://host/path`.query -> [:]
`?a=b;c=d`.query -> ["a":"b", "c":"d"]
`?a=b&c=d`.query -> ["a":"b", "c":"d"]
`?a=b;;c=d;`.query -> ["a":"b", "c":"d"]
`?a=b;;c`.query -> ["a":"b", "c":"true"]
queryStr

Str queryStr()

Return the query component of the Uri which is everything after the "?" but before the "#" fragment. Return null if no query string specified.

Examples:

`http://host/path?query#frag`.queryStr -> "query"
`http://host/path?query`.queryStr -> "query"
`http://host/path`.queryStr -> null
`../foo?a=b&c=d`.queryStr -> "a=b&c=d"
`?a=b;c;`.queryStr -> "a=b;c;"
resolve

Resource resolve(Resource base := def, Bool checked := def)

Resolve this Uri to a resource within the local virtual machine's namespace. If this Uri is path absolute, then it is resolved against Sys.namespace. If base is null, then the Uri must be path absolute. If the resource cannot be resolved and checked is false return null, otherwise throw UnresolvedErr.

TODO: doc things like how scheme is handled???

scheme

Str scheme()

Return the scheme component or null if not absolute. The scheme is always normalized into lowercase.

Examples:

`http://foo/a/b/c`.scheme -> "http"
`HTTP://foo/a/b/c`.scheme -> "http"
`mailto:who@there.com`.scheme -> "mailto"
tail

Uri tail(Int levels := def)

Return this Uri relativized against the specified number of parent levels of the path. If path is null or path.size < levels then throw ArgErr.

Examples:

`http://host/a/b/c?query#frag`.tail -> `b/c`
`a/b/c/d`.tail -> `b/c/d`
`a/b/c`.tail(2) -> `c`
`a`.tail, ``
toFile

File toFile()

Convenience for File.make(this) - no guarantee is made that the file exists.

toStr

override Str toStr()

Return normalized string representation.

userInfo

Str userInfo()

User info is string information embedded in the authority using the "@" character. Its use is discouraged for security reasons.

Examples:

`http://brian:pass@host/'.userInfo -> "brian:pass"
`http://www.cool.com/`.userInfo -> null