WIP: fix(http): trailing slash vs missing path param - #232
Draft
HarshMN2345 wants to merge 1 commit into
Draft
Conversation
HarshMN2345
requested review from
Meldiron,
eldadfux and
lohanidamodar
as code owners
September 8, 2026 10:41
Benchmark resultshttp — Swoole modes (4 cores, 200 VUs, 20s/run)
a = HYPERLOOP_A (process), b = HYPERLOOP_B (coroutine) Shared CI runners — treat absolute numbers as rough, compare modes within a run. Commit ba8d451. |
`Router::match()` dropped every empty path segment before matching, so a trailing slash silently collapsed onto the shorter route: `/blog/` matched `/blog` rather than `/blog/:post`. On an API that pairs a list route with a get route this turns a missing ID into a full listing. Offer the empty trailing segment to a route that expects a param there before dropping it. `/blog/` now reaches `/blog/:post` with an empty post, which the param validator rejects, while `/about/` still falls back to `/about` because no route expects a param at that position.
HarshMN2345
force-pushed
the
fix/router-trailing-slash-param
branch
from
September 8, 2026 10:56
8b3d8ef to
124637b
Compare
HarshMN2345
marked this pull request as draft
September 8, 2026 11:33
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.
The bug
Router::match()strips every empty path segment before matching:So a trailing slash collapses onto the shorter route, and on an API that pairs a list route with a get route a missing ID becomes a full listing. Appwrite has carried this as appwrite/appwrite#6225 since 2023:
GET /v1/functions/:functionId/deployments/with an emptydeploymentIdreturns200and every deployment of the function.Why this branch does not fix it
The commit makes the router try the un-collapsed path first, so an empty trailing segment can bind to a
:param. Probing it against a real route table shows three problems.1. It mis-dispatches requests that have nothing to do with a missing ID. Trying the raw candidate first lets a longer param route take a request away from a static or wildcard route:
The bound value is not an obviously-invalid empty ID —
post=authorslooks valid and reaches the wrong handler. In Appwrite,DELETE /v1/databases/transactions/moves from delete-database (scopedatabases.write) to delete-transaction (scopedocuments.write) with an empty transaction ID. An earlier version of this description claimed wildcard routes were unchanged; that was wrong.2. It is incomplete. Only a single trailing slash is handled. Two or more still collapse:
3. It reverses a deliberate contract.
HttpTest::testNoMismatchRoutewas not incidental. It came from utopia-php/http#96, "Fix route mismatch against shorter paths when params are missing", whose commite8e12a8asserts that/d/must not match/d/:id. Trailing-slash tolerance arrived separately in4114cf8, "add support for trailing slashes in routes and urls". Both are intentional, and #6225 sits in the gap between them. Rewriting that assertion silently picks a winner.Where this leaves it
The router cannot tell a malformed get (
getDeploymentwith an empty ID) from a caller requesting a slash-terminated list URL — both arrive as/v1/functions/x/deployments/. Only the SDK knows which operation was called, so that is where #6225 should be fixed: reject empty required path arguments in the generator templates, before the request is built. Required empty body and query values can be legitimate, and a falsy check would wrongly reject"0".If stricter server-side routing is wanted on top of that, it needs to be a stated policy with a migration story — plausibly an opt-in flag, the way Express exposes
strict routing— not a silent default change in a shared library.