summaryrefslogtreecommitdiff
path: root/lib/uri
diff options
context:
space:
mode:
Diffstat (limited to 'lib/uri')
-rw-r--r--lib/uri/common.rb618
-rw-r--r--lib/uri/file.rb14
-rw-r--r--lib/uri/ftp.rb6
-rw-r--r--lib/uri/generic.rb161
-rw-r--r--lib/uri/http.rb55
-rw-r--r--lib/uri/https.rb4
-rw-r--r--lib/uri/ldap.rb4
-rw-r--r--lib/uri/ldaps.rb3
-rw-r--r--lib/uri/mailto.rb5
-rw-r--r--lib/uri/rfc2396_parser.rb71
-rw-r--r--lib/uri/rfc3986_parser.rb181
-rw-r--r--lib/uri/uri.gemspec42
-rw-r--r--lib/uri/version.rb6
-rw-r--r--lib/uri/ws.rb83
-rw-r--r--lib/uri/wss.rb23
15 files changed, 885 insertions, 391 deletions
diff --git a/lib/uri/common.rb b/lib/uri/common.rb
index b886923c9e..0b3bb4f099 100644
--- a/lib/uri/common.rb
+++ b/lib/uri/common.rb
@@ -3,7 +3,6 @@
# = uri/common.rb
#
# Author:: Akira Yamada <akira@ruby-lang.org>
-# Revision:: $Id$
# License::
# You can redistribute it and/or modify it under the same term as Ruby.
#
@@ -14,19 +13,53 @@ require_relative "rfc2396_parser"
require_relative "rfc3986_parser"
module URI
- REGEXP = RFC2396_REGEXP
- Parser = RFC2396_Parser
+ # The default parser instance for RFC 2396.
+ RFC2396_PARSER = RFC2396_Parser.new
+ Ractor.make_shareable(RFC2396_PARSER) if defined?(Ractor)
+
+ # The default parser instance for RFC 3986.
RFC3986_PARSER = RFC3986_Parser.new
+ Ractor.make_shareable(RFC3986_PARSER) if defined?(Ractor)
+
+ # The default parser instance.
+ DEFAULT_PARSER = RFC3986_PARSER
+ Ractor.make_shareable(DEFAULT_PARSER) if defined?(Ractor)
+
+ # Set the default parser instance.
+ def self.parser=(parser = RFC3986_PARSER)
+ remove_const(:Parser) if defined?(::URI::Parser)
+ const_set("Parser", parser.class)
- # URI::Parser.new
- DEFAULT_PARSER = Parser.new
- DEFAULT_PARSER.pattern.each_pair do |sym, str|
- unless REGEXP::PATTERN.const_defined?(sym)
- REGEXP::PATTERN.const_set(sym, str)
+ remove_const(:PARSER) if defined?(::URI::PARSER)
+ const_set("PARSER", parser)
+
+ remove_const(:REGEXP) if defined?(::URI::REGEXP)
+ remove_const(:PATTERN) if defined?(::URI::PATTERN)
+ if Parser == RFC2396_Parser
+ const_set("REGEXP", URI::RFC2396_REGEXP)
+ const_set("PATTERN", URI::RFC2396_REGEXP::PATTERN)
+ end
+
+ Parser.new.regexp.each_pair do |sym, str|
+ remove_const(sym) if const_defined?(sym, false)
+ const_set(sym, str)
end
end
- DEFAULT_PARSER.regexp.each_pair do |sym, str|
- const_set(sym, str)
+ self.parser = RFC3986_PARSER
+
+ def self.const_missing(const) # :nodoc:
+ if const == :REGEXP
+ warn "URI::REGEXP is obsolete. Use URI::RFC2396_REGEXP explicitly.", uplevel: 1 if $VERBOSE
+ URI::RFC2396_REGEXP
+ elsif value = RFC2396_PARSER.regexp[const]
+ warn "URI::#{const} is obsolete. Use URI::RFC2396_PARSER.regexp[#{const.inspect}] explicitly.", uplevel: 1 if $VERBOSE
+ value
+ elsif value = RFC2396_Parser.const_get(const)
+ warn "URI::#{const} is obsolete. Use URI::RFC2396_Parser::#{const} explicitly.", uplevel: 1 if $VERBOSE
+ value
+ else
+ super
+ end
end
module Util # :nodoc:
@@ -61,88 +94,104 @@ module URI
module_function :make_components_hash
end
- # Module for escaping unsafe characters with codes.
- module Escape
- #
- # == Synopsis
- #
- # URI.escape(str [, unsafe])
- #
- # == Args
- #
- # +str+::
- # String to replaces in.
- # +unsafe+::
- # Regexp that matches all symbols that must be replaced with codes.
- # By default uses <tt>UNSAFE</tt>.
- # When this argument is a String, it represents a character set.
- #
- # == Description
- #
- # Escapes the string, replacing all unsafe characters with codes.
- #
- # This method is obsolete and should not be used. Instead, use
- # CGI.escape, URI.encode_www_form or URI.encode_www_form_component
- # depending on your specific use case.
- #
- # == Usage
- #
- # require 'uri'
- #
- # enc_uri = URI.escape("http://example.com/?a=\11\15")
- # # => "http://example.com/?a=%09%0D"
- #
- # URI.unescape(enc_uri)
- # # => "http://example.com/?a=\t\r"
- #
- # URI.escape("@?@!", "!?")
- # # => "@%3F@%21"
- #
- def escape(*arg)
- warn "URI.escape is obsolete", uplevel: 1
- DEFAULT_PARSER.escape(*arg)
- end
- alias encode escape
- #
- # == Synopsis
- #
- # URI.unescape(str)
- #
- # == Args
- #
- # +str+::
- # String to unescape.
- #
- # == Description
- #
- # This method is obsolete and should not be used. Instead, use
- # CGI.unescape, URI.decode_www_form or URI.decode_www_form_component
- # depending on your specific use case.
- #
- # == Usage
- #
- # require 'uri'
- #
- # enc_uri = URI.escape("http://example.com/?a=\11\15")
- # # => "http://example.com/?a=%09%0D"
- #
- # URI.unescape(enc_uri)
- # # => "http://example.com/?a=\t\r"
- #
- def unescape(*arg)
- warn "URI.unescape is obsolete", uplevel: 1
- DEFAULT_PARSER.unescape(*arg)
+ module Schemes # :nodoc:
+ class << self
+ ReservedChars = ".+-"
+ EscapedChars = "\u01C0\u01C1\u01C2"
+ # Use Lo category chars as escaped chars for TruffleRuby, which
+ # does not allow Symbol categories as identifiers.
+
+ 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 character 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
- alias decode unescape
- end # module Escape
+ end
+ private_constant :Schemes
- extend Escape
- include REGEXP
+ # Registers the given +klass+ as the class to be instantiated
+ # when parsing a \URI with the given +scheme+:
+ #
+ # URI.register_scheme('MS_SEARCH', URI::Generic) # => URI::Generic
+ # URI.scheme_list['MS_SEARCH'] # => URI::Generic
+ #
+ # Note that after calling String#upcase on +scheme+, it must be a valid
+ # constant name.
+ def self.register_scheme(scheme, klass)
+ Schemes.register(scheme, klass)
+ end
- @@schemes = {}
- # Returns a Hash of the defined schemes.
+ # Returns a hash of the defined schemes:
+ #
+ # URI.scheme_list
+ # # =>
+ # {"MAILTO"=>URI::MailTo,
+ # "LDAPS"=>URI::LDAPS,
+ # "WS"=>URI::WS,
+ # "HTTP"=>URI::HTTP,
+ # "HTTPS"=>URI::HTTPS,
+ # "LDAP"=>URI::LDAP,
+ # "FILE"=>URI::File,
+ # "FTP"=>URI::FTP}
+ #
+ # Related: URI.register_scheme.
def self.scheme_list
- @@schemes
+ Schemes.list
+ end
+
+ # :stopdoc:
+ INITIAL_SCHEMES = scheme_list
+ private_constant :INITIAL_SCHEMES
+ Ractor.make_shareable(INITIAL_SCHEMES) if defined?(Ractor)
+ # :startdoc:
+
+ # Returns a new object constructed from the given +scheme+, +arguments+,
+ # and +default+:
+ #
+ # - The new object is an instance of <tt>URI.scheme_list[scheme.upcase]</tt>.
+ # - The object is initialized by calling the class initializer
+ # using +scheme+ and +arguments+.
+ # See URI::Generic.new.
+ #
+ # Examples:
+ #
+ # values = ['john.doe', 'www.example.com', '123', nil, '/forum/questions/', nil, 'tag=networking&order=newest', 'top']
+ # URI.for('https', *values)
+ # # => #<URI::HTTPS https://john.doe@www.example.com:123/forum/questions/?tag=networking&order=newest#top>
+ # URI.for('foo', *values, default: URI::HTTP)
+ # # => #<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 = Schemes.escape(scheme)
+
+ uri_class = INITIAL_SCHEMES[const_name]
+ uri_class ||= Schemes.find(const_name)
+ uri_class ||= default
+
+ return uri_class.new(scheme, *arguments)
end
#
@@ -162,95 +211,49 @@ module URI
#
class BadURIError < Error; end
- #
- # == Synopsis
- #
- # URI::split(uri)
- #
- # == Args
- #
- # +uri+::
- # String with URI.
- #
- # == Description
- #
- # Splits the string on following parts and returns array with result:
- #
- # * Scheme
- # * Userinfo
- # * Host
- # * Port
- # * Registry
- # * Path
- # * Opaque
- # * Query
- # * Fragment
- #
- # == Usage
- #
- # require 'uri'
- #
- # URI.split("http://www.ruby-lang.org/")
- # # => ["http", nil, "www.ruby-lang.org", nil, nil, "/", nil, nil, nil]
+ # Returns a 9-element array representing the parts of the \URI
+ # formed from the string +uri+;
+ # each array element is a string or +nil+:
+ #
+ # names = %w[scheme userinfo host port registry path opaque query fragment]
+ # values = URI.split('https://john.doe@www.example.com:123/forum/questions/?tag=networking&order=newest#top')
+ # names.zip(values)
+ # # =>
+ # [["scheme", "https"],
+ # ["userinfo", "john.doe"],
+ # ["host", "www.example.com"],
+ # ["port", "123"],
+ # ["registry", nil],
+ # ["path", "/forum/questions/"],
+ # ["opaque", nil],
+ # ["query", "tag=networking&order=newest"],
+ # ["fragment", "top"]]
#
def self.split(uri)
- RFC3986_PARSER.split(uri)
+ PARSER.split(uri)
end
+ # Returns a new \URI object constructed from the given string +uri+:
#
- # == Synopsis
- #
- # URI::parse(uri_str)
- #
- # == Args
- #
- # +uri_str+::
- # String with URI.
- #
- # == Description
- #
- # Creates one of the URI's subclasses instance from the string.
- #
- # == Raises
- #
- # URI::InvalidURIError::
- # Raised if URI given is not a correct one.
- #
- # == Usage
- #
- # require 'uri'
- #
- # uri = URI.parse("http://www.ruby-lang.org/")
- # # => #<URI::HTTP http://www.ruby-lang.org/>
- # uri.scheme
- # # => "http"
- # uri.host
- # # => "www.ruby-lang.org"
+ # URI.parse('https://john.doe@www.example.com:123/forum/questions/?tag=networking&order=newest#top')
+ # # => #<URI::HTTPS https://john.doe@www.example.com:123/forum/questions/?tag=networking&order=newest#top>
+ # URI.parse('http://john.doe@www.example.com:123/forum/questions/?tag=networking&order=newest#top')
+ # # => #<URI::HTTP http://john.doe@www.example.com:123/forum/questions/?tag=networking&order=newest#top>
#
- # It's recommended to first ::escape the provided +uri_str+ if there are any
- # invalid URI characters.
+ # It's recommended to first URI::RFC2396_PARSER.escape string +uri+
+ # if it may contain invalid URI characters.
#
def self.parse(uri)
- RFC3986_PARSER.parse(uri)
+ PARSER.parse(uri)
end
+ # Merges the given URI strings +str+
+ # per {RFC 2396}[https://www.rfc-editor.org/rfc/rfc2396.html].
#
- # == Synopsis
- #
- # URI::join(str[, str, ...])
- #
- # == Args
- #
- # +str+::
- # String(s) to work with, will be converted to RFC3986 URIs before merging.
- #
- # == Description
- #
- # Joins URIs.
+ # Each string in +str+ is converted to an
+ # {RFC3986 URI}[https://www.rfc-editor.org/rfc/rfc3986.html] before being merged.
#
- # == Usage
- #
- # require 'uri'
+ # Examples:
#
# URI.join("http://example.com/","main.rbx")
# # => #<URI::HTTP http://example.com/main.rbx>
@@ -268,7 +271,7 @@ module URI
# # => #<URI::HTTP http://example.com/foo/bar>
#
def self.join(*str)
- RFC3986_PARSER.join(*str)
+ DEFAULT_PARSER.join(*str)
end
#
@@ -295,9 +298,9 @@ module URI
# URI.extract("text here http://foo.example.org/bla and here mailto:test@example.com and here also.")
# # => ["http://foo.example.com/bla", "mailto:test@example.com"]
#
- def self.extract(str, schemes = nil, &block)
+ def self.extract(str, schemes = nil, &block) # :nodoc:
warn "URI.extract is obsolete", uplevel: 1 if $VERBOSE
- DEFAULT_PARSER.extract(str, schemes, &block)
+ PARSER.extract(str, schemes, &block)
end
#
@@ -315,7 +318,7 @@ module URI
#
# Returns a Regexp object which matches to URI-like strings.
# The Regexp object returned by this method includes arbitrary
- # number of capture group (parentheses). Never rely on it's number.
+ # number of capture group (parentheses). Never rely on its number.
#
# == Usage
#
@@ -332,15 +335,16 @@ module URI
# p $&
# end
#
- def self.regexp(schemes = nil)
+ def self.regexp(schemes = nil)# :nodoc:
warn "URI.regexp is obsolete", uplevel: 1 if $VERBOSE
- DEFAULT_PARSER.make_regexp(schemes)
+ PARSER.make_regexp(schemes)
end
TBLENCWWWCOMP_ = {} # :nodoc:
256.times do |i|
TBLENCWWWCOMP_[-i.chr] = -('%%%02X' % i)
end
+ TBLENCURICOMP_ = TBLENCWWWCOMP_.dup.freeze # :nodoc:
TBLENCWWWCOMP_[' '] = '+'
TBLENCWWWCOMP_.freeze
TBLDECWWWCOMP_ = {} # :nodoc:
@@ -354,18 +358,93 @@ module URI
TBLDECWWWCOMP_['+'] = ' '
TBLDECWWWCOMP_.freeze
- # Encodes given +str+ to URL-encoded form data.
+ # Returns a URL-encoded string derived from the given string +str+.
+ #
+ # The returned string:
+ #
+ # - Preserves:
+ #
+ # - Characters <tt>'*'</tt>, <tt>'.'</tt>, <tt>'-'</tt>, and <tt>'_'</tt>.
+ # - Character in ranges <tt>'a'..'z'</tt>, <tt>'A'..'Z'</tt>,
+ # and <tt>'0'..'9'</tt>.
+ #
+ # Example:
#
- # This method doesn't convert *, -, ., 0-9, A-Z, _, a-z, but does convert SP
- # (ASCII space) to + and converts others to %XX.
+ # URI.encode_www_form_component('*.-_azAZ09')
+ # # => "*.-_azAZ09"
#
- # If +enc+ is given, convert +str+ to the encoding before percent encoding.
+ # - Converts:
#
- # This is an implementation of
- # http://www.w3.org/TR/2013/CR-html5-20130806/forms.html#url-encoded-form-data.
+ # - Character <tt>' '</tt> to character <tt>'+'</tt>.
+ # - Any other character to "percent notation";
+ # the percent notation for character <i>c</i> is <tt>'%%%X' % c.ord</tt>.
#
- # See URI.decode_www_form_component, URI.encode_www_form.
+ # Example:
+ #
+ # URI.encode_www_form_component('Here are some punctuation characters: ,;?:')
+ # # => "Here+are+some+punctuation+characters%3A+%2C%3B%3F%3A"
+ #
+ # Encoding:
+ #
+ # - If +str+ has encoding Encoding::ASCII_8BIT, argument +enc+ is ignored.
+ # - Otherwise +str+ is converted first to Encoding::UTF_8
+ # (with suitable character replacements),
+ # and then to encoding +enc+.
+ #
+ # In either case, the returned string has forced encoding Encoding::US_ASCII.
+ #
+ # Related: URI.encode_uri_component (encodes <tt>' '</tt> as <tt>'%20'</tt>).
def self.encode_www_form_component(str, enc=nil)
+ _encode_uri_component(/[^*\-.0-9A-Z_a-z]/, TBLENCWWWCOMP_, str, enc)
+ end
+
+ # Returns a string decoded from the given \URL-encoded string +str+.
+ #
+ # The given string is first encoded as Encoding::ASCII-8BIT (using String#b),
+ # then decoded (as below), and finally force-encoded to the given encoding +enc+.
+ #
+ # The returned string:
+ #
+ # - Preserves:
+ #
+ # - Characters <tt>'*'</tt>, <tt>'.'</tt>, <tt>'-'</tt>, and <tt>'_'</tt>.
+ # - Character in ranges <tt>'a'..'z'</tt>, <tt>'A'..'Z'</tt>,
+ # and <tt>'0'..'9'</tt>.
+ #
+ # Example:
+ #
+ # URI.decode_www_form_component('*.-_azAZ09')
+ # # => "*.-_azAZ09"
+ #
+ # - Converts:
+ #
+ # - Character <tt>'+'</tt> to character <tt>' '</tt>.
+ # - Each "percent notation" to an ASCII character.
+ #
+ # Example:
+ #
+ # URI.decode_www_form_component('Here+are+some+punctuation+characters%3A+%2C%3B%3F%3A')
+ # # => "Here are some punctuation characters: ,;?:"
+ #
+ # Related: URI.decode_uri_component (preserves <tt>'+'</tt>).
+ def self.decode_www_form_component(str, enc=Encoding::UTF_8)
+ _decode_uri_component(/\+|%\h\h/, str, enc)
+ end
+
+ # Like URI.encode_www_form_component, except that <tt>' '</tt> (space)
+ # is encoded as <tt>'%20'</tt> (instead of <tt>'+'</tt>).
+ def self.encode_uri_component(str, enc=nil)
+ _encode_uri_component(/[^*\-.0-9A-Z_a-z]/, TBLENCURICOMP_, str, enc)
+ end
+
+ # Like URI.decode_www_form_component, except that <tt>'+'</tt> is preserved.
+ def self.decode_uri_component(str, enc=Encoding::UTF_8)
+ _decode_uri_component(/%\h\h/, str, enc)
+ end
+
+ # Returns a string derived from the given string +str+ with
+ # URI-encoded characters matching +regexp+ according to +table+.
+ def self._encode_uri_component(regexp, table, str, enc)
str = str.to_s.dup
if str.encoding != Encoding::ASCII_8BIT
if enc && enc != Encoding::ASCII_8BIT
@@ -374,47 +453,117 @@ module URI
end
str.force_encoding(Encoding::ASCII_8BIT)
end
- str.gsub!(/[^*\-.0-9A-Z_a-z]/, TBLENCWWWCOMP_)
+ str.gsub!(regexp, table)
str.force_encoding(Encoding::US_ASCII)
end
+ private_class_method :_encode_uri_component
- # Decodes given +str+ of URL-encoded form data.
- #
- # This decodes + to SP.
- #
- # See URI.encode_www_form_component, URI.decode_www_form.
- def self.decode_www_form_component(str, enc=Encoding::UTF_8)
- raise ArgumentError, "invalid %-encoding (#{str})" if /%(?!\h\h)/ =~ str
- str.b.gsub(/\+|%\h\h/, TBLDECWWWCOMP_).force_encoding(enc)
+ # Returns a string decoding characters matching +regexp+ from the
+ # given \URL-encoded string +str+.
+ def self._decode_uri_component(regexp, str, enc)
+ raise ArgumentError, "invalid %-encoding (#{str})" if /%(?!\h\h)/.match?(str)
+ str.b.gsub(regexp, TBLDECWWWCOMP_).force_encoding(enc)
end
+ private_class_method :_decode_uri_component
- # Generates URL-encoded form data from given +enum+.
+ # Returns a URL-encoded string derived from the given
+ # {Enumerable}[rdoc-ref:Enumerable@Enumerable+in+Ruby+Classes]
+ # +enum+.
+ #
+ # The result is suitable for use as form data
+ # for an \HTTP request whose <tt>Content-Type</tt> is
+ # <tt>'application/x-www-form-urlencoded'</tt>.
+ #
+ # The returned string consists of the elements of +enum+,
+ # each converted to one or more URL-encoded strings,
+ # and all joined with character <tt>'&'</tt>.
+ #
+ # Simple examples:
+ #
+ # URI.encode_www_form([['foo', 0], ['bar', 1], ['baz', 2]])
+ # # => "foo=0&bar=1&baz=2"
+ # URI.encode_www_form({foo: 0, bar: 1, baz: 2})
+ # # => "foo=0&bar=1&baz=2"
+ #
+ # The returned string is formed using method URI.encode_www_form_component,
+ # which converts certain characters:
+ #
+ # URI.encode_www_form('f#o': '/', 'b-r': '$', 'b z': '@')
+ # # => "f%23o=%2F&b-r=%24&b+z=%40"
+ #
+ # When +enum+ is Array-like, each element +ele+ is converted to a field:
+ #
+ # - If +ele+ is an array of two or more elements,
+ # the field is formed from its first two elements
+ # (and any additional elements are ignored):
+ #
+ # name = URI.encode_www_form_component(ele[0], enc)
+ # value = URI.encode_www_form_component(ele[1], enc)
+ # "#{name}=#{value}"
+ #
+ # Examples:
+ #
+ # URI.encode_www_form([%w[foo bar], %w[baz bat bah]])
+ # # => "foo=bar&baz=bat"
+ # URI.encode_www_form([['foo', 0], ['bar', :baz, 'bat']])
+ # # => "foo=0&bar=baz"
+ #
+ # - If +ele+ is an array of one element,
+ # the field is formed from <tt>ele[0]</tt>:
+ #
+ # URI.encode_www_form_component(ele[0])
+ #
+ # Example:
+ #
+ # URI.encode_www_form([['foo'], [:bar], [0]])
+ # # => "foo&bar&0"
+ #
+ # - Otherwise the field is formed from +ele+:
+ #
+ # URI.encode_www_form_component(ele)
#
- # This generates application/x-www-form-urlencoded data defined in HTML5
- # from given an Enumerable object.
+ # Example:
#
- # This internally uses URI.encode_www_form_component(str).
+ # URI.encode_www_form(['foo', :bar, 0])
+ # # => "foo&bar&0"
#
- # This method doesn't convert the encoding of given items, so convert them
- # before calling this method if you want to send data as other than original
- # encoding or mixed encoding data. (Strings which are encoded in an HTML5
- # ASCII incompatible encoding are converted to UTF-8.)
+ # The elements of an Array-like +enum+ may be mixture:
#
- # This method doesn't handle files. When you send a file, use
- # multipart/form-data.
+ # URI.encode_www_form([['foo', 0], ['bar', 1, 2], ['baz'], :bat])
+ # # => "foo=0&bar=1&baz&bat"
#
- # This refers http://url.spec.whatwg.org/#concept-urlencoded-serializer
+ # When +enum+ is Hash-like,
+ # each +key+/+value+ pair is converted to one or more fields:
#
- # URI.encode_www_form([["q", "ruby"], ["lang", "en"]])
- # #=> "q=ruby&lang=en"
- # URI.encode_www_form("q" => "ruby", "lang" => "en")
- # #=> "q=ruby&lang=en"
- # URI.encode_www_form("q" => ["ruby", "perl"], "lang" => "en")
- # #=> "q=ruby&q=perl&lang=en"
- # URI.encode_www_form([["q", "ruby"], ["q", "perl"], ["lang", "en"]])
- # #=> "q=ruby&q=perl&lang=en"
+ # - If +value+ is
+ # {Array-convertible}[rdoc-ref:implicit_conversion.rdoc@Array-Convertible+Objects],
+ # each element +ele+ in +value+ is paired with +key+ to form a field:
+ #
+ # name = URI.encode_www_form_component(key, enc)
+ # value = URI.encode_www_form_component(ele, enc)
+ # "#{name}=#{value}"
+ #
+ # Example:
+ #
+ # URI.encode_www_form({foo: [:bar, 1], baz: [:bat, :bam, 2]})
+ # # => "foo=bar&foo=1&baz=bat&baz=bam&baz=2"
+ #
+ # - Otherwise, +key+ and +value+ are paired to form a field:
+ #
+ # name = URI.encode_www_form_component(key, enc)
+ # value = URI.encode_www_form_component(value, enc)
+ # "#{name}=#{value}"
+ #
+ # Example:
+ #
+ # URI.encode_www_form({foo: 0, bar: 1, baz: 2})
+ # # => "foo=0&bar=1&baz=2"
+ #
+ # The elements of a Hash-like +enum+ may be mixture:
+ #
+ # URI.encode_www_form({foo: [0, 1], bar: 2})
+ # # => "foo=0&foo=1&bar=2"
#
- # See URI.encode_www_form_component, URI.decode_www_form.
def self.encode_www_form(enum, enc=nil)
enum.map do |k,v|
if v.nil?
@@ -435,22 +584,39 @@ module URI
end.join('&')
end
- # Decodes URL-encoded form data from given +str+.
+ # Returns name/value pairs derived from the given string +str+,
+ # which must be an ASCII string.
#
- # This decodes application/x-www-form-urlencoded data
- # and returns an array of key-value arrays.
+ # The method may be used to decode the body of Net::HTTPResponse object +res+
+ # for which <tt>res['Content-Type']</tt> is <tt>'application/x-www-form-urlencoded'</tt>.
#
- # This refers http://url.spec.whatwg.org/#concept-urlencoded-parser,
- # so this supports only &-separator, and doesn't support ;-separator.
+ # The returned data is an array of 2-element subarrays;
+ # each subarray is a name/value pair (both are strings).
+ # Each returned string has encoding +enc+,
+ # and has had invalid characters removed via
+ # {String#scrub}[rdoc-ref:String#scrub].
#
- # ary = URI.decode_www_form("a=1&a=2&b=3")
- # ary #=> [['a', '1'], ['a', '2'], ['b', '3']]
- # ary.assoc('a').last #=> '1'
- # ary.assoc('b').last #=> '3'
- # ary.rassoc('a').last #=> '2'
- # Hash[ary] #=> {"a"=>"2", "b"=>"3"}
+ # A simple example:
+ #
+ # URI.decode_www_form('foo=0&bar=1&baz')
+ # # => [["foo", "0"], ["bar", "1"], ["baz", ""]]
+ #
+ # The returned strings have certain conversions,
+ # similar to those performed in URI.decode_www_form_component:
+ #
+ # URI.decode_www_form('f%23o=%2F&b-r=%24&b+z=%40')
+ # # => [["f#o", "/"], ["b-r", "$"], ["b z", "@"]]
+ #
+ # The given string may contain consecutive separators:
+ #
+ # URI.decode_www_form('foo=0&&bar=1&&baz=2')
+ # # => [["foo", "0"], ["", ""], ["bar", "1"], ["", ""], ["baz", "2"]]
+ #
+ # A different separator may be specified:
+ #
+ # URI.decode_www_form('foo=0--bar=1--baz', separator: '--')
+ # # => [["foo", "0"], ["bar", "1"], ["baz", ""]]
#
- # See URI.decode_www_form_component, URI.encode_www_form.
def self.decode_www_form(str, enc=Encoding::UTF_8, separator: '&', use__charset_: false, isindex: false)
raise ArgumentError, "the input of #{self.name}.#{__method__} must be ASCII only string" unless str.ascii_only?
ary = []
@@ -716,6 +882,7 @@ module URI
"utf-16"=>"utf-16le",
"utf-16le"=>"utf-16le",
} # :nodoc:
+ Ractor.make_shareable(WEB_ENCODINGS_) if defined?(Ractor)
# :nodoc:
# return encoding or nil
@@ -728,7 +895,18 @@ end # module URI
module Kernel
#
- # Returns +uri+ converted to an URI object.
+ # Returns a \URI object derived from the given +uri+,
+ # which may be a \URI string or an existing \URI object:
+ #
+ # require 'uri'
+ # # Returns a new URI.
+ # uri = URI('http://github.com/ruby/ruby')
+ # # => #<URI::HTTP http://github.com/ruby/ruby>
+ # # Returns the given URI.
+ # URI(uri)
+ # # => #<URI::HTTP http://github.com/ruby/ruby>
+ #
+ # You must require 'uri' to use this method.
#
def URI(uri)
if uri.is_a?(URI::Generic)
diff --git a/lib/uri/file.rb b/lib/uri/file.rb
index 561ec703c4..47b5aef067 100644
--- a/lib/uri/file.rb
+++ b/lib/uri/file.rb
@@ -33,6 +33,9 @@ module URI
# If an Array is used, the components must be passed in the
# order <code>[host, path]</code>.
#
+ # A path from e.g. the File class should be escaped before
+ # being passed.
+ #
# Examples:
#
# require 'uri'
@@ -44,6 +47,9 @@ module URI
# :path => '/ruby/src'})
# uri2.to_s # => "file://host.example.com/ruby/src"
#
+ # uri3 = URI::File.build({:path => URI::RFC2396_PARSER.escape('/path/my file.txt')})
+ # uri3.to_s # => "file:///path/my%20file.txt"
+ #
def self.build(args)
tmp = Util::make_components_hash(self, args)
super(tmp)
@@ -64,17 +70,17 @@ module URI
# raise InvalidURIError
def check_userinfo(user)
- raise URI::InvalidURIError, "can not set userinfo for file URI"
+ raise URI::InvalidURIError, "cannot set userinfo for file URI"
end
# raise InvalidURIError
def check_user(user)
- raise URI::InvalidURIError, "can not set user for file URI"
+ raise URI::InvalidURIError, "cannot set user for file URI"
end
# raise InvalidURIError
def check_password(user)
- raise URI::InvalidURIError, "can not set password for file URI"
+ raise URI::InvalidURIError, "cannot set password for file URI"
end
# do nothing
@@ -90,5 +96,5 @@ module URI
end
end
- @@schemes['FILE'] = File
+ register_scheme 'FILE', File
end
diff --git a/lib/uri/ftp.rb b/lib/uri/ftp.rb
index f57b4b7df9..1c75e242ba 100644
--- a/lib/uri/ftp.rb
+++ b/lib/uri/ftp.rb
@@ -3,7 +3,6 @@
#
# Author:: Akira Yamada <akira@ruby-lang.org>
# License:: You can redistribute it and/or modify it under the same term as Ruby.
-# Revision:: $Id$
#
# See URI for general documentation
#
@@ -18,7 +17,7 @@ module URI
# This class will be redesigned because of difference of implementations;
# the structure of its path. draft-hoffman-ftp-uri-04 is a draft but it
# is a good summary about the de facto spec.
- # http://tools.ietf.org/html/draft-hoffman-ftp-uri-04
+ # https://datatracker.ietf.org/doc/html/draft-hoffman-ftp-uri-04
#
class FTP < Generic
# A Default port of 21 for URI::FTP.
@@ -263,5 +262,6 @@ module URI
return str
end
end
- @@schemes['FTP'] = FTP
+
+ register_scheme 'FTP', FTP
end
diff --git a/lib/uri/generic.rb b/lib/uri/generic.rb
index ea79e7950a..6a0f638d76 100644
--- a/lib/uri/generic.rb
+++ b/lib/uri/generic.rb
@@ -4,7 +4,6 @@
#
# Author:: Akira Yamada <akira@ruby-lang.org>
# License:: You can redistribute it and/or modify it under the same term as Ruby.
-# Revision:: $Id$
#
# See URI for general documentation
#
@@ -74,7 +73,7 @@ module URI
#
# At first, tries to create a new URI::Generic instance using
# URI::Generic::build. But, if exception URI::InvalidComponentError is raised,
- # then it does URI::Escape.escape all URI components and tries again.
+ # then it does URI::RFC2396_PARSER.escape all URI components and tries again.
#
def self.build2(args)
begin
@@ -83,7 +82,7 @@ module URI
if args.kind_of?(Array)
return self.build(args.collect{|x|
if x.is_a?(String)
- DEFAULT_PARSER.escape(x)
+ URI::RFC2396_PARSER.escape(x)
else
x
end
@@ -92,7 +91,7 @@ module URI
tmp = {}
args.each do |key, value|
tmp[key] = if value
- DEFAULT_PARSER.escape(value)
+ URI::RFC2396_PARSER.escape(value)
else
value
end
@@ -127,9 +126,9 @@ module URI
end
end
else
- component = self.class.component rescue ::URI::Generic::COMPONENT
+ component = self.component rescue ::URI::Generic::COMPONENT
raise ArgumentError,
- "expected Array of or Hash of components of #{self.class} (#{component.join(', ')})"
+ "expected Array of or Hash of components of #{self} (#{component.join(', ')})"
end
tmp << nil
@@ -187,18 +186,18 @@ module URI
if arg_check
self.scheme = scheme
- self.userinfo = userinfo
self.hostname = host
self.port = port
+ self.userinfo = userinfo
self.path = path
self.query = query
self.opaque = opaque
self.fragment = fragment
else
self.set_scheme(scheme)
- self.set_userinfo(userinfo)
self.set_host(host)
self.set_port(port)
+ self.set_userinfo(userinfo)
self.set_path(path)
self.query = query
self.set_opaque(opaque)
@@ -285,7 +284,7 @@ module URI
# Returns the parser to be used.
#
- # Unless a URI::Parser is defined, DEFAULT_PARSER is used.
+ # Unless the +parser+ is defined, DEFAULT_PARSER is used.
#
def parser
if !defined?(@parser) || !@parser
@@ -316,7 +315,7 @@ module URI
end
#
- # Checks the scheme +v+ component against the URI::Parser Regexp for :SCHEME.
+ # Checks the scheme +v+ component against the +parser+ Regexp for :SCHEME.
#
def check_scheme(v)
if v && parser.regexp[:SCHEME] !~ v
@@ -386,7 +385,7 @@ module URI
#
# Checks the user +v+ component for RFC2396 compliance
- # and against the URI::Parser Regexp for :USERINFO.
+ # and against the +parser+ Regexp for :USERINFO.
#
# Can not have a registry or opaque component defined,
# with a user component defined.
@@ -394,7 +393,7 @@ module URI
def check_user(v)
if @opaque
raise InvalidURIError,
- "can not set user with opaque"
+ "cannot set user with opaque"
end
return v unless v
@@ -410,7 +409,7 @@ module URI
#
# Checks the password +v+ component for RFC2396 compliance
- # and against the URI::Parser Regexp for :USERINFO.
+ # and against the +parser+ Regexp for :USERINFO.
#
# Can not have a registry or opaque component defined,
# with a user component defined.
@@ -418,7 +417,7 @@ module URI
def check_password(v, user = @user)
if @opaque
raise InvalidURIError,
- "can not set password with opaque"
+ "cannot set password with opaque"
end
return v unless v
@@ -467,7 +466,7 @@ module URI
#
# uri = URI.parse("http://john:S3nsit1ve@my.example.com")
# uri.user = "sam"
- # uri.to_s #=> "http://sam:V3ry_S3nsit1ve@my.example.com"
+ # uri.to_s #=> "http://sam@my.example.com"
#
def user=(user)
check_user(user)
@@ -512,7 +511,7 @@ module URI
user, password = split_userinfo(user)
end
@user = user
- @password = password if password
+ @password = password
[@user, @password]
end
@@ -523,7 +522,7 @@ module URI
# See also URI::Generic.user=.
#
def set_user(v)
- set_userinfo(v, @password)
+ set_userinfo(v, nil)
v
end
protected :set_user
@@ -565,19 +564,35 @@ module URI
end
end
- # Returns the user component.
+ # Returns the user component (without URI decoding).
def user
@user
end
- # Returns the password component.
+ # Returns the password component (without URI decoding).
def password
@password
end
+ # Returns the authority info (array of user, password, host and
+ # port), if any is set. Or returns +nil+.
+ def authority
+ return @user, @password, @host, @port if @user || @password || @host || @port
+ end
+
+ # Returns the user component after URI decoding.
+ def decoded_user
+ URI.decode_uri_component(@user) if @user
+ end
+
+ # Returns the password component after URI decoding.
+ def decoded_password
+ URI.decode_uri_component(@password) if @password
+ end
+
#
# Checks the host +v+ component for RFC2396 compliance
- # and against the URI::Parser Regexp for :HOST.
+ # and against the +parser+ Regexp for :HOST.
#
# Can not have a registry or opaque component defined,
# with a host component defined.
@@ -587,7 +602,7 @@ module URI
if @opaque
raise InvalidURIError,
- "can not set host with registry or opaque"
+ "cannot set host with registry or opaque"
elsif parser.regexp[:HOST] !~ v
raise InvalidComponentError,
"bad component(expected host component): #{v}"
@@ -606,6 +621,13 @@ module URI
end
protected :set_host
+ # Protected setter for the authority info (+user+, +password+, +host+
+ # and +port+). If +port+ is +nil+, +default_port+ will be set.
+ #
+ protected def set_authority(user, password, host, port = nil)
+ @user, @password, @host, @port = user, password, host, port || self.default_port
+ end
+
#
# == Args
#
@@ -630,6 +652,7 @@ module URI
def host=(v)
check_host(v)
set_host(v)
+ set_userinfo(nil)
v
end
@@ -644,7 +667,7 @@ module URI
#
def hostname
v = self.host
- /\A\[(.*)\]\z/ =~ v ? $1 : v
+ v&.start_with?('[') && v.end_with?(']') ? v[1..-2] : v
end
# Sets the host part of the URI as the argument with brackets for IPv6 addresses.
@@ -660,13 +683,13 @@ module URI
# it is wrapped with brackets.
#
def hostname=(v)
- v = "[#{v}]" if /\A\[.*\]\z/ !~ v && /:/ =~ v
+ v = "[#{v}]" if !(v&.start_with?('[') && v&.end_with?(']')) && v&.index(':')
self.host = v
end
#
# Checks the port +v+ component for RFC2396 compliance
- # and against the URI::Parser Regexp for :PORT.
+ # and against the +parser+ Regexp for :PORT.
#
# Can not have a registry or opaque component defined,
# with a port component defined.
@@ -676,7 +699,7 @@ module URI
if @opaque
raise InvalidURIError,
- "can not set port with registry or opaque"
+ "cannot set port with registry or opaque"
elsif !v.kind_of?(Integer) && parser.regexp[:PORT] !~ v
raise InvalidComponentError,
"bad component(expected port component): #{v.inspect}"
@@ -720,26 +743,27 @@ module URI
def port=(v)
check_port(v)
set_port(v)
+ set_userinfo(nil)
port
end
def check_registry(v) # :nodoc:
- raise InvalidURIError, "can not set registry"
+ raise InvalidURIError, "cannot set registry"
end
private :check_registry
- def set_registry(v) #:nodoc:
- raise InvalidURIError, "can not set registry"
+ def set_registry(v) # :nodoc:
+ raise InvalidURIError, "cannot set registry"
end
protected :set_registry
- def registry=(v)
- raise InvalidURIError, "can not set registry"
+ def registry=(v) # :nodoc:
+ raise InvalidURIError, "cannot set registry"
end
#
# Checks the path +v+ component for RFC2396 compliance
- # and against the URI::Parser Regexp
+ # and against the +parser+ Regexp
# for :ABS_PATH and :REL_PATH.
#
# Can not have a opaque component defined,
@@ -836,6 +860,7 @@ module URI
v.encode!(Encoding::UTF_8) rescue nil
v.delete!("\t\r\n")
v.force_encoding(Encoding::ASCII_8BIT)
+ raise InvalidURIError, "invalid percent escape: #{$1}" if /(%\H\H)/n.match(v)
v.gsub!(/(?!%\h\h|[!$-&(-;=?-_a-~])./n.freeze){'%%%02X' % $&.ord}
v.force_encoding(Encoding::US_ASCII)
@query = v
@@ -843,7 +868,7 @@ module URI
#
# Checks the opaque +v+ component for RFC2396 compliance and
- # against the URI::Parser Regexp for :OPAQUE.
+ # against the +parser+ Regexp for :OPAQUE.
#
# Can not have a host, port, user, or path component defined,
# with an opaque component defined.
@@ -856,7 +881,7 @@ module URI
# hier_part = ( net_path | abs_path ) [ "?" query ]
if @host || @port || @user || @path # userinfo = @user + ':' + @password
raise InvalidURIError,
- "can not set opaque with host, port, userinfo or path"
+ "cannot set opaque with host, port, userinfo or path"
elsif v && parser.regexp[:OPAQUE] !~ v
raise InvalidComponentError,
"bad component(expected opaque component): #{v}"
@@ -895,7 +920,7 @@ module URI
end
#
- # Checks the fragment +v+ component against the URI::Parser Regexp for :FRAGMENT.
+ # Checks the fragment +v+ component against the +parser+ Regexp for :FRAGMENT.
#
#
# == Args
@@ -935,7 +960,7 @@ module URI
# == Description
#
# URI has components listed in order of decreasing significance from left to right,
- # see RFC3986 https://tools.ietf.org/html/rfc3986 1.2.3.
+ # see RFC3986 https://www.rfc-editor.org/rfc/rfc3986 1.2.3.
#
# == Usage
#
@@ -1097,7 +1122,7 @@ module URI
# # => "http://my.example.com/main.rbx?page=1"
#
def merge(oth)
- rel = parser.send(:convert_to_uri, oth)
+ rel = parser.__send__(:convert_to_uri, oth)
if rel.absolute?
#raise BadURIError, "both URI are absolute" if absolute?
@@ -1111,7 +1136,7 @@ module URI
base = self.dup
- authority = rel.userinfo || rel.host || rel.port
+ authority = rel.authority
# RFC2396, Section 5.2, 2)
if (rel.path.nil? || rel.path.empty?) && !authority && !rel.query
@@ -1123,17 +1148,14 @@ module URI
base.fragment=(nil)
# RFC2396, Section 5.2, 4)
- if !authority
- base.set_path(merge_path(base.path, rel.path)) if base.path && rel.path
- else
- # RFC2396, Section 5.2, 4)
- base.set_path(rel.path) if rel.path
+ if authority
+ base.set_authority(*authority)
+ base.set_path(rel.path)
+ elsif base.path && rel.path
+ base.set_path(merge_path(base.path, rel.path))
end
# RFC2396, Section 5.2, 7)
- base.set_userinfo(rel.userinfo) if rel.userinfo
- base.set_host(rel.host) if rel.host
- base.set_port(rel.port) if rel.port
base.query = rel.query if rel.query
base.fragment=(rel.fragment) if rel.fragment
@@ -1182,7 +1204,7 @@ module URI
# :stopdoc:
def route_from0(oth)
- oth = parser.send(:convert_to_uri, oth)
+ oth = parser.__send__(:convert_to_uri, oth)
if self.relative?
raise BadURIError,
"relative URI: #{self}"
@@ -1225,7 +1247,7 @@ module URI
return rel, rel
end
- # you can modify `rel', but can not `oth'.
+ # you can modify `rel', but cannot `oth'.
return oth, rel
end
private :route_from0
@@ -1250,7 +1272,7 @@ module URI
# #=> #<URI::Generic /main.rbx?page=1>
#
def route_from(oth)
- # you can modify `rel', but can not `oth'.
+ # you can modify `rel', but cannot `oth'.
begin
oth, rel = route_from0(oth)
rescue
@@ -1290,7 +1312,7 @@ module URI
# #=> #<URI::Generic /main.rbx?page=1>
#
def route_to(oth)
- parser.send(:convert_to_uri, oth).route_from(self)
+ parser.__send__(:convert_to_uri, oth).route_from(self)
end
#
@@ -1354,6 +1376,9 @@ module URI
str << ':'
str << @port.to_s
end
+ if (@host || @port) && !@path.empty? && !@path.start_with?('/')
+ str << '/'
+ end
str << @path
if @query
str << '?'
@@ -1366,6 +1391,7 @@ module URI
end
str
end
+ alias to_str to_s
#
# Compares two URIs.
@@ -1378,33 +1404,22 @@ module URI
end
end
+ # Returns the hash value.
def hash
self.component_ary.hash
end
+ # Compares with _oth_ for Hash.
def eql?(oth)
self.class == oth.class &&
parser == oth.parser &&
self.component_ary.eql?(oth.component_ary)
end
-=begin
-
---- URI::Generic#===(oth)
-
-=end
-# def ===(oth)
-# raise NotImplementedError
-# end
-
-=begin
-=end
-
-
# Returns an Array of the components defined from the COMPONENT Array.
def component_ary
component.collect do |x|
- self.send(x)
+ self.__send__(x)
end
end
protected :component_ary
@@ -1429,7 +1444,7 @@ module URI
def select(*components)
components.collect do |c|
if component.include?(c)
- self.send(c)
+ self.__send__(c)
else
raise ArgumentError,
"expected of components of #{self.class} (#{self.class.component.join(', ')})"
@@ -1437,7 +1452,7 @@ module URI
end
end
- def inspect
+ def inspect # :nodoc:
"#<#{self.class} #{self}>"
end
@@ -1514,9 +1529,19 @@ module URI
proxy_uri = env["CGI_#{name.upcase}"]
end
elsif name == 'http_proxy'
- unless proxy_uri = env[name]
- if proxy_uri = env[name.upcase]
- warn 'The environment variable HTTP_PROXY is discouraged. Use http_proxy.', uplevel: 1
+ if RUBY_ENGINE == 'jruby' && p_addr = ENV_JAVA['http.proxyHost']
+ p_port = ENV_JAVA['http.proxyPort']
+ if p_user = ENV_JAVA['http.proxyUser']
+ p_pass = ENV_JAVA['http.proxyPass']
+ proxy_uri = "http://#{p_user}:#{p_pass}@#{p_addr}:#{p_port}"
+ else
+ proxy_uri = "http://#{p_addr}:#{p_port}"
+ end
+ else
+ unless proxy_uri = env[name]
+ if proxy_uri = env[name.upcase]
+ warn 'The environment variable HTTP_PROXY is discouraged. Please use http_proxy instead.', uplevel: 1
+ end
end
end
else
diff --git a/lib/uri/http.rb b/lib/uri/http.rb
index 2e2ebcc1d3..3c41cd4e93 100644
--- a/lib/uri/http.rb
+++ b/lib/uri/http.rb
@@ -3,7 +3,6 @@
#
# Author:: Akira Yamada <akira@ruby-lang.org>
# License:: You can redistribute it and/or modify it under the same term as Ruby.
-# Revision:: $Id$
#
# See URI for general documentation
#
@@ -62,6 +61,18 @@ module URI
super(tmp)
end
+ # Do not allow empty host names, as they are not allowed by RFC 3986.
+ def check_host(v)
+ ret = super
+
+ if ret && v.empty?
+ raise InvalidComponentError,
+ "bad component(expected host component): #{v}"
+ end
+
+ ret
+ end
+
#
# == Description
#
@@ -81,8 +92,46 @@ module URI
url = @query ? "#@path?#@query" : @path.dup
url.start_with?(?/.freeze) ? url : ?/ + url
end
- end
- @@schemes['HTTP'] = HTTP
+ #
+ # == Description
+ #
+ # Returns the authority for an HTTP uri, as defined in
+ # https://www.rfc-editor.org/rfc/rfc3986#section-3.2.
+ #
+ #
+ # Example:
+ #
+ # URI::HTTP.build(host: 'www.example.com', path: '/foo/bar').authority #=> "www.example.com"
+ # URI::HTTP.build(host: 'www.example.com', port: 8000, path: '/foo/bar').authority #=> "www.example.com:8000"
+ # URI::HTTP.build(host: 'www.example.com', port: 80, path: '/foo/bar').authority #=> "www.example.com"
+ #
+ def authority
+ if port == default_port
+ host
+ else
+ "#{host}:#{port}"
+ end
+ end
+
+ #
+ # == Description
+ #
+ # Returns the origin for an HTTP uri, as defined in
+ # https://www.rfc-editor.org/rfc/rfc6454.
+ #
+ #
+ # Example:
+ #
+ # URI::HTTP.build(host: 'www.example.com', path: '/foo/bar').origin #=> "http://www.example.com"
+ # URI::HTTP.build(host: 'www.example.com', port: 8000, path: '/foo/bar').origin #=> "http://www.example.com:8000"
+ # URI::HTTP.build(host: 'www.example.com', port: 80, path: '/foo/bar').origin #=> "http://www.example.com"
+ # URI::HTTPS.build(host: 'www.example.com', path: '/foo/bar').origin #=> "https://www.example.com"
+ #
+ def origin
+ "#{scheme}://#{authority}"
+ end
+ end
+ register_scheme 'HTTP', HTTP
end
diff --git a/lib/uri/https.rb b/lib/uri/https.rb
index 4780ee0a44..50a5cabaf8 100644
--- a/lib/uri/https.rb
+++ b/lib/uri/https.rb
@@ -3,7 +3,6 @@
#
# Author:: Akira Yamada <akira@ruby-lang.org>
# License:: You can redistribute it and/or modify it under the same term as Ruby.
-# Revision:: $Id$
#
# See URI for general documentation
#
@@ -19,5 +18,6 @@ module URI
# A Default port of 443 for URI::HTTPS
DEFAULT_PORT = 443
end
- @@schemes['HTTPS'] = HTTPS
+
+ register_scheme 'HTTPS', HTTPS
end
diff --git a/lib/uri/ldap.rb b/lib/uri/ldap.rb
index 228c793cbd..4544349f18 100644
--- a/lib/uri/ldap.rb
+++ b/lib/uri/ldap.rb
@@ -7,7 +7,6 @@
# License::
# URI::LDAP is copyrighted free software by Takaaki Tateishi and Akira Yamada.
# You can redistribute it and/or modify it under the same term as Ruby.
-# Revision:: $Id$
#
# See URI for general documentation
#
@@ -119,6 +118,7 @@ module URI
# Private method to cleanup +dn+ from using the +path+ component attribute.
def parse_dn
+ raise InvalidURIError, 'bad LDAP URL' unless @path
@dn = @path[1..-1]
end
private :parse_dn
@@ -257,5 +257,5 @@ module URI
end
end
- @@schemes['LDAP'] = LDAP
+ register_scheme 'LDAP', LDAP
end
diff --git a/lib/uri/ldaps.rb b/lib/uri/ldaps.rb
index 227e7fab35..58228f5894 100644
--- a/lib/uri/ldaps.rb
+++ b/lib/uri/ldaps.rb
@@ -17,5 +17,6 @@ module URI
# A Default port of 636 for URI::LDAPS
DEFAULT_PORT = 636
end
- @@schemes['LDAPS'] = LDAPS
+
+ register_scheme 'LDAPS', LDAPS
end
diff --git a/lib/uri/mailto.rb b/lib/uri/mailto.rb
index 9c06871c7a..cb8024f301 100644
--- a/lib/uri/mailto.rb
+++ b/lib/uri/mailto.rb
@@ -3,7 +3,6 @@
#
# Author:: Akira Yamada <akira@ruby-lang.org>
# License:: You can redistribute it and/or modify it under the same term as Ruby.
-# Revision:: $Id$
#
# See URI for general documentation
#
@@ -16,7 +15,7 @@ module URI
# RFC6068, the mailto URL scheme.
#
class MailTo < Generic
- include REGEXP
+ include RFC2396_REGEXP
# A Default port of nil for URI::MailTo.
DEFAULT_PORT = nil
@@ -290,5 +289,5 @@ module URI
alias to_rfc822text to_mailtext
end
- @@schemes['MAILTO'] = MailTo
+ register_scheme 'MAILTO', MailTo
end
diff --git a/lib/uri/rfc2396_parser.rb b/lib/uri/rfc2396_parser.rb
index 843fe124ee..cefd126cc6 100644
--- a/lib/uri/rfc2396_parser.rb
+++ b/lib/uri/rfc2396_parser.rb
@@ -3,7 +3,6 @@
# = uri/common.rb
#
# Author:: Akira Yamada <akira@ruby-lang.org>
-# Revision:: $Id$
# License::
# You can redistribute it and/or modify it under the same term as Ruby.
#
@@ -68,7 +67,7 @@ module URI
#
# == Synopsis
#
- # URI::Parser.new([opts])
+ # URI::RFC2396_Parser.new([opts])
#
# == Args
#
@@ -87,7 +86,7 @@ module URI
#
# == Examples
#
- # p = URI::Parser.new(:ESCAPED => "(?:%[a-fA-F0-9]{2}|%u[a-fA-F0-9]{4})")
+ # p = URI::RFC2396_Parser.new(:ESCAPED => "(?:%[a-fA-F0-9]{2}|%u[a-fA-F0-9]{4})")
# u = p.parse("http://example.jp/%uABCD") #=> #<URI::HTTP http://example.jp/%uABCD>
# URI.parse(u.to_s) #=> raises URI::InvalidURIError
#
@@ -109,15 +108,15 @@ module URI
# The Hash of patterns.
#
- # See also URI::Parser.initialize_pattern.
+ # See also #initialize_pattern.
attr_reader :pattern
# The Hash of Regexp.
#
- # See also URI::Parser.initialize_regexp.
+ # See also #initialize_regexp.
attr_reader :regexp
- # Returns a split URI against regexp[:ABS_URI].
+ # Returns a split URI against +regexp[:ABS_URI]+.
def split(uri)
case uri
when ''
@@ -141,11 +140,11 @@ module URI
if !scheme
raise InvalidURIError,
- "bad URI(absolute but no scheme): #{uri}"
+ "bad URI (absolute but no scheme): #{uri}"
end
if !opaque && (!path && (!host && !registry))
raise InvalidURIError,
- "bad URI(absolute but no path): #{uri}"
+ "bad URI (absolute but no path): #{uri}"
end
when @regexp[:REL_URI]
@@ -174,7 +173,7 @@ module URI
# server = [ [ userinfo "@" ] hostport ]
else
- raise InvalidURIError, "bad URI(is not URI?): #{uri}"
+ raise InvalidURIError, "bad URI (is not URI?): #{uri}"
end
path = '' if !path && !opaque # (see RFC2396 Section 5.2)
@@ -203,26 +202,13 @@ module URI
#
# == Usage
#
- # p = URI::Parser.new
- # p.parse("ldap://ldap.example.com/dc=example?user=john")
+ # URI::RFC2396_PARSER.parse("ldap://ldap.example.com/dc=example?user=john")
# #=> #<URI::LDAP ldap://ldap.example.com/dc=example?user=john>
#
def parse(uri)
- scheme, userinfo, host, port,
- registry, path, opaque, query, fragment = self.split(uri)
-
- if scheme && URI.scheme_list.include?(scheme.upcase)
- URI.scheme_list[scheme.upcase].new(scheme, userinfo, host, port,
- registry, path, opaque, query,
- fragment, self)
- else
- Generic.new(scheme, userinfo, host, port,
- registry, path, opaque, query,
- fragment, self)
- end
+ URI.for(*self.split(uri), self)
end
-
#
# == Args
#
@@ -257,7 +243,7 @@ module URI
# If no +block+ given, then returns the result,
# else it calls +block+ for each element in result.
#
- # See also URI::Parser.make_regexp.
+ # See also #make_regexp.
#
def extract(str, schemes = nil)
if block_given?
@@ -270,13 +256,13 @@ module URI
end
end
- # Returns Regexp that is default self.regexp[:ABS_URI_REF],
- # unless +schemes+ is provided. Then it is a Regexp.union with self.pattern[:X_ABS_URI].
+ # Returns Regexp that is default +self.regexp[:ABS_URI_REF]+,
+ # unless +schemes+ is provided. Then it is a Regexp.union with +self.pattern[:X_ABS_URI]+.
def make_regexp(schemes = nil)
unless schemes
@regexp[:ABS_URI_REF]
else
- /(?=#{Regexp.union(*schemes)}:)#{@pattern[:X_ABS_URI]}/x
+ /(?=(?i:#{Regexp.union(*schemes).source}):)#{@pattern[:X_ABS_URI]}/x
end
end
@@ -290,7 +276,7 @@ module URI
# +str+::
# String to make safe
# +unsafe+::
- # Regexp to apply. Defaults to self.regexp[:UNSAFE]
+ # Regexp to apply. Defaults to +self.regexp[:UNSAFE]+
#
# == Description
#
@@ -322,7 +308,7 @@ module URI
# +str+::
# String to remove escapes from
# +escaped+::
- # Regexp to apply. Defaults to self.regexp[:ESCAPED]
+ # Regexp to apply. Defaults to +self.regexp[:ESCAPED]+
#
# == Description
#
@@ -334,9 +320,15 @@ module URI
str.gsub(escaped) { [$&[1, 2]].pack('H2').force_encoding(enc) }
end
- @@to_s = Kernel.instance_method(:to_s)
- def inspect
- @@to_s.bind(self).call
+ TO_S = Kernel.instance_method(:to_s) # :nodoc:
+ if TO_S.respond_to?(:bind_call)
+ def inspect # :nodoc:
+ TO_S.bind_call(self)
+ end
+ else
+ def inspect # :nodoc:
+ TO_S.bind(self).call
+ end
end
private
@@ -504,8 +496,8 @@ module URI
ret = {}
# for URI::split
- ret[:ABS_URI] = Regexp.new('\A\s*' + pattern[:X_ABS_URI] + '\s*\z', Regexp::EXTENDED)
- ret[:REL_URI] = Regexp.new('\A\s*' + pattern[:X_REL_URI] + '\s*\z', Regexp::EXTENDED)
+ ret[:ABS_URI] = Regexp.new('\A\s*+' + pattern[:X_ABS_URI] + '\s*\z', Regexp::EXTENDED)
+ ret[:REL_URI] = Regexp.new('\A\s*+' + pattern[:X_REL_URI] + '\s*\z', Regexp::EXTENDED)
# for URI::extract
ret[:URI_REF] = Regexp.new(pattern[:URI_REF])
@@ -531,6 +523,8 @@ module URI
ret
end
+ # Returns +uri+ as-is if it is URI, or convert it to URI if it is
+ # a String.
def convert_to_uri(uri)
if uri.is_a?(URI::Generic)
uri
@@ -543,4 +537,11 @@ module URI
end
end # class Parser
+
+ # Backward compatibility for URI::REGEXP::PATTERN::*
+ RFC2396_Parser.new.pattern.each_pair do |sym, str|
+ unless RFC2396_REGEXP::PATTERN.const_defined?(sym, false)
+ RFC2396_REGEXP::PATTERN.const_set(sym, str)
+ end
+ end
end # module URI
diff --git a/lib/uri/rfc3986_parser.rb b/lib/uri/rfc3986_parser.rb
index 135e847e88..0b5f0c4488 100644
--- a/lib/uri/rfc3986_parser.rb
+++ b/lib/uri/rfc3986_parser.rb
@@ -1,10 +1,73 @@
-# frozen_string_literal: false
+# frozen_string_literal: true
module URI
class RFC3986_Parser # :nodoc:
# URI defined in RFC3986
- # this regexp is modified not to host is not empty string
- RFC3986_URI = /\A(?<URI>(?<scheme>[A-Za-z][+\-.0-9A-Za-z]*):(?<hier-part>\/\/(?<authority>(?:(?<userinfo>(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*)@)?(?<host>(?<IP-literal>\[(?:(?<IPv6address>(?:\h{1,4}:){6}(?<ls32>\h{1,4}:\h{1,4}|(?<IPv4address>(?<dec-octet>[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g<dec-octet>\.\g<dec-octet>\.\g<dec-octet>))|::(?:\h{1,4}:){5}\g<ls32>|\h{1,4}?::(?:\h{1,4}:){4}\g<ls32>|(?:(?:\h{1,4}:)?\h{1,4})?::(?:\h{1,4}:){3}\g<ls32>|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g<ls32>|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g<ls32>|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g<ls32>|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?<IPvFuture>v\h+\.[!$&-.0-;=A-Z_a-z~]+))\])|\g<IPv4address>|(?<reg-name>(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])+))?(?::(?<port>\d*))?)(?<path-abempty>(?:\/(?<segment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*))*)|(?<path-absolute>\/(?:(?<segment-nz>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])+)(?:\/\g<segment>)*)?)|(?<path-rootless>\g<segment-nz>(?:\/\g<segment>)*)|(?<path-empty>))(?:\?(?<query>[^#]*))?(?:\#(?<fragment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*))?)\z/
- RFC3986_relative_ref = /\A(?<relative-ref>(?<relative-part>\/\/(?<authority>(?:(?<userinfo>(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*)@)?(?<host>(?<IP-literal>\[(?<IPv6address>(?:\h{1,4}:){6}(?<ls32>\h{1,4}:\h{1,4}|(?<IPv4address>(?<dec-octet>[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g<dec-octet>\.\g<dec-octet>\.\g<dec-octet>))|::(?:\h{1,4}:){5}\g<ls32>|\h{1,4}?::(?:\h{1,4}:){4}\g<ls32>|(?:(?:\h{1,4}:){,1}\h{1,4})?::(?:\h{1,4}:){3}\g<ls32>|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g<ls32>|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g<ls32>|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g<ls32>|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?<IPvFuture>v\h+\.[!$&-.0-;=A-Z_a-z~]+)\])|\g<IPv4address>|(?<reg-name>(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])+))?(?::(?<port>\d*))?)(?<path-abempty>(?:\/(?<segment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*))*)|(?<path-absolute>\/(?:(?<segment-nz>(?:%\h\h|[!$&-.0-;=@-Z_a-z~])+)(?:\/\g<segment>)*)?)|(?<path-noscheme>(?<segment-nz-nc>(?:%\h\h|[!$&-.0-9;=@-Z_a-z~])+)(?:\/\g<segment>)*)|(?<path-empty>))(?:\?(?<query>[^#]*))?(?:\#(?<fragment>(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*))?)\z/
+ HOST = %r[
+ (?<IP-literal>\[(?:
+ (?<IPv6address>
+ (?:\h{1,4}:){6}
+ (?<ls32>\h{1,4}:\h{1,4}
+ | (?<IPv4address>(?<dec-octet>[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)
+ \.\g<dec-octet>\.\g<dec-octet>\.\g<dec-octet>)
+ )
+ | ::(?:\h{1,4}:){5}\g<ls32>
+ | \h{1,4}?::(?:\h{1,4}:){4}\g<ls32>
+ | (?:(?:\h{1,4}:)?\h{1,4})?::(?:\h{1,4}:){3}\g<ls32>
+ | (?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g<ls32>
+ | (?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g<ls32>
+ | (?:(?:\h{1,4}:){,4}\h{1,4})?::\g<ls32>
+ | (?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}
+ | (?:(?:\h{1,4}:){,6}\h{1,4})?::
+ )
+ | (?<IPvFuture>v\h++\.[!$&-.0-9:;=A-Z_a-z~]++)
+ )\])
+ | \g<IPv4address>
+ | (?<reg-name>(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])*+)
+ ]x
+
+ USERINFO = /(?:%\h\h|[!$&-.0-9:;=A-Z_a-z~])*+/
+
+ SCHEME = %r[[A-Za-z][+\-.0-9A-Za-z]*+].source
+ SEG = %r[(?:%\h\h|[!$&-.0-9:;=@A-Z_a-z~/])].source
+ SEG_NC = %r[(?:%\h\h|[!$&-.0-9;=@A-Z_a-z~])].source
+ FRAGMENT = %r[(?:%\h\h|[!$&-.0-9:;=@A-Z_a-z~/?])*+].source
+
+ RFC3986_URI = %r[\A
+ (?<seg>#{SEG}){0}
+ (?<URI>
+ (?<scheme>#{SCHEME}):
+ (?<hier-part>//
+ (?<authority>
+ (?:(?<userinfo>#{USERINFO.source})@)?
+ (?<host>#{HOST.source.delete(" \n")})
+ (?::(?<port>\d*+))?
+ )
+ (?<path-abempty>(?:/\g<seg>*+)?)
+ | (?<path-absolute>/((?!/)\g<seg>++)?)
+ | (?<path-rootless>(?!/)\g<seg>++)
+ | (?<path-empty>)
+ )
+ (?:\?(?<query>[^\#]*+))?
+ (?:\#(?<fragment>#{FRAGMENT}))?
+ )\z]x
+
+ RFC3986_relative_ref = %r[\A
+ (?<seg>#{SEG}){0}
+ (?<relative-ref>
+ (?<relative-part>//
+ (?<authority>
+ (?:(?<userinfo>#{USERINFO.source})@)?
+ (?<host>#{HOST.source.delete(" \n")}(?<!/))?
+ (?::(?<port>\d*+))?
+ )
+ (?<path-abempty>(?:/\g<seg>*+)?)
+ | (?<path-absolute>/\g<seg>*+)
+ | (?<path-noscheme>#{SEG_NC}++(?:/\g<seg>*+)?)
+ | (?<path-empty>)
+ )
+ (?:\?(?<query>[^#]*+))?
+ (?:\#(?<fragment>#{FRAGMENT}))?
+ )\z]x
attr_reader :regexp
def initialize
@@ -15,14 +78,14 @@ module URI
begin
uri = uri.to_str
rescue NoMethodError
- raise InvalidURIError, "bad URI(is not URI?): #{uri.inspect}"
+ raise InvalidURIError, "bad URI (is not URI?): #{uri.inspect}"
end
uri.ascii_only? or
raise InvalidURIError, "URI must be ascii only #{uri.dump}"
if m = RFC3986_URI.match(uri)
- query = m["query".freeze]
- scheme = m["scheme".freeze]
- opaque = m["path-rootless".freeze]
+ query = m["query"]
+ scheme = m["scheme"]
+ opaque = m["path-rootless"]
if opaque
opaque << "?#{query}" if query
[ scheme,
@@ -33,80 +96,98 @@ module URI
nil, # path
opaque,
nil, # query
- m["fragment".freeze]
+ m["fragment"]
]
else # normal
[ scheme,
- m["userinfo".freeze],
- m["host".freeze],
- m["port".freeze],
+ m["userinfo"],
+ m["host"],
+ m["port"],
nil, # registry
- (m["path-abempty".freeze] ||
- m["path-absolute".freeze] ||
- m["path-empty".freeze]),
+ (m["path-abempty"] ||
+ m["path-absolute"] ||
+ m["path-empty"]),
nil, # opaque
query,
- m["fragment".freeze]
+ m["fragment"]
]
end
elsif m = RFC3986_relative_ref.match(uri)
[ nil, # scheme
- m["userinfo".freeze],
- m["host".freeze],
- m["port".freeze],
+ m["userinfo"],
+ m["host"],
+ m["port"],
nil, # registry,
- (m["path-abempty".freeze] ||
- m["path-absolute".freeze] ||
- m["path-noscheme".freeze] ||
- m["path-empty".freeze]),
+ (m["path-abempty"] ||
+ m["path-absolute"] ||
+ m["path-noscheme"] ||
+ m["path-empty"]),
nil, # opaque
- m["query".freeze],
- m["fragment".freeze]
+ m["query"],
+ m["fragment"]
]
else
- raise InvalidURIError, "bad URI(is not URI?): #{uri.inspect}"
+ raise InvalidURIError, "bad URI (is not URI?): #{uri.inspect}"
end
end
def parse(uri) # :nodoc:
- scheme, userinfo, host, port,
- registry, path, opaque, query, fragment = self.split(uri)
- scheme_list = URI.scheme_list
- if scheme && scheme_list.include?(uc = scheme.upcase)
- scheme_list[uc].new(scheme, userinfo, host, port,
- registry, path, opaque, query,
- fragment, self)
- else
- Generic.new(scheme, userinfo, host, port,
- registry, path, opaque, query,
- fragment, self)
- end
+ URI.for(*self.split(uri), self)
end
-
def join(*uris) # :nodoc:
uris[0] = convert_to_uri(uris[0])
uris.inject :merge
end
+ # Compatibility for RFC2396 parser
+ def extract(str, schemes = nil, &block) # :nodoc:
+ warn "URI::RFC3986_PARSER.extract is obsolete. Use URI::RFC2396_PARSER.extract explicitly.", uplevel: 1 if $VERBOSE
+ RFC2396_PARSER.extract(str, schemes, &block)
+ end
+
+ # Compatibility for RFC2396 parser
+ def make_regexp(schemes = nil) # :nodoc:
+ warn "URI::RFC3986_PARSER.make_regexp is obsolete. Use URI::RFC2396_PARSER.make_regexp explicitly.", uplevel: 1 if $VERBOSE
+ RFC2396_PARSER.make_regexp(schemes)
+ end
+
+ # Compatibility for RFC2396 parser
+ def escape(str, unsafe = nil) # :nodoc:
+ warn "URI::RFC3986_PARSER.escape is obsolete. Use URI::RFC2396_PARSER.escape explicitly.", uplevel: 1 if $VERBOSE
+ unsafe ? RFC2396_PARSER.escape(str, unsafe) : RFC2396_PARSER.escape(str)
+ end
+
+ # Compatibility for RFC2396 parser
+ def unescape(str, escaped = nil) # :nodoc:
+ warn "URI::RFC3986_PARSER.unescape is obsolete. Use URI::RFC2396_PARSER.unescape explicitly.", uplevel: 1 if $VERBOSE
+ escaped ? RFC2396_PARSER.unescape(str, escaped) : RFC2396_PARSER.unescape(str)
+ end
+
@@to_s = Kernel.instance_method(:to_s)
- def inspect
- @@to_s.bind(self).call
+ if @@to_s.respond_to?(:bind_call)
+ def inspect
+ @@to_s.bind_call(self)
+ end
+ else
+ def inspect
+ @@to_s.bind(self).call
+ end
end
private
def default_regexp # :nodoc:
{
- SCHEME: /\A[A-Za-z][A-Za-z0-9+\-.]*\z/,
- USERINFO: /\A(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*\z/,
- HOST: /\A(?:(?<IP-literal>\[(?:(?<IPv6address>(?:\h{1,4}:){6}(?<ls32>\h{1,4}:\h{1,4}|(?<IPv4address>(?<dec-octet>[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g<dec-octet>\.\g<dec-octet>\.\g<dec-octet>))|::(?:\h{1,4}:){5}\g<ls32>|\h{,4}::(?:\h{1,4}:){4}\g<ls32>|(?:(?:\h{1,4}:)?\h{1,4})?::(?:\h{1,4}:){3}\g<ls32>|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g<ls32>|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g<ls32>|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g<ls32>|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?<IPvFuture>v\h+\.[!$&-.0-;=A-Z_a-z~]+))\])|\g<IPv4address>|(?<reg-name>(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])*))\z/,
- ABS_PATH: /\A\/(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*(?:\/(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*)*\z/,
- REL_PATH: /\A(?:%\h\h|[!$&-.0-;=@-Z_a-z~])+(?:\/(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*)*\z/,
- QUERY: /\A(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*\z/,
- FRAGMENT: /\A(?:%\h\h|[!$&-.0-;=@-Z_a-z~\/?])*\z/,
- OPAQUE: /\A(?:[^\/].*)?\z/,
- PORT: /\A[\x09\x0a\x0c\x0d ]*\d*[\x09\x0a\x0c\x0d ]*\z/,
+ SCHEME: %r[\A#{SCHEME}\z]o,
+ USERINFO: %r[\A#{USERINFO}\z]o,
+ HOST: %r[\A#{HOST}\z]o,
+ ABS_PATH: %r[\A/#{SEG}*+\z]o,
+ REL_PATH: %r[\A(?!/)#{SEG}++\z]o,
+ QUERY: %r[\A(?:%\h\h|[!$&-.0-9:;=@A-Z_a-z~/?])*+\z],
+ FRAGMENT: %r[\A#{FRAGMENT}\z]o,
+ OPAQUE: %r[\A(?:[^/].*)?\z],
+ PORT: /\A[\x09\x0a\x0c\x0d ]*+\d*[\x09\x0a\x0c\x0d ]*\z/,
}
end
diff --git a/lib/uri/uri.gemspec b/lib/uri/uri.gemspec
new file mode 100644
index 0000000000..0d0f897cba
--- /dev/null
+++ b/lib/uri/uri.gemspec
@@ -0,0 +1,42 @@
+begin
+ require_relative "lib/uri/version"
+rescue LoadError # Fallback to load version file in ruby core repository
+ require_relative "version"
+end
+
+Gem::Specification.new do |spec|
+ spec.name = "uri"
+ spec.version = URI::VERSION
+ spec.authors = ["Akira Yamada"]
+ spec.email = ["akira@ruby-lang.org"]
+
+ spec.summary = %q{URI is a module providing classes to handle Uniform Resource Identifiers}
+ spec.description = spec.summary
+
+ github_link = "https://github.com/ruby/uri"
+
+ spec.homepage = github_link
+ spec.licenses = ["Ruby", "BSD-2-Clause"]
+
+ spec.required_ruby_version = '>= 2.5'
+
+ spec.metadata = {
+ "bug_tracker_uri" => "#{github_link}/issues",
+ "changelog_uri" => "#{github_link}/releases",
+ "documentation_uri" => "https://ruby.github.io/uri/",
+ "homepage_uri" => spec.homepage,
+ "source_code_uri" => github_link
+ }
+
+ # Specify which files should be added to the gem when it is released.
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
+ gemspec = File.basename(__FILE__)
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
+ `git ls-files -z 2>#{IO::NULL}`.split("\x0").reject do |file|
+ (file == gemspec) || file.start_with?(*%w[bin/ test/ rakelib/ .github/ .gitignore Gemfile Rakefile])
+ end
+ end
+ spec.bindir = "exe"
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
+ spec.require_paths = ["lib"]
+end
diff --git a/lib/uri/version.rb b/lib/uri/version.rb
new file mode 100644
index 0000000000..1f810602eb
--- /dev/null
+++ b/lib/uri/version.rb
@@ -0,0 +1,6 @@
+module URI
+ # :stopdoc:
+ VERSION = '1.1.1'.freeze
+ VERSION_CODE = VERSION.split('.').map{|s| s.rjust(2, '0')}.join.freeze
+ # :startdoc:
+end
diff --git a/lib/uri/ws.rb b/lib/uri/ws.rb
new file mode 100644
index 0000000000..ff3c554484
--- /dev/null
+++ b/lib/uri/ws.rb
@@ -0,0 +1,83 @@
+# frozen_string_literal: false
+# = uri/ws.rb
+#
+# Author:: Matt Muller <mamuller@amazon.com>
+# License:: You can redistribute it and/or modify it under the same term as Ruby.
+#
+# See URI for general documentation
+#
+
+require_relative 'generic'
+
+module URI
+
+ #
+ # The syntax of WS URIs is defined in RFC6455 section 3.
+ #
+ # Note that the Ruby URI library allows WS URLs containing usernames and
+ # passwords. This is not legal as per the RFC, but used to be
+ # supported in Internet Explorer 5 and 6, before the MS04-004 security
+ # update. See <URL:http://support.microsoft.com/kb/834489>.
+ #
+ class WS < Generic
+ # A Default port of 80 for URI::WS.
+ DEFAULT_PORT = 80
+
+ # An Array of the available components for URI::WS.
+ COMPONENT = %i[
+ scheme
+ userinfo host port
+ path
+ query
+ ].freeze
+
+ #
+ # == Description
+ #
+ # Creates a new URI::WS object from components, with syntax checking.
+ #
+ # The components accepted are userinfo, host, port, path, and query.
+ #
+ # The components should be provided either as an Array, or as a Hash
+ # with keys formed by preceding the component names with a colon.
+ #
+ # If an Array is used, the components must be passed in the
+ # order <code>[userinfo, host, port, path, query]</code>.
+ #
+ # Example:
+ #
+ # uri = URI::WS.build(host: 'www.example.com', path: '/foo/bar')
+ #
+ # uri = URI::WS.build([nil, "www.example.com", nil, "/path", "query"])
+ #
+ # Currently, if passed userinfo components this method generates
+ # invalid WS URIs as per RFC 1738.
+ #
+ def self.build(args)
+ tmp = Util.make_components_hash(self, args)
+ super(tmp)
+ end
+
+ #
+ # == Description
+ #
+ # Returns the full path for a WS URI, as required by Net::HTTP::Get.
+ #
+ # If the URI contains a query, the full path is URI#path + '?' + URI#query.
+ # Otherwise, the path is simply URI#path.
+ #
+ # Example:
+ #
+ # uri = URI::WS.build(path: '/foo/bar', query: 'test=true')
+ # uri.request_uri # => "/foo/bar?test=true"
+ #
+ def request_uri
+ return unless @path
+
+ url = @query ? "#@path?#@query" : @path.dup
+ url.start_with?(?/.freeze) ? url : ?/ + url
+ end
+ end
+
+ register_scheme 'WS', WS
+end
diff --git a/lib/uri/wss.rb b/lib/uri/wss.rb
new file mode 100644
index 0000000000..7cea9d773b
--- /dev/null
+++ b/lib/uri/wss.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: false
+# = uri/wss.rb
+#
+# Author:: Matt Muller <mamuller@amazon.com>
+# License:: You can redistribute it and/or modify it under the same term as Ruby.
+#
+# See URI for general documentation
+#
+
+require_relative 'ws'
+
+module URI
+
+ # The default port for WSS URIs is 443, and the scheme is 'wss:' rather
+ # than 'ws:'. Other than that, WSS URIs are identical to WS URIs;
+ # see URI::WS.
+ class WSS < WS
+ # A Default port of 443 for URI::WSS
+ DEFAULT_PORT = 443
+ end
+
+ register_scheme 'WSS', WSS
+end