Skip to content

Commit 95bb319

Browse files
committed
BUG/MINOR: fix HTTP header add filter to append to existing header value
The Gateway API "add" semantics require that if the header is already present, the new value is appended to the existing one with a comma separator. The previous implementation emitted a plain add-header rule which adds a second header line; the echo backend does not merge them, so the conformance test received only the newly added value. Two rules are now emitted per add entry: a conditional set-header that constructs "<existing>,<new>" when the header is already present, and a fallback add-header for when it is absent. The same fix is applied to responseHeaderModifierRules using res.hdr.
1 parent 8e2b2da commit 95bb319

2 files changed

Lines changed: 62 additions & 11 deletions

File tree

k8s/gate/haproxy/filters/http_filters.go

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -429,11 +429,26 @@ func requestHeaderModifierRules(f *gatewayv1.HTTPHeaderFilter) models.HTTPReques
429429
})
430430
}
431431
for _, h := range f.Add {
432-
rules = append(rules, &models.HTTPRequestRule{
433-
Type: "add-header",
434-
HdrName: string(h.Name),
435-
HdrFormat: h.Value,
436-
})
432+
name := string(h.Name)
433+
// Gateway API "add" semantics: append to the existing header value with a
434+
// comma separator. Two rules are needed: one that combines when the header
435+
// is already present, and one that sets it when it is absent.
436+
rules = append(rules,
437+
&models.HTTPRequestRule{
438+
Type: "set-header",
439+
HdrName: name,
440+
HdrFormat: fmt.Sprintf("%%[req.hdr(%s)],%s", name, h.Value),
441+
Cond: "if",
442+
CondTest: fmt.Sprintf("{ hdr_cnt(%s) gt 0 }", name),
443+
},
444+
&models.HTTPRequestRule{
445+
Type: "add-header",
446+
HdrName: name,
447+
HdrFormat: h.Value,
448+
Cond: "unless",
449+
CondTest: fmt.Sprintf("{ hdr_cnt(%s) gt 0 }", name),
450+
},
451+
)
437452
}
438453
for _, name := range f.Remove {
439454
rules = append(rules, &models.HTTPRequestRule{
@@ -455,11 +470,23 @@ func responseHeaderModifierRules(f *gatewayv1.HTTPHeaderFilter) models.HTTPRespo
455470
})
456471
}
457472
for _, h := range f.Add {
458-
rules = append(rules, &models.HTTPResponseRule{
459-
Type: "add-header",
460-
HdrName: string(h.Name),
461-
HdrFormat: h.Value,
462-
})
473+
name := string(h.Name)
474+
rules = append(rules,
475+
&models.HTTPResponseRule{
476+
Type: "set-header",
477+
HdrName: name,
478+
HdrFormat: fmt.Sprintf("%%[res.hdr(%s)],%s", name, h.Value),
479+
Cond: "if",
480+
CondTest: fmt.Sprintf("{ hdr_cnt(%s) gt 0 }", name),
481+
},
482+
&models.HTTPResponseRule{
483+
Type: "add-header",
484+
HdrName: name,
485+
HdrFormat: h.Value,
486+
Cond: "unless",
487+
CondTest: fmt.Sprintf("{ hdr_cnt(%s) gt 0 }", name),
488+
},
489+
)
463490
}
464491
for _, name := range f.Remove {
465492
rules = append(rules, &models.HTTPResponseRule{

k8s/gate/haproxy/filters/http_filters_test.go

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -346,11 +346,35 @@ func TestToHAProxyRules(t *testing.T) {
346346
},
347347
},
348348
}, "")
349-
assert.Len(t, result.HTTPRequestRules, 3)
349+
// 1 set-header + 2 rules per add (conditional set + fallback add) + 1 del-header
350+
assert.Len(t, result.HTTPRequestRules, 4)
350351
assert.Empty(t, result.HTTPResponseRules)
351352
assert.False(t, result.IsRedirect)
352353
})
353354

355+
t.Run("add header appends to existing value", func(t *testing.T) {
356+
result := ToHAProxyRules([]gatewayv1.HTTPRouteFilter{
357+
{
358+
Type: gatewayv1.HTTPRouteFilterRequestHeaderModifier,
359+
RequestHeaderModifier: &gatewayv1.HTTPHeaderFilter{
360+
Add: []gatewayv1.HTTPHeader{{Name: "X-Append", Value: "new-val"}},
361+
},
362+
},
363+
}, "")
364+
require.Len(t, result.HTTPRequestRules, 2)
365+
// Rule 0: conditional set-header combining existing + new value
366+
r0 := result.HTTPRequestRules[0]
367+
assert.Equal(t, "set-header", r0.Type)
368+
assert.Equal(t, "X-Append", r0.HdrName)
369+
assert.Equal(t, "%[req.hdr(X-Append)],new-val", r0.HdrFormat)
370+
assert.Equal(t, "if", r0.Cond)
371+
assert.Equal(t, "{ hdr_cnt(X-Append) gt 0 }", r0.CondTest)
372+
// Rule 1: add-header when header is absent
373+
r1 := result.HTTPRequestRules[1]
374+
assert.Equal(t, "add-header", r1.Type)
375+
assert.Equal(t, "unless", r1.Cond)
376+
})
377+
354378
t.Run("response header modifier", func(t *testing.T) {
355379
result := ToHAProxyRules([]gatewayv1.HTTPRouteFilter{
356380
{

0 commit comments

Comments
 (0)