envoyproxy / envoyproxy/gateway
HttpRouteFilter.urlrewrite.replace RegexMatch: substitution containing "?" produces two "?" when client request has query string.
- Dominant language
- Go
- Stars
- 3k
- Forks
- 864
- Avg merge
- 2d 2h
- Merged PRs (30d)
- 140
Description
*Description*:
`HTTPRouteFilter.urlRewrite.path.replaceRegexMatch` cannot safely inject a query
string when the client request already has one. The substitution is treated as a
literal path, then Envoy re-attaches the original query string with another `?`,
yielding a malformed `?a=1?b=2` request-target. This is the common shape of an
nginx-ingress migration that uses `rewrite-target: /$2?brand=$1` to convert a
captured path segment into a query parameter.
### Setup
Envoy Gateway with Gateway API. An `HTTPRoute` matches `/{tenant}/widgets/...` on a
shared hostname, and an attached `HTTPRouteFilter` does nginx-style "rewrite path +
inject brand as query arg".
```yaml
# HTTPRoute (excerpt)
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: widgets-route
spec:
hostnames: ["app.example.com"]
rules:
- matches:
- path:
type: RegularExpression
value: ^/[^/]+/widgets(/.*)?$
filters:
- type: ExtensionRef
extensionRef:
group: gateway.envoyproxy.io
kind: HTTPRouteFilter
name: widgets-path-rewrite
backendRefs:
- name: widgets-svc
port: 80
---
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: HTTPRouteFilter
metadata:
name: widgets-path-rewrite
spec:
urlRewrite:
path:
type: ReplaceRegexMatch
type: ReplaceRegexMatch
replaceRegexMatch:
pattern: ^/([^/]+)/Widgets/?(.*)$
substitution: '/\2?tenant=\1'
## Expected behavior
For `GET /acme/Widgets/items?page=1&size=25`, the upstream should receive:
```http
GET /items?tenant=acme&page=1&size=25
```
i.e. a single `?`, query parameters joined with `&`. This is exactly how
explicitly state:
> "If a replacement string contains the new request arguments, they will be
> appended after them."
## Actual behavior
`substitution: '/\2?tenant=\1'` is treated as a literal path. Envoy appends
another `?`, producing:
```http
GET /items?tenant=acme?page=1&size=25
^ injected ^ original query glued on
```
Two `?` in the request-target. Standard query parsers bind `tenant`
Query] / `@RequestParam`-style validation 400s before the handler runs.
Concrete repro matrix:
Client request Substitution produces Final request-target seen upstream
GET /acme/widgets/items (no /items?tenant=acme /items?tenant=acme ✅
query)
GET /acme/widgets/items?page /items?tenant=acme /items?tenant=acme?page=1
=1 ❌
GET /acme/widgets/items?page /items?tenant=acme /items?tenant=acme?page=1&si
=1&size=25 ze=25 ❌
So the filter only works when the client request has no query string.
Help needed
Is there a supported, atomic way to express "rewrite path and inject a query parameter, merging with any existing
query string" purely via Gateway API / HTTPRouteFilter on Envoy Gateway? Specifically, one of:
1. Semantic fix, no new API. Change replaceRegexMatch so that when the substitution contains ?, the portion
before it becomes the path and the portion after is treated as new query parameters that are merged with the
client's existing query (joined with & ), matching nginx semantics.
2. New declarative API. Add a queryParams mutation block to HTTPRouteFilter (analogous to RequestHeaderMo
difier ), backed by core Envoy's now-available query-parameter mutation (envoyproxy/envoy#37555,
Dec 2024), so users can express "inject brand="
If not a fix at hand, for the time being can someone help recommend a work-around for this.
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.