Skip to content

Commit fa67f00

Browse files
committed
Add websocket support (nginx#962)
* Add websocket support * Ignore resource not found error in conformance cleanup make command * Review feedback
1 parent 9e69505 commit fa67f00

File tree

11 files changed

+66
-9
lines changed

11 files changed

+66
-9
lines changed

conformance/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ uninstall-nkg: uninstall-k8s-components undo-manifests-update ## Uninstall NKG o
8989

9090
.PHONY: uninstall-k8s-components
9191
uninstall-k8s-components: ## Uninstall installed components on configured kind cluster
92-
kubectl delete -f $(NKG_MANIFEST)
92+
-kubectl delete -f $(NKG_MANIFEST)
9393
./scripts/uninstall-gateway.sh $(GW_API_VERSION)
9494
kubectl delete clusterrole nginx-gateway-provisioner
9595
kubectl delete clusterrolebinding nginx-gateway-provisioner

internal/mode/static/nginx/config/maps_template.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,11 @@ map $http_host $gw_api_compliant_host {
1717
'' $host;
1818
default $http_host;
1919
}
20+
21+
# Set $connection_header variable to upgrade when the $http_upgrade header is set, otherwise, set it to close. This
22+
# allows support for websocket connections. See https://nginx.org/en/docs/http/websocket.html.
23+
map $http_upgrade $connection_upgrade {
24+
default upgrade;
25+
'' close;
26+
}
2027
`

internal/mode/static/nginx/config/maps_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ func TestExecuteMaps(t *testing.T) {
8585
"~.* ${http_my_second_add_header},;": 1,
8686
"map ${http_my_set_header} $my_set_header_header_var {": 0,
8787
"map $http_host $gw_api_compliant_host {": 1,
88+
"map $http_upgrade $connection_upgrade {": 1,
8889
}
8990

9091
maps := string(executeMaps(conf))

internal/mode/static/nginx/config/servers_template.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ server {
5151
proxy_set_header {{ $h.Name }} "{{ $h.Value }}";
5252
{{- end }}
5353
proxy_set_header Host $gw_api_compliant_host;
54+
proxy_http_version 1.1;
55+
proxy_set_header Upgrade $http_upgrade;
56+
proxy_set_header Connection $connection_upgrade;
5457
proxy_pass {{ $l.ProxyPass }}$request_uri;
5558
{{- end }}
5659
}

internal/mode/static/nginx/config/validation/common.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,19 +52,25 @@ func validateEscapedStringNoVarExpansion(value string, examples []string) error
5252
}
5353

5454
const (
55-
invalidHostHeaderErrMsg string = "redefining the Host request header is not supported"
56-
maxHeaderLength int = 256
55+
invalidHeadersErrMsg string = "unsupported header name configured, unsupported names are: "
56+
maxHeaderLength int = 256
5757
)
5858

59+
var invalidHeaders = map[string]struct{}{
60+
"host": {},
61+
"connection": {},
62+
"upgrade": {},
63+
}
64+
5965
func validateHeaderName(name string) error {
6066
if len(name) > maxHeaderLength {
6167
return errors.New(k8svalidation.MaxLenError(maxHeaderLength))
6268
}
6369
if msg := k8svalidation.IsHTTPHeaderName(name); msg != nil {
6470
return errors.New(msg[0])
6571
}
66-
if strings.ToLower(name) == "host" {
67-
return errors.New(invalidHostHeaderErrMsg)
72+
if valid, invalidHeadersAsStrings := validateNoUnsupportedValues(strings.ToLower(name), invalidHeaders); !valid {
73+
return errors.New(invalidHeadersErrMsg + strings.Join(invalidHeadersAsStrings, ", "))
6874
}
6975
return nil
7076
}

internal/mode/static/nginx/config/validation/common_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ func TestValidateValidHeaderName(t *testing.T) {
6464
`$test`,
6565
"Host",
6666
"host",
67+
"connection",
68+
"upgrade",
6769
"my-header[]",
6870
"my-header&",
6971
strings.Repeat("very-long-header", 16)+"1",

internal/mode/static/nginx/config/validation/framework.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,16 @@ func validateInSupportedValues[T configValue](
2020
return false, getSortedKeysAsString(supportedValues)
2121
}
2222

23+
func validateNoUnsupportedValues[T configValue](
24+
value T,
25+
unsupportedValues map[T]struct{},
26+
) (valid bool, unsupportedValuesAsStrings []string) {
27+
if _, exist := unsupportedValues[value]; exist {
28+
return false, getSortedKeysAsString(unsupportedValues)
29+
}
30+
return true, nil
31+
}
32+
2333
func getSortedKeysAsString[T configValue](m map[T]struct{}) []string {
2434
keysAsString := make([]string, 0, len(m))
2535

internal/mode/static/nginx/config/validation/framework_test.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,34 @@ func TestValidateInSupportedValues(t *testing.T) {
9999
)
100100
}
101101

102+
func TestValidateNoUnsupportedValues(t *testing.T) {
103+
unsupportedValues := map[string]struct{}{
104+
"badvalue1": {},
105+
"badvalue2": {},
106+
"badvalue3": {},
107+
}
108+
109+
validator := func(value string) (bool, []string) {
110+
return validateNoUnsupportedValues(value, unsupportedValues)
111+
}
112+
113+
testValidValuesForSupportedValuesValidator(
114+
t,
115+
validator,
116+
"value1",
117+
"value2",
118+
"value3",
119+
)
120+
testInvalidValuesForSupportedValuesValidator(
121+
t,
122+
validator,
123+
unsupportedValues,
124+
"badvalue1",
125+
"badvalue2",
126+
"badvalue3",
127+
)
128+
}
129+
102130
func TestGetSortedKeysAsString(t *testing.T) {
103131
values := map[string]struct{}{
104132
"value3": {},

internal/mode/static/nginx/config/validation/http_filters_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ func TestValidateRequestHeaderName(t *testing.T) {
7474
t,
7575
validator.ValidateRequestHeaderName,
7676
"Content-Encoding",
77-
"Connection",
77+
"MyBespokeHeader",
7878
)
7979

8080
testInvalidValuesForSimpleValidator(t, validator.ValidateRequestHeaderName, "$Content-Encoding")

internal/mode/static/state/dataplane/configuration_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1609,8 +1609,8 @@ func TestCreateFilters(t *testing.T) {
16091609
RequestHeaderModifier: &v1beta1.HTTPHeaderFilter{
16101610
Set: []v1beta1.HTTPHeader{
16111611
{
1612-
Name: "Connection",
1613-
Value: "close",
1612+
Name: "MyBespokeHeader",
1613+
Value: "my-value",
16141614
},
16151615
},
16161616
},

internal/mode/static/state/graph/httproute_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1836,7 +1836,7 @@ func TestValidateFilterRequestHeaderModifier(t *testing.T) {
18361836
Type: v1beta1.HTTPRouteFilterRequestHeaderModifier,
18371837
RequestHeaderModifier: &v1beta1.HTTPHeaderFilter{
18381838
Set: []v1beta1.HTTPHeader{
1839-
{Name: "Connection", Value: "close"},
1839+
{Name: "MyBespokeHeader", Value: "my-value"},
18401840
},
18411841
Add: []v1beta1.HTTPHeader{
18421842
{Name: "Accept-Encoding", Value: "gzip"},

0 commit comments

Comments
 (0)