mirror of
https://github.com/agent-substrate/substrate.git
synced 2026-10-02 03:24:42 +08:00
Update google.golang.org/grpc to address vulnerability: GO-2026-6061 (#551)
The weekly govulncheck run on main is failing: [GO-2026-6061](https://pkg.go.dev/vuln/GO-2026-6061) (vulnerabilities in the xDS RBAC engine and the HTTP/2 transport server) in google.golang.org/grpc v1.81.0. Bump grpc to the fixed v1.82.
This commit is contained in:
@@ -48,8 +48,8 @@ require (
|
||||
golang.org/x/sync v0.21.0
|
||||
golang.org/x/sys v0.46.0
|
||||
google.golang.org/api v0.274.0
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260406210006-6f92a3bedf2d
|
||||
google.golang.org/grpc v1.81.0
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478
|
||||
google.golang.org/grpc v1.82.1
|
||||
google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af
|
||||
k8s.io/api v0.36.1
|
||||
k8s.io/apiextensions-apiserver v0.36.1
|
||||
@@ -67,7 +67,7 @@ require (
|
||||
cloud.google.com/go/auth v0.19.0 // indirect
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
|
||||
cloud.google.com/go/longrunning v0.9.0 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.32.0 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.55.0 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.55.0 // indirect
|
||||
github.com/asaskevich/EventBus v0.0.0-20200907212545-49d423059eef // indirect
|
||||
@@ -165,7 +165,7 @@ require (
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
github.com/zeromq/goczmq v4.1.0+incompatible // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.42.0 // indirect
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.10.0 // indirect
|
||||
go.uber.org/atomic v1.11.0 // indirect
|
||||
@@ -180,7 +180,7 @@ require (
|
||||
golang.org/x/time v0.15.0 // indirect
|
||||
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478 // indirect
|
||||
gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gotest.tools/v3 v3.5.2 // indirect
|
||||
|
||||
@@ -26,8 +26,8 @@ cloud.google.com/go/storage v1.62.1 h1:Os0G3XbUbjZumkpDUf2Y0rLoXJTCF1kU2kWUujKYX
|
||||
cloud.google.com/go/storage v1.62.1/go.mod h1:cpYz/kRVZ+UQAF1uHeea10/9ewcRbxGoGNKsS9daSXA=
|
||||
cloud.google.com/go/trace v1.11.7 h1:kDNDX8JkaAG3R2nq1lIdkb7FCSi1rCmsEtKVsty7p+U=
|
||||
cloud.google.com/go/trace v1.11.7/go.mod h1:TNn9d5V3fQVf6s4SCveVMIBS2LJUqo73GACmq/Tky0s=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0 h1:DHa2U07rk8syqvCge0QIGMCE1WxGj9njT44GH7zNJLQ=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0/go.mod h1:P4WPRUkOhJC13W//jWpyfJNDAIpvRbAUIYLX/4jtlE0=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.32.0 h1:rIkQfkCOVKc1OiRCNcSDD8ml5RJlZbH/Xsq7lbpynwc=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.32.0/go.mod h1:RD2SsorTmYhF6HkTmDw7KmPYQk8OBYwTkuasChwv7R4=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.55.0 h1:UnDZ/zFfG1JhH/DqxIZYU/1CUAlTUScoXD/LcM2Ykk8=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.55.0/go.mod h1:IA1C1U7jO/ENqm/vhi7V9YYpBsp+IMyqNrEN94N7tVc=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.55.0 h1:7t/qx5Ost0s0wbA/VDrByOooURhp+ikYwv20i9Y07TQ=
|
||||
@@ -331,8 +331,8 @@ github.com/zeromq/goczmq v4.1.0+incompatible h1:cGVQaU6kIwwrGso0Pgbl84tzAz/h7FJ3
|
||||
github.com/zeromq/goczmq v4.1.0+incompatible/go.mod h1:1uZybAJoSRCvZMH2rZxEwWBSmC4T7CB/xQOfChwPEzg=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.42.0 h1:kpt2PEJuOuqYkPcktfJqWWDjTEd/FNgrxcniL7kQrXQ=
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.42.0/go.mod h1:W9zQ439utxymRrXsUOzZbFX4JhLxXU4+ZnCt8GG7yA8=
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.43.0 h1:62yY3dT7/ShwOxzA0RsKRgshBmfElKI4d/Myu2OxDFU=
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.43.0/go.mod h1:RyaZMFY7yi1kAs45S6mbFGz8O8rqB0dTY14uzvG4LCs=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0 h1:0Qx7VGBacMm9ZENQ7TnNObTYI4ShC+lHI16seduaxZo=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0/go.mod h1:Sje3i3MjSPKTSPvVWCaL8ugBzJwik3u4smCjUeuupqg=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0 h1:7iP2uCb7sGddAr30RRS6xjKy7AZ2JtTOPA3oolgVSw8=
|
||||
@@ -403,12 +403,12 @@ google.golang.org/api v0.274.0 h1:aYhycS5QQCwxHLwfEHRRLf9yNsfvp1JadKKWBE54RFA=
|
||||
google.golang.org/api v0.274.0/go.mod h1:JbAt7mF+XVmWu6xNP8/+CTiGH30ofmCmk9nM8d8fHew=
|
||||
google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7 h1:XzmzkmB14QhVhgnawEVsOn6OFsnpyxNPRY9QV01dNB0=
|
||||
google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7/go.mod h1:L43LFes82YgSonw6iTXTxXUX1OlULt4AQtkik4ULL/I=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 h1:VPWxll4HlMw1Vs/qXtN7BvhZqsS9cdAittCNvVENElA=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:7QBABkRtR8z+TEnmXTqIqwJLlzrZKVfAUm7tY3yGv0M=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260406210006-6f92a3bedf2d h1:wT2n40TBqFY6wiwazVK9/iTWbsQrgk5ZfCSVFLO9LQA=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260406210006-6f92a3bedf2d/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||
google.golang.org/grpc v1.81.0 h1:W3G9N3KQf3BU+YuCtGKJk0CmxQNbAISICD/9AORxLIw=
|
||||
google.golang.org/grpc v1.81.0/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478 h1:yQugLulqltosq0B/f8l4w9VryjV+N/5gcW0jQ3N8Qec=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478/go.mod h1:C6ADNqOxbgdUUeRTU+LCHDPB9ttAMCTff6auwCVa4uc=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478 h1:RmoJA1ujG+/lRGNfUnOMfhCy5EipVMyvUE+KNbPbTlw=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||
google.golang.org/grpc v1.82.1 h1:NnAxzGRA0677vCa4BUkOAnO5+FfQqVl9iUXeD0IqcGE=
|
||||
google.golang.org/grpc v1.82.1/go.mod h1:yzTZ1TB1Z3SG+LIYaI+WiE8D5+PZ3ArnrSp8zF3+/ZA=
|
||||
google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af h1:+5/Sw3GsDNlEmu7TfklWKPdQ0Ykja5VEmq2i817+jbI=
|
||||
google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
Generated
Vendored
+371
@@ -0,0 +1,371 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.36.10
|
||||
// protoc v6.33.2
|
||||
// source: envoy/extensions/filters/http/gcp_authn/v3/gcp_authn.proto
|
||||
|
||||
package gcp_authnv3
|
||||
|
||||
import (
|
||||
_ "github.com/cncf/xds/go/udpa/annotations"
|
||||
_ "github.com/envoyproxy/go-control-plane/envoy/annotations"
|
||||
v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
|
||||
_ "github.com/envoyproxy/protoc-gen-validate/validate"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
durationpb "google.golang.org/protobuf/types/known/durationpb"
|
||||
wrapperspb "google.golang.org/protobuf/types/known/wrapperspb"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
unsafe "unsafe"
|
||||
)
|
||||
|
||||
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)
|
||||
)
|
||||
|
||||
// Filter configuration.
|
||||
// [#next-free-field: 7]
|
||||
type GcpAuthnFilterConfig struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
// The HTTP URI to fetch tokens from GCE Metadata Server(https://cloud.google.com/compute/docs/metadata/overview).
|
||||
// The URL format is "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=[AUDIENCE]"
|
||||
//
|
||||
// This field is deprecated because it does not match the API surface provided by the google auth libraries.
|
||||
// Control planes should not attempt to override the metadata server URI.
|
||||
// The cluster and timeout can be configured using the “cluster“ and “timeout“ fields instead.
|
||||
// For backward compatibility, the cluster and timeout configured in this field will be used
|
||||
// if the new “cluster“ and “timeout“ fields are not set.
|
||||
//
|
||||
// Deprecated: Marked as deprecated in envoy/extensions/filters/http/gcp_authn/v3/gcp_authn.proto.
|
||||
HttpUri *v3.HttpUri `protobuf:"bytes,1,opt,name=http_uri,json=httpUri,proto3" json:"http_uri,omitempty"`
|
||||
// Retry policy for fetching tokens.
|
||||
// Not supported by all data planes.
|
||||
RetryPolicy *v3.RetryPolicy `protobuf:"bytes,2,opt,name=retry_policy,json=retryPolicy,proto3" json:"retry_policy,omitempty"`
|
||||
// Token cache configuration. This field is optional.
|
||||
CacheConfig *TokenCacheConfig `protobuf:"bytes,3,opt,name=cache_config,json=cacheConfig,proto3" json:"cache_config,omitempty"`
|
||||
// Request header location to extract the token. By default (i.e. if this field is not specified), the token
|
||||
// is extracted to the Authorization HTTP header, in the format "Authorization: Bearer <token>".
|
||||
// Not supported by all data planes.
|
||||
TokenHeader *TokenHeader `protobuf:"bytes,4,opt,name=token_header,json=tokenHeader,proto3" json:"token_header,omitempty"`
|
||||
// Cluster to send traffic to the GCE metadata server. Not supported
|
||||
// by all data planes; a data plane may instead have its own mechanism
|
||||
// for contacting the metadata server.
|
||||
Cluster string `protobuf:"bytes,5,opt,name=cluster,proto3" json:"cluster,omitempty"`
|
||||
// Timeout for fetching the tokens from the GCE metadata server.
|
||||
// Not supported by all data planes.
|
||||
Timeout *durationpb.Duration `protobuf:"bytes,6,opt,name=timeout,proto3" json:"timeout,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *GcpAuthnFilterConfig) Reset() {
|
||||
*x = GcpAuthnFilterConfig{}
|
||||
mi := &file_envoy_extensions_filters_http_gcp_authn_v3_gcp_authn_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *GcpAuthnFilterConfig) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*GcpAuthnFilterConfig) ProtoMessage() {}
|
||||
|
||||
func (x *GcpAuthnFilterConfig) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_envoy_extensions_filters_http_gcp_authn_v3_gcp_authn_proto_msgTypes[0]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use GcpAuthnFilterConfig.ProtoReflect.Descriptor instead.
|
||||
func (*GcpAuthnFilterConfig) Descriptor() ([]byte, []int) {
|
||||
return file_envoy_extensions_filters_http_gcp_authn_v3_gcp_authn_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
// Deprecated: Marked as deprecated in envoy/extensions/filters/http/gcp_authn/v3/gcp_authn.proto.
|
||||
func (x *GcpAuthnFilterConfig) GetHttpUri() *v3.HttpUri {
|
||||
if x != nil {
|
||||
return x.HttpUri
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *GcpAuthnFilterConfig) GetRetryPolicy() *v3.RetryPolicy {
|
||||
if x != nil {
|
||||
return x.RetryPolicy
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *GcpAuthnFilterConfig) GetCacheConfig() *TokenCacheConfig {
|
||||
if x != nil {
|
||||
return x.CacheConfig
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *GcpAuthnFilterConfig) GetTokenHeader() *TokenHeader {
|
||||
if x != nil {
|
||||
return x.TokenHeader
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *GcpAuthnFilterConfig) GetCluster() string {
|
||||
if x != nil {
|
||||
return x.Cluster
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *GcpAuthnFilterConfig) GetTimeout() *durationpb.Duration {
|
||||
if x != nil {
|
||||
return x.Timeout
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Audience is the URL of the receiving service that performs token authentication.
|
||||
// It will be provided to the filter through cluster's typed_filter_metadata.
|
||||
type Audience struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Url string `protobuf:"bytes,1,opt,name=url,proto3" json:"url,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *Audience) Reset() {
|
||||
*x = Audience{}
|
||||
mi := &file_envoy_extensions_filters_http_gcp_authn_v3_gcp_authn_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *Audience) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Audience) ProtoMessage() {}
|
||||
|
||||
func (x *Audience) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_envoy_extensions_filters_http_gcp_authn_v3_gcp_authn_proto_msgTypes[1]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Audience.ProtoReflect.Descriptor instead.
|
||||
func (*Audience) Descriptor() ([]byte, []int) {
|
||||
return file_envoy_extensions_filters_http_gcp_authn_v3_gcp_authn_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *Audience) GetUrl() string {
|
||||
if x != nil {
|
||||
return x.Url
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Token Cache configuration.
|
||||
type TokenCacheConfig struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
// The number of cache entries. The maximum number of entries is INT64_MAX as it is constrained by underlying cache implementation.
|
||||
// Default value 0 (i.e., proto3 defaults) disables the cache by default. Other default values will enable the cache.
|
||||
CacheSize *wrapperspb.UInt64Value `protobuf:"bytes,1,opt,name=cache_size,json=cacheSize,proto3" json:"cache_size,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *TokenCacheConfig) Reset() {
|
||||
*x = TokenCacheConfig{}
|
||||
mi := &file_envoy_extensions_filters_http_gcp_authn_v3_gcp_authn_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *TokenCacheConfig) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*TokenCacheConfig) ProtoMessage() {}
|
||||
|
||||
func (x *TokenCacheConfig) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_envoy_extensions_filters_http_gcp_authn_v3_gcp_authn_proto_msgTypes[2]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use TokenCacheConfig.ProtoReflect.Descriptor instead.
|
||||
func (*TokenCacheConfig) Descriptor() ([]byte, []int) {
|
||||
return file_envoy_extensions_filters_http_gcp_authn_v3_gcp_authn_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *TokenCacheConfig) GetCacheSize() *wrapperspb.UInt64Value {
|
||||
if x != nil {
|
||||
return x.CacheSize
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type TokenHeader struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
// The HTTP header's name.
|
||||
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
||||
// The header's prefix. The format is "value_prefix<token>"
|
||||
// For example, for "Authorization: Bearer <token>", value_prefix="Bearer " with a space at the
|
||||
// end.
|
||||
ValuePrefix string `protobuf:"bytes,2,opt,name=value_prefix,json=valuePrefix,proto3" json:"value_prefix,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *TokenHeader) Reset() {
|
||||
*x = TokenHeader{}
|
||||
mi := &file_envoy_extensions_filters_http_gcp_authn_v3_gcp_authn_proto_msgTypes[3]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *TokenHeader) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*TokenHeader) ProtoMessage() {}
|
||||
|
||||
func (x *TokenHeader) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_envoy_extensions_filters_http_gcp_authn_v3_gcp_authn_proto_msgTypes[3]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use TokenHeader.ProtoReflect.Descriptor instead.
|
||||
func (*TokenHeader) Descriptor() ([]byte, []int) {
|
||||
return file_envoy_extensions_filters_http_gcp_authn_v3_gcp_authn_proto_rawDescGZIP(), []int{3}
|
||||
}
|
||||
|
||||
func (x *TokenHeader) GetName() string {
|
||||
if x != nil {
|
||||
return x.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *TokenHeader) GetValuePrefix() string {
|
||||
if x != nil {
|
||||
return x.ValuePrefix
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var File_envoy_extensions_filters_http_gcp_authn_v3_gcp_authn_proto protoreflect.FileDescriptor
|
||||
|
||||
const file_envoy_extensions_filters_http_gcp_authn_v3_gcp_authn_proto_rawDesc = "" +
|
||||
"\n" +
|
||||
":envoy/extensions/filters/http/gcp_authn/v3/gcp_authn.proto\x12*envoy.extensions.filters.http.gcp_authn.v3\x1a\x1fenvoy/config/core/v3/base.proto\x1a#envoy/config/core/v3/http_uri.proto\x1a\x1egoogle/protobuf/duration.proto\x1a\x1egoogle/protobuf/wrappers.proto\x1a#envoy/annotations/deprecation.proto\x1a\x1dudpa/annotations/status.proto\x1a\x17validate/validate.proto\"\xc1\x03\n" +
|
||||
"\x14GcpAuthnFilterConfig\x12E\n" +
|
||||
"\bhttp_uri\x18\x01 \x01(\v2\x1d.envoy.config.core.v3.HttpUriB\v\x92dž\xd8\x04\x033.0\x18\x01R\ahttpUri\x12D\n" +
|
||||
"\fretry_policy\x18\x02 \x01(\v2!.envoy.config.core.v3.RetryPolicyR\vretryPolicy\x12_\n" +
|
||||
"\fcache_config\x18\x03 \x01(\v2<.envoy.extensions.filters.http.gcp_authn.v3.TokenCacheConfigR\vcacheConfig\x12Z\n" +
|
||||
"\ftoken_header\x18\x04 \x01(\v27.envoy.extensions.filters.http.gcp_authn.v3.TokenHeaderR\vtokenHeader\x12\x18\n" +
|
||||
"\acluster\x18\x05 \x01(\tR\acluster\x12E\n" +
|
||||
"\atimeout\x18\x06 \x01(\v2\x19.google.protobuf.DurationB\x10\xfaB\r\xaa\x01\n" +
|
||||
"\x1a\x06\b\x80\x80\x80\x80\x102\x00R\atimeout\"%\n" +
|
||||
"\bAudience\x12\x19\n" +
|
||||
"\x03url\x18\x01 \x01(\tB\a\xfaB\x04r\x02\x10\x01R\x03url\"`\n" +
|
||||
"\x10TokenCacheConfig\x12L\n" +
|
||||
"\n" +
|
||||
"cache_size\x18\x01 \x01(\v2\x1c.google.protobuf.UInt64ValueB\x0f\xfaB\f2\n" +
|
||||
"\x18\xff\xff\xff\xff\xff\xff\xff\xff\x7fR\tcacheSize\"`\n" +
|
||||
"\vTokenHeader\x12!\n" +
|
||||
"\x04name\x18\x01 \x01(\tB\r\xfaB\n" +
|
||||
"r\b\x10\x01\xc8\x01\x00\xc0\x01\x01R\x04name\x12.\n" +
|
||||
"\fvalue_prefix\x18\x02 \x01(\tB\v\xfaB\br\x06\xc8\x01\x00\xc0\x01\x02R\vvaluePrefixB\xb2\x01\xba\x80\xc8\xd1\x06\x02\x10\x02\n" +
|
||||
"8io.envoyproxy.envoy.extensions.filters.http.gcp_authn.v3B\rGcpAuthnProtoP\x01Z]github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/gcp_authn/v3;gcp_authnv3b\x06proto3"
|
||||
|
||||
var (
|
||||
file_envoy_extensions_filters_http_gcp_authn_v3_gcp_authn_proto_rawDescOnce sync.Once
|
||||
file_envoy_extensions_filters_http_gcp_authn_v3_gcp_authn_proto_rawDescData []byte
|
||||
)
|
||||
|
||||
func file_envoy_extensions_filters_http_gcp_authn_v3_gcp_authn_proto_rawDescGZIP() []byte {
|
||||
file_envoy_extensions_filters_http_gcp_authn_v3_gcp_authn_proto_rawDescOnce.Do(func() {
|
||||
file_envoy_extensions_filters_http_gcp_authn_v3_gcp_authn_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_envoy_extensions_filters_http_gcp_authn_v3_gcp_authn_proto_rawDesc), len(file_envoy_extensions_filters_http_gcp_authn_v3_gcp_authn_proto_rawDesc)))
|
||||
})
|
||||
return file_envoy_extensions_filters_http_gcp_authn_v3_gcp_authn_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_envoy_extensions_filters_http_gcp_authn_v3_gcp_authn_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
|
||||
var file_envoy_extensions_filters_http_gcp_authn_v3_gcp_authn_proto_goTypes = []any{
|
||||
(*GcpAuthnFilterConfig)(nil), // 0: envoy.extensions.filters.http.gcp_authn.v3.GcpAuthnFilterConfig
|
||||
(*Audience)(nil), // 1: envoy.extensions.filters.http.gcp_authn.v3.Audience
|
||||
(*TokenCacheConfig)(nil), // 2: envoy.extensions.filters.http.gcp_authn.v3.TokenCacheConfig
|
||||
(*TokenHeader)(nil), // 3: envoy.extensions.filters.http.gcp_authn.v3.TokenHeader
|
||||
(*v3.HttpUri)(nil), // 4: envoy.config.core.v3.HttpUri
|
||||
(*v3.RetryPolicy)(nil), // 5: envoy.config.core.v3.RetryPolicy
|
||||
(*durationpb.Duration)(nil), // 6: google.protobuf.Duration
|
||||
(*wrapperspb.UInt64Value)(nil), // 7: google.protobuf.UInt64Value
|
||||
}
|
||||
var file_envoy_extensions_filters_http_gcp_authn_v3_gcp_authn_proto_depIdxs = []int32{
|
||||
4, // 0: envoy.extensions.filters.http.gcp_authn.v3.GcpAuthnFilterConfig.http_uri:type_name -> envoy.config.core.v3.HttpUri
|
||||
5, // 1: envoy.extensions.filters.http.gcp_authn.v3.GcpAuthnFilterConfig.retry_policy:type_name -> envoy.config.core.v3.RetryPolicy
|
||||
2, // 2: envoy.extensions.filters.http.gcp_authn.v3.GcpAuthnFilterConfig.cache_config:type_name -> envoy.extensions.filters.http.gcp_authn.v3.TokenCacheConfig
|
||||
3, // 3: envoy.extensions.filters.http.gcp_authn.v3.GcpAuthnFilterConfig.token_header:type_name -> envoy.extensions.filters.http.gcp_authn.v3.TokenHeader
|
||||
6, // 4: envoy.extensions.filters.http.gcp_authn.v3.GcpAuthnFilterConfig.timeout:type_name -> google.protobuf.Duration
|
||||
7, // 5: envoy.extensions.filters.http.gcp_authn.v3.TokenCacheConfig.cache_size:type_name -> google.protobuf.UInt64Value
|
||||
6, // [6:6] is the sub-list for method output_type
|
||||
6, // [6:6] is the sub-list for method input_type
|
||||
6, // [6:6] is the sub-list for extension type_name
|
||||
6, // [6:6] is the sub-list for extension extendee
|
||||
0, // [0:6] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_envoy_extensions_filters_http_gcp_authn_v3_gcp_authn_proto_init() }
|
||||
func file_envoy_extensions_filters_http_gcp_authn_v3_gcp_authn_proto_init() {
|
||||
if File_envoy_extensions_filters_http_gcp_authn_v3_gcp_authn_proto != nil {
|
||||
return
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_envoy_extensions_filters_http_gcp_authn_v3_gcp_authn_proto_rawDesc), len(file_envoy_extensions_filters_http_gcp_authn_v3_gcp_authn_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 4,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_envoy_extensions_filters_http_gcp_authn_v3_gcp_authn_proto_goTypes,
|
||||
DependencyIndexes: file_envoy_extensions_filters_http_gcp_authn_v3_gcp_authn_proto_depIdxs,
|
||||
MessageInfos: file_envoy_extensions_filters_http_gcp_authn_v3_gcp_authn_proto_msgTypes,
|
||||
}.Build()
|
||||
File_envoy_extensions_filters_http_gcp_authn_v3_gcp_authn_proto = out.File
|
||||
file_envoy_extensions_filters_http_gcp_authn_v3_gcp_authn_proto_goTypes = nil
|
||||
file_envoy_extensions_filters_http_gcp_authn_v3_gcp_authn_proto_depIdxs = nil
|
||||
}
|
||||
Generated
Vendored
+649
@@ -0,0 +1,649 @@
|
||||
//go:build !disable_pgv
|
||||
// Code generated by protoc-gen-validate. DO NOT EDIT.
|
||||
// source: envoy/extensions/filters/http/gcp_authn/v3/gcp_authn.proto
|
||||
|
||||
package gcp_authnv3
|
||||
|
||||
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 GcpAuthnFilterConfig 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 *GcpAuthnFilterConfig) Validate() error {
|
||||
return m.validate(false)
|
||||
}
|
||||
|
||||
// ValidateAll checks the field values on GcpAuthnFilterConfig 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
|
||||
// GcpAuthnFilterConfigMultiError, or nil if none found.
|
||||
func (m *GcpAuthnFilterConfig) ValidateAll() error {
|
||||
return m.validate(true)
|
||||
}
|
||||
|
||||
func (m *GcpAuthnFilterConfig) validate(all bool) error {
|
||||
if m == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var errors []error
|
||||
|
||||
if all {
|
||||
switch v := interface{}(m.GetHttpUri()).(type) {
|
||||
case interface{ ValidateAll() error }:
|
||||
if err := v.ValidateAll(); err != nil {
|
||||
errors = append(errors, GcpAuthnFilterConfigValidationError{
|
||||
field: "HttpUri",
|
||||
reason: "embedded message failed validation",
|
||||
cause: err,
|
||||
})
|
||||
}
|
||||
case interface{ Validate() error }:
|
||||
if err := v.Validate(); err != nil {
|
||||
errors = append(errors, GcpAuthnFilterConfigValidationError{
|
||||
field: "HttpUri",
|
||||
reason: "embedded message failed validation",
|
||||
cause: err,
|
||||
})
|
||||
}
|
||||
}
|
||||
} else if v, ok := interface{}(m.GetHttpUri()).(interface{ Validate() error }); ok {
|
||||
if err := v.Validate(); err != nil {
|
||||
return GcpAuthnFilterConfigValidationError{
|
||||
field: "HttpUri",
|
||||
reason: "embedded message failed validation",
|
||||
cause: err,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if all {
|
||||
switch v := interface{}(m.GetRetryPolicy()).(type) {
|
||||
case interface{ ValidateAll() error }:
|
||||
if err := v.ValidateAll(); err != nil {
|
||||
errors = append(errors, GcpAuthnFilterConfigValidationError{
|
||||
field: "RetryPolicy",
|
||||
reason: "embedded message failed validation",
|
||||
cause: err,
|
||||
})
|
||||
}
|
||||
case interface{ Validate() error }:
|
||||
if err := v.Validate(); err != nil {
|
||||
errors = append(errors, GcpAuthnFilterConfigValidationError{
|
||||
field: "RetryPolicy",
|
||||
reason: "embedded message failed validation",
|
||||
cause: err,
|
||||
})
|
||||
}
|
||||
}
|
||||
} else if v, ok := interface{}(m.GetRetryPolicy()).(interface{ Validate() error }); ok {
|
||||
if err := v.Validate(); err != nil {
|
||||
return GcpAuthnFilterConfigValidationError{
|
||||
field: "RetryPolicy",
|
||||
reason: "embedded message failed validation",
|
||||
cause: err,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if all {
|
||||
switch v := interface{}(m.GetCacheConfig()).(type) {
|
||||
case interface{ ValidateAll() error }:
|
||||
if err := v.ValidateAll(); err != nil {
|
||||
errors = append(errors, GcpAuthnFilterConfigValidationError{
|
||||
field: "CacheConfig",
|
||||
reason: "embedded message failed validation",
|
||||
cause: err,
|
||||
})
|
||||
}
|
||||
case interface{ Validate() error }:
|
||||
if err := v.Validate(); err != nil {
|
||||
errors = append(errors, GcpAuthnFilterConfigValidationError{
|
||||
field: "CacheConfig",
|
||||
reason: "embedded message failed validation",
|
||||
cause: err,
|
||||
})
|
||||
}
|
||||
}
|
||||
} else if v, ok := interface{}(m.GetCacheConfig()).(interface{ Validate() error }); ok {
|
||||
if err := v.Validate(); err != nil {
|
||||
return GcpAuthnFilterConfigValidationError{
|
||||
field: "CacheConfig",
|
||||
reason: "embedded message failed validation",
|
||||
cause: err,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if all {
|
||||
switch v := interface{}(m.GetTokenHeader()).(type) {
|
||||
case interface{ ValidateAll() error }:
|
||||
if err := v.ValidateAll(); err != nil {
|
||||
errors = append(errors, GcpAuthnFilterConfigValidationError{
|
||||
field: "TokenHeader",
|
||||
reason: "embedded message failed validation",
|
||||
cause: err,
|
||||
})
|
||||
}
|
||||
case interface{ Validate() error }:
|
||||
if err := v.Validate(); err != nil {
|
||||
errors = append(errors, GcpAuthnFilterConfigValidationError{
|
||||
field: "TokenHeader",
|
||||
reason: "embedded message failed validation",
|
||||
cause: err,
|
||||
})
|
||||
}
|
||||
}
|
||||
} else if v, ok := interface{}(m.GetTokenHeader()).(interface{ Validate() error }); ok {
|
||||
if err := v.Validate(); err != nil {
|
||||
return GcpAuthnFilterConfigValidationError{
|
||||
field: "TokenHeader",
|
||||
reason: "embedded message failed validation",
|
||||
cause: err,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// no validation rules for Cluster
|
||||
|
||||
if d := m.GetTimeout(); d != nil {
|
||||
dur, err := d.AsDuration(), d.CheckValid()
|
||||
if err != nil {
|
||||
err = GcpAuthnFilterConfigValidationError{
|
||||
field: "Timeout",
|
||||
reason: "value is not a valid duration",
|
||||
cause: err,
|
||||
}
|
||||
if !all {
|
||||
return err
|
||||
}
|
||||
errors = append(errors, err)
|
||||
} else {
|
||||
|
||||
lt := time.Duration(4294967296*time.Second + 0*time.Nanosecond)
|
||||
gte := time.Duration(0*time.Second + 0*time.Nanosecond)
|
||||
|
||||
if dur < gte || dur >= lt {
|
||||
err := GcpAuthnFilterConfigValidationError{
|
||||
field: "Timeout",
|
||||
reason: "value must be inside range [0s, 1193046h28m16s)",
|
||||
}
|
||||
if !all {
|
||||
return err
|
||||
}
|
||||
errors = append(errors, err)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if len(errors) > 0 {
|
||||
return GcpAuthnFilterConfigMultiError(errors)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GcpAuthnFilterConfigMultiError is an error wrapping multiple validation
|
||||
// errors returned by GcpAuthnFilterConfig.ValidateAll() if the designated
|
||||
// constraints aren't met.
|
||||
type GcpAuthnFilterConfigMultiError []error
|
||||
|
||||
// Error returns a concatenation of all the error messages it wraps.
|
||||
func (m GcpAuthnFilterConfigMultiError) Error() string {
|
||||
msgs := make([]string, 0, len(m))
|
||||
for _, err := range m {
|
||||
msgs = append(msgs, err.Error())
|
||||
}
|
||||
return strings.Join(msgs, "; ")
|
||||
}
|
||||
|
||||
// AllErrors returns a list of validation violation errors.
|
||||
func (m GcpAuthnFilterConfigMultiError) AllErrors() []error { return m }
|
||||
|
||||
// GcpAuthnFilterConfigValidationError is the validation error returned by
|
||||
// GcpAuthnFilterConfig.Validate if the designated constraints aren't met.
|
||||
type GcpAuthnFilterConfigValidationError struct {
|
||||
field string
|
||||
reason string
|
||||
cause error
|
||||
key bool
|
||||
}
|
||||
|
||||
// Field function returns field value.
|
||||
func (e GcpAuthnFilterConfigValidationError) Field() string { return e.field }
|
||||
|
||||
// Reason function returns reason value.
|
||||
func (e GcpAuthnFilterConfigValidationError) Reason() string { return e.reason }
|
||||
|
||||
// Cause function returns cause value.
|
||||
func (e GcpAuthnFilterConfigValidationError) Cause() error { return e.cause }
|
||||
|
||||
// Key function returns key value.
|
||||
func (e GcpAuthnFilterConfigValidationError) Key() bool { return e.key }
|
||||
|
||||
// ErrorName returns error name.
|
||||
func (e GcpAuthnFilterConfigValidationError) ErrorName() string {
|
||||
return "GcpAuthnFilterConfigValidationError"
|
||||
}
|
||||
|
||||
// Error satisfies the builtin error interface
|
||||
func (e GcpAuthnFilterConfigValidationError) 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 %sGcpAuthnFilterConfig.%s: %s%s",
|
||||
key,
|
||||
e.field,
|
||||
e.reason,
|
||||
cause)
|
||||
}
|
||||
|
||||
var _ error = GcpAuthnFilterConfigValidationError{}
|
||||
|
||||
var _ interface {
|
||||
Field() string
|
||||
Reason() string
|
||||
Key() bool
|
||||
Cause() error
|
||||
ErrorName() string
|
||||
} = GcpAuthnFilterConfigValidationError{}
|
||||
|
||||
// Validate checks the field values on Audience 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 *Audience) Validate() error {
|
||||
return m.validate(false)
|
||||
}
|
||||
|
||||
// ValidateAll checks the field values on Audience 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 AudienceMultiError, or nil
|
||||
// if none found.
|
||||
func (m *Audience) ValidateAll() error {
|
||||
return m.validate(true)
|
||||
}
|
||||
|
||||
func (m *Audience) validate(all bool) error {
|
||||
if m == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var errors []error
|
||||
|
||||
if utf8.RuneCountInString(m.GetUrl()) < 1 {
|
||||
err := AudienceValidationError{
|
||||
field: "Url",
|
||||
reason: "value length must be at least 1 runes",
|
||||
}
|
||||
if !all {
|
||||
return err
|
||||
}
|
||||
errors = append(errors, err)
|
||||
}
|
||||
|
||||
if len(errors) > 0 {
|
||||
return AudienceMultiError(errors)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AudienceMultiError is an error wrapping multiple validation errors returned
|
||||
// by Audience.ValidateAll() if the designated constraints aren't met.
|
||||
type AudienceMultiError []error
|
||||
|
||||
// Error returns a concatenation of all the error messages it wraps.
|
||||
func (m AudienceMultiError) Error() string {
|
||||
msgs := make([]string, 0, len(m))
|
||||
for _, err := range m {
|
||||
msgs = append(msgs, err.Error())
|
||||
}
|
||||
return strings.Join(msgs, "; ")
|
||||
}
|
||||
|
||||
// AllErrors returns a list of validation violation errors.
|
||||
func (m AudienceMultiError) AllErrors() []error { return m }
|
||||
|
||||
// AudienceValidationError is the validation error returned by
|
||||
// Audience.Validate if the designated constraints aren't met.
|
||||
type AudienceValidationError struct {
|
||||
field string
|
||||
reason string
|
||||
cause error
|
||||
key bool
|
||||
}
|
||||
|
||||
// Field function returns field value.
|
||||
func (e AudienceValidationError) Field() string { return e.field }
|
||||
|
||||
// Reason function returns reason value.
|
||||
func (e AudienceValidationError) Reason() string { return e.reason }
|
||||
|
||||
// Cause function returns cause value.
|
||||
func (e AudienceValidationError) Cause() error { return e.cause }
|
||||
|
||||
// Key function returns key value.
|
||||
func (e AudienceValidationError) Key() bool { return e.key }
|
||||
|
||||
// ErrorName returns error name.
|
||||
func (e AudienceValidationError) ErrorName() string { return "AudienceValidationError" }
|
||||
|
||||
// Error satisfies the builtin error interface
|
||||
func (e AudienceValidationError) 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 %sAudience.%s: %s%s",
|
||||
key,
|
||||
e.field,
|
||||
e.reason,
|
||||
cause)
|
||||
}
|
||||
|
||||
var _ error = AudienceValidationError{}
|
||||
|
||||
var _ interface {
|
||||
Field() string
|
||||
Reason() string
|
||||
Key() bool
|
||||
Cause() error
|
||||
ErrorName() string
|
||||
} = AudienceValidationError{}
|
||||
|
||||
// Validate checks the field values on TokenCacheConfig 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 *TokenCacheConfig) Validate() error {
|
||||
return m.validate(false)
|
||||
}
|
||||
|
||||
// ValidateAll checks the field values on TokenCacheConfig 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
|
||||
// TokenCacheConfigMultiError, or nil if none found.
|
||||
func (m *TokenCacheConfig) ValidateAll() error {
|
||||
return m.validate(true)
|
||||
}
|
||||
|
||||
func (m *TokenCacheConfig) validate(all bool) error {
|
||||
if m == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var errors []error
|
||||
|
||||
if wrapper := m.GetCacheSize(); wrapper != nil {
|
||||
|
||||
if wrapper.GetValue() > 9223372036854775807 {
|
||||
err := TokenCacheConfigValidationError{
|
||||
field: "CacheSize",
|
||||
reason: "value must be less than or equal to 9223372036854775807",
|
||||
}
|
||||
if !all {
|
||||
return err
|
||||
}
|
||||
errors = append(errors, err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if len(errors) > 0 {
|
||||
return TokenCacheConfigMultiError(errors)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// TokenCacheConfigMultiError is an error wrapping multiple validation errors
|
||||
// returned by TokenCacheConfig.ValidateAll() if the designated constraints
|
||||
// aren't met.
|
||||
type TokenCacheConfigMultiError []error
|
||||
|
||||
// Error returns a concatenation of all the error messages it wraps.
|
||||
func (m TokenCacheConfigMultiError) Error() string {
|
||||
msgs := make([]string, 0, len(m))
|
||||
for _, err := range m {
|
||||
msgs = append(msgs, err.Error())
|
||||
}
|
||||
return strings.Join(msgs, "; ")
|
||||
}
|
||||
|
||||
// AllErrors returns a list of validation violation errors.
|
||||
func (m TokenCacheConfigMultiError) AllErrors() []error { return m }
|
||||
|
||||
// TokenCacheConfigValidationError is the validation error returned by
|
||||
// TokenCacheConfig.Validate if the designated constraints aren't met.
|
||||
type TokenCacheConfigValidationError struct {
|
||||
field string
|
||||
reason string
|
||||
cause error
|
||||
key bool
|
||||
}
|
||||
|
||||
// Field function returns field value.
|
||||
func (e TokenCacheConfigValidationError) Field() string { return e.field }
|
||||
|
||||
// Reason function returns reason value.
|
||||
func (e TokenCacheConfigValidationError) Reason() string { return e.reason }
|
||||
|
||||
// Cause function returns cause value.
|
||||
func (e TokenCacheConfigValidationError) Cause() error { return e.cause }
|
||||
|
||||
// Key function returns key value.
|
||||
func (e TokenCacheConfigValidationError) Key() bool { return e.key }
|
||||
|
||||
// ErrorName returns error name.
|
||||
func (e TokenCacheConfigValidationError) ErrorName() string { return "TokenCacheConfigValidationError" }
|
||||
|
||||
// Error satisfies the builtin error interface
|
||||
func (e TokenCacheConfigValidationError) 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 %sTokenCacheConfig.%s: %s%s",
|
||||
key,
|
||||
e.field,
|
||||
e.reason,
|
||||
cause)
|
||||
}
|
||||
|
||||
var _ error = TokenCacheConfigValidationError{}
|
||||
|
||||
var _ interface {
|
||||
Field() string
|
||||
Reason() string
|
||||
Key() bool
|
||||
Cause() error
|
||||
ErrorName() string
|
||||
} = TokenCacheConfigValidationError{}
|
||||
|
||||
// Validate checks the field values on TokenHeader 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 *TokenHeader) Validate() error {
|
||||
return m.validate(false)
|
||||
}
|
||||
|
||||
// ValidateAll checks the field values on TokenHeader 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 TokenHeaderMultiError, or
|
||||
// nil if none found.
|
||||
func (m *TokenHeader) ValidateAll() error {
|
||||
return m.validate(true)
|
||||
}
|
||||
|
||||
func (m *TokenHeader) validate(all bool) error {
|
||||
if m == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var errors []error
|
||||
|
||||
if utf8.RuneCountInString(m.GetName()) < 1 {
|
||||
err := TokenHeaderValidationError{
|
||||
field: "Name",
|
||||
reason: "value length must be at least 1 runes",
|
||||
}
|
||||
if !all {
|
||||
return err
|
||||
}
|
||||
errors = append(errors, err)
|
||||
}
|
||||
|
||||
if !_TokenHeader_Name_Pattern.MatchString(m.GetName()) {
|
||||
err := TokenHeaderValidationError{
|
||||
field: "Name",
|
||||
reason: "value does not match regex pattern \"^[^\\x00\\n\\r]*$\"",
|
||||
}
|
||||
if !all {
|
||||
return err
|
||||
}
|
||||
errors = append(errors, err)
|
||||
}
|
||||
|
||||
if !_TokenHeader_ValuePrefix_Pattern.MatchString(m.GetValuePrefix()) {
|
||||
err := TokenHeaderValidationError{
|
||||
field: "ValuePrefix",
|
||||
reason: "value does not match regex pattern \"^[^\\x00\\n\\r]*$\"",
|
||||
}
|
||||
if !all {
|
||||
return err
|
||||
}
|
||||
errors = append(errors, err)
|
||||
}
|
||||
|
||||
if len(errors) > 0 {
|
||||
return TokenHeaderMultiError(errors)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// TokenHeaderMultiError is an error wrapping multiple validation errors
|
||||
// returned by TokenHeader.ValidateAll() if the designated constraints aren't met.
|
||||
type TokenHeaderMultiError []error
|
||||
|
||||
// Error returns a concatenation of all the error messages it wraps.
|
||||
func (m TokenHeaderMultiError) Error() string {
|
||||
msgs := make([]string, 0, len(m))
|
||||
for _, err := range m {
|
||||
msgs = append(msgs, err.Error())
|
||||
}
|
||||
return strings.Join(msgs, "; ")
|
||||
}
|
||||
|
||||
// AllErrors returns a list of validation violation errors.
|
||||
func (m TokenHeaderMultiError) AllErrors() []error { return m }
|
||||
|
||||
// TokenHeaderValidationError is the validation error returned by
|
||||
// TokenHeader.Validate if the designated constraints aren't met.
|
||||
type TokenHeaderValidationError struct {
|
||||
field string
|
||||
reason string
|
||||
cause error
|
||||
key bool
|
||||
}
|
||||
|
||||
// Field function returns field value.
|
||||
func (e TokenHeaderValidationError) Field() string { return e.field }
|
||||
|
||||
// Reason function returns reason value.
|
||||
func (e TokenHeaderValidationError) Reason() string { return e.reason }
|
||||
|
||||
// Cause function returns cause value.
|
||||
func (e TokenHeaderValidationError) Cause() error { return e.cause }
|
||||
|
||||
// Key function returns key value.
|
||||
func (e TokenHeaderValidationError) Key() bool { return e.key }
|
||||
|
||||
// ErrorName returns error name.
|
||||
func (e TokenHeaderValidationError) ErrorName() string { return "TokenHeaderValidationError" }
|
||||
|
||||
// Error satisfies the builtin error interface
|
||||
func (e TokenHeaderValidationError) 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 %sTokenHeader.%s: %s%s",
|
||||
key,
|
||||
e.field,
|
||||
e.reason,
|
||||
cause)
|
||||
}
|
||||
|
||||
var _ error = TokenHeaderValidationError{}
|
||||
|
||||
var _ interface {
|
||||
Field() string
|
||||
Reason() string
|
||||
Key() bool
|
||||
Cause() error
|
||||
ErrorName() string
|
||||
} = TokenHeaderValidationError{}
|
||||
|
||||
var _TokenHeader_Name_Pattern = regexp.MustCompile("^[^\x00\n\r]*$")
|
||||
|
||||
var _TokenHeader_ValuePrefix_Pattern = regexp.MustCompile("^[^\x00\n\r]*$")
|
||||
Generated
Vendored
+358
@@ -0,0 +1,358 @@
|
||||
//go:build vtprotobuf
|
||||
// +build vtprotobuf
|
||||
|
||||
// Code generated by protoc-gen-go-vtproto. DO NOT EDIT.
|
||||
// source: envoy/extensions/filters/http/gcp_authn/v3/gcp_authn.proto
|
||||
|
||||
package gcp_authnv3
|
||||
|
||||
import (
|
||||
protohelpers "github.com/planetscale/vtprotobuf/protohelpers"
|
||||
durationpb "github.com/planetscale/vtprotobuf/types/known/durationpb"
|
||||
wrapperspb "github.com/planetscale/vtprotobuf/types/known/wrapperspb"
|
||||
proto "google.golang.org/protobuf/proto"
|
||||
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 *GcpAuthnFilterConfig) 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 *GcpAuthnFilterConfig) MarshalToVTStrict(dAtA []byte) (int, error) {
|
||||
size := m.SizeVT()
|
||||
return m.MarshalToSizedBufferVTStrict(dAtA[:size])
|
||||
}
|
||||
|
||||
func (m *GcpAuthnFilterConfig) 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)
|
||||
}
|
||||
if m.Timeout != nil {
|
||||
size, err := (*durationpb.Duration)(m.Timeout).MarshalToSizedBufferVTStrict(dAtA[:i])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
i -= size
|
||||
i = protohelpers.EncodeVarint(dAtA, i, uint64(size))
|
||||
i--
|
||||
dAtA[i] = 0x32
|
||||
}
|
||||
if len(m.Cluster) > 0 {
|
||||
i -= len(m.Cluster)
|
||||
copy(dAtA[i:], m.Cluster)
|
||||
i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Cluster)))
|
||||
i--
|
||||
dAtA[i] = 0x2a
|
||||
}
|
||||
if m.TokenHeader != nil {
|
||||
size, err := m.TokenHeader.MarshalToSizedBufferVTStrict(dAtA[:i])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
i -= size
|
||||
i = protohelpers.EncodeVarint(dAtA, i, uint64(size))
|
||||
i--
|
||||
dAtA[i] = 0x22
|
||||
}
|
||||
if m.CacheConfig != nil {
|
||||
size, err := m.CacheConfig.MarshalToSizedBufferVTStrict(dAtA[:i])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
i -= size
|
||||
i = protohelpers.EncodeVarint(dAtA, i, uint64(size))
|
||||
i--
|
||||
dAtA[i] = 0x1a
|
||||
}
|
||||
if m.RetryPolicy != nil {
|
||||
if vtmsg, ok := interface{}(m.RetryPolicy).(interface {
|
||||
MarshalToSizedBufferVTStrict([]byte) (int, error)
|
||||
}); ok {
|
||||
size, err := vtmsg.MarshalToSizedBufferVTStrict(dAtA[:i])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
i -= size
|
||||
i = protohelpers.EncodeVarint(dAtA, i, uint64(size))
|
||||
} else {
|
||||
encoded, err := proto.Marshal(m.RetryPolicy)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
i -= len(encoded)
|
||||
copy(dAtA[i:], encoded)
|
||||
i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded)))
|
||||
}
|
||||
i--
|
||||
dAtA[i] = 0x12
|
||||
}
|
||||
if m.HttpUri != nil {
|
||||
if vtmsg, ok := interface{}(m.HttpUri).(interface {
|
||||
MarshalToSizedBufferVTStrict([]byte) (int, error)
|
||||
}); ok {
|
||||
size, err := vtmsg.MarshalToSizedBufferVTStrict(dAtA[:i])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
i -= size
|
||||
i = protohelpers.EncodeVarint(dAtA, i, uint64(size))
|
||||
} else {
|
||||
encoded, err := proto.Marshal(m.HttpUri)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
i -= len(encoded)
|
||||
copy(dAtA[i:], encoded)
|
||||
i = protohelpers.EncodeVarint(dAtA, i, uint64(len(encoded)))
|
||||
}
|
||||
i--
|
||||
dAtA[i] = 0xa
|
||||
}
|
||||
return len(dAtA) - i, nil
|
||||
}
|
||||
|
||||
func (m *Audience) 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 *Audience) MarshalToVTStrict(dAtA []byte) (int, error) {
|
||||
size := m.SizeVT()
|
||||
return m.MarshalToSizedBufferVTStrict(dAtA[:size])
|
||||
}
|
||||
|
||||
func (m *Audience) 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)
|
||||
}
|
||||
if len(m.Url) > 0 {
|
||||
i -= len(m.Url)
|
||||
copy(dAtA[i:], m.Url)
|
||||
i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Url)))
|
||||
i--
|
||||
dAtA[i] = 0xa
|
||||
}
|
||||
return len(dAtA) - i, nil
|
||||
}
|
||||
|
||||
func (m *TokenCacheConfig) 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 *TokenCacheConfig) MarshalToVTStrict(dAtA []byte) (int, error) {
|
||||
size := m.SizeVT()
|
||||
return m.MarshalToSizedBufferVTStrict(dAtA[:size])
|
||||
}
|
||||
|
||||
func (m *TokenCacheConfig) 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)
|
||||
}
|
||||
if m.CacheSize != nil {
|
||||
size, err := (*wrapperspb.UInt64Value)(m.CacheSize).MarshalToSizedBufferVTStrict(dAtA[:i])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
i -= size
|
||||
i = protohelpers.EncodeVarint(dAtA, i, uint64(size))
|
||||
i--
|
||||
dAtA[i] = 0xa
|
||||
}
|
||||
return len(dAtA) - i, nil
|
||||
}
|
||||
|
||||
func (m *TokenHeader) 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 *TokenHeader) MarshalToVTStrict(dAtA []byte) (int, error) {
|
||||
size := m.SizeVT()
|
||||
return m.MarshalToSizedBufferVTStrict(dAtA[:size])
|
||||
}
|
||||
|
||||
func (m *TokenHeader) 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)
|
||||
}
|
||||
if len(m.ValuePrefix) > 0 {
|
||||
i -= len(m.ValuePrefix)
|
||||
copy(dAtA[i:], m.ValuePrefix)
|
||||
i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.ValuePrefix)))
|
||||
i--
|
||||
dAtA[i] = 0x12
|
||||
}
|
||||
if len(m.Name) > 0 {
|
||||
i -= len(m.Name)
|
||||
copy(dAtA[i:], m.Name)
|
||||
i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.Name)))
|
||||
i--
|
||||
dAtA[i] = 0xa
|
||||
}
|
||||
return len(dAtA) - i, nil
|
||||
}
|
||||
|
||||
func (m *GcpAuthnFilterConfig) SizeVT() (n int) {
|
||||
if m == nil {
|
||||
return 0
|
||||
}
|
||||
var l int
|
||||
_ = l
|
||||
if m.HttpUri != nil {
|
||||
if size, ok := interface{}(m.HttpUri).(interface {
|
||||
SizeVT() int
|
||||
}); ok {
|
||||
l = size.SizeVT()
|
||||
} else {
|
||||
l = proto.Size(m.HttpUri)
|
||||
}
|
||||
n += 1 + l + protohelpers.SizeOfVarint(uint64(l))
|
||||
}
|
||||
if m.RetryPolicy != nil {
|
||||
if size, ok := interface{}(m.RetryPolicy).(interface {
|
||||
SizeVT() int
|
||||
}); ok {
|
||||
l = size.SizeVT()
|
||||
} else {
|
||||
l = proto.Size(m.RetryPolicy)
|
||||
}
|
||||
n += 1 + l + protohelpers.SizeOfVarint(uint64(l))
|
||||
}
|
||||
if m.CacheConfig != nil {
|
||||
l = m.CacheConfig.SizeVT()
|
||||
n += 1 + l + protohelpers.SizeOfVarint(uint64(l))
|
||||
}
|
||||
if m.TokenHeader != nil {
|
||||
l = m.TokenHeader.SizeVT()
|
||||
n += 1 + l + protohelpers.SizeOfVarint(uint64(l))
|
||||
}
|
||||
l = len(m.Cluster)
|
||||
if l > 0 {
|
||||
n += 1 + l + protohelpers.SizeOfVarint(uint64(l))
|
||||
}
|
||||
if m.Timeout != nil {
|
||||
l = (*durationpb.Duration)(m.Timeout).SizeVT()
|
||||
n += 1 + l + protohelpers.SizeOfVarint(uint64(l))
|
||||
}
|
||||
n += len(m.unknownFields)
|
||||
return n
|
||||
}
|
||||
|
||||
func (m *Audience) SizeVT() (n int) {
|
||||
if m == nil {
|
||||
return 0
|
||||
}
|
||||
var l int
|
||||
_ = l
|
||||
l = len(m.Url)
|
||||
if l > 0 {
|
||||
n += 1 + l + protohelpers.SizeOfVarint(uint64(l))
|
||||
}
|
||||
n += len(m.unknownFields)
|
||||
return n
|
||||
}
|
||||
|
||||
func (m *TokenCacheConfig) SizeVT() (n int) {
|
||||
if m == nil {
|
||||
return 0
|
||||
}
|
||||
var l int
|
||||
_ = l
|
||||
if m.CacheSize != nil {
|
||||
l = (*wrapperspb.UInt64Value)(m.CacheSize).SizeVT()
|
||||
n += 1 + l + protohelpers.SizeOfVarint(uint64(l))
|
||||
}
|
||||
n += len(m.unknownFields)
|
||||
return n
|
||||
}
|
||||
|
||||
func (m *TokenHeader) SizeVT() (n int) {
|
||||
if m == nil {
|
||||
return 0
|
||||
}
|
||||
var l int
|
||||
_ = l
|
||||
l = len(m.Name)
|
||||
if l > 0 {
|
||||
n += 1 + l + protohelpers.SizeOfVarint(uint64(l))
|
||||
}
|
||||
l = len(m.ValuePrefix)
|
||||
if l > 0 {
|
||||
n += 1 + l + protohelpers.SizeOfVarint(uint64(l))
|
||||
}
|
||||
n += len(m.unknownFields)
|
||||
return n
|
||||
}
|
||||
+1
-1
@@ -5,7 +5,7 @@ package gcp // import "go.opentelemetry.io/contrib/detectors/gcp"
|
||||
|
||||
// Version is the current release version of the GCP resource detector.
|
||||
func Version() string {
|
||||
return "1.42.0"
|
||||
return "1.43.0"
|
||||
// This string is updated by the pre_release.sh script during release
|
||||
}
|
||||
|
||||
|
||||
+4
@@ -238,6 +238,8 @@ type CommonLanguageSettings struct {
|
||||
// The destination where API teams want this client library to be published.
|
||||
Destinations []ClientLibraryDestination `protobuf:"varint,2,rep,packed,name=destinations,proto3,enum=google.api.ClientLibraryDestination" json:"destinations,omitempty"`
|
||||
// Configuration for which RPCs should be generated in the GAPIC client.
|
||||
//
|
||||
// Note: This field should not be used in most cases.
|
||||
SelectiveGapicGeneration *SelectiveGapicGeneration `protobuf:"bytes,3,opt,name=selective_gapic_generation,json=selectiveGapicGeneration,proto3" json:"selective_gapic_generation,omitempty"`
|
||||
}
|
||||
|
||||
@@ -1249,6 +1251,8 @@ func (x *MethodSettings) GetBatching() *BatchingConfigProto {
|
||||
|
||||
// This message is used to configure the generation of a subset of the RPCs in
|
||||
// a service for client libraries.
|
||||
//
|
||||
// Note: This feature should not be used in most cases.
|
||||
type SelectiveGapicGeneration struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
|
||||
+2
-2
@@ -60,7 +60,7 @@ func Register(b Builder) {
|
||||
if !envconfig.CaseSensitiveBalancerRegistries {
|
||||
name = strings.ToLower(name)
|
||||
if name != b.Name() {
|
||||
logger.Warningf("Balancer registered with name %q. grpc-go will be switching to case sensitive balancer registries soon. After 2 releases, we will enable the env var by default.", b.Name())
|
||||
logger.Warningf("Balancer registered with name %q. grpc-go has switched to case sensitive balancer registries. GRPC_GO_EXPERIMENTAL_CASE_SENSITIVE_BALANCER_REGISTRIES env variable will be removed in release v1.82.0", b.Name())
|
||||
}
|
||||
}
|
||||
m[name] = b
|
||||
@@ -85,7 +85,7 @@ func Get(name string) Builder {
|
||||
if !envconfig.CaseSensitiveBalancerRegistries {
|
||||
lowerName := strings.ToLower(name)
|
||||
if lowerName != name {
|
||||
logger.Warningf("Balancer retrieved for name %q. grpc-go will be switching to case sensitive balancer registries soon. After 2 releases, we will enable the env var by default.", name)
|
||||
logger.Warningf("Balancer retrieved for name %q. grpc-go has switched to case sensitive balancer registries. GRPC_GO_EXPERIMENTAL_CASE_SENSITIVE_BALANCER_REGISTRIES env variable will be removed in release v1.82.0", name)
|
||||
}
|
||||
name = lowerName
|
||||
}
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -19,7 +19,7 @@
|
||||
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.6.1
|
||||
// - protoc-gen-go-grpc v1.6.2
|
||||
// - protoc v5.27.1
|
||||
// source: grpc/lb/v1/load_balancer.proto
|
||||
|
||||
|
||||
+1
-1
@@ -35,9 +35,9 @@ import (
|
||||
"google.golang.org/grpc/balancer"
|
||||
"google.golang.org/grpc/balancer/pickfirst/internal"
|
||||
"google.golang.org/grpc/connectivity"
|
||||
"google.golang.org/grpc/experimental/balancer/weight"
|
||||
expstats "google.golang.org/grpc/experimental/stats"
|
||||
"google.golang.org/grpc/grpclog"
|
||||
"google.golang.org/grpc/internal/balancer/weight"
|
||||
"google.golang.org/grpc/internal/envconfig"
|
||||
internalgrpclog "google.golang.org/grpc/internal/grpclog"
|
||||
"google.golang.org/grpc/internal/pretty"
|
||||
|
||||
+1
-1
@@ -42,7 +42,7 @@ import (
|
||||
"google.golang.org/grpc/balancer/lazy"
|
||||
"google.golang.org/grpc/balancer/pickfirst"
|
||||
"google.golang.org/grpc/connectivity"
|
||||
"google.golang.org/grpc/internal/balancer/weight"
|
||||
"google.golang.org/grpc/experimental/balancer/weight"
|
||||
"google.golang.org/grpc/internal/grpclog"
|
||||
"google.golang.org/grpc/internal/pretty"
|
||||
iringhash "google.golang.org/grpc/internal/ringhash"
|
||||
|
||||
+48
-76
@@ -29,7 +29,6 @@ import (
|
||||
"google.golang.org/grpc/connectivity"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
"google.golang.org/grpc/internal"
|
||||
"google.golang.org/grpc/internal/buffer"
|
||||
internalgrpclog "google.golang.org/grpc/internal/grpclog"
|
||||
"google.golang.org/grpc/internal/grpcsync"
|
||||
"google.golang.org/grpc/internal/pretty"
|
||||
@@ -39,6 +38,12 @@ import (
|
||||
|
||||
var newAdaptiveThrottler = func() adaptiveThrottler { return adaptive.New() }
|
||||
|
||||
// newConnectivityStateSubscriber is a variable that can be overridden in tests
|
||||
// to wrap the connectivity state subscriber for testing purposes.
|
||||
var newConnectivityStateSubscriber = func(sub grpcsync.Subscriber) grpcsync.Subscriber {
|
||||
return sub
|
||||
}
|
||||
|
||||
type adaptiveThrottler interface {
|
||||
ShouldThrottle() bool
|
||||
RegisterBackendResponse(throttled bool)
|
||||
@@ -57,12 +62,11 @@ type controlChannel struct {
|
||||
// hammering the RLS service while it is overloaded or down.
|
||||
throttler adaptiveThrottler
|
||||
|
||||
cc *grpc.ClientConn
|
||||
client rlsgrpc.RouteLookupServiceClient
|
||||
logger *internalgrpclog.PrefixLogger
|
||||
connectivityStateCh *buffer.Unbounded
|
||||
unsubscribe func()
|
||||
monitorDoneCh chan struct{}
|
||||
cc *grpc.ClientConn
|
||||
client rlsgrpc.RouteLookupServiceClient
|
||||
logger *internalgrpclog.PrefixLogger
|
||||
dropConnStateSubscriber func()
|
||||
seenTransientFailure bool
|
||||
}
|
||||
|
||||
// newControlChannel creates a controlChannel to rlsServerName and uses
|
||||
@@ -70,11 +74,9 @@ type controlChannel struct {
|
||||
// gRPC channel.
|
||||
func newControlChannel(rlsServerName, serviceConfig string, rpcTimeout time.Duration, bOpts balancer.BuildOptions, backToReadyFunc func()) (*controlChannel, error) {
|
||||
ctrlCh := &controlChannel{
|
||||
rpcTimeout: rpcTimeout,
|
||||
backToReadyFunc: backToReadyFunc,
|
||||
throttler: newAdaptiveThrottler(),
|
||||
connectivityStateCh: buffer.NewUnbounded(),
|
||||
monitorDoneCh: make(chan struct{}),
|
||||
rpcTimeout: rpcTimeout,
|
||||
backToReadyFunc: backToReadyFunc,
|
||||
throttler: newAdaptiveThrottler(),
|
||||
}
|
||||
ctrlCh.logger = internalgrpclog.NewPrefixLogger(logger, fmt.Sprintf("[rls-control-channel %p] ", ctrlCh))
|
||||
|
||||
@@ -88,11 +90,11 @@ func newControlChannel(rlsServerName, serviceConfig string, rpcTimeout time.Dura
|
||||
}
|
||||
// Subscribe to connectivity state before connecting to avoid missing initial
|
||||
// updates, which are only delivered to active subscribers.
|
||||
ctrlCh.unsubscribe = internal.SubscribeToConnectivityStateChanges.(func(cc *grpc.ClientConn, s grpcsync.Subscriber) func())(ctrlCh.cc, ctrlCh)
|
||||
subscribe := internal.SubscribeToConnectivityStateChanges.(func(cc *grpc.ClientConn, s grpcsync.Subscriber) func())
|
||||
ctrlCh.dropConnStateSubscriber = subscribe(ctrlCh.cc, newConnectivityStateSubscriber(ctrlCh))
|
||||
ctrlCh.cc.Connect()
|
||||
ctrlCh.client = rlsgrpc.NewRouteLookupServiceClient(ctrlCh.cc)
|
||||
ctrlCh.logger.Infof("Control channel created to RLS server at: %v", rlsServerName)
|
||||
go ctrlCh.monitorConnectivityState()
|
||||
return ctrlCh, nil
|
||||
}
|
||||
|
||||
@@ -101,7 +103,37 @@ func (cc *controlChannel) OnMessage(msg any) {
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("Unexpected message type %T , wanted connectectivity.State type", msg))
|
||||
}
|
||||
cc.connectivityStateCh.Put(st)
|
||||
|
||||
switch st {
|
||||
case connectivity.Ready:
|
||||
// Only reset backoff when transitioning from TRANSIENT_FAILURE to READY.
|
||||
// This indicates the RLS server has recovered from being unreachable, so
|
||||
// we reset backoff state in all cache entries to allow pending RPCs to
|
||||
// proceed immediately. We skip benign transitions like READY → IDLE → READY
|
||||
// since those don't represent actual failures.
|
||||
if cc.seenTransientFailure {
|
||||
if cc.logger.V(2) {
|
||||
cc.logger.Infof("Control channel back to READY after TRANSIENT_FAILURE")
|
||||
}
|
||||
cc.seenTransientFailure = false
|
||||
if cc.backToReadyFunc != nil {
|
||||
cc.backToReadyFunc()
|
||||
}
|
||||
} else {
|
||||
if cc.logger.V(2) {
|
||||
cc.logger.Infof("Control channel is READY")
|
||||
}
|
||||
}
|
||||
case connectivity.TransientFailure:
|
||||
// Track that we've entered TRANSIENT_FAILURE state so we know to reset
|
||||
// backoffs when we recover to READY.
|
||||
cc.logger.Warningf("Control channel is TRANSIENT_FAILURE")
|
||||
cc.seenTransientFailure = true
|
||||
default:
|
||||
if cc.logger.V(2) {
|
||||
cc.logger.Infof("Control channel connectivity state is %s", st)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// dialOpts constructs the dial options for the control plane channel.
|
||||
@@ -148,68 +180,8 @@ func (cc *controlChannel) dialOpts(bOpts balancer.BuildOptions, serviceConfig st
|
||||
return dopts, nil
|
||||
}
|
||||
|
||||
func (cc *controlChannel) monitorConnectivityState() {
|
||||
cc.logger.Infof("Starting connectivity state monitoring goroutine")
|
||||
defer close(cc.monitorDoneCh)
|
||||
|
||||
// Since we use two mechanisms to deal with RLS server being down:
|
||||
// - adaptive throttling for the channel as a whole
|
||||
// - exponential backoff on a per-request basis
|
||||
// we need a way to avoid double-penalizing requests by counting failures
|
||||
// toward both mechanisms when the RLS server is unreachable.
|
||||
//
|
||||
// To accomplish this, we monitor the state of the control plane channel. If
|
||||
// the state has been TRANSIENT_FAILURE since the last time it was in state
|
||||
// READY, and it then transitions into state READY, we push on a channel
|
||||
// which is being read by the LB policy.
|
||||
//
|
||||
// The LB the policy will iterate through the cache to reset the backoff
|
||||
// timeouts in all cache entries. Specifically, this means that it will
|
||||
// reset the backoff state and cancel the pending backoff timer. Note that
|
||||
// when cancelling the backoff timer, just like when the backoff timer fires
|
||||
// normally, a new picker is returned to the channel, to force it to
|
||||
// re-process any wait-for-ready RPCs that may still be queued if we failed
|
||||
// them while we were in backoff. However, we should optimize this case by
|
||||
// returning only one new picker, regardless of how many backoff timers are
|
||||
// cancelled.
|
||||
|
||||
// Wait for the control channel to become READY for the first time.
|
||||
for s, ok := <-cc.connectivityStateCh.Get(); s != connectivity.Ready; s, ok = <-cc.connectivityStateCh.Get() {
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
cc.connectivityStateCh.Load()
|
||||
if s == connectivity.Shutdown {
|
||||
return
|
||||
}
|
||||
}
|
||||
cc.connectivityStateCh.Load()
|
||||
cc.logger.Infof("Connectivity state is READY")
|
||||
|
||||
for {
|
||||
s, ok := <-cc.connectivityStateCh.Get()
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
cc.connectivityStateCh.Load()
|
||||
|
||||
if s == connectivity.Shutdown {
|
||||
return
|
||||
}
|
||||
if s == connectivity.Ready {
|
||||
cc.logger.Infof("Control channel back to READY")
|
||||
cc.backToReadyFunc()
|
||||
}
|
||||
|
||||
cc.logger.Infof("Connectivity state is %s", s)
|
||||
}
|
||||
}
|
||||
|
||||
func (cc *controlChannel) close() {
|
||||
cc.unsubscribe()
|
||||
cc.connectivityStateCh.Close()
|
||||
<-cc.monitorDoneCh
|
||||
cc.dropConnStateSubscriber()
|
||||
cc.cc.Close()
|
||||
cc.logger.Infof("Shutdown")
|
||||
}
|
||||
|
||||
+5
-18
@@ -181,16 +181,9 @@ func (g *altsTC) ClientHandshake(ctx context.Context, addr string, rawConn net.C
|
||||
}
|
||||
// Do not close hsConn since it is shared with other handshakes.
|
||||
|
||||
// Possible context leak:
|
||||
// The cancel function for the child context we create will only be
|
||||
// called a non-nil error is returned.
|
||||
var cancel context.CancelFunc
|
||||
ctx, cancel = context.WithCancel(ctx)
|
||||
defer func() {
|
||||
if err != nil {
|
||||
cancel()
|
||||
}
|
||||
}()
|
||||
defer cancel()
|
||||
|
||||
opts := handshaker.DefaultClientHandshakerOptions()
|
||||
opts.TargetName = addr
|
||||
@@ -204,11 +197,8 @@ func (g *altsTC) ClientHandshake(ctx context.Context, addr string, rawConn net.C
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
chs.Close()
|
||||
}
|
||||
}()
|
||||
// Close the handshaker since we have obtained a connection.
|
||||
defer chs.Close()
|
||||
secConn, authInfo, err := chs.ClientHandshake(ctx)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@@ -247,15 +237,12 @@ func (g *altsTC) ServerHandshake(rawConn net.Conn) (_ net.Conn, _ credentials.Au
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
shs.Close()
|
||||
}
|
||||
}()
|
||||
secConn, authInfo, err := shs.ServerHandshake(ctx)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
// Close the handshaker since we have obtained a connection.
|
||||
defer shs.Close()
|
||||
altsAuthInfo, ok := authInfo.(AuthInfo)
|
||||
if !ok {
|
||||
return nil, nil, errors.New("server-side auth info is not of type alts.AuthInfo")
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -17,7 +17,7 @@
|
||||
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.6.1
|
||||
// - protoc-gen-go-grpc v1.6.2
|
||||
// - protoc v5.27.1
|
||||
// source: grpc/gcp/handshaker.proto
|
||||
|
||||
|
||||
+18
-4
@@ -173,10 +173,8 @@ func newJoinDialOption(opts ...DialOption) DialOption {
|
||||
// If this option is set to true every connection will release the buffer after
|
||||
// flushing the data on the wire.
|
||||
//
|
||||
// # Experimental
|
||||
//
|
||||
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
|
||||
// later release.
|
||||
// Deprecated: shared write buffer is enabled by default. WithSharedWriteBuffer
|
||||
// will be removed in a future release.
|
||||
func WithSharedWriteBuffer(val bool) DialOption {
|
||||
return newFuncDialOption(func(o *dialOptions) {
|
||||
o.copts.SharedWriteBuffer = val
|
||||
@@ -229,6 +227,14 @@ func WithInitialConnWindowSize(s int32) DialOption {
|
||||
|
||||
// WithStaticStreamWindowSize returns a DialOption which sets the initial
|
||||
// stream window size to the value provided and disables dynamic flow control.
|
||||
//
|
||||
// Note that this also disables dynamic flow control for the connection,
|
||||
// falling back to a default static connection-level window of 64KB. To
|
||||
// use a larger connection-level window, you must also use the
|
||||
// [WithStaticConnWindowSize] DialOption.
|
||||
//
|
||||
// Most users should not configure static flow control windows unless
|
||||
// operating in a memory-constrained environment.
|
||||
func WithStaticStreamWindowSize(s int32) DialOption {
|
||||
return newFuncDialOption(func(o *dialOptions) {
|
||||
o.copts.InitialWindowSize = s
|
||||
@@ -239,6 +245,14 @@ func WithStaticStreamWindowSize(s int32) DialOption {
|
||||
// WithStaticConnWindowSize returns a DialOption which sets the initial
|
||||
// connection window size to the value provided and disables dynamic flow
|
||||
// control.
|
||||
//
|
||||
// Note that this also disables dynamic flow control for individual streams,
|
||||
// falling back to a default static connection-level window of 64KB. To
|
||||
// explicitly configure the stream-level window size, you must also use the
|
||||
// [WithStaticStreamWindowSize] DialOption.
|
||||
//
|
||||
// Most users should not configure static flow control windows unless
|
||||
// operating in a memory-constrained environment.
|
||||
func WithStaticConnWindowSize(s int32) DialOption {
|
||||
return newFuncDialOption(func(o *dialOptions) {
|
||||
o.copts.InitialConnWindowSize = s
|
||||
|
||||
+3
@@ -66,6 +66,9 @@ type Compressor interface {
|
||||
// Decompress reads data from r, decompresses it, and provides the
|
||||
// uncompressed data via the returned io.Reader. If an error occurs while
|
||||
// initializing the decompressor, that error is returned instead.
|
||||
//
|
||||
// The returned io.Reader may optionally implement io.ReadCloser, and if it
|
||||
// does, gRPC will call Close() exactly once.
|
||||
Decompress(r io.Reader) (io.Reader, error)
|
||||
// Name is the name of the compression codec and is used to set the content
|
||||
// coding header. The result must be static; the result cannot change
|
||||
|
||||
+9
-5
@@ -81,6 +81,8 @@ func (z *writer) Close() error {
|
||||
return z.Writer.Close()
|
||||
}
|
||||
|
||||
var _ io.Closer = &reader{}
|
||||
|
||||
type reader struct {
|
||||
*gzip.Reader
|
||||
pool *sync.Pool
|
||||
@@ -102,14 +104,16 @@ func (c *compressor) Decompress(r io.Reader) (io.Reader, error) {
|
||||
return z, nil
|
||||
}
|
||||
|
||||
func (z *reader) Read(p []byte) (n int, err error) {
|
||||
n, err = z.Reader.Read(p)
|
||||
if err == io.EOF {
|
||||
z.pool.Put(z)
|
||||
}
|
||||
func (r *reader) Read(p []byte) (n int, err error) {
|
||||
n, err = r.Reader.Read(p)
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (r *reader) Close() error {
|
||||
defer r.pool.Put(r)
|
||||
return r.Reader.Close()
|
||||
}
|
||||
|
||||
func (c *compressor) Name() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
*
|
||||
* Copyright 2026 gRPC 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 hostname contains utilities for the endpoint hostname attribute
|
||||
// (used for per-endpoint :authority / SNI override).
|
||||
//
|
||||
// # Experimental
|
||||
//
|
||||
// Notice: All APIs in this package are EXPERIMENTAL and may be changed
|
||||
// or removed in a later release.
|
||||
package hostname
|
||||
|
||||
import "google.golang.org/grpc/resolver"
|
||||
|
||||
type hostnameKey struct{}
|
||||
|
||||
// Set returns a copy of the given endpoint with the hostname attribute
|
||||
// set. If hostname is empty the endpoint is returned unmodified.
|
||||
func Set(endpoint resolver.Endpoint, hostname string) resolver.Endpoint {
|
||||
if hostname == "" {
|
||||
return endpoint
|
||||
}
|
||||
endpoint.Attributes = endpoint.Attributes.WithValue(hostnameKey{}, hostname)
|
||||
return endpoint
|
||||
}
|
||||
|
||||
// FromEndpoint returns the hostname attribute of endpoint. If this
|
||||
// attribute is not set, it returns the empty string.
|
||||
func FromEndpoint(endpoint resolver.Endpoint) string {
|
||||
h, _ := endpoint.Attributes.Value(hostnameKey{}).(string)
|
||||
return h
|
||||
}
|
||||
Generated
Vendored
+15
-21
@@ -16,23 +16,23 @@
|
||||
*
|
||||
*/
|
||||
|
||||
// Package weight contains utilities to manage endpoint weights. Weights are
|
||||
// used by LB policies such as ringhash to distribute load across multiple
|
||||
// endpoints.
|
||||
// Package weight contains utilities to manage endpoint weights.
|
||||
// Weights may be used by LB policies to distribute load across
|
||||
// multiple endpoints.
|
||||
//
|
||||
// # Experimental
|
||||
//
|
||||
// Notice: All APIs in this package are EXPERIMENTAL and may be changed
|
||||
// or removed in a later release.
|
||||
package weight
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"google.golang.org/grpc/resolver"
|
||||
)
|
||||
import "google.golang.org/grpc/resolver"
|
||||
|
||||
// attributeKey is the type used as the key to store EndpointInfo in the
|
||||
// Attributes field of resolver.Endpoint.
|
||||
type attributeKey struct{}
|
||||
|
||||
// EndpointInfo will be stored in the Attributes field of Endpoints in order to
|
||||
// use the ringhash balancer.
|
||||
// EndpointInfo will be stored in the Attributes field of Endpoints.
|
||||
type EndpointInfo struct {
|
||||
Weight uint32
|
||||
}
|
||||
@@ -43,22 +43,16 @@ func (a EndpointInfo) Equal(o any) bool {
|
||||
return ok && oa.Weight == a.Weight
|
||||
}
|
||||
|
||||
// Set returns a copy of endpoint in which the Attributes field is updated with
|
||||
// EndpointInfo.
|
||||
// Set returns a copy of endpoint in which the Attributes field is
|
||||
// updated with EndpointInfo.
|
||||
func Set(endpoint resolver.Endpoint, epInfo EndpointInfo) resolver.Endpoint {
|
||||
endpoint.Attributes = endpoint.Attributes.WithValue(attributeKey{}, epInfo)
|
||||
return endpoint
|
||||
}
|
||||
|
||||
// String returns a human-readable representation of EndpointInfo.
|
||||
// This method is intended for logging, testing, and debugging purposes only.
|
||||
// Do not rely on the output format, as it is not guaranteed to remain stable.
|
||||
func (a EndpointInfo) String() string {
|
||||
return fmt.Sprintf("Weight: %d", a.Weight)
|
||||
}
|
||||
|
||||
// FromEndpoint returns the EndpointInfo stored in the Attributes field of an
|
||||
// endpoint. It returns an empty EndpointInfo if attribute is not found.
|
||||
// FromEndpoint returns the EndpointInfo stored in the Attributes
|
||||
// field of an endpoint. It returns an empty EndpointInfo if attribute
|
||||
// is not found.
|
||||
func FromEndpoint(endpoint resolver.Endpoint) EndpointInfo {
|
||||
v := endpoint.Attributes.Value(attributeKey{})
|
||||
ei, _ := v.(EndpointInfo)
|
||||
+1
-1
@@ -17,7 +17,7 @@
|
||||
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.6.1
|
||||
// - protoc-gen-go-grpc v1.6.2
|
||||
// - protoc v5.27.1
|
||||
// source: grpc/health/v1/health.proto
|
||||
|
||||
|
||||
+85
-20
@@ -59,6 +59,15 @@ var (
|
||||
// unconditionally.
|
||||
XDSEndpointHashKeyBackwardCompat = boolFromEnv("GRPC_XDS_ENDPOINT_HASH_KEY_BACKWARD_COMPAT", false)
|
||||
|
||||
// LabelServerGoroutines controls setting [runtime/pprof.Labels] on the
|
||||
// goroutines spawned by [grpc.Server] type.
|
||||
// For now, this is limited to the goroutines spawned to handle incoming
|
||||
// requests on the server.
|
||||
// Set "GRPC_GO_SERVER_GOROUTINE_LABELS" to "grpc.method=true" to
|
||||
// enable this grpc.method label, or "all" to enable all valid labels.
|
||||
// This variable is a bit-field.
|
||||
LabelServerGoroutines = goroutineLabelsFromEnv("GRPC_GO_SERVER_GOROUTINE_LABELS", 0)
|
||||
|
||||
// RingHashSetRequestHashKey is set if the ring hash balancer can get the
|
||||
// request hash header by setting the "requestHashHeader" field, according
|
||||
// to gRFC A76. It can be disabled by setting the environment variable
|
||||
@@ -78,12 +87,12 @@ var (
|
||||
EnableDefaultPortForProxyTarget = boolFromEnv("GRPC_EXPERIMENTAL_ENABLE_DEFAULT_PORT_FOR_PROXY_TARGET", true)
|
||||
|
||||
// CaseSensitiveBalancerRegistries is set if the balancer registry should be
|
||||
// case-sensitive. This is disabled by default, but can be enabled by setting
|
||||
// case-sensitive. This is enabled by default, but can be disabled by setting
|
||||
// the env variable "GRPC_GO_EXPERIMENTAL_CASE_SENSITIVE_BALANCER_REGISTRIES"
|
||||
// to "true".
|
||||
// to "false".
|
||||
//
|
||||
// TODO: After 2 releases, we will enable the env var by default.
|
||||
CaseSensitiveBalancerRegistries = boolFromEnv("GRPC_GO_EXPERIMENTAL_CASE_SENSITIVE_BALANCER_REGISTRIES", false)
|
||||
// This env varible will be removed in release v1.82.0.
|
||||
CaseSensitiveBalancerRegistries = boolFromEnv("GRPC_GO_EXPERIMENTAL_CASE_SENSITIVE_BALANCER_REGISTRIES", true)
|
||||
|
||||
// XDSAuthorityRewrite indicates whether xDS authority rewriting is enabled.
|
||||
// This feature is defined in gRFC A81 and is enabled by setting the
|
||||
@@ -104,22 +113,6 @@ var (
|
||||
// to "false".
|
||||
XDSRecoverPanicInResourceParsing = boolFromEnv("GRPC_GO_EXPERIMENTAL_XDS_RESOURCE_PANIC_RECOVERY", true)
|
||||
|
||||
// DisableStrictPathChecking indicates whether strict path checking is
|
||||
// disabled. This feature can be disabled by setting the environment
|
||||
// variable GRPC_GO_EXPERIMENTAL_DISABLE_STRICT_PATH_CHECKING to "true".
|
||||
//
|
||||
// When strict path checking is enabled, gRPC will reject requests with
|
||||
// paths that do not conform to the gRPC over HTTP/2 specification found at
|
||||
// https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md.
|
||||
//
|
||||
// When disabled, gRPC will allow paths that do not contain a leading slash.
|
||||
// Enabling strict path checking is recommended for security reasons, as it
|
||||
// prevents potential path traversal vulnerabilities.
|
||||
//
|
||||
// A future release will remove this environment variable, enabling strict
|
||||
// path checking behavior unconditionally.
|
||||
DisableStrictPathChecking = boolFromEnv("GRPC_GO_EXPERIMENTAL_DISABLE_STRICT_PATH_CHECKING", false)
|
||||
|
||||
// EnablePriorityLBChildPolicyCache controls whether the priority balancer
|
||||
// should cache child balancers that are removed from the LB policy config,
|
||||
// for a period of 15 minutes. This is disabled by default, but can be
|
||||
@@ -127,6 +120,18 @@ var (
|
||||
// GRPC_EXPERIMENTAL_ENABLE_PRIORITY_LB_CHILD_POLICY_CACHE to true.
|
||||
EnablePriorityLBChildPolicyCache = boolFromEnv("GRPC_EXPERIMENTAL_ENABLE_PRIORITY_LB_CHILD_POLICY_CACHE", false)
|
||||
|
||||
// Enable8KBDefaultHeaderListSize indicates that default maximum header list
|
||||
// size is restricted to 8KB. This is disabled by default, but can be enabled
|
||||
// by setting the environment variable
|
||||
// "GRPC_GO_EXPERIMENTAL_ENABLE_8KB_DEFAULT_HEADER_LIST_SIZE" to "true".
|
||||
// When disabled, the default maximum header list size of 16MB is used.
|
||||
//
|
||||
// When enabled, RPCs with a total size of headers exceeding 8KB will fail
|
||||
// unless explicitly configured otherwise by the user.
|
||||
//
|
||||
// TODO: In release v1.82.0, env var will be enabled by default.
|
||||
Enable8KBDefaultHeaderListSize = boolFromEnv("GRPC_GO_EXPERIMENTAL_ENABLE_8KB_DEFAULT_HEADER_LIST_SIZE", false)
|
||||
|
||||
// EnableHTTPFramerReadBufferPooling enables the use of the
|
||||
// readyreader.Reader interface to perform non-memory-pinning reads,
|
||||
// provided the underlying net.Conn supports it. This reduces memory usage
|
||||
@@ -136,6 +141,17 @@ var (
|
||||
// feature if unforeseen issues arise, and it will be removed in a future
|
||||
// release.
|
||||
EnableHTTPFramerReadBufferPooling = boolFromEnv("GRPC_GO_EXPERIMENTAL_HTTP_FRAMER_READ_BUFFER_POOLING", true)
|
||||
|
||||
// ControlBufferThrottleLimit is the maximum number of control frames that can
|
||||
// be queued in the control buffer before throttling is applied. The value
|
||||
// must be between 1 and 10,000, and is set to 100 by default.
|
||||
//
|
||||
// This environment variable serves as an escape hatch to increase the
|
||||
// throttling limit if unforeseen issues arise, and it will be removed in a
|
||||
// future release.
|
||||
//
|
||||
// TODO: Remove this env var once v1.83.0 is release.
|
||||
ControlBufferThrottleLimit = uint64FromEnv("GRPC_GO_EXPERIMENTAL_CONTROL_BUFFER_THROTTLE_LIMIT", 100, 1, 10000)
|
||||
)
|
||||
|
||||
func boolFromEnv(envVar string, def bool) bool {
|
||||
@@ -160,3 +176,52 @@ func uint64FromEnv(envVar string, def, min, max uint64) uint64 {
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// GoroutineLabels is a bitfield indicating which goroutine labels are enabled.
|
||||
type GoroutineLabels uint16
|
||||
|
||||
func goroutineLabelsFromEnv(envVar string, def GoroutineLabels) GoroutineLabels {
|
||||
val := def
|
||||
v := os.Getenv(envVar)
|
||||
if strings.EqualFold(v, "all") {
|
||||
return AllGoroutineLabels
|
||||
} else if strings.EqualFold(v, "none") {
|
||||
return 0
|
||||
}
|
||||
for s := range strings.SplitSeq(v, ",") {
|
||||
s = strings.TrimSpace(s)
|
||||
if len(s) == 0 {
|
||||
continue
|
||||
}
|
||||
pre, post, ok := strings.Cut(s, "=")
|
||||
if !ok {
|
||||
// no equals sign
|
||||
continue
|
||||
}
|
||||
post = strings.TrimSpace(post)
|
||||
pre = strings.TrimSpace(pre)
|
||||
bitDesignator := GoroutineLabels(0)
|
||||
switch {
|
||||
case strings.EqualFold(pre, "grpc.method"):
|
||||
bitDesignator = GoroutineLabelServerMethod
|
||||
default:
|
||||
continue
|
||||
}
|
||||
if strings.EqualFold(post, "true") {
|
||||
val |= bitDesignator
|
||||
} else if strings.EqualFold(post, "false") {
|
||||
val &^= bitDesignator
|
||||
}
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
const (
|
||||
// GoroutineLabelServerMethod sets the grpc.method label on new
|
||||
// server-side gRPC streams.
|
||||
GoroutineLabelServerMethod GoroutineLabels = 1 << iota
|
||||
)
|
||||
|
||||
// AllGoroutineLabels is an or'd together bitfield of all valid GoroutineLabels
|
||||
// constant values (above).
|
||||
const AllGoroutineLabels = GoroutineLabelServerMethod
|
||||
|
||||
+10
@@ -89,4 +89,14 @@ var (
|
||||
// filtered and prefix-propagated to the LRS server. For more details, see:
|
||||
// https://github.com/grpc/proposal/blob/master/A85-lrs-custom-metrics-changes.md
|
||||
XDSORCAToLRSPropEnabled = boolFromEnv("GRPC_EXPERIMENTAL_XDS_ORCA_LRS_PROPAGATION", false)
|
||||
|
||||
// XDSClientExtProcEnabled indicates whether ExtProc filter is enabled on
|
||||
// the client side. For more details, see:
|
||||
// https://github.com/grpc/proposal/blob/master/A93-xds-ext-proc.md
|
||||
XDSClientExtProcEnabled = boolFromEnv("GRPC_EXPERIMENTAL_XDS_EXT_PROC_ON_CLIENT", false)
|
||||
|
||||
// GCPAuthenticationFilterEnabled enables the xDS GCP Authentication
|
||||
// filter. For more details, see:
|
||||
// https://github.com/grpc/proposal/blob/master/A83-xds-gcp-authn-filter.md
|
||||
GCPAuthenticationFilterEnabled = boolFromEnv("GRPC_EXPERIMENTAL_XDS_GCP_AUTHENTICATION_FILTER", false)
|
||||
)
|
||||
|
||||
-1
@@ -39,7 +39,6 @@ func div(d, r time.Duration) int64 {
|
||||
//
|
||||
// https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#requests
|
||||
func EncodeDuration(t time.Duration) string {
|
||||
// TODO: This is simplistic and not bandwidth efficient. Improve it.
|
||||
if t <= 0 {
|
||||
return "0n"
|
||||
}
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@
|
||||
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.6.1
|
||||
// - protoc-gen-go-grpc v1.6.2
|
||||
// - protoc v5.27.1
|
||||
// source: grpc/lookup/v1/rls.proto
|
||||
|
||||
|
||||
+17
-7
@@ -106,14 +106,24 @@ type ClientStream interface {
|
||||
|
||||
// ClientInterceptor is an interceptor for gRPC client streams.
|
||||
type ClientInterceptor interface {
|
||||
// NewStream produces a ClientStream for an RPC which may optionally use
|
||||
// the provided function to produce a stream for delegation. Note:
|
||||
// RPCInfo.Context should not be used (will be nil).
|
||||
// NewStream creates a ClientStream for an RPC.
|
||||
//
|
||||
// done is invoked when the RPC is finished using its connection, or could
|
||||
// not be assigned a connection. RPC operations may still occur on
|
||||
// ClientStream after done is called, since the interceptor is invoked by
|
||||
// application-layer operations. done must never be nil when called.
|
||||
// Implementations must delegate stream creation to the provided newStream
|
||||
// function. To intercept or override stream behavior, implementations
|
||||
// may wrap the ClientStream returned by the delegate.
|
||||
//
|
||||
// Note: RPCInfo.Context is currently unused and will be nil.
|
||||
//
|
||||
// The done function is invoked when the RPC has finished using its
|
||||
// underlying connection or if a connection could not be assigned. Because
|
||||
// interceptors operate at the application layer, RPC operations may
|
||||
// continue on the ClientStream even after done has been called. The
|
||||
// caller must ensure done is non-nil.
|
||||
//
|
||||
// To ensure RPC completion notifications propagate through the entire
|
||||
// interceptor chain, implementations must ensure that the done function
|
||||
// passed to the delegate newStream invokes the done function passed to
|
||||
// NewStream.
|
||||
NewStream(ctx context.Context, ri RPCInfo, done func(), newStream func(ctx context.Context, done func()) (ClientStream, error)) (ClientStream, error)
|
||||
// Close closes the interceptor. Once called, no new calls to NewStream are
|
||||
// accepted. Ongoing calls to NewStream are allowed to complete.
|
||||
|
||||
+46
-14
@@ -19,24 +19,56 @@
|
||||
// Package stats provides internal stats related functionality.
|
||||
package stats
|
||||
|
||||
import "context"
|
||||
import (
|
||||
"context"
|
||||
"maps"
|
||||
)
|
||||
|
||||
// Labels are the labels for metrics.
|
||||
type Labels struct {
|
||||
// TelemetryLabels are the telemetry labels to record.
|
||||
TelemetryLabels map[string]string
|
||||
// LabelCallback is a function that is executed when telemetry
|
||||
// label keys are updated.
|
||||
type LabelCallback func(map[string]string)
|
||||
type telemetryLabelCallbackKey struct{}
|
||||
|
||||
// UpdateLabels executes registered telemetry callbacks with the update labels. Labels
|
||||
// are copied before being processed by any callbacks to ensure mutations are not
|
||||
// shared among derived contexts.
|
||||
//
|
||||
// It is the responsibility of the registrant to handle conflicts or label resets.
|
||||
func UpdateLabels(ctx context.Context, update map[string]string) {
|
||||
executeTelemetryLabelCallbacks(ctx, update)
|
||||
}
|
||||
|
||||
type labelsKey struct{}
|
||||
// RegisterTelemetryLabelCallback registers a callback function that is executed whenever
|
||||
// telemetry labels are updated.
|
||||
func RegisterTelemetryLabelCallback(ctx context.Context, callback LabelCallback) context.Context {
|
||||
if callback == nil {
|
||||
return ctx
|
||||
}
|
||||
|
||||
callbacks, ok := ctx.Value(telemetryLabelCallbackKey{}).([]LabelCallback)
|
||||
if !ok {
|
||||
return context.WithValue(ctx, telemetryLabelCallbackKey{}, []LabelCallback{callback})
|
||||
}
|
||||
return context.WithValue(ctx, telemetryLabelCallbackKey{}, append(append([]LabelCallback(nil), callbacks...), callback))
|
||||
|
||||
// GetLabels returns the Labels stored in the context, or nil if there is one.
|
||||
func GetLabels(ctx context.Context) *Labels {
|
||||
labels, _ := ctx.Value(labelsKey{}).(*Labels)
|
||||
return labels
|
||||
}
|
||||
|
||||
// SetLabels sets the Labels in the context.
|
||||
func SetLabels(ctx context.Context, labels *Labels) context.Context {
|
||||
// could also append
|
||||
return context.WithValue(ctx, labelsKey{}, labels)
|
||||
// executeTelemetryLabelCallback runs the registered callbacks in the order they were
|
||||
// registered on the context with the provided labels. If no callbacks are registered
|
||||
// it does nothing.
|
||||
//
|
||||
// To ensure callbacks do not mutate the state of the provided label map it is copied
|
||||
// before execution.
|
||||
func executeTelemetryLabelCallbacks(ctx context.Context, labels map[string]string) {
|
||||
callbacks, ok := ctx.Value(telemetryLabelCallbackKey{}).([]LabelCallback)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
labelsCopy := map[string]string{}
|
||||
maps.Copy(labelsCopy, labels)
|
||||
for _, callback := range callbacks {
|
||||
callback(labelsCopy)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+35
-1
@@ -19,6 +19,7 @@
|
||||
package transport
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync/atomic"
|
||||
|
||||
"golang.org/x/net/http2"
|
||||
@@ -28,6 +29,12 @@ import (
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// nonGRPCDataMaxLen is the maximum length of nonGRPCDataBuf.
|
||||
//
|
||||
// NOTE: If changed this value, you MUST update the corresponding test in:
|
||||
// - /test/end2end_test.go:TestHTTPServerSendsNonGRPCHeaderSurfaceFurtherData
|
||||
const nonGRPCDataMaxLen = 1024
|
||||
|
||||
// ClientStream implements streaming functionality for a gRPC client.
|
||||
type ClientStream struct {
|
||||
Stream // Embed for common stream functionality.
|
||||
@@ -46,7 +53,11 @@ type ClientStream struct {
|
||||
// headerValid indicates whether a valid header was received. Only
|
||||
// meaningful after headerChan is closed (always call waitOnHeader() before
|
||||
// reading its value).
|
||||
headerValid bool
|
||||
headerValid bool
|
||||
|
||||
nonGRPCStatus *status.Status // the initial status from the non-gRPC response header, finalized with collected data before closing.
|
||||
nonGRPCDataBuf []byte // stores the data of a non-gRPC response.
|
||||
|
||||
noHeaders bool // set if the client never received headers (set only after the stream is done).
|
||||
headerChanClosed uint32 // set when headerChan is closed. Used to avoid closing headerChan multiple times.
|
||||
bytesReceived atomic.Bool // indicates whether any bytes have been received on this stream
|
||||
@@ -54,6 +65,29 @@ type ClientStream struct {
|
||||
statsHandler stats.Handler // nil for internal streams (e.g., health check, ORCA) where telemetry is not supported.
|
||||
}
|
||||
|
||||
func (s *ClientStream) startNonGRPCDataCollection(st *status.Status) {
|
||||
s.nonGRPCStatus = st
|
||||
s.nonGRPCDataBuf = make([]byte, 0, nonGRPCDataMaxLen)
|
||||
}
|
||||
|
||||
// finalizeNonGRPCStatus builds the terminal status by appending the collected
|
||||
// response body to the original non-gRPC status message.
|
||||
func (s *ClientStream) finalizeNonGRPCStatus() *status.Status {
|
||||
msg := fmt.Sprintf("%s\ndata: %q", s.nonGRPCStatus.Message(), s.nonGRPCDataBuf)
|
||||
return status.New(s.nonGRPCStatus.Code(), msg)
|
||||
}
|
||||
|
||||
// handleNonGRPCData collects non-gRPC body from the given data frame.
|
||||
// It returns non-nil value when the stream should be closed with it.
|
||||
func (s *ClientStream) handleNonGRPCData(f *parsedDataFrame) *status.Status {
|
||||
n := min(f.data.Len(), nonGRPCDataMaxLen-len(s.nonGRPCDataBuf))
|
||||
s.nonGRPCDataBuf = append(s.nonGRPCDataBuf, f.data.ReadOnlyData()[0:n]...)
|
||||
if len(s.nonGRPCDataBuf) >= nonGRPCDataMaxLen || f.StreamEnded() {
|
||||
return s.finalizeNonGRPCStatus()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Read reads an n byte message from the input stream.
|
||||
func (s *ClientStream) Read(n int) (mem.BufferSlice, error) {
|
||||
b, err := s.Stream.read(n)
|
||||
|
||||
+97
-120
@@ -29,6 +29,7 @@ import (
|
||||
|
||||
"golang.org/x/net/http2"
|
||||
"golang.org/x/net/http2/hpack"
|
||||
"google.golang.org/grpc/internal/envconfig"
|
||||
"google.golang.org/grpc/internal/grpclog"
|
||||
"google.golang.org/grpc/mem"
|
||||
)
|
||||
@@ -96,61 +97,70 @@ func (il *itemList) isEmpty() bool {
|
||||
return il.head == nil
|
||||
}
|
||||
|
||||
// maxQueuedControlBufferItems is the maximum number of frames (other than
|
||||
// HEADERS and DATA) that we will buffer before preventing new reads from
|
||||
// occurring on the transport. These are control frames sent in response to
|
||||
// client requests, or frames that result in work being scheduled, such as
|
||||
// RST_STREAM due to bad headers or settings acks.
|
||||
var maxQueuedControlBufferItems = int(envconfig.ControlBufferThrottleLimit)
|
||||
|
||||
type cbItem interface {
|
||||
isThrottled() bool
|
||||
}
|
||||
|
||||
// throttledItem represents every item in the controlBuffer to which the overall
|
||||
// throttling limit applies, other than outgoing HEADERS and DATA frames.
|
||||
type throttledItem struct{}
|
||||
|
||||
func (throttledItem) isThrottled() bool { return true }
|
||||
|
||||
// The following defines various control items which could flow through
|
||||
// the control buffer of transport. They represent different aspects of
|
||||
// control tasks, e.g., flow control, settings, streaming resetting, etc.
|
||||
|
||||
// maxQueuedTransportResponseFrames is the most queued "transport response"
|
||||
// frames we will buffer before preventing new reads from occurring on the
|
||||
// transport. These are control frames sent in response to client requests,
|
||||
// such as RST_STREAM due to bad headers or settings acks.
|
||||
const maxQueuedTransportResponseFrames = 50
|
||||
|
||||
type cbItem interface {
|
||||
isTransportResponseFrame() bool
|
||||
}
|
||||
|
||||
// registerStream is used to register an incoming stream with loopy writer.
|
||||
type registerStream struct {
|
||||
throttledItem
|
||||
streamID uint32
|
||||
wq *writeQuota
|
||||
}
|
||||
|
||||
func (*registerStream) isTransportResponseFrame() bool { return false }
|
||||
|
||||
// headerFrame is also used to register stream on the client-side.
|
||||
type headerFrame struct {
|
||||
type clientHeaders struct {
|
||||
streamID uint32
|
||||
hf []hpack.HeaderField
|
||||
endStream bool // Valid on server side.
|
||||
initStream func(uint32) error // Used only on the client side.
|
||||
initStream func(uint32) error
|
||||
onWrite func()
|
||||
wq *writeQuota // write quota for the stream created.
|
||||
cleanup *cleanupStream // Valid on the server side.
|
||||
onOrphaned func(error) // Valid on client-side
|
||||
wq *writeQuota
|
||||
onOrphaned func(error)
|
||||
}
|
||||
|
||||
func (h *headerFrame) isTransportResponseFrame() bool {
|
||||
return h.cleanup != nil && h.cleanup.rst // Results in a RST_STREAM
|
||||
func (*clientHeaders) isThrottled() bool { return false }
|
||||
|
||||
type serverHeaders struct {
|
||||
streamID uint32
|
||||
hf []hpack.HeaderField
|
||||
endStream bool
|
||||
onWrite func()
|
||||
cleanup *cleanupStream
|
||||
}
|
||||
|
||||
func (h *serverHeaders) isThrottled() bool { return false }
|
||||
|
||||
type cleanupStream struct {
|
||||
throttledItem
|
||||
streamID uint32
|
||||
rst bool
|
||||
rstCode http2.ErrCode
|
||||
onWrite func()
|
||||
}
|
||||
|
||||
func (c *cleanupStream) isTransportResponseFrame() bool { return c.rst } // Results in a RST_STREAM
|
||||
|
||||
type earlyAbortStream struct {
|
||||
throttledItem
|
||||
streamID uint32
|
||||
rst bool
|
||||
hf []hpack.HeaderField // Pre-built header fields
|
||||
}
|
||||
|
||||
func (*earlyAbortStream) isTransportResponseFrame() bool { return false }
|
||||
|
||||
type dataFrame struct {
|
||||
streamID uint32
|
||||
endStream bool
|
||||
@@ -162,70 +172,60 @@ type dataFrame struct {
|
||||
onEachWrite func()
|
||||
}
|
||||
|
||||
func (*dataFrame) isTransportResponseFrame() bool { return false }
|
||||
func (*dataFrame) isThrottled() bool { return false }
|
||||
|
||||
type incomingWindowUpdate struct {
|
||||
throttledItem
|
||||
streamID uint32
|
||||
increment uint32
|
||||
}
|
||||
|
||||
func (*incomingWindowUpdate) isTransportResponseFrame() bool { return false }
|
||||
|
||||
type outgoingWindowUpdate struct {
|
||||
throttledItem
|
||||
streamID uint32
|
||||
increment uint32
|
||||
}
|
||||
|
||||
func (*outgoingWindowUpdate) isTransportResponseFrame() bool {
|
||||
return false // window updates are throttled by thresholds
|
||||
}
|
||||
|
||||
type incomingSettings struct {
|
||||
throttledItem
|
||||
ss []http2.Setting
|
||||
}
|
||||
|
||||
func (*incomingSettings) isTransportResponseFrame() bool { return true } // Results in a settings ACK
|
||||
|
||||
type outgoingSettings struct {
|
||||
throttledItem
|
||||
ss []http2.Setting
|
||||
}
|
||||
|
||||
func (*outgoingSettings) isTransportResponseFrame() bool { return false }
|
||||
|
||||
type incomingGoAway struct {
|
||||
throttledItem
|
||||
}
|
||||
|
||||
func (*incomingGoAway) isTransportResponseFrame() bool { return false }
|
||||
|
||||
type goAway struct {
|
||||
throttledItem
|
||||
code http2.ErrCode
|
||||
debugData []byte
|
||||
headsUp bool
|
||||
closeConn error // if set, loopyWriter will exit with this error
|
||||
}
|
||||
|
||||
func (*goAway) isTransportResponseFrame() bool { return false }
|
||||
|
||||
type ping struct {
|
||||
throttledItem
|
||||
ack bool
|
||||
data [8]byte
|
||||
}
|
||||
|
||||
func (*ping) isTransportResponseFrame() bool { return true }
|
||||
|
||||
type outFlowControlSizeRequest struct {
|
||||
throttledItem
|
||||
resp chan uint32
|
||||
}
|
||||
|
||||
func (*outFlowControlSizeRequest) isTransportResponseFrame() bool { return false }
|
||||
|
||||
// closeConnection is an instruction to tell the loopy writer to flush the
|
||||
// framer and exit, which will cause the transport's connection to be closed
|
||||
// (by the client or server). The transport itself will close after the reader
|
||||
// encounters the EOF caused by the connection closure.
|
||||
type closeConnection struct{}
|
||||
|
||||
func (closeConnection) isTransportResponseFrame() bool { return false }
|
||||
type closeConnection struct {
|
||||
throttledItem
|
||||
}
|
||||
|
||||
type outStreamState int
|
||||
|
||||
@@ -379,9 +379,9 @@ func (c *controlBuffer) executeAndPut(f func() bool, it cbItem) (bool, error) {
|
||||
c.consumerWaiting = false
|
||||
}
|
||||
c.list.enqueue(it)
|
||||
if it.isTransportResponseFrame() {
|
||||
if it.isThrottled() {
|
||||
c.transportResponseFrames++
|
||||
if c.transportResponseFrames == maxQueuedTransportResponseFrames {
|
||||
if c.transportResponseFrames == maxQueuedControlBufferItems {
|
||||
// We are adding the frame that puts us over the threshold; create
|
||||
// a throttling channel.
|
||||
ch := make(chan struct{})
|
||||
@@ -436,8 +436,8 @@ func (c *controlBuffer) getOnceLocked() (any, error) {
|
||||
return nil, nil
|
||||
}
|
||||
h := c.list.dequeue().(cbItem)
|
||||
if h.isTransportResponseFrame() {
|
||||
if c.transportResponseFrames == maxQueuedTransportResponseFrames {
|
||||
if h.isThrottled() {
|
||||
if c.transportResponseFrames == maxQueuedControlBufferItems {
|
||||
// We are removing the frame that put us over the
|
||||
// threshold; close and clear the throttling channel.
|
||||
ch := c.trfChan.Swap(nil)
|
||||
@@ -464,10 +464,8 @@ func (c *controlBuffer) finish() {
|
||||
// is still not aware of these yet.
|
||||
for head := c.list.dequeueAll(); head != nil; head = head.next {
|
||||
switch v := head.it.(type) {
|
||||
case *headerFrame:
|
||||
if v.onOrphaned != nil { // It will be nil on the server-side.
|
||||
v.onOrphaned(ErrConnClosing)
|
||||
}
|
||||
case *clientHeaders:
|
||||
v.onOrphaned(ErrConnClosing)
|
||||
case *dataFrame:
|
||||
if !v.processing {
|
||||
v.data.Free()
|
||||
@@ -680,42 +678,38 @@ func (l *loopyWriter) registerStreamHandler(h *registerStream) {
|
||||
l.estdStreams[h.streamID] = str
|
||||
}
|
||||
|
||||
func (l *loopyWriter) headerHandler(h *headerFrame) error {
|
||||
if l.side == serverSide {
|
||||
str, ok := l.estdStreams[h.streamID]
|
||||
if !ok {
|
||||
if l.logger.V(logLevel) {
|
||||
l.logger.Infof("Unrecognized streamID %d in loopyWriter", h.streamID)
|
||||
}
|
||||
return nil
|
||||
func (l *loopyWriter) serverHeaderHandler(hdr *serverHeaders) error {
|
||||
str, ok := l.estdStreams[hdr.streamID]
|
||||
if !ok {
|
||||
if l.logger.V(logLevel) {
|
||||
l.logger.Infof("Unrecognized streamID %d in loopyWriter", hdr.streamID)
|
||||
}
|
||||
// Case 1.A: Server is responding back with headers.
|
||||
if !h.endStream {
|
||||
return l.writeHeader(h.streamID, h.endStream, h.hf, h.onWrite)
|
||||
}
|
||||
// else: Case 1.B: Server wants to close stream.
|
||||
return nil
|
||||
}
|
||||
|
||||
if str.state != empty { // either active or waiting on stream quota.
|
||||
// add it str's list of items.
|
||||
str.itl.enqueue(h)
|
||||
return nil
|
||||
}
|
||||
if err := l.writeHeader(h.streamID, h.endStream, h.hf, h.onWrite); err != nil {
|
||||
return err
|
||||
}
|
||||
return l.cleanupStreamHandler(h.cleanup)
|
||||
// Case 1: Server is responding back with headers.
|
||||
if !hdr.endStream {
|
||||
return l.writeHeader(hdr.streamID, hdr.endStream, hdr.hf, hdr.onWrite)
|
||||
}
|
||||
// Case 2: Client wants to originate stream.
|
||||
str := &outStream{
|
||||
id: h.streamID,
|
||||
state: empty,
|
||||
itl: &itemList{},
|
||||
wq: h.wq,
|
||||
|
||||
// Case 2: Server is closing the stream.
|
||||
if str.state != empty { // either active or waiting on stream quota.
|
||||
str.itl.enqueue(hdr)
|
||||
return nil
|
||||
}
|
||||
return l.originateStream(str, h)
|
||||
if err := l.writeHeader(hdr.streamID, hdr.endStream, hdr.hf, hdr.onWrite); err != nil {
|
||||
return err
|
||||
}
|
||||
return l.cleanupStreamHandler(hdr.cleanup)
|
||||
}
|
||||
|
||||
func (l *loopyWriter) originateStream(str *outStream, hdr *headerFrame) error {
|
||||
func (l *loopyWriter) clientHeaderHandler(hdr *clientHeaders) error {
|
||||
str := &outStream{
|
||||
id: hdr.streamID,
|
||||
state: empty,
|
||||
itl: &itemList{},
|
||||
wq: hdr.wq,
|
||||
}
|
||||
// l.draining is set when handling GoAway. In which case, we want to avoid
|
||||
// creating new streams.
|
||||
if l.draining {
|
||||
@@ -726,7 +720,7 @@ func (l *loopyWriter) originateStream(str *outStream, hdr *headerFrame) error {
|
||||
if err := hdr.initStream(str.id); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := l.writeHeader(str.id, hdr.endStream, hdr.hf, hdr.onWrite); err != nil {
|
||||
if err := l.writeHeader(str.id, false, hdr.hf, hdr.onWrite); err != nil {
|
||||
return err
|
||||
}
|
||||
l.estdStreams[str.id] = str
|
||||
@@ -882,8 +876,10 @@ func (l *loopyWriter) handle(i any) error {
|
||||
return l.incomingSettingsHandler(i)
|
||||
case *outgoingSettings:
|
||||
return l.outgoingSettingsHandler(i)
|
||||
case *headerFrame:
|
||||
return l.headerHandler(i)
|
||||
case *clientHeaders:
|
||||
return l.clientHeaderHandler(i)
|
||||
case *serverHeaders:
|
||||
return l.serverHeaderHandler(i)
|
||||
case *registerStream:
|
||||
l.registerStreamHandler(i)
|
||||
case *cleanupStream:
|
||||
@@ -956,39 +952,16 @@ func (l *loopyWriter) processData() (bool, error) {
|
||||
// from data is copied to h to make as big as the maximum possible HTTP2 frame
|
||||
// size.
|
||||
|
||||
if len(dataItem.h) == 0 && reader.Remaining() == 0 { // Empty data frame
|
||||
// Client sends out empty data frame with endStream = true
|
||||
if err := l.framer.writeData(dataItem.streamID, dataItem.endStream, nil); err != nil {
|
||||
return false, err
|
||||
}
|
||||
str.itl.dequeue() // remove the empty data item from stream
|
||||
reader.Close()
|
||||
if str.itl.isEmpty() {
|
||||
str.state = empty
|
||||
} else if trailer, ok := str.itl.peek().(*headerFrame); ok { // the next item is trailers.
|
||||
if err := l.writeHeader(trailer.streamID, trailer.endStream, trailer.hf, trailer.onWrite); err != nil {
|
||||
return false, err
|
||||
}
|
||||
if err := l.cleanupStreamHandler(trailer.cleanup); err != nil {
|
||||
return false, err
|
||||
}
|
||||
} else {
|
||||
l.activeStreams.enqueue(str)
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
isEmpty := len(dataItem.h) == 0 && reader.Remaining() == 0
|
||||
// Figure out the maximum size we can send
|
||||
maxSize := http2MaxFrameLen
|
||||
if strQuota := int(l.oiws) - str.bytesOutStanding; strQuota <= 0 { // stream-level flow control.
|
||||
strQuota := int(l.oiws) - str.bytesOutStanding
|
||||
if strQuota <= 0 && !isEmpty { // stream-level flow control.
|
||||
str.state = waitingOnStreamQuota
|
||||
return false, nil
|
||||
} else if maxSize > strQuota {
|
||||
maxSize = strQuota
|
||||
}
|
||||
if maxSize > int(l.sendQuota) { // connection-level flow control.
|
||||
maxSize = int(l.sendQuota)
|
||||
}
|
||||
maxSize = min(maxSize, max(strQuota, 0))
|
||||
maxSize = min(maxSize, int(l.sendQuota)) // connection-level flow control.
|
||||
// Compute how much of the header and data we can send within quota and max frame length
|
||||
hSize := min(maxSize, len(dataItem.h))
|
||||
dSize := min(maxSize-hSize, reader.Remaining())
|
||||
@@ -1039,19 +1012,23 @@ func (l *loopyWriter) processData() (bool, error) {
|
||||
reader.Close()
|
||||
str.itl.dequeue()
|
||||
}
|
||||
return false, l.updateStreamAfterWrite(str)
|
||||
}
|
||||
|
||||
func (l *loopyWriter) updateStreamAfterWrite(str *outStream) error {
|
||||
if str.itl.isEmpty() {
|
||||
str.state = empty
|
||||
} else if trailer, ok := str.itl.peek().(*headerFrame); ok { // The next item is trailers.
|
||||
} else if trailer, ok := str.itl.peek().(*serverHeaders); ok { // the next item is trailers.
|
||||
if err := l.writeHeader(trailer.streamID, trailer.endStream, trailer.hf, trailer.onWrite); err != nil {
|
||||
return false, err
|
||||
return err
|
||||
}
|
||||
if err := l.cleanupStreamHandler(trailer.cleanup); err != nil {
|
||||
return false, err
|
||||
return err
|
||||
}
|
||||
} else if int(l.oiws)-str.bytesOutStanding <= 0 { // Ran out of stream quota.
|
||||
str.state = waitingOnStreamQuota
|
||||
} else { // Otherwise add it back to the list of active streams.
|
||||
l.activeStreams.enqueue(str)
|
||||
}
|
||||
return false, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
+4
-6
@@ -115,7 +115,6 @@ func (f *trInFlow) getSize() uint32 {
|
||||
return atomic.LoadUint32(&f.effectiveWindowSize)
|
||||
}
|
||||
|
||||
// TODO(mmukhi): Simplify this code.
|
||||
// inFlow deals with inbound flow control
|
||||
type inFlow struct {
|
||||
mu sync.Mutex
|
||||
@@ -174,14 +173,14 @@ func (f *inFlow) maybeAdjust(n uint32) uint32 {
|
||||
// onData is invoked when some data frame is received. It updates pendingData.
|
||||
func (f *inFlow) onData(n uint32) error {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
|
||||
f.pendingData += n
|
||||
if f.pendingData+f.pendingUpdate > f.limit+f.delta {
|
||||
limit := f.limit
|
||||
rcvd := f.pendingData + f.pendingUpdate
|
||||
f.mu.Unlock()
|
||||
return fmt.Errorf("received %d-bytes data exceeding the limit %d bytes", rcvd, limit)
|
||||
}
|
||||
f.mu.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -189,8 +188,9 @@ func (f *inFlow) onData(n uint32) error {
|
||||
// to be sent to the peer.
|
||||
func (f *inFlow) onRead(n uint32) uint32 {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
|
||||
if f.pendingData == 0 {
|
||||
f.mu.Unlock()
|
||||
return 0
|
||||
}
|
||||
f.pendingData -= n
|
||||
@@ -205,9 +205,7 @@ func (f *inFlow) onRead(n uint32) uint32 {
|
||||
if f.pendingUpdate >= f.limit/4 {
|
||||
wu := f.pendingUpdate
|
||||
f.pendingUpdate = 0
|
||||
f.mu.Unlock()
|
||||
return wu
|
||||
}
|
||||
f.mu.Unlock()
|
||||
return 0
|
||||
}
|
||||
|
||||
+2
-2
@@ -479,8 +479,8 @@ func (ht *serverHandlerTransport) runStream() {
|
||||
|
||||
func (ht *serverHandlerTransport) incrMsgRecv() {}
|
||||
|
||||
func (ht *serverHandlerTransport) Drain(string) {
|
||||
panic("Drain() is not implemented")
|
||||
func (ht *serverHandlerTransport) Drain(s string) {
|
||||
ht.Close(errors.New(s))
|
||||
}
|
||||
|
||||
// mapRecvMsgError returns the non-nil err into the appropriate
|
||||
|
||||
+50
-8
@@ -39,6 +39,7 @@ import (
|
||||
"google.golang.org/grpc/internal"
|
||||
"google.golang.org/grpc/internal/channelz"
|
||||
icredentials "google.golang.org/grpc/internal/credentials"
|
||||
"google.golang.org/grpc/internal/envconfig"
|
||||
"google.golang.org/grpc/internal/grpclog"
|
||||
"google.golang.org/grpc/internal/grpcsync"
|
||||
"google.golang.org/grpc/internal/grpcutil"
|
||||
@@ -318,7 +319,13 @@ func NewHTTP2Client(connectCtx, ctx context.Context, addr resolver.Address, opts
|
||||
}
|
||||
writeBufSize := opts.WriteBufferSize
|
||||
readBufSize := opts.ReadBufferSize
|
||||
// The default header list size is moving from 16MB to 8KB. The 8KB limit
|
||||
// is only used if Enable8KBDefaultHeaderListSize is true; otherwise, the
|
||||
// old 16MB default is used. User-specified options always take precedence.
|
||||
maxHeaderListSize := defaultClientMaxHeaderListSize
|
||||
if envconfig.Enable8KBDefaultHeaderListSize {
|
||||
maxHeaderListSize = upcomingDefaultHeaderListSize
|
||||
}
|
||||
if opts.MaxHeaderListSize != nil {
|
||||
maxHeaderListSize = *opts.MaxHeaderListSize
|
||||
}
|
||||
@@ -799,9 +806,8 @@ func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr, handler s
|
||||
close(s.headerChan)
|
||||
}
|
||||
}
|
||||
hdr := &headerFrame{
|
||||
hf: headerFields,
|
||||
endStream: false,
|
||||
hdr := &clientHeaders{
|
||||
hf: headerFields,
|
||||
initStream: func(uint32) error {
|
||||
t.mu.Lock()
|
||||
// TODO: handle transport closure in loopy instead and remove this
|
||||
@@ -879,8 +885,8 @@ func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr, handler s
|
||||
return false
|
||||
}
|
||||
}
|
||||
if sz > int64(upcomingDefaultHeaderListSize) {
|
||||
t.logger.Warningf("Header list size to send (%d bytes) is larger than the upcoming default limit (%d bytes). In a future release, this will be restricted to %d bytes.", sz, upcomingDefaultHeaderListSize, upcomingDefaultHeaderListSize)
|
||||
if !envconfig.Enable8KBDefaultHeaderListSize && sz > int64(upcomingDefaultHeaderListSize) {
|
||||
t.logger.Warningf("Header list size to send (%d bytes) is larger than the upcoming default limit (%d bytes). In release v1.82.0, GRPC_GO_EXPERIMENTAL_ENABLE_8KB_DEFAULT_HEADER_LIST_SIZE will be enabled by default, enforcing this limit.", sz, upcomingDefaultHeaderListSize)
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -1224,10 +1230,30 @@ func (t *http2Client) handleData(f *parsedDataFrame) {
|
||||
t.closeStream(s, io.EOF, true, http2.ErrCodeFlowControl, status.New(codes.Internal, err.Error()), nil, false)
|
||||
return
|
||||
}
|
||||
|
||||
if s.nonGRPCStatus != nil {
|
||||
// The frame should be handled as a non-gRPC response body
|
||||
st := s.handleNonGRPCData(f)
|
||||
if st != nil {
|
||||
t.closeStream(s, st.Err(), true, http2.ErrCodeProtocol, st, nil, true)
|
||||
return
|
||||
}
|
||||
if w := s.fc.onRead(size); w > 0 {
|
||||
t.controlBuf.put(&outgoingWindowUpdate{
|
||||
streamID: s.id,
|
||||
increment: w,
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
dataLen := f.data.Len()
|
||||
if f.Header().Flags.Has(http2.FlagDataPadded) {
|
||||
if w := s.fc.onRead(size - uint32(dataLen)); w > 0 {
|
||||
t.controlBuf.put(&outgoingWindowUpdate{s.id, w})
|
||||
t.controlBuf.put(&outgoingWindowUpdate{
|
||||
streamID: s.id,
|
||||
increment: w,
|
||||
})
|
||||
}
|
||||
}
|
||||
if dataLen > 0 {
|
||||
@@ -1468,6 +1494,17 @@ func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) {
|
||||
return
|
||||
}
|
||||
|
||||
// If we are collecting non-gRPC response data and receive a trailing
|
||||
// HEADERS frame with END_STREAM, finalize the buffered data and close
|
||||
// the stream.
|
||||
if s.nonGRPCStatus != nil {
|
||||
if endStream {
|
||||
st := s.finalizeNonGRPCStatus()
|
||||
t.closeStream(s, st.Err(), true, http2.ErrCodeProtocol, st, nil, true)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
// If a gRPC Response-Headers has already been received, then it means
|
||||
// that the peer is speaking gRPC and we are in gRPC mode.
|
||||
@@ -1568,7 +1605,12 @@ func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) {
|
||||
}
|
||||
|
||||
se := status.New(grpcErrorCode, strings.Join(errs, "; "))
|
||||
t.closeStream(s, se.Err(), true, http2.ErrCodeProtocol, se, nil, endStream)
|
||||
if endStream {
|
||||
t.closeStream(s, se.Err(), true, http2.ErrCodeProtocol, se, nil, true)
|
||||
return
|
||||
}
|
||||
|
||||
s.startNonGRPCDataCollection(se)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1839,7 +1881,7 @@ func (t *http2Client) getOutFlowWindow() int64 {
|
||||
resp := make(chan uint32, 1)
|
||||
timer := time.NewTimer(time.Second)
|
||||
defer timer.Stop()
|
||||
t.controlBuf.put(&outFlowControlSizeRequest{resp})
|
||||
t.controlBuf.put(&outFlowControlSizeRequest{resp: resp})
|
||||
select {
|
||||
case sz := <-resp:
|
||||
return int64(sz)
|
||||
|
||||
+20
-9
@@ -38,11 +38,13 @@ import (
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"google.golang.org/grpc/internal"
|
||||
"google.golang.org/grpc/internal/envconfig"
|
||||
"google.golang.org/grpc/internal/grpclog"
|
||||
"google.golang.org/grpc/internal/grpcutil"
|
||||
"google.golang.org/grpc/internal/pretty"
|
||||
istatus "google.golang.org/grpc/internal/status"
|
||||
"google.golang.org/grpc/internal/syscall"
|
||||
transportinternal "google.golang.org/grpc/internal/transport/internal"
|
||||
"google.golang.org/grpc/mem"
|
||||
|
||||
"google.golang.org/grpc/codes"
|
||||
@@ -165,7 +167,13 @@ func NewServerTransport(conn net.Conn, config *ServerConfig) (_ ServerTransport,
|
||||
}
|
||||
writeBufSize := config.WriteBufferSize
|
||||
readBufSize := config.ReadBufferSize
|
||||
// The default header list size is moving from 16MB to 8KB. The 8KB limit
|
||||
// is only used if Enable8KBDefaultHeaderListSize is true; otherwise, the
|
||||
// old 16MB default is used. User-specified options always take precedence.
|
||||
maxHeaderListSize := defaultServerMaxHeaderListSize
|
||||
if envconfig.Enable8KBDefaultHeaderListSize {
|
||||
maxHeaderListSize = upcomingDefaultHeaderListSize
|
||||
}
|
||||
if config.MaxHeaderListSize != nil {
|
||||
maxHeaderListSize = *config.MaxHeaderListSize
|
||||
}
|
||||
@@ -802,7 +810,10 @@ func (t *http2Server) handleData(f *parsedDataFrame) {
|
||||
dataLen := f.data.Len()
|
||||
if f.Header().Flags.Has(http2.FlagDataPadded) {
|
||||
if w := s.fc.onRead(size - uint32(dataLen)); w > 0 {
|
||||
t.controlBuf.put(&outgoingWindowUpdate{s.id, w})
|
||||
t.controlBuf.put(&outgoingWindowUpdate{
|
||||
streamID: s.id,
|
||||
increment: w,
|
||||
})
|
||||
}
|
||||
}
|
||||
if dataLen > 0 {
|
||||
@@ -948,8 +959,8 @@ func (t *http2Server) checkForHeaderListSize(hf []hpack.HeaderField) bool {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if sz > int64(upcomingDefaultHeaderListSize) {
|
||||
t.logger.Warningf("Header list size to send (%d bytes) is larger than the upcoming default limit (%d bytes). In a future release, this will be restricted to %d bytes.", sz, upcomingDefaultHeaderListSize, upcomingDefaultHeaderListSize)
|
||||
if !envconfig.Enable8KBDefaultHeaderListSize && sz > int64(upcomingDefaultHeaderListSize) {
|
||||
t.logger.Warningf("Header list size to send (%d bytes) is larger than the upcoming default limit (%d bytes). In release v1.82.0, GRPC_GO_EXPERIMENTAL_ENABLE_8KB_DEFAULT_HEADER_LIST_SIZE will be enabled by default, enforcing this limit.", sz, upcomingDefaultHeaderListSize)
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -1039,7 +1050,7 @@ func (t *http2Server) writeHeaderLocked(s *ServerStream) error {
|
||||
headerFields = append(headerFields, hpack.HeaderField{Name: "grpc-encoding", Value: s.sendCompress})
|
||||
}
|
||||
headerFields = appendHeaderFieldsFromMD(headerFields, s.header)
|
||||
hf := &headerFrame{
|
||||
hf := &serverHeaders{
|
||||
streamID: s.id,
|
||||
hf: headerFields,
|
||||
endStream: false,
|
||||
@@ -1107,7 +1118,7 @@ func (t *http2Server) writeStatus(s *ServerStream, st *status.Status) error {
|
||||
|
||||
// Attach the trailer metadata.
|
||||
headerFields = appendHeaderFieldsFromMD(headerFields, s.trailer)
|
||||
trailingHeader := &headerFrame{
|
||||
trailingHeader := &serverHeaders{
|
||||
streamID: s.id,
|
||||
hf: headerFields,
|
||||
endStream: true,
|
||||
@@ -1317,7 +1328,7 @@ func (t *http2Server) deleteStream(s *ServerStream, eosReceived bool) {
|
||||
}
|
||||
|
||||
// finishStream closes the stream and puts the trailing headerFrame into controlbuf.
|
||||
func (t *http2Server) finishStream(s *ServerStream, rst bool, rstCode http2.ErrCode, hdr *headerFrame, eosReceived bool) {
|
||||
func (t *http2Server) finishStream(s *ServerStream, rst bool, rstCode http2.ErrCode, hdr *serverHeaders, eosReceived bool) {
|
||||
// In case stream sending and receiving are invoked in separate
|
||||
// goroutines (e.g., bi-directional streaming), cancel needs to be
|
||||
// called to interrupt the potential blocking on other goroutines.
|
||||
@@ -1441,14 +1452,14 @@ func (t *http2Server) socketMetrics() *channelz.EphemeralSocketMetrics {
|
||||
func (t *http2Server) incrMsgSent() {
|
||||
if channelz.IsOn() {
|
||||
t.channelz.SocketMetrics.MessagesSent.Add(1)
|
||||
t.channelz.SocketMetrics.LastMessageSentTimestamp.Add(1)
|
||||
t.channelz.SocketMetrics.LastMessageSentTimestamp.Store(transportinternal.TimeNowFunc())
|
||||
}
|
||||
}
|
||||
|
||||
func (t *http2Server) incrMsgRecv() {
|
||||
if channelz.IsOn() {
|
||||
t.channelz.SocketMetrics.MessagesReceived.Add(1)
|
||||
t.channelz.SocketMetrics.LastMessageReceivedTimestamp.Add(1)
|
||||
t.channelz.SocketMetrics.LastMessageReceivedTimestamp.Store(transportinternal.TimeNowFunc())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1456,7 +1467,7 @@ func (t *http2Server) getOutFlowWindow() int64 {
|
||||
resp := make(chan uint32, 1)
|
||||
timer := time.NewTimer(time.Second)
|
||||
defer timer.Stop()
|
||||
t.controlBuf.put(&outFlowControlSizeRequest{resp})
|
||||
t.controlBuf.put(&outFlowControlSizeRequest{resp: resp})
|
||||
select {
|
||||
case sz := <-resp:
|
||||
return int64(sz)
|
||||
|
||||
Generated
Vendored
+7
-13
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
*
|
||||
* Copyright 2021 gRPC authors.
|
||||
* Copyright 2026 gRPC authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,16 +16,10 @@
|
||||
*
|
||||
*/
|
||||
|
||||
package grpcutil
|
||||
// Package internal contains functionality internal to the transport package.
|
||||
package internal
|
||||
|
||||
import "regexp"
|
||||
|
||||
// FullMatchWithRegex returns whether the full text matches the regex provided.
|
||||
func FullMatchWithRegex(re *regexp.Regexp, text string) bool {
|
||||
if len(text) == 0 {
|
||||
return re.MatchString(text)
|
||||
}
|
||||
re.Longest()
|
||||
rem := re.FindString(text)
|
||||
return len(rem) == len(text)
|
||||
}
|
||||
// TimeNowFunc is a variable that can be set to override the default behavior of
|
||||
// getting the current time in nanoseconds. It is used in transport code to set
|
||||
// channelz timestamps, and is exposed here for testing purposes.
|
||||
var TimeNowFunc func() int64
|
||||
+5
@@ -35,6 +35,7 @@ import (
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/internal/channelz"
|
||||
"google.golang.org/grpc/internal/transport/internal"
|
||||
"google.golang.org/grpc/keepalive"
|
||||
"google.golang.org/grpc/mem"
|
||||
"google.golang.org/grpc/metadata"
|
||||
@@ -46,6 +47,10 @@ import (
|
||||
|
||||
const logLevel = 2
|
||||
|
||||
func init() {
|
||||
internal.TimeNowFunc = func() int64 { return time.Now().UnixNano() }
|
||||
}
|
||||
|
||||
// recvMsg represents the received msg from the transport. All transport
|
||||
// protocol specific info has been removed.
|
||||
type recvMsg struct {
|
||||
|
||||
+3
-2
@@ -24,7 +24,8 @@ import (
|
||||
"maps"
|
||||
"slices"
|
||||
|
||||
"google.golang.org/grpc/internal/balancer/weight"
|
||||
"google.golang.org/grpc/experimental/balancer/hostname"
|
||||
"google.golang.org/grpc/experimental/balancer/weight"
|
||||
"google.golang.org/grpc/internal/envconfig"
|
||||
"google.golang.org/grpc/internal/hierarchy"
|
||||
internalserviceconfig "google.golang.org/grpc/internal/serviceconfig"
|
||||
@@ -181,7 +182,7 @@ func buildClusterImplConfigForDNS(g *nameGenerator, config *xdsresource.ClusterC
|
||||
// LB policies that rely on locality information (like weighted_target)
|
||||
// continue to work.
|
||||
localityStr := xdsinternal.LocalityString(clients.Locality{})
|
||||
retEndpoint = xdsresource.SetHostname(hierarchy.SetInEndpoint(retEndpoint, []string{pName, localityStr}), clusterUpdate.DNSHostName)
|
||||
retEndpoint = hostname.Set(hierarchy.SetInEndpoint(retEndpoint, []string{pName, localityStr}), clusterUpdate.DNSHostName)
|
||||
// Set the locality weight to 1. This is required because the child policy
|
||||
// like weighted_target which relies on locality weights to distribute
|
||||
// traffic. These policies may drop traffic if the weight is 0.
|
||||
|
||||
+4
-4
@@ -575,18 +575,18 @@ func (b *clusterImplBalancer) NewSubConn(addrs []resolver.Address, opts balancer
|
||||
newAddrs[i] = xdsinternal.SetXDSHandshakeClusterName(addr, clusterName)
|
||||
newAddrs[i] = xds.SetHandshakeInfo(newAddrs[i], &b.xdsHIPtr)
|
||||
|
||||
hostname := xdsresource.Hostname(addr)
|
||||
host := xdsresource.Hostname(addr)
|
||||
// If the hostname contains a port, strip it. Per [RFC 6066, Section
|
||||
// 3](https://www.rfc-editor.org/rfc/rfc6066.html#section-3), the SNI
|
||||
// may only contain a qualified DNS hostname, which excludes port
|
||||
// numbers.
|
||||
h, _, err := net.SplitHostPort(hostname)
|
||||
h, _, err := net.SplitHostPort(host)
|
||||
if err == nil {
|
||||
hostname = h
|
||||
host = h
|
||||
}
|
||||
// Store hostname in the address attributes, so that it can be used in
|
||||
// the client handshake.
|
||||
newAddrs[i] = xds.SetAddressHostname(newAddrs[i], hostname)
|
||||
newAddrs[i] = xds.SetAddressHostname(newAddrs[i], host)
|
||||
}
|
||||
var sc balancer.SubConn
|
||||
scw := &scWrapper{}
|
||||
|
||||
+9
-21
@@ -20,7 +20,6 @@ package clusterimpl
|
||||
|
||||
import (
|
||||
"context"
|
||||
"maps"
|
||||
|
||||
v3orcapb "github.com/cncf/xds/go/xds/data/orca/v3"
|
||||
"google.golang.org/grpc/balancer"
|
||||
@@ -95,24 +94,10 @@ type picker struct {
|
||||
metrics *xdsresource.LRSReportEndpointMetricsConfig
|
||||
}
|
||||
|
||||
func telemetryLabels(ctx context.Context) map[string]string {
|
||||
if ctx == nil {
|
||||
return nil
|
||||
}
|
||||
labels := stats.GetLabels(ctx)
|
||||
if labels == nil {
|
||||
return nil
|
||||
}
|
||||
return labels.TelemetryLabels
|
||||
}
|
||||
|
||||
func (d *picker) Pick(info balancer.PickInfo) (balancer.PickResult, error) {
|
||||
// Unconditionally set labels if present, even dropped or queued RPC's can
|
||||
// Unconditionally update labels if present, even dropped or queued RPC's can
|
||||
// use these labels.
|
||||
labels := telemetryLabels(info.Ctx)
|
||||
if labels != nil {
|
||||
maps.Copy(labels, d.telemetryLabels)
|
||||
}
|
||||
stats.UpdateLabels(info.Ctx, d.telemetryLabels)
|
||||
|
||||
// Don't drop unless the inner picker is READY. Similar to
|
||||
// https://github.com/grpc/grpc-go/issues/2622.
|
||||
@@ -167,10 +152,13 @@ func (d *picker) Pick(info balancer.PickInfo) (balancer.PickResult, error) {
|
||||
return pr, err
|
||||
}
|
||||
|
||||
if labels != nil {
|
||||
labels["grpc.lb.locality"] = xdsinternal.LocalityString(lID)
|
||||
labels["grpc.lb.backend_service"] = d.clusterName
|
||||
}
|
||||
stats.UpdateLabels(
|
||||
info.Ctx,
|
||||
map[string]string{
|
||||
"grpc.lb.locality": xdsinternal.LocalityString(lID),
|
||||
"grpc.lb.backend_service": d.clusterName,
|
||||
},
|
||||
)
|
||||
|
||||
if d.loadStore != nil {
|
||||
locality := clients.Locality{Region: lID.Region, Zone: lID.Zone, SubZone: lID.SubZone}
|
||||
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
*
|
||||
* Copyright 2026 gRPC 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 httpfilter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
|
||||
"google.golang.org/grpc/internal/xds/matcher"
|
||||
|
||||
v3mutationpb "github.com/envoyproxy/go-control-plane/envoy/config/common/mutation_rules/v3"
|
||||
v3matcherpb "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3"
|
||||
)
|
||||
|
||||
// HeaderMutationRules specifies the rules for what modifications an external
|
||||
// processing server may make to headers sent on the data plane RPC.
|
||||
type HeaderMutationRules struct {
|
||||
// AllowExpr specifies a regular expression that matches the headers that can
|
||||
// be mutated.
|
||||
AllowExpr *regexp.Regexp
|
||||
// DisallowExpr specifies a regular expression that matches the headers that
|
||||
// cannot be mutated. This overrides the above allowExpr if a header matches
|
||||
// both.
|
||||
DisallowExpr *regexp.Regexp
|
||||
// DisallowAll specifies that no header mutations are allowed. This overrides
|
||||
// all other settings.
|
||||
DisallowAll bool
|
||||
// DisallowIsError specifies whether to return an error if a header mutation
|
||||
// is disallowed. If true, the data plane RPC will be failed with a grpc
|
||||
// status code of Unknown.
|
||||
DisallowIsError bool
|
||||
}
|
||||
|
||||
// ConvertStringMatchers converts a slice of protobuf StringMatcher messages to
|
||||
// a slice of matcher.StringMatcher.
|
||||
func ConvertStringMatchers(patterns []*v3matcherpb.StringMatcher) ([]matcher.StringMatcher, error) {
|
||||
matchers := make([]matcher.StringMatcher, 0, len(patterns))
|
||||
for _, p := range patterns {
|
||||
sm, err := matcher.StringMatcherFromProto(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
matchers = append(matchers, sm)
|
||||
}
|
||||
return matchers, nil
|
||||
}
|
||||
|
||||
// HeaderMutationRulesFromProto converts a protobuf HeaderMutationRules proto
|
||||
// message to a HeaderMutationRules struct.
|
||||
func HeaderMutationRulesFromProto(mr *v3mutationpb.HeaderMutationRules) (HeaderMutationRules, error) {
|
||||
var rules HeaderMutationRules
|
||||
if mr == nil {
|
||||
return rules, nil
|
||||
}
|
||||
if allowExpr := mr.GetAllowExpression(); allowExpr != nil {
|
||||
re, err := matcher.CompileSafeRegex(allowExpr.GetRegex())
|
||||
if err != nil {
|
||||
return rules, fmt.Errorf("httpfilter: %v", err)
|
||||
}
|
||||
rules.AllowExpr = re
|
||||
}
|
||||
if disallowExpr := mr.GetDisallowExpression(); disallowExpr != nil {
|
||||
re, err := matcher.CompileSafeRegex(disallowExpr.GetRegex())
|
||||
if err != nil {
|
||||
return rules, fmt.Errorf("httpfilter: %v", err)
|
||||
}
|
||||
rules.DisallowExpr = re
|
||||
}
|
||||
rules.DisallowAll = mr.GetDisallowAll().GetValue()
|
||||
rules.DisallowIsError = mr.GetDisallowIsError().GetValue()
|
||||
return rules, nil
|
||||
}
|
||||
+10
@@ -31,6 +31,16 @@ type FilterConfig interface {
|
||||
isFilterConfig()
|
||||
}
|
||||
|
||||
// DisabledFilterConfig represents a disabled filter override. It implements the
|
||||
// FilterConfig interface and can be returned by ParseFilterConfigOverride to
|
||||
// indicate that the filter should be disabled. It is not used as a config for
|
||||
// any filter, and is only used as a marker in the override configuration. For
|
||||
// more information, see
|
||||
// envoyproxy.io/docs/envoy/latest/intro/arch_overview/http/http_filters#route-based-filter-chain
|
||||
type DisabledFilterConfig struct{}
|
||||
|
||||
func (DisabledFilterConfig) isFilterConfig() {}
|
||||
|
||||
// Builder defines the parsing functionality of an HTTP filter. A Builder may
|
||||
// optionally implement either ClientFilterBuilder or ServerFilterBuilder or
|
||||
// both, indicating it is capable of working on the client side or server side
|
||||
|
||||
+1
-2
@@ -24,7 +24,6 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"google.golang.org/grpc/internal/grpcutil"
|
||||
"google.golang.org/grpc/metadata"
|
||||
)
|
||||
|
||||
@@ -94,7 +93,7 @@ func (hrm *HeaderRegexMatcher) Match(md metadata.MD) bool {
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return grpcutil.FullMatchWithRegex(hrm.re, v) != hrm.invert
|
||||
return hrm.re.MatchString(v) != hrm.invert
|
||||
}
|
||||
|
||||
func (hrm *HeaderRegexMatcher) String() string {
|
||||
|
||||
+14
-4
@@ -27,7 +27,6 @@ import (
|
||||
"strings"
|
||||
|
||||
v3matcherpb "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3"
|
||||
"google.golang.org/grpc/internal/grpcutil"
|
||||
)
|
||||
|
||||
// StringMatcher contains match criteria for matching a string, and is an
|
||||
@@ -70,7 +69,7 @@ func (sm StringMatcher) Match(input string) bool {
|
||||
}
|
||||
return strings.Contains(input, *sm.containsMatch)
|
||||
case sm.regexMatch != nil:
|
||||
return grpcutil.FullMatchWithRegex(sm.regexMatch, input)
|
||||
return sm.regexMatch.MatchString(input)
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -116,11 +115,11 @@ func StringMatcherFromProto(matcherProto *v3matcherpb.StringMatcher) (StringMatc
|
||||
matcher.suffixMatch = newStrPtr(&mt.Suffix, matcher.ignoreCase)
|
||||
case *v3matcherpb.StringMatcher_SafeRegex:
|
||||
regex := matcherProto.GetSafeRegex().GetRegex()
|
||||
re, err := regexp.Compile(regex)
|
||||
re, err := CompileSafeRegex(regex)
|
||||
if err != nil {
|
||||
return StringMatcher{}, fmt.Errorf("safe_regex matcher %q is invalid", regex)
|
||||
}
|
||||
matcher.regexMatch = re
|
||||
matcher = NewRegexStringMatcher(re)
|
||||
case *v3matcherpb.StringMatcher_Contains:
|
||||
if matcherProto.GetContains() == "" {
|
||||
return StringMatcher{}, errors.New("empty contains is not allowed in StringMatcher")
|
||||
@@ -217,3 +216,14 @@ func (sm StringMatcher) Equal(other StringMatcher) bool {
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// CompileSafeRegex attempts to compile the provided pattern as a regular
|
||||
// expression. It first compiles the unanchored pattern to catch syntax errors
|
||||
// and then compiles and returns the explicitly anchored pattern to guarantee
|
||||
// full-string matching.
|
||||
func CompileSafeRegex(pattern string) (*regexp.Regexp, error) {
|
||||
if _, err := regexp.Compile(pattern); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return regexp.Compile(fmt.Sprintf("^(?:%s)$", pattern))
|
||||
}
|
||||
|
||||
+77
-23
@@ -21,7 +21,6 @@ import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/netip"
|
||||
"regexp"
|
||||
|
||||
v3corepb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
|
||||
v3rbacpb "github.com/envoyproxy/go-control-plane/envoy/config/rbac/v3"
|
||||
@@ -79,7 +78,7 @@ func (pm *policyMatcher) match(data *rpcData) bool {
|
||||
func matchersFromPermissions(permissions []*v3rbacpb.Permission) ([]matcher, error) {
|
||||
var matchers []matcher
|
||||
for _, permission := range permissions {
|
||||
switch permission.GetRule().(type) {
|
||||
switch p := permission.GetRule().(type) {
|
||||
case *v3rbacpb.Permission_AndRules:
|
||||
mList, err := matchersFromPermissions(permission.GetAndRules().Rules)
|
||||
if err != nil {
|
||||
@@ -121,16 +120,24 @@ func matchersFromPermissions(permissions []*v3rbacpb.Permission) ([]matcher, err
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(mList) != 1 {
|
||||
return nil, fmt.Errorf("NotRule must contain exactly one rule")
|
||||
}
|
||||
matchers = append(matchers, ¬Matcher{matcherToNot: mList[0]})
|
||||
case *v3rbacpb.Permission_Metadata:
|
||||
// Never matches - so no-op if not inverted, always match if
|
||||
// inverted.
|
||||
if permission.GetMetadata().GetInvert() { // Test metadata being no-op and also metadata with invert always matching
|
||||
if permission.GetMetadata().GetInvert() {
|
||||
matchers = append(matchers, &alwaysMatcher{})
|
||||
} else {
|
||||
matchers = append(matchers, &neverMatcher{})
|
||||
}
|
||||
case *v3rbacpb.Permission_RequestedServerName:
|
||||
// Not supported in gRPC RBAC currently - a permission typed as
|
||||
// requested server name in the initial config will be a no-op.
|
||||
m, err := newRequestedServerNameMatcher(permission.GetRequestedServerName())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
matchers = append(matchers, m)
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported permission rule type: %T", p)
|
||||
}
|
||||
}
|
||||
return matchers, nil
|
||||
@@ -139,7 +146,7 @@ func matchersFromPermissions(permissions []*v3rbacpb.Permission) ([]matcher, err
|
||||
func matchersFromPrincipals(principals []*v3rbacpb.Principal) ([]matcher, error) {
|
||||
var matchers []matcher
|
||||
for _, principal := range principals {
|
||||
switch principal.GetIdentifier().(type) {
|
||||
switch p := principal.GetIdentifier().(type) {
|
||||
case *v3rbacpb.Principal_AndIds:
|
||||
mList, err := matchersFromPrincipals(principal.GetAndIds().Ids)
|
||||
if err != nil {
|
||||
@@ -179,16 +186,29 @@ func matchersFromPrincipals(principals []*v3rbacpb.Principal) ([]matcher, error)
|
||||
return nil, err
|
||||
}
|
||||
matchers = append(matchers, m)
|
||||
case *v3rbacpb.Principal_Metadata:
|
||||
if principal.GetMetadata().GetInvert() {
|
||||
matchers = append(matchers, &alwaysMatcher{})
|
||||
} else {
|
||||
matchers = append(matchers, &neverMatcher{})
|
||||
}
|
||||
case *v3rbacpb.Principal_NotId:
|
||||
mList, err := matchersFromPrincipals([]*v3rbacpb.Principal{{Identifier: principal.GetNotId().Identifier}})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(mList) != 1 {
|
||||
return nil, fmt.Errorf("NotId must contain exactly one identifier")
|
||||
}
|
||||
matchers = append(matchers, ¬Matcher{matcherToNot: mList[0]})
|
||||
case *v3rbacpb.Principal_SourceIp:
|
||||
// The source ip principal identifier is deprecated. Thus, a
|
||||
// principal typed as a source ip in the identifier will be a no-op.
|
||||
// The config should use DirectRemoteIp instead.
|
||||
// The source ip principal identifier is deprecated, but gRPC RBAC
|
||||
// treats it as equivalent to direct_remote_ip as per A41.
|
||||
m, err := newRemoteIPMatcher(principal.GetSourceIp())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
matchers = append(matchers, m)
|
||||
case *v3rbacpb.Principal_RemoteIp:
|
||||
// RBAC in gRPC treats direct_remote_ip and remote_ip as logically
|
||||
// equivalent, as per A41.
|
||||
@@ -197,9 +217,8 @@ func matchersFromPrincipals(principals []*v3rbacpb.Principal) ([]matcher, error)
|
||||
return nil, err
|
||||
}
|
||||
matchers = append(matchers, m)
|
||||
case *v3rbacpb.Principal_Metadata:
|
||||
// Not supported in gRPC RBAC currently - a principal typed as
|
||||
// Metadata in the initial config will be a no-op.
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported principal identifier type: %T", p)
|
||||
}
|
||||
}
|
||||
return matchers, nil
|
||||
@@ -249,6 +268,16 @@ func (am *alwaysMatcher) match(*rpcData) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// neverMatcher is a matcher that will never match. This logically represents a
|
||||
// permission or principal that is unsupported in gRPC. neverMatcher implements
|
||||
// the matcher interface.
|
||||
type neverMatcher struct {
|
||||
}
|
||||
|
||||
func (nm *neverMatcher) match(*rpcData) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// notMatcher is a matcher that nots an underlying matcher. notMatcher
|
||||
// implements the matcher interface.
|
||||
type notMatcher struct {
|
||||
@@ -271,7 +300,7 @@ func newHeaderMatcher(headerMatcherConfig *v3route_componentspb.HeaderMatcher) (
|
||||
case *v3route_componentspb.HeaderMatcher_ExactMatch:
|
||||
m = internalmatcher.NewHeaderExactMatcher(headerMatcherConfig.Name, headerMatcherConfig.GetExactMatch(), headerMatcherConfig.InvertMatch)
|
||||
case *v3route_componentspb.HeaderMatcher_SafeRegexMatch:
|
||||
regex, err := regexp.Compile(headerMatcherConfig.GetSafeRegexMatch().Regex)
|
||||
regex, err := internalmatcher.CompileSafeRegex(headerMatcherConfig.GetSafeRegexMatch().GetRegex())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -398,6 +427,25 @@ func (pm *portMatcher) match(data *rpcData) bool {
|
||||
return data.destinationPort == pm.destinationPort
|
||||
}
|
||||
|
||||
// requestedServerNameMatcher matches on if the given string matcher matches
|
||||
// on "", as per A41-xds-rbac.md. requestedServerNameMatcher implements
|
||||
// the matcher interface.
|
||||
type requestedServerNameMatcher struct {
|
||||
stringMatcher internalmatcher.StringMatcher
|
||||
}
|
||||
|
||||
func newRequestedServerNameMatcher(stringMatcherProto *v3matcherpb.StringMatcher) (*requestedServerNameMatcher, error) {
|
||||
stringMatcher, err := internalmatcher.StringMatcherFromProto(stringMatcherProto)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &requestedServerNameMatcher{stringMatcher: stringMatcher}, nil
|
||||
}
|
||||
|
||||
func (r *requestedServerNameMatcher) match(*rpcData) bool {
|
||||
return r.stringMatcher.Match("")
|
||||
}
|
||||
|
||||
// authenticatedMatcher matches on the name of the Principal. If set, the URI
|
||||
// SAN or DNS SAN in that order is used from the certificate, otherwise the
|
||||
// subject field is used. If unset, it applies to any user that is
|
||||
@@ -435,17 +483,23 @@ func (am *authenticatedMatcher) match(data *rpcData) bool {
|
||||
return am.stringMatcher.Match("")
|
||||
}
|
||||
cert := data.certs[0]
|
||||
// The order of matching as per the RBAC documentation (see package-level comments)
|
||||
// is as follows: URI SANs, DNS SANs, and then subject name.
|
||||
for _, uriSAN := range cert.URIs {
|
||||
if am.stringMatcher.Match(uriSAN.String()) {
|
||||
return true
|
||||
// Use the first non-empty identity source in priority order:
|
||||
// URI SANs, then DNS SANs, then Subject.
|
||||
if len(cert.URIs) > 0 {
|
||||
for _, uriSAN := range cert.URIs {
|
||||
if am.stringMatcher.Match(uriSAN.String()) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
for _, dnsSAN := range cert.DNSNames {
|
||||
if am.stringMatcher.Match(dnsSAN) {
|
||||
return true
|
||||
if len(cert.DNSNames) > 0 {
|
||||
for _, dnsSAN := range cert.DNSNames {
|
||||
if am.stringMatcher.Match(dnsSAN) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
return am.stringMatcher.Match(cert.Subject.String())
|
||||
}
|
||||
|
||||
+9
-15
@@ -24,7 +24,6 @@ import (
|
||||
"math/bits"
|
||||
rand "math/rand/v2"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
xxhash "github.com/cespare/xxhash/v2"
|
||||
@@ -196,16 +195,13 @@ func (cs *configSelector) SelectConfig(rpcInfo iresolver.RPCInfo) (*iresolver.RP
|
||||
return nil, annotateErrorWithNodeID(status.Errorf(codes.Internal, "error retrieving cluster for match: %v (%T)", cluster, cluster), cs.xdsNodeID)
|
||||
}
|
||||
|
||||
// Add a ref to the selected cluster, as this RPC needs this cluster until
|
||||
// it is committed.
|
||||
var ref *int32
|
||||
// Add a ref to the selected cluster/plugin, as this RPC needs this
|
||||
// cluster/plugin until it is committed.
|
||||
if info, ok := cs.clusters[cluster.name]; ok {
|
||||
ref = &info.refCount
|
||||
info.refCount.Add(1)
|
||||
} else if info, ok := cs.plugins[cluster.name]; ok {
|
||||
info.refCount.Add(1)
|
||||
}
|
||||
if info, ok := cs.plugins[cluster.name]; ok {
|
||||
ref = &info.refCount
|
||||
}
|
||||
atomic.AddInt32(ref, 1)
|
||||
|
||||
lbCtx := clustermanager.SetPickedCluster(rpcInfo.Context, cluster.name)
|
||||
lbCtx = xdsresource.NewContextWithXDSConfig(lbCtx, cs.xdsConfig)
|
||||
@@ -221,8 +217,7 @@ func (cs *configSelector) SelectConfig(rpcInfo iresolver.RPCInfo) (*iresolver.RP
|
||||
// When the RPC is committed, the cluster is no longer required.
|
||||
// Decrease its ref.
|
||||
if info, ok := cs.clusters[cluster.name]; ok {
|
||||
ref := &info.refCount
|
||||
if v := atomic.AddInt32(ref, -1); v == 0 {
|
||||
if v := info.refCount.Add(-1); v == 0 {
|
||||
// We call unsubscribe rather than sendNewServiceConfig to
|
||||
// prevent redundant updates. If the reference count in the
|
||||
// dependency manager drops to zero, it will automatically
|
||||
@@ -233,8 +228,7 @@ func (cs *configSelector) SelectConfig(rpcInfo iresolver.RPCInfo) (*iresolver.RP
|
||||
}
|
||||
}
|
||||
if info, ok := cs.plugins[cluster.name]; ok {
|
||||
ref := &info.refCount
|
||||
if v := atomic.AddInt32(ref, -1); v == 0 {
|
||||
if v := info.refCount.Add(-1); v == 0 {
|
||||
// This entry will be removed from activePlugins when
|
||||
// producing a new service config update.
|
||||
cs.sendNewServiceConfig()
|
||||
@@ -350,12 +344,12 @@ func (cs *configSelector) stop() {
|
||||
// after a new one is active, we must trigger a subsequent update to delete
|
||||
// the now-unused clusters.
|
||||
for _, ci := range cs.clusters {
|
||||
if v := atomic.AddInt32(&ci.refCount, -1); v == 0 {
|
||||
if v := ci.refCount.Add(-1); v == 0 {
|
||||
ci.unsubscribe()
|
||||
}
|
||||
}
|
||||
for _, ci := range cs.plugins {
|
||||
if v := atomic.AddInt32(&ci.refCount, -1); v == 0 {
|
||||
if v := ci.refCount.Add(-1); v == 0 {
|
||||
cs.sendNewServiceConfig()
|
||||
}
|
||||
}
|
||||
|
||||
+41
-7
@@ -415,9 +415,26 @@ func (r *xdsResolver) newConfigSelector() (_ *configSelector, err error) {
|
||||
for i, rt := range r.xdsConfig.VirtualHost.Routes {
|
||||
clusters := rinternal.NewWRR.(func() wrr.WRR)()
|
||||
interceptors := []iresolver.ClientInterceptor{}
|
||||
// TODO: Carve out the common logic between the ClusterSpecifierPlugin
|
||||
// and WeightedClusters.
|
||||
if rt.ClusterSpecifierPlugin != "" {
|
||||
clusterName := clusterSpecifierPluginPrefix + rt.ClusterSpecifierPlugin
|
||||
clusters.Add(&routeCluster{name: clusterName}, 1)
|
||||
interceptor, err := r.newInterceptor(r.xdsConfig.Listener.APIListener.HTTPFilters, nil, rt.HTTPFilterConfigOverride, r.xdsConfig.VirtualHost.HTTPFilterConfigOverride)
|
||||
if err != nil {
|
||||
// Clean up any interceptors that were successfully built
|
||||
// for the current route before this error occurred. Note
|
||||
// that this is not handled by the call to cs.stop() in the
|
||||
// deferred function.
|
||||
for _, i := range interceptors {
|
||||
i.Close()
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
clusters.Add(&routeCluster{
|
||||
name: clusterName,
|
||||
interceptor: interceptor,
|
||||
}, 1)
|
||||
interceptors = append(interceptors, interceptor)
|
||||
ci := r.addOrGetActiveClusterInfo(clusterName, "")
|
||||
ci.cfg = xdsChildConfig{ChildPolicy: balancerConfig(r.xdsConfig.RouteConfig.ClusterSpecifierPlugins[rt.ClusterSpecifierPlugin])}
|
||||
cs.plugins[clusterName] = ci
|
||||
@@ -464,10 +481,10 @@ func (r *xdsResolver) newConfigSelector() (_ *configSelector, err error) {
|
||||
// errors may occur. Note: cs.clusters are pointers to entries in
|
||||
// activeClusters.
|
||||
for _, ci := range cs.clusters {
|
||||
atomic.AddInt32(&ci.refCount, 1)
|
||||
ci.refCount.Add(1)
|
||||
}
|
||||
for _, ci := range cs.plugins {
|
||||
atomic.AddInt32(&ci.refCount, 1)
|
||||
ci.refCount.Add(1)
|
||||
}
|
||||
|
||||
// Cleanup filter instances that are no longer specified in the current
|
||||
@@ -496,13 +513,13 @@ func (r *xdsResolver) newConfigSelector() (_ *configSelector, err error) {
|
||||
// Only executed in the context of a serializer callback.
|
||||
func (r *xdsResolver) pruneActiveClustersAndPlugins() {
|
||||
for cluster, ci := range r.activeClusters {
|
||||
if atomic.LoadInt32(&ci.refCount) == 0 {
|
||||
if ci.refCount.Load() == 0 {
|
||||
ci.unsubscribe()
|
||||
delete(r.activeClusters, cluster)
|
||||
}
|
||||
}
|
||||
for cluster, ci := range r.activePlugins {
|
||||
if atomic.LoadInt32(&ci.refCount) == 0 {
|
||||
if ci.refCount.Load() == 0 {
|
||||
delete(r.activePlugins, cluster)
|
||||
}
|
||||
}
|
||||
@@ -535,8 +552,8 @@ func (r *xdsResolver) addOrGetActiveClusterInfo(key string, name string) *cluste
|
||||
}
|
||||
|
||||
type clusterInfo struct {
|
||||
// number of references to this cluster; accessed atomically
|
||||
refCount int32
|
||||
// refCount is the number of references to this cluster.
|
||||
refCount atomic.Int32
|
||||
// cfg is the child configuration for this cluster, containing either the
|
||||
// csp config or the cds cluster config.
|
||||
cfg xdsChildConfig
|
||||
@@ -596,6 +613,23 @@ func (r *xdsResolver) newInterceptor(filters []xdsresource.HTTPFilter, clusterOv
|
||||
if override == nil {
|
||||
override = virtualHostOverride[filter.Name]
|
||||
}
|
||||
|
||||
// Determine the effective disabled state of the filter. The base
|
||||
// configuration's disabled state is used unless an override is present.
|
||||
// If an override is present, the filter is disabled if the override is
|
||||
// a DisabledFilterConfig.
|
||||
disabled := filter.Disabled
|
||||
if override != nil {
|
||||
_, disabled = override.(httpfilter.DisabledFilterConfig)
|
||||
}
|
||||
|
||||
if disabled {
|
||||
if r.logger.V(2) {
|
||||
r.logger.Infof("Filter %q has been disabled.", filter.Name)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
builder, ok := filter.Filter.(httpfilter.ClientFilterBuilder)
|
||||
if !ok {
|
||||
// Should not happen if it passed xdsClient validation.
|
||||
|
||||
+78
-25
@@ -122,17 +122,7 @@ func (fcm *filterChainManager) filterChainFromConfig(config *xdsresource.Network
|
||||
|
||||
func (fcm *filterChainManager) stop() {
|
||||
for _, fc := range fcm.filterChains {
|
||||
urc := fc.usableRouteConfiguration.Load()
|
||||
if urc.err != nil {
|
||||
continue
|
||||
}
|
||||
for _, vh := range urc.vhs {
|
||||
for _, r := range vh.routes {
|
||||
if r.interceptor != nil {
|
||||
r.interceptor.Close()
|
||||
}
|
||||
}
|
||||
}
|
||||
fc.usableRouteConfiguration.Load().stop()
|
||||
fc.stop()
|
||||
}
|
||||
}
|
||||
@@ -172,11 +162,20 @@ type sourcePrefixEntry struct {
|
||||
// Listener resource. This struct contains the active state of a filter chain,
|
||||
// which includes the usable route configuration.
|
||||
type filterChain struct {
|
||||
securityCfg *xdsresource.SecurityConfig
|
||||
httpFilters []xdsresource.HTTPFilter
|
||||
serverFilters []httpfilter.ServerFilter // Server filters with reference counts, stored for cleanup purposes.
|
||||
routeConfigName string
|
||||
inlineRouteConfig *xdsresource.RouteConfigUpdate
|
||||
// The following fields are set at initialization time, and are not
|
||||
// updated afterwards.
|
||||
securityCfg *xdsresource.SecurityConfig
|
||||
httpFilters []xdsresource.HTTPFilter
|
||||
routeConfigName string
|
||||
inlineRouteConfig *xdsresource.RouteConfigUpdate
|
||||
|
||||
// Server filters with reference counts. Is updated when a usable route
|
||||
// configuration is set, and when the filter chain is stopped. Both these
|
||||
// operations always happen with the listener wrapper's lock held.
|
||||
serverFilters []httpfilter.ServerFilter
|
||||
|
||||
// The usable route configuration, is passed to the connWrapper, and is used
|
||||
// to route incoming RPCs. Therefore, this needs to be accessed atomically.
|
||||
usableRouteConfiguration *atomic.Pointer[usableRouteConfiguration]
|
||||
}
|
||||
|
||||
@@ -188,6 +187,16 @@ type usableRouteConfiguration struct {
|
||||
nodeID string // For logging purposes. Populated by the listener wrapper.
|
||||
}
|
||||
|
||||
func (rc *usableRouteConfiguration) stop() {
|
||||
for _, vh := range rc.vhs {
|
||||
for _, r := range vh.routes {
|
||||
if r.interceptor != nil {
|
||||
r.interceptor.Close()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// virtualHostWithInterceptors captures information present in a VirtualHost
|
||||
// update, and also contains routes with instantiated HTTP Filters.
|
||||
type virtualHostWithInterceptors struct {
|
||||
@@ -397,32 +406,61 @@ func (fc *filterChain) stop() {
|
||||
// state across resource updates.
|
||||
type serverFilterProvider func(filter xdsresource.HTTPFilter) (httpfilter.ServerFilter, error)
|
||||
|
||||
// constructUsableRouteConfiguration takes Route Configuration and converts it
|
||||
// updateUsableRouteConfiguration takes Route Configuration and converts it
|
||||
// into matchable route configuration, with instantiated HTTP Filters per route.
|
||||
func (fc *filterChain) constructUsableRouteConfiguration(config xdsresource.RouteConfigUpdate, provider serverFilterProvider) *usableRouteConfiguration {
|
||||
vhs := make([]virtualHostWithInterceptors, 0, len(config.VirtualHosts))
|
||||
func (fc *filterChain) updateUsableRouteConfiguration(config *xdsresource.RouteConfigUpdate, updateErr error, provider serverFilterProvider, nodeID string) {
|
||||
if updateErr != nil {
|
||||
urc := &usableRouteConfiguration{err: updateErr, nodeID: nodeID}
|
||||
fc.applyConfiguration(urc, nil)
|
||||
return
|
||||
}
|
||||
|
||||
var serverFilters []httpfilter.ServerFilter
|
||||
vhs := make([]virtualHostWithInterceptors, 0, len(config.VirtualHosts))
|
||||
for _, vh := range config.VirtualHosts {
|
||||
vhwi, sfs, err := fc.convertVirtualHost(vh, provider)
|
||||
if err != nil {
|
||||
for _, sf := range serverFilters {
|
||||
sf.Close()
|
||||
}
|
||||
// Close interceptors from successfully converted virtual hosts.
|
||||
for _, v := range vhs {
|
||||
for _, r := range v.routes {
|
||||
if r.interceptor != nil {
|
||||
r.interceptor.Close()
|
||||
r.interceptor = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
// Non nil if (lds + rds) fails, shouldn't happen since validated by
|
||||
// xDS Client, treat as L7 error but shouldn't happen.
|
||||
return &usableRouteConfiguration{err: fmt.Errorf("virtual host construction: %v", err)}
|
||||
urc := &usableRouteConfiguration{err: fmt.Errorf("virtual host construction: %v", err), nodeID: nodeID}
|
||||
fc.applyConfiguration(urc, nil)
|
||||
return
|
||||
}
|
||||
vhs = append(vhs, vhwi)
|
||||
serverFilters = append(serverFilters, sfs...)
|
||||
}
|
||||
|
||||
// Release references to old server filters before replacing with new ones.
|
||||
for _, sf := range fc.serverFilters {
|
||||
sf.Close()
|
||||
}
|
||||
urc := &usableRouteConfiguration{vhs: vhs, nodeID: nodeID}
|
||||
fc.applyConfiguration(urc, serverFilters)
|
||||
}
|
||||
|
||||
func (fc *filterChain) applyConfiguration(urc *usableRouteConfiguration, serverFilters []httpfilter.ServerFilter) {
|
||||
// Swap in the new configuration first so new RPCs use it immediately.
|
||||
oldURC := fc.usableRouteConfiguration.Swap(urc)
|
||||
oldFilters := fc.serverFilters
|
||||
fc.serverFilters = serverFilters
|
||||
|
||||
return &usableRouteConfiguration{vhs: vhs}
|
||||
// Stop the old interceptors before releasing the filters they might depend on.
|
||||
if oldURC != nil {
|
||||
oldURC.stop()
|
||||
}
|
||||
|
||||
// Release references to old server filters.
|
||||
for _, sf := range oldFilters {
|
||||
sf.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func (fc *filterChain) convertVirtualHost(virtualHost *xdsresource.VirtualHost, provider serverFilterProvider) (_ virtualHostWithInterceptors, _ []httpfilter.ServerFilter, err error) {
|
||||
@@ -441,6 +479,13 @@ func (fc *filterChain) convertVirtualHost(virtualHost *xdsresource.VirtualHost,
|
||||
rs[i].matcher = xdsresource.RouteToMatcher(r)
|
||||
interceptor, sfs, err := fc.newInterceptor(r.HTTPFilterConfigOverride, virtualHost.HTTPFilterConfigOverride, provider)
|
||||
if err != nil {
|
||||
// Close interceptors from successfully converted routes.
|
||||
for _, route := range rs {
|
||||
if route.interceptor != nil {
|
||||
route.interceptor.Close()
|
||||
route.interceptor = nil
|
||||
}
|
||||
}
|
||||
return virtualHostWithInterceptors{}, nil, err
|
||||
}
|
||||
serverFilters = append(serverFilters, sfs...)
|
||||
@@ -478,6 +523,14 @@ func (fc *filterChain) newInterceptor(routeOverride, virtualHostOverride map[str
|
||||
override = virtualHostOverride[filter.Name]
|
||||
}
|
||||
|
||||
disabled := filter.Disabled
|
||||
if override != nil {
|
||||
_, disabled = override.(httpfilter.DisabledFilterConfig)
|
||||
}
|
||||
if disabled {
|
||||
continue
|
||||
}
|
||||
|
||||
serverFilter, err := provider(filter)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
|
||||
+6
-16
@@ -154,7 +154,6 @@ func (l *listenerWrapper) maybeUpdateFilterChains() {
|
||||
}
|
||||
|
||||
l.mu.Lock()
|
||||
l.switchModeLocked(connectivity.ServingModeServing, nil)
|
||||
// "Updates to a Listener cause all older connections on that Listener to be
|
||||
// gracefully shut down with a grace period of 10 minutes for long-lived
|
||||
// RPC's, such that clients will reconnect and have the updated
|
||||
@@ -189,6 +188,7 @@ func (l *listenerWrapper) maybeUpdateFilterChains() {
|
||||
delete(l.httpFilters, key)
|
||||
}
|
||||
}
|
||||
l.switchModeLocked(connectivity.ServingModeServing, nil)
|
||||
l.mu.Unlock()
|
||||
|
||||
go func() {
|
||||
@@ -210,14 +210,10 @@ func (l *listenerWrapper) handleRDSUpdate(routeName string, rcu rdsWatcherUpdate
|
||||
continue
|
||||
}
|
||||
if rcu.err != nil && rcu.data == nil { // Either NACK before update, or resource not found triggers this conditional.
|
||||
urc := &usableRouteConfiguration{err: rcu.err}
|
||||
urc.nodeID = l.xdsNodeID
|
||||
fc.usableRouteConfiguration.Store(urc)
|
||||
fc.updateUsableRouteConfiguration(nil, rcu.err, l.getOrCreateServerFilterLocked, l.xdsNodeID)
|
||||
continue
|
||||
}
|
||||
urc := fc.constructUsableRouteConfiguration(*rcu.data, l.getOrCreateServerFilterLocked)
|
||||
urc.nodeID = l.xdsNodeID
|
||||
fc.usableRouteConfiguration.Store(urc)
|
||||
fc.updateUsableRouteConfiguration(rcu.data, nil, l.getOrCreateServerFilterLocked, l.xdsNodeID)
|
||||
}
|
||||
}
|
||||
l.mu.Unlock()
|
||||
@@ -235,21 +231,15 @@ func (l *listenerWrapper) handleRDSUpdate(routeName string, rcu rdsWatcherUpdate
|
||||
func (l *listenerWrapper) instantiateFilterChainRoutingConfigurationsLocked() {
|
||||
for _, fc := range l.activeFilterChainManager.filterChains {
|
||||
if fc.inlineRouteConfig != nil {
|
||||
urc := fc.constructUsableRouteConfiguration(*fc.inlineRouteConfig, l.getOrCreateServerFilterLocked)
|
||||
urc.nodeID = l.xdsNodeID
|
||||
fc.usableRouteConfiguration.Store(urc) // Can't race with an RPC coming in but no harm making atomic.
|
||||
fc.updateUsableRouteConfiguration(fc.inlineRouteConfig, nil, l.getOrCreateServerFilterLocked, l.xdsNodeID)
|
||||
continue
|
||||
} // Inline configuration constructed once here, will remain for lifetime of filter chain.
|
||||
rcu := l.rdsHandler.updates[fc.routeConfigName]
|
||||
if rcu.err != nil && rcu.data == nil {
|
||||
urc := &usableRouteConfiguration{err: rcu.err}
|
||||
urc.nodeID = l.xdsNodeID
|
||||
fc.usableRouteConfiguration.Store(urc)
|
||||
fc.updateUsableRouteConfiguration(nil, rcu.err, l.getOrCreateServerFilterLocked, l.xdsNodeID)
|
||||
continue
|
||||
}
|
||||
urc := fc.constructUsableRouteConfiguration(*rcu.data, l.getOrCreateServerFilterLocked)
|
||||
urc.nodeID = l.xdsNodeID
|
||||
fc.usableRouteConfiguration.Store(urc) // Can't race with an RPC coming in but no harm making atomic.
|
||||
fc.updateUsableRouteConfiguration(rcu.data, nil, l.getOrCreateServerFilterLocked, l.xdsNodeID)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
*
|
||||
* Copyright 2026 gRPC 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 xdsresource
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc/metadata"
|
||||
)
|
||||
|
||||
// GRPCServiceConfig contains the configuration for an external server. It is
|
||||
// the parsed configuration for the GrpcService proto message.
|
||||
// See: https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/core/v3/grpc_service.proto
|
||||
type GRPCServiceConfig struct {
|
||||
// TargetURI is the name of the external server.
|
||||
TargetURI string
|
||||
// ChannelCredentials specifies the configuration for the transport
|
||||
// credentials to use to connect to the external server, as a JSON string.
|
||||
ChannelCredentials string
|
||||
// CallCredentials specifies the configuration for the per-RPC credentials to
|
||||
// use when making calls to the external server, as a JSON string.
|
||||
CallCredentials string
|
||||
// Timeout is the RPC timeout for the call to the external server. If unset,
|
||||
// the timeout depends on the usage of this external server. For example,
|
||||
// cases like ext_authz and ext_proc, where there is a 1:1 mapping between the
|
||||
// data plane RPC and the external server call, the timeout will be capped by
|
||||
// the timeout on the data plane RPC. For cases like RLQS where there is a
|
||||
// side channel to the external server, an unset timeout will result in no
|
||||
// timeout being applied to the external server call.
|
||||
Timeout time.Duration
|
||||
// InitialMetadata is the additional metadata to include in all RPCs sent to
|
||||
// the external server.
|
||||
InitialMetadata metadata.MD
|
||||
}
|
||||
+1
-3
@@ -20,8 +20,6 @@ package xdsresource
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"google.golang.org/grpc/internal/grpcutil"
|
||||
)
|
||||
|
||||
type pathMatcher interface {
|
||||
@@ -94,7 +92,7 @@ func newPathRegexMatcher(re *regexp.Regexp) *pathRegexMatcher {
|
||||
}
|
||||
|
||||
func (prm *pathRegexMatcher) match(path string) bool {
|
||||
return grpcutil.FullMatchWithRegex(prm.re, path)
|
||||
return prm.re.MatchString(path)
|
||||
}
|
||||
|
||||
func (prm *pathRegexMatcher) String() string {
|
||||
|
||||
+31
-1
@@ -21,15 +21,20 @@ import (
|
||||
"fmt"
|
||||
"net/netip"
|
||||
|
||||
v3corepb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
|
||||
"google.golang.org/grpc/internal/envconfig"
|
||||
"google.golang.org/protobuf/types/known/anypb"
|
||||
|
||||
v3corepb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
|
||||
v3gcpauthnpb "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/gcp_authn/v3"
|
||||
)
|
||||
|
||||
func init() {
|
||||
if envconfig.XDSHTTPConnectEnabled {
|
||||
registerMetadataConverter("type.googleapis.com/envoy.config.core.v3.Address", proxyAddressConvertor{})
|
||||
}
|
||||
if envconfig.GCPAuthenticationFilterEnabled {
|
||||
registerMetadataConverter("type.googleapis.com/envoy.extensions.filters.http.gcp_authn.v3.Audience", audienceConverter{})
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -100,3 +105,28 @@ func (proxyAddressConvertor) convert(anyProto *anypb.Any) (any, error) {
|
||||
}
|
||||
return ProxyAddressMetadataValue{Address: parseAddress(socketaddress)}, nil
|
||||
}
|
||||
|
||||
// AudienceMetadataValue holds the audience parsed from the
|
||||
// envoy.extensions.filters.http.gcp_authn.v3.Audience proto message, as
|
||||
// specified in gRFC A83.
|
||||
type AudienceMetadataValue struct {
|
||||
// Audience is the URL of the receiving service that performs token
|
||||
// authentication.
|
||||
Audience string
|
||||
}
|
||||
|
||||
// audienceConverter implements the metadataConverter interface to
|
||||
// handle the conversion of envoy.extensions.filters.http.gcp_authn.v3.Audience
|
||||
// protobuf messages into an internal representation.
|
||||
type audienceConverter struct{}
|
||||
|
||||
func (audienceConverter) convert(anyProto *anypb.Any) (any, error) {
|
||||
audienceProto := &v3gcpauthnpb.Audience{}
|
||||
if err := anyProto.UnmarshalTo(audienceProto); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal the envoy.extensions.filters.http.gcp_authn.v3.Audience resource from Any proto: %v", err)
|
||||
}
|
||||
if audienceProto.GetUrl() == "" {
|
||||
return nil, fmt.Errorf("empty url field in audience metadata")
|
||||
}
|
||||
return AudienceMetadataValue{Audience: audienceProto.GetUrl()}, nil
|
||||
}
|
||||
|
||||
+3
@@ -83,6 +83,9 @@ type ClusterUpdate struct {
|
||||
// LRSReportEndpointMetrics specifies the subset of ORCA metrics that
|
||||
// should be propagated to the LRS server.
|
||||
LRSReportEndpointMetrics *LRSReportEndpointMetricsConfig
|
||||
|
||||
// Metadata contains the metadata from the cluster resource.
|
||||
Metadata map[string]any
|
||||
}
|
||||
|
||||
// LRSReportEndpointMetricsConfig holds the configuration for propagating ORCA
|
||||
|
||||
+3
@@ -69,6 +69,9 @@ type HTTPFilter struct {
|
||||
Filter httpfilter.Builder
|
||||
// Config contains the filter's configuration
|
||||
Config httpfilter.FilterConfig
|
||||
// Disabled specifies if the filter is disabled. For more information, see
|
||||
// envoyproxy.io/docs/envoy/latest/intro/arch_overview/http/http_filters#route-based-filter-chain
|
||||
Disabled bool
|
||||
}
|
||||
|
||||
// InboundListenerConfig contains information about the inbound listener, i.e
|
||||
|
||||
Generated
Vendored
+9
@@ -215,6 +215,14 @@ func validateClusterAndConstructClusterUpdate(cluster *v3clusterpb.Cluster, serv
|
||||
}
|
||||
}
|
||||
|
||||
var metadata map[string]any
|
||||
if envconfig.GCPAuthenticationFilterEnabled {
|
||||
var err error
|
||||
if metadata, err = validateAndConstructMetadata(cluster.GetMetadata()); err != nil {
|
||||
return ClusterUpdate{}, err
|
||||
}
|
||||
}
|
||||
|
||||
ret := ClusterUpdate{
|
||||
ClusterName: cluster.GetName(),
|
||||
SecurityCfg: sc,
|
||||
@@ -223,6 +231,7 @@ func validateClusterAndConstructClusterUpdate(cluster *v3clusterpb.Cluster, serv
|
||||
OutlierDetection: od,
|
||||
TelemetryLabels: telemetryLabels,
|
||||
LRSReportEndpointMetrics: lrsReportEndpointMetrics,
|
||||
Metadata: metadata,
|
||||
}
|
||||
|
||||
if lrs := cluster.GetLrsServer(); lrs != nil {
|
||||
|
||||
Generated
Vendored
+9
-21
@@ -26,6 +26,7 @@ import (
|
||||
v3corepb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
|
||||
v3endpointpb "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3"
|
||||
v3typepb "github.com/envoyproxy/go-control-plane/envoy/type/v3"
|
||||
"google.golang.org/grpc/experimental/balancer/hostname"
|
||||
"google.golang.org/grpc/internal/envconfig"
|
||||
"google.golang.org/grpc/internal/pretty"
|
||||
xdsinternal "google.golang.org/grpc/internal/xds"
|
||||
@@ -36,27 +37,12 @@ import (
|
||||
"google.golang.org/protobuf/types/known/anypb"
|
||||
)
|
||||
|
||||
// hostnameKeyType is the key to store the hostname attribute in
|
||||
// a resolver.Endpoint.
|
||||
type hostnameKeyType struct{}
|
||||
|
||||
// SetHostname returns a copy of the given endpoint with hostname added
|
||||
// as an attribute.
|
||||
func SetHostname(endpoint resolver.Endpoint, hostname string) resolver.Endpoint {
|
||||
// Only set if non-empty; xds_cluster_impl uses this to trigger :authority
|
||||
// rewriting.
|
||||
if hostname == "" {
|
||||
return endpoint
|
||||
}
|
||||
endpoint.Attributes = endpoint.Attributes.WithValue(hostnameKeyType{}, hostname)
|
||||
return endpoint
|
||||
}
|
||||
|
||||
// Hostname returns the hostname from the BalancerAttributes of the given
|
||||
// Address. If this attribute is not set, it returns the empty string.
|
||||
// Hostname returns the hostname from the BalancerAttributes of the
|
||||
// given Address. If this attribute is not set, it returns the empty
|
||||
// string.
|
||||
func Hostname(addr resolver.Address) string {
|
||||
hostname, _ := addr.BalancerAttributes.Value(hostnameKeyType{}).(string)
|
||||
return hostname
|
||||
ep := resolver.Endpoint{Attributes: addr.BalancerAttributes}
|
||||
return hostname.FromEndpoint(ep)
|
||||
}
|
||||
|
||||
func unmarshalEndpointsResource(r *anypb.Any) (string, EndpointsUpdate, error) {
|
||||
@@ -166,7 +152,7 @@ func parseEndpoints(lbEndpoints []*v3endpointpb.LbEndpoint, uniqueEndpointAddrs
|
||||
}
|
||||
}
|
||||
endpoint := resolver.Endpoint{Addresses: address}
|
||||
endpoint = SetHostname(endpoint, lbEndpoint.GetEndpoint().GetHostname())
|
||||
endpoint = hostname.Set(endpoint, lbEndpoint.GetEndpoint().GetHostname())
|
||||
endpoint = ringhash.SetHashKey(endpoint, hashKey)
|
||||
endpoints = append(endpoints, Endpoint{
|
||||
ResolverEndpoint: endpoint,
|
||||
@@ -268,6 +254,8 @@ func parseEDSRespProto(m *v3endpointpb.ClusterLoadAssignment) (EndpointsUpdate,
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// validateAndConstructMetadata processes the metadata from the xDS resource
|
||||
// and returns a map of parsed metadata values.
|
||||
func validateAndConstructMetadata(metadataProto *v3corepb.Metadata) (map[string]any, error) {
|
||||
if metadataProto == nil {
|
||||
return nil, nil
|
||||
|
||||
Generated
Vendored
+15
-1
@@ -30,6 +30,7 @@ import (
|
||||
v3routepb "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
|
||||
v3httppb "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3"
|
||||
v3tlspb "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
|
||||
"google.golang.org/grpc/internal/envconfig"
|
||||
"google.golang.org/grpc/internal/xds/clients/xdsclient"
|
||||
"google.golang.org/grpc/internal/xds/httpfilter"
|
||||
"google.golang.org/grpc/internal/xds/xdsclient/xdsresource/version"
|
||||
@@ -178,6 +179,7 @@ func processHTTPFilterOverrides(cfgs map[string]*anypb.Any) (map[string]httpfilt
|
||||
m := make(map[string]httpfilter.FilterConfig)
|
||||
for name, cfg := range cfgs {
|
||||
optional := false
|
||||
disabled := false
|
||||
s := new(v3routepb.FilterConfig)
|
||||
if cfg.MessageIs(s) {
|
||||
if err := cfg.UnmarshalTo(s); err != nil {
|
||||
@@ -185,6 +187,14 @@ func processHTTPFilterOverrides(cfgs map[string]*anypb.Any) (map[string]httpfilt
|
||||
}
|
||||
cfg = s.GetConfig()
|
||||
optional = s.GetIsOptional()
|
||||
if envconfig.XDSClientExtProcEnabled {
|
||||
disabled = s.GetDisabled()
|
||||
}
|
||||
}
|
||||
|
||||
if disabled {
|
||||
m[name] = httpfilter.DisabledFilterConfig{}
|
||||
continue
|
||||
}
|
||||
|
||||
httpFilter, config, err := validateHTTPFilterConfig(cfg, false, optional)
|
||||
@@ -235,8 +245,12 @@ func processHTTPFilters(filters []*v3httppb.HttpFilter, server bool) ([]HTTPFilt
|
||||
return nil, fmt.Errorf("HTTP filter %q not supported client-side", name)
|
||||
}
|
||||
|
||||
disabled := false
|
||||
if envconfig.XDSClientExtProcEnabled {
|
||||
disabled = filter.GetDisabled()
|
||||
}
|
||||
// Save name/config
|
||||
ret = append(ret, HTTPFilter{Name: name, Filter: httpFilter, Config: config})
|
||||
ret = append(ret, HTTPFilter{Name: name, Filter: httpFilter, Config: config, Disabled: disabled})
|
||||
}
|
||||
// "Validation will fail if a terminal filter is not the last filter in the
|
||||
// chain or if a non-terminal filter is the last filter in the chain." - A39
|
||||
|
||||
Generated
Vendored
+2
-2
@@ -241,7 +241,7 @@ func routesProtoToSlice(routes []*v3routepb.Route, csps map[string]clusterspecif
|
||||
route.Path = &pt.Path
|
||||
case *v3routepb.RouteMatch_SafeRegex:
|
||||
regex := pt.SafeRegex.GetRegex()
|
||||
re, err := regexp.Compile(regex)
|
||||
re, err := matcher.CompileSafeRegex(regex)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("route %+v contains an invalid regex %q", r, regex)
|
||||
}
|
||||
@@ -261,7 +261,7 @@ func routesProtoToSlice(routes []*v3routepb.Route, csps map[string]clusterspecif
|
||||
header.ExactMatch = &ht.ExactMatch
|
||||
case *v3routepb.HeaderMatcher_SafeRegexMatch:
|
||||
regex := ht.SafeRegexMatch.GetRegex()
|
||||
re, err := regexp.Compile(regex)
|
||||
re, err := matcher.CompileSafeRegex(regex)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("route %+v contains an invalid regex %q", r, regex)
|
||||
}
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -21,7 +21,7 @@
|
||||
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.6.1
|
||||
// - protoc-gen-go-grpc v1.6.2
|
||||
// - protoc v5.27.1
|
||||
// source: grpc/reflection/v1/reflection.proto
|
||||
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -18,7 +18,7 @@
|
||||
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.6.1
|
||||
// - protoc-gen-go-grpc v1.6.2
|
||||
// - protoc v5.27.1
|
||||
// grpc/reflection/v1alpha/reflection.proto is a deprecated file.
|
||||
|
||||
|
||||
+36
-6
@@ -128,6 +128,16 @@ func NewGZIPDecompressor() Decompressor {
|
||||
}
|
||||
|
||||
func (d *gzipDecompressor) Do(r io.Reader) ([]byte, error) {
|
||||
return d.doWithMaxSize(r, math.MaxInt64)
|
||||
}
|
||||
|
||||
// doWithMaxSize behaves like Do but caps the size of the decompressed
|
||||
// payload at maxMessageSize+1 bytes. The Decompressor interface does not
|
||||
// allow extra parameters, so callers inside the package type-assert to
|
||||
// *gzipDecompressor to invoke this method directly. The +1 byte makes it
|
||||
// possible for the caller to detect that the limit was exceeded and
|
||||
// return ResourceExhausted instead of materializing an unbounded payload.
|
||||
func (d *gzipDecompressor) doWithMaxSize(r io.Reader, maxMessageSize int64) ([]byte, error) {
|
||||
var z *gzip.Reader
|
||||
switch maybeZ := d.pool.Get().(type) {
|
||||
case nil:
|
||||
@@ -148,7 +158,11 @@ func (d *gzipDecompressor) Do(r io.Reader) ([]byte, error) {
|
||||
z.Close()
|
||||
d.pool.Put(z)
|
||||
}()
|
||||
return io.ReadAll(z)
|
||||
var src io.Reader = z
|
||||
if maxMessageSize < math.MaxInt64 {
|
||||
src = io.LimitReader(z, maxMessageSize+1)
|
||||
}
|
||||
return io.ReadAll(src)
|
||||
}
|
||||
|
||||
func (d *gzipDecompressor) Type() string {
|
||||
@@ -830,15 +844,15 @@ func compress(in mem.BufferSlice, cp Compressor, compressor encoding.Compressor,
|
||||
if compressor != nil {
|
||||
z, err := compressor.Compress(w)
|
||||
if err != nil {
|
||||
return nil, 0, wrapErr(err)
|
||||
return nil, compressionNone, wrapErr(err)
|
||||
}
|
||||
for _, b := range in {
|
||||
if _, err := z.Write(b.ReadOnlyData()); err != nil {
|
||||
return nil, 0, wrapErr(err)
|
||||
return nil, compressionNone, wrapErr(err)
|
||||
}
|
||||
}
|
||||
if err := z.Close(); err != nil {
|
||||
return nil, 0, wrapErr(err)
|
||||
return nil, compressionNone, wrapErr(err)
|
||||
}
|
||||
} else {
|
||||
// This is obviously really inefficient since it fully materializes the data, but
|
||||
@@ -848,7 +862,7 @@ func compress(in mem.BufferSlice, cp Compressor, compressor encoding.Compressor,
|
||||
buf := in.MaterializeToBuffer(pool)
|
||||
defer buf.Free()
|
||||
if err := cp.Do(w, buf.ReadOnlyData()); err != nil {
|
||||
return nil, 0, wrapErr(err)
|
||||
return nil, compressionNone, wrapErr(err)
|
||||
}
|
||||
}
|
||||
return out, compressionMade, nil
|
||||
@@ -971,7 +985,20 @@ func recvAndDecompress(p *parser, s recvCompressor, dc Decompressor, maxReceiveM
|
||||
func decompress(compressor encoding.Compressor, d mem.BufferSlice, dc Decompressor, maxReceiveMessageSize int, pool mem.BufferPool) (mem.BufferSlice, error) {
|
||||
if dc != nil {
|
||||
r := d.Reader()
|
||||
uncompressed, err := dc.Do(r)
|
||||
// For the built-in gzip decompressor, bound the decompressed output
|
||||
// at maxReceiveMessageSize+1 so that a small but highly compressed
|
||||
// payload (a "zip bomb") cannot expand to gigabytes in memory before
|
||||
// the post-decompression size check below has a chance to fire. The
|
||||
// Decompressor interface does not accept an extra size parameter,
|
||||
// so we type-assert to invoke a size-aware helper. Third-party
|
||||
// Decompressor implementations keep the original Do behavior.
|
||||
var uncompressed []byte
|
||||
var err error
|
||||
if gd, ok := dc.(*gzipDecompressor); ok {
|
||||
uncompressed, err = gd.doWithMaxSize(r, int64(maxReceiveMessageSize))
|
||||
} else {
|
||||
uncompressed, err = dc.Do(r)
|
||||
}
|
||||
if err != nil {
|
||||
r.Close() // ensure buffers are reused
|
||||
return nil, status.Errorf(codes.Internal, "grpc: failed to decompress the received message: %v", err)
|
||||
@@ -989,6 +1016,9 @@ func decompress(compressor encoding.Compressor, d mem.BufferSlice, dc Decompress
|
||||
r.Close() // ensure buffers are reused
|
||||
return nil, status.Errorf(codes.Internal, "grpc: failed to decompress the message: %v", err)
|
||||
}
|
||||
if closer, ok := dcReader.(io.Closer); ok {
|
||||
defer closer.Close()
|
||||
}
|
||||
|
||||
// Read at most one byte more than the limit from the decompressor.
|
||||
// Unless the limit is MaxInt64, in which case, that's impossible, so
|
||||
|
||||
+27
-25
@@ -28,6 +28,7 @@ import (
|
||||
"net/http"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"runtime/pprof"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
@@ -150,8 +151,6 @@ type Server struct {
|
||||
|
||||
serverWorkerChannel chan func()
|
||||
serverWorkerChannelClose func()
|
||||
|
||||
strictPathCheckingLogEmitted atomic.Bool
|
||||
}
|
||||
|
||||
type serverOptions struct {
|
||||
@@ -250,10 +249,8 @@ func newJoinServerOption(opts ...ServerOption) ServerOption {
|
||||
// If this option is set to true every connection will release the buffer after
|
||||
// flushing the data on the wire.
|
||||
//
|
||||
// # Experimental
|
||||
//
|
||||
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
|
||||
// later release.
|
||||
// Deprecated: shared write buffer is enabled by default. SharedWriteBuffer
|
||||
// will be removed in a future release.
|
||||
func SharedWriteBuffer(val bool) ServerOption {
|
||||
return newFuncServerOption(func(o *serverOptions) {
|
||||
o.sharedWriteBuffer = val
|
||||
@@ -302,6 +299,14 @@ func InitialConnWindowSize(s int32) ServerOption {
|
||||
// window size to the value provided and disables dynamic flow control.
|
||||
// The lower bound for window size is 64K and any value smaller than that
|
||||
// will be ignored.
|
||||
//
|
||||
// Note that this also disables dynamic flow control for the connection,
|
||||
// falling back to a default static connection-level window of 64KB. To
|
||||
// use a larger connection-level window, you must also use the
|
||||
// [StaticConnWindowSize] ServerOption.
|
||||
//
|
||||
// Most users should not configure static flow control windows unless
|
||||
// operating in a memory-constrained environment.
|
||||
func StaticStreamWindowSize(s int32) ServerOption {
|
||||
return newFuncServerOption(func(o *serverOptions) {
|
||||
o.initialWindowSize = s
|
||||
@@ -313,6 +318,14 @@ func StaticStreamWindowSize(s int32) ServerOption {
|
||||
// window size to the value provided and disables dynamic flow control.
|
||||
// The lower bound for window size is 64K and any value smaller than that
|
||||
// will be ignored.
|
||||
//
|
||||
// Note that this also disables dynamic flow control for individual streams,
|
||||
// falling back to a default static connection-level window of 64KB. To
|
||||
// explicitly configure the stream-level window size, you must also use the
|
||||
// [StaticStreamWindowSize] ServerOption.
|
||||
//
|
||||
// Most users should not configure static flow control windows unless
|
||||
// operating in a memory-constrained environment.
|
||||
func StaticConnWindowSize(s int32) ServerOption {
|
||||
return newFuncServerOption(func(o *serverOptions) {
|
||||
o.initialConnWindowSize = s
|
||||
@@ -1787,6 +1800,12 @@ func (s *Server) handleMalformedMethodName(stream *transport.ServerStream, ti *t
|
||||
func (s *Server) handleStream(t transport.ServerTransport, stream *transport.ServerStream) {
|
||||
ctx := stream.Context()
|
||||
ctx = contextWithServer(ctx, s)
|
||||
if envconfig.LabelServerGoroutines&envconfig.GoroutineLabelServerMethod != 0 {
|
||||
// This method always runs in its own goroutine, so we can set a
|
||||
// goroutine label without needing to restore a previous context.
|
||||
ctx = pprof.WithLabels(ctx, pprof.Labels("grpc.method", stream.Method()))
|
||||
pprof.SetGoroutineLabels(ctx)
|
||||
}
|
||||
var ti *traceInfo
|
||||
if EnableTracing {
|
||||
tr := newTrace("grpc.Recv."+methodFamily(stream.Method()), stream.Method())
|
||||
@@ -1803,28 +1822,11 @@ func (s *Server) handleStream(t transport.ServerTransport, stream *transport.Ser
|
||||
}
|
||||
}
|
||||
|
||||
sm := stream.Method()
|
||||
if sm == "" {
|
||||
sm, found := strings.CutPrefix(stream.Method(), "/")
|
||||
if !found {
|
||||
s.handleMalformedMethodName(stream, ti)
|
||||
return
|
||||
}
|
||||
if sm[0] != '/' {
|
||||
// TODO(easwars): Add a link to the CVE in the below log messages once
|
||||
// published.
|
||||
if envconfig.DisableStrictPathChecking {
|
||||
if old := s.strictPathCheckingLogEmitted.Swap(true); !old {
|
||||
channelz.Warningf(logger, s.channelz, "grpc: Server.handleStream received malformed method name %q. Allowing it because the environment variable GRPC_GO_EXPERIMENTAL_DISABLE_STRICT_PATH_CHECKING is set to true, but this option will be removed in a future release.", sm)
|
||||
}
|
||||
} else {
|
||||
if old := s.strictPathCheckingLogEmitted.Swap(true); !old {
|
||||
channelz.Warningf(logger, s.channelz, "grpc: Server.handleStream rejected malformed method name %q. To temporarily allow such requests, set the environment variable GRPC_GO_EXPERIMENTAL_DISABLE_STRICT_PATH_CHECKING to true. Note that this is not recommended as it may allow requests to bypass security policies.", sm)
|
||||
}
|
||||
s.handleMalformedMethodName(stream, ti)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
sm = sm[1:]
|
||||
}
|
||||
pos := strings.LastIndex(sm, "/")
|
||||
if pos == -1 {
|
||||
s.handleMalformedMethodName(stream, ti)
|
||||
|
||||
+18
-28
@@ -18,6 +18,7 @@ package opentelemetry
|
||||
|
||||
import (
|
||||
"context"
|
||||
"maps"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
@@ -76,7 +77,7 @@ func getOrCreateCallInfo(ctx context.Context, cc *grpc.ClientConn, method string
|
||||
target: cc.CanonicalTarget(),
|
||||
method: determineMethod(method, opts...),
|
||||
}
|
||||
ctx = setCallInfo(ctx, ci)
|
||||
ctx = context.WithValue(ctx, callInfoKey{}, ci)
|
||||
}
|
||||
return ctx, ci
|
||||
}
|
||||
@@ -157,47 +158,36 @@ func (h *clientMetricsHandler) TagConn(ctx context.Context, _ *stats.ConnTagInfo
|
||||
// HandleConn exists to satisfy stats.Handler.
|
||||
func (h *clientMetricsHandler) HandleConn(context.Context, stats.ConnStats) {}
|
||||
|
||||
// getOrCreateRPCAttemptInfo retrieves or creates an rpc attemptInfo object
|
||||
// and ensures it is set in the context along with the rpcInfo.
|
||||
func getOrCreateRPCAttemptInfo(ctx context.Context) (context.Context, *attemptInfo) {
|
||||
ri := getRPCInfo(ctx)
|
||||
if ri != nil {
|
||||
return ctx, ri.ai
|
||||
}
|
||||
ri = &rpcInfo{ai: &attemptInfo{}}
|
||||
return setRPCInfo(ctx, ri), ri.ai
|
||||
}
|
||||
|
||||
// TagRPC implements per RPC attempt context management for metrics.
|
||||
func (h *clientMetricsHandler) TagRPC(ctx context.Context, info *stats.RPCTagInfo) context.Context {
|
||||
// Numerous stats handlers can be used for the same channel. The cluster
|
||||
// impl balancer which writes to this will only write once, thus have this
|
||||
// stats handler's per attempt scoped context point to the same optional
|
||||
// labels map if set.
|
||||
var labels *istats.Labels
|
||||
if labels = istats.GetLabels(ctx); labels == nil {
|
||||
labels = &istats.Labels{
|
||||
ctx, ri := getOrCreateClientRPCInfo(ctx)
|
||||
ai := ri.ai
|
||||
if ai.xdsLabels == nil {
|
||||
ai.xdsLabels = map[string]string{
|
||||
// The defaults for all the per call labels from a plugin that
|
||||
// executes on the callpath that this OpenTelemetry component
|
||||
// currently supports.
|
||||
TelemetryLabels: map[string]string{
|
||||
"grpc.lb.locality": "",
|
||||
"grpc.lb.backend_service": "",
|
||||
},
|
||||
"grpc.lb.locality": "",
|
||||
"grpc.lb.backend_service": "",
|
||||
}
|
||||
ctx = istats.SetLabels(ctx, labels)
|
||||
}
|
||||
ctx, ai := getOrCreateRPCAttemptInfo(ctx)
|
||||
|
||||
// Numerous stats handlers can be used for the same channel. This callback
|
||||
// ensures that all label updates are propagated to the rpc attempt info across
|
||||
// derived contexts.
|
||||
ctx = istats.RegisterTelemetryLabelCallback(ctx, func(labels map[string]string) {
|
||||
maps.Copy(ai.xdsLabels, labels)
|
||||
})
|
||||
|
||||
ai.startTime = time.Now()
|
||||
ai.xdsLabels = labels.TelemetryLabels
|
||||
ai.method = removeLeadingSlash(info.FullMethodName)
|
||||
|
||||
return setRPCInfo(ctx, &rpcInfo{ai: ai})
|
||||
return ctx
|
||||
}
|
||||
|
||||
// HandleRPC handles per RPC stats implementation.
|
||||
func (h *clientMetricsHandler) HandleRPC(ctx context.Context, rs stats.RPCStats) {
|
||||
ri := getRPCInfo(ctx)
|
||||
ri := clientRPCInfo(ctx)
|
||||
if ri == nil {
|
||||
logger.Error("ctx passed into client side stats handler metrics event handling has no client attempt data present")
|
||||
return
|
||||
|
||||
+25
-6
@@ -21,6 +21,7 @@ import (
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
otelcodes "go.opentelemetry.io/otel/codes"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"google.golang.org/grpc"
|
||||
@@ -83,7 +84,10 @@ func (h *clientTracingHandler) finishTrace(err error, ts trace.Span) {
|
||||
// It creates a new outgoing carrier which serializes information about this
|
||||
// span into gRPC Metadata, if TextMapPropagator is provided in the trace
|
||||
// options. if TextMapPropagator is not provided, it returns the context as is.
|
||||
func (h *clientTracingHandler) traceTagRPC(ctx context.Context, ai *attemptInfo, nameResolutionDelayed bool) (context.Context, *attemptInfo) {
|
||||
//
|
||||
// Note: The passed attemptInfo pointer (ai) is mutated in-place. Fields such as
|
||||
// ai.traceSpan are updated directly. No new attemptInfo is returned.
|
||||
func (h *clientTracingHandler) traceTagRPC(ctx context.Context, ai *attemptInfo, nameResolutionDelayed bool) context.Context {
|
||||
// Add a "Delayed name resolution complete" event to the call span
|
||||
// if there was name resolution delay. In case of multiple retry attempts,
|
||||
// ensure that event is added only once.
|
||||
@@ -98,7 +102,7 @@ func (h *clientTracingHandler) traceTagRPC(ctx context.Context, ai *attemptInfo,
|
||||
carrier := otelinternaltracing.NewOutgoingCarrier(ctx)
|
||||
h.options.TraceOptions.TextMapPropagator.Inject(ctx, carrier)
|
||||
ai.traceSpan = span
|
||||
return carrier.Context(), ai
|
||||
return carrier.Context()
|
||||
}
|
||||
|
||||
// createCallTraceSpan creates a call span to put in the provided context using
|
||||
@@ -120,17 +124,32 @@ func (h *clientTracingHandler) HandleConn(context.Context, stats.ConnStats) {}
|
||||
|
||||
// TagRPC implements per RPC attempt context management for traces.
|
||||
func (h *clientTracingHandler) TagRPC(ctx context.Context, info *stats.RPCTagInfo) context.Context {
|
||||
ctx, ai := getOrCreateRPCAttemptInfo(ctx)
|
||||
ctx, ai = h.traceTagRPC(ctx, ai, info.NameResolutionDelay)
|
||||
return setRPCInfo(ctx, &rpcInfo{ai: ai})
|
||||
ctx, ri := getOrCreateClientRPCInfo(ctx)
|
||||
ci := getCallInfo(ctx)
|
||||
if ci == nil {
|
||||
logger.Error("context passed into client side stats handler (TagRPC) has no call info")
|
||||
return ctx
|
||||
}
|
||||
ctx = h.traceTagRPC(ctx, ri.ai, info.NameResolutionDelay)
|
||||
return ctx
|
||||
}
|
||||
|
||||
// HandleRPC handles per RPC tracing implementation.
|
||||
func (h *clientTracingHandler) HandleRPC(ctx context.Context, rs stats.RPCStats) {
|
||||
ri := getRPCInfo(ctx)
|
||||
ri := clientRPCInfo(ctx)
|
||||
if ri == nil {
|
||||
logger.Error("ctx passed into client side tracing handler trace event handling has no client attempt data present")
|
||||
return
|
||||
}
|
||||
|
||||
// Client-specific Begin attributes.
|
||||
if begin, ok := rs.(*stats.Begin); ok {
|
||||
ci := getCallInfo(ctx)
|
||||
previousRPCAttempts := ci.previousRPCAttempts.Add(1) - 1
|
||||
ri.ai.traceSpan.SetAttributes(
|
||||
attribute.Int64("previous-rpc-attempts", int64(previousRPCAttempts)),
|
||||
attribute.Bool("transparent-retry", begin.IsTransparentRetryAttempt),
|
||||
)
|
||||
}
|
||||
populateSpan(rs, ri.ai)
|
||||
}
|
||||
|
||||
+33
-13
@@ -179,14 +179,13 @@ type callInfo struct {
|
||||
// nameResolutionEventAdded is set when the resolver delay trace event
|
||||
// is added. Prevents duplicate events, since it is reported per-attempt.
|
||||
nameResolutionEventAdded atomic.Bool
|
||||
// previousRPCAttempts holds the count of RPC attempts that have happened
|
||||
// before current attempt. Transparent retries are excluded.
|
||||
previousRPCAttempts atomic.Uint32
|
||||
}
|
||||
|
||||
type callInfoKey struct{}
|
||||
|
||||
func setCallInfo(ctx context.Context, ci *callInfo) context.Context {
|
||||
return context.WithValue(ctx, callInfoKey{}, ci)
|
||||
}
|
||||
|
||||
// getCallInfo returns the callInfo stored in the context, or nil
|
||||
// if there isn't one.
|
||||
func getCallInfo(ctx context.Context) *callInfo {
|
||||
@@ -200,19 +199,41 @@ type rpcInfo struct {
|
||||
ai *attemptInfo
|
||||
}
|
||||
|
||||
type rpcInfoKey struct{}
|
||||
type clientRPCInfoKey struct{}
|
||||
type serverRPCInfoKey struct{}
|
||||
|
||||
func setRPCInfo(ctx context.Context, ri *rpcInfo) context.Context {
|
||||
return context.WithValue(ctx, rpcInfoKey{}, ri)
|
||||
// clientRPCInfo returns the rpcInfo stored in the context for client, or nil
|
||||
// if there isn't one.
|
||||
func clientRPCInfo(ctx context.Context) *rpcInfo {
|
||||
ri, _ := ctx.Value(clientRPCInfoKey{}).(*rpcInfo)
|
||||
return ri
|
||||
}
|
||||
|
||||
// getRPCInfo returns the rpcInfo stored in the context, or nil
|
||||
// serverRPCInfo returns the rpcInfo stored in the context for server, or nil
|
||||
// if there isn't one.
|
||||
func getRPCInfo(ctx context.Context) *rpcInfo {
|
||||
ri, _ := ctx.Value(rpcInfoKey{}).(*rpcInfo)
|
||||
func serverRPCInfo(ctx context.Context) *rpcInfo {
|
||||
ri, _ := ctx.Value(serverRPCInfoKey{}).(*rpcInfo)
|
||||
return ri
|
||||
}
|
||||
|
||||
func getOrCreateClientRPCInfo(ctx context.Context) (context.Context, *rpcInfo) {
|
||||
ri := clientRPCInfo(ctx)
|
||||
if ri != nil {
|
||||
return ctx, ri
|
||||
}
|
||||
ri = &rpcInfo{ai: &attemptInfo{}}
|
||||
return context.WithValue(ctx, clientRPCInfoKey{}, ri), ri
|
||||
}
|
||||
|
||||
func getOrCreateServerRPCInfo(ctx context.Context) (context.Context, *rpcInfo) {
|
||||
ri := serverRPCInfo(ctx)
|
||||
if ri != nil {
|
||||
return ctx, ri
|
||||
}
|
||||
ri = &rpcInfo{ai: &attemptInfo{}}
|
||||
return context.WithValue(ctx, serverRPCInfoKey{}, ri), ri
|
||||
}
|
||||
|
||||
func removeLeadingSlash(mn string) string {
|
||||
return strings.TrimLeft(mn, "/")
|
||||
}
|
||||
@@ -239,9 +260,8 @@ type attemptInfo struct {
|
||||
// message counters for sent and received messages (used for
|
||||
// generating message IDs), and the number of previous RPC attempts for the
|
||||
// associated call.
|
||||
countSentMsg uint32
|
||||
countRecvMsg uint32
|
||||
previousRPCAttempts uint32
|
||||
countSentMsg uint32
|
||||
countRecvMsg uint32
|
||||
}
|
||||
|
||||
type clientMetrics struct {
|
||||
|
||||
+4
-3
@@ -196,16 +196,17 @@ func (h *serverMetricsHandler) TagRPC(ctx context.Context, info *stats.RPCTagInf
|
||||
method = "other"
|
||||
}
|
||||
}
|
||||
ctx, ai := getOrCreateRPCAttemptInfo(ctx)
|
||||
ctx, ri := getOrCreateServerRPCInfo(ctx)
|
||||
ai := ri.ai
|
||||
ai.startTime = time.Now()
|
||||
ai.method = removeLeadingSlash(method)
|
||||
|
||||
return setRPCInfo(ctx, &rpcInfo{ai: ai})
|
||||
return ctx
|
||||
}
|
||||
|
||||
// HandleRPC handles per RPC stats implementation.
|
||||
func (h *serverMetricsHandler) HandleRPC(ctx context.Context, rs stats.RPCStats) {
|
||||
ri := getRPCInfo(ctx)
|
||||
ri := serverRPCInfo(ctx)
|
||||
if ri == nil {
|
||||
logger.Error("ctx passed into server side stats handler metrics event handling has no server call data present")
|
||||
return
|
||||
|
||||
+4
-4
@@ -40,9 +40,9 @@ func (h *serverTracingHandler) initializeTraces() {
|
||||
|
||||
// TagRPC implements per RPC attempt context management for traces.
|
||||
func (h *serverTracingHandler) TagRPC(ctx context.Context, _ *stats.RPCTagInfo) context.Context {
|
||||
ctx, ai := getOrCreateRPCAttemptInfo(ctx)
|
||||
ctx, ai = h.traceTagRPC(ctx, ai)
|
||||
return setRPCInfo(ctx, &rpcInfo{ai: ai})
|
||||
ctx, ri := getOrCreateServerRPCInfo(ctx)
|
||||
ctx, _ = h.traceTagRPC(ctx, ri.ai)
|
||||
return ctx
|
||||
}
|
||||
|
||||
// traceTagRPC populates context with new span data using the TextMapPropagator
|
||||
@@ -67,7 +67,7 @@ func (h *serverTracingHandler) traceTagRPC(ctx context.Context, ai *attemptInfo)
|
||||
|
||||
// HandleRPC handles per RPC tracing implementation.
|
||||
func (h *serverTracingHandler) HandleRPC(ctx context.Context, rs stats.RPCStats) {
|
||||
ri := getRPCInfo(ctx)
|
||||
ri := serverRPCInfo(ctx)
|
||||
if ri == nil {
|
||||
logger.Error("ctx passed into server side tracing handler trace event handling has no server call data present")
|
||||
return
|
||||
|
||||
-14
@@ -17,8 +17,6 @@
|
||||
package opentelemetry
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
otelcodes "go.opentelemetry.io/otel/codes"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
@@ -40,18 +38,6 @@ func populateSpan(rs stats.RPCStats, ai *attemptInfo) {
|
||||
span := ai.traceSpan
|
||||
|
||||
switch rs := rs.(type) {
|
||||
case *stats.Begin:
|
||||
// Note: Go always added Client and FailFast attributes even though they are not
|
||||
// defined by the OpenCensus gRPC spec. Thus, they are unimportant for
|
||||
// correctness.
|
||||
span.SetAttributes(
|
||||
attribute.Bool("Client", rs.Client),
|
||||
attribute.Bool("FailFast", rs.FailFast),
|
||||
attribute.Int64("previous-rpc-attempts", int64(ai.previousRPCAttempts)),
|
||||
attribute.Bool("transparent-retry", rs.IsTransparentRetryAttempt),
|
||||
)
|
||||
// increment previous rpc attempts applicable for next attempt
|
||||
atomic.AddUint32(&ai.previousRPCAttempts, 1)
|
||||
case *stats.DelayedPickComplete:
|
||||
span.AddEvent("Delayed LB pick complete")
|
||||
case *stats.InPayload:
|
||||
|
||||
+1
-1
@@ -19,4 +19,4 @@
|
||||
package grpc
|
||||
|
||||
// Version is the current grpc version.
|
||||
const Version = "1.81.0"
|
||||
const Version = "1.82.1"
|
||||
|
||||
Vendored
+9
-6
@@ -73,7 +73,7 @@ cloud.google.com/go/storage/experimental
|
||||
cloud.google.com/go/storage/internal
|
||||
cloud.google.com/go/storage/internal/apiv2
|
||||
cloud.google.com/go/storage/internal/apiv2/storagepb
|
||||
# github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0
|
||||
# github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.32.0
|
||||
## explicit; go 1.24.0
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp
|
||||
# github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.55.0
|
||||
@@ -314,6 +314,7 @@ github.com/envoyproxy/go-control-plane/envoy/extensions/filters/common/fault/v3
|
||||
github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/dynamic_forward_proxy/v3
|
||||
github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/ext_proc/v3
|
||||
github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/fault/v3
|
||||
github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/gcp_authn/v3
|
||||
github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/rbac/v3
|
||||
github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/router/v3
|
||||
github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3
|
||||
@@ -748,7 +749,7 @@ github.com/zeromq/goczmq
|
||||
## explicit; go 1.24.0
|
||||
go.opentelemetry.io/auto/sdk
|
||||
go.opentelemetry.io/auto/sdk/internal/telemetry
|
||||
# go.opentelemetry.io/contrib/detectors/gcp v1.42.0
|
||||
# go.opentelemetry.io/contrib/detectors/gcp v1.43.0
|
||||
## explicit; go 1.25.0
|
||||
go.opentelemetry.io/contrib/detectors/gcp
|
||||
# go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0
|
||||
@@ -957,7 +958,7 @@ google.golang.org/genproto/googleapis/type/date
|
||||
google.golang.org/genproto/googleapis/type/expr
|
||||
google.golang.org/genproto/googleapis/type/interval
|
||||
google.golang.org/genproto/googleapis/type/timeofday
|
||||
# google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9
|
||||
# google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478
|
||||
## explicit; go 1.25.0
|
||||
google.golang.org/genproto/googleapis/api
|
||||
google.golang.org/genproto/googleapis/api/annotations
|
||||
@@ -968,12 +969,12 @@ google.golang.org/genproto/googleapis/api/label
|
||||
google.golang.org/genproto/googleapis/api/metric
|
||||
google.golang.org/genproto/googleapis/api/monitoredres
|
||||
google.golang.org/genproto/googleapis/api/serviceconfig
|
||||
# google.golang.org/genproto/googleapis/rpc v0.0.0-20260406210006-6f92a3bedf2d
|
||||
# google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478
|
||||
## explicit; go 1.25.0
|
||||
google.golang.org/genproto/googleapis/rpc/code
|
||||
google.golang.org/genproto/googleapis/rpc/errdetails
|
||||
google.golang.org/genproto/googleapis/rpc/status
|
||||
# google.golang.org/grpc v1.81.0
|
||||
# google.golang.org/grpc v1.82.1
|
||||
## explicit; go 1.25.0
|
||||
google.golang.org/grpc
|
||||
google.golang.org/grpc/attributes
|
||||
@@ -1021,6 +1022,8 @@ google.golang.org/grpc/encoding
|
||||
google.golang.org/grpc/encoding/gzip
|
||||
google.golang.org/grpc/encoding/internal
|
||||
google.golang.org/grpc/encoding/proto
|
||||
google.golang.org/grpc/experimental/balancer/hostname
|
||||
google.golang.org/grpc/experimental/balancer/weight
|
||||
google.golang.org/grpc/experimental/opentelemetry
|
||||
google.golang.org/grpc/experimental/stats
|
||||
google.golang.org/grpc/grpclog
|
||||
@@ -1032,7 +1035,6 @@ google.golang.org/grpc/internal/admin
|
||||
google.golang.org/grpc/internal/backoff
|
||||
google.golang.org/grpc/internal/balancer/gracefulswitch
|
||||
google.golang.org/grpc/internal/balancer/nop
|
||||
google.golang.org/grpc/internal/balancer/weight
|
||||
google.golang.org/grpc/internal/balancergroup
|
||||
google.golang.org/grpc/internal/balancerload
|
||||
google.golang.org/grpc/internal/binarylog
|
||||
@@ -1066,6 +1068,7 @@ google.golang.org/grpc/internal/stats
|
||||
google.golang.org/grpc/internal/status
|
||||
google.golang.org/grpc/internal/syscall
|
||||
google.golang.org/grpc/internal/transport
|
||||
google.golang.org/grpc/internal/transport/internal
|
||||
google.golang.org/grpc/internal/transport/networktype
|
||||
google.golang.org/grpc/internal/transport/readyreader
|
||||
google.golang.org/grpc/internal/wrr
|
||||
|
||||
Reference in New Issue
Block a user