diff options
author | pluralcafe-docker <git@plural.cafe> | 2018-12-02 08:24:11 +0000 |
---|---|---|
committer | pluralcafe-docker <git@plural.cafe> | 2018-12-02 08:24:11 +0000 |
commit | ae737d94ac10284738928109c11f43aeecca8a0b (patch) | |
tree | ebe739f663d259e0f2aaf2e2f2984a8d0bc9cf1e /app/lib/request.rb | |
parent | 384a602fd4117a73338542c59985f54acf5fb3f8 (diff) | |
parent | 4b85bf12ab7005a5a6b4472b8aeaf022ce042ad7 (diff) |
Merge branch 'glitch'
Diffstat (limited to 'app/lib/request.rb')
-rw-r--r-- | app/lib/request.rb | 52 |
1 files changed, 42 insertions, 10 deletions
diff --git a/app/lib/request.rb b/app/lib/request.rb index 73b495ce1..4a81773e3 100644 --- a/app/lib/request.rb +++ b/app/lib/request.rb @@ -2,6 +2,17 @@ require 'ipaddr' require 'socket' +require 'resolv' + +# Monkey-patch the HTTP.rb timeout class to avoid using a timeout block +# around the Socket#open method, since we use our own timeout blocks inside +# that method +class HTTP::Timeout::PerOperation + def connect(socket_class, host, port, nodelay = false) + @socket = socket_class.open(host, port) + @socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1) if nodelay + end +end class Request REQUEST_TARGET = '(request-target)' @@ -45,7 +56,7 @@ class Request end begin - yield response.extend(ClientLimit) + yield response.extend(ClientLimit) if block_given? ensure http_client.close end @@ -94,7 +105,11 @@ class Request end def timeout - { connect: 1, read: 10, write: 10 } + # We enforce a 1s timeout on DNS resolving, 10s timeout on socket opening + # and 5s timeout on the TLS handshake, meaning the worst case should take + # about 16s in total + + { connect: 5, read: 10, write: 10 } end def http_client @@ -139,17 +154,34 @@ class Request class Socket < TCPSocket class << self def open(host, *args) - return super host, *args if thru_hidden_service? host + return super(host, *args) if thru_hidden_service?(host) + outer_e = nil - Addrinfo.foreach(host, nil, nil, :SOCK_STREAM) do |address| - begin - raise Mastodon::HostValidationError if PrivateAddressCheck.private_address? IPAddr.new(address.ip_address) - return super address.ip_address, *args - rescue => e - outer_e = e + + Resolv::DNS.open do |dns| + dns.timeouts = 1 + + addresses = dns.getaddresses(host).take(2) + time_slot = 10.0 / addresses.size + + addresses.each do |address| + begin + raise Mastodon::HostValidationError if PrivateAddressCheck.private_address?(IPAddr.new(address.to_s)) + + ::Timeout.timeout(time_slot, HTTP::TimeoutError) do + return super(address.to_s, *args) + end + rescue => e + outer_e = e + end end end - raise outer_e if outer_e + + if outer_e + raise outer_e + else + raise SocketError, "No address for #{host}" + end end alias new open |