Revert "hotfix: remove http filter grpc-web for support http content type for…" (#778)

This reverts commit 6130fcbe88.
This commit is contained in:
naison
2026-04-19 21:59:47 +08:00
committed by GitHub
parent 5d39a032fe
commit 3646046288
6 changed files with 359 additions and 98 deletions
+7 -3
View File
@@ -15,6 +15,7 @@ import (
route "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
accesslogfilev3 "github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/file/v3"
corsv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/cors/v3"
grpcwebv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/grpc_web/v3"
routerv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/router/v3"
httpinspector "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/listener/http_inspector/v3"
dstv3inspector "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/listener/original_dst/v3"
@@ -391,10 +392,13 @@ func ToListener(listenerName string, routeName string, port int32, p corev1.Prot
},
},
// "details": "Error: terminal filter named envoy.filters.http.router of type envoy.filters.http.router must be the last filter in a http filter chain."
// Note: grpc-web filter is intentionally excluded here because it
// intercepts requests with Content-Type: multipart/form-data and
// application/x-www-form-urlencoded, breaking non-gRPC HTTP traffic.
HttpFilters: []*httpconnectionmanager.HttpFilter{
{
Name: wellknown.GRPCWeb,
ConfigType: &httpconnectionmanager.HttpFilter_TypedConfig{
TypedConfig: anyFunc(&grpcwebv3.GrpcWeb{}),
},
},
{
Name: wellknown.CORS,
ConfigType: &httpconnectionmanager.HttpFilter_TypedConfig{
-95
View File
@@ -1,95 +0,0 @@
package controlplane
import (
"testing"
"github.com/envoyproxy/go-control-plane/pkg/wellknown"
"github.com/stretchr/testify/assert"
corev1 "k8s.io/api/core/v1"
listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
httpconnectionmanager "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3"
)
func extractHCM(t *testing.T, listener *listener.Listener) *httpconnectionmanager.HttpConnectionManager {
t.Helper()
for _, fc := range listener.FilterChains {
for _, f := range fc.Filters {
if f.Name == wellknown.HTTPConnectionManager {
anyMsg := f.GetTypedConfig()
if anyMsg != nil {
hcm := &httpconnectionmanager.HttpConnectionManager{}
if err := anyMsg.UnmarshalTo(hcm); err != nil {
t.Fatalf("failed to unmarshal HCM: %v", err)
}
return hcm
}
}
}
}
return nil
}
func TestToListenerNoGrpcWeb(t *testing.T) {
tests := []struct {
name string
listenerName string
routeName string
port int32
protocol corev1.Protocol
isFargateMode bool
}{
{
name: "TCP non-fargate",
listenerName: "test_listener",
routeName: "test_route",
port: 8080,
protocol: corev1.ProtocolTCP,
isFargateMode: false,
},
{
name: "TCP fargate",
listenerName: "test_listener_fargate",
routeName: "test_route_fargate",
port: 8081,
protocol: corev1.ProtocolTCP,
isFargateMode: true,
},
{
name: "UDP non-fargate",
listenerName: "test_listener_udp",
routeName: "test_route_udp",
port: 8082,
protocol: corev1.ProtocolUDP,
isFargateMode: false,
},
{
name: "SCTP non-fargate",
listenerName: "test_listener_sctp",
routeName: "test_route_sctp",
port: 8083,
protocol: corev1.ProtocolSCTP,
isFargateMode: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
listener := ToListener(tt.listenerName, tt.routeName, tt.port, tt.protocol, tt.isFargateMode)
hcm := extractHCM(t, listener)
// HTTP connection manager should always be present (filter chain is always created)
assert.NotNil(t, hcm, "HTTP connection manager should be present")
if hcm == nil {
return
}
// Check that grpc-web filter is not present
for _, hf := range hcm.HttpFilters {
assert.NotEqual(t, wellknown.GRPCWeb, hf.Name, "HTTP filters should not contain grpc-web filter")
}
// Ensure only expected filters are present
assert.Len(t, hcm.HttpFilters, 2, "Expected exactly two HTTP filters")
assert.Equal(t, wellknown.CORS, hcm.HttpFilters[0].Name, "First filter should be CORS")
assert.Equal(t, wellknown.Router, hcm.HttpFilters[1].Name, "Second filter should be Router")
})
}
}
@@ -0,0 +1,155 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.30.0
// protoc v5.29.3
// source: envoy/extensions/filters/http/grpc_web/v3/grpc_web.proto
package grpc_webv3
import (
_ "github.com/cncf/xds/go/udpa/annotations"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// gRPC Web filter config.
type GrpcWeb struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
}
func (x *GrpcWeb) Reset() {
*x = GrpcWeb{}
if protoimpl.UnsafeEnabled {
mi := &file_envoy_extensions_filters_http_grpc_web_v3_grpc_web_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *GrpcWeb) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*GrpcWeb) ProtoMessage() {}
func (x *GrpcWeb) ProtoReflect() protoreflect.Message {
mi := &file_envoy_extensions_filters_http_grpc_web_v3_grpc_web_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use GrpcWeb.ProtoReflect.Descriptor instead.
func (*GrpcWeb) Descriptor() ([]byte, []int) {
return file_envoy_extensions_filters_http_grpc_web_v3_grpc_web_proto_rawDescGZIP(), []int{0}
}
var File_envoy_extensions_filters_http_grpc_web_v3_grpc_web_proto protoreflect.FileDescriptor
var file_envoy_extensions_filters_http_grpc_web_v3_grpc_web_proto_rawDesc = []byte{
0x0a, 0x38, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x2f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f,
0x6e, 0x73, 0x2f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x2f, 0x68, 0x74, 0x74, 0x70, 0x2f,
0x67, 0x72, 0x70, 0x63, 0x5f, 0x77, 0x65, 0x62, 0x2f, 0x76, 0x33, 0x2f, 0x67, 0x72, 0x70, 0x63,
0x5f, 0x77, 0x65, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x29, 0x65, 0x6e, 0x76, 0x6f,
0x79, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x66, 0x69, 0x6c,
0x74, 0x65, 0x72, 0x73, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x5f, 0x77,
0x65, 0x62, 0x2e, 0x76, 0x33, 0x1a, 0x1d, 0x75, 0x64, 0x70, 0x61, 0x2f, 0x61, 0x6e, 0x6e, 0x6f,
0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x21, 0x75, 0x64, 0x70, 0x61, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x69, 0x6e,
0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x3e, 0x0a, 0x07, 0x47, 0x72, 0x70, 0x63, 0x57,
0x65, 0x62, 0x3a, 0x33, 0x9a, 0xc5, 0x88, 0x1e, 0x2e, 0x0a, 0x2c, 0x65, 0x6e, 0x76, 0x6f, 0x79,
0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x2e, 0x68,
0x74, 0x74, 0x70, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x5f, 0x77, 0x65, 0x62, 0x2e, 0x76, 0x32, 0x2e,
0x47, 0x72, 0x70, 0x63, 0x57, 0x65, 0x62, 0x42, 0xae, 0x01, 0xba, 0x80, 0xc8, 0xd1, 0x06, 0x02,
0x10, 0x02, 0x0a, 0x37, 0x69, 0x6f, 0x2e, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x70, 0x72, 0x6f, 0x78,
0x79, 0x2e, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f,
0x6e, 0x73, 0x2e, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x2e, 0x68, 0x74, 0x74, 0x70, 0x2e,
0x67, 0x72, 0x70, 0x63, 0x5f, 0x77, 0x65, 0x62, 0x2e, 0x76, 0x33, 0x42, 0x0c, 0x47, 0x72, 0x70,
0x63, 0x57, 0x65, 0x62, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x5b, 0x67, 0x69, 0x74,
0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x70, 0x72, 0x6f,
0x78, 0x79, 0x2f, 0x67, 0x6f, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2d, 0x70, 0x6c,
0x61, 0x6e, 0x65, 0x2f, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x2f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73,
0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x2f, 0x68, 0x74, 0x74,
0x70, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x5f, 0x77, 0x65, 0x62, 0x2f, 0x76, 0x33, 0x3b, 0x67, 0x72,
0x70, 0x63, 0x5f, 0x77, 0x65, 0x62, 0x76, 0x33, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_envoy_extensions_filters_http_grpc_web_v3_grpc_web_proto_rawDescOnce sync.Once
file_envoy_extensions_filters_http_grpc_web_v3_grpc_web_proto_rawDescData = file_envoy_extensions_filters_http_grpc_web_v3_grpc_web_proto_rawDesc
)
func file_envoy_extensions_filters_http_grpc_web_v3_grpc_web_proto_rawDescGZIP() []byte {
file_envoy_extensions_filters_http_grpc_web_v3_grpc_web_proto_rawDescOnce.Do(func() {
file_envoy_extensions_filters_http_grpc_web_v3_grpc_web_proto_rawDescData = protoimpl.X.CompressGZIP(file_envoy_extensions_filters_http_grpc_web_v3_grpc_web_proto_rawDescData)
})
return file_envoy_extensions_filters_http_grpc_web_v3_grpc_web_proto_rawDescData
}
var file_envoy_extensions_filters_http_grpc_web_v3_grpc_web_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_envoy_extensions_filters_http_grpc_web_v3_grpc_web_proto_goTypes = []interface{}{
(*GrpcWeb)(nil), // 0: envoy.extensions.filters.http.grpc_web.v3.GrpcWeb
}
var file_envoy_extensions_filters_http_grpc_web_v3_grpc_web_proto_depIdxs = []int32{
0, // [0:0] is the sub-list for method output_type
0, // [0:0] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_envoy_extensions_filters_http_grpc_web_v3_grpc_web_proto_init() }
func file_envoy_extensions_filters_http_grpc_web_v3_grpc_web_proto_init() {
if File_envoy_extensions_filters_http_grpc_web_v3_grpc_web_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_envoy_extensions_filters_http_grpc_web_v3_grpc_web_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*GrpcWeb); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_envoy_extensions_filters_http_grpc_web_v3_grpc_web_proto_rawDesc,
NumEnums: 0,
NumMessages: 1,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_envoy_extensions_filters_http_grpc_web_v3_grpc_web_proto_goTypes,
DependencyIndexes: file_envoy_extensions_filters_http_grpc_web_v3_grpc_web_proto_depIdxs,
MessageInfos: file_envoy_extensions_filters_http_grpc_web_v3_grpc_web_proto_msgTypes,
}.Build()
File_envoy_extensions_filters_http_grpc_web_v3_grpc_web_proto = out.File
file_envoy_extensions_filters_http_grpc_web_v3_grpc_web_proto_rawDesc = nil
file_envoy_extensions_filters_http_grpc_web_v3_grpc_web_proto_goTypes = nil
file_envoy_extensions_filters_http_grpc_web_v3_grpc_web_proto_depIdxs = nil
}
@@ -0,0 +1,135 @@
//go:build !disable_pgv
// Code generated by protoc-gen-validate. DO NOT EDIT.
// source: envoy/extensions/filters/http/grpc_web/v3/grpc_web.proto
package grpc_webv3
import (
"bytes"
"errors"
"fmt"
"net"
"net/mail"
"net/url"
"regexp"
"sort"
"strings"
"time"
"unicode/utf8"
"google.golang.org/protobuf/types/known/anypb"
)
// ensure the imports are used
var (
_ = bytes.MinRead
_ = errors.New("")
_ = fmt.Print
_ = utf8.UTFMax
_ = (*regexp.Regexp)(nil)
_ = (*strings.Reader)(nil)
_ = net.IPv4len
_ = time.Duration(0)
_ = (*url.URL)(nil)
_ = (*mail.Address)(nil)
_ = anypb.Any{}
_ = sort.Sort
)
// Validate checks the field values on GrpcWeb with the rules defined in the
// proto definition for this message. If any rules are violated, the first
// error encountered is returned, or nil if there are no violations.
func (m *GrpcWeb) Validate() error {
return m.validate(false)
}
// ValidateAll checks the field values on GrpcWeb with the rules defined in the
// proto definition for this message. If any rules are violated, the result is
// a list of violation errors wrapped in GrpcWebMultiError, or nil if none found.
func (m *GrpcWeb) ValidateAll() error {
return m.validate(true)
}
func (m *GrpcWeb) validate(all bool) error {
if m == nil {
return nil
}
var errors []error
if len(errors) > 0 {
return GrpcWebMultiError(errors)
}
return nil
}
// GrpcWebMultiError is an error wrapping multiple validation errors returned
// by GrpcWeb.ValidateAll() if the designated constraints aren't met.
type GrpcWebMultiError []error
// Error returns a concatenation of all the error messages it wraps.
func (m GrpcWebMultiError) Error() string {
var msgs []string
for _, err := range m {
msgs = append(msgs, err.Error())
}
return strings.Join(msgs, "; ")
}
// AllErrors returns a list of validation violation errors.
func (m GrpcWebMultiError) AllErrors() []error { return m }
// GrpcWebValidationError is the validation error returned by GrpcWeb.Validate
// if the designated constraints aren't met.
type GrpcWebValidationError struct {
field string
reason string
cause error
key bool
}
// Field function returns field value.
func (e GrpcWebValidationError) Field() string { return e.field }
// Reason function returns reason value.
func (e GrpcWebValidationError) Reason() string { return e.reason }
// Cause function returns cause value.
func (e GrpcWebValidationError) Cause() error { return e.cause }
// Key function returns key value.
func (e GrpcWebValidationError) Key() bool { return e.key }
// ErrorName returns error name.
func (e GrpcWebValidationError) ErrorName() string { return "GrpcWebValidationError" }
// Error satisfies the builtin error interface
func (e GrpcWebValidationError) Error() string {
cause := ""
if e.cause != nil {
cause = fmt.Sprintf(" | caused by: %v", e.cause)
}
key := ""
if e.key {
key = "key for "
}
return fmt.Sprintf(
"invalid %sGrpcWeb.%s: %s%s",
key,
e.field,
e.reason,
cause)
}
var _ error = GrpcWebValidationError{}
var _ interface {
Field() string
Reason() string
Key() bool
Cause() error
ErrorName() string
} = GrpcWebValidationError{}
@@ -0,0 +1,61 @@
//go:build vtprotobuf
// +build vtprotobuf
// Code generated by protoc-gen-go-vtproto. DO NOT EDIT.
// source: envoy/extensions/filters/http/grpc_web/v3/grpc_web.proto
package grpc_webv3
import (
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
func (m *GrpcWeb) MarshalVTStrict() (dAtA []byte, err error) {
if m == nil {
return nil, nil
}
size := m.SizeVT()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBufferVTStrict(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *GrpcWeb) MarshalToVTStrict(dAtA []byte) (int, error) {
size := m.SizeVT()
return m.MarshalToSizedBufferVTStrict(dAtA[:size])
}
func (m *GrpcWeb) MarshalToSizedBufferVTStrict(dAtA []byte) (int, error) {
if m == nil {
return 0, nil
}
i := len(dAtA)
_ = i
var l int
_ = l
if m.unknownFields != nil {
i -= len(m.unknownFields)
copy(dAtA[i:], m.unknownFields)
}
return len(dAtA) - i, nil
}
func (m *GrpcWeb) SizeVT() (n int) {
if m == nil {
return 0
}
var l int
_ = l
n += len(m.unknownFields)
return n
}
+1
View File
@@ -277,6 +277,7 @@ github.com/envoyproxy/go-control-plane/envoy/config/trace/v3
github.com/envoyproxy/go-control-plane/envoy/data/accesslog/v3
github.com/envoyproxy/go-control-plane/envoy/extensions/access_loggers/file/v3
github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/cors/v3
github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/grpc_web/v3
github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/router/v3
github.com/envoyproxy/go-control-plane/envoy/extensions/filters/listener/http_inspector/v3
github.com/envoyproxy/go-control-plane/envoy/extensions/filters/listener/original_dst/v3