Class: Protocol::HTTY::Stream
- Inherits:
-
Object
- Object
- Protocol::HTTY::Stream
- 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"- HTTP2_FRAME_HEADER_SIZE =
9
Instance Attribute Summary collapse
-
#input ⇒ Object
readonly
Returns the value of attribute input.
-
#output ⇒ Object
readonly
Returns the value of attribute output.
Class Method Summary collapse
Instance Method Summary collapse
-
#close_write(error = nil) ⇒ Object
(also: #close)
Close the local write side of this stream abstraction.
-
#closed? ⇒ Boolean
Check whether the local side of the transport is closed.
-
#flush ⇒ Object
Flush any buffered output through the underlying stream.
-
#initialize(input, output) ⇒ Stream
constructor
Create a stream on top of raw byte-preserving endpoints.
-
#io ⇒ Object
Return the underlying output stream.
-
#read(length = nil) ⇒ Object
Read application bytes from the HTTY transport.
- #read_bootstrap ⇒ Object
-
#readable? ⇒ Boolean
Check whether the remote side may still provide more data.
-
#write(data, flush: false) ⇒ Object
Write application bytes after bootstrap.
- #write_bootstrap(mode = RAW_MODE) ⇒ Object
Constructor Details
#initialize(input, output) ⇒ Stream
Create a stream on top of raw byte-preserving endpoints.
40 41 42 43 44 45 |
# File 'lib/protocol/htty/stream.rb', line 40 def initialize(input, output) @input = input @output = ::IO::Stream(output) @frame_remaining = nil @local_closed = false end |
Instance Attribute Details
#input ⇒ Object (readonly)
Returns the value of attribute input.
47 48 49 |
# File 'lib/protocol/htty/stream.rb', line 47 def input @input end |
#output ⇒ Object (readonly)
Returns the value of attribute output.
48 49 50 |
# File 'lib/protocol/htty/stream.rb', line 48 def output @output end |
Class Method Details
.frame_length(buffer) ⇒ Object
143 144 145 146 |
# File 'lib/protocol/htty/stream.rb', line 143 def self.frame_length(buffer) length_high, length_low = buffer.unpack("Cn") return (length_high << 16) | length_low end |
.open(input, output, bootstrap: nil, mode: RAW_MODE) ⇒ Object
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
# File 'lib/protocol/htty/stream.rb', line 20 def self.open(input, output, bootstrap: nil, mode: RAW_MODE) stream = self.new(input, output) 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.
120 121 122 123 124 125 |
# File 'lib/protocol/htty/stream.rb', line 120 def close_write(error = nil) unless @local_closed @local_closed = true @output.flush end end |
#closed? ⇒ Boolean
Check whether the local side of the transport is closed.
131 132 133 |
# File 'lib/protocol/htty/stream.rb', line 131 def closed? @local_closed end |
#flush ⇒ Object
Flush any buffered output through the underlying stream.
113 114 115 |
# File 'lib/protocol/htty/stream.rb', line 113 def flush @output.flush end |
#io ⇒ Object
Return the underlying output stream.
51 52 53 |
# File 'lib/protocol/htty/stream.rb', line 51 def io @output end |
#read(length = nil) ⇒ Object
Read application bytes from the HTTY transport.
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
# File 'lib/protocol/htty/stream.rb', line 76 def read(length = nil) if length == 0 @frame_remaining = nil if @frame_remaining == 0 return +"".b end requested_length = length length = [length, @frame_remaining].min if length && @frame_remaining && @frame_remaining > 0 buffer = read_exact(length) if buffer && requested_length == HTTP2_FRAME_HEADER_SIZE && !@frame_remaining if buffer.bytesize == HTTP2_FRAME_HEADER_SIZE frame_length = self.class.frame_length(buffer) @frame_remaining = frame_length if frame_length > 0 end elsif buffer && @frame_remaining @frame_remaining -= buffer.bytesize @frame_remaining = nil if @frame_remaining <= 0 end return buffer end |
#read_bootstrap ⇒ Object
60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
# File 'lib/protocol/htty/stream.rb', line 60 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.
137 138 139 |
# File 'lib/protocol/htty/stream.rb', line 137 def readable? !(@input.respond_to?(:closed?) && @input.closed?) end |
#write(data, flush: false) ⇒ Object
Write application bytes after bootstrap.
102 103 104 105 106 107 108 109 |
# File 'lib/protocol/htty/stream.rb', line 102 def write(data, flush: false) raise IOError, "HTTY stream is closed for writing!" if @local_closed @output.write(data.to_s.b) @output.flush if flush return self end |
#write_bootstrap(mode = RAW_MODE) ⇒ Object
55 56 57 58 |
# File 'lib/protocol/htty/stream.rb', line 55 def write_bootstrap(mode = RAW_MODE) @output.write("#{DCS}#{BOOTSTRAP_PREFIX}#{mode}#{ST}") @output.flush end |