Class: TCPTimeout::TCPSocket

Inherits:
Object
  • Object
show all
Defined in:
lib/handset_detection/vendor/tcp_timeout.rb

Instance Method Summary collapse

Constructor Details

#initialize(host, port, opts = {}) ⇒ TCPSocket

Returns a new instance of TCPSocket.



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/handset_detection/vendor/tcp_timeout.rb', line 27

def initialize(host, port, opts = {})
  @connect_timeout = opts[:connect_timeout]
  @write_timeout = opts[:write_timeout]
  @read_timeout = opts[:read_timeout]

  family = opts[:family] || Socket::AF_INET
  address = Socket.getaddrinfo(host, nil, family).first[3]
  @sockaddr = Socket.pack_sockaddr_in(port, address)

  @socket = Socket.new(family, Socket::SOCK_STREAM, 0)
  @socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)

  local_host = opts[:local_host]
  local_port = opts[:local_port]
  if local_host || local_port
    local_host ||= ''
    local_address = Socket.getaddrinfo(local_host, nil, family).first[3]
    local_sockaddr = Socket.pack_sockaddr_in(local_port, local_address)
    @socket.bind(local_sockaddr)
  end

  connect
end

Instance Method Details

#connectObject



51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/handset_detection/vendor/tcp_timeout.rb', line 51

def connect
  return @socket.connect(@sockaddr) unless @connect_timeout

  begin
    @socket.connect_nonblock(@sockaddr)
  rescue Errno::EINPROGRESS
    select_timeout(:connect, @connect_timeout)
    # If there was a failure this will raise an Error
    begin
      @socket.connect_nonblock(@sockaddr)
    rescue Errno::EISCONN
      # Successfully connected
    end
  end
end

#read(length = nil, *args) ⇒ Object

Raises:

  • (ArgumentError)


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
# File 'lib/handset_detection/vendor/tcp_timeout.rb', line 88

def read(length = nil, *args)
  raise ArgumentError, 'too many arguments' if args.length > 2

  timeout = (args.length > 1) ? args.pop : @read_timeout
  return @socket.read(length, *args) unless length > 0 && timeout

  buffer = args.first || ''.force_encoding(Encoding::ASCII_8BIT)

  begin
    # Drain internal buffers
    @socket.read_nonblock(length, buffer)
    return buffer if buffer.bytesize >= length
  rescue Errno::EWOULDBLOCK, Errno::EAGAIN
    # Internal buffers were empty
    buffer.clear
  rescue EOFError
    return nil
  end

  @chunk ||= ''.force_encoding(Encoding::ASCII_8BIT)

  loop do
    timeout = select_timeout(:read, timeout)

    begin
      @socket.read_nonblock(length, @chunk)
    rescue Errno::EWOULDBLOCK, Errno::EAGAIN
      retry
    rescue EOFError
      return buffer.empty? ? nil : buffer
    end
    buffer << @chunk

    if length
      length -= @chunk.bytesize
      return buffer if length <= 0
    end
  end
end

#readbyteObject



142
143
144
# File 'lib/handset_detection/vendor/tcp_timeout.rb', line 142

def readbyte
  readpartial(1).ord
end

#readpartial(length, *args) ⇒ Object

Raises:

  • (ArgumentError)


128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/handset_detection/vendor/tcp_timeout.rb', line 128

def readpartial(length, *args)
  raise ArgumentError, 'too many arguments' if args.length > 2

  timeout = (args.length > 1) ? args.pop : @read_timeout
  return @socket.readpartial(length, *args) unless length > 0 && timeout

  begin
    @socket.read_nonblock(length, *args)
  rescue Errno::EWOULDBLOCK, Errno::EAGAIN
    timeout = select_timeout(:read, timeout)
    retry
  end
end

#write(data, timeout = nil) ⇒ Object



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/handset_detection/vendor/tcp_timeout.rb', line 67

def write(data, timeout = nil)
  timeout ||= @write_timeout
  return @socket.write(data) unless timeout

  length = data.bytesize

  total_count = 0
  loop do
    begin
      count = @socket.write_nonblock(data)
    rescue Errno::EWOULDBLOCK, Errno::EAGAIN
      timeout = select_timeout(:write, timeout)
      retry
    end

    total_count += count
    return total_count if total_count >= length
    data = data.byteslice(count..-1)
  end
end