Skip to content

Commit 8f170bb

Browse files
committed
Addressing review
1 parent 0283a77 commit 8f170bb

File tree

9 files changed

+105
-228
lines changed

9 files changed

+105
-228
lines changed

caddytest/integration/caddyfile_adapt/reverse_proxy_retry_match_oneline.caddyfiletest

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
reverse_proxy 127.0.0.1:65535 {
44
lb_retries 3
55
lb_retry_match expression `{rp.status_code} in [502, 503]`
6-
lb_retry_match expression `isTransportError() || {rp.status_code} == 502`
6+
lb_retry_match expression `{rp.is_transport_error} || {rp.status_code} == 502`
77
lb_retry_match expression `method('POST') && {rp.status_code} == 503`
8+
lb_retry_match `{rp.status_code} == 504`
9+
lb_retry_match `{rp.is_transport_error} && method('PUT')`
810
}
911
----------
1012
{
@@ -27,10 +29,16 @@ reverse_proxy 127.0.0.1:65535 {
2729
"expression": "{http.reverse_proxy.status_code} in [502, 503]"
2830
},
2931
{
30-
"expression": "isTransportError() || {http.reverse_proxy.status_code} == 502"
32+
"expression": "{http.reverse_proxy.is_transport_error} || {http.reverse_proxy.status_code} == 502"
3133
},
3234
{
3335
"expression": "method('POST') \u0026\u0026 {http.reverse_proxy.status_code} == 503"
36+
},
37+
{
38+
"expression": "{http.reverse_proxy.status_code} == 504"
39+
},
40+
{
41+
"expression": "{http.reverse_proxy.is_transport_error} \u0026\u0026 method('PUT')"
3442
}
3543
]
3644
},

caddytest/integration/caddyfile_adapt/reverse_proxy_retry_match_response.caddyfiletest

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,12 @@ reverse_proxy 127.0.0.1:65535 {
5050
expression `header_regexp('Content-Type', '^application/json') && {rp.status_code} == 502`
5151
}
5252

53-
# isTransportError() for transport error handling
53+
# transport error handling via placeholder
5454
lb_retry_match {
55-
expression `isTransportError() || {rp.status_code} in [502, 503]`
55+
expression `{rp.is_transport_error} || {rp.status_code} in [502, 503]`
5656
}
5757
lb_retry_match {
58-
expression `isTransportError() && method('POST')`
58+
expression `{rp.is_transport_error} && method('POST')`
5959
}
6060
}
6161
----------
@@ -124,10 +124,10 @@ reverse_proxy 127.0.0.1:65535 {
124124
"expression": "header_regexp('Content-Type', '^application/json') \u0026\u0026 {http.reverse_proxy.status_code} == 502"
125125
},
126126
{
127-
"expression": "isTransportError() || {http.reverse_proxy.status_code} in [502, 503]"
127+
"expression": "{http.reverse_proxy.is_transport_error} || {http.reverse_proxy.status_code} in [502, 503]"
128128
},
129129
{
130-
"expression": "isTransportError() \u0026\u0026 method('POST')"
130+
"expression": "{http.reverse_proxy.is_transport_error} \u0026\u0026 method('POST')"
131131
}
132132
]
133133
},

caddytest/integration/reverseproxy_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -738,7 +738,7 @@ func TestReverseProxyRetryMatchCombined(t *testing.T) {
738738
}
739739

740740
// TestReverseProxyRetryMatchIsTransportError verifies that the
741-
// isTransportError() CEL function correctly identifies transport errors
741+
// {rp.is_transport_error} == true CEL function correctly identifies transport errors
742742
// and allows retrying them alongside response-based matching
743743
func TestReverseProxyRetryMatchIsTransportError(t *testing.T) {
744744
// Good upstream: returns 200
@@ -784,7 +784,7 @@ func TestReverseProxyRetryMatchIsTransportError(t *testing.T) {
784784
lb_policy round_robin
785785
lb_retries 1
786786
lb_retry_match {
787-
expression `+"`isTransportError() || {rp.status_code} in [502, 503]`"+`
787+
expression `+"`{rp.is_transport_error} || {rp.status_code} in [502, 503]`"+`
788788
}
789789
}
790790
}

modules/caddyhttp/celmatcher.go

Lines changed: 0 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -126,10 +126,6 @@ func (m *MatchExpression) Provision(ctx caddy.Context) error {
126126
// light (and possibly naïve) syntactic sugar
127127
m.expandedExpr = placeholderRegexp.ReplaceAllString(m.Expr, placeholderExpansion)
128128

129-
// rewrite isTransportError() to isTransportError(req) so the
130-
// function receives the request context
131-
m.expandedExpr = transportErrorRegexp.ReplaceAllString(m.expandedExpr, transportErrorExpansion)
132-
133129
// as a second pass, we'll strip the escape character from an escaped
134130
// placeholder, so that it can be used as an input to other CEL functions
135131
m.expandedExpr = escapedPlaceholderRegexp.ReplaceAllString(m.expandedExpr, escapedPlaceholderExpansion)
@@ -172,11 +168,6 @@ func (m *MatchExpression) Provision(ctx caddy.Context) error {
172168
[]*cel.Type{httpRequestObjectType, cel.StringType},
173169
cel.AnyType,
174170
)),
175-
cel.Function(CELTransportErrorFuncName, cel.SingletonUnaryBinding(isTransportErrorFunc), cel.Overload(
176-
CELTransportErrorFuncName+"_httpRequest",
177-
[]*cel.Type{httpRequestObjectType},
178-
cel.BoolType,
179-
)),
180171
cel.Variable(CELRequestVarName, httpRequestObjectType),
181172
cel.CustomTypeAdapter(m.ta),
182173
ext.Strings(),
@@ -218,10 +209,6 @@ func (m MatchExpression) Match(r *http.Request) bool {
218209
return match
219210
}
220211

