class HTTPX::TCP

  1. lib/httpx/io/tcp.rb
Superclass: Object

Included modules

  1. Loggable

Attributes

addresses [R]
host [R]
interests [R]
ip [R]
port [R]
state [R]

Public Class methods

new(origin, addresses, options)
[show source]
   # File lib/httpx/io/tcp.rb
15 def initialize(origin, addresses, options)
16   @state = :idle
17   @keep_open = false
18   @addresses = []
19   @ip_index = -1
20   @ip = nil
21   @hostname = origin.host
22   @options = options
23   @fallback_protocol = @options.fallback_protocol
24   @port = origin.port
25   @interests = :w
26   if (io = @options.io)
27     io =
28       case io
29       when Hash
30         io[origin.authority]
31       else
32         io
33       end
34     raise Error, "Given IO objects do not match the request authority" unless io
35 
36     # @type var io: TCPSocket | OpenSSL::SSL::SSLSocket
37 
38     _, _, _, ip = io.addr
39     @io = io
40     @addresses << (@ip = Resolver::Entry.new(ip))
41     @keep_open = true
42     @state = :connected
43   else
44     add_addresses(addresses)
45   end
46   @ip_index = @addresses.size - 1
47 end

Public Instance methods

add_addresses(addrs)
[show source]
   # File lib/httpx/io/tcp.rb
53 def add_addresses(addrs)
54   return if addrs.empty?
55 
56   ip_index = @ip_index || (@addresses.size - 1)
57   if addrs.first.ipv6?
58     # should be the next in line
59     @addresses = [*@addresses[0, ip_index], *addrs, *@addresses[ip_index..-1]]
60   else
61     @addresses.unshift(*addrs)
62   end
63   @ip_index += addrs.size
64 end
addresses?()

eliminates expired entries and returns whether there are still any left.

[show source]
   # File lib/httpx/io/tcp.rb
67 def addresses?
68   prev_addr_size = @addresses.size
69 
70   @addresses.delete_if(&:expired?).sort! do |addr1, addr2|
71     if addr1.ipv6?
72       addr2.ipv6? ? 0 : 1
73     else
74       addr2.ipv6? ? -1 : 0
75     end
76   end
77 
78   @ip_index = @addresses.size - 1 if prev_addr_size != @addresses.size
79 
80   @addresses.any?
81 end
close()
[show source]
    # File lib/httpx/io/tcp.rb
184 def close
185   return if @keep_open || closed?
186 
187   begin
188     @io.close
189   rescue StandardError => e
190     log { "error closing socket" }
191     log { e.full_message(highlight: false) }
192   ensure
193     transition(:closed)
194   end
195 end
closed?()
[show source]
    # File lib/httpx/io/tcp.rb
201 def closed?
202   @state == :idle || @state == :closed
203 end
connect()
[show source]
    # File lib/httpx/io/tcp.rb
 91 def connect
 92   return unless closed?
 93 
 94   if @addresses.empty?
 95     # an idle connection trying to connect with no available addresses is a connection
 96     # out of the initial context which is back to the DNS resolution loop. This may
 97     # happen in a fiber-aware context where a connection reconnects with expired addresses,
 98     # and context is passed back to a fiber on the same connection while waiting for the
 99     # DNS answer.
100     log { "tried connecting while resolving, skipping..." }
101 
102     return
103   end
104 
105   if !@io || @io.closed?
106     transition(:idle)
107     @io = build_socket
108   end
109   try_connect
110 rescue Errno::EHOSTUNREACH,
111        Errno::ENETUNREACH => e
112   @ip_index -= 1
113 
114   raise e if @ip_index.negative?
115 
116   log { "failed connecting to #{@ip} (#{e.message}), evict from cache and trying next..." }
117   @options.resolver_cache.evict(@hostname, @ip)
118 
119   @io = build_socket
120   retry
121 rescue Errno::ECONNREFUSED,
122        Errno::EADDRNOTAVAIL,
123        SocketError,
124        IOError => e
125   @ip_index -= 1
126 
127   raise e if @ip_index.negative?
128 
129   log { "failed connecting to #{@ip} (#{e.message}), trying next..." }
130   @io = build_socket
131   retry
132 rescue Errno::ETIMEDOUT => e
133   @ip_index -= 1
134 
135   raise ConnectTimeoutError.new(@options.timeout[:connect_timeout], e.message) if @ip_index.negative?
136 
137   log { "failed connecting to #{@ip} (#{e.message}), trying next..." }
138 
139   @io = build_socket
140   retry
141 end
connected?()
[show source]
    # File lib/httpx/io/tcp.rb
197 def connected?
198   @state == :connected
199 end
inspect()

:nocov:

[show source]
    # File lib/httpx/io/tcp.rb
206 def inspect
207   "#<#{self.class}:#{object_id} " \
208     "#{@ip}:#{@port} " \
209     "@state=#{@state} " \
210     "@hostname=#{@hostname} " \
211     "@addresses=#{@addresses} " \
212     "@state=#{@state}>"
213 end
protocol()
[show source]
   # File lib/httpx/io/tcp.rb
87 def protocol
88   @fallback_protocol
89 end
read(size, buffer)
[show source]
    # File lib/httpx/io/tcp.rb
161 def read(size, buffer)
162   ret = @io.read_nonblock(size, buffer, exception: false)
163   if ret == :wait_readable
164     buffer.clear
165     return 0
166   end
167   return if ret.nil?
168 
169   log { "READ: #{buffer.bytesize} bytes..." }
170   buffer.bytesize
171 end
socket()
[show source]
   # File lib/httpx/io/tcp.rb
49 def socket
50   @io
51 end
to_io()
[show source]
   # File lib/httpx/io/tcp.rb
83 def to_io
84   @io.to_io
85 end
write(buffer)
[show source]
    # File lib/httpx/io/tcp.rb
173 def write(buffer)
174   siz = @io.write_nonblock(buffer, exception: false)
175   return 0 if siz == :wait_writable
176   return if siz.nil?
177 
178   log { "WRITE: #{siz} bytes..." }
179 
180   buffer.shift!(siz)
181   siz
182 end