fix(frontend): collapse double slash when joining trailing-slash upstream base path - #308
Open
detail-app[bot] wants to merge 1 commit into
Conversation
ApprovabilityVerdict: Approved at Macroscope's review found this PR approvable — This is a small, self-contained URI-joining bug fix that removes redundant boundary slashes while preserving existing path and query behavior. Regression tests cover the relevant combinations and no broader runtime or API changes are introduced. You can add or adjust custom eligibility rules. Learn more. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Detail bug report: View on Detail
Bug
UpstreamManager.buildUpstreamUriinsrc/frontend/upstream.zigjoins the configured upstream URL's base path with the inbound request target to form the URI the proxy dials on every forwarded request. The join logic could only insert a missing/between the two halves; it had no branch to collapse a redundant/at the boundary. So when an operator configured an upstream URL whose path ends in/(e.g.https://internal-gateway.corp/datadog/) and a forwarded request arrived with the usual origin-form target starting with/(e.g./api/v2/logs), the join wrotebase_pathverbatim followed byrequest_pathverbatim, emitting…/datadog//api/v2/logs(two adjacent slashes) instead of…/datadog/api/v2/logs.The malformed
//survivesstd.Uri.parse(the next step atsrc/frontend/exec.zig:167) and reachesstd.http.Client.requestas the request target, so the proxy silently forwards to a different resource path than the operator intended. The shipped defaults all use path-less upstream URLs, so out-of-the-box deployments don't hit this; it only surfaces for the base-path configuration the join code exists to support.Fix
Reworked the join in
buildUpstreamUriso that when the written base path ends with/and the request path begins with/, the request's leading/is dropped (single/at the boundary). Otherwise a/is inserted only when neither side provides one. Storage increateUpstreamis left verbatim, sogetUpstreamConfig().base_pathbehavior is unchanged and the previously-correct join combinations are unaffected.The fix is localized to the single copy of the join logic (verified via repo-wide search; there are no duplicates).
Testing
src/frontend/upstream.zig(registered viasrc/root.zig), added two regression guards:buildUpstreamUri collapse boundary slashes— trailing-slash base × leading/no-leading request, with query, with empty path, and asserts the produced URI round-trips throughstd.Uri.parseto a path with no//.buildUpstreamUri join boundary combinations— matrix overbase_path ∈ {"", "/", "/v2", "/v2/"}×request_pathshapes in one manager, covering the previously-correct branches as regression guards.zig build test --summary all(Debug) andzig build test -Doptimize=ReleaseSafe --summary allboth pass (0 failures).zig fmt --check,ziglint,task do, andtask build:safeare clean;zig build -Dfrontend=httpzandzig build -Dfrontend=stdioboth build.http://127.0.0.1:18080/datadog/+ inboundPOST /api/v2/logs→ mock receivedPOST /datadog/api/v2/logs HTTP/1.1(single/), client got200 OKwith the relayed body. With the bug this was/datadog//api/v2/logs.http://127.0.0.1:18080→ mock receivedPOST /api/v2/logs HTTP/1.1(default-config shape unaffected).http://127.0.0.1:18080/datadog→ mock receivedPOST /datadog/api/v2/logs HTTP/1.1(previously-correct path unaffected).std.Io.Threadedbackend during the upstream socket write (programmer bug caused syscall error: INVALinstd/Io/Threaded.zig), andsingle_threadedcan't start its per-connection spawn (ConcurrencyUnavailable);evented/uringare rejected as having no networking in 0.16.0. This abort is in the upstream write phase (afterbuildUpstreamUriran) and is a toolchain issue, not the fix. Both frontends share the identical join + upstream-open code atsrc/frontend/exec.zig:166-167, so the httpz wire results above exercise the fixed join path; a standalonezig testalso confirmsstd.Uri.parsepreserves//in the buggy string and produces none in the fixed string.Fixes ENG-709
Automatic Fixes PRs can be configured here.