envoyconfig: preserve case of HTTP headers when using HTTP/1 (#3956)

This commit is contained in:
Caleb Doxsey 2023-02-10 16:29:10 -07:00 committed by GitHub
parent f2adeadaca
commit e66c26c9ad
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 71 additions and 4 deletions

View file

@ -340,7 +340,8 @@ func (b *Builder) buildMainHTTPConnectionManagerFilter(
IdleTimeout: durationpb.New(options.IdleTimeout),
MaxStreamDuration: maxStreamDuration,
},
RequestTimeout: durationpb.New(options.ReadTimeout),
HttpProtocolOptions: http1ProtocolOptions,
RequestTimeout: durationpb.New(options.ReadTimeout),
Tracing: &envoy_http_connection_manager.HttpConnectionManager_Tracing{
RandomSampling: &envoy_type_v3.Percent{Value: options.TracingSampleRate * 100},
Provider: tracingProvider,

View file

@ -742,6 +742,16 @@ func Test_buildMainHTTPConnectionManagerFilter(t *testing.T) {
"useRemoteAddress": true,
"skipXffAppend": true,
"xffNumTrustedHops": 1,
"httpProtocolOptions": {
"headerKeyFormat": {
"statefulFormatter": {
"name": "preserve_case",
"typedConfig": {
"@type": "type.googleapis.com/envoy.extensions.http.header_formatters.preserve_case.v3.PreserveCaseFormatterConfig"
}
}
}
},
"localReplyConfig":{
"mappers":[
{

View file

@ -4,6 +4,7 @@ import (
"context"
envoy_config_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
envoy_extensions_http_header_formatters_preserve_case_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/http/header_formatters/preserve_case/v3"
envoy_extensions_upstreams_http_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/upstreams/http/v3"
"google.golang.org/protobuf/types/known/anypb"
"google.golang.org/protobuf/types/known/wrapperspb"
@ -28,6 +29,18 @@ const (
initialConnectionWindowSizeLimit uint32 = 1 * 1024 * 1024
)
var http1ProtocolOptions = &envoy_config_core_v3.Http1ProtocolOptions{
// fix for #3935, preserve case of HTTP headers for applications that are case-sensitive
HeaderKeyFormat: &envoy_config_core_v3.Http1ProtocolOptions_HeaderKeyFormat{
HeaderFormat: &envoy_config_core_v3.Http1ProtocolOptions_HeaderKeyFormat_StatefulFormatter{
StatefulFormatter: &envoy_config_core_v3.TypedExtensionConfig{
Name: "preserve_case",
TypedConfig: marshalAny(&envoy_extensions_http_header_formatters_preserve_case_v3.PreserveCaseFormatterConfig{}),
},
},
},
}
var http2ProtocolOptions = &envoy_config_core_v3.Http2ProtocolOptions{
AllowConnect: true,
MaxConcurrentStreams: wrapperspb.UInt32(maxConcurrentStreams),
@ -35,13 +48,19 @@ var http2ProtocolOptions = &envoy_config_core_v3.Http2ProtocolOptions{
InitialConnectionWindowSize: wrapperspb.UInt32(initialConnectionWindowSizeLimit),
}
func buildTypedExtensionProtocolOptions(endpoints []Endpoint, upstreamProtocol upstreamProtocolConfig) map[string]*anypb.Any {
func buildTypedExtensionProtocolOptions(
endpoints []Endpoint,
upstreamProtocol upstreamProtocolConfig,
) map[string]*anypb.Any {
return map[string]*anypb.Any{
"envoy.extensions.upstreams.http.v3.HttpProtocolOptions": marshalAny(buildUpstreamProtocolOptions(endpoints, upstreamProtocol)),
}
}
func buildUpstreamProtocolOptions(endpoints []Endpoint, upstreamProtocol upstreamProtocolConfig) *envoy_extensions_upstreams_http_v3.HttpProtocolOptions {
func buildUpstreamProtocolOptions(
endpoints []Endpoint,
upstreamProtocol upstreamProtocolConfig,
) *envoy_extensions_upstreams_http_v3.HttpProtocolOptions {
switch upstreamProtocol {
case upstreamProtocolHTTP2:
// when explicitly configured, force HTTP/2
@ -66,6 +85,7 @@ func buildUpstreamProtocolOptions(endpoints []Endpoint, upstreamProtocol upstrea
return &envoy_extensions_upstreams_http_v3.HttpProtocolOptions{
UpstreamProtocolOptions: &envoy_extensions_upstreams_http_v3.HttpProtocolOptions_AutoConfig{
AutoConfig: &envoy_extensions_upstreams_http_v3.HttpProtocolOptions_AutoHttpConfig{
HttpProtocolOptions: http1ProtocolOptions,
Http2ProtocolOptions: http2ProtocolOptions,
},
},
@ -78,7 +98,7 @@ func buildUpstreamProtocolOptions(endpoints []Endpoint, upstreamProtocol upstrea
UpstreamProtocolOptions: &envoy_extensions_upstreams_http_v3.HttpProtocolOptions_ExplicitHttpConfig_{
ExplicitHttpConfig: &envoy_extensions_upstreams_http_v3.HttpProtocolOptions_ExplicitHttpConfig{
ProtocolConfig: &envoy_extensions_upstreams_http_v3.HttpProtocolOptions_ExplicitHttpConfig_HttpProtocolOptions{
HttpProtocolOptions: &envoy_config_core_v3.Http1ProtocolOptions{},
HttpProtocolOptions: http1ProtocolOptions,
},
},
},

View file

@ -0,0 +1,36 @@
package envoyconfig
import (
"testing"
envoy_config_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
envoy_extensions_http_header_formatters_preserve_case_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/http/header_formatters/preserve_case/v3"
envoy_extensions_upstreams_http_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/upstreams/http/v3"
"github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/assert"
"google.golang.org/protobuf/testing/protocmp"
)
func Test_buildUpstreamProtocolOptions(t *testing.T) {
t.Parallel()
assert.Empty(t,
cmp.Diff(&envoy_extensions_upstreams_http_v3.HttpProtocolOptions{
UpstreamProtocolOptions: &envoy_extensions_upstreams_http_v3.HttpProtocolOptions_ExplicitHttpConfig_{
ExplicitHttpConfig: &envoy_extensions_upstreams_http_v3.HttpProtocolOptions_ExplicitHttpConfig{
ProtocolConfig: &envoy_extensions_upstreams_http_v3.HttpProtocolOptions_ExplicitHttpConfig_HttpProtocolOptions{
HttpProtocolOptions: &envoy_config_core_v3.Http1ProtocolOptions{
HeaderKeyFormat: &envoy_config_core_v3.Http1ProtocolOptions_HeaderKeyFormat{
HeaderFormat: &envoy_config_core_v3.Http1ProtocolOptions_HeaderKeyFormat_StatefulFormatter{
StatefulFormatter: &envoy_config_core_v3.TypedExtensionConfig{
Name: "preserve_case",
TypedConfig: marshalAny(&envoy_extensions_http_header_formatters_preserve_case_v3.PreserveCaseFormatterConfig{}),
},
},
},
},
},
},
},
}, buildUpstreamProtocolOptions(nil, upstreamProtocolHTTP1), protocmp.Transform()))
}