Class: RaptorIO::Socket::TCP

Inherits:
RaptorIO::Socket show all
Defined in:
lib/raptor-io/socket/tcp.rb

Overview

TCP client socket

Direct Known Subclasses

SSL

Defined Under Namespace

Classes: SSL

Constant Summary collapse

DEFAULT_OPTIONS =

Default configuration options.

{
  connect_timeout: 5,
}
DEFAULT_SSL_OPTIONS =

Default options for SSL streams connected through this socket.

See Also:

{
  ssl_version:         :TLSv1,
  ssl_verify_mode:     OpenSSL::SSL::VERIFY_NONE,
}

Instance Attribute Summary collapse

Attributes inherited from RaptorIO::Socket

#options

Instance Method Summary collapse

Methods inherited from RaptorIO::Socket

#closed?, getaddrinfo, method_missing, respond_to_missing?, select, translate_errors

Constructor Details

#initialize(socket, options = {}) ⇒ TCP

Returns a new instance of TCP.

Parameters:



25
26
27
28
29
# File 'lib/raptor-io/socket/tcp.rb', line 25

def initialize(socket, options = {})
  options = DEFAULT_OPTIONS.merge(options)
  super
  @plaintext_socket = @socket = socket
end

Instance Attribute Details

#socketIO

The underlying IO for this socket. Usually this is the ‘socket` passed to #initialize

Returns:

  • (IO)


22
23
24
# File 'lib/raptor-io/socket/tcp.rb', line 22

def socket
  @socket
end

Instance Method Details

#closevoid

This method returns an undefined value.

Close this socket. If this socket is an SSL stream, closes both the SSL stream and the underlying socket



145
146
147
148
149
150
151
152
153
# File 'lib/raptor-io/socket/tcp.rb', line 145

def close
  begin
    super
  ensure
    if (!@plaintext_socket.closed?)
      @plaintext_socket.close
    end
  end
end

#getpeername(string) ⇒ String

Return a Sockaddr struct for the socket. Note that this is the

Returns:



34
# File 'lib/raptor-io/socket/tcp.rb', line 34

def_delegator :@plaintext_socket, :getpeername, :getpeername

#gets(*args) ⇒ Object

Note:

May block

Ruby ‘Socket#gets` accepts:

  • ‘gets( sep = $/ )`

  • ‘gets( limit = nil )`

  • ‘gets( sep = $/, limit = nil )`

‘OpenSSL::SSL::SSLSocket#gets` however only supports `gets(sep=$/, limit=nil)`. This hack allows SSLSocket to behave the same as Ruby Socket.



126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/raptor-io/socket/tcp.rb', line 126

def gets(*args)
  translate_errors do
    if args.size == 1
      arg = args.first
      if arg.is_a?(Numeric)
        @socket.gets($/, arg)
      else
        @socket.gets(arg)
      end
    else
      @socket.gets(*args)
    end
  end
end

#read(maxlen) ⇒ String

Note:

May block

Read exactly ‘maxlen` bytes from the #socket. If fewer than `maxlen` bytes are available for reading, wait until enough data is sent.

Parameters:

  • maxlen (Fixnum)

Returns:



81
82
83
84
85
86
87
88
89
# File 'lib/raptor-io/socket/tcp.rb', line 81

def read(maxlen)
  buf = ""
  until 0 == maxlen
    prev_length = buf.length
    buf << readpartial(maxlen)
    maxlen -= buf.length - prev_length
  end
  buf
end

#read_nonblock(maxlen = nil) ⇒ String

Read at most ‘maxlen` bytes from the #socket.

Parameters:

  • maxlen (Fixnum) (defaults to: nil)

Returns:



110
111
112
113
114
# File 'lib/raptor-io/socket/tcp.rb', line 110

def read_nonblock(maxlen = nil)
  translate_errors do
    @socket.read_nonblock(maxlen)
  end
end

#readpartial(maxlen = nil) ⇒ String

Note:

May block

