mirror of https://github.com/docker/cli.git
vendor: google.golang.org/grpc v1.5.2
full diff: https://github.com/grpc/grpc-go/compare/v1.27.1...v1.38.0 Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
parent
2ef71e502c
commit
509cc32182
|
@ -79,7 +79,7 @@ golang.org/x/term f5c789dd3221ff39d752ac54467d
|
||||||
golang.org/x/text 23ae387dee1f90d29a23c0e87ee0b46038fbed0e # v0.3.3
|
golang.org/x/text 23ae387dee1f90d29a23c0e87ee0b46038fbed0e # v0.3.3
|
||||||
golang.org/x/time 3af7569d3a1e776fc2a3c1cec133b43105ea9c2e
|
golang.org/x/time 3af7569d3a1e776fc2a3c1cec133b43105ea9c2e
|
||||||
google.golang.org/genproto 8816d57aaa9ad8cba31b2a8ecb6199c494bdf8b4 # v0.0.0-20201110150050-8816d57aaa9a
|
google.golang.org/genproto 8816d57aaa9ad8cba31b2a8ecb6199c494bdf8b4 # v0.0.0-20201110150050-8816d57aaa9a
|
||||||
google.golang.org/grpc f495f5b15ae7ccda3b38c53a1bfcde4c1a58a2bc # v1.27.1
|
google.golang.org/grpc 0257c8657362b76f24e7a8cfb61df48d4cb735d3 # v1.38.0
|
||||||
google.golang.org/protobuf f2d1f6cbe10b90d22296ea09a7217081c2798009 # v1.26.0
|
google.golang.org/protobuf f2d1f6cbe10b90d22296ea09a7217081c2798009 # v1.26.0
|
||||||
gopkg.in/inf.v0 d2d2541c53f18d2a059457998ce2876cc8e67cbf # v0.9.1
|
gopkg.in/inf.v0 d2d2541c53f18d2a059457998ce2876cc8e67cbf # v0.9.1
|
||||||
gopkg.in/yaml.v2 53403b58ad1b561927d19068c655246f2db79d48 # v2.2.8
|
gopkg.in/yaml.v2 53403b58ad1b561927d19068c655246f2db79d48 # v2.2.8
|
||||||
|
|
|
@ -1,64 +1,53 @@
|
||||||
# gRPC-Go
|
# gRPC-Go
|
||||||
|
|
||||||
[![Build Status](https://travis-ci.org/grpc/grpc-go.svg)](https://travis-ci.org/grpc/grpc-go)
|
[![Build Status](https://travis-ci.org/grpc/grpc-go.svg)](https://travis-ci.org/grpc/grpc-go)
|
||||||
[![GoDoc](https://godoc.org/google.golang.org/grpc?status.svg)](https://godoc.org/google.golang.org/grpc)
|
[![GoDoc](https://pkg.go.dev/badge/google.golang.org/grpc)][API]
|
||||||
[![GoReportCard](https://goreportcard.com/badge/grpc/grpc-go)](https://goreportcard.com/report/github.com/grpc/grpc-go)
|
[![GoReportCard](https://goreportcard.com/badge/grpc/grpc-go)](https://goreportcard.com/report/github.com/grpc/grpc-go)
|
||||||
|
|
||||||
The Go implementation of [gRPC](https://grpc.io/): A high performance, open
|
The [Go][] implementation of [gRPC][]: A high performance, open source, general
|
||||||
source, general RPC framework that puts mobile and HTTP/2 first. For more
|
RPC framework that puts mobile and HTTP/2 first. For more information see the
|
||||||
information see the [gRPC Quick Start:
|
[Go gRPC docs][], or jump directly into the [quick start][].
|
||||||
Go](https://grpc.io/docs/quickstart/go.html) guide.
|
|
||||||
|
|
||||||
Installation
|
## Prerequisites
|
||||||
------------
|
|
||||||
|
|
||||||
To install this package, you need to install Go and setup your Go workspace on
|
- **[Go][]**: any one of the **three latest major** [releases][go-releases].
|
||||||
your computer. The simplest way to install the library is to run:
|
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
With [Go module][] support (Go 1.11+), simply add the following import
|
||||||
|
|
||||||
|
```go
|
||||||
|
import "google.golang.org/grpc"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
to your code, and then `go [build|run|test]` will automatically fetch the
|
||||||
|
necessary dependencies.
|
||||||
|
|
||||||
|
Otherwise, to install the `grpc-go` package, run the following command:
|
||||||
|
|
||||||
|
```console
|
||||||
$ go get -u google.golang.org/grpc
|
$ go get -u google.golang.org/grpc
|
||||||
```
|
```
|
||||||
|
|
||||||
With Go module support (Go 1.11+), simply `import "google.golang.org/grpc"` in
|
> **Note:** If you are trying to access `grpc-go` from **China**, see the
|
||||||
your source code and `go [build|run|test]` will automatically download the
|
> [FAQ](#FAQ) below.
|
||||||
necessary dependencies ([Go modules
|
|
||||||
ref](https://github.com/golang/go/wiki/Modules)).
|
|
||||||
|
|
||||||
If you are trying to access grpc-go from within China, please see the
|
## Learn more
|
||||||
[FAQ](#FAQ) below.
|
|
||||||
|
|
||||||
Prerequisites
|
- [Go gRPC docs][], which include a [quick start][] and [API
|
||||||
-------------
|
reference][API] among other resources
|
||||||
gRPC-Go requires Go 1.9 or later.
|
- [Low-level technical docs](Documentation) from this repository
|
||||||
|
- [Performance benchmark][]
|
||||||
|
- [Examples](examples)
|
||||||
|
|
||||||
Documentation
|
## FAQ
|
||||||
-------------
|
|
||||||
- See [godoc](https://godoc.org/google.golang.org/grpc) for package and API
|
|
||||||
descriptions.
|
|
||||||
- Documentation on specific topics can be found in the [Documentation
|
|
||||||
directory](Documentation/).
|
|
||||||
- Examples can be found in the [examples directory](examples/).
|
|
||||||
|
|
||||||
Performance
|
### I/O Timeout Errors
|
||||||
-----------
|
|
||||||
Performance benchmark data for grpc-go and other languages is maintained in
|
|
||||||
[this
|
|
||||||
dashboard](https://performance-dot-grpc-testing.appspot.com/explore?dashboard=5652536396611584&widget=490377658&container=1286539696).
|
|
||||||
|
|
||||||
Status
|
|
||||||
------
|
|
||||||
General Availability [Google Cloud Platform Launch
|
|
||||||
Stages](https://cloud.google.com/terms/launch-stages).
|
|
||||||
|
|
||||||
FAQ
|
|
||||||
---
|
|
||||||
|
|
||||||
#### I/O Timeout Errors
|
|
||||||
|
|
||||||
The `golang.org` domain may be blocked from some countries. `go get` usually
|
The `golang.org` domain may be blocked from some countries. `go get` usually
|
||||||
produces an error like the following when this happens:
|
produces an error like the following when this happens:
|
||||||
|
|
||||||
```
|
```console
|
||||||
$ go get -u google.golang.org/grpc
|
$ go get -u google.golang.org/grpc
|
||||||
package google.golang.org/grpc: unrecognized import path "google.golang.org/grpc" (https fetch: Get https://google.golang.org/grpc?go-get=1: dial tcp 216.239.37.1:443: i/o timeout)
|
package google.golang.org/grpc: unrecognized import path "google.golang.org/grpc" (https fetch: Get https://google.golang.org/grpc?go-get=1: dial tcp 216.239.37.1:443: i/o timeout)
|
||||||
```
|
```
|
||||||
|
@ -69,7 +58,7 @@ To build Go code, there are several options:
|
||||||
|
|
||||||
- Without Go module support: `git clone` the repo manually:
|
- Without Go module support: `git clone` the repo manually:
|
||||||
|
|
||||||
```
|
```sh
|
||||||
git clone https://github.com/grpc/grpc-go.git $GOPATH/src/google.golang.org/grpc
|
git clone https://github.com/grpc/grpc-go.git $GOPATH/src/google.golang.org/grpc
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -79,7 +68,7 @@ To build Go code, there are several options:
|
||||||
- With Go module support: it is possible to use the `replace` feature of `go
|
- With Go module support: it is possible to use the `replace` feature of `go
|
||||||
mod` to create aliases for golang.org packages. In your project's directory:
|
mod` to create aliases for golang.org packages. In your project's directory:
|
||||||
|
|
||||||
```
|
```sh
|
||||||
go mod edit -replace=google.golang.org/grpc=github.com/grpc/grpc-go@latest
|
go mod edit -replace=google.golang.org/grpc=github.com/grpc/grpc-go@latest
|
||||||
go mod tidy
|
go mod tidy
|
||||||
go mod vendor
|
go mod vendor
|
||||||
|
@ -87,35 +76,66 @@ To build Go code, there are several options:
|
||||||
```
|
```
|
||||||
|
|
||||||
Again, this will need to be done for all transitive dependencies hosted on
|
Again, this will need to be done for all transitive dependencies hosted on
|
||||||
golang.org as well. Please refer to [this
|
golang.org as well. For details, refer to [golang/go issue #28652](https://github.com/golang/go/issues/28652).
|
||||||
issue](https://github.com/golang/go/issues/28652) in the golang repo regarding
|
|
||||||
this concern.
|
|
||||||
|
|
||||||
#### Compiling error, undefined: grpc.SupportPackageIsVersion
|
### Compiling error, undefined: grpc.SupportPackageIsVersion
|
||||||
|
|
||||||
Please update proto package, gRPC package and rebuild the proto files:
|
#### If you are using Go modules:
|
||||||
- `go get -u github.com/golang/protobuf/{proto,protoc-gen-go}`
|
|
||||||
- `go get -u google.golang.org/grpc`
|
|
||||||
- `protoc --go_out=plugins=grpc:. *.proto`
|
|
||||||
|
|
||||||
#### How to turn on logging
|
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:
|
||||||
|
|
||||||
The default logger is controlled by the environment variables. Turn everything
|
```go
|
||||||
on by setting:
|
module <your module name>
|
||||||
|
|
||||||
```
|
require (
|
||||||
GRPC_GO_LOG_VERBOSITY_LEVEL=99 GRPC_GO_LOG_SEVERITY_LEVEL=info
|
google.golang.org/grpc v1.27.0
|
||||||
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
#### The RPC failed with error `"code = Unavailable desc = transport is closing"`
|
#### If you are *not* using Go modules:
|
||||||
|
|
||||||
|
Update the `proto` package, gRPC package, and rebuild the `.proto` files:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
go get -u github.com/golang/protobuf/{proto,protoc-gen-go}
|
||||||
|
go get -u google.golang.org/grpc
|
||||||
|
protoc --go_out=plugins=grpc:. *.proto
|
||||||
|
```
|
||||||
|
|
||||||
|
### How to turn on logging
|
||||||
|
|
||||||
|
The default logger is controlled by environment variables. Turn everything on
|
||||||
|
like this:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ export GRPC_GO_LOG_VERBOSITY_LEVEL=99
|
||||||
|
$ export GRPC_GO_LOG_SEVERITY_LEVEL=info
|
||||||
|
```
|
||||||
|
|
||||||
|
### The RPC failed with error `"code = Unavailable desc = transport is closing"`
|
||||||
|
|
||||||
This error means the connection the RPC is using was closed, and there are many
|
This error means the connection the RPC is using was closed, and there are many
|
||||||
possible reasons, including:
|
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
|
||||||
logging on __both client and server__, and see if there are any transport
|
logging on __both client and server__, and see if there are any transport
|
||||||
errors.
|
errors.
|
||||||
|
|
||||||
|
[API]: https://pkg.go.dev/google.golang.org/grpc
|
||||||
|
[Go]: https://golang.org
|
||||||
|
[Go module]: https://github.com/golang/go/wiki/Modules
|
||||||
|
[gRPC]: https://grpc.io
|
||||||
|
[Go gRPC docs]: https://grpc.io/docs/languages/go
|
||||||
|
[Performance benchmark]: https://performance-dot-grpc-testing.appspot.com/explore?dashboard=5652536396611584&widget=490377658&container=1286539696
|
||||||
|
[quick start]: https://grpc.io/docs/languages/go/quickstart
|
||||||
|
[go-releases]: https://golang.org/doc/devel/release.html
|
||||||
|
|
|
@ -19,7 +19,10 @@
|
||||||
// Package attributes defines a generic key/value store used in various gRPC
|
// Package attributes defines a generic key/value store used in various gRPC
|
||||||
// components.
|
// components.
|
||||||
//
|
//
|
||||||
// All APIs in this package are EXPERIMENTAL.
|
// Experimental
|
||||||
|
//
|
||||||
|
// Notice: This package is EXPERIMENTAL and may be changed or removed in a
|
||||||
|
// later release.
|
||||||
package attributes
|
package attributes
|
||||||
|
|
||||||
import "fmt"
|
import "fmt"
|
||||||
|
@ -50,6 +53,9 @@ func New(kvs ...interface{}) *Attributes {
|
||||||
// times, the last value overwrites all previous values for that key. To
|
// times, the last value overwrites all previous values for that key. To
|
||||||
// remove an existing key, use a nil value.
|
// remove an existing key, use a nil value.
|
||||||
func (a *Attributes) WithValues(kvs ...interface{}) *Attributes {
|
func (a *Attributes) WithValues(kvs ...interface{}) *Attributes {
|
||||||
|
if a == nil {
|
||||||
|
return New(kvs...)
|
||||||
|
}
|
||||||
if len(kvs)%2 != 0 {
|
if len(kvs)%2 != 0 {
|
||||||
panic(fmt.Sprintf("attributes.New called with unexpected input: len(kvs) = %v", len(kvs)))
|
panic(fmt.Sprintf("attributes.New called with unexpected input: len(kvs) = %v", len(kvs)))
|
||||||
}
|
}
|
||||||
|
@ -66,5 +72,8 @@ func (a *Attributes) WithValues(kvs ...interface{}) *Attributes {
|
||||||
// Value returns the value associated with these attributes for key, or nil if
|
// Value returns the value associated with these attributes for key, or nil if
|
||||||
// no value is associated with key.
|
// no value is associated with key.
|
||||||
func (a *Attributes) Value(key interface{}) interface{} {
|
func (a *Attributes) Value(key interface{}) interface{} {
|
||||||
|
if a == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
return a.m[key]
|
return a.m[key]
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,10 @@ type BackoffConfig struct {
|
||||||
// here for more details:
|
// here for more details:
|
||||||
// https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md.
|
// https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md.
|
||||||
//
|
//
|
||||||
// This API is EXPERIMENTAL.
|
// Experimental
|
||||||
|
//
|
||||||
|
// Notice: This type is EXPERIMENTAL and may be changed or removed in a
|
||||||
|
// later release.
|
||||||
type ConnectParams struct {
|
type ConnectParams struct {
|
||||||
// Backoff specifies the configuration options for connection backoff.
|
// Backoff specifies the configuration options for connection backoff.
|
||||||
Backoff backoff.Config
|
Backoff backoff.Config
|
||||||
|
|
|
@ -1,391 +0,0 @@
|
||||||
/*
|
|
||||||
*
|
|
||||||
* Copyright 2016 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 grpc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"net"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"google.golang.org/grpc/codes"
|
|
||||||
"google.golang.org/grpc/credentials"
|
|
||||||
"google.golang.org/grpc/grpclog"
|
|
||||||
"google.golang.org/grpc/naming"
|
|
||||||
"google.golang.org/grpc/status"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Address represents a server the client connects to.
|
|
||||||
//
|
|
||||||
// Deprecated: please use package balancer.
|
|
||||||
type Address struct {
|
|
||||||
// Addr is the server address on which a connection will be established.
|
|
||||||
Addr string
|
|
||||||
// Metadata is the information associated with Addr, which may be used
|
|
||||||
// to make load balancing decision.
|
|
||||||
Metadata interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// BalancerConfig specifies the configurations for Balancer.
|
|
||||||
//
|
|
||||||
// Deprecated: please use package balancer. May be removed in a future 1.x release.
|
|
||||||
type BalancerConfig struct {
|
|
||||||
// DialCreds is the transport credential the Balancer implementation can
|
|
||||||
// use to dial to a remote load balancer server. The Balancer implementations
|
|
||||||
// can ignore this if it does not need to talk to another party securely.
|
|
||||||
DialCreds credentials.TransportCredentials
|
|
||||||
// Dialer is the custom dialer the Balancer implementation can use to dial
|
|
||||||
// to a remote load balancer server. The Balancer implementations
|
|
||||||
// can ignore this if it doesn't need to talk to remote balancer.
|
|
||||||
Dialer func(context.Context, string) (net.Conn, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// BalancerGetOptions configures a Get call.
|
|
||||||
//
|
|
||||||
// Deprecated: please use package balancer. May be removed in a future 1.x release.
|
|
||||||
type BalancerGetOptions struct {
|
|
||||||
// BlockingWait specifies whether Get should block when there is no
|
|
||||||
// connected address.
|
|
||||||
BlockingWait bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// Balancer chooses network addresses for RPCs.
|
|
||||||
//
|
|
||||||
// Deprecated: please use package balancer. May be removed in a future 1.x release.
|
|
||||||
type Balancer interface {
|
|
||||||
// Start does the initialization work to bootstrap a Balancer. For example,
|
|
||||||
// this function may start the name resolution and watch the updates. It will
|
|
||||||
// be called when dialing.
|
|
||||||
Start(target string, config BalancerConfig) error
|
|
||||||
// Up informs the Balancer that gRPC has a connection to the server at
|
|
||||||
// addr. It returns down which is called once the connection to addr gets
|
|
||||||
// lost or closed.
|
|
||||||
// TODO: It is not clear how to construct and take advantage of the meaningful error
|
|
||||||
// parameter for down. Need realistic demands to guide.
|
|
||||||
Up(addr Address) (down func(error))
|
|
||||||
// Get gets the address of a server for the RPC corresponding to ctx.
|
|
||||||
// i) If it returns a connected address, gRPC internals issues the RPC on the
|
|
||||||
// connection to this address;
|
|
||||||
// ii) If it returns an address on which the connection is under construction
|
|
||||||
// (initiated by Notify(...)) but not connected, gRPC internals
|
|
||||||
// * fails RPC if the RPC is fail-fast and connection is in the TransientFailure or
|
|
||||||
// Shutdown state;
|
|
||||||
// or
|
|
||||||
// * issues RPC on the connection otherwise.
|
|
||||||
// iii) If it returns an address on which the connection does not exist, gRPC
|
|
||||||
// internals treats it as an error and will fail the corresponding RPC.
|
|
||||||
//
|
|
||||||
// Therefore, the following is the recommended rule when writing a custom Balancer.
|
|
||||||
// If opts.BlockingWait is true, it should return a connected address or
|
|
||||||
// block if there is no connected address. It should respect the timeout or
|
|
||||||
// cancellation of ctx when blocking. If opts.BlockingWait is false (for fail-fast
|
|
||||||
// RPCs), it should return an address it has notified via Notify(...) immediately
|
|
||||||
// instead of blocking.
|
|
||||||
//
|
|
||||||
// The function returns put which is called once the rpc has completed or failed.
|
|
||||||
// put can collect and report RPC stats to a remote load balancer.
|
|
||||||
//
|
|
||||||
// This function should only return the errors Balancer cannot recover by itself.
|
|
||||||
// gRPC internals will fail the RPC if an error is returned.
|
|
||||||
Get(ctx context.Context, opts BalancerGetOptions) (addr Address, put func(), err error)
|
|
||||||
// Notify returns a channel that is used by gRPC internals to watch the addresses
|
|
||||||
// gRPC needs to connect. The addresses might be from a name resolver or remote
|
|
||||||
// load balancer. gRPC internals will compare it with the existing connected
|
|
||||||
// addresses. If the address Balancer notified is not in the existing connected
|
|
||||||
// addresses, gRPC starts to connect the address. If an address in the existing
|
|
||||||
// connected addresses is not in the notification list, the corresponding connection
|
|
||||||
// is shutdown gracefully. Otherwise, there are no operations to take. Note that
|
|
||||||
// the Address slice must be the full list of the Addresses which should be connected.
|
|
||||||
// It is NOT delta.
|
|
||||||
Notify() <-chan []Address
|
|
||||||
// Close shuts down the balancer.
|
|
||||||
Close() error
|
|
||||||
}
|
|
||||||
|
|
||||||
// RoundRobin returns a Balancer that selects addresses round-robin. It uses r to watch
|
|
||||||
// the name resolution updates and updates the addresses available correspondingly.
|
|
||||||
//
|
|
||||||
// Deprecated: please use package balancer/roundrobin. May be removed in a future 1.x release.
|
|
||||||
func RoundRobin(r naming.Resolver) Balancer {
|
|
||||||
return &roundRobin{r: r}
|
|
||||||
}
|
|
||||||
|
|
||||||
type addrInfo struct {
|
|
||||||
addr Address
|
|
||||||
connected bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type roundRobin struct {
|
|
||||||
r naming.Resolver
|
|
||||||
w naming.Watcher
|
|
||||||
addrs []*addrInfo // all the addresses the client should potentially connect
|
|
||||||
mu sync.Mutex
|
|
||||||
addrCh chan []Address // the channel to notify gRPC internals the list of addresses the client should connect to.
|
|
||||||
next int // index of the next address to return for Get()
|
|
||||||
waitCh chan struct{} // the channel to block when there is no connected address available
|
|
||||||
done bool // The Balancer is closed.
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rr *roundRobin) watchAddrUpdates() error {
|
|
||||||
updates, err := rr.w.Next()
|
|
||||||
if err != nil {
|
|
||||||
grpclog.Warningf("grpc: the naming watcher stops working due to %v.", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
rr.mu.Lock()
|
|
||||||
defer rr.mu.Unlock()
|
|
||||||
for _, update := range updates {
|
|
||||||
addr := Address{
|
|
||||||
Addr: update.Addr,
|
|
||||||
Metadata: update.Metadata,
|
|
||||||
}
|
|
||||||
switch update.Op {
|
|
||||||
case naming.Add:
|
|
||||||
var exist bool
|
|
||||||
for _, v := range rr.addrs {
|
|
||||||
if addr == v.addr {
|
|
||||||
exist = true
|
|
||||||
grpclog.Infoln("grpc: The name resolver wanted to add an existing address: ", addr)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if exist {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
rr.addrs = append(rr.addrs, &addrInfo{addr: addr})
|
|
||||||
case naming.Delete:
|
|
||||||
for i, v := range rr.addrs {
|
|
||||||
if addr == v.addr {
|
|
||||||
copy(rr.addrs[i:], rr.addrs[i+1:])
|
|
||||||
rr.addrs = rr.addrs[:len(rr.addrs)-1]
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
grpclog.Errorln("Unknown update.Op ", update.Op)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Make a copy of rr.addrs and write it onto rr.addrCh so that gRPC internals gets notified.
|
|
||||||
open := make([]Address, len(rr.addrs))
|
|
||||||
for i, v := range rr.addrs {
|
|
||||||
open[i] = v.addr
|
|
||||||
}
|
|
||||||
if rr.done {
|
|
||||||
return ErrClientConnClosing
|
|
||||||
}
|
|
||||||
select {
|
|
||||||
case <-rr.addrCh:
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
rr.addrCh <- open
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rr *roundRobin) Start(target string, config BalancerConfig) error {
|
|
||||||
rr.mu.Lock()
|
|
||||||
defer rr.mu.Unlock()
|
|
||||||
if rr.done {
|
|
||||||
return ErrClientConnClosing
|
|
||||||
}
|
|
||||||
if rr.r == nil {
|
|
||||||
// If there is no name resolver installed, it is not needed to
|
|
||||||
// do name resolution. In this case, target is added into rr.addrs
|
|
||||||
// as the only address available and rr.addrCh stays nil.
|
|
||||||
rr.addrs = append(rr.addrs, &addrInfo{addr: Address{Addr: target}})
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
w, err := rr.r.Resolve(target)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
rr.w = w
|
|
||||||
rr.addrCh = make(chan []Address, 1)
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
if err := rr.watchAddrUpdates(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Up sets the connected state of addr and sends notification if there are pending
|
|
||||||
// Get() calls.
|
|
||||||
func (rr *roundRobin) Up(addr Address) func(error) {
|
|
||||||
rr.mu.Lock()
|
|
||||||
defer rr.mu.Unlock()
|
|
||||||
var cnt int
|
|
||||||
for _, a := range rr.addrs {
|
|
||||||
if a.addr == addr {
|
|
||||||
if a.connected {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
a.connected = true
|
|
||||||
}
|
|
||||||
if a.connected {
|
|
||||||
cnt++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// addr is only one which is connected. Notify the Get() callers who are blocking.
|
|
||||||
if cnt == 1 && rr.waitCh != nil {
|
|
||||||
close(rr.waitCh)
|
|
||||||
rr.waitCh = nil
|
|
||||||
}
|
|
||||||
return func(err error) {
|
|
||||||
rr.down(addr, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// down unsets the connected state of addr.
|
|
||||||
func (rr *roundRobin) down(addr Address, err error) {
|
|
||||||
rr.mu.Lock()
|
|
||||||
defer rr.mu.Unlock()
|
|
||||||
for _, a := range rr.addrs {
|
|
||||||
if addr == a.addr {
|
|
||||||
a.connected = false
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get returns the next addr in the rotation.
|
|
||||||
func (rr *roundRobin) Get(ctx context.Context, opts BalancerGetOptions) (addr Address, put func(), err error) {
|
|
||||||
var ch chan struct{}
|
|
||||||
rr.mu.Lock()
|
|
||||||
if rr.done {
|
|
||||||
rr.mu.Unlock()
|
|
||||||
err = ErrClientConnClosing
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(rr.addrs) > 0 {
|
|
||||||
if rr.next >= len(rr.addrs) {
|
|
||||||
rr.next = 0
|
|
||||||
}
|
|
||||||
next := rr.next
|
|
||||||
for {
|
|
||||||
a := rr.addrs[next]
|
|
||||||
next = (next + 1) % len(rr.addrs)
|
|
||||||
if a.connected {
|
|
||||||
addr = a.addr
|
|
||||||
rr.next = next
|
|
||||||
rr.mu.Unlock()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if next == rr.next {
|
|
||||||
// Has iterated all the possible address but none is connected.
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !opts.BlockingWait {
|
|
||||||
if len(rr.addrs) == 0 {
|
|
||||||
rr.mu.Unlock()
|
|
||||||
err = status.Errorf(codes.Unavailable, "there is no address available")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Returns the next addr on rr.addrs for failfast RPCs.
|
|
||||||
addr = rr.addrs[rr.next].addr
|
|
||||||
rr.next++
|
|
||||||
rr.mu.Unlock()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Wait on rr.waitCh for non-failfast RPCs.
|
|
||||||
if rr.waitCh == nil {
|
|
||||||
ch = make(chan struct{})
|
|
||||||
rr.waitCh = ch
|
|
||||||
} else {
|
|
||||||
ch = rr.waitCh
|
|
||||||
}
|
|
||||||
rr.mu.Unlock()
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
err = ctx.Err()
|
|
||||||
return
|
|
||||||
case <-ch:
|
|
||||||
rr.mu.Lock()
|
|
||||||
if rr.done {
|
|
||||||
rr.mu.Unlock()
|
|
||||||
err = ErrClientConnClosing
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(rr.addrs) > 0 {
|
|
||||||
if rr.next >= len(rr.addrs) {
|
|
||||||
rr.next = 0
|
|
||||||
}
|
|
||||||
next := rr.next
|
|
||||||
for {
|
|
||||||
a := rr.addrs[next]
|
|
||||||
next = (next + 1) % len(rr.addrs)
|
|
||||||
if a.connected {
|
|
||||||
addr = a.addr
|
|
||||||
rr.next = next
|
|
||||||
rr.mu.Unlock()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if next == rr.next {
|
|
||||||
// Has iterated all the possible address but none is connected.
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// The newly added addr got removed by Down() again.
|
|
||||||
if rr.waitCh == nil {
|
|
||||||
ch = make(chan struct{})
|
|
||||||
rr.waitCh = ch
|
|
||||||
} else {
|
|
||||||
ch = rr.waitCh
|
|
||||||
}
|
|
||||||
rr.mu.Unlock()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rr *roundRobin) Notify() <-chan []Address {
|
|
||||||
return rr.addrCh
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rr *roundRobin) Close() error {
|
|
||||||
rr.mu.Lock()
|
|
||||||
defer rr.mu.Unlock()
|
|
||||||
if rr.done {
|
|
||||||
return errBalancerClosed
|
|
||||||
}
|
|
||||||
rr.done = true
|
|
||||||
if rr.w != nil {
|
|
||||||
rr.w.Close()
|
|
||||||
}
|
|
||||||
if rr.waitCh != nil {
|
|
||||||
close(rr.waitCh)
|
|
||||||
rr.waitCh = nil
|
|
||||||
}
|
|
||||||
if rr.addrCh != nil {
|
|
||||||
close(rr.addrCh)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// pickFirst is used to test multi-addresses in one addrConn in which all addresses share the same addrConn.
|
|
||||||
// It is a wrapper around roundRobin balancer. The logic of all methods works fine because balancer.Get()
|
|
||||||
// returns the only address Up by resetTransport().
|
|
||||||
type pickFirst struct {
|
|
||||||
*roundRobin
|
|
||||||
}
|
|
|
@ -101,6 +101,9 @@ type SubConn interface {
|
||||||
// a new connection will be created.
|
// a new connection will be created.
|
||||||
//
|
//
|
||||||
// This will trigger a state transition for the SubConn.
|
// This will trigger a state transition for the SubConn.
|
||||||
|
//
|
||||||
|
// Deprecated: This method is now part of the ClientConn interface and will
|
||||||
|
// eventually be removed from here.
|
||||||
UpdateAddresses([]resolver.Address)
|
UpdateAddresses([]resolver.Address)
|
||||||
// Connect starts the connecting for this SubConn.
|
// Connect starts the connecting for this SubConn.
|
||||||
Connect()
|
Connect()
|
||||||
|
@ -111,6 +114,9 @@ type NewSubConnOptions struct {
|
||||||
// CredsBundle is the credentials bundle that will be used in the created
|
// CredsBundle is the credentials bundle that will be used in the created
|
||||||
// SubConn. If it's nil, the original creds from grpc DialOptions will be
|
// SubConn. If it's nil, the original creds from grpc DialOptions will be
|
||||||
// used.
|
// used.
|
||||||
|
//
|
||||||
|
// Deprecated: Use the Attributes field in resolver.Address to pass
|
||||||
|
// arbitrary data to the credential handshaker.
|
||||||
CredsBundle credentials.Bundle
|
CredsBundle credentials.Bundle
|
||||||
// HealthCheckEnabled indicates whether health check service should be
|
// HealthCheckEnabled indicates whether health check service should be
|
||||||
// enabled on this SubConn
|
// enabled on this SubConn
|
||||||
|
@ -123,7 +129,7 @@ type State struct {
|
||||||
// determine the state of the ClientConn.
|
// determine the state of the ClientConn.
|
||||||
ConnectivityState connectivity.State
|
ConnectivityState connectivity.State
|
||||||
// Picker is used to choose connections (SubConns) for RPCs.
|
// Picker is used to choose connections (SubConns) for RPCs.
|
||||||
Picker V2Picker
|
Picker Picker
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClientConn represents a gRPC ClientConn.
|
// ClientConn represents a gRPC ClientConn.
|
||||||
|
@ -140,21 +146,19 @@ type ClientConn interface {
|
||||||
// RemoveSubConn removes the SubConn from ClientConn.
|
// RemoveSubConn removes the SubConn from ClientConn.
|
||||||
// The SubConn will be shutdown.
|
// The SubConn will be shutdown.
|
||||||
RemoveSubConn(SubConn)
|
RemoveSubConn(SubConn)
|
||||||
|
// UpdateAddresses updates the addresses used in the passed in SubConn.
|
||||||
// UpdateBalancerState is called by balancer to notify gRPC that some internal
|
// gRPC checks if the currently connected address is still in the new list.
|
||||||
// state in balancer has changed.
|
// If so, the connection will be kept. Else, the connection will be
|
||||||
|
// gracefully closed, and a new connection will be created.
|
||||||
//
|
//
|
||||||
// gRPC will update the connectivity state of the ClientConn, and will call pick
|
// This will trigger a state transition for the SubConn.
|
||||||
// on the new picker to pick new SubConn.
|
UpdateAddresses(SubConn, []resolver.Address)
|
||||||
//
|
|
||||||
// Deprecated: use UpdateState instead
|
|
||||||
UpdateBalancerState(s connectivity.State, p Picker)
|
|
||||||
|
|
||||||
// UpdateState notifies gRPC that the balancer's internal state has
|
// UpdateState notifies gRPC that the balancer's internal state has
|
||||||
// changed.
|
// changed.
|
||||||
//
|
//
|
||||||
// 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
|
||||||
// on the new picker to pick new SubConns.
|
// Pick on the new Picker to pick new SubConns.
|
||||||
UpdateState(State)
|
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.
|
||||||
|
@ -180,6 +184,10 @@ type BuildOptions struct {
|
||||||
Dialer func(context.Context, string) (net.Conn, error)
|
Dialer func(context.Context, string) (net.Conn, error)
|
||||||
// ChannelzParentID is the entity parent's channelz unique identification number.
|
// ChannelzParentID is the entity parent's channelz unique identification number.
|
||||||
ChannelzParentID int64
|
ChannelzParentID int64
|
||||||
|
// CustomUserAgent is the custom user agent set on the parent ClientConn.
|
||||||
|
// The balancer should set the same custom user agent if it creates a
|
||||||
|
// ClientConn.
|
||||||
|
CustomUserAgent string
|
||||||
// Target contains the parsed address info of the dial target. It is the same resolver.Target as
|
// Target contains the parsed address info of the dial target. It is the same resolver.Target as
|
||||||
// passed to the resolver.
|
// passed to the resolver.
|
||||||
// See the documentation for the resolver.Target type for details about what it contains.
|
// See the documentation for the resolver.Target type for details about what it contains.
|
||||||
|
@ -232,56 +240,17 @@ type DoneInfo struct {
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// ErrNoSubConnAvailable indicates no SubConn is available for pick().
|
// ErrNoSubConnAvailable indicates no SubConn is available for pick().
|
||||||
// gRPC will block the RPC until a new picker is available via UpdateBalancerState().
|
// gRPC will block the RPC until a new picker is available via UpdateState().
|
||||||
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 = TransientFailureError(errors.New("all SubConns are in TransientFailure"))
|
//
|
||||||
|
// Deprecated: return an appropriate error based on the last resolution or
|
||||||
|
// connection attempt instead. The behavior is the same for any non-gRPC
|
||||||
|
// status error.
|
||||||
|
ErrTransientFailure = errors.New("all SubConns are in TransientFailure")
|
||||||
)
|
)
|
||||||
|
|
||||||
// Picker 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().
|
|
||||||
//
|
|
||||||
// Deprecated: use V2Picker instead
|
|
||||||
type Picker interface {
|
|
||||||
// Pick returns the SubConn to be used to send the RPC.
|
|
||||||
// The returned SubConn must be one returned by NewSubConn().
|
|
||||||
//
|
|
||||||
// This functions is expected to return:
|
|
||||||
// - a SubConn that is known to be READY;
|
|
||||||
// - ErrNoSubConnAvailable if no SubConn is available, but progress is being
|
|
||||||
// made (for example, some SubConn is in CONNECTING mode);
|
|
||||||
// - other errors if no active connecting is happening (for example, all SubConn
|
|
||||||
// are in TRANSIENT_FAILURE mode).
|
|
||||||
//
|
|
||||||
// If a SubConn is returned:
|
|
||||||
// - If it is READY, gRPC will send the RPC on it;
|
|
||||||
// - If it is not ready, or becomes not ready after it's returned, gRPC will
|
|
||||||
// block until UpdateBalancerState() is called and will call pick on the
|
|
||||||
// new picker. The done function returned from Pick(), if not nil, will be
|
|
||||||
// called with nil error, no bytes sent and no bytes received.
|
|
||||||
//
|
|
||||||
// If the returned error is not nil:
|
|
||||||
// - If the error is ErrNoSubConnAvailable, gRPC will block until UpdateBalancerState()
|
|
||||||
// - If the error is ErrTransientFailure or implements IsTransientFailure()
|
|
||||||
// bool, returning true:
|
|
||||||
// - If the RPC is wait-for-ready, gRPC will block until UpdateBalancerState()
|
|
||||||
// is called to pick again;
|
|
||||||
// - Otherwise, RPC will fail with unavailable error.
|
|
||||||
// - Else (error is other non-nil 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,
|
|
||||||
// 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
|
|
||||||
// doesn't care about the RPC status.
|
|
||||||
Pick(ctx context.Context, info PickInfo) (conn SubConn, done func(DoneInfo), err error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// PickResult contains information related to a connection chosen for an RPC.
|
// PickResult contains information related to a connection chosen for an RPC.
|
||||||
type PickResult struct {
|
type PickResult struct {
|
||||||
// SubConn is the connection to use for this pick, if its state is Ready.
|
// SubConn is the connection to use for this pick, if its state is Ready.
|
||||||
|
@ -297,24 +266,19 @@ type PickResult struct {
|
||||||
Done func(DoneInfo)
|
Done func(DoneInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
type transientFailureError struct {
|
// TransientFailureError returns e. It exists for backward compatibility and
|
||||||
error
|
// will be deleted soon.
|
||||||
}
|
//
|
||||||
|
// Deprecated: no longer necessary, picker errors are treated this way by
|
||||||
|
// default.
|
||||||
|
func TransientFailureError(e error) error { return e }
|
||||||
|
|
||||||
func (e *transientFailureError) IsTransientFailure() bool { return true }
|
// Picker is used by gRPC to pick a SubConn to send an RPC.
|
||||||
|
|
||||||
// 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
|
// Balancer is expected to generate a new picker from its snapshot every time its
|
||||||
// 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.UpdateState().
|
||||||
type V2Picker interface {
|
type Picker interface {
|
||||||
// Pick returns the connection to use for this RPC and related information.
|
// 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
|
// Pick should not block. If the balancer needs to do I/O or any blocking
|
||||||
|
@ -327,14 +291,13 @@ type V2Picker interface {
|
||||||
// - If the error is ErrNoSubConnAvailable, gRPC will block until a new
|
// - If the error is ErrNoSubConnAvailable, gRPC will block until a new
|
||||||
// Picker is provided by the balancer (using ClientConn.UpdateState).
|
// Picker is provided by the balancer (using ClientConn.UpdateState).
|
||||||
//
|
//
|
||||||
// - If the error implements IsTransientFailure() bool, returning true,
|
// - If the error is a status error (implemented by the grpc/status
|
||||||
// wait for ready RPCs will wait, but non-wait for ready RPCs will be
|
// package), gRPC will terminate the RPC with the code and message
|
||||||
// terminated with this error's Error() string and status code
|
// provided.
|
||||||
// Unavailable.
|
|
||||||
//
|
//
|
||||||
// - Any other errors terminate all RPCs with the code and message
|
// - For all other errors, wait for ready RPCs will wait, but non-wait for
|
||||||
// provided. If the error is not a status error, it will be converted by
|
// ready RPCs will be terminated with this error's Error() string and
|
||||||
// gRPC to a status error with code Unknown.
|
// status code Unavailable.
|
||||||
Pick(info PickInfo) (PickResult, error)
|
Pick(info PickInfo) (PickResult, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -343,29 +306,21 @@ type V2Picker interface {
|
||||||
//
|
//
|
||||||
// It also generates and updates the Picker used by gRPC to pick SubConns for RPCs.
|
// It also generates and updates the Picker used by gRPC to pick SubConns for RPCs.
|
||||||
//
|
//
|
||||||
// HandleSubConnectionStateChange, HandleResolvedAddrs and Close are guaranteed
|
// UpdateClientConnState, ResolverError, UpdateSubConnState, and Close are
|
||||||
// to be called synchronously from the same goroutine.
|
// guaranteed to be called synchronously from the same goroutine. There's no
|
||||||
// There's no guarantee on picker.Pick, it may be called anytime.
|
// guarantee on picker.Pick, it may be called anytime.
|
||||||
type Balancer interface {
|
type Balancer interface {
|
||||||
// HandleSubConnStateChange is called by gRPC when the connectivity state
|
// UpdateClientConnState is called by gRPC when the state of the ClientConn
|
||||||
// of sc has changed.
|
// changes. If the error returned is ErrBadResolverState, the ClientConn
|
||||||
// Balancer is expected to aggregate all the state of SubConn and report
|
// will begin calling ResolveNow on the active name resolver with
|
||||||
// that back to gRPC.
|
// exponential backoff until a subsequent call to UpdateClientConnState
|
||||||
// Balancer should also generate and update Pickers when its internal state has
|
// returns a nil error. Any other errors are currently ignored.
|
||||||
// been changed by the new state.
|
UpdateClientConnState(ClientConnState) error
|
||||||
//
|
// ResolverError is called by gRPC when the name resolver reports an error.
|
||||||
// Deprecated: if V2Balancer is implemented by the Balancer,
|
ResolverError(error)
|
||||||
// UpdateSubConnState will be called instead.
|
// UpdateSubConnState is called by gRPC when the state of a SubConn
|
||||||
HandleSubConnStateChange(sc SubConn, state connectivity.State)
|
// changes.
|
||||||
// HandleResolvedAddrs is called by gRPC to send updated resolved addresses to
|
UpdateSubConnState(SubConn, SubConnState)
|
||||||
// balancers.
|
|
||||||
// Balancer can create new SubConn or remove SubConn with the addresses.
|
|
||||||
// An empty address slice and a non-nil error will be passed if the resolver returns
|
|
||||||
// non-nil error to gRPC.
|
|
||||||
//
|
|
||||||
// Deprecated: if V2Balancer is implemented by the Balancer,
|
|
||||||
// UpdateClientConnState will be called instead.
|
|
||||||
HandleResolvedAddrs([]resolver.Address, error)
|
|
||||||
// Close closes the balancer. The balancer is not required to call
|
// Close closes the balancer. The balancer is not required to call
|
||||||
// ClientConn.RemoveSubConn for its existing SubConns.
|
// ClientConn.RemoveSubConn for its existing SubConns.
|
||||||
Close()
|
Close()
|
||||||
|
@ -393,27 +348,6 @@ type ClientConnState struct {
|
||||||
// problem with the provided name resolver data.
|
// problem with the provided name resolver data.
|
||||||
var ErrBadResolverState = errors.New("bad resolver state")
|
var ErrBadResolverState = errors.New("bad resolver state")
|
||||||
|
|
||||||
// V2Balancer is defined for documentation purposes. If a Balancer also
|
|
||||||
// implements V2Balancer, its UpdateClientConnState method will be called
|
|
||||||
// instead of HandleResolvedAddrs and its UpdateSubConnState will be called
|
|
||||||
// instead of HandleSubConnStateChange.
|
|
||||||
type V2Balancer interface {
|
|
||||||
// UpdateClientConnState is called by gRPC when the state of the ClientConn
|
|
||||||
// changes. If the error returned is ErrBadResolverState, the ClientConn
|
|
||||||
// will begin calling ResolveNow on the active name resolver with
|
|
||||||
// exponential backoff until a subsequent call to UpdateClientConnState
|
|
||||||
// returns a nil error. Any other errors are currently ignored.
|
|
||||||
UpdateClientConnState(ClientConnState) error
|
|
||||||
// ResolverError is called by gRPC when the name resolver reports an error.
|
|
||||||
ResolverError(error)
|
|
||||||
// UpdateSubConnState is called by gRPC when the state of a SubConn
|
|
||||||
// changes.
|
|
||||||
UpdateSubConnState(SubConn, SubConnState)
|
|
||||||
// Close closes the balancer. The balancer is not required to call
|
|
||||||
// ClientConn.RemoveSubConn for its existing SubConns.
|
|
||||||
Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ConnectivityStateEvaluator takes the connectivity states of multiple SubConns
|
// ConnectivityStateEvaluator takes the connectivity states of multiple SubConns
|
||||||
// and returns one aggregated connectivity state.
|
// and returns one aggregated connectivity state.
|
||||||
//
|
//
|
||||||
|
|
|
@ -19,20 +19,21 @@
|
||||||
package base
|
package base
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"google.golang.org/grpc/attributes"
|
||||||
"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/grpclog"
|
||||||
"google.golang.org/grpc/resolver"
|
"google.golang.org/grpc/resolver"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var logger = grpclog.Component("balancer")
|
||||||
|
|
||||||
type baseBuilder struct {
|
type baseBuilder struct {
|
||||||
name string
|
name string
|
||||||
pickerBuilder PickerBuilder
|
pickerBuilder PickerBuilder
|
||||||
v2PickerBuilder V2PickerBuilder
|
|
||||||
config Config
|
config Config
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,9 +41,8 @@ func (bb *baseBuilder) Build(cc balancer.ClientConn, opt balancer.BuildOptions)
|
||||||
bal := &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]subConnInfo),
|
||||||
scStates: make(map[balancer.SubConn]connectivity.State),
|
scStates: make(map[balancer.SubConn]connectivity.State),
|
||||||
csEvltr: &balancer.ConnectivityStateEvaluator{},
|
csEvltr: &balancer.ConnectivityStateEvaluator{},
|
||||||
config: bb.config,
|
config: bb.config,
|
||||||
|
@ -50,11 +50,7 @@ func (bb *baseBuilder) Build(cc balancer.ClientConn, opt balancer.BuildOptions)
|
||||||
// Initialize picker to a picker that always returns
|
// Initialize picker to a picker that always returns
|
||||||
// ErrNoSubConnAvailable, because when state of a SubConn changes, we
|
// ErrNoSubConnAvailable, because when state of a SubConn changes, we
|
||||||
// may call UpdateState with this picker.
|
// may call UpdateState with this picker.
|
||||||
if bb.pickerBuilder != nil {
|
|
||||||
bal.picker = NewErrPicker(balancer.ErrNoSubConnAvailable)
|
bal.picker = NewErrPicker(balancer.ErrNoSubConnAvailable)
|
||||||
} else {
|
|
||||||
bal.v2Picker = NewErrPickerV2(balancer.ErrNoSubConnAvailable)
|
|
||||||
}
|
|
||||||
return bal
|
return bal
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,88 +58,111 @@ func (bb *baseBuilder) Name() string {
|
||||||
return bb.name
|
return bb.name
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ balancer.V2Balancer = (*baseBalancer)(nil) // Assert that we implement V2Balancer
|
type subConnInfo struct {
|
||||||
|
subConn balancer.SubConn
|
||||||
|
attrs *attributes.Attributes
|
||||||
|
}
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
subConns map[resolver.Address]balancer.SubConn
|
subConns map[resolver.Address]subConnInfo // `attributes` is stripped from the keys of this map (the addresses)
|
||||||
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
|
resolverErr error // the last error reported by the resolver; cleared on successful resolution
|
||||||
connErr error // the last connection error; cleared upon leaving TransientFailure
|
connErr error // the last connection error; cleared upon leaving TransientFailure
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *baseBalancer) HandleResolvedAddrs(addrs []resolver.Address, err error) {
|
|
||||||
panic("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *baseBalancer) ResolverError(err error) {
|
func (b *baseBalancer) ResolverError(err error) {
|
||||||
b.resolverErr = err
|
b.resolverErr = err
|
||||||
if len(b.subConns) == 0 {
|
if len(b.subConns) == 0 {
|
||||||
b.state = connectivity.TransientFailure
|
b.state = connectivity.TransientFailure
|
||||||
}
|
}
|
||||||
|
|
||||||
if b.state != connectivity.TransientFailure {
|
if b.state != connectivity.TransientFailure {
|
||||||
// The picker will not change since the balancer does not currently
|
// The picker will not change since the balancer does not currently
|
||||||
// report an error.
|
// report an error.
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
b.regeneratePicker()
|
b.regeneratePicker()
|
||||||
if b.picker != nil {
|
|
||||||
b.cc.UpdateBalancerState(b.state, b.picker)
|
|
||||||
} else {
|
|
||||||
b.cc.UpdateState(balancer.State{
|
b.cc.UpdateState(balancer.State{
|
||||||
ConnectivityState: b.state,
|
ConnectivityState: b.state,
|
||||||
Picker: b.v2Picker,
|
Picker: b.picker,
|
||||||
})
|
})
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *baseBalancer) UpdateClientConnState(s balancer.ClientConnState) error {
|
func (b *baseBalancer) UpdateClientConnState(s balancer.ClientConnState) error {
|
||||||
// TODO: handle s.ResolverState.Err (log if not nil) once implemented.
|
|
||||||
// TODO: handle s.ResolverState.ServiceConfig?
|
// TODO: handle s.ResolverState.ServiceConfig?
|
||||||
if grpclog.V(2) {
|
if logger.V(2) {
|
||||||
grpclog.Infoln("base.baseBalancer: got new ClientConn state: ", s)
|
logger.Info("base.baseBalancer: got new ClientConn state: ", s)
|
||||||
}
|
|
||||||
if len(s.ResolverState.Addresses) == 0 {
|
|
||||||
b.ResolverError(errors.New("produced zero addresses"))
|
|
||||||
return balancer.ErrBadResolverState
|
|
||||||
}
|
}
|
||||||
// Successful resolution; clear resolver error and ensure we return nil.
|
// Successful resolution; clear resolver error and ensure we return nil.
|
||||||
b.resolverErr = 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 {
|
||||||
addrsSet[a] = struct{}{}
|
// Strip attributes from addresses before using them as map keys. So
|
||||||
if _, ok := b.subConns[a]; !ok {
|
// that when two addresses only differ in attributes pointers (but with
|
||||||
|
// the same attribute content), they are considered the same address.
|
||||||
|
//
|
||||||
|
// Note that this doesn't handle the case where the attribute content is
|
||||||
|
// different. So if users want to set different attributes to create
|
||||||
|
// duplicate connections to the same backend, it doesn't work. This is
|
||||||
|
// fine for now, because duplicate is done by setting Metadata today.
|
||||||
|
//
|
||||||
|
// TODO: read attributes to handle duplicate connections.
|
||||||
|
aNoAttrs := a
|
||||||
|
aNoAttrs.Attributes = nil
|
||||||
|
addrsSet[aNoAttrs] = struct{}{}
|
||||||
|
if scInfo, ok := b.subConns[aNoAttrs]; !ok {
|
||||||
// a is a new address (not existing in b.subConns).
|
// a is a new address (not existing in b.subConns).
|
||||||
|
//
|
||||||
|
// When creating SubConn, the original address with attributes is
|
||||||
|
// passed through. So that connection configurations in attributes
|
||||||
|
// (like creds) will be used.
|
||||||
sc, err := b.cc.NewSubConn([]resolver.Address{a}, balancer.NewSubConnOptions{HealthCheckEnabled: b.config.HealthCheck})
|
sc, err := b.cc.NewSubConn([]resolver.Address{a}, balancer.NewSubConnOptions{HealthCheckEnabled: b.config.HealthCheck})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
grpclog.Warningf("base.baseBalancer: failed to create new SubConn: %v", err)
|
logger.Warningf("base.baseBalancer: failed to create new SubConn: %v", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
b.subConns[a] = sc
|
b.subConns[aNoAttrs] = subConnInfo{subConn: sc, attrs: a.Attributes}
|
||||||
b.scStates[sc] = connectivity.Idle
|
b.scStates[sc] = connectivity.Idle
|
||||||
sc.Connect()
|
sc.Connect()
|
||||||
|
} else {
|
||||||
|
// Always update the subconn's address in case the attributes
|
||||||
|
// changed.
|
||||||
|
//
|
||||||
|
// The SubConn does a reflect.DeepEqual of the new and old
|
||||||
|
// addresses. So this is a noop if the current address is the same
|
||||||
|
// as the old one (including attributes).
|
||||||
|
scInfo.attrs = a.Attributes
|
||||||
|
b.subConns[aNoAttrs] = scInfo
|
||||||
|
b.cc.UpdateAddresses(scInfo.subConn, []resolver.Address{a})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for a, sc := range b.subConns {
|
for a, scInfo := range b.subConns {
|
||||||
// a was removed by resolver.
|
// a was removed by resolver.
|
||||||
if _, ok := addrsSet[a]; !ok {
|
if _, ok := addrsSet[a]; !ok {
|
||||||
b.cc.RemoveSubConn(sc)
|
b.cc.RemoveSubConn(scInfo.subConn)
|
||||||
delete(b.subConns, a)
|
delete(b.subConns, a)
|
||||||
// Keep the state of this sc in b.scStates until sc's state becomes Shutdown.
|
// Keep the state of this sc in b.scStates until sc's state becomes Shutdown.
|
||||||
// The entry will be deleted in HandleSubConnStateChange.
|
// The entry will be deleted in UpdateSubConnState.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,52 +186,39 @@ func (b *baseBalancer) mergeErrors() error {
|
||||||
// - 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(b.mergeErrors())
|
||||||
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)
|
|
||||||
|
|
||||||
// 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[addr] = sc
|
|
||||||
}
|
|
||||||
}
|
|
||||||
b.picker = b.pickerBuilder.Build(readySCs)
|
|
||||||
} else {
|
|
||||||
readySCs := make(map[balancer.SubConn]SubConnInfo)
|
readySCs := make(map[balancer.SubConn]SubConnInfo)
|
||||||
|
|
||||||
// Filter out all ready SCs from full subConn map.
|
// Filter out all ready SCs from full subConn map.
|
||||||
for addr, sc := range b.subConns {
|
for addr, scInfo := range b.subConns {
|
||||||
if st, ok := b.scStates[sc]; ok && st == connectivity.Ready {
|
if st, ok := b.scStates[scInfo.subConn]; ok && st == connectivity.Ready {
|
||||||
readySCs[sc] = SubConnInfo{Address: addr}
|
addr.Attributes = scInfo.attrs
|
||||||
|
readySCs[scInfo.subConn] = SubConnInfo{Address: addr}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
b.v2Picker = b.v2PickerBuilder.Build(PickerBuildInfo{ReadySCs: readySCs})
|
b.picker = b.pickerBuilder.Build(PickerBuildInfo{ReadySCs: readySCs})
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *baseBalancer) HandleSubConnStateChange(sc balancer.SubConn, s connectivity.State) {
|
|
||||||
panic("not implemented")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *baseBalancer) UpdateSubConnState(sc balancer.SubConn, state balancer.SubConnState) {
|
func (b *baseBalancer) UpdateSubConnState(sc balancer.SubConn, state balancer.SubConnState) {
|
||||||
s := state.ConnectivityState
|
s := state.ConnectivityState
|
||||||
if grpclog.V(2) {
|
if logger.V(2) {
|
||||||
grpclog.Infof("base.baseBalancer: handle SubConn state change: %p, %v", sc, s)
|
logger.Infof("base.baseBalancer: handle SubConn state change: %p, %v", sc, s)
|
||||||
}
|
}
|
||||||
oldS, ok := b.scStates[sc]
|
oldS, ok := b.scStates[sc]
|
||||||
if !ok {
|
if !ok {
|
||||||
if grpclog.V(2) {
|
if logger.V(2) {
|
||||||
grpclog.Infof("base.baseBalancer: got state changes for an unknown SubConn: %p, %v", sc, s)
|
logger.Infof("base.baseBalancer: got state changes for an unknown SubConn: %p, %v", sc, s)
|
||||||
}
|
}
|
||||||
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:
|
||||||
|
@ -221,29 +227,23 @@ 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)
|
||||||
|
|
||||||
// Set or clear the last connection error accordingly.
|
|
||||||
b.connErr = state.ConnectionError
|
|
||||||
|
|
||||||
// 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.UpdateState(balancer.State{ConnectivityState: b.state, Picker: 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,
|
||||||
|
@ -251,28 +251,20 @@ func (b *baseBalancer) UpdateSubConnState(sc balancer.SubConn, state balancer.Su
|
||||||
func (b *baseBalancer) Close() {
|
func (b *baseBalancer) Close() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewErrPicker returns a picker that always returns err on Pick().
|
// NewErrPicker returns a Picker that always returns err on Pick().
|
||||||
func NewErrPicker(err error) balancer.Picker {
|
func NewErrPicker(err error) balancer.Picker {
|
||||||
return &errPicker{err: err}
|
return &errPicker{err: err}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewErrPickerV2 is temporarily defined for backward compatibility reasons.
|
||||||
|
//
|
||||||
|
// Deprecated: use NewErrPicker instead.
|
||||||
|
var NewErrPickerV2 = NewErrPicker
|
||||||
|
|
||||||
type errPicker struct {
|
type errPicker struct {
|
||||||
err error // Pick() always returns this err.
|
err error // Pick() always returns this err.
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *errPicker) Pick(context.Context, balancer.PickInfo) (balancer.SubConn, func(balancer.DoneInfo), error) {
|
func (p *errPicker) Pick(info balancer.PickInfo) (balancer.PickResult, error) {
|
||||||
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
|
return balancer.PickResult{}, p.err
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,15 +37,8 @@ import (
|
||||||
|
|
||||||
// PickerBuilder creates balancer.Picker.
|
// PickerBuilder creates balancer.Picker.
|
||||||
type PickerBuilder interface {
|
type PickerBuilder interface {
|
||||||
// Build takes a slice of ready SubConns, and returns a picker that will be
|
|
||||||
// used by gRPC to pick a SubConn.
|
|
||||||
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 returns a picker that will be used by gRPC to pick a SubConn.
|
||||||
Build(info PickerBuildInfo) balancer.V2Picker
|
Build(info PickerBuildInfo) balancer.Picker
|
||||||
}
|
}
|
||||||
|
|
||||||
// PickerBuildInfo contains information needed by the picker builder to
|
// PickerBuildInfo contains information needed by the picker builder to
|
||||||
|
@ -62,32 +55,17 @@ type SubConnInfo struct {
|
||||||
Address resolver.Address // the address used to create this SubConn
|
Address resolver.Address // the address used to create this SubConn
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBalancerBuilder returns a balancer builder. The balancers
|
|
||||||
// built by this builder will use the picker builder to build pickers.
|
|
||||||
func NewBalancerBuilder(name string, pb PickerBuilder) balancer.Builder {
|
|
||||||
return NewBalancerBuilderWithConfig(name, pb, Config{})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Config contains the config info about the base balancer builder.
|
// Config contains the config info about the base balancer builder.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
// HealthCheck indicates whether health checking should be enabled for this specific balancer.
|
// HealthCheck indicates whether health checking should be enabled for this specific balancer.
|
||||||
HealthCheck bool
|
HealthCheck bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBalancerBuilderWithConfig returns a base balancer builder configured by the provided config.
|
// NewBalancerBuilder returns a base balancer builder configured by the provided config.
|
||||||
func NewBalancerBuilderWithConfig(name string, pb PickerBuilder, config Config) balancer.Builder {
|
func NewBalancerBuilder(name string, pb PickerBuilder, config Config) balancer.Builder {
|
||||||
return &baseBuilder{
|
return &baseBuilder{
|
||||||
name: name,
|
name: name,
|
||||||
pickerBuilder: pb,
|
pickerBuilder: pb,
|
||||||
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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* 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 state declares grpclb types to be set by resolvers wishing to pass
|
||||||
|
// information to grpclb via resolver.State Attributes.
|
||||||
|
package state
|
||||||
|
|
||||||
|
import (
|
||||||
|
"google.golang.org/grpc/resolver"
|
||||||
|
)
|
||||||
|
|
||||||
|
// keyType is the key to use for storing State in Attributes.
|
||||||
|
type keyType string
|
||||||
|
|
||||||
|
const key = keyType("grpc.grpclb.state")
|
||||||
|
|
||||||
|
// State contains gRPCLB-relevant data passed from the name resolver.
|
||||||
|
type State struct {
|
||||||
|
// BalancerAddresses contains the remote load balancer address(es). If
|
||||||
|
// set, overrides any resolver-provided addresses with Type of GRPCLB.
|
||||||
|
BalancerAddresses []resolver.Address
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set returns a copy of the provided state with attributes containing s. s's
|
||||||
|
// data should not be mutated after calling Set.
|
||||||
|
func Set(state resolver.State, s *State) resolver.State {
|
||||||
|
state.Attributes = state.Attributes.WithValues(key, s)
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns the grpclb State in the resolver.State, or nil if not present.
|
||||||
|
// The returned data should not be mutated.
|
||||||
|
func Get(state resolver.State) *State {
|
||||||
|
s, _ := state.Attributes.Value(key).(*State)
|
||||||
|
return s
|
||||||
|
}
|
|
@ -33,9 +33,11 @@ import (
|
||||||
// Name is the name of round_robin balancer.
|
// Name is the name of round_robin balancer.
|
||||||
const Name = "round_robin"
|
const Name = "round_robin"
|
||||||
|
|
||||||
|
var logger = grpclog.Component("roundrobin")
|
||||||
|
|
||||||
// newBuilder creates a new roundrobin balancer builder.
|
// newBuilder creates a new roundrobin balancer builder.
|
||||||
func newBuilder() balancer.Builder {
|
func newBuilder() balancer.Builder {
|
||||||
return base.NewBalancerBuilderV2(Name, &rrPickerBuilder{}, base.Config{HealthCheck: true})
|
return base.NewBalancerBuilder(Name, &rrPickerBuilder{}, base.Config{HealthCheck: true})
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -44,10 +46,10 @@ func init() {
|
||||||
|
|
||||||
type rrPickerBuilder struct{}
|
type rrPickerBuilder struct{}
|
||||||
|
|
||||||
func (*rrPickerBuilder) Build(info base.PickerBuildInfo) balancer.V2Picker {
|
func (*rrPickerBuilder) Build(info base.PickerBuildInfo) balancer.Picker {
|
||||||
grpclog.Infof("roundrobinPicker: newPicker called with info: %v", info)
|
logger.Infof("roundrobinPicker: newPicker called with info: %v", info)
|
||||||
if len(info.ReadySCs) == 0 {
|
if len(info.ReadySCs) == 0 {
|
||||||
return base.NewErrPickerV2(balancer.ErrNoSubConnAvailable)
|
return base.NewErrPicker(balancer.ErrNoSubConnAvailable)
|
||||||
}
|
}
|
||||||
var scs []balancer.SubConn
|
var scs []balancer.SubConn
|
||||||
for sc := range info.ReadySCs {
|
for sc := range info.ReadySCs {
|
||||||
|
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
@ -74,11 +74,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 {
|
ccb.balancer.UpdateSubConnState(su.sc, balancer.SubConnState{ConnectivityState: su.state, ConnectionError: su.err})
|
||||||
ub.UpdateSubConnState(su.sc, balancer.SubConnState{ConnectivityState: su.state, ConnectionError: su.err})
|
|
||||||
} else {
|
|
||||||
ccb.balancer.HandleSubConnStateChange(su.sc, su.state)
|
|
||||||
}
|
|
||||||
ccb.balancerMu.Unlock()
|
ccb.balancerMu.Unlock()
|
||||||
case <-ccb.done.Done():
|
case <-ccb.done.Done():
|
||||||
}
|
}
|
||||||
|
@ -123,19 +119,13 @@ func (ccb *ccBalancerWrapper) handleSubConnStateChange(sc balancer.SubConn, s co
|
||||||
func (ccb *ccBalancerWrapper) updateClientConnState(ccs *balancer.ClientConnState) error {
|
func (ccb *ccBalancerWrapper) updateClientConnState(ccs *balancer.ClientConnState) error {
|
||||||
ccb.balancerMu.Lock()
|
ccb.balancerMu.Lock()
|
||||||
defer ccb.balancerMu.Unlock()
|
defer ccb.balancerMu.Unlock()
|
||||||
if ub, ok := ccb.balancer.(balancer.V2Balancer); ok {
|
return ccb.balancer.UpdateClientConnState(*ccs)
|
||||||
return ub.UpdateClientConnState(*ccs)
|
|
||||||
}
|
|
||||||
ccb.balancer.HandleResolvedAddrs(ccs.ResolverState.Addresses, nil)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ccb *ccBalancerWrapper) resolverError(err error) {
|
func (ccb *ccBalancerWrapper) resolverError(err error) {
|
||||||
if ub, ok := ccb.balancer.(balancer.V2Balancer); ok {
|
|
||||||
ccb.balancerMu.Lock()
|
ccb.balancerMu.Lock()
|
||||||
ub.ResolverError(err)
|
ccb.balancer.ResolverError(err)
|
||||||
ccb.balancerMu.Unlock()
|
ccb.balancerMu.Unlock()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ccb *ccBalancerWrapper) NewSubConn(addrs []resolver.Address, opts balancer.NewSubConnOptions) (balancer.SubConn, error) {
|
func (ccb *ccBalancerWrapper) NewSubConn(addrs []resolver.Address, opts balancer.NewSubConnOptions) (balancer.SubConn, error) {
|
||||||
|
@ -173,19 +163,12 @@ func (ccb *ccBalancerWrapper) RemoveSubConn(sc balancer.SubConn) {
|
||||||
ccb.cc.removeAddrConn(acbw.getAddrConn(), errConnDrain)
|
ccb.cc.removeAddrConn(acbw.getAddrConn(), errConnDrain)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ccb *ccBalancerWrapper) UpdateBalancerState(s connectivity.State, p balancer.Picker) {
|
func (ccb *ccBalancerWrapper) UpdateAddresses(sc balancer.SubConn, addrs []resolver.Address) {
|
||||||
ccb.mu.Lock()
|
acbw, ok := sc.(*acBalancerWrapper)
|
||||||
defer ccb.mu.Unlock()
|
if !ok {
|
||||||
if ccb.subConns == nil {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Update picker before updating state. Even though the ordering here does
|
acbw.UpdateAddresses(addrs)
|
||||||
// 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.updatePicker(p)
|
|
||||||
ccb.cc.csMgr.updateState(s)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ccb *ccBalancerWrapper) UpdateState(s balancer.State) {
|
func (ccb *ccBalancerWrapper) UpdateState(s balancer.State) {
|
||||||
|
@ -199,7 +182,7 @@ func (ccb *ccBalancerWrapper) UpdateState(s balancer.State) {
|
||||||
// case where we wait for ready and then perform an RPC. If the picker is
|
// 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 later, we could call the "connecting" picker when the state is
|
||||||
// updated, and then call the "ready" picker after the picker gets updated.
|
// updated, and then call the "ready" picker after the picker gets updated.
|
||||||
ccb.cc.blockingpicker.updatePickerV2(s.Picker)
|
ccb.cc.blockingpicker.updatePicker(s.Picker)
|
||||||
ccb.cc.csMgr.updateState(s.ConnectivityState)
|
ccb.cc.csMgr.updateState(s.ConnectivityState)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,7 +205,7 @@ func (acbw *acBalancerWrapper) UpdateAddresses(addrs []resolver.Address) {
|
||||||
acbw.mu.Lock()
|
acbw.mu.Lock()
|
||||||
defer acbw.mu.Unlock()
|
defer acbw.mu.Unlock()
|
||||||
if len(addrs) <= 0 {
|
if len(addrs) <= 0 {
|
||||||
acbw.ac.tearDown(errConnDrain)
|
acbw.ac.cc.removeAddrConn(acbw.ac, errConnDrain)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !acbw.ac.tryUpdateAddrs(addrs) {
|
if !acbw.ac.tryUpdateAddrs(addrs) {
|
||||||
|
@ -237,7 +220,7 @@ func (acbw *acBalancerWrapper) UpdateAddresses(addrs []resolver.Address) {
|
||||||
acbw.ac.acbw = nil
|
acbw.ac.acbw = nil
|
||||||
acbw.ac.mu.Unlock()
|
acbw.ac.mu.Unlock()
|
||||||
acState := acbw.ac.getState()
|
acState := acbw.ac.getState()
|
||||||
acbw.ac.tearDown(errConnDrain)
|
acbw.ac.cc.removeAddrConn(acbw.ac, errConnDrain)
|
||||||
|
|
||||||
if acState == connectivity.Shutdown {
|
if acState == connectivity.Shutdown {
|
||||||
return
|
return
|
||||||
|
@ -245,7 +228,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(logger, acbw.ac.channelzID, "acBalancerWrapper: UpdateAddresses: failed to newAddrConn: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
acbw.ac = ac
|
acbw.ac = ac
|
||||||
|
|
|
@ -1,334 +0,0 @@
|
||||||
/*
|
|
||||||
*
|
|
||||||
* Copyright 2017 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 grpc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"google.golang.org/grpc/balancer"
|
|
||||||
"google.golang.org/grpc/connectivity"
|
|
||||||
"google.golang.org/grpc/grpclog"
|
|
||||||
"google.golang.org/grpc/resolver"
|
|
||||||
)
|
|
||||||
|
|
||||||
type balancerWrapperBuilder struct {
|
|
||||||
b Balancer // The v1 balancer.
|
|
||||||
}
|
|
||||||
|
|
||||||
func (bwb *balancerWrapperBuilder) Build(cc balancer.ClientConn, opts balancer.BuildOptions) balancer.Balancer {
|
|
||||||
bwb.b.Start(opts.Target.Endpoint, BalancerConfig{
|
|
||||||
DialCreds: opts.DialCreds,
|
|
||||||
Dialer: opts.Dialer,
|
|
||||||
})
|
|
||||||
_, pickfirst := bwb.b.(*pickFirst)
|
|
||||||
bw := &balancerWrapper{
|
|
||||||
balancer: bwb.b,
|
|
||||||
pickfirst: pickfirst,
|
|
||||||
cc: cc,
|
|
||||||
targetAddr: opts.Target.Endpoint,
|
|
||||||
startCh: make(chan struct{}),
|
|
||||||
conns: make(map[resolver.Address]balancer.SubConn),
|
|
||||||
connSt: make(map[balancer.SubConn]*scState),
|
|
||||||
csEvltr: &balancer.ConnectivityStateEvaluator{},
|
|
||||||
state: connectivity.Idle,
|
|
||||||
}
|
|
||||||
cc.UpdateState(balancer.State{ConnectivityState: connectivity.Idle, Picker: bw})
|
|
||||||
go bw.lbWatcher()
|
|
||||||
return bw
|
|
||||||
}
|
|
||||||
|
|
||||||
func (bwb *balancerWrapperBuilder) Name() string {
|
|
||||||
return "wrapper"
|
|
||||||
}
|
|
||||||
|
|
||||||
type scState struct {
|
|
||||||
addr Address // The v1 address type.
|
|
||||||
s connectivity.State
|
|
||||||
down func(error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type balancerWrapper struct {
|
|
||||||
balancer Balancer // The v1 balancer.
|
|
||||||
pickfirst bool
|
|
||||||
|
|
||||||
cc balancer.ClientConn
|
|
||||||
targetAddr string // Target without the scheme.
|
|
||||||
|
|
||||||
mu sync.Mutex
|
|
||||||
conns map[resolver.Address]balancer.SubConn
|
|
||||||
connSt map[balancer.SubConn]*scState
|
|
||||||
// This channel is closed when handling the first resolver result.
|
|
||||||
// lbWatcher blocks until this is closed, to avoid race between
|
|
||||||
// - NewSubConn is created, cc wants to notify balancer of state changes;
|
|
||||||
// - Build hasn't return, cc doesn't have access to balancer.
|
|
||||||
startCh chan struct{}
|
|
||||||
|
|
||||||
// To aggregate the connectivity state.
|
|
||||||
csEvltr *balancer.ConnectivityStateEvaluator
|
|
||||||
state connectivity.State
|
|
||||||
}
|
|
||||||
|
|
||||||
// lbWatcher watches the Notify channel of the balancer and manages
|
|
||||||
// connections accordingly.
|
|
||||||
func (bw *balancerWrapper) lbWatcher() {
|
|
||||||
<-bw.startCh
|
|
||||||
notifyCh := bw.balancer.Notify()
|
|
||||||
if notifyCh == nil {
|
|
||||||
// There's no resolver in the balancer. Connect directly.
|
|
||||||
a := resolver.Address{
|
|
||||||
Addr: bw.targetAddr,
|
|
||||||
Type: resolver.Backend,
|
|
||||||
}
|
|
||||||
sc, err := bw.cc.NewSubConn([]resolver.Address{a}, balancer.NewSubConnOptions{})
|
|
||||||
if err != nil {
|
|
||||||
grpclog.Warningf("Error creating connection to %v. Err: %v", a, err)
|
|
||||||
} else {
|
|
||||||
bw.mu.Lock()
|
|
||||||
bw.conns[a] = sc
|
|
||||||
bw.connSt[sc] = &scState{
|
|
||||||
addr: Address{Addr: bw.targetAddr},
|
|
||||||
s: connectivity.Idle,
|
|
||||||
}
|
|
||||||
bw.mu.Unlock()
|
|
||||||
sc.Connect()
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for addrs := range notifyCh {
|
|
||||||
grpclog.Infof("balancerWrapper: got update addr from Notify: %v", addrs)
|
|
||||||
if bw.pickfirst {
|
|
||||||
var (
|
|
||||||
oldA resolver.Address
|
|
||||||
oldSC balancer.SubConn
|
|
||||||
)
|
|
||||||
bw.mu.Lock()
|
|
||||||
for oldA, oldSC = range bw.conns {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
bw.mu.Unlock()
|
|
||||||
if len(addrs) <= 0 {
|
|
||||||
if oldSC != nil {
|
|
||||||
// Teardown old sc.
|
|
||||||
bw.mu.Lock()
|
|
||||||
delete(bw.conns, oldA)
|
|
||||||
delete(bw.connSt, oldSC)
|
|
||||||
bw.mu.Unlock()
|
|
||||||
bw.cc.RemoveSubConn(oldSC)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
var newAddrs []resolver.Address
|
|
||||||
for _, a := range addrs {
|
|
||||||
newAddr := resolver.Address{
|
|
||||||
Addr: a.Addr,
|
|
||||||
Type: resolver.Backend, // All addresses from balancer are all backends.
|
|
||||||
ServerName: "",
|
|
||||||
Metadata: a.Metadata,
|
|
||||||
}
|
|
||||||
newAddrs = append(newAddrs, newAddr)
|
|
||||||
}
|
|
||||||
if oldSC == nil {
|
|
||||||
// Create new sc.
|
|
||||||
sc, err := bw.cc.NewSubConn(newAddrs, balancer.NewSubConnOptions{})
|
|
||||||
if err != nil {
|
|
||||||
grpclog.Warningf("Error creating connection to %v. Err: %v", newAddrs, err)
|
|
||||||
} else {
|
|
||||||
bw.mu.Lock()
|
|
||||||
// For pickfirst, there should be only one SubConn, so the
|
|
||||||
// address doesn't matter. All states updating (up and down)
|
|
||||||
// and picking should all happen on that only SubConn.
|
|
||||||
bw.conns[resolver.Address{}] = sc
|
|
||||||
bw.connSt[sc] = &scState{
|
|
||||||
addr: addrs[0], // Use the first address.
|
|
||||||
s: connectivity.Idle,
|
|
||||||
}
|
|
||||||
bw.mu.Unlock()
|
|
||||||
sc.Connect()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
bw.mu.Lock()
|
|
||||||
bw.connSt[oldSC].addr = addrs[0]
|
|
||||||
bw.mu.Unlock()
|
|
||||||
oldSC.UpdateAddresses(newAddrs)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
var (
|
|
||||||
add []resolver.Address // Addresses need to setup connections.
|
|
||||||
del []balancer.SubConn // Connections need to tear down.
|
|
||||||
)
|
|
||||||
resAddrs := make(map[resolver.Address]Address)
|
|
||||||
for _, a := range addrs {
|
|
||||||
resAddrs[resolver.Address{
|
|
||||||
Addr: a.Addr,
|
|
||||||
Type: resolver.Backend, // All addresses from balancer are all backends.
|
|
||||||
ServerName: "",
|
|
||||||
Metadata: a.Metadata,
|
|
||||||
}] = a
|
|
||||||
}
|
|
||||||
bw.mu.Lock()
|
|
||||||
for a := range resAddrs {
|
|
||||||
if _, ok := bw.conns[a]; !ok {
|
|
||||||
add = append(add, a)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for a, c := range bw.conns {
|
|
||||||
if _, ok := resAddrs[a]; !ok {
|
|
||||||
del = append(del, c)
|
|
||||||
delete(bw.conns, a)
|
|
||||||
// Keep the state of this sc in bw.connSt until its state becomes Shutdown.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bw.mu.Unlock()
|
|
||||||
for _, a := range add {
|
|
||||||
sc, err := bw.cc.NewSubConn([]resolver.Address{a}, balancer.NewSubConnOptions{})
|
|
||||||
if err != nil {
|
|
||||||
grpclog.Warningf("Error creating connection to %v. Err: %v", a, err)
|
|
||||||
} else {
|
|
||||||
bw.mu.Lock()
|
|
||||||
bw.conns[a] = sc
|
|
||||||
bw.connSt[sc] = &scState{
|
|
||||||
addr: resAddrs[a],
|
|
||||||
s: connectivity.Idle,
|
|
||||||
}
|
|
||||||
bw.mu.Unlock()
|
|
||||||
sc.Connect()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, c := range del {
|
|
||||||
bw.cc.RemoveSubConn(c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (bw *balancerWrapper) HandleSubConnStateChange(sc balancer.SubConn, s connectivity.State) {
|
|
||||||
bw.mu.Lock()
|
|
||||||
defer bw.mu.Unlock()
|
|
||||||
scSt, ok := bw.connSt[sc]
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if s == connectivity.Idle {
|
|
||||||
sc.Connect()
|
|
||||||
}
|
|
||||||
oldS := scSt.s
|
|
||||||
scSt.s = s
|
|
||||||
if oldS != connectivity.Ready && s == connectivity.Ready {
|
|
||||||
scSt.down = bw.balancer.Up(scSt.addr)
|
|
||||||
} else if oldS == connectivity.Ready && s != connectivity.Ready {
|
|
||||||
if scSt.down != nil {
|
|
||||||
scSt.down(errConnClosing)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sa := bw.csEvltr.RecordTransition(oldS, s)
|
|
||||||
if bw.state != sa {
|
|
||||||
bw.state = sa
|
|
||||||
}
|
|
||||||
bw.cc.UpdateState(balancer.State{ConnectivityState: bw.state, Picker: bw})
|
|
||||||
if s == connectivity.Shutdown {
|
|
||||||
// Remove state for this sc.
|
|
||||||
delete(bw.connSt, sc)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (bw *balancerWrapper) HandleResolvedAddrs([]resolver.Address, error) {
|
|
||||||
bw.mu.Lock()
|
|
||||||
defer bw.mu.Unlock()
|
|
||||||
select {
|
|
||||||
case <-bw.startCh:
|
|
||||||
default:
|
|
||||||
close(bw.startCh)
|
|
||||||
}
|
|
||||||
// There should be a resolver inside the balancer.
|
|
||||||
// All updates here, if any, are ignored.
|
|
||||||
}
|
|
||||||
|
|
||||||
func (bw *balancerWrapper) Close() {
|
|
||||||
bw.mu.Lock()
|
|
||||||
defer bw.mu.Unlock()
|
|
||||||
select {
|
|
||||||
case <-bw.startCh:
|
|
||||||
default:
|
|
||||||
close(bw.startCh)
|
|
||||||
}
|
|
||||||
bw.balancer.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
// The picker is the balancerWrapper itself.
|
|
||||||
// It either blocks or returns error, consistent with v1 balancer Get().
|
|
||||||
func (bw *balancerWrapper) Pick(info balancer.PickInfo) (result balancer.PickResult, err error) {
|
|
||||||
failfast := true // Default failfast is true.
|
|
||||||
if ss, ok := rpcInfoFromContext(info.Ctx); ok {
|
|
||||||
failfast = ss.failfast
|
|
||||||
}
|
|
||||||
a, p, err := bw.balancer.Get(info.Ctx, BalancerGetOptions{BlockingWait: !failfast})
|
|
||||||
if err != nil {
|
|
||||||
return balancer.PickResult{}, toRPCErr(err)
|
|
||||||
}
|
|
||||||
if p != nil {
|
|
||||||
result.Done = func(balancer.DoneInfo) { p() }
|
|
||||||
defer func() {
|
|
||||||
if err != nil {
|
|
||||||
p()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
bw.mu.Lock()
|
|
||||||
defer bw.mu.Unlock()
|
|
||||||
if bw.pickfirst {
|
|
||||||
// Get the first sc in conns.
|
|
||||||
for _, result.SubConn = range bw.conns {
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
return balancer.PickResult{}, balancer.ErrNoSubConnAvailable
|
|
||||||
}
|
|
||||||
var ok1 bool
|
|
||||||
result.SubConn, ok1 = bw.conns[resolver.Address{
|
|
||||||
Addr: a.Addr,
|
|
||||||
Type: resolver.Backend,
|
|
||||||
ServerName: "",
|
|
||||||
Metadata: a.Metadata,
|
|
||||||
}]
|
|
||||||
s, ok2 := bw.connSt[result.SubConn]
|
|
||||||
if !ok1 || !ok2 {
|
|
||||||
// This can only happen due to a race where Get() returned an address
|
|
||||||
// that was subsequently removed by Notify. In this case we should
|
|
||||||
// retry always.
|
|
||||||
return balancer.PickResult{}, balancer.ErrNoSubConnAvailable
|
|
||||||
}
|
|
||||||
switch s.s {
|
|
||||||
case connectivity.Ready, connectivity.Idle:
|
|
||||||
return result, nil
|
|
||||||
case connectivity.Shutdown, connectivity.TransientFailure:
|
|
||||||
// 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
|
|
||||||
// non-failfast).
|
|
||||||
return balancer.PickResult{}, balancer.ErrTransientFailure
|
|
||||||
default:
|
|
||||||
// For other states (connecting or unknown), the v1 balancer would
|
|
||||||
// traditionally wait until ready and then issue the RPC. Returning
|
|
||||||
// ErrNoSubConnAvailable will be a slight improvement in that it will
|
|
||||||
// allow the balancer to choose another address in case others are
|
|
||||||
// connected.
|
|
||||||
return balancer.PickResult{}, balancer.ErrNoSubConnAvailable
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
|
@ -23,7 +23,6 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"net"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
@ -35,10 +34,11 @@ 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"
|
||||||
|
iresolver "google.golang.org/grpc/internal/resolver"
|
||||||
"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"
|
||||||
|
@ -48,6 +48,7 @@ import (
|
||||||
_ "google.golang.org/grpc/balancer/roundrobin" // To register roundrobin.
|
_ "google.golang.org/grpc/balancer/roundrobin" // To register roundrobin.
|
||||||
_ "google.golang.org/grpc/internal/resolver/dns" // To register dns resolver.
|
_ "google.golang.org/grpc/internal/resolver/dns" // To register dns resolver.
|
||||||
_ "google.golang.org/grpc/internal/resolver/passthrough" // To register passthrough resolver.
|
_ "google.golang.org/grpc/internal/resolver/passthrough" // To register passthrough resolver.
|
||||||
|
_ "google.golang.org/grpc/internal/resolver/unix" // To register unix resolver.
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -68,8 +69,6 @@ var (
|
||||||
errConnDrain = errors.New("grpc: the connection is drained")
|
errConnDrain = errors.New("grpc: the connection is drained")
|
||||||
// errConnClosing indicates that the connection is closing.
|
// errConnClosing indicates that the connection is closing.
|
||||||
errConnClosing = errors.New("grpc: the connection is closing")
|
errConnClosing = errors.New("grpc: the connection is closing")
|
||||||
// errBalancerClosed indicates that the balancer is closed.
|
|
||||||
errBalancerClosed = errors.New("grpc: balancer is closed")
|
|
||||||
// invalidDefaultServiceConfigErrPrefix is used to prefix the json parsing error for the default
|
// invalidDefaultServiceConfigErrPrefix is used to prefix the json parsing error for the default
|
||||||
// service config.
|
// service config.
|
||||||
invalidDefaultServiceConfigErrPrefix = "grpc: the provided default service config is invalid"
|
invalidDefaultServiceConfigErrPrefix = "grpc: the provided default service config is invalid"
|
||||||
|
@ -106,6 +105,17 @@ func Dial(target string, opts ...DialOption) (*ClientConn, error) {
|
||||||
return DialContext(context.Background(), target, opts...)
|
return DialContext(context.Background(), target, opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type defaultConfigSelector struct {
|
||||||
|
sc *ServiceConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dcs *defaultConfigSelector) SelectConfig(rpcInfo iresolver.RPCInfo) (*iresolver.RPCConfig, error) {
|
||||||
|
return &iresolver.RPCConfig{
|
||||||
|
Context: rpcInfo.Context,
|
||||||
|
MethodConfig: getMethodConfig(dcs.sc, rpcInfo.Method),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// DialContext creates a client connection to the given target. By default, it's
|
// DialContext creates a client connection to the given target. By default, it's
|
||||||
// a non-blocking dial (the function won't wait for connections to be
|
// a non-blocking dial (the function won't wait for connections to be
|
||||||
// established, and connecting happens in the background). To make it a blocking
|
// established, and connecting happens in the background). To make it a blocking
|
||||||
|
@ -133,6 +143,7 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *
|
||||||
firstResolveEvent: grpcsync.NewEvent(),
|
firstResolveEvent: grpcsync.NewEvent(),
|
||||||
}
|
}
|
||||||
cc.retryThrottler.Store((*retryThrottler)(nil))
|
cc.retryThrottler.Store((*retryThrottler)(nil))
|
||||||
|
cc.safeConfigSelector.UpdateConfigSelector(&defaultConfigSelector{nil})
|
||||||
cc.ctx, cc.cancel = context.WithCancel(context.Background())
|
cc.ctx, cc.cancel = context.WithCancel(context.Background())
|
||||||
|
|
||||||
for _, opt := range opts {
|
for _, opt := range opts {
|
||||||
|
@ -151,20 +162,17 @@ 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(logger, cc.channelzID, 0, &channelz.TraceEventDesc{
|
||||||
Desc: "Channel Created",
|
Desc: "Channel Created",
|
||||||
Severity: channelz.CtINFO,
|
Severity: channelz.CtInfo,
|
||||||
Parent: &channelz.TraceEventDesc{
|
Parent: &channelz.TraceEventDesc{
|
||||||
Desc: fmt.Sprintf("Nested Channel(id:%d) created", cc.channelzID),
|
Desc: fmt.Sprintf("Nested Channel(id:%d) created", cc.channelzID),
|
||||||
Severity: channelz.CtINFO,
|
Severity: channelz.CtInfo,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
} 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(logger, cc.channelzID, "Channel Created")
|
||||||
Desc: "Channel Created",
|
|
||||||
Severity: channelz.CtINFO,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
cc.csMgr.channelzID = cc.channelzID
|
cc.csMgr.channelzID = cc.channelzID
|
||||||
}
|
}
|
||||||
|
@ -196,15 +204,6 @@ 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 {
|
|
||||||
cc.dopts.copts.Dialer = newProxyDialer(
|
|
||||||
func(ctx context.Context, addr string) (net.Conn, error) {
|
|
||||||
network, addr := parseDialTarget(addr)
|
|
||||||
return (&net.Dialer{}).DialContext(ctx, network, addr)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if cc.dopts.copts.UserAgent != "" {
|
if cc.dopts.copts.UserAgent != "" {
|
||||||
cc.dopts.copts.UserAgent += " " + grpcUA
|
cc.dopts.copts.UserAgent += " " + grpcUA
|
||||||
} else {
|
} else {
|
||||||
|
@ -219,7 +218,14 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *
|
||||||
defer func() {
|
defer func() {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
|
switch {
|
||||||
|
case ctx.Err() == err:
|
||||||
|
conn = nil
|
||||||
|
case err == nil || !cc.dopts.returnLastError:
|
||||||
conn, err = nil, ctx.Err()
|
conn, err = nil, ctx.Err()
|
||||||
|
default:
|
||||||
|
conn, err = nil, fmt.Errorf("%v: %v", ctx.Err(), err)
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
@ -231,6 +237,7 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *
|
||||||
case sc, ok := <-cc.dopts.scChan:
|
case sc, ok := <-cc.dopts.scChan:
|
||||||
if ok {
|
if ok {
|
||||||
cc.sc = &sc
|
cc.sc = &sc
|
||||||
|
cc.safeConfigSelector.UpdateConfigSelector(&defaultConfigSelector{&sc})
|
||||||
scSet = true
|
scSet = true
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
@ -241,14 +248,14 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine the resolver to use.
|
// Determine the resolver to use.
|
||||||
cc.parsedTarget = parseTarget(cc.target)
|
cc.parsedTarget = grpcutil.ParseTarget(cc.target, cc.dopts.copts.Dialer != nil)
|
||||||
grpclog.Infof("parsed scheme: %q", cc.parsedTarget.Scheme)
|
channelz.Infof(logger, cc.channelzID, "parsed scheme: %q", cc.parsedTarget.Scheme)
|
||||||
resolverBuilder := cc.getResolver(cc.parsedTarget.Scheme)
|
resolverBuilder := cc.getResolver(cc.parsedTarget.Scheme)
|
||||||
if 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(logger, 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,
|
||||||
|
@ -264,6 +271,10 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *
|
||||||
cc.authority = creds.Info().ServerName
|
cc.authority = creds.Info().ServerName
|
||||||
} else if cc.dopts.insecure && cc.dopts.authority != "" {
|
} else if cc.dopts.insecure && cc.dopts.authority != "" {
|
||||||
cc.authority = cc.dopts.authority
|
cc.authority = cc.dopts.authority
|
||||||
|
} else if strings.HasPrefix(cc.target, "unix:") || strings.HasPrefix(cc.target, "unix-abstract:") {
|
||||||
|
cc.authority = "localhost"
|
||||||
|
} else if strings.HasPrefix(cc.parsedTarget.Endpoint, ":") {
|
||||||
|
cc.authority = "localhost" + cc.parsedTarget.Endpoint
|
||||||
} else {
|
} else {
|
||||||
// Use endpoint from "scheme://authority/endpoint" as the default
|
// Use endpoint from "scheme://authority/endpoint" as the default
|
||||||
// authority for ClientConn.
|
// authority for ClientConn.
|
||||||
|
@ -276,6 +287,7 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *
|
||||||
case sc, ok := <-cc.dopts.scChan:
|
case sc, ok := <-cc.dopts.scChan:
|
||||||
if ok {
|
if ok {
|
||||||
cc.sc = &sc
|
cc.sc = &sc
|
||||||
|
cc.safeConfigSelector.UpdateConfigSelector(&defaultConfigSelector{&sc})
|
||||||
}
|
}
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return nil, ctx.Err()
|
return nil, ctx.Err()
|
||||||
|
@ -293,6 +305,7 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *
|
||||||
DialCreds: credsClone,
|
DialCreds: credsClone,
|
||||||
CredsBundle: cc.dopts.copts.CredsBundle,
|
CredsBundle: cc.dopts.copts.CredsBundle,
|
||||||
Dialer: cc.dopts.copts.Dialer,
|
Dialer: cc.dopts.copts.Dialer,
|
||||||
|
CustomUserAgent: cc.dopts.copts.UserAgent,
|
||||||
ChannelzParentID: cc.channelzID,
|
ChannelzParentID: cc.channelzID,
|
||||||
Target: cc.parsedTarget,
|
Target: cc.parsedTarget,
|
||||||
}
|
}
|
||||||
|
@ -313,7 +326,7 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *
|
||||||
if s == connectivity.Ready {
|
if s == connectivity.Ready {
|
||||||
break
|
break
|
||||||
} else if cc.dopts.copts.FailOnNonTempDialError && s == connectivity.TransientFailure {
|
} else if cc.dopts.copts.FailOnNonTempDialError && s == connectivity.TransientFailure {
|
||||||
if err = cc.blockingpicker.connectionError(); err != nil {
|
if err = cc.connectionError(); err != nil {
|
||||||
terr, ok := err.(interface {
|
terr, ok := err.(interface {
|
||||||
Temporary() bool
|
Temporary() bool
|
||||||
})
|
})
|
||||||
|
@ -324,6 +337,9 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *
|
||||||
}
|
}
|
||||||
if !cc.WaitForStateChange(ctx, s) {
|
if !cc.WaitForStateChange(ctx, s) {
|
||||||
// ctx got timeout or canceled.
|
// ctx got timeout or canceled.
|
||||||
|
if err = cc.connectionError(); err != nil && cc.dopts.returnLastError {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
return nil, ctx.Err()
|
return nil, ctx.Err()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -416,12 +432,7 @@ func (csm *connectivityStateManager) updateState(state connectivity.State) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
csm.state = state
|
csm.state = state
|
||||||
if channelz.IsOn() {
|
channelz.Infof(logger, 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)
|
||||||
|
@ -483,6 +494,8 @@ type ClientConn struct {
|
||||||
balancerBuildOpts balancer.BuildOptions
|
balancerBuildOpts balancer.BuildOptions
|
||||||
blockingpicker *pickerWrapper
|
blockingpicker *pickerWrapper
|
||||||
|
|
||||||
|
safeConfigSelector iresolver.SafeConfigSelector
|
||||||
|
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
resolverWrapper *ccResolverWrapper
|
resolverWrapper *ccResolverWrapper
|
||||||
sc *ServiceConfig
|
sc *ServiceConfig
|
||||||
|
@ -497,11 +510,18 @@ type ClientConn struct {
|
||||||
|
|
||||||
channelzID int64 // channelz unique identification number
|
channelzID int64 // channelz unique identification number
|
||||||
czData *channelzData
|
czData *channelzData
|
||||||
|
|
||||||
|
lceMu sync.Mutex // protects lastConnectionError
|
||||||
|
lastConnectionError error
|
||||||
}
|
}
|
||||||
|
|
||||||
// WaitForStateChange waits until the connectivity.State of ClientConn changes from sourceState or
|
// WaitForStateChange waits until the connectivity.State of ClientConn changes from sourceState or
|
||||||
// ctx expires. A true value is returned in former case and false in latter.
|
// ctx expires. A true value is returned in former case and false in latter.
|
||||||
// This is an EXPERIMENTAL API.
|
//
|
||||||
|
// Experimental
|
||||||
|
//
|
||||||
|
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
|
||||||
|
// later release.
|
||||||
func (cc *ClientConn) WaitForStateChange(ctx context.Context, sourceState connectivity.State) bool {
|
func (cc *ClientConn) WaitForStateChange(ctx context.Context, sourceState connectivity.State) bool {
|
||||||
ch := cc.csMgr.getNotifyChan()
|
ch := cc.csMgr.getNotifyChan()
|
||||||
if cc.csMgr.getState() != sourceState {
|
if cc.csMgr.getState() != sourceState {
|
||||||
|
@ -516,7 +536,11 @@ func (cc *ClientConn) WaitForStateChange(ctx context.Context, sourceState connec
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetState returns the connectivity.State of ClientConn.
|
// GetState returns the connectivity.State of ClientConn.
|
||||||
// This is an EXPERIMENTAL API.
|
//
|
||||||
|
// Experimental
|
||||||
|
//
|
||||||
|
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
|
||||||
|
// later release.
|
||||||
func (cc *ClientConn) GetState() connectivity.State {
|
func (cc *ClientConn) GetState() connectivity.State {
|
||||||
return cc.csMgr.getState()
|
return cc.csMgr.getState()
|
||||||
}
|
}
|
||||||
|
@ -532,6 +556,7 @@ func (cc *ClientConn) scWatcher() {
|
||||||
// TODO: load balance policy runtime change is ignored.
|
// TODO: load balance policy runtime change is ignored.
|
||||||
// We may revisit this decision in the future.
|
// We may revisit this decision in the future.
|
||||||
cc.sc = &sc
|
cc.sc = &sc
|
||||||
|
cc.safeConfigSelector.UpdateConfigSelector(&defaultConfigSelector{&sc})
|
||||||
cc.mu.Unlock()
|
cc.mu.Unlock()
|
||||||
case <-cc.ctx.Done():
|
case <-cc.ctx.Done():
|
||||||
return
|
return
|
||||||
|
@ -570,13 +595,13 @@ func init() {
|
||||||
|
|
||||||
func (cc *ClientConn) maybeApplyDefaultServiceConfig(addrs []resolver.Address) {
|
func (cc *ClientConn) maybeApplyDefaultServiceConfig(addrs []resolver.Address) {
|
||||||
if cc.sc != nil {
|
if cc.sc != nil {
|
||||||
cc.applyServiceConfigAndBalancer(cc.sc, addrs)
|
cc.applyServiceConfigAndBalancer(cc.sc, nil, addrs)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if cc.dopts.defaultServiceConfig != nil {
|
if cc.dopts.defaultServiceConfig != nil {
|
||||||
cc.applyServiceConfigAndBalancer(cc.dopts.defaultServiceConfig, addrs)
|
cc.applyServiceConfigAndBalancer(cc.dopts.defaultServiceConfig, &defaultConfigSelector{cc.dopts.defaultServiceConfig}, addrs)
|
||||||
} else {
|
} else {
|
||||||
cc.applyServiceConfigAndBalancer(emptyServiceConfig, addrs)
|
cc.applyServiceConfigAndBalancer(emptyServiceConfig, &defaultConfigSelector{emptyServiceConfig}, addrs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -613,7 +638,15 @@ func (cc *ClientConn) updateResolverState(s resolver.State, err error) error {
|
||||||
// default, per the error handling design?
|
// default, per the error handling design?
|
||||||
} else {
|
} else {
|
||||||
if sc, ok := s.ServiceConfig.Config.(*ServiceConfig); s.ServiceConfig.Err == nil && ok {
|
if sc, ok := s.ServiceConfig.Config.(*ServiceConfig); s.ServiceConfig.Err == nil && ok {
|
||||||
cc.applyServiceConfigAndBalancer(sc, s.Addresses)
|
configSelector := iresolver.GetConfigSelector(s)
|
||||||
|
if configSelector != nil {
|
||||||
|
if len(s.ServiceConfig.Config.(*ServiceConfig).Methods) != 0 {
|
||||||
|
channelz.Infof(logger, cc.channelzID, "method configs in service config will be ignored due to presence of config selector")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
configSelector = &defaultConfigSelector{sc}
|
||||||
|
}
|
||||||
|
cc.applyServiceConfigAndBalancer(sc, configSelector, s.Addresses)
|
||||||
} else {
|
} else {
|
||||||
ret = balancer.ErrBadResolverState
|
ret = balancer.ErrBadResolverState
|
||||||
if cc.balancerWrapper == nil {
|
if cc.balancerWrapper == nil {
|
||||||
|
@ -623,6 +656,7 @@ func (cc *ClientConn) updateResolverState(s resolver.State, err error) error {
|
||||||
} else {
|
} else {
|
||||||
err = status.Errorf(codes.Unavailable, "illegal service config type: %T", s.ServiceConfig.Config)
|
err = status.Errorf(codes.Unavailable, "illegal service config type: %T", s.ServiceConfig.Config)
|
||||||
}
|
}
|
||||||
|
cc.safeConfigSelector.UpdateConfigSelector(&defaultConfigSelector{cc.sc})
|
||||||
cc.blockingpicker.updatePicker(base.NewErrPicker(err))
|
cc.blockingpicker.updatePicker(base.NewErrPicker(err))
|
||||||
cc.csMgr.updateState(connectivity.TransientFailure)
|
cc.csMgr.updateState(connectivity.TransientFailure)
|
||||||
cc.mu.Unlock()
|
cc.mu.Unlock()
|
||||||
|
@ -671,9 +705,9 @@ func (cc *ClientConn) switchBalancer(name string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
grpclog.Infof("ClientConn switching balancer to %q", name)
|
channelz.Infof(logger, 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(logger, cc.channelzID, "ignoring balancer switching: Balancer DialOption used instead")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if cc.balancerWrapper != nil {
|
if cc.balancerWrapper != nil {
|
||||||
|
@ -681,22 +715,12 @@ 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(logger, 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(logger, 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(logger, cc.channelzID, "Channel switches to new LB policy %q", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
cc.curBalancerName = builder.Name()
|
cc.curBalancerName = builder.Name()
|
||||||
|
@ -720,6 +744,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,
|
||||||
|
@ -736,12 +761,12 @@ 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(logger, ac.channelzID, 0, &channelz.TraceEventDesc{
|
||||||
Desc: "Subchannel Created",
|
Desc: "Subchannel Created",
|
||||||
Severity: channelz.CtINFO,
|
Severity: channelz.CtInfo,
|
||||||
Parent: &channelz.TraceEventDesc{
|
Parent: &channelz.TraceEventDesc{
|
||||||
Desc: fmt.Sprintf("Subchannel(id:%d) created", ac.channelzID),
|
Desc: fmt.Sprintf("Subchannel(id:%d) created", ac.channelzID),
|
||||||
Severity: channelz.CtINFO,
|
Severity: channelz.CtInfo,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -775,7 +800,11 @@ func (cc *ClientConn) channelzMetric() *channelz.ChannelInternalMetric {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Target returns the target string of the ClientConn.
|
// Target returns the target string of the ClientConn.
|
||||||
// This is an EXPERIMENTAL API.
|
//
|
||||||
|
// Experimental
|
||||||
|
//
|
||||||
|
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
|
||||||
|
// later release.
|
||||||
func (cc *ClientConn) Target() string {
|
func (cc *ClientConn) Target() string {
|
||||||
return cc.target
|
return cc.target
|
||||||
}
|
}
|
||||||
|
@ -834,7 +863,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(logger, 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 {
|
||||||
|
@ -854,7 +883,7 @@ func (ac *addrConn) tryUpdateAddrs(addrs []resolver.Address) bool {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
grpclog.Infof("addrConn: tryUpdateAddrs curAddrFound: %v", curAddrFound)
|
channelz.Infof(logger, ac.channelzID, "addrConn: tryUpdateAddrs curAddrFound: %v", curAddrFound)
|
||||||
if curAddrFound {
|
if curAddrFound {
|
||||||
ac.addrs = addrs
|
ac.addrs = addrs
|
||||||
}
|
}
|
||||||
|
@ -862,26 +891,33 @@ func (ac *addrConn) tryUpdateAddrs(addrs []resolver.Address) bool {
|
||||||
return curAddrFound
|
return curAddrFound
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getMethodConfig(sc *ServiceConfig, method string) MethodConfig {
|
||||||
|
if sc == nil {
|
||||||
|
return MethodConfig{}
|
||||||
|
}
|
||||||
|
if m, ok := sc.Methods[method]; ok {
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
i := strings.LastIndex(method, "/")
|
||||||
|
if m, ok := sc.Methods[method[:i+1]]; ok {
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
return sc.Methods[""]
|
||||||
|
}
|
||||||
|
|
||||||
// GetMethodConfig gets the method config of the input method.
|
// GetMethodConfig gets the method config of the input method.
|
||||||
// If there's an exact match for input method (i.e. /service/method), we return
|
// If there's an exact match for input method (i.e. /service/method), we return
|
||||||
// the corresponding MethodConfig.
|
// the corresponding MethodConfig.
|
||||||
// If there isn't an exact match for the input method, we look for the default config
|
// If there isn't an exact match for the input method, we look for the service's default
|
||||||
// under the service (i.e /service/). If there is a default MethodConfig for
|
// config under the service (i.e /service/) and then for the default for all services (empty string).
|
||||||
// the service, we return it.
|
//
|
||||||
|
// If there is a default MethodConfig for the service, we return it.
|
||||||
// Otherwise, we return an empty MethodConfig.
|
// Otherwise, we return an empty MethodConfig.
|
||||||
func (cc *ClientConn) GetMethodConfig(method string) MethodConfig {
|
func (cc *ClientConn) GetMethodConfig(method string) MethodConfig {
|
||||||
// TODO: Avoid the locking here.
|
// TODO: Avoid the locking here.
|
||||||
cc.mu.RLock()
|
cc.mu.RLock()
|
||||||
defer cc.mu.RUnlock()
|
defer cc.mu.RUnlock()
|
||||||
if cc.sc == nil {
|
return getMethodConfig(cc.sc, method)
|
||||||
return MethodConfig{}
|
|
||||||
}
|
|
||||||
m, ok := cc.sc.Methods[method]
|
|
||||||
if !ok {
|
|
||||||
i := strings.LastIndex(method, "/")
|
|
||||||
m = cc.sc.Methods[method[:i+1]]
|
|
||||||
}
|
|
||||||
return m
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cc *ClientConn) healthCheckConfig() *healthCheckConfig {
|
func (cc *ClientConn) healthCheckConfig() *healthCheckConfig {
|
||||||
|
@ -904,12 +940,15 @@ func (cc *ClientConn) getTransport(ctx context.Context, failfast bool, method st
|
||||||
return t, done, nil
|
return t, done, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cc *ClientConn) applyServiceConfigAndBalancer(sc *ServiceConfig, addrs []resolver.Address) {
|
func (cc *ClientConn) applyServiceConfigAndBalancer(sc *ServiceConfig, configSelector iresolver.ConfigSelector, addrs []resolver.Address) {
|
||||||
if sc == nil {
|
if sc == nil {
|
||||||
// should never reach here.
|
// should never reach here.
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
cc.sc = sc
|
cc.sc = sc
|
||||||
|
if configSelector != nil {
|
||||||
|
cc.safeConfigSelector.UpdateConfigSelector(configSelector)
|
||||||
|
}
|
||||||
|
|
||||||
if cc.sc.retryThrottling != nil {
|
if cc.sc.retryThrottling != nil {
|
||||||
newThrottler := &retryThrottler{
|
newThrottler := &retryThrottler{
|
||||||
|
@ -973,7 +1012,10 @@ func (cc *ClientConn) resolveNow(o resolver.ResolveNowOptions) {
|
||||||
// However, if a previously unavailable network becomes available, this may be
|
// However, if a previously unavailable network becomes available, this may be
|
||||||
// used to trigger an immediate reconnect.
|
// used to trigger an immediate reconnect.
|
||||||
//
|
//
|
||||||
// This API is EXPERIMENTAL.
|
// Experimental
|
||||||
|
//
|
||||||
|
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
|
||||||
|
// later release.
|
||||||
func (cc *ClientConn) ResetConnectBackoff() {
|
func (cc *ClientConn) ResetConnectBackoff() {
|
||||||
cc.mu.Lock()
|
cc.mu.Lock()
|
||||||
conns := cc.conns
|
conns := cc.conns
|
||||||
|
@ -1017,15 +1059,15 @@ func (cc *ClientConn) Close() error {
|
||||||
if channelz.IsOn() {
|
if channelz.IsOn() {
|
||||||
ted := &channelz.TraceEventDesc{
|
ted := &channelz.TraceEventDesc{
|
||||||
Desc: "Channel Deleted",
|
Desc: "Channel Deleted",
|
||||||
Severity: channelz.CtINFO,
|
Severity: channelz.CtInfo,
|
||||||
}
|
}
|
||||||
if cc.dopts.channelzParentID != 0 {
|
if cc.dopts.channelzParentID != 0 {
|
||||||
ted.Parent = &channelz.TraceEventDesc{
|
ted.Parent = &channelz.TraceEventDesc{
|
||||||
Desc: fmt.Sprintf("Nested channel(id:%d) deleted", cc.channelzID),
|
Desc: fmt.Sprintf("Nested channel(id:%d) deleted", cc.channelzID),
|
||||||
Severity: channelz.CtINFO,
|
Severity: channelz.CtInfo,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
channelz.AddTraceEvent(cc.channelzID, ted)
|
channelz.AddTraceEvent(logger, 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)
|
||||||
|
@ -1068,15 +1110,8 @@ 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(logger, ac.channelzID, "Subchannel Connectivity change to %v", s)
|
||||||
channelz.AddTraceEvent(ac.channelzID, &channelz.TraceEventDesc{
|
|
||||||
Desc: updateMsg,
|
|
||||||
Severity: channelz.CtINFO,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
ac.cc.handleSubConnStateChange(ac.acbw, s, lastErr)
|
ac.cc.handleSubConnStateChange(ac.acbw, s, lastErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1163,7 +1198,7 @@ func (ac *addrConn) resetTransport() {
|
||||||
ac.mu.Lock()
|
ac.mu.Lock()
|
||||||
if ac.state == connectivity.Shutdown {
|
if ac.state == connectivity.Shutdown {
|
||||||
ac.mu.Unlock()
|
ac.mu.Unlock()
|
||||||
newTr.Close()
|
newTr.Close(fmt.Errorf("reached connectivity state: SHUTDOWN"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ac.curAddr = addr
|
ac.curAddr = addr
|
||||||
|
@ -1213,12 +1248,7 @@ func (ac *addrConn) tryAllAddrs(addrs []resolver.Address, connectDeadline time.T
|
||||||
}
|
}
|
||||||
ac.mu.Unlock()
|
ac.mu.Unlock()
|
||||||
|
|
||||||
if channelz.IsOn() {
|
channelz.Infof(logger, 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 {
|
||||||
|
@ -1227,7 +1257,7 @@ func (ac *addrConn) tryAllAddrs(addrs []resolver.Address, connectDeadline time.T
|
||||||
if firstConnErr == nil {
|
if firstConnErr == nil {
|
||||||
firstConnErr = err
|
firstConnErr = err
|
||||||
}
|
}
|
||||||
ac.cc.blockingpicker.updateConnectionError(err)
|
ac.cc.updateConnectionError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Couldn't connect to any address.
|
// Couldn't connect to any address.
|
||||||
|
@ -1242,16 +1272,9 @@ func (ac *addrConn) createTransport(addr resolver.Address, copts transport.Conne
|
||||||
onCloseCalled := make(chan struct{})
|
onCloseCalled := make(chan struct{})
|
||||||
reconnect := grpcsync.NewEvent()
|
reconnect := grpcsync.NewEvent()
|
||||||
|
|
||||||
authority := ac.cc.authority
|
|
||||||
// addr.ServerName takes precedent over ClientConn authority, if present.
|
// addr.ServerName takes precedent over ClientConn authority, if present.
|
||||||
if addr.ServerName != "" {
|
if addr.ServerName == "" {
|
||||||
authority = addr.ServerName
|
addr.ServerName = ac.cc.authority
|
||||||
}
|
|
||||||
|
|
||||||
target := transport.TargetInfo{
|
|
||||||
Addr: addr.Addr,
|
|
||||||
Metadata: addr.Metadata,
|
|
||||||
Authority: authority,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
once := sync.Once{}
|
once := sync.Once{}
|
||||||
|
@ -1297,18 +1320,18 @@ func (ac *addrConn) createTransport(addr resolver.Address, copts transport.Conne
|
||||||
copts.ChannelzParentID = ac.channelzID
|
copts.ChannelzParentID = ac.channelzID
|
||||||
}
|
}
|
||||||
|
|
||||||
newTr, err := transport.NewClientTransport(connectCtx, ac.cc.ctx, target, copts, onPrefaceReceipt, onGoAway, onClose)
|
newTr, err := transport.NewClientTransport(connectCtx, ac.cc.ctx, addr, 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(logger, 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(time.Until(connectDeadline)):
|
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(fmt.Errorf("failed to receive server preface within timeout"))
|
||||||
grpclog.Warningf("grpc: addrConn.createTransport failed to connect to %v: didn't receive server preface in time. Reconnecting...", addr)
|
channelz.Warningf(logger, 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.
|
||||||
|
@ -1325,7 +1348,7 @@ func (ac *addrConn) createTransport(addr resolver.Address, copts transport.Conne
|
||||||
//
|
//
|
||||||
// LB channel health checking is enabled when all requirements below are met:
|
// LB channel health checking is enabled when all requirements below are met:
|
||||||
// 1. it is not disabled by the user with the WithDisableHealthCheck DialOption
|
// 1. it is not disabled by the user with the WithDisableHealthCheck DialOption
|
||||||
// 2. internal.HealthCheckFunc is set by importing the grpc/healthcheck package
|
// 2. internal.HealthCheckFunc is set by importing the grpc/health package
|
||||||
// 3. a service config with non-empty healthCheckConfig field is provided
|
// 3. a service config with non-empty healthCheckConfig field is provided
|
||||||
// 4. the load balancer requests it
|
// 4. the load balancer requests it
|
||||||
//
|
//
|
||||||
|
@ -1355,7 +1378,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(logger, ac.channelzID, "Health check is requested but health check function is not set.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1385,15 +1408,9 @@ func (ac *addrConn) startHealthCheck(ctx context.Context) {
|
||||||
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(logger, 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(logger, ac.channelzID, "HealthCheckFunc exits with unexpected error %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
@ -1430,10 +1447,9 @@ func (ac *addrConn) getReadyTransport() (transport.ClientTransport, bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// tearDown starts to tear down the addrConn.
|
// tearDown starts to tear down the addrConn.
|
||||||
// TODO(zhaoq): Make this synchronous to avoid unbounded memory consumption in
|
//
|
||||||
// some edge cases (e.g., the caller opens and closes many addrConn's in a
|
// Note that tearDown doesn't remove ac from ac.cc.conns, so the addrConn struct
|
||||||
// tight loop.
|
// will leak. In most cases, call cc.removeAddrConn() instead.
|
||||||
// tearDown doesn't remove ac from ac.cc.conns.
|
|
||||||
func (ac *addrConn) tearDown(err error) {
|
func (ac *addrConn) tearDown(err error) {
|
||||||
ac.mu.Lock()
|
ac.mu.Lock()
|
||||||
if ac.state == connectivity.Shutdown {
|
if ac.state == connectivity.Shutdown {
|
||||||
|
@ -1458,12 +1474,12 @@ 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(logger, ac.channelzID, 0, &channelz.TraceEventDesc{
|
||||||
Desc: "Subchannel Deleted",
|
Desc: "Subchannel Deleted",
|
||||||
Severity: channelz.CtINFO,
|
Severity: channelz.CtInfo,
|
||||||
Parent: &channelz.TraceEventDesc{
|
Parent: &channelz.TraceEventDesc{
|
||||||
Desc: fmt.Sprintf("Subchanel(id:%d) deleted", ac.channelzID),
|
Desc: fmt.Sprintf("Subchanel(id:%d) deleted", ac.channelzID),
|
||||||
Severity: channelz.CtINFO,
|
Severity: channelz.CtInfo,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
// 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
|
||||||
|
@ -1560,9 +1576,21 @@ var ErrClientConnTimeout = errors.New("grpc: timed out when dialing")
|
||||||
|
|
||||||
func (cc *ClientConn) getResolver(scheme string) resolver.Builder {
|
func (cc *ClientConn) getResolver(scheme string) resolver.Builder {
|
||||||
for _, rb := range cc.dopts.resolvers {
|
for _, rb := range cc.dopts.resolvers {
|
||||||
if cc.parsedTarget.Scheme == rb.Scheme() {
|
if scheme == rb.Scheme() {
|
||||||
return rb
|
return rb
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return resolver.Get(cc.parsedTarget.Scheme)
|
return resolver.Get(scheme)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cc *ClientConn) updateConnectionError(err error) {
|
||||||
|
cc.lceMu.Lock()
|
||||||
|
cc.lastConnectionError = err
|
||||||
|
cc.lceMu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cc *ClientConn) connectionError() error {
|
||||||
|
cc.lceMu.Lock()
|
||||||
|
defer cc.lceMu.Unlock()
|
||||||
|
return cc.lastConnectionError
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,9 @@ const (
|
||||||
OK Code = 0
|
OK Code = 0
|
||||||
|
|
||||||
// Canceled indicates the operation was canceled (typically by the caller).
|
// Canceled indicates the operation was canceled (typically by the caller).
|
||||||
|
//
|
||||||
|
// The gRPC framework will generate this error code when cancellation
|
||||||
|
// is requested.
|
||||||
Canceled Code = 1
|
Canceled Code = 1
|
||||||
|
|
||||||
// Unknown error. An example of where this error may be returned is
|
// Unknown error. An example of where this error may be returned is
|
||||||
|
@ -40,12 +43,17 @@ const (
|
||||||
// an error-space that is not known in this address space. Also
|
// an error-space that is not known in this address space. Also
|
||||||
// errors raised by APIs that do not return enough error information
|
// errors raised by APIs that do not return enough error information
|
||||||
// may be converted to this error.
|
// may be converted to this error.
|
||||||
|
//
|
||||||
|
// The gRPC framework will generate this error code in the above two
|
||||||
|
// mentioned cases.
|
||||||
Unknown Code = 2
|
Unknown Code = 2
|
||||||
|
|
||||||
// InvalidArgument indicates client specified an invalid argument.
|
// InvalidArgument indicates client specified an invalid argument.
|
||||||
// Note that this differs from FailedPrecondition. It indicates arguments
|
// Note that this differs from FailedPrecondition. It indicates arguments
|
||||||
// that are problematic regardless of the state of the system
|
// that are problematic regardless of the state of the system
|
||||||
// (e.g., a malformed file name).
|
// (e.g., a malformed file name).
|
||||||
|
//
|
||||||
|
// This error code will not be generated by the gRPC framework.
|
||||||
InvalidArgument Code = 3
|
InvalidArgument Code = 3
|
||||||
|
|
||||||
// DeadlineExceeded means operation expired before completion.
|
// DeadlineExceeded means operation expired before completion.
|
||||||
|
@ -53,14 +61,21 @@ const (
|
||||||
// returned even if the operation has completed successfully. For
|
// returned even if the operation has completed successfully. For
|
||||||
// example, a successful response from a server could have been delayed
|
// example, a successful response from a server could have been delayed
|
||||||
// long enough for the deadline to expire.
|
// long enough for the deadline to expire.
|
||||||
|
//
|
||||||
|
// The gRPC framework will generate this error code when the deadline is
|
||||||
|
// exceeded.
|
||||||
DeadlineExceeded Code = 4
|
DeadlineExceeded Code = 4
|
||||||
|
|
||||||
// NotFound means some requested entity (e.g., file or directory) was
|
// NotFound means some requested entity (e.g., file or directory) was
|
||||||
// not found.
|
// not found.
|
||||||
|
//
|
||||||
|
// This error code will not be generated by the gRPC framework.
|
||||||
NotFound Code = 5
|
NotFound Code = 5
|
||||||
|
|
||||||
// AlreadyExists means an attempt to create an entity failed because one
|
// AlreadyExists means an attempt to create an entity failed because one
|
||||||
// already exists.
|
// already exists.
|
||||||
|
//
|
||||||
|
// This error code will not be generated by the gRPC framework.
|
||||||
AlreadyExists Code = 6
|
AlreadyExists Code = 6
|
||||||
|
|
||||||
// PermissionDenied indicates the caller does not have permission to
|
// PermissionDenied indicates the caller does not have permission to
|
||||||
|
@ -69,10 +84,17 @@ const (
|
||||||
// instead for those errors). It must not be
|
// instead for those errors). It must not be
|
||||||
// used if the caller cannot be identified (use Unauthenticated
|
// used if the caller cannot be identified (use Unauthenticated
|
||||||
// instead for those errors).
|
// instead for those errors).
|
||||||
|
//
|
||||||
|
// This error code will not be generated by the gRPC core framework,
|
||||||
|
// but expect authentication middleware to use it.
|
||||||
PermissionDenied Code = 7
|
PermissionDenied Code = 7
|
||||||
|
|
||||||
// ResourceExhausted indicates some resource has been exhausted, perhaps
|
// ResourceExhausted indicates some resource has been exhausted, perhaps
|
||||||
// a per-user quota, or perhaps the entire file system is out of space.
|
// a per-user quota, or perhaps the entire file system is out of space.
|
||||||
|
//
|
||||||
|
// This error code will be generated by the gRPC framework in
|
||||||
|
// out-of-memory and server overload situations, or when a message is
|
||||||
|
// larger than the configured maximum size.
|
||||||
ResourceExhausted Code = 8
|
ResourceExhausted Code = 8
|
||||||
|
|
||||||
// FailedPrecondition indicates operation was rejected because the
|
// FailedPrecondition indicates operation was rejected because the
|
||||||
|
@ -94,6 +116,8 @@ const (
|
||||||
// REST Get/Update/Delete on a resource and the resource on the
|
// REST Get/Update/Delete on a resource and the resource on the
|
||||||
// server does not match the condition. E.g., conflicting
|
// server does not match the condition. E.g., conflicting
|
||||||
// read-modify-write on the same resource.
|
// read-modify-write on the same resource.
|
||||||
|
//
|
||||||
|
// This error code will not be generated by the gRPC framework.
|
||||||
FailedPrecondition Code = 9
|
FailedPrecondition Code = 9
|
||||||
|
|
||||||
// Aborted indicates the operation was aborted, typically due to a
|
// Aborted indicates the operation was aborted, typically due to a
|
||||||
|
@ -102,6 +126,8 @@ const (
|
||||||
//
|
//
|
||||||
// See litmus test above for deciding between FailedPrecondition,
|
// See litmus test above for deciding between FailedPrecondition,
|
||||||
// Aborted, and Unavailable.
|
// Aborted, and Unavailable.
|
||||||
|
//
|
||||||
|
// This error code will not be generated by the gRPC framework.
|
||||||
Aborted Code = 10
|
Aborted Code = 10
|
||||||
|
|
||||||
// OutOfRange means operation was attempted past the valid range.
|
// OutOfRange means operation was attempted past the valid range.
|
||||||
|
@ -119,15 +145,26 @@ const (
|
||||||
// error) when it applies so that callers who are iterating through
|
// error) when it applies so that callers who are iterating through
|
||||||
// a space can easily look for an OutOfRange error to detect when
|
// a space can easily look for an OutOfRange error to detect when
|
||||||
// they are done.
|
// they are done.
|
||||||
|
//
|
||||||
|
// This error code will not be generated by the gRPC framework.
|
||||||
OutOfRange Code = 11
|
OutOfRange Code = 11
|
||||||
|
|
||||||
// Unimplemented indicates operation is not implemented or not
|
// Unimplemented indicates operation is not implemented or not
|
||||||
// supported/enabled in this service.
|
// supported/enabled in this service.
|
||||||
|
//
|
||||||
|
// This error code will be generated by the gRPC framework. Most
|
||||||
|
// commonly, you will see this error code when a method implementation
|
||||||
|
// is missing on the server. It can also be generated for unknown
|
||||||
|
// compression algorithms or a disagreement as to whether an RPC should
|
||||||
|
// be streaming.
|
||||||
Unimplemented Code = 12
|
Unimplemented Code = 12
|
||||||
|
|
||||||
// Internal errors. Means some invariants expected by underlying
|
// Internal errors. Means some invariants expected by underlying
|
||||||
// system has been broken. If you see one of these errors,
|
// system has been broken. If you see one of these errors,
|
||||||
// something is very broken.
|
// something is very broken.
|
||||||
|
//
|
||||||
|
// This error code will be generated by the gRPC framework in several
|
||||||
|
// internal error conditions.
|
||||||
Internal Code = 13
|
Internal Code = 13
|
||||||
|
|
||||||
// Unavailable indicates the service is currently unavailable.
|
// Unavailable indicates the service is currently unavailable.
|
||||||
|
@ -137,13 +174,22 @@ const (
|
||||||
//
|
//
|
||||||
// See litmus test above for deciding between FailedPrecondition,
|
// See litmus test above for deciding between FailedPrecondition,
|
||||||
// Aborted, and Unavailable.
|
// Aborted, and Unavailable.
|
||||||
|
//
|
||||||
|
// This error code will be generated by the gRPC framework during
|
||||||
|
// abrupt shutdown of a server process or network connection.
|
||||||
Unavailable Code = 14
|
Unavailable Code = 14
|
||||||
|
|
||||||
// DataLoss indicates unrecoverable data loss or corruption.
|
// DataLoss indicates unrecoverable data loss or corruption.
|
||||||
|
//
|
||||||
|
// This error code will not be generated by the gRPC framework.
|
||||||
DataLoss Code = 15
|
DataLoss Code = 15
|
||||||
|
|
||||||
// Unauthenticated indicates the request does not have valid
|
// Unauthenticated indicates the request does not have valid
|
||||||
// authentication credentials for the operation.
|
// authentication credentials for the operation.
|
||||||
|
//
|
||||||
|
// The gRPC framework will generate this error code when the
|
||||||
|
// authentication metadata is invalid or a Credentials callback fails,
|
||||||
|
// but also expect authentication middleware to generate it.
|
||||||
Unauthenticated Code = 16
|
Unauthenticated Code = 16
|
||||||
|
|
||||||
_maxCode = 17
|
_maxCode = 17
|
||||||
|
|
|
@ -22,11 +22,11 @@
|
||||||
package connectivity
|
package connectivity
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
|
|
||||||
"google.golang.org/grpc/grpclog"
|
"google.golang.org/grpc/grpclog"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var logger = grpclog.Component("core")
|
||||||
|
|
||||||
// State indicates the state of connectivity.
|
// State indicates the state of connectivity.
|
||||||
// It can be the state of a ClientConn or SubConn.
|
// It can be the state of a ClientConn or SubConn.
|
||||||
type State int
|
type State int
|
||||||
|
@ -44,7 +44,7 @@ func (s State) String() string {
|
||||||
case Shutdown:
|
case Shutdown:
|
||||||
return "SHUTDOWN"
|
return "SHUTDOWN"
|
||||||
default:
|
default:
|
||||||
grpclog.Errorf("unknown connectivity state: %d", s)
|
logger.Errorf("unknown connectivity state: %d", s)
|
||||||
return "Invalid-State"
|
return "Invalid-State"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,13 +61,3 @@ const (
|
||||||
// Shutdown indicates the ClientConn has started shutting down.
|
// Shutdown indicates the ClientConn has started shutting down.
|
||||||
Shutdown
|
Shutdown
|
||||||
)
|
)
|
||||||
|
|
||||||
// Reporter reports the connectivity states.
|
|
||||||
type Reporter interface {
|
|
||||||
// CurrentState returns the current state of the reporter.
|
|
||||||
CurrentState() State
|
|
||||||
// WaitForStateChange blocks until the reporter's state is different from the given state,
|
|
||||||
// and returns true.
|
|
||||||
// It returns false if <-ctx.Done() can proceed (ctx got timeout or got canceled).
|
|
||||||
WaitForStateChange(context.Context, State) bool
|
|
||||||
}
|
|
||||||
|
|
|
@ -29,7 +29,8 @@ import (
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/golang/protobuf/proto"
|
"github.com/golang/protobuf/proto"
|
||||||
"google.golang.org/grpc/internal"
|
"google.golang.org/grpc/attributes"
|
||||||
|
icredentials "google.golang.org/grpc/internal/credentials"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PerRPCCredentials defines the common interface for the credentials which need to
|
// PerRPCCredentials defines the common interface for the credentials which need to
|
||||||
|
@ -57,9 +58,11 @@ type PerRPCCredentials interface {
|
||||||
type SecurityLevel int
|
type SecurityLevel int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// NoSecurity indicates a connection is insecure.
|
// InvalidSecurityLevel indicates an invalid security level.
|
||||||
// The zero SecurityLevel value is invalid for backward compatibility.
|
// The zero SecurityLevel value is invalid for backward compatibility.
|
||||||
NoSecurity SecurityLevel = iota + 1
|
InvalidSecurityLevel SecurityLevel = iota
|
||||||
|
// NoSecurity indicates a connection is insecure.
|
||||||
|
NoSecurity
|
||||||
// IntegrityOnly indicates a connection only provides integrity protection.
|
// IntegrityOnly indicates a connection only provides integrity protection.
|
||||||
IntegrityOnly
|
IntegrityOnly
|
||||||
// PrivacyAndIntegrity indicates a connection provides both privacy and integrity protection.
|
// PrivacyAndIntegrity indicates a connection provides both privacy and integrity protection.
|
||||||
|
@ -89,7 +92,7 @@ type CommonAuthInfo struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCommonAuthInfo returns the pointer to CommonAuthInfo struct.
|
// GetCommonAuthInfo returns the pointer to CommonAuthInfo struct.
|
||||||
func (c *CommonAuthInfo) GetCommonAuthInfo() *CommonAuthInfo {
|
func (c CommonAuthInfo) GetCommonAuthInfo() CommonAuthInfo {
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,7 +103,11 @@ 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
|
||||||
|
@ -120,15 +127,18 @@ var ErrConnDispatched = errors.New("credentials: rawConn is dispatched out of gR
|
||||||
// TransportCredentials defines the common interface for all the live gRPC wire
|
// TransportCredentials defines the common interface for all the live gRPC wire
|
||||||
// protocols and supported transport security protocols (e.g., TLS, SSL).
|
// protocols and supported transport security protocols (e.g., TLS, SSL).
|
||||||
type TransportCredentials interface {
|
type TransportCredentials interface {
|
||||||
// ClientHandshake does the authentication handshake specified by the corresponding
|
// ClientHandshake does the authentication handshake specified by the
|
||||||
// authentication protocol on rawConn for clients. It returns the authenticated
|
// corresponding authentication protocol on rawConn for clients. It returns
|
||||||
// connection and the corresponding auth information about the connection.
|
// the authenticated connection and the corresponding auth information
|
||||||
// The auth information should embed CommonAuthInfo to return additional information about
|
// about the connection. The auth information should embed CommonAuthInfo
|
||||||
// the credentials. Implementations must use the provided context to implement timely cancellation.
|
// to return additional information about the credentials. Implementations
|
||||||
// gRPC will try to reconnect if the error returned is a temporary error
|
// must use the provided context to implement timely cancellation. gRPC
|
||||||
// (io.EOF, context.DeadlineExceeded or err.Temporary() == true).
|
// will try to reconnect if the error returned is a temporary error
|
||||||
// If the returned error is a wrapper error, implementations should make sure that
|
// (io.EOF, context.DeadlineExceeded or err.Temporary() == true). If the
|
||||||
|
// returned error is a wrapper error, implementations should make sure that
|
||||||
// the error implements Temporary() to have the correct retry behaviors.
|
// the error implements Temporary() to have the correct retry behaviors.
|
||||||
|
// Additionally, ClientHandshakeInfo data will be available via the context
|
||||||
|
// passed to this call.
|
||||||
//
|
//
|
||||||
// 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.
|
||||||
ClientHandshake(context.Context, string, net.Conn) (net.Conn, AuthInfo, error)
|
ClientHandshake(context.Context, string, net.Conn) (net.Conn, AuthInfo, error)
|
||||||
|
@ -178,15 +188,33 @@ type RequestInfo struct {
|
||||||
AuthInfo AuthInfo
|
AuthInfo AuthInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
// requestInfoKey is a struct to be used as the key when attaching a RequestInfo to a context object.
|
|
||||||
type requestInfoKey struct{}
|
|
||||||
|
|
||||||
// RequestInfoFromContext extracts the RequestInfo from the context if it exists.
|
// RequestInfoFromContext extracts the RequestInfo from the context if it exists.
|
||||||
//
|
//
|
||||||
// This API is experimental.
|
// This API is experimental.
|
||||||
func RequestInfoFromContext(ctx context.Context) (ri RequestInfo, ok bool) {
|
func RequestInfoFromContext(ctx context.Context) (ri RequestInfo, ok bool) {
|
||||||
ri, ok = ctx.Value(requestInfoKey{}).(RequestInfo)
|
ri, ok = icredentials.RequestInfoFromContext(ctx).(RequestInfo)
|
||||||
return
|
return ri, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClientHandshakeInfo holds data to be passed to ClientHandshake. This makes
|
||||||
|
// it possible to pass arbitrary data to the handshaker from gRPC, resolver,
|
||||||
|
// balancer etc. Individual credential implementations control the actual
|
||||||
|
// format of the data that they are willing to receive.
|
||||||
|
//
|
||||||
|
// This API is experimental.
|
||||||
|
type ClientHandshakeInfo struct {
|
||||||
|
// Attributes contains the attributes for the address. It could be provided
|
||||||
|
// by the gRPC, resolver, balancer etc.
|
||||||
|
Attributes *attributes.Attributes
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClientHandshakeInfoFromContext returns the ClientHandshakeInfo struct stored
|
||||||
|
// in ctx.
|
||||||
|
//
|
||||||
|
// This API is experimental.
|
||||||
|
func ClientHandshakeInfoFromContext(ctx context.Context) ClientHandshakeInfo {
|
||||||
|
chi, _ := icredentials.ClientHandshakeInfoFromContext(ctx).(ClientHandshakeInfo)
|
||||||
|
return chi
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckSecurityLevel checks if a connection's security level is greater than or equal to the specified one.
|
// CheckSecurityLevel checks if a connection's security level is greater than or equal to the specified one.
|
||||||
|
@ -194,17 +222,16 @@ func RequestInfoFromContext(ctx context.Context) (ri RequestInfo, ok bool) {
|
||||||
// or 3) CommonAuthInfo.SecurityLevel has an invalid zero value. For 2) and 3), it is for the purpose of backward-compatibility.
|
// 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.
|
// This API is experimental.
|
||||||
func CheckSecurityLevel(ctx context.Context, level SecurityLevel) error {
|
func CheckSecurityLevel(ai AuthInfo, level SecurityLevel) error {
|
||||||
type internalInfo interface {
|
type internalInfo interface {
|
||||||
GetCommonAuthInfo() *CommonAuthInfo
|
GetCommonAuthInfo() CommonAuthInfo
|
||||||
}
|
}
|
||||||
ri, _ := RequestInfoFromContext(ctx)
|
if ai == nil {
|
||||||
if ri.AuthInfo == nil {
|
return errors.New("AuthInfo is nil")
|
||||||
return errors.New("unable to obtain SecurityLevel from context")
|
|
||||||
}
|
}
|
||||||
if ci, ok := ri.AuthInfo.(internalInfo); ok {
|
if ci, ok := ai.(internalInfo); ok {
|
||||||
// CommonAuthInfo.SecurityLevel has an invalid value.
|
// CommonAuthInfo.SecurityLevel has an invalid value.
|
||||||
if ci.GetCommonAuthInfo().SecurityLevel == 0 {
|
if ci.GetCommonAuthInfo().SecurityLevel == InvalidSecurityLevel {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if ci.GetCommonAuthInfo().SecurityLevel < level {
|
if ci.GetCommonAuthInfo().SecurityLevel < level {
|
||||||
|
@ -215,12 +242,6 @@ func CheckSecurityLevel(ctx context.Context, level SecurityLevel) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
|
||||||
internal.NewRequestInfoContext = func(ctx context.Context, ri RequestInfo) context.Context {
|
|
||||||
return context.WithValue(ctx, requestInfoKey{}, ri)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ChannelzSecurityInfo defines the interface that security protocols should implement
|
// ChannelzSecurityInfo defines the interface that security protocols should implement
|
||||||
// in order to provide security info to channelz.
|
// in order to provide security info to channelz.
|
||||||
//
|
//
|
||||||
|
|
|
@ -25,8 +25,9 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net"
|
"net"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
"google.golang.org/grpc/credentials/internal"
|
credinternal "google.golang.org/grpc/internal/credentials"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TLSInfo contains the auth information for a TLS authenticated connection.
|
// TLSInfo contains the auth information for a TLS authenticated connection.
|
||||||
|
@ -34,6 +35,8 @@ import (
|
||||||
type TLSInfo struct {
|
type TLSInfo struct {
|
||||||
State tls.ConnectionState
|
State tls.ConnectionState
|
||||||
CommonAuthInfo
|
CommonAuthInfo
|
||||||
|
// This API is experimental.
|
||||||
|
SPIFFEID *url.URL
|
||||||
}
|
}
|
||||||
|
|
||||||
// AuthType returns the type of TLSInfo as a string.
|
// AuthType returns the type of TLSInfo as a string.
|
||||||
|
@ -69,7 +72,7 @@ func (c tlsCreds) Info() ProtocolInfo {
|
||||||
|
|
||||||
func (c *tlsCreds) ClientHandshake(ctx context.Context, authority string, rawConn net.Conn) (_ net.Conn, _ AuthInfo, err error) {
|
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
|
// use local cfg to avoid clobbering ServerName if using multiple endpoints
|
||||||
cfg := cloneTLSConfig(c.config)
|
cfg := credinternal.CloneTLSConfig(c.config)
|
||||||
if cfg.ServerName == "" {
|
if cfg.ServerName == "" {
|
||||||
serverName, _, err := net.SplitHostPort(authority)
|
serverName, _, err := net.SplitHostPort(authority)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -94,7 +97,17 @@ func (c *tlsCreds) ClientHandshake(ctx context.Context, authority string, rawCon
|
||||||
conn.Close()
|
conn.Close()
|
||||||
return nil, nil, ctx.Err()
|
return nil, nil, ctx.Err()
|
||||||
}
|
}
|
||||||
return internal.WrapSyscallConn(rawConn, conn), TLSInfo{conn.ConnectionState(), CommonAuthInfo{PrivacyAndIntegrity}}, nil
|
tlsInfo := TLSInfo{
|
||||||
|
State: conn.ConnectionState(),
|
||||||
|
CommonAuthInfo: CommonAuthInfo{
|
||||||
|
SecurityLevel: PrivacyAndIntegrity,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
id := credinternal.SPIFFEIDFromState(conn.ConnectionState())
|
||||||
|
if id != nil {
|
||||||
|
tlsInfo.SPIFFEID = id
|
||||||
|
}
|
||||||
|
return credinternal.WrapSyscallConn(rawConn, conn), tlsInfo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *tlsCreds) ServerHandshake(rawConn net.Conn) (net.Conn, AuthInfo, error) {
|
func (c *tlsCreds) ServerHandshake(rawConn net.Conn) (net.Conn, AuthInfo, error) {
|
||||||
|
@ -103,7 +116,17 @@ func (c *tlsCreds) ServerHandshake(rawConn net.Conn) (net.Conn, AuthInfo, error)
|
||||||
conn.Close()
|
conn.Close()
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
return internal.WrapSyscallConn(rawConn, conn), TLSInfo{conn.ConnectionState(), CommonAuthInfo{PrivacyAndIntegrity}}, nil
|
tlsInfo := TLSInfo{
|
||||||
|
State: conn.ConnectionState(),
|
||||||
|
CommonAuthInfo: CommonAuthInfo{
|
||||||
|
SecurityLevel: PrivacyAndIntegrity,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
id := credinternal.SPIFFEIDFromState(conn.ConnectionState())
|
||||||
|
if id != nil {
|
||||||
|
tlsInfo.SPIFFEID = id
|
||||||
|
}
|
||||||
|
return credinternal.WrapSyscallConn(rawConn, conn), tlsInfo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *tlsCreds) Clone() TransportCredentials {
|
func (c *tlsCreds) Clone() TransportCredentials {
|
||||||
|
@ -115,36 +138,33 @@ func (c *tlsCreds) OverrideServerName(serverNameOverride string) error {
|
||||||
return nil
|
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.
|
// NewTLS uses c to construct a TransportCredentials based on TLS.
|
||||||
func NewTLS(c *tls.Config) TransportCredentials {
|
func NewTLS(c *tls.Config) TransportCredentials {
|
||||||
tc := &tlsCreds{cloneTLSConfig(c)}
|
tc := &tlsCreds{credinternal.CloneTLSConfig(c)}
|
||||||
tc.config.NextProtos = appendH2ToNextProtos(tc.config.NextProtos)
|
tc.config.NextProtos = credinternal.AppendH2ToNextProtos(tc.config.NextProtos)
|
||||||
return tc
|
return tc
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewClientTLSFromCert constructs TLS credentials from the input certificate for client.
|
// 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,
|
// 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.
|
// it will override the virtual host name of authority (e.g. :authority header
|
||||||
|
// field) in requests.
|
||||||
func NewClientTLSFromCert(cp *x509.CertPool, serverNameOverride string) TransportCredentials {
|
func NewClientTLSFromCert(cp *x509.CertPool, serverNameOverride string) TransportCredentials {
|
||||||
return NewTLS(&tls.Config{ServerName: serverNameOverride, RootCAs: cp})
|
return NewTLS(&tls.Config{ServerName: serverNameOverride, RootCAs: cp})
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewClientTLSFromFile constructs TLS credentials from the input certificate file for client.
|
// 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,
|
// 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.
|
// it will override the virtual host name of authority (e.g. :authority header
|
||||||
|
// field) in requests.
|
||||||
func NewClientTLSFromFile(certFile, serverNameOverride string) (TransportCredentials, error) {
|
func NewClientTLSFromFile(certFile, serverNameOverride string) (TransportCredentials, error) {
|
||||||
b, err := ioutil.ReadFile(certFile)
|
b, err := ioutil.ReadFile(certFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -175,7 +195,10 @@ func NewServerTLSFromFile(certFile, keyFile string) (TransportCredentials, error
|
||||||
// TLSChannelzSecurityValue defines the struct that TLS protocol should return
|
// TLSChannelzSecurityValue defines the struct that TLS protocol should return
|
||||||
// from GetSecurityValue(), containing security info like cipher and certificate used.
|
// from GetSecurityValue(), containing security info like cipher and certificate used.
|
||||||
//
|
//
|
||||||
// This API is EXPERIMENTAL.
|
// Experimental
|
||||||
|
//
|
||||||
|
// Notice: This type is EXPERIMENTAL and may be changed or removed in a
|
||||||
|
// later release.
|
||||||
type TLSChannelzSecurityValue struct {
|
type TLSChannelzSecurityValue struct {
|
||||||
ChannelzSecurityValue
|
ChannelzSecurityValue
|
||||||
StandardName string
|
StandardName string
|
||||||
|
@ -208,18 +231,3 @@ var cipherSuiteLookup = map[uint16]string{
|
||||||
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305: "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305",
|
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",
|
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()
|
|
||||||
}
|
|
||||||
|
|
|
@ -27,7 +27,6 @@ import (
|
||||||
"google.golang.org/grpc/backoff"
|
"google.golang.org/grpc/backoff"
|
||||||
"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"
|
"google.golang.org/grpc/internal"
|
||||||
internalbackoff "google.golang.org/grpc/internal/backoff"
|
internalbackoff "google.golang.org/grpc/internal/backoff"
|
||||||
"google.golang.org/grpc/internal/envconfig"
|
"google.golang.org/grpc/internal/envconfig"
|
||||||
|
@ -50,14 +49,14 @@ type dialOptions struct {
|
||||||
dc Decompressor
|
dc Decompressor
|
||||||
bs internalbackoff.Strategy
|
bs internalbackoff.Strategy
|
||||||
block bool
|
block bool
|
||||||
|
returnLastError bool
|
||||||
insecure bool
|
insecure bool
|
||||||
timeout time.Duration
|
timeout time.Duration
|
||||||
scChan <-chan ServiceConfig
|
scChan <-chan ServiceConfig
|
||||||
authority string
|
authority string
|
||||||
copts transport.ConnectOptions
|
copts transport.ConnectOptions
|
||||||
callOptions []CallOption
|
callOptions []CallOption
|
||||||
// This is used by v1 balancer dial option WithBalancer to support v1
|
// This is used by WithBalancerName dial option.
|
||||||
// balancer, and also by WithBalancerName dial option.
|
|
||||||
balancerBuilder balancer.Builder
|
balancerBuilder balancer.Builder
|
||||||
channelzParentID int64
|
channelzParentID int64
|
||||||
disableServiceConfig bool
|
disableServiceConfig bool
|
||||||
|
@ -67,10 +66,6 @@ type dialOptions struct {
|
||||||
minConnectTimeout func() time.Duration
|
minConnectTimeout func() time.Duration
|
||||||
defaultServiceConfig *ServiceConfig // defaultServiceConfig is parsed from defaultServiceConfigRawJSON.
|
defaultServiceConfig *ServiceConfig // defaultServiceConfig is parsed from defaultServiceConfigRawJSON.
|
||||||
defaultServiceConfigRawJSON *string
|
defaultServiceConfigRawJSON *string
|
||||||
// This is used by ccResolverWrapper to backoff between successive calls to
|
|
||||||
// resolver.ResolveNow(). The user will have no need to configure this, but
|
|
||||||
// we need to be able to configure this in tests.
|
|
||||||
resolveNowBackoff func(int) time.Duration
|
|
||||||
resolvers []resolver.Builder
|
resolvers []resolver.Builder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,7 +77,10 @@ type DialOption interface {
|
||||||
// EmptyDialOption does not alter the dial configuration. It can be embedded in
|
// EmptyDialOption does not alter the dial configuration. It can be embedded in
|
||||||
// another structure to build custom dial options.
|
// another structure to build custom dial options.
|
||||||
//
|
//
|
||||||
// This API is EXPERIMENTAL.
|
// Experimental
|
||||||
|
//
|
||||||
|
// Notice: This type is EXPERIMENTAL and may be changed or removed in a
|
||||||
|
// later release.
|
||||||
type EmptyDialOption struct{}
|
type EmptyDialOption struct{}
|
||||||
|
|
||||||
func (EmptyDialOption) apply(*dialOptions) {}
|
func (EmptyDialOption) apply(*dialOptions) {}
|
||||||
|
@ -198,19 +196,6 @@ func WithDecompressor(dc Decompressor) DialOption {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithBalancer returns a DialOption which sets a load balancer with the v1 API.
|
|
||||||
// Name resolver will be ignored if this DialOption is specified.
|
|
||||||
//
|
|
||||||
// Deprecated: use the new balancer APIs in balancer package and
|
|
||||||
// WithBalancerName. Will be removed in a future 1.x release.
|
|
||||||
func WithBalancer(b Balancer) DialOption {
|
|
||||||
return newFuncDialOption(func(o *dialOptions) {
|
|
||||||
o.balancerBuilder = &balancerWrapperBuilder{
|
|
||||||
b: b,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithBalancerName sets the balancer that the ClientConn will be initialized
|
// WithBalancerName sets the balancer that the ClientConn will be initialized
|
||||||
// with. Balancer registered with balancerName will be used. This function
|
// with. Balancer registered with balancerName will be used. This function
|
||||||
// panics if no balancer was registered by balancerName.
|
// panics if no balancer was registered by balancerName.
|
||||||
|
@ -251,7 +236,10 @@ func WithServiceConfig(c <-chan ServiceConfig) DialOption {
|
||||||
// using the backoff.DefaultConfig as a base, in cases where you want to
|
// using the backoff.DefaultConfig as a base, in cases where you want to
|
||||||
// override only a subset of the backoff configuration.
|
// override only a subset of the backoff configuration.
|
||||||
//
|
//
|
||||||
// This API is EXPERIMENTAL.
|
// Experimental
|
||||||
|
//
|
||||||
|
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
|
||||||
|
// later release.
|
||||||
func WithConnectParams(p ConnectParams) DialOption {
|
func WithConnectParams(p ConnectParams) DialOption {
|
||||||
return newFuncDialOption(func(o *dialOptions) {
|
return newFuncDialOption(func(o *dialOptions) {
|
||||||
o.bs = internalbackoff.Exponential{Config: p.Backoff}
|
o.bs = internalbackoff.Exponential{Config: p.Backoff}
|
||||||
|
@ -298,6 +286,22 @@ func WithBlock() DialOption {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithReturnConnectionError returns a DialOption which makes the client connection
|
||||||
|
// return a string containing both the last connection error that occurred and
|
||||||
|
// the context.DeadlineExceeded error.
|
||||||
|
// Implies WithBlock()
|
||||||
|
//
|
||||||
|
// Experimental
|
||||||
|
//
|
||||||
|
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
|
||||||
|
// later release.
|
||||||
|
func WithReturnConnectionError() DialOption {
|
||||||
|
return newFuncDialOption(func(o *dialOptions) {
|
||||||
|
o.block = true
|
||||||
|
o.returnLastError = true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// WithInsecure returns a DialOption which disables transport security for this
|
// WithInsecure returns a DialOption which disables transport security for this
|
||||||
// ClientConn. Note that transport security is required unless WithInsecure is
|
// ClientConn. Note that transport security is required unless WithInsecure is
|
||||||
// set.
|
// set.
|
||||||
|
@ -307,6 +311,19 @@ 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.
|
||||||
|
//
|
||||||
|
// Experimental
|
||||||
|
//
|
||||||
|
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
|
||||||
|
// later release.
|
||||||
|
func WithNoProxy() DialOption {
|
||||||
|
return newFuncDialOption(func(o *dialOptions) {
|
||||||
|
o.copts.UseProxy = 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.
|
||||||
|
@ -328,7 +345,10 @@ func WithPerRPCCredentials(creds credentials.PerRPCCredentials) DialOption {
|
||||||
// the ClientConn.WithCreds. This should not be used together with
|
// the ClientConn.WithCreds. This should not be used together with
|
||||||
// WithTransportCredentials.
|
// WithTransportCredentials.
|
||||||
//
|
//
|
||||||
// This API is experimental.
|
// Experimental
|
||||||
|
//
|
||||||
|
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
|
||||||
|
// later release.
|
||||||
func WithCredentialsBundle(b credentials.Bundle) DialOption {
|
func WithCredentialsBundle(b credentials.Bundle) DialOption {
|
||||||
return newFuncDialOption(func(o *dialOptions) {
|
return newFuncDialOption(func(o *dialOptions) {
|
||||||
o.copts.CredsBundle = b
|
o.copts.CredsBundle = b
|
||||||
|
@ -393,7 +413,10 @@ func WithStatsHandler(h stats.Handler) DialOption {
|
||||||
// FailOnNonTempDialError only affects the initial dial, and does not do
|
// FailOnNonTempDialError only affects the initial dial, and does not do
|
||||||
// anything useful unless you are also using WithBlock().
|
// anything useful unless you are also using WithBlock().
|
||||||
//
|
//
|
||||||
// This is an EXPERIMENTAL API.
|
// Experimental
|
||||||
|
//
|
||||||
|
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
|
||||||
|
// later release.
|
||||||
func FailOnNonTempDialError(f bool) DialOption {
|
func FailOnNonTempDialError(f bool) DialOption {
|
||||||
return newFuncDialOption(func(o *dialOptions) {
|
return newFuncDialOption(func(o *dialOptions) {
|
||||||
o.copts.FailOnNonTempDialError = f
|
o.copts.FailOnNonTempDialError = f
|
||||||
|
@ -412,7 +435,7 @@ func WithUserAgent(s string) DialOption {
|
||||||
// for the client transport.
|
// for the client transport.
|
||||||
func WithKeepaliveParams(kp keepalive.ClientParameters) DialOption {
|
func WithKeepaliveParams(kp keepalive.ClientParameters) DialOption {
|
||||||
if kp.Time < internal.KeepaliveMinPingTime {
|
if kp.Time < internal.KeepaliveMinPingTime {
|
||||||
grpclog.Warningf("Adjusting keepalive ping interval to minimum period of %v", internal.KeepaliveMinPingTime)
|
logger.Warningf("Adjusting keepalive ping interval to minimum period of %v", internal.KeepaliveMinPingTime)
|
||||||
kp.Time = internal.KeepaliveMinPingTime
|
kp.Time = internal.KeepaliveMinPingTime
|
||||||
}
|
}
|
||||||
return newFuncDialOption(func(o *dialOptions) {
|
return newFuncDialOption(func(o *dialOptions) {
|
||||||
|
@ -448,7 +471,7 @@ func WithStreamInterceptor(f StreamClientInterceptor) DialOption {
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithChainStreamInterceptor returns a DialOption that specifies the chained
|
// WithChainStreamInterceptor returns a DialOption that specifies the chained
|
||||||
// interceptor for unary RPCs. The first interceptor will be the outer most,
|
// interceptor for streaming RPCs. The first interceptor will be the outer most,
|
||||||
// while the last interceptor will be the inner most wrapper around the real call.
|
// while the last interceptor will be the inner most wrapper around the real call.
|
||||||
// All interceptors added by this method will be chained, and the interceptor
|
// All interceptors added by this method will be chained, and the interceptor
|
||||||
// defined by WithStreamInterceptor will always be prepended to the chain.
|
// defined by WithStreamInterceptor will always be prepended to the chain.
|
||||||
|
@ -471,7 +494,10 @@ func WithAuthority(a string) DialOption {
|
||||||
// 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.
|
// Experimental
|
||||||
|
//
|
||||||
|
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
|
||||||
|
// later release.
|
||||||
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
|
||||||
|
@ -497,7 +523,10 @@ func WithDisableServiceConfig() DialOption {
|
||||||
// 2. Resolver does not return a service config or if the resolver returns an
|
// 2. Resolver does not return a service config or if the resolver returns an
|
||||||
// invalid service config.
|
// invalid service config.
|
||||||
//
|
//
|
||||||
// This API is EXPERIMENTAL.
|
// Experimental
|
||||||
|
//
|
||||||
|
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
|
||||||
|
// later release.
|
||||||
func WithDefaultServiceConfig(s string) DialOption {
|
func WithDefaultServiceConfig(s string) DialOption {
|
||||||
return newFuncDialOption(func(o *dialOptions) {
|
return newFuncDialOption(func(o *dialOptions) {
|
||||||
o.defaultServiceConfigRawJSON = &s
|
o.defaultServiceConfigRawJSON = &s
|
||||||
|
@ -513,7 +542,10 @@ func WithDefaultServiceConfig(s string) DialOption {
|
||||||
// default in the future. Until then, it may be enabled by setting the
|
// default in the future. Until then, it may be enabled by setting the
|
||||||
// environment variable "GRPC_GO_RETRY" to "on".
|
// environment variable "GRPC_GO_RETRY" to "on".
|
||||||
//
|
//
|
||||||
// This API is EXPERIMENTAL.
|
// Experimental
|
||||||
|
//
|
||||||
|
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
|
||||||
|
// later release.
|
||||||
func WithDisableRetry() DialOption {
|
func WithDisableRetry() DialOption {
|
||||||
return newFuncDialOption(func(o *dialOptions) {
|
return newFuncDialOption(func(o *dialOptions) {
|
||||||
o.disableRetry = true
|
o.disableRetry = true
|
||||||
|
@ -531,7 +563,10 @@ func WithMaxHeaderListSize(s uint32) DialOption {
|
||||||
// WithDisableHealthCheck disables the LB channel health checking for all
|
// WithDisableHealthCheck disables the LB channel health checking for all
|
||||||
// SubConns of this ClientConn.
|
// SubConns of this ClientConn.
|
||||||
//
|
//
|
||||||
// This API is EXPERIMENTAL.
|
// Experimental
|
||||||
|
//
|
||||||
|
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
|
||||||
|
// later release.
|
||||||
func WithDisableHealthCheck() DialOption {
|
func WithDisableHealthCheck() DialOption {
|
||||||
return newFuncDialOption(func(o *dialOptions) {
|
return newFuncDialOption(func(o *dialOptions) {
|
||||||
o.disableHealthCheck = true
|
o.disableHealthCheck = true
|
||||||
|
@ -555,8 +590,8 @@ func defaultDialOptions() dialOptions {
|
||||||
copts: transport.ConnectOptions{
|
copts: transport.ConnectOptions{
|
||||||
WriteBufferSize: defaultWriteBufSize,
|
WriteBufferSize: defaultWriteBufSize,
|
||||||
ReadBufferSize: defaultReadBufSize,
|
ReadBufferSize: defaultReadBufSize,
|
||||||
|
UseProxy: true,
|
||||||
},
|
},
|
||||||
resolveNowBackoff: internalbackoff.DefaultExponential.Backoff,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -571,22 +606,15 @@ func withMinConnectDeadline(f func() time.Duration) DialOption {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// withResolveNowBackoff specifies the function that clientconn uses to backoff
|
|
||||||
// between successive calls to resolver.ResolveNow().
|
|
||||||
//
|
|
||||||
// For testing purpose only.
|
|
||||||
func withResolveNowBackoff(f func(int) time.Duration) DialOption {
|
|
||||||
return newFuncDialOption(func(o *dialOptions) {
|
|
||||||
o.resolveNowBackoff = f
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithResolvers allows a list of resolver implementations to be registered
|
// WithResolvers allows a list of resolver implementations to be registered
|
||||||
// locally with the ClientConn without needing to be globally registered via
|
// locally with the ClientConn without needing to be globally registered via
|
||||||
// resolver.Register. They will be matched against the scheme used for the
|
// resolver.Register. They will be matched against the scheme used for the
|
||||||
// current Dial only, and will take precedence over the global registry.
|
// current Dial only, and will take precedence over the global registry.
|
||||||
//
|
//
|
||||||
// This API is EXPERIMENTAL.
|
// Experimental
|
||||||
|
//
|
||||||
|
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
|
||||||
|
// later release.
|
||||||
func WithResolvers(rs ...resolver.Builder) DialOption {
|
func WithResolvers(rs ...resolver.Builder) DialOption {
|
||||||
return newFuncDialOption(func(o *dialOptions) {
|
return newFuncDialOption(func(o *dialOptions) {
|
||||||
o.resolvers = append(o.resolvers, rs...)
|
o.resolvers = append(o.resolvers, rs...)
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
//go:generate ./regenerate.sh
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Package grpc implements an RPC system called gRPC.
|
Package grpc implements an RPC system called gRPC.
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,10 @@
|
||||||
// Package encoding defines the interface for the compressor and codec, and
|
// Package encoding defines the interface for the compressor and codec, and
|
||||||
// functions to register and retrieve compressors and codecs.
|
// functions to register and retrieve compressors and codecs.
|
||||||
//
|
//
|
||||||
// This package is EXPERIMENTAL.
|
// Experimental
|
||||||
|
//
|
||||||
|
// Notice: This package is EXPERIMENTAL and may be changed or removed in a
|
||||||
|
// later release.
|
||||||
package encoding
|
package encoding
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -46,10 +49,15 @@ type Compressor interface {
|
||||||
// coding header. The result must be static; the result cannot change
|
// coding header. The result must be static; the result cannot change
|
||||||
// between calls.
|
// between calls.
|
||||||
Name() string
|
Name() string
|
||||||
// EXPERIMENTAL: if a Compressor implements
|
// If a Compressor implements
|
||||||
// DecompressedSize(compressedBytes []byte) int, gRPC will call it
|
// DecompressedSize(compressedBytes []byte) int, gRPC will call it
|
||||||
// to determine the size of the buffer allocated for the result of decompression.
|
// to determine the size of the buffer allocated for the result of decompression.
|
||||||
// Return -1 to indicate unknown size.
|
// Return -1 to indicate unknown size.
|
||||||
|
//
|
||||||
|
// Experimental
|
||||||
|
//
|
||||||
|
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
|
||||||
|
// later release.
|
||||||
}
|
}
|
||||||
|
|
||||||
var registeredCompressor = make(map[string]Compressor)
|
var registeredCompressor = make(map[string]Compressor)
|
||||||
|
|
|
@ -21,8 +21,7 @@
|
||||||
package proto
|
package proto
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math"
|
"fmt"
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/golang/protobuf/proto"
|
"github.com/golang/protobuf/proto"
|
||||||
"google.golang.org/grpc/encoding"
|
"google.golang.org/grpc/encoding"
|
||||||
|
@ -38,73 +37,22 @@ func init() {
|
||||||
// codec is a Codec implementation with protobuf. It is the default codec for gRPC.
|
// codec is a Codec implementation with protobuf. It is the default codec for gRPC.
|
||||||
type codec struct{}
|
type codec struct{}
|
||||||
|
|
||||||
type cachedProtoBuffer struct {
|
|
||||||
lastMarshaledSize uint32
|
|
||||||
proto.Buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
func capToMaxInt32(val int) uint32 {
|
|
||||||
if val > math.MaxInt32 {
|
|
||||||
return uint32(math.MaxInt32)
|
|
||||||
}
|
|
||||||
return uint32(val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func marshal(v interface{}, cb *cachedProtoBuffer) ([]byte, error) {
|
|
||||||
protoMsg := v.(proto.Message)
|
|
||||||
newSlice := make([]byte, 0, cb.lastMarshaledSize)
|
|
||||||
|
|
||||||
cb.SetBuf(newSlice)
|
|
||||||
cb.Reset()
|
|
||||||
if err := cb.Marshal(protoMsg); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
out := cb.Bytes()
|
|
||||||
cb.lastMarshaledSize = capToMaxInt32(len(out))
|
|
||||||
return out, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (codec) Marshal(v interface{}) ([]byte, error) {
|
func (codec) Marshal(v interface{}) ([]byte, error) {
|
||||||
if pm, ok := v.(proto.Marshaler); ok {
|
vv, ok := v.(proto.Message)
|
||||||
// object can marshal itself, no need for buffer
|
if !ok {
|
||||||
return pm.Marshal()
|
return nil, fmt.Errorf("failed to marshal, message is %T, want proto.Message", v)
|
||||||
}
|
}
|
||||||
|
return proto.Marshal(vv)
|
||||||
cb := protoBufferPool.Get().(*cachedProtoBuffer)
|
|
||||||
out, err := marshal(v, cb)
|
|
||||||
|
|
||||||
// put back buffer and lose the ref to the slice
|
|
||||||
cb.SetBuf(nil)
|
|
||||||
protoBufferPool.Put(cb)
|
|
||||||
return out, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (codec) Unmarshal(data []byte, v interface{}) error {
|
func (codec) Unmarshal(data []byte, v interface{}) error {
|
||||||
protoMsg := v.(proto.Message)
|
vv, ok := v.(proto.Message)
|
||||||
protoMsg.Reset()
|
if !ok {
|
||||||
|
return fmt.Errorf("failed to unmarshal, message is %T, want proto.Message", v)
|
||||||
if pu, ok := protoMsg.(proto.Unmarshaler); ok {
|
|
||||||
// object can unmarshal itself, no need for buffer
|
|
||||||
return pu.Unmarshal(data)
|
|
||||||
}
|
}
|
||||||
|
return proto.Unmarshal(data, vv)
|
||||||
cb := protoBufferPool.Get().(*cachedProtoBuffer)
|
|
||||||
cb.SetBuf(data)
|
|
||||||
err := cb.Unmarshal(protoMsg)
|
|
||||||
cb.SetBuf(nil)
|
|
||||||
protoBufferPool.Put(cb)
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (codec) Name() string {
|
func (codec) Name() string {
|
||||||
return Name
|
return Name
|
||||||
}
|
}
|
||||||
|
|
||||||
var protoBufferPool = &sync.Pool{
|
|
||||||
New: func() interface{} {
|
|
||||||
return &cachedProtoBuffer{
|
|
||||||
Buffer: proto.Buffer{},
|
|
||||||
lastMarshaledSize: 16,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
|
@ -3,14 +3,15 @@ module google.golang.org/grpc
|
||||||
go 1.11
|
go 1.11
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473
|
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0
|
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d
|
||||||
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/protobuf v1.4.2
|
||||||
github.com/golang/protobuf v1.3.2
|
github.com/google/go-cmp v0.5.0
|
||||||
github.com/google/go-cmp v0.2.0
|
github.com/google/uuid v1.1.2
|
||||||
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
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a
|
||||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55
|
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013
|
||||||
|
google.golang.org/protobuf v1.25.0
|
||||||
)
|
)
|
||||||
|
|
|
@ -0,0 +1,117 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"google.golang.org/grpc/internal/grpclog"
|
||||||
|
)
|
||||||
|
|
||||||
|
// componentData records the settings for a component.
|
||||||
|
type componentData struct {
|
||||||
|
name string
|
||||||
|
}
|
||||||
|
|
||||||
|
var cache = map[string]*componentData{}
|
||||||
|
|
||||||
|
func (c *componentData) InfoDepth(depth int, args ...interface{}) {
|
||||||
|
args = append([]interface{}{"[" + string(c.name) + "]"}, args...)
|
||||||
|
grpclog.InfoDepth(depth+1, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *componentData) WarningDepth(depth int, args ...interface{}) {
|
||||||
|
args = append([]interface{}{"[" + string(c.name) + "]"}, args...)
|
||||||
|
grpclog.WarningDepth(depth+1, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *componentData) ErrorDepth(depth int, args ...interface{}) {
|
||||||
|
args = append([]interface{}{"[" + string(c.name) + "]"}, args...)
|
||||||
|
grpclog.ErrorDepth(depth+1, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *componentData) FatalDepth(depth int, args ...interface{}) {
|
||||||
|
args = append([]interface{}{"[" + string(c.name) + "]"}, args...)
|
||||||
|
grpclog.FatalDepth(depth+1, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *componentData) Info(args ...interface{}) {
|
||||||
|
c.InfoDepth(1, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *componentData) Warning(args ...interface{}) {
|
||||||
|
c.WarningDepth(1, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *componentData) Error(args ...interface{}) {
|
||||||
|
c.ErrorDepth(1, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *componentData) Fatal(args ...interface{}) {
|
||||||
|
c.FatalDepth(1, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *componentData) Infof(format string, args ...interface{}) {
|
||||||
|
c.InfoDepth(1, fmt.Sprintf(format, args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *componentData) Warningf(format string, args ...interface{}) {
|
||||||
|
c.WarningDepth(1, fmt.Sprintf(format, args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *componentData) Errorf(format string, args ...interface{}) {
|
||||||
|
c.ErrorDepth(1, fmt.Sprintf(format, args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *componentData) Fatalf(format string, args ...interface{}) {
|
||||||
|
c.FatalDepth(1, fmt.Sprintf(format, args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *componentData) Infoln(args ...interface{}) {
|
||||||
|
c.InfoDepth(1, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *componentData) Warningln(args ...interface{}) {
|
||||||
|
c.WarningDepth(1, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *componentData) Errorln(args ...interface{}) {
|
||||||
|
c.ErrorDepth(1, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *componentData) Fatalln(args ...interface{}) {
|
||||||
|
c.FatalDepth(1, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *componentData) V(l int) bool {
|
||||||
|
return V(l)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Component creates a new component and returns it for logging. If a component
|
||||||
|
// with the name already exists, nothing will be created and it will be
|
||||||
|
// returned. SetLoggerV2 will panic if it is called with a logger created by
|
||||||
|
// Component.
|
||||||
|
func Component(componentName string) DepthLoggerV2 {
|
||||||
|
if cData, ok := cache[componentName]; ok {
|
||||||
|
return cData
|
||||||
|
}
|
||||||
|
c := &componentData{componentName}
|
||||||
|
cache[componentName] = c
|
||||||
|
return c
|
||||||
|
}
|
|
@ -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,11 @@ 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
|
if _, ok := l.(*componentData); ok {
|
||||||
|
panic("cannot use component logger as grpclog logger")
|
||||||
|
}
|
||||||
|
grpclog.Logger = l
|
||||||
|
grpclog.DepthLogger, _ = l.(grpclog.DepthLoggerV2)
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -193,3 +199,23 @@ 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.
|
||||||
|
//
|
||||||
|
// Experimental
|
||||||
|
//
|
||||||
|
// Notice: This type is EXPERIMENTAL and may be changed or removed in a
|
||||||
|
// later release.
|
||||||
|
type DepthLoggerV2 interface {
|
||||||
|
LoggerV2
|
||||||
|
// 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{})
|
||||||
|
}
|
||||||
|
|
|
@ -1,28 +1,46 @@
|
||||||
|
// Copyright 2015 The 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.
|
||||||
|
|
||||||
|
// The canonical version of this proto can be found at
|
||||||
|
// https://github.com/grpc/grpc-proto/blob/master/grpc/health/v1/health.proto
|
||||||
|
|
||||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// protoc-gen-go v1.25.0
|
||||||
|
// protoc v3.14.0
|
||||||
// source: grpc/health/v1/health.proto
|
// source: grpc/health/v1/health.proto
|
||||||
|
|
||||||
package grpc_health_v1
|
package grpc_health_v1
|
||||||
|
|
||||||
import (
|
import (
|
||||||
context "context"
|
|
||||||
fmt "fmt"
|
|
||||||
proto "github.com/golang/protobuf/proto"
|
proto "github.com/golang/protobuf/proto"
|
||||||
grpc "google.golang.org/grpc"
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
codes "google.golang.org/grpc/codes"
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
status "google.golang.org/grpc/status"
|
reflect "reflect"
|
||||||
math "math"
|
sync "sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Reference imports to suppress errors if they are not otherwise used.
|
const (
|
||||||
var _ = proto.Marshal
|
// Verify that this generated code is sufficiently up-to-date.
|
||||||
var _ = fmt.Errorf
|
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||||
var _ = math.Inf
|
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
|
)
|
||||||
|
|
||||||
// This is a compile-time assertion to ensure that this generated file
|
// This is a compile-time assertion that a sufficiently up-to-date version
|
||||||
// is compatible with the proto package it is being compiled against.
|
// of the legacy proto package is being used.
|
||||||
// A compilation error at this line likely means your copy of the
|
const _ = proto.ProtoPackageIsVersion4
|
||||||
// proto package needs to be updated.
|
|
||||||
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
|
||||||
|
|
||||||
type HealthCheckResponse_ServingStatus int32
|
type HealthCheckResponse_ServingStatus int32
|
||||||
|
|
||||||
|
@ -30,314 +48,266 @@ const (
|
||||||
HealthCheckResponse_UNKNOWN HealthCheckResponse_ServingStatus = 0
|
HealthCheckResponse_UNKNOWN HealthCheckResponse_ServingStatus = 0
|
||||||
HealthCheckResponse_SERVING HealthCheckResponse_ServingStatus = 1
|
HealthCheckResponse_SERVING HealthCheckResponse_ServingStatus = 1
|
||||||
HealthCheckResponse_NOT_SERVING HealthCheckResponse_ServingStatus = 2
|
HealthCheckResponse_NOT_SERVING HealthCheckResponse_ServingStatus = 2
|
||||||
HealthCheckResponse_SERVICE_UNKNOWN HealthCheckResponse_ServingStatus = 3
|
HealthCheckResponse_SERVICE_UNKNOWN HealthCheckResponse_ServingStatus = 3 // Used only by the Watch method.
|
||||||
)
|
)
|
||||||
|
|
||||||
var HealthCheckResponse_ServingStatus_name = map[int32]string{
|
// Enum value maps for HealthCheckResponse_ServingStatus.
|
||||||
|
var (
|
||||||
|
HealthCheckResponse_ServingStatus_name = map[int32]string{
|
||||||
0: "UNKNOWN",
|
0: "UNKNOWN",
|
||||||
1: "SERVING",
|
1: "SERVING",
|
||||||
2: "NOT_SERVING",
|
2: "NOT_SERVING",
|
||||||
3: "SERVICE_UNKNOWN",
|
3: "SERVICE_UNKNOWN",
|
||||||
}
|
}
|
||||||
|
HealthCheckResponse_ServingStatus_value = map[string]int32{
|
||||||
var HealthCheckResponse_ServingStatus_value = map[string]int32{
|
|
||||||
"UNKNOWN": 0,
|
"UNKNOWN": 0,
|
||||||
"SERVING": 1,
|
"SERVING": 1,
|
||||||
"NOT_SERVING": 2,
|
"NOT_SERVING": 2,
|
||||||
"SERVICE_UNKNOWN": 3,
|
"SERVICE_UNKNOWN": 3,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (x HealthCheckResponse_ServingStatus) Enum() *HealthCheckResponse_ServingStatus {
|
||||||
|
p := new(HealthCheckResponse_ServingStatus)
|
||||||
|
*p = x
|
||||||
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x HealthCheckResponse_ServingStatus) String() string {
|
func (x HealthCheckResponse_ServingStatus) String() string {
|
||||||
return proto.EnumName(HealthCheckResponse_ServingStatus_name, int32(x))
|
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (HealthCheckResponse_ServingStatus) Descriptor() protoreflect.EnumDescriptor {
|
||||||
|
return file_grpc_health_v1_health_proto_enumTypes[0].Descriptor()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (HealthCheckResponse_ServingStatus) Type() protoreflect.EnumType {
|
||||||
|
return &file_grpc_health_v1_health_proto_enumTypes[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x HealthCheckResponse_ServingStatus) Number() protoreflect.EnumNumber {
|
||||||
|
return protoreflect.EnumNumber(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use HealthCheckResponse_ServingStatus.Descriptor instead.
|
||||||
func (HealthCheckResponse_ServingStatus) EnumDescriptor() ([]byte, []int) {
|
func (HealthCheckResponse_ServingStatus) EnumDescriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_e265fd9d4e077217, []int{1, 0}
|
return file_grpc_health_v1_health_proto_rawDescGZIP(), []int{1, 0}
|
||||||
}
|
}
|
||||||
|
|
||||||
type HealthCheckRequest struct {
|
type HealthCheckRequest struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
Service string `protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"`
|
Service string `protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"`
|
||||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
|
||||||
XXX_unrecognized []byte `json:"-"`
|
|
||||||
XXX_sizecache int32 `json:"-"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *HealthCheckRequest) Reset() { *m = HealthCheckRequest{} }
|
func (x *HealthCheckRequest) Reset() {
|
||||||
func (m *HealthCheckRequest) String() string { return proto.CompactTextString(m) }
|
*x = HealthCheckRequest{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_grpc_health_v1_health_proto_msgTypes[0]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HealthCheckRequest) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
func (*HealthCheckRequest) ProtoMessage() {}
|
func (*HealthCheckRequest) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *HealthCheckRequest) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_grpc_health_v1_health_proto_msgTypes[0]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use HealthCheckRequest.ProtoReflect.Descriptor instead.
|
||||||
func (*HealthCheckRequest) Descriptor() ([]byte, []int) {
|
func (*HealthCheckRequest) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_e265fd9d4e077217, []int{0}
|
return file_grpc_health_v1_health_proto_rawDescGZIP(), []int{0}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *HealthCheckRequest) XXX_Unmarshal(b []byte) error {
|
func (x *HealthCheckRequest) GetService() string {
|
||||||
return xxx_messageInfo_HealthCheckRequest.Unmarshal(m, b)
|
if x != nil {
|
||||||
}
|
return x.Service
|
||||||
func (m *HealthCheckRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
|
||||||
return xxx_messageInfo_HealthCheckRequest.Marshal(b, m, deterministic)
|
|
||||||
}
|
|
||||||
func (m *HealthCheckRequest) XXX_Merge(src proto.Message) {
|
|
||||||
xxx_messageInfo_HealthCheckRequest.Merge(m, src)
|
|
||||||
}
|
|
||||||
func (m *HealthCheckRequest) XXX_Size() int {
|
|
||||||
return xxx_messageInfo_HealthCheckRequest.Size(m)
|
|
||||||
}
|
|
||||||
func (m *HealthCheckRequest) XXX_DiscardUnknown() {
|
|
||||||
xxx_messageInfo_HealthCheckRequest.DiscardUnknown(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
var xxx_messageInfo_HealthCheckRequest proto.InternalMessageInfo
|
|
||||||
|
|
||||||
func (m *HealthCheckRequest) GetService() string {
|
|
||||||
if m != nil {
|
|
||||||
return m.Service
|
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
type HealthCheckResponse struct {
|
type HealthCheckResponse struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
Status HealthCheckResponse_ServingStatus `protobuf:"varint,1,opt,name=status,proto3,enum=grpc.health.v1.HealthCheckResponse_ServingStatus" json:"status,omitempty"`
|
Status HealthCheckResponse_ServingStatus `protobuf:"varint,1,opt,name=status,proto3,enum=grpc.health.v1.HealthCheckResponse_ServingStatus" json:"status,omitempty"`
|
||||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
|
||||||
XXX_unrecognized []byte `json:"-"`
|
|
||||||
XXX_sizecache int32 `json:"-"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *HealthCheckResponse) Reset() { *m = HealthCheckResponse{} }
|
func (x *HealthCheckResponse) Reset() {
|
||||||
func (m *HealthCheckResponse) String() string { return proto.CompactTextString(m) }
|
*x = HealthCheckResponse{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_grpc_health_v1_health_proto_msgTypes[1]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *HealthCheckResponse) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
func (*HealthCheckResponse) ProtoMessage() {}
|
func (*HealthCheckResponse) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *HealthCheckResponse) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_grpc_health_v1_health_proto_msgTypes[1]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use HealthCheckResponse.ProtoReflect.Descriptor instead.
|
||||||
func (*HealthCheckResponse) Descriptor() ([]byte, []int) {
|
func (*HealthCheckResponse) Descriptor() ([]byte, []int) {
|
||||||
return fileDescriptor_e265fd9d4e077217, []int{1}
|
return file_grpc_health_v1_health_proto_rawDescGZIP(), []int{1}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *HealthCheckResponse) XXX_Unmarshal(b []byte) error {
|
func (x *HealthCheckResponse) GetStatus() HealthCheckResponse_ServingStatus {
|
||||||
return xxx_messageInfo_HealthCheckResponse.Unmarshal(m, b)
|
if x != nil {
|
||||||
}
|
return x.Status
|
||||||
func (m *HealthCheckResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
|
||||||
return xxx_messageInfo_HealthCheckResponse.Marshal(b, m, deterministic)
|
|
||||||
}
|
|
||||||
func (m *HealthCheckResponse) XXX_Merge(src proto.Message) {
|
|
||||||
xxx_messageInfo_HealthCheckResponse.Merge(m, src)
|
|
||||||
}
|
|
||||||
func (m *HealthCheckResponse) XXX_Size() int {
|
|
||||||
return xxx_messageInfo_HealthCheckResponse.Size(m)
|
|
||||||
}
|
|
||||||
func (m *HealthCheckResponse) XXX_DiscardUnknown() {
|
|
||||||
xxx_messageInfo_HealthCheckResponse.DiscardUnknown(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
var xxx_messageInfo_HealthCheckResponse proto.InternalMessageInfo
|
|
||||||
|
|
||||||
func (m *HealthCheckResponse) GetStatus() HealthCheckResponse_ServingStatus {
|
|
||||||
if m != nil {
|
|
||||||
return m.Status
|
|
||||||
}
|
}
|
||||||
return HealthCheckResponse_UNKNOWN
|
return HealthCheckResponse_UNKNOWN
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
var File_grpc_health_v1_health_proto protoreflect.FileDescriptor
|
||||||
proto.RegisterEnum("grpc.health.v1.HealthCheckResponse_ServingStatus", HealthCheckResponse_ServingStatus_name, HealthCheckResponse_ServingStatus_value)
|
|
||||||
proto.RegisterType((*HealthCheckRequest)(nil), "grpc.health.v1.HealthCheckRequest")
|
var file_grpc_health_v1_health_proto_rawDesc = []byte{
|
||||||
proto.RegisterType((*HealthCheckResponse)(nil), "grpc.health.v1.HealthCheckResponse")
|
0x0a, 0x1b, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x2f, 0x76, 0x31,
|
||||||
|
0x2f, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0e, 0x67,
|
||||||
|
0x72, 0x70, 0x63, 0x2e, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x22, 0x2e, 0x0a,
|
||||||
|
0x12, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75,
|
||||||
|
0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x01,
|
||||||
|
0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x22, 0xb1, 0x01,
|
||||||
|
0x0a, 0x13, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73,
|
||||||
|
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x49, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18,
|
||||||
|
0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x31, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x68, 0x65, 0x61,
|
||||||
|
0x6c, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65,
|
||||||
|
0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69,
|
||||||
|
0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73,
|
||||||
|
0x22, 0x4f, 0x0a, 0x0d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x75,
|
||||||
|
0x73, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0b,
|
||||||
|
0x0a, 0x07, 0x53, 0x45, 0x52, 0x56, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x0f, 0x0a, 0x0b, 0x4e,
|
||||||
|
0x4f, 0x54, 0x5f, 0x53, 0x45, 0x52, 0x56, 0x49, 0x4e, 0x47, 0x10, 0x02, 0x12, 0x13, 0x0a, 0x0f,
|
||||||
|
0x53, 0x45, 0x52, 0x56, 0x49, 0x43, 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10,
|
||||||
|
0x03, 0x32, 0xae, 0x01, 0x0a, 0x06, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x12, 0x50, 0x0a, 0x05,
|
||||||
|
0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x22, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x68, 0x65, 0x61,
|
||||||
|
0x6c, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43, 0x68, 0x65,
|
||||||
|
0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x67, 0x72, 0x70, 0x63,
|
||||||
|
0x2e, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x65, 0x61, 0x6c, 0x74,
|
||||||
|
0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x52,
|
||||||
|
0x0a, 0x05, 0x57, 0x61, 0x74, 0x63, 0x68, 0x12, 0x22, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x68,
|
||||||
|
0x65, 0x61, 0x6c, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x43,
|
||||||
|
0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x67, 0x72,
|
||||||
|
0x70, 0x63, 0x2e, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x65, 0x61,
|
||||||
|
0x6c, 0x74, 0x68, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
||||||
|
0x30, 0x01, 0x42, 0x61, 0x0a, 0x11, 0x69, 0x6f, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x68, 0x65,
|
||||||
|
0x61, 0x6c, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x42, 0x0b, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x50,
|
||||||
|
0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x2c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x67,
|
||||||
|
0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x68,
|
||||||
|
0x65, 0x61, 0x6c, 0x74, 0x68, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x5f, 0x68, 0x65, 0x61, 0x6c, 0x74,
|
||||||
|
0x68, 0x5f, 0x76, 0x31, 0xaa, 0x02, 0x0e, 0x47, 0x72, 0x70, 0x63, 0x2e, 0x48, 0x65, 0x61, 0x6c,
|
||||||
|
0x74, 0x68, 0x2e, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() { proto.RegisterFile("grpc/health/v1/health.proto", fileDescriptor_e265fd9d4e077217) }
|
var (
|
||||||
|
file_grpc_health_v1_health_proto_rawDescOnce sync.Once
|
||||||
|
file_grpc_health_v1_health_proto_rawDescData = file_grpc_health_v1_health_proto_rawDesc
|
||||||
|
)
|
||||||
|
|
||||||
var fileDescriptor_e265fd9d4e077217 = []byte{
|
func file_grpc_health_v1_health_proto_rawDescGZIP() []byte {
|
||||||
// 297 bytes of a gzipped FileDescriptorProto
|
file_grpc_health_v1_health_proto_rawDescOnce.Do(func() {
|
||||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4e, 0x2f, 0x2a, 0x48,
|
file_grpc_health_v1_health_proto_rawDescData = protoimpl.X.CompressGZIP(file_grpc_health_v1_health_proto_rawDescData)
|
||||||
0xd6, 0xcf, 0x48, 0x4d, 0xcc, 0x29, 0xc9, 0xd0, 0x2f, 0x33, 0x84, 0xb2, 0xf4, 0x0a, 0x8a, 0xf2,
|
})
|
||||||
0x4b, 0xf2, 0x85, 0xf8, 0x40, 0x92, 0x7a, 0x50, 0xa1, 0x32, 0x43, 0x25, 0x3d, 0x2e, 0x21, 0x0f,
|
return file_grpc_health_v1_health_proto_rawDescData
|
||||||
0x30, 0xc7, 0x39, 0x23, 0x35, 0x39, 0x3b, 0x28, 0xb5, 0xb0, 0x34, 0xb5, 0xb8, 0x44, 0x48, 0x82,
|
|
||||||
0x8b, 0xbd, 0x38, 0xb5, 0xa8, 0x2c, 0x33, 0x39, 0x55, 0x82, 0x51, 0x81, 0x51, 0x83, 0x33, 0x08,
|
|
||||||
0xc6, 0x55, 0xda, 0xc8, 0xc8, 0x25, 0x8c, 0xa2, 0xa1, 0xb8, 0x20, 0x3f, 0xaf, 0x38, 0x55, 0xc8,
|
|
||||||
0x93, 0x8b, 0xad, 0xb8, 0x24, 0xb1, 0xa4, 0xb4, 0x18, 0xac, 0x81, 0xcf, 0xc8, 0x50, 0x0f, 0xd5,
|
|
||||||
0x22, 0x3d, 0x2c, 0x9a, 0xf4, 0x82, 0x41, 0x86, 0xe6, 0xa5, 0x07, 0x83, 0x35, 0x06, 0x41, 0x0d,
|
|
||||||
0x50, 0xf2, 0xe7, 0xe2, 0x45, 0x91, 0x10, 0xe2, 0xe6, 0x62, 0x0f, 0xf5, 0xf3, 0xf6, 0xf3, 0x0f,
|
|
||||||
0xf7, 0x13, 0x60, 0x00, 0x71, 0x82, 0x5d, 0x83, 0xc2, 0x3c, 0xfd, 0xdc, 0x05, 0x18, 0x85, 0xf8,
|
|
||||||
0xb9, 0xb8, 0xfd, 0xfc, 0x43, 0xe2, 0x61, 0x02, 0x4c, 0x42, 0xc2, 0x5c, 0xfc, 0x60, 0x8e, 0xb3,
|
|
||||||
0x6b, 0x3c, 0x4c, 0x0b, 0xb3, 0xd1, 0x3a, 0x46, 0x2e, 0x36, 0x88, 0xf5, 0x42, 0x01, 0x5c, 0xac,
|
|
||||||
0x60, 0x27, 0x08, 0x29, 0xe1, 0x75, 0x1f, 0x38, 0x14, 0xa4, 0x94, 0x89, 0xf0, 0x83, 0x50, 0x10,
|
|
||||||
0x17, 0x6b, 0x78, 0x62, 0x49, 0x72, 0x06, 0xd5, 0x4c, 0x34, 0x60, 0x74, 0x4a, 0xe4, 0x12, 0xcc,
|
|
||||||
0xcc, 0x47, 0x53, 0xea, 0xc4, 0x0d, 0x51, 0x1b, 0x00, 0x8a, 0xc6, 0x00, 0xc6, 0x28, 0x9d, 0xf4,
|
|
||||||
0xfc, 0xfc, 0xf4, 0x9c, 0x54, 0xbd, 0xf4, 0xfc, 0x9c, 0xc4, 0xbc, 0x74, 0xbd, 0xfc, 0xa2, 0x74,
|
|
||||||
0x7d, 0xe4, 0x78, 0x07, 0xb1, 0xe3, 0x21, 0xec, 0xf8, 0x32, 0xc3, 0x55, 0x4c, 0x7c, 0xee, 0x20,
|
|
||||||
0xd3, 0x20, 0x46, 0xe8, 0x85, 0x19, 0x26, 0xb1, 0x81, 0x93, 0x83, 0x31, 0x20, 0x00, 0x00, 0xff,
|
|
||||||
0xff, 0x12, 0x7d, 0x96, 0xcb, 0x2d, 0x02, 0x00, 0x00,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reference imports to suppress errors if they are not otherwise used.
|
var file_grpc_health_v1_health_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
|
||||||
var _ context.Context
|
var file_grpc_health_v1_health_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
|
||||||
var _ grpc.ClientConn
|
var file_grpc_health_v1_health_proto_goTypes = []interface{}{
|
||||||
|
(HealthCheckResponse_ServingStatus)(0), // 0: grpc.health.v1.HealthCheckResponse.ServingStatus
|
||||||
// This is a compile-time assertion to ensure that this generated file
|
(*HealthCheckRequest)(nil), // 1: grpc.health.v1.HealthCheckRequest
|
||||||
// is compatible with the grpc package it is being compiled against.
|
(*HealthCheckResponse)(nil), // 2: grpc.health.v1.HealthCheckResponse
|
||||||
const _ = grpc.SupportPackageIsVersion4
|
}
|
||||||
|
var file_grpc_health_v1_health_proto_depIdxs = []int32{
|
||||||
// HealthClient is the client API for Health service.
|
0, // 0: grpc.health.v1.HealthCheckResponse.status:type_name -> grpc.health.v1.HealthCheckResponse.ServingStatus
|
||||||
//
|
1, // 1: grpc.health.v1.Health.Check:input_type -> grpc.health.v1.HealthCheckRequest
|
||||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
|
1, // 2: grpc.health.v1.Health.Watch:input_type -> grpc.health.v1.HealthCheckRequest
|
||||||
type HealthClient interface {
|
2, // 3: grpc.health.v1.Health.Check:output_type -> grpc.health.v1.HealthCheckResponse
|
||||||
// If the requested service is unknown, the call will fail with status
|
2, // 4: grpc.health.v1.Health.Watch:output_type -> grpc.health.v1.HealthCheckResponse
|
||||||
// NOT_FOUND.
|
3, // [3:5] is the sub-list for method output_type
|
||||||
Check(ctx context.Context, in *HealthCheckRequest, opts ...grpc.CallOption) (*HealthCheckResponse, error)
|
1, // [1:3] is the sub-list for method input_type
|
||||||
// Performs a watch for the serving status of the requested service.
|
1, // [1:1] is the sub-list for extension type_name
|
||||||
// The server will immediately send back a message indicating the current
|
1, // [1:1] is the sub-list for extension extendee
|
||||||
// serving status. It will then subsequently send a new message whenever
|
0, // [0:1] is the sub-list for field type_name
|
||||||
// the service's serving status changes.
|
|
||||||
//
|
|
||||||
// If the requested service is unknown when the call is received, the
|
|
||||||
// server will send a message setting the serving status to
|
|
||||||
// SERVICE_UNKNOWN but will *not* terminate the call. If at some
|
|
||||||
// future point, the serving status of the service becomes known, the
|
|
||||||
// server will send a new message with the service's serving status.
|
|
||||||
//
|
|
||||||
// If the call terminates with status UNIMPLEMENTED, then clients
|
|
||||||
// should assume this method is not supported and should not retry the
|
|
||||||
// call. If the call terminates with any other status (including OK),
|
|
||||||
// clients should retry the call with appropriate exponential backoff.
|
|
||||||
Watch(ctx context.Context, in *HealthCheckRequest, opts ...grpc.CallOption) (Health_WatchClient, error)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type healthClient struct {
|
func init() { file_grpc_health_v1_health_proto_init() }
|
||||||
cc *grpc.ClientConn
|
func file_grpc_health_v1_health_proto_init() {
|
||||||
}
|
if File_grpc_health_v1_health_proto != nil {
|
||||||
|
return
|
||||||
func NewHealthClient(cc *grpc.ClientConn) HealthClient {
|
|
||||||
return &healthClient{cc}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *healthClient) Check(ctx context.Context, in *HealthCheckRequest, opts ...grpc.CallOption) (*HealthCheckResponse, error) {
|
|
||||||
out := new(HealthCheckResponse)
|
|
||||||
err := c.cc.Invoke(ctx, "/grpc.health.v1.Health/Check", in, out, opts...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
return out, nil
|
if !protoimpl.UnsafeEnabled {
|
||||||
}
|
file_grpc_health_v1_health_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*HealthCheckRequest); i {
|
||||||
func (c *healthClient) Watch(ctx context.Context, in *HealthCheckRequest, opts ...grpc.CallOption) (Health_WatchClient, error) {
|
case 0:
|
||||||
stream, err := c.cc.NewStream(ctx, &_Health_serviceDesc.Streams[0], "/grpc.health.v1.Health/Watch", opts...)
|
return &v.state
|
||||||
if err != nil {
|
case 1:
|
||||||
return nil, err
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
x := &healthWatchClient{stream}
|
|
||||||
if err := x.ClientStream.SendMsg(in); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
if err := x.ClientStream.CloseSend(); err != nil {
|
file_grpc_health_v1_health_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||||
return nil, err
|
switch v := v.(*HealthCheckResponse); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
return x, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type Health_WatchClient interface {
|
|
||||||
Recv() (*HealthCheckResponse, error)
|
|
||||||
grpc.ClientStream
|
|
||||||
}
|
|
||||||
|
|
||||||
type healthWatchClient struct {
|
|
||||||
grpc.ClientStream
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *healthWatchClient) Recv() (*HealthCheckResponse, error) {
|
|
||||||
m := new(HealthCheckResponse)
|
|
||||||
if err := x.ClientStream.RecvMsg(m); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
return m, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// HealthServer is the server API for Health service.
|
|
||||||
type HealthServer interface {
|
|
||||||
// If the requested service is unknown, the call will fail with status
|
|
||||||
// NOT_FOUND.
|
|
||||||
Check(context.Context, *HealthCheckRequest) (*HealthCheckResponse, error)
|
|
||||||
// Performs a watch for the serving status of the requested service.
|
|
||||||
// The server will immediately send back a message indicating the current
|
|
||||||
// serving status. It will then subsequently send a new message whenever
|
|
||||||
// the service's serving status changes.
|
|
||||||
//
|
|
||||||
// If the requested service is unknown when the call is received, the
|
|
||||||
// server will send a message setting the serving status to
|
|
||||||
// SERVICE_UNKNOWN but will *not* terminate the call. If at some
|
|
||||||
// future point, the serving status of the service becomes known, the
|
|
||||||
// server will send a new message with the service's serving status.
|
|
||||||
//
|
|
||||||
// If the call terminates with status UNIMPLEMENTED, then clients
|
|
||||||
// should assume this method is not supported and should not retry the
|
|
||||||
// call. If the call terminates with any other status (including OK),
|
|
||||||
// clients should retry the call with appropriate exponential backoff.
|
|
||||||
Watch(*HealthCheckRequest, Health_WatchServer) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnimplementedHealthServer can be embedded to have forward compatible implementations.
|
|
||||||
type UnimplementedHealthServer struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*UnimplementedHealthServer) Check(ctx context.Context, req *HealthCheckRequest) (*HealthCheckResponse, error) {
|
|
||||||
return nil, status.Errorf(codes.Unimplemented, "method Check not implemented")
|
|
||||||
}
|
|
||||||
func (*UnimplementedHealthServer) Watch(req *HealthCheckRequest, srv Health_WatchServer) error {
|
|
||||||
return status.Errorf(codes.Unimplemented, "method Watch not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
func RegisterHealthServer(s *grpc.Server, srv HealthServer) {
|
|
||||||
s.RegisterService(&_Health_serviceDesc, srv)
|
|
||||||
}
|
|
||||||
|
|
||||||
func _Health_Check_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
|
||||||
in := new(HealthCheckRequest)
|
|
||||||
if err := dec(in); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
if interceptor == nil {
|
type x struct{}
|
||||||
return srv.(HealthServer).Check(ctx, in)
|
out := protoimpl.TypeBuilder{
|
||||||
}
|
File: protoimpl.DescBuilder{
|
||||||
info := &grpc.UnaryServerInfo{
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
Server: srv,
|
RawDescriptor: file_grpc_health_v1_health_proto_rawDesc,
|
||||||
FullMethod: "/grpc.health.v1.Health/Check",
|
NumEnums: 1,
|
||||||
}
|
NumMessages: 2,
|
||||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
NumExtensions: 0,
|
||||||
return srv.(HealthServer).Check(ctx, req.(*HealthCheckRequest))
|
NumServices: 1,
|
||||||
}
|
|
||||||
return interceptor(ctx, in, info, handler)
|
|
||||||
}
|
|
||||||
|
|
||||||
func _Health_Watch_Handler(srv interface{}, stream grpc.ServerStream) error {
|
|
||||||
m := new(HealthCheckRequest)
|
|
||||||
if err := stream.RecvMsg(m); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return srv.(HealthServer).Watch(m, &healthWatchServer{stream})
|
|
||||||
}
|
|
||||||
|
|
||||||
type Health_WatchServer interface {
|
|
||||||
Send(*HealthCheckResponse) error
|
|
||||||
grpc.ServerStream
|
|
||||||
}
|
|
||||||
|
|
||||||
type healthWatchServer struct {
|
|
||||||
grpc.ServerStream
|
|
||||||
}
|
|
||||||
|
|
||||||
func (x *healthWatchServer) Send(m *HealthCheckResponse) error {
|
|
||||||
return x.ServerStream.SendMsg(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
var _Health_serviceDesc = grpc.ServiceDesc{
|
|
||||||
ServiceName: "grpc.health.v1.Health",
|
|
||||||
HandlerType: (*HealthServer)(nil),
|
|
||||||
Methods: []grpc.MethodDesc{
|
|
||||||
{
|
|
||||||
MethodName: "Check",
|
|
||||||
Handler: _Health_Check_Handler,
|
|
||||||
},
|
},
|
||||||
},
|
GoTypes: file_grpc_health_v1_health_proto_goTypes,
|
||||||
Streams: []grpc.StreamDesc{
|
DependencyIndexes: file_grpc_health_v1_health_proto_depIdxs,
|
||||||
{
|
EnumInfos: file_grpc_health_v1_health_proto_enumTypes,
|
||||||
StreamName: "Watch",
|
MessageInfos: file_grpc_health_v1_health_proto_msgTypes,
|
||||||
Handler: _Health_Watch_Handler,
|
}.Build()
|
||||||
ServerStreams: true,
|
File_grpc_health_v1_health_proto = out.File
|
||||||
},
|
file_grpc_health_v1_health_proto_rawDesc = nil
|
||||||
},
|
file_grpc_health_v1_health_proto_goTypes = nil
|
||||||
Metadata: "grpc/health/v1/health.proto",
|
file_grpc_health_v1_health_proto_depIdxs = nil
|
||||||
}
|
}
|
||||||
|
|
201
vendor/google.golang.org/grpc/health/grpc_health_v1/health_grpc.pb.go
generated
vendored
Normal file
201
vendor/google.golang.org/grpc/health/grpc_health_v1/health_grpc.pb.go
generated
vendored
Normal file
|
@ -0,0 +1,201 @@
|
||||||
|
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// - protoc-gen-go-grpc v1.1.0
|
||||||
|
// - protoc v3.14.0
|
||||||
|
// source: grpc/health/v1/health.proto
|
||||||
|
|
||||||
|
package grpc_health_v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
context "context"
|
||||||
|
grpc "google.golang.org/grpc"
|
||||||
|
codes "google.golang.org/grpc/codes"
|
||||||
|
status "google.golang.org/grpc/status"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This is a compile-time assertion to ensure that this generated file
|
||||||
|
// is compatible with the grpc package it is being compiled against.
|
||||||
|
// Requires gRPC-Go v1.32.0 or later.
|
||||||
|
const _ = grpc.SupportPackageIsVersion7
|
||||||
|
|
||||||
|
// HealthClient is the client API for Health service.
|
||||||
|
//
|
||||||
|
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||||
|
type HealthClient interface {
|
||||||
|
// If the requested service is unknown, the call will fail with status
|
||||||
|
// NOT_FOUND.
|
||||||
|
Check(ctx context.Context, in *HealthCheckRequest, opts ...grpc.CallOption) (*HealthCheckResponse, error)
|
||||||
|
// Performs a watch for the serving status of the requested service.
|
||||||
|
// The server will immediately send back a message indicating the current
|
||||||
|
// serving status. It will then subsequently send a new message whenever
|
||||||
|
// the service's serving status changes.
|
||||||
|
//
|
||||||
|
// If the requested service is unknown when the call is received, the
|
||||||
|
// server will send a message setting the serving status to
|
||||||
|
// SERVICE_UNKNOWN but will *not* terminate the call. If at some
|
||||||
|
// future point, the serving status of the service becomes known, the
|
||||||
|
// server will send a new message with the service's serving status.
|
||||||
|
//
|
||||||
|
// If the call terminates with status UNIMPLEMENTED, then clients
|
||||||
|
// should assume this method is not supported and should not retry the
|
||||||
|
// call. If the call terminates with any other status (including OK),
|
||||||
|
// clients should retry the call with appropriate exponential backoff.
|
||||||
|
Watch(ctx context.Context, in *HealthCheckRequest, opts ...grpc.CallOption) (Health_WatchClient, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type healthClient struct {
|
||||||
|
cc grpc.ClientConnInterface
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHealthClient(cc grpc.ClientConnInterface) HealthClient {
|
||||||
|
return &healthClient{cc}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *healthClient) Check(ctx context.Context, in *HealthCheckRequest, opts ...grpc.CallOption) (*HealthCheckResponse, error) {
|
||||||
|
out := new(HealthCheckResponse)
|
||||||
|
err := c.cc.Invoke(ctx, "/grpc.health.v1.Health/Check", in, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *healthClient) Watch(ctx context.Context, in *HealthCheckRequest, opts ...grpc.CallOption) (Health_WatchClient, error) {
|
||||||
|
stream, err := c.cc.NewStream(ctx, &Health_ServiceDesc.Streams[0], "/grpc.health.v1.Health/Watch", opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
x := &healthWatchClient{stream}
|
||||||
|
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := x.ClientStream.CloseSend(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return x, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Health_WatchClient interface {
|
||||||
|
Recv() (*HealthCheckResponse, error)
|
||||||
|
grpc.ClientStream
|
||||||
|
}
|
||||||
|
|
||||||
|
type healthWatchClient struct {
|
||||||
|
grpc.ClientStream
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *healthWatchClient) Recv() (*HealthCheckResponse, error) {
|
||||||
|
m := new(HealthCheckResponse)
|
||||||
|
if err := x.ClientStream.RecvMsg(m); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HealthServer is the server API for Health service.
|
||||||
|
// All implementations should embed UnimplementedHealthServer
|
||||||
|
// for forward compatibility
|
||||||
|
type HealthServer interface {
|
||||||
|
// If the requested service is unknown, the call will fail with status
|
||||||
|
// NOT_FOUND.
|
||||||
|
Check(context.Context, *HealthCheckRequest) (*HealthCheckResponse, error)
|
||||||
|
// Performs a watch for the serving status of the requested service.
|
||||||
|
// The server will immediately send back a message indicating the current
|
||||||
|
// serving status. It will then subsequently send a new message whenever
|
||||||
|
// the service's serving status changes.
|
||||||
|
//
|
||||||
|
// If the requested service is unknown when the call is received, the
|
||||||
|
// server will send a message setting the serving status to
|
||||||
|
// SERVICE_UNKNOWN but will *not* terminate the call. If at some
|
||||||
|
// future point, the serving status of the service becomes known, the
|
||||||
|
// server will send a new message with the service's serving status.
|
||||||
|
//
|
||||||
|
// If the call terminates with status UNIMPLEMENTED, then clients
|
||||||
|
// should assume this method is not supported and should not retry the
|
||||||
|
// call. If the call terminates with any other status (including OK),
|
||||||
|
// clients should retry the call with appropriate exponential backoff.
|
||||||
|
Watch(*HealthCheckRequest, Health_WatchServer) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnimplementedHealthServer should be embedded to have forward compatible implementations.
|
||||||
|
type UnimplementedHealthServer struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (UnimplementedHealthServer) Check(context.Context, *HealthCheckRequest) (*HealthCheckResponse, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method Check not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedHealthServer) Watch(*HealthCheckRequest, Health_WatchServer) error {
|
||||||
|
return status.Errorf(codes.Unimplemented, "method Watch not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnsafeHealthServer may be embedded to opt out of forward compatibility for this service.
|
||||||
|
// Use of this interface is not recommended, as added methods to HealthServer will
|
||||||
|
// result in compilation errors.
|
||||||
|
type UnsafeHealthServer interface {
|
||||||
|
mustEmbedUnimplementedHealthServer()
|
||||||
|
}
|
||||||
|
|
||||||
|
func RegisterHealthServer(s grpc.ServiceRegistrar, srv HealthServer) {
|
||||||
|
s.RegisterService(&Health_ServiceDesc, srv)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _Health_Check_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(HealthCheckRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(HealthServer).Check(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: "/grpc.health.v1.Health/Check",
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(HealthServer).Check(ctx, req.(*HealthCheckRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _Health_Watch_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||||
|
m := new(HealthCheckRequest)
|
||||||
|
if err := stream.RecvMsg(m); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return srv.(HealthServer).Watch(m, &healthWatchServer{stream})
|
||||||
|
}
|
||||||
|
|
||||||
|
type Health_WatchServer interface {
|
||||||
|
Send(*HealthCheckResponse) error
|
||||||
|
grpc.ServerStream
|
||||||
|
}
|
||||||
|
|
||||||
|
type healthWatchServer struct {
|
||||||
|
grpc.ServerStream
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *healthWatchServer) Send(m *HealthCheckResponse) error {
|
||||||
|
return x.ServerStream.SendMsg(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Health_ServiceDesc is the grpc.ServiceDesc for Health service.
|
||||||
|
// It's only intended for direct use with grpc.RegisterService,
|
||||||
|
// and not to be introspected or modified (even as a copy)
|
||||||
|
var Health_ServiceDesc = grpc.ServiceDesc{
|
||||||
|
ServiceName: "grpc.health.v1.Health",
|
||||||
|
HandlerType: (*HealthServer)(nil),
|
||||||
|
Methods: []grpc.MethodDesc{
|
||||||
|
{
|
||||||
|
MethodName: "Check",
|
||||||
|
Handler: _Health_Check_Handler,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Streams: []grpc.StreamDesc{
|
||||||
|
{
|
||||||
|
StreamName: "Watch",
|
||||||
|
Handler: _Health_Watch_Handler,
|
||||||
|
ServerStreams: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Metadata: "grpc/health/v1/health.proto",
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
* Copyright 2017 gRPC authors.
|
* Copyright 2020 gRPC authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -16,29 +16,8 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// This file contains wrappers for grpclog functions.
|
package health
|
||||||
// The transport package only logs to verbose level 2 by default.
|
|
||||||
|
|
||||||
package transport
|
|
||||||
|
|
||||||
import "google.golang.org/grpc/grpclog"
|
import "google.golang.org/grpc/grpclog"
|
||||||
|
|
||||||
const logLevel = 2
|
var logger = grpclog.Component("health_service")
|
||||||
|
|
||||||
func infof(format string, args ...interface{}) {
|
|
||||||
if grpclog.V(logLevel) {
|
|
||||||
grpclog.Infof(format, args...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func warningf(format string, args ...interface{}) {
|
|
||||||
if grpclog.V(logLevel) {
|
|
||||||
grpclog.Warningf(format, args...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func errorf(format string, args ...interface{}) {
|
|
||||||
if grpclog.V(logLevel) {
|
|
||||||
grpclog.Errorf(format, args...)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -16,8 +16,6 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
//go:generate ./regenerate.sh
|
|
||||||
|
|
||||||
// Package health provides a service that exposes server's health and it must be
|
// Package health provides a service that exposes server's health and it must be
|
||||||
// imported to enable support for client-side health checks.
|
// imported to enable support for client-side health checks.
|
||||||
package health
|
package health
|
||||||
|
@ -27,7 +25,6 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/grpclog"
|
|
||||||
healthgrpc "google.golang.org/grpc/health/grpc_health_v1"
|
healthgrpc "google.golang.org/grpc/health/grpc_health_v1"
|
||||||
healthpb "google.golang.org/grpc/health/grpc_health_v1"
|
healthpb "google.golang.org/grpc/health/grpc_health_v1"
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
|
@ -35,6 +32,7 @@ import (
|
||||||
|
|
||||||
// Server implements `service Health`.
|
// Server implements `service Health`.
|
||||||
type Server struct {
|
type Server struct {
|
||||||
|
healthgrpc.UnimplementedHealthServer
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
// If shutdown is true, it's expected all serving status is NOT_SERVING, and
|
// If shutdown is true, it's expected all serving status is NOT_SERVING, and
|
||||||
// will stay in NOT_SERVING.
|
// will stay in NOT_SERVING.
|
||||||
|
@ -115,7 +113,7 @@ func (s *Server) SetServingStatus(service string, servingStatus healthpb.HealthC
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
defer s.mu.Unlock()
|
defer s.mu.Unlock()
|
||||||
if s.shutdown {
|
if s.shutdown {
|
||||||
grpclog.Infof("health: status changing for %s to %v is ignored because health service is shutdown", service, servingStatus)
|
logger.Infof("health: status changing for %s to %v is ignored because health service is shutdown", service, servingStatus)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,17 +25,41 @@ import (
|
||||||
// UnaryInvoker is called by UnaryClientInterceptor to complete RPCs.
|
// UnaryInvoker is called by UnaryClientInterceptor to complete RPCs.
|
||||||
type UnaryInvoker func(ctx context.Context, method string, req, reply interface{}, cc *ClientConn, opts ...CallOption) error
|
type UnaryInvoker func(ctx context.Context, method string, req, reply interface{}, cc *ClientConn, opts ...CallOption) error
|
||||||
|
|
||||||
// UnaryClientInterceptor intercepts the execution of a unary RPC on the client. invoker is the handler to complete the RPC
|
// UnaryClientInterceptor intercepts the execution of a unary RPC on the client.
|
||||||
// and it is the responsibility of the interceptor to call it.
|
// Unary interceptors can be specified as a DialOption, using
|
||||||
// This is an EXPERIMENTAL API.
|
// WithUnaryInterceptor() or WithChainUnaryInterceptor(), when creating a
|
||||||
|
// ClientConn. When a unary interceptor(s) is set on a ClientConn, gRPC
|
||||||
|
// delegates all unary RPC invocations to the interceptor, and it is the
|
||||||
|
// responsibility of the interceptor to call invoker to complete the processing
|
||||||
|
// of the RPC.
|
||||||
|
//
|
||||||
|
// method is the RPC name. req and reply are the corresponding request and
|
||||||
|
// response messages. cc is the ClientConn on which the RPC was invoked. invoker
|
||||||
|
// is the handler to complete the RPC and it is the responsibility of the
|
||||||
|
// interceptor to call it. opts contain all applicable call options, including
|
||||||
|
// defaults from the ClientConn as well as per-call options.
|
||||||
|
//
|
||||||
|
// The returned error must be compatible with the status package.
|
||||||
type UnaryClientInterceptor func(ctx context.Context, method string, req, reply interface{}, cc *ClientConn, invoker UnaryInvoker, opts ...CallOption) error
|
type UnaryClientInterceptor func(ctx context.Context, method string, req, reply interface{}, cc *ClientConn, invoker UnaryInvoker, opts ...CallOption) error
|
||||||
|
|
||||||
// Streamer is called by StreamClientInterceptor to create a ClientStream.
|
// Streamer is called by StreamClientInterceptor to create a ClientStream.
|
||||||
type Streamer func(ctx context.Context, desc *StreamDesc, cc *ClientConn, method string, opts ...CallOption) (ClientStream, error)
|
type Streamer func(ctx context.Context, desc *StreamDesc, cc *ClientConn, method string, opts ...CallOption) (ClientStream, error)
|
||||||
|
|
||||||
// StreamClientInterceptor intercepts the creation of ClientStream. It may return a custom ClientStream to intercept all I/O
|
// StreamClientInterceptor intercepts the creation of a ClientStream. Stream
|
||||||
// operations. streamer is the handler to create a ClientStream and it is the responsibility of the interceptor to call it.
|
// interceptors can be specified as a DialOption, using WithStreamInterceptor()
|
||||||
// This is an EXPERIMENTAL API.
|
// or WithChainStreamInterceptor(), when creating a ClientConn. When a stream
|
||||||
|
// interceptor(s) is set on the ClientConn, gRPC delegates all stream creations
|
||||||
|
// to the interceptor, and it is the responsibility of the interceptor to call
|
||||||
|
// streamer.
|
||||||
|
//
|
||||||
|
// desc contains a description of the stream. cc is the ClientConn on which the
|
||||||
|
// RPC was invoked. streamer is the handler to create a ClientStream and it is
|
||||||
|
// the responsibility of the interceptor to call it. opts contain all applicable
|
||||||
|
// call options, including defaults from the ClientConn as well as per-call
|
||||||
|
// options.
|
||||||
|
//
|
||||||
|
// StreamClientInterceptor may return a custom ClientStream to intercept all I/O
|
||||||
|
// operations. The returned error must be compatible with the status package.
|
||||||
type StreamClientInterceptor func(ctx context.Context, desc *StreamDesc, cc *ClientConn, method string, streamer Streamer, opts ...CallOption) (ClientStream, error)
|
type StreamClientInterceptor func(ctx context.Context, desc *StreamDesc, cc *ClientConn, method string, streamer Streamer, opts ...CallOption) (ClientStream, error)
|
||||||
|
|
||||||
// UnaryServerInfo consists of various information about a unary RPC on
|
// UnaryServerInfo consists of various information about a unary RPC on
|
||||||
|
|
|
@ -25,6 +25,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"google.golang.org/grpc/grpclog"
|
"google.golang.org/grpc/grpclog"
|
||||||
|
"google.golang.org/grpc/internal/grpcutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Logger is the global binary logger. It can be used to get binary logger for
|
// Logger is the global binary logger. It can be used to get binary logger for
|
||||||
|
@ -39,6 +40,8 @@ type Logger interface {
|
||||||
// It is used to get a methodLogger for each individual method.
|
// It is used to get a methodLogger for each individual method.
|
||||||
var binLogger Logger
|
var binLogger Logger
|
||||||
|
|
||||||
|
var grpclogLogger = grpclog.Component("binarylog")
|
||||||
|
|
||||||
// SetLogger sets the binarg logger.
|
// SetLogger sets the binarg logger.
|
||||||
//
|
//
|
||||||
// Only call this at init time.
|
// Only call this at init time.
|
||||||
|
@ -146,9 +149,9 @@ func (l *logger) setBlacklist(method string) error {
|
||||||
// Each methodLogger returned by this method is a new instance. This is to
|
// Each methodLogger returned by this method is a new instance. This is to
|
||||||
// generate sequence id within the call.
|
// generate sequence id within the call.
|
||||||
func (l *logger) getMethodLogger(methodName string) *MethodLogger {
|
func (l *logger) getMethodLogger(methodName string) *MethodLogger {
|
||||||
s, m, err := parseMethodName(methodName)
|
s, m, err := grpcutil.ParseMethod(methodName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
grpclog.Infof("binarylogging: failed to parse %q: %v", methodName, err)
|
grpclogLogger.Infof("binarylogging: failed to parse %q: %v", methodName, err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if ml, ok := l.methods[s+"/"+m]; ok {
|
if ml, ok := l.methods[s+"/"+m]; ok {
|
||||||
|
|
|
@ -24,8 +24,6 @@ import (
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"google.golang.org/grpc/grpclog"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewLoggerFromConfigString reads the string and build a logger. It can be used
|
// NewLoggerFromConfigString reads the string and build a logger. It can be used
|
||||||
|
@ -52,7 +50,7 @@ func NewLoggerFromConfigString(s string) Logger {
|
||||||
methods := strings.Split(s, ",")
|
methods := strings.Split(s, ",")
|
||||||
for _, method := range methods {
|
for _, method := range methods {
|
||||||
if err := l.fillMethodLoggerWithConfigString(method); err != nil {
|
if err := l.fillMethodLoggerWithConfigString(method); err != nil {
|
||||||
grpclog.Warningf("failed to parse binary log config: %v", err)
|
grpclogLogger.Warningf("failed to parse binary log config: %v", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,6 @@ import (
|
||||||
"github.com/golang/protobuf/proto"
|
"github.com/golang/protobuf/proto"
|
||||||
"github.com/golang/protobuf/ptypes"
|
"github.com/golang/protobuf/ptypes"
|
||||||
pb "google.golang.org/grpc/binarylog/grpc_binarylog_v1"
|
pb "google.golang.org/grpc/binarylog/grpc_binarylog_v1"
|
||||||
"google.golang.org/grpc/grpclog"
|
|
||||||
"google.golang.org/grpc/metadata"
|
"google.golang.org/grpc/metadata"
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
)
|
)
|
||||||
|
@ -66,7 +65,7 @@ func newMethodLogger(h, m uint64) *MethodLogger {
|
||||||
callID: idGen.next(),
|
callID: idGen.next(),
|
||||||
idWithinCallGen: &callIDGenerator{},
|
idWithinCallGen: &callIDGenerator{},
|
||||||
|
|
||||||
sink: defaultSink, // TODO(blog): make it plugable.
|
sink: DefaultSink, // TODO(blog): make it plugable.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -219,12 +218,12 @@ func (c *ClientMessage) toProto() *pb.GrpcLogEntry {
|
||||||
if m, ok := c.Message.(proto.Message); ok {
|
if m, ok := c.Message.(proto.Message); ok {
|
||||||
data, err = proto.Marshal(m)
|
data, err = proto.Marshal(m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
grpclog.Infof("binarylogging: failed to marshal proto message: %v", err)
|
grpclogLogger.Infof("binarylogging: failed to marshal proto message: %v", err)
|
||||||
}
|
}
|
||||||
} else if b, ok := c.Message.([]byte); ok {
|
} else if b, ok := c.Message.([]byte); ok {
|
||||||
data = b
|
data = b
|
||||||
} else {
|
} else {
|
||||||
grpclog.Infof("binarylogging: message to log is neither proto.message nor []byte")
|
grpclogLogger.Infof("binarylogging: message to log is neither proto.message nor []byte")
|
||||||
}
|
}
|
||||||
ret := &pb.GrpcLogEntry{
|
ret := &pb.GrpcLogEntry{
|
||||||
Type: pb.GrpcLogEntry_EVENT_TYPE_CLIENT_MESSAGE,
|
Type: pb.GrpcLogEntry_EVENT_TYPE_CLIENT_MESSAGE,
|
||||||
|
@ -259,12 +258,12 @@ func (c *ServerMessage) toProto() *pb.GrpcLogEntry {
|
||||||
if m, ok := c.Message.(proto.Message); ok {
|
if m, ok := c.Message.(proto.Message); ok {
|
||||||
data, err = proto.Marshal(m)
|
data, err = proto.Marshal(m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
grpclog.Infof("binarylogging: failed to marshal proto message: %v", err)
|
grpclogLogger.Infof("binarylogging: failed to marshal proto message: %v", err)
|
||||||
}
|
}
|
||||||
} else if b, ok := c.Message.([]byte); ok {
|
} else if b, ok := c.Message.([]byte); ok {
|
||||||
data = b
|
data = b
|
||||||
} else {
|
} else {
|
||||||
grpclog.Infof("binarylogging: message to log is neither proto.message nor []byte")
|
grpclogLogger.Infof("binarylogging: message to log is neither proto.message nor []byte")
|
||||||
}
|
}
|
||||||
ret := &pb.GrpcLogEntry{
|
ret := &pb.GrpcLogEntry{
|
||||||
Type: pb.GrpcLogEntry_EVENT_TYPE_SERVER_MESSAGE,
|
Type: pb.GrpcLogEntry_EVENT_TYPE_SERVER_MESSAGE,
|
||||||
|
@ -315,7 +314,7 @@ type ServerTrailer struct {
|
||||||
func (c *ServerTrailer) toProto() *pb.GrpcLogEntry {
|
func (c *ServerTrailer) toProto() *pb.GrpcLogEntry {
|
||||||
st, ok := status.FromError(c.Err)
|
st, ok := status.FromError(c.Err)
|
||||||
if !ok {
|
if !ok {
|
||||||
grpclog.Info("binarylogging: error in trailer is not a status error")
|
grpclogLogger.Info("binarylogging: error in trailer is not a status error")
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
detailsBytes []byte
|
detailsBytes []byte
|
||||||
|
@ -325,7 +324,7 @@ func (c *ServerTrailer) toProto() *pb.GrpcLogEntry {
|
||||||
if stProto != nil && len(stProto.Details) != 0 {
|
if stProto != nil && len(stProto.Details) != 0 {
|
||||||
detailsBytes, err = proto.Marshal(stProto)
|
detailsBytes, err = proto.Marshal(stProto)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
grpclog.Infof("binarylogging: failed to marshal status proto: %v", err)
|
grpclogLogger.Infof("binarylogging: failed to marshal status proto: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ret := &pb.GrpcLogEntry{
|
ret := &pb.GrpcLogEntry{
|
||||||
|
|
|
@ -21,32 +21,23 @@ package binarylog
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/golang/protobuf/proto"
|
"github.com/golang/protobuf/proto"
|
||||||
pb "google.golang.org/grpc/binarylog/grpc_binarylog_v1"
|
pb "google.golang.org/grpc/binarylog/grpc_binarylog_v1"
|
||||||
"google.golang.org/grpc/grpclog"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
defaultSink Sink = &noopSink{} // TODO(blog): change this default (file in /tmp).
|
// DefaultSink is the sink where the logs will be written to. It's exported
|
||||||
|
// for the binarylog package to update.
|
||||||
|
DefaultSink Sink = &noopSink{} // TODO(blog): change this default (file in /tmp).
|
||||||
)
|
)
|
||||||
|
|
||||||
// SetDefaultSink sets the sink where binary logs will be written to.
|
|
||||||
//
|
|
||||||
// Not thread safe. Only set during initialization.
|
|
||||||
func SetDefaultSink(s Sink) {
|
|
||||||
if defaultSink != nil {
|
|
||||||
defaultSink.Close()
|
|
||||||
}
|
|
||||||
defaultSink = s
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sink writes log entry into the binary log sink.
|
// Sink writes log entry into the binary log sink.
|
||||||
|
//
|
||||||
|
// sink is a copy of the exported binarylog.Sink, to avoid circular dependency.
|
||||||
type Sink interface {
|
type Sink interface {
|
||||||
// Write will be called to write the log entry into the sink.
|
// Write will be called to write the log entry into the sink.
|
||||||
//
|
//
|
||||||
|
@ -67,7 +58,7 @@ func (ns *noopSink) Close() error { return nil }
|
||||||
// message is prefixed with a 4 byte big endian unsigned integer as the length.
|
// message is prefixed with a 4 byte big endian unsigned integer as the length.
|
||||||
//
|
//
|
||||||
// No buffer is done, Close() doesn't try to close the writer.
|
// No buffer is done, Close() doesn't try to close the writer.
|
||||||
func newWriterSink(w io.Writer) *writerSink {
|
func newWriterSink(w io.Writer) Sink {
|
||||||
return &writerSink{out: w}
|
return &writerSink{out: w}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,7 +69,7 @@ type writerSink struct {
|
||||||
func (ws *writerSink) Write(e *pb.GrpcLogEntry) error {
|
func (ws *writerSink) Write(e *pb.GrpcLogEntry) error {
|
||||||
b, err := proto.Marshal(e)
|
b, err := proto.Marshal(e)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
grpclog.Infof("binary logging: failed to marshal proto message: %v", err)
|
grpclogLogger.Infof("binary logging: failed to marshal proto message: %v", err)
|
||||||
}
|
}
|
||||||
hdr := make([]byte, 4)
|
hdr := make([]byte, 4)
|
||||||
binary.BigEndian.PutUint32(hdr, uint32(len(b)))
|
binary.BigEndian.PutUint32(hdr, uint32(len(b)))
|
||||||
|
@ -93,17 +84,17 @@ func (ws *writerSink) Write(e *pb.GrpcLogEntry) error {
|
||||||
|
|
||||||
func (ws *writerSink) Close() error { return nil }
|
func (ws *writerSink) Close() error { return nil }
|
||||||
|
|
||||||
type bufWriteCloserSink struct {
|
type bufferedSink struct {
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
closer io.Closer
|
closer io.Closer
|
||||||
out *writerSink // out is built on buf.
|
out Sink // out is built on buf.
|
||||||
buf *bufio.Writer // buf is kept for flush.
|
buf *bufio.Writer // buf is kept for flush.
|
||||||
|
|
||||||
writeStartOnce sync.Once
|
writeStartOnce sync.Once
|
||||||
writeTicker *time.Ticker
|
writeTicker *time.Ticker
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fs *bufWriteCloserSink) Write(e *pb.GrpcLogEntry) error {
|
func (fs *bufferedSink) Write(e *pb.GrpcLogEntry) error {
|
||||||
// Start the write loop when Write is called.
|
// Start the write loop when Write is called.
|
||||||
fs.writeStartOnce.Do(fs.startFlushGoroutine)
|
fs.writeStartOnce.Do(fs.startFlushGoroutine)
|
||||||
fs.mu.Lock()
|
fs.mu.Lock()
|
||||||
|
@ -119,44 +110,50 @@ const (
|
||||||
bufFlushDuration = 60 * time.Second
|
bufFlushDuration = 60 * time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
func (fs *bufWriteCloserSink) startFlushGoroutine() {
|
func (fs *bufferedSink) startFlushGoroutine() {
|
||||||
fs.writeTicker = time.NewTicker(bufFlushDuration)
|
fs.writeTicker = time.NewTicker(bufFlushDuration)
|
||||||
go func() {
|
go func() {
|
||||||
for range fs.writeTicker.C {
|
for range fs.writeTicker.C {
|
||||||
fs.mu.Lock()
|
fs.mu.Lock()
|
||||||
fs.buf.Flush()
|
if err := fs.buf.Flush(); err != nil {
|
||||||
|
grpclogLogger.Warningf("failed to flush to Sink: %v", err)
|
||||||
|
}
|
||||||
fs.mu.Unlock()
|
fs.mu.Unlock()
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fs *bufWriteCloserSink) Close() error {
|
func (fs *bufferedSink) Close() error {
|
||||||
if fs.writeTicker != nil {
|
if fs.writeTicker != nil {
|
||||||
fs.writeTicker.Stop()
|
fs.writeTicker.Stop()
|
||||||
}
|
}
|
||||||
fs.mu.Lock()
|
fs.mu.Lock()
|
||||||
fs.buf.Flush()
|
if err := fs.buf.Flush(); err != nil {
|
||||||
fs.closer.Close()
|
grpclogLogger.Warningf("failed to flush to Sink: %v", err)
|
||||||
fs.out.Close()
|
}
|
||||||
|
if err := fs.closer.Close(); err != nil {
|
||||||
|
grpclogLogger.Warningf("failed to close the underlying WriterCloser: %v", err)
|
||||||
|
}
|
||||||
|
if err := fs.out.Close(); err != nil {
|
||||||
|
grpclogLogger.Warningf("failed to close the Sink: %v", err)
|
||||||
|
}
|
||||||
fs.mu.Unlock()
|
fs.mu.Unlock()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func newBufWriteCloserSink(o io.WriteCloser) Sink {
|
// NewBufferedSink creates a binary log sink with the given WriteCloser.
|
||||||
|
//
|
||||||
|
// Write() marshals the proto message and writes it to the given writer. Each
|
||||||
|
// message is prefixed with a 4 byte big endian unsigned integer as the length.
|
||||||
|
//
|
||||||
|
// Content is kept in a buffer, and is flushed every 60 seconds.
|
||||||
|
//
|
||||||
|
// Close closes the WriteCloser.
|
||||||
|
func NewBufferedSink(o io.WriteCloser) Sink {
|
||||||
bufW := bufio.NewWriter(o)
|
bufW := bufio.NewWriter(o)
|
||||||
return &bufWriteCloserSink{
|
return &bufferedSink{
|
||||||
closer: o,
|
closer: o,
|
||||||
out: newWriterSink(bufW),
|
out: newWriterSink(bufW),
|
||||||
buf: bufW,
|
buf: bufW,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTempFileSink creates a temp file and returns a Sink that writes to this
|
|
||||||
// file.
|
|
||||||
func NewTempFileSink() (Sink, error) {
|
|
||||||
tempFile, err := ioutil.TempFile("/tmp", "grpcgo_binarylog_*.txt")
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to create temp file: %v", err)
|
|
||||||
}
|
|
||||||
return newBufWriteCloserSink(tempFile), nil
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,41 +0,0 @@
|
||||||
/*
|
|
||||||
*
|
|
||||||
* Copyright 2018 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 binarylog
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// parseMethodName splits service and method from the input. It expects format
|
|
||||||
// "/service/method".
|
|
||||||
//
|
|
||||||
// TODO: move to internal/grpcutil.
|
|
||||||
func parseMethodName(methodName string) (service, method string, _ error) {
|
|
||||||
if !strings.HasPrefix(methodName, "/") {
|
|
||||||
return "", "", errors.New("invalid method name: should start with /")
|
|
||||||
}
|
|
||||||
methodName = methodName[1:]
|
|
||||||
|
|
||||||
pos := strings.LastIndex(methodName, "/")
|
|
||||||
if pos < 0 {
|
|
||||||
return "", "", errors.New("invalid method name: suffix /method is missing")
|
|
||||||
}
|
|
||||||
return methodName[:pos], methodName[pos+1:], nil
|
|
||||||
}
|
|
|
@ -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")
|
logger.Error("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")
|
logger.Error("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")
|
logger.Error("a NormalSocket's parent id cannot be 0")
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
id := idGen.genID()
|
id := idGen.genID()
|
||||||
|
@ -294,7 +294,17 @@ 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(l grpclog.DepthLoggerV2, id int64, depth int, desc *TraceEventDesc) {
|
||||||
|
for d := desc; d != nil; d = d.Parent {
|
||||||
|
switch d.Severity {
|
||||||
|
case CtUnknown, CtInfo:
|
||||||
|
l.InfoDepth(depth+1, d.Desc)
|
||||||
|
case CtWarning:
|
||||||
|
l.WarningDepth(depth+1, d.Desc)
|
||||||
|
case CtError:
|
||||||
|
l.ErrorDepth(depth+1, d.Desc)
|
||||||
|
}
|
||||||
|
}
|
||||||
if getMaxTraceEntry() == 0 {
|
if getMaxTraceEntry() == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,102 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* 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/grpclog"
|
||||||
|
)
|
||||||
|
|
||||||
|
var logger = grpclog.Component("channelz")
|
||||||
|
|
||||||
|
// Info logs and adds a trace event if channelz is on.
|
||||||
|
func Info(l grpclog.DepthLoggerV2, id int64, args ...interface{}) {
|
||||||
|
if IsOn() {
|
||||||
|
AddTraceEvent(l, id, 1, &TraceEventDesc{
|
||||||
|
Desc: fmt.Sprint(args...),
|
||||||
|
Severity: CtInfo,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
l.InfoDepth(1, args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Infof logs and adds a trace event if channelz is on.
|
||||||
|
func Infof(l grpclog.DepthLoggerV2, id int64, format string, args ...interface{}) {
|
||||||
|
msg := fmt.Sprintf(format, args...)
|
||||||
|
if IsOn() {
|
||||||
|
AddTraceEvent(l, id, 1, &TraceEventDesc{
|
||||||
|
Desc: msg,
|
||||||
|
Severity: CtInfo,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
l.InfoDepth(1, msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warning logs and adds a trace event if channelz is on.
|
||||||
|
func Warning(l grpclog.DepthLoggerV2, id int64, args ...interface{}) {
|
||||||
|
if IsOn() {
|
||||||
|
AddTraceEvent(l, id, 1, &TraceEventDesc{
|
||||||
|
Desc: fmt.Sprint(args...),
|
||||||
|
Severity: CtWarning,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
l.WarningDepth(1, args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warningf logs and adds a trace event if channelz is on.
|
||||||
|
func Warningf(l grpclog.DepthLoggerV2, id int64, format string, args ...interface{}) {
|
||||||
|
msg := fmt.Sprintf(format, args...)
|
||||||
|
if IsOn() {
|
||||||
|
AddTraceEvent(l, id, 1, &TraceEventDesc{
|
||||||
|
Desc: msg,
|
||||||
|
Severity: CtWarning,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
l.WarningDepth(1, msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error logs and adds a trace event if channelz is on.
|
||||||
|
func Error(l grpclog.DepthLoggerV2, id int64, args ...interface{}) {
|
||||||
|
if IsOn() {
|
||||||
|
AddTraceEvent(l, id, 1, &TraceEventDesc{
|
||||||
|
Desc: fmt.Sprint(args...),
|
||||||
|
Severity: CtError,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
l.ErrorDepth(1, args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Errorf logs and adds a trace event if channelz is on.
|
||||||
|
func Errorf(l grpclog.DepthLoggerV2, id int64, format string, args ...interface{}) {
|
||||||
|
msg := fmt.Sprintf(format, args...)
|
||||||
|
if IsOn() {
|
||||||
|
AddTraceEvent(l, id, 1, &TraceEventDesc{
|
||||||
|
Desc: msg,
|
||||||
|
Severity: CtError,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
l.ErrorDepth(1, msg)
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,7 +26,6 @@ import (
|
||||||
|
|
||||||
"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"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// entry represents a node in the channelz database.
|
// entry represents a node in the channelz database.
|
||||||
|
@ -60,17 +59,17 @@ func (d *dummyEntry) addChild(id int64, e entry) {
|
||||||
// the addrConn will create a new transport. And when registering the new transport in
|
// the addrConn will create a new transport. And when registering the new transport in
|
||||||
// channelz, its parent addrConn could have already been torn down and deleted
|
// channelz, its parent addrConn could have already been torn down and deleted
|
||||||
// from channelz tracking, and thus reach the code here.
|
// from channelz tracking, and thus reach the code here.
|
||||||
grpclog.Infof("attempt to add child of type %T with id %d to a parent (id=%d) that doesn't currently exist", e, id, d.idNotFound)
|
logger.Infof("attempt to add child of type %T with id %d to a parent (id=%d) that doesn't currently exist", e, id, d.idNotFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *dummyEntry) deleteChild(id int64) {
|
func (d *dummyEntry) deleteChild(id int64) {
|
||||||
// It is possible for a normal program to reach here under race condition.
|
// It is possible for a normal program to reach here under race condition.
|
||||||
// Refer to the example described in addChild().
|
// Refer to the example described in addChild().
|
||||||
grpclog.Infof("attempt to delete child with id %d from a parent (id=%d) that doesn't currently exist", id, d.idNotFound)
|
logger.Infof("attempt to delete child with id %d from a parent (id=%d) that doesn't currently exist", id, d.idNotFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *dummyEntry) triggerDelete() {
|
func (d *dummyEntry) triggerDelete() {
|
||||||
grpclog.Warningf("attempt to delete an entry (id=%d) that doesn't currently exist", d.idNotFound)
|
logger.Warningf("attempt to delete an entry (id=%d) that doesn't currently exist", d.idNotFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*dummyEntry) deleteSelfIfReady() {
|
func (*dummyEntry) deleteSelfIfReady() {
|
||||||
|
@ -215,7 +214,7 @@ func (c *channel) addChild(id int64, e entry) {
|
||||||
case *channel:
|
case *channel:
|
||||||
c.nestedChans[id] = v.refName
|
c.nestedChans[id] = v.refName
|
||||||
default:
|
default:
|
||||||
grpclog.Errorf("cannot add a child (id = %d) of type %T to a channel", id, e)
|
logger.Errorf("cannot add a child (id = %d) of type %T to a channel", id, e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -326,7 +325,7 @@ func (sc *subChannel) addChild(id int64, e entry) {
|
||||||
if v, ok := e.(*normalSocket); ok {
|
if v, ok := e.(*normalSocket); ok {
|
||||||
sc.sockets[id] = v.refName
|
sc.sockets[id] = v.refName
|
||||||
} else {
|
} else {
|
||||||
grpclog.Errorf("cannot add a child (id = %d) of type %T to a subChannel", id, e)
|
logger.Errorf("cannot add a child (id = %d) of type %T to a subChannel", id, e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -493,11 +492,11 @@ type listenSocket struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ls *listenSocket) addChild(id int64, e entry) {
|
func (ls *listenSocket) addChild(id int64, e entry) {
|
||||||
grpclog.Errorf("cannot add a child (id = %d) of type %T to a listen socket", id, e)
|
logger.Errorf("cannot add a child (id = %d) of type %T to a listen socket", id, e)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ls *listenSocket) deleteChild(id int64) {
|
func (ls *listenSocket) deleteChild(id int64) {
|
||||||
grpclog.Errorf("cannot delete a child (id = %d) from a listen socket", id)
|
logger.Errorf("cannot delete a child (id = %d) from a listen socket", id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ls *listenSocket) triggerDelete() {
|
func (ls *listenSocket) triggerDelete() {
|
||||||
|
@ -506,7 +505,7 @@ func (ls *listenSocket) triggerDelete() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ls *listenSocket) deleteSelfIfReady() {
|
func (ls *listenSocket) deleteSelfIfReady() {
|
||||||
grpclog.Errorf("cannot call deleteSelfIfReady on a listen socket")
|
logger.Errorf("cannot call deleteSelfIfReady on a listen socket")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ls *listenSocket) getParentID() int64 {
|
func (ls *listenSocket) getParentID() int64 {
|
||||||
|
@ -522,11 +521,11 @@ type normalSocket struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ns *normalSocket) addChild(id int64, e entry) {
|
func (ns *normalSocket) addChild(id int64, e entry) {
|
||||||
grpclog.Errorf("cannot add a child (id = %d) of type %T to a normal socket", id, e)
|
logger.Errorf("cannot add a child (id = %d) of type %T to a normal socket", id, e)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ns *normalSocket) deleteChild(id int64) {
|
func (ns *normalSocket) deleteChild(id int64) {
|
||||||
grpclog.Errorf("cannot delete a child (id = %d) from a normal socket", id)
|
logger.Errorf("cannot delete a child (id = %d) from a normal socket", id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ns *normalSocket) triggerDelete() {
|
func (ns *normalSocket) triggerDelete() {
|
||||||
|
@ -535,7 +534,7 @@ func (ns *normalSocket) triggerDelete() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ns *normalSocket) deleteSelfIfReady() {
|
func (ns *normalSocket) deleteSelfIfReady() {
|
||||||
grpclog.Errorf("cannot call deleteSelfIfReady on a normal socket")
|
logger.Errorf("cannot call deleteSelfIfReady on a normal socket")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ns *normalSocket) getParentID() int64 {
|
func (ns *normalSocket) getParentID() int64 {
|
||||||
|
@ -594,7 +593,7 @@ func (s *server) addChild(id int64, e entry) {
|
||||||
case *listenSocket:
|
case *listenSocket:
|
||||||
s.listenSockets[id] = v.refName
|
s.listenSockets[id] = v.refName
|
||||||
default:
|
default:
|
||||||
grpclog.Errorf("cannot add a child (id = %d) of type %T to a server", id, e)
|
logger.Errorf("cannot add a child (id = %d) of type %T to a server", id, e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -673,10 +672,10 @@ func (c *channelTrace) clear() {
|
||||||
type Severity int
|
type Severity int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// CtUNKNOWN indicates unknown severity of a trace event.
|
// CtUnknown indicates unknown severity of a trace event.
|
||||||
CtUNKNOWN Severity = iota
|
CtUnknown Severity = iota
|
||||||
// CtINFO indicates info level severity of a trace event.
|
// CtInfo indicates info level severity of a trace event.
|
||||||
CtINFO
|
CtInfo
|
||||||
// CtWarning indicates warning level severity of a trace event.
|
// CtWarning indicates warning level severity of a trace event.
|
||||||
CtWarning
|
CtWarning
|
||||||
// CtError indicates error level severity of a trace event.
|
// CtError indicates error level severity of a trace event.
|
||||||
|
|
|
@ -22,8 +22,6 @@ package channelz
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"google.golang.org/grpc/grpclog"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var once sync.Once
|
var once sync.Once
|
||||||
|
@ -39,6 +37,6 @@ type SocketOptionData struct {
|
||||||
// Windows OS doesn't support Socket Option
|
// Windows OS doesn't support Socket Option
|
||||||
func (s *SocketOptionData) Getsockopt(fd uintptr) {
|
func (s *SocketOptionData) Getsockopt(fd uintptr) {
|
||||||
once.Do(func() {
|
once.Do(func() {
|
||||||
grpclog.Warningln("Channelz: socket options are not supported on non-linux os and appengine.")
|
logger.Warning("Channelz: socket options are not supported on non-linux os and appengine.")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021 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"
|
||||||
|
)
|
||||||
|
|
||||||
|
// requestInfoKey is a struct to be used as the key to store RequestInfo in a
|
||||||
|
// context.
|
||||||
|
type requestInfoKey struct{}
|
||||||
|
|
||||||
|
// NewRequestInfoContext creates a context with ri.
|
||||||
|
func NewRequestInfoContext(ctx context.Context, ri interface{}) context.Context {
|
||||||
|
return context.WithValue(ctx, requestInfoKey{}, ri)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RequestInfoFromContext extracts the RequestInfo from ctx.
|
||||||
|
func RequestInfoFromContext(ctx context.Context) interface{} {
|
||||||
|
return ctx.Value(requestInfoKey{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// clientHandshakeInfoKey is a struct used as the key to store
|
||||||
|
// ClientHandshakeInfo in a context.
|
||||||
|
type clientHandshakeInfoKey struct{}
|
||||||
|
|
||||||
|
// ClientHandshakeInfoFromContext extracts the ClientHandshakeInfo from ctx.
|
||||||
|
func ClientHandshakeInfoFromContext(ctx context.Context) interface{} {
|
||||||
|
return ctx.Value(clientHandshakeInfoKey{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClientHandshakeInfoContext creates a context with chi.
|
||||||
|
func NewClientHandshakeInfoContext(ctx context.Context, chi interface{}) context.Context {
|
||||||
|
return context.WithValue(ctx, clientHandshakeInfoKey{}, chi)
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
// +build !appengine
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* 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 credentials defines APIs for parsing SPIFFE ID.
|
||||||
|
//
|
||||||
|
// All APIs in this package are experimental.
|
||||||
|
package credentials
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"google.golang.org/grpc/grpclog"
|
||||||
|
)
|
||||||
|
|
||||||
|
var logger = grpclog.Component("credentials")
|
||||||
|
|
||||||
|
// SPIFFEIDFromState parses the SPIFFE ID from State. If the SPIFFE ID format
|
||||||
|
// is invalid, return nil with warning.
|
||||||
|
func SPIFFEIDFromState(state tls.ConnectionState) *url.URL {
|
||||||
|
if len(state.PeerCertificates) == 0 || len(state.PeerCertificates[0].URIs) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return SPIFFEIDFromCert(state.PeerCertificates[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
// SPIFFEIDFromCert parses the SPIFFE ID from x509.Certificate. If the SPIFFE
|
||||||
|
// ID format is invalid, return nil with warning.
|
||||||
|
func SPIFFEIDFromCert(cert *x509.Certificate) *url.URL {
|
||||||
|
if cert == nil || cert.URIs == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var spiffeID *url.URL
|
||||||
|
for _, uri := range cert.URIs {
|
||||||
|
if uri == nil || uri.Scheme != "spiffe" || uri.Opaque != "" || (uri.User != nil && uri.User.Username() != "") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// From this point, we assume the uri is intended for a SPIFFE ID.
|
||||||
|
if len(uri.String()) > 2048 {
|
||||||
|
logger.Warning("invalid SPIFFE ID: total ID length larger than 2048 bytes")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if len(uri.Host) == 0 || len(uri.Path) == 0 {
|
||||||
|
logger.Warning("invalid SPIFFE ID: domain or workload ID is empty")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if len(uri.Host) > 255 {
|
||||||
|
logger.Warning("invalid SPIFFE ID: domain length larger than 255 characters")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// A valid SPIFFE certificate can only have exactly one URI SAN field.
|
||||||
|
if len(cert.URIs) > 1 {
|
||||||
|
logger.Warning("invalid SPIFFE ID: multiple URI SANs")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
spiffeID = uri
|
||||||
|
}
|
||||||
|
return spiffeID
|
||||||
|
}
|
31
vendor/google.golang.org/grpc/internal/credentials/spiffe_appengine.go
generated
vendored
Normal file
31
vendor/google.golang.org/grpc/internal/credentials/spiffe_appengine.go
generated
vendored
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
// +build appengine
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* 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 credentials
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"net/url"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SPIFFEIDFromState is a no-op for appengine builds.
|
||||||
|
func SPIFFEIDFromState(state tls.ConnectionState) *url.URL {
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -18,8 +18,7 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Package internal contains credentials-internal code.
|
package credentials
|
||||||
package internal
|
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
|
@ -18,7 +18,7 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package internal
|
package credentials
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* 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 credentials
|
||||||
|
|
||||||
|
import "crypto/tls"
|
||||||
|
|
||||||
|
const alpnProtoStrH2 = "h2"
|
||||||
|
|
||||||
|
// AppendH2ToNextProtos appends h2 to next protos.
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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()
|
||||||
|
}
|
|
@ -34,5 +34,5 @@ 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 is set if TXT errors should be ignored ("GRPC_GO_IGNORE_TXT_ERRORS" is not "false").
|
||||||
TXTErrIgnore = !strings.EqualFold(os.Getenv(retryStr), "false")
|
TXTErrIgnore = !strings.EqualFold(os.Getenv(txtErrIgnoreStr), "false")
|
||||||
)
|
)
|
||||||
|
|
|
@ -0,0 +1,126 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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.Infoln(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.Warningln(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.Errorln(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.Fatalln(args...)
|
||||||
|
}
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
//
|
||||||
|
// Experimental
|
||||||
|
//
|
||||||
|
// Notice: This type is EXPERIMENTAL and may be changed or removed in a
|
||||||
|
// later release.
|
||||||
|
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,81 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PrefixLogger does logging with a prefix.
|
||||||
|
//
|
||||||
|
// Logging method on a nil logs without any prefix.
|
||||||
|
type PrefixLogger struct {
|
||||||
|
logger DepthLoggerV2
|
||||||
|
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
|
||||||
|
pl.logger.InfoDepth(1, fmt.Sprintf(format, args...))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
InfoDepth(1, fmt.Sprintf(format, args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warningf does warning logging.
|
||||||
|
func (pl *PrefixLogger) Warningf(format string, args ...interface{}) {
|
||||||
|
if pl != nil {
|
||||||
|
format = pl.prefix + format
|
||||||
|
pl.logger.WarningDepth(1, fmt.Sprintf(format, args...))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
WarningDepth(1, fmt.Sprintf(format, args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Errorf does error logging.
|
||||||
|
func (pl *PrefixLogger) Errorf(format string, args ...interface{}) {
|
||||||
|
if pl != nil {
|
||||||
|
format = pl.prefix + format
|
||||||
|
pl.logger.ErrorDepth(1, fmt.Sprintf(format, args...))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ErrorDepth(1, fmt.Sprintf(format, args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debugf does info logging at verbose level 2.
|
||||||
|
func (pl *PrefixLogger) Debugf(format string, args ...interface{}) {
|
||||||
|
if !Logger.V(2) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if pl != nil {
|
||||||
|
// Handle nil, so the tests can pass in a nil logger.
|
||||||
|
format = pl.prefix + format
|
||||||
|
pl.logger.InfoDepth(1, fmt.Sprintf(format, args...))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
InfoDepth(1, fmt.Sprintf(format, args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPrefixLogger creates a prefix logger with the given prefix.
|
||||||
|
func NewPrefixLogger(logger DepthLoggerV2, prefix string) *PrefixLogger {
|
||||||
|
return &PrefixLogger{logger: logger, prefix: prefix}
|
||||||
|
}
|
63
vendor/google.golang.org/grpc/internal/grpcutil/encode_duration.go
generated
vendored
Normal file
63
vendor/google.golang.org/grpc/internal/grpcutil/encode_duration.go
generated
vendored
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2020 gRPC authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package grpcutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const maxTimeoutValue int64 = 100000000 - 1
|
||||||
|
|
||||||
|
// div does integer division and round-up the result. Note that this is
|
||||||
|
// equivalent to (d+r-1)/r but has less chance to overflow.
|
||||||
|
func div(d, r time.Duration) int64 {
|
||||||
|
if d%r > 0 {
|
||||||
|
return int64(d/r + 1)
|
||||||
|
}
|
||||||
|
return int64(d / r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeDuration encodes the duration to the format grpc-timeout header
|
||||||
|
// accepts.
|
||||||
|
//
|
||||||
|
// https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#requests
|
||||||
|
func EncodeDuration(t time.Duration) string {
|
||||||
|
// TODO: This is simplistic and not bandwidth efficient. Improve it.
|
||||||
|
if t <= 0 {
|
||||||
|
return "0n"
|
||||||
|
}
|
||||||
|
if d := div(t, time.Nanosecond); d <= maxTimeoutValue {
|
||||||
|
return strconv.FormatInt(d, 10) + "n"
|
||||||
|
}
|
||||||
|
if d := div(t, time.Microsecond); d <= maxTimeoutValue {
|
||||||
|
return strconv.FormatInt(d, 10) + "u"
|
||||||
|
}
|
||||||
|
if d := div(t, time.Millisecond); d <= maxTimeoutValue {
|
||||||
|
return strconv.FormatInt(d, 10) + "m"
|
||||||
|
}
|
||||||
|
if d := div(t, time.Second); d <= maxTimeoutValue {
|
||||||
|
return strconv.FormatInt(d, 10) + "S"
|
||||||
|
}
|
||||||
|
if d := div(t, time.Minute); d <= maxTimeoutValue {
|
||||||
|
return strconv.FormatInt(d, 10) + "M"
|
||||||
|
}
|
||||||
|
// Note that maxTimeoutValue * time.Hour > MaxInt64.
|
||||||
|
return strconv.FormatInt(div(t, time.Hour), 10) + "H"
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"google.golang.org/grpc/metadata"
|
||||||
|
)
|
||||||
|
|
||||||
|
type mdExtraKey struct{}
|
||||||
|
|
||||||
|
// WithExtraMetadata creates a new context with incoming md attached.
|
||||||
|
func WithExtraMetadata(ctx context.Context, md metadata.MD) context.Context {
|
||||||
|
return context.WithValue(ctx, mdExtraKey{}, md)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtraMetadata returns the incoming metadata in ctx if it exists. The
|
||||||
|
// returned MD should not be modified. Writing to it may cause races.
|
||||||
|
// Modification should be made to copies of the returned MD.
|
||||||
|
func ExtraMetadata(ctx context.Context) (md metadata.MD, ok bool) {
|
||||||
|
md, ok = ctx.Value(mdExtraKey{}).(metadata.MD)
|
||||||
|
return
|
||||||
|
}
|
|
@ -0,0 +1,84 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2018 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
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ParseMethod splits service and method from the input. It expects format
|
||||||
|
// "/service/method".
|
||||||
|
//
|
||||||
|
func ParseMethod(methodName string) (service, method string, _ error) {
|
||||||
|
if !strings.HasPrefix(methodName, "/") {
|
||||||
|
return "", "", errors.New("invalid method name: should start with /")
|
||||||
|
}
|
||||||
|
methodName = methodName[1:]
|
||||||
|
|
||||||
|
pos := strings.LastIndex(methodName, "/")
|
||||||
|
if pos < 0 {
|
||||||
|
return "", "", errors.New("invalid method name: suffix /method is missing")
|
||||||
|
}
|
||||||
|
return methodName[:pos], methodName[pos+1:], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const baseContentType = "application/grpc"
|
||||||
|
|
||||||
|
// ContentSubtype returns the content-subtype for the given content-type. The
|
||||||
|
// given content-type must be a valid content-type that starts with
|
||||||
|
// "application/grpc". A content-subtype will follow "application/grpc" after a
|
||||||
|
// "+" or ";". See
|
||||||
|
// https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#requests for
|
||||||
|
// more details.
|
||||||
|
//
|
||||||
|
// If contentType is not a valid content-type for gRPC, the boolean
|
||||||
|
// will be false, otherwise true. If content-type == "application/grpc",
|
||||||
|
// "application/grpc+", or "application/grpc;", the boolean will be true,
|
||||||
|
// but no content-subtype will be returned.
|
||||||
|
//
|
||||||
|
// contentType is assumed to be lowercase already.
|
||||||
|
func ContentSubtype(contentType string) (string, bool) {
|
||||||
|
if contentType == baseContentType {
|
||||||
|
return "", true
|
||||||
|
}
|
||||||
|
if !strings.HasPrefix(contentType, baseContentType) {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
// guaranteed since != baseContentType and has baseContentType prefix
|
||||||
|
switch contentType[len(baseContentType)] {
|
||||||
|
case '+', ';':
|
||||||
|
// this will return true for "application/grpc+" or "application/grpc;"
|
||||||
|
// which the previous validContentType function tested to be valid, so we
|
||||||
|
// just say that no content-subtype is specified in this case
|
||||||
|
return contentType[len(baseContentType)+1:], true
|
||||||
|
default:
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContentType builds full content type with the given sub-type.
|
||||||
|
//
|
||||||
|
// contentSubtype is assumed to be lowercase
|
||||||
|
func ContentType(contentSubtype string) string {
|
||||||
|
if contentSubtype == "" {
|
||||||
|
return baseContentType
|
||||||
|
}
|
||||||
|
return baseContentType + "+" + contentSubtype
|
||||||
|
}
|
|
@ -0,0 +1,89 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* 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. skipUnixColonParsing indicates that the parse should
|
||||||
|
// not parse "unix:[path]" cases. This should be true in cases where a custom
|
||||||
|
// dialer is present, to prevent a behavior change.
|
||||||
|
//
|
||||||
|
// If target is not a valid scheme://authority/endpoint as specified in
|
||||||
|
// https://github.com/grpc/grpc/blob/master/doc/naming.md,
|
||||||
|
// it returns {Endpoint: target}.
|
||||||
|
func ParseTarget(target string, skipUnixColonParsing bool) (ret resolver.Target) {
|
||||||
|
var ok bool
|
||||||
|
if strings.HasPrefix(target, "unix-abstract:") {
|
||||||
|
if strings.HasPrefix(target, "unix-abstract://") {
|
||||||
|
// Maybe, with Authority specified, try to parse it
|
||||||
|
var remain string
|
||||||
|
ret.Scheme, remain, _ = split2(target, "://")
|
||||||
|
ret.Authority, ret.Endpoint, ok = split2(remain, "/")
|
||||||
|
if !ok {
|
||||||
|
// No Authority, add the "//" back
|
||||||
|
ret.Endpoint = "//" + remain
|
||||||
|
} else {
|
||||||
|
// Found Authority, add the "/" back
|
||||||
|
ret.Endpoint = "/" + ret.Endpoint
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Without Authority specified, split target on ":"
|
||||||
|
ret.Scheme, ret.Endpoint, _ = split2(target, ":")
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
ret.Scheme, ret.Endpoint, ok = split2(target, "://")
|
||||||
|
if !ok {
|
||||||
|
if strings.HasPrefix(target, "unix:") && !skipUnixColonParsing {
|
||||||
|
// Handle the "unix:[local/path]" and "unix:[/absolute/path]" cases,
|
||||||
|
// because splitting on :// only handles the
|
||||||
|
// "unix://[/absolute/path]" case. Only handle if the dialer is nil,
|
||||||
|
// to avoid a behavior change with custom dialers.
|
||||||
|
return resolver.Target{Scheme: "unix", Endpoint: target[len("unix:"):]}
|
||||||
|
}
|
||||||
|
return resolver.Target{Endpoint: target}
|
||||||
|
}
|
||||||
|
ret.Authority, ret.Endpoint, ok = split2(ret.Endpoint, "/")
|
||||||
|
if !ok {
|
||||||
|
return resolver.Target{Endpoint: target}
|
||||||
|
}
|
||||||
|
if ret.Scheme == "unix" {
|
||||||
|
// Add the "/" back in the unix case, so the unix resolver receives the
|
||||||
|
// actual endpoint in the "unix://[/absolute/path]" case.
|
||||||
|
ret.Endpoint = "/" + ret.Endpoint
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
|
@ -25,6 +25,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"google.golang.org/grpc/connectivity"
|
"google.golang.org/grpc/connectivity"
|
||||||
|
"google.golang.org/grpc/serviceconfig"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -37,17 +38,32 @@ 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
|
|
||||||
// the passed in RequestInfo to the new context.
|
|
||||||
NewRequestInfoContext interface{} // func(context.Context, credentials.RequestInfo) context.Context
|
|
||||||
// ParseServiceConfigForTesting is for creating a fake
|
// ParseServiceConfigForTesting is for creating a fake
|
||||||
// ClientConn for resolver testing only
|
// ClientConn for resolver testing only
|
||||||
ParseServiceConfigForTesting interface{} // func(string) *serviceconfig.ParseResult
|
ParseServiceConfigForTesting interface{} // func(string) *serviceconfig.ParseResult
|
||||||
|
// EqualServiceConfigForTesting is for testing service config generation and
|
||||||
|
// parsing. Both a and b should be returned by ParseServiceConfigForTesting.
|
||||||
|
// This function compares the config without rawJSON stripped, in case the
|
||||||
|
// there's difference in white space.
|
||||||
|
EqualServiceConfigForTesting func(a, b serviceconfig.Config) bool
|
||||||
|
// GetCertificateProviderBuilder returns the registered builder for the
|
||||||
|
// given name. This is set by package certprovider for use from xDS
|
||||||
|
// bootstrap code while parsing certificate provider configs in the
|
||||||
|
// bootstrap file.
|
||||||
|
GetCertificateProviderBuilder interface{} // func(string) certprovider.Builder
|
||||||
|
// GetXDSHandshakeInfoForTesting returns a pointer to the xds.HandshakeInfo
|
||||||
|
// stored in the passed in attributes. This is set by
|
||||||
|
// credentials/xds/xds.go.
|
||||||
|
GetXDSHandshakeInfoForTesting interface{} // func (*attributes.Attributes) *xds.HandshakeInfo
|
||||||
|
// GetServerCredentials returns the transport credentials configured on a
|
||||||
|
// gRPC server. An xDS-enabled server needs to know what type of credentials
|
||||||
|
// is configured on the underlying gRPC server. This is set by server.go.
|
||||||
|
GetServerCredentials interface{} // func (*grpc.Server) credentials.TransportCredentials
|
||||||
|
// DrainServerTransports initiates a graceful close of existing connections
|
||||||
|
// on a gRPC server accepted on the provided listener address. An
|
||||||
|
// xDS-enabled server invokes this method on a grpc.Server when a particular
|
||||||
|
// listener moves to "not-serving" mode.
|
||||||
|
DrainServerTransports interface{} // func(*grpc.Server, string)
|
||||||
)
|
)
|
||||||
|
|
||||||
// HealthChecker defines the signature of the client-side LB channel health checking function.
|
// HealthChecker defines the signature of the client-side LB channel health checking function.
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* 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 metadata contains functions to set and get metadata from addresses.
|
||||||
|
//
|
||||||
|
// This package is experimental.
|
||||||
|
package metadata
|
||||||
|
|
||||||
|
import (
|
||||||
|
"google.golang.org/grpc/metadata"
|
||||||
|
"google.golang.org/grpc/resolver"
|
||||||
|
)
|
||||||
|
|
||||||
|
type mdKeyType string
|
||||||
|
|
||||||
|
const mdKey = mdKeyType("grpc.internal.address.metadata")
|
||||||
|
|
||||||
|
// Get returns the metadata of addr.
|
||||||
|
func Get(addr resolver.Address) metadata.MD {
|
||||||
|
attrs := addr.Attributes
|
||||||
|
if attrs == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
md, _ := attrs.Value(mdKey).(metadata.MD)
|
||||||
|
return md
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set sets (overrides) the metadata in addr.
|
||||||
|
//
|
||||||
|
// When a SubConn is created with this address, the RPCs sent on it will all
|
||||||
|
// have this metadata.
|
||||||
|
func Set(addr resolver.Address, md metadata.MD) resolver.Address {
|
||||||
|
addr.Attributes = addr.Attributes.WithValues(mdKey, md)
|
||||||
|
return addr
|
||||||
|
}
|
164
vendor/google.golang.org/grpc/internal/resolver/config_selector.go
generated
vendored
Normal file
164
vendor/google.golang.org/grpc/internal/resolver/config_selector.go
generated
vendored
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* 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 resolver provides internal resolver-related functionality.
|
||||||
|
package resolver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"google.golang.org/grpc/internal/serviceconfig"
|
||||||
|
"google.golang.org/grpc/metadata"
|
||||||
|
"google.golang.org/grpc/resolver"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ConfigSelector controls what configuration to use for every RPC.
|
||||||
|
type ConfigSelector interface {
|
||||||
|
// Selects the configuration for the RPC, or terminates it using the error.
|
||||||
|
// This error will be converted by the gRPC library to a status error with
|
||||||
|
// code UNKNOWN if it is not returned as a status error.
|
||||||
|
SelectConfig(RPCInfo) (*RPCConfig, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RPCInfo contains RPC information needed by a ConfigSelector.
|
||||||
|
type RPCInfo struct {
|
||||||
|
// Context is the user's context for the RPC and contains headers and
|
||||||
|
// application timeout. It is passed for interception purposes and for
|
||||||
|
// efficiency reasons. SelectConfig should not be blocking.
|
||||||
|
Context context.Context
|
||||||
|
Method string // i.e. "/Service/Method"
|
||||||
|
}
|
||||||
|
|
||||||
|
// RPCConfig describes the configuration to use for each RPC.
|
||||||
|
type RPCConfig struct {
|
||||||
|
// The context to use for the remainder of the RPC; can pass info to LB
|
||||||
|
// policy or affect timeout or metadata.
|
||||||
|
Context context.Context
|
||||||
|
MethodConfig serviceconfig.MethodConfig // configuration to use for this RPC
|
||||||
|
OnCommitted func() // Called when the RPC has been committed (retries no longer possible)
|
||||||
|
Interceptor ClientInterceptor
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClientStream is the same as grpc.ClientStream, but defined here for circular
|
||||||
|
// dependency reasons.
|
||||||
|
type ClientStream interface {
|
||||||
|
// Header returns the header metadata received from the server if there
|
||||||
|
// is any. It blocks if the metadata is not ready to read.
|
||||||
|
Header() (metadata.MD, error)
|
||||||
|
// Trailer returns the trailer metadata from the server, if there is any.
|
||||||
|
// It must only be called after stream.CloseAndRecv has returned, or
|
||||||
|
// stream.Recv has returned a non-nil error (including io.EOF).
|
||||||
|
Trailer() metadata.MD
|
||||||
|
// CloseSend closes the send direction of the stream. It closes the stream
|
||||||
|
// when non-nil error is met. It is also not safe to call CloseSend
|
||||||
|
// concurrently with SendMsg.
|
||||||
|
CloseSend() error
|
||||||
|
// Context returns the context for this stream.
|
||||||
|
//
|
||||||
|
// It should not be called until after Header or RecvMsg has returned. Once
|
||||||
|
// called, subsequent client-side retries are disabled.
|
||||||
|
Context() context.Context
|
||||||
|
// SendMsg is generally called by generated code. On error, SendMsg aborts
|
||||||
|
// the stream. If the error was generated by the client, the status is
|
||||||
|
// returned directly; otherwise, io.EOF is returned and the status of
|
||||||
|
// the stream may be discovered using RecvMsg.
|
||||||
|
//
|
||||||
|
// SendMsg blocks until:
|
||||||
|
// - There is sufficient flow control to schedule m with the transport, or
|
||||||
|
// - The stream is done, or
|
||||||
|
// - The stream breaks.
|
||||||
|
//
|
||||||
|
// SendMsg does not wait until the message is received by the server. An
|
||||||
|
// untimely stream closure may result in lost messages. To ensure delivery,
|
||||||
|
// users should ensure the RPC completed successfully using RecvMsg.
|
||||||
|
//
|
||||||
|
// It is safe to have a goroutine calling SendMsg and another goroutine
|
||||||
|
// calling RecvMsg on the same stream at the same time, but it is not safe
|
||||||
|
// to call SendMsg on the same stream in different goroutines. It is also
|
||||||
|
// not safe to call CloseSend concurrently with SendMsg.
|
||||||
|
SendMsg(m interface{}) error
|
||||||
|
// RecvMsg blocks until it receives a message into m or the stream is
|
||||||
|
// done. It returns io.EOF when the stream completes successfully. On
|
||||||
|
// any other error, the stream is aborted and the error contains the RPC
|
||||||
|
// status.
|
||||||
|
//
|
||||||
|
// It is safe to have a goroutine calling SendMsg and another goroutine
|
||||||
|
// calling RecvMsg on the same stream at the same time, but it is not
|
||||||
|
// safe to call RecvMsg on the same stream in different goroutines.
|
||||||
|
RecvMsg(m interface{}) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClientInterceptor is an interceptor for gRPC client streams.
|
||||||
|
type ClientInterceptor interface {
|
||||||
|
// NewStream produces a ClientStream for an RPC which may optionally use
|
||||||
|
// the provided function to produce a stream for delegation. Note:
|
||||||
|
// RPCInfo.Context should not be used (will be nil).
|
||||||
|
//
|
||||||
|
// done is invoked when the RPC is finished using its connection, or could
|
||||||
|
// not be assigned a connection. RPC operations may still occur on
|
||||||
|
// ClientStream after done is called, since the interceptor is invoked by
|
||||||
|
// application-layer operations. done must never be nil when called.
|
||||||
|
NewStream(ctx context.Context, ri RPCInfo, done func(), newStream func(ctx context.Context, done func()) (ClientStream, error)) (ClientStream, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerInterceptor is unimplementable; do not use.
|
||||||
|
type ServerInterceptor interface {
|
||||||
|
notDefined()
|
||||||
|
}
|
||||||
|
|
||||||
|
type csKeyType string
|
||||||
|
|
||||||
|
const csKey = csKeyType("grpc.internal.resolver.configSelector")
|
||||||
|
|
||||||
|
// SetConfigSelector sets the config selector in state and returns the new
|
||||||
|
// state.
|
||||||
|
func SetConfigSelector(state resolver.State, cs ConfigSelector) resolver.State {
|
||||||
|
state.Attributes = state.Attributes.WithValues(csKey, cs)
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetConfigSelector retrieves the config selector from state, if present, and
|
||||||
|
// returns it or nil if absent.
|
||||||
|
func GetConfigSelector(state resolver.State) ConfigSelector {
|
||||||
|
cs, _ := state.Attributes.Value(csKey).(ConfigSelector)
|
||||||
|
return cs
|
||||||
|
}
|
||||||
|
|
||||||
|
// SafeConfigSelector allows for safe switching of ConfigSelector
|
||||||
|
// implementations such that previous values are guaranteed to not be in use
|
||||||
|
// when UpdateConfigSelector returns.
|
||||||
|
type SafeConfigSelector struct {
|
||||||
|
mu sync.RWMutex
|
||||||
|
cs ConfigSelector
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateConfigSelector swaps to the provided ConfigSelector and blocks until
|
||||||
|
// all uses of the previous ConfigSelector have completed.
|
||||||
|
func (scs *SafeConfigSelector) UpdateConfigSelector(cs ConfigSelector) {
|
||||||
|
scs.mu.Lock()
|
||||||
|
defer scs.mu.Unlock()
|
||||||
|
scs.cs = cs
|
||||||
|
}
|
||||||
|
|
||||||
|
// SelectConfig defers to the current ConfigSelector in scs.
|
||||||
|
func (scs *SafeConfigSelector) SelectConfig(r RPCInfo) (*RPCConfig, error) {
|
||||||
|
scs.mu.RLock()
|
||||||
|
defer scs.mu.RUnlock()
|
||||||
|
return scs.cs.SelectConfig(r)
|
||||||
|
}
|
|
@ -32,7 +32,9 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
grpclbstate "google.golang.org/grpc/balancer/grpclb/state"
|
||||||
"google.golang.org/grpc/grpclog"
|
"google.golang.org/grpc/grpclog"
|
||||||
|
"google.golang.org/grpc/internal/backoff"
|
||||||
"google.golang.org/grpc/internal/envconfig"
|
"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"
|
||||||
|
@ -43,6 +45,15 @@ import (
|
||||||
// addresses from SRV records. Must not be changed after init time.
|
// addresses from SRV records. Must not be changed after init time.
|
||||||
var EnableSRVLookups = false
|
var EnableSRVLookups = false
|
||||||
|
|
||||||
|
var logger = grpclog.Component("dns")
|
||||||
|
|
||||||
|
// Globals to stub out in tests. TODO: Perhaps these two can be combined into a
|
||||||
|
// single variable for testing the resolver?
|
||||||
|
var (
|
||||||
|
newTimer = time.NewTimer
|
||||||
|
newTimerDNSResRate = time.NewTimer
|
||||||
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
resolver.Register(NewBuilder())
|
resolver.Register(NewBuilder())
|
||||||
}
|
}
|
||||||
|
@ -140,7 +151,6 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,28 +208,38 @@ func (d *dnsResolver) Close() {
|
||||||
|
|
||||||
func (d *dnsResolver) watcher() {
|
func (d *dnsResolver) watcher() {
|
||||||
defer d.wg.Done()
|
defer d.wg.Done()
|
||||||
|
backoffIndex := 1
|
||||||
for {
|
for {
|
||||||
|
state, err := d.lookup()
|
||||||
|
if err != nil {
|
||||||
|
// Report error to the underlying grpc.ClientConn.
|
||||||
|
d.cc.ReportError(err)
|
||||||
|
} else {
|
||||||
|
err = d.cc.UpdateState(*state)
|
||||||
|
}
|
||||||
|
|
||||||
|
var timer *time.Timer
|
||||||
|
if err == nil {
|
||||||
|
// Success resolving, wait for the next ResolveNow. However, also wait 30 seconds at the very least
|
||||||
|
// to prevent constantly re-resolving.
|
||||||
|
backoffIndex = 1
|
||||||
|
timer = newTimerDNSResRate(minDNSResRate)
|
||||||
select {
|
select {
|
||||||
case <-d.ctx.Done():
|
case <-d.ctx.Done():
|
||||||
|
timer.Stop()
|
||||||
return
|
return
|
||||||
case <-d.rn:
|
case <-d.rn:
|
||||||
}
|
}
|
||||||
|
|
||||||
state, err := d.lookup()
|
|
||||||
if err != nil {
|
|
||||||
d.cc.ReportError(err)
|
|
||||||
} else {
|
} else {
|
||||||
d.cc.UpdateState(*state)
|
// Poll on an error found in DNS Resolver or an error received from ClientConn.
|
||||||
|
timer = newTimer(backoff.DefaultExponential.Backoff(backoffIndex))
|
||||||
|
backoffIndex++
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sleep to prevent excessive re-resolutions. Incoming resolution requests
|
|
||||||
// will be queued in d.rn.
|
|
||||||
t := time.NewTimer(minDNSResRate)
|
|
||||||
select {
|
select {
|
||||||
case <-t.C:
|
|
||||||
case <-d.ctx.Done():
|
case <-d.ctx.Done():
|
||||||
t.Stop()
|
timer.Stop()
|
||||||
return
|
return
|
||||||
|
case <-timer.C:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -251,7 +271,7 @@ func (d *dnsResolver) lookupSRV() ([]resolver.Address, error) {
|
||||||
return nil, fmt.Errorf("dns: error parsing A record IP address %v", a)
|
return nil, fmt.Errorf("dns: error parsing A record IP address %v", a)
|
||||||
}
|
}
|
||||||
addr := ip + ":" + 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, ServerName: s.Target})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return newAddrs, nil
|
return newAddrs, nil
|
||||||
|
@ -271,7 +291,7 @@ func handleDNSError(err error, lookupType string) error {
|
||||||
err = filterError(err)
|
err = filterError(err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("dns: %v record lookup error: %v", lookupType, err)
|
err = fmt.Errorf("dns: %v record lookup error: %v", lookupType, err)
|
||||||
grpclog.Infoln(err)
|
logger.Info(err)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -294,7 +314,7 @@ func (d *dnsResolver) lookupTXT() *serviceconfig.ParseResult {
|
||||||
|
|
||||||
// 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("dns: TXT record %v missing %v attribute", res, txtAttribute)
|
logger.Warningf("dns: TXT record %v missing %v attribute", res, txtAttribute)
|
||||||
// This is not an error; it is the equivalent of not having a service config.
|
// This is not an error; it is the equivalent of not having a service config.
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -326,13 +346,15 @@ func (d *dnsResolver) lookup() (*resolver.State, error) {
|
||||||
if hostErr != nil && (srvErr != nil || len(srv) == 0) {
|
if hostErr != nil && (srvErr != nil || len(srv) == 0) {
|
||||||
return nil, hostErr
|
return nil, hostErr
|
||||||
}
|
}
|
||||||
state := &resolver.State{
|
|
||||||
Addresses: append(addrs, srv...),
|
state := resolver.State{Addresses: addrs}
|
||||||
|
if len(srv) > 0 {
|
||||||
|
state = grpclbstate.Set(state, &grpclbstate.State{BalancerAddresses: srv})
|
||||||
}
|
}
|
||||||
if !d.disableServiceConfig {
|
if !d.disableServiceConfig {
|
||||||
state.ServiceConfig = d.lookupTXT()
|
state.ServiceConfig = d.lookupTXT()
|
||||||
}
|
}
|
||||||
return state, nil
|
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.
|
||||||
|
@ -418,12 +440,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("dns: error parsing service config json: %v", err)
|
logger.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("dns: error getting client hostname: %v", err)
|
logger.Warningf("dns: error getting client hostname: %v", err)
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
var sc string
|
var sc string
|
||||||
|
|
|
@ -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 unix implements a resolver for unix targets.
|
||||||
|
package unix
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"google.golang.org/grpc/internal/transport/networktype"
|
||||||
|
"google.golang.org/grpc/resolver"
|
||||||
|
)
|
||||||
|
|
||||||
|
const unixScheme = "unix"
|
||||||
|
const unixAbstractScheme = "unix-abstract"
|
||||||
|
|
||||||
|
type builder struct {
|
||||||
|
scheme string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *builder) Build(target resolver.Target, cc resolver.ClientConn, _ resolver.BuildOptions) (resolver.Resolver, error) {
|
||||||
|
if target.Authority != "" {
|
||||||
|
return nil, fmt.Errorf("invalid (non-empty) authority: %v", target.Authority)
|
||||||
|
}
|
||||||
|
addr := resolver.Address{Addr: target.Endpoint}
|
||||||
|
if b.scheme == unixAbstractScheme {
|
||||||
|
// prepend "\x00" to address for unix-abstract
|
||||||
|
addr.Addr = "\x00" + addr.Addr
|
||||||
|
}
|
||||||
|
cc.UpdateState(resolver.State{Addresses: []resolver.Address{networktype.Set(addr, "unix")}})
|
||||||
|
return &nopResolver{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *builder) Scheme() string {
|
||||||
|
return b.scheme
|
||||||
|
}
|
||||||
|
|
||||||
|
type nopResolver struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*nopResolver) ResolveNow(resolver.ResolveNowOptions) {}
|
||||||
|
|
||||||
|
func (*nopResolver) Close() {}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
resolver.Register(&builder{scheme: unixScheme})
|
||||||
|
resolver.Register(&builder{scheme: unixAbstractScheme})
|
||||||
|
}
|
178
vendor/google.golang.org/grpc/internal/serviceconfig/serviceconfig.go
generated
vendored
Normal file
178
vendor/google.golang.org/grpc/internal/serviceconfig/serviceconfig.go
generated
vendored
Normal file
|
@ -0,0 +1,178 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* 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 serviceconfig contains utility functions to parse service config.
|
||||||
|
package serviceconfig
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"google.golang.org/grpc/balancer"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/grpclog"
|
||||||
|
externalserviceconfig "google.golang.org/grpc/serviceconfig"
|
||||||
|
)
|
||||||
|
|
||||||
|
var logger = grpclog.Component("core")
|
||||||
|
|
||||||
|
// BalancerConfig wraps the name and config associated with one load balancing
|
||||||
|
// policy. It corresponds to a single entry of the loadBalancingConfig field
|
||||||
|
// from ServiceConfig.
|
||||||
|
//
|
||||||
|
// It implements the json.Unmarshaler interface.
|
||||||
|
//
|
||||||
|
// https://github.com/grpc/grpc-proto/blob/54713b1e8bc6ed2d4f25fb4dff527842150b91b2/grpc/service_config/service_config.proto#L247
|
||||||
|
type BalancerConfig struct {
|
||||||
|
Name string
|
||||||
|
Config externalserviceconfig.LoadBalancingConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
type intermediateBalancerConfig []map[string]json.RawMessage
|
||||||
|
|
||||||
|
// MarshalJSON implements the json.Marshaler interface.
|
||||||
|
//
|
||||||
|
// It marshals the balancer and config into a length-1 slice
|
||||||
|
// ([]map[string]config).
|
||||||
|
func (bc *BalancerConfig) MarshalJSON() ([]byte, error) {
|
||||||
|
if bc.Config == nil {
|
||||||
|
// If config is nil, return empty config `{}`.
|
||||||
|
return []byte(fmt.Sprintf(`[{%q: %v}]`, bc.Name, "{}")), nil
|
||||||
|
}
|
||||||
|
c, err := json.Marshal(bc.Config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return []byte(fmt.Sprintf(`[{%q: %s}]`, bc.Name, c)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON implements the json.Unmarshaler interface.
|
||||||
|
//
|
||||||
|
// ServiceConfig contains a list of loadBalancingConfigs, each with a name and
|
||||||
|
// config. This method iterates through that list in order, and stops at the
|
||||||
|
// first policy that is supported.
|
||||||
|
// - If the config for the first supported policy is invalid, the whole service
|
||||||
|
// config is invalid.
|
||||||
|
// - If the list doesn't contain any supported policy, the whole service config
|
||||||
|
// is invalid.
|
||||||
|
func (bc *BalancerConfig) UnmarshalJSON(b []byte) error {
|
||||||
|
var ir intermediateBalancerConfig
|
||||||
|
err := json.Unmarshal(b, &ir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, lbcfg := range ir {
|
||||||
|
if len(lbcfg) != 1 {
|
||||||
|
return fmt.Errorf("invalid loadBalancingConfig: entry %v does not contain exactly 1 policy/config pair: %q", i, lbcfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
name string
|
||||||
|
jsonCfg json.RawMessage
|
||||||
|
)
|
||||||
|
// Get the key:value pair from the map. We have already made sure that
|
||||||
|
// the map contains a single entry.
|
||||||
|
for name, jsonCfg = range lbcfg {
|
||||||
|
}
|
||||||
|
|
||||||
|
builder := balancer.Get(name)
|
||||||
|
if builder == nil {
|
||||||
|
// If the balancer is not registered, move on to the next config.
|
||||||
|
// This is not an error.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
bc.Name = name
|
||||||
|
|
||||||
|
parser, ok := builder.(balancer.ConfigParser)
|
||||||
|
if !ok {
|
||||||
|
if string(jsonCfg) != "{}" {
|
||||||
|
logger.Warningf("non-empty balancer configuration %q, but balancer does not implement ParseConfig", string(jsonCfg))
|
||||||
|
}
|
||||||
|
// Stop at this, though the builder doesn't support parsing config.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg, err := parser.ParseConfig(jsonCfg)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error parsing loadBalancingConfig for policy %q: %v", name, err)
|
||||||
|
}
|
||||||
|
bc.Config = cfg
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// This is reached when the for loop iterates over all entries, but didn't
|
||||||
|
// return. This means we had a loadBalancingConfig slice but did not
|
||||||
|
// encounter a registered policy. The config is considered invalid in this
|
||||||
|
// case.
|
||||||
|
return fmt.Errorf("invalid loadBalancingConfig: no supported policies found")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MethodConfig defines the configuration recommended by the service providers for a
|
||||||
|
// particular method.
|
||||||
|
type MethodConfig struct {
|
||||||
|
// WaitForReady indicates whether RPCs sent to this method should wait until
|
||||||
|
// the connection is ready by default (!failfast). The value specified via the
|
||||||
|
// gRPC client API will override the value set here.
|
||||||
|
WaitForReady *bool
|
||||||
|
// Timeout is the default timeout for RPCs sent to this method. The actual
|
||||||
|
// deadline used will be the minimum of the value specified here and the value
|
||||||
|
// set by the application via the gRPC client API. If either one is not set,
|
||||||
|
// then the other will be used. If neither is set, then the RPC has no deadline.
|
||||||
|
Timeout *time.Duration
|
||||||
|
// MaxReqSize is the maximum allowed payload size for an individual request in a
|
||||||
|
// stream (client->server) in bytes. The size which is measured is the serialized
|
||||||
|
// payload after per-message compression (but before stream compression) in bytes.
|
||||||
|
// The actual value used is the minimum of the value specified here and the value set
|
||||||
|
// by the application via the gRPC client API. If either one is not set, then the other
|
||||||
|
// will be used. If neither is set, then the built-in default is used.
|
||||||
|
MaxReqSize *int
|
||||||
|
// MaxRespSize is the maximum allowed payload size for an individual response in a
|
||||||
|
// stream (server->client) in bytes.
|
||||||
|
MaxRespSize *int
|
||||||
|
// RetryPolicy configures retry options for the method.
|
||||||
|
RetryPolicy *RetryPolicy
|
||||||
|
}
|
||||||
|
|
||||||
|
// RetryPolicy defines the go-native version of the retry policy defined by the
|
||||||
|
// service config here:
|
||||||
|
// https://github.com/grpc/proposal/blob/master/A6-client-retries.md#integration-with-service-config
|
||||||
|
type RetryPolicy struct {
|
||||||
|
// MaxAttempts is the maximum number of attempts, including the original RPC.
|
||||||
|
//
|
||||||
|
// This field is required and must be two or greater.
|
||||||
|
MaxAttempts int
|
||||||
|
|
||||||
|
// Exponential backoff parameters. The initial retry attempt will occur at
|
||||||
|
// random(0, initialBackoff). In general, the nth attempt will occur at
|
||||||
|
// random(0,
|
||||||
|
// min(initialBackoff*backoffMultiplier**(n-1), maxBackoff)).
|
||||||
|
//
|
||||||
|
// These fields are required and must be greater than zero.
|
||||||
|
InitialBackoff time.Duration
|
||||||
|
MaxBackoff time.Duration
|
||||||
|
BackoffMultiplier float64
|
||||||
|
|
||||||
|
// The set of status codes which may be retried.
|
||||||
|
//
|
||||||
|
// Status codes are specified as strings, e.g., "UNAVAILABLE".
|
||||||
|
//
|
||||||
|
// This field is required and must be non-empty.
|
||||||
|
// Note: a set is used to store this for easy lookup.
|
||||||
|
RetryableStatusCodes map[codes.Code]bool
|
||||||
|
}
|
|
@ -0,0 +1,162 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* 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{e: s.Proto()}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 wraps a pointer of a status proto. It implements error and Status,
|
||||||
|
// and a nil *Error should never be returned by this package.
|
||||||
|
type Error struct {
|
||||||
|
e *spb.Status
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Error) Error() string {
|
||||||
|
return fmt.Sprintf("rpc error: code = %s desc = %s", codes.Code(e.e.GetCode()), e.e.GetMessage())
|
||||||
|
}
|
||||||
|
|
||||||
|
// GRPCStatus returns the Status represented by se.
|
||||||
|
func (e *Error) GRPCStatus() *Status {
|
||||||
|
return FromProto(e.e)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is implements future error.Is functionality.
|
||||||
|
// A Error is equivalent if the code and message are identical.
|
||||||
|
func (e *Error) Is(target error) bool {
|
||||||
|
tse, ok := target.(*Error)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return proto.Equal(e.e, tse.e)
|
||||||
|
}
|
|
@ -32,35 +32,35 @@ import (
|
||||||
"google.golang.org/grpc/grpclog"
|
"google.golang.org/grpc/grpclog"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var logger = grpclog.Component("core")
|
||||||
|
|
||||||
// GetCPUTime returns the how much CPU time has passed since the start of this process.
|
// GetCPUTime returns the how much CPU time has passed since the start of this process.
|
||||||
func GetCPUTime() int64 {
|
func GetCPUTime() int64 {
|
||||||
var ts unix.Timespec
|
var ts unix.Timespec
|
||||||
if err := unix.ClockGettime(unix.CLOCK_PROCESS_CPUTIME_ID, &ts); err != nil {
|
if err := unix.ClockGettime(unix.CLOCK_PROCESS_CPUTIME_ID, &ts); err != nil {
|
||||||
grpclog.Fatal(err)
|
logger.Fatal(err)
|
||||||
}
|
}
|
||||||
return ts.Nano()
|
return ts.Nano()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rusage is an alias for syscall.Rusage under linux non-appengine environment.
|
// Rusage is an alias for syscall.Rusage under linux environment.
|
||||||
type Rusage syscall.Rusage
|
type Rusage = syscall.Rusage
|
||||||
|
|
||||||
// GetRusage returns the resource usage of current process.
|
// GetRusage returns the resource usage of current process.
|
||||||
func GetRusage() (rusage *Rusage) {
|
func GetRusage() *Rusage {
|
||||||
rusage = new(Rusage)
|
rusage := new(Rusage)
|
||||||
syscall.Getrusage(syscall.RUSAGE_SELF, (*syscall.Rusage)(rusage))
|
syscall.Getrusage(syscall.RUSAGE_SELF, rusage)
|
||||||
return
|
return rusage
|
||||||
}
|
}
|
||||||
|
|
||||||
// CPUTimeDiff returns the differences of user CPU time and system CPU time used
|
// CPUTimeDiff returns the differences of user CPU time and system CPU time used
|
||||||
// between two Rusage structs.
|
// between two Rusage structs.
|
||||||
func CPUTimeDiff(first *Rusage, latest *Rusage) (float64, float64) {
|
func CPUTimeDiff(first *Rusage, latest *Rusage) (float64, float64) {
|
||||||
f := (*syscall.Rusage)(first)
|
|
||||||
l := (*syscall.Rusage)(latest)
|
|
||||||
var (
|
var (
|
||||||
utimeDiffs = l.Utime.Sec - f.Utime.Sec
|
utimeDiffs = latest.Utime.Sec - first.Utime.Sec
|
||||||
utimeDiffus = l.Utime.Usec - f.Utime.Usec
|
utimeDiffus = latest.Utime.Usec - first.Utime.Usec
|
||||||
stimeDiffs = l.Stime.Sec - f.Stime.Sec
|
stimeDiffs = latest.Stime.Sec - first.Stime.Sec
|
||||||
stimeDiffus = l.Stime.Usec - f.Stime.Usec
|
stimeDiffus = latest.Stime.Usec - first.Stime.Usec
|
||||||
)
|
)
|
||||||
|
|
||||||
uTimeElapsed := float64(utimeDiffs) + float64(utimeDiffus)*1.0e-6
|
uTimeElapsed := float64(utimeDiffs) + float64(utimeDiffus)*1.0e-6
|
||||||
|
|
|
@ -18,6 +18,8 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// Package syscall provides functionalities that grpc uses to get low-level
|
||||||
|
// operating system stats/info.
|
||||||
package syscall
|
package syscall
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -29,10 +31,11 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var once sync.Once
|
var once sync.Once
|
||||||
|
var logger = grpclog.Component("core")
|
||||||
|
|
||||||
func log() {
|
func log() {
|
||||||
once.Do(func() {
|
once.Do(func() {
|
||||||
grpclog.Info("CPU time info is unavailable on non-linux or appengine environment.")
|
logger.Info("CPU time info is unavailable on non-linux or appengine environment.")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,7 +50,7 @@ func GetCPUTime() int64 {
|
||||||
type Rusage struct{}
|
type Rusage struct{}
|
||||||
|
|
||||||
// GetRusage is a no-op function under non-linux or appengine environment.
|
// GetRusage is a no-op function under non-linux or appengine environment.
|
||||||
func GetRusage() (rusage *Rusage) {
|
func GetRusage() *Rusage {
|
||||||
log()
|
log()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,13 +20,17 @@ package transport
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
"golang.org/x/net/http2"
|
"golang.org/x/net/http2"
|
||||||
"golang.org/x/net/http2/hpack"
|
"golang.org/x/net/http2/hpack"
|
||||||
|
"google.golang.org/grpc/internal/grpcutil"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
)
|
)
|
||||||
|
|
||||||
var updateHeaderTblSize = func(e *hpack.Encoder, v uint32) {
|
var updateHeaderTblSize = func(e *hpack.Encoder, v uint32) {
|
||||||
|
@ -128,6 +132,14 @@ type cleanupStream struct {
|
||||||
|
|
||||||
func (c *cleanupStream) isTransportResponseFrame() bool { return c.rst } // Results in a RST_STREAM
|
func (c *cleanupStream) isTransportResponseFrame() bool { return c.rst } // Results in a RST_STREAM
|
||||||
|
|
||||||
|
type earlyAbortStream struct {
|
||||||
|
streamID uint32
|
||||||
|
contentSubtype string
|
||||||
|
status *status.Status
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*earlyAbortStream) isTransportResponseFrame() bool { return false }
|
||||||
|
|
||||||
type dataFrame struct {
|
type dataFrame struct {
|
||||||
streamID uint32
|
streamID uint32
|
||||||
endStream bool
|
endStream bool
|
||||||
|
@ -505,7 +517,9 @@ func (l *loopyWriter) run() (err error) {
|
||||||
// 1. When the connection is closed by some other known issue.
|
// 1. When the connection is closed by some other known issue.
|
||||||
// 2. User closed the connection.
|
// 2. User closed the connection.
|
||||||
// 3. A graceful close of connection.
|
// 3. A graceful close of connection.
|
||||||
infof("transport: loopyWriter.run returning. %v", err)
|
if logger.V(logLevel) {
|
||||||
|
logger.Infof("transport: loopyWriter.run returning. %v", err)
|
||||||
|
}
|
||||||
err = nil
|
err = nil
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
@ -605,7 +619,9 @@ func (l *loopyWriter) headerHandler(h *headerFrame) error {
|
||||||
if l.side == serverSide {
|
if l.side == serverSide {
|
||||||
str, ok := l.estdStreams[h.streamID]
|
str, ok := l.estdStreams[h.streamID]
|
||||||
if !ok {
|
if !ok {
|
||||||
warningf("transport: loopy doesn't recognize the stream: %d", h.streamID)
|
if logger.V(logLevel) {
|
||||||
|
logger.Warningf("transport: loopy doesn't recognize the stream: %d", h.streamID)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
// Case 1.A: Server is responding back with headers.
|
// Case 1.A: Server is responding back with headers.
|
||||||
|
@ -658,7 +674,9 @@ func (l *loopyWriter) writeHeader(streamID uint32, endStream bool, hf []hpack.He
|
||||||
l.hBuf.Reset()
|
l.hBuf.Reset()
|
||||||
for _, f := range hf {
|
for _, f := range hf {
|
||||||
if err := l.hEnc.WriteField(f); err != nil {
|
if err := l.hEnc.WriteField(f); err != nil {
|
||||||
warningf("transport: loopyWriter.writeHeader encountered error while encoding headers:", err)
|
if logger.V(logLevel) {
|
||||||
|
logger.Warningf("transport: loopyWriter.writeHeader encountered error while encoding headers: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
|
@ -743,6 +761,24 @@ func (l *loopyWriter) cleanupStreamHandler(c *cleanupStream) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (l *loopyWriter) earlyAbortStreamHandler(eas *earlyAbortStream) error {
|
||||||
|
if l.side == clientSide {
|
||||||
|
return errors.New("earlyAbortStream not handled on client")
|
||||||
|
}
|
||||||
|
|
||||||
|
headerFields := []hpack.HeaderField{
|
||||||
|
{Name: ":status", Value: "200"},
|
||||||
|
{Name: "content-type", Value: grpcutil.ContentType(eas.contentSubtype)},
|
||||||
|
{Name: "grpc-status", Value: strconv.Itoa(int(eas.status.Code()))},
|
||||||
|
{Name: "grpc-message", Value: encodeGrpcMessage(eas.status.Message())},
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := l.writeHeader(eas.streamID, true, headerFields, nil); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (l *loopyWriter) incomingGoAwayHandler(*incomingGoAway) error {
|
func (l *loopyWriter) incomingGoAwayHandler(*incomingGoAway) error {
|
||||||
if l.side == clientSide {
|
if l.side == clientSide {
|
||||||
l.draining = true
|
l.draining = true
|
||||||
|
@ -781,6 +817,8 @@ func (l *loopyWriter) handle(i interface{}) error {
|
||||||
return l.registerStreamHandler(i)
|
return l.registerStreamHandler(i)
|
||||||
case *cleanupStream:
|
case *cleanupStream:
|
||||||
return l.cleanupStreamHandler(i)
|
return l.cleanupStreamHandler(i)
|
||||||
|
case *earlyAbortStream:
|
||||||
|
return l.earlyAbortStreamHandler(i)
|
||||||
case *incomingGoAway:
|
case *incomingGoAway:
|
||||||
return l.incomingGoAwayHandler(i)
|
return l.incomingGoAwayHandler(i)
|
||||||
case *dataFrame:
|
case *dataFrame:
|
||||||
|
@ -857,53 +895,56 @@ func (l *loopyWriter) processData() (bool, error) {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
idx int
|
|
||||||
buf []byte
|
buf []byte
|
||||||
)
|
)
|
||||||
if len(dataItem.h) != 0 { // data header has not been written out yet.
|
// Figure out the maximum size we can send
|
||||||
buf = dataItem.h
|
maxSize := http2MaxFrameLen
|
||||||
} else {
|
|
||||||
idx = 1
|
|
||||||
buf = dataItem.d
|
|
||||||
}
|
|
||||||
size := http2MaxFrameLen
|
|
||||||
if len(buf) < size {
|
|
||||||
size = len(buf)
|
|
||||||
}
|
|
||||||
if strQuota := int(l.oiws) - str.bytesOutStanding; strQuota <= 0 { // stream-level flow control.
|
if strQuota := int(l.oiws) - str.bytesOutStanding; strQuota <= 0 { // stream-level flow control.
|
||||||
str.state = waitingOnStreamQuota
|
str.state = waitingOnStreamQuota
|
||||||
return false, nil
|
return false, nil
|
||||||
} else if strQuota < size {
|
} else if maxSize > strQuota {
|
||||||
size = strQuota
|
maxSize = strQuota
|
||||||
|
}
|
||||||
|
if maxSize > int(l.sendQuota) { // connection-level flow control.
|
||||||
|
maxSize = int(l.sendQuota)
|
||||||
|
}
|
||||||
|
// Compute how much of the header and data we can send within quota and max frame length
|
||||||
|
hSize := min(maxSize, len(dataItem.h))
|
||||||
|
dSize := min(maxSize-hSize, len(dataItem.d))
|
||||||
|
if hSize != 0 {
|
||||||
|
if dSize == 0 {
|
||||||
|
buf = dataItem.h
|
||||||
|
} else {
|
||||||
|
// We can add some data to grpc message header to distribute bytes more equally across frames.
|
||||||
|
// Copy on the stack to avoid generating garbage
|
||||||
|
var localBuf [http2MaxFrameLen]byte
|
||||||
|
copy(localBuf[:hSize], dataItem.h)
|
||||||
|
copy(localBuf[hSize:], dataItem.d[:dSize])
|
||||||
|
buf = localBuf[:hSize+dSize]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
buf = dataItem.d
|
||||||
}
|
}
|
||||||
|
|
||||||
if l.sendQuota < uint32(size) { // connection-level flow control.
|
size := hSize + dSize
|
||||||
size = int(l.sendQuota)
|
|
||||||
}
|
|
||||||
// Now that outgoing flow controls are checked we can replenish str's write quota
|
// Now that outgoing flow controls are checked we can replenish str's write quota
|
||||||
str.wq.replenish(size)
|
str.wq.replenish(size)
|
||||||
var endStream bool
|
var endStream bool
|
||||||
// If this is the last data message on this stream and all of it can be written in this iteration.
|
// If this is the last data message on this stream and all of it can be written in this iteration.
|
||||||
if dataItem.endStream && size == len(buf) {
|
if dataItem.endStream && len(dataItem.h)+len(dataItem.d) <= size {
|
||||||
// buf contains either data or it contains header but data is empty.
|
|
||||||
if idx == 1 || len(dataItem.d) == 0 {
|
|
||||||
endStream = true
|
endStream = true
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if dataItem.onEachWrite != nil {
|
if dataItem.onEachWrite != nil {
|
||||||
dataItem.onEachWrite()
|
dataItem.onEachWrite()
|
||||||
}
|
}
|
||||||
if err := l.framer.fr.WriteData(dataItem.streamID, endStream, buf[:size]); err != nil {
|
if err := l.framer.fr.WriteData(dataItem.streamID, endStream, buf[:size]); err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
buf = buf[size:]
|
|
||||||
str.bytesOutStanding += size
|
str.bytesOutStanding += size
|
||||||
l.sendQuota -= uint32(size)
|
l.sendQuota -= uint32(size)
|
||||||
if idx == 0 {
|
dataItem.h = dataItem.h[hSize:]
|
||||||
dataItem.h = buf
|
dataItem.d = dataItem.d[dSize:]
|
||||||
} else {
|
|
||||||
dataItem.d = buf
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(dataItem.h) == 0 && len(dataItem.d) == 0 { // All the data from that message was written out.
|
if len(dataItem.h) == 0 && len(dataItem.d) == 0 { // All the data from that message was written out.
|
||||||
str.itl.dequeue()
|
str.itl.dequeue()
|
||||||
|
@ -924,3 +965,10 @@ func (l *loopyWriter) processData() (bool, error) {
|
||||||
}
|
}
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func min(a, b int) int {
|
||||||
|
if a < b {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
|
@ -39,6 +39,7 @@ import (
|
||||||
"golang.org/x/net/http2"
|
"golang.org/x/net/http2"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/credentials"
|
"google.golang.org/grpc/credentials"
|
||||||
|
"google.golang.org/grpc/internal/grpcutil"
|
||||||
"google.golang.org/grpc/metadata"
|
"google.golang.org/grpc/metadata"
|
||||||
"google.golang.org/grpc/peer"
|
"google.golang.org/grpc/peer"
|
||||||
"google.golang.org/grpc/stats"
|
"google.golang.org/grpc/stats"
|
||||||
|
@ -57,7 +58,7 @@ func NewServerHandlerTransport(w http.ResponseWriter, r *http.Request, stats sta
|
||||||
}
|
}
|
||||||
contentType := r.Header.Get("Content-Type")
|
contentType := r.Header.Get("Content-Type")
|
||||||
// TODO: do we assume contentType is lowercase? we did before
|
// TODO: do we assume contentType is lowercase? we did before
|
||||||
contentSubtype, validContentType := contentSubtype(contentType)
|
contentSubtype, validContentType := grpcutil.ContentSubtype(contentType)
|
||||||
if !validContentType {
|
if !validContentType {
|
||||||
return nil, errors.New("invalid gRPC request content-type")
|
return nil, errors.New("invalid gRPC request content-type")
|
||||||
}
|
}
|
||||||
|
@ -116,7 +117,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 +186,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,6 +230,8 @@ 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 {
|
||||||
|
// 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{
|
ht.stats.HandleRPC(s.Context(), &stats.OutTrailer{
|
||||||
Trailer: s.trailer.Copy(),
|
Trailer: s.trailer.Copy(),
|
||||||
})
|
})
|
||||||
|
@ -236,14 +241,16 @@ func (ht *serverHandlerTransport) WriteStatus(s *Stream, st *status.Status) erro
|
||||||
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)
|
||||||
|
@ -262,9 +269,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()
|
||||||
|
@ -272,27 +300,27 @@ 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 {
|
||||||
|
// 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{
|
ht.stats.HandleRPC(s.Context(), &stats.OutHeader{
|
||||||
Header: md.Copy(),
|
Header: md.Copy(),
|
||||||
|
Compression: s.sendCompress,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -338,7 +366,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, CommonAuthInfo: credentials.CommonAuthInfo{credentials.PrivacyAndIntegrity}}
|
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)
|
||||||
|
|
|
@ -32,15 +32,18 @@ import (
|
||||||
|
|
||||||
"golang.org/x/net/http2"
|
"golang.org/x/net/http2"
|
||||||
"golang.org/x/net/http2/hpack"
|
"golang.org/x/net/http2/hpack"
|
||||||
|
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/credentials"
|
"google.golang.org/grpc/credentials"
|
||||||
"google.golang.org/grpc/internal"
|
|
||||||
"google.golang.org/grpc/internal/channelz"
|
"google.golang.org/grpc/internal/channelz"
|
||||||
|
icredentials "google.golang.org/grpc/internal/credentials"
|
||||||
|
"google.golang.org/grpc/internal/grpcutil"
|
||||||
|
imetadata "google.golang.org/grpc/internal/metadata"
|
||||||
"google.golang.org/grpc/internal/syscall"
|
"google.golang.org/grpc/internal/syscall"
|
||||||
|
"google.golang.org/grpc/internal/transport/networktype"
|
||||||
"google.golang.org/grpc/keepalive"
|
"google.golang.org/grpc/keepalive"
|
||||||
"google.golang.org/grpc/metadata"
|
"google.golang.org/grpc/metadata"
|
||||||
"google.golang.org/grpc/peer"
|
"google.golang.org/grpc/peer"
|
||||||
|
"google.golang.org/grpc/resolver"
|
||||||
"google.golang.org/grpc/stats"
|
"google.golang.org/grpc/stats"
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
)
|
)
|
||||||
|
@ -57,7 +60,7 @@ type http2Client struct {
|
||||||
cancel context.CancelFunc
|
cancel context.CancelFunc
|
||||||
ctxDone <-chan struct{} // Cache the ctx.Done() chan.
|
ctxDone <-chan struct{} // Cache the ctx.Done() chan.
|
||||||
userAgent string
|
userAgent string
|
||||||
md interface{}
|
md metadata.MD
|
||||||
conn net.Conn // underlying communication channel
|
conn net.Conn // underlying communication channel
|
||||||
loopy *loopyWriter
|
loopy *loopyWriter
|
||||||
remoteAddr net.Addr
|
remoteAddr net.Addr
|
||||||
|
@ -112,6 +115,9 @@ type http2Client struct {
|
||||||
// goAwayReason records the http2.ErrCode and debug data received with the
|
// goAwayReason records the http2.ErrCode and debug data received with the
|
||||||
// GoAway frame.
|
// GoAway frame.
|
||||||
goAwayReason GoAwayReason
|
goAwayReason GoAwayReason
|
||||||
|
// goAwayDebugMessage contains a detailed human readable string about a
|
||||||
|
// GoAway frame, useful for error messages.
|
||||||
|
goAwayDebugMessage string
|
||||||
// A condition variable used to signal when the keepalive goroutine should
|
// A condition variable used to signal when the keepalive goroutine should
|
||||||
// go dormant. The condition for dormancy is based on the number of active
|
// go dormant. The condition for dormancy is based on the number of active
|
||||||
// streams and the `PermitWithoutStream` keepalive client parameter. And
|
// streams and the `PermitWithoutStream` keepalive client parameter. And
|
||||||
|
@ -135,11 +141,27 @@ type http2Client struct {
|
||||||
connectionID uint64
|
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 resolver.Address, useProxy bool, grpcUA string) (net.Conn, error) {
|
||||||
|
address := addr.Addr
|
||||||
|
networkType, ok := networktype.Get(addr)
|
||||||
if fn != nil {
|
if fn != nil {
|
||||||
return fn(ctx, addr)
|
if networkType == "unix" && !strings.HasPrefix(address, "\x00") {
|
||||||
|
// For backward compatibility, if the user dialed "unix:///path",
|
||||||
|
// the passthrough resolver would be used and the user's custom
|
||||||
|
// dialer would see "unix:///path". Since the unix resolver is used
|
||||||
|
// and the address is now "/path", prepend "unix://" so the user's
|
||||||
|
// custom dialer sees the same address.
|
||||||
|
return fn(ctx, "unix://"+address)
|
||||||
}
|
}
|
||||||
return (&net.Dialer{}).DialContext(ctx, "tcp", addr)
|
return fn(ctx, address)
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
networkType, address = parseDialTarget(address)
|
||||||
|
}
|
||||||
|
if networkType == "tcp" && useProxy {
|
||||||
|
return proxyDial(ctx, address, grpcUA)
|
||||||
|
}
|
||||||
|
return (&net.Dialer{}).DialContext(ctx, networkType, address)
|
||||||
}
|
}
|
||||||
|
|
||||||
func isTemporary(err error) bool {
|
func isTemporary(err error) bool {
|
||||||
|
@ -161,7 +183,7 @@ func isTemporary(err error) bool {
|
||||||
// newHTTP2Client constructs a connected ClientTransport to addr based on HTTP2
|
// newHTTP2Client constructs a connected ClientTransport to addr based on HTTP2
|
||||||
// and starts to receive messages on it. Non-nil error returns if construction
|
// and starts to receive messages on it. Non-nil error returns if construction
|
||||||
// fails.
|
// fails.
|
||||||
func newHTTP2Client(connectCtx, ctx context.Context, addr TargetInfo, opts ConnectOptions, onPrefaceReceipt func(), onGoAway func(GoAwayReason), onClose func()) (_ *http2Client, err error) {
|
func newHTTP2Client(connectCtx, ctx context.Context, addr resolver.Address, opts ConnectOptions, onPrefaceReceipt func(), onGoAway func(GoAwayReason), onClose func()) (_ *http2Client, err error) {
|
||||||
scheme := "http"
|
scheme := "http"
|
||||||
ctx, cancel := context.WithCancel(ctx)
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
defer func() {
|
defer func() {
|
||||||
|
@ -170,7 +192,7 @@ func newHTTP2Client(connectCtx, ctx context.Context, addr TargetInfo, opts Conne
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
conn, err := dial(connectCtx, opts.Dialer, addr.Addr)
|
conn, err := dial(connectCtx, opts.Dialer, addr, opts.UseProxy, opts.UserAgent)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if opts.FailOnNonTempDialError {
|
if opts.FailOnNonTempDialError {
|
||||||
return nil, connectionErrorf(isTemporary(err), err, "transport: error while dialing: %v", err)
|
return nil, connectionErrorf(isTemporary(err), err, "transport: error while dialing: %v", err)
|
||||||
|
@ -214,12 +236,31 @@ func newHTTP2Client(connectCtx, ctx context.Context, addr TargetInfo, opts Conne
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if transportCreds != nil {
|
if transportCreds != nil {
|
||||||
scheme = "https"
|
// gRPC, resolver, balancer etc. can specify arbitrary data in the
|
||||||
conn, authInfo, err = transportCreds.ClientHandshake(connectCtx, addr.Authority, conn)
|
// Attributes field of resolver.Address, which is shoved into connectCtx
|
||||||
|
// and passed to the credential handshaker. This makes it possible for
|
||||||
|
// address specific arbitrary data to reach the credential handshaker.
|
||||||
|
connectCtx = icredentials.NewClientHandshakeInfoContext(connectCtx, credentials.ClientHandshakeInfo{Attributes: addr.Attributes})
|
||||||
|
conn, authInfo, err = transportCreds.ClientHandshake(connectCtx, addr.ServerName, conn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, connectionErrorf(isTemporary(err), err, "transport: authentication handshake failed: %v", err)
|
return nil, connectionErrorf(isTemporary(err), err, "transport: authentication handshake failed: %v", err)
|
||||||
}
|
}
|
||||||
|
for _, cd := range perRPCCreds {
|
||||||
|
if cd.RequireTransportSecurity() {
|
||||||
|
if ci, ok := authInfo.(interface {
|
||||||
|
GetCommonAuthInfo() credentials.CommonAuthInfo
|
||||||
|
}); ok {
|
||||||
|
secLevel := ci.GetCommonAuthInfo().SecurityLevel
|
||||||
|
if secLevel != credentials.InvalidSecurityLevel && secLevel < credentials.PrivacyAndIntegrity {
|
||||||
|
return nil, connectionErrorf(true, nil, "transport: cannot send secure credentials on an insecure connection")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
isSecure = true
|
isSecure = true
|
||||||
|
if transportCreds.Info().SecurityProtocol == "tls" {
|
||||||
|
scheme = "https"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
dynamicWindow := true
|
dynamicWindow := true
|
||||||
icwz := int32(initialWindowSize)
|
icwz := int32(initialWindowSize)
|
||||||
|
@ -238,7 +279,6 @@ func newHTTP2Client(connectCtx, ctx context.Context, addr TargetInfo, opts Conne
|
||||||
ctxDone: ctx.Done(), // Cache Done chan.
|
ctxDone: ctx.Done(), // Cache Done chan.
|
||||||
cancel: cancel,
|
cancel: cancel,
|
||||||
userAgent: opts.UserAgent,
|
userAgent: opts.UserAgent,
|
||||||
md: addr.Metadata,
|
|
||||||
conn: conn,
|
conn: conn,
|
||||||
remoteAddr: conn.RemoteAddr(),
|
remoteAddr: conn.RemoteAddr(),
|
||||||
localAddr: conn.LocalAddr(),
|
localAddr: conn.LocalAddr(),
|
||||||
|
@ -266,6 +306,12 @@ func newHTTP2Client(connectCtx, ctx context.Context, addr TargetInfo, opts Conne
|
||||||
keepaliveEnabled: keepaliveEnabled,
|
keepaliveEnabled: keepaliveEnabled,
|
||||||
bufferPool: newBufferPool(),
|
bufferPool: newBufferPool(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if md, ok := addr.Metadata.(*metadata.MD); ok {
|
||||||
|
t.md = *md
|
||||||
|
} else if md := imetadata.Get(addr); md != nil {
|
||||||
|
t.md = md
|
||||||
|
}
|
||||||
t.controlBuf = newControlBuffer(t.ctxDone)
|
t.controlBuf = newControlBuffer(t.ctxDone)
|
||||||
if opts.InitialWindowSize >= defaultWindowSize {
|
if opts.InitialWindowSize >= defaultWindowSize {
|
||||||
t.initialWindowSize = opts.InitialWindowSize
|
t.initialWindowSize = opts.InitialWindowSize
|
||||||
|
@ -302,12 +348,14 @@ func newHTTP2Client(connectCtx, ctx context.Context, addr TargetInfo, opts Conne
|
||||||
// Send connection preface to server.
|
// Send connection preface to server.
|
||||||
n, err := t.conn.Write(clientPreface)
|
n, err := t.conn.Write(clientPreface)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Close()
|
err = connectionErrorf(true, err, "transport: failed to write client preface: %v", err)
|
||||||
return nil, connectionErrorf(true, err, "transport: failed to write client preface: %v", err)
|
t.Close(err)
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
if n != len(clientPreface) {
|
if n != len(clientPreface) {
|
||||||
t.Close()
|
err = connectionErrorf(true, nil, "transport: preface mismatch, wrote %d bytes; want %d", n, len(clientPreface))
|
||||||
return nil, connectionErrorf(true, err, "transport: preface mismatch, wrote %d bytes; want %d", n, len(clientPreface))
|
t.Close(err)
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
var ss []http2.Setting
|
var ss []http2.Setting
|
||||||
|
|
||||||
|
@ -325,14 +373,16 @@ func newHTTP2Client(connectCtx, ctx context.Context, addr TargetInfo, opts Conne
|
||||||
}
|
}
|
||||||
err = t.framer.fr.WriteSettings(ss...)
|
err = t.framer.fr.WriteSettings(ss...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Close()
|
err = connectionErrorf(true, err, "transport: failed to write initial settings frame: %v", err)
|
||||||
return nil, connectionErrorf(true, err, "transport: failed to write initial settings frame: %v", err)
|
t.Close(err)
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
// Adjust the connection flow control window if needed.
|
// Adjust the connection flow control window if needed.
|
||||||
if delta := uint32(icwz - defaultWindowSize); delta > 0 {
|
if delta := uint32(icwz - defaultWindowSize); delta > 0 {
|
||||||
if err := t.framer.fr.WriteWindowUpdate(0, delta); err != nil {
|
if err := t.framer.fr.WriteWindowUpdate(0, delta); err != nil {
|
||||||
t.Close()
|
err = connectionErrorf(true, err, "transport: failed to write window update: %v", err)
|
||||||
return nil, connectionErrorf(true, err, "transport: failed to write window update: %v", err)
|
t.Close(err)
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -345,7 +395,9 @@ func newHTTP2Client(connectCtx, ctx context.Context, addr TargetInfo, opts Conne
|
||||||
t.loopy = newLoopyWriter(clientSide, t.framer, t.controlBuf, t.bdpEst)
|
t.loopy = newLoopyWriter(clientSide, t.framer, t.controlBuf, t.bdpEst)
|
||||||
err := t.loopy.run()
|
err := t.loopy.run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorf("transport: loopyWriter.run returning. Err: %v", err)
|
if logger.V(logLevel) {
|
||||||
|
logger.Errorf("transport: loopyWriter.run returning. Err: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// If it's a connection error, let reader goroutine handle it
|
// If it's a connection error, let reader goroutine handle it
|
||||||
// since there might be data in the buffers.
|
// since there might be data in the buffers.
|
||||||
|
@ -367,6 +419,7 @@ func (t *http2Client) newStream(ctx context.Context, callHdr *CallHdr) *Stream {
|
||||||
buf: newRecvBuffer(),
|
buf: newRecvBuffer(),
|
||||||
headerChan: make(chan struct{}),
|
headerChan: make(chan struct{}),
|
||||||
contentSubtype: callHdr.ContentSubtype,
|
contentSubtype: callHdr.ContentSubtype,
|
||||||
|
doneFunc: callHdr.DoneFunc,
|
||||||
}
|
}
|
||||||
s.wq = newWriteQuota(defaultWriteQuota, s.done)
|
s.wq = newWriteQuota(defaultWriteQuota, s.done)
|
||||||
s.requestRead = func(n int) {
|
s.requestRead = func(n int) {
|
||||||
|
@ -406,7 +459,7 @@ func (t *http2Client) createHeaderFields(ctx context.Context, callHdr *CallHdr)
|
||||||
Method: callHdr.Method,
|
Method: callHdr.Method,
|
||||||
AuthInfo: t.authInfo,
|
AuthInfo: t.authInfo,
|
||||||
}
|
}
|
||||||
ctxWithRequestInfo := internal.NewRequestInfoContext.(func(context.Context, credentials.RequestInfo) context.Context)(ctx, ri)
|
ctxWithRequestInfo := icredentials.NewRequestInfoContext(ctx, ri)
|
||||||
authData, err := t.getTrAuthData(ctxWithRequestInfo, aud)
|
authData, err := t.getTrAuthData(ctxWithRequestInfo, aud)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -425,7 +478,7 @@ func (t *http2Client) createHeaderFields(ctx context.Context, callHdr *CallHdr)
|
||||||
headerFields = append(headerFields, hpack.HeaderField{Name: ":scheme", Value: t.scheme})
|
headerFields = append(headerFields, hpack.HeaderField{Name: ":scheme", Value: t.scheme})
|
||||||
headerFields = append(headerFields, hpack.HeaderField{Name: ":path", Value: callHdr.Method})
|
headerFields = append(headerFields, hpack.HeaderField{Name: ":path", Value: callHdr.Method})
|
||||||
headerFields = append(headerFields, hpack.HeaderField{Name: ":authority", Value: callHdr.Host})
|
headerFields = append(headerFields, hpack.HeaderField{Name: ":authority", Value: callHdr.Host})
|
||||||
headerFields = append(headerFields, hpack.HeaderField{Name: "content-type", Value: contentType(callHdr.ContentSubtype)})
|
headerFields = append(headerFields, hpack.HeaderField{Name: "content-type", Value: grpcutil.ContentType(callHdr.ContentSubtype)})
|
||||||
headerFields = append(headerFields, hpack.HeaderField{Name: "user-agent", Value: t.userAgent})
|
headerFields = append(headerFields, hpack.HeaderField{Name: "user-agent", Value: t.userAgent})
|
||||||
headerFields = append(headerFields, hpack.HeaderField{Name: "te", Value: "trailers"})
|
headerFields = append(headerFields, hpack.HeaderField{Name: "te", Value: "trailers"})
|
||||||
if callHdr.PreviousAttempts > 0 {
|
if callHdr.PreviousAttempts > 0 {
|
||||||
|
@ -440,7 +493,7 @@ func (t *http2Client) createHeaderFields(ctx context.Context, callHdr *CallHdr)
|
||||||
// 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.
|
||||||
// TODO(mmukhi): Perhaps this field should be updated when actually writing out to the wire.
|
// TODO(mmukhi): Perhaps this field should be updated when actually writing out to the wire.
|
||||||
timeout := time.Until(dl)
|
timeout := time.Until(dl)
|
||||||
headerFields = append(headerFields, hpack.HeaderField{Name: "grpc-timeout", Value: encodeTimeout(timeout)})
|
headerFields = append(headerFields, hpack.HeaderField{Name: "grpc-timeout", Value: grpcutil.EncodeDuration(timeout)})
|
||||||
}
|
}
|
||||||
for k, v := range authData {
|
for k, v := range authData {
|
||||||
headerFields = append(headerFields, hpack.HeaderField{Name: k, Value: encodeMetadataHeader(k, v)})
|
headerFields = append(headerFields, hpack.HeaderField{Name: k, Value: encodeMetadataHeader(k, v)})
|
||||||
|
@ -469,19 +522,18 @@ func (t *http2Client) createHeaderFields(ctx context.Context, callHdr *CallHdr)
|
||||||
for _, vv := range added {
|
for _, vv := range added {
|
||||||
for i, v := range vv {
|
for i, v := range vv {
|
||||||
if i%2 == 0 {
|
if i%2 == 0 {
|
||||||
k = v
|
k = strings.ToLower(v)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// HTTP doesn't allow you to set pseudoheaders after non pseudoheaders were set.
|
// HTTP doesn't allow you to set pseudoheaders after non pseudoheaders were set.
|
||||||
if isReservedHeader(k) {
|
if isReservedHeader(k) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
headerFields = append(headerFields, hpack.HeaderField{Name: strings.ToLower(k), Value: encodeMetadataHeader(k, v)})
|
headerFields = append(headerFields, hpack.HeaderField{Name: k, Value: encodeMetadataHeader(k, v)})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if md, ok := t.md.(*metadata.MD); ok {
|
for k, vv := range t.md {
|
||||||
for k, vv := range *md {
|
|
||||||
if isReservedHeader(k) {
|
if isReservedHeader(k) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -489,7 +541,6 @@ func (t *http2Client) createHeaderFields(ctx context.Context, callHdr *CallHdr)
|
||||||
headerFields = append(headerFields, hpack.HeaderField{Name: k, Value: encodeMetadataHeader(k, v)})
|
headerFields = append(headerFields, hpack.HeaderField{Name: k, Value: encodeMetadataHeader(k, v)})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return headerFields, nil
|
return headerFields, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -537,9 +588,12 @@ func (t *http2Client) getCallAuthData(ctx context.Context, audience string, call
|
||||||
// Note: if these credentials are provided both via dial options and call
|
// Note: if these credentials are provided both via dial options and call
|
||||||
// options, then both sets of credentials will be applied.
|
// options, then both sets of credentials will be applied.
|
||||||
if callCreds := callHdr.Creds; callCreds != nil {
|
if callCreds := callHdr.Creds; callCreds != nil {
|
||||||
if !t.isSecure && callCreds.RequireTransportSecurity() {
|
if callCreds.RequireTransportSecurity() {
|
||||||
|
ri, _ := credentials.RequestInfoFromContext(ctx)
|
||||||
|
if !t.isSecure || credentials.CheckSecurityLevel(ri.AuthInfo, credentials.PrivacyAndIntegrity) != nil {
|
||||||
return nil, status.Error(codes.Unauthenticated, "transport: cannot send secure credentials on an insecure connection")
|
return nil, status.Error(codes.Unauthenticated, "transport: cannot send secure credentials on an insecure connection")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
data, err := callCreds.GetRequestMetadata(ctx, audience)
|
data, err := callCreds.GetRequestMetadata(ctx, audience)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, status.Errorf(codes.Internal, "transport: %v", err)
|
return nil, status.Errorf(codes.Internal, "transport: %v", err)
|
||||||
|
@ -554,13 +608,26 @@ func (t *http2Client) getCallAuthData(ctx context.Context, audience string, call
|
||||||
return callAuthData, nil
|
return callAuthData, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PerformedIOError wraps an error to indicate IO may have been performed
|
||||||
|
// before the error occurred.
|
||||||
|
type PerformedIOError struct {
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error implements error.
|
||||||
|
func (p PerformedIOError) Error() string {
|
||||||
|
return p.Err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
// NewStream creates a stream and registers it into the transport as "active"
|
// NewStream creates a stream and registers it into the transport as "active"
|
||||||
// streams.
|
// streams.
|
||||||
func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (_ *Stream, err error) {
|
func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (_ *Stream, err error) {
|
||||||
ctx = peer.NewContext(ctx, t.getPeer())
|
ctx = peer.NewContext(ctx, t.getPeer())
|
||||||
headerFields, err := t.createHeaderFields(ctx, callHdr)
|
headerFields, err := t.createHeaderFields(ctx, callHdr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
// We may have performed I/O in the per-RPC creds callback, so do not
|
||||||
|
// allow transparent retry.
|
||||||
|
return nil, PerformedIOError{err}
|
||||||
}
|
}
|
||||||
s := t.newStream(ctx, callHdr)
|
s := t.newStream(ctx, callHdr)
|
||||||
cleanup := func(err error) {
|
cleanup := func(err error) {
|
||||||
|
@ -680,14 +747,21 @@ func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (_ *Strea
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if t.statsHandler != nil {
|
if t.statsHandler != nil {
|
||||||
header, _, _ := metadata.FromOutgoingContextRaw(ctx)
|
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.Copy(),
|
Header: header,
|
||||||
}
|
}
|
||||||
t.statsHandler.HandleRPC(s.ctx, outHeader)
|
t.statsHandler.HandleRPC(s.ctx, outHeader)
|
||||||
}
|
}
|
||||||
|
@ -764,6 +838,9 @@ func (t *http2Client) closeStream(s *Stream, err error, rst bool, rstCode http2.
|
||||||
t.controlBuf.executeAndPut(addBackStreamQuota, cleanup)
|
t.controlBuf.executeAndPut(addBackStreamQuota, cleanup)
|
||||||
// This will unblock write.
|
// This will unblock write.
|
||||||
close(s.done)
|
close(s.done)
|
||||||
|
if s.doneFunc != nil {
|
||||||
|
s.doneFunc()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close kicks off the shutdown process of the transport. This should be called
|
// Close kicks off the shutdown process of the transport. This should be called
|
||||||
|
@ -773,12 +850,12 @@ func (t *http2Client) closeStream(s *Stream, err error, rst bool, rstCode http2.
|
||||||
// This method blocks until the addrConn that initiated this transport is
|
// This method blocks until the addrConn that initiated this transport is
|
||||||
// re-connected. This happens because t.onClose() begins reconnect logic at the
|
// re-connected. This happens because t.onClose() begins reconnect logic at the
|
||||||
// addrConn level and blocks until the addrConn is successfully connected.
|
// addrConn level and blocks until the addrConn is successfully connected.
|
||||||
func (t *http2Client) Close() error {
|
func (t *http2Client) Close(err error) {
|
||||||
t.mu.Lock()
|
t.mu.Lock()
|
||||||
// Make sure we only Close once.
|
// Make sure we only Close once.
|
||||||
if t.state == closing {
|
if t.state == closing {
|
||||||
t.mu.Unlock()
|
t.mu.Unlock()
|
||||||
return nil
|
return
|
||||||
}
|
}
|
||||||
// Call t.onClose before setting the state to closing to prevent the client
|
// Call t.onClose before setting the state to closing to prevent the client
|
||||||
// from attempting to create new streams ASAP.
|
// from attempting to create new streams ASAP.
|
||||||
|
@ -794,13 +871,19 @@ func (t *http2Client) Close() error {
|
||||||
t.mu.Unlock()
|
t.mu.Unlock()
|
||||||
t.controlBuf.finish()
|
t.controlBuf.finish()
|
||||||
t.cancel()
|
t.cancel()
|
||||||
err := t.conn.Close()
|
t.conn.Close()
|
||||||
if channelz.IsOn() {
|
if channelz.IsOn() {
|
||||||
channelz.RemoveEntry(t.channelzID)
|
channelz.RemoveEntry(t.channelzID)
|
||||||
}
|
}
|
||||||
|
// Append info about previous goaways if there were any, since this may be important
|
||||||
|
// for understanding the root cause for this connection to be closed.
|
||||||
|
_, goAwayDebugMessage := t.GetGoAwayReason()
|
||||||
|
if len(goAwayDebugMessage) > 0 {
|
||||||
|
err = fmt.Errorf("closing transport due to: %v, received prior goaway: %v", err, goAwayDebugMessage)
|
||||||
|
}
|
||||||
// Notify all active streams.
|
// Notify all active streams.
|
||||||
for _, s := range streams {
|
for _, s := range streams {
|
||||||
t.closeStream(s, ErrConnClosing, false, http2.ErrCodeNo, status.New(codes.Unavailable, ErrConnClosing.Desc), nil, false)
|
t.closeStream(s, err, false, http2.ErrCodeNo, status.New(codes.Unavailable, err.Error()), nil, false)
|
||||||
}
|
}
|
||||||
if t.statsHandler != nil {
|
if t.statsHandler != nil {
|
||||||
connEnd := &stats.ConnEnd{
|
connEnd := &stats.ConnEnd{
|
||||||
|
@ -808,7 +891,6 @@ func (t *http2Client) Close() error {
|
||||||
}
|
}
|
||||||
t.statsHandler.HandleConn(t.ctx, connEnd)
|
t.statsHandler.HandleConn(t.ctx, connEnd)
|
||||||
}
|
}
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GracefulClose sets the state to draining, which prevents new streams from
|
// GracefulClose sets the state to draining, which prevents new streams from
|
||||||
|
@ -827,7 +909,7 @@ func (t *http2Client) GracefulClose() {
|
||||||
active := len(t.activeStreams)
|
active := len(t.activeStreams)
|
||||||
t.mu.Unlock()
|
t.mu.Unlock()
|
||||||
if active == 0 {
|
if active == 0 {
|
||||||
t.Close()
|
t.Close(ErrConnClosing)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
t.controlBuf.put(&incomingGoAway{})
|
t.controlBuf.put(&incomingGoAway{})
|
||||||
|
@ -847,18 +929,10 @@ func (t *http2Client) Write(s *Stream, hdr []byte, data []byte, opts *Options) e
|
||||||
df := &dataFrame{
|
df := &dataFrame{
|
||||||
streamID: s.id,
|
streamID: s.id,
|
||||||
endStream: opts.Last,
|
endStream: opts.Last,
|
||||||
|
h: hdr,
|
||||||
|
d: data,
|
||||||
}
|
}
|
||||||
if hdr != nil || data != nil { // If it's not an empty data frame.
|
if hdr != nil || data != nil { // If it's not an empty data frame, check quota.
|
||||||
// Add some data to grpc message header so that we can equally
|
|
||||||
// distribute bytes across frames.
|
|
||||||
emptyLen := http2MaxFrameLen - len(hdr)
|
|
||||||
if emptyLen > len(data) {
|
|
||||||
emptyLen = len(data)
|
|
||||||
}
|
|
||||||
hdr = append(hdr, data[:emptyLen]...)
|
|
||||||
data = data[emptyLen:]
|
|
||||||
df.h, df.d = hdr, data
|
|
||||||
// TODO(mmukhi): The above logic in this if can be moved to loopyWriter's data handler.
|
|
||||||
if err := s.wq.get(int32(len(hdr) + len(data))); err != nil {
|
if err := s.wq.get(int32(len(hdr) + len(data))); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -992,7 +1066,9 @@ func (t *http2Client) handleRSTStream(f *http2.RSTStreamFrame) {
|
||||||
}
|
}
|
||||||
statusCode, ok := http2ErrConvTab[f.ErrCode]
|
statusCode, ok := http2ErrConvTab[f.ErrCode]
|
||||||
if !ok {
|
if !ok {
|
||||||
warningf("transport: http2Client.handleRSTStream found no mapped gRPC status for the received http2 error %v", f.ErrCode)
|
if logger.V(logLevel) {
|
||||||
|
logger.Warningf("transport: http2Client.handleRSTStream found no mapped gRPC status for the received http2 error %v", f.ErrCode)
|
||||||
|
}
|
||||||
statusCode = codes.Unknown
|
statusCode = codes.Unknown
|
||||||
}
|
}
|
||||||
if statusCode == codes.Canceled {
|
if statusCode == codes.Canceled {
|
||||||
|
@ -1074,12 +1150,14 @@ func (t *http2Client) handleGoAway(f *http2.GoAwayFrame) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if f.ErrCode == http2.ErrCodeEnhanceYourCalm {
|
if f.ErrCode == http2.ErrCodeEnhanceYourCalm {
|
||||||
infof("Client received GoAway with http2.ErrCodeEnhanceYourCalm.")
|
if logger.V(logLevel) {
|
||||||
|
logger.Infof("Client received GoAway with http2.ErrCodeEnhanceYourCalm.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
id := f.LastStreamID
|
id := f.LastStreamID
|
||||||
if id > 0 && id%2 != 1 {
|
if id > 0 && id%2 == 0 {
|
||||||
t.mu.Unlock()
|
t.mu.Unlock()
|
||||||
t.Close()
|
t.Close(connectionErrorf(true, nil, "received goaway with non-zero even-numbered numbered stream id: %v", id))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// A client can receive multiple GoAways from the server (see
|
// A client can receive multiple GoAways from the server (see
|
||||||
|
@ -1097,7 +1175,7 @@ func (t *http2Client) handleGoAway(f *http2.GoAwayFrame) {
|
||||||
// If there are multiple GoAways the first one should always have an ID greater than the following ones.
|
// If there are multiple GoAways the first one should always have an ID greater than the following ones.
|
||||||
if id > t.prevGoAwayID {
|
if id > t.prevGoAwayID {
|
||||||
t.mu.Unlock()
|
t.mu.Unlock()
|
||||||
t.Close()
|
t.Close(connectionErrorf(true, nil, "received goaway with stream id: %v, which exceeds stream id of previous goaway: %v", id, t.prevGoAwayID))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
@ -1127,7 +1205,7 @@ func (t *http2Client) handleGoAway(f *http2.GoAwayFrame) {
|
||||||
active := len(t.activeStreams)
|
active := len(t.activeStreams)
|
||||||
t.mu.Unlock()
|
t.mu.Unlock()
|
||||||
if active == 0 {
|
if active == 0 {
|
||||||
t.Close()
|
t.Close(connectionErrorf(true, nil, "received goaway and there are no active streams"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1143,12 +1221,13 @@ func (t *http2Client) setGoAwayReason(f *http2.GoAwayFrame) {
|
||||||
t.goAwayReason = GoAwayTooManyPings
|
t.goAwayReason = GoAwayTooManyPings
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
t.goAwayDebugMessage = fmt.Sprintf("code: %s, debug data: %v", f.ErrCode, string(f.DebugData()))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *http2Client) GetGoAwayReason() GoAwayReason {
|
func (t *http2Client) GetGoAwayReason() (GoAwayReason, string) {
|
||||||
t.mu.Lock()
|
t.mu.Lock()
|
||||||
defer t.mu.Unlock()
|
defer t.mu.Unlock()
|
||||||
return t.goAwayReason
|
return t.goAwayReason, t.goAwayDebugMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *http2Client) handleWindowUpdate(f *http2.WindowUpdateFrame) {
|
func (t *http2Client) handleWindowUpdate(f *http2.WindowUpdateFrame) {
|
||||||
|
@ -1178,8 +1257,8 @@ func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) {
|
||||||
state := &decodeState{}
|
state := &decodeState{}
|
||||||
// Initialize isGRPC value to be !initialHeader, since if a gRPC Response-Headers has already been received, then it means that the peer is speaking gRPC and we are in gRPC mode.
|
// Initialize isGRPC value to be !initialHeader, since if a gRPC Response-Headers has already been received, then it means that the peer is speaking gRPC and we are in gRPC mode.
|
||||||
state.data.isGRPC = !initialHeader
|
state.data.isGRPC = !initialHeader
|
||||||
if err := state.decodeHeader(frame); err != nil {
|
if h2code, err := state.decodeHeader(frame); err != nil {
|
||||||
t.closeStream(s, err, true, http2.ErrCodeProtocol, status.Convert(err), nil, endStream)
|
t.closeStream(s, err, true, h2code, status.Convert(err), nil, endStream)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1191,6 +1270,7 @@ func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) {
|
||||||
Client: true,
|
Client: true,
|
||||||
WireLength: int(frame.Header().Length),
|
WireLength: int(frame.Header().Length),
|
||||||
Header: s.header.Copy(),
|
Header: s.header.Copy(),
|
||||||
|
Compression: s.recvCompress,
|
||||||
}
|
}
|
||||||
t.statsHandler.HandleRPC(s.ctx, inHeader)
|
t.statsHandler.HandleRPC(s.ctx, inHeader)
|
||||||
} else {
|
} else {
|
||||||
|
@ -1244,7 +1324,8 @@ func (t *http2Client) reader() {
|
||||||
// Check the validity of server preface.
|
// Check the validity of server preface.
|
||||||
frame, err := t.framer.fr.ReadFrame()
|
frame, err := t.framer.fr.ReadFrame()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Close() // this kicks off resetTransport, so must be last before return
|
err = connectionErrorf(true, err, "error reading server preface: %v", err)
|
||||||
|
t.Close(err) // this kicks off resetTransport, so must be last before return
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
t.conn.SetReadDeadline(time.Time{}) // reset deadline once we get the settings frame (we didn't time out, yay!)
|
t.conn.SetReadDeadline(time.Time{}) // reset deadline once we get the settings frame (we didn't time out, yay!)
|
||||||
|
@ -1253,7 +1334,8 @@ func (t *http2Client) reader() {
|
||||||
}
|
}
|
||||||
sf, ok := frame.(*http2.SettingsFrame)
|
sf, ok := frame.(*http2.SettingsFrame)
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Close() // this kicks off resetTransport, so must be last before return
|
// this kicks off resetTransport, so must be last before return
|
||||||
|
t.Close(connectionErrorf(true, nil, "initial http2 frame from server is not a settings frame: %T", frame))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
t.onPrefaceReceipt()
|
t.onPrefaceReceipt()
|
||||||
|
@ -1277,13 +1359,19 @@ func (t *http2Client) reader() {
|
||||||
if s != nil {
|
if s != nil {
|
||||||
// use error detail to provide better err message
|
// use error detail to provide better err message
|
||||||
code := http2ErrConvTab[se.Code]
|
code := http2ErrConvTab[se.Code]
|
||||||
msg := t.framer.fr.ErrorDetail().Error()
|
errorDetail := t.framer.fr.ErrorDetail()
|
||||||
|
var msg string
|
||||||
|
if errorDetail != nil {
|
||||||
|
msg = errorDetail.Error()
|
||||||
|
} else {
|
||||||
|
msg = "received invalid frame"
|
||||||
|
}
|
||||||
t.closeStream(s, status.Error(code, msg), true, http2.ErrCodeProtocol, status.New(code, msg), nil, false)
|
t.closeStream(s, status.Error(code, msg), true, http2.ErrCodeProtocol, status.New(code, msg), nil, false)
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
} else {
|
} else {
|
||||||
// Transport error.
|
// Transport error.
|
||||||
t.Close()
|
t.Close(connectionErrorf(true, err, "error reading from server: %v", err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1303,7 +1391,9 @@ func (t *http2Client) reader() {
|
||||||
case *http2.WindowUpdateFrame:
|
case *http2.WindowUpdateFrame:
|
||||||
t.handleWindowUpdate(frame)
|
t.handleWindowUpdate(frame)
|
||||||
default:
|
default:
|
||||||
errorf("transport: http2Client.reader got unhandled frame type %v.", frame)
|
if logger.V(logLevel) {
|
||||||
|
logger.Errorf("transport: http2Client.reader got unhandled frame type %v.", frame)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1340,7 +1430,7 @@ func (t *http2Client) keepalive() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if outstandingPing && timeoutLeft <= 0 {
|
if outstandingPing && timeoutLeft <= 0 {
|
||||||
t.Close()
|
t.Close(connectionErrorf(true, nil, "keepalive ping failed to receive ACK within timeout"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
t.mu.Lock()
|
t.mu.Lock()
|
||||||
|
|
|
@ -26,6 +26,7 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"math"
|
"math"
|
||||||
"net"
|
"net"
|
||||||
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
@ -34,12 +35,10 @@ import (
|
||||||
"github.com/golang/protobuf/proto"
|
"github.com/golang/protobuf/proto"
|
||||||
"golang.org/x/net/http2"
|
"golang.org/x/net/http2"
|
||||||
"golang.org/x/net/http2/hpack"
|
"golang.org/x/net/http2/hpack"
|
||||||
|
"google.golang.org/grpc/internal/grpcutil"
|
||||||
|
|
||||||
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/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,9 +56,6 @@ 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
|
// serverConnectionCounter counts the number of connections a server has seen
|
||||||
|
@ -294,7 +290,9 @@ func newHTTP2Server(conn net.Conn, config *ServerConfig) (_ ServerTransport, err
|
||||||
t.loopy = newLoopyWriter(serverSide, t.framer, t.controlBuf, t.bdpEst)
|
t.loopy = newLoopyWriter(serverSide, t.framer, t.controlBuf, t.bdpEst)
|
||||||
t.loopy.ssGoAwayHandler = t.outgoingGoAwayHandler
|
t.loopy.ssGoAwayHandler = t.outgoingGoAwayHandler
|
||||||
if err := t.loopy.run(); err != nil {
|
if err := t.loopy.run(); err != nil {
|
||||||
errorf("transport: loopyWriter.run returning. Err: %v", err)
|
if logger.V(logLevel) {
|
||||||
|
logger.Errorf("transport: loopyWriter.run returning. Err: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
t.conn.Close()
|
t.conn.Close()
|
||||||
close(t.writerDone)
|
close(t.writerDone)
|
||||||
|
@ -309,12 +307,12 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(
|
||||||
state := &decodeState{
|
state := &decodeState{
|
||||||
serverSide: true,
|
serverSide: true,
|
||||||
}
|
}
|
||||||
if err := state.decodeHeader(frame); err != nil {
|
if h2code, err := state.decodeHeader(frame); err != nil {
|
||||||
if se, ok := status.FromError(err); ok {
|
if _, ok := status.FromError(err); ok {
|
||||||
t.controlBuf.put(&cleanupStream{
|
t.controlBuf.put(&cleanupStream{
|
||||||
streamID: streamID,
|
streamID: streamID,
|
||||||
rst: true,
|
rst: true,
|
||||||
rstCode: statusCodeConvTab[se.Code()],
|
rstCode: h2code,
|
||||||
onWrite: func() {},
|
onWrite: func() {},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -358,24 +356,6 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(
|
||||||
if state.data.statsTrace != nil {
|
if state.data.statsTrace != nil {
|
||||||
s.ctx = stats.SetIncomingTrace(s.ctx, state.data.statsTrace)
|
s.ctx = stats.SetIncomingTrace(s.ctx, state.data.statsTrace)
|
||||||
}
|
}
|
||||||
if t.inTapHandle != nil {
|
|
||||||
var err error
|
|
||||||
info := &tap.Info{
|
|
||||||
FullMethodName: state.data.method,
|
|
||||||
}
|
|
||||||
s.ctx, err = t.inTapHandle(s.ctx, info)
|
|
||||||
if err != nil {
|
|
||||||
warningf("transport: http2Server.operateHeaders got an error from InTapHandle: %v", err)
|
|
||||||
t.controlBuf.put(&cleanupStream{
|
|
||||||
streamID: s.id,
|
|
||||||
rst: true,
|
|
||||||
rstCode: http2.ErrCodeRefusedStream,
|
|
||||||
onWrite: func() {},
|
|
||||||
})
|
|
||||||
s.cancel()
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
t.mu.Lock()
|
t.mu.Lock()
|
||||||
if t.state != reachable {
|
if t.state != reachable {
|
||||||
t.mu.Unlock()
|
t.mu.Unlock()
|
||||||
|
@ -396,11 +376,46 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(
|
||||||
if streamID%2 != 1 || streamID <= t.maxStreamID {
|
if streamID%2 != 1 || streamID <= t.maxStreamID {
|
||||||
t.mu.Unlock()
|
t.mu.Unlock()
|
||||||
// illegal gRPC stream id.
|
// illegal gRPC stream id.
|
||||||
errorf("transport: http2Server.HandleStreams received an illegal stream id: %v", streamID)
|
if logger.V(logLevel) {
|
||||||
|
logger.Errorf("transport: http2Server.HandleStreams received an illegal stream id: %v", streamID)
|
||||||
|
}
|
||||||
s.cancel()
|
s.cancel()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
t.maxStreamID = streamID
|
t.maxStreamID = streamID
|
||||||
|
if state.data.httpMethod != http.MethodPost {
|
||||||
|
t.mu.Unlock()
|
||||||
|
if logger.V(logLevel) {
|
||||||
|
logger.Warningf("transport: http2Server.operateHeaders parsed a :method field: %v which should be POST", state.data.httpMethod)
|
||||||
|
}
|
||||||
|
t.controlBuf.put(&cleanupStream{
|
||||||
|
streamID: streamID,
|
||||||
|
rst: true,
|
||||||
|
rstCode: http2.ErrCodeProtocol,
|
||||||
|
onWrite: func() {},
|
||||||
|
})
|
||||||
|
s.cancel()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if t.inTapHandle != nil {
|
||||||
|
var err error
|
||||||
|
if s.ctx, err = t.inTapHandle(s.ctx, &tap.Info{FullMethodName: state.data.method}); err != nil {
|
||||||
|
t.mu.Unlock()
|
||||||
|
if logger.V(logLevel) {
|
||||||
|
logger.Infof("transport: http2Server.operateHeaders got an error from InTapHandle: %v", err)
|
||||||
|
}
|
||||||
|
stat, ok := status.FromError(err)
|
||||||
|
if !ok {
|
||||||
|
stat = status.New(codes.PermissionDenied, err.Error())
|
||||||
|
}
|
||||||
|
t.controlBuf.put(&earlyAbortStream{
|
||||||
|
streamID: s.id,
|
||||||
|
contentSubtype: s.contentSubtype,
|
||||||
|
status: stat,
|
||||||
|
})
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
t.activeStreams[streamID] = s
|
t.activeStreams[streamID] = s
|
||||||
if len(t.activeStreams) == 1 {
|
if len(t.activeStreams) == 1 {
|
||||||
t.idle = time.Time{}
|
t.idle = time.Time{}
|
||||||
|
@ -459,7 +474,9 @@ func (t *http2Server) HandleStreams(handle func(*Stream), traceCtx func(context.
|
||||||
atomic.StoreInt64(&t.lastRead, time.Now().UnixNano())
|
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)
|
if logger.V(logLevel) {
|
||||||
|
logger.Warningf("transport: http2Server.HandleStreams encountered http2.StreamError: %v", se)
|
||||||
|
}
|
||||||
t.mu.Lock()
|
t.mu.Lock()
|
||||||
s := t.activeStreams[se.StreamID]
|
s := t.activeStreams[se.StreamID]
|
||||||
t.mu.Unlock()
|
t.mu.Unlock()
|
||||||
|
@ -479,7 +496,9 @@ func (t *http2Server) HandleStreams(handle func(*Stream), traceCtx func(context.
|
||||||
t.Close()
|
t.Close()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
warningf("transport: http2Server.HandleStreams failed to read frame: %v", err)
|
if logger.V(logLevel) {
|
||||||
|
logger.Warningf("transport: http2Server.HandleStreams failed to read frame: %v", err)
|
||||||
|
}
|
||||||
t.Close()
|
t.Close()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -502,7 +521,9 @@ func (t *http2Server) HandleStreams(handle func(*Stream), traceCtx func(context.
|
||||||
case *http2.GoAwayFrame:
|
case *http2.GoAwayFrame:
|
||||||
// TODO: Handle GoAway from the client appropriately.
|
// TODO: Handle GoAway from the client appropriately.
|
||||||
default:
|
default:
|
||||||
errorf("transport: http2Server.HandleStreams found unhandled frame type %v.", frame)
|
if logger.V(logLevel) {
|
||||||
|
logger.Errorf("transport: http2Server.HandleStreams found unhandled frame type %v.", frame)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -604,6 +625,10 @@ func (t *http2Server) handleData(f *http2.DataFrame) {
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if s.getState() == streamReadDone {
|
||||||
|
t.closeStream(s, true, http2.ErrCodeStreamClosed, false)
|
||||||
|
return
|
||||||
|
}
|
||||||
if size > 0 {
|
if size > 0 {
|
||||||
if err := s.fc.onData(size); err != nil {
|
if err := s.fc.onData(size); err != nil {
|
||||||
t.closeStream(s, true, http2.ErrCodeFlowControl, false)
|
t.closeStream(s, true, http2.ErrCodeFlowControl, false)
|
||||||
|
@ -724,7 +749,9 @@ func (t *http2Server) handlePing(f *http2.PingFrame) {
|
||||||
|
|
||||||
if t.pingStrikes > maxPingStrikes {
|
if t.pingStrikes > maxPingStrikes {
|
||||||
// Send goaway and close the connection.
|
// Send goaway and close the connection.
|
||||||
errorf("transport: Got too many pings from the client, closing the connection.")
|
if logger.V(logLevel) {
|
||||||
|
logger.Errorf("transport: Got too many pings from the client, closing the connection.")
|
||||||
|
}
|
||||||
t.controlBuf.put(&goAway{code: http2.ErrCodeEnhanceYourCalm, debugData: []byte("too_many_pings"), closeConn: true})
|
t.controlBuf.put(&goAway{code: http2.ErrCodeEnhanceYourCalm, debugData: []byte("too_many_pings"), closeConn: true})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -757,7 +784,9 @@ func (t *http2Server) checkForHeaderListSize(it interface{}) bool {
|
||||||
var sz int64
|
var sz int64
|
||||||
for _, f := range hdrFrame.hf {
|
for _, f := range hdrFrame.hf {
|
||||||
if sz += int64(f.Size()); sz > int64(*t.maxSendHeaderListSize) {
|
if sz += int64(f.Size()); sz > int64(*t.maxSendHeaderListSize) {
|
||||||
errorf("header list size to send violates the maximum size (%d bytes) set by client", *t.maxSendHeaderListSize)
|
if logger.V(logLevel) {
|
||||||
|
logger.Errorf("header list size to send violates the maximum size (%d bytes) set by client", *t.maxSendHeaderListSize)
|
||||||
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -794,7 +823,7 @@ func (t *http2Server) writeHeaderLocked(s *Stream) error {
|
||||||
// first and create a slice of that exact size.
|
// first and create a slice of that exact size.
|
||||||
headerFields := make([]hpack.HeaderField, 0, 2) // at least :status, content-type will be there if none else.
|
headerFields := make([]hpack.HeaderField, 0, 2) // at least :status, content-type will be there if none else.
|
||||||
headerFields = append(headerFields, hpack.HeaderField{Name: ":status", Value: "200"})
|
headerFields = append(headerFields, hpack.HeaderField{Name: ":status", Value: "200"})
|
||||||
headerFields = append(headerFields, hpack.HeaderField{Name: "content-type", Value: contentType(s.contentSubtype)})
|
headerFields = append(headerFields, hpack.HeaderField{Name: "content-type", Value: grpcutil.ContentType(s.contentSubtype)})
|
||||||
if s.sendCompress != "" {
|
if s.sendCompress != "" {
|
||||||
headerFields = append(headerFields, hpack.HeaderField{Name: "grpc-encoding", Value: s.sendCompress})
|
headerFields = append(headerFields, hpack.HeaderField{Name: "grpc-encoding", Value: s.sendCompress})
|
||||||
}
|
}
|
||||||
|
@ -813,10 +842,11 @@ 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(),
|
Header: s.header.Copy(),
|
||||||
|
Compression: s.sendCompress,
|
||||||
}
|
}
|
||||||
t.stats.HandleRPC(s.Context(), outHeader)
|
t.stats.HandleRPC(s.Context(), outHeader)
|
||||||
}
|
}
|
||||||
|
@ -843,17 +873,17 @@ func (t *http2Server) WriteStatus(s *Stream, st *status.Status) error {
|
||||||
}
|
}
|
||||||
} else { // Send a trailer only response.
|
} else { // Send a trailer only response.
|
||||||
headerFields = append(headerFields, hpack.HeaderField{Name: ":status", Value: "200"})
|
headerFields = append(headerFields, hpack.HeaderField{Name: ":status", Value: "200"})
|
||||||
headerFields = append(headerFields, hpack.HeaderField{Name: "content-type", Value: contentType(s.contentSubtype)})
|
headerFields = append(headerFields, hpack.HeaderField{Name: "content-type", Value: grpcutil.ContentType(s.contentSubtype)})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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.
|
||||||
grpclog.Errorf("transport: failed to marshal rpc status: %v, error: %v", p, err)
|
logger.Errorf("transport: failed to marshal rpc status: %v, error: %v", p, err)
|
||||||
} else {
|
} else {
|
||||||
headerFields = append(headerFields, hpack.HeaderField{Name: "grpc-status-details-bin", Value: encodeBinHeader(stBytes)})
|
headerFields = append(headerFields, hpack.HeaderField{Name: "grpc-status-details-bin", Value: encodeBinHeader(stBytes)})
|
||||||
}
|
}
|
||||||
|
@ -880,6 +910,8 @@ 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 {
|
||||||
|
// 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{
|
t.stats.HandleRPC(s.Context(), &stats.OutTrailer{
|
||||||
Trailer: s.trailer.Copy(),
|
Trailer: s.trailer.Copy(),
|
||||||
})
|
})
|
||||||
|
@ -911,13 +943,6 @@ func (t *http2Server) Write(s *Stream, hdr []byte, data []byte, opts *Options) e
|
||||||
return ContextErr(s.ctx.Err())
|
return ContextErr(s.ctx.Err())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Add some data to header frame so that we can equally distribute bytes across frames.
|
|
||||||
emptyLen := http2MaxFrameLen - len(hdr)
|
|
||||||
if emptyLen > len(data) {
|
|
||||||
emptyLen = len(data)
|
|
||||||
}
|
|
||||||
hdr = append(hdr, data[:emptyLen]...)
|
|
||||||
data = data[emptyLen:]
|
|
||||||
df := &dataFrame{
|
df := &dataFrame{
|
||||||
streamID: s.id,
|
streamID: s.id,
|
||||||
h: hdr,
|
h: hdr,
|
||||||
|
@ -989,7 +1014,9 @@ func (t *http2Server) keepalive() {
|
||||||
select {
|
select {
|
||||||
case <-ageTimer.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.")
|
if logger.V(logLevel) {
|
||||||
|
logger.Infof("transport: closing server transport due to maximum connection age.")
|
||||||
|
}
|
||||||
t.Close()
|
t.Close()
|
||||||
case <-t.done:
|
case <-t.done:
|
||||||
}
|
}
|
||||||
|
@ -1006,7 +1033,9 @@ func (t *http2Server) keepalive() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if outstandingPing && kpTimeoutLeft <= 0 {
|
if outstandingPing && kpTimeoutLeft <= 0 {
|
||||||
infof("transport: closing server transport due to idleness.")
|
if logger.V(logLevel) {
|
||||||
|
logger.Infof("transport: closing server transport due to idleness.")
|
||||||
|
}
|
||||||
t.Close()
|
t.Close()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ import (
|
||||||
"math"
|
"math"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
@ -37,6 +38,8 @@ import (
|
||||||
"golang.org/x/net/http2/hpack"
|
"golang.org/x/net/http2/hpack"
|
||||||
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/grpclog"
|
||||||
|
"google.golang.org/grpc/internal/grpcutil"
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -50,7 +53,7 @@ const (
|
||||||
// "proto" as a suffix after "+" or ";". See
|
// "proto" as a suffix after "+" or ";". See
|
||||||
// https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#requests
|
// https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#requests
|
||||||
// for more details.
|
// for more details.
|
||||||
baseContentType = "application/grpc"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -71,13 +74,6 @@ var (
|
||||||
http2.ErrCodeInadequateSecurity: codes.PermissionDenied,
|
http2.ErrCodeInadequateSecurity: codes.PermissionDenied,
|
||||||
http2.ErrCodeHTTP11Required: codes.Internal,
|
http2.ErrCodeHTTP11Required: codes.Internal,
|
||||||
}
|
}
|
||||||
statusCodeConvTab = map[codes.Code]http2.ErrCode{
|
|
||||||
codes.Internal: http2.ErrCodeInternal,
|
|
||||||
codes.Canceled: http2.ErrCodeCancel,
|
|
||||||
codes.Unavailable: http2.ErrCodeRefusedStream,
|
|
||||||
codes.ResourceExhausted: http2.ErrCodeEnhanceYourCalm,
|
|
||||||
codes.PermissionDenied: http2.ErrCodeInadequateSecurity,
|
|
||||||
}
|
|
||||||
// HTTPStatusConvTab is the HTTP status code to gRPC error code conversion table.
|
// HTTPStatusConvTab is the HTTP status code to gRPC error code conversion table.
|
||||||
HTTPStatusConvTab = map[int]codes.Code{
|
HTTPStatusConvTab = map[int]codes.Code{
|
||||||
// 400 Bad Request - INTERNAL.
|
// 400 Bad Request - INTERNAL.
|
||||||
|
@ -97,6 +93,7 @@ var (
|
||||||
// 504 Gateway timeout - UNAVAILABLE.
|
// 504 Gateway timeout - UNAVAILABLE.
|
||||||
http.StatusGatewayTimeout: codes.Unavailable,
|
http.StatusGatewayTimeout: codes.Unavailable,
|
||||||
}
|
}
|
||||||
|
logger = grpclog.Component("transport")
|
||||||
)
|
)
|
||||||
|
|
||||||
type parsedHeaderData struct {
|
type parsedHeaderData struct {
|
||||||
|
@ -114,6 +111,7 @@ type parsedHeaderData struct {
|
||||||
timeoutSet bool
|
timeoutSet bool
|
||||||
timeout time.Duration
|
timeout time.Duration
|
||||||
method string
|
method string
|
||||||
|
httpMethod string
|
||||||
// key-value metadata map from the peer.
|
// key-value metadata map from the peer.
|
||||||
mdata map[string][]string
|
mdata map[string][]string
|
||||||
statsTags []byte
|
statsTags []byte
|
||||||
|
@ -182,46 +180,6 @@ func isWhitelistedHeader(hdr string) bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// contentSubtype returns the content-subtype for the given content-type. The
|
|
||||||
// given content-type must be a valid content-type that starts with
|
|
||||||
// "application/grpc". A content-subtype will follow "application/grpc" after a
|
|
||||||
// "+" or ";". See
|
|
||||||
// https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#requests for
|
|
||||||
// more details.
|
|
||||||
//
|
|
||||||
// If contentType is not a valid content-type for gRPC, the boolean
|
|
||||||
// will be false, otherwise true. If content-type == "application/grpc",
|
|
||||||
// "application/grpc+", or "application/grpc;", the boolean will be true,
|
|
||||||
// but no content-subtype will be returned.
|
|
||||||
//
|
|
||||||
// contentType is assumed to be lowercase already.
|
|
||||||
func contentSubtype(contentType string) (string, bool) {
|
|
||||||
if contentType == baseContentType {
|
|
||||||
return "", true
|
|
||||||
}
|
|
||||||
if !strings.HasPrefix(contentType, baseContentType) {
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
// guaranteed since != baseContentType and has baseContentType prefix
|
|
||||||
switch contentType[len(baseContentType)] {
|
|
||||||
case '+', ';':
|
|
||||||
// this will return true for "application/grpc+" or "application/grpc;"
|
|
||||||
// which the previous validContentType function tested to be valid, so we
|
|
||||||
// just say that no content-subtype is specified in this case
|
|
||||||
return contentType[len(baseContentType)+1:], true
|
|
||||||
default:
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// contentSubtype is assumed to be lowercase
|
|
||||||
func contentType(contentSubtype string) string {
|
|
||||||
if contentSubtype == "" {
|
|
||||||
return baseContentType
|
|
||||||
}
|
|
||||||
return baseContentType + "+" + contentSubtype
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *decodeState) status() *status.Status {
|
func (d *decodeState) status() *status.Status {
|
||||||
if d.data.statusGen == nil {
|
if d.data.statusGen == nil {
|
||||||
// No status-details were provided; generate status using code/msg.
|
// No status-details were provided; generate status using code/msg.
|
||||||
|
@ -259,11 +217,11 @@ func decodeMetadataHeader(k, v string) (string, error) {
|
||||||
return v, nil
|
return v, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *decodeState) decodeHeader(frame *http2.MetaHeadersFrame) error {
|
func (d *decodeState) decodeHeader(frame *http2.MetaHeadersFrame) (http2.ErrCode, error) {
|
||||||
// frame.Truncated is set to true when framer detects that the current header
|
// frame.Truncated is set to true when framer detects that the current header
|
||||||
// list size hits MaxHeaderListSize limit.
|
// list size hits MaxHeaderListSize limit.
|
||||||
if frame.Truncated {
|
if frame.Truncated {
|
||||||
return status.Error(codes.Internal, "peer header list size exceeded limit")
|
return http2.ErrCodeFrameSize, status.Error(codes.Internal, "peer header list size exceeded limit")
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, hf := range frame.Fields {
|
for _, hf := range frame.Fields {
|
||||||
|
@ -272,10 +230,10 @@ func (d *decodeState) decodeHeader(frame *http2.MetaHeadersFrame) error {
|
||||||
|
|
||||||
if d.data.isGRPC {
|
if d.data.isGRPC {
|
||||||
if d.data.grpcErr != nil {
|
if d.data.grpcErr != nil {
|
||||||
return d.data.grpcErr
|
return http2.ErrCodeProtocol, d.data.grpcErr
|
||||||
}
|
}
|
||||||
if d.serverSide {
|
if d.serverSide {
|
||||||
return nil
|
return http2.ErrCodeNo, nil
|
||||||
}
|
}
|
||||||
if d.data.rawStatusCode == nil && d.data.statusGen == nil {
|
if d.data.rawStatusCode == nil && d.data.statusGen == nil {
|
||||||
// gRPC status doesn't exist.
|
// gRPC status doesn't exist.
|
||||||
|
@ -287,12 +245,12 @@ func (d *decodeState) decodeHeader(frame *http2.MetaHeadersFrame) error {
|
||||||
code := int(codes.Unknown)
|
code := int(codes.Unknown)
|
||||||
d.data.rawStatusCode = &code
|
d.data.rawStatusCode = &code
|
||||||
}
|
}
|
||||||
return nil
|
return http2.ErrCodeNo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// HTTP fallback mode
|
// HTTP fallback mode
|
||||||
if d.data.httpErr != nil {
|
if d.data.httpErr != nil {
|
||||||
return d.data.httpErr
|
return http2.ErrCodeProtocol, d.data.httpErr
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -307,7 +265,7 @@ func (d *decodeState) decodeHeader(frame *http2.MetaHeadersFrame) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return status.Error(code, d.constructHTTPErrMsg())
|
return http2.ErrCodeProtocol, status.Error(code, d.constructHTTPErrMsg())
|
||||||
}
|
}
|
||||||
|
|
||||||
// constructErrMsg constructs error message to be returned in HTTP fallback mode.
|
// constructErrMsg constructs error message to be returned in HTTP fallback mode.
|
||||||
|
@ -340,7 +298,7 @@ func (d *decodeState) addMetadata(k, v string) {
|
||||||
func (d *decodeState) processHeaderField(f hpack.HeaderField) {
|
func (d *decodeState) processHeaderField(f hpack.HeaderField) {
|
||||||
switch f.Name {
|
switch f.Name {
|
||||||
case "content-type":
|
case "content-type":
|
||||||
contentSubtype, validContentType := contentSubtype(f.Value)
|
contentSubtype, validContentType := grpcutil.ContentSubtype(f.Value)
|
||||||
if !validContentType {
|
if !validContentType {
|
||||||
d.data.contentTypeErr = fmt.Sprintf("transport: received the unexpected content-type %q", f.Value)
|
d.data.contentTypeErr = fmt.Sprintf("transport: received the unexpected content-type %q", f.Value)
|
||||||
return
|
return
|
||||||
|
@ -406,13 +364,17 @@ func (d *decodeState) processHeaderField(f hpack.HeaderField) {
|
||||||
}
|
}
|
||||||
d.data.statsTrace = v
|
d.data.statsTrace = v
|
||||||
d.addMetadata(f.Name, string(v))
|
d.addMetadata(f.Name, string(v))
|
||||||
|
case ":method":
|
||||||
|
d.data.httpMethod = f.Value
|
||||||
default:
|
default:
|
||||||
if isReservedHeader(f.Name) && !isWhitelistedHeader(f.Name) {
|
if isReservedHeader(f.Name) && !isWhitelistedHeader(f.Name) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
v, err := decodeMetadataHeader(f.Name, f.Value)
|
v, err := decodeMetadataHeader(f.Name, f.Value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorf("Failed to decode metadata header (%q, %q): %v", f.Name, f.Value, err)
|
if logger.V(logLevel) {
|
||||||
|
logger.Errorf("Failed to decode metadata header (%q, %q): %v", f.Name, f.Value, err)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
d.addMetadata(f.Name, v)
|
d.addMetadata(f.Name, v)
|
||||||
|
@ -449,41 +411,6 @@ func timeoutUnitToDuration(u timeoutUnit) (d time.Duration, ok bool) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const maxTimeoutValue int64 = 100000000 - 1
|
|
||||||
|
|
||||||
// div does integer division and round-up the result. Note that this is
|
|
||||||
// equivalent to (d+r-1)/r but has less chance to overflow.
|
|
||||||
func div(d, r time.Duration) int64 {
|
|
||||||
if m := d % r; m > 0 {
|
|
||||||
return int64(d/r + 1)
|
|
||||||
}
|
|
||||||
return int64(d / r)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(zhaoq): It is the simplistic and not bandwidth efficient. Improve it.
|
|
||||||
func encodeTimeout(t time.Duration) string {
|
|
||||||
if t <= 0 {
|
|
||||||
return "0n"
|
|
||||||
}
|
|
||||||
if d := div(t, time.Nanosecond); d <= maxTimeoutValue {
|
|
||||||
return strconv.FormatInt(d, 10) + "n"
|
|
||||||
}
|
|
||||||
if d := div(t, time.Microsecond); d <= maxTimeoutValue {
|
|
||||||
return strconv.FormatInt(d, 10) + "u"
|
|
||||||
}
|
|
||||||
if d := div(t, time.Millisecond); d <= maxTimeoutValue {
|
|
||||||
return strconv.FormatInt(d, 10) + "m"
|
|
||||||
}
|
|
||||||
if d := div(t, time.Second); d <= maxTimeoutValue {
|
|
||||||
return strconv.FormatInt(d, 10) + "S"
|
|
||||||
}
|
|
||||||
if d := div(t, time.Minute); d <= maxTimeoutValue {
|
|
||||||
return strconv.FormatInt(d, 10) + "M"
|
|
||||||
}
|
|
||||||
// Note that maxTimeoutValue * time.Hour > MaxInt64.
|
|
||||||
return strconv.FormatInt(div(t, time.Hour), 10) + "H"
|
|
||||||
}
|
|
||||||
|
|
||||||
func decodeTimeout(s string) (time.Duration, error) {
|
func decodeTimeout(s string) (time.Duration, error) {
|
||||||
size := len(s)
|
size := len(s)
|
||||||
if size < 2 {
|
if size < 2 {
|
||||||
|
@ -675,3 +602,31 @@ func newFramer(conn net.Conn, writeBufferSize, readBufferSize int, maxHeaderList
|
||||||
f.fr.ReadMetaHeaders = hpack.NewDecoder(http2InitHeaderTableSize, nil)
|
f.fr.ReadMetaHeaders = hpack.NewDecoder(http2InitHeaderTableSize, nil)
|
||||||
return f
|
return f
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// parseDialTarget returns the network and address to pass to dialer.
|
||||||
|
func parseDialTarget(target string) (string, string) {
|
||||||
|
net := "tcp"
|
||||||
|
m1 := strings.Index(target, ":")
|
||||||
|
m2 := strings.Index(target, ":/")
|
||||||
|
// handle unix:addr which will fail with url.Parse
|
||||||
|
if m1 >= 0 && m2 < 0 {
|
||||||
|
if n := target[0:m1]; n == "unix" {
|
||||||
|
return n, target[m1+1:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if m2 >= 0 {
|
||||||
|
t, err := url.Parse(target)
|
||||||
|
if err != nil {
|
||||||
|
return net, target
|
||||||
|
}
|
||||||
|
scheme := t.Scheme
|
||||||
|
addr := t.Path
|
||||||
|
if scheme == "unix" {
|
||||||
|
if addr == "" {
|
||||||
|
addr = t.Host
|
||||||
|
}
|
||||||
|
return scheme, addr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return net, target
|
||||||
|
}
|
||||||
|
|
46
vendor/google.golang.org/grpc/internal/transport/networktype/networktype.go
generated
vendored
Normal file
46
vendor/google.golang.org/grpc/internal/transport/networktype/networktype.go
generated
vendored
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* 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 networktype declares the network type to be used in the default
|
||||||
|
// dailer. Attribute of a resolver.Address.
|
||||||
|
package networktype
|
||||||
|
|
||||||
|
import (
|
||||||
|
"google.golang.org/grpc/resolver"
|
||||||
|
)
|
||||||
|
|
||||||
|
// keyType is the key to use for storing State in Attributes.
|
||||||
|
type keyType string
|
||||||
|
|
||||||
|
const key = keyType("grpc.internal.transport.networktype")
|
||||||
|
|
||||||
|
// Set returns a copy of the provided address with attributes containing networkType.
|
||||||
|
func Set(address resolver.Address, networkType string) resolver.Address {
|
||||||
|
address.Attributes = address.Attributes.WithValues(key, networkType)
|
||||||
|
return address
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns the network type in the resolver.Address and true, or "", false
|
||||||
|
// if not present.
|
||||||
|
func Get(address resolver.Address) (string, bool) {
|
||||||
|
v := address.Attributes.Value(key)
|
||||||
|
if v == nil {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
return v.(string), true
|
||||||
|
}
|
|
@ -16,13 +16,12 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package grpc
|
package transport
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"context"
|
"context"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
|
@ -34,8 +33,6 @@ import (
|
||||||
const proxyAuthHeaderKey = "Proxy-Authorization"
|
const proxyAuthHeaderKey = "Proxy-Authorization"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// errDisabled indicates that proxy is disabled for the address.
|
|
||||||
errDisabled = errors.New("proxy is disabled for the address")
|
|
||||||
// The following variable will be overwritten in the tests.
|
// The following variable will be overwritten in the tests.
|
||||||
httpProxyFromEnvironment = http.ProxyFromEnvironment
|
httpProxyFromEnvironment = http.ProxyFromEnvironment
|
||||||
)
|
)
|
||||||
|
@ -51,9 +48,6 @@ func mapAddress(ctx context.Context, address string) (*url.URL, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if url == nil {
|
|
||||||
return nil, errDisabled
|
|
||||||
}
|
|
||||||
return url, nil
|
return url, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,7 +70,7 @@ func basicAuth(username, password string) string {
|
||||||
return base64.StdEncoding.EncodeToString([]byte(auth))
|
return base64.StdEncoding.EncodeToString([]byte(auth))
|
||||||
}
|
}
|
||||||
|
|
||||||
func doHTTPConnectHandshake(ctx context.Context, conn net.Conn, backendAddr string, proxyURL *url.URL) (_ net.Conn, err error) {
|
func doHTTPConnectHandshake(ctx context.Context, conn net.Conn, backendAddr string, proxyURL *url.URL, grpcUA string) (_ net.Conn, err error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
conn.Close()
|
conn.Close()
|
||||||
|
@ -115,32 +109,28 @@ func doHTTPConnectHandshake(ctx context.Context, conn net.Conn, backendAddr stri
|
||||||
return &bufConn{Conn: conn, r: r}, nil
|
return &bufConn{Conn: conn, r: r}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// newProxyDialer returns a dialer that connects to proxy first if necessary.
|
// proxyDial dials, connecting to a proxy first if necessary. Checks if a proxy
|
||||||
// The returned dialer checks if a proxy is necessary, dial to the proxy with the
|
// is necessary, dials, does the HTTP CONNECT handshake, and returns the
|
||||||
// provided dialer, does HTTP CONNECT handshake and returns the connection.
|
// connection.
|
||||||
func newProxyDialer(dialer func(context.Context, string) (net.Conn, error)) func(context.Context, string) (net.Conn, error) {
|
func proxyDial(ctx context.Context, addr string, grpcUA string) (conn net.Conn, err error) {
|
||||||
return func(ctx context.Context, addr string) (conn net.Conn, err error) {
|
newAddr := addr
|
||||||
var newAddr string
|
|
||||||
proxyURL, err := mapAddress(ctx, addr)
|
proxyURL, err := mapAddress(ctx, addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err != errDisabled {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
newAddr = addr
|
if proxyURL != nil {
|
||||||
} else {
|
|
||||||
newAddr = proxyURL.Host
|
newAddr = proxyURL.Host
|
||||||
}
|
}
|
||||||
|
|
||||||
conn, err = dialer(ctx, newAddr)
|
conn, err = (&net.Dialer{}).DialContext(ctx, "tcp", newAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if proxyURL != nil {
|
if proxyURL != nil {
|
||||||
// proxy is disabled if proxyURL is nil.
|
// proxy is disabled if proxyURL is nil.
|
||||||
conn, err = doHTTPConnectHandshake(ctx, conn, addr, proxyURL)
|
conn, err = doHTTPConnectHandshake(ctx, conn, addr, proxyURL, grpcUA)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func sendHTTPRequest(ctx context.Context, req *http.Request, conn net.Conn) error {
|
func sendHTTPRequest(ctx context.Context, req *http.Request, conn net.Conn) error {
|
|
@ -35,11 +35,14 @@ import (
|
||||||
"google.golang.org/grpc/credentials"
|
"google.golang.org/grpc/credentials"
|
||||||
"google.golang.org/grpc/keepalive"
|
"google.golang.org/grpc/keepalive"
|
||||||
"google.golang.org/grpc/metadata"
|
"google.golang.org/grpc/metadata"
|
||||||
|
"google.golang.org/grpc/resolver"
|
||||||
"google.golang.org/grpc/stats"
|
"google.golang.org/grpc/stats"
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
"google.golang.org/grpc/tap"
|
"google.golang.org/grpc/tap"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const logLevel = 2
|
||||||
|
|
||||||
type bufferPool struct {
|
type bufferPool struct {
|
||||||
pool sync.Pool
|
pool sync.Pool
|
||||||
}
|
}
|
||||||
|
@ -238,6 +241,7 @@ type Stream struct {
|
||||||
ctx context.Context // the associated context of the stream
|
ctx context.Context // the associated context of the stream
|
||||||
cancel context.CancelFunc // always nil for client side Stream
|
cancel context.CancelFunc // always nil for client side Stream
|
||||||
done chan struct{} // closed at the end of stream to unblock writers. On the client side.
|
done chan struct{} // closed at the end of stream to unblock writers. On the client side.
|
||||||
|
doneFunc func() // invoked at the end of stream on client side.
|
||||||
ctxDone <-chan struct{} // same as done chan but for server side. Cache of ctx.Done() (for performance)
|
ctxDone <-chan struct{} // same as done chan but for server side. Cache of ctx.Done() (for performance)
|
||||||
method string // the associated RPC method of the stream
|
method string // the associated RPC method of the stream
|
||||||
recvCompress string
|
recvCompress string
|
||||||
|
@ -566,19 +570,14 @@ type ConnectOptions struct {
|
||||||
ChannelzParentID int64
|
ChannelzParentID int64
|
||||||
// MaxHeaderListSize sets the max (uncompressed) size of header list that is prepared to be received.
|
// MaxHeaderListSize sets the max (uncompressed) size of header list that is prepared to be received.
|
||||||
MaxHeaderListSize *uint32
|
MaxHeaderListSize *uint32
|
||||||
}
|
// UseProxy specifies if a proxy should be used.
|
||||||
|
UseProxy bool
|
||||||
// TargetInfo contains the information of the target such as network address and metadata.
|
|
||||||
type TargetInfo struct {
|
|
||||||
Addr string
|
|
||||||
Metadata interface{}
|
|
||||||
Authority string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewClientTransport establishes the transport with the required ConnectOptions
|
// NewClientTransport establishes the transport with the required ConnectOptions
|
||||||
// and returns it to the caller.
|
// and returns it to the caller.
|
||||||
func NewClientTransport(connectCtx, ctx context.Context, target TargetInfo, opts ConnectOptions, onPrefaceReceipt func(), onGoAway func(GoAwayReason), onClose func()) (ClientTransport, error) {
|
func NewClientTransport(connectCtx, ctx context.Context, addr resolver.Address, opts ConnectOptions, onPrefaceReceipt func(), onGoAway func(GoAwayReason), onClose func()) (ClientTransport, error) {
|
||||||
return newHTTP2Client(connectCtx, ctx, target, opts, onPrefaceReceipt, onGoAway, onClose)
|
return newHTTP2Client(connectCtx, ctx, addr, opts, onPrefaceReceipt, onGoAway, onClose)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Options provides additional hints and information for message
|
// Options provides additional hints and information for message
|
||||||
|
@ -613,6 +612,8 @@ type CallHdr struct {
|
||||||
ContentSubtype string
|
ContentSubtype string
|
||||||
|
|
||||||
PreviousAttempts int // value of grpc-previous-rpc-attempts header to set
|
PreviousAttempts int // value of grpc-previous-rpc-attempts header to set
|
||||||
|
|
||||||
|
DoneFunc func() // called when the stream is finished
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClientTransport is the common interface for all gRPC client-side transport
|
// ClientTransport is the common interface for all gRPC client-side transport
|
||||||
|
@ -621,7 +622,7 @@ type ClientTransport interface {
|
||||||
// Close tears down this transport. Once it returns, the transport
|
// Close tears down this transport. Once it returns, the transport
|
||||||
// should not be accessed any more. The caller must make sure this
|
// should not be accessed any more. The caller must make sure this
|
||||||
// is called only once.
|
// is called only once.
|
||||||
Close() error
|
Close(err error)
|
||||||
|
|
||||||
// GracefulClose starts to tear down the transport: the transport will stop
|
// GracefulClose starts to tear down the transport: the transport will stop
|
||||||
// accepting new RPCs and NewStream will return error. Once all streams are
|
// accepting new RPCs and NewStream will return error. Once all streams are
|
||||||
|
@ -655,8 +656,9 @@ type ClientTransport interface {
|
||||||
// HTTP/2).
|
// HTTP/2).
|
||||||
GoAway() <-chan struct{}
|
GoAway() <-chan struct{}
|
||||||
|
|
||||||
// GetGoAwayReason returns the reason why GoAway frame was received.
|
// GetGoAwayReason returns the reason why GoAway frame was received, along
|
||||||
GetGoAwayReason() GoAwayReason
|
// with a human readable string with debug info.
|
||||||
|
GetGoAwayReason() (GoAwayReason, string)
|
||||||
|
|
||||||
// RemoteAddr returns the remote network address.
|
// RemoteAddr returns the remote network address.
|
||||||
RemoteAddr() net.Addr
|
RemoteAddr() net.Addr
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021 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 internal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"google.golang.org/grpc/attributes"
|
||||||
|
"google.golang.org/grpc/resolver"
|
||||||
|
)
|
||||||
|
|
||||||
|
// handshakeClusterNameKey is the type used as the key to store cluster name in
|
||||||
|
// the Attributes field of resolver.Address.
|
||||||
|
type handshakeClusterNameKey struct{}
|
||||||
|
|
||||||
|
// SetXDSHandshakeClusterName returns a copy of addr in which the Attributes field
|
||||||
|
// is updated with the cluster name.
|
||||||
|
func SetXDSHandshakeClusterName(addr resolver.Address, clusterName string) resolver.Address {
|
||||||
|
addr.Attributes = addr.Attributes.WithValues(handshakeClusterNameKey{}, clusterName)
|
||||||
|
return addr
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetXDSHandshakeClusterName returns cluster name stored in attr.
|
||||||
|
func GetXDSHandshakeClusterName(attr *attributes.Attributes) (string, bool) {
|
||||||
|
v := attr.Value(handshakeClusterNameKey{})
|
||||||
|
name, ok := v.(string)
|
||||||
|
return name, ok
|
||||||
|
}
|
|
@ -75,13 +75,9 @@ func Pairs(kv ...string) MD {
|
||||||
panic(fmt.Sprintf("metadata: Pairs got the odd number of input pairs for metadata: %d", len(kv)))
|
panic(fmt.Sprintf("metadata: Pairs got the odd number of input pairs for metadata: %d", len(kv)))
|
||||||
}
|
}
|
||||||
md := MD{}
|
md := MD{}
|
||||||
var key string
|
for i := 0; i < len(kv); i += 2 {
|
||||||
for i, s := range kv {
|
key := strings.ToLower(kv[i])
|
||||||
if i%2 == 0 {
|
md[key] = append(md[key], kv[i+1])
|
||||||
key = strings.ToLower(s)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
md[key] = append(md[key], s)
|
|
||||||
}
|
}
|
||||||
return md
|
return md
|
||||||
}
|
}
|
||||||
|
@ -195,12 +191,18 @@ func FromOutgoingContext(ctx context.Context) (MD, bool) {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
mds := make([]MD, 0, len(raw.added)+1)
|
out := raw.md.Copy()
|
||||||
mds = append(mds, raw.md)
|
for _, added := range raw.added {
|
||||||
for _, vv := range raw.added {
|
if len(added)%2 == 1 {
|
||||||
mds = append(mds, Pairs(vv...))
|
panic(fmt.Sprintf("metadata: FromOutgoingContext got an odd number of input pairs for metadata: %d", len(added)))
|
||||||
}
|
}
|
||||||
return Join(mds...), ok
|
|
||||||
|
for i := 0; i < len(added); i += 2 {
|
||||||
|
key := strings.ToLower(added[i])
|
||||||
|
out[key] = append(out[key], added[i+1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
type rawMD struct {
|
type rawMD struct {
|
||||||
|
|
|
@ -1,293 +0,0 @@
|
||||||
/*
|
|
||||||
*
|
|
||||||
* Copyright 2017 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 naming
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"strconv"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"google.golang.org/grpc/grpclog"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
defaultPort = "443"
|
|
||||||
defaultFreq = time.Minute * 30
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
errMissingAddr = errors.New("missing address")
|
|
||||||
errWatcherClose = errors.New("watcher has been closed")
|
|
||||||
|
|
||||||
lookupHost = net.DefaultResolver.LookupHost
|
|
||||||
lookupSRV = net.DefaultResolver.LookupSRV
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewDNSResolverWithFreq creates a DNS Resolver that can resolve DNS names, and
|
|
||||||
// create watchers that poll the DNS server using the frequency set by freq.
|
|
||||||
func NewDNSResolverWithFreq(freq time.Duration) (Resolver, error) {
|
|
||||||
return &dnsResolver{freq: freq}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewDNSResolver creates a DNS Resolver that can resolve DNS names, and create
|
|
||||||
// watchers that poll the DNS server using the default frequency defined by defaultFreq.
|
|
||||||
func NewDNSResolver() (Resolver, error) {
|
|
||||||
return NewDNSResolverWithFreq(defaultFreq)
|
|
||||||
}
|
|
||||||
|
|
||||||
// dnsResolver handles name resolution for names following the DNS scheme
|
|
||||||
type dnsResolver struct {
|
|
||||||
// frequency of polling the DNS server that the watchers created by this resolver will use.
|
|
||||||
freq time.Duration
|
|
||||||
}
|
|
||||||
|
|
||||||
// formatIP returns ok = false if addr is not a valid textual representation of an IP address.
|
|
||||||
// If addr is an IPv4 address, return the addr and ok = true.
|
|
||||||
// If addr is an IPv6 address, return the addr enclosed in square brackets and ok = true.
|
|
||||||
func formatIP(addr string) (addrIP string, ok bool) {
|
|
||||||
ip := net.ParseIP(addr)
|
|
||||||
if ip == nil {
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
if ip.To4() != nil {
|
|
||||||
return addr, true
|
|
||||||
}
|
|
||||||
return "[" + addr + "]", true
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseTarget takes the user input target string, returns formatted host and port info.
|
|
||||||
// If target doesn't specify a port, set the port to be the defaultPort.
|
|
||||||
// If target is in IPv6 format and host-name is enclosed in square brackets, brackets
|
|
||||||
// are stripped when setting the host.
|
|
||||||
// examples:
|
|
||||||
// target: "www.google.com" returns host: "www.google.com", port: "443"
|
|
||||||
// target: "ipv4-host:80" returns host: "ipv4-host", port: "80"
|
|
||||||
// target: "[ipv6-host]" returns host: "ipv6-host", port: "443"
|
|
||||||
// target: ":80" returns host: "localhost", port: "80"
|
|
||||||
// target: ":" returns host: "localhost", port: "443"
|
|
||||||
func parseTarget(target string) (host, port string, err error) {
|
|
||||||
if target == "" {
|
|
||||||
return "", "", errMissingAddr
|
|
||||||
}
|
|
||||||
|
|
||||||
if ip := net.ParseIP(target); ip != nil {
|
|
||||||
// target is an IPv4 or IPv6(without brackets) address
|
|
||||||
return target, defaultPort, nil
|
|
||||||
}
|
|
||||||
if host, port, err := net.SplitHostPort(target); err == nil {
|
|
||||||
// target has port, i.e ipv4-host:port, [ipv6-host]:port, host-name:port
|
|
||||||
if host == "" {
|
|
||||||
// Keep consistent with net.Dial(): If the host is empty, as in ":80", the local system is assumed.
|
|
||||||
host = "localhost"
|
|
||||||
}
|
|
||||||
if port == "" {
|
|
||||||
// If the port field is empty(target ends with colon), e.g. "[::1]:", defaultPort is used.
|
|
||||||
port = defaultPort
|
|
||||||
}
|
|
||||||
return host, port, nil
|
|
||||||
}
|
|
||||||
if host, port, err := net.SplitHostPort(target + ":" + defaultPort); err == nil {
|
|
||||||
// target doesn't have port
|
|
||||||
return host, port, nil
|
|
||||||
}
|
|
||||||
return "", "", fmt.Errorf("invalid target address %v", target)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resolve creates a watcher that watches the name resolution of the target.
|
|
||||||
func (r *dnsResolver) Resolve(target string) (Watcher, error) {
|
|
||||||
host, port, err := parseTarget(target)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if net.ParseIP(host) != nil {
|
|
||||||
ipWatcher := &ipWatcher{
|
|
||||||
updateChan: make(chan *Update, 1),
|
|
||||||
}
|
|
||||||
host, _ = formatIP(host)
|
|
||||||
ipWatcher.updateChan <- &Update{Op: Add, Addr: host + ":" + port}
|
|
||||||
return ipWatcher, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
return &dnsWatcher{
|
|
||||||
r: r,
|
|
||||||
host: host,
|
|
||||||
port: port,
|
|
||||||
ctx: ctx,
|
|
||||||
cancel: cancel,
|
|
||||||
t: time.NewTimer(0),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// dnsWatcher watches for the name resolution update for a specific target
|
|
||||||
type dnsWatcher struct {
|
|
||||||
r *dnsResolver
|
|
||||||
host string
|
|
||||||
port string
|
|
||||||
// The latest resolved address set
|
|
||||||
curAddrs map[string]*Update
|
|
||||||
ctx context.Context
|
|
||||||
cancel context.CancelFunc
|
|
||||||
t *time.Timer
|
|
||||||
}
|
|
||||||
|
|
||||||
// ipWatcher watches for the name resolution update for an IP address.
|
|
||||||
type ipWatcher struct {
|
|
||||||
updateChan chan *Update
|
|
||||||
}
|
|
||||||
|
|
||||||
// Next returns the address resolution Update for the target. For IP address,
|
|
||||||
// the resolution is itself, thus polling name server is unnecessary. Therefore,
|
|
||||||
// Next() will return an Update the first time it is called, and will be blocked
|
|
||||||
// for all following calls as no Update exists until watcher is closed.
|
|
||||||
func (i *ipWatcher) Next() ([]*Update, error) {
|
|
||||||
u, ok := <-i.updateChan
|
|
||||||
if !ok {
|
|
||||||
return nil, errWatcherClose
|
|
||||||
}
|
|
||||||
return []*Update{u}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close closes the ipWatcher.
|
|
||||||
func (i *ipWatcher) Close() {
|
|
||||||
close(i.updateChan)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddressType indicates the address type returned by name resolution.
|
|
||||||
type AddressType uint8
|
|
||||||
|
|
||||||
const (
|
|
||||||
// Backend indicates the server is a backend server.
|
|
||||||
Backend AddressType = iota
|
|
||||||
// GRPCLB indicates the server is a grpclb load balancer.
|
|
||||||
GRPCLB
|
|
||||||
)
|
|
||||||
|
|
||||||
// AddrMetadataGRPCLB contains the information the name resolver for grpclb should provide. The
|
|
||||||
// name resolver used by the grpclb balancer is required to provide this type of metadata in
|
|
||||||
// its address updates.
|
|
||||||
type AddrMetadataGRPCLB struct {
|
|
||||||
// AddrType is the type of server (grpc load balancer or backend).
|
|
||||||
AddrType AddressType
|
|
||||||
// ServerName is the name of the grpc load balancer. Used for authentication.
|
|
||||||
ServerName string
|
|
||||||
}
|
|
||||||
|
|
||||||
// compileUpdate compares the old resolved addresses and newly resolved addresses,
|
|
||||||
// and generates an update list
|
|
||||||
func (w *dnsWatcher) compileUpdate(newAddrs map[string]*Update) []*Update {
|
|
||||||
var res []*Update
|
|
||||||
for a, u := range w.curAddrs {
|
|
||||||
if _, ok := newAddrs[a]; !ok {
|
|
||||||
u.Op = Delete
|
|
||||||
res = append(res, u)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for a, u := range newAddrs {
|
|
||||||
if _, ok := w.curAddrs[a]; !ok {
|
|
||||||
res = append(res, u)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *dnsWatcher) lookupSRV() map[string]*Update {
|
|
||||||
newAddrs := make(map[string]*Update)
|
|
||||||
_, srvs, err := lookupSRV(w.ctx, "grpclb", "tcp", w.host)
|
|
||||||
if err != nil {
|
|
||||||
grpclog.Infof("grpc: failed dns SRV record lookup due to %v.\n", err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
for _, s := range srvs {
|
|
||||||
lbAddrs, err := lookupHost(w.ctx, s.Target)
|
|
||||||
if err != nil {
|
|
||||||
grpclog.Warningf("grpc: failed load balancer address dns lookup due to %v.\n", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for _, a := range lbAddrs {
|
|
||||||
a, ok := formatIP(a)
|
|
||||||
if !ok {
|
|
||||||
grpclog.Errorf("grpc: failed IP parsing due to %v.\n", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
addr := a + ":" + strconv.Itoa(int(s.Port))
|
|
||||||
newAddrs[addr] = &Update{Addr: addr,
|
|
||||||
Metadata: AddrMetadataGRPCLB{AddrType: GRPCLB, ServerName: s.Target}}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return newAddrs
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *dnsWatcher) lookupHost() map[string]*Update {
|
|
||||||
newAddrs := make(map[string]*Update)
|
|
||||||
addrs, err := lookupHost(w.ctx, w.host)
|
|
||||||
if err != nil {
|
|
||||||
grpclog.Warningf("grpc: failed dns A record lookup due to %v.\n", err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
for _, a := range addrs {
|
|
||||||
a, ok := formatIP(a)
|
|
||||||
if !ok {
|
|
||||||
grpclog.Errorf("grpc: failed IP parsing due to %v.\n", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
addr := a + ":" + w.port
|
|
||||||
newAddrs[addr] = &Update{Addr: addr}
|
|
||||||
}
|
|
||||||
return newAddrs
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *dnsWatcher) lookup() []*Update {
|
|
||||||
newAddrs := w.lookupSRV()
|
|
||||||
if newAddrs == nil {
|
|
||||||
// If failed to get any balancer address (either no corresponding SRV for the
|
|
||||||
// target, or caused by failure during resolution/parsing of the balancer target),
|
|
||||||
// return any A record info available.
|
|
||||||
newAddrs = w.lookupHost()
|
|
||||||
}
|
|
||||||
result := w.compileUpdate(newAddrs)
|
|
||||||
w.curAddrs = newAddrs
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Next returns the resolved address update(delta) for the target. If there's no
|
|
||||||
// change, it will sleep for 30 mins and try to resolve again after that.
|
|
||||||
func (w *dnsWatcher) Next() ([]*Update, error) {
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-w.ctx.Done():
|
|
||||||
return nil, errWatcherClose
|
|
||||||
case <-w.t.C:
|
|
||||||
}
|
|
||||||
result := w.lookup()
|
|
||||||
// Next lookup should happen after an interval defined by w.r.freq.
|
|
||||||
w.t.Reset(w.r.freq)
|
|
||||||
if len(result) > 0 {
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *dnsWatcher) Close() {
|
|
||||||
w.cancel()
|
|
||||||
}
|
|
|
@ -1,68 +0,0 @@
|
||||||
/*
|
|
||||||
*
|
|
||||||
* 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 naming defines the naming API and related data structures for gRPC.
|
|
||||||
//
|
|
||||||
// This package is deprecated: please use package resolver instead.
|
|
||||||
package naming
|
|
||||||
|
|
||||||
// Operation defines the corresponding operations for a name resolution change.
|
|
||||||
//
|
|
||||||
// Deprecated: please use package resolver.
|
|
||||||
type Operation uint8
|
|
||||||
|
|
||||||
const (
|
|
||||||
// Add indicates a new address is added.
|
|
||||||
Add Operation = iota
|
|
||||||
// Delete indicates an existing address is deleted.
|
|
||||||
Delete
|
|
||||||
)
|
|
||||||
|
|
||||||
// Update defines a name resolution update. Notice that it is not valid having both
|
|
||||||
// empty string Addr and nil Metadata in an Update.
|
|
||||||
//
|
|
||||||
// Deprecated: please use package resolver.
|
|
||||||
type Update struct {
|
|
||||||
// Op indicates the operation of the update.
|
|
||||||
Op Operation
|
|
||||||
// Addr is the updated address. It is empty string if there is no address update.
|
|
||||||
Addr string
|
|
||||||
// Metadata is the updated metadata. It is nil if there is no metadata update.
|
|
||||||
// Metadata is not required for a custom naming implementation.
|
|
||||||
Metadata interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resolver creates a Watcher for a target to track its resolution changes.
|
|
||||||
//
|
|
||||||
// Deprecated: please use package resolver.
|
|
||||||
type Resolver interface {
|
|
||||||
// Resolve creates a Watcher for target.
|
|
||||||
Resolve(target string) (Watcher, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Watcher watches for the updates on the specified target.
|
|
||||||
//
|
|
||||||
// Deprecated: please use package resolver.
|
|
||||||
type Watcher interface {
|
|
||||||
// Next blocks until an update or error happens. It may return one or more
|
|
||||||
// updates. The first call should get the full set of the results. It should
|
|
||||||
// return an error if and only if Watcher cannot recover.
|
|
||||||
Next() ([]*Update, error)
|
|
||||||
// Close closes the Watcher.
|
|
||||||
Close()
|
|
||||||
}
|
|
|
@ -20,80 +20,31 @@ package grpc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"google.golang.org/grpc/balancer"
|
"google.golang.org/grpc/balancer"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/grpclog"
|
|
||||||
"google.golang.org/grpc/internal/channelz"
|
"google.golang.org/grpc/internal/channelz"
|
||||||
"google.golang.org/grpc/internal/transport"
|
"google.golang.org/grpc/internal/transport"
|
||||||
"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.V2Picker
|
picker balancer.Picker
|
||||||
|
|
||||||
// The latest connection error. TODO: remove when V1 picker is deprecated;
|
|
||||||
// balancer should be responsible for providing the error.
|
|
||||||
*connErr
|
|
||||||
}
|
|
||||||
|
|
||||||
type connErr struct {
|
|
||||||
mu sync.Mutex
|
|
||||||
err error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *connErr) updateConnectionError(err error) {
|
|
||||||
c.mu.Lock()
|
|
||||||
c.err = err
|
|
||||||
c.mu.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *connErr) connectionError() error {
|
|
||||||
c.mu.Lock()
|
|
||||||
err := c.err
|
|
||||||
c.mu.Unlock()
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func newPickerWrapper() *pickerWrapper {
|
func newPickerWrapper() *pickerWrapper {
|
||||||
return &pickerWrapper{blockingCh: make(chan struct{}), connErr: &connErr{}}
|
return &pickerWrapper{blockingCh: make(chan struct{})}
|
||||||
}
|
}
|
||||||
|
|
||||||
// updatePicker is called by UpdateBalancerState. It unblocks all blocked pick.
|
// updatePicker is called by UpdateBalancerState. It unblocks all blocked pick.
|
||||||
func (pw *pickerWrapper) updatePicker(p balancer.Picker) {
|
func (pw *pickerWrapper) updatePicker(p balancer.Picker) {
|
||||||
pw.updatePickerV2(&v2PickerWrapper{picker: p, connErr: pw.connErr})
|
|
||||||
}
|
|
||||||
|
|
||||||
// updatePicker is called by UpdateBalancerState. It unblocks all blocked pick.
|
|
||||||
func (pw *pickerWrapper) updatePickerV2(p balancer.V2Picker) {
|
|
||||||
pw.mu.Lock()
|
pw.mu.Lock()
|
||||||
if pw.done {
|
if pw.done {
|
||||||
pw.mu.Unlock()
|
pw.mu.Unlock()
|
||||||
|
@ -154,8 +105,6 @@ func (pw *pickerWrapper) pick(ctx context.Context, failfast bool, info balancer.
|
||||||
var errStr string
|
var errStr string
|
||||||
if lastPickErr != nil {
|
if lastPickErr != nil {
|
||||||
errStr = "latest balancer error: " + lastPickErr.Error()
|
errStr = "latest balancer error: " + lastPickErr.Error()
|
||||||
} else if connectionErr := pw.connectionError(); connectionErr != nil {
|
|
||||||
errStr = "latest connection error: " + connectionErr.Error()
|
|
||||||
} else {
|
} else {
|
||||||
errStr = ctx.Err().Error()
|
errStr = ctx.Err().Error()
|
||||||
}
|
}
|
||||||
|
@ -180,23 +129,22 @@ func (pw *pickerWrapper) pick(ctx context.Context, failfast bool, info balancer.
|
||||||
if err == balancer.ErrNoSubConnAvailable {
|
if err == balancer.ErrNoSubConnAvailable {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if tfe, ok := err.(interface{ IsTransientFailure() bool }); ok && tfe.IsTransientFailure() {
|
if _, ok := status.FromError(err); ok {
|
||||||
|
// Status error: end the RPC unconditionally with this status.
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
// For all other errors, wait for ready RPCs should block and other
|
||||||
|
// RPCs should fail with unavailable.
|
||||||
if !failfast {
|
if !failfast {
|
||||||
lastPickErr = err
|
lastPickErr = err
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
return nil, nil, status.Error(codes.Unavailable, err.Error())
|
return nil, nil, status.Error(codes.Unavailable, err.Error())
|
||||||
}
|
}
|
||||||
if _, ok := status.FromError(err); ok {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
// err is some other error.
|
|
||||||
return nil, nil, status.Error(codes.Unknown, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
acw, ok := pickResult.SubConn.(*acBalancerWrapper)
|
acw, ok := pickResult.SubConn.(*acBalancerWrapper)
|
||||||
if !ok {
|
if !ok {
|
||||||
grpclog.Error("subconn returned from pick is not *acBalancerWrapper")
|
logger.Error("subconn returned from pick is not *acBalancerWrapper")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if t, ok := acw.getAddrConn().getReadyTransport(); ok {
|
if t, ok := acw.getAddrConn().getReadyTransport(); ok {
|
||||||
|
@ -210,7 +158,7 @@ func (pw *pickerWrapper) pick(ctx context.Context, failfast bool, info balancer.
|
||||||
// DoneInfo with default value works.
|
// DoneInfo with default value works.
|
||||||
pickResult.Done(balancer.DoneInfo{})
|
pickResult.Done(balancer.DoneInfo{})
|
||||||
}
|
}
|
||||||
grpclog.Infof("blockingPicker: the picked transport is not ready, loop back to repick")
|
logger.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.
|
||||||
// A valid picker always returns READY subConn. This means the state of ac
|
// A valid picker always returns READY subConn. This means the state of ac
|
||||||
// just changed, and picker will be updated shortly.
|
// just changed, and picker will be updated shortly.
|
||||||
|
|
|
@ -20,13 +20,10 @@ package grpc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"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/resolver"
|
|
||||||
"google.golang.org/grpc/status"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// PickFirstBalancerName is the name of the pick_first balancer.
|
// PickFirstBalancerName is the name of the pick_first balancer.
|
||||||
|
@ -52,30 +49,16 @@ type pickfirstBalancer struct {
|
||||||
sc balancer.SubConn
|
sc balancer.SubConn
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ balancer.V2Balancer = &pickfirstBalancer{} // Assert we implement v2
|
|
||||||
|
|
||||||
func (b *pickfirstBalancer) HandleResolvedAddrs(addrs []resolver.Address, err error) {
|
|
||||||
if err != nil {
|
|
||||||
b.ResolverError(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
b.UpdateClientConnState(balancer.ClientConnState{ResolverState: resolver.State{Addresses: addrs}}) // Ignore error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *pickfirstBalancer) HandleSubConnStateChange(sc balancer.SubConn, s connectivity.State) {
|
|
||||||
b.UpdateSubConnState(sc, balancer.SubConnState{ConnectivityState: s})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *pickfirstBalancer) ResolverError(err error) {
|
func (b *pickfirstBalancer) ResolverError(err error) {
|
||||||
switch b.state {
|
switch b.state {
|
||||||
case connectivity.TransientFailure, connectivity.Idle, connectivity.Connecting:
|
case connectivity.TransientFailure, connectivity.Idle, connectivity.Connecting:
|
||||||
// Set a failing picker if we don't have a good picker.
|
// Set a failing picker if we don't have a good picker.
|
||||||
b.cc.UpdateState(balancer.State{ConnectivityState: connectivity.TransientFailure,
|
b.cc.UpdateState(balancer.State{ConnectivityState: connectivity.TransientFailure,
|
||||||
Picker: &picker{err: status.Errorf(codes.Unavailable, "name resolver error: %v", err)}},
|
Picker: &picker{err: fmt.Errorf("name resolver error: %v", err)},
|
||||||
)
|
})
|
||||||
}
|
}
|
||||||
if grpclog.V(2) {
|
if logger.V(2) {
|
||||||
grpclog.Infof("pickfirstBalancer: ResolverError called with error %v", err)
|
logger.Infof("pickfirstBalancer: ResolverError called with error %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,32 +71,32 @@ func (b *pickfirstBalancer) UpdateClientConnState(cs balancer.ClientConnState) e
|
||||||
var err error
|
var err error
|
||||||
b.sc, err = b.cc.NewSubConn(cs.ResolverState.Addresses, balancer.NewSubConnOptions{})
|
b.sc, err = b.cc.NewSubConn(cs.ResolverState.Addresses, balancer.NewSubConnOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if grpclog.V(2) {
|
if logger.V(2) {
|
||||||
grpclog.Errorf("pickfirstBalancer: failed to NewSubConn: %v", err)
|
logger.Errorf("pickfirstBalancer: failed to NewSubConn: %v", err)
|
||||||
}
|
}
|
||||||
b.state = connectivity.TransientFailure
|
b.state = connectivity.TransientFailure
|
||||||
b.cc.UpdateState(balancer.State{ConnectivityState: connectivity.TransientFailure,
|
b.cc.UpdateState(balancer.State{ConnectivityState: connectivity.TransientFailure,
|
||||||
Picker: &picker{err: status.Errorf(codes.Unavailable, "error creating connection: %v", err)}},
|
Picker: &picker{err: fmt.Errorf("error creating connection: %v", err)},
|
||||||
)
|
})
|
||||||
return balancer.ErrBadResolverState
|
return balancer.ErrBadResolverState
|
||||||
}
|
}
|
||||||
b.state = connectivity.Idle
|
b.state = connectivity.Idle
|
||||||
b.cc.UpdateState(balancer.State{ConnectivityState: connectivity.Idle, Picker: &picker{result: balancer.PickResult{SubConn: b.sc}}})
|
b.cc.UpdateState(balancer.State{ConnectivityState: connectivity.Idle, Picker: &picker{result: balancer.PickResult{SubConn: b.sc}}})
|
||||||
b.sc.Connect()
|
b.sc.Connect()
|
||||||
} else {
|
} else {
|
||||||
b.sc.UpdateAddresses(cs.ResolverState.Addresses)
|
b.cc.UpdateAddresses(b.sc, cs.ResolverState.Addresses)
|
||||||
b.sc.Connect()
|
b.sc.Connect()
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *pickfirstBalancer) UpdateSubConnState(sc balancer.SubConn, s balancer.SubConnState) {
|
func (b *pickfirstBalancer) UpdateSubConnState(sc balancer.SubConn, s balancer.SubConnState) {
|
||||||
if grpclog.V(2) {
|
if logger.V(2) {
|
||||||
grpclog.Infof("pickfirstBalancer: HandleSubConnStateChange: %p, %v", sc, s)
|
logger.Infof("pickfirstBalancer: UpdateSubConnState: %p, %v", sc, s)
|
||||||
}
|
}
|
||||||
if b.sc != sc {
|
if b.sc != sc {
|
||||||
if grpclog.V(2) {
|
if logger.V(2) {
|
||||||
grpclog.Infof("pickfirstBalancer: ignored state change because sc is not recognized")
|
logger.Infof("pickfirstBalancer: ignored state change because sc is not recognized")
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -129,15 +112,9 @@ func (b *pickfirstBalancer) UpdateSubConnState(sc balancer.SubConn, s balancer.S
|
||||||
case connectivity.Connecting:
|
case connectivity.Connecting:
|
||||||
b.cc.UpdateState(balancer.State{ConnectivityState: s.ConnectivityState, Picker: &picker{err: balancer.ErrNoSubConnAvailable}})
|
b.cc.UpdateState(balancer.State{ConnectivityState: s.ConnectivityState, Picker: &picker{err: balancer.ErrNoSubConnAvailable}})
|
||||||
case connectivity.TransientFailure:
|
case connectivity.TransientFailure:
|
||||||
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{
|
b.cc.UpdateState(balancer.State{
|
||||||
ConnectivityState: s.ConnectivityState,
|
ConnectivityState: s.ConnectivityState,
|
||||||
Picker: &picker{err: err},
|
Picker: &picker{err: s.ConnectionError},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,10 @@ import (
|
||||||
|
|
||||||
// PreparedMsg is responsible for creating a Marshalled and Compressed object.
|
// PreparedMsg is responsible for creating a Marshalled and Compressed object.
|
||||||
//
|
//
|
||||||
// This API is EXPERIMENTAL.
|
// Experimental
|
||||||
|
//
|
||||||
|
// Notice: This type is EXPERIMENTAL and may be changed or removed in a
|
||||||
|
// later release.
|
||||||
type PreparedMsg struct {
|
type PreparedMsg struct {
|
||||||
// Struct for preparing msg before sending them
|
// Struct for preparing msg before sending them
|
||||||
encodedData []byte
|
encodedData []byte
|
||||||
|
|
|
@ -85,12 +85,19 @@ const (
|
||||||
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.
|
// Deprecated: to select the GRPCLB load balancing policy, use a service
|
||||||
|
// config with a corresponding loadBalancingConfig. To supply balancer
|
||||||
|
// addresses to the GRPCLB load balancing policy, set State.Attributes
|
||||||
|
// using balancer/grpclb/state.Set.
|
||||||
GRPCLB
|
GRPCLB
|
||||||
)
|
)
|
||||||
|
|
||||||
// Address represents a server the client connects to.
|
// Address represents a server the client connects to.
|
||||||
// This is the EXPERIMENTAL API and may be changed or extended in the future.
|
//
|
||||||
|
// Experimental
|
||||||
|
//
|
||||||
|
// Notice: This type is EXPERIMENTAL and may be changed or removed in a
|
||||||
|
// later release.
|
||||||
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
|
||||||
|
@ -174,7 +181,7 @@ type State struct {
|
||||||
// gRPC to add new methods to this interface.
|
// gRPC to add new methods to this interface.
|
||||||
type ClientConn interface {
|
type ClientConn interface {
|
||||||
// UpdateState updates the state of the ClientConn appropriately.
|
// UpdateState updates the state of the ClientConn appropriately.
|
||||||
UpdateState(State)
|
UpdateState(State) error
|
||||||
// ReportError notifies the ClientConn that the Resolver encountered an
|
// ReportError notifies the ClientConn that the Resolver encountered an
|
||||||
// error. The ClientConn will notify the load balancer and begin calling
|
// error. The ClientConn will notify the load balancer and begin calling
|
||||||
// ResolveNow on the Resolver with exponential backoff.
|
// ResolveNow on the Resolver with exponential backoff.
|
||||||
|
|
|
@ -22,11 +22,9 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
|
||||||
|
|
||||||
"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"
|
||||||
|
@ -41,37 +39,6 @@ type ccResolverWrapper struct {
|
||||||
resolver resolver.Resolver
|
resolver resolver.Resolver
|
||||||
done *grpcsync.Event
|
done *grpcsync.Event
|
||||||
curState resolver.State
|
curState resolver.State
|
||||||
|
|
||||||
pollingMu sync.Mutex
|
|
||||||
polling chan struct{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 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 to build a Resolver and
|
// newCCResolverWrapper uses the resolver.Builder to build a Resolver and
|
||||||
|
@ -122,73 +89,27 @@ func (ccr *ccResolverWrapper) close() {
|
||||||
ccr.resolverMu.Unlock()
|
ccr.resolverMu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
// poll begins or ends asynchronous polling of the resolver based on whether
|
func (ccr *ccResolverWrapper) UpdateState(s resolver.State) error {
|
||||||
// err is ErrBadResolverState.
|
|
||||||
func (ccr *ccResolverWrapper) poll(err error) {
|
|
||||||
ccr.pollingMu.Lock()
|
|
||||||
defer ccr.pollingMu.Unlock()
|
|
||||||
if err != balancer.ErrBadResolverState {
|
|
||||||
// stop polling
|
|
||||||
if ccr.polling != nil {
|
|
||||||
close(ccr.polling)
|
|
||||||
ccr.polling = nil
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if ccr.polling != nil {
|
|
||||||
// already polling
|
|
||||||
return
|
|
||||||
}
|
|
||||||
p := make(chan struct{})
|
|
||||||
ccr.polling = p
|
|
||||||
go func() {
|
|
||||||
for i := 0; ; i++ {
|
|
||||||
ccr.resolveNow(resolver.ResolveNowOptions{})
|
|
||||||
t := time.NewTimer(ccr.cc.dopts.resolveNowBackoff(i))
|
|
||||||
select {
|
|
||||||
case <-p:
|
|
||||||
t.Stop()
|
|
||||||
return
|
|
||||||
case <-ccr.done.Done():
|
|
||||||
// Resolver has been closed.
|
|
||||||
t.Stop()
|
|
||||||
return
|
|
||||||
case <-t.C:
|
|
||||||
select {
|
|
||||||
case <-p:
|
|
||||||
return
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
// Timer expired; re-resolve.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ccr *ccResolverWrapper) UpdateState(s resolver.State) {
|
|
||||||
if ccr.done.HasFired() {
|
if ccr.done.HasFired() {
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
grpclog.Infof("ccResolverWrapper: sending update to cc: %v", s)
|
channelz.Infof(logger, ccr.cc.channelzID, "ccResolverWrapper: sending update to cc: %v", s)
|
||||||
if channelz.IsOn() {
|
if channelz.IsOn() {
|
||||||
ccr.addChannelzTraceEvent(s)
|
ccr.addChannelzTraceEvent(s)
|
||||||
}
|
}
|
||||||
ccr.curState = s
|
ccr.curState = s
|
||||||
ccr.poll(ccr.cc.updateResolverState(ccr.curState, nil))
|
if err := ccr.cc.updateResolverState(ccr.curState, nil); err == balancer.ErrBadResolverState {
|
||||||
|
return balancer.ErrBadResolverState
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ccr *ccResolverWrapper) ReportError(err error) {
|
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(logger, ccr.cc.channelzID, "ccResolverWrapper: reporting error to cc: %v", err)
|
||||||
if channelz.IsOn() {
|
ccr.cc.updateResolverState(resolver.State{}, err)
|
||||||
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))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAddress is called by the resolver implementation to send addresses to gRPC.
|
// NewAddress is called by the resolver implementation to send addresses to gRPC.
|
||||||
|
@ -196,12 +117,12 @@ 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(logger, 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})
|
||||||
}
|
}
|
||||||
ccr.curState.Addresses = addrs
|
ccr.curState.Addresses = addrs
|
||||||
ccr.poll(ccr.cc.updateResolverState(ccr.curState, nil))
|
ccr.cc.updateResolverState(ccr.curState, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewServiceConfig is called by the resolver implementation to send service
|
// NewServiceConfig is called by the resolver implementation to send service
|
||||||
|
@ -210,28 +131,21 @@ 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(logger, ccr.cc.channelzID, "ccResolverWrapper: got new service config: %v", sc)
|
||||||
if ccr.cc.dopts.disableServiceConfig {
|
if ccr.cc.dopts.disableServiceConfig {
|
||||||
grpclog.Infof("Service config lookups disabled; ignoring config")
|
channelz.Info(logger, ccr.cc.channelzID, "Service config lookups disabled; ignoring config")
|
||||||
return
|
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(logger, 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)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if channelz.IsOn() {
|
if channelz.IsOn() {
|
||||||
ccr.addChannelzTraceEvent(resolver.State{Addresses: ccr.curState.Addresses, ServiceConfig: scpr})
|
ccr.addChannelzTraceEvent(resolver.State{Addresses: ccr.curState.Addresses, ServiceConfig: scpr})
|
||||||
}
|
}
|
||||||
ccr.curState.ServiceConfig = scpr
|
ccr.curState.ServiceConfig = scpr
|
||||||
ccr.poll(ccr.cc.updateResolverState(ccr.curState, nil))
|
ccr.cc.updateResolverState(ccr.curState, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ccr *ccResolverWrapper) ParseServiceConfig(scJSON string) *serviceconfig.ParseResult {
|
func (ccr *ccResolverWrapper) ParseServiceConfig(scJSON string) *serviceconfig.ParseResult {
|
||||||
|
@ -256,8 +170,8 @@ 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(logger, 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,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,6 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math"
|
"math"
|
||||||
"net/url"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
@ -155,7 +154,6 @@ func (d *gzipDecompressor) Type() string {
|
||||||
type callInfo struct {
|
type callInfo struct {
|
||||||
compressorType string
|
compressorType string
|
||||||
failFast bool
|
failFast bool
|
||||||
stream ClientStream
|
|
||||||
maxReceiveMessageSize *int
|
maxReceiveMessageSize *int
|
||||||
maxSendMessageSize *int
|
maxSendMessageSize *int
|
||||||
creds credentials.PerRPCCredentials
|
creds credentials.PerRPCCredentials
|
||||||
|
@ -180,7 +178,7 @@ type CallOption interface {
|
||||||
|
|
||||||
// after is called after the call has completed. after cannot return an
|
// after is called after the call has completed. after cannot return an
|
||||||
// error, so any failures should be reported via output parameters.
|
// error, so any failures should be reported via output parameters.
|
||||||
after(*callInfo)
|
after(*callInfo, *csAttempt)
|
||||||
}
|
}
|
||||||
|
|
||||||
// EmptyCallOption does not alter the Call configuration.
|
// EmptyCallOption does not alter the Call configuration.
|
||||||
|
@ -189,7 +187,7 @@ type CallOption interface {
|
||||||
type EmptyCallOption struct{}
|
type EmptyCallOption struct{}
|
||||||
|
|
||||||
func (EmptyCallOption) before(*callInfo) error { return nil }
|
func (EmptyCallOption) before(*callInfo) error { return nil }
|
||||||
func (EmptyCallOption) after(*callInfo) {}
|
func (EmptyCallOption) after(*callInfo, *csAttempt) {}
|
||||||
|
|
||||||
// Header returns a CallOptions that retrieves the header metadata
|
// Header returns a CallOptions that retrieves the header metadata
|
||||||
// for a unary RPC.
|
// for a unary RPC.
|
||||||
|
@ -199,16 +197,18 @@ func Header(md *metadata.MD) CallOption {
|
||||||
|
|
||||||
// HeaderCallOption is a CallOption for collecting response header metadata.
|
// HeaderCallOption is a CallOption for collecting response header metadata.
|
||||||
// The metadata field will be populated *after* the RPC completes.
|
// The metadata field will be populated *after* the RPC completes.
|
||||||
// This is an EXPERIMENTAL API.
|
//
|
||||||
|
// Experimental
|
||||||
|
//
|
||||||
|
// Notice: This type is EXPERIMENTAL and may be changed or removed in a
|
||||||
|
// later release.
|
||||||
type HeaderCallOption struct {
|
type HeaderCallOption struct {
|
||||||
HeaderAddr *metadata.MD
|
HeaderAddr *metadata.MD
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o HeaderCallOption) before(c *callInfo) error { return nil }
|
func (o HeaderCallOption) before(c *callInfo) error { return nil }
|
||||||
func (o HeaderCallOption) after(c *callInfo) {
|
func (o HeaderCallOption) after(c *callInfo, attempt *csAttempt) {
|
||||||
if c.stream != nil {
|
*o.HeaderAddr, _ = attempt.s.Header()
|
||||||
*o.HeaderAddr, _ = c.stream.Header()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trailer returns a CallOptions that retrieves the trailer metadata
|
// Trailer returns a CallOptions that retrieves the trailer metadata
|
||||||
|
@ -219,16 +219,18 @@ func Trailer(md *metadata.MD) CallOption {
|
||||||
|
|
||||||
// TrailerCallOption is a CallOption for collecting response trailer metadata.
|
// TrailerCallOption is a CallOption for collecting response trailer metadata.
|
||||||
// The metadata field will be populated *after* the RPC completes.
|
// The metadata field will be populated *after* the RPC completes.
|
||||||
// This is an EXPERIMENTAL API.
|
//
|
||||||
|
// Experimental
|
||||||
|
//
|
||||||
|
// Notice: This type is EXPERIMENTAL and may be changed or removed in a
|
||||||
|
// later release.
|
||||||
type TrailerCallOption struct {
|
type TrailerCallOption struct {
|
||||||
TrailerAddr *metadata.MD
|
TrailerAddr *metadata.MD
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o TrailerCallOption) before(c *callInfo) error { return nil }
|
func (o TrailerCallOption) before(c *callInfo) error { return nil }
|
||||||
func (o TrailerCallOption) after(c *callInfo) {
|
func (o TrailerCallOption) after(c *callInfo, attempt *csAttempt) {
|
||||||
if c.stream != nil {
|
*o.TrailerAddr = attempt.s.Trailer()
|
||||||
*o.TrailerAddr = c.stream.Trailer()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Peer returns a CallOption that retrieves peer information for a unary RPC.
|
// Peer returns a CallOption that retrieves peer information for a unary RPC.
|
||||||
|
@ -239,18 +241,20 @@ func Peer(p *peer.Peer) CallOption {
|
||||||
|
|
||||||
// PeerCallOption is a CallOption for collecting the identity of the remote
|
// PeerCallOption is a CallOption for collecting the identity of the remote
|
||||||
// peer. The peer field will be populated *after* the RPC completes.
|
// peer. The peer field will be populated *after* the RPC completes.
|
||||||
// This is an EXPERIMENTAL API.
|
//
|
||||||
|
// Experimental
|
||||||
|
//
|
||||||
|
// Notice: This type is EXPERIMENTAL and may be changed or removed in a
|
||||||
|
// later release.
|
||||||
type PeerCallOption struct {
|
type PeerCallOption struct {
|
||||||
PeerAddr *peer.Peer
|
PeerAddr *peer.Peer
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o PeerCallOption) before(c *callInfo) error { return nil }
|
func (o PeerCallOption) before(c *callInfo) error { return nil }
|
||||||
func (o PeerCallOption) after(c *callInfo) {
|
func (o PeerCallOption) after(c *callInfo, attempt *csAttempt) {
|
||||||
if c.stream != nil {
|
if x, ok := peer.FromContext(attempt.s.Context()); ok {
|
||||||
if x, ok := peer.FromContext(c.stream.Context()); ok {
|
|
||||||
*o.PeerAddr = *x
|
*o.PeerAddr = *x
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// WaitForReady configures the action to take when an RPC is attempted on broken
|
// WaitForReady configures the action to take when an RPC is attempted on broken
|
||||||
|
@ -276,7 +280,11 @@ func FailFast(failFast bool) CallOption {
|
||||||
|
|
||||||
// FailFastCallOption is a CallOption for indicating whether an RPC should fail
|
// FailFastCallOption is a CallOption for indicating whether an RPC should fail
|
||||||
// fast or not.
|
// fast or not.
|
||||||
// This is an EXPERIMENTAL API.
|
//
|
||||||
|
// Experimental
|
||||||
|
//
|
||||||
|
// Notice: This type is EXPERIMENTAL and may be changed or removed in a
|
||||||
|
// later release.
|
||||||
type FailFastCallOption struct {
|
type FailFastCallOption struct {
|
||||||
FailFast bool
|
FailFast bool
|
||||||
}
|
}
|
||||||
|
@ -285,16 +293,21 @@ func (o FailFastCallOption) before(c *callInfo) error {
|
||||||
c.failFast = o.FailFast
|
c.failFast = o.FailFast
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
func (o FailFastCallOption) after(c *callInfo) {}
|
func (o FailFastCallOption) after(c *callInfo, attempt *csAttempt) {}
|
||||||
|
|
||||||
// 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.
|
//
|
||||||
|
// Experimental
|
||||||
|
//
|
||||||
|
// Notice: This type is EXPERIMENTAL and may be changed or removed in a
|
||||||
|
// later release.
|
||||||
type MaxRecvMsgSizeCallOption struct {
|
type MaxRecvMsgSizeCallOption struct {
|
||||||
MaxRecvMsgSize int
|
MaxRecvMsgSize int
|
||||||
}
|
}
|
||||||
|
@ -303,16 +316,21 @@ func (o MaxRecvMsgSizeCallOption) before(c *callInfo) error {
|
||||||
c.maxReceiveMessageSize = &o.MaxRecvMsgSize
|
c.maxReceiveMessageSize = &o.MaxRecvMsgSize
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
func (o MaxRecvMsgSizeCallOption) after(c *callInfo) {}
|
func (o MaxRecvMsgSizeCallOption) after(c *callInfo, attempt *csAttempt) {}
|
||||||
|
|
||||||
// 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.
|
//
|
||||||
|
// Experimental
|
||||||
|
//
|
||||||
|
// Notice: This type is EXPERIMENTAL and may be changed or removed in a
|
||||||
|
// later release.
|
||||||
type MaxSendMsgSizeCallOption struct {
|
type MaxSendMsgSizeCallOption struct {
|
||||||
MaxSendMsgSize int
|
MaxSendMsgSize int
|
||||||
}
|
}
|
||||||
|
@ -321,7 +339,7 @@ func (o MaxSendMsgSizeCallOption) before(c *callInfo) error {
|
||||||
c.maxSendMessageSize = &o.MaxSendMsgSize
|
c.maxSendMessageSize = &o.MaxSendMsgSize
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
func (o MaxSendMsgSizeCallOption) after(c *callInfo) {}
|
func (o MaxSendMsgSizeCallOption) after(c *callInfo, attempt *csAttempt) {}
|
||||||
|
|
||||||
// PerRPCCredentials returns a CallOption that sets credentials.PerRPCCredentials
|
// PerRPCCredentials returns a CallOption that sets credentials.PerRPCCredentials
|
||||||
// for a call.
|
// for a call.
|
||||||
|
@ -331,7 +349,11 @@ func PerRPCCredentials(creds credentials.PerRPCCredentials) CallOption {
|
||||||
|
|
||||||
// PerRPCCredsCallOption is a CallOption that indicates the per-RPC
|
// PerRPCCredsCallOption is a CallOption that indicates the per-RPC
|
||||||
// credentials to use for the call.
|
// credentials to use for the call.
|
||||||
// This is an EXPERIMENTAL API.
|
//
|
||||||
|
// Experimental
|
||||||
|
//
|
||||||
|
// Notice: This type is EXPERIMENTAL and may be changed or removed in a
|
||||||
|
// later release.
|
||||||
type PerRPCCredsCallOption struct {
|
type PerRPCCredsCallOption struct {
|
||||||
Creds credentials.PerRPCCredentials
|
Creds credentials.PerRPCCredentials
|
||||||
}
|
}
|
||||||
|
@ -340,19 +362,26 @@ func (o PerRPCCredsCallOption) before(c *callInfo) error {
|
||||||
c.creds = o.Creds
|
c.creds = o.Creds
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
func (o PerRPCCredsCallOption) after(c *callInfo) {}
|
func (o PerRPCCredsCallOption) after(c *callInfo, attempt *csAttempt) {}
|
||||||
|
|
||||||
// UseCompressor returns a CallOption which sets the compressor used when
|
// UseCompressor returns a CallOption which sets the compressor used when
|
||||||
// sending the request. If WithCompressor is also set, UseCompressor has
|
// sending the request. If WithCompressor is also set, UseCompressor has
|
||||||
// higher priority.
|
// higher priority.
|
||||||
//
|
//
|
||||||
// This API is EXPERIMENTAL.
|
// Experimental
|
||||||
|
//
|
||||||
|
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
|
||||||
|
// later release.
|
||||||
func UseCompressor(name string) CallOption {
|
func UseCompressor(name string) CallOption {
|
||||||
return CompressorCallOption{CompressorType: name}
|
return CompressorCallOption{CompressorType: name}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CompressorCallOption is a CallOption that indicates the compressor to use.
|
// CompressorCallOption is a CallOption that indicates the compressor to use.
|
||||||
// This is an EXPERIMENTAL API.
|
//
|
||||||
|
// Experimental
|
||||||
|
//
|
||||||
|
// Notice: This type is EXPERIMENTAL and may be changed or removed in a
|
||||||
|
// later release.
|
||||||
type CompressorCallOption struct {
|
type CompressorCallOption struct {
|
||||||
CompressorType string
|
CompressorType string
|
||||||
}
|
}
|
||||||
|
@ -361,7 +390,7 @@ func (o CompressorCallOption) before(c *callInfo) error {
|
||||||
c.compressorType = o.CompressorType
|
c.compressorType = o.CompressorType
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
func (o CompressorCallOption) after(c *callInfo) {}
|
func (o CompressorCallOption) after(c *callInfo, attempt *csAttempt) {}
|
||||||
|
|
||||||
// CallContentSubtype returns a CallOption that will set the content-subtype
|
// CallContentSubtype returns a CallOption that will set the content-subtype
|
||||||
// for a call. For example, if content-subtype is "json", the Content-Type over
|
// for a call. For example, if content-subtype is "json", the Content-Type over
|
||||||
|
@ -385,7 +414,11 @@ func CallContentSubtype(contentSubtype string) CallOption {
|
||||||
|
|
||||||
// ContentSubtypeCallOption is a CallOption that indicates the content-subtype
|
// ContentSubtypeCallOption is a CallOption that indicates the content-subtype
|
||||||
// used for marshaling messages.
|
// used for marshaling messages.
|
||||||
// This is an EXPERIMENTAL API.
|
//
|
||||||
|
// Experimental
|
||||||
|
//
|
||||||
|
// Notice: This type is EXPERIMENTAL and may be changed or removed in a
|
||||||
|
// later release.
|
||||||
type ContentSubtypeCallOption struct {
|
type ContentSubtypeCallOption struct {
|
||||||
ContentSubtype string
|
ContentSubtype string
|
||||||
}
|
}
|
||||||
|
@ -394,11 +427,12 @@ func (o ContentSubtypeCallOption) before(c *callInfo) error {
|
||||||
c.contentSubtype = o.ContentSubtype
|
c.contentSubtype = o.ContentSubtype
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
func (o ContentSubtypeCallOption) after(c *callInfo) {}
|
func (o ContentSubtypeCallOption) after(c *callInfo, attempt *csAttempt) {}
|
||||||
|
|
||||||
// ForceCodec returns a CallOption that will set the given Codec to be
|
// ForceCodec returns a CallOption that will set codec to be used for all
|
||||||
// used for all request and response messages for a call. The result of calling
|
// request and response messages for a call. The result of calling Name() will
|
||||||
// String() will be used as the content-subtype in a case-insensitive manner.
|
// be used as the content-subtype after converting to lowercase, unless
|
||||||
|
// CallContentSubtype is also used.
|
||||||
//
|
//
|
||||||
// See Content-Type on
|
// See Content-Type on
|
||||||
// https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#requests for
|
// https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#requests for
|
||||||
|
@ -409,7 +443,10 @@ func (o ContentSubtypeCallOption) after(c *callInfo) {}
|
||||||
// This function is provided for advanced users; prefer to use only
|
// This function is provided for advanced users; prefer to use only
|
||||||
// CallContentSubtype to select a registered codec instead.
|
// CallContentSubtype to select a registered codec instead.
|
||||||
//
|
//
|
||||||
// This is an EXPERIMENTAL API.
|
// Experimental
|
||||||
|
//
|
||||||
|
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
|
||||||
|
// later release.
|
||||||
func ForceCodec(codec encoding.Codec) CallOption {
|
func ForceCodec(codec encoding.Codec) CallOption {
|
||||||
return ForceCodecCallOption{Codec: codec}
|
return ForceCodecCallOption{Codec: codec}
|
||||||
}
|
}
|
||||||
|
@ -417,7 +454,10 @@ func ForceCodec(codec encoding.Codec) CallOption {
|
||||||
// ForceCodecCallOption is a CallOption that indicates the codec used for
|
// ForceCodecCallOption is a CallOption that indicates the codec used for
|
||||||
// marshaling messages.
|
// marshaling messages.
|
||||||
//
|
//
|
||||||
// This is an EXPERIMENTAL API.
|
// Experimental
|
||||||
|
//
|
||||||
|
// Notice: This type is EXPERIMENTAL and may be changed or removed in a
|
||||||
|
// later release.
|
||||||
type ForceCodecCallOption struct {
|
type ForceCodecCallOption struct {
|
||||||
Codec encoding.Codec
|
Codec encoding.Codec
|
||||||
}
|
}
|
||||||
|
@ -426,7 +466,7 @@ func (o ForceCodecCallOption) before(c *callInfo) error {
|
||||||
c.codec = o.Codec
|
c.codec = o.Codec
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
func (o ForceCodecCallOption) after(c *callInfo) {}
|
func (o ForceCodecCallOption) after(c *callInfo, attempt *csAttempt) {}
|
||||||
|
|
||||||
// CallCustomCodec behaves like ForceCodec, but accepts a grpc.Codec instead of
|
// CallCustomCodec behaves like ForceCodec, but accepts a grpc.Codec instead of
|
||||||
// an encoding.Codec.
|
// an encoding.Codec.
|
||||||
|
@ -439,7 +479,10 @@ func CallCustomCodec(codec Codec) CallOption {
|
||||||
// CustomCodecCallOption is a CallOption that indicates the codec used for
|
// CustomCodecCallOption is a CallOption that indicates the codec used for
|
||||||
// marshaling messages.
|
// marshaling messages.
|
||||||
//
|
//
|
||||||
// This is an EXPERIMENTAL API.
|
// Experimental
|
||||||
|
//
|
||||||
|
// Notice: This type is EXPERIMENTAL and may be changed or removed in a
|
||||||
|
// later release.
|
||||||
type CustomCodecCallOption struct {
|
type CustomCodecCallOption struct {
|
||||||
Codec Codec
|
Codec Codec
|
||||||
}
|
}
|
||||||
|
@ -448,19 +491,26 @@ func (o CustomCodecCallOption) before(c *callInfo) error {
|
||||||
c.codec = o.Codec
|
c.codec = o.Codec
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
func (o CustomCodecCallOption) after(c *callInfo) {}
|
func (o CustomCodecCallOption) after(c *callInfo, attempt *csAttempt) {}
|
||||||
|
|
||||||
// MaxRetryRPCBufferSize returns a CallOption that limits the amount of memory
|
// MaxRetryRPCBufferSize returns a CallOption that limits the amount of memory
|
||||||
// used for buffering this RPC's requests for retry purposes.
|
// used for buffering this RPC's requests for retry purposes.
|
||||||
//
|
//
|
||||||
// This API is EXPERIMENTAL.
|
// Experimental
|
||||||
|
//
|
||||||
|
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
|
||||||
|
// later release.
|
||||||
func MaxRetryRPCBufferSize(bytes int) CallOption {
|
func MaxRetryRPCBufferSize(bytes int) CallOption {
|
||||||
return MaxRetryRPCBufferSizeCallOption{bytes}
|
return MaxRetryRPCBufferSizeCallOption{bytes}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MaxRetryRPCBufferSizeCallOption is a CallOption indicating the amount of
|
// MaxRetryRPCBufferSizeCallOption is a CallOption indicating the amount of
|
||||||
// memory to be used for caching this RPC for retry purposes.
|
// memory to be used for caching this RPC for retry purposes.
|
||||||
// This is an EXPERIMENTAL API.
|
//
|
||||||
|
// Experimental
|
||||||
|
//
|
||||||
|
// Notice: This type is EXPERIMENTAL and may be changed or removed in a
|
||||||
|
// later release.
|
||||||
type MaxRetryRPCBufferSizeCallOption struct {
|
type MaxRetryRPCBufferSizeCallOption struct {
|
||||||
MaxRetryRPCBufferSize int
|
MaxRetryRPCBufferSize int
|
||||||
}
|
}
|
||||||
|
@ -469,7 +519,7 @@ func (o MaxRetryRPCBufferSizeCallOption) before(c *callInfo) error {
|
||||||
c.maxRetryRPCBufferSize = o.MaxRetryRPCBufferSize
|
c.maxRetryRPCBufferSize = o.MaxRetryRPCBufferSize
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
func (o MaxRetryRPCBufferSizeCallOption) after(c *callInfo) {}
|
func (o MaxRetryRPCBufferSizeCallOption) after(c *callInfo, attempt *csAttempt) {}
|
||||||
|
|
||||||
// The format of the payload: compressed or not?
|
// The format of the payload: compressed or not?
|
||||||
type payloadFormat uint8
|
type payloadFormat uint8
|
||||||
|
@ -804,7 +854,17 @@ func toRPCErr(err error) error {
|
||||||
// setCallInfoCodec should only be called after CallOptions have been applied.
|
// setCallInfoCodec should only be called after CallOptions have been applied.
|
||||||
func setCallInfoCodec(c *callInfo) error {
|
func setCallInfoCodec(c *callInfo) error {
|
||||||
if c.codec != nil {
|
if c.codec != nil {
|
||||||
// codec was already set by a CallOption; use it.
|
// codec was already set by a CallOption; use it, but set the content
|
||||||
|
// subtype if it is not set.
|
||||||
|
if c.contentSubtype == "" {
|
||||||
|
// c.codec is a baseCodec to hide the difference between grpc.Codec and
|
||||||
|
// encoding.Codec (Name vs. String method name). We only support
|
||||||
|
// setting content subtype from encoding.Codec to avoid a behavior
|
||||||
|
// change with the deprecated version.
|
||||||
|
if ec, ok := c.codec.(encoding.Codec); ok {
|
||||||
|
c.contentSubtype = strings.ToLower(ec.Name())
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -822,40 +882,6 @@ func setCallInfoCodec(c *callInfo) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseDialTarget returns the network and address to pass to dialer
|
|
||||||
func parseDialTarget(target string) (net string, addr string) {
|
|
||||||
net = "tcp"
|
|
||||||
|
|
||||||
m1 := strings.Index(target, ":")
|
|
||||||
m2 := strings.Index(target, ":/")
|
|
||||||
|
|
||||||
// handle unix:addr which will fail with url.Parse
|
|
||||||
if m1 >= 0 && m2 < 0 {
|
|
||||||
if n := target[0:m1]; n == "unix" {
|
|
||||||
net = n
|
|
||||||
addr = target[m1+1:]
|
|
||||||
return net, addr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if m2 >= 0 {
|
|
||||||
t, err := url.Parse(target)
|
|
||||||
if err != nil {
|
|
||||||
return net, target
|
|
||||||
}
|
|
||||||
scheme := t.Scheme
|
|
||||||
addr = t.Path
|
|
||||||
if scheme == "unix" {
|
|
||||||
net = scheme
|
|
||||||
if addr == "" {
|
|
||||||
addr = t.Host
|
|
||||||
}
|
|
||||||
return net, addr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return net, target
|
|
||||||
}
|
|
||||||
|
|
||||||
// channelzData is used to store channelz related data for ClientConn, addrConn and Server.
|
// channelzData is used to store channelz related data for ClientConn, addrConn and Server.
|
||||||
// These fields cannot be embedded in the original structs (e.g. ClientConn), since to do atomic
|
// These fields cannot be embedded in the original structs (e.g. ClientConn), since to do atomic
|
||||||
// operation on int64 variable on 32-bit machine, user is responsible to enforce memory alignment.
|
// operation on int64 variable on 32-bit machine, user is responsible to enforce memory alignment.
|
||||||
|
@ -871,10 +897,9 @@ 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 6.
|
// support package version is 7.
|
||||||
//
|
//
|
||||||
// Older versions are kept for compatibility. They may be removed if
|
// Older versions are kept for compatibility.
|
||||||
// compatibility cannot be maintained.
|
|
||||||
//
|
//
|
||||||
// These constants should not be referenced from any other code.
|
// These constants should not be referenced from any other code.
|
||||||
const (
|
const (
|
||||||
|
@ -882,6 +907,7 @@ const (
|
||||||
SupportPackageIsVersion4 = true
|
SupportPackageIsVersion4 = true
|
||||||
SupportPackageIsVersion5 = true
|
SupportPackageIsVersion5 = true
|
||||||
SupportPackageIsVersion6 = true
|
SupportPackageIsVersion6 = true
|
||||||
|
SupportPackageIsVersion7 = true
|
||||||
)
|
)
|
||||||
|
|
||||||
const grpcUA = "grpc-go/" + Version
|
const grpcUA = "grpc-go/" + Version
|
||||||
|
|
|
@ -40,8 +40,10 @@ import (
|
||||||
"google.golang.org/grpc/encoding"
|
"google.golang.org/grpc/encoding"
|
||||||
"google.golang.org/grpc/encoding/proto"
|
"google.golang.org/grpc/encoding/proto"
|
||||||
"google.golang.org/grpc/grpclog"
|
"google.golang.org/grpc/grpclog"
|
||||||
|
"google.golang.org/grpc/internal"
|
||||||
"google.golang.org/grpc/internal/binarylog"
|
"google.golang.org/grpc/internal/binarylog"
|
||||||
"google.golang.org/grpc/internal/channelz"
|
"google.golang.org/grpc/internal/channelz"
|
||||||
|
"google.golang.org/grpc/internal/grpcrand"
|
||||||
"google.golang.org/grpc/internal/grpcsync"
|
"google.golang.org/grpc/internal/grpcsync"
|
||||||
"google.golang.org/grpc/internal/transport"
|
"google.golang.org/grpc/internal/transport"
|
||||||
"google.golang.org/grpc/keepalive"
|
"google.golang.org/grpc/keepalive"
|
||||||
|
@ -55,9 +57,26 @@ import (
|
||||||
const (
|
const (
|
||||||
defaultServerMaxReceiveMessageSize = 1024 * 1024 * 4
|
defaultServerMaxReceiveMessageSize = 1024 * 1024 * 4
|
||||||
defaultServerMaxSendMessageSize = math.MaxInt32
|
defaultServerMaxSendMessageSize = math.MaxInt32
|
||||||
|
|
||||||
|
// Server transports are tracked in a map which is keyed on listener
|
||||||
|
// address. For regular gRPC traffic, connections are accepted in Serve()
|
||||||
|
// through a call to Accept(), and we use the actual listener address as key
|
||||||
|
// when we add it to the map. But for connections received through
|
||||||
|
// ServeHTTP(), we do not have a listener and hence use this dummy value.
|
||||||
|
listenerAddressForServeHTTP = "listenerAddressForServeHTTP"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
internal.GetServerCredentials = func(srv *Server) credentials.TransportCredentials {
|
||||||
|
return srv.opts.creds
|
||||||
|
}
|
||||||
|
internal.DrainServerTransports = func(srv *Server, addr string) {
|
||||||
|
srv.drainServerTransports(addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var statusOK = status.New(codes.OK, "")
|
var statusOK = status.New(codes.OK, "")
|
||||||
|
var logger = grpclog.Component("core")
|
||||||
|
|
||||||
type methodHandler func(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor UnaryServerInterceptor) (interface{}, error)
|
type methodHandler func(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor UnaryServerInterceptor) (interface{}, error)
|
||||||
|
|
||||||
|
@ -78,26 +97,36 @@ type ServiceDesc struct {
|
||||||
Metadata interface{}
|
Metadata interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// service consists of the information of the server serving this service and
|
// serviceInfo wraps information about a service. It is very similar to
|
||||||
// the methods in this service.
|
// ServiceDesc and is constructed from it for internal purposes.
|
||||||
type service struct {
|
type serviceInfo struct {
|
||||||
server interface{} // the server for service methods
|
// Contains the implementation for the methods in this service.
|
||||||
md map[string]*MethodDesc
|
serviceImpl interface{}
|
||||||
sd map[string]*StreamDesc
|
methods map[string]*MethodDesc
|
||||||
|
streams map[string]*StreamDesc
|
||||||
mdata interface{}
|
mdata interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type serverWorkerData struct {
|
||||||
|
st transport.ServerTransport
|
||||||
|
wg *sync.WaitGroup
|
||||||
|
stream *transport.Stream
|
||||||
|
}
|
||||||
|
|
||||||
// Server is a gRPC server to serve RPC requests.
|
// Server is a gRPC server to serve RPC requests.
|
||||||
type Server struct {
|
type Server struct {
|
||||||
opts serverOptions
|
opts serverOptions
|
||||||
|
|
||||||
mu sync.Mutex // guards following
|
mu sync.Mutex // guards following
|
||||||
lis map[net.Listener]bool
|
lis map[net.Listener]bool
|
||||||
conns map[transport.ServerTransport]bool
|
// conns contains all active server transports. It is a map keyed on a
|
||||||
|
// listener address with the value being the set of active transports
|
||||||
|
// belonging to that listener.
|
||||||
|
conns map[string]map[transport.ServerTransport]bool
|
||||||
serve bool
|
serve bool
|
||||||
drain bool
|
drain bool
|
||||||
cv *sync.Cond // signaled when connections close for GracefulStop
|
cv *sync.Cond // signaled when connections close for GracefulStop
|
||||||
m map[string]*service // service name -> service info
|
services map[string]*serviceInfo // service name -> service info
|
||||||
events trace.EventLog
|
events trace.EventLog
|
||||||
|
|
||||||
quit *grpcsync.Event
|
quit *grpcsync.Event
|
||||||
|
@ -107,6 +136,8 @@ type Server struct {
|
||||||
|
|
||||||
channelzID int64 // channelz unique identification number
|
channelzID int64 // channelz unique identification number
|
||||||
czData *channelzData
|
czData *channelzData
|
||||||
|
|
||||||
|
serverWorkerChannels []chan *serverWorkerData
|
||||||
}
|
}
|
||||||
|
|
||||||
type serverOptions struct {
|
type serverOptions struct {
|
||||||
|
@ -116,6 +147,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
|
||||||
|
@ -131,6 +164,7 @@ type serverOptions struct {
|
||||||
connectionTimeout time.Duration
|
connectionTimeout time.Duration
|
||||||
maxHeaderListSize *uint32
|
maxHeaderListSize *uint32
|
||||||
headerTableSize *uint32
|
headerTableSize *uint32
|
||||||
|
numServerWorkers uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
var defaultServerOptions = serverOptions{
|
var defaultServerOptions = serverOptions{
|
||||||
|
@ -149,7 +183,10 @@ type ServerOption interface {
|
||||||
// EmptyServerOption does not alter the server configuration. It can be embedded
|
// EmptyServerOption does not alter the server configuration. It can be embedded
|
||||||
// in another structure to build custom server options.
|
// in another structure to build custom server options.
|
||||||
//
|
//
|
||||||
// This API is EXPERIMENTAL.
|
// Experimental
|
||||||
|
//
|
||||||
|
// Notice: This type is EXPERIMENTAL and may be changed or removed in a
|
||||||
|
// later release.
|
||||||
type EmptyServerOption struct{}
|
type EmptyServerOption struct{}
|
||||||
|
|
||||||
func (EmptyServerOption) apply(*serverOptions) {}
|
func (EmptyServerOption) apply(*serverOptions) {}
|
||||||
|
@ -211,7 +248,7 @@ func InitialConnWindowSize(s int32) ServerOption {
|
||||||
// KeepaliveParams returns a ServerOption that sets keepalive and max-age parameters for the server.
|
// KeepaliveParams returns a ServerOption that sets keepalive and max-age parameters for the server.
|
||||||
func KeepaliveParams(kp keepalive.ServerParameters) ServerOption {
|
func KeepaliveParams(kp keepalive.ServerParameters) ServerOption {
|
||||||
if kp.Time > 0 && kp.Time < time.Second {
|
if kp.Time > 0 && kp.Time < time.Second {
|
||||||
grpclog.Warning("Adjusting keepalive ping interval to minimum period of 1s")
|
logger.Warning("Adjusting keepalive ping interval to minimum period of 1s")
|
||||||
kp.Time = time.Second
|
kp.Time = time.Second
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -230,19 +267,55 @@ func KeepaliveEnforcementPolicy(kep keepalive.EnforcementPolicy) ServerOption {
|
||||||
// CustomCodec returns a ServerOption that sets a codec for message marshaling and unmarshaling.
|
// CustomCodec returns a ServerOption that sets a codec for message marshaling and unmarshaling.
|
||||||
//
|
//
|
||||||
// This will override any lookups by content-subtype for Codecs registered with RegisterCodec.
|
// This will override any lookups by content-subtype for Codecs registered with RegisterCodec.
|
||||||
|
//
|
||||||
|
// Deprecated: register codecs using encoding.RegisterCodec. The server will
|
||||||
|
// automatically use registered codecs based on the incoming requests' headers.
|
||||||
|
// See also
|
||||||
|
// https://github.com/grpc/grpc-go/blob/master/Documentation/encoding.md#using-a-codec.
|
||||||
|
// Will be supported throughout 1.x.
|
||||||
func CustomCodec(codec Codec) ServerOption {
|
func CustomCodec(codec Codec) ServerOption {
|
||||||
return newFuncServerOption(func(o *serverOptions) {
|
return newFuncServerOption(func(o *serverOptions) {
|
||||||
o.codec = codec
|
o.codec = codec
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ForceServerCodec returns a ServerOption that sets a codec for message
|
||||||
|
// marshaling and unmarshaling.
|
||||||
|
//
|
||||||
|
// This will override any lookups by content-subtype for Codecs registered
|
||||||
|
// with RegisterCodec.
|
||||||
|
//
|
||||||
|
// See Content-Type on
|
||||||
|
// https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#requests for
|
||||||
|
// more details. Also see the documentation on RegisterCodec and
|
||||||
|
// CallContentSubtype for more details on the interaction between encoding.Codec
|
||||||
|
// and content-subtype.
|
||||||
|
//
|
||||||
|
// This function is provided for advanced users; prefer to register codecs
|
||||||
|
// using encoding.RegisterCodec.
|
||||||
|
// The server will automatically use registered codecs based on the incoming
|
||||||
|
// requests' headers. See also
|
||||||
|
// https://github.com/grpc/grpc-go/blob/master/Documentation/encoding.md#using-a-codec.
|
||||||
|
// Will be supported throughout 1.x.
|
||||||
|
//
|
||||||
|
// Experimental
|
||||||
|
//
|
||||||
|
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
|
||||||
|
// later release.
|
||||||
|
func ForceServerCodec(codec encoding.Codec) ServerOption {
|
||||||
|
return newFuncServerOption(func(o *serverOptions) {
|
||||||
|
o.codec = codec
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// RPCCompressor returns a ServerOption that sets a compressor for outbound
|
// RPCCompressor returns a ServerOption that sets a compressor for outbound
|
||||||
// messages. For backward compatibility, all outbound messages will be sent
|
// messages. For backward compatibility, all outbound messages will be sent
|
||||||
// using this compressor, regardless of incoming message compression. By
|
// using this compressor, regardless of incoming message compression. By
|
||||||
// default, server messages will be sent using the same compressor with which
|
// default, server messages will be sent using the same compressor with which
|
||||||
// request messages were sent.
|
// request messages were sent.
|
||||||
//
|
//
|
||||||
// Deprecated: use encoding.RegisterCompressor instead.
|
// Deprecated: use encoding.RegisterCompressor instead. Will be supported
|
||||||
|
// throughout 1.x.
|
||||||
func RPCCompressor(cp Compressor) ServerOption {
|
func RPCCompressor(cp Compressor) ServerOption {
|
||||||
return newFuncServerOption(func(o *serverOptions) {
|
return newFuncServerOption(func(o *serverOptions) {
|
||||||
o.cp = cp
|
o.cp = cp
|
||||||
|
@ -253,7 +326,8 @@ func RPCCompressor(cp Compressor) ServerOption {
|
||||||
// messages. It has higher priority than decompressors registered via
|
// messages. It has higher priority than decompressors registered via
|
||||||
// encoding.RegisterCompressor.
|
// encoding.RegisterCompressor.
|
||||||
//
|
//
|
||||||
// Deprecated: use encoding.RegisterCompressor instead.
|
// Deprecated: use encoding.RegisterCompressor instead. Will be supported
|
||||||
|
// throughout 1.x.
|
||||||
func RPCDecompressor(dc Decompressor) ServerOption {
|
func RPCDecompressor(dc Decompressor) ServerOption {
|
||||||
return newFuncServerOption(func(o *serverOptions) {
|
return newFuncServerOption(func(o *serverOptions) {
|
||||||
o.dc = dc
|
o.dc = dc
|
||||||
|
@ -263,7 +337,7 @@ func RPCDecompressor(dc Decompressor) ServerOption {
|
||||||
// MaxMsgSize returns a ServerOption to set the max message size in bytes the server can receive.
|
// MaxMsgSize returns a ServerOption to set the max message size in bytes the server can receive.
|
||||||
// If this is not set, gRPC uses the default limit.
|
// If this is not set, gRPC uses the default limit.
|
||||||
//
|
//
|
||||||
// Deprecated: use MaxRecvMsgSize instead.
|
// Deprecated: use MaxRecvMsgSize instead. Will be supported throughout 1.x.
|
||||||
func MaxMsgSize(m int) ServerOption {
|
func MaxMsgSize(m int) ServerOption {
|
||||||
return MaxRecvMsgSize(m)
|
return MaxRecvMsgSize(m)
|
||||||
}
|
}
|
||||||
|
@ -311,6 +385,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,8 +406,23 @@ func StreamInterceptor(i StreamServerInterceptor) ServerOption {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ChainStreamInterceptor returns a ServerOption that specifies the chained interceptor
|
||||||
|
// for streaming 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.
|
||||||
|
//
|
||||||
|
// Experimental
|
||||||
|
//
|
||||||
|
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
|
||||||
|
// later release.
|
||||||
func InTapHandle(h tap.ServerInHandle) ServerOption {
|
func InTapHandle(h tap.ServerInHandle) ServerOption {
|
||||||
return newFuncServerOption(func(o *serverOptions) {
|
return newFuncServerOption(func(o *serverOptions) {
|
||||||
if o.inTapHandle != nil {
|
if o.inTapHandle != nil {
|
||||||
|
@ -363,7 +462,10 @@ func UnknownServiceHandler(streamHandler StreamHandler) ServerOption {
|
||||||
// new connections. If this is not set, the default is 120 seconds. A zero or
|
// new connections. If this is not set, the default is 120 seconds. A zero or
|
||||||
// negative value will result in an immediate timeout.
|
// negative value will result in an immediate timeout.
|
||||||
//
|
//
|
||||||
// This API is EXPERIMENTAL.
|
// Experimental
|
||||||
|
//
|
||||||
|
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
|
||||||
|
// later release.
|
||||||
func ConnectionTimeout(d time.Duration) ServerOption {
|
func ConnectionTimeout(d time.Duration) ServerOption {
|
||||||
return newFuncServerOption(func(o *serverOptions) {
|
return newFuncServerOption(func(o *serverOptions) {
|
||||||
o.connectionTimeout = d
|
o.connectionTimeout = d
|
||||||
|
@ -381,13 +483,79 @@ func MaxHeaderListSize(s uint32) ServerOption {
|
||||||
// HeaderTableSize returns a ServerOption that sets the size of dynamic
|
// HeaderTableSize returns a ServerOption that sets the size of dynamic
|
||||||
// header table for stream.
|
// header table for stream.
|
||||||
//
|
//
|
||||||
// This API is EXPERIMENTAL.
|
// Experimental
|
||||||
|
//
|
||||||
|
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
|
||||||
|
// later release.
|
||||||
func HeaderTableSize(s uint32) ServerOption {
|
func HeaderTableSize(s uint32) ServerOption {
|
||||||
return newFuncServerOption(func(o *serverOptions) {
|
return newFuncServerOption(func(o *serverOptions) {
|
||||||
o.headerTableSize = &s
|
o.headerTableSize = &s
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NumStreamWorkers returns a ServerOption that sets the number of worker
|
||||||
|
// goroutines that should be used to process incoming streams. Setting this to
|
||||||
|
// zero (default) will disable workers and spawn a new goroutine for each
|
||||||
|
// stream.
|
||||||
|
//
|
||||||
|
// Experimental
|
||||||
|
//
|
||||||
|
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
|
||||||
|
// later release.
|
||||||
|
func NumStreamWorkers(numServerWorkers uint32) ServerOption {
|
||||||
|
// TODO: If/when this API gets stabilized (i.e. stream workers become the
|
||||||
|
// only way streams are processed), change the behavior of the zero value to
|
||||||
|
// a sane default. Preliminary experiments suggest that a value equal to the
|
||||||
|
// number of CPUs available is most performant; requires thorough testing.
|
||||||
|
return newFuncServerOption(func(o *serverOptions) {
|
||||||
|
o.numServerWorkers = numServerWorkers
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// serverWorkerResetThreshold defines how often the stack must be reset. Every
|
||||||
|
// N requests, by spawning a new goroutine in its place, a worker can reset its
|
||||||
|
// stack so that large stacks don't live in memory forever. 2^16 should allow
|
||||||
|
// each goroutine stack to live for at least a few seconds in a typical
|
||||||
|
// workload (assuming a QPS of a few thousand requests/sec).
|
||||||
|
const serverWorkerResetThreshold = 1 << 16
|
||||||
|
|
||||||
|
// serverWorkers blocks on a *transport.Stream channel forever and waits for
|
||||||
|
// data to be fed by serveStreams. This allows different requests to be
|
||||||
|
// processed by the same goroutine, removing the need for expensive stack
|
||||||
|
// re-allocations (see the runtime.morestack problem [1]).
|
||||||
|
//
|
||||||
|
// [1] https://github.com/golang/go/issues/18138
|
||||||
|
func (s *Server) serverWorker(ch chan *serverWorkerData) {
|
||||||
|
// To make sure all server workers don't reset at the same time, choose a
|
||||||
|
// random number of iterations before resetting.
|
||||||
|
threshold := serverWorkerResetThreshold + grpcrand.Intn(serverWorkerResetThreshold)
|
||||||
|
for completed := 0; completed < threshold; completed++ {
|
||||||
|
data, ok := <-ch
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.handleStream(data.st, data.stream, s.traceInfo(data.st, data.stream))
|
||||||
|
data.wg.Done()
|
||||||
|
}
|
||||||
|
go s.serverWorker(ch)
|
||||||
|
}
|
||||||
|
|
||||||
|
// initServerWorkers creates worker goroutines and channels to process incoming
|
||||||
|
// connections to reduce the time spent overall on runtime.morestack.
|
||||||
|
func (s *Server) initServerWorkers() {
|
||||||
|
s.serverWorkerChannels = make([]chan *serverWorkerData, s.opts.numServerWorkers)
|
||||||
|
for i := uint32(0); i < s.opts.numServerWorkers; i++ {
|
||||||
|
s.serverWorkerChannels[i] = make(chan *serverWorkerData)
|
||||||
|
go s.serverWorker(s.serverWorkerChannels[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) stopServerWorkers() {
|
||||||
|
for i := uint32(0); i < s.opts.numServerWorkers; i++ {
|
||||||
|
close(s.serverWorkerChannels[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// NewServer creates a gRPC server which has no service registered and has not
|
// NewServer creates a gRPC server which has no service registered and has not
|
||||||
// started to accept requests yet.
|
// started to accept requests yet.
|
||||||
func NewServer(opt ...ServerOption) *Server {
|
func NewServer(opt ...ServerOption) *Server {
|
||||||
|
@ -398,18 +566,24 @@ func NewServer(opt ...ServerOption) *Server {
|
||||||
s := &Server{
|
s := &Server{
|
||||||
lis: make(map[net.Listener]bool),
|
lis: make(map[net.Listener]bool),
|
||||||
opts: opts,
|
opts: opts,
|
||||||
conns: make(map[transport.ServerTransport]bool),
|
conns: make(map[string]map[transport.ServerTransport]bool),
|
||||||
m: make(map[string]*service),
|
services: make(map[string]*serviceInfo),
|
||||||
quit: grpcsync.NewEvent(),
|
quit: grpcsync.NewEvent(),
|
||||||
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)
|
||||||
s.events = trace.NewEventLog("grpc.Server", fmt.Sprintf("%s:%d", file, line))
|
s.events = trace.NewEventLog("grpc.Server", fmt.Sprintf("%s:%d", file, line))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if s.opts.numServerWorkers > 0 {
|
||||||
|
s.initServerWorkers()
|
||||||
|
}
|
||||||
|
|
||||||
if channelz.IsOn() {
|
if channelz.IsOn() {
|
||||||
s.channelzID = channelz.RegisterServer(&channelzServer{s}, "")
|
s.channelzID = channelz.RegisterServer(&channelzServer{s}, "")
|
||||||
}
|
}
|
||||||
|
@ -432,14 +606,29 @@ func (s *Server) errorf(format string, a ...interface{}) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ServiceRegistrar wraps a single method that supports service registration. It
|
||||||
|
// enables users to pass concrete types other than grpc.Server to the service
|
||||||
|
// registration methods exported by the IDL generated code.
|
||||||
|
type ServiceRegistrar interface {
|
||||||
|
// RegisterService registers a service and its implementation to the
|
||||||
|
// concrete type implementing this interface. It may not be called
|
||||||
|
// once the server has started serving.
|
||||||
|
// desc describes the service and its methods and handlers. impl is the
|
||||||
|
// service implementation which is passed to the method handlers.
|
||||||
|
RegisterService(desc *ServiceDesc, impl interface{})
|
||||||
|
}
|
||||||
|
|
||||||
// RegisterService registers a service and its implementation to the gRPC
|
// RegisterService registers a service and its implementation to the gRPC
|
||||||
// server. It is called from the IDL generated code. This must be called before
|
// server. It is called from the IDL generated code. This must be called before
|
||||||
// invoking Serve.
|
// invoking Serve. If ss is non-nil (for legacy code), its type is checked to
|
||||||
|
// ensure it implements sd.HandlerType.
|
||||||
func (s *Server) RegisterService(sd *ServiceDesc, ss interface{}) {
|
func (s *Server) RegisterService(sd *ServiceDesc, ss interface{}) {
|
||||||
|
if ss != nil {
|
||||||
ht := reflect.TypeOf(sd.HandlerType).Elem()
|
ht := reflect.TypeOf(sd.HandlerType).Elem()
|
||||||
st := reflect.TypeOf(ss)
|
st := reflect.TypeOf(ss)
|
||||||
if !st.Implements(ht) {
|
if !st.Implements(ht) {
|
||||||
grpclog.Fatalf("grpc: Server.RegisterService found the handler of type %v that does not satisfy %v", st, ht)
|
logger.Fatalf("grpc: Server.RegisterService found the handler of type %v that does not satisfy %v", st, ht)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
s.register(sd, ss)
|
s.register(sd, ss)
|
||||||
}
|
}
|
||||||
|
@ -449,26 +638,26 @@ func (s *Server) register(sd *ServiceDesc, ss interface{}) {
|
||||||
defer s.mu.Unlock()
|
defer s.mu.Unlock()
|
||||||
s.printf("RegisterService(%q)", sd.ServiceName)
|
s.printf("RegisterService(%q)", sd.ServiceName)
|
||||||
if s.serve {
|
if s.serve {
|
||||||
grpclog.Fatalf("grpc: Server.RegisterService after Server.Serve for %q", sd.ServiceName)
|
logger.Fatalf("grpc: Server.RegisterService after Server.Serve for %q", sd.ServiceName)
|
||||||
}
|
}
|
||||||
if _, ok := s.m[sd.ServiceName]; ok {
|
if _, ok := s.services[sd.ServiceName]; ok {
|
||||||
grpclog.Fatalf("grpc: Server.RegisterService found duplicate service registration for %q", sd.ServiceName)
|
logger.Fatalf("grpc: Server.RegisterService found duplicate service registration for %q", sd.ServiceName)
|
||||||
}
|
}
|
||||||
srv := &service{
|
info := &serviceInfo{
|
||||||
server: ss,
|
serviceImpl: ss,
|
||||||
md: make(map[string]*MethodDesc),
|
methods: make(map[string]*MethodDesc),
|
||||||
sd: make(map[string]*StreamDesc),
|
streams: make(map[string]*StreamDesc),
|
||||||
mdata: sd.Metadata,
|
mdata: sd.Metadata,
|
||||||
}
|
}
|
||||||
for i := range sd.Methods {
|
for i := range sd.Methods {
|
||||||
d := &sd.Methods[i]
|
d := &sd.Methods[i]
|
||||||
srv.md[d.MethodName] = d
|
info.methods[d.MethodName] = d
|
||||||
}
|
}
|
||||||
for i := range sd.Streams {
|
for i := range sd.Streams {
|
||||||
d := &sd.Streams[i]
|
d := &sd.Streams[i]
|
||||||
srv.sd[d.StreamName] = d
|
info.streams[d.StreamName] = d
|
||||||
}
|
}
|
||||||
s.m[sd.ServiceName] = srv
|
s.services[sd.ServiceName] = info
|
||||||
}
|
}
|
||||||
|
|
||||||
// MethodInfo contains the information of an RPC including its method name and type.
|
// MethodInfo contains the information of an RPC including its method name and type.
|
||||||
|
@ -492,16 +681,16 @@ type ServiceInfo struct {
|
||||||
// Service names include the package names, in the form of <package>.<service>.
|
// Service names include the package names, in the form of <package>.<service>.
|
||||||
func (s *Server) GetServiceInfo() map[string]ServiceInfo {
|
func (s *Server) GetServiceInfo() map[string]ServiceInfo {
|
||||||
ret := make(map[string]ServiceInfo)
|
ret := make(map[string]ServiceInfo)
|
||||||
for n, srv := range s.m {
|
for n, srv := range s.services {
|
||||||
methods := make([]MethodInfo, 0, len(srv.md)+len(srv.sd))
|
methods := make([]MethodInfo, 0, len(srv.methods)+len(srv.streams))
|
||||||
for m := range srv.md {
|
for m := range srv.methods {
|
||||||
methods = append(methods, MethodInfo{
|
methods = append(methods, MethodInfo{
|
||||||
Name: m,
|
Name: m,
|
||||||
IsClientStream: false,
|
IsClientStream: false,
|
||||||
IsServerStream: false,
|
IsServerStream: false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
for m, d := range srv.sd {
|
for m, d := range srv.streams {
|
||||||
methods = append(methods, MethodInfo{
|
methods = append(methods, MethodInfo{
|
||||||
Name: m,
|
Name: m,
|
||||||
IsClientStream: d.ClientStreams,
|
IsClientStream: d.ClientStreams,
|
||||||
|
@ -636,7 +825,7 @@ func (s *Server) Serve(lis net.Listener) error {
|
||||||
// s.conns before this conn can be added.
|
// s.conns before this conn can be added.
|
||||||
s.serveWG.Add(1)
|
s.serveWG.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
s.handleRawConn(rawConn)
|
s.handleRawConn(lis.Addr().String(), rawConn)
|
||||||
s.serveWG.Done()
|
s.serveWG.Done()
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
@ -644,7 +833,7 @@ func (s *Server) Serve(lis net.Listener) error {
|
||||||
|
|
||||||
// handleRawConn forks a goroutine to handle a just-accepted connection that
|
// handleRawConn forks a goroutine to handle a just-accepted connection that
|
||||||
// has not had any I/O performed on it yet.
|
// has not had any I/O performed on it yet.
|
||||||
func (s *Server) handleRawConn(rawConn net.Conn) {
|
func (s *Server) handleRawConn(lisAddr string, rawConn net.Conn) {
|
||||||
if s.quit.HasFired() {
|
if s.quit.HasFired() {
|
||||||
rawConn.Close()
|
rawConn.Close()
|
||||||
return
|
return
|
||||||
|
@ -658,7 +847,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(logger, 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{})
|
||||||
|
@ -672,15 +861,24 @@ func (s *Server) handleRawConn(rawConn net.Conn) {
|
||||||
}
|
}
|
||||||
|
|
||||||
rawConn.SetDeadline(time.Time{})
|
rawConn.SetDeadline(time.Time{})
|
||||||
if !s.addConn(st) {
|
if !s.addConn(lisAddr, st) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
go func() {
|
go func() {
|
||||||
s.serveStreams(st)
|
s.serveStreams(st)
|
||||||
s.removeConn(st)
|
s.removeConn(lisAddr, st)
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Server) drainServerTransports(addr string) {
|
||||||
|
s.mu.Lock()
|
||||||
|
conns := s.conns[addr]
|
||||||
|
for st := range conns {
|
||||||
|
st.Drain()
|
||||||
|
}
|
||||||
|
s.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
// newHTTP2Transport sets up a http/2 transport (using the
|
// newHTTP2Transport sets up a http/2 transport (using the
|
||||||
// gRPC http2 server transport in transport/http2_server.go).
|
// gRPC http2 server transport in transport/http2_server.go).
|
||||||
func (s *Server) newHTTP2Transport(c net.Conn, authInfo credentials.AuthInfo) transport.ServerTransport {
|
func (s *Server) newHTTP2Transport(c net.Conn, authInfo credentials.AuthInfo) transport.ServerTransport {
|
||||||
|
@ -705,7 +903,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(logger, s.channelzID, "grpc: Server.Serve failed to create ServerTransport: ", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -715,12 +913,27 @@ func (s *Server) newHTTP2Transport(c net.Conn, authInfo credentials.AuthInfo) tr
|
||||||
func (s *Server) serveStreams(st transport.ServerTransport) {
|
func (s *Server) serveStreams(st transport.ServerTransport) {
|
||||||
defer st.Close()
|
defer st.Close()
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
|
var roundRobinCounter uint32
|
||||||
st.HandleStreams(func(stream *transport.Stream) {
|
st.HandleStreams(func(stream *transport.Stream) {
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
|
if s.opts.numServerWorkers > 0 {
|
||||||
|
data := &serverWorkerData{st: st, wg: &wg, stream: stream}
|
||||||
|
select {
|
||||||
|
case s.serverWorkerChannels[atomic.AddUint32(&roundRobinCounter, 1)%s.opts.numServerWorkers] <- data:
|
||||||
|
default:
|
||||||
|
// If all stream workers are busy, fallback to the default code path.
|
||||||
|
go func() {
|
||||||
|
s.handleStream(st, stream, s.traceInfo(st, stream))
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
go func() {
|
go func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
s.handleStream(st, stream, s.traceInfo(st, stream))
|
s.handleStream(st, stream, s.traceInfo(st, stream))
|
||||||
}()
|
}()
|
||||||
|
}
|
||||||
}, func(ctx context.Context, method string) context.Context {
|
}, func(ctx context.Context, method string) context.Context {
|
||||||
if !EnableTracing {
|
if !EnableTracing {
|
||||||
return ctx
|
return ctx
|
||||||
|
@ -755,18 +968,22 @@ var _ http.Handler = (*Server)(nil)
|
||||||
// Note that ServeHTTP uses Go's HTTP/2 server implementation which is totally
|
// Note that ServeHTTP uses Go's HTTP/2 server implementation which is totally
|
||||||
// separate from grpc-go's HTTP/2 server. Performance and features may vary
|
// separate from grpc-go's HTTP/2 server. Performance and features may vary
|
||||||
// between the two paths. ServeHTTP does not support some gRPC features
|
// between the two paths. ServeHTTP does not support some gRPC features
|
||||||
// available through grpc-go's HTTP/2 server, and it is currently EXPERIMENTAL
|
// available through grpc-go's HTTP/2 server.
|
||||||
// and subject to change.
|
//
|
||||||
|
// Experimental
|
||||||
|
//
|
||||||
|
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
|
||||||
|
// later release.
|
||||||
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
st, err := transport.NewServerHandlerTransport(w, r, s.opts.statsHandler)
|
st, err := transport.NewServerHandlerTransport(w, r, s.opts.statsHandler)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !s.addConn(st) {
|
if !s.addConn(listenerAddressForServeHTTP, st) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer s.removeConn(st)
|
defer s.removeConn(listenerAddressForServeHTTP, st)
|
||||||
s.serveStreams(st)
|
s.serveStreams(st)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -794,7 +1011,7 @@ func (s *Server) traceInfo(st transport.ServerTransport, stream *transport.Strea
|
||||||
return trInfo
|
return trInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) addConn(st transport.ServerTransport) bool {
|
func (s *Server) addConn(addr string, st transport.ServerTransport) bool {
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
defer s.mu.Unlock()
|
defer s.mu.Unlock()
|
||||||
if s.conns == nil {
|
if s.conns == nil {
|
||||||
|
@ -806,15 +1023,28 @@ func (s *Server) addConn(st transport.ServerTransport) bool {
|
||||||
// immediately.
|
// immediately.
|
||||||
st.Drain()
|
st.Drain()
|
||||||
}
|
}
|
||||||
s.conns[st] = true
|
|
||||||
|
if s.conns[addr] == nil {
|
||||||
|
// Create a map entry if this is the first connection on this listener.
|
||||||
|
s.conns[addr] = make(map[transport.ServerTransport]bool)
|
||||||
|
}
|
||||||
|
s.conns[addr][st] = true
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) removeConn(st transport.ServerTransport) {
|
func (s *Server) removeConn(addr string, st transport.ServerTransport) {
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
defer s.mu.Unlock()
|
defer s.mu.Unlock()
|
||||||
if s.conns != nil {
|
|
||||||
delete(s.conns, st)
|
conns := s.conns[addr]
|
||||||
|
if conns != nil {
|
||||||
|
delete(conns, st)
|
||||||
|
if len(conns) == 0 {
|
||||||
|
// If the last connection for this address is being removed, also
|
||||||
|
// remove the map entry corresponding to the address. This is used
|
||||||
|
// in GracefulStop() when waiting for all connections to be closed.
|
||||||
|
delete(s.conns, addr)
|
||||||
|
}
|
||||||
s.cv.Broadcast()
|
s.cv.Broadcast()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -844,12 +1074,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(logger, 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(logger, s.channelzID, "grpc: server failed to compress response: ", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
hdr, payload := msgHeader(data, compData)
|
hdr, payload := msgHeader(data, compData)
|
||||||
|
@ -864,7 +1094,41 @@ func (s *Server) sendResponse(t transport.ServerTransport, stream *transport.Str
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.Stream, srv *service, md *MethodDesc, trInfo *traceInfo) (err error) {
|
// chainUnaryServerInterceptors chains all unary server interceptors into one.
|
||||||
|
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, info *serviceInfo, md *MethodDesc, trInfo *traceInfo) (err error) {
|
||||||
sh := s.opts.statsHandler
|
sh := s.opts.statsHandler
|
||||||
if sh != nil || trInfo != nil || channelz.IsOn() {
|
if sh != nil || trInfo != nil || channelz.IsOn() {
|
||||||
if channelz.IsOn() {
|
if channelz.IsOn() {
|
||||||
|
@ -987,10 +1251,8 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.
|
||||||
}
|
}
|
||||||
d, err := recvAndDecompress(&parser{r: stream}, stream, dc, s.opts.maxReceiveMessageSize, payInfo, decomp)
|
d, err := recvAndDecompress(&parser{r: stream}, stream, dc, s.opts.maxReceiveMessageSize, payInfo, decomp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if st, ok := status.FromError(err); ok {
|
if e := t.WriteStatus(stream, status.Convert(err)); e != nil {
|
||||||
if e := t.WriteStatus(stream, st); e != nil {
|
channelz.Warningf(logger, s.channelzID, "grpc: Server.processUnaryRPC failed to write status %v", e)
|
||||||
grpclog.Warningf("grpc: Server.processUnaryRPC failed to write status %v", e)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -1005,7 +1267,7 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.
|
||||||
sh.HandleRPC(stream.Context(), &stats.InPayload{
|
sh.HandleRPC(stream.Context(), &stats.InPayload{
|
||||||
RecvTime: time.Now(),
|
RecvTime: time.Now(),
|
||||||
Payload: v,
|
Payload: v,
|
||||||
WireLength: payInfo.wireLength,
|
WireLength: payInfo.wireLength + headerLen,
|
||||||
Data: d,
|
Data: d,
|
||||||
Length: len(d),
|
Length: len(d),
|
||||||
})
|
})
|
||||||
|
@ -1021,7 +1283,7 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
ctx := NewContextWithServerTransportStream(stream.Context(), stream)
|
ctx := NewContextWithServerTransportStream(stream.Context(), stream)
|
||||||
reply, appErr := md.Handler(srv.server, ctx, df, s.opts.unaryInt)
|
reply, appErr := md.Handler(info.serviceImpl, ctx, df, s.opts.unaryInt)
|
||||||
if appErr != nil {
|
if appErr != nil {
|
||||||
appStatus, ok := status.FromError(appErr)
|
appStatus, ok := status.FromError(appErr)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -1034,7 +1296,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(logger, 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 {
|
||||||
|
@ -1061,9 +1323,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(logger, s.channelzID, "grpc: Server.processUnaryRPC failed to write status: %v", e)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
switch st := err.(type) {
|
switch st := err.(type) {
|
||||||
|
@ -1113,7 +1375,41 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) processStreamingRPC(t transport.ServerTransport, stream *transport.Stream, srv *service, sd *StreamDesc, trInfo *traceInfo) (err error) {
|
// 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, info *serviceInfo, sd *StreamDesc, trInfo *traceInfo) (err error) {
|
||||||
if channelz.IsOn() {
|
if channelz.IsOn() {
|
||||||
s.incrCallsStarted()
|
s.incrCallsStarted()
|
||||||
}
|
}
|
||||||
|
@ -1230,8 +1526,8 @@ func (s *Server) processStreamingRPC(t transport.ServerTransport, stream *transp
|
||||||
}
|
}
|
||||||
var appErr error
|
var appErr error
|
||||||
var server interface{}
|
var server interface{}
|
||||||
if srv != nil {
|
if info != nil {
|
||||||
server = srv.server
|
server = info.serviceImpl
|
||||||
}
|
}
|
||||||
if s.opts.streamInt == nil {
|
if s.opts.streamInt == nil {
|
||||||
appErr = sd.Handler(server, ss)
|
appErr = sd.Handler(server, ss)
|
||||||
|
@ -1297,7 +1593,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(logger, s.channelzID, "grpc: Server.handleStream failed to write status: %v", err)
|
||||||
}
|
}
|
||||||
if trInfo != nil {
|
if trInfo != nil {
|
||||||
trInfo.tr.Finish()
|
trInfo.tr.Finish()
|
||||||
|
@ -1307,13 +1603,13 @@ func (s *Server) handleStream(t transport.ServerTransport, stream *transport.Str
|
||||||
service := sm[:pos]
|
service := sm[:pos]
|
||||||
method := sm[pos+1:]
|
method := sm[pos+1:]
|
||||||
|
|
||||||
srv, knownService := s.m[service]
|
srv, knownService := s.services[service]
|
||||||
if knownService {
|
if knownService {
|
||||||
if md, ok := srv.md[method]; ok {
|
if md, ok := srv.methods[method]; ok {
|
||||||
s.processUnaryRPC(t, stream, srv, md, trInfo)
|
s.processUnaryRPC(t, stream, srv, md, trInfo)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if sd, ok := srv.sd[method]; ok {
|
if sd, ok := srv.streams[method]; ok {
|
||||||
s.processStreamingRPC(t, stream, srv, sd, trInfo)
|
s.processStreamingRPC(t, stream, srv, sd, trInfo)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -1338,7 +1634,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(logger, s.channelzID, "grpc: Server.handleStream failed to write status: %v", err)
|
||||||
}
|
}
|
||||||
if trInfo != nil {
|
if trInfo != nil {
|
||||||
trInfo.tr.Finish()
|
trInfo.tr.Finish()
|
||||||
|
@ -1351,7 +1647,10 @@ type streamKey struct{}
|
||||||
// NewContextWithServerTransportStream creates a new context from ctx and
|
// NewContextWithServerTransportStream creates a new context from ctx and
|
||||||
// attaches stream to it.
|
// attaches stream to it.
|
||||||
//
|
//
|
||||||
// This API is EXPERIMENTAL.
|
// Experimental
|
||||||
|
//
|
||||||
|
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
|
||||||
|
// later release.
|
||||||
func NewContextWithServerTransportStream(ctx context.Context, stream ServerTransportStream) context.Context {
|
func NewContextWithServerTransportStream(ctx context.Context, stream ServerTransportStream) context.Context {
|
||||||
return context.WithValue(ctx, streamKey{}, stream)
|
return context.WithValue(ctx, streamKey{}, stream)
|
||||||
}
|
}
|
||||||
|
@ -1363,7 +1662,10 @@ func NewContextWithServerTransportStream(ctx context.Context, stream ServerTrans
|
||||||
//
|
//
|
||||||
// See also NewContextWithServerTransportStream.
|
// See also NewContextWithServerTransportStream.
|
||||||
//
|
//
|
||||||
// This API is EXPERIMENTAL.
|
// Experimental
|
||||||
|
//
|
||||||
|
// Notice: This type is EXPERIMENTAL and may be changed or removed in a
|
||||||
|
// later release.
|
||||||
type ServerTransportStream interface {
|
type ServerTransportStream interface {
|
||||||
Method() string
|
Method() string
|
||||||
SetHeader(md metadata.MD) error
|
SetHeader(md metadata.MD) error
|
||||||
|
@ -1375,7 +1677,10 @@ type ServerTransportStream interface {
|
||||||
// ctx. Returns nil if the given context has no stream associated with it
|
// ctx. Returns nil if the given context has no stream associated with it
|
||||||
// (which implies it is not an RPC invocation context).
|
// (which implies it is not an RPC invocation context).
|
||||||
//
|
//
|
||||||
// This API is EXPERIMENTAL.
|
// Experimental
|
||||||
|
//
|
||||||
|
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
|
||||||
|
// later release.
|
||||||
func ServerTransportStreamFromContext(ctx context.Context) ServerTransportStream {
|
func ServerTransportStreamFromContext(ctx context.Context) ServerTransportStream {
|
||||||
s, _ := ctx.Value(streamKey{}).(ServerTransportStream)
|
s, _ := ctx.Value(streamKey{}).(ServerTransportStream)
|
||||||
return s
|
return s
|
||||||
|
@ -1403,7 +1708,7 @@ func (s *Server) Stop() {
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
listeners := s.lis
|
listeners := s.lis
|
||||||
s.lis = nil
|
s.lis = nil
|
||||||
st := s.conns
|
conns := s.conns
|
||||||
s.conns = nil
|
s.conns = nil
|
||||||
// interrupt GracefulStop if Stop and GracefulStop are called concurrently.
|
// interrupt GracefulStop if Stop and GracefulStop are called concurrently.
|
||||||
s.cv.Broadcast()
|
s.cv.Broadcast()
|
||||||
|
@ -1412,8 +1717,13 @@ func (s *Server) Stop() {
|
||||||
for lis := range listeners {
|
for lis := range listeners {
|
||||||
lis.Close()
|
lis.Close()
|
||||||
}
|
}
|
||||||
for c := range st {
|
for _, cs := range conns {
|
||||||
c.Close()
|
for st := range cs {
|
||||||
|
st.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if s.opts.numServerWorkers > 0 {
|
||||||
|
s.stopServerWorkers()
|
||||||
}
|
}
|
||||||
|
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
|
@ -1447,9 +1757,11 @@ func (s *Server) GracefulStop() {
|
||||||
}
|
}
|
||||||
s.lis = nil
|
s.lis = nil
|
||||||
if !s.drain {
|
if !s.drain {
|
||||||
for st := range s.conns {
|
for _, conns := range s.conns {
|
||||||
|
for st := range conns {
|
||||||
st.Drain()
|
st.Drain()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
s.drain = true
|
s.drain = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,15 +20,16 @@ package grpc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"google.golang.org/grpc/balancer"
|
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/grpclog"
|
|
||||||
"google.golang.org/grpc/internal"
|
"google.golang.org/grpc/internal"
|
||||||
|
internalserviceconfig "google.golang.org/grpc/internal/serviceconfig"
|
||||||
"google.golang.org/grpc/serviceconfig"
|
"google.golang.org/grpc/serviceconfig"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -40,29 +41,7 @@ const maxInt = int(^uint(0) >> 1)
|
||||||
// Deprecated: Users should not use this struct. Service config should be received
|
// Deprecated: Users should not use this struct. Service config should be received
|
||||||
// through name resolver, as specified here
|
// through name resolver, as specified here
|
||||||
// https://github.com/grpc/grpc/blob/master/doc/service_config.md
|
// https://github.com/grpc/grpc/blob/master/doc/service_config.md
|
||||||
type MethodConfig struct {
|
type MethodConfig = internalserviceconfig.MethodConfig
|
||||||
// WaitForReady indicates whether RPCs sent to this method should wait until
|
|
||||||
// the connection is ready by default (!failfast). The value specified via the
|
|
||||||
// gRPC client API will override the value set here.
|
|
||||||
WaitForReady *bool
|
|
||||||
// Timeout is the default timeout for RPCs sent to this method. The actual
|
|
||||||
// deadline used will be the minimum of the value specified here and the value
|
|
||||||
// set by the application via the gRPC client API. If either one is not set,
|
|
||||||
// then the other will be used. If neither is set, then the RPC has no deadline.
|
|
||||||
Timeout *time.Duration
|
|
||||||
// MaxReqSize is the maximum allowed payload size for an individual request in a
|
|
||||||
// stream (client->server) in bytes. The size which is measured is the serialized
|
|
||||||
// payload after per-message compression (but before stream compression) in bytes.
|
|
||||||
// The actual value used is the minimum of the value specified here and the value set
|
|
||||||
// by the application via the gRPC client API. If either one is not set, then the other
|
|
||||||
// will be used. If neither is set, then the built-in default is used.
|
|
||||||
MaxReqSize *int
|
|
||||||
// MaxRespSize is the maximum allowed payload size for an individual response in a
|
|
||||||
// stream (server->client) in bytes.
|
|
||||||
MaxRespSize *int
|
|
||||||
// RetryPolicy configures retry options for the method.
|
|
||||||
retryPolicy *retryPolicy
|
|
||||||
}
|
|
||||||
|
|
||||||
type lbConfig struct {
|
type lbConfig struct {
|
||||||
name string
|
name string
|
||||||
|
@ -79,7 +58,7 @@ type ServiceConfig struct {
|
||||||
serviceconfig.Config
|
serviceconfig.Config
|
||||||
|
|
||||||
// LB is the load balancer the service providers recommends. The balancer
|
// LB is the load balancer the service providers recommends. The balancer
|
||||||
// specified via grpc.WithBalancer will override this. This is deprecated;
|
// specified via grpc.WithBalancerName will override this. This is deprecated;
|
||||||
// lbConfigs is preferred. If lbConfig and LB are both present, lbConfig
|
// lbConfigs is preferred. If lbConfig and LB are both present, lbConfig
|
||||||
// will be used.
|
// will be used.
|
||||||
LB *string
|
LB *string
|
||||||
|
@ -126,34 +105,6 @@ type healthCheckConfig struct {
|
||||||
ServiceName string
|
ServiceName string
|
||||||
}
|
}
|
||||||
|
|
||||||
// retryPolicy defines the go-native version of the retry policy defined by the
|
|
||||||
// service config here:
|
|
||||||
// https://github.com/grpc/proposal/blob/master/A6-client-retries.md#integration-with-service-config
|
|
||||||
type retryPolicy struct {
|
|
||||||
// MaxAttempts is the maximum number of attempts, including the original RPC.
|
|
||||||
//
|
|
||||||
// This field is required and must be two or greater.
|
|
||||||
maxAttempts int
|
|
||||||
|
|
||||||
// Exponential backoff parameters. The initial retry attempt will occur at
|
|
||||||
// random(0, initialBackoff). In general, the nth attempt will occur at
|
|
||||||
// random(0,
|
|
||||||
// min(initialBackoff*backoffMultiplier**(n-1), maxBackoff)).
|
|
||||||
//
|
|
||||||
// These fields are required and must be greater than zero.
|
|
||||||
initialBackoff time.Duration
|
|
||||||
maxBackoff time.Duration
|
|
||||||
backoffMultiplier float64
|
|
||||||
|
|
||||||
// The set of status codes which may be retried.
|
|
||||||
//
|
|
||||||
// Status codes are specified as strings, e.g., "UNAVAILABLE".
|
|
||||||
//
|
|
||||||
// This field is required and must be non-empty.
|
|
||||||
// Note: a set is used to store this for easy lookup.
|
|
||||||
retryableStatusCodes map[codes.Code]bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type jsonRetryPolicy struct {
|
type jsonRetryPolicy struct {
|
||||||
MaxAttempts int
|
MaxAttempts int
|
||||||
InitialBackoff string
|
InitialBackoff string
|
||||||
|
@ -224,19 +175,27 @@ func parseDuration(s *string) (*time.Duration, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
type jsonName struct {
|
type jsonName struct {
|
||||||
Service *string
|
Service string
|
||||||
Method *string
|
Method string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j jsonName) generatePath() (string, bool) {
|
var (
|
||||||
if j.Service == nil {
|
errDuplicatedName = errors.New("duplicated name")
|
||||||
return "", false
|
errEmptyServiceNonEmptyMethod = errors.New("cannot combine empty 'service' and non-empty 'method'")
|
||||||
|
)
|
||||||
|
|
||||||
|
func (j jsonName) generatePath() (string, error) {
|
||||||
|
if j.Service == "" {
|
||||||
|
if j.Method != "" {
|
||||||
|
return "", errEmptyServiceNonEmptyMethod
|
||||||
}
|
}
|
||||||
res := "/" + *j.Service + "/"
|
return "", nil
|
||||||
if j.Method != nil {
|
|
||||||
res += *j.Method
|
|
||||||
}
|
}
|
||||||
return res, true
|
res := "/" + j.Service + "/"
|
||||||
|
if j.Method != "" {
|
||||||
|
res += j.Method
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(lyuxuan): delete this struct after cleaning up old service config implementation.
|
// TODO(lyuxuan): delete this struct after cleaning up old service config implementation.
|
||||||
|
@ -249,12 +208,10 @@ type jsonMC struct {
|
||||||
RetryPolicy *jsonRetryPolicy
|
RetryPolicy *jsonRetryPolicy
|
||||||
}
|
}
|
||||||
|
|
||||||
type loadBalancingConfig map[string]json.RawMessage
|
|
||||||
|
|
||||||
// TODO(lyuxuan): delete this struct after cleaning up old service config implementation.
|
// TODO(lyuxuan): delete this struct after cleaning up old service config implementation.
|
||||||
type jsonSC struct {
|
type jsonSC struct {
|
||||||
LoadBalancingPolicy *string
|
LoadBalancingPolicy *string
|
||||||
LoadBalancingConfig *[]loadBalancingConfig
|
LoadBalancingConfig *internalserviceconfig.BalancerConfig
|
||||||
MethodConfig *[]jsonMC
|
MethodConfig *[]jsonMC
|
||||||
RetryThrottling *retryThrottlingPolicy
|
RetryThrottling *retryThrottlingPolicy
|
||||||
HealthCheckConfig *healthCheckConfig
|
HealthCheckConfig *healthCheckConfig
|
||||||
|
@ -270,7 +227,7 @@ func parseServiceConfig(js string) *serviceconfig.ParseResult {
|
||||||
var rsc jsonSC
|
var rsc jsonSC
|
||||||
err := json.Unmarshal([]byte(js), &rsc)
|
err := json.Unmarshal([]byte(js), &rsc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
grpclog.Warningf("grpc: parseServiceConfig error unmarshaling %s due to %v", js, err)
|
logger.Warningf("grpc: parseServiceConfig error unmarshaling %s due to %v", js, err)
|
||||||
return &serviceconfig.ParseResult{Err: err}
|
return &serviceconfig.ParseResult{Err: err}
|
||||||
}
|
}
|
||||||
sc := ServiceConfig{
|
sc := ServiceConfig{
|
||||||
|
@ -280,53 +237,25 @@ func parseServiceConfig(js string) *serviceconfig.ParseResult {
|
||||||
healthCheckConfig: rsc.HealthCheckConfig,
|
healthCheckConfig: rsc.HealthCheckConfig,
|
||||||
rawJSONString: js,
|
rawJSONString: js,
|
||||||
}
|
}
|
||||||
if rsc.LoadBalancingConfig != nil {
|
if c := rsc.LoadBalancingConfig; c != nil {
|
||||||
for i, lbcfg := range *rsc.LoadBalancingConfig {
|
sc.lbConfig = &lbConfig{
|
||||||
if len(lbcfg) != 1 {
|
name: c.Name,
|
||||||
err := fmt.Errorf("invalid loadBalancingConfig: entry %v does not contain exactly 1 policy/config pair: %q", i, lbcfg)
|
cfg: c.Config,
|
||||||
grpclog.Warningf(err.Error())
|
|
||||||
return &serviceconfig.ParseResult{Err: err}
|
|
||||||
}
|
|
||||||
var name string
|
|
||||||
var jsonCfg json.RawMessage
|
|
||||||
for name, jsonCfg = range lbcfg {
|
|
||||||
}
|
|
||||||
builder := balancer.Get(name)
|
|
||||||
if builder == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
sc.lbConfig = &lbConfig{name: name}
|
|
||||||
if parser, ok := builder.(balancer.ConfigParser); ok {
|
|
||||||
var err error
|
|
||||||
sc.lbConfig.cfg, err = parser.ParseConfig(jsonCfg)
|
|
||||||
if err != nil {
|
|
||||||
return &serviceconfig.ParseResult{Err: fmt.Errorf("error parsing loadBalancingConfig for policy %q: %v", name, err)}
|
|
||||||
}
|
|
||||||
} else if string(jsonCfg) != "{}" {
|
|
||||||
grpclog.Warningf("non-empty balancer configuration %q, but balancer does not implement ParseConfig", string(jsonCfg))
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if sc.lbConfig == nil {
|
|
||||||
// We had a loadBalancingConfig field but did not encounter a
|
|
||||||
// supported policy. The config is considered invalid in this
|
|
||||||
// case.
|
|
||||||
err := fmt.Errorf("invalid loadBalancingConfig: no supported policies found")
|
|
||||||
grpclog.Warningf(err.Error())
|
|
||||||
return &serviceconfig.ParseResult{Err: err}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if rsc.MethodConfig == nil {
|
if rsc.MethodConfig == nil {
|
||||||
return &serviceconfig.ParseResult{Config: &sc}
|
return &serviceconfig.ParseResult{Config: &sc}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
paths := map[string]struct{}{}
|
||||||
for _, m := range *rsc.MethodConfig {
|
for _, m := range *rsc.MethodConfig {
|
||||||
if m.Name == nil {
|
if m.Name == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
d, err := parseDuration(m.Timeout)
|
d, err := parseDuration(m.Timeout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
grpclog.Warningf("grpc: parseServiceConfig error unmarshaling %s due to %v", js, err)
|
logger.Warningf("grpc: parseServiceConfig error unmarshaling %s due to %v", js, err)
|
||||||
return &serviceconfig.ParseResult{Err: err}
|
return &serviceconfig.ParseResult{Err: err}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -334,8 +263,8 @@ func parseServiceConfig(js string) *serviceconfig.ParseResult {
|
||||||
WaitForReady: m.WaitForReady,
|
WaitForReady: m.WaitForReady,
|
||||||
Timeout: d,
|
Timeout: d,
|
||||||
}
|
}
|
||||||
if mc.retryPolicy, err = convertRetryPolicy(m.RetryPolicy); err != nil {
|
if mc.RetryPolicy, err = convertRetryPolicy(m.RetryPolicy); err != nil {
|
||||||
grpclog.Warningf("grpc: parseServiceConfig error unmarshaling %s due to %v", js, err)
|
logger.Warningf("grpc: parseServiceConfig error unmarshaling %s due to %v", js, err)
|
||||||
return &serviceconfig.ParseResult{Err: err}
|
return &serviceconfig.ParseResult{Err: err}
|
||||||
}
|
}
|
||||||
if m.MaxRequestMessageBytes != nil {
|
if m.MaxRequestMessageBytes != nil {
|
||||||
|
@ -352,10 +281,20 @@ func parseServiceConfig(js string) *serviceconfig.ParseResult {
|
||||||
mc.MaxRespSize = newInt(int(*m.MaxResponseMessageBytes))
|
mc.MaxRespSize = newInt(int(*m.MaxResponseMessageBytes))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, n := range *m.Name {
|
for i, n := range *m.Name {
|
||||||
if path, valid := n.generatePath(); valid {
|
path, err := n.generatePath()
|
||||||
sc.Methods[path] = mc
|
if err != nil {
|
||||||
|
logger.Warningf("grpc: parseServiceConfig error unmarshaling %s due to methodConfig[%d]: %v", js, i, err)
|
||||||
|
return &serviceconfig.ParseResult{Err: err}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, ok := paths[path]; ok {
|
||||||
|
err = errDuplicatedName
|
||||||
|
logger.Warningf("grpc: parseServiceConfig error unmarshaling %s due to methodConfig[%d]: %v", js, i, err)
|
||||||
|
return &serviceconfig.ParseResult{Err: err}
|
||||||
|
}
|
||||||
|
paths[path] = struct{}{}
|
||||||
|
sc.Methods[path] = mc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -370,7 +309,7 @@ func parseServiceConfig(js string) *serviceconfig.ParseResult {
|
||||||
return &serviceconfig.ParseResult{Config: &sc}
|
return &serviceconfig.ParseResult{Config: &sc}
|
||||||
}
|
}
|
||||||
|
|
||||||
func convertRetryPolicy(jrp *jsonRetryPolicy) (p *retryPolicy, err error) {
|
func convertRetryPolicy(jrp *jsonRetryPolicy) (p *internalserviceconfig.RetryPolicy, err error) {
|
||||||
if jrp == nil {
|
if jrp == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
@ -388,23 +327,23 @@ func convertRetryPolicy(jrp *jsonRetryPolicy) (p *retryPolicy, err error) {
|
||||||
*mb <= 0 ||
|
*mb <= 0 ||
|
||||||
jrp.BackoffMultiplier <= 0 ||
|
jrp.BackoffMultiplier <= 0 ||
|
||||||
len(jrp.RetryableStatusCodes) == 0 {
|
len(jrp.RetryableStatusCodes) == 0 {
|
||||||
grpclog.Warningf("grpc: ignoring retry policy %v due to illegal configuration", jrp)
|
logger.Warningf("grpc: ignoring retry policy %v due to illegal configuration", jrp)
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
rp := &retryPolicy{
|
rp := &internalserviceconfig.RetryPolicy{
|
||||||
maxAttempts: jrp.MaxAttempts,
|
MaxAttempts: jrp.MaxAttempts,
|
||||||
initialBackoff: *ib,
|
InitialBackoff: *ib,
|
||||||
maxBackoff: *mb,
|
MaxBackoff: *mb,
|
||||||
backoffMultiplier: jrp.BackoffMultiplier,
|
BackoffMultiplier: jrp.BackoffMultiplier,
|
||||||
retryableStatusCodes: make(map[codes.Code]bool),
|
RetryableStatusCodes: make(map[codes.Code]bool),
|
||||||
}
|
}
|
||||||
if rp.maxAttempts > 5 {
|
if rp.MaxAttempts > 5 {
|
||||||
// TODO(retry): Make the max maxAttempts configurable.
|
// TODO(retry): Make the max maxAttempts configurable.
|
||||||
rp.maxAttempts = 5
|
rp.MaxAttempts = 5
|
||||||
}
|
}
|
||||||
for _, code := range jrp.RetryableStatusCodes {
|
for _, code := range jrp.RetryableStatusCodes {
|
||||||
rp.retryableStatusCodes[code] = true
|
rp.RetryableStatusCodes[code] = true
|
||||||
}
|
}
|
||||||
return rp, nil
|
return rp, nil
|
||||||
}
|
}
|
||||||
|
@ -432,3 +371,34 @@ func getMaxSize(mcMax, doptMax *int, defaultVal int) *int {
|
||||||
func newInt(b int) *int {
|
func newInt(b int) *int {
|
||||||
return &b
|
return &b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
internal.EqualServiceConfigForTesting = equalServiceConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
// equalServiceConfig compares two configs. The rawJSONString field is ignored,
|
||||||
|
// because they may diff in white spaces.
|
||||||
|
//
|
||||||
|
// If any of them is NOT *ServiceConfig, return false.
|
||||||
|
func equalServiceConfig(a, b serviceconfig.Config) bool {
|
||||||
|
aa, ok := a.(*ServiceConfig)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
bb, ok := b.(*ServiceConfig)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
aaRaw := aa.rawJSONString
|
||||||
|
aa.rawJSONString = ""
|
||||||
|
bbRaw := bb.rawJSONString
|
||||||
|
bb.rawJSONString = ""
|
||||||
|
defer func() {
|
||||||
|
aa.rawJSONString = aaRaw
|
||||||
|
bb.rawJSONString = bbRaw
|
||||||
|
}()
|
||||||
|
// Using reflect.DeepEqual instead of cmp.Equal because many balancer
|
||||||
|
// configs are unexported, and cmp.Equal cannot compare unexported fields
|
||||||
|
// from unexported structs.
|
||||||
|
return reflect.DeepEqual(aa, bb)
|
||||||
|
}
|
||||||
|
|
|
@ -19,7 +19,10 @@
|
||||||
// Package serviceconfig defines types and methods for operating on gRPC
|
// Package serviceconfig defines types and methods for operating on gRPC
|
||||||
// service configs.
|
// service configs.
|
||||||
//
|
//
|
||||||
// This package is EXPERIMENTAL.
|
// Experimental
|
||||||
|
//
|
||||||
|
// Notice: This package is EXPERIMENTAL and may be changed or removed in a
|
||||||
|
// later release.
|
||||||
package serviceconfig
|
package serviceconfig
|
||||||
|
|
||||||
// Config represents an opaque data structure holding a service config.
|
// Config represents an opaque data structure holding a service config.
|
||||||
|
|
|
@ -16,8 +16,6 @@
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
//go:generate protoc --go_out=plugins=grpc:. grpc_testing/test.proto
|
|
||||||
|
|
||||||
// Package stats is for collecting and reporting various network and RPC stats.
|
// Package stats is for collecting and reporting various network and RPC stats.
|
||||||
// This package is for monitoring purpose only. All fields are read-only.
|
// This package is for monitoring purpose only. All fields are read-only.
|
||||||
// All APIs are experimental.
|
// All APIs are experimental.
|
||||||
|
@ -81,6 +79,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,10 +91,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
|
|
||||||
// Header contains the header metadata received.
|
|
||||||
Header metadata.MD
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsClient indicates if the stats information is from client side.
|
// IsClient indicates if the stats information is from client side.
|
||||||
|
@ -141,6 +139,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.
|
||||||
|
@ -149,10 +151,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
|
|
||||||
// Header contains the header metadata sent.
|
|
||||||
Header metadata.MD
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsClient indicates if this stats information is from client side.
|
// IsClient indicates if this stats information is from client side.
|
||||||
|
@ -165,6 +163,9 @@ 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
|
// Trailer contains the trailer metadata sent to the client. This
|
||||||
// field is only valid if this OutTrailer is from the server side.
|
// field is only valid if this OutTrailer is from the server side.
|
||||||
|
|
|
@ -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,12 +70,14 @@ 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 by this
|
||||||
// package or has a method `GRPCStatus() *Status`. Otherwise, ok is false and a
|
// package or has a method `GRPCStatus() *Status`.
|
||||||
// Status is returned with codes.Unknown and the original error message.
|
// If err is nil, a Status is returned with codes.OK and no message.
|
||||||
|
// Otherwise, ok is false and a Status is returned with codes.Unknown and
|
||||||
|
// the original error message.
|
||||||
func FromError(err error) (s *Status, ok bool) {
|
func FromError(err error) (s *Status, ok bool) {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return nil, true
|
return nil, true
|
||||||
|
@ -160,42 +97,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,11 +31,13 @@ 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"
|
||||||
"google.golang.org/grpc/internal/grpcrand"
|
"google.golang.org/grpc/internal/grpcrand"
|
||||||
|
"google.golang.org/grpc/internal/grpcutil"
|
||||||
|
iresolver "google.golang.org/grpc/internal/resolver"
|
||||||
|
"google.golang.org/grpc/internal/serviceconfig"
|
||||||
"google.golang.org/grpc/internal/transport"
|
"google.golang.org/grpc/internal/transport"
|
||||||
"google.golang.org/grpc/metadata"
|
"google.golang.org/grpc/metadata"
|
||||||
"google.golang.org/grpc/peer"
|
"google.golang.org/grpc/peer"
|
||||||
|
@ -50,14 +52,20 @@ import (
|
||||||
// of the RPC.
|
// of the RPC.
|
||||||
type StreamHandler func(srv interface{}, stream ServerStream) error
|
type StreamHandler func(srv interface{}, stream ServerStream) error
|
||||||
|
|
||||||
// StreamDesc represents a streaming RPC service's method specification.
|
// StreamDesc represents a streaming RPC service's method specification. Used
|
||||||
|
// on the server when registering services and on the client when initiating
|
||||||
|
// new streams.
|
||||||
type StreamDesc struct {
|
type StreamDesc struct {
|
||||||
StreamName string
|
// StreamName and Handler are only used when registering handlers on a
|
||||||
Handler StreamHandler
|
// server.
|
||||||
|
StreamName string // the name of the method excluding the service
|
||||||
|
Handler StreamHandler // the handler called for the method
|
||||||
|
|
||||||
// At least one of these is true.
|
// ServerStreams and ClientStreams are used for registering handlers on a
|
||||||
ServerStreams bool
|
// server as well as defining RPC behavior when passed to NewClientStream
|
||||||
ClientStreams bool
|
// and ClientConn.NewStream. At least one must be true.
|
||||||
|
ServerStreams bool // indicates the server can perform streaming sends
|
||||||
|
ClientStreams bool // indicates the client can perform streaming sends
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stream defines the common interface a client or server stream has to satisfy.
|
// Stream defines the common interface a client or server stream has to satisfy.
|
||||||
|
@ -164,13 +172,48 @@ func newClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, meth
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
c := defaultCallInfo()
|
|
||||||
// Provide an opportunity for the first RPC to see the first service config
|
// Provide an opportunity for the first RPC to see the first service config
|
||||||
// provided by the resolver.
|
// provided by the resolver.
|
||||||
if err := cc.waitForResolvedAddrs(ctx); err != nil {
|
if err := cc.waitForResolvedAddrs(ctx); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
mc := cc.GetMethodConfig(method)
|
|
||||||
|
var mc serviceconfig.MethodConfig
|
||||||
|
var onCommit func()
|
||||||
|
var newStream = func(ctx context.Context, done func()) (iresolver.ClientStream, error) {
|
||||||
|
return newClientStreamWithParams(ctx, desc, cc, method, mc, onCommit, done, opts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
rpcInfo := iresolver.RPCInfo{Context: ctx, Method: method}
|
||||||
|
rpcConfig, err := cc.safeConfigSelector.SelectConfig(rpcInfo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, toRPCErr(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if rpcConfig != nil {
|
||||||
|
if rpcConfig.Context != nil {
|
||||||
|
ctx = rpcConfig.Context
|
||||||
|
}
|
||||||
|
mc = rpcConfig.MethodConfig
|
||||||
|
onCommit = rpcConfig.OnCommitted
|
||||||
|
if rpcConfig.Interceptor != nil {
|
||||||
|
rpcInfo.Context = nil
|
||||||
|
ns := newStream
|
||||||
|
newStream = func(ctx context.Context, done func()) (iresolver.ClientStream, error) {
|
||||||
|
cs, err := rpcConfig.Interceptor.NewStream(ctx, rpcInfo, done, ns)
|
||||||
|
if err != nil {
|
||||||
|
return nil, toRPCErr(err)
|
||||||
|
}
|
||||||
|
return cs, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return newStream(ctx, func() {})
|
||||||
|
}
|
||||||
|
|
||||||
|
func newClientStreamWithParams(ctx context.Context, desc *StreamDesc, cc *ClientConn, method string, mc serviceconfig.MethodConfig, onCommit, doneFunc func(), opts ...CallOption) (_ iresolver.ClientStream, err error) {
|
||||||
|
c := defaultCallInfo()
|
||||||
if mc.WaitForReady != nil {
|
if mc.WaitForReady != nil {
|
||||||
c.failFast = !*mc.WaitForReady
|
c.failFast = !*mc.WaitForReady
|
||||||
}
|
}
|
||||||
|
@ -207,6 +250,7 @@ func newClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, meth
|
||||||
Host: cc.authority,
|
Host: cc.authority,
|
||||||
Method: method,
|
Method: method,
|
||||||
ContentSubtype: c.contentSubtype,
|
ContentSubtype: c.contentSubtype,
|
||||||
|
DoneFunc: doneFunc,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set our outgoing compression according to the UseCompressor CallOption, if
|
// Set our outgoing compression according to the UseCompressor CallOption, if
|
||||||
|
@ -272,13 +316,13 @@ func newClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, meth
|
||||||
cancel: cancel,
|
cancel: cancel,
|
||||||
beginTime: beginTime,
|
beginTime: beginTime,
|
||||||
firstAttempt: true,
|
firstAttempt: true,
|
||||||
|
onCommit: onCommit,
|
||||||
}
|
}
|
||||||
if !cc.dopts.disableRetry {
|
if !cc.dopts.disableRetry {
|
||||||
cs.retryThrottler = cc.retryThrottler.Load().(*retryThrottler)
|
cs.retryThrottler = cc.retryThrottler.Load().(*retryThrottler)
|
||||||
}
|
}
|
||||||
cs.binlog = binarylog.GetMethodLogger(method)
|
cs.binlog = binarylog.GetMethodLogger(method)
|
||||||
|
|
||||||
cs.callInfo.stream = cs
|
|
||||||
// Only this initial attempt has stats/tracing.
|
// Only this initial attempt has stats/tracing.
|
||||||
// TODO(dfawley): move to newAttempt when per-attempt stats are implemented.
|
// TODO(dfawley): move to newAttempt when per-attempt stats are implemented.
|
||||||
if err := cs.newAttemptLocked(sh, trInfo); err != nil {
|
if err := cs.newAttemptLocked(sh, trInfo); err != nil {
|
||||||
|
@ -348,7 +392,16 @@ func (cs *clientStream) newAttemptLocked(sh stats.Handler, trInfo *traceInfo) (r
|
||||||
if err := cs.ctx.Err(); err != nil {
|
if err := cs.ctx.Err(); err != nil {
|
||||||
return toRPCErr(err)
|
return toRPCErr(err)
|
||||||
}
|
}
|
||||||
t, done, err := cs.cc.getTransport(cs.ctx, cs.callInfo.failFast, cs.callHdr.Method)
|
|
||||||
|
ctx := cs.ctx
|
||||||
|
if cs.cc.parsedTarget.Scheme == "xds" {
|
||||||
|
// Add extra metadata (metadata that will be added by transport) to context
|
||||||
|
// so the balancer can see them.
|
||||||
|
ctx = grpcutil.WithExtraMetadata(cs.ctx, metadata.Pairs(
|
||||||
|
"content-type", grpcutil.ContentType(cs.callHdr.ContentSubtype),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
t, done, err := cs.cc.getTransport(ctx, cs.callInfo.failFast, cs.callHdr.Method)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -366,6 +419,11 @@ func (a *csAttempt) newStream() error {
|
||||||
cs.callHdr.PreviousAttempts = cs.numRetries
|
cs.callHdr.PreviousAttempts = cs.numRetries
|
||||||
s, err := a.t.NewStream(cs.ctx, cs.callHdr)
|
s, err := a.t.NewStream(cs.ctx, cs.callHdr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if _, ok := err.(transport.PerformedIOError); ok {
|
||||||
|
// Return without converting to an RPC error so retry code can
|
||||||
|
// inspect.
|
||||||
|
return err
|
||||||
|
}
|
||||||
return toRPCErr(err)
|
return toRPCErr(err)
|
||||||
}
|
}
|
||||||
cs.attempt.s = s
|
cs.attempt.s = s
|
||||||
|
@ -420,6 +478,7 @@ type clientStream struct {
|
||||||
attempt *csAttempt
|
attempt *csAttempt
|
||||||
// TODO(hedging): hedging will have multiple attempts simultaneously.
|
// TODO(hedging): hedging will have multiple attempts simultaneously.
|
||||||
committed bool // active attempt committed for retry?
|
committed bool // active attempt committed for retry?
|
||||||
|
onCommit func()
|
||||||
buffer []func(a *csAttempt) error // operations to replay on retry
|
buffer []func(a *csAttempt) error // operations to replay on retry
|
||||||
bufferSize int // current size of buffer
|
bufferSize int // current size of buffer
|
||||||
}
|
}
|
||||||
|
@ -448,6 +507,9 @@ type csAttempt struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cs *clientStream) commitAttemptLocked() {
|
func (cs *clientStream) commitAttemptLocked() {
|
||||||
|
if !cs.committed && cs.onCommit != nil {
|
||||||
|
cs.onCommit()
|
||||||
|
}
|
||||||
cs.committed = true
|
cs.committed = true
|
||||||
cs.buffer = nil
|
cs.buffer = nil
|
||||||
}
|
}
|
||||||
|
@ -461,12 +523,22 @@ func (cs *clientStream) commitAttempt() {
|
||||||
// shouldRetry returns nil if the RPC should be retried; otherwise it returns
|
// shouldRetry returns nil if the RPC should be retried; otherwise it returns
|
||||||
// the error that should be returned by the operation.
|
// the error that should be returned by the operation.
|
||||||
func (cs *clientStream) shouldRetry(err error) error {
|
func (cs *clientStream) shouldRetry(err error) error {
|
||||||
if cs.attempt.s == nil && !cs.callInfo.failFast {
|
unprocessed := false
|
||||||
// In the event of any error from NewStream (attempt.s == nil), we
|
if cs.attempt.s == nil {
|
||||||
|
pioErr, ok := err.(transport.PerformedIOError)
|
||||||
|
if ok {
|
||||||
|
// Unwrap error.
|
||||||
|
err = toRPCErr(pioErr.Err)
|
||||||
|
} else {
|
||||||
|
unprocessed = true
|
||||||
|
}
|
||||||
|
if !ok && !cs.callInfo.failFast {
|
||||||
|
// In the event of a non-IO operation error from NewStream, we
|
||||||
// never attempted to write anything to the wire, so we can retry
|
// never attempted to write anything to the wire, so we can retry
|
||||||
// indefinitely for non-fail-fast RPCs.
|
// indefinitely for non-fail-fast RPCs.
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if cs.finished || cs.committed {
|
if cs.finished || cs.committed {
|
||||||
// RPC is finished or committed; cannot retry.
|
// RPC is finished or committed; cannot retry.
|
||||||
return err
|
return err
|
||||||
|
@ -474,13 +546,12 @@ func (cs *clientStream) shouldRetry(err error) error {
|
||||||
// Wait for the trailers.
|
// Wait for the trailers.
|
||||||
if cs.attempt.s != nil {
|
if cs.attempt.s != nil {
|
||||||
<-cs.attempt.s.Done()
|
<-cs.attempt.s.Done()
|
||||||
|
unprocessed = cs.attempt.s.Unprocessed()
|
||||||
}
|
}
|
||||||
if cs.firstAttempt && (cs.attempt.s == nil || cs.attempt.s.Unprocessed()) {
|
if cs.firstAttempt && unprocessed {
|
||||||
// First attempt, stream unprocessed: transparently retry.
|
// First attempt, stream unprocessed: transparently retry.
|
||||||
cs.firstAttempt = false
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
cs.firstAttempt = false
|
|
||||||
if cs.cc.dopts.disableRetry {
|
if cs.cc.dopts.disableRetry {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -498,13 +569,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(logger, 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(logger, 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
|
||||||
}
|
}
|
||||||
|
@ -517,8 +588,8 @@ func (cs *clientStream) shouldRetry(err error) error {
|
||||||
code = status.Convert(err).Code()
|
code = status.Convert(err).Code()
|
||||||
}
|
}
|
||||||
|
|
||||||
rp := cs.methodConfig.retryPolicy
|
rp := cs.methodConfig.RetryPolicy
|
||||||
if rp == nil || !rp.retryableStatusCodes[code] {
|
if rp == nil || !rp.RetryableStatusCodes[code] {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -527,7 +598,7 @@ func (cs *clientStream) shouldRetry(err error) error {
|
||||||
if cs.retryThrottler.throttle() {
|
if cs.retryThrottler.throttle() {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if cs.numRetries+1 >= rp.maxAttempts {
|
if cs.numRetries+1 >= rp.MaxAttempts {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -536,9 +607,9 @@ func (cs *clientStream) shouldRetry(err error) error {
|
||||||
dur = time.Millisecond * time.Duration(pushback)
|
dur = time.Millisecond * time.Duration(pushback)
|
||||||
cs.numRetriesSincePushback = 0
|
cs.numRetriesSincePushback = 0
|
||||||
} else {
|
} else {
|
||||||
fact := math.Pow(rp.backoffMultiplier, float64(cs.numRetriesSincePushback))
|
fact := math.Pow(rp.BackoffMultiplier, float64(cs.numRetriesSincePushback))
|
||||||
cur := float64(rp.initialBackoff) * fact
|
cur := float64(rp.InitialBackoff) * fact
|
||||||
if max := float64(rp.maxBackoff); cur > max {
|
if max := float64(rp.MaxBackoff); cur > max {
|
||||||
cur = max
|
cur = max
|
||||||
}
|
}
|
||||||
dur = time.Duration(grpcrand.Int63n(int64(cur)))
|
dur = time.Duration(grpcrand.Int63n(int64(cur)))
|
||||||
|
@ -566,6 +637,7 @@ func (cs *clientStream) retryLocked(lastErr error) error {
|
||||||
cs.commitAttemptLocked()
|
cs.commitAttemptLocked()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
cs.firstAttempt = false
|
||||||
if err := cs.newAttemptLocked(nil, nil); err != nil {
|
if err := cs.newAttemptLocked(nil, nil); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -800,6 +872,15 @@ func (cs *clientStream) finish(err error) {
|
||||||
}
|
}
|
||||||
cs.finished = true
|
cs.finished = true
|
||||||
cs.commitAttemptLocked()
|
cs.commitAttemptLocked()
|
||||||
|
if cs.attempt != nil {
|
||||||
|
cs.attempt.finish(err)
|
||||||
|
// after functions all rely upon having a stream.
|
||||||
|
if cs.attempt.s != nil {
|
||||||
|
for _, o := range cs.opts {
|
||||||
|
o.after(cs.callInfo, cs.attempt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
cs.mu.Unlock()
|
cs.mu.Unlock()
|
||||||
// For binary logging. only log cancel in finish (could be caused by RPC ctx
|
// For binary logging. only log cancel in finish (could be caused by RPC ctx
|
||||||
// canceled or ClientConn closed). Trailer will be logged in RecvMsg.
|
// canceled or ClientConn closed). Trailer will be logged in RecvMsg.
|
||||||
|
@ -821,15 +902,6 @@ func (cs *clientStream) finish(err error) {
|
||||||
cs.cc.incrCallsSucceeded()
|
cs.cc.incrCallsSucceeded()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if cs.attempt != nil {
|
|
||||||
cs.attempt.finish(err)
|
|
||||||
// after functions all rely upon having a stream.
|
|
||||||
if cs.attempt.s != nil {
|
|
||||||
for _, o := range cs.opts {
|
|
||||||
o.after(cs.callInfo)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cs.cancel()
|
cs.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -906,7 +978,7 @@ func (a *csAttempt) recvMsg(m interface{}, payInfo *payloadInfo) (err error) {
|
||||||
Payload: m,
|
Payload: m,
|
||||||
// TODO truncate large payload.
|
// TODO truncate large payload.
|
||||||
Data: payInfo.uncompressedBytes,
|
Data: payInfo.uncompressedBytes,
|
||||||
WireLength: payInfo.wireLength,
|
WireLength: payInfo.wireLength + headerLen,
|
||||||
Length: len(payInfo.uncompressedBytes),
|
Length: len(payInfo.uncompressedBytes),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1067,7 +1139,6 @@ func newNonRetryClientStream(ctx context.Context, desc *StreamDesc, method strin
|
||||||
t: t,
|
t: t,
|
||||||
}
|
}
|
||||||
|
|
||||||
as.callInfo.stream = as
|
|
||||||
s, err := as.t.NewStream(as.ctx, as.callHdr)
|
s, err := as.t.NewStream(as.ctx, as.callHdr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = toRPCErr(err)
|
err = toRPCErr(err)
|
||||||
|
@ -1489,7 +1560,7 @@ func (ss *serverStream) RecvMsg(m interface{}) (err error) {
|
||||||
Payload: m,
|
Payload: m,
|
||||||
// TODO truncate large payload.
|
// TODO truncate large payload.
|
||||||
Data: payInfo.uncompressedBytes,
|
Data: payInfo.uncompressedBytes,
|
||||||
WireLength: payInfo.wireLength,
|
WireLength: payInfo.wireLength + headerLen,
|
||||||
Length: len(payInfo.uncompressedBytes),
|
Length: len(payInfo.uncompressedBytes),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,12 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Package tap defines the function handles which are executed on the transport
|
// Package tap defines the function handles which are executed on the transport
|
||||||
// layer of gRPC-Go and related information. Everything here is EXPERIMENTAL.
|
// layer of gRPC-Go and related information.
|
||||||
|
//
|
||||||
|
// Experimental
|
||||||
|
//
|
||||||
|
// Notice: This API is EXPERIMENTAL and may be changed or removed in a
|
||||||
|
// later release.
|
||||||
package tap
|
package tap
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -32,16 +37,16 @@ type Info struct {
|
||||||
// TODO: More to be added.
|
// TODO: More to be added.
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServerInHandle defines the function which runs before a new stream is created
|
// ServerInHandle defines the function which runs before a new stream is
|
||||||
// on the server side. If it returns a non-nil error, the stream will not be
|
// created on the server side. If it returns a non-nil error, the stream will
|
||||||
// created and a RST_STREAM will be sent back to the client with REFUSED_STREAM.
|
// not be created and an error will be returned to the client. If the error
|
||||||
// The client will receive an RPC error "code = Unavailable, desc = stream
|
// returned is a status error, that status code and message will be used,
|
||||||
// terminated by RST_STREAM with error code: REFUSED_STREAM".
|
// otherwise PermissionDenied will be the code and err.Error() will be the
|
||||||
|
// message.
|
||||||
//
|
//
|
||||||
// It's intended to be used in situations where you don't want to waste the
|
// It's intended to be used in situations where you don't want to waste the
|
||||||
// resources to accept the new stream (e.g. rate-limiting). And the content of
|
// resources to accept the new stream (e.g. rate-limiting). For other general
|
||||||
// the error will be ignored and won't be sent back to the client. For other
|
// usages, please use interceptors.
|
||||||
// general usages, please use interceptors.
|
|
||||||
//
|
//
|
||||||
// Note that it is executed in the per-connection I/O goroutine(s) instead of
|
// Note that it is executed in the per-connection I/O goroutine(s) instead of
|
||||||
// per-RPC goroutine. Therefore, users should NOT have any
|
// per-RPC goroutine. Therefore, users should NOT have any
|
||||||
|
|
|
@ -19,4 +19,4 @@
|
||||||
package grpc
|
package grpc
|
||||||
|
|
||||||
// Version is the current grpc version.
|
// Version is the current grpc version.
|
||||||
const Version = "1.27.1"
|
const Version = "1.38.0"
|
||||||
|
|
Loading…
Reference in New Issue