221-
// CanMatchResponse is a marker indicating that expression matchers
222-
// may reference response data via placeholders like {rp.status_code}
223-
func (m MatchExpression) CanMatchResponse() {}
224-
225212
// MatchWithError returns true if r matches m.
226213
func (m MatchExpression) MatchWithError(r *http.Request) (bool, error) {
227214
celReq := celHTTPRequest{r}
@@ -293,29 +280,6 @@ func (m MatchExpression) caddyPlaceholderFunc(lhs, rhs ref.Val) ref.Val {
293280
return m.ta.NativeToValue(val)
294281
}
295282

296-
// isTransportErrorFunc implements the isTransportError() CEL function
297-
// that returns true when the evaluation is happening during a transport
298-
// error retry decision (as opposed to a response-based retry decision)
299-
func isTransportErrorFunc(val ref.Val) ref.Val {
300-
celReq, ok := val.(celHTTPRequest)
301-
if !ok {
302-
return types.NewErr(
303-
"invalid request of type '%v' to %s(request)",
304-
val.Type(),
305-
CELTransportErrorFuncName,
306-
)
307-
}
308-
repl, ok := celReq.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
309-
if !ok || repl == nil {
310-
return types.Bool(false)
311-
}
312-
v, _ := repl.Get("http.reverse_proxy.is_transport_error")
313-
if b, ok := v.(bool); ok {
314-
return types.Bool(b)
315-
}
316-
return types.Bool(false)
317-
}
318-
319283
// httpRequestCELType is the type representation of a native HTTP request.
320284
var httpRequestCELType = cel.ObjectType("http.Request", traits.ReceiverType)
321285

@@ -850,10 +814,6 @@ var (
850814
placeholderRegexp = regexp.MustCompile(`([^\\]|^){([a-zA-Z][\w.-]+)}`)
851815
placeholderExpansion = `${1}ph(req, "${2}")`
852816

853-
// rewrite isTransportError() calls to pass the request object
854-
transportErrorRegexp = regexp.MustCompile(`isTransportError\(\)`)
855-
transportErrorExpansion = `isTransportError(req)`
856-
857817
// As a second pass, we need to strip the escape character in front of
858818
// the placeholder, if it exists.
859819
escapedPlaceholderRegexp = regexp.MustCompile(`\\{([a-zA-Z][\w.-]+)}`)
@@ -867,10 +827,6 @@ var httpRequestObjectType = cel.ObjectType("http.Request")
867827
// The name of the CEL function which accesses Replacer values.
868828
const CELPlaceholderFuncName = "ph"
869829

870-
// The name of the CEL function that checks if the current evaluation
871-
// context is a transport error (connection succeeded but roundtrip failed)
872-
const CELTransportErrorFuncName = "isTransportError"
873-
874830
// The name of the CEL request variable.
875831
const CELRequestVarName = "req"
876832

modules/caddyhttp/matchers.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1562,6 +1562,14 @@ func ParseCaddyfileNestedMatcherSet(d *caddyfile.Dispenser) (caddy.ModuleMap, er
15621562
// instances of the matcher in this set
15631563
tokensByMatcherName := make(map[string][]caddyfile.Token)
15641564
for nesting := d.Nesting(); d.NextArg() || d.NextBlock(nesting); {
1565+
// if the token is quoted (backtick), treat it as a shorthand
1566+
// for an expression matcher, same as @named matcher parsing
1567+
if d.Token().Quoted() {
1568+
expressionToken := d.Token().Clone()
1569+
expressionToken.Text = "expression"
1570+
tokensByMatcherName["expression"] = append(tokensByMatcherName["expression"], expressionToken, d.Token())
1571+
continue
1572+
}
15651573
matcherName := d.Val()
15661574
tokensByMatcherName[matcherName] = append(tokensByMatcherName[matcherName], d.NextSegment()...)
15671575
}

modules/caddyhttp/reverseproxy/caddyfile.go

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -327,13 +327,6 @@ func (h *Handler) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
327327
if err != nil {
328328
return d.Errf("failed to parse lb_retry_match: %v", err)
329329
}
330-
// expression matchers and non-expression matchers cannot be
331-
// mixed in the same block - expression matchers evaluate
332-
// against responses while non-expression matchers evaluate
333-
// against transport errors
334-
if _, hasExpr := matcherSet["expression"]; hasExpr && len(matcherSet) > 1 {
335-
return d.Errf("lb_retry_match: expression cannot be mixed with other matchers in the same block; use either an expression or request matchers, not both")
336-
}
337330
if h.LoadBalancing == nil {
338331
h.LoadBalancing = new(LoadBalancing)
339332
}

0 commit comments

Comments
 (0)