Methods
Public Class
Public Instance
Included modules
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
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
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