class HTTP::Timeout::Global

Timeout handler with a single global timeout for the entire request

def connect(socket_class, host, port, nodelay: false)

Returns:
  • (void) -

Other tags:
    Api: - public

Parameters:
  • nodelay (Boolean) --
  • port (Integer) --
  • host (String) --
  • socket_class (Class) --
def connect(socket_class, host, port, nodelay: false)
  reset_timer
  @socket = open_socket(socket_class, host, port, connect_timeout: effective_timeout(@connect_timeout))
  @socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1) if nodelay
  log_time
end

def connect_ssl

Returns:
  • (void) -

Other tags:
    Api: - public
def connect_ssl
  reset_timer
  begin
    @socket.connect_nonblock
  rescue IO::WaitReadable
    wait_readable_or_timeout(@connect_timeout)
    retry
  rescue IO::WaitWritable
    wait_writable_or_timeout(@connect_timeout)
    retry
  end
end

def effective_timeout(per_op_timeout)

Returns:
  • (Numeric) -

Other tags:
    Api: - private

Parameters:
  • per_op_timeout (Numeric, nil) -- per-operation timeout limit
def effective_timeout(per_op_timeout)
  return @time_left unless per_op_timeout
  [per_op_timeout, @time_left].min
end

def handle_io_result(result)

Returns:
  • (Object, Symbol) -

Other tags:
    Api: - private
def handle_io_result(result)
  result.nil? ? :eof : result
end

def initialize(global_timeout:, read_timeout: nil, write_timeout: nil, connect_timeout: nil)

Returns:
  • (HTTP::Timeout::Global) -

Other tags:
    Api: - public

Parameters:
  • connect_timeout (Numeric, nil) -- Connect timeout in seconds
  • write_timeout (Numeric, nil) -- Write timeout in seconds
  • read_timeout (Numeric, nil) -- Read timeout in seconds
  • global_timeout (Numeric) -- Global timeout in seconds
def initialize(global_timeout:, read_timeout: nil, write_timeout: nil, connect_timeout: nil)
  super
  @timeout = @time_left = global_timeout
  @read_timeout    = read_timeout
  @write_timeout   = write_timeout
  @connect_timeout = connect_timeout
end

def log_time

Returns:
  • (void) -

Other tags:
    Api: - private
def log_time
  @time_left -= (Time.now - @started)
  raise TimeoutError, "Timed out after using the allocated #{@timeout} seconds" if @time_left <= 0
  reset_timer
end

def perform_io(per_op_timeout = nil)

Returns:
  • (Object) -

Other tags:
    Api: - private

Parameters:
  • per_op_timeout (Numeric, nil) -- per-operation timeout limit
def perform_io(per_op_timeout = nil)
  reset_timer
  loop do
    result = yield
    return handle_io_result(result) unless WAIT_RESULTS.include?(result)
    wait_for_io(result, per_op_timeout)
  rescue IO::WaitReadable then wait_readable_or_timeout(per_op_timeout)
  rescue IO::WaitWritable then wait_writable_or_timeout(per_op_timeout)
  end
rescue EOFError
  :eof
end

def read_nonblock(size, buffer = nil)

Returns:
  • (String, Symbol) -

Other tags:
    Api: - private
def read_nonblock(size, buffer = nil)
  @socket.read_nonblock(size, buffer, exception: false)
end

def readpartial(size, buffer = nil)

Returns:
  • (String, :eof) -

Other tags:
    Api: - public

Parameters:
  • buffer (String, nil) --
  • size (Integer) --
def readpartial(size, buffer = nil)
  perform_io(@read_timeout) { read_nonblock(size, buffer) }
end

def reset_counter

Returns:
  • (Numeric) -

Other tags:
    Api: - public
def reset_counter
  @time_left = @timeout
end

def reset_timer

Returns:
  • (Time) -

Other tags:
    Api: - private
def reset_timer
  @started = Time.now
end

def wait_for_io(result, per_op_timeout = nil)

Returns:
  • (void) -

Other tags:
    Api: - private

Parameters:
  • per_op_timeout (Numeric, nil) -- per-operation timeout limit
  • result (Symbol) -- the I/O wait type
def wait_for_io(result, per_op_timeout = nil)
  if result == :wait_readable
    wait_readable_or_timeout(per_op_timeout)
  else
    wait_writable_or_timeout(per_op_timeout)
  end
end

def wait_readable_or_timeout(per_op = nil)

Returns:
  • (void) -

Other tags:
    Api: - private

Parameters:
  • per_op (Numeric, nil) -- per-operation timeout limit
def wait_readable_or_timeout(per_op = nil)
  timeout = effective_timeout(per_op)
  result = @socket.to_io.wait_readable(timeout)
  log_time
  raise TimeoutError, "Read timed out after #{per_op} seconds" if per_op && result.nil?
end

def wait_writable_or_timeout(per_op = nil)

Returns:
  • (void) -

Other tags:
    Api: - private

Parameters:
  • per_op (Numeric, nil) -- per-operation timeout limit
def wait_writable_or_timeout(per_op = nil)
  timeout = effective_timeout(per_op)
  result = @socket.to_io.wait_writable(timeout)
  log_time
  raise TimeoutError, "Write timed out after #{per_op} seconds" if per_op && result.nil?
end

def write(data)

Returns:
  • (Integer, :eof) -

Other tags:
    Api: - public

Parameters:
  • data (String) --
def write(data)
  perform_io(@write_timeout) { write_nonblock(data) }
end

def write_nonblock(data)

Returns:
  • (Integer, Symbol) -

Other tags:
    Api: - private
def write_nonblock(data)
  @socket.write_nonblock(data, exception: false)
end