Skip to content

Commit

Permalink
Escape reserved characters in scheme name
Browse files Browse the repository at this point in the history
  • Loading branch information
nobu committed Jan 23, 2025
1 parent 24cf280 commit b92e44d
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 19 deletions.
46 changes: 37 additions & 9 deletions lib/uri/common.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ module URI
DEFAULT_PARSER = RFC3986_PARSER
Ractor.make_shareable(DEFAULT_PARSER) if defined?(Ractor)

def self.parser=(parser = RFC3986_PARSER)
def self.parser=(parser)
remove_const(:Parser) if defined?(::URI::Parser)
const_set("Parser", parser.class)

Expand Down Expand Up @@ -88,6 +88,38 @@ def make_components_hash(klass, array_hash)
end

module Schemes
class << self
ReservedChars = ".+-"
EscapedChars = "\uFE52\uFE62\uFE63"

def escape(name)
unless name and name.ascii_only?
return nil
end
name.upcase.tr(ReservedChars, EscapedChars)
end

def unescape(name)
name.tr(EscapedChars, ReservedChars).encode(Encoding::US_ASCII).upcase
end

def find(name)
const_get(name, false) if name and const_defined?(name, false)
end

def register(name, klass)
unless scheme = escape(name)
raise ArgumentError, "invalid characater as scheme - #{name}"
end
const_set(scheme, klass)
end

def list
constants.map { |name|
[unescape(name.to_s), const_get(name)]
}.to_h
end
end
end
private_constant :Schemes

Expand All @@ -100,7 +132,7 @@ module Schemes
# Note that after calling String#upcase on +scheme+, it must be a valid
# constant name.
def self.register_scheme(scheme, klass)
Schemes.const_set(scheme.to_s.upcase, klass)
Schemes.register(scheme, klass)
end

# Returns a hash of the defined schemes:
Expand All @@ -118,9 +150,7 @@ def self.register_scheme(scheme, klass)
#
# Related: URI.register_scheme.
def self.scheme_list
Schemes.constants.map { |name|
[name.to_s.upcase, Schemes.const_get(name)]
}.to_h
Schemes.list
end

INITIAL_SCHEMES = scheme_list
Expand All @@ -144,12 +174,10 @@ def self.scheme_list
# # => #<URI::HTTP foo://john.doe@www.example.com:123/forum/questions/?tag=networking&order=newest#top>
#
def self.for(scheme, *arguments, default: Generic)
const_name = scheme.to_s.upcase
const_name = Schemes.escape(scheme)

uri_class = INITIAL_SCHEMES[const_name]
uri_class ||= if /\A[A-Z]\w*\z/.match?(const_name) && Schemes.const_defined?(const_name, false)
Schemes.const_get(const_name, false)
end
uri_class ||= Schemes.find(const_name)
uri_class ||= default

return uri_class.new(scheme, *arguments)
Expand Down
21 changes: 11 additions & 10 deletions test/uri/test_common.rb
Original file line number Diff line number Diff line change
Expand Up @@ -113,17 +113,18 @@ def test_register_scheme_lowercase

def test_register_scheme_with_symbols
# Valid schemes from https://www.iana.org/assignments/uri-schemes/uri-schemes.xhtml
some_uri_class = Class.new(URI::Generic)
assert_raise(NameError) { URI.register_scheme 'ms-search', some_uri_class }
assert_raise(NameError) { URI.register_scheme 'microsoft.windows.camera', some_uri_class }
assert_raise(NameError) { URI.register_scheme 'coaps+ws', some_uri_class }
list = []
%w[ms-search microsoft.windows.camera coaps+ws].each {|name|
list << [name, URI.register_scheme(name, Class.new(URI::Generic))]

Check failure on line 118 in test/uri/test_common.rb

View workflow job for this annotation

GitHub Actions / build (truffleruby / ubuntu-latest)

Error

NameError: wrong constant name MS﹣SEARCH /home/runner/work/uri/uri/lib/uri/common.rb:114:in `const_set' /home/runner/work/uri/uri/lib/uri/common.rb:114:in `register' /home/runner/work/uri/uri/lib/uri/common.rb:135:in `register_scheme' /home/runner/work/uri/uri/test/uri/test_common.rb:118:in `block in test_register_scheme_with_symbols' /home/runner/work/uri/uri/test/uri/test_common.rb:117:in `each' /home/runner/work/uri/uri/test/uri/test_common.rb:117:in `test_register_scheme_with_symbols' <internal:core> core/throw_catch.rb:36:in `catch' <internal:core> core/throw_catch.rb:36:in `catch'

Check failure on line 118 in test/uri/test_common.rb

View workflow job for this annotation

GitHub Actions / build (truffleruby-head / ubuntu-latest)

Error

NameError: wrong constant name MS﹣SEARCH /home/runner/work/uri/uri/lib/uri/common.rb:114:in `const_set' /home/runner/work/uri/uri/lib/uri/common.rb:114:in `register' /home/runner/work/uri/uri/lib/uri/common.rb:135:in `register_scheme' /home/runner/work/uri/uri/test/uri/test_common.rb:118:in `block in test_register_scheme_with_symbols' /home/runner/work/uri/uri/test/uri/test_common.rb:117:in `each' /home/runner/work/uri/uri/test/uri/test_common.rb:117:in `test_register_scheme_with_symbols' <internal:core> core/throw_catch.rb:36:in `catch' <internal:core> core/throw_catch.rb:36:in `catch'
}

ms_search_class = Class.new(URI::Generic)
URI.register_scheme 'MS_SEARCH', ms_search_class
begin
assert_equal URI::Generic, URI.parse('ms-search://localhost').class
ensure
URI.const_get(:Schemes).send(:remove_const, :MS_SEARCH)
list.each do |scheme, uri_class|
assert_equal uri_class, URI.parse("#{scheme}://localhost").class
end
ensure
schemes = URI.const_get(:Schemes)
list.each do |scheme, |
schemes.send(:remove_const, schemes.escape(scheme))
end
end

Expand Down

0 comments on commit b92e44d

Please sign in to comment.