lib/httpx/io/tcp.rb in httpx-0.12.0 vs lib/httpx/io/tcp.rb in httpx-0.13.0
- old
+ new
@@ -5,18 +5,19 @@
module HTTPX
class TCP
include Loggable
+ using URIExtensions
+
attr_reader :ip, :port, :addresses, :state, :interests
alias_method :host, :ip
def initialize(origin, addresses, options)
@state = :idle
@hostname = origin.host
- @addresses = addresses
@options = Options.new(options)
@fallback_protocol = @options.fallback_protocol
@port = origin.port
@interests = :w
if @options.io
@@ -24,21 +25,21 @@
when Hash
@options.io[origin.authority]
else
@options.io
end
+ raise Error, "Given IO objects do not match the request authority" unless @io
+
_, _, _, @ip = @io.addr
@addresses ||= [@ip]
@ip_index = @addresses.size - 1
- unless @io.nil?
- @keep_open = true
- @state = :connected
- end
+ @keep_open = true
+ @state = :connected
else
- @ip_index = @addresses.size - 1
- @ip = @addresses[@ip_index]
+ @addresses = addresses.map { |addr| addr.is_a?(IPAddr) ? addr : IPAddr.new(addr) }
end
+ @ip_index = @addresses.size - 1
@io ||= build_socket
end
def to_io
@io.to_io
@@ -49,40 +50,44 @@
end
def connect
return unless closed?
- begin
- if @io.closed?
- transition(:idle)
- @io = build_socket
- end
- @io.connect_nonblock(Socket.sockaddr_in(@port, @ip.to_s))
- rescue Errno::EISCONN
+ if @io.closed?
+ transition(:idle)
+ @io = build_socket
end
- @interests = :w
-
- transition(:connected)
+ try_connect
rescue Errno::EHOSTUNREACH => e
raise e if @ip_index <= 0
@ip_index -= 1
retry
rescue Errno::ETIMEDOUT => e
raise ConnectTimeoutError.new(@options.timeout.connect_timeout, e.message) if @ip_index <= 0
@ip_index -= 1
retry
- rescue Errno::EINPROGRESS,
- Errno::EALREADY
- @interests = :w
- rescue ::IO::WaitReadable
- @interests = :r
end
if RUBY_VERSION < "2.3"
# :nocov:
+ def try_connect
+ @io.connect_nonblock(Socket.sockaddr_in(@port, @ip.to_s))
+ rescue ::IO::WaitWritable, Errno::EALREADY
+ @interests = :w
+ rescue ::IO::WaitReadable
+ @interests = :r
+ rescue Errno::EISCONN
+ transition(:connected)
+ @interests = :w
+ else
+ transition(:connected)
+ @interests = :w
+ end
+ private :try_connect
+
def read(size, buffer)
@io.read_nonblock(size, buffer)
log { "READ: #{buffer.bytesize} bytes..." }
buffer.bytesize
rescue ::IO::WaitReadable
@@ -102,10 +107,24 @@
rescue EOFError
nil
end
# :nocov:
else
+ def try_connect
+ case @io.connect_nonblock(Socket.sockaddr_in(@port, @ip.to_s), exception: false)
+ when :wait_readable
+ @interests = :r
+ return
+ when :wait_writable
+ @interests = :w
+ return
+ end
+ transition(:connected)
+ @interests = :w
+ end
+ private :try_connect
+
def read(size, buffer)
ret = @io.read_nonblock(size, buffer, exception: false)
if ret == :wait_readable
buffer.clear
return 0
@@ -146,18 +165,18 @@
@state == :idle || @state == :closed
end
# :nocov:
def inspect
- id = @io.closed? ? "closed" : @io.fileno
- "#<TCP(fd: #{id}): #{@ip}:#{@port} (state: #{@state})>"
+ "#<#{self.class}: #{@ip}:#{@port} (state: #{@state})>"
end
# :nocov:
private
def build_socket
+ @ip = @addresses[@ip_index]
Socket.new(@ip.family, :STREAM, 0)
end
def transition(nextstate)
case nextstate
@@ -176,12 +195,12 @@
end
def log_transition_state(nextstate)
case nextstate
when :connected
- "Connected to #{@hostname} (#{@ip}) port #{@port} (##{@io.fileno})"
+ "Connected to #{host} (##{@io.fileno})"
else
- "#{@ip}:#{@port} #{@state} -> #{nextstate}"
+ "#{host} #{@state} -> #{nextstate}"
end
end
end
end