Class: HTTPX::TCP

Inherits:
Object
  • Object
show all
Includes:
Loggable
Defined in:
lib/httpx/io/tcp.rb

Direct Known Subclasses

SSL, UNIX

Constant Summary

Constants included from Loggable

Loggable::COLORS, Loggable::USE_DEBUG_LOG

Instance Attribute Summary collapse

Instance Method Summary collapse

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
# File 'lib/httpx/io/tcp.rb', line 15

def initialize(origin, addresses, options)
  @state = :idle
  @addresses = []
  @hostname = origin.host
  @options = options
  @fallback_protocol = @options.fallback_protocol
  @port = origin.port
  @interests = :w
  if @options.io
    @io = case @options.io
          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
    @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

#addressesObject (readonly)

Returns the value of attribute addresses.



11
12
13
# File 'lib/httpx/io/tcp.rb', line 11

def addresses
  @addresses
end

#interestsObject (readonly)

Returns the value of attribute interests.



11
12
13
# File 'lib/httpx/io/tcp.rb', line 11

def interests
  @interests
end

#ipObject (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

#portObject (readonly)

Returns the value of attribute port.



11
12
13
# File 'lib/httpx/io/tcp.rb', line 11

def port
  @port
end

#stateObject (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



47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/httpx/io/tcp.rb', line 47

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)
    @ip_index += addrs.size if @ip_index
  end
end

#addresses?Boolean

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

Returns:

  • (Boolean)


61
62
63
64
65
66
67
68
69
70
71
# File 'lib/httpx/io/tcp.rb', line 61

def addresses?
  prev_addr_size = @addresses.size

  @addresses.delete_if(&:expired?)

  unless (decr = prev_addr_size - @addresses.size).zero?
    @ip_index = @addresses.size - decr
  end

  @addresses.any?
end

#closeObject



159
160
161
162
163
164
165
166
167
# File 'lib/httpx/io/tcp.rb', line 159

def close
  return if @keep_open || closed?

  begin
    @io.close
  ensure
    transition(:closed)
  end
end

#closed?Boolean

Returns:

  • (Boolean)


173
174
175
# File 'lib/httpx/io/tcp.rb', line 173

def closed?
  @state == :idle || @state == :closed
end

#connectObject



81
82
83
84
85
86
87
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
# File 'lib/httpx/io/tcp.rb', line 81

def connect
  return unless closed?

  if !@io || @io.closed?
    transition(:idle)
    @io = build_socket
  end
  try_connect
rescue Errno::EHOSTUNREACH,
       Errno::ENETUNREACH => e
  raise e if @ip_index <= 0

  log { "failed connecting to #{@ip} (#{e.message}), evict from cache and trying next..." }
  Resolver.cached_lookup_evict(@hostname, @ip)

  @ip_index -= 1
  @io = build_socket
  retry
rescue Errno::ECONNREFUSED,
       Errno::EADDRNOTAVAIL,
       SocketError,
       IOError => e
  raise e if @ip_index <= 0

  log { "failed connecting to #{@ip} (#{e.message}), trying next..." }
  @ip_index -= 1
  @io = build_socket
  retry
rescue Errno::ETIMEDOUT => e
  raise ConnectTimeoutError.new(@options.timeout[:connect_timeout], e.message) if @ip_index <= 0

  log { "failed connecting to #{@ip} (#{e.message}), trying next..." }
  @ip_index -= 1
  @io = build_socket
  retry
end

#connected?Boolean

Returns:

  • (Boolean)


169
170
171
# File 'lib/httpx/io/tcp.rb', line 169

def connected?
  @state == :connected
end

#inspectObject

:nocov:



178
179
180
181
182
183
184
185
# File 'lib/httpx/io/tcp.rb', line 178

def inspect
  "#<#{self.class}:#{object_id} " \
    "#{@ip}:#{@port} " \
    "@state=#{@state} " \
    "@hostname=#{@hostname} " \
    "@addresses=#{@addresses} " \
    "@state=#{@state}>"
end

#protocolObject



77
78
79
# File 'lib/httpx/io/tcp.rb', line 77

def protocol
  @fallback_protocol
end

#read(size, buffer) ⇒ Object



136
137
138
139
140
141
142
143
144
145
146
# File 'lib/httpx/io/tcp.rb', line 136

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

#socketObject



43
44
45
# File 'lib/httpx/io/tcp.rb', line 43

def socket
  @io
end

#to_ioObject



73
74
75
# File 'lib/httpx/io/tcp.rb', line 73

def to_io
  @io.to_io
end

#write(buffer) ⇒ Object



148
149
150
151
152
153
154
155
156
157
# File 'lib/httpx/io/tcp.rb', line 148

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