diff --git a/cmd/contour/servecontext.go b/cmd/contour/servecontext.go index c4efed93493..8c45ca11a86 100644 --- a/cmd/contour/servecontext.go +++ b/cmd/contour/servecontext.go @@ -23,7 +23,7 @@ import ( "strings" "time" - envoy_v2 "github.com/projectcontour/contour/internal/envoy/v2" + "github.com/projectcontour/contour/internal/envoy" xdscache_v2 "github.com/projectcontour/contour/internal/xdscache/v2" "github.com/projectcontour/contour/pkg/config" "github.com/sirupsen/logrus" @@ -234,19 +234,19 @@ func (ctx *serveContext) proxyRootNamespaces() []string { // parseDefaultHTTPVersions parses a list of supported HTTP versions // (of the form "HTTP/xx") into a slice of unique version constants. -func parseDefaultHTTPVersions(versions []config.HTTPVersionType) []envoy_v2.HTTPVersionType { - wanted := map[envoy_v2.HTTPVersionType]struct{}{} +func parseDefaultHTTPVersions(versions []config.HTTPVersionType) []envoy.HTTPVersionType { + wanted := map[envoy.HTTPVersionType]struct{}{} for _, v := range versions { switch v { case config.HTTPVersion1: - wanted[envoy_v2.HTTPVersion1] = struct{}{} + wanted[envoy.HTTPVersion1] = struct{}{} case config.HTTPVersion2: - wanted[envoy_v2.HTTPVersion2] = struct{}{} + wanted[envoy.HTTPVersion2] = struct{}{} } } - var parsed []envoy_v2.HTTPVersionType + var parsed []envoy.HTTPVersionType for k := range wanted { parsed = append(parsed, k) diff --git a/cmd/contour/servecontext_test.go b/cmd/contour/servecontext_test.go index b2fdee64868..1fcb113c085 100644 --- a/cmd/contour/servecontext_test.go +++ b/cmd/contour/servecontext_test.go @@ -26,7 +26,8 @@ import ( "testing" "time" - envoy_v2 "github.com/projectcontour/contour/internal/envoy/v2" + "github.com/projectcontour/contour/internal/envoy" + "github.com/projectcontour/contour/internal/fixture" "github.com/projectcontour/contour/pkg/config" "github.com/stretchr/testify/assert" @@ -327,7 +328,7 @@ func peekError(conn net.Conn) error { func TestParseHTTPVersions(t *testing.T) { cases := map[string]struct { versions []config.HTTPVersionType - parseVersions []envoy_v2.HTTPVersionType + parseVersions []envoy.HTTPVersionType }{ "empty": { versions: []config.HTTPVersionType{}, @@ -335,17 +336,17 @@ func TestParseHTTPVersions(t *testing.T) { }, "http/1.1": { versions: []config.HTTPVersionType{config.HTTPVersion1}, - parseVersions: []envoy_v2.HTTPVersionType{envoy_v2.HTTPVersion1}, + parseVersions: []envoy.HTTPVersionType{envoy.HTTPVersion1}, }, "http/1.1+http/2": { versions: []config.HTTPVersionType{config.HTTPVersion1, config.HTTPVersion2}, - parseVersions: []envoy_v2.HTTPVersionType{envoy_v2.HTTPVersion1, envoy_v2.HTTPVersion2}, + parseVersions: []envoy.HTTPVersionType{envoy.HTTPVersion1, envoy.HTTPVersion2}, }, "http/1.1+http/2 duplicated": { versions: []config.HTTPVersionType{ config.HTTPVersion1, config.HTTPVersion2, config.HTTPVersion1, config.HTTPVersion2}, - parseVersions: []envoy_v2.HTTPVersionType{envoy_v2.HTTPVersion1, envoy_v2.HTTPVersion2}, + parseVersions: []envoy.HTTPVersionType{envoy.HTTPVersion1, envoy.HTTPVersion2}, }, } diff --git a/internal/envoy/listener.go b/internal/envoy/listener.go new file mode 100644 index 00000000000..9297683bc66 --- /dev/null +++ b/internal/envoy/listener.go @@ -0,0 +1,79 @@ +// Copyright Project Contour 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 envoy + +import ( + "log" + + http "github.com/envoyproxy/go-control-plane/envoy/config/filter/network/http_connection_manager/v2" +) + +type HTTPVersionType = http.HttpConnectionManager_CodecType + +const ( + HTTPVersionAuto HTTPVersionType = http.HttpConnectionManager_AUTO + HTTPVersion1 HTTPVersionType = http.HttpConnectionManager_HTTP1 + HTTPVersion2 HTTPVersionType = http.HttpConnectionManager_HTTP2 + HTTPVersion3 HTTPVersionType = http.HttpConnectionManager_HTTP3 +) + +// ProtoNamesForVersions returns the slice of ALPN protocol names for the give HTTP versions. +func ProtoNamesForVersions(versions ...HTTPVersionType) []string { + protocols := map[HTTPVersionType]string{ + HTTPVersion1: "http/1.1", + HTTPVersion2: "h2", + HTTPVersion3: "", + } + defaultVersions := []string{"h2", "http/1.1"} + wantedVersions := map[HTTPVersionType]struct{}{} + + if versions == nil { + return defaultVersions + } + + for _, v := range versions { + wantedVersions[v] = struct{}{} + } + + var alpn []string + + // Check for versions in preference order. + for _, v := range []HTTPVersionType{HTTPVersionAuto, HTTPVersion2, HTTPVersion1} { + if _, ok := wantedVersions[v]; ok { + if v == HTTPVersionAuto { + return defaultVersions + } + + log.Printf("wanted %d -> %s", v, protocols[v]) + alpn = append(alpn, protocols[v]) + } + } + + return alpn +} + +// CodecForVersions determines a single Envoy HTTP codec constant +// that support all the given HTTP protocol versions. +func CodecForVersions(versions ...HTTPVersionType) HTTPVersionType { + switch len(versions) { + case 1: + return versions[0] + case 0: + // Default is to autodetect. + return HTTPVersionAuto + default: + // If more than one version is allowed, autodetect and let ALPN sort it out. + return HTTPVersionAuto + } +} diff --git a/internal/envoy/listener_test.go b/internal/envoy/listener_test.go new file mode 100644 index 00000000000..671edca5e9f --- /dev/null +++ b/internal/envoy/listener_test.go @@ -0,0 +1,36 @@ +// Copyright Project Contour 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 envoy + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestCodecForVersions(t *testing.T) { + assert.Equal(t, CodecForVersions(HTTPVersionAuto), HTTPVersionAuto) + assert.Equal(t, CodecForVersions(HTTPVersion1, HTTPVersion2), HTTPVersionAuto) + assert.Equal(t, CodecForVersions(HTTPVersion1), HTTPVersion1) + assert.Equal(t, CodecForVersions(HTTPVersion2), HTTPVersion2) +} + +func TestProtoNamesForVersions(t *testing.T) { + assert.Equal(t, ProtoNamesForVersions(), []string{"h2", "http/1.1"}) + assert.Equal(t, ProtoNamesForVersions(HTTPVersionAuto), []string{"h2", "http/1.1"}) + assert.Equal(t, ProtoNamesForVersions(HTTPVersion1), []string{"http/1.1"}) + assert.Equal(t, ProtoNamesForVersions(HTTPVersion2), []string{"h2"}) + assert.Equal(t, ProtoNamesForVersions(HTTPVersion3), []string(nil)) + assert.Equal(t, ProtoNamesForVersions(HTTPVersion1, HTTPVersion2), []string{"h2", "http/1.1"}) +} diff --git a/internal/envoy/v2/listener.go b/internal/envoy/v2/listener.go index ad9f76f0dd1..a701c309b24 100644 --- a/internal/envoy/v2/listener.go +++ b/internal/envoy/v2/listener.go @@ -15,7 +15,6 @@ package v2 import ( "fmt" - "log" "sort" "strings" "time" @@ -38,65 +37,6 @@ import ( "github.com/projectcontour/contour/internal/timeout" ) -type HTTPVersionType = http.HttpConnectionManager_CodecType - -const ( - HTTPVersionAuto HTTPVersionType = http.HttpConnectionManager_AUTO - HTTPVersion1 HTTPVersionType = http.HttpConnectionManager_HTTP1 - HTTPVersion2 HTTPVersionType = http.HttpConnectionManager_HTTP2 - HTTPVersion3 HTTPVersionType = http.HttpConnectionManager_HTTP3 -) - -// ProtoNamesForVersions returns the slice of ALPN protocol names for the give HTTP versions. -func ProtoNamesForVersions(versions ...HTTPVersionType) []string { - protocols := map[HTTPVersionType]string{ - HTTPVersion1: "http/1.1", - HTTPVersion2: "h2", - HTTPVersion3: "", - } - defaultVersions := []string{"h2", "http/1.1"} - wantedVersions := map[HTTPVersionType]struct{}{} - - if versions == nil { - return defaultVersions - } - - for _, v := range versions { - wantedVersions[v] = struct{}{} - } - - var alpn []string - - // Check for versions in preference order. - for _, v := range []HTTPVersionType{HTTPVersionAuto, HTTPVersion2, HTTPVersion1} { - if _, ok := wantedVersions[v]; ok { - if v == HTTPVersionAuto { - return defaultVersions - } - - log.Printf("wanted %d -> %s", v, protocols[v]) - alpn = append(alpn, protocols[v]) - } - } - - return alpn -} - -// CodecForVersions determines a single Envoy HTTP codec constant -// that support all the given HTTP protocol versions. -func CodecForVersions(versions ...HTTPVersionType) HTTPVersionType { - switch len(versions) { - case 1: - return versions[0] - case 0: - // Default is to autodetect. - return HTTPVersionAuto - default: - // If more than one version is allowed, autodetect and let ALPN sort it out. - return HTTPVersionAuto - } -} - // TLSInspector returns a new TLS inspector listener filter. func TLSInspector() *envoy_api_v2_listener.ListenerFilter { return &envoy_api_v2_listener.ListenerFilter{ @@ -140,7 +80,7 @@ type httpConnectionManagerBuilder struct { maxConnectionDuration timeout.Setting connectionShutdownGracePeriod timeout.Setting filters []*http.HttpFilter - codec HTTPVersionType // Note the zero value is AUTO, which is the default we want. + codec envoy.HTTPVersionType // Note the zero value is AUTO, which is the default we want. } // RouteConfigName sets the name of the RDS element that contains @@ -161,7 +101,7 @@ func (b *httpConnectionManagerBuilder) MetricsPrefix(prefix string) *httpConnect } // Codec sets the HTTP codec for the manager. The default is AUTO. -func (b *httpConnectionManagerBuilder) Codec(codecType HTTPVersionType) *httpConnectionManagerBuilder { +func (b *httpConnectionManagerBuilder) Codec(codecType envoy.HTTPVersionType) *httpConnectionManagerBuilder { b.codec = codecType return b } diff --git a/internal/envoy/v2/listener_test.go b/internal/envoy/v2/listener_test.go index f3e5d413da2..41990f2f054 100644 --- a/internal/envoy/v2/listener_test.go +++ b/internal/envoy/v2/listener_test.go @@ -36,22 +36,6 @@ import ( "k8s.io/apimachinery/pkg/util/intstr" ) -func TestCodecForVersions(t *testing.T) { - assert.Equal(t, CodecForVersions(HTTPVersionAuto), HTTPVersionAuto) - assert.Equal(t, CodecForVersions(HTTPVersion1, HTTPVersion2), HTTPVersionAuto) - assert.Equal(t, CodecForVersions(HTTPVersion1), HTTPVersion1) - assert.Equal(t, CodecForVersions(HTTPVersion2), HTTPVersion2) -} - -func TestProtoNamesForVersions(t *testing.T) { - assert.Equal(t, ProtoNamesForVersions(), []string{"h2", "http/1.1"}) - assert.Equal(t, ProtoNamesForVersions(HTTPVersionAuto), []string{"h2", "http/1.1"}) - assert.Equal(t, ProtoNamesForVersions(HTTPVersion1), []string{"http/1.1"}) - assert.Equal(t, ProtoNamesForVersions(HTTPVersion2), []string{"h2"}) - assert.Equal(t, ProtoNamesForVersions(HTTPVersion3), []string(nil)) - assert.Equal(t, ProtoNamesForVersions(HTTPVersion1, HTTPVersion2), []string{"h2", "http/1.1"}) -} - func TestListener(t *testing.T) { tests := map[string]struct { name, address string diff --git a/internal/envoy/v3/listener_test.go b/internal/envoy/v3/listener_test.go new file mode 100644 index 00000000000..ce57fcbd327 --- /dev/null +++ b/internal/envoy/v3/listener_test.go @@ -0,0 +1,14 @@ +// Copyright Project Contour 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 v3 diff --git a/internal/xdscache/v2/listener.go b/internal/xdscache/v2/listener.go index ee39578c8f7..d4ed7c99510 100644 --- a/internal/xdscache/v2/listener.go +++ b/internal/xdscache/v2/listener.go @@ -27,6 +27,7 @@ import ( "github.com/golang/protobuf/proto" "github.com/projectcontour/contour/internal/contour" "github.com/projectcontour/contour/internal/dag" + "github.com/projectcontour/contour/internal/envoy" envoy_v2 "github.com/projectcontour/contour/internal/envoy/v2" "github.com/projectcontour/contour/internal/protobuf" "github.com/projectcontour/contour/internal/sorter" @@ -86,7 +87,7 @@ type ListenerConfig struct { // supported versions are accepted. This is applied to both // HTTP and HTTPS listeners but has practical effect only for // HTTPS, because we don't support h2c. - DefaultHTTPVersions []envoy_v2.HTTPVersionType + DefaultHTTPVersions []envoy.HTTPVersionType // AccessLogType defines if Envoy logs should be output as Envoy's default or JSON. // Valid values: 'envoy', 'json' @@ -317,7 +318,7 @@ func visitListeners(root dag.Vertex, lvc *ListenerConfig) map[string]*envoy_api_ if lv.http { // Add a listener if there are vhosts bound to http. cm := envoy_v2.HTTPConnectionManagerBuilder(). - Codec(envoy_v2.CodecForVersions(lv.DefaultHTTPVersions...)). + Codec(envoy.CodecForVersions(lv.DefaultHTTPVersions...)). DefaultFilters(). RouteConfigName(ENVOY_HTTP_LISTENER). MetricsPrefix(ENVOY_HTTP_LISTENER). @@ -401,7 +402,7 @@ func (v *listenerVisitor) visit(vertex dag.Vertex) { // coded into monitoring dashboards. filters = envoy_v2.Filters( envoy_v2.HTTPConnectionManagerBuilder(). - Codec(envoy_v2.CodecForVersions(v.DefaultHTTPVersions...)). + Codec(envoy.CodecForVersions(v.DefaultHTTPVersions...)). AddFilter(envoy_v2.FilterMisdirectedRequests(vh.VirtualHost.Name)). DefaultFilters(). AddFilter(authFilter). @@ -416,7 +417,7 @@ func (v *listenerVisitor) visit(vertex dag.Vertex) { Get(), ) - alpnProtos = envoy_v2.ProtoNamesForVersions(v.DefaultHTTPVersions...) + alpnProtos = envoy.ProtoNamesForVersions(v.DefaultHTTPVersions...) } else { filters = envoy_v2.Filters( envoy_v2.TCPProxy(ENVOY_HTTPS_LISTENER,