Skip to content

Commit d543c0d

Browse files
committed
Escape reserved characters in scheme name
Fix #89
1 parent d08f0ef commit d543c0d

File tree

2 files changed

+47
-18
lines changed

2 files changed

+47
-18
lines changed

lib/uri/common.rb

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,38 @@ def make_components_hash(klass, array_hash)
9292
end
9393

9494
module Schemes # :nodoc:
95+
class << self
96+
ReservedChars = ".+-"
97+
EscapedChars = "\uFE52\uFE62\uFE63"
98+
99+
def escape(name)
100+
unless name and name.ascii_only?
101+
return nil
102+
end
103+
name.upcase.tr(ReservedChars, EscapedChars)
104+
end
105+
106+
def unescape(name)
107+
name.tr(EscapedChars, ReservedChars).encode(Encoding::US_ASCII).upcase
108+
end
109+
110+
def find(name)
111+
const_get(name, false) if name and const_defined?(name, false)
112+
end
113+
114+
def register(name, klass)
115+
unless scheme = escape(name)
116+
raise ArgumentError, "invalid characater as scheme - #{name}"
117+
end
118+
const_set(scheme, klass)
119+
end
120+
121+
def list
122+
constants.map { |name|
123+
[unescape(name.to_s), const_get(name)]
124+
}.to_h
125+
end
126+
end
95127
end
96128
private_constant :Schemes
97129

@@ -104,7 +136,7 @@ module Schemes # :nodoc:
104136
# Note that after calling String#upcase on +scheme+, it must be a valid
105137
# constant name.
106138
def self.register_scheme(scheme, klass)
107-
Schemes.const_set(scheme.to_s.upcase, klass)
139+
Schemes.register(scheme, klass)
108140
end
109141

110142
# Returns a hash of the defined schemes:
@@ -122,9 +154,7 @@ def self.register_scheme(scheme, klass)
122154
#
123155
# Related: URI.register_scheme.
124156
def self.scheme_list
125-
Schemes.constants.map { |name|
126-
[name.to_s.upcase, Schemes.const_get(name)]
127-
}.to_h
157+
Schemes.list
128158
end
129159

130160
INITIAL_SCHEMES = scheme_list
@@ -148,12 +178,10 @@ def self.scheme_list
148178
# # => #<URI::HTTP foo://[email protected]:123/forum/questions/?tag=networking&order=newest#top>
149179
#
150180
def self.for(scheme, *arguments, default: Generic)
151-
const_name = scheme.to_s.upcase
181+
const_name = Schemes.escape(scheme)
152182

153183
uri_class = INITIAL_SCHEMES[const_name]
154-
uri_class ||= if /\A[A-Z]\w*\z/.match?(const_name) && Schemes.const_defined?(const_name, false)
155-
Schemes.const_get(const_name, false)
156-
end
184+
uri_class ||= Schemes.find(const_name)
157185
uri_class ||= default
158186

159187
return uri_class.new(scheme, *arguments)

test/uri/test_common.rb

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -113,17 +113,18 @@ def test_register_scheme_lowercase
113113

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

121-
ms_search_class = Class.new(URI::Generic)
122-
URI.register_scheme 'MS_SEARCH', ms_search_class
123-
begin
124-
assert_equal URI::Generic, URI.parse('ms-search://localhost').class
125-
ensure
126-
URI.const_get(:Schemes).send(:remove_const, :MS_SEARCH)
121+
list.each do |scheme, uri_class|
122+
assert_equal uri_class, URI.parse("#{scheme}://localhost").class
123+
end
124+
ensure
125+
schemes = URI.const_get(:Schemes)
126+
list.each do |scheme, |
127+
schemes.send(:remove_const, schemes.escape(scheme))
127128
end
128129
end
129130

0 commit comments

Comments
 (0)