protocol-htty 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: c222bcf565b63477735114b057e88c45508674629d78646c0fa73ab34b1abbd9
4
+ data.tar.gz: fd7405a11f04ee961c218473951ef8f0051dde6f0c581c2ecc129a87f9fbfe37
5
+ SHA512:
6
+ metadata.gz: 0cf629cde9cb0d76603be89e9290335087a540c92be10e0f433732e6f323306ec8ddd7ba5cbe104ea0b7ef9ac57983bd3b0017005742c4065af1ecceebeca34d
7
+ data.tar.gz: db74e7230fdd22dbc18f1f4e85526856d3ebdd9eb550c1f7c3fdd6c3e84fcfc45d9ccb97559f813c9985d39d693e0ba2ef21d28a337086f94f76367c8de6dbfc
checksums.yaml.gz.sig ADDED
Binary file
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2026, by Samuel Williams.
5
+
6
+ # @namespace
7
+ module Protocol
8
+ # @namespace
9
+ module HTTY
10
+ # The base class for HTTY transport errors.
11
+ class Error < StandardError
12
+ end
13
+
14
+ # Raised when an HTTY control packet is malformed or unsupported.
15
+ class ProtocolError < Error
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2026, by Samuel Williams.
5
+
6
+ require "base64"
7
+ require "protocol/http2/framer"
8
+
9
+ module Protocol
10
+ module HTTY
11
+ # Encode and decode HTTY chunks on top of byte-oriented IO objects.
12
+ class Framer
13
+ ESC = "\e"
14
+ DCS = "#{ESC}P"
15
+ ST = "#{ESC}\\"
16
+ PREFIX = "HTTY;1;"
17
+
18
+ # Create a framer around the given input and output streams.
19
+ # @parameter input [Interface(:read)] The stream to read framed packets from.
20
+ # @parameter output [Interface(:write, :flush) | Nil] The stream to write framed packets to.
21
+ def initialize(input, output = input)
22
+ @input = input
23
+ @output = output
24
+ end
25
+
26
+ attr :input
27
+ attr :output
28
+
29
+ # Write a single HTTY chunk to the output stream.
30
+ # @parameter payload [String | Array(Integer)] The opaque bytes to encode.
31
+ # @returns [void]
32
+ def write_chunk(payload)
33
+ encoded = Base64.strict_encode64(payload.to_s.b)
34
+ @output.write("#{DCS}#{PREFIX}#{encoded}#{ST}")
35
+ end
36
+
37
+ # Read the next HTTY chunk from the input stream.
38
+ # Non-HTTY terminal output is ignored until a valid chunk prefix is found.
39
+ # @returns [String | Nil] The decoded payload, or `nil` on end of stream.
40
+ # @raises [ProtocolError] If the chunk prefix or chunk structure is invalid.
41
+ # @raises [ArgumentError] If the packet payload is not valid base64.
42
+ # @raises [EOFError] If the chunk terminator is missing.
43
+ def read_chunk
44
+ while payload = read_payload
45
+ if payload.start_with?("HTTY;") && !payload.start_with?(PREFIX)
46
+ raise ProtocolError, "Unsupported HTTY chunk version: #{payload.inspect}"
47
+ end
48
+
49
+ next unless payload.start_with?(PREFIX)
50
+ encoded = payload.delete_prefix(PREFIX)
51
+ return Base64.strict_decode64(encoded)
52
+ end
53
+
54
+ return nil
55
+ end
56
+
57
+ # Flush the output stream if it supports flushing.
58
+ # @returns [void]
59
+ def flush
60
+ @output.flush if @output.respond_to?(:flush)
61
+ end
62
+
63
+ # Close the wrapped input and output streams.
64
+ # If input and output are the same object, it is only closed once.
65
+ # @returns [void]
66
+ def close
67
+ @output.close if @output.respond_to?(:close)
68
+ @input.close if !@input.equal?(@output) && @input.respond_to?(:close)
69
+ end
70
+
71
+ # Check whether the output stream has been closed.
72
+ # @returns [bool] True if the output stream reports that it is closed.
73
+ def closed?
74
+ @output.respond_to?(:closed?) && @output.closed?
75
+ end
76
+
77
+ private
78
+
79
+ def read_payload
80
+ while prefix = @input.read(1)
81
+ next unless prefix == ESC
82
+
83
+ marker = @input.read(1)
84
+ return nil unless marker
85
+ next unless marker == "P"
86
+
87
+ return consume_packet
88
+ end
89
+
90
+ return nil
91
+ end
92
+
93
+ def consume_packet
94
+ buffer = +""
95
+
96
+ while chunk = @input.read(1)
97
+ if chunk == ESC
98
+ terminator = @input.read(1)
99
+ return buffer if terminator == "\\"
100
+
101
+ buffer << chunk
102
+ buffer << terminator if terminator
103
+ else
104
+ buffer << chunk
105
+ end
106
+ end
107
+
108
+ raise EOFError, "Incomplete HTTY chunk!"
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,124 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2026, by Samuel Williams.
5
+
6
+ require "io/stream"
7
+
8
+ module Protocol
9
+ module HTTY
10
+ # Transport an opaque byte stream over HTTY chunks.
11
+ class Stream
12
+ # 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:
13
+ PACKET_SIZE = 1024*3
14
+
15
+ # Create a stream on top of HTTY framed input and output.
16
+ # @parameter input [IO | IO::Stream] The source of framed HTTY chunks.
17
+ # @parameter output [IO | IO::Stream | Nil] The sink for framed HTTY chunks.
18
+ # @parameter packet_size [Integer] The maximum payload size for each chunk.
19
+ def initialize(input, output = input, packet_size: PACKET_SIZE)
20
+ @framer = Framer.new(::IO::Stream(input), ::IO::Stream(output))
21
+ @packet_size = packet_size
22
+ @buffer = +"".b
23
+ @local_closed = false
24
+ @remote_closed = false
25
+ end
26
+
27
+ attr :framer
28
+
29
+ # Return the writable IO object used by the underlying framer.
30
+ # @returns [IO | IO::Stream] The output side of the framed transport.
31
+ def io
32
+ @framer.output
33
+ end
34
+
35
+ # Read application bytes from the HTTY transport.
36
+ # @parameter length [Integer | Nil] The exact number of bytes to read, or `nil` for all buffered bytes.
37
+ # @returns [String | Nil] The requested bytes, an empty binary string for `0`, or `nil` if more data is required or the remote side is closed.
38
+ def read(length = nil)
39
+ return +"".b if length == 0
40
+
41
+ fill(length)
42
+
43
+ return nil if @buffer.empty? && @remote_closed
44
+ return nil if @buffer.empty?
45
+ return nil if length && @buffer.bytesize < length && !@remote_closed
46
+
47
+ if length
48
+ return @buffer.slice!(0, length)
49
+ else
50
+ return @buffer.slice!(0, @buffer.bytesize)
51
+ end
52
+ end
53
+
54
+ # Write application bytes as one or more HTTY chunks.
55
+ # @parameter data [String | Array(Integer)] The opaque bytes to send.
56
+ # @returns [self]
57
+ # @raises [IOError] If the local side of the transport is closed.
58
+ def write(data)
59
+ raise IOError, "HTTY stream is closed for writing!" if @local_closed
60
+
61
+ data = data.to_s.b
62
+
63
+ until data.empty?
64
+ chunk = data.byteslice(0, @packet_size)
65
+ @framer.write_chunk(chunk)
66
+ data = data.byteslice(chunk.bytesize..)
67
+ end
68
+
69
+ @framer.flush
70
+
71
+ return self
72
+ end
73
+
74
+ # Flush any buffered output through the underlying framer.
75
+ # @returns [void]
76
+ def flush
77
+ @framer.flush
78
+ end
79
+
80
+ # Close the local write side of this stream abstraction.
81
+ # HTTY does not define a close packet, and closing this object does not close the underlying terminal IO.
82
+ # @returns [void]
83
+ def close
84
+ unless @local_closed
85
+ @local_closed = true
86
+ @framer.flush
87
+ end
88
+ end
89
+
90
+ # Check whether the local side of the transport is closed.
91
+ # @returns [bool] True if local writes have been closed.
92
+ def closed?
93
+ @local_closed
94
+ end
95
+
96
+ # Check whether the remote side may still provide more data.
97
+ # @returns [bool] True if the remote side has not sent or implied a close.
98
+ def readable?
99
+ !@remote_closed
100
+ end
101
+
102
+ private
103
+
104
+ def fill(length)
105
+ while needs_more_data?(length)
106
+ chunk = @framer.read_chunk
107
+
108
+ unless chunk
109
+ @remote_closed = true
110
+ break
111
+ end
112
+ @buffer << chunk
113
+ end
114
+ end
115
+
116
+ def needs_more_data?(length)
117
+ return false if @remote_closed
118
+ return @buffer.empty? unless length
119
+
120
+ @buffer.bytesize < length
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2026, by Samuel Williams.
5
+
6
+ module Protocol
7
+ module HTTY
8
+ VERSION = "0.1.0"
9
+ end
10
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2026, by Samuel Williams.
5
+
6
+ require_relative "htty/version"
7
+ require_relative "htty/error"
8
+ require_relative "htty/framer"
9
+ require_relative "htty/stream"
data/license.md ADDED
@@ -0,0 +1,21 @@
1
+ # MIT License
2
+
3
+ Copyright, 2026, by Samuel Williams.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/readme.md ADDED
@@ -0,0 +1,73 @@
1
+ # Protocol::HTTY
2
+
3
+ `protocol-htty` defines a small, terminal-safe framing layer for carrying an opaque byte stream over TTY side channels.
4
+
5
+ [![Development Status](https://github.com/socketry/protocol-htty/workflows/Test/badge.svg)](https://github.com/socketry/protocol-htty/actions?workflow=Test)
6
+
7
+ ## Motivation
8
+
9
+ Traditional terminal user interfaces are useful, but they are also a poor fit for many modern interactions. They are constrained by character-cell rendering, limited layout semantics, awkward input models, and a presentation layer that was never designed for rich documents or structured application state.
10
+
11
+ In practice, this means TUIs often force applications into compromises: text-heavy layouts, ad-hoc protocols, and bespoke escape-sequence behavior that is hard to standardise across runtimes and terminals.
12
+
13
+ HTTY exists to keep the portability and deployment advantages of terminal workflows while avoiding the need to build an entire application model out of terminal control codes. Instead of asking the terminal stream itself to represent higher-level UI state, HTTY provides a small framing layer that can carry a normal plaintext HTTP/2 connection alongside terminal traffic, enabling applications to attach browser surfaces to a normal terminal session over HTTY.
14
+
15
+ ## Design
16
+
17
+ HTTY does not model application requests, regions, or resources. It transports the two directions of a single plaintext HTTP/2 (`h2c`) connection over terminal-safe chunks without introducing a second session protocol.
18
+
19
+ Each chunk is encoded as a DCS sequence:
20
+
21
+ ``` text
22
+ ESC P HTTY;1;BASE64_CHUNK ESC \
23
+ ```
24
+
25
+ The framing layer intentionally stays small so it can be reimplemented in other runtimes.
26
+
27
+ ## Usage
28
+
29
+ Please see the [project documentation](https://socketry.github.io/protocol-htty/) for more details.
30
+
31
+ - [Getting Started](https://socketry.github.io/protocol-htty/guides/getting-started/index) - This guide explains how to get started with `protocol-htty` for terminal-safe HTTP/2 byte stream transport.
32
+
33
+ - [HTTY Specification](https://socketry.github.io/protocol-htty/guides/specification/index) - This document specifies HTTY as a terminal-safe framing layer for carrying a plaintext HTTP/2 (`h2c`) byte stream over terminal side channels.
34
+
35
+ ## Releases
36
+
37
+ Please see the [project releases](https://socketry.github.io/protocol-htty/releases/index) for all releases.
38
+
39
+ ### v0.1.0
40
+
41
+ ## Contributing
42
+
43
+ We welcome contributions to this project.
44
+
45
+ 1. Fork it.
46
+ 2. Create your feature branch (`git checkout -b my-new-feature`).
47
+ 3. Commit your changes (`git commit -am 'Add some feature'`).
48
+ 4. Push to the branch (`git push origin my-new-feature`).
49
+ 5. Create new Pull Request.
50
+
51
+ ### Running Tests
52
+
53
+ To run the test suite:
54
+
55
+ ``` shell
56
+ bundle exec sus
57
+ ```
58
+
59
+ ### Making Releases
60
+
61
+ To make a new release:
62
+
63
+ ``` shell
64
+ bundle exec bake gem:release:patch # or minor or major
65
+ ```
66
+
67
+ ### Developer Certificate of Origin
68
+
69
+ In order to protect users of this project, we require all contributors to comply with the [Developer Certificate of Origin](https://developercertificate.org/). This ensures that all contributions are properly licensed and attributed.
70
+
71
+ ### Community Guidelines
72
+
73
+ This project is best served by a collaborative and respectful environment. Treat each other professionally, respect differing viewpoints, and engage constructively. Harassment, discrimination, or harmful behavior is not tolerated. Communicate clearly, listen actively, and support one another. If any issues arise, please inform the project maintainers.
data/releases.md ADDED
@@ -0,0 +1,3 @@
1
+ # Releases
2
+
3
+ ## v0.1.0
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2026, by Samuel Williams.
5
+
6
+ require "stringio"
7
+ require "protocol/http2/framer"
8
+ require "protocol/htty"
9
+
10
+ describe Protocol::HTTY::Framer do
11
+ let(:input) {StringIO.new}
12
+ let(:output) {StringIO.new}
13
+ let(:framer) {subject.new(input, output)}
14
+
15
+ it "writes terminal-safe chunks" do
16
+ framer.write_chunk(Protocol::HTTP2::CONNECTION_PREFACE)
17
+
18
+ expect(output.string).to be == "\ePHTTY;1;UFJJICogSFRUUC8yLjANCg0KU00NCg0K\e\\"
19
+ end
20
+
21
+ it "reads terminal-safe chunks" do
22
+ input.string = "hello\ePHTTY;1;UFJJICogSFRUUC8yLjANCg0KU00NCg0K\e\\world"
23
+ input.rewind
24
+
25
+ chunk = framer.read_chunk
26
+
27
+ expect(chunk).to be == Protocol::HTTP2::CONNECTION_PREFACE
28
+ end
29
+
30
+ it "raises on malformed chunks" do
31
+ input.string = "\ePHTTY;1\e\\"
32
+ input.rewind
33
+
34
+ expect do
35
+ framer.read_chunk
36
+ end.to raise_exception(Protocol::HTTY::ProtocolError)
37
+ end
38
+
39
+ it "raises on incomplete chunks" do
40
+ input.string = "\ePHTTY;1;QUJD"
41
+ input.rewind
42
+
43
+ expect do
44
+ framer.read_chunk
45
+ end.to raise_exception(EOFError)
46
+ end
47
+
48
+ it "closes distinct input and output streams" do
49
+ framer.close
50
+
51
+ expect(input).to be(:closed?)
52
+ expect(output).to be(:closed?)
53
+ expect(framer).to be(:closed?)
54
+ end
55
+
56
+ it "handles escaped bytes inside an invalid chunk payload" do
57
+ input.string = "\ePHTTY;1;QUJD\eXRA==\e\\"
58
+ input.rewind
59
+
60
+ expect do
61
+ framer.read_chunk
62
+ end.to raise_exception(ArgumentError)
63
+ end
64
+ end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2026, by Samuel Williams.
5
+
6
+ require "stringio"
7
+ require "tempfile"
8
+ require "protocol/http2/framer"
9
+ require "protocol/htty"
10
+
11
+ describe Protocol::HTTY::Stream do
12
+ let(:writer) {StringIO.new}
13
+ let(:stream) {subject.new(StringIO.new, writer, packet_size: 8)}
14
+
15
+ it "chunks opaque payload into HTTY chunks" do
16
+ stream.write(Protocol::HTTP2::CONNECTION_PREFACE)
17
+
18
+ expect(writer.string.scan(/\ePHTTY;1;/).size).to be > 1
19
+ end
20
+
21
+ it "reads back opaque bytes from HTTY chunks" do
22
+ stream.write(Protocol::HTTP2::CONNECTION_PREFACE)
23
+ writer.rewind
24
+
25
+ reader = subject.new(writer, StringIO.new)
26
+
27
+ expect(reader.read(Protocol::HTTP2::CONNECTION_PREFACE.bytesize)).to be == Protocol::HTTP2::CONNECTION_PREFACE
28
+ reader.close
29
+ expect(reader.read).to be_nil
30
+ end
31
+
32
+ it "returns all buffered bytes when length is omitted" do
33
+ stream.write("hello")
34
+ stream.close
35
+ writer.rewind
36
+
37
+ reader = subject.new(writer, StringIO.new)
38
+
39
+ expect(reader.read).to be == "hello"
40
+ expect(reader.read).to be_nil
41
+ end
42
+
43
+ it "exposes the underlying output stream" do
44
+ expect(stream.io).to be(:is_a?, ::IO::Stream::Buffered)
45
+ end
46
+
47
+ it "flushes through the underlying framer" do
48
+ stream.write("hello")
49
+
50
+ expect do
51
+ stream.flush
52
+ end.not.to raise_exception
53
+ end
54
+
55
+ it "reports when the local side is closed" do
56
+ expect(stream).not.to be(:closed?)
57
+
58
+ stream.close
59
+
60
+ expect(stream).to be(:closed?)
61
+ end
62
+
63
+ it "does not close the underlying output stream" do
64
+ stream.close
65
+
66
+ expect(writer).not.to be(:closed?)
67
+ end
68
+
69
+ it "reports when the remote side is still readable" do
70
+ expect(stream).to be(:readable?)
71
+ end
72
+
73
+ it "rejects writes after the local side is closed" do
74
+ stream.close
75
+
76
+ expect do
77
+ stream.write("hello")
78
+ end.to raise_exception(IOError)
79
+ end
80
+
81
+ it "wraps raw IO handles using IO::Stream" do
82
+ Tempfile.create("protocol-htty") do |file|
83
+ io_stream = subject.new(file, file).io
84
+
85
+ expect(io_stream).to be(:is_a?, ::IO::Stream::Buffered)
86
+ io_stream.close
87
+ end
88
+ end
89
+
90
+ it "does not close wrapped raw IO handles when closed" do
91
+ Tempfile.create("protocol-htty") do |file|
92
+ wrapped_stream = subject.new(file, file)
93
+
94
+ wrapped_stream.close
95
+
96
+ expect(file).not.to be(:closed?)
97
+ file.close
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2026, by Samuel Williams.
5
+
6
+ require "protocol/htty/version"
7
+
8
+ describe Protocol::HTTY do
9
+ it "has a version number" do
10
+ expect(Protocol::HTTY::VERSION).to be =~ /\d+\.\d+\.\d+/
11
+ end
12
+ end
data.tar.gz.sig ADDED
@@ -0,0 +1,3 @@
1
+ 9�1H��t���e&�a�A\�����~� d{i����N��`�޿� ��i�0�T���ې%��
2
+ `��/���З�!��h��2_���VN�PUϳ�q�r�.D�Mn���&���a�@�����Қ4�ovK�Sn����0�Ͼl�-�;W�s�è�mI �����w�����.��0��ȷ��8�(^t[)� k����|�J��(ƻ� g'ۉIk�&�Hy��T�mk�:"�[�
3
+ _��u����<���T:�C�ga��jW����rP-My��f����O
metadata ADDED
@@ -0,0 +1,122 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: protocol-htty
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Samuel Williams
8
+ bindir: bin
9
+ cert_chain:
10
+ - |
11
+ -----BEGIN CERTIFICATE-----
12
+ MIIE2DCCA0CgAwIBAgIBATANBgkqhkiG9w0BAQsFADBhMRgwFgYDVQQDDA9zYW11
13
+ ZWwud2lsbGlhbXMxHTAbBgoJkiaJk/IsZAEZFg1vcmlvbnRyYW5zZmVyMRIwEAYK
14
+ CZImiZPyLGQBGRYCY28xEjAQBgoJkiaJk/IsZAEZFgJuejAeFw0yMjA4MDYwNDUz
15
+ MjRaFw0zMjA4MDMwNDUzMjRaMGExGDAWBgNVBAMMD3NhbXVlbC53aWxsaWFtczEd
16
+ MBsGCgmSJomT8ixkARkWDW9yaW9udHJhbnNmZXIxEjAQBgoJkiaJk/IsZAEZFgJj
17
+ bzESMBAGCgmSJomT8ixkARkWAm56MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIB
18
+ igKCAYEAomvSopQXQ24+9DBB6I6jxRI2auu3VVb4nOjmmHq7XWM4u3HL+pni63X2
19
+ 9qZdoq9xt7H+RPbwL28LDpDNflYQXoOhoVhQ37Pjn9YDjl8/4/9xa9+NUpl9XDIW
20
+ sGkaOY0eqsQm1pEWkHJr3zn/fxoKPZPfaJOglovdxf7dgsHz67Xgd/ka+Wo1YqoE
21
+ e5AUKRwUuvaUaumAKgPH+4E4oiLXI4T1Ff5Q7xxv6yXvHuYtlMHhYfgNn8iiW8WN
22
+ XibYXPNP7NtieSQqwR/xM6IRSoyXKuS+ZNGDPUUGk8RoiV/xvVN4LrVm9upSc0ss
23
+ RZ6qwOQmXCo/lLcDUxJAgG95cPw//sI00tZan75VgsGzSWAOdjQpFM0l4dxvKwHn
24
+ tUeT3ZsAgt0JnGqNm2Bkz81kG4A2hSyFZTFA8vZGhp+hz+8Q573tAR89y9YJBdYM
25
+ zp0FM4zwMNEUwgfRzv1tEVVUEXmoFCyhzonUUw4nE4CFu/sE3ffhjKcXcY//qiSW
26
+ xm4erY3XAgMBAAGjgZowgZcwCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwHQYDVR0O
27
+ BBYEFO9t7XWuFf2SKLmuijgqR4sGDlRsMC4GA1UdEQQnMCWBI3NhbXVlbC53aWxs
28
+ aWFtc0BvcmlvbnRyYW5zZmVyLmNvLm56MC4GA1UdEgQnMCWBI3NhbXVlbC53aWxs
29
+ aWFtc0BvcmlvbnRyYW5zZmVyLmNvLm56MA0GCSqGSIb3DQEBCwUAA4IBgQB5sxkE
30
+ cBsSYwK6fYpM+hA5B5yZY2+L0Z+27jF1pWGgbhPH8/FjjBLVn+VFok3CDpRqwXCl
31
+ xCO40JEkKdznNy2avOMra6PFiQyOE74kCtv7P+Fdc+FhgqI5lMon6tt9rNeXmnW/
32
+ c1NaMRdxy999hmRGzUSFjozcCwxpy/LwabxtdXwXgSay4mQ32EDjqR1TixS1+smp
33
+ 8C/NCWgpIfzpHGJsjvmH2wAfKtTTqB9CVKLCWEnCHyCaRVuKkrKjqhYCdmMBqCws
34
+ JkxfQWC+jBVeG9ZtPhQgZpfhvh+6hMhraUYRQ6XGyvBqEUe+yo6DKIT3MtGE2+CP
35
+ eX9i9ZWBydWb8/rvmwmX2kkcBbX0hZS1rcR593hGc61JR6lvkGYQ2MYskBveyaxt
36
+ Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8
37
+ voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=
38
+ -----END CERTIFICATE-----
39
+ date: 1980-01-02 00:00:00.000000000 Z
40
+ dependencies:
41
+ - !ruby/object:Gem::Dependency
42
+ name: base64
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: protocol-http2
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: io-stream
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ executables: []
84
+ extensions: []
85
+ extra_rdoc_files: []
86
+ files:
87
+ - lib/protocol/htty.rb
88
+ - lib/protocol/htty/error.rb
89
+ - lib/protocol/htty/framer.rb
90
+ - lib/protocol/htty/stream.rb
91
+ - lib/protocol/htty/version.rb
92
+ - license.md
93
+ - readme.md
94
+ - releases.md
95
+ - test/protocol/htty.rb
96
+ - test/protocol/htty/framer.rb
97
+ - test/protocol/htty/stream.rb
98
+ homepage: https://github.com/socketry/protocol-htty
99
+ licenses:
100
+ - MIT
101
+ metadata:
102
+ documentation_uri: https://socketry.github.io/protocol-htty/
103
+ source_code_uri: https://github.com/socketry/protocol-htty.git
104
+ rdoc_options: []
105
+ require_paths:
106
+ - lib
107
+ required_ruby_version: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: '3.3'
112
+ required_rubygems_version: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ requirements: []
118
+ rubygems_version: 4.0.6
119
+ specification_version: 4
120
+ summary: A terminal-safe transport for carrying opaque HTTP/2 bytes over TTY side
121
+ channels.
122
+ test_files: []
metadata.gz.sig ADDED
Binary file