Class: Protocol::HTTY::Stream

Inherits:
Object
  • Object
show all
Defined in:
lib/protocol/htty/stream.rb

Overview

Transport an opaque byte stream after the HTTY bootstrap handshake.

Constant Summary collapse

ESC =
"\e"
DCS =
"#{ESC}P"
ST =
"#{ESC}\\"
BOOTSTRAP_PREFIX =
"+H"
RAW_MODE =
"raw"

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(stream) ⇒ Stream

Create a stream on top of a raw byte-preserving transport.



37
38
39
40
# File 'lib/protocol/htty/stream.rb', line 37

def initialize(stream)
	@stream = stream
	@local_closed = false
end

Instance Attribute Details

#streamObject (readonly)

Returns the value of attribute stream.



42
43
44
# File 'lib/protocol/htty/stream.rb', line 42

def stream
  @stream
end

Class Method Details

.open(stream, bootstrap: nil, mode: RAW_MODE) ⇒ Object



18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/protocol/htty/stream.rb', line 18

def self.open(stream, bootstrap: nil, mode: RAW_MODE)
	stream = self.new(::IO::Stream(stream))
	
	case bootstrap
	when :write
		stream.write_bootstrap(mode)
	when :read
		actual_mode = stream.read_bootstrap
		
		unless actual_mode == mode
			raise ProtocolError, "Expected HTTY bootstrap mode #{mode.inspect}, got #{actual_mode.inspect}"
		end
	end
	
	return stream
end

Instance Method Details

#close_write(error = nil) ⇒ Object Also known as: 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.



95
96
97
98
99
100
# File 'lib/protocol/htty/stream.rb', line 95

def close_write(error = nil)
	unless @local_closed
		@local_closed = true
		@stream.flush
	end
end

#closed?Boolean

Check whether the local side of the transport is closed.

Returns:

  • (Boolean)


106
107
108
# File 'lib/protocol/htty/stream.rb', line 106

def closed?
	@local_closed
end

#flushObject

Flush any buffered output through the underlying stream.



88
89
90
# File 'lib/protocol/htty/stream.rb', line 88

def flush
	@stream.flush
end

#ioObject

Return the underlying duplex stream.



45
46
47
# File 'lib/protocol/htty/stream.rb', line 45

def io
	@stream
end

#read(length = nil) ⇒ Object

Read application bytes from the HTTY transport.



70
71
72
73
# File 'lib/protocol/htty/stream.rb', line 70

def read(length = nil)
	return +"".b if length == 0
	return @stream.read(length)
end

#read_bootstrapObject



54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/protocol/htty/stream.rb', line 54

def read_bootstrap
	while payload = read_payload
		next unless payload.start_with?(BOOTSTRAP_PREFIX)
		mode = payload.delete_prefix(BOOTSTRAP_PREFIX)
		
		unless mode == RAW_MODE
			raise ProtocolError, "Unsupported HTTY bootstrap mode: #{mode.inspect}"
		end
		
		return mode
	end
	
	return nil
end

#readable?Boolean

Check whether the remote side may still provide more data.

Returns:

  • (Boolean)


112
113
114
# File 'lib/protocol/htty/stream.rb', line 112

def readable?
	!@stream.closed?
end

#write(data) ⇒ Object

Write application bytes after bootstrap.

Raises:

  • (IOError)


78
79
80
81
82
83
84
# File 'lib/protocol/htty/stream.rb', line 78

def write(data)
	raise IOError, "HTTY stream is closed for writing!" if @local_closed
	
	@stream.write(data.to_s.b)
	
	return self
end

#write_bootstrap(mode = RAW_MODE) ⇒ Object



49
50
51
52
# File 'lib/protocol/htty/stream.rb', line 49

def write_bootstrap(mode = RAW_MODE)
	@stream.write("#{DCS}#{BOOTSTRAP_PREFIX}#{mode}#{ST}")
	@stream.flush
end