Skip to content

Bump goproxy to v1.9.0 and fix keep-alive framing desync - #201

Merged
kbukum1 merged 4 commits into
mainfrom
kbukum/bump-goproxy-v1.9.0
Aug 13, 2026
Merged

Bump goproxy to v1.9.0 and fix keep-alive framing desync#201
kbukum1 merged 4 commits into
mainfrom
kbukum/bump-goproxy-v1.9.0

Conversation

@kbukum1

@kbukum1 kbukum1 commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

What are you trying to accomplish?

Upgrade github.com/elazarl/goproxy to v1.9.0.

The bump on its own re-introduces a bug we saw with earlier goproxy versions: some jobs fail with errors like Cargo's [8] Weird server reply (Invalid status line). This PR bumps goproxy and fixes that failure so the upgrade is safe to ship.

Root cause (short version):

  • Older goproxy closed the client↔proxy tunnel after every response. v1.9.0 keeps it alive and reuses it for many requests (a good performance change).
  • Our cache attaches a body to every response, including ones that must have no body (e.g. 304, 204, HEAD). That produced a few stray bytes the client never reads.
  • Before, the connection closed each time, so those bytes were harmless. With the reused connection, they now corrupt the next request on the same tunnel → the failures above.

This affects any ecosystem that gets these responses, not just Cargo.

Anything you want to highlight for special attention from reviewers?

  • The fix targets the root cause (the cache), not the symptom: bodyless responses now stay bodyless through both cache paths. It keys off HTTP status/method only, so it's ecosystem-agnostic.
  • We intentionally keep goproxy's new keep-alive behavior (the performance win) rather than reverting to always-close.

How will you know you've accomplished your goal?

  • New tests reproduce the bad case for 304 / 204 / HEAD across both cache paths and assert the response stays correctly framed.
  • Full go vet + go test ./... pass.

Checklist

  • I have run the complete test suite to ensure all tests and linters pass.
  • I have thoroughly tested my code changes to ensure they work as expected, including adding additional tests for new functionality.
  • I have written clear and descriptive commit messages.
  • I have provided a detailed description of the changes in the pull request, including the problem it addresses, how it fixes the problem, and any relevant details about the implementation.
  • I have ensured that the code is well-documented and easy to understand.

Copilot AI balanced review requested due to automatic review settings August 13, 2026 17:24
@kbukum1
kbukum1 requested a review from a team as a code owner August 13, 2026 17:24
@kbukum1
kbukum1 marked this pull request as draft August 13, 2026 17:29

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Upgrades goproxy to v1.9.0 while preventing cached bodyless responses from corrupting persistent MITM connections.

Changes:

  • Updates and vendors goproxy v1.9.0.
  • Preserves bodyless framing for HEAD, 1xx, 204, and 304 responses.
  • Adds regression tests for cache misses and hits.
Show a summary per file
File Description
go.mod Bumps goproxy dependency.
go.sum Updates dependency checksums.
internal/cache/handlers.go Handles bodyless cached responses.
internal/cache/handlers_test.go Tests bodyless cache behavior.
vendor/modules.txt Updates vendored module metadata.
vendor/github.com/elazarl/goproxy/.golangci.yml Adds upstream lint configuration.
vendor/github.com/elazarl/goproxy/README.md Updates upstream documentation.
vendor/github.com/elazarl/goproxy/actions.go Updates handler documentation and types.
vendor/github.com/elazarl/goproxy/certs.go Updates CA initialization and TLS defaults.
vendor/github.com/elazarl/goproxy/chunked.go Removes legacy chunk writer.
vendor/github.com/elazarl/goproxy/ctx.go Updates proxy context behavior.
vendor/github.com/elazarl/goproxy/dispatcher.go Updates request conditions and helpers.
vendor/github.com/elazarl/goproxy/doc.go Refreshes package documentation.
vendor/github.com/elazarl/goproxy/h2.go Removes legacy HTTP/2 forwarding.
vendor/github.com/elazarl/goproxy/http.go Adds regular HTTP response handling.
vendor/github.com/elazarl/goproxy/http2.go Adds stream-aware HTTP/2 MITM support.
vendor/github.com/elazarl/goproxy/https.go Reworks CONNECT, MITM, and keep-alive handling.
vendor/github.com/elazarl/goproxy/internal/http1parser/header.go Adds HTTP/1 header parsing.
vendor/github.com/elazarl/goproxy/internal/http1parser/request.go Adds request parsing without canonicalization.
vendor/github.com/elazarl/goproxy/internal/signer/counterecryptor.go Moves and extends signer entropy support.
vendor/github.com/elazarl/goproxy/internal/signer/signer.go Adds internal certificate signer.
vendor/github.com/elazarl/goproxy/logger.go Updates logger interface documentation.
vendor/github.com/elazarl/goproxy/proxy.go Refactors proxy configuration and routing.
vendor/github.com/elazarl/goproxy/responses.go Modernizes response construction.
vendor/github.com/elazarl/goproxy/signer.go Removes superseded signer implementation.
vendor/github.com/elazarl/goproxy/websocket.go Refactors WebSocket proxying.