Read at most ‘maxlen` bytes from the #socket.

Parameters:

  • maxlen (Fixnum) (defaults to: nil)

Returns:



97
98
99
100
101
102
103
104
# File 'lib/raptor-io/socket/tcp.rb', line 97

def readpartial(maxlen = nil)
  begin
    read_nonblock(maxlen)
  rescue IO::WaitReadable
    IO.select([@socket])
    retry
  end
end

#remote_addressObject



46
47
48
# File 'lib/raptor-io/socket/tcp.rb', line 46

def remote_address
  ::Addrinfo.new([ "AF_INET", options[:peer_port], options[:peer_host], options[:peer_host] ])
end

#ssl?Boolean

Whether this socket is encrypted with SSL

Returns:

  • (Boolean)


163
164
165
# File 'lib/raptor-io/socket/tcp.rb', line 163

def ssl?
  !!(@socket.respond_to?(:context) && @socket.context)
end

#ssl_contextOpenSSL::SSL::SSLContext?

Returns:

  • (OpenSSL::SSL::SSLContext)
  • (nil)

    If this socket is not SSL (see #ssl?)



179
180
181
182
# File 'lib/raptor-io/socket/tcp.rb', line 179

def ssl_context
  return nil unless ssl?
  @socket.context
end

#ssl_versionString?

The version of SSL/TLS that was negotiated with the server.

Returns:

  • (String)

    See OpenSSL::SSL::SSLSocket#ssl_version for possible values

  • (nil)

    If this socket is not SSL



172
173
174
175
# File 'lib/raptor-io/socket/tcp.rb', line 172

def ssl_version
  return nil unless ssl?
  @socket.ssl_version
end

#to_ioIO

Attempt to turn this into something usable by ‘IO.select`.

Returns:

  • (IO)


158
159
160
# File 'lib/raptor-io/socket/tcp.rb', line 158

def to_io
  IO.try_convert(@socket) || @socket
end

#to_ssl(ssl_options = {}) ⇒ RaptorIO::Socket::TCP::SSL

Note:

The original socket is replaced by the newly connected SSL socket

Note:

May block

Starts an SSL/TLS stream over this connection.

Using this as opposed to directly instantiating SSL allows you to start a TLS connection after data has already been exchanged to enable things like ‘STARTTLS`.

Parameters:

  • ssl_options (Hash) (defaults to: {})

    Options

  • ssl_config (Hash)

    a customizable set of options

Options Hash (ssl_options):

  • :ssl_version (Symbol) — default: :TLSv1
  • :ssl_verify_mode (Constant) — default: OpenSSL::SSL::VERIFY_PEER

    Peer verification mode.

Returns:



204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/raptor-io/socket/tcp.rb', line 204

def to_ssl(ssl_options = {})
  if ssl_options[:ssl_context]
    options[:ssl_context] = ssl_options[:ssl_context]
  else
    ssl_options = DEFAULT_SSL_OPTIONS.merge(ssl_options)
    options[:ssl_context] = OpenSSL::SSL::SSLContext.new.tap do |ctx|
      ctx.ssl_version = ssl_options[:ssl_version]
      ctx.verify_mode = ssl_options[:ssl_verify_mode]
    end
  end

  s = RaptorIO::Socket::TCP::SSL.new(@plaintext_socket, options)
  @socket = s
  s
end

#ungetcnil

Pushes back one character onto the #socket‘s read buffer. Note that some streams will *lose data* if this is called with a `string` larger than one byte or called more than once between calls to #read!

Parameters:

  • string (String)

    A single-byte string

Returns:

  • (nil)


44
# File 'lib/raptor-io/socket/tcp.rb', line 44

def_delegator :@socket, :ungetc, :ungetc

#write(data) ⇒ Fixnum

Write ‘data` to the #socket.

Parameters:

Returns:

  • (Fixnum)


54
55
56
57
58
59
60
61
# File 'lib/raptor-io/socket/tcp.rb', line 54

def write(data)
  begin
    write_nonblock(data)
  rescue IO::WaitWritable
    IO.select(nil, [@socket])
    retry
  end
end

#write_nonblock(data) ⇒ String

Try to write ‘data` to the #socket.

Parameters:

  • maxlen (Fixnum)

Returns:



67
68
69
70
71
# File 'lib/raptor-io/socket/tcp.rb', line 67

def write_nonblock(data)
  translate_errors do
    @socket.write_nonblock(data)
  end
end