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

Constructor Details

#initialize(origin, addresses, options) ⇒ TCP

Returns a new instance of TCP.



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 16

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
    @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.



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

def addresses
  @addresses
end

#interestsObject (readonly)

Returns the value of attribute interests.



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

def interests
  @interests
end

#ipObject (readonly) Also known as: host

Returns the value of attribute ip.



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

def ip
  @ip
end

#portObject (readonly)

Returns the value of attribute port.



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

def port
  @port
end

#stateObject (readonly)

Returns the value of attribute state.



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

def state
  @state
end

Instance Method Details

#add_addresses(addrs) ⇒ Object



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

def add_addresses(addrs)
  return if addrs.empty?

  addrs = addrs.map { |addr| addr.is_a?(IPAddr) ? addr : IPAddr.new(addr) }

  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

#closeObject



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

def close
  return if @keep_open || closed?

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

#closed?Boolean

Returns:

  • (Boolean)


153
154
155
# File 'lib/httpx/io/tcp.rb', line 153

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

#connectObject



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/httpx/io/tcp.rb', line 70

def connect
  return unless closed?

  if !@io || @io.closed?
    transition(:idle)
    @io = build_socket
  end
  try_connect
rescue Errno::ECONNREFUSED,
       Errno::EADDRNOTAVAIL,
       Errno::EHOSTUNREACH,
       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)


149
150
151
# File 'lib/httpx/io/tcp.rb', line 149

def connected?
  @state == :connected
end

#expired?Boolean

Returns:

  • (Boolean)


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

def expired?
  # do not mess with external sockets
  return false if @options.io

  return true unless @addresses

  resolver_addresses = Resolver.nolookup_resolve(@hostname)

  (Array(resolver_addresses) & @addresses).empty?
end

#inspectObject

:nocov:



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

def inspect
  "#<#{self.class}: #{@ip}:#{@port} (state: #{@state})>"
end

#protocolObject



66
67
68
# File 'lib/httpx/io/tcp.rb', line 66

def protocol
  @fallback_protocol
end

#read(size, buffer) ⇒ Object



116
117
118
119
120
121
122
123
124
125
126
# File 'lib/httpx/io/tcp.rb', line 116

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



62
63
64
# File 'lib/httpx/io/tcp.rb', line 62

def to_io
  @io.to_io
end

#write(buffer) ⇒ Object



128
129
130
131
132
133
134
135
136
137
# File 'lib/httpx/io/tcp.rb', line 128

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