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 @options.io
27     @io = case @options.io
28           when Hash
29             @options.io[origin.authority]
30           else
31             @options.io
32     end
33     raise Error, "Given IO objects do not match the request authority" unless @io
34 
35     _, _, _, ip = @io.addr
36     @ip = Resolver::Entry.new(ip)
37     @addresses << @ip
38     @keep_open = true
39     @state = :connected
40   else
41     add_addresses(addresses)
42   end
43   @ip_index = @addresses.size - 1
44 end

Public Instance methods

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

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

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

:nocov:

[show source]
    # File lib/httpx/io/tcp.rb
200 def inspect
201   "#<#{self.class}:#{object_id} " \
202     "#{@ip}:#{@port} " \
203     "@state=#{@state} " \
204     "@hostname=#{@hostname} " \
205     "@addresses=#{@addresses} " \
206     "@state=#{@state}>"
207 end
protocol()
[show source]
   # File lib/httpx/io/tcp.rb
84 def protocol
85   @fallback_protocol
86 end
read(size, buffer)
[show source]
    # File lib/httpx/io/tcp.rb
158 def read(size, buffer)
159   ret = @io.read_nonblock(size, buffer, exception: false)
160   if ret == :wait_readable
161     buffer.clear
162     return 0
163   end
164   return if ret.nil?
165 
166   log { "READ: #{buffer.bytesize} bytes..." }
167   buffer.bytesize
168 end
socket()
[show source]
   # File lib/httpx/io/tcp.rb
46 def socket
47   @io
48 end
to_io()
[show source]
   # File lib/httpx/io/tcp.rb
80 def to_io
81   @io.to_io
82 end
write(buffer)
[show source]
    # File lib/httpx/io/tcp.rb
170 def write(buffer)
171   siz = @io.write_nonblock(buffer, exception: false)
172   return 0 if siz == :wait_writable
173   return if siz.nil?
174 
175   log { "WRITE: #{siz} bytes..." }
176 
177   buffer.shift!(siz)
178   siz
179 end