parent
4a2029b293
commit
d2ae471026
4
go.mod
4
go.mod
|
|
@ -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
6
go.sum
|
|
@ -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=
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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/...
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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]
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
|
@ -337,7 +421,6 @@ type V2Balancer interface {
|
||||||
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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
@ -30,23 +32,30 @@ import (
|
||||||
type baseBuilder struct {
|
type baseBuilder struct {
|
||||||
name string
|
name string
|
||||||
pickerBuilder PickerBuilder
|
pickerBuilder PickerBuilder
|
||||||
|
v2PickerBuilder V2PickerBuilder
|
||||||
config Config
|
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
|
|
||||||
// ErrNoSubConnAvailable, because when state of a SubConn changes, we
|
|
||||||
// may call UpdateBalancerState with this picker.
|
|
||||||
picker: NewErrPicker(balancer.ErrNoSubConnAvailable),
|
|
||||||
config: bb.config,
|
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 {
|
||||||
|
|
@ -58,6 +67,7 @@ var _ balancer.V2Balancer = (*baseBalancer)(nil) // Assert that we implement V2B
|
||||||
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,18 +140,45 @@ 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 {
|
||||||
|
if b.pickerBuilder != nil {
|
||||||
b.picker = NewErrPicker(balancer.ErrTransientFailure)
|
b.picker = NewErrPicker(balancer.ErrTransientFailure)
|
||||||
|
} else {
|
||||||
|
b.v2Picker = NewErrPickerV2(balancer.TransientFailureError(b.mergeErrors()))
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if b.pickerBuilder != nil {
|
||||||
readySCs := make(map[resolver.Address]balancer.SubConn)
|
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.
|
||||||
|
|
@ -128,6 +188,17 @@ func (b *baseBalancer) regeneratePicker() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
b.picker = b.pickerBuilder.Build(readySCs)
|
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})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if b.picker != nil {
|
||||||
b.cc.UpdateBalancerState(b.state, b.picker)
|
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
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
if builder == nil {
|
||||||
channelz.AddTraceEvent(cc.channelzID, &channelz.TraceEventDesc{
|
channelz.Warningf(cc.channelzID, "Channel switches to new LB policy %q due to fallback from invalid balancer name", PickFirstBalancerName)
|
||||||
Desc: fmt.Sprintf("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)
|
||||||
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 {
|
|
||||||
grpclog.Infof("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)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
}
|
||||||
|
|
@ -59,8 +59,6 @@ type dialOptions struct {
|
||||||
// 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...)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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=
|
||||||
|
|
|
||||||
|
|
@ -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...)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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{})
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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{})
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -27,9 +27,12 @@ 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")
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -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{})
|
||||||
|
}
|
||||||
|
|
@ -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}
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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,44 +155,15 @@ 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
|
|
||||||
backoff internalbackoff.Exponential
|
|
||||||
retryCount int
|
|
||||||
host string
|
host string
|
||||||
port string
|
port string
|
||||||
resolver netResolver
|
resolver netResolver
|
||||||
|
|
@ -213,7 +172,6 @@ type dnsResolver struct {
|
||||||
cc resolver.ClientConn
|
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
|
||||||
|
if err == nil {
|
||||||
|
// If there are other SRV records, look them up and ignore this
|
||||||
|
// one that does not exist.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
for _, a := range lbAddrs {
|
for _, a := range lbAddrs {
|
||||||
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 + ":" + strconv.Itoa(int(s.Port))
|
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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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() {}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
}
|
||||||
|
|
@ -116,7 +116,6 @@ type serverHandlerTransport struct {
|
||||||
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)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
@ -395,6 +404,7 @@ func (t *http2Client) createHeaderFields(ctx context.Context, callHdr *CallHdr)
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
@ -1177,12 +1197,15 @@ func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) {
|
||||||
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() {
|
||||||
|
|
|
||||||
|
|
@ -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)
|
t.controlBuf.put(p)
|
||||||
keepalive.Reset(t.kp.Timeout)
|
kpTimeoutLeft = t.kp.Timeout
|
||||||
|
outstandingPing = true
|
||||||
|
}
|
||||||
|
// The amount of time to sleep here is the minimum of kp.Time and
|
||||||
|
// 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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
if lastPickErr != nil {
|
||||||
|
errStr = "latest balancer error: " + lastPickErr.Error()
|
||||||
|
} else if connectionErr := pw.connectionError(); connectionErr != nil {
|
||||||
|
errStr = "latest connection error: " + connectionErr.Error()
|
||||||
|
} else {
|
||||||
|
errStr = ctx.Err().Error()
|
||||||
|
}
|
||||||
switch ctx.Err() {
|
switch ctx.Err() {
|
||||||
case context.DeadlineExceeded:
|
case context.DeadlineExceeded:
|
||||||
return nil, nil, status.Errorf(codes.DeadlineExceeded, "latest connection error: %v", connectionErr)
|
return nil, nil, status.Error(codes.DeadlineExceeded, errStr)
|
||||||
case context.Canceled:
|
case context.Canceled:
|
||||||
return nil, nil, status.Errorf(codes.Canceled, "latest connection error: %v", connectionErr)
|
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
|
|
||||||
case balancer.ErrTransientFailure:
|
|
||||||
if !failfast {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
return nil, nil, status.Errorf(codes.Unavailable, "%v, latest connection error: %v", err, bp.connectionError())
|
if tfe, ok := err.(interface{ IsTransientFailure() bool }); ok && tfe.IsTransientFailure() {
|
||||||
case context.DeadlineExceeded:
|
if !failfast {
|
||||||
return nil, nil, status.Error(codes.DeadlineExceeded, err.Error())
|
lastPickErr = err
|
||||||
case context.Canceled:
|
continue
|
||||||
return nil, nil, status.Error(codes.Canceled, err.Error())
|
}
|
||||||
default:
|
return nil, nil, status.Error(codes.Unavailable, err.Error())
|
||||||
|
}
|
||||||
if _, ok := status.FromError(err); ok {
|
if _, ok := status.FromError(err); ok {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
// err is some other error.
|
// err is some other error.
|
||||||
return nil, nil, status.Error(codes.Unknown, err.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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
state connectivity.State
|
||||||
cc balancer.ClientConn
|
cc balancer.ClientConn
|
||||||
sc balancer.SubConn
|
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 {
|
||||||
|
result balancer.PickResult
|
||||||
err error
|
err error
|
||||||
sc balancer.SubConn
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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() {
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// chainUnaryServerInterceptors chains all unary server interceptors into one.
|
||||||
|
func chainUnaryServerInterceptors(s *Server) {
|
||||||
|
// Prepend opts.unaryInt to the chaining interceptors if it exists, since unaryInt will
|
||||||
|
// be executed before any other chained interceptors.
|
||||||
|
interceptors := s.opts.chainUnaryInts
|
||||||
|
if s.opts.unaryInt != nil {
|
||||||
|
interceptors = append([]UnaryServerInterceptor{s.opts.unaryInt}, s.opts.chainUnaryInts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
var chainedInt UnaryServerInterceptor
|
||||||
|
if len(interceptors) == 0 {
|
||||||
|
chainedInt = nil
|
||||||
|
} 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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s.opts.unaryInt = chainedInt
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
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() {
|
if channelz.IsOn() {
|
||||||
s.incrCallsStarted()
|
s.incrCallsStarted()
|
||||||
defer func() {
|
|
||||||
if err != nil && err != io.EOF {
|
|
||||||
s.incrCallsFailed()
|
|
||||||
} else {
|
|
||||||
s.incrCallsSucceeded()
|
|
||||||
}
|
}
|
||||||
}()
|
var statsBegin *stats.Begin
|
||||||
}
|
|
||||||
sh := s.opts.statsHandler
|
|
||||||
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)
|
||||||
|
}
|
||||||
|
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 trInfo != nil {
|
||||||
|
if err != nil && err != io.EOF {
|
||||||
|
trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true)
|
||||||
|
trInfo.tr.SetError()
|
||||||
|
}
|
||||||
|
trInfo.tr.Finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
if sh != nil {
|
||||||
end := &stats.End{
|
end := &stats.End{
|
||||||
BeginTime: beginTime,
|
BeginTime: statsBegin.BeginTime,
|
||||||
EndTime: time.Now(),
|
EndTime: time.Now(),
|
||||||
}
|
}
|
||||||
if err != nil && err != io.EOF {
|
if err != nil && err != io.EOF {
|
||||||
end.Error = toRPCErr(err)
|
end.Error = toRPCErr(err)
|
||||||
}
|
}
|
||||||
sh.HandleRPC(stream.Context(), end)
|
sh.HandleRPC(stream.Context(), end)
|
||||||
}()
|
|
||||||
}
|
}
|
||||||
if trInfo != nil {
|
|
||||||
defer trInfo.tr.Finish()
|
if channelz.IsOn() {
|
||||||
trInfo.tr.LazyLog(&trInfo.firstLine, false)
|
|
||||||
defer func() {
|
|
||||||
if err != nil && err != io.EOF {
|
if err != nil && err != io.EOF {
|
||||||
trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true)
|
s.incrCallsFailed()
|
||||||
trInfo.tr.SetError()
|
} 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()
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue