From 378a1f0d162a6e83093fb0751504cffda02ec155 Mon Sep 17 00:00:00 2001 From: Huabing Zhao Date: Mon, 22 Apr 2024 12:24:16 -0700 Subject: [PATCH] HTTP2 settings API (#3222) * HTTP2 settings API Signed-off-by: huabing zhao * api for http2 settings Signed-off-by: huabing zhao * fix typo Signed-off-by: huabing zhao * fix test Signed-off-by: huabing zhao * fix Signed-off-by: huabing zhao * fix Signed-off-by: huabing zhao * fix Signed-off-by: huabing zhao * fix test Signed-off-by: huabing zhao * fix test Signed-off-by: huabing zhao * fix test Signed-off-by: huabing zhao * add validation test Signed-off-by: huabing zhao --------- Signed-off-by: huabing zhao --- api/v1alpha1/clienttrafficpolicy_types.go | 42 +++++++++++--- api/v1alpha1/zz_generated.deepcopy.go | 55 +++++++++++++++---- ...y.envoyproxy.io_clienttrafficpolicies.yaml | 36 ++++++++++++ site/content/en/latest/api/extension_types.md | 21 ++++++- .../clienttrafficpolicy_test.go | 40 ++++++++++++++ 5 files changed, 174 insertions(+), 20 deletions(-) diff --git a/api/v1alpha1/clienttrafficpolicy_types.go b/api/v1alpha1/clienttrafficpolicy_types.go index b722a33655d..c920274008b 100644 --- a/api/v1alpha1/clienttrafficpolicy_types.go +++ b/api/v1alpha1/clienttrafficpolicy_types.go @@ -6,6 +6,7 @@ package v1alpha1 import ( + "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" gwapiv1a2 "sigs.k8s.io/gateway-api/apis/v1alpha2" ) @@ -62,10 +63,6 @@ type ClientTrafficPolicySpec struct { // // +optional ClientIPDetection *ClientIPDetectionSettings `json:"clientIPDetection,omitempty"` - // HTTP3 provides HTTP/3 configuration on the listener. - // - // +optional - HTTP3 *HTTP3Settings `json:"http3,omitempty"` // TLS settings configure TLS termination settings with the downstream client. // // +optional @@ -74,10 +71,6 @@ type ClientTrafficPolicySpec struct { // // +optional Path *PathSettings `json:"path,omitempty"` - // HTTP1 provides HTTP/1 configuration on the listener. - // - // +optional - HTTP1 *HTTP1Settings `json:"http1,omitempty"` // HeaderSettings provides configuration for header management. // // +optional @@ -90,6 +83,18 @@ type ClientTrafficPolicySpec struct { // // +optional Connection *Connection `json:"connection,omitempty"` + // HTTP1 provides HTTP/1 configuration on the listener. + // + // +optional + HTTP1 *HTTP1Settings `json:"http1,omitempty"` + // HTTP2 provides HTTP/2 configuration on the listener. + // + // +optional + HTTP2 *HTTP2Settings `json:"http2,omitempty"` + // HTTP3 provides HTTP/3 configuration on the listener. + // + // +optional + HTTP3 *HTTP3Settings `json:"http3,omitempty"` } // HeaderSettings provides configuration options for headers on the listener. @@ -199,6 +204,27 @@ type HTTP10Settings struct { UseDefaultHost *bool `json:"useDefaultHost,omitempty"` } +// HTTP2Settings provides HTTP/2 configuration on the listener. +type HTTP2Settings struct { + // InitialStreamWindowSize sets the initial window size for HTTP/2 streams. + // If not set, the default value is 64 KiB(64*1024). + // + // +kubebuilder:validation:XValidation:rule="type(self) == string ? self.matches(r\"^[1-9]+[0-9]*([EPTGMK]i|[EPTGMk])?$\") : type(self) == int",message="initialStreamWindowSize must be of the format \"^[1-9]+[0-9]*([EPTGMK]i|[EPTGMk])?$\"" + // +optional + InitialStreamWindowSize *resource.Quantity `json:"initialStreamWindowSize,omitempty"` + + // InitialConnectionWindowSize sets the initial window size for HTTP/2 connections. + // If not set, the default value is 1 MiB. + // + // +kubebuilder:validation:XValidation:rule="type(self) == string ? self.matches(r\"^[1-9]+[0-9]*([EPTGMK]i|[EPTGMk])?$\") : type(self) == int",message="initialConnectionWindowSize must be of the format \"^[1-9]+[0-9]*([EPTGMK]i|[EPTGMk])?$\"" + // +optional + InitialConnectionWindowSize *resource.Quantity `json:"initialConnectionWindowSize,omitempty"` + + // MaxConcurrentStreams sets the maximum number of concurrent streams allowed per connection. + // If not set, the default value is 100. + MaxConcurrentStreams *uint32 `json:"maxConcurrentStreams,omitempty"` +} + const ( // PolicyConditionOverridden indicates whether the policy has // completely attached to all the sections within the target or not. diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 6d3564d19be..8338973cdf1 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -562,11 +562,6 @@ func (in *ClientTrafficPolicySpec) DeepCopyInto(out *ClientTrafficPolicySpec) { *out = new(ClientIPDetectionSettings) (*in).DeepCopyInto(*out) } - if in.HTTP3 != nil { - in, out := &in.HTTP3, &out.HTTP3 - *out = new(HTTP3Settings) - **out = **in - } if in.TLS != nil { in, out := &in.TLS, &out.TLS *out = new(TLSSettings) @@ -577,11 +572,6 @@ func (in *ClientTrafficPolicySpec) DeepCopyInto(out *ClientTrafficPolicySpec) { *out = new(PathSettings) (*in).DeepCopyInto(*out) } - if in.HTTP1 != nil { - in, out := &in.HTTP1, &out.HTTP1 - *out = new(HTTP1Settings) - (*in).DeepCopyInto(*out) - } if in.Headers != nil { in, out := &in.Headers, &out.Headers *out = new(HeaderSettings) @@ -597,6 +587,21 @@ func (in *ClientTrafficPolicySpec) DeepCopyInto(out *ClientTrafficPolicySpec) { *out = new(Connection) (*in).DeepCopyInto(*out) } + if in.HTTP1 != nil { + in, out := &in.HTTP1, &out.HTTP1 + *out = new(HTTP1Settings) + (*in).DeepCopyInto(*out) + } + if in.HTTP2 != nil { + in, out := &in.HTTP2, &out.HTTP2 + *out = new(HTTP2Settings) + (*in).DeepCopyInto(*out) + } + if in.HTTP3 != nil { + in, out := &in.HTTP3, &out.HTTP3 + *out = new(HTTP3Settings) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClientTrafficPolicySpec. @@ -2014,6 +2019,36 @@ func (in *HTTP1Settings) DeepCopy() *HTTP1Settings { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTP2Settings) DeepCopyInto(out *HTTP2Settings) { + *out = *in + if in.InitialStreamWindowSize != nil { + in, out := &in.InitialStreamWindowSize, &out.InitialStreamWindowSize + x := (*in).DeepCopy() + *out = &x + } + if in.InitialConnectionWindowSize != nil { + in, out := &in.InitialConnectionWindowSize, &out.InitialConnectionWindowSize + x := (*in).DeepCopy() + *out = &x + } + if in.MaxConcurrentStreams != nil { + in, out := &in.MaxConcurrentStreams, &out.MaxConcurrentStreams + *out = new(uint32) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTP2Settings. +func (in *HTTP2Settings) DeepCopy() *HTTP2Settings { + if in == nil { + return nil + } + out := new(HTTP2Settings) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *HTTP3Settings) DeepCopyInto(out *HTTP3Settings) { *out = *in diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml index 48015fd067c..b8dacf05eea 100644 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_clienttrafficpolicies.yaml @@ -183,6 +183,42 @@ spec: By default, Envoy will lowercase all the headers. type: boolean type: object + http2: + description: HTTP2 provides HTTP/2 configuration on the listener. + properties: + initialConnectionWindowSize: + anyOf: + - type: integer + - type: string + description: |- + InitialConnectionWindowSize sets the initial window size for HTTP/2 connections. + If not set, the default value is 1 MiB. + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + x-kubernetes-validations: + - message: initialConnectionWindowSize must be of the format "^[1-9]+[0-9]*([EPTGMK]i|[EPTGMk])?$" + rule: 'type(self) == string ? self.matches(r"^[1-9]+[0-9]*([EPTGMK]i|[EPTGMk])?$") + : type(self) == int' + initialStreamWindowSize: + anyOf: + - type: integer + - type: string + description: |- + InitialStreamWindowSize sets the initial window size for HTTP/2 streams. + If not set, the default value is 64 KiB(64*1024). + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + x-kubernetes-validations: + - message: initialStreamWindowSize must be of the format "^[1-9]+[0-9]*([EPTGMK]i|[EPTGMk])?$" + rule: 'type(self) == string ? self.matches(r"^[1-9]+[0-9]*([EPTGMK]i|[EPTGMk])?$") + : type(self) == int' + maxConcurrentStreams: + description: |- + MaxConcurrentStreams sets the maximum number of concurrent streams allowed per connection. + If not set, the default value is 100. + format: int32 + type: integer + type: object http3: description: HTTP3 provides HTTP/3 configuration on the listener. type: object diff --git a/site/content/en/latest/api/extension_types.md b/site/content/en/latest/api/extension_types.md index fd41e84c1bb..e5e5d9fd11b 100644 --- a/site/content/en/latest/api/extension_types.md +++ b/site/content/en/latest/api/extension_types.md @@ -422,13 +422,14 @@ _Appears in:_ | `tcpKeepalive` | _[TCPKeepalive](#tcpkeepalive)_ | false | TcpKeepalive settings associated with the downstream client connection.
If defined, sets SO_KEEPALIVE on the listener socket to enable TCP Keepalives.
Disabled by default. | | `enableProxyProtocol` | _boolean_ | false | EnableProxyProtocol interprets the ProxyProtocol header and adds the
Client Address into the X-Forwarded-For header.
Note Proxy Protocol must be present when this field is set, else the connection
is closed. | | `clientIPDetection` | _[ClientIPDetectionSettings](#clientipdetectionsettings)_ | false | ClientIPDetectionSettings provides configuration for determining the original client IP address for requests. | -| `http3` | _[HTTP3Settings](#http3settings)_ | false | HTTP3 provides HTTP/3 configuration on the listener. | | `tls` | _[TLSSettings](#tlssettings)_ | false | TLS settings configure TLS termination settings with the downstream client. | | `path` | _[PathSettings](#pathsettings)_ | false | Path enables managing how the incoming path set by clients can be normalized. | -| `http1` | _[HTTP1Settings](#http1settings)_ | false | HTTP1 provides HTTP/1 configuration on the listener. | | `headers` | _[HeaderSettings](#headersettings)_ | false | HeaderSettings provides configuration for header management. | | `timeout` | _[ClientTimeout](#clienttimeout)_ | false | Timeout settings for the client connections. | | `connection` | _[Connection](#connection)_ | false | Connection includes client connection settings. | +| `http1` | _[HTTP1Settings](#http1settings)_ | false | HTTP1 provides HTTP/1 configuration on the listener. | +| `http2` | _[HTTP2Settings](#http2settings)_ | false | HTTP2 provides HTTP/2 configuration on the listener. | +| `http3` | _[HTTP3Settings](#http3settings)_ | false | HTTP3 provides HTTP/3 configuration on the listener. | #### ClientValidationContext @@ -1483,6 +1484,22 @@ _Appears in:_ | `http10` | _[HTTP10Settings](#http10settings)_ | false | HTTP10 turns on support for HTTP/1.0 and HTTP/0.9 requests. | +#### HTTP2Settings + + + +HTTP2Settings provides HTTP/2 configuration on the listener. + +_Appears in:_ +- [ClientTrafficPolicySpec](#clienttrafficpolicyspec) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `initialStreamWindowSize` | _[Quantity](#quantity)_ | false | InitialStreamWindowSize sets the initial window size for HTTP/2 streams.
If not set, the default value is 64 KiB(64*1024). | +| `initialConnectionWindowSize` | _[Quantity](#quantity)_ | false | InitialConnectionWindowSize sets the initial window size for HTTP/2 connections.
If not set, the default value is 1 MiB. | +| `maxConcurrentStreams` | _integer_ | true | MaxConcurrentStreams sets the maximum number of concurrent streams allowed per connection.
If not set, the default value is 100. | + + #### HTTP3Settings diff --git a/test/cel-validation/clienttrafficpolicy_test.go b/test/cel-validation/clienttrafficpolicy_test.go index 5f5e56fec1c..554d6240d8e 100644 --- a/test/cel-validation/clienttrafficpolicy_test.go +++ b/test/cel-validation/clienttrafficpolicy_test.go @@ -286,6 +286,46 @@ func TestClientTrafficPolicyTarget(t *testing.T) { "spec.connection.bufferLimit: Invalid value: \"\": bufferLimit must be of the format \"^[1-9]+[0-9]*([EPTGMK]i|[EPTGMk])?$\"", }, }, + { + desc: "invalid InitialStreamWindowSize format", + mutate: func(ctp *egv1a1.ClientTrafficPolicy) { + ctp.Spec = egv1a1.ClientTrafficPolicySpec{ + TargetRef: gwapiv1a2.PolicyTargetReferenceWithSectionName{ + PolicyTargetReference: gwapiv1a2.PolicyTargetReference{ + Group: gwapiv1a2.Group("gateway.networking.k8s.io"), + Kind: gwapiv1a2.Kind("Gateway"), + Name: gwapiv1a2.ObjectName("eg"), + }, + }, + HTTP2: &egv1a1.HTTP2Settings{ + InitialStreamWindowSize: ptr.To(resource.MustParse("15m")), + }, + } + }, + wantErrors: []string{ + "spec.http2.initialStreamWindowSize: Invalid value: \"\": initialStreamWindowSize must be of the format \"^[1-9]+[0-9]*([EPTGMK]i|[EPTGMk])?$\"", + }, + }, + { + desc: "invalid InitialConnectionWindowSize format", + mutate: func(ctp *egv1a1.ClientTrafficPolicy) { + ctp.Spec = egv1a1.ClientTrafficPolicySpec{ + TargetRef: gwapiv1a2.PolicyTargetReferenceWithSectionName{ + PolicyTargetReference: gwapiv1a2.PolicyTargetReference{ + Group: gwapiv1a2.Group("gateway.networking.k8s.io"), + Kind: gwapiv1a2.Kind("Gateway"), + Name: gwapiv1a2.ObjectName("eg"), + }, + }, + HTTP2: &egv1a1.HTTP2Settings{ + InitialConnectionWindowSize: ptr.To(resource.MustParse("15m")), + }, + } + }, + wantErrors: []string{ + "spec.http2.InitialConnectionWindowSize: Invalid value: \"\": initialConnectionWindowSize must be of the format \"^[1-9]+[0-9]*([EPTGMK]i|[EPTGMk])?$\"", + }, + }, } for _, tc := range cases {