Plan M4a: gatewayd in 15 tasks, with skeletons and given tests
Each task's tests were run against a reference at its end state; the end states were replayed from master in order with the gate at each step (650 to 762 tests); each skeleton compiles against its tests and fails them. The reference is kept off this machine. Lessons T27 (every wait in a test has a limit) and T28 (mutate the reference before hand-over) come from this work. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
# M4a task 07: the WebSocket handshake
|
||||
|
||||
**Branch:** `m4a` (run `git switch m4a`; `git status --short` must be empty, otherwise stop)
|
||||
**Commit subject:** `gatewayd: ws, the WebSocket error type and the opening handshake`
|
||||
|
||||
## Goal
|
||||
|
||||
Mattermost pushes events over a WebSocket at `/api/v4/websocket` (RFC 6455). Our client is our own:
|
||||
blocking, one connection, tested against hostile input. This task is its first part: the error
|
||||
type, base64, and the opening handshake (RFC 6455, section 4.1; spec section 6).
|
||||
|
||||
The request, exactly (it is written for you in `request_text`):
|
||||
|
||||
```text
|
||||
GET <path> HTTP/1.1\r\n
|
||||
Host: <host>\r\n
|
||||
Upgrade: websocket\r\n
|
||||
Connection: Upgrade\r\n
|
||||
Sec-WebSocket-Key: <16 random bytes in base64>\r\n
|
||||
Sec-WebSocket-Version: 13\r\n
|
||||
Authorization: Bearer <token>\r\n
|
||||
\r\n
|
||||
```
|
||||
|
||||
The answer must be status 101, with `Upgrade: websocket`, a `Connection` header holding the token
|
||||
`upgrade`, and `Sec-WebSocket-Accept` equal to base64(SHA-1(key + the GUID
|
||||
`258EAFA5-E914-47DA-95CA-C5AB0DC85B11`)). Anything else is an error. SHA-1 is `proto::sha1::sha1`
|
||||
(task 01); the head is read with `http::read_head` (task 06), which stops at the blank line.
|
||||
|
||||
## Files
|
||||
|
||||
- Copy: `crates/gatewayd/tests/ws_handshake.rs`, and `crates/gatewayd/src/ws/mod.rs` (complete)
|
||||
and the skeleton `crates/gatewayd/src/ws/handshake.rs`
|
||||
- Modify: `crates/gatewayd/src/lib.rs` (`pub mod ws;`), `docs/implementer-log.md`
|
||||
|
||||
`ws/mod.rs` is complete: `WsError` (`Handshake`, `Protocol`, `TooLarge`, `Closed`, `Dead`, `Io`)
|
||||
with `Display` and `From<std::io::Error>`, and `pub mod handshake;`. Tasks 08 and 09 add their
|
||||
`pub mod` lines to it.
|
||||
|
||||
## The skeleton
|
||||
|
||||
Written: `GUID`, `ALPHABET`, `request_text`. To fill: `base64`, `accept_for`, `new_key`,
|
||||
`check_response`, `handshake`. The key's 16 bytes come from `random: &mut dyn Read` (in `gatewayd`,
|
||||
`/dev/urandom`; in the tests, fixed bytes), so the tests know the key.
|
||||
|
||||
## Steps
|
||||
|
||||
- [ ] **1. Copy.** `git switch m4a`, then
|
||||
`mkdir -p crates/gatewayd/src/ws && cp docs/plans/M4a/files/crates/gatewayd/src/ws/mod.rs docs/plans/M4a/files/crates/gatewayd/src/ws/handshake.rs crates/gatewayd/src/ws/ && cp docs/plans/M4a/files/crates/gatewayd/tests/ws_handshake.rs crates/gatewayd/tests/`.
|
||||
Add `pub mod ws;` to `lib.rs`.
|
||||
- [ ] **2. See it fail.** `cargo test -p gatewayd --test ws_handshake`. Expected: it compiles; 6
|
||||
fail and 1 passes (`the_request_is_exactly_this`, since `request_text` is given).
|
||||
- [ ] **3. Fill `base64`, `accept_for`, `new_key`, `check_response`, `handshake`**, in that order,
|
||||
`cargo check -p gatewayd` after each.
|
||||
- [ ] **4. See it pass.** `cargo test -p gatewayd --test ws_handshake`. Expected: 7 passed,
|
||||
including the RFC 6455 example (`dGhlIHNhbXBsZSBub25jZQ==` gives `s3pPLMBiTxaQ9kYGzzhZRbK+xOo=`)
|
||||
and a handshake whose first frame arrives in the same packet as the head and is left unread.
|
||||
- [ ] **5. Run the gate.** `cargo fmt --all`, then `make gate`. Expected last line: `gate: ok`.
|
||||
- [ ] **6. Log and commit.** `git add crates/gatewayd docs/implementer-log.md Cargo.lock && git commit`
|
||||
|
||||
## Done when
|
||||
|
||||
- `cargo test -p gatewayd --test ws_handshake` passes; `make gate` prints `gate: ok`.
|
||||
Reference in New Issue
Block a user