Skip to content

Commit

Permalink
Implement DNS failover handling feature (#638)
Browse files Browse the repository at this point in the history
  • Loading branch information
midnight-wonderer committed Sep 10, 2021
1 parent d6186a5 commit b461810
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 11 deletions.
32 changes: 25 additions & 7 deletions lib/http/timeout/global.rb
@@ -1,7 +1,8 @@
# frozen_string_literal: true

require "timeout"
require "io/wait"
require "resolv"
require "timeout"

require "http/timeout/null"

Expand All @@ -12,21 +13,38 @@ def initialize(*args)
super

@timeout = @time_left = options.fetch(:global_timeout)
@dns_resolver = options.fetch(:dns_resolver) do
::Resolv.method(:getaddresses)
end
end

# To future me: Don't remove this again, past you was smarter.
def reset_counter
@time_left = @timeout
end

def connect(socket_class, host, port, nodelay = false)
def connect(socket_class, host_name, *args)
connect_operation = lambda do |host_address|
::Timeout.timeout(@time_left, TimeoutError) do
super(socket_class, host_address, *args)
end
end
host_addresses = @dns_resolver.call(host_name)
# ensure something to iterates
trying_targets = host_addresses.empty? ? [host_name] : host_addresses
reset_timer
::Timeout.timeout(@time_left, TimeoutError) do
@socket = socket_class.open(host, port)
@socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1) if nodelay
trying_iterator = trying_targets.lazy
error = nil
begin
connect_operation.call(trying_iterator.next).tap do
log_time
end
rescue TimeoutError => e
error = e
retry
rescue ::StopIteration
raise error
end

log_time
end

def connect_ssl
Expand Down
26 changes: 22 additions & 4 deletions lib/http/timeout/per_operation.rb
@@ -1,5 +1,6 @@
# frozen_string_literal: true

require "resolv"
require "timeout"

require "http/timeout/null"
Expand All @@ -17,12 +18,29 @@ def initialize(*args)
@read_timeout = options.fetch(:read_timeout, READ_TIMEOUT)
@write_timeout = options.fetch(:write_timeout, WRITE_TIMEOUT)
@connect_timeout = options.fetch(:connect_timeout, CONNECT_TIMEOUT)
@dns_resolver = options.fetch(:dns_resolver) do
::Resolv.method(:getaddresses)
end
end

def connect(socket_class, host, port, nodelay = false)
::Timeout.timeout(@connect_timeout, TimeoutError) do
@socket = socket_class.open(host, port)
@socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1) if nodelay
def connect(socket_class, host_name, *args)
connect_operation = lambda do |host_address|
::Timeout.timeout(@connect_timeout, TimeoutError) do
super(socket_class, host_address, *args)
end
end
host_addresses = @dns_resolver.call(host_name)
# ensure something to iterates
trying_targets = host_addresses.empty? ? [host_name] : host_addresses
trying_iterator = trying_targets.lazy
error = nil
begin
connect_operation.call(trying_iterator.next)
rescue TimeoutError => e
error = e
retry
rescue ::StopIteration
raise error
end
end

Expand Down

0 comments on commit b461810

Please sign in to comment.