Skip to content

http: ServeMux mishandles {wildcard...}, {$}, and method-prefixed patterns #55

Description

@ultramcu

The custom ServeMux in http/server.go supports basic {name} placeholders, but three Go-1.22 routing features are mishandled. In each case the behavior diverges from net/http.ServeMux in upstream Go. I have a fix + tests ready (verified against the real net/http.ServeMux as an oracle) and am happy to open a PR, but since this touches routing behavior I wanted to check the intended scope first.

All examples below: pattern(s) registered on a ServeMux, a request path, what tinygo-org/net currently does, and what upstream net/http does.

1. Multi-segment wildcard {name...} never matches

mux.Handle("/files/{path...}", h)
GET /files/a/b/c
  • Upstream: matches, r.PathValue("path") == "a/b/c".
  • tinygo-org/net: 404 — the request never reaches h.

Root cause: both patternCouldMatch and extractPathValues split on / and require len(patternParts) == len(pathParts), so a trailing {x...} can only ever match a single segment count, never the rest of the path.

2. {$} end-of-path anchor is treated as an ordinary wildcard

mux.Handle("/exact/{$}", h)
GET /exact/sub
  • Upstream: 404{$} anchors the pattern to the end, so /exact/{$} matches only /exact/, not /exact/sub; {$} is not a captured value.
  • tinygo-org/net: matches h (over-match) and sets a bogus path value r.PathValue("$") == "sub".

Root cause: {$} satisfies the generic strings.HasPrefix(part,"{") && strings.HasSuffix(part,"}") test in patternCouldMatch/extractPathValues, so it is handled as a normal capturing placeholder.

3. Method-prefixed patterns ("GET /path") don't route, and corrupt host routing

mux.Handle("GET /foo", h)
GET /foo
  • Upstream: routes GET /foo to h for GET requests.
  • tinygo-org/net: 404, and every other route in the mux breaks.

Root cause: Handle stores the entry under the full key "GET /foo" (so match looking up the bare path /foo never finds it), and because pattern[0] == 'G' != '/' it also sets mux.hosts = true — which makes handler() prepend the host to every subsequent lookup, breaking routing for the whole mux.


Notes

  • The net/ip/mac/parse files appear to be faithful ports of upstream and look correct — these three issues are all in the hand-written ServeMux, which has no direct upstream twin (upstream uses the routing-tree implementation), so I assume it's intentionally a lighter reimplementation.
  • I verified a fix locally against net/http.ServeMux (Go 1.26.3) as an oracle across ~90 cases (the three features above plus precedence interactions like {id} vs {x...} and {$} vs a trailing-slash subtree, method vs method-less, and no-regression on exact / {id} / subtree / host patterns). Fail-before/pass-after holds.

Questions before I PR

  1. Is full Go-1.22 pattern routing ({x...}, {$}, METHOD /path) in scope for tinygo-org/net's ServeMux, or is the minimal {name}-only subset intentional? (Happy to fix all three, or just a subset, per your preference.)
  2. Preferred test location/format — a server_test.go in the package that runs under tinygo test? (My local tests run on host via copied helpers since the package imports internal/*; I'd adapt them to the repo's convention.)

Thanks!

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions