Class: HTTPX::Resolver::Resolver

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

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 =
{
  Socket::AF_INET6 => Resolv::DNS::Resource::IN::AAAA,
  Socket::AF_INET => Resolv::DNS::Resource::IN::A,
}.freeze
FAMILY_TYPES =
{
  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_redact, #log_redact_body, #log_redact_headers

Constructor Details

#initialize(family, options) ⇒ Resolver

Returns a new instance of Resolver.



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

    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

    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

#familyObject (readonly)

Returns the value of attribute family.



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

def family
  @family
end

#multiObject

Returns the value of attribute multi.



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

def multi
  @multi
end

#optionsObject (readonly)

Returns the value of attribute options.



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

#closeObject Also known as: terminate



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

def close; end

#closed?Boolean

Returns:

  • (Boolean)


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

def closed?
  true
end

#each_connection(&block) ⇒ Object



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) ⇒ Object

rubocop:disable Naming/PredicateMethod



132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/httpx/resolver/resolver.rb', line 132

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) ⇒ Object



73
74
75
76
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
# File 'lib/httpx/resolver/resolver.rb', line 73

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

#empty?Boolean

Returns:

  • (Boolean)


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

def empty?
  true
end

#force_close(*args) ⇒ Object



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

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

#handle_error(error) ⇒ Object



110
111
112
113
114
115
116
117
118
119
120
# File 'lib/httpx/resolver/resolver.rb', line 110

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)


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

def inflight?
  false
end

#lazy_resolve(connection) ⇒ Object



146
147
148
149
150
151
152
153
154
155
156
157
# File 'lib/httpx/resolver/resolver.rb', line 146

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



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

def on_error(error)
  handle_error(error)
  disconnect
end

#on_io_error(e) ⇒ Object



122
123
124
125
# File 'lib/httpx/resolver/resolver.rb', line 122

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