Skip to content

Commit

Permalink
implement BTP HTTP2
Browse files Browse the repository at this point in the history
Signed-off-by: Guy Daich <guy.daich@sap.com>
  • Loading branch information
guydc committed Jul 23, 2024
1 parent 9852333 commit 3fd193d
Show file tree
Hide file tree
Showing 44 changed files with 944 additions and 63 deletions.
5 changes: 5 additions & 0 deletions api/v1alpha1/backendtrafficpolicy_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,11 @@ type BackendTrafficPolicySpec struct {
//
// +optional
Connection *BackendConnection `json:"connection,omitempty"`

// HTTP2 provides HTTP/2 configuration for backend connections.
//
// +optional
HTTP2 *HTTP2Settings `json:"http2,omitempty"`
}

// +kubebuilder:object:root=true
Expand Down
25 changes: 0 additions & 25 deletions api/v1alpha1/clienttrafficpolicy_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
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"
)
Expand Down Expand Up @@ -289,30 +288,6 @@ 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.
// +kubebuilder:validation:Minimum=1
// +kubebuilder:validation:Maximum=2147483647
// +optional
MaxConcurrentStreams *uint32 `json:"maxConcurrentStreams,omitempty"`
}

// HealthCheckSettings provides HealthCheck configuration on the HTTP/HTTPS listener.
type HealthCheckSettings struct {
// Path specifies the HTTP path to match on for health check requests.
Expand Down
31 changes: 31 additions & 0 deletions api/v1alpha1/shared_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
autoscalingv2 "k8s.io/api/autoscaling/v2"
corev1 "k8s.io/api/core/v1"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apimachinery/pkg/api/resource"
gwapiv1 "sigs.k8s.io/gateway-api/apis/v1"
)

