Module: Dommy::Rack::Url
- Defined in:
- lib/dommy/rack/url.rb
Overview
IRI → URI normalization. Links and redirect Locations on real pages
often carry raw, unescaped UTF-8 (e.g. https://note.com/hashtag/応援).
Ruby's stdlib URI parser is ASCII-only and raises URI::InvalidURIError
on such a string, which would crash navigation / cookie matching. A
browser percent-encodes the non-ASCII bytes (UTF-8) before parsing; this
does the same.
Class Method Summary collapse
-
.encode_iri(url) ⇒ Object
Percent-encode the non-ASCII characters in
urlso the ASCII-only URI parser accepts it. -
.escape_non_ascii(str) ⇒ Object
Escape every non-ASCII byte as %XX (UTF-8), so a multibyte character becomes its sequence of percent-encoded bytes (応 → %E5%BF%9C).
-
.http_host(uri) ⇒ Object
hostorhost:port, omitting a default port (theHostheader / tuple-origin serialization rule shared by HTTP and WebSocket). -
.origin(uri) ⇒ Object
scheme://host[:port], the tuple origin foruri(used for theOriginheader a same-origin WebSocket connection presents). -
.split_authority(str) ⇒ Object
Split
scheme://authority(left intact) from the path/query/fragment remainder.
Class Method Details
.encode_iri(url) ⇒ Object
Percent-encode the non-ASCII characters in url so the ASCII-only URI
parser accepts it. Already-encoded %XX and every ASCII character
(including reserved / sub-delims and % itself) are left untouched, so
the result is idempotent. The authority (host) is left alone: a
non-ASCII host needs IDNA/Punycode, which is a separate concern — only
the path / query / fragment bytes are escaped.
22 23 24 25 26 27 28 |
# File 'lib/dommy/rack/url.rb', line 22 def encode_iri(url) str = url.to_s return str if str.ascii_only? prefix, rest = (str) prefix + escape_non_ascii(rest) end |
.escape_non_ascii(str) ⇒ Object
Escape every non-ASCII byte as %XX (UTF-8), so a multibyte character becomes its sequence of percent-encoded bytes (応 → %E5%BF%9C).
43 44 45 |
# File 'lib/dommy/rack/url.rb', line 43 def escape_non_ascii(str) str.b.gsub(/[^\x00-\x7F]/n) { |byte| format("%%%02X", byte.unpack1("C")) } end |
.http_host(uri) ⇒ Object
host or host:port, omitting a default port (the Host header /
tuple-origin serialization rule shared by HTTP and WebSocket).
49 50 51 |
# File 'lib/dommy/rack/url.rb', line 49 def http_host(uri) uri.port == uri.default_port ? uri.host : "#{uri.host}:#{uri.port}" end |
.origin(uri) ⇒ Object
scheme://host[:port], the tuple origin for uri (used for the
Origin header a same-origin WebSocket connection presents).
55 56 57 |
# File 'lib/dommy/rack/url.rb', line 55 def origin(uri) "#{uri.scheme}://#{http_host(uri)}" end |
.split_authority(str) ⇒ Object
Split scheme://authority (left intact) from the path/query/fragment
remainder. A string with no scheme://authority (a relative ref like
/hashtag/応援) yields an empty prefix and is escaped whole.
33 34 35 36 37 38 39 |
# File 'lib/dommy/rack/url.rb', line 33 def (str) if (m = str.match(%r{\A([a-zA-Z][a-zA-Z0-9+.\-]*://[^/?#]*)(.*)\z}m)) [m[1], m[2]] else ["", str] end end |