Validates origins against sites stored in ETS for WebSocket connections and CORS, and reads the
host or the address out of what a request or a setting carries (host_name/1, web_url?/1,
site_path?/1) with URI.parse/1 rather than by cutting strings.
Origins
A CORS origin is a scheme, a host and a port, held as origin/0. request_origin/1 reads the
one a request's Origin header names and site_origins/1 lists the ones a site allows; both go
through the same reading, so the two compare as equal or not at all. serialize/1 writes one back
the way a browser writes it.
Summary
Types
{scheme, host, port}: the scheme "http" or "https", the host lowercased and without IPv6
brackets, and the port always written — https://Site.com is {"https", "site.com", 443}.
Functions
The host a Host header names, without its port: "example.com:4000" is "example.com" and
"[::1]:4000" is "::1". nil for a header that names none.
Whether host, already read out of an address and lowercased, names this machine: localhost, a
name under .localhost (RFC 6761), 127.0.0.1 or ::1. Usable in a guard.
The origin a request's Origin header names, or :error.
origin written the way a browser writes it in an Origin header: no port when it is the
scheme's own, and an IPv6 host in brackets.
Every origin site allows, read from its :host, :frontend_domain and :allowed_origins
Whether value is a path on this site ("/uploads/a.png"): one leading slash, so neither a
protocol-relative address ("//host/a") nor a backslash form a browser reads as one.
Whether value is an absolute http or https address with a host. A bare domain such as
"httpbin.example.com" is not one, whatever its first letters are.
Types
Functions
The host a Host header names, without its port: "example.com:4000" is "example.com" and
"[::1]:4000" is "::1". nil for a header that names none.
Whether host, already read out of an address and lowercased, names this machine: localhost, a
name under .localhost (RFC 6761), 127.0.0.1 or ::1. Usable in a guard.
is_loopback_host("site-a.localhost") #=> true
is_loopback_host("evil-localhost.com") #=> false
is_loopback_host("localhost.evil.com") #=> false
The origin a request's Origin header names, or :error.
Only a bare origin is one: an http or https scheme, a host and an optional port from 1 to
65535, with no path, query, fragment or credentials. null, a malformed value and a port that is
not a number are :error.
request_origin("HTTPS://Site.com") #=> {:ok, {"https", "site.com", 443}}
request_origin("http://[::1]:3000") #=> {:ok, {"http", "::1", 3000}}
request_origin("https://site.com/path") #=> :error
request_origin("null") #=> :error
origin written the way a browser writes it in an Origin header: no port when it is the
scheme's own, and an IPv6 host in brackets.
serialize({"https", "site.com", 443}) #=> "https://site.com"
serialize({"http", "::1", 3000}) #=> "http://[::1]:3000"
Every origin site allows, read from its :host, :frontend_domain and :allowed_origins:
hostis a domain, allowed ashttps://<host>on port 443. A loopback host (seeis_loopback_host/1) is also allowed ashttp://<host>on port 80.frontend_domainwritten as an origin (https://app.example.com:8443) is that origin. Written as a bare domain (app.example.com,app.example.com:3000) it is read ashostis, on the port it writes.- each entry of
allowed_originsis the origin it writes, on the scheme's own port when it writes none. What follows the host and port is ignored, sohttps://partner.com/ishttps://partner.com.
A value that is none of these allows nothing.
Whether value is a path on this site ("/uploads/a.png"): one leading slash, so neither a
protocol-relative address ("//host/a") nor a backslash form a browser reads as one.
Whether value is an absolute http or https address with a host. A bare domain such as
"httpbin.example.com" is not one, whatever its first letters are.