Expand Down Expand Up @@ -478,3 +479,33 @@ type BackendRef struct {
// A CIDR can be an IPv4 address range such as "192.168.1.0/24" or an IPv6 address range such as "2001:0db8:11a3:09d7::/64".
// +kubebuilder:validation:Pattern=`((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\/([0-9]+))|((([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))\/([0-9]+))`
type CIDR string

// HTTP2Settings provides HTTP/2 configuration for listeners and backends.
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.
// +kubebuilder:validation:Minimum=1
// +kubebuilder:validation:Maximum=2147483647
// +optional
MaxConcurrentStreams *uint32 `json:"maxConcurrentStreams,omitempty"`

// ResetStreamOnError determines if Envoy will terminate the stream or the connection in the event of HTTP messaging error
// It's recommended for L2 Envoy deployments to set this value to true.
// https://www.envoyproxy.io/docs/envoy/latest/configuration/best_practices/level_two
// +optional
ResetStreamOnError *bool `json:"resetStreamOnError,omitempty"`
}
10 changes: 10 additions & 0 deletions api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,50 @@ spec:
type: boolean
type: object
type: object
http2:
description: HTTP2 provides HTTP/2 configuration for backend connections.
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
maximum: 2147483647
minimum: 1
type: integer
resetStreamOnError:
description: |-
ResetStreamOnError determines if Envoy will terminate the stream or the connection in the event of HTTP messaging error
It's recommended for L2 Envoy deployments to set this value to true.
https://www.envoyproxy.io/docs/envoy/latest/configuration/best_practices/level_two
type: boolean
type: object
loadBalancer:
description: |-
LoadBalancer policy to apply when routing traffic from the gateway to
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,12 @@ spec:
maximum: 2147483647
minimum: 1
type: integer
resetStreamOnError:
description: |-
ResetStreamOnError determines if Envoy will terminate the stream or the connection in the event of HTTP messaging error
It's recommended for L2 Envoy deployments to set this value to true.
https://www.envoyproxy.io/docs/envoy/latest/configuration/best_practices/level_two
type: boolean
type: object
http3:
description: HTTP3 provides HTTP/3 configuration on the listener.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -804,7 +804,9 @@ xds:
envoy.extensions.upstreams.http.v3.HttpProtocolOptions:
'@type': type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions
explicitHttpConfig:
http2ProtocolOptions: {}
http2ProtocolOptions:
initialConnectionWindowSize: 1048576
initialStreamWindowSize: 65536
- cluster:
'@type': type.googleapis.com/envoy.config.cluster.v3.Cluster
circuitBreakers:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -504,7 +504,10 @@
"envoy.extensions.upstreams.http.v3.HttpProtocolOptions": {
"@type": "type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions",
"explicitHttpConfig": {
"http2ProtocolOptions": {}
"http2ProtocolOptions": {
"initialConnectionWindowSize": 1048576,
"initialStreamWindowSize": 65536
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,9 @@ xds:
envoy.extensions.upstreams.http.v3.HttpProtocolOptions:
'@type': type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions
explicitHttpConfig:
http2ProtocolOptions: {}
http2ProtocolOptions:
initialConnectionWindowSize: 1048576
initialStreamWindowSize: 65536
- cluster:
'@type': type.googleapis.com/envoy.config.cluster.v3.Cluster
circuitBreakers:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@ xds:
envoy.extensions.upstreams.http.v3.HttpProtocolOptions:
'@type': type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions
explicitHttpConfig:
http2ProtocolOptions: {}
http2ProtocolOptions:
initialConnectionWindowSize: 1048576
initialStreamWindowSize: 65536
- cluster:
'@type': type.googleapis.com/envoy.config.cluster.v3.Cluster
circuitBreakers:
Expand Down
17 changes: 17 additions & 0 deletions internal/gatewayapi/backendtrafficpolicy.go
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,7 @@ func (t *Translator) translateBackendTrafficPolicyForRoute(policy *egv1a1.Backen
ka *ir.TCPKeepalive
rt *ir.Retry
bc *ir.BackendConnection
h2 *ir.HTTP2Settings
err, errs error
)

Expand Down Expand Up @@ -373,6 +374,13 @@ func (t *Translator) translateBackendTrafficPolicyForRoute(policy *egv1a1.Backen
}
}

if policy.Spec.HTTP2 != nil {
if h2, err = buildIRHTTP2Settings(policy.Spec.HTTP2); err != nil {
err = perr.WithMessage(err, "HTTP2")
errs = errors.Join(errs, err)

Check warning on line 380 in internal/gatewayapi/backendtrafficpolicy.go

View check run for this annotation

Codecov / codecov/patch

internal/gatewayapi/backendtrafficpolicy.go#L379-L380

Added lines #L379 - L380 were not covered by tests
}
}

// Early return if got any errors
if errs != nil {
return errs
Expand Down Expand Up @@ -422,6 +430,7 @@ func (t *Translator) translateBackendTrafficPolicyForRoute(policy *egv1a1.Backen
TCPKeepalive: ka,
Retry: rt,
BackendConnection: bc,
HTTP2: h2,
}

// Update the Host field in HealthCheck, now that we have access to the Route Hostname.
Expand Down Expand Up @@ -456,6 +465,7 @@ func (t *Translator) translateBackendTrafficPolicyForGateway(policy *egv1a1.Back
ct *ir.Timeout
ka *ir.TCPKeepalive
rt *ir.Retry
h2 *ir.HTTP2Settings
err, errs error
)

Expand Down Expand Up @@ -502,6 +512,12 @@ func (t *Translator) translateBackendTrafficPolicyForGateway(policy *egv1a1.Back
errs = errors.Join(errs, err)
}
}
if policy.Spec.HTTP2 != nil {
if h2, err = buildIRHTTP2Settings(policy.Spec.HTTP2); err != nil {
err = perr.WithMessage(err, "HTTP2")
errs = errors.Join(errs, err)

Check warning on line 518 in internal/gatewayapi/backendtrafficpolicy.go

View check run for this annotation

Codecov / codecov/patch

internal/gatewayapi/backendtrafficpolicy.go#L517-L518

Added lines #L517 - L518 were not covered by tests
}
}

// Early return if got any errors
if errs != nil {
Expand Down Expand Up @@ -590,6 +606,7 @@ func (t *Translator) translateBackendTrafficPolicyForGateway(policy *egv1a1.Back
FaultInjection: fi,
TCPKeepalive: ka,
Retry: rt,
HTTP2: h2,
}

// Update the Host field in HealthCheck, now that we have access to the Route Hostname.
Expand Down
6 changes: 1 addition & 5 deletions internal/gatewayapi/clienttrafficpolicy.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,7 @@ import (

const (
// Use an invalid string to represent all sections (listeners) within a Gateway
AllSections = "/"
MinHTTP2InitialStreamWindowSize = 65535 // https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/core/v3/protocol.proto#envoy-v3-api-field-config-core-v3-http2protocoloptions-initial-stream-window-size
MaxHTTP2InitialStreamWindowSize = 2147483647 // https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/core/v3/protocol.proto#envoy-v3-api-field-config-core-v3-http2protocoloptions-initial-stream-window-size
MinHTTP2InitialConnectionWindowSize = MinHTTP2InitialStreamWindowSize
MaxHTTP2InitialConnectionWindowSize = MaxHTTP2InitialStreamWindowSize
AllSections = "/"
)

func hasSectionName(target *gwapiv1a2.LocalPolicyTargetReferenceWithSectionName) bool {
Expand Down
66 changes: 66 additions & 0 deletions internal/gatewayapi/http.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Copyright Envoy Gateway Authors
// SPDX-License-Identifier: Apache-2.0
// The full text of the Apache license is available in the LICENSE file at
// the root of the repo.

package gatewayapi

import (
"errors"
"fmt"

"k8s.io/utils/ptr"

egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1"
"github.com/envoyproxy/gateway/internal/ir"
)

const (
MinHTTP2InitialStreamWindowSize = 65535 // https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/core/v3/protocol.proto#envoy-v3-api-field-config-core-v3-http2protocoloptions-initial-stream-window-size
MaxHTTP2InitialStreamWindowSize = 2147483647 // https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/core/v3/protocol.proto#envoy-v3-api-field-config-core-v3-http2protocoloptions-initial-stream-window-size
MinHTTP2InitialConnectionWindowSize = MinHTTP2InitialStreamWindowSize
MaxHTTP2InitialConnectionWindowSize = MaxHTTP2InitialStreamWindowSize
)

func buildIRHTTP2Settings(http2Settings *egv1a1.HTTP2Settings) (*ir.HTTP2Settings, error) {
var (
http2 = &ir.HTTP2Settings{}
errs error
)

if http2Settings.InitialStreamWindowSize != nil {
initialStreamWindowSize, ok := http2Settings.InitialStreamWindowSize.AsInt64()
switch {
case !ok:
errs = errors.Join(errs, fmt.Errorf("invalid InitialStreamWindowSize value %s", http2Settings.InitialStreamWindowSize.String()))
case initialStreamWindowSize < MinHTTP2InitialStreamWindowSize || initialStreamWindowSize > MaxHTTP2InitialStreamWindowSize:
errs = errors.Join(errs, fmt.Errorf("InitialStreamWindowSize value %s is out of range, must be between %d and %d",
http2Settings.InitialStreamWindowSize.String(),
MinHTTP2InitialStreamWindowSize,
MaxHTTP2InitialStreamWindowSize))

Check warning on line 40 in internal/gatewayapi/http.go

View check run for this annotation

Codecov / codecov/patch

internal/gatewayapi/http.go#L34-L40

Added lines #L34 - L40 were not covered by tests
default:
http2.InitialStreamWindowSize = ptr.To(uint32(initialStreamWindowSize))
}
}

if http2Settings.InitialConnectionWindowSize != nil {
initialConnectionWindowSize, ok := http2Settings.InitialConnectionWindowSize.AsInt64()
switch {
case !ok:
errs = errors.Join(errs, fmt.Errorf("invalid InitialConnectionWindowSize value %s", http2Settings.InitialConnectionWindowSize.String()))
case initialConnectionWindowSize < MinHTTP2InitialConnectionWindowSize || initialConnectionWindowSize > MaxHTTP2InitialConnectionWindowSize:
errs = errors.Join(errs, fmt.Errorf("InitialConnectionWindowSize value %s is out of range, must be between %d and %d",
http2Settings.InitialConnectionWindowSize.String(),
MinHTTP2InitialConnectionWindowSize,
MaxHTTP2InitialConnectionWindowSize))

Check warning on line 55 in internal/gatewayapi/http.go

View check run for this annotation

Codecov / codecov/patch

internal/gatewayapi/http.go#L49-L55

Added lines #L49 - L55 were not covered by tests
default:
http2.InitialConnectionWindowSize = ptr.To(uint32(initialConnectionWindowSize))
}
}

http2.MaxConcurrentStreams = http2Settings.MaxConcurrentStreams

http2.ResetStreamOnError = http2Settings.ResetStreamOnError

return http2, errs
}
Loading

0 comments on commit 3fd193d

Please sign in to comment.