Review details

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

  • Files reviewed: 3/26 changed files
  • Comments generated: 1
  • Review effort level: Balanced

Comment thread internal/cache/handlers.go
@kbukum1
kbukum1 force-pushed the kbukum/bump-goproxy-v1.9.0 branch from e6a799b to f144528 Compare August 13, 2026 17:30
@kbukum1
kbukum1 requested a balanced review from Copilot August 13, 2026 17:31

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review details

Suppressed comments (2)

internal/cache/handlers_test.go:141

  • This test exercises the cache hooks only; it never serializes either response through goproxy or reuses a tunnel, so it would still pass if framing bytes corrupted the next response. Add a regression test that sends a bodyless response followed by a normal response on the same MITM connection, for both cache-miss and cache-hit paths, and verifies the second status line parses correctly.
func TestCache_BodylessResponses(t *testing.T) {

internal/cache/handlers.go:229

  • A HEAD response may legally advertise the non-zero length that a corresponding GET would return. Forcing ContentLength to zero makes a cache hit's http.Response differ from the cache miss and can cause that length metadata to be omitted or replaced when goproxy writes it. Restore the cached Content-Length for HEAD while still using http.NoBody and no transfer encoding.
			resp.ContentLength = 0
  • Files reviewed: 3/26 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

@kbukum1
kbukum1 marked this pull request as ready for review August 13, 2026 18:07
jeffwidman
jeffwidman previously approved these changes Aug 13, 2026

@jeffwidman jeffwidman left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-blocking observation:

Our cache attaches a body to every response, including ones that must have no body (e.g. 304, 204, HEAD). That produced a few stray bytes the client never reads.

It sounds like this is a bug already present on main regardless of the goproxy bump.

In that case, it might be simpler/safer to have a PR fixing just this, to ensure it works as expected pre-bump. And only then do the separate bump of the proxy (which you could probably do via :dependabot: if you wanted).

I'm also fine with shipping as-is (I noted you kept the bump change separate from the behavioral change as two distinct commits, thanks!)

It's probably faster to ship both, but if we do revert for some other bug we might trip upon in the proxy, we should probably only revert the proxy bump, not this wrapper fix.

Comment thread internal/cache/handlers.go Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review details

  • Files reviewed: 5/28 changed files
  • Comments generated: 1
  • Review effort level: Balanced

Comment thread internal/cache/handlers.go
…r response framing and content length preservation
@kbukum1
kbukum1 force-pushed the kbukum/bump-goproxy-v1.9.0 branch from 5704ef9 to fae4da5 Compare August 13, 2026 20:48
@kbukum1

kbukum1 commented Aug 13, 2026

Copy link
Copy Markdown
Contributor Author

Non-blocking observation:

Our cache attaches a body to every response, including ones that must have no body (e.g. 304, 204, HEAD). That produced a few stray bytes the client never reads.

It sounds like this is a bug already present on main regardless of the goproxy bump.

In that case, it might be simpler/safer to have a PR fixing just this, to ensure it works as expected pre-bump. And only then do the separate bump of the proxy (which you could probably do via :dependabot: if you wanted).

I'm also fine with shipping as-is (I noted you kept the bump change separate from the behavioral change as two distinct commits, thanks!)

It's probably faster to ship both, but if we do revert for some other bug we might trip upon in the proxy, we should probably only revert the proxy bump, not this wrapper fix.

Actually we don't have a bug if we don't use the bumped one, because it never hits the no-response situation. No response only happens because of request interference on the same connection. If we don't bump, we can't even prove we fixed anything — so I think we should have both together so I can test properly when I deploy, and revert as well.

jeffwidman
jeffwidman previously approved these changes Aug 13, 2026

@jeffwidman jeffwidman left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not super familiar with this code/networking protocols that it's touching, but all the explanation here makes sense.

I think we should try this in production, and see how it behaves. If there are further issues, we can fix forwards as we go, or upstream if needed changes back to goproxy.

Comment thread proxy_test.go
}))
defer upstream.Close()

t.Setenv("PROXY_CACHE", "false")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

observation (not something to necessarily fix)

these env vars are global to the process, so when go test runs tests, it runs them in parallel for each package (serial within a package, parallel across packages) so it can introduce races.

I don't see a way around that here, and IMO this is just fine/convenient to use the env var... just mentioning it though.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review details

Suppressed comments (1)

internal/cache/handlers.go:218

  • Only mark this request as cached after a bodyless entry is accepted or the cache file has opened successfully. As written, a missing/unreadable cache file falls through to the upstream request but remains tagged as a cache hit, so response logs and cache statistics incorrectly report the upstream response as cached.
		proxyctx.SetValue(proxyCtx, wasCached, true)
		d.cached++
  • Files reviewed: 5/28 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

@kbukum1
kbukum1 dismissed stale reviews from thavaahariharangit and jeffwidman via e5a7f83 August 13, 2026 22:43

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review details

  • Files reviewed: 5/28 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

@kbukum1
kbukum1 merged commit ba6c27b into main Aug 13, 2026
112 checks passed
@kbukum1
kbukum1 deleted the kbukum/bump-goproxy-v1.9.0 branch August 13, 2026 23:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants