Class: HTTPX::Resolver::Resolver

Inherits:
Object
  • Object
show all
Includes:
Loggable, _Selectable
Defined in:
lib/httpx/resolver/resolver.rb,
sig/resolver/resolver.rbs

Overview

Base class for all internal internet name resolvers. It handles basic blocks from the Selectable API.

Direct Known Subclasses

HTTPS, Native, System

Constant Summary collapse

RECORD_TYPES =

Returns:

  • (Hash[ip_family, singleton(Resolv::DNS::Resource)])
{
  Socket::AF_INET6 => Resolv::DNS::Resource::IN::AAAA,
  Socket::AF_INET => Resolv::DNS::Resource::IN::A,
}.freeze
FAMILY_TYPES =

Returns:

  • (Hash[singleton(Resolv::DNS::Resource), String])
{
  Resolv::DNS::Resource::IN::AAAA => "AAAA",
  Resolv::DNS::Resource::IN::A => "A",
}.freeze

Constants included from Loggable

Loggable::COLORS, Loggable::USE_DEBUG_LOG

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Loggable

#log, #log_exception, log_identifiers, #log_redact, #log_redact_body, #log_redact_headers

Methods included from _Selectable

#call, #handle_socket_timeout, #interests, #state, #timeout, #to_io

Constructor Details

#initialize(family, options) ⇒ Resolver

Returns a new instance of Resolver.

Parameters:

  • family (ip_family)
  • options (Options)


36
37
38
39
40
41
# File 'lib/httpx/resolver/resolver.rb', line 36

def initialize(family, options)
  @family = family
  @record_type = RECORD_TYPES[family]
  @options = options
  @connections = []
end

Instance Attribute Details

#current_selector=(value) ⇒ Object (writeonly)

Sets the attribute current_selector

Parameters:

  • value (Selector, nil)

    the value to set the attribute current_selector to.



32
33
34
# File 'lib/httpx/resolver/resolver.rb', line 32

def current_selector=(value)
  @current_selector = value
end

#current_session=(value) ⇒ Object (writeonly)

Sets the attribute current_session

Parameters:

  • value (Session, nil)

    the value to set the attribute current_session to.



32
33
34
# File 'lib/httpx/resolver/resolver.rb', line 32

def current_session=(value)
  @current_session = value
end

#familyip_family (readonly)

Returns the value of attribute family.

Returns:

  • (ip_family)


30
31
32
# File 'lib/httpx/resolver/resolver.rb', line 30

def family
  @family
end

#multiMulti?

Returns the value of attribute multi.

Returns:



34
35
36
# File 'lib/httpx/resolver/resolver.rb', line 34

def multi
  @multi
end

#optionsOptions (readonly)

Returns the value of attribute options.

Returns:



30
31
32
# File 'lib/httpx/resolver/resolver.rb', line 30

def options
  @options
end

Class Method Details

.multi?Boolean

Returns:

  • (Boolean)


25
26
27
# File 'lib/httpx/resolver/resolver.rb', line 25

def multi?
  true
end

Instance Method Details

#closevoid Also known as: terminate

This method returns an undefined value.



51
# File 'lib/httpx/resolver/resolver.rb', line 51

def close; end

#close_or_resolvevoid

This method returns an undefined value.



60
# File 'sig/resolver/resolver.rbs', line 60

def close_or_resolve: () -> void

#closed?Boolean

Returns:

  • (Boolean)


65
66
67
# File 'lib/httpx/resolver/resolver.rb', line 65

def closed?
  true
end

#disconnectvoid

This method returns an undefined value.



205
206
207
208
209
210
211
# File 'lib/httpx/resolver/resolver.rb', line 205

def disconnect
  close

  return unless closed?

  @current_session.deselect_resolver(self, @current_selector)
end

#each_connection {|connection| ... } ⇒ void

This method returns an undefined value.

Yields:

Yield Parameters:

Yield Returns:

  • (void)


43
44
45
46
47
48
49
# File 'lib/httpx/resolver/resolver.rb', line 43

def each_connection(&block)
  enum_for(__method__) unless block

  return unless @connections

  @connections.each(&block)
end

#early_resolve(connection, hostname: connection.peer.host) ⇒ Boolean

rubocop:disable Naming/PredicateMethod

Parameters:

  • connection (Connection)
  • hostname: (String) (defaults to: connection.peer.host)

Returns:

  • (Boolean)


136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/httpx/resolver/resolver.rb', line 136

def early_resolve(connection, hostname: connection.peer.host) # rubocop:disable Naming/PredicateMethod
  addresses = @resolver_options[:cache] && (connection.addresses || @options.resolver_cache.resolve(hostname))

  return false unless addresses

  addresses = addresses.select { |addr| addr.family == @family }

  return false if addresses.empty?

  emit_addresses(connection, @family, addresses, true)

  true
end

#emit_addresses(connection, family, addresses, early_resolve = false) ⇒ void

This method returns an undefined value.

Parameters:

  • connection (Connection)
  • family (ip_family)
  • (Array[ipaddr])
  • early_resolve (Boolean) (defaults to: false)


77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/httpx/resolver/resolver.rb', line 77

