From 9e690577680a872419cd53f915f6fc451f12bc4a Mon Sep 17 00:00:00 2001 From: kaihsun Date: Sun, 21 Jan 2024 23:38:44 +0000 Subject: [PATCH 01/72] Add support for ResponseHeaderModifier for HTTPRouteRule objects Problem: Users want to add, set, and remove response headers. Solution: Use `add_header` NGINX directive to support `ResponseHeaderModifier`. * If the action is `set`, we simply configure the `add_header` directive with the given value in the HTTPRoute spec. * If the action is `remove`, we configure the `add_header` directive with the value set to an empty string. * If the action is `add`, we create a mapping for the `$http_header_name` variable appending a comma at the end of any client provided headers (if present) and prepend this to the given value in the HTTPRoute spec. --- .../mode/static/nginx/config/http/config.go | 1 + internal/mode/static/nginx/config/maps.go | 18 ++++++---- internal/mode/static/nginx/config/servers.go | 33 +++++++++++++++++++ .../static/nginx/config/servers_template.go | 3 ++ .../mode/static/nginx/config/servers_test.go | 24 ++++++++++++++ .../static/state/dataplane/configuration.go | 5 +++ internal/mode/static/state/dataplane/types.go | 2 ++ internal/mode/static/state/graph/httproute.go | 9 ++--- .../mode/static/state/graph/httproute_test.go | 2 +- 9 files changed, 86 insertions(+), 11 deletions(-) diff --git a/internal/mode/static/nginx/config/http/config.go b/internal/mode/static/nginx/config/http/config.go index f20a94f2cf..d04b8e0811 100644 --- a/internal/mode/static/nginx/config/http/config.go +++ b/internal/mode/static/nginx/config/http/config.go @@ -21,6 +21,7 @@ type Location struct { Return *Return Rewrites []string GRPC bool + AddHeaders []Header } // Header defines an HTTP header to be passed to the proxied server. diff --git a/internal/mode/static/nginx/config/maps.go b/internal/mode/static/nginx/config/maps.go index a4d6c419b6..01e4f1764e 100644 --- a/internal/mode/static/nginx/config/maps.go +++ b/internal/mode/static/nginx/config/maps.go @@ -21,17 +21,23 @@ func executeMaps(conf dataplane.Configuration) []executeResult { func buildAddHeaderMaps(servers []dataplane.VirtualServer) []http.Map { addHeaderNames := make(map[string]struct{}) + extractAddHeaderNames := func(headerModifiers []dataplane.HTTPHeader) { + for _, addHeader := range headerModifiers { + lowerName := strings.ToLower(addHeader.Name) + if _, ok := addHeaderNames[lowerName]; !ok { + addHeaderNames[lowerName] = struct{}{} + } + } + } for _, s := range servers { for _, pr := range s.PathRules { for _, mr := range pr.MatchRules { if mr.Filters.RequestHeaderModifiers != nil { - for _, addHeader := range mr.Filters.RequestHeaderModifiers.Add { - lowerName := strings.ToLower(addHeader.Name) - if _, ok := addHeaderNames[lowerName]; !ok { - addHeaderNames[lowerName] = struct{}{} - } - } + extractAddHeaderNames(mr.Filters.RequestHeaderModifiers.Add) + } + if mr.Filters.ResponseHeaderModifiers != nil { + extractAddHeaderNames(mr.Filters.ResponseHeaderModifiers.Add) } } } diff --git a/internal/mode/static/nginx/config/servers.go b/internal/mode/static/nginx/config/servers.go index 6884007630..a44cb60336 100644 --- a/internal/mode/static/nginx/config/servers.go +++ b/internal/mode/static/nginx/config/servers.go @@ -294,6 +294,7 @@ func updateLocationsForFilters( rewrites := createRewritesValForRewriteFilter(filters.RequestURLRewrite, path) proxySetHeaders := generateProxySetHeaders(&matchRule.Filters, grpc) + responseAddHeaders := generateAddHeaders(&matchRule.Filters) for i := range buildLocations { if rewrites != nil { if rewrites.Rewrite != "" { @@ -308,6 +309,7 @@ func updateLocationsForFilters( generateProtocolString(buildLocations[i].ProxySSLVerify, grpc), grpc, ) + buildLocations[i].AddHeaders = responseAddHeaders buildLocations[i].ProxyPass = proxyPass buildLocations[i].GRPC = grpc } @@ -596,6 +598,37 @@ func generateProxySetHeaders(filters *dataplane.HTTPFilters, grpc bool) []http.H return append(proxySetHeaders, headers...) } +func generateAddHeaders(filters *dataplane.HTTPFilters) []http.Header { + headers := make([]http.Header, len(baseHeaders)) + copy(headers, baseHeaders) + + if filters == nil || filters.ResponseHeaderModifiers == nil { + return headers + } + + headerFilter := filters.ResponseHeaderModifiers + + headerLen := len(headerFilter.Add) + len(headerFilter.Set) + len(headerFilter.Remove) + len(headers) + responseAddHeaders := make([]http.Header, 0, headerLen) + if len(headerFilter.Add) > 0 { + addHeaders := convertAddHeaders(headerFilter.Add) + responseAddHeaders = append(responseAddHeaders, addHeaders...) + } + if len(headerFilter.Set) > 0 { + setHeaders := convertSetHeaders(headerFilter.Set) + responseAddHeaders = append(responseAddHeaders, setHeaders...) + } + // If the value of a header field is an empty string then this field will not be passed to a proxied server + for _, h := range headerFilter.Remove { + responseAddHeaders = append(responseAddHeaders, http.Header{ + Name: h, + Value: "", + }) + } + + return append(responseAddHeaders, headers...) +} + func convertAddHeaders(headers []dataplane.HTTPHeader) []http.Header { locHeaders := make([]http.Header, 0, len(headers)) for _, h := range headers { diff --git a/internal/mode/static/nginx/config/servers_template.go b/internal/mode/static/nginx/config/servers_template.go index d4ad022202..5b4ce443e4 100644 --- a/internal/mode/static/nginx/config/servers_template.go +++ b/internal/mode/static/nginx/config/servers_template.go @@ -58,6 +58,9 @@ server { {{ $proxyOrGRPC }}_set_header {{ $h.Name }} "{{ $h.Value }}"; {{- end }} {{ $proxyOrGRPC }}_pass {{ $l.ProxyPass }}; + {{ range $h := $l.AddHeaders }} + add_header {{ $h.Name }} "{{ $h.Value }}"; + {{- end }} proxy_http_version 1.1; {{- if $l.ProxySSLVerify }} {{ $proxyOrGRPC }}_ssl_verify on; diff --git a/internal/mode/static/nginx/config/servers_test.go b/internal/mode/static/nginx/config/servers_test.go index 990a3a4c52..3c66a0224c 100644 --- a/internal/mode/static/nginx/config/servers_test.go +++ b/internal/mode/static/nginx/config/servers_test.go @@ -615,16 +615,19 @@ func TestCreateServers(t *testing.T) { Path: "@rule0-route0", ProxyPass: "http://test_foo_80$request_uri", ProxySetHeaders: baseHeaders, + AddHeaders: baseHeaders, }, { Path: "@rule0-route1", ProxyPass: "http://test_foo_80$request_uri", ProxySetHeaders: baseHeaders, + AddHeaders: baseHeaders, }, { Path: "@rule0-route2", ProxyPass: "http://test_foo_80$request_uri", ProxySetHeaders: baseHeaders, + AddHeaders: baseHeaders, }, { Path: "/", @@ -634,6 +637,7 @@ func TestCreateServers(t *testing.T) { Path: "@rule1-route0", ProxyPass: "http://$test__route1_rule1$request_uri", ProxySetHeaders: baseHeaders, + AddHeaders: baseHeaders, }, { Path: "/test/", @@ -643,11 +647,13 @@ func TestCreateServers(t *testing.T) { Path: "/path-only/", ProxyPass: "http://invalid-backend-ref$request_uri", ProxySetHeaders: baseHeaders, + AddHeaders: baseHeaders, }, { Path: "= /path-only", ProxyPass: "http://invalid-backend-ref$request_uri", ProxySetHeaders: baseHeaders, + AddHeaders: baseHeaders, }, { Path: "/backend-tls-policy/", @@ -715,18 +721,21 @@ func TestCreateServers(t *testing.T) { Rewrites: []string{"^ /replacement break"}, ProxyPass: "http://test_foo_80", ProxySetHeaders: rewriteProxySetHeaders, + AddHeaders: baseHeaders, }, { Path: "= /rewrite", Rewrites: []string{"^ /replacement break"}, ProxyPass: "http://test_foo_80", ProxySetHeaders: rewriteProxySetHeaders, + AddHeaders: baseHeaders, }, { Path: "@rule8-route0", Rewrites: []string{"^/rewrite-with-headers(.*)$ /prefix-replacement$1 break"}, ProxyPass: "http://test_foo_80", ProxySetHeaders: rewriteProxySetHeaders, + AddHeaders: baseHeaders, }, { Path: "/rewrite-with-headers/", @@ -766,11 +775,13 @@ func TestCreateServers(t *testing.T) { Path: "= /exact", ProxyPass: "http://test_foo_80$request_uri", ProxySetHeaders: baseHeaders, + AddHeaders: baseHeaders, }, { Path: "@rule12-route0", ProxyPass: "http://test_foo_80$request_uri", ProxySetHeaders: baseHeaders, + AddHeaders: baseHeaders, }, { Path: "= /test", @@ -801,6 +812,7 @@ func TestCreateServers(t *testing.T) { Value: "$connection_upgrade", }, }, + AddHeaders: baseHeaders, }, { Path: "= /proxy-set-headers", @@ -827,6 +839,7 @@ func TestCreateServers(t *testing.T) { Value: "$connection_upgrade", }, }, + AddHeaders: baseHeaders, }, { Path: "= /grpc/method", @@ -963,11 +976,13 @@ func TestCreateServersConflicts(t *testing.T) { Path: "/coffee/", ProxyPass: "http://test_foo_80$request_uri", ProxySetHeaders: baseHeaders, + AddHeaders: baseHeaders, }, { Path: "= /coffee", ProxyPass: "http://test_bar_80$request_uri", ProxySetHeaders: baseHeaders, + AddHeaders: baseHeaders, }, createDefaultRootLocation(), }, @@ -1001,11 +1016,13 @@ func TestCreateServersConflicts(t *testing.T) { Path: "= /coffee", ProxyPass: "http://test_foo_80$request_uri", ProxySetHeaders: baseHeaders, + AddHeaders: baseHeaders, }, { Path: "/coffee/", ProxyPass: "http://test_bar_80$request_uri", ProxySetHeaders: baseHeaders, + AddHeaders: baseHeaders, }, createDefaultRootLocation(), }, @@ -1049,11 +1066,13 @@ func TestCreateServersConflicts(t *testing.T) { Path: "/coffee/", ProxyPass: "http://test_bar_80$request_uri", ProxySetHeaders: baseHeaders, + AddHeaders: baseHeaders, }, { Path: "= /coffee", ProxyPass: "http://test_baz_80$request_uri", ProxySetHeaders: baseHeaders, + AddHeaders: baseHeaders, }, createDefaultRootLocation(), }, @@ -1172,11 +1191,13 @@ func TestCreateLocationsRootPath(t *testing.T) { Path: "/path-1", ProxyPass: "http://test_foo_80$request_uri", ProxySetHeaders: baseHeaders, + AddHeaders: baseHeaders, }, { Path: "/path-2", ProxyPass: "http://test_foo_80$request_uri", ProxySetHeaders: baseHeaders, + AddHeaders: baseHeaders, }, { Path: "/", @@ -1222,16 +1243,19 @@ func TestCreateLocationsRootPath(t *testing.T) { Path: "/path-1", ProxyPass: "http://test_foo_80$request_uri", ProxySetHeaders: baseHeaders, + AddHeaders: baseHeaders, }, { Path: "/path-2", ProxyPass: "http://test_foo_80$request_uri", ProxySetHeaders: baseHeaders, + AddHeaders: baseHeaders, }, { Path: "/", ProxyPass: "http://test_foo_80$request_uri", ProxySetHeaders: baseHeaders, + AddHeaders: baseHeaders, }, }, }, diff --git a/internal/mode/static/state/dataplane/configuration.go b/internal/mode/static/state/dataplane/configuration.go index 36999dcd4e..3978438a73 100644 --- a/internal/mode/static/state/dataplane/configuration.go +++ b/internal/mode/static/state/dataplane/configuration.go @@ -541,6 +541,11 @@ func createHTTPFilters(filters []v1.HTTPRouteFilter) HTTPFilters { // using the first filter result.RequestHeaderModifiers = convertHTTPHeaderFilter(f.RequestHeaderModifier) } + case v1.HTTPRouteFilterResponseHeaderModifier: + if result.ResponseHeaderModifiers == nil { + // using the first filter + result.ResponseHeaderModifiers = convertHTTPHeaderFilter(f.ResponseHeaderModifier) + } } } return result diff --git a/internal/mode/static/state/dataplane/types.go b/internal/mode/static/state/dataplane/types.go index 53453b0d82..3e7a861afc 100644 --- a/internal/mode/static/state/dataplane/types.go +++ b/internal/mode/static/state/dataplane/types.go @@ -114,6 +114,8 @@ type HTTPFilters struct { RequestURLRewrite *HTTPURLRewriteFilter // RequestHeaderModifiers holds the HTTPHeaderFilter. RequestHeaderModifiers *HTTPHeaderFilter + // ResponseHeaderModifiers holds the HTTPHeaderFilter. + ResponseHeaderModifiers *HTTPHeaderFilter } // HTTPHeader represents an HTTP header. diff --git a/internal/mode/static/state/graph/httproute.go b/internal/mode/static/state/graph/httproute.go index 5a61d92185..9ac9ca81dc 100644 --- a/internal/mode/static/state/graph/httproute.go +++ b/internal/mode/static/state/graph/httproute.go @@ -247,7 +247,9 @@ func validateFilter( case v1.HTTPRouteFilterURLRewrite: return validateFilterRewrite(validator, filter, filterPath) case v1.HTTPRouteFilterRequestHeaderModifier: - return validateFilterHeaderModifier(validator, filter, filterPath) + return validateFilterHeaderModifier(validator, filter.RequestHeaderModifier, filterPath) + case v1.HTTPRouteFilterResponseHeaderModifier: + return validateFilterHeaderModifier(validator, filter.ResponseHeaderModifier, filterPath) default: valErr := field.NotSupported( filterPath.Child("type"), @@ -256,6 +258,7 @@ func validateFilter( string(v1.HTTPRouteFilterRequestRedirect), string(v1.HTTPRouteFilterURLRewrite), string(v1.HTTPRouteFilterRequestHeaderModifier), + string(v1.HTTPRouteFilterResponseHeaderModifier), }, ) allErrs = append(allErrs, valErr) @@ -358,11 +361,9 @@ func validateFilterRewrite( func validateFilterHeaderModifier( validator validation.HTTPFieldsValidator, - filter v1.HTTPRouteFilter, + headerModifier *v1.HTTPHeaderFilter, filterPath *field.Path, ) field.ErrorList { - headerModifier := filter.RequestHeaderModifier - headerModifierPath := filterPath.Child("requestHeaderModifier") if headerModifier == nil { diff --git a/internal/mode/static/state/graph/httproute_test.go b/internal/mode/static/state/graph/httproute_test.go index 36bddc430c..258cfb10c6 100644 --- a/internal/mode/static/state/graph/httproute_test.go +++ b/internal/mode/static/state/graph/httproute_test.go @@ -1318,7 +1318,7 @@ func TestValidateFilterRequestHeaderModifier(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { g := NewWithT(t) - allErrs := validateFilterHeaderModifier(test.validator, test.filter, filterPath) + allErrs := validateFilterHeaderModifier(test.validator, test.filter.RequestHeaderModifier, filterPath) g.Expect(allErrs).To(HaveLen(test.expectErrCount)) }) } From d440ae7cac227a7d7ff5035a96a8c1b32a047763 Mon Sep 17 00:00:00 2001 From: kaihsun Date: Mon, 22 Jan 2024 01:06:18 +0000 Subject: [PATCH 02/72] refactor validateFilterHeaderModifier --- internal/mode/static/state/graph/httproute.go | 10 ++++++---- internal/mode/static/state/graph/httproute_test.go | 4 +++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/internal/mode/static/state/graph/httproute.go b/internal/mode/static/state/graph/httproute.go index 9ac9ca81dc..70ec604b60 100644 --- a/internal/mode/static/state/graph/httproute.go +++ b/internal/mode/static/state/graph/httproute.go @@ -247,9 +247,9 @@ func validateFilter( case v1.HTTPRouteFilterURLRewrite: return validateFilterRewrite(validator, filter, filterPath) case v1.HTTPRouteFilterRequestHeaderModifier: - return validateFilterHeaderModifier(validator, filter.RequestHeaderModifier, filterPath) + return validateFilterHeaderModifier(filter.Type, validator, filter.RequestHeaderModifier, filterPath) case v1.HTTPRouteFilterResponseHeaderModifier: - return validateFilterHeaderModifier(validator, filter.ResponseHeaderModifier, filterPath) + return validateFilterHeaderModifier(filter.Type, validator, filter.ResponseHeaderModifier, filterPath) default: valErr := field.NotSupported( filterPath.Child("type"), @@ -360,14 +360,16 @@ func validateFilterRewrite( } func validateFilterHeaderModifier( + filterType v1.HTTPRouteFilterType, validator validation.HTTPFieldsValidator, headerModifier *v1.HTTPHeaderFilter, filterPath *field.Path, ) field.ErrorList { - headerModifierPath := filterPath.Child("requestHeaderModifier") + filterTypeStr := string(filterType) + headerModifierPath := filterPath.Child(filterTypeStr) if headerModifier == nil { - return field.ErrorList{field.Required(headerModifierPath, "requestHeaderModifier cannot be nil")} + return field.ErrorList{field.Required(filterPath, "cannot be nil")} } return validateFilterHeaderModifierFields(validator, headerModifier, headerModifierPath) diff --git a/internal/mode/static/state/graph/httproute_test.go b/internal/mode/static/state/graph/httproute_test.go index 258cfb10c6..5983d54ac6 100644 --- a/internal/mode/static/state/graph/httproute_test.go +++ b/internal/mode/static/state/graph/httproute_test.go @@ -1318,7 +1318,9 @@ func TestValidateFilterRequestHeaderModifier(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { g := NewWithT(t) - allErrs := validateFilterHeaderModifier(test.validator, test.filter.RequestHeaderModifier, filterPath) + allErrs := validateFilterHeaderModifier( + gatewayv1.HTTPRouteFilterRequestHeaderModifier, test.validator, test.filter.RequestHeaderModifier, filterPath, + ) g.Expect(allErrs).To(HaveLen(test.expectErrCount)) }) } From c4cbba2dfb46a9473af41d03bbfb90cae7a50410 Mon Sep 17 00:00:00 2001 From: kaihsun Date: Mon, 22 Jan 2024 01:24:30 +0000 Subject: [PATCH 03/72] Rename ValidateRequestHeader to ValidateFilterHeader --- .../nginx/config/validation/http_filters.go | 4 +- .../config/validation/http_filters_test.go | 12 +- internal/mode/static/state/graph/httproute.go | 10 +- .../mode/static/state/graph/httproute_test.go | 10 +- .../fake_httpfields_validator.go | 290 +++++++++--------- .../mode/static/state/validation/validator.go | 4 +- 6 files changed, 165 insertions(+), 165 deletions(-) diff --git a/internal/mode/static/nginx/config/validation/http_filters.go b/internal/mode/static/nginx/config/validation/http_filters.go index 3fc638108e..ba12a86257 100644 --- a/internal/mode/static/nginx/config/validation/http_filters.go +++ b/internal/mode/static/nginx/config/validation/http_filters.go @@ -72,13 +72,13 @@ func (HTTPURLRewriteValidator) ValidateRewritePath(path string) error { return nil } -func (HTTPRequestHeaderValidator) ValidateRequestHeaderName(name string) error { +func (HTTPRequestHeaderValidator) ValidateFilterHeaderName(name string) error { return validateHeaderName(name) } var requestHeaderValueExamples = []string{"my-header-value", "example/12345=="} -func (HTTPRequestHeaderValidator) ValidateRequestHeaderValue(value string) error { +func (HTTPRequestHeaderValidator) ValidateFilterHeaderValue(value string) error { // Variables in header values are supported by NGINX but not required by the Gateway API. return validateEscapedStringNoVarExpansion(value, requestHeaderValueExamples) } diff --git a/internal/mode/static/nginx/config/validation/http_filters_test.go b/internal/mode/static/nginx/config/validation/http_filters_test.go index c216a30224..4bd8fe6589 100644 --- a/internal/mode/static/nginx/config/validation/http_filters_test.go +++ b/internal/mode/static/nginx/config/validation/http_filters_test.go @@ -88,25 +88,25 @@ func TestValidateRewritePath(t *testing.T) { ) } -func TestValidateRequestHeaderName(t *testing.T) { +func TestValidateFilterHeaderName(t *testing.T) { validator := HTTPRequestHeaderValidator{} testValidValuesForSimpleValidator( t, - validator.ValidateRequestHeaderName, + validator.ValidateFilterHeaderName, "Content-Encoding", "MyBespokeHeader", ) - testInvalidValuesForSimpleValidator(t, validator.ValidateRequestHeaderName, "$Content-Encoding") + testInvalidValuesForSimpleValidator(t, validator.ValidateFilterHeaderName, "$Content-Encoding") } -func TestValidateRequestHeaderValue(t *testing.T) { +func TestValidateFilterHeaderValue(t *testing.T) { validator := HTTPRequestHeaderValidator{} testValidValuesForSimpleValidator( t, - validator.ValidateRequestHeaderValue, + validator.ValidateFilterHeaderValue, "my-cookie-name", "ssl_(server_name}", "example/1234==", @@ -115,7 +115,7 @@ func TestValidateRequestHeaderValue(t *testing.T) { testInvalidValuesForSimpleValidator( t, - validator.ValidateRequestHeaderValue, + validator.ValidateFilterHeaderValue, "$Content-Encoding", `"example"`, ) diff --git a/internal/mode/static/state/graph/httproute.go b/internal/mode/static/state/graph/httproute.go index 70ec604b60..6c2a542db1 100644 --- a/internal/mode/static/state/graph/httproute.go +++ b/internal/mode/static/state/graph/httproute.go @@ -397,27 +397,27 @@ func validateFilterHeaderModifierFields( ) for _, h := range headerModifier.Add { - if err := validator.ValidateRequestHeaderName(string(h.Name)); err != nil { + if err := validator.ValidateFilterHeaderName(string(h.Name)); err != nil { valErr := field.Invalid(headerModifierPath.Child("add"), h, err.Error()) allErrs = append(allErrs, valErr) } - if err := validator.ValidateRequestHeaderValue(h.Value); err != nil { + if err := validator.ValidateFilterHeaderValue(h.Value); err != nil { valErr := field.Invalid(headerModifierPath.Child("add"), h, err.Error()) allErrs = append(allErrs, valErr) } } for _, h := range headerModifier.Set { - if err := validator.ValidateRequestHeaderName(string(h.Name)); err != nil { + if err := validator.ValidateFilterHeaderName(string(h.Name)); err != nil { valErr := field.Invalid(headerModifierPath.Child("set"), h, err.Error()) allErrs = append(allErrs, valErr) } - if err := validator.ValidateRequestHeaderValue(h.Value); err != nil { + if err := validator.ValidateFilterHeaderValue(h.Value); err != nil { valErr := field.Invalid(headerModifierPath.Child("set"), h, err.Error()) allErrs = append(allErrs, valErr) } } for _, h := range headerModifier.Remove { - if err := validator.ValidateRequestHeaderName(h); err != nil { + if err := validator.ValidateFilterHeaderName(h); err != nil { valErr := field.Invalid(headerModifierPath.Child("remove"), h, err.Error()) allErrs = append(allErrs, valErr) } diff --git a/internal/mode/static/state/graph/httproute_test.go b/internal/mode/static/state/graph/httproute_test.go index 5983d54ac6..f2eef51f0d 100644 --- a/internal/mode/static/state/graph/httproute_test.go +++ b/internal/mode/static/state/graph/httproute_test.go @@ -1223,7 +1223,7 @@ func TestValidateFilterRequestHeaderModifier(t *testing.T) { { validator: func() *validationfakes.FakeHTTPFieldsValidator { v := createAllValidValidator() - v.ValidateRequestHeaderNameReturns(errors.New("Invalid header")) + v.ValidateFilterHeaderNameReturns(errors.New("Invalid header")) return v }(), filter: gatewayv1.HTTPRouteFilter{ @@ -1240,7 +1240,7 @@ func TestValidateFilterRequestHeaderModifier(t *testing.T) { { validator: func() *validationfakes.FakeHTTPFieldsValidator { v := createAllValidValidator() - v.ValidateRequestHeaderNameReturns(errors.New("Invalid header")) + v.ValidateFilterHeaderNameReturns(errors.New("Invalid header")) return v }(), filter: gatewayv1.HTTPRouteFilter{ @@ -1255,7 +1255,7 @@ func TestValidateFilterRequestHeaderModifier(t *testing.T) { { validator: func() *validationfakes.FakeHTTPFieldsValidator { v := createAllValidValidator() - v.ValidateRequestHeaderValueReturns(errors.New("Invalid header value")) + v.ValidateFilterHeaderValueReturns(errors.New("Invalid header value")) return v }(), filter: gatewayv1.HTTPRouteFilter{ @@ -1272,8 +1272,8 @@ func TestValidateFilterRequestHeaderModifier(t *testing.T) { { validator: func() *validationfakes.FakeHTTPFieldsValidator { v := createAllValidValidator() - v.ValidateRequestHeaderValueReturns(errors.New("Invalid header value")) - v.ValidateRequestHeaderNameReturns(errors.New("Invalid header")) + v.ValidateFilterHeaderValueReturns(errors.New("Invalid header value")) + v.ValidateFilterHeaderNameReturns(errors.New("Invalid header")) return v }(), filter: gatewayv1.HTTPRouteFilter{ diff --git a/internal/mode/static/state/validation/validationfakes/fake_httpfields_validator.go b/internal/mode/static/state/validation/validationfakes/fake_httpfields_validator.go index 05b4c620b6..64d9b09349 100644 --- a/internal/mode/static/state/validation/validationfakes/fake_httpfields_validator.go +++ b/internal/mode/static/state/validation/validationfakes/fake_httpfields_validator.go @@ -8,6 +8,28 @@ import ( ) type FakeHTTPFieldsValidator struct { + ValidateFilterHeaderNameStub func(string) error + validateFilterHeaderNameMutex sync.RWMutex + validateFilterHeaderNameArgsForCall []struct { + arg1 string + } + validateFilterHeaderNameReturns struct { + result1 error + } + validateFilterHeaderNameReturnsOnCall map[int]struct { + result1 error + } + ValidateFilterHeaderValueStub func(string) error + validateFilterHeaderValueMutex sync.RWMutex + validateFilterHeaderValueArgsForCall []struct { + arg1 string + } + validateFilterHeaderValueReturns struct { + result1 error + } + validateFilterHeaderValueReturnsOnCall map[int]struct { + result1 error + } ValidateHeaderNameInMatchStub func(string) error validateHeaderNameInMatchMutex sync.RWMutex validateHeaderNameInMatchArgsForCall []struct { @@ -124,41 +146,141 @@ type FakeHTTPFieldsValidator struct { result1 bool result2 []string } - ValidateRequestHeaderNameStub func(string) error - validateRequestHeaderNameMutex sync.RWMutex - validateRequestHeaderNameArgsForCall []struct { + ValidateRewritePathStub func(string) error + validateRewritePathMutex sync.RWMutex + validateRewritePathArgsForCall []struct { arg1 string } - validateRequestHeaderNameReturns struct { + validateRewritePathReturns struct { result1 error } - validateRequestHeaderNameReturnsOnCall map[int]struct { + validateRewritePathReturnsOnCall map[int]struct { result1 error } - ValidateRequestHeaderValueStub func(string) error - validateRequestHeaderValueMutex sync.RWMutex - validateRequestHeaderValueArgsForCall []struct { + invocations map[string][][]interface{} + invocationsMutex sync.RWMutex +} + +func (fake *FakeHTTPFieldsValidator) ValidateFilterHeaderName(arg1 string) error { + fake.validateFilterHeaderNameMutex.Lock() + ret, specificReturn := fake.validateFilterHeaderNameReturnsOnCall[len(fake.validateFilterHeaderNameArgsForCall)] + fake.validateFilterHeaderNameArgsForCall = append(fake.validateFilterHeaderNameArgsForCall, struct { arg1 string + }{arg1}) + stub := fake.ValidateFilterHeaderNameStub + fakeReturns := fake.validateFilterHeaderNameReturns + fake.recordInvocation("ValidateFilterHeaderName", []interface{}{arg1}) + fake.validateFilterHeaderNameMutex.Unlock() + if stub != nil { + return stub(arg1) } - validateRequestHeaderValueReturns struct { - result1 error + if specificReturn { + return ret.result1 } - validateRequestHeaderValueReturnsOnCall map[int]struct { + return fakeReturns.result1 +} + +func (fake *FakeHTTPFieldsValidator) ValidateFilterHeaderNameCallCount() int { + fake.validateFilterHeaderNameMutex.RLock() + defer fake.validateFilterHeaderNameMutex.RUnlock() + return len(fake.validateFilterHeaderNameArgsForCall) +} + +func (fake *FakeHTTPFieldsValidator) ValidateFilterHeaderNameCalls(stub func(string) error) { + fake.validateFilterHeaderNameMutex.Lock() + defer fake.validateFilterHeaderNameMutex.Unlock() + fake.ValidateFilterHeaderNameStub = stub +} + +func (fake *FakeHTTPFieldsValidator) ValidateFilterHeaderNameArgsForCall(i int) string { + fake.validateFilterHeaderNameMutex.RLock() + defer fake.validateFilterHeaderNameMutex.RUnlock() + argsForCall := fake.validateFilterHeaderNameArgsForCall[i] + return argsForCall.arg1 +} + +func (fake *FakeHTTPFieldsValidator) ValidateFilterHeaderNameReturns(result1 error) { + fake.validateFilterHeaderNameMutex.Lock() + defer fake.validateFilterHeaderNameMutex.Unlock() + fake.ValidateFilterHeaderNameStub = nil + fake.validateFilterHeaderNameReturns = struct { result1 error + }{result1} +} + +func (fake *FakeHTTPFieldsValidator) ValidateFilterHeaderNameReturnsOnCall(i int, result1 error) { + fake.validateFilterHeaderNameMutex.Lock() + defer fake.validateFilterHeaderNameMutex.Unlock() + fake.ValidateFilterHeaderNameStub = nil + if fake.validateFilterHeaderNameReturnsOnCall == nil { + fake.validateFilterHeaderNameReturnsOnCall = make(map[int]struct { + result1 error + }) } - ValidateRewritePathStub func(string) error - validateRewritePathMutex sync.RWMutex - validateRewritePathArgsForCall []struct { + fake.validateFilterHeaderNameReturnsOnCall[i] = struct { + result1 error + }{result1} +} + +func (fake *FakeHTTPFieldsValidator) ValidateFilterHeaderValue(arg1 string) error { + fake.validateFilterHeaderValueMutex.Lock() + ret, specificReturn := fake.validateFilterHeaderValueReturnsOnCall[len(fake.validateFilterHeaderValueArgsForCall)] + fake.validateFilterHeaderValueArgsForCall = append(fake.validateFilterHeaderValueArgsForCall, struct { arg1 string + }{arg1}) + stub := fake.ValidateFilterHeaderValueStub + fakeReturns := fake.validateFilterHeaderValueReturns + fake.recordInvocation("ValidateFilterHeaderValue", []interface{}{arg1}) + fake.validateFilterHeaderValueMutex.Unlock() + if stub != nil { + return stub(arg1) } - validateRewritePathReturns struct { - result1 error + if specificReturn { + return ret.result1 } - validateRewritePathReturnsOnCall map[int]struct { + return fakeReturns.result1 +} + +func (fake *FakeHTTPFieldsValidator) ValidateFilterHeaderValueCallCount() int { + fake.validateFilterHeaderValueMutex.RLock() + defer fake.validateFilterHeaderValueMutex.RUnlock() + return len(fake.validateFilterHeaderValueArgsForCall) +} + +func (fake *FakeHTTPFieldsValidator) ValidateFilterHeaderValueCalls(stub func(string) error) { + fake.validateFilterHeaderValueMutex.Lock() + defer fake.validateFilterHeaderValueMutex.Unlock() + fake.ValidateFilterHeaderValueStub = stub +} + +func (fake *FakeHTTPFieldsValidator) ValidateFilterHeaderValueArgsForCall(i int) string { + fake.validateFilterHeaderValueMutex.RLock() + defer fake.validateFilterHeaderValueMutex.RUnlock() + argsForCall := fake.validateFilterHeaderValueArgsForCall[i] + return argsForCall.arg1 +} + +func (fake *FakeHTTPFieldsValidator) ValidateFilterHeaderValueReturns(result1 error) { + fake.validateFilterHeaderValueMutex.Lock() + defer fake.validateFilterHeaderValueMutex.Unlock() + fake.ValidateFilterHeaderValueStub = nil + fake.validateFilterHeaderValueReturns = struct { result1 error + }{result1} +} + +func (fake *FakeHTTPFieldsValidator) ValidateFilterHeaderValueReturnsOnCall(i int, result1 error) { + fake.validateFilterHeaderValueMutex.Lock() + defer fake.validateFilterHeaderValueMutex.Unlock() + fake.ValidateFilterHeaderValueStub = nil + if fake.validateFilterHeaderValueReturnsOnCall == nil { + fake.validateFilterHeaderValueReturnsOnCall = make(map[int]struct { + result1 error + }) } - invocations map[string][][]interface{} - invocationsMutex sync.RWMutex + fake.validateFilterHeaderValueReturnsOnCall[i] = struct { + result1 error + }{result1} } func (fake *FakeHTTPFieldsValidator) ValidateHeaderNameInMatch(arg1 string) error { @@ -780,128 +902,6 @@ func (fake *FakeHTTPFieldsValidator) ValidateRedirectStatusCodeReturnsOnCall(i i }{result1, result2} } -func (fake *FakeHTTPFieldsValidator) ValidateRequestHeaderName(arg1 string) error { - fake.validateRequestHeaderNameMutex.Lock() - ret, specificReturn := fake.validateRequestHeaderNameReturnsOnCall[len(fake.validateRequestHeaderNameArgsForCall)] - fake.validateRequestHeaderNameArgsForCall = append(fake.validateRequestHeaderNameArgsForCall, struct { - arg1 string - }{arg1}) - stub := fake.ValidateRequestHeaderNameStub - fakeReturns := fake.validateRequestHeaderNameReturns - fake.recordInvocation("ValidateRequestHeaderName", []interface{}{arg1}) - fake.validateRequestHeaderNameMutex.Unlock() - if stub != nil { - return stub(arg1) - } - if specificReturn { - return ret.result1 - } - return fakeReturns.result1 -} - -func (fake *FakeHTTPFieldsValidator) ValidateRequestHeaderNameCallCount() int { - fake.validateRequestHeaderNameMutex.RLock() - defer fake.validateRequestHeaderNameMutex.RUnlock() - return len(fake.validateRequestHeaderNameArgsForCall) -} - -func (fake *FakeHTTPFieldsValidator) ValidateRequestHeaderNameCalls(stub func(string) error) { - fake.validateRequestHeaderNameMutex.Lock() - defer fake.validateRequestHeaderNameMutex.Unlock() - fake.ValidateRequestHeaderNameStub = stub -} - -func (fake *FakeHTTPFieldsValidator) ValidateRequestHeaderNameArgsForCall(i int) string { - fake.validateRequestHeaderNameMutex.RLock() - defer fake.validateRequestHeaderNameMutex.RUnlock() - argsForCall := fake.validateRequestHeaderNameArgsForCall[i] - return argsForCall.arg1 -} - -func (fake *FakeHTTPFieldsValidator) ValidateRequestHeaderNameReturns(result1 error) { - fake.validateRequestHeaderNameMutex.Lock() - defer fake.validateRequestHeaderNameMutex.Unlock() - fake.ValidateRequestHeaderNameStub = nil - fake.validateRequestHeaderNameReturns = struct { - result1 error - }{result1} -} - -func (fake *FakeHTTPFieldsValidator) ValidateRequestHeaderNameReturnsOnCall(i int, result1 error) { - fake.validateRequestHeaderNameMutex.Lock() - defer fake.validateRequestHeaderNameMutex.Unlock() - fake.ValidateRequestHeaderNameStub = nil - if fake.validateRequestHeaderNameReturnsOnCall == nil { - fake.validateRequestHeaderNameReturnsOnCall = make(map[int]struct { - result1 error - }) - } - fake.validateRequestHeaderNameReturnsOnCall[i] = struct { - result1 error - }{result1} -} - -func (fake *FakeHTTPFieldsValidator) ValidateRequestHeaderValue(arg1 string) error { - fake.validateRequestHeaderValueMutex.Lock() - ret, specificReturn := fake.validateRequestHeaderValueReturnsOnCall[len(fake.validateRequestHeaderValueArgsForCall)] - fake.validateRequestHeaderValueArgsForCall = append(fake.validateRequestHeaderValueArgsForCall, struct { - arg1 string - }{arg1}) - stub := fake.ValidateRequestHeaderValueStub - fakeReturns := fake.validateRequestHeaderValueReturns - fake.recordInvocation("ValidateRequestHeaderValue", []interface{}{arg1}) - fake.validateRequestHeaderValueMutex.Unlock() - if stub != nil { - return stub(arg1) - } - if specificReturn { - return ret.result1 - } - return fakeReturns.result1 -} - -func (fake *FakeHTTPFieldsValidator) ValidateRequestHeaderValueCallCount() int { - fake.validateRequestHeaderValueMutex.RLock() - defer fake.validateRequestHeaderValueMutex.RUnlock() - return len(fake.validateRequestHeaderValueArgsForCall) -} - -func (fake *FakeHTTPFieldsValidator) ValidateRequestHeaderValueCalls(stub func(string) error) { - fake.validateRequestHeaderValueMutex.Lock() - defer fake.validateRequestHeaderValueMutex.Unlock() - fake.ValidateRequestHeaderValueStub = stub -} - -func (fake *FakeHTTPFieldsValidator) ValidateRequestHeaderValueArgsForCall(i int) string { - fake.validateRequestHeaderValueMutex.RLock() - defer fake.validateRequestHeaderValueMutex.RUnlock() - argsForCall := fake.validateRequestHeaderValueArgsForCall[i] - return argsForCall.arg1 -} - -func (fake *FakeHTTPFieldsValidator) ValidateRequestHeaderValueReturns(result1 error) { - fake.validateRequestHeaderValueMutex.Lock() - defer fake.validateRequestHeaderValueMutex.Unlock() - fake.ValidateRequestHeaderValueStub = nil - fake.validateRequestHeaderValueReturns = struct { - result1 error - }{result1} -} - -func (fake *FakeHTTPFieldsValidator) ValidateRequestHeaderValueReturnsOnCall(i int, result1 error) { - fake.validateRequestHeaderValueMutex.Lock() - defer fake.validateRequestHeaderValueMutex.Unlock() - fake.ValidateRequestHeaderValueStub = nil - if fake.validateRequestHeaderValueReturnsOnCall == nil { - fake.validateRequestHeaderValueReturnsOnCall = make(map[int]struct { - result1 error - }) - } - fake.validateRequestHeaderValueReturnsOnCall[i] = struct { - result1 error - }{result1} -} - func (fake *FakeHTTPFieldsValidator) ValidateRewritePath(arg1 string) error { fake.validateRewritePathMutex.Lock() ret, specificReturn := fake.validateRewritePathReturnsOnCall[len(fake.validateRewritePathArgsForCall)] @@ -966,6 +966,10 @@ func (fake *FakeHTTPFieldsValidator) ValidateRewritePathReturnsOnCall(i int, res func (fake *FakeHTTPFieldsValidator) Invocations() map[string][][]interface{} { fake.invocationsMutex.RLock() defer fake.invocationsMutex.RUnlock() + fake.validateFilterHeaderNameMutex.RLock() + defer fake.validateFilterHeaderNameMutex.RUnlock() + fake.validateFilterHeaderValueMutex.RLock() + defer fake.validateFilterHeaderValueMutex.RUnlock() fake.validateHeaderNameInMatchMutex.RLock() defer fake.validateHeaderNameInMatchMutex.RUnlock() fake.validateHeaderValueInMatchMutex.RLock() @@ -986,10 +990,6 @@ func (fake *FakeHTTPFieldsValidator) Invocations() map[string][][]interface{} { defer fake.validateRedirectSchemeMutex.RUnlock() fake.validateRedirectStatusCodeMutex.RLock() defer fake.validateRedirectStatusCodeMutex.RUnlock() - fake.validateRequestHeaderNameMutex.RLock() - defer fake.validateRequestHeaderNameMutex.RUnlock() - fake.validateRequestHeaderValueMutex.RLock() - defer fake.validateRequestHeaderValueMutex.RUnlock() fake.validateRewritePathMutex.RLock() defer fake.validateRewritePathMutex.RUnlock() copiedInvocations := map[string][][]interface{}{} diff --git a/internal/mode/static/state/validation/validator.go b/internal/mode/static/state/validation/validator.go index 52e20bb47f..3bc5847004 100644 --- a/internal/mode/static/state/validation/validator.go +++ b/internal/mode/static/state/validation/validator.go @@ -25,8 +25,8 @@ type HTTPFieldsValidator interface { ValidateRedirectStatusCode(statusCode int) (valid bool, supportedValues []string) ValidateHostname(hostname string) error ValidateRewritePath(path string) error - ValidateRequestHeaderName(name string) error - ValidateRequestHeaderValue(value string) error + ValidateFilterHeaderName(name string) error + ValidateFilterHeaderValue(value string) error } // GenericValidator validates any generic values from NGF API resources from the perspective of a data-plane. From ac6281ccf618aa5c3e99009d510d8da24cd47492 Mon Sep 17 00:00:00 2001 From: kaihsun Date: Mon, 22 Jan 2024 01:40:44 +0000 Subject: [PATCH 04/72] Rename AddHeaders to AddHeaderDirectives --- .../mode/static/nginx/config/http/config.go | 19 +- internal/mode/static/nginx/config/servers.go | 7 +- .../mode/static/nginx/config/servers_test.go | 186 +++++++++--------- 3 files changed, 107 insertions(+), 105 deletions(-) diff --git a/internal/mode/static/nginx/config/http/config.go b/internal/mode/static/nginx/config/http/config.go index d04b8e0811..168373e519 100644 --- a/internal/mode/static/nginx/config/http/config.go +++ b/internal/mode/static/nginx/config/http/config.go @@ -13,15 +13,16 @@ type Server struct { // Location holds all configuration for an HTTP location. type Location struct { - Path string - ProxyPass string - HTTPMatchKey string - ProxySetHeaders []Header - ProxySSLVerify *ProxySSLVerify - Return *Return - Rewrites []string - GRPC bool - AddHeaders []Header + Path string + ProxyPass string + HTTPMatchKey string + ProxySetHeaders []Header + ProxySSLVerify *ProxySSLVerify + Return *Return + Rewrites []string + GRPC bool + HTTPMatchVar string + AddHeaderDirectives []Header } // Header defines an HTTP header to be passed to the proxied server. diff --git a/internal/mode/static/nginx/config/servers.go b/internal/mode/static/nginx/config/servers.go index a44cb60336..e08e17dde3 100644 --- a/internal/mode/static/nginx/config/servers.go +++ b/internal/mode/static/nginx/config/servers.go @@ -294,7 +294,8 @@ func updateLocationsForFilters( rewrites := createRewritesValForRewriteFilter(filters.RequestURLRewrite, path) proxySetHeaders := generateProxySetHeaders(&matchRule.Filters, grpc) - responseAddHeaders := generateAddHeaders(&matchRule.Filters) + addHeaderDirectives := generateAddHeaderDirectives(&matchRule.Filters) + for i := range buildLocations { if rewrites != nil { if rewrites.Rewrite != "" { @@ -309,7 +310,7 @@ func updateLocationsForFilters( generateProtocolString(buildLocations[i].ProxySSLVerify, grpc), grpc, ) - buildLocations[i].AddHeaders = responseAddHeaders + buildLocations[i].AddHeaderDirectives = addHeaderDirectives buildLocations[i].ProxyPass = proxyPass buildLocations[i].GRPC = grpc } @@ -598,7 +599,7 @@ func generateProxySetHeaders(filters *dataplane.HTTPFilters, grpc bool) []http.H return append(proxySetHeaders, headers...) } -func generateAddHeaders(filters *dataplane.HTTPFilters) []http.Header { +func generateAddHeaderDirectives(filters *dataplane.HTTPFilters) []http.Header { headers := make([]http.Header, len(baseHeaders)) copy(headers, baseHeaders) diff --git a/internal/mode/static/nginx/config/servers_test.go b/internal/mode/static/nginx/config/servers_test.go index 3c66a0224c..765c0abc36 100644 --- a/internal/mode/static/nginx/config/servers_test.go +++ b/internal/mode/static/nginx/config/servers_test.go @@ -612,48 +612,48 @@ func TestCreateServers(t *testing.T) { return []http.Location{ { - Path: "@rule0-route0", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: baseHeaders, - AddHeaders: baseHeaders, + Path: "@rule0-route0", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: baseHeaders, + AddHeaderDirectives: baseHeaders, }, { - Path: "@rule0-route1", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: baseHeaders, - AddHeaders: baseHeaders, + Path: "@rule0-route1", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: baseHeaders, + AddHeaderDirectives: baseHeaders, }, { - Path: "@rule0-route2", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: baseHeaders, - AddHeaders: baseHeaders, + Path: "@rule0-route2", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: baseHeaders, + AddHeaderDirectives: baseHeaders, }, { Path: "/", HTTPMatchKey: ssl + "1_0", }, { - Path: "@rule1-route0", - ProxyPass: "http://$test__route1_rule1$request_uri", - ProxySetHeaders: baseHeaders, - AddHeaders: baseHeaders, + Path: "@rule1-route0", + ProxyPass: "http://$test__route1_rule1$request_uri", + ProxySetHeaders: baseHeaders, + AddHeaderDirectives: baseHeaders, }, { Path: "/test/", HTTPMatchKey: ssl + "1_1", }, { - Path: "/path-only/", - ProxyPass: "http://invalid-backend-ref$request_uri", - ProxySetHeaders: baseHeaders, - AddHeaders: baseHeaders, + Path: "/path-only/", + ProxyPass: "http://invalid-backend-ref$request_uri", + ProxySetHeaders: baseHeaders, + AddHeaderDirectives: baseHeaders, }, { - Path: "= /path-only", - ProxyPass: "http://invalid-backend-ref$request_uri", - ProxySetHeaders: baseHeaders, - AddHeaders: baseHeaders, + Path: "= /path-only", + ProxyPass: "http://invalid-backend-ref$request_uri", + ProxySetHeaders: baseHeaders, + AddHeaderDirectives: baseHeaders, }, { Path: "/backend-tls-policy/", @@ -717,25 +717,25 @@ func TestCreateServers(t *testing.T) { HTTPMatchKey: ssl + "1_6", }, { - Path: "/rewrite/", - Rewrites: []string{"^ /replacement break"}, - ProxyPass: "http://test_foo_80", - ProxySetHeaders: rewriteProxySetHeaders, - AddHeaders: baseHeaders, + Path: "/rewrite/", + Rewrites: []string{"^ /replacement break"}, + ProxyPass: "http://test_foo_80", + ProxySetHeaders: rewriteProxySetHeaders, + AddHeaderDirectives: baseHeaders, }, { - Path: "= /rewrite", - Rewrites: []string{"^ /replacement break"}, - ProxyPass: "http://test_foo_80", - ProxySetHeaders: rewriteProxySetHeaders, - AddHeaders: baseHeaders, + Path: "= /rewrite", + Rewrites: []string{"^ /replacement break"}, + ProxyPass: "http://test_foo_80", + ProxySetHeaders: rewriteProxySetHeaders, + AddHeaderDirectives: baseHeaders, }, { - Path: "@rule8-route0", - Rewrites: []string{"^/rewrite-with-headers(.*)$ /prefix-replacement$1 break"}, - ProxyPass: "http://test_foo_80", - ProxySetHeaders: rewriteProxySetHeaders, - AddHeaders: baseHeaders, + Path: "@rule7-route0", + Rewrites: []string{"^/rewrite-with-headers(.*)$ /prefix-replacement$1 break"}, + ProxyPass: "http://test_foo_80", + ProxySetHeaders: rewriteProxySetHeaders, + AddHeaderDirectives: baseHeaders, }, { Path: "/rewrite-with-headers/", @@ -772,16 +772,16 @@ func TestCreateServers(t *testing.T) { HTTPMatchKey: ssl + "1_10", }, { - Path: "= /exact", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: baseHeaders, - AddHeaders: baseHeaders, + Path: "= /exact", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: baseHeaders, + AddHeaderDirectives: baseHeaders, }, { - Path: "@rule12-route0", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: baseHeaders, - AddHeaders: baseHeaders, + Path: "@rule11-route0", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: baseHeaders, + AddHeaderDirectives: baseHeaders, }, { Path: "= /test", @@ -812,7 +812,7 @@ func TestCreateServers(t *testing.T) { Value: "$connection_upgrade", }, }, - AddHeaders: baseHeaders, + AddHeaderDirectives: baseHeaders, }, { Path: "= /proxy-set-headers", @@ -839,7 +839,7 @@ func TestCreateServers(t *testing.T) { Value: "$connection_upgrade", }, }, - AddHeaders: baseHeaders, + AddHeaderDirectives: baseHeaders, }, { Path: "= /grpc/method", @@ -973,16 +973,16 @@ func TestCreateServersConflicts(t *testing.T) { }, expLocs: []http.Location{ { - Path: "/coffee/", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: baseHeaders, - AddHeaders: baseHeaders, + Path: "/coffee/", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: baseHeaders, + AddHeaderDirectives: baseHeaders, }, { - Path: "= /coffee", - ProxyPass: "http://test_bar_80$request_uri", - ProxySetHeaders: baseHeaders, - AddHeaders: baseHeaders, + Path: "= /coffee", + ProxyPass: "http://test_bar_80$request_uri", + ProxySetHeaders: baseHeaders, + AddHeaderDirectives: baseHeaders, }, createDefaultRootLocation(), }, @@ -1013,16 +1013,16 @@ func TestCreateServersConflicts(t *testing.T) { }, expLocs: []http.Location{ { - Path: "= /coffee", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: baseHeaders, - AddHeaders: baseHeaders, + Path: "= /coffee", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: baseHeaders, + AddHeaderDirectives: baseHeaders, }, { - Path: "/coffee/", - ProxyPass: "http://test_bar_80$request_uri", - ProxySetHeaders: baseHeaders, - AddHeaders: baseHeaders, + Path: "/coffee/", + ProxyPass: "http://test_bar_80$request_uri", + ProxySetHeaders: baseHeaders, + AddHeaderDirectives: baseHeaders, }, createDefaultRootLocation(), }, @@ -1063,16 +1063,16 @@ func TestCreateServersConflicts(t *testing.T) { }, expLocs: []http.Location{ { - Path: "/coffee/", - ProxyPass: "http://test_bar_80$request_uri", - ProxySetHeaders: baseHeaders, - AddHeaders: baseHeaders, + Path: "/coffee/", + ProxyPass: "http://test_bar_80$request_uri", + ProxySetHeaders: baseHeaders, + AddHeaderDirectives: baseHeaders, }, { - Path: "= /coffee", - ProxyPass: "http://test_baz_80$request_uri", - ProxySetHeaders: baseHeaders, - AddHeaders: baseHeaders, + Path: "= /coffee", + ProxyPass: "http://test_baz_80$request_uri", + ProxySetHeaders: baseHeaders, + AddHeaderDirectives: baseHeaders, }, createDefaultRootLocation(), }, @@ -1188,16 +1188,16 @@ func TestCreateLocationsRootPath(t *testing.T) { pathRules: getPathRules(false /* rootPath */, false /* grpc */), expLocations: []http.Location{ { - Path: "/path-1", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: baseHeaders, - AddHeaders: baseHeaders, + Path: "/path-1", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: baseHeaders, + AddHeaderDirectives: baseHeaders, }, { - Path: "/path-2", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: baseHeaders, - AddHeaders: baseHeaders, + Path: "/path-2", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: baseHeaders, + AddHeaderDirectives: baseHeaders, }, { Path: "/", @@ -1240,22 +1240,22 @@ func TestCreateLocationsRootPath(t *testing.T) { pathRules: getPathRules(true /* rootPath */, false /* grpc */), expLocations: []http.Location{ { - Path: "/path-1", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: baseHeaders, - AddHeaders: baseHeaders, + Path: "/path-1", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: baseHeaders, + AddHeaderDirectives: baseHeaders, }, { - Path: "/path-2", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: baseHeaders, - AddHeaders: baseHeaders, + Path: "/path-2", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: baseHeaders, + AddHeaderDirectives: baseHeaders, }, { - Path: "/", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: baseHeaders, - AddHeaders: baseHeaders, + Path: "/", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: baseHeaders, + AddHeaderDirectives: baseHeaders, }, }, }, From 51b823801e89271318f80f307ba6d8707f866931 Mon Sep 17 00:00:00 2001 From: kaihsun Date: Mon, 22 Jan 2024 07:06:53 +0000 Subject: [PATCH 05/72] Update servers_template.go --- internal/mode/static/nginx/config/servers_template.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/mode/static/nginx/config/servers_template.go b/internal/mode/static/nginx/config/servers_template.go index 5b4ce443e4..285e14443e 100644 --- a/internal/mode/static/nginx/config/servers_template.go +++ b/internal/mode/static/nginx/config/servers_template.go @@ -58,7 +58,7 @@ server { {{ $proxyOrGRPC }}_set_header {{ $h.Name }} "{{ $h.Value }}"; {{- end }} {{ $proxyOrGRPC }}_pass {{ $l.ProxyPass }}; - {{ range $h := $l.AddHeaders }} + {{ range $h := $l.AddHeaderDirectives }} add_header {{ $h.Name }} "{{ $h.Value }}"; {{- end }} proxy_http_version 1.1; From f247e7baecebbc3e965cf48c3b7af010b0eeb5a8 Mon Sep 17 00:00:00 2001 From: kaihsun Date: Sun, 4 Feb 2024 06:53:47 +0000 Subject: [PATCH 06/72] Pass conformance tests --- .../mode/static/nginx/config/http/config.go | 22 +- internal/mode/static/nginx/config/servers.go | 46 ++-- .../static/nginx/config/servers_template.go | 11 +- .../mode/static/nginx/config/servers_test.go | 234 +++++++++++------- 4 files changed, 193 insertions(+), 120 deletions(-) diff --git a/internal/mode/static/nginx/config/http/config.go b/internal/mode/static/nginx/config/http/config.go index 168373e519..5c64b06959 100644 --- a/internal/mode/static/nginx/config/http/config.go +++ b/internal/mode/static/nginx/config/http/config.go @@ -13,16 +13,18 @@ type Server struct { // Location holds all configuration for an HTTP location. type Location struct { - Path string - ProxyPass string - HTTPMatchKey string - ProxySetHeaders []Header - ProxySSLVerify *ProxySSLVerify - Return *Return - Rewrites []string - GRPC bool - HTTPMatchVar string - AddHeaderDirectives []Header + Path string + ProxyPass string + HTTPMatchKey string + ProxySetHeaders []Header + ProxySSLVerify *ProxySSLVerify + Return *Return + Rewrites []string + GRPC bool + HTTPMatchVar string + AddResponseHeaders []Header + SetResponseHeaders []Header + RemoveResponseHeaders []string } // Header defines an HTTP header to be passed to the proxied server. diff --git a/internal/mode/static/nginx/config/servers.go b/internal/mode/static/nginx/config/servers.go index e08e17dde3..760dac8790 100644 --- a/internal/mode/static/nginx/config/servers.go +++ b/internal/mode/static/nginx/config/servers.go @@ -294,8 +294,9 @@ func updateLocationsForFilters( rewrites := createRewritesValForRewriteFilter(filters.RequestURLRewrite, path) proxySetHeaders := generateProxySetHeaders(&matchRule.Filters, grpc) - addHeaderDirectives := generateAddHeaderDirectives(&matchRule.Filters) - + addResponseHeaders := generateAddResponseHeaders(&matchRule.Filters) + setResponseHeaders := generateSetResponseHeaders(&matchRule.Filters) + removeResponseHeaders := generateRemoveResponseHeaders(&matchRule.Filters) for i := range buildLocations { if rewrites != nil { if rewrites.Rewrite != "" { @@ -310,7 +311,9 @@ func updateLocationsForFilters( generateProtocolString(buildLocations[i].ProxySSLVerify, grpc), grpc, ) - buildLocations[i].AddHeaderDirectives = addHeaderDirectives + buildLocations[i].AddResponseHeaders = addResponseHeaders + buildLocations[i].SetResponseHeaders = setResponseHeaders + buildLocations[i].RemoveResponseHeaders = removeResponseHeaders buildLocations[i].ProxyPass = proxyPass buildLocations[i].GRPC = grpc } @@ -599,35 +602,48 @@ func generateProxySetHeaders(filters *dataplane.HTTPFilters, grpc bool) []http.H return append(proxySetHeaders, headers...) } -func generateAddHeaderDirectives(filters *dataplane.HTTPFilters) []http.Header { +func generateAddResponseHeaders(filters *dataplane.HTTPFilters) []http.Header { headers := make([]http.Header, len(baseHeaders)) copy(headers, baseHeaders) if filters == nil || filters.ResponseHeaderModifiers == nil { return headers } - headerFilter := filters.ResponseHeaderModifiers - headerLen := len(headerFilter.Add) + len(headerFilter.Set) + len(headerFilter.Remove) + len(headers) + headerLen := len(headerFilter.Add) + len(headers) responseAddHeaders := make([]http.Header, 0, headerLen) if len(headerFilter.Add) > 0 { addHeaders := convertAddHeaders(headerFilter.Add) responseAddHeaders = append(responseAddHeaders, addHeaders...) } + return append(responseAddHeaders, headers...) +} + +func generateSetResponseHeaders(filters *dataplane.HTTPFilters) []http.Header { + if filters == nil || filters.ResponseHeaderModifiers == nil { + return []http.Header{} + } + headerFilter := filters.ResponseHeaderModifiers + headerLen := len(headerFilter.Set) + responseSetHeaders := make([]http.Header, 0, headerLen) if len(headerFilter.Set) > 0 { setHeaders := convertSetHeaders(headerFilter.Set) - responseAddHeaders = append(responseAddHeaders, setHeaders...) - } - // If the value of a header field is an empty string then this field will not be passed to a proxied server - for _, h := range headerFilter.Remove { - responseAddHeaders = append(responseAddHeaders, http.Header{ - Name: h, - Value: "", - }) + responseSetHeaders = append(responseSetHeaders, setHeaders...) } + return responseSetHeaders +} - return append(responseAddHeaders, headers...) +func generateRemoveResponseHeaders(filters *dataplane.HTTPFilters) []string { + if filters == nil || filters.ResponseHeaderModifiers == nil { + return []string{} + } + removeHeaders := filters.ResponseHeaderModifiers.Remove + headerLen := len(removeHeaders) + responseRemoveHeaders := make([]string, headerLen) + copy(responseRemoveHeaders, removeHeaders) + fmt.Printf("removeHeaders: %v; responseRemoveHeaders: %v\n", removeHeaders, responseRemoveHeaders) + return responseRemoveHeaders } func convertAddHeaders(headers []dataplane.HTTPHeader) []http.Header { diff --git a/internal/mode/static/nginx/config/servers_template.go b/internal/mode/static/nginx/config/servers_template.go index 285e14443e..6e4d035e52 100644 --- a/internal/mode/static/nginx/config/servers_template.go +++ b/internal/mode/static/nginx/config/servers_template.go @@ -58,8 +58,15 @@ server { {{ $proxyOrGRPC }}_set_header {{ $h.Name }} "{{ $h.Value }}"; {{- end }} {{ $proxyOrGRPC }}_pass {{ $l.ProxyPass }}; - {{ range $h := $l.AddHeaderDirectives }} - add_header {{ $h.Name }} "{{ $h.Value }}"; + {{ range $h := $l.AddResponseHeaders }} + add_header {{ $h.Name }} "{{ $h.Value }}" always; + {{- end }} + {{ range $h := $l.SetResponseHeaders }} + proxy_hide_header {{ $h.Name }}; + add_header {{ $h.Name }} "{{ $h.Value }}" always; + {{- end }} + {{ range $h := $l.RemoveResponseHeaders }} + proxy_hide_header {{ $h }}; {{- end }} proxy_http_version 1.1; {{- if $l.ProxySSLVerify }} diff --git a/internal/mode/static/nginx/config/servers_test.go b/internal/mode/static/nginx/config/servers_test.go index 765c0abc36..1c9200ba51 100644 --- a/internal/mode/static/nginx/config/servers_test.go +++ b/internal/mode/static/nginx/config/servers_test.go @@ -612,48 +612,60 @@ func TestCreateServers(t *testing.T) { return []http.Location{ { - Path: "@rule0-route0", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: baseHeaders, - AddHeaderDirectives: baseHeaders, + Path: "@rule0-route0", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: baseHeaders, + AddResponseHeaders: baseHeaders, + SetResponseHeaders: []http.Header{}, + RemoveResponseHeaders: []string{}, }, { - Path: "@rule0-route1", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: baseHeaders, - AddHeaderDirectives: baseHeaders, + Path: "@rule0-route1", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: baseHeaders, + AddResponseHeaders: baseHeaders, + SetResponseHeaders: []http.Header{}, + RemoveResponseHeaders: []string{}, }, { - Path: "@rule0-route2", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: baseHeaders, - AddHeaderDirectives: baseHeaders, + Path: "@rule0-route2", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: baseHeaders, + AddResponseHeaders: baseHeaders, + SetResponseHeaders: []http.Header{}, + RemoveResponseHeaders: []string{}, }, { Path: "/", HTTPMatchKey: ssl + "1_0", }, { - Path: "@rule1-route0", - ProxyPass: "http://$test__route1_rule1$request_uri", - ProxySetHeaders: baseHeaders, - AddHeaderDirectives: baseHeaders, + Path: "@rule1-route0", + ProxyPass: "http://$test__route1_rule1$request_uri", + ProxySetHeaders: baseHeaders, + AddResponseHeaders: baseHeaders, + SetResponseHeaders: []http.Header{}, + RemoveResponseHeaders: []string{}, }, { Path: "/test/", HTTPMatchKey: ssl + "1_1", }, { - Path: "/path-only/", - ProxyPass: "http://invalid-backend-ref$request_uri", - ProxySetHeaders: baseHeaders, - AddHeaderDirectives: baseHeaders, + Path: "/path-only/", + ProxyPass: "http://invalid-backend-ref$request_uri", + ProxySetHeaders: baseHeaders, + AddResponseHeaders: baseHeaders, + SetResponseHeaders: []http.Header{}, + RemoveResponseHeaders: []string{}, }, { - Path: "= /path-only", - ProxyPass: "http://invalid-backend-ref$request_uri", - ProxySetHeaders: baseHeaders, - AddHeaderDirectives: baseHeaders, + Path: "= /path-only", + ProxyPass: "http://invalid-backend-ref$request_uri", + ProxySetHeaders: baseHeaders, + AddResponseHeaders: baseHeaders, + SetResponseHeaders: []http.Header{}, + RemoveResponseHeaders: []string{}, }, { Path: "/backend-tls-policy/", @@ -717,25 +729,31 @@ func TestCreateServers(t *testing.T) { HTTPMatchKey: ssl + "1_6", }, { - Path: "/rewrite/", - Rewrites: []string{"^ /replacement break"}, - ProxyPass: "http://test_foo_80", - ProxySetHeaders: rewriteProxySetHeaders, - AddHeaderDirectives: baseHeaders, + Path: "/rewrite/", + Rewrites: []string{"^ /replacement break"}, + ProxyPass: "http://test_foo_80", + ProxySetHeaders: rewriteProxySetHeaders, + AddResponseHeaders: baseHeaders, + SetResponseHeaders: []http.Header{}, + RemoveResponseHeaders: []string{}, }, { - Path: "= /rewrite", - Rewrites: []string{"^ /replacement break"}, - ProxyPass: "http://test_foo_80", - ProxySetHeaders: rewriteProxySetHeaders, - AddHeaderDirectives: baseHeaders, + Path: "= /rewrite", + Rewrites: []string{"^ /replacement break"}, + ProxyPass: "http://test_foo_80", + ProxySetHeaders: rewriteProxySetHeaders, + AddResponseHeaders: baseHeaders, + SetResponseHeaders: []http.Header{}, + RemoveResponseHeaders: []string{}, }, { - Path: "@rule7-route0", - Rewrites: []string{"^/rewrite-with-headers(.*)$ /prefix-replacement$1 break"}, - ProxyPass: "http://test_foo_80", - ProxySetHeaders: rewriteProxySetHeaders, - AddHeaderDirectives: baseHeaders, + Path: "@rule7-route0", + Rewrites: []string{"^/rewrite-with-headers(.*)$ /prefix-replacement$1 break"}, + ProxyPass: "http://test_foo_80", + ProxySetHeaders: rewriteProxySetHeaders, + AddResponseHeaders: baseHeaders, + SetResponseHeaders: []http.Header{}, + RemoveResponseHeaders: []string{}, }, { Path: "/rewrite-with-headers/", @@ -772,16 +790,20 @@ func TestCreateServers(t *testing.T) { HTTPMatchKey: ssl + "1_10", }, { - Path: "= /exact", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: baseHeaders, - AddHeaderDirectives: baseHeaders, + Path: "= /exact", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: baseHeaders, + AddResponseHeaders: baseHeaders, + SetResponseHeaders: []http.Header{}, + RemoveResponseHeaders: []string{}, }, { - Path: "@rule11-route0", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: baseHeaders, - AddHeaderDirectives: baseHeaders, + Path: "@rule11-route0", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: baseHeaders, + AddResponseHeaders: baseHeaders, + SetResponseHeaders: []http.Header{}, + RemoveResponseHeaders: []string{}, }, { Path: "= /test", @@ -812,7 +834,9 @@ func TestCreateServers(t *testing.T) { Value: "$connection_upgrade", }, }, - AddHeaderDirectives: baseHeaders, + AddResponseHeaders: baseHeaders, + SetResponseHeaders: []http.Header{}, + RemoveResponseHeaders: []string{}, }, { Path: "= /proxy-set-headers", @@ -839,7 +863,9 @@ func TestCreateServers(t *testing.T) { Value: "$connection_upgrade", }, }, - AddHeaderDirectives: baseHeaders, + AddResponseHeaders: baseHeaders, + SetResponseHeaders: []http.Header{}, + RemoveResponseHeaders: []string{}, }, { Path: "= /grpc/method", @@ -973,16 +999,20 @@ func TestCreateServersConflicts(t *testing.T) { }, expLocs: []http.Location{ { - Path: "/coffee/", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: baseHeaders, - AddHeaderDirectives: baseHeaders, + Path: "/coffee/", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: baseHeaders, + AddResponseHeaders: baseHeaders, + SetResponseHeaders: []http.Header{}, + RemoveResponseHeaders: []string{}, }, { - Path: "= /coffee", - ProxyPass: "http://test_bar_80$request_uri", - ProxySetHeaders: baseHeaders, - AddHeaderDirectives: baseHeaders, + Path: "= /coffee", + ProxyPass: "http://test_bar_80$request_uri", + ProxySetHeaders: baseHeaders, + AddResponseHeaders: baseHeaders, + SetResponseHeaders: []http.Header{}, + RemoveResponseHeaders: []string{}, }, createDefaultRootLocation(), }, @@ -1013,16 +1043,20 @@ func TestCreateServersConflicts(t *testing.T) { }, expLocs: []http.Location{ { - Path: "= /coffee", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: baseHeaders, - AddHeaderDirectives: baseHeaders, + Path: "= /coffee", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: baseHeaders, + AddResponseHeaders: baseHeaders, + SetResponseHeaders: []http.Header{}, + RemoveResponseHeaders: []string{}, }, { - Path: "/coffee/", - ProxyPass: "http://test_bar_80$request_uri", - ProxySetHeaders: baseHeaders, - AddHeaderDirectives: baseHeaders, + Path: "/coffee/", + ProxyPass: "http://test_bar_80$request_uri", + ProxySetHeaders: baseHeaders, + AddResponseHeaders: baseHeaders, + SetResponseHeaders: []http.Header{}, + RemoveResponseHeaders: []string{}, }, createDefaultRootLocation(), }, @@ -1063,16 +1097,20 @@ func TestCreateServersConflicts(t *testing.T) { }, expLocs: []http.Location{ { - Path: "/coffee/", - ProxyPass: "http://test_bar_80$request_uri", - ProxySetHeaders: baseHeaders, - AddHeaderDirectives: baseHeaders, + Path: "/coffee/", + ProxyPass: "http://test_bar_80$request_uri", + ProxySetHeaders: baseHeaders, + AddResponseHeaders: baseHeaders, + SetResponseHeaders: []http.Header{}, + RemoveResponseHeaders: []string{}, }, { - Path: "= /coffee", - ProxyPass: "http://test_baz_80$request_uri", - ProxySetHeaders: baseHeaders, - AddHeaderDirectives: baseHeaders, + Path: "= /coffee", + ProxyPass: "http://test_baz_80$request_uri", + ProxySetHeaders: baseHeaders, + AddResponseHeaders: baseHeaders, + SetResponseHeaders: []http.Header{}, + RemoveResponseHeaders: []string{}, }, createDefaultRootLocation(), }, @@ -1188,16 +1226,20 @@ func TestCreateLocationsRootPath(t *testing.T) { pathRules: getPathRules(false /* rootPath */, false /* grpc */), expLocations: []http.Location{ { - Path: "/path-1", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: baseHeaders, - AddHeaderDirectives: baseHeaders, + Path: "/path-1", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: baseHeaders, + AddResponseHeaders: baseHeaders, + SetResponseHeaders: []http.Header{}, + RemoveResponseHeaders: []string{}, }, { - Path: "/path-2", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: baseHeaders, - AddHeaderDirectives: baseHeaders, + Path: "/path-2", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: baseHeaders, + AddResponseHeaders: baseHeaders, + SetResponseHeaders: []http.Header{}, + RemoveResponseHeaders: []string{}, }, { Path: "/", @@ -1240,22 +1282,28 @@ func TestCreateLocationsRootPath(t *testing.T) { pathRules: getPathRules(true /* rootPath */, false /* grpc */), expLocations: []http.Location{ { - Path: "/path-1", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: baseHeaders, - AddHeaderDirectives: baseHeaders, + Path: "/path-1", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: baseHeaders, + AddResponseHeaders: baseHeaders, + SetResponseHeaders: []http.Header{}, + RemoveResponseHeaders: []string{}, }, { - Path: "/path-2", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: baseHeaders, - AddHeaderDirectives: baseHeaders, + Path: "/path-2", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: baseHeaders, + AddResponseHeaders: baseHeaders, + SetResponseHeaders: []http.Header{}, + RemoveResponseHeaders: []string{}, }, { - Path: "/", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: baseHeaders, - AddHeaderDirectives: baseHeaders, + Path: "/", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: baseHeaders, + AddResponseHeaders: baseHeaders, + SetResponseHeaders: []http.Header{}, + RemoveResponseHeaders: []string{}, }, }, }, From 3fc4053602c9f84eeb8fdbdd4afe334ea3f820f7 Mon Sep 17 00:00:00 2001 From: kaihsun Date: Sun, 4 Feb 2024 07:05:15 +0000 Subject: [PATCH 07/72] Remove Map --- internal/mode/static/nginx/config/maps.go | 18 ++++++------------ internal/mode/static/nginx/config/servers.go | 3 ++- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/internal/mode/static/nginx/config/maps.go b/internal/mode/static/nginx/config/maps.go index 01e4f1764e..a4d6c419b6 100644 --- a/internal/mode/static/nginx/config/maps.go +++ b/internal/mode/static/nginx/config/maps.go @@ -21,23 +21,17 @@ func executeMaps(conf dataplane.Configuration) []executeResult { func buildAddHeaderMaps(servers []dataplane.VirtualServer) []http.Map { addHeaderNames := make(map[string]struct{}) - extractAddHeaderNames := func(headerModifiers []dataplane.HTTPHeader) { - for _, addHeader := range headerModifiers { - lowerName := strings.ToLower(addHeader.Name) - if _, ok := addHeaderNames[lowerName]; !ok { - addHeaderNames[lowerName] = struct{}{} - } - } - } for _, s := range servers { for _, pr := range s.PathRules { for _, mr := range pr.MatchRules { if mr.Filters.RequestHeaderModifiers != nil { - extractAddHeaderNames(mr.Filters.RequestHeaderModifiers.Add) - } - if mr.Filters.ResponseHeaderModifiers != nil { - extractAddHeaderNames(mr.Filters.ResponseHeaderModifiers.Add) + for _, addHeader := range mr.Filters.RequestHeaderModifiers.Add { + lowerName := strings.ToLower(addHeader.Name) + if _, ok := addHeaderNames[lowerName]; !ok { + addHeaderNames[lowerName] = struct{}{} + } + } } } } diff --git a/internal/mode/static/nginx/config/servers.go b/internal/mode/static/nginx/config/servers.go index 760dac8790..86750b3bff 100644 --- a/internal/mode/static/nginx/config/servers.go +++ b/internal/mode/static/nginx/config/servers.go @@ -614,7 +614,8 @@ func generateAddResponseHeaders(filters *dataplane.HTTPFilters) []http.Header { headerLen := len(headerFilter.Add) + len(headers) responseAddHeaders := make([]http.Header, 0, headerLen) if len(headerFilter.Add) > 0 { - addHeaders := convertAddHeaders(headerFilter.Add) + // TODO (kevin85421): Should we use a different function? + addHeaders := convertSetHeaders(headerFilter.Add) responseAddHeaders = append(responseAddHeaders, addHeaders...) } return append(responseAddHeaders, headers...) From d23528618cfc97cb5cc08d46c7d05ebb4d18eb2b Mon Sep 17 00:00:00 2001 From: kaihsun Date: Sun, 4 Feb 2024 07:07:26 +0000 Subject: [PATCH 08/72] Remove fmt.Printf --- internal/mode/static/nginx/config/servers.go | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/mode/static/nginx/config/servers.go b/internal/mode/static/nginx/config/servers.go index 86750b3bff..40eacf5ae9 100644 --- a/internal/mode/static/nginx/config/servers.go +++ b/internal/mode/static/nginx/config/servers.go @@ -643,7 +643,6 @@ func generateRemoveResponseHeaders(filters *dataplane.HTTPFilters) []string { headerLen := len(removeHeaders) responseRemoveHeaders := make([]string, headerLen) copy(responseRemoveHeaders, removeHeaders) - fmt.Printf("removeHeaders: %v; responseRemoveHeaders: %v\n", removeHeaders, responseRemoveHeaders) return responseRemoveHeaders } From 712411cfd50922eaa8cdedc4d1a2bc4888bbbb81 Mon Sep 17 00:00:00 2001 From: kaihsun Date: Sun, 4 Feb 2024 07:18:28 +0000 Subject: [PATCH 09/72] Change TODO to FIXME --- internal/mode/static/nginx/config/servers.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/mode/static/nginx/config/servers.go b/internal/mode/static/nginx/config/servers.go index 40eacf5ae9..9d6a072e5f 100644 --- a/internal/mode/static/nginx/config/servers.go +++ b/internal/mode/static/nginx/config/servers.go @@ -614,7 +614,7 @@ func generateAddResponseHeaders(filters *dataplane.HTTPFilters) []http.Header { headerLen := len(headerFilter.Add) + len(headers) responseAddHeaders := make([]http.Header, 0, headerLen) if len(headerFilter.Add) > 0 { - // TODO (kevin85421): Should we use a different function? + // FIXME(kevin85421): Should we use a different function? addHeaders := convertSetHeaders(headerFilter.Add) responseAddHeaders = append(responseAddHeaders, addHeaders...) } From 6559b44fe7b0e6f053dee1b45b769ed1568efb31 Mon Sep 17 00:00:00 2001 From: kaihsun Date: Mon, 19 Feb 2024 21:24:27 +0000 Subject: [PATCH 10/72] Address comments --- internal/mode/static/nginx/config/servers.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/mode/static/nginx/config/servers.go b/internal/mode/static/nginx/config/servers.go index 9d6a072e5f..dd2c8e6d2f 100644 --- a/internal/mode/static/nginx/config/servers.go +++ b/internal/mode/static/nginx/config/servers.go @@ -642,6 +642,8 @@ func generateRemoveResponseHeaders(filters *dataplane.HTTPFilters) []string { removeHeaders := filters.ResponseHeaderModifiers.Remove headerLen := len(removeHeaders) responseRemoveHeaders := make([]string, headerLen) + + // Make a deep copy to prevent the slice from being accidentally modified. copy(responseRemoveHeaders, removeHeaders) return responseRemoveHeaders } From bfe683d65a822e78914d70216b2a0dd07afb35f2 Mon Sep 17 00:00:00 2001 From: kaihsun Date: Mon, 19 Feb 2024 21:31:43 +0000 Subject: [PATCH 11/72] Address comments --- internal/mode/static/nginx/config/servers.go | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/internal/mode/static/nginx/config/servers.go b/internal/mode/static/nginx/config/servers.go index dd2c8e6d2f..6209f589a8 100644 --- a/internal/mode/static/nginx/config/servers.go +++ b/internal/mode/static/nginx/config/servers.go @@ -610,9 +610,7 @@ func generateAddResponseHeaders(filters *dataplane.HTTPFilters) []http.Header { return headers } headerFilter := filters.ResponseHeaderModifiers - - headerLen := len(headerFilter.Add) + len(headers) - responseAddHeaders := make([]http.Header, 0, headerLen) + responseAddHeaders := make([]http.Header, 0, len(headerFilter.Add)+len(headers)) if len(headerFilter.Add) > 0 { // FIXME(kevin85421): Should we use a different function? addHeaders := convertSetHeaders(headerFilter.Add) @@ -626,8 +624,7 @@ func generateSetResponseHeaders(filters *dataplane.HTTPFilters) []http.Header { return []http.Header{} } headerFilter := filters.ResponseHeaderModifiers - headerLen := len(headerFilter.Set) - responseSetHeaders := make([]http.Header, 0, headerLen) + responseSetHeaders := make([]http.Header, 0, len(headerFilter.Set)) if len(headerFilter.Set) > 0 { setHeaders := convertSetHeaders(headerFilter.Set) responseSetHeaders = append(responseSetHeaders, setHeaders...) @@ -640,8 +637,7 @@ func generateRemoveResponseHeaders(filters *dataplane.HTTPFilters) []string { return []string{} } removeHeaders := filters.ResponseHeaderModifiers.Remove - headerLen := len(removeHeaders) - responseRemoveHeaders := make([]string, headerLen) + responseRemoveHeaders := make([]string, len(removeHeaders)) // Make a deep copy to prevent the slice from being accidentally modified. copy(responseRemoveHeaders, removeHeaders) From d37216165e313cf2e13b3227599ef28fd9311f5a Mon Sep 17 00:00:00 2001 From: kaihsun Date: Mon, 19 Feb 2024 22:01:18 +0000 Subject: [PATCH 12/72] Address comments --- .../mode/static/nginx/config/http/config.go | 8 + internal/mode/static/nginx/config/servers.go | 16 +- .../static/nginx/config/servers_template.go | 6 +- .../mode/static/nginx/config/servers_test.go | 330 ++++++++++-------- 4 files changed, 210 insertions(+), 150 deletions(-) diff --git a/internal/mode/static/nginx/config/http/config.go b/internal/mode/static/nginx/config/http/config.go index 5c64b06959..2202f0961a 100644 --- a/internal/mode/static/nginx/config/http/config.go +++ b/internal/mode/static/nginx/config/http/config.go @@ -25,6 +25,7 @@ type Location struct { AddResponseHeaders []Header SetResponseHeaders []Header RemoveResponseHeaders []string + ResponseHeaders ResponseHeaders } // Header defines an HTTP header to be passed to the proxied server. @@ -33,6 +34,13 @@ type Header struct { Value string } +// ResponseHeaders holds all response headers to be added, set, or removed. +type ResponseHeaders struct { + Add []Header + Set []Header + Remove []string +} + // Return represents an HTTP return. type Return struct { Body string diff --git a/internal/mode/static/nginx/config/servers.go b/internal/mode/static/nginx/config/servers.go index 6209f589a8..a17e4d80e7 100644 --- a/internal/mode/static/nginx/config/servers.go +++ b/internal/mode/static/nginx/config/servers.go @@ -294,9 +294,7 @@ func updateLocationsForFilters( rewrites := createRewritesValForRewriteFilter(filters.RequestURLRewrite, path) proxySetHeaders := generateProxySetHeaders(&matchRule.Filters, grpc) - addResponseHeaders := generateAddResponseHeaders(&matchRule.Filters) - setResponseHeaders := generateSetResponseHeaders(&matchRule.Filters) - removeResponseHeaders := generateRemoveResponseHeaders(&matchRule.Filters) + responseHeaders := generateResponseHeaders(&matchRule.Filters) for i := range buildLocations { if rewrites != nil { if rewrites.Rewrite != "" { @@ -311,9 +309,7 @@ func updateLocationsForFilters( generateProtocolString(buildLocations[i].ProxySSLVerify, grpc), grpc, ) - buildLocations[i].AddResponseHeaders = addResponseHeaders - buildLocations[i].SetResponseHeaders = setResponseHeaders - buildLocations[i].RemoveResponseHeaders = removeResponseHeaders + buildLocations[i].ResponseHeaders = responseHeaders buildLocations[i].ProxyPass = proxyPass buildLocations[i].GRPC = grpc } @@ -602,6 +598,14 @@ func generateProxySetHeaders(filters *dataplane.HTTPFilters, grpc bool) []http.H return append(proxySetHeaders, headers...) } +func generateResponseHeaders(filters *dataplane.HTTPFilters) http.ResponseHeaders { + return http.ResponseHeaders{ + Add: generateAddResponseHeaders(filters), + Set: generateSetResponseHeaders(filters), + Remove: generateRemoveResponseHeaders(filters), + } +} + func generateAddResponseHeaders(filters *dataplane.HTTPFilters) []http.Header { headers := make([]http.Header, len(baseHeaders)) copy(headers, baseHeaders) diff --git a/internal/mode/static/nginx/config/servers_template.go b/internal/mode/static/nginx/config/servers_template.go index 6e4d035e52..c0723c000b 100644 --- a/internal/mode/static/nginx/config/servers_template.go +++ b/internal/mode/static/nginx/config/servers_template.go @@ -58,14 +58,14 @@ server { {{ $proxyOrGRPC }}_set_header {{ $h.Name }} "{{ $h.Value }}"; {{- end }} {{ $proxyOrGRPC }}_pass {{ $l.ProxyPass }}; - {{ range $h := $l.AddResponseHeaders }} + {{ range $h := $l.ResponseHeaders.Add }} add_header {{ $h.Name }} "{{ $h.Value }}" always; {{- end }} - {{ range $h := $l.SetResponseHeaders }} + {{ range $h := $l.ResponseHeaders.Set }} proxy_hide_header {{ $h.Name }}; add_header {{ $h.Name }} "{{ $h.Value }}" always; {{- end }} - {{ range $h := $l.RemoveResponseHeaders }} + {{ range $h := $l.ResponseHeaders.Remove }} proxy_hide_header {{ $h }}; {{- end }} proxy_http_version 1.1; diff --git a/internal/mode/static/nginx/config/servers_test.go b/internal/mode/static/nginx/config/servers_test.go index 1c9200ba51..cc0644a52b 100644 --- a/internal/mode/static/nginx/config/servers_test.go +++ b/internal/mode/static/nginx/config/servers_test.go @@ -612,60 +612,72 @@ func TestCreateServers(t *testing.T) { return []http.Location{ { - Path: "@rule0-route0", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: baseHeaders, - AddResponseHeaders: baseHeaders, - SetResponseHeaders: []http.Header{}, - RemoveResponseHeaders: []string{}, + Path: "@rule0-route0", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: baseHeaders, + ResponseHeaders: http.ResponseHeaders{ + Add: baseHeaders, + Set: []http.Header{}, + Remove: []string{}, + }, }, { - Path: "@rule0-route1", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: baseHeaders, - AddResponseHeaders: baseHeaders, - SetResponseHeaders: []http.Header{}, - RemoveResponseHeaders: []string{}, + Path: "@rule0-route1", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: baseHeaders, + ResponseHeaders: http.ResponseHeaders{ + Add: baseHeaders, + Set: []http.Header{}, + Remove: []string{}, + }, }, { - Path: "@rule0-route2", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: baseHeaders, - AddResponseHeaders: baseHeaders, - SetResponseHeaders: []http.Header{}, - RemoveResponseHeaders: []string{}, + Path: "@rule0-route2", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: baseHeaders, + ResponseHeaders: http.ResponseHeaders{ + Add: baseHeaders, + Set: []http.Header{}, + Remove: []string{}, + }, }, { Path: "/", HTTPMatchKey: ssl + "1_0", }, { - Path: "@rule1-route0", - ProxyPass: "http://$test__route1_rule1$request_uri", - ProxySetHeaders: baseHeaders, - AddResponseHeaders: baseHeaders, - SetResponseHeaders: []http.Header{}, - RemoveResponseHeaders: []string{}, + Path: "@rule1-route0", + ProxyPass: "http://$test__route1_rule1$request_uri", + ProxySetHeaders: baseHeaders, + ResponseHeaders: http.ResponseHeaders{ + Add: baseHeaders, + Set: []http.Header{}, + Remove: []string{}, + }, }, { Path: "/test/", HTTPMatchKey: ssl + "1_1", }, { - Path: "/path-only/", - ProxyPass: "http://invalid-backend-ref$request_uri", - ProxySetHeaders: baseHeaders, - AddResponseHeaders: baseHeaders, - SetResponseHeaders: []http.Header{}, - RemoveResponseHeaders: []string{}, + Path: "/path-only/", + ProxyPass: "http://invalid-backend-ref$request_uri", + ProxySetHeaders: baseHeaders, + ResponseHeaders: http.ResponseHeaders{ + Add: baseHeaders, + Set: []http.Header{}, + Remove: []string{}, + }, }, { - Path: "= /path-only", - ProxyPass: "http://invalid-backend-ref$request_uri", - ProxySetHeaders: baseHeaders, - AddResponseHeaders: baseHeaders, - SetResponseHeaders: []http.Header{}, - RemoveResponseHeaders: []string{}, + Path: "= /path-only", + ProxyPass: "http://invalid-backend-ref$request_uri", + ProxySetHeaders: baseHeaders, + ResponseHeaders: http.ResponseHeaders{ + Add: baseHeaders, + Set: []http.Header{}, + Remove: []string{}, + }, }, { Path: "/backend-tls-policy/", @@ -729,31 +741,37 @@ func TestCreateServers(t *testing.T) { HTTPMatchKey: ssl + "1_6", }, { - Path: "/rewrite/", - Rewrites: []string{"^ /replacement break"}, - ProxyPass: "http://test_foo_80", - ProxySetHeaders: rewriteProxySetHeaders, - AddResponseHeaders: baseHeaders, - SetResponseHeaders: []http.Header{}, - RemoveResponseHeaders: []string{}, + Path: "/rewrite/", + Rewrites: []string{"^ /replacement break"}, + ProxyPass: "http://test_foo_80", + ProxySetHeaders: rewriteProxySetHeaders, + ResponseHeaders: http.ResponseHeaders{ + Add: baseHeaders, + Set: []http.Header{}, + Remove: []string{}, + }, }, { - Path: "= /rewrite", - Rewrites: []string{"^ /replacement break"}, - ProxyPass: "http://test_foo_80", - ProxySetHeaders: rewriteProxySetHeaders, - AddResponseHeaders: baseHeaders, - SetResponseHeaders: []http.Header{}, - RemoveResponseHeaders: []string{}, + Path: "= /rewrite", + Rewrites: []string{"^ /replacement break"}, + ProxyPass: "http://test_foo_80", + ProxySetHeaders: rewriteProxySetHeaders, + ResponseHeaders: http.ResponseHeaders{ + Add: baseHeaders, + Set: []http.Header{}, + Remove: []string{}, + }, }, { - Path: "@rule7-route0", - Rewrites: []string{"^/rewrite-with-headers(.*)$ /prefix-replacement$1 break"}, - ProxyPass: "http://test_foo_80", - ProxySetHeaders: rewriteProxySetHeaders, - AddResponseHeaders: baseHeaders, - SetResponseHeaders: []http.Header{}, - RemoveResponseHeaders: []string{}, + Path: "@rule7-route0", + Rewrites: []string{"^/rewrite-with-headers(.*)$ /prefix-replacement$1 break"}, + ProxyPass: "http://test_foo_80", + ProxySetHeaders: rewriteProxySetHeaders, + ResponseHeaders: http.ResponseHeaders{ + Add: baseHeaders, + Set: []http.Header{}, + Remove: []string{}, + }, }, { Path: "/rewrite-with-headers/", @@ -790,20 +808,24 @@ func TestCreateServers(t *testing.T) { HTTPMatchKey: ssl + "1_10", }, { - Path: "= /exact", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: baseHeaders, - AddResponseHeaders: baseHeaders, - SetResponseHeaders: []http.Header{}, - RemoveResponseHeaders: []string{}, + Path: "= /exact", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: baseHeaders, + ResponseHeaders: http.ResponseHeaders{ + Add: baseHeaders, + Set: []http.Header{}, + Remove: []string{}, + }, }, { - Path: "@rule11-route0", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: baseHeaders, - AddResponseHeaders: baseHeaders, - SetResponseHeaders: []http.Header{}, - RemoveResponseHeaders: []string{}, + Path: "@rule11-route0", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: baseHeaders, + ResponseHeaders: http.ResponseHeaders{ + Add: baseHeaders, + Set: []http.Header{}, + Remove: []string{}, + }, }, { Path: "= /test", @@ -834,9 +856,11 @@ func TestCreateServers(t *testing.T) { Value: "$connection_upgrade", }, }, - AddResponseHeaders: baseHeaders, - SetResponseHeaders: []http.Header{}, - RemoveResponseHeaders: []string{}, + ResponseHeaders: http.ResponseHeaders{ + Add: baseHeaders, + Set: []http.Header{}, + Remove: []string{}, + }, }, { Path: "= /proxy-set-headers", @@ -863,9 +887,11 @@ func TestCreateServers(t *testing.T) { Value: "$connection_upgrade", }, }, - AddResponseHeaders: baseHeaders, - SetResponseHeaders: []http.Header{}, - RemoveResponseHeaders: []string{}, + ResponseHeaders: http.ResponseHeaders{ + Add: baseHeaders, + Set: []http.Header{}, + Remove: []string{}, + }, }, { Path: "= /grpc/method", @@ -999,20 +1025,24 @@ func TestCreateServersConflicts(t *testing.T) { }, expLocs: []http.Location{ { - Path: "/coffee/", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: baseHeaders, - AddResponseHeaders: baseHeaders, - SetResponseHeaders: []http.Header{}, - RemoveResponseHeaders: []string{}, + Path: "/coffee/", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: baseHeaders, + ResponseHeaders: http.ResponseHeaders{ + Add: baseHeaders, + Set: []http.Header{}, + Remove: []string{}, + }, }, { - Path: "= /coffee", - ProxyPass: "http://test_bar_80$request_uri", - ProxySetHeaders: baseHeaders, - AddResponseHeaders: baseHeaders, - SetResponseHeaders: []http.Header{}, - RemoveResponseHeaders: []string{}, + Path: "= /coffee", + ProxyPass: "http://test_bar_80$request_uri", + ProxySetHeaders: baseHeaders, + ResponseHeaders: http.ResponseHeaders{ + Add: baseHeaders, + Set: []http.Header{}, + Remove: []string{}, + }, }, createDefaultRootLocation(), }, @@ -1043,20 +1073,24 @@ func TestCreateServersConflicts(t *testing.T) { }, expLocs: []http.Location{ { - Path: "= /coffee", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: baseHeaders, - AddResponseHeaders: baseHeaders, - SetResponseHeaders: []http.Header{}, - RemoveResponseHeaders: []string{}, + Path: "= /coffee", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: baseHeaders, + ResponseHeaders: http.ResponseHeaders{ + Add: baseHeaders, + Set: []http.Header{}, + Remove: []string{}, + }, }, { - Path: "/coffee/", - ProxyPass: "http://test_bar_80$request_uri", - ProxySetHeaders: baseHeaders, - AddResponseHeaders: baseHeaders, - SetResponseHeaders: []http.Header{}, - RemoveResponseHeaders: []string{}, + Path: "/coffee/", + ProxyPass: "http://test_bar_80$request_uri", + ProxySetHeaders: baseHeaders, + ResponseHeaders: http.ResponseHeaders{ + Add: baseHeaders, + Set: []http.Header{}, + Remove: []string{}, + }, }, createDefaultRootLocation(), }, @@ -1097,20 +1131,24 @@ func TestCreateServersConflicts(t *testing.T) { }, expLocs: []http.Location{ { - Path: "/coffee/", - ProxyPass: "http://test_bar_80$request_uri", - ProxySetHeaders: baseHeaders, - AddResponseHeaders: baseHeaders, - SetResponseHeaders: []http.Header{}, - RemoveResponseHeaders: []string{}, + Path: "/coffee/", + ProxyPass: "http://test_bar_80$request_uri", + ProxySetHeaders: baseHeaders, + ResponseHeaders: http.ResponseHeaders{ + Add: baseHeaders, + Set: []http.Header{}, + Remove: []string{}, + }, }, { - Path: "= /coffee", - ProxyPass: "http://test_baz_80$request_uri", - ProxySetHeaders: baseHeaders, - AddResponseHeaders: baseHeaders, - SetResponseHeaders: []http.Header{}, - RemoveResponseHeaders: []string{}, + Path: "= /coffee", + ProxyPass: "http://test_baz_80$request_uri", + ProxySetHeaders: baseHeaders, + ResponseHeaders: http.ResponseHeaders{ + Add: baseHeaders, + Set: []http.Header{}, + Remove: []string{}, + }, }, createDefaultRootLocation(), }, @@ -1226,20 +1264,24 @@ func TestCreateLocationsRootPath(t *testing.T) { pathRules: getPathRules(false /* rootPath */, false /* grpc */), expLocations: []http.Location{ { - Path: "/path-1", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: baseHeaders, - AddResponseHeaders: baseHeaders, - SetResponseHeaders: []http.Header{}, - RemoveResponseHeaders: []string{}, + Path: "/path-1", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: baseHeaders, + ResponseHeaders: http.ResponseHeaders{ + Add: baseHeaders, + Set: []http.Header{}, + Remove: []string{}, + }, }, { - Path: "/path-2", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: baseHeaders, - AddResponseHeaders: baseHeaders, - SetResponseHeaders: []http.Header{}, - RemoveResponseHeaders: []string{}, + Path: "/path-2", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: baseHeaders, + ResponseHeaders: http.ResponseHeaders{ + Add: baseHeaders, + Set: []http.Header{}, + Remove: []string{}, + }, }, { Path: "/", @@ -1282,28 +1324,34 @@ func TestCreateLocationsRootPath(t *testing.T) { pathRules: getPathRules(true /* rootPath */, false /* grpc */), expLocations: []http.Location{ { - Path: "/path-1", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: baseHeaders, - AddResponseHeaders: baseHeaders, - SetResponseHeaders: []http.Header{}, - RemoveResponseHeaders: []string{}, + Path: "/path-1", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: baseHeaders, + ResponseHeaders: http.ResponseHeaders{ + Add: baseHeaders, + Set: []http.Header{}, + Remove: []string{}, + }, }, { - Path: "/path-2", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: baseHeaders, - AddResponseHeaders: baseHeaders, - SetResponseHeaders: []http.Header{}, - RemoveResponseHeaders: []string{}, + Path: "/path-2", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: baseHeaders, + ResponseHeaders: http.ResponseHeaders{ + Add: baseHeaders, + Set: []http.Header{}, + Remove: []string{}, + }, }, { - Path: "/", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: baseHeaders, - AddResponseHeaders: baseHeaders, - SetResponseHeaders: []http.Header{}, - RemoveResponseHeaders: []string{}, + Path: "/", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: baseHeaders, + ResponseHeaders: http.ResponseHeaders{ + Add: baseHeaders, + Set: []http.Header{}, + Remove: []string{}, + }, }, }, }, From 20f2600afd9780b14b910a076285cff3bd3ebcdd Mon Sep 17 00:00:00 2001 From: kaihsun Date: Mon, 19 Feb 2024 22:19:32 +0000 Subject: [PATCH 13/72] Address comments --- internal/mode/static/nginx/config/servers.go | 9 ++-- .../mode/static/nginx/config/servers_test.go | 48 +++++++++---------- 2 files changed, 27 insertions(+), 30 deletions(-) diff --git a/internal/mode/static/nginx/config/servers.go b/internal/mode/static/nginx/config/servers.go index a17e4d80e7..5e261c70bb 100644 --- a/internal/mode/static/nginx/config/servers.go +++ b/internal/mode/static/nginx/config/servers.go @@ -607,20 +607,17 @@ func generateResponseHeaders(filters *dataplane.HTTPFilters) http.ResponseHeader } func generateAddResponseHeaders(filters *dataplane.HTTPFilters) []http.Header { - headers := make([]http.Header, len(baseHeaders)) - copy(headers, baseHeaders) - if filters == nil || filters.ResponseHeaderModifiers == nil { - return headers + return []http.Header{} } headerFilter := filters.ResponseHeaderModifiers - responseAddHeaders := make([]http.Header, 0, len(headerFilter.Add)+len(headers)) + responseAddHeaders := make([]http.Header, 0, len(headerFilter.Add)) if len(headerFilter.Add) > 0 { // FIXME(kevin85421): Should we use a different function? addHeaders := convertSetHeaders(headerFilter.Add) responseAddHeaders = append(responseAddHeaders, addHeaders...) } - return append(responseAddHeaders, headers...) + return responseAddHeaders } func generateSetResponseHeaders(filters *dataplane.HTTPFilters) []http.Header { diff --git a/internal/mode/static/nginx/config/servers_test.go b/internal/mode/static/nginx/config/servers_test.go index cc0644a52b..6883505f4d 100644 --- a/internal/mode/static/nginx/config/servers_test.go +++ b/internal/mode/static/nginx/config/servers_test.go @@ -616,7 +616,7 @@ func TestCreateServers(t *testing.T) { ProxyPass: "http://test_foo_80$request_uri", ProxySetHeaders: baseHeaders, ResponseHeaders: http.ResponseHeaders{ - Add: baseHeaders, + Add: []http.Header{}, Set: []http.Header{}, Remove: []string{}, }, @@ -626,7 +626,7 @@ func TestCreateServers(t *testing.T) { ProxyPass: "http://test_foo_80$request_uri", ProxySetHeaders: baseHeaders, ResponseHeaders: http.ResponseHeaders{ - Add: baseHeaders, + Add: []http.Header{}, Set: []http.Header{}, Remove: []string{}, }, @@ -636,7 +636,7 @@ func TestCreateServers(t *testing.T) { ProxyPass: "http://test_foo_80$request_uri", ProxySetHeaders: baseHeaders, ResponseHeaders: http.ResponseHeaders{ - Add: baseHeaders, + Add: []http.Header{}, Set: []http.Header{}, Remove: []string{}, }, @@ -650,7 +650,7 @@ func TestCreateServers(t *testing.T) { ProxyPass: "http://$test__route1_rule1$request_uri", ProxySetHeaders: baseHeaders, ResponseHeaders: http.ResponseHeaders{ - Add: baseHeaders, + Add: []http.Header{}, Set: []http.Header{}, Remove: []string{}, }, @@ -664,7 +664,7 @@ func TestCreateServers(t *testing.T) { ProxyPass: "http://invalid-backend-ref$request_uri", ProxySetHeaders: baseHeaders, ResponseHeaders: http.ResponseHeaders{ - Add: baseHeaders, + Add: []http.Header{}, Set: []http.Header{}, Remove: []string{}, }, @@ -674,7 +674,7 @@ func TestCreateServers(t *testing.T) { ProxyPass: "http://invalid-backend-ref$request_uri", ProxySetHeaders: baseHeaders, ResponseHeaders: http.ResponseHeaders{ - Add: baseHeaders, + Add: []http.Header{}, Set: []http.Header{}, Remove: []string{}, }, @@ -746,7 +746,7 @@ func TestCreateServers(t *testing.T) { ProxyPass: "http://test_foo_80", ProxySetHeaders: rewriteProxySetHeaders, ResponseHeaders: http.ResponseHeaders{ - Add: baseHeaders, + Add: []http.Header{}, Set: []http.Header{}, Remove: []string{}, }, @@ -757,7 +757,7 @@ func TestCreateServers(t *testing.T) { ProxyPass: "http://test_foo_80", ProxySetHeaders: rewriteProxySetHeaders, ResponseHeaders: http.ResponseHeaders{ - Add: baseHeaders, + Add: []http.Header{}, Set: []http.Header{}, Remove: []string{}, }, @@ -768,7 +768,7 @@ func TestCreateServers(t *testing.T) { ProxyPass: "http://test_foo_80", ProxySetHeaders: rewriteProxySetHeaders, ResponseHeaders: http.ResponseHeaders{ - Add: baseHeaders, + Add: []http.Header{}, Set: []http.Header{}, Remove: []string{}, }, @@ -812,7 +812,7 @@ func TestCreateServers(t *testing.T) { ProxyPass: "http://test_foo_80$request_uri", ProxySetHeaders: baseHeaders, ResponseHeaders: http.ResponseHeaders{ - Add: baseHeaders, + Add: []http.Header{}, Set: []http.Header{}, Remove: []string{}, }, @@ -822,7 +822,7 @@ func TestCreateServers(t *testing.T) { ProxyPass: "http://test_foo_80$request_uri", ProxySetHeaders: baseHeaders, ResponseHeaders: http.ResponseHeaders{ - Add: baseHeaders, + Add: []http.Header{}, Set: []http.Header{}, Remove: []string{}, }, @@ -857,7 +857,7 @@ func TestCreateServers(t *testing.T) { }, }, ResponseHeaders: http.ResponseHeaders{ - Add: baseHeaders, + Add: []http.Header{}, Set: []http.Header{}, Remove: []string{}, }, @@ -888,7 +888,7 @@ func TestCreateServers(t *testing.T) { }, }, ResponseHeaders: http.ResponseHeaders{ - Add: baseHeaders, + Add: []http.Header{}, Set: []http.Header{}, Remove: []string{}, }, @@ -1029,7 +1029,7 @@ func TestCreateServersConflicts(t *testing.T) { ProxyPass: "http://test_foo_80$request_uri", ProxySetHeaders: baseHeaders, ResponseHeaders: http.ResponseHeaders{ - Add: baseHeaders, + Add: []http.Header{}, Set: []http.Header{}, Remove: []string{}, }, @@ -1039,7 +1039,7 @@ func TestCreateServersConflicts(t *testing.T) { ProxyPass: "http://test_bar_80$request_uri", ProxySetHeaders: baseHeaders, ResponseHeaders: http.ResponseHeaders{ - Add: baseHeaders, + Add: []http.Header{}, Set: []http.Header{}, Remove: []string{}, }, @@ -1077,7 +1077,7 @@ func TestCreateServersConflicts(t *testing.T) { ProxyPass: "http://test_foo_80$request_uri", ProxySetHeaders: baseHeaders, ResponseHeaders: http.ResponseHeaders{ - Add: baseHeaders, + Add: []http.Header{}, Set: []http.Header{}, Remove: []string{}, }, @@ -1087,7 +1087,7 @@ func TestCreateServersConflicts(t *testing.T) { ProxyPass: "http://test_bar_80$request_uri", ProxySetHeaders: baseHeaders, ResponseHeaders: http.ResponseHeaders{ - Add: baseHeaders, + Add: []http.Header{}, Set: []http.Header{}, Remove: []string{}, }, @@ -1135,7 +1135,7 @@ func TestCreateServersConflicts(t *testing.T) { ProxyPass: "http://test_bar_80$request_uri", ProxySetHeaders: baseHeaders, ResponseHeaders: http.ResponseHeaders{ - Add: baseHeaders, + Add: []http.Header{}, Set: []http.Header{}, Remove: []string{}, }, @@ -1145,7 +1145,7 @@ func TestCreateServersConflicts(t *testing.T) { ProxyPass: "http://test_baz_80$request_uri", ProxySetHeaders: baseHeaders, ResponseHeaders: http.ResponseHeaders{ - Add: baseHeaders, + Add: []http.Header{}, Set: []http.Header{}, Remove: []string{}, }, @@ -1268,7 +1268,7 @@ func TestCreateLocationsRootPath(t *testing.T) { ProxyPass: "http://test_foo_80$request_uri", ProxySetHeaders: baseHeaders, ResponseHeaders: http.ResponseHeaders{ - Add: baseHeaders, + Add: []http.Header{}, Set: []http.Header{}, Remove: []string{}, }, @@ -1278,7 +1278,7 @@ func TestCreateLocationsRootPath(t *testing.T) { ProxyPass: "http://test_foo_80$request_uri", ProxySetHeaders: baseHeaders, ResponseHeaders: http.ResponseHeaders{ - Add: baseHeaders, + Add: []http.Header{}, Set: []http.Header{}, Remove: []string{}, }, @@ -1328,7 +1328,7 @@ func TestCreateLocationsRootPath(t *testing.T) { ProxyPass: "http://test_foo_80$request_uri", ProxySetHeaders: baseHeaders, ResponseHeaders: http.ResponseHeaders{ - Add: baseHeaders, + Add: []http.Header{}, Set: []http.Header{}, Remove: []string{}, }, @@ -1338,7 +1338,7 @@ func TestCreateLocationsRootPath(t *testing.T) { ProxyPass: "http://test_foo_80$request_uri", ProxySetHeaders: baseHeaders, ResponseHeaders: http.ResponseHeaders{ - Add: baseHeaders, + Add: []http.Header{}, Set: []http.Header{}, Remove: []string{}, }, @@ -1348,7 +1348,7 @@ func TestCreateLocationsRootPath(t *testing.T) { ProxyPass: "http://test_foo_80$request_uri", ProxySetHeaders: baseHeaders, ResponseHeaders: http.ResponseHeaders{ - Add: baseHeaders, + Add: []http.Header{}, Set: []http.Header{}, Remove: []string{}, }, From 120420bb87d161cf808cab55a17019908a5530a3 Mon Sep 17 00:00:00 2001 From: kaihsun Date: Mon, 19 Feb 2024 23:19:08 +0000 Subject: [PATCH 14/72] Address comments --- internal/mode/static/nginx/config/servers.go | 43 +++++++------------- 1 file changed, 14 insertions(+), 29 deletions(-) diff --git a/internal/mode/static/nginx/config/servers.go b/internal/mode/static/nginx/config/servers.go index 5e261c70bb..6a2bf29c21 100644 --- a/internal/mode/static/nginx/config/servers.go +++ b/internal/mode/static/nginx/config/servers.go @@ -599,50 +599,35 @@ func generateProxySetHeaders(filters *dataplane.HTTPFilters, grpc bool) []http.H } func generateResponseHeaders(filters *dataplane.HTTPFilters) http.ResponseHeaders { - return http.ResponseHeaders{ - Add: generateAddResponseHeaders(filters), - Set: generateSetResponseHeaders(filters), - Remove: generateRemoveResponseHeaders(filters), - } -} - -func generateAddResponseHeaders(filters *dataplane.HTTPFilters) []http.Header { if filters == nil || filters.ResponseHeaderModifiers == nil { - return []http.Header{} + return http.ResponseHeaders{ + Add: []http.Header{}, + Set: []http.Header{}, + Remove: []string{}, + } } + headerFilter := filters.ResponseHeaderModifiers responseAddHeaders := make([]http.Header, 0, len(headerFilter.Add)) + responseSetHeaders := make([]http.Header, 0, len(headerFilter.Set)) + responseRemoveHeaders := make([]string, len(headerFilter.Remove)) if len(headerFilter.Add) > 0 { // FIXME(kevin85421): Should we use a different function? addHeaders := convertSetHeaders(headerFilter.Add) responseAddHeaders = append(responseAddHeaders, addHeaders...) } - return responseAddHeaders -} - -func generateSetResponseHeaders(filters *dataplane.HTTPFilters) []http.Header { - if filters == nil || filters.ResponseHeaderModifiers == nil { - return []http.Header{} - } - headerFilter := filters.ResponseHeaderModifiers - responseSetHeaders := make([]http.Header, 0, len(headerFilter.Set)) if len(headerFilter.Set) > 0 { setHeaders := convertSetHeaders(headerFilter.Set) responseSetHeaders = append(responseSetHeaders, setHeaders...) } - return responseSetHeaders -} + // Make a deep copy to prevent the slice from being accidentally modified. + copy(responseRemoveHeaders, headerFilter.Remove) -func generateRemoveResponseHeaders(filters *dataplane.HTTPFilters) []string { - if filters == nil || filters.ResponseHeaderModifiers == nil { - return []string{} + return http.ResponseHeaders{ + Add: responseAddHeaders, + Set: responseSetHeaders, + Remove: responseRemoveHeaders, } - removeHeaders := filters.ResponseHeaderModifiers.Remove - responseRemoveHeaders := make([]string, len(removeHeaders)) - - // Make a deep copy to prevent the slice from being accidentally modified. - copy(responseRemoveHeaders, removeHeaders) - return responseRemoveHeaders } func convertAddHeaders(headers []dataplane.HTTPHeader) []http.Header { From e622be9c2c1a8bcdd1223dd5bf8e788c1647dc03 Mon Sep 17 00:00:00 2001 From: kaihsun Date: Mon, 19 Feb 2024 23:54:20 +0000 Subject: [PATCH 15/72] Address comments --- internal/mode/static/nginx/config/servers.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/internal/mode/static/nginx/config/servers.go b/internal/mode/static/nginx/config/servers.go index 6a2bf29c21..3066cbc2ad 100644 --- a/internal/mode/static/nginx/config/servers.go +++ b/internal/mode/static/nginx/config/servers.go @@ -584,7 +584,7 @@ func generateProxySetHeaders(filters *dataplane.HTTPFilters, grpc bool) []http.H proxySetHeaders = append(proxySetHeaders, addHeaders...) } if len(headerFilter.Set) > 0 { - setHeaders := convertSetHeaders(headerFilter.Set) + setHeaders := createHeaders(headerFilter.Set) proxySetHeaders = append(proxySetHeaders, setHeaders...) } // If the value of a header field is an empty string then this field will not be passed to a proxied server @@ -612,12 +612,11 @@ func generateResponseHeaders(filters *dataplane.HTTPFilters) http.ResponseHeader responseSetHeaders := make([]http.Header, 0, len(headerFilter.Set)) responseRemoveHeaders := make([]string, len(headerFilter.Remove)) if len(headerFilter.Add) > 0 { - // FIXME(kevin85421): Should we use a different function? - addHeaders := convertSetHeaders(headerFilter.Add) + addHeaders := createHeaders(headerFilter.Add) responseAddHeaders = append(responseAddHeaders, addHeaders...) } if len(headerFilter.Set) > 0 { - setHeaders := convertSetHeaders(headerFilter.Set) + setHeaders := createHeaders(headerFilter.Set) responseSetHeaders = append(responseSetHeaders, setHeaders...) } // Make a deep copy to prevent the slice from being accidentally modified. @@ -642,7 +641,7 @@ func convertAddHeaders(headers []dataplane.HTTPHeader) []http.Header { return locHeaders } -func convertSetHeaders(headers []dataplane.HTTPHeader) []http.Header { +func createHeaders(headers []dataplane.HTTPHeader) []http.Header { locHeaders := make([]http.Header, 0, len(headers)) for _, h := range headers { locHeaders = append(locHeaders, http.Header{ From cd777a25a0d98555b5d8be2b612d1a9ebd238b35 Mon Sep 17 00:00:00 2001 From: kaihsun Date: Tue, 20 Feb 2024 00:02:25 +0000 Subject: [PATCH 16/72] Address comments --- internal/mode/static/nginx/config/servers.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/mode/static/nginx/config/servers.go b/internal/mode/static/nginx/config/servers.go index 3066cbc2ad..2ede036ee9 100644 --- a/internal/mode/static/nginx/config/servers.go +++ b/internal/mode/static/nginx/config/servers.go @@ -580,7 +580,7 @@ func generateProxySetHeaders(filters *dataplane.HTTPFilters, grpc bool) []http.H headerLen := len(headerFilter.Add) + len(headerFilter.Set) + len(headerFilter.Remove) + len(headers) proxySetHeaders := make([]http.Header, 0, headerLen) if len(headerFilter.Add) > 0 { - addHeaders := convertAddHeaders(headerFilter.Add) + addHeaders := createHeadersWithVarName(headerFilter.Add) proxySetHeaders = append(proxySetHeaders, addHeaders...) } if len(headerFilter.Set) > 0 { @@ -629,7 +629,7 @@ func generateResponseHeaders(filters *dataplane.HTTPFilters) http.ResponseHeader } } -func convertAddHeaders(headers []dataplane.HTTPHeader) []http.Header { +func createHeadersWithVarName(headers []dataplane.HTTPHeader) []http.Header { locHeaders := make([]http.Header, 0, len(headers)) for _, h := range headers { mapVarName := "${" + generateAddHeaderMapVariableName(h.Name) + "}" From d8ce13e224cd5915abe7c5515a1b66f296b6b2dd Mon Sep 17 00:00:00 2001 From: kaihsun Date: Tue, 20 Feb 2024 01:13:02 +0000 Subject: [PATCH 17/72] Add HTTPRouteResponseHeaderModification to the conformance tests --- conformance/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conformance/Makefile b/conformance/Makefile index 27de8f1786..91ce92da7e 100644 --- a/conformance/Makefile +++ b/conformance/Makefile @@ -5,7 +5,7 @@ NGINX_PREFIX = $(PREFIX)/nginx NGINX_PLUS_PREFIX ?= $(PREFIX)/nginx-plus GW_API_VERSION ?= 1.0.0 GATEWAY_CLASS = nginx -SUPPORTED_FEATURES = HTTPRouteQueryParamMatching,HTTPRouteMethodMatching,HTTPRoutePortRedirect,HTTPRouteSchemeRedirect,HTTPRouteHostRewrite,HTTPRoutePathRewrite,GatewayPort8080 +SUPPORTED_FEATURES = HTTPRouteQueryParamMatching,HTTPRouteMethodMatching,HTTPRoutePortRedirect,HTTPRouteSchemeRedirect,HTTPRouteHostRewrite,HTTPRoutePathRewrite,GatewayPort8080,HTTPRouteResponseHeaderModification KIND_IMAGE ?= $(shell grep -m1 'FROM kindest/node' Date: Tue, 20 Feb 2024 01:20:13 +0000 Subject: [PATCH 18/72] Add support for ResponseHeaderModifier for HTTPRouteRule objects Problem: Users want to add, set, and remove response headers. Solution: Use add_header and proxy_hide_header NGINX directives to support ResponseHeaderModifier. If the action is set, we configure the proxy_hide_header directive to remove the header if exists and add_header directive with the given value in the HTTPRoute spec. If the action is remove, we configure the proxy_hide_header directive to remove the header. If the action is add, we simply configure the add_header directive with the given value in the HTTPRoute spec. --- site/content/overview/gateway-api-compatibility.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/site/content/overview/gateway-api-compatibility.md b/site/content/overview/gateway-api-compatibility.md index 82e25523f1..71ca290dd4 100644 --- a/site/content/overview/gateway-api-compatibility.md +++ b/site/content/overview/gateway-api-compatibility.md @@ -155,10 +155,11 @@ See the [static-mode]({{< relref "/reference/cli-help.md#static-mode">}}) comman - `method`: Supported. - `filters` - `type`: Supported. - - `requestRedirect`: Supported except for the experimental `path` field. If multiple filters are configured, NGINX Gateway Fabric will choose the first and ignore the rest. Incompatible with `urlRewrite`. - - `requestHeaderModifier`: Supported. If multiple filters are configured, NGINX Gateway Fabric will choose the first and ignore the rest. - - `urlRewrite`: Supported. If multiple filters are configured, NGINX Gateway Fabric will choose the first and ignore the rest. Incompatible with `requestRedirect`. - - `responseHeaderModifier`, `requestMirror`, `extensionRef`: Not supported. + - `requestRedirect`: Supported except for the experimental `path` field. If multiple filters are configured, NGINX Gateway Fabric will choose the first and ignore the rest. + - `requestHeaderModifier`: Supported. If multiple filters are configured, NGINX Gateway Fabric will choose the first and ignore the rest. Incompatible with `urlRewrite`. + - `urlRewrite`: Supported. If multiple filters are configured, NGINX Gateway Fabric will choose the first and ignore the rest. Incompatible with `requestHeaderModifier`. + - `responseHeaderModifier`: Supported. + - `requestMirror`, `extensionRef`: Not supported. - `backendRefs`: Partially supported. Backend ref `filters` are not supported. - `status` - `parents` From 5cd44a19431c1c595bc8f3be7c6dfac33a42aa83 Mon Sep 17 00:00:00 2001 From: kaihsun Date: Sun, 25 Feb 2024 17:28:57 +0000 Subject: [PATCH 19/72] Add support for ResponseHeaderModifier for HTTPRouteRule objects Problem: Users want to add, set, and remove response headers. Solution: Use add_header and proxy_hide_header NGINX directives to support ResponseHeaderModifier. If the action is set, we configure the proxy_hide_header directive to remove the header if exists and add_header directive with the given value in the HTTPRoute spec. If the action is remove, we configure the proxy_hide_header directive to remove the header. If the action is add, we simply configure the add_header directive with the given value in the HTTPRoute spec. --- internal/mode/static/nginx/config/servers.go | 6 +- .../mode/static/nginx/config/servers_test.go | 282 ++++++------------ 2 files changed, 94 insertions(+), 194 deletions(-) diff --git a/internal/mode/static/nginx/config/servers.go b/internal/mode/static/nginx/config/servers.go index 2ede036ee9..0f0488a49e 100644 --- a/internal/mode/static/nginx/config/servers.go +++ b/internal/mode/static/nginx/config/servers.go @@ -600,11 +600,7 @@ func generateProxySetHeaders(filters *dataplane.HTTPFilters, grpc bool) []http.H func generateResponseHeaders(filters *dataplane.HTTPFilters) http.ResponseHeaders { if filters == nil || filters.ResponseHeaderModifiers == nil { - return http.ResponseHeaders{ - Add: []http.Header{}, - Set: []http.Header{}, - Remove: []string{}, - } + return http.ResponseHeaders{} } headerFilter := filters.ResponseHeaderModifiers diff --git a/internal/mode/static/nginx/config/servers_test.go b/internal/mode/static/nginx/config/servers_test.go index 6883505f4d..765c0abc36 100644 --- a/internal/mode/static/nginx/config/servers_test.go +++ b/internal/mode/static/nginx/config/servers_test.go @@ -612,72 +612,48 @@ func TestCreateServers(t *testing.T) { return []http.Location{ { - Path: "@rule0-route0", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: baseHeaders, - ResponseHeaders: http.ResponseHeaders{ - Add: []http.Header{}, - Set: []http.Header{}, - Remove: []string{}, - }, + Path: "@rule0-route0", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: baseHeaders, + AddHeaderDirectives: baseHeaders, }, { - Path: "@rule0-route1", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: baseHeaders, - ResponseHeaders: http.ResponseHeaders{ - Add: []http.Header{}, - Set: []http.Header{}, - Remove: []string{}, - }, + Path: "@rule0-route1", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: baseHeaders, + AddHeaderDirectives: baseHeaders, }, { - Path: "@rule0-route2", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: baseHeaders, - ResponseHeaders: http.ResponseHeaders{ - Add: []http.Header{}, - Set: []http.Header{}, - Remove: []string{}, - }, + Path: "@rule0-route2", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: baseHeaders, + AddHeaderDirectives: baseHeaders, }, { Path: "/", HTTPMatchKey: ssl + "1_0", }, { - Path: "@rule1-route0", - ProxyPass: "http://$test__route1_rule1$request_uri", - ProxySetHeaders: baseHeaders, - ResponseHeaders: http.ResponseHeaders{ - Add: []http.Header{}, - Set: []http.Header{}, - Remove: []string{}, - }, + Path: "@rule1-route0", + ProxyPass: "http://$test__route1_rule1$request_uri", + ProxySetHeaders: baseHeaders, + AddHeaderDirectives: baseHeaders, }, { Path: "/test/", HTTPMatchKey: ssl + "1_1", }, { - Path: "/path-only/", - ProxyPass: "http://invalid-backend-ref$request_uri", - ProxySetHeaders: baseHeaders, - ResponseHeaders: http.ResponseHeaders{ - Add: []http.Header{}, - Set: []http.Header{}, - Remove: []string{}, - }, + Path: "/path-only/", + ProxyPass: "http://invalid-backend-ref$request_uri", + ProxySetHeaders: baseHeaders, + AddHeaderDirectives: baseHeaders, }, { - Path: "= /path-only", - ProxyPass: "http://invalid-backend-ref$request_uri", - ProxySetHeaders: baseHeaders, - ResponseHeaders: http.ResponseHeaders{ - Add: []http.Header{}, - Set: []http.Header{}, - Remove: []string{}, - }, + Path: "= /path-only", + ProxyPass: "http://invalid-backend-ref$request_uri", + ProxySetHeaders: baseHeaders, + AddHeaderDirectives: baseHeaders, }, { Path: "/backend-tls-policy/", @@ -741,37 +717,25 @@ func TestCreateServers(t *testing.T) { HTTPMatchKey: ssl + "1_6", }, { - Path: "/rewrite/", - Rewrites: []string{"^ /replacement break"}, - ProxyPass: "http://test_foo_80", - ProxySetHeaders: rewriteProxySetHeaders, - ResponseHeaders: http.ResponseHeaders{ - Add: []http.Header{}, - Set: []http.Header{}, - Remove: []string{}, - }, + Path: "/rewrite/", + Rewrites: []string{"^ /replacement break"}, + ProxyPass: "http://test_foo_80", + ProxySetHeaders: rewriteProxySetHeaders, + AddHeaderDirectives: baseHeaders, }, { - Path: "= /rewrite", - Rewrites: []string{"^ /replacement break"}, - ProxyPass: "http://test_foo_80", - ProxySetHeaders: rewriteProxySetHeaders, - ResponseHeaders: http.ResponseHeaders{ - Add: []http.Header{}, - Set: []http.Header{}, - Remove: []string{}, - }, + Path: "= /rewrite", + Rewrites: []string{"^ /replacement break"}, + ProxyPass: "http://test_foo_80", + ProxySetHeaders: rewriteProxySetHeaders, + AddHeaderDirectives: baseHeaders, }, { - Path: "@rule7-route0", - Rewrites: []string{"^/rewrite-with-headers(.*)$ /prefix-replacement$1 break"}, - ProxyPass: "http://test_foo_80", - ProxySetHeaders: rewriteProxySetHeaders, - ResponseHeaders: http.ResponseHeaders{ - Add: []http.Header{}, - Set: []http.Header{}, - Remove: []string{}, - }, + Path: "@rule7-route0", + Rewrites: []string{"^/rewrite-with-headers(.*)$ /prefix-replacement$1 break"}, + ProxyPass: "http://test_foo_80", + ProxySetHeaders: rewriteProxySetHeaders, + AddHeaderDirectives: baseHeaders, }, { Path: "/rewrite-with-headers/", @@ -808,24 +772,16 @@ func TestCreateServers(t *testing.T) { HTTPMatchKey: ssl + "1_10", }, { - Path: "= /exact", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: baseHeaders, - ResponseHeaders: http.ResponseHeaders{ - Add: []http.Header{}, - Set: []http.Header{}, - Remove: []string{}, - }, + Path: "= /exact", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: baseHeaders, + AddHeaderDirectives: baseHeaders, }, { - Path: "@rule11-route0", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: baseHeaders, - ResponseHeaders: http.ResponseHeaders{ - Add: []http.Header{}, - Set: []http.Header{}, - Remove: []string{}, - }, + Path: "@rule11-route0", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: baseHeaders, + AddHeaderDirectives: baseHeaders, }, { Path: "= /test", @@ -856,11 +812,7 @@ func TestCreateServers(t *testing.T) { Value: "$connection_upgrade", }, }, - ResponseHeaders: http.ResponseHeaders{ - Add: []http.Header{}, - Set: []http.Header{}, - Remove: []string{}, - }, + AddHeaderDirectives: baseHeaders, }, { Path: "= /proxy-set-headers", @@ -887,11 +839,7 @@ func TestCreateServers(t *testing.T) { Value: "$connection_upgrade", }, }, - ResponseHeaders: http.ResponseHeaders{ - Add: []http.Header{}, - Set: []http.Header{}, - Remove: []string{}, - }, + AddHeaderDirectives: baseHeaders, }, { Path: "= /grpc/method", @@ -1025,24 +973,16 @@ func TestCreateServersConflicts(t *testing.T) { }, expLocs: []http.Location{ { - Path: "/coffee/", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: baseHeaders, - ResponseHeaders: http.ResponseHeaders{ - Add: []http.Header{}, - Set: []http.Header{}, - Remove: []string{}, - }, + Path: "/coffee/", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: baseHeaders, + AddHeaderDirectives: baseHeaders, }, { - Path: "= /coffee", - ProxyPass: "http://test_bar_80$request_uri", - ProxySetHeaders: baseHeaders, - ResponseHeaders: http.ResponseHeaders{ - Add: []http.Header{}, - Set: []http.Header{}, - Remove: []string{}, - }, + Path: "= /coffee", + ProxyPass: "http://test_bar_80$request_uri", + ProxySetHeaders: baseHeaders, + AddHeaderDirectives: baseHeaders, }, createDefaultRootLocation(), }, @@ -1073,24 +1013,16 @@ func TestCreateServersConflicts(t *testing.T) { }, expLocs: []http.Location{ { - Path: "= /coffee", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: baseHeaders, - ResponseHeaders: http.ResponseHeaders{ - Add: []http.Header{}, - Set: []http.Header{}, - Remove: []string{}, - }, + Path: "= /coffee", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: baseHeaders, + AddHeaderDirectives: baseHeaders, }, { - Path: "/coffee/", - ProxyPass: "http://test_bar_80$request_uri", - ProxySetHeaders: baseHeaders, - ResponseHeaders: http.ResponseHeaders{ - Add: []http.Header{}, - Set: []http.Header{}, - Remove: []string{}, - }, + Path: "/coffee/", + ProxyPass: "http://test_bar_80$request_uri", + ProxySetHeaders: baseHeaders, + AddHeaderDirectives: baseHeaders, }, createDefaultRootLocation(), }, @@ -1131,24 +1063,16 @@ func TestCreateServersConflicts(t *testing.T) { }, expLocs: []http.Location{ { - Path: "/coffee/", - ProxyPass: "http://test_bar_80$request_uri", - ProxySetHeaders: baseHeaders, - ResponseHeaders: http.ResponseHeaders{ - Add: []http.Header{}, - Set: []http.Header{}, - Remove: []string{}, - }, + Path: "/coffee/", + ProxyPass: "http://test_bar_80$request_uri", + ProxySetHeaders: baseHeaders, + AddHeaderDirectives: baseHeaders, }, { - Path: "= /coffee", - ProxyPass: "http://test_baz_80$request_uri", - ProxySetHeaders: baseHeaders, - ResponseHeaders: http.ResponseHeaders{ - Add: []http.Header{}, - Set: []http.Header{}, - Remove: []string{}, - }, + Path: "= /coffee", + ProxyPass: "http://test_baz_80$request_uri", + ProxySetHeaders: baseHeaders, + AddHeaderDirectives: baseHeaders, }, createDefaultRootLocation(), }, @@ -1264,24 +1188,16 @@ func TestCreateLocationsRootPath(t *testing.T) { pathRules: getPathRules(false /* rootPath */, false /* grpc */), expLocations: []http.Location{ { - Path: "/path-1", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: baseHeaders, - ResponseHeaders: http.ResponseHeaders{ - Add: []http.Header{}, - Set: []http.Header{}, - Remove: []string{}, - }, + Path: "/path-1", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: baseHeaders, + AddHeaderDirectives: baseHeaders, }, { - Path: "/path-2", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: baseHeaders, - ResponseHeaders: http.ResponseHeaders{ - Add: []http.Header{}, - Set: []http.Header{}, - Remove: []string{}, - }, + Path: "/path-2", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: baseHeaders, + AddHeaderDirectives: baseHeaders, }, { Path: "/", @@ -1324,34 +1240,22 @@ func TestCreateLocationsRootPath(t *testing.T) { pathRules: getPathRules(true /* rootPath */, false /* grpc */), expLocations: []http.Location{ { - Path: "/path-1", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: baseHeaders, - ResponseHeaders: http.ResponseHeaders{ - Add: []http.Header{}, - Set: []http.Header{}, - Remove: []string{}, - }, + Path: "/path-1", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: baseHeaders, + AddHeaderDirectives: baseHeaders, }, { - Path: "/path-2", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: baseHeaders, - ResponseHeaders: http.ResponseHeaders{ - Add: []http.Header{}, - Set: []http.Header{}, - Remove: []string{}, - }, + Path: "/path-2", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: baseHeaders, + AddHeaderDirectives: baseHeaders, }, { - Path: "/", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: baseHeaders, - ResponseHeaders: http.ResponseHeaders{ - Add: []http.Header{}, - Set: []http.Header{}, - Remove: []string{}, - }, + Path: "/", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: baseHeaders, + AddHeaderDirectives: baseHeaders, }, }, }, From 21af9bc0654de489894b11d1260e35e00e27f286 Mon Sep 17 00:00:00 2001 From: kaihsun Date: Sun, 25 Feb 2024 17:41:01 +0000 Subject: [PATCH 20/72] Add support for ResponseHeaderModifier for HTTPRouteRule objects Problem: Users want to add, set, and remove response headers. Solution: Use add_header and proxy_hide_header NGINX directives to support ResponseHeaderModifier. If the action is set, we configure the proxy_hide_header directive to remove the header if exists and add_header directive with the given value in the HTTPRoute spec. If the action is remove, we configure the proxy_hide_header directive to remove the header. If the action is add, we simply configure the add_header directive with the given value in the HTTPRoute spec. --- internal/mode/static/nginx/config/servers.go | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/internal/mode/static/nginx/config/servers.go b/internal/mode/static/nginx/config/servers.go index 0f0488a49e..9e3fe06177 100644 --- a/internal/mode/static/nginx/config/servers.go +++ b/internal/mode/static/nginx/config/servers.go @@ -604,23 +604,14 @@ func generateResponseHeaders(filters *dataplane.HTTPFilters) http.ResponseHeader } headerFilter := filters.ResponseHeaderModifiers - responseAddHeaders := make([]http.Header, 0, len(headerFilter.Add)) - responseSetHeaders := make([]http.Header, 0, len(headerFilter.Set)) responseRemoveHeaders := make([]string, len(headerFilter.Remove)) - if len(headerFilter.Add) > 0 { - addHeaders := createHeaders(headerFilter.Add) - responseAddHeaders = append(responseAddHeaders, addHeaders...) - } - if len(headerFilter.Set) > 0 { - setHeaders := createHeaders(headerFilter.Set) - responseSetHeaders = append(responseSetHeaders, setHeaders...) - } + // Make a deep copy to prevent the slice from being accidentally modified. copy(responseRemoveHeaders, headerFilter.Remove) return http.ResponseHeaders{ - Add: responseAddHeaders, - Set: responseSetHeaders, + Add: createHeaders(headerFilter.Add), + Set: createHeaders(headerFilter.Set), Remove: responseRemoveHeaders, } } From 2719fa4402d8509b989085ccd07638f8a0f70130 Mon Sep 17 00:00:00 2001 From: kaihsun Date: Sun, 25 Feb 2024 18:13:47 +0000 Subject: [PATCH 21/72] Add support for ResponseHeaderModifier for HTTPRouteRule objects Problem: Users want to add, set, and remove response headers. Solution: Use add_header and proxy_hide_header NGINX directives to support ResponseHeaderModifier. If the action is set, we configure the proxy_hide_header directive to remove the header if exists and add_header directive with the given value in the HTTPRoute spec. If the action is remove, we configure the proxy_hide_header directive to remove the header. If the action is add, we simply configure the add_header directive with the given value in the HTTPRoute spec. --- internal/mode/static/state/graph/httproute.go | 10 +++------- internal/mode/static/state/graph/httproute_test.go | 2 +- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/internal/mode/static/state/graph/httproute.go b/internal/mode/static/state/graph/httproute.go index 6c2a542db1..ad69b1c32c 100644 --- a/internal/mode/static/state/graph/httproute.go +++ b/internal/mode/static/state/graph/httproute.go @@ -247,9 +247,9 @@ func validateFilter( case v1.HTTPRouteFilterURLRewrite: return validateFilterRewrite(validator, filter, filterPath) case v1.HTTPRouteFilterRequestHeaderModifier: - return validateFilterHeaderModifier(filter.Type, validator, filter.RequestHeaderModifier, filterPath) + return validateFilterHeaderModifier(validator, filter.RequestHeaderModifier, filterPath.Child(string(filter.Type))) case v1.HTTPRouteFilterResponseHeaderModifier: - return validateFilterHeaderModifier(filter.Type, validator, filter.ResponseHeaderModifier, filterPath) + return validateFilterHeaderModifier(validator, filter.ResponseHeaderModifier, filterPath.Child(string(filter.Type))) default: valErr := field.NotSupported( filterPath.Child("type"), @@ -360,19 +360,15 @@ func validateFilterRewrite( } func validateFilterHeaderModifier( - filterType v1.HTTPRouteFilterType, validator validation.HTTPFieldsValidator, headerModifier *v1.HTTPHeaderFilter, filterPath *field.Path, ) field.ErrorList { - filterTypeStr := string(filterType) - headerModifierPath := filterPath.Child(filterTypeStr) - if headerModifier == nil { return field.ErrorList{field.Required(filterPath, "cannot be nil")} } - return validateFilterHeaderModifierFields(validator, headerModifier, headerModifierPath) + return validateFilterHeaderModifierFields(validator, headerModifier, filterPath) } func validateFilterHeaderModifierFields( diff --git a/internal/mode/static/state/graph/httproute_test.go b/internal/mode/static/state/graph/httproute_test.go index f2eef51f0d..c22d6556e3 100644 --- a/internal/mode/static/state/graph/httproute_test.go +++ b/internal/mode/static/state/graph/httproute_test.go @@ -1319,7 +1319,7 @@ func TestValidateFilterRequestHeaderModifier(t *testing.T) { t.Run(test.name, func(t *testing.T) { g := NewWithT(t) allErrs := validateFilterHeaderModifier( - gatewayv1.HTTPRouteFilterRequestHeaderModifier, test.validator, test.filter.RequestHeaderModifier, filterPath, + test.validator, test.filter.RequestHeaderModifier, filterPath, ) g.Expect(allErrs).To(HaveLen(test.expectErrCount)) }) From d4f0ea599e333b56b8fd80e1086024e7e9337d1c Mon Sep 17 00:00:00 2001 From: kaihsun Date: Mon, 11 Mar 2024 13:21:40 +0000 Subject: [PATCH 22/72] update --- internal/mode/static/state/graph/httproute.go | 59 +++++- .../mode/static/state/graph/httproute_test.go | 178 ++++++++++++++++++ 2 files changed, 236 insertions(+), 1 deletion(-) diff --git a/internal/mode/static/state/graph/httproute.go b/internal/mode/static/state/graph/httproute.go index ad69b1c32c..0fe5e45e25 100644 --- a/internal/mode/static/state/graph/httproute.go +++ b/internal/mode/static/state/graph/httproute.go @@ -249,7 +249,9 @@ func validateFilter( case v1.HTTPRouteFilterRequestHeaderModifier: return validateFilterHeaderModifier(validator, filter.RequestHeaderModifier, filterPath.Child(string(filter.Type))) case v1.HTTPRouteFilterResponseHeaderModifier: - return validateFilterHeaderModifier(validator, filter.ResponseHeaderModifier, filterPath.Child(string(filter.Type))) + return validateFilterResponseHeaderModifier( + validator, filter.ResponseHeaderModifier, filterPath.Child(string(filter.Type)), + ) default: valErr := field.NotSupported( filterPath.Child("type"), @@ -422,6 +424,61 @@ func validateFilterHeaderModifierFields( return allErrs } +func validateFilterResponseHeaderModifier( + validator validation.HTTPFieldsValidator, + responseHeaderModifier *v1.HTTPHeaderFilter, + filterPath *field.Path, +) field.ErrorList { + if errList := validateFilterHeaderModifier(validator, responseHeaderModifier, filterPath); errList != nil { + return errList + } + var allErrs field.ErrorList + disallowedResponseHeaderSet := map[string]struct{}{ + "server": {}, + "date": {}, + "x-pad": {}, + "content-type": {}, + "content-length": {}, + "connection": {}, + } + invalidPrefix := "x-accel" + for _, h := range responseHeaderModifier.Add { + valErr := field.Invalid(filterPath.Child("add"), h, "header name is not allowed") + name := strings.ToLower(string(h.Name)) + if _, exists := disallowedResponseHeaderSet[name]; exists { + allErrs = append(allErrs, valErr) + } else { + if strings.HasPrefix(name, strings.ToLower(invalidPrefix)) { + allErrs = append(allErrs, valErr) + } + } + } + for _, h := range responseHeaderModifier.Set { + valErr := field.Invalid(filterPath.Child("set"), h, "header name is not allowed") + name := strings.ToLower(string(h.Name)) + if _, exists := disallowedResponseHeaderSet[name]; exists { + allErrs = append(allErrs, valErr) + } else { + if strings.HasPrefix(name, strings.ToLower(invalidPrefix)) { + allErrs = append(allErrs, valErr) + } + } + } + for _, h := range responseHeaderModifier.Remove { + valErr := field.Invalid(filterPath.Child("remove"), h, "header name is not allowed") + name := strings.ToLower(h) + if _, exists := disallowedResponseHeaderSet[name]; exists { + allErrs = append(allErrs, valErr) + } else { + if strings.HasPrefix(name, strings.ToLower(invalidPrefix)) { + allErrs = append(allErrs, valErr) + } + } + } + + return allErrs +} + func validateRequestHeadersCaseInsensitiveUnique( headers []v1.HTTPHeader, path *field.Path, diff --git a/internal/mode/static/state/graph/httproute_test.go b/internal/mode/static/state/graph/httproute_test.go index c22d6556e3..389352f89c 100644 --- a/internal/mode/static/state/graph/httproute_test.go +++ b/internal/mode/static/state/graph/httproute_test.go @@ -1325,3 +1325,181 @@ func TestValidateFilterRequestHeaderModifier(t *testing.T) { }) } } + +func TestValidateFilterResponseHeaderModifier(t *testing.T) { + createAllValidValidator := func() *validationfakes.FakeHTTPFieldsValidator { + v := &validationfakes.FakeHTTPFieldsValidator{} + return v + } + + tests := []struct { + filter gatewayv1.HTTPRouteFilter + validator *validationfakes.FakeHTTPFieldsValidator + name string + expectErrCount int + }{ + { + validator: createAllValidValidator(), + filter: gatewayv1.HTTPRouteFilter{ + Type: gatewayv1.HTTPRouteFilterResponseHeaderModifier, + ResponseHeaderModifier: &gatewayv1.HTTPHeaderFilter{ + Set: []gatewayv1.HTTPHeader{ + {Name: "MyBespokeHeader", Value: "my-value"}, + }, + Add: []gatewayv1.HTTPHeader{ + {Name: "Accept-Encoding", Value: "gzip"}, + }, + Remove: []string{"Cache-Control"}, + }, + }, + expectErrCount: 0, + name: "valid response header modifier filter", + }, + { + validator: createAllValidValidator(), + filter: gatewayv1.HTTPRouteFilter{ + Type: gatewayv1.HTTPRouteFilterResponseHeaderModifier, + ResponseHeaderModifier: nil, + }, + expectErrCount: 1, + name: "nil response header modifier filter", + }, + { + validator: func() *validationfakes.FakeHTTPFieldsValidator { + v := createAllValidValidator() + v.ValidateFilterHeaderNameReturns(errors.New("Invalid header")) + return v + }(), + filter: gatewayv1.HTTPRouteFilter{ + Type: gatewayv1.HTTPRouteFilterResponseHeaderModifier, + ResponseHeaderModifier: &gatewayv1.HTTPHeaderFilter{ + Add: []gatewayv1.HTTPHeader{ + {Name: "$var_name", Value: "gzip"}, + }, + }, + }, + expectErrCount: 1, + name: "response header modifier filter with invalid add", + }, + { + validator: func() *validationfakes.FakeHTTPFieldsValidator { + v := createAllValidValidator() + v.ValidateFilterHeaderNameReturns(errors.New("Invalid header")) + return v + }(), + filter: gatewayv1.HTTPRouteFilter{ + Type: gatewayv1.HTTPRouteFilterResponseHeaderModifier, + ResponseHeaderModifier: &gatewayv1.HTTPHeaderFilter{ + Remove: []string{"$var-name"}, + }, + }, + expectErrCount: 1, + name: "response header modifier filter with invalid remove", + }, + { + validator: func() *validationfakes.FakeHTTPFieldsValidator { + v := createAllValidValidator() + v.ValidateFilterHeaderValueReturns(errors.New("Invalid header value")) + return v + }(), + filter: gatewayv1.HTTPRouteFilter{ + Type: gatewayv1.HTTPRouteFilterResponseHeaderModifier, + ResponseHeaderModifier: &gatewayv1.HTTPHeaderFilter{ + Add: []gatewayv1.HTTPHeader{ + {Name: "Accept-Encoding", Value: "yhu$"}, + }, + }, + }, + expectErrCount: 1, + name: "response header modifier filter with invalid header value", + }, + { + validator: func() *validationfakes.FakeHTTPFieldsValidator { + v := createAllValidValidator() + v.ValidateFilterHeaderValueReturns(errors.New("Invalid header value")) + v.ValidateFilterHeaderNameReturns(errors.New("Invalid header")) + return v + }(), + filter: gatewayv1.HTTPRouteFilter{ + Type: gatewayv1.HTTPRouteFilterResponseHeaderModifier, + ResponseHeaderModifier: &gatewayv1.HTTPHeaderFilter{ + Set: []gatewayv1.HTTPHeader{ + {Name: "Host", Value: "my_host"}, + }, + Add: []gatewayv1.HTTPHeader{ + {Name: "}90yh&$", Value: "gzip$"}, + {Name: "}67yh&$", Value: "compress$"}, + }, + Remove: []string{"Cache-Control$}"}, + }, + }, + expectErrCount: 7, + name: "response header modifier filter all fields invalid", + }, + { + validator: createAllValidValidator(), + filter: gatewayv1.HTTPRouteFilter{ + Type: gatewayv1.HTTPRouteFilterResponseHeaderModifier, + ResponseHeaderModifier: &gatewayv1.HTTPHeaderFilter{ + Set: []gatewayv1.HTTPHeader{ + {Name: "MyBespokeHeader", Value: "my-value"}, + {Name: "mYbespokeHEader", Value: "duplicate"}, + }, + Add: []gatewayv1.HTTPHeader{ + {Name: "Accept-Encoding", Value: "gzip"}, + {Name: "accept-encodING", Value: "gzip"}, + }, + Remove: []string{"Cache-Control", "cache-control"}, + }, + }, + expectErrCount: 3, + name: "response header modifier filter not unique names", + }, + { + validator: createAllValidValidator(), + filter: gatewayv1.HTTPRouteFilter{ + Type: gatewayv1.HTTPRouteFilterResponseHeaderModifier, + ResponseHeaderModifier: &gatewayv1.HTTPHeaderFilter{ + Set: []gatewayv1.HTTPHeader{ + {Name: "Content-Length", Value: "163"}, + }, + Add: []gatewayv1.HTTPHeader{ + {Name: "Content-Type", Value: "text/plain"}, + }, + Remove: []string{"X-Pad"}, + }, + }, + expectErrCount: 3, + name: "invalid response header modifier filter", + }, + { + validator: createAllValidValidator(), + filter: gatewayv1.HTTPRouteFilter{ + Type: gatewayv1.HTTPRouteFilterResponseHeaderModifier, + ResponseHeaderModifier: &gatewayv1.HTTPHeaderFilter{ + Set: []gatewayv1.HTTPHeader{ + {Name: "X-Accel-Redirect", Value: "/protected/iso.img"}, + }, + Add: []gatewayv1.HTTPHeader{ + {Name: "X-Accel-Limit-Rate", Value: "1024"}, + }, + Remove: []string{"X-Accel-Charset"}, + }, + }, + expectErrCount: 3, + name: "invalid response header modifier filter", + }, + } + + filterPath := field.NewPath("test") + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + g := NewWithT(t) + allErrs := validateFilterResponseHeaderModifier( + test.validator, test.filter.ResponseHeaderModifier, filterPath, + ) + g.Expect(allErrs).To(HaveLen(test.expectErrCount)) + }) + } +} From 381fe0454a702afb035726aa88ec2190129d032d Mon Sep 17 00:00:00 2001 From: Saloni Date: Wed, 24 Apr 2024 10:35:04 -0600 Subject: [PATCH 23/72] fix unit tests after rebase --- .../mode/static/nginx/config/http/config.go | 23 +-- .../mode/static/nginx/config/servers_test.go | 186 +++++++++--------- 2 files changed, 103 insertions(+), 106 deletions(-) diff --git a/internal/mode/static/nginx/config/http/config.go b/internal/mode/static/nginx/config/http/config.go index 2202f0961a..f6f0e05643 100644 --- a/internal/mode/static/nginx/config/http/config.go +++ b/internal/mode/static/nginx/config/http/config.go @@ -13,19 +13,16 @@ type Server struct { // Location holds all configuration for an HTTP location. type Location struct { - Path string - ProxyPass string - HTTPMatchKey string - ProxySetHeaders []Header - ProxySSLVerify *ProxySSLVerify - Return *Return - Rewrites []string - GRPC bool - HTTPMatchVar string - AddResponseHeaders []Header - SetResponseHeaders []Header - RemoveResponseHeaders []string - ResponseHeaders ResponseHeaders + Path string + ProxyPass string + HTTPMatchKey string + ProxySetHeaders []Header + ProxySSLVerify *ProxySSLVerify + Return *Return + Rewrites []string + GRPC bool + HTTPMatchVar string + ResponseHeaders ResponseHeaders } // Header defines an HTTP header to be passed to the proxied server. diff --git a/internal/mode/static/nginx/config/servers_test.go b/internal/mode/static/nginx/config/servers_test.go index 765c0abc36..b139b7acbc 100644 --- a/internal/mode/static/nginx/config/servers_test.go +++ b/internal/mode/static/nginx/config/servers_test.go @@ -612,48 +612,48 @@ func TestCreateServers(t *testing.T) { return []http.Location{ { - Path: "@rule0-route0", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: baseHeaders, - AddHeaderDirectives: baseHeaders, + Path: "@rule0-route0", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: baseHeaders, + ResponseHeaders: http.ResponseHeaders{}, }, { - Path: "@rule0-route1", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: baseHeaders, - AddHeaderDirectives: baseHeaders, + Path: "@rule0-route1", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: baseHeaders, + ResponseHeaders: http.ResponseHeaders{}, }, { - Path: "@rule0-route2", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: baseHeaders, - AddHeaderDirectives: baseHeaders, + Path: "@rule0-route2", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: baseHeaders, + ResponseHeaders: http.ResponseHeaders{}, }, { Path: "/", HTTPMatchKey: ssl + "1_0", }, { - Path: "@rule1-route0", - ProxyPass: "http://$test__route1_rule1$request_uri", - ProxySetHeaders: baseHeaders, - AddHeaderDirectives: baseHeaders, + Path: "@rule1-route0", + ProxyPass: "http://$test__route1_rule1$request_uri", + ProxySetHeaders: baseHeaders, + ResponseHeaders: http.ResponseHeaders{}, }, { Path: "/test/", HTTPMatchKey: ssl + "1_1", }, { - Path: "/path-only/", - ProxyPass: "http://invalid-backend-ref$request_uri", - ProxySetHeaders: baseHeaders, - AddHeaderDirectives: baseHeaders, + Path: "/path-only/", + ProxyPass: "http://invalid-backend-ref$request_uri", + ProxySetHeaders: baseHeaders, + ResponseHeaders: http.ResponseHeaders{}, }, { - Path: "= /path-only", - ProxyPass: "http://invalid-backend-ref$request_uri", - ProxySetHeaders: baseHeaders, - AddHeaderDirectives: baseHeaders, + Path: "= /path-only", + ProxyPass: "http://invalid-backend-ref$request_uri", + ProxySetHeaders: baseHeaders, + ResponseHeaders: http.ResponseHeaders{}, }, { Path: "/backend-tls-policy/", @@ -717,25 +717,25 @@ func TestCreateServers(t *testing.T) { HTTPMatchKey: ssl + "1_6", }, { - Path: "/rewrite/", - Rewrites: []string{"^ /replacement break"}, - ProxyPass: "http://test_foo_80", - ProxySetHeaders: rewriteProxySetHeaders, - AddHeaderDirectives: baseHeaders, + Path: "/rewrite/", + Rewrites: []string{"^ /replacement break"}, + ProxyPass: "http://test_foo_80", + ProxySetHeaders: rewriteProxySetHeaders, + ResponseHeaders: http.ResponseHeaders{}, }, { - Path: "= /rewrite", - Rewrites: []string{"^ /replacement break"}, - ProxyPass: "http://test_foo_80", - ProxySetHeaders: rewriteProxySetHeaders, - AddHeaderDirectives: baseHeaders, + Path: "= /rewrite", + Rewrites: []string{"^ /replacement break"}, + ProxyPass: "http://test_foo_80", + ProxySetHeaders: rewriteProxySetHeaders, + ResponseHeaders: http.ResponseHeaders{}, }, { - Path: "@rule7-route0", - Rewrites: []string{"^/rewrite-with-headers(.*)$ /prefix-replacement$1 break"}, - ProxyPass: "http://test_foo_80", - ProxySetHeaders: rewriteProxySetHeaders, - AddHeaderDirectives: baseHeaders, + Path: "@rule8-route0", + Rewrites: []string{"^/rewrite-with-headers(.*)$ /prefix-replacement$1 break"}, + ProxyPass: "http://test_foo_80", + ProxySetHeaders: rewriteProxySetHeaders, + ResponseHeaders: http.ResponseHeaders{}, }, { Path: "/rewrite-with-headers/", @@ -772,16 +772,16 @@ func TestCreateServers(t *testing.T) { HTTPMatchKey: ssl + "1_10", }, { - Path: "= /exact", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: baseHeaders, - AddHeaderDirectives: baseHeaders, + Path: "= /exact", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: baseHeaders, + ResponseHeaders: http.ResponseHeaders{}, }, { - Path: "@rule11-route0", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: baseHeaders, - AddHeaderDirectives: baseHeaders, + Path: "@rule12-route0", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: baseHeaders, + ResponseHeaders: http.ResponseHeaders{}, }, { Path: "= /test", @@ -812,7 +812,7 @@ func TestCreateServers(t *testing.T) { Value: "$connection_upgrade", }, }, - AddHeaderDirectives: baseHeaders, + ResponseHeaders: http.ResponseHeaders{}, }, { Path: "= /proxy-set-headers", @@ -839,7 +839,7 @@ func TestCreateServers(t *testing.T) { Value: "$connection_upgrade", }, }, - AddHeaderDirectives: baseHeaders, + ResponseHeaders: http.ResponseHeaders{}, }, { Path: "= /grpc/method", @@ -973,16 +973,16 @@ func TestCreateServersConflicts(t *testing.T) { }, expLocs: []http.Location{ { - Path: "/coffee/", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: baseHeaders, - AddHeaderDirectives: baseHeaders, + Path: "/coffee/", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: baseHeaders, + ResponseHeaders: http.ResponseHeaders{}, }, { - Path: "= /coffee", - ProxyPass: "http://test_bar_80$request_uri", - ProxySetHeaders: baseHeaders, - AddHeaderDirectives: baseHeaders, + Path: "= /coffee", + ProxyPass: "http://test_bar_80$request_uri", + ProxySetHeaders: baseHeaders, + ResponseHeaders: http.ResponseHeaders{}, }, createDefaultRootLocation(), }, @@ -1013,16 +1013,16 @@ func TestCreateServersConflicts(t *testing.T) { }, expLocs: []http.Location{ { - Path: "= /coffee", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: baseHeaders, - AddHeaderDirectives: baseHeaders, + Path: "= /coffee", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: baseHeaders, + ResponseHeaders: http.ResponseHeaders{}, }, { - Path: "/coffee/", - ProxyPass: "http://test_bar_80$request_uri", - ProxySetHeaders: baseHeaders, - AddHeaderDirectives: baseHeaders, + Path: "/coffee/", + ProxyPass: "http://test_bar_80$request_uri", + ProxySetHeaders: baseHeaders, + ResponseHeaders: http.ResponseHeaders{}, }, createDefaultRootLocation(), }, @@ -1063,16 +1063,16 @@ func TestCreateServersConflicts(t *testing.T) { }, expLocs: []http.Location{ { - Path: "/coffee/", - ProxyPass: "http://test_bar_80$request_uri", - ProxySetHeaders: baseHeaders, - AddHeaderDirectives: baseHeaders, + Path: "/coffee/", + ProxyPass: "http://test_bar_80$request_uri", + ProxySetHeaders: baseHeaders, + ResponseHeaders: http.ResponseHeaders{}, }, { - Path: "= /coffee", - ProxyPass: "http://test_baz_80$request_uri", - ProxySetHeaders: baseHeaders, - AddHeaderDirectives: baseHeaders, + Path: "= /coffee", + ProxyPass: "http://test_baz_80$request_uri", + ProxySetHeaders: baseHeaders, + ResponseHeaders: http.ResponseHeaders{}, }, createDefaultRootLocation(), }, @@ -1188,16 +1188,16 @@ func TestCreateLocationsRootPath(t *testing.T) { pathRules: getPathRules(false /* rootPath */, false /* grpc */), expLocations: []http.Location{ { - Path: "/path-1", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: baseHeaders, - AddHeaderDirectives: baseHeaders, + Path: "/path-1", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: baseHeaders, + ResponseHeaders: http.ResponseHeaders{}, }, { - Path: "/path-2", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: baseHeaders, - AddHeaderDirectives: baseHeaders, + Path: "/path-2", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: baseHeaders, + ResponseHeaders: http.ResponseHeaders{}, }, { Path: "/", @@ -1240,22 +1240,22 @@ func TestCreateLocationsRootPath(t *testing.T) { pathRules: getPathRules(true /* rootPath */, false /* grpc */), expLocations: []http.Location{ { - Path: "/path-1", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: baseHeaders, - AddHeaderDirectives: baseHeaders, + Path: "/path-1", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: baseHeaders, + ResponseHeaders: http.ResponseHeaders{}, }, { - Path: "/path-2", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: baseHeaders, - AddHeaderDirectives: baseHeaders, + Path: "/path-2", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: baseHeaders, + ResponseHeaders: http.ResponseHeaders{}, }, { - Path: "/", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: baseHeaders, - AddHeaderDirectives: baseHeaders, + Path: "/", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: baseHeaders, + ResponseHeaders: http.ResponseHeaders{}, }, }, }, From 387f05a4c1a6e81fa63a394b5f7dfb4067895175 Mon Sep 17 00:00:00 2001 From: Saloni Date: Mon, 29 Apr 2024 17:44:16 -0600 Subject: [PATCH 24/72] address nit comments and add unit tests --- .../mode/static/nginx/config/servers_test.go | 68 +++++++++++++++++++ .../mode/static/state/graph/httproute_test.go | 4 +- 2 files changed, 70 insertions(+), 2 deletions(-) diff --git a/internal/mode/static/nginx/config/servers_test.go b/internal/mode/static/nginx/config/servers_test.go index b139b7acbc..5897b892c3 100644 --- a/internal/mode/static/nginx/config/servers_test.go +++ b/internal/mode/static/nginx/config/servers_test.go @@ -2078,3 +2078,71 @@ func TestConvertBackendTLSFromGroup(t *testing.T) { }) } } + +func TestGenerateResponseHeaders(t *testing.T) { + tests := []struct { + filters *dataplane.HTTPFilters + msg string + expectedHeaders http.ResponseHeaders + }{ + { + msg: "no filter set", + filters: &dataplane.HTTPFilters{ + RequestHeaderModifiers: &dataplane.HTTPHeaderFilter{}, + }, + expectedHeaders: http.ResponseHeaders{}, + }, + { + msg: "set filters correctly", + filters: &dataplane.HTTPFilters{ + ResponseHeaderModifiers: &dataplane.HTTPHeaderFilter{ + Add: []dataplane.HTTPHeader{ + { + Name: "Accept-Encoding", + Value: "gzip", + }, + { + Name: "Authorization", + Value: "my-auth", + }, + }, + Set: []dataplane.HTTPHeader{ + { + Name: "Accept-Encoding", + Value: "my-new-overwritten-value", + }, + }, + Remove: []string{"Authorization"}, + }, + }, + expectedHeaders: http.ResponseHeaders{ + Add: []http.Header{ + { + Name: "Accept-Encoding", + Value: "gzip", + }, + { + Name: "Authorization", + Value: "my-auth", + }, + }, + Set: []http.Header{ + { + Name: "Accept-Encoding", + Value: "my-new-overwritten-value", + }, + }, + Remove: []string{"Authorization"}, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.msg, func(t *testing.T) { + g := NewWithT(t) + + headers := generateResponseHeaders(tc.filters) + g.Expect(headers).To(Equal(tc.expectedHeaders)) + }) + } +} diff --git a/internal/mode/static/state/graph/httproute_test.go b/internal/mode/static/state/graph/httproute_test.go index 389352f89c..3818fd345b 100644 --- a/internal/mode/static/state/graph/httproute_test.go +++ b/internal/mode/static/state/graph/httproute_test.go @@ -1470,7 +1470,7 @@ func TestValidateFilterResponseHeaderModifier(t *testing.T) { }, }, expectErrCount: 3, - name: "invalid response header modifier filter", + name: "response header modifier filter with disallowed header name", }, { validator: createAllValidValidator(), @@ -1487,7 +1487,7 @@ func TestValidateFilterResponseHeaderModifier(t *testing.T) { }, }, expectErrCount: 3, - name: "invalid response header modifier filter", + name: "response header modifier filter with disallowed header name prefix", }, } From decffb312ebb53f5e673d8bc7bf3dbdfc1b3006e Mon Sep 17 00:00:00 2001 From: Saloni Date: Wed, 1 May 2024 09:30:14 -0600 Subject: [PATCH 25/72] add docs for http filters --- examples/http-header-filter/README.md | 14 +- examples/http-header-filter/echo-route.yaml | 12 ++ .../request-and-response-headers.md | 158 ++++++++++++++++++ 3 files changed, 182 insertions(+), 2 deletions(-) create mode 100644 site/content/how-to/traffic-management/request-and-response-headers.md diff --git a/examples/http-header-filter/README.md b/examples/http-header-filter/README.md index fcb8ea1e6f..cee0e3af04 100644 --- a/examples/http-header-filter/README.md +++ b/examples/http-header-filter/README.md @@ -57,11 +57,14 @@ headers to the request. ## 4. Test the Application -To access the application, we will use `curl` to send requests to the `headers` Service, including sending headers with -our request. +To access the application, we will use `curl` to send requests to the `headers` Service, including sending headers with our request. Notice our configured header values can be seen in the `requestHeaders` section below, and that the `User-Agent` header is absent. + +1. We will send the curl request to modify request headers. Notice our configured request header values can be seen in the `requestHeaders` section below, and that the `Accept` header +is absent. + ```shell curl -s --resolve echo.example.com:$GW_PORT:$GW_IP http://echo.example.com:$GW_PORT/headers -H "My-Cool-Header:my-client-value" -H "My-Overwrite-Header:dont-see-this" ``` @@ -76,3 +79,10 @@ Headers: header 'Connection' is 'close' header 'Accept' is '*/*' ``` + + +1. We will send the curl request to modify response headers. Notice our configured response header values can be seen in the `responseHeaders` section below, and that the `User-Agent` header +is absent. + + +// TODO diff --git a/examples/http-header-filter/echo-route.yaml b/examples/http-header-filter/echo-route.yaml index 43ed5d52db..a339e800c0 100644 --- a/examples/http-header-filter/echo-route.yaml +++ b/examples/http-header-filter/echo-route.yaml @@ -26,6 +26,18 @@ spec: value: this-is-an-appended-value remove: - User-Agent + - type: ResponseHeaderModifier + responseHeaderModifier: + set: + - name: My-Overwrite-Header-Response + value: this-is-the-only-value-response + add: + - name: Accept-Encoding-Response + value: compress-response + - name: My-cool-header-Response + value: this-is-an-appended-value-response + remove: + - Accept backendRefs: - name: headers port: 80 diff --git a/site/content/how-to/traffic-management/request-and-response-headers.md b/site/content/how-to/traffic-management/request-and-response-headers.md new file mode 100644 index 0000000000..f333537bbe --- /dev/null +++ b/site/content/how-to/traffic-management/request-and-response-headers.md @@ -0,0 +1,158 @@ +--- +title: "HTTP Request and Response Headers" +description: "Learn how to modify request and response headers for your HTTP Route using NGINX Gateway Fabric." +weight: 700 +toc: true +docs: "" +--- + +[HTTPRoute](https://gateway-api.sigs.k8s.io/api-types/httproute/) filters can modify the headers during the request-response lifecycle. [HTTP Header Modifiers](https://gateway-api.sigs.k8s.io/guides/http-header-modifier/?h=request#http-header-modifiers) can be used to add, modify or remove headers in incoming requests. We have two types of [filter](https://gateway-api.sigs.k8s.io/api-types/httproute/#filters-optional) that can be used to instruct the Gateway for desired behaviour. + +1. [RequestHeaderModifier](https://gateway-api.sigs.k8s.io/guides/http-header-modifier/?h=request#http-request-header-modifier) to alter headers in request before forwarding the request upstream. +1. [ResponseHeaderModifier](https://gateway-api.sigs.k8s.io/guides/http-header-modifier/?h=request#http-response-header-modifier) to alter headers in response before responding to the downstream. + + +In this guide we will modify the headers of HTTP request and HTTP responses from clients. For an introduction to exposing your application, we recommend that you follow the [basic guide]({{< relref "/how-to/traffic-management/routing-traffic-to-your-app.md" >}}) first. + + +## Prerequisites + +- [Install]({{< relref "/installation/" >}}) NGINX Gateway Fabric. +- [Expose NGINX Gateway Fabric]({{< relref "installation/expose-nginx-gateway-fabric.md" >}}) and save the public IP + address and port of NGINX Gateway Fabric into shell variables: + + ```text + GW_IP=XXX.YYY.ZZZ.III + GW_PORT= + ``` + +{{< note >}}In a production environment, you should have a DNS record for the external IP address that is exposed, and it should refer to the hostname that the gateway will forward for.{{< /note >}} + +## Echo Application + +### Deploy the Headers Application + +Begin by deploying the `headers` application: + +```shell +kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric/v1.2.0/examples/http-header-filter/headers.yaml +``` + +### Deploy the Gateway API Resources for the Headers Applications + +The [gateway](https://gateway-api.sigs.k8s.io/api-types/gateway/) resource is typically deployed by the [cluster operator](https://gateway-api.sigs.k8s.io/concepts/roles-and-personas/#roles-and-personas_1). To deploy the gateway: + +```yaml +kubectl apply -f - <}}If the request does not have the header configured by the filter, then that header will be added to the request. If the request already has the header configured by the filter, then the value of the header in the filter will be appended to the value of the header in the request.{{< /note >}} + +### Send Traffic to Headers + +To access the application, we will use `curl` to send requests to the `headers` Service, including sending headers with our request. + +{{< note >}}If you have a DNS record allocated for `echo.example.com`, you can send the request directly to that hostname, without needing to resolve.{{< /note >}} + + +1. Send traffic to headers to modify request headers. + +```shell +curl -s --resolve echo.example.com:$GW_PORT:$GW_IP http://echo.example.com:$GW_PORT/headers -H "My-Cool-Header:my-client-value" -H "My-Overwrite-Header:dont-see-this" +``` + +```text +Headers: + header 'Accept-Encoding' is 'compress' + header 'My-cool-header' is 'my-client-value, this-is-an-appended-value' + header 'My-Overwrite-Header' is 'this-is-the-only-value' + header 'Host' is 'echo.example.com:$GW_PORT' + header 'X-Forwarded-For' is '$GW_IP' + header 'Connection' is 'close' + header 'Accept' is '*/*' +``` + +```shell +curl -v --resolve echo.example.com:$GW_PORT:$GW_IP http://echo.example.com:$GW_PORT/headers +``` + + +1. Send traffic to headers to modify response headers. + + +// TODO From 7a05dbc3f5f1c32f3aae920c9b1d3ebf8748e78c Mon Sep 17 00:00:00 2001 From: Saloni Date: Wed, 1 May 2024 12:33:10 -0600 Subject: [PATCH 26/72] fix lint issues --- internal/mode/static/nginx/config/http/config.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/mode/static/nginx/config/http/config.go b/internal/mode/static/nginx/config/http/config.go index f6f0e05643..cae46e1b8d 100644 --- a/internal/mode/static/nginx/config/http/config.go +++ b/internal/mode/static/nginx/config/http/config.go @@ -16,13 +16,13 @@ type Location struct { Path string ProxyPass string HTTPMatchKey string + HTTPMatchVar string + Rewrites []string ProxySetHeaders []Header ProxySSLVerify *ProxySSLVerify Return *Return - Rewrites []string - GRPC bool - HTTPMatchVar string ResponseHeaders ResponseHeaders + GRPC bool } // Header defines an HTTP header to be passed to the proxied server. From 152c799b2593e1e9a26e74efa9af43ea62ea6d9f Mon Sep 17 00:00:00 2001 From: Saloni Date: Wed, 1 May 2024 19:28:17 -0600 Subject: [PATCH 27/72] add tutorial for response headers --- .../README.md | 23 ++-- .../echo-route.yaml | 12 -- .../gateway.yaml | 0 .../headers.yaml | 0 .../http-response-header-filter/README.md | 79 +++++++++++ .../echo-route.yaml | 29 ++++ .../http-response-header-filter/gateway.yaml | 10 ++ .../http-response-header-filter/headers.yaml | 71 ++++++++++ .../request-and-response-headers.md | 129 +----------------- 9 files changed, 204 insertions(+), 149 deletions(-) rename examples/{http-header-filter => http-request-header-filter}/README.md (76%) rename examples/{http-header-filter => http-request-header-filter}/echo-route.yaml (63%) rename examples/{http-header-filter => http-request-header-filter}/gateway.yaml (100%) rename examples/{http-header-filter => http-request-header-filter}/headers.yaml (100%) create mode 100644 examples/http-response-header-filter/README.md create mode 100644 examples/http-response-header-filter/echo-route.yaml create mode 100644 examples/http-response-header-filter/gateway.yaml create mode 100644 examples/http-response-header-filter/headers.yaml diff --git a/examples/http-header-filter/README.md b/examples/http-request-header-filter/README.md similarity index 76% rename from examples/http-header-filter/README.md rename to examples/http-request-header-filter/README.md index cee0e3af04..706489fc9b 100644 --- a/examples/http-header-filter/README.md +++ b/examples/http-request-header-filter/README.md @@ -22,7 +22,7 @@ headers to the request. GW_PORT= ``` -## 2. Deploy the Cafe Application +## 2. Deploy the Headers Application 1. Create the headers Deployment and Service: @@ -55,14 +55,18 @@ headers to the request. kubectl apply -f echo-route.yaml ``` -## 4. Test the Application +This HTTPRoute has a few important properties: -To access the application, we will use `curl` to send requests to the `headers` Service, including sending headers with our request. -Notice our configured header values can be seen in the `requestHeaders` section below, and that the `User-Agent` header -is absent. +- The `parentRefs` references the gateway resource that we created, and specifically defines the `http` listener to attach to, via the `sectionName` field. +- `echo.example.com` is the hostname that is matched for all requests to the backends defined in this HTTPRoute. +- The `match` rule defines that all requests with the path prefix `/headers` are sent to the `headers` Service. +- There is `RequestHeaderModifier` filter defined for the path prefix `/headers`. +## 4. Test the Application -1. We will send the curl request to modify request headers. Notice our configured request header values can be seen in the `requestHeaders` section below, and that the `Accept` header +To access the application, we will use `curl` to send requests to the `headers` Service, including sending headers with +our request. +Notice our configured header values can be seen in the `requestHeaders` section below, and that the `User-Agent` header is absent. ```shell @@ -79,10 +83,3 @@ Headers: header 'Connection' is 'close' header 'Accept' is '*/*' ``` - - -1. We will send the curl request to modify response headers. Notice our configured response header values can be seen in the `responseHeaders` section below, and that the `User-Agent` header -is absent. - - -// TODO diff --git a/examples/http-header-filter/echo-route.yaml b/examples/http-request-header-filter/echo-route.yaml similarity index 63% rename from examples/http-header-filter/echo-route.yaml rename to examples/http-request-header-filter/echo-route.yaml index a339e800c0..43ed5d52db 100644 --- a/examples/http-header-filter/echo-route.yaml +++ b/examples/http-request-header-filter/echo-route.yaml @@ -26,18 +26,6 @@ spec: value: this-is-an-appended-value remove: - User-Agent - - type: ResponseHeaderModifier - responseHeaderModifier: - set: - - name: My-Overwrite-Header-Response - value: this-is-the-only-value-response - add: - - name: Accept-Encoding-Response - value: compress-response - - name: My-cool-header-Response - value: this-is-an-appended-value-response - remove: - - Accept backendRefs: - name: headers port: 80 diff --git a/examples/http-header-filter/gateway.yaml b/examples/http-request-header-filter/gateway.yaml similarity index 100% rename from examples/http-header-filter/gateway.yaml rename to examples/http-request-header-filter/gateway.yaml diff --git a/examples/http-header-filter/headers.yaml b/examples/http-request-header-filter/headers.yaml similarity index 100% rename from examples/http-header-filter/headers.yaml rename to examples/http-request-header-filter/headers.yaml diff --git a/examples/http-response-header-filter/README.md b/examples/http-response-header-filter/README.md new file mode 100644 index 0000000000..d00d708620 --- /dev/null +++ b/examples/http-response-header-filter/README.md @@ -0,0 +1,79 @@ +# Example + +In this example we will deploy NGINX Gateway Fabric and configure traffic routing for a simple echo server. +We will use HTTPRoute resources to route traffic to the echo server, using the `RequestHeaderModifier` filter to modify +headers to the request. + +## Running the Example + +## 1. Deploy NGINX Gateway Fabric + +1. Follow the [installation instructions](https://docs.nginx.com/nginx-gateway-fabric/installation/) to deploy NGINX Gateway Fabric. + +1. Save the public IP address of NGINX Gateway Fabric into a shell variable: + + ```text + GW_IP=XXX.YYY.ZZZ.III + ``` + +1. Save the port of NGINX Gateway Fabric: + + ```text + GW_PORT= + ``` + +## 2. Deploy the Headers Application + +1. Create the headers Deployment and Service: + + ```shell + kubectl apply -f headers.yaml + ``` + +1. Check that the Pod is running in the `default` Namespace: + + ```shell + kubectl -n default get pods + ``` + + ```text + NAME READY STATUS RESTARTS AGE + headers-6f4b79b975-2sb28 1/1 Running 0 12s + ``` + +## 3. Configure Routing + +1. Create the Gateway: + + ```shell + kubectl apply -f gateway.yaml + ``` + +1. Create the HTTPRoute resources: + + ```shell + kubectl apply -f echo-route.yaml + ``` + +## 4. Test the Application + +To access the application, we will use `curl` to send requests to the `headers` Service, including sending headers with our request. + + +Notice our configured header values can be seen in the `responseHeaders` section below, and that the `Header-to-remove` header is absent. The header `My-cool-header` gets the appended with value `respond-with-this` from the `responseHeaderModifier` filter and the value of header `Response-Overwrite-Header` gets overwritten as defined in the *HttpRoute*. + +```shell +curl -s -i --resolve echo.example.com:$GW_PORT:$GW_IP http://echo.example.com:$GW_PORT/headers -H "Header-to-remove:remove-this-header" +``` + +```text +HTTP/1.1 200 OK +Server: nginx/1.25.5 +Date: Thu, 02 May 2024 01:11:37 GMT +Content-Type: text/plain +Content-Length: 2 +Connection: keep-alive +My-cool-header: this-is-the-value +My-cool-header: respond-with-this +Response-Overwrite-Header: overwritten-value +``` diff --git a/examples/http-response-header-filter/echo-route.yaml b/examples/http-response-header-filter/echo-route.yaml new file mode 100644 index 0000000000..618f8807e0 --- /dev/null +++ b/examples/http-response-header-filter/echo-route.yaml @@ -0,0 +1,29 @@ +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: headers +spec: + parentRefs: + - name: gateway + sectionName: http + hostnames: + - "echo.example.com" + rules: + - matches: + - path: + type: PathPrefix + value: /headers + filters: + - type: ResponseHeaderModifier + responseHeaderModifier: + set: + - name: Response-Overwrite-Header + value: overwritten-value + add: + - name: My-cool-header + value: respond-with-this + remove: + - Header-to-remove + backendRefs: + - name: headers + port: 80 diff --git a/examples/http-response-header-filter/gateway.yaml b/examples/http-response-header-filter/gateway.yaml new file mode 100644 index 0000000000..9d402bd5a1 --- /dev/null +++ b/examples/http-response-header-filter/gateway.yaml @@ -0,0 +1,10 @@ +apiVersion: gateway.networking.k8s.io/v1 +kind: Gateway +metadata: + name: gateway +spec: + gatewayClassName: nginx + listeners: + - name: http + port: 80 + protocol: HTTP diff --git a/examples/http-response-header-filter/headers.yaml b/examples/http-response-header-filter/headers.yaml new file mode 100644 index 0000000000..556e80f85d --- /dev/null +++ b/examples/http-response-header-filter/headers.yaml @@ -0,0 +1,71 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: headers +spec: + replicas: 1 + selector: + matchLabels: + app: headers + template: + metadata: + labels: + app: headers + spec: + containers: + - name: headers + image: nginx + ports: + - containerPort: 8080 + volumeMounts: + - name: config-volume + mountPath: /etc/nginx + readOnly: true + volumes: + - name: config-volume + configMap: + name: headers-config +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: headers-config +# yamllint disable rule:indentation +data: + nginx.conf: |- + user nginx; + worker_processes 1; + + pid /var/run/nginx.pid; + + load_module /usr/lib/nginx/modules/ngx_http_js_module.so; + + events {} + + http { + default_type text/plain; + + server { + listen 8080; + add_header My-cool-header "this-is-the-value" always; + + proxy_hide_header Response-Overwrite-Header; + add_header Response-Overwrite-Header "this-is-the-actual-value" always; + + return 200 "ok"; + } + } +# yamllint enable rule:indentation +--- +apiVersion: v1 +kind: Service +metadata: + name: headers +spec: + ports: + - port: 80 + targetPort: 8080 + protocol: TCP + name: http + selector: + app: headers diff --git a/site/content/how-to/traffic-management/request-and-response-headers.md b/site/content/how-to/traffic-management/request-and-response-headers.md index f333537bbe..e9ee7b31cf 100644 --- a/site/content/how-to/traffic-management/request-and-response-headers.md +++ b/site/content/how-to/traffic-management/request-and-response-headers.md @@ -9,6 +9,7 @@ docs: "" [HTTPRoute](https://gateway-api.sigs.k8s.io/api-types/httproute/) filters can modify the headers during the request-response lifecycle. [HTTP Header Modifiers](https://gateway-api.sigs.k8s.io/guides/http-header-modifier/?h=request#http-header-modifiers) can be used to add, modify or remove headers in incoming requests. We have two types of [filter](https://gateway-api.sigs.k8s.io/api-types/httproute/#filters-optional) that can be used to instruct the Gateway for desired behaviour. 1. [RequestHeaderModifier](https://gateway-api.sigs.k8s.io/guides/http-header-modifier/?h=request#http-request-header-modifier) to alter headers in request before forwarding the request upstream. + 1. [ResponseHeaderModifier](https://gateway-api.sigs.k8s.io/guides/http-header-modifier/?h=request#http-response-header-modifier) to alter headers in response before responding to the downstream. @@ -28,131 +29,11 @@ In this guide we will modify the headers of HTTP request and HTTP responses from {{< note >}}In a production environment, you should have a DNS record for the external IP address that is exposed, and it should refer to the hostname that the gateway will forward for.{{< /note >}} -## Echo Application - -### Deploy the Headers Application - -Begin by deploying the `headers` application: - -```shell -kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric/v1.2.0/examples/http-header-filter/headers.yaml -``` - -### Deploy the Gateway API Resources for the Headers Applications - -The [gateway](https://gateway-api.sigs.k8s.io/api-types/gateway/) resource is typically deployed by the [cluster operator](https://gateway-api.sigs.k8s.io/concepts/roles-and-personas/#roles-and-personas_1). To deploy the gateway: - -```yaml -kubectl apply -f - <}}If the request does not have the header configured by the filter, then that header will be added to the request. If the request already has the header configured by the filter, then the value of the header in the filter will be appended to the value of the header in the request.{{< /note >}} - -### Send Traffic to Headers - -To access the application, we will use `curl` to send requests to the `headers` Service, including sending headers with our request. - -{{< note >}}If you have a DNS record allocated for `echo.example.com`, you can send the request directly to that hostname, without needing to resolve.{{< /note >}} - - -1. Send traffic to headers to modify request headers. - -```shell -curl -s --resolve echo.example.com:$GW_PORT:$GW_IP http://echo.example.com:$GW_PORT/headers -H "My-Cool-Header:my-client-value" -H "My-Overwrite-Header:dont-see-this" -``` - -```text -Headers: - header 'Accept-Encoding' is 'compress' - header 'My-cool-header' is 'my-client-value, this-is-an-appended-value' - header 'My-Overwrite-Header' is 'this-is-the-only-value' - header 'Host' is 'echo.example.com:$GW_PORT' - header 'X-Forwarded-For' is '$GW_IP' - header 'Connection' is 'close' - header 'Accept' is '*/*' -``` - -```shell -curl -v --resolve echo.example.com:$GW_PORT:$GW_IP http://echo.example.com:$GW_PORT/headers -``` +## Request Header Filter +Follow this [guide](https://github.com/nginxinc/nginx-gateway-fabric/tree/main/examples/http-request-header-filter/README.md) to modify request headers for a client request. -1. Send traffic to headers to modify response headers. +## Response Header Filter -// TODO +Follow this [guide](https://github.com/nginxinc/nginx-gateway-fabric/tree/main/examples/http-response-header-filter/README.md) to modify response headers for a client request. From 8239e315e3bcf16a8f223f0601a135511e5ba325 Mon Sep 17 00:00:00 2001 From: Saloni Date: Wed, 1 May 2024 19:29:53 -0600 Subject: [PATCH 28/72] line spacing --- .../how-to/traffic-management/request-and-response-headers.md | 1 + 1 file changed, 1 insertion(+) diff --git a/site/content/how-to/traffic-management/request-and-response-headers.md b/site/content/how-to/traffic-management/request-and-response-headers.md index e9ee7b31cf..11fed16c9b 100644 --- a/site/content/how-to/traffic-management/request-and-response-headers.md +++ b/site/content/how-to/traffic-management/request-and-response-headers.md @@ -8,6 +8,7 @@ docs: "" [HTTPRoute](https://gateway-api.sigs.k8s.io/api-types/httproute/) filters can modify the headers during the request-response lifecycle. [HTTP Header Modifiers](https://gateway-api.sigs.k8s.io/guides/http-header-modifier/?h=request#http-header-modifiers) can be used to add, modify or remove headers in incoming requests. We have two types of [filter](https://gateway-api.sigs.k8s.io/api-types/httproute/#filters-optional) that can be used to instruct the Gateway for desired behaviour. + 1. [RequestHeaderModifier](https://gateway-api.sigs.k8s.io/guides/http-header-modifier/?h=request#http-request-header-modifier) to alter headers in request before forwarding the request upstream. 1. [ResponseHeaderModifier](https://gateway-api.sigs.k8s.io/guides/http-header-modifier/?h=request#http-response-header-modifier) to alter headers in response before responding to the downstream. From 7f404578b6deb525f8ae2c8072bc18a2a653ce77 Mon Sep 17 00:00:00 2001 From: Saloni Date: Thu, 2 May 2024 10:55:24 -0600 Subject: [PATCH 29/72] fix docs and add unit tests --- .../http-response-header-filter/README.md | 7 +++ .../mode/static/nginx/config/servers_test.go | 54 +++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/examples/http-response-header-filter/README.md b/examples/http-response-header-filter/README.md index d00d708620..2182d426d8 100644 --- a/examples/http-response-header-filter/README.md +++ b/examples/http-response-header-filter/README.md @@ -55,6 +55,13 @@ headers to the request. kubectl apply -f echo-route.yaml ``` +This HTTPRoute has a few important properties: + +- The `parentRefs` references the gateway resource that we created, and specifically defines the `http` listener to attach to, via the `sectionName` field. +- `echo.example.com` is the hostname that is matched for all requests to the backends defined in this HTTPRoute. +- The `match` rule defines that all requests with the path prefix `/headers` are sent to the `headers` Service. +- There is `ResponseHeaderModifier` filter defined for the path prefix `/headers` to set header `Response-Overwrite-Header` and add header `My-cool-header`. + ## 4. Test the Application To access the application, we will use `curl` to send requests to the `headers` Service, including sending headers with our request. diff --git a/internal/mode/static/nginx/config/servers_test.go b/internal/mode/static/nginx/config/servers_test.go index 5897b892c3..d121f8d32a 100644 --- a/internal/mode/static/nginx/config/servers_test.go +++ b/internal/mode/static/nginx/config/servers_test.go @@ -489,6 +489,26 @@ func TestCreateServers(t *testing.T) { }, }, }, + { + Path: "/add-headers", + PathType: dataplane.PathTypePrefix, + MatchRules: []dataplane.MatchRule{ + { + Match: dataplane.Match{}, + BackendGroup: fooGroup, + Filters: dataplane.HTTPFilters{ + ResponseHeaderModifiers: &dataplane.HTTPHeaderFilter{ + Add: []dataplane.HTTPHeader{ + { + Name: "my-response-header", + Value: "some-value-response", + }, + }, + }, + }, + }, + }, + }, { Path: "/grpc/method", PathType: dataplane.PathTypeExact, @@ -841,6 +861,40 @@ func TestCreateServers(t *testing.T) { }, ResponseHeaders: http.ResponseHeaders{}, }, + { + Path: "/add-headers/", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: []http.Header{ + {Name: "Host", Value: "$gw_api_compliant_host"}, + {Name: "X-Forwarded-For", Value: "$proxy_add_x_forwarded_for"}, + {Name: "Upgrade", Value: "$http_upgrade"}, + {Name: "Connection", Value: "$connection_upgrade"}, + }, + ResponseHeaders: http.ResponseHeaders{ + Add: []http.Header{ + {Name: "my-response-header", Value: "some-value-response"}, + }, + Set: []http.Header{}, + Remove: []string{}, + }, + }, + { + Path: "= /add-headers", + ProxyPass: "http://test_foo_80$request_uri", + ProxySetHeaders: []http.Header{ + {Name: "Host", Value: "$gw_api_compliant_host"}, + {Name: "X-Forwarded-For", Value: "$proxy_add_x_forwarded_for"}, + {Name: "Upgrade", Value: "$http_upgrade"}, + {Name: "Connection", Value: "$connection_upgrade"}, + }, + ResponseHeaders: http.ResponseHeaders{ + Add: []http.Header{ + {Name: "my-response-header", Value: "some-value-response"}, + }, + Set: []http.Header{}, + Remove: []string{}, + }, + }, { Path: "= /grpc/method", ProxyPass: "grpc://test_foo_80", From 793076113b29ff3dca9f2c270f060db6d9532fa7 Mon Sep 17 00:00:00 2001 From: Saloni Date: Thu, 2 May 2024 13:11:42 -0600 Subject: [PATCH 30/72] refactor code --- internal/mode/static/state/graph/httproute.go | 84 +++++++++++-------- 1 file changed, 47 insertions(+), 37 deletions(-) diff --git a/internal/mode/static/state/graph/httproute.go b/internal/mode/static/state/graph/httproute.go index 0fe5e45e25..bd5aaefdce 100644 --- a/internal/mode/static/state/graph/httproute.go +++ b/internal/mode/static/state/graph/httproute.go @@ -12,6 +12,12 @@ import ( "github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/state/validation" ) +var ( + add = "add" + set = "set" + remove = "remove" +) + func buildHTTPRoute( validator validation.HTTPFieldsValidator, ghr *v1.HTTPRoute, @@ -383,40 +389,40 @@ func validateFilterHeaderModifierFields( // Ensure that the header names are case-insensitive unique allErrs = append(allErrs, validateRequestHeadersCaseInsensitiveUnique( headerModifier.Add, - headerModifierPath.Child("add"))..., + headerModifierPath.Child(add))..., ) allErrs = append(allErrs, validateRequestHeadersCaseInsensitiveUnique( headerModifier.Set, - headerModifierPath.Child("set"))..., + headerModifierPath.Child(set))..., ) allErrs = append(allErrs, validateRequestHeaderStringCaseInsensitiveUnique( headerModifier.Remove, - headerModifierPath.Child("remove"))..., + headerModifierPath.Child(remove))..., ) for _, h := range headerModifier.Add { if err := validator.ValidateFilterHeaderName(string(h.Name)); err != nil { - valErr := field.Invalid(headerModifierPath.Child("add"), h, err.Error()) + valErr := field.Invalid(headerModifierPath.Child(add), h, err.Error()) allErrs = append(allErrs, valErr) } if err := validator.ValidateFilterHeaderValue(h.Value); err != nil { - valErr := field.Invalid(headerModifierPath.Child("add"), h, err.Error()) + valErr := field.Invalid(headerModifierPath.Child(add), h, err.Error()) allErrs = append(allErrs, valErr) } } for _, h := range headerModifier.Set { if err := validator.ValidateFilterHeaderName(string(h.Name)); err != nil { - valErr := field.Invalid(headerModifierPath.Child("set"), h, err.Error()) + valErr := field.Invalid(headerModifierPath.Child(set), h, err.Error()) allErrs = append(allErrs, valErr) } if err := validator.ValidateFilterHeaderValue(h.Value); err != nil { - valErr := field.Invalid(headerModifierPath.Child("set"), h, err.Error()) + valErr := field.Invalid(headerModifierPath.Child(set), h, err.Error()) allErrs = append(allErrs, valErr) } } for _, h := range headerModifier.Remove { if err := validator.ValidateFilterHeaderName(h); err != nil { - valErr := field.Invalid(headerModifierPath.Child("remove"), h, err.Error()) + valErr := field.Invalid(headerModifierPath.Child(remove), h, err.Error()) allErrs = append(allErrs, valErr) } } @@ -433,6 +439,35 @@ func validateFilterResponseHeaderModifier( return errList } var allErrs field.ErrorList + + allErrs = append(allErrs, validateResponseHeaders( + responseHeaderModifier.Add, + filterPath.Child(add))..., + ) + + allErrs = append(allErrs, validateResponseHeaders( + responseHeaderModifier.Set, + filterPath.Child(set))..., + ) + + var removeHeaders []v1.HTTPHeader + for _, h := range responseHeaderModifier.Remove { + removeHeaders = append(removeHeaders, v1.HTTPHeader{Name: v1.HTTPHeaderName(h)}) + } + + allErrs = append(allErrs, validateResponseHeaders( + removeHeaders, + filterPath.Child(remove))..., + ) + + return allErrs +} + +func validateResponseHeaders( + headers []v1.HTTPHeader, + path *field.Path, +) field.ErrorList { + var allErrs field.ErrorList disallowedResponseHeaderSet := map[string]struct{}{ "server": {}, "date": {}, @@ -442,37 +477,12 @@ func validateFilterResponseHeaderModifier( "connection": {}, } invalidPrefix := "x-accel" - for _, h := range responseHeaderModifier.Add { - valErr := field.Invalid(filterPath.Child("add"), h, "header name is not allowed") - name := strings.ToLower(string(h.Name)) - if _, exists := disallowedResponseHeaderSet[name]; exists { - allErrs = append(allErrs, valErr) - } else { - if strings.HasPrefix(name, strings.ToLower(invalidPrefix)) { - allErrs = append(allErrs, valErr) - } - } - } - for _, h := range responseHeaderModifier.Set { - valErr := field.Invalid(filterPath.Child("set"), h, "header name is not allowed") + + for _, h := range headers { + valErr := field.Invalid(path, h, "header name is not allowed") name := strings.ToLower(string(h.Name)) - if _, exists := disallowedResponseHeaderSet[name]; exists { + if _, exists := disallowedResponseHeaderSet[name]; exists || strings.HasPrefix(name, strings.ToLower(invalidPrefix)) { allErrs = append(allErrs, valErr) - } else { - if strings.HasPrefix(name, strings.ToLower(invalidPrefix)) { - allErrs = append(allErrs, valErr) - } - } - } - for _, h := range responseHeaderModifier.Remove { - valErr := field.Invalid(filterPath.Child("remove"), h, "header name is not allowed") - name := strings.ToLower(h) - if _, exists := disallowedResponseHeaderSet[name]; exists { - allErrs = append(allErrs, valErr) - } else { - if strings.HasPrefix(name, strings.ToLower(invalidPrefix)) { - allErrs = append(allErrs, valErr) - } } } From a80a7b5879273265e13ae37c2cebfbd9b3d6bc34 Mon Sep 17 00:00:00 2001 From: salonichf5 <146118978+salonichf5@users.noreply.github.com> Date: Thu, 2 May 2024 13:13:06 -0600 Subject: [PATCH 31/72] Update examples/http-response-header-filter/README.md Co-authored-by: Kate Osborn <50597707+kate-osborn@users.noreply.github.com> --- examples/http-response-header-filter/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/http-response-header-filter/README.md b/examples/http-response-header-filter/README.md index 2182d426d8..74fd6cbbf1 100644 --- a/examples/http-response-header-filter/README.md +++ b/examples/http-response-header-filter/README.md @@ -2,7 +2,7 @@ In this example we will deploy NGINX Gateway Fabric and configure traffic routing for a simple echo server. We will use HTTPRoute resources to route traffic to the echo server, using the `RequestHeaderModifier` filter to modify -headers to the request. +the response headers. ## Running the Example From b375b2a3c80c565a26f26a6a39020af05d7b8d4a Mon Sep 17 00:00:00 2001 From: salonichf5 <146118978+salonichf5@users.noreply.github.com> Date: Thu, 2 May 2024 13:13:19 -0600 Subject: [PATCH 32/72] Update examples/http-response-header-filter/README.md Co-authored-by: Kate Osborn <50597707+kate-osborn@users.noreply.github.com> --- examples/http-response-header-filter/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/http-response-header-filter/README.md b/examples/http-response-header-filter/README.md index 74fd6cbbf1..b3bececed1 100644 --- a/examples/http-response-header-filter/README.md +++ b/examples/http-response-header-filter/README.md @@ -1,7 +1,7 @@ # Example In this example we will deploy NGINX Gateway Fabric and configure traffic routing for a simple echo server. -We will use HTTPRoute resources to route traffic to the echo server, using the `RequestHeaderModifier` filter to modify +We will use HTTPRoute resources to route traffic to the echo server, using the `ResponseHeaderModifier` filter to modify the response headers. ## Running the Example From d9c50f3a4319ad67ef78d9891f5df9def61227f2 Mon Sep 17 00:00:00 2001 From: salonichf5 <146118978+salonichf5@users.noreply.github.com> Date: Thu, 2 May 2024 13:13:29 -0600 Subject: [PATCH 33/72] Update site/content/overview/gateway-api-compatibility.md Co-authored-by: Kate Osborn <50597707+kate-osborn@users.noreply.github.com> --- site/content/overview/gateway-api-compatibility.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/overview/gateway-api-compatibility.md b/site/content/overview/gateway-api-compatibility.md index 71ca290dd4..3fc597f105 100644 --- a/site/content/overview/gateway-api-compatibility.md +++ b/site/content/overview/gateway-api-compatibility.md @@ -155,7 +155,7 @@ See the [static-mode]({{< relref "/reference/cli-help.md#static-mode">}}) comman - `method`: Supported. - `filters` - `type`: Supported. - - `requestRedirect`: Supported except for the experimental `path` field. If multiple filters are configured, NGINX Gateway Fabric will choose the first and ignore the rest. + - `requestRedirect`: Supported except for the experimental `path` field. If multiple filters are configured, NGINX Gateway Fabric will choose the first and ignore the rest. Incompatible with `urlRewrite`. - `requestHeaderModifier`: Supported. If multiple filters are configured, NGINX Gateway Fabric will choose the first and ignore the rest. Incompatible with `urlRewrite`. - `urlRewrite`: Supported. If multiple filters are configured, NGINX Gateway Fabric will choose the first and ignore the rest. Incompatible with `requestHeaderModifier`. - `responseHeaderModifier`: Supported. From 52448c4f97716482d9a796379784ccb3206da8ac Mon Sep 17 00:00:00 2001 From: salonichf5 <146118978+salonichf5@users.noreply.github.com> Date: Thu, 2 May 2024 13:13:37 -0600 Subject: [PATCH 34/72] Update site/content/overview/gateway-api-compatibility.md Co-authored-by: Kate Osborn <50597707+kate-osborn@users.noreply.github.com> --- site/content/overview/gateway-api-compatibility.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/overview/gateway-api-compatibility.md b/site/content/overview/gateway-api-compatibility.md index 3fc597f105..db5fd9473e 100644 --- a/site/content/overview/gateway-api-compatibility.md +++ b/site/content/overview/gateway-api-compatibility.md @@ -156,7 +156,7 @@ See the [static-mode]({{< relref "/reference/cli-help.md#static-mode">}}) comman - `filters` - `type`: Supported. - `requestRedirect`: Supported except for the experimental `path` field. If multiple filters are configured, NGINX Gateway Fabric will choose the first and ignore the rest. Incompatible with `urlRewrite`. - - `requestHeaderModifier`: Supported. If multiple filters are configured, NGINX Gateway Fabric will choose the first and ignore the rest. Incompatible with `urlRewrite`. + - `requestHeaderModifier`: Supported. If multiple filters are configured, NGINX Gateway Fabric will choose the first and ignore the rest. - `urlRewrite`: Supported. If multiple filters are configured, NGINX Gateway Fabric will choose the first and ignore the rest. Incompatible with `requestHeaderModifier`. - `responseHeaderModifier`: Supported. - `requestMirror`, `extensionRef`: Not supported. From e3362e37288d56d8ec9a6506457d4076f29fd5ee Mon Sep 17 00:00:00 2001 From: salonichf5 <146118978+salonichf5@users.noreply.github.com> Date: Thu, 2 May 2024 13:14:08 -0600 Subject: [PATCH 35/72] Update site/content/overview/gateway-api-compatibility.md Co-authored-by: Kate Osborn <50597707+kate-osborn@users.noreply.github.com> --- site/content/overview/gateway-api-compatibility.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/overview/gateway-api-compatibility.md b/site/content/overview/gateway-api-compatibility.md index db5fd9473e..24cf02d81c 100644 --- a/site/content/overview/gateway-api-compatibility.md +++ b/site/content/overview/gateway-api-compatibility.md @@ -157,7 +157,7 @@ See the [static-mode]({{< relref "/reference/cli-help.md#static-mode">}}) comman - `type`: Supported. - `requestRedirect`: Supported except for the experimental `path` field. If multiple filters are configured, NGINX Gateway Fabric will choose the first and ignore the rest. Incompatible with `urlRewrite`. - `requestHeaderModifier`: Supported. If multiple filters are configured, NGINX Gateway Fabric will choose the first and ignore the rest. - - `urlRewrite`: Supported. If multiple filters are configured, NGINX Gateway Fabric will choose the first and ignore the rest. Incompatible with `requestHeaderModifier`. + - `urlRewrite`: Supported. If multiple filters are configured, NGINX Gateway Fabric will choose the first and ignore the rest. Incompatible with `requestRedirect`. - `responseHeaderModifier`: Supported. - `requestMirror`, `extensionRef`: Not supported. - `backendRefs`: Partially supported. Backend ref `filters` are not supported. From 6a2bd93d688816f3c9d6869bcfa889ba2e0b5644 Mon Sep 17 00:00:00 2001 From: salonichf5 <146118978+salonichf5@users.noreply.github.com> Date: Thu, 2 May 2024 13:14:18 -0600 Subject: [PATCH 36/72] Update site/content/overview/gateway-api-compatibility.md Co-authored-by: Kate Osborn <50597707+kate-osborn@users.noreply.github.com> --- site/content/overview/gateway-api-compatibility.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/overview/gateway-api-compatibility.md b/site/content/overview/gateway-api-compatibility.md index 24cf02d81c..d16f3e83a4 100644 --- a/site/content/overview/gateway-api-compatibility.md +++ b/site/content/overview/gateway-api-compatibility.md @@ -158,7 +158,7 @@ See the [static-mode]({{< relref "/reference/cli-help.md#static-mode">}}) comman - `requestRedirect`: Supported except for the experimental `path` field. If multiple filters are configured, NGINX Gateway Fabric will choose the first and ignore the rest. Incompatible with `urlRewrite`. - `requestHeaderModifier`: Supported. If multiple filters are configured, NGINX Gateway Fabric will choose the first and ignore the rest. - `urlRewrite`: Supported. If multiple filters are configured, NGINX Gateway Fabric will choose the first and ignore the rest. Incompatible with `requestRedirect`. - - `responseHeaderModifier`: Supported. + - `responseHeaderModifier`: Supported. If multiple filters are configured, NGINX Gateway Fabric will choose the first and ignore the rest. - `requestMirror`, `extensionRef`: Not supported. - `backendRefs`: Partially supported. Backend ref `filters` are not supported. - `status` From bc6e8ea8e226fd01b45ea43b849193acd5bbc907 Mon Sep 17 00:00:00 2001 From: salonichf5 <146118978+salonichf5@users.noreply.github.com> Date: Thu, 2 May 2024 13:14:35 -0600 Subject: [PATCH 37/72] Update examples/http-response-header-filter/README.md Co-authored-by: Kate Osborn <50597707+kate-osborn@users.noreply.github.com> --- examples/http-response-header-filter/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/http-response-header-filter/README.md b/examples/http-response-header-filter/README.md index b3bececed1..1de9961585 100644 --- a/examples/http-response-header-filter/README.md +++ b/examples/http-response-header-filter/README.md @@ -70,7 +70,7 @@ To access the application, we will use `curl` to send requests to the `headers` Notice our configured header values can be seen in the `responseHeaders` section below, and that the `Header-to-remove` header is absent. The header `My-cool-header` gets the appended with value `respond-with-this` from the `responseHeaderModifier` filter and the value of header `Response-Overwrite-Header` gets overwritten as defined in the *HttpRoute*. ```shell -curl -s -i --resolve echo.example.com:$GW_PORT:$GW_IP http://echo.example.com:$GW_PORT/headers -H "Header-to-remove:remove-this-header" +curl -s -i --resolve echo.example.com:$GW_PORT:$GW_IP http://echo.example.com:$GW_PORT/headers ``` ```text From 60c1c5db858ebc467a09836a2e06e497a8df0aea Mon Sep 17 00:00:00 2001 From: salonichf5 <146118978+salonichf5@users.noreply.github.com> Date: Thu, 2 May 2024 13:15:06 -0600 Subject: [PATCH 38/72] Update examples/http-response-header-filter/README.md Co-authored-by: Kate Osborn <50597707+kate-osborn@users.noreply.github.com> --- examples/http-response-header-filter/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/http-response-header-filter/README.md b/examples/http-response-header-filter/README.md index 1de9961585..89c3ecdad1 100644 --- a/examples/http-response-header-filter/README.md +++ b/examples/http-response-header-filter/README.md @@ -64,7 +64,7 @@ This HTTPRoute has a few important properties: ## 4. Test the Application -To access the application, we will use `curl` to send requests to the `headers` Service, including sending headers with our request. +To access the application, we will use `curl` to send requests to the `headers` endpoint. Notice our configured header values can be seen in the `responseHeaders` section below, and that the `Header-to-remove` header is absent. The header `My-cool-header` gets the appended with value `respond-with-this` from the `responseHeaderModifier` filter and the value of header `Response-Overwrite-Header` gets overwritten as defined in the *HttpRoute*. From 5aabd0ac61297469025164b25ea39fae7e34215a Mon Sep 17 00:00:00 2001 From: Saloni Date: Thu, 2 May 2024 14:52:41 -0600 Subject: [PATCH 39/72] address comments --- .../http-response-header-filter/README.md | 12 +-- .../http-response-header-filter/headers.yaml | 12 +-- .../{echo-route.yaml => http-route.yaml} | 6 +- .../mode/static/nginx/config/servers_test.go | 64 +++++---------- .../nginx/config/validation/http_filters.go | 2 +- .../config/validation/http_filters_test.go | 6 +- .../state/dataplane/configuration_test.go | 49 ++++++++++++ internal/mode/static/state/graph/httproute.go | 6 +- .../mode/static/state/graph/httproute_test.go | 12 +-- .../fake_httpfields_validator.go | 78 +++++++++---------- .../mode/static/state/validation/validator.go | 2 +- 11 files changed, 136 insertions(+), 113 deletions(-) rename examples/http-response-header-filter/{echo-route.yaml => http-route.yaml} (85%) diff --git a/examples/http-response-header-filter/README.md b/examples/http-response-header-filter/README.md index 89c3ecdad1..a04b77f942 100644 --- a/examples/http-response-header-filter/README.md +++ b/examples/http-response-header-filter/README.md @@ -1,7 +1,7 @@ # Example -In this example we will deploy NGINX Gateway Fabric and configure traffic routing for a simple echo server. -We will use HTTPRoute resources to route traffic to the echo server, using the `ResponseHeaderModifier` filter to modify +In this example we will deploy NGINX Gateway Fabric and configure traffic routing. +We will use HTTPRoute resources to route traffic to the server, using the `ResponseHeaderModifier` filter to modify the response headers. ## Running the Example @@ -52,25 +52,25 @@ the response headers. 1. Create the HTTPRoute resources: ```shell - kubectl apply -f echo-route.yaml + kubectl apply -f http-route.yaml ``` This HTTPRoute has a few important properties: - The `parentRefs` references the gateway resource that we created, and specifically defines the `http` listener to attach to, via the `sectionName` field. -- `echo.example.com` is the hostname that is matched for all requests to the backends defined in this HTTPRoute. +- `cafe.example.com` is the hostname that is matched for all requests to the backends defined in this HTTPRoute. - The `match` rule defines that all requests with the path prefix `/headers` are sent to the `headers` Service. - There is `ResponseHeaderModifier` filter defined for the path prefix `/headers` to set header `Response-Overwrite-Header` and add header `My-cool-header`. ## 4. Test the Application -To access the application, we will use `curl` to send requests to the `headers` endpoint. +To access the application, we will use `curl` to send requests to the `headers` endpoint. Notice our configured header values can be seen in the `responseHeaders` section below, and that the `Header-to-remove` header is absent. The header `My-cool-header` gets the appended with value `respond-with-this` from the `responseHeaderModifier` filter and the value of header `Response-Overwrite-Header` gets overwritten as defined in the *HttpRoute*. ```shell -curl -s -i --resolve echo.example.com:$GW_PORT:$GW_IP http://echo.example.com:$GW_PORT/headers +curl -s -i --resolve cafe.example.com:$GW_PORT:$GW_IP http://echo.example.com:$GW_PORT/headers ``` ```text diff --git a/examples/http-response-header-filter/headers.yaml b/examples/http-response-header-filter/headers.yaml index 556e80f85d..409ddea8ff 100644 --- a/examples/http-response-header-filter/headers.yaml +++ b/examples/http-response-header-filter/headers.yaml @@ -38,8 +38,6 @@ data: pid /var/run/nginx.pid; - load_module /usr/lib/nginx/modules/ngx_http_js_module.so; - events {} http { @@ -47,10 +45,14 @@ data: server { listen 8080; - add_header My-cool-header "this-is-the-value" always; - proxy_hide_header Response-Overwrite-Header; - add_header Response-Overwrite-Header "this-is-the-actual-value" always; + add_header X-Custom-Header "this-stays-unchanged" always; + + add_header My-cool-header "this-is-the-appended-value" always; + + add_header Response-Overwrite-Header "this-value-to-be-returned" always; + + add_header X-Server-Version "1.2.3" always; return 200 "ok"; } diff --git a/examples/http-response-header-filter/echo-route.yaml b/examples/http-response-header-filter/http-route.yaml similarity index 85% rename from examples/http-response-header-filter/echo-route.yaml rename to examples/http-response-header-filter/http-route.yaml index 618f8807e0..da47eff3db 100644 --- a/examples/http-response-header-filter/echo-route.yaml +++ b/examples/http-response-header-filter/http-route.yaml @@ -7,7 +7,7 @@ spec: - name: gateway sectionName: http hostnames: - - "echo.example.com" + - "cafe.example.com" rules: - matches: - path: @@ -21,9 +21,9 @@ spec: value: overwritten-value add: - name: My-cool-header - value: respond-with-this + value: this-is-the-value remove: - - Header-to-remove + - X-Server-Version backendRefs: - name: headers port: 80 diff --git a/internal/mode/static/nginx/config/servers_test.go b/internal/mode/static/nginx/config/servers_test.go index d121f8d32a..adad59f7fc 100644 --- a/internal/mode/static/nginx/config/servers_test.go +++ b/internal/mode/static/nginx/config/servers_test.go @@ -485,23 +485,11 @@ func TestCreateServers(t *testing.T) { }, }, }, - }, - }, - }, - }, - { - Path: "/add-headers", - PathType: dataplane.PathTypePrefix, - MatchRules: []dataplane.MatchRule{ - { - Match: dataplane.Match{}, - BackendGroup: fooGroup, - Filters: dataplane.HTTPFilters{ ResponseHeaderModifiers: &dataplane.HTTPHeaderFilter{ Add: []dataplane.HTTPHeader{ { - Name: "my-response-header", - Value: "some-value-response", + Name: "my-header-response", + Value: "some-value-response-123", }, }, }, @@ -832,7 +820,16 @@ func TestCreateServers(t *testing.T) { Value: "$connection_upgrade", }, }, - ResponseHeaders: http.ResponseHeaders{}, + ResponseHeaders: http.ResponseHeaders{ + Add: []http.Header{ + { + Name: "my-header-response", + Value: "some-value-response-123", + }, + }, + Set: []http.Header{}, + Remove: []string{}, + }, }, { Path: "= /proxy-set-headers", @@ -859,37 +856,12 @@ func TestCreateServers(t *testing.T) { Value: "$connection_upgrade", }, }, - ResponseHeaders: http.ResponseHeaders{}, - }, - { - Path: "/add-headers/", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: []http.Header{ - {Name: "Host", Value: "$gw_api_compliant_host"}, - {Name: "X-Forwarded-For", Value: "$proxy_add_x_forwarded_for"}, - {Name: "Upgrade", Value: "$http_upgrade"}, - {Name: "Connection", Value: "$connection_upgrade"}, - }, ResponseHeaders: http.ResponseHeaders{ Add: []http.Header{ - {Name: "my-response-header", Value: "some-value-response"}, - }, - Set: []http.Header{}, - Remove: []string{}, - }, - }, - { - Path: "= /add-headers", - ProxyPass: "http://test_foo_80$request_uri", - ProxySetHeaders: []http.Header{ - {Name: "Host", Value: "$gw_api_compliant_host"}, - {Name: "X-Forwarded-For", Value: "$proxy_add_x_forwarded_for"}, - {Name: "Upgrade", Value: "$http_upgrade"}, - {Name: "Connection", Value: "$connection_upgrade"}, - }, - ResponseHeaders: http.ResponseHeaders{ - Add: []http.Header{ - {Name: "my-response-header", Value: "some-value-response"}, + { + Name: "my-header-response", + Value: "some-value-response-123", + }, }, Set: []http.Header{}, Remove: []string{}, @@ -2166,7 +2138,7 @@ func TestGenerateResponseHeaders(t *testing.T) { Value: "my-new-overwritten-value", }, }, - Remove: []string{"Authorization"}, + Remove: []string{"Transfer-Encoding"}, }, }, expectedHeaders: http.ResponseHeaders{ @@ -2186,7 +2158,7 @@ func TestGenerateResponseHeaders(t *testing.T) { Value: "my-new-overwritten-value", }, }, - Remove: []string{"Authorization"}, + Remove: []string{"Transfer-Encoding"}, }, }, } diff --git a/internal/mode/static/nginx/config/validation/http_filters.go b/internal/mode/static/nginx/config/validation/http_filters.go index ba12a86257..4ae7fb8405 100644 --- a/internal/mode/static/nginx/config/validation/http_filters.go +++ b/internal/mode/static/nginx/config/validation/http_filters.go @@ -72,7 +72,7 @@ func (HTTPURLRewriteValidator) ValidateRewritePath(path string) error { return nil } -func (HTTPRequestHeaderValidator) ValidateFilterHeaderName(name string) error { +func (HTTPRequestHeaderValidator) HTTPHeaderValidator(name string) error { return validateHeaderName(name) } diff --git a/internal/mode/static/nginx/config/validation/http_filters_test.go b/internal/mode/static/nginx/config/validation/http_filters_test.go index 4bd8fe6589..aeb7765388 100644 --- a/internal/mode/static/nginx/config/validation/http_filters_test.go +++ b/internal/mode/static/nginx/config/validation/http_filters_test.go @@ -88,17 +88,17 @@ func TestValidateRewritePath(t *testing.T) { ) } -func TestValidateFilterHeaderName(t *testing.T) { +func TestHTTPHeaderValidator(t *testing.T) { validator := HTTPRequestHeaderValidator{} testValidValuesForSimpleValidator( t, - validator.ValidateFilterHeaderName, + validator.HTTPHeaderValidator, "Content-Encoding", "MyBespokeHeader", ) - testInvalidValuesForSimpleValidator(t, validator.ValidateFilterHeaderName, "$Content-Encoding") + testInvalidValuesForSimpleValidator(t, validator.HTTPHeaderValidator, "$Content-Encoding") } func TestValidateFilterHeaderValue(t *testing.T) { diff --git a/internal/mode/static/state/dataplane/configuration_test.go b/internal/mode/static/state/dataplane/configuration_test.go index def4563ab8..f73dd60651 100644 --- a/internal/mode/static/state/dataplane/configuration_test.go +++ b/internal/mode/static/state/dataplane/configuration_test.go @@ -2083,6 +2083,30 @@ func TestCreateFilters(t *testing.T) { }, } + responseHeaderModifiers1 := v1.HTTPRouteFilter{ + Type: v1.HTTPRouteFilterResponseHeaderModifier, + ResponseHeaderModifier: &v1.HTTPHeaderFilter{ + Add: []v1.HTTPHeader{ + { + Name: "X-Server-Version", + Value: "2.3", + }, + }, + }, + } + + invalidResponseHeaderModifiers2 := v1.HTTPRouteFilter{ + Type: v1.HTTPRouteFilterResponseHeaderModifier, + ResponseHeaderModifier: &v1.HTTPHeaderFilter{ + Set: []v1.HTTPHeader{ + { + Name: "Server", + Value: "new-response-value", + }, + }, + }, + } + expectedRedirect1 := HTTPRequestRedirectFilter{ Hostname: helpers.GetPointer("foo.example.com"), } @@ -2098,6 +2122,15 @@ func TestCreateFilters(t *testing.T) { }, } + expectedresponseHeaderModifier := HTTPHeaderFilter{ + Add: []HTTPHeader{ + { + Name: "X-Server-Version", + Value: "2.3", + }, + }, + } + tests := []struct { expected HTTPFilters msg string @@ -2155,6 +2188,22 @@ func TestCreateFilters(t *testing.T) { }, msg: "two of each filter, first value for each wins", }, + { + filters: []v1.HTTPRouteFilter{ + redirect1, + rewrite1, + requestHeaderModifiers1, + responseHeaderModifiers1, + invalidResponseHeaderModifiers2, + }, + expected: HTTPFilters{ + RequestRedirect: &expectedRedirect1, + RequestURLRewrite: &expectedRewrite1, + RequestHeaderModifiers: &expectedHeaderModifier1, + ResponseHeaderModifiers: &expectedresponseHeaderModifier, + }, + msg: "one of each redirect, rewrite, request filter and two response filters, one invalid response filter", + }, } for _, test := range tests { diff --git a/internal/mode/static/state/graph/httproute.go b/internal/mode/static/state/graph/httproute.go index bd5aaefdce..b33518bed0 100644 --- a/internal/mode/static/state/graph/httproute.go +++ b/internal/mode/static/state/graph/httproute.go @@ -401,7 +401,7 @@ func validateFilterHeaderModifierFields( ) for _, h := range headerModifier.Add { - if err := validator.ValidateFilterHeaderName(string(h.Name)); err != nil { + if err := validator.HTTPHeaderValidator(string(h.Name)); err != nil { valErr := field.Invalid(headerModifierPath.Child(add), h, err.Error()) allErrs = append(allErrs, valErr) } @@ -411,7 +411,7 @@ func validateFilterHeaderModifierFields( } } for _, h := range headerModifier.Set { - if err := validator.ValidateFilterHeaderName(string(h.Name)); err != nil { + if err := validator.HTTPHeaderValidator(string(h.Name)); err != nil { valErr := field.Invalid(headerModifierPath.Child(set), h, err.Error()) allErrs = append(allErrs, valErr) } @@ -421,7 +421,7 @@ func validateFilterHeaderModifierFields( } } for _, h := range headerModifier.Remove { - if err := validator.ValidateFilterHeaderName(h); err != nil { + if err := validator.HTTPHeaderValidator(h); err != nil { valErr := field.Invalid(headerModifierPath.Child(remove), h, err.Error()) allErrs = append(allErrs, valErr) } diff --git a/internal/mode/static/state/graph/httproute_test.go b/internal/mode/static/state/graph/httproute_test.go index 3818fd345b..9e60c002c4 100644 --- a/internal/mode/static/state/graph/httproute_test.go +++ b/internal/mode/static/state/graph/httproute_test.go @@ -1223,7 +1223,7 @@ func TestValidateFilterRequestHeaderModifier(t *testing.T) { { validator: func() *validationfakes.FakeHTTPFieldsValidator { v := createAllValidValidator() - v.ValidateFilterHeaderNameReturns(errors.New("Invalid header")) + v.HTTPHeaderValidatorReturns(errors.New("Invalid header")) return v }(), filter: gatewayv1.HTTPRouteFilter{ @@ -1240,7 +1240,7 @@ func TestValidateFilterRequestHeaderModifier(t *testing.T) { { validator: func() *validationfakes.FakeHTTPFieldsValidator { v := createAllValidValidator() - v.ValidateFilterHeaderNameReturns(errors.New("Invalid header")) + v.HTTPHeaderValidatorReturns(errors.New("Invalid header")) return v }(), filter: gatewayv1.HTTPRouteFilter{ @@ -1273,7 +1273,7 @@ func TestValidateFilterRequestHeaderModifier(t *testing.T) { validator: func() *validationfakes.FakeHTTPFieldsValidator { v := createAllValidValidator() v.ValidateFilterHeaderValueReturns(errors.New("Invalid header value")) - v.ValidateFilterHeaderNameReturns(errors.New("Invalid header")) + v.HTTPHeaderValidatorReturns(errors.New("Invalid header")) return v }(), filter: gatewayv1.HTTPRouteFilter{ @@ -1367,7 +1367,7 @@ func TestValidateFilterResponseHeaderModifier(t *testing.T) { { validator: func() *validationfakes.FakeHTTPFieldsValidator { v := createAllValidValidator() - v.ValidateFilterHeaderNameReturns(errors.New("Invalid header")) + v.HTTPHeaderValidatorReturns(errors.New("Invalid header")) return v }(), filter: gatewayv1.HTTPRouteFilter{ @@ -1384,7 +1384,7 @@ func TestValidateFilterResponseHeaderModifier(t *testing.T) { { validator: func() *validationfakes.FakeHTTPFieldsValidator { v := createAllValidValidator() - v.ValidateFilterHeaderNameReturns(errors.New("Invalid header")) + v.HTTPHeaderValidatorReturns(errors.New("Invalid header")) return v }(), filter: gatewayv1.HTTPRouteFilter{ @@ -1417,7 +1417,7 @@ func TestValidateFilterResponseHeaderModifier(t *testing.T) { validator: func() *validationfakes.FakeHTTPFieldsValidator { v := createAllValidValidator() v.ValidateFilterHeaderValueReturns(errors.New("Invalid header value")) - v.ValidateFilterHeaderNameReturns(errors.New("Invalid header")) + v.HTTPHeaderValidatorReturns(errors.New("Invalid header")) return v }(), filter: gatewayv1.HTTPRouteFilter{ diff --git a/internal/mode/static/state/validation/validationfakes/fake_httpfields_validator.go b/internal/mode/static/state/validation/validationfakes/fake_httpfields_validator.go index 64d9b09349..967d7deeb7 100644 --- a/internal/mode/static/state/validation/validationfakes/fake_httpfields_validator.go +++ b/internal/mode/static/state/validation/validationfakes/fake_httpfields_validator.go @@ -8,15 +8,15 @@ import ( ) type FakeHTTPFieldsValidator struct { - ValidateFilterHeaderNameStub func(string) error - validateFilterHeaderNameMutex sync.RWMutex - validateFilterHeaderNameArgsForCall []struct { + HTTPHeaderValidatorStub func(string) error + hTTPHeaderValidatorMutex sync.RWMutex + hTTPHeaderValidatorArgsForCall []struct { arg1 string } - validateFilterHeaderNameReturns struct { + hTTPHeaderValidatorReturns struct { result1 error } - validateFilterHeaderNameReturnsOnCall map[int]struct { + hTTPHeaderValidatorReturnsOnCall map[int]struct { result1 error } ValidateFilterHeaderValueStub func(string) error @@ -161,16 +161,16 @@ type FakeHTTPFieldsValidator struct { invocationsMutex sync.RWMutex } -func (fake *FakeHTTPFieldsValidator) ValidateFilterHeaderName(arg1 string) error { - fake.validateFilterHeaderNameMutex.Lock() - ret, specificReturn := fake.validateFilterHeaderNameReturnsOnCall[len(fake.validateFilterHeaderNameArgsForCall)] - fake.validateFilterHeaderNameArgsForCall = append(fake.validateFilterHeaderNameArgsForCall, struct { +func (fake *FakeHTTPFieldsValidator) HTTPHeaderValidator(arg1 string) error { + fake.hTTPHeaderValidatorMutex.Lock() + ret, specificReturn := fake.hTTPHeaderValidatorReturnsOnCall[len(fake.hTTPHeaderValidatorArgsForCall)] + fake.hTTPHeaderValidatorArgsForCall = append(fake.hTTPHeaderValidatorArgsForCall, struct { arg1 string }{arg1}) - stub := fake.ValidateFilterHeaderNameStub - fakeReturns := fake.validateFilterHeaderNameReturns - fake.recordInvocation("ValidateFilterHeaderName", []interface{}{arg1}) - fake.validateFilterHeaderNameMutex.Unlock() + stub := fake.HTTPHeaderValidatorStub + fakeReturns := fake.hTTPHeaderValidatorReturns + fake.recordInvocation("HTTPHeaderValidator", []interface{}{arg1}) + fake.hTTPHeaderValidatorMutex.Unlock() if stub != nil { return stub(arg1) } @@ -180,44 +180,44 @@ func (fake *FakeHTTPFieldsValidator) ValidateFilterHeaderName(arg1 string) error return fakeReturns.result1 } -func (fake *FakeHTTPFieldsValidator) ValidateFilterHeaderNameCallCount() int { - fake.validateFilterHeaderNameMutex.RLock() - defer fake.validateFilterHeaderNameMutex.RUnlock() - return len(fake.validateFilterHeaderNameArgsForCall) +func (fake *FakeHTTPFieldsValidator) HTTPHeaderValidatorCallCount() int { + fake.hTTPHeaderValidatorMutex.RLock() + defer fake.hTTPHeaderValidatorMutex.RUnlock() + return len(fake.hTTPHeaderValidatorArgsForCall) } -func (fake *FakeHTTPFieldsValidator) ValidateFilterHeaderNameCalls(stub func(string) error) { - fake.validateFilterHeaderNameMutex.Lock() - defer fake.validateFilterHeaderNameMutex.Unlock() - fake.ValidateFilterHeaderNameStub = stub +func (fake *FakeHTTPFieldsValidator) HTTPHeaderValidatorCalls(stub func(string) error) { + fake.hTTPHeaderValidatorMutex.Lock() + defer fake.hTTPHeaderValidatorMutex.Unlock() + fake.HTTPHeaderValidatorStub = stub } -func (fake *FakeHTTPFieldsValidator) ValidateFilterHeaderNameArgsForCall(i int) string { - fake.validateFilterHeaderNameMutex.RLock() - defer fake.validateFilterHeaderNameMutex.RUnlock() - argsForCall := fake.validateFilterHeaderNameArgsForCall[i] +func (fake *FakeHTTPFieldsValidator) HTTPHeaderValidatorArgsForCall(i int) string { + fake.hTTPHeaderValidatorMutex.RLock() + defer fake.hTTPHeaderValidatorMutex.RUnlock() + argsForCall := fake.hTTPHeaderValidatorArgsForCall[i] return argsForCall.arg1 } -func (fake *FakeHTTPFieldsValidator) ValidateFilterHeaderNameReturns(result1 error) { - fake.validateFilterHeaderNameMutex.Lock() - defer fake.validateFilterHeaderNameMutex.Unlock() - fake.ValidateFilterHeaderNameStub = nil - fake.validateFilterHeaderNameReturns = struct { +func (fake *FakeHTTPFieldsValidator) HTTPHeaderValidatorReturns(result1 error) { + fake.hTTPHeaderValidatorMutex.Lock() + defer fake.hTTPHeaderValidatorMutex.Unlock() + fake.HTTPHeaderValidatorStub = nil + fake.hTTPHeaderValidatorReturns = struct { result1 error }{result1} } -func (fake *FakeHTTPFieldsValidator) ValidateFilterHeaderNameReturnsOnCall(i int, result1 error) { - fake.validateFilterHeaderNameMutex.Lock() - defer fake.validateFilterHeaderNameMutex.Unlock() - fake.ValidateFilterHeaderNameStub = nil - if fake.validateFilterHeaderNameReturnsOnCall == nil { - fake.validateFilterHeaderNameReturnsOnCall = make(map[int]struct { +func (fake *FakeHTTPFieldsValidator) HTTPHeaderValidatorReturnsOnCall(i int, result1 error) { + fake.hTTPHeaderValidatorMutex.Lock() + defer fake.hTTPHeaderValidatorMutex.Unlock() + fake.HTTPHeaderValidatorStub = nil + if fake.hTTPHeaderValidatorReturnsOnCall == nil { + fake.hTTPHeaderValidatorReturnsOnCall = make(map[int]struct { result1 error }) } - fake.validateFilterHeaderNameReturnsOnCall[i] = struct { + fake.hTTPHeaderValidatorReturnsOnCall[i] = struct { result1 error }{result1} } @@ -966,8 +966,8 @@ func (fake *FakeHTTPFieldsValidator) ValidateRewritePathReturnsOnCall(i int, res func (fake *FakeHTTPFieldsValidator) Invocations() map[string][][]interface{} { fake.invocationsMutex.RLock() defer fake.invocationsMutex.RUnlock() - fake.validateFilterHeaderNameMutex.RLock() - defer fake.validateFilterHeaderNameMutex.RUnlock() + fake.hTTPHeaderValidatorMutex.RLock() + defer fake.hTTPHeaderValidatorMutex.RUnlock() fake.validateFilterHeaderValueMutex.RLock() defer fake.validateFilterHeaderValueMutex.RUnlock() fake.validateHeaderNameInMatchMutex.RLock() diff --git a/internal/mode/static/state/validation/validator.go b/internal/mode/static/state/validation/validator.go index 3bc5847004..385eb11e78 100644 --- a/internal/mode/static/state/validation/validator.go +++ b/internal/mode/static/state/validation/validator.go @@ -25,7 +25,7 @@ type HTTPFieldsValidator interface { ValidateRedirectStatusCode(statusCode int) (valid bool, supportedValues []string) ValidateHostname(hostname string) error ValidateRewritePath(path string) error - ValidateFilterHeaderName(name string) error + HTTPHeaderValidator(name string) error ValidateFilterHeaderValue(value string) error } From 394a3f33a12b8130320639e582f78777a220e639 Mon Sep 17 00:00:00 2001 From: Saloni Date: Thu, 2 May 2024 15:00:06 -0600 Subject: [PATCH 40/72] update examples for response headers --- examples/http-response-header-filter/README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/examples/http-response-header-filter/README.md b/examples/http-response-header-filter/README.md index a04b77f942..751b911e92 100644 --- a/examples/http-response-header-filter/README.md +++ b/examples/http-response-header-filter/README.md @@ -67,20 +67,21 @@ This HTTPRoute has a few important properties: To access the application, we will use `curl` to send requests to the `headers` endpoint. -Notice our configured header values can be seen in the `responseHeaders` section below, and that the `Header-to-remove` header is absent. The header `My-cool-header` gets the appended with value `respond-with-this` from the `responseHeaderModifier` filter and the value of header `Response-Overwrite-Header` gets overwritten as defined in the *HttpRoute*. +Notice our configured header values can be seen in the `responseHeaders` section below, and that the `X-Server-Version` header is absent. The header `My-cool-header` gets the appended with value `respond-with-this` from the `responseHeaderModifier` filter and the value of header `Response-Overwrite-Header` gets overwritten as defined in the *HttpRoute*. ```shell -curl -s -i --resolve cafe.example.com:$GW_PORT:$GW_IP http://echo.example.com:$GW_PORT/headers +curl -v -i --resolve cafe.example.com:$GW_PORT:$GW_IP http://cafe.example.com:$GW_PORT/headers ``` ```text HTTP/1.1 200 OK Server: nginx/1.25.5 -Date: Thu, 02 May 2024 01:11:37 GMT +Date: Thu, 02 May 2024 20:58:54 GMT Content-Type: text/plain Content-Length: 2 Connection: keep-alive +X-Custom-Header: this-stays-unchanged +My-cool-header: this-is-the-appended-value My-cool-header: this-is-the-value -My-cool-header: respond-with-this Response-Overwrite-Header: overwritten-value ``` From bd0495f4ecaa09b9919db8ccafcff662266aa10b Mon Sep 17 00:00:00 2001 From: Saloni Date: Thu, 2 May 2024 15:02:56 -0600 Subject: [PATCH 41/72] improve docs --- examples/http-request-header-filter/README.md | 7 ------- examples/http-response-header-filter/README.md | 9 +-------- 2 files changed, 1 insertion(+), 15 deletions(-) diff --git a/examples/http-request-header-filter/README.md b/examples/http-request-header-filter/README.md index 706489fc9b..87312de8af 100644 --- a/examples/http-request-header-filter/README.md +++ b/examples/http-request-header-filter/README.md @@ -55,13 +55,6 @@ headers to the request. kubectl apply -f echo-route.yaml ``` -This HTTPRoute has a few important properties: - -- The `parentRefs` references the gateway resource that we created, and specifically defines the `http` listener to attach to, via the `sectionName` field. -- `echo.example.com` is the hostname that is matched for all requests to the backends defined in this HTTPRoute. -- The `match` rule defines that all requests with the path prefix `/headers` are sent to the `headers` Service. -- There is `RequestHeaderModifier` filter defined for the path prefix `/headers`. - ## 4. Test the Application To access the application, we will use `curl` to send requests to the `headers` Service, including sending headers with diff --git a/examples/http-response-header-filter/README.md b/examples/http-response-header-filter/README.md index 751b911e92..86a3d5df21 100644 --- a/examples/http-response-header-filter/README.md +++ b/examples/http-response-header-filter/README.md @@ -55,19 +55,12 @@ the response headers. kubectl apply -f http-route.yaml ``` -This HTTPRoute has a few important properties: - -- The `parentRefs` references the gateway resource that we created, and specifically defines the `http` listener to attach to, via the `sectionName` field. -- `cafe.example.com` is the hostname that is matched for all requests to the backends defined in this HTTPRoute. -- The `match` rule defines that all requests with the path prefix `/headers` are sent to the `headers` Service. -- There is `ResponseHeaderModifier` filter defined for the path prefix `/headers` to set header `Response-Overwrite-Header` and add header `My-cool-header`. - ## 4. Test the Application To access the application, we will use `curl` to send requests to the `headers` endpoint. -Notice our configured header values can be seen in the `responseHeaders` section below, and that the `X-Server-Version` header is absent. The header `My-cool-header` gets the appended with value `respond-with-this` from the `responseHeaderModifier` filter and the value of header `Response-Overwrite-Header` gets overwritten as defined in the *HttpRoute*. +Notice our configured header values can be seen in the `responseHeaders` section below, and that the `X-Server-Version` header is absent. The header `My-cool-header` gets the appended with value `this-is-the-appended-value` from the `responseHeaderModifier` filter and the value of header `Response-Overwrite-Header` gets overwritten to `overwritten-value` as defined in the *HttpRoute*. ```shell curl -v -i --resolve cafe.example.com:$GW_PORT:$GW_IP http://cafe.example.com:$GW_PORT/headers From 579eacad03f68779991a1d0915e146439899e2df Mon Sep 17 00:00:00 2001 From: Saloni Date: Thu, 2 May 2024 15:46:22 -0600 Subject: [PATCH 42/72] update readme --- .../http-response-header-filter/README.md | 81 +-------- .../request-and-response-headers.md | 40 ----- .../traffic-management/response-headers.md | 155 ++++++++++++++++++ 3 files changed, 157 insertions(+), 119 deletions(-) delete mode 100644 site/content/how-to/traffic-management/request-and-response-headers.md create mode 100644 site/content/how-to/traffic-management/response-headers.md diff --git a/examples/http-response-header-filter/README.md b/examples/http-response-header-filter/README.md index 86a3d5df21..ace5ec59df 100644 --- a/examples/http-response-header-filter/README.md +++ b/examples/http-response-header-filter/README.md @@ -1,80 +1,3 @@ -# Example +# HTTP Response Headers -In this example we will deploy NGINX Gateway Fabric and configure traffic routing. -We will use HTTPRoute resources to route traffic to the server, using the `ResponseHeaderModifier` filter to modify -the response headers. - -## Running the Example - -## 1. Deploy NGINX Gateway Fabric - -1. Follow the [installation instructions](https://docs.nginx.com/nginx-gateway-fabric/installation/) to deploy NGINX Gateway Fabric. - -1. Save the public IP address of NGINX Gateway Fabric into a shell variable: - - ```text - GW_IP=XXX.YYY.ZZZ.III - ``` - -1. Save the port of NGINX Gateway Fabric: - - ```text - GW_PORT= - ``` - -## 2. Deploy the Headers Application - -1. Create the headers Deployment and Service: - - ```shell - kubectl apply -f headers.yaml - ``` - -1. Check that the Pod is running in the `default` Namespace: - - ```shell - kubectl -n default get pods - ``` - - ```text - NAME READY STATUS RESTARTS AGE - headers-6f4b79b975-2sb28 1/1 Running 0 12s - ``` - -## 3. Configure Routing - -1. Create the Gateway: - - ```shell - kubectl apply -f gateway.yaml - ``` - -1. Create the HTTPRoute resources: - - ```shell - kubectl apply -f http-route.yaml - ``` - -## 4. Test the Application - -To access the application, we will use `curl` to send requests to the `headers` endpoint. - - -Notice our configured header values can be seen in the `responseHeaders` section below, and that the `X-Server-Version` header is absent. The header `My-cool-header` gets the appended with value `this-is-the-appended-value` from the `responseHeaderModifier` filter and the value of header `Response-Overwrite-Header` gets overwritten to `overwritten-value` as defined in the *HttpRoute*. - -```shell -curl -v -i --resolve cafe.example.com:$GW_PORT:$GW_IP http://cafe.example.com:$GW_PORT/headers -``` - -```text -HTTP/1.1 200 OK -Server: nginx/1.25.5 -Date: Thu, 02 May 2024 20:58:54 GMT -Content-Type: text/plain -Content-Length: 2 -Connection: keep-alive -X-Custom-Header: this-stays-unchanged -My-cool-header: this-is-the-appended-value -My-cool-header: this-is-the-value -Response-Overwrite-Header: overwritten-value -``` +This directory contains the YAML files used in the [HTTP Response Headers](https://docs.nginx.com/nginx-gateway-fabric/how-to/traffic-management/response-headers.md) guide. diff --git a/site/content/how-to/traffic-management/request-and-response-headers.md b/site/content/how-to/traffic-management/request-and-response-headers.md deleted file mode 100644 index 11fed16c9b..0000000000 --- a/site/content/how-to/traffic-management/request-and-response-headers.md +++ /dev/null @@ -1,40 +0,0 @@ ---- -title: "HTTP Request and Response Headers" -description: "Learn how to modify request and response headers for your HTTP Route using NGINX Gateway Fabric." -weight: 700 -toc: true -docs: "" ---- - -[HTTPRoute](https://gateway-api.sigs.k8s.io/api-types/httproute/) filters can modify the headers during the request-response lifecycle. [HTTP Header Modifiers](https://gateway-api.sigs.k8s.io/guides/http-header-modifier/?h=request#http-header-modifiers) can be used to add, modify or remove headers in incoming requests. We have two types of [filter](https://gateway-api.sigs.k8s.io/api-types/httproute/#filters-optional) that can be used to instruct the Gateway for desired behaviour. - - -1. [RequestHeaderModifier](https://gateway-api.sigs.k8s.io/guides/http-header-modifier/?h=request#http-request-header-modifier) to alter headers in request before forwarding the request upstream. - -1. [ResponseHeaderModifier](https://gateway-api.sigs.k8s.io/guides/http-header-modifier/?h=request#http-response-header-modifier) to alter headers in response before responding to the downstream. - - -In this guide we will modify the headers of HTTP request and HTTP responses from clients. For an introduction to exposing your application, we recommend that you follow the [basic guide]({{< relref "/how-to/traffic-management/routing-traffic-to-your-app.md" >}}) first. - - -## Prerequisites - -- [Install]({{< relref "/installation/" >}}) NGINX Gateway Fabric. -- [Expose NGINX Gateway Fabric]({{< relref "installation/expose-nginx-gateway-fabric.md" >}}) and save the public IP - address and port of NGINX Gateway Fabric into shell variables: - - ```text - GW_IP=XXX.YYY.ZZZ.III - GW_PORT= - ``` - -{{< note >}}In a production environment, you should have a DNS record for the external IP address that is exposed, and it should refer to the hostname that the gateway will forward for.{{< /note >}} - -## Request Header Filter - -Follow this [guide](https://github.com/nginxinc/nginx-gateway-fabric/tree/main/examples/http-request-header-filter/README.md) to modify request headers for a client request. - - -## Response Header Filter - -Follow this [guide](https://github.com/nginxinc/nginx-gateway-fabric/tree/main/examples/http-response-header-filter/README.md) to modify response headers for a client request. diff --git a/site/content/how-to/traffic-management/response-headers.md b/site/content/how-to/traffic-management/response-headers.md new file mode 100644 index 0000000000..feb0420667 --- /dev/null +++ b/site/content/how-to/traffic-management/response-headers.md @@ -0,0 +1,155 @@ +--- +title: "HTTP Response Headers" +description: "Learn how to modify response headers for your HTTP Route using NGINX Gateway Fabric." +weight: 700 +toc: true +docs: "" +--- + +[HTTPRoute](https://gateway-api.sigs.k8s.io/api-types/httproute/) filters can modify the headers during the request-response lifecycle. [HTTP Header Modifiers](https://gateway-api.sigs.k8s.io/guides/http-header-modifier/?h=request#http-header-modifiers) can be used to add, modify or remove headers in incoming requests. We have two types of [filter](https://gateway-api.sigs.k8s.io/api-types/httproute/#filters-optional) that can be used to instruct the Gateway for desired behaviour. + +1. [ResponseHeaderModifier](https://gateway-api.sigs.k8s.io/guides/http-header-modifier/?h=request#http-response-header-modifier) to alter headers in response before responding to the downstream. + + +In this guide we will modify the headers for HTTP responses when client requests are made. For an introduction to exposing your application, we recommend that you follow the [basic guide]({{< relref "/how-to/traffic-management/routing-traffic-to-your-app.md" >}}) first. + + +## Prerequisites + +- [Install]({{< relref "/installation/" >}}) NGINX Gateway Fabric. +- [Expose NGINX Gateway Fabric]({{< relref "installation/expose-nginx-gateway-fabric.md" >}}) and save the public IP + address and port of NGINX Gateway Fabric into shell variables: + + ```text + GW_IP=XXX.YYY.ZZZ.III + GW_PORT= + ``` + +{{< note >}}In a production environment, you should have a DNS record for the external IP address that is exposed, and it should refer to the hostname that the gateway will forward for.{{< /note >}} + + +## Response Header Filter + +In this guide we will deploy NGINX Gateway Fabric and use HTTPRoute resources to route traffic to the server. We will use `ResponseHeaderModifier` filter to modify the HTTP response headers. + + +### Deploy the Headers application + +Begin by deploying the `headers` applications: + + ```shell + kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric/tree/main/examples/http-response-header-filter/headers.yaml + ``` + +Verify if the Pod is running in the `default` Namespace: + + ```shell + kubectl -n default get pods + ``` + + ```text + NAME READY STATUS RESTARTS AGE + headers-6f854c478-k9z2f 1/1 Running 0 32m + ``` + +### Deploy the Gateway API Resources for the Coffee Applications + + +The [gateway](https://gateway-api.sigs.k8s.io/api-types/gateway/) resource is typically deployed by the [cluster operator](https://gateway-api.sigs.k8s.io/concepts/roles-and-personas/#roles-and-personas_1). To deploy the gateway: + +```yaml +kubectl apply -f - < Date: Fri, 3 May 2024 10:46:56 -0600 Subject: [PATCH 43/72] add tests for code coverage --- internal/mode/static/state/graph/httproute_test.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/internal/mode/static/state/graph/httproute_test.go b/internal/mode/static/state/graph/httproute_test.go index 9e60c002c4..2693d458d6 100644 --- a/internal/mode/static/state/graph/httproute_test.go +++ b/internal/mode/static/state/graph/httproute_test.go @@ -865,6 +865,14 @@ func TestValidateFilter(t *testing.T) { expectErrCount: 0, name: "valid request header modifiers filter", }, + { + filter: gatewayv1.HTTPRouteFilter{ + Type: gatewayv1.HTTPRouteFilterResponseHeaderModifier, + ResponseHeaderModifier: &gatewayv1.HTTPHeaderFilter{}, + }, + expectErrCount: 0, + name: "valid response header modifiers filter", + }, { filter: gatewayv1.HTTPRouteFilter{ Type: gatewayv1.HTTPRouteFilterRequestMirror, From 07539fb398e539a34e22afb592ffd85ac91f5d19 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 3 May 2024 17:18:42 +0000 Subject: [PATCH 44/72] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- site/content/overview/gateway-api-compatibility.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/overview/gateway-api-compatibility.md b/site/content/overview/gateway-api-compatibility.md index d16f3e83a4..cdb8896d18 100644 --- a/site/content/overview/gateway-api-compatibility.md +++ b/site/content/overview/gateway-api-compatibility.md @@ -158,7 +158,7 @@ See the [static-mode]({{< relref "/reference/cli-help.md#static-mode">}}) comman - `requestRedirect`: Supported except for the experimental `path` field. If multiple filters are configured, NGINX Gateway Fabric will choose the first and ignore the rest. Incompatible with `urlRewrite`. - `requestHeaderModifier`: Supported. If multiple filters are configured, NGINX Gateway Fabric will choose the first and ignore the rest. - `urlRewrite`: Supported. If multiple filters are configured, NGINX Gateway Fabric will choose the first and ignore the rest. Incompatible with `requestRedirect`. - - `responseHeaderModifier`: Supported. If multiple filters are configured, NGINX Gateway Fabric will choose the first and ignore the rest. + - `responseHeaderModifier`: Supported. If multiple filters are configured, NGINX Gateway Fabric will choose the first and ignore the rest. - `requestMirror`, `extensionRef`: Not supported. - `backendRefs`: Partially supported. Backend ref `filters` are not supported. - `status` From c3f040a6a43d20e028c93e32224869bc3ded71e0 Mon Sep 17 00:00:00 2001 From: Saloni Date: Fri, 3 May 2024 11:52:10 -0600 Subject: [PATCH 45/72] update doc links --- site/content/how-to/traffic-management/response-headers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/how-to/traffic-management/response-headers.md b/site/content/how-to/traffic-management/response-headers.md index feb0420667..d2e079a1f1 100644 --- a/site/content/how-to/traffic-management/response-headers.md +++ b/site/content/how-to/traffic-management/response-headers.md @@ -38,7 +38,7 @@ In this guide we will deploy NGINX Gateway Fabric and use HTTPRoute resources to Begin by deploying the `headers` applications: ```shell - kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric/tree/main/examples/http-response-header-filter/headers.yaml + kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric/v1.3.0/examples/http-response-header-filter/headers.yaml ``` Verify if the Pod is running in the `default` Namespace: From 35861ad7f869ba75b91cb90ee1712aa30e489816 Mon Sep 17 00:00:00 2001 From: Saloni Date: Mon, 6 May 2024 11:46:51 -0600 Subject: [PATCH 46/72] update tutorial --- .../http-response-header-filter/headers.yaml | 11 +- .../http-route.yaml | 8 +- .../nginx/config/validation/http_filters.go | 8 +- .../config/validation/http_filters_test.go | 10 +- .../nginx/config/validation/http_validator.go | 2 +- .../state/dataplane/configuration_test.go | 21 +-- internal/mode/static/state/graph/httproute.go | 6 +- .../mode/static/state/graph/httproute_test.go | 12 +- .../fake_httpfields_validator.go | 148 +++++++++--------- .../mode/static/state/validation/validator.go | 2 +- .../traffic-management/response-headers.md | 22 +-- 11 files changed, 117 insertions(+), 133 deletions(-) diff --git a/examples/http-response-header-filter/headers.yaml b/examples/http-response-header-filter/headers.yaml index 409ddea8ff..7e6849f9b0 100644 --- a/examples/http-response-header-filter/headers.yaml +++ b/examples/http-response-header-filter/headers.yaml @@ -46,13 +46,10 @@ data: server { listen 8080; - add_header X-Custom-Header "this-stays-unchanged" always; - - add_header My-cool-header "this-is-the-appended-value" always; - - add_header Response-Overwrite-Header "this-value-to-be-returned" always; - - add_header X-Server-Version "1.2.3" always; + add_header X-Header-Unmodified "unmodified"; + add_header X-Header-Add "add-to"; + add_header X-Header-Set "overwrite"; + add_header X-Header-Remove "remove"; return 200 "ok"; } diff --git a/examples/http-response-header-filter/http-route.yaml b/examples/http-response-header-filter/http-route.yaml index da47eff3db..c48b760f8b 100644 --- a/examples/http-response-header-filter/http-route.yaml +++ b/examples/http-response-header-filter/http-route.yaml @@ -17,13 +17,13 @@ spec: - type: ResponseHeaderModifier responseHeaderModifier: set: - - name: Response-Overwrite-Header + - name: X-Header-Set value: overwritten-value add: - - name: My-cool-header - value: this-is-the-value + - name: X-Header-Add + value: this-is-the-appended-value remove: - - X-Server-Version + - X-Header-Remove backendRefs: - name: headers port: 80 diff --git a/internal/mode/static/nginx/config/validation/http_filters.go b/internal/mode/static/nginx/config/validation/http_filters.go index 4ae7fb8405..579e5c00a9 100644 --- a/internal/mode/static/nginx/config/validation/http_filters.go +++ b/internal/mode/static/nginx/config/validation/http_filters.go @@ -14,9 +14,9 @@ type HTTPRedirectValidator struct{} // HTTPURLRewriteValidator validates values for a URL rewrite. type HTTPURLRewriteValidator struct{} -// HTTPRequestHeaderValidator validates values for request headers, +// HTTPHeaderValidator validates values for request headers, // which in NGINX is done with the proxy_set_header directive. -type HTTPRequestHeaderValidator struct{} +type HTTPHeaderValidator struct{} var supportedRedirectSchemes = map[string]struct{}{ "http": {}, @@ -72,13 +72,13 @@ func (HTTPURLRewriteValidator) ValidateRewritePath(path string) error { return nil } -func (HTTPRequestHeaderValidator) HTTPHeaderValidator(name string) error { +func (HTTPHeaderValidator) ValidateRequestHeaderName(name string) error { return validateHeaderName(name) } var requestHeaderValueExamples = []string{"my-header-value", "example/12345=="} -func (HTTPRequestHeaderValidator) ValidateFilterHeaderValue(value string) error { +func (HTTPHeaderValidator) ValidateFilterHeaderValue(value string) error { // Variables in header values are supported by NGINX but not required by the Gateway API. return validateEscapedStringNoVarExpansion(value, requestHeaderValueExamples) } diff --git a/internal/mode/static/nginx/config/validation/http_filters_test.go b/internal/mode/static/nginx/config/validation/http_filters_test.go index aeb7765388..8d5a6cdd63 100644 --- a/internal/mode/static/nginx/config/validation/http_filters_test.go +++ b/internal/mode/static/nginx/config/validation/http_filters_test.go @@ -88,21 +88,21 @@ func TestValidateRewritePath(t *testing.T) { ) } -func TestHTTPHeaderValidator(t *testing.T) { - validator := HTTPRequestHeaderValidator{} +func TestValidateRequestHeaderName(t *testing.T) { + validator := HTTPHeaderValidator{} testValidValuesForSimpleValidator( t, - validator.HTTPHeaderValidator, + validator.ValidateRequestHeaderName, "Content-Encoding", "MyBespokeHeader", ) - testInvalidValuesForSimpleValidator(t, validator.HTTPHeaderValidator, "$Content-Encoding") + testInvalidValuesForSimpleValidator(t, validator.ValidateRequestHeaderName, "$Content-Encoding") } func TestValidateFilterHeaderValue(t *testing.T) { - validator := HTTPRequestHeaderValidator{} + validator := HTTPHeaderValidator{} testValidValuesForSimpleValidator( t, diff --git a/internal/mode/static/nginx/config/validation/http_validator.go b/internal/mode/static/nginx/config/validation/http_validator.go index f33adb8434..4e744124c2 100644 --- a/internal/mode/static/nginx/config/validation/http_validator.go +++ b/internal/mode/static/nginx/config/validation/http_validator.go @@ -11,7 +11,7 @@ type HTTPValidator struct { HTTPNJSMatchValidator HTTPRedirectValidator HTTPURLRewriteValidator - HTTPRequestHeaderValidator + HTTPHeaderValidator } var _ validation.HTTPFieldsValidator = HTTPValidator{} diff --git a/internal/mode/static/state/dataplane/configuration_test.go b/internal/mode/static/state/dataplane/configuration_test.go index f73dd60651..ab78a46388 100644 --- a/internal/mode/static/state/dataplane/configuration_test.go +++ b/internal/mode/static/state/dataplane/configuration_test.go @@ -2095,12 +2095,12 @@ func TestCreateFilters(t *testing.T) { }, } - invalidResponseHeaderModifiers2 := v1.HTTPRouteFilter{ + responseHeaderModifiers2 := v1.HTTPRouteFilter{ Type: v1.HTTPRouteFilterResponseHeaderModifier, ResponseHeaderModifier: &v1.HTTPHeaderFilter{ Set: []v1.HTTPHeader{ { - Name: "Server", + Name: "X-Route", Value: "new-response-value", }, }, @@ -2180,21 +2180,8 @@ func TestCreateFilters(t *testing.T) { rewrite2, requestHeaderModifiers1, requestHeaderModifiers2, - }, - expected: HTTPFilters{ - RequestRedirect: &expectedRedirect1, - RequestURLRewrite: &expectedRewrite1, - RequestHeaderModifiers: &expectedHeaderModifier1, - }, - msg: "two of each filter, first value for each wins", - }, - { - filters: []v1.HTTPRouteFilter{ - redirect1, - rewrite1, - requestHeaderModifiers1, responseHeaderModifiers1, - invalidResponseHeaderModifiers2, + responseHeaderModifiers2, }, expected: HTTPFilters{ RequestRedirect: &expectedRedirect1, @@ -2202,7 +2189,7 @@ func TestCreateFilters(t *testing.T) { RequestHeaderModifiers: &expectedHeaderModifier1, ResponseHeaderModifiers: &expectedresponseHeaderModifier, }, - msg: "one of each redirect, rewrite, request filter and two response filters, one invalid response filter", + msg: "two of each filter, first value for each wins", }, } diff --git a/internal/mode/static/state/graph/httproute.go b/internal/mode/static/state/graph/httproute.go index b33518bed0..bf7923f5fa 100644 --- a/internal/mode/static/state/graph/httproute.go +++ b/internal/mode/static/state/graph/httproute.go @@ -401,7 +401,7 @@ func validateFilterHeaderModifierFields( ) for _, h := range headerModifier.Add { - if err := validator.HTTPHeaderValidator(string(h.Name)); err != nil { + if err := validator.ValidateRequestHeaderName(string(h.Name)); err != nil { valErr := field.Invalid(headerModifierPath.Child(add), h, err.Error()) allErrs = append(allErrs, valErr) } @@ -411,7 +411,7 @@ func validateFilterHeaderModifierFields( } } for _, h := range headerModifier.Set { - if err := validator.HTTPHeaderValidator(string(h.Name)); err != nil { + if err := validator.ValidateRequestHeaderName(string(h.Name)); err != nil { valErr := field.Invalid(headerModifierPath.Child(set), h, err.Error()) allErrs = append(allErrs, valErr) } @@ -421,7 +421,7 @@ func validateFilterHeaderModifierFields( } } for _, h := range headerModifier.Remove { - if err := validator.HTTPHeaderValidator(h); err != nil { + if err := validator.ValidateRequestHeaderName(h); err != nil { valErr := field.Invalid(headerModifierPath.Child(remove), h, err.Error()) allErrs = append(allErrs, valErr) } diff --git a/internal/mode/static/state/graph/httproute_test.go b/internal/mode/static/state/graph/httproute_test.go index 2693d458d6..8db744f0b7 100644 --- a/internal/mode/static/state/graph/httproute_test.go +++ b/internal/mode/static/state/graph/httproute_test.go @@ -1231,7 +1231,7 @@ func TestValidateFilterRequestHeaderModifier(t *testing.T) { { validator: func() *validationfakes.FakeHTTPFieldsValidator { v := createAllValidValidator() - v.HTTPHeaderValidatorReturns(errors.New("Invalid header")) + v.ValidateRequestHeaderNameReturns(errors.New("Invalid header")) return v }(), filter: gatewayv1.HTTPRouteFilter{ @@ -1248,7 +1248,7 @@ func TestValidateFilterRequestHeaderModifier(t *testing.T) { { validator: func() *validationfakes.FakeHTTPFieldsValidator { v := createAllValidValidator() - v.HTTPHeaderValidatorReturns(errors.New("Invalid header")) + v.ValidateRequestHeaderNameReturns(errors.New("Invalid header")) return v }(), filter: gatewayv1.HTTPRouteFilter{ @@ -1281,7 +1281,7 @@ func TestValidateFilterRequestHeaderModifier(t *testing.T) { validator: func() *validationfakes.FakeHTTPFieldsValidator { v := createAllValidValidator() v.ValidateFilterHeaderValueReturns(errors.New("Invalid header value")) - v.HTTPHeaderValidatorReturns(errors.New("Invalid header")) + v.ValidateRequestHeaderNameReturns(errors.New("Invalid header")) return v }(), filter: gatewayv1.HTTPRouteFilter{ @@ -1375,7 +1375,7 @@ func TestValidateFilterResponseHeaderModifier(t *testing.T) { { validator: func() *validationfakes.FakeHTTPFieldsValidator { v := createAllValidValidator() - v.HTTPHeaderValidatorReturns(errors.New("Invalid header")) + v.ValidateRequestHeaderNameReturns(errors.New("Invalid header")) return v }(), filter: gatewayv1.HTTPRouteFilter{ @@ -1392,7 +1392,7 @@ func TestValidateFilterResponseHeaderModifier(t *testing.T) { { validator: func() *validationfakes.FakeHTTPFieldsValidator { v := createAllValidValidator() - v.HTTPHeaderValidatorReturns(errors.New("Invalid header")) + v.ValidateRequestHeaderNameReturns(errors.New("Invalid header")) return v }(), filter: gatewayv1.HTTPRouteFilter{ @@ -1425,7 +1425,7 @@ func TestValidateFilterResponseHeaderModifier(t *testing.T) { validator: func() *validationfakes.FakeHTTPFieldsValidator { v := createAllValidValidator() v.ValidateFilterHeaderValueReturns(errors.New("Invalid header value")) - v.HTTPHeaderValidatorReturns(errors.New("Invalid header")) + v.ValidateRequestHeaderNameReturns(errors.New("Invalid header")) return v }(), filter: gatewayv1.HTTPRouteFilter{ diff --git a/internal/mode/static/state/validation/validationfakes/fake_httpfields_validator.go b/internal/mode/static/state/validation/validationfakes/fake_httpfields_validator.go index 967d7deeb7..a8af77b4a5 100644 --- a/internal/mode/static/state/validation/validationfakes/fake_httpfields_validator.go +++ b/internal/mode/static/state/validation/validationfakes/fake_httpfields_validator.go @@ -8,17 +8,6 @@ import ( ) type FakeHTTPFieldsValidator struct { - HTTPHeaderValidatorStub func(string) error - hTTPHeaderValidatorMutex sync.RWMutex - hTTPHeaderValidatorArgsForCall []struct { - arg1 string - } - hTTPHeaderValidatorReturns struct { - result1 error - } - hTTPHeaderValidatorReturnsOnCall map[int]struct { - result1 error - } ValidateFilterHeaderValueStub func(string) error validateFilterHeaderValueMutex sync.RWMutex validateFilterHeaderValueArgsForCall []struct { @@ -146,6 +135,17 @@ type FakeHTTPFieldsValidator struct { result1 bool result2 []string } + ValidateRequestHeaderNameStub func(string) error + validateRequestHeaderNameMutex sync.RWMutex + validateRequestHeaderNameArgsForCall []struct { + arg1 string + } + validateRequestHeaderNameReturns struct { + result1 error + } + validateRequestHeaderNameReturnsOnCall map[int]struct { + result1 error + } ValidateRewritePathStub func(string) error validateRewritePathMutex sync.RWMutex validateRewritePathArgsForCall []struct { @@ -161,67 +161,6 @@ type FakeHTTPFieldsValidator struct { invocationsMutex sync.RWMutex } -func (fake *FakeHTTPFieldsValidator) HTTPHeaderValidator(arg1 string) error { - fake.hTTPHeaderValidatorMutex.Lock() - ret, specificReturn := fake.hTTPHeaderValidatorReturnsOnCall[len(fake.hTTPHeaderValidatorArgsForCall)] - fake.hTTPHeaderValidatorArgsForCall = append(fake.hTTPHeaderValidatorArgsForCall, struct { - arg1 string - }{arg1}) - stub := fake.HTTPHeaderValidatorStub - fakeReturns := fake.hTTPHeaderValidatorReturns - fake.recordInvocation("HTTPHeaderValidator", []interface{}{arg1}) - fake.hTTPHeaderValidatorMutex.Unlock() - if stub != nil { - return stub(arg1) - } - if specificReturn { - return ret.result1 - } - return fakeReturns.result1 -} - -func (fake *FakeHTTPFieldsValidator) HTTPHeaderValidatorCallCount() int { - fake.hTTPHeaderValidatorMutex.RLock() - defer fake.hTTPHeaderValidatorMutex.RUnlock() - return len(fake.hTTPHeaderValidatorArgsForCall) -} - -func (fake *FakeHTTPFieldsValidator) HTTPHeaderValidatorCalls(stub func(string) error) { - fake.hTTPHeaderValidatorMutex.Lock() - defer fake.hTTPHeaderValidatorMutex.Unlock() - fake.HTTPHeaderValidatorStub = stub -} - -func (fake *FakeHTTPFieldsValidator) HTTPHeaderValidatorArgsForCall(i int) string { - fake.hTTPHeaderValidatorMutex.RLock() - defer fake.hTTPHeaderValidatorMutex.RUnlock() - argsForCall := fake.hTTPHeaderValidatorArgsForCall[i] - return argsForCall.arg1 -} - -func (fake *FakeHTTPFieldsValidator) HTTPHeaderValidatorReturns(result1 error) { - fake.hTTPHeaderValidatorMutex.Lock() - defer fake.hTTPHeaderValidatorMutex.Unlock() - fake.HTTPHeaderValidatorStub = nil - fake.hTTPHeaderValidatorReturns = struct { - result1 error - }{result1} -} - -func (fake *FakeHTTPFieldsValidator) HTTPHeaderValidatorReturnsOnCall(i int, result1 error) { - fake.hTTPHeaderValidatorMutex.Lock() - defer fake.hTTPHeaderValidatorMutex.Unlock() - fake.HTTPHeaderValidatorStub = nil - if fake.hTTPHeaderValidatorReturnsOnCall == nil { - fake.hTTPHeaderValidatorReturnsOnCall = make(map[int]struct { - result1 error - }) - } - fake.hTTPHeaderValidatorReturnsOnCall[i] = struct { - result1 error - }{result1} -} - func (fake *FakeHTTPFieldsValidator) ValidateFilterHeaderValue(arg1 string) error { fake.validateFilterHeaderValueMutex.Lock() ret, specificReturn := fake.validateFilterHeaderValueReturnsOnCall[len(fake.validateFilterHeaderValueArgsForCall)] @@ -902,6 +841,67 @@ func (fake *FakeHTTPFieldsValidator) ValidateRedirectStatusCodeReturnsOnCall(i i }{result1, result2} } +func (fake *FakeHTTPFieldsValidator) ValidateRequestHeaderName(arg1 string) error { + fake.validateRequestHeaderNameMutex.Lock() + ret, specificReturn := fake.validateRequestHeaderNameReturnsOnCall[len(fake.validateRequestHeaderNameArgsForCall)] + fake.validateRequestHeaderNameArgsForCall = append(fake.validateRequestHeaderNameArgsForCall, struct { + arg1 string + }{arg1}) + stub := fake.ValidateRequestHeaderNameStub + fakeReturns := fake.validateRequestHeaderNameReturns + fake.recordInvocation("ValidateRequestHeaderName", []interface{}{arg1}) + fake.validateRequestHeaderNameMutex.Unlock() + if stub != nil { + return stub(arg1) + } + if specificReturn { + return ret.result1 + } + return fakeReturns.result1 +} + +func (fake *FakeHTTPFieldsValidator) ValidateRequestHeaderNameCallCount() int { + fake.validateRequestHeaderNameMutex.RLock() + defer fake.validateRequestHeaderNameMutex.RUnlock() + return len(fake.validateRequestHeaderNameArgsForCall) +} + +func (fake *FakeHTTPFieldsValidator) ValidateRequestHeaderNameCalls(stub func(string) error) { + fake.validateRequestHeaderNameMutex.Lock() + defer fake.validateRequestHeaderNameMutex.Unlock() + fake.ValidateRequestHeaderNameStub = stub +} + +func (fake *FakeHTTPFieldsValidator) ValidateRequestHeaderNameArgsForCall(i int) string { + fake.validateRequestHeaderNameMutex.RLock() + defer fake.validateRequestHeaderNameMutex.RUnlock() + argsForCall := fake.validateRequestHeaderNameArgsForCall[i] + return argsForCall.arg1 +} + +func (fake *FakeHTTPFieldsValidator) ValidateRequestHeaderNameReturns(result1 error) { + fake.validateRequestHeaderNameMutex.Lock() + defer fake.validateRequestHeaderNameMutex.Unlock() + fake.ValidateRequestHeaderNameStub = nil + fake.validateRequestHeaderNameReturns = struct { + result1 error + }{result1} +} + +func (fake *FakeHTTPFieldsValidator) ValidateRequestHeaderNameReturnsOnCall(i int, result1 error) { + fake.validateRequestHeaderNameMutex.Lock() + defer fake.validateRequestHeaderNameMutex.Unlock() + fake.ValidateRequestHeaderNameStub = nil + if fake.validateRequestHeaderNameReturnsOnCall == nil { + fake.validateRequestHeaderNameReturnsOnCall = make(map[int]struct { + result1 error + }) + } + fake.validateRequestHeaderNameReturnsOnCall[i] = struct { + result1 error + }{result1} +} + func (fake *FakeHTTPFieldsValidator) ValidateRewritePath(arg1 string) error { fake.validateRewritePathMutex.Lock() ret, specificReturn := fake.validateRewritePathReturnsOnCall[len(fake.validateRewritePathArgsForCall)] @@ -966,8 +966,6 @@ func (fake *FakeHTTPFieldsValidator) ValidateRewritePathReturnsOnCall(i int, res func (fake *FakeHTTPFieldsValidator) Invocations() map[string][][]interface{} { fake.invocationsMutex.RLock() defer fake.invocationsMutex.RUnlock() - fake.hTTPHeaderValidatorMutex.RLock() - defer fake.hTTPHeaderValidatorMutex.RUnlock() fake.validateFilterHeaderValueMutex.RLock() defer fake.validateFilterHeaderValueMutex.RUnlock() fake.validateHeaderNameInMatchMutex.RLock() @@ -990,6 +988,8 @@ func (fake *FakeHTTPFieldsValidator) Invocations() map[string][][]interface{} { defer fake.validateRedirectSchemeMutex.RUnlock() fake.validateRedirectStatusCodeMutex.RLock() defer fake.validateRedirectStatusCodeMutex.RUnlock() + fake.validateRequestHeaderNameMutex.RLock() + defer fake.validateRequestHeaderNameMutex.RUnlock() fake.validateRewritePathMutex.RLock() defer fake.validateRewritePathMutex.RUnlock() copiedInvocations := map[string][][]interface{}{} diff --git a/internal/mode/static/state/validation/validator.go b/internal/mode/static/state/validation/validator.go index 385eb11e78..222b2483a1 100644 --- a/internal/mode/static/state/validation/validator.go +++ b/internal/mode/static/state/validation/validator.go @@ -25,7 +25,7 @@ type HTTPFieldsValidator interface { ValidateRedirectStatusCode(statusCode int) (valid bool, supportedValues []string) ValidateHostname(hostname string) error ValidateRewritePath(path string) error - HTTPHeaderValidator(name string) error + ValidateRequestHeaderName(name string) error ValidateFilterHeaderValue(value string) error } diff --git a/site/content/how-to/traffic-management/response-headers.md b/site/content/how-to/traffic-management/response-headers.md index d2e079a1f1..bc37553e1d 100644 --- a/site/content/how-to/traffic-management/response-headers.md +++ b/site/content/how-to/traffic-management/response-headers.md @@ -99,13 +99,13 @@ spec: - type: ResponseHeaderModifier responseHeaderModifier: set: - - name: Response-Overwrite-Header + - name: X-Header-Set value: overwritten-value add: - - name: My-cool-header - value: this-is-the-value + - name: X-Header-Add + value: this-is-the-appended-value remove: - - X-Server-Version + - X-Header-Remove backendRefs: - name: headers port: 80 @@ -117,14 +117,14 @@ This HTTPRoute has a few important properties: - The `parentRefs` references the gateway resource that we created, and specifically defines the `http` listener to attach to, via the `sectionName` field. - `cafe.example.com` is the hostname that is matched for all requests to the backends defined in this HTTPRoute. - The `match` rule defines that all requests with the path prefix `/headers` are sent to the `headers` Service. -- There is `ResponseHeaderModifier` filter defined for the path prefix `/headers` to overwrite value for header `Response-Overwrite-Header`, append value to header `My-cool-header` and remove `X-Server-Version` header from the HTTP response. +- There is `ResponseHeaderModifier` filter defined for the path prefix `/headers` to overwrite value for header `X-Header-Set`, append value to header `X-Header-Add` and remove `X-Header-Remove` header from the HTTP response. ### Send Traffic to Headers To access the application, we will use `curl` to send requests to the `headers` endpoint. -Notice our configured header values can be seen in the `responseHeaders` section below, and that the `X-Server-Version` header is absent. The header `My-cool-header` gets appended with the new value and `Response-Overwrite-Header` gets overwritten to `overwritten-value` as defined in the *HttpRoute*. +Notice our configured header values can be seen in the `responseHeaders` section below, and that the `X-Header-Remove` header is absent. The header `X-Header-Add` gets appended with the new value and `X-Header-Set` gets overwritten to `overwritten-value` as defined in the *HttpRoute*. ```shell curl -v -i --resolve cafe.example.com:$GW_PORT:$GW_IP http://cafe.example.com:$GW_PORT/headers @@ -133,14 +133,14 @@ curl -v -i --resolve cafe.example.com:$GW_PORT:$GW_IP http://cafe.example.com:$G ```text HTTP/1.1 200 OK Server: nginx/1.25.5 -Date: Thu, 02 May 2024 20:58:54 GMT +Date: Mon, 06 May 2024 17:58:33 GMT Content-Type: text/plain Content-Length: 2 Connection: keep-alive -X-Custom-Header: this-stays-unchanged -My-cool-header: this-is-the-appended-value -My-cool-header: this-is-the-value -Response-Overwrite-Header: overwritten-value +X-Header-Unmodified: unmodified +X-Header-Add: add-to +X-Header-Add: this-is-the-appended-value +X-Header-Set: overwritten-value ok ``` From b661b40aed3609d2491501091097f165daa189db Mon Sep 17 00:00:00 2001 From: salonichf5 <146118978+salonichf5@users.noreply.github.com> Date: Mon, 6 May 2024 12:02:35 -0600 Subject: [PATCH 47/72] Update site/content/how-to/traffic-management/response-headers.md Co-authored-by: Kate Osborn <50597707+kate-osborn@users.noreply.github.com> --- site/content/how-to/traffic-management/response-headers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/how-to/traffic-management/response-headers.md b/site/content/how-to/traffic-management/response-headers.md index bc37553e1d..f7c9d94304 100644 --- a/site/content/how-to/traffic-management/response-headers.md +++ b/site/content/how-to/traffic-management/response-headers.md @@ -1,6 +1,6 @@ --- title: "HTTP Response Headers" -description: "Learn how to modify response headers for your HTTP Route using NGINX Gateway Fabric." +description: "Learn how to modify the response headers of your application using NGINX Gateway Fabric." weight: 700 toc: true docs: "" From 292ba3a0d78e1dd95785c85c7a97225bfb86d940 Mon Sep 17 00:00:00 2001 From: salonichf5 <146118978+salonichf5@users.noreply.github.com> Date: Mon, 6 May 2024 12:02:54 -0600 Subject: [PATCH 48/72] Update site/content/how-to/traffic-management/response-headers.md Co-authored-by: Kate Osborn <50597707+kate-osborn@users.noreply.github.com> --- site/content/how-to/traffic-management/response-headers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/how-to/traffic-management/response-headers.md b/site/content/how-to/traffic-management/response-headers.md index f7c9d94304..a852df0064 100644 --- a/site/content/how-to/traffic-management/response-headers.md +++ b/site/content/how-to/traffic-management/response-headers.md @@ -35,7 +35,7 @@ In this guide we will deploy NGINX Gateway Fabric and use HTTPRoute resources to ### Deploy the Headers application -Begin by deploying the `headers` applications: +Begin by deploying the example application `headers`: ```shell kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric/v1.3.0/examples/http-response-header-filter/headers.yaml From db137954f972cc74cef73a65edd90f308dda0446 Mon Sep 17 00:00:00 2001 From: salonichf5 <146118978+salonichf5@users.noreply.github.com> Date: Mon, 6 May 2024 12:03:03 -0600 Subject: [PATCH 49/72] Update site/content/how-to/traffic-management/response-headers.md Co-authored-by: Kate Osborn <50597707+kate-osborn@users.noreply.github.com> --- site/content/how-to/traffic-management/response-headers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/how-to/traffic-management/response-headers.md b/site/content/how-to/traffic-management/response-headers.md index a852df0064..2ed58d4ec9 100644 --- a/site/content/how-to/traffic-management/response-headers.md +++ b/site/content/how-to/traffic-management/response-headers.md @@ -72,7 +72,7 @@ spec: EOF ``` -### Configure Routing +### Configure the HTTPRoute This gateway defines a single listener on port 80. Since no hostname is specified, this listener matches on all hostnames. From 6c0ae2ca739b59642af96cfd56d5230e6cb79d6b Mon Sep 17 00:00:00 2001 From: salonichf5 <146118978+salonichf5@users.noreply.github.com> Date: Mon, 6 May 2024 12:03:42 -0600 Subject: [PATCH 50/72] Update site/content/how-to/traffic-management/response-headers.md Co-authored-by: Kate Osborn <50597707+kate-osborn@users.noreply.github.com> --- site/content/how-to/traffic-management/response-headers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/how-to/traffic-management/response-headers.md b/site/content/how-to/traffic-management/response-headers.md index 2ed58d4ec9..03582d123b 100644 --- a/site/content/how-to/traffic-management/response-headers.md +++ b/site/content/how-to/traffic-management/response-headers.md @@ -120,7 +120,7 @@ This HTTPRoute has a few important properties: - There is `ResponseHeaderModifier` filter defined for the path prefix `/headers` to overwrite value for header `X-Header-Set`, append value to header `X-Header-Add` and remove `X-Header-Remove` header from the HTTP response. -### Send Traffic to Headers +### Send Traffic to the Headers Application To access the application, we will use `curl` to send requests to the `headers` endpoint. From 7bc98979d7acb05f797e781f2d258cbd746a2937 Mon Sep 17 00:00:00 2001 From: salonichf5 <146118978+salonichf5@users.noreply.github.com> Date: Mon, 6 May 2024 12:05:52 -0600 Subject: [PATCH 51/72] Update site/content/how-to/traffic-management/response-headers.md Co-authored-by: Kate Osborn <50597707+kate-osborn@users.noreply.github.com> --- site/content/how-to/traffic-management/response-headers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/how-to/traffic-management/response-headers.md b/site/content/how-to/traffic-management/response-headers.md index 03582d123b..79a12af135 100644 --- a/site/content/how-to/traffic-management/response-headers.md +++ b/site/content/how-to/traffic-management/response-headers.md @@ -30,7 +30,7 @@ In this guide we will modify the headers for HTTP responses when client requests ## Response Header Filter -In this guide we will deploy NGINX Gateway Fabric and use HTTPRoute resources to route traffic to the server. We will use `ResponseHeaderModifier` filter to modify the HTTP response headers. +In this guide, we will configure an HTTPRoute with a `ResponseHeaderModifier` filter to modify the HTTP response headers of an application. ### Deploy the Headers application From f390cccb234346aa2b0df1aae96124a4ecf8a08d Mon Sep 17 00:00:00 2001 From: salonichf5 <146118978+salonichf5@users.noreply.github.com> Date: Mon, 6 May 2024 12:06:02 -0600 Subject: [PATCH 52/72] Update site/content/how-to/traffic-management/response-headers.md Co-authored-by: Kate Osborn <50597707+kate-osborn@users.noreply.github.com> --- site/content/how-to/traffic-management/response-headers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/how-to/traffic-management/response-headers.md b/site/content/how-to/traffic-management/response-headers.md index 79a12af135..0ed0e64f5a 100644 --- a/site/content/how-to/traffic-management/response-headers.md +++ b/site/content/how-to/traffic-management/response-headers.md @@ -52,7 +52,7 @@ Verify if the Pod is running in the `default` Namespace: headers-6f854c478-k9z2f 1/1 Running 0 32m ``` -### Deploy the Gateway API Resources for the Coffee Applications +### Deploy the Gateway API Resources for the Header Application The [gateway](https://gateway-api.sigs.k8s.io/api-types/gateway/) resource is typically deployed by the [cluster operator](https://gateway-api.sigs.k8s.io/concepts/roles-and-personas/#roles-and-personas_1). To deploy the gateway: From 3aa2f23ef754bbf9c1ee727680bc86c8f6421318 Mon Sep 17 00:00:00 2001 From: Saloni Date: Mon, 6 May 2024 13:16:44 -0600 Subject: [PATCH 53/72] tutorial modifications --- .../http-route-filters.yaml | 29 ++++++++ .../http-route.yaml | 11 --- .../traffic-management/response-headers.md | 68 +++++++++++++++++-- 3 files changed, 90 insertions(+), 18 deletions(-) create mode 100644 examples/http-response-header-filter/http-route-filters.yaml diff --git a/examples/http-response-header-filter/http-route-filters.yaml b/examples/http-response-header-filter/http-route-filters.yaml new file mode 100644 index 0000000000..c48b760f8b --- /dev/null +++ b/examples/http-response-header-filter/http-route-filters.yaml @@ -0,0 +1,29 @@ +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: headers +spec: + parentRefs: + - name: gateway + sectionName: http + hostnames: + - "cafe.example.com" + rules: + - matches: + - path: + type: PathPrefix + value: /headers + filters: + - type: ResponseHeaderModifier + responseHeaderModifier: + set: + - name: X-Header-Set + value: overwritten-value + add: + - name: X-Header-Add + value: this-is-the-appended-value + remove: + - X-Header-Remove + backendRefs: + - name: headers + port: 80 diff --git a/examples/http-response-header-filter/http-route.yaml b/examples/http-response-header-filter/http-route.yaml index c48b760f8b..6777308a19 100644 --- a/examples/http-response-header-filter/http-route.yaml +++ b/examples/http-response-header-filter/http-route.yaml @@ -13,17 +13,6 @@ spec: - path: type: PathPrefix value: /headers - filters: - - type: ResponseHeaderModifier - responseHeaderModifier: - set: - - name: X-Header-Set - value: overwritten-value - add: - - name: X-Header-Add - value: this-is-the-appended-value - remove: - - X-Header-Remove backendRefs: - name: headers port: 80 diff --git a/site/content/how-to/traffic-management/response-headers.md b/site/content/how-to/traffic-management/response-headers.md index 0ed0e64f5a..3b046d6536 100644 --- a/site/content/how-to/traffic-management/response-headers.md +++ b/site/content/how-to/traffic-management/response-headers.md @@ -30,7 +30,7 @@ In this guide we will modify the headers for HTTP responses when client requests ## Response Header Filter -In this guide, we will configure an HTTPRoute with a `ResponseHeaderModifier` filter to modify the HTTP response headers of an application. +In this guide, we will configure an HTTPRoute with a `ResponseHeaderModifier` filter to modify the HTTP response headers of an application. ### Deploy the Headers application @@ -72,12 +72,68 @@ spec: EOF ``` -### Configure the HTTPRoute +### Configure the basic HTTPRoute This gateway defines a single listener on port 80. Since no hostname is specified, this listener matches on all hostnames. The [HTTPRoute](https://gateway-api.sigs.k8s.io/api-types/httproute/) is typically deployed by the [application developer](https://gateway-api.sigs.k8s.io/concepts/roles-and-personas/#roles-and-personas_1). To deploy the `headers` HTTPRoute: + +```yaml +kubectl apply -f - < Date: Mon, 6 May 2024 13:19:21 -0600 Subject: [PATCH 54/72] remove extras --- site/content/how-to/traffic-management/response-headers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/how-to/traffic-management/response-headers.md b/site/content/how-to/traffic-management/response-headers.md index 3b046d6536..3491da1c8a 100644 --- a/site/content/how-to/traffic-management/response-headers.md +++ b/site/content/how-to/traffic-management/response-headers.md @@ -6,7 +6,7 @@ toc: true docs: "" --- -[HTTPRoute](https://gateway-api.sigs.k8s.io/api-types/httproute/) filters can modify the headers during the request-response lifecycle. [HTTP Header Modifiers](https://gateway-api.sigs.k8s.io/guides/http-header-modifier/?h=request#http-header-modifiers) can be used to add, modify or remove headers in incoming requests. We have two types of [filter](https://gateway-api.sigs.k8s.io/api-types/httproute/#filters-optional) that can be used to instruct the Gateway for desired behaviour. +[HTTPRoute](https://gateway-api.sigs.k8s.io/api-types/httproute/) filters can modify the headers during the request-response lifecycle. [HTTP Header Modifiers](https://gateway-api.sigs.k8s.io/guides/http-header-modifier/?h=request#http-header-modifiers) can be used to add, modify or remove headers in incoming requests. 1. [ResponseHeaderModifier](https://gateway-api.sigs.k8s.io/guides/http-header-modifier/?h=request#http-response-header-modifier) to alter headers in response before responding to the downstream. From 9c40e3a3bedaacc1e3847c618d204a4b0ccef5ad Mon Sep 17 00:00:00 2001 From: salonichf5 <146118978+salonichf5@users.noreply.github.com> Date: Mon, 6 May 2024 14:15:10 -0600 Subject: [PATCH 55/72] Update site/content/how-to/traffic-management/response-headers.md Co-authored-by: Saylor Berman --- site/content/how-to/traffic-management/response-headers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/how-to/traffic-management/response-headers.md b/site/content/how-to/traffic-management/response-headers.md index 3491da1c8a..c754ed391a 100644 --- a/site/content/how-to/traffic-management/response-headers.md +++ b/site/content/how-to/traffic-management/response-headers.md @@ -8,7 +8,7 @@ docs: "" [HTTPRoute](https://gateway-api.sigs.k8s.io/api-types/httproute/) filters can modify the headers during the request-response lifecycle. [HTTP Header Modifiers](https://gateway-api.sigs.k8s.io/guides/http-header-modifier/?h=request#http-header-modifiers) can be used to add, modify or remove headers in incoming requests. -1. [ResponseHeaderModifier](https://gateway-api.sigs.k8s.io/guides/http-header-modifier/?h=request#http-response-header-modifier) to alter headers in response before responding to the downstream. +1. The [ResponseHeaderModifier](https://gateway-api.sigs.k8s.io/guides/http-header-modifier/#http-response-header-modifier) is used to alter headers in a response to the client. In this guide we will modify the headers for HTTP responses when client requests are made. For an introduction to exposing your application, we recommend that you follow the [basic guide]({{< relref "/how-to/traffic-management/routing-traffic-to-your-app.md" >}}) first. From 3ff6015fe29f7dd5c40072cd3dff35886ac8d559 Mon Sep 17 00:00:00 2001 From: Saloni Date: Mon, 6 May 2024 15:06:19 -0600 Subject: [PATCH 56/72] fix repetition in doc --- .../how-to/traffic-management/response-headers.md | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/site/content/how-to/traffic-management/response-headers.md b/site/content/how-to/traffic-management/response-headers.md index c754ed391a..0c9dfe4ff4 100644 --- a/site/content/how-to/traffic-management/response-headers.md +++ b/site/content/how-to/traffic-management/response-headers.md @@ -30,9 +30,6 @@ In this guide we will modify the headers for HTTP responses when client requests ## Response Header Filter -In this guide, we will configure an HTTPRoute with a `ResponseHeaderModifier` filter to modify the HTTP response headers of an application. - - ### Deploy the Headers application Begin by deploying the example application `headers`: @@ -76,7 +73,7 @@ EOF This gateway defines a single listener on port 80. Since no hostname is specified, this listener matches on all hostnames. -The [HTTPRoute](https://gateway-api.sigs.k8s.io/api-types/httproute/) is typically deployed by the [application developer](https://gateway-api.sigs.k8s.io/concepts/roles-and-personas/#roles-and-personas_1). To deploy the `headers` HTTPRoute: +The HTTPRoute is typically deployed by the [application developer](https://gateway-api.sigs.k8s.io/concepts/roles-and-personas/#roles-and-personas_1). To deploy the `headers` HTTPRoute: ```yaml @@ -102,12 +99,9 @@ spec: EOF ``` - - - ### Send Traffic to the Basic Headers Application -To access the application, we will use `curl` to send requests to the `headers` endpoint. Notice our configured header values can be seen in the `responseHeaders` section below. We have four custom response headers defined. +To access the application, we will use `curl` to send requests to the `headers` endpoint. Notice our configured header values can be seen in the `responseHeaders` section below. We have four custom response headers defined - `X-Header-Unmodified`, `X-Header-Add`, `X-Header-Set`, `X-Header-Remove`. ```shell curl -i --resolve cafe.example.com:$GW_PORT:$GW_IP http://cafe.example.com:$GW_PORT/headers @@ -130,9 +124,7 @@ ok ### Configure the HTTPRoute with Response Header Modifiers -This gateway defines a single listener on port 80. Since no hostname is specified, this listener matches on all hostnames. - -The [HTTPRoute](https://gateway-api.sigs.k8s.io/api-types/httproute/) is typically deployed by the [application developer](https://gateway-api.sigs.k8s.io/concepts/roles-and-personas/#roles-and-personas_1). To deploy the `headers` HTTPRoute with `filters` to modify response headers: +Let's configure the HTTPRoute with `ResponseHeaderModifier` filter: ```yaml kubectl apply -f - < Date: Tue, 7 May 2024 11:49:05 -0600 Subject: [PATCH 57/72] Update site/content/how-to/traffic-management/response-headers.md Co-authored-by: Jon Torre <78599298+Jcahilltorre@users.noreply.github.com> --- site/content/how-to/traffic-management/response-headers.md | 1 - 1 file changed, 1 deletion(-) diff --git a/site/content/how-to/traffic-management/response-headers.md b/site/content/how-to/traffic-management/response-headers.md index 0c9dfe4ff4..4dffdd1215 100644 --- a/site/content/how-to/traffic-management/response-headers.md +++ b/site/content/how-to/traffic-management/response-headers.md @@ -3,7 +3,6 @@ title: "HTTP Response Headers" description: "Learn how to modify the response headers of your application using NGINX Gateway Fabric." weight: 700 toc: true -docs: "" --- [HTTPRoute](https://gateway-api.sigs.k8s.io/api-types/httproute/) filters can modify the headers during the request-response lifecycle. [HTTP Header Modifiers](https://gateway-api.sigs.k8s.io/guides/http-header-modifier/?h=request#http-header-modifiers) can be used to add, modify or remove headers in incoming requests. From f3695e457e942a204c438ccbb4ed57047c455fce Mon Sep 17 00:00:00 2001 From: salonichf5 <146118978+salonichf5@users.noreply.github.com> Date: Tue, 7 May 2024 15:45:56 -0600 Subject: [PATCH 58/72] Update site/content/how-to/traffic-management/response-headers.md Co-authored-by: Kate Osborn <50597707+kate-osborn@users.noreply.github.com> --- site/content/how-to/traffic-management/response-headers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/how-to/traffic-management/response-headers.md b/site/content/how-to/traffic-management/response-headers.md index 4dffdd1215..738c4dc85a 100644 --- a/site/content/how-to/traffic-management/response-headers.md +++ b/site/content/how-to/traffic-management/response-headers.md @@ -98,7 +98,7 @@ spec: EOF ``` -### Send Traffic to the Basic Headers Application +### Send Traffic to the Headers Application To access the application, we will use `curl` to send requests to the `headers` endpoint. Notice our configured header values can be seen in the `responseHeaders` section below. We have four custom response headers defined - `X-Header-Unmodified`, `X-Header-Add`, `X-Header-Set`, `X-Header-Remove`. From e56b8abc5b1fe1338547b7738aeb571079aa3005 Mon Sep 17 00:00:00 2001 From: salonichf5 <146118978+salonichf5@users.noreply.github.com> Date: Tue, 7 May 2024 15:46:05 -0600 Subject: [PATCH 59/72] Update site/content/how-to/traffic-management/response-headers.md Co-authored-by: Kate Osborn <50597707+kate-osborn@users.noreply.github.com> --- site/content/how-to/traffic-management/response-headers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/how-to/traffic-management/response-headers.md b/site/content/how-to/traffic-management/response-headers.md index 738c4dc85a..910f8f4cf8 100644 --- a/site/content/how-to/traffic-management/response-headers.md +++ b/site/content/how-to/traffic-management/response-headers.md @@ -123,7 +123,7 @@ ok ### Configure the HTTPRoute with Response Header Modifiers -Let's configure the HTTPRoute with `ResponseHeaderModifier` filter: +Let's update the HTTPRoute by adding a `ResponseHeaderModifier` filter: ```yaml kubectl apply -f - < Date: Tue, 7 May 2024 15:46:12 -0600 Subject: [PATCH 60/72] Update site/content/how-to/traffic-management/response-headers.md Co-authored-by: Kate Osborn <50597707+kate-osborn@users.noreply.github.com> --- site/content/how-to/traffic-management/response-headers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/how-to/traffic-management/response-headers.md b/site/content/how-to/traffic-management/response-headers.md index 910f8f4cf8..ca2f21ded7 100644 --- a/site/content/how-to/traffic-management/response-headers.md +++ b/site/content/how-to/traffic-management/response-headers.md @@ -121,7 +121,7 @@ X-Header-Remove: remove ok ``` -### Configure the HTTPRoute with Response Header Modifiers +### Update the HTTPRoute to Modify the Response Headers Let's update the HTTPRoute by adding a `ResponseHeaderModifier` filter: From 1dd4c8ab2415d7ffe6beb0fd21b4bd19f267b3fa Mon Sep 17 00:00:00 2001 From: salonichf5 <146118978+salonichf5@users.noreply.github.com> Date: Tue, 7 May 2024 15:46:27 -0600 Subject: [PATCH 61/72] Update site/content/how-to/traffic-management/response-headers.md Co-authored-by: Kate Osborn <50597707+kate-osborn@users.noreply.github.com> --- site/content/how-to/traffic-management/response-headers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/how-to/traffic-management/response-headers.md b/site/content/how-to/traffic-management/response-headers.md index ca2f21ded7..6774712016 100644 --- a/site/content/how-to/traffic-management/response-headers.md +++ b/site/content/how-to/traffic-management/response-headers.md @@ -100,7 +100,7 @@ EOF ### Send Traffic to the Headers Application -To access the application, we will use `curl` to send requests to the `headers` endpoint. Notice our configured header values can be seen in the `responseHeaders` section below. We have four custom response headers defined - `X-Header-Unmodified`, `X-Header-Add`, `X-Header-Set`, `X-Header-Remove`. +We will use `curl` with the `-i` flag to access the application and include the response headers in the output: ```shell curl -i --resolve cafe.example.com:$GW_PORT:$GW_IP http://cafe.example.com:$GW_PORT/headers From 71e1881fe4c64a8769f5acdf10fccfe5858671ce Mon Sep 17 00:00:00 2001 From: salonichf5 <146118978+salonichf5@users.noreply.github.com> Date: Tue, 7 May 2024 15:46:37 -0600 Subject: [PATCH 62/72] Update site/content/how-to/traffic-management/response-headers.md Co-authored-by: Kate Osborn <50597707+kate-osborn@users.noreply.github.com> --- site/content/how-to/traffic-management/response-headers.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/site/content/how-to/traffic-management/response-headers.md b/site/content/how-to/traffic-management/response-headers.md index 6774712016..2b70b84cdb 100644 --- a/site/content/how-to/traffic-management/response-headers.md +++ b/site/content/how-to/traffic-management/response-headers.md @@ -120,7 +120,13 @@ X-Header-Remove: remove ok ``` +In the output above, you can see that the headers application adds the following custom headers to the response: +- X-Header-Unmodified: unmodified +- X-Header-Add: add-to +- X-Header-Set: overwrite +- X-Header-Remove: remove +In the next section we will modify these headers by adding a ResponseHeaderModifier filter to the headers HTTPRoute. ### Update the HTTPRoute to Modify the Response Headers Let's update the HTTPRoute by adding a `ResponseHeaderModifier` filter: From 9fbb7deab915808d2bf80d87b62a1670e6cf17ad Mon Sep 17 00:00:00 2001 From: salonichf5 <146118978+salonichf5@users.noreply.github.com> Date: Tue, 7 May 2024 15:46:56 -0600 Subject: [PATCH 63/72] Update internal/mode/static/nginx/config/validation/http_filters.go Co-authored-by: Kate Osborn <50597707+kate-osborn@users.noreply.github.com> --- internal/mode/static/nginx/config/validation/http_filters.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/mode/static/nginx/config/validation/http_filters.go b/internal/mode/static/nginx/config/validation/http_filters.go index 579e5c00a9..0a8de17957 100644 --- a/internal/mode/static/nginx/config/validation/http_filters.go +++ b/internal/mode/static/nginx/config/validation/http_filters.go @@ -72,7 +72,7 @@ func (HTTPURLRewriteValidator) ValidateRewritePath(path string) error { return nil } -func (HTTPHeaderValidator) ValidateRequestHeaderName(name string) error { +func (HTTPHeaderValidator) ValidateFilterHeaderName(name string) error { return validateHeaderName(name) } From 9fcdbd3b771d26564709316a266f96426e5387b1 Mon Sep 17 00:00:00 2001 From: Saloni Date: Tue, 7 May 2024 16:28:18 -0600 Subject: [PATCH 64/72] address comments --- .../config/validation/http_filters_test.go | 6 +- internal/mode/static/state/graph/httproute.go | 6 +- .../mode/static/state/graph/httproute_test.go | 12 +- .../fake_httpfields_validator.go | 148 +++++++++--------- .../mode/static/state/validation/validator.go | 2 +- .../traffic-management/response-headers.md | 78 +++++---- 6 files changed, 137 insertions(+), 115 deletions(-) diff --git a/internal/mode/static/nginx/config/validation/http_filters_test.go b/internal/mode/static/nginx/config/validation/http_filters_test.go index 8d5a6cdd63..d58716599c 100644 --- a/internal/mode/static/nginx/config/validation/http_filters_test.go +++ b/internal/mode/static/nginx/config/validation/http_filters_test.go @@ -88,17 +88,17 @@ func TestValidateRewritePath(t *testing.T) { ) } -func TestValidateRequestHeaderName(t *testing.T) { +func TestValidateFilterHeaderName(t *testing.T) { validator := HTTPHeaderValidator{} testValidValuesForSimpleValidator( t, - validator.ValidateRequestHeaderName, + validator.ValidateFilterHeaderName, "Content-Encoding", "MyBespokeHeader", ) - testInvalidValuesForSimpleValidator(t, validator.ValidateRequestHeaderName, "$Content-Encoding") + testInvalidValuesForSimpleValidator(t, validator.ValidateFilterHeaderName, "$Content-Encoding") } func TestValidateFilterHeaderValue(t *testing.T) { diff --git a/internal/mode/static/state/graph/httproute.go b/internal/mode/static/state/graph/httproute.go index bf7923f5fa..bd5aaefdce 100644 --- a/internal/mode/static/state/graph/httproute.go +++ b/internal/mode/static/state/graph/httproute.go @@ -401,7 +401,7 @@ func validateFilterHeaderModifierFields( ) for _, h := range headerModifier.Add { - if err := validator.ValidateRequestHeaderName(string(h.Name)); err != nil { + if err := validator.ValidateFilterHeaderName(string(h.Name)); err != nil { valErr := field.Invalid(headerModifierPath.Child(add), h, err.Error()) allErrs = append(allErrs, valErr) } @@ -411,7 +411,7 @@ func validateFilterHeaderModifierFields( } } for _, h := range headerModifier.Set { - if err := validator.ValidateRequestHeaderName(string(h.Name)); err != nil { + if err := validator.ValidateFilterHeaderName(string(h.Name)); err != nil { valErr := field.Invalid(headerModifierPath.Child(set), h, err.Error()) allErrs = append(allErrs, valErr) } @@ -421,7 +421,7 @@ func validateFilterHeaderModifierFields( } } for _, h := range headerModifier.Remove { - if err := validator.ValidateRequestHeaderName(h); err != nil { + if err := validator.ValidateFilterHeaderName(h); err != nil { valErr := field.Invalid(headerModifierPath.Child(remove), h, err.Error()) allErrs = append(allErrs, valErr) } diff --git a/internal/mode/static/state/graph/httproute_test.go b/internal/mode/static/state/graph/httproute_test.go index 8db744f0b7..ede2abcbd7 100644 --- a/internal/mode/static/state/graph/httproute_test.go +++ b/internal/mode/static/state/graph/httproute_test.go @@ -1231,7 +1231,7 @@ func TestValidateFilterRequestHeaderModifier(t *testing.T) { { validator: func() *validationfakes.FakeHTTPFieldsValidator { v := createAllValidValidator() - v.ValidateRequestHeaderNameReturns(errors.New("Invalid header")) + v.ValidateFilterHeaderNameReturns(errors.New("Invalid header")) return v }(), filter: gatewayv1.HTTPRouteFilter{ @@ -1248,7 +1248,7 @@ func TestValidateFilterRequestHeaderModifier(t *testing.T) { { validator: func() *validationfakes.FakeHTTPFieldsValidator { v := createAllValidValidator() - v.ValidateRequestHeaderNameReturns(errors.New("Invalid header")) + v.ValidateFilterHeaderNameReturns(errors.New("Invalid header")) return v }(), filter: gatewayv1.HTTPRouteFilter{ @@ -1281,7 +1281,7 @@ func TestValidateFilterRequestHeaderModifier(t *testing.T) { validator: func() *validationfakes.FakeHTTPFieldsValidator { v := createAllValidValidator() v.ValidateFilterHeaderValueReturns(errors.New("Invalid header value")) - v.ValidateRequestHeaderNameReturns(errors.New("Invalid header")) + v.ValidateFilterHeaderNameReturns(errors.New("Invalid header")) return v }(), filter: gatewayv1.HTTPRouteFilter{ @@ -1375,7 +1375,7 @@ func TestValidateFilterResponseHeaderModifier(t *testing.T) { { validator: func() *validationfakes.FakeHTTPFieldsValidator { v := createAllValidValidator() - v.ValidateRequestHeaderNameReturns(errors.New("Invalid header")) + v.ValidateFilterHeaderNameReturns(errors.New("Invalid header")) return v }(), filter: gatewayv1.HTTPRouteFilter{ @@ -1392,7 +1392,7 @@ func TestValidateFilterResponseHeaderModifier(t *testing.T) { { validator: func() *validationfakes.FakeHTTPFieldsValidator { v := createAllValidValidator() - v.ValidateRequestHeaderNameReturns(errors.New("Invalid header")) + v.ValidateFilterHeaderNameReturns(errors.New("Invalid header")) return v }(), filter: gatewayv1.HTTPRouteFilter{ @@ -1425,7 +1425,7 @@ func TestValidateFilterResponseHeaderModifier(t *testing.T) { validator: func() *validationfakes.FakeHTTPFieldsValidator { v := createAllValidValidator() v.ValidateFilterHeaderValueReturns(errors.New("Invalid header value")) - v.ValidateRequestHeaderNameReturns(errors.New("Invalid header")) + v.ValidateFilterHeaderNameReturns(errors.New("Invalid header")) return v }(), filter: gatewayv1.HTTPRouteFilter{ diff --git a/internal/mode/static/state/validation/validationfakes/fake_httpfields_validator.go b/internal/mode/static/state/validation/validationfakes/fake_httpfields_validator.go index a8af77b4a5..64d9b09349 100644 --- a/internal/mode/static/state/validation/validationfakes/fake_httpfields_validator.go +++ b/internal/mode/static/state/validation/validationfakes/fake_httpfields_validator.go @@ -8,6 +8,17 @@ import ( ) type FakeHTTPFieldsValidator struct { + ValidateFilterHeaderNameStub func(string) error + validateFilterHeaderNameMutex sync.RWMutex + validateFilterHeaderNameArgsForCall []struct { + arg1 string + } + validateFilterHeaderNameReturns struct { + result1 error + } + validateFilterHeaderNameReturnsOnCall map[int]struct { + result1 error + } ValidateFilterHeaderValueStub func(string) error validateFilterHeaderValueMutex sync.RWMutex validateFilterHeaderValueArgsForCall []struct { @@ -135,17 +146,6 @@ type FakeHTTPFieldsValidator struct { result1 bool result2 []string } - ValidateRequestHeaderNameStub func(string) error - validateRequestHeaderNameMutex sync.RWMutex - validateRequestHeaderNameArgsForCall []struct { - arg1 string - } - validateRequestHeaderNameReturns struct { - result1 error - } - validateRequestHeaderNameReturnsOnCall map[int]struct { - result1 error - } ValidateRewritePathStub func(string) error validateRewritePathMutex sync.RWMutex validateRewritePathArgsForCall []struct { @@ -161,6 +161,67 @@ type FakeHTTPFieldsValidator struct { invocationsMutex sync.RWMutex } +func (fake *FakeHTTPFieldsValidator) ValidateFilterHeaderName(arg1 string) error { + fake.validateFilterHeaderNameMutex.Lock() + ret, specificReturn := fake.validateFilterHeaderNameReturnsOnCall[len(fake.validateFilterHeaderNameArgsForCall)] + fake.validateFilterHeaderNameArgsForCall = append(fake.validateFilterHeaderNameArgsForCall, struct { + arg1 string + }{arg1}) + stub := fake.ValidateFilterHeaderNameStub + fakeReturns := fake.validateFilterHeaderNameReturns + fake.recordInvocation("ValidateFilterHeaderName", []interface{}{arg1}) + fake.validateFilterHeaderNameMutex.Unlock() + if stub != nil { + return stub(arg1) + } + if specificReturn { + return ret.result1 + } + return fakeReturns.result1 +} + +func (fake *FakeHTTPFieldsValidator) ValidateFilterHeaderNameCallCount() int { + fake.validateFilterHeaderNameMutex.RLock() + defer fake.validateFilterHeaderNameMutex.RUnlock() + return len(fake.validateFilterHeaderNameArgsForCall) +} + +func (fake *FakeHTTPFieldsValidator) ValidateFilterHeaderNameCalls(stub func(string) error) { + fake.validateFilterHeaderNameMutex.Lock() + defer fake.validateFilterHeaderNameMutex.Unlock() + fake.ValidateFilterHeaderNameStub = stub +} + +func (fake *FakeHTTPFieldsValidator) ValidateFilterHeaderNameArgsForCall(i int) string { + fake.validateFilterHeaderNameMutex.RLock() + defer fake.validateFilterHeaderNameMutex.RUnlock() + argsForCall := fake.validateFilterHeaderNameArgsForCall[i] + return argsForCall.arg1 +} + +func (fake *FakeHTTPFieldsValidator) ValidateFilterHeaderNameReturns(result1 error) { + fake.validateFilterHeaderNameMutex.Lock() + defer fake.validateFilterHeaderNameMutex.Unlock() + fake.ValidateFilterHeaderNameStub = nil + fake.validateFilterHeaderNameReturns = struct { + result1 error + }{result1} +} + +func (fake *FakeHTTPFieldsValidator) ValidateFilterHeaderNameReturnsOnCall(i int, result1 error) { + fake.validateFilterHeaderNameMutex.Lock() + defer fake.validateFilterHeaderNameMutex.Unlock() + fake.ValidateFilterHeaderNameStub = nil + if fake.validateFilterHeaderNameReturnsOnCall == nil { + fake.validateFilterHeaderNameReturnsOnCall = make(map[int]struct { + result1 error + }) + } + fake.validateFilterHeaderNameReturnsOnCall[i] = struct { + result1 error + }{result1} +} + func (fake *FakeHTTPFieldsValidator) ValidateFilterHeaderValue(arg1 string) error { fake.validateFilterHeaderValueMutex.Lock() ret, specificReturn := fake.validateFilterHeaderValueReturnsOnCall[len(fake.validateFilterHeaderValueArgsForCall)] @@ -841,67 +902,6 @@ func (fake *FakeHTTPFieldsValidator) ValidateRedirectStatusCodeReturnsOnCall(i i }{result1, result2} } -func (fake *FakeHTTPFieldsValidator) ValidateRequestHeaderName(arg1 string) error { - fake.validateRequestHeaderNameMutex.Lock() - ret, specificReturn := fake.validateRequestHeaderNameReturnsOnCall[len(fake.validateRequestHeaderNameArgsForCall)] - fake.validateRequestHeaderNameArgsForCall = append(fake.validateRequestHeaderNameArgsForCall, struct { - arg1 string - }{arg1}) - stub := fake.ValidateRequestHeaderNameStub - fakeReturns := fake.validateRequestHeaderNameReturns - fake.recordInvocation("ValidateRequestHeaderName", []interface{}{arg1}) - fake.validateRequestHeaderNameMutex.Unlock() - if stub != nil { - return stub(arg1) - } - if specificReturn { - return ret.result1 - } - return fakeReturns.result1 -} - -func (fake *FakeHTTPFieldsValidator) ValidateRequestHeaderNameCallCount() int { - fake.validateRequestHeaderNameMutex.RLock() - defer fake.validateRequestHeaderNameMutex.RUnlock() - return len(fake.validateRequestHeaderNameArgsForCall) -} - -func (fake *FakeHTTPFieldsValidator) ValidateRequestHeaderNameCalls(stub func(string) error) { - fake.validateRequestHeaderNameMutex.Lock() - defer fake.validateRequestHeaderNameMutex.Unlock() - fake.ValidateRequestHeaderNameStub = stub -} - -func (fake *FakeHTTPFieldsValidator) ValidateRequestHeaderNameArgsForCall(i int) string { - fake.validateRequestHeaderNameMutex.RLock() - defer fake.validateRequestHeaderNameMutex.RUnlock() - argsForCall := fake.validateRequestHeaderNameArgsForCall[i] - return argsForCall.arg1 -} - -func (fake *FakeHTTPFieldsValidator) ValidateRequestHeaderNameReturns(result1 error) { - fake.validateRequestHeaderNameMutex.Lock() - defer fake.validateRequestHeaderNameMutex.Unlock() - fake.ValidateRequestHeaderNameStub = nil - fake.validateRequestHeaderNameReturns = struct { - result1 error - }{result1} -} - -func (fake *FakeHTTPFieldsValidator) ValidateRequestHeaderNameReturnsOnCall(i int, result1 error) { - fake.validateRequestHeaderNameMutex.Lock() - defer fake.validateRequestHeaderNameMutex.Unlock() - fake.ValidateRequestHeaderNameStub = nil - if fake.validateRequestHeaderNameReturnsOnCall == nil { - fake.validateRequestHeaderNameReturnsOnCall = make(map[int]struct { - result1 error - }) - } - fake.validateRequestHeaderNameReturnsOnCall[i] = struct { - result1 error - }{result1} -} - func (fake *FakeHTTPFieldsValidator) ValidateRewritePath(arg1 string) error { fake.validateRewritePathMutex.Lock() ret, specificReturn := fake.validateRewritePathReturnsOnCall[len(fake.validateRewritePathArgsForCall)] @@ -966,6 +966,8 @@ func (fake *FakeHTTPFieldsValidator) ValidateRewritePathReturnsOnCall(i int, res func (fake *FakeHTTPFieldsValidator) Invocations() map[string][][]interface{} { fake.invocationsMutex.RLock() defer fake.invocationsMutex.RUnlock() + fake.validateFilterHeaderNameMutex.RLock() + defer fake.validateFilterHeaderNameMutex.RUnlock() fake.validateFilterHeaderValueMutex.RLock() defer fake.validateFilterHeaderValueMutex.RUnlock() fake.validateHeaderNameInMatchMutex.RLock() @@ -988,8 +990,6 @@ func (fake *FakeHTTPFieldsValidator) Invocations() map[string][][]interface{} { defer fake.validateRedirectSchemeMutex.RUnlock() fake.validateRedirectStatusCodeMutex.RLock() defer fake.validateRedirectStatusCodeMutex.RUnlock() - fake.validateRequestHeaderNameMutex.RLock() - defer fake.validateRequestHeaderNameMutex.RUnlock() fake.validateRewritePathMutex.RLock() defer fake.validateRewritePathMutex.RUnlock() copiedInvocations := map[string][][]interface{}{} diff --git a/internal/mode/static/state/validation/validator.go b/internal/mode/static/state/validation/validator.go index 222b2483a1..3bc5847004 100644 --- a/internal/mode/static/state/validation/validator.go +++ b/internal/mode/static/state/validation/validator.go @@ -25,7 +25,7 @@ type HTTPFieldsValidator interface { ValidateRedirectStatusCode(statusCode int) (valid bool, supportedValues []string) ValidateHostname(hostname string) error ValidateRewritePath(path string) error - ValidateRequestHeaderName(name string) error + ValidateFilterHeaderName(name string) error ValidateFilterHeaderValue(value string) error } diff --git a/site/content/how-to/traffic-management/response-headers.md b/site/content/how-to/traffic-management/response-headers.md index 2b70b84cdb..6bc2b1077c 100644 --- a/site/content/how-to/traffic-management/response-headers.md +++ b/site/content/how-to/traffic-management/response-headers.md @@ -5,10 +5,7 @@ weight: 700 toc: true --- -[HTTPRoute](https://gateway-api.sigs.k8s.io/api-types/httproute/) filters can modify the headers during the request-response lifecycle. [HTTP Header Modifiers](https://gateway-api.sigs.k8s.io/guides/http-header-modifier/?h=request#http-header-modifiers) can be used to add, modify or remove headers in incoming requests. - -1. The [ResponseHeaderModifier](https://gateway-api.sigs.k8s.io/guides/http-header-modifier/#http-response-header-modifier) is used to alter headers in a response to the client. - +[HTTP Header Modifiers](https://gateway-api.sigs.k8s.io/guides/http-header-modifier/?h=request#http-header-modifiers) can be used to add, modify or remove headers during the request-response lifecycle. The [ResponseHeaderModifier](https://gateway-api.sigs.k8s.io/guides/http-header-modifier/#http-response-header-modifier) is used to alter headers in a response to the client. In this guide we will modify the headers for HTTP responses when client requests are made. For an introduction to exposing your application, we recommend that you follow the [basic guide]({{< relref "/how-to/traffic-management/routing-traffic-to-your-app.md" >}}) first. @@ -29,29 +26,45 @@ In this guide we will modify the headers for HTTP responses when client requests ## Response Header Filter +In this guide, we'll begin by configuring an app with custom headers and a straightforward httproute. We'll then observe the server response in relation to header responses. Next, we'll delve into modifying some of those headers using an httpRoute with filters to modify *response* headers. Our aim will be to verify whether the server responds with the modified headers. + ### Deploy the Headers application -Begin by deploying the example application `headers`: +Begin by deploying the example application `headers`. It is a simple application that adds response headers which we'll later tweak and customize. - ```shell - kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric/v1.3.0/examples/http-response-header-filter/headers.yaml - ``` +```yaml +apiVersion: v1 +kind: Service +metadata: + name: headers +spec: + ports: + - port: 80 + targetPort: 8080 + protocol: TCP + name: http + selector: + app: headers +``` + +```shell +kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric/v1.3.0/examples/http-response-header-filter/headers.yaml +``` Verify if the Pod is running in the `default` Namespace: - ```shell - kubectl -n default get pods - ``` +```shell +kubectl -n default get pods +``` - ```text - NAME READY STATUS RESTARTS AGE - headers-6f854c478-k9z2f 1/1 Running 0 32m - ``` +```text +NAME READY STATUS RESTARTS AGE +headers-6f854c478-k9z2f 1/1 Running 0 32m +``` ### Deploy the Gateway API Resources for the Header Application - -The [gateway](https://gateway-api.sigs.k8s.io/api-types/gateway/) resource is typically deployed by the [cluster operator](https://gateway-api.sigs.k8s.io/concepts/roles-and-personas/#roles-and-personas_1). To deploy the gateway: +The [gateway](https://gateway-api.sigs.k8s.io/api-types/gateway/) resource is typically deployed by the [cluster operator](https://gateway-api.sigs.k8s.io/concepts/roles-and-personas/#roles-and-personas_1). This gateway defines a single listener on port 80. Since no hostname is specified, this listener matches on all hostnames. To deploy the gateway: ```yaml kubectl apply -f - < Date: Wed, 8 May 2024 12:27:37 -0600 Subject: [PATCH 65/72] update tutorial --- .../traffic-management/response-headers.md | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/site/content/how-to/traffic-management/response-headers.md b/site/content/how-to/traffic-management/response-headers.md index 6bc2b1077c..fdbaa32074 100644 --- a/site/content/how-to/traffic-management/response-headers.md +++ b/site/content/how-to/traffic-management/response-headers.md @@ -32,26 +32,11 @@ In this guide, we'll begin by configuring an app with custom headers and a strai Begin by deploying the example application `headers`. It is a simple application that adds response headers which we'll later tweak and customize. -```yaml -apiVersion: v1 -kind: Service -metadata: - name: headers -spec: - ports: - - port: 80 - targetPort: 8080 - protocol: TCP - name: http - selector: - app: headers -``` - ```shell kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric/v1.3.0/examples/http-response-header-filter/headers.yaml ``` -Verify if the Pod is running in the `default` Namespace: +This will create the headers service and a deployment with one pod. Run the following command to verify the resources were created: ```shell kubectl -n default get pods From 5b6df53d4bf849406b227d4da5ab98b12b85ac0b Mon Sep 17 00:00:00 2001 From: salonichf5 <146118978+salonichf5@users.noreply.github.com> Date: Wed, 8 May 2024 12:27:59 -0600 Subject: [PATCH 66/72] Update site/content/how-to/traffic-management/response-headers.md Co-authored-by: Kate Osborn <50597707+kate-osborn@users.noreply.github.com> --- site/content/how-to/traffic-management/response-headers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/how-to/traffic-management/response-headers.md b/site/content/how-to/traffic-management/response-headers.md index fdbaa32074..453caff5b5 100644 --- a/site/content/how-to/traffic-management/response-headers.md +++ b/site/content/how-to/traffic-management/response-headers.md @@ -68,7 +68,7 @@ EOF ### Configure the basic HTTPRoute -Next, let's create a simple HTTPRoute that exposes the header application outside the cluster using the listener created in the previous section.To do this, create the following HTTPRoute: +Next, let's create a simple HTTPRoute that exposes the header application outside the cluster using the listener created in the previous section. To do this, create the following HTTPRoute: ```yaml kubectl apply -f - < Date: Wed, 8 May 2024 12:50:06 -0600 Subject: [PATCH 67/72] capitalize resource name --- .../content/how-to/traffic-management/response-headers.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/site/content/how-to/traffic-management/response-headers.md b/site/content/how-to/traffic-management/response-headers.md index 453caff5b5..2f854e8598 100644 --- a/site/content/how-to/traffic-management/response-headers.md +++ b/site/content/how-to/traffic-management/response-headers.md @@ -26,7 +26,7 @@ In this guide we will modify the headers for HTTP responses when client requests ## Response Header Filter -In this guide, we'll begin by configuring an app with custom headers and a straightforward httproute. We'll then observe the server response in relation to header responses. Next, we'll delve into modifying some of those headers using an httpRoute with filters to modify *response* headers. Our aim will be to verify whether the server responds with the modified headers. +In this guide, we'll begin by configuring an app with custom headers and a straightforward HTTPRoute. We'll then observe the server response in relation to header responses. Next, we'll delve into modifying some of those headers using an HTTPRoute with filters to modify *response* headers. Our aim will be to verify whether the server responds with the modified headers. ### Deploy the Headers application @@ -36,7 +36,7 @@ Begin by deploying the example application `headers`. It is a simple application kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric/v1.3.0/examples/http-response-header-filter/headers.yaml ``` -This will create the headers service and a deployment with one pod. Run the following command to verify the resources were created: +This will create the headers Service and a Deployment with one pod. Run the following command to verify the resources were created: ```shell kubectl -n default get pods @@ -49,7 +49,7 @@ headers-6f854c478-k9z2f 1/1 Running 0 32m ### Deploy the Gateway API Resources for the Header Application -The [gateway](https://gateway-api.sigs.k8s.io/api-types/gateway/) resource is typically deployed by the [cluster operator](https://gateway-api.sigs.k8s.io/concepts/roles-and-personas/#roles-and-personas_1). This gateway defines a single listener on port 80. Since no hostname is specified, this listener matches on all hostnames. To deploy the gateway: +The [Gateway](https://gateway-api.sigs.k8s.io/api-types/gateway/) resource is typically deployed by the [Cluster Operator](https://gateway-api.sigs.k8s.io/concepts/roles-and-personas/#roles-and-personas_1). This Gateway defines a single listener on port 80. Since no hostname is specified, this listener matches on all hostnames. To deploy the gateway: ```yaml kubectl apply -f - < Date: Wed, 8 May 2024 12:54:52 -0600 Subject: [PATCH 68/72] capitalization of resource name --- site/content/how-to/traffic-management/response-headers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/how-to/traffic-management/response-headers.md b/site/content/how-to/traffic-management/response-headers.md index 2f854e8598..737bf2c47c 100644 --- a/site/content/how-to/traffic-management/response-headers.md +++ b/site/content/how-to/traffic-management/response-headers.md @@ -49,7 +49,7 @@ headers-6f854c478-k9z2f 1/1 Running 0 32m ### Deploy the Gateway API Resources for the Header Application -The [Gateway](https://gateway-api.sigs.k8s.io/api-types/gateway/) resource is typically deployed by the [Cluster Operator](https://gateway-api.sigs.k8s.io/concepts/roles-and-personas/#roles-and-personas_1). This Gateway defines a single listener on port 80. Since no hostname is specified, this listener matches on all hostnames. To deploy the gateway: +The [Gateway](https://gateway-api.sigs.k8s.io/api-types/gateway/) resource is typically deployed by the [Cluster Operator](https://gateway-api.sigs.k8s.io/concepts/roles-and-personas/#roles-and-personas_1). This Gateway defines a single listener on port 80. Since no hostname is specified, this listener matches on all hostnames. To deploy the Gateway: ```yaml kubectl apply -f - < Date: Thu, 9 May 2024 10:14:48 -0600 Subject: [PATCH 69/72] Update site/content/how-to/traffic-management/response-headers.md Co-authored-by: Saylor Berman --- site/content/how-to/traffic-management/response-headers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/how-to/traffic-management/response-headers.md b/site/content/how-to/traffic-management/response-headers.md index 737bf2c47c..917139673c 100644 --- a/site/content/how-to/traffic-management/response-headers.md +++ b/site/content/how-to/traffic-management/response-headers.md @@ -26,7 +26,7 @@ In this guide we will modify the headers for HTTP responses when client requests ## Response Header Filter -In this guide, we'll begin by configuring an app with custom headers and a straightforward HTTPRoute. We'll then observe the server response in relation to header responses. Next, we'll delve into modifying some of those headers using an HTTPRoute with filters to modify *response* headers. Our aim will be to verify whether the server responds with the modified headers. +We'll begin by configuring an app with custom headers and a straightforward HTTPRoute. We'll then observe the server response in relation to header responses. Next, we'll delve into modifying some of those headers using an HTTPRoute with filters to modify *response* headers. Our aim will be to verify whether the server responds with the modified headers. ### Deploy the Headers application From 2fc154f0188c76196b7532b9d3bea4834c0bf480 Mon Sep 17 00:00:00 2001 From: Saloni Date: Thu, 9 May 2024 10:22:22 -0600 Subject: [PATCH 70/72] arrange headers for tutorial --- .../how-to/traffic-management/response-headers.md | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/site/content/how-to/traffic-management/response-headers.md b/site/content/how-to/traffic-management/response-headers.md index 917139673c..d748f96696 100644 --- a/site/content/how-to/traffic-management/response-headers.md +++ b/site/content/how-to/traffic-management/response-headers.md @@ -9,6 +9,8 @@ toc: true In this guide we will modify the headers for HTTP responses when client requests are made. For an introduction to exposing your application, we recommend that you follow the [basic guide]({{< relref "/how-to/traffic-management/routing-traffic-to-your-app.md" >}}) first. +We'll begin by configuring an app with custom headers and a straightforward HTTPRoute. We'll then observe the server response in relation to header responses. Next, we'll delve into modifying some of those headers using an HTTPRoute with filters to modify *response* headers. Our aim will be to verify whether the server responds with the modified headers. + ## Prerequisites @@ -23,11 +25,6 @@ In this guide we will modify the headers for HTTP responses when client requests {{< note >}}In a production environment, you should have a DNS record for the external IP address that is exposed, and it should refer to the hostname that the gateway will forward for.{{< /note >}} - -## Response Header Filter - -We'll begin by configuring an app with custom headers and a straightforward HTTPRoute. We'll then observe the server response in relation to header responses. Next, we'll delve into modifying some of those headers using an HTTPRoute with filters to modify *response* headers. Our aim will be to verify whether the server responds with the modified headers. - ### Deploy the Headers application Begin by deploying the example application `headers`. It is a simple application that adds response headers which we'll later tweak and customize. @@ -36,7 +33,7 @@ Begin by deploying the example application `headers`. It is a simple application kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric/v1.3.0/examples/http-response-header-filter/headers.yaml ``` -This will create the headers Service and a Deployment with one pod. Run the following command to verify the resources were created: +This will create the headers Service and a Deployment with one Pod. Run the following command to verify the resources were created: ```shell kubectl -n default get pods From 268acb8d7bbfed60812b390e356f678cce04ba3d Mon Sep 17 00:00:00 2001 From: salonichf5 <146118978+salonichf5@users.noreply.github.com> Date: Tue, 14 May 2024 09:56:41 -0600 Subject: [PATCH 71/72] Update site/content/how-to/traffic-management/response-headers.md Co-authored-by: Kate Osborn <50597707+kate-osborn@users.noreply.github.com> --- site/content/how-to/traffic-management/response-headers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/how-to/traffic-management/response-headers.md b/site/content/how-to/traffic-management/response-headers.md index d748f96696..71b2299f46 100644 --- a/site/content/how-to/traffic-management/response-headers.md +++ b/site/content/how-to/traffic-management/response-headers.md @@ -36,7 +36,7 @@ kubectl apply -f https://raw.githubusercontent.com/nginxinc/nginx-gateway-fabric This will create the headers Service and a Deployment with one Pod. Run the following command to verify the resources were created: ```shell -kubectl -n default get pods +kubectl get pods,svc ``` ```text From 0cbfa7b351f3423002de519e108fa91cff270777 Mon Sep 17 00:00:00 2001 From: Saloni Date: Tue, 14 May 2024 09:59:57 -0600 Subject: [PATCH 72/72] address nit comments --- .../how-to/traffic-management/response-headers.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/site/content/how-to/traffic-management/response-headers.md b/site/content/how-to/traffic-management/response-headers.md index 71b2299f46..03d6f60da0 100644 --- a/site/content/how-to/traffic-management/response-headers.md +++ b/site/content/how-to/traffic-management/response-headers.md @@ -40,8 +40,11 @@ kubectl get pods,svc ``` ```text -NAME READY STATUS RESTARTS AGE -headers-6f854c478-k9z2f 1/1 Running 0 32m +NAME READY STATUS RESTARTS AGE +pod/headers-6f854c478-hd2jr 1/1 Running 0 95s + +NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE +service/headers ClusterIP 10.96.15.12 80/TCP 95s ``` ### Deploy the Gateway API Resources for the Header Application @@ -198,7 +201,7 @@ ok In the output above, you can see that the headers application modifies the following custom headers: -In the output above you can notice the modified response headers as the `X-Header-Remove` header is absent. The header `X-Header-Add` gets appended with the new value and `X-Header-Set` gets overwritten to `overwritten-value` as defined in the *HttpRoute*. +In the output above you can notice the modified response headers as the `X-Header-Unmodified` remains unchanged as we did not include it in the filter and `X-Header-Remove` header is absent. The header `X-Header-Add` gets appended with the new value and `X-Header-Set` gets overwritten to `overwritten-value` as defined in the *HttpRoute*. ## Further Reading