Upgrade gRPC to v1.29.0 (#4790)

Incidentally upgrade golang/protobuf.
This commit is contained in:
Jacob Hoffman-Andrews 2020-04-22 18:33:16 -07:00 committed by GitHub
parent 4a2029b293
commit d2ae471026
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
55 changed files with 2308 additions and 1099 deletions

4
go.mod
View File

@ -10,7 +10,7 @@ require (
github.com/go-gorp/gorp v2.0.0+incompatible // indirect github.com/go-gorp/gorp v2.0.0+incompatible // indirect
github.com/go-sql-driver/mysql v1.4.1 github.com/go-sql-driver/mysql v1.4.1
github.com/golang/mock v1.3.1 github.com/golang/mock v1.3.1
github.com/golang/protobuf v1.3.2 github.com/golang/protobuf v1.3.3
github.com/golang/snappy v0.0.0-20170215233205-553a64147049 // indirect github.com/golang/snappy v0.0.0-20170215233205-553a64147049 // indirect
github.com/google/certificate-transparency-go v1.0.22-0.20181127102053-c25855a82c75 github.com/google/certificate-transparency-go v1.0.22-0.20181127102053-c25855a82c75
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
@ -33,7 +33,7 @@ require (
golang.org/x/crypto v0.0.0-20200124225646-8b5121be2f68 golang.org/x/crypto v0.0.0-20200124225646-8b5121be2f68
golang.org/x/net v0.0.0-20191112182307-2180aed22343 golang.org/x/net v0.0.0-20191112182307-2180aed22343
golang.org/x/text v0.3.2 golang.org/x/text v0.3.2
google.golang.org/grpc v1.25.1 google.golang.org/grpc v1.29.0
gopkg.in/go-gorp/gorp.v2 v2.0.1-0.20180410155428-6032c66e0f5f gopkg.in/go-gorp/gorp.v2 v2.0.1-0.20180410155428-6032c66e0f5f
gopkg.in/square/go-jose.v2 v2.4.1 gopkg.in/square/go-jose.v2 v2.4.1
gopkg.in/yaml.v2 v2.2.5 gopkg.in/yaml.v2 v2.2.5

6
go.sum
View File

@ -34,6 +34,7 @@ github.com/cloudflare/go-metrics v0.0.0-20151117154305-6a9aea36fb41 h1:/8sZyuGTA
github.com/cloudflare/go-metrics v0.0.0-20151117154305-6a9aea36fb41/go.mod h1:eaZPlJWD+G9wseg1BuRXlHnjntPMrywMsyxf+LTOdP4= github.com/cloudflare/go-metrics v0.0.0-20151117154305-6a9aea36fb41/go.mod h1:eaZPlJWD+G9wseg1BuRXlHnjntPMrywMsyxf+LTOdP4=
github.com/cloudflare/redoctober v0.0.0-20171127175943-746a508df14c h1:p0Q1GvgWtVf46XpMMibupKiE7aQxPYUIb+/jLTTK2kM= github.com/cloudflare/redoctober v0.0.0-20171127175943-746a508df14c h1:p0Q1GvgWtVf46XpMMibupKiE7aQxPYUIb+/jLTTK2kM=
github.com/cloudflare/redoctober v0.0.0-20171127175943-746a508df14c/go.mod h1:6Se34jNoqrd8bTxrmJB2Bg2aoZ2CdSXonils9NsiNgo= github.com/cloudflare/redoctober v0.0.0-20171127175943-746a508df14c/go.mod h1:6Se34jNoqrd8bTxrmJB2Bg2aoZ2CdSXonils9NsiNgo=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/daaku/go.zipexe v1.0.0 h1:VSOgZtH418pH9L16hC/JrgSNJbbAL26pj7lmD1+CGdY= github.com/daaku/go.zipexe v1.0.0 h1:VSOgZtH418pH9L16hC/JrgSNJbbAL26pj7lmD1+CGdY=
github.com/daaku/go.zipexe v1.0.0/go.mod h1:z8IiR6TsVLEYKwXAoE/I+8ys/sDkgTzSL0CLnGVd57E= github.com/daaku/go.zipexe v1.0.0/go.mod h1:z8IiR6TsVLEYKwXAoE/I+8ys/sDkgTzSL0CLnGVd57E=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
@ -43,6 +44,7 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/eggsampler/acme/v3 v3.0.0 h1:Fl1fWD94NcdC7Ensb6Ed/CJZ6S24PpekLo/jZB6Ltg8= github.com/eggsampler/acme/v3 v3.0.0 h1:Fl1fWD94NcdC7Ensb6Ed/CJZ6S24PpekLo/jZB6Ltg8=
github.com/eggsampler/acme/v3 v3.0.0/go.mod h1:gw64Ckma6iKulWks9BtE/g/9z/Vdz9D1lM7x7M1X1Ag= github.com/eggsampler/acme/v3 v3.0.0/go.mod h1:gw64Ckma6iKulWks9BtE/g/9z/Vdz9D1lM7x7M1X1Ag=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
@ -70,6 +72,8 @@ github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/snappy v0.0.0-20170215233205-553a64147049 h1:K9KHZbXKpGydfDN0aZrsoHpLJlZsBrGMFWbgLDGnPZk= github.com/golang/snappy v0.0.0-20170215233205-553a64147049 h1:K9KHZbXKpGydfDN0aZrsoHpLJlZsBrGMFWbgLDGnPZk=
github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/certificate-transparency-go v1.0.21 h1:Yf1aXowfZ2nuboBsg7iYGLmwsOARdV86pfH3g95wXmE= github.com/google/certificate-transparency-go v1.0.21 h1:Yf1aXowfZ2nuboBsg7iYGLmwsOARdV86pfH3g95wXmE=
@ -253,6 +257,8 @@ google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZi
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1 h1:wdKvqQk7IttEw92GoRyKG2IDrUIpgpj6H6m81yfeMW0= google.golang.org/grpc v1.25.1 h1:wdKvqQk7IttEw92GoRyKG2IDrUIpgpj6H6m81yfeMW0=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.29.0 h1:2pJjwYOdkZ9HlN4sWRYBg9ttH5bCOlsueaM+b/oYjwo=
google.golang.org/grpc v1.29.0/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View File

@ -393,7 +393,7 @@ func (p *Buffer) Bytes() []byte { return p.buf }
// than relying on this API. // than relying on this API.
// //
// If deterministic serialization is requested, map entries will be sorted // If deterministic serialization is requested, map entries will be sorted
// by keys in lexographical order. This is an implementation detail and // by keys in lexicographical order. This is an implementation detail and
// subject to change. // subject to change.
func (p *Buffer) SetDeterministic(deterministic bool) { func (p *Buffer) SetDeterministic(deterministic bool) {
p.deterministic = deterministic p.deterministic = deterministic

View File

@ -456,6 +456,8 @@ func (tm *TextMarshaler) writeStruct(w *textWriter, sv reflect.Value) error {
return nil return nil
} }
var textMarshalerType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
// writeAny writes an arbitrary field. // writeAny writes an arbitrary field.
func (tm *TextMarshaler) writeAny(w *textWriter, v reflect.Value, props *Properties) error { func (tm *TextMarshaler) writeAny(w *textWriter, v reflect.Value, props *Properties) error {
v = reflect.Indirect(v) v = reflect.Indirect(v)
@ -519,8 +521,8 @@ func (tm *TextMarshaler) writeAny(w *textWriter, v reflect.Value, props *Propert
// mutating this value. // mutating this value.
v = v.Addr() v = v.Addr()
} }
if etm, ok := v.Interface().(encoding.TextMarshaler); ok { if v.Type().Implements(textMarshalerType) {
text, err := etm.MarshalText() text, err := v.Interface().(encoding.TextMarshaler).MarshalText()
if err != nil { if err != nil {
return err return err
} }

View File

@ -11,7 +11,7 @@ matrix:
- go: 1.13.x - go: 1.13.x
env: GRPC_GO_RETRY=on env: GRPC_GO_RETRY=on
- go: 1.13.x - go: 1.13.x
env: TESTEXAMPLES=1 env: TESTEXTRAS=1
- go: 1.12.x - go: 1.12.x
env: GO111MODULE=on env: GO111MODULE=on
- go: 1.11.x - go: 1.11.x
@ -35,7 +35,7 @@ install:
script: script:
- set -e - set -e
- if [[ -n "${TESTEXAMPLES}" ]]; then examples/examples_test.sh; exit 0; fi - if [[ -n "${TESTEXTRAS}" ]]; then examples/examples_test.sh; interop/interop_test.sh; make testsubmodule; exit 0; fi
- if [[ -n "${VET}" ]]; then ./vet.sh; fi - if [[ -n "${VET}" ]]; then ./vet.sh; fi
- if [[ -n "${GAE}" ]]; then make testappengine; exit 0; fi - if [[ -n "${GAE}" ]]; then make testappengine; exit 0; fi
- if [[ -n "${RACE}" ]]; then make testrace; exit 0; fi - if [[ -n "${RACE}" ]]; then make testrace; exit 0; fi

View File

@ -19,6 +19,9 @@ proto:
test: testdeps test: testdeps
go test -cpu 1,4 -timeout 7m google.golang.org/grpc/... go test -cpu 1,4 -timeout 7m google.golang.org/grpc/...
testsubmodule: testdeps
cd security/advancedtls && go test -cpu 1,4 -timeout 7m google.golang.org/grpc/security/advancedtls/...
testappengine: testappenginedeps testappengine: testappenginedeps
goapp test -cpu 1,4 -timeout 7m google.golang.org/grpc/... goapp test -cpu 1,4 -timeout 7m google.golang.org/grpc/...

View File

@ -93,6 +93,22 @@ To build Go code, there are several options:
#### Compiling error, undefined: grpc.SupportPackageIsVersion #### Compiling error, undefined: grpc.SupportPackageIsVersion
##### If you are using Go modules:
Please ensure your gRPC-Go version is `require`d at the appropriate version in
the same module containing the generated `.pb.go` files. For example,
`SupportPackageIsVersion6` needs `v1.27.0`, so in your `go.mod` file:
```
module <your module name>
require (
google.golang.org/grpc v1.27.0
)
```
##### If you are *not* using Go modules:
Please update proto package, gRPC package and rebuild the proto files: Please update proto package, gRPC package and rebuild the proto files:
- `go get -u github.com/golang/protobuf/{proto,protoc-gen-go}` - `go get -u github.com/golang/protobuf/{proto,protoc-gen-go}`
- `go get -u google.golang.org/grpc` - `go get -u google.golang.org/grpc`
@ -114,6 +130,10 @@ possible reasons, including:
1. mis-configured transport credentials, connection failed on handshaking 1. mis-configured transport credentials, connection failed on handshaking
1. bytes disrupted, possibly by a proxy in between 1. bytes disrupted, possibly by a proxy in between
1. server shutdown 1. server shutdown
1. Keepalive parameters caused connection shutdown, for example if you have configured
your server to terminate connections regularly to [trigger DNS lookups](https://github.com/grpc/grpc-go/issues/3170#issuecomment-552517779).
If this is the case, you may want to increase your [MaxConnectionAgeGrace](https://pkg.go.dev/google.golang.org/grpc/keepalive?tab=doc#ServerParameters),
to allow longer RPC calls to finish.
It can be tricky to debug this because the error happens on the client side but It can be tricky to debug this because the error happens on the client side but
the root cause of the connection being closed is on the server side. Turn on the root cause of the connection being closed is on the server side. Turn on

70
vendor/google.golang.org/grpc/attributes/attributes.go generated vendored Normal file
View File

@ -0,0 +1,70 @@
/*
*
* Copyright 2019 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 attributes defines a generic key/value store used in various gRPC
// components.
//
// All APIs in this package are EXPERIMENTAL.
package attributes
import "fmt"
// Attributes is an immutable struct for storing and retrieving generic
// key/value pairs. Keys must be hashable, and users should define their own
// types for keys.
type Attributes struct {
m map[interface{}]interface{}
}
// New returns a new Attributes containing all key/value pairs in kvs. If the
// same key appears multiple times, the last value overwrites all previous
// values for that key. Panics if len(kvs) is not even.
func New(kvs ...interface{}) *Attributes {
if len(kvs)%2 != 0 {
panic(fmt.Sprintf("attributes.New called with unexpected input: len(kvs) = %v", len(kvs)))
}
a := &Attributes{m: make(map[interface{}]interface{}, len(kvs)/2)}
for i := 0; i < len(kvs)/2; i++ {
a.m[kvs[i*2]] = kvs[i*2+1]
}
return a
}
// WithValues returns a new Attributes containing all key/value pairs in a and
// kvs. Panics if len(kvs) is not even. 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.
func (a *Attributes) WithValues(kvs ...interface{}) *Attributes {
if len(kvs)%2 != 0 {
panic(fmt.Sprintf("attributes.New called with unexpected input: len(kvs) = %v", len(kvs)))
}
n := &Attributes{m: make(map[interface{}]interface{}, len(a.m)+len(kvs)/2)}
for k, v := range a.m {
n.m[k] = v
}
for i := 0; i < len(kvs)/2; i++ {
n.m[kvs[i*2]] = kvs[i*2+1]
}
return n
}
// Value returns the value associated with these attributes for key, or nil if
// no value is associated with key.
func (a *Attributes) Value(key interface{}) interface{} {
return a.m[key]
}

View File

@ -117,6 +117,15 @@ type NewSubConnOptions struct {
HealthCheckEnabled bool HealthCheckEnabled bool
} }
// State contains the balancer's state relevant to the gRPC ClientConn.
type State struct {
// State contains the connectivity state of the balancer, which is used to
// determine the state of the ClientConn.
ConnectivityState connectivity.State
// Picker is used to choose connections (SubConns) for RPCs.
Picker V2Picker
}
// ClientConn represents a gRPC ClientConn. // ClientConn represents a gRPC ClientConn.
// //
// This interface is to be implemented by gRPC. Users should not need a // This interface is to be implemented by gRPC. Users should not need a
@ -137,10 +146,19 @@ type ClientConn interface {
// //
// gRPC will update the connectivity state of the ClientConn, and will call pick // gRPC will update the connectivity state of the ClientConn, and will call pick
// on the new picker to pick new SubConn. // on the new picker to pick new SubConn.
//
// Deprecated: use UpdateState instead
UpdateBalancerState(s connectivity.State, p Picker) UpdateBalancerState(s connectivity.State, p Picker)
// UpdateState notifies gRPC that the balancer's internal state has
// changed.
//
// gRPC will update the connectivity state of the ClientConn, and will call pick
// on the new picker to pick new SubConns.
UpdateState(State)
// ResolveNow is called by balancer to notify gRPC to do a name resolving. // ResolveNow is called by balancer to notify gRPC to do a name resolving.
ResolveNow(resolver.ResolveNowOption) ResolveNow(resolver.ResolveNowOptions)
// Target returns the dial target for this ClientConn. // Target returns the dial target for this ClientConn.
// //
@ -185,11 +203,14 @@ type ConfigParser interface {
ParseConfig(LoadBalancingConfigJSON json.RawMessage) (serviceconfig.LoadBalancingConfig, error) ParseConfig(LoadBalancingConfigJSON json.RawMessage) (serviceconfig.LoadBalancingConfig, error)
} }
// PickOptions contains addition information for the Pick operation. // PickInfo contains additional information for the Pick operation.
type PickOptions struct { type PickInfo struct {
// FullMethodName is the method name that NewClientStream() is called // FullMethodName is the method name that NewClientStream() is called
// with. The canonical format is /service/Method. // with. The canonical format is /service/Method.
FullMethodName string FullMethodName string
// Ctx is the RPC's context, and may contain relevant RPC-level information
// like the outgoing header metadata.
Ctx context.Context
} }
// DoneInfo contains additional information for done. // DoneInfo contains additional information for done.
@ -215,7 +236,7 @@ var (
ErrNoSubConnAvailable = errors.New("no SubConn is available") ErrNoSubConnAvailable = errors.New("no SubConn is available")
// ErrTransientFailure indicates all SubConns are in TransientFailure. // ErrTransientFailure indicates all SubConns are in TransientFailure.
// WaitForReady RPCs will block, non-WaitForReady RPCs will fail. // WaitForReady RPCs will block, non-WaitForReady RPCs will fail.
ErrTransientFailure = errors.New("all SubConns are in TransientFailure") ErrTransientFailure = TransientFailureError(errors.New("all SubConns are in TransientFailure"))
) )
// Picker is used by gRPC to pick a SubConn to send an RPC. // Picker is used by gRPC to pick a SubConn to send an RPC.
@ -223,6 +244,8 @@ var (
// internal state has changed. // internal state has changed.
// //
// The pickers used by gRPC can be updated by ClientConn.UpdateBalancerState(). // The pickers used by gRPC can be updated by ClientConn.UpdateBalancerState().
//
// Deprecated: use V2Picker instead
type Picker interface { type Picker interface {
// Pick returns the SubConn to be used to send the RPC. // Pick returns the SubConn to be used to send the RPC.
// The returned SubConn must be one returned by NewSubConn(). // The returned SubConn must be one returned by NewSubConn().
@ -243,18 +266,76 @@ type Picker interface {
// //
// If the returned error is not nil: // If the returned error is not nil:
// - If the error is ErrNoSubConnAvailable, gRPC will block until UpdateBalancerState() // - If the error is ErrNoSubConnAvailable, gRPC will block until UpdateBalancerState()
// - If the error is ErrTransientFailure: // - If the error is ErrTransientFailure or implements IsTransientFailure()
// bool, returning true:
// - If the RPC is wait-for-ready, gRPC will block until UpdateBalancerState() // - If the RPC is wait-for-ready, gRPC will block until UpdateBalancerState()
// is called to pick again; // is called to pick again;
// - Otherwise, RPC will fail with unavailable error. // - Otherwise, RPC will fail with unavailable error.
// - Else (error is other non-nil error): // - Else (error is other non-nil error):
// - The RPC will fail with unavailable error. // - The RPC will fail with the error's status code, or Unknown if it is
// not a status error.
// //
// The returned done() function will be called once the rpc has finished, // The returned done() function will be called once the rpc has finished,
// with the final status of that RPC. If the SubConn returned is not a // with the final status of that RPC. If the SubConn returned is not a
// valid SubConn type, done may not be called. done may be nil if balancer // valid SubConn type, done may not be called. done may be nil if balancer
// doesn't care about the RPC status. // doesn't care about the RPC status.
Pick(ctx context.Context, opts PickOptions) (conn SubConn, done func(DoneInfo), err error) Pick(ctx context.Context, info PickInfo) (conn SubConn, done func(DoneInfo), err error)
}
// PickResult contains information related to a connection chosen for an RPC.
type PickResult struct {
// SubConn is the connection to use for this pick, if its state is Ready.
// If the state is not Ready, gRPC will block the RPC until a new Picker is
// provided by the balancer (using ClientConn.UpdateState). The SubConn
// must be one returned by ClientConn.NewSubConn.
SubConn SubConn
// Done is called when the RPC is completed. If the SubConn is not ready,
// this will be called with a nil parameter. If the SubConn is not a valid
// type, Done may not be called. May be nil if the balancer does not wish
// to be notified when the RPC completes.
Done func(DoneInfo)
}
type transientFailureError struct {
error
}
func (e *transientFailureError) IsTransientFailure() bool { return true }
// TransientFailureError wraps err in an error implementing
// IsTransientFailure() bool, returning true.
func TransientFailureError(err error) error {
return &transientFailureError{error: err}
}
// V2Picker is used by gRPC to pick a SubConn to send an RPC.
// Balancer is expected to generate a new picker from its snapshot every time its
// internal state has changed.
//
// The pickers used by gRPC can be updated by ClientConn.UpdateBalancerState().
type V2Picker interface {
// Pick returns the connection to use for this RPC and related information.
//
// Pick should not block. If the balancer needs to do I/O or any blocking
// or time-consuming work to service this call, it should return
// ErrNoSubConnAvailable, and the Pick call will be repeated by gRPC when
// the Picker is updated (using ClientConn.UpdateState).
//
// If an error is returned:
//
// - If the error is ErrNoSubConnAvailable, gRPC will block until a new
// Picker is provided by the balancer (using ClientConn.UpdateState).
//
// - If the error implements IsTransientFailure() bool, returning true,
// wait for ready RPCs will wait, but non-wait for ready RPCs will be
// terminated with this error's Error() string and status code
// Unavailable.
//
// - Any other errors terminate all RPCs with the code and message
// provided. If the error is not a status error, it will be converted by
// gRPC to a status error with code Unknown.
Pick(info PickInfo) (PickResult, error)
} }
// Balancer takes input from gRPC, manages SubConns, and collects and aggregates // Balancer takes input from gRPC, manages SubConns, and collects and aggregates
@ -292,8 +373,11 @@ type Balancer interface {
// SubConnState describes the state of a SubConn. // SubConnState describes the state of a SubConn.
type SubConnState struct { type SubConnState struct {
// ConnectivityState is the connectivity state of the SubConn.
ConnectivityState connectivity.State ConnectivityState connectivity.State
// TODO: add last connection error // ConnectionError is set if the ConnectivityState is TransientFailure,
// describing the reason the SubConn failed. Otherwise, it is nil.
ConnectionError error
} }
// ClientConnState describes the state of a ClientConn relevant to the // ClientConnState describes the state of a ClientConn relevant to the
@ -335,9 +419,8 @@ type V2Balancer interface {
// //
// It's not thread safe. // It's not thread safe.
type ConnectivityStateEvaluator struct { type ConnectivityStateEvaluator struct {
numReady uint64 // Number of addrConns in ready state. numReady uint64 // Number of addrConns in ready state.
numConnecting uint64 // Number of addrConns in connecting state. numConnecting uint64 // Number of addrConns in connecting state.
numTransientFailure uint64 // Number of addrConns in transientFailure.
} }
// RecordTransition records state change happening in subConn and based on that // RecordTransition records state change happening in subConn and based on that
@ -357,8 +440,6 @@ func (cse *ConnectivityStateEvaluator) RecordTransition(oldState, newState conne
cse.numReady += updateVal cse.numReady += updateVal
case connectivity.Connecting: case connectivity.Connecting:
cse.numConnecting += updateVal cse.numConnecting += updateVal
case connectivity.TransientFailure:
cse.numTransientFailure += updateVal
} }
} }

View File

@ -20,6 +20,8 @@ package base
import ( import (
"context" "context"
"errors"
"fmt"
"google.golang.org/grpc/balancer" "google.golang.org/grpc/balancer"
"google.golang.org/grpc/connectivity" "google.golang.org/grpc/connectivity"
@ -28,25 +30,32 @@ import (
) )
type baseBuilder struct { type baseBuilder struct {
name string name string
pickerBuilder PickerBuilder pickerBuilder PickerBuilder
config Config v2PickerBuilder V2PickerBuilder
config Config
} }
func (bb *baseBuilder) Build(cc balancer.ClientConn, opt balancer.BuildOptions) balancer.Balancer { func (bb *baseBuilder) Build(cc balancer.ClientConn, opt balancer.BuildOptions) balancer.Balancer {
return &baseBalancer{ bal := &baseBalancer{
cc: cc, cc: cc,
pickerBuilder: bb.pickerBuilder, pickerBuilder: bb.pickerBuilder,
v2PickerBuilder: bb.v2PickerBuilder,
subConns: make(map[resolver.Address]balancer.SubConn), subConns: make(map[resolver.Address]balancer.SubConn),
scStates: make(map[balancer.SubConn]connectivity.State), scStates: make(map[balancer.SubConn]connectivity.State),
csEvltr: &balancer.ConnectivityStateEvaluator{}, csEvltr: &balancer.ConnectivityStateEvaluator{},
// Initialize picker to a picker that always return config: bb.config,
// ErrNoSubConnAvailable, because when state of a SubConn changes, we
// may call UpdateBalancerState with this picker.
picker: NewErrPicker(balancer.ErrNoSubConnAvailable),
config: bb.config,
} }
// Initialize picker to a picker that always returns
// ErrNoSubConnAvailable, because when state of a SubConn changes, we
// may call UpdateState with this picker.
if bb.pickerBuilder != nil {
bal.picker = NewErrPicker(balancer.ErrNoSubConnAvailable)
} else {
bal.v2Picker = NewErrPickerV2(balancer.ErrNoSubConnAvailable)
}
return bal
} }
func (bb *baseBuilder) Name() string { func (bb *baseBuilder) Name() string {
@ -56,8 +65,9 @@ func (bb *baseBuilder) Name() string {
var _ balancer.V2Balancer = (*baseBalancer)(nil) // Assert that we implement V2Balancer var _ balancer.V2Balancer = (*baseBalancer)(nil) // Assert that we implement V2Balancer
type baseBalancer struct { type baseBalancer struct {
cc balancer.ClientConn cc balancer.ClientConn
pickerBuilder PickerBuilder pickerBuilder PickerBuilder
v2PickerBuilder V2PickerBuilder
csEvltr *balancer.ConnectivityStateEvaluator csEvltr *balancer.ConnectivityStateEvaluator
state connectivity.State state connectivity.State
@ -65,15 +75,36 @@ type baseBalancer struct {
subConns map[resolver.Address]balancer.SubConn subConns map[resolver.Address]balancer.SubConn
scStates map[balancer.SubConn]connectivity.State scStates map[balancer.SubConn]connectivity.State
picker balancer.Picker picker balancer.Picker
v2Picker balancer.V2Picker
config Config config Config
resolverErr error // the last error reported by the resolver; cleared on successful resolution
connErr error // the last connection error; cleared upon leaving TransientFailure
} }
func (b *baseBalancer) HandleResolvedAddrs(addrs []resolver.Address, err error) { func (b *baseBalancer) HandleResolvedAddrs(addrs []resolver.Address, err error) {
panic("not implemented") panic("not implemented")
} }
func (b *baseBalancer) ResolverError(error) { func (b *baseBalancer) ResolverError(err error) {
// Ignore b.resolverErr = err
if len(b.subConns) == 0 {
b.state = connectivity.TransientFailure
}
if b.state != connectivity.TransientFailure {
// The picker will not change since the balancer does not currently
// report an error.
return
}
b.regeneratePicker()
if b.picker != nil {
b.cc.UpdateBalancerState(b.state, b.picker)
} else {
b.cc.UpdateState(balancer.State{
ConnectivityState: b.state,
Picker: b.v2Picker,
})
}
} }
func (b *baseBalancer) UpdateClientConnState(s balancer.ClientConnState) error { func (b *baseBalancer) UpdateClientConnState(s balancer.ClientConnState) error {
@ -82,6 +113,8 @@ func (b *baseBalancer) UpdateClientConnState(s balancer.ClientConnState) error {
if grpclog.V(2) { if grpclog.V(2) {
grpclog.Infoln("base.baseBalancer: got new ClientConn state: ", s) grpclog.Infoln("base.baseBalancer: got new ClientConn state: ", s)
} }
// Successful resolution; clear resolver error and ensure we return nil.
b.resolverErr = nil
// addrsSet is the set converted from addrs, it's used for quick lookup of an address. // addrsSet is the set converted from addrs, it's used for quick lookup of an address.
addrsSet := make(map[resolver.Address]struct{}) addrsSet := make(map[resolver.Address]struct{})
for _, a := range s.ResolverState.Addresses { for _, a := range s.ResolverState.Addresses {
@ -107,27 +140,65 @@ func (b *baseBalancer) UpdateClientConnState(s balancer.ClientConnState) error {
// The entry will be deleted in HandleSubConnStateChange. // The entry will be deleted in HandleSubConnStateChange.
} }
} }
// If resolver state contains no addresses, return an error so ClientConn
// will trigger re-resolve. Also records this as an resolver error, so when
// the overall state turns transient failure, the error message will have
// the zero address information.
if len(s.ResolverState.Addresses) == 0 {
b.ResolverError(errors.New("produced zero addresses"))
return balancer.ErrBadResolverState
}
return nil return nil
} }
// mergeErrors builds an error from the last connection error and the last
// resolver error. Must only be called if b.state is TransientFailure.
func (b *baseBalancer) mergeErrors() error {
// connErr must always be non-nil unless there are no SubConns, in which
// case resolverErr must be non-nil.
if b.connErr == nil {
return fmt.Errorf("last resolver error: %v", b.resolverErr)
}
if b.resolverErr == nil {
return fmt.Errorf("last connection error: %v", b.connErr)
}
return fmt.Errorf("last connection error: %v; last resolver error: %v", b.connErr, b.resolverErr)
}
// regeneratePicker takes a snapshot of the balancer, and generates a picker // regeneratePicker takes a snapshot of the balancer, and generates a picker
// from it. The picker is // from it. The picker is
// - errPicker with ErrTransientFailure if the balancer is in TransientFailure, // - errPicker if the balancer is in TransientFailure,
// - built by the pickerBuilder with all READY SubConns otherwise. // - built by the pickerBuilder with all READY SubConns otherwise.
func (b *baseBalancer) regeneratePicker() { func (b *baseBalancer) regeneratePicker() {
if b.state == connectivity.TransientFailure { if b.state == connectivity.TransientFailure {
b.picker = NewErrPicker(balancer.ErrTransientFailure) if b.pickerBuilder != nil {
b.picker = NewErrPicker(balancer.ErrTransientFailure)
} else {
b.v2Picker = NewErrPickerV2(balancer.TransientFailureError(b.mergeErrors()))
}
return return
} }
readySCs := make(map[resolver.Address]balancer.SubConn) if b.pickerBuilder != nil {
readySCs := make(map[resolver.Address]balancer.SubConn)
// Filter out all ready SCs from full subConn map. // Filter out all ready SCs from full subConn map.
for addr, sc := range b.subConns { for addr, sc := range b.subConns {
if st, ok := b.scStates[sc]; ok && st == connectivity.Ready { if st, ok := b.scStates[sc]; ok && st == connectivity.Ready {
readySCs[addr] = sc readySCs[addr] = sc
}
} }
b.picker = b.pickerBuilder.Build(readySCs)
} else {
readySCs := make(map[balancer.SubConn]SubConnInfo)
// Filter out all ready SCs from full subConn map.
for addr, sc := range b.subConns {
if st, ok := b.scStates[sc]; ok && st == connectivity.Ready {
readySCs[sc] = SubConnInfo{Address: addr}
}
}
b.v2Picker = b.v2PickerBuilder.Build(PickerBuildInfo{ReadySCs: readySCs})
} }
b.picker = b.pickerBuilder.Build(readySCs)
} }
func (b *baseBalancer) HandleSubConnStateChange(sc balancer.SubConn, s connectivity.State) { func (b *baseBalancer) HandleSubConnStateChange(sc balancer.SubConn, s connectivity.State) {
@ -146,6 +217,12 @@ func (b *baseBalancer) UpdateSubConnState(sc balancer.SubConn, state balancer.Su
} }
return return
} }
if oldS == connectivity.TransientFailure && s == connectivity.Connecting {
// Once a subconn enters TRANSIENT_FAILURE, ignore subsequent
// CONNECTING transitions to prevent the aggregated state from being
// always CONNECTING when many backends exist but are all down.
return
}
b.scStates[sc] = s b.scStates[sc] = s
switch s { switch s {
case connectivity.Idle: case connectivity.Idle:
@ -154,22 +231,27 @@ func (b *baseBalancer) UpdateSubConnState(sc balancer.SubConn, state balancer.Su
// When an address was removed by resolver, b called RemoveSubConn but // When an address was removed by resolver, b called RemoveSubConn but
// kept the sc's state in scStates. Remove state for this sc here. // kept the sc's state in scStates. Remove state for this sc here.
delete(b.scStates, sc) delete(b.scStates, sc)
case connectivity.TransientFailure:
// Save error to be reported via picker.
b.connErr = state.ConnectionError
} }
oldAggrState := b.state
b.state = b.csEvltr.RecordTransition(oldS, s) b.state = b.csEvltr.RecordTransition(oldS, s)
// Regenerate picker when one of the following happens: // Regenerate picker when one of the following happens:
// - this sc became ready from not-ready // - this sc entered or left ready
// - this sc became not-ready from ready // - the aggregated state of balancer is TransientFailure
// - the aggregated state of balancer became TransientFailure from non-TransientFailure // (may need to update error message)
// - the aggregated state of balancer became non-TransientFailure from TransientFailure
if (s == connectivity.Ready) != (oldS == connectivity.Ready) || if (s == connectivity.Ready) != (oldS == connectivity.Ready) ||
(b.state == connectivity.TransientFailure) != (oldAggrState == connectivity.TransientFailure) { b.state == connectivity.TransientFailure {
b.regeneratePicker() b.regeneratePicker()
} }
b.cc.UpdateBalancerState(b.state, b.picker) if b.picker != nil {
b.cc.UpdateBalancerState(b.state, b.picker)
} else {
b.cc.UpdateState(balancer.State{ConnectivityState: b.state, Picker: b.v2Picker})
}
} }
// Close is a nop because base balancer doesn't have internal state to clean up, // Close is a nop because base balancer doesn't have internal state to clean up,
@ -186,6 +268,19 @@ type errPicker struct {
err error // Pick() always returns this err. err error // Pick() always returns this err.
} }
func (p *errPicker) Pick(ctx context.Context, opts balancer.PickOptions) (balancer.SubConn, func(balancer.DoneInfo), error) { func (p *errPicker) Pick(context.Context, balancer.PickInfo) (balancer.SubConn, func(balancer.DoneInfo), error) {
return nil, nil, p.err return nil, nil, p.err
} }
// NewErrPickerV2 returns a V2Picker that always returns err on Pick().
func NewErrPickerV2(err error) balancer.V2Picker {
return &errPickerV2{err: err}
}
type errPickerV2 struct {
err error // Pick() always returns this err.
}
func (p *errPickerV2) Pick(info balancer.PickInfo) (balancer.PickResult, error) {
return balancer.PickResult{}, p.err
}

View File

@ -42,6 +42,26 @@ type PickerBuilder interface {
Build(readySCs map[resolver.Address]balancer.SubConn) balancer.Picker Build(readySCs map[resolver.Address]balancer.SubConn) balancer.Picker
} }
// V2PickerBuilder creates balancer.V2Picker.
type V2PickerBuilder interface {
// Build returns a picker that will be used by gRPC to pick a SubConn.
Build(info PickerBuildInfo) balancer.V2Picker
}
// PickerBuildInfo contains information needed by the picker builder to
// construct a picker.
type PickerBuildInfo struct {
// ReadySCs is a map from all ready SubConns to the Addresses used to
// create them.
ReadySCs map[balancer.SubConn]SubConnInfo
}
// SubConnInfo contains information about a SubConn created by the base
// balancer.
type SubConnInfo struct {
Address resolver.Address // the address used to create this SubConn
}
// NewBalancerBuilder returns a balancer builder. The balancers // NewBalancerBuilder returns a balancer builder. The balancers
// built by this builder will use the picker builder to build pickers. // built by this builder will use the picker builder to build pickers.
func NewBalancerBuilder(name string, pb PickerBuilder) balancer.Builder { func NewBalancerBuilder(name string, pb PickerBuilder) balancer.Builder {
@ -62,3 +82,12 @@ func NewBalancerBuilderWithConfig(name string, pb PickerBuilder, config Config)
config: config, config: config,
} }
} }
// NewBalancerBuilderV2 returns a base balancer builder configured by the provided config.
func NewBalancerBuilderV2(name string, pb V2PickerBuilder, config Config) balancer.Builder {
return &baseBuilder{
name: name,
v2PickerBuilder: pb,
config: config,
}
}

View File

@ -22,14 +22,12 @@
package roundrobin package roundrobin
import ( import (
"context"
"sync" "sync"
"google.golang.org/grpc/balancer" "google.golang.org/grpc/balancer"
"google.golang.org/grpc/balancer/base" "google.golang.org/grpc/balancer/base"
"google.golang.org/grpc/grpclog" "google.golang.org/grpc/grpclog"
"google.golang.org/grpc/internal/grpcrand" "google.golang.org/grpc/internal/grpcrand"
"google.golang.org/grpc/resolver"
) )
// Name is the name of round_robin balancer. // Name is the name of round_robin balancer.
@ -37,7 +35,7 @@ const Name = "round_robin"
// newBuilder creates a new roundrobin balancer builder. // newBuilder creates a new roundrobin balancer builder.
func newBuilder() balancer.Builder { func newBuilder() balancer.Builder {
return base.NewBalancerBuilderWithConfig(Name, &rrPickerBuilder{}, base.Config{HealthCheck: true}) return base.NewBalancerBuilderV2(Name, &rrPickerBuilder{}, base.Config{HealthCheck: true})
} }
func init() { func init() {
@ -46,13 +44,13 @@ func init() {
type rrPickerBuilder struct{} type rrPickerBuilder struct{}
func (*rrPickerBuilder) Build(readySCs map[resolver.Address]balancer.SubConn) balancer.Picker { func (*rrPickerBuilder) Build(info base.PickerBuildInfo) balancer.V2Picker {
grpclog.Infof("roundrobinPicker: newPicker called with readySCs: %v", readySCs) grpclog.Infof("roundrobinPicker: newPicker called with info: %v", info)
if len(readySCs) == 0 { if len(info.ReadySCs) == 0 {
return base.NewErrPicker(balancer.ErrNoSubConnAvailable) return base.NewErrPickerV2(balancer.ErrNoSubConnAvailable)
} }
var scs []balancer.SubConn var scs []balancer.SubConn
for _, sc := range readySCs { for sc := range info.ReadySCs {
scs = append(scs, sc) scs = append(scs, sc)
} }
return &rrPicker{ return &rrPicker{
@ -74,10 +72,10 @@ type rrPicker struct {
next int next int
} }
func (p *rrPicker) Pick(ctx context.Context, opts balancer.PickOptions) (balancer.SubConn, func(balancer.DoneInfo), error) { func (p *rrPicker) Pick(balancer.PickInfo) (balancer.PickResult, error) {
p.mu.Lock() p.mu.Lock()
sc := p.subConns[p.next] sc := p.subConns[p.next]
p.next = (p.next + 1) % len(p.subConns) p.next = (p.next + 1) % len(p.subConns)
p.mu.Unlock() p.mu.Unlock()
return sc, nil, nil return balancer.PickResult{SubConn: sc}, nil
} }

View File

@ -24,8 +24,8 @@ import (
"google.golang.org/grpc/balancer" "google.golang.org/grpc/balancer"
"google.golang.org/grpc/connectivity" "google.golang.org/grpc/connectivity"
"google.golang.org/grpc/grpclog"
"google.golang.org/grpc/internal/buffer" "google.golang.org/grpc/internal/buffer"
"google.golang.org/grpc/internal/channelz"
"google.golang.org/grpc/internal/grpcsync" "google.golang.org/grpc/internal/grpcsync"
"google.golang.org/grpc/resolver" "google.golang.org/grpc/resolver"
) )
@ -34,6 +34,7 @@ import (
type scStateUpdate struct { type scStateUpdate struct {
sc balancer.SubConn sc balancer.SubConn
state connectivity.State state connectivity.State
err error
} }
// ccBalancerWrapper is a wrapper on top of cc for balancers. // ccBalancerWrapper is a wrapper on top of cc for balancers.
@ -74,7 +75,7 @@ func (ccb *ccBalancerWrapper) watcher() {
ccb.balancerMu.Lock() ccb.balancerMu.Lock()
su := t.(*scStateUpdate) su := t.(*scStateUpdate)
if ub, ok := ccb.balancer.(balancer.V2Balancer); ok { if ub, ok := ccb.balancer.(balancer.V2Balancer); ok {
ub.UpdateSubConnState(su.sc, balancer.SubConnState{ConnectivityState: su.state}) ub.UpdateSubConnState(su.sc, balancer.SubConnState{ConnectivityState: su.state, ConnectionError: su.err})
} else { } else {
ccb.balancer.HandleSubConnStateChange(su.sc, su.state) ccb.balancer.HandleSubConnStateChange(su.sc, su.state)
} }
@ -91,7 +92,7 @@ func (ccb *ccBalancerWrapper) watcher() {
for acbw := range scs { for acbw := range scs {
ccb.cc.removeAddrConn(acbw.getAddrConn(), errConnDrain) ccb.cc.removeAddrConn(acbw.getAddrConn(), errConnDrain)
} }
ccb.UpdateBalancerState(connectivity.Connecting, nil) ccb.UpdateState(balancer.State{ConnectivityState: connectivity.Connecting, Picker: nil})
return return
} }
} }
@ -101,7 +102,7 @@ func (ccb *ccBalancerWrapper) close() {
ccb.done.Fire() ccb.done.Fire()
} }
func (ccb *ccBalancerWrapper) handleSubConnStateChange(sc balancer.SubConn, s connectivity.State) { func (ccb *ccBalancerWrapper) handleSubConnStateChange(sc balancer.SubConn, s connectivity.State, err error) {
// When updating addresses for a SubConn, if the address in use is not in // When updating addresses for a SubConn, if the address in use is not in
// the new addresses, the old ac will be tearDown() and a new ac will be // the new addresses, the old ac will be tearDown() and a new ac will be
// created. tearDown() generates a state change with Shutdown state, we // created. tearDown() generates a state change with Shutdown state, we
@ -115,6 +116,7 @@ func (ccb *ccBalancerWrapper) handleSubConnStateChange(sc balancer.SubConn, s co
ccb.scBuffer.Put(&scStateUpdate{ ccb.scBuffer.Put(&scStateUpdate{
sc: sc, sc: sc,
state: s, state: s,
err: err,
}) })
} }
@ -186,7 +188,22 @@ func (ccb *ccBalancerWrapper) UpdateBalancerState(s connectivity.State, p balanc
ccb.cc.csMgr.updateState(s) ccb.cc.csMgr.updateState(s)
} }
func (ccb *ccBalancerWrapper) ResolveNow(o resolver.ResolveNowOption) { func (ccb *ccBalancerWrapper) UpdateState(s balancer.State) {
ccb.mu.Lock()
defer ccb.mu.Unlock()
if ccb.subConns == nil {
return
}
// Update picker before updating state. Even though the ordering here does
// not matter, it can lead to multiple calls of Pick in the common start-up
// case where we wait for ready and then perform an RPC. If the picker is
// updated later, we could call the "connecting" picker when the state is
// updated, and then call the "ready" picker after the picker gets updated.
ccb.cc.blockingpicker.updatePickerV2(s.Picker)
ccb.cc.csMgr.updateState(s.ConnectivityState)
}
func (ccb *ccBalancerWrapper) ResolveNow(o resolver.ResolveNowOptions) {
ccb.cc.resolveNow(o) ccb.cc.resolveNow(o)
} }
@ -228,7 +245,7 @@ func (acbw *acBalancerWrapper) UpdateAddresses(addrs []resolver.Address) {
ac, err := cc.newAddrConn(addrs, opts) ac, err := cc.newAddrConn(addrs, opts)
if err != nil { if err != nil {
grpclog.Warningf("acBalancerWrapper: UpdateAddresses: failed to newAddrConn: %v", err) channelz.Warningf(acbw.ac.channelzID, "acBalancerWrapper: UpdateAddresses: failed to newAddrConn: %v", err)
return return
} }
acbw.ac = ac acbw.ac = ac

View File

@ -19,7 +19,6 @@
package grpc package grpc
import ( import (
"context"
"sync" "sync"
"google.golang.org/grpc/balancer" "google.golang.org/grpc/balancer"
@ -49,7 +48,7 @@ func (bwb *balancerWrapperBuilder) Build(cc balancer.ClientConn, opts balancer.B
csEvltr: &balancer.ConnectivityStateEvaluator{}, csEvltr: &balancer.ConnectivityStateEvaluator{},
state: connectivity.Idle, state: connectivity.Idle,
} }
cc.UpdateBalancerState(connectivity.Idle, bw) cc.UpdateState(balancer.State{ConnectivityState: connectivity.Idle, Picker: bw})
go bw.lbWatcher() go bw.lbWatcher()
return bw return bw
} }
@ -243,7 +242,7 @@ func (bw *balancerWrapper) HandleSubConnStateChange(sc balancer.SubConn, s conne
if bw.state != sa { if bw.state != sa {
bw.state = sa bw.state = sa
} }
bw.cc.UpdateBalancerState(bw.state, bw) bw.cc.UpdateState(balancer.State{ConnectivityState: bw.state, Picker: bw})
if s == connectivity.Shutdown { if s == connectivity.Shutdown {
// Remove state for this sc. // Remove state for this sc.
delete(bw.connSt, sc) delete(bw.connSt, sc)
@ -275,17 +274,17 @@ func (bw *balancerWrapper) Close() {
// The picker is the balancerWrapper itself. // The picker is the balancerWrapper itself.
// It either blocks or returns error, consistent with v1 balancer Get(). // It either blocks or returns error, consistent with v1 balancer Get().
func (bw *balancerWrapper) Pick(ctx context.Context, opts balancer.PickOptions) (sc balancer.SubConn, done func(balancer.DoneInfo), err error) { func (bw *balancerWrapper) Pick(info balancer.PickInfo) (result balancer.PickResult, err error) {
failfast := true // Default failfast is true. failfast := true // Default failfast is true.
if ss, ok := rpcInfoFromContext(ctx); ok { if ss, ok := rpcInfoFromContext(info.Ctx); ok {
failfast = ss.failfast failfast = ss.failfast
} }
a, p, err := bw.balancer.Get(ctx, BalancerGetOptions{BlockingWait: !failfast}) a, p, err := bw.balancer.Get(info.Ctx, BalancerGetOptions{BlockingWait: !failfast})
if err != nil { if err != nil {
return nil, nil, err return balancer.PickResult{}, toRPCErr(err)
} }
if p != nil { if p != nil {
done = func(balancer.DoneInfo) { p() } result.Done = func(balancer.DoneInfo) { p() }
defer func() { defer func() {
if err != nil { if err != nil {
p() p()
@ -297,38 +296,39 @@ func (bw *balancerWrapper) Pick(ctx context.Context, opts balancer.PickOptions)
defer bw.mu.Unlock() defer bw.mu.Unlock()
if bw.pickfirst { if bw.pickfirst {
// Get the first sc in conns. // Get the first sc in conns.
for _, sc := range bw.conns { for _, result.SubConn = range bw.conns {
return sc, done, nil return result, nil
} }
return nil, nil, balancer.ErrNoSubConnAvailable return balancer.PickResult{}, balancer.ErrNoSubConnAvailable
} }
sc, ok1 := bw.conns[resolver.Address{ var ok1 bool
result.SubConn, ok1 = bw.conns[resolver.Address{
Addr: a.Addr, Addr: a.Addr,
Type: resolver.Backend, Type: resolver.Backend,
ServerName: "", ServerName: "",
Metadata: a.Metadata, Metadata: a.Metadata,
}] }]
s, ok2 := bw.connSt[sc] s, ok2 := bw.connSt[result.SubConn]
if !ok1 || !ok2 { if !ok1 || !ok2 {
// This can only happen due to a race where Get() returned an address // This can only happen due to a race where Get() returned an address
// that was subsequently removed by Notify. In this case we should // that was subsequently removed by Notify. In this case we should
// retry always. // retry always.
return nil, nil, balancer.ErrNoSubConnAvailable return balancer.PickResult{}, balancer.ErrNoSubConnAvailable
} }
switch s.s { switch s.s {
case connectivity.Ready, connectivity.Idle: case connectivity.Ready, connectivity.Idle:
return sc, done, nil return result, nil
case connectivity.Shutdown, connectivity.TransientFailure: case connectivity.Shutdown, connectivity.TransientFailure:
// If the returned sc has been shut down or is in transient failure, // If the returned sc has been shut down or is in transient failure,
// return error, and this RPC will fail or wait for another picker (if // return error, and this RPC will fail or wait for another picker (if
// non-failfast). // non-failfast).
return nil, nil, balancer.ErrTransientFailure return balancer.PickResult{}, balancer.ErrTransientFailure
default: default:
// For other states (connecting or unknown), the v1 balancer would // For other states (connecting or unknown), the v1 balancer would
// traditionally wait until ready and then issue the RPC. Returning // traditionally wait until ready and then issue the RPC. Returning
// ErrNoSubConnAvailable will be a slight improvement in that it will // ErrNoSubConnAvailable will be a slight improvement in that it will
// allow the balancer to choose another address in case others are // allow the balancer to choose another address in case others are
// connected. // connected.
return nil, nil, balancer.ErrNoSubConnAvailable return balancer.PickResult{}, balancer.ErrNoSubConnAvailable
} }
} }

View File

@ -35,10 +35,10 @@ import (
"google.golang.org/grpc/codes" "google.golang.org/grpc/codes"
"google.golang.org/grpc/connectivity" "google.golang.org/grpc/connectivity"
"google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials"
"google.golang.org/grpc/grpclog"
"google.golang.org/grpc/internal/backoff" "google.golang.org/grpc/internal/backoff"
"google.golang.org/grpc/internal/channelz" "google.golang.org/grpc/internal/channelz"
"google.golang.org/grpc/internal/grpcsync" "google.golang.org/grpc/internal/grpcsync"
"google.golang.org/grpc/internal/grpcutil"
"google.golang.org/grpc/internal/transport" "google.golang.org/grpc/internal/transport"
"google.golang.org/grpc/keepalive" "google.golang.org/grpc/keepalive"
"google.golang.org/grpc/resolver" "google.golang.org/grpc/resolver"
@ -151,7 +151,7 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *
if channelz.IsOn() { if channelz.IsOn() {
if cc.dopts.channelzParentID != 0 { if cc.dopts.channelzParentID != 0 {
cc.channelzID = channelz.RegisterChannel(&channelzChannel{cc}, cc.dopts.channelzParentID, target) cc.channelzID = channelz.RegisterChannel(&channelzChannel{cc}, cc.dopts.channelzParentID, target)
channelz.AddTraceEvent(cc.channelzID, &channelz.TraceEventDesc{ channelz.AddTraceEvent(cc.channelzID, 0, &channelz.TraceEventDesc{
Desc: "Channel Created", Desc: "Channel Created",
Severity: channelz.CtINFO, Severity: channelz.CtINFO,
Parent: &channelz.TraceEventDesc{ Parent: &channelz.TraceEventDesc{
@ -161,10 +161,7 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *
}) })
} else { } else {
cc.channelzID = channelz.RegisterChannel(&channelzChannel{cc}, 0, target) cc.channelzID = channelz.RegisterChannel(&channelzChannel{cc}, 0, target)
channelz.AddTraceEvent(cc.channelzID, &channelz.TraceEventDesc{ channelz.Info(cc.channelzID, "Channel Created")
Desc: "Channel Created",
Severity: channelz.CtINFO,
})
} }
cc.csMgr.channelzID = cc.channelzID cc.csMgr.channelzID = cc.channelzID
} }
@ -197,12 +194,13 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *
cc.mkp = cc.dopts.copts.KeepaliveParams cc.mkp = cc.dopts.copts.KeepaliveParams
if cc.dopts.copts.Dialer == nil { if cc.dopts.copts.Dialer == nil {
cc.dopts.copts.Dialer = newProxyDialer( cc.dopts.copts.Dialer = func(ctx context.Context, addr string) (net.Conn, error) {
func(ctx context.Context, addr string) (net.Conn, error) { network, addr := parseDialTarget(addr)
network, addr := parseDialTarget(addr) return (&net.Dialer{}).DialContext(ctx, network, addr)
return (&net.Dialer{}).DialContext(ctx, network, addr) }
}, if cc.dopts.withProxy {
) cc.dopts.copts.Dialer = newProxyDialer(cc.dopts.copts.Dialer)
}
} }
if cc.dopts.copts.UserAgent != "" { if cc.dopts.copts.UserAgent != "" {
@ -239,25 +237,26 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *
if cc.dopts.bs == nil { if cc.dopts.bs == nil {
cc.dopts.bs = backoff.DefaultExponential cc.dopts.bs = backoff.DefaultExponential
} }
if cc.dopts.resolverBuilder == nil {
// Only try to parse target when resolver builder is not already set. // Determine the resolver to use.
cc.parsedTarget = parseTarget(cc.target) cc.parsedTarget = grpcutil.ParseTarget(cc.target)
grpclog.Infof("parsed scheme: %q", cc.parsedTarget.Scheme) channelz.Infof(cc.channelzID, "parsed scheme: %q", cc.parsedTarget.Scheme)
cc.dopts.resolverBuilder = resolver.Get(cc.parsedTarget.Scheme) resolverBuilder := cc.getResolver(cc.parsedTarget.Scheme)
if cc.dopts.resolverBuilder == nil { if resolverBuilder == nil {
// If resolver builder is still nil, the parsed target's scheme is // If resolver builder is still nil, the parsed target's scheme is
// not registered. Fallback to default resolver and set Endpoint to // not registered. Fallback to default resolver and set Endpoint to
// the original target. // the original target.
grpclog.Infof("scheme %q not registered, fallback to default scheme", cc.parsedTarget.Scheme) channelz.Infof(cc.channelzID, "scheme %q not registered, fallback to default scheme", cc.parsedTarget.Scheme)
cc.parsedTarget = resolver.Target{ cc.parsedTarget = resolver.Target{
Scheme: resolver.GetDefaultScheme(), Scheme: resolver.GetDefaultScheme(),
Endpoint: target, Endpoint: target,
} }
cc.dopts.resolverBuilder = resolver.Get(cc.parsedTarget.Scheme) resolverBuilder = cc.getResolver(cc.parsedTarget.Scheme)
if resolverBuilder == nil {
return nil, fmt.Errorf("could not get resolver for default scheme: %q", cc.parsedTarget.Scheme)
} }
} else {
cc.parsedTarget = resolver.Target{Endpoint: target}
} }
creds := cc.dopts.copts.TransportCredentials creds := cc.dopts.copts.TransportCredentials
if creds != nil && creds.Info().ServerName != "" { if creds != nil && creds.Info().ServerName != "" {
cc.authority = creds.Info().ServerName cc.authority = creds.Info().ServerName
@ -297,14 +296,14 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *
} }
// Build the resolver. // Build the resolver.
rWrapper, err := newCCResolverWrapper(cc) rWrapper, err := newCCResolverWrapper(cc, resolverBuilder)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to build resolver: %v", err) return nil, fmt.Errorf("failed to build resolver: %v", err)
} }
cc.mu.Lock() cc.mu.Lock()
cc.resolverWrapper = rWrapper cc.resolverWrapper = rWrapper
cc.mu.Unlock() cc.mu.Unlock()
// A blocking dial blocks until the clientConn is ready. // A blocking dial blocks until the clientConn is ready.
if cc.dopts.block { if cc.dopts.block {
for { for {
@ -415,12 +414,7 @@ func (csm *connectivityStateManager) updateState(state connectivity.State) {
return return
} }
csm.state = state csm.state = state
if channelz.IsOn() { channelz.Infof(csm.channelzID, "Channel Connectivity change to %v", state)
channelz.AddTraceEvent(csm.channelzID, &channelz.TraceEventDesc{
Desc: fmt.Sprintf("Channel Connectivity change to %v", state),
Severity: channelz.CtINFO,
})
}
if csm.notifyChan != nil { if csm.notifyChan != nil {
// There are other goroutines waiting on this channel. // There are other goroutines waiting on this channel.
close(csm.notifyChan) close(csm.notifyChan)
@ -443,6 +437,20 @@ func (csm *connectivityStateManager) getNotifyChan() <-chan struct{} {
return csm.notifyChan return csm.notifyChan
} }
// ClientConnInterface defines the functions clients need to perform unary and
// streaming RPCs. It is implemented by *ClientConn, and is only intended to
// be referenced by generated code.
type ClientConnInterface interface {
// Invoke performs a unary RPC and returns after the response is received
// into reply.
Invoke(ctx context.Context, method string, args interface{}, reply interface{}, opts ...CallOption) error
// NewStream begins a streaming RPC.
NewStream(ctx context.Context, desc *StreamDesc, method string, opts ...CallOption) (ClientStream, error)
}
// Assert *ClientConn implements ClientConnInterface.
var _ ClientConnInterface = (*ClientConn)(nil)
// ClientConn represents a virtual connection to a conceptual endpoint, to // ClientConn represents a virtual connection to a conceptual endpoint, to
// perform RPCs. // perform RPCs.
// //
@ -656,9 +664,9 @@ func (cc *ClientConn) switchBalancer(name string) {
return return
} }
grpclog.Infof("ClientConn switching balancer to %q", name) channelz.Infof(cc.channelzID, "ClientConn switching balancer to %q", name)
if cc.dopts.balancerBuilder != nil { if cc.dopts.balancerBuilder != nil {
grpclog.Infoln("ignoring balancer switching: Balancer DialOption used instead") channelz.Info(cc.channelzID, "ignoring balancer switching: Balancer DialOption used instead")
return return
} }
if cc.balancerWrapper != nil { if cc.balancerWrapper != nil {
@ -666,29 +674,19 @@ func (cc *ClientConn) switchBalancer(name string) {
} }
builder := balancer.Get(name) builder := balancer.Get(name)
if channelz.IsOn() {
if builder == nil {
channelz.AddTraceEvent(cc.channelzID, &channelz.TraceEventDesc{
Desc: fmt.Sprintf("Channel switches to new LB policy %q due to fallback from invalid balancer name", PickFirstBalancerName),
Severity: channelz.CtWarning,
})
} else {
channelz.AddTraceEvent(cc.channelzID, &channelz.TraceEventDesc{
Desc: fmt.Sprintf("Channel switches to new LB policy %q", name),
Severity: channelz.CtINFO,
})
}
}
if builder == nil { if builder == nil {
grpclog.Infof("failed to get balancer builder for: %v, using pick_first instead", name) channelz.Warningf(cc.channelzID, "Channel switches to new LB policy %q due to fallback from invalid balancer name", PickFirstBalancerName)
channelz.Infof(cc.channelzID, "failed to get balancer builder for: %v, using pick_first instead", name)
builder = newPickfirstBuilder() builder = newPickfirstBuilder()
} else {
channelz.Infof(cc.channelzID, "Channel switches to new LB policy %q", name)
} }
cc.curBalancerName = builder.Name() cc.curBalancerName = builder.Name()
cc.balancerWrapper = newCCBalancerWrapper(cc, builder, cc.balancerBuildOpts) cc.balancerWrapper = newCCBalancerWrapper(cc, builder, cc.balancerBuildOpts)
} }
func (cc *ClientConn) handleSubConnStateChange(sc balancer.SubConn, s connectivity.State) { func (cc *ClientConn) handleSubConnStateChange(sc balancer.SubConn, s connectivity.State, err error) {
cc.mu.Lock() cc.mu.Lock()
if cc.conns == nil { if cc.conns == nil {
cc.mu.Unlock() cc.mu.Unlock()
@ -696,7 +694,7 @@ func (cc *ClientConn) handleSubConnStateChange(sc balancer.SubConn, s connectivi
} }
// TODO(bar switching) send updates to all balancer wrappers when balancer // TODO(bar switching) send updates to all balancer wrappers when balancer
// gracefully switching is supported. // gracefully switching is supported.
cc.balancerWrapper.handleSubConnStateChange(sc, s) cc.balancerWrapper.handleSubConnStateChange(sc, s, err)
cc.mu.Unlock() cc.mu.Unlock()
} }
@ -705,6 +703,7 @@ func (cc *ClientConn) handleSubConnStateChange(sc balancer.SubConn, s connectivi
// Caller needs to make sure len(addrs) > 0. // Caller needs to make sure len(addrs) > 0.
func (cc *ClientConn) newAddrConn(addrs []resolver.Address, opts balancer.NewSubConnOptions) (*addrConn, error) { func (cc *ClientConn) newAddrConn(addrs []resolver.Address, opts balancer.NewSubConnOptions) (*addrConn, error) {
ac := &addrConn{ ac := &addrConn{
state: connectivity.Idle,
cc: cc, cc: cc,
addrs: addrs, addrs: addrs,
scopts: opts, scopts: opts,
@ -721,7 +720,7 @@ func (cc *ClientConn) newAddrConn(addrs []resolver.Address, opts balancer.NewSub
} }
if channelz.IsOn() { if channelz.IsOn() {
ac.channelzID = channelz.RegisterSubChannel(ac, cc.channelzID, "") ac.channelzID = channelz.RegisterSubChannel(ac, cc.channelzID, "")
channelz.AddTraceEvent(ac.channelzID, &channelz.TraceEventDesc{ channelz.AddTraceEvent(ac.channelzID, 0, &channelz.TraceEventDesc{
Desc: "Subchannel Created", Desc: "Subchannel Created",
Severity: channelz.CtINFO, Severity: channelz.CtINFO,
Parent: &channelz.TraceEventDesc{ Parent: &channelz.TraceEventDesc{
@ -793,7 +792,7 @@ func (ac *addrConn) connect() error {
} }
// Update connectivity state within the lock to prevent subsequent or // Update connectivity state within the lock to prevent subsequent or
// concurrent calls from resetting the transport more than once. // concurrent calls from resetting the transport more than once.
ac.updateConnectivityState(connectivity.Connecting) ac.updateConnectivityState(connectivity.Connecting, nil)
ac.mu.Unlock() ac.mu.Unlock()
// Start a goroutine connecting to the server asynchronously. // Start a goroutine connecting to the server asynchronously.
@ -819,7 +818,7 @@ func (ac *addrConn) connect() error {
func (ac *addrConn) tryUpdateAddrs(addrs []resolver.Address) bool { func (ac *addrConn) tryUpdateAddrs(addrs []resolver.Address) bool {
ac.mu.Lock() ac.mu.Lock()
defer ac.mu.Unlock() defer ac.mu.Unlock()
grpclog.Infof("addrConn: tryUpdateAddrs curAddr: %v, addrs: %v", ac.curAddr, addrs) channelz.Infof(ac.channelzID, "addrConn: tryUpdateAddrs curAddr: %v, addrs: %v", ac.curAddr, addrs)
if ac.state == connectivity.Shutdown || if ac.state == connectivity.Shutdown ||
ac.state == connectivity.TransientFailure || ac.state == connectivity.TransientFailure ||
ac.state == connectivity.Idle { ac.state == connectivity.Idle {
@ -839,7 +838,7 @@ func (ac *addrConn) tryUpdateAddrs(addrs []resolver.Address) bool {
break break
} }
} }
grpclog.Infof("addrConn: tryUpdateAddrs curAddrFound: %v", curAddrFound) channelz.Infof(ac.channelzID, "addrConn: tryUpdateAddrs curAddrFound: %v", curAddrFound)
if curAddrFound { if curAddrFound {
ac.addrs = addrs ac.addrs = addrs
} }
@ -879,7 +878,8 @@ func (cc *ClientConn) healthCheckConfig() *healthCheckConfig {
} }
func (cc *ClientConn) getTransport(ctx context.Context, failfast bool, method string) (transport.ClientTransport, func(balancer.DoneInfo), error) { func (cc *ClientConn) getTransport(ctx context.Context, failfast bool, method string) (transport.ClientTransport, func(balancer.DoneInfo), error) {
t, done, err := cc.blockingpicker.pick(ctx, failfast, balancer.PickOptions{ t, done, err := cc.blockingpicker.pick(ctx, failfast, balancer.PickInfo{
Ctx: ctx,
FullMethodName: method, FullMethodName: method,
}) })
if err != nil { if err != nil {
@ -938,7 +938,7 @@ func (cc *ClientConn) applyServiceConfigAndBalancer(sc *ServiceConfig, addrs []r
} }
} }
func (cc *ClientConn) resolveNow(o resolver.ResolveNowOption) { func (cc *ClientConn) resolveNow(o resolver.ResolveNowOptions) {
cc.mu.RLock() cc.mu.RLock()
r := cc.resolverWrapper r := cc.resolverWrapper
cc.mu.RUnlock() cc.mu.RUnlock()
@ -1009,7 +1009,7 @@ func (cc *ClientConn) Close() error {
Severity: channelz.CtINFO, Severity: channelz.CtINFO,
} }
} }
channelz.AddTraceEvent(cc.channelzID, ted) channelz.AddTraceEvent(cc.channelzID, 0, ted)
// TraceEvent needs to be called before RemoveEntry, as TraceEvent may add trace reference to // TraceEvent needs to be called before RemoveEntry, as TraceEvent may add trace reference to
// the entity being deleted, and thus prevent it from being deleted right away. // the entity being deleted, and thus prevent it from being deleted right away.
channelz.RemoveEntry(cc.channelzID) channelz.RemoveEntry(cc.channelzID)
@ -1048,20 +1048,13 @@ type addrConn struct {
} }
// Note: this requires a lock on ac.mu. // Note: this requires a lock on ac.mu.
func (ac *addrConn) updateConnectivityState(s connectivity.State) { func (ac *addrConn) updateConnectivityState(s connectivity.State, lastErr error) {
if ac.state == s { if ac.state == s {
return return
} }
updateMsg := fmt.Sprintf("Subchannel Connectivity change to %v", s)
ac.state = s ac.state = s
if channelz.IsOn() { channelz.Infof(ac.channelzID, "Subchannel Connectivity change to %v", s)
channelz.AddTraceEvent(ac.channelzID, &channelz.TraceEventDesc{ ac.cc.handleSubConnStateChange(ac.acbw, s, lastErr)
Desc: updateMsg,
Severity: channelz.CtINFO,
})
}
ac.cc.handleSubConnStateChange(ac.acbw, s)
} }
// adjustParams updates parameters used to create transports upon // adjustParams updates parameters used to create transports upon
@ -1081,7 +1074,7 @@ func (ac *addrConn) adjustParams(r transport.GoAwayReason) {
func (ac *addrConn) resetTransport() { func (ac *addrConn) resetTransport() {
for i := 0; ; i++ { for i := 0; ; i++ {
if i > 0 { if i > 0 {
ac.cc.resolveNow(resolver.ResolveNowOption{}) ac.cc.resolveNow(resolver.ResolveNowOptions{})
} }
ac.mu.Lock() ac.mu.Lock()
@ -1110,7 +1103,7 @@ func (ac *addrConn) resetTransport() {
// https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md#proposed-backoff-algorithm // https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md#proposed-backoff-algorithm
connectDeadline := time.Now().Add(dialDuration) connectDeadline := time.Now().Add(dialDuration)
ac.updateConnectivityState(connectivity.Connecting) ac.updateConnectivityState(connectivity.Connecting, nil)
ac.transport = nil ac.transport = nil
ac.mu.Unlock() ac.mu.Unlock()
@ -1123,7 +1116,7 @@ func (ac *addrConn) resetTransport() {
ac.mu.Unlock() ac.mu.Unlock()
return return
} }
ac.updateConnectivityState(connectivity.TransientFailure) ac.updateConnectivityState(connectivity.TransientFailure, err)
// Backoff. // Backoff.
b := ac.resetBackoff b := ac.resetBackoff
@ -1179,6 +1172,7 @@ func (ac *addrConn) resetTransport() {
// first successful one. It returns the transport, the address and a Event in // first successful one. It returns the transport, the address and a Event in
// the successful case. The Event fires when the returned transport disconnects. // the successful case. The Event fires when the returned transport disconnects.
func (ac *addrConn) tryAllAddrs(addrs []resolver.Address, connectDeadline time.Time) (transport.ClientTransport, resolver.Address, *grpcsync.Event, error) { func (ac *addrConn) tryAllAddrs(addrs []resolver.Address, connectDeadline time.Time) (transport.ClientTransport, resolver.Address, *grpcsync.Event, error) {
var firstConnErr error
for _, addr := range addrs { for _, addr := range addrs {
ac.mu.Lock() ac.mu.Lock()
if ac.state == connectivity.Shutdown { if ac.state == connectivity.Shutdown {
@ -1196,22 +1190,20 @@ func (ac *addrConn) tryAllAddrs(addrs []resolver.Address, connectDeadline time.T
} }
ac.mu.Unlock() ac.mu.Unlock()
if channelz.IsOn() { channelz.Infof(ac.channelzID, "Subchannel picks a new address %q to connect", addr.Addr)
channelz.AddTraceEvent(ac.channelzID, &channelz.TraceEventDesc{
Desc: fmt.Sprintf("Subchannel picks a new address %q to connect", addr.Addr),
Severity: channelz.CtINFO,
})
}
newTr, reconnect, err := ac.createTransport(addr, copts, connectDeadline) newTr, reconnect, err := ac.createTransport(addr, copts, connectDeadline)
if err == nil { if err == nil {
return newTr, addr, reconnect, nil return newTr, addr, reconnect, nil
} }
if firstConnErr == nil {
firstConnErr = err
}
ac.cc.blockingpicker.updateConnectionError(err) ac.cc.blockingpicker.updateConnectionError(err)
} }
// Couldn't connect to any address. // Couldn't connect to any address.
return nil, resolver.Address{}, nil, fmt.Errorf("couldn't connect to any address") return nil, resolver.Address{}, nil, firstConnErr
} }
// createTransport creates a connection to addr. It returns the transport and a // createTransport creates a connection to addr. It returns the transport and a
@ -1244,7 +1236,7 @@ func (ac *addrConn) createTransport(addr resolver.Address, copts transport.Conne
// state to Connecting. // state to Connecting.
// //
// TODO: this should be Idle when grpc-go properly supports it. // TODO: this should be Idle when grpc-go properly supports it.
ac.updateConnectivityState(connectivity.Connecting) ac.updateConnectivityState(connectivity.Connecting, nil)
} }
}) })
ac.mu.Unlock() ac.mu.Unlock()
@ -1259,7 +1251,7 @@ func (ac *addrConn) createTransport(addr resolver.Address, copts transport.Conne
// state to Connecting. // state to Connecting.
// //
// TODO: this should be Idle when grpc-go properly supports it. // TODO: this should be Idle when grpc-go properly supports it.
ac.updateConnectivityState(connectivity.Connecting) ac.updateConnectivityState(connectivity.Connecting, nil)
} }
}) })
ac.mu.Unlock() ac.mu.Unlock()
@ -1280,15 +1272,15 @@ func (ac *addrConn) createTransport(addr resolver.Address, copts transport.Conne
newTr, err := transport.NewClientTransport(connectCtx, ac.cc.ctx, target, copts, onPrefaceReceipt, onGoAway, onClose) newTr, err := transport.NewClientTransport(connectCtx, ac.cc.ctx, target, copts, onPrefaceReceipt, onGoAway, onClose)
if err != nil { if err != nil {
// newTr is either nil, or closed. // newTr is either nil, or closed.
grpclog.Warningf("grpc: addrConn.createTransport failed to connect to %v. Err :%v. Reconnecting...", addr, err) channelz.Warningf(ac.channelzID, "grpc: addrConn.createTransport failed to connect to %v. Err: %v. Reconnecting...", addr, err)
return nil, nil, err return nil, nil, err
} }
select { select {
case <-time.After(connectDeadline.Sub(time.Now())): case <-time.After(time.Until(connectDeadline)):
// We didn't get the preface in time. // We didn't get the preface in time.
newTr.Close() newTr.Close()
grpclog.Warningf("grpc: addrConn.createTransport failed to connect to %v: didn't receive server preface in time. Reconnecting...", addr) channelz.Warningf(ac.channelzID, "grpc: addrConn.createTransport failed to connect to %v: didn't receive server preface in time. Reconnecting...", addr)
return nil, nil, errors.New("timed out waiting for server handshake") return nil, nil, errors.New("timed out waiting for server handshake")
case <-prefaceReceived: case <-prefaceReceived:
// We got the preface - huzzah! things are good. // We got the preface - huzzah! things are good.
@ -1316,7 +1308,7 @@ func (ac *addrConn) startHealthCheck(ctx context.Context) {
var healthcheckManagingState bool var healthcheckManagingState bool
defer func() { defer func() {
if !healthcheckManagingState { if !healthcheckManagingState {
ac.updateConnectivityState(connectivity.Ready) ac.updateConnectivityState(connectivity.Ready, nil)
} }
}() }()
@ -1335,7 +1327,7 @@ func (ac *addrConn) startHealthCheck(ctx context.Context) {
// The health package is not imported to set health check function. // The health package is not imported to set health check function.
// //
// TODO: add a link to the health check doc in the error message. // TODO: add a link to the health check doc in the error message.
grpclog.Error("Health check is requested but health check function is not set.") channelz.Error(ac.channelzID, "Health check is requested but health check function is not set.")
return return
} }
@ -1352,28 +1344,22 @@ func (ac *addrConn) startHealthCheck(ctx context.Context) {
ac.mu.Unlock() ac.mu.Unlock()
return newNonRetryClientStream(ctx, &StreamDesc{ServerStreams: true}, method, currentTr, ac) return newNonRetryClientStream(ctx, &StreamDesc{ServerStreams: true}, method, currentTr, ac)
} }
setConnectivityState := func(s connectivity.State) { setConnectivityState := func(s connectivity.State, lastErr error) {
ac.mu.Lock() ac.mu.Lock()
defer ac.mu.Unlock() defer ac.mu.Unlock()
if ac.transport != currentTr { if ac.transport != currentTr {
return return
} }
ac.updateConnectivityState(s) ac.updateConnectivityState(s, lastErr)
} }
// Start the health checking stream. // Start the health checking stream.
go func() { go func() {
err := ac.cc.dopts.healthCheckFunc(ctx, newStream, setConnectivityState, healthCheckConfig.ServiceName) err := ac.cc.dopts.healthCheckFunc(ctx, newStream, setConnectivityState, healthCheckConfig.ServiceName)
if err != nil { if err != nil {
if status.Code(err) == codes.Unimplemented { if status.Code(err) == codes.Unimplemented {
if channelz.IsOn() { channelz.Error(ac.channelzID, "Subchannel health check is unimplemented at server side, thus health check is disabled")
channelz.AddTraceEvent(ac.channelzID, &channelz.TraceEventDesc{
Desc: "Subchannel health check is unimplemented at server side, thus health check is disabled",
Severity: channelz.CtError,
})
}
grpclog.Error("Subchannel health check is unimplemented at server side, thus health check is disabled")
} else { } else {
grpclog.Errorf("HealthCheckFunc exits with unexpected error %v", err) channelz.Errorf(ac.channelzID, "HealthCheckFunc exits with unexpected error %v", err)
} }
} }
}() }()
@ -1424,7 +1410,7 @@ func (ac *addrConn) tearDown(err error) {
ac.transport = nil ac.transport = nil
// We have to set the state to Shutdown before anything else to prevent races // We have to set the state to Shutdown before anything else to prevent races
// between setting the state and logic that waits on context cancellation / etc. // between setting the state and logic that waits on context cancellation / etc.
ac.updateConnectivityState(connectivity.Shutdown) ac.updateConnectivityState(connectivity.Shutdown, nil)
ac.cancel() ac.cancel()
ac.curAddr = resolver.Address{} ac.curAddr = resolver.Address{}
if err == errConnDrain && curTr != nil { if err == errConnDrain && curTr != nil {
@ -1438,7 +1424,7 @@ func (ac *addrConn) tearDown(err error) {
ac.mu.Lock() ac.mu.Lock()
} }
if channelz.IsOn() { if channelz.IsOn() {
channelz.AddTraceEvent(ac.channelzID, &channelz.TraceEventDesc{ channelz.AddTraceEvent(ac.channelzID, 0, &channelz.TraceEventDesc{
Desc: "Subchannel Deleted", Desc: "Subchannel Deleted",
Severity: channelz.CtINFO, Severity: channelz.CtINFO,
Parent: &channelz.TraceEventDesc{ Parent: &channelz.TraceEventDesc{
@ -1537,3 +1523,12 @@ func (c *channelzChannel) ChannelzMetric() *channelz.ChannelInternalMetric {
// Deprecated: This error is never returned by grpc and should not be // Deprecated: This error is never returned by grpc and should not be
// referenced by users. // referenced by users.
var ErrClientConnTimeout = errors.New("grpc: timed out when dialing") var ErrClientConnTimeout = errors.New("grpc: timed out when dialing")
func (cc *ClientConn) getResolver(scheme string) resolver.Builder {
for _, rb := range cc.dopts.resolvers {
if scheme == rb.Scheme() {
return rb
}
}
return resolver.Get(scheme)
}

View File

@ -24,17 +24,12 @@ package credentials // import "google.golang.org/grpc/credentials"
import ( import (
"context" "context"
"crypto/tls"
"crypto/x509"
"errors" "errors"
"fmt" "fmt"
"io/ioutil"
"net" "net"
"github.com/golang/protobuf/proto" "github.com/golang/protobuf/proto"
"google.golang.org/grpc/internal"
"google.golang.org/grpc/credentials/internal"
ginternal "google.golang.org/grpc/internal"
) )
// PerRPCCredentials defines the common interface for the credentials which need to // PerRPCCredentials defines the common interface for the credentials which need to
@ -56,6 +51,48 @@ type PerRPCCredentials interface {
RequireTransportSecurity() bool RequireTransportSecurity() bool
} }
// SecurityLevel defines the protection level on an established connection.
//
// This API is experimental.
type SecurityLevel int
const (
// NoSecurity indicates a connection is insecure.
// The zero SecurityLevel value is invalid for backward compatibility.
NoSecurity SecurityLevel = iota + 1
// IntegrityOnly indicates a connection only provides integrity protection.
IntegrityOnly
// PrivacyAndIntegrity indicates a connection provides both privacy and integrity protection.
PrivacyAndIntegrity
)
// String returns SecurityLevel in a string format.
func (s SecurityLevel) String() string {
switch s {
case NoSecurity:
return "NoSecurity"
case IntegrityOnly:
return "IntegrityOnly"
case PrivacyAndIntegrity:
return "PrivacyAndIntegrity"
}
return fmt.Sprintf("invalid SecurityLevel: %v", int(s))
}
// CommonAuthInfo contains authenticated information common to AuthInfo implementations.
// It should be embedded in a struct implementing AuthInfo to provide additional information
// about the credentials.
//
// This API is experimental.
type CommonAuthInfo struct {
SecurityLevel SecurityLevel
}
// GetCommonAuthInfo returns the pointer to CommonAuthInfo struct.
func (c *CommonAuthInfo) GetCommonAuthInfo() *CommonAuthInfo {
return c
}
// ProtocolInfo provides information regarding the gRPC wire protocol version, // ProtocolInfo provides information regarding the gRPC wire protocol version,
// security protocol, security protocol version in use, server name, etc. // security protocol, security protocol version in use, server name, etc.
type ProtocolInfo struct { type ProtocolInfo struct {
@ -63,13 +100,19 @@ type ProtocolInfo struct {
ProtocolVersion string ProtocolVersion string
// SecurityProtocol is the security protocol in use. // SecurityProtocol is the security protocol in use.
SecurityProtocol string SecurityProtocol string
// SecurityVersion is the security protocol version. // SecurityVersion is the security protocol version. It is a static version string from the
// credentials, not a value that reflects per-connection protocol negotiation. To retrieve
// details about the credentials used for a connection, use the Peer's AuthInfo field instead.
//
// Deprecated: please use Peer.AuthInfo.
SecurityVersion string SecurityVersion string
// ServerName is the user-configured server name. // ServerName is the user-configured server name.
ServerName string ServerName string
} }
// AuthInfo defines the common interface for the auth information the users are interested in. // AuthInfo defines the common interface for the auth information the users are interested in.
// A struct that implements AuthInfo should embed CommonAuthInfo by including additional
// information about the credentials in it.
type AuthInfo interface { type AuthInfo interface {
AuthType() string AuthType() string
} }
@ -84,7 +127,8 @@ type TransportCredentials interface {
// ClientHandshake does the authentication handshake specified by the corresponding // ClientHandshake does the authentication handshake specified by the corresponding
// authentication protocol on rawConn for clients. It returns the authenticated // authentication protocol on rawConn for clients. It returns the authenticated
// connection and the corresponding auth information about the connection. // connection and the corresponding auth information about the connection.
// Implementations must use the provided context to implement timely cancellation. // The auth information should embed CommonAuthInfo to return additional information about
// the credentials. Implementations must use the provided context to implement timely cancellation.
// gRPC will try to reconnect if the error returned is a temporary error // gRPC will try to reconnect if the error returned is a temporary error
// (io.EOF, context.DeadlineExceeded or err.Temporary() == true). // (io.EOF, context.DeadlineExceeded or err.Temporary() == true).
// If the returned error is a wrapper error, implementations should make sure that // If the returned error is a wrapper error, implementations should make sure that
@ -94,7 +138,8 @@ type TransportCredentials interface {
ClientHandshake(context.Context, string, net.Conn) (net.Conn, AuthInfo, error) ClientHandshake(context.Context, string, net.Conn) (net.Conn, AuthInfo, error)
// ServerHandshake does the authentication handshake for servers. It returns // ServerHandshake does the authentication handshake for servers. It returns
// the authenticated connection and the corresponding auth information about // the authenticated connection and the corresponding auth information about
// the connection. // the connection. The auth information should embed CommonAuthInfo to return additional information
// about the credentials.
// //
// If the returned net.Conn is closed, it MUST close the net.Conn provided. // If the returned net.Conn is closed, it MUST close the net.Conn provided.
ServerHandshake(net.Conn) (net.Conn, AuthInfo, error) ServerHandshake(net.Conn) (net.Conn, AuthInfo, error)
@ -127,223 +172,14 @@ type Bundle interface {
NewWithMode(mode string) (Bundle, error) NewWithMode(mode string) (Bundle, error)
} }
// TLSInfo contains the auth information for a TLS authenticated connection.
// It implements the AuthInfo interface.
type TLSInfo struct {
State tls.ConnectionState
}
// AuthType returns the type of TLSInfo as a string.
func (t TLSInfo) AuthType() string {
return "tls"
}
// GetSecurityValue returns security info requested by channelz.
func (t TLSInfo) GetSecurityValue() ChannelzSecurityValue {
v := &TLSChannelzSecurityValue{
StandardName: cipherSuiteLookup[t.State.CipherSuite],
}
// Currently there's no way to get LocalCertificate info from tls package.
if len(t.State.PeerCertificates) > 0 {
v.RemoteCertificate = t.State.PeerCertificates[0].Raw
}
return v
}
// tlsCreds is the credentials required for authenticating a connection using TLS.
type tlsCreds struct {
// TLS configuration
config *tls.Config
}
func (c tlsCreds) Info() ProtocolInfo {
return ProtocolInfo{
SecurityProtocol: "tls",
SecurityVersion: "1.2",
ServerName: c.config.ServerName,
}
}
func (c *tlsCreds) ClientHandshake(ctx context.Context, authority string, rawConn net.Conn) (_ net.Conn, _ AuthInfo, err error) {
// use local cfg to avoid clobbering ServerName if using multiple endpoints
cfg := cloneTLSConfig(c.config)
if cfg.ServerName == "" {
serverName, _, err := net.SplitHostPort(authority)
if err != nil {
// If the authority had no host port or if the authority cannot be parsed, use it as-is.
serverName = authority
}
cfg.ServerName = serverName
}
conn := tls.Client(rawConn, cfg)
errChannel := make(chan error, 1)
go func() {
errChannel <- conn.Handshake()
}()
select {
case err := <-errChannel:
if err != nil {
return nil, nil, err
}
case <-ctx.Done():
return nil, nil, ctx.Err()
}
return internal.WrapSyscallConn(rawConn, conn), TLSInfo{conn.ConnectionState()}, nil
}
func (c *tlsCreds) ServerHandshake(rawConn net.Conn) (net.Conn, AuthInfo, error) {
conn := tls.Server(rawConn, c.config)
if err := conn.Handshake(); err != nil {
return nil, nil, err
}
return internal.WrapSyscallConn(rawConn, conn), TLSInfo{conn.ConnectionState()}, nil
}
func (c *tlsCreds) Clone() TransportCredentials {
return NewTLS(c.config)
}
func (c *tlsCreds) OverrideServerName(serverNameOverride string) error {
c.config.ServerName = serverNameOverride
return nil
}
const alpnProtoStrH2 = "h2"
func appendH2ToNextProtos(ps []string) []string {
for _, p := range ps {
if p == alpnProtoStrH2 {
return ps
}
}
ret := make([]string, 0, len(ps)+1)
ret = append(ret, ps...)
return append(ret, alpnProtoStrH2)
}
// NewTLS uses c to construct a TransportCredentials based on TLS.
func NewTLS(c *tls.Config) TransportCredentials {
tc := &tlsCreds{cloneTLSConfig(c)}
tc.config.NextProtos = appendH2ToNextProtos(tc.config.NextProtos)
return tc
}
// NewClientTLSFromCert constructs TLS credentials from the input certificate for client.
// serverNameOverride is for testing only. If set to a non empty string,
// it will override the virtual host name of authority (e.g. :authority header field) in requests.
func NewClientTLSFromCert(cp *x509.CertPool, serverNameOverride string) TransportCredentials {
return NewTLS(&tls.Config{ServerName: serverNameOverride, RootCAs: cp})
}
// NewClientTLSFromFile constructs TLS credentials from the input certificate file for client.
// serverNameOverride is for testing only. If set to a non empty string,
// it will override the virtual host name of authority (e.g. :authority header field) in requests.
func NewClientTLSFromFile(certFile, serverNameOverride string) (TransportCredentials, error) {
b, err := ioutil.ReadFile(certFile)
if err != nil {
return nil, err
}
cp := x509.NewCertPool()
if !cp.AppendCertsFromPEM(b) {
return nil, fmt.Errorf("credentials: failed to append certificates")
}
return NewTLS(&tls.Config{ServerName: serverNameOverride, RootCAs: cp}), nil
}
// NewServerTLSFromCert constructs TLS credentials from the input certificate for server.
func NewServerTLSFromCert(cert *tls.Certificate) TransportCredentials {
return NewTLS(&tls.Config{Certificates: []tls.Certificate{*cert}})
}
// NewServerTLSFromFile constructs TLS credentials from the input certificate file and key
// file for server.
func NewServerTLSFromFile(certFile, keyFile string) (TransportCredentials, error) {
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
if err != nil {
return nil, err
}
return NewTLS(&tls.Config{Certificates: []tls.Certificate{cert}}), nil
}
// ChannelzSecurityInfo defines the interface that security protocols should implement
// in order to provide security info to channelz.
type ChannelzSecurityInfo interface {
GetSecurityValue() ChannelzSecurityValue
}
// ChannelzSecurityValue defines the interface that GetSecurityValue() return value
// should satisfy. This interface should only be satisfied by *TLSChannelzSecurityValue
// and *OtherChannelzSecurityValue.
type ChannelzSecurityValue interface {
isChannelzSecurityValue()
}
// TLSChannelzSecurityValue defines the struct that TLS protocol should return
// from GetSecurityValue(), containing security info like cipher and certificate used.
type TLSChannelzSecurityValue struct {
ChannelzSecurityValue
StandardName string
LocalCertificate []byte
RemoteCertificate []byte
}
// OtherChannelzSecurityValue defines the struct that non-TLS protocol should return
// from GetSecurityValue(), which contains protocol specific security info. Note
// the Value field will be sent to users of channelz requesting channel info, and
// thus sensitive info should better be avoided.
type OtherChannelzSecurityValue struct {
ChannelzSecurityValue
Name string
Value proto.Message
}
var cipherSuiteLookup = map[uint16]string{
tls.TLS_RSA_WITH_RC4_128_SHA: "TLS_RSA_WITH_RC4_128_SHA",
tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA: "TLS_RSA_WITH_3DES_EDE_CBC_SHA",
tls.TLS_RSA_WITH_AES_128_CBC_SHA: "TLS_RSA_WITH_AES_128_CBC_SHA",
tls.TLS_RSA_WITH_AES_256_CBC_SHA: "TLS_RSA_WITH_AES_256_CBC_SHA",
tls.TLS_RSA_WITH_AES_128_GCM_SHA256: "TLS_RSA_WITH_AES_128_GCM_SHA256",
tls.TLS_RSA_WITH_AES_256_GCM_SHA384: "TLS_RSA_WITH_AES_256_GCM_SHA384",
tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA: "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA: "TLS_ECDHE_RSA_WITH_RC4_128_SHA",
tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA: "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
tls.TLS_FALLBACK_SCSV: "TLS_FALLBACK_SCSV",
tls.TLS_RSA_WITH_AES_128_CBC_SHA256: "TLS_RSA_WITH_AES_128_CBC_SHA256",
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305: "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305",
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305: "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305",
}
// cloneTLSConfig returns a shallow clone of the exported
// fields of cfg, ignoring the unexported sync.Once, which
// contains a mutex and must not be copied.
//
// If cfg is nil, a new zero tls.Config is returned.
//
// TODO: inline this function if possible.
func cloneTLSConfig(cfg *tls.Config) *tls.Config {
if cfg == nil {
return &tls.Config{}
}
return cfg.Clone()
}
// RequestInfo contains request data attached to the context passed to GetRequestMetadata calls. // RequestInfo contains request data attached to the context passed to GetRequestMetadata calls.
// //
// This API is experimental. // This API is experimental.
type RequestInfo struct { type RequestInfo struct {
// The method passed to Invoke or NewStream for this RPC. (For proto methods, this has the format "/some.Service/Method") // The method passed to Invoke or NewStream for this RPC. (For proto methods, this has the format "/some.Service/Method")
Method string Method string
// AuthInfo contains the information from a security handshake (TransportCredentials.ClientHandshake, TransportCredentials.ServerHandshake)
AuthInfo AuthInfo
} }
// requestInfoKey is a struct to be used as the key when attaching a RequestInfo to a context object. // requestInfoKey is a struct to be used as the key when attaching a RequestInfo to a context object.
@ -357,8 +193,63 @@ func RequestInfoFromContext(ctx context.Context) (ri RequestInfo, ok bool) {
return return
} }
// CheckSecurityLevel checks if a connection's security level is greater than or equal to the specified one.
// It returns success if 1) the condition is satisified or 2) AuthInfo struct does not implement GetCommonAuthInfo() method
// or 3) CommonAuthInfo.SecurityLevel has an invalid zero value. For 2) and 3), it is for the purpose of backward-compatibility.
//
// This API is experimental.
func CheckSecurityLevel(ctx context.Context, level SecurityLevel) error {
type internalInfo interface {
GetCommonAuthInfo() *CommonAuthInfo
}
ri, _ := RequestInfoFromContext(ctx)
if ri.AuthInfo == nil {
return errors.New("unable to obtain SecurityLevel from context")
}
if ci, ok := ri.AuthInfo.(internalInfo); ok {
// CommonAuthInfo.SecurityLevel has an invalid value.
if ci.GetCommonAuthInfo().SecurityLevel == 0 {
return nil
}
if ci.GetCommonAuthInfo().SecurityLevel < level {
return fmt.Errorf("requires SecurityLevel %v; connection has %v", level, ci.GetCommonAuthInfo().SecurityLevel)
}
}
// The condition is satisfied or AuthInfo struct does not implement GetCommonAuthInfo() method.
return nil
}
func init() { func init() {
ginternal.NewRequestInfoContext = func(ctx context.Context, ri RequestInfo) context.Context { internal.NewRequestInfoContext = func(ctx context.Context, ri RequestInfo) context.Context {
return context.WithValue(ctx, requestInfoKey{}, ri) return context.WithValue(ctx, requestInfoKey{}, ri)
} }
} }
// ChannelzSecurityInfo defines the interface that security protocols should implement
// in order to provide security info to channelz.
//
// This API is experimental.
type ChannelzSecurityInfo interface {
GetSecurityValue() ChannelzSecurityValue
}
// ChannelzSecurityValue defines the interface that GetSecurityValue() return value
// should satisfy. This interface should only be satisfied by *TLSChannelzSecurityValue
// and *OtherChannelzSecurityValue.
//
// This API is experimental.
type ChannelzSecurityValue interface {
isChannelzSecurityValue()
}
// OtherChannelzSecurityValue defines the struct that non-TLS protocol should return
// from GetSecurityValue(), which contains protocol specific security info. Note
// the Value field will be sent to users of channelz requesting channel info, and
// thus sensitive info should better be avoided.
//
// This API is experimental.
type OtherChannelzSecurityValue struct {
ChannelzSecurityValue
Name string
Value proto.Message
}

235
vendor/google.golang.org/grpc/credentials/tls.go generated vendored Normal file
View File

@ -0,0 +1,235 @@
/*
*
* Copyright 2014 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 credentials
import (
"context"
"crypto/tls"
"crypto/x509"
"fmt"
"io/ioutil"
"net"
"google.golang.org/grpc/credentials/internal"
)
// TLSInfo contains the auth information for a TLS authenticated connection.
// It implements the AuthInfo interface.
type TLSInfo struct {
State tls.ConnectionState
CommonAuthInfo
}
// AuthType returns the type of TLSInfo as a string.
func (t TLSInfo) AuthType() string {
return "tls"
}
// GetSecurityValue returns security info requested by channelz.
func (t TLSInfo) GetSecurityValue() ChannelzSecurityValue {
v := &TLSChannelzSecurityValue{
StandardName: cipherSuiteLookup[t.State.CipherSuite],
}
// Currently there's no way to get LocalCertificate info from tls package.
if len(t.State.PeerCertificates) > 0 {
v.RemoteCertificate = t.State.PeerCertificates[0].Raw
}
return v
}
// tlsCreds is the credentials required for authenticating a connection using TLS.
type tlsCreds struct {
// TLS configuration
config *tls.Config
}
func (c tlsCreds) Info() ProtocolInfo {
return ProtocolInfo{
SecurityProtocol: "tls",
SecurityVersion: "1.2",
ServerName: c.config.ServerName,
}
}
func (c *tlsCreds) ClientHandshake(ctx context.Context, authority string, rawConn net.Conn) (_ net.Conn, _ AuthInfo, err error) {
// use local cfg to avoid clobbering ServerName if using multiple endpoints
cfg := cloneTLSConfig(c.config)
if cfg.ServerName == "" {
serverName, _, err := net.SplitHostPort(authority)
if err != nil {
// If the authority had no host port or if the authority cannot be parsed, use it as-is.
serverName = authority
}
cfg.ServerName = serverName
}
conn := tls.Client(rawConn, cfg)
errChannel := make(chan error, 1)
go func() {
errChannel <- conn.Handshake()
close(errChannel)
}()
select {
case err := <-errChannel:
if err != nil {
conn.Close()
return nil, nil, err
}
case <-ctx.Done():
conn.Close()
return nil, nil, ctx.Err()
}
return internal.WrapSyscallConn(rawConn, conn), TLSInfo{conn.ConnectionState(), CommonAuthInfo{PrivacyAndIntegrity}}, nil
}
func (c *tlsCreds) ServerHandshake(rawConn net.Conn) (net.Conn, AuthInfo, error) {
conn := tls.Server(rawConn, c.config)
if err := conn.Handshake(); err != nil {
conn.Close()
return nil, nil, err
}
return internal.WrapSyscallConn(rawConn, conn), TLSInfo{conn.ConnectionState(), CommonAuthInfo{PrivacyAndIntegrity}}, nil
}
func (c *tlsCreds) Clone() TransportCredentials {
return NewTLS(c.config)
}
func (c *tlsCreds) OverrideServerName(serverNameOverride string) error {
c.config.ServerName = serverNameOverride
return nil
}
const alpnProtoStrH2 = "h2"
func appendH2ToNextProtos(ps []string) []string {
for _, p := range ps {
if p == alpnProtoStrH2 {
return ps
}
}
ret := make([]string, 0, len(ps)+1)
ret = append(ret, ps...)
return append(ret, alpnProtoStrH2)
}
// NewTLS uses c to construct a TransportCredentials based on TLS.
func NewTLS(c *tls.Config) TransportCredentials {
tc := &tlsCreds{cloneTLSConfig(c)}
tc.config.NextProtos = appendH2ToNextProtos(tc.config.NextProtos)
return tc
}
// NewClientTLSFromCert constructs TLS credentials from the provided root
// certificate authority certificate(s) to validate server connections. If
// certificates to establish the identity of the client need to be included in
// the credentials (eg: for mTLS), use NewTLS instead, where a complete
// tls.Config can be specified.
// serverNameOverride is for testing only. If set to a non empty string,
// it will override the virtual host name of authority (e.g. :authority header
// field) in requests.
func NewClientTLSFromCert(cp *x509.CertPool, serverNameOverride string) TransportCredentials {
return NewTLS(&tls.Config{ServerName: serverNameOverride, RootCAs: cp})
}
// NewClientTLSFromFile constructs TLS credentials from the provided root
// certificate authority certificate file(s) to validate server connections. If
// certificates to establish the identity of the client need to be included in
// the credentials (eg: for mTLS), use NewTLS instead, where a complete
// tls.Config can be specified.
// serverNameOverride is for testing only. If set to a non empty string,
// it will override the virtual host name of authority (e.g. :authority header
// field) in requests.
func NewClientTLSFromFile(certFile, serverNameOverride string) (TransportCredentials, error) {
b, err := ioutil.ReadFile(certFile)
if err != nil {
return nil, err
}
cp := x509.NewCertPool()
if !cp.AppendCertsFromPEM(b) {
return nil, fmt.Errorf("credentials: failed to append certificates")
}
return NewTLS(&tls.Config{ServerName: serverNameOverride, RootCAs: cp}), nil
}
// NewServerTLSFromCert constructs TLS credentials from the input certificate for server.
func NewServerTLSFromCert(cert *tls.Certificate) TransportCredentials {
return NewTLS(&tls.Config{Certificates: []tls.Certificate{*cert}})
}
// NewServerTLSFromFile constructs TLS credentials from the input certificate file and key
// file for server.
func NewServerTLSFromFile(certFile, keyFile string) (TransportCredentials, error) {
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
if err != nil {
return nil, err
}
return NewTLS(&tls.Config{Certificates: []tls.Certificate{cert}}), nil
}
// TLSChannelzSecurityValue defines the struct that TLS protocol should return
// from GetSecurityValue(), containing security info like cipher and certificate used.
//
// This API is EXPERIMENTAL.
type TLSChannelzSecurityValue struct {
ChannelzSecurityValue
StandardName string
LocalCertificate []byte
RemoteCertificate []byte
}
var cipherSuiteLookup = map[uint16]string{
tls.TLS_RSA_WITH_RC4_128_SHA: "TLS_RSA_WITH_RC4_128_SHA",
tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA: "TLS_RSA_WITH_3DES_EDE_CBC_SHA",
tls.TLS_RSA_WITH_AES_128_CBC_SHA: "TLS_RSA_WITH_AES_128_CBC_SHA",
tls.TLS_RSA_WITH_AES_256_CBC_SHA: "TLS_RSA_WITH_AES_256_CBC_SHA",
tls.TLS_RSA_WITH_AES_128_GCM_SHA256: "TLS_RSA_WITH_AES_128_GCM_SHA256",
tls.TLS_RSA_WITH_AES_256_GCM_SHA384: "TLS_RSA_WITH_AES_256_GCM_SHA384",
tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA: "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA: "TLS_ECDHE_RSA_WITH_RC4_128_SHA",
tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA: "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
tls.TLS_FALLBACK_SCSV: "TLS_FALLBACK_SCSV",
tls.TLS_RSA_WITH_AES_128_CBC_SHA256: "TLS_RSA_WITH_AES_128_CBC_SHA256",
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305: "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305",
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305: "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305",
}
// cloneTLSConfig returns a shallow clone of the exported
// fields of cfg, ignoring the unexported sync.Once, which
// contains a mutex and must not be copied.
//
// If cfg is nil, a new zero tls.Config is returned.
//
// TODO: inline this function if possible.
func cloneTLSConfig(cfg *tls.Config) *tls.Config {
if cfg == nil {
return &tls.Config{}
}
return cfg.Clone()
}

View File

@ -58,9 +58,7 @@ type dialOptions struct {
callOptions []CallOption callOptions []CallOption
// This is used by v1 balancer dial option WithBalancer to support v1 // This is used by v1 balancer dial option WithBalancer to support v1
// balancer, and also by WithBalancerName dial option. // balancer, and also by WithBalancerName dial option.
balancerBuilder balancer.Builder balancerBuilder balancer.Builder
// This is to support grpclb.
resolverBuilder resolver.Builder
channelzParentID int64 channelzParentID int64
disableServiceConfig bool disableServiceConfig bool
disableRetry bool disableRetry bool
@ -73,6 +71,8 @@ type dialOptions struct {
// resolver.ResolveNow(). The user will have no need to configure this, but // resolver.ResolveNow(). The user will have no need to configure this, but
// we need to be able to configure this in tests. // we need to be able to configure this in tests.
resolveNowBackoff func(int) time.Duration resolveNowBackoff func(int) time.Duration
resolvers []resolver.Builder
withProxy bool
} }
// DialOption configures how we set up the connection. // DialOption configures how we set up the connection.
@ -231,13 +231,6 @@ func WithBalancerName(balancerName string) DialOption {
}) })
} }
// withResolverBuilder is only for grpclb.
func withResolverBuilder(b resolver.Builder) DialOption {
return newFuncDialOption(func(o *dialOptions) {
o.resolverBuilder = b
})
}
// WithServiceConfig returns a DialOption which has a channel to read the // WithServiceConfig returns a DialOption which has a channel to read the
// service configuration. // service configuration.
// //
@ -315,6 +308,16 @@ func WithInsecure() DialOption {
}) })
} }
// WithNoProxy returns a DialOption which disables the use of proxies for this
// ClientConn. This is ignored if WithDialer or WithContextDialer are used.
//
// This API is EXPERIMENTAL.
func WithNoProxy() DialOption {
return newFuncDialOption(func(o *dialOptions) {
o.withProxy = false
})
}
// WithTransportCredentials returns a DialOption which configures a connection // WithTransportCredentials returns a DialOption which configures a connection
// level security credentials (e.g., TLS/SSL). This should not be used together // level security credentials (e.g., TLS/SSL). This should not be used together
// with WithCredentialsBundle. // with WithCredentialsBundle.
@ -346,8 +349,8 @@ func WithCredentialsBundle(b credentials.Bundle) DialOption {
// WithTimeout returns a DialOption that configures a timeout for dialing a // WithTimeout returns a DialOption that configures a timeout for dialing a
// ClientConn initially. This is valid if and only if WithBlock() is present. // ClientConn initially. This is valid if and only if WithBlock() is present.
// //
// Deprecated: use DialContext and context.WithTimeout instead. Will be // Deprecated: use DialContext instead of Dial and context.WithTimeout
// supported throughout 1.x. // instead. Will be supported throughout 1.x.
func WithTimeout(d time.Duration) DialOption { func WithTimeout(d time.Duration) DialOption {
return newFuncDialOption(func(o *dialOptions) { return newFuncDialOption(func(o *dialOptions) {
o.timeout = d o.timeout = d
@ -365,7 +368,6 @@ func WithContextDialer(f func(context.Context, string) (net.Conn, error)) DialOp
} }
func init() { func init() {
internal.WithResolverBuilder = withResolverBuilder
internal.WithHealthCheckFunc = withHealthCheckFunc internal.WithHealthCheckFunc = withHealthCheckFunc
} }
@ -479,6 +481,8 @@ func WithAuthority(a string) DialOption {
// WithChannelzParentID returns a DialOption that specifies the channelz ID of // WithChannelzParentID returns a DialOption that specifies the channelz ID of
// current ClientConn's parent. This function is used in nested channel creation // current ClientConn's parent. This function is used in nested channel creation
// (e.g. grpclb dial). // (e.g. grpclb dial).
//
// This API is EXPERIMENTAL.
func WithChannelzParentID(id int64) DialOption { func WithChannelzParentID(id int64) DialOption {
return newFuncDialOption(func(o *dialOptions) { return newFuncDialOption(func(o *dialOptions) {
o.channelzParentID = id o.channelzParentID = id
@ -564,6 +568,7 @@ func defaultDialOptions() dialOptions {
ReadBufferSize: defaultReadBufSize, ReadBufferSize: defaultReadBufSize,
}, },
resolveNowBackoff: internalbackoff.DefaultExponential.Backoff, resolveNowBackoff: internalbackoff.DefaultExponential.Backoff,
withProxy: true,
} }
} }
@ -587,3 +592,15 @@ func withResolveNowBackoff(f func(int) time.Duration) DialOption {
o.resolveNowBackoff = f o.resolveNowBackoff = f
}) })
} }
// WithResolvers allows a list of resolver implementations to be registered
// locally with the ClientConn without needing to be globally registered via
// resolver.Register. They will be matched against the scheme used for the
// current Dial only, and will take precedence over the global registry.
//
// This API is EXPERIMENTAL.
func WithResolvers(rs ...resolver.Builder) DialOption {
return newFuncDialOption(func(o *dialOptions) {
o.resolvers = append(o.resolvers, rs...)
})
}

View File

@ -3,11 +3,11 @@ module google.golang.org/grpc
go 1.11 go 1.11
require ( require (
github.com/envoyproxy/go-control-plane v0.9.0 github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f
github.com/envoyproxy/protoc-gen-validate v0.1.0 github.com/envoyproxy/go-control-plane v0.9.4
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b
github.com/golang/mock v1.1.1 github.com/golang/mock v1.1.1
github.com/golang/protobuf v1.3.2 github.com/golang/protobuf v1.3.3
github.com/google/go-cmp v0.2.0 github.com/google/go-cmp v0.2.0
golang.org/x/net v0.0.0-20190311183353-d8887717615a golang.org/x/net v0.0.0-20190311183353-d8887717615a
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be

13
vendor/google.golang.org/grpc/go.sum generated vendored
View File

@ -1,10 +1,15 @@
cloud.google.com/go v0.26.0 h1:e0WKqKTd5BnrG8aKH3J3h+QvEIQtSUcf2n5UZ5ZgLtQ= cloud.google.com/go v0.26.0 h1:e0WKqKTd5BnrG8aKH3J3h+QvEIQtSUcf2n5UZ5ZgLtQ=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/envoyproxy/go-control-plane v0.9.0 h1:67WMNTvGrl7V1dWdKCeTwxDr7nio9clKoTlLhwIPnT4= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f h1:WBZRG4aNOuI15bLRrCgN8fCq8E5Xuty6jGbmSNEvSsU=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4 h1:rEvIZUSZ3fx39WIi3JkQqQBitGwpELBIYWeBVh6wn+E=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A= github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
@ -14,6 +19,8 @@ github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfb
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
@ -21,6 +28,7 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3 h1:XQyxROzUlZH+WIQwySDgnISgOivlhjIEwaQaJEJrrN0=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -40,6 +48,7 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135 h1:5Beo0mZN8dRzgrMMkDp0jc8YXQKx9DiJ2k1dkvGsn5A=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
@ -49,5 +58,7 @@ google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2El
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc h1:/hemPrYIhOhy8zYrNj+069zDB68us2sMGsfkFJO0iZs=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View File

@ -26,64 +26,70 @@
// verbosity level can be set by GRPC_GO_LOG_VERBOSITY_LEVEL. // verbosity level can be set by GRPC_GO_LOG_VERBOSITY_LEVEL.
package grpclog // import "google.golang.org/grpc/grpclog" package grpclog // import "google.golang.org/grpc/grpclog"
import "os" import (
"os"
var logger = newLoggerV2() "google.golang.org/grpc/internal/grpclog"
)
func init() {
SetLoggerV2(newLoggerV2())
}
// V reports whether verbosity level l is at least the requested verbose level. // V reports whether verbosity level l is at least the requested verbose level.
func V(l int) bool { func V(l int) bool {
return logger.V(l) return grpclog.Logger.V(l)
} }
// Info logs to the INFO log. // Info logs to the INFO log.
func Info(args ...interface{}) { func Info(args ...interface{}) {
logger.Info(args...) grpclog.Logger.Info(args...)
} }
// Infof logs to the INFO log. Arguments are handled in the manner of fmt.Printf. // Infof logs to the INFO log. Arguments are handled in the manner of fmt.Printf.
func Infof(format string, args ...interface{}) { func Infof(format string, args ...interface{}) {
logger.Infof(format, args...) grpclog.Logger.Infof(format, args...)
} }
// Infoln logs to the INFO log. Arguments are handled in the manner of fmt.Println. // Infoln logs to the INFO log. Arguments are handled in the manner of fmt.Println.
func Infoln(args ...interface{}) { func Infoln(args ...interface{}) {
logger.Infoln(args...) grpclog.Logger.Infoln(args...)
} }
// Warning logs to the WARNING log. // Warning logs to the WARNING log.
func Warning(args ...interface{}) { func Warning(args ...interface{}) {
logger.Warning(args...) grpclog.Logger.Warning(args...)
} }
// Warningf logs to the WARNING log. Arguments are handled in the manner of fmt.Printf. // Warningf logs to the WARNING log. Arguments are handled in the manner of fmt.Printf.
func Warningf(format string, args ...interface{}) { func Warningf(format string, args ...interface{}) {
logger.Warningf(format, args...) grpclog.Logger.Warningf(format, args...)
} }
// Warningln logs to the WARNING log. Arguments are handled in the manner of fmt.Println. // Warningln logs to the WARNING log. Arguments are handled in the manner of fmt.Println.
func Warningln(args ...interface{}) { func Warningln(args ...interface{}) {
logger.Warningln(args...) grpclog.Logger.Warningln(args...)
} }
// Error logs to the ERROR log. // Error logs to the ERROR log.
func Error(args ...interface{}) { func Error(args ...interface{}) {
logger.Error(args...) grpclog.Logger.Error(args...)
} }
// Errorf logs to the ERROR log. Arguments are handled in the manner of fmt.Printf. // Errorf logs to the ERROR log. Arguments are handled in the manner of fmt.Printf.
func Errorf(format string, args ...interface{}) { func Errorf(format string, args ...interface{}) {
logger.Errorf(format, args...) grpclog.Logger.Errorf(format, args...)
} }
// Errorln logs to the ERROR log. Arguments are handled in the manner of fmt.Println. // Errorln logs to the ERROR log. Arguments are handled in the manner of fmt.Println.
func Errorln(args ...interface{}) { func Errorln(args ...interface{}) {
logger.Errorln(args...) grpclog.Logger.Errorln(args...)
} }
// Fatal logs to the FATAL log. Arguments are handled in the manner of fmt.Print. // Fatal logs to the FATAL log. Arguments are handled in the manner of fmt.Print.
// It calls os.Exit() with exit code 1. // It calls os.Exit() with exit code 1.
func Fatal(args ...interface{}) { func Fatal(args ...interface{}) {
logger.Fatal(args...) grpclog.Logger.Fatal(args...)
// Make sure fatal logs will exit. // Make sure fatal logs will exit.
os.Exit(1) os.Exit(1)
} }
@ -91,7 +97,7 @@ func Fatal(args ...interface{}) {
// Fatalf logs to the FATAL log. Arguments are handled in the manner of fmt.Printf. // Fatalf logs to the FATAL log. Arguments are handled in the manner of fmt.Printf.
// It calls os.Exit() with exit code 1. // It calls os.Exit() with exit code 1.
func Fatalf(format string, args ...interface{}) { func Fatalf(format string, args ...interface{}) {
logger.Fatalf(format, args...) grpclog.Logger.Fatalf(format, args...)
// Make sure fatal logs will exit. // Make sure fatal logs will exit.
os.Exit(1) os.Exit(1)
} }
@ -99,7 +105,7 @@ func Fatalf(format string, args ...interface{}) {
// Fatalln logs to the FATAL log. Arguments are handled in the manner of fmt.Println. // Fatalln logs to the FATAL log. Arguments are handled in the manner of fmt.Println.
// It calle os.Exit()) with exit code 1. // It calle os.Exit()) with exit code 1.
func Fatalln(args ...interface{}) { func Fatalln(args ...interface{}) {
logger.Fatalln(args...) grpclog.Logger.Fatalln(args...)
// Make sure fatal logs will exit. // Make sure fatal logs will exit.
os.Exit(1) os.Exit(1)
} }
@ -108,19 +114,19 @@ func Fatalln(args ...interface{}) {
// //
// Deprecated: use Info. // Deprecated: use Info.
func Print(args ...interface{}) { func Print(args ...interface{}) {
logger.Info(args...) grpclog.Logger.Info(args...)
} }
// Printf prints to the logger. Arguments are handled in the manner of fmt.Printf. // Printf prints to the logger. Arguments are handled in the manner of fmt.Printf.
// //
// Deprecated: use Infof. // Deprecated: use Infof.
func Printf(format string, args ...interface{}) { func Printf(format string, args ...interface{}) {
logger.Infof(format, args...) grpclog.Logger.Infof(format, args...)
} }
// Println prints to the logger. Arguments are handled in the manner of fmt.Println. // Println prints to the logger. Arguments are handled in the manner of fmt.Println.
// //
// Deprecated: use Infoln. // Deprecated: use Infoln.
func Println(args ...interface{}) { func Println(args ...interface{}) {
logger.Infoln(args...) grpclog.Logger.Infoln(args...)
} }

View File

@ -18,6 +18,8 @@
package grpclog package grpclog
import "google.golang.org/grpc/internal/grpclog"
// Logger mimics golang's standard Logger as an interface. // Logger mimics golang's standard Logger as an interface.
// //
// Deprecated: use LoggerV2. // Deprecated: use LoggerV2.
@ -35,7 +37,7 @@ type Logger interface {
// //
// Deprecated: use SetLoggerV2. // Deprecated: use SetLoggerV2.
func SetLogger(l Logger) { func SetLogger(l Logger) {
logger = &loggerWrapper{Logger: l} grpclog.Logger = &loggerWrapper{Logger: l}
} }
// loggerWrapper wraps Logger into a LoggerV2. // loggerWrapper wraps Logger into a LoggerV2.

View File

@ -24,6 +24,8 @@ import (
"log" "log"
"os" "os"
"strconv" "strconv"
"google.golang.org/grpc/internal/grpclog"
) )
// LoggerV2 does underlying logging work for grpclog. // LoggerV2 does underlying logging work for grpclog.
@ -65,7 +67,8 @@ type LoggerV2 interface {
// SetLoggerV2 sets logger that is used in grpc to a V2 logger. // SetLoggerV2 sets logger that is used in grpc to a V2 logger.
// Not mutex-protected, should be called before any gRPC functions. // Not mutex-protected, should be called before any gRPC functions.
func SetLoggerV2(l LoggerV2) { func SetLoggerV2(l LoggerV2) {
logger = l grpclog.Logger = l
grpclog.DepthLogger, _ = l.(grpclog.DepthLoggerV2)
} }
const ( const (
@ -193,3 +196,19 @@ func (g *loggerT) Fatalf(format string, args ...interface{}) {
func (g *loggerT) V(l int) bool { func (g *loggerT) V(l int) bool {
return l <= g.v return l <= g.v
} }
// DepthLoggerV2 logs at a specified call frame. If a LoggerV2 also implements
// DepthLoggerV2, the below functions will be called with the appropriate stack
// depth set for trivial functions the logger may ignore.
//
// This API is EXPERIMENTAL.
type DepthLoggerV2 interface {
// InfoDepth logs to INFO log at the specified depth. Arguments are handled in the manner of fmt.Print.
InfoDepth(depth int, args ...interface{})
// WarningDepth logs to WARNING log at the specified depth. Arguments are handled in the manner of fmt.Print.
WarningDepth(depth int, args ...interface{})
// ErrorDetph logs to ERROR log at the specified depth. Arguments are handled in the manner of fmt.Print.
ErrorDepth(depth int, args ...interface{})
// FatalDepth logs to FATAL log at the specified depth. Arguments are handled in the manner of fmt.Print.
FatalDepth(depth int, args ...interface{})
}

View File

@ -98,7 +98,7 @@ func (l *logger) setDefaultMethodLogger(ml *methodLoggerConfig) error {
// New methodLogger with same service overrides the old one. // New methodLogger with same service overrides the old one.
func (l *logger) setServiceMethodLogger(service string, ml *methodLoggerConfig) error { func (l *logger) setServiceMethodLogger(service string, ml *methodLoggerConfig) error {
if _, ok := l.services[service]; ok { if _, ok := l.services[service]; ok {
return fmt.Errorf("conflicting rules for service %v found", service) return fmt.Errorf("conflicting service rules for service %v found", service)
} }
if l.services == nil { if l.services == nil {
l.services = make(map[string]*methodLoggerConfig) l.services = make(map[string]*methodLoggerConfig)
@ -112,10 +112,10 @@ func (l *logger) setServiceMethodLogger(service string, ml *methodLoggerConfig)
// New methodLogger with same method overrides the old one. // New methodLogger with same method overrides the old one.
func (l *logger) setMethodMethodLogger(method string, ml *methodLoggerConfig) error { func (l *logger) setMethodMethodLogger(method string, ml *methodLoggerConfig) error {
if _, ok := l.blacklist[method]; ok { if _, ok := l.blacklist[method]; ok {
return fmt.Errorf("conflicting rules for method %v found", method) return fmt.Errorf("conflicting blacklist rules for method %v found", method)
} }
if _, ok := l.methods[method]; ok { if _, ok := l.methods[method]; ok {
return fmt.Errorf("conflicting rules for method %v found", method) return fmt.Errorf("conflicting method rules for method %v found", method)
} }
if l.methods == nil { if l.methods == nil {
l.methods = make(map[string]*methodLoggerConfig) l.methods = make(map[string]*methodLoggerConfig)
@ -127,10 +127,10 @@ func (l *logger) setMethodMethodLogger(method string, ml *methodLoggerConfig) er
// Set blacklist method for "-service/method". // Set blacklist method for "-service/method".
func (l *logger) setBlacklist(method string) error { func (l *logger) setBlacklist(method string) error {
if _, ok := l.blacklist[method]; ok { if _, ok := l.blacklist[method]; ok {
return fmt.Errorf("conflicting rules for method %v found", method) return fmt.Errorf("conflicting blacklist rules for method %v found", method)
} }
if _, ok := l.methods[method]; ok { if _, ok := l.methods[method]; ok {
return fmt.Errorf("conflicting rules for method %v found", method) return fmt.Errorf("conflicting method rules for method %v found", method)
} }
if l.blacklist == nil { if l.blacklist == nil {
l.blacklist = make(map[string]struct{}) l.blacklist = make(map[string]struct{})

View File

@ -26,6 +26,13 @@ import "sync"
// //
// All methods on this type are thread-safe and don't block on anything except // All methods on this type are thread-safe and don't block on anything except
// the underlying mutex used for synchronization. // the underlying mutex used for synchronization.
//
// Unbounded supports values of any type to be stored in it by using a channel
// of `interface{}`. This means that a call to Put() incurs an extra memory
// allocation, and also that users need a type assertion while reading. For
// performance critical code paths, using Unbounded is strongly discouraged and
// defining a new type specific implementation of this buffer is preferred. See
// internal/transport/transport.go for an example of this.
type Unbounded struct { type Unbounded struct {
c chan interface{} c chan interface{}
mu sync.Mutex mu sync.Mutex

View File

@ -30,7 +30,7 @@ import (
"sync/atomic" "sync/atomic"
"time" "time"
"google.golang.org/grpc/grpclog" "google.golang.org/grpc/internal/grpclog"
) )
const ( const (
@ -216,7 +216,7 @@ func RegisterChannel(c Channel, pid int64, ref string) int64 {
// by pid). It returns the unique channelz tracking id assigned to this subchannel. // by pid). It returns the unique channelz tracking id assigned to this subchannel.
func RegisterSubChannel(c Channel, pid int64, ref string) int64 { func RegisterSubChannel(c Channel, pid int64, ref string) int64 {
if pid == 0 { if pid == 0 {
grpclog.Error("a SubChannel's parent id cannot be 0") grpclog.ErrorDepth(0, "a SubChannel's parent id cannot be 0")
return 0 return 0
} }
id := idGen.genID() id := idGen.genID()
@ -253,7 +253,7 @@ func RegisterServer(s Server, ref string) int64 {
// this listen socket. // this listen socket.
func RegisterListenSocket(s Socket, pid int64, ref string) int64 { func RegisterListenSocket(s Socket, pid int64, ref string) int64 {
if pid == 0 { if pid == 0 {
grpclog.Error("a ListenSocket's parent id cannot be 0") grpclog.ErrorDepth(0, "a ListenSocket's parent id cannot be 0")
return 0 return 0
} }
id := idGen.genID() id := idGen.genID()
@ -268,7 +268,7 @@ func RegisterListenSocket(s Socket, pid int64, ref string) int64 {
// this normal socket. // this normal socket.
func RegisterNormalSocket(s Socket, pid int64, ref string) int64 { func RegisterNormalSocket(s Socket, pid int64, ref string) int64 {
if pid == 0 { if pid == 0 {
grpclog.Error("a NormalSocket's parent id cannot be 0") grpclog.ErrorDepth(0, "a NormalSocket's parent id cannot be 0")
return 0 return 0
} }
id := idGen.genID() id := idGen.genID()
@ -294,7 +294,19 @@ type TraceEventDesc struct {
} }
// AddTraceEvent adds trace related to the entity with specified id, using the provided TraceEventDesc. // AddTraceEvent adds trace related to the entity with specified id, using the provided TraceEventDesc.
func AddTraceEvent(id int64, desc *TraceEventDesc) { func AddTraceEvent(id int64, depth int, desc *TraceEventDesc) {
for d := desc; d != nil; d = d.Parent {
switch d.Severity {
case CtUNKNOWN:
grpclog.InfoDepth(depth+1, d.Desc)
case CtINFO:
grpclog.InfoDepth(depth+1, d.Desc)
case CtWarning:
grpclog.WarningDepth(depth+1, d.Desc)
case CtError:
grpclog.ErrorDepth(depth+1, d.Desc)
}
}
if getMaxTraceEntry() == 0 { if getMaxTraceEntry() == 0 {
return return
} }

View File

@ -0,0 +1,100 @@
/*
*
* Copyright 2020 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 channelz
import (
"fmt"
"google.golang.org/grpc/internal/grpclog"
)
// Info logs through grpclog.Info and adds a trace event if channelz is on.
func Info(id int64, args ...interface{}) {
if IsOn() {
AddTraceEvent(id, 1, &TraceEventDesc{
Desc: fmt.Sprint(args...),
Severity: CtINFO,
})
} else {
grpclog.InfoDepth(1, args...)
}
}
// Infof logs through grpclog.Infof and adds a trace event if channelz is on.
func Infof(id int64, format string, args ...interface{}) {
msg := fmt.Sprintf(format, args...)
if IsOn() {
AddTraceEvent(id, 1, &TraceEventDesc{
Desc: msg,
Severity: CtINFO,
})
} else {
grpclog.InfoDepth(1, msg)
}
}
// Warning logs through grpclog.Warning and adds a trace event if channelz is on.
func Warning(id int64, args ...interface{}) {
if IsOn() {
AddTraceEvent(id, 1, &TraceEventDesc{
Desc: fmt.Sprint(args...),
Severity: CtWarning,
})
} else {
grpclog.WarningDepth(1, args...)
}
}
// Warningf logs through grpclog.Warningf and adds a trace event if channelz is on.
func Warningf(id int64, format string, args ...interface{}) {
msg := fmt.Sprintf(format, args...)
if IsOn() {
AddTraceEvent(id, 1, &TraceEventDesc{
Desc: msg,
Severity: CtWarning,
})
} else {
grpclog.WarningDepth(1, msg)
}
}
// Error logs through grpclog.Error and adds a trace event if channelz is on.
func Error(id int64, args ...interface{}) {
if IsOn() {
AddTraceEvent(id, 1, &TraceEventDesc{
Desc: fmt.Sprint(args...),
Severity: CtError,
})
} else {
grpclog.ErrorDepth(1, args...)
}
}
// Errorf logs through grpclog.Errorf and adds a trace event if channelz is on.
func Errorf(id int64, format string, args ...interface{}) {
msg := fmt.Sprintf(format, args...)
if IsOn() {
AddTraceEvent(id, 1, &TraceEventDesc{
Desc: msg,
Severity: CtError,
})
} else {
grpclog.ErrorDepth(1, msg)
}
}

View File

@ -25,11 +25,14 @@ import (
) )
const ( const (
prefix = "GRPC_GO_" prefix = "GRPC_GO_"
retryStr = prefix + "RETRY" retryStr = prefix + "RETRY"
txtErrIgnoreStr = prefix + "IGNORE_TXT_ERRORS"
) )
var ( var (
// Retry is set if retry is explicitly enabled via "GRPC_GO_RETRY=on". // Retry is set if retry is explicitly enabled via "GRPC_GO_RETRY=on".
Retry = strings.EqualFold(os.Getenv(retryStr), "on") Retry = strings.EqualFold(os.Getenv(retryStr), "on")
// TXTErrIgnore is set if TXT errors should be ignored ("GRPC_GO_IGNORE_TXT_ERRORS" is not "false").
TXTErrIgnore = !strings.EqualFold(os.Getenv(retryStr), "false")
) )

View File

@ -0,0 +1,118 @@
/*
*
* Copyright 2020 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 grpclog (internal) defines depth logging for grpc.
package grpclog
// Logger is the logger used for the non-depth log functions.
var Logger LoggerV2
// DepthLogger is the logger used for the depth log functions.
var DepthLogger DepthLoggerV2
// InfoDepth logs to the INFO log at the specified depth.
func InfoDepth(depth int, args ...interface{}) {
if DepthLogger != nil {
DepthLogger.InfoDepth(depth, args...)
} else {
Logger.Info(args...)
}
}
// WarningDepth logs to the WARNING log at the specified depth.
func WarningDepth(depth int, args ...interface{}) {
if DepthLogger != nil {
DepthLogger.WarningDepth(depth, args...)
} else {
Logger.Warning(args...)
}
}
// ErrorDepth logs to the ERROR log at the specified depth.
func ErrorDepth(depth int, args ...interface{}) {
if DepthLogger != nil {
DepthLogger.ErrorDepth(depth, args...)
} else {
Logger.Error(args...)
}
}
// FatalDepth logs to the FATAL log at the specified depth.
func FatalDepth(depth int, args ...interface{}) {
if DepthLogger != nil {
DepthLogger.FatalDepth(depth, args...)
} else {
Logger.Fatal(args...)
}
}
// LoggerV2 does underlying logging work for grpclog.
// This is a copy of the LoggerV2 defined in the external grpclog package. It
// is defined here to avoid a circular dependency.
type LoggerV2 interface {
// Info logs to INFO log. Arguments are handled in the manner of fmt.Print.
Info(args ...interface{})
// Infoln logs to INFO log. Arguments are handled in the manner of fmt.Println.
Infoln(args ...interface{})
// Infof logs to INFO log. Arguments are handled in the manner of fmt.Printf.
Infof(format string, args ...interface{})
// Warning logs to WARNING log. Arguments are handled in the manner of fmt.Print.
Warning(args ...interface{})
// Warningln logs to WARNING log. Arguments are handled in the manner of fmt.Println.
Warningln(args ...interface{})
// Warningf logs to WARNING log. Arguments are handled in the manner of fmt.Printf.
Warningf(format string, args ...interface{})
// Error logs to ERROR log. Arguments are handled in the manner of fmt.Print.
Error(args ...interface{})
// Errorln logs to ERROR log. Arguments are handled in the manner of fmt.Println.
Errorln(args ...interface{})
// Errorf logs to ERROR log. Arguments are handled in the manner of fmt.Printf.
Errorf(format string, args ...interface{})
// Fatal logs to ERROR log. Arguments are handled in the manner of fmt.Print.
// gRPC ensures that all Fatal logs will exit with os.Exit(1).
// Implementations may also call os.Exit() with a non-zero exit code.
Fatal(args ...interface{})
// Fatalln logs to ERROR log. Arguments are handled in the manner of fmt.Println.
// gRPC ensures that all Fatal logs will exit with os.Exit(1).
// Implementations may also call os.Exit() with a non-zero exit code.
Fatalln(args ...interface{})
// Fatalf logs to ERROR log. Arguments are handled in the manner of fmt.Printf.
// gRPC ensures that all Fatal logs will exit with os.Exit(1).
// Implementations may also call os.Exit() with a non-zero exit code.
Fatalf(format string, args ...interface{})
// V reports whether verbosity level l is at least the requested verbose level.
V(l int) bool
}
// DepthLoggerV2 logs at a specified call frame. If a LoggerV2 also implements
// DepthLoggerV2, the below functions will be called with the appropriate stack
// depth set for trivial functions the logger may ignore.
// This is a copy of the DepthLoggerV2 defined in the external grpclog package.
// It is defined here to avoid a circular dependency.
//
// This API is EXPERIMENTAL.
type DepthLoggerV2 interface {
// InfoDepth logs to INFO log at the specified depth. Arguments are handled in the manner of fmt.Print.
InfoDepth(depth int, args ...interface{})
// WarningDepth logs to WARNING log at the specified depth. Arguments are handled in the manner of fmt.Print.
WarningDepth(depth int, args ...interface{})
// ErrorDetph logs to ERROR log at the specified depth. Arguments are handled in the manner of fmt.Print.
ErrorDepth(depth int, args ...interface{})
// FatalDepth logs to FATAL log at the specified depth. Arguments are handled in the manner of fmt.Print.
FatalDepth(depth int, args ...interface{})
}

View File

@ -0,0 +1,63 @@
/*
*
* Copyright 2020 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 grpclog
// PrefixLogger does logging with a prefix.
//
// Logging method on a nil logs without any prefix.
type PrefixLogger struct {
prefix string
}
// Infof does info logging.
func (pl *PrefixLogger) Infof(format string, args ...interface{}) {
if pl != nil {
// Handle nil, so the tests can pass in a nil logger.
format = pl.prefix + format
}
Logger.Infof(format, args...)
}
// Warningf does warning logging.
func (pl *PrefixLogger) Warningf(format string, args ...interface{}) {
if pl != nil {
format = pl.prefix + format
}
Logger.Warningf(format, args...)
}
// Errorf does error logging.
func (pl *PrefixLogger) Errorf(format string, args ...interface{}) {
if pl != nil {
format = pl.prefix + format
}
Logger.Errorf(format, args...)
}
// Debugf does info logging at verbose level 2.
func (pl *PrefixLogger) Debugf(format string, args ...interface{}) {
if Logger.V(2) {
pl.Infof(format, args...)
}
}
// NewPrefixLogger creates a prefix logger with the given prefix.
func NewPrefixLogger(prefix string) *PrefixLogger {
return &PrefixLogger{prefix: prefix}
}

View File

@ -0,0 +1,55 @@
/*
*
* Copyright 2020 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 grpcutil provides a bunch of utility functions to be used across the
// gRPC codebase.
package grpcutil
import (
"strings"
"google.golang.org/grpc/resolver"
)
// split2 returns the values from strings.SplitN(s, sep, 2).
// If sep is not found, it returns ("", "", false) instead.
func split2(s, sep string) (string, string, bool) {
spl := strings.SplitN(s, sep, 2)
if len(spl) < 2 {
return "", "", false
}
return spl[0], spl[1], true
}
// ParseTarget splits target into a resolver.Target struct containing scheme,
// authority and endpoint.
//
// If target is not a valid scheme://authority/endpoint, it returns {Endpoint:
// target}.
func ParseTarget(target string) (ret resolver.Target) {
var ok bool
ret.Scheme, ret.Endpoint, ok = split2(target, "://")
if !ok {
return resolver.Target{Endpoint: target}
}
ret.Authority, ret.Endpoint, ok = split2(ret.Endpoint, "/")
if !ok {
return resolver.Target{Endpoint: target}
}
return ret
}

View File

@ -28,8 +28,6 @@ import (
) )
var ( var (
// WithResolverBuilder is set by dialoptions.go
WithResolverBuilder interface{} // func (resolver.Builder) grpc.DialOption
// WithHealthCheckFunc is set by dialoptions.go // WithHealthCheckFunc is set by dialoptions.go
WithHealthCheckFunc interface{} // func (HealthChecker) DialOption WithHealthCheckFunc interface{} // func (HealthChecker) DialOption
// HealthCheckFunc is used to provide client-side LB channel health checking // HealthCheckFunc is used to provide client-side LB channel health checking
@ -39,11 +37,6 @@ var (
// KeepaliveMinPingTime is the minimum ping interval. This must be 10s by // KeepaliveMinPingTime is the minimum ping interval. This must be 10s by
// default, but tests may wish to set it lower for convenience. // default, but tests may wish to set it lower for convenience.
KeepaliveMinPingTime = 10 * time.Second KeepaliveMinPingTime = 10 * time.Second
// StatusRawProto is exported by status/status.go. This func returns a
// pointer to the wrapped Status proto for a given status.Status without a
// call to proto.Clone(). The returned Status proto should not be mutated by
// the caller.
StatusRawProto interface{} // func (*status.Status) *spb.Status
// NewRequestInfoContext creates a new context based on the argument context attaching // NewRequestInfoContext creates a new context based on the argument context attaching
// the passed in RequestInfo to the new context. // the passed in RequestInfo to the new context.
NewRequestInfoContext interface{} // func(context.Context, credentials.RequestInfo) context.Context NewRequestInfoContext interface{} // func(context.Context, credentials.RequestInfo) context.Context
@ -60,7 +53,7 @@ var (
// //
// The health checking protocol is defined at: // The health checking protocol is defined at:
// https://github.com/grpc/grpc/blob/master/doc/health-checking.md // https://github.com/grpc/grpc/blob/master/doc/health-checking.md
type HealthChecker func(ctx context.Context, newStream func(string) (interface{}, error), setConnectivityState func(connectivity.State), serviceName string) error type HealthChecker func(ctx context.Context, newStream func(string) (interface{}, error), setConnectivityState func(connectivity.State, error), serviceName string) error
const ( const (
// CredsBundleModeFallback switches GoogleDefaultCreds to fallback mode. // CredsBundleModeFallback switches GoogleDefaultCreds to fallback mode.

View File

@ -32,20 +32,23 @@ import (
"sync" "sync"
"time" "time"
"google.golang.org/grpc/backoff"
"google.golang.org/grpc/grpclog" "google.golang.org/grpc/grpclog"
internalbackoff "google.golang.org/grpc/internal/backoff" "google.golang.org/grpc/internal/envconfig"
"google.golang.org/grpc/internal/grpcrand" "google.golang.org/grpc/internal/grpcrand"
"google.golang.org/grpc/resolver" "google.golang.org/grpc/resolver"
"google.golang.org/grpc/serviceconfig"
) )
// EnableSRVLookups controls whether the DNS resolver attempts to fetch gRPCLB
// addresses from SRV records. Must not be changed after init time.
var EnableSRVLookups = false
func init() { func init() {
resolver.Register(NewBuilder()) resolver.Register(NewBuilder())
} }
const ( const (
defaultPort = "443" defaultPort = "443"
defaultFreq = time.Minute * 30
defaultDNSSvrPort = "53" defaultDNSSvrPort = "53"
golang = "GO" golang = "GO"
// txtPrefix is the prefix string to be prepended to the host name for txt record lookup. // txtPrefix is the prefix string to be prepended to the host name for txt record lookup.
@ -95,49 +98,33 @@ var customAuthorityResolver = func(authority string) (netResolver, error) {
// NewBuilder creates a dnsBuilder which is used to factory DNS resolvers. // NewBuilder creates a dnsBuilder which is used to factory DNS resolvers.
func NewBuilder() resolver.Builder { func NewBuilder() resolver.Builder {
return &dnsBuilder{minFreq: defaultFreq} return &dnsBuilder{}
} }
type dnsBuilder struct { type dnsBuilder struct{}
// minimum frequency of polling the DNS server.
minFreq time.Duration
}
// Build creates and starts a DNS resolver that watches the name resolution of the target. // Build creates and starts a DNS resolver that watches the name resolution of the target.
func (b *dnsBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOption) (resolver.Resolver, error) { func (b *dnsBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOptions) (resolver.Resolver, error) {
host, port, err := parseTarget(target.Endpoint, defaultPort) host, port, err := parseTarget(target.Endpoint, defaultPort)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// IP address. // IP address.
if net.ParseIP(host) != nil { if ipAddr, ok := formatIP(host); ok {
host, _ = formatIP(host) addr := []resolver.Address{{Addr: ipAddr + ":" + port}}
addr := []resolver.Address{{Addr: host + ":" + port}} cc.UpdateState(resolver.State{Addresses: addr})
i := &ipResolver{ return deadResolver{}, nil
cc: cc,
ip: addr,
rn: make(chan struct{}, 1),
q: make(chan struct{}),
}
cc.NewAddress(addr)
go i.watcher()
return i, nil
} }
// DNS address (non-IP). // DNS address (non-IP).
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
bc := backoff.DefaultConfig
bc.MaxDelay = b.minFreq
d := &dnsResolver{ d := &dnsResolver{
freq: b.minFreq,
backoff: internalbackoff.Exponential{Config: bc},
host: host, host: host,
port: port, port: port,
ctx: ctx, ctx: ctx,
cancel: cancel, cancel: cancel,
cc: cc, cc: cc,
t: time.NewTimer(0),
rn: make(chan struct{}, 1), rn: make(chan struct{}, 1),
disableServiceConfig: opts.DisableServiceConfig, disableServiceConfig: opts.DisableServiceConfig,
} }
@ -153,6 +140,7 @@ func (b *dnsBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts
d.wg.Add(1) d.wg.Add(1)
go d.watcher() go d.watcher()
d.ResolveNow(resolver.ResolveNowOptions{})
return d, nil return d, nil
} }
@ -167,53 +155,23 @@ type netResolver interface {
LookupTXT(ctx context.Context, name string) (txts []string, err error) LookupTXT(ctx context.Context, name string) (txts []string, err error)
} }
// ipResolver watches for the name resolution update for an IP address. // deadResolver is a resolver that does nothing.
type ipResolver struct { type deadResolver struct{}
cc resolver.ClientConn
ip []resolver.Address
// rn channel is used by ResolveNow() to force an immediate resolution of the target.
rn chan struct{}
q chan struct{}
}
// ResolveNow resend the address it stores, no resolution is needed. func (deadResolver) ResolveNow(resolver.ResolveNowOptions) {}
func (i *ipResolver) ResolveNow(opt resolver.ResolveNowOption) {
select {
case i.rn <- struct{}{}:
default:
}
}
// Close closes the ipResolver. func (deadResolver) Close() {}
func (i *ipResolver) Close() {
close(i.q)
}
func (i *ipResolver) watcher() {
for {
select {
case <-i.rn:
i.cc.NewAddress(i.ip)
case <-i.q:
return
}
}
}
// dnsResolver watches for the name resolution update for a non-IP target. // dnsResolver watches for the name resolution update for a non-IP target.
type dnsResolver struct { type dnsResolver struct {
freq time.Duration host string
backoff internalbackoff.Exponential port string
retryCount int resolver netResolver
host string ctx context.Context
port string cancel context.CancelFunc
resolver netResolver cc resolver.ClientConn
ctx context.Context
cancel context.CancelFunc
cc resolver.ClientConn
// rn channel is used by ResolveNow() to force an immediate resolution of the target. // rn channel is used by ResolveNow() to force an immediate resolution of the target.
rn chan struct{} rn chan struct{}
t *time.Timer
// wg is used to enforce Close() to return after the watcher() goroutine has finished. // wg is used to enforce Close() to return after the watcher() goroutine has finished.
// Otherwise, data race will be possible. [Race Example] in dns_resolver_test we // Otherwise, data race will be possible. [Race Example] in dns_resolver_test we
// replace the real lookup functions with mocked ones to facilitate testing. // replace the real lookup functions with mocked ones to facilitate testing.
@ -225,7 +183,7 @@ type dnsResolver struct {
} }
// ResolveNow invoke an immediate resolution of the target that this dnsResolver watches. // ResolveNow invoke an immediate resolution of the target that this dnsResolver watches.
func (d *dnsResolver) ResolveNow(opt resolver.ResolveNowOption) { func (d *dnsResolver) ResolveNow(resolver.ResolveNowOptions) {
select { select {
case d.rn <- struct{}{}: case d.rn <- struct{}{}:
default: default:
@ -236,7 +194,6 @@ func (d *dnsResolver) ResolveNow(opt resolver.ResolveNowOption) {
func (d *dnsResolver) Close() { func (d *dnsResolver) Close() {
d.cancel() d.cancel()
d.wg.Wait() d.wg.Wait()
d.t.Stop()
} }
func (d *dnsResolver) watcher() { func (d *dnsResolver) watcher() {
@ -245,27 +202,15 @@ func (d *dnsResolver) watcher() {
select { select {
case <-d.ctx.Done(): case <-d.ctx.Done():
return return
case <-d.t.C:
case <-d.rn: case <-d.rn:
if !d.t.Stop() {
// Before resetting a timer, it should be stopped to prevent racing with
// reads on it's channel.
<-d.t.C
}
} }
result, sc := d.lookup() state, err := d.lookup()
// Next lookup should happen within an interval defined by d.freq. It may be if err != nil {
// more often due to exponential retry on empty address list. d.cc.ReportError(err)
if len(result) == 0 {
d.retryCount++
d.t.Reset(d.backoff.Backoff(d.retryCount))
} else { } else {
d.retryCount = 0 d.cc.UpdateState(*state)
d.t.Reset(d.freq)
} }
d.cc.NewServiceConfig(sc)
d.cc.NewAddress(result)
// Sleep to prevent excessive re-resolutions. Incoming resolution requests // Sleep to prevent excessive re-resolutions. Incoming resolution requests
// will be queued in d.rn. // will be queued in d.rn.
@ -279,37 +224,68 @@ func (d *dnsResolver) watcher() {
} }
} }
func (d *dnsResolver) lookupSRV() []resolver.Address { func (d *dnsResolver) lookupSRV() ([]resolver.Address, error) {
if !EnableSRVLookups {
return nil, nil
}
var newAddrs []resolver.Address var newAddrs []resolver.Address
_, srvs, err := d.resolver.LookupSRV(d.ctx, "grpclb", "tcp", d.host) _, srvs, err := d.resolver.LookupSRV(d.ctx, "grpclb", "tcp", d.host)
if err != nil { if err != nil {
grpclog.Infof("grpc: failed dns SRV record lookup due to %v.\n", err) err = handleDNSError(err, "SRV") // may become nil
return nil return nil, err
} }
for _, s := range srvs { for _, s := range srvs {
lbAddrs, err := d.resolver.LookupHost(d.ctx, s.Target) lbAddrs, err := d.resolver.LookupHost(d.ctx, s.Target)
if err != nil { if err != nil {
grpclog.Infof("grpc: failed load balancer address dns lookup due to %v.\n", err) err = handleDNSError(err, "A") // may become nil
continue if err == nil {
} // If there are other SRV records, look them up and ignore this
for _, a := range lbAddrs { // one that does not exist.
a, ok := formatIP(a)
if !ok {
grpclog.Errorf("grpc: failed IP parsing due to %v.\n", err)
continue continue
} }
addr := a + ":" + strconv.Itoa(int(s.Port)) return nil, err
}
for _, a := range lbAddrs {
ip, ok := formatIP(a)
if !ok {
return nil, fmt.Errorf("dns: error parsing A record IP address %v", a)
}
addr := ip + ":" + strconv.Itoa(int(s.Port))
newAddrs = append(newAddrs, resolver.Address{Addr: addr, Type: resolver.GRPCLB, ServerName: s.Target}) newAddrs = append(newAddrs, resolver.Address{Addr: addr, Type: resolver.GRPCLB, ServerName: s.Target})
} }
} }
return newAddrs return newAddrs, nil
} }
func (d *dnsResolver) lookupTXT() string { var filterError = func(err error) error {
if dnsErr, ok := err.(*net.DNSError); ok && !dnsErr.IsTimeout && !dnsErr.IsTemporary {
// Timeouts and temporary errors should be communicated to gRPC to
// attempt another DNS query (with backoff). Other errors should be
// suppressed (they may represent the absence of a TXT record).
return nil
}
return err
}
func handleDNSError(err error, lookupType string) error {
err = filterError(err)
if err != nil {
err = fmt.Errorf("dns: %v record lookup error: %v", lookupType, err)
grpclog.Infoln(err)
}
return err
}
func (d *dnsResolver) lookupTXT() *serviceconfig.ParseResult {
ss, err := d.resolver.LookupTXT(d.ctx, txtPrefix+d.host) ss, err := d.resolver.LookupTXT(d.ctx, txtPrefix+d.host)
if err != nil { if err != nil {
grpclog.Infof("grpc: failed dns TXT record lookup due to %v.\n", err) if envconfig.TXTErrIgnore {
return "" return nil
}
if err = handleDNSError(err, "TXT"); err != nil {
return &serviceconfig.ParseResult{Err: err}
}
return nil
} }
var res string var res string
for _, s := range ss { for _, s := range ss {
@ -318,40 +294,45 @@ func (d *dnsResolver) lookupTXT() string {
// TXT record must have "grpc_config=" attribute in order to be used as service config. // TXT record must have "grpc_config=" attribute in order to be used as service config.
if !strings.HasPrefix(res, txtAttribute) { if !strings.HasPrefix(res, txtAttribute) {
grpclog.Warningf("grpc: TXT record %v missing %v attribute", res, txtAttribute) grpclog.Warningf("dns: TXT record %v missing %v attribute", res, txtAttribute)
return "" // This is not an error; it is the equivalent of not having a service config.
return nil
} }
return strings.TrimPrefix(res, txtAttribute) sc := canaryingSC(strings.TrimPrefix(res, txtAttribute))
return d.cc.ParseServiceConfig(sc)
} }
func (d *dnsResolver) lookupHost() []resolver.Address { func (d *dnsResolver) lookupHost() ([]resolver.Address, error) {
var newAddrs []resolver.Address var newAddrs []resolver.Address
addrs, err := d.resolver.LookupHost(d.ctx, d.host) addrs, err := d.resolver.LookupHost(d.ctx, d.host)
if err != nil { if err != nil {
grpclog.Warningf("grpc: failed dns A record lookup due to %v.\n", err) err = handleDNSError(err, "A")
return nil return nil, err
} }
for _, a := range addrs { for _, a := range addrs {
a, ok := formatIP(a) ip, ok := formatIP(a)
if !ok { if !ok {
grpclog.Errorf("grpc: failed IP parsing due to %v.\n", err) return nil, fmt.Errorf("dns: error parsing A record IP address %v", a)
continue
} }
addr := a + ":" + d.port addr := ip + ":" + d.port
newAddrs = append(newAddrs, resolver.Address{Addr: addr}) newAddrs = append(newAddrs, resolver.Address{Addr: addr})
} }
return newAddrs return newAddrs, nil
} }
func (d *dnsResolver) lookup() ([]resolver.Address, string) { func (d *dnsResolver) lookup() (*resolver.State, error) {
newAddrs := d.lookupSRV() srv, srvErr := d.lookupSRV()
// Support fallback to non-balancer address. addrs, hostErr := d.lookupHost()
newAddrs = append(newAddrs, d.lookupHost()...) if hostErr != nil && (srvErr != nil || len(srv) == 0) {
if d.disableServiceConfig { return nil, hostErr
return newAddrs, ""
} }
sc := d.lookupTXT() state := &resolver.State{
return newAddrs, canaryingSC(sc) Addresses: append(addrs, srv...),
}
if !d.disableServiceConfig {
state.ServiceConfig = d.lookupTXT()
}
return state, nil
} }
// formatIP returns ok = false if addr is not a valid textual representation of an IP address. // formatIP returns ok = false if addr is not a valid textual representation of an IP address.
@ -437,12 +418,12 @@ func canaryingSC(js string) string {
var rcs []rawChoice var rcs []rawChoice
err := json.Unmarshal([]byte(js), &rcs) err := json.Unmarshal([]byte(js), &rcs)
if err != nil { if err != nil {
grpclog.Warningf("grpc: failed to parse service config json string due to %v.\n", err) grpclog.Warningf("dns: error parsing service config json: %v", err)
return "" return ""
} }
cliHostname, err := os.Hostname() cliHostname, err := os.Hostname()
if err != nil { if err != nil {
grpclog.Warningf("grpc: failed to get client hostname due to %v.\n", err) grpclog.Warningf("dns: error getting client hostname: %v", err)
return "" return ""
} }
var sc string var sc string

View File

@ -0,0 +1,33 @@
// +build go1.13
/*
*
* Copyright 2019 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 dns
import "net"
func init() {
filterError = func(err error) error {
if dnsErr, ok := err.(*net.DNSError); ok && dnsErr.IsNotFound {
// The name does not exist; not an error.
return nil
}
return err
}
}

View File

@ -26,7 +26,7 @@ const scheme = "passthrough"
type passthroughBuilder struct{} type passthroughBuilder struct{}
func (*passthroughBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOption) (resolver.Resolver, error) { func (*passthroughBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOptions) (resolver.Resolver, error) {
r := &passthroughResolver{ r := &passthroughResolver{
target: target, target: target,
cc: cc, cc: cc,
@ -48,7 +48,7 @@ func (r *passthroughResolver) start() {
r.cc.UpdateState(resolver.State{Addresses: []resolver.Address{{Addr: r.target.Endpoint}}}) r.cc.UpdateState(resolver.State{Addresses: []resolver.Address{{Addr: r.target.Endpoint}}})
} }
func (*passthroughResolver) ResolveNow(o resolver.ResolveNowOption) {} func (*passthroughResolver) ResolveNow(o resolver.ResolveNowOptions) {}
func (*passthroughResolver) Close() {} func (*passthroughResolver) Close() {}

166
vendor/google.golang.org/grpc/internal/status/status.go generated vendored Normal file
View File

@ -0,0 +1,166 @@
/*
*
* Copyright 2020 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 status implements errors returned by gRPC. These errors are
// serialized and transmitted on the wire between server and client, and allow
// for additional data to be transmitted via the Details field in the status
// proto. gRPC service handlers should return an error created by this
// package, and gRPC clients should expect a corresponding error to be
// returned from the RPC call.
//
// This package upholds the invariants that a non-nil error may not
// contain an OK code, and an OK code must result in a nil error.
package status
import (
"errors"
"fmt"
"github.com/golang/protobuf/proto"
"github.com/golang/protobuf/ptypes"
spb "google.golang.org/genproto/googleapis/rpc/status"
"google.golang.org/grpc/codes"
)
// Status represents an RPC status code, message, and details. It is immutable
// and should be created with New, Newf, or FromProto.
type Status struct {
s *spb.Status
}
// New returns a Status representing c and msg.
func New(c codes.Code, msg string) *Status {
return &Status{s: &spb.Status{Code: int32(c), Message: msg}}
}
// Newf returns New(c, fmt.Sprintf(format, a...)).
func Newf(c codes.Code, format string, a ...interface{}) *Status {
return New(c, fmt.Sprintf(format, a...))
}
// FromProto returns a Status representing s.
func FromProto(s *spb.Status) *Status {
return &Status{s: proto.Clone(s).(*spb.Status)}
}
// Err returns an error representing c and msg. If c is OK, returns nil.
func Err(c codes.Code, msg string) error {
return New(c, msg).Err()
}
// Errorf returns Error(c, fmt.Sprintf(format, a...)).
func Errorf(c codes.Code, format string, a ...interface{}) error {
return Err(c, fmt.Sprintf(format, a...))
}
// Code returns the status code contained in s.
func (s *Status) Code() codes.Code {
if s == nil || s.s == nil {
return codes.OK
}
return codes.Code(s.s.Code)
}
// Message returns the message contained in s.
func (s *Status) Message() string {
if s == nil || s.s == nil {
return ""
}
return s.s.Message
}
// Proto returns s's status as an spb.Status proto message.
func (s *Status) Proto() *spb.Status {
if s == nil {
return nil
}
return proto.Clone(s.s).(*spb.Status)
}
// Err returns an immutable error representing s; returns nil if s.Code() is OK.
func (s *Status) Err() error {
if s.Code() == codes.OK {
return nil
}
return (*Error)(s.Proto())
}
func (s *Status) Error() string {
p := s.Proto()
return fmt.Sprintf("rpc error: code = %s desc = %s", codes.Code(p.GetCode()), p.GetMessage())
}
// WithDetails returns a new status with the provided details messages appended to the status.
// If any errors are encountered, it returns nil and the first error encountered.
func (s *Status) WithDetails(details ...proto.Message) (*Status, error) {
if s.Code() == codes.OK {
return nil, errors.New("no error details for status with code OK")
}
// s.Code() != OK implies that s.Proto() != nil.
p := s.Proto()
for _, detail := range details {
any, err := ptypes.MarshalAny(detail)
if err != nil {
return nil, err
}
p.Details = append(p.Details, any)
}
return &Status{s: p}, nil
}
// Details returns a slice of details messages attached to the status.
// If a detail cannot be decoded, the error is returned in place of the detail.
func (s *Status) Details() []interface{} {
if s == nil || s.s == nil {
return nil
}
details := make([]interface{}, 0, len(s.s.Details))
for _, any := range s.s.Details {
detail := &ptypes.DynamicAny{}
if err := ptypes.UnmarshalAny(any, detail); err != nil {
details = append(details, err)
continue
}
details = append(details, detail.Message)
}
return details
}
// Error is an alias of a status proto. It implements error and Status,
// and a nil Error should never be returned by this package.
type Error spb.Status
func (se *Error) Error() string {
p := (*spb.Status)(se)
return fmt.Sprintf("rpc error: code = %s desc = %s", codes.Code(p.GetCode()), p.GetMessage())
}
// GRPCStatus returns the Status represented by se.
func (se *Error) GRPCStatus() *Status {
return FromProto((*spb.Status)(se))
}
// Is implements future error.Is functionality.
// A Error is equivalent if the code and message are identical.
func (se *Error) Is(target error) bool {
tse, ok := target.(*Error)
if !ok {
return false
}
return proto.Equal((*spb.Status)(se), (*spb.Status)(tse))
}

View File

@ -112,11 +112,10 @@ func NewServerHandlerTransport(w http.ResponseWriter, r *http.Request, stats sta
// at this point to be speaking over HTTP/2, so it's able to speak valid // at this point to be speaking over HTTP/2, so it's able to speak valid
// gRPC. // gRPC.
type serverHandlerTransport struct { type serverHandlerTransport struct {
rw http.ResponseWriter rw http.ResponseWriter
req *http.Request req *http.Request
timeoutSet bool timeoutSet bool
timeout time.Duration timeout time.Duration
didCommonHeaders bool
headerMD metadata.MD headerMD metadata.MD
@ -186,8 +185,11 @@ func (ht *serverHandlerTransport) WriteStatus(s *Stream, st *status.Status) erro
ht.writeStatusMu.Lock() ht.writeStatusMu.Lock()
defer ht.writeStatusMu.Unlock() defer ht.writeStatusMu.Unlock()
headersWritten := s.updateHeaderSent()
err := ht.do(func() { err := ht.do(func() {
ht.writeCommonHeaders(s) if !headersWritten {
ht.writePendingHeaders(s)
}
// And flush, in case no header or body has been sent yet. // And flush, in case no header or body has been sent yet.
// This forces a separation of headers and trailers if this is the // This forces a separation of headers and trailers if this is the
@ -227,21 +229,27 @@ func (ht *serverHandlerTransport) WriteStatus(s *Stream, st *status.Status) erro
if err == nil { // transport has not been closed if err == nil { // transport has not been closed
if ht.stats != nil { if ht.stats != nil {
ht.stats.HandleRPC(s.Context(), &stats.OutTrailer{}) // Note: The trailer fields are compressed with hpack after this call returns.
// No WireLength field is set here.
ht.stats.HandleRPC(s.Context(), &stats.OutTrailer{
Trailer: s.trailer.Copy(),
})
} }
} }
ht.Close() ht.Close()
return err return err
} }
// writePendingHeaders sets common and custom headers on the first
// write call (Write, WriteHeader, or WriteStatus)
func (ht *serverHandlerTransport) writePendingHeaders(s *Stream) {
ht.writeCommonHeaders(s)
ht.writeCustomHeaders(s)
}
// writeCommonHeaders sets common headers on the first write // writeCommonHeaders sets common headers on the first write
// call (Write, WriteHeader, or WriteStatus). // call (Write, WriteHeader, or WriteStatus).
func (ht *serverHandlerTransport) writeCommonHeaders(s *Stream) { func (ht *serverHandlerTransport) writeCommonHeaders(s *Stream) {
if ht.didCommonHeaders {
return
}
ht.didCommonHeaders = true
h := ht.rw.Header() h := ht.rw.Header()
h["Date"] = nil // suppress Date to make tests happy; TODO: restore h["Date"] = nil // suppress Date to make tests happy; TODO: restore
h.Set("Content-Type", ht.contentType) h.Set("Content-Type", ht.contentType)
@ -260,9 +268,30 @@ func (ht *serverHandlerTransport) writeCommonHeaders(s *Stream) {
} }
} }
// writeCustomHeaders sets custom headers set on the stream via SetHeader
// on the first write call (Write, WriteHeader, or WriteStatus).
func (ht *serverHandlerTransport) writeCustomHeaders(s *Stream) {
h := ht.rw.Header()
s.hdrMu.Lock()
for k, vv := range s.header {
if isReservedHeader(k) {
continue
}
for _, v := range vv {
h.Add(k, encodeMetadataHeader(k, v))
}
}
s.hdrMu.Unlock()
}
func (ht *serverHandlerTransport) Write(s *Stream, hdr []byte, data []byte, opts *Options) error { func (ht *serverHandlerTransport) Write(s *Stream, hdr []byte, data []byte, opts *Options) error {
headersWritten := s.updateHeaderSent()
return ht.do(func() { return ht.do(func() {
ht.writeCommonHeaders(s) if !headersWritten {
ht.writePendingHeaders(s)
}
ht.rw.Write(hdr) ht.rw.Write(hdr)
ht.rw.Write(data) ht.rw.Write(data)
ht.rw.(http.Flusher).Flush() ht.rw.(http.Flusher).Flush()
@ -270,26 +299,28 @@ func (ht *serverHandlerTransport) Write(s *Stream, hdr []byte, data []byte, opts
} }
func (ht *serverHandlerTransport) WriteHeader(s *Stream, md metadata.MD) error { func (ht *serverHandlerTransport) WriteHeader(s *Stream, md metadata.MD) error {
if err := s.SetHeader(md); err != nil {
return err
}
headersWritten := s.updateHeaderSent()
err := ht.do(func() { err := ht.do(func() {
ht.writeCommonHeaders(s) if !headersWritten {
h := ht.rw.Header() ht.writePendingHeaders(s)
for k, vv := range md {
// Clients don't tolerate reading restricted headers after some non restricted ones were sent.
if isReservedHeader(k) {
continue
}
for _, v := range vv {
v = encodeMetadataHeader(k, v)
h.Add(k, v)
}
} }
ht.rw.WriteHeader(200) ht.rw.WriteHeader(200)
ht.rw.(http.Flusher).Flush() ht.rw.(http.Flusher).Flush()
}) })
if err == nil { if err == nil {
if ht.stats != nil { if ht.stats != nil {
ht.stats.HandleRPC(s.Context(), &stats.OutHeader{}) // Note: The header fields are compressed with hpack after this call returns.
// No WireLength field is set here.
ht.stats.HandleRPC(s.Context(), &stats.OutHeader{
Header: md.Copy(),
Compression: s.sendCompress,
})
} }
} }
return err return err
@ -334,7 +365,7 @@ func (ht *serverHandlerTransport) HandleStreams(startStream func(*Stream), trace
Addr: ht.RemoteAddr(), Addr: ht.RemoteAddr(),
} }
if req.TLS != nil { if req.TLS != nil {
pr.AuthInfo = credentials.TLSInfo{State: *req.TLS} pr.AuthInfo = credentials.TLSInfo{State: *req.TLS, CommonAuthInfo: credentials.CommonAuthInfo{SecurityLevel: credentials.PrivacyAndIntegrity}}
} }
ctx = metadata.NewIncomingContext(ctx, ht.headerMD) ctx = metadata.NewIncomingContext(ctx, ht.headerMD)
s.ctx = peer.NewContext(ctx, pr) s.ctx = peer.NewContext(ctx, pr)

View File

@ -45,9 +45,14 @@ import (
"google.golang.org/grpc/status" "google.golang.org/grpc/status"
) )
// clientConnectionCounter counts the number of connections a client has
// initiated (equal to the number of http2Clients created). Must be accessed
// atomically.
var clientConnectionCounter uint64
// http2Client implements the ClientTransport interface with HTTP2. // http2Client implements the ClientTransport interface with HTTP2.
type http2Client struct { type http2Client struct {
lastRead int64 // keep this field 64-bit aligned lastRead int64 // Keep this field 64-bit aligned. Accessed atomically.
ctx context.Context ctx context.Context
cancel context.CancelFunc cancel context.CancelFunc
ctxDone <-chan struct{} // Cache the ctx.Done() chan. ctxDone <-chan struct{} // Cache the ctx.Done() chan.
@ -126,6 +131,8 @@ type http2Client struct {
onClose func() onClose func()
bufferPool *bufferPool bufferPool *bufferPool
connectionID uint64
} }
func dial(ctx context.Context, fn func(context.Context, string) (net.Conn, error), addr string) (net.Conn, error) { func dial(ctx context.Context, fn func(context.Context, string) (net.Conn, error), addr string) (net.Conn, error) {
@ -329,6 +336,8 @@ func newHTTP2Client(connectCtx, ctx context.Context, addr TargetInfo, opts Conne
} }
} }
t.connectionID = atomic.AddUint64(&clientConnectionCounter, 1)
if err := t.framer.writer.Flush(); err != nil { if err := t.framer.writer.Flush(); err != nil {
return nil, err return nil, err
} }
@ -394,7 +403,8 @@ func (t *http2Client) getPeer() *peer.Peer {
func (t *http2Client) createHeaderFields(ctx context.Context, callHdr *CallHdr) ([]hpack.HeaderField, error) { func (t *http2Client) createHeaderFields(ctx context.Context, callHdr *CallHdr) ([]hpack.HeaderField, error) {
aud := t.createAudience(callHdr) aud := t.createAudience(callHdr)
ri := credentials.RequestInfo{ ri := credentials.RequestInfo{
Method: callHdr.Method, Method: callHdr.Method,
AuthInfo: t.authInfo,
} }
ctxWithRequestInfo := internal.NewRequestInfoContext.(func(context.Context, credentials.RequestInfo) context.Context)(ctx, ri) ctxWithRequestInfo := internal.NewRequestInfoContext.(func(context.Context, credentials.RequestInfo) context.Context)(ctx, ri)
authData, err := t.getTrAuthData(ctxWithRequestInfo, aud) authData, err := t.getTrAuthData(ctxWithRequestInfo, aud)
@ -424,6 +434,7 @@ func (t *http2Client) createHeaderFields(ctx context.Context, callHdr *CallHdr)
if callHdr.SendCompress != "" { if callHdr.SendCompress != "" {
headerFields = append(headerFields, hpack.HeaderField{Name: "grpc-encoding", Value: callHdr.SendCompress}) headerFields = append(headerFields, hpack.HeaderField{Name: "grpc-encoding", Value: callHdr.SendCompress})
headerFields = append(headerFields, hpack.HeaderField{Name: "grpc-accept-encoding", Value: callHdr.SendCompress})
} }
if dl, ok := ctx.Deadline(); ok { if dl, ok := ctx.Deadline(); ok {
// Send out timeout regardless its value. The server can detect timeout context by itself. // Send out timeout regardless its value. The server can detect timeout context by itself.
@ -669,12 +680,21 @@ func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (_ *Strea
} }
} }
if t.statsHandler != nil { if t.statsHandler != nil {
header, ok := metadata.FromOutgoingContext(ctx)
if ok {
header.Set("user-agent", t.userAgent)
} else {
header = metadata.Pairs("user-agent", t.userAgent)
}
// Note: The header fields are compressed with hpack after this call returns.
// No WireLength field is set here.
outHeader := &stats.OutHeader{ outHeader := &stats.OutHeader{
Client: true, Client: true,
FullMethod: callHdr.Method, FullMethod: callHdr.Method,
RemoteAddr: t.remoteAddr, RemoteAddr: t.remoteAddr,
LocalAddr: t.localAddr, LocalAddr: t.localAddr,
Compression: callHdr.SendCompress, Compression: callHdr.SendCompress,
Header: header,
} }
t.statsHandler.HandleRPC(s.ctx, outHeader) t.statsHandler.HandleRPC(s.ctx, outHeader)
} }
@ -1175,14 +1195,17 @@ func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) {
if t.statsHandler != nil { if t.statsHandler != nil {
if isHeader { if isHeader {
inHeader := &stats.InHeader{ inHeader := &stats.InHeader{
Client: true, Client: true,
WireLength: int(frame.Header().Length), WireLength: int(frame.Header().Length),
Header: s.header.Copy(),
Compression: s.recvCompress,
} }
t.statsHandler.HandleRPC(s.ctx, inHeader) t.statsHandler.HandleRPC(s.ctx, inHeader)
} else { } else {
inTrailer := &stats.InTrailer{ inTrailer := &stats.InTrailer{
Client: true, Client: true,
WireLength: int(frame.Header().Length), WireLength: int(frame.Header().Length),
Trailer: s.trailer.Copy(),
} }
t.statsHandler.HandleRPC(s.ctx, inTrailer) t.statsHandler.HandleRPC(s.ctx, inTrailer)
} }
@ -1369,7 +1392,6 @@ func (t *http2Client) keepalive() {
// acked). // acked).
sleepDuration := minTime(t.kp.Time, timeoutLeft) sleepDuration := minTime(t.kp.Time, timeoutLeft)
timeoutLeft -= sleepDuration timeoutLeft -= sleepDuration
prevNano = lastRead
timer.Reset(sleepDuration) timer.Reset(sleepDuration)
case <-t.ctx.Done(): case <-t.ctx.Done():
if !timer.Stop() { if !timer.Stop() {

View File

@ -35,11 +35,9 @@ import (
"golang.org/x/net/http2" "golang.org/x/net/http2"
"golang.org/x/net/http2/hpack" "golang.org/x/net/http2/hpack"
spb "google.golang.org/genproto/googleapis/rpc/status"
"google.golang.org/grpc/codes" "google.golang.org/grpc/codes"
"google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials"
"google.golang.org/grpc/grpclog" "google.golang.org/grpc/grpclog"
"google.golang.org/grpc/internal"
"google.golang.org/grpc/internal/channelz" "google.golang.org/grpc/internal/channelz"
"google.golang.org/grpc/internal/grpcrand" "google.golang.org/grpc/internal/grpcrand"
"google.golang.org/grpc/keepalive" "google.golang.org/grpc/keepalive"
@ -57,13 +55,15 @@ var (
// ErrHeaderListSizeLimitViolation indicates that the header list size is larger // ErrHeaderListSizeLimitViolation indicates that the header list size is larger
// than the limit set by peer. // than the limit set by peer.
ErrHeaderListSizeLimitViolation = errors.New("transport: trying to send header list size larger than the limit set by peer") ErrHeaderListSizeLimitViolation = errors.New("transport: trying to send header list size larger than the limit set by peer")
// statusRawProto is a function to get to the raw status proto wrapped in a
// status.Status without a proto.Clone().
statusRawProto = internal.StatusRawProto.(func(*status.Status) *spb.Status)
) )
// serverConnectionCounter counts the number of connections a server has seen
// (equal to the number of http2Servers created). Must be accessed atomically.
var serverConnectionCounter uint64
// http2Server implements the ServerTransport interface with HTTP2. // http2Server implements the ServerTransport interface with HTTP2.
type http2Server struct { type http2Server struct {
lastRead int64 // Keep this field 64-bit aligned. Accessed atomically.
ctx context.Context ctx context.Context
done chan struct{} done chan struct{}
conn net.Conn conn net.Conn
@ -83,12 +83,8 @@ type http2Server struct {
controlBuf *controlBuffer controlBuf *controlBuffer
fc *trInFlow fc *trInFlow
stats stats.Handler stats stats.Handler
// Flag to keep track of reading activity on transport.
// 1 is true and 0 is false.
activity uint32 // Accessed atomically.
// Keepalive and max-age parameters for the server. // Keepalive and max-age parameters for the server.
kp keepalive.ServerParameters kp keepalive.ServerParameters
// Keepalive enforcement policy. // Keepalive enforcement policy.
kep keepalive.EnforcementPolicy kep keepalive.EnforcementPolicy
// The time instance last ping was received. // The time instance last ping was received.
@ -124,6 +120,8 @@ type http2Server struct {
channelzID int64 // channelz unique identification number channelzID int64 // channelz unique identification number
czData *channelzData czData *channelzData
bufferPool *bufferPool bufferPool *bufferPool
connectionID uint64
} }
// newHTTP2Server constructs a ServerTransport based on HTTP2. ConnectionError is // newHTTP2Server constructs a ServerTransport based on HTTP2. ConnectionError is
@ -253,6 +251,9 @@ func newHTTP2Server(conn net.Conn, config *ServerConfig) (_ ServerTransport, err
if channelz.IsOn() { if channelz.IsOn() {
t.channelzID = channelz.RegisterNormalSocket(t, config.ChannelzParentID, fmt.Sprintf("%s -> %s", t.remoteAddr, t.localAddr)) t.channelzID = channelz.RegisterNormalSocket(t, config.ChannelzParentID, fmt.Sprintf("%s -> %s", t.remoteAddr, t.localAddr))
} }
t.connectionID = atomic.AddUint64(&serverConnectionCounter, 1)
t.framer.writer.Flush() t.framer.writer.Flush()
defer func() { defer func() {
@ -277,7 +278,7 @@ func newHTTP2Server(conn net.Conn, config *ServerConfig) (_ ServerTransport, err
if err != nil { if err != nil {
return nil, connectionErrorf(false, err, "transport: http2Server.HandleStreams failed to read initial settings frame: %v", err) return nil, connectionErrorf(false, err, "transport: http2Server.HandleStreams failed to read initial settings frame: %v", err)
} }
atomic.StoreUint32(&t.activity, 1) atomic.StoreInt64(&t.lastRead, time.Now().UnixNano())
sf, ok := frame.(*http2.SettingsFrame) sf, ok := frame.(*http2.SettingsFrame)
if !ok { if !ok {
return nil, connectionErrorf(false, nil, "transport: http2Server.HandleStreams saw invalid preface type %T from client", frame) return nil, connectionErrorf(false, nil, "transport: http2Server.HandleStreams saw invalid preface type %T from client", frame)
@ -416,6 +417,7 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(
LocalAddr: t.localAddr, LocalAddr: t.localAddr,
Compression: s.recvCompress, Compression: s.recvCompress,
WireLength: int(frame.Header().Length), WireLength: int(frame.Header().Length),
Header: metadata.MD(state.data.mdata).Copy(),
} }
t.stats.HandleRPC(s.ctx, inHeader) t.stats.HandleRPC(s.ctx, inHeader)
} }
@ -449,7 +451,7 @@ func (t *http2Server) HandleStreams(handle func(*Stream), traceCtx func(context.
for { for {
t.controlBuf.throttle() t.controlBuf.throttle()
frame, err := t.framer.fr.ReadFrame() frame, err := t.framer.fr.ReadFrame()
atomic.StoreUint32(&t.activity, 1) atomic.StoreInt64(&t.lastRead, time.Now().UnixNano())
if err != nil { if err != nil {
if se, ok := err.(http2.StreamError); ok { if se, ok := err.(http2.StreamError); ok {
warningf("transport: http2Server.HandleStreams encountered http2.StreamError: %v", se) warningf("transport: http2Server.HandleStreams encountered http2.StreamError: %v", se)
@ -806,9 +808,12 @@ func (t *http2Server) writeHeaderLocked(s *Stream) error {
return ErrHeaderListSizeLimitViolation return ErrHeaderListSizeLimitViolation
} }
if t.stats != nil { if t.stats != nil {
// Note: WireLength is not set in outHeader. // Note: Headers are compressed with hpack after this call returns.
// TODO(mmukhi): Revisit this later, if needed. // No WireLength field is set here.
outHeader := &stats.OutHeader{} outHeader := &stats.OutHeader{
Header: s.header.Copy(),
Compression: s.sendCompress,
}
t.stats.HandleRPC(s.Context(), outHeader) t.stats.HandleRPC(s.Context(), outHeader)
} }
return nil return nil
@ -840,7 +845,7 @@ func (t *http2Server) WriteStatus(s *Stream, st *status.Status) error {
headerFields = append(headerFields, hpack.HeaderField{Name: "grpc-status", Value: strconv.Itoa(int(st.Code()))}) headerFields = append(headerFields, hpack.HeaderField{Name: "grpc-status", Value: strconv.Itoa(int(st.Code()))})
headerFields = append(headerFields, hpack.HeaderField{Name: "grpc-message", Value: encodeGrpcMessage(st.Message())}) headerFields = append(headerFields, hpack.HeaderField{Name: "grpc-message", Value: encodeGrpcMessage(st.Message())})
if p := statusRawProto(st); p != nil && len(p.Details) > 0 { if p := st.Proto(); p != nil && len(p.Details) > 0 {
stBytes, err := proto.Marshal(p) stBytes, err := proto.Marshal(p)
if err != nil { if err != nil {
// TODO: return error instead, when callers are able to handle it. // TODO: return error instead, when callers are able to handle it.
@ -871,7 +876,11 @@ func (t *http2Server) WriteStatus(s *Stream, st *status.Status) error {
rst := s.getState() == streamActive rst := s.getState() == streamActive
t.finishStream(s, rst, http2.ErrCodeNo, trailingHeader, true) t.finishStream(s, rst, http2.ErrCodeNo, trailingHeader, true)
if t.stats != nil { if t.stats != nil {
t.stats.HandleRPC(s.Context(), &stats.OutTrailer{}) // Note: The trailer fields are compressed with hpack after this call returns.
// No WireLength field is set here.
t.stats.HandleRPC(s.Context(), &stats.OutTrailer{
Trailer: s.trailer.Copy(),
})
} }
return nil return nil
} }
@ -932,32 +941,35 @@ func (t *http2Server) Write(s *Stream, hdr []byte, data []byte, opts *Options) e
// after an additional duration of keepalive.Timeout. // after an additional duration of keepalive.Timeout.
func (t *http2Server) keepalive() { func (t *http2Server) keepalive() {
p := &ping{} p := &ping{}
var pingSent bool // True iff a ping has been sent, and no data has been received since then.
maxIdle := time.NewTimer(t.kp.MaxConnectionIdle) outstandingPing := false
maxAge := time.NewTimer(t.kp.MaxConnectionAge) // Amount of time remaining before which we should receive an ACK for the
keepalive := time.NewTimer(t.kp.Time) // last sent ping.
// NOTE: All exit paths of this function should reset their kpTimeoutLeft := time.Duration(0)
// respective timers. A failure to do so will cause the // Records the last value of t.lastRead before we go block on the timer.
// following clean-up to deadlock and eventually leak. // This is required to check for read activity since then.
prevNano := time.Now().UnixNano()
// Initialize the different timers to their default values.
idleTimer := time.NewTimer(t.kp.MaxConnectionIdle)
ageTimer := time.NewTimer(t.kp.MaxConnectionAge)
kpTimer := time.NewTimer(t.kp.Time)
defer func() { defer func() {
if !maxIdle.Stop() { // We need to drain the underlying channel in these timers after a call
<-maxIdle.C // to Stop(), only if we are interested in resetting them. Clearly we
} // are not interested in resetting them here.
if !maxAge.Stop() { idleTimer.Stop()
<-maxAge.C ageTimer.Stop()
} kpTimer.Stop()
if !keepalive.Stop() {
<-keepalive.C
}
}() }()
for { for {
select { select {
case <-maxIdle.C: case <-idleTimer.C:
t.mu.Lock() t.mu.Lock()
idle := t.idle idle := t.idle
if idle.IsZero() { // The connection is non-idle. if idle.IsZero() { // The connection is non-idle.
t.mu.Unlock() t.mu.Unlock()
maxIdle.Reset(t.kp.MaxConnectionIdle) idleTimer.Reset(t.kp.MaxConnectionIdle)
continue continue
} }
val := t.kp.MaxConnectionIdle - time.Since(idle) val := t.kp.MaxConnectionIdle - time.Since(idle)
@ -966,43 +978,51 @@ func (t *http2Server) keepalive() {
// The connection has been idle for a duration of keepalive.MaxConnectionIdle or more. // The connection has been idle for a duration of keepalive.MaxConnectionIdle or more.
// Gracefully close the connection. // Gracefully close the connection.
t.drain(http2.ErrCodeNo, []byte{}) t.drain(http2.ErrCodeNo, []byte{})
// Resetting the timer so that the clean-up doesn't deadlock.
maxIdle.Reset(infinity)
return return
} }
maxIdle.Reset(val) idleTimer.Reset(val)
case <-maxAge.C: case <-ageTimer.C:
t.drain(http2.ErrCodeNo, []byte{}) t.drain(http2.ErrCodeNo, []byte{})
maxAge.Reset(t.kp.MaxConnectionAgeGrace) ageTimer.Reset(t.kp.MaxConnectionAgeGrace)
select { select {
case <-maxAge.C: case <-ageTimer.C:
// Close the connection after grace period. // Close the connection after grace period.
infof("transport: closing server transport due to maximum connection age.") infof("transport: closing server transport due to maximum connection age.")
t.Close() t.Close()
// Resetting the timer so that the clean-up doesn't deadlock.
maxAge.Reset(infinity)
case <-t.done: case <-t.done:
} }
return return
case <-keepalive.C: case <-kpTimer.C:
if atomic.CompareAndSwapUint32(&t.activity, 1, 0) { lastRead := atomic.LoadInt64(&t.lastRead)
pingSent = false if lastRead > prevNano {
keepalive.Reset(t.kp.Time) // There has been read activity since the last time we were
// here. Setup the timer to fire at kp.Time seconds from
// lastRead time and continue.
outstandingPing = false
kpTimer.Reset(time.Duration(lastRead) + t.kp.Time - time.Duration(time.Now().UnixNano()))
prevNano = lastRead
continue continue
} }
if pingSent { if outstandingPing && kpTimeoutLeft <= 0 {
infof("transport: closing server transport due to idleness.") infof("transport: closing server transport due to idleness.")
t.Close() t.Close()
// Resetting the timer so that the clean-up doesn't deadlock.
keepalive.Reset(infinity)
return return
} }
pingSent = true if !outstandingPing {
if channelz.IsOn() { if channelz.IsOn() {
atomic.AddInt64(&t.czData.kpCount, 1) atomic.AddInt64(&t.czData.kpCount, 1)
}
t.controlBuf.put(p)
kpTimeoutLeft = t.kp.Timeout
outstandingPing = true
} }
t.controlBuf.put(p) // The amount of time to sleep here is the minimum of kp.Time and
keepalive.Reset(t.kp.Timeout) // timeoutLeft. This will ensure that we wait only for kp.Time
// before sending out the next ping (for cases where the ping is
// acked).
sleepDuration := minTime(t.kp.Time, kpTimeoutLeft)
kpTimeoutLeft -= sleepDuration
kpTimer.Reset(sleepDuration)
case <-t.done: case <-t.done:
return return
} }

View File

@ -73,10 +73,11 @@ type recvMsg struct {
} }
// recvBuffer is an unbounded channel of recvMsg structs. // recvBuffer is an unbounded channel of recvMsg structs.
// Note recvBuffer differs from controlBuffer only in that recvBuffer //
// holds a channel of only recvMsg structs instead of objects implementing "item" interface. // Note: recvBuffer differs from buffer.Unbounded only in the fact that it
// recvBuffer is written to much more often than // holds a channel of recvMsg structs instead of objects implementing "item"
// controlBuffer and using strict recvMsg structs helps avoid allocation in "recvBuffer.put" // interface. recvBuffer is written to much more often and using strict recvMsg
// structs helps avoid allocation in "recvBuffer.put"
type recvBuffer struct { type recvBuffer struct {
c chan recvMsg c chan recvMsg
mu sync.Mutex mu sync.Mutex

View File

@ -20,6 +20,7 @@ package grpc
import ( import (
"context" "context"
"fmt"
"io" "io"
"sync" "sync"
@ -31,49 +32,78 @@ import (
"google.golang.org/grpc/status" "google.golang.org/grpc/status"
) )
// v2PickerWrapper wraps a balancer.Picker while providing the
// balancer.V2Picker API. It requires a pickerWrapper to generate errors
// including the latest connectionError. To be deleted when balancer.Picker is
// updated to the balancer.V2Picker API.
type v2PickerWrapper struct {
picker balancer.Picker
connErr *connErr
}
func (v *v2PickerWrapper) Pick(info balancer.PickInfo) (balancer.PickResult, error) {
sc, done, err := v.picker.Pick(info.Ctx, info)
if err != nil {
if err == balancer.ErrTransientFailure {
return balancer.PickResult{}, balancer.TransientFailureError(fmt.Errorf("%v, latest connection error: %v", err, v.connErr.connectionError()))
}
return balancer.PickResult{}, err
}
return balancer.PickResult{SubConn: sc, Done: done}, nil
}
// pickerWrapper is a wrapper of balancer.Picker. It blocks on certain pick // pickerWrapper is a wrapper of balancer.Picker. It blocks on certain pick
// actions and unblock when there's a picker update. // actions and unblock when there's a picker update.
type pickerWrapper struct { type pickerWrapper struct {
mu sync.Mutex mu sync.Mutex
done bool done bool
blockingCh chan struct{} blockingCh chan struct{}
picker balancer.Picker picker balancer.V2Picker
// The latest connection happened. // The latest connection error. TODO: remove when V1 picker is deprecated;
connErrMu sync.Mutex // balancer should be responsible for providing the error.
connErr error *connErr
} }
func newPickerWrapper() *pickerWrapper { type connErr struct {
bp := &pickerWrapper{blockingCh: make(chan struct{})} mu sync.Mutex
return bp err error
} }
func (bp *pickerWrapper) updateConnectionError(err error) { func (c *connErr) updateConnectionError(err error) {
bp.connErrMu.Lock() c.mu.Lock()
bp.connErr = err c.err = err
bp.connErrMu.Unlock() c.mu.Unlock()
} }
func (bp *pickerWrapper) connectionError() error { func (c *connErr) connectionError() error {
bp.connErrMu.Lock() c.mu.Lock()
err := bp.connErr err := c.err
bp.connErrMu.Unlock() c.mu.Unlock()
return err return err
} }
func newPickerWrapper() *pickerWrapper {
return &pickerWrapper{blockingCh: make(chan struct{}), connErr: &connErr{}}
}
// updatePicker is called by UpdateBalancerState. It unblocks all blocked pick. // updatePicker is called by UpdateBalancerState. It unblocks all blocked pick.
func (bp *pickerWrapper) updatePicker(p balancer.Picker) { func (pw *pickerWrapper) updatePicker(p balancer.Picker) {
bp.mu.Lock() pw.updatePickerV2(&v2PickerWrapper{picker: p, connErr: pw.connErr})
if bp.done { }
bp.mu.Unlock()
// updatePicker is called by UpdateBalancerState. It unblocks all blocked pick.
func (pw *pickerWrapper) updatePickerV2(p balancer.V2Picker) {
pw.mu.Lock()
if pw.done {
pw.mu.Unlock()
return return
} }
bp.picker = p pw.picker = p
// bp.blockingCh should never be nil. // pw.blockingCh should never be nil.
close(bp.blockingCh) close(pw.blockingCh)
bp.blockingCh = make(chan struct{}) pw.blockingCh = make(chan struct{})
bp.mu.Unlock() pw.mu.Unlock()
} }
func doneChannelzWrapper(acw *acBalancerWrapper, done func(balancer.DoneInfo)) func(balancer.DoneInfo) { func doneChannelzWrapper(acw *acBalancerWrapper, done func(balancer.DoneInfo)) func(balancer.DoneInfo) {
@ -100,83 +130,85 @@ func doneChannelzWrapper(acw *acBalancerWrapper, done func(balancer.DoneInfo)) f
// - the current picker returns other errors and failfast is false. // - the current picker returns other errors and failfast is false.
// - the subConn returned by the current picker is not READY // - the subConn returned by the current picker is not READY
// When one of these situations happens, pick blocks until the picker gets updated. // When one of these situations happens, pick blocks until the picker gets updated.
func (bp *pickerWrapper) pick(ctx context.Context, failfast bool, opts balancer.PickOptions) (transport.ClientTransport, func(balancer.DoneInfo), error) { func (pw *pickerWrapper) pick(ctx context.Context, failfast bool, info balancer.PickInfo) (transport.ClientTransport, func(balancer.DoneInfo), error) {
var ch chan struct{} var ch chan struct{}
var lastPickErr error
for { for {
bp.mu.Lock() pw.mu.Lock()
if bp.done { if pw.done {
bp.mu.Unlock() pw.mu.Unlock()
return nil, nil, ErrClientConnClosing return nil, nil, ErrClientConnClosing
} }
if bp.picker == nil { if pw.picker == nil {
ch = bp.blockingCh ch = pw.blockingCh
} }
if ch == bp.blockingCh { if ch == pw.blockingCh {
// This could happen when either: // This could happen when either:
// - bp.picker is nil (the previous if condition), or // - pw.picker is nil (the previous if condition), or
// - has called pick on the current picker. // - has called pick on the current picker.
bp.mu.Unlock() pw.mu.Unlock()
select { select {
case <-ctx.Done(): case <-ctx.Done():
if connectionErr := bp.connectionError(); connectionErr != nil { var errStr string
switch ctx.Err() { if lastPickErr != nil {
case context.DeadlineExceeded: errStr = "latest balancer error: " + lastPickErr.Error()
return nil, nil, status.Errorf(codes.DeadlineExceeded, "latest connection error: %v", connectionErr) } else if connectionErr := pw.connectionError(); connectionErr != nil {
case context.Canceled: errStr = "latest connection error: " + connectionErr.Error()
return nil, nil, status.Errorf(codes.Canceled, "latest connection error: %v", connectionErr) } else {
} errStr = ctx.Err().Error()
}
switch ctx.Err() {
case context.DeadlineExceeded:
return nil, nil, status.Error(codes.DeadlineExceeded, errStr)
case context.Canceled:
return nil, nil, status.Error(codes.Canceled, errStr)
} }
return nil, nil, ctx.Err()
case <-ch: case <-ch:
} }
continue continue
} }
ch = bp.blockingCh ch = pw.blockingCh
p := bp.picker p := pw.picker
bp.mu.Unlock() pw.mu.Unlock()
subConn, done, err := p.Pick(ctx, opts) pickResult, err := p.Pick(info)
if err != nil { if err != nil {
switch err { if err == balancer.ErrNoSubConnAvailable {
case balancer.ErrNoSubConnAvailable:
continue continue
case balancer.ErrTransientFailure: }
if tfe, ok := err.(interface{ IsTransientFailure() bool }); ok && tfe.IsTransientFailure() {
if !failfast { if !failfast {
lastPickErr = err
continue continue
} }
return nil, nil, status.Errorf(codes.Unavailable, "%v, latest connection error: %v", err, bp.connectionError()) return nil, nil, status.Error(codes.Unavailable, err.Error())
case context.DeadlineExceeded:
return nil, nil, status.Error(codes.DeadlineExceeded, err.Error())
case context.Canceled:
return nil, nil, status.Error(codes.Canceled, err.Error())
default:
if _, ok := status.FromError(err); ok {
return nil, nil, err
}
// err is some other error.
return nil, nil, status.Error(codes.Unknown, err.Error())
} }
if _, ok := status.FromError(err); ok {
return nil, nil, err
}
// err is some other error.
return nil, nil, status.Error(codes.Unknown, err.Error())
} }
acw, ok := subConn.(*acBalancerWrapper) acw, ok := pickResult.SubConn.(*acBalancerWrapper)
if !ok { if !ok {
grpclog.Error("subconn returned from pick is not *acBalancerWrapper") grpclog.Error("subconn returned from pick is not *acBalancerWrapper")
continue continue
} }
if t, ok := acw.getAddrConn().getReadyTransport(); ok { if t, ok := acw.getAddrConn().getReadyTransport(); ok {
if channelz.IsOn() { if channelz.IsOn() {
return t, doneChannelzWrapper(acw, done), nil return t, doneChannelzWrapper(acw, pickResult.Done), nil
} }
return t, done, nil return t, pickResult.Done, nil
} }
if done != nil { if pickResult.Done != nil {
// Calling done with nil error, no bytes sent and no bytes received. // Calling done with nil error, no bytes sent and no bytes received.
// DoneInfo with default value works. // DoneInfo with default value works.
done(balancer.DoneInfo{}) pickResult.Done(balancer.DoneInfo{})
} }
grpclog.Infof("blockingPicker: the picked transport is not ready, loop back to repick") grpclog.Infof("blockingPicker: the picked transport is not ready, loop back to repick")
// If ok == false, ac.state is not READY. // If ok == false, ac.state is not READY.
@ -186,12 +218,12 @@ func (bp *pickerWrapper) pick(ctx context.Context, failfast bool, opts balancer.
} }
} }
func (bp *pickerWrapper) close() { func (pw *pickerWrapper) close() {
bp.mu.Lock() pw.mu.Lock()
defer bp.mu.Unlock() defer pw.mu.Unlock()
if bp.done { if pw.done {
return return
} }
bp.done = true pw.done = true
close(bp.blockingCh) close(pw.blockingCh)
} }

View File

@ -19,12 +19,14 @@
package grpc package grpc
import ( import (
"context" "errors"
"google.golang.org/grpc/balancer" "google.golang.org/grpc/balancer"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/connectivity" "google.golang.org/grpc/connectivity"
"google.golang.org/grpc/grpclog" "google.golang.org/grpc/grpclog"
"google.golang.org/grpc/resolver" "google.golang.org/grpc/resolver"
"google.golang.org/grpc/status"
) )
// PickFirstBalancerName is the name of the pick_first balancer. // PickFirstBalancerName is the name of the pick_first balancer.
@ -45,35 +47,67 @@ func (*pickfirstBuilder) Name() string {
} }
type pickfirstBalancer struct { type pickfirstBalancer struct {
cc balancer.ClientConn state connectivity.State
sc balancer.SubConn cc balancer.ClientConn
sc balancer.SubConn
} }
var _ balancer.V2Balancer = &pickfirstBalancer{} // Assert we implement v2
func (b *pickfirstBalancer) HandleResolvedAddrs(addrs []resolver.Address, err error) { func (b *pickfirstBalancer) HandleResolvedAddrs(addrs []resolver.Address, err error) {
if err != nil { if err != nil {
if grpclog.V(2) { b.ResolverError(err)
grpclog.Infof("pickfirstBalancer: HandleResolvedAddrs called with error %v", err)
}
return return
} }
if b.sc == nil { b.UpdateClientConnState(balancer.ClientConnState{ResolverState: resolver.State{Addresses: addrs}}) // Ignore error
b.sc, err = b.cc.NewSubConn(addrs, balancer.NewSubConnOptions{})
if err != nil {
//TODO(yuxuanli): why not change the cc state to Idle?
if grpclog.V(2) {
grpclog.Errorf("pickfirstBalancer: failed to NewSubConn: %v", err)
}
return
}
b.cc.UpdateBalancerState(connectivity.Idle, &picker{sc: b.sc})
b.sc.Connect()
} else {
b.sc.UpdateAddresses(addrs)
b.sc.Connect()
}
} }
func (b *pickfirstBalancer) HandleSubConnStateChange(sc balancer.SubConn, s connectivity.State) { func (b *pickfirstBalancer) HandleSubConnStateChange(sc balancer.SubConn, s connectivity.State) {
b.UpdateSubConnState(sc, balancer.SubConnState{ConnectivityState: s})
}
func (b *pickfirstBalancer) ResolverError(err error) {
switch b.state {
case connectivity.TransientFailure, connectivity.Idle, connectivity.Connecting:
// Set a failing picker if we don't have a good picker.
b.cc.UpdateState(balancer.State{ConnectivityState: connectivity.TransientFailure,
Picker: &picker{err: status.Errorf(codes.Unavailable, "name resolver error: %v", err)}},
)
}
if grpclog.V(2) {
grpclog.Infof("pickfirstBalancer: ResolverError called with error %v", err)
}
}
func (b *pickfirstBalancer) UpdateClientConnState(cs balancer.ClientConnState) error {
if len(cs.ResolverState.Addresses) == 0 {
b.ResolverError(errors.New("produced zero addresses"))
return balancer.ErrBadResolverState
}
if b.sc == nil {
var err error
b.sc, err = b.cc.NewSubConn(cs.ResolverState.Addresses, balancer.NewSubConnOptions{})
if err != nil {
if grpclog.V(2) {
grpclog.Errorf("pickfirstBalancer: failed to NewSubConn: %v", err)
}
b.state = connectivity.TransientFailure
b.cc.UpdateState(balancer.State{ConnectivityState: connectivity.TransientFailure,
Picker: &picker{err: status.Errorf(codes.Unavailable, "error creating connection: %v", err)}},
)
return balancer.ErrBadResolverState
}
b.state = connectivity.Idle
b.cc.UpdateState(balancer.State{ConnectivityState: connectivity.Idle, Picker: &picker{result: balancer.PickResult{SubConn: b.sc}}})
b.sc.Connect()
} else {
b.sc.UpdateAddresses(cs.ResolverState.Addresses)
b.sc.Connect()
}
return nil
}
func (b *pickfirstBalancer) UpdateSubConnState(sc balancer.SubConn, s balancer.SubConnState) {
if grpclog.V(2) { if grpclog.V(2) {
grpclog.Infof("pickfirstBalancer: HandleSubConnStateChange: %p, %v", sc, s) grpclog.Infof("pickfirstBalancer: HandleSubConnStateChange: %p, %v", sc, s)
} }
@ -83,18 +117,28 @@ func (b *pickfirstBalancer) HandleSubConnStateChange(sc balancer.SubConn, s conn
} }
return return
} }
if s == connectivity.Shutdown { b.state = s.ConnectivityState
if s.ConnectivityState == connectivity.Shutdown {
b.sc = nil b.sc = nil
return return
} }
switch s { switch s.ConnectivityState {
case connectivity.Ready, connectivity.Idle: case connectivity.Ready, connectivity.Idle:
b.cc.UpdateBalancerState(s, &picker{sc: sc}) b.cc.UpdateState(balancer.State{ConnectivityState: s.ConnectivityState, Picker: &picker{result: balancer.PickResult{SubConn: sc}}})
case connectivity.Connecting: case connectivity.Connecting:
b.cc.UpdateBalancerState(s, &picker{err: balancer.ErrNoSubConnAvailable}) b.cc.UpdateState(balancer.State{ConnectivityState: s.ConnectivityState, Picker: &picker{err: balancer.ErrNoSubConnAvailable}})
case connectivity.TransientFailure: case connectivity.TransientFailure:
b.cc.UpdateBalancerState(s, &picker{err: balancer.ErrTransientFailure}) err := balancer.ErrTransientFailure
// TODO: this can be unconditional after the V1 API is removed, as
// SubConnState will always contain a connection error.
if s.ConnectionError != nil {
err = balancer.TransientFailureError(s.ConnectionError)
}
b.cc.UpdateState(balancer.State{
ConnectivityState: s.ConnectivityState,
Picker: &picker{err: err},
})
} }
} }
@ -102,15 +146,12 @@ func (b *pickfirstBalancer) Close() {
} }
type picker struct { type picker struct {
err error result balancer.PickResult
sc balancer.SubConn err error
} }
func (p *picker) Pick(ctx context.Context, opts balancer.PickOptions) (balancer.SubConn, func(balancer.DoneInfo), error) { func (p *picker) Pick(info balancer.PickInfo) (balancer.PickResult, error) {
if p.err != nil { return p.result, p.err
return nil, nil, p.err
}
return p.sc, nil, nil
} }
func init() { func init() {

View File

@ -24,6 +24,7 @@ import (
"context" "context"
"net" "net"
"google.golang.org/grpc/attributes"
"google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials"
"google.golang.org/grpc/serviceconfig" "google.golang.org/grpc/serviceconfig"
) )
@ -73,12 +74,18 @@ func GetDefaultScheme() string {
} }
// AddressType indicates the address type returned by name resolution. // AddressType indicates the address type returned by name resolution.
//
// Deprecated: use Attributes in Address instead.
type AddressType uint8 type AddressType uint8
const ( const (
// Backend indicates the address is for a backend server. // Backend indicates the address is for a backend server.
//
// Deprecated: use Attributes in Address instead.
Backend AddressType = iota Backend AddressType = iota
// GRPCLB indicates the address is for a grpclb load balancer. // GRPCLB indicates the address is for a grpclb load balancer.
//
// Deprecated: use Attributes in Address instead.
GRPCLB GRPCLB
) )
@ -87,8 +94,7 @@ const (
type Address struct { type Address struct {
// Addr is the server address on which a connection will be established. // Addr is the server address on which a connection will be established.
Addr string Addr string
// Type is the type of this address.
Type AddressType
// ServerName is the name of this address. // ServerName is the name of this address.
// If non-empty, the ServerName is used as the transport certification authority for // If non-empty, the ServerName is used as the transport certification authority for
// the address, instead of the hostname from the Dial target string. In most cases, // the address, instead of the hostname from the Dial target string. In most cases,
@ -101,14 +107,26 @@ type Address struct {
// is insecure to populate it with data from untrusted inputs since untrusted // is insecure to populate it with data from untrusted inputs since untrusted
// values could be used to bypass the authority checks performed by TLS. // values could be used to bypass the authority checks performed by TLS.
ServerName string ServerName string
// Attributes contains arbitrary data about this address intended for
// consumption by the load balancing policy.
Attributes *attributes.Attributes
// Type is the type of this address.
//
// Deprecated: use Attributes instead.
Type AddressType
// Metadata is the information associated with Addr, which may be used // Metadata is the information associated with Addr, which may be used
// to make load balancing decision. // to make load balancing decision.
//
// Deprecated: use Attributes instead.
Metadata interface{} Metadata interface{}
} }
// BuildOption includes additional information for the builder to create // BuildOptions includes additional information for the builder to create
// the resolver. // the resolver.
type BuildOption struct { type BuildOptions struct {
// DisableServiceConfig indicates whether a resolver implementation should // DisableServiceConfig indicates whether a resolver implementation should
// fetch service config data. // fetch service config data.
DisableServiceConfig bool DisableServiceConfig bool
@ -141,6 +159,10 @@ type State struct {
// config. If it is nil, it indicates no service config is present or the // config. If it is nil, it indicates no service config is present or the
// resolver does not provide service configs. // resolver does not provide service configs.
ServiceConfig *serviceconfig.ParseResult ServiceConfig *serviceconfig.ParseResult
// Attributes contains arbitrary data about the resolver intended for
// consumption by the load balancing policy.
Attributes *attributes.Attributes
} }
// ClientConn contains the callbacks for resolver to notify any updates // ClientConn contains the callbacks for resolver to notify any updates
@ -202,14 +224,14 @@ type Builder interface {
// //
// gRPC dial calls Build synchronously, and fails if the returned error is // gRPC dial calls Build synchronously, and fails if the returned error is
// not nil. // not nil.
Build(target Target, cc ClientConn, opts BuildOption) (Resolver, error) Build(target Target, cc ClientConn, opts BuildOptions) (Resolver, error)
// Scheme returns the scheme supported by this resolver. // Scheme returns the scheme supported by this resolver.
// Scheme is defined at https://github.com/grpc/grpc/blob/master/doc/naming.md. // Scheme is defined at https://github.com/grpc/grpc/blob/master/doc/naming.md.
Scheme() string Scheme() string
} }
// ResolveNowOption includes additional information for ResolveNow. // ResolveNowOptions includes additional information for ResolveNow.
type ResolveNowOption struct{} type ResolveNowOptions struct{}
// Resolver watches for the updates on the specified target. // Resolver watches for the updates on the specified target.
// Updates include address updates and service config updates. // Updates include address updates and service config updates.
@ -218,7 +240,7 @@ type Resolver interface {
// again. It's just a hint, resolver can ignore this if it's not necessary. // again. It's just a hint, resolver can ignore this if it's not necessary.
// //
// It could be called multiple times concurrently. // It could be called multiple times concurrently.
ResolveNow(ResolveNowOption) ResolveNow(ResolveNowOptions)
// Close closes the resolver. // Close closes the resolver.
Close() Close()
} }

View File

@ -26,7 +26,6 @@ import (
"google.golang.org/grpc/balancer" "google.golang.org/grpc/balancer"
"google.golang.org/grpc/credentials" "google.golang.org/grpc/credentials"
"google.golang.org/grpc/grpclog"
"google.golang.org/grpc/internal/channelz" "google.golang.org/grpc/internal/channelz"
"google.golang.org/grpc/internal/grpcsync" "google.golang.org/grpc/internal/grpcsync"
"google.golang.org/grpc/resolver" "google.golang.org/grpc/resolver"
@ -34,7 +33,7 @@ import (
) )
// ccResolverWrapper is a wrapper on top of cc for resolvers. // ccResolverWrapper is a wrapper on top of cc for resolvers.
// It implements resolver.ClientConnection interface. // It implements resolver.ClientConn interface.
type ccResolverWrapper struct { type ccResolverWrapper struct {
cc *ClientConn cc *ClientConn
resolverMu sync.Mutex resolverMu sync.Mutex
@ -46,43 +45,9 @@ type ccResolverWrapper struct {
polling chan struct{} polling chan struct{}
} }
// split2 returns the values from strings.SplitN(s, sep, 2). // newCCResolverWrapper uses the resolver.Builder to build a Resolver and
// If sep is not found, it returns ("", "", false) instead. // returns a ccResolverWrapper object which wraps the newly built resolver.
func split2(s, sep string) (string, string, bool) { func newCCResolverWrapper(cc *ClientConn, rb resolver.Builder) (*ccResolverWrapper, error) {
spl := strings.SplitN(s, sep, 2)
if len(spl) < 2 {
return "", "", false
}
return spl[0], spl[1], true
}
// parseTarget splits target into a struct containing scheme, authority and
// endpoint.
//
// If target is not a valid scheme://authority/endpoint, it returns {Endpoint:
// target}.
func parseTarget(target string) (ret resolver.Target) {
var ok bool
ret.Scheme, ret.Endpoint, ok = split2(target, "://")
if !ok {
return resolver.Target{Endpoint: target}
}
ret.Authority, ret.Endpoint, ok = split2(ret.Endpoint, "/")
if !ok {
return resolver.Target{Endpoint: target}
}
return ret
}
// newCCResolverWrapper uses the resolver.Builder stored in the ClientConn to
// build a Resolver and returns a ccResolverWrapper object which wraps the
// newly built resolver.
func newCCResolverWrapper(cc *ClientConn) (*ccResolverWrapper, error) {
rb := cc.dopts.resolverBuilder
if rb == nil {
return nil, fmt.Errorf("could not get resolver for scheme: %q", cc.parsedTarget.Scheme)
}
ccr := &ccResolverWrapper{ ccr := &ccResolverWrapper{
cc: cc, cc: cc,
done: grpcsync.NewEvent(), done: grpcsync.NewEvent(),
@ -92,7 +57,7 @@ func newCCResolverWrapper(cc *ClientConn) (*ccResolverWrapper, error) {
if creds := cc.dopts.copts.TransportCredentials; creds != nil { if creds := cc.dopts.copts.TransportCredentials; creds != nil {
credsClone = creds.Clone() credsClone = creds.Clone()
} }
rbo := resolver.BuildOption{ rbo := resolver.BuildOptions{
DisableServiceConfig: cc.dopts.disableServiceConfig, DisableServiceConfig: cc.dopts.disableServiceConfig,
DialCreds: credsClone, DialCreds: credsClone,
CredsBundle: cc.dopts.copts.CredsBundle, CredsBundle: cc.dopts.copts.CredsBundle,
@ -105,15 +70,15 @@ func newCCResolverWrapper(cc *ClientConn) (*ccResolverWrapper, error) {
// rb.Build-->ccr.ReportError-->ccr.poll-->ccr.resolveNow, would end up // rb.Build-->ccr.ReportError-->ccr.poll-->ccr.resolveNow, would end up
// accessing ccr.resolver which is being assigned here. // accessing ccr.resolver which is being assigned here.
ccr.resolverMu.Lock() ccr.resolverMu.Lock()
defer ccr.resolverMu.Unlock()
ccr.resolver, err = rb.Build(cc.parsedTarget, ccr, rbo) ccr.resolver, err = rb.Build(cc.parsedTarget, ccr, rbo)
if err != nil { if err != nil {
return nil, err return nil, err
} }
ccr.resolverMu.Unlock()
return ccr, nil return ccr, nil
} }
func (ccr *ccResolverWrapper) resolveNow(o resolver.ResolveNowOption) { func (ccr *ccResolverWrapper) resolveNow(o resolver.ResolveNowOptions) {
ccr.resolverMu.Lock() ccr.resolverMu.Lock()
if !ccr.done.HasFired() { if !ccr.done.HasFired() {
ccr.resolver.ResolveNow(o) ccr.resolver.ResolveNow(o)
@ -149,7 +114,7 @@ func (ccr *ccResolverWrapper) poll(err error) {
ccr.polling = p ccr.polling = p
go func() { go func() {
for i := 0; ; i++ { for i := 0; ; i++ {
ccr.resolveNow(resolver.ResolveNowOption{}) ccr.resolveNow(resolver.ResolveNowOptions{})
t := time.NewTimer(ccr.cc.dopts.resolveNowBackoff(i)) t := time.NewTimer(ccr.cc.dopts.resolveNowBackoff(i))
select { select {
case <-p: case <-p:
@ -175,7 +140,7 @@ func (ccr *ccResolverWrapper) UpdateState(s resolver.State) {
if ccr.done.HasFired() { if ccr.done.HasFired() {
return return
} }
grpclog.Infof("ccResolverWrapper: sending update to cc: %v", s) channelz.Infof(ccr.cc.channelzID, "ccResolverWrapper: sending update to cc: %v", s)
if channelz.IsOn() { if channelz.IsOn() {
ccr.addChannelzTraceEvent(s) ccr.addChannelzTraceEvent(s)
} }
@ -187,13 +152,7 @@ func (ccr *ccResolverWrapper) ReportError(err error) {
if ccr.done.HasFired() { if ccr.done.HasFired() {
return return
} }
grpclog.Warningf("ccResolverWrapper: reporting error to cc: %v", err) channelz.Warningf(ccr.cc.channelzID, "ccResolverWrapper: reporting error to cc: %v", err)
if channelz.IsOn() {
channelz.AddTraceEvent(ccr.cc.channelzID, &channelz.TraceEventDesc{
Desc: fmt.Sprintf("Resolver reported error: %v", err),
Severity: channelz.CtWarning,
})
}
ccr.poll(ccr.cc.updateResolverState(resolver.State{}, err)) ccr.poll(ccr.cc.updateResolverState(resolver.State{}, err))
} }
@ -202,7 +161,7 @@ func (ccr *ccResolverWrapper) NewAddress(addrs []resolver.Address) {
if ccr.done.HasFired() { if ccr.done.HasFired() {
return return
} }
grpclog.Infof("ccResolverWrapper: sending new addresses to cc: %v", addrs) channelz.Infof(ccr.cc.channelzID, "ccResolverWrapper: sending new addresses to cc: %v", addrs)
if channelz.IsOn() { if channelz.IsOn() {
ccr.addChannelzTraceEvent(resolver.State{Addresses: addrs, ServiceConfig: ccr.curState.ServiceConfig}) ccr.addChannelzTraceEvent(resolver.State{Addresses: addrs, ServiceConfig: ccr.curState.ServiceConfig})
} }
@ -216,16 +175,14 @@ func (ccr *ccResolverWrapper) NewServiceConfig(sc string) {
if ccr.done.HasFired() { if ccr.done.HasFired() {
return return
} }
grpclog.Infof("ccResolverWrapper: got new service config: %v", sc) channelz.Infof(ccr.cc.channelzID, "ccResolverWrapper: got new service config: %v", sc)
if ccr.cc.dopts.disableServiceConfig {
channelz.Info(ccr.cc.channelzID, "Service config lookups disabled; ignoring config")
return
}
scpr := parseServiceConfig(sc) scpr := parseServiceConfig(sc)
if scpr.Err != nil { if scpr.Err != nil {
grpclog.Warningf("ccResolverWrapper: error parsing service config: %v", scpr.Err) channelz.Warningf(ccr.cc.channelzID, "ccResolverWrapper: error parsing service config: %v", scpr.Err)
if channelz.IsOn() {
channelz.AddTraceEvent(ccr.cc.channelzID, &channelz.TraceEventDesc{
Desc: fmt.Sprintf("Error parsing service config: %v", scpr.Err),
Severity: channelz.CtWarning,
})
}
ccr.poll(balancer.ErrBadResolverState) ccr.poll(balancer.ErrBadResolverState)
return return
} }
@ -258,7 +215,7 @@ func (ccr *ccResolverWrapper) addChannelzTraceEvent(s resolver.State) {
} else if len(ccr.curState.Addresses) == 0 && len(s.Addresses) > 0 { } else if len(ccr.curState.Addresses) == 0 && len(s.Addresses) > 0 {
updates = append(updates, "resolver returned new addresses") updates = append(updates, "resolver returned new addresses")
} }
channelz.AddTraceEvent(ccr.cc.channelzID, &channelz.TraceEventDesc{ channelz.AddTraceEvent(ccr.cc.channelzID, 0, &channelz.TraceEventDesc{
Desc: fmt.Sprintf("Resolver state updated: %+v (%v)", s, strings.Join(updates, "; ")), Desc: fmt.Sprintf("Resolver state updated: %+v (%v)", s, strings.Join(updates, "; ")),
Severity: channelz.CtINFO, Severity: channelz.CtINFO,
}) })

View File

@ -287,13 +287,14 @@ func (o FailFastCallOption) before(c *callInfo) error {
} }
func (o FailFastCallOption) after(c *callInfo) {} func (o FailFastCallOption) after(c *callInfo) {}
// MaxCallRecvMsgSize returns a CallOption which sets the maximum message size the client can receive. // MaxCallRecvMsgSize returns a CallOption which sets the maximum message size
func MaxCallRecvMsgSize(s int) CallOption { // in bytes the client can receive.
return MaxRecvMsgSizeCallOption{MaxRecvMsgSize: s} func MaxCallRecvMsgSize(bytes int) CallOption {
return MaxRecvMsgSizeCallOption{MaxRecvMsgSize: bytes}
} }
// MaxRecvMsgSizeCallOption is a CallOption that indicates the maximum message // MaxRecvMsgSizeCallOption is a CallOption that indicates the maximum message
// size the client can receive. // size in bytes the client can receive.
// This is an EXPERIMENTAL API. // This is an EXPERIMENTAL API.
type MaxRecvMsgSizeCallOption struct { type MaxRecvMsgSizeCallOption struct {
MaxRecvMsgSize int MaxRecvMsgSize int
@ -305,13 +306,14 @@ func (o MaxRecvMsgSizeCallOption) before(c *callInfo) error {
} }
func (o MaxRecvMsgSizeCallOption) after(c *callInfo) {} func (o MaxRecvMsgSizeCallOption) after(c *callInfo) {}
// MaxCallSendMsgSize returns a CallOption which sets the maximum message size the client can send. // MaxCallSendMsgSize returns a CallOption which sets the maximum message size
func MaxCallSendMsgSize(s int) CallOption { // in bytes the client can send.
return MaxSendMsgSizeCallOption{MaxSendMsgSize: s} func MaxCallSendMsgSize(bytes int) CallOption {
return MaxSendMsgSizeCallOption{MaxSendMsgSize: bytes}
} }
// MaxSendMsgSizeCallOption is a CallOption that indicates the maximum message // MaxSendMsgSizeCallOption is a CallOption that indicates the maximum message
// size the client can send. // size in bytes the client can send.
// This is an EXPERIMENTAL API. // This is an EXPERIMENTAL API.
type MaxSendMsgSizeCallOption struct { type MaxSendMsgSizeCallOption struct {
MaxSendMsgSize int MaxSendMsgSize int
@ -871,7 +873,7 @@ type channelzData struct {
// The SupportPackageIsVersion variables are referenced from generated protocol // The SupportPackageIsVersion variables are referenced from generated protocol
// buffer files to ensure compatibility with the gRPC version used. The latest // buffer files to ensure compatibility with the gRPC version used. The latest
// support package version is 5. // support package version is 6.
// //
// Older versions are kept for compatibility. They may be removed if // Older versions are kept for compatibility. They may be removed if
// compatibility cannot be maintained. // compatibility cannot be maintained.
@ -881,6 +883,7 @@ const (
SupportPackageIsVersion3 = true SupportPackageIsVersion3 = true
SupportPackageIsVersion4 = true SupportPackageIsVersion4 = true
SupportPackageIsVersion5 = true SupportPackageIsVersion5 = true
SupportPackageIsVersion6 = true
) )
const grpcUA = "grpc-go/" + Version const grpcUA = "grpc-go/" + Version

View File

@ -116,6 +116,8 @@ type serverOptions struct {
dc Decompressor dc Decompressor
unaryInt UnaryServerInterceptor unaryInt UnaryServerInterceptor
streamInt StreamServerInterceptor streamInt StreamServerInterceptor
chainUnaryInts []UnaryServerInterceptor
chainStreamInts []StreamServerInterceptor
inTapHandle tap.ServerInHandle inTapHandle tap.ServerInHandle
statsHandler stats.Handler statsHandler stats.Handler
maxConcurrentStreams uint32 maxConcurrentStreams uint32
@ -311,6 +313,16 @@ func UnaryInterceptor(i UnaryServerInterceptor) ServerOption {
}) })
} }
// ChainUnaryInterceptor returns a ServerOption that specifies the chained interceptor
// for unary RPCs. The first interceptor will be the outer most,
// while the last interceptor will be the inner most wrapper around the real call.
// All unary interceptors added by this method will be chained.
func ChainUnaryInterceptor(interceptors ...UnaryServerInterceptor) ServerOption {
return newFuncServerOption(func(o *serverOptions) {
o.chainUnaryInts = append(o.chainUnaryInts, interceptors...)
})
}
// StreamInterceptor returns a ServerOption that sets the StreamServerInterceptor for the // StreamInterceptor returns a ServerOption that sets the StreamServerInterceptor for the
// server. Only one stream interceptor can be installed. // server. Only one stream interceptor can be installed.
func StreamInterceptor(i StreamServerInterceptor) ServerOption { func StreamInterceptor(i StreamServerInterceptor) ServerOption {
@ -322,6 +334,16 @@ func StreamInterceptor(i StreamServerInterceptor) ServerOption {
}) })
} }
// ChainStreamInterceptor returns a ServerOption that specifies the chained interceptor
// for stream RPCs. The first interceptor will be the outer most,
// while the last interceptor will be the inner most wrapper around the real call.
// All stream interceptors added by this method will be chained.
func ChainStreamInterceptor(interceptors ...StreamServerInterceptor) ServerOption {
return newFuncServerOption(func(o *serverOptions) {
o.chainStreamInts = append(o.chainStreamInts, interceptors...)
})
}
// InTapHandle returns a ServerOption that sets the tap handle for all the server // InTapHandle returns a ServerOption that sets the tap handle for all the server
// transport to be created. Only one can be installed. // transport to be created. Only one can be installed.
func InTapHandle(h tap.ServerInHandle) ServerOption { func InTapHandle(h tap.ServerInHandle) ServerOption {
@ -344,8 +366,8 @@ func StatsHandler(h stats.Handler) ServerOption {
// unknown service handler. The provided method is a bidi-streaming RPC service // unknown service handler. The provided method is a bidi-streaming RPC service
// handler that will be invoked instead of returning the "unimplemented" gRPC // handler that will be invoked instead of returning the "unimplemented" gRPC
// error whenever a request is received for an unregistered service or method. // error whenever a request is received for an unregistered service or method.
// The handling function has full access to the Context of the request and the // The handling function and stream interceptor (if set) have full access to
// stream, and the invocation bypasses interceptors. // the ServerStream, including its Context.
func UnknownServiceHandler(streamHandler StreamHandler) ServerOption { func UnknownServiceHandler(streamHandler StreamHandler) ServerOption {
return newFuncServerOption(func(o *serverOptions) { return newFuncServerOption(func(o *serverOptions) {
o.unknownStreamDesc = &StreamDesc{ o.unknownStreamDesc = &StreamDesc{
@ -404,6 +426,8 @@ func NewServer(opt ...ServerOption) *Server {
done: grpcsync.NewEvent(), done: grpcsync.NewEvent(),
czData: new(channelzData), czData: new(channelzData),
} }
chainUnaryServerInterceptors(s)
chainStreamServerInterceptors(s)
s.cv = sync.NewCond(&s.mu) s.cv = sync.NewCond(&s.mu)
if EnableTracing { if EnableTracing {
_, file, line, _ := runtime.Caller(1) _, file, line, _ := runtime.Caller(1)
@ -658,7 +682,7 @@ func (s *Server) handleRawConn(rawConn net.Conn) {
s.mu.Lock() s.mu.Lock()
s.errorf("ServerHandshake(%q) failed: %v", rawConn.RemoteAddr(), err) s.errorf("ServerHandshake(%q) failed: %v", rawConn.RemoteAddr(), err)
s.mu.Unlock() s.mu.Unlock()
grpclog.Warningf("grpc: Server.Serve failed to complete security handshake from %q: %v", rawConn.RemoteAddr(), err) channelz.Warningf(s.channelzID, "grpc: Server.Serve failed to complete security handshake from %q: %v", rawConn.RemoteAddr(), err)
rawConn.Close() rawConn.Close()
} }
rawConn.SetDeadline(time.Time{}) rawConn.SetDeadline(time.Time{})
@ -705,7 +729,7 @@ func (s *Server) newHTTP2Transport(c net.Conn, authInfo credentials.AuthInfo) tr
s.errorf("NewServerTransport(%q) failed: %v", c.RemoteAddr(), err) s.errorf("NewServerTransport(%q) failed: %v", c.RemoteAddr(), err)
s.mu.Unlock() s.mu.Unlock()
c.Close() c.Close()
grpclog.Warningln("grpc: Server.Serve failed to create ServerTransport: ", err) channelz.Warning(s.channelzID, "grpc: Server.Serve failed to create ServerTransport: ", err)
return nil return nil
} }
@ -844,12 +868,12 @@ func (s *Server) incrCallsFailed() {
func (s *Server) sendResponse(t transport.ServerTransport, stream *transport.Stream, msg interface{}, cp Compressor, opts *transport.Options, comp encoding.Compressor) error { func (s *Server) sendResponse(t transport.ServerTransport, stream *transport.Stream, msg interface{}, cp Compressor, opts *transport.Options, comp encoding.Compressor) error {
data, err := encode(s.getCodec(stream.ContentSubtype()), msg) data, err := encode(s.getCodec(stream.ContentSubtype()), msg)
if err != nil { if err != nil {
grpclog.Errorln("grpc: server failed to encode response: ", err) channelz.Error(s.channelzID, "grpc: server failed to encode response: ", err)
return err return err
} }
compData, err := compress(data, cp, comp) compData, err := compress(data, cp, comp)
if err != nil { if err != nil {
grpclog.Errorln("grpc: server failed to compress response: ", err) channelz.Error(s.channelzID, "grpc: server failed to compress response: ", err)
return err return err
} }
hdr, payload := msgHeader(data, compData) hdr, payload := msgHeader(data, compData)
@ -864,42 +888,93 @@ func (s *Server) sendResponse(t transport.ServerTransport, stream *transport.Str
return err return err
} }
func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.Stream, srv *service, md *MethodDesc, trInfo *traceInfo) (err error) { // chainUnaryServerInterceptors chains all unary server interceptors into one.
if channelz.IsOn() { func chainUnaryServerInterceptors(s *Server) {
s.incrCallsStarted() // Prepend opts.unaryInt to the chaining interceptors if it exists, since unaryInt will
defer func() { // be executed before any other chained interceptors.
if err != nil && err != io.EOF { interceptors := s.opts.chainUnaryInts
s.incrCallsFailed() if s.opts.unaryInt != nil {
} else { interceptors = append([]UnaryServerInterceptor{s.opts.unaryInt}, s.opts.chainUnaryInts...)
s.incrCallsSucceeded()
}
}()
} }
sh := s.opts.statsHandler
if sh != nil { var chainedInt UnaryServerInterceptor
beginTime := time.Now() if len(interceptors) == 0 {
begin := &stats.Begin{ chainedInt = nil
BeginTime: beginTime, } else if len(interceptors) == 1 {
chainedInt = interceptors[0]
} else {
chainedInt = func(ctx context.Context, req interface{}, info *UnaryServerInfo, handler UnaryHandler) (interface{}, error) {
return interceptors[0](ctx, req, info, getChainUnaryHandler(interceptors, 0, info, handler))
} }
sh.HandleRPC(stream.Context(), begin)
defer func() {
end := &stats.End{
BeginTime: beginTime,
EndTime: time.Now(),
}
if err != nil && err != io.EOF {
end.Error = toRPCErr(err)
}
sh.HandleRPC(stream.Context(), end)
}()
} }
if trInfo != nil {
defer trInfo.tr.Finish() s.opts.unaryInt = chainedInt
trInfo.tr.LazyLog(&trInfo.firstLine, false) }
// getChainUnaryHandler recursively generate the chained UnaryHandler
func getChainUnaryHandler(interceptors []UnaryServerInterceptor, curr int, info *UnaryServerInfo, finalHandler UnaryHandler) UnaryHandler {
if curr == len(interceptors)-1 {
return finalHandler
}
return func(ctx context.Context, req interface{}) (interface{}, error) {
return interceptors[curr+1](ctx, req, info, getChainUnaryHandler(interceptors, curr+1, info, finalHandler))
}
}
func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.Stream, srv *service, md *MethodDesc, trInfo *traceInfo) (err error) {
sh := s.opts.statsHandler
if sh != nil || trInfo != nil || channelz.IsOn() {
if channelz.IsOn() {
s.incrCallsStarted()
}
var statsBegin *stats.Begin
if sh != nil {
beginTime := time.Now()
statsBegin = &stats.Begin{
BeginTime: beginTime,
}
sh.HandleRPC(stream.Context(), statsBegin)
}
if trInfo != nil {
trInfo.tr.LazyLog(&trInfo.firstLine, false)
}
// The deferred error handling for tracing, stats handler and channelz are
// combined into one function to reduce stack usage -- a defer takes ~56-64
// bytes on the stack, so overflowing the stack will require a stack
// re-allocation, which is expensive.
//
// To maintain behavior similar to separate deferred statements, statements
// should be executed in the reverse order. That is, tracing first, stats
// handler second, and channelz last. Note that panics *within* defers will
// lead to different behavior, but that's an acceptable compromise; that
// would be undefined behavior territory anyway.
defer func() { defer func() {
if err != nil && err != io.EOF { if trInfo != nil {
trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true) if err != nil && err != io.EOF {
trInfo.tr.SetError() trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true)
trInfo.tr.SetError()
}
trInfo.tr.Finish()
}
if sh != nil {
end := &stats.End{
BeginTime: statsBegin.BeginTime,
EndTime: time.Now(),
}
if err != nil && err != io.EOF {
end.Error = toRPCErr(err)
}
sh.HandleRPC(stream.Context(), end)
}
if channelz.IsOn() {
if err != nil && err != io.EOF {
s.incrCallsFailed()
} else {
s.incrCallsSucceeded()
}
} }
}() }()
} }
@ -972,7 +1047,7 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.
if err != nil { if err != nil {
if st, ok := status.FromError(err); ok { if st, ok := status.FromError(err); ok {
if e := t.WriteStatus(stream, st); e != nil { if e := t.WriteStatus(stream, st); e != nil {
grpclog.Warningf("grpc: Server.processUnaryRPC failed to write status %v", e) channelz.Warningf(s.channelzID, "grpc: Server.processUnaryRPC failed to write status %v", e)
} }
} }
return err return err
@ -1017,7 +1092,7 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.
trInfo.tr.SetError() trInfo.tr.SetError()
} }
if e := t.WriteStatus(stream, appStatus); e != nil { if e := t.WriteStatus(stream, appStatus); e != nil {
grpclog.Warningf("grpc: Server.processUnaryRPC failed to write status: %v", e) channelz.Warningf(s.channelzID, "grpc: Server.processUnaryRPC failed to write status: %v", e)
} }
if binlog != nil { if binlog != nil {
if h, _ := stream.Header(); h.Len() > 0 { if h, _ := stream.Header(); h.Len() > 0 {
@ -1044,9 +1119,9 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.
// The entire stream is done (for unary RPC only). // The entire stream is done (for unary RPC only).
return err return err
} }
if s, ok := status.FromError(err); ok { if sts, ok := status.FromError(err); ok {
if e := t.WriteStatus(stream, s); e != nil { if e := t.WriteStatus(stream, sts); e != nil {
grpclog.Warningf("grpc: Server.processUnaryRPC failed to write status: %v", e) channelz.Warningf(s.channelzID, "grpc: Server.processUnaryRPC failed to write status: %v", e)
} }
} else { } else {
switch st := err.(type) { switch st := err.(type) {
@ -1096,34 +1171,52 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.
return err return err
} }
// chainStreamServerInterceptors chains all stream server interceptors into one.
func chainStreamServerInterceptors(s *Server) {
// Prepend opts.streamInt to the chaining interceptors if it exists, since streamInt will
// be executed before any other chained interceptors.
interceptors := s.opts.chainStreamInts
if s.opts.streamInt != nil {
interceptors = append([]StreamServerInterceptor{s.opts.streamInt}, s.opts.chainStreamInts...)
}
var chainedInt StreamServerInterceptor
if len(interceptors) == 0 {
chainedInt = nil
} else if len(interceptors) == 1 {
chainedInt = interceptors[0]
} else {
chainedInt = func(srv interface{}, ss ServerStream, info *StreamServerInfo, handler StreamHandler) error {
return interceptors[0](srv, ss, info, getChainStreamHandler(interceptors, 0, info, handler))
}
}
s.opts.streamInt = chainedInt
}
// getChainStreamHandler recursively generate the chained StreamHandler
func getChainStreamHandler(interceptors []StreamServerInterceptor, curr int, info *StreamServerInfo, finalHandler StreamHandler) StreamHandler {
if curr == len(interceptors)-1 {
return finalHandler
}
return func(srv interface{}, ss ServerStream) error {
return interceptors[curr+1](srv, ss, info, getChainStreamHandler(interceptors, curr+1, info, finalHandler))
}
}
func (s *Server) processStreamingRPC(t transport.ServerTransport, stream *transport.Stream, srv *service, sd *StreamDesc, trInfo *traceInfo) (err error) { func (s *Server) processStreamingRPC(t transport.ServerTransport, stream *transport.Stream, srv *service, sd *StreamDesc, trInfo *traceInfo) (err error) {
if channelz.IsOn() { if channelz.IsOn() {
s.incrCallsStarted() s.incrCallsStarted()
defer func() {
if err != nil && err != io.EOF {
s.incrCallsFailed()
} else {
s.incrCallsSucceeded()
}
}()
} }
sh := s.opts.statsHandler sh := s.opts.statsHandler
var statsBegin *stats.Begin
if sh != nil { if sh != nil {
beginTime := time.Now() beginTime := time.Now()
begin := &stats.Begin{ statsBegin = &stats.Begin{
BeginTime: beginTime, BeginTime: beginTime,
} }
sh.HandleRPC(stream.Context(), begin) sh.HandleRPC(stream.Context(), statsBegin)
defer func() {
end := &stats.End{
BeginTime: beginTime,
EndTime: time.Now(),
}
if err != nil && err != io.EOF {
end.Error = toRPCErr(err)
}
sh.HandleRPC(stream.Context(), end)
}()
} }
ctx := NewContextWithServerTransportStream(stream.Context(), stream) ctx := NewContextWithServerTransportStream(stream.Context(), stream)
ss := &serverStream{ ss := &serverStream{
@ -1138,6 +1231,41 @@ func (s *Server) processStreamingRPC(t transport.ServerTransport, stream *transp
statsHandler: sh, statsHandler: sh,
} }
if sh != nil || trInfo != nil || channelz.IsOn() {
// See comment in processUnaryRPC on defers.
defer func() {
if trInfo != nil {
ss.mu.Lock()
if err != nil && err != io.EOF {
ss.trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true)
ss.trInfo.tr.SetError()
}
ss.trInfo.tr.Finish()
ss.trInfo.tr = nil
ss.mu.Unlock()
}
if sh != nil {
end := &stats.End{
BeginTime: statsBegin.BeginTime,
EndTime: time.Now(),
}
if err != nil && err != io.EOF {
end.Error = toRPCErr(err)
}
sh.HandleRPC(stream.Context(), end)
}
if channelz.IsOn() {
if err != nil && err != io.EOF {
s.incrCallsFailed()
} else {
s.incrCallsSucceeded()
}
}
}()
}
ss.binlog = binarylog.GetMethodLogger(stream.Method()) ss.binlog = binarylog.GetMethodLogger(stream.Method())
if ss.binlog != nil { if ss.binlog != nil {
md, _ := metadata.FromIncomingContext(ctx) md, _ := metadata.FromIncomingContext(ctx)
@ -1191,16 +1319,6 @@ func (s *Server) processStreamingRPC(t transport.ServerTransport, stream *transp
if trInfo != nil { if trInfo != nil {
trInfo.tr.LazyLog(&trInfo.firstLine, false) trInfo.tr.LazyLog(&trInfo.firstLine, false)
defer func() {
ss.mu.Lock()
if err != nil && err != io.EOF {
ss.trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true)
ss.trInfo.tr.SetError()
}
ss.trInfo.tr.Finish()
ss.trInfo.tr = nil
ss.mu.Unlock()
}()
} }
var appErr error var appErr error
var server interface{} var server interface{}
@ -1271,7 +1389,7 @@ func (s *Server) handleStream(t transport.ServerTransport, stream *transport.Str
trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true) trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true)
trInfo.tr.SetError() trInfo.tr.SetError()
} }
grpclog.Warningf("grpc: Server.handleStream failed to write status: %v", err) channelz.Warningf(s.channelzID, "grpc: Server.handleStream failed to write status: %v", err)
} }
if trInfo != nil { if trInfo != nil {
trInfo.tr.Finish() trInfo.tr.Finish()
@ -1312,7 +1430,7 @@ func (s *Server) handleStream(t transport.ServerTransport, stream *transport.Str
trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true) trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true)
trInfo.tr.SetError() trInfo.tr.SetError()
} }
grpclog.Warningf("grpc: Server.handleStream failed to write status: %v", err) channelz.Warningf(s.channelzID, "grpc: Server.handleStream failed to write status: %v", err)
} }
if trInfo != nil { if trInfo != nil {
trInfo.tr.Finish() trInfo.tr.Finish()

View File

@ -136,9 +136,9 @@ type retryPolicy struct {
maxAttempts int maxAttempts int
// Exponential backoff parameters. The initial retry attempt will occur at // Exponential backoff parameters. The initial retry attempt will occur at
// random(0, initialBackoffMS). In general, the nth attempt will occur at // random(0, initialBackoff). In general, the nth attempt will occur at
// random(0, // random(0,
// min(initialBackoffMS*backoffMultiplier**(n-1), maxBackoffMS)). // min(initialBackoff*backoffMultiplier**(n-1), maxBackoff)).
// //
// These fields are required and must be greater than zero. // These fields are required and must be greater than zero.
initialBackoff time.Duration initialBackoff time.Duration

View File

@ -81,6 +81,10 @@ type InHeader struct {
Client bool Client bool
// WireLength is the wire length of header. // WireLength is the wire length of header.
WireLength int WireLength int
// Compression is the compression algorithm used for the RPC.
Compression string
// Header contains the header metadata received.
Header metadata.MD
// The following fields are valid only if Client is false. // The following fields are valid only if Client is false.
// FullMethod is the full RPC method string, i.e., /package.service/method. // FullMethod is the full RPC method string, i.e., /package.service/method.
@ -89,8 +93,6 @@ type InHeader struct {
RemoteAddr net.Addr RemoteAddr net.Addr
// LocalAddr is the local address of the corresponding connection. // LocalAddr is the local address of the corresponding connection.
LocalAddr net.Addr LocalAddr net.Addr
// Compression is the compression algorithm used for the RPC.
Compression string
} }
// IsClient indicates if the stats information is from client side. // IsClient indicates if the stats information is from client side.
@ -104,6 +106,9 @@ type InTrailer struct {
Client bool Client bool
// WireLength is the wire length of trailer. // WireLength is the wire length of trailer.
WireLength int WireLength int
// Trailer contains the trailer metadata received from the server. This
// field is only valid if this InTrailer is from the client side.
Trailer metadata.MD
} }
// IsClient indicates if the stats information is from client side. // IsClient indicates if the stats information is from client side.
@ -136,6 +141,10 @@ func (s *OutPayload) isRPCStats() {}
type OutHeader struct { type OutHeader struct {
// Client is true if this OutHeader is from client side. // Client is true if this OutHeader is from client side.
Client bool Client bool
// Compression is the compression algorithm used for the RPC.
Compression string
// Header contains the header metadata sent.
Header metadata.MD
// The following fields are valid only if Client is true. // The following fields are valid only if Client is true.
// FullMethod is the full RPC method string, i.e., /package.service/method. // FullMethod is the full RPC method string, i.e., /package.service/method.
@ -144,8 +153,6 @@ type OutHeader struct {
RemoteAddr net.Addr RemoteAddr net.Addr
// LocalAddr is the local address of the corresponding connection. // LocalAddr is the local address of the corresponding connection.
LocalAddr net.Addr LocalAddr net.Addr
// Compression is the compression algorithm used for the RPC.
Compression string
} }
// IsClient indicates if this stats information is from client side. // IsClient indicates if this stats information is from client side.
@ -158,7 +165,13 @@ type OutTrailer struct {
// Client is true if this OutTrailer is from client side. // Client is true if this OutTrailer is from client side.
Client bool Client bool
// WireLength is the wire length of trailer. // WireLength is the wire length of trailer.
//
// Deprecated: This field is never set. The length is not known when this message is
// emitted because the trailer fields are compressed with hpack after that.
WireLength int WireLength int
// Trailer contains the trailer metadata sent to the client. This
// field is only valid if this OutTrailer is from the server side.
Trailer metadata.MD
} }
// IsClient indicates if this stats information is from client side. // IsClient indicates if this stats information is from client side.
@ -176,6 +189,7 @@ type End struct {
EndTime time.Time EndTime time.Time
// Trailer contains the trailer metadata received from the server. This // Trailer contains the trailer metadata received from the server. This
// field is only valid if this End is from the client side. // field is only valid if this End is from the client side.
// Deprecated: use Trailer in InTrailer instead.
Trailer metadata.MD Trailer metadata.MD
// Error is the error the RPC ended with. It is an error generated from // Error is the error the RPC ended with. It is an error generated from
// status.Status and can be converted back to status.Status using // status.Status and can be converted back to status.Status using

View File

@ -29,88 +29,23 @@ package status
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"github.com/golang/protobuf/proto"
"github.com/golang/protobuf/ptypes"
spb "google.golang.org/genproto/googleapis/rpc/status" spb "google.golang.org/genproto/googleapis/rpc/status"
"google.golang.org/grpc/codes" "google.golang.org/grpc/codes"
"google.golang.org/grpc/internal" "google.golang.org/grpc/internal/status"
) )
func init() { // Status references google.golang.org/grpc/internal/status. It represents an
internal.StatusRawProto = statusRawProto // RPC status code, message, and details. It is immutable and should be
} // created with New, Newf, or FromProto.
// https://godoc.org/google.golang.org/grpc/internal/status
func statusRawProto(s *Status) *spb.Status { return s.s } type Status = status.Status
// statusError is an alias of a status proto. It implements error and Status,
// and a nil statusError should never be returned by this package.
type statusError spb.Status
func (se *statusError) Error() string {
p := (*spb.Status)(se)
return fmt.Sprintf("rpc error: code = %s desc = %s", codes.Code(p.GetCode()), p.GetMessage())
}
func (se *statusError) GRPCStatus() *Status {
return &Status{s: (*spb.Status)(se)}
}
// Is implements future error.Is functionality.
// A statusError is equivalent if the code and message are identical.
func (se *statusError) Is(target error) bool {
tse, ok := target.(*statusError)
if !ok {
return false
}
return proto.Equal((*spb.Status)(se), (*spb.Status)(tse))
}
// Status represents an RPC status code, message, and details. It is immutable
// and should be created with New, Newf, or FromProto.
type Status struct {
s *spb.Status
}
// Code returns the status code contained in s.
func (s *Status) Code() codes.Code {
if s == nil || s.s == nil {
return codes.OK
}
return codes.Code(s.s.Code)
}
// Message returns the message contained in s.
func (s *Status) Message() string {
if s == nil || s.s == nil {
return ""
}
return s.s.Message
}
// Proto returns s's status as an spb.Status proto message.
func (s *Status) Proto() *spb.Status {
if s == nil {
return nil
}
return proto.Clone(s.s).(*spb.Status)
}
// Err returns an immutable error representing s; returns nil if s.Code() is
// OK.
func (s *Status) Err() error {
if s.Code() == codes.OK {
return nil
}
return (*statusError)(s.s)
}
// New returns a Status representing c and msg. // New returns a Status representing c and msg.
func New(c codes.Code, msg string) *Status { func New(c codes.Code, msg string) *Status {
return &Status{s: &spb.Status{Code: int32(c), Message: msg}} return status.New(c, msg)
} }
// Newf returns New(c, fmt.Sprintf(format, a...)). // Newf returns New(c, fmt.Sprintf(format, a...)).
@ -135,7 +70,7 @@ func ErrorProto(s *spb.Status) error {
// FromProto returns a Status representing s. // FromProto returns a Status representing s.
func FromProto(s *spb.Status) *Status { func FromProto(s *spb.Status) *Status {
return &Status{s: proto.Clone(s).(*spb.Status)} return status.FromProto(s)
} }
// FromError returns a Status representing err if it was produced from this // FromError returns a Status representing err if it was produced from this
@ -160,42 +95,6 @@ func Convert(err error) *Status {
return s return s
} }
// WithDetails returns a new status with the provided details messages appended to the status.
// If any errors are encountered, it returns nil and the first error encountered.
func (s *Status) WithDetails(details ...proto.Message) (*Status, error) {
if s.Code() == codes.OK {
return nil, errors.New("no error details for status with code OK")
}
// s.Code() != OK implies that s.Proto() != nil.
p := s.Proto()
for _, detail := range details {
any, err := ptypes.MarshalAny(detail)
if err != nil {
return nil, err
}
p.Details = append(p.Details, any)
}
return &Status{s: p}, nil
}
// Details returns a slice of details messages attached to the status.
// If a detail cannot be decoded, the error is returned in place of the detail.
func (s *Status) Details() []interface{} {
if s == nil || s.s == nil {
return nil
}
details := make([]interface{}, 0, len(s.s.Details))
for _, any := range s.s.Details {
detail := &ptypes.DynamicAny{}
if err := ptypes.UnmarshalAny(any, detail); err != nil {
details = append(details, err)
continue
}
details = append(details, detail.Message)
}
return details
}
// Code returns the Code of the error if it is a Status error, codes.OK if err // Code returns the Code of the error if it is a Status error, codes.OK if err
// is nil, or codes.Unknown otherwise. // is nil, or codes.Unknown otherwise.
func Code(err error) codes.Code { func Code(err error) codes.Code {

View File

@ -31,7 +31,6 @@ import (
"google.golang.org/grpc/balancer" "google.golang.org/grpc/balancer"
"google.golang.org/grpc/codes" "google.golang.org/grpc/codes"
"google.golang.org/grpc/encoding" "google.golang.org/grpc/encoding"
"google.golang.org/grpc/grpclog"
"google.golang.org/grpc/internal/balancerload" "google.golang.org/grpc/internal/balancerload"
"google.golang.org/grpc/internal/binarylog" "google.golang.org/grpc/internal/binarylog"
"google.golang.org/grpc/internal/channelz" "google.golang.org/grpc/internal/channelz"
@ -498,13 +497,13 @@ func (cs *clientStream) shouldRetry(err error) error {
if len(sps) == 1 { if len(sps) == 1 {
var e error var e error
if pushback, e = strconv.Atoi(sps[0]); e != nil || pushback < 0 { if pushback, e = strconv.Atoi(sps[0]); e != nil || pushback < 0 {
grpclog.Infof("Server retry pushback specified to abort (%q).", sps[0]) channelz.Infof(cs.cc.channelzID, "Server retry pushback specified to abort (%q).", sps[0])
cs.retryThrottler.throttle() // This counts as a failure for throttling. cs.retryThrottler.throttle() // This counts as a failure for throttling.
return err return err
} }
hasPushback = true hasPushback = true
} else if len(sps) > 1 { } else if len(sps) > 1 {
grpclog.Warningf("Server retry pushback specified multiple values (%q); not retrying.", sps) channelz.Warningf(cs.cc.channelzID, "Server retry pushback specified multiple values (%q); not retrying.", sps)
cs.retryThrottler.throttle() // This counts as a failure for throttling. cs.retryThrottler.throttle() // This counts as a failure for throttling.
return err return err
} }

View File

@ -41,9 +41,6 @@ func methodFamily(m string) string {
if i := strings.Index(m, "/"); i >= 0 { if i := strings.Index(m, "/"); i >= 0 {
m = m[:i] // remove everything from second slash m = m[:i] // remove everything from second slash
} }
if i := strings.LastIndex(m, "."); i >= 0 {
m = m[i+1:] // cut down to last dotted component
}
return m return m
} }

View File

@ -19,4 +19,4 @@
package grpc package grpc
// Version is the current grpc version. // Version is the current grpc version.
const Version = "1.25.1" const Version = "1.29.0"

111
vendor/google.golang.org/grpc/vet.sh generated vendored
View File

@ -1,20 +1,22 @@
#!/bin/bash #!/bin/bash
if [[ `uname -a` = *"Darwin"* ]]; then
echo "It seems you are running on Mac. This script does not work on Mac. See https://github.com/grpc/grpc-go/issues/2047"
exit 1
fi
set -ex # Exit on error; debugging enabled. set -ex # Exit on error; debugging enabled.
set -o pipefail # Fail a pipe if any sub-command fails. set -o pipefail # Fail a pipe if any sub-command fails.
# not makes sure the command passed to it does not exit with a return code of 0.
not() {
# This is required instead of the earlier (! $COMMAND) because subshells and
# pipefail don't work the same on Darwin as in Linux.
! "$@"
}
die() { die() {
echo "$@" >&2 echo "$@" >&2
exit 1 exit 1
} }
fail_on_output() { fail_on_output() {
tee /dev/stderr | (! read) tee /dev/stderr | not read
} }
# Check to make sure it's safe to modify the user's git repo. # Check to make sure it's safe to modify the user's git repo.
@ -60,7 +62,7 @@ if [[ "$1" = "-install" ]]; then
unzip ${PROTOC_FILENAME} unzip ${PROTOC_FILENAME}
bin/protoc --version bin/protoc --version
popd popd
elif ! which protoc > /dev/null; then elif not which protoc > /dev/null; then
die "Please install protoc into your path" die "Please install protoc into your path"
fi fi
fi fi
@ -70,21 +72,21 @@ elif [[ "$#" -ne 0 ]]; then
fi fi
# - Ensure all source files contain a copyright message. # - Ensure all source files contain a copyright message.
(! git grep -L "\(Copyright [0-9]\{4,\} gRPC authors\)\|DO NOT EDIT" -- '*.go') not git grep -L "\(Copyright [0-9]\{4,\} gRPC authors\)\|DO NOT EDIT" -- '*.go'
# - Make sure all tests in grpc and grpc/test use leakcheck via Teardown. # - Make sure all tests in grpc and grpc/test use leakcheck via Teardown.
(! grep 'func Test[^(]' *_test.go) not grep 'func Test[^(]' *_test.go
(! grep 'func Test[^(]' test/*.go) not grep 'func Test[^(]' test/*.go
# - Do not import x/net/context. # - Do not import x/net/context.
(! git grep -l 'x/net/context' -- "*.go") not git grep -l 'x/net/context' -- "*.go"
# - Do not import math/rand for real library code. Use internal/grpcrand for # - Do not import math/rand for real library code. Use internal/grpcrand for
# thread safety. # thread safety.
git grep -l '"math/rand"' -- "*.go" 2>&1 | (! grep -v '^examples\|^stress\|grpcrand\|wrr_test') git grep -l '"math/rand"' -- "*.go" 2>&1 | not grep -v '^examples\|^stress\|grpcrand\|^benchmark\|wrr_test'
# - Ensure all ptypes proto packages are renamed when importing. # - Ensure all ptypes proto packages are renamed when importing.
(! git grep "\(import \|^\s*\)\"github.com/golang/protobuf/ptypes/" -- "*.go") not git grep "\(import \|^\s*\)\"github.com/golang/protobuf/ptypes/" -- "*.go"
# - Check imports that are illegal in appengine (until Go 1.11). # - Check imports that are illegal in appengine (until Go 1.11).
# TODO: Remove when we drop Go 1.10 support # TODO: Remove when we drop Go 1.10 support
@ -92,9 +94,11 @@ go list -f {{.Dir}} ./... | xargs go run test/go_vet/vet.go
# - gofmt, goimports, golint (with exceptions for generated code), go vet. # - gofmt, goimports, golint (with exceptions for generated code), go vet.
gofmt -s -d -l . 2>&1 | fail_on_output gofmt -s -d -l . 2>&1 | fail_on_output
goimports -l . 2>&1 | (! grep -vE "(_mock|\.pb)\.go") | fail_on_output goimports -l . 2>&1 | not grep -vE "(_mock|\.pb)\.go"
golint ./... 2>&1 | (! grep -vE "(_mock|\.pb)\.go:") golint ./... 2>&1 | not grep -vE "(_mock|\.pb)\.go:"
go vet -all . go vet -all ./...
misspell -error .
# - Check that generated proto files are up to date. # - Check that generated proto files are up to date.
if [[ -z "${VET_SKIP_PROTO}" ]]; then if [[ -z "${VET_SKIP_PROTO}" ]]; then
@ -111,32 +115,49 @@ if go help mod >& /dev/null; then
fi fi
# - Collection of static analysis checks # - Collection of static analysis checks
# TODO(dfawley): don't use deprecated functions in examples. #
staticcheck -go 1.9 -checks 'inherit,-ST1015' -ignore ' # TODO(dfawley): don't use deprecated functions in examples or first-party
google.golang.org/grpc/balancer.go:SA1019 # plugins.
google.golang.org/grpc/balancer/grpclb/grpclb_remote_balancer.go:SA1019 SC_OUT="$(mktemp)"
google.golang.org/grpc/balancer/grpclb/grpclb_test.go:SA1019 staticcheck -go 1.9 -checks 'inherit,-ST1015' ./... > "${SC_OUT}" || true
google.golang.org/grpc/balancer/roundrobin/roundrobin_test.go:SA1019 # Error if anything other than deprecation warnings are printed.
google.golang.org/grpc/xds/internal/balancer/edsbalancer/balancergroup.go:SA1019 not grep -v "is deprecated:.*SA1019" "${SC_OUT}"
google.golang.org/grpc/xds/internal/resolver/xds_resolver.go:SA1019 # Only ignore the following deprecated types/fields/functions.
google.golang.org/grpc/xds/internal/balancer/xds.go:SA1019 not grep -Fv '.HandleResolvedAddrs
google.golang.org/grpc/xds/internal/balancer/xds_client.go:SA1019 .HandleSubConnStateChange
google.golang.org/grpc/balancer_conn_wrappers.go:SA1019 .HeaderMap
google.golang.org/grpc/balancer_test.go:SA1019 .NewAddress
google.golang.org/grpc/benchmark/benchmain/main.go:SA1019 .NewServiceConfig
google.golang.org/grpc/benchmark/worker/benchmark_client.go:SA1019 .Metadata is deprecated: use Attributes
google.golang.org/grpc/clientconn.go:S1024 .Type is deprecated: use Attributes
google.golang.org/grpc/clientconn_state_transition_test.go:SA1019 .UpdateBalancerState
google.golang.org/grpc/clientconn_test.go:SA1019 balancer.Picker
google.golang.org/grpc/examples/features/debugging/client/main.go:SA1019 grpc.CallCustomCodec
google.golang.org/grpc/examples/features/load_balancing/client/main.go:SA1019 grpc.Code
google.golang.org/grpc/internal/transport/handler_server.go:SA1019 grpc.Compressor
google.golang.org/grpc/internal/transport/handler_server_test.go:SA1019 grpc.Decompressor
google.golang.org/grpc/internal/resolver/dns/dns_resolver.go:SA1019 grpc.MaxMsgSize
google.golang.org/grpc/stats/stats_test.go:SA1019 grpc.MethodConfig
google.golang.org/grpc/test/balancer_test.go:SA1019 grpc.NewGZIPCompressor
google.golang.org/grpc/test/channelz_test.go:SA1019 grpc.NewGZIPDecompressor
google.golang.org/grpc/test/end2end_test.go:SA1019 grpc.RPCCompressor
google.golang.org/grpc/test/healthcheck_test.go:SA1019 grpc.RPCDecompressor
' ./... grpc.RoundRobin
misspell -error . grpc.ServiceConfig
grpc.WithBalancer
grpc.WithBalancerName
grpc.WithCompressor
grpc.WithDecompressor
grpc.WithDialer
grpc.WithMaxMsgSize
grpc.WithServiceConfig
grpc.WithTimeout
http.CloseNotifier
info.SecurityVersion
naming.Resolver
naming.Update
naming.Watcher
resolver.Backend
resolver.GRPCLB' "${SC_OUT}"
echo SUCCESS

8
vendor/modules.txt vendored
View File

@ -25,7 +25,7 @@ github.com/go-sql-driver/mysql
# github.com/golang/mock v1.3.1 # github.com/golang/mock v1.3.1
github.com/golang/mock/gomock github.com/golang/mock/gomock
github.com/golang/mock/mockgen/model github.com/golang/mock/mockgen/model
# github.com/golang/protobuf v1.3.2 # github.com/golang/protobuf v1.3.3
github.com/golang/protobuf/proto github.com/golang/protobuf/proto
github.com/golang/protobuf/ptypes github.com/golang/protobuf/ptypes
github.com/golang/protobuf/ptypes/any github.com/golang/protobuf/ptypes/any
@ -145,8 +145,9 @@ golang.org/x/text/unicode/norm
google.golang.org/appengine/cloudsql google.golang.org/appengine/cloudsql
# google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 # google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55
google.golang.org/genproto/googleapis/rpc/status google.golang.org/genproto/googleapis/rpc/status
# google.golang.org/grpc v1.25.1 # google.golang.org/grpc v1.29.0
google.golang.org/grpc google.golang.org/grpc
google.golang.org/grpc/attributes
google.golang.org/grpc/backoff google.golang.org/grpc/backoff
google.golang.org/grpc/balancer google.golang.org/grpc/balancer
google.golang.org/grpc/balancer/base google.golang.org/grpc/balancer/base
@ -166,10 +167,13 @@ google.golang.org/grpc/internal/binarylog
google.golang.org/grpc/internal/buffer google.golang.org/grpc/internal/buffer
google.golang.org/grpc/internal/channelz google.golang.org/grpc/internal/channelz
google.golang.org/grpc/internal/envconfig google.golang.org/grpc/internal/envconfig
google.golang.org/grpc/internal/grpclog
google.golang.org/grpc/internal/grpcrand google.golang.org/grpc/internal/grpcrand
google.golang.org/grpc/internal/grpcsync google.golang.org/grpc/internal/grpcsync
google.golang.org/grpc/internal/grpcutil
google.golang.org/grpc/internal/resolver/dns google.golang.org/grpc/internal/resolver/dns
google.golang.org/grpc/internal/resolver/passthrough google.golang.org/grpc/internal/resolver/passthrough
google.golang.org/grpc/internal/status
google.golang.org/grpc/internal/syscall google.golang.org/grpc/internal/syscall
google.golang.org/grpc/internal/transport google.golang.org/grpc/internal/transport
google.golang.org/grpc/keepalive google.golang.org/grpc/keepalive