http_gun/destination

Decides which hosts, ports and network addresses a client may reach.

A Policy admits public addresses by default. The three setups that tests and local services need are each one line:

// Public addresses and loopback.
config.default() |> config.allow_loopback
// Loopback only.
config.default() |> config.with_destination(destination.loopback_only())
// Loopback, pinned to one local server.
config.default()
|> config.with_destination(
  destination.loopback_only() |> destination.only_hosts(["127.0.0.1:8080"]),
)

allow_private admits private networks. Reserved addresses, including cloud metadata addresses, are always refused. Every address that a host name resolves to is checked before a connection opens, and the connection uses that checked address.

with_plaintext decides whether http:// is admitted. The default allows it; PlaintextToLoopbackOnly admits it only when every resolved address is loopback, so http://localhost passes and http:// to anywhere else fails, while https:// is unaffected:

destination.default()
|> destination.allow_loopback
|> destination.with_plaintext(destination.PlaintextToLoopbackOnly)

http_gun.with_destination narrows the policy for one client view: a request must satisfy both the client’s policy and every policy its view adds, so a view can never widen what the client admits. check applies a policy to a host and port without resolving names, for an application that validates URLs itself.

Types

An IP address. parse_address reads one from text.

pub type Address {
  Ipv4(Int, Int, Int, Int)
  Ipv6(Int, Int, Int, Int, Int, Int, Int, Int)
}

Constructors

  • Ipv4(Int, Int, Int, Int)
  • Ipv6(Int, Int, Int, Int, Int, Int, Int, Int)

The class of an address, as classify reports it.

pub type Class {
  Public
  Loopback
  Private
  Reserved
}

Constructors

  • Public
  • Loopback
  • Private
  • Reserved

Whether a policy admits plaintext http://. https:// is never affected.

pub type Plaintext {
  AllowPlaintext
  PlaintextToLoopbackOnly
  RequireTls
}

Constructors

  • AllowPlaintext

    Admit http:// to every address the policy admits. The default.

  • PlaintextToLoopbackOnly

    Admit http:// only when every resolved address is loopback.

  • RequireTls

    Refuse every http:// request.

Which destinations a client admits. Build one with default or loopback_only and the functions below.

pub opaque type Policy

Why a policy refused a destination.

pub type Rejection {
  HostNotAllowed
  AddressRefused(Class)
  PlaintextRefused(Class)
}

Constructors

  • HostNotAllowed

    The host, or its port, is not in the only_hosts list.

  • AddressRefused(Class)

    The host is, or resolved to, an address whose class the policy refuses.

  • PlaintextRefused(Class)

    The policy’s plaintext rule refuses http:// to an address of this class.

Values

pub fn allow_loopback(policy: Policy) -> Policy

Also admit loopback addresses.

pub fn allow_private(policy: Policy) -> Policy

Also admit private network addresses: 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 100.64.0.0/10 and fc00::/7.

pub fn check(
  policy: Policy,
  host: String,
  port: Int,
) -> Result(Nil, Rejection)

Check a host and port against the policy without resolving names. A host that is an IP literal (with or without brackets) is also classified; a host name passes when the host list admits it, and its resolved addresses are checked again when a client connects.

pub fn check_address(
  policy: Policy,
  address: Address,
) -> Result(Nil, Rejection)

Check one resolved address against the policy’s address classes.

pub fn check_plaintext(
  policy: Policy,
  address: Address,
) -> Result(Nil, Rejection)

Check a plaintext http:// connection to one resolved address against the policy’s plaintext rule. The address classes are checked separately by check_address.

pub fn classify(address: Address) -> Class

Classify an address. Invalid components fail closed as Reserved. IPv4-mapped and well-known NAT64 IPv6 addresses inherit the embedded IPv4 classification.

pub fn default() -> Policy

Public addresses only.

pub fn loopback_only() -> Policy

Loopback addresses only (127.0.0.0/8 and ::1): public, private and reserved addresses are refused.

pub fn narrows(policy: Policy, within client: Policy) -> Bool

Whether policy, added by a client view, narrows client, the client’s policy: it has an only_hosts list, or it refuses an address class that client admits. The plaintext rule never counts. A client configured with config.require_view_destination admits a view only when one of its policies narrows the client’s policy this way.

let client = destination.default() |> destination.allow_loopback
destination.narrows(destination.loopback_only(), within: client)  // True
destination.default()
|> destination.allow_loopback
|> destination.with_plaintext(destination.RequireTls)
|> destination.narrows(within: client)                            // False
pub fn only_hosts(policy: Policy, hosts: List(String)) -> Policy

Admit only these hosts, compared without case. An entry is "host", which admits every port, or "host:port", which admits one port; write an IPv6 address with a port as "[::1]:8080". Address classes still apply, so loopback_only() |> only_hosts(["127.0.0.1:8080"]) admits exactly one local server. Calling it again replaces the list.

pub fn parse_address(text: String) -> Result(Address, Nil)

Parse an IPv4 or IPv6 literal, as "127.0.0.1" or "::1".

pub fn validate(policy: Policy) -> Result(Policy, String)

Return the first malformed only_hosts entry: empty, containing whitespace, /, ?, # or @, or with a port outside 1..65535. A malformed entry never matches a request; config.validate and http_gun.start report it.

pub fn with_plaintext(
  policy: Policy,
  plaintext: Plaintext,
) -> Policy

Choose whether http:// is admitted. The rule is decided against each resolved address, like the address classes, including the addresses of a pooled connection a request would reuse; a refused request fails with PlaintextRefused and NotSent. A view’s policy can tighten the rule and never loosen it, since every policy must admit the request.

✨ Search Document