Skip to content

Commit

Permalink
GEP-1911 - h2c backend protocol
Browse files Browse the repository at this point in the history
  • Loading branch information
dprotaso committed Oct 12, 2023
1 parent 38125d1 commit aed554d
Show file tree
Hide file tree
Showing 5 changed files with 167 additions and 17 deletions.
88 changes: 88 additions & 0 deletions conformance/tests/httproute-backend-protocol-h2c.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
Copyright 2023 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package tests

import (
"context"
"crypto/tls"
"net"
"testing"

"golang.org/x/net/http2"

"k8s.io/apimachinery/pkg/types"

"sigs.k8s.io/gateway-api/conformance/utils/http"
"sigs.k8s.io/gateway-api/conformance/utils/kubernetes"
"sigs.k8s.io/gateway-api/conformance/utils/suite"
)

func init() {
ConformanceTests = append(ConformanceTests, HTTPRouteBackendProtocolH2C)
}

var HTTPRouteBackendProtocolH2C = suite.ConformanceTest{
ShortName: "HTTPRouteBackendProtocolH2C",
Description: "A HTTPRoute with a BackendRef that has an appProtocol kubernetes.io/h2c should be functional",
Features: []suite.SupportedFeature{
suite.SupportGateway,
suite.SupportHTTPRoute,
suite.SupportHTTPRouteBackendProtocolH2C,
},
Manifests: []string{
"tests/httproute-backend-protocol-h2c.yaml",
},
Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
ns := "gateway-conformance-infra"
routeNN := types.NamespacedName{Name: "backend-protocol-h2c", Namespace: ns}
gwNN := types.NamespacedName{Name: "same-namespace", Namespace: ns}
gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN)

t.Run("http2 prior knowledge request should reach backend", func(t *testing.T) {
http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, http.ExpectedResponse{
Request: http.Request{
Path: "/h2c",
Transport: &http2.Transport{
AllowHTTP: true,
DialTLSContext: func(ctx context.Context, network, addr string, cfg *tls.Config) (net.Conn, error) {
var d net.Dialer
return d.DialContext(ctx, network, addr)
},
},
},
Response: http.Response{StatusCode: 200},
Backend: "h2c-backend",
Namespace: "gateway-conformance-web-backend",
})
})

t.Run("h2c upgrade request should reach backend", func(t *testing.T) {
http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, http.ExpectedResponse{
Request: http.Request{
Path: "/h2c",
Headers: map[string]string{
"HTTP2-Settings": "",
"Connection": "Upgrade, HTTP2-Settings",
},
},
Response: http.Response{StatusCode: 200},
Backend: "h2c-backend",
Namespace: "gateway-conformance-web-backend",
})
})
},
}
50 changes: 50 additions & 0 deletions conformance/tests/httproute-backend-protocol-h2c.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
name: backend-protocol-h2c
namespace: gateway-conformance-infra
spec:
parentRefs:
- name: same-namespace
rules:
- backendRefs:
- name: h2c-backend
port: 8080
---
apiVersion: v1
kind: Service
metadata:
name: h2c-backend
namespace: gateway-conformance-infra
spec:
selector:
app: h2c-backend
ports:
- protocol: TCP
appProtocol: kubernetes.io/h2c
port: 8080
targetPort: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: h2c-backend
namespace: gateway-conformance-infra
labels:
app: h2c-backend
spec:
replicas: 1
selector:
matchLabels:
app: h2c-backend
template:
metadata:
labels:
app: h2c-backend
spec:
containers:
- name: h2c-backend
image: gcr.io/k8s-staging-gateway-api/echo-basic:latest
resources:
requests:
cpu: 10m
3 changes: 3 additions & 0 deletions conformance/utils/http/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package http
import (
"fmt"
"net"
"net/http"
"net/url"
"strings"
"testing"
Expand Down Expand Up @@ -69,6 +70,7 @@ type Request struct {
Headers map[string]string
UnfollowRedirect bool
Protocol string
Transport http.RoundTripper
}

// ExpectedRequest defines expected properties of a request that reaches a backend.
Expand Down Expand Up @@ -128,6 +130,7 @@ func MakeRequest(t *testing.T, expected *ExpectedResponse, gwAddr, protocol, sch
Protocol: protocol,
Headers: map[string][]string{},
UnfollowRedirect: expected.Request.UnfollowRedirect,
Transport: expected.Request.Transport,
}

if expected.Request.Headers != nil {
Expand Down
39 changes: 22 additions & 17 deletions conformance/utils/roundtripper/roundtripper.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ type Request struct {
CertPem []byte
KeyPem []byte
Server string
Transport http.RoundTripper
}

// String returns a printable version of Request for logging. Note that the
Expand Down Expand Up @@ -117,25 +118,29 @@ func (d *DefaultRoundTripper) CaptureRoundTrip(request Request) (*CapturedReques
}
}

transport := &http.Transport{
DialContext: d.CustomDialContext,
// We disable keep-alives so that we don't leak established TCP connections.
// Leaking TCP connections is bad because we could eventually hit the
// threshold of maximum number of open TCP connections to a specific
// destination. Keep-alives are not presently utilized so disabling this has
// no adverse affect.
//
// Ref. https://github.com/kubernetes-sigs/gateway-api/issues/2357
DisableKeepAlives: true,
}
if request.Server != "" && len(request.CertPem) != 0 && len(request.KeyPem) != 0 {
tlsConfig, err := tlsClientConfig(request.Server, request.CertPem, request.KeyPem)
if err != nil {
return nil, nil, err
if request.Transport != nil {
client.Transport = request.Transport
} else {
transport := &http.Transport{
DialContext: d.CustomDialContext,
// We disable keep-alives so that we don't leak established TCP connections.
// Leaking TCP connections is bad because we could eventually hit the
// threshold of maximum number of open TCP connections to a specific
// destination. Keep-alives are not presently utilized so disabling this has
// no adverse affect.
//
// Ref. https://github.com/kubernetes-sigs/gateway-api/issues/2357
DisableKeepAlives: true,
}
if request.Server != "" && len(request.CertPem) != 0 && len(request.KeyPem) != 0 {
tlsConfig, err := tlsClientConfig(request.Server, request.CertPem, request.KeyPem)
if err != nil {
return nil, nil, err
}
transport.TLSClientConfig = tlsConfig
}
transport.TLSClientConfig = tlsConfig
client.Transport = transport
}
client.Transport = transport

method := "GET"
if request.Method != "" {
Expand Down
4 changes: 4 additions & 0 deletions conformance/utils/suite/features.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,9 @@ const (

// This option indicates support for multiple RequestMirror filters within the same HTTPRoute rule (extended conformance).
SupportHTTPRouteRequestMultipleMirrors SupportedFeature = "HTTPRouteRequestMultipleMirrors"

// This option indicates support for HTTPRoute with a backendref with an appProtoocol 'kubernetes.io/h2c'
SupportHTTPRouteBackendProtocolH2C SupportedFeature = "HTTPRouteBackendProtocolH2C"
)

// HTTPRouteExtendedFeatures includes all the supported features for HTTPRoute
Expand All @@ -137,6 +140,7 @@ var HTTPRouteExtendedFeatures = sets.New(
SupportHTTPRoutePathRewrite,
SupportHTTPRouteRequestMirror,
SupportHTTPRouteRequestMultipleMirrors,
SupportHTTPRouteBackendProtocolH2C,
)

// -----------------------------------------------------------------------------
Expand Down

0 comments on commit aed554d

Please sign in to comment.