Repository navigation
fix(api): forward parsed ClickHouse proxy bodies - #3383
kodiakhq[bot] merged 5 commits into
Conversation
🦋 Changeset detectedLatest commit: 7607077 The changes in this PR will be included in the next version bump. This PR includes changesets to release 3 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
@adityareddy-dev is attempting to deploy a commit to the HyperDX Team on Vercel. A member of the Team first needs to authorize it. |
|
Hi @adityareddy-dev, thanks for the pull request! Before we review code from a first-time contributor, a maintainer vouches for them, and you're not on our list yet. There's nothing else to file and this PR stays open — a maintainer will look at it, usually within a day or two, and vouch for you right here if it's a genuine attempt at the problem. To make that quick, check the PR description has:
More detail in our contributing guide. |
PR Review
2 finding(s): 🔴 0 critical · 🟠 0 major · 🔵 2 minor 1 posted as inline comment(s) on the changed lines. 1 listed below. Findings outside the changed lines1 minor
Severity is the reviewer's own estimate and is used for ordering, not filtering. No finding blocks merge automatically; a maintainer decides. |
[Medium impact] The PR appears safe to merge; no actionable defects were found. SummaryThe PR forwards the bytes consumed by Express instead of rebuilding ClickHouse request bodies.
No actionable issues were found. Tests were not run during this review.
|
Deep Review✅ No critical issues found. The core fix is sound: capturing inflated pre-parse bytes in an Express 🟡 P2 — recommended
🔵 P3 nitpicks (2)
Reviewers (8): security, testing, maintainability, kieran-typescript returned; correctness, adversarial, reliability, api-contract did not return within the synthesis window — those dimensions were covered by orchestrator analysis (framing/content-length correctness, gzip/identity encoding handling, fail-fast reachability, request-smuggling, and the manual-write vs http-proxy-middleware stream-pipe interaction), which surfaced no P0/P1. Testing gaps:
No finding blocks merge automatically. A maintainer will expect P0/P1 findings in code this PR changes to be fixed; P2/P3 are your call -- fix or reply. Never fix findings about surrounding code here; reply instead. Do not widen the PR. How to respond |
| try { | ||
| await agent | ||
| .post('/clickhouse-proxy/?query_id=test-multipart') | ||
| .set('x-hyperdx-connection-id', connectionId) |
There was a problem hiding this comment.
🔵 minor — The console.error spy cannot catch a failure in the new write path
writeProxyBody logs failures through logger.error (packages/api/src/utils/proxyBody.ts:649), not console.error. So expect(consoleError).not.toHaveBeenCalled() would still pass if the multipart path started going through the catch branch. Spy on the logger, or assert that writeProxyBody returned early, for example by checking that proxyReq.write was never called.
Advisory. Fix if it is a small defect in code this PR changes; otherwise reply in the thread. Do not widen the PR or touch files it did not already change. How to respond
There was a problem hiding this comment.
Switched the spy to logger.error in this commit. Tried it by logging from the unparsed-stream branch locally, the multipart test fails on that call now.
|
Went through the second round in one commit. The mount path one was real. On the Deep Review notes, That's two rounds on these, would be great if a maintainer could take a look from here |
| _res: ServerResponse, | ||
| body: Buffer, | ||
| ) { | ||
| if (/^\/clickhouse-proxy(?:\/|$)/i.test(req.path ?? '')) { |
There was a problem hiding this comment.
🔵 minor — Proxy path in the capture regex must be kept in sync with the mount path by hand, and a mismatch makes every parsed proxy request fail
The capture regex hardcodes /clickhouse-proxy, separately from the mount in api-app.ts:121. If the mount path is renamed or a prefix is added, _hdxProxyBody silently stops being set. writeProxyBody then hits its _body && !captured branch and destroys every JSON, text or urlencoded proxy request. Fix by not parsing proxy bodies at all. Mount clickhouseProxyRouter before the global express.json/text/urlencoded (with a route-local express.json() on /test), or give the parsers a type predicate that skips that router. http-proxy then pipes the original stream untouched. This also removes the capture/reframe logic, the extra 32MB buffer that is held alongside the unused req.body, and the retained /clickhouse-proxy/test credential body, which the regex also matches.
Advisory. Fix if it is a small defect in code this PR changes; otherwise reply in the thread. Do not widen the PR or touch files it did not already change. How to respond
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Summary
Fix request bodies being dropped by the ClickHouse proxy after Express consumes them, including charset-suffixed JSON and URL-encoded forms. Capture the parsers' inflated bytes before decoding or parsing, then forward those bytes with matching framing. This preserves large integers, duplicate JSON keys, empty bodies, form escapes and text encodings. Unparsed multipart streams still pass through untouched.
How to reproduce
Starting point: the API integration stack from CONTRIBUTING.md. Install with
yarn install --immutable, then runmake dev-int-build. The frontend demo and Vercel local mode have no API and cannot reproduce this path.On
main: see #2942. The parser consumes charset-suffixed JSON and URL-encoded forms, then the proxy tries to write the parsed object and never forwards its bytes. The charset-JSON regression case times out.On the earlier PR commit
9a88bbf0: reconstruction rounds integers such as9007199254740993, collapses duplicate JSON keys and changes an empty body into{}. The new fidelity regression cases reproduce those changes.On this branch: run
JEST_RETRY_TIMES=0 make dev-int FILE=clickhouseProxy. The suite uses the authenticated API and a real upstream echo server. It checks exact JSON and form bytes, UTF-16 charset preservation, gzip inflation, chunked framing, empty bodies, duplicate charset parameters, invalid JSON rejection and binary multipart passthrough.The focused unit suite is
JEST_RETRY_TIMES=0 yarn workspace @hyperdx/api exec jest --runInBand --ci --coverage=false src/utils/__tests__/proxyBody.test.ts.Validation: 24 unit tests and 16 real-proxy integration tests passed with retries disabled. The four new fidelity regression cases fail against the earlier PR commit
9a88bbf0.yarn lint:fix, API TypeScript checks and focused ESLint passed. This run used isolated ports and the published ClickStack collector image with the repository's MongoDB/ClickHouse services. Upstream code workflows still need repository approval, and Vercel deployment needs HyperDX team authorization.How to test on Vercel preview
N/A, API-only change.
References
Closes #2942. This follows the deferred work from #2932 and the closed, unmerged attempt in #2978.