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(input, output) ⇒ Stream

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



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

def initialize(input, output)
	@input = input
	@output = output
	@local_closed = false
end

Instance Attribute Details

#inputObject (readonly)

Returns the value of attribute input.



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

def input
  @input
end

#outputObject (readonly)

Returns the value of attribute output.



52
53
54
# File 'lib/protocol/htty/stream.rb', line 52

def output
  @output
end

Class Method Details

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



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

def self.open(input, output, bootstrap: nil, mode: RAW_MODE)
	stream = self.new(input, output)
	
	# Disable buffering:
	input.sync = true
	output.sync = true
	
	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.



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

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.

Returns:

  • (Boolean)


117
118
119
# File 'lib/protocol/htty/stream.rb', line 117

def closed?
	@local_closed
end

#flushObject

Flush any buffered output through the underlying stream.



99
100
101
# File 'lib/protocol/htty/stream.rb', line 99

def flush
	@output.flush
end

#ioObject

Return the underlying output stream.



55
56
57
# File 'lib/protocol/htty/stream.rb', line 55

def io
	@output
end

#read(length = nil) ⇒ Object

Read application bytes from the HTTY transport. The HTTP/2 framer always requests exact byte counts (header size, then payload length), so we delegate directly to the underlying input.



81
82
83
# File 'lib/protocol/htty/stream.rb', line 81

def read(length = nil)
	@input.read(length)
end

#read_bootstrapObject



64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/protocol/htty/stream.rb', line 64

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)


123
124
125
# File 'lib/protocol/htty/stream.rb', line 123

def readable?
	!(@input.respond_to?(:closed?) && @input.closed?)
end

#remote_addressObject

Required by Protocol::HTTP::Peer but not applicable to HTTY, which does not have a concept of remote addresses.



47
48
49
# File 'lib/protocol/htty/stream.rb', line 47

def remote_address
	return nil
end

#write(data, flush: false) ⇒ Object

Write application bytes after bootstrap.

Raises:

  • (IOError)


88
89
90
91
92
93
94
95
# File 'lib/protocol/htty/stream.rb', line 88

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



59
60
61
62
# File 'lib/protocol/htty/stream.rb', line 59

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