mirror of
https://github.com/HoneyryderChuck/httpx.git
synced 2025-10-07 00:05:02 -04:00
149 lines
4.9 KiB
Ruby
149 lines
4.9 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
#
|
|
# domain_name.rb - Domain Name manipulation library for Ruby
|
|
#
|
|
# Copyright (C) 2011-2017 Akinori MUSHA, All rights reserved.
|
|
#
|
|
# Redistribution and use in source and binary forms, with or without
|
|
# modification, are permitted provided that the following conditions
|
|
# are met:
|
|
# 1. Redistributions of source code must retain the above copyright
|
|
# notice, this list of conditions and the following disclaimer.
|
|
# 2. Redistributions in binary form must reproduce the above copyright
|
|
# notice, this list of conditions and the following disclaimer in the
|
|
# documentation and/or other materials provided with the distribution.
|
|
#
|
|
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
# SUCH DAMAGE.
|
|
|
|
require "ipaddr"
|
|
|
|
module HTTPX
|
|
# Represents a domain name ready for extracting its registered domain
|
|
# and TLD.
|
|
class DomainName
|
|
include Comparable
|
|
|
|
# The full host name normalized, ASCII-ized and downcased using the
|
|
# Unicode NFC rules and the Punycode algorithm. If initialized with
|
|
# an IP address, the string representation of the IP address
|
|
# suitable for opening a connection to.
|
|
attr_reader :hostname
|
|
|
|
# The Unicode representation of the #hostname property.
|
|
#
|
|
# :attr_reader: hostname_idn
|
|
|
|
# The least "universally original" domain part of this domain name.
|
|
# For example, "example.co.uk" for "www.sub.example.co.uk". This
|
|
# may be nil if the hostname does not have one, like when it is an
|
|
# IP address, an effective TLD or higher itself, or of a
|
|
# non-canonical domain.
|
|
attr_reader :domain
|
|
|
|
DOT = "." # :nodoc:
|
|
|
|
class << self
|
|
def new(domain)
|
|
return domain if domain.is_a?(self)
|
|
|
|
super(domain)
|
|
end
|
|
|
|
# Normalizes a _domain_ using the Punycode algorithm as necessary.
|
|
# The result will be a downcased, ASCII-only string.
|
|
def normalize(domain)
|
|
domain = domain.ascii_only? ? domain : domain.chomp(DOT).unicode_normalize(:nfc)
|
|
Punycode.encode_hostname(domain).downcase
|
|
end
|
|
end
|
|
|
|
# Parses _hostname_ into a DomainName object. An IP address is also
|
|
# accepted. An IPv6 address may be enclosed in square brackets.
|
|
def initialize(hostname)
|
|
hostname = String(hostname)
|
|
|
|
raise ArgumentError, "domain name must not start with a dot: #{hostname}" if hostname.start_with?(DOT)
|
|
|
|
begin
|
|
@ipaddr = IPAddr.new(hostname)
|
|
@hostname = @ipaddr.to_s
|
|
return
|
|
rescue IPAddr::Error
|
|
nil
|
|
end
|
|
|
|
@hostname = DomainName.normalize(hostname)
|
|
tld = if (last_dot = @hostname.rindex(DOT))
|
|
@hostname[(last_dot + 1)..-1]
|
|
else
|
|
@hostname
|
|
end
|
|
|
|
# unknown/local TLD
|
|
@domain = if last_dot
|
|
# fallback - accept cookies down to second level
|
|
# cf. http://www.dkim-reputation.org/regdom-libs/
|
|
if (penultimate_dot = @hostname.rindex(DOT, last_dot - 1))
|
|
@hostname[(penultimate_dot + 1)..-1]
|
|
else
|
|
@hostname
|
|
end
|
|
else
|
|
# no domain part - must be a local hostname
|
|
tld
|
|
end
|
|
end
|
|
|
|
# Checks if the server represented by this domain is qualified to
|
|
# send and receive cookies with a domain attribute value of
|
|
# _domain_. A true value given as the second argument represents
|
|
# cookies without a domain attribute value, in which case only
|
|
# hostname equality is checked.
|
|
def cookie_domain?(domain, host_only = false)
|
|
# RFC 6265 #5.3
|
|
# When the user agent "receives a cookie":
|
|
return self == @domain if host_only
|
|
|
|
domain = DomainName.new(domain)
|
|
|
|
# RFC 6265 #5.1.3
|
|
# Do not perform subdomain matching against IP addresses.
|
|
@hostname == domain.hostname if @ipaddr
|
|
|
|
# RFC 6265 #4.1.1
|
|
# Domain-value must be a subdomain.
|
|
@domain && self <= domain && domain <= @domain ? true : false
|
|
end
|
|
|
|
# def ==(other)
|
|
# other = DomainName.new(other)
|
|
# other.hostname == @hostname
|
|
# end
|
|
|
|
def <=>(other)
|
|
other = DomainName.new(other)
|
|
othername = other.hostname
|
|
if othername == @hostname
|
|
0
|
|
elsif @hostname.end_with?(othername) && @hostname[-othername.size - 1, 1] == DOT
|
|
# The other is higher
|
|
-1
|
|
else
|
|
# The other is lower
|
|
1
|
|
end
|
|
end
|
|
end
|
|
end
|