Module: Net::SSH::BufferedIo

Includes:
Loggable
Included in:
Transport::PacketStream
Defined in:
lib/net/ssh/buffered_io.rb

Overview

This module is used to extend sockets and other IO objects, to allow them to be buffered for both read and write. This abstraction makes it quite easy to write a select-based event loop (see Net::SSH::Connection::Session#listen_to).

The general idea is that instead of calling #read directly on an IO that has been extended with this module, you call #fill (to add pending input to the internal read buffer), and then #read_available (to read from that buffer). Likewise, you don’t call #write directly, you call #enqueue to add data to the write buffer, and then #send_pending or #wait_for_pending_sends to actually send the data across the wire.

In this way you can easily use the object as an argument to IO.select, calling #fill when it is available for read, or #send_pending when it is available for write, and then call #enqueue and #read_available during the idle times.

socket = TCPSocket.new(address, port)
socket.extend(Net::SSH::BufferedIo)

ssh.listen_to(socket)

ssh.loop do
  if socket.available > 0
    puts socket.read_available
    socket.enqueue("response\n")
  end
end

Note that this module must be used to extend an instance, and should not be included in a class. If you do want to use it via an include, then you must make sure to invoke the private #initialize_buffered_io method in your class’ #initialize method:

class Foo < IO
  include Net::SSH::BufferedIo

  def initialize
    initialize_buffered_io
    # ...
  end
end

Instance Attribute Summary

Attributes included from Loggable

#logger

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Loggable

#debug, #error, #fatal, #info, #lwarn

Class Method Details

.extended(object) ⇒ Object

Called when the #extend is called on an object, with this module as the argument. It ensures that the modules instance variables are all properly initialized.



54
55
56
57
# File 'lib/net/ssh/buffered_io.rb', line 54

def self.extended(object) # :nodoc:
  # need to use __send__ because #send is overridden in Socket
  object.__send__(:initialize_buffered_io)
end

Instance Method Details

#availableObject

Returns the number of bytes available to be read from the input buffer. (See #read_available.)



81
82
83
# File 'lib/net/ssh/buffered_io.rb', line 81

def available
  input.available
end

#enqueue(data) ⇒ Object

Enqueues data in the output buffer, to be written when #send_pending is called. Note that the data is not sent immediately by this method!



87
88
89
# File 'lib/net/ssh/buffered_io.rb', line 87

def enqueue(data)
  output.append(data)
end

#fill(n = 8192) ⇒ Object

Tries to read up to n bytes of data from the remote end, and appends the data to the input buffer. It returns the number of bytes read, or 0 if no data was available to be read.



62
63
64
65
66
67
68
69
70
71
# File 'lib/net/ssh/buffered_io.rb', line 62

def fill(n = 8192)
  input.consume!
  data = recv(n)
  debug { "read #{data.length} bytes" }
  input.append(data)
  return data.length
rescue EOFError => e
  @input_errors << e
  return 0
end

#pending_write?Boolean

Returns true if there is data waiting in the output buffer, and false otherwise.

Returns:

  • (Boolean)


93
94
95
# File 'lib/net/ssh/buffered_io.rb', line 93

def pending_write?
  output.length > 0
end

#read_available(length = nil) ⇒ Object

Read up to length bytes from the input buffer. If length is nil, all available data is read from the buffer. (See #available.)



75
76
77
# File 'lib/net/ssh/buffered_io.rb', line 75

def read_available(length = nil)
  input.read(length || available)
end

#read_bufferObject

:nodoc:



128
129
130
# File 'lib/net/ssh/buffered_io.rb', line 128

def read_buffer # :nodoc:
  input.to_s
end

#send_pendingObject

Sends as much of the pending output as possible. Returns true if any data was sent, and false otherwise.



99
100
101
102
103
104
105
106
107
108
# File 'lib/net/ssh/buffered_io.rb', line 99

def send_pending
  if output.length > 0
    sent = send(output.to_s, 0)
    debug { "sent #{sent} bytes" }
    output.consume!(sent)
    return sent > 0
  else
    return false
  end
end

#wait_for_pending_sendsObject

Calls #send_pending repeatedly, if necessary, blocking until the output buffer is empty.



112
113
114
115
116
117
118
119
120
# File 'lib/net/ssh/buffered_io.rb', line 112

def wait_for_pending_sends
  send_pending
  while output.length > 0
    result = IO.select(nil, [self]) or next
    next unless result[1].any?

    send_pending
  end
end

#write_bufferObject

:nodoc:



124
125
126
# File 'lib/net/ssh/buffered_io.rb', line 124

def write_buffer # :nodoc:
  output.to_s
end