srdusr
aboutsummaryrefslogtreecommitdiffstats
path: root/internal/proxy/capture.go
AgeCommit message (Collapse)AuthorFilesLines
2024-09-19Fix hang and data-race bugs found while re-verifying steps 1-5srdusr1-8/+6
Audited every file in the proxy/store/ipc/TUI stack before starting step 6, per request. Found and fixed three real bugs in already-shipped code, all confirmed with live tests (including a race-detector build) rather than just read: 1. No timeout covered the write-request/read-response phase of an upstream exchange, in either the main proxy path (roundTripH1/ roundTripH2) or Repeater - only the dial itself was bounded. A server that accepted the connection and then never finished responding hung the request forever. Fixed with conn.SetDeadline after a successful dial in both forward() and Repeat() (new upstreamTimeout constant, 60s). Verified against a real hung TCP listener: the daemon returned a clean "i/o timeout" error at exactly 60s instead of hanging. 2. ipc.Client shared one connection/encoder/decoder with no locking. Bubble Tea dispatches each request as its own goroutine, and viewList's 'r' key doesn't change mode while its loadDetail call is in flight - pressing it again (or 'enter' on another row) before the first response arrives calls Get/List/Repeat concurrently on the same connection, which can interleave JSON on the wire or hand one call another's response. Fixed with a mutex serializing round trips. Stress-tested with rapid overlapping key input against a -race build of both binaries: no warnings, no corruption. 3. The IPC "subscribe" handler only noticed a disconnected client when the next broadcast's Encode failed - a subscriber that quit while the daemon was otherwise idle leaked its goroutine and channel indefinitely. Fixed by reading the connection in the background too, so disconnection is detected immediately regardless of traffic. Also removed a dead, misleading parameter: captureResponse took a *teeConn it was never actually called with (the exact-capture path is handled directly in forward()), so the branch using it was unreachable. Re-verified all five prior steps end-to-end against a fresh build: plain HTTP, HTTPS H1.1/H2/untrusted-CA-rejection, exact vs reconstructed capture flags cross-checked directly in SQLite, Repeater over both HTTP and HTTPS, and search (plain text, dotted domains, hyphenated terms, column filters) - all correct.
2024-02-14History view: SQLite storage, daemon/TUI split over Unix socketsrdusr1-0/+83
Implements build-order step 3. Adds: - internal/store: SQLite (WAL, single-writer) history table, raw request/response blobs plus metadata for the list view. - internal/proxy: request/response capture wired into forward(). HTTP/1.1 legs are captured byte-exact via a teeConn that records wire bytes as they're read, taken right after the message is fully drained (so no manual re-reading/replaying is needed - RoundTrip's own streaming does the draining). HTTP/2 legs (no meaningful "raw bytes" of their own - multiplexed, HPACK-compressed framing) are reconstructed instead, and marked as such in storage. - internal/ipc: JSON-over-Unix-socket protocol between mitmuxd (owns the proxy and the DB) and any client - list/get for queries, subscribe for a live push stream of newly captured entries. Keeps the proxy engine independent of the UI, per the architecture sketch. - cmd/mitmux: Bubble Tea TUI - a live-updating history table and a request/response detail view with raw bytes. Two real bugs surfaced during testing and got fixed before commit: 1. http.Transport's HTTP/2 auto-dispatch does a literal *tls.Conn type assertion on the dialed connection; wrapping it in a capturing teeConn broke that silently, and HTTP/2 framing got parsed as HTTP/1.1 text. Fixed by dropping http.Transport for the upstream leg entirely in favor of an explicit per-protocol round trip (see PLAN.md stack note). 2. singleConnListener wrapped the client teeConn *inside* a closeSignalConn, so ConnContext's type assertion for it silently failed and HTTP/1.1 client-side capture never activated. Fixed the wrap order; verified via direct SQLite inspection that request_exact flips back to 1 and the stored bytes are genuinely wire-exact (preserved chunked-encoding framing, original header casing/order). Verified live: plain HTTP, HTTPS H1.1, HTTPS H2, and a POST with a body, checked against the raw stored bytes directly in SQLite; IPC list/get/ subscribe against a throwaway client; and the TUI driven end-to-end in a tmux session (list, detail view, tab between request/response, live update on a new request while sitting on the list).