feat(capture): live-proxy sockets, MITM handshake relay + concurrency (closes #56)#64
Merged
Merged
Conversation
Completes the MITM proxy (#56 part 2): the real-socket transport, the MITM opening-handshake relay, and the bidirectional concurrency that pt1's pump pipeline plugs into. - run() wires std.Io.net: IpAddress.listen -> Server.accept (CP) and IpAddress.connect (CSMS), driving the relay over each stream's reader/writer. - relayHandshake performs two independent RFC 6455 handshakes (server to the CP, client to the CSMS), reusing the #54 both-halves codec and mirroring the CP's request path upstream. The upstream key is derived deterministically from the CP's key (sessionNonce) -- not a secret, and reproducible under test. - relayStreams runs the two pump directions with io.concurrent (true parallelism, since both block on reads; io.async could deadlock), guarded on the shared Sink by std.Io.Mutex. When one side closes, the other is cancelled. - relayStreams is generic over *Io.Reader/*Io.Writer, so the whole relay is driven in-memory under test with a std.Io.Threaded io -- no OS socketpair (socketpair(2) here supports only AF_INET, which the OS rejects). The integration test proves the MITM handshake, concurrent pump, relay correctness, the recording re-parsing offline, and streaming detection matching a cold offline pass -- the S5 exit criterion. - Wall-clock time moved onto io (Clock.now(.real, io)) behind a small TimeSource union (fixed for tests, wall for live), replacing pt1's wallClockMs, which used the removed std.time.milliTimestamp and had never been analyzed. ADR-0010 records the transport and concurrency decisions. Closes #56.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #56 — completes the MITM proxy, the heart of the S5 flagship. Part 1 (the socket-free pump pipeline) landed in #63; this is part 2: the transport, the handshake relay, and the concurrency.
What's here
run) — wiresstd.Io.net:IpAddress.listen→Server.accept(downstream CP) andIpAddress.connect(upstream CSMS), driving the relay over each stream's reader/writer.relayHandshake) — two independent RFC 6455 handshakes, one per peer (server to the CP, client to the CSMS), reusing the feat(capture): WebSocket transport — RFC 6455 subset (ADR-0008) #54 both-halves codec and mirroring the CP's request path upstream.relayStreams) — the two pump directions run underio.concurrent(true parallelism — both block on reads, soio.asynccould deadlock), guarded on the sharedSinkbystd.Io.Mutex. One side closing cancels the other.The integration test = the S5 exit criterion
relayStreamsis generic over*Io.Reader/*Io.Writer, so the whole relay is driven in-memory under test with a realstd.Io.Threadedio (genuine concurrency + the mutex). One test proves, end to end: MITM handshake → concurrent pump → relay correctness → the recording re-parses offline → streaming detection matches a cold offline pass.No OS socketpair is used —
socketpair(2)here supports onlyAF_INET, which the OS rejects, and noAF_UNIXpair is exposed. The thin socket glue inrun()is force-analyzed by a test (_ = &run) and will be exercised live when thestudio captureCLI wires it (#57).Zig 0.16 notes (hard-won)
io(Clock.now(.real, io)), so a plain?i64closure can't supply wall-clock — replaced with a smallTimeSourceunion (fixedfor tests,wallfor live). This also fixes pt1'swallClockMs, which used the removedstd.time.milliTimestampand had never been analyzed (lazy analysis).std.Thread.Mutex→std.Io.Mutex;std.crypto.random/std.timetimestamps gone.Verification
native test -Dplatform=null→ 150/150.native build(ReleaseFast) → clean.zig fmt --checkclean.Deferred (noted)
Single session per
run(accept one CP, relay, return) — a multi-session accept loop is a later refinement behind the same relay core. TLS stays post-0.5 (ADR-0008).