class HTTP::Timeout::Global

Attributes

time_left[R]
total_timeout[R]

Public Class Methods

new(*args) click to toggle source
Calls superclass method
# File lib/http/timeout/global.rb, line 12
def initialize(*args)
  super
  reset_counter
end

Public Instance Methods

<<(data)
Alias for: write
connect(socket_class, host, port, nodelay = false) click to toggle source
# File lib/http/timeout/global.rb, line 23
def connect(socket_class, host, port, nodelay = false)
  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
  end

  log_time
end
connect_ssl() click to toggle source
# File lib/http/timeout/global.rb, line 33
def connect_ssl
  reset_timer

  begin
    @socket.connect_nonblock
  rescue IO::WaitReadable
    IO.select([@socket], nil, nil, time_left)
    log_time
    retry
  rescue IO::WaitWritable
    IO.select(nil, [@socket], nil, time_left)
    log_time
    retry
  end
end
readpartial(size, buffer = nil) click to toggle source

Read from the socket

# File lib/http/timeout/global.rb, line 50
def readpartial(size, buffer = nil)
  perform_io { read_nonblock(size, buffer) }
end
reset_counter() click to toggle source

To future me: Don't remove this again, past you was smarter.

# File lib/http/timeout/global.rb, line 18
def reset_counter
  @time_left = connect_timeout + read_timeout + write_timeout
  @total_timeout = time_left
end
write(data) click to toggle source

Write to the socket

# File lib/http/timeout/global.rb, line 55
def write(data)
  perform_io { write_nonblock(data) }
end
Also aliased as: <<

Private Instance Methods

log_time() click to toggle source
# File lib/http/timeout/global.rb, line 123
def log_time
  @time_left -= (Time.now - @started)
  if time_left <= 0
    raise TimeoutError, "Timed out after using the allocated #{total_timeout} seconds"
  end

  reset_timer
end
perform_io() { || ... } click to toggle source

Perform the given I/O operation with the given argument

# File lib/http/timeout/global.rb, line 82
def perform_io
  reset_timer

  loop do
    begin
      result = yield

      case result
      when :wait_readable then wait_readable_or_timeout
      when :wait_writable then wait_writable_or_timeout
      when NilClass       then return :eof
      else                return result
      end
    rescue IO::WaitReadable
      wait_readable_or_timeout
    rescue IO::WaitWritable
      wait_writable_or_timeout
    end
  end
rescue EOFError
  :eof
end
read_nonblock(size, buffer = nil) click to toggle source
# File lib/http/timeout/global.rb, line 64
def read_nonblock(size, buffer = nil)
  @socket.read_nonblock(size, buffer)
end
reset_timer() click to toggle source

Due to the run/retry nature of nonblocking I/O, it's easier to keep track of time via method calls instead of a block to monitor.

# File lib/http/timeout/global.rb, line 119
def reset_timer
  @started = Time.now
end
wait_readable_or_timeout() click to toggle source

Wait for a socket to become readable

# File lib/http/timeout/global.rb, line 106
def wait_readable_or_timeout
  @socket.to_io.wait_readable(time_left)
  log_time
end
wait_writable_or_timeout() click to toggle source

Wait for a socket to become writable

# File lib/http/timeout/global.rb, line 112
def wait_writable_or_timeout
  @socket.to_io.wait_writable(time_left)
  log_time
end
write_nonblock(data) click to toggle source
# File lib/http/timeout/global.rb, line 68
def write_nonblock(data)
  @socket.write_nonblock(data)
end