class Endpoint
Represents a way to connect to a remote HTTP server.
Definitions
def self.parse(string, endpoint = nil, **options)
Parse a URL string into an endpoint.
Signature
-
parameter
stringString The URL to parse.
-
parameter
endpointIO::Endpoint::Generic | Nil An optional underlying endpoint to use.
-
parameter
optionsHash Additional options to pass to
Async::HTTP::Endpoint#initialize.-
returns
Endpoint The parsed endpoint.
Implementation
def self.parse(string, endpoint = nil, **options)
url = URI.parse(string).normalize
return self.new(url, endpoint, **options)
end
def self.for(scheme, hostname, path = "/", **options)
Construct an endpoint with a specified scheme, hostname, optional path, and options.
Signature
-
parameter
schemeString The scheme to use, e.g. "http" or "https".
-
parameter
hostnameString The hostname to connect to (or bind to).
-
parameter
*optionsHash Additional options, passed to
Async::HTTP::Endpoint#initialize.
Implementation
def self.for(scheme, hostname, path = "/", **options)
# TODO: Consider using URI.for once it becomes available:
uri_klass = SCHEMES.fetch(scheme.downcase) do
raise ArgumentError, "Unsupported scheme: #{scheme.inspect}"
end
self.new(
uri_klass.new(scheme, nil, hostname, nil, nil, path, nil, nil, nil).normalize,
**options
)
end
def self.[](url)
Coerce the given object into an endpoint.
Signature
-
parameter
urlString | Endpoint The URL or endpoint to convert.
Implementation
def self.[](url)
if url.is_a?(Endpoint)
return url
else
Endpoint.parse(url.to_s)
end
end
def initialize(url, endpoint = nil, **options)
Signature
-
option
schemeString the scheme to use, overrides the URL scheme.
-
option
hostnameString the hostname to connect to (or bind to), overrides the URL hostname (used for SNI).
-
option
portInteger the port to bind to, overrides the URL port.
-
option
ssl_contextOpenSSL::SSL::SSLContext the context to use for TLS.
-
option
alpn_protocolsArray(String) the alpn protocols to negotiate.
Implementation
def initialize(url, endpoint = nil, **options)
super(**options)
raise ArgumentError, "URL must be absolute (include scheme, host): #{url}" unless url.absolute?
@url = url
if endpoint
@endpoint = self.build_endpoint(endpoint)
else
@endpoint = nil
end
end
def to_url
Signature
-
returns
URI The URL representation of this endpoint, including port if non-default.
Implementation
def to_url
url = @url.dup
unless default_port?
url.port = self.port
end
return url
end
def to_s
Signature
-
returns
String A short string representation of this endpoint.
Implementation
def to_s
"\#<#{self.class} #{self.to_url} #{@options}>"
end
def inspect
Signature
-
returns
String A detailed string representation of this endpoint.
Implementation
def inspect
"\#<#{self.class} #{self.to_url} #{@options.inspect}>"
end
def address
Signature
-
returns
Addrinfo The address of the underlying endpoint.
Implementation
def address
endpoint.address
end
def secure?
Signature
-
returns
Boolean Whether this endpoint uses a secure protocol (HTTPS or WSS).
Implementation
def secure?
["https", "wss"].include?(self.scheme)
end
def protocol
Signature
-
returns
Protocol The protocol to use for this endpoint.
Implementation
def protocol
@options.fetch(:protocol) do
if secure?
Protocol::HTTPS
else
Protocol::HTTP
end
end
end
def default_port
Signature
-
returns
Integer The default port for this endpoint's scheme.
Implementation
def default_port
secure? ? 443 : 80
end
def default_port?
Signature
-
returns
Boolean Whether the endpoint's port is the default for its scheme.
Implementation
def default_port?
port == default_port
end
def port
Signature
-
returns
Integer The port number for this endpoint.
Implementation
def port
@options[:port] || @url.port || default_port
end
def hostname
The hostname is the server we are connecting to:
Implementation
def hostname
@options[:hostname] || @url.hostname
end
def scheme
Signature
-
returns
String The URL scheme, e.g.
"http"or"https".
Implementation
def scheme
@options[:scheme] || @url.scheme
end
def path
Return the path and query components of the given URL.
Implementation
def path
buffer = @url.path || "/"
if query = @url.query
buffer = "#{buffer}?#{query}"
end
return buffer
end
def alpn_protocols
Signature
-
returns
Array(String) The ALPN protocol names for TLS negotiation.
Implementation
def alpn_protocols
@options.fetch(:alpn_protocols){self.protocol.names}
end
def localhost?
Signature
-
returns
Boolean Whether the endpoint refers to a localhost address.
Implementation
def localhost?
@url.hostname =~ /^(.*?\.)?localhost\.?$/
end
def ssl_verify_mode
We don't try to validate peer certificates when talking to localhost because they would always be self-signed.
Implementation
def ssl_verify_mode
if self.localhost?
OpenSSL::SSL::VERIFY_NONE
else
OpenSSL::SSL::VERIFY_PEER
end
end
def ssl_context
Signature
-
returns
OpenSSL::SSL::SSLContext The SSL context for TLS connections.
Implementation
def ssl_context
@options[:ssl_context] || OpenSSL::SSL::SSLContext.new.tap do |context|
if alpn_protocols = self.alpn_protocols
context.alpn_protocols = alpn_protocols
end
context.set_params(
verify_mode: self.ssl_verify_mode
)
end
end
def build_endpoint(endpoint = nil)
Build a suitable endpoint, optionally wrapping in TLS for secure connections.
Signature
-
parameter
endpointIO::Endpoint::Generic | Nil An optional underlying endpoint to wrap.
-
returns
IO::Endpoint::Generic The constructed endpoint.
Implementation
def build_endpoint(endpoint = nil)
endpoint ||= tcp_endpoint
if secure?
# Wrap it in SSL:
return ::IO::Endpoint::SSLEndpoint.new(endpoint,
ssl_context: self.ssl_context,
hostname: @url.hostname,
timeout: self.timeout,
)
end
return endpoint
end
def endpoint
Signature
-
returns
IO::Endpoint::Generic The resolved endpoint, built on demand.
Implementation
def endpoint
@endpoint ||= build_endpoint
end
def endpoint=(endpoint)
Set the underlying endpoint, wrapping it as needed.
Signature
-
parameter
endpointIO::Endpoint::Generic The endpoint to assign.
Implementation
def endpoint=(endpoint)
@endpoint = build_endpoint(endpoint)
end
def bind(*arguments, &block)
Bind to the endpoint.
Implementation
def bind(*arguments, &block)
endpoint.bind(*arguments, &block)
end
def connect(&block)
Connect to the endpoint.
Implementation
def connect(&block)
endpoint.connect(&block)
end
def each
Enumerate all resolved endpoints.
Signature
-
yields
{|endpoint| ...} Each resolved endpoint.
-
parameter
endpointEndpoint The resolved endpoint.
-
parameter
Implementation
def each
return to_enum unless block_given?
self.tcp_endpoint.each do |endpoint|
yield self.class.new(@url, endpoint, **@options)
end
end
def key
Signature
-
returns
Array A key suitable for identifying this endpoint in a hash.
Implementation
def key
[@url, @options]
end
def eql?(other)
Signature
-
returns
Boolean Whether two endpoints are equal.
Implementation
def eql? other
self.key.eql? other.key
end
def hash
Signature
-
returns
Integer The hash code for this endpoint.
Implementation
def hash
self.key.hash
end