Module: Ruby::Rego::Builtins::Uri::Parser

Defined in:
lib/ruby/rego/builtins/uri/parser.rb,
lib/ruby/rego/builtins/uri/parser/host.rb,
lib/ruby/rego/builtins/uri/parser/escaping.rb,
lib/ruby/rego/builtins/uri/parser/serialize.rb

Overview

Port of Go’s url.URL.String / EscapedPath / EscapedFragment / Userinfo.String (src/net/url/url.go): re-serialize parsed Components back to a URL string. crypto.x509’s URIStrings field is exactly this serialization of each URI SAN. Lives apart from the parse driver so parser.rb stays under RubyCritic’s complexity budget; reopens Parser so the mode constants and escape/unescape resolve via lexical scope.

Defined Under Namespace

Classes: Components

Constant Summary collapse

PATH =

unescape/escape modes (Go’s encoding enum); only the ones the parse path uses.

:path
HOST =
:host
ZONE =
:zone
USER_PASSWORD =
:user_password
FRAGMENT =
:fragment
USERINFO_PUNCT =

The non-alphanumeric bytes RFC 3986 §3.2.1 allows in userinfo (sub-delims + “:~%@.-_”).

"-._:~!$&'()*+,;=%@".bytes.freeze
UPPERHEX =
"0123456789ABCDEF"
HOST_SUBDELIMS =

§host sub-delims plus the extras Go allows unescaped in a host/zone.

"!$&'()*+,;=:[]<>\"".bytes.freeze
FRAGMENT_SUBDELIMS =

Fragment sub-delims Go leaves unescaped.

"!()*".bytes.freeze
VALID_ENCODED_ALLOW =

Port of net/url validEncoded: every byte is either in the explicit allowlist (sub-delims plus : @ [ ] %, matching Go’s char-literal switch) or one the given mode would not escape.

"!$&'()*+,;=:@[]%".bytes.freeze

Class Method Summary collapse

Class Method Details

.escaped_path(components) ⇒ Object

Port of EscapedPath: prefer RawPath when it is a VALID encoding of Path, else default-escape Path. “Valid” means both validEncoded(RawPath) (no byte the path mode would escape, outside the allowlist) AND that it decodes back to Path — so a RawPath carrying a raw must-escape byte (a space, a rune >= 0x80, " < > \ ^ \ { | }`) is re-escaped, not preserved. Public: the canonical-URI form reused by providers.aws.sign_req’s SigV4 canonical request.



74
75
76
77
78
79
80
81
# File 'lib/ruby/rego/builtins/uri/parser/serialize.rb', line 74

def self.escaped_path(components)
  raw = components.raw_path.to_s
  path = components.path.to_s
  return raw if !raw.empty? && valid_encoded?(raw, PATH) && unescape(raw, PATH) == path
  return "*" if path == "*"

  escape(path, PATH)
end

.hostname(host) ⇒ Object

Port of Hostname(): the host part of host[:port], stripping IPv6 brackets.



105
106
107
# File 'lib/ruby/rego/builtins/uri/parser/host.rb', line 105

def self.hostname(host)
  split_host_port(host).first
end

.parse(raw) ⇒ Components?

Parse a raw URI, returning Components or nil when Go’s url.Parse would error. :reek:TooManyStatements – a faithful single-function port of Go’s Parse() (fragment split).

Parameters:

  • raw (String)

Returns:



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/ruby/rego/builtins/uri/parser.rb', line 37

def self.parse(raw)
  base, _, frag = raw.partition("#")
  url = parse_url(base, via_request: false)
  return nil unless url
  return url if frag.empty? && !raw.include?("#")

  decoded = unescape(frag, FRAGMENT)
  return nil unless decoded

  url.fragment = decoded
  # RawFragment keeps the original encoding only when the default re-escape differs (Go's
  # setFragment invariant, mirroring setPath's RawPath rule).
  url.raw_fragment = escape(decoded, FRAGMENT) == frag ? nil : frag
  url
end

.port(host) ⇒ Object

Port of Port(): the numeric port of host[:port], or “”.



110
111
112
# File 'lib/ruby/rego/builtins/uri/parser/host.rb', line 110

def self.port(host)
  split_host_port(host).last
end

.string(components) ⇒ String

rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength :reek:TooManyStatements – a faithful single-function port of url.URL.String().

Parameters:

Returns:

  • (String)


17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/ruby/rego/builtins/uri/parser/serialize.rb', line 17

def self.string(components)
  buf = +""
  scheme = components.scheme.to_s
  buf << scheme << ":" unless scheme.empty?
  opaque = components.opaque.to_s
  return finish_query_fragment(buf << opaque, components) unless opaque.empty?

  write_authority(buf, components)
  path = escaped_path(components)
  buf << "/" if !path.empty? && path[0] != "/" && !components.host.to_s.empty?
  buf << "./" if buf.empty? && path.split("/", 2).first.to_s.include?(":")
  buf << path
  finish_query_fragment(buf, components)
end