making the connection ping when keep alive timeout is exceeded; this way if the connection is still available, it's reused

This commit is contained in:
HoneyryderChuck 2020-05-03 15:21:14 +01:00
parent 754bb6b5ed
commit bb310dc106
4 changed files with 41 additions and 9 deletions

View File

@ -88,8 +88,6 @@ module HTTPX
return false if exhausted?
return false if @keep_alive_timer && @keep_alive_timer.fires_in.negative?
(
(
@origins.include?(uri.origin) &&
@ -107,8 +105,6 @@ module HTTPX
return false if exhausted?
return false if @keep_alive_timer && @keep_alive_timer.fires_in.negative?
!(@io.addresses & connection.addresses).empty? && @options == connection.options
end
@ -229,8 +225,18 @@ module HTTPX
def send(request)
if @parser && !@write_buffer.full?
request.headers["alt-used"] = @origin.authority if match_altsvcs?(request.uri)
if @keep_alive_timer
# when pushing a request into an existing connection, we have to check whether there
# is the possibility that the connection might have extended the keep alive timeout.
# for such cases, we want to ping for availability before deciding to shovel requests.
if @keep_alive_timer.fires_in.negative?
parser.ping
return
end
@keep_alive_timer.pause
end
@inflight += 1
@keep_alive_timer.pause if @keep_alive_timer
parser.send(request)
else
@pending << request
@ -361,6 +367,8 @@ module HTTPX
emit(:altsvc, alt_origin, origin, alt_params)
end
parser.on(:pong, &method(:send_pending))
parser.on(:promise) do |request, stream|
request.emit(:promise, parser, stream)
end
@ -469,8 +477,8 @@ module HTTPX
else
@keep_alive_timer = @timers.after(@keep_alive_timeout) do
unless @inflight.zero?
log { "(#{object_id})) keep alive timeout expired, closing..." }
reset
log { "(#{@origin}): keep alive timeout expired" }
parser.ping
end
end
end

View File

@ -1,5 +1,6 @@
# frozen_string_literal: true
require "securerandom"
require "io/wait"
require "http/2/next"
@ -109,6 +110,11 @@ module HTTPX
end
end
def ping
@ping_payload = SecureRandom.bytes(8)
@connection.ping(@ping_payload)
end
private
def send_pending
@ -143,6 +149,7 @@ module HTTPX
@connection.on(:promise, &method(:on_promise))
@connection.on(:altsvc) { |frame| on_altsvc(frame[:origin], frame) }
@connection.on(:settings_ack, &method(:on_settings))
@connection.on(:ack, &method(:on_pong))
@connection.on(:goaway, &method(:on_close))
#
# Some servers initiate HTTP/2 negotiation right away, some don't.
@ -306,6 +313,13 @@ module HTTPX
emit(:origin, origin)
end
def on_pong(_payload)
# TODO: what to do when ping doesn't match
emit(:pong)
ensure
@ping_payload = nil
end
def respond_to_missing?(meth, *args)
@connection.respond_to?(meth, *args) || super
end

View File

@ -96,8 +96,8 @@ class SessionTest < Minitest::Test
verify_status(response1, 200)
verify_status(response2, 200)
connection_count = http.pool.connection_count
assert connection_count == 2, "session should have closed the first connection (#{connection_count})"
ping_count = http.pool.ping_count
assert ping_count == 1, "session should have pinged after timeout (#{ping_count})"
end
end unless RUBY_VERSION < "2.3" || RUBY_ENGINE == "jruby"

View File

@ -25,15 +25,18 @@ module SessionWithPool
ConnectionPool = Class.new(HTTPX::Pool) do
attr_reader :connections
attr_reader :connection_count
attr_reader :ping_count
def initialize(*)
super
@connection_count = 0
@ping_count = 0
end
def init_connection(connection, _)
super
connection.on(:open) { @connection_count += 1 }
connection.on(:pong) { @ping_count += 1 }
end
end
@ -42,6 +45,13 @@ module SessionWithPool
@pool ||= ConnectionPool.new
end
end
module ConnectionMethods
def set_parser_callbacks(parser)
super
parser.on(:pong) { emit(:pong) }
end
end
end
# 9090 drops SYN packets for connect timeout tests, make sure there's a server binding there.