# frozen_string_literal: true
# Author:: Akira Yamada <akira@ruby-lang.org>
# License:: You can redistribute it and/or modify it under the same term as Ruby.
# See URI for general documentation
require_relative 'common'
autoload :IPSocket, 'socket'
autoload :IPAddr, 'ipaddr'
# Base class for all URI classes.
# Implements generic URI syntax as per RFC 2396.
# A Default port of nil for URI::Generic.
# An Array of the available components for URI::Generic.
:userinfo, :host, :port, :registry,
# Components of the URI in the order.
USE_REGISTRY = false # :nodoc:
def self.use_registry # :nodoc:
# 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.
rescue InvalidComponentError
return self.build(args.collect{|x|
elsif args.kind_of?(Hash)
args.each do |key, value|
DEFAULT_PARSER.escape(value)
# Creates a new URI::Generic instance from components of URI::Generic
# with check. Components are: scheme, userinfo, host, port, registry, path,
# opaque, query, and fragment. You can provide arguments either by an Array or a Hash.
# See ::new for hash keys to use or for order of array items.
if args.kind_of?(Array) &&
args.size == ::URI::Generic::COMPONENT.size
elsif args.kind_of?(Hash)
tmp = ::URI::Generic::COMPONENT.collect do |c|
component = self.class.component rescue ::URI::Generic::COMPONENT
"expected Array of or Hash of components of #{self.class} (#{component.join(', ')})"
# Protocol scheme, i.e. 'http','ftp','mailto' and so on.
# User name and password, i.e. 'sdmitry:bla'.
# Registry of naming authorities.
# Part of the URI after '#' character.
# Parser for internal use [URI::DEFAULT_PARSER by default].
# Check arguments [false by default].
# Creates a new URI::Generic instance from ``generic'' components without check.
userinfo, host, port, registry,
@parser = parser == DEFAULT_PARSER ? nil : parser
self.set_userinfo(userinfo)
"the scheme #{@scheme} does not accept registry part: #{registry} (or bad hostname?)"
self.set_path('') if !@path && !@opaque # (see RFC2396 Section 5.2)
self.set_port(self.default_port) if self.default_port && !@port
# Returns the scheme component of the URI.
# URI("http://foo/bar/baz").scheme #=> "http"
# Returns the host component of the URI.
# URI("http://foo/bar/baz").host #=> "foo"
# It returns nil if no host component exists.
# URI("mailto:foo@example.org").host #=> nil
# The component does not contain the port number.
# URI("http://foo:8080/bar/baz").host #=> "foo"
# Since IPv6 addresses are wrapped with brackets in URIs,
# this method returns IPv6 addresses wrapped with brackets.
# This form is not appropriate to pass to socket methods such as TCPSocket.open.
# If unwrapped host names are required, use the #hostname method.
# URI("http://[::1]/bar/baz").host #=> "[::1]"
# URI("http://[::1]/bar/baz").hostname #=> "::1"
# Returns the port component of the URI.
# URI("http://foo/bar/baz").port #=> 80
# URI("http://foo:8080/bar/baz").port #=> 8080
# Returns the path component of the URI.
# URI("http://foo/bar/baz").path #=> "/bar/baz"
# Returns the query component of the URI.
# URI("http://foo/bar/baz?search=FooBar").query #=> "search=FooBar"
# Returns the opaque part of the URI.
# URI("mailto:foo@example.org").opaque #=> "foo@example.org"
# URI("http://foo/bar/baz").opaque #=> nil
# The portion of the path that does not make use of the slash '/'.
# The path typically refers to an absolute path or an opaque part.
# (See RFC2396 Section 3 and 5.2.)
# Returns the fragment component of the URI.
# URI("http://foo/bar/baz?search=FooBar#ponies").fragment #=> "ponies"
# Returns the parser to be used.
# Unless a URI::Parser is defined, DEFAULT_PARSER is used.
if !defined?(@parser) || !@parser
@parser || DEFAULT_PARSER
# Replaces self by other URI object.
if self.class != oth.class
raise ArgumentError, "expected #{self.class} object"
self.__send__("#{c}=", oth.__send__(c))
# Components of the URI in the order.
# Checks the scheme +v+ component against the URI::Parser Regexp for :SCHEME.
if v && parser.regexp[:SCHEME] !~ v
raise InvalidComponentError,
"bad component(expected scheme component): #{v}"
# Protected setter for the scheme component +v+.
# See also URI::Generic.scheme=.
# Public setter for the scheme component +v+
# See also URI::Generic.check_scheme.
# uri = URI.parse("http://my.example.com")
# uri.to_s #=> "https://my.example.com"
# Checks the +user+ and +password+.
# If +password+ is not provided, then +user+ is
# split, using URI::Generic.split_userinfo, to
# pull +user+ and +password.
# See also URI::Generic.check_user, URI::Generic.check_password.
def check_userinfo(user, password = nil)
user, password = split_userinfo(user)
check_password(password, user)
# Checks the user +v+ component for RFC2396 compliance
# and against the URI::Parser Regexp for :USERINFO.
# Can not have a registry or opaque component defined,
# with a user component defined.
"can not set user with opaque"
if parser.regexp[:USERINFO] !~ v
raise InvalidComponentError,
"bad component(expected userinfo component or user component): #{v}"
# Checks the password +v+ component for RFC2396 compliance
# and against the URI::Parser Regexp for :USERINFO.
# Can not have a registry or opaque component defined,
# with a user component defined.
def check_password(v, user = @user)
"can not set password with opaque"
"password component depends user component"
if parser.regexp[:USERINFO] !~ v
raise InvalidComponentError,
# Sets userinfo, argument is string like 'name:pass'.
check_userinfo(*userinfo)
# Public setter for the +user+ component
# See also URI::Generic.check_user.
# uri = URI.parse("http://john:S3nsit1ve@my.example.com")
# uri.to_s #=> "http://sam:V3ry_S3nsit1ve@my.example.com"
# Public setter for the +password+ component
# See also URI::Generic.check_password.
# uri = URI.parse("http://john:S3nsit1ve@my.example.com")
# uri.password = "V3ry_S3nsit1ve"
# uri.to_s #=> "http://john:V3ry_S3nsit1ve@my.example.com"