Class: HTTPX::TCP
Constant Summary
Constants included from Loggable
Loggable::COLORS, Loggable::USE_DEBUG_LOG
Instance Attribute Summary collapse
-
#addresses ⇒ Object
readonly
Returns the value of attribute addresses.
-
#interests ⇒ Object
readonly
Returns the value of attribute interests.
-
#ip ⇒ Object
(also: #host)
readonly
Returns the value of attribute ip.
-
#port ⇒ Object
readonly
Returns the value of attribute port.
-
#state ⇒ Object
readonly
Returns the value of attribute state.
Instance Method Summary collapse
- #add_addresses(addrs) ⇒ Object
-
#addresses? ⇒ Boolean
eliminates expired entries and returns whether there are still any left.
- #close ⇒ Object
- #closed? ⇒ Boolean
- #connect ⇒ Object
- #connected? ⇒ Boolean
-
#initialize(origin, addresses, options) ⇒ TCP
constructor
A new instance of TCP.
-
#inspect ⇒ Object
:nocov:.
- #protocol ⇒ Object
- #read(size, buffer) ⇒ Object
- #socket ⇒ Object
- #to_io ⇒ Object
- #write(buffer) ⇒ Object
Methods included from Loggable
#log, #log_exception, #log_redact
Constructor Details
#initialize(origin, addresses, options) ⇒ TCP
Returns a new instance of TCP.
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
# File 'lib/httpx/io/tcp.rb', line 15 def initialize(origin, addresses, ) @state = :idle @keep_open = false @addresses = [] @ip_index = -1 @ip = nil @hostname = origin.host @options = @fallback_protocol = @options.fallback_protocol @port = origin.port @interests = :w if @options.io @io = case @options.io when Hash @options.io[origin.] else @options.io end raise Error, "Given IO objects do not match the request authority" unless @io _, _, _, ip = @io.addr @ip = Resolver::Entry.new(ip) @addresses << @ip @keep_open = true @state = :connected else add_addresses(addresses) end @ip_index = @addresses.size - 1 end |
Instance Attribute Details
#addresses ⇒ Object (readonly)
Returns the value of attribute addresses.
11 12 13 |
# File 'lib/httpx/io/tcp.rb', line 11 def addresses @addresses end |
#interests ⇒ Object (readonly)
Returns the value of attribute interests.
11 12 13 |
# File 'lib/httpx/io/tcp.rb', line 11 def interests @interests end |
#ip ⇒ Object (readonly) Also known as: host
Returns the value of attribute ip.
11 12 13 |
# File 'lib/httpx/io/tcp.rb', line 11 def ip @ip end |
#port ⇒ Object (readonly)
Returns the value of attribute port.
11 12 13 |
# File 'lib/httpx/io/tcp.rb', line 11 def port @port end |
#state ⇒ Object (readonly)
Returns the value of attribute state.
11 12 13 |
# File 'lib/httpx/io/tcp.rb', line 11 def state @state end |
Instance Method Details
#add_addresses(addrs) ⇒ Object
50 51 52 53 54 55 56 57 58 59 60 61 |
# File 'lib/httpx/io/tcp.rb', line 50 def add_addresses(addrs) return if addrs.empty? ip_index = @ip_index || (@addresses.size - 1) if addrs.first.ipv6? # should be the next in line @addresses = [*@addresses[0, ip_index], *addrs, *@addresses[ip_index..-1]] else @addresses.unshift(*addrs) end @ip_index += addrs.size end |
#addresses? ⇒ Boolean
eliminates expired entries and returns whether there are still any left.
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
# File 'lib/httpx/io/tcp.rb', line 64 def addresses? prev_addr_size = @addresses.size @addresses.delete_if(&:expired?).sort! do |addr1, addr2| if addr1.ipv6? addr2.ipv6? ? 0 : 1 else addr2.ipv6? ? -1 : 0 end end @ip_index = @addresses.size - 1 if prev_addr_size != @addresses.size @addresses.any? end |
#close ⇒ Object
181 182 183 184 185 186 187 188 189 |
# File 'lib/httpx/io/tcp.rb', line 181 def close return if @keep_open || closed? begin @io.close ensure transition(:closed) end end |
#closed? ⇒ Boolean
195 196 197 |
# File 'lib/httpx/io/tcp.rb', line 195 def closed? @state == :idle || @state == :closed end |
#connect ⇒ Object
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
# File 'lib/httpx/io/tcp.rb', line 88 def connect return unless closed? if @addresses.empty? # an idle connection trying to connect with no available addresses is a connection # out of the initial context which is back to the DNS resolution loop. This may # happen in a fiber-aware context where a connection reconnects with expired addresses, # and context is passed back to a fiber on the same connection while waiting for the # DNS answer. log { "tried connecting while resolving, skipping..." } return end if !@io || @io.closed? transition(:idle) @io = build_socket end try_connect rescue Errno::EHOSTUNREACH, Errno::ENETUNREACH => e @ip_index -= 1 raise e if @ip_index.negative? log { "failed connecting to #{@ip} (#{e.}), evict from cache and trying next..." } Resolver.cached_lookup_evict(@hostname, @ip) @io = build_socket retry rescue Errno::ECONNREFUSED, Errno::EADDRNOTAVAIL, SocketError, IOError => e @ip_index -= 1 raise e if @ip_index.negative? log { "failed connecting to #{@ip} (#{e.}), trying next..." } @io = build_socket retry rescue Errno::ETIMEDOUT => e @ip_index -= 1 raise ConnectTimeoutError.new(@options.timeout[:connect_timeout], e.) if @ip_index.negative? log { "failed connecting to #{@ip} (#{e.}), trying next..." } @io = build_socket retry end |
#connected? ⇒ Boolean
191 192 193 |
# File 'lib/httpx/io/tcp.rb', line 191 def connected? @state == :connected end |
#inspect ⇒ Object
:nocov:
200 201 202 203 204 205 206 207 |
# File 'lib/httpx/io/tcp.rb', line 200 def inspect "#<#{self.class}:#{object_id} " \ "#{@ip}:#{@port} " \ "@state=#{@state} " \ "@hostname=#{@hostname} " \ "@addresses=#{@addresses} " \ "@state=#{@state}>" end |
#protocol ⇒ Object
84 85 86 |
# File 'lib/httpx/io/tcp.rb', line 84 def protocol @fallback_protocol end |
#read(size, buffer) ⇒ Object
158 159 160 161 162 163 164 165 166 167 168 |
# File 'lib/httpx/io/tcp.rb', line 158 def read(size, buffer) ret = @io.read_nonblock(size, buffer, exception: false) if ret == :wait_readable buffer.clear return 0 end return if ret.nil? log { "READ: #{buffer.bytesize} bytes..." } buffer.bytesize end |
#socket ⇒ Object
46 47 48 |
# File 'lib/httpx/io/tcp.rb', line 46 def socket @io end |
#to_io ⇒ Object
80 81 82 |
# File 'lib/httpx/io/tcp.rb', line 80 def to_io @io.to_io end |
#write(buffer) ⇒ Object
170 171 172 173 174 175 176 177 178 179 |
# File 'lib/httpx/io/tcp.rb', line 170 def write(buffer) siz = @io.write_nonblock(buffer, exception: false) return 0 if siz == :wait_writable return if siz.nil? log { "WRITE: #{siz} bytes..." } buffer.shift!(siz) siz end |