fix chown and seccomp
Signed-off-by: Jess Frazelle <acidburn@microsoft.com>
This commit is contained in:
parent
60f032f6f5
commit
2569457739
8197 changed files with 30742 additions and 1596554 deletions
23
vendor/google.golang.org/grpc/.travis.yml
generated
vendored
Normal file
23
vendor/google.golang.org/grpc/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
language: go
|
||||
|
||||
go:
|
||||
- 1.6.x
|
||||
- 1.7.x
|
||||
- 1.8.x
|
||||
- 1.9.x
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- go: 1.9.x
|
||||
env: RUN386=1
|
||||
|
||||
go_import_path: google.golang.org/grpc
|
||||
|
||||
before_install:
|
||||
- if [[ "$TRAVIS_GO_VERSION" = 1.9* && "$GOARCH" != "386" ]]; then ./vet.sh -install || exit 1; fi
|
||||
|
||||
script:
|
||||
- if [[ -n "$RUN386" ]]; then export GOARCH=386; fi
|
||||
- if [[ "$TRAVIS_GO_VERSION" = 1.9* && "$GOARCH" != "386" ]]; then ./vet.sh || exit 1; fi
|
||||
- make test || exit 1
|
||||
- if [[ "$GOARCH" != "386" ]]; then make testrace; fi
|
1
vendor/google.golang.org/grpc/AUTHORS
generated
vendored
Normal file
1
vendor/google.golang.org/grpc/AUTHORS
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
Google Inc.
|
32
vendor/google.golang.org/grpc/CONTRIBUTING.md
generated
vendored
Normal file
32
vendor/google.golang.org/grpc/CONTRIBUTING.md
generated
vendored
Normal file
|
@ -0,0 +1,32 @@
|
|||
# How to contribute
|
||||
|
||||
We definitely welcome your patches and contributions to gRPC!
|
||||
|
||||
If you are new to github, please start by reading [Pull Request howto](https://help.github.com/articles/about-pull-requests/)
|
||||
|
||||
## Legal requirements
|
||||
|
||||
In order to protect both you and ourselves, you will need to sign the
|
||||
[Contributor License Agreement](https://identity.linuxfoundation.org/projects/cncf).
|
||||
|
||||
## Guidelines for Pull Requests
|
||||
How to get your contributions merged smoothly and quickly.
|
||||
|
||||
- Create **small PRs** that are narrowly focused on **addressing a single concern**. We often times receive PRs that are trying to fix several things at a time, but only one fix is considered acceptable, nothing gets merged and both author's & review's time is wasted. Create more PRs to address different concerns and everyone will be happy.
|
||||
|
||||
- For speculative changes, consider opening an issue and discussing it first. If you are suggesting a behavioral or API change, consider starting with a [gRFC proposal](https://github.com/grpc/proposal).
|
||||
|
||||
- Provide a good **PR description** as a record of **what** change is being made and **why** it was made. Link to a github issue if it exists.
|
||||
|
||||
- Don't fix code style and formatting unless you are already changing that line to address an issue. PRs with irrelevant changes won't be merged. If you do want to fix formatting or style, do that in a separate PR.
|
||||
|
||||
- Unless your PR is trivial, you should expect there will be reviewer comments that you'll need to address before merging. We expect you to be reasonably responsive to those comments, otherwise the PR will be closed after 2-3 weeks of inactivity.
|
||||
|
||||
- Maintain **clean commit history** and use **meaningful commit messages**. PRs with messy commit history are difficult to review and won't be merged. Use `rebase -i upstream/master` to curate your commit history and/or to bring in latest changes from master (but avoid rebasing in the middle of a code review).
|
||||
|
||||
- Keep your PR up to date with upstream/master (if there are merge conflicts, we can't really merge your change).
|
||||
|
||||
- **All tests need to be passing** before your change can be merged. We recommend you **run tests locally** before creating your PR to catch breakages early on.
|
||||
|
||||
- Exceptions to the rules can be made if there's a compelling reason for doing so.
|
||||
|
202
vendor/google.golang.org/grpc/LICENSE
generated
vendored
Normal file
202
vendor/google.golang.org/grpc/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,202 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
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.
|
45
vendor/google.golang.org/grpc/Makefile
generated
vendored
Normal file
45
vendor/google.golang.org/grpc/Makefile
generated
vendored
Normal file
|
@ -0,0 +1,45 @@
|
|||
all: test testrace
|
||||
|
||||
deps:
|
||||
go get -d -v google.golang.org/grpc/...
|
||||
|
||||
updatedeps:
|
||||
go get -d -v -u -f google.golang.org/grpc/...
|
||||
|
||||
testdeps:
|
||||
go get -d -v -t google.golang.org/grpc/...
|
||||
|
||||
updatetestdeps:
|
||||
go get -d -v -t -u -f google.golang.org/grpc/...
|
||||
|
||||
build: deps
|
||||
go build google.golang.org/grpc/...
|
||||
|
||||
proto:
|
||||
@ if ! which protoc > /dev/null; then \
|
||||
echo "error: protoc not installed" >&2; \
|
||||
exit 1; \
|
||||
fi
|
||||
go generate google.golang.org/grpc/...
|
||||
|
||||
test: testdeps
|
||||
go test -cpu 1,4 -timeout 5m google.golang.org/grpc/...
|
||||
|
||||
testrace: testdeps
|
||||
go test -race -cpu 1,4 -timeout 7m google.golang.org/grpc/...
|
||||
|
||||
clean:
|
||||
go clean -i google.golang.org/grpc/...
|
||||
|
||||
.PHONY: \
|
||||
all \
|
||||
deps \
|
||||
updatedeps \
|
||||
testdeps \
|
||||
updatetestdeps \
|
||||
build \
|
||||
proto \
|
||||
test \
|
||||
testrace \
|
||||
clean \
|
||||
coverage
|
46
vendor/google.golang.org/grpc/README.md
generated
vendored
Normal file
46
vendor/google.golang.org/grpc/README.md
generated
vendored
Normal file
|
@ -0,0 +1,46 @@
|
|||
# gRPC-Go
|
||||
|
||||
[](https://travis-ci.org/grpc/grpc-go) [](https://godoc.org/google.golang.org/grpc) [](https://goreportcard.com/report/github.com/grpc/grpc-go)
|
||||
|
||||
The Go implementation of [gRPC](https://grpc.io/): A high performance, open source, general RPC framework that puts mobile and HTTP/2 first. For more information see the [gRPC Quick Start: Go](https://grpc.io/docs/quickstart/go.html) guide.
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
To install this package, you need to install Go and setup your Go workspace on your computer. The simplest way to install the library is to run:
|
||||
|
||||
```
|
||||
$ go get -u google.golang.org/grpc
|
||||
```
|
||||
|
||||
Prerequisites
|
||||
-------------
|
||||
|
||||
This requires Go 1.6 or later. Go 1.7 will be required as of the next gRPC-Go
|
||||
release (1.8).
|
||||
|
||||
Constraints
|
||||
-----------
|
||||
The grpc package should only depend on standard Go packages and a small number of exceptions. If your contribution introduces new dependencies which are NOT in the [list](http://godoc.org/google.golang.org/grpc?imports), you need a discussion with gRPC-Go authors and consultants.
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
See [API documentation](https://godoc.org/google.golang.org/grpc) for package and API descriptions and find examples in the [examples directory](examples/).
|
||||
|
||||
Performance
|
||||
-----------
|
||||
See the current benchmarks for some of the languages supported 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
|
||||
---
|
||||
|
||||
#### Compiling error, undefined: grpc.SupportPackageIsVersion
|
||||
|
||||
Please update proto package, gRPC package and rebuild the proto files:
|
||||
- `go get -u github.com/golang/protobuf/{proto,protoc-gen-go}`
|
||||
- `go get -u google.golang.org/grpc`
|
||||
- `protoc --go_out=plugins=grpc:. *.proto`
|
96
vendor/google.golang.org/grpc/backoff.go
generated
vendored
Normal file
96
vendor/google.golang.org/grpc/backoff.go
generated
vendored
Normal file
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
*
|
||||
* 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 (
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
// DefaultBackoffConfig uses values specified for backoff in
|
||||
// https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md.
|
||||
var DefaultBackoffConfig = BackoffConfig{
|
||||
MaxDelay: 120 * time.Second,
|
||||
baseDelay: 1.0 * time.Second,
|
||||
factor: 1.6,
|
||||
jitter: 0.2,
|
||||
}
|
||||
|
||||
// backoffStrategy defines the methodology for backing off after a grpc
|
||||
// connection failure.
|
||||
//
|
||||
// This is unexported until the gRPC project decides whether or not to allow
|
||||
// alternative backoff strategies. Once a decision is made, this type and its
|
||||
// method may be exported.
|
||||
type backoffStrategy interface {
|
||||
// backoff returns the amount of time to wait before the next retry given
|
||||
// the number of consecutive failures.
|
||||
backoff(retries int) time.Duration
|
||||
}
|
||||
|
||||
// BackoffConfig defines the parameters for the default gRPC backoff strategy.
|
||||
type BackoffConfig struct {
|
||||
// MaxDelay is the upper bound of backoff delay.
|
||||
MaxDelay time.Duration
|
||||
|
||||
// TODO(stevvooe): The following fields are not exported, as allowing
|
||||
// changes would violate the current gRPC specification for backoff. If
|
||||
// gRPC decides to allow more interesting backoff strategies, these fields
|
||||
// may be opened up in the future.
|
||||
|
||||
// baseDelay is the amount of time to wait before retrying after the first
|
||||
// failure.
|
||||
baseDelay time.Duration
|
||||
|
||||
// factor is applied to the backoff after each retry.
|
||||
factor float64
|
||||
|
||||
// jitter provides a range to randomize backoff delays.
|
||||
jitter float64
|
||||
}
|
||||
|
||||
func setDefaults(bc *BackoffConfig) {
|
||||
md := bc.MaxDelay
|
||||
*bc = DefaultBackoffConfig
|
||||
|
||||
if md > 0 {
|
||||
bc.MaxDelay = md
|
||||
}
|
||||
}
|
||||
|
||||
func (bc BackoffConfig) backoff(retries int) time.Duration {
|
||||
if retries == 0 {
|
||||
return bc.baseDelay
|
||||
}
|
||||
backoff, max := float64(bc.baseDelay), float64(bc.MaxDelay)
|
||||
for backoff < max && retries > 0 {
|
||||
backoff *= bc.factor
|
||||
retries--
|
||||
}
|
||||
if backoff > max {
|
||||
backoff = max
|
||||
}
|
||||
// Randomize backoff delays so that if a cluster of requests start at
|
||||
// the same time, they won't operate in lockstep.
|
||||
backoff *= 1 + bc.jitter*(rand.Float64()*2-1)
|
||||
if backoff < 0 {
|
||||
return 0
|
||||
}
|
||||
return time.Duration(backoff)
|
||||
}
|
29
vendor/google.golang.org/grpc/backoff_test.go
generated
vendored
Normal file
29
vendor/google.golang.org/grpc/backoff_test.go
generated
vendored
Normal file
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
*
|
||||
* 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 "testing"
|
||||
|
||||
func TestBackoffConfigDefaults(t *testing.T) {
|
||||
b := BackoffConfig{}
|
||||
setDefaults(&b)
|
||||
if b != DefaultBackoffConfig {
|
||||
t.Fatalf("expected BackoffConfig to pickup default parameters: %v != %v", b, DefaultBackoffConfig)
|
||||
}
|
||||
}
|
409
vendor/google.golang.org/grpc/balancer.go
generated
vendored
Normal file
409
vendor/google.golang.org/grpc/balancer.go
generated
vendored
Normal file
|
@ -0,0 +1,409 @@
|
|||
/*
|
||||
*
|
||||
* 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 (
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"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.
|
||||
// This is the EXPERIMENTAL API and may be changed or extended in the future.
|
||||
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.
|
||||
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.
|
||||
// This is the EXPERIMENTAL API and may be changed or extended in the future.
|
||||
type BalancerGetOptions struct {
|
||||
// BlockingWait specifies whether Get should block when there is no
|
||||
// connected address.
|
||||
BlockingWait bool
|
||||
}
|
||||
|
||||
// Balancer chooses network addresses for RPCs.
|
||||
// This is the EXPERIMENTAL API and may be changed or extended in the future.
|
||||
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
|
||||
}
|
||||
|
||||
// downErr implements net.Error. It is constructed by gRPC internals and passed to the down
|
||||
// call of Balancer.
|
||||
type downErr struct {
|
||||
timeout bool
|
||||
temporary bool
|
||||
desc string
|
||||
}
|
||||
|
||||
func (e downErr) Error() string { return e.desc }
|
||||
func (e downErr) Timeout() bool { return e.timeout }
|
||||
func (e downErr) Temporary() bool { return e.temporary }
|
||||
|
||||
func downErrorf(timeout, temporary bool, format string, a ...interface{}) downErr {
|
||||
return downErr{
|
||||
timeout: timeout,
|
||||
temporary: temporary,
|
||||
desc: fmt.Sprintf(format, a...),
|
||||
}
|
||||
}
|
||||
|
||||
// RoundRobin returns a Balancer that selects addresses round-robin. It uses r to watch
|
||||
// the name resolution updates and updates the addresses available correspondingly.
|
||||
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
|
||||
}
|
||||
|
||||
func pickFirstBalancerV1(r naming.Resolver) Balancer {
|
||||
return &pickFirst{&roundRobin{r: r}}
|
||||
}
|
300
vendor/google.golang.org/grpc/balancer_conn_wrappers.go
generated
vendored
Normal file
300
vendor/google.golang.org/grpc/balancer_conn_wrappers.go
generated
vendored
Normal file
|
@ -0,0 +1,300 @@
|
|||
/*
|
||||
*
|
||||
* 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 (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"google.golang.org/grpc/balancer"
|
||||
"google.golang.org/grpc/connectivity"
|
||||
"google.golang.org/grpc/grpclog"
|
||||
"google.golang.org/grpc/resolver"
|
||||
)
|
||||
|
||||
// scStateUpdate contains the subConn and the new state it changed to.
|
||||
type scStateUpdate struct {
|
||||
sc balancer.SubConn
|
||||
state connectivity.State
|
||||
}
|
||||
|
||||
// scStateUpdateBuffer is an unbounded channel for scStateChangeTuple.
|
||||
// TODO make a general purpose buffer that uses interface{}.
|
||||
type scStateUpdateBuffer struct {
|
||||
c chan *scStateUpdate
|
||||
mu sync.Mutex
|
||||
backlog []*scStateUpdate
|
||||
}
|
||||
|
||||
func newSCStateUpdateBuffer() *scStateUpdateBuffer {
|
||||
return &scStateUpdateBuffer{
|
||||
c: make(chan *scStateUpdate, 1),
|
||||
}
|
||||
}
|
||||
|
||||
func (b *scStateUpdateBuffer) put(t *scStateUpdate) {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
if len(b.backlog) == 0 {
|
||||
select {
|
||||
case b.c <- t:
|
||||
return
|
||||
default:
|
||||
}
|
||||
}
|
||||
b.backlog = append(b.backlog, t)
|
||||
}
|
||||
|
||||
func (b *scStateUpdateBuffer) load() {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
if len(b.backlog) > 0 {
|
||||
select {
|
||||
case b.c <- b.backlog[0]:
|
||||
b.backlog[0] = nil
|
||||
b.backlog = b.backlog[1:]
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get returns the channel that the scStateUpdate will be sent to.
|
||||
//
|
||||
// Upon receiving, the caller should call load to send another
|
||||
// scStateChangeTuple onto the channel if there is any.
|
||||
func (b *scStateUpdateBuffer) get() <-chan *scStateUpdate {
|
||||
return b.c
|
||||
}
|
||||
|
||||
// resolverUpdate contains the new resolved addresses or error if there's
|
||||
// any.
|
||||
type resolverUpdate struct {
|
||||
addrs []resolver.Address
|
||||
err error
|
||||
}
|
||||
|
||||
// ccBalancerWrapper is a wrapper on top of cc for balancers.
|
||||
// It implements balancer.ClientConn interface.
|
||||
type ccBalancerWrapper struct {
|
||||
cc *ClientConn
|
||||
balancer balancer.Balancer
|
||||
stateChangeQueue *scStateUpdateBuffer
|
||||
resolverUpdateCh chan *resolverUpdate
|
||||
done chan struct{}
|
||||
|
||||
mu sync.Mutex
|
||||
subConns map[*acBalancerWrapper]struct{}
|
||||
}
|
||||
|
||||
func newCCBalancerWrapper(cc *ClientConn, b balancer.Builder, bopts balancer.BuildOptions) *ccBalancerWrapper {
|
||||
ccb := &ccBalancerWrapper{
|
||||
cc: cc,
|
||||
stateChangeQueue: newSCStateUpdateBuffer(),
|
||||
resolverUpdateCh: make(chan *resolverUpdate, 1),
|
||||
done: make(chan struct{}),
|
||||
subConns: make(map[*acBalancerWrapper]struct{}),
|
||||
}
|
||||
go ccb.watcher()
|
||||
ccb.balancer = b.Build(ccb, bopts)
|
||||
return ccb
|
||||
}
|
||||
|
||||
// watcher balancer functions sequencially, so the balancer can be implemeneted
|
||||
// lock-free.
|
||||
func (ccb *ccBalancerWrapper) watcher() {
|
||||
for {
|
||||
select {
|
||||
case t := <-ccb.stateChangeQueue.get():
|
||||
ccb.stateChangeQueue.load()
|
||||
select {
|
||||
case <-ccb.done:
|
||||
ccb.balancer.Close()
|
||||
return
|
||||
default:
|
||||
}
|
||||
ccb.balancer.HandleSubConnStateChange(t.sc, t.state)
|
||||
case t := <-ccb.resolverUpdateCh:
|
||||
select {
|
||||
case <-ccb.done:
|
||||
ccb.balancer.Close()
|
||||
return
|
||||
default:
|
||||
}
|
||||
ccb.balancer.HandleResolvedAddrs(t.addrs, t.err)
|
||||
case <-ccb.done:
|
||||
}
|
||||
|
||||
select {
|
||||
case <-ccb.done:
|
||||
ccb.balancer.Close()
|
||||
ccb.mu.Lock()
|
||||
scs := ccb.subConns
|
||||
ccb.subConns = nil
|
||||
ccb.mu.Unlock()
|
||||
for acbw := range scs {
|
||||
ccb.cc.removeAddrConn(acbw.getAddrConn(), errConnDrain)
|
||||
}
|
||||
return
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (ccb *ccBalancerWrapper) close() {
|
||||
close(ccb.done)
|
||||
}
|
||||
|
||||
func (ccb *ccBalancerWrapper) handleSubConnStateChange(sc balancer.SubConn, s connectivity.State) {
|
||||
// When updating addresses for a SubConn, if the address in use is not in
|
||||
// the new addresses, the old ac will be tearDown() and a new ac will be
|
||||
// created. tearDown() generates a state change with Shutdown state, we
|
||||
// don't want the balancer to receive this state change. So before
|
||||
// tearDown() on the old ac, ac.acbw (acWrapper) will be set to nil, and
|
||||
// this function will be called with (nil, Shutdown). We don't need to call
|
||||
// balancer method in this case.
|
||||
if sc == nil {
|
||||
return
|
||||
}
|
||||
ccb.stateChangeQueue.put(&scStateUpdate{
|
||||
sc: sc,
|
||||
state: s,
|
||||
})
|
||||
}
|
||||
|
||||
func (ccb *ccBalancerWrapper) handleResolvedAddrs(addrs []resolver.Address, err error) {
|
||||
select {
|
||||
case <-ccb.resolverUpdateCh:
|
||||
default:
|
||||
}
|
||||
ccb.resolverUpdateCh <- &resolverUpdate{
|
||||
addrs: addrs,
|
||||
err: err,
|
||||
}
|
||||
}
|
||||
|
||||
func (ccb *ccBalancerWrapper) NewSubConn(addrs []resolver.Address, opts balancer.NewSubConnOptions) (balancer.SubConn, error) {
|
||||
if len(addrs) <= 0 {
|
||||
return nil, fmt.Errorf("grpc: cannot create SubConn with empty address list")
|
||||
}
|
||||
ccb.mu.Lock()
|
||||
defer ccb.mu.Unlock()
|
||||
if ccb.subConns == nil {
|
||||
return nil, fmt.Errorf("grpc: ClientConn balancer wrapper was closed")
|
||||
}
|
||||
ac, err := ccb.cc.newAddrConn(addrs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
acbw := &acBalancerWrapper{ac: ac}
|
||||
acbw.ac.mu.Lock()
|
||||
ac.acbw = acbw
|
||||
acbw.ac.mu.Unlock()
|
||||
ccb.subConns[acbw] = struct{}{}
|
||||
return acbw, nil
|
||||
}
|
||||
|
||||
func (ccb *ccBalancerWrapper) RemoveSubConn(sc balancer.SubConn) {
|
||||
acbw, ok := sc.(*acBalancerWrapper)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
ccb.mu.Lock()
|
||||
defer ccb.mu.Unlock()
|
||||
if ccb.subConns == nil {
|
||||
return
|
||||
}
|
||||
delete(ccb.subConns, acbw)
|
||||
ccb.cc.removeAddrConn(acbw.getAddrConn(), errConnDrain)
|
||||
}
|
||||
|
||||
func (ccb *ccBalancerWrapper) UpdateBalancerState(s connectivity.State, p balancer.Picker) {
|
||||
ccb.mu.Lock()
|
||||
defer ccb.mu.Unlock()
|
||||
if ccb.subConns == nil {
|
||||
return
|
||||
}
|
||||
ccb.cc.csMgr.updateState(s)
|
||||
ccb.cc.blockingpicker.updatePicker(p)
|
||||
}
|
||||
|
||||
func (ccb *ccBalancerWrapper) ResolveNow(o resolver.ResolveNowOption) {
|
||||
ccb.cc.resolveNow(o)
|
||||
}
|
||||
|
||||
func (ccb *ccBalancerWrapper) Target() string {
|
||||
return ccb.cc.target
|
||||
}
|
||||
|
||||
// acBalancerWrapper is a wrapper on top of ac for balancers.
|
||||
// It implements balancer.SubConn interface.
|
||||
type acBalancerWrapper struct {
|
||||
mu sync.Mutex
|
||||
ac *addrConn
|
||||
}
|
||||
|
||||
func (acbw *acBalancerWrapper) UpdateAddresses(addrs []resolver.Address) {
|
||||
acbw.mu.Lock()
|
||||
defer acbw.mu.Unlock()
|
||||
if len(addrs) <= 0 {
|
||||
acbw.ac.tearDown(errConnDrain)
|
||||
return
|
||||
}
|
||||
if !acbw.ac.tryUpdateAddrs(addrs) {
|
||||
cc := acbw.ac.cc
|
||||
acbw.ac.mu.Lock()
|
||||
// Set old ac.acbw to nil so the Shutdown state update will be ignored
|
||||
// by balancer.
|
||||
//
|
||||
// TODO(bar) the state transition could be wrong when tearDown() old ac
|
||||
// and creating new ac, fix the transition.
|
||||
acbw.ac.acbw = nil
|
||||
acbw.ac.mu.Unlock()
|
||||
acState := acbw.ac.getState()
|
||||
acbw.ac.tearDown(errConnDrain)
|
||||
|
||||
if acState == connectivity.Shutdown {
|
||||
return
|
||||
}
|
||||
|
||||
ac, err := cc.newAddrConn(addrs)
|
||||
if err != nil {
|
||||
grpclog.Warningf("acBalancerWrapper: UpdateAddresses: failed to newAddrConn: %v", err)
|
||||
return
|
||||
}
|
||||
acbw.ac = ac
|
||||
ac.mu.Lock()
|
||||
ac.acbw = acbw
|
||||
ac.mu.Unlock()
|
||||
if acState != connectivity.Idle {
|
||||
ac.connect()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (acbw *acBalancerWrapper) Connect() {
|
||||
acbw.mu.Lock()
|
||||
defer acbw.mu.Unlock()
|
||||
acbw.ac.connect()
|
||||
}
|
||||
|
||||
func (acbw *acBalancerWrapper) getAddrConn() *addrConn {
|
||||
acbw.mu.Lock()
|
||||
defer acbw.mu.Unlock()
|
||||
return acbw.ac
|
||||
}
|
443
vendor/google.golang.org/grpc/balancer_switching_test.go
generated
vendored
Normal file
443
vendor/google.golang.org/grpc/balancer_switching_test.go
generated
vendored
Normal file
|
@ -0,0 +1,443 @@
|
|||
/*
|
||||
*
|
||||
* 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 (
|
||||
"fmt"
|
||||
"math"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc/balancer/roundrobin"
|
||||
_ "google.golang.org/grpc/grpclog/glogger"
|
||||
"google.golang.org/grpc/resolver"
|
||||
"google.golang.org/grpc/resolver/manual"
|
||||
"google.golang.org/grpc/test/leakcheck"
|
||||
)
|
||||
|
||||
func checkPickFirst(cc *ClientConn, servers []*server) error {
|
||||
var (
|
||||
req = "port"
|
||||
reply string
|
||||
err error
|
||||
)
|
||||
connected := false
|
||||
for i := 0; i < 5000; i++ {
|
||||
if err = Invoke(context.Background(), "/foo/bar", &req, &reply, cc); errorDesc(err) == servers[0].port {
|
||||
if connected {
|
||||
// connected is set to false if peer is not server[0]. So if
|
||||
// connected is true here, this is the second time we saw
|
||||
// server[0] in a row. Break because pickfirst is in effect.
|
||||
break
|
||||
}
|
||||
connected = true
|
||||
} else {
|
||||
connected = false
|
||||
}
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
if !connected {
|
||||
return fmt.Errorf("pickfirst is not in effect after 5 second, EmptyCall() = _, %v, want _, %v", err, servers[0].port)
|
||||
}
|
||||
// The following RPCs should all succeed with the first server.
|
||||
for i := 0; i < 3; i++ {
|
||||
err = Invoke(context.Background(), "/foo/bar", &req, &reply, cc)
|
||||
if errorDesc(err) != servers[0].port {
|
||||
return fmt.Errorf("Index %d: want peer %v, got peer %v", i, servers[0].port, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkRoundRobin(cc *ClientConn, servers []*server) error {
|
||||
var (
|
||||
req = "port"
|
||||
reply string
|
||||
err error
|
||||
)
|
||||
|
||||
// Make sure connections to all servers are up.
|
||||
for i := 0; i < 2; i++ {
|
||||
// Do this check twice, otherwise the first RPC's transport may still be
|
||||
// picked by the closing pickfirst balancer, and the test becomes flaky.
|
||||
for _, s := range servers {
|
||||
var up bool
|
||||
for i := 0; i < 5000; i++ {
|
||||
if err = Invoke(context.Background(), "/foo/bar", &req, &reply, cc); errorDesc(err) == s.port {
|
||||
up = true
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
if !up {
|
||||
return fmt.Errorf("server %v is not up within 5 second", s.port)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
serverCount := len(servers)
|
||||
for i := 0; i < 3*serverCount; i++ {
|
||||
err = Invoke(context.Background(), "/foo/bar", &req, &reply, cc)
|
||||
if errorDesc(err) != servers[i%serverCount].port {
|
||||
return fmt.Errorf("Index %d: want peer %v, got peer %v", i, servers[i%serverCount].port, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestSwitchBalancer(t *testing.T) {
|
||||
defer leakcheck.Check(t)
|
||||
r, rcleanup := manual.GenerateAndRegisterManualResolver()
|
||||
defer rcleanup()
|
||||
|
||||
numServers := 2
|
||||
servers, _, scleanup := startServers(t, numServers, math.MaxInt32)
|
||||
defer scleanup()
|
||||
|
||||
cc, err := Dial(r.Scheme()+":///test.server", WithInsecure(), WithCodec(testCodec{}))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to dial: %v", err)
|
||||
}
|
||||
defer cc.Close()
|
||||
r.NewAddress([]resolver.Address{{Addr: servers[0].addr}, {Addr: servers[1].addr}})
|
||||
// The default balancer is pickfirst.
|
||||
if err := checkPickFirst(cc, servers); err != nil {
|
||||
t.Fatalf("check pickfirst returned non-nil error: %v", err)
|
||||
}
|
||||
// Switch to roundrobin.
|
||||
cc.handleServiceConfig(`{"loadBalancingPolicy": "round_robin"}`)
|
||||
if err := checkRoundRobin(cc, servers); err != nil {
|
||||
t.Fatalf("check roundrobin returned non-nil error: %v", err)
|
||||
}
|
||||
// Switch to pickfirst.
|
||||
cc.handleServiceConfig(`{"loadBalancingPolicy": "pick_first"}`)
|
||||
if err := checkPickFirst(cc, servers); err != nil {
|
||||
t.Fatalf("check pickfirst returned non-nil error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Test that balancer specified by dial option will not be overridden.
|
||||
func TestBalancerDialOption(t *testing.T) {
|
||||
defer leakcheck.Check(t)
|
||||
r, rcleanup := manual.GenerateAndRegisterManualResolver()
|
||||
defer rcleanup()
|
||||
|
||||
numServers := 2
|
||||
servers, _, scleanup := startServers(t, numServers, math.MaxInt32)
|
||||
defer scleanup()
|
||||
|
||||
cc, err := Dial(r.Scheme()+":///test.server", WithInsecure(), WithCodec(testCodec{}), WithBalancerName(roundrobin.Name))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to dial: %v", err)
|
||||
}
|
||||
defer cc.Close()
|
||||
r.NewAddress([]resolver.Address{{Addr: servers[0].addr}, {Addr: servers[1].addr}})
|
||||
// The init balancer is roundrobin.
|
||||
if err := checkRoundRobin(cc, servers); err != nil {
|
||||
t.Fatalf("check roundrobin returned non-nil error: %v", err)
|
||||
}
|
||||
// Switch to pickfirst.
|
||||
cc.handleServiceConfig(`{"loadBalancingPolicy": "pick_first"}`)
|
||||
// Balancer is still roundrobin.
|
||||
if err := checkRoundRobin(cc, servers); err != nil {
|
||||
t.Fatalf("check roundrobin returned non-nil error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// First addr update contains grpclb.
|
||||
func TestSwitchBalancerGRPCLBFirst(t *testing.T) {
|
||||
defer leakcheck.Check(t)
|
||||
r, rcleanup := manual.GenerateAndRegisterManualResolver()
|
||||
defer rcleanup()
|
||||
|
||||
cc, err := Dial(r.Scheme()+":///test.server", WithInsecure(), WithCodec(testCodec{}))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to dial: %v", err)
|
||||
}
|
||||
defer cc.Close()
|
||||
|
||||
// ClientConn will switch balancer to grpclb when receives an address of
|
||||
// type GRPCLB.
|
||||
r.NewAddress([]resolver.Address{{Addr: "backend"}, {Addr: "grpclb", Type: resolver.GRPCLB}})
|
||||
var isGRPCLB bool
|
||||
for i := 0; i < 5000; i++ {
|
||||
cc.mu.Lock()
|
||||
isGRPCLB = cc.curBalancerName == "grpclb"
|
||||
cc.mu.Unlock()
|
||||
if isGRPCLB {
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
if !isGRPCLB {
|
||||
t.Fatalf("after 5 second, cc.balancer is of type %v, not grpclb", cc.curBalancerName)
|
||||
}
|
||||
|
||||
// New update containing new backend and new grpclb. Should not switch
|
||||
// balancer.
|
||||
r.NewAddress([]resolver.Address{{Addr: "backend2"}, {Addr: "grpclb2", Type: resolver.GRPCLB}})
|
||||
for i := 0; i < 200; i++ {
|
||||
cc.mu.Lock()
|
||||
isGRPCLB = cc.curBalancerName == "grpclb"
|
||||
cc.mu.Unlock()
|
||||
if !isGRPCLB {
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
if !isGRPCLB {
|
||||
t.Fatalf("within 200 ms, cc.balancer switched to !grpclb, want grpclb")
|
||||
}
|
||||
|
||||
var isPickFirst bool
|
||||
// Switch balancer to pickfirst.
|
||||
r.NewAddress([]resolver.Address{{Addr: "backend"}})
|
||||
for i := 0; i < 5000; i++ {
|
||||
cc.mu.Lock()
|
||||
isPickFirst = cc.curBalancerName == PickFirstBalancerName
|
||||
cc.mu.Unlock()
|
||||
if isPickFirst {
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
if !isPickFirst {
|
||||
t.Fatalf("after 5 second, cc.balancer is of type %v, not pick_first", cc.curBalancerName)
|
||||
}
|
||||
}
|
||||
|
||||
// First addr update does not contain grpclb.
|
||||
func TestSwitchBalancerGRPCLBSecond(t *testing.T) {
|
||||
defer leakcheck.Check(t)
|
||||
r, rcleanup := manual.GenerateAndRegisterManualResolver()
|
||||
defer rcleanup()
|
||||
|
||||
cc, err := Dial(r.Scheme()+":///test.server", WithInsecure(), WithCodec(testCodec{}))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to dial: %v", err)
|
||||
}
|
||||
defer cc.Close()
|
||||
|
||||
r.NewAddress([]resolver.Address{{Addr: "backend"}})
|
||||
var isPickFirst bool
|
||||
for i := 0; i < 5000; i++ {
|
||||
cc.mu.Lock()
|
||||
isPickFirst = cc.curBalancerName == PickFirstBalancerName
|
||||
cc.mu.Unlock()
|
||||
if isPickFirst {
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
if !isPickFirst {
|
||||
t.Fatalf("after 5 second, cc.balancer is of type %v, not pick_first", cc.curBalancerName)
|
||||
}
|
||||
|
||||
// ClientConn will switch balancer to grpclb when receives an address of
|
||||
// type GRPCLB.
|
||||
r.NewAddress([]resolver.Address{{Addr: "backend"}, {Addr: "grpclb", Type: resolver.GRPCLB}})
|
||||
var isGRPCLB bool
|
||||
for i := 0; i < 5000; i++ {
|
||||
cc.mu.Lock()
|
||||
isGRPCLB = cc.curBalancerName == "grpclb"
|
||||
cc.mu.Unlock()
|
||||
if isGRPCLB {
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
if !isGRPCLB {
|
||||
t.Fatalf("after 5 second, cc.balancer is of type %v, not grpclb", cc.curBalancerName)
|
||||
}
|
||||
|
||||
// New update containing new backend and new grpclb. Should not switch
|
||||
// balancer.
|
||||
r.NewAddress([]resolver.Address{{Addr: "backend2"}, {Addr: "grpclb2", Type: resolver.GRPCLB}})
|
||||
for i := 0; i < 200; i++ {
|
||||
cc.mu.Lock()
|
||||
isGRPCLB = cc.curBalancerName == "grpclb"
|
||||
cc.mu.Unlock()
|
||||
if !isGRPCLB {
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
if !isGRPCLB {
|
||||
t.Fatalf("within 200 ms, cc.balancer switched to !grpclb, want grpclb")
|
||||
}
|
||||
|
||||
// Switch balancer back.
|
||||
r.NewAddress([]resolver.Address{{Addr: "backend"}})
|
||||
for i := 0; i < 5000; i++ {
|
||||
cc.mu.Lock()
|
||||
isPickFirst = cc.curBalancerName == PickFirstBalancerName
|
||||
cc.mu.Unlock()
|
||||
if isPickFirst {
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
if !isPickFirst {
|
||||
t.Fatalf("after 5 second, cc.balancer is of type %v, not pick_first", cc.curBalancerName)
|
||||
}
|
||||
}
|
||||
|
||||
// Test that if the current balancer is roundrobin, after switching to grpclb,
|
||||
// when the resolved address doesn't contain grpclb addresses, balancer will be
|
||||
// switched back to roundrobin.
|
||||
func TestSwitchBalancerGRPCLBRoundRobin(t *testing.T) {
|
||||
defer leakcheck.Check(t)
|
||||
r, rcleanup := manual.GenerateAndRegisterManualResolver()
|
||||
defer rcleanup()
|
||||
|
||||
cc, err := Dial(r.Scheme()+":///test.server", WithInsecure(), WithCodec(testCodec{}))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to dial: %v", err)
|
||||
}
|
||||
defer cc.Close()
|
||||
|
||||
r.NewServiceConfig(`{"loadBalancingPolicy": "round_robin"}`)
|
||||
|
||||
r.NewAddress([]resolver.Address{{Addr: "backend"}})
|
||||
var isRoundRobin bool
|
||||
for i := 0; i < 5000; i++ {
|
||||
cc.mu.Lock()
|
||||
isRoundRobin = cc.curBalancerName == "round_robin"
|
||||
cc.mu.Unlock()
|
||||
if isRoundRobin {
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
if !isRoundRobin {
|
||||
t.Fatalf("after 5 second, cc.balancer is of type %v, not round_robin", cc.curBalancerName)
|
||||
}
|
||||
|
||||
// ClientConn will switch balancer to grpclb when receives an address of
|
||||
// type GRPCLB.
|
||||
r.NewAddress([]resolver.Address{{Addr: "grpclb", Type: resolver.GRPCLB}})
|
||||
var isGRPCLB bool
|
||||
for i := 0; i < 5000; i++ {
|
||||
cc.mu.Lock()
|
||||
isGRPCLB = cc.curBalancerName == "grpclb"
|
||||
cc.mu.Unlock()
|
||||
if isGRPCLB {
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
if !isGRPCLB {
|
||||
t.Fatalf("after 5 second, cc.balancer is of type %v, not grpclb", cc.curBalancerName)
|
||||
}
|
||||
|
||||
// Switch balancer back.
|
||||
r.NewAddress([]resolver.Address{{Addr: "backend"}})
|
||||
for i := 0; i < 5000; i++ {
|
||||
cc.mu.Lock()
|
||||
isRoundRobin = cc.curBalancerName == "round_robin"
|
||||
cc.mu.Unlock()
|
||||
if isRoundRobin {
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
if !isRoundRobin {
|
||||
t.Fatalf("after 5 second, cc.balancer is of type %v, not round_robin", cc.curBalancerName)
|
||||
}
|
||||
}
|
||||
|
||||
// Test that if resolved address list contains grpclb, the balancer option in
|
||||
// service config won't take effect. But when there's no grpclb address in a new
|
||||
// resolved address list, balancer will be switched to the new one.
|
||||
func TestSwitchBalancerGRPCLBServiceConfig(t *testing.T) {
|
||||
defer leakcheck.Check(t)
|
||||
r, rcleanup := manual.GenerateAndRegisterManualResolver()
|
||||
defer rcleanup()
|
||||
|
||||
cc, err := Dial(r.Scheme()+":///test.server", WithInsecure(), WithCodec(testCodec{}))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to dial: %v", err)
|
||||
}
|
||||
defer cc.Close()
|
||||
|
||||
r.NewAddress([]resolver.Address{{Addr: "backend"}})
|
||||
var isPickFirst bool
|
||||
for i := 0; i < 5000; i++ {
|
||||
cc.mu.Lock()
|
||||
isPickFirst = cc.curBalancerName == PickFirstBalancerName
|
||||
cc.mu.Unlock()
|
||||
if isPickFirst {
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
if !isPickFirst {
|
||||
t.Fatalf("after 5 second, cc.balancer is of type %v, not pick_first", cc.curBalancerName)
|
||||
}
|
||||
|
||||
// ClientConn will switch balancer to grpclb when receives an address of
|
||||
// type GRPCLB.
|
||||
r.NewAddress([]resolver.Address{{Addr: "grpclb", Type: resolver.GRPCLB}})
|
||||
var isGRPCLB bool
|
||||
for i := 0; i < 5000; i++ {
|
||||
cc.mu.Lock()
|
||||
isGRPCLB = cc.curBalancerName == "grpclb"
|
||||
cc.mu.Unlock()
|
||||
if isGRPCLB {
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
if !isGRPCLB {
|
||||
t.Fatalf("after 5 second, cc.balancer is of type %v, not grpclb", cc.curBalancerName)
|
||||
}
|
||||
|
||||
r.NewServiceConfig(`{"loadBalancingPolicy": "round_robin"}`)
|
||||
var isRoundRobin bool
|
||||
for i := 0; i < 200; i++ {
|
||||
cc.mu.Lock()
|
||||
isRoundRobin = cc.curBalancerName == "round_robin"
|
||||
cc.mu.Unlock()
|
||||
if isRoundRobin {
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
// Balancer should NOT switch to round_robin because resolved list contains
|
||||
// grpclb.
|
||||
if isRoundRobin {
|
||||
t.Fatalf("within 200 ms, cc.balancer switched to round_robin, want grpclb")
|
||||
}
|
||||
|
||||
// Switch balancer back.
|
||||
r.NewAddress([]resolver.Address{{Addr: "backend"}})
|
||||
for i := 0; i < 5000; i++ {
|
||||
cc.mu.Lock()
|
||||
isRoundRobin = cc.curBalancerName == "round_robin"
|
||||
cc.mu.Unlock()
|
||||
if isRoundRobin {
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
if !isRoundRobin {
|
||||
t.Fatalf("after 5 second, cc.balancer is of type %v, not round_robin", cc.curBalancerName)
|
||||
}
|
||||
}
|
804
vendor/google.golang.org/grpc/balancer_test.go
generated
vendored
Normal file
804
vendor/google.golang.org/grpc/balancer_test.go
generated
vendored
Normal file
|
@ -0,0 +1,804 @@
|
|||
/*
|
||||
*
|
||||
* 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 (
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc/codes"
|
||||
_ "google.golang.org/grpc/grpclog/glogger"
|
||||
"google.golang.org/grpc/naming"
|
||||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/grpc/test/leakcheck"
|
||||
|
||||
// V1 balancer tests use passthrough resolver instead of dns.
|
||||
// TODO(bar) remove this when removing v1 balaner entirely.
|
||||
|
||||
_ "google.golang.org/grpc/resolver/passthrough"
|
||||
)
|
||||
|
||||
type testWatcher struct {
|
||||
// the channel to receives name resolution updates
|
||||
update chan *naming.Update
|
||||
// the side channel to get to know how many updates in a batch
|
||||
side chan int
|
||||
// the channel to notifiy update injector that the update reading is done
|
||||
readDone chan int
|
||||
}
|
||||
|
||||
func (w *testWatcher) Next() (updates []*naming.Update, err error) {
|
||||
n := <-w.side
|
||||
if n == 0 {
|
||||
return nil, fmt.Errorf("w.side is closed")
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
u := <-w.update
|
||||
if u != nil {
|
||||
updates = append(updates, u)
|
||||
}
|
||||
}
|
||||
w.readDone <- 0
|
||||
return
|
||||
}
|
||||
|
||||
func (w *testWatcher) Close() {
|
||||
close(w.side)
|
||||
}
|
||||
|
||||
// Inject naming resolution updates to the testWatcher.
|
||||
func (w *testWatcher) inject(updates []*naming.Update) {
|
||||
w.side <- len(updates)
|
||||
for _, u := range updates {
|
||||
w.update <- u
|
||||
}
|
||||
<-w.readDone
|
||||
}
|
||||
|
||||
type testNameResolver struct {
|
||||
w *testWatcher
|
||||
addr string
|
||||
}
|
||||
|
||||
func (r *testNameResolver) Resolve(target string) (naming.Watcher, error) {
|
||||
r.w = &testWatcher{
|
||||
update: make(chan *naming.Update, 1),
|
||||
side: make(chan int, 1),
|
||||
readDone: make(chan int),
|
||||
}
|
||||
r.w.side <- 1
|
||||
r.w.update <- &naming.Update{
|
||||
Op: naming.Add,
|
||||
Addr: r.addr,
|
||||
}
|
||||
go func() {
|
||||
<-r.w.readDone
|
||||
}()
|
||||
return r.w, nil
|
||||
}
|
||||
|
||||
func startServers(t *testing.T, numServers int, maxStreams uint32) ([]*server, *testNameResolver, func()) {
|
||||
var servers []*server
|
||||
for i := 0; i < numServers; i++ {
|
||||
s := newTestServer()
|
||||
servers = append(servers, s)
|
||||
go s.start(t, 0, maxStreams)
|
||||
s.wait(t, 2*time.Second)
|
||||
}
|
||||
// Point to server[0]
|
||||
addr := "localhost:" + servers[0].port
|
||||
return servers, &testNameResolver{
|
||||
addr: addr,
|
||||
}, func() {
|
||||
for i := 0; i < numServers; i++ {
|
||||
servers[i].stop()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNameDiscovery(t *testing.T) {
|
||||
defer leakcheck.Check(t)
|
||||
// Start 2 servers on 2 ports.
|
||||
numServers := 2
|
||||
servers, r, cleanup := startServers(t, numServers, math.MaxUint32)
|
||||
defer cleanup()
|
||||
cc, err := Dial("passthrough:///foo.bar.com", WithBalancer(RoundRobin(r)), WithBlock(), WithInsecure(), WithCodec(testCodec{}))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create ClientConn: %v", err)
|
||||
}
|
||||
defer cc.Close()
|
||||
req := "port"
|
||||
var reply string
|
||||
if err := Invoke(context.Background(), "/foo/bar", &req, &reply, cc); err == nil || errorDesc(err) != servers[0].port {
|
||||
t.Fatalf("grpc.Invoke(_, _, _, _, _) = %v, want %s", err, servers[0].port)
|
||||
}
|
||||
// Inject the name resolution change to remove servers[0] and add servers[1].
|
||||
var updates []*naming.Update
|
||||
updates = append(updates, &naming.Update{
|
||||
Op: naming.Delete,
|
||||
Addr: "localhost:" + servers[0].port,
|
||||
})
|
||||
updates = append(updates, &naming.Update{
|
||||
Op: naming.Add,
|
||||
Addr: "localhost:" + servers[1].port,
|
||||
})
|
||||
r.w.inject(updates)
|
||||
// Loop until the rpcs in flight talks to servers[1].
|
||||
for {
|
||||
if err := Invoke(context.Background(), "/foo/bar", &req, &reply, cc); err != nil && errorDesc(err) == servers[1].port {
|
||||
break
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmptyAddrs(t *testing.T) {
|
||||
defer leakcheck.Check(t)
|
||||
servers, r, cleanup := startServers(t, 1, math.MaxUint32)
|
||||
defer cleanup()
|
||||
cc, err := Dial("passthrough:///foo.bar.com", WithBalancer(RoundRobin(r)), WithBlock(), WithInsecure(), WithCodec(testCodec{}))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create ClientConn: %v", err)
|
||||
}
|
||||
defer cc.Close()
|
||||
var reply string
|
||||
if err := Invoke(context.Background(), "/foo/bar", &expectedRequest, &reply, cc); err != nil || reply != expectedResponse {
|
||||
t.Fatalf("grpc.Invoke(_, _, _, _, _) = %v, reply = %q, want %q, <nil>", err, reply, expectedResponse)
|
||||
}
|
||||
// Inject name resolution change to remove the server so that there is no address
|
||||
// available after that.
|
||||
u := &naming.Update{
|
||||
Op: naming.Delete,
|
||||
Addr: "localhost:" + servers[0].port,
|
||||
}
|
||||
r.w.inject([]*naming.Update{u})
|
||||
// Loop until the above updates apply.
|
||||
for {
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Millisecond)
|
||||
if err := Invoke(ctx, "/foo/bar", &expectedRequest, &reply, cc); err != nil {
|
||||
cancel()
|
||||
break
|
||||
}
|
||||
cancel()
|
||||
}
|
||||
}
|
||||
|
||||
func TestRoundRobin(t *testing.T) {
|
||||
defer leakcheck.Check(t)
|
||||
// Start 3 servers on 3 ports.
|
||||
numServers := 3
|
||||
servers, r, cleanup := startServers(t, numServers, math.MaxUint32)
|
||||
defer cleanup()
|
||||
cc, err := Dial("passthrough:///foo.bar.com", WithBalancer(RoundRobin(r)), WithBlock(), WithInsecure(), WithCodec(testCodec{}))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create ClientConn: %v", err)
|
||||
}
|
||||
defer cc.Close()
|
||||
// Add servers[1] to the service discovery.
|
||||
u := &naming.Update{
|
||||
Op: naming.Add,
|
||||
Addr: "localhost:" + servers[1].port,
|
||||
}
|
||||
r.w.inject([]*naming.Update{u})
|
||||
req := "port"
|
||||
var reply string
|
||||
// Loop until servers[1] is up
|
||||
for {
|
||||
if err := Invoke(context.Background(), "/foo/bar", &req, &reply, cc); err != nil && errorDesc(err) == servers[1].port {
|
||||
break
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
// Add server2[2] to the service discovery.
|
||||
u = &naming.Update{
|
||||
Op: naming.Add,
|
||||
Addr: "localhost:" + servers[2].port,
|
||||
}
|
||||
r.w.inject([]*naming.Update{u})
|
||||
// Loop until both servers[2] are up.
|
||||
for {
|
||||
if err := Invoke(context.Background(), "/foo/bar", &req, &reply, cc); err != nil && errorDesc(err) == servers[2].port {
|
||||
break
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
// Check the incoming RPCs served in a round-robin manner.
|
||||
for i := 0; i < 10; i++ {
|
||||
if err := Invoke(context.Background(), "/foo/bar", &req, &reply, cc); err == nil || errorDesc(err) != servers[i%numServers].port {
|
||||
t.Fatalf("Index %d: Invoke(_, _, _, _, _) = %v, want %s", i, err, servers[i%numServers].port)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCloseWithPendingRPC(t *testing.T) {
|
||||
defer leakcheck.Check(t)
|
||||
servers, r, cleanup := startServers(t, 1, math.MaxUint32)
|
||||
defer cleanup()
|
||||
cc, err := Dial("passthrough:///foo.bar.com", WithBalancer(RoundRobin(r)), WithBlock(), WithInsecure(), WithCodec(testCodec{}))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create ClientConn: %v", err)
|
||||
}
|
||||
defer cc.Close()
|
||||
var reply string
|
||||
if err := Invoke(context.Background(), "/foo/bar", &expectedRequest, &reply, cc, FailFast(false)); err != nil {
|
||||
t.Fatalf("grpc.Invoke(_, _, _, _, _) = %v, want %s", err, servers[0].port)
|
||||
}
|
||||
// Remove the server.
|
||||
updates := []*naming.Update{{
|
||||
Op: naming.Delete,
|
||||
Addr: "localhost:" + servers[0].port,
|
||||
}}
|
||||
r.w.inject(updates)
|
||||
// Loop until the above update applies.
|
||||
for {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Millisecond)
|
||||
if err := Invoke(ctx, "/foo/bar", &expectedRequest, &reply, cc, FailFast(false)); status.Code(err) == codes.DeadlineExceeded {
|
||||
cancel()
|
||||
break
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
cancel()
|
||||
}
|
||||
// Issue 2 RPCs which should be completed with error status once cc is closed.
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(2)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
var reply string
|
||||
if err := Invoke(context.Background(), "/foo/bar", &expectedRequest, &reply, cc, FailFast(false)); err == nil {
|
||||
t.Errorf("grpc.Invoke(_, _, _, _, _) = %v, want not nil", err)
|
||||
}
|
||||
}()
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
var reply string
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
if err := Invoke(context.Background(), "/foo/bar", &expectedRequest, &reply, cc, FailFast(false)); err == nil {
|
||||
t.Errorf("grpc.Invoke(_, _, _, _, _) = %v, want not nil", err)
|
||||
}
|
||||
}()
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
cc.Close()
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func TestGetOnWaitChannel(t *testing.T) {
|
||||
defer leakcheck.Check(t)
|
||||
servers, r, cleanup := startServers(t, 1, math.MaxUint32)
|
||||
defer cleanup()
|
||||
cc, err := Dial("passthrough:///foo.bar.com", WithBalancer(RoundRobin(r)), WithBlock(), WithInsecure(), WithCodec(testCodec{}))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create ClientConn: %v", err)
|
||||
}
|
||||
defer cc.Close()
|
||||
// Remove all servers so that all upcoming RPCs will block on waitCh.
|
||||
updates := []*naming.Update{{
|
||||
Op: naming.Delete,
|
||||
Addr: "localhost:" + servers[0].port,
|
||||
}}
|
||||
r.w.inject(updates)
|
||||
for {
|
||||
var reply string
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Millisecond)
|
||||
if err := Invoke(ctx, "/foo/bar", &expectedRequest, &reply, cc, FailFast(false)); status.Code(err) == codes.DeadlineExceeded {
|
||||
cancel()
|
||||
break
|
||||
}
|
||||
cancel()
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
var reply string
|
||||
if err := Invoke(context.Background(), "/foo/bar", &expectedRequest, &reply, cc, FailFast(false)); err != nil {
|
||||
t.Errorf("grpc.Invoke(_, _, _, _, _) = %v, want <nil>", err)
|
||||
}
|
||||
}()
|
||||
// Add a connected server to get the above RPC through.
|
||||
updates = []*naming.Update{{
|
||||
Op: naming.Add,
|
||||
Addr: "localhost:" + servers[0].port,
|
||||
}}
|
||||
r.w.inject(updates)
|
||||
// Wait until the above RPC succeeds.
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func TestOneServerDown(t *testing.T) {
|
||||
defer leakcheck.Check(t)
|
||||
// Start 2 servers.
|
||||
numServers := 2
|
||||
servers, r, cleanup := startServers(t, numServers, math.MaxUint32)
|
||||
defer cleanup()
|
||||
cc, err := Dial("passthrough:///foo.bar.com", WithBalancer(RoundRobin(r)), WithBlock(), WithInsecure(), WithCodec(testCodec{}), WithWaitForHandshake())
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create ClientConn: %v", err)
|
||||
}
|
||||
defer cc.Close()
|
||||
// Add servers[1] to the service discovery.
|
||||
var updates []*naming.Update
|
||||
updates = append(updates, &naming.Update{
|
||||
Op: naming.Add,
|
||||
Addr: "localhost:" + servers[1].port,
|
||||
})
|
||||
r.w.inject(updates)
|
||||
req := "port"
|
||||
var reply string
|
||||
// Loop until servers[1] is up
|
||||
for {
|
||||
if err := Invoke(context.Background(), "/foo/bar", &req, &reply, cc); err != nil && errorDesc(err) == servers[1].port {
|
||||
break
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
numRPC := 100
|
||||
sleepDuration := 10 * time.Millisecond
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
time.Sleep(sleepDuration)
|
||||
// After sleepDuration, kill server[0].
|
||||
servers[0].stop()
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
// All non-failfast RPCs should not block because there's at least one connection available.
|
||||
for i := 0; i < numRPC; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
time.Sleep(sleepDuration)
|
||||
// After sleepDuration, invoke RPC.
|
||||
// server[0] is killed around the same time to make it racy between balancer and gRPC internals.
|
||||
Invoke(context.Background(), "/foo/bar", &req, &reply, cc, FailFast(false))
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func TestOneAddressRemoval(t *testing.T) {
|
||||
defer leakcheck.Check(t)
|
||||
// Start 2 servers.
|
||||
numServers := 2
|
||||
servers, r, cleanup := startServers(t, numServers, math.MaxUint32)
|
||||
defer cleanup()
|
||||
cc, err := Dial("passthrough:///foo.bar.com", WithBalancer(RoundRobin(r)), WithBlock(), WithInsecure(), WithCodec(testCodec{}))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create ClientConn: %v", err)
|
||||
}
|
||||
defer cc.Close()
|
||||
// Add servers[1] to the service discovery.
|
||||
var updates []*naming.Update
|
||||
updates = append(updates, &naming.Update{
|
||||
Op: naming.Add,
|
||||
Addr: "localhost:" + servers[1].port,
|
||||
})
|
||||
r.w.inject(updates)
|
||||
req := "port"
|
||||
var reply string
|
||||
// Loop until servers[1] is up
|
||||
for {
|
||||
if err := Invoke(context.Background(), "/foo/bar", &req, &reply, cc); err != nil && errorDesc(err) == servers[1].port {
|
||||
break
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
numRPC := 100
|
||||
sleepDuration := 10 * time.Millisecond
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
time.Sleep(sleepDuration)
|
||||
// After sleepDuration, delete server[0].
|
||||
var updates []*naming.Update
|
||||
updates = append(updates, &naming.Update{
|
||||
Op: naming.Delete,
|
||||
Addr: "localhost:" + servers[0].port,
|
||||
})
|
||||
r.w.inject(updates)
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
// All non-failfast RPCs should not fail because there's at least one connection available.
|
||||
for i := 0; i < numRPC; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
var reply string
|
||||
time.Sleep(sleepDuration)
|
||||
// After sleepDuration, invoke RPC.
|
||||
// server[0] is removed around the same time to make it racy between balancer and gRPC internals.
|
||||
if err := Invoke(context.Background(), "/foo/bar", &expectedRequest, &reply, cc, FailFast(false)); err != nil {
|
||||
t.Errorf("grpc.Invoke(_, _, _, _, _) = %v, want not nil", err)
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func checkServerUp(t *testing.T, currentServer *server) {
|
||||
req := "port"
|
||||
port := currentServer.port
|
||||
cc, err := Dial("passthrough:///localhost:"+port, WithBlock(), WithInsecure(), WithCodec(testCodec{}))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create ClientConn: %v", err)
|
||||
}
|
||||
defer cc.Close()
|
||||
var reply string
|
||||
for {
|
||||
if err := Invoke(context.Background(), "/foo/bar", &req, &reply, cc); err != nil && errorDesc(err) == port {
|
||||
break
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPickFirstEmptyAddrs(t *testing.T) {
|
||||
defer leakcheck.Check(t)
|
||||
servers, r, cleanup := startServers(t, 1, math.MaxUint32)
|
||||
defer cleanup()
|
||||
cc, err := Dial("passthrough:///foo.bar.com", WithBalancer(pickFirstBalancerV1(r)), WithBlock(), WithInsecure(), WithCodec(testCodec{}))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create ClientConn: %v", err)
|
||||
}
|
||||
defer cc.Close()
|
||||
var reply string
|
||||
if err := Invoke(context.Background(), "/foo/bar", &expectedRequest, &reply, cc); err != nil || reply != expectedResponse {
|
||||
t.Fatalf("grpc.Invoke(_, _, _, _, _) = %v, reply = %q, want %q, <nil>", err, reply, expectedResponse)
|
||||
}
|
||||
// Inject name resolution change to remove the server so that there is no address
|
||||
// available after that.
|
||||
u := &naming.Update{
|
||||
Op: naming.Delete,
|
||||
Addr: "localhost:" + servers[0].port,
|
||||
}
|
||||
r.w.inject([]*naming.Update{u})
|
||||
// Loop until the above updates apply.
|
||||
for {
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Millisecond)
|
||||
if err := Invoke(ctx, "/foo/bar", &expectedRequest, &reply, cc); err != nil {
|
||||
cancel()
|
||||
break
|
||||
}
|
||||
cancel()
|
||||
}
|
||||
}
|
||||
|
||||
func TestPickFirstCloseWithPendingRPC(t *testing.T) {
|
||||
defer leakcheck.Check(t)
|
||||
servers, r, cleanup := startServers(t, 1, math.MaxUint32)
|
||||
defer cleanup()
|
||||
cc, err := Dial("passthrough:///foo.bar.com", WithBalancer(pickFirstBalancerV1(r)), WithBlock(), WithInsecure(), WithCodec(testCodec{}))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create ClientConn: %v", err)
|
||||
}
|
||||
defer cc.Close()
|
||||
var reply string
|
||||
if err := Invoke(context.Background(), "/foo/bar", &expectedRequest, &reply, cc, FailFast(false)); err != nil {
|
||||
t.Fatalf("grpc.Invoke(_, _, _, _, _) = %v, want %s", err, servers[0].port)
|
||||
}
|
||||
// Remove the server.
|
||||
updates := []*naming.Update{{
|
||||
Op: naming.Delete,
|
||||
Addr: "localhost:" + servers[0].port,
|
||||
}}
|
||||
r.w.inject(updates)
|
||||
// Loop until the above update applies.
|
||||
for {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Millisecond)
|
||||
if err := Invoke(ctx, "/foo/bar", &expectedRequest, &reply, cc, FailFast(false)); status.Code(err) == codes.DeadlineExceeded {
|
||||
cancel()
|
||||
break
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
cancel()
|
||||
}
|
||||
// Issue 2 RPCs which should be completed with error status once cc is closed.
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(2)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
var reply string
|
||||
if err := Invoke(context.Background(), "/foo/bar", &expectedRequest, &reply, cc, FailFast(false)); err == nil {
|
||||
t.Errorf("grpc.Invoke(_, _, _, _, _) = %v, want not nil", err)
|
||||
}
|
||||
}()
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
var reply string
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
if err := Invoke(context.Background(), "/foo/bar", &expectedRequest, &reply, cc, FailFast(false)); err == nil {
|
||||
t.Errorf("grpc.Invoke(_, _, _, _, _) = %v, want not nil", err)
|
||||
}
|
||||
}()
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
cc.Close()
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func TestPickFirstOrderAllServerUp(t *testing.T) {
|
||||
defer leakcheck.Check(t)
|
||||
// Start 3 servers on 3 ports.
|
||||
numServers := 3
|
||||
servers, r, cleanup := startServers(t, numServers, math.MaxUint32)
|
||||
defer cleanup()
|
||||
cc, err := Dial("passthrough:///foo.bar.com", WithBalancer(pickFirstBalancerV1(r)), WithBlock(), WithInsecure(), WithCodec(testCodec{}))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create ClientConn: %v", err)
|
||||
}
|
||||
defer cc.Close()
|
||||
// Add servers[1] and [2] to the service discovery.
|
||||
u := &naming.Update{
|
||||
Op: naming.Add,
|
||||
Addr: "localhost:" + servers[1].port,
|
||||
}
|
||||
r.w.inject([]*naming.Update{u})
|
||||
|
||||
u = &naming.Update{
|
||||
Op: naming.Add,
|
||||
Addr: "localhost:" + servers[2].port,
|
||||
}
|
||||
r.w.inject([]*naming.Update{u})
|
||||
|
||||
// Loop until all 3 servers are up
|
||||
checkServerUp(t, servers[0])
|
||||
checkServerUp(t, servers[1])
|
||||
checkServerUp(t, servers[2])
|
||||
|
||||
// Check the incoming RPCs served in server[0]
|
||||
req := "port"
|
||||
var reply string
|
||||
for i := 0; i < 20; i++ {
|
||||
if err := Invoke(context.Background(), "/foo/bar", &req, &reply, cc); err == nil || errorDesc(err) != servers[0].port {
|
||||
t.Fatalf("Index %d: Invoke(_, _, _, _, _) = %v, want %s", 0, err, servers[0].port)
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
|
||||
// Delete server[0] in the balancer, the incoming RPCs served in server[1]
|
||||
// For test addrconn, close server[0] instead
|
||||
u = &naming.Update{
|
||||
Op: naming.Delete,
|
||||
Addr: "localhost:" + servers[0].port,
|
||||
}
|
||||
r.w.inject([]*naming.Update{u})
|
||||
// Loop until it changes to server[1]
|
||||
for {
|
||||
if err := Invoke(context.Background(), "/foo/bar", &req, &reply, cc); err != nil && errorDesc(err) == servers[1].port {
|
||||
break
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
for i := 0; i < 20; i++ {
|
||||
if err := Invoke(context.Background(), "/foo/bar", &req, &reply, cc); err == nil || errorDesc(err) != servers[1].port {
|
||||
t.Fatalf("Index %d: Invoke(_, _, _, _, _) = %v, want %s", 1, err, servers[1].port)
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
|
||||
// Add server[0] back to the balancer, the incoming RPCs served in server[1]
|
||||
// Add is append operation, the order of Notify now is {server[1].port server[2].port server[0].port}
|
||||
u = &naming.Update{
|
||||
Op: naming.Add,
|
||||
Addr: "localhost:" + servers[0].port,
|
||||
}
|
||||
r.w.inject([]*naming.Update{u})
|
||||
for i := 0; i < 20; i++ {
|
||||
if err := Invoke(context.Background(), "/foo/bar", &req, &reply, cc); err == nil || errorDesc(err) != servers[1].port {
|
||||
t.Fatalf("Index %d: Invoke(_, _, _, _, _) = %v, want %s", 1, err, servers[1].port)
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
|
||||
// Delete server[1] in the balancer, the incoming RPCs served in server[2]
|
||||
u = &naming.Update{
|
||||
Op: naming.Delete,
|
||||
Addr: "localhost:" + servers[1].port,
|
||||
}
|
||||
r.w.inject([]*naming.Update{u})
|
||||
for {
|
||||
if err := Invoke(context.Background(), "/foo/bar", &req, &reply, cc); err != nil && errorDesc(err) == servers[2].port {
|
||||
break
|
||||
}
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
for i := 0; i < 20; i++ {
|
||||
if err := Invoke(context.Background(), "/foo/bar", &req, &reply, cc); err == nil || errorDesc(err) != servers[2].port {
|
||||
t.Fatalf("Index %d: Invoke(_, _, _, _, _) = %v, want %s", 2, err, servers[2].port)
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
|
||||
// Delete server[2] in the balancer, the incoming RPCs served in server[0]
|
||||
u = &naming.Update{
|
||||
Op: naming.Delete,
|
||||
Addr: "localhost:" + servers[2].port,
|
||||
}
|
||||
r.w.inject([]*naming.Update{u})
|
||||
for {
|
||||
if err := Invoke(context.Background(), "/foo/bar", &req, &reply, cc); err != nil && errorDesc(err) == servers[0].port {
|
||||
break
|
||||
}
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
for i := 0; i < 20; i++ {
|
||||
if err := Invoke(context.Background(), "/foo/bar", &req, &reply, cc); err == nil || errorDesc(err) != servers[0].port {
|
||||
t.Fatalf("Index %d: Invoke(_, _, _, _, _) = %v, want %s", 0, err, servers[0].port)
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPickFirstOrderOneServerDown(t *testing.T) {
|
||||
defer leakcheck.Check(t)
|
||||
// Start 3 servers on 3 ports.
|
||||
numServers := 3
|
||||
servers, r, cleanup := startServers(t, numServers, math.MaxUint32)
|
||||
defer cleanup()
|
||||
cc, err := Dial("passthrough:///foo.bar.com", WithBalancer(pickFirstBalancerV1(r)), WithBlock(), WithInsecure(), WithCodec(testCodec{}), WithWaitForHandshake())
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create ClientConn: %v", err)
|
||||
}
|
||||
defer cc.Close()
|
||||
// Add servers[1] and [2] to the service discovery.
|
||||
u := &naming.Update{
|
||||
Op: naming.Add,
|
||||
Addr: "localhost:" + servers[1].port,
|
||||
}
|
||||
r.w.inject([]*naming.Update{u})
|
||||
|
||||
u = &naming.Update{
|
||||
Op: naming.Add,
|
||||
Addr: "localhost:" + servers[2].port,
|
||||
}
|
||||
r.w.inject([]*naming.Update{u})
|
||||
|
||||
// Loop until all 3 servers are up
|
||||
checkServerUp(t, servers[0])
|
||||
checkServerUp(t, servers[1])
|
||||
checkServerUp(t, servers[2])
|
||||
|
||||
// Check the incoming RPCs served in server[0]
|
||||
req := "port"
|
||||
var reply string
|
||||
for i := 0; i < 20; i++ {
|
||||
if err := Invoke(context.Background(), "/foo/bar", &req, &reply, cc); err == nil || errorDesc(err) != servers[0].port {
|
||||
t.Fatalf("Index %d: Invoke(_, _, _, _, _) = %v, want %s", 0, err, servers[0].port)
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
|
||||
// server[0] down, incoming RPCs served in server[1], but the order of Notify still remains
|
||||
// {server[0] server[1] server[2]}
|
||||
servers[0].stop()
|
||||
// Loop until it changes to server[1]
|
||||
for {
|
||||
if err := Invoke(context.Background(), "/foo/bar", &req, &reply, cc); err != nil && errorDesc(err) == servers[1].port {
|
||||
break
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
for i := 0; i < 20; i++ {
|
||||
if err := Invoke(context.Background(), "/foo/bar", &req, &reply, cc); err == nil || errorDesc(err) != servers[1].port {
|
||||
t.Fatalf("Index %d: Invoke(_, _, _, _, _) = %v, want %s", 1, err, servers[1].port)
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
|
||||
// up the server[0] back, the incoming RPCs served in server[1]
|
||||
p, _ := strconv.Atoi(servers[0].port)
|
||||
servers[0] = newTestServer()
|
||||
go servers[0].start(t, p, math.MaxUint32)
|
||||
defer servers[0].stop()
|
||||
servers[0].wait(t, 2*time.Second)
|
||||
checkServerUp(t, servers[0])
|
||||
|
||||
for i := 0; i < 20; i++ {
|
||||
if err := Invoke(context.Background(), "/foo/bar", &req, &reply, cc); err == nil || errorDesc(err) != servers[1].port {
|
||||
t.Fatalf("Index %d: Invoke(_, _, _, _, _) = %v, want %s", 1, err, servers[1].port)
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
|
||||
// Delete server[1] in the balancer, the incoming RPCs served in server[0]
|
||||
u = &naming.Update{
|
||||
Op: naming.Delete,
|
||||
Addr: "localhost:" + servers[1].port,
|
||||
}
|
||||
r.w.inject([]*naming.Update{u})
|
||||
for {
|
||||
if err := Invoke(context.Background(), "/foo/bar", &req, &reply, cc); err != nil && errorDesc(err) == servers[0].port {
|
||||
break
|
||||
}
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
for i := 0; i < 20; i++ {
|
||||
if err := Invoke(context.Background(), "/foo/bar", &req, &reply, cc); err == nil || errorDesc(err) != servers[0].port {
|
||||
t.Fatalf("Index %d: Invoke(_, _, _, _, _) = %v, want %s", 0, err, servers[0].port)
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPickFirstOneAddressRemoval(t *testing.T) {
|
||||
defer leakcheck.Check(t)
|
||||
// Start 2 servers.
|
||||
numServers := 2
|
||||
servers, r, cleanup := startServers(t, numServers, math.MaxUint32)
|
||||
defer cleanup()
|
||||
cc, err := Dial("passthrough:///localhost:"+servers[0].port, WithBalancer(pickFirstBalancerV1(r)), WithBlock(), WithInsecure(), WithCodec(testCodec{}))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create ClientConn: %v", err)
|
||||
}
|
||||
defer cc.Close()
|
||||
// Add servers[1] to the service discovery.
|
||||
var updates []*naming.Update
|
||||
updates = append(updates, &naming.Update{
|
||||
Op: naming.Add,
|
||||
Addr: "localhost:" + servers[1].port,
|
||||
})
|
||||
r.w.inject(updates)
|
||||
|
||||
// Create a new cc to Loop until servers[1] is up
|
||||
checkServerUp(t, servers[0])
|
||||
checkServerUp(t, servers[1])
|
||||
|
||||
var wg sync.WaitGroup
|
||||
numRPC := 100
|
||||
sleepDuration := 10 * time.Millisecond
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
time.Sleep(sleepDuration)
|
||||
// After sleepDuration, delete server[0].
|
||||
var updates []*naming.Update
|
||||
updates = append(updates, &naming.Update{
|
||||
Op: naming.Delete,
|
||||
Addr: "localhost:" + servers[0].port,
|
||||
})
|
||||
r.w.inject(updates)
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
// All non-failfast RPCs should not fail because there's at least one connection available.
|
||||
for i := 0; i < numRPC; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
var reply string
|
||||
time.Sleep(sleepDuration)
|
||||
// After sleepDuration, invoke RPC.
|
||||
// server[0] is removed around the same time to make it racy between balancer and gRPC internals.
|
||||
if err := Invoke(context.Background(), "/foo/bar", &expectedRequest, &reply, cc, FailFast(false)); err != nil {
|
||||
t.Errorf("grpc.Invoke(_, _, _, _, _) = %v, want not nil", err)
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
375
vendor/google.golang.org/grpc/balancer_v1_wrapper.go
generated
vendored
Normal file
375
vendor/google.golang.org/grpc/balancer_v1_wrapper.go
generated
vendored
Normal file
|
@ -0,0 +1,375 @@
|
|||
/*
|
||||
*
|
||||
* 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 (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc/balancer"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/connectivity"
|
||||
"google.golang.org/grpc/grpclog"
|
||||
"google.golang.org/grpc/resolver"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
type balancerWrapperBuilder struct {
|
||||
b Balancer // The v1 balancer.
|
||||
}
|
||||
|
||||
func (bwb *balancerWrapperBuilder) Build(cc balancer.ClientConn, opts balancer.BuildOptions) balancer.Balancer {
|
||||
targetAddr := cc.Target()
|
||||
targetSplitted := strings.Split(targetAddr, ":///")
|
||||
if len(targetSplitted) >= 2 {
|
||||
targetAddr = targetSplitted[1]
|
||||
}
|
||||
|
||||
bwb.b.Start(targetAddr, BalancerConfig{
|
||||
DialCreds: opts.DialCreds,
|
||||
Dialer: opts.Dialer,
|
||||
})
|
||||
_, pickfirst := bwb.b.(*pickFirst)
|
||||
bw := &balancerWrapper{
|
||||
balancer: bwb.b,
|
||||
pickfirst: pickfirst,
|
||||
cc: cc,
|
||||
targetAddr: targetAddr,
|
||||
startCh: make(chan struct{}),
|
||||
conns: make(map[resolver.Address]balancer.SubConn),
|
||||
connSt: make(map[balancer.SubConn]*scState),
|
||||
csEvltr: &connectivityStateEvaluator{},
|
||||
state: connectivity.Idle,
|
||||
}
|
||||
cc.UpdateBalancerState(connectivity.Idle, 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.
|
||||
|
||||
// To aggregate the connectivity state.
|
||||
csEvltr *connectivityStateEvaluator
|
||||
state connectivity.State
|
||||
|
||||
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{}
|
||||
}
|
||||
|
||||
// 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\n", 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.UpdateBalancerState(bw.state, bw)
|
||||
if s == connectivity.Shutdown {
|
||||
// Remove state for this sc.
|
||||
delete(bw.connSt, sc)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
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.
|
||||
return
|
||||
}
|
||||
|
||||
func (bw *balancerWrapper) Close() {
|
||||
bw.mu.Lock()
|
||||
defer bw.mu.Unlock()
|
||||
select {
|
||||
case <-bw.startCh:
|
||||
default:
|
||||
close(bw.startCh)
|
||||
}
|
||||
bw.balancer.Close()
|
||||
return
|
||||
}
|
||||
|
||||
// The picker is the balancerWrapper itself.
|
||||
// Pick should never return ErrNoSubConnAvailable.
|
||||
// It either blocks or returns error, consistent with v1 balancer Get().
|
||||
func (bw *balancerWrapper) Pick(ctx context.Context, opts balancer.PickOptions) (balancer.SubConn, func(balancer.DoneInfo), error) {
|
||||
failfast := true // Default failfast is true.
|
||||
if ss, ok := rpcInfoFromContext(ctx); ok {
|
||||
failfast = ss.failfast
|
||||
}
|
||||
a, p, err := bw.balancer.Get(ctx, BalancerGetOptions{BlockingWait: !failfast})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
var done func(balancer.DoneInfo)
|
||||
if p != nil {
|
||||
done = func(i balancer.DoneInfo) { p() }
|
||||
}
|
||||
var sc balancer.SubConn
|
||||
bw.mu.Lock()
|
||||
defer bw.mu.Unlock()
|
||||
if bw.pickfirst {
|
||||
// Get the first sc in conns.
|
||||
for _, sc = range bw.conns {
|
||||
break
|
||||
}
|
||||
} else {
|
||||
var ok bool
|
||||
sc, ok = bw.conns[resolver.Address{
|
||||
Addr: a.Addr,
|
||||
Type: resolver.Backend,
|
||||
ServerName: "",
|
||||
Metadata: a.Metadata,
|
||||
}]
|
||||
if !ok && failfast {
|
||||
return nil, nil, status.Errorf(codes.Unavailable, "there is no connection available")
|
||||
}
|
||||
if s, ok := bw.connSt[sc]; failfast && (!ok || s.s != connectivity.Ready) {
|
||||
// If the returned sc is not ready and RPC is failfast,
|
||||
// return error, and this RPC will fail.
|
||||
return nil, nil, status.Errorf(codes.Unavailable, "there is no connection available")
|
||||
}
|
||||
}
|
||||
|
||||
return sc, done, nil
|
||||
}
|
||||
|
||||
// connectivityStateEvaluator gets updated by addrConns when their
|
||||
// states transition, based on which it evaluates the state of
|
||||
// ClientConn.
|
||||
type connectivityStateEvaluator struct {
|
||||
mu sync.Mutex
|
||||
numReady uint64 // Number of addrConns in ready state.
|
||||
numConnecting uint64 // Number of addrConns in connecting state.
|
||||
numTransientFailure uint64 // Number of addrConns in transientFailure.
|
||||
}
|
||||
|
||||
// recordTransition records state change happening in every subConn and based on
|
||||
// that it evaluates what aggregated state should be.
|
||||
// It can only transition between Ready, Connecting and TransientFailure. Other states,
|
||||
// Idle and Shutdown are transitioned into by ClientConn; in the beginning of the connection
|
||||
// before any subConn is created ClientConn is in idle state. In the end when ClientConn
|
||||
// closes it is in Shutdown state.
|
||||
// TODO Note that in later releases, a ClientConn with no activity will be put into an Idle state.
|
||||
func (cse *connectivityStateEvaluator) recordTransition(oldState, newState connectivity.State) connectivity.State {
|
||||
cse.mu.Lock()
|
||||
defer cse.mu.Unlock()
|
||||
|
||||
// Update counters.
|
||||
for idx, state := range []connectivity.State{oldState, newState} {
|
||||
updateVal := 2*uint64(idx) - 1 // -1 for oldState and +1 for new.
|
||||
switch state {
|
||||
case connectivity.Ready:
|
||||
cse.numReady += updateVal
|
||||
case connectivity.Connecting:
|
||||
cse.numConnecting += updateVal
|
||||
case connectivity.TransientFailure:
|
||||
cse.numTransientFailure += updateVal
|
||||
}
|
||||
}
|
||||
|
||||
// Evaluate.
|
||||
if cse.numReady > 0 {
|
||||
return connectivity.Ready
|
||||
}
|
||||
if cse.numConnecting > 0 {
|
||||
return connectivity.Connecting
|
||||
}
|
||||
return connectivity.TransientFailure
|
||||
}
|
74
vendor/google.golang.org/grpc/call.go
generated
vendored
Normal file
74
vendor/google.golang.org/grpc/call.go
generated
vendored
Normal file
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
*
|
||||
* 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 grpc
|
||||
|
||||
import (
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// Invoke sends the RPC request on the wire and returns after response is
|
||||
// received. This is typically called by generated code.
|
||||
//
|
||||
// All errors returned by Invoke are compatible with the status package.
|
||||
func (cc *ClientConn) Invoke(ctx context.Context, method string, args, reply interface{}, opts ...CallOption) error {
|
||||
if cc.dopts.unaryInt != nil {
|
||||
return cc.dopts.unaryInt(ctx, method, args, reply, cc, invoke, opts...)
|
||||
}
|
||||
return invoke(ctx, method, args, reply, cc, opts...)
|
||||
}
|
||||
|
||||
// Invoke sends the RPC request on the wire and returns after response is
|
||||
// received. This is typically called by generated code.
|
||||
//
|
||||
// DEPRECATED: Use ClientConn.Invoke instead.
|
||||
func Invoke(ctx context.Context, method string, args, reply interface{}, cc *ClientConn, opts ...CallOption) error {
|
||||
return cc.Invoke(ctx, method, args, reply, opts...)
|
||||
}
|
||||
|
||||
var unaryStreamDesc = &StreamDesc{ServerStreams: false, ClientStreams: false}
|
||||
|
||||
func invoke(ctx context.Context, method string, req, reply interface{}, cc *ClientConn, opts ...CallOption) error {
|
||||
// TODO: implement retries in clientStream and make this simply
|
||||
// newClientStream, SendMsg, RecvMsg.
|
||||
firstAttempt := true
|
||||
for {
|
||||
csInt, err := newClientStream(ctx, unaryStreamDesc, cc, method, opts...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cs := csInt.(*clientStream)
|
||||
if err := cs.SendMsg(req); err != nil {
|
||||
if !cs.c.failFast && cs.s.Unprocessed() && firstAttempt {
|
||||
// TODO: Add a field to header for grpc-transparent-retry-attempts
|
||||
firstAttempt = false
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
if err := cs.RecvMsg(reply); err != nil {
|
||||
if !cs.c.failFast && cs.s.Unprocessed() && firstAttempt {
|
||||
// TODO: Add a field to header for grpc-transparent-retry-attempts
|
||||
firstAttempt = false
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
292
vendor/google.golang.org/grpc/call_test.go
generated
vendored
Normal file
292
vendor/google.golang.org/grpc/call_test.go
generated
vendored
Normal file
|
@ -0,0 +1,292 @@
|
|||
/*
|
||||
*
|
||||
* 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 grpc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/grpc/test/leakcheck"
|
||||
"google.golang.org/grpc/transport"
|
||||
)
|
||||
|
||||
var (
|
||||
expectedRequest = "ping"
|
||||
expectedResponse = "pong"
|
||||
weirdError = "format verbs: %v%s"
|
||||
sizeLargeErr = 1024 * 1024
|
||||
canceled = 0
|
||||
)
|
||||
|
||||
type testCodec struct {
|
||||
}
|
||||
|
||||
func (testCodec) Marshal(v interface{}) ([]byte, error) {
|
||||
return []byte(*(v.(*string))), nil
|
||||
}
|
||||
|
||||
func (testCodec) Unmarshal(data []byte, v interface{}) error {
|
||||
*(v.(*string)) = string(data)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (testCodec) String() string {
|
||||
return "test"
|
||||
}
|
||||
|
||||
type testStreamHandler struct {
|
||||
port string
|
||||
t transport.ServerTransport
|
||||
}
|
||||
|
||||
func (h *testStreamHandler) handleStream(t *testing.T, s *transport.Stream) {
|
||||
p := &parser{r: s}
|
||||
for {
|
||||
pf, req, err := p.recvMsg(math.MaxInt32)
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if pf != compressionNone {
|
||||
t.Errorf("Received the mistaken message format %d, want %d", pf, compressionNone)
|
||||
return
|
||||
}
|
||||
var v string
|
||||
codec := testCodec{}
|
||||
if err := codec.Unmarshal(req, &v); err != nil {
|
||||
t.Errorf("Failed to unmarshal the received message: %v", err)
|
||||
return
|
||||
}
|
||||
if v == "weird error" {
|
||||
h.t.WriteStatus(s, status.New(codes.Internal, weirdError))
|
||||
return
|
||||
}
|
||||
if v == "canceled" {
|
||||
canceled++
|
||||
h.t.WriteStatus(s, status.New(codes.Internal, ""))
|
||||
return
|
||||
}
|
||||
if v == "port" {
|
||||
h.t.WriteStatus(s, status.New(codes.Internal, h.port))
|
||||
return
|
||||
}
|
||||
|
||||
if v != expectedRequest {
|
||||
h.t.WriteStatus(s, status.New(codes.Internal, strings.Repeat("A", sizeLargeErr)))
|
||||
return
|
||||
}
|
||||
}
|
||||
// send a response back to end the stream.
|
||||
hdr, data, err := encode(testCodec{}, &expectedResponse, nil, nil, nil)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to encode the response: %v", err)
|
||||
return
|
||||
}
|
||||
h.t.Write(s, hdr, data, &transport.Options{})
|
||||
h.t.WriteStatus(s, status.New(codes.OK, ""))
|
||||
}
|
||||
|
||||
type server struct {
|
||||
lis net.Listener
|
||||
port string
|
||||
addr string
|
||||
startedErr chan error // sent nil or an error after server starts
|
||||
mu sync.Mutex
|
||||
conns map[transport.ServerTransport]bool
|
||||
}
|
||||
|
||||
func newTestServer() *server {
|
||||
return &server{startedErr: make(chan error, 1)}
|
||||
}
|
||||
|
||||
// start starts server. Other goroutines should block on s.startedErr for further operations.
|
||||
func (s *server) start(t *testing.T, port int, maxStreams uint32) {
|
||||
var err error
|
||||
if port == 0 {
|
||||
s.lis, err = net.Listen("tcp", "localhost:0")
|
||||
} else {
|
||||
s.lis, err = net.Listen("tcp", "localhost:"+strconv.Itoa(port))
|
||||
}
|
||||
if err != nil {
|
||||
s.startedErr <- fmt.Errorf("failed to listen: %v", err)
|
||||
return
|
||||
}
|
||||
s.addr = s.lis.Addr().String()
|
||||
_, p, err := net.SplitHostPort(s.addr)
|
||||
if err != nil {
|
||||
s.startedErr <- fmt.Errorf("failed to parse listener address: %v", err)
|
||||
return
|
||||
}
|
||||
s.port = p
|
||||
s.conns = make(map[transport.ServerTransport]bool)
|
||||
s.startedErr <- nil
|
||||
for {
|
||||
conn, err := s.lis.Accept()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
config := &transport.ServerConfig{
|
||||
MaxStreams: maxStreams,
|
||||
}
|
||||
st, err := transport.NewServerTransport("http2", conn, config)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
s.mu.Lock()
|
||||
if s.conns == nil {
|
||||
s.mu.Unlock()
|
||||
st.Close()
|
||||
return
|
||||
}
|
||||
s.conns[st] = true
|
||||
s.mu.Unlock()
|
||||
h := &testStreamHandler{
|
||||
port: s.port,
|
||||
t: st,
|
||||
}
|
||||
go st.HandleStreams(func(s *transport.Stream) {
|
||||
go h.handleStream(t, s)
|
||||
}, func(ctx context.Context, method string) context.Context {
|
||||
return ctx
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *server) wait(t *testing.T, timeout time.Duration) {
|
||||
select {
|
||||
case err := <-s.startedErr:
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
case <-time.After(timeout):
|
||||
t.Fatalf("Timed out after %v waiting for server to be ready", timeout)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *server) stop() {
|
||||
s.lis.Close()
|
||||
s.mu.Lock()
|
||||
for c := range s.conns {
|
||||
c.Close()
|
||||
}
|
||||
s.conns = nil
|
||||
s.mu.Unlock()
|
||||
}
|
||||
|
||||
func setUp(t *testing.T, port int, maxStreams uint32) (*server, *ClientConn) {
|
||||
server := newTestServer()
|
||||
go server.start(t, port, maxStreams)
|
||||
server.wait(t, 2*time.Second)
|
||||
addr := "localhost:" + server.port
|
||||
cc, err := Dial(addr, WithBlock(), WithInsecure(), WithCodec(testCodec{}))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create ClientConn: %v", err)
|
||||
}
|
||||
return server, cc
|
||||
}
|
||||
|
||||
func TestInvoke(t *testing.T) {
|
||||
defer leakcheck.Check(t)
|
||||
server, cc := setUp(t, 0, math.MaxUint32)
|
||||
var reply string
|
||||
if err := Invoke(context.Background(), "/foo/bar", &expectedRequest, &reply, cc); err != nil || reply != expectedResponse {
|
||||
t.Fatalf("grpc.Invoke(_, _, _, _, _) = %v, want <nil>", err)
|
||||
}
|
||||
cc.Close()
|
||||
server.stop()
|
||||
}
|
||||
|
||||
func TestInvokeLargeErr(t *testing.T) {
|
||||
defer leakcheck.Check(t)
|
||||
server, cc := setUp(t, 0, math.MaxUint32)
|
||||
var reply string
|
||||
req := "hello"
|
||||
err := Invoke(context.Background(), "/foo/bar", &req, &reply, cc)
|
||||
if _, ok := status.FromError(err); !ok {
|
||||
t.Fatalf("grpc.Invoke(_, _, _, _, _) receives non rpc error.")
|
||||
}
|
||||
if status.Code(err) != codes.Internal || len(errorDesc(err)) != sizeLargeErr {
|
||||
t.Fatalf("grpc.Invoke(_, _, _, _, _) = %v, want an error of code %d and desc size %d", err, codes.Internal, sizeLargeErr)
|
||||
}
|
||||
cc.Close()
|
||||
server.stop()
|
||||
}
|
||||
|
||||
// TestInvokeErrorSpecialChars checks that error messages don't get mangled.
|
||||
func TestInvokeErrorSpecialChars(t *testing.T) {
|
||||
defer leakcheck.Check(t)
|
||||
server, cc := setUp(t, 0, math.MaxUint32)
|
||||
var reply string
|
||||
req := "weird error"
|
||||
err := Invoke(context.Background(), "/foo/bar", &req, &reply, cc)
|
||||
if _, ok := status.FromError(err); !ok {
|
||||
t.Fatalf("grpc.Invoke(_, _, _, _, _) receives non rpc error.")
|
||||
}
|
||||
if got, want := errorDesc(err), weirdError; got != want {
|
||||
t.Fatalf("grpc.Invoke(_, _, _, _, _) error = %q, want %q", got, want)
|
||||
}
|
||||
cc.Close()
|
||||
server.stop()
|
||||
}
|
||||
|
||||
// TestInvokeCancel checks that an Invoke with a canceled context is not sent.
|
||||
func TestInvokeCancel(t *testing.T) {
|
||||
defer leakcheck.Check(t)
|
||||
server, cc := setUp(t, 0, math.MaxUint32)
|
||||
var reply string
|
||||
req := "canceled"
|
||||
for i := 0; i < 100; i++ {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel()
|
||||
Invoke(ctx, "/foo/bar", &req, &reply, cc)
|
||||
}
|
||||
if canceled != 0 {
|
||||
t.Fatalf("received %d of 100 canceled requests", canceled)
|
||||
}
|
||||
cc.Close()
|
||||
server.stop()
|
||||
}
|
||||
|
||||
// TestInvokeCancelClosedNonFail checks that a canceled non-failfast RPC
|
||||
// on a closed client will terminate.
|
||||
func TestInvokeCancelClosedNonFailFast(t *testing.T) {
|
||||
defer leakcheck.Check(t)
|
||||
server, cc := setUp(t, 0, math.MaxUint32)
|
||||
var reply string
|
||||
cc.Close()
|
||||
req := "hello"
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel()
|
||||
if err := Invoke(ctx, "/foo/bar", &req, &reply, cc, FailFast(false)); err == nil {
|
||||
t.Fatalf("canceled invoke on closed connection should fail")
|
||||
}
|
||||
server.stop()
|
||||
}
|
1384
vendor/google.golang.org/grpc/clientconn.go
generated
vendored
Normal file
1384
vendor/google.golang.org/grpc/clientconn.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
664
vendor/google.golang.org/grpc/clientconn_test.go
generated
vendored
Normal file
664
vendor/google.golang.org/grpc/clientconn_test.go
generated
vendored
Normal file
|
@ -0,0 +1,664 @@
|
|||
/*
|
||||
*
|
||||
* 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 grpc
|
||||
|
||||
import (
|
||||
"io"
|
||||
"math"
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"golang.org/x/net/http2"
|
||||
|
||||
"google.golang.org/grpc/connectivity"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/keepalive"
|
||||
"google.golang.org/grpc/naming"
|
||||
"google.golang.org/grpc/resolver"
|
||||
"google.golang.org/grpc/resolver/manual"
|
||||
_ "google.golang.org/grpc/resolver/passthrough"
|
||||
"google.golang.org/grpc/test/leakcheck"
|
||||
"google.golang.org/grpc/testdata"
|
||||
)
|
||||
|
||||
func assertState(wantState connectivity.State, cc *ClientConn) (connectivity.State, bool) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
||||
defer cancel()
|
||||
var state connectivity.State
|
||||
for state = cc.GetState(); state != wantState && cc.WaitForStateChange(ctx, state); state = cc.GetState() {
|
||||
}
|
||||
return state, state == wantState
|
||||
}
|
||||
|
||||
func TestDialWithMultipleBackendsNotSendingServerPreface(t *testing.T) {
|
||||
defer leakcheck.Check(t)
|
||||
numServers := 2
|
||||
servers := make([]net.Listener, numServers)
|
||||
var err error
|
||||
for i := 0; i < numServers; i++ {
|
||||
servers[i], err = net.Listen("tcp", "localhost:0")
|
||||
if err != nil {
|
||||
t.Fatalf("Error while listening. Err: %v", err)
|
||||
}
|
||||
}
|
||||
dones := make([]chan struct{}, numServers)
|
||||
for i := 0; i < numServers; i++ {
|
||||
dones[i] = make(chan struct{})
|
||||
}
|
||||
for i := 0; i < numServers; i++ {
|
||||
go func(i int) {
|
||||
defer func() {
|
||||
close(dones[i])
|
||||
}()
|
||||
conn, err := servers[i].Accept()
|
||||
if err != nil {
|
||||
t.Errorf("Error while accepting. Err: %v", err)
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
switch i {
|
||||
case 0: // 1st server accepts the connection and immediately closes it.
|
||||
case 1: // 2nd server accepts the connection and sends settings frames.
|
||||
framer := http2.NewFramer(conn, conn)
|
||||
if err := framer.WriteSettings(http2.Setting{}); err != nil {
|
||||
t.Errorf("Error while writing settings frame. %v", err)
|
||||
return
|
||||
}
|
||||
conn.SetDeadline(time.Now().Add(time.Second))
|
||||
buf := make([]byte, 1024)
|
||||
for { // Make sure the connection stays healthy.
|
||||
_, err = conn.Read(buf)
|
||||
if err == nil {
|
||||
continue
|
||||
}
|
||||
if nerr, ok := err.(net.Error); !ok || !nerr.Timeout() {
|
||||
t.Errorf("Server expected the conn.Read(_) to timeout instead got error: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}(i)
|
||||
}
|
||||
r, cleanup := manual.GenerateAndRegisterManualResolver()
|
||||
defer cleanup()
|
||||
resolvedAddrs := make([]resolver.Address, numServers)
|
||||
for i := 0; i < numServers; i++ {
|
||||
resolvedAddrs[i] = resolver.Address{Addr: servers[i].Addr().String()}
|
||||
}
|
||||
r.InitialAddrs(resolvedAddrs)
|
||||
client, err := Dial(r.Scheme()+":///test.server", WithInsecure())
|
||||
if err != nil {
|
||||
t.Errorf("Dial failed. Err: %v", err)
|
||||
} else {
|
||||
defer client.Close()
|
||||
}
|
||||
time.Sleep(time.Second) // Close the servers after a second for cleanup.
|
||||
for _, s := range servers {
|
||||
s.Close()
|
||||
}
|
||||
for _, done := range dones {
|
||||
<-done
|
||||
}
|
||||
}
|
||||
|
||||
func TestDialWaitsForServerSettings(t *testing.T) {
|
||||
defer leakcheck.Check(t)
|
||||
server, err := net.Listen("tcp", "localhost:0")
|
||||
if err != nil {
|
||||
t.Fatalf("Error while listening. Err: %v", err)
|
||||
}
|
||||
defer server.Close()
|
||||
done := make(chan struct{})
|
||||
sent := make(chan struct{})
|
||||
dialDone := make(chan struct{})
|
||||
go func() { // Launch the server.
|
||||
defer func() {
|
||||
close(done)
|
||||
}()
|
||||
conn, err := server.Accept()
|
||||
if err != nil {
|
||||
t.Errorf("Error while accepting. Err: %v", err)
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
// Sleep so that if the test were to fail it
|
||||
// will fail more often than not.
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
framer := http2.NewFramer(conn, conn)
|
||||
close(sent)
|
||||
if err := framer.WriteSettings(http2.Setting{}); err != nil {
|
||||
t.Errorf("Error while writing settings. Err: %v", err)
|
||||
return
|
||||
}
|
||||
<-dialDone // Close conn only after dial returns.
|
||||
}()
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
|
||||
defer cancel()
|
||||
client, err := DialContext(ctx, server.Addr().String(), WithInsecure(), WithWaitForHandshake(), WithBlock())
|
||||
close(dialDone)
|
||||
if err != nil {
|
||||
cancel()
|
||||
t.Fatalf("Error while dialing. Err: %v", err)
|
||||
}
|
||||
defer client.Close()
|
||||
select {
|
||||
case <-sent:
|
||||
default:
|
||||
t.Fatalf("Dial returned before server settings were sent")
|
||||
}
|
||||
<-done
|
||||
|
||||
}
|
||||
|
||||
func TestCloseConnectionWhenServerPrefaceNotReceived(t *testing.T) {
|
||||
mctBkp := minConnectTimeout
|
||||
// Call this only after transportMonitor goroutine has ended.
|
||||
defer func() {
|
||||
minConnectTimeout = mctBkp
|
||||
}()
|
||||
defer leakcheck.Check(t)
|
||||
minConnectTimeout = time.Millisecond * 500
|
||||
server, err := net.Listen("tcp", "localhost:0")
|
||||
if err != nil {
|
||||
t.Fatalf("Error while listening. Err: %v", err)
|
||||
}
|
||||
defer server.Close()
|
||||
done := make(chan struct{})
|
||||
clientDone := make(chan struct{})
|
||||
go func() { // Launch the server.
|
||||
defer func() {
|
||||
if done != nil {
|
||||
close(done)
|
||||
}
|
||||
}()
|
||||
conn1, err := server.Accept()
|
||||
if err != nil {
|
||||
t.Errorf("Error while accepting. Err: %v", err)
|
||||
return
|
||||
}
|
||||
defer conn1.Close()
|
||||
// Don't send server settings and make sure the connection is closed.
|
||||
time.Sleep(time.Millisecond * 1500) // Since the first backoff is for a second.
|
||||
conn1.SetDeadline(time.Now().Add(time.Second))
|
||||
b := make([]byte, 24)
|
||||
for {
|
||||
// Make sure the connection was closed by client.
|
||||
_, err = conn1.Read(b)
|
||||
if err == nil {
|
||||
continue
|
||||
}
|
||||
if err != io.EOF {
|
||||
t.Errorf(" conn1.Read(_) = _, %v, want _, io.EOF", err)
|
||||
return
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
conn2, err := server.Accept() // Accept a reconnection request from client.
|
||||
if err != nil {
|
||||
t.Errorf("Error while accepting. Err: %v", err)
|
||||
return
|
||||
}
|
||||
defer conn2.Close()
|
||||
framer := http2.NewFramer(conn2, conn2)
|
||||
if err := framer.WriteSettings(http2.Setting{}); err != nil {
|
||||
t.Errorf("Error while writing settings. Err: %v", err)
|
||||
return
|
||||
}
|
||||
time.Sleep(time.Millisecond * 1500) // Since the first backoff is for a second.
|
||||
conn2.SetDeadline(time.Now().Add(time.Millisecond * 500))
|
||||
for {
|
||||
// Make sure the connection stays open and is closed
|
||||
// only by connection timeout.
|
||||
_, err = conn2.Read(b)
|
||||
if err == nil {
|
||||
continue
|
||||
}
|
||||
if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
|
||||
return
|
||||
}
|
||||
t.Errorf("Unexpected error while reading. Err: %v, want timeout error", err)
|
||||
break
|
||||
}
|
||||
close(done)
|
||||
done = nil
|
||||
<-clientDone
|
||||
|
||||
}()
|
||||
client, err := Dial(server.Addr().String(), WithInsecure())
|
||||
if err != nil {
|
||||
t.Fatalf("Error while dialing. Err: %v", err)
|
||||
}
|
||||
<-done
|
||||
// TODO: The code from BEGIN to END should be delete once issue
|
||||
// https://github.com/grpc/grpc-go/issues/1750 is fixed.
|
||||
// BEGIN
|
||||
// Set underlying addrConns state to Shutdown so that no reconnect
|
||||
// attempts take place and thereby resetting minConnectTimeout is
|
||||
// race free.
|
||||
client.mu.Lock()
|
||||
addrConns := client.conns
|
||||
client.mu.Unlock()
|
||||
for ac := range addrConns {
|
||||
ac.mu.Lock()
|
||||
ac.state = connectivity.Shutdown
|
||||
ac.mu.Unlock()
|
||||
}
|
||||
// END
|
||||
client.Close()
|
||||
close(clientDone)
|
||||
}
|
||||
|
||||
func TestBackoffWhenNoServerPrefaceReceived(t *testing.T) {
|
||||
defer leakcheck.Check(t)
|
||||
server, err := net.Listen("tcp", "localhost:0")
|
||||
if err != nil {
|
||||
t.Fatalf("Error while listening. Err: %v", err)
|
||||
}
|
||||
defer server.Close()
|
||||
done := make(chan struct{})
|
||||
go func() { // Launch the server.
|
||||
defer func() {
|
||||
close(done)
|
||||
}()
|
||||
conn, err := server.Accept() // Accept the connection only to close it immediately.
|
||||
if err != nil {
|
||||
t.Errorf("Error while accepting. Err: %v", err)
|
||||
return
|
||||
}
|
||||
prevAt := time.Now()
|
||||
conn.Close()
|
||||
var prevDuration time.Duration
|
||||
// Make sure the retry attempts are backed off properly.
|
||||
for i := 0; i < 3; i++ {
|
||||
conn, err := server.Accept()
|
||||
if err != nil {
|
||||
t.Errorf("Error while accepting. Err: %v", err)
|
||||
return
|
||||
}
|
||||
meow := time.Now()
|
||||
conn.Close()
|
||||
dr := meow.Sub(prevAt)
|
||||
if dr <= prevDuration {
|
||||
t.Errorf("Client backoff did not increase with retries. Previous duration: %v, current duration: %v", prevDuration, dr)
|
||||
return
|
||||
}
|
||||
prevDuration = dr
|
||||
prevAt = meow
|
||||
}
|
||||
}()
|
||||
client, err := Dial(server.Addr().String(), WithInsecure())
|
||||
if err != nil {
|
||||
t.Fatalf("Error while dialing. Err: %v", err)
|
||||
}
|
||||
defer client.Close()
|
||||
<-done
|
||||
|
||||
}
|
||||
|
||||
func TestConnectivityStates(t *testing.T) {
|
||||
defer leakcheck.Check(t)
|
||||
servers, resolver, cleanup := startServers(t, 2, math.MaxUint32)
|
||||
defer cleanup()
|
||||
cc, err := Dial("passthrough:///foo.bar.com", WithBalancer(RoundRobin(resolver)), WithInsecure())
|
||||
if err != nil {
|
||||
t.Fatalf("Dial(\"foo.bar.com\", WithBalancer(_)) = _, %v, want _ <nil>", err)
|
||||
}
|
||||
defer cc.Close()
|
||||
wantState := connectivity.Ready
|
||||
if state, ok := assertState(wantState, cc); !ok {
|
||||
t.Fatalf("asserState(%s) = %s, false, want %s, true", wantState, state, wantState)
|
||||
}
|
||||
// Send an update to delete the server connection (tearDown addrConn).
|
||||
update := []*naming.Update{
|
||||
{
|
||||
Op: naming.Delete,
|
||||
Addr: "localhost:" + servers[0].port,
|
||||
},
|
||||
}
|
||||
resolver.w.inject(update)
|
||||
wantState = connectivity.TransientFailure
|
||||
if state, ok := assertState(wantState, cc); !ok {
|
||||
t.Fatalf("asserState(%s) = %s, false, want %s, true", wantState, state, wantState)
|
||||
}
|
||||
update[0] = &naming.Update{
|
||||
Op: naming.Add,
|
||||
Addr: "localhost:" + servers[1].port,
|
||||
}
|
||||
resolver.w.inject(update)
|
||||
wantState = connectivity.Ready
|
||||
if state, ok := assertState(wantState, cc); !ok {
|
||||
t.Fatalf("asserState(%s) = %s, false, want %s, true", wantState, state, wantState)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestDialTimeout(t *testing.T) {
|
||||
defer leakcheck.Check(t)
|
||||
conn, err := Dial("passthrough:///Non-Existent.Server:80", WithTimeout(time.Millisecond), WithBlock(), WithInsecure())
|
||||
if err == nil {
|
||||
conn.Close()
|
||||
}
|
||||
if err != context.DeadlineExceeded {
|
||||
t.Fatalf("Dial(_, _) = %v, %v, want %v", conn, err, context.DeadlineExceeded)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTLSDialTimeout(t *testing.T) {
|
||||
defer leakcheck.Check(t)
|
||||
creds, err := credentials.NewClientTLSFromFile(testdata.Path("ca.pem"), "x.test.youtube.com")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create credentials %v", err)
|
||||
}
|
||||
conn, err := Dial("passthrough:///Non-Existent.Server:80", WithTransportCredentials(creds), WithTimeout(time.Millisecond), WithBlock())
|
||||
if err == nil {
|
||||
conn.Close()
|
||||
}
|
||||
if err != context.DeadlineExceeded {
|
||||
t.Fatalf("Dial(_, _) = %v, %v, want %v", conn, err, context.DeadlineExceeded)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefaultAuthority(t *testing.T) {
|
||||
defer leakcheck.Check(t)
|
||||
target := "Non-Existent.Server:8080"
|
||||
conn, err := Dial(target, WithInsecure())
|
||||
if err != nil {
|
||||
t.Fatalf("Dial(_, _) = _, %v, want _, <nil>", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
if conn.authority != target {
|
||||
t.Fatalf("%v.authority = %v, want %v", conn, conn.authority, target)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTLSServerNameOverwrite(t *testing.T) {
|
||||
defer leakcheck.Check(t)
|
||||
overwriteServerName := "over.write.server.name"
|
||||
creds, err := credentials.NewClientTLSFromFile(testdata.Path("ca.pem"), overwriteServerName)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create credentials %v", err)
|
||||
}
|
||||
conn, err := Dial("passthrough:///Non-Existent.Server:80", WithTransportCredentials(creds))
|
||||
if err != nil {
|
||||
t.Fatalf("Dial(_, _) = _, %v, want _, <nil>", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
if conn.authority != overwriteServerName {
|
||||
t.Fatalf("%v.authority = %v, want %v", conn, conn.authority, overwriteServerName)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithAuthority(t *testing.T) {
|
||||
defer leakcheck.Check(t)
|
||||
overwriteServerName := "over.write.server.name"
|
||||
conn, err := Dial("passthrough:///Non-Existent.Server:80", WithInsecure(), WithAuthority(overwriteServerName))
|
||||
if err != nil {
|
||||
t.Fatalf("Dial(_, _) = _, %v, want _, <nil>", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
if conn.authority != overwriteServerName {
|
||||
t.Fatalf("%v.authority = %v, want %v", conn, conn.authority, overwriteServerName)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithAuthorityAndTLS(t *testing.T) {
|
||||
defer leakcheck.Check(t)
|
||||
overwriteServerName := "over.write.server.name"
|
||||
creds, err := credentials.NewClientTLSFromFile(testdata.Path("ca.pem"), overwriteServerName)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create credentials %v", err)
|
||||
}
|
||||
conn, err := Dial("passthrough:///Non-Existent.Server:80", WithTransportCredentials(creds), WithAuthority("no.effect.authority"))
|
||||
if err != nil {
|
||||
t.Fatalf("Dial(_, _) = _, %v, want _, <nil>", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
if conn.authority != overwriteServerName {
|
||||
t.Fatalf("%v.authority = %v, want %v", conn, conn.authority, overwriteServerName)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDialContextCancel(t *testing.T) {
|
||||
defer leakcheck.Check(t)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel()
|
||||
if _, err := DialContext(ctx, "Non-Existent.Server:80", WithBlock(), WithInsecure()); err != context.Canceled {
|
||||
t.Fatalf("DialContext(%v, _) = _, %v, want _, %v", ctx, err, context.Canceled)
|
||||
}
|
||||
}
|
||||
|
||||
// blockingBalancer mimics the behavior of balancers whose initialization takes a long time.
|
||||
// In this test, reading from blockingBalancer.Notify() blocks forever.
|
||||
type blockingBalancer struct {
|
||||
ch chan []Address
|
||||
}
|
||||
|
||||
func newBlockingBalancer() Balancer {
|
||||
return &blockingBalancer{ch: make(chan []Address)}
|
||||
}
|
||||
func (b *blockingBalancer) Start(target string, config BalancerConfig) error {
|
||||
return nil
|
||||
}
|
||||
func (b *blockingBalancer) Up(addr Address) func(error) {
|
||||
return nil
|
||||
}
|
||||
func (b *blockingBalancer) Get(ctx context.Context, opts BalancerGetOptions) (addr Address, put func(), err error) {
|
||||
return Address{}, nil, nil
|
||||
}
|
||||
func (b *blockingBalancer) Notify() <-chan []Address {
|
||||
return b.ch
|
||||
}
|
||||
func (b *blockingBalancer) Close() error {
|
||||
close(b.ch)
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestDialWithBlockingBalancer(t *testing.T) {
|
||||
defer leakcheck.Check(t)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
dialDone := make(chan struct{})
|
||||
go func() {
|
||||
DialContext(ctx, "Non-Existent.Server:80", WithBlock(), WithInsecure(), WithBalancer(newBlockingBalancer()))
|
||||
close(dialDone)
|
||||
}()
|
||||
cancel()
|
||||
<-dialDone
|
||||
}
|
||||
|
||||
// securePerRPCCredentials always requires transport security.
|
||||
type securePerRPCCredentials struct{}
|
||||
|
||||
func (c securePerRPCCredentials) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (c securePerRPCCredentials) RequireTransportSecurity() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func TestCredentialsMisuse(t *testing.T) {
|
||||
defer leakcheck.Check(t)
|
||||
tlsCreds, err := credentials.NewClientTLSFromFile(testdata.Path("ca.pem"), "x.test.youtube.com")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create authenticator %v", err)
|
||||
}
|
||||
// Two conflicting credential configurations
|
||||
if _, err := Dial("passthrough:///Non-Existent.Server:80", WithTransportCredentials(tlsCreds), WithBlock(), WithInsecure()); err != errCredentialsConflict {
|
||||
t.Fatalf("Dial(_, _) = _, %v, want _, %v", err, errCredentialsConflict)
|
||||
}
|
||||
// security info on insecure connection
|
||||
if _, err := Dial("passthrough:///Non-Existent.Server:80", WithPerRPCCredentials(securePerRPCCredentials{}), WithBlock(), WithInsecure()); err != errTransportCredentialsMissing {
|
||||
t.Fatalf("Dial(_, _) = _, %v, want _, %v", err, errTransportCredentialsMissing)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithBackoffConfigDefault(t *testing.T) {
|
||||
defer leakcheck.Check(t)
|
||||
testBackoffConfigSet(t, &DefaultBackoffConfig)
|
||||
}
|
||||
|
||||
func TestWithBackoffConfig(t *testing.T) {
|
||||
defer leakcheck.Check(t)
|
||||
b := BackoffConfig{MaxDelay: DefaultBackoffConfig.MaxDelay / 2}
|
||||
expected := b
|
||||
setDefaults(&expected) // defaults should be set
|
||||
testBackoffConfigSet(t, &expected, WithBackoffConfig(b))
|
||||
}
|
||||
|
||||
func TestWithBackoffMaxDelay(t *testing.T) {
|
||||
defer leakcheck.Check(t)
|
||||
md := DefaultBackoffConfig.MaxDelay / 2
|
||||
expected := BackoffConfig{MaxDelay: md}
|
||||
setDefaults(&expected)
|
||||
testBackoffConfigSet(t, &expected, WithBackoffMaxDelay(md))
|
||||
}
|
||||
|
||||
func testBackoffConfigSet(t *testing.T, expected *BackoffConfig, opts ...DialOption) {
|
||||
opts = append(opts, WithInsecure())
|
||||
conn, err := Dial("passthrough:///foo:80", opts...)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error dialing connection: %v", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
if conn.dopts.bs == nil {
|
||||
t.Fatalf("backoff config not set")
|
||||
}
|
||||
|
||||
actual, ok := conn.dopts.bs.(BackoffConfig)
|
||||
if !ok {
|
||||
t.Fatalf("unexpected type of backoff config: %#v", conn.dopts.bs)
|
||||
}
|
||||
|
||||
if actual != *expected {
|
||||
t.Fatalf("unexpected backoff config on connection: %v, want %v", actual, expected)
|
||||
}
|
||||
}
|
||||
|
||||
// emptyBalancer returns an empty set of servers.
|
||||
type emptyBalancer struct {
|
||||
ch chan []Address
|
||||
}
|
||||
|
||||
func newEmptyBalancer() Balancer {
|
||||
return &emptyBalancer{ch: make(chan []Address, 1)}
|
||||
}
|
||||
func (b *emptyBalancer) Start(_ string, _ BalancerConfig) error {
|
||||
b.ch <- nil
|
||||
return nil
|
||||
}
|
||||
func (b *emptyBalancer) Up(_ Address) func(error) {
|
||||
return nil
|
||||
}
|
||||
func (b *emptyBalancer) Get(_ context.Context, _ BalancerGetOptions) (Address, func(), error) {
|
||||
return Address{}, nil, nil
|
||||
}
|
||||
func (b *emptyBalancer) Notify() <-chan []Address {
|
||||
return b.ch
|
||||
}
|
||||
func (b *emptyBalancer) Close() error {
|
||||
close(b.ch)
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestNonblockingDialWithEmptyBalancer(t *testing.T) {
|
||||
defer leakcheck.Check(t)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
dialDone := make(chan error)
|
||||
go func() {
|
||||
dialDone <- func() error {
|
||||
conn, err := DialContext(ctx, "Non-Existent.Server:80", WithInsecure(), WithBalancer(newEmptyBalancer()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return conn.Close()
|
||||
}()
|
||||
}()
|
||||
if err := <-dialDone; err != nil {
|
||||
t.Fatalf("unexpected error dialing connection: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolverServiceConfigBeforeAddressNotPanic(t *testing.T) {
|
||||
defer leakcheck.Check(t)
|
||||
r, rcleanup := manual.GenerateAndRegisterManualResolver()
|
||||
defer rcleanup()
|
||||
|
||||
cc, err := Dial(r.Scheme()+":///test.server", WithInsecure())
|
||||
if err != nil {
|
||||
t.Fatalf("failed to dial: %v", err)
|
||||
}
|
||||
defer cc.Close()
|
||||
|
||||
// SwitchBalancer before NewAddress. There was no balancer created, this
|
||||
// makes sure we don't call close on nil balancerWrapper.
|
||||
r.NewServiceConfig(`{"loadBalancingPolicy": "round_robin"}`) // This should not panic.
|
||||
|
||||
time.Sleep(time.Second) // Sleep to make sure the service config is handled by ClientConn.
|
||||
}
|
||||
|
||||
func TestResolverEmptyUpdateNotPanic(t *testing.T) {
|
||||
defer leakcheck.Check(t)
|
||||
r, rcleanup := manual.GenerateAndRegisterManualResolver()
|
||||
defer rcleanup()
|
||||
|
||||
cc, err := Dial(r.Scheme()+":///test.server", WithInsecure())
|
||||
if err != nil {
|
||||
t.Fatalf("failed to dial: %v", err)
|
||||
}
|
||||
defer cc.Close()
|
||||
|
||||
// This make sure we don't create addrConn with empty address list.
|
||||
r.NewAddress([]resolver.Address{}) // This should not panic.
|
||||
|
||||
time.Sleep(time.Second) // Sleep to make sure the service config is handled by ClientConn.
|
||||
}
|
||||
|
||||
func TestClientUpdatesParamsAfterGoAway(t *testing.T) {
|
||||
defer leakcheck.Check(t)
|
||||
lis, err := net.Listen("tcp", "localhost:0")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to listen. Err: %v", err)
|
||||
}
|
||||
defer lis.Close()
|
||||
addr := lis.Addr().String()
|
||||
s := NewServer()
|
||||
go s.Serve(lis)
|
||||
defer s.Stop()
|
||||
cc, err := Dial(addr, WithBlock(), WithInsecure(), WithKeepaliveParams(keepalive.ClientParameters{
|
||||
Time: 50 * time.Millisecond,
|
||||
Timeout: 100 * time.Millisecond,
|
||||
PermitWithoutStream: true,
|
||||
}))
|
||||
if err != nil {
|
||||
t.Fatalf("Dial(%s, _) = _, %v, want _, <nil>", addr, err)
|
||||
}
|
||||
defer cc.Close()
|
||||
time.Sleep(1 * time.Second)
|
||||
cc.mu.RLock()
|
||||
defer cc.mu.RUnlock()
|
||||
v := cc.mkp.Time
|
||||
if v < 100*time.Millisecond {
|
||||
t.Fatalf("cc.dopts.copts.Keepalive.Time = %v , want 100ms", v)
|
||||
}
|
||||
}
|
50
vendor/google.golang.org/grpc/codec.go
generated
vendored
Normal file
50
vendor/google.golang.org/grpc/codec.go
generated
vendored
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
*
|
||||
* 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 grpc
|
||||
|
||||
import (
|
||||
"google.golang.org/grpc/encoding"
|
||||
_ "google.golang.org/grpc/encoding/proto" // to register the Codec for "proto"
|
||||
)
|
||||
|
||||
// baseCodec contains the functionality of both Codec and encoding.Codec, but
|
||||
// omits the name/string, which vary between the two and are not needed for
|
||||
// anything besides the registry in the encoding package.
|
||||
type baseCodec interface {
|
||||
Marshal(v interface{}) ([]byte, error)
|
||||
Unmarshal(data []byte, v interface{}) error
|
||||
}
|
||||
|
||||
var _ baseCodec = Codec(nil)
|
||||
var _ baseCodec = encoding.Codec(nil)
|
||||
|
||||
// Codec defines the interface gRPC uses to encode and decode messages.
|
||||
// Note that implementations of this interface must be thread safe;
|
||||
// a Codec's methods can be called from concurrent goroutines.
|
||||
//
|
||||
// Deprecated: use encoding.Codec instead.
|
||||
type Codec interface {
|
||||
// Marshal returns the wire format of v.
|
||||
Marshal(v interface{}) ([]byte, error)
|
||||
// Unmarshal parses the wire format into v.
|
||||
Unmarshal(data []byte, v interface{}) error
|
||||
// String returns the name of the Codec implementation. This is unused by
|
||||
// gRPC.
|
||||
String() string
|
||||
}
|
32
vendor/google.golang.org/grpc/codec_test.go
generated
vendored
Normal file
32
vendor/google.golang.org/grpc/codec_test.go
generated
vendored
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
*
|
||||
* 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 grpc
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"google.golang.org/grpc/encoding"
|
||||
"google.golang.org/grpc/encoding/proto"
|
||||
)
|
||||
|
||||
func TestGetCodecForProtoIsNotNil(t *testing.T) {
|
||||
if encoding.GetCodec(proto.Name) == nil {
|
||||
t.Fatalf("encoding.GetCodec(%q) must not be nil by default", proto.Name)
|
||||
}
|
||||
}
|
17
vendor/google.golang.org/grpc/codegen.sh
generated
vendored
Executable file
17
vendor/google.golang.org/grpc/codegen.sh
generated
vendored
Executable file
|
@ -0,0 +1,17 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# This script serves as an example to demonstrate how to generate the gRPC-Go
|
||||
# interface and the related messages from .proto file.
|
||||
#
|
||||
# It assumes the installation of i) Google proto buffer compiler at
|
||||
# https://github.com/google/protobuf (after v2.6.1) and ii) the Go codegen
|
||||
# plugin at https://github.com/golang/protobuf (after 2015-02-20). If you have
|
||||
# not, please install them first.
|
||||
#
|
||||
# We recommend running this script at $GOPATH/src.
|
||||
#
|
||||
# If this is not what you need, feel free to make your own scripts. Again, this
|
||||
# script is for demonstration purpose.
|
||||
#
|
||||
proto=$1
|
||||
protoc --go_out=plugins=grpc:. $proto
|
62
vendor/google.golang.org/grpc/codes/code_string.go
generated
vendored
Normal file
62
vendor/google.golang.org/grpc/codes/code_string.go
generated
vendored
Normal file
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
*
|
||||
* 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 codes
|
||||
|
||||
import "strconv"
|
||||
|
||||
func (c Code) String() string {
|
||||
switch c {
|
||||
case OK:
|
||||
return "OK"
|
||||
case Canceled:
|
||||
return "Canceled"
|
||||
case Unknown:
|
||||
return "Unknown"
|
||||
case InvalidArgument:
|
||||
return "InvalidArgument"
|
||||
case DeadlineExceeded:
|
||||
return "DeadlineExceeded"
|
||||
case NotFound:
|
||||
return "NotFound"
|
||||
case AlreadyExists:
|
||||
return "AlreadyExists"
|
||||
case PermissionDenied:
|
||||
return "PermissionDenied"
|
||||
case ResourceExhausted:
|
||||
return "ResourceExhausted"
|
||||
case FailedPrecondition:
|
||||
return "FailedPrecondition"
|
||||
case Aborted:
|
||||
return "Aborted"
|
||||
case OutOfRange:
|
||||
return "OutOfRange"
|
||||
case Unimplemented:
|
||||
return "Unimplemented"
|
||||
case Internal:
|
||||
return "Internal"
|
||||
case Unavailable:
|
||||
return "Unavailable"
|
||||
case DataLoss:
|
||||
return "DataLoss"
|
||||
case Unauthenticated:
|
||||
return "Unauthenticated"
|
||||
default:
|
||||
return "Code(" + strconv.FormatInt(int64(c), 10) + ")"
|
||||
}
|
||||
}
|
184
vendor/google.golang.org/grpc/codes/codes.go
generated
vendored
Normal file
184
vendor/google.golang.org/grpc/codes/codes.go
generated
vendored
Normal file
|
@ -0,0 +1,184 @@
|
|||
/*
|
||||
*
|
||||
* 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 codes defines the canonical error codes used by gRPC. It is
|
||||
// consistent across various languages.
|
||||
package codes // import "google.golang.org/grpc/codes"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// A Code is an unsigned 32-bit error code as defined in the gRPC spec.
|
||||
type Code uint32
|
||||
|
||||
const (
|
||||
// OK is returned on success.
|
||||
OK Code = 0
|
||||
|
||||
// Canceled indicates the operation was canceled (typically by the caller).
|
||||
Canceled Code = 1
|
||||
|
||||
// Unknown error. An example of where this error may be returned is
|
||||
// if a Status value received from another address space belongs to
|
||||
// an error-space that is not known in this address space. Also
|
||||
// errors raised by APIs that do not return enough error information
|
||||
// may be converted to this error.
|
||||
Unknown Code = 2
|
||||
|
||||
// InvalidArgument indicates client specified an invalid argument.
|
||||
// Note that this differs from FailedPrecondition. It indicates arguments
|
||||
// that are problematic regardless of the state of the system
|
||||
// (e.g., a malformed file name).
|
||||
InvalidArgument Code = 3
|
||||
|
||||
// DeadlineExceeded means operation expired before completion.
|
||||
// For operations that change the state of the system, this error may be
|
||||
// returned even if the operation has completed successfully. For
|
||||
// example, a successful response from a server could have been delayed
|
||||
// long enough for the deadline to expire.
|
||||
DeadlineExceeded Code = 4
|
||||
|
||||
// NotFound means some requested entity (e.g., file or directory) was
|
||||
// not found.
|
||||
NotFound Code = 5
|
||||
|
||||
// AlreadyExists means an attempt to create an entity failed because one
|
||||
// already exists.
|
||||
AlreadyExists Code = 6
|
||||
|
||||
// PermissionDenied indicates the caller does not have permission to
|
||||
// execute the specified operation. It must not be used for rejections
|
||||
// caused by exhausting some resource (use ResourceExhausted
|
||||
// instead for those errors). It must not be
|
||||
// used if the caller cannot be identified (use Unauthenticated
|
||||
// instead for those errors).
|
||||
PermissionDenied Code = 7
|
||||
|
||||
// ResourceExhausted indicates some resource has been exhausted, perhaps
|
||||
// a per-user quota, or perhaps the entire file system is out of space.
|
||||
ResourceExhausted Code = 8
|
||||
|
||||
// FailedPrecondition indicates operation was rejected because the
|
||||
// system is not in a state required for the operation's execution.
|
||||
// For example, directory to be deleted may be non-empty, an rmdir
|
||||
// operation is applied to a non-directory, etc.
|
||||
//
|
||||
// A litmus test that may help a service implementor in deciding
|
||||
// between FailedPrecondition, Aborted, and Unavailable:
|
||||
// (a) Use Unavailable if the client can retry just the failing call.
|
||||
// (b) Use Aborted if the client should retry at a higher-level
|
||||
// (e.g., restarting a read-modify-write sequence).
|
||||
// (c) Use FailedPrecondition if the client should not retry until
|
||||
// the system state has been explicitly fixed. E.g., if an "rmdir"
|
||||
// fails because the directory is non-empty, FailedPrecondition
|
||||
// should be returned since the client should not retry unless
|
||||
// they have first fixed up the directory by deleting files from it.
|
||||
// (d) Use FailedPrecondition if the client performs conditional
|
||||
// REST Get/Update/Delete on a resource and the resource on the
|
||||
// server does not match the condition. E.g., conflicting
|
||||
// read-modify-write on the same resource.
|
||||
FailedPrecondition Code = 9
|
||||
|
||||
// Aborted indicates the operation was aborted, typically due to a
|
||||
// concurrency issue like sequencer check failures, transaction aborts,
|
||||
// etc.
|
||||
//
|
||||
// See litmus test above for deciding between FailedPrecondition,
|
||||
// Aborted, and Unavailable.
|
||||
Aborted Code = 10
|
||||
|
||||
// OutOfRange means operation was attempted past the valid range.
|
||||
// E.g., seeking or reading past end of file.
|
||||
//
|
||||
// Unlike InvalidArgument, this error indicates a problem that may
|
||||
// be fixed if the system state changes. For example, a 32-bit file
|
||||
// system will generate InvalidArgument if asked to read at an
|
||||
// offset that is not in the range [0,2^32-1], but it will generate
|
||||
// OutOfRange if asked to read from an offset past the current
|
||||
// file size.
|
||||
//
|
||||
// There is a fair bit of overlap between FailedPrecondition and
|
||||
// OutOfRange. We recommend using OutOfRange (the more specific
|
||||
// error) when it applies so that callers who are iterating through
|
||||
// a space can easily look for an OutOfRange error to detect when
|
||||
// they are done.
|
||||
OutOfRange Code = 11
|
||||
|
||||
// Unimplemented indicates operation is not implemented or not
|
||||
// supported/enabled in this service.
|
||||
Unimplemented Code = 12
|
||||
|
||||
// Internal errors. Means some invariants expected by underlying
|
||||
// system has been broken. If you see one of these errors,
|
||||
// something is very broken.
|
||||
Internal Code = 13
|
||||
|
||||
// Unavailable indicates the service is currently unavailable.
|
||||
// This is a most likely a transient condition and may be corrected
|
||||
// by retrying with a backoff.
|
||||
//
|
||||
// See litmus test above for deciding between FailedPrecondition,
|
||||
// Aborted, and Unavailable.
|
||||
Unavailable Code = 14
|
||||
|
||||
// DataLoss indicates unrecoverable data loss or corruption.
|
||||
DataLoss Code = 15
|
||||
|
||||
// Unauthenticated indicates the request does not have valid
|
||||
// authentication credentials for the operation.
|
||||
Unauthenticated Code = 16
|
||||
)
|
||||
|
||||
var strToCode = map[string]Code{
|
||||
`"OK"`: OK,
|
||||
`"CANCELLED"`:/* [sic] */ Canceled,
|
||||
`"UNKNOWN"`: Unknown,
|
||||
`"INVALID_ARGUMENT"`: InvalidArgument,
|
||||
`"DEADLINE_EXCEEDED"`: DeadlineExceeded,
|
||||
`"NOT_FOUND"`: NotFound,
|
||||
`"ALREADY_EXISTS"`: AlreadyExists,
|
||||
`"PERMISSION_DENIED"`: PermissionDenied,
|
||||
`"RESOURCE_EXHAUSTED"`: ResourceExhausted,
|
||||
`"FAILED_PRECONDITION"`: FailedPrecondition,
|
||||
`"ABORTED"`: Aborted,
|
||||
`"OUT_OF_RANGE"`: OutOfRange,
|
||||
`"UNIMPLEMENTED"`: Unimplemented,
|
||||
`"INTERNAL"`: Internal,
|
||||
`"UNAVAILABLE"`: Unavailable,
|
||||
`"DATA_LOSS"`: DataLoss,
|
||||
`"UNAUTHENTICATED"`: Unauthenticated,
|
||||
}
|
||||
|
||||
// UnmarshalJSON unmarshals b into the Code.
|
||||
func (c *Code) UnmarshalJSON(b []byte) error {
|
||||
// From json.Unmarshaler: By convention, to approximate the behavior of
|
||||
// Unmarshal itself, Unmarshalers implement UnmarshalJSON([]byte("null")) as
|
||||
// a no-op.
|
||||
if string(b) == "null" {
|
||||
return nil
|
||||
}
|
||||
if c == nil {
|
||||
return fmt.Errorf("nil receiver passed to UnmarshalJSON")
|
||||
}
|
||||
if jc, ok := strToCode[string(b)]; ok {
|
||||
*c = jc
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("invalid code: %q", string(b))
|
||||
}
|
64
vendor/google.golang.org/grpc/codes/codes_test.go
generated
vendored
Normal file
64
vendor/google.golang.org/grpc/codes/codes_test.go
generated
vendored
Normal file
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
*
|
||||
* 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 codes
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
cpb "google.golang.org/genproto/googleapis/rpc/code"
|
||||
)
|
||||
|
||||
func TestUnmarshalJSON(t *testing.T) {
|
||||
for s, v := range cpb.Code_value {
|
||||
want := Code(v)
|
||||
var got Code
|
||||
if err := got.UnmarshalJSON([]byte(`"` + s + `"`)); err != nil || got != want {
|
||||
t.Errorf("got.UnmarshalJSON(%q) = %v; want <nil>. got=%v; want %v", s, err, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestJSONUnmarshal(t *testing.T) {
|
||||
var got []Code
|
||||
want := []Code{OK, NotFound, Internal, Canceled}
|
||||
in := `["OK", "NOT_FOUND", "INTERNAL", "CANCELLED"]`
|
||||
err := json.Unmarshal([]byte(in), &got)
|
||||
if err != nil || !reflect.DeepEqual(got, want) {
|
||||
t.Fatalf("json.Unmarshal(%q, &got) = %v; want <nil>. got=%v; want %v", in, err, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnmarshalJSON_NilReceiver(t *testing.T) {
|
||||
var got *Code
|
||||
in := OK.String()
|
||||
if err := got.UnmarshalJSON([]byte(in)); err == nil {
|
||||
t.Errorf("got.UnmarshalJSON(%q) = nil; want <non-nil>. got=%v", in, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnmarshalJSON_UnknownInput(t *testing.T) {
|
||||
var got Code
|
||||
for _, in := range [][]byte{[]byte(""), []byte("xxx"), []byte("Code(17)"), nil} {
|
||||
if err := got.UnmarshalJSON([]byte(in)); err == nil {
|
||||
t.Errorf("got.UnmarshalJSON(%q) = nil; want <non-nil>. got=%v", in, got)
|
||||
}
|
||||
}
|
||||
}
|
24
vendor/google.golang.org/grpc/doc.go
generated
vendored
Normal file
24
vendor/google.golang.org/grpc/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
*
|
||||
* Copyright 2015 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 implements an RPC system called gRPC.
|
||||
|
||||
See grpc.io for more information about gRPC.
|
||||
*/
|
||||
package grpc // import "google.golang.org/grpc"
|
99
vendor/google.golang.org/grpc/go16.go
generated
vendored
Normal file
99
vendor/google.golang.org/grpc/go16.go
generated
vendored
Normal file
|
@ -0,0 +1,99 @@
|
|||
// +build go1.6,!go1.7
|
||||
|
||||
/*
|
||||
*
|
||||
* 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 (
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/grpc/transport"
|
||||
)
|
||||
|
||||
// dialContext connects to the address on the named network.
|
||||
func dialContext(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
return (&net.Dialer{Cancel: ctx.Done()}).Dial(network, address)
|
||||
}
|
||||
|
||||
func sendHTTPRequest(ctx context.Context, req *http.Request, conn net.Conn) error {
|
||||
req.Cancel = ctx.Done()
|
||||
if err := req.Write(conn); err != nil {
|
||||
return fmt.Errorf("failed to write the HTTP request: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// toRPCErr converts an error into an error from the status package.
|
||||
func toRPCErr(err error) error {
|
||||
if err == nil || err == io.EOF {
|
||||
return err
|
||||
}
|
||||
if _, ok := status.FromError(err); ok {
|
||||
return err
|
||||
}
|
||||
switch e := err.(type) {
|
||||
case transport.StreamError:
|
||||
return status.Error(e.Code, e.Desc)
|
||||
case transport.ConnectionError:
|
||||
return status.Error(codes.Unavailable, e.Desc)
|
||||
default:
|
||||
switch err {
|
||||
case context.DeadlineExceeded:
|
||||
return status.Error(codes.DeadlineExceeded, err.Error())
|
||||
case context.Canceled:
|
||||
return status.Error(codes.Canceled, err.Error())
|
||||
}
|
||||
}
|
||||
return status.Error(codes.Unknown, err.Error())
|
||||
}
|
||||
|
||||
// convertCode converts a standard Go error into its canonical code. Note that
|
||||
// this is only used to translate the error returned by the server applications.
|
||||
func convertCode(err error) codes.Code {
|
||||
switch err {
|
||||
case nil:
|
||||
return codes.OK
|
||||
case io.EOF:
|
||||
return codes.OutOfRange
|
||||
case io.ErrClosedPipe, io.ErrNoProgress, io.ErrShortBuffer, io.ErrShortWrite, io.ErrUnexpectedEOF:
|
||||
return codes.FailedPrecondition
|
||||
case os.ErrInvalid:
|
||||
return codes.InvalidArgument
|
||||
case context.Canceled:
|
||||
return codes.Canceled
|
||||
case context.DeadlineExceeded:
|
||||
return codes.DeadlineExceeded
|
||||
}
|
||||
switch {
|
||||
case os.IsExist(err):
|
||||
return codes.AlreadyExists
|
||||
case os.IsNotExist(err):
|
||||
return codes.NotFound
|
||||
case os.IsPermission(err):
|
||||
return codes.PermissionDenied
|
||||
}
|
||||
return codes.Unknown
|
||||
}
|
100
vendor/google.golang.org/grpc/go17.go
generated
vendored
Normal file
100
vendor/google.golang.org/grpc/go17.go
generated
vendored
Normal file
|
@ -0,0 +1,100 @@
|
|||
// +build go1.7
|
||||
|
||||
/*
|
||||
*
|
||||
* 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"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
netctx "golang.org/x/net/context"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/grpc/transport"
|
||||
)
|
||||
|
||||
// dialContext connects to the address on the named network.
|
||||
func dialContext(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
return (&net.Dialer{}).DialContext(ctx, network, address)
|
||||
}
|
||||
|
||||
func sendHTTPRequest(ctx context.Context, req *http.Request, conn net.Conn) error {
|
||||
req = req.WithContext(ctx)
|
||||
if err := req.Write(conn); err != nil {
|
||||
return fmt.Errorf("failed to write the HTTP request: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// toRPCErr converts an error into an error from the status package.
|
||||
func toRPCErr(err error) error {
|
||||
if err == nil || err == io.EOF {
|
||||
return err
|
||||
}
|
||||
if _, ok := status.FromError(err); ok {
|
||||
return err
|
||||
}
|
||||
switch e := err.(type) {
|
||||
case transport.StreamError:
|
||||
return status.Error(e.Code, e.Desc)
|
||||
case transport.ConnectionError:
|
||||
return status.Error(codes.Unavailable, e.Desc)
|
||||
default:
|
||||
switch err {
|
||||
case context.DeadlineExceeded, netctx.DeadlineExceeded:
|
||||
return status.Error(codes.DeadlineExceeded, err.Error())
|
||||
case context.Canceled, netctx.Canceled:
|
||||
return status.Error(codes.Canceled, err.Error())
|
||||
}
|
||||
}
|
||||
return status.Error(codes.Unknown, err.Error())
|
||||
}
|
||||
|
||||
// convertCode converts a standard Go error into its canonical code. Note that
|
||||
// this is only used to translate the error returned by the server applications.
|
||||
func convertCode(err error) codes.Code {
|
||||
switch err {
|
||||
case nil:
|
||||
return codes.OK
|
||||
case io.EOF:
|
||||
return codes.OutOfRange
|
||||
case io.ErrClosedPipe, io.ErrNoProgress, io.ErrShortBuffer, io.ErrShortWrite, io.ErrUnexpectedEOF:
|
||||
return codes.FailedPrecondition
|
||||
case os.ErrInvalid:
|
||||
return codes.InvalidArgument
|
||||
case context.Canceled, netctx.Canceled:
|
||||
return codes.Canceled
|
||||
case context.DeadlineExceeded, netctx.DeadlineExceeded:
|
||||
return codes.DeadlineExceeded
|
||||
}
|
||||
switch {
|
||||
case os.IsExist(err):
|
||||
return codes.AlreadyExists
|
||||
case os.IsNotExist(err):
|
||||
return codes.NotFound
|
||||
case os.IsPermission(err):
|
||||
return codes.PermissionDenied
|
||||
}
|
||||
return codes.Unknown
|
||||
}
|
342
vendor/google.golang.org/grpc/grpclb.go
generated
vendored
Normal file
342
vendor/google.golang.org/grpc/grpclb.go
generated
vendored
Normal file
|
@ -0,0 +1,342 @@
|
|||
/*
|
||||
*
|
||||
* 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 (
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc/balancer"
|
||||
"google.golang.org/grpc/connectivity"
|
||||
lbpb "google.golang.org/grpc/grpclb/grpc_lb_v1/messages"
|
||||
"google.golang.org/grpc/grpclog"
|
||||
"google.golang.org/grpc/resolver"
|
||||
)
|
||||
|
||||
const (
|
||||
lbTokeyKey = "lb-token"
|
||||
defaultFallbackTimeout = 10 * time.Second
|
||||
grpclbName = "grpclb"
|
||||
)
|
||||
|
||||
func convertDuration(d *lbpb.Duration) time.Duration {
|
||||
if d == nil {
|
||||
return 0
|
||||
}
|
||||
return time.Duration(d.Seconds)*time.Second + time.Duration(d.Nanos)*time.Nanosecond
|
||||
}
|
||||
|
||||
// Client API for LoadBalancer service.
|
||||
// Mostly copied from generated pb.go file.
|
||||
// To avoid circular dependency.
|
||||
type loadBalancerClient struct {
|
||||
cc *ClientConn
|
||||
}
|
||||
|
||||
func (c *loadBalancerClient) BalanceLoad(ctx context.Context, opts ...CallOption) (*balanceLoadClientStream, error) {
|
||||
desc := &StreamDesc{
|
||||
StreamName: "BalanceLoad",
|
||||
ServerStreams: true,
|
||||
ClientStreams: true,
|
||||
}
|
||||
stream, err := NewClientStream(ctx, desc, c.cc, "/grpc.lb.v1.LoadBalancer/BalanceLoad", opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &balanceLoadClientStream{stream}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
type balanceLoadClientStream struct {
|
||||
ClientStream
|
||||
}
|
||||
|
||||
func (x *balanceLoadClientStream) Send(m *lbpb.LoadBalanceRequest) error {
|
||||
return x.ClientStream.SendMsg(m)
|
||||
}
|
||||
|
||||
func (x *balanceLoadClientStream) Recv() (*lbpb.LoadBalanceResponse, error) {
|
||||
m := new(lbpb.LoadBalanceResponse)
|
||||
if err := x.ClientStream.RecvMsg(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
balancer.Register(newLBBuilder())
|
||||
}
|
||||
|
||||
// newLBBuilder creates a builder for grpclb.
|
||||
func newLBBuilder() balancer.Builder {
|
||||
return NewLBBuilderWithFallbackTimeout(defaultFallbackTimeout)
|
||||
}
|
||||
|
||||
// NewLBBuilderWithFallbackTimeout creates a grpclb builder with the given
|
||||
// fallbackTimeout. If no response is received from the remote balancer within
|
||||
// fallbackTimeout, the backend addresses from the resolved address list will be
|
||||
// used.
|
||||
//
|
||||
// Only call this function when a non-default fallback timeout is needed.
|
||||
func NewLBBuilderWithFallbackTimeout(fallbackTimeout time.Duration) balancer.Builder {
|
||||
return &lbBuilder{
|
||||
fallbackTimeout: fallbackTimeout,
|
||||
}
|
||||
}
|
||||
|
||||
type lbBuilder struct {
|
||||
fallbackTimeout time.Duration
|
||||
}
|
||||
|
||||
func (b *lbBuilder) Name() string {
|
||||
return grpclbName
|
||||
}
|
||||
|
||||
func (b *lbBuilder) Build(cc balancer.ClientConn, opt balancer.BuildOptions) balancer.Balancer {
|
||||
// This generates a manual resolver builder with a random scheme. This
|
||||
// scheme will be used to dial to remote LB, so we can send filtered address
|
||||
// updates to remote LB ClientConn using this manual resolver.
|
||||
scheme := "grpclb_internal_" + strconv.FormatInt(time.Now().UnixNano(), 36)
|
||||
r := &lbManualResolver{scheme: scheme, ccb: cc}
|
||||
|
||||
var target string
|
||||
targetSplitted := strings.Split(cc.Target(), ":///")
|
||||
if len(targetSplitted) < 2 {
|
||||
target = cc.Target()
|
||||
} else {
|
||||
target = targetSplitted[1]
|
||||
}
|
||||
|
||||
lb := &lbBalancer{
|
||||
cc: cc,
|
||||
target: target,
|
||||
opt: opt,
|
||||
fallbackTimeout: b.fallbackTimeout,
|
||||
doneCh: make(chan struct{}),
|
||||
|
||||
manualResolver: r,
|
||||
csEvltr: &connectivityStateEvaluator{},
|
||||
subConns: make(map[resolver.Address]balancer.SubConn),
|
||||
scStates: make(map[balancer.SubConn]connectivity.State),
|
||||
picker: &errPicker{err: balancer.ErrNoSubConnAvailable},
|
||||
clientStats: &rpcStats{},
|
||||
}
|
||||
|
||||
return lb
|
||||
}
|
||||
|
||||
type lbBalancer struct {
|
||||
cc balancer.ClientConn
|
||||
target string
|
||||
opt balancer.BuildOptions
|
||||
fallbackTimeout time.Duration
|
||||
doneCh chan struct{}
|
||||
|
||||
// manualResolver is used in the remote LB ClientConn inside grpclb. When
|
||||
// resolved address updates are received by grpclb, filtered updates will be
|
||||
// send to remote LB ClientConn through this resolver.
|
||||
manualResolver *lbManualResolver
|
||||
// The ClientConn to talk to the remote balancer.
|
||||
ccRemoteLB *ClientConn
|
||||
|
||||
// Support client side load reporting. Each picker gets a reference to this,
|
||||
// and will update its content.
|
||||
clientStats *rpcStats
|
||||
|
||||
mu sync.Mutex // guards everything following.
|
||||
// The full server list including drops, used to check if the newly received
|
||||
// serverList contains anything new. Each generate picker will also have
|
||||
// reference to this list to do the first layer pick.
|
||||
fullServerList []*lbpb.Server
|
||||
// All backends addresses, with metadata set to nil. This list contains all
|
||||
// backend addresses in the same order and with the same duplicates as in
|
||||
// serverlist. When generating picker, a SubConn slice with the same order
|
||||
// but with only READY SCs will be gerenated.
|
||||
backendAddrs []resolver.Address
|
||||
// Roundrobin functionalities.
|
||||
csEvltr *connectivityStateEvaluator
|
||||
state connectivity.State
|
||||
subConns map[resolver.Address]balancer.SubConn // Used to new/remove SubConn.
|
||||
scStates map[balancer.SubConn]connectivity.State // Used to filter READY SubConns.
|
||||
picker balancer.Picker
|
||||
// Support fallback to resolved backend addresses if there's no response
|
||||
// from remote balancer within fallbackTimeout.
|
||||
fallbackTimerExpired bool
|
||||
serverListReceived bool
|
||||
// resolvedBackendAddrs is resolvedAddrs minus remote balancers. It's set
|
||||
// when resolved address updates are received, and read in the goroutine
|
||||
// handling fallback.
|
||||
resolvedBackendAddrs []resolver.Address
|
||||
}
|
||||
|
||||
// regeneratePicker takes a snapshot of the balancer, and generates a picker from
|
||||
// it. The picker
|
||||
// - always returns ErrTransientFailure if the balancer is in TransientFailure,
|
||||
// - does two layer roundrobin pick otherwise.
|
||||
// Caller must hold lb.mu.
|
||||
func (lb *lbBalancer) regeneratePicker() {
|
||||
if lb.state == connectivity.TransientFailure {
|
||||
lb.picker = &errPicker{err: balancer.ErrTransientFailure}
|
||||
return
|
||||
}
|
||||
var readySCs []balancer.SubConn
|
||||
for _, a := range lb.backendAddrs {
|
||||
if sc, ok := lb.subConns[a]; ok {
|
||||
if st, ok := lb.scStates[sc]; ok && st == connectivity.Ready {
|
||||
readySCs = append(readySCs, sc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(lb.fullServerList) <= 0 {
|
||||
if len(readySCs) <= 0 {
|
||||
lb.picker = &errPicker{err: balancer.ErrNoSubConnAvailable}
|
||||
return
|
||||
}
|
||||
lb.picker = &rrPicker{subConns: readySCs}
|
||||
return
|
||||
}
|
||||
lb.picker = &lbPicker{
|
||||
serverList: lb.fullServerList,
|
||||
subConns: readySCs,
|
||||
stats: lb.clientStats,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (lb *lbBalancer) HandleSubConnStateChange(sc balancer.SubConn, s connectivity.State) {
|
||||
grpclog.Infof("lbBalancer: handle SubConn state change: %p, %v", sc, s)
|
||||
lb.mu.Lock()
|
||||
defer lb.mu.Unlock()
|
||||
|
||||
oldS, ok := lb.scStates[sc]
|
||||
if !ok {
|
||||
grpclog.Infof("lbBalancer: got state changes for an unknown SubConn: %p, %v", sc, s)
|
||||
return
|
||||
}
|
||||
lb.scStates[sc] = s
|
||||
switch s {
|
||||
case connectivity.Idle:
|
||||
sc.Connect()
|
||||
case connectivity.Shutdown:
|
||||
// When an address was removed by resolver, b called RemoveSubConn but
|
||||
// kept the sc's state in scStates. Remove state for this sc here.
|
||||
delete(lb.scStates, sc)
|
||||
}
|
||||
|
||||
oldAggrState := lb.state
|
||||
lb.state = lb.csEvltr.recordTransition(oldS, s)
|
||||
|
||||
// Regenerate picker when one of the following happens:
|
||||
// - this sc became ready from not-ready
|
||||
// - this sc became not-ready from ready
|
||||
// - the aggregated state of balancer became TransientFailure from non-TransientFailure
|
||||
// - the aggregated state of balancer became non-TransientFailure from TransientFailure
|
||||
if (oldS == connectivity.Ready) != (s == connectivity.Ready) ||
|
||||
(lb.state == connectivity.TransientFailure) != (oldAggrState == connectivity.TransientFailure) {
|
||||
lb.regeneratePicker()
|
||||
}
|
||||
|
||||
lb.cc.UpdateBalancerState(lb.state, lb.picker)
|
||||
return
|
||||
}
|
||||
|
||||
// fallbackToBackendsAfter blocks for fallbackTimeout and falls back to use
|
||||
// resolved backends (backends received from resolver, not from remote balancer)
|
||||
// if no connection to remote balancers was successful.
|
||||
func (lb *lbBalancer) fallbackToBackendsAfter(fallbackTimeout time.Duration) {
|
||||
timer := time.NewTimer(fallbackTimeout)
|
||||
defer timer.Stop()
|
||||
select {
|
||||
case <-timer.C:
|
||||
case <-lb.doneCh:
|
||||
return
|
||||
}
|
||||
lb.mu.Lock()
|
||||
if lb.serverListReceived {
|
||||
lb.mu.Unlock()
|
||||
return
|
||||
}
|
||||
lb.fallbackTimerExpired = true
|
||||
lb.refreshSubConns(lb.resolvedBackendAddrs)
|
||||
lb.mu.Unlock()
|
||||
}
|
||||
|
||||
// HandleResolvedAddrs sends the updated remoteLB addresses to remoteLB
|
||||
// clientConn. The remoteLB clientConn will handle creating/removing remoteLB
|
||||
// connections.
|
||||
func (lb *lbBalancer) HandleResolvedAddrs(addrs []resolver.Address, err error) {
|
||||
grpclog.Infof("lbBalancer: handleResolvedResult: %+v", addrs)
|
||||
if len(addrs) <= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
var remoteBalancerAddrs, backendAddrs []resolver.Address
|
||||
for _, a := range addrs {
|
||||
if a.Type == resolver.GRPCLB {
|
||||
remoteBalancerAddrs = append(remoteBalancerAddrs, a)
|
||||
} else {
|
||||
backendAddrs = append(backendAddrs, a)
|
||||
}
|
||||
}
|
||||
|
||||
if lb.ccRemoteLB == nil {
|
||||
if len(remoteBalancerAddrs) <= 0 {
|
||||
grpclog.Errorf("grpclb: no remote balancer address is available, should never happen")
|
||||
return
|
||||
}
|
||||
// First time receiving resolved addresses, create a cc to remote
|
||||
// balancers.
|
||||
lb.dialRemoteLB(remoteBalancerAddrs[0].ServerName)
|
||||
// Start the fallback goroutine.
|
||||
go lb.fallbackToBackendsAfter(lb.fallbackTimeout)
|
||||
}
|
||||
|
||||
// cc to remote balancers uses lb.manualResolver. Send the updated remote
|
||||
// balancer addresses to it through manualResolver.
|
||||
lb.manualResolver.NewAddress(remoteBalancerAddrs)
|
||||
|
||||
lb.mu.Lock()
|
||||
lb.resolvedBackendAddrs = backendAddrs
|
||||
// If serverListReceived is true, connection to remote balancer was
|
||||
// successful and there's no need to do fallback anymore.
|
||||
// If fallbackTimerExpired is false, fallback hasn't happened yet.
|
||||
if !lb.serverListReceived && lb.fallbackTimerExpired {
|
||||
// This means we received a new list of resolved backends, and we are
|
||||
// still in fallback mode. Need to update the list of backends we are
|
||||
// using to the new list of backends.
|
||||
lb.refreshSubConns(lb.resolvedBackendAddrs)
|
||||
}
|
||||
lb.mu.Unlock()
|
||||
}
|
||||
|
||||
func (lb *lbBalancer) Close() {
|
||||
select {
|
||||
case <-lb.doneCh:
|
||||
return
|
||||
default:
|
||||
}
|
||||
close(lb.doneCh)
|
||||
if lb.ccRemoteLB != nil {
|
||||
lb.ccRemoteLB.Close()
|
||||
}
|
||||
}
|
159
vendor/google.golang.org/grpc/grpclb_picker.go
generated
vendored
Normal file
159
vendor/google.golang.org/grpc/grpclb_picker.go
generated
vendored
Normal file
|
@ -0,0 +1,159 @@
|
|||
/*
|
||||
*
|
||||
* 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"
|
||||
"sync/atomic"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc/balancer"
|
||||
"google.golang.org/grpc/codes"
|
||||
lbpb "google.golang.org/grpc/grpclb/grpc_lb_v1/messages"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
type rpcStats struct {
|
||||
NumCallsStarted int64
|
||||
NumCallsFinished int64
|
||||
NumCallsFinishedWithDropForRateLimiting int64
|
||||
NumCallsFinishedWithDropForLoadBalancing int64
|
||||
NumCallsFinishedWithClientFailedToSend int64
|
||||
NumCallsFinishedKnownReceived int64
|
||||
}
|
||||
|
||||
// toClientStats converts rpcStats to lbpb.ClientStats, and clears rpcStats.
|
||||
func (s *rpcStats) toClientStats() *lbpb.ClientStats {
|
||||
stats := &lbpb.ClientStats{
|
||||
NumCallsStarted: atomic.SwapInt64(&s.NumCallsStarted, 0),
|
||||
NumCallsFinished: atomic.SwapInt64(&s.NumCallsFinished, 0),
|
||||
NumCallsFinishedWithDropForRateLimiting: atomic.SwapInt64(&s.NumCallsFinishedWithDropForRateLimiting, 0),
|
||||
NumCallsFinishedWithDropForLoadBalancing: atomic.SwapInt64(&s.NumCallsFinishedWithDropForLoadBalancing, 0),
|
||||
NumCallsFinishedWithClientFailedToSend: atomic.SwapInt64(&s.NumCallsFinishedWithClientFailedToSend, 0),
|
||||
NumCallsFinishedKnownReceived: atomic.SwapInt64(&s.NumCallsFinishedKnownReceived, 0),
|
||||
}
|
||||
return stats
|
||||
}
|
||||
|
||||
func (s *rpcStats) dropForRateLimiting() {
|
||||
atomic.AddInt64(&s.NumCallsStarted, 1)
|
||||
atomic.AddInt64(&s.NumCallsFinishedWithDropForRateLimiting, 1)
|
||||
atomic.AddInt64(&s.NumCallsFinished, 1)
|
||||
}
|
||||
|
||||
func (s *rpcStats) dropForLoadBalancing() {
|
||||
atomic.AddInt64(&s.NumCallsStarted, 1)
|
||||
atomic.AddInt64(&s.NumCallsFinishedWithDropForLoadBalancing, 1)
|
||||
atomic.AddInt64(&s.NumCallsFinished, 1)
|
||||
}
|
||||
|
||||
func (s *rpcStats) failedToSend() {
|
||||
atomic.AddInt64(&s.NumCallsStarted, 1)
|
||||
atomic.AddInt64(&s.NumCallsFinishedWithClientFailedToSend, 1)
|
||||
atomic.AddInt64(&s.NumCallsFinished, 1)
|
||||
}
|
||||
|
||||
func (s *rpcStats) knownReceived() {
|
||||
atomic.AddInt64(&s.NumCallsStarted, 1)
|
||||
atomic.AddInt64(&s.NumCallsFinishedKnownReceived, 1)
|
||||
atomic.AddInt64(&s.NumCallsFinished, 1)
|
||||
}
|
||||
|
||||
type errPicker struct {
|
||||
// Pick always returns this err.
|
||||
err error
|
||||
}
|
||||
|
||||
func (p *errPicker) Pick(ctx context.Context, opts balancer.PickOptions) (balancer.SubConn, func(balancer.DoneInfo), error) {
|
||||
return nil, nil, p.err
|
||||
}
|
||||
|
||||
// rrPicker does roundrobin on subConns. It's typically used when there's no
|
||||
// response from remote balancer, and grpclb falls back to the resolved
|
||||
// backends.
|
||||
//
|
||||
// It guaranteed that len(subConns) > 0.
|
||||
type rrPicker struct {
|
||||
mu sync.Mutex
|
||||
subConns []balancer.SubConn // The subConns that were READY when taking the snapshot.
|
||||
subConnsNext int
|
||||
}
|
||||
|
||||
func (p *rrPicker) Pick(ctx context.Context, opts balancer.PickOptions) (balancer.SubConn, func(balancer.DoneInfo), error) {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
sc := p.subConns[p.subConnsNext]
|
||||
p.subConnsNext = (p.subConnsNext + 1) % len(p.subConns)
|
||||
return sc, nil, nil
|
||||
}
|
||||
|
||||
// lbPicker does two layers of picks:
|
||||
//
|
||||
// First layer: roundrobin on all servers in serverList, including drops and backends.
|
||||
// - If it picks a drop, the RPC will fail as being dropped.
|
||||
// - If it picks a backend, do a second layer pick to pick the real backend.
|
||||
//
|
||||
// Second layer: roundrobin on all READY backends.
|
||||
//
|
||||
// It's guaranteed that len(serverList) > 0.
|
||||
type lbPicker struct {
|
||||
mu sync.Mutex
|
||||
serverList []*lbpb.Server
|
||||
serverListNext int
|
||||
subConns []balancer.SubConn // The subConns that were READY when taking the snapshot.
|
||||
subConnsNext int
|
||||
|
||||
stats *rpcStats
|
||||
}
|
||||
|
||||
func (p *lbPicker) Pick(ctx context.Context, opts balancer.PickOptions) (balancer.SubConn, func(balancer.DoneInfo), error) {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
|
||||
// Layer one roundrobin on serverList.
|
||||
s := p.serverList[p.serverListNext]
|
||||
p.serverListNext = (p.serverListNext + 1) % len(p.serverList)
|
||||
|
||||
// If it's a drop, return an error and fail the RPC.
|
||||
if s.DropForRateLimiting {
|
||||
p.stats.dropForRateLimiting()
|
||||
return nil, nil, status.Errorf(codes.Unavailable, "request dropped by grpclb")
|
||||
}
|
||||
if s.DropForLoadBalancing {
|
||||
p.stats.dropForLoadBalancing()
|
||||
return nil, nil, status.Errorf(codes.Unavailable, "request dropped by grpclb")
|
||||
}
|
||||
|
||||
// If not a drop but there's no ready subConns.
|
||||
if len(p.subConns) <= 0 {
|
||||
return nil, nil, balancer.ErrNoSubConnAvailable
|
||||
}
|
||||
|
||||
// Return the next ready subConn in the list, also collect rpc stats.
|
||||
sc := p.subConns[p.subConnsNext]
|
||||
p.subConnsNext = (p.subConnsNext + 1) % len(p.subConns)
|
||||
done := func(info balancer.DoneInfo) {
|
||||
if !info.BytesSent {
|
||||
p.stats.failedToSend()
|
||||
} else if info.BytesReceived {
|
||||
p.stats.knownReceived()
|
||||
}
|
||||
}
|
||||
return sc, done, nil
|
||||
}
|
254
vendor/google.golang.org/grpc/grpclb_remote_balancer.go
generated
vendored
Normal file
254
vendor/google.golang.org/grpc/grpclb_remote_balancer.go
generated
vendored
Normal file
|
@ -0,0 +1,254 @@
|
|||
/*
|
||||
*
|
||||
* 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 (
|
||||
"fmt"
|
||||
"net"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc/balancer"
|
||||
"google.golang.org/grpc/connectivity"
|
||||
lbpb "google.golang.org/grpc/grpclb/grpc_lb_v1/messages"
|
||||
"google.golang.org/grpc/grpclog"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/resolver"
|
||||
)
|
||||
|
||||
// processServerList updates balaner's internal state, create/remove SubConns
|
||||
// and regenerates picker using the received serverList.
|
||||
func (lb *lbBalancer) processServerList(l *lbpb.ServerList) {
|
||||
grpclog.Infof("lbBalancer: processing server list: %+v", l)
|
||||
lb.mu.Lock()
|
||||
defer lb.mu.Unlock()
|
||||
|
||||
// Set serverListReceived to true so fallback will not take effect if it has
|
||||
// not hit timeout.
|
||||
lb.serverListReceived = true
|
||||
|
||||
// If the new server list == old server list, do nothing.
|
||||
if reflect.DeepEqual(lb.fullServerList, l.Servers) {
|
||||
grpclog.Infof("lbBalancer: new serverlist same as the previous one, ignoring")
|
||||
return
|
||||
}
|
||||
lb.fullServerList = l.Servers
|
||||
|
||||
var backendAddrs []resolver.Address
|
||||
for _, s := range l.Servers {
|
||||
if s.DropForLoadBalancing || s.DropForRateLimiting {
|
||||
continue
|
||||
}
|
||||
|
||||
md := metadata.Pairs(lbTokeyKey, s.LoadBalanceToken)
|
||||
ip := net.IP(s.IpAddress)
|
||||
ipStr := ip.String()
|
||||
if ip.To4() == nil {
|
||||
// Add square brackets to ipv6 addresses, otherwise net.Dial() and
|
||||
// net.SplitHostPort() will return too many colons error.
|
||||
ipStr = fmt.Sprintf("[%s]", ipStr)
|
||||
}
|
||||
addr := resolver.Address{
|
||||
Addr: fmt.Sprintf("%s:%d", ipStr, s.Port),
|
||||
Metadata: &md,
|
||||
}
|
||||
|
||||
backendAddrs = append(backendAddrs, addr)
|
||||
}
|
||||
|
||||
// Call refreshSubConns to create/remove SubConns.
|
||||
backendsUpdated := lb.refreshSubConns(backendAddrs)
|
||||
// If no backend was updated, no SubConn will be newed/removed. But since
|
||||
// the full serverList was different, there might be updates in drops or
|
||||
// pick weights(different number of duplicates). We need to update picker
|
||||
// with the fulllist.
|
||||
if !backendsUpdated {
|
||||
lb.regeneratePicker()
|
||||
lb.cc.UpdateBalancerState(lb.state, lb.picker)
|
||||
}
|
||||
}
|
||||
|
||||
// refreshSubConns creates/removes SubConns with backendAddrs. It returns a bool
|
||||
// indicating whether the backendAddrs are different from the cached
|
||||
// backendAddrs (whether any SubConn was newed/removed).
|
||||
// Caller must hold lb.mu.
|
||||
func (lb *lbBalancer) refreshSubConns(backendAddrs []resolver.Address) bool {
|
||||
lb.backendAddrs = nil
|
||||
var backendsUpdated bool
|
||||
// addrsSet is the set converted from backendAddrs, it's used to quick
|
||||
// lookup for an address.
|
||||
addrsSet := make(map[resolver.Address]struct{})
|
||||
// Create new SubConns.
|
||||
for _, addr := range backendAddrs {
|
||||
addrWithoutMD := addr
|
||||
addrWithoutMD.Metadata = nil
|
||||
addrsSet[addrWithoutMD] = struct{}{}
|
||||
lb.backendAddrs = append(lb.backendAddrs, addrWithoutMD)
|
||||
|
||||
if _, ok := lb.subConns[addrWithoutMD]; !ok {
|
||||
backendsUpdated = true
|
||||
|
||||
// Use addrWithMD to create the SubConn.
|
||||
sc, err := lb.cc.NewSubConn([]resolver.Address{addr}, balancer.NewSubConnOptions{})
|
||||
if err != nil {
|
||||
grpclog.Warningf("roundrobinBalancer: failed to create new SubConn: %v", err)
|
||||
continue
|
||||
}
|
||||
lb.subConns[addrWithoutMD] = sc // Use the addr without MD as key for the map.
|
||||
lb.scStates[sc] = connectivity.Idle
|
||||
sc.Connect()
|
||||
}
|
||||
}
|
||||
|
||||
for a, sc := range lb.subConns {
|
||||
// a was removed by resolver.
|
||||
if _, ok := addrsSet[a]; !ok {
|
||||
backendsUpdated = true
|
||||
|
||||
lb.cc.RemoveSubConn(sc)
|
||||
delete(lb.subConns, a)
|
||||
// Keep the state of this sc in b.scStates until sc's state becomes Shutdown.
|
||||
// The entry will be deleted in HandleSubConnStateChange.
|
||||
}
|
||||
}
|
||||
|
||||
return backendsUpdated
|
||||
}
|
||||
|
||||
func (lb *lbBalancer) readServerList(s *balanceLoadClientStream) error {
|
||||
for {
|
||||
reply, err := s.Recv()
|
||||
if err != nil {
|
||||
return fmt.Errorf("grpclb: failed to recv server list: %v", err)
|
||||
}
|
||||
if serverList := reply.GetServerList(); serverList != nil {
|
||||
lb.processServerList(serverList)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (lb *lbBalancer) sendLoadReport(s *balanceLoadClientStream, interval time.Duration) {
|
||||
ticker := time.NewTicker(interval)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
case <-s.Context().Done():
|
||||
return
|
||||
}
|
||||
stats := lb.clientStats.toClientStats()
|
||||
t := time.Now()
|
||||
stats.Timestamp = &lbpb.Timestamp{
|
||||
Seconds: t.Unix(),
|
||||
Nanos: int32(t.Nanosecond()),
|
||||
}
|
||||
if err := s.Send(&lbpb.LoadBalanceRequest{
|
||||
LoadBalanceRequestType: &lbpb.LoadBalanceRequest_ClientStats{
|
||||
ClientStats: stats,
|
||||
},
|
||||
}); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
func (lb *lbBalancer) callRemoteBalancer() error {
|
||||
lbClient := &loadBalancerClient{cc: lb.ccRemoteLB}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
stream, err := lbClient.BalanceLoad(ctx, FailFast(false))
|
||||
if err != nil {
|
||||
return fmt.Errorf("grpclb: failed to perform RPC to the remote balancer %v", err)
|
||||
}
|
||||
|
||||
// grpclb handshake on the stream.
|
||||
initReq := &lbpb.LoadBalanceRequest{
|
||||
LoadBalanceRequestType: &lbpb.LoadBalanceRequest_InitialRequest{
|
||||
InitialRequest: &lbpb.InitialLoadBalanceRequest{
|
||||
Name: lb.target,
|
||||
},
|
||||
},
|
||||
}
|
||||
if err := stream.Send(initReq); err != nil {
|
||||
return fmt.Errorf("grpclb: failed to send init request: %v", err)
|
||||
}
|
||||
reply, err := stream.Recv()
|
||||
if err != nil {
|
||||
return fmt.Errorf("grpclb: failed to recv init response: %v", err)
|
||||
}
|
||||
initResp := reply.GetInitialResponse()
|
||||
if initResp == nil {
|
||||
return fmt.Errorf("grpclb: reply from remote balancer did not include initial response")
|
||||
}
|
||||
if initResp.LoadBalancerDelegate != "" {
|
||||
return fmt.Errorf("grpclb: Delegation is not supported")
|
||||
}
|
||||
|
||||
go func() {
|
||||
if d := convertDuration(initResp.ClientStatsReportInterval); d > 0 {
|
||||
lb.sendLoadReport(stream, d)
|
||||
}
|
||||
}()
|
||||
return lb.readServerList(stream)
|
||||
}
|
||||
|
||||
func (lb *lbBalancer) watchRemoteBalancer() {
|
||||
for {
|
||||
err := lb.callRemoteBalancer()
|
||||
select {
|
||||
case <-lb.doneCh:
|
||||
return
|
||||
default:
|
||||
if err != nil {
|
||||
grpclog.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func (lb *lbBalancer) dialRemoteLB(remoteLBName string) {
|
||||
var dopts []DialOption
|
||||
if creds := lb.opt.DialCreds; creds != nil {
|
||||
if err := creds.OverrideServerName(remoteLBName); err == nil {
|
||||
dopts = append(dopts, WithTransportCredentials(creds))
|
||||
} else {
|
||||
grpclog.Warningf("grpclb: failed to override the server name in the credentials: %v, using Insecure", err)
|
||||
dopts = append(dopts, WithInsecure())
|
||||
}
|
||||
} else {
|
||||
dopts = append(dopts, WithInsecure())
|
||||
}
|
||||
if lb.opt.Dialer != nil {
|
||||
// WithDialer takes a different type of function, so we instead use a
|
||||
// special DialOption here.
|
||||
dopts = append(dopts, withContextDialer(lb.opt.Dialer))
|
||||
}
|
||||
// Explicitly set pickfirst as the balancer.
|
||||
dopts = append(dopts, WithBalancerName(PickFirstBalancerName))
|
||||
dopts = append(dopts, withResolverBuilder(lb.manualResolver))
|
||||
// Dial using manualResolver.Scheme, which is a random scheme generated
|
||||
// when init grpclb. The target name is not important.
|
||||
cc, err := Dial("grpclb:///grpclb.server", dopts...)
|
||||
if err != nil {
|
||||
grpclog.Fatalf("failed to dial: %v", err)
|
||||
}
|
||||
lb.ccRemoteLB = cc
|
||||
go lb.watchRemoteBalancer()
|
||||
}
|
90
vendor/google.golang.org/grpc/grpclb_util.go
generated
vendored
Normal file
90
vendor/google.golang.org/grpc/grpclb_util.go
generated
vendored
Normal file
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
*
|
||||
* 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 (
|
||||
"google.golang.org/grpc/balancer"
|
||||
"google.golang.org/grpc/resolver"
|
||||
)
|
||||
|
||||
// The parent ClientConn should re-resolve when grpclb loses connection to the
|
||||
// remote balancer. When the ClientConn inside grpclb gets a TransientFailure,
|
||||
// it calls lbManualResolver.ResolveNow(), which calls parent ClientConn's
|
||||
// ResolveNow, and eventually results in re-resolve happening in parent
|
||||
// ClientConn's resolver (DNS for example).
|
||||
//
|
||||
// parent
|
||||
// ClientConn
|
||||
// +-----------------------------------------------------------------+
|
||||
// | parent +---------------------------------+ |
|
||||
// | DNS ClientConn | grpclb | |
|
||||
// | resolver balancerWrapper | | |
|
||||
// | + + | grpclb grpclb | |
|
||||
// | | | | ManualResolver ClientConn | |
|
||||
// | | | | + + | |
|
||||
// | | | | | | Transient | |
|
||||
// | | | | | | Failure | |
|
||||
// | | | | | <--------- | | |
|
||||
// | | | <--------------- | ResolveNow | | |
|
||||
// | | <--------- | ResolveNow | | | | |
|
||||
// | | ResolveNow | | | | | |
|
||||
// | | | | | | | |
|
||||
// | + + | + + | |
|
||||
// | +---------------------------------+ |
|
||||
// +-----------------------------------------------------------------+
|
||||
|
||||
// lbManualResolver is used by the ClientConn inside grpclb. It's a manual
|
||||
// resolver with a special ResolveNow() function.
|
||||
//
|
||||
// When ResolveNow() is called, it calls ResolveNow() on the parent ClientConn,
|
||||
// so when grpclb client lose contact with remote balancers, the parent
|
||||
// ClientConn's resolver will re-resolve.
|
||||
type lbManualResolver struct {
|
||||
scheme string
|
||||
ccr resolver.ClientConn
|
||||
|
||||
ccb balancer.ClientConn
|
||||
}
|
||||
|
||||
func (r *lbManualResolver) Build(_ resolver.Target, cc resolver.ClientConn, _ resolver.BuildOption) (resolver.Resolver, error) {
|
||||
r.ccr = cc
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func (r *lbManualResolver) Scheme() string {
|
||||
return r.scheme
|
||||
}
|
||||
|
||||
// ResolveNow calls resolveNow on the parent ClientConn.
|
||||
func (r *lbManualResolver) ResolveNow(o resolver.ResolveNowOption) {
|
||||
r.ccb.ResolveNow(o)
|
||||
}
|
||||
|
||||
// Close is a noop for Resolver.
|
||||
func (*lbManualResolver) Close() {}
|
||||
|
||||
// NewAddress calls cc.NewAddress.
|
||||
func (r *lbManualResolver) NewAddress(addrs []resolver.Address) {
|
||||
r.ccr.NewAddress(addrs)
|
||||
}
|
||||
|
||||
// NewServiceConfig calls cc.NewServiceConfig.
|
||||
func (r *lbManualResolver) NewServiceConfig(sc string) {
|
||||
r.ccr.NewServiceConfig(sc)
|
||||
}
|
123
vendor/google.golang.org/grpc/grpclog/grpclog.go
generated
vendored
Normal file
123
vendor/google.golang.org/grpc/grpclog/grpclog.go
generated
vendored
Normal file
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
*
|
||||
* 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 grpclog defines logging for grpc.
|
||||
//
|
||||
// All logs in transport package only go to verbose level 2.
|
||||
// All logs in other packages in grpc are logged in spite of the verbosity level.
|
||||
//
|
||||
// In the default logger,
|
||||
// severity level can be set by environment variable GRPC_GO_LOG_SEVERITY_LEVEL,
|
||||
// verbosity level can be set by GRPC_GO_LOG_VERBOSITY_LEVEL.
|
||||
package grpclog // import "google.golang.org/grpc/grpclog"
|
||||
|
||||
import "os"
|
||||
|
||||
var logger = newLoggerV2()
|
||||
|
||||
// V reports whether verbosity level l is at least the requested verbose level.
|
||||
func V(l int) bool {
|
||||
return logger.V(l)
|
||||
}
|
||||
|
||||
// Info logs to the INFO log.
|
||||
func Info(args ...interface{}) {
|
||||
logger.Info(args...)
|
||||
}
|
||||
|
||||
// Infof logs to the INFO log. Arguments are handled in the manner of fmt.Printf.
|
||||
func Infof(format string, args ...interface{}) {
|
||||
logger.Infof(format, args...)
|
||||
}
|
||||
|
||||
// Infoln logs to the INFO log. Arguments are handled in the manner of fmt.Println.
|
||||
func Infoln(args ...interface{}) {
|
||||
logger.Infoln(args...)
|
||||
}
|
||||
|
||||
// Warning logs to the WARNING log.
|
||||
func Warning(args ...interface{}) {
|
||||
logger.Warning(args...)
|
||||
}
|
||||
|
||||
// Warningf logs to the WARNING log. Arguments are handled in the manner of fmt.Printf.
|
||||
func Warningf(format string, args ...interface{}) {
|
||||
logger.Warningf(format, args...)
|
||||
}
|
||||
|
||||
// Warningln logs to the WARNING log. Arguments are handled in the manner of fmt.Println.
|
||||
func Warningln(args ...interface{}) {
|
||||
logger.Warningln(args...)
|
||||
}
|
||||
|
||||
// Error logs to the ERROR log.
|
||||
func Error(args ...interface{}) {
|
||||
logger.Error(args...)
|
||||
}
|
||||
|
||||
// Errorf logs to the ERROR log. Arguments are handled in the manner of fmt.Printf.
|
||||
func Errorf(format string, args ...interface{}) {
|
||||
logger.Errorf(format, args...)
|
||||
}
|
||||
|
||||
// Errorln logs to the ERROR log. Arguments are handled in the manner of fmt.Println.
|
||||
func Errorln(args ...interface{}) {
|
||||
logger.Errorln(args...)
|
||||
}
|
||||
|
||||
// Fatal logs to the FATAL log. Arguments are handled in the manner of fmt.Print.
|
||||
// It calls os.Exit() with exit code 1.
|
||||
func Fatal(args ...interface{}) {
|
||||
logger.Fatal(args...)
|
||||
// Make sure fatal logs will exit.
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Fatalf logs to the FATAL log. Arguments are handled in the manner of fmt.Printf.
|
||||
// It calles os.Exit() with exit code 1.
|
||||
func Fatalf(format string, args ...interface{}) {
|
||||
logger.Fatalf(format, args...)
|
||||
// Make sure fatal logs will exit.
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Fatalln logs to the FATAL log. Arguments are handled in the manner of fmt.Println.
|
||||
// It calle os.Exit()) with exit code 1.
|
||||
func Fatalln(args ...interface{}) {
|
||||
logger.Fatalln(args...)
|
||||
// Make sure fatal logs will exit.
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Print prints to the logger. Arguments are handled in the manner of fmt.Print.
|
||||
// Deprecated: use Info.
|
||||
func Print(args ...interface{}) {
|
||||
logger.Info(args...)
|
||||
}
|
||||
|
||||
// Printf prints to the logger. Arguments are handled in the manner of fmt.Printf.
|
||||
// Deprecated: use Infof.
|
||||
func Printf(format string, args ...interface{}) {
|
||||
logger.Infof(format, args...)
|
||||
}
|
||||
|
||||
// Println prints to the logger. Arguments are handled in the manner of fmt.Println.
|
||||
// Deprecated: use Infoln.
|
||||
func Println(args ...interface{}) {
|
||||
logger.Infoln(args...)
|
||||
}
|
83
vendor/google.golang.org/grpc/grpclog/logger.go
generated
vendored
Normal file
83
vendor/google.golang.org/grpc/grpclog/logger.go
generated
vendored
Normal file
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
*
|
||||
* Copyright 2015 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
|
||||
|
||||
// Logger mimics golang's standard Logger as an interface.
|
||||
// Deprecated: use LoggerV2.
|
||||
type Logger interface {
|
||||
Fatal(args ...interface{})
|
||||
Fatalf(format string, args ...interface{})
|
||||
Fatalln(args ...interface{})
|
||||
Print(args ...interface{})
|
||||
Printf(format string, args ...interface{})
|
||||
Println(args ...interface{})
|
||||
}
|
||||
|
||||
// SetLogger sets the logger that is used in grpc. Call only from
|
||||
// init() functions.
|
||||
// Deprecated: use SetLoggerV2.
|
||||
func SetLogger(l Logger) {
|
||||
logger = &loggerWrapper{Logger: l}
|
||||
}
|
||||
|
||||
// loggerWrapper wraps Logger into a LoggerV2.
|
||||
type loggerWrapper struct {
|
||||
Logger
|
||||
}
|
||||
|
||||
func (g *loggerWrapper) Info(args ...interface{}) {
|
||||
g.Logger.Print(args...)
|
||||
}
|
||||
|
||||
func (g *loggerWrapper) Infoln(args ...interface{}) {
|
||||
g.Logger.Println(args...)
|
||||
}
|
||||
|
||||
func (g *loggerWrapper) Infof(format string, args ...interface{}) {
|
||||
g.Logger.Printf(format, args...)
|
||||
}
|
||||
|
||||
func (g *loggerWrapper) Warning(args ...interface{}) {
|
||||
g.Logger.Print(args...)
|
||||
}
|
||||
|
||||
func (g *loggerWrapper) Warningln(args ...interface{}) {
|
||||
g.Logger.Println(args...)
|
||||
}
|
||||
|
||||
func (g *loggerWrapper) Warningf(format string, args ...interface{}) {
|
||||
g.Logger.Printf(format, args...)
|
||||
}
|
||||
|
||||
func (g *loggerWrapper) Error(args ...interface{}) {
|
||||
g.Logger.Print(args...)
|
||||
}
|
||||
|
||||
func (g *loggerWrapper) Errorln(args ...interface{}) {
|
||||
g.Logger.Println(args...)
|
||||
}
|
||||
|
||||
func (g *loggerWrapper) Errorf(format string, args ...interface{}) {
|
||||
g.Logger.Printf(format, args...)
|
||||
}
|
||||
|
||||
func (g *loggerWrapper) V(l int) bool {
|
||||
// Returns true for all verbose level.
|
||||
return true
|
||||
}
|
195
vendor/google.golang.org/grpc/grpclog/loggerv2.go
generated
vendored
Normal file
195
vendor/google.golang.org/grpc/grpclog/loggerv2.go
generated
vendored
Normal file
|
@ -0,0 +1,195 @@
|
|||
/*
|
||||
*
|
||||
* 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 grpclog
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// LoggerV2 does underlying logging work for grpclog.
|
||||
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
|
||||
}
|
||||
|
||||
// SetLoggerV2 sets logger that is used in grpc to a V2 logger.
|
||||
// Not mutex-protected, should be called before any gRPC functions.
|
||||
func SetLoggerV2(l LoggerV2) {
|
||||
logger = l
|
||||
}
|
||||
|
||||
const (
|
||||
// infoLog indicates Info severity.
|
||||
infoLog int = iota
|
||||
// warningLog indicates Warning severity.
|
||||
warningLog
|
||||
// errorLog indicates Error severity.
|
||||
errorLog
|
||||
// fatalLog indicates Fatal severity.
|
||||
fatalLog
|
||||
)
|
||||
|
||||
// severityName contains the string representation of each severity.
|
||||
var severityName = []string{
|
||||
infoLog: "INFO",
|
||||
warningLog: "WARNING",
|
||||
errorLog: "ERROR",
|
||||
fatalLog: "FATAL",
|
||||
}
|
||||
|
||||
// loggerT is the default logger used by grpclog.
|
||||
type loggerT struct {
|
||||
m []*log.Logger
|
||||
v int
|
||||
}
|
||||
|
||||
// NewLoggerV2 creates a loggerV2 with the provided writers.
|
||||
// Fatal logs will be written to errorW, warningW, infoW, followed by exit(1).
|
||||
// Error logs will be written to errorW, warningW and infoW.
|
||||
// Warning logs will be written to warningW and infoW.
|
||||
// Info logs will be written to infoW.
|
||||
func NewLoggerV2(infoW, warningW, errorW io.Writer) LoggerV2 {
|
||||
return NewLoggerV2WithVerbosity(infoW, warningW, errorW, 0)
|
||||
}
|
||||
|
||||
// NewLoggerV2WithVerbosity creates a loggerV2 with the provided writers and
|
||||
// verbosity level.
|
||||
func NewLoggerV2WithVerbosity(infoW, warningW, errorW io.Writer, v int) LoggerV2 {
|
||||
var m []*log.Logger
|
||||
m = append(m, log.New(infoW, severityName[infoLog]+": ", log.LstdFlags))
|
||||
m = append(m, log.New(io.MultiWriter(infoW, warningW), severityName[warningLog]+": ", log.LstdFlags))
|
||||
ew := io.MultiWriter(infoW, warningW, errorW) // ew will be used for error and fatal.
|
||||
m = append(m, log.New(ew, severityName[errorLog]+": ", log.LstdFlags))
|
||||
m = append(m, log.New(ew, severityName[fatalLog]+": ", log.LstdFlags))
|
||||
return &loggerT{m: m, v: v}
|
||||
}
|
||||
|
||||
// newLoggerV2 creates a loggerV2 to be used as default logger.
|
||||
// All logs are written to stderr.
|
||||
func newLoggerV2() LoggerV2 {
|
||||
errorW := ioutil.Discard
|
||||
warningW := ioutil.Discard
|
||||
infoW := ioutil.Discard
|
||||
|
||||
logLevel := os.Getenv("GRPC_GO_LOG_SEVERITY_LEVEL")
|
||||
switch logLevel {
|
||||
case "", "ERROR", "error": // If env is unset, set level to ERROR.
|
||||
errorW = os.Stderr
|
||||
case "WARNING", "warning":
|
||||
warningW = os.Stderr
|
||||
case "INFO", "info":
|
||||
infoW = os.Stderr
|
||||
}
|
||||
|
||||
var v int
|
||||
vLevel := os.Getenv("GRPC_GO_LOG_VERBOSITY_LEVEL")
|
||||
if vl, err := strconv.Atoi(vLevel); err == nil {
|
||||
v = vl
|
||||
}
|
||||
return NewLoggerV2WithVerbosity(infoW, warningW, errorW, v)
|
||||
}
|
||||
|
||||
func (g *loggerT) Info(args ...interface{}) {
|
||||
g.m[infoLog].Print(args...)
|
||||
}
|
||||
|
||||
func (g *loggerT) Infoln(args ...interface{}) {
|
||||
g.m[infoLog].Println(args...)
|
||||
}
|
||||
|
||||
func (g *loggerT) Infof(format string, args ...interface{}) {
|
||||
g.m[infoLog].Printf(format, args...)
|
||||
}
|
||||
|
||||
func (g *loggerT) Warning(args ...interface{}) {
|
||||
g.m[warningLog].Print(args...)
|
||||
}
|
||||
|
||||
func (g *loggerT) Warningln(args ...interface{}) {
|
||||
g.m[warningLog].Println(args...)
|
||||
}
|
||||
|
||||
func (g *loggerT) Warningf(format string, args ...interface{}) {
|
||||
g.m[warningLog].Printf(format, args...)
|
||||
}
|
||||
|
||||
func (g *loggerT) Error(args ...interface{}) {
|
||||
g.m[errorLog].Print(args...)
|
||||
}
|
||||
|
||||
func (g *loggerT) Errorln(args ...interface{}) {
|
||||
g.m[errorLog].Println(args...)
|
||||
}
|
||||
|
||||
func (g *loggerT) Errorf(format string, args ...interface{}) {
|
||||
g.m[errorLog].Printf(format, args...)
|
||||
}
|
||||
|
||||
func (g *loggerT) Fatal(args ...interface{}) {
|
||||
g.m[fatalLog].Fatal(args...)
|
||||
// No need to call os.Exit() again because log.Logger.Fatal() calls os.Exit().
|
||||
}
|
||||
|
||||
func (g *loggerT) Fatalln(args ...interface{}) {
|
||||
g.m[fatalLog].Fatalln(args...)
|
||||
// No need to call os.Exit() again because log.Logger.Fatal() calls os.Exit().
|
||||
}
|
||||
|
||||
func (g *loggerT) Fatalf(format string, args ...interface{}) {
|
||||
g.m[fatalLog].Fatalf(format, args...)
|
||||
// No need to call os.Exit() again because log.Logger.Fatal() calls os.Exit().
|
||||
}
|
||||
|
||||
func (g *loggerT) V(l int) bool {
|
||||
return l <= g.v
|
||||
}
|
62
vendor/google.golang.org/grpc/grpclog/loggerv2_test.go
generated
vendored
Normal file
62
vendor/google.golang.org/grpc/grpclog/loggerv2_test.go
generated
vendored
Normal file
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
*
|
||||
* 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 grpclog
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLoggerV2Severity(t *testing.T) {
|
||||
buffers := []*bytes.Buffer{new(bytes.Buffer), new(bytes.Buffer), new(bytes.Buffer)}
|
||||
SetLoggerV2(NewLoggerV2(buffers[infoLog], buffers[warningLog], buffers[errorLog]))
|
||||
|
||||
Info(severityName[infoLog])
|
||||
Warning(severityName[warningLog])
|
||||
Error(severityName[errorLog])
|
||||
|
||||
for i := 0; i < fatalLog; i++ {
|
||||
buf := buffers[i]
|
||||
// The content of info buffer should be something like:
|
||||
// INFO: 2017/04/07 14:55:42 INFO
|
||||
// WARNING: 2017/04/07 14:55:42 WARNING
|
||||
// ERROR: 2017/04/07 14:55:42 ERROR
|
||||
for j := i; j < fatalLog; j++ {
|
||||
b, err := buf.ReadBytes('\n')
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := checkLogForSeverity(j, b); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check if b is in the format of:
|
||||
// WARNING: 2017/04/07 14:55:42 WARNING
|
||||
func checkLogForSeverity(s int, b []byte) error {
|
||||
expected := regexp.MustCompile(fmt.Sprintf(`^%s: [0-9]{4}/[0-9]{2}/[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} %s\n$`, severityName[s], severityName[s]))
|
||||
if m := expected.Match(b); !m {
|
||||
return fmt.Errorf("got: %v, want string in format of: %v", string(b), severityName[s]+": 2016/10/05 17:09:26 "+severityName[s])
|
||||
}
|
||||
return nil
|
||||
}
|
75
vendor/google.golang.org/grpc/interceptor.go
generated
vendored
Normal file
75
vendor/google.golang.org/grpc/interceptor.go
generated
vendored
Normal file
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
*
|
||||
* 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 (
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// UnaryInvoker is called by UnaryClientInterceptor to complete RPCs.
|
||||
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
|
||||
// and it is the responsibility of the interceptor to call it.
|
||||
// This is an EXPERIMENTAL API.
|
||||
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.
|
||||
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
|
||||
// operations. streamer is the handler to create a ClientStream and it is the responsibility of the interceptor to call it.
|
||||
// This is an EXPERIMENTAL API.
|
||||
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
|
||||
// server side. All per-rpc information may be mutated by the interceptor.
|
||||
type UnaryServerInfo struct {
|
||||
// Server is the service implementation the user provides. This is read-only.
|
||||
Server interface{}
|
||||
// FullMethod is the full RPC method string, i.e., /package.service/method.
|
||||
FullMethod string
|
||||
}
|
||||
|
||||
// UnaryHandler defines the handler invoked by UnaryServerInterceptor to complete the normal
|
||||
// execution of a unary RPC.
|
||||
type UnaryHandler func(ctx context.Context, req interface{}) (interface{}, error)
|
||||
|
||||
// UnaryServerInterceptor provides a hook to intercept the execution of a unary RPC on the server. info
|
||||
// contains all the information of this RPC the interceptor can operate on. And handler is the wrapper
|
||||
// of the service method implementation. It is the responsibility of the interceptor to invoke handler
|
||||
// to complete the RPC.
|
||||
type UnaryServerInterceptor func(ctx context.Context, req interface{}, info *UnaryServerInfo, handler UnaryHandler) (resp interface{}, err error)
|
||||
|
||||
// StreamServerInfo consists of various information about a streaming RPC on
|
||||
// server side. All per-rpc information may be mutated by the interceptor.
|
||||
type StreamServerInfo struct {
|
||||
// FullMethod is the full RPC method string, i.e., /package.service/method.
|
||||
FullMethod string
|
||||
// IsClientStream indicates whether the RPC is a client streaming RPC.
|
||||
IsClientStream bool
|
||||
// IsServerStream indicates whether the RPC is a server streaming RPC.
|
||||
IsServerStream bool
|
||||
}
|
||||
|
||||
// StreamServerInterceptor provides a hook to intercept the execution of a streaming RPC on the server.
|
||||
// info contains all the information of this RPC the interceptor can operate on. And handler is the
|
||||
// service method implementation. It is the responsibility of the interceptor to invoke handler to
|
||||
// complete the RPC.
|
||||
type StreamServerInterceptor func(srv interface{}, ss ServerStream, info *StreamServerInfo, handler StreamHandler) error
|
180
vendor/google.golang.org/grpc/metadata/metadata.go
generated
vendored
Normal file
180
vendor/google.golang.org/grpc/metadata/metadata.go
generated
vendored
Normal file
|
@ -0,0 +1,180 @@
|
|||
/*
|
||||
*
|
||||
* 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 metadata define the structure of the metadata supported by gRPC library.
|
||||
// Please refer to https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md
|
||||
// for more information about custom-metadata.
|
||||
package metadata // import "google.golang.org/grpc/metadata"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// DecodeKeyValue returns k, v, nil. It is deprecated and should not be used.
|
||||
func DecodeKeyValue(k, v string) (string, string, error) {
|
||||
return k, v, nil
|
||||
}
|
||||
|
||||
// MD is a mapping from metadata keys to values. Users should use the following
|
||||
// two convenience functions New and Pairs to generate MD.
|
||||
type MD map[string][]string
|
||||
|
||||
// New creates an MD from a given key-value map.
|
||||
//
|
||||
// Only the following ASCII characters are allowed in keys:
|
||||
// - digits: 0-9
|
||||
// - uppercase letters: A-Z (normalized to lower)
|
||||
// - lowercase letters: a-z
|
||||
// - special characters: -_.
|
||||
// Uppercase letters are automatically converted to lowercase.
|
||||
//
|
||||
// Keys beginning with "grpc-" are reserved for grpc-internal use only and may
|
||||
// result in errors if set in metadata.
|
||||
func New(m map[string]string) MD {
|
||||
md := MD{}
|
||||
for k, val := range m {
|
||||
key := strings.ToLower(k)
|
||||
md[key] = append(md[key], val)
|
||||
}
|
||||
return md
|
||||
}
|
||||
|
||||
// Pairs returns an MD formed by the mapping of key, value ...
|
||||
// Pairs panics if len(kv) is odd.
|
||||
//
|
||||
// Only the following ASCII characters are allowed in keys:
|
||||
// - digits: 0-9
|
||||
// - uppercase letters: A-Z (normalized to lower)
|
||||
// - lowercase letters: a-z
|
||||
// - special characters: -_.
|
||||
// Uppercase letters are automatically converted to lowercase.
|
||||
//
|
||||
// Keys beginning with "grpc-" are reserved for grpc-internal use only and may
|
||||
// result in errors if set in metadata.
|
||||
func Pairs(kv ...string) MD {
|
||||
if len(kv)%2 == 1 {
|
||||
panic(fmt.Sprintf("metadata: Pairs got the odd number of input pairs for metadata: %d", len(kv)))
|
||||
}
|
||||
md := MD{}
|
||||
var key string
|
||||
for i, s := range kv {
|
||||
if i%2 == 0 {
|
||||
key = strings.ToLower(s)
|
||||
continue
|
||||
}
|
||||
md[key] = append(md[key], s)
|
||||
}
|
||||
return md
|
||||
}
|
||||
|
||||
// Len returns the number of items in md.
|
||||
func (md MD) Len() int {
|
||||
return len(md)
|
||||
}
|
||||
|
||||
// Copy returns a copy of md.
|
||||
func (md MD) Copy() MD {
|
||||
return Join(md)
|
||||
}
|
||||
|
||||
// Join joins any number of mds into a single MD.
|
||||
// The order of values for each key is determined by the order in which
|
||||
// the mds containing those values are presented to Join.
|
||||
func Join(mds ...MD) MD {
|
||||
out := MD{}
|
||||
for _, md := range mds {
|
||||
for k, v := range md {
|
||||
out[k] = append(out[k], v...)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
type mdIncomingKey struct{}
|
||||
type mdOutgoingKey struct{}
|
||||
|
||||
// NewIncomingContext creates a new context with incoming md attached.
|
||||
func NewIncomingContext(ctx context.Context, md MD) context.Context {
|
||||
return context.WithValue(ctx, mdIncomingKey{}, md)
|
||||
}
|
||||
|
||||
// NewOutgoingContext creates a new context with outgoing md attached. If used
|
||||
// in conjunction with AppendToOutgoingContext, NewOutgoingContext will
|
||||
// overwrite any previously-appended metadata.
|
||||
func NewOutgoingContext(ctx context.Context, md MD) context.Context {
|
||||
return context.WithValue(ctx, mdOutgoingKey{}, rawMD{md: md})
|
||||
}
|
||||
|
||||
// AppendToOutgoingContext returns a new context with the provided kv merged
|
||||
// with any existing metadata in the context. Please refer to the
|
||||
// documentation of Pairs for a description of kv.
|
||||
func AppendToOutgoingContext(ctx context.Context, kv ...string) context.Context {
|
||||
if len(kv)%2 == 1 {
|
||||
panic(fmt.Sprintf("metadata: AppendToOutgoingContext got an odd number of input pairs for metadata: %d", len(kv)))
|
||||
}
|
||||
md, _ := ctx.Value(mdOutgoingKey{}).(rawMD)
|
||||
return context.WithValue(ctx, mdOutgoingKey{}, rawMD{md: md.md, added: append(md.added, kv)})
|
||||
}
|
||||
|
||||
// FromIncomingContext 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 FromIncomingContext(ctx context.Context) (md MD, ok bool) {
|
||||
md, ok = ctx.Value(mdIncomingKey{}).(MD)
|
||||
return
|
||||
}
|
||||
|
||||
// FromOutgoingContextRaw returns the un-merged, intermediary contents
|
||||
// of rawMD. Remember to perform strings.ToLower on the keys. The returned
|
||||
// MD should not be modified. Writing to it may cause races. Modification
|
||||
// should be made to copies of the returned MD.
|
||||
//
|
||||
// This is intended for gRPC-internal use ONLY.
|
||||
func FromOutgoingContextRaw(ctx context.Context) (MD, [][]string, bool) {
|
||||
raw, ok := ctx.Value(mdOutgoingKey{}).(rawMD)
|
||||
if !ok {
|
||||
return nil, nil, false
|
||||
}
|
||||
|
||||
return raw.md, raw.added, true
|
||||
}
|
||||
|
||||
// FromOutgoingContext returns the outgoing metadata in ctx if it exists. The
|
||||
// returned MD should not be modified. Writing to it may cause races.
|
||||
// Modification should be made to the copies of the returned MD.
|
||||
func FromOutgoingContext(ctx context.Context) (MD, bool) {
|
||||
raw, ok := ctx.Value(mdOutgoingKey{}).(rawMD)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
mds := make([]MD, 0, len(raw.added)+1)
|
||||
mds = append(mds, raw.md)
|
||||
for _, vv := range raw.added {
|
||||
mds = append(mds, Pairs(vv...))
|
||||
}
|
||||
return Join(mds...), ok
|
||||
}
|
||||
|
||||
type rawMD struct {
|
||||
md MD
|
||||
added [][]string
|
||||
}
|
125
vendor/google.golang.org/grpc/metadata/metadata_test.go
generated
vendored
Normal file
125
vendor/google.golang.org/grpc/metadata/metadata_test.go
generated
vendored
Normal file
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
*
|
||||
* 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 metadata
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
func TestPairsMD(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
// input
|
||||
kv []string
|
||||
// output
|
||||
md MD
|
||||
}{
|
||||
{[]string{}, MD{}},
|
||||
{[]string{"k1", "v1", "k1", "v2"}, MD{"k1": []string{"v1", "v2"}}},
|
||||
} {
|
||||
md := Pairs(test.kv...)
|
||||
if !reflect.DeepEqual(md, test.md) {
|
||||
t.Fatalf("Pairs(%v) = %v, want %v", test.kv, md, test.md)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCopy(t *testing.T) {
|
||||
const key, val = "key", "val"
|
||||
orig := Pairs(key, val)
|
||||
copy := orig.Copy()
|
||||
if !reflect.DeepEqual(orig, copy) {
|
||||
t.Errorf("copied value not equal to the original, got %v, want %v", copy, orig)
|
||||
}
|
||||
orig[key][0] = "foo"
|
||||
if v := copy[key][0]; v != val {
|
||||
t.Errorf("change in original should not affect copy, got %q, want %q", v, val)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJoin(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
mds []MD
|
||||
want MD
|
||||
}{
|
||||
{[]MD{}, MD{}},
|
||||
{[]MD{Pairs("foo", "bar")}, Pairs("foo", "bar")},
|
||||
{[]MD{Pairs("foo", "bar"), Pairs("foo", "baz")}, Pairs("foo", "bar", "foo", "baz")},
|
||||
{[]MD{Pairs("foo", "bar"), Pairs("foo", "baz"), Pairs("zip", "zap")}, Pairs("foo", "bar", "foo", "baz", "zip", "zap")},
|
||||
} {
|
||||
md := Join(test.mds...)
|
||||
if !reflect.DeepEqual(md, test.want) {
|
||||
t.Errorf("context's metadata is %v, want %v", md, test.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppendToOutgoingContext(t *testing.T) {
|
||||
// Pre-existing metadata
|
||||
ctx := NewOutgoingContext(context.Background(), Pairs("k1", "v1", "k2", "v2"))
|
||||
ctx = AppendToOutgoingContext(ctx, "k1", "v3")
|
||||
ctx = AppendToOutgoingContext(ctx, "k1", "v4")
|
||||
md, ok := FromOutgoingContext(ctx)
|
||||
if !ok {
|
||||
t.Errorf("Expected MD to exist in ctx, but got none")
|
||||
}
|
||||
want := Pairs("k1", "v1", "k1", "v3", "k1", "v4", "k2", "v2")
|
||||
if !reflect.DeepEqual(md, want) {
|
||||
t.Errorf("context's metadata is %v, want %v", md, want)
|
||||
}
|
||||
|
||||
// No existing metadata
|
||||
ctx = AppendToOutgoingContext(context.Background(), "k1", "v1")
|
||||
md, ok = FromOutgoingContext(ctx)
|
||||
if !ok {
|
||||
t.Errorf("Expected MD to exist in ctx, but got none")
|
||||
}
|
||||
want = Pairs("k1", "v1")
|
||||
if !reflect.DeepEqual(md, want) {
|
||||
t.Errorf("context's metadata is %v, want %v", md, want)
|
||||
}
|
||||
}
|
||||
|
||||
// Old/slow approach to adding metadata to context
|
||||
func Benchmark_AddingMetadata_ContextManipulationApproach(b *testing.B) {
|
||||
for n := 0; n < b.N; n++ {
|
||||
ctx := context.Background()
|
||||
md, _ := FromOutgoingContext(ctx)
|
||||
NewOutgoingContext(ctx, Join(Pairs("k1", "v1", "k2", "v2"), md))
|
||||
}
|
||||
}
|
||||
|
||||
// Newer/faster approach to adding metadata to context
|
||||
func BenchmarkAppendToOutgoingContext(b *testing.B) {
|
||||
for n := 0; n < b.N; n++ {
|
||||
AppendToOutgoingContext(context.Background(), "k1", "v1", "k2", "v2")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkFromOutgoingContext(b *testing.B) {
|
||||
ctx := context.Background()
|
||||
ctx = NewOutgoingContext(ctx, MD{"k3": {"v3", "v4"}})
|
||||
ctx = AppendToOutgoingContext(ctx, "k1", "v1", "k2", "v2")
|
||||
|
||||
for n := 0; n < b.N; n++ {
|
||||
FromOutgoingContext(ctx)
|
||||
}
|
||||
}
|
141
vendor/google.golang.org/grpc/picker_wrapper.go
generated
vendored
Normal file
141
vendor/google.golang.org/grpc/picker_wrapper.go
generated
vendored
Normal file
|
@ -0,0 +1,141 @@
|
|||
/*
|
||||
*
|
||||
* 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"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc/balancer"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/grpclog"
|
||||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/grpc/transport"
|
||||
)
|
||||
|
||||
// pickerWrapper is a wrapper of balancer.Picker. It blocks on certain pick
|
||||
// actions and unblock when there's a picker update.
|
||||
type pickerWrapper struct {
|
||||
mu sync.Mutex
|
||||
done bool
|
||||
blockingCh chan struct{}
|
||||
picker balancer.Picker
|
||||
}
|
||||
|
||||
func newPickerWrapper() *pickerWrapper {
|
||||
bp := &pickerWrapper{blockingCh: make(chan struct{})}
|
||||
return bp
|
||||
}
|
||||
|
||||
// updatePicker is called by UpdateBalancerState. It unblocks all blocked pick.
|
||||
func (bp *pickerWrapper) updatePicker(p balancer.Picker) {
|
||||
bp.mu.Lock()
|
||||
if bp.done {
|
||||
bp.mu.Unlock()
|
||||
return
|
||||
}
|
||||
bp.picker = p
|
||||
// bp.blockingCh should never be nil.
|
||||
close(bp.blockingCh)
|
||||
bp.blockingCh = make(chan struct{})
|
||||
bp.mu.Unlock()
|
||||
}
|
||||
|
||||
// pick returns the transport that will be used for the RPC.
|
||||
// It may block in the following cases:
|
||||
// - there's no picker
|
||||
// - the current picker returns ErrNoSubConnAvailable
|
||||
// - the current picker returns other errors and failfast is false.
|
||||
// - the subConn returned by the current picker is not READY
|
||||
// When one of these situations happens, pick blocks until the picker gets updated.
|
||||
func (bp *pickerWrapper) pick(ctx context.Context, failfast bool, opts balancer.PickOptions) (transport.ClientTransport, func(balancer.DoneInfo), error) {
|
||||
var (
|
||||
p balancer.Picker
|
||||
ch chan struct{}
|
||||
)
|
||||
|
||||
for {
|
||||
bp.mu.Lock()
|
||||
if bp.done {
|
||||
bp.mu.Unlock()
|
||||
return nil, nil, ErrClientConnClosing
|
||||
}
|
||||
|
||||
if bp.picker == nil {
|
||||
ch = bp.blockingCh
|
||||
}
|
||||
if ch == bp.blockingCh {
|
||||
// This could happen when either:
|
||||
// - bp.picker is nil (the previous if condition), or
|
||||
// - has called pick on the current picker.
|
||||
bp.mu.Unlock()
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, nil, ctx.Err()
|
||||
case <-ch:
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
ch = bp.blockingCh
|
||||
p = bp.picker
|
||||
bp.mu.Unlock()
|
||||
|
||||
subConn, done, err := p.Pick(ctx, opts)
|
||||
|
||||
if err != nil {
|
||||
switch err {
|
||||
case balancer.ErrNoSubConnAvailable:
|
||||
continue
|
||||
case balancer.ErrTransientFailure:
|
||||
if !failfast {
|
||||
continue
|
||||
}
|
||||
return nil, nil, status.Errorf(codes.Unavailable, "%v", err)
|
||||
default:
|
||||
// err is some other error.
|
||||
return nil, nil, toRPCErr(err)
|
||||
}
|
||||
}
|
||||
|
||||
acw, ok := subConn.(*acBalancerWrapper)
|
||||
if !ok {
|
||||
grpclog.Infof("subconn returned from pick is not *acBalancerWrapper")
|
||||
continue
|
||||
}
|
||||
if t, ok := acw.getAddrConn().getReadyTransport(); ok {
|
||||
return t, done, nil
|
||||
}
|
||||
grpclog.Infof("blockingPicker: the picked transport is not ready, loop back to repick")
|
||||
// If ok == false, ac.state is not READY.
|
||||
// A valid picker always returns READY subConn. This means the state of ac
|
||||
// just changed, and picker will be updated shortly.
|
||||
// continue back to the beginning of the for loop to repick.
|
||||
}
|
||||
}
|
||||
|
||||
func (bp *pickerWrapper) close() {
|
||||
bp.mu.Lock()
|
||||
defer bp.mu.Unlock()
|
||||
if bp.done {
|
||||
return
|
||||
}
|
||||
bp.done = true
|
||||
close(bp.blockingCh)
|
||||
}
|
160
vendor/google.golang.org/grpc/picker_wrapper_test.go
generated
vendored
Normal file
160
vendor/google.golang.org/grpc/picker_wrapper_test.go
generated
vendored
Normal file
|
@ -0,0 +1,160 @@
|
|||
/*
|
||||
*
|
||||
* 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 (
|
||||
"fmt"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc/balancer"
|
||||
"google.golang.org/grpc/connectivity"
|
||||
_ "google.golang.org/grpc/grpclog/glogger"
|
||||
"google.golang.org/grpc/test/leakcheck"
|
||||
"google.golang.org/grpc/transport"
|
||||
)
|
||||
|
||||
const goroutineCount = 5
|
||||
|
||||
var (
|
||||
testT = &testTransport{}
|
||||
testSC = &acBalancerWrapper{ac: &addrConn{
|
||||
state: connectivity.Ready,
|
||||
transport: testT,
|
||||
}}
|
||||
testSCNotReady = &acBalancerWrapper{ac: &addrConn{
|
||||
state: connectivity.TransientFailure,
|
||||
}}
|
||||
)
|
||||
|
||||
type testTransport struct {
|
||||
transport.ClientTransport
|
||||
}
|
||||
|
||||
type testingPicker struct {
|
||||
err error
|
||||
sc balancer.SubConn
|
||||
maxCalled int64
|
||||
}
|
||||
|
||||
func (p *testingPicker) Pick(ctx context.Context, opts balancer.PickOptions) (balancer.SubConn, func(balancer.DoneInfo), error) {
|
||||
if atomic.AddInt64(&p.maxCalled, -1) < 0 {
|
||||
return nil, nil, fmt.Errorf("Pick called to many times (> goroutineCount)")
|
||||
}
|
||||
if p.err != nil {
|
||||
return nil, nil, p.err
|
||||
}
|
||||
return p.sc, nil, nil
|
||||
}
|
||||
|
||||
func TestBlockingPickTimeout(t *testing.T) {
|
||||
defer leakcheck.Check(t)
|
||||
bp := newPickerWrapper()
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond)
|
||||
defer cancel()
|
||||
if _, _, err := bp.pick(ctx, true, balancer.PickOptions{}); err != context.DeadlineExceeded {
|
||||
t.Errorf("bp.pick returned error %v, want DeadlineExceeded", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBlockingPick(t *testing.T) {
|
||||
defer leakcheck.Check(t)
|
||||
bp := newPickerWrapper()
|
||||
// All goroutines should block because picker is nil in bp.
|
||||
var finishedCount uint64
|
||||
for i := goroutineCount; i > 0; i-- {
|
||||
go func() {
|
||||
if tr, _, err := bp.pick(context.Background(), true, balancer.PickOptions{}); err != nil || tr != testT {
|
||||
t.Errorf("bp.pick returned non-nil error: %v", err)
|
||||
}
|
||||
atomic.AddUint64(&finishedCount, 1)
|
||||
}()
|
||||
}
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
if c := atomic.LoadUint64(&finishedCount); c != 0 {
|
||||
t.Errorf("finished goroutines count: %v, want 0", c)
|
||||
}
|
||||
bp.updatePicker(&testingPicker{sc: testSC, maxCalled: goroutineCount})
|
||||
}
|
||||
|
||||
func TestBlockingPickNoSubAvailable(t *testing.T) {
|
||||
defer leakcheck.Check(t)
|
||||
bp := newPickerWrapper()
|
||||
var finishedCount uint64
|
||||
bp.updatePicker(&testingPicker{err: balancer.ErrNoSubConnAvailable, maxCalled: goroutineCount})
|
||||
// All goroutines should block because picker returns no sc available.
|
||||
for i := goroutineCount; i > 0; i-- {
|
||||
go func() {
|
||||
if tr, _, err := bp.pick(context.Background(), true, balancer.PickOptions{}); err != nil || tr != testT {
|
||||
t.Errorf("bp.pick returned non-nil error: %v", err)
|
||||
}
|
||||
atomic.AddUint64(&finishedCount, 1)
|
||||
}()
|
||||
}
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
if c := atomic.LoadUint64(&finishedCount); c != 0 {
|
||||
t.Errorf("finished goroutines count: %v, want 0", c)
|
||||
}
|
||||
bp.updatePicker(&testingPicker{sc: testSC, maxCalled: goroutineCount})
|
||||
}
|
||||
|
||||
func TestBlockingPickTransientWaitforready(t *testing.T) {
|
||||
defer leakcheck.Check(t)
|
||||
bp := newPickerWrapper()
|
||||
bp.updatePicker(&testingPicker{err: balancer.ErrTransientFailure, maxCalled: goroutineCount})
|
||||
var finishedCount uint64
|
||||
// All goroutines should block because picker returns transientFailure and
|
||||
// picks are not failfast.
|
||||
for i := goroutineCount; i > 0; i-- {
|
||||
go func() {
|
||||
if tr, _, err := bp.pick(context.Background(), false, balancer.PickOptions{}); err != nil || tr != testT {
|
||||
t.Errorf("bp.pick returned non-nil error: %v", err)
|
||||
}
|
||||
atomic.AddUint64(&finishedCount, 1)
|
||||
}()
|
||||
}
|
||||
time.Sleep(time.Millisecond)
|
||||
if c := atomic.LoadUint64(&finishedCount); c != 0 {
|
||||
t.Errorf("finished goroutines count: %v, want 0", c)
|
||||
}
|
||||
bp.updatePicker(&testingPicker{sc: testSC, maxCalled: goroutineCount})
|
||||
}
|
||||
|
||||
func TestBlockingPickSCNotReady(t *testing.T) {
|
||||
defer leakcheck.Check(t)
|
||||
bp := newPickerWrapper()
|
||||
bp.updatePicker(&testingPicker{sc: testSCNotReady, maxCalled: goroutineCount})
|
||||
var finishedCount uint64
|
||||
// All goroutines should block because sc is not ready.
|
||||
for i := goroutineCount; i > 0; i-- {
|
||||
go func() {
|
||||
if tr, _, err := bp.pick(context.Background(), true, balancer.PickOptions{}); err != nil || tr != testT {
|
||||
t.Errorf("bp.pick returned non-nil error: %v", err)
|
||||
}
|
||||
atomic.AddUint64(&finishedCount, 1)
|
||||
}()
|
||||
}
|
||||
time.Sleep(time.Millisecond)
|
||||
if c := atomic.LoadUint64(&finishedCount); c != 0 {
|
||||
t.Errorf("finished goroutines count: %v, want 0", c)
|
||||
}
|
||||
bp.updatePicker(&testingPicker{sc: testSC, maxCalled: goroutineCount})
|
||||
}
|
108
vendor/google.golang.org/grpc/pickfirst.go
generated
vendored
Normal file
108
vendor/google.golang.org/grpc/pickfirst.go
generated
vendored
Normal file
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
*
|
||||
* 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 (
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc/balancer"
|
||||
"google.golang.org/grpc/connectivity"
|
||||
"google.golang.org/grpc/grpclog"
|
||||
"google.golang.org/grpc/resolver"
|
||||
)
|
||||
|
||||
// PickFirstBalancerName is the name of the pick_first balancer.
|
||||
const PickFirstBalancerName = "pick_first"
|
||||
|
||||
func newPickfirstBuilder() balancer.Builder {
|
||||
return &pickfirstBuilder{}
|
||||
}
|
||||
|
||||
type pickfirstBuilder struct{}
|
||||
|
||||
func (*pickfirstBuilder) Build(cc balancer.ClientConn, opt balancer.BuildOptions) balancer.Balancer {
|
||||
return &pickfirstBalancer{cc: cc}
|
||||
}
|
||||
|
||||
func (*pickfirstBuilder) Name() string {
|
||||
return PickFirstBalancerName
|
||||
}
|
||||
|
||||
type pickfirstBalancer struct {
|
||||
cc balancer.ClientConn
|
||||
sc balancer.SubConn
|
||||
}
|
||||
|
||||
func (b *pickfirstBalancer) HandleResolvedAddrs(addrs []resolver.Address, err error) {
|
||||
if err != nil {
|
||||
grpclog.Infof("pickfirstBalancer: HandleResolvedAddrs called with error %v", err)
|
||||
return
|
||||
}
|
||||
if b.sc == nil {
|
||||
b.sc, err = b.cc.NewSubConn(addrs, balancer.NewSubConnOptions{})
|
||||
if err != nil {
|
||||
grpclog.Errorf("pickfirstBalancer: failed to NewSubConn: %v", err)
|
||||
return
|
||||
}
|
||||
b.cc.UpdateBalancerState(connectivity.Idle, &picker{sc: b.sc})
|
||||
b.sc.Connect()
|
||||
} else {
|
||||
b.sc.UpdateAddresses(addrs)
|
||||
b.sc.Connect()
|
||||
}
|
||||
}
|
||||
|
||||
func (b *pickfirstBalancer) HandleSubConnStateChange(sc balancer.SubConn, s connectivity.State) {
|
||||
grpclog.Infof("pickfirstBalancer: HandleSubConnStateChange: %p, %v", sc, s)
|
||||
if b.sc != sc {
|
||||
grpclog.Infof("pickfirstBalancer: ignored state change because sc is not recognized")
|
||||
return
|
||||
}
|
||||
if s == connectivity.Shutdown {
|
||||
b.sc = nil
|
||||
return
|
||||
}
|
||||
|
||||
switch s {
|
||||
case connectivity.Ready, connectivity.Idle:
|
||||
b.cc.UpdateBalancerState(s, &picker{sc: sc})
|
||||
case connectivity.Connecting:
|
||||
b.cc.UpdateBalancerState(s, &picker{err: balancer.ErrNoSubConnAvailable})
|
||||
case connectivity.TransientFailure:
|
||||
b.cc.UpdateBalancerState(s, &picker{err: balancer.ErrTransientFailure})
|
||||
}
|
||||
}
|
||||
|
||||
func (b *pickfirstBalancer) Close() {
|
||||
}
|
||||
|
||||
type picker struct {
|
||||
err error
|
||||
sc balancer.SubConn
|
||||
}
|
||||
|
||||
func (p *picker) Pick(ctx context.Context, opts balancer.PickOptions) (balancer.SubConn, func(balancer.DoneInfo), error) {
|
||||
if p.err != nil {
|
||||
return nil, nil, p.err
|
||||
}
|
||||
return p.sc, nil, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
balancer.Register(newPickfirstBuilder())
|
||||
}
|
360
vendor/google.golang.org/grpc/pickfirst_test.go
generated
vendored
Normal file
360
vendor/google.golang.org/grpc/pickfirst_test.go
generated
vendored
Normal file
|
@ -0,0 +1,360 @@
|
|||
/*
|
||||
*
|
||||
* 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 (
|
||||
"math"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/resolver"
|
||||
"google.golang.org/grpc/resolver/manual"
|
||||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/grpc/test/leakcheck"
|
||||
)
|
||||
|
||||
func errorDesc(err error) string {
|
||||
if s, ok := status.FromError(err); ok {
|
||||
return s.Message()
|
||||
}
|
||||
return err.Error()
|
||||
}
|
||||
|
||||
func TestOneBackendPickfirst(t *testing.T) {
|
||||
defer leakcheck.Check(t)
|
||||
r, rcleanup := manual.GenerateAndRegisterManualResolver()
|
||||
defer rcleanup()
|
||||
|
||||
numServers := 1
|
||||
servers, _, scleanup := startServers(t, numServers, math.MaxInt32)
|
||||
defer scleanup()
|
||||
|
||||
cc, err := Dial(r.Scheme()+":///test.server", WithInsecure(), WithCodec(testCodec{}))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to dial: %v", err)
|
||||
}
|
||||
defer cc.Close()
|
||||
// The first RPC should fail because there's no address.
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond)
|
||||
defer cancel()
|
||||
req := "port"
|
||||
var reply string
|
||||
if err := Invoke(ctx, "/foo/bar", &req, &reply, cc); err == nil || status.Code(err) != codes.DeadlineExceeded {
|
||||
t.Fatalf("EmptyCall() = _, %v, want _, DeadlineExceeded", err)
|
||||
}
|
||||
|
||||
r.NewAddress([]resolver.Address{{Addr: servers[0].addr}})
|
||||
// The second RPC should succeed.
|
||||
for i := 0; i < 1000; i++ {
|
||||
if err = Invoke(context.Background(), "/foo/bar", &req, &reply, cc); err != nil && errorDesc(err) == servers[0].port {
|
||||
return
|
||||
}
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
t.Fatalf("EmptyCall() = _, %v, want _, %v", err, servers[0].port)
|
||||
}
|
||||
|
||||
func TestBackendsPickfirst(t *testing.T) {
|
||||
defer leakcheck.Check(t)
|
||||
r, rcleanup := manual.GenerateAndRegisterManualResolver()
|
||||
defer rcleanup()
|
||||
|
||||
numServers := 2
|
||||
servers, _, scleanup := startServers(t, numServers, math.MaxInt32)
|
||||
defer scleanup()
|
||||
|
||||
cc, err := Dial(r.Scheme()+":///test.server", WithInsecure(), WithCodec(testCodec{}))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to dial: %v", err)
|
||||
}
|
||||
defer cc.Close()
|
||||
// The first RPC should fail because there's no address.
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond)
|
||||
defer cancel()
|
||||
req := "port"
|
||||
var reply string
|
||||
if err := Invoke(ctx, "/foo/bar", &req, &reply, cc); err == nil || status.Code(err) != codes.DeadlineExceeded {
|
||||
t.Fatalf("EmptyCall() = _, %v, want _, DeadlineExceeded", err)
|
||||
}
|
||||
|
||||
r.NewAddress([]resolver.Address{{Addr: servers[0].addr}, {Addr: servers[1].addr}})
|
||||
// The second RPC should succeed with the first server.
|
||||
for i := 0; i < 1000; i++ {
|
||||
if err = Invoke(context.Background(), "/foo/bar", &req, &reply, cc); err != nil && errorDesc(err) == servers[0].port {
|
||||
return
|
||||
}
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
t.Fatalf("EmptyCall() = _, %v, want _, %v", err, servers[0].port)
|
||||
}
|
||||
|
||||
func TestNewAddressWhileBlockingPickfirst(t *testing.T) {
|
||||
defer leakcheck.Check(t)
|
||||
r, rcleanup := manual.GenerateAndRegisterManualResolver()
|
||||
defer rcleanup()
|
||||
|
||||
numServers := 1
|
||||
servers, _, scleanup := startServers(t, numServers, math.MaxInt32)
|
||||
defer scleanup()
|
||||
|
||||
cc, err := Dial(r.Scheme()+":///test.server", WithInsecure(), WithCodec(testCodec{}))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to dial: %v", err)
|
||||
}
|
||||
defer cc.Close()
|
||||
// The first RPC should fail because there's no address.
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond)
|
||||
defer cancel()
|
||||
req := "port"
|
||||
var reply string
|
||||
if err := Invoke(ctx, "/foo/bar", &req, &reply, cc); err == nil || status.Code(err) != codes.DeadlineExceeded {
|
||||
t.Fatalf("EmptyCall() = _, %v, want _, DeadlineExceeded", err)
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
for i := 0; i < 3; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
// This RPC blocks until NewAddress is called.
|
||||
Invoke(context.Background(), "/foo/bar", &req, &reply, cc)
|
||||
}()
|
||||
}
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
r.NewAddress([]resolver.Address{{Addr: servers[0].addr}})
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func TestCloseWithPendingRPCPickfirst(t *testing.T) {
|
||||
defer leakcheck.Check(t)
|
||||
r, rcleanup := manual.GenerateAndRegisterManualResolver()
|
||||
defer rcleanup()
|
||||
|
||||
numServers := 1
|
||||
_, _, scleanup := startServers(t, numServers, math.MaxInt32)
|
||||
defer scleanup()
|
||||
|
||||
cc, err := Dial(r.Scheme()+":///test.server", WithInsecure(), WithCodec(testCodec{}))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to dial: %v", err)
|
||||
}
|
||||
defer cc.Close()
|
||||
// The first RPC should fail because there's no address.
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond)
|
||||
defer cancel()
|
||||
req := "port"
|
||||
var reply string
|
||||
if err := Invoke(ctx, "/foo/bar", &req, &reply, cc); err == nil || status.Code(err) != codes.DeadlineExceeded {
|
||||
t.Fatalf("EmptyCall() = _, %v, want _, DeadlineExceeded", err)
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
for i := 0; i < 3; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
// This RPC blocks until NewAddress is called.
|
||||
Invoke(context.Background(), "/foo/bar", &req, &reply, cc)
|
||||
}()
|
||||
}
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
cc.Close()
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func TestOneServerDownPickfirst(t *testing.T) {
|
||||
defer leakcheck.Check(t)
|
||||
r, rcleanup := manual.GenerateAndRegisterManualResolver()
|
||||
defer rcleanup()
|
||||
|
||||
numServers := 2
|
||||
servers, _, scleanup := startServers(t, numServers, math.MaxInt32)
|
||||
defer scleanup()
|
||||
|
||||
cc, err := Dial(r.Scheme()+":///test.server", WithInsecure(), WithCodec(testCodec{}), WithWaitForHandshake())
|
||||
if err != nil {
|
||||
t.Fatalf("failed to dial: %v", err)
|
||||
}
|
||||
defer cc.Close()
|
||||
// The first RPC should fail because there's no address.
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond)
|
||||
defer cancel()
|
||||
req := "port"
|
||||
var reply string
|
||||
if err := Invoke(ctx, "/foo/bar", &req, &reply, cc); err == nil || status.Code(err) != codes.DeadlineExceeded {
|
||||
t.Fatalf("EmptyCall() = _, %v, want _, DeadlineExceeded", err)
|
||||
}
|
||||
|
||||
r.NewAddress([]resolver.Address{{Addr: servers[0].addr}, {Addr: servers[1].addr}})
|
||||
// The second RPC should succeed with the first server.
|
||||
for i := 0; i < 1000; i++ {
|
||||
if err = Invoke(context.Background(), "/foo/bar", &req, &reply, cc); err != nil && errorDesc(err) == servers[0].port {
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
|
||||
servers[0].stop()
|
||||
for i := 0; i < 1000; i++ {
|
||||
if err = Invoke(context.Background(), "/foo/bar", &req, &reply, cc); err != nil && errorDesc(err) == servers[1].port {
|
||||
return
|
||||
}
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
t.Fatalf("EmptyCall() = _, %v, want _, %v", err, servers[0].port)
|
||||
}
|
||||
|
||||
func TestAllServersDownPickfirst(t *testing.T) {
|
||||
defer leakcheck.Check(t)
|
||||
r, rcleanup := manual.GenerateAndRegisterManualResolver()
|
||||
defer rcleanup()
|
||||
|
||||
numServers := 2
|
||||
servers, _, scleanup := startServers(t, numServers, math.MaxInt32)
|
||||
defer scleanup()
|
||||
|
||||
cc, err := Dial(r.Scheme()+":///test.server", WithInsecure(), WithCodec(testCodec{}), WithWaitForHandshake())
|
||||
if err != nil {
|
||||
t.Fatalf("failed to dial: %v", err)
|
||||
}
|
||||
defer cc.Close()
|
||||
// The first RPC should fail because there's no address.
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond)
|
||||
defer cancel()
|
||||
req := "port"
|
||||
var reply string
|
||||
if err := Invoke(ctx, "/foo/bar", &req, &reply, cc); err == nil || status.Code(err) != codes.DeadlineExceeded {
|
||||
t.Fatalf("EmptyCall() = _, %v, want _, DeadlineExceeded", err)
|
||||
}
|
||||
|
||||
r.NewAddress([]resolver.Address{{Addr: servers[0].addr}, {Addr: servers[1].addr}})
|
||||
// The second RPC should succeed with the first server.
|
||||
for i := 0; i < 1000; i++ {
|
||||
if err = Invoke(context.Background(), "/foo/bar", &req, &reply, cc); err != nil && errorDesc(err) == servers[0].port {
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
|
||||
for i := 0; i < numServers; i++ {
|
||||
servers[i].stop()
|
||||
}
|
||||
for i := 0; i < 1000; i++ {
|
||||
if err = Invoke(context.Background(), "/foo/bar", &req, &reply, cc); status.Code(err) == codes.Unavailable {
|
||||
return
|
||||
}
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
t.Fatalf("EmptyCall() = _, %v, want _, error with code unavailable", err)
|
||||
}
|
||||
|
||||
func TestAddressesRemovedPickfirst(t *testing.T) {
|
||||
defer leakcheck.Check(t)
|
||||
r, rcleanup := manual.GenerateAndRegisterManualResolver()
|
||||
defer rcleanup()
|
||||
|
||||
numServers := 3
|
||||
servers, _, scleanup := startServers(t, numServers, math.MaxInt32)
|
||||
defer scleanup()
|
||||
|
||||
cc, err := Dial(r.Scheme()+":///test.server", WithInsecure(), WithCodec(testCodec{}))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to dial: %v", err)
|
||||
}
|
||||
defer cc.Close()
|
||||
// The first RPC should fail because there's no address.
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond)
|
||||
defer cancel()
|
||||
req := "port"
|
||||
var reply string
|
||||
if err := Invoke(ctx, "/foo/bar", &req, &reply, cc); err == nil || status.Code(err) != codes.DeadlineExceeded {
|
||||
t.Fatalf("EmptyCall() = _, %v, want _, DeadlineExceeded", err)
|
||||
}
|
||||
|
||||
r.NewAddress([]resolver.Address{{Addr: servers[0].addr}, {Addr: servers[1].addr}, {Addr: servers[2].addr}})
|
||||
for i := 0; i < 1000; i++ {
|
||||
if err = Invoke(context.Background(), "/foo/bar", &req, &reply, cc); err != nil && errorDesc(err) == servers[0].port {
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
for i := 0; i < 20; i++ {
|
||||
if err := Invoke(context.Background(), "/foo/bar", &req, &reply, cc); err == nil || errorDesc(err) != servers[0].port {
|
||||
t.Fatalf("Index %d: Invoke(_, _, _, _, _) = %v, want %s", 0, err, servers[0].port)
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
|
||||
// Remove server[0].
|
||||
r.NewAddress([]resolver.Address{{Addr: servers[1].addr}, {Addr: servers[2].addr}})
|
||||
for i := 0; i < 1000; i++ {
|
||||
if err = Invoke(context.Background(), "/foo/bar", &req, &reply, cc); err != nil && errorDesc(err) == servers[1].port {
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
for i := 0; i < 20; i++ {
|
||||
if err := Invoke(context.Background(), "/foo/bar", &req, &reply, cc); err == nil || errorDesc(err) != servers[1].port {
|
||||
t.Fatalf("Index %d: Invoke(_, _, _, _, _) = %v, want %s", 1, err, servers[1].port)
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
|
||||
// Append server[0], nothing should change.
|
||||
r.NewAddress([]resolver.Address{{Addr: servers[1].addr}, {Addr: servers[2].addr}, {Addr: servers[0].addr}})
|
||||
for i := 0; i < 20; i++ {
|
||||
if err := Invoke(context.Background(), "/foo/bar", &req, &reply, cc); err == nil || errorDesc(err) != servers[1].port {
|
||||
t.Fatalf("Index %d: Invoke(_, _, _, _, _) = %v, want %s", 1, err, servers[1].port)
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
|
||||
// Remove server[1].
|
||||
r.NewAddress([]resolver.Address{{Addr: servers[2].addr}, {Addr: servers[0].addr}})
|
||||
for i := 0; i < 1000; i++ {
|
||||
if err = Invoke(context.Background(), "/foo/bar", &req, &reply, cc); err != nil && errorDesc(err) == servers[2].port {
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
for i := 0; i < 20; i++ {
|
||||
if err := Invoke(context.Background(), "/foo/bar", &req, &reply, cc); err == nil || errorDesc(err) != servers[2].port {
|
||||
t.Fatalf("Index %d: Invoke(_, _, _, _, _) = %v, want %s", 2, err, servers[2].port)
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
|
||||
// Remove server[2].
|
||||
r.NewAddress([]resolver.Address{{Addr: servers[0].addr}})
|
||||
for i := 0; i < 1000; i++ {
|
||||
if err = Invoke(context.Background(), "/foo/bar", &req, &reply, cc); err != nil && errorDesc(err) == servers[0].port {
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
for i := 0; i < 20; i++ {
|
||||
if err := Invoke(context.Background(), "/foo/bar", &req, &reply, cc); err == nil || errorDesc(err) != servers[0].port {
|
||||
t.Fatalf("Index %d: Invoke(_, _, _, _, _) = %v, want %s", 0, err, servers[0].port)
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
}
|
130
vendor/google.golang.org/grpc/proxy.go
generated
vendored
Normal file
130
vendor/google.golang.org/grpc/proxy.go
generated
vendored
Normal file
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
*
|
||||
* 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 (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
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.
|
||||
httpProxyFromEnvironment = http.ProxyFromEnvironment
|
||||
)
|
||||
|
||||
func mapAddress(ctx context.Context, address string) (string, error) {
|
||||
req := &http.Request{
|
||||
URL: &url.URL{
|
||||
Scheme: "https",
|
||||
Host: address,
|
||||
},
|
||||
}
|
||||
url, err := httpProxyFromEnvironment(req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if url == nil {
|
||||
return "", errDisabled
|
||||
}
|
||||
return url.Host, nil
|
||||
}
|
||||
|
||||
// To read a response from a net.Conn, http.ReadResponse() takes a bufio.Reader.
|
||||
// It's possible that this reader reads more than what's need for the response and stores
|
||||
// those bytes in the buffer.
|
||||
// bufConn wraps the original net.Conn and the bufio.Reader to make sure we don't lose the
|
||||
// bytes in the buffer.
|
||||
type bufConn struct {
|
||||
net.Conn
|
||||
r io.Reader
|
||||
}
|
||||
|
||||
func (c *bufConn) Read(b []byte) (int, error) {
|
||||
return c.r.Read(b)
|
||||
}
|
||||
|
||||
func doHTTPConnectHandshake(ctx context.Context, conn net.Conn, addr string) (_ net.Conn, err error) {
|
||||
defer func() {
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
req := (&http.Request{
|
||||
Method: http.MethodConnect,
|
||||
URL: &url.URL{Host: addr},
|
||||
Header: map[string][]string{"User-Agent": {grpcUA}},
|
||||
})
|
||||
|
||||
if err := sendHTTPRequest(ctx, req, conn); err != nil {
|
||||
return nil, fmt.Errorf("failed to write the HTTP request: %v", err)
|
||||
}
|
||||
|
||||
r := bufio.NewReader(conn)
|
||||
resp, err := http.ReadResponse(r, req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("reading server HTTP response: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
dump, err := httputil.DumpResponse(resp, true)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to do connect handshake, status code: %s", resp.Status)
|
||||
}
|
||||
return nil, fmt.Errorf("failed to do connect handshake, response: %q", dump)
|
||||
}
|
||||
|
||||
return &bufConn{Conn: conn, r: r}, nil
|
||||
}
|
||||
|
||||
// newProxyDialer returns a dialer that connects to proxy first if necessary.
|
||||
// The returned dialer checks if a proxy is necessary, dial to the proxy with the
|
||||
// provided dialer, does HTTP CONNECT handshake and returns the connection.
|
||||
func newProxyDialer(dialer func(context.Context, string) (net.Conn, error)) func(context.Context, string) (net.Conn, error) {
|
||||
return func(ctx context.Context, addr string) (conn net.Conn, err error) {
|
||||
var skipHandshake bool
|
||||
newAddr, err := mapAddress(ctx, addr)
|
||||
if err != nil {
|
||||
if err != errDisabled {
|
||||
return nil, err
|
||||
}
|
||||
skipHandshake = true
|
||||
newAddr = addr
|
||||
}
|
||||
|
||||
conn, err = dialer(ctx, newAddr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if !skipHandshake {
|
||||
conn, err = doHTTPConnectHandshake(ctx, conn, addr)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
182
vendor/google.golang.org/grpc/proxy_test.go
generated
vendored
Normal file
182
vendor/google.golang.org/grpc/proxy_test.go
generated
vendored
Normal file
|
@ -0,0 +1,182 @@
|
|||
// +build !race
|
||||
|
||||
/*
|
||||
*
|
||||
* 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 (
|
||||
"bufio"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc/test/leakcheck"
|
||||
)
|
||||
|
||||
const (
|
||||
envTestAddr = "1.2.3.4:8080"
|
||||
envProxyAddr = "2.3.4.5:7687"
|
||||
)
|
||||
|
||||
// overwriteAndRestore overwrite function httpProxyFromEnvironment and
|
||||
// returns a function to restore the default values.
|
||||
func overwrite(hpfe func(req *http.Request) (*url.URL, error)) func() {
|
||||
backHPFE := httpProxyFromEnvironment
|
||||
httpProxyFromEnvironment = hpfe
|
||||
return func() {
|
||||
httpProxyFromEnvironment = backHPFE
|
||||
}
|
||||
}
|
||||
|
||||
type proxyServer struct {
|
||||
t *testing.T
|
||||
lis net.Listener
|
||||
in net.Conn
|
||||
out net.Conn
|
||||
}
|
||||
|
||||
func (p *proxyServer) run() {
|
||||
in, err := p.lis.Accept()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
p.in = in
|
||||
|
||||
req, err := http.ReadRequest(bufio.NewReader(in))
|
||||
if err != nil {
|
||||
p.t.Errorf("failed to read CONNECT req: %v", err)
|
||||
return
|
||||
}
|
||||
if req.Method != http.MethodConnect || req.UserAgent() != grpcUA {
|
||||
resp := http.Response{StatusCode: http.StatusMethodNotAllowed}
|
||||
resp.Write(p.in)
|
||||
p.in.Close()
|
||||
p.t.Errorf("get wrong CONNECT req: %+v", req)
|
||||
return
|
||||
}
|
||||
|
||||
out, err := net.Dial("tcp", req.URL.Host)
|
||||
if err != nil {
|
||||
p.t.Errorf("failed to dial to server: %v", err)
|
||||
return
|
||||
}
|
||||
resp := http.Response{StatusCode: http.StatusOK, Proto: "HTTP/1.0"}
|
||||
resp.Write(p.in)
|
||||
p.out = out
|
||||
go io.Copy(p.in, p.out)
|
||||
go io.Copy(p.out, p.in)
|
||||
}
|
||||
|
||||
func (p *proxyServer) stop() {
|
||||
p.lis.Close()
|
||||
if p.in != nil {
|
||||
p.in.Close()
|
||||
}
|
||||
if p.out != nil {
|
||||
p.out.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func TestHTTPConnect(t *testing.T) {
|
||||
defer leakcheck.Check(t)
|
||||
plis, err := net.Listen("tcp", "localhost:0")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to listen: %v", err)
|
||||
}
|
||||
p := &proxyServer{t: t, lis: plis}
|
||||
go p.run()
|
||||
defer p.stop()
|
||||
|
||||
blis, err := net.Listen("tcp", "localhost:0")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to listen: %v", err)
|
||||
}
|
||||
|
||||
msg := []byte{4, 3, 5, 2}
|
||||
recvBuf := make([]byte, len(msg), len(msg))
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
in, err := blis.Accept()
|
||||
if err != nil {
|
||||
t.Errorf("failed to accept: %v", err)
|
||||
return
|
||||
}
|
||||
defer in.Close()
|
||||
in.Read(recvBuf)
|
||||
close(done)
|
||||
}()
|
||||
|
||||
// Overwrite the function in the test and restore them in defer.
|
||||
hpfe := func(req *http.Request) (*url.URL, error) {
|
||||
return &url.URL{Host: plis.Addr().String()}, nil
|
||||
}
|
||||
defer overwrite(hpfe)()
|
||||
|
||||
// Dial to proxy server.
|
||||
dialer := newProxyDialer(func(ctx context.Context, addr string) (net.Conn, error) {
|
||||
if deadline, ok := ctx.Deadline(); ok {
|
||||
return net.DialTimeout("tcp", addr, deadline.Sub(time.Now()))
|
||||
}
|
||||
return net.Dial("tcp", addr)
|
||||
})
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
||||
defer cancel()
|
||||
c, err := dialer(ctx, blis.Addr().String())
|
||||
if err != nil {
|
||||
t.Fatalf("http connect Dial failed: %v", err)
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
// Send msg on the connection.
|
||||
c.Write(msg)
|
||||
<-done
|
||||
|
||||
// Check received msg.
|
||||
if string(recvBuf) != string(msg) {
|
||||
t.Fatalf("received msg: %v, want %v", recvBuf, msg)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMapAddressEnv(t *testing.T) {
|
||||
defer leakcheck.Check(t)
|
||||
// Overwrite the function in the test and restore them in defer.
|
||||
hpfe := func(req *http.Request) (*url.URL, error) {
|
||||
if req.URL.Host == envTestAddr {
|
||||
return &url.URL{
|
||||
Scheme: "https",
|
||||
Host: envProxyAddr,
|
||||
}, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
defer overwrite(hpfe)()
|
||||
|
||||
// envTestAddr should be handled by ProxyFromEnvironment.
|
||||
got, err := mapAddress(context.Background(), envTestAddr)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if got != envProxyAddr {
|
||||
t.Errorf("want %v, got %v", envProxyAddr, got)
|
||||
}
|
||||
}
|
157
vendor/google.golang.org/grpc/resolver_conn_wrapper.go
generated
vendored
Normal file
157
vendor/google.golang.org/grpc/resolver_conn_wrapper.go
generated
vendored
Normal file
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
*
|
||||
* 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 (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"google.golang.org/grpc/grpclog"
|
||||
"google.golang.org/grpc/resolver"
|
||||
)
|
||||
|
||||
// ccResolverWrapper is a wrapper on top of cc for resolvers.
|
||||
// It implements resolver.ClientConnection interface.
|
||||
type ccResolverWrapper struct {
|
||||
cc *ClientConn
|
||||
resolver resolver.Resolver
|
||||
addrCh chan []resolver.Address
|
||||
scCh chan string
|
||||
done chan struct{}
|
||||
}
|
||||
|
||||
// split2 returns the values from strings.SplitN(s, sep, 2).
|
||||
// If sep is not found, it returns ("", s, 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.
|
||||
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, _ = split2(ret.Endpoint, "/")
|
||||
return ret
|
||||
}
|
||||
|
||||
// newCCResolverWrapper parses cc.target for scheme and gets the resolver
|
||||
// builder for this scheme. It then builds the resolver and starts the
|
||||
// monitoring goroutine for it.
|
||||
//
|
||||
// If withResolverBuilder dial option is set, the specified resolver will be
|
||||
// used instead.
|
||||
func newCCResolverWrapper(cc *ClientConn) (*ccResolverWrapper, error) {
|
||||
grpclog.Infof("dialing to target with scheme: %q", cc.parsedTarget.Scheme)
|
||||
|
||||
rb := cc.dopts.resolverBuilder
|
||||
if rb == nil {
|
||||
rb = resolver.Get(cc.parsedTarget.Scheme)
|
||||
if rb == nil {
|
||||
return nil, fmt.Errorf("could not get resolver for scheme: %q", cc.parsedTarget.Scheme)
|
||||
}
|
||||
}
|
||||
|
||||
ccr := &ccResolverWrapper{
|
||||
cc: cc,
|
||||
addrCh: make(chan []resolver.Address, 1),
|
||||
scCh: make(chan string, 1),
|
||||
done: make(chan struct{}),
|
||||
}
|
||||
|
||||
var err error
|
||||
ccr.resolver, err = rb.Build(cc.parsedTarget, ccr, resolver.BuildOption{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ccr, nil
|
||||
}
|
||||
|
||||
func (ccr *ccResolverWrapper) start() {
|
||||
go ccr.watcher()
|
||||
}
|
||||
|
||||
// watcher processes address updates and service config updates sequencially.
|
||||
// Otherwise, we need to resolve possible races between address and service
|
||||
// config (e.g. they specify different balancer types).
|
||||
func (ccr *ccResolverWrapper) watcher() {
|
||||
for {
|
||||
select {
|
||||
case <-ccr.done:
|
||||
return
|
||||
default:
|
||||
}
|
||||
|
||||
select {
|
||||
case addrs := <-ccr.addrCh:
|
||||
select {
|
||||
case <-ccr.done:
|
||||
return
|
||||
default:
|
||||
}
|
||||
grpclog.Infof("ccResolverWrapper: sending new addresses to cc: %v", addrs)
|
||||
ccr.cc.handleResolvedAddrs(addrs, nil)
|
||||
case sc := <-ccr.scCh:
|
||||
select {
|
||||
case <-ccr.done:
|
||||
return
|
||||
default:
|
||||
}
|
||||
grpclog.Infof("ccResolverWrapper: got new service config: %v", sc)
|
||||
ccr.cc.handleServiceConfig(sc)
|
||||
case <-ccr.done:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (ccr *ccResolverWrapper) resolveNow(o resolver.ResolveNowOption) {
|
||||
ccr.resolver.ResolveNow(o)
|
||||
}
|
||||
|
||||
func (ccr *ccResolverWrapper) close() {
|
||||
ccr.resolver.Close()
|
||||
close(ccr.done)
|
||||
}
|
||||
|
||||
// NewAddress is called by the resolver implemenetion to send addresses to gRPC.
|
||||
func (ccr *ccResolverWrapper) NewAddress(addrs []resolver.Address) {
|
||||
select {
|
||||
case <-ccr.addrCh:
|
||||
default:
|
||||
}
|
||||
ccr.addrCh <- addrs
|
||||
}
|
||||
|
||||
// NewServiceConfig is called by the resolver implemenetion to send service
|
||||
// configs to gPRC.
|
||||
func (ccr *ccResolverWrapper) NewServiceConfig(sc string) {
|
||||
select {
|
||||
case <-ccr.scCh:
|
||||
default:
|
||||
}
|
||||
ccr.scCh <- sc
|
||||
}
|
79
vendor/google.golang.org/grpc/resolver_conn_wrapper_test.go
generated
vendored
Normal file
79
vendor/google.golang.org/grpc/resolver_conn_wrapper_test.go
generated
vendored
Normal file
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
*
|
||||
* 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 (
|
||||
"testing"
|
||||
|
||||
"google.golang.org/grpc/resolver"
|
||||
)
|
||||
|
||||
func TestParseTarget(t *testing.T) {
|
||||
for _, test := range []resolver.Target{
|
||||
{"", "", ""},
|
||||
{"a", "", ""},
|
||||
{"", "a", ""},
|
||||
{"", "", "a"},
|
||||
{"a", "b", ""},
|
||||
{"a", "", "b"},
|
||||
{"", "a", "b"},
|
||||
{"a", "b", "c"},
|
||||
{"dns", "", "google.com"},
|
||||
{"dns", "a.server.com", "google.com"},
|
||||
{"dns", "a.server.com", "google.com/?a=b"},
|
||||
{"", "", "/unix/socket/address"},
|
||||
} {
|
||||
str := test.Scheme + "://" + test.Authority + "/" + test.Endpoint
|
||||
got := parseTarget(str)
|
||||
if got != test {
|
||||
t.Errorf("parseTarget(%q) = %+v, want %+v", str, got, test)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseTargetString(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
targetStr string
|
||||
want resolver.Target
|
||||
}{
|
||||
{"", resolver.Target{"", "", ""}},
|
||||
{"://", resolver.Target{"", "", ""}},
|
||||
{":///", resolver.Target{"", "", ""}},
|
||||
{"a:///", resolver.Target{"a", "", ""}},
|
||||
{"://a/", resolver.Target{"", "a", ""}},
|
||||
{":///a", resolver.Target{"", "", "a"}},
|
||||
{"a://b/", resolver.Target{"a", "b", ""}},
|
||||
{"a:///b", resolver.Target{"a", "", "b"}},
|
||||
{"://a/b", resolver.Target{"", "a", "b"}},
|
||||
{"a://b/c", resolver.Target{"a", "b", "c"}},
|
||||
{"dns:///google.com", resolver.Target{"dns", "", "google.com"}},
|
||||
{"dns://a.server.com/google.com", resolver.Target{"dns", "a.server.com", "google.com"}},
|
||||
{"dns://a.server.com/google.com/?a=b", resolver.Target{"dns", "a.server.com", "google.com/?a=b"}},
|
||||
|
||||
{"/", resolver.Target{"", "", "/"}},
|
||||
{"google.com", resolver.Target{"", "", "google.com"}},
|
||||
{"google.com/?a=b", resolver.Target{"", "", "google.com/?a=b"}},
|
||||
{"/unix/socket/address", resolver.Target{"", "", "/unix/socket/address"}},
|
||||
} {
|
||||
got := parseTarget(test.targetStr)
|
||||
if got != test.want {
|
||||
t.Errorf("parseTarget(%q) = %+v, want %+v", test.targetStr, got, test.want)
|
||||
}
|
||||
}
|
||||
}
|
577
vendor/google.golang.org/grpc/rpc_util.go
generated
vendored
Normal file
577
vendor/google.golang.org/grpc/rpc_util.go
generated
vendored
Normal file
|
@ -0,0 +1,577 @@
|
|||
/*
|
||||
*
|
||||
* 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 grpc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/encoding"
|
||||
"google.golang.org/grpc/encoding/proto"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/peer"
|
||||
"google.golang.org/grpc/stats"
|
||||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/grpc/transport"
|
||||
)
|
||||
|
||||
// Compressor defines the interface gRPC uses to compress a message.
|
||||
type Compressor interface {
|
||||
// Do compresses p into w.
|
||||
Do(w io.Writer, p []byte) error
|
||||
// Type returns the compression algorithm the Compressor uses.
|
||||
Type() string
|
||||
}
|
||||
|
||||
type gzipCompressor struct {
|
||||
pool sync.Pool
|
||||
}
|
||||
|
||||
// NewGZIPCompressor creates a Compressor based on GZIP.
|
||||
func NewGZIPCompressor() Compressor {
|
||||
return &gzipCompressor{
|
||||
pool: sync.Pool{
|
||||
New: func() interface{} {
|
||||
return gzip.NewWriter(ioutil.Discard)
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (c *gzipCompressor) Do(w io.Writer, p []byte) error {
|
||||
z := c.pool.Get().(*gzip.Writer)
|
||||
defer c.pool.Put(z)
|
||||
z.Reset(w)
|
||||
if _, err := z.Write(p); err != nil {
|
||||
return err
|
||||
}
|
||||
return z.Close()
|
||||
}
|
||||
|
||||
func (c *gzipCompressor) Type() string {
|
||||
return "gzip"
|
||||
}
|
||||
|
||||
// Decompressor defines the interface gRPC uses to decompress a message.
|
||||
type Decompressor interface {
|
||||
// Do reads the data from r and uncompress them.
|
||||
Do(r io.Reader) ([]byte, error)
|
||||
// Type returns the compression algorithm the Decompressor uses.
|
||||
Type() string
|
||||
}
|
||||
|
||||
type gzipDecompressor struct {
|
||||
pool sync.Pool
|
||||
}
|
||||
|
||||
// NewGZIPDecompressor creates a Decompressor based on GZIP.
|
||||
func NewGZIPDecompressor() Decompressor {
|
||||
return &gzipDecompressor{}
|
||||
}
|
||||
|
||||
func (d *gzipDecompressor) Do(r io.Reader) ([]byte, error) {
|
||||
var z *gzip.Reader
|
||||
switch maybeZ := d.pool.Get().(type) {
|
||||
case nil:
|
||||
newZ, err := gzip.NewReader(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
z = newZ
|
||||
case *gzip.Reader:
|
||||
z = maybeZ
|
||||
if err := z.Reset(r); err != nil {
|
||||
d.pool.Put(z)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
defer func() {
|
||||
z.Close()
|
||||
d.pool.Put(z)
|
||||
}()
|
||||
return ioutil.ReadAll(z)
|
||||
}
|
||||
|
||||
func (d *gzipDecompressor) Type() string {
|
||||
return "gzip"
|
||||
}
|
||||
|
||||
// callInfo contains all related configuration and information about an RPC.
|
||||
type callInfo struct {
|
||||
compressorType string
|
||||
failFast bool
|
||||
stream *transport.Stream
|
||||
traceInfo traceInfo // in trace.go
|
||||
maxReceiveMessageSize *int
|
||||
maxSendMessageSize *int
|
||||
creds credentials.PerRPCCredentials
|
||||
contentSubtype string
|
||||
codec baseCodec
|
||||
}
|
||||
|
||||
func defaultCallInfo() *callInfo {
|
||||
return &callInfo{failFast: true}
|
||||
}
|
||||
|
||||
// CallOption configures a Call before it starts or extracts information from
|
||||
// a Call after it completes.
|
||||
type CallOption interface {
|
||||
// before is called before the call is sent to any server. If before
|
||||
// returns a non-nil error, the RPC fails with that error.
|
||||
before(*callInfo) error
|
||||
|
||||
// after is called after the call has completed. after cannot return an
|
||||
// error, so any failures should be reported via output parameters.
|
||||
after(*callInfo)
|
||||
}
|
||||
|
||||
// EmptyCallOption does not alter the Call configuration.
|
||||
// It can be embedded in another structure to carry satellite data for use
|
||||
// by interceptors.
|
||||
type EmptyCallOption struct{}
|
||||
|
||||
func (EmptyCallOption) before(*callInfo) error { return nil }
|
||||
func (EmptyCallOption) after(*callInfo) {}
|
||||
|
||||
type beforeCall func(c *callInfo) error
|
||||
|
||||
func (o beforeCall) before(c *callInfo) error { return o(c) }
|
||||
func (o beforeCall) after(c *callInfo) {}
|
||||
|
||||
type afterCall func(c *callInfo)
|
||||
|
||||
func (o afterCall) before(c *callInfo) error { return nil }
|
||||
func (o afterCall) after(c *callInfo) { o(c) }
|
||||
|
||||
// Header returns a CallOptions that retrieves the header metadata
|
||||
// for a unary RPC.
|
||||
func Header(md *metadata.MD) CallOption {
|
||||
return afterCall(func(c *callInfo) {
|
||||
if c.stream != nil {
|
||||
*md, _ = c.stream.Header()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Trailer returns a CallOptions that retrieves the trailer metadata
|
||||
// for a unary RPC.
|
||||
func Trailer(md *metadata.MD) CallOption {
|
||||
return afterCall(func(c *callInfo) {
|
||||
if c.stream != nil {
|
||||
*md = c.stream.Trailer()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Peer returns a CallOption that retrieves peer information for a
|
||||
// unary RPC.
|
||||
func Peer(p *peer.Peer) CallOption {
|
||||
return afterCall(func(c *callInfo) {
|
||||
if c.stream != nil {
|
||||
if x, ok := peer.FromContext(c.stream.Context()); ok {
|
||||
*p = *x
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// FailFast configures the action to take when an RPC is attempted on broken
|
||||
// connections or unreachable servers. If failFast is true, the RPC will fail
|
||||
// immediately. Otherwise, the RPC client will block the call until a
|
||||
// connection is available (or the call is canceled or times out) and will
|
||||
// retry the call if it fails due to a transient error. gRPC will not retry if
|
||||
// data was written to the wire unless the server indicates it did not process
|
||||
// the data. Please refer to
|
||||
// https://github.com/grpc/grpc/blob/master/doc/wait-for-ready.md.
|
||||
//
|
||||
// By default, RPCs are "Fail Fast".
|
||||
func FailFast(failFast bool) CallOption {
|
||||
return beforeCall(func(c *callInfo) error {
|
||||
c.failFast = failFast
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// MaxCallRecvMsgSize returns a CallOption which sets the maximum message size the client can receive.
|
||||
func MaxCallRecvMsgSize(s int) CallOption {
|
||||
return beforeCall(func(o *callInfo) error {
|
||||
o.maxReceiveMessageSize = &s
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// MaxCallSendMsgSize returns a CallOption which sets the maximum message size the client can send.
|
||||
func MaxCallSendMsgSize(s int) CallOption {
|
||||
return beforeCall(func(o *callInfo) error {
|
||||
o.maxSendMessageSize = &s
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// PerRPCCredentials returns a CallOption that sets credentials.PerRPCCredentials
|
||||
// for a call.
|
||||
func PerRPCCredentials(creds credentials.PerRPCCredentials) CallOption {
|
||||
return beforeCall(func(c *callInfo) error {
|
||||
c.creds = creds
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// UseCompressor returns a CallOption which sets the compressor used when
|
||||
// sending the request. If WithCompressor is also set, UseCompressor has
|
||||
// higher priority.
|
||||
//
|
||||
// This API is EXPERIMENTAL.
|
||||
func UseCompressor(name string) CallOption {
|
||||
return beforeCall(func(c *callInfo) error {
|
||||
c.compressorType = name
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// CallContentSubtype returns a CallOption that will set the content-subtype
|
||||
// for a call. For example, if content-subtype is "json", the Content-Type over
|
||||
// the wire will be "application/grpc+json". The content-subtype is converted
|
||||
// to lowercase before being included in Content-Type. See Content-Type on
|
||||
// https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#requests for
|
||||
// more details.
|
||||
//
|
||||
// If CallCustomCodec is not also used, the content-subtype will be used to
|
||||
// look up the Codec to use in the registry controlled by RegisterCodec. See
|
||||
// the documention on RegisterCodec for details on registration. The lookup
|
||||
// of content-subtype is case-insensitive. If no such Codec is found, the call
|
||||
// will result in an error with code codes.Internal.
|
||||
//
|
||||
// If CallCustomCodec is also used, that Codec will be used for all request and
|
||||
// response messages, with the content-subtype set to the given contentSubtype
|
||||
// here for requests.
|
||||
func CallContentSubtype(contentSubtype string) CallOption {
|
||||
contentSubtype = strings.ToLower(contentSubtype)
|
||||
return beforeCall(func(c *callInfo) error {
|
||||
c.contentSubtype = contentSubtype
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// CallCustomCodec returns a CallOption that will set the given Codec to be
|
||||
// used for all request and response messages for a call. The result of calling
|
||||
// String() will be used as the content-subtype in a case-insensitive manner.
|
||||
//
|
||||
// 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 Codec and
|
||||
// content-subtype.
|
||||
//
|
||||
// This function is provided for advanced users; prefer to use only
|
||||
// CallContentSubtype to select a registered codec instead.
|
||||
func CallCustomCodec(codec Codec) CallOption {
|
||||
return beforeCall(func(c *callInfo) error {
|
||||
c.codec = codec
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// The format of the payload: compressed or not?
|
||||
type payloadFormat uint8
|
||||
|
||||
const (
|
||||
compressionNone payloadFormat = iota // no compression
|
||||
compressionMade
|
||||
)
|
||||
|
||||
// parser reads complete gRPC messages from the underlying reader.
|
||||
type parser struct {
|
||||
// r is the underlying reader.
|
||||
// See the comment on recvMsg for the permissible
|
||||
// error types.
|
||||
r io.Reader
|
||||
|
||||
// The header of a gRPC message. Find more detail at
|
||||
// https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md
|
||||
header [5]byte
|
||||
}
|
||||
|
||||
// recvMsg reads a complete gRPC message from the stream.
|
||||
//
|
||||
// It returns the message and its payload (compression/encoding)
|
||||
// format. The caller owns the returned msg memory.
|
||||
//
|
||||
// If there is an error, possible values are:
|
||||
// * io.EOF, when no messages remain
|
||||
// * io.ErrUnexpectedEOF
|
||||
// * of type transport.ConnectionError
|
||||
// * of type transport.StreamError
|
||||
// No other error values or types must be returned, which also means
|
||||
// that the underlying io.Reader must not return an incompatible
|
||||
// error.
|
||||
func (p *parser) recvMsg(maxReceiveMessageSize int) (pf payloadFormat, msg []byte, err error) {
|
||||
if _, err := p.r.Read(p.header[:]); err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
pf = payloadFormat(p.header[0])
|
||||
length := binary.BigEndian.Uint32(p.header[1:])
|
||||
|
||||
if length == 0 {
|
||||
return pf, nil, nil
|
||||
}
|
||||
if int64(length) > int64(maxInt) {
|
||||
return 0, nil, status.Errorf(codes.ResourceExhausted, "grpc: received message larger than max length allowed on current machine (%d vs. %d)", length, maxInt)
|
||||
}
|
||||
if int(length) > maxReceiveMessageSize {
|
||||
return 0, nil, status.Errorf(codes.ResourceExhausted, "grpc: received message larger than max (%d vs. %d)", length, maxReceiveMessageSize)
|
||||
}
|
||||
// TODO(bradfitz,zhaoq): garbage. reuse buffer after proto decoding instead
|
||||
// of making it for each message:
|
||||
msg = make([]byte, int(length))
|
||||
if _, err := p.r.Read(msg); err != nil {
|
||||
if err == io.EOF {
|
||||
err = io.ErrUnexpectedEOF
|
||||
}
|
||||
return 0, nil, err
|
||||
}
|
||||
return pf, msg, nil
|
||||
}
|
||||
|
||||
// encode serializes msg and returns a buffer of message header and a buffer of msg.
|
||||
// If msg is nil, it generates the message header and an empty msg buffer.
|
||||
// TODO(ddyihai): eliminate extra Compressor parameter.
|
||||
func encode(c baseCodec, msg interface{}, cp Compressor, outPayload *stats.OutPayload, compressor encoding.Compressor) ([]byte, []byte, error) {
|
||||
var (
|
||||
b []byte
|
||||
cbuf *bytes.Buffer
|
||||
)
|
||||
const (
|
||||
payloadLen = 1
|
||||
sizeLen = 4
|
||||
)
|
||||
if msg != nil {
|
||||
var err error
|
||||
b, err = c.Marshal(msg)
|
||||
if err != nil {
|
||||
return nil, nil, status.Errorf(codes.Internal, "grpc: error while marshaling: %v", err.Error())
|
||||
}
|
||||
if outPayload != nil {
|
||||
outPayload.Payload = msg
|
||||
// TODO truncate large payload.
|
||||
outPayload.Data = b
|
||||
outPayload.Length = len(b)
|
||||
}
|
||||
if compressor != nil || cp != nil {
|
||||
cbuf = new(bytes.Buffer)
|
||||
// Has compressor, check Compressor is set by UseCompressor first.
|
||||
if compressor != nil {
|
||||
z, _ := compressor.Compress(cbuf)
|
||||
if _, err := z.Write(b); err != nil {
|
||||
return nil, nil, status.Errorf(codes.Internal, "grpc: error while compressing: %v", err.Error())
|
||||
}
|
||||
z.Close()
|
||||
} else {
|
||||
// If Compressor is not set by UseCompressor, use default Compressor
|
||||
if err := cp.Do(cbuf, b); err != nil {
|
||||
return nil, nil, status.Errorf(codes.Internal, "grpc: error while compressing: %v", err.Error())
|
||||
}
|
||||
}
|
||||
b = cbuf.Bytes()
|
||||
}
|
||||
}
|
||||
if uint(len(b)) > math.MaxUint32 {
|
||||
return nil, nil, status.Errorf(codes.ResourceExhausted, "grpc: message too large (%d bytes)", len(b))
|
||||
}
|
||||
|
||||
bufHeader := make([]byte, payloadLen+sizeLen)
|
||||
if compressor != nil || cp != nil {
|
||||
bufHeader[0] = byte(compressionMade)
|
||||
} else {
|
||||
bufHeader[0] = byte(compressionNone)
|
||||
}
|
||||
|
||||
// Write length of b into buf
|
||||
binary.BigEndian.PutUint32(bufHeader[payloadLen:], uint32(len(b)))
|
||||
if outPayload != nil {
|
||||
outPayload.WireLength = payloadLen + sizeLen + len(b)
|
||||
}
|
||||
return bufHeader, b, nil
|
||||
}
|
||||
|
||||
func checkRecvPayload(pf payloadFormat, recvCompress string, haveCompressor bool) *status.Status {
|
||||
switch pf {
|
||||
case compressionNone:
|
||||
case compressionMade:
|
||||
if recvCompress == "" || recvCompress == encoding.Identity {
|
||||
return status.New(codes.Internal, "grpc: compressed flag set with identity or empty encoding")
|
||||
}
|
||||
if !haveCompressor {
|
||||
return status.Newf(codes.Unimplemented, "grpc: Decompressor is not installed for grpc-encoding %q", recvCompress)
|
||||
}
|
||||
default:
|
||||
return status.Newf(codes.Internal, "grpc: received unexpected payload format %d", pf)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// For the two compressor parameters, both should not be set, but if they are,
|
||||
// dc takes precedence over compressor.
|
||||
// TODO(dfawley): wrap the old compressor/decompressor using the new API?
|
||||
func recv(p *parser, c baseCodec, s *transport.Stream, dc Decompressor, m interface{}, maxReceiveMessageSize int, inPayload *stats.InPayload, compressor encoding.Compressor) error {
|
||||
pf, d, err := p.recvMsg(maxReceiveMessageSize)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if inPayload != nil {
|
||||
inPayload.WireLength = len(d)
|
||||
}
|
||||
|
||||
if st := checkRecvPayload(pf, s.RecvCompress(), compressor != nil || dc != nil); st != nil {
|
||||
return st.Err()
|
||||
}
|
||||
|
||||
if pf == compressionMade {
|
||||
// To match legacy behavior, if the decompressor is set by WithDecompressor or RPCDecompressor,
|
||||
// use this decompressor as the default.
|
||||
if dc != nil {
|
||||
d, err = dc.Do(bytes.NewReader(d))
|
||||
if err != nil {
|
||||
return status.Errorf(codes.Internal, "grpc: failed to decompress the received message %v", err)
|
||||
}
|
||||
} else {
|
||||
dcReader, err := compressor.Decompress(bytes.NewReader(d))
|
||||
if err != nil {
|
||||
return status.Errorf(codes.Internal, "grpc: failed to decompress the received message %v", err)
|
||||
}
|
||||
d, err = ioutil.ReadAll(dcReader)
|
||||
if err != nil {
|
||||
return status.Errorf(codes.Internal, "grpc: failed to decompress the received message %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(d) > maxReceiveMessageSize {
|
||||
// TODO: Revisit the error code. Currently keep it consistent with java
|
||||
// implementation.
|
||||
return status.Errorf(codes.ResourceExhausted, "grpc: received message larger than max (%d vs. %d)", len(d), maxReceiveMessageSize)
|
||||
}
|
||||
if err := c.Unmarshal(d, m); err != nil {
|
||||
return status.Errorf(codes.Internal, "grpc: failed to unmarshal the received message %v", err)
|
||||
}
|
||||
if inPayload != nil {
|
||||
inPayload.RecvTime = time.Now()
|
||||
inPayload.Payload = m
|
||||
// TODO truncate large payload.
|
||||
inPayload.Data = d
|
||||
inPayload.Length = len(d)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type rpcInfo struct {
|
||||
failfast bool
|
||||
}
|
||||
|
||||
type rpcInfoContextKey struct{}
|
||||
|
||||
func newContextWithRPCInfo(ctx context.Context, failfast bool) context.Context {
|
||||
return context.WithValue(ctx, rpcInfoContextKey{}, &rpcInfo{failfast: failfast})
|
||||
}
|
||||
|
||||
func rpcInfoFromContext(ctx context.Context) (s *rpcInfo, ok bool) {
|
||||
s, ok = ctx.Value(rpcInfoContextKey{}).(*rpcInfo)
|
||||
return
|
||||
}
|
||||
|
||||
// Code returns the error code for err if it was produced by the rpc system.
|
||||
// Otherwise, it returns codes.Unknown.
|
||||
//
|
||||
// Deprecated: use status.FromError and Code method instead.
|
||||
func Code(err error) codes.Code {
|
||||
if s, ok := status.FromError(err); ok {
|
||||
return s.Code()
|
||||
}
|
||||
return codes.Unknown
|
||||
}
|
||||
|
||||
// ErrorDesc returns the error description of err if it was produced by the rpc system.
|
||||
// Otherwise, it returns err.Error() or empty string when err is nil.
|
||||
//
|
||||
// Deprecated: use status.FromError and Message method instead.
|
||||
func ErrorDesc(err error) string {
|
||||
if s, ok := status.FromError(err); ok {
|
||||
return s.Message()
|
||||
}
|
||||
return err.Error()
|
||||
}
|
||||
|
||||
// Errorf returns an error containing an error code and a description;
|
||||
// Errorf returns nil if c is OK.
|
||||
//
|
||||
// Deprecated: use status.Errorf instead.
|
||||
func Errorf(c codes.Code, format string, a ...interface{}) error {
|
||||
return status.Errorf(c, format, a...)
|
||||
}
|
||||
|
||||
// setCallInfoCodec should only be called after CallOptions have been applied.
|
||||
func setCallInfoCodec(c *callInfo) error {
|
||||
if c.codec != nil {
|
||||
// codec was already set by a CallOption; use it.
|
||||
return nil
|
||||
}
|
||||
|
||||
if c.contentSubtype == "" {
|
||||
// No codec specified in CallOptions; use proto by default.
|
||||
c.codec = encoding.GetCodec(proto.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
// c.contentSubtype is already lowercased in CallContentSubtype
|
||||
c.codec = encoding.GetCodec(c.contentSubtype)
|
||||
if c.codec == nil {
|
||||
return status.Errorf(codes.Internal, "no codec registered for content-subtype %s", c.contentSubtype)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// The SupportPackageIsVersion variables are referenced from generated protocol
|
||||
// buffer files to ensure compatibility with the gRPC version used. The latest
|
||||
// support package version is 5.
|
||||
//
|
||||
// Older versions are kept for compatibility. They may be removed if
|
||||
// compatibility cannot be maintained.
|
||||
//
|
||||
// These constants should not be referenced from any other code.
|
||||
const (
|
||||
SupportPackageIsVersion3 = true
|
||||
SupportPackageIsVersion4 = true
|
||||
SupportPackageIsVersion5 = true
|
||||
)
|
||||
|
||||
// Version is the current grpc version.
|
||||
const Version = "1.10.0"
|
||||
|
||||
const grpcUA = "grpc-go/" + Version
|
240
vendor/google.golang.org/grpc/rpc_util_test.go
generated
vendored
Normal file
240
vendor/google.golang.org/grpc/rpc_util_test.go
generated
vendored
Normal file
|
@ -0,0 +1,240 @@
|
|||
/*
|
||||
*
|
||||
* 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 grpc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"math"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/encoding"
|
||||
protoenc "google.golang.org/grpc/encoding/proto"
|
||||
"google.golang.org/grpc/status"
|
||||
perfpb "google.golang.org/grpc/test/codec_perf"
|
||||
"google.golang.org/grpc/transport"
|
||||
)
|
||||
|
||||
type fullReader struct {
|
||||
reader io.Reader
|
||||
}
|
||||
|
||||
func (f fullReader) Read(p []byte) (int, error) {
|
||||
return io.ReadFull(f.reader, p)
|
||||
}
|
||||
|
||||
var _ CallOption = EmptyCallOption{} // ensure EmptyCallOption implements the interface
|
||||
|
||||
func TestSimpleParsing(t *testing.T) {
|
||||
bigMsg := bytes.Repeat([]byte{'x'}, 1<<24)
|
||||
for _, test := range []struct {
|
||||
// input
|
||||
p []byte
|
||||
// outputs
|
||||
err error
|
||||
b []byte
|
||||
pt payloadFormat
|
||||
}{
|
||||
{nil, io.EOF, nil, compressionNone},
|
||||
{[]byte{0, 0, 0, 0, 0}, nil, nil, compressionNone},
|
||||
{[]byte{0, 0, 0, 0, 1, 'a'}, nil, []byte{'a'}, compressionNone},
|
||||
{[]byte{1, 0}, io.ErrUnexpectedEOF, nil, compressionNone},
|
||||
{[]byte{0, 0, 0, 0, 10, 'a'}, io.ErrUnexpectedEOF, nil, compressionNone},
|
||||
// Check that messages with length >= 2^24 are parsed.
|
||||
{append([]byte{0, 1, 0, 0, 0}, bigMsg...), nil, bigMsg, compressionNone},
|
||||
} {
|
||||
buf := fullReader{bytes.NewReader(test.p)}
|
||||
parser := &parser{r: buf}
|
||||
pt, b, err := parser.recvMsg(math.MaxInt32)
|
||||
if err != test.err || !bytes.Equal(b, test.b) || pt != test.pt {
|
||||
t.Fatalf("parser{%v}.recvMsg(_) = %v, %v, %v\nwant %v, %v, %v", test.p, pt, b, err, test.pt, test.b, test.err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultipleParsing(t *testing.T) {
|
||||
// Set a byte stream consists of 3 messages with their headers.
|
||||
p := []byte{0, 0, 0, 0, 1, 'a', 0, 0, 0, 0, 2, 'b', 'c', 0, 0, 0, 0, 1, 'd'}
|
||||
b := fullReader{bytes.NewReader(p)}
|
||||
parser := &parser{r: b}
|
||||
|
||||
wantRecvs := []struct {
|
||||
pt payloadFormat
|
||||
data []byte
|
||||
}{
|
||||
{compressionNone, []byte("a")},
|
||||
{compressionNone, []byte("bc")},
|
||||
{compressionNone, []byte("d")},
|
||||
}
|
||||
for i, want := range wantRecvs {
|
||||
pt, data, err := parser.recvMsg(math.MaxInt32)
|
||||
if err != nil || pt != want.pt || !reflect.DeepEqual(data, want.data) {
|
||||
t.Fatalf("after %d calls, parser{%v}.recvMsg(_) = %v, %v, %v\nwant %v, %v, <nil>",
|
||||
i, p, pt, data, err, want.pt, want.data)
|
||||
}
|
||||
}
|
||||
|
||||
pt, data, err := parser.recvMsg(math.MaxInt32)
|
||||
if err != io.EOF {
|
||||
t.Fatalf("after %d recvMsgs calls, parser{%v}.recvMsg(_) = %v, %v, %v\nwant _, _, %v",
|
||||
len(wantRecvs), p, pt, data, err, io.EOF)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncode(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
// input
|
||||
msg proto.Message
|
||||
cp Compressor
|
||||
// outputs
|
||||
hdr []byte
|
||||
data []byte
|
||||
err error
|
||||
}{
|
||||
{nil, nil, []byte{0, 0, 0, 0, 0}, []byte{}, nil},
|
||||
} {
|
||||
hdr, data, err := encode(encoding.GetCodec(protoenc.Name), test.msg, nil, nil, nil)
|
||||
if err != test.err || !bytes.Equal(hdr, test.hdr) || !bytes.Equal(data, test.data) {
|
||||
t.Fatalf("encode(_, _, %v, _) = %v, %v, %v\nwant %v, %v, %v", test.cp, hdr, data, err, test.hdr, test.data, test.err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompress(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
// input
|
||||
data []byte
|
||||
cp Compressor
|
||||
dc Decompressor
|
||||
// outputs
|
||||
err error
|
||||
}{
|
||||
{make([]byte, 1024), NewGZIPCompressor(), NewGZIPDecompressor(), nil},
|
||||
} {
|
||||
b := new(bytes.Buffer)
|
||||
if err := test.cp.Do(b, test.data); err != test.err {
|
||||
t.Fatalf("Compressor.Do(_, %v) = %v, want %v", test.data, err, test.err)
|
||||
}
|
||||
if b.Len() >= len(test.data) {
|
||||
t.Fatalf("The compressor fails to compress data.")
|
||||
}
|
||||
if p, err := test.dc.Do(b); err != nil || !bytes.Equal(test.data, p) {
|
||||
t.Fatalf("Decompressor.Do(%v) = %v, %v, want %v, <nil>", b, p, err, test.data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestToRPCErr(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
// input
|
||||
errIn error
|
||||
// outputs
|
||||
errOut error
|
||||
}{
|
||||
{transport.StreamError{Code: codes.Unknown, Desc: ""}, status.Error(codes.Unknown, "")},
|
||||
{transport.ErrConnClosing, status.Error(codes.Unavailable, transport.ErrConnClosing.Desc)},
|
||||
} {
|
||||
err := toRPCErr(test.errIn)
|
||||
if _, ok := status.FromError(err); !ok {
|
||||
t.Fatalf("toRPCErr{%v} returned type %T, want %T", test.errIn, err, status.Error(codes.Unknown, ""))
|
||||
}
|
||||
if !reflect.DeepEqual(err, test.errOut) {
|
||||
t.Fatalf("toRPCErr{%v} = %v \nwant %v", test.errIn, err, test.errOut)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// bmEncode benchmarks encoding a Protocol Buffer message containing mSize
|
||||
// bytes.
|
||||
func bmEncode(b *testing.B, mSize int) {
|
||||
cdc := encoding.GetCodec(protoenc.Name)
|
||||
msg := &perfpb.Buffer{Body: make([]byte, mSize)}
|
||||
encodeHdr, encodeData, _ := encode(cdc, msg, nil, nil, nil)
|
||||
encodedSz := int64(len(encodeHdr) + len(encodeData))
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
encode(cdc, msg, nil, nil, nil)
|
||||
}
|
||||
b.SetBytes(encodedSz)
|
||||
}
|
||||
|
||||
func BenchmarkEncode1B(b *testing.B) {
|
||||
bmEncode(b, 1)
|
||||
}
|
||||
|
||||
func BenchmarkEncode1KiB(b *testing.B) {
|
||||
bmEncode(b, 1024)
|
||||
}
|
||||
|
||||
func BenchmarkEncode8KiB(b *testing.B) {
|
||||
bmEncode(b, 8*1024)
|
||||
}
|
||||
|
||||
func BenchmarkEncode64KiB(b *testing.B) {
|
||||
bmEncode(b, 64*1024)
|
||||
}
|
||||
|
||||
func BenchmarkEncode512KiB(b *testing.B) {
|
||||
bmEncode(b, 512*1024)
|
||||
}
|
||||
|
||||
func BenchmarkEncode1MiB(b *testing.B) {
|
||||
bmEncode(b, 1024*1024)
|
||||
}
|
||||
|
||||
// bmCompressor benchmarks a compressor of a Protocol Buffer message containing
|
||||
// mSize bytes.
|
||||
func bmCompressor(b *testing.B, mSize int, cp Compressor) {
|
||||
payload := make([]byte, mSize)
|
||||
cBuf := bytes.NewBuffer(make([]byte, mSize))
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
cp.Do(cBuf, payload)
|
||||
cBuf.Reset()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkGZIPCompressor1B(b *testing.B) {
|
||||
bmCompressor(b, 1, NewGZIPCompressor())
|
||||
}
|
||||
|
||||
func BenchmarkGZIPCompressor1KiB(b *testing.B) {
|
||||
bmCompressor(b, 1024, NewGZIPCompressor())
|
||||
}
|
||||
|
||||
func BenchmarkGZIPCompressor8KiB(b *testing.B) {
|
||||
bmCompressor(b, 8*1024, NewGZIPCompressor())
|
||||
}
|
||||
|
||||
func BenchmarkGZIPCompressor64KiB(b *testing.B) {
|
||||
bmCompressor(b, 64*1024, NewGZIPCompressor())
|
||||
}
|
||||
|
||||
func BenchmarkGZIPCompressor512KiB(b *testing.B) {
|
||||
bmCompressor(b, 512*1024, NewGZIPCompressor())
|
||||
}
|
||||
|
||||
func BenchmarkGZIPCompressor1MiB(b *testing.B) {
|
||||
bmCompressor(b, 1024*1024, NewGZIPCompressor())
|
||||
}
|
1325
vendor/google.golang.org/grpc/server.go
generated
vendored
Normal file
1325
vendor/google.golang.org/grpc/server.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
124
vendor/google.golang.org/grpc/server_test.go
generated
vendored
Normal file
124
vendor/google.golang.org/grpc/server_test.go
generated
vendored
Normal file
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
*
|
||||
* 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 (
|
||||
"net"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc/test/leakcheck"
|
||||
)
|
||||
|
||||
type emptyServiceServer interface{}
|
||||
|
||||
type testServer struct{}
|
||||
|
||||
func TestStopBeforeServe(t *testing.T) {
|
||||
defer leakcheck.Check(t)
|
||||
lis, err := net.Listen("tcp", "localhost:0")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create listener: %v", err)
|
||||
}
|
||||
|
||||
server := NewServer()
|
||||
server.Stop()
|
||||
err = server.Serve(lis)
|
||||
if err != ErrServerStopped {
|
||||
t.Fatalf("server.Serve() error = %v, want %v", err, ErrServerStopped)
|
||||
}
|
||||
|
||||
// server.Serve is responsible for closing the listener, even if the
|
||||
// server was already stopped.
|
||||
err = lis.Close()
|
||||
if got, want := errorDesc(err), "use of closed"; !strings.Contains(got, want) {
|
||||
t.Errorf("Close() error = %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGracefulStop(t *testing.T) {
|
||||
defer leakcheck.Check(t)
|
||||
|
||||
lis, err := net.Listen("tcp", "localhost:0")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create listener: %v", err)
|
||||
}
|
||||
|
||||
server := NewServer()
|
||||
go func() {
|
||||
// make sure Serve() is called
|
||||
time.Sleep(time.Millisecond * 500)
|
||||
server.GracefulStop()
|
||||
}()
|
||||
|
||||
err = server.Serve(lis)
|
||||
if err != nil {
|
||||
t.Fatalf("Serve() returned non-nil error on GracefulStop: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetServiceInfo(t *testing.T) {
|
||||
defer leakcheck.Check(t)
|
||||
testSd := ServiceDesc{
|
||||
ServiceName: "grpc.testing.EmptyService",
|
||||
HandlerType: (*emptyServiceServer)(nil),
|
||||
Methods: []MethodDesc{
|
||||
{
|
||||
MethodName: "EmptyCall",
|
||||
Handler: nil,
|
||||
},
|
||||
},
|
||||
Streams: []StreamDesc{
|
||||
{
|
||||
StreamName: "EmptyStream",
|
||||
Handler: nil,
|
||||
ServerStreams: false,
|
||||
ClientStreams: true,
|
||||
},
|
||||
},
|
||||
Metadata: []int{0, 2, 1, 3},
|
||||
}
|
||||
|
||||
server := NewServer()
|
||||
server.RegisterService(&testSd, &testServer{})
|
||||
|
||||
info := server.GetServiceInfo()
|
||||
want := map[string]ServiceInfo{
|
||||
"grpc.testing.EmptyService": {
|
||||
Methods: []MethodInfo{
|
||||
{
|
||||
Name: "EmptyCall",
|
||||
IsClientStream: false,
|
||||
IsServerStream: false,
|
||||
},
|
||||
{
|
||||
Name: "EmptyStream",
|
||||
IsClientStream: true,
|
||||
IsServerStream: false,
|
||||
}},
|
||||
Metadata: []int{0, 2, 1, 3},
|
||||
},
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(info, want) {
|
||||
t.Errorf("GetServiceInfo() = %+v, want %+v", info, want)
|
||||
}
|
||||
}
|
226
vendor/google.golang.org/grpc/service_config.go
generated
vendored
Normal file
226
vendor/google.golang.org/grpc/service_config.go
generated
vendored
Normal file
|
@ -0,0 +1,226 @@
|
|||
/*
|
||||
*
|
||||
* 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 (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc/grpclog"
|
||||
)
|
||||
|
||||
const maxInt = int(^uint(0) >> 1)
|
||||
|
||||
// MethodConfig defines the configuration recommended by the service providers for a
|
||||
// particular method.
|
||||
// DEPRECATED: Users should not use this struct. Service config should be received
|
||||
// through name resolver, as specified here
|
||||
// https://github.com/grpc/grpc/blob/master/doc/service_config.md
|
||||
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
|
||||
}
|
||||
|
||||
// ServiceConfig is provided by the service provider and contains parameters for how
|
||||
// clients that connect to the service should behave.
|
||||
// DEPRECATED: Users should not use this struct. Service config should be received
|
||||
// through name resolver, as specified here
|
||||
// https://github.com/grpc/grpc/blob/master/doc/service_config.md
|
||||
type ServiceConfig struct {
|
||||
// LB is the load balancer the service providers recommends. The balancer specified
|
||||
// via grpc.WithBalancer will override this.
|
||||
LB *string
|
||||
// Methods contains a map for the methods in this service.
|
||||
// If there is an exact match for a method (i.e. /service/method) in the map, use the corresponding MethodConfig.
|
||||
// If there's no exact match, look for the default config for the service (/service/) and use the corresponding MethodConfig if it exists.
|
||||
// Otherwise, the method has no MethodConfig to use.
|
||||
Methods map[string]MethodConfig
|
||||
}
|
||||
|
||||
func parseDuration(s *string) (*time.Duration, error) {
|
||||
if s == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if !strings.HasSuffix(*s, "s") {
|
||||
return nil, fmt.Errorf("malformed duration %q", *s)
|
||||
}
|
||||
ss := strings.SplitN((*s)[:len(*s)-1], ".", 3)
|
||||
if len(ss) > 2 {
|
||||
return nil, fmt.Errorf("malformed duration %q", *s)
|
||||
}
|
||||
// hasDigits is set if either the whole or fractional part of the number is
|
||||
// present, since both are optional but one is required.
|
||||
hasDigits := false
|
||||
var d time.Duration
|
||||
if len(ss[0]) > 0 {
|
||||
i, err := strconv.ParseInt(ss[0], 10, 32)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("malformed duration %q: %v", *s, err)
|
||||
}
|
||||
d = time.Duration(i) * time.Second
|
||||
hasDigits = true
|
||||
}
|
||||
if len(ss) == 2 && len(ss[1]) > 0 {
|
||||
if len(ss[1]) > 9 {
|
||||
return nil, fmt.Errorf("malformed duration %q", *s)
|
||||
}
|
||||
f, err := strconv.ParseInt(ss[1], 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("malformed duration %q: %v", *s, err)
|
||||
}
|
||||
for i := 9; i > len(ss[1]); i-- {
|
||||
f *= 10
|
||||
}
|
||||
d += time.Duration(f)
|
||||
hasDigits = true
|
||||
}
|
||||
if !hasDigits {
|
||||
return nil, fmt.Errorf("malformed duration %q", *s)
|
||||
}
|
||||
|
||||
return &d, nil
|
||||
}
|
||||
|
||||
type jsonName struct {
|
||||
Service *string
|
||||
Method *string
|
||||
}
|
||||
|
||||
func (j jsonName) generatePath() (string, bool) {
|
||||
if j.Service == nil {
|
||||
return "", false
|
||||
}
|
||||
res := "/" + *j.Service + "/"
|
||||
if j.Method != nil {
|
||||
res += *j.Method
|
||||
}
|
||||
return res, true
|
||||
}
|
||||
|
||||
// TODO(lyuxuan): delete this struct after cleaning up old service config implementation.
|
||||
type jsonMC struct {
|
||||
Name *[]jsonName
|
||||
WaitForReady *bool
|
||||
Timeout *string
|
||||
MaxRequestMessageBytes *int64
|
||||
MaxResponseMessageBytes *int64
|
||||
}
|
||||
|
||||
// TODO(lyuxuan): delete this struct after cleaning up old service config implementation.
|
||||
type jsonSC struct {
|
||||
LoadBalancingPolicy *string
|
||||
MethodConfig *[]jsonMC
|
||||
}
|
||||
|
||||
func parseServiceConfig(js string) (ServiceConfig, error) {
|
||||
var rsc jsonSC
|
||||
err := json.Unmarshal([]byte(js), &rsc)
|
||||
if err != nil {
|
||||
grpclog.Warningf("grpc: parseServiceConfig error unmarshaling %s due to %v", js, err)
|
||||
return ServiceConfig{}, err
|
||||
}
|
||||
sc := ServiceConfig{
|
||||
LB: rsc.LoadBalancingPolicy,
|
||||
Methods: make(map[string]MethodConfig),
|
||||
}
|
||||
if rsc.MethodConfig == nil {
|
||||
return sc, nil
|
||||
}
|
||||
|
||||
for _, m := range *rsc.MethodConfig {
|
||||
if m.Name == nil {
|
||||
continue
|
||||
}
|
||||
d, err := parseDuration(m.Timeout)
|
||||
if err != nil {
|
||||
grpclog.Warningf("grpc: parseServiceConfig error unmarshaling %s due to %v", js, err)
|
||||
return ServiceConfig{}, err
|
||||
}
|
||||
|
||||
mc := MethodConfig{
|
||||
WaitForReady: m.WaitForReady,
|
||||
Timeout: d,
|
||||
}
|
||||
if m.MaxRequestMessageBytes != nil {
|
||||
if *m.MaxRequestMessageBytes > int64(maxInt) {
|
||||
mc.MaxReqSize = newInt(maxInt)
|
||||
} else {
|
||||
mc.MaxReqSize = newInt(int(*m.MaxRequestMessageBytes))
|
||||
}
|
||||
}
|
||||
if m.MaxResponseMessageBytes != nil {
|
||||
if *m.MaxResponseMessageBytes > int64(maxInt) {
|
||||
mc.MaxRespSize = newInt(maxInt)
|
||||
} else {
|
||||
mc.MaxRespSize = newInt(int(*m.MaxResponseMessageBytes))
|
||||
}
|
||||
}
|
||||
for _, n := range *m.Name {
|
||||
if path, valid := n.generatePath(); valid {
|
||||
sc.Methods[path] = mc
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sc, nil
|
||||
}
|
||||
|
||||
func min(a, b *int) *int {
|
||||
if *a < *b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func getMaxSize(mcMax, doptMax *int, defaultVal int) *int {
|
||||
if mcMax == nil && doptMax == nil {
|
||||
return &defaultVal
|
||||
}
|
||||
if mcMax != nil && doptMax != nil {
|
||||
return min(mcMax, doptMax)
|
||||
}
|
||||
if mcMax != nil {
|
||||
return mcMax
|
||||
}
|
||||
return doptMax
|
||||
}
|
||||
|
||||
func newInt(b int) *int {
|
||||
return &b
|
||||
}
|
386
vendor/google.golang.org/grpc/service_config_test.go
generated
vendored
Normal file
386
vendor/google.golang.org/grpc/service_config_test.go
generated
vendored
Normal file
|
@ -0,0 +1,386 @@
|
|||
/*
|
||||
*
|
||||
* 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 (
|
||||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestParseLoadBalancer(t *testing.T) {
|
||||
testcases := []struct {
|
||||
scjs string
|
||||
wantSC ServiceConfig
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
`{
|
||||
"loadBalancingPolicy": "round_robin",
|
||||
"methodConfig": [
|
||||
{
|
||||
"name": [
|
||||
{
|
||||
"service": "foo",
|
||||
"method": "Bar"
|
||||
}
|
||||
],
|
||||
"waitForReady": true
|
||||
}
|
||||
]
|
||||
}`,
|
||||
ServiceConfig{
|
||||
LB: newString("round_robin"),
|
||||
Methods: map[string]MethodConfig{
|
||||
"/foo/Bar": {
|
||||
WaitForReady: newBool(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
`{
|
||||
"loadBalancingPolicy": 1,
|
||||
"methodConfig": [
|
||||
{
|
||||
"name": [
|
||||
{
|
||||
"service": "foo",
|
||||
"method": "Bar"
|
||||
}
|
||||
],
|
||||
"waitForReady": false
|
||||
}
|
||||
]
|
||||
}`,
|
||||
ServiceConfig{},
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range testcases {
|
||||
sc, err := parseServiceConfig(c.scjs)
|
||||
if c.wantErr != (err != nil) || !reflect.DeepEqual(sc, c.wantSC) {
|
||||
t.Fatalf("parseServiceConfig(%s) = %+v, %v, want %+v, %v", c.scjs, sc, err, c.wantSC, c.wantErr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseWaitForReady(t *testing.T) {
|
||||
testcases := []struct {
|
||||
scjs string
|
||||
wantSC ServiceConfig
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
`{
|
||||
"methodConfig": [
|
||||
{
|
||||
"name": [
|
||||
{
|
||||
"service": "foo",
|
||||
"method": "Bar"
|
||||
}
|
||||
],
|
||||
"waitForReady": true
|
||||
}
|
||||
]
|
||||
}`,
|
||||
ServiceConfig{
|
||||
Methods: map[string]MethodConfig{
|
||||
"/foo/Bar": {
|
||||
WaitForReady: newBool(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
`{
|
||||
"methodConfig": [
|
||||
{
|
||||
"name": [
|
||||
{
|
||||
"service": "foo",
|
||||
"method": "Bar"
|
||||
}
|
||||
],
|
||||
"waitForReady": false
|
||||
}
|
||||
]
|
||||
}`,
|
||||
ServiceConfig{
|
||||
Methods: map[string]MethodConfig{
|
||||
"/foo/Bar": {
|
||||
WaitForReady: newBool(false),
|
||||
},
|
||||
},
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
`{
|
||||
"methodConfig": [
|
||||
{
|
||||
"name": [
|
||||
{
|
||||
"service": "foo",
|
||||
"method": "Bar"
|
||||
}
|
||||
],
|
||||
"waitForReady": fall
|
||||
},
|
||||
{
|
||||
"name": [
|
||||
{
|
||||
"service": "foo",
|
||||
"method": "Bar"
|
||||
}
|
||||
],
|
||||
"waitForReady": true
|
||||
}
|
||||
]
|
||||
}`,
|
||||
ServiceConfig{},
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range testcases {
|
||||
sc, err := parseServiceConfig(c.scjs)
|
||||
if c.wantErr != (err != nil) || !reflect.DeepEqual(sc, c.wantSC) {
|
||||
t.Fatalf("parseServiceConfig(%s) = %+v, %v, want %+v, %v", c.scjs, sc, err, c.wantSC, c.wantErr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPraseTimeOut(t *testing.T) {
|
||||
testcases := []struct {
|
||||
scjs string
|
||||
wantSC ServiceConfig
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
`{
|
||||
"methodConfig": [
|
||||
{
|
||||
"name": [
|
||||
{
|
||||
"service": "foo",
|
||||
"method": "Bar"
|
||||
}
|
||||
],
|
||||
"timeout": "1s"
|
||||
}
|
||||
]
|
||||
}`,
|
||||
ServiceConfig{
|
||||
Methods: map[string]MethodConfig{
|
||||
"/foo/Bar": {
|
||||
Timeout: newDuration(time.Second),
|
||||
},
|
||||
},
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
`{
|
||||
"methodConfig": [
|
||||
{
|
||||
"name": [
|
||||
{
|
||||
"service": "foo",
|
||||
"method": "Bar"
|
||||
}
|
||||
],
|
||||
"timeout": "3c"
|
||||
}
|
||||
]
|
||||
}`,
|
||||
ServiceConfig{},
|
||||
true,
|
||||
},
|
||||
{
|
||||
`{
|
||||
"methodConfig": [
|
||||
{
|
||||
"name": [
|
||||
{
|
||||
"service": "foo",
|
||||
"method": "Bar"
|
||||
}
|
||||
],
|
||||
"timeout": "3c"
|
||||
},
|
||||
{
|
||||
"name": [
|
||||
{
|
||||
"service": "foo",
|
||||
"method": "Bar"
|
||||
}
|
||||
],
|
||||
"timeout": "1s"
|
||||
}
|
||||
]
|
||||
}`,
|
||||
ServiceConfig{},
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range testcases {
|
||||
sc, err := parseServiceConfig(c.scjs)
|
||||
if c.wantErr != (err != nil) || !reflect.DeepEqual(sc, c.wantSC) {
|
||||
t.Fatalf("parseServiceConfig(%s) = %+v, %v, want %+v, %v", c.scjs, sc, err, c.wantSC, c.wantErr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPraseMsgSize(t *testing.T) {
|
||||
testcases := []struct {
|
||||
scjs string
|
||||
wantSC ServiceConfig
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
`{
|
||||
"methodConfig": [
|
||||
{
|
||||
"name": [
|
||||
{
|
||||
"service": "foo",
|
||||
"method": "Bar"
|
||||
}
|
||||
],
|
||||
"maxRequestMessageBytes": 1024,
|
||||
"maxResponseMessageBytes": 2048
|
||||
}
|
||||
]
|
||||
}`,
|
||||
ServiceConfig{
|
||||
Methods: map[string]MethodConfig{
|
||||
"/foo/Bar": {
|
||||
MaxReqSize: newInt(1024),
|
||||
MaxRespSize: newInt(2048),
|
||||
},
|
||||
},
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
`{
|
||||
"methodConfig": [
|
||||
{
|
||||
"name": [
|
||||
{
|
||||
"service": "foo",
|
||||
"method": "Bar"
|
||||
}
|
||||
],
|
||||
"maxRequestMessageBytes": "1024",
|
||||
"maxResponseMessageBytes": "2048"
|
||||
},
|
||||
{
|
||||
"name": [
|
||||
{
|
||||
"service": "foo",
|
||||
"method": "Bar"
|
||||
}
|
||||
],
|
||||
"maxRequestMessageBytes": 1024,
|
||||
"maxResponseMessageBytes": 2048
|
||||
}
|
||||
]
|
||||
}`,
|
||||
ServiceConfig{},
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range testcases {
|
||||
sc, err := parseServiceConfig(c.scjs)
|
||||
if c.wantErr != (err != nil) || !reflect.DeepEqual(sc, c.wantSC) {
|
||||
t.Fatalf("parseServiceConfig(%s) = %+v, %v, want %+v, %v", c.scjs, sc, err, c.wantSC, c.wantErr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseDuration(t *testing.T) {
|
||||
testCases := []struct {
|
||||
s *string
|
||||
want *time.Duration
|
||||
err bool
|
||||
}{
|
||||
{s: nil, want: nil},
|
||||
{s: newString("1s"), want: newDuration(time.Second)},
|
||||
{s: newString("-1s"), want: newDuration(-time.Second)},
|
||||
{s: newString("1.1s"), want: newDuration(1100 * time.Millisecond)},
|
||||
{s: newString("1.s"), want: newDuration(time.Second)},
|
||||
{s: newString("1.0s"), want: newDuration(time.Second)},
|
||||
{s: newString(".002s"), want: newDuration(2 * time.Millisecond)},
|
||||
{s: newString(".002000s"), want: newDuration(2 * time.Millisecond)},
|
||||
{s: newString("0.003s"), want: newDuration(3 * time.Millisecond)},
|
||||
{s: newString("0.000004s"), want: newDuration(4 * time.Microsecond)},
|
||||
{s: newString("5000.000000009s"), want: newDuration(5000*time.Second + 9*time.Nanosecond)},
|
||||
{s: newString("4999.999999999s"), want: newDuration(5000*time.Second - time.Nanosecond)},
|
||||
{s: newString("1"), err: true},
|
||||
{s: newString("s"), err: true},
|
||||
{s: newString(".s"), err: true},
|
||||
{s: newString("1 s"), err: true},
|
||||
{s: newString(" 1s"), err: true},
|
||||
{s: newString("1ms"), err: true},
|
||||
{s: newString("1.1.1s"), err: true},
|
||||
{s: newString("Xs"), err: true},
|
||||
{s: newString("as"), err: true},
|
||||
{s: newString(".0000000001s"), err: true},
|
||||
{s: newString(fmt.Sprint(math.MaxInt32) + "s"), want: newDuration(math.MaxInt32 * time.Second)},
|
||||
{s: newString(fmt.Sprint(int64(math.MaxInt32)+1) + "s"), err: true},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
got, err := parseDuration(tc.s)
|
||||
if tc.err != (err != nil) ||
|
||||
(got == nil) != (tc.want == nil) ||
|
||||
(got != nil && *got != *tc.want) {
|
||||
wantErr := "<nil>"
|
||||
if tc.err {
|
||||
wantErr = "<non-nil error>"
|
||||
}
|
||||
s := "<nil>"
|
||||
if tc.s != nil {
|
||||
s = `&"` + *tc.s + `"`
|
||||
}
|
||||
t.Errorf("parseDuration(%v) = %v, %v; want %v, %v", s, got, err, tc.want, wantErr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func newBool(b bool) *bool {
|
||||
return &b
|
||||
}
|
||||
|
||||
func newDuration(b time.Duration) *time.Duration {
|
||||
return &b
|
||||
}
|
||||
|
||||
func newString(b string) *string {
|
||||
return &b
|
||||
}
|
189
vendor/google.golang.org/grpc/status/status.go
generated
vendored
Normal file
189
vendor/google.golang.org/grpc/status/status.go
generated
vendored
Normal file
|
@ -0,0 +1,189 @@
|
|||
/*
|
||||
*
|
||||
* 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 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"
|
||||
)
|
||||
|
||||
// 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) status() *Status {
|
||||
return &Status{s: (*spb.Status)(se)}
|
||||
}
|
||||
|
||||
// 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.
|
||||
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...))
|
||||
}
|
||||
|
||||
// Error returns an error representing c and msg. If c is OK, returns nil.
|
||||
func Error(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 Error(c, fmt.Sprintf(format, a...))
|
||||
}
|
||||
|
||||
// ErrorProto returns an error representing s. If s.Code is OK, returns nil.
|
||||
func ErrorProto(s *spb.Status) error {
|
||||
return FromProto(s).Err()
|
||||
}
|
||||
|
||||
// FromProto returns a Status representing s.
|
||||
func FromProto(s *spb.Status) *Status {
|
||||
return &Status{s: proto.Clone(s).(*spb.Status)}
|
||||
}
|
||||
|
||||
// FromError returns a Status representing err if it was produced from this
|
||||
// package. 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) {
|
||||
if err == nil {
|
||||
return &Status{s: &spb.Status{Code: int32(codes.OK)}}, true
|
||||
}
|
||||
if se, ok := err.(*statusError); ok {
|
||||
return se.status(), true
|
||||
}
|
||||
return New(codes.Unknown, err.Error()), false
|
||||
}
|
||||
|
||||
// Convert is a convenience function which removes the need to handle the
|
||||
// boolean return value from FromError.
|
||||
func Convert(err error) *Status {
|
||||
s, _ := FromError(err)
|
||||
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
|
||||
// is nil, or codes.Unknown otherwise.
|
||||
func Code(err error) codes.Code {
|
||||
// Don't use FromError to avoid allocation of OK status.
|
||||
if err == nil {
|
||||
return codes.OK
|
||||
}
|
||||
if se, ok := err.(*statusError); ok {
|
||||
return se.status().Code()
|
||||
}
|
||||
return codes.Unknown
|
||||
}
|
288
vendor/google.golang.org/grpc/status/status_test.go
generated
vendored
Normal file
288
vendor/google.golang.org/grpc/status/status_test.go
generated
vendored
Normal file
|
@ -0,0 +1,288 @@
|
|||
/*
|
||||
*
|
||||
* 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 status
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/golang/protobuf/ptypes"
|
||||
apb "github.com/golang/protobuf/ptypes/any"
|
||||
dpb "github.com/golang/protobuf/ptypes/duration"
|
||||
cpb "google.golang.org/genproto/googleapis/rpc/code"
|
||||
epb "google.golang.org/genproto/googleapis/rpc/errdetails"
|
||||
spb "google.golang.org/genproto/googleapis/rpc/status"
|
||||
"google.golang.org/grpc/codes"
|
||||
)
|
||||
|
||||
func TestErrorsWithSameParameters(t *testing.T) {
|
||||
const description = "some description"
|
||||
e1 := Errorf(codes.AlreadyExists, description)
|
||||
e2 := Errorf(codes.AlreadyExists, description)
|
||||
if e1 == e2 || !reflect.DeepEqual(e1, e2) {
|
||||
t.Fatalf("Errors should be equivalent but unique - e1: %v, %v e2: %p, %v", e1.(*statusError), e1, e2.(*statusError), e2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFromToProto(t *testing.T) {
|
||||
s := &spb.Status{
|
||||
Code: int32(codes.Internal),
|
||||
Message: "test test test",
|
||||
Details: []*apb.Any{{TypeUrl: "foo", Value: []byte{3, 2, 1}}},
|
||||
}
|
||||
|
||||
err := FromProto(s)
|
||||
if got := err.Proto(); !proto.Equal(s, got) {
|
||||
t.Fatalf("Expected errors to be identical - s: %v got: %v", s, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFromNilProto(t *testing.T) {
|
||||
tests := []*Status{nil, FromProto(nil)}
|
||||
for _, s := range tests {
|
||||
if c := s.Code(); c != codes.OK {
|
||||
t.Errorf("s: %v - Expected s.Code() = OK; got %v", s, c)
|
||||
}
|
||||
if m := s.Message(); m != "" {
|
||||
t.Errorf("s: %v - Expected s.Message() = \"\"; got %q", s, m)
|
||||
}
|
||||
if p := s.Proto(); p != nil {
|
||||
t.Errorf("s: %v - Expected s.Proto() = nil; got %q", s, p)
|
||||
}
|
||||
if e := s.Err(); e != nil {
|
||||
t.Errorf("s: %v - Expected s.Err() = nil; got %v", s, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestError(t *testing.T) {
|
||||
err := Error(codes.Internal, "test description")
|
||||
if got, want := err.Error(), "rpc error: code = Internal desc = test description"; got != want {
|
||||
t.Fatalf("err.Error() = %q; want %q", got, want)
|
||||
}
|
||||
s, _ := FromError(err)
|
||||
if got, want := s.Code(), codes.Internal; got != want {
|
||||
t.Fatalf("err.Code() = %s; want %s", got, want)
|
||||
}
|
||||
if got, want := s.Message(), "test description"; got != want {
|
||||
t.Fatalf("err.Message() = %s; want %s", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestErrorOK(t *testing.T) {
|
||||
err := Error(codes.OK, "foo")
|
||||
if err != nil {
|
||||
t.Fatalf("Error(codes.OK, _) = %p; want nil", err.(*statusError))
|
||||
}
|
||||
}
|
||||
|
||||
func TestErrorProtoOK(t *testing.T) {
|
||||
s := &spb.Status{Code: int32(codes.OK)}
|
||||
if got := ErrorProto(s); got != nil {
|
||||
t.Fatalf("ErrorProto(%v) = %v; want nil", s, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFromError(t *testing.T) {
|
||||
code, message := codes.Internal, "test description"
|
||||
err := Error(code, message)
|
||||
s, ok := FromError(err)
|
||||
if !ok || s.Code() != code || s.Message() != message || s.Err() == nil {
|
||||
t.Fatalf("FromError(%v) = %v, %v; want <Code()=%s, Message()=%q, Err()!=nil>, true", err, s, ok, code, message)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFromErrorOK(t *testing.T) {
|
||||
code, message := codes.OK, ""
|
||||
s, ok := FromError(nil)
|
||||
if !ok || s.Code() != code || s.Message() != message || s.Err() != nil {
|
||||
t.Fatalf("FromError(nil) = %v, %v; want <Code()=%s, Message()=%q, Err=nil>, true", s, ok, code, message)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFromErrorUnknownError(t *testing.T) {
|
||||
code, message := codes.Unknown, "unknown error"
|
||||
err := errors.New("unknown error")
|
||||
s, ok := FromError(err)
|
||||
if ok || s.Code() != code || s.Message() != message {
|
||||
t.Fatalf("FromError(%v) = %v, %v; want <Code()=%s, Message()=%q>, false", err, s, ok, code, message)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertKnownError(t *testing.T) {
|
||||
code, message := codes.Internal, "test description"
|
||||
err := Error(code, message)
|
||||
s := Convert(err)
|
||||
if s.Code() != code || s.Message() != message {
|
||||
t.Fatalf("Convert(%v) = %v; want <Code()=%s, Message()=%q>", err, s, code, message)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertUnknownError(t *testing.T) {
|
||||
code, message := codes.Unknown, "unknown error"
|
||||
err := errors.New("unknown error")
|
||||
s := Convert(err)
|
||||
if s.Code() != code || s.Message() != message {
|
||||
t.Fatalf("Convert(%v) = %v; want <Code()=%s, Message()=%q>", err, s, code, message)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStatus_ErrorDetails(t *testing.T) {
|
||||
tests := []struct {
|
||||
code codes.Code
|
||||
details []proto.Message
|
||||
}{
|
||||
{
|
||||
code: codes.NotFound,
|
||||
details: nil,
|
||||
},
|
||||
{
|
||||
code: codes.NotFound,
|
||||
details: []proto.Message{
|
||||
&epb.ResourceInfo{
|
||||
ResourceType: "book",
|
||||
ResourceName: "projects/1234/books/5678",
|
||||
Owner: "User",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
code: codes.Internal,
|
||||
details: []proto.Message{
|
||||
&epb.DebugInfo{
|
||||
StackEntries: []string{
|
||||
"first stack",
|
||||
"second stack",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
code: codes.Unavailable,
|
||||
details: []proto.Message{
|
||||
&epb.RetryInfo{
|
||||
RetryDelay: &dpb.Duration{Seconds: 60},
|
||||
},
|
||||
&epb.ResourceInfo{
|
||||
ResourceType: "book",
|
||||
ResourceName: "projects/1234/books/5678",
|
||||
Owner: "User",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
s, err := New(tc.code, "").WithDetails(tc.details...)
|
||||
if err != nil {
|
||||
t.Fatalf("(%v).WithDetails(%+v) failed: %v", str(s), tc.details, err)
|
||||
}
|
||||
details := s.Details()
|
||||
for i := range details {
|
||||
if !proto.Equal(details[i].(proto.Message), tc.details[i]) {
|
||||
t.Fatalf("(%v).Details()[%d] = %+v, want %+v", str(s), i, details[i], tc.details[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStatus_WithDetails_Fail(t *testing.T) {
|
||||
tests := []*Status{
|
||||
nil,
|
||||
FromProto(nil),
|
||||
New(codes.OK, ""),
|
||||
}
|
||||
for _, s := range tests {
|
||||
if s, err := s.WithDetails(); err == nil || s != nil {
|
||||
t.Fatalf("(%v).WithDetails(%+v) = %v, %v; want nil, non-nil", str(s), []proto.Message{}, s, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStatus_ErrorDetails_Fail(t *testing.T) {
|
||||
tests := []struct {
|
||||
s *Status
|
||||
i []interface{}
|
||||
}{
|
||||
{
|
||||
nil,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
FromProto(nil),
|
||||
nil,
|
||||
},
|
||||
{
|
||||
New(codes.OK, ""),
|
||||
[]interface{}{},
|
||||
},
|
||||
{
|
||||
FromProto(&spb.Status{
|
||||
Code: int32(cpb.Code_CANCELLED),
|
||||
Details: []*apb.Any{
|
||||
{
|
||||
TypeUrl: "",
|
||||
Value: []byte{},
|
||||
},
|
||||
mustMarshalAny(&epb.ResourceInfo{
|
||||
ResourceType: "book",
|
||||
ResourceName: "projects/1234/books/5678",
|
||||
Owner: "User",
|
||||
}),
|
||||
},
|
||||
}),
|
||||
[]interface{}{
|
||||
errors.New(`message type url "" is invalid`),
|
||||
&epb.ResourceInfo{
|
||||
ResourceType: "book",
|
||||
ResourceName: "projects/1234/books/5678",
|
||||
Owner: "User",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
got := tc.s.Details()
|
||||
if !reflect.DeepEqual(got, tc.i) {
|
||||
t.Errorf("(%v).Details() = %+v, want %+v", str(tc.s), got, tc.i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func str(s *Status) string {
|
||||
if s == nil {
|
||||
return "nil"
|
||||
}
|
||||
if s.s == nil {
|
||||
return "<Code=OK>"
|
||||
}
|
||||
return fmt.Sprintf("<Code=%v, Message=%q, Details=%+v>", codes.Code(s.s.GetCode()), s.s.GetMessage(), s.s.GetDetails())
|
||||
}
|
||||
|
||||
// mustMarshalAny converts a protobuf message to an any.
|
||||
func mustMarshalAny(msg proto.Message) *apb.Any {
|
||||
any, err := ptypes.MarshalAny(msg)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("ptypes.MarshalAny(%+v) failed: %v", msg, err))
|
||||
}
|
||||
return any
|
||||
}
|
683
vendor/google.golang.org/grpc/stream.go
generated
vendored
Normal file
683
vendor/google.golang.org/grpc/stream.go
generated
vendored
Normal file
|
@ -0,0 +1,683 @@
|
|||
/*
|
||||
*
|
||||
* 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 grpc
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"golang.org/x/net/trace"
|
||||
"google.golang.org/grpc/balancer"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/encoding"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/stats"
|
||||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/grpc/transport"
|
||||
)
|
||||
|
||||
// StreamHandler defines the handler called by gRPC server to complete the
|
||||
// execution of a streaming RPC.
|
||||
type StreamHandler func(srv interface{}, stream ServerStream) error
|
||||
|
||||
// StreamDesc represents a streaming RPC service's method specification.
|
||||
type StreamDesc struct {
|
||||
StreamName string
|
||||
Handler StreamHandler
|
||||
|
||||
// At least one of these is true.
|
||||
ServerStreams bool
|
||||
ClientStreams bool
|
||||
}
|
||||
|
||||
// Stream defines the common interface a client or server stream has to satisfy.
|
||||
//
|
||||
// All errors returned from Stream are compatible with the status package.
|
||||
type Stream interface {
|
||||
// Context returns the context for this stream.
|
||||
Context() context.Context
|
||||
// SendMsg blocks until it sends m, the stream is done or the stream
|
||||
// breaks.
|
||||
// On error, it aborts the stream and returns an RPC status on client
|
||||
// side. On server side, it simply returns the error to the caller.
|
||||
// SendMsg is called by generated code. Also Users can call SendMsg
|
||||
// directly when it is really needed in their use cases.
|
||||
// It's 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.
|
||||
SendMsg(m interface{}) error
|
||||
// RecvMsg blocks until it receives a message or the stream is
|
||||
// done. On client side, it returns io.EOF when the stream is done. On
|
||||
// any other error, it aborts the stream and returns an RPC status. On
|
||||
// server side, it simply returns the error to the caller.
|
||||
// It's 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
|
||||
}
|
||||
|
||||
// ClientStream defines the interface a client stream has to satisfy.
|
||||
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.
|
||||
CloseSend() error
|
||||
// Stream.SendMsg() may return a non-nil error when something wrong happens sending
|
||||
// the request. The returned error indicates the status of this sending, not the final
|
||||
// status of the RPC.
|
||||
//
|
||||
// Always call Stream.RecvMsg() to drain the stream and get the final
|
||||
// status, otherwise there could be leaked resources.
|
||||
Stream
|
||||
}
|
||||
|
||||
// NewStream creates a new Stream for the client side. This is typically
|
||||
// called by generated code.
|
||||
func (cc *ClientConn) NewStream(ctx context.Context, desc *StreamDesc, method string, opts ...CallOption) (ClientStream, error) {
|
||||
if cc.dopts.streamInt != nil {
|
||||
return cc.dopts.streamInt(ctx, desc, cc, method, newClientStream, opts...)
|
||||
}
|
||||
return newClientStream(ctx, desc, cc, method, opts...)
|
||||
}
|
||||
|
||||
// NewClientStream creates a new Stream for the client side. This is typically
|
||||
// called by generated code.
|
||||
//
|
||||
// DEPRECATED: Use ClientConn.NewStream instead.
|
||||
func NewClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, method string, opts ...CallOption) (ClientStream, error) {
|
||||
return cc.NewStream(ctx, desc, method, opts...)
|
||||
}
|
||||
|
||||
func newClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, method string, opts ...CallOption) (_ ClientStream, err error) {
|
||||
c := defaultCallInfo()
|
||||
mc := cc.GetMethodConfig(method)
|
||||
if mc.WaitForReady != nil {
|
||||
c.failFast = !*mc.WaitForReady
|
||||
}
|
||||
|
||||
// Possible context leak:
|
||||
// The cancel function for the child context we create will only be called
|
||||
// when RecvMsg returns a non-nil error, if the ClientConn is closed, or if
|
||||
// an error is generated by SendMsg.
|
||||
// https://github.com/grpc/grpc-go/issues/1818.
|
||||
var cancel context.CancelFunc
|
||||
if mc.Timeout != nil && *mc.Timeout >= 0 {
|
||||
ctx, cancel = context.WithTimeout(ctx, *mc.Timeout)
|
||||
} else {
|
||||
ctx, cancel = context.WithCancel(ctx)
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
cancel()
|
||||
}
|
||||
}()
|
||||
|
||||
opts = append(cc.dopts.callOptions, opts...)
|
||||
for _, o := range opts {
|
||||
if err := o.before(c); err != nil {
|
||||
return nil, toRPCErr(err)
|
||||
}
|
||||
}
|
||||
c.maxSendMessageSize = getMaxSize(mc.MaxReqSize, c.maxSendMessageSize, defaultClientMaxSendMessageSize)
|
||||
c.maxReceiveMessageSize = getMaxSize(mc.MaxRespSize, c.maxReceiveMessageSize, defaultClientMaxReceiveMessageSize)
|
||||
if err := setCallInfoCodec(c); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
callHdr := &transport.CallHdr{
|
||||
Host: cc.authority,
|
||||
Method: method,
|
||||
// If it's not client streaming, we should already have the request to be sent,
|
||||
// so we don't flush the header.
|
||||
// If it's client streaming, the user may never send a request or send it any
|
||||
// time soon, so we ask the transport to flush the header.
|
||||
Flush: desc.ClientStreams,
|
||||
ContentSubtype: c.contentSubtype,
|
||||
}
|
||||
|
||||
// Set our outgoing compression according to the UseCompressor CallOption, if
|
||||
// set. In that case, also find the compressor from the encoding package.
|
||||
// Otherwise, use the compressor configured by the WithCompressor DialOption,
|
||||
// if set.
|
||||
var cp Compressor
|
||||
var comp encoding.Compressor
|
||||
if ct := c.compressorType; ct != "" {
|
||||
callHdr.SendCompress = ct
|
||||
if ct != encoding.Identity {
|
||||
comp = encoding.GetCompressor(ct)
|
||||
if comp == nil {
|
||||
return nil, status.Errorf(codes.Internal, "grpc: Compressor is not installed for requested grpc-encoding %q", ct)
|
||||
}
|
||||
}
|
||||
} else if cc.dopts.cp != nil {
|
||||
callHdr.SendCompress = cc.dopts.cp.Type()
|
||||
cp = cc.dopts.cp
|
||||
}
|
||||
if c.creds != nil {
|
||||
callHdr.Creds = c.creds
|
||||
}
|
||||
var trInfo traceInfo
|
||||
if EnableTracing {
|
||||
trInfo.tr = trace.New("grpc.Sent."+methodFamily(method), method)
|
||||
trInfo.firstLine.client = true
|
||||
if deadline, ok := ctx.Deadline(); ok {
|
||||
trInfo.firstLine.deadline = deadline.Sub(time.Now())
|
||||
}
|
||||
trInfo.tr.LazyLog(&trInfo.firstLine, false)
|
||||
ctx = trace.NewContext(ctx, trInfo.tr)
|
||||
defer func() {
|
||||
if err != nil {
|
||||
// Need to call tr.finish() if error is returned.
|
||||
// Because tr will not be returned to caller.
|
||||
trInfo.tr.LazyPrintf("RPC: [%v]", err)
|
||||
trInfo.tr.SetError()
|
||||
trInfo.tr.Finish()
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = newContextWithRPCInfo(ctx, c.failFast)
|
||||
sh := cc.dopts.copts.StatsHandler
|
||||
if sh != nil {
|
||||
ctx = sh.TagRPC(ctx, &stats.RPCTagInfo{FullMethodName: method, FailFast: c.failFast})
|
||||
begin := &stats.Begin{
|
||||
Client: true,
|
||||
BeginTime: time.Now(),
|
||||
FailFast: c.failFast,
|
||||
}
|
||||
sh.HandleRPC(ctx, begin)
|
||||
defer func() {
|
||||
if err != nil {
|
||||
// Only handle end stats if err != nil.
|
||||
end := &stats.End{
|
||||
Client: true,
|
||||
Error: err,
|
||||
}
|
||||
sh.HandleRPC(ctx, end)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
var (
|
||||
t transport.ClientTransport
|
||||
s *transport.Stream
|
||||
done func(balancer.DoneInfo)
|
||||
)
|
||||
for {
|
||||
// Check to make sure the context has expired. This will prevent us from
|
||||
// looping forever if an error occurs for wait-for-ready RPCs where no data
|
||||
// is sent on the wire.
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, toRPCErr(ctx.Err())
|
||||
default:
|
||||
}
|
||||
|
||||
t, done, err = cc.getTransport(ctx, c.failFast)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s, err = t.NewStream(ctx, callHdr)
|
||||
if err != nil {
|
||||
if done != nil {
|
||||
done(balancer.DoneInfo{Err: err})
|
||||
done = nil
|
||||
}
|
||||
// In the event of any error from NewStream, we never attempted to write
|
||||
// anything to the wire, so we can retry indefinitely for non-fail-fast
|
||||
// RPCs.
|
||||
if !c.failFast {
|
||||
continue
|
||||
}
|
||||
return nil, toRPCErr(err)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
c.stream = s
|
||||
cs := &clientStream{
|
||||
opts: opts,
|
||||
c: c,
|
||||
desc: desc,
|
||||
codec: c.codec,
|
||||
cp: cp,
|
||||
dc: cc.dopts.dc,
|
||||
comp: comp,
|
||||
cancel: cancel,
|
||||
|
||||
done: done,
|
||||
t: t,
|
||||
s: s,
|
||||
p: &parser{r: s},
|
||||
|
||||
tracing: EnableTracing,
|
||||
trInfo: trInfo,
|
||||
|
||||
statsCtx: ctx,
|
||||
statsHandler: cc.dopts.copts.StatsHandler,
|
||||
}
|
||||
if desc != unaryStreamDesc {
|
||||
// Listen on cc and stream contexts to cleanup when the user closes the
|
||||
// ClientConn or cancels the stream context. In all other cases, an error
|
||||
// should already be injected into the recv buffer by the transport, which
|
||||
// the client will eventually receive, and then we will cancel the stream's
|
||||
// context in clientStream.finish.
|
||||
go func() {
|
||||
select {
|
||||
case <-cc.ctx.Done():
|
||||
cs.finish(ErrClientConnClosing)
|
||||
case <-ctx.Done():
|
||||
cs.finish(toRPCErr(s.Context().Err()))
|
||||
}
|
||||
}()
|
||||
}
|
||||
return cs, nil
|
||||
}
|
||||
|
||||
// clientStream implements a client side Stream.
|
||||
type clientStream struct {
|
||||
opts []CallOption
|
||||
c *callInfo
|
||||
t transport.ClientTransport
|
||||
s *transport.Stream
|
||||
p *parser
|
||||
desc *StreamDesc
|
||||
|
||||
codec baseCodec
|
||||
cp Compressor
|
||||
dc Decompressor
|
||||
comp encoding.Compressor
|
||||
decomp encoding.Compressor
|
||||
decompSet bool
|
||||
|
||||
// cancel is only called when RecvMsg() returns non-nil error, which means
|
||||
// the stream finishes with error or with io.EOF.
|
||||
cancel context.CancelFunc
|
||||
|
||||
tracing bool // set to EnableTracing when the clientStream is created.
|
||||
|
||||
mu sync.Mutex
|
||||
done func(balancer.DoneInfo)
|
||||
sentLast bool // sent an end stream
|
||||
finished bool
|
||||
// trInfo.tr is set when the clientStream is created (if EnableTracing is true),
|
||||
// and is set to nil when the clientStream's finish method is called.
|
||||
trInfo traceInfo
|
||||
|
||||
// statsCtx keeps the user context for stats handling.
|
||||
// All stats collection should use the statsCtx (instead of the stream context)
|
||||
// so that all the generated stats for a particular RPC can be associated in the processing phase.
|
||||
statsCtx context.Context
|
||||
statsHandler stats.Handler
|
||||
}
|
||||
|
||||
func (cs *clientStream) Context() context.Context {
|
||||
return cs.s.Context()
|
||||
}
|
||||
|
||||
func (cs *clientStream) Header() (metadata.MD, error) {
|
||||
m, err := cs.s.Header()
|
||||
if err != nil {
|
||||
err = toRPCErr(err)
|
||||
cs.finish(err)
|
||||
}
|
||||
return m, err
|
||||
}
|
||||
|
||||
func (cs *clientStream) Trailer() metadata.MD {
|
||||
return cs.s.Trailer()
|
||||
}
|
||||
|
||||
func (cs *clientStream) SendMsg(m interface{}) (err error) {
|
||||
// TODO: Check cs.sentLast and error if we already ended the stream.
|
||||
if cs.tracing {
|
||||
cs.mu.Lock()
|
||||
if cs.trInfo.tr != nil {
|
||||
cs.trInfo.tr.LazyLog(&payload{sent: true, msg: m}, true)
|
||||
}
|
||||
cs.mu.Unlock()
|
||||
}
|
||||
// TODO Investigate how to signal the stats handling party.
|
||||
// generate error stats if err != nil && err != io.EOF?
|
||||
defer func() {
|
||||
// For non-client-streaming RPCs, we return nil instead of EOF on success
|
||||
// because the generated code requires it. finish is not called; RecvMsg()
|
||||
// will call it with the stream's status independently.
|
||||
if err == io.EOF && !cs.desc.ClientStreams {
|
||||
err = nil
|
||||
}
|
||||
if err != nil && err != io.EOF {
|
||||
// Call finish for errors generated by this SendMsg call. (Transport
|
||||
// errors are converted to an io.EOF error below; the real error will be
|
||||
// returned from RecvMsg eventually in that case.)
|
||||
cs.finish(err)
|
||||
}
|
||||
}()
|
||||
var outPayload *stats.OutPayload
|
||||
if cs.statsHandler != nil {
|
||||
outPayload = &stats.OutPayload{
|
||||
Client: true,
|
||||
}
|
||||
}
|
||||
hdr, data, err := encode(cs.codec, m, cs.cp, outPayload, cs.comp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(data) > *cs.c.maxSendMessageSize {
|
||||
return status.Errorf(codes.ResourceExhausted, "trying to send message larger than max (%d vs. %d)", len(data), *cs.c.maxSendMessageSize)
|
||||
}
|
||||
if !cs.desc.ClientStreams {
|
||||
cs.sentLast = true
|
||||
}
|
||||
err = cs.t.Write(cs.s, hdr, data, &transport.Options{Last: !cs.desc.ClientStreams})
|
||||
if err == nil {
|
||||
if outPayload != nil {
|
||||
outPayload.SentTime = time.Now()
|
||||
cs.statsHandler.HandleRPC(cs.statsCtx, outPayload)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return io.EOF
|
||||
}
|
||||
|
||||
func (cs *clientStream) RecvMsg(m interface{}) (err error) {
|
||||
defer func() {
|
||||
if err != nil || !cs.desc.ServerStreams {
|
||||
// err != nil or non-server-streaming indicates end of stream.
|
||||
cs.finish(err)
|
||||
}
|
||||
}()
|
||||
var inPayload *stats.InPayload
|
||||
if cs.statsHandler != nil {
|
||||
inPayload = &stats.InPayload{
|
||||
Client: true,
|
||||
}
|
||||
}
|
||||
if !cs.decompSet {
|
||||
// Block until we receive headers containing received message encoding.
|
||||
if ct := cs.s.RecvCompress(); ct != "" && ct != encoding.Identity {
|
||||
if cs.dc == nil || cs.dc.Type() != ct {
|
||||
// No configured decompressor, or it does not match the incoming
|
||||
// message encoding; attempt to find a registered compressor that does.
|
||||
cs.dc = nil
|
||||
cs.decomp = encoding.GetCompressor(ct)
|
||||
}
|
||||
} else {
|
||||
// No compression is used; disable our decompressor.
|
||||
cs.dc = nil
|
||||
}
|
||||
// Only initialize this state once per stream.
|
||||
cs.decompSet = true
|
||||
}
|
||||
err = recv(cs.p, cs.codec, cs.s, cs.dc, m, *cs.c.maxReceiveMessageSize, inPayload, cs.decomp)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
if statusErr := cs.s.Status().Err(); statusErr != nil {
|
||||
return statusErr
|
||||
}
|
||||
return io.EOF // indicates successful end of stream.
|
||||
}
|
||||
return toRPCErr(err)
|
||||
}
|
||||
if cs.tracing {
|
||||
cs.mu.Lock()
|
||||
if cs.trInfo.tr != nil {
|
||||
cs.trInfo.tr.LazyLog(&payload{sent: false, msg: m}, true)
|
||||
}
|
||||
cs.mu.Unlock()
|
||||
}
|
||||
if inPayload != nil {
|
||||
cs.statsHandler.HandleRPC(cs.statsCtx, inPayload)
|
||||
}
|
||||
if cs.desc.ServerStreams {
|
||||
// Subsequent messages should be received by subsequent RecvMsg calls.
|
||||
return nil
|
||||
}
|
||||
|
||||
// Special handling for non-server-stream rpcs.
|
||||
// This recv expects EOF or errors, so we don't collect inPayload.
|
||||
err = recv(cs.p, cs.codec, cs.s, cs.dc, m, *cs.c.maxReceiveMessageSize, nil, cs.decomp)
|
||||
if err == nil {
|
||||
return toRPCErr(errors.New("grpc: client streaming protocol violation: get <nil>, want <EOF>"))
|
||||
}
|
||||
if err == io.EOF {
|
||||
return cs.s.Status().Err() // non-server streaming Recv returns nil on success
|
||||
}
|
||||
return toRPCErr(err)
|
||||
}
|
||||
|
||||
func (cs *clientStream) CloseSend() error {
|
||||
if cs.sentLast {
|
||||
return nil
|
||||
}
|
||||
cs.sentLast = true
|
||||
cs.t.Write(cs.s, nil, nil, &transport.Options{Last: true})
|
||||
// We ignore errors from Write and always return nil here. Any error it
|
||||
// would return would also be returned by a subsequent RecvMsg call, and the
|
||||
// user is supposed to always finish the stream by calling RecvMsg until it
|
||||
// returns err != nil.
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cs *clientStream) finish(err error) {
|
||||
if err == io.EOF {
|
||||
// Ending a stream with EOF indicates a success.
|
||||
err = nil
|
||||
}
|
||||
cs.mu.Lock()
|
||||
defer cs.mu.Unlock()
|
||||
if cs.finished {
|
||||
return
|
||||
}
|
||||
cs.finished = true
|
||||
cs.t.CloseStream(cs.s, err)
|
||||
for _, o := range cs.opts {
|
||||
o.after(cs.c)
|
||||
}
|
||||
if cs.done != nil {
|
||||
cs.done(balancer.DoneInfo{
|
||||
Err: err,
|
||||
BytesSent: true,
|
||||
BytesReceived: cs.s.BytesReceived(),
|
||||
})
|
||||
cs.done = nil
|
||||
}
|
||||
if cs.statsHandler != nil {
|
||||
end := &stats.End{
|
||||
Client: true,
|
||||
EndTime: time.Now(),
|
||||
Error: err,
|
||||
}
|
||||
cs.statsHandler.HandleRPC(cs.statsCtx, end)
|
||||
}
|
||||
cs.cancel()
|
||||
if !cs.tracing {
|
||||
return
|
||||
}
|
||||
if cs.trInfo.tr != nil {
|
||||
if err == nil {
|
||||
cs.trInfo.tr.LazyPrintf("RPC: [OK]")
|
||||
} else {
|
||||
cs.trInfo.tr.LazyPrintf("RPC: [%v]", err)
|
||||
cs.trInfo.tr.SetError()
|
||||
}
|
||||
cs.trInfo.tr.Finish()
|
||||
cs.trInfo.tr = nil
|
||||
}
|
||||
}
|
||||
|
||||
// ServerStream defines the interface a server stream has to satisfy.
|
||||
type ServerStream interface {
|
||||
// SetHeader sets the header metadata. It may be called multiple times.
|
||||
// When call multiple times, all the provided metadata will be merged.
|
||||
// All the metadata will be sent out when one of the following happens:
|
||||
// - ServerStream.SendHeader() is called;
|
||||
// - The first response is sent out;
|
||||
// - An RPC status is sent out (error or success).
|
||||
SetHeader(metadata.MD) error
|
||||
// SendHeader sends the header metadata.
|
||||
// The provided md and headers set by SetHeader() will be sent.
|
||||
// It fails if called multiple times.
|
||||
SendHeader(metadata.MD) error
|
||||
// SetTrailer sets the trailer metadata which will be sent with the RPC status.
|
||||
// When called more than once, all the provided metadata will be merged.
|
||||
SetTrailer(metadata.MD)
|
||||
Stream
|
||||
}
|
||||
|
||||
// serverStream implements a server side Stream.
|
||||
type serverStream struct {
|
||||
t transport.ServerTransport
|
||||
s *transport.Stream
|
||||
p *parser
|
||||
codec baseCodec
|
||||
|
||||
cp Compressor
|
||||
dc Decompressor
|
||||
comp encoding.Compressor
|
||||
decomp encoding.Compressor
|
||||
|
||||
maxReceiveMessageSize int
|
||||
maxSendMessageSize int
|
||||
trInfo *traceInfo
|
||||
|
||||
statsHandler stats.Handler
|
||||
|
||||
mu sync.Mutex // protects trInfo.tr after the service handler runs.
|
||||
}
|
||||
|
||||
func (ss *serverStream) Context() context.Context {
|
||||
return ss.s.Context()
|
||||
}
|
||||
|
||||
func (ss *serverStream) SetHeader(md metadata.MD) error {
|
||||
if md.Len() == 0 {
|
||||
return nil
|
||||
}
|
||||
return ss.s.SetHeader(md)
|
||||
}
|
||||
|
||||
func (ss *serverStream) SendHeader(md metadata.MD) error {
|
||||
return ss.t.WriteHeader(ss.s, md)
|
||||
}
|
||||
|
||||
func (ss *serverStream) SetTrailer(md metadata.MD) {
|
||||
if md.Len() == 0 {
|
||||
return
|
||||
}
|
||||
ss.s.SetTrailer(md)
|
||||
return
|
||||
}
|
||||
|
||||
func (ss *serverStream) SendMsg(m interface{}) (err error) {
|
||||
defer func() {
|
||||
if ss.trInfo != nil {
|
||||
ss.mu.Lock()
|
||||
if ss.trInfo.tr != nil {
|
||||
if err == nil {
|
||||
ss.trInfo.tr.LazyLog(&payload{sent: true, msg: m}, true)
|
||||
} else {
|
||||
ss.trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true)
|
||||
ss.trInfo.tr.SetError()
|
||||
}
|
||||
}
|
||||
ss.mu.Unlock()
|
||||
}
|
||||
if err != nil && err != io.EOF {
|
||||
st, _ := status.FromError(toRPCErr(err))
|
||||
ss.t.WriteStatus(ss.s, st)
|
||||
}
|
||||
}()
|
||||
var outPayload *stats.OutPayload
|
||||
if ss.statsHandler != nil {
|
||||
outPayload = &stats.OutPayload{}
|
||||
}
|
||||
hdr, data, err := encode(ss.codec, m, ss.cp, outPayload, ss.comp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(data) > ss.maxSendMessageSize {
|
||||
return status.Errorf(codes.ResourceExhausted, "trying to send message larger than max (%d vs. %d)", len(data), ss.maxSendMessageSize)
|
||||
}
|
||||
if err := ss.t.Write(ss.s, hdr, data, &transport.Options{Last: false}); err != nil {
|
||||
return toRPCErr(err)
|
||||
}
|
||||
if outPayload != nil {
|
||||
outPayload.SentTime = time.Now()
|
||||
ss.statsHandler.HandleRPC(ss.s.Context(), outPayload)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ss *serverStream) RecvMsg(m interface{}) (err error) {
|
||||
defer func() {
|
||||
if ss.trInfo != nil {
|
||||
ss.mu.Lock()
|
||||
if ss.trInfo.tr != nil {
|
||||
if err == nil {
|
||||
ss.trInfo.tr.LazyLog(&payload{sent: false, msg: m}, true)
|
||||
} else if err != io.EOF {
|
||||
ss.trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true)
|
||||
ss.trInfo.tr.SetError()
|
||||
}
|
||||
}
|
||||
ss.mu.Unlock()
|
||||
}
|
||||
if err != nil && err != io.EOF {
|
||||
st, _ := status.FromError(toRPCErr(err))
|
||||
ss.t.WriteStatus(ss.s, st)
|
||||
}
|
||||
}()
|
||||
var inPayload *stats.InPayload
|
||||
if ss.statsHandler != nil {
|
||||
inPayload = &stats.InPayload{}
|
||||
}
|
||||
if err := recv(ss.p, ss.codec, ss.s, ss.dc, m, ss.maxReceiveMessageSize, inPayload, ss.decomp); err != nil {
|
||||
if err == io.EOF {
|
||||
return err
|
||||
}
|
||||
if err == io.ErrUnexpectedEOF {
|
||||
err = status.Errorf(codes.Internal, io.ErrUnexpectedEOF.Error())
|
||||
}
|
||||
return toRPCErr(err)
|
||||
}
|
||||
if inPayload != nil {
|
||||
ss.statsHandler.HandleRPC(ss.s.Context(), inPayload)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MethodFromServerStream returns the method string for the input stream.
|
||||
// The returned string is in the format of "/service/method".
|
||||
func MethodFromServerStream(stream ServerStream) (string, bool) {
|
||||
s, ok := transport.StreamFromContext(stream.Context())
|
||||
if !ok {
|
||||
return "", ok
|
||||
}
|
||||
return s.Method(), ok
|
||||
}
|
113
vendor/google.golang.org/grpc/trace.go
generated
vendored
Normal file
113
vendor/google.golang.org/grpc/trace.go
generated
vendored
Normal file
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
*
|
||||
* Copyright 2015 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 (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/trace"
|
||||
)
|
||||
|
||||
// EnableTracing controls whether to trace RPCs using the golang.org/x/net/trace package.
|
||||
// This should only be set before any RPCs are sent or received by this program.
|
||||
var EnableTracing bool
|
||||
|
||||
// methodFamily returns the trace family for the given method.
|
||||
// It turns "/pkg.Service/GetFoo" into "pkg.Service".
|
||||
func methodFamily(m string) string {
|
||||
m = strings.TrimPrefix(m, "/") // remove leading slash
|
||||
if i := strings.Index(m, "/"); i >= 0 {
|
||||
m = m[:i] // remove everything from second slash
|
||||
}
|
||||
if i := strings.LastIndex(m, "."); i >= 0 {
|
||||
m = m[i+1:] // cut down to last dotted component
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// traceInfo contains tracing information for an RPC.
|
||||
type traceInfo struct {
|
||||
tr trace.Trace
|
||||
firstLine firstLine
|
||||
}
|
||||
|
||||
// firstLine is the first line of an RPC trace.
|
||||
type firstLine struct {
|
||||
client bool // whether this is a client (outgoing) RPC
|
||||
remoteAddr net.Addr
|
||||
deadline time.Duration // may be zero
|
||||
}
|
||||
|
||||
func (f *firstLine) String() string {
|
||||
var line bytes.Buffer
|
||||
io.WriteString(&line, "RPC: ")
|
||||
if f.client {
|
||||
io.WriteString(&line, "to")
|
||||
} else {
|
||||
io.WriteString(&line, "from")
|
||||
}
|
||||
fmt.Fprintf(&line, " %v deadline:", f.remoteAddr)
|
||||
if f.deadline != 0 {
|
||||
fmt.Fprint(&line, f.deadline)
|
||||
} else {
|
||||
io.WriteString(&line, "none")
|
||||
}
|
||||
return line.String()
|
||||
}
|
||||
|
||||
const truncateSize = 100
|
||||
|
||||
func truncate(x string, l int) string {
|
||||
if l > len(x) {
|
||||
return x
|
||||
}
|
||||
return x[:l]
|
||||
}
|
||||
|
||||
// payload represents an RPC request or response payload.
|
||||
type payload struct {
|
||||
sent bool // whether this is an outgoing payload
|
||||
msg interface{} // e.g. a proto.Message
|
||||
// TODO(dsymonds): add stringifying info to codec, and limit how much we hold here?
|
||||
}
|
||||
|
||||
func (p payload) String() string {
|
||||
if p.sent {
|
||||
return truncate(fmt.Sprintf("sent: %v", p.msg), truncateSize)
|
||||
}
|
||||
return truncate(fmt.Sprintf("recv: %v", p.msg), truncateSize)
|
||||
}
|
||||
|
||||
type fmtStringer struct {
|
||||
format string
|
||||
a []interface{}
|
||||
}
|
||||
|
||||
func (f *fmtStringer) String() string {
|
||||
return fmt.Sprintf(f.format, f.a...)
|
||||
}
|
||||
|
||||
type stringer string
|
||||
|
||||
func (s stringer) String() string { return string(s) }
|
84
vendor/google.golang.org/grpc/vet.sh
generated
vendored
Executable file
84
vendor/google.golang.org/grpc/vet.sh
generated
vendored
Executable file
|
@ -0,0 +1,84 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -ex # Exit on error; debugging enabled.
|
||||
set -o pipefail # Fail a pipe if any sub-command fails.
|
||||
|
||||
die() {
|
||||
echo "$@" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
PATH="$GOPATH/bin:$GOROOT/bin:$PATH"
|
||||
|
||||
# Check proto in manual runs or cron runs.
|
||||
if [[ "$TRAVIS" != "true" || "$TRAVIS_EVENT_TYPE" = "cron" ]]; then
|
||||
check_proto="true"
|
||||
fi
|
||||
|
||||
if [ "$1" = "-install" ]; then
|
||||
go get -d \
|
||||
google.golang.org/grpc/...
|
||||
go get -u \
|
||||
github.com/golang/lint/golint \
|
||||
golang.org/x/tools/cmd/goimports \
|
||||
honnef.co/go/tools/cmd/staticcheck \
|
||||
github.com/client9/misspell/cmd/misspell \
|
||||
github.com/golang/protobuf/protoc-gen-go
|
||||
if [[ "$check_proto" = "true" ]]; then
|
||||
if [[ "$TRAVIS" = "true" ]]; then
|
||||
PROTOBUF_VERSION=3.3.0
|
||||
PROTOC_FILENAME=protoc-${PROTOBUF_VERSION}-linux-x86_64.zip
|
||||
pushd /home/travis
|
||||
wget https://github.com/google/protobuf/releases/download/v${PROTOBUF_VERSION}/${PROTOC_FILENAME}
|
||||
unzip ${PROTOC_FILENAME}
|
||||
bin/protoc --version
|
||||
popd
|
||||
elif ! which protoc > /dev/null; then
|
||||
die "Please install protoc into your path"
|
||||
fi
|
||||
fi
|
||||
exit 0
|
||||
elif [[ "$#" -ne 0 ]]; then
|
||||
die "Unknown argument(s): $*"
|
||||
fi
|
||||
|
||||
# TODO: Remove this check and the mangling below once "context" is imported
|
||||
# directly.
|
||||
if git status --porcelain | read; then
|
||||
die "Uncommitted or untracked files found; commit changes first"
|
||||
fi
|
||||
|
||||
git ls-files "*.go" | xargs grep -L "\(Copyright [0-9]\{4,\} gRPC authors\)\|DO NOT EDIT" 2>&1 | tee /dev/stderr | (! read)
|
||||
gofmt -s -d -l . 2>&1 | tee /dev/stderr | (! read)
|
||||
goimports -l . 2>&1 | tee /dev/stderr | (! read)
|
||||
golint ./... 2>&1 | (grep -vE "(_mock|\.pb)\.go:" || true) | tee /dev/stderr | (! read)
|
||||
|
||||
# Undo any edits made by this script.
|
||||
cleanup() {
|
||||
git reset --hard HEAD
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
# Rewrite golang.org/x/net/context -> context imports (see grpc/grpc-go#1484).
|
||||
# TODO: Remove this mangling once "context" is imported directly (grpc/grpc-go#711).
|
||||
git ls-files "*.go" | xargs sed -i 's:"golang.org/x/net/context":"context":'
|
||||
set +o pipefail
|
||||
# TODO: Stop filtering pb.go files once golang/protobuf#214 is fixed.
|
||||
go tool vet -all . 2>&1 | grep -vE '(clientconn|transport\/transport_test).go:.*cancel (function|var)' | grep -vF '.pb.go:' | tee /dev/stderr | (! read)
|
||||
set -o pipefail
|
||||
git reset --hard HEAD
|
||||
|
||||
if [[ "$check_proto" = "true" ]]; then
|
||||
PATH="/home/travis/bin:$PATH" make proto && \
|
||||
git status --porcelain 2>&1 | (! read) || \
|
||||
(git status; git --no-pager diff; exit 1)
|
||||
fi
|
||||
|
||||
# TODO(menghanl): fix errors in transport_test.
|
||||
staticcheck -ignore '
|
||||
google.golang.org/grpc/transport/transport_test.go:SA2002
|
||||
google.golang.org/grpc/benchmark/benchmain/main.go:SA1019
|
||||
google.golang.org/grpc/stats/stats_test.go:SA1019
|
||||
google.golang.org/grpc/test/end2end_test.go:SA1019
|
||||
' ./...
|
||||
misspell -error .
|
Loading…
Add table
Add a link
Reference in a new issue