Skip to content

Commit 2ae0422

Browse files
committed
fix(egress): switch mitmproxy to connection_strategy=eager
Lazy was originally chosen so denied requests could short-circuit before touching upstream, but in practice the egress policy defaults to allow and denies only a small minority of requests. The savings on the deny path do not justify the cost on the allow path: lazy checks the upstream connection pool per-request, and on h1 keepalive sessions this exposes a stale-conn race where the second request (e.g. POST /git-upload-pack right after GET /info/refs) picks an upstream conn the peer has already closed, surfacing as a silent transport error with no fatal output on the client. Eager opens the upstream connection alongside the client connection, so mitm's IO loop continuously observes upstream FIN/RST and a stale conn is detected before the client's next request arrives. The cost is a wasted TCP/TLS handshake for the small fraction of denied requests — acceptable because a denied flow still short-circuits with `flow.response = ...` before any HTTP write reaches upstream, so upstream sees no path / method / headers, only an unfinished handshake. Audited the bundled addons (system.py, custom.py): no hook depends on lazy-only behavior (no server_connect routing, no flow.request.host modification, no client-SNI-based upstream decisions).
1 parent 2a91287 commit 2ae0422

1 file changed

Lines changed: 12 additions & 3 deletions

File tree

components/egress/pkg/mitmproxy/launch.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,9 +107,18 @@ func Launch(cfg Config) (*Running, error) {
107107
// Stream large bodies instead of buffering them in memory (OOM prevention).
108108
args = append(args, "--set", "stream_large_bodies=1m")
109109

110-
// Lazy connection strategy: defer upstream connection until the request is fully received,
111-
// which avoids unnecessary connections for blocked/filtered requests.
112-
args = append(args, "--set", "connection_strategy=lazy")
110+
// Eager connection strategy: open the upstream connection alongside the client
111+
// connection so mitm's IO loop continuously observes upstream FIN/RST. Lazy
112+
// defers the upstream open until the full request is buffered and checks the
113+
// upstream pool per-request; on h1 keepalive sessions this exposes a stale-
114+
// connection race where the second request (e.g. POST /git-upload-pack right
115+
// after GET /info/refs) picks an upstream conn the peer has already closed,
116+
// surfacing as a silent transport error with no fatal output on the client.
117+
// Eager trades a wasted TCP/TLS handshake for the small fraction of requests
118+
// denied by the egress addon — acceptable because a denied flow already short-
119+
// circuits with `flow.response = ...` before any HTTP write reaches upstream,
120+
// so the upstream still sees no path/method/headers from the denied request.
121+
args = append(args, "--set", "connection_strategy=eager")
113122

114123
// Transparent mode redirects TCP to IP addresses. Clients connecting to IPs
115124
// do not send SNI, so upstream TLS cert hostname verification fails with

0 commit comments

Comments
 (0)