M4a tasks 08 and 09: finer skeletons after task 08's session wrote nothing

Task 08's `header` was one todo!() with a dozen branches; Ornith planned it in its head until the
turn ran out (tip T25). `next_message` and `header` are now written as glue over seven small
helpers, and task 09's `poll` over four, each checked to pass when filled from its comments.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-23 20:37:48 -07:00
co-authored by Claude Opus 5.5
parent d56fcf8611
commit 02681069b6
6 changed files with 227 additions and 54 deletions
+13 -6
View File
@@ -32,18 +32,25 @@ impl Ws {
mut random: Box<dyn Read + Send>) -> Result<Ws, WsError>;
fn send(&mut self, opcode: u8, payload: &[u8]) -> Result<(), WsError>;
pub fn send_text(&mut self, text: &str) -> Result<(), WsError>;
pub fn poll(&mut self, wait: Duration) -> Result<Option<String>, WsError>;
pub fn poll(&mut self, wait: Duration) -> Result<Option<String>, WsError>; // written
fn take_messages(&mut self) -> Result<Option<String>, WsError>;
fn keep_alive(&mut self, now: Instant) -> Result<(), WsError>;
fn read_timeout(&self, until: Instant, now: Instant) -> Duration;
fn read_some(&mut self, timeout: Duration) -> Result<(), WsError>;
pub fn close(mut self);
}
pub fn host_header(server: &ServerUrl) -> String;
```
All are `todo!()`, each with its steps above it. `poll` is the one with the most in it: it returns
**`poll` is written for you**: it is the glue, and it calls the four helpers after it. It returns
the next text message, or `None` after about `wait` with none, and does the pinging, the pong
answers and the dead-peer check on the way. Its read timeout is always the time to the **next
thing it must do** (the end of `wait`, the next ping, or the dead-after limit), so a quiet
connection neither spins nor oversleeps. Write it as its comment says, step by step; a helper
function for step 1 is fine.
connection neither spins nor oversleeps. Read it first.
Everything else is `todo!()`, each a few lines, with the steps and the expressions to use above it.
Use them as written; do not weigh other ways to write the same thing. Write the function, run
`cargo check -p gatewayd`, go on to the next.
`random` gives the handshake key and a fresh 4-byte mask for every frame we send (in `gatewayd`,
`/dev/urandom`).
@@ -55,8 +62,8 @@ function for step 1 is fine.
Add `pub mod conn;` to `crates/gatewayd/src/ws/mod.rs`.
- [ ] **2. See it fail.** `cargo test -p gatewayd --test ws_conn`. Expected: it compiles and 10
tests fail.
- [ ] **3. Fill `host_header`, `send`, `send_text`, `open`, `close`, then `poll`**,
`cargo check -p gatewayd` after each.
- [ ] **3. Fill `host_header`, `send`, `send_text`, `open`, `close`, then `take_messages`,
`keep_alive`, `read_timeout`, `read_some`**, `cargo check -p gatewayd` after each.
- [ ] **4. See it pass.** `cargo test -p gatewayd --test ws_conn`, five times. Expected: 10 passed
each time, in under a second.
- [ ] **5. Run the gate.** `cargo fmt --all`, then `make gate`. Expected last line: `gate: ok`.