class Stream
Transport an opaque byte stream over HTTY chunks.
Definitions
PACKET_SIZE = 1024*3
Since base64 encoding adds 33% overhead, we can fit 3KB of binary data into a single HTTY chunk without exceeding the typical MTU of 4KB:
def initialize(input, output = input, packet_size: PACKET_SIZE)
Create a stream on top of HTTY framed input and output.
Signature
-
parameter
inputIO | IO::Stream The source of framed HTTY chunks.
-
parameter
outputIO | IO::Stream | Nil The sink for framed HTTY chunks.
-
parameter
packet_sizeInteger The maximum payload size for each chunk.
Implementation
def initialize(input, output = input, packet_size: PACKET_SIZE)
@framer = Framer.new(::IO::Stream(input), ::IO::Stream(output))
@packet_size = packet_size
@buffer = +"".b
@local_closed = false
@remote_closed = false
end
def io
Return the writable IO object used by the underlying framer.
Signature
-
returns
IO | IO::Stream The output side of the framed transport.
Implementation
def io
@framer.output
end
def read(length = nil)
Read application bytes from the HTTY transport.
Signature
-
parameter
lengthInteger | Nil The exact number of bytes to read, or
nilfor all buffered bytes.-
returns
String | Nil The requested bytes, an empty binary string for
0, ornilif more data is required or the remote side is closed.
Implementation
def read(length = nil)
return +"".b if length == 0
fill(length)
return nil if @buffer.empty? && @remote_closed
return nil if @buffer.empty?
return nil if length && @buffer.bytesize < length && !@remote_closed
if length
return @buffer.slice!(0, length)
else
return @buffer.slice!(0, @buffer.bytesize)
end
end
def write(data)
Write application bytes as one or more HTTY chunks.
Signature
-
parameter
dataString | Array(Integer) The opaque bytes to send.
-
returns
self -
raises
IOError If the local side of the transport is closed.
Implementation
def write(data)
raise IOError, "HTTY stream is closed for writing!" if @local_closed
data = data.to_s.b
until data.empty?
chunk = data.byteslice(0, @packet_size)
@framer.write_chunk(chunk)
data = data.byteslice(chunk.bytesize..)
end
@framer.flush
return self
end
def flush
Flush any buffered output through the underlying framer.
Signature
-
returns
void
Implementation
def flush
@framer.flush
end
def close
Close the local write side of this stream abstraction. HTTY does not define a close packet, and closing this object does not close the underlying terminal IO.
Signature
-
returns
void
Implementation
def close
unless @local_closed
@local_closed = true
@framer.flush
end
end
def closed?
Check whether the local side of the transport is closed.
Signature
-
returns
bool True if local writes have been closed.
Implementation
def closed?
@local_closed
end
def readable?
Check whether the remote side may still provide more data.
Signature
-
returns
bool True if the remote side has not sent or implied a close.
Implementation
def readable?
!@remote_closed
end