URI — Elixir v1.16.3

Search documentation of Elixir

# View Source URI (Elixir v1.16.3)

Utilities for working with URIs.

This module provides functions for working with URIs (for example, parsing URIs or encoding query strings). The functions in this module are implemented according to RFC 3986.

# Summary

## Types

authority() deprecated
t()

## Functions

%URI{}

The URI struct.

append_path(uri, path)

Appends `path` to the given `uri`.

append_query(uri, query)

Appends `query` to the given `uri`.

char_reserved?(character)

Checks if `character` is a reserved one in a URI.

char_unescaped?(character)

Checks if `character` is allowed unescaped in a URI.

char_unreserved?(character)

Checks if `character` is an unreserved one in a URI.

decode(uri)

Percent-unescapes a URI.

decode_query(query, map \\ %{}, encoding \\ :www_form)

Decodes `query` into a map.

decode_www_form(string)

Decodes `string` as "x-www-form-urlencoded".

default_port(scheme)

Returns the default port for a given `scheme`.

default_port(scheme, port)

Registers the default `port` for the given `scheme`.

encode(string, predicate \\ &char_unescaped?/1)

Percent-encodes all characters that require escaping in `string`.

encode_query(enumerable, encoding \\ :www_form)

Encodes `enumerable` into a query string using `encoding`.

encode_www_form(string)

Encodes `string` as "x-www-form-urlencoded".

merge(uri, rel)

Merges two URIs.

new(uri)

Creates a new URI struct from a URI or a string.

new!(uri)

Similar to `new/1` but raises `URI.Error` if an invalid string is given.

parse(uri)

Parses a URI into its components, without further validation.

query_decoder(query, encoding \\ :www_form)

Returns a stream of two-element tuples representing key-value pairs in the given `query`.

to_string(uri)

Returns the string representation of the given URI struct.

# Types

Link to this opaque

# authority()

View Source
This opaque is deprecated. The authority field is deprecated.

    @opaque authority()

Link to this type

# t()

View Source

    @type t() :: %URI{
      authority: authority(),
      fragment: nil | binary(),
      host: nil | binary(),
      path: nil | binary(),
      port: nil | :inet.port_number(),
      query: nil | binary(),
      scheme: nil | binary(),
      userinfo: nil | binary()
    }

# Functions

Link to this function

# %URI{}

View Source (struct)

The URI struct.

The fields are defined to match the following URI representation (with field names between brackets):

    [scheme]://[userinfo]@[host]:[port][path]?[query]#[fragment]

Note the `authority` field is deprecated. `parse/1` will still populate it for backwards compatibility but you should generally avoid setting or getting it.

Link to this function

# append_path(uri, path)

View Source (since 1.15.0)

    @spec append_path(t(), String.t()) :: t()

Appends `path` to the given `uri`.

Path must start with `/` and cannot contain additional URL components like fragments or query strings. This function further assumes the path is valid and it does not contain a query string or fragment parts.

## Examples

    iex> URI.append_path(URI.parse("http://example.com/foo/?x=1"), "/my-path") |> URI.to_string()
    "http://example.com/foo/my-path?x=1"

    iex> URI.append_path(URI.parse("http://example.com"), "my-path")
    ** (ArgumentError) path must start with "/", got: "my-path"

Link to this function

# append_query(uri, query)

View Source (since 1.14.0)

    @spec append_query(t(), binary()) :: t()

Appends `query` to the given `uri`.

The given `query` is not automatically encoded, use `encode/2` or `encode_www_form/1`.

## Examples

    iex> URI.append_query(URI.parse("http://example.com/"), "x=1") |> URI.to_string()
    "http://example.com/?x=1"

    iex> URI.append_query(URI.parse("http://example.com/?x=1"), "y=2") |> URI.to_string()
    "http://example.com/?x=1&y=2"

    iex> URI.append_query(URI.parse("http://example.com/?x=1"), "x=2") |> URI.to_string()
    "http://example.com/?x=1&x=2"

Link to this function

# char_reserved?(character)

View Source

    @spec char_reserved?(byte()) :: boolean()

Checks if `character` is a reserved one in a URI.

As specified in RFC 3986, section 2.2, the following characters are reserved: `:`, `/`, `?`, `#`, `[`, `]`, `@`, `!`, `$`, `&`, `'`, `(`, `)`, `*`, `+`, `,`, `;`, `=`

## Examples

    iex> URI.char_reserved?(?+)
    true

Link to this function

# char_unescaped?(character)

View Source

    @spec char_unescaped?(byte()) :: boolean()

Checks if `character` is allowed unescaped in a URI.

This is the default used by `URI.encode/2` where both reserved and unreserved characters are kept unescaped.

