protocol-htty 0.4.0 → 0.5.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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data/context/getting-started.md +107 -0
- data/context/index.yaml +18 -0
- data/context/specification.md +294 -0
- data/lib/protocol/htty/stream.rb +5 -0
- data/lib/protocol/htty/version.rb +1 -1
- data/readme.md +4 -0
- data/releases.md +4 -0
- data/test/protocol/htty/stream.rb +7 -3
- data.tar.gz.sig +0 -0
- metadata +4 -1
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 342eb75c5b06023d41d3089d2a3e0ce8088384671c6aa7159da037514b37ca5d
|
|
4
|
+
data.tar.gz: 2c4240bdb10b2576f0abe53a8587eb9dc531acaf4aa4f1957ca8f5deadc1cdd9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f987eb2e3cddbcfd865583a1ad54fe399fb27a2731d371c9fe24515b6ff45324b7dc90c4d6338328a32524ec5ce3fed1db399a08a7fda6409c5d58dcdedbd0ca
|
|
7
|
+
data.tar.gz: 9814c5ba0ffef27fb738f238122cf248cb843dbf1d2b01d63077298095f6ecbd38b8aa83b7eaff262f3a10cd2598137c71944b458eb69bcead7724d539c2b69e
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# Getting Started
|
|
2
|
+
|
|
3
|
+
This guide explains how to get started with `protocol-htty` for DCS-bootstrapped raw HTTP/2 byte stream transport.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
Add the gem to your project:
|
|
8
|
+
|
|
9
|
+
~~~ bash
|
|
10
|
+
$ bundle add protocol-htty
|
|
11
|
+
~~~
|
|
12
|
+
|
|
13
|
+
## Why HTTY Exists
|
|
14
|
+
|
|
15
|
+
When you need to carry an `h2c` connection over stdin/stdout, PTYs, or SSH sessions, raw HTTP/2 bytes are not safe to write directly into a terminal that is still operating in ordinary text mode. HTTY solves that by emitting one terminal-safe bootstrap sequence and then handing the session over to raw byte transport.
|
|
16
|
+
|
|
17
|
+
Use `protocol-htty` when you need:
|
|
18
|
+
|
|
19
|
+
- **Terminal-safe takeover**: Start HTTY without rendering the transition as visible terminal text.
|
|
20
|
+
- **Raw byte transport**: Carry HTTP/2 bytes directly once takeover has happened.
|
|
21
|
+
- **Cross-runtime interoperability**: Keep the bootstrap small enough to reimplement in other languages or environments.
|
|
22
|
+
|
|
23
|
+
Without HTTY, higher-level systems would need to invent their own ad hoc terminal bootstrap and raw-mode coordination before they could safely carry HTTP/2 over terminal I/O.
|
|
24
|
+
|
|
25
|
+
## Core Concepts
|
|
26
|
+
|
|
27
|
+
- {ruby Protocol::HTTY::Stream} writes and reads the HTTY bootstrap sequence, then exposes the raw byte stream used after bootstrap.
|
|
28
|
+
- HTTY transports bytes only. HTTP/2 connection setup, stream lifecycle, and shutdown semantics remain owned by HTTP/2.
|
|
29
|
+
|
|
30
|
+
## Usage
|
|
31
|
+
|
|
32
|
+
The low-level API is intentionally small. Use {ruby Protocol::HTTY::Stream} to perform the bootstrap step and then carry raw bytes.
|
|
33
|
+
|
|
34
|
+
### Writing The Bootstrap
|
|
35
|
+
|
|
36
|
+
If you are integrating HTTY into an existing transport, create a stream wrapper around your byte-oriented IO. This gives you direct access to the terminal-safe bootstrap without adding any higher-level protocol policy.
|
|
37
|
+
|
|
38
|
+
Use explicit bootstrap calls when you need:
|
|
39
|
+
|
|
40
|
+
- **Protocol integration**: You already manage stream ownership elsewhere.
|
|
41
|
+
- **Debugging**: You want to inspect the actual bootstrap bytes.
|
|
42
|
+
- **Custom transport composition**: You are building your own raw byte-stream wrapper.
|
|
43
|
+
|
|
44
|
+
~~~ ruby
|
|
45
|
+
require "stringio"
|
|
46
|
+
require "protocol/htty"
|
|
47
|
+
|
|
48
|
+
output = StringIO.new
|
|
49
|
+
htty = Protocol::HTTY::Stream.new(StringIO.new, output)
|
|
50
|
+
|
|
51
|
+
htty.write_bootstrap
|
|
52
|
+
|
|
53
|
+
output.string
|
|
54
|
+
# => "\eP+Hraw\e\\"
|
|
55
|
+
~~~
|
|
56
|
+
|
|
57
|
+
### Bootstrapping A Raw Stream
|
|
58
|
+
|
|
59
|
+
If you want HTTY to behave like a normal byte stream, use {ruby Protocol::HTTY::Stream}. It can emit the bootstrap for you, or consume it on the receiving side, and then expose the carried bytes directly.
|
|
60
|
+
|
|
61
|
+
This is the right level when you need:
|
|
62
|
+
|
|
63
|
+
- **HTTP/2 transport bridging**: Feed an `h2c` connection through HTTY without defining another message layer.
|
|
64
|
+
- **Bootstrap handling**: Keep the raw-mode transition in one place.
|
|
65
|
+
- **Simple integration**: Read and write bytes without manually parsing terminal control data.
|
|
66
|
+
|
|
67
|
+
~~~ ruby
|
|
68
|
+
require "stringio"
|
|
69
|
+
require "protocol/htty"
|
|
70
|
+
|
|
71
|
+
transport = StringIO.new
|
|
72
|
+
writer = Protocol::HTTY::Stream.open(StringIO.new, transport, bootstrap: :write)
|
|
73
|
+
|
|
74
|
+
writer.write("hello world")
|
|
75
|
+
writer.flush
|
|
76
|
+
|
|
77
|
+
transport.rewind
|
|
78
|
+
|
|
79
|
+
reader = Protocol::HTTY::Stream.open(transport, StringIO.new, bootstrap: :read)
|
|
80
|
+
|
|
81
|
+
reader.read(11)
|
|
82
|
+
# => "hello world"
|
|
83
|
+
~~~
|
|
84
|
+
|
|
85
|
+
### Carrying an HTTP/2 Preface
|
|
86
|
+
|
|
87
|
+
HTTY does not interpret HTTP/2 frames. It only performs the bootstrap and then preserves byte ordering while the connection runs over the raw transport. That makes it suitable for forwarding the client connection preface and subsequent frame exchange unchanged.
|
|
88
|
+
|
|
89
|
+
~~~ ruby
|
|
90
|
+
require "stringio"
|
|
91
|
+
require "protocol/http2"
|
|
92
|
+
require "protocol/htty"
|
|
93
|
+
|
|
94
|
+
transport = StringIO.new
|
|
95
|
+
stream = Protocol::HTTY::Stream.open(StringIO.new, transport, bootstrap: :write)
|
|
96
|
+
|
|
97
|
+
stream.write(Protocol::HTTP2::CONNECTION_PREFACE)
|
|
98
|
+
stream.flush
|
|
99
|
+
|
|
100
|
+
transport.rewind
|
|
101
|
+
|
|
102
|
+
reader = Protocol::HTTY::Stream.open(transport, StringIO.new, bootstrap: :read)
|
|
103
|
+
preface = reader.read(Protocol::HTTP2::CONNECTION_PREFACE.bytesize)
|
|
104
|
+
|
|
105
|
+
puts preface == Protocol::HTTP2::CONNECTION_PREFACE
|
|
106
|
+
# => true
|
|
107
|
+
~~~
|
data/context/index.yaml
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Automatically generated context index for Utopia::Project guides.
|
|
2
|
+
# Do not edit then files in this directory directly, instead edit the guides and then run `bake utopia:project:agent:context:update`.
|
|
3
|
+
---
|
|
4
|
+
description: A terminal-safe transport for carrying opaque HTTP/2 bytes over TTY side
|
|
5
|
+
channels.
|
|
6
|
+
metadata:
|
|
7
|
+
documentation_uri: https://socketry.github.io/protocol-htty/
|
|
8
|
+
source_code_uri: https://github.com/socketry/protocol-htty.git
|
|
9
|
+
files:
|
|
10
|
+
- path: getting-started.md
|
|
11
|
+
title: Getting Started
|
|
12
|
+
description: This guide explains how to get started with `protocol-htty` for DCS-bootstrapped
|
|
13
|
+
raw HTTP/2 byte stream transport.
|
|
14
|
+
- path: specification.md
|
|
15
|
+
title: HTTY Specification
|
|
16
|
+
description: This document specifies HTTY as a DCS-bootstrapped raw-mode takeover
|
|
17
|
+
transport for carrying a plaintext HTTP/2 (`h2c`) connection over terminal-attached
|
|
18
|
+
sessions.
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
# HTTY Specification
|
|
2
|
+
|
|
3
|
+
This document specifies HTTY as a DCS-bootstrapped raw-mode takeover transport for carrying a plaintext HTTP/2 (`h2c`) connection over terminal-attached sessions.
|
|
4
|
+
|
|
5
|
+
HTTY does not define a second application protocol beside HTTP/2. Once HTTY is active, connection establishment, readiness, multiplexing, end-of-stream, reset, and graceful shutdown semantics are all provided by HTTP/2 itself.
|
|
6
|
+
|
|
7
|
+
## Conventions
|
|
8
|
+
|
|
9
|
+
The key words `MUST`, `MUST NOT`, `SHOULD`, `SHOULD NOT`, and `MAY` in this document are to be interpreted as normative requirement levels.
|
|
10
|
+
|
|
11
|
+
## Goals
|
|
12
|
+
|
|
13
|
+
HTTY is designed to provide the following properties:
|
|
14
|
+
|
|
15
|
+
1. It can carry an `h2c` connection over terminal stdin/stdout, PTYs, and SSH sessions.
|
|
16
|
+
2. It can switch a terminal-attached session into a mode where HTTP/2 bytes are transported directly without terminal-oriented interpretation.
|
|
17
|
+
3. It can be implemented consistently across different runtimes.
|
|
18
|
+
4. It can operate without auxiliary sockets or shared filesystems.
|
|
19
|
+
5. It adds as little protocol state as possible beyond the decision to enter HTTY mode.
|
|
20
|
+
|
|
21
|
+
## Non-Goals
|
|
22
|
+
|
|
23
|
+
HTTY does not define:
|
|
24
|
+
|
|
25
|
+
- HTML rendering.
|
|
26
|
+
- Browser embedding.
|
|
27
|
+
- Resource stores.
|
|
28
|
+
- Request routing.
|
|
29
|
+
- Form or click events.
|
|
30
|
+
- JavaScript execution.
|
|
31
|
+
- Inline scrollback placement.
|
|
32
|
+
- A filesystem mapping.
|
|
33
|
+
- A higher-level application model beyond HTTP/2 byte transport.
|
|
34
|
+
- A second lifecycle model parallel to HTTP/2.
|
|
35
|
+
|
|
36
|
+
Those concerns belong either to HTTP/2 itself or to higher-level systems built on top of HTTY.
|
|
37
|
+
|
|
38
|
+
## Transport Model
|
|
39
|
+
|
|
40
|
+
An HTTY session carries one bidirectional HTTP/2 connection over the terminal's standard input and standard output streams.
|
|
41
|
+
|
|
42
|
+
Before HTTY takes over, the surrounding terminal session MAY still be operating in ordinary terminal mode. HTTY support discovery and advertisement are specified by this document via the `HTTY` environment variable. HTTY v1 defines one interoperable bootstrap sequence for entering takeover mode.
|
|
43
|
+
|
|
44
|
+
Once HTTY takes over a session:
|
|
45
|
+
|
|
46
|
+
- the surrounding terminal or PTY MUST be placed into raw mode or an equivalent byte-preserving mode,
|
|
47
|
+
- HTTY MUST have exclusive ownership of the session byte stream,
|
|
48
|
+
- bytes on standard input and standard output MUST be interpreted only as the two directions of one plaintext HTTP/2 connection.
|
|
49
|
+
|
|
50
|
+
In particular:
|
|
51
|
+
|
|
52
|
+
- standard input carries bytes from the terminal side toward the command,
|
|
53
|
+
- standard output carries bytes from the command toward the terminal side,
|
|
54
|
+
- together those two ordered byte streams MUST be interpreted as one plaintext HTTP/2 connection.
|
|
55
|
+
|
|
56
|
+
The first meaningful bytes sent by the HTTP/2 client after takeover MUST therefore be the HTTP/2 client connection preface, followed by the normal HTTP/2 frame exchange.
|
|
57
|
+
|
|
58
|
+
HTTY itself is responsible only for establishing this byte-stream takeover model. All connection and stream semantics above that layer are owned by HTTP/2.
|
|
59
|
+
|
|
60
|
+
## Bootstrap And Takeover
|
|
61
|
+
|
|
62
|
+
HTTY v1 defines a terminal-directed bootstrap sequence which precedes raw takeover:
|
|
63
|
+
|
|
64
|
+
~~~ text
|
|
65
|
+
ESC P + H raw ESC \
|
|
66
|
+
~~~
|
|
67
|
+
|
|
68
|
+
Equivalently, using C-style escapes:
|
|
69
|
+
|
|
70
|
+
~~~ text
|
|
71
|
+
\u001bP+Hraw\u001b\\
|
|
72
|
+
~~~
|
|
73
|
+
|
|
74
|
+
This bootstrap is a DCS sequence with:
|
|
75
|
+
|
|
76
|
+
- intermediate byte `+`,
|
|
77
|
+
- final byte `H`,
|
|
78
|
+
- payload `raw`.
|
|
79
|
+
|
|
80
|
+
The bootstrap sequence is emitted on the command-to-terminal direction before HTTP/2 bytes begin flowing. It is terminal control data, not part of the carried HTTP/2 connection.
|
|
81
|
+
|
|
82
|
+
HTTY v1 only defines the `+H` DCS sequence with payload `raw`. Other terminal control sequences, including implementation-specific DCS markers used by a session owner before takeover or after recovery, are outside the HTTY v1 wire protocol and MUST NOT be interpreted as HTTY lifecycle messages.
|
|
83
|
+
|
|
84
|
+
An HTTY v1 receiver that supports takeover MUST:
|
|
85
|
+
|
|
86
|
+
1. recognize this bootstrap sequence on the terminal-facing output stream,
|
|
87
|
+
2. consume it without rendering it as ordinary terminal output,
|
|
88
|
+
3. switch the session into raw or equivalent byte-preserving mode,
|
|
89
|
+
4. treat subsequent bytes on standard input and standard output as the two directions of one plaintext HTTP/2 connection.
|
|
90
|
+
|
|
91
|
+
After the bootstrap has been accepted, HTTY is defined around the following takeover boundary:
|
|
92
|
+
|
|
93
|
+
1. A higher-level session owner decides that HTTY should begin.
|
|
94
|
+
2. The command side switches its own stdin to raw or byte-preserving mode.
|
|
95
|
+
3. The command side emits the HTTY bootstrap sequence.
|
|
96
|
+
4. The terminal-facing side consumes the bootstrap and switches the terminal-attached transport into raw mode.
|
|
97
|
+
5. From that point until termination, the transport carries plain `h2c` bytes.
|
|
98
|
+
|
|
99
|
+
Implementations MAY still use environment discovery, process setup, terminal integration, or other out-of-band coordination to decide whether HTTY should begin. However, once HTTY v1 takeover is initiated in-band, the bootstrap sequence above is the interoperable mechanism by which the command side signals the transition to raw transport.
|
|
100
|
+
|
|
101
|
+
The important interoperability rule is that once HTTY has taken over a session, the peers MUST speak plain `h2c` over the raw byte stream until the session ends.
|
|
102
|
+
|
|
103
|
+
### Command-Side Ordering
|
|
104
|
+
|
|
105
|
+
On the command side, raw or byte-preserving mode MUST be activated on stdin BEFORE the bootstrap sequence is emitted. The terminal-facing side may transmit the HTTP/2 connection preface immediately upon receiving the bootstrap; if raw mode is not yet active when those bytes arrive, the line discipline may corrupt them.
|
|
106
|
+
|
|
107
|
+
The required ordering is:
|
|
108
|
+
|
|
109
|
+
1. Activate raw mode on stdin.
|
|
110
|
+
2. Emit the bootstrap sequence on stdout.
|
|
111
|
+
3. Scan incoming bytes for the HTTP/2 connection preface, discarding any bytes that precede it.
|
|
112
|
+
4. Pass the connection preface and all subsequent bytes to the HTTP/2 implementation.
|
|
113
|
+
|
|
114
|
+
Bytes that arrive before the HTTP/2 connection preface MUST be discarded rather than forwarded. They may include terminal noise, echoed command text, or residual bytes from a prior session.
|
|
115
|
+
|
|
116
|
+
## Framing
|
|
117
|
+
|
|
118
|
+
HTTY adds one bootstrap sequence before takeover and no framing after takeover.
|
|
119
|
+
|
|
120
|
+
Once raw mode is active, the transport payload is simply the carried HTTP/2 byte stream itself.
|
|
121
|
+
|
|
122
|
+
In particular:
|
|
123
|
+
|
|
124
|
+
- The bootstrap sequence above is the only HTTY-defined control envelope in v1.
|
|
125
|
+
- HTTP/2 frame boundaries remain defined only by HTTP/2.
|
|
126
|
+
- HTTY MUST NOT add another message boundary layer above the raw byte stream.
|
|
127
|
+
- Receivers MUST pass the bytes through to the HTTP/2 implementation in order.
|
|
128
|
+
|
|
129
|
+
## Transport Lifecycle
|
|
130
|
+
|
|
131
|
+
HTTY does not define transport-level open or close packets.
|
|
132
|
+
|
|
133
|
+
Instead, lifecycle is mapped directly onto the surrounding terminal session and the carried HTTP/2 connection:
|
|
134
|
+
|
|
135
|
+
- HTTP/2 readiness MUST be expressed by the normal connection preface and `SETTINGS` exchange.
|
|
136
|
+
- HTTP/2 stream lifecycle MUST be expressed by standard HTTP/2 frame and flag semantics.
|
|
137
|
+
- Graceful connection shutdown SHOULD be expressed by HTTP/2 mechanisms such as `GOAWAY`.
|
|
138
|
+
- Abrupt termination MUST be expressed by terminal or PTY EOF, process exit, or equivalent transport loss.
|
|
139
|
+
|
|
140
|
+
For HTTY session lifecycle, the command-to-terminal direction is authoritative for graceful session completion. A terminal-facing HTTP/2 client MAY send `GOAWAY` at any time according to normal HTTP/2 semantics, but that does not by itself end the HTTY session or authorize the terminal-facing side to resume bootstrap scanning. A session is gracefully complete when the command side sends `GOAWAY`.
|
|
141
|
+
|
|
142
|
+
If the underlying command exits or the terminal-attached transport closes unexpectedly before command-side `GOAWAY`, the carried HTTP/2 connection MUST be considered aborted.
|
|
143
|
+
|
|
144
|
+
Closing a local HTTY stream abstraction does not by itself imply closing the surrounding PTY or terminal session. Session teardown remains a responsibility of the session owner.
|
|
145
|
+
|
|
146
|
+
### Session Resumption
|
|
147
|
+
|
|
148
|
+
A terminal-attached session MAY carry more than one HTTY session over its lifetime. After a session ends gracefully:
|
|
149
|
+
|
|
150
|
+
1. The command side sends `GOAWAY` to signal graceful shutdown of the HTTP/2 connection.
|
|
151
|
+
2. The command side SHOULD restore ordinary terminal mode (cooked mode or equivalent) on its stdin.
|
|
152
|
+
3. The terminal-facing side resumes scanning the byte stream for the next bootstrap sequence.
|
|
153
|
+
4. A new HTTY session begins when the command side emits a new bootstrap sequence, following the same ordering requirements as the first session.
|
|
154
|
+
|
|
155
|
+
For graceful session boundaries, implementations MUST preserve bytes buffered across session boundaries. Bytes read from the transport past the last frame of a completed session MUST remain available for the next bootstrap scan rather than being discarded.
|
|
156
|
+
|
|
157
|
+
Bytes that arrive at the terminal-facing side between the end of one session and the detection of the next bootstrap MUST NOT be forwarded to any HTTP/2 implementation. They may include `GOAWAY` frame bytes or other residual data from the prior session and MUST be treated as pre-bootstrap noise by the next scan.
|
|
158
|
+
|
|
159
|
+
Aborted sessions are a session-owner concern rather than an HTTY reset protocol. If the carried HTTP/2 connection fails, the command crashes, or the terminal byte stream becomes unsynchronized, the session owner MAY discard buffered bytes from the failed session, restore or nudge the surrounding shell or terminal state by local policy, and resume scanning for a fresh HTTY bootstrap. Any marker used to automate that recovery is implementation-specific terminal control data, not an HTTY v1 control envelope.
|
|
160
|
+
|
|
161
|
+
For example:
|
|
162
|
+
|
|
163
|
+
- if the client closes all tabs and no HTTY consumer remains, the session owner SHOULD terminate the terminal-emulator-to-command side of the session and SHOULD tear down the session as a whole,
|
|
164
|
+
- if the command exits, the session owner SHOULD treat the command-to-terminal side as finished and SHOULD terminate the session as a whole.
|
|
165
|
+
|
|
166
|
+
## Terminal Input
|
|
167
|
+
|
|
168
|
+
Once HTTY has taken over a session, ordinary terminal keystrokes are no longer a separate interactive protocol.
|
|
169
|
+
|
|
170
|
+
Implementations MUST NOT treat arbitrary terminal input as unrelated text traffic once the raw HTTY transport is active.
|
|
171
|
+
|
|
172
|
+
In practice, this means:
|
|
173
|
+
|
|
174
|
+
- session owners SHOULD stop forwarding unmanaged terminal keystrokes into the transport,
|
|
175
|
+
- any input that continues to reach the raw transport MUST be considered part of the carried HTTP/2 connection,
|
|
176
|
+
- implementations MAY reserve explicit detach or escape mechanisms, but such mechanisms are session-management features outside this wire specification.
|
|
177
|
+
|
|
178
|
+
## Environment Discovery
|
|
179
|
+
|
|
180
|
+
An implementation MAY advertise HTTY support to child processes using the `HTTY` environment variable.
|
|
181
|
+
|
|
182
|
+
This environment variable is the standard discovery and advertisement mechanism defined by HTTY v1. It allows a parent terminal environment to tell a child process whether HTTY is available and which transport version is supported.
|
|
183
|
+
|
|
184
|
+
`HTTY` specifies the maximum supported HTTY transport version exposed by the current terminal environment.
|
|
185
|
+
|
|
186
|
+
The variable is interpreted as follows:
|
|
187
|
+
|
|
188
|
+
- If `HTTY` is absent, HTTY support is not advertised.
|
|
189
|
+
- If `HTTY=0`, HTTY is explicitly unsupported or disabled.
|
|
190
|
+
- If `HTTY` is a positive integer, that value is the maximum supported HTTY transport version.
|
|
191
|
+
- If `HTTY` is present but not a valid non-negative integer, it MUST be treated as unsupported.
|
|
192
|
+
|
|
193
|
+
Senders SHOULD treat `HTTY` as an out-of-band capability advertisement, not as the sole protocol negotiation mechanism.
|
|
194
|
+
|
|
195
|
+
The `HTTY` environment variable does not, by itself, require takeover to begin. It advertises support and version bounds. The decision to request or initiate raw-mode takeover remains a session-management concern outside the wire protocol defined here.
|
|
196
|
+
|
|
197
|
+
This mechanism allows a terminal or intermediate environment to explicitly disable HTTY while preserving forward compatibility for future transport versions.
|
|
198
|
+
|
|
199
|
+
## Sender Requirements
|
|
200
|
+
|
|
201
|
+
Once HTTY has taken over a session, senders MUST:
|
|
202
|
+
|
|
203
|
+
1. Preserve byte ordering on each transport direction.
|
|
204
|
+
2. Preserve the byte order of the carried HTTP/2 connection exactly.
|
|
205
|
+
3. Stop emitting HTTY transport bytes once the underlying session transport has ended.
|
|
206
|
+
4. Avoid emitting unrelated terminal text on a transport direction that has been taken over by HTTY.
|
|
207
|
+
|
|
208
|
+
Senders MAY:
|
|
209
|
+
|
|
210
|
+
- buffer writes according to the needs of the underlying runtime,
|
|
211
|
+
- split writes arbitrarily, provided byte ordering is preserved,
|
|
212
|
+
- coalesce adjacent writes arbitrarily, provided byte ordering is preserved.
|
|
213
|
+
|
|
214
|
+
## Receiver Requirements
|
|
215
|
+
|
|
216
|
+
Once HTTY has taken over a session, receivers MUST:
|
|
217
|
+
|
|
218
|
+
1. Treat standard input and standard output as raw byte streams.
|
|
219
|
+
2. Preserve byte ordering on each transport direction.
|
|
220
|
+
3. Reconstruct the original HTTP/2 byte stream for each direction without assigning extra meaning to write boundaries.
|
|
221
|
+
4. Deliver those bytes to the HTTP/2 implementation unchanged.
|
|
222
|
+
|
|
223
|
+
Receivers SHOULD:
|
|
224
|
+
|
|
225
|
+
- bound buffered data to avoid unbounded memory growth,
|
|
226
|
+
- treat unexpected transport loss as connection abort.
|
|
227
|
+
|
|
228
|
+
## Error Handling
|
|
229
|
+
|
|
230
|
+
Before takeover, malformed or unsupported bootstrap data MUST NOT cause the receiver to enter HTTY mode. Such data MAY be ignored, logged, or treated as ordinary terminal output according to local policy.
|
|
231
|
+
|
|
232
|
+
Once HTTY is active, malformed data is not an HTTY framing error because HTTY no longer defines a framing envelope beyond the initial bootstrap. Unexpected bytes are simply malformed input to the carried HTTP/2 connection.
|
|
233
|
+
|
|
234
|
+
Accordingly:
|
|
235
|
+
|
|
236
|
+
- invalid bytes or invalid structure after takeover MUST be handled as HTTP/2 protocol errors,
|
|
237
|
+
- terminal or PTY EOF before graceful HTTP/2 shutdown MUST be treated as connection abort,
|
|
238
|
+
- implementations MUST NOT infer higher-level HTTP/2 state from an abruptly terminated transport beyond what HTTP/2 itself defines.
|
|
239
|
+
|
|
240
|
+
## Terminal Compatibility
|
|
241
|
+
|
|
242
|
+
HTTY is designed for transport over terminal-attached byte streams.
|
|
243
|
+
|
|
244
|
+
Compatibility requirements:
|
|
245
|
+
|
|
246
|
+
- It MUST work over stdin/stdout once those streams have been placed into a raw byte-preserving mode.
|
|
247
|
+
- It SHOULD work over SSH and PTYs provided the session owner can switch the connection into raw mode.
|
|
248
|
+
- It MUST NOT require auxiliary localhost networking or shared filesystem state.
|
|
249
|
+
|
|
250
|
+
HTTY does not require:
|
|
251
|
+
|
|
252
|
+
- Localhost networking.
|
|
253
|
+
- Side channels such as Unix sockets.
|
|
254
|
+
- Shared storage between sender and receiver.
|
|
255
|
+
|
|
256
|
+
## Security Considerations
|
|
257
|
+
|
|
258
|
+
HTTY itself only defines takeover into a raw byte transport for HTTP/2. The main safety concern is therefore session ownership: once takeover occurs, arbitrary bytes on the transport are interpreted as part of the carried HTTP/2 connection.
|
|
259
|
+
|
|
260
|
+
Implementations SHOULD consider:
|
|
261
|
+
|
|
262
|
+
- preventing unmanaged terminal keystrokes from being injected into the active HTTY transport,
|
|
263
|
+
- making detach and teardown behavior explicit at the session-owner layer,
|
|
264
|
+
- bounding buffer growth,
|
|
265
|
+
- treating unexpected transport loss as abort.
|
|
266
|
+
|
|
267
|
+
Security policies for HTML, requests, rendering, resource access, and user interaction are outside the scope of HTTY and must be defined by higher-level consumers.
|
|
268
|
+
|
|
269
|
+
## Relationship to HTTP/2
|
|
270
|
+
|
|
271
|
+
HTTY is not an application protocol parallel to HTTP/2.
|
|
272
|
+
|
|
273
|
+
Instead, HTTY is a transport takeover convention that allows an `h2c` connection to own a raw terminal-attached byte stream.
|
|
274
|
+
|
|
275
|
+
That means:
|
|
276
|
+
|
|
277
|
+
- HTTY does not replace HTTP/2 frame semantics.
|
|
278
|
+
- HTTY does not add another stream lifecycle model.
|
|
279
|
+
- HTTY does not redefine requests, responses, events, or resources.
|
|
280
|
+
- HTTY exists only to give HTTP/2 exclusive ownership of a terminal-attached byte stream.
|
|
281
|
+
|
|
282
|
+
## Relationship to Higher-Level Systems
|
|
283
|
+
|
|
284
|
+
Higher-level systems can build directly on top of the resulting HTTP/2 connection.
|
|
285
|
+
|
|
286
|
+
Those systems define request semantics, rendering, resource handling, browser behavior, and user interaction. HTTY does not.
|
|
287
|
+
|
|
288
|
+
## Implementation Notes
|
|
289
|
+
|
|
290
|
+
Implementations that currently expose chunk-oriented or packet-oriented helpers should converge on the simpler raw-byte transport model described here.
|
|
291
|
+
|
|
292
|
+
Implementations that expose stream-like wrappers MAY model `close` as local write shutdown only, provided this does not implicitly close any broader session transport.
|
|
293
|
+
|
|
294
|
+
The core design goal remains the same: HTTY should stay small enough to be reimplemented consistently without introducing a second application protocol beside HTTP/2.
|
data/lib/protocol/htty/stream.rb
CHANGED
data/readme.md
CHANGED
|
@@ -36,6 +36,10 @@ Please see the [project documentation](https://socketry.github.io/protocol-htty/
|
|
|
36
36
|
|
|
37
37
|
Please see the [project releases](https://socketry.github.io/protocol-htty/releases/index) for all releases.
|
|
38
38
|
|
|
39
|
+
### v0.5.0
|
|
40
|
+
|
|
41
|
+
- Add `Stream#remote_address` returning `nil` to satisfy the `Protocol::HTTP::Peer` interface.
|
|
42
|
+
|
|
39
43
|
### v0.4.0
|
|
40
44
|
|
|
41
45
|
- **Breaking**: Drop the `io-stream` dependency. `Stream.new` and `Stream.open` no longer coerce or wrap their `input` and `output` arguments; raw IO objects are used as-is. Callers that relied on `stream.io` returning an `IO::Stream::Buffered` should wrap the IO themselves before passing it in.
|
data/releases.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# Releases
|
|
2
2
|
|
|
3
|
+
## v0.5.0
|
|
4
|
+
|
|
5
|
+
- Add `Stream#remote_address` returning `nil` to satisfy the `Protocol::HTTP::Peer` interface.
|
|
6
|
+
|
|
3
7
|
## v0.4.0
|
|
4
8
|
|
|
5
9
|
- **Breaking**: Drop the `io-stream` dependency. `Stream.new` and `Stream.open` no longer coerce or wrap their `input` and `output` arguments; raw IO objects are used as-is. Callers that relied on `stream.io` returning an `IO::Stream::Buffered` should wrap the IO themselves before passing it in.
|
|
@@ -22,7 +22,7 @@ describe Protocol::HTTY::Stream do
|
|
|
22
22
|
it "consumes the bootstrap before reading raw bytes" do
|
|
23
23
|
writer.write("\eP+Hraw\e\\#{Protocol::HTTP2::CONNECTION_PREFACE}")
|
|
24
24
|
writer.rewind
|
|
25
|
-
|
|
25
|
+
|
|
26
26
|
reader = subject.open(writer, StringIO.new, bootstrap: :read)
|
|
27
27
|
|
|
28
28
|
expect(reader.read(Protocol::HTTP2::CONNECTION_PREFACE.bytesize)).to be == Protocol::HTTP2::CONNECTION_PREFACE
|
|
@@ -72,10 +72,10 @@ describe Protocol::HTTY::Stream do
|
|
|
72
72
|
|
|
73
73
|
expect(writer).not.to be(:closed?)
|
|
74
74
|
end
|
|
75
|
-
|
|
75
|
+
|
|
76
76
|
it "close_write closes writes but preserves readability" do
|
|
77
77
|
stream.close_write
|
|
78
|
-
|
|
78
|
+
|
|
79
79
|
expect(stream).to be(:closed?)
|
|
80
80
|
expect(stream).to be(:readable?)
|
|
81
81
|
expect(writer).not.to be(:closed?)
|
|
@@ -92,4 +92,8 @@ describe Protocol::HTTY::Stream do
|
|
|
92
92
|
stream.write("hello")
|
|
93
93
|
end.to raise_exception(IOError)
|
|
94
94
|
end
|
|
95
|
+
|
|
96
|
+
it "returns nil for remote_address" do
|
|
97
|
+
expect(stream.remote_address).to be_nil
|
|
98
|
+
end
|
|
95
99
|
end
|
data.tar.gz.sig
CHANGED
|
Binary file
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: protocol-htty
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Samuel Williams
|
|
@@ -56,6 +56,9 @@ executables: []
|
|
|
56
56
|
extensions: []
|
|
57
57
|
extra_rdoc_files: []
|
|
58
58
|
files:
|
|
59
|
+
- context/getting-started.md
|
|
60
|
+
- context/index.yaml
|
|
61
|
+
- context/specification.md
|
|
59
62
|
- lib/protocol/htty.rb
|
|
60
63
|
- lib/protocol/htty/error.rb
|
|
61
64
|
- lib/protocol/htty/stream.rb
|
metadata.gz.sig
CHANGED
|
Binary file
|