Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions docs/request_rewriters.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,34 @@ In a Rule's `case` configurations, provide `req_rewriter_name`. If there is a Ru

In a Request Rewriter instruction using the `chain` instruction type. Provide the Rewriter Name as the third argument in the instruction as follows: `[ 'chain', 'exec', '$rewriter_name']`. See more information [below](#chain).

## Regex Capture Tokens

An `rmatch` rule can expose its regular expression matches to request rewriters. Numeric tokens use `${0}` for the complete match and `${1}`, `${2}`, etc. for capture groups. A named capture such as `(?P<tenant>[a-z0-9]{3})` is also available as `${tenant}`.

Captures are available only to the matched case rewriter and the current rule's egress rewriter. The ingress rewriter runs before matching and cannot use captures from its own rule, and captures are cleared before the request enters the next route. If the regular expression does not match, no capture tokens are available to the no-match or egress rewriter. Undefined tokens are left unchanged, while an optional capture group that did not participate in a successful match expands to an empty string.

Token expansion is supported in setter and appender values for headers and
parameters, and in configured values for path, method, host, hostname, port and
scheme instructions. Header and parameter replace/delete instructions also
expand their key, search and replacement fields. Chained rewriters receive the
same captures. For example:

```yaml
request_rewriters:
tenant-host:
instructions:
- [ 'hostname', 'set', '${tenant}.writer.example.com' ]
- [ 'header', 'set', 'X-Tenant', '${1}' ]
```

Captured values are inserted without additional validation or escaping. Use
restrictive regular expressions for values written to a hostname, path, header
or query parameter. In particular, a client-controlled authority token can
route a request, including configured upstream credentials, to an unintended
host if the expression is too broad.

When Trickster constructs the final upstream URL, only host components explicitly changed by a request rewriter override the configured backend `origin_url`. A `hostname` rewrite preserves the `origin_url` port, while `host` replaces both hostname and port. The inbound request's host never overrides `origin_url` by itself.

## Instruction Construction Guide

### header
Expand Down
39 changes: 39 additions & 0 deletions docs/rule.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,3 +142,42 @@ backends:
path_routing_disabled: true # restrict routing to this backend via rule only, so
# users cannot directly access via /example-writer-cluster/
```

## Example Rule - Rewrite a Hostname From a Regex Capture

An `rmatch` rule makes its numeric and named capture groups available to the matched case and egress request rewriters. This example routes a request containing a three-character tenant label and prefixes the destination hostname with that tenant.

```yaml
request_rewriters:
tenant-host:
instructions:
- [ 'hostname', 'set', '${tenant}.writer.example.com' ]

rules:
tenant-router:
next_route: example-reader-cluster
input_source: path
input_type: string
operation: rmatch
operation_arg: '\{mylabel="(?P<tenant>[a-z0-9]{3})"\}'
cases:
- matches: [ 'true' ]
req_rewriter_name: tenant-host
next_route: example-writer-cluster

backends:
example:
provider: rule
rule_name: tenant-router

example-reader-cluster:
provider: rpc
origin_url: 'http://reader-cluster.example.com'

example-writer-cluster:
provider: rpc
origin_url: 'http://writer-cluster.example.com'
path_routing_disabled: true
```

For `input_source: path`, matching uses Go's decoded `URL.Path`. For example, `%7Bmylabel%3D%22abc%22%7D` is matched as `{mylabel="abc"}` and `${tenant}` expands to `abc`. `${0}` represents the complete regex match, while `${1}` represents the first capture group. See [Request Rewriters](./request_rewriters.md#regex-capture-tokens) for token lifetime and safety details.
11 changes: 11 additions & 0 deletions pkg/backends/rule/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ func (c *Client) parseOptions(o *ro.Options, rwi rewriter.InstructionsLookup) er
return err
}
compiledRegexes[r.operationArg] = re
r.regex = re
}

if len(o.CaseOptions) > 0 {
Expand Down Expand Up @@ -205,6 +206,16 @@ func (c *Client) parseOptions(o *ro.Options, rwi rewriter.InstructionsLookup) er
}
}

r.hasCaptureTokens = r.egressReqRewriter.HasTokens()
if !r.hasCaptureTokens {
for _, c := range r.cases {
if c.rewriter.HasTokens() {
r.hasCaptureTokens = true
break
}
}
}

c.rule = r
return nil
}
Loading
Loading