def emit_addresses(connection, family, addresses, early_resolve = false)
  addresses.map! { |address| address.is_a?(Resolver::Entry) ? address : Resolver::Entry.new(address) }

  # double emission check, but allow early resolution to work
  conn_addrs = connection.addresses
  return if !early_resolve && conn_addrs && !conn_addrs.empty? && !addresses.intersect?(conn_addrs)

  log do
    "resolver #{FAMILY_TYPES[RECORD_TYPES[family]]}: " \
      "answer #{connection.peer.host}: #{addresses.inspect} (early resolve: #{early_resolve})"
  end

  # do not apply resolution delay for non-dns name resolution
  if !early_resolve &&
     # just in case...
     @current_selector &&
     # resolution delay only applies to IPv4
     family == Socket::AF_INET &&
     # connection already has addresses and initiated/ended handshake
     !connection.io &&
     # no need to delay if not supporting dual stack / multi-homed IP
     (connection.options.ip_families || Resolver.supported_ip_families).size > 1 &&
     # connection URL host is already the IP (early resolve included perhaps?)
     addresses.first.to_s != connection.peer.host.to_s
    log { "resolver #{FAMILY_TYPES[RECORD_TYPES[family]]}: applying resolution delay..." }

    @current_selector.after(0.05) do
      # double emission check
      unless connection.addresses && addresses.intersect?(connection.addresses)
        emit_resolved_connection(connection, addresses, early_resolve)
      end
    end
  else
    emit_resolved_connection(connection, addresses, early_resolve)
  end
end

#emit_connection_error(connection, error) ⇒ void

This method returns an undefined value.

Parameters:



199
200
201
202
203
# File 'lib/httpx/resolver/resolver.rb', line 199

def emit_connection_error(connection, error)
  return connection.handle_connect_error(error) if connection.connecting?

  connection.on_error(error)
end

#emit_resolve_error(connection, hostname = connection.peer.host, ex = nil) ⇒ void

This method returns an undefined value.

Parameters:

  • connection (Connection)
  • hostname (String) (defaults to: connection.peer.host)
  • (StandardError)


182
183
184
# File 'lib/httpx/resolver/resolver.rb', line 182

def emit_resolve_error(connection, hostname = connection.peer.host, ex = nil)
  emit_connection_error(connection, resolve_error(hostname, ex))
end

#emit_resolved_connection(connection, addresses, early_resolve) ⇒ void

This method returns an undefined value.

Parameters:



165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
# File 'lib/httpx/resolver/resolver.rb', line 165

def emit_resolved_connection(connection, addresses, early_resolve)
  begin
    connection.addresses = addresses

    return if connection.state == :closed

    resolve_connection(connection)
  rescue StandardError => e
    if early_resolve
      connection.force_close
      throw(:resolve_error, e)
    else
      emit_connection_error(connection, e)
    end
  end
end

#empty?Boolean

Returns:

  • (Boolean)


69
70
71
# File 'lib/httpx/resolver/resolver.rb', line 69

def empty?
  true
end

#force_close(*args) ⇒ Object



59
60
61
62
63
# File 'lib/httpx/resolver/resolver.rb', line 59

def force_close(*args)
  while (connection = @connections.shift)
    connection.force_close(*args)
  end
end

#handle_error(error) ⇒ void

This method returns an undefined value.

Parameters:



114
115
116
117
118
119
120
121
122
123
124
# File 'lib/httpx/resolver/resolver.rb', line 114

def handle_error(error)
  if error.respond_to?(:connection) &&
     error.respond_to?(:host)
    @connections.delete(error.connection)
    emit_resolve_error(error.connection, error.host, error)
  else
    while (connection = @connections.shift)
      emit_resolve_error(connection, connection.peer.host, error)
    end
  end
end

#inflight?Boolean

Returns:

  • (Boolean)


73
74
75
# File 'lib/httpx/resolver/resolver.rb', line 73

def inflight?
  false
end

#initial_callObject



55
56
57
# File 'lib/httpx/resolver/resolver.rb', line 55

def initial_call
  call
end

#lazy_resolve(connection) ⇒ void

This method returns an undefined value.

Parameters:



150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/httpx/resolver/resolver.rb', line 150

def lazy_resolve(connection)
  return unless @current_session && @current_selector

  conn_to_resolve = @current_session.try_clone_connection(connection, @current_selector, @family)
  self << conn_to_resolve

  return if empty?

  # both the resolver and the connection it's resolving must be pinned to the session
  @current_session.pin(conn_to_resolve, @current_selector)
  @current_session.select_resolver(self, @current_selector)
end

#on_error(error) ⇒ Object



131
132
133
134
# File 'lib/httpx/resolver/resolver.rb', line 131

def on_error(error)
  handle_error(error)
  disconnect
end

#on_io_error(e) ⇒ Object



126
127
128
129
# File 'lib/httpx/resolver/resolver.rb', line 126

def on_io_error(e)
  on_error(e)
  force_close(true)
end

#resolvevoid

This method returns an undefined value.

Parameters:



46
# File 'sig/resolver/resolver.rbs', line 46

def resolve: (?Connection connection, ?String hostname) -> void

#resolve_connection(connection) ⇒ void

This method returns an undefined value.

Parameters:



195
196
197
# File 'lib/httpx/resolver/resolver.rb', line 195

def resolve_connection(connection)
  @current_session.__send__(:on_resolver_connection, connection, @current_selector)
end

#resolve_error(hostname, ex = nil) ⇒ ResolveError, ResolveTimeoutError

Parameters:

  • hostname (String)
  • (StandardError, nil)

Returns:



186
187
188
189
190
191
192
193
# File 'lib/httpx/resolver/resolver.rb', line 186

def resolve_error(hostname, ex = nil)
  return ex if ex.is_a?(ResolveError) || ex.is_a?(ResolveTimeoutError)

  message = ex ? ex.message : "Can't resolve #{hostname}"
  error = ResolveError.new(message)
  error.set_backtrace(ex ? ex.backtrace : caller)
  error
end