vendor: update buildkit to v0.30.0-rc1

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
This commit is contained in:
Tonis Tiigi
2026-05-06 15:06:54 -07:00
parent 0439d7fe8e
commit a3147eba54
222 changed files with 7928 additions and 2565 deletions
File diff suppressed because it is too large Load Diff
@@ -1,4 +1,4 @@
// Copyright 2025 Google LLC
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
// Copyright 2025 Google LLC
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright 2025 Google LLC
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
// Copyright 2025 Google LLC
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
// Copyright 2025 Google LLC
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -66,9 +66,13 @@ const (
// app_profile_id: profiles/prof_qux
// }
//
// The routing header consists of one or multiple key-value pairs. Every key
// and value must be percent-encoded, and joined together in the format of
// `key1=value1&key2=value2`.
// The routing header consists of one or multiple key-value pairs. The order of
// the key-value pairs is undefined, the order of the `routing_parameters` in
// the `RoutingRule` only matters for the evaluation order of the path
// templates when `field` is the same. See the examples below for more details.
//
// Every key and value in the routing header must be percent-encoded,
// and joined together in the following format: `key1=value1&key2=value2`.
// The examples below skip the percent-encoding for readability.
//
// # Example 1
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright 2025 Google LLC
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
+1 -1
View File
@@ -1,4 +1,4 @@
// Copyright 2025 Google LLC
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
// Copyright 2025 Google LLC
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -957,17 +957,17 @@ type BadRequest_FieldViolation struct {
// In this example, in proto `field` could take one of the following values:
//
// - `full_name` for a violation in the `full_name` value
// - `email_addresses[1].email` for a violation in the `email` field of the
// - `email_addresses[0].email` for a violation in the `email` field of the
// first `email_addresses` message
// - `email_addresses[3].type[2]` for a violation in the second `type`
// - `email_addresses[2].type[1]` for a violation in the second `type`
// value in the third `email_addresses` message.
//
// In JSON, the same values are represented as:
//
// - `fullName` for a violation in the `fullName` value
// - `emailAddresses[1].email` for a violation in the `email` field of the
// - `emailAddresses[0].email` for a violation in the `email` field of the
// first `emailAddresses` message
// - `emailAddresses[3].type[2]` for a violation in the second `type`
// - `emailAddresses[2].type[1]` for a violation in the second `type`
// value in the third `emailAddresses` message.
Field string `protobuf:"bytes,1,opt,name=field,proto3" json:"field,omitempty"`
// A description of why the request element is bad.
+3 -4
View File
@@ -1,4 +1,4 @@
// Copyright 2025 Google LLC
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -127,14 +127,13 @@ var file_google_rpc_status_proto_rawDesc = []byte{
0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x2e, 0x0a, 0x07, 0x64, 0x65, 0x74, 0x61,
0x69, 0x6c, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52,
0x07, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x42, 0x61, 0x0a, 0x0e, 0x63, 0x6f, 0x6d, 0x2e,
0x07, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x42, 0x5e, 0x0a, 0x0e, 0x63, 0x6f, 0x6d, 0x2e,
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x72, 0x70, 0x63, 0x42, 0x0b, 0x53, 0x74, 0x61, 0x74,
0x75, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x37, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
0x65, 0x2e, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x67, 0x65, 0x6e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x61, 0x70, 0x69, 0x73,
0x2f, 0x72, 0x70, 0x63, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x3b, 0x73, 0x74, 0x61, 0x74,
0x75, 0x73, 0xf8, 0x01, 0x01, 0xa2, 0x02, 0x03, 0x52, 0x50, 0x43, 0x62, 0x06, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x33,
0x75, 0x73, 0xa2, 0x02, 0x03, 0x52, 0x50, 0x43, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
+55 -22
View File
@@ -27,6 +27,8 @@ package attributes
import (
"fmt"
"iter"
"maps"
"strings"
)
@@ -37,37 +39,46 @@ import (
// any) bool', it will be called by (*Attributes).Equal to determine whether
// two values with the same key should be considered equal.
type Attributes struct {
m map[any]any
parent *Attributes
key, value any
}
// New returns a new Attributes containing the key/value pair.
func New(key, value any) *Attributes {
return &Attributes{m: map[any]any{key: value}}
return &Attributes{
key: key,
value: value,
}
}
// WithValue returns a new Attributes containing the previous keys and values
// and the new key/value pair. If the same key appears multiple times, the
// last value overwrites all previous values for that key. To remove an
// existing key, use a nil value. value should not be modified later.
// last value overwrites all previous values for that key. value should not be
// modified later.
//
// Note that Attributes do not support deletion. Avoid using untyped nil values.
// Since the Value method returns an untyped nil when a key is absent, it is
// impossible to distinguish between a missing key and a key explicitly set to
// an untyped nil. If you need to represent a value being unset, consider
// storing a specific sentinel type or a wrapper struct with a boolean field
// indicating presence.
func (a *Attributes) WithValue(key, value any) *Attributes {
if a == nil {
return New(key, value)
return &Attributes{
parent: a,
key: key,
value: value,
}
n := &Attributes{m: make(map[any]any, len(a.m)+1)}
for k, v := range a.m {
n.m[k] = v
}
n.m[key] = value
return n
}
// Value returns the value associated with these attributes for key, or nil if
// no value is associated with key. The returned value should not be modified.
func (a *Attributes) Value(key any) any {
if a == nil {
return nil
for cur := a; cur != nil; cur = cur.parent {
if cur.key == key {
return cur.value
}
}
return a.m[key]
return nil
}
// Equal returns whether a and o are equivalent. If 'Equal(o any) bool' is
@@ -83,11 +94,15 @@ func (a *Attributes) Equal(o *Attributes) bool {
if a == nil || o == nil {
return false
}
if len(a.m) != len(o.m) {
return false
if a == o {
return true
}
for k, v := range a.m {
ov, ok := o.m[k]
m := maps.Collect(o.all())
lenA := 0
for k, v := range a.all() {
lenA++
ov, ok := m[k]
if !ok {
// o missing element of a
return false
@@ -101,7 +116,7 @@ func (a *Attributes) Equal(o *Attributes) bool {
return false
}
}
return true
return lenA == len(m)
}
// String prints the attribute map. If any key or values throughout the map
@@ -110,11 +125,11 @@ func (a *Attributes) String() string {
var sb strings.Builder
sb.WriteString("{")
first := true
for k, v := range a.m {
for k, v := range a.all() {
if !first {
sb.WriteString(", ")
}
sb.WriteString(fmt.Sprintf("%q: %q ", str(k), str(v)))
fmt.Fprintf(&sb, "%q: %q ", str(k), str(v))
first = false
}
sb.WriteString("}")
@@ -139,3 +154,21 @@ func str(x any) (s string) {
func (a *Attributes) MarshalJSON() ([]byte, error) {
return []byte(a.String()), nil
}
// all returns an iterator that yields all key-value pairs in the Attributes
// chain. If a key appears multiple times, only the most recently added value
// is yielded.
func (a *Attributes) all() iter.Seq2[any, any] {
return func(yield func(any, any) bool) {
seen := map[any]bool{}
for cur := a; cur != nil; cur = cur.parent {
if seen[cur.key] {
continue
}
if !yield(cur.key, cur.value) {
return
}
seen[cur.key] = true
}
}
}
+17 -15
View File
@@ -33,6 +33,7 @@ import (
estats "google.golang.org/grpc/experimental/stats"
"google.golang.org/grpc/grpclog"
"google.golang.org/grpc/internal"
"google.golang.org/grpc/internal/envconfig"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/resolver"
"google.golang.org/grpc/serviceconfig"
@@ -46,8 +47,8 @@ var (
)
// Register registers the balancer builder to the balancer map. b.Name
// (lowercased) will be used as the name registered with this builder. If the
// Builder implements ConfigParser, ParseConfig will be called when new service
// will be used as the name registered with this builder. If the Builder
// implements ConfigParser, ParseConfig will be called when new service
// configs are received by the resolver, and the result will be provided to the
// Balancer in UpdateClientConnState.
//
@@ -55,12 +56,12 @@ var (
// an init() function), and is not thread-safe. If multiple Balancers are
// registered with the same name, the one registered last will take effect.
func Register(b Builder) {
name := strings.ToLower(b.Name())
if name != b.Name() {
// TODO: Skip the use of strings.ToLower() to index the map after v1.59
// is released to switch to case sensitive balancer registry. Also,
// remove this warning and update the docstrings for Register and Get.
logger.Warningf("Balancer registered with name %q. grpc-go will be switching to case sensitive balancer registries soon", b.Name())
name := b.Name()
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())
}
}
m[name] = b
}
@@ -78,16 +79,17 @@ func init() {
}
// Get returns the resolver builder registered with the given name.
// Note that the compare is done in a case-insensitive fashion.
// Note that the compare is done in a case-sensitive fashion.
// If no builder is register with the name, nil will be returned.
func Get(name string) Builder {
if strings.ToLower(name) != name {
// TODO: Skip the use of strings.ToLower() to index the map after v1.59
// is released to switch to case sensitive balancer registry. Also,
// remove this warning and update the docstrings for Register and Get.
logger.Warningf("Balancer retrieved for name %q. grpc-go will be switching to case sensitive balancer registries soon", name)
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)
}
name = lowerName
}
if b, ok := m[strings.ToLower(name)]; ok {
if b, ok := m[name]; ok {
return b
}
return nil
+2 -4
View File
@@ -121,8 +121,7 @@ func (b *baseBalancer) UpdateClientConnState(s balancer.ClientConnState) error {
sc.Connect()
}
}
for _, a := range b.subConns.Keys() {
sc, _ := b.subConns.Get(a)
for a, sc := range b.subConns.All() {
// a was removed by resolver.
if _, ok := addrsSet.Get(a); !ok {
sc.Shutdown()
@@ -171,8 +170,7 @@ func (b *baseBalancer) regeneratePicker() {
readySCs := make(map[balancer.SubConn]SubConnInfo)
// Filter out all ready SCs from full subConn map.
for _, addr := range b.subConns.Keys() {
sc, _ := b.subConns.Get(addr)
for addr, sc := range b.subConns.All() {
if st, ok := b.scStates[sc]; ok && st == connectivity.Ready {
readySCs[sc] = SubConnInfo{Address: addr}
}
@@ -187,8 +187,7 @@ func (es *endpointSharding) UpdateClientConnState(state balancer.ClientConnState
}
}
// Delete old children that are no longer present.
for _, e := range children.Keys() {
child, _ := children.Get(e)
for e, child := range children.All() {
if _, ok := newChildren.Get(e); !ok {
child.closeLocked()
}
@@ -212,7 +211,7 @@ func (es *endpointSharding) ResolverError(err error) {
es.updateState()
}()
children := es.children.Load()
for _, child := range children.Values() {
for _, child := range children.All() {
child.resolverErrorLocked(err)
}
}
@@ -225,7 +224,7 @@ func (es *endpointSharding) Close() {
es.childMu.Lock()
defer es.childMu.Unlock()
children := es.children.Load()
for _, child := range children.Values() {
for _, child := range children.All() {
child.closeLocked()
}
}
@@ -233,7 +232,7 @@ func (es *endpointSharding) Close() {
func (es *endpointSharding) ExitIdle() {
es.childMu.Lock()
defer es.childMu.Unlock()
for _, bw := range es.children.Load().Values() {
for _, bw := range es.children.Load().All() {
if !bw.isClosed {
bw.child.ExitIdle()
}
@@ -255,7 +254,7 @@ func (es *endpointSharding) updateState() {
children := es.children.Load()
childStates := make([]ChildState, 0, children.Len())
for _, child := range children.Values() {
for _, child := range children.All() {
childState := child.childState
childStates = append(childStates, childState)
childPicker := childState.State.Picker
+6 -6
View File
@@ -399,14 +399,14 @@ func (b *pickfirstBalancer) startFirstPassLocked() {
b.firstPass = true
b.numTF = 0
// Reset the connection attempt record for existing SubConns.
for _, sd := range b.subConns.Values() {
for _, sd := range b.subConns.All() {
sd.connectionFailedInFirstPass = false
}
b.requestConnectionLocked()
}
func (b *pickfirstBalancer) closeSubConnsLocked() {
for _, sd := range b.subConns.Values() {
for _, sd := range b.subConns.All() {
sd.subConn.Shutdown()
}
b.subConns = resolver.NewAddressMapV2[*scData]()
@@ -506,7 +506,7 @@ func (b *pickfirstBalancer) reconcileSubConnsLocked(newAddrs []resolver.Address)
newAddrsMap.Set(addr, true)
}
for _, oldAddr := range b.subConns.Keys() {
for oldAddr := range b.subConns.All() {
if _, ok := newAddrsMap.Get(oldAddr); ok {
continue
}
@@ -520,7 +520,7 @@ func (b *pickfirstBalancer) reconcileSubConnsLocked(newAddrs []resolver.Address)
// becomes ready, which means that all other subConn must be shutdown.
func (b *pickfirstBalancer) shutdownRemainingLocked(selected *scData) {
b.cancelConnectionTimer()
for _, sd := range b.subConns.Values() {
for _, sd := range b.subConns.All() {
if sd.subConn != selected.subConn {
sd.subConn.Shutdown()
}
@@ -771,7 +771,7 @@ func (b *pickfirstBalancer) endFirstPassIfPossibleLocked(lastErr error) {
}
// Connect() has been called on all the SubConns. The first pass can be
// ended if all the SubConns have reported a failure.
for _, sd := range b.subConns.Values() {
for _, sd := range b.subConns.All() {
if !sd.connectionFailedInFirstPass {
return
}
@@ -782,7 +782,7 @@ func (b *pickfirstBalancer) endFirstPassIfPossibleLocked(lastErr error) {
Picker: &picker{err: lastErr},
})
// Start re-connecting all the SubConns that are already in IDLE.
for _, sd := range b.subConns.Values() {
for _, sd := range b.subConns.All() {
if sd.rawConnectivityState == connectivity.Idle {
sd.subConn.Connect()
}
+1 -1
View File
@@ -18,7 +18,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.10
// protoc-gen-go v1.36.11
// protoc v5.27.1
// source: grpc/binlog/v1/binarylog.proto
+8 -10
View File
@@ -22,7 +22,6 @@ import (
"context"
"crypto/tls"
"crypto/x509"
"errors"
"fmt"
"net"
"net/url"
@@ -52,22 +51,21 @@ func (t TLSInfo) AuthType() string {
}
// ValidateAuthority validates the provided authority being used to override the
// :authority header by verifying it against the peer certificates. It returns a
// :authority header by verifying it against the peer certificate. It returns a
// non-nil error if the validation fails.
func (t TLSInfo) ValidateAuthority(authority string) error {
var errs []error
host, _, err := net.SplitHostPort(authority)
if err != nil {
host = authority
}
for _, cert := range t.State.PeerCertificates {
var err error
if err = cert.VerifyHostname(host); err == nil {
return nil
}
errs = append(errs, err)
// Verify authority against the leaf certificate.
if len(t.State.PeerCertificates) == 0 {
// This is not expected to happen as the TLS handshake has already
// completed and should have populated PeerCertificates.
return fmt.Errorf("credentials: no peer certificates found to verify authority %q", host)
}
return fmt.Errorf("credentials: invalid authority %q: %v", authority, errors.Join(errs...))
return t.State.PeerCertificates[0].VerifyHostname(host)
}
// cipherSuiteLookup returns the string version of a TLS cipher suite ID.
+5 -4
View File
@@ -705,10 +705,11 @@ func WithDisableHealthCheck() DialOption {
func defaultDialOptions() dialOptions {
return dialOptions{
copts: transport.ConnectOptions{
ReadBufferSize: defaultReadBufSize,
WriteBufferSize: defaultWriteBufSize,
UserAgent: grpcUA,
BufferPool: mem.DefaultBufferPool(),
ReadBufferSize: defaultReadBufSize,
WriteBufferSize: defaultWriteBufSize,
SharedWriteBuffer: true,
UserAgent: grpcUA,
BufferPool: mem.DefaultBufferPool(),
},
bs: internalbackoff.DefaultExponential,
idleTimeout: 30 * time.Minute,
+1 -1
View File
@@ -17,7 +17,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.10
// protoc-gen-go v1.36.11
// protoc v5.27.1
// source: grpc/health/v1/health.proto
+1 -1
View File
@@ -17,7 +17,7 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.6.0
// - protoc-gen-go-grpc v1.6.1
// - protoc v5.27.1
// source: grpc/health/v1/health.proto
+30 -8
View File
@@ -54,17 +54,16 @@ var (
// XDSEndpointHashKeyBackwardCompat controls the parsing of the endpoint hash
// key from EDS LbEndpoint metadata. Endpoint hash keys can be disabled by
// setting "GRPC_XDS_ENDPOINT_HASH_KEY_BACKWARD_COMPAT" to "true". When the
// implementation of A76 is stable, we will flip the default value to false
// in a subsequent release. A final release will remove this environment
// variable, enabling the new behavior unconditionally.
XDSEndpointHashKeyBackwardCompat = boolFromEnv("GRPC_XDS_ENDPOINT_HASH_KEY_BACKWARD_COMPAT", true)
// setting "GRPC_XDS_ENDPOINT_HASH_KEY_BACKWARD_COMPAT" to "true". A future
// release will remove this environment variable, enabling the new behavior
// unconditionally.
XDSEndpointHashKeyBackwardCompat = boolFromEnv("GRPC_XDS_ENDPOINT_HASH_KEY_BACKWARD_COMPAT", false)
// 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 enabled by setting the environment variable
// "GRPC_EXPERIMENTAL_RING_HASH_SET_REQUEST_HASH_KEY" to "true".
RingHashSetRequestHashKey = boolFromEnv("GRPC_EXPERIMENTAL_RING_HASH_SET_REQUEST_HASH_KEY", false)
// to gRFC A76. It can be disabled by setting the environment variable
// "GRPC_EXPERIMENTAL_RING_HASH_SET_REQUEST_HASH_KEY" to "false".
RingHashSetRequestHashKey = boolFromEnv("GRPC_EXPERIMENTAL_RING_HASH_SET_REQUEST_HASH_KEY", true)
// ALTSHandshakerKeepaliveParams is set if we should add the
// KeepaliveParams when dial the ALTS handshaker service.
@@ -78,6 +77,14 @@ var (
// - The DNS resolver is being used.
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
// the env variable "GRPC_GO_EXPERIMENTAL_CASE_SENSITIVE_BALANCER_REGISTRIES"
// to "true".
//
// TODO: After 2 releases, we will enable the env var by default.
CaseSensitiveBalancerRegistries = boolFromEnv("GRPC_GO_EXPERIMENTAL_CASE_SENSITIVE_BALANCER_REGISTRIES", false)
// XDSAuthorityRewrite indicates whether xDS authority rewriting is enabled.
// This feature is defined in gRFC A81 and is enabled by setting the
// environment variable GRPC_EXPERIMENTAL_XDS_AUTHORITY_REWRITE to "true".
@@ -89,6 +96,14 @@ var (
// GRPC_EXPERIMENTAL_PF_WEIGHTED_SHUFFLING to "false".
PickFirstWeightedShuffling = boolFromEnv("GRPC_EXPERIMENTAL_PF_WEIGHTED_SHUFFLING", true)
// XDSRecoverPanicInResourceParsing indicates whether the xdsclient should
// recover from panics while parsing xDS resources.
//
// This feature can be disabled (e.g. for fuzz testing) by setting the
// environment variable "GRPC_GO_EXPERIMENTAL_XDS_RESOURCE_PANIC_RECOVERY"
// 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".
@@ -104,6 +119,13 @@ var (
// 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
// enabled by setting the env variable
// GRPC_EXPERIMENTAL_ENABLE_PRIORITY_LB_CHILD_POLICY_CACHE to true.
EnablePriorityLBChildPolicyCache = boolFromEnv("GRPC_EXPERIMENTAL_ENABLE_PRIORITY_LB_CHILD_POLICY_CACHE", false)
)
func boolFromEnv(envVar string, def bool) bool {
+338
View File
@@ -0,0 +1,338 @@
/*
*
* 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 mem provides utilities that facilitate memory reuse in byte slices
// that are used as buffers.
package mem
import (
"fmt"
"math/bits"
"slices"
"sort"
"sync"
)
const (
goPageSize = 4 * 1024 // 4KiB. N.B. this must be a power of 2.
)
var uintSize = bits.UintSize // use a variable for mocking during tests.
// bufferPool is a copy of the public bufferPool interface used to avoid
// circular dependencies.
type bufferPool interface {
// Get returns a buffer with specified length from the pool.
Get(length int) *[]byte
// Put returns a buffer to the pool.
//
// The provided pointer must hold a prefix of the buffer obtained via
// BufferPool.Get to ensure the buffer's entire capacity can be re-used.
Put(*[]byte)
}
// BinaryTieredBufferPool is a buffer pool that uses multiple sub-pools with
// power-of-two sizes.
type BinaryTieredBufferPool struct {
// exponentToNextLargestPoolMap maps a power-of-two exponent (e.g., 12 for
// 4KB) to the index of the next largest sizedBufferPool. This is used by
// Get() to find the smallest pool that can satisfy a request for a given
// size.
exponentToNextLargestPoolMap []int
// exponentToPreviousLargestPoolMap maps a power-of-two exponent to the
// index of the previous largest sizedBufferPool. This is used by Put()
// to return a buffer to the most appropriate pool based on its capacity.
exponentToPreviousLargestPoolMap []int
sizedPools []bufferPool
fallbackPool bufferPool
maxPoolCap int // Optimization: Cache max capacity
}
// NewBinaryTieredBufferPool returns a BufferPool backed by multiple sub-pools.
// This structure enables O(1) lookup time for Get and Put operations.
//
// The arguments provided are the exponents for the buffer capacities (powers
// of 2), not the raw byte sizes. For example, to create a pool of 16KB buffers
// (2^14 bytes), pass 14 as the argument.
func NewBinaryTieredBufferPool(powerOfTwoExponents ...uint8) (*BinaryTieredBufferPool, error) {
return newBinaryTiered(func(size int) bufferPool {
return newSizedBufferPool(size, true)
}, &simpleBufferPool{shouldZero: true}, powerOfTwoExponents...)
}
// NewDirtyBinaryTieredBufferPool returns a BufferPool backed by multiple
// sub-pools. It is similar to NewBinaryTieredBufferPool but it does not
// initialize the buffers before returning them.
func NewDirtyBinaryTieredBufferPool(powerOfTwoExponents ...uint8) (*BinaryTieredBufferPool, error) {
return newBinaryTiered(func(size int) bufferPool {
return newSizedBufferPool(size, false)
}, &simpleBufferPool{shouldZero: false}, powerOfTwoExponents...)
}
func newBinaryTiered(sizedPoolFactory func(int) bufferPool, fallbackPool bufferPool, powerOfTwoExponents ...uint8) (*BinaryTieredBufferPool, error) {
slices.Sort(powerOfTwoExponents)
powerOfTwoExponents = slices.Compact(powerOfTwoExponents)
// Determine the maximum exponent we need to support. This depends on the
// word size (32-bit vs 64-bit).
maxExponent := uintSize - 2
indexOfNextLargestBit := slices.Repeat([]int{-1}, maxExponent+1)
indexOfPreviousLargestBit := slices.Repeat([]int{-1}, maxExponent+1)
maxTier := 0
pools := make([]bufferPool, 0, len(powerOfTwoExponents))
for i, exp := range powerOfTwoExponents {
// Allocating slices of size > 2^maxExponent isn't possible on
// maxExponent-bit machines.
if int(exp) > maxExponent {
return nil, fmt.Errorf("mem: allocating slice of size 2^%d is not possible", exp)
}
tierSize := 1 << exp
pools = append(pools, sizedPoolFactory(tierSize))
maxTier = max(maxTier, tierSize)
// Map the exact power of 2 to this pool index.
indexOfNextLargestBit[exp] = i
indexOfPreviousLargestBit[exp] = i
}
// Fill gaps for Get() (Next Largest)
// We iterate backwards. If current is empty, take the value from the right (larger).
for i := maxExponent - 1; i >= 0; i-- {
if indexOfNextLargestBit[i] == -1 {
indexOfNextLargestBit[i] = indexOfNextLargestBit[i+1]
}
}
// Fill gaps for Put() (Previous Largest)
// We iterate forwards. If current is empty, take the value from the left (smaller).
for i := 1; i <= maxExponent; i++ {
if indexOfPreviousLargestBit[i] == -1 {
indexOfPreviousLargestBit[i] = indexOfPreviousLargestBit[i-1]
}
}
return &BinaryTieredBufferPool{
exponentToNextLargestPoolMap: indexOfNextLargestBit,
exponentToPreviousLargestPoolMap: indexOfPreviousLargestBit,
sizedPools: pools,
maxPoolCap: maxTier,
fallbackPool: fallbackPool,
}, nil
}
// Get returns a buffer with specified length from the pool.
func (b *BinaryTieredBufferPool) Get(size int) *[]byte {
return b.poolForGet(size).Get(size)
}
func (b *BinaryTieredBufferPool) poolForGet(size int) bufferPool {
if size == 0 || size > b.maxPoolCap {
return b.fallbackPool
}
// Calculate the exponent of the smallest power of 2 >= size.
// We subtract 1 from size to handle exact powers of 2 correctly.
//
// Examples:
// size=16 (0b10000) -> size-1=15 (0b01111) -> bits.Len=4 -> Pool for 2^4
// size=17 (0b10001) -> size-1=16 (0b10000) -> bits.Len=5 -> Pool for 2^5
querySize := uint(size - 1)
poolIdx := b.exponentToNextLargestPoolMap[bits.Len(querySize)]
return b.sizedPools[poolIdx]
}
// Put returns a buffer to the pool.
func (b *BinaryTieredBufferPool) Put(buf *[]byte) {
// We pass the capacity of the buffer, and not the size of the buffer here.
// If we did the latter, all buffers would eventually move to the smallest
// pool.
b.poolForPut(cap(*buf)).Put(buf)
}
func (b *BinaryTieredBufferPool) poolForPut(bCap int) bufferPool {
if bCap == 0 {
return NopBufferPool{}
}
if bCap > b.maxPoolCap {
return b.fallbackPool
}
// Find the pool with the largest capacity <= bCap.
//
// We calculate the exponent of the largest power of 2 <= bCap.
// bits.Len(x) returns the minimum number of bits required to represent x;
// i.e. the number of bits up to and including the most significant bit.
// Subtracting 1 gives the 0-based index of the most significant bit,
// which is the exponent of the largest power of 2 <= bCap.
//
// Examples:
// cap=16 (0b10000) -> Len=5 -> 5-1=4 -> 2^4
// cap=15 (0b01111) -> Len=4 -> 4-1=3 -> 2^3
largestPowerOfTwo := bits.Len(uint(bCap)) - 1
poolIdx := b.exponentToPreviousLargestPoolMap[largestPowerOfTwo]
// The buffer is smaller than the smallest power of 2, discard it.
if poolIdx == -1 {
// Buffer is smaller than our smallest pool bucket.
return NopBufferPool{}
}
return b.sizedPools[poolIdx]
}
// NopBufferPool is a buffer pool that returns new buffers without pooling.
type NopBufferPool struct{}
// Get returns a buffer with specified length from the pool.
func (NopBufferPool) Get(length int) *[]byte {
b := make([]byte, length)
return &b
}
// Put returns a buffer to the pool.
func (NopBufferPool) Put(*[]byte) {
}
// sizedBufferPool is a BufferPool implementation that is optimized for specific
// buffer sizes. For example, HTTP/2 frames within gRPC have a default max size
// of 16kb and a sizedBufferPool can be configured to only return buffers with a
// capacity of 16kb. Note that however it does not support returning larger
// buffers and in fact panics if such a buffer is requested. Because of this,
// this BufferPool implementation is not meant to be used on its own and rather
// is intended to be embedded in a TieredBufferPool such that Get is only
// invoked when the required size is smaller than or equal to defaultSize.
type sizedBufferPool struct {
pool sync.Pool
defaultSize int
shouldZero bool
}
func (p *sizedBufferPool) Get(size int) *[]byte {
buf, ok := p.pool.Get().(*[]byte)
if !ok {
buf := make([]byte, size, p.defaultSize)
return &buf
}
b := *buf
if p.shouldZero {
clear(b[:cap(b)])
}
*buf = b[:size]
return buf
}
func (p *sizedBufferPool) Put(buf *[]byte) {
if cap(*buf) < p.defaultSize {
// Ignore buffers that are too small to fit in the pool. Otherwise, when
// Get is called it will panic as it tries to index outside the bounds
// of the buffer.
return
}
p.pool.Put(buf)
}
func newSizedBufferPool(size int, zero bool) *sizedBufferPool {
return &sizedBufferPool{
defaultSize: size,
shouldZero: zero,
}
}
// TieredBufferPool implements the BufferPool interface with multiple tiers of
// buffer pools for different sizes of buffers.
type TieredBufferPool struct {
sizedPools []*sizedBufferPool
fallbackPool simpleBufferPool
}
// NewTieredBufferPool returns a BufferPool implementation that uses multiple
// underlying pools of the given pool sizes.
func NewTieredBufferPool(poolSizes ...int) *TieredBufferPool {
sort.Ints(poolSizes)
pools := make([]*sizedBufferPool, len(poolSizes))
for i, s := range poolSizes {
pools[i] = newSizedBufferPool(s, true)
}
return &TieredBufferPool{
sizedPools: pools,
fallbackPool: simpleBufferPool{shouldZero: true},
}
}
// Get returns a buffer with specified length from the pool.
func (p *TieredBufferPool) Get(size int) *[]byte {
return p.getPool(size).Get(size)
}
// Put returns a buffer to the pool.
func (p *TieredBufferPool) Put(buf *[]byte) {
p.getPool(cap(*buf)).Put(buf)
}
func (p *TieredBufferPool) getPool(size int) bufferPool {
poolIdx := sort.Search(len(p.sizedPools), func(i int) bool {
return p.sizedPools[i].defaultSize >= size
})
if poolIdx == len(p.sizedPools) {
return &p.fallbackPool
}
return p.sizedPools[poolIdx]
}
// simpleBufferPool is an implementation of the BufferPool interface that
// attempts to pool buffers with a sync.Pool. When Get is invoked, it tries to
// acquire a buffer from the pool but if that buffer is too small, it returns it
// to the pool and creates a new one.
type simpleBufferPool struct {
pool sync.Pool
shouldZero bool
}
func (p *simpleBufferPool) Get(size int) *[]byte {
bs, ok := p.pool.Get().(*[]byte)
if ok && cap(*bs) >= size {
if p.shouldZero {
clear((*bs)[:cap(*bs)])
}
*bs = (*bs)[:size]
return bs
}
// A buffer was pulled from the pool, but it is too small. Put it back in
// the pool and create one large enough.
if ok {
p.pool.Put(bs)
}
// If we're going to allocate, round up to the nearest page. This way if
// requests frequently arrive with small variation we don't allocate
// repeatedly if we get unlucky and they increase over time. By default we
// only allocate here if size > 1MiB. Because goPageSize is a power of 2, we
// can round up efficiently.
allocSize := (size + goPageSize - 1) & ^(goPageSize - 1)
b := make([]byte, size, allocSize)
return &b
}
func (p *simpleBufferPool) Put(buf *[]byte) {
p.pool.Put(buf)
}
+1
View File
@@ -46,6 +46,7 @@ const (
defaultWriteQuota = 64 * 1024
defaultClientMaxHeaderListSize = uint32(16 << 20)
defaultServerMaxHeaderListSize = uint32(16 << 20)
upcomingDefaultHeaderListSize = uint32(8 << 10)
)
// MaxStreamID is the upper bound for the stream ID before the current
+5 -1
View File
@@ -871,11 +871,15 @@ func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr, handler s
}
var sz int64
for _, f := range hdr.hf {
if sz += int64(f.Size()); sz > int64(*t.maxSendHeaderListSize) {
sz += int64(f.Size())
if sz > int64(*t.maxSendHeaderListSize) {
hdrListSizeErr = status.Errorf(codes.Internal, "header list size to send violates the maximum size (%d bytes) set by server", *t.maxSendHeaderListSize)
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)
}
return true
}
for {
+5 -1
View File
@@ -940,13 +940,17 @@ func (t *http2Server) checkForHeaderListSize(hf []hpack.HeaderField) bool {
}
var sz int64
for _, f := range hf {
if sz += int64(f.Size()); sz > int64(*t.maxSendHeaderListSize) {
sz += int64(f.Size())
if sz > int64(*t.maxSendHeaderListSize) {
if t.logger.V(logLevel) {
t.logger.Infof("Header list size to send violates the maximum size (%d bytes) set by client", *t.maxSendHeaderListSize)
}
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)
}
return true
}
+29 -138
View File
@@ -19,10 +19,10 @@
package mem
import (
"sort"
"sync"
"fmt"
"google.golang.org/grpc/internal"
"google.golang.org/grpc/internal/mem"
)
// BufferPool is a pool of buffers that can be shared and reused, resulting in
@@ -38,20 +38,23 @@ type BufferPool interface {
Put(*[]byte)
}
const goPageSize = 4 << 10 // 4KiB. N.B. this must be a power of 2.
var defaultBufferPoolSizes = []int{
256,
goPageSize,
16 << 10, // 16KB (max HTTP/2 frame size used by gRPC)
32 << 10, // 32KB (default buffer size for io.Copy)
1 << 20, // 1MB
}
var defaultBufferPool BufferPool
var (
defaultBufferPoolSizeExponents = []uint8{
8,
12, // Go page size, 4KB
14, // 16KB (max HTTP/2 frame size used by gRPC)
15, // 32KB (default buffer size for io.Copy)
20, // 1MB
}
defaultBufferPool BufferPool
)
func init() {
defaultBufferPool = NewTieredBufferPool(defaultBufferPoolSizes...)
var err error
defaultBufferPool, err = NewBinaryTieredBufferPool(defaultBufferPoolSizeExponents...)
if err != nil {
panic(fmt.Sprintf("Failed to create default buffer pool: %v", err))
}
internal.SetDefaultBufferPool = func(pool BufferPool) {
defaultBufferPool = pool
@@ -72,134 +75,22 @@ func DefaultBufferPool() BufferPool {
// NewTieredBufferPool returns a BufferPool implementation that uses multiple
// underlying pools of the given pool sizes.
func NewTieredBufferPool(poolSizes ...int) BufferPool {
sort.Ints(poolSizes)
pools := make([]*sizedBufferPool, len(poolSizes))
for i, s := range poolSizes {
pools[i] = newSizedBufferPool(s)
}
return &tieredBufferPool{
sizedPools: pools,
}
return mem.NewTieredBufferPool(poolSizes...)
}
// tieredBufferPool implements the BufferPool interface with multiple tiers of
// buffer pools for different sizes of buffers.
type tieredBufferPool struct {
sizedPools []*sizedBufferPool
fallbackPool simpleBufferPool
// NewBinaryTieredBufferPool returns a BufferPool backed by multiple sub-pools.
// This structure enables O(1) lookup time for Get and Put operations.
//
// The arguments provided are the exponents for the buffer capacities (powers
// of 2), not the raw byte sizes. For example, to create a pool of 16KB buffers
// (2^14 bytes), pass 14 as the argument.
func NewBinaryTieredBufferPool(powerOfTwoExponents ...uint8) (BufferPool, error) {
return mem.NewBinaryTieredBufferPool(powerOfTwoExponents...)
}
func (p *tieredBufferPool) Get(size int) *[]byte {
return p.getPool(size).Get(size)
}
func (p *tieredBufferPool) Put(buf *[]byte) {
p.getPool(cap(*buf)).Put(buf)
}
func (p *tieredBufferPool) getPool(size int) BufferPool {
poolIdx := sort.Search(len(p.sizedPools), func(i int) bool {
return p.sizedPools[i].defaultSize >= size
})
if poolIdx == len(p.sizedPools) {
return &p.fallbackPool
}
return p.sizedPools[poolIdx]
}
// sizedBufferPool is a BufferPool implementation that is optimized for specific
// buffer sizes. For example, HTTP/2 frames within gRPC have a default max size
// of 16kb and a sizedBufferPool can be configured to only return buffers with a
// capacity of 16kb. Note that however it does not support returning larger
// buffers and in fact panics if such a buffer is requested. Because of this,
// this BufferPool implementation is not meant to be used on its own and rather
// is intended to be embedded in a tieredBufferPool such that Get is only
// invoked when the required size is smaller than or equal to defaultSize.
type sizedBufferPool struct {
pool sync.Pool
defaultSize int
}
func (p *sizedBufferPool) Get(size int) *[]byte {
buf, ok := p.pool.Get().(*[]byte)
if !ok {
buf := make([]byte, size, p.defaultSize)
return &buf
}
b := *buf
clear(b[:cap(b)])
*buf = b[:size]
return buf
}
func (p *sizedBufferPool) Put(buf *[]byte) {
if cap(*buf) < p.defaultSize {
// Ignore buffers that are too small to fit in the pool. Otherwise, when
// Get is called it will panic as it tries to index outside the bounds
// of the buffer.
return
}
p.pool.Put(buf)
}
func newSizedBufferPool(size int) *sizedBufferPool {
return &sizedBufferPool{
defaultSize: size,
}
}
var _ BufferPool = (*simpleBufferPool)(nil)
// simpleBufferPool is an implementation of the BufferPool interface that
// attempts to pool buffers with a sync.Pool. When Get is invoked, it tries to
// acquire a buffer from the pool but if that buffer is too small, it returns it
// to the pool and creates a new one.
type simpleBufferPool struct {
pool sync.Pool
}
func (p *simpleBufferPool) Get(size int) *[]byte {
bs, ok := p.pool.Get().(*[]byte)
if ok && cap(*bs) >= size {
clear((*bs)[:cap(*bs)])
*bs = (*bs)[:size]
return bs
}
// A buffer was pulled from the pool, but it is too small. Put it back in
// the pool and create one large enough.
if ok {
p.pool.Put(bs)
}
// If we're going to allocate, round up to the nearest page. This way if
// requests frequently arrive with small variation we don't allocate
// repeatedly if we get unlucky and they increase over time. By default we
// only allocate here if size > 1MiB. Because goPageSize is a power of 2, we
// can round up efficiently.
allocSize := (size + goPageSize - 1) & ^(goPageSize - 1)
b := make([]byte, size, allocSize)
return &b
}
func (p *simpleBufferPool) Put(buf *[]byte) {
p.pool.Put(buf)
// NopBufferPool is a buffer pool that returns new buffers without pooling.
type NopBufferPool struct {
mem.NopBufferPool
}
var _ BufferPool = NopBufferPool{}
// NopBufferPool is a buffer pool that returns new buffers without pooling.
type NopBufferPool struct{}
// Get returns a buffer with specified length from the pool.
func (NopBufferPool) Get(length int) *[]byte {
b := make([]byte, length)
return &b
}
// Put returns a buffer to the pool.
func (NopBufferPool) Put(*[]byte) {
}
+3 -1
View File
@@ -192,7 +192,9 @@ func (pw *pickerWrapper) pick(ctx context.Context, failfast bool, info balancer.
// DoneInfo with default value works.
pickResult.Done(balancer.DoneInfo{})
}
logger.Infof("blockingPicker: the picked transport is not ready, loop back to repick")
if logger.V(2) {
logger.Infof("blockingPicker: the picked transport is not ready, loop back to repick")
}
// If ok == false, ac.state is not READY.
// A valid picker always returns READY subConn. This means the state of ac
// just changed, and picker will be updated shortly.
+34
View File
@@ -20,6 +20,7 @@ package resolver
import (
"encoding/base64"
"iter"
"sort"
"strings"
)
@@ -135,6 +136,7 @@ func (a *AddressMapV2[T]) Len() int {
}
// Keys returns a slice of all current map keys.
// Deprecated: Use AddressMapV2.All() instead.
func (a *AddressMapV2[T]) Keys() []Address {
ret := make([]Address, 0, a.Len())
for _, entryList := range a.m {
@@ -146,6 +148,7 @@ func (a *AddressMapV2[T]) Keys() []Address {
}
// Values returns a slice of all current map values.
// Deprecated: Use AddressMapV2.All() instead.
func (a *AddressMapV2[T]) Values() []T {
ret := make([]T, 0, a.Len())
for _, entryList := range a.m {
@@ -156,6 +159,19 @@ func (a *AddressMapV2[T]) Values() []T {
return ret
}
// All returns an iterator over all elements.
func (a *AddressMapV2[T]) All() iter.Seq2[Address, T] {
return func(yield func(Address, T) bool) {
for _, entryList := range a.m {
for _, entry := range entryList {
if !yield(entry.addr, entry.value) {
return
}
}
}
}
}
type endpointMapKey string
// EndpointMap is a map of endpoints to arbitrary values keyed on only the
@@ -223,6 +239,7 @@ func (em *EndpointMap[T]) Len() int {
// the unordered set of addresses. Thus, endpoint information returned is not
// the full endpoint data (drops duplicated addresses and attributes) but can be
// used for EndpointMap accesses.
// Deprecated: Use EndpointMap.All() instead.
func (em *EndpointMap[T]) Keys() []Endpoint {
ret := make([]Endpoint, 0, len(em.endpoints))
for _, en := range em.endpoints {
@@ -232,6 +249,7 @@ func (em *EndpointMap[T]) Keys() []Endpoint {
}
// Values returns a slice of all current map values.
// Deprecated: Use EndpointMap.All() instead.
func (em *EndpointMap[T]) Values() []T {
ret := make([]T, 0, len(em.endpoints))
for _, val := range em.endpoints {
@@ -240,6 +258,22 @@ func (em *EndpointMap[T]) Values() []T {
return ret
}
// All returns an iterator over all elements.
// The map keys are endpoints specifying the addresses present in the endpoint
// map, in which uniqueness is determined by the unordered set of addresses.
// Thus, endpoint information returned is not the full endpoint data (drops
// duplicated addresses and attributes) but can be used for EndpointMap
// accesses.
func (em *EndpointMap[T]) All() iter.Seq2[Endpoint, T] {
return func(yield func(Endpoint, T) bool) {
for _, en := range em.endpoints {
if !yield(en.decodedKey, en.value) {
return
}
}
}
}
// Delete removes the specified endpoint from the map.
func (em *EndpointMap[T]) Delete(e Endpoint) {
en := encodeEndpoint(e)
+16 -6
View File
@@ -961,24 +961,32 @@ func recvAndDecompress(p *parser, s recvCompressor, dc Decompressor, maxReceiveM
return out, nil
}
// decompress processes the given data by decompressing it using either a custom decompressor or a standard compressor.
// If a custom decompressor is provided, it takes precedence. The function validates that the decompressed data
// does not exceed the specified maximum size and returns an error if this limit is exceeded.
// On success, it returns the decompressed data. Otherwise, it returns an error if decompression fails or the data exceeds the size limit.
// decompress processes the given data by decompressing it using either
// a custom decompressor or a standard compressor. If a custom decompressor
// is provided, it takes precedence. The function validates that
// the decompressed data does not exceed the specified maximum size and returns
// an error if this limit is exceeded. On success, it returns the decompressed
// data. Otherwise, it returns an error if decompression fails or the data
// exceeds the size limit.
func decompress(compressor encoding.Compressor, d mem.BufferSlice, dc Decompressor, maxReceiveMessageSize int, pool mem.BufferPool) (mem.BufferSlice, error) {
if dc != nil {
uncompressed, err := dc.Do(d.Reader())
r := d.Reader()
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)
}
if len(uncompressed) > maxReceiveMessageSize {
r.Close() // ensure buffers are reused
return nil, status.Errorf(codes.ResourceExhausted, "grpc: message after decompression larger than max (%d vs. %d)", len(uncompressed), maxReceiveMessageSize)
}
return mem.BufferSlice{mem.SliceBuffer(uncompressed)}, nil
}
if compressor != nil {
dcReader, err := compressor.Decompress(d.Reader())
r := d.Reader()
dcReader, err := compressor.Decompress(r)
if err != nil {
r.Close() // ensure buffers are reused
return nil, status.Errorf(codes.Internal, "grpc: failed to decompress the message: %v", err)
}
@@ -990,11 +998,13 @@ func decompress(compressor encoding.Compressor, d mem.BufferSlice, dc Decompress
}
out, err := mem.ReadAll(dcReader, pool)
if err != nil {
r.Close() // ensure buffers are reused
out.Free()
return nil, status.Errorf(codes.Internal, "grpc: failed to read decompressed data: %v", err)
}
if out.Len() > maxReceiveMessageSize {
r.Close() // ensure buffers are reused
out.Free()
return nil, status.Errorf(codes.ResourceExhausted, "grpc: received message after decompression larger than max %d", maxReceiveMessageSize)
}
+1
View File
@@ -192,6 +192,7 @@ var defaultServerOptions = serverOptions{
maxSendMessageSize: defaultServerMaxSendMessageSize,
connectionTimeout: 120 * time.Second,
writeBufferSize: defaultWriteBufSize,
sharedWriteBuffer: true,
readBufferSize: defaultReadBufSize,
bufferPool: mem.DefaultBufferPool(),
}
+1 -1
View File
@@ -19,4 +19,4 @@
package grpc
// Version is the current grpc version.
const Version = "1.79.3"
const Version = "1.80.0"