Class: Noise::Transport::Stream

Inherits:
Object
  • Object
show all
Defined in:
lib/noise/transport/stream.rb

Overview

The IO a framed transport sits on, and the only place that deals with the ways a stream differs from a message: it hands over fewer bytes than were asked for, takes fewer than it was given, ends in the middle of something, or goes quiet.

A transport holds one of these and asks it for whole frames. Like the transport above it, one belongs to one thread: a stream has a position, and two threads reading it take halves of each other's frames.

Instance Method Summary collapse

Constructor Details

#initialize(io, read_timeout: nil) ⇒ Stream

Returns a new instance of Stream.

Parameters:

  • io (IO)

    the stream. Anything that answers #write and #readpartial will do, which is what makes a StringIO usable in a test.

  • read_timeout (Numeric, nil) (defaults to: nil)

    how long to wait for the next bytes before giving up, in seconds. nil waits as long as the IO does. It applies to each wait rather than to a frame as a whole, so a peer that sends a byte at a time holds a read open without ever tripping it.

Raises:

  • (ArgumentError)

    if read_timeout is negative, or if the IO cannot be waited on and the timeout would therefore do nothing.



21
22
23
24
25
26
# File 'lib/noise/transport/stream.rb', line 21

def initialize(io, read_timeout: nil)
  validate_read_timeout!(io, read_timeout)

  @io = io
  @read_timeout = read_timeout
end

Instance Method Details

#read_exactly(length) ⇒ String

Returns exactly length bytes.

Parameters:

  • length (Integer)

    how many bytes to read.

Returns:

  • (String)

    exactly length bytes.

Raises:



49
50
51
# File 'lib/noise/transport/stream.rb', line 49

def read_exactly(length)
  gather(length) || truncated!(length, 0)
end

#read_exactly_or_nil(length) ⇒ String?

The same, for the first bytes of a frame, where a stream that has ended has not been cut short: it is the other party saying goodbye between one frame and the next.

Parameters:

  • length (Integer)

    how many bytes to read.

Returns:

  • (String, nil)

    exactly length bytes, or nil if the stream had already ended.

Raises:



60
61
62
# File 'lib/noise/transport/stream.rb', line 60

def read_exactly_or_nil(length)
  gather(length)
end

#write(frame) ⇒ Integer

Writes all of it, however many writes that takes.

Parameters:

  • frame (String)

    the bytes to write.

Returns:

  • (Integer)

    frame.bytesize, once all of it has gone out.

Raises:

  • (IOError)

    if a write takes none of them, which would otherwise spin here forever.



33
34
35
36
37
38
39
40
41
42
43
# File 'lib/noise/transport/stream.rb', line 33

def write(frame)
  written = 0
  while written < frame.bytesize
    taken = @io.write(frame.byteslice(written, frame.bytesize - written))
    raise IOError, "#{@io.class} took #{taken.inspect} of the #{frame.bytesize} bytes it was given." unless
      taken.is_a?(Integer) && taken.positive?

    written += taken
  end
  written
end