feat(ws): RFC 6455 opening handshake — Conn::upgrade, 101 short-circuit (v0.4 chunk 1)
Design (as agreed in the v0.4 pass): - Dep #3 spent: sha1_smol 1.0.1 (zero transitive deps) instead of a vendored SHA-1 — user call: no hand-rolled crypto. Consequence noted in ROADMAP: the v0.6 wire-format JSON question now costs dep #4. - Base64 stays in-tree but ENCODE-ONLY (RFC 4648 vectors tested): it is an encoding, not crypto, and the decode direction (where parsing bugs live) is deliberately not implemented. - src/ws/{mod,handshake}.rs: validate() implements §4.2.1 (GET, 1.1, Host, upgrade/connection token lists case-insensitively, key shape = base64 of exactly 16 bytes, version 13). Version mismatch -> 426 + sec-websocket-version: 13 (§4.2.2); everything else -> 400. Rejected upgrades stay plain HTTP with keep-alive intact (tested: 400 then 101 on the same connection). - Conn grows pub upgrade: Option<WsUpgrade> (opaque marker; chunk 3 turns it into the duplex/handler handoff payload — shape deliberately uncommitted while the handler API is open). Conn::upgrade(self) is the commit point: 101 + accept key + marker + halt, or the rejection. - conn_actor: upgrade marker + status 101 -> write head, leave the HTTP loop. Until chunk 3 that drops the fd (clients see 101 then EOF); status check is defensive against a post-handler plug clobbering 101. - serialise_response: 1xx no longer get content-length injected (RFC 7230 §3.3.2). 204 left alone on purpose — separate conversation. Accept-key path pinned to the §1.3 worked example end-to-end (unit + wire). Suite: 34 unit + 33 integration + 2 doc.
This commit is contained in:
@@ -231,6 +231,20 @@ pub fn run_connection(
|
||||
.put_body(RespBody::Empty);
|
||||
}
|
||||
|
||||
// ----- 3.5 WebSocket upgrade short-circuit. -----
|
||||
// An accepted handshake (marker + 101) ends HTTP on this socket:
|
||||
// write the 101 head and leave the request loop. Chunk 3 (duplex
|
||||
// wiring) takes the socket over right here; until then leaving
|
||||
// the loop closes it via OwnedFd::drop — the 101 is still the
|
||||
// honest, testable wire artefact. The status check is defensive:
|
||||
// a post-handler plug that clobbered the 101 forfeits the
|
||||
// upgrade and falls through to plain HTTP.
|
||||
if response_conn.upgrade.is_some() && response_conn.status == Some(101) {
|
||||
let head = parser::serialise_response(&response_conn, true);
|
||||
let _ = write_all(raw, &head, Instant::now() + limits.write_timeout);
|
||||
return;
|
||||
}
|
||||
|
||||
// ----- 4. Write the response. -----
|
||||
// A Stream body on HTTP/1.0 has no chunked framing: the body is
|
||||
// delimited by EOF, so keep-alive is forced off for this response
|
||||
|
||||
Reference in New Issue
Block a user