## Examples

    iex> URI.char_unescaped?(?{)
    false

Link to this function

# char_unreserved?(character)

View Source

    @spec char_unreserved?(byte()) :: boolean()

Checks if `character` is an unreserved one in a URI.

As specified in RFC 3986, section 2.3, the following characters are unreserved:

- Alphanumeric characters: `A-Z`, `a-z`, `0-9`
- `~`, `_`, `-`, `.`

## Examples

    iex> URI.char_unreserved?(?_)
    true

Link to this function

# decode(uri)

View Source

    @spec decode(binary()) :: binary()

Percent-unescapes a URI.

## Examples

    iex> URI.decode("https%3A%2F%2Felixir-lang.org")
    "https://elixir-lang.org"

Link to this function

# decode_query(query, map \\ %{}, encoding \\ :www_form)

View Source

    @spec decode_query(binary(), %{optional(binary()) => binary()}, :rfc3986 | :www_form) ::
      %{
        optional(binary()) => binary()
      }

Decodes `query` into a map.

Given a query string in the form of `key1=value1&key2=value2...`, this function inserts each key-value pair in the query string as one entry in the given `map`. Keys and values in the resulting map will be binaries. Keys and values will be percent-unescaped.

You can specify one of the following `encoding` options:

- `:www_form` - (default, since v1.12.0) keys and values are decoded as per `decode_www_form/1`. This is the format typically used by browsers on query strings and form data. It decodes "+" as " ".

- `:rfc3986` - (since v1.12.0) keys and values are decoded as per `decode/1`. The result is the same as `:www_form` except for leaving "+" as is in line with RFC 3986.

Encoding defaults to `:www_form` for backward compatibility.

Use `query_decoder/1` if you want to iterate over each value manually.

## Examples

    iex> URI.decode_query("foo=1&bar=2")
    %{"bar" => "2", "foo" => "1"}

    iex> URI.decode_query("percent=oh+yes%21", %{"starting" => "map"})
    %{"percent" => "oh yes!", "starting" => "map"}

    iex> URI.decode_query("percent=oh+yes%21", %{}, :rfc3986)
    %{"percent" => "oh+yes!"}

Link to this function

# decode_www_form(string)

View Source

    @spec decode_www_form(binary()) :: binary()

Decodes `string` as "x-www-form-urlencoded".

Note "x-www-form-urlencoded" is not specified as part of RFC 3986. However, it is a commonly used format to encode query strings and form data by browsers.

## Examples

    iex> URI.decode_www_form("%3Call+in%2F")
    "<all in/"

Link to this function

# default_port(scheme)

View Source

    @spec default_port(binary()) :: nil | non_neg_integer()

Returns the default port for a given `scheme`.

If the scheme is unknown to the `URI` module, this function returns `nil`. The default port for any scheme can be configured globally via `default_port/2`.

## Examples

    iex> URI.default_port("ftp")
    21

    iex> URI.default_port("ponzi")
    nil

Link to this function

# default_port(scheme, port)

View Source

    @spec default_port(binary(), non_neg_integer()) :: :ok

Registers the default `port` for the given `scheme`.

After this function is called, `port` will be returned by `default_port/1` for the given scheme `scheme`. Note that this function changes the default port for the given `scheme` globally, meaning for every application.

It is recommended for this function to be invoked in your application's start callback in case you want to register new URIs.

Link to this function

# encode(string, predicate \\ &char_unescaped?/1)

View Source

    @spec encode(binary(), (byte() -> as_boolean(term()))) :: binary()

Percent-encodes all characters that require escaping in `string`.

By default, this function is meant to escape the whole URI, and therefore it will escape all characters which are foreign to the URI specification. Reserved characters (such as `:` and `/`) or unreserved (such as letters and numbers) are not escaped.

Because different components of a URI require different escaping rules, this function also accepts a `predicate` function as an optional argument. If passed, this function will be called with each byte in `string` as its argument and should return a truthy value (anything other than `false` or `nil`) if the given byte should be left as is, or return a falsy value (`false` or `nil`) if the character should be escaped. Defaults to `URI.char_unescaped?/1`.

See `encode_www_form/1` if you are interested in escaping reserved characters too.

## Examples

    iex> URI.encode("ftp://s-ite.tld/?value=put it+й")
    "ftp://s-ite.tld/?value=put%20it+%D0%B9"

    iex> URI.encode("a string", &(&1 != ?i))
    "a str%69ng"

Link to this function

# encode_query(enumerable, encoding \\ :www_form)

View Source

    @spec encode_query(Enumerable.t(), :rfc3986 | :www_form) :: binary()

Encodes `enumerable` into a query string using `encoding`.

Takes an enumerable that enumerates as a list of two-element tuples (for instance, a map or a keyword list) and returns a string in the form of `key1=value1&key2=value2...`.

Keys and values can be any term that implements the `String.Chars` protocol with the exception of lists, which are explicitly forbidden.

You can specify one of the following `encoding` strategies:

- `:www_form` - (default, since v1.12.0) keys and values are URL encoded as per `encode_www_form/1`. This is the format typically used by browsers on query strings and form data. It encodes " " as "+".

- `:rfc3986` - (since v1.12.0) the same as `:www_form` except it encodes " " as "%20" according RFC 3986. This is the best option if you are encoding in a non-browser situation, since encoding spaces as "+" can be ambiguous to URI parsers. This can inadvertently lead to spaces being interpreted as literal plus signs.

Encoding defaults to `:www_form` for backward compatibility.

## Examples

    iex> query = %{"foo" => 1, "bar" => 2}
    iex> URI.encode_query(query)
    "bar=2&foo=1"

    iex> query = %{"key" => "value with spaces"}
    iex> URI.encode_query(query)
    "key=value+with+spaces"

    iex> query = %{"key" => "value with spaces"}
    iex> URI.encode_query(query, :rfc3986)
    "key=value%20with%20spaces"

    iex> URI.encode_query(%{key: [:a, :list]})
    ** (ArgumentError) encode_query/2 values cannot be lists, got: [:a, :list]

Link to this function

# encode_www_form(string)

View Source

    @spec encode_www_form(binary()) :: binary()

Encodes `string` as "x-www-form-urlencoded".

Note "x-www-form-urlencoded" is not specified as part of RFC 3986. However, it is a commonly used format to encode query strings and form data by browsers.

## Example

    iex> URI.encode_www_form("put: it+й")
    "put%3A+it%2B%D0%B9"

Link to this function

# merge(uri, rel)

View Source

    @spec merge(t() | binary(), t() | binary()) :: t()

Merges two URIs.

This function merges two URIs as per RFC 3986, section 5.2.

## Examples

    iex> URI.merge(URI.parse("http://google.com"), "/query") |> to_string()
    "http://google.com/query"

    iex> URI.merge("http://example.com", "http://google.com") |> to_string()
    "http://google.com"

Link to this function

# new(uri)

View Source (since 1.13.0)

    @spec new(t() | String.t()) :: {:ok, t()} | {:error, String.t()}

Creates a new URI struct from a URI or a string.

If a `%URI{}` struct is given, it returns `{:ok, uri}`. If a string is given, it will parse and validate it. If the string is valid, it returns `{:ok, uri}`, otherwise it returns `{:error, part}` with the invalid part of the URI. For parsing URIs without further validation, see `parse/1`.

This function can parse both absolute and relative URLs. You can check if a URI is absolute or relative by checking if the `scheme` field is `nil` or not.

When a URI is given without a port, the value returned by `URI.default_port/1` for the URI's scheme is used for the `:port` field. The scheme is also normalized to lowercase.

## Examples

    iex> URI.new("https://elixir-lang.org/")
    {:ok, %URI{
      fragment: nil,
      host: "elixir-lang.org",
      path: "/",
      port: 443,
      query: nil,
      scheme: "https",
      userinfo: nil
    }}

    iex> URI.new("//elixir-lang.org/")
    {:ok, %URI{
      fragment: nil,
      host: "elixir-lang.org",
      path: "/",
      port: nil,
      query: nil,
      scheme: nil,
      userinfo: nil
    }}

    iex> URI.new("/foo/bar")
    {:ok, %URI{
      fragment: nil,
      host: nil,
      path: "/foo/bar",
      port: nil,
      query: nil,
      scheme: nil,
      userinfo: nil
    }}

    iex> URI.new("foo/bar")
    {:ok, %URI{
      fragment: nil,
      host: nil,
      path: "foo/bar",
      port: nil,
      query: nil,
      scheme: nil,
      userinfo: nil
    }}

    iex> URI.new("//[fe80::]/")
    {:ok, %URI{
      fragment: nil,
      host: "fe80::",
      path: "/",
      port: nil,
      query: nil,
      scheme: nil,
      userinfo: nil
    }}

    iex> URI.new("https:?query")
    {:ok, %URI{
      fragment: nil,
      host: nil,
      path: nil,
      port: 443,
      query: "query",
      scheme: "https",
      userinfo: nil
    }}

    iex> URI.new("/invalid_greater_than_in_path/>")
    {:error, ">"}

Giving an existing URI simply returns it wrapped in a tuple:

    iex> {:ok, uri} = URI.new("https://elixir-lang.org/")
    iex> URI.new(uri)
    {:ok, %URI{
      fragment: nil,
      host: "elixir-lang.org",
      path: "/",
      port: 443,
      query: nil,
      scheme: "https",
      userinfo: nil
    }}

Link to this function

# new!(uri)

View Source (since 1.13.0)

    @spec new!(t() | String.t()) :: t()

Similar to `new/1` but raises `URI.Error` if an invalid string is given.

## Examples

    iex> URI.new!("https://elixir-lang.org/")
    %URI{
      fragment: nil,
      host: "elixir-lang.org",
      path: "/",
      port: 443,
      query: nil,
      scheme: "https",
      userinfo: nil
    }

    iex> URI.new!("/invalid_greater_than_in_path/>")
    ** (URI.Error) cannot parse due to reason invalid_uri: ">"

Giving an existing URI simply returns it:

    iex> uri = URI.new!("https://elixir-lang.org/")
    iex> URI.new!(uri)
    %URI{
      fragment: nil,
      host: "elixir-lang.org",
      path: "/",
      port: 443,
      query: nil,
      scheme: "https",
      userinfo: nil
    }

Link to this function

# parse(uri)

View Source

    @spec parse(t() | binary()) :: t()

Parses a URI into its components, without further validation.

This function can parse both absolute and relative URLs. You can check if a URI is absolute or relative by checking if the `scheme` field is nil or not. Furthermore, this function expects both absolute and relative URIs to be well-formed and does not perform any validation. See the "Examples" section below. Use `new/1` if you want to validate the URI fields after parsing.

When a URI is given without a port, the value returned by `URI.default_port/1` for the URI's scheme is used for the `:port` field. The scheme is also normalized to lowercase.

If a `%URI{}` struct is given to this function, this function returns it unmodified.

> #### `:authority` field

> This function sets the field `:authority` for backwards-compatibility reasons but it is deprecated.

## Examples

    iex> URI.parse("https://elixir-lang.org/")
    %URI{
      authority: "elixir-lang.org",
      fragment: nil,
      host: "elixir-lang.org",
      path: "/",
      port: 443,
      query: nil,
      scheme: "https",
      userinfo: nil
    }

    iex> URI.parse("//elixir-lang.org/")
    %URI{
      authority: "elixir-lang.org",
      fragment: nil,
      host: "elixir-lang.org",
      path: "/",
      port: nil,
      query: nil,
      scheme: nil,
      userinfo: nil
    }

    iex> URI.parse("/foo/bar")
    %URI{
      fragment: nil,
      host: nil,
      path: "/foo/bar",
      port: nil,
      query: nil,
      scheme: nil,
      userinfo: nil
    }

    iex> URI.parse("foo/bar")
    %URI{
      fragment: nil,
      host: nil,
      path: "foo/bar",
      port: nil,
      query: nil,
      scheme: nil,
      userinfo: nil
    }

In contrast to `URI.new/1`, this function will parse poorly-formed URIs, for example:

    iex> URI.parse("/invalid_greater_than_in_path/>")
    %URI{
      fragment: nil,
      host: nil,
      path: "/invalid_greater_than_in_path/>",
      port: nil,
      query: nil,
      scheme: nil,
      userinfo: nil
    }

Another example is a URI with brackets in query strings. It is accepted by `parse/1`, it is commonly accepted by browsers, but it will be refused by `new/1`:

    iex> URI.parse("/?foo[bar]=baz")
    %URI{
      fragment: nil,
      host: nil,
      path: "/",
      port: nil,
      query: "foo[bar]=baz",
      scheme: nil,
      userinfo: nil
    }

Link to this function

# query_decoder(query, encoding \\ :www_form)

View Source

    @spec query_decoder(binary(), :rfc3986 | :www_form) :: Enumerable.t()

Returns a stream of two-element tuples representing key-value pairs in the given `query`.

Key and value in each tuple will be binaries and will be percent-unescaped.

You can specify one of the following `encoding` options:

- `:www_form` - (default, since v1.12.0) keys and values are decoded as per `decode_www_form/1`. This is the format typically used by browsers on query strings and form data. It decodes "+" as " ".

- `:rfc3986` - (since v1.12.0) keys and values are decoded as per `decode/1`. The result is the same as `:www_form` except for leaving "+" as is in line with RFC 3986.

Encoding defaults to `:www_form` for backward compatibility.

## Examples

    iex> URI.query_decoder("foo=1&bar=2") |> Enum.to_list()
    [{"foo", "1"}, {"bar", "2"}]

    iex> URI.query_decoder("food=bread%26butter&drinks=tap%20water+please") |> Enum.to_list()
    [{"food", "bread&butter"}, {"drinks", "tap water please"}]

    iex> URI.query_decoder("food=bread%26butter&drinks=tap%20water+please", :rfc3986) |> Enum.to_list()
    [{"food", "bread&butter"}, {"drinks", "tap water+please"}]

Link to this function

# to_string(uri)

View Source

    @spec to_string(t()) :: binary()

Returns the string representation of the given URI struct.

## Examples

    iex> uri = URI.parse("http://google.com")
    iex> URI.to_string(uri)
    "http://google.com"

    iex> uri = URI.parse("foo://bar.baz")
    iex> URI.to_string(uri)
    "foo://bar.baz"