feat(envd): bound stalled transfers with idle write/read timeouts#3047
feat(envd): bound stalled transfers with idle write/read timeouts#3047mishushakov wants to merge 3 commits into
Conversation
Add a per-write idle timeout (netlimit.WriteTimeoutListener) so a client that stops reading can't pin a streaming handler via TCP backpressure, and a per-read idle timeout scoped to file upload (PostFiles) so a client that stops sending mid-upload can't hold the open file open forever. Also set ReadHeaderTimeout for slowloris protection. Bump envd version to 0.6.7. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
PR SummaryMedium Risk Overview File PostFiles wraps the raw request body with a matching per-read idle deadline via ResponseController, then restores the original r.Body and clears the read deadline on exit so net/http connection reuse and early-close detection stay correct. Version bumps to 0.6.7. Reviewed by Cursor Bugbot for commit fffee4b. Bugbot is set up for automated code reviews on this repo. Configure here. |
❌ 1 Tests Failed:
View the top 1 failed test(s) by shortest run time
To view more test analytics, go to the Test Analytics Dashboard |
There was a problem hiding this comment.
Code Review
Wrapping the accepted connection in writeTimeoutConn prevents http.Server from asserting the underlying connection to *net.TCPConn, which bypasses the server's default TCP keep-alive configuration and disables keep-alives entirely for all incoming connections. To prevent this, TCP keep-alives should be enabled on the raw connection before wrapping it.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
|
Thanks @gemini-code-assist — I looked into this and it doesn't apply on our Go version (1.26). TCP keep-alive isn't affected by wrapping the accepted conn:
The (One real effect of wrapping — losing the |
|
That is a very clear and thorough explanation. You are correct that |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1caa8ba011
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
Address two issues from review of the idle-timeout change: - Upload: restore r.Body to net/http's native *body before PostFiles returns. The server type-asserts r.Body to *body for keep-alive reuse / early-close accounting; leaving the IdleReadCloser wrapper in place defeated that and could let leftover bytes from an aborted upload be parsed as the next request (smuggling) on a reused connection. Also clear the read deadline on return. - writeTimeoutConn: forward CloseWrite to the underlying conn so net/http can half-close (send FIN, keep reading) before a full close when replying with an error mid-upload, instead of an RST that can truncate the response. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Thanks @codex — both findings were real and are fixed in 689620e. 1. Body reuse / smuggling (upload.go): confirmed. 2. CloseWrite (writedeadline.go): confirmed. A full regression test needs a live keep-alive server since the bug only manifests on connection reuse with a real |
|
To use Codex here, create a Codex account and connect to github. |
The restore (not the deadline clear) is what prevents request smuggling: shouldReuseConnection -> closedRequestBodyEarly type-asserts r.Body to *body, so a leftover wrapper hides an early-closed/undrained upload and the conn gets reused. Verified empirically. Attribute the deadline clear as hygiene only. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
envd serves long-lived streams over HTTP/1.1, so a peer that stalls mid-transfer can pin a handler — and the process/file behind it — indefinitely. This adds a
netlimitpackage with a per-write idle timeout (applied globally via a listener wrapper, since a write deadline only arms on genuine TCP backpressure and is safe everywhere) and a per-read idle timeout scoped to file upload (PostFiles), where "no bytes for N seconds" is pathological — unlikeStreamInput/process streams where a quiet gap is normal. It also setsReadHeaderTimeoutfor slowloris protection (header read only, never bodies or streams) and bumps the envd version to 0.6.7. Both timeouts reset per chunk, so slow-but-progressing transfers survive and only true stalls are reclaimed.🤖 Generated with Claude Code