Class: RaptorIO::Socket

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Defined in:
lib/raptor-io/socket.rb

Overview

A basic class for specific transports to inherit from. Analogous to stdlib’s BasicSocket

Direct Known Subclasses

TCP, TCPServer

Defined Under Namespace

Classes: Comm, CommChain, Error, SwitchBoard, TCP, TCPServer

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

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

Returns a new instance of Socket.

Parameters:

  • socket (IO)

    An already-connected socket.

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

    Options (see #options).



159
160
161
162
# File 'lib/raptor-io/socket.rb', line 159

def initialize( socket, options = {} )
  @socket  = socket
  @options = options
end

Instance Attribute Details

#optionsHash<Symbol,Object>

Options for this socket.

Returns:

  • (Hash<Symbol,Object>)


150
151
152
# File 'lib/raptor-io/socket.rb', line 150

def options
  @options
end

Class Method Details

.getaddrinfo(*args) ⇒ Object

Delegates to ‘::Socket.getaddrinfo`.



121
122
123
124
125
126
127
128
# File 'lib/raptor-io/socket.rb', line 121

def getaddrinfo( *args )
  begin
    ::Socket.getaddrinfo( *args )
  # OSX raises SocketError.
  rescue ::SocketError, ::Errno::ENOENT => e
    raise RaptorIO::Socket::Error::CouldNotResolve.new( e.to_s )
  end
end

.method_missing(meth, *args, &block) ⇒ Object

Delegate to Ruby Socket.



131
132
133
134
135
136
137
138
139
140
# File 'lib/raptor-io/socket.rb', line 131

def method_missing(meth, *args, &block)
  #$stderr.puts("Socket.method_missing(#{meth}, #{args.inspect}")
  if ::Socket.respond_to?(meth)
    translate_errors do
      ::Socket.__send__(meth, *args, &block)
    end
  else
    super
  end
end

.respond_to_missing?(meth, include_private = false) ⇒ Boolean

Returns:

  • (Boolean)


142
143
144
# File 'lib/raptor-io/socket.rb', line 142

def respond_to_missing?(meth, include_private=false)
  ::Socket.respond_to?(meth, include_private)
end

.select(read_array = [], write_array = [], error_array = [], timeout = nil) ⇒ Array?

Like IO.select, but smarter

OpenSSL does its own buffering which can result in a consumed TCP buffer, leading ‘IO.select` to think that the SSLSocket has no more data to provide, when that’s not the case, effectively making ‘IO.select` block forever, even though the SSLSocket’s buffer has not yet been consumed.

We work around this by attempting a non-blocking read of one byte on each of the ‘read_array`, and putting the byte back with `Socket#ungetc` if it worked, or running it through the the real `IO.select` if it doesn’t.

Parameters:

  • read_array (Array) (defaults to: [])

    (see IO.select)

  • write_array (Array) (defaults to: [])

    (see IO.select)

  • error_array (Array) (defaults to: [])

    (see IO.select)

  • timeout (Fixnum, nil) (defaults to: nil)

    (see IO.select)

Returns:

  • (Array)

    An Array containing three arrays of IO objects that are ready for reading, ready for writing, or have pending errors, respectively.

  • (nil)

    If optional ‘timeout` is given and `timeout` seconds elapse before any data is available

See Also:



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
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
97
98
99
100
101
102
103
104
105
# File 'lib/raptor-io/socket.rb', line 41

def self.select(read_array=[], write_array=[], error_array=[], timeout=nil)
  read_array  ||= []
  write_array ||= []
  error_array ||= []

  readers_with_data = []

  selectable_readers = read_array.dup.delete_if do |reader|
    begin
      # If this socket doesn't have a read_nonblock method, then it's
      # a server of some kind and we have to run it through the real
      # select to see if it can {TCPServer#accept accept}.
      next false unless reader.respond_to? :read_nonblock

      byte = reader.read_nonblock(1)
    rescue IO::WaitReadable, IO::WaitWritable
      # Then this thing needs to go through the real select to be able
      # to tell if it has data.
      #
      # Note that {IO::WaitWritable} is needed here because OpenSSL
      # sockets can block for writing when calling `read*` because of
      # session renegotiation and the like.
      false
    rescue EOFError
      # Then this thing has an empty read buffer and there's no more
      # on the wire. We mark it as having data so a subsequent
      # read or read_nonblock will raise EOFError appropriately.
      readers_with_data << reader
      true
    else
      # Then this thing has data already in its read buffer and we can
      # skip the real select for it.
      reader.ungetc(byte)
      readers_with_data << reader
      true
    end
  end

  if readers_with_data.any?
    if selectable_readers.any? || write_array.any? || error_array.any?
      #$stderr.puts(" ----- Selecting readers:")
      #pp selectable_readers
      # Then see if anything has data right now by using a 0 timeout
      r,w,e = IO.select(selectable_readers, write_array, error_array, 0)

      real = [
        readers_with_data | (r || []),
        w || [],
        e || []
      ]
    else
      # Then there's nothing selectable and we can just return stuff
      # that has buffered data
      real = [ readers_with_data, [], [] ]
    end
  else
    # Then wait the given timeout, regardless of whether the arrays
    # are empty
    real = IO.select(read_array, write_array, error_array, timeout)
  end

  #$stderr.puts '------ RaptorIO::Socket.select result ------'
  #pp real
  return real
end

.translate_errors(&block) ⇒ Object

Captures Ruby exceptions and converts them to RaptorIO Errors.

Parameters:

  • block (Block)

    Block to run.



112
113
114
115
116
117
118
# File 'lib/raptor-io/socket.rb', line 112

def translate_errors( &block )
  block.call
rescue ::Errno::EPIPE, ::Errno::ECONNRESET => e
  raise RaptorIO::Socket::Error::BrokenPipe, e.to_s
rescue ::Errno::ECONNREFUSED => e
  raise RaptorIO::Socket::Error::ConnectionRefused, e.to_s
end

Instance Method Details

#closeObject



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

def_delegator :@socket, :close, :close

#closed?Object



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

def_delegator :@socket, :closed?, :closed?

#ssl?Boolean

Whether this socket is an SSL stream.

Returns:

  • (Boolean)


171
172
173
# File 'lib/raptor-io/socket.rb', line 171

def ssl?
  false
end

#to_ioIO

Used by Kernel.select

Returns:

  • (IO)


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

def_delegator :@socket, :to_io, :to_io