pax_global_header 0000666 0000000 0000000 00000000064 12603607541 0014516 g ustar 00root root 0000000 0000000 52 comment=3e7b7e58f491074e9577050058fb95d2351a60b0
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/ 0000775 0000000 0000000 00000000000 12603607541 0017507 5 ustar 00root root 0000000 0000000 grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/.travis.yml 0000664 0000000 0000000 00000000473 12603607541 0021624 0 ustar 00root root 0000000 0000000 language: go
before_install:
- go get github.com/axw/gocov/gocov
- go get github.com/mattn/goveralls
- go get golang.org/x/tools/cmd/cover
install:
- mkdir -p "$GOPATH/src/google.golang.org"
- mv "$TRAVIS_BUILD_DIR" "$GOPATH/src/google.golang.org/grpc"
script:
- make test testrace
- make coverage
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/CONTRIBUTING.md 0000664 0000000 0000000 00000001351 12603607541 0021740 0 ustar 00root root 0000000 0000000 # How to contribute
We definitely welcome patches and contribution to grpc! Here is some guideline
and information about how to do so.
## Getting started
### Legal requirements
In order to protect both you and ourselves, you will need to sign the
[Contributor License Agreement](https://cla.developers.google.com/clas).
### Filing Issues
When filing an issue, make sure to answer these five questions:
1. What version of Go are you using (`go version`)?
2. What operating system and processor architecture are you using?
3. What did you do?
4. What did you expect to see?
5. What did you see instead?
### Contributing code
Unless otherwise noted, the Go source files are distributed under the BSD-style license found in the LICENSE file.
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/Documentation/ 0000775 0000000 0000000 00000000000 12603607541 0022320 5 ustar 00root root 0000000 0000000 grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/Documentation/grpc-auth-support.md 0000664 0000000 0000000 00000002544 12603607541 0026253 0 ustar 00root root 0000000 0000000 # Authentication
As outlined in the [gRPC authentication guide](http://www.grpc.io/docs/guides/auth.html) there are a number of different mechanisms for asserting identity between an client and server. We'll present some code-samples here demonstrating how to provide TLS support encryption and identity assertions as well as passing OAuth2 tokens to services that support it.
# Enabling TLS on a gRPC client
```Go
conn, err := grpc.Dial(serverAddr, grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(nil, ""))
```
# Enabling TLS on a gRPC server
```Go
creds, err := credentials.NewServerTLSFromFile(certFile, keyFile)
if err != nil {
log.Fatalf("Failed to generate credentials %v", err)
}
lis, err := net.Listen("tcp", ":0")
server := grpc.NewServer(grpc.Creds(creds))
...
server.Serve(lis)
```
# Authenticating with Google
## Google Compute Engine (GCE)
```Go
conn, err := grpc.Dial(serverAddr, grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(nil, ""), grpc.WithPerRPCCredentials(oauth.NewComputeEngine())))
```
## JWT
```Go
jwtCreds, err := oauth.NewServiceAccountFromFile(*serviceAccountKeyFile, *oauthScope)
if err != nil {
log.Fatalf("Failed to create JWT credentials: %v", err)
}
conn, err := grpc.Dial(serverAddr, grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(nil, ""), grpc.WithPerRPCCredentials(jwtCreds)))
```
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/LICENSE 0000664 0000000 0000000 00000002703 12603607541 0020516 0 ustar 00root root 0000000 0000000 Copyright 2014, Google Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/Makefile 0000664 0000000 0000000 00000001673 12603607541 0021156 0 ustar 00root root 0000000 0000000 .PHONY: \
all \
deps \
updatedeps \
testdeps \
updatetestdeps \
build \
proto \
test \
testrace \
clean \
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 get -v github.com/golang/protobuf/protoc-gen-go
for file in $$(git ls-files '*.proto'); do \
protoc -I $$(dirname $$file) --go_out=plugins=grpc:$$(dirname $$file) $$file; \
done
test: testdeps
go test -v -cpu 1,4 google.golang.org/grpc/...
testrace: testdeps
go test -v -race -cpu 1,4 google.golang.org/grpc/...
clean:
go clean google.golang.org/grpc/...
coverage: testdeps
goveralls -v google.golang.org/grpc/...
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/PATENTS 0000664 0000000 0000000 00000002443 12603607541 0020553 0 ustar 00root root 0000000 0000000 Additional IP Rights Grant (Patents)
"This implementation" means the copyrightable works distributed by
Google as part of the GRPC project.
Google 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,
transfer and otherwise run, modify and propagate the contents of this
implementation of GRPC, where such license applies only to those patent
claims, both currently owned or controlled by Google and acquired in
the future, licensable by Google that are necessarily infringed by this
implementation of GRPC. This grant does not include claims that would be
infringed only as a consequence of further modification of this
implementation. If you or your agent or exclusive licensee institute or
order or agree to the institution of patent litigation against any
entity (including a cross-claim or counterclaim in a lawsuit) alleging
that this implementation of GRPC or any code incorporated within this
implementation of GRPC constitutes direct or contributory patent
infringement, or inducement of patent infringement, then any patent
rights granted to you under this License for this implementation of GRPC
shall terminate as of the date such litigation is filed.
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/README.md 0000664 0000000 0000000 00000002335 12603607541 0020771 0 ustar 00root root 0000000 0000000 #gRPC-Go
[](https://travis-ci.org/grpc/grpc-go) [](https://godoc.org/google.golang.org/grpc)
The Go implementation of [gRPC](http://www.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](http://www.grpc.io/docs/) guide.
Installation
------------
To install this package, you need to install Go 1.4 or above and setup your Go workspace on your computer. The simplest way to install the library is to run:
```
$ go get google.golang.org/grpc
```
Prerequisites
-------------
This requires Go 1.4 or above.
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/).
Status
------
Beta release
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/benchmark/ 0000775 0000000 0000000 00000000000 12603607541 0021441 5 ustar 00root root 0000000 0000000 grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/benchmark/benchmark.go 0000664 0000000 0000000 00000011255 12603607541 0023726 0 ustar 00root root 0000000 0000000 /*
*
* Copyright 2014, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/*
Package benchmark implements the building blocks to setup end-to-end gRPC benchmarks.
*/
package benchmark
import (
"io"
"math"
"net"
"golang.org/x/net/context"
"google.golang.org/grpc"
testpb "google.golang.org/grpc/benchmark/grpc_testing"
"google.golang.org/grpc/grpclog"
)
func newPayload(t testpb.PayloadType, size int) *testpb.Payload {
if size < 0 {
grpclog.Fatalf("Requested a response with invalid length %d", size)
}
body := make([]byte, size)
switch t {
case testpb.PayloadType_COMPRESSABLE:
case testpb.PayloadType_UNCOMPRESSABLE:
grpclog.Fatalf("PayloadType UNCOMPRESSABLE is not supported")
default:
grpclog.Fatalf("Unsupported payload type: %d", t)
}
return &testpb.Payload{
Type: t,
Body: body,
}
}
type testServer struct {
}
func (s *testServer) UnaryCall(ctx context.Context, in *testpb.SimpleRequest) (*testpb.SimpleResponse, error) {
return &testpb.SimpleResponse{
Payload: newPayload(in.ResponseType, int(in.ResponseSize)),
}, nil
}
func (s *testServer) StreamingCall(stream testpb.TestService_StreamingCallServer) error {
for {
in, err := stream.Recv()
if err == io.EOF {
// read done.
return nil
}
if err != nil {
return err
}
if err := stream.Send(&testpb.SimpleResponse{
Payload: newPayload(in.ResponseType, int(in.ResponseSize)),
}); err != nil {
return err
}
}
}
// StartServer starts a gRPC server serving a benchmark service on the given
// address, which may be something like "localhost:0". It returns its listen
// address and a function to stop the server.
func StartServer(addr string) (string, func()) {
lis, err := net.Listen("tcp", addr)
if err != nil {
grpclog.Fatalf("Failed to listen: %v", err)
}
s := grpc.NewServer(grpc.MaxConcurrentStreams(math.MaxUint32))
testpb.RegisterTestServiceServer(s, &testServer{})
go s.Serve(lis)
return lis.Addr().String(), func() {
s.Stop()
}
}
// DoUnaryCall performs an unary RPC with given stub and request and response sizes.
func DoUnaryCall(tc testpb.TestServiceClient, reqSize, respSize int) {
pl := newPayload(testpb.PayloadType_COMPRESSABLE, reqSize)
req := &testpb.SimpleRequest{
ResponseType: pl.Type,
ResponseSize: int32(respSize),
Payload: pl,
}
if _, err := tc.UnaryCall(context.Background(), req); err != nil {
grpclog.Fatal("/TestService/UnaryCall RPC failed: ", err)
}
}
// DoStreamingRoundTrip performs a round trip for a single streaming rpc.
func DoStreamingRoundTrip(tc testpb.TestServiceClient, stream testpb.TestService_StreamingCallClient, reqSize, respSize int) {
pl := newPayload(testpb.PayloadType_COMPRESSABLE, reqSize)
req := &testpb.SimpleRequest{
ResponseType: pl.Type,
ResponseSize: int32(respSize),
Payload: pl,
}
if err := stream.Send(req); err != nil {
grpclog.Fatalf("StreamingCall(_).Send: %v", err)
}
if _, err := stream.Recv(); err != nil {
grpclog.Fatalf("StreamingCall(_).Recv: %v", err)
}
}
// NewClientConn creates a gRPC client connection to addr.
func NewClientConn(addr string) *grpc.ClientConn {
conn, err := grpc.Dial(addr, grpc.WithInsecure())
if err != nil {
grpclog.Fatalf("NewClientConn(%q) failed to create a ClientConn %v", addr, err)
}
return conn
}
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/benchmark/benchmark_test.go 0000664 0000000 0000000 00000007612 12603607541 0024767 0 ustar 00root root 0000000 0000000 package benchmark
import (
"os"
"sync"
"testing"
"time"
"golang.org/x/net/context"
"google.golang.org/grpc"
testpb "google.golang.org/grpc/benchmark/grpc_testing"
"google.golang.org/grpc/benchmark/stats"
)
func runUnary(b *testing.B, maxConcurrentCalls int) {
s := stats.AddStats(b, 38)
b.StopTimer()
target, stopper := StartServer("localhost:0")
defer stopper()
conn := NewClientConn(target)
tc := testpb.NewTestServiceClient(conn)
// Warm up connection.
for i := 0; i < 10; i++ {
unaryCaller(tc)
}
ch := make(chan int, maxConcurrentCalls*4)
var (
mu sync.Mutex
wg sync.WaitGroup
)
wg.Add(maxConcurrentCalls)
// Distribute the b.N calls over maxConcurrentCalls workers.
for i := 0; i < maxConcurrentCalls; i++ {
go func() {
for range ch {
start := time.Now()
unaryCaller(tc)
elapse := time.Since(start)
mu.Lock()
s.Add(elapse)
mu.Unlock()
}
wg.Done()
}()
}
b.StartTimer()
for i := 0; i < b.N; i++ {
ch <- i
}
b.StopTimer()
close(ch)
wg.Wait()
conn.Close()
}
func runStream(b *testing.B, maxConcurrentCalls int) {
s := stats.AddStats(b, 38)
b.StopTimer()
target, stopper := StartServer("localhost:0")
defer stopper()
conn := NewClientConn(target)
tc := testpb.NewTestServiceClient(conn)
// Warm up connection.
stream, err := tc.StreamingCall(context.Background())
if err != nil {
b.Fatalf("%v.StreamingCall(_) = _, %v", tc, err)
}
for i := 0; i < 10; i++ {
streamCaller(tc, stream)
}
ch := make(chan int, maxConcurrentCalls*4)
var (
mu sync.Mutex
wg sync.WaitGroup
)
wg.Add(maxConcurrentCalls)
// Distribute the b.N calls over maxConcurrentCalls workers.
for i := 0; i < maxConcurrentCalls; i++ {
go func() {
stream, err := tc.StreamingCall(context.Background())
if err != nil {
b.Fatalf("%v.StreamingCall(_) = _, %v", tc, err)
}
for range ch {
start := time.Now()
streamCaller(tc, stream)
elapse := time.Since(start)
mu.Lock()
s.Add(elapse)
mu.Unlock()
}
wg.Done()
}()
}
b.StartTimer()
for i := 0; i < b.N; i++ {
ch <- i
}
b.StopTimer()
close(ch)
wg.Wait()
conn.Close()
}
func unaryCaller(client testpb.TestServiceClient) {
DoUnaryCall(client, 1, 1)
}
func streamCaller(client testpb.TestServiceClient, stream testpb.TestService_StreamingCallClient) {
DoStreamingRoundTrip(client, stream, 1, 1)
}
func BenchmarkClientStreamc1(b *testing.B) {
grpc.EnableTracing = true
runStream(b, 1)
}
func BenchmarkClientStreamc8(b *testing.B) {
grpc.EnableTracing = true
runStream(b, 8)
}
func BenchmarkClientStreamc64(b *testing.B) {
grpc.EnableTracing = true
runStream(b, 64)
}
func BenchmarkClientStreamc512(b *testing.B) {
grpc.EnableTracing = true
runStream(b, 512)
}
func BenchmarkClientUnaryc1(b *testing.B) {
grpc.EnableTracing = true
runUnary(b, 1)
}
func BenchmarkClientUnaryc8(b *testing.B) {
grpc.EnableTracing = true
runUnary(b, 8)
}
func BenchmarkClientUnaryc64(b *testing.B) {
grpc.EnableTracing = true
runUnary(b, 64)
}
func BenchmarkClientUnaryc512(b *testing.B) {
grpc.EnableTracing = true
runUnary(b, 512)
}
func BenchmarkClientStreamNoTracec1(b *testing.B) {
grpc.EnableTracing = false
runStream(b, 1)
}
func BenchmarkClientStreamNoTracec8(b *testing.B) {
grpc.EnableTracing = false
runStream(b, 8)
}
func BenchmarkClientStreamNoTracec64(b *testing.B) {
grpc.EnableTracing = false
runStream(b, 64)
}
func BenchmarkClientStreamNoTracec512(b *testing.B) {
grpc.EnableTracing = false
runStream(b, 512)
}
func BenchmarkClientUnaryNoTracec1(b *testing.B) {
grpc.EnableTracing = false
runUnary(b, 1)
}
func BenchmarkClientUnaryNoTracec8(b *testing.B) {
grpc.EnableTracing = false
runUnary(b, 8)
}
func BenchmarkClientUnaryNoTracec64(b *testing.B) {
grpc.EnableTracing = false
runUnary(b, 64)
}
func BenchmarkClientUnaryNoTracec512(b *testing.B) {
grpc.EnableTracing = false
runUnary(b, 512)
}
func TestMain(m *testing.M) {
os.Exit(stats.RunTestMain(m))
}
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/benchmark/client/ 0000775 0000000 0000000 00000000000 12603607541 0022717 5 ustar 00root root 0000000 0000000 grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/benchmark/client/main.go 0000664 0000000 0000000 00000006600 12603607541 0024174 0 ustar 00root root 0000000 0000000 package main
import (
"flag"
"math"
"net"
"net/http"
_ "net/http/pprof"
"sync"
"time"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/benchmark"
testpb "google.golang.org/grpc/benchmark/grpc_testing"
"google.golang.org/grpc/benchmark/stats"
"google.golang.org/grpc/grpclog"
)
var (
server = flag.String("server", "", "The server address")
maxConcurrentRPCs = flag.Int("max_concurrent_rpcs", 1, "The max number of concurrent RPCs")
duration = flag.Int("duration", math.MaxInt32, "The duration in seconds to run the benchmark client")
trace = flag.Bool("trace", true, "Whether tracing is on")
rpcType = flag.Int("rpc_type", 0,
`Configure different client rpc type. Valid options are:
0 : unary call;
1 : streaming call.`)
)
func unaryCaller(client testpb.TestServiceClient) {
benchmark.DoUnaryCall(client, 1, 1)
}
func streamCaller(client testpb.TestServiceClient, stream testpb.TestService_StreamingCallClient) {
benchmark.DoStreamingRoundTrip(client, stream, 1, 1)
}
func buildConnection() (s *stats.Stats, conn *grpc.ClientConn, tc testpb.TestServiceClient) {
s = stats.NewStats(256)
conn = benchmark.NewClientConn(*server)
tc = testpb.NewTestServiceClient(conn)
return s, conn, tc
}
func closeLoopUnary() {
s, conn, tc := buildConnection()
for i := 0; i < 100; i++ {
unaryCaller(tc)
}
ch := make(chan int, *maxConcurrentRPCs*4)
var (
mu sync.Mutex
wg sync.WaitGroup
)
wg.Add(*maxConcurrentRPCs)
for i := 0; i < *maxConcurrentRPCs; i++ {
go func() {
for _ = range ch {
start := time.Now()
unaryCaller(tc)
elapse := time.Since(start)
mu.Lock()
s.Add(elapse)
mu.Unlock()
}
wg.Done()
}()
}
// Stop the client when time is up.
done := make(chan struct{})
go func() {
<-time.After(time.Duration(*duration) * time.Second)
close(done)
}()
ok := true
for ok {
select {
case ch <- 0:
case <-done:
ok = false
}
}
close(ch)
wg.Wait()
conn.Close()
grpclog.Println(s.String())
}
func closeLoopStream() {
s, conn, tc := buildConnection()
stream, err := tc.StreamingCall(context.Background())
if err != nil {
grpclog.Fatalf("%v.StreamingCall(_) = _, %v", tc, err)
}
for i := 0; i < 100; i++ {
streamCaller(tc, stream)
}
ch := make(chan int, *maxConcurrentRPCs*4)
var (
mu sync.Mutex
wg sync.WaitGroup
)
wg.Add(*maxConcurrentRPCs)
// Distribute RPCs over maxConcurrentCalls workers.
for i := 0; i < *maxConcurrentRPCs; i++ {
go func() {
for _ = range ch {
start := time.Now()
streamCaller(tc, stream)
elapse := time.Since(start)
mu.Lock()
s.Add(elapse)
mu.Unlock()
}
wg.Done()
}()
}
// Stop the client when time is up.
done := make(chan struct{})
go func() {
<-time.After(time.Duration(*duration) * time.Second)
close(done)
}()
ok := true
for ok {
select {
case ch <- 0:
case <-done:
ok = false
}
}
close(ch)
wg.Wait()
conn.Close()
grpclog.Println(s.String())
}
func main() {
flag.Parse()
grpc.EnableTracing = *trace
go func() {
lis, err := net.Listen("tcp", ":0")
if err != nil {
grpclog.Fatalf("Failed to listen: %v", err)
}
grpclog.Println("Client profiling address: ", lis.Addr().String())
if err := http.Serve(lis, nil); err != nil {
grpclog.Fatalf("Failed to serve: %v", err)
}
}()
switch *rpcType {
case 0:
closeLoopUnary()
case 1:
closeLoopStream()
}
}
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/benchmark/grpc_testing/ 0000775 0000000 0000000 00000000000 12603607541 0024131 5 ustar 00root root 0000000 0000000 grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/benchmark/grpc_testing/test.pb.go 0000664 0000000 0000000 00000044241 12603607541 0026044 0 ustar 00root root 0000000 0000000 // Code generated by protoc-gen-go.
// source: test.proto
// DO NOT EDIT!
/*
Package grpc_testing is a generated protocol buffer package.
It is generated from these files:
test.proto
It has these top-level messages:
StatsRequest
ServerStats
Payload
HistogramData
ClientConfig
Mark
ClientArgs
ClientStats
ClientStatus
ServerConfig
ServerArgs
ServerStatus
SimpleRequest
SimpleResponse
*/
package grpc_testing
import proto "github.com/golang/protobuf/proto"
import (
context "golang.org/x/net/context"
grpc "google.golang.org/grpc"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConn
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
type PayloadType int32
const (
// Compressable text format.
PayloadType_COMPRESSABLE PayloadType = 0
// Uncompressable binary format.
PayloadType_UNCOMPRESSABLE PayloadType = 1
// Randomly chosen from all other formats defined in this enum.
PayloadType_RANDOM PayloadType = 2
)
var PayloadType_name = map[int32]string{
0: "COMPRESSABLE",
1: "UNCOMPRESSABLE",
2: "RANDOM",
}
var PayloadType_value = map[string]int32{
"COMPRESSABLE": 0,
"UNCOMPRESSABLE": 1,
"RANDOM": 2,
}
func (x PayloadType) String() string {
return proto.EnumName(PayloadType_name, int32(x))
}
type ClientType int32
const (
ClientType_SYNCHRONOUS_CLIENT ClientType = 0
ClientType_ASYNC_CLIENT ClientType = 1
)
var ClientType_name = map[int32]string{
0: "SYNCHRONOUS_CLIENT",
1: "ASYNC_CLIENT",
}
var ClientType_value = map[string]int32{
"SYNCHRONOUS_CLIENT": 0,
"ASYNC_CLIENT": 1,
}
func (x ClientType) String() string {
return proto.EnumName(ClientType_name, int32(x))
}
type ServerType int32
const (
ServerType_SYNCHRONOUS_SERVER ServerType = 0
ServerType_ASYNC_SERVER ServerType = 1
)
var ServerType_name = map[int32]string{
0: "SYNCHRONOUS_SERVER",
1: "ASYNC_SERVER",
}
var ServerType_value = map[string]int32{
"SYNCHRONOUS_SERVER": 0,
"ASYNC_SERVER": 1,
}
func (x ServerType) String() string {
return proto.EnumName(ServerType_name, int32(x))
}
type RpcType int32
const (
RpcType_UNARY RpcType = 0
RpcType_STREAMING RpcType = 1
)
var RpcType_name = map[int32]string{
0: "UNARY",
1: "STREAMING",
}
var RpcType_value = map[string]int32{
"UNARY": 0,
"STREAMING": 1,
}
func (x RpcType) String() string {
return proto.EnumName(RpcType_name, int32(x))
}
type StatsRequest struct {
// run number
TestNum int32 `protobuf:"varint,1,opt,name=test_num" json:"test_num,omitempty"`
}
func (m *StatsRequest) Reset() { *m = StatsRequest{} }
func (m *StatsRequest) String() string { return proto.CompactTextString(m) }
func (*StatsRequest) ProtoMessage() {}
type ServerStats struct {
// wall clock time
TimeElapsed float64 `protobuf:"fixed64,1,opt,name=time_elapsed" json:"time_elapsed,omitempty"`
// user time used by the server process and threads
TimeUser float64 `protobuf:"fixed64,2,opt,name=time_user" json:"time_user,omitempty"`
// server time used by the server process and all threads
TimeSystem float64 `protobuf:"fixed64,3,opt,name=time_system" json:"time_system,omitempty"`
}
func (m *ServerStats) Reset() { *m = ServerStats{} }
func (m *ServerStats) String() string { return proto.CompactTextString(m) }
func (*ServerStats) ProtoMessage() {}
type Payload struct {
// The type of data in body.
Type PayloadType `protobuf:"varint,1,opt,name=type,enum=grpc.testing.PayloadType" json:"type,omitempty"`
// Primary contents of payload.
Body []byte `protobuf:"bytes,2,opt,name=body,proto3" json:"body,omitempty"`
}
func (m *Payload) Reset() { *m = Payload{} }
func (m *Payload) String() string { return proto.CompactTextString(m) }
func (*Payload) ProtoMessage() {}
type HistogramData struct {
Bucket []uint32 `protobuf:"varint,1,rep,name=bucket" json:"bucket,omitempty"`
MinSeen float64 `protobuf:"fixed64,2,opt,name=min_seen" json:"min_seen,omitempty"`
MaxSeen float64 `protobuf:"fixed64,3,opt,name=max_seen" json:"max_seen,omitempty"`
Sum float64 `protobuf:"fixed64,4,opt,name=sum" json:"sum,omitempty"`
SumOfSquares float64 `protobuf:"fixed64,5,opt,name=sum_of_squares" json:"sum_of_squares,omitempty"`
Count float64 `protobuf:"fixed64,6,opt,name=count" json:"count,omitempty"`
}
func (m *HistogramData) Reset() { *m = HistogramData{} }
func (m *HistogramData) String() string { return proto.CompactTextString(m) }
func (*HistogramData) ProtoMessage() {}
type ClientConfig struct {
ServerTargets []string `protobuf:"bytes,1,rep,name=server_targets" json:"server_targets,omitempty"`
ClientType ClientType `protobuf:"varint,2,opt,name=client_type,enum=grpc.testing.ClientType" json:"client_type,omitempty"`
EnableSsl bool `protobuf:"varint,3,opt,name=enable_ssl" json:"enable_ssl,omitempty"`
OutstandingRpcsPerChannel int32 `protobuf:"varint,4,opt,name=outstanding_rpcs_per_channel" json:"outstanding_rpcs_per_channel,omitempty"`
ClientChannels int32 `protobuf:"varint,5,opt,name=client_channels" json:"client_channels,omitempty"`
PayloadSize int32 `protobuf:"varint,6,opt,name=payload_size" json:"payload_size,omitempty"`
// only for async client:
AsyncClientThreads int32 `protobuf:"varint,7,opt,name=async_client_threads" json:"async_client_threads,omitempty"`
RpcType RpcType `protobuf:"varint,8,opt,name=rpc_type,enum=grpc.testing.RpcType" json:"rpc_type,omitempty"`
}
func (m *ClientConfig) Reset() { *m = ClientConfig{} }
func (m *ClientConfig) String() string { return proto.CompactTextString(m) }
func (*ClientConfig) ProtoMessage() {}
// Request current stats
type Mark struct {
}
func (m *Mark) Reset() { *m = Mark{} }
func (m *Mark) String() string { return proto.CompactTextString(m) }
func (*Mark) ProtoMessage() {}
type ClientArgs struct {
Setup *ClientConfig `protobuf:"bytes,1,opt,name=setup" json:"setup,omitempty"`
Mark *Mark `protobuf:"bytes,2,opt,name=mark" json:"mark,omitempty"`
}
func (m *ClientArgs) Reset() { *m = ClientArgs{} }
func (m *ClientArgs) String() string { return proto.CompactTextString(m) }
func (*ClientArgs) ProtoMessage() {}
func (m *ClientArgs) GetSetup() *ClientConfig {
if m != nil {
return m.Setup
}
return nil
}
func (m *ClientArgs) GetMark() *Mark {
if m != nil {
return m.Mark
}
return nil
}
type ClientStats struct {
Latencies *HistogramData `protobuf:"bytes,1,opt,name=latencies" json:"latencies,omitempty"`
TimeElapsed float64 `protobuf:"fixed64,3,opt,name=time_elapsed" json:"time_elapsed,omitempty"`
TimeUser float64 `protobuf:"fixed64,4,opt,name=time_user" json:"time_user,omitempty"`
TimeSystem float64 `protobuf:"fixed64,5,opt,name=time_system" json:"time_system,omitempty"`
}
func (m *ClientStats) Reset() { *m = ClientStats{} }
func (m *ClientStats) String() string { return proto.CompactTextString(m) }
func (*ClientStats) ProtoMessage() {}
func (m *ClientStats) GetLatencies() *HistogramData {
if m != nil {
return m.Latencies
}
return nil
}
type ClientStatus struct {
Stats *ClientStats `protobuf:"bytes,1,opt,name=stats" json:"stats,omitempty"`
}
func (m *ClientStatus) Reset() { *m = ClientStatus{} }
func (m *ClientStatus) String() string { return proto.CompactTextString(m) }
func (*ClientStatus) ProtoMessage() {}
func (m *ClientStatus) GetStats() *ClientStats {
if m != nil {
return m.Stats
}
return nil
}
type ServerConfig struct {
ServerType ServerType `protobuf:"varint,1,opt,name=server_type,enum=grpc.testing.ServerType" json:"server_type,omitempty"`
Threads int32 `protobuf:"varint,2,opt,name=threads" json:"threads,omitempty"`
EnableSsl bool `protobuf:"varint,3,opt,name=enable_ssl" json:"enable_ssl,omitempty"`
}
func (m *ServerConfig) Reset() { *m = ServerConfig{} }
func (m *ServerConfig) String() string { return proto.CompactTextString(m) }
func (*ServerConfig) ProtoMessage() {}
type ServerArgs struct {
Setup *ServerConfig `protobuf:"bytes,1,opt,name=setup" json:"setup,omitempty"`
Mark *Mark `protobuf:"bytes,2,opt,name=mark" json:"mark,omitempty"`
}
func (m *ServerArgs) Reset() { *m = ServerArgs{} }
func (m *ServerArgs) String() string { return proto.CompactTextString(m) }
func (*ServerArgs) ProtoMessage() {}
func (m *ServerArgs) GetSetup() *ServerConfig {
if m != nil {
return m.Setup
}
return nil
}
func (m *ServerArgs) GetMark() *Mark {
if m != nil {
return m.Mark
}
return nil
}
type ServerStatus struct {
Stats *ServerStats `protobuf:"bytes,1,opt,name=stats" json:"stats,omitempty"`
Port int32 `protobuf:"varint,2,opt,name=port" json:"port,omitempty"`
}
func (m *ServerStatus) Reset() { *m = ServerStatus{} }
func (m *ServerStatus) String() string { return proto.CompactTextString(m) }
func (*ServerStatus) ProtoMessage() {}
func (m *ServerStatus) GetStats() *ServerStats {
if m != nil {
return m.Stats
}
return nil
}
type SimpleRequest struct {
// Desired payload type in the response from the server.
// If response_type is RANDOM, server randomly chooses one from other formats.
ResponseType PayloadType `protobuf:"varint,1,opt,name=response_type,enum=grpc.testing.PayloadType" json:"response_type,omitempty"`
// Desired payload size in the response from the server.
// If response_type is COMPRESSABLE, this denotes the size before compression.
ResponseSize int32 `protobuf:"varint,2,opt,name=response_size" json:"response_size,omitempty"`
// Optional input payload sent along with the request.
Payload *Payload `protobuf:"bytes,3,opt,name=payload" json:"payload,omitempty"`
}
func (m *SimpleRequest) Reset() { *m = SimpleRequest{} }
func (m *SimpleRequest) String() string { return proto.CompactTextString(m) }
func (*SimpleRequest) ProtoMessage() {}
func (m *SimpleRequest) GetPayload() *Payload {
if m != nil {
return m.Payload
}
return nil
}
type SimpleResponse struct {
Payload *Payload `protobuf:"bytes,1,opt,name=payload" json:"payload,omitempty"`
}
func (m *SimpleResponse) Reset() { *m = SimpleResponse{} }
func (m *SimpleResponse) String() string { return proto.CompactTextString(m) }
func (*SimpleResponse) ProtoMessage() {}
func (m *SimpleResponse) GetPayload() *Payload {
if m != nil {
return m.Payload
}
return nil
}
func init() {
proto.RegisterEnum("grpc.testing.PayloadType", PayloadType_name, PayloadType_value)
proto.RegisterEnum("grpc.testing.ClientType", ClientType_name, ClientType_value)
proto.RegisterEnum("grpc.testing.ServerType", ServerType_name, ServerType_value)
proto.RegisterEnum("grpc.testing.RpcType", RpcType_name, RpcType_value)
}
// Client API for TestService service
type TestServiceClient interface {
// One request followed by one response.
// The server returns the client payload as-is.
UnaryCall(ctx context.Context, in *SimpleRequest, opts ...grpc.CallOption) (*SimpleResponse, error)
// One request followed by one response.
// The server returns the client payload as-is.
StreamingCall(ctx context.Context, opts ...grpc.CallOption) (TestService_StreamingCallClient, error)
}
type testServiceClient struct {
cc *grpc.ClientConn
}
func NewTestServiceClient(cc *grpc.ClientConn) TestServiceClient {
return &testServiceClient{cc}
}
func (c *testServiceClient) UnaryCall(ctx context.Context, in *SimpleRequest, opts ...grpc.CallOption) (*SimpleResponse, error) {
out := new(SimpleResponse)
err := grpc.Invoke(ctx, "/grpc.testing.TestService/UnaryCall", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *testServiceClient) StreamingCall(ctx context.Context, opts ...grpc.CallOption) (TestService_StreamingCallClient, error) {
stream, err := grpc.NewClientStream(ctx, &_TestService_serviceDesc.Streams[0], c.cc, "/grpc.testing.TestService/StreamingCall", opts...)
if err != nil {
return nil, err
}
x := &testServiceStreamingCallClient{stream}
return x, nil
}
type TestService_StreamingCallClient interface {
Send(*SimpleRequest) error
Recv() (*SimpleResponse, error)
grpc.ClientStream
}
type testServiceStreamingCallClient struct {
grpc.ClientStream
}
func (x *testServiceStreamingCallClient) Send(m *SimpleRequest) error {
return x.ClientStream.SendMsg(m)
}
func (x *testServiceStreamingCallClient) Recv() (*SimpleResponse, error) {
m := new(SimpleResponse)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
// Server API for TestService service
type TestServiceServer interface {
// One request followed by one response.
// The server returns the client payload as-is.
UnaryCall(context.Context, *SimpleRequest) (*SimpleResponse, error)
// One request followed by one response.
// The server returns the client payload as-is.
StreamingCall(TestService_StreamingCallServer) error
}
func RegisterTestServiceServer(s *grpc.Server, srv TestServiceServer) {
s.RegisterService(&_TestService_serviceDesc, srv)
}
func _TestService_UnaryCall_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error) (interface{}, error) {
in := new(SimpleRequest)
if err := dec(in); err != nil {
return nil, err
}
out, err := srv.(TestServiceServer).UnaryCall(ctx, in)
if err != nil {
return nil, err
}
return out, nil
}
func _TestService_StreamingCall_Handler(srv interface{}, stream grpc.ServerStream) error {
return srv.(TestServiceServer).StreamingCall(&testServiceStreamingCallServer{stream})
}
type TestService_StreamingCallServer interface {
Send(*SimpleResponse) error
Recv() (*SimpleRequest, error)
grpc.ServerStream
}
type testServiceStreamingCallServer struct {
grpc.ServerStream
}
func (x *testServiceStreamingCallServer) Send(m *SimpleResponse) error {
return x.ServerStream.SendMsg(m)
}
func (x *testServiceStreamingCallServer) Recv() (*SimpleRequest, error) {
m := new(SimpleRequest)
if err := x.ServerStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
var _TestService_serviceDesc = grpc.ServiceDesc{
ServiceName: "grpc.testing.TestService",
HandlerType: (*TestServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "UnaryCall",
Handler: _TestService_UnaryCall_Handler,
},
},
Streams: []grpc.StreamDesc{
{
StreamName: "StreamingCall",
Handler: _TestService_StreamingCall_Handler,
ServerStreams: true,
ClientStreams: true,
},
},
}
// Client API for Worker service
type WorkerClient interface {
// Start test with specified workload
RunTest(ctx context.Context, opts ...grpc.CallOption) (Worker_RunTestClient, error)
// Start test with specified workload
RunServer(ctx context.Context, opts ...grpc.CallOption) (Worker_RunServerClient, error)
}
type workerClient struct {
cc *grpc.ClientConn
}
func NewWorkerClient(cc *grpc.ClientConn) WorkerClient {
return &workerClient{cc}
}
func (c *workerClient) RunTest(ctx context.Context, opts ...grpc.CallOption) (Worker_RunTestClient, error) {
stream, err := grpc.NewClientStream(ctx, &_Worker_serviceDesc.Streams[0], c.cc, "/grpc.testing.Worker/RunTest", opts...)
if err != nil {
return nil, err
}
x := &workerRunTestClient{stream}
return x, nil
}
type Worker_RunTestClient interface {
Send(*ClientArgs) error
Recv() (*ClientStatus, error)
grpc.ClientStream
}
type workerRunTestClient struct {
grpc.ClientStream
}
func (x *workerRunTestClient) Send(m *ClientArgs) error {
return x.ClientStream.SendMsg(m)
}
func (x *workerRunTestClient) Recv() (*ClientStatus, error) {
m := new(ClientStatus)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
func (c *workerClient) RunServer(ctx context.Context, opts ...grpc.CallOption) (Worker_RunServerClient, error) {
stream, err := grpc.NewClientStream(ctx, &_Worker_serviceDesc.Streams[1], c.cc, "/grpc.testing.Worker/RunServer", opts...)
if err != nil {
return nil, err
}
x := &workerRunServerClient{stream}
return x, nil
}
type Worker_RunServerClient interface {
Send(*ServerArgs) error
Recv() (*ServerStatus, error)
grpc.ClientStream
}
type workerRunServerClient struct {
grpc.ClientStream
}
func (x *workerRunServerClient) Send(m *ServerArgs) error {
return x.ClientStream.SendMsg(m)
}
func (x *workerRunServerClient) Recv() (*ServerStatus, error) {
m := new(ServerStatus)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
// Server API for Worker service
type WorkerServer interface {
// Start test with specified workload
RunTest(Worker_RunTestServer) error
// Start test with specified workload
RunServer(Worker_RunServerServer) error
}
func RegisterWorkerServer(s *grpc.Server, srv WorkerServer) {
s.RegisterService(&_Worker_serviceDesc, srv)
}
func _Worker_RunTest_Handler(srv interface{}, stream grpc.ServerStream) error {
return srv.(WorkerServer).RunTest(&workerRunTestServer{stream})
}
type Worker_RunTestServer interface {
Send(*ClientStatus) error
Recv() (*ClientArgs, error)
grpc.ServerStream
}
type workerRunTestServer struct {
grpc.ServerStream
}
func (x *workerRunTestServer) Send(m *ClientStatus) error {
return x.ServerStream.SendMsg(m)
}
func (x *workerRunTestServer) Recv() (*ClientArgs, error) {
m := new(ClientArgs)
if err := x.ServerStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
func _Worker_RunServer_Handler(srv interface{}, stream grpc.ServerStream) error {
return srv.(WorkerServer).RunServer(&workerRunServerServer{stream})
}
type Worker_RunServerServer interface {
Send(*ServerStatus) error
Recv() (*ServerArgs, error)
grpc.ServerStream
}
type workerRunServerServer struct {
grpc.ServerStream
}
func (x *workerRunServerServer) Send(m *ServerStatus) error {
return x.ServerStream.SendMsg(m)
}
func (x *workerRunServerServer) Recv() (*ServerArgs, error) {
m := new(ServerArgs)
if err := x.ServerStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
var _Worker_serviceDesc = grpc.ServiceDesc{
ServiceName: "grpc.testing.Worker",
HandlerType: (*WorkerServer)(nil),
Methods: []grpc.MethodDesc{},
Streams: []grpc.StreamDesc{
{
StreamName: "RunTest",
Handler: _Worker_RunTest_Handler,
ServerStreams: true,
ClientStreams: true,
},
{
StreamName: "RunServer",
Handler: _Worker_RunServer_Handler,
ServerStreams: true,
ClientStreams: true,
},
},
}
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/benchmark/grpc_testing/test.proto 0000664 0000000 0000000 00000006324 12603607541 0026202 0 ustar 00root root 0000000 0000000 // An integration test service that covers all the method signature permutations
// of unary/streaming requests/responses.
syntax = "proto3";
package grpc.testing;
enum PayloadType {
// Compressable text format.
COMPRESSABLE = 0;
// Uncompressable binary format.
UNCOMPRESSABLE = 1;
// Randomly chosen from all other formats defined in this enum.
RANDOM = 2;
}
message StatsRequest {
// run number
optional int32 test_num = 1;
}
message ServerStats {
// wall clock time
double time_elapsed = 1;
// user time used by the server process and threads
double time_user = 2;
// server time used by the server process and all threads
double time_system = 3;
}
message Payload {
// The type of data in body.
PayloadType type = 1;
// Primary contents of payload.
bytes body = 2;
}
message HistogramData {
repeated uint32 bucket = 1;
double min_seen = 2;
double max_seen = 3;
double sum = 4;
double sum_of_squares = 5;
double count = 6;
}
enum ClientType {
SYNCHRONOUS_CLIENT = 0;
ASYNC_CLIENT = 1;
}
enum ServerType {
SYNCHRONOUS_SERVER = 0;
ASYNC_SERVER = 1;
}
enum RpcType {
UNARY = 0;
STREAMING = 1;
}
message ClientConfig {
repeated string server_targets = 1;
ClientType client_type = 2;
bool enable_ssl = 3;
int32 outstanding_rpcs_per_channel = 4;
int32 client_channels = 5;
int32 payload_size = 6;
// only for async client:
int32 async_client_threads = 7;
RpcType rpc_type = 8;
}
// Request current stats
message Mark {}
message ClientArgs {
oneof argtype {
ClientConfig setup = 1;
Mark mark = 2;
}
}
message ClientStats {
HistogramData latencies = 1;
double time_elapsed = 3;
double time_user = 4;
double time_system = 5;
}
message ClientStatus {
ClientStats stats = 1;
}
message ServerConfig {
ServerType server_type = 1;
int32 threads = 2;
bool enable_ssl = 3;
}
message ServerArgs {
oneof argtype {
ServerConfig setup = 1;
Mark mark = 2;
}
}
message ServerStatus {
ServerStats stats = 1;
int32 port = 2;
}
message SimpleRequest {
// Desired payload type in the response from the server.
// If response_type is RANDOM, server randomly chooses one from other formats.
PayloadType response_type = 1;
// Desired payload size in the response from the server.
// If response_type is COMPRESSABLE, this denotes the size before compression.
int32 response_size = 2;
// Optional input payload sent along with the request.
Payload payload = 3;
}
message SimpleResponse {
Payload payload = 1;
}
service TestService {
// One request followed by one response.
// The server returns the client payload as-is.
rpc UnaryCall(SimpleRequest) returns (SimpleResponse);
// One request followed by one response.
// The server returns the client payload as-is.
rpc StreamingCall(stream SimpleRequest) returns (stream SimpleResponse);
}
service Worker {
// Start test with specified workload
rpc RunTest(stream ClientArgs) returns (stream ClientStatus);
// Start test with specified workload
rpc RunServer(stream ServerArgs) returns (stream ServerStatus);
}
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/benchmark/server/ 0000775 0000000 0000000 00000000000 12603607541 0022747 5 ustar 00root root 0000000 0000000 grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/benchmark/server/main.go 0000664 0000000 0000000 00000001407 12603607541 0024224 0 ustar 00root root 0000000 0000000 package main
import (
"flag"
"math"
"net"
"net/http"
_ "net/http/pprof"
"time"
"google.golang.org/grpc/benchmark"
"google.golang.org/grpc/grpclog"
)
var (
duration = flag.Int("duration", math.MaxInt32, "The duration in seconds to run the benchmark server")
)
func main() {
flag.Parse()
go func() {
lis, err := net.Listen("tcp", ":0")
if err != nil {
grpclog.Fatalf("Failed to listen: %v", err)
}
grpclog.Println("Server profiling address: ", lis.Addr().String())
if err := http.Serve(lis, nil); err != nil {
grpclog.Fatalf("Failed to serve: %v", err)
}
}()
addr, stopper := benchmark.StartServer(":0") // listen on all interfaces
grpclog.Println("Server Address: ", addr)
<-time.After(time.Duration(*duration) * time.Second)
stopper()
}
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/benchmark/stats/ 0000775 0000000 0000000 00000000000 12603607541 0022577 5 ustar 00root root 0000000 0000000 grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/benchmark/stats/counter.go 0000664 0000000 0000000 00000005420 12603607541 0024606 0 ustar 00root root 0000000 0000000 package stats
import (
"sync"
"time"
)
var (
// TimeNow is used for testing.
TimeNow = time.Now
)
const (
hour = 0
tenminutes = 1
minute = 2
)
// Counter is a counter that keeps track of its recent values over a given
// period of time, and with a given resolution. Use newCounter() to instantiate.
type Counter struct {
mu sync.RWMutex
ts [3]*timeseries
lastUpdate time.Time
}
// newCounter returns a new Counter.
func newCounter() *Counter {
now := TimeNow()
c := &Counter{}
c.ts[hour] = newTimeSeries(now, time.Hour, time.Minute)
c.ts[tenminutes] = newTimeSeries(now, 10*time.Minute, 10*time.Second)
c.ts[minute] = newTimeSeries(now, time.Minute, time.Second)
return c
}
func (c *Counter) advance() time.Time {
now := TimeNow()
for _, ts := range c.ts {
ts.advanceTime(now)
}
return now
}
// Value returns the current value of the counter.
func (c *Counter) Value() int64 {
c.mu.RLock()
defer c.mu.RUnlock()
return c.ts[minute].headValue()
}
// LastUpdate returns the last update time of the counter.
func (c *Counter) LastUpdate() time.Time {
c.mu.RLock()
defer c.mu.RUnlock()
return c.lastUpdate
}
// Set updates the current value of the counter.
func (c *Counter) Set(value int64) {
c.mu.Lock()
defer c.mu.Unlock()
c.lastUpdate = c.advance()
for _, ts := range c.ts {
ts.set(value)
}
}
// Incr increments the current value of the counter by 'delta'.
func (c *Counter) Incr(delta int64) {
c.mu.Lock()
defer c.mu.Unlock()
c.lastUpdate = c.advance()
for _, ts := range c.ts {
ts.incr(delta)
}
}
// Delta1h returns the delta for the last hour.
func (c *Counter) Delta1h() int64 {
c.mu.RLock()
defer c.mu.RUnlock()
c.advance()
return c.ts[hour].delta()
}
// Delta10m returns the delta for the last 10 minutes.
func (c *Counter) Delta10m() int64 {
c.mu.RLock()
defer c.mu.RUnlock()
c.advance()
return c.ts[tenminutes].delta()
}
// Delta1m returns the delta for the last minute.
func (c *Counter) Delta1m() int64 {
c.mu.RLock()
defer c.mu.RUnlock()
c.advance()
return c.ts[minute].delta()
}
// Rate1h returns the rate of change of the counter in the last hour.
func (c *Counter) Rate1h() float64 {
c.mu.RLock()
defer c.mu.RUnlock()
c.advance()
return c.ts[hour].rate()
}
// Rate10m returns the rate of change of the counter in the last 10 minutes.
func (c *Counter) Rate10m() float64 {
c.mu.RLock()
defer c.mu.RUnlock()
c.advance()
return c.ts[tenminutes].rate()
}
// Rate1m returns the rate of change of the counter in the last minute.
func (c *Counter) Rate1m() float64 {
c.mu.RLock()
defer c.mu.RUnlock()
c.advance()
return c.ts[minute].rate()
}
// Reset resets the counter to an empty state.
func (c *Counter) Reset() {
c.mu.Lock()
defer c.mu.Unlock()
now := TimeNow()
for _, ts := range c.ts {
ts.reset(now)
}
}
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/benchmark/stats/histogram.go 0000664 0000000 0000000 00000015000 12603607541 0025117 0 ustar 00root root 0000000 0000000 package stats
import (
"bytes"
"fmt"
"io"
"strconv"
"strings"
"time"
)
// HistogramValue is the value of Histogram objects.
type HistogramValue struct {
// Count is the total number of values added to the histogram.
Count int64
// Sum is the sum of all the values added to the histogram.
Sum int64
// Min is the minimum of all the values added to the histogram.
Min int64
// Max is the maximum of all the values added to the histogram.
Max int64
// Buckets contains all the buckets of the histogram.
Buckets []HistogramBucket
}
// HistogramBucket is one histogram bucket.
type HistogramBucket struct {
// LowBound is the lower bound of the bucket.
LowBound int64
// Count is the number of values in the bucket.
Count int64
}
// Print writes textual output of the histogram values.
func (v HistogramValue) Print(w io.Writer) {
avg := float64(v.Sum) / float64(v.Count)
fmt.Fprintf(w, "Count: %d Min: %d Max: %d Avg: %.2f\n", v.Count, v.Min, v.Max, avg)
fmt.Fprintf(w, "%s\n", strings.Repeat("-", 60))
if v.Count <= 0 {
return
}
maxBucketDigitLen := len(strconv.FormatInt(v.Buckets[len(v.Buckets)-1].LowBound, 10))
if maxBucketDigitLen < 3 {
// For "inf".
maxBucketDigitLen = 3
}
maxCountDigitLen := len(strconv.FormatInt(v.Count, 10))
percentMulti := 100 / float64(v.Count)
accCount := int64(0)
for i, b := range v.Buckets {
fmt.Fprintf(w, "[%*d, ", maxBucketDigitLen, b.LowBound)
if i+1 < len(v.Buckets) {
fmt.Fprintf(w, "%*d)", maxBucketDigitLen, v.Buckets[i+1].LowBound)
} else {
fmt.Fprintf(w, "%*s)", maxBucketDigitLen, "inf")
}
accCount += b.Count
fmt.Fprintf(w, " %*d %5.1f%% %5.1f%%", maxCountDigitLen, b.Count, float64(b.Count)*percentMulti, float64(accCount)*percentMulti)
const barScale = 0.1
barLength := int(float64(b.Count)*percentMulti*barScale + 0.5)
fmt.Fprintf(w, " %s\n", strings.Repeat("#", barLength))
}
}
// String returns the textual output of the histogram values as string.
func (v HistogramValue) String() string {
var b bytes.Buffer
v.Print(&b)
return b.String()
}
// A Histogram accumulates values in the form of a histogram. The type of the
// values is int64, which is suitable for keeping track of things like RPC
// latency in milliseconds. New histogram objects should be obtained via the
// New() function.
type Histogram struct {
opts HistogramOptions
buckets []bucketInternal
count *Counter
sum *Counter
tracker *Tracker
}
// HistogramOptions contains the parameters that define the histogram's buckets.
type HistogramOptions struct {
// NumBuckets is the number of buckets.
NumBuckets int
// GrowthFactor is the growth factor of the buckets. A value of 0.1
// indicates that bucket N+1 will be 10% larger than bucket N.
GrowthFactor float64
// SmallestBucketSize is the size of the first bucket. Bucket sizes are
// rounded down to the nearest integer.
SmallestBucketSize float64
// MinValue is the lower bound of the first bucket.
MinValue int64
}
// bucketInternal is the internal representation of a bucket, which includes a
// rate counter.
type bucketInternal struct {
lowBound int64
count *Counter
}
// NewHistogram returns a pointer to a new Histogram object that was created
// with the provided options.
func NewHistogram(opts HistogramOptions) *Histogram {
if opts.NumBuckets == 0 {
opts.NumBuckets = 32
}
if opts.SmallestBucketSize == 0.0 {
opts.SmallestBucketSize = 1.0
}
h := Histogram{
opts: opts,
buckets: make([]bucketInternal, opts.NumBuckets),
count: newCounter(),
sum: newCounter(),
tracker: newTracker(),
}
low := opts.MinValue
delta := opts.SmallestBucketSize
for i := 0; i < opts.NumBuckets; i++ {
h.buckets[i].lowBound = low
h.buckets[i].count = newCounter()
low = low + int64(delta)
delta = delta * (1.0 + opts.GrowthFactor)
}
return &h
}
// Opts returns a copy of the options used to create the Histogram.
func (h *Histogram) Opts() HistogramOptions {
return h.opts
}
// Add adds a value to the histogram.
func (h *Histogram) Add(value int64) error {
bucket, err := h.findBucket(value)
if err != nil {
return err
}
h.buckets[bucket].count.Incr(1)
h.count.Incr(1)
h.sum.Incr(value)
h.tracker.Push(value)
return nil
}
// LastUpdate returns the time at which the object was last updated.
func (h *Histogram) LastUpdate() time.Time {
return h.count.LastUpdate()
}
// Value returns the accumulated state of the histogram since it was created.
func (h *Histogram) Value() HistogramValue {
b := make([]HistogramBucket, len(h.buckets))
for i, v := range h.buckets {
b[i] = HistogramBucket{
LowBound: v.lowBound,
Count: v.count.Value(),
}
}
v := HistogramValue{
Count: h.count.Value(),
Sum: h.sum.Value(),
Min: h.tracker.Min(),
Max: h.tracker.Max(),
Buckets: b,
}
return v
}
// Delta1h returns the change in the last hour.
func (h *Histogram) Delta1h() HistogramValue {
b := make([]HistogramBucket, len(h.buckets))
for i, v := range h.buckets {
b[i] = HistogramBucket{
LowBound: v.lowBound,
Count: v.count.Delta1h(),
}
}
v := HistogramValue{
Count: h.count.Delta1h(),
Sum: h.sum.Delta1h(),
Min: h.tracker.Min1h(),
Max: h.tracker.Max1h(),
Buckets: b,
}
return v
}
// Delta10m returns the change in the last 10 minutes.
func (h *Histogram) Delta10m() HistogramValue {
b := make([]HistogramBucket, len(h.buckets))
for i, v := range h.buckets {
b[i] = HistogramBucket{
LowBound: v.lowBound,
Count: v.count.Delta10m(),
}
}
v := HistogramValue{
Count: h.count.Delta10m(),
Sum: h.sum.Delta10m(),
Min: h.tracker.Min10m(),
Max: h.tracker.Max10m(),
Buckets: b,
}
return v
}
// Delta1m returns the change in the last 10 minutes.
func (h *Histogram) Delta1m() HistogramValue {
b := make([]HistogramBucket, len(h.buckets))
for i, v := range h.buckets {
b[i] = HistogramBucket{
LowBound: v.lowBound,
Count: v.count.Delta1m(),
}
}
v := HistogramValue{
Count: h.count.Delta1m(),
Sum: h.sum.Delta1m(),
Min: h.tracker.Min1m(),
Max: h.tracker.Max1m(),
Buckets: b,
}
return v
}
// findBucket does a binary search to find in which bucket the value goes.
func (h *Histogram) findBucket(value int64) (int, error) {
lastBucket := len(h.buckets) - 1
min, max := 0, lastBucket
for max >= min {
b := (min + max) / 2
if value >= h.buckets[b].lowBound && (b == lastBucket || value < h.buckets[b+1].lowBound) {
return b, nil
}
if value < h.buckets[b].lowBound {
max = b - 1
continue
}
min = b + 1
}
return 0, fmt.Errorf("no bucket for value: %d", value)
}
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/benchmark/stats/stats.go 0000664 0000000 0000000 00000005333 12603607541 0024270 0 ustar 00root root 0000000 0000000 package stats
import (
"bytes"
"fmt"
"io"
"math"
"time"
)
// Stats is a simple helper for gathering additional statistics like histogram
// during benchmarks. This is not thread safe.
type Stats struct {
numBuckets int
unit time.Duration
min, max int64
histogram *Histogram
durations durationSlice
dirty bool
}
type durationSlice []time.Duration
// NewStats creates a new Stats instance. If numBuckets is not positive,
// the default value (16) will be used.
func NewStats(numBuckets int) *Stats {
if numBuckets <= 0 {
numBuckets = 16
}
return &Stats{
// Use one more bucket for the last unbounded bucket.
numBuckets: numBuckets + 1,
durations: make(durationSlice, 0, 100000),
}
}
// Add adds an elapsed time per operation to the stats.
func (stats *Stats) Add(d time.Duration) {
stats.durations = append(stats.durations, d)
stats.dirty = true
}
// Clear resets the stats, removing all values.
func (stats *Stats) Clear() {
stats.durations = stats.durations[:0]
stats.histogram = nil
stats.dirty = false
}
// maybeUpdate updates internal stat data if there was any newly added
// stats since this was updated.
func (stats *Stats) maybeUpdate() {
if !stats.dirty {
return
}
stats.min = math.MaxInt64
stats.max = 0
for _, d := range stats.durations {
if stats.min > int64(d) {
stats.min = int64(d)
}
if stats.max < int64(d) {
stats.max = int64(d)
}
}
// Use the largest unit that can represent the minimum time duration.
stats.unit = time.Nanosecond
for _, u := range []time.Duration{time.Microsecond, time.Millisecond, time.Second} {
if stats.min <= int64(u) {
break
}
stats.unit = u
}
// Adjust the min/max according to the new unit.
stats.min /= int64(stats.unit)
stats.max /= int64(stats.unit)
numBuckets := stats.numBuckets
if n := int(stats.max - stats.min + 1); n < numBuckets {
numBuckets = n
}
stats.histogram = NewHistogram(HistogramOptions{
NumBuckets: numBuckets,
// max(i.e., Nth lower bound) = min + (1 + growthFactor)^(numBuckets-2).
GrowthFactor: math.Pow(float64(stats.max-stats.min), 1/float64(stats.numBuckets-2)) - 1,
SmallestBucketSize: 1.0,
MinValue: stats.min})
for _, d := range stats.durations {
stats.histogram.Add(int64(d / stats.unit))
}
stats.dirty = false
}
// Print writes textual output of the Stats.
func (stats *Stats) Print(w io.Writer) {
stats.maybeUpdate()
if stats.histogram == nil {
fmt.Fprint(w, "Histogram (empty)\n")
} else {
fmt.Fprintf(w, "Histogram (unit: %s)\n", fmt.Sprintf("%v", stats.unit)[1:])
stats.histogram.Value().Print(w)
}
}
// String returns the textual output of the Stats as string.
func (stats *Stats) String() string {
var b bytes.Buffer
stats.Print(&b)
return b.String()
}
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/benchmark/stats/timeseries.go 0000664 0000000 0000000 00000010343 12603607541 0025300 0 ustar 00root root 0000000 0000000 package stats
import (
"math"
"time"
)
// timeseries holds the history of a changing value over a predefined period of
// time.
type timeseries struct {
size int // The number of time slots. Equivalent to len(slots).
resolution time.Duration // The time resolution of each slot.
stepCount int64 // The number of intervals seen since creation.
head int // The position of the current time in slots.
time time.Time // The time at the beginning of the current time slot.
slots []int64 // A circular buffer of time slots.
}
// newTimeSeries returns a newly allocated timeseries that covers the requested
// period with the given resolution.
func newTimeSeries(initialTime time.Time, period, resolution time.Duration) *timeseries {
size := int(period.Nanoseconds()/resolution.Nanoseconds()) + 1
return ×eries{
size: size,
resolution: resolution,
stepCount: 1,
time: initialTime,
slots: make([]int64, size),
}
}
// advanceTimeWithFill moves the timeseries forward to time t and fills in any
// slots that get skipped in the process with the given value. Values older than
// the timeseries period are lost.
func (ts *timeseries) advanceTimeWithFill(t time.Time, value int64) {
advanceTo := t.Truncate(ts.resolution)
if !advanceTo.After(ts.time) {
// This is shortcut for the most common case of a busy counter
// where updates come in many times per ts.resolution.
ts.time = advanceTo
return
}
steps := int(advanceTo.Sub(ts.time).Nanoseconds() / ts.resolution.Nanoseconds())
ts.stepCount += int64(steps)
if steps > ts.size {
steps = ts.size
}
for steps > 0 {
ts.head = (ts.head + 1) % ts.size
ts.slots[ts.head] = value
steps--
}
ts.time = advanceTo
}
// advanceTime moves the timeseries forward to time t and fills in any slots
// that get skipped in the process with the head value. Values older than the
// timeseries period are lost.
func (ts *timeseries) advanceTime(t time.Time) {
ts.advanceTimeWithFill(t, ts.slots[ts.head])
}
// set sets the current value of the timeseries.
func (ts *timeseries) set(value int64) {
ts.slots[ts.head] = value
}
// incr sets the current value of the timeseries.
func (ts *timeseries) incr(delta int64) {
ts.slots[ts.head] += delta
}
// headValue returns the latest value from the timeseries.
func (ts *timeseries) headValue() int64 {
return ts.slots[ts.head]
}
// headTime returns the time of the latest value from the timeseries.
func (ts *timeseries) headTime() time.Time {
return ts.time
}
// tailValue returns the oldest value from the timeseries.
func (ts *timeseries) tailValue() int64 {
if ts.stepCount < int64(ts.size) {
return 0
}
return ts.slots[(ts.head+1)%ts.size]
}
// tailTime returns the time of the oldest value from the timeseries.
func (ts *timeseries) tailTime() time.Time {
size := int64(ts.size)
if ts.stepCount < size {
size = ts.stepCount
}
return ts.time.Add(-time.Duration(size-1) * ts.resolution)
}
// delta returns the difference between the newest and oldest values from the
// timeseries.
func (ts *timeseries) delta() int64 {
return ts.headValue() - ts.tailValue()
}
// rate returns the rate of change between the oldest and newest values from
// the timeseries in units per second.
func (ts *timeseries) rate() float64 {
deltaTime := ts.headTime().Sub(ts.tailTime()).Seconds()
if deltaTime == 0 {
return 0
}
return float64(ts.delta()) / deltaTime
}
// min returns the smallest value from the timeseries.
func (ts *timeseries) min() int64 {
to := ts.size
if ts.stepCount < int64(ts.size) {
to = ts.head + 1
}
tail := (ts.head + 1) % ts.size
min := int64(math.MaxInt64)
for b := 0; b < to; b++ {
if b != tail && ts.slots[b] < min {
min = ts.slots[b]
}
}
return min
}
// max returns the largest value from the timeseries.
func (ts *timeseries) max() int64 {
to := ts.size
if ts.stepCount < int64(ts.size) {
to = ts.head + 1
}
tail := (ts.head + 1) % ts.size
max := int64(math.MinInt64)
for b := 0; b < to; b++ {
if b != tail && ts.slots[b] > max {
max = ts.slots[b]
}
}
return max
}
// reset resets the timeseries to an empty state.
func (ts *timeseries) reset(t time.Time) {
ts.head = 0
ts.time = t
ts.stepCount = 1
ts.slots = make([]int64, ts.size)
}
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/benchmark/stats/tracker.go 0000664 0000000 0000000 00000006611 12603607541 0024565 0 ustar 00root root 0000000 0000000 package stats
import (
"math"
"sync"
"time"
)
// Tracker is a min/max value tracker that keeps track of its min/max values
// over a given period of time, and with a given resolution. The initial min
// and max values are math.MaxInt64 and math.MinInt64 respectively.
type Tracker struct {
mu sync.RWMutex
min, max int64 // All time min/max.
minTS, maxTS [3]*timeseries
lastUpdate time.Time
}
// newTracker returns a new Tracker.
func newTracker() *Tracker {
now := TimeNow()
t := &Tracker{}
t.minTS[hour] = newTimeSeries(now, time.Hour, time.Minute)
t.minTS[tenminutes] = newTimeSeries(now, 10*time.Minute, 10*time.Second)
t.minTS[minute] = newTimeSeries(now, time.Minute, time.Second)
t.maxTS[hour] = newTimeSeries(now, time.Hour, time.Minute)
t.maxTS[tenminutes] = newTimeSeries(now, 10*time.Minute, 10*time.Second)
t.maxTS[minute] = newTimeSeries(now, time.Minute, time.Second)
t.init()
return t
}
func (t *Tracker) init() {
t.min = math.MaxInt64
t.max = math.MinInt64
for _, ts := range t.minTS {
ts.set(math.MaxInt64)
}
for _, ts := range t.maxTS {
ts.set(math.MinInt64)
}
}
func (t *Tracker) advance() time.Time {
now := TimeNow()
for _, ts := range t.minTS {
ts.advanceTimeWithFill(now, math.MaxInt64)
}
for _, ts := range t.maxTS {
ts.advanceTimeWithFill(now, math.MinInt64)
}
return now
}
// LastUpdate returns the last update time of the range.
func (t *Tracker) LastUpdate() time.Time {
t.mu.RLock()
defer t.mu.RUnlock()
return t.lastUpdate
}
// Push adds a new value if it is a new minimum or maximum.
func (t *Tracker) Push(value int64) {
t.mu.Lock()
defer t.mu.Unlock()
t.lastUpdate = t.advance()
if t.min > value {
t.min = value
}
if t.max < value {
t.max = value
}
for _, ts := range t.minTS {
if ts.headValue() > value {
ts.set(value)
}
}
for _, ts := range t.maxTS {
if ts.headValue() < value {
ts.set(value)
}
}
}
// Min returns the minimum value of the tracker
func (t *Tracker) Min() int64 {
t.mu.RLock()
defer t.mu.RUnlock()
return t.min
}
// Max returns the maximum value of the tracker.
func (t *Tracker) Max() int64 {
t.mu.RLock()
defer t.mu.RUnlock()
return t.max
}
// Min1h returns the minimum value for the last hour.
func (t *Tracker) Min1h() int64 {
t.mu.Lock()
defer t.mu.Unlock()
t.advance()
return t.minTS[hour].min()
}
// Max1h returns the maximum value for the last hour.
func (t *Tracker) Max1h() int64 {
t.mu.Lock()
defer t.mu.Unlock()
t.advance()
return t.maxTS[hour].max()
}
// Min10m returns the minimum value for the last 10 minutes.
func (t *Tracker) Min10m() int64 {
t.mu.Lock()
defer t.mu.Unlock()
t.advance()
return t.minTS[tenminutes].min()
}
// Max10m returns the maximum value for the last 10 minutes.
func (t *Tracker) Max10m() int64 {
t.mu.Lock()
defer t.mu.Unlock()
t.advance()
return t.maxTS[tenminutes].max()
}
// Min1m returns the minimum value for the last 1 minute.
func (t *Tracker) Min1m() int64 {
t.mu.Lock()
defer t.mu.Unlock()
t.advance()
return t.minTS[minute].min()
}
// Max1m returns the maximum value for the last 1 minute.
func (t *Tracker) Max1m() int64 {
t.mu.Lock()
defer t.mu.Unlock()
t.advance()
return t.maxTS[minute].max()
}
// Reset resets the range to an empty state.
func (t *Tracker) Reset() {
t.mu.Lock()
defer t.mu.Unlock()
now := TimeNow()
for _, ts := range t.minTS {
ts.reset(now)
}
for _, ts := range t.maxTS {
ts.reset(now)
}
t.init()
}
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/benchmark/stats/util.go 0000664 0000000 0000000 00000011604 12603607541 0024105 0 ustar 00root root 0000000 0000000 package stats
import (
"bufio"
"bytes"
"fmt"
"os"
"runtime"
"sort"
"strings"
"sync"
"testing"
)
var (
curB *testing.B
curBenchName string
curStats map[string]*Stats
orgStdout *os.File
nextOutPos int
injectCond *sync.Cond
injectDone chan struct{}
)
// AddStats adds a new unnamed Stats instance to the current benchmark. You need
// to run benchmarks by calling RunTestMain() to inject the stats to the
// benchmark results. If numBuckets is not positive, the default value (16) will
// be used. Please note that this calls b.ResetTimer() since it may be blocked
// until the previous benchmark stats is printed out. So AddStats() should
// typically be called at the very beginning of each benchmark function.
func AddStats(b *testing.B, numBuckets int) *Stats {
return AddStatsWithName(b, "", numBuckets)
}
// AddStatsWithName adds a new named Stats instance to the current benchmark.
// With this, you can add multiple stats in a single benchmark. You need
// to run benchmarks by calling RunTestMain() to inject the stats to the
// benchmark results. If numBuckets is not positive, the default value (16) will
// be used. Please note that this calls b.ResetTimer() since it may be blocked
// until the previous benchmark stats is printed out. So AddStatsWithName()
// should typically be called at the very beginning of each benchmark function.
func AddStatsWithName(b *testing.B, name string, numBuckets int) *Stats {
var benchName string
for i := 1; ; i++ {
pc, _, _, ok := runtime.Caller(i)
if !ok {
panic("benchmark function not found")
}
p := strings.Split(runtime.FuncForPC(pc).Name(), ".")
benchName = p[len(p)-1]
if strings.HasPrefix(benchName, "Benchmark") {
break
}
}
procs := runtime.GOMAXPROCS(-1)
if procs != 1 {
benchName = fmt.Sprintf("%s-%d", benchName, procs)
}
stats := NewStats(numBuckets)
if injectCond != nil {
// We need to wait until the previous benchmark stats is printed out.
injectCond.L.Lock()
for curB != nil && curBenchName != benchName {
injectCond.Wait()
}
curB = b
curBenchName = benchName
curStats[name] = stats
injectCond.L.Unlock()
}
b.ResetTimer()
return stats
}
// RunTestMain runs the tests with enabling injection of benchmark stats. It
// returns an exit code to pass to os.Exit.
func RunTestMain(m *testing.M) int {
startStatsInjector()
defer stopStatsInjector()
return m.Run()
}
// startStatsInjector starts stats injection to benchmark results.
func startStatsInjector() {
orgStdout = os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
nextOutPos = 0
resetCurBenchStats()
injectCond = sync.NewCond(&sync.Mutex{})
injectDone = make(chan struct{})
go func() {
defer close(injectDone)
scanner := bufio.NewScanner(r)
scanner.Split(splitLines)
for scanner.Scan() {
injectStatsIfFinished(scanner.Text())
}
if err := scanner.Err(); err != nil {
panic(err)
}
}()
}
// stopStatsInjector stops stats injection and restores os.Stdout.
func stopStatsInjector() {
os.Stdout.Close()
<-injectDone
injectCond = nil
os.Stdout = orgStdout
}
// splitLines is a split function for a bufio.Scanner that returns each line
// of text, teeing texts to the original stdout even before each line ends.
func splitLines(data []byte, eof bool) (advance int, token []byte, err error) {
if eof && len(data) == 0 {
return 0, nil, nil
}
if i := bytes.IndexByte(data, '\n'); i >= 0 {
orgStdout.Write(data[nextOutPos : i+1])
nextOutPos = 0
return i + 1, data[0:i], nil
}
orgStdout.Write(data[nextOutPos:])
nextOutPos = len(data)
if eof {
// This is a final, non-terminated line. Return it.
return len(data), data, nil
}
return 0, nil, nil
}
// injectStatsIfFinished prints out the stats if the current benchmark finishes.
func injectStatsIfFinished(line string) {
injectCond.L.Lock()
defer injectCond.L.Unlock()
// We assume that the benchmark results start with the benchmark name.
if curB == nil || !strings.HasPrefix(line, curBenchName) {
return
}
if !curB.Failed() {
// Output all stats in alphabetical order.
names := make([]string, 0, len(curStats))
for name := range curStats {
names = append(names, name)
}
sort.Strings(names)
for _, name := range names {
stats := curStats[name]
// The output of stats starts with a header like "Histogram (unit: ms)"
// followed by statistical properties and the buckets. Add the stats name
// if it is a named stats and indent them as Go testing outputs.
lines := strings.Split(stats.String(), "\n")
if n := len(lines); n > 0 {
if name != "" {
name = ": " + name
}
fmt.Fprintf(orgStdout, "--- %s%s\n", lines[0], name)
for _, line := range lines[1 : n-1] {
fmt.Fprintf(orgStdout, "\t%s\n", line)
}
}
}
}
resetCurBenchStats()
injectCond.Signal()
}
// resetCurBenchStats resets the current benchmark stats.
func resetCurBenchStats() {
curB = nil
curBenchName = ""
curStats = make(map[string]*Stats)
}
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/call.go 0000664 0000000 0000000 00000013551 12603607541 0020756 0 ustar 00root root 0000000 0000000 /*
*
* Copyright 2014, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
package grpc
import (
"io"
"time"
"golang.org/x/net/context"
"golang.org/x/net/trace"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/transport"
)
// recvResponse receives and parses an RPC response.
// On error, it returns the error and indicates whether the call should be retried.
//
// TODO(zhaoq): Check whether the received message sequence is valid.
func recvResponse(codec Codec, t transport.ClientTransport, c *callInfo, stream *transport.Stream, reply interface{}) error {
// Try to acquire header metadata from the server if there is any.
var err error
c.headerMD, err = stream.Header()
if err != nil {
return err
}
p := &parser{s: stream}
for {
if err = recv(p, codec, reply); err != nil {
if err == io.EOF {
break
}
return err
}
}
c.trailerMD = stream.Trailer()
return nil
}
// sendRequest writes out various information of an RPC such as Context and Message.
func sendRequest(ctx context.Context, codec Codec, callHdr *transport.CallHdr, t transport.ClientTransport, args interface{}, opts *transport.Options) (_ *transport.Stream, err error) {
stream, err := t.NewStream(ctx, callHdr)
if err != nil {
return nil, err
}
defer func() {
if err != nil {
if _, ok := err.(transport.ConnectionError); !ok {
t.CloseStream(stream, err)
}
}
}()
// TODO(zhaoq): Support compression.
outBuf, err := encode(codec, args, compressionNone)
if err != nil {
return nil, transport.StreamErrorf(codes.Internal, "grpc: %v", err)
}
err = t.Write(stream, outBuf, opts)
if err != nil {
return nil, err
}
// Sent successfully.
return stream, nil
}
// callInfo contains all related configuration and information about an RPC.
type callInfo struct {
failFast bool
headerMD metadata.MD
trailerMD metadata.MD
traceInfo traceInfo // in trace.go
}
// Invoke is called by the generated code. It sends the RPC request on the
// wire and returns after response is received.
func Invoke(ctx context.Context, method string, args, reply interface{}, cc *ClientConn, opts ...CallOption) (err error) {
var c callInfo
for _, o := range opts {
if err := o.before(&c); err != nil {
return toRPCErr(err)
}
}
defer func() {
for _, o := range opts {
o.after(&c)
}
}()
if EnableTracing {
c.traceInfo.tr = trace.New("grpc.Sent."+transport.MethodFamily(method), method)
defer c.traceInfo.tr.Finish()
c.traceInfo.firstLine.client = true
if deadline, ok := ctx.Deadline(); ok {
c.traceInfo.firstLine.deadline = deadline.Sub(time.Now())
}
c.traceInfo.tr.LazyLog(&c.traceInfo.firstLine, false)
// TODO(dsymonds): Arrange for c.traceInfo.firstLine.remoteAddr to be set.
defer func() {
if err != nil {
c.traceInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true)
c.traceInfo.tr.SetError()
}
}()
}
topts := &transport.Options{
Last: true,
Delay: false,
}
var (
lastErr error // record the error that happened
)
for {
var (
err error
t transport.ClientTransport
stream *transport.Stream
conn *Conn
)
// TODO(zhaoq): Need a formal spec of retry strategy for non-failfast rpcs.
if lastErr != nil && c.failFast {
return toRPCErr(lastErr)
}
conn, err = cc.dopts.picker.Pick()
if err != nil {
return toRPCErr(err)
}
callHdr := &transport.CallHdr{
Host: conn.authority,
Method: method,
}
t, err = conn.Wait(ctx)
if err != nil {
if err == ErrTransientFailure {
continue
}
if lastErr != nil {
// This was a retry; return the error from the last attempt.
return toRPCErr(lastErr)
}
return toRPCErr(err)
}
if c.traceInfo.tr != nil {
c.traceInfo.tr.LazyLog(&payload{sent: true, msg: args}, true)
}
stream, err = sendRequest(ctx, conn.dopts.codec, callHdr, t, args, topts)
if err != nil {
if _, ok := err.(transport.ConnectionError); ok {
lastErr = err
continue
}
if lastErr != nil {
return toRPCErr(lastErr)
}
return toRPCErr(err)
}
// Receive the response
lastErr = recvResponse(conn.dopts.codec, t, &c, stream, reply)
if _, ok := lastErr.(transport.ConnectionError); ok {
continue
}
if c.traceInfo.tr != nil {
c.traceInfo.tr.LazyLog(&payload{sent: false, msg: reply}, true)
}
t.CloseStream(stream, lastErr)
if lastErr != nil {
return toRPCErr(lastErr)
}
return Errorf(stream.StatusCode(), stream.StatusDesc())
}
}
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/clientconn.go 0000664 0000000 0000000 00000035133 12603607541 0022177 0 ustar 00root root 0000000 0000000 /*
*
* Copyright 2014, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
package grpc
import (
"errors"
"fmt"
"net"
"strings"
"sync"
"time"
"golang.org/x/net/context"
"golang.org/x/net/trace"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/grpclog"
"google.golang.org/grpc/transport"
)
var (
// ErrUnspecTarget indicates that the target address is unspecified.
ErrUnspecTarget = errors.New("grpc: target is unspecified")
// ErrNoTransportSecurity indicates that there is no transport security
// being set for ClientConn. Users should either set one or explicityly
// call WithInsecure DialOption to disable security.
ErrNoTransportSecurity = errors.New("grpc: no transport security set (use grpc.WithInsecure() explicitly or set credentials)")
// ErrCredentialsMisuse indicates that users want to transmit security infomation
// (e.g., oauth2 token) which requires secure connection on an insecure
// connection.
ErrCredentialsMisuse = errors.New("grpc: the credentials require transport level security (use grpc.WithTransportAuthenticator() to set)")
// ErrClientConnClosing indicates that the operation is illegal because
// the session is closing.
ErrClientConnClosing = errors.New("grpc: the client connection is closing")
// ErrClientConnTimeout indicates that the connection could not be
// established or re-established within the specified timeout.
ErrClientConnTimeout = errors.New("grpc: timed out trying to connect")
// ErrTransientFailure indicates the connection failed due to a transient error.
ErrTransientFailure = errors.New("grpc: transient connection failure")
// minimum time to give a connection to complete
minConnectTimeout = 20 * time.Second
)
// dialOptions configure a Dial call. dialOptions are set by the DialOption
// values passed to Dial.
type dialOptions struct {
codec Codec
picker Picker
block bool
insecure bool
copts transport.ConnectOptions
}
// DialOption configures how we set up the connection.
type DialOption func(*dialOptions)
// WithCodec returns a DialOption which sets a codec for message marshaling and unmarshaling.
func WithCodec(c Codec) DialOption {
return func(o *dialOptions) {
o.codec = c
}
}
// WithBlock returns a DialOption which makes caller of Dial blocks until the underlying
// connection is up. Without this, Dial returns immediately and connecting the server
// happens in background.
func WithBlock() DialOption {
return func(o *dialOptions) {
o.block = true
}
}
func WithInsecure() DialOption {
return func(o *dialOptions) {
o.insecure = true
}
}
// WithTransportCredentials returns a DialOption which configures a
// connection level security credentials (e.g., TLS/SSL).
func WithTransportCredentials(creds credentials.TransportAuthenticator) DialOption {
return func(o *dialOptions) {
o.copts.AuthOptions = append(o.copts.AuthOptions, creds)
}
}
// WithPerRPCCredentials returns a DialOption which sets
// credentials which will place auth state on each outbound RPC.
func WithPerRPCCredentials(creds credentials.Credentials) DialOption {
return func(o *dialOptions) {
o.copts.AuthOptions = append(o.copts.AuthOptions, creds)
}
}
// WithTimeout returns a DialOption that configures a timeout for dialing a client connection.
func WithTimeout(d time.Duration) DialOption {
return func(o *dialOptions) {
o.copts.Timeout = d
}
}
// WithDialer returns a DialOption that specifies a function to use for dialing network addresses.
func WithDialer(f func(addr string, timeout time.Duration) (net.Conn, error)) DialOption {
return func(o *dialOptions) {
o.copts.Dialer = f
}
}
// WithUserAgent returns a DialOption that specifies a user agent string for all the RPCs.
func WithUserAgent(s string) DialOption {
return func(o *dialOptions) {
o.copts.UserAgent = s
}
}
// Dial creates a client connection the given target.
func Dial(target string, opts ...DialOption) (*ClientConn, error) {
cc := &ClientConn{
target: target,
}
for _, opt := range opts {
opt(&cc.dopts)
}
if cc.dopts.picker == nil {
cc.dopts.picker = &unicastPicker{}
}
if err := cc.dopts.picker.Init(cc); err != nil {
return nil, err
}
return cc, nil
}
// ConnectivityState indicates the state of a client connection.
type ConnectivityState int
const (
// Idle indicates the ClientConn is idle.
Idle ConnectivityState = iota
// Connecting indicates the ClienConn is connecting.
Connecting
// Ready indicates the ClientConn is ready for work.
Ready
// TransientFailure indicates the ClientConn has seen a failure but expects to recover.
TransientFailure
// Shutdown indicates the ClientConn has started shutting down.
Shutdown
)
func (s ConnectivityState) String() string {
switch s {
case Idle:
return "IDLE"
case Connecting:
return "CONNECTING"
case Ready:
return "READY"
case TransientFailure:
return "TRANSIENT_FAILURE"
case Shutdown:
return "SHUTDOWN"
default:
panic(fmt.Sprintf("unknown connectivity state: %d", s))
}
}
// ClientConn represents a client connection to an RPC service.
type ClientConn struct {
target string
dopts dialOptions
}
// State returns the connectivity state of cc.
// This is EXPERIMENTAL API.
func (cc *ClientConn) State() ConnectivityState {
return cc.dopts.picker.State()
}
// WaitForStateChange blocks until the state changes to something other than the sourceState
// or timeout fires on cc. It returns false if timeout fires, and true otherwise.
// This is EXPERIMENTAL API.
func (cc *ClientConn) WaitForStateChange(timeout time.Duration, sourceState ConnectivityState) bool {
return cc.dopts.picker.WaitForStateChange(timeout, sourceState)
}
// Close starts to tear down the ClientConn.
func (cc *ClientConn) Close() error {
return cc.dopts.picker.Close()
}
// Conn is a client connection to a single destination.
type Conn struct {
target string
authority string
dopts dialOptions
shutdownChan chan struct{}
events trace.EventLog
mu sync.Mutex
state ConnectivityState
stateCV *sync.Cond
// ready is closed and becomes nil when a new transport is up or failed
// due to timeout.
ready chan struct{}
transport transport.ClientTransport
}
// NewConn creates a Conn.
func NewConn(cc *ClientConn) (*Conn, error) {
if cc.target == "" {
return nil, ErrUnspecTarget
}
c := &Conn{
target: cc.target,
dopts: cc.dopts,
shutdownChan: make(chan struct{}),
}
if EnableTracing {
c.events = trace.NewEventLog("grpc.ClientConn", c.target)
}
if !c.dopts.insecure {
var ok bool
for _, cd := range c.dopts.copts.AuthOptions {
if _, ok := cd.(credentials.TransportAuthenticator); !ok {
continue
}
ok = true
}
if !ok {
return nil, ErrNoTransportSecurity
}
} else {
for _, cd := range c.dopts.copts.AuthOptions {
if cd.RequireTransportSecurity() {
return nil, ErrCredentialsMisuse
}
}
}
colonPos := strings.LastIndex(c.target, ":")
if colonPos == -1 {
colonPos = len(c.target)
}
c.authority = c.target[:colonPos]
if c.dopts.codec == nil {
// Set the default codec.
c.dopts.codec = protoCodec{}
}
c.stateCV = sync.NewCond(&c.mu)
if c.dopts.block {
if err := c.resetTransport(false); err != nil {
c.Close()
return nil, err
}
// Start to monitor the error status of transport.
go c.transportMonitor()
} else {
// Start a goroutine connecting to the server asynchronously.
go func() {
if err := c.resetTransport(false); err != nil {
grpclog.Printf("Failed to dial %s: %v; please retry.", c.target, err)
c.Close()
return
}
c.transportMonitor()
}()
}
return c, nil
}
// printf records an event in cc's event log, unless cc has been closed.
// REQUIRES cc.mu is held.
func (cc *Conn) printf(format string, a ...interface{}) {
if cc.events != nil {
cc.events.Printf(format, a...)
}
}
// errorf records an error in cc's event log, unless cc has been closed.
// REQUIRES cc.mu is held.
func (cc *Conn) errorf(format string, a ...interface{}) {
if cc.events != nil {
cc.events.Errorf(format, a...)
}
}
// State returns the connectivity state of the Conn
func (cc *Conn) State() ConnectivityState {
cc.mu.Lock()
defer cc.mu.Unlock()
return cc.state
}
// WaitForStateChange blocks until the state changes to something other than the sourceState
// or timeout fires. It returns false if timeout fires and true otherwise.
// TODO(zhaoq): Rewrite for complex Picker.
func (cc *Conn) WaitForStateChange(timeout time.Duration, sourceState ConnectivityState) bool {
start := time.Now()
cc.mu.Lock()
defer cc.mu.Unlock()
if sourceState != cc.state {
return true
}
expired := timeout <= time.Since(start)
if expired {
return false
}
done := make(chan struct{})
go func() {
select {
case <-time.After(timeout - time.Since(start)):
cc.mu.Lock()
expired = true
cc.stateCV.Broadcast()
cc.mu.Unlock()
case <-done:
}
}()
defer close(done)
for sourceState == cc.state {
cc.stateCV.Wait()
if expired {
return false
}
}
return true
}
func (cc *Conn) resetTransport(closeTransport bool) error {
var retries int
start := time.Now()
for {
cc.mu.Lock()
cc.printf("connecting")
if cc.state == Shutdown {
cc.mu.Unlock()
return ErrClientConnClosing
}
cc.state = Connecting
cc.stateCV.Broadcast()
cc.mu.Unlock()
if closeTransport {
cc.transport.Close()
}
// Adjust timeout for the current try.
copts := cc.dopts.copts
if copts.Timeout < 0 {
cc.Close()
return ErrClientConnTimeout
}
if copts.Timeout > 0 {
copts.Timeout -= time.Since(start)
if copts.Timeout <= 0 {
cc.Close()
return ErrClientConnTimeout
}
}
sleepTime := backoff(retries)
timeout := sleepTime
if timeout < minConnectTimeout {
timeout = minConnectTimeout
}
if copts.Timeout == 0 || copts.Timeout > timeout {
copts.Timeout = timeout
}
connectTime := time.Now()
newTransport, err := transport.NewClientTransport(cc.target, &copts)
if err != nil {
cc.mu.Lock()
cc.errorf("transient failure: %v", err)
cc.state = TransientFailure
cc.stateCV.Broadcast()
if cc.ready != nil {
close(cc.ready)
cc.ready = nil
}
cc.mu.Unlock()
sleepTime -= time.Since(connectTime)
if sleepTime < 0 {
sleepTime = 0
}
// Fail early before falling into sleep.
if cc.dopts.copts.Timeout > 0 && cc.dopts.copts.Timeout < sleepTime+time.Since(start) {
cc.mu.Lock()
cc.errorf("connection timeout")
cc.mu.Unlock()
cc.Close()
return ErrClientConnTimeout
}
closeTransport = false
time.Sleep(sleepTime)
retries++
grpclog.Printf("grpc: ClientConn.resetTransport failed to create client transport: %v; Reconnecting to %q", err, cc.target)
continue
}
cc.mu.Lock()
cc.printf("ready")
if cc.state == Shutdown {
// cc.Close() has been invoked.
cc.mu.Unlock()
newTransport.Close()
return ErrClientConnClosing
}
cc.state = Ready
cc.stateCV.Broadcast()
cc.transport = newTransport
if cc.ready != nil {
close(cc.ready)
cc.ready = nil
}
cc.mu.Unlock()
return nil
}
}
// Run in a goroutine to track the error in transport and create the
// new transport if an error happens. It returns when the channel is closing.
func (cc *Conn) transportMonitor() {
for {
select {
// shutdownChan is needed to detect the teardown when
// the ClientConn is idle (i.e., no RPC in flight).
case <-cc.shutdownChan:
return
case <-cc.transport.Error():
cc.mu.Lock()
cc.state = TransientFailure
cc.stateCV.Broadcast()
cc.mu.Unlock()
if err := cc.resetTransport(true); err != nil {
// The ClientConn is closing.
cc.mu.Lock()
cc.printf("transport exiting: %v", err)
cc.mu.Unlock()
grpclog.Printf("grpc: ClientConn.transportMonitor exits due to: %v", err)
return
}
continue
}
}
}
// Wait blocks until i) the new transport is up or ii) ctx is done or iii)
func (cc *Conn) Wait(ctx context.Context) (transport.ClientTransport, error) {
for {
cc.mu.Lock()
switch {
case cc.state == Shutdown:
cc.mu.Unlock()
return nil, ErrClientConnClosing
case cc.state == Ready:
cc.mu.Unlock()
return cc.transport, nil
case cc.state == TransientFailure:
cc.mu.Unlock()
// Break out so that the caller gets chance to pick another transport to
// perform rpc instead of sticking to this transport.
return nil, ErrTransientFailure
default:
ready := cc.ready
if ready == nil {
ready = make(chan struct{})
cc.ready = ready
}
cc.mu.Unlock()
select {
case <-ctx.Done():
return nil, transport.ContextErr(ctx.Err())
// Wait until the new transport is ready or failed.
case <-ready:
}
}
}
}
// Close starts to tear down the Conn. Returns ErrClientConnClosing if
// it has been closed (mostly due to dial time-out).
// TODO(zhaoq): Make this synchronous to avoid unbounded memory consumption in
// some edge cases (e.g., the caller opens and closes many ClientConn's in a
// tight loop.
func (cc *Conn) Close() error {
cc.mu.Lock()
defer cc.mu.Unlock()
if cc.state == Shutdown {
return ErrClientConnClosing
}
cc.state = Shutdown
cc.stateCV.Broadcast()
if cc.events != nil {
cc.events.Finish()
cc.events = nil
}
if cc.ready != nil {
close(cc.ready)
cc.ready = nil
}
if cc.transport != nil {
cc.transport.Close()
}
if cc.shutdownChan != nil {
close(cc.shutdownChan)
}
return nil
}
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/codegen.sh 0000775 0000000 0000000 00000001163 12603607541 0021453 0 ustar 00root root 0000000 0000000 #!/bin/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
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/codes/ 0000775 0000000 0000000 00000000000 12603607541 0020604 5 ustar 00root root 0000000 0000000 grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/codes/code_string.go 0000664 0000000 0000000 00000001064 12603607541 0023434 0 ustar 00root root 0000000 0000000 // generated by stringer -type=Code; DO NOT EDIT
package codes
import "fmt"
const _Code_name = "OKCanceledUnknownInvalidArgumentDeadlineExceededNotFoundAlreadyExistsPermissionDeniedResourceExhaustedFailedPreconditionAbortedOutOfRangeUnimplementedInternalUnavailableDataLossUnauthenticated"
var _Code_index = [...]uint8{0, 2, 10, 17, 32, 48, 56, 69, 85, 102, 120, 127, 137, 150, 158, 169, 177, 192}
func (i Code) String() string {
if i+1 >= Code(len(_Code_index)) {
return fmt.Sprintf("Code(%d)", i)
}
return _Code_name[_Code_index[i]:_Code_index[i+1]]
}
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/codes/codes.go 0000664 0000000 0000000 00000015253 12603607541 0022236 0 ustar 00root root 0000000 0000000 /*
*
* Copyright 2014, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
// Package codes defines the canonical error codes used by gRPC. It is
// consistent across various languages.
package codes // import "google.golang.org/grpc/codes"
// A Code is an unsigned 32-bit error code as defined in the gRPC spec.
type Code uint32
//go:generate stringer -type=Code
const (
// OK is returned on success.
OK Code = 0
// Canceled indicates the operation was cancelled (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
// Unauthenticated indicates the request does not have valid
// authentication credentials for the operation.
Unauthenticated Code = 16
// 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
)
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/credentials/ 0000775 0000000 0000000 00000000000 12603607541 0022004 5 ustar 00root root 0000000 0000000 grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/credentials/credentials.go 0000664 0000000 0000000 00000020304 12603607541 0024627 0 ustar 00root root 0000000 0000000 /*
*
* Copyright 2014, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
// Package credentials implements various credentials supported by gRPC library,
// which encapsulate all the state needed by a client to authenticate with a
// server and make various assertions, e.g., about the client's identity, role,
// or whether it is authorized to make a particular call.
package credentials // import "google.golang.org/grpc/credentials"
import (
"crypto/tls"
"crypto/x509"
"fmt"
"io/ioutil"
"net"
"strings"
"time"
"golang.org/x/net/context"
)
var (
// alpnProtoStr are the specified application level protocols for gRPC.
alpnProtoStr = []string{"h2"}
)
// Credentials defines the common interface all supported credentials must
// implement.
type Credentials interface {
// GetRequestMetadata gets the current request metadata, refreshing
// tokens if required. This should be called by the transport layer on
// each request, and the data should be populated in headers or other
// context. uri is the URI of the entry point for the request. When
// supported by the underlying implementation, ctx can be used for
// timeout and cancellation.
// TODO(zhaoq): Define the set of the qualified keys instead of leaving
// it as an arbitrary string.
GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error)
// RequireTransportSecurity indicates whether the credentails requires
// transport security.
RequireTransportSecurity() bool
}
// ProtocolInfo provides information regarding the gRPC wire protocol version,
// security protocol, security protocol version in use, etc.
type ProtocolInfo struct {
// ProtocolVersion is the gRPC wire protocol version.
ProtocolVersion string
// SecurityProtocol is the security protocol in use.
SecurityProtocol string
// SecurityVersion is the security protocol version.
SecurityVersion string
}
// AuthInfo defines the common interface for the auth information the users are interested in.
type AuthInfo interface {
AuthType() string
}
type authInfoKey struct{}
// NewContext creates a new context with authInfo attached.
func NewContext(ctx context.Context, authInfo AuthInfo) context.Context {
return context.WithValue(ctx, authInfoKey{}, authInfo)
}
// FromContext returns the authInfo in ctx if it exists.
func FromContext(ctx context.Context) (authInfo AuthInfo, ok bool) {
authInfo, ok = ctx.Value(authInfoKey{}).(AuthInfo)
return
}
// TransportAuthenticator defines the common interface for all the live gRPC wire
// protocols and supported transport security protocols (e.g., TLS, SSL).
type TransportAuthenticator interface {
// ClientHandshake does the authentication handshake specified by the corresponding
// authentication protocol on rawConn for clients. It returns the authenticated
// connection and the corresponding auth information about the connection.
ClientHandshake(addr string, rawConn net.Conn, timeout time.Duration) (net.Conn, AuthInfo, error)
// ServerHandshake does the authentication handshake for servers. It returns
// the authenticated connection and the corresponding auth information about
// the connection.
ServerHandshake(rawConn net.Conn) (net.Conn, AuthInfo, error)
// Info provides the ProtocolInfo of this TransportAuthenticator.
Info() ProtocolInfo
Credentials
}
// TLSInfo contains the auth information for a TLS authenticated connection.
// It implements the AuthInfo interface.
type TLSInfo struct {
State tls.ConnectionState
}
func (t TLSInfo) AuthType() string {
return "tls"
}
// tlsCreds is the credentials required for authenticating a connection using TLS.
type tlsCreds struct {
// TLS configuration
config tls.Config
}
func (c tlsCreds) Info() ProtocolInfo {
return ProtocolInfo{
SecurityProtocol: "tls",
SecurityVersion: "1.2",
}
}
// GetRequestMetadata returns nil, nil since TLS credentials does not have
// metadata.
func (c *tlsCreds) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
return nil, nil
}
func (c *tlsCreds) RequireTransportSecurity() bool {
return true
}
type timeoutError struct{}
func (timeoutError) Error() string { return "credentials: Dial timed out" }
func (timeoutError) Timeout() bool { return true }
func (timeoutError) Temporary() bool { return true }
func (c *tlsCreds) ClientHandshake(addr string, rawConn net.Conn, timeout time.Duration) (_ net.Conn, _ AuthInfo, err error) {
// borrow some code from tls.DialWithDialer
var errChannel chan error
if timeout != 0 {
errChannel = make(chan error, 2)
time.AfterFunc(timeout, func() {
errChannel <- timeoutError{}
})
}
if c.config.ServerName == "" {
colonPos := strings.LastIndex(addr, ":")
if colonPos == -1 {
colonPos = len(addr)
}
c.config.ServerName = addr[:colonPos]
}
conn := tls.Client(rawConn, &c.config)
if timeout == 0 {
err = conn.Handshake()
} else {
go func() {
errChannel <- conn.Handshake()
}()
err = <-errChannel
}
if err != nil {
rawConn.Close()
return nil, nil, err
}
// TODO(zhaoq): Omit the auth info for client now. It is more for
// information than anything else.
return conn, nil, nil
}
func (c *tlsCreds) ServerHandshake(rawConn net.Conn) (net.Conn, AuthInfo, error) {
conn := tls.Server(rawConn, &c.config)
if err := conn.Handshake(); err != nil {
rawConn.Close()
return nil, nil, err
}
return conn, TLSInfo{conn.ConnectionState()}, nil
}
// NewTLS uses c to construct a TransportAuthenticator based on TLS.
func NewTLS(c *tls.Config) TransportAuthenticator {
tc := &tlsCreds{*c}
tc.config.NextProtos = alpnProtoStr
return tc
}
// NewClientTLSFromCert constructs a TLS from the input certificate for client.
func NewClientTLSFromCert(cp *x509.CertPool, serverName string) TransportAuthenticator {
return NewTLS(&tls.Config{ServerName: serverName, RootCAs: cp})
}
// NewClientTLSFromFile constructs a TLS from the input certificate file for client.
func NewClientTLSFromFile(certFile, serverName string) (TransportAuthenticator, error) {
b, err := ioutil.ReadFile(certFile)
if err != nil {
return nil, err
}
cp := x509.NewCertPool()
if !cp.AppendCertsFromPEM(b) {
return nil, fmt.Errorf("credentials: failed to append certificates")
}
return NewTLS(&tls.Config{ServerName: serverName, RootCAs: cp}), nil
}
// NewServerTLSFromCert constructs a TLS from the input certificate for server.
func NewServerTLSFromCert(cert *tls.Certificate) TransportAuthenticator {
return NewTLS(&tls.Config{Certificates: []tls.Certificate{*cert}})
}
// NewServerTLSFromFile constructs a TLS from the input certificate file and key
// file for server.
func NewServerTLSFromFile(certFile, keyFile string) (TransportAuthenticator, error) {
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
if err != nil {
return nil, err
}
return NewTLS(&tls.Config{Certificates: []tls.Certificate{cert}}), nil
}
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/credentials/oauth/ 0000775 0000000 0000000 00000000000 12603607541 0023124 5 ustar 00root root 0000000 0000000 grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/credentials/oauth/oauth.go 0000664 0000000 0000000 00000013264 12603607541 0024601 0 ustar 00root root 0000000 0000000 /*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
// Package oauth implements gRPC credentials using OAuth.
package oauth
import (
"fmt"
"io/ioutil"
"golang.org/x/net/context"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
"golang.org/x/oauth2/jwt"
"google.golang.org/grpc/credentials"
)
// TokenSource supplies credentials from an oauth2.TokenSource.
type TokenSource struct {
oauth2.TokenSource
}
// GetRequestMetadata gets the request metadata as a map from a TokenSource.
func (ts TokenSource) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
token, err := ts.Token()
if err != nil {
return nil, err
}
return map[string]string{
"authorization": token.TokenType + " " + token.AccessToken,
}, nil
}
func (ts TokenSource) RequireTransportSecurity() bool {
return true
}
type jwtAccess struct {
jsonKey []byte
}
func NewJWTAccessFromFile(keyFile string) (credentials.Credentials, error) {
jsonKey, err := ioutil.ReadFile(keyFile)
if err != nil {
return nil, fmt.Errorf("credentials: failed to read the service account key file: %v", err)
}
return NewJWTAccessFromKey(jsonKey)
}
func NewJWTAccessFromKey(jsonKey []byte) (credentials.Credentials, error) {
return jwtAccess{jsonKey}, nil
}
func (j jwtAccess) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
ts, err := google.JWTAccessTokenSourceFromJSON(j.jsonKey, uri[0])
if err != nil {
return nil, err
}
token, err := ts.Token()
if err != nil {
return nil, err
}
return map[string]string{
"authorization": token.TokenType + " " + token.AccessToken,
}, nil
}
func (j jwtAccess) RequireTransportSecurity() bool {
return true
}
// oauthAccess supplies credentials from a given token.
type oauthAccess struct {
token oauth2.Token
}
// NewOauthAccess constructs the credentials using a given token.
func NewOauthAccess(token *oauth2.Token) credentials.Credentials {
return oauthAccess{token: *token}
}
func (oa oauthAccess) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
return map[string]string{
"authorization": oa.token.TokenType + " " + oa.token.AccessToken,
}, nil
}
func (oa oauthAccess) RequireTransportSecurity() bool {
return true
}
// NewComputeEngine constructs the credentials that fetches access tokens from
// Google Compute Engine (GCE)'s metadata server. It is only valid to use this
// if your program is running on a GCE instance.
// TODO(dsymonds): Deprecate and remove this.
func NewComputeEngine() credentials.Credentials {
return TokenSource{google.ComputeTokenSource("")}
}
// serviceAccount represents credentials via JWT signing key.
type serviceAccount struct {
config *jwt.Config
}
func (s serviceAccount) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
token, err := s.config.TokenSource(ctx).Token()
if err != nil {
return nil, err
}
return map[string]string{
"authorization": token.TokenType + " " + token.AccessToken,
}, nil
}
func (s serviceAccount) RequireTransportSecurity() bool {
return true
}
// NewServiceAccountFromKey constructs the credentials using the JSON key slice
// from a Google Developers service account.
func NewServiceAccountFromKey(jsonKey []byte, scope ...string) (credentials.Credentials, error) {
config, err := google.JWTConfigFromJSON(jsonKey, scope...)
if err != nil {
return nil, err
}
return serviceAccount{config: config}, nil
}
// NewServiceAccountFromFile constructs the credentials using the JSON key file
// of a Google Developers service account.
func NewServiceAccountFromFile(keyFile string, scope ...string) (credentials.Credentials, error) {
jsonKey, err := ioutil.ReadFile(keyFile)
if err != nil {
return nil, fmt.Errorf("credentials: failed to read the service account key file: %v", err)
}
return NewServiceAccountFromKey(jsonKey, scope...)
}
// NewApplicationDefault returns "Application Default Credentials". For more
// detail, see https://developers.google.com/accounts/docs/application-default-credentials.
func NewApplicationDefault(ctx context.Context, scope ...string) (credentials.Credentials, error) {
t, err := google.DefaultTokenSource(ctx, scope...)
if err != nil {
return nil, err
}
return TokenSource{t}, nil
}
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/doc.go 0000664 0000000 0000000 00000000254 12603607541 0020604 0 ustar 00root root 0000000 0000000 /*
Package grpc implements an RPC system called gRPC.
See https://github.com/grpc/grpc for more information about gRPC.
*/
package grpc // import "google.golang.org/grpc"
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/examples/ 0000775 0000000 0000000 00000000000 12603607541 0021325 5 ustar 00root root 0000000 0000000 grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/examples/README.md 0000664 0000000 0000000 00000002312 12603607541 0022602 0 ustar 00root root 0000000 0000000 gRPC in 3 minutes (Go)
======================
BACKGROUND
-------------
For this sample, we've already generated the server and client stubs from [helloworld.proto](helloworld/helloworld/helloworld.proto).
PREREQUISITES
-------------
- This requires Go 1.4
- Requires that [GOPATH is set](https://golang.org/doc/code.html#GOPATH)
```
$ go help gopath
$ # ensure the PATH contains $GOPATH/bin
$ export PATH=$PATH:$GOPATH/bin
```
INSTALL
-------
```
$ go get -u google.golang.org/grpc/examples/helloworld/greeter_client
$ go get -u google.golang.org/grpc/examples/helloworld/greeter_server
```
TRY IT!
-------
- Run the server
```
$ greeter_server &
```
- Run the client
```
$ greeter_client
```
OPTIONAL - Rebuilding the generated code
----------------------------------------
1 First [install protoc](https://github.com/google/protobuf/blob/master/INSTALL.txt)
- For now, this needs to be installed from source
- This is will change once proto3 is officially released
2 Install the protoc Go plugin.
```
$ go get -a github.com/golang/protobuf/protoc-gen-go
$
$ # from this dir; invoke protoc
$ protoc -I ./helloworld/helloworld/ ./helloworld/helloworld/helloworld.proto --go_out=plugins=grpc:helloworld
```
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/examples/gotutorial.md 0000664 0000000 0000000 00000051463 12603607541 0024051 0 ustar 00root root 0000000 0000000 #gRPC Basics: Go
This tutorial provides a basic Go programmer's introduction to working with gRPC. By walking through this example you'll learn how to:
- Define a service in a .proto file.
- Generate server and client code using the protocol buffer compiler.
- Use the Go gRPC API to write a simple client and server for your service.
It assumes that you have read the [Getting started](https://github.com/grpc/grpc/tree/master/examples) guide and are familiar with [protocol buffers] (https://developers.google.com/protocol-buffers/docs/overview). Note that the example in this tutorial uses the proto3 version of the protocol buffers language, which is currently in alpha release:you can find out more in the [proto3 language guide](https://developers.google.com/protocol-buffers/docs/proto3) and see the [release notes](https://github.com/google/protobuf/releases) for the new version in the protocol buffers Github repository.
This isn't a comprehensive guide to using gRPC in Go: more reference documentation is coming soon.
## Why use gRPC?
Our example is a simple route mapping application that lets clients get information about features on their route, create a summary of their route, and exchange route information such as traffic updates with the server and other clients.
With gRPC we can define our service once in a .proto file and implement clients and servers in any of gRPC's supported languages, which in turn can be run in environments ranging from servers inside Google to your own tablet - all the complexity of communication between different languages and environments is handled for you by gRPC. We also get all the advantages of working with protocol buffers, including efficient serialization, a simple IDL, and easy interface updating.
## Example code and setup
The example code for our tutorial is in [grpc/grpc-go/examples/route_guide](https://github.com/grpc/grpc-go/tree/master/examples/route_guide). To download the example, clone the `grpc-go` repository by running the following command:
```shell
$ go get google.golang.org/grpc
```
Then change your current directory to `grpc-go/examples/route_guide`:
```shell
$ cd $GOPATH/src/google.golang.org/grpc/examples/route_guide
```
You also should have the relevant tools installed to generate the server and client interface code - if you don't already, follow the setup instructions in [the Go quick start guide](examples/).
## Defining the service
Our first step (as you'll know from the [quick start](http://www.grpc.io/docs/#quick-start)) is to define the gRPC *service* and the method *request* and *response* types using [protocol buffers] (https://developers.google.com/protocol-buffers/docs/overview). You can see the complete .proto file in [`examples/route_guide/proto/route_guide.proto`](examples/route_guide/proto/route_guide.proto).
To define a service, you specify a named `service` in your .proto file:
```proto
service RouteGuide {
...
}
```
Then you define `rpc` methods inside your service definition, specifying their request and response types. gRPC lets you define four kinds of service method, all of which are used in the `RouteGuide` service:
- A *simple RPC* where the client sends a request to the server using the stub and waits for a response to come back, just like a normal function call.
```proto
// Obtains the feature at a given position.
rpc GetFeature(Point) returns (Feature) {}
```
- A *server-side streaming RPC* where the client sends a request to the server and gets a stream to read a sequence of messages back. The client reads from the returned stream until there are no more messages. As you can see in our example, you specify a server-side streaming method by placing the `stream` keyword before the *response* type.
```proto
// Obtains the Features available within the given Rectangle. Results are
// streamed rather than returned at once (e.g. in a response message with a
// repeated field), as the rectangle may cover a large area and contain a
// huge number of features.
rpc ListFeatures(Rectangle) returns (stream Feature) {}
```
- A *client-side streaming RPC* where the client writes a sequence of messages and sends them to the server, again using a provided stream. Once the client has finished writing the messages, it waits for the server to read them all and return its response. You specify a client-side streaming method by placing the `stream` keyword before the *request* type.
```proto
// Accepts a stream of Points on a route being traversed, returning a
// RouteSummary when traversal is completed.
rpc RecordRoute(stream Point) returns (RouteSummary) {}
```
- A *bidirectional streaming RPC* where both sides send a sequence of messages using a read-write stream. The two streams operate independently, so clients and servers can read and write in whatever order they like: for example, the server could wait to receive all the client messages before writing its responses, or it could alternately read a message then write a message, or some other combination of reads and writes. The order of messages in each stream is preserved. You specify this type of method by placing the `stream` keyword before both the request and the response.
```proto
// Accepts a stream of RouteNotes sent while a route is being traversed,
// while receiving other RouteNotes (e.g. from other users).
rpc RouteChat(stream RouteNote) returns (stream RouteNote) {}
```
Our .proto file also contains protocol buffer message type definitions for all the request and response types used in our service methods - for example, here's the `Point` message type:
```proto
// Points are represented as latitude-longitude pairs in the E7 representation
// (degrees multiplied by 10**7 and rounded to the nearest integer).
// Latitudes should be in the range +/- 90 degrees and longitude should be in
// the range +/- 180 degrees (inclusive).
message Point {
int32 latitude = 1;
int32 longitude = 2;
}
```
## Generating client and server code
Next we need to generate the gRPC client and server interfaces from our .proto service definition. We do this using the protocol buffer compiler `protoc` with a special gRPC Go plugin.
For simplicity, we've provided a [bash script](https://github.com/grpc/grpc-go/blob/master/codegen.sh) that runs `protoc` for you with the appropriate plugin, input, and output (if you want to run this by yourself, make sure you've installed protoc and followed the gRPC-Go [installation instructions](https://github.com/grpc/grpc-go/blob/master/README.md) first):
```shell
$ codegen.sh route_guide.proto
```
which actually runs:
```shell
$ protoc --go_out=plugins=grpc:. route_guide.proto
```
Running this command generates the following file in your current directory:
- `route_guide.pb.go`
This contains:
- All the protocol buffer code to populate, serialize, and retrieve our request and response message types
- An interface type (or *stub*) for clients to call with the methods defined in the `RouteGuide` service.
- An interface type for servers to implement, also with the methods defined in the `RouteGuide` service.
## Creating the server
First let's look at how we create a `RouteGuide` server. If you're only interested in creating gRPC clients, you can skip this section and go straight to [Creating the client](#client) (though you might find it interesting anyway!).
There are two parts to making our `RouteGuide` service do its job:
- Implementing the service interface generated from our service definition: doing the actual "work" of our service.
- Running a gRPC server to listen for requests from clients and dispatch them to the right service implementation.
You can find our example `RouteGuide` server in [grpc-go/examples/route_guide/server/server.go](https://github.com/grpc/grpc-go/tree/master/examples/route_guide/server/server.go). Let's take a closer look at how it works.
### Implementing RouteGuide
As you can see, our server has a `routeGuideServer` struct type that implements the generated `RouteGuideServer` interface:
```go
type routeGuideServer struct {
...
}
...
func (s *routeGuideServer) GetFeature(ctx context.Context, point *pb.Point) (*pb.Feature, error) {
...
}
...
func (s *routeGuideServer) ListFeatures(rect *pb.Rectangle, stream pb.RouteGuide_ListFeaturesServer) error {
...
}
...
func (s *routeGuideServer) RecordRoute(stream pb.RouteGuide_RecordRouteServer) error {
...
}
...
func (s *routeGuideServer) RouteChat(stream pb.RouteGuide_RouteChatServer) error {
...
}
...
```
#### Simple RPC
`routeGuideServer` implements all our service methods. Let's look at the simplest type first, `GetFeature`, which just gets a `Point` from the client and returns the corresponding feature information from its database in a `Feature`.
```go
func (s *routeGuideServer) GetFeature(ctx context.Context, point *pb.Point) (*pb.Feature, error) {
for _, feature := range s.savedFeatures {
if proto.Equal(feature.Location, point) {
return feature, nil
}
}
// No feature was found, return an unnamed feature
return &pb.Feature{"", point}, nil
}
```
The method is passed a context object for the RPC and the client's `Point` protocol buffer request. It returns a `Feature` protocol buffer object with the response information and an `error`. In the method we populate the `Feature` with the appropriate information, and then `return` it along with an `nil` error to tell gRPC that we've finished dealing with the RPC and that the `Feature` can be returned to the client.
#### Server-side streaming RPC
Now let's look at one of our streaming RPCs. `ListFeatures` is a server-side streaming RPC, so we need to send back multiple `Feature`s to our client.
```go
func (s *routeGuideServer) ListFeatures(rect *pb.Rectangle, stream pb.RouteGuide_ListFeaturesServer) error {
for _, feature := range s.savedFeatures {
if inRange(feature.Location, rect) {
if err := stream.Send(feature); err != nil {
return err
}
}
}
return nil
}
```
As you can see, instead of getting simple request and response objects in our method parameters, this time we get a request object (the `Rectangle` in which our client wants to find `Feature`s) and a special `RouteGuide_ListFeaturesServer` object to write our responses.
In the method, we populate as many `Feature` objects as we need to return, writing them to the `RouteGuide_ListFeaturesServer` using its `Send()` method. Finally, as in our simple RPC, we return a `nil` error to tell gRPC that we've finished writing responses. Should any error happen in this call, we return a non-`nil` error; the gRPC layer will translate it into an appropriate RPC status to be sent on the wire.
#### Client-side streaming RPC
Now let's look at something a little more complicated: the client-side streaming method `RecordRoute`, where we get a stream of `Point`s from the client and return a single `RouteSummary` with information about their trip. As you can see, this time the method doesn't have a request parameter at all. Instead, it gets a `RouteGuide_RecordRouteServer` stream, which the server can use to both read *and* write messages - it can receive client messages using its `Recv()` method and return its single response using its `SendAndClose()` method.
```go
func (s *routeGuideServer) RecordRoute(stream pb.RouteGuide_RecordRouteServer) error {
var pointCount, featureCount, distance int32
var lastPoint *pb.Point
startTime := time.Now()
for {
point, err := stream.Recv()
if err == io.EOF {
endTime := time.Now()
return stream.SendAndClose(&pb.RouteSummary{
PointCount: pointCount,
FeatureCount: featureCount,
Distance: distance,
ElapsedTime: int32(endTime.Sub(startTime).Seconds()),
})
}
if err != nil {
return err
}
pointCount++
for _, feature := range s.savedFeatures {
if proto.Equal(feature.Location, point) {
featureCount++
}
}
if lastPoint != nil {
distance += calcDistance(lastPoint, point)
}
lastPoint = point
}
}
```
In the method body we use the `RouteGuide_RecordRouteServer`s `Recv()` method to repeatedly read in our client's requests to a request object (in this case a `Point`) until there are no more messages: the server needs to check the the error returned from `Recv()` after each call. If this is `nil`, the stream is still good and it can continue reading; if it's `io.EOF` the message stream has ended and the server can return its `RouteSummary`. If it has any other value, we return the error "as is" so that it'll be translated to an RPC status by the gRPC layer.
#### Bidirectional streaming RPC
Finally, let's look at our bidirectional streaming RPC `RouteChat()`.
```go
func (s *routeGuideServer) RouteChat(stream pb.RouteGuide_RouteChatServer) error {
for {
in, err := stream.Recv()
if err == io.EOF {
return nil
}
if err != nil {
return err
}
key := serialize(in.Location)
... // look for notes to be sent to client
for _, note := range s.routeNotes[key] {
if err := stream.Send(note); err != nil {
return err
}
}
}
}
```
This time we get a `RouteGuide_RouteChatServer` stream that, as in our client-side streaming example, can be used to read and write messages. However, this time we return values via our method's stream while the client is still writing messages to *their* message stream.
The syntax for reading and writing here is very similar to our client-streaming method, except the server uses the stream's `Send()` method rather than `SendAndClose()` because it's writing multiple responses. Although each side will always get the other's messages in the order they were written, both the client and server can read and write in any order — the streams operate completely independently.
### Starting the server
Once we've implemented all our methods, we also need to start up a gRPC server so that clients can actually use our service. The following snippet shows how we do this for our `RouteGuide` service:
```go
flag.Parse()
lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port))
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
grpcServer := grpc.NewServer()
pb.RegisterRouteGuideServer(grpcServer, &routeGuideServer{})
... // determine whether to use TLS
grpcServer.Serve(lis)
```
To build and start a server, we:
1. Specify the port we want to use to listen for client requests using `lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port))`.
2. Create an instance of the gRPC server using `grpc.NewServer()`.
3. Register our service implementation with the gRPC server.
4. Call `Serve()` on the server with our port details to do a blocking wait until the process is killed or `Stop()` is called.
## Creating the client
In this section, we'll look at creating a Go client for our `RouteGuide` service. You can see our complete example client code in [grpc-go/examples/route_guide/client/client.go](https://github.com/grpc/grpc-go/tree/master/examples/route_guide/client/client.go).
### Creating a stub
To call service methods, we first need to create a gRPC *channel* to communicate with the server. We create this by passing the server address and port number to `grpc.Dial()` as follows:
```go
conn, err := grpc.Dial(*serverAddr)
if err != nil {
...
}
defer conn.Close()
```
You can use `DialOptions` to set the auth credentials (e.g., TLS, GCE credentials, JWT credentials) in `grpc.Dial` if the service you request requires that - however, we don't need to do this for our `RouteGuide` service.
Once the gRPC *channel* is setup, we need a client *stub* to perform RPCs. We get this using the `NewRouteGuideClient` method provided in the `pb` package we generated from our .proto.
```go
client := pb.NewRouteGuideClient(conn)
```
### Calling service methods
Now let's look at how we call our service methods. Note that in gRPC-Go, RPCs operate in a blocking/synchronous mode, which means that the RPC call waits for the server to respond, and will either return a response or an error.
#### Simple RPC
Calling the simple RPC `GetFeature` is nearly as straightforward as calling a local method.
```go
feature, err := client.GetFeature(context.Background(), &pb.Point{409146138, -746188906})
if err != nil {
...
}
```
As you can see, we call the method on the stub we got earlier. In our method parameters we create and populate a request protocol buffer object (in our case `Point`). We also pass a `context.Context` object which lets us change our RPC's behaviour if necessary, such as time-out/cancel an RPC in flight. If the call doesn't return an error, then we can read the response information from the server from the first return value.
```go
log.Println(feature)
```
#### Server-side streaming RPC
Here's where we call the server-side streaming method `ListFeatures`, which returns a stream of geographical `Feature`s. If you've already read [Creating the server](#server) some of this may look very familiar - streaming RPCs are implemented in a similar way on both sides.
```go
rect := &pb.Rectangle{ ... } // initialize a pb.Rectangle
stream, err := client.ListFeatures(context.Background(), rect)
if err != nil {
...
}
for {
feature, err := stream.Recv()
if err == io.EOF {
break
}
if err != nil {
log.Fatalf("%v.ListFeatures(_) = _, %v", client, err)
}
log.Println(feature)
}
```
As in the simple RPC, we pass the method a context and a request. However, instead of getting a response object back, we get back an instance of `RouteGuide_ListFeaturesClient`. The client can use the `RouteGuide_ListFeaturesClient` stream to read the server's responses.
We use the `RouteGuide_ListFeaturesClient`'s `Recv()` method to repeatedly read in the server's responses to a response protocol buffer object (in this case a `Feature`) until there are no more messages: the client needs to check the error `err` returned from `Recv()` after each call. If `nil`, the stream is still good and it can continue reading; if it's `io.EOF` then the message stream has ended; otherwise there must be an RPC error, which is passed over through `err`.
#### Client-side streaming RPC
The client-side streaming method `RecordRoute` is similar to the server-side method, except that we only pass the method a context and get a `RouteGuide_RecordRouteClient` stream back, which we can use to both write *and* read messages.
```go
// Create a random number of random points
r := rand.New(rand.NewSource(time.Now().UnixNano()))
pointCount := int(r.Int31n(100)) + 2 // Traverse at least two points
var points []*pb.Point
for i := 0; i < pointCount; i++ {
points = append(points, randomPoint(r))
}
log.Printf("Traversing %d points.", len(points))
stream, err := client.RecordRoute(context.Background())
if err != nil {
log.Fatalf("%v.RecordRoute(_) = _, %v", client, err)
}
for _, point := range points {
if err := stream.Send(point); err != nil {
log.Fatalf("%v.Send(%v) = %v", stream, point, err)
}
}
reply, err := stream.CloseAndRecv()
if err != nil {
log.Fatalf("%v.CloseAndRecv() got error %v, want %v", stream, err, nil)
}
log.Printf("Route summary: %v", reply)
```
The `RouteGuide_RecordRouteClient` has a `Send()` method that we can use to send requests to the server. Once we've finished writing our client's requests to the stream using `Send()`, we need to call `CloseAndRecv()` on the stream to let gRPC know that we've finished writing and are expecting to receive a response. We get our RPC status from the `err` returned from `CloseAndRecv()`. If the status is `nil`, then the first return value from `CloseAndRecv()` will be a valid server response.
#### Bidirectional streaming RPC
Finally, let's look at our bidirectional streaming RPC `RouteChat()`. As in the case of `RecordRoute`, we only pass the method a context object and get back a stream that we can use to both write and read messages. However, this time we return values via our method's stream while the server is still writing messages to *their* message stream.
```go
stream, err := client.RouteChat(context.Background())
waitc := make(chan struct{})
go func() {
for {
in, err := stream.Recv()
if err == io.EOF {
// read done.
close(waitc)
return
}
if err != nil {
log.Fatalf("Failed to receive a note : %v", err)
}
log.Printf("Got message %s at point(%d, %d)", in.Message, in.Location.Latitude, in.Location.Longitude)
}
}()
for _, note := range notes {
if err := stream.Send(note); err != nil {
log.Fatalf("Failed to send a note: %v", err)
}
}
stream.CloseSend()
<-waitc
```
The syntax for reading and writing here is very similar to our client-side streaming method, except we use the stream's `CloseSend()` method once we've finished our call. Although each side will always get the other's messages in the order they were written, both the client and server can read and write in any order — the streams operate completely independently.
## Try it out!
To compile and run the server, assuming you are in the folder
`$GOPATH/src/google.golang.org/grpc/examples/route_guide`, simply:
```sh
$ go run server/server.go
```
Likewise, to run the client:
```sh
$ go run client/client.go
```
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/examples/helloworld/ 0000775 0000000 0000000 00000000000 12603607541 0023500 5 ustar 00root root 0000000 0000000 grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/examples/helloworld/greeter_client/ 0000775 0000000 0000000 00000000000 12603607541 0026473 5 ustar 00root root 0000000 0000000 grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/examples/helloworld/greeter_client/main.go 0000664 0000000 0000000 00000004402 12603607541 0027746 0 ustar 00root root 0000000 0000000 /*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
package main
import (
"log"
"os"
pb "google.golang.org/grpc/examples/helloworld/helloworld"
"golang.org/x/net/context"
"google.golang.org/grpc"
)
const (
address = "localhost:50051"
defaultName = "world"
)
func main() {
// Set up a connection to the server.
conn, err := grpc.Dial(address, grpc.WithInsecure())
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()
c := pb.NewGreeterClient(conn)
// Contact the server and print out its response.
name := defaultName
if len(os.Args) > 1 {
name = os.Args[1]
}
r, err := c.SayHello(context.Background(), &pb.HelloRequest{Name: name})
if err != nil {
log.Fatalf("could not greet: %v", err)
}
log.Printf("Greeting: %s", r.Message)
}
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/examples/helloworld/greeter_server/ 0000775 0000000 0000000 00000000000 12603607541 0026523 5 ustar 00root root 0000000 0000000 grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/examples/helloworld/greeter_server/main.go 0000664 0000000 0000000 00000004261 12603607541 0030001 0 ustar 00root root 0000000 0000000 /*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
package main
import (
"log"
"net"
pb "google.golang.org/grpc/examples/helloworld/helloworld"
"golang.org/x/net/context"
"google.golang.org/grpc"
)
const (
port = ":50051"
)
// server is used to implement hellowrld.GreeterServer.
type server struct{}
// SayHello implements helloworld.GreeterServer
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
return &pb.HelloReply{Message: "Hello " + in.Name}, nil
}
func main() {
lis, err := net.Listen("tcp", port)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
pb.RegisterGreeterServer(s, &server{})
s.Serve(lis)
}
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/examples/helloworld/helloworld/ 0000775 0000000 0000000 00000000000 12603607541 0025653 5 ustar 00root root 0000000 0000000 grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/examples/helloworld/helloworld/helloworld.pb.go 0000664 0000000 0000000 00000005274 12603607541 0030765 0 ustar 00root root 0000000 0000000 // Code generated by protoc-gen-go.
// source: helloworld.proto
// DO NOT EDIT!
/*
Package helloworld is a generated protocol buffer package.
It is generated from these files:
helloworld.proto
It has these top-level messages:
HelloRequest
HelloReply
*/
package helloworld
import proto "github.com/golang/protobuf/proto"
import (
context "golang.org/x/net/context"
grpc "google.golang.org/grpc"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConn
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
// The request message containing the user's name.
type HelloRequest struct {
Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
}
func (m *HelloRequest) Reset() { *m = HelloRequest{} }
func (m *HelloRequest) String() string { return proto.CompactTextString(m) }
func (*HelloRequest) ProtoMessage() {}
// The response message containing the greetings
type HelloReply struct {
Message string `protobuf:"bytes,1,opt,name=message" json:"message,omitempty"`
}
func (m *HelloReply) Reset() { *m = HelloReply{} }
func (m *HelloReply) String() string { return proto.CompactTextString(m) }
func (*HelloReply) ProtoMessage() {}
func init() {
}
// Client API for Greeter service
type GreeterClient interface {
// Sends a greeting
SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error)
}
type greeterClient struct {
cc *grpc.ClientConn
}
func NewGreeterClient(cc *grpc.ClientConn) GreeterClient {
return &greeterClient{cc}
}
func (c *greeterClient) SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error) {
out := new(HelloReply)
err := grpc.Invoke(ctx, "/helloworld.Greeter/SayHello", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// Server API for Greeter service
type GreeterServer interface {
// Sends a greeting
SayHello(context.Context, *HelloRequest) (*HelloReply, error)
}
func RegisterGreeterServer(s *grpc.Server, srv GreeterServer) {
s.RegisterService(&_Greeter_serviceDesc, srv)
}
func _Greeter_SayHello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error) (interface{}, error) {
in := new(HelloRequest)
if err := dec(in); err != nil {
return nil, err
}
out, err := srv.(GreeterServer).SayHello(ctx, in)
if err != nil {
return nil, err
}
return out, nil
}
var _Greeter_serviceDesc = grpc.ServiceDesc{
ServiceName: "helloworld.Greeter",
HandlerType: (*GreeterServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "SayHello",
Handler: _Greeter_SayHello_Handler,
},
},
Streams: []grpc.StreamDesc{},
}
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/examples/helloworld/helloworld/helloworld.proto 0000664 0000000 0000000 00000003716 12603607541 0031122 0 ustar 00root root 0000000 0000000 // Copyright 2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
syntax = "proto3";
option java_package = "io.grpc.examples";
option objc_class_prefix = "HLW";
package helloworld;
// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings
message HelloReply {
string message = 1;
}
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/examples/route_guide/ 0000775 0000000 0000000 00000000000 12603607541 0023640 5 ustar 00root root 0000000 0000000 grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/examples/route_guide/README.md 0000664 0000000 0000000 00000001546 12603607541 0025125 0 ustar 00root root 0000000 0000000 # Description
The route guide server and client demonstrate how to use grpc go libraries to
perform unary, client streaming, server streaming and full duplex RPCs.
Please refer to [gRPC Basics: Go] (http://www.grpc.io/docs/tutorials/basic/go.html) for more information.
See the definition of the route guide service in proto/route_guide.proto.
# Run the sample code
To compile and run the server, assuming you are in the root of the route_guide
folder, i.e., .../examples/route_guide/, simply:
```sh
$ go run server/server.go
```
Likewise, to run the client:
```sh
$ go run client/client.go
```
# Optional command line flags
The server and client both take optional command line flags. For example, the
client and server run without TLS by default. To enable TLS:
```sh
$ go run server/server.go -tls=true
```
and
```sh
$ go run client/client.go -tls=true
```
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/examples/route_guide/client/ 0000775 0000000 0000000 00000000000 12603607541 0025116 5 ustar 00root root 0000000 0000000 grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/examples/route_guide/client/client.go 0000664 0000000 0000000 00000015144 12603607541 0026730 0 ustar 00root root 0000000 0000000 /*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
// Package main implements a simple gRPC client that demonstrates how to use gRPC-Go libraries
// to perform unary, client streaming, server streaming and full duplex RPCs.
//
// It interacts with the route guide service whose definition can be found in proto/route_guide.proto.
package main
import (
"flag"
"io"
"math/rand"
"time"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
pb "google.golang.org/grpc/examples/route_guide/routeguide"
"google.golang.org/grpc/grpclog"
)
var (
tls = flag.Bool("tls", false, "Connection uses TLS if true, else plain TCP")
caFile = flag.String("ca_file", "testdata/ca.pem", "The file containning the CA root cert file")
serverAddr = flag.String("server_addr", "127.0.0.1:10000", "The server address in the format of host:port")
serverHostOverride = flag.String("server_host_override", "x.test.youtube.com", "The server name use to verify the hostname returned by TLS handshake")
)
// printFeature gets the feature for the given point.
func printFeature(client pb.RouteGuideClient, point *pb.Point) {
grpclog.Printf("Getting feature for point (%d, %d)", point.Latitude, point.Longitude)
feature, err := client.GetFeature(context.Background(), point)
if err != nil {
grpclog.Fatalf("%v.GetFeatures(_) = _, %v: ", client, err)
}
grpclog.Println(feature)
}
// printFeatures lists all the features within the given bounding Rectangle.
func printFeatures(client pb.RouteGuideClient, rect *pb.Rectangle) {
grpclog.Printf("Looking for features within %v", rect)
stream, err := client.ListFeatures(context.Background(), rect)
if err != nil {
grpclog.Fatalf("%v.ListFeatures(_) = _, %v", client, err)
}
for {
feature, err := stream.Recv()
if err == io.EOF {
break
}
if err != nil {
grpclog.Fatalf("%v.ListFeatures(_) = _, %v", client, err)
}
grpclog.Println(feature)
}
}
// runRecordRoute sends a sequence of points to server and expects to get a RouteSummary from server.
func runRecordRoute(client pb.RouteGuideClient) {
// Create a random number of random points
r := rand.New(rand.NewSource(time.Now().UnixNano()))
pointCount := int(r.Int31n(100)) + 2 // Traverse at least two points
var points []*pb.Point
for i := 0; i < pointCount; i++ {
points = append(points, randomPoint(r))
}
grpclog.Printf("Traversing %d points.", len(points))
stream, err := client.RecordRoute(context.Background())
if err != nil {
grpclog.Fatalf("%v.RecordRoute(_) = _, %v", client, err)
}
for _, point := range points {
if err := stream.Send(point); err != nil {
grpclog.Fatalf("%v.Send(%v) = %v", stream, point, err)
}
}
reply, err := stream.CloseAndRecv()
if err != nil {
grpclog.Fatalf("%v.CloseAndRecv() got error %v, want %v", stream, err, nil)
}
grpclog.Printf("Route summary: %v", reply)
}
// runRouteChat receives a sequence of route notes, while sending notes for various locations.
func runRouteChat(client pb.RouteGuideClient) {
notes := []*pb.RouteNote{
{&pb.Point{0, 1}, "First message"},
{&pb.Point{0, 2}, "Second message"},
{&pb.Point{0, 3}, "Third message"},
{&pb.Point{0, 1}, "Fourth message"},
{&pb.Point{0, 2}, "Fifth message"},
{&pb.Point{0, 3}, "Sixth message"},
}
stream, err := client.RouteChat(context.Background())
if err != nil {
grpclog.Fatalf("%v.RouteChat(_) = _, %v", client, err)
}
waitc := make(chan struct{})
go func() {
for {
in, err := stream.Recv()
if err == io.EOF {
// read done.
close(waitc)
return
}
if err != nil {
grpclog.Fatalf("Failed to receive a note : %v", err)
}
grpclog.Printf("Got message %s at point(%d, %d)", in.Message, in.Location.Latitude, in.Location.Longitude)
}
}()
for _, note := range notes {
if err := stream.Send(note); err != nil {
grpclog.Fatalf("Failed to send a note: %v", err)
}
}
stream.CloseSend()
<-waitc
}
func randomPoint(r *rand.Rand) *pb.Point {
lat := (r.Int31n(180) - 90) * 1e7
long := (r.Int31n(360) - 180) * 1e7
return &pb.Point{lat, long}
}
func main() {
flag.Parse()
var opts []grpc.DialOption
if *tls {
var sn string
if *serverHostOverride != "" {
sn = *serverHostOverride
}
var creds credentials.TransportAuthenticator
if *caFile != "" {
var err error
creds, err = credentials.NewClientTLSFromFile(*caFile, sn)
if err != nil {
grpclog.Fatalf("Failed to create TLS credentials %v", err)
}
} else {
creds = credentials.NewClientTLSFromCert(nil, sn)
}
opts = append(opts, grpc.WithTransportCredentials(creds))
} else {
opts = append(opts, grpc.WithInsecure())
}
conn, err := grpc.Dial(*serverAddr, opts...)
if err != nil {
grpclog.Fatalf("fail to dial: %v", err)
}
defer conn.Close()
client := pb.NewRouteGuideClient(conn)
// Looking for a valid feature
printFeature(client, &pb.Point{409146138, -746188906})
// Feature missing.
printFeature(client, &pb.Point{0, 0})
// Looking for features between 40, -75 and 42, -73.
printFeatures(client, &pb.Rectangle{&pb.Point{400000000, -750000000}, &pb.Point{420000000, -730000000}})
// RecordRoute
runRecordRoute(client)
// RouteChat
runRouteChat(client)
}
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/examples/route_guide/routeguide/ 0000775 0000000 0000000 00000000000 12603607541 0026014 5 ustar 00root root 0000000 0000000 grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/examples/route_guide/routeguide/route_guide.pb.go 0000664 0000000 0000000 00000030122 12603607541 0031254 0 ustar 00root root 0000000 0000000 // Code generated by protoc-gen-go.
// source: route_guide.proto
// DO NOT EDIT!
/*
Package proto is a generated protocol buffer package.
It is generated from these files:
route_guide.proto
It has these top-level messages:
Point
Rectangle
Feature
RouteNote
RouteSummary
*/
package routeguide
import proto1 "github.com/golang/protobuf/proto"
import (
context "golang.org/x/net/context"
grpc "google.golang.org/grpc"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConn
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto1.Marshal
// Points are represented as latitude-longitude pairs in the E7 representation
// (degrees multiplied by 10**7 and rounded to the nearest integer).
// Latitudes should be in the range +/- 90 degrees and longitude should be in
// the range +/- 180 degrees (inclusive).
type Point struct {
Latitude int32 `protobuf:"varint,1,opt,name=latitude" json:"latitude,omitempty"`
Longitude int32 `protobuf:"varint,2,opt,name=longitude" json:"longitude,omitempty"`
}
func (m *Point) Reset() { *m = Point{} }
func (m *Point) String() string { return proto1.CompactTextString(m) }
func (*Point) ProtoMessage() {}
// A latitude-longitude rectangle, represented as two diagonally opposite
// points "lo" and "hi".
type Rectangle struct {
// One corner of the rectangle.
Lo *Point `protobuf:"bytes,1,opt,name=lo" json:"lo,omitempty"`
// The other corner of the rectangle.
Hi *Point `protobuf:"bytes,2,opt,name=hi" json:"hi,omitempty"`
}
func (m *Rectangle) Reset() { *m = Rectangle{} }
func (m *Rectangle) String() string { return proto1.CompactTextString(m) }
func (*Rectangle) ProtoMessage() {}
func (m *Rectangle) GetLo() *Point {
if m != nil {
return m.Lo
}
return nil
}
func (m *Rectangle) GetHi() *Point {
if m != nil {
return m.Hi
}
return nil
}
// A feature names something at a given point.
//
// If a feature could not be named, the name is empty.
type Feature struct {
// The name of the feature.
Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
// The point where the feature is detected.
Location *Point `protobuf:"bytes,2,opt,name=location" json:"location,omitempty"`
}
func (m *Feature) Reset() { *m = Feature{} }
func (m *Feature) String() string { return proto1.CompactTextString(m) }
func (*Feature) ProtoMessage() {}
func (m *Feature) GetLocation() *Point {
if m != nil {
return m.Location
}
return nil
}
// A RouteNote is a message sent while at a given point.
type RouteNote struct {
// The location from which the message is sent.
Location *Point `protobuf:"bytes,1,opt,name=location" json:"location,omitempty"`
// The message to be sent.
Message string `protobuf:"bytes,2,opt,name=message" json:"message,omitempty"`
}
func (m *RouteNote) Reset() { *m = RouteNote{} }
func (m *RouteNote) String() string { return proto1.CompactTextString(m) }
func (*RouteNote) ProtoMessage() {}
func (m *RouteNote) GetLocation() *Point {
if m != nil {
return m.Location
}
return nil
}
// A RouteSummary is received in response to a RecordRoute rpc.
//
// It contains the number of individual points received, the number of
// detected features, and the total distance covered as the cumulative sum of
// the distance between each point.
type RouteSummary struct {
// The number of points received.
PointCount int32 `protobuf:"varint,1,opt,name=point_count" json:"point_count,omitempty"`
// The number of known features passed while traversing the route.
FeatureCount int32 `protobuf:"varint,2,opt,name=feature_count" json:"feature_count,omitempty"`
// The distance covered in metres.
Distance int32 `protobuf:"varint,3,opt,name=distance" json:"distance,omitempty"`
// The duration of the traversal in seconds.
ElapsedTime int32 `protobuf:"varint,4,opt,name=elapsed_time" json:"elapsed_time,omitempty"`
}
func (m *RouteSummary) Reset() { *m = RouteSummary{} }
func (m *RouteSummary) String() string { return proto1.CompactTextString(m) }
func (*RouteSummary) ProtoMessage() {}
func init() {
}
// Client API for RouteGuide service
type RouteGuideClient interface {
// A simple RPC.
//
// Obtains the feature at a given position.
//
// If no feature is found for the given point, a feature with an empty name
// should be returned.
GetFeature(ctx context.Context, in *Point, opts ...grpc.CallOption) (*Feature, error)
// A server-to-client streaming RPC.
//
// Obtains the Features available within the given Rectangle. Results are
// streamed rather than returned at once (e.g. in a response message with a
// repeated field), as the rectangle may cover a large area and contain a
// huge number of features.
ListFeatures(ctx context.Context, in *Rectangle, opts ...grpc.CallOption) (RouteGuide_ListFeaturesClient, error)
// A client-to-server streaming RPC.
//
// Accepts a stream of Points on a route being traversed, returning a
// RouteSummary when traversal is completed.
RecordRoute(ctx context.Context, opts ...grpc.CallOption) (RouteGuide_RecordRouteClient, error)
// A Bidirectional streaming RPC.
//
// Accepts a stream of RouteNotes sent while a route is being traversed,
// while receiving other RouteNotes (e.g. from other users).
RouteChat(ctx context.Context, opts ...grpc.CallOption) (RouteGuide_RouteChatClient, error)
}
type routeGuideClient struct {
cc *grpc.ClientConn
}
func NewRouteGuideClient(cc *grpc.ClientConn) RouteGuideClient {
return &routeGuideClient{cc}
}
func (c *routeGuideClient) GetFeature(ctx context.Context, in *Point, opts ...grpc.CallOption) (*Feature, error) {
out := new(Feature)
err := grpc.Invoke(ctx, "/proto.RouteGuide/GetFeature", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *routeGuideClient) ListFeatures(ctx context.Context, in *Rectangle, opts ...grpc.CallOption) (RouteGuide_ListFeaturesClient, error) {
stream, err := grpc.NewClientStream(ctx, &_RouteGuide_serviceDesc.Streams[0], c.cc, "/proto.RouteGuide/ListFeatures", opts...)
if err != nil {
return nil, err
}
x := &routeGuideListFeaturesClient{stream}
if err := x.ClientStream.SendMsg(in); err != nil {
return nil, err
}
if err := x.ClientStream.CloseSend(); err != nil {
return nil, err
}
return x, nil
}
type RouteGuide_ListFeaturesClient interface {
Recv() (*Feature, error)
grpc.ClientStream
}
type routeGuideListFeaturesClient struct {
grpc.ClientStream
}
func (x *routeGuideListFeaturesClient) Recv() (*Feature, error) {
m := new(Feature)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
func (c *routeGuideClient) RecordRoute(ctx context.Context, opts ...grpc.CallOption) (RouteGuide_RecordRouteClient, error) {
stream, err := grpc.NewClientStream(ctx, &_RouteGuide_serviceDesc.Streams[1], c.cc, "/proto.RouteGuide/RecordRoute", opts...)
if err != nil {
return nil, err
}
x := &routeGuideRecordRouteClient{stream}
return x, nil
}
type RouteGuide_RecordRouteClient interface {
Send(*Point) error
CloseAndRecv() (*RouteSummary, error)
grpc.ClientStream
}
type routeGuideRecordRouteClient struct {
grpc.ClientStream
}
func (x *routeGuideRecordRouteClient) Send(m *Point) error {
return x.ClientStream.SendMsg(m)
}
func (x *routeGuideRecordRouteClient) CloseAndRecv() (*RouteSummary, error) {
if err := x.ClientStream.CloseSend(); err != nil {
return nil, err
}
m := new(RouteSummary)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
func (c *routeGuideClient) RouteChat(ctx context.Context, opts ...grpc.CallOption) (RouteGuide_RouteChatClient, error) {
stream, err := grpc.NewClientStream(ctx, &_RouteGuide_serviceDesc.Streams[2], c.cc, "/proto.RouteGuide/RouteChat", opts...)
if err != nil {
return nil, err
}
x := &routeGuideRouteChatClient{stream}
return x, nil
}
type RouteGuide_RouteChatClient interface {
Send(*RouteNote) error
Recv() (*RouteNote, error)
grpc.ClientStream
}
type routeGuideRouteChatClient struct {
grpc.ClientStream
}
func (x *routeGuideRouteChatClient) Send(m *RouteNote) error {
return x.ClientStream.SendMsg(m)
}
func (x *routeGuideRouteChatClient) Recv() (*RouteNote, error) {
m := new(RouteNote)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
// Server API for RouteGuide service
type RouteGuideServer interface {
// A simple RPC.
//
// Obtains the feature at a given position.
//
// If no feature is found for the given point, a feature with an empty name
// should be returned.
GetFeature(context.Context, *Point) (*Feature, error)
// A server-to-client streaming RPC.
//
// Obtains the Features available within the given Rectangle. Results are
// streamed rather than returned at once (e.g. in a response message with a
// repeated field), as the rectangle may cover a large area and contain a
// huge number of features.
ListFeatures(*Rectangle, RouteGuide_ListFeaturesServer) error
// A client-to-server streaming RPC.
//
// Accepts a stream of Points on a route being traversed, returning a
// RouteSummary when traversal is completed.
RecordRoute(RouteGuide_RecordRouteServer) error
// A Bidirectional streaming RPC.
//
// Accepts a stream of RouteNotes sent while a route is being traversed,
// while receiving other RouteNotes (e.g. from other users).
RouteChat(RouteGuide_RouteChatServer) error
}
func RegisterRouteGuideServer(s *grpc.Server, srv RouteGuideServer) {
s.RegisterService(&_RouteGuide_serviceDesc, srv)
}
func _RouteGuide_GetFeature_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error) (interface{}, error) {
in := new(Point)
if err := dec(in); err != nil {
return nil, err
}
out, err := srv.(RouteGuideServer).GetFeature(ctx, in)
if err != nil {
return nil, err
}
return out, nil
}
func _RouteGuide_ListFeatures_Handler(srv interface{}, stream grpc.ServerStream) error {
m := new(Rectangle)
if err := stream.RecvMsg(m); err != nil {
return err
}
return srv.(RouteGuideServer).ListFeatures(m, &routeGuideListFeaturesServer{stream})
}
type RouteGuide_ListFeaturesServer interface {
Send(*Feature) error
grpc.ServerStream
}
type routeGuideListFeaturesServer struct {
grpc.ServerStream
}
func (x *routeGuideListFeaturesServer) Send(m *Feature) error {
return x.ServerStream.SendMsg(m)
}
func _RouteGuide_RecordRoute_Handler(srv interface{}, stream grpc.ServerStream) error {
return srv.(RouteGuideServer).RecordRoute(&routeGuideRecordRouteServer{stream})
}
type RouteGuide_RecordRouteServer interface {
SendAndClose(*RouteSummary) error
Recv() (*Point, error)
grpc.ServerStream
}
type routeGuideRecordRouteServer struct {
grpc.ServerStream
}
func (x *routeGuideRecordRouteServer) SendAndClose(m *RouteSummary) error {
return x.ServerStream.SendMsg(m)
}
func (x *routeGuideRecordRouteServer) Recv() (*Point, error) {
m := new(Point)
if err := x.ServerStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
func _RouteGuide_RouteChat_Handler(srv interface{}, stream grpc.ServerStream) error {
return srv.(RouteGuideServer).RouteChat(&routeGuideRouteChatServer{stream})
}
type RouteGuide_RouteChatServer interface {
Send(*RouteNote) error
Recv() (*RouteNote, error)
grpc.ServerStream
}
type routeGuideRouteChatServer struct {
grpc.ServerStream
}
func (x *routeGuideRouteChatServer) Send(m *RouteNote) error {
return x.ServerStream.SendMsg(m)
}
func (x *routeGuideRouteChatServer) Recv() (*RouteNote, error) {
m := new(RouteNote)
if err := x.ServerStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
var _RouteGuide_serviceDesc = grpc.ServiceDesc{
ServiceName: "proto.RouteGuide",
HandlerType: (*RouteGuideServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "GetFeature",
Handler: _RouteGuide_GetFeature_Handler,
},
},
Streams: []grpc.StreamDesc{
{
StreamName: "ListFeatures",
Handler: _RouteGuide_ListFeatures_Handler,
ServerStreams: true,
},
{
StreamName: "RecordRoute",
Handler: _RouteGuide_RecordRoute_Handler,
ClientStreams: true,
},
{
StreamName: "RouteChat",
Handler: _RouteGuide_RouteChat_Handler,
ServerStreams: true,
ClientStreams: true,
},
},
}
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/examples/route_guide/routeguide/route_guide.proto 0000664 0000000 0000000 00000010302 12603607541 0031410 0 ustar 00root root 0000000 0000000 // Copyright 2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
syntax = "proto3";
package routeguide;
// Interface exported by the server.
service RouteGuide {
// A simple RPC.
//
// Obtains the feature at a given position.
//
// If no feature is found for the given point, a feature with an empty name
// should be returned.
rpc GetFeature(Point) returns (Feature) {}
// A server-to-client streaming RPC.
//
// Obtains the Features available within the given Rectangle. Results are
// streamed rather than returned at once (e.g. in a response message with a
// repeated field), as the rectangle may cover a large area and contain a
// huge number of features.
rpc ListFeatures(Rectangle) returns (stream Feature) {}
// A client-to-server streaming RPC.
//
// Accepts a stream of Points on a route being traversed, returning a
// RouteSummary when traversal is completed.
rpc RecordRoute(stream Point) returns (RouteSummary) {}
// A Bidirectional streaming RPC.
//
// Accepts a stream of RouteNotes sent while a route is being traversed,
// while receiving other RouteNotes (e.g. from other users).
rpc RouteChat(stream RouteNote) returns (stream RouteNote) {}
}
// Points are represented as latitude-longitude pairs in the E7 representation
// (degrees multiplied by 10**7 and rounded to the nearest integer).
// Latitudes should be in the range +/- 90 degrees and longitude should be in
// the range +/- 180 degrees (inclusive).
message Point {
int32 latitude = 1;
int32 longitude = 2;
}
// A latitude-longitude rectangle, represented as two diagonally opposite
// points "lo" and "hi".
message Rectangle {
// One corner of the rectangle.
Point lo = 1;
// The other corner of the rectangle.
Point hi = 2;
}
// A feature names something at a given point.
//
// If a feature could not be named, the name is empty.
message Feature {
// The name of the feature.
string name = 1;
// The point where the feature is detected.
Point location = 2;
}
// A RouteNote is a message sent while at a given point.
message RouteNote {
// The location from which the message is sent.
Point location = 1;
// The message to be sent.
string message = 2;
}
// A RouteSummary is received in response to a RecordRoute rpc.
//
// It contains the number of individual points received, the number of
// detected features, and the total distance covered as the cumulative sum of
// the distance between each point.
message RouteSummary {
// The number of points received.
int32 point_count = 1;
// The number of known features passed while traversing the route.
int32 feature_count = 2;
// The distance covered in metres.
int32 distance = 3;
// The duration of the traversal in seconds.
int32 elapsed_time = 4;
}
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/examples/route_guide/server/ 0000775 0000000 0000000 00000000000 12603607541 0025146 5 ustar 00root root 0000000 0000000 grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/examples/route_guide/server/server.go 0000664 0000000 0000000 00000016711 12603607541 0027011 0 ustar 00root root 0000000 0000000 /*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
// Package main implements a simple gRPC server that demonstrates how to use gRPC-Go libraries
// to perform unary, client streaming, server streaming and full duplex RPCs.
//
// It implements the route guide service whose definition can be found in proto/route_guide.proto.
package main
import (
"encoding/json"
"flag"
"fmt"
"io"
"io/ioutil"
"math"
"net"
"time"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/grpclog"
"github.com/golang/protobuf/proto"
pb "google.golang.org/grpc/examples/route_guide/routeguide"
)
var (
tls = flag.Bool("tls", false, "Connection uses TLS if true, else plain TCP")
certFile = flag.String("cert_file", "testdata/server1.pem", "The TLS cert file")
keyFile = flag.String("key_file", "testdata/server1.key", "The TLS key file")
jsonDBFile = flag.String("json_db_file", "testdata/route_guide_db.json", "A json file containing a list of features")
port = flag.Int("port", 10000, "The server port")
)
type routeGuideServer struct {
savedFeatures []*pb.Feature
routeNotes map[string][]*pb.RouteNote
}
// GetFeature returns the feature at the given point.
func (s *routeGuideServer) GetFeature(ctx context.Context, point *pb.Point) (*pb.Feature, error) {
for _, feature := range s.savedFeatures {
if proto.Equal(feature.Location, point) {
return feature, nil
}
}
// No feature was found, return an unnamed feature
return &pb.Feature{"", point}, nil
}
// ListFeatures lists all features comtained within the given bounding Rectangle.
func (s *routeGuideServer) ListFeatures(rect *pb.Rectangle, stream pb.RouteGuide_ListFeaturesServer) error {
for _, feature := range s.savedFeatures {
if inRange(feature.Location, rect) {
if err := stream.Send(feature); err != nil {
return err
}
}
}
return nil
}
// RecordRoute records a route composited of a sequence of points.
//
// It gets a stream of points, and responds with statistics about the "trip":
// number of points, number of known features visited, total distance traveled, and
// total time spent.
func (s *routeGuideServer) RecordRoute(stream pb.RouteGuide_RecordRouteServer) error {
var pointCount, featureCount, distance int32
var lastPoint *pb.Point
startTime := time.Now()
for {
point, err := stream.Recv()
if err == io.EOF {
endTime := time.Now()
return stream.SendAndClose(&pb.RouteSummary{
PointCount: pointCount,
FeatureCount: featureCount,
Distance: distance,
ElapsedTime: int32(endTime.Sub(startTime).Seconds()),
})
}
if err != nil {
return err
}
pointCount++
for _, feature := range s.savedFeatures {
if proto.Equal(feature.Location, point) {
featureCount++
}
}
if lastPoint != nil {
distance += calcDistance(lastPoint, point)
}
lastPoint = point
}
}
// RouteChat receives a stream of message/location pairs, and responds with a stream of all
// previous messages at each of those locations.
func (s *routeGuideServer) RouteChat(stream pb.RouteGuide_RouteChatServer) error {
for {
in, err := stream.Recv()
if err == io.EOF {
return nil
}
if err != nil {
return err
}
key := serialize(in.Location)
if _, present := s.routeNotes[key]; !present {
s.routeNotes[key] = []*pb.RouteNote{in}
} else {
s.routeNotes[key] = append(s.routeNotes[key], in)
}
for _, note := range s.routeNotes[key] {
if err := stream.Send(note); err != nil {
return err
}
}
}
}
// loadFeatures loads features from a JSON file.
func (s *routeGuideServer) loadFeatures(filePath string) {
file, err := ioutil.ReadFile(filePath)
if err != nil {
grpclog.Fatalf("Failed to load default features: %v", err)
}
if err := json.Unmarshal(file, &s.savedFeatures); err != nil {
grpclog.Fatalf("Failed to load default features: %v", err)
}
}
func toRadians(num float64) float64 {
return num * math.Pi / float64(180)
}
// calcDistance calculates the distance between two points using the "haversine" formula.
// This code was taken from http://www.movable-type.co.uk/scripts/latlong.html.
func calcDistance(p1 *pb.Point, p2 *pb.Point) int32 {
const CordFactor float64 = 1e7
const R float64 = float64(6371000) // metres
lat1 := float64(p1.Latitude) / CordFactor
lat2 := float64(p2.Latitude) / CordFactor
lng1 := float64(p1.Longitude) / CordFactor
lng2 := float64(p2.Longitude) / CordFactor
φ1 := toRadians(lat1)
φ2 := toRadians(lat2)
Δφ := toRadians(lat2 - lat1)
Δλ := toRadians(lng2 - lng1)
a := math.Sin(Δφ/2)*math.Sin(Δφ/2) +
math.Cos(φ1)*math.Cos(φ2)*
math.Sin(Δλ/2)*math.Sin(Δλ/2)
c := 2 * math.Atan2(math.Sqrt(a), math.Sqrt(1-a))
distance := R * c
return int32(distance)
}
func inRange(point *pb.Point, rect *pb.Rectangle) bool {
left := math.Min(float64(rect.Lo.Longitude), float64(rect.Hi.Longitude))
right := math.Max(float64(rect.Lo.Longitude), float64(rect.Hi.Longitude))
top := math.Max(float64(rect.Lo.Latitude), float64(rect.Hi.Latitude))
bottom := math.Min(float64(rect.Lo.Latitude), float64(rect.Hi.Latitude))
if float64(point.Longitude) >= left &&
float64(point.Longitude) <= right &&
float64(point.Latitude) >= bottom &&
float64(point.Latitude) <= top {
return true
}
return false
}
func serialize(point *pb.Point) string {
return fmt.Sprintf("%d %d", point.Latitude, point.Longitude)
}
func newServer() *routeGuideServer {
s := new(routeGuideServer)
s.loadFeatures(*jsonDBFile)
s.routeNotes = make(map[string][]*pb.RouteNote)
return s
}
func main() {
flag.Parse()
lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port))
if err != nil {
grpclog.Fatalf("failed to listen: %v", err)
}
var opts []grpc.ServerOption
if *tls {
creds, err := credentials.NewServerTLSFromFile(*certFile, *keyFile)
if err != nil {
grpclog.Fatalf("Failed to generate credentials %v", err)
}
opts = []grpc.ServerOption{grpc.Creds(creds)}
}
grpcServer := grpc.NewServer(opts...)
pb.RegisterRouteGuideServer(grpcServer, newServer())
grpcServer.Serve(lis)
}
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/examples/route_guide/testdata/ 0000775 0000000 0000000 00000000000 12603607541 0025451 5 ustar 00root root 0000000 0000000 grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/examples/route_guide/testdata/ca.pem 0000664 0000000 0000000 00000001442 12603607541 0026540 0 ustar 00root root 0000000 0000000 -----BEGIN CERTIFICATE-----
MIICIzCCAYwCCQCFTbF7XNSvvjANBgkqhkiG9w0BAQUFADBWMQswCQYDVQQGEwJB
VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0
cyBQdHkgTHRkMQ8wDQYDVQQDDAZ0ZXN0Y2EwHhcNMTQwNzE3MjMxNzUxWhcNMjQw
NzE0MjMxNzUxWjBWMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEh
MB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMQ8wDQYDVQQDDAZ0ZXN0
Y2EwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMBA3wVeTGHZR1Rye/i+J8a2
cu5gXwFV6TnObzGM7bLFCO5i9v4mLo4iFzPsHmWDUxKS3Y8iXbu0eYBlLoNY0lSv
xDx33O+DuwMmVN+DzSD+Eod9zfvwOWHsazYCZT2PhNxnVWIuJXViY4JAHUGodjx+
QAi6yCAurUZGvYXGgZSBAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAQoQVD8bwdtWJ
AniGBwcCfqYyH+/KpA10AcebJVVTyYbYvI9Q8d6RSVu4PZy9OALHR/QrWBdYTAyz
fNAmc2cmdkSRJzjhIaOstnQy1J+Fk0T9XyvQtq499yFbq9xogUVlEGH62xP6vH0Y
5ukK//dCPAzA11YuX2rnex0JhuTQfcI=
-----END CERTIFICATE-----
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/examples/route_guide/testdata/route_guide_db.json 0000664 0000000 0000000 00000032710 12603607541 0031327 0 ustar 00root root 0000000 0000000 [{
"location": {
"latitude": 407838351,
"longitude": -746143763
},
"name": "Patriots Path, Mendham, NJ 07945, USA"
}, {
"location": {
"latitude": 408122808,
"longitude": -743999179
},
"name": "101 New Jersey 10, Whippany, NJ 07981, USA"
}, {
"location": {
"latitude": 413628156,
"longitude": -749015468
},
"name": "U.S. 6, Shohola, PA 18458, USA"
}, {
"location": {
"latitude": 419999544,
"longitude": -740371136
},
"name": "5 Conners Road, Kingston, NY 12401, USA"
}, {
"location": {
"latitude": 414008389,
"longitude": -743951297
},
"name": "Mid Hudson Psychiatric Center, New Hampton, NY 10958, USA"
}, {
"location": {
"latitude": 419611318,
"longitude": -746524769
},
"name": "287 Flugertown Road, Livingston Manor, NY 12758, USA"
}, {
"location": {
"latitude": 406109563,
"longitude": -742186778
},
"name": "4001 Tremley Point Road, Linden, NJ 07036, USA"
}, {
"location": {
"latitude": 416802456,
"longitude": -742370183
},
"name": "352 South Mountain Road, Wallkill, NY 12589, USA"
}, {
"location": {
"latitude": 412950425,
"longitude": -741077389
},
"name": "Bailey Turn Road, Harriman, NY 10926, USA"
}, {
"location": {
"latitude": 412144655,
"longitude": -743949739
},
"name": "193-199 Wawayanda Road, Hewitt, NJ 07421, USA"
}, {
"location": {
"latitude": 415736605,
"longitude": -742847522
},
"name": "406-496 Ward Avenue, Pine Bush, NY 12566, USA"
}, {
"location": {
"latitude": 413843930,
"longitude": -740501726
},
"name": "162 Merrill Road, Highland Mills, NY 10930, USA"
}, {
"location": {
"latitude": 410873075,
"longitude": -744459023
},
"name": "Clinton Road, West Milford, NJ 07480, USA"
}, {
"location": {
"latitude": 412346009,
"longitude": -744026814
},
"name": "16 Old Brook Lane, Warwick, NY 10990, USA"
}, {
"location": {
"latitude": 402948455,
"longitude": -747903913
},
"name": "3 Drake Lane, Pennington, NJ 08534, USA"
}, {
"location": {
"latitude": 406337092,
"longitude": -740122226
},
"name": "6324 8th Avenue, Brooklyn, NY 11220, USA"
}, {
"location": {
"latitude": 406421967,
"longitude": -747727624
},
"name": "1 Merck Access Road, Whitehouse Station, NJ 08889, USA"
}, {
"location": {
"latitude": 416318082,
"longitude": -749677716
},
"name": "78-98 Schalck Road, Narrowsburg, NY 12764, USA"
}, {
"location": {
"latitude": 415301720,
"longitude": -748416257
},
"name": "282 Lakeview Drive Road, Highland Lake, NY 12743, USA"
}, {
"location": {
"latitude": 402647019,
"longitude": -747071791
},
"name": "330 Evelyn Avenue, Hamilton Township, NJ 08619, USA"
}, {
"location": {
"latitude": 412567807,
"longitude": -741058078
},
"name": "New York State Reference Route 987E, Southfields, NY 10975, USA"
}, {
"location": {
"latitude": 416855156,
"longitude": -744420597
},
"name": "103-271 Tempaloni Road, Ellenville, NY 12428, USA"
}, {
"location": {
"latitude": 404663628,
"longitude": -744820157
},
"name": "1300 Airport Road, North Brunswick Township, NJ 08902, USA"
}, {
"location": {
"latitude": 407113723,
"longitude": -749746483
},
"name": ""
}, {
"location": {
"latitude": 402133926,
"longitude": -743613249
},
"name": ""
}, {
"location": {
"latitude": 400273442,
"longitude": -741220915
},
"name": ""
}, {
"location": {
"latitude": 411236786,
"longitude": -744070769
},
"name": ""
}, {
"location": {
"latitude": 411633782,
"longitude": -746784970
},
"name": "211-225 Plains Road, Augusta, NJ 07822, USA"
}, {
"location": {
"latitude": 415830701,
"longitude": -742952812
},
"name": ""
}, {
"location": {
"latitude": 413447164,
"longitude": -748712898
},
"name": "165 Pedersen Ridge Road, Milford, PA 18337, USA"
}, {
"location": {
"latitude": 405047245,
"longitude": -749800722
},
"name": "100-122 Locktown Road, Frenchtown, NJ 08825, USA"
}, {
"location": {
"latitude": 418858923,
"longitude": -746156790
},
"name": ""
}, {
"location": {
"latitude": 417951888,
"longitude": -748484944
},
"name": "650-652 Willi Hill Road, Swan Lake, NY 12783, USA"
}, {
"location": {
"latitude": 407033786,
"longitude": -743977337
},
"name": "26 East 3rd Street, New Providence, NJ 07974, USA"
}, {
"location": {
"latitude": 417548014,
"longitude": -740075041
},
"name": ""
}, {
"location": {
"latitude": 410395868,
"longitude": -744972325
},
"name": ""
}, {
"location": {
"latitude": 404615353,
"longitude": -745129803
},
"name": ""
}, {
"location": {
"latitude": 406589790,
"longitude": -743560121
},
"name": "611 Lawrence Avenue, Westfield, NJ 07090, USA"
}, {
"location": {
"latitude": 414653148,
"longitude": -740477477
},
"name": "18 Lannis Avenue, New Windsor, NY 12553, USA"
}, {
"location": {
"latitude": 405957808,
"longitude": -743255336
},
"name": "82-104 Amherst Avenue, Colonia, NJ 07067, USA"
}, {
"location": {
"latitude": 411733589,
"longitude": -741648093
},
"name": "170 Seven Lakes Drive, Sloatsburg, NY 10974, USA"
}, {
"location": {
"latitude": 412676291,
"longitude": -742606606
},
"name": "1270 Lakes Road, Monroe, NY 10950, USA"
}, {
"location": {
"latitude": 409224445,
"longitude": -748286738
},
"name": "509-535 Alphano Road, Great Meadows, NJ 07838, USA"
}, {
"location": {
"latitude": 406523420,
"longitude": -742135517
},
"name": "652 Garden Street, Elizabeth, NJ 07202, USA"
}, {
"location": {
"latitude": 401827388,
"longitude": -740294537
},
"name": "349 Sea Spray Court, Neptune City, NJ 07753, USA"
}, {
"location": {
"latitude": 410564152,
"longitude": -743685054
},
"name": "13-17 Stanley Street, West Milford, NJ 07480, USA"
}, {
"location": {
"latitude": 408472324,
"longitude": -740726046
},
"name": "47 Industrial Avenue, Teterboro, NJ 07608, USA"
}, {
"location": {
"latitude": 412452168,
"longitude": -740214052
},
"name": "5 White Oak Lane, Stony Point, NY 10980, USA"
}, {
"location": {
"latitude": 409146138,
"longitude": -746188906
},
"name": "Berkshire Valley Management Area Trail, Jefferson, NJ, USA"
}, {
"location": {
"latitude": 404701380,
"longitude": -744781745
},
"name": "1007 Jersey Avenue, New Brunswick, NJ 08901, USA"
}, {
"location": {
"latitude": 409642566,
"longitude": -746017679
},
"name": "6 East Emerald Isle Drive, Lake Hopatcong, NJ 07849, USA"
}, {
"location": {
"latitude": 408031728,
"longitude": -748645385
},
"name": "1358-1474 New Jersey 57, Port Murray, NJ 07865, USA"
}, {
"location": {
"latitude": 413700272,
"longitude": -742135189
},
"name": "367 Prospect Road, Chester, NY 10918, USA"
}, {
"location": {
"latitude": 404310607,
"longitude": -740282632
},
"name": "10 Simon Lake Drive, Atlantic Highlands, NJ 07716, USA"
}, {
"location": {
"latitude": 409319800,
"longitude": -746201391
},
"name": "11 Ward Street, Mount Arlington, NJ 07856, USA"
}, {
"location": {
"latitude": 406685311,
"longitude": -742108603
},
"name": "300-398 Jefferson Avenue, Elizabeth, NJ 07201, USA"
}, {
"location": {
"latitude": 419018117,
"longitude": -749142781
},
"name": "43 Dreher Road, Roscoe, NY 12776, USA"
}, {
"location": {
"latitude": 412856162,
"longitude": -745148837
},
"name": "Swan Street, Pine Island, NY 10969, USA"
}, {
"location": {
"latitude": 416560744,
"longitude": -746721964
},
"name": "66 Pleasantview Avenue, Monticello, NY 12701, USA"
}, {
"location": {
"latitude": 405314270,
"longitude": -749836354
},
"name": ""
}, {
"location": {
"latitude": 414219548,
"longitude": -743327440
},
"name": ""
}, {
"location": {
"latitude": 415534177,
"longitude": -742900616
},
"name": "565 Winding Hills Road, Montgomery, NY 12549, USA"
}, {
"location": {
"latitude": 406898530,
"longitude": -749127080
},
"name": "231 Rocky Run Road, Glen Gardner, NJ 08826, USA"
}, {
"location": {
"latitude": 407586880,
"longitude": -741670168
},
"name": "100 Mount Pleasant Avenue, Newark, NJ 07104, USA"
}, {
"location": {
"latitude": 400106455,
"longitude": -742870190
},
"name": "517-521 Huntington Drive, Manchester Township, NJ 08759, USA"
}, {
"location": {
"latitude": 400066188,
"longitude": -746793294
},
"name": ""
}, {
"location": {
"latitude": 418803880,
"longitude": -744102673
},
"name": "40 Mountain Road, Napanoch, NY 12458, USA"
}, {
"location": {
"latitude": 414204288,
"longitude": -747895140
},
"name": ""
}, {
"location": {
"latitude": 414777405,
"longitude": -740615601
},
"name": ""
}, {
"location": {
"latitude": 415464475,
"longitude": -747175374
},
"name": "48 North Road, Forestburgh, NY 12777, USA"
}, {
"location": {
"latitude": 404062378,
"longitude": -746376177
},
"name": ""
}, {
"location": {
"latitude": 405688272,
"longitude": -749285130
},
"name": ""
}, {
"location": {
"latitude": 400342070,
"longitude": -748788996
},
"name": ""
}, {
"location": {
"latitude": 401809022,
"longitude": -744157964
},
"name": ""
}, {
"location": {
"latitude": 404226644,
"longitude": -740517141
},
"name": "9 Thompson Avenue, Leonardo, NJ 07737, USA"
}, {
"location": {
"latitude": 410322033,
"longitude": -747871659
},
"name": ""
}, {
"location": {
"latitude": 407100674,
"longitude": -747742727
},
"name": ""
}, {
"location": {
"latitude": 418811433,
"longitude": -741718005
},
"name": "213 Bush Road, Stone Ridge, NY 12484, USA"
}, {
"location": {
"latitude": 415034302,
"longitude": -743850945
},
"name": ""
}, {
"location": {
"latitude": 411349992,
"longitude": -743694161
},
"name": ""
}, {
"location": {
"latitude": 404839914,
"longitude": -744759616
},
"name": "1-17 Bergen Court, New Brunswick, NJ 08901, USA"
}, {
"location": {
"latitude": 414638017,
"longitude": -745957854
},
"name": "35 Oakland Valley Road, Cuddebackville, NY 12729, USA"
}, {
"location": {
"latitude": 412127800,
"longitude": -740173578
},
"name": ""
}, {
"location": {
"latitude": 401263460,
"longitude": -747964303
},
"name": ""
}, {
"location": {
"latitude": 412843391,
"longitude": -749086026
},
"name": ""
}, {
"location": {
"latitude": 418512773,
"longitude": -743067823
},
"name": ""
}, {
"location": {
"latitude": 404318328,
"longitude": -740835638
},
"name": "42-102 Main Street, Belford, NJ 07718, USA"
}, {
"location": {
"latitude": 419020746,
"longitude": -741172328
},
"name": ""
}, {
"location": {
"latitude": 404080723,
"longitude": -746119569
},
"name": ""
}, {
"location": {
"latitude": 401012643,
"longitude": -744035134
},
"name": ""
}, {
"location": {
"latitude": 404306372,
"longitude": -741079661
},
"name": ""
}, {
"location": {
"latitude": 403966326,
"longitude": -748519297
},
"name": ""
}, {
"location": {
"latitude": 405002031,
"longitude": -748407866
},
"name": ""
}, {
"location": {
"latitude": 409532885,
"longitude": -742200683
},
"name": ""
}, {
"location": {
"latitude": 416851321,
"longitude": -742674555
},
"name": ""
}, {
"location": {
"latitude": 406411633,
"longitude": -741722051
},
"name": "3387 Richmond Terrace, Staten Island, NY 10303, USA"
}, {
"location": {
"latitude": 413069058,
"longitude": -744597778
},
"name": "261 Van Sickle Road, Goshen, NY 10924, USA"
}, {
"location": {
"latitude": 418465462,
"longitude": -746859398
},
"name": ""
}, {
"location": {
"latitude": 411733222,
"longitude": -744228360
},
"name": ""
}, {
"location": {
"latitude": 410248224,
"longitude": -747127767
},
"name": "3 Hasta Way, Newton, NJ 07860, USA"
}]
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/examples/route_guide/testdata/server1.key 0000664 0000000 0000000 00000001567 12603607541 0027563 0 ustar 00root root 0000000 0000000 -----BEGIN RSA PRIVATE KEY-----
MIICWwIBAAKBgQDhwxUnKCwlSaWAwzOB2LSHVegJHv7DDWminTg4wzLLsf+LQ8nZ
bpjfn5vgIzxCuRh4Rp9QYM5FhfrJX9wcYawP/HTbJ7p7LVQO2QYAP+akMTHxgKuM
BzVV++3wWToKfVZUjFX8nfTfGMGwWAHJDnlEGnU4tl9UujoCV4ENJtzFoQIDAQAB
AoGAJ+6hpzNr24yTQZtFWQpDpEyFplddKJMOxDya3S9ppK3vTWrIITV2xNcucw7I
ceTbdyrGsyjsU0/HdCcIf9ym2jfmGLUwmyhltKVw0QYcFB0XLkc0nI5YvEYoeVDg
omZIXn1E3EW+sSIWSbkMu9bY2kstKXR2UZmMgWDtmBEPMaECQQD6yT4TAZM5hGBb
ciBKgMUP6PwOhPhOMPIvijO50Aiu6iuCV88l1QIy38gWVhxjNrq6P346j4IBg+kB
9alwpCODAkEA5nSnm9k6ykYeQWNS0fNWiRinCdl23A7usDGSuKKlm019iomJ/Rgd
MKDOp0q/2OostbteOWM2MRFf4jMH3wyVCwJAfAdjJ8szoNKTRSagSbh9vWygnB2v
IByc6l4TTuZQJRGzCveafz9lovuB3WohCABdQRd9ukCXL2CpsEpqzkafOQJAJUjc
USedDlq3zGZwYM1Yw8d8RuirBUFZNqJelYai+nRYClDkRVFgb5yksoYycbq5TxGo
VeqKOvgPpj4RWPHlLwJAGUMk3bqT91xBUCnLRs/vfoCpHpg6eywQTBDAV6xkyz4a
RH3I7/+yj3ZxR2JoWHgUwZ7lZk1VnhffFye7SBXyag==
-----END RSA PRIVATE KEY-----
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/examples/route_guide/testdata/server1.pem 0000664 0000000 0000000 00000001704 12603607541 0027545 0 ustar 00root root 0000000 0000000 -----BEGIN CERTIFICATE-----
MIICmzCCAgSgAwIBAgIBAzANBgkqhkiG9w0BAQUFADBWMQswCQYDVQQGEwJBVTET
MBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQ
dHkgTHRkMQ8wDQYDVQQDDAZ0ZXN0Y2EwHhcNMTQwNzIyMDYwMDU3WhcNMjQwNzE5
MDYwMDU3WjBkMQswCQYDVQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNV
BAcTB0NoaWNhZ28xFDASBgNVBAoTC0dvb2dsZSBJbmMuMRowGAYDVQQDFBEqLnRl
c3QuZ29vZ2xlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA4cMVJygs
JUmlgMMzgdi0h1XoCR7+ww1pop04OMMyy7H/i0PJ2W6Y35+b4CM8QrkYeEafUGDO
RYX6yV/cHGGsD/x02ye6ey1UDtkGAD/mpDEx8YCrjAc1Vfvt8Fk6Cn1WVIxV/J30
3xjBsFgByQ55RBp1OLZfVLo6AleBDSbcxaECAwEAAaNrMGkwCQYDVR0TBAIwADAL
BgNVHQ8EBAMCBeAwTwYDVR0RBEgwRoIQKi50ZXN0Lmdvb2dsZS5mcoIYd2F0ZXJ6
b29pLnRlc3QuZ29vZ2xlLmJlghIqLnRlc3QueW91dHViZS5jb22HBMCoAQMwDQYJ
KoZIhvcNAQEFBQADgYEAM2Ii0LgTGbJ1j4oqX9bxVcxm+/R5Yf8oi0aZqTJlnLYS
wXcBykxTx181s7WyfJ49WwrYXo78zTDAnf1ma0fPq3e4mpspvyndLh1a+OarHa1e
aT0DIIYk7qeEa1YcVljx2KyLd0r1BBAfrwyGaEPVeJQVYWaOJRU2we/KD4ojf9s=
-----END CERTIFICATE-----
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/grpclog/ 0000775 0000000 0000000 00000000000 12603607541 0021144 5 ustar 00root root 0000000 0000000 grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/grpclog/glogger/ 0000775 0000000 0000000 00000000000 12603607541 0022572 5 ustar 00root root 0000000 0000000 grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/grpclog/glogger/glogger.go 0000664 0000000 0000000 00000004335 12603607541 0024554 0 ustar 00root root 0000000 0000000 /*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/*
Package glogger defines glog-based logging for grpc.
*/
package glogger
import (
"github.com/golang/glog"
"google.golang.org/grpc/grpclog"
)
func init() {
grpclog.SetLogger(&glogger{})
}
type glogger struct{}
func (g *glogger) Fatal(args ...interface{}) {
glog.Fatal(args...)
}
func (g *glogger) Fatalf(format string, args ...interface{}) {
glog.Fatalf(format, args...)
}
func (g *glogger) Fatalln(args ...interface{}) {
glog.Fatalln(args...)
}
func (g *glogger) Print(args ...interface{}) {
glog.Info(args...)
}
func (g *glogger) Printf(format string, args ...interface{}) {
glog.Infof(format, args...)
}
func (g *glogger) Println(args ...interface{}) {
glog.Infoln(args...)
}
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/grpclog/logger.go 0000664 0000000 0000000 00000006105 12603607541 0022754 0 ustar 00root root 0000000 0000000 /*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/*
Package grpclog defines logging for grpc.
*/
package grpclog // import "google.golang.org/grpc/grpclog"
import (
"log"
"os"
)
// Use golang's standard logger by default.
var logger Logger = log.New(os.Stderr, "", log.LstdFlags)
// Logger mimics golang's standard Logger as an interface.
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.
func SetLogger(l Logger) {
logger = l
}
// Fatal is equivalent to Print() followed by a call to os.Exit() with a non-zero exit code.
func Fatal(args ...interface{}) {
logger.Fatal(args...)
}
// Fatalf is equivalent to Printf() followed by a call to os.Exit() with a non-zero exit code.
func Fatalf(format string, args ...interface{}) {
logger.Fatalf(format, args...)
}
// Fatalln is equivalent to Println() followed by a call to os.Exit()) with a non-zero exit code.
func Fatalln(args ...interface{}) {
logger.Fatalln(args...)
}
// Print prints to the logger. Arguments are handled in the manner of fmt.Print.
func Print(args ...interface{}) {
logger.Print(args...)
}
// Printf prints to the logger. Arguments are handled in the manner of fmt.Printf.
func Printf(format string, args ...interface{}) {
logger.Printf(format, args...)
}
// Println prints to the logger. Arguments are handled in the manner of fmt.Println.
func Println(args ...interface{}) {
logger.Println(args...)
}
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/health/ 0000775 0000000 0000000 00000000000 12603607541 0020754 5 ustar 00root root 0000000 0000000 grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/health/grpc_health_v1alpha/ 0000775 0000000 0000000 00000000000 12603607541 0024650 5 ustar 00root root 0000000 0000000 grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/health/grpc_health_v1alpha/health.pb.go 0000664 0000000 0000000 00000007175 12603607541 0027056 0 ustar 00root root 0000000 0000000 // Code generated by protoc-gen-go.
// source: health.proto
// DO NOT EDIT!
/*
Package grpc_health_v1alpha is a generated protocol buffer package.
It is generated from these files:
health.proto
It has these top-level messages:
HealthCheckRequest
HealthCheckResponse
*/
package grpc_health_v1alpha
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
import (
context "golang.org/x/net/context"
grpc "google.golang.org/grpc"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
type HealthCheckResponse_ServingStatus int32
const (
HealthCheckResponse_UNKNOWN HealthCheckResponse_ServingStatus = 0
HealthCheckResponse_SERVING HealthCheckResponse_ServingStatus = 1
HealthCheckResponse_NOT_SERVING HealthCheckResponse_ServingStatus = 2
)
var HealthCheckResponse_ServingStatus_name = map[int32]string{
0: "UNKNOWN",
1: "SERVING",
2: "NOT_SERVING",
}
var HealthCheckResponse_ServingStatus_value = map[string]int32{
"UNKNOWN": 0,
"SERVING": 1,
"NOT_SERVING": 2,
}
func (x HealthCheckResponse_ServingStatus) String() string {
return proto.EnumName(HealthCheckResponse_ServingStatus_name, int32(x))
}
type HealthCheckRequest struct {
Service string `protobuf:"bytes,1,opt,name=service" json:"service,omitempty"`
}
func (m *HealthCheckRequest) Reset() { *m = HealthCheckRequest{} }
func (m *HealthCheckRequest) String() string { return proto.CompactTextString(m) }
func (*HealthCheckRequest) ProtoMessage() {}
type HealthCheckResponse struct {
Status HealthCheckResponse_ServingStatus `protobuf:"varint,1,opt,name=status,enum=grpc.health.v1alpha.HealthCheckResponse_ServingStatus" json:"status,omitempty"`
}
func (m *HealthCheckResponse) Reset() { *m = HealthCheckResponse{} }
func (m *HealthCheckResponse) String() string { return proto.CompactTextString(m) }
func (*HealthCheckResponse) ProtoMessage() {}
func init() {
proto.RegisterEnum("grpc.health.v1alpha.HealthCheckResponse_ServingStatus", HealthCheckResponse_ServingStatus_name, HealthCheckResponse_ServingStatus_value)
}
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConn
// Client API for Health service
type HealthClient interface {
Check(ctx context.Context, in *HealthCheckRequest, opts ...grpc.CallOption) (*HealthCheckResponse, error)
}
type healthClient struct {
cc *grpc.ClientConn
}
func NewHealthClient(cc *grpc.ClientConn) HealthClient {
return &healthClient{cc}
}
func (c *healthClient) Check(ctx context.Context, in *HealthCheckRequest, opts ...grpc.CallOption) (*HealthCheckResponse, error) {
out := new(HealthCheckResponse)
err := grpc.Invoke(ctx, "/grpc.health.v1alpha.Health/Check", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// Server API for Health service
type HealthServer interface {
Check(context.Context, *HealthCheckRequest) (*HealthCheckResponse, error)
}
func RegisterHealthServer(s *grpc.Server, srv HealthServer) {
s.RegisterService(&_Health_serviceDesc, srv)
}
func _Health_Check_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error) (interface{}, error) {
in := new(HealthCheckRequest)
if err := dec(in); err != nil {
return nil, err
}
out, err := srv.(HealthServer).Check(ctx, in)
if err != nil {
return nil, err
}
return out, nil
}
var _Health_serviceDesc = grpc.ServiceDesc{
ServiceName: "grpc.health.v1alpha.Health",
HandlerType: (*HealthServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Check",
Handler: _Health_Check_Handler,
},
},
Streams: []grpc.StreamDesc{},
}
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/health/grpc_health_v1alpha/health.proto 0000664 0000000 0000000 00000000501 12603607541 0027176 0 ustar 00root root 0000000 0000000 syntax = "proto3";
package grpc.health.v1alpha;
message HealthCheckRequest {
string service = 1;
}
message HealthCheckResponse {
enum ServingStatus {
UNKNOWN = 0;
SERVING = 1;
NOT_SERVING = 2;
}
ServingStatus status = 1;
}
service Health{
rpc Check(HealthCheckRequest) returns (HealthCheckResponse);
}
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/health/health.go 0000664 0000000 0000000 00000002764 12603607541 0022561 0 ustar 00root root 0000000 0000000 // Package health provides some utility functions to health-check a server. The implementation
// is based on protobuf. Users need to write their own implementations if other IDLs are used.
package health
import (
"sync"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
healthpb "google.golang.org/grpc/health/grpc_health_v1alpha"
)
type HealthServer struct {
mu sync.Mutex
// statusMap stores the serving status of the services this HealthServer monitors.
statusMap map[string]healthpb.HealthCheckResponse_ServingStatus
}
func NewHealthServer() *HealthServer {
return &HealthServer{
statusMap: make(map[string]healthpb.HealthCheckResponse_ServingStatus),
}
}
func (s *HealthServer) Check(ctx context.Context, in *healthpb.HealthCheckRequest) (*healthpb.HealthCheckResponse, error) {
s.mu.Lock()
defer s.mu.Unlock()
if in.Service == "" {
// check the server overall health status.
return &healthpb.HealthCheckResponse{
Status: healthpb.HealthCheckResponse_SERVING,
}, nil
}
if status, ok := s.statusMap[in.Service]; ok {
return &healthpb.HealthCheckResponse{
Status: status,
}, nil
}
return nil, grpc.Errorf(codes.NotFound, "unknown service")
}
// SetServingStatus is called when need to reset the serving status of a service
// or insert a new service entry into the statusMap.
func (s *HealthServer) SetServingStatus(service string, status healthpb.HealthCheckResponse_ServingStatus) {
s.mu.Lock()
s.statusMap[service] = status
s.mu.Unlock()
}
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/interop/ 0000775 0000000 0000000 00000000000 12603607541 0021167 5 ustar 00root root 0000000 0000000 grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/interop/client/ 0000775 0000000 0000000 00000000000 12603607541 0022445 5 ustar 00root root 0000000 0000000 grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/interop/client/client.go 0000664 0000000 0000000 00000047472 12603607541 0024270 0 ustar 00root root 0000000 0000000 /*
*
* Copyright 2014, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
package main
import (
"flag"
"io"
"io/ioutil"
"net"
"strconv"
"strings"
"time"
"github.com/golang/protobuf/proto"
"golang.org/x/net/context"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/oauth"
"google.golang.org/grpc/grpclog"
testpb "google.golang.org/grpc/interop/grpc_testing"
"google.golang.org/grpc/metadata"
)
var (
useTLS = flag.Bool("use_tls", false, "Connection uses TLS if true, else plain TCP")
caFile = flag.String("tls_ca_file", "testdata/ca.pem", "The file containning the CA root cert file")
serviceAccountKeyFile = flag.String("service_account_key_file", "", "Path to service account json key file")
oauthScope = flag.String("oauth_scope", "", "The scope for OAuth2 tokens")
defaultServiceAccount = flag.String("default_service_account", "", "Email of GCE default service account")
serverHost = flag.String("server_host", "127.0.0.1", "The server host name")
serverPort = flag.Int("server_port", 10000, "The server port number")
tlsServerName = flag.String("server_host_override", "x.test.youtube.com", "The server name use to verify the hostname returned by TLS handshake if it is not empty. Otherwise, --server_host is used.")
testCase = flag.String("test_case", "large_unary",
`Configure different test cases. Valid options are:
empty_unary : empty (zero bytes) request and response;
large_unary : single request and (large) response;
client_streaming : request streaming with single response;
server_streaming : single request with response streaming;
ping_pong : full-duplex streaming;
empty_stream : full-duplex streaming with zero message;
timeout_on_sleeping_server: fullduplex streaming;
compute_engine_creds: large_unary with compute engine auth;
service_account_creds: large_unary with service account auth;
jwt_token_creds: large_unary with jwt token auth;
per_rpc_creds: large_unary with per rpc token;
oauth2_auth_token: large_unary with oauth2 token auth;
cancel_after_begin: cancellation after metadata has been sent but before payloads are sent;
cancel_after_first_response: cancellation after receiving 1st message from the server.`)
)
var (
reqSizes = []int{27182, 8, 1828, 45904}
respSizes = []int{31415, 9, 2653, 58979}
largeReqSize = 271828
largeRespSize = 314159
)
func newPayload(t testpb.PayloadType, size int) *testpb.Payload {
if size < 0 {
grpclog.Fatalf("Requested a response with invalid length %d", size)
}
body := make([]byte, size)
switch t {
case testpb.PayloadType_COMPRESSABLE:
case testpb.PayloadType_UNCOMPRESSABLE:
grpclog.Fatalf("PayloadType UNCOMPRESSABLE is not supported")
default:
grpclog.Fatalf("Unsupported payload type: %d", t)
}
return &testpb.Payload{
Type: t.Enum(),
Body: body,
}
}
func doEmptyUnaryCall(tc testpb.TestServiceClient) {
reply, err := tc.EmptyCall(context.Background(), &testpb.Empty{})
if err != nil {
grpclog.Fatal("/TestService/EmptyCall RPC failed: ", err)
}
if !proto.Equal(&testpb.Empty{}, reply) {
grpclog.Fatalf("/TestService/EmptyCall receives %v, want %v", reply, testpb.Empty{})
}
grpclog.Println("EmptyUnaryCall done")
}
func doLargeUnaryCall(tc testpb.TestServiceClient) {
pl := newPayload(testpb.PayloadType_COMPRESSABLE, largeReqSize)
req := &testpb.SimpleRequest{
ResponseType: testpb.PayloadType_COMPRESSABLE.Enum(),
ResponseSize: proto.Int32(int32(largeRespSize)),
Payload: pl,
}
reply, err := tc.UnaryCall(context.Background(), req)
if err != nil {
grpclog.Fatal("/TestService/UnaryCall RPC failed: ", err)
}
t := reply.GetPayload().GetType()
s := len(reply.GetPayload().GetBody())
if t != testpb.PayloadType_COMPRESSABLE || s != largeRespSize {
grpclog.Fatalf("Got the reply with type %d len %d; want %d, %d", t, s, testpb.PayloadType_COMPRESSABLE, largeRespSize)
}
grpclog.Println("LargeUnaryCall done")
}
func doClientStreaming(tc testpb.TestServiceClient) {
stream, err := tc.StreamingInputCall(context.Background())
if err != nil {
grpclog.Fatalf("%v.StreamingInputCall(_) = _, %v", tc, err)
}
var sum int
for _, s := range reqSizes {
pl := newPayload(testpb.PayloadType_COMPRESSABLE, s)
req := &testpb.StreamingInputCallRequest{
Payload: pl,
}
if err := stream.Send(req); err != nil {
grpclog.Fatalf("%v.Send(%v) = %v", stream, req, err)
}
sum += s
grpclog.Printf("Sent a request of size %d, aggregated size %d", s, sum)
}
reply, err := stream.CloseAndRecv()
if err != nil {
grpclog.Fatalf("%v.CloseAndRecv() got error %v, want %v", stream, err, nil)
}
if reply.GetAggregatedPayloadSize() != int32(sum) {
grpclog.Fatalf("%v.CloseAndRecv().GetAggregatePayloadSize() = %v; want %v", stream, reply.GetAggregatedPayloadSize(), sum)
}
grpclog.Println("ClientStreaming done")
}
func doServerStreaming(tc testpb.TestServiceClient) {
respParam := make([]*testpb.ResponseParameters, len(respSizes))
for i, s := range respSizes {
respParam[i] = &testpb.ResponseParameters{
Size: proto.Int32(int32(s)),
}
}
req := &testpb.StreamingOutputCallRequest{
ResponseType: testpb.PayloadType_COMPRESSABLE.Enum(),
ResponseParameters: respParam,
}
stream, err := tc.StreamingOutputCall(context.Background(), req)
if err != nil {
grpclog.Fatalf("%v.StreamingOutputCall(_) = _, %v", tc, err)
}
var rpcStatus error
var respCnt int
var index int
for {
reply, err := stream.Recv()
if err != nil {
rpcStatus = err
break
}
t := reply.GetPayload().GetType()
if t != testpb.PayloadType_COMPRESSABLE {
grpclog.Fatalf("Got the reply of type %d, want %d", t, testpb.PayloadType_COMPRESSABLE)
}
size := len(reply.GetPayload().GetBody())
if size != int(respSizes[index]) {
grpclog.Fatalf("Got reply body of length %d, want %d", size, respSizes[index])
}
index++
respCnt++
}
if rpcStatus != io.EOF {
grpclog.Fatalf("Failed to finish the server streaming rpc: %v", err)
}
if respCnt != len(respSizes) {
grpclog.Fatalf("Got %d reply, want %d", len(respSizes), respCnt)
}
grpclog.Println("ServerStreaming done")
}
func doPingPong(tc testpb.TestServiceClient) {
stream, err := tc.FullDuplexCall(context.Background())
if err != nil {
grpclog.Fatalf("%v.FullDuplexCall(_) = _, %v", tc, err)
}
var index int
for index < len(reqSizes) {
respParam := []*testpb.ResponseParameters{
{
Size: proto.Int32(int32(respSizes[index])),
},
}
pl := newPayload(testpb.PayloadType_COMPRESSABLE, reqSizes[index])
req := &testpb.StreamingOutputCallRequest{
ResponseType: testpb.PayloadType_COMPRESSABLE.Enum(),
ResponseParameters: respParam,
Payload: pl,
}
if err := stream.Send(req); err != nil {
grpclog.Fatalf("%v.Send(%v) = %v", stream, req, err)
}
reply, err := stream.Recv()
if err != nil {
grpclog.Fatalf("%v.Recv() = %v", stream, err)
}
t := reply.GetPayload().GetType()
if t != testpb.PayloadType_COMPRESSABLE {
grpclog.Fatalf("Got the reply of type %d, want %d", t, testpb.PayloadType_COMPRESSABLE)
}
size := len(reply.GetPayload().GetBody())
if size != int(respSizes[index]) {
grpclog.Fatalf("Got reply body of length %d, want %d", size, respSizes[index])
}
index++
}
if err := stream.CloseSend(); err != nil {
grpclog.Fatalf("%v.CloseSend() got %v, want %v", stream, err, nil)
}
if _, err := stream.Recv(); err != io.EOF {
grpclog.Fatalf("%v failed to complele the ping pong test: %v", stream, err)
}
grpclog.Println("Pingpong done")
}
func doEmptyStream(tc testpb.TestServiceClient) {
stream, err := tc.FullDuplexCall(context.Background())
if err != nil {
grpclog.Fatalf("%v.FullDuplexCall(_) = _, %v", tc, err)
}
if err := stream.CloseSend(); err != nil {
grpclog.Fatalf("%v.CloseSend() got %v, want %v", stream, err, nil)
}
if _, err := stream.Recv(); err != io.EOF {
grpclog.Fatalf("%v failed to complete the empty stream test: %v", stream, err)
}
grpclog.Println("Emptystream done")
}
func doTimeoutOnSleepingServer(tc testpb.TestServiceClient) {
ctx, _ := context.WithTimeout(context.Background(), 1*time.Millisecond)
stream, err := tc.FullDuplexCall(ctx)
if err != nil {
if grpc.Code(err) == codes.DeadlineExceeded {
grpclog.Println("TimeoutOnSleepingServer done")
return
}
grpclog.Fatalf("%v.FullDuplexCall(_) = _, %v", tc, err)
}
pl := newPayload(testpb.PayloadType_COMPRESSABLE, 27182)
req := &testpb.StreamingOutputCallRequest{
ResponseType: testpb.PayloadType_COMPRESSABLE.Enum(),
Payload: pl,
}
if err := stream.Send(req); err != nil {
grpclog.Fatalf("%v.Send(%v) = %v", stream, req, err)
}
if _, err := stream.Recv(); grpc.Code(err) != codes.DeadlineExceeded {
grpclog.Fatalf("%v.Recv() = _, %v, want error code %d", stream, err, codes.DeadlineExceeded)
}
grpclog.Println("TimeoutOnSleepingServer done")
}
func doComputeEngineCreds(tc testpb.TestServiceClient) {
pl := newPayload(testpb.PayloadType_COMPRESSABLE, largeReqSize)
req := &testpb.SimpleRequest{
ResponseType: testpb.PayloadType_COMPRESSABLE.Enum(),
ResponseSize: proto.Int32(int32(largeRespSize)),
Payload: pl,
FillUsername: proto.Bool(true),
FillOauthScope: proto.Bool(true),
}
reply, err := tc.UnaryCall(context.Background(), req)
if err != nil {
grpclog.Fatal("/TestService/UnaryCall RPC failed: ", err)
}
user := reply.GetUsername()
scope := reply.GetOauthScope()
if user != *defaultServiceAccount {
grpclog.Fatalf("Got user name %q, want %q.", user, *defaultServiceAccount)
}
if !strings.Contains(*oauthScope, scope) {
grpclog.Fatalf("Got OAuth scope %q which is NOT a substring of %q.", scope, *oauthScope)
}
grpclog.Println("ComputeEngineCreds done")
}
func getServiceAccountJSONKey() []byte {
jsonKey, err := ioutil.ReadFile(*serviceAccountKeyFile)
if err != nil {
grpclog.Fatalf("Failed to read the service account key file: %v", err)
}
return jsonKey
}
func doServiceAccountCreds(tc testpb.TestServiceClient) {
pl := newPayload(testpb.PayloadType_COMPRESSABLE, largeReqSize)
req := &testpb.SimpleRequest{
ResponseType: testpb.PayloadType_COMPRESSABLE.Enum(),
ResponseSize: proto.Int32(int32(largeRespSize)),
Payload: pl,
FillUsername: proto.Bool(true),
FillOauthScope: proto.Bool(true),
}
reply, err := tc.UnaryCall(context.Background(), req)
if err != nil {
grpclog.Fatal("/TestService/UnaryCall RPC failed: ", err)
}
jsonKey := getServiceAccountJSONKey()
user := reply.GetUsername()
scope := reply.GetOauthScope()
if !strings.Contains(string(jsonKey), user) {
grpclog.Fatalf("Got user name %q which is NOT a substring of %q.", user, jsonKey)
}
if !strings.Contains(*oauthScope, scope) {
grpclog.Fatalf("Got OAuth scope %q which is NOT a substring of %q.", scope, *oauthScope)
}
grpclog.Println("ServiceAccountCreds done")
}
func doJWTTokenCreds(tc testpb.TestServiceClient) {
pl := newPayload(testpb.PayloadType_COMPRESSABLE, largeReqSize)
req := &testpb.SimpleRequest{
ResponseType: testpb.PayloadType_COMPRESSABLE.Enum(),
ResponseSize: proto.Int32(int32(largeRespSize)),
Payload: pl,
FillUsername: proto.Bool(true),
}
reply, err := tc.UnaryCall(context.Background(), req)
if err != nil {
grpclog.Fatal("/TestService/UnaryCall RPC failed: ", err)
}
jsonKey := getServiceAccountJSONKey()
user := reply.GetUsername()
if !strings.Contains(string(jsonKey), user) {
grpclog.Fatalf("Got user name %q which is NOT a substring of %q.", user, jsonKey)
}
grpclog.Println("JWTtokenCreds done")
}
func getToken() *oauth2.Token {
jsonKey := getServiceAccountJSONKey()
config, err := google.JWTConfigFromJSON(jsonKey, *oauthScope)
if err != nil {
grpclog.Fatalf("Failed to get the config: %v", err)
}
token, err := config.TokenSource(context.Background()).Token()
if err != nil {
grpclog.Fatalf("Failed to get the token: %v", err)
}
return token
}
func doOauth2TokenCreds(tc testpb.TestServiceClient) {
pl := newPayload(testpb.PayloadType_COMPRESSABLE, largeReqSize)
req := &testpb.SimpleRequest{
ResponseType: testpb.PayloadType_COMPRESSABLE.Enum(),
ResponseSize: proto.Int32(int32(largeRespSize)),
Payload: pl,
FillUsername: proto.Bool(true),
FillOauthScope: proto.Bool(true),
}
reply, err := tc.UnaryCall(context.Background(), req)
if err != nil {
grpclog.Fatal("/TestService/UnaryCall RPC failed: ", err)
}
jsonKey := getServiceAccountJSONKey()
user := reply.GetUsername()
scope := reply.GetOauthScope()
if !strings.Contains(string(jsonKey), user) {
grpclog.Fatalf("Got user name %q which is NOT a substring of %q.", user, jsonKey)
}
if !strings.Contains(*oauthScope, scope) {
grpclog.Fatalf("Got OAuth scope %q which is NOT a substring of %q.", scope, *oauthScope)
}
grpclog.Println("Oauth2TokenCreds done")
}
func doPerRPCCreds(tc testpb.TestServiceClient) {
jsonKey := getServiceAccountJSONKey()
pl := newPayload(testpb.PayloadType_COMPRESSABLE, largeReqSize)
req := &testpb.SimpleRequest{
ResponseType: testpb.PayloadType_COMPRESSABLE.Enum(),
ResponseSize: proto.Int32(int32(largeRespSize)),
Payload: pl,
FillUsername: proto.Bool(true),
FillOauthScope: proto.Bool(true),
}
token := getToken()
kv := map[string]string{"authorization": token.TokenType + " " + token.AccessToken}
ctx := metadata.NewContext(context.Background(), metadata.MD{"authorization": []string{kv["authorization"]}})
reply, err := tc.UnaryCall(ctx, req)
if err != nil {
grpclog.Fatal("/TestService/UnaryCall RPC failed: ", err)
}
user := reply.GetUsername()
scope := reply.GetOauthScope()
if !strings.Contains(string(jsonKey), user) {
grpclog.Fatalf("Got user name %q which is NOT a substring of %q.", user, jsonKey)
}
if !strings.Contains(*oauthScope, scope) {
grpclog.Fatalf("Got OAuth scope %q which is NOT a substring of %q.", scope, *oauthScope)
}
grpclog.Println("PerRPCCreds done")
}
var (
testMetadata = metadata.MD{
"key1": []string{"value1"},
"key2": []string{"value2"},
}
)
func doCancelAfterBegin(tc testpb.TestServiceClient) {
ctx, cancel := context.WithCancel(metadata.NewContext(context.Background(), testMetadata))
stream, err := tc.StreamingInputCall(ctx)
if err != nil {
grpclog.Fatalf("%v.StreamingInputCall(_) = _, %v", tc, err)
}
cancel()
_, err = stream.CloseAndRecv()
if grpc.Code(err) != codes.Canceled {
grpclog.Fatalf("%v.CloseAndRecv() got error code %d, want %d", stream, grpc.Code(err), codes.Canceled)
}
grpclog.Println("CancelAfterBegin done")
}
func doCancelAfterFirstResponse(tc testpb.TestServiceClient) {
ctx, cancel := context.WithCancel(context.Background())
stream, err := tc.FullDuplexCall(ctx)
if err != nil {
grpclog.Fatalf("%v.FullDuplexCall(_) = _, %v", tc, err)
}
respParam := []*testpb.ResponseParameters{
{
Size: proto.Int32(31415),
},
}
pl := newPayload(testpb.PayloadType_COMPRESSABLE, 27182)
req := &testpb.StreamingOutputCallRequest{
ResponseType: testpb.PayloadType_COMPRESSABLE.Enum(),
ResponseParameters: respParam,
Payload: pl,
}
if err := stream.Send(req); err != nil {
grpclog.Fatalf("%v.Send(%v) = %v", stream, req, err)
}
if _, err := stream.Recv(); err != nil {
grpclog.Fatalf("%v.Recv() = %v", stream, err)
}
cancel()
if _, err := stream.Recv(); grpc.Code(err) != codes.Canceled {
grpclog.Fatalf("%v compleled with error code %d, want %d", stream, grpc.Code(err), codes.Canceled)
}
grpclog.Println("CancelAfterFirstResponse done")
}
func main() {
flag.Parse()
serverAddr := net.JoinHostPort(*serverHost, strconv.Itoa(*serverPort))
var opts []grpc.DialOption
if *useTLS {
var sn string
if *tlsServerName != "" {
sn = *tlsServerName
}
var creds credentials.TransportAuthenticator
if *caFile != "" {
var err error
creds, err = credentials.NewClientTLSFromFile(*caFile, sn)
if err != nil {
grpclog.Fatalf("Failed to create TLS credentials %v", err)
}
} else {
creds = credentials.NewClientTLSFromCert(nil, sn)
}
opts = append(opts, grpc.WithTransportCredentials(creds))
if *testCase == "compute_engine_creds" {
opts = append(opts, grpc.WithPerRPCCredentials(oauth.NewComputeEngine()))
} else if *testCase == "service_account_creds" {
jwtCreds, err := oauth.NewServiceAccountFromFile(*serviceAccountKeyFile, *oauthScope)
if err != nil {
grpclog.Fatalf("Failed to create JWT credentials: %v", err)
}
opts = append(opts, grpc.WithPerRPCCredentials(jwtCreds))
} else if *testCase == "jwt_token_creds" {
jwtCreds, err := oauth.NewJWTAccessFromFile(*serviceAccountKeyFile)
if err != nil {
grpclog.Fatalf("Failed to create JWT credentials: %v", err)
}
opts = append(opts, grpc.WithPerRPCCredentials(jwtCreds))
} else if *testCase == "oauth2_auth_token" {
opts = append(opts, grpc.WithPerRPCCredentials(oauth.NewOauthAccess(getToken())))
}
} else {
opts = append(opts, grpc.WithInsecure())
}
conn, err := grpc.Dial(serverAddr, opts...)
if err != nil {
grpclog.Fatalf("Fail to dial: %v", err)
}
defer conn.Close()
tc := testpb.NewTestServiceClient(conn)
switch *testCase {
case "empty_unary":
doEmptyUnaryCall(tc)
case "large_unary":
doLargeUnaryCall(tc)
case "client_streaming":
doClientStreaming(tc)
case "server_streaming":
doServerStreaming(tc)
case "ping_pong":
doPingPong(tc)
case "empty_stream":
doEmptyStream(tc)
case "timeout_on_sleeping_server":
doTimeoutOnSleepingServer(tc)
case "compute_engine_creds":
if !*useTLS {
grpclog.Fatalf("TLS is not enabled. TLS is required to execute compute_engine_creds test case.")
}
doComputeEngineCreds(tc)
case "service_account_creds":
if !*useTLS {
grpclog.Fatalf("TLS is not enabled. TLS is required to execute service_account_creds test case.")
}
doServiceAccountCreds(tc)
case "jwt_token_creds":
if !*useTLS {
grpclog.Fatalf("TLS is not enabled. TLS is required to execute jwt_token_creds test case.")
}
doJWTTokenCreds(tc)
case "per_rpc_creds":
if !*useTLS {
grpclog.Fatalf("TLS is not enabled. TLS is required to execute per_rpc_creds test case.")
}
doPerRPCCreds(tc)
case "oauth2_auth_token":
if !*useTLS {
grpclog.Fatalf("TLS is not enabled. TLS is required to execute oauth2_auth_token test case.")
}
doOauth2TokenCreds(tc)
case "cancel_after_begin":
doCancelAfterBegin(tc)
case "cancel_after_first_response":
doCancelAfterFirstResponse(tc)
default:
grpclog.Fatal("Unsupported test case: ", *testCase)
}
}
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/interop/client/testdata/ 0000775 0000000 0000000 00000000000 12603607541 0024256 5 ustar 00root root 0000000 0000000 grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/interop/client/testdata/ca.pem 0000664 0000000 0000000 00000001442 12603607541 0025345 0 ustar 00root root 0000000 0000000 -----BEGIN CERTIFICATE-----
MIICIzCCAYwCCQCFTbF7XNSvvjANBgkqhkiG9w0BAQUFADBWMQswCQYDVQQGEwJB
VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0
cyBQdHkgTHRkMQ8wDQYDVQQDDAZ0ZXN0Y2EwHhcNMTQwNzE3MjMxNzUxWhcNMjQw
NzE0MjMxNzUxWjBWMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEh
MB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMQ8wDQYDVQQDDAZ0ZXN0
Y2EwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMBA3wVeTGHZR1Rye/i+J8a2
cu5gXwFV6TnObzGM7bLFCO5i9v4mLo4iFzPsHmWDUxKS3Y8iXbu0eYBlLoNY0lSv
xDx33O+DuwMmVN+DzSD+Eod9zfvwOWHsazYCZT2PhNxnVWIuJXViY4JAHUGodjx+
QAi6yCAurUZGvYXGgZSBAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAQoQVD8bwdtWJ
AniGBwcCfqYyH+/KpA10AcebJVVTyYbYvI9Q8d6RSVu4PZy9OALHR/QrWBdYTAyz
fNAmc2cmdkSRJzjhIaOstnQy1J+Fk0T9XyvQtq499yFbq9xogUVlEGH62xP6vH0Y
5ukK//dCPAzA11YuX2rnex0JhuTQfcI=
-----END CERTIFICATE-----
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/interop/client/testdata/server1.key 0000664 0000000 0000000 00000001567 12603607541 0026370 0 ustar 00root root 0000000 0000000 -----BEGIN RSA PRIVATE KEY-----
MIICWwIBAAKBgQDhwxUnKCwlSaWAwzOB2LSHVegJHv7DDWminTg4wzLLsf+LQ8nZ
bpjfn5vgIzxCuRh4Rp9QYM5FhfrJX9wcYawP/HTbJ7p7LVQO2QYAP+akMTHxgKuM
BzVV++3wWToKfVZUjFX8nfTfGMGwWAHJDnlEGnU4tl9UujoCV4ENJtzFoQIDAQAB
AoGAJ+6hpzNr24yTQZtFWQpDpEyFplddKJMOxDya3S9ppK3vTWrIITV2xNcucw7I
ceTbdyrGsyjsU0/HdCcIf9ym2jfmGLUwmyhltKVw0QYcFB0XLkc0nI5YvEYoeVDg
omZIXn1E3EW+sSIWSbkMu9bY2kstKXR2UZmMgWDtmBEPMaECQQD6yT4TAZM5hGBb
ciBKgMUP6PwOhPhOMPIvijO50Aiu6iuCV88l1QIy38gWVhxjNrq6P346j4IBg+kB
9alwpCODAkEA5nSnm9k6ykYeQWNS0fNWiRinCdl23A7usDGSuKKlm019iomJ/Rgd
MKDOp0q/2OostbteOWM2MRFf4jMH3wyVCwJAfAdjJ8szoNKTRSagSbh9vWygnB2v
IByc6l4TTuZQJRGzCveafz9lovuB3WohCABdQRd9ukCXL2CpsEpqzkafOQJAJUjc
USedDlq3zGZwYM1Yw8d8RuirBUFZNqJelYai+nRYClDkRVFgb5yksoYycbq5TxGo
VeqKOvgPpj4RWPHlLwJAGUMk3bqT91xBUCnLRs/vfoCpHpg6eywQTBDAV6xkyz4a
RH3I7/+yj3ZxR2JoWHgUwZ7lZk1VnhffFye7SBXyag==
-----END RSA PRIVATE KEY-----
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/interop/client/testdata/server1.pem 0000664 0000000 0000000 00000001704 12603607541 0026352 0 ustar 00root root 0000000 0000000 -----BEGIN CERTIFICATE-----
MIICmzCCAgSgAwIBAgIBAzANBgkqhkiG9w0BAQUFADBWMQswCQYDVQQGEwJBVTET
MBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQ
dHkgTHRkMQ8wDQYDVQQDDAZ0ZXN0Y2EwHhcNMTQwNzIyMDYwMDU3WhcNMjQwNzE5
MDYwMDU3WjBkMQswCQYDVQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNV
BAcTB0NoaWNhZ28xFDASBgNVBAoTC0dvb2dsZSBJbmMuMRowGAYDVQQDFBEqLnRl
c3QuZ29vZ2xlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA4cMVJygs
JUmlgMMzgdi0h1XoCR7+ww1pop04OMMyy7H/i0PJ2W6Y35+b4CM8QrkYeEafUGDO
RYX6yV/cHGGsD/x02ye6ey1UDtkGAD/mpDEx8YCrjAc1Vfvt8Fk6Cn1WVIxV/J30
3xjBsFgByQ55RBp1OLZfVLo6AleBDSbcxaECAwEAAaNrMGkwCQYDVR0TBAIwADAL
BgNVHQ8EBAMCBeAwTwYDVR0RBEgwRoIQKi50ZXN0Lmdvb2dsZS5mcoIYd2F0ZXJ6
b29pLnRlc3QuZ29vZ2xlLmJlghIqLnRlc3QueW91dHViZS5jb22HBMCoAQMwDQYJ
KoZIhvcNAQEFBQADgYEAM2Ii0LgTGbJ1j4oqX9bxVcxm+/R5Yf8oi0aZqTJlnLYS
wXcBykxTx181s7WyfJ49WwrYXo78zTDAnf1ma0fPq3e4mpspvyndLh1a+OarHa1e
aT0DIIYk7qeEa1YcVljx2KyLd0r1BBAfrwyGaEPVeJQVYWaOJRU2we/KD4ojf9s=
-----END CERTIFICATE-----
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/interop/grpc_testing/ 0000775 0000000 0000000 00000000000 12603607541 0023657 5 ustar 00root root 0000000 0000000 grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/interop/grpc_testing/test.pb.go 0000775 0000000 0000000 00000053553 12603607541 0025603 0 ustar 00root root 0000000 0000000 // Code generated by protoc-gen-go.
// source: test.proto
// DO NOT EDIT!
/*
Package grpc_testing is a generated protocol buffer package.
It is generated from these files:
test.proto
It has these top-level messages:
Empty
Payload
SimpleRequest
SimpleResponse
StreamingInputCallRequest
StreamingInputCallResponse
ResponseParameters
StreamingOutputCallRequest
StreamingOutputCallResponse
*/
package grpc_testing
import proto "github.com/golang/protobuf/proto"
import math "math"
import (
context "golang.org/x/net/context"
grpc "google.golang.org/grpc"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConn
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = math.Inf
// The type of payload that should be returned.
type PayloadType int32
const (
// Compressable text format.
PayloadType_COMPRESSABLE PayloadType = 0
// Uncompressable binary format.
PayloadType_UNCOMPRESSABLE PayloadType = 1
// Randomly chosen from all other formats defined in this enum.
PayloadType_RANDOM PayloadType = 2
)
var PayloadType_name = map[int32]string{
0: "COMPRESSABLE",
1: "UNCOMPRESSABLE",
2: "RANDOM",
}
var PayloadType_value = map[string]int32{
"COMPRESSABLE": 0,
"UNCOMPRESSABLE": 1,
"RANDOM": 2,
}
func (x PayloadType) Enum() *PayloadType {
p := new(PayloadType)
*p = x
return p
}
func (x PayloadType) String() string {
return proto.EnumName(PayloadType_name, int32(x))
}
func (x *PayloadType) UnmarshalJSON(data []byte) error {
value, err := proto.UnmarshalJSONEnum(PayloadType_value, data, "PayloadType")
if err != nil {
return err
}
*x = PayloadType(value)
return nil
}
type Empty struct {
XXX_unrecognized []byte `json:"-"`
}
func (m *Empty) Reset() { *m = Empty{} }
func (m *Empty) String() string { return proto.CompactTextString(m) }
func (*Empty) ProtoMessage() {}
// A block of data, to simply increase gRPC message size.
type Payload struct {
// The type of data in body.
Type *PayloadType `protobuf:"varint,1,opt,name=type,enum=grpc.testing.PayloadType" json:"type,omitempty"`
// Primary contents of payload.
Body []byte `protobuf:"bytes,2,opt,name=body" json:"body,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *Payload) Reset() { *m = Payload{} }
func (m *Payload) String() string { return proto.CompactTextString(m) }
func (*Payload) ProtoMessage() {}
func (m *Payload) GetType() PayloadType {
if m != nil && m.Type != nil {
return *m.Type
}
return PayloadType_COMPRESSABLE
}
func (m *Payload) GetBody() []byte {
if m != nil {
return m.Body
}
return nil
}
// Unary request.
type SimpleRequest struct {
// Desired payload type in the response from the server.
// If response_type is RANDOM, server randomly chooses one from other formats.
ResponseType *PayloadType `protobuf:"varint,1,opt,name=response_type,enum=grpc.testing.PayloadType" json:"response_type,omitempty"`
// Desired payload size in the response from the server.
// If response_type is COMPRESSABLE, this denotes the size before compression.
ResponseSize *int32 `protobuf:"varint,2,opt,name=response_size" json:"response_size,omitempty"`
// Optional input payload sent along with the request.
Payload *Payload `protobuf:"bytes,3,opt,name=payload" json:"payload,omitempty"`
// Whether SimpleResponse should include username.
FillUsername *bool `protobuf:"varint,4,opt,name=fill_username" json:"fill_username,omitempty"`
// Whether SimpleResponse should include OAuth scope.
FillOauthScope *bool `protobuf:"varint,5,opt,name=fill_oauth_scope" json:"fill_oauth_scope,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *SimpleRequest) Reset() { *m = SimpleRequest{} }
func (m *SimpleRequest) String() string { return proto.CompactTextString(m) }
func (*SimpleRequest) ProtoMessage() {}
func (m *SimpleRequest) GetResponseType() PayloadType {
if m != nil && m.ResponseType != nil {
return *m.ResponseType
}
return PayloadType_COMPRESSABLE
}
func (m *SimpleRequest) GetResponseSize() int32 {
if m != nil && m.ResponseSize != nil {
return *m.ResponseSize
}
return 0
}
func (m *SimpleRequest) GetPayload() *Payload {
if m != nil {
return m.Payload
}
return nil
}
func (m *SimpleRequest) GetFillUsername() bool {
if m != nil && m.FillUsername != nil {
return *m.FillUsername
}
return false
}
func (m *SimpleRequest) GetFillOauthScope() bool {
if m != nil && m.FillOauthScope != nil {
return *m.FillOauthScope
}
return false
}
// Unary response, as configured by the request.
type SimpleResponse struct {
// Payload to increase message size.
Payload *Payload `protobuf:"bytes,1,opt,name=payload" json:"payload,omitempty"`
// The user the request came from, for verifying authentication was
// successful when the client expected it.
Username *string `protobuf:"bytes,2,opt,name=username" json:"username,omitempty"`
// OAuth scope.
OauthScope *string `protobuf:"bytes,3,opt,name=oauth_scope" json:"oauth_scope,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *SimpleResponse) Reset() { *m = SimpleResponse{} }
func (m *SimpleResponse) String() string { return proto.CompactTextString(m) }
func (*SimpleResponse) ProtoMessage() {}
func (m *SimpleResponse) GetPayload() *Payload {
if m != nil {
return m.Payload
}
return nil
}
func (m *SimpleResponse) GetUsername() string {
if m != nil && m.Username != nil {
return *m.Username
}
return ""
}
func (m *SimpleResponse) GetOauthScope() string {
if m != nil && m.OauthScope != nil {
return *m.OauthScope
}
return ""
}
// Client-streaming request.
type StreamingInputCallRequest struct {
// Optional input payload sent along with the request.
Payload *Payload `protobuf:"bytes,1,opt,name=payload" json:"payload,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *StreamingInputCallRequest) Reset() { *m = StreamingInputCallRequest{} }
func (m *StreamingInputCallRequest) String() string { return proto.CompactTextString(m) }
func (*StreamingInputCallRequest) ProtoMessage() {}
func (m *StreamingInputCallRequest) GetPayload() *Payload {
if m != nil {
return m.Payload
}
return nil
}
// Client-streaming response.
type StreamingInputCallResponse struct {
// Aggregated size of payloads received from the client.
AggregatedPayloadSize *int32 `protobuf:"varint,1,opt,name=aggregated_payload_size" json:"aggregated_payload_size,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *StreamingInputCallResponse) Reset() { *m = StreamingInputCallResponse{} }
func (m *StreamingInputCallResponse) String() string { return proto.CompactTextString(m) }
func (*StreamingInputCallResponse) ProtoMessage() {}
func (m *StreamingInputCallResponse) GetAggregatedPayloadSize() int32 {
if m != nil && m.AggregatedPayloadSize != nil {
return *m.AggregatedPayloadSize
}
return 0
}
// Configuration for a particular response.
type ResponseParameters struct {
// Desired payload sizes in responses from the server.
// If response_type is COMPRESSABLE, this denotes the size before compression.
Size *int32 `protobuf:"varint,1,opt,name=size" json:"size,omitempty"`
// Desired interval between consecutive responses in the response stream in
// microseconds.
IntervalUs *int32 `protobuf:"varint,2,opt,name=interval_us" json:"interval_us,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *ResponseParameters) Reset() { *m = ResponseParameters{} }
func (m *ResponseParameters) String() string { return proto.CompactTextString(m) }
func (*ResponseParameters) ProtoMessage() {}
func (m *ResponseParameters) GetSize() int32 {
if m != nil && m.Size != nil {
return *m.Size
}
return 0
}
func (m *ResponseParameters) GetIntervalUs() int32 {
if m != nil && m.IntervalUs != nil {
return *m.IntervalUs
}
return 0
}
// Server-streaming request.
type StreamingOutputCallRequest struct {
// Desired payload type in the response from the server.
// If response_type is RANDOM, the payload from each response in the stream
// might be of different types. This is to simulate a mixed type of payload
// stream.
ResponseType *PayloadType `protobuf:"varint,1,opt,name=response_type,enum=grpc.testing.PayloadType" json:"response_type,omitempty"`
// Configuration for each expected response message.
ResponseParameters []*ResponseParameters `protobuf:"bytes,2,rep,name=response_parameters" json:"response_parameters,omitempty"`
// Optional input payload sent along with the request.
Payload *Payload `protobuf:"bytes,3,opt,name=payload" json:"payload,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *StreamingOutputCallRequest) Reset() { *m = StreamingOutputCallRequest{} }
func (m *StreamingOutputCallRequest) String() string { return proto.CompactTextString(m) }
func (*StreamingOutputCallRequest) ProtoMessage() {}
func (m *StreamingOutputCallRequest) GetResponseType() PayloadType {
if m != nil && m.ResponseType != nil {
return *m.ResponseType
}
return PayloadType_COMPRESSABLE
}
func (m *StreamingOutputCallRequest) GetResponseParameters() []*ResponseParameters {
if m != nil {
return m.ResponseParameters
}
return nil
}
func (m *StreamingOutputCallRequest) GetPayload() *Payload {
if m != nil {
return m.Payload
}
return nil
}
// Server-streaming response, as configured by the request and parameters.
type StreamingOutputCallResponse struct {
// Payload to increase response size.
Payload *Payload `protobuf:"bytes,1,opt,name=payload" json:"payload,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *StreamingOutputCallResponse) Reset() { *m = StreamingOutputCallResponse{} }
func (m *StreamingOutputCallResponse) String() string { return proto.CompactTextString(m) }
func (*StreamingOutputCallResponse) ProtoMessage() {}
func (m *StreamingOutputCallResponse) GetPayload() *Payload {
if m != nil {
return m.Payload
}
return nil
}
func init() {
proto.RegisterEnum("grpc.testing.PayloadType", PayloadType_name, PayloadType_value)
}
// Client API for TestService service
type TestServiceClient interface {
// One empty request followed by one empty response.
EmptyCall(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error)
// One request followed by one response.
// The server returns the client payload as-is.
UnaryCall(ctx context.Context, in *SimpleRequest, opts ...grpc.CallOption) (*SimpleResponse, error)
// One request followed by a sequence of responses (streamed download).
// The server returns the payload with client desired type and sizes.
StreamingOutputCall(ctx context.Context, in *StreamingOutputCallRequest, opts ...grpc.CallOption) (TestService_StreamingOutputCallClient, error)
// A sequence of requests followed by one response (streamed upload).
// The server returns the aggregated size of client payload as the result.
StreamingInputCall(ctx context.Context, opts ...grpc.CallOption) (TestService_StreamingInputCallClient, error)
// A sequence of requests with each request served by the server immediately.
// As one request could lead to multiple responses, this interface
// demonstrates the idea of full duplexing.
FullDuplexCall(ctx context.Context, opts ...grpc.CallOption) (TestService_FullDuplexCallClient, error)
// A sequence of requests followed by a sequence of responses.
// The server buffers all the client requests and then serves them in order. A
// stream of responses are returned to the client when the server starts with
// first request.
HalfDuplexCall(ctx context.Context, opts ...grpc.CallOption) (TestService_HalfDuplexCallClient, error)
}
type testServiceClient struct {
cc *grpc.ClientConn
}
func NewTestServiceClient(cc *grpc.ClientConn) TestServiceClient {
return &testServiceClient{cc}
}
func (c *testServiceClient) EmptyCall(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error) {
out := new(Empty)
err := grpc.Invoke(ctx, "/grpc.testing.TestService/EmptyCall", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *testServiceClient) UnaryCall(ctx context.Context, in *SimpleRequest, opts ...grpc.CallOption) (*SimpleResponse, error) {
out := new(SimpleResponse)
err := grpc.Invoke(ctx, "/grpc.testing.TestService/UnaryCall", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *testServiceClient) StreamingOutputCall(ctx context.Context, in *StreamingOutputCallRequest, opts ...grpc.CallOption) (TestService_StreamingOutputCallClient, error) {
stream, err := grpc.NewClientStream(ctx, &_TestService_serviceDesc.Streams[0], c.cc, "/grpc.testing.TestService/StreamingOutputCall", opts...)
if err != nil {
return nil, err
}
x := &testServiceStreamingOutputCallClient{stream}
if err := x.ClientStream.SendMsg(in); err != nil {
return nil, err
}
if err := x.ClientStream.CloseSend(); err != nil {
return nil, err
}
return x, nil
}
type TestService_StreamingOutputCallClient interface {
Recv() (*StreamingOutputCallResponse, error)
grpc.ClientStream
}
type testServiceStreamingOutputCallClient struct {
grpc.ClientStream
}
func (x *testServiceStreamingOutputCallClient) Recv() (*StreamingOutputCallResponse, error) {
m := new(StreamingOutputCallResponse)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
func (c *testServiceClient) StreamingInputCall(ctx context.Context, opts ...grpc.CallOption) (TestService_StreamingInputCallClient, error) {
stream, err := grpc.NewClientStream(ctx, &_TestService_serviceDesc.Streams[1], c.cc, "/grpc.testing.TestService/StreamingInputCall", opts...)
if err != nil {
return nil, err
}
x := &testServiceStreamingInputCallClient{stream}
return x, nil
}
type TestService_StreamingInputCallClient interface {
Send(*StreamingInputCallRequest) error
CloseAndRecv() (*StreamingInputCallResponse, error)
grpc.ClientStream
}
type testServiceStreamingInputCallClient struct {
grpc.ClientStream
}
func (x *testServiceStreamingInputCallClient) Send(m *StreamingInputCallRequest) error {
return x.ClientStream.SendMsg(m)
}
func (x *testServiceStreamingInputCallClient) CloseAndRecv() (*StreamingInputCallResponse, error) {
if err := x.ClientStream.CloseSend(); err != nil {
return nil, err
}
m := new(StreamingInputCallResponse)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
func (c *testServiceClient) FullDuplexCall(ctx context.Context, opts ...grpc.CallOption) (TestService_FullDuplexCallClient, error) {
stream, err := grpc.NewClientStream(ctx, &_TestService_serviceDesc.Streams[2], c.cc, "/grpc.testing.TestService/FullDuplexCall", opts...)
if err != nil {
return nil, err
}
x := &testServiceFullDuplexCallClient{stream}
return x, nil
}
type TestService_FullDuplexCallClient interface {
Send(*StreamingOutputCallRequest) error
Recv() (*StreamingOutputCallResponse, error)
grpc.ClientStream
}
type testServiceFullDuplexCallClient struct {
grpc.ClientStream
}
func (x *testServiceFullDuplexCallClient) Send(m *StreamingOutputCallRequest) error {
return x.ClientStream.SendMsg(m)
}
func (x *testServiceFullDuplexCallClient) Recv() (*StreamingOutputCallResponse, error) {
m := new(StreamingOutputCallResponse)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
func (c *testServiceClient) HalfDuplexCall(ctx context.Context, opts ...grpc.CallOption) (TestService_HalfDuplexCallClient, error) {
stream, err := grpc.NewClientStream(ctx, &_TestService_serviceDesc.Streams[3], c.cc, "/grpc.testing.TestService/HalfDuplexCall", opts...)
if err != nil {
return nil, err
}
x := &testServiceHalfDuplexCallClient{stream}
return x, nil
}
type TestService_HalfDuplexCallClient interface {
Send(*StreamingOutputCallRequest) error
Recv() (*StreamingOutputCallResponse, error)
grpc.ClientStream
}
type testServiceHalfDuplexCallClient struct {
grpc.ClientStream
}
func (x *testServiceHalfDuplexCallClient) Send(m *StreamingOutputCallRequest) error {
return x.ClientStream.SendMsg(m)
}
func (x *testServiceHalfDuplexCallClient) Recv() (*StreamingOutputCallResponse, error) {
m := new(StreamingOutputCallResponse)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
// Server API for TestService service
type TestServiceServer interface {
// One empty request followed by one empty response.
EmptyCall(context.Context, *Empty) (*Empty, error)
// One request followed by one response.
// The server returns the client payload as-is.
UnaryCall(context.Context, *SimpleRequest) (*SimpleResponse, error)
// One request followed by a sequence of responses (streamed download).
// The server returns the payload with client desired type and sizes.
StreamingOutputCall(*StreamingOutputCallRequest, TestService_StreamingOutputCallServer) error
// A sequence of requests followed by one response (streamed upload).
// The server returns the aggregated size of client payload as the result.
StreamingInputCall(TestService_StreamingInputCallServer) error
// A sequence of requests with each request served by the server immediately.
// As one request could lead to multiple responses, this interface
// demonstrates the idea of full duplexing.
FullDuplexCall(TestService_FullDuplexCallServer) error
// A sequence of requests followed by a sequence of responses.
// The server buffers all the client requests and then serves them in order. A
// stream of responses are returned to the client when the server starts with
// first request.
HalfDuplexCall(TestService_HalfDuplexCallServer) error
}
func RegisterTestServiceServer(s *grpc.Server, srv TestServiceServer) {
s.RegisterService(&_TestService_serviceDesc, srv)
}
func _TestService_EmptyCall_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error) (interface{}, error) {
in := new(Empty)
if err := dec(in); err != nil {
return nil, err
}
out, err := srv.(TestServiceServer).EmptyCall(ctx, in)
if err != nil {
return nil, err
}
return out, nil
}
func _TestService_UnaryCall_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error) (interface{}, error) {
in := new(SimpleRequest)
if err := dec(in); err != nil {
return nil, err
}
out, err := srv.(TestServiceServer).UnaryCall(ctx, in)
if err != nil {
return nil, err
}
return out, nil
}
func _TestService_StreamingOutputCall_Handler(srv interface{}, stream grpc.ServerStream) error {
m := new(StreamingOutputCallRequest)
if err := stream.RecvMsg(m); err != nil {
return err
}
return srv.(TestServiceServer).StreamingOutputCall(m, &testServiceStreamingOutputCallServer{stream})
}
type TestService_StreamingOutputCallServer interface {
Send(*StreamingOutputCallResponse) error
grpc.ServerStream
}
type testServiceStreamingOutputCallServer struct {
grpc.ServerStream
}
func (x *testServiceStreamingOutputCallServer) Send(m *StreamingOutputCallResponse) error {
return x.ServerStream.SendMsg(m)
}
func _TestService_StreamingInputCall_Handler(srv interface{}, stream grpc.ServerStream) error {
return srv.(TestServiceServer).StreamingInputCall(&testServiceStreamingInputCallServer{stream})
}
type TestService_StreamingInputCallServer interface {
SendAndClose(*StreamingInputCallResponse) error
Recv() (*StreamingInputCallRequest, error)
grpc.ServerStream
}
type testServiceStreamingInputCallServer struct {
grpc.ServerStream
}
func (x *testServiceStreamingInputCallServer) SendAndClose(m *StreamingInputCallResponse) error {
return x.ServerStream.SendMsg(m)
}
func (x *testServiceStreamingInputCallServer) Recv() (*StreamingInputCallRequest, error) {
m := new(StreamingInputCallRequest)
if err := x.ServerStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
func _TestService_FullDuplexCall_Handler(srv interface{}, stream grpc.ServerStream) error {
return srv.(TestServiceServer).FullDuplexCall(&testServiceFullDuplexCallServer{stream})
}
type TestService_FullDuplexCallServer interface {
Send(*StreamingOutputCallResponse) error
Recv() (*StreamingOutputCallRequest, error)
grpc.ServerStream
}
type testServiceFullDuplexCallServer struct {
grpc.ServerStream
}
func (x *testServiceFullDuplexCallServer) Send(m *StreamingOutputCallResponse) error {
return x.ServerStream.SendMsg(m)
}
func (x *testServiceFullDuplexCallServer) Recv() (*StreamingOutputCallRequest, error) {
m := new(StreamingOutputCallRequest)
if err := x.ServerStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
func _TestService_HalfDuplexCall_Handler(srv interface{}, stream grpc.ServerStream) error {
return srv.(TestServiceServer).HalfDuplexCall(&testServiceHalfDuplexCallServer{stream})
}
type TestService_HalfDuplexCallServer interface {
Send(*StreamingOutputCallResponse) error
Recv() (*StreamingOutputCallRequest, error)
grpc.ServerStream
}
type testServiceHalfDuplexCallServer struct {
grpc.ServerStream
}
func (x *testServiceHalfDuplexCallServer) Send(m *StreamingOutputCallResponse) error {
return x.ServerStream.SendMsg(m)
}
func (x *testServiceHalfDuplexCallServer) Recv() (*StreamingOutputCallRequest, error) {
m := new(StreamingOutputCallRequest)
if err := x.ServerStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
var _TestService_serviceDesc = grpc.ServiceDesc{
ServiceName: "grpc.testing.TestService",
HandlerType: (*TestServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "EmptyCall",
Handler: _TestService_EmptyCall_Handler,
},
{
MethodName: "UnaryCall",
Handler: _TestService_UnaryCall_Handler,
},
},
Streams: []grpc.StreamDesc{
{
StreamName: "StreamingOutputCall",
Handler: _TestService_StreamingOutputCall_Handler,
ServerStreams: true,
},
{
StreamName: "StreamingInputCall",
Handler: _TestService_StreamingInputCall_Handler,
ClientStreams: true,
},
{
StreamName: "FullDuplexCall",
Handler: _TestService_FullDuplexCall_Handler,
ServerStreams: true,
ClientStreams: true,
},
{
StreamName: "HalfDuplexCall",
Handler: _TestService_HalfDuplexCall_Handler,
ServerStreams: true,
ClientStreams: true,
},
},
}
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/interop/grpc_testing/test.proto 0000664 0000000 0000000 00000011155 12603607541 0025726 0 ustar 00root root 0000000 0000000 // An integration test service that covers all the method signature permutations
// of unary/streaming requests/responses.
syntax = "proto2";
package grpc.testing;
message Empty {}
// The type of payload that should be returned.
enum PayloadType {
// Compressable text format.
COMPRESSABLE = 0;
// Uncompressable binary format.
UNCOMPRESSABLE = 1;
// Randomly chosen from all other formats defined in this enum.
RANDOM = 2;
}
// A block of data, to simply increase gRPC message size.
message Payload {
// The type of data in body.
optional PayloadType type = 1;
// Primary contents of payload.
optional bytes body = 2;
}
// Unary request.
message SimpleRequest {
// Desired payload type in the response from the server.
// If response_type is RANDOM, server randomly chooses one from other formats.
optional PayloadType response_type = 1;
// Desired payload size in the response from the server.
// If response_type is COMPRESSABLE, this denotes the size before compression.
optional int32 response_size = 2;
// Optional input payload sent along with the request.
optional Payload payload = 3;
// Whether SimpleResponse should include username.
optional bool fill_username = 4;
// Whether SimpleResponse should include OAuth scope.
optional bool fill_oauth_scope = 5;
}
// Unary response, as configured by the request.
message SimpleResponse {
// Payload to increase message size.
optional Payload payload = 1;
// The user the request came from, for verifying authentication was
// successful when the client expected it.
optional string username = 2;
// OAuth scope.
optional string oauth_scope = 3;
}
// Client-streaming request.
message StreamingInputCallRequest {
// Optional input payload sent along with the request.
optional Payload payload = 1;
// Not expecting any payload from the response.
}
// Client-streaming response.
message StreamingInputCallResponse {
// Aggregated size of payloads received from the client.
optional int32 aggregated_payload_size = 1;
}
// Configuration for a particular response.
message ResponseParameters {
// Desired payload sizes in responses from the server.
// If response_type is COMPRESSABLE, this denotes the size before compression.
optional int32 size = 1;
// Desired interval between consecutive responses in the response stream in
// microseconds.
optional int32 interval_us = 2;
}
// Server-streaming request.
message StreamingOutputCallRequest {
// Desired payload type in the response from the server.
// If response_type is RANDOM, the payload from each response in the stream
// might be of different types. This is to simulate a mixed type of payload
// stream.
optional PayloadType response_type = 1;
// Configuration for each expected response message.
repeated ResponseParameters response_parameters = 2;
// Optional input payload sent along with the request.
optional Payload payload = 3;
}
// Server-streaming response, as configured by the request and parameters.
message StreamingOutputCallResponse {
// Payload to increase response size.
optional Payload payload = 1;
}
// A simple service to test the various types of RPCs and experiment with
// performance with various types of payload.
service TestService {
// One empty request followed by one empty response.
rpc EmptyCall(Empty) returns (Empty);
// One request followed by one response.
// The server returns the client payload as-is.
rpc UnaryCall(SimpleRequest) returns (SimpleResponse);
// One request followed by a sequence of responses (streamed download).
// The server returns the payload with client desired type and sizes.
rpc StreamingOutputCall(StreamingOutputCallRequest)
returns (stream StreamingOutputCallResponse);
// A sequence of requests followed by one response (streamed upload).
// The server returns the aggregated size of client payload as the result.
rpc StreamingInputCall(stream StreamingInputCallRequest)
returns (StreamingInputCallResponse);
// A sequence of requests with each request served by the server immediately.
// As one request could lead to multiple responses, this interface
// demonstrates the idea of full duplexing.
rpc FullDuplexCall(stream StreamingOutputCallRequest)
returns (stream StreamingOutputCallResponse);
// A sequence of requests followed by a sequence of responses.
// The server buffers all the client requests and then serves them in order. A
// stream of responses are returned to the client when the server starts with
// first request.
rpc HalfDuplexCall(stream StreamingOutputCallRequest)
returns (stream StreamingOutputCallResponse);
}
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/interop/server/ 0000775 0000000 0000000 00000000000 12603607541 0022475 5 ustar 00root root 0000000 0000000 grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/interop/server/server.go 0000664 0000000 0000000 00000013362 12603607541 0024337 0 ustar 00root root 0000000 0000000 /*
*
* Copyright 2014, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
package main
import (
"flag"
"fmt"
"io"
"net"
"strconv"
"time"
"github.com/golang/protobuf/proto"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/grpclog"
testpb "google.golang.org/grpc/interop/grpc_testing"
)
var (
useTLS = flag.Bool("use_tls", false, "Connection uses TLS if true, else plain TCP")
certFile = flag.String("tls_cert_file", "testdata/server1.pem", "The TLS cert file")
keyFile = flag.String("tls_key_file", "testdata/server1.key", "The TLS key file")
port = flag.Int("port", 10000, "The server port")
)
type testServer struct {
}
func (s *testServer) EmptyCall(ctx context.Context, in *testpb.Empty) (*testpb.Empty, error) {
return new(testpb.Empty), nil
}
func newPayload(t testpb.PayloadType, size int32) (*testpb.Payload, error) {
if size < 0 {
return nil, fmt.Errorf("requested a response with invalid length %d", size)
}
body := make([]byte, size)
switch t {
case testpb.PayloadType_COMPRESSABLE:
case testpb.PayloadType_UNCOMPRESSABLE:
return nil, fmt.Errorf("payloadType UNCOMPRESSABLE is not supported")
default:
return nil, fmt.Errorf("unsupported payload type: %d", t)
}
return &testpb.Payload{
Type: t.Enum(),
Body: body,
}, nil
}
func (s *testServer) UnaryCall(ctx context.Context, in *testpb.SimpleRequest) (*testpb.SimpleResponse, error) {
pl, err := newPayload(in.GetResponseType(), in.GetResponseSize())
if err != nil {
return nil, err
}
return &testpb.SimpleResponse{
Payload: pl,
}, nil
}
func (s *testServer) StreamingOutputCall(args *testpb.StreamingOutputCallRequest, stream testpb.TestService_StreamingOutputCallServer) error {
cs := args.GetResponseParameters()
for _, c := range cs {
if us := c.GetIntervalUs(); us > 0 {
time.Sleep(time.Duration(us) * time.Microsecond)
}
pl, err := newPayload(args.GetResponseType(), c.GetSize())
if err != nil {
return err
}
if err := stream.Send(&testpb.StreamingOutputCallResponse{
Payload: pl,
}); err != nil {
return err
}
}
return nil
}
func (s *testServer) StreamingInputCall(stream testpb.TestService_StreamingInputCallServer) error {
var sum int
for {
in, err := stream.Recv()
if err == io.EOF {
return stream.SendAndClose(&testpb.StreamingInputCallResponse{
AggregatedPayloadSize: proto.Int32(int32(sum)),
})
}
if err != nil {
return err
}
p := in.GetPayload().GetBody()
sum += len(p)
}
}
func (s *testServer) FullDuplexCall(stream testpb.TestService_FullDuplexCallServer) error {
for {
in, err := stream.Recv()
if err == io.EOF {
// read done.
return nil
}
if err != nil {
return err
}
cs := in.GetResponseParameters()
for _, c := range cs {
if us := c.GetIntervalUs(); us > 0 {
time.Sleep(time.Duration(us) * time.Microsecond)
}
pl, err := newPayload(in.GetResponseType(), c.GetSize())
if err != nil {
return err
}
if err := stream.Send(&testpb.StreamingOutputCallResponse{
Payload: pl,
}); err != nil {
return err
}
}
}
}
func (s *testServer) HalfDuplexCall(stream testpb.TestService_HalfDuplexCallServer) error {
var msgBuf []*testpb.StreamingOutputCallRequest
for {
in, err := stream.Recv()
if err == io.EOF {
// read done.
break
}
if err != nil {
return err
}
msgBuf = append(msgBuf, in)
}
for _, m := range msgBuf {
cs := m.GetResponseParameters()
for _, c := range cs {
if us := c.GetIntervalUs(); us > 0 {
time.Sleep(time.Duration(us) * time.Microsecond)
}
pl, err := newPayload(m.GetResponseType(), c.GetSize())
if err != nil {
return err
}
if err := stream.Send(&testpb.StreamingOutputCallResponse{
Payload: pl,
}); err != nil {
return err
}
}
}
return nil
}
func main() {
flag.Parse()
p := strconv.Itoa(*port)
lis, err := net.Listen("tcp", ":"+p)
if err != nil {
grpclog.Fatalf("failed to listen: %v", err)
}
var opts []grpc.ServerOption
if *useTLS {
creds, err := credentials.NewServerTLSFromFile(*certFile, *keyFile)
if err != nil {
grpclog.Fatalf("Failed to generate credentials %v", err)
}
opts = []grpc.ServerOption{grpc.Creds(creds)}
}
server := grpc.NewServer(opts...)
testpb.RegisterTestServiceServer(server, &testServer{})
server.Serve(lis)
}
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/interop/server/testdata/ 0000775 0000000 0000000 00000000000 12603607541 0024306 5 ustar 00root root 0000000 0000000 grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/interop/server/testdata/ca.pem 0000664 0000000 0000000 00000001442 12603607541 0025375 0 ustar 00root root 0000000 0000000 -----BEGIN CERTIFICATE-----
MIICIzCCAYwCCQCFTbF7XNSvvjANBgkqhkiG9w0BAQUFADBWMQswCQYDVQQGEwJB
VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0
cyBQdHkgTHRkMQ8wDQYDVQQDDAZ0ZXN0Y2EwHhcNMTQwNzE3MjMxNzUxWhcNMjQw
NzE0MjMxNzUxWjBWMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEh
MB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMQ8wDQYDVQQDDAZ0ZXN0
Y2EwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMBA3wVeTGHZR1Rye/i+J8a2
cu5gXwFV6TnObzGM7bLFCO5i9v4mLo4iFzPsHmWDUxKS3Y8iXbu0eYBlLoNY0lSv
xDx33O+DuwMmVN+DzSD+Eod9zfvwOWHsazYCZT2PhNxnVWIuJXViY4JAHUGodjx+
QAi6yCAurUZGvYXGgZSBAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAQoQVD8bwdtWJ
AniGBwcCfqYyH+/KpA10AcebJVVTyYbYvI9Q8d6RSVu4PZy9OALHR/QrWBdYTAyz
fNAmc2cmdkSRJzjhIaOstnQy1J+Fk0T9XyvQtq499yFbq9xogUVlEGH62xP6vH0Y
5ukK//dCPAzA11YuX2rnex0JhuTQfcI=
-----END CERTIFICATE-----
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/interop/server/testdata/server1.key 0000664 0000000 0000000 00000001567 12603607541 0026420 0 ustar 00root root 0000000 0000000 -----BEGIN RSA PRIVATE KEY-----
MIICWwIBAAKBgQDhwxUnKCwlSaWAwzOB2LSHVegJHv7DDWminTg4wzLLsf+LQ8nZ
bpjfn5vgIzxCuRh4Rp9QYM5FhfrJX9wcYawP/HTbJ7p7LVQO2QYAP+akMTHxgKuM
BzVV++3wWToKfVZUjFX8nfTfGMGwWAHJDnlEGnU4tl9UujoCV4ENJtzFoQIDAQAB
AoGAJ+6hpzNr24yTQZtFWQpDpEyFplddKJMOxDya3S9ppK3vTWrIITV2xNcucw7I
ceTbdyrGsyjsU0/HdCcIf9ym2jfmGLUwmyhltKVw0QYcFB0XLkc0nI5YvEYoeVDg
omZIXn1E3EW+sSIWSbkMu9bY2kstKXR2UZmMgWDtmBEPMaECQQD6yT4TAZM5hGBb
ciBKgMUP6PwOhPhOMPIvijO50Aiu6iuCV88l1QIy38gWVhxjNrq6P346j4IBg+kB
9alwpCODAkEA5nSnm9k6ykYeQWNS0fNWiRinCdl23A7usDGSuKKlm019iomJ/Rgd
MKDOp0q/2OostbteOWM2MRFf4jMH3wyVCwJAfAdjJ8szoNKTRSagSbh9vWygnB2v
IByc6l4TTuZQJRGzCveafz9lovuB3WohCABdQRd9ukCXL2CpsEpqzkafOQJAJUjc
USedDlq3zGZwYM1Yw8d8RuirBUFZNqJelYai+nRYClDkRVFgb5yksoYycbq5TxGo
VeqKOvgPpj4RWPHlLwJAGUMk3bqT91xBUCnLRs/vfoCpHpg6eywQTBDAV6xkyz4a
RH3I7/+yj3ZxR2JoWHgUwZ7lZk1VnhffFye7SBXyag==
-----END RSA PRIVATE KEY-----
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/interop/server/testdata/server1.pem 0000664 0000000 0000000 00000001704 12603607541 0026402 0 ustar 00root root 0000000 0000000 -----BEGIN CERTIFICATE-----
MIICmzCCAgSgAwIBAgIBAzANBgkqhkiG9w0BAQUFADBWMQswCQYDVQQGEwJBVTET
MBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQ
dHkgTHRkMQ8wDQYDVQQDDAZ0ZXN0Y2EwHhcNMTQwNzIyMDYwMDU3WhcNMjQwNzE5
MDYwMDU3WjBkMQswCQYDVQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNV
BAcTB0NoaWNhZ28xFDASBgNVBAoTC0dvb2dsZSBJbmMuMRowGAYDVQQDFBEqLnRl
c3QuZ29vZ2xlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA4cMVJygs
JUmlgMMzgdi0h1XoCR7+ww1pop04OMMyy7H/i0PJ2W6Y35+b4CM8QrkYeEafUGDO
RYX6yV/cHGGsD/x02ye6ey1UDtkGAD/mpDEx8YCrjAc1Vfvt8Fk6Cn1WVIxV/J30
3xjBsFgByQ55RBp1OLZfVLo6AleBDSbcxaECAwEAAaNrMGkwCQYDVR0TBAIwADAL
BgNVHQ8EBAMCBeAwTwYDVR0RBEgwRoIQKi50ZXN0Lmdvb2dsZS5mcoIYd2F0ZXJ6
b29pLnRlc3QuZ29vZ2xlLmJlghIqLnRlc3QueW91dHViZS5jb22HBMCoAQMwDQYJ
KoZIhvcNAQEFBQADgYEAM2Ii0LgTGbJ1j4oqX9bxVcxm+/R5Yf8oi0aZqTJlnLYS
wXcBykxTx181s7WyfJ49WwrYXo78zTDAnf1ma0fPq3e4mpspvyndLh1a+OarHa1e
aT0DIIYk7qeEa1YcVljx2KyLd0r1BBAfrwyGaEPVeJQVYWaOJRU2we/KD4ojf9s=
-----END CERTIFICATE-----
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/metadata/ 0000775 0000000 0000000 00000000000 12603607541 0021267 5 ustar 00root root 0000000 0000000 grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/metadata/metadata.go 0000664 0000000 0000000 00000010165 12603607541 0023401 0 ustar 00root root 0000000 0000000 /*
*
* Copyright 2014, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
// Package metadata define the structure of the metadata supported by gRPC library.
package metadata // import "google.golang.org/grpc/metadata"
import (
"encoding/base64"
"fmt"
"strings"
"golang.org/x/net/context"
)
const (
binHdrSuffix = "-bin"
)
// grpc-http2 requires ASCII header key and value (more detail can be found in
// "Requests" subsection in go/grpc-http2).
func isASCII(s string) bool {
for _, c := range s {
if c > 127 {
return false
}
}
return true
}
// encodeKeyValue encodes key and value qualified for transmission via gRPC.
// Transmitting binary headers violates HTTP/2 spec.
// TODO(zhaoq): Maybe check if k is ASCII also.
func encodeKeyValue(k, v string) (string, string) {
if isASCII(v) {
return k, v
}
key := strings.ToLower(k + binHdrSuffix)
val := base64.StdEncoding.EncodeToString([]byte(v))
return key, string(val)
}
// DecodeKeyValue returns the original key and value corresponding to the
// encoded data in k, v.
func DecodeKeyValue(k, v string) (string, string, error) {
if !strings.HasSuffix(k, binHdrSuffix) {
return k, v, nil
}
key := k[:len(k)-len(binHdrSuffix)]
val, err := base64.StdEncoding.DecodeString(v)
if err != nil {
return "", "", err
}
return key, string(val), 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 a MD from given key-value map.
func New(m map[string]string) MD {
md := MD{}
for k, v := range m {
key, val := encodeKeyValue(k, v)
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.
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 k string
for i, s := range kv {
if i%2 == 0 {
k = s
continue
}
key, val := encodeKeyValue(k, s)
md[key] = append(md[key], val)
}
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 {
out := MD{}
for k, v := range md {
for _, i := range v {
out[k] = append(out[k], i)
}
}
return out
}
type mdKey struct{}
// NewContext creates a new context with md attached.
func NewContext(ctx context.Context, md MD) context.Context {
return context.WithValue(ctx, mdKey{}, md)
}
// FromContext returns the MD in ctx if it exists.
func FromContext(ctx context.Context) (md MD, ok bool) {
md, ok = ctx.Value(mdKey{}).(MD)
return
}
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/metadata/metadata_test.go 0000664 0000000 0000000 00000005122 12603607541 0024435 0 ustar 00root root 0000000 0000000 /*
*
* Copyright 2014, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
package metadata
import (
"reflect"
"testing"
)
const binaryValue = string(128)
func TestDecodeKeyValue(t *testing.T) {
for _, test := range []struct {
// input
kin string
vin string
// output
kout string
vout string
err error
}{
{"a", "abc", "a", "abc", nil},
{"key-bin", "Zm9vAGJhcg==", "key", "foo\x00bar", nil},
{"key-bin", "woA=", "key", binaryValue, nil},
} {
k, v, err := DecodeKeyValue(test.kin, test.vin)
if k != test.kout || !reflect.DeepEqual(v, test.vout) || !reflect.DeepEqual(err, test.err) {
t.Fatalf("DecodeKeyValue(%q, %q) = %q, %q, %v, want %q, %q, %v", test.kin, test.vin, k, v, err, test.kout, test.vout, test.err)
}
}
}
func TestPairsMD(t *testing.T) {
for _, test := range []struct {
// input
kv []string
// output
md MD
}{
{[]string{}, MD{}},
{[]string{"k1", "v1", "k2", binaryValue}, New(map[string]string{
"k1": "v1",
"k2-bin": "woA=",
})},
} {
md := Pairs(test.kv...)
if !reflect.DeepEqual(md, test.md) {
t.Fatalf("Pairs(%v) = %v, want %v", test.kv, md, test.md)
}
}
}
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/naming/ 0000775 0000000 0000000 00000000000 12603607541 0020760 5 ustar 00root root 0000000 0000000 grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/naming/etcd/ 0000775 0000000 0000000 00000000000 12603607541 0021677 5 ustar 00root root 0000000 0000000 grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/naming/etcd/etcd.go 0000664 0000000 0000000 00000010010 12603607541 0023135 0 ustar 00root root 0000000 0000000 /*
*
* Copyright 2014, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
package etcd
import (
etcdcl "github.com/coreos/etcd/client"
"golang.org/x/net/context"
"google.golang.org/grpc/naming"
)
// update defines an etcd key-value update.
type update struct {
key, val string
}
// getNode reports the set of changes starting from node recursively.
func getNode(node *etcdcl.Node) (updates []*update) {
for _, v := range node.Nodes {
updates = append(updates, getNode(v)...)
}
if !node.Dir {
u := &update{
key: node.Key,
val: node.Value,
}
updates = []*update{u}
}
return
}
type watcher struct {
wr etcdcl.Watcher
ctx context.Context
cancel context.CancelFunc
kv map[string]string
}
func (w *watcher) Next() (nu []*naming.Update, _ error) {
for {
resp, err := w.wr.Next(w.ctx)
if err != nil {
return nil, err
}
updates := getNode(resp.Node)
for _, u := range updates {
switch resp.Action {
case "set":
if resp.PrevNode == nil {
w.kv[u.key] = u.val
nu = append(nu, &naming.Update{
Op: naming.Add,
Addr: u.val,
})
} else {
nu = append(nu, &naming.Update{
Op: naming.Delete,
Addr: w.kv[u.key],
})
nu = append(nu, &naming.Update{
Op: naming.Add,
Addr: u.val,
})
w.kv[u.key] = u.val
}
case "delete":
nu = append(nu, &naming.Update{
Op: naming.Delete,
Addr: w.kv[u.key],
})
delete(w.kv, u.key)
}
}
if len(nu) > 0 {
break
}
}
return nu, nil
}
func (w *watcher) Stop() {
w.cancel()
}
type resolver struct {
kapi etcdcl.KeysAPI
kv map[string]string
}
func (r *resolver) NewWatcher(target string) naming.Watcher {
ctx, cancel := context.WithCancel(context.Background())
w := &watcher{
wr: r.kapi.Watcher(target, &etcdcl.WatcherOptions{Recursive: true}),
ctx: ctx,
cancel: cancel,
}
for k, v := range r.kv {
w.kv[k] = v
}
return w
}
func (r *resolver) Resolve(target string) (nu []*naming.Update, _ error) {
resp, err := r.kapi.Get(context.Background(), target, &etcdcl.GetOptions{Recursive: true})
if err != nil {
return nil, err
}
updates := getNode(resp.Node)
for _, u := range updates {
r.kv[u.key] = u.val
nu = append(nu, &naming.Update{
Op: naming.Add,
Addr: u.val,
})
}
return nu, nil
}
// NewResolver creates an etcd-based naming.Resolver.
func NewResolver(cfg etcdcl.Config) (naming.Resolver, error) {
c, err := etcdcl.New(cfg)
if err != nil {
return nil, err
}
return &resolver{
kapi: etcdcl.NewKeysAPI(c),
kv: make(map[string]string),
}, nil
}
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/naming/naming.go 0000664 0000000 0000000 00000005200 12603607541 0022555 0 ustar 00root root 0000000 0000000 /*
*
* Copyright 2014, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
// Package naming defines the naming API and related data structures for gRPC.
// The interface is EXPERIMENTAL and may be suject to change.
package naming
// OP defines the corresponding operations for a name resolution change.
type OP uint8
const (
// Add indicates a new address is added.
Add = iota
// Delete indicates an exisiting address is deleted.
Delete
)
type ServiceConfig interface{}
// Update defines a name resolution change.
type Update struct {
// Op indicates the operation of the update.
Op OP
Addr string
Config ServiceConfig
}
// Resolver does one-shot name resolution and creates a Watcher to
// watch the future updates.
type Resolver interface {
// Resolve returns the name resolution results.
Resolve(target string) ([]*Update, error)
// NewWatcher creates a Watcher to watch the changes on target.
NewWatcher(target string) Watcher
}
// Watcher watches the updates for a particular target.
type Watcher interface {
// Next blocks until an update or error happens. It may return one or more
// updates.
Next() ([]*Update, error)
// Stop stops the Watcher.
Stop()
}
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/picker.go 0000664 0000000 0000000 00000006124 12603607541 0021316 0 ustar 00root root 0000000 0000000 /*
*
* Copyright 2014, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
package grpc
import (
"time"
)
// Picker picks a Conn for RPC requests.
// This is EXPERIMENTAL and Please do not implement your own Picker for now.
type Picker interface {
// Init does initial processing for the Picker, e.g., initiate some connections.
Init(cc *ClientConn) error
// Pick returns the Conn to use for the upcoming RPC. It may return different
// Conn's up to the implementation.
Pick() (*Conn, error)
// State returns the connectivity state of the underlying connections.
State() ConnectivityState
// WaitForStateChange blocks until the state changes to something other than
// the sourceState or timeout fires on cc. It returns false if timeout fires,
// and true otherwise.
WaitForStateChange(timeout time.Duration, sourceState ConnectivityState) bool
// Close closes all the Conn's owned by this Picker.
Close() error
}
// unicastPicker is the default Picker which is used when there is no custom Picker
// specified by users. It always picks the same Conn.
type unicastPicker struct {
conn *Conn
}
func (p *unicastPicker) Init(cc *ClientConn) error {
c, err := NewConn(cc)
if err != nil {
return err
}
p.conn = c
return nil
}
func (p *unicastPicker) Pick() (*Conn, error) {
return p.conn, nil
}
func (p *unicastPicker) State() ConnectivityState {
return p.conn.State()
}
func (p *unicastPicker) WaitForStateChange(timeout time.Duration, sourceState ConnectivityState) bool {
return p.conn.WaitForStateChange(timeout, sourceState)
}
func (p *unicastPicker) Close() error {
if p.conn != nil {
return p.conn.Close()
}
return nil
}
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/rpc_util.go 0000664 0000000 0000000 00000021321 12603607541 0021656 0 ustar 00root root 0000000 0000000 /*
*
* Copyright 2014, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
package grpc
import (
"bytes"
"encoding/binary"
"fmt"
"io"
"math/rand"
"os"
"time"
"github.com/golang/protobuf/proto"
"golang.org/x/net/context"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/transport"
)
// Codec defines the interface gRPC uses to encode and decode messages.
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. The returned
// string will be used as part of content type in transmission.
String() string
}
// protoCodec is a Codec implemetation with protobuf. It is the default codec for gRPC.
type protoCodec struct{}
func (protoCodec) Marshal(v interface{}) ([]byte, error) {
return proto.Marshal(v.(proto.Message))
}
func (protoCodec) Unmarshal(data []byte, v interface{}) error {
return proto.Unmarshal(data, v.(proto.Message))
}
func (protoCodec) String() string {
return "proto"
}
// 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)
}
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) {
*md = c.headerMD
})
}
// Trailer returns a CallOptions that retrieves the trailer metadata
// for a unary RPC.
func Trailer(md *metadata.MD) CallOption {
return afterCall(func(c *callInfo) {
*md = c.trailerMD
})
}
// The format of the payload: compressed or not?
type payloadFormat uint8
const (
compressionNone payloadFormat = iota // no compression
compressionFlate
// More formats
)
// parser reads complelete gRPC messages from the underlying reader.
type parser struct {
s io.Reader
}
// msgFixedHeader defines the header of a gRPC message (go/grpc-wirefmt).
type msgFixedHeader struct {
T payloadFormat
Length uint32
}
// recvMsg is to read a complete gRPC message from the stream. It is blocking if
// the message has not been complete yet. It returns the message and its type,
// EOF is returned with nil msg and 0 pf if the entire stream is done. Other
// non-nil error is returned if something is wrong on reading.
func (p *parser) recvMsg() (pf payloadFormat, msg []byte, err error) {
var hdr msgFixedHeader
if err := binary.Read(p.s, binary.BigEndian, &hdr); err != nil {
return 0, nil, err
}
if hdr.Length == 0 {
return hdr.T, nil, nil
}
msg = make([]byte, int(hdr.Length))
if _, err := io.ReadFull(p.s, msg); err != nil {
if err == io.EOF {
err = io.ErrUnexpectedEOF
}
return 0, nil, err
}
return hdr.T, msg, nil
}
// encode serializes msg and prepends the message header. If msg is nil, it
// generates the message header of 0 message length.
func encode(c Codec, msg interface{}, pf payloadFormat) ([]byte, error) {
var buf bytes.Buffer
// Write message fixed header.
buf.WriteByte(uint8(pf))
var b []byte
var length uint32
if msg != nil {
var err error
// TODO(zhaoq): optimize to reduce memory alloc and copying.
b, err = c.Marshal(msg)
if err != nil {
return nil, err
}
length = uint32(len(b))
}
var szHdr [4]byte
binary.BigEndian.PutUint32(szHdr[:], length)
buf.Write(szHdr[:])
buf.Write(b)
return buf.Bytes(), nil
}
func recv(p *parser, c Codec, m interface{}) error {
pf, d, err := p.recvMsg()
if err != nil {
return err
}
switch pf {
case compressionNone:
if err := c.Unmarshal(d, m); err != nil {
return Errorf(codes.Internal, "grpc: %v", err)
}
default:
return Errorf(codes.Internal, "gprc: compression is not supported yet.")
}
return nil
}
// rpcError defines the status from an RPC.
type rpcError struct {
code codes.Code
desc string
}
func (e rpcError) Error() string {
return fmt.Sprintf("rpc error: code = %d desc = %q", e.code, e.desc)
}
// Code returns the error code for err if it was produced by the rpc system.
// Otherwise, it returns codes.Unknown.
func Code(err error) codes.Code {
if err == nil {
return codes.OK
}
if e, ok := err.(rpcError); ok {
return e.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.
func ErrorDesc(err error) string {
if err == nil {
return ""
}
if e, ok := err.(rpcError); ok {
return e.desc
}
return err.Error()
}
// Errorf returns an error containing an error code and a description;
// Errorf returns nil if c is OK.
func Errorf(c codes.Code, format string, a ...interface{}) error {
if c == codes.OK {
return nil
}
return rpcError{
code: c,
desc: fmt.Sprintf(format, a...),
}
}
// toRPCErr converts an error into a rpcError.
func toRPCErr(err error) error {
switch e := err.(type) {
case transport.StreamError:
return rpcError{
code: e.Code,
desc: e.Desc,
}
case transport.ConnectionError:
return rpcError{
code: codes.Internal,
desc: e.Desc,
}
}
return Errorf(codes.Unknown, "%v", err)
}
// 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
}
const (
// how long to wait after the first failure before retrying
baseDelay = 1.0 * time.Second
// upper bound of backoff delay
maxDelay = 120 * time.Second
// backoff increases by this factor on each retry
backoffFactor = 1.6
// backoff is randomized downwards by this factor
backoffJitter = 0.2
)
func backoff(retries int) (t time.Duration) {
if retries == 0 {
return baseDelay
}
backoff, max := float64(baseDelay), float64(maxDelay)
for backoff < max && retries > 0 {
backoff *= backoffFactor
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 + backoffJitter*(rand.Float64()*2-1)
if backoff < 0 {
return 0
}
return time.Duration(backoff)
}
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/rpc_util_test.go 0000664 0000000 0000000 00000012676 12603607541 0022732 0 ustar 00root root 0000000 0000000 /*
*
* Copyright 2014, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
package grpc
import (
"bytes"
"io"
"reflect"
"testing"
"github.com/golang/protobuf/proto"
"golang.org/x/net/context"
"google.golang.org/grpc/codes"
perfpb "google.golang.org/grpc/test/codec_perf"
"google.golang.org/grpc/transport"
)
func TestSimpleParsing(t *testing.T) {
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},
} {
buf := bytes.NewReader(test.p)
parser := &parser{buf}
pt, b, err := parser.recvMsg()
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 := bytes.NewReader(p)
parser := &parser{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()
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, ",
i, p, pt, data, err, want.pt, want.data)
}
}
pt, data, err := parser.recvMsg()
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
pt payloadFormat
// outputs
b []byte
err error
}{
{nil, compressionNone, []byte{0, 0, 0, 0, 0}, nil},
} {
b, err := encode(protoCodec{}, test.msg, test.pt)
if err != test.err || !bytes.Equal(b, test.b) {
t.Fatalf("encode(_, _, %d) = %v, %v\nwant %v, %v", test.pt, b, err, test.b, test.err)
}
}
}
func TestToRPCErr(t *testing.T) {
for _, test := range []struct {
// input
errIn error
// outputs
errOut error
}{
{transport.StreamErrorf(codes.Unknown, ""), Errorf(codes.Unknown, "")},
{transport.ErrConnClosing, Errorf(codes.Internal, transport.ErrConnClosing.Desc)},
} {
err := toRPCErr(test.errIn)
if err != test.errOut {
t.Fatalf("toRPCErr{%v} = %v \nwant %v", test.errIn, err, test.errOut)
}
}
}
func TestContextErr(t *testing.T) {
for _, test := range []struct {
// input
errIn error
// outputs
errOut transport.StreamError
}{
{context.DeadlineExceeded, transport.StreamErrorf(codes.DeadlineExceeded, "%v", context.DeadlineExceeded)},
{context.Canceled, transport.StreamErrorf(codes.Canceled, "%v", context.Canceled)},
} {
err := transport.ContextErr(test.errIn)
if err != test.errOut {
t.Fatalf("ContextErr{%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) {
msg := &perfpb.Buffer{Body: make([]byte, mSize)}
encoded, _ := encode(protoCodec{}, msg, compressionNone)
encodedSz := int64(len(encoded))
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
encode(protoCodec{}, msg, compressionNone)
}
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)
}
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/server.go 0000664 0000000 0000000 00000036456 12603607541 0021362 0 ustar 00root root 0000000 0000000 /*
*
* Copyright 2014, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
package grpc
import (
"errors"
"fmt"
"io"
"net"
"reflect"
"runtime"
"strings"
"sync"
"time"
"golang.org/x/net/context"
"golang.org/x/net/trace"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/grpclog"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/transport"
)
type methodHandler func(srv interface{}, ctx context.Context, dec func(interface{}) error) (interface{}, error)
// MethodDesc represents an RPC service's method specification.
type MethodDesc struct {
MethodName string
Handler methodHandler
}
// ServiceDesc represents an RPC service's specification.
type ServiceDesc struct {
ServiceName string
// The pointer to the service interface. Used to check whether the user
// provided implementation satisfies the interface requirements.
HandlerType interface{}
Methods []MethodDesc
Streams []StreamDesc
}
// service consists of the information of the server serving this service and
// the methods in this service.
type service struct {
server interface{} // the server for service methods
md map[string]*MethodDesc
sd map[string]*StreamDesc
}
// Server is a gRPC server to serve RPC requests.
type Server struct {
opts options
mu sync.Mutex
lis map[net.Listener]bool
conns map[transport.ServerTransport]bool
m map[string]*service // service name -> service info
events trace.EventLog
}
type options struct {
creds credentials.Credentials
codec Codec
maxConcurrentStreams uint32
}
// A ServerOption sets options.
type ServerOption func(*options)
// CustomCodec returns a ServerOption that sets a codec for message marshaling and unmarshaling.
func CustomCodec(codec Codec) ServerOption {
return func(o *options) {
o.codec = codec
}
}
// MaxConcurrentStreams returns a ServerOption that will apply a limit on the number
// of concurrent streams to each ServerTransport.
func MaxConcurrentStreams(n uint32) ServerOption {
return func(o *options) {
o.maxConcurrentStreams = n
}
}
// Creds returns a ServerOption that sets credentials for server connections.
func Creds(c credentials.Credentials) ServerOption {
return func(o *options) {
o.creds = c
}
}
// NewServer creates a gRPC server which has no service registered and has not
// started to accept requests yet.
func NewServer(opt ...ServerOption) *Server {
var opts options
for _, o := range opt {
o(&opts)
}
if opts.codec == nil {
// Set the default codec.
opts.codec = protoCodec{}
}
s := &Server{
lis: make(map[net.Listener]bool),
opts: opts,
conns: make(map[transport.ServerTransport]bool),
m: make(map[string]*service),
}
if EnableTracing {
_, file, line, _ := runtime.Caller(1)
s.events = trace.NewEventLog("grpc.Server", fmt.Sprintf("%s:%d", file, line))
}
return s
}
// printf records an event in s's event log, unless s has been stopped.
// REQUIRES s.mu is held.
func (s *Server) printf(format string, a ...interface{}) {
if s.events != nil {
s.events.Printf(format, a...)
}
}
// errorf records an error in s's event log, unless s has been stopped.
// REQUIRES s.mu is held.
func (s *Server) errorf(format string, a ...interface{}) {
if s.events != nil {
s.events.Errorf(format, a...)
}
}
// RegisterService register a service and its implementation to the gRPC
// server. Called from the IDL generated code. This must be called before
// invoking Serve.
func (s *Server) RegisterService(sd *ServiceDesc, ss interface{}) {
ht := reflect.TypeOf(sd.HandlerType).Elem()
st := reflect.TypeOf(ss)
if !st.Implements(ht) {
grpclog.Fatalf("grpc: Server.RegisterService found the handler of type %v that does not satisfy %v", st, ht)
}
s.register(sd, ss)
}
func (s *Server) register(sd *ServiceDesc, ss interface{}) {
s.mu.Lock()
defer s.mu.Unlock()
s.printf("RegisterService(%q)", sd.ServiceName)
if _, ok := s.m[sd.ServiceName]; ok {
grpclog.Fatalf("grpc: Server.RegisterService found duplicate service registration for %q", sd.ServiceName)
}
srv := &service{
server: ss,
md: make(map[string]*MethodDesc),
sd: make(map[string]*StreamDesc),
}
for i := range sd.Methods {
d := &sd.Methods[i]
srv.md[d.MethodName] = d
}
for i := range sd.Streams {
d := &sd.Streams[i]
srv.sd[d.StreamName] = d
}
s.m[sd.ServiceName] = srv
}
var (
// ErrServerStopped indicates that the operation is now illegal because of
// the server being stopped.
ErrServerStopped = errors.New("grpc: the server has been stopped")
)
// Serve accepts incoming connections on the listener lis, creating a new
// ServerTransport and service goroutine for each. The service goroutines
// read gRPC request and then call the registered handlers to reply to them.
// Service returns when lis.Accept fails.
func (s *Server) Serve(lis net.Listener) error {
s.mu.Lock()
s.printf("serving")
if s.lis == nil {
s.mu.Unlock()
return ErrServerStopped
}
s.lis[lis] = true
s.mu.Unlock()
defer func() {
lis.Close()
s.mu.Lock()
delete(s.lis, lis)
s.mu.Unlock()
}()
for {
c, err := lis.Accept()
if err != nil {
s.mu.Lock()
s.printf("done serving; Accept = %v", err)
s.mu.Unlock()
return err
}
var authInfo credentials.AuthInfo
if creds, ok := s.opts.creds.(credentials.TransportAuthenticator); ok {
var conn net.Conn
conn, authInfo, err = creds.ServerHandshake(c)
if err != nil {
s.mu.Lock()
s.errorf("ServerHandshake(%q) failed: %v", c.RemoteAddr(), err)
s.mu.Unlock()
grpclog.Println("grpc: Server.Serve failed to complete security handshake.")
continue
}
c = conn
}
s.mu.Lock()
if s.conns == nil {
s.mu.Unlock()
c.Close()
return nil
}
st, err := transport.NewServerTransport("http2", c, s.opts.maxConcurrentStreams, authInfo, EnableTracing)
if err != nil {
s.errorf("NewServerTransport(%q) failed: %v", c.RemoteAddr(), err)
s.mu.Unlock()
c.Close()
grpclog.Println("grpc: Server.Serve failed to create ServerTransport: ", err)
continue
}
s.conns[st] = true
s.mu.Unlock()
go func() {
st.HandleStreams(func(stream *transport.Stream) {
s.handleStream(st, stream)
})
s.mu.Lock()
delete(s.conns, st)
s.mu.Unlock()
}()
}
}
func (s *Server) sendResponse(t transport.ServerTransport, stream *transport.Stream, msg interface{}, pf payloadFormat, opts *transport.Options) error {
p, err := encode(s.opts.codec, msg, pf)
if err != nil {
// This typically indicates a fatal issue (e.g., memory
// corruption or hardware faults) the application program
// cannot handle.
//
// TODO(zhaoq): There exist other options also such as only closing the
// faulty stream locally and remotely (Other streams can keep going). Find
// the optimal option.
grpclog.Fatalf("grpc: Server failed to encode response %v", err)
}
return t.Write(stream, p, opts)
}
func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.Stream, srv *service, md *MethodDesc) (err error) {
var traceInfo traceInfo
if EnableTracing {
traceInfo.tr = stream.Trace()
defer traceInfo.tr.Finish()
traceInfo.firstLine.client = false
traceInfo.firstLine.remoteAddr = t.RemoteAddr()
if dl, ok := stream.Context().Deadline(); ok {
traceInfo.firstLine.deadline = dl.Sub(time.Now())
}
traceInfo.tr.LazyLog(&traceInfo.firstLine, false)
defer func() {
if err != nil && err != io.EOF {
traceInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true)
traceInfo.tr.SetError()
}
}()
}
p := &parser{s: stream}
for {
pf, req, err := p.recvMsg()
if err == io.EOF {
// The entire stream is done (for unary RPC only).
return err
}
if err != nil {
switch err := err.(type) {
case transport.ConnectionError:
// Nothing to do here.
case transport.StreamError:
if err := t.WriteStatus(stream, err.Code, err.Desc); err != nil {
grpclog.Printf("grpc: Server.processUnaryRPC failed to write status: %v", err)
}
default:
panic(fmt.Sprintf("grpc: Unexpected error (%T) from recvMsg: %v", err, err))
}
return err
}
switch pf {
case compressionNone:
statusCode := codes.OK
statusDesc := ""
df := func(v interface{}) error {
if err := s.opts.codec.Unmarshal(req, v); err != nil {
return err
}
if traceInfo.tr != nil {
traceInfo.tr.LazyLog(&payload{sent: false, msg: v}, true)
}
return nil
}
reply, appErr := md.Handler(srv.server, stream.Context(), df)
if appErr != nil {
if err, ok := appErr.(rpcError); ok {
statusCode = err.code
statusDesc = err.desc
} else {
statusCode = convertCode(appErr)
statusDesc = appErr.Error()
}
if traceInfo.tr != nil && statusCode != codes.OK {
traceInfo.tr.LazyLog(stringer(statusDesc), true)
traceInfo.tr.SetError()
}
if err := t.WriteStatus(stream, statusCode, statusDesc); err != nil {
grpclog.Printf("grpc: Server.processUnaryRPC failed to write status: %v", err)
return err
}
return nil
}
if traceInfo.tr != nil {
traceInfo.tr.LazyLog(stringer("OK"), false)
}
opts := &transport.Options{
Last: true,
Delay: false,
}
if err := s.sendResponse(t, stream, reply, compressionNone, opts); err != nil {
switch err := err.(type) {
case transport.ConnectionError:
// Nothing to do here.
case transport.StreamError:
statusCode = err.Code
statusDesc = err.Desc
default:
statusCode = codes.Unknown
statusDesc = err.Error()
}
return err
}
if traceInfo.tr != nil {
traceInfo.tr.LazyLog(&payload{sent: true, msg: reply}, true)
}
return t.WriteStatus(stream, statusCode, statusDesc)
default:
panic(fmt.Sprintf("payload format to be supported: %d", pf))
}
}
}
func (s *Server) processStreamingRPC(t transport.ServerTransport, stream *transport.Stream, srv *service, sd *StreamDesc) (err error) {
ss := &serverStream{
t: t,
s: stream,
p: &parser{s: stream},
codec: s.opts.codec,
tracing: EnableTracing,
}
if ss.tracing {
ss.traceInfo.tr = stream.Trace()
ss.traceInfo.firstLine.client = false
ss.traceInfo.firstLine.remoteAddr = t.RemoteAddr()
if dl, ok := stream.Context().Deadline(); ok {
ss.traceInfo.firstLine.deadline = dl.Sub(time.Now())
}
ss.traceInfo.tr.LazyLog(&ss.traceInfo.firstLine, false)
defer func() {
ss.mu.Lock()
if err != nil && err != io.EOF {
ss.traceInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true)
ss.traceInfo.tr.SetError()
}
ss.traceInfo.tr.Finish()
ss.traceInfo.tr = nil
ss.mu.Unlock()
}()
}
if appErr := sd.Handler(srv.server, ss); appErr != nil {
if err, ok := appErr.(rpcError); ok {
ss.statusCode = err.code
ss.statusDesc = err.desc
} else {
ss.statusCode = convertCode(appErr)
ss.statusDesc = appErr.Error()
}
}
if ss.tracing {
ss.mu.Lock()
if ss.statusCode != codes.OK {
ss.traceInfo.tr.LazyLog(stringer(ss.statusDesc), true)
ss.traceInfo.tr.SetError()
} else {
ss.traceInfo.tr.LazyLog(stringer("OK"), false)
}
ss.mu.Unlock()
}
return t.WriteStatus(ss.s, ss.statusCode, ss.statusDesc)
}
func (s *Server) handleStream(t transport.ServerTransport, stream *transport.Stream) {
sm := stream.Method()
if sm != "" && sm[0] == '/' {
sm = sm[1:]
}
pos := strings.LastIndex(sm, "/")
if pos == -1 {
if err := t.WriteStatus(stream, codes.InvalidArgument, fmt.Sprintf("malformed method name: %q", stream.Method())); err != nil {
grpclog.Printf("grpc: Server.handleStream failed to write status: %v", err)
}
return
}
service := sm[:pos]
method := sm[pos+1:]
srv, ok := s.m[service]
if !ok {
if err := t.WriteStatus(stream, codes.Unimplemented, fmt.Sprintf("unknown service %v", service)); err != nil {
grpclog.Printf("grpc: Server.handleStream failed to write status: %v", err)
}
return
}
// Unary RPC or Streaming RPC?
if md, ok := srv.md[method]; ok {
s.processUnaryRPC(t, stream, srv, md)
return
}
if sd, ok := srv.sd[method]; ok {
s.processStreamingRPC(t, stream, srv, sd)
return
}
if err := t.WriteStatus(stream, codes.Unimplemented, fmt.Sprintf("unknown method %v", method)); err != nil {
grpclog.Printf("grpc: Server.handleStream failed to write status: %v", err)
}
}
// Stop stops the gRPC server. Once Stop returns, the server stops accepting
// connection requests and closes all the connected connections.
func (s *Server) Stop() {
s.mu.Lock()
listeners := s.lis
s.lis = nil
cs := s.conns
s.conns = nil
s.mu.Unlock()
for lis := range listeners {
lis.Close()
}
for c := range cs {
c.Close()
}
s.mu.Lock()
if s.events != nil {
s.events.Finish()
s.events = nil
}
s.mu.Unlock()
}
// TestingCloseConns closes all exiting transports but keeps s.lis accepting new
// connections. This is for test only now.
func (s *Server) TestingCloseConns() {
s.mu.Lock()
for c := range s.conns {
c.Close()
}
s.conns = make(map[transport.ServerTransport]bool)
s.mu.Unlock()
}
// SendHeader sends header metadata. It may be called at most once from a unary
// RPC handler. The ctx is the RPC handler's Context or one derived from it.
func SendHeader(ctx context.Context, md metadata.MD) error {
if md.Len() == 0 {
return nil
}
stream, ok := transport.StreamFromContext(ctx)
if !ok {
return fmt.Errorf("grpc: failed to fetch the stream from the context %v", ctx)
}
t := stream.ServerTransport()
if t == nil {
grpclog.Fatalf("grpc: SendHeader: %v has no ServerTransport to send header metadata.", stream)
}
return t.WriteHeader(stream, md)
}
// SetTrailer sets the trailer metadata that will be sent when an RPC returns.
// It may be called at most once from a unary RPC handler. The ctx is the RPC
// handler's Context or one derived from it.
func SetTrailer(ctx context.Context, md metadata.MD) error {
if md.Len() == 0 {
return nil
}
stream, ok := transport.StreamFromContext(ctx)
if !ok {
return fmt.Errorf("grpc: failed to fetch the stream from the context %v", ctx)
}
return stream.SetTrailer(md)
}
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/stream.go 0000664 0000000 0000000 00000024111 12603607541 0021330 0 ustar 00root root 0000000 0000000 /*
*
* Copyright 2014, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
package grpc
import (
"errors"
"io"
"sync"
"time"
"golang.org/x/net/context"
"golang.org/x/net/trace"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/transport"
)
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.
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.
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 streama nd returns an RPC status. On
// server side, it simply returns the error to the caller.
RecvMsg(m interface{}) error
}
// ClientStream defines the interface a client stream has to satify.
type ClientStream interface {
// Header returns the header metedata 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. It must be called
// after stream.Recv() returns non-nil error (including io.EOF) for
// bi-directional streaming and server streaming or stream.CloseAndRecv()
// returns for client streaming in order to receive trailer metadata if
// present. Otherwise, it could returns an empty MD even though trailer
// is present.
Trailer() metadata.MD
// CloseSend closes the send direction of the stream. It closes the stream
// when non-nil error is met.
CloseSend() error
Stream
}
// NewClientStream creates a new Stream for the client side. This is called
// by generated code.
func NewClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, method string, opts ...CallOption) (ClientStream, error) {
var (
conn *Conn
t transport.ClientTransport
err error
)
for {
conn, err = cc.dopts.picker.Pick()
if err != nil {
return nil, toRPCErr(err)
}
t, err = conn.Wait(ctx)
if err != nil {
if err == ErrTransientFailure {
continue
}
return nil, toRPCErr(err)
}
break
}
// TODO(zhaoq): CallOption is omitted. Add support when it is needed.
callHdr := &transport.CallHdr{
Host: conn.authority,
Method: method,
}
cs := &clientStream{
desc: desc,
codec: conn.dopts.codec,
tracing: EnableTracing,
}
if cs.tracing {
cs.traceInfo.tr = trace.New("grpc.Sent."+transport.MethodFamily(method), method)
cs.traceInfo.firstLine.client = true
if deadline, ok := ctx.Deadline(); ok {
cs.traceInfo.firstLine.deadline = deadline.Sub(time.Now())
}
cs.traceInfo.tr.LazyLog(&cs.traceInfo.firstLine, false)
ctx = trace.NewContext(ctx, cs.traceInfo.tr)
}
s, err := t.NewStream(ctx, callHdr)
if err != nil {
return nil, toRPCErr(err)
}
cs.t = t
cs.s = s
cs.p = &parser{s: s}
return cs, nil
}
// clientStream implements a client side Stream.
type clientStream struct {
t transport.ClientTransport
s *transport.Stream
p *parser
desc *StreamDesc
codec Codec
tracing bool // set to EnableTracing when the clientStream is created.
mu sync.Mutex // protects traceInfo
// traceInfo.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.
traceInfo traceInfo
}
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 {
if _, ok := err.(transport.ConnectionError); !ok {
cs.t.CloseStream(cs.s, err)
}
}
return m, err
}
func (cs *clientStream) Trailer() metadata.MD {
return cs.s.Trailer()
}
func (cs *clientStream) SendMsg(m interface{}) (err error) {
if cs.tracing {
cs.mu.Lock()
if cs.traceInfo.tr != nil {
cs.traceInfo.tr.LazyLog(&payload{sent: true, msg: m}, true)
}
cs.mu.Unlock()
}
defer func() {
if err == nil || err == io.EOF {
return
}
if _, ok := err.(transport.ConnectionError); !ok {
cs.t.CloseStream(cs.s, err)
}
err = toRPCErr(err)
}()
out, err := encode(cs.codec, m, compressionNone)
if err != nil {
return transport.StreamErrorf(codes.Internal, "grpc: %v", err)
}
return cs.t.Write(cs.s, out, &transport.Options{Last: false})
}
func (cs *clientStream) RecvMsg(m interface{}) (err error) {
err = recv(cs.p, cs.codec, m)
defer func() {
// err != nil indicates the termination of the stream.
if err != nil {
cs.finish(err)
}
}()
if err == nil {
if cs.tracing {
cs.mu.Lock()
if cs.traceInfo.tr != nil {
cs.traceInfo.tr.LazyLog(&payload{sent: false, msg: m}, true)
}
cs.mu.Unlock()
}
if !cs.desc.ClientStreams || cs.desc.ServerStreams {
return
}
// Special handling for client streaming rpc.
err = recv(cs.p, cs.codec, m)
cs.t.CloseStream(cs.s, err)
if err == nil {
return toRPCErr(errors.New("grpc: client streaming protocol violation: get , want "))
}
if err == io.EOF {
if cs.s.StatusCode() == codes.OK {
return nil
}
return Errorf(cs.s.StatusCode(), cs.s.StatusDesc())
}
return toRPCErr(err)
}
if _, ok := err.(transport.ConnectionError); !ok {
cs.t.CloseStream(cs.s, err)
}
if err == io.EOF {
if cs.s.StatusCode() == codes.OK {
// Returns io.EOF to indicate the end of the stream.
return
}
return Errorf(cs.s.StatusCode(), cs.s.StatusDesc())
}
return toRPCErr(err)
}
func (cs *clientStream) CloseSend() (err error) {
err = cs.t.Write(cs.s, nil, &transport.Options{Last: true})
if err == nil || err == io.EOF {
return
}
if _, ok := err.(transport.ConnectionError); !ok {
cs.t.CloseStream(cs.s, err)
}
err = toRPCErr(err)
return
}
func (cs *clientStream) finish(err error) {
if !cs.tracing {
return
}
cs.mu.Lock()
defer cs.mu.Unlock()
if cs.traceInfo.tr != nil {
if err == nil || err == io.EOF {
cs.traceInfo.tr.LazyPrintf("RPC: [OK]")
} else {
cs.traceInfo.tr.LazyPrintf("RPC: [%v]", err)
cs.traceInfo.tr.SetError()
}
cs.traceInfo.tr.Finish()
cs.traceInfo.tr = nil
}
}
// ServerStream defines the interface a server stream has to satisfy.
type ServerStream interface {
// SendHeader sends the header metadata. It should not be called
// after SendProto. It fails if called multiple times or if
// called after SendProto.
SendHeader(metadata.MD) error
// SetTrailer sets the trailer metadata which will be sent with the
// RPC status.
SetTrailer(metadata.MD)
Stream
}
// serverStream implements a server side Stream.
type serverStream struct {
t transport.ServerTransport
s *transport.Stream
p *parser
codec Codec
statusCode codes.Code
statusDesc string
tracing bool // set to EnableTracing when the serverStream is created.
mu sync.Mutex // protects traceInfo
// traceInfo.tr is set when the serverStream is created (if EnableTracing is true),
// and is set to nil when the serverStream's finish method is called.
traceInfo traceInfo
}
func (ss *serverStream) Context() context.Context {
return ss.s.Context()
}
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.tracing {
ss.mu.Lock()
if err == nil {
ss.traceInfo.tr.LazyLog(&payload{sent: true, msg: m}, true)
} else {
ss.traceInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true)
ss.traceInfo.tr.SetError()
}
ss.mu.Unlock()
}
}()
out, err := encode(ss.codec, m, compressionNone)
if err != nil {
err = transport.StreamErrorf(codes.Internal, "grpc: %v", err)
return err
}
return ss.t.Write(ss.s, out, &transport.Options{Last: false})
}
func (ss *serverStream) RecvMsg(m interface{}) (err error) {
defer func() {
if ss.tracing {
ss.mu.Lock()
if err == nil {
ss.traceInfo.tr.LazyLog(&payload{sent: false, msg: m}, true)
} else if err != io.EOF {
ss.traceInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true)
ss.traceInfo.tr.SetError()
}
ss.mu.Unlock()
}
}()
return recv(ss.p, ss.codec, m)
}
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/test/ 0000775 0000000 0000000 00000000000 12603607541 0020466 5 ustar 00root root 0000000 0000000 grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/test/codec_perf/ 0000775 0000000 0000000 00000000000 12603607541 0022557 5 ustar 00root root 0000000 0000000 grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/test/codec_perf/perf.pb.go 0000664 0000000 0000000 00000001661 12603607541 0024446 0 ustar 00root root 0000000 0000000 // Code generated by protoc-gen-go.
// source: perf.proto
// DO NOT EDIT!
/*
Package codec_perf is a generated protocol buffer package.
It is generated from these files:
perf.proto
It has these top-level messages:
Buffer
*/
package codec_perf
import proto "github.com/golang/protobuf/proto"
import math "math"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = math.Inf
// Buffer is a message that contains a body of bytes that is used to exercise
// encoding and decoding overheads.
type Buffer struct {
Body []byte `protobuf:"bytes,1,opt,name=body" json:"body,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *Buffer) Reset() { *m = Buffer{} }
func (m *Buffer) String() string { return proto.CompactTextString(m) }
func (*Buffer) ProtoMessage() {}
func (m *Buffer) GetBody() []byte {
if m != nil {
return m.Body
}
return nil
}
func init() {
}
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/test/codec_perf/perf.proto 0000664 0000000 0000000 00000000466 12603607541 0024606 0 ustar 00root root 0000000 0000000 // Messages used for performance tests that may not reference grpc directly for
// reasons of import cycles.
syntax = "proto2";
package codec.perf;
// Buffer is a message that contains a body of bytes that is used to exercise
// encoding and decoding overheads.
message Buffer {
optional bytes body = 1;
}
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/test/end2end_test.go 0000664 0000000 0000000 00000101033 12603607541 0023371 0 ustar 00root root 0000000 0000000 /*
*
* Copyright 2014, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
package grpc_test
import (
"fmt"
"io"
"math"
"net"
"reflect"
"runtime"
"sync"
"syscall"
"testing"
"time"
"github.com/golang/protobuf/proto"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/health"
healthpb "google.golang.org/grpc/health/grpc_health_v1alpha"
"google.golang.org/grpc/metadata"
testpb "google.golang.org/grpc/test/grpc_testing"
)
var (
testMetadata = metadata.MD{
"key1": []string{"value1"},
"key2": []string{"value2"},
}
testAppUA = "myApp1/1.0 myApp2/0.9"
)
type testServer struct {
security string // indicate the authentication protocol used by this server.
}
func (s *testServer) EmptyCall(ctx context.Context, in *testpb.Empty) (*testpb.Empty, error) {
if md, ok := metadata.FromContext(ctx); ok {
// For testing purpose, returns an error if there is attached metadata other than
// the user agent set by the client application.
if _, ok := md["user-agent"]; !ok {
return nil, grpc.Errorf(codes.DataLoss, "got extra metadata")
}
var str []string
for _, entry := range md["user-agent"] {
str = append(str, "ua", entry)
}
grpc.SendHeader(ctx, metadata.Pairs(str...))
}
return new(testpb.Empty), nil
}
func newPayload(t testpb.PayloadType, size int32) (*testpb.Payload, error) {
if size < 0 {
return nil, fmt.Errorf("Requested a response with invalid length %d", size)
}
body := make([]byte, size)
switch t {
case testpb.PayloadType_COMPRESSABLE:
case testpb.PayloadType_UNCOMPRESSABLE:
return nil, fmt.Errorf("PayloadType UNCOMPRESSABLE is not supported")
default:
return nil, fmt.Errorf("Unsupported payload type: %d", t)
}
return &testpb.Payload{
Type: t.Enum(),
Body: body,
}, nil
}
func (s *testServer) UnaryCall(ctx context.Context, in *testpb.SimpleRequest) (*testpb.SimpleResponse, error) {
md, ok := metadata.FromContext(ctx)
if ok {
if err := grpc.SendHeader(ctx, md); err != nil {
return nil, fmt.Errorf("grpc.SendHeader(%v, %v) = %v, want %v", ctx, md, err, nil)
}
grpc.SetTrailer(ctx, md)
}
if s.security != "" {
// Check Auth info
authInfo, ok := credentials.FromContext(ctx)
if !ok {
return nil, fmt.Errorf("Failed to get AuthInfo from ctx.")
}
var authType, serverName string
switch info := authInfo.(type) {
case credentials.TLSInfo:
authType = info.AuthType()
serverName = info.State.ServerName
default:
return nil, fmt.Errorf("Unknown AuthInfo type")
}
if authType != s.security {
return nil, fmt.Errorf("Wrong auth type: got %q, want %q", authType, s.security)
}
if serverName != "x.test.youtube.com" {
return nil, fmt.Errorf("Unknown server name %q", serverName)
}
}
// Simulate some service delay.
time.Sleep(time.Second)
payload, err := newPayload(in.GetResponseType(), in.GetResponseSize())
if err != nil {
return nil, err
}
return &testpb.SimpleResponse{
Payload: payload,
}, nil
}
func (s *testServer) StreamingOutputCall(args *testpb.StreamingOutputCallRequest, stream testpb.TestService_StreamingOutputCallServer) error {
if md, ok := metadata.FromContext(stream.Context()); ok {
// For testing purpose, returns an error if there is attached metadata.
if len(md) > 0 {
return grpc.Errorf(codes.DataLoss, "got extra metadata")
}
}
cs := args.GetResponseParameters()
for _, c := range cs {
if us := c.GetIntervalUs(); us > 0 {
time.Sleep(time.Duration(us) * time.Microsecond)
}
payload, err := newPayload(args.GetResponseType(), c.GetSize())
if err != nil {
return err
}
if err := stream.Send(&testpb.StreamingOutputCallResponse{
Payload: payload,
}); err != nil {
return err
}
}
return nil
}
func (s *testServer) StreamingInputCall(stream testpb.TestService_StreamingInputCallServer) error {
var sum int
for {
in, err := stream.Recv()
if err == io.EOF {
return stream.SendAndClose(&testpb.StreamingInputCallResponse{
AggregatedPayloadSize: proto.Int32(int32(sum)),
})
}
if err != nil {
return err
}
p := in.GetPayload().GetBody()
sum += len(p)
}
}
func (s *testServer) FullDuplexCall(stream testpb.TestService_FullDuplexCallServer) error {
md, ok := metadata.FromContext(stream.Context())
if ok {
if err := stream.SendHeader(md); err != nil {
return fmt.Errorf("%v.SendHeader(%v) = %v, want %v", stream, md, err, nil)
}
stream.SetTrailer(md)
}
for {
in, err := stream.Recv()
if err == io.EOF {
// read done.
return nil
}
if err != nil {
return err
}
cs := in.GetResponseParameters()
for _, c := range cs {
if us := c.GetIntervalUs(); us > 0 {
time.Sleep(time.Duration(us) * time.Microsecond)
}
payload, err := newPayload(in.GetResponseType(), c.GetSize())
if err != nil {
return err
}
if err := stream.Send(&testpb.StreamingOutputCallResponse{
Payload: payload,
}); err != nil {
return err
}
}
}
}
func (s *testServer) HalfDuplexCall(stream testpb.TestService_HalfDuplexCallServer) error {
var msgBuf []*testpb.StreamingOutputCallRequest
for {
in, err := stream.Recv()
if err == io.EOF {
// read done.
break
}
if err != nil {
return err
}
msgBuf = append(msgBuf, in)
}
for _, m := range msgBuf {
cs := m.GetResponseParameters()
for _, c := range cs {
if us := c.GetIntervalUs(); us > 0 {
time.Sleep(time.Duration(us) * time.Microsecond)
}
payload, err := newPayload(m.GetResponseType(), c.GetSize())
if err != nil {
return err
}
if err := stream.Send(&testpb.StreamingOutputCallResponse{
Payload: payload,
}); err != nil {
return err
}
}
}
return nil
}
const tlsDir = "testdata/"
func TestDialTimeout(t *testing.T) {
conn, err := grpc.Dial("Non-Existent.Server:80", grpc.WithTimeout(time.Millisecond), grpc.WithBlock(), grpc.WithInsecure())
if err == nil {
conn.Close()
}
if err != grpc.ErrClientConnTimeout {
t.Fatalf("grpc.Dial(_, _) = %v, %v, want %v", conn, err, grpc.ErrClientConnTimeout)
}
}
func TestTLSDialTimeout(t *testing.T) {
creds, err := credentials.NewClientTLSFromFile(tlsDir+"ca.pem", "x.test.youtube.com")
if err != nil {
t.Fatalf("Failed to create credentials %v", err)
}
conn, err := grpc.Dial("Non-Existent.Server:80", grpc.WithTransportCredentials(creds), grpc.WithTimeout(time.Millisecond), grpc.WithBlock())
if err == nil {
conn.Close()
}
if err != grpc.ErrClientConnTimeout {
t.Fatalf("grpc.Dial(_, _) = %v, %v, want %v", conn, err, grpc.ErrClientConnTimeout)
}
}
func TestCredentialsMisuse(t *testing.T) {
creds, err := credentials.NewClientTLSFromFile(tlsDir+"ca.pem", "x.test.youtube.com")
if err != nil {
t.Fatalf("Failed to create credentials %v", err)
}
// Two conflicting credential configurations
if _, err := grpc.Dial("Non-Existent.Server:80", grpc.WithTransportCredentials(creds), grpc.WithTimeout(time.Millisecond), grpc.WithBlock(), grpc.WithInsecure()); err != grpc.ErrCredentialsMisuse {
t.Fatalf("grpc.Dial(_, _) = _, %v, want _, %v", err, grpc.ErrCredentialsMisuse)
}
// security info on insecure connection
if _, err := grpc.Dial("Non-Existent.Server:80", grpc.WithPerRPCCredentials(creds), grpc.WithTimeout(time.Millisecond), grpc.WithBlock(), grpc.WithInsecure()); err != grpc.ErrCredentialsMisuse {
t.Fatalf("grpc.Dial(_, _) = _, %v, want _, %v", err, grpc.ErrCredentialsMisuse)
}
}
func TestReconnectTimeout(t *testing.T) {
lis, err := net.Listen("tcp", ":0")
if err != nil {
t.Fatalf("Failed to listen: %v", err)
}
_, port, err := net.SplitHostPort(lis.Addr().String())
if err != nil {
t.Fatalf("Failed to parse listener address: %v", err)
}
addr := "localhost:" + port
conn, err := grpc.Dial(addr, grpc.WithTimeout(5*time.Second), grpc.WithBlock(), grpc.WithInsecure())
if err != nil {
t.Fatalf("Failed to dial to the server %q: %v", addr, err)
}
// Close unaccepted connection (i.e., conn).
lis.Close()
tc := testpb.NewTestServiceClient(conn)
waitC := make(chan struct{})
go func() {
defer close(waitC)
argSize := 271828
respSize := 314159
payload, err := newPayload(testpb.PayloadType_COMPRESSABLE, int32(argSize))
if err != nil {
t.Fatal(err)
}
req := &testpb.SimpleRequest{
ResponseType: testpb.PayloadType_COMPRESSABLE.Enum(),
ResponseSize: proto.Int32(int32(respSize)),
Payload: payload,
}
if _, err := tc.UnaryCall(context.Background(), req); err == nil {
t.Fatalf("TestService/UnaryCall(_, _) = _, , want _, non-nil")
}
}()
// Block untill reconnect times out.
<-waitC
if err := conn.Close(); err != grpc.ErrClientConnClosing {
t.Fatalf("%v.Close() = %v, want %v", conn, err, grpc.ErrClientConnClosing)
}
}
func unixDialer(addr string, timeout time.Duration) (net.Conn, error) {
return net.DialTimeout("unix", addr, timeout)
}
type env struct {
network string // The type of network such as tcp, unix, etc.
dialer func(addr string, timeout time.Duration) (net.Conn, error)
security string // The security protocol such as TLS, SSH, etc.
}
func listTestEnv() []env {
if runtime.GOOS == "windows" {
return []env{{"tcp", nil, ""}, {"tcp", nil, "tls"}}
}
return []env{{"tcp", nil, ""}, {"tcp", nil, "tls"}, {"unix", unixDialer, ""}, {"unix", unixDialer, "tls"}}
}
func setUp(t *testing.T, hs *health.HealthServer, maxStream uint32, ua string, e env) (s *grpc.Server, cc *grpc.ClientConn) {
sopts := []grpc.ServerOption{grpc.MaxConcurrentStreams(maxStream)}
la := ":0"
switch e.network {
case "unix":
la = "/tmp/testsock" + fmt.Sprintf("%d", time.Now())
syscall.Unlink(la)
}
lis, err := net.Listen(e.network, la)
if err != nil {
t.Fatalf("Failed to listen: %v", err)
}
if e.security == "tls" {
creds, err := credentials.NewServerTLSFromFile(tlsDir+"server1.pem", tlsDir+"server1.key")
if err != nil {
t.Fatalf("Failed to generate credentials %v", err)
}
sopts = append(sopts, grpc.Creds(creds))
}
s = grpc.NewServer(sopts...)
if hs != nil {
healthpb.RegisterHealthServer(s, hs)
}
testpb.RegisterTestServiceServer(s, &testServer{security: e.security})
go s.Serve(lis)
addr := la
switch e.network {
case "unix":
default:
_, port, err := net.SplitHostPort(lis.Addr().String())
if err != nil {
t.Fatalf("Failed to parse listener address: %v", err)
}
addr = "localhost:" + port
}
if e.security == "tls" {
creds, err := credentials.NewClientTLSFromFile(tlsDir+"ca.pem", "x.test.youtube.com")
if err != nil {
t.Fatalf("Failed to create credentials %v", err)
}
cc, err = grpc.Dial(addr, grpc.WithTransportCredentials(creds), grpc.WithDialer(e.dialer), grpc.WithUserAgent(ua))
} else {
cc, err = grpc.Dial(addr, grpc.WithDialer(e.dialer), grpc.WithInsecure(), grpc.WithUserAgent(ua))
}
if err != nil {
t.Fatalf("Dial(%q) = %v", addr, err)
}
return
}
func tearDown(s *grpc.Server, cc *grpc.ClientConn) {
cc.Close()
s.Stop()
}
func TestTimeoutOnDeadServer(t *testing.T) {
for _, e := range listTestEnv() {
testTimeoutOnDeadServer(t, e)
}
}
func testTimeoutOnDeadServer(t *testing.T, e env) {
s, cc := setUp(t, nil, math.MaxUint32, "", e)
tc := testpb.NewTestServiceClient(cc)
if ok := cc.WaitForStateChange(time.Second, grpc.Idle); !ok {
t.Fatalf("cc.WaitForStateChange(_, %s) = %t, want true", grpc.Idle, ok)
}
if ok := cc.WaitForStateChange(time.Second, grpc.Connecting); !ok {
t.Fatalf("cc.WaitForStateChange(_, %s) = %t, want true", grpc.Connecting, ok)
}
if cc.State() != grpc.Ready {
t.Fatalf("cc.State() = %s, want %s", cc.State(), grpc.Ready)
}
if ok := cc.WaitForStateChange(time.Millisecond, grpc.Ready); ok {
t.Fatalf("cc.WaitForStateChange(_, %s) = %t, want false", grpc.Ready, ok)
}
s.Stop()
// Set -1 as the timeout to make sure if transportMonitor gets error
// notification in time the failure path of the 1st invoke of
// ClientConn.wait hits the deadline exceeded error.
ctx, _ := context.WithTimeout(context.Background(), -1)
if _, err := tc.EmptyCall(ctx, &testpb.Empty{}); grpc.Code(err) != codes.DeadlineExceeded {
t.Fatalf("TestService/EmptyCall(%v, _) = _, error %v, want _, error code: %d", ctx, err, codes.DeadlineExceeded)
}
if ok := cc.WaitForStateChange(time.Second, grpc.Ready); !ok {
t.Fatalf("cc.WaitForStateChange(_, %s) = %t, want true", grpc.Ready, ok)
}
state := cc.State()
if state != grpc.Connecting && state != grpc.TransientFailure {
t.Fatalf("cc.State() = %s, want %s or %s", state, grpc.Connecting, grpc.TransientFailure)
}
cc.Close()
}
func healthCheck(t time.Duration, cc *grpc.ClientConn, serviceName string) (*healthpb.HealthCheckResponse, error) {
ctx, _ := context.WithTimeout(context.Background(), t)
hc := healthpb.NewHealthClient(cc)
req := &healthpb.HealthCheckRequest{
Service: serviceName,
}
return hc.Check(ctx, req)
}
func TestHealthCheckOnSuccess(t *testing.T) {
for _, e := range listTestEnv() {
testHealthCheckOnSuccess(t, e)
}
}
func testHealthCheckOnSuccess(t *testing.T, e env) {
hs := health.NewHealthServer()
hs.SetServingStatus("grpc.health.v1alpha.Health", 1)
s, cc := setUp(t, hs, math.MaxUint32, "", e)
defer tearDown(s, cc)
if _, err := healthCheck(1*time.Second, cc, "grpc.health.v1alpha.Health"); err != nil {
t.Fatalf("Health/Check(_, _) = _, %v, want _, ", err)
}
}
func TestHealthCheckOnFailure(t *testing.T) {
for _, e := range listTestEnv() {
testHealthCheckOnFailure(t, e)
}
}
func testHealthCheckOnFailure(t *testing.T, e env) {
hs := health.NewHealthServer()
hs.SetServingStatus("grpc.health.v1alpha.HealthCheck", 1)
s, cc := setUp(t, hs, math.MaxUint32, "", e)
defer tearDown(s, cc)
if _, err := healthCheck(0*time.Second, cc, "grpc.health.v1alpha.Health"); err != grpc.Errorf(codes.DeadlineExceeded, "context deadline exceeded") {
t.Fatalf("Health/Check(_, _) = _, %v, want _, error code %d", err, codes.DeadlineExceeded)
}
}
func TestHealthCheckOff(t *testing.T) {
for _, e := range listTestEnv() {
testHealthCheckOff(t, e)
}
}
func testHealthCheckOff(t *testing.T, e env) {
s, cc := setUp(t, nil, math.MaxUint32, "", e)
defer tearDown(s, cc)
if _, err := healthCheck(1*time.Second, cc, ""); err != grpc.Errorf(codes.Unimplemented, "unknown service grpc.health.v1alpha.Health") {
t.Fatalf("Health/Check(_, _) = _, %v, want _, error code %d", err, codes.Unimplemented)
}
}
func TestHealthCheckServingStatus(t *testing.T) {
for _, e := range listTestEnv() {
testHealthCheckServingStatus(t, e)
}
}
func testHealthCheckServingStatus(t *testing.T, e env) {
hs := health.NewHealthServer()
s, cc := setUp(t, hs, math.MaxUint32, "", e)
defer tearDown(s, cc)
out, err := healthCheck(1*time.Second, cc, "")
if err != nil {
t.Fatalf("Health/Check(_, _) = _, %v, want _, ", err)
}
if out.Status != healthpb.HealthCheckResponse_SERVING {
t.Fatalf("Got the serving status %v, want SERVING", out.Status)
}
if _, err := healthCheck(1*time.Second, cc, "grpc.health.v1alpha.Health"); err != grpc.Errorf(codes.NotFound, "unknown service") {
t.Fatalf("Health/Check(_, _) = _, %v, want _, error code %d", err, codes.NotFound)
}
hs.SetServingStatus("grpc.health.v1alpha.Health", healthpb.HealthCheckResponse_SERVING)
out, err = healthCheck(1*time.Second, cc, "grpc.health.v1alpha.Health")
if err != nil {
t.Fatalf("Health/Check(_, _) = _, %v, want _, ", err)
}
if out.Status != healthpb.HealthCheckResponse_SERVING {
t.Fatalf("Got the serving status %v, want SERVING", out.Status)
}
hs.SetServingStatus("grpc.health.v1alpha.Health", healthpb.HealthCheckResponse_NOT_SERVING)
out, err = healthCheck(1*time.Second, cc, "grpc.health.v1alpha.Health")
if err != nil {
t.Fatalf("Health/Check(_, _) = _, %v, want _, ", err)
}
if out.Status != healthpb.HealthCheckResponse_NOT_SERVING {
t.Fatalf("Got the serving status %v, want NOT_SERVING", out.Status)
}
}
func TestEmptyUnaryWithUserAgent(t *testing.T) {
for _, e := range listTestEnv() {
testEmptyUnaryWithUserAgent(t, e)
}
}
func testEmptyUnaryWithUserAgent(t *testing.T, e env) {
s, cc := setUp(t, nil, math.MaxUint32, testAppUA, e)
// Wait until cc is connected.
if ok := cc.WaitForStateChange(time.Second, grpc.Idle); !ok {
t.Fatalf("cc.WaitForStateChange(_, %s) = %t, want true", grpc.Idle, ok)
}
if ok := cc.WaitForStateChange(10*time.Second, grpc.Connecting); !ok {
t.Fatalf("cc.WaitForStateChange(_, %s) = %t, want true", grpc.Connecting, ok)
}
if cc.State() != grpc.Ready {
t.Fatalf("cc.State() = %s, want %s", cc.State(), grpc.Ready)
}
if ok := cc.WaitForStateChange(time.Second, grpc.Ready); ok {
t.Fatalf("cc.WaitForStateChange(_, %s) = %t, want false", grpc.Ready, ok)
}
tc := testpb.NewTestServiceClient(cc)
var header metadata.MD
reply, err := tc.EmptyCall(context.Background(), &testpb.Empty{}, grpc.Header(&header))
if err != nil || !proto.Equal(&testpb.Empty{}, reply) {
t.Fatalf("TestService/EmptyCall(_, _) = %v, %v, want %v, ", reply, err, &testpb.Empty{})
}
if v, ok := header["ua"]; !ok || v[0] != testAppUA {
t.Fatalf("header[\"ua\"] = %q, %t, want %q, true", v, ok, testAppUA)
}
tearDown(s, cc)
if ok := cc.WaitForStateChange(5*time.Second, grpc.Ready); !ok {
t.Fatalf("cc.WaitForStateChange(_, %s) = %t, want true", grpc.Ready, ok)
}
if cc.State() != grpc.Shutdown {
t.Fatalf("cc.State() = %s, want %s", cc.State(), grpc.Shutdown)
}
}
func TestFailedEmptyUnary(t *testing.T) {
for _, e := range listTestEnv() {
testFailedEmptyUnary(t, e)
}
}
func testFailedEmptyUnary(t *testing.T, e env) {
s, cc := setUp(t, nil, math.MaxUint32, "", e)
tc := testpb.NewTestServiceClient(cc)
defer tearDown(s, cc)
ctx := metadata.NewContext(context.Background(), testMetadata)
if _, err := tc.EmptyCall(ctx, &testpb.Empty{}); err != grpc.Errorf(codes.DataLoss, "got extra metadata") {
t.Fatalf("TestService/EmptyCall(_, _) = _, %v, want _, %v", err, grpc.Errorf(codes.DataLoss, "got extra metadata"))
}
}
func TestLargeUnary(t *testing.T) {
for _, e := range listTestEnv() {
testLargeUnary(t, e)
}
}
func testLargeUnary(t *testing.T, e env) {
s, cc := setUp(t, nil, math.MaxUint32, "", e)
tc := testpb.NewTestServiceClient(cc)
defer tearDown(s, cc)
argSize := 271828
respSize := 314159
payload, err := newPayload(testpb.PayloadType_COMPRESSABLE, int32(argSize))
if err != nil {
t.Fatal(err)
}
req := &testpb.SimpleRequest{
ResponseType: testpb.PayloadType_COMPRESSABLE.Enum(),
ResponseSize: proto.Int32(int32(respSize)),
Payload: payload,
}
reply, err := tc.UnaryCall(context.Background(), req)
if err != nil {
t.Fatalf("TestService/UnaryCall(_, _) = _, %v, want _, ", err)
}
pt := reply.GetPayload().GetType()
ps := len(reply.GetPayload().GetBody())
if pt != testpb.PayloadType_COMPRESSABLE || ps != respSize {
t.Fatalf("Got the reply with type %d len %d; want %d, %d", pt, ps, testpb.PayloadType_COMPRESSABLE, respSize)
}
}
func TestMetadataUnaryRPC(t *testing.T) {
for _, e := range listTestEnv() {
testMetadataUnaryRPC(t, e)
}
}
func testMetadataUnaryRPC(t *testing.T, e env) {
s, cc := setUp(t, nil, math.MaxUint32, "", e)
tc := testpb.NewTestServiceClient(cc)
defer tearDown(s, cc)
argSize := 2718
respSize := 314
payload, err := newPayload(testpb.PayloadType_COMPRESSABLE, int32(argSize))
if err != nil {
t.Fatal(err)
}
req := &testpb.SimpleRequest{
ResponseType: testpb.PayloadType_COMPRESSABLE.Enum(),
ResponseSize: proto.Int32(int32(respSize)),
Payload: payload,
}
var header, trailer metadata.MD
ctx := metadata.NewContext(context.Background(), testMetadata)
if _, err := tc.UnaryCall(ctx, req, grpc.Header(&header), grpc.Trailer(&trailer)); err != nil {
t.Fatalf("TestService.UnaryCall(%v, _, _, _) = _, %v; want _, ", ctx, err)
}
if !reflect.DeepEqual(testMetadata, header) {
t.Fatalf("Received header metadata %v, want %v", header, testMetadata)
}
if !reflect.DeepEqual(testMetadata, trailer) {
t.Fatalf("Received trailer metadata %v, want %v", trailer, testMetadata)
}
}
func performOneRPC(t *testing.T, tc testpb.TestServiceClient, wg *sync.WaitGroup) {
argSize := 2718
respSize := 314
payload, err := newPayload(testpb.PayloadType_COMPRESSABLE, int32(argSize))
if err != nil {
t.Fatal(err)
}
req := &testpb.SimpleRequest{
ResponseType: testpb.PayloadType_COMPRESSABLE.Enum(),
ResponseSize: proto.Int32(int32(respSize)),
Payload: payload,
}
reply, err := tc.UnaryCall(context.Background(), req)
if err != nil {
t.Fatalf("TestService/UnaryCall(_, _) = _, %v, want _, ", err)
}
pt := reply.GetPayload().GetType()
ps := len(reply.GetPayload().GetBody())
if pt != testpb.PayloadType_COMPRESSABLE || ps != respSize {
t.Fatalf("Got the reply with type %d len %d; want %d, %d", pt, ps, testpb.PayloadType_COMPRESSABLE, respSize)
}
wg.Done()
}
func TestRetry(t *testing.T) {
for _, e := range listTestEnv() {
testRetry(t, e)
}
}
// This test mimics a user who sends 1000 RPCs concurrently on a faulty transport.
// TODO(zhaoq): Refactor to make this clearer and add more cases to test racy
// and error-prone paths.
func testRetry(t *testing.T, e env) {
s, cc := setUp(t, nil, math.MaxUint32, "", e)
tc := testpb.NewTestServiceClient(cc)
defer tearDown(s, cc)
var wg sync.WaitGroup
wg.Add(1)
go func() {
time.Sleep(1 * time.Second)
// The server shuts down the network connection to make a
// transport error which will be detected by the client side
// code.
s.TestingCloseConns()
wg.Done()
}()
// All these RPCs should succeed eventually.
for i := 0; i < 1000; i++ {
time.Sleep(2 * time.Millisecond)
wg.Add(1)
go performOneRPC(t, tc, &wg)
}
wg.Wait()
}
func TestRPCTimeout(t *testing.T) {
for _, e := range listTestEnv() {
testRPCTimeout(t, e)
}
}
// TODO(zhaoq): Have a better test coverage of timeout and cancellation mechanism.
func testRPCTimeout(t *testing.T, e env) {
s, cc := setUp(t, nil, math.MaxUint32, "", e)
tc := testpb.NewTestServiceClient(cc)
defer tearDown(s, cc)
argSize := 2718
respSize := 314
payload, err := newPayload(testpb.PayloadType_COMPRESSABLE, int32(argSize))
if err != nil {
t.Fatal(err)
}
req := &testpb.SimpleRequest{
ResponseType: testpb.PayloadType_COMPRESSABLE.Enum(),
ResponseSize: proto.Int32(int32(respSize)),
Payload: payload,
}
for i := -1; i <= 10; i++ {
ctx, _ := context.WithTimeout(context.Background(), time.Duration(i)*time.Millisecond)
reply, err := tc.UnaryCall(ctx, req)
if grpc.Code(err) != codes.DeadlineExceeded {
t.Fatalf(`TestService/UnaryCallv(_, _) = %v, %v; want , error code: %d`, reply, err, codes.DeadlineExceeded)
}
}
}
func TestCancel(t *testing.T) {
for _, e := range listTestEnv() {
testCancel(t, e)
}
}
func testCancel(t *testing.T, e env) {
s, cc := setUp(t, nil, math.MaxUint32, "", e)
tc := testpb.NewTestServiceClient(cc)
defer tearDown(s, cc)
argSize := 2718
respSize := 314
payload, err := newPayload(testpb.PayloadType_COMPRESSABLE, int32(argSize))
if err != nil {
t.Fatal(err)
}
req := &testpb.SimpleRequest{
ResponseType: testpb.PayloadType_COMPRESSABLE.Enum(),
ResponseSize: proto.Int32(int32(respSize)),
Payload: payload,
}
ctx, cancel := context.WithCancel(context.Background())
time.AfterFunc(1*time.Millisecond, cancel)
reply, err := tc.UnaryCall(ctx, req)
if grpc.Code(err) != codes.Canceled {
t.Fatalf(`TestService/UnaryCall(_, _) = %v, %v; want , error code: %d`, reply, err, codes.Canceled)
}
}
// The following tests the gRPC streaming RPC implementations.
// TODO(zhaoq): Have better coverage on error cases.
var (
reqSizes = []int{27182, 8, 1828, 45904}
respSizes = []int{31415, 9, 2653, 58979}
)
func TestPingPong(t *testing.T) {
for _, e := range listTestEnv() {
testPingPong(t, e)
}
}
func testPingPong(t *testing.T, e env) {
s, cc := setUp(t, nil, math.MaxUint32, "", e)
tc := testpb.NewTestServiceClient(cc)
defer tearDown(s, cc)
stream, err := tc.FullDuplexCall(context.Background())
if err != nil {
t.Fatalf("%v.FullDuplexCall(_) = _, %v, want ", tc, err)
}
var index int
for index < len(reqSizes) {
respParam := []*testpb.ResponseParameters{
{
Size: proto.Int32(int32(respSizes[index])),
},
}
payload, err := newPayload(testpb.PayloadType_COMPRESSABLE, int32(reqSizes[index]))
if err != nil {
t.Fatal(err)
}
req := &testpb.StreamingOutputCallRequest{
ResponseType: testpb.PayloadType_COMPRESSABLE.Enum(),
ResponseParameters: respParam,
Payload: payload,
}
if err := stream.Send(req); err != nil {
t.Fatalf("%v.Send(%v) = %v, want ", stream, req, err)
}
reply, err := stream.Recv()
if err != nil {
t.Fatalf("%v.Recv() = %v, want ", stream, err)
}
pt := reply.GetPayload().GetType()
if pt != testpb.PayloadType_COMPRESSABLE {
t.Fatalf("Got the reply of type %d, want %d", pt, testpb.PayloadType_COMPRESSABLE)
}
size := len(reply.GetPayload().GetBody())
if size != int(respSizes[index]) {
t.Fatalf("Got reply body of length %d, want %d", size, respSizes[index])
}
index++
}
if err := stream.CloseSend(); err != nil {
t.Fatalf("%v.CloseSend() got %v, want %v", stream, err, nil)
}
if _, err := stream.Recv(); err != io.EOF {
t.Fatalf("%v failed to complele the ping pong test: %v", stream, err)
}
}
func TestMetadataStreamingRPC(t *testing.T) {
for _, e := range listTestEnv() {
testMetadataStreamingRPC(t, e)
}
}
func testMetadataStreamingRPC(t *testing.T, e env) {
s, cc := setUp(t, nil, math.MaxUint32, "", e)
tc := testpb.NewTestServiceClient(cc)
defer tearDown(s, cc)
ctx := metadata.NewContext(context.Background(), testMetadata)
stream, err := tc.FullDuplexCall(ctx)
if err != nil {
t.Fatalf("%v.FullDuplexCall(_) = _, %v, want ", tc, err)
}
go func() {
headerMD, err := stream.Header()
if e.security == "tls" {
delete(headerMD, "transport_security_type")
}
if err != nil || !reflect.DeepEqual(testMetadata, headerMD) {
t.Errorf("#1 %v.Header() = %v, %v, want %v, ", stream, headerMD, err, testMetadata)
}
// test the cached value.
headerMD, err = stream.Header()
if err != nil || !reflect.DeepEqual(testMetadata, headerMD) {
t.Errorf("#2 %v.Header() = %v, %v, want %v, ", stream, headerMD, err, testMetadata)
}
var index int
for index < len(reqSizes) {
respParam := []*testpb.ResponseParameters{
{
Size: proto.Int32(int32(respSizes[index])),
},
}
payload, err := newPayload(testpb.PayloadType_COMPRESSABLE, int32(reqSizes[index]))
if err != nil {
t.Fatal(err)
}
req := &testpb.StreamingOutputCallRequest{
ResponseType: testpb.PayloadType_COMPRESSABLE.Enum(),
ResponseParameters: respParam,
Payload: payload,
}
if err := stream.Send(req); err != nil {
t.Errorf("%v.Send(%v) = %v, want ", stream, req, err)
return
}
index++
}
// Tell the server we're done sending args.
stream.CloseSend()
}()
for {
if _, err := stream.Recv(); err != nil {
break
}
}
trailerMD := stream.Trailer()
if !reflect.DeepEqual(testMetadata, trailerMD) {
t.Fatalf("%v.Trailer() = %v, want %v", stream, trailerMD, testMetadata)
}
}
func TestServerStreaming(t *testing.T) {
for _, e := range listTestEnv() {
testServerStreaming(t, e)
}
}
func testServerStreaming(t *testing.T, e env) {
s, cc := setUp(t, nil, math.MaxUint32, "", e)
tc := testpb.NewTestServiceClient(cc)
defer tearDown(s, cc)
respParam := make([]*testpb.ResponseParameters, len(respSizes))
for i, s := range respSizes {
respParam[i] = &testpb.ResponseParameters{
Size: proto.Int32(int32(s)),
}
}
req := &testpb.StreamingOutputCallRequest{
ResponseType: testpb.PayloadType_COMPRESSABLE.Enum(),
ResponseParameters: respParam,
}
stream, err := tc.StreamingOutputCall(context.Background(), req)
if err != nil {
t.Fatalf("%v.StreamingOutputCall(_) = _, %v, want ", tc, err)
}
var rpcStatus error
var respCnt int
var index int
for {
reply, err := stream.Recv()
if err != nil {
rpcStatus = err
break
}
pt := reply.GetPayload().GetType()
if pt != testpb.PayloadType_COMPRESSABLE {
t.Fatalf("Got the reply of type %d, want %d", pt, testpb.PayloadType_COMPRESSABLE)
}
size := len(reply.GetPayload().GetBody())
if size != int(respSizes[index]) {
t.Fatalf("Got reply body of length %d, want %d", size, respSizes[index])
}
index++
respCnt++
}
if rpcStatus != io.EOF {
t.Fatalf("Failed to finish the server streaming rpc: %v, want ", rpcStatus)
}
if respCnt != len(respSizes) {
t.Fatalf("Got %d reply, want %d", len(respSizes), respCnt)
}
}
func TestFailedServerStreaming(t *testing.T) {
for _, e := range listTestEnv() {
testFailedServerStreaming(t, e)
}
}
func testFailedServerStreaming(t *testing.T, e env) {
s, cc := setUp(t, nil, math.MaxUint32, "", e)
tc := testpb.NewTestServiceClient(cc)
defer tearDown(s, cc)
respParam := make([]*testpb.ResponseParameters, len(respSizes))
for i, s := range respSizes {
respParam[i] = &testpb.ResponseParameters{
Size: proto.Int32(int32(s)),
}
}
req := &testpb.StreamingOutputCallRequest{
ResponseType: testpb.PayloadType_COMPRESSABLE.Enum(),
ResponseParameters: respParam,
}
ctx := metadata.NewContext(context.Background(), testMetadata)
stream, err := tc.StreamingOutputCall(ctx, req)
if err != nil {
t.Fatalf("%v.StreamingOutputCall(_) = _, %v, want ", tc, err)
}
if _, err := stream.Recv(); err != grpc.Errorf(codes.DataLoss, "got extra metadata") {
t.Fatalf("%v.Recv() = _, %v, want _, %v", stream, err, grpc.Errorf(codes.DataLoss, "got extra metadata"))
}
}
func TestClientStreaming(t *testing.T) {
for _, e := range listTestEnv() {
testClientStreaming(t, e)
}
}
func testClientStreaming(t *testing.T, e env) {
s, cc := setUp(t, nil, math.MaxUint32, "", e)
tc := testpb.NewTestServiceClient(cc)
defer tearDown(s, cc)
stream, err := tc.StreamingInputCall(context.Background())
if err != nil {
t.Fatalf("%v.StreamingInputCall(_) = _, %v, want ", tc, err)
}
var sum int
for _, s := range reqSizes {
payload, err := newPayload(testpb.PayloadType_COMPRESSABLE, int32(s))
if err != nil {
t.Fatal(err)
}
req := &testpb.StreamingInputCallRequest{
Payload: payload,
}
if err := stream.Send(req); err != nil {
t.Fatalf("%v.Send(%v) = %v, want ", stream, req, err)
}
sum += s
}
reply, err := stream.CloseAndRecv()
if err != nil {
t.Fatalf("%v.CloseAndRecv() got error %v, want %v", stream, err, nil)
}
if reply.GetAggregatedPayloadSize() != int32(sum) {
t.Fatalf("%v.CloseAndRecv().GetAggregatePayloadSize() = %v; want %v", stream, reply.GetAggregatedPayloadSize(), sum)
}
}
func TestExceedMaxStreamsLimit(t *testing.T) {
for _, e := range listTestEnv() {
testExceedMaxStreamsLimit(t, e)
}
}
func testExceedMaxStreamsLimit(t *testing.T, e env) {
// Only allows 1 live stream per server transport.
s, cc := setUp(t, nil, 1, "", e)
tc := testpb.NewTestServiceClient(cc)
defer tearDown(s, cc)
done := make(chan struct{})
ch := make(chan int)
go func() {
for {
select {
case <-time.After(5 * time.Millisecond):
ch <- 0
case <-time.After(5 * time.Second):
close(done)
return
}
}
}()
// Loop until a stream creation hangs due to the new max stream setting.
for {
select {
case <-ch:
ctx, _ := context.WithTimeout(context.Background(), time.Second)
if _, err := tc.StreamingInputCall(ctx); err != nil {
if grpc.Code(err) == codes.DeadlineExceeded {
return
}
t.Fatalf("%v.StreamingInputCall(_) = %v, want ", tc, err)
}
case <-done:
t.Fatalf("Client has not received the max stream setting in 5 seconds.")
}
}
}
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/test/grpc_testing/ 0000775 0000000 0000000 00000000000 12603607541 0023156 5 ustar 00root root 0000000 0000000 grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/test/grpc_testing/test.pb.go 0000664 0000000 0000000 00000053553 12603607541 0025077 0 ustar 00root root 0000000 0000000 // Code generated by protoc-gen-go.
// source: test.proto
// DO NOT EDIT!
/*
Package grpc_testing is a generated protocol buffer package.
It is generated from these files:
test.proto
It has these top-level messages:
Empty
Payload
SimpleRequest
SimpleResponse
StreamingInputCallRequest
StreamingInputCallResponse
ResponseParameters
StreamingOutputCallRequest
StreamingOutputCallResponse
*/
package grpc_testing
import proto "github.com/golang/protobuf/proto"
import math "math"
import (
context "golang.org/x/net/context"
grpc "google.golang.org/grpc"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConn
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = math.Inf
// The type of payload that should be returned.
type PayloadType int32
const (
// Compressable text format.
PayloadType_COMPRESSABLE PayloadType = 0
// Uncompressable binary format.
PayloadType_UNCOMPRESSABLE PayloadType = 1
// Randomly chosen from all other formats defined in this enum.
PayloadType_RANDOM PayloadType = 2
)
var PayloadType_name = map[int32]string{
0: "COMPRESSABLE",
1: "UNCOMPRESSABLE",
2: "RANDOM",
}
var PayloadType_value = map[string]int32{
"COMPRESSABLE": 0,
"UNCOMPRESSABLE": 1,
"RANDOM": 2,
}
func (x PayloadType) Enum() *PayloadType {
p := new(PayloadType)
*p = x
return p
}
func (x PayloadType) String() string {
return proto.EnumName(PayloadType_name, int32(x))
}
func (x *PayloadType) UnmarshalJSON(data []byte) error {
value, err := proto.UnmarshalJSONEnum(PayloadType_value, data, "PayloadType")
if err != nil {
return err
}
*x = PayloadType(value)
return nil
}
type Empty struct {
XXX_unrecognized []byte `json:"-"`
}
func (m *Empty) Reset() { *m = Empty{} }
func (m *Empty) String() string { return proto.CompactTextString(m) }
func (*Empty) ProtoMessage() {}
// A block of data, to simply increase gRPC message size.
type Payload struct {
// The type of data in body.
Type *PayloadType `protobuf:"varint,1,opt,name=type,enum=grpc.testing.PayloadType" json:"type,omitempty"`
// Primary contents of payload.
Body []byte `protobuf:"bytes,2,opt,name=body" json:"body,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *Payload) Reset() { *m = Payload{} }
func (m *Payload) String() string { return proto.CompactTextString(m) }
func (*Payload) ProtoMessage() {}
func (m *Payload) GetType() PayloadType {
if m != nil && m.Type != nil {
return *m.Type
}
return PayloadType_COMPRESSABLE
}
func (m *Payload) GetBody() []byte {
if m != nil {
return m.Body
}
return nil
}
// Unary request.
type SimpleRequest struct {
// Desired payload type in the response from the server.
// If response_type is RANDOM, server randomly chooses one from other formats.
ResponseType *PayloadType `protobuf:"varint,1,opt,name=response_type,enum=grpc.testing.PayloadType" json:"response_type,omitempty"`
// Desired payload size in the response from the server.
// If response_type is COMPRESSABLE, this denotes the size before compression.
ResponseSize *int32 `protobuf:"varint,2,opt,name=response_size" json:"response_size,omitempty"`
// Optional input payload sent along with the request.
Payload *Payload `protobuf:"bytes,3,opt,name=payload" json:"payload,omitempty"`
// Whether SimpleResponse should include username.
FillUsername *bool `protobuf:"varint,4,opt,name=fill_username" json:"fill_username,omitempty"`
// Whether SimpleResponse should include OAuth scope.
FillOauthScope *bool `protobuf:"varint,5,opt,name=fill_oauth_scope" json:"fill_oauth_scope,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *SimpleRequest) Reset() { *m = SimpleRequest{} }
func (m *SimpleRequest) String() string { return proto.CompactTextString(m) }
func (*SimpleRequest) ProtoMessage() {}
func (m *SimpleRequest) GetResponseType() PayloadType {
if m != nil && m.ResponseType != nil {
return *m.ResponseType
}
return PayloadType_COMPRESSABLE
}
func (m *SimpleRequest) GetResponseSize() int32 {
if m != nil && m.ResponseSize != nil {
return *m.ResponseSize
}
return 0
}
func (m *SimpleRequest) GetPayload() *Payload {
if m != nil {
return m.Payload
}
return nil
}
func (m *SimpleRequest) GetFillUsername() bool {
if m != nil && m.FillUsername != nil {
return *m.FillUsername
}
return false
}
func (m *SimpleRequest) GetFillOauthScope() bool {
if m != nil && m.FillOauthScope != nil {
return *m.FillOauthScope
}
return false
}
// Unary response, as configured by the request.
type SimpleResponse struct {
// Payload to increase message size.
Payload *Payload `protobuf:"bytes,1,opt,name=payload" json:"payload,omitempty"`
// The user the request came from, for verifying authentication was
// successful when the client expected it.
Username *string `protobuf:"bytes,2,opt,name=username" json:"username,omitempty"`
// OAuth scope.
OauthScope *string `protobuf:"bytes,3,opt,name=oauth_scope" json:"oauth_scope,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *SimpleResponse) Reset() { *m = SimpleResponse{} }
func (m *SimpleResponse) String() string { return proto.CompactTextString(m) }
func (*SimpleResponse) ProtoMessage() {}
func (m *SimpleResponse) GetPayload() *Payload {
if m != nil {
return m.Payload
}
return nil
}
func (m *SimpleResponse) GetUsername() string {
if m != nil && m.Username != nil {
return *m.Username
}
return ""
}
func (m *SimpleResponse) GetOauthScope() string {
if m != nil && m.OauthScope != nil {
return *m.OauthScope
}
return ""
}
// Client-streaming request.
type StreamingInputCallRequest struct {
// Optional input payload sent along with the request.
Payload *Payload `protobuf:"bytes,1,opt,name=payload" json:"payload,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *StreamingInputCallRequest) Reset() { *m = StreamingInputCallRequest{} }
func (m *StreamingInputCallRequest) String() string { return proto.CompactTextString(m) }
func (*StreamingInputCallRequest) ProtoMessage() {}
func (m *StreamingInputCallRequest) GetPayload() *Payload {
if m != nil {
return m.Payload
}
return nil
}
// Client-streaming response.
type StreamingInputCallResponse struct {
// Aggregated size of payloads received from the client.
AggregatedPayloadSize *int32 `protobuf:"varint,1,opt,name=aggregated_payload_size" json:"aggregated_payload_size,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *StreamingInputCallResponse) Reset() { *m = StreamingInputCallResponse{} }
func (m *StreamingInputCallResponse) String() string { return proto.CompactTextString(m) }
func (*StreamingInputCallResponse) ProtoMessage() {}
func (m *StreamingInputCallResponse) GetAggregatedPayloadSize() int32 {
if m != nil && m.AggregatedPayloadSize != nil {
return *m.AggregatedPayloadSize
}
return 0
}
// Configuration for a particular response.
type ResponseParameters struct {
// Desired payload sizes in responses from the server.
// If response_type is COMPRESSABLE, this denotes the size before compression.
Size *int32 `protobuf:"varint,1,opt,name=size" json:"size,omitempty"`
// Desired interval between consecutive responses in the response stream in
// microseconds.
IntervalUs *int32 `protobuf:"varint,2,opt,name=interval_us" json:"interval_us,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *ResponseParameters) Reset() { *m = ResponseParameters{} }
func (m *ResponseParameters) String() string { return proto.CompactTextString(m) }
func (*ResponseParameters) ProtoMessage() {}
func (m *ResponseParameters) GetSize() int32 {
if m != nil && m.Size != nil {
return *m.Size
}
return 0
}
func (m *ResponseParameters) GetIntervalUs() int32 {
if m != nil && m.IntervalUs != nil {
return *m.IntervalUs
}
return 0
}
// Server-streaming request.
type StreamingOutputCallRequest struct {
// Desired payload type in the response from the server.
// If response_type is RANDOM, the payload from each response in the stream
// might be of different types. This is to simulate a mixed type of payload
// stream.
ResponseType *PayloadType `protobuf:"varint,1,opt,name=response_type,enum=grpc.testing.PayloadType" json:"response_type,omitempty"`
// Configuration for each expected response message.
ResponseParameters []*ResponseParameters `protobuf:"bytes,2,rep,name=response_parameters" json:"response_parameters,omitempty"`
// Optional input payload sent along with the request.
Payload *Payload `protobuf:"bytes,3,opt,name=payload" json:"payload,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *StreamingOutputCallRequest) Reset() { *m = StreamingOutputCallRequest{} }
func (m *StreamingOutputCallRequest) String() string { return proto.CompactTextString(m) }
func (*StreamingOutputCallRequest) ProtoMessage() {}
func (m *StreamingOutputCallRequest) GetResponseType() PayloadType {
if m != nil && m.ResponseType != nil {
return *m.ResponseType
}
return PayloadType_COMPRESSABLE
}
func (m *StreamingOutputCallRequest) GetResponseParameters() []*ResponseParameters {
if m != nil {
return m.ResponseParameters
}
return nil
}
func (m *StreamingOutputCallRequest) GetPayload() *Payload {
if m != nil {
return m.Payload
}
return nil
}
// Server-streaming response, as configured by the request and parameters.
type StreamingOutputCallResponse struct {
// Payload to increase response size.
Payload *Payload `protobuf:"bytes,1,opt,name=payload" json:"payload,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *StreamingOutputCallResponse) Reset() { *m = StreamingOutputCallResponse{} }
func (m *StreamingOutputCallResponse) String() string { return proto.CompactTextString(m) }
func (*StreamingOutputCallResponse) ProtoMessage() {}
func (m *StreamingOutputCallResponse) GetPayload() *Payload {
if m != nil {
return m.Payload
}
return nil
}
func init() {
proto.RegisterEnum("grpc.testing.PayloadType", PayloadType_name, PayloadType_value)
}
// Client API for TestService service
type TestServiceClient interface {
// One empty request followed by one empty response.
EmptyCall(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error)
// One request followed by one response.
// The server returns the client payload as-is.
UnaryCall(ctx context.Context, in *SimpleRequest, opts ...grpc.CallOption) (*SimpleResponse, error)
// One request followed by a sequence of responses (streamed download).
// The server returns the payload with client desired type and sizes.
StreamingOutputCall(ctx context.Context, in *StreamingOutputCallRequest, opts ...grpc.CallOption) (TestService_StreamingOutputCallClient, error)
// A sequence of requests followed by one response (streamed upload).
// The server returns the aggregated size of client payload as the result.
StreamingInputCall(ctx context.Context, opts ...grpc.CallOption) (TestService_StreamingInputCallClient, error)
// A sequence of requests with each request served by the server immediately.
// As one request could lead to multiple responses, this interface
// demonstrates the idea of full duplexing.
FullDuplexCall(ctx context.Context, opts ...grpc.CallOption) (TestService_FullDuplexCallClient, error)
// A sequence of requests followed by a sequence of responses.
// The server buffers all the client requests and then serves them in order. A
// stream of responses are returned to the client when the server starts with
// first request.
HalfDuplexCall(ctx context.Context, opts ...grpc.CallOption) (TestService_HalfDuplexCallClient, error)
}
type testServiceClient struct {
cc *grpc.ClientConn
}
func NewTestServiceClient(cc *grpc.ClientConn) TestServiceClient {
return &testServiceClient{cc}
}
func (c *testServiceClient) EmptyCall(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error) {
out := new(Empty)
err := grpc.Invoke(ctx, "/grpc.testing.TestService/EmptyCall", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *testServiceClient) UnaryCall(ctx context.Context, in *SimpleRequest, opts ...grpc.CallOption) (*SimpleResponse, error) {
out := new(SimpleResponse)
err := grpc.Invoke(ctx, "/grpc.testing.TestService/UnaryCall", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *testServiceClient) StreamingOutputCall(ctx context.Context, in *StreamingOutputCallRequest, opts ...grpc.CallOption) (TestService_StreamingOutputCallClient, error) {
stream, err := grpc.NewClientStream(ctx, &_TestService_serviceDesc.Streams[0], c.cc, "/grpc.testing.TestService/StreamingOutputCall", opts...)
if err != nil {
return nil, err
}
x := &testServiceStreamingOutputCallClient{stream}
if err := x.ClientStream.SendMsg(in); err != nil {
return nil, err
}
if err := x.ClientStream.CloseSend(); err != nil {
return nil, err
}
return x, nil
}
type TestService_StreamingOutputCallClient interface {
Recv() (*StreamingOutputCallResponse, error)
grpc.ClientStream
}
type testServiceStreamingOutputCallClient struct {
grpc.ClientStream
}
func (x *testServiceStreamingOutputCallClient) Recv() (*StreamingOutputCallResponse, error) {
m := new(StreamingOutputCallResponse)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
func (c *testServiceClient) StreamingInputCall(ctx context.Context, opts ...grpc.CallOption) (TestService_StreamingInputCallClient, error) {
stream, err := grpc.NewClientStream(ctx, &_TestService_serviceDesc.Streams[1], c.cc, "/grpc.testing.TestService/StreamingInputCall", opts...)
if err != nil {
return nil, err
}
x := &testServiceStreamingInputCallClient{stream}
return x, nil
}
type TestService_StreamingInputCallClient interface {
Send(*StreamingInputCallRequest) error
CloseAndRecv() (*StreamingInputCallResponse, error)
grpc.ClientStream
}
type testServiceStreamingInputCallClient struct {
grpc.ClientStream
}
func (x *testServiceStreamingInputCallClient) Send(m *StreamingInputCallRequest) error {
return x.ClientStream.SendMsg(m)
}
func (x *testServiceStreamingInputCallClient) CloseAndRecv() (*StreamingInputCallResponse, error) {
if err := x.ClientStream.CloseSend(); err != nil {
return nil, err
}
m := new(StreamingInputCallResponse)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
func (c *testServiceClient) FullDuplexCall(ctx context.Context, opts ...grpc.CallOption) (TestService_FullDuplexCallClient, error) {
stream, err := grpc.NewClientStream(ctx, &_TestService_serviceDesc.Streams[2], c.cc, "/grpc.testing.TestService/FullDuplexCall", opts...)
if err != nil {
return nil, err
}
x := &testServiceFullDuplexCallClient{stream}
return x, nil
}
type TestService_FullDuplexCallClient interface {
Send(*StreamingOutputCallRequest) error
Recv() (*StreamingOutputCallResponse, error)
grpc.ClientStream
}
type testServiceFullDuplexCallClient struct {
grpc.ClientStream
}
func (x *testServiceFullDuplexCallClient) Send(m *StreamingOutputCallRequest) error {
return x.ClientStream.SendMsg(m)
}
func (x *testServiceFullDuplexCallClient) Recv() (*StreamingOutputCallResponse, error) {
m := new(StreamingOutputCallResponse)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
func (c *testServiceClient) HalfDuplexCall(ctx context.Context, opts ...grpc.CallOption) (TestService_HalfDuplexCallClient, error) {
stream, err := grpc.NewClientStream(ctx, &_TestService_serviceDesc.Streams[3], c.cc, "/grpc.testing.TestService/HalfDuplexCall", opts...)
if err != nil {
return nil, err
}
x := &testServiceHalfDuplexCallClient{stream}
return x, nil
}
type TestService_HalfDuplexCallClient interface {
Send(*StreamingOutputCallRequest) error
Recv() (*StreamingOutputCallResponse, error)
grpc.ClientStream
}
type testServiceHalfDuplexCallClient struct {
grpc.ClientStream
}
func (x *testServiceHalfDuplexCallClient) Send(m *StreamingOutputCallRequest) error {
return x.ClientStream.SendMsg(m)
}
func (x *testServiceHalfDuplexCallClient) Recv() (*StreamingOutputCallResponse, error) {
m := new(StreamingOutputCallResponse)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
// Server API for TestService service
type TestServiceServer interface {
// One empty request followed by one empty response.
EmptyCall(context.Context, *Empty) (*Empty, error)
// One request followed by one response.
// The server returns the client payload as-is.
UnaryCall(context.Context, *SimpleRequest) (*SimpleResponse, error)
// One request followed by a sequence of responses (streamed download).
// The server returns the payload with client desired type and sizes.
StreamingOutputCall(*StreamingOutputCallRequest, TestService_StreamingOutputCallServer) error
// A sequence of requests followed by one response (streamed upload).
// The server returns the aggregated size of client payload as the result.
StreamingInputCall(TestService_StreamingInputCallServer) error
// A sequence of requests with each request served by the server immediately.
// As one request could lead to multiple responses, this interface
// demonstrates the idea of full duplexing.
FullDuplexCall(TestService_FullDuplexCallServer) error
// A sequence of requests followed by a sequence of responses.
// The server buffers all the client requests and then serves them in order. A
// stream of responses are returned to the client when the server starts with
// first request.
HalfDuplexCall(TestService_HalfDuplexCallServer) error
}
func RegisterTestServiceServer(s *grpc.Server, srv TestServiceServer) {
s.RegisterService(&_TestService_serviceDesc, srv)
}
func _TestService_EmptyCall_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error) (interface{}, error) {
in := new(Empty)
if err := dec(in); err != nil {
return nil, err
}
out, err := srv.(TestServiceServer).EmptyCall(ctx, in)
if err != nil {
return nil, err
}
return out, nil
}
func _TestService_UnaryCall_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error) (interface{}, error) {
in := new(SimpleRequest)
if err := dec(in); err != nil {
return nil, err
}
out, err := srv.(TestServiceServer).UnaryCall(ctx, in)
if err != nil {
return nil, err
}
return out, nil
}
func _TestService_StreamingOutputCall_Handler(srv interface{}, stream grpc.ServerStream) error {
m := new(StreamingOutputCallRequest)
if err := stream.RecvMsg(m); err != nil {
return err
}
return srv.(TestServiceServer).StreamingOutputCall(m, &testServiceStreamingOutputCallServer{stream})
}
type TestService_StreamingOutputCallServer interface {
Send(*StreamingOutputCallResponse) error
grpc.ServerStream
}
type testServiceStreamingOutputCallServer struct {
grpc.ServerStream
}
func (x *testServiceStreamingOutputCallServer) Send(m *StreamingOutputCallResponse) error {
return x.ServerStream.SendMsg(m)
}
func _TestService_StreamingInputCall_Handler(srv interface{}, stream grpc.ServerStream) error {
return srv.(TestServiceServer).StreamingInputCall(&testServiceStreamingInputCallServer{stream})
}
type TestService_StreamingInputCallServer interface {
SendAndClose(*StreamingInputCallResponse) error
Recv() (*StreamingInputCallRequest, error)
grpc.ServerStream
}
type testServiceStreamingInputCallServer struct {
grpc.ServerStream
}
func (x *testServiceStreamingInputCallServer) SendAndClose(m *StreamingInputCallResponse) error {
return x.ServerStream.SendMsg(m)
}
func (x *testServiceStreamingInputCallServer) Recv() (*StreamingInputCallRequest, error) {
m := new(StreamingInputCallRequest)
if err := x.ServerStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
func _TestService_FullDuplexCall_Handler(srv interface{}, stream grpc.ServerStream) error {
return srv.(TestServiceServer).FullDuplexCall(&testServiceFullDuplexCallServer{stream})
}
type TestService_FullDuplexCallServer interface {
Send(*StreamingOutputCallResponse) error
Recv() (*StreamingOutputCallRequest, error)
grpc.ServerStream
}
type testServiceFullDuplexCallServer struct {
grpc.ServerStream
}
func (x *testServiceFullDuplexCallServer) Send(m *StreamingOutputCallResponse) error {
return x.ServerStream.SendMsg(m)
}
func (x *testServiceFullDuplexCallServer) Recv() (*StreamingOutputCallRequest, error) {
m := new(StreamingOutputCallRequest)
if err := x.ServerStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
func _TestService_HalfDuplexCall_Handler(srv interface{}, stream grpc.ServerStream) error {
return srv.(TestServiceServer).HalfDuplexCall(&testServiceHalfDuplexCallServer{stream})
}
type TestService_HalfDuplexCallServer interface {
Send(*StreamingOutputCallResponse) error
Recv() (*StreamingOutputCallRequest, error)
grpc.ServerStream
}
type testServiceHalfDuplexCallServer struct {
grpc.ServerStream
}
func (x *testServiceHalfDuplexCallServer) Send(m *StreamingOutputCallResponse) error {
return x.ServerStream.SendMsg(m)
}
func (x *testServiceHalfDuplexCallServer) Recv() (*StreamingOutputCallRequest, error) {
m := new(StreamingOutputCallRequest)
if err := x.ServerStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
var _TestService_serviceDesc = grpc.ServiceDesc{
ServiceName: "grpc.testing.TestService",
HandlerType: (*TestServiceServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "EmptyCall",
Handler: _TestService_EmptyCall_Handler,
},
{
MethodName: "UnaryCall",
Handler: _TestService_UnaryCall_Handler,
},
},
Streams: []grpc.StreamDesc{
{
StreamName: "StreamingOutputCall",
Handler: _TestService_StreamingOutputCall_Handler,
ServerStreams: true,
},
{
StreamName: "StreamingInputCall",
Handler: _TestService_StreamingInputCall_Handler,
ClientStreams: true,
},
{
StreamName: "FullDuplexCall",
Handler: _TestService_FullDuplexCall_Handler,
ServerStreams: true,
ClientStreams: true,
},
{
StreamName: "HalfDuplexCall",
Handler: _TestService_HalfDuplexCall_Handler,
ServerStreams: true,
ClientStreams: true,
},
},
}
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/test/grpc_testing/test.proto 0000664 0000000 0000000 00000011155 12603607541 0025225 0 ustar 00root root 0000000 0000000 // An integration test service that covers all the method signature permutations
// of unary/streaming requests/responses.
syntax = "proto2";
package grpc.testing;
message Empty {}
// The type of payload that should be returned.
enum PayloadType {
// Compressable text format.
COMPRESSABLE = 0;
// Uncompressable binary format.
UNCOMPRESSABLE = 1;
// Randomly chosen from all other formats defined in this enum.
RANDOM = 2;
}
// A block of data, to simply increase gRPC message size.
message Payload {
// The type of data in body.
optional PayloadType type = 1;
// Primary contents of payload.
optional bytes body = 2;
}
// Unary request.
message SimpleRequest {
// Desired payload type in the response from the server.
// If response_type is RANDOM, server randomly chooses one from other formats.
optional PayloadType response_type = 1;
// Desired payload size in the response from the server.
// If response_type is COMPRESSABLE, this denotes the size before compression.
optional int32 response_size = 2;
// Optional input payload sent along with the request.
optional Payload payload = 3;
// Whether SimpleResponse should include username.
optional bool fill_username = 4;
// Whether SimpleResponse should include OAuth scope.
optional bool fill_oauth_scope = 5;
}
// Unary response, as configured by the request.
message SimpleResponse {
// Payload to increase message size.
optional Payload payload = 1;
// The user the request came from, for verifying authentication was
// successful when the client expected it.
optional string username = 2;
// OAuth scope.
optional string oauth_scope = 3;
}
// Client-streaming request.
message StreamingInputCallRequest {
// Optional input payload sent along with the request.
optional Payload payload = 1;
// Not expecting any payload from the response.
}
// Client-streaming response.
message StreamingInputCallResponse {
// Aggregated size of payloads received from the client.
optional int32 aggregated_payload_size = 1;
}
// Configuration for a particular response.
message ResponseParameters {
// Desired payload sizes in responses from the server.
// If response_type is COMPRESSABLE, this denotes the size before compression.
optional int32 size = 1;
// Desired interval between consecutive responses in the response stream in
// microseconds.
optional int32 interval_us = 2;
}
// Server-streaming request.
message StreamingOutputCallRequest {
// Desired payload type in the response from the server.
// If response_type is RANDOM, the payload from each response in the stream
// might be of different types. This is to simulate a mixed type of payload
// stream.
optional PayloadType response_type = 1;
// Configuration for each expected response message.
repeated ResponseParameters response_parameters = 2;
// Optional input payload sent along with the request.
optional Payload payload = 3;
}
// Server-streaming response, as configured by the request and parameters.
message StreamingOutputCallResponse {
// Payload to increase response size.
optional Payload payload = 1;
}
// A simple service to test the various types of RPCs and experiment with
// performance with various types of payload.
service TestService {
// One empty request followed by one empty response.
rpc EmptyCall(Empty) returns (Empty);
// One request followed by one response.
// The server returns the client payload as-is.
rpc UnaryCall(SimpleRequest) returns (SimpleResponse);
// One request followed by a sequence of responses (streamed download).
// The server returns the payload with client desired type and sizes.
rpc StreamingOutputCall(StreamingOutputCallRequest)
returns (stream StreamingOutputCallResponse);
// A sequence of requests followed by one response (streamed upload).
// The server returns the aggregated size of client payload as the result.
rpc StreamingInputCall(stream StreamingInputCallRequest)
returns (StreamingInputCallResponse);
// A sequence of requests with each request served by the server immediately.
// As one request could lead to multiple responses, this interface
// demonstrates the idea of full duplexing.
rpc FullDuplexCall(stream StreamingOutputCallRequest)
returns (stream StreamingOutputCallResponse);
// A sequence of requests followed by a sequence of responses.
// The server buffers all the client requests and then serves them in order. A
// stream of responses are returned to the client when the server starts with
// first request.
rpc HalfDuplexCall(stream StreamingOutputCallRequest)
returns (stream StreamingOutputCallResponse);
}
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/test/testdata/ 0000775 0000000 0000000 00000000000 12603607541 0022277 5 ustar 00root root 0000000 0000000 grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/test/testdata/ca.pem 0000664 0000000 0000000 00000001442 12603607541 0023366 0 ustar 00root root 0000000 0000000 -----BEGIN CERTIFICATE-----
MIICIzCCAYwCCQCFTbF7XNSvvjANBgkqhkiG9w0BAQUFADBWMQswCQYDVQQGEwJB
VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0
cyBQdHkgTHRkMQ8wDQYDVQQDDAZ0ZXN0Y2EwHhcNMTQwNzE3MjMxNzUxWhcNMjQw
NzE0MjMxNzUxWjBWMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEh
MB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMQ8wDQYDVQQDDAZ0ZXN0
Y2EwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMBA3wVeTGHZR1Rye/i+J8a2
cu5gXwFV6TnObzGM7bLFCO5i9v4mLo4iFzPsHmWDUxKS3Y8iXbu0eYBlLoNY0lSv
xDx33O+DuwMmVN+DzSD+Eod9zfvwOWHsazYCZT2PhNxnVWIuJXViY4JAHUGodjx+
QAi6yCAurUZGvYXGgZSBAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAQoQVD8bwdtWJ
AniGBwcCfqYyH+/KpA10AcebJVVTyYbYvI9Q8d6RSVu4PZy9OALHR/QrWBdYTAyz
fNAmc2cmdkSRJzjhIaOstnQy1J+Fk0T9XyvQtq499yFbq9xogUVlEGH62xP6vH0Y
5ukK//dCPAzA11YuX2rnex0JhuTQfcI=
-----END CERTIFICATE-----
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/test/testdata/server1.key 0000664 0000000 0000000 00000001567 12603607541 0024411 0 ustar 00root root 0000000 0000000 -----BEGIN RSA PRIVATE KEY-----
MIICWwIBAAKBgQDhwxUnKCwlSaWAwzOB2LSHVegJHv7DDWminTg4wzLLsf+LQ8nZ
bpjfn5vgIzxCuRh4Rp9QYM5FhfrJX9wcYawP/HTbJ7p7LVQO2QYAP+akMTHxgKuM
BzVV++3wWToKfVZUjFX8nfTfGMGwWAHJDnlEGnU4tl9UujoCV4ENJtzFoQIDAQAB
AoGAJ+6hpzNr24yTQZtFWQpDpEyFplddKJMOxDya3S9ppK3vTWrIITV2xNcucw7I
ceTbdyrGsyjsU0/HdCcIf9ym2jfmGLUwmyhltKVw0QYcFB0XLkc0nI5YvEYoeVDg
omZIXn1E3EW+sSIWSbkMu9bY2kstKXR2UZmMgWDtmBEPMaECQQD6yT4TAZM5hGBb
ciBKgMUP6PwOhPhOMPIvijO50Aiu6iuCV88l1QIy38gWVhxjNrq6P346j4IBg+kB
9alwpCODAkEA5nSnm9k6ykYeQWNS0fNWiRinCdl23A7usDGSuKKlm019iomJ/Rgd
MKDOp0q/2OostbteOWM2MRFf4jMH3wyVCwJAfAdjJ8szoNKTRSagSbh9vWygnB2v
IByc6l4TTuZQJRGzCveafz9lovuB3WohCABdQRd9ukCXL2CpsEpqzkafOQJAJUjc
USedDlq3zGZwYM1Yw8d8RuirBUFZNqJelYai+nRYClDkRVFgb5yksoYycbq5TxGo
VeqKOvgPpj4RWPHlLwJAGUMk3bqT91xBUCnLRs/vfoCpHpg6eywQTBDAV6xkyz4a
RH3I7/+yj3ZxR2JoWHgUwZ7lZk1VnhffFye7SBXyag==
-----END RSA PRIVATE KEY-----
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/test/testdata/server1.pem 0000664 0000000 0000000 00000001704 12603607541 0024373 0 ustar 00root root 0000000 0000000 -----BEGIN CERTIFICATE-----
MIICmzCCAgSgAwIBAgIBAzANBgkqhkiG9w0BAQUFADBWMQswCQYDVQQGEwJBVTET
MBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQ
dHkgTHRkMQ8wDQYDVQQDDAZ0ZXN0Y2EwHhcNMTQwNzIyMDYwMDU3WhcNMjQwNzE5
MDYwMDU3WjBkMQswCQYDVQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNV
BAcTB0NoaWNhZ28xFDASBgNVBAoTC0dvb2dsZSBJbmMuMRowGAYDVQQDFBEqLnRl
c3QuZ29vZ2xlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA4cMVJygs
JUmlgMMzgdi0h1XoCR7+ww1pop04OMMyy7H/i0PJ2W6Y35+b4CM8QrkYeEafUGDO
RYX6yV/cHGGsD/x02ye6ey1UDtkGAD/mpDEx8YCrjAc1Vfvt8Fk6Cn1WVIxV/J30
3xjBsFgByQ55RBp1OLZfVLo6AleBDSbcxaECAwEAAaNrMGkwCQYDVR0TBAIwADAL
BgNVHQ8EBAMCBeAwTwYDVR0RBEgwRoIQKi50ZXN0Lmdvb2dsZS5mcoIYd2F0ZXJ6
b29pLnRlc3QuZ29vZ2xlLmJlghIqLnRlc3QueW91dHViZS5jb22HBMCoAQMwDQYJ
KoZIhvcNAQEFBQADgYEAM2Ii0LgTGbJ1j4oqX9bxVcxm+/R5Yf8oi0aZqTJlnLYS
wXcBykxTx181s7WyfJ49WwrYXo78zTDAnf1ma0fPq3e4mpspvyndLh1a+OarHa1e
aT0DIIYk7qeEa1YcVljx2KyLd0r1BBAfrwyGaEPVeJQVYWaOJRU2we/KD4ojf9s=
-----END CERTIFICATE-----
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/trace.go 0000664 0000000 0000000 00000006144 12603607541 0021141 0 ustar 00root root 0000000 0000000 /*
*
* Copyright 2015, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
package grpc
import (
"bytes"
"fmt"
"io"
"net"
"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 = true
// 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()
}
// 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 fmt.Sprintf("sent: %v", p.msg)
} else {
return fmt.Sprintf("recv: %v", p.msg)
}
}
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) }
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/transport/ 0000775 0000000 0000000 00000000000 12603607541 0021543 5 ustar 00root root 0000000 0000000 grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/transport/control.go 0000664 0000000 0000000 00000014256 12603607541 0023562 0 ustar 00root root 0000000 0000000 /*
*
* Copyright 2014, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
package transport
import (
"fmt"
"sync"
"golang.org/x/net/http2"
)
const (
// The default value of flow control window size in HTTP2 spec.
defaultWindowSize = 65535
// The initial window size for flow control.
initialWindowSize = defaultWindowSize // for an RPC
initialConnWindowSize = defaultWindowSize * 16 // for a connection
)
// The following defines various control items which could flow through
// the control buffer of transport. They represent different aspects of
// control tasks, e.g., flow control, settings, streaming resetting, etc.
type windowUpdate struct {
streamID uint32
increment uint32
}
func (windowUpdate) isItem() bool {
return true
}
type settings struct {
ack bool
ss []http2.Setting
}
func (settings) isItem() bool {
return true
}
type resetStream struct {
streamID uint32
code http2.ErrCode
}
func (resetStream) isItem() bool {
return true
}
type flushIO struct {
}
func (flushIO) isItem() bool {
return true
}
type ping struct {
ack bool
}
func (ping) isItem() bool {
return true
}
// quotaPool is a pool which accumulates the quota and sends it to acquire()
// when it is available.
type quotaPool struct {
c chan int
mu sync.Mutex
quota int
}
// newQuotaPool creates a quotaPool which has quota q available to consume.
func newQuotaPool(q int) *quotaPool {
qb := "aPool{
c: make(chan int, 1),
}
if q > 0 {
qb.c <- q
} else {
qb.quota = q
}
return qb
}
// add adds n to the available quota and tries to send it on acquire.
func (qb *quotaPool) add(n int) {
qb.mu.Lock()
defer qb.mu.Unlock()
qb.quota += n
if qb.quota <= 0 {
return
}
select {
case qb.c <- qb.quota:
qb.quota = 0
default:
}
}
// cancel cancels the pending quota sent on acquire, if any.
func (qb *quotaPool) cancel() {
qb.mu.Lock()
defer qb.mu.Unlock()
select {
case n := <-qb.c:
qb.quota += n
default:
}
}
// reset cancels the pending quota sent on acquired, incremented by v and sends
// it back on acquire.
func (qb *quotaPool) reset(v int) {
qb.mu.Lock()
defer qb.mu.Unlock()
select {
case n := <-qb.c:
qb.quota += n
default:
}
qb.quota += v
if qb.quota <= 0 {
return
}
select {
case qb.c <- qb.quota:
qb.quota = 0
default:
}
}
// acquire returns the channel on which available quota amounts are sent.
func (qb *quotaPool) acquire() <-chan int {
return qb.c
}
// inFlow deals with inbound flow control
type inFlow struct {
// The inbound flow control limit for pending data.
limit uint32
// conn points to the shared connection-level inFlow that is shared
// by all streams on that conn. It is nil for the inFlow on the conn
// directly.
conn *inFlow
mu sync.Mutex
// pendingData is the overall data which have been received but not been
// consumed by applications.
pendingData uint32
// The amount of data the application has consumed but grpc has not sent
// window update for them. Used to reduce window update frequency.
pendingUpdate uint32
}
// onData is invoked when some data frame is received. It increments not only its
// own pendingData but also that of the associated connection-level flow.
func (f *inFlow) onData(n uint32) error {
if n == 0 {
return nil
}
f.mu.Lock()
defer f.mu.Unlock()
if f.pendingData+f.pendingUpdate+n > f.limit {
return fmt.Errorf("recieved %d-bytes data exceeding the limit %d bytes", f.pendingData+f.pendingUpdate+n, f.limit)
}
if f.conn != nil {
if err := f.conn.onData(n); err != nil {
return ConnectionErrorf("%v", err)
}
}
f.pendingData += n
return nil
}
// connOnRead updates the connection level states when the application consumes data.
func (f *inFlow) connOnRead(n uint32) uint32 {
if n == 0 || f.conn != nil {
return 0
}
f.mu.Lock()
defer f.mu.Unlock()
f.pendingData -= n
f.pendingUpdate += n
if f.pendingUpdate >= f.limit/4 {
ret := f.pendingUpdate
f.pendingUpdate = 0
return ret
}
return 0
}
// onRead is invoked when the application reads the data. It returns the window updates
// for both stream and connection level.
func (f *inFlow) onRead(n uint32) (swu, cwu uint32) {
if n == 0 {
return
}
f.mu.Lock()
defer f.mu.Unlock()
if f.pendingData == 0 {
// pendingData has been adjusted by restoreConn.
return
}
f.pendingData -= n
f.pendingUpdate += n
if f.pendingUpdate >= f.limit/4 {
swu = f.pendingUpdate
f.pendingUpdate = 0
}
cwu = f.conn.connOnRead(n)
return
}
// restoreConn is invoked when a stream is terminated. It removes its stake in
// the connection-level flow and resets its own state.
func (f *inFlow) restoreConn() uint32 {
if f.conn == nil {
return 0
}
f.mu.Lock()
defer f.mu.Unlock()
n := f.pendingData
f.pendingData = 0
f.pendingUpdate = 0
return f.conn.connOnRead(n)
}
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/transport/http2_client.go 0000664 0000000 0000000 00000060470 12603607541 0024500 0 ustar 00root root 0000000 0000000 /*
*
* Copyright 2014, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
package transport
import (
"bytes"
"errors"
"io"
"math"
"net"
"strings"
"sync"
"time"
"golang.org/x/net/context"
"golang.org/x/net/http2"
"golang.org/x/net/http2/hpack"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/grpclog"
"google.golang.org/grpc/metadata"
)
// http2Client implements the ClientTransport interface with HTTP2.
type http2Client struct {
target string // server name/addr
userAgent string
conn net.Conn // underlying communication channel
authInfo credentials.AuthInfo // auth info about the connection
nextID uint32 // the next stream ID to be used
// writableChan synchronizes write access to the transport.
// A writer acquires the write lock by sending a value on writableChan
// and releases it by receiving from writableChan.
writableChan chan int
// shutdownChan is closed when Close is called.
// Blocking operations should select on shutdownChan to avoid
// blocking forever after Close.
// TODO(zhaoq): Maybe have a channel context?
shutdownChan chan struct{}
// errorChan is closed to notify the I/O error to the caller.
errorChan chan struct{}
framer *framer
hBuf *bytes.Buffer // the buffer for HPACK encoding
hEnc *hpack.Encoder // HPACK encoder
// controlBuf delivers all the control related tasks (e.g., window
// updates, reset streams, and various settings) to the controller.
controlBuf *recvBuffer
fc *inFlow
// sendQuotaPool provides flow control to outbound message.
sendQuotaPool *quotaPool
// streamsQuota limits the max number of concurrent streams.
streamsQuota *quotaPool
// The scheme used: https if TLS is on, http otherwise.
scheme string
authCreds []credentials.Credentials
mu sync.Mutex // guard the following variables
state transportState // the state of underlying connection
activeStreams map[uint32]*Stream
// The max number of concurrent streams
maxStreams int
// the per-stream outbound flow control window size set by the peer.
streamSendQuota uint32
}
// newHTTP2Client constructs a connected ClientTransport to addr based on HTTP2
// and starts to receive messages on it. Non-nil error returns if construction
// fails.
func newHTTP2Client(addr string, opts *ConnectOptions) (_ ClientTransport, err error) {
if opts.Dialer == nil {
// Set the default Dialer.
opts.Dialer = func(addr string, timeout time.Duration) (net.Conn, error) {
return net.DialTimeout("tcp", addr, timeout)
}
}
scheme := "http"
startT := time.Now()
timeout := opts.Timeout
conn, connErr := opts.Dialer(addr, timeout)
if connErr != nil {
return nil, ConnectionErrorf("transport: %v", connErr)
}
var authInfo credentials.AuthInfo
for _, c := range opts.AuthOptions {
if ccreds, ok := c.(credentials.TransportAuthenticator); ok {
scheme = "https"
// TODO(zhaoq): Now the first TransportAuthenticator is used if there are
// multiple ones provided. Revisit this if it is not appropriate. Probably
// place the ClientTransport construction into a separate function to make
// things clear.
if timeout > 0 {
timeout -= time.Since(startT)
}
conn, authInfo, connErr = ccreds.ClientHandshake(addr, conn, timeout)
break
}
}
if connErr != nil {
return nil, ConnectionErrorf("transport: %v", connErr)
}
defer func() {
if err != nil {
conn.Close()
}
}()
// Send connection preface to server.
n, err := conn.Write(clientPreface)
if err != nil {
return nil, ConnectionErrorf("transport: %v", err)
}
if n != len(clientPreface) {
return nil, ConnectionErrorf("transport: preface mismatch, wrote %d bytes; want %d", n, len(clientPreface))
}
framer := newFramer(conn)
if initialWindowSize != defaultWindowSize {
err = framer.writeSettings(true, http2.Setting{http2.SettingInitialWindowSize, uint32(initialWindowSize)})
} else {
err = framer.writeSettings(true)
}
if err != nil {
return nil, ConnectionErrorf("transport: %v", err)
}
// Adjust the connection flow control window if needed.
if delta := uint32(initialConnWindowSize - defaultWindowSize); delta > 0 {
if err := framer.writeWindowUpdate(true, 0, delta); err != nil {
return nil, ConnectionErrorf("transport: %v", err)
}
}
ua := primaryUA
if opts.UserAgent != "" {
ua = opts.UserAgent + " " + ua
}
var buf bytes.Buffer
t := &http2Client{
target: addr,
userAgent: ua,
conn: conn,
authInfo: authInfo,
// The client initiated stream id is odd starting from 1.
nextID: 1,
writableChan: make(chan int, 1),
shutdownChan: make(chan struct{}),
errorChan: make(chan struct{}),
framer: framer,
hBuf: &buf,
hEnc: hpack.NewEncoder(&buf),
controlBuf: newRecvBuffer(),
fc: &inFlow{limit: initialConnWindowSize},
sendQuotaPool: newQuotaPool(defaultWindowSize),
scheme: scheme,
state: reachable,
activeStreams: make(map[uint32]*Stream),
authCreds: opts.AuthOptions,
maxStreams: math.MaxInt32,
streamSendQuota: defaultWindowSize,
}
go t.controller()
t.writableChan <- 0
// Start the reader goroutine for incoming message. The threading model
// on receiving is that each transport has a dedicated goroutine which
// reads HTTP2 frame from network. Then it dispatches the frame to the
// corresponding stream entity.
go t.reader()
return t, nil
}
func (t *http2Client) newStream(ctx context.Context, callHdr *CallHdr) *Stream {
fc := &inFlow{
limit: initialWindowSize,
conn: t.fc,
}
// TODO(zhaoq): Handle uint32 overflow of Stream.id.
s := &Stream{
id: t.nextID,
method: callHdr.Method,
buf: newRecvBuffer(),
fc: fc,
sendQuotaPool: newQuotaPool(int(t.streamSendQuota)),
headerChan: make(chan struct{}),
}
t.nextID += 2
s.windowHandler = func(n int) {
t.updateWindow(s, uint32(n))
}
// Make a stream be able to cancel the pending operations by itself.
s.ctx, s.cancel = context.WithCancel(ctx)
s.dec = &recvBufferReader{
ctx: s.ctx,
recv: s.buf,
}
return s
}
// NewStream creates a stream and register it into the transport as "active"
// streams.
func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (_ *Stream, err error) {
// Record the timeout value on the context.
var timeout time.Duration
if dl, ok := ctx.Deadline(); ok {
timeout = dl.Sub(time.Now())
if timeout <= 0 {
return nil, ContextErr(context.DeadlineExceeded)
}
}
// Attach Auth info if there is any.
if t.authInfo != nil {
ctx = credentials.NewContext(ctx, t.authInfo)
}
authData := make(map[string]string)
for _, c := range t.authCreds {
// Construct URI required to get auth request metadata.
var port string
if pos := strings.LastIndex(t.target, ":"); pos != -1 {
// Omit port if it is the default one.
if t.target[pos+1:] != "443" {
port = ":" + t.target[pos+1:]
}
}
pos := strings.LastIndex(callHdr.Method, "/")
if pos == -1 {
return nil, StreamErrorf(codes.InvalidArgument, "transport: malformed method name: %q", callHdr.Method)
}
audience := "https://" + callHdr.Host + port + callHdr.Method[:pos]
data, err := c.GetRequestMetadata(ctx, audience)
if err != nil {
return nil, StreamErrorf(codes.InvalidArgument, "transport: %v", err)
}
for k, v := range data {
authData[k] = v
}
}
t.mu.Lock()
if t.state != reachable {
t.mu.Unlock()
return nil, ErrConnClosing
}
checkStreamsQuota := t.streamsQuota != nil
t.mu.Unlock()
if checkStreamsQuota {
sq, err := wait(ctx, t.shutdownChan, t.streamsQuota.acquire())
if err != nil {
return nil, err
}
// Returns the quota balance back.
if sq > 1 {
t.streamsQuota.add(sq - 1)
}
}
if _, err := wait(ctx, t.shutdownChan, t.writableChan); err != nil {
// t.streamsQuota will be updated when t.CloseStream is invoked.
return nil, err
}
t.mu.Lock()
if t.state != reachable {
t.mu.Unlock()
return nil, ErrConnClosing
}
s := t.newStream(ctx, callHdr)
t.activeStreams[s.id] = s
t.mu.Unlock()
// HPACK encodes various headers. Note that once WriteField(...) is
// called, the corresponding headers/continuation frame has to be sent
// because hpack.Encoder is stateful.
t.hBuf.Reset()
t.hEnc.WriteField(hpack.HeaderField{Name: ":method", Value: "POST"})
t.hEnc.WriteField(hpack.HeaderField{Name: ":scheme", Value: t.scheme})
t.hEnc.WriteField(hpack.HeaderField{Name: ":path", Value: callHdr.Method})
t.hEnc.WriteField(hpack.HeaderField{Name: ":authority", Value: callHdr.Host})
t.hEnc.WriteField(hpack.HeaderField{Name: "content-type", Value: "application/grpc"})
t.hEnc.WriteField(hpack.HeaderField{Name: "user-agent", Value: t.userAgent})
t.hEnc.WriteField(hpack.HeaderField{Name: "te", Value: "trailers"})
if timeout > 0 {
t.hEnc.WriteField(hpack.HeaderField{Name: "grpc-timeout", Value: timeoutEncode(timeout)})
}
for k, v := range authData {
t.hEnc.WriteField(hpack.HeaderField{Name: k, Value: v})
}
var (
hasMD bool
endHeaders bool
)
if md, ok := metadata.FromContext(ctx); ok {
hasMD = true
for k, v := range md {
for _, entry := range v {
t.hEnc.WriteField(hpack.HeaderField{Name: k, Value: entry})
}
}
}
first := true
// Sends the headers in a single batch even when they span multiple frames.
for !endHeaders {
size := t.hBuf.Len()
if size > http2MaxFrameLen {
size = http2MaxFrameLen
} else {
endHeaders = true
}
if first {
// Sends a HeadersFrame to server to start a new stream.
p := http2.HeadersFrameParam{
StreamID: s.id,
BlockFragment: t.hBuf.Next(size),
EndStream: false,
EndHeaders: endHeaders,
}
// Do a force flush for the buffered frames iff it is the last headers frame
// and there is header metadata to be sent. Otherwise, there is flushing until
// the corresponding data frame is written.
err = t.framer.writeHeaders(hasMD && endHeaders, p)
first = false
} else {
// Sends Continuation frames for the leftover headers.
err = t.framer.writeContinuation(hasMD && endHeaders, s.id, endHeaders, t.hBuf.Next(size))
}
if err != nil {
t.notifyError(err)
return nil, ConnectionErrorf("transport: %v", err)
}
}
t.writableChan <- 0
return s, nil
}
// CloseStream clears the footprint of a stream when the stream is not needed any more.
// This must not be executed in reader's goroutine.
func (t *http2Client) CloseStream(s *Stream, err error) {
var updateStreams bool
t.mu.Lock()
if t.streamsQuota != nil {
updateStreams = true
}
delete(t.activeStreams, s.id)
t.mu.Unlock()
if updateStreams {
t.streamsQuota.add(1)
}
s.mu.Lock()
if q := s.fc.restoreConn(); q > 0 {
t.controlBuf.put(&windowUpdate{0, q})
}
if s.state == streamDone {
s.mu.Unlock()
return
}
if !s.headerDone {
close(s.headerChan)
s.headerDone = true
}
s.state = streamDone
s.mu.Unlock()
// In case stream sending and receiving are invoked in separate
// goroutines (e.g., bi-directional streaming), the caller needs
// to call cancel on the stream to interrupt the blocking on
// other goroutines.
s.cancel()
if _, ok := err.(StreamError); ok {
t.controlBuf.put(&resetStream{s.id, http2.ErrCodeCancel})
}
}
// Close kicks off the shutdown process of the transport. This should be called
// only once on a transport. Once it is called, the transport should not be
// accessed any more.
func (t *http2Client) Close() (err error) {
t.mu.Lock()
if t.state == closing {
t.mu.Unlock()
return errors.New("transport: Close() was already called")
}
t.state = closing
t.mu.Unlock()
close(t.shutdownChan)
err = t.conn.Close()
t.mu.Lock()
streams := t.activeStreams
t.activeStreams = nil
t.mu.Unlock()
// Notify all active streams.
for _, s := range streams {
s.mu.Lock()
if !s.headerDone {
close(s.headerChan)
s.headerDone = true
}
s.mu.Unlock()
s.write(recvMsg{err: ErrConnClosing})
}
return
}
// Write formats the data into HTTP2 data frame(s) and sends it out. The caller
// should proceed only if Write returns nil.
// TODO(zhaoq): opts.Delay is ignored in this implementation. Support it later
// if it improves the performance.
func (t *http2Client) Write(s *Stream, data []byte, opts *Options) error {
r := bytes.NewBuffer(data)
for {
var p []byte
if r.Len() > 0 {
size := http2MaxFrameLen
s.sendQuotaPool.add(0)
// Wait until the stream has some quota to send the data.
sq, err := wait(s.ctx, t.shutdownChan, s.sendQuotaPool.acquire())
if err != nil {
return err
}
t.sendQuotaPool.add(0)
// Wait until the transport has some quota to send the data.
tq, err := wait(s.ctx, t.shutdownChan, t.sendQuotaPool.acquire())
if err != nil {
if _, ok := err.(StreamError); ok {
t.sendQuotaPool.cancel()
}
return err
}
if sq < size {
size = sq
}
if tq < size {
size = tq
}
p = r.Next(size)
ps := len(p)
if ps < sq {
// Overbooked stream quota. Return it back.
s.sendQuotaPool.add(sq - ps)
}
if ps < tq {
// Overbooked transport quota. Return it back.
t.sendQuotaPool.add(tq - ps)
}
}
var (
endStream bool
forceFlush bool
)
if opts.Last && r.Len() == 0 {
endStream = true
}
// Indicate there is a writer who is about to write a data frame.
t.framer.adjustNumWriters(1)
// Got some quota. Try to acquire writing privilege on the transport.
if _, err := wait(s.ctx, t.shutdownChan, t.writableChan); err != nil {
if t.framer.adjustNumWriters(-1) == 0 {
// This writer is the last one in this batch and has the
// responsibility to flush the buffered frames. It queues
// a flush request to controlBuf instead of flushing directly
// in order to avoid the race with other writing or flushing.
t.controlBuf.put(&flushIO{})
}
return err
}
if r.Len() == 0 && t.framer.adjustNumWriters(0) == 1 {
// Do a force flush iff this is last frame for the entire gRPC message
// and the caller is the only writer at this moment.
forceFlush = true
}
// If WriteData fails, all the pending streams will be handled
// by http2Client.Close(). No explicit CloseStream() needs to be
// invoked.
if err := t.framer.writeData(forceFlush, s.id, endStream, p); err != nil {
t.notifyError(err)
return ConnectionErrorf("transport: %v", err)
}
if t.framer.adjustNumWriters(-1) == 0 {
t.framer.flushWrite()
}
t.writableChan <- 0
if r.Len() == 0 {
break
}
}
if !opts.Last {
return nil
}
s.mu.Lock()
if s.state != streamDone {
if s.state == streamReadDone {
s.state = streamDone
} else {
s.state = streamWriteDone
}
}
s.mu.Unlock()
return nil
}
func (t *http2Client) getStream(f http2.Frame) (*Stream, bool) {
t.mu.Lock()
defer t.mu.Unlock()
if t.activeStreams == nil {
// The transport is closing.
return nil, false
}
if s, ok := t.activeStreams[f.Header().StreamID]; ok {
return s, true
}
return nil, false
}
// updateWindow adjusts the inbound quota for the stream and the transport.
// Window updates will deliver to the controller for sending when
// the cumulative quota exceeds the corresponding threshold.
func (t *http2Client) updateWindow(s *Stream, n uint32) {
swu, cwu := s.fc.onRead(n)
if swu > 0 {
t.controlBuf.put(&windowUpdate{s.id, swu})
}
if cwu > 0 {
t.controlBuf.put(&windowUpdate{0, cwu})
}
}
func (t *http2Client) handleData(f *http2.DataFrame) {
// Select the right stream to dispatch.
s, ok := t.getStream(f)
if !ok {
return
}
size := len(f.Data())
if size > 0 {
if err := s.fc.onData(uint32(size)); err != nil {
if _, ok := err.(ConnectionError); ok {
t.notifyError(err)
return
}
s.mu.Lock()
if s.state == streamDone {
s.mu.Unlock()
return
}
s.state = streamDone
s.statusCode = codes.Internal
s.statusDesc = err.Error()
s.mu.Unlock()
s.write(recvMsg{err: io.EOF})
t.controlBuf.put(&resetStream{s.id, http2.ErrCodeFlowControl})
return
}
// TODO(bradfitz, zhaoq): A copy is required here because there is no
// guarantee f.Data() is consumed before the arrival of next frame.
// Can this copy be eliminated?
data := make([]byte, size)
copy(data, f.Data())
s.write(recvMsg{data: data})
}
// The server has closed the stream without sending trailers. Record that
// the read direction is closed, and set the status appropriately.
if f.FrameHeader.Flags.Has(http2.FlagDataEndStream) {
s.mu.Lock()
if s.state == streamWriteDone {
s.state = streamDone
} else {
s.state = streamReadDone
}
s.statusCode = codes.Internal
s.statusDesc = "server closed the stream without sending trailers"
s.mu.Unlock()
s.write(recvMsg{err: io.EOF})
}
}
func (t *http2Client) handleRSTStream(f *http2.RSTStreamFrame) {
s, ok := t.getStream(f)
if !ok {
return
}
s.mu.Lock()
if s.state == streamDone {
s.mu.Unlock()
return
}
s.state = streamDone
if !s.headerDone {
close(s.headerChan)
s.headerDone = true
}
s.statusCode, ok = http2RSTErrConvTab[http2.ErrCode(f.ErrCode)]
if !ok {
grpclog.Println("transport: http2Client.handleRSTStream found no mapped gRPC status for the received http2 error ", f.ErrCode)
}
s.mu.Unlock()
s.write(recvMsg{err: io.EOF})
}
func (t *http2Client) handleSettings(f *http2.SettingsFrame) {
if f.IsAck() {
return
}
var ss []http2.Setting
f.ForeachSetting(func(s http2.Setting) error {
ss = append(ss, s)
return nil
})
// The settings will be applied once the ack is sent.
t.controlBuf.put(&settings{ack: true, ss: ss})
}
func (t *http2Client) handlePing(f *http2.PingFrame) {
t.controlBuf.put(&ping{true})
}
func (t *http2Client) handleGoAway(f *http2.GoAwayFrame) {
// TODO(zhaoq): GoAwayFrame handler to be implemented
}
func (t *http2Client) handleWindowUpdate(f *http2.WindowUpdateFrame) {
id := f.Header().StreamID
incr := f.Increment
if id == 0 {
t.sendQuotaPool.add(int(incr))
return
}
if s, ok := t.getStream(f); ok {
s.sendQuotaPool.add(int(incr))
}
}
// operateHeader takes action on the decoded headers. It returns the current
// stream if there are remaining headers on the wire (in the following
// Continuation frame).
func (t *http2Client) operateHeaders(hDec *hpackDecoder, s *Stream, frame headerFrame, endStream bool) (pendingStream *Stream) {
defer func() {
if pendingStream == nil {
hDec.state = decodeState{}
}
}()
endHeaders, err := hDec.decodeClientHTTP2Headers(frame)
if s == nil {
// s has been closed.
return nil
}
if err != nil {
s.write(recvMsg{err: err})
// Something wrong. Stops reading even when there is remaining.
return nil
}
if !endHeaders {
return s
}
s.mu.Lock()
if !s.headerDone {
if !endStream && len(hDec.state.mdata) > 0 {
s.header = hDec.state.mdata
}
close(s.headerChan)
s.headerDone = true
}
if !endStream || s.state == streamDone {
s.mu.Unlock()
return nil
}
if len(hDec.state.mdata) > 0 {
s.trailer = hDec.state.mdata
}
s.state = streamDone
s.statusCode = hDec.state.statusCode
s.statusDesc = hDec.state.statusDesc
s.mu.Unlock()
s.write(recvMsg{err: io.EOF})
return nil
}
// reader runs as a separate goroutine in charge of reading data from network
// connection.
//
// TODO(zhaoq): currently one reader per transport. Investigate whether this is
// optimal.
// TODO(zhaoq): Check the validity of the incoming frame sequence.
func (t *http2Client) reader() {
// Check the validity of server preface.
frame, err := t.framer.readFrame()
if err != nil {
t.notifyError(err)
return
}
sf, ok := frame.(*http2.SettingsFrame)
if !ok {
t.notifyError(err)
return
}
t.handleSettings(sf)
hDec := newHPACKDecoder()
var curStream *Stream
// loop to keep reading incoming messages on this transport.
for {
frame, err := t.framer.readFrame()
if err != nil {
t.notifyError(err)
return
}
switch frame := frame.(type) {
case *http2.HeadersFrame:
// operateHeaders has to be invoked regardless the value of curStream
// because the HPACK decoder needs to be updated using the received
// headers.
curStream, _ = t.getStream(frame)
endStream := frame.Header().Flags.Has(http2.FlagHeadersEndStream)
curStream = t.operateHeaders(hDec, curStream, frame, endStream)
case *http2.ContinuationFrame:
curStream = t.operateHeaders(hDec, curStream, frame, false)
case *http2.DataFrame:
t.handleData(frame)
case *http2.RSTStreamFrame:
t.handleRSTStream(frame)
case *http2.SettingsFrame:
t.handleSettings(frame)
case *http2.PingFrame:
t.handlePing(frame)
case *http2.GoAwayFrame:
t.handleGoAway(frame)
case *http2.WindowUpdateFrame:
t.handleWindowUpdate(frame)
default:
grpclog.Printf("transport: http2Client.reader got unhandled frame type %v.", frame)
}
}
}
func (t *http2Client) applySettings(ss []http2.Setting) {
for _, s := range ss {
switch s.ID {
case http2.SettingMaxConcurrentStreams:
// TODO(zhaoq): This is a hack to avoid significant refactoring of the
// code to deal with the unrealistic int32 overflow. Probably will try
// to find a better way to handle this later.
if s.Val > math.MaxInt32 {
s.Val = math.MaxInt32
}
t.mu.Lock()
reset := t.streamsQuota != nil
if !reset {
t.streamsQuota = newQuotaPool(int(s.Val) - len(t.activeStreams))
}
ms := t.maxStreams
t.maxStreams = int(s.Val)
t.mu.Unlock()
if reset {
t.streamsQuota.reset(int(s.Val) - ms)
}
case http2.SettingInitialWindowSize:
t.mu.Lock()
for _, stream := range t.activeStreams {
// Adjust the sending quota for each stream.
stream.sendQuotaPool.reset(int(s.Val - t.streamSendQuota))
}
t.streamSendQuota = s.Val
t.mu.Unlock()
}
}
}
// controller running in a separate goroutine takes charge of sending control
// frames (e.g., window update, reset stream, setting, etc.) to the server.
func (t *http2Client) controller() {
for {
select {
case i := <-t.controlBuf.get():
t.controlBuf.load()
select {
case <-t.writableChan:
switch i := i.(type) {
case *windowUpdate:
t.framer.writeWindowUpdate(true, i.streamID, i.increment)
case *settings:
if i.ack {
t.framer.writeSettingsAck(true)
t.applySettings(i.ss)
} else {
t.framer.writeSettings(true, i.ss...)
}
case *resetStream:
t.framer.writeRSTStream(true, i.streamID, i.code)
case *flushIO:
t.framer.flushWrite()
case *ping:
// TODO(zhaoq): Ack with all-0 data now. will change to some
// meaningful content when this is actually in use.
t.framer.writePing(true, i.ack, [8]byte{})
default:
grpclog.Printf("transport: http2Client.controller got unexpected item type %v\n", i)
}
t.writableChan <- 0
continue
case <-t.shutdownChan:
return
}
case <-t.shutdownChan:
return
}
}
}
func (t *http2Client) Error() <-chan struct{} {
return t.errorChan
}
func (t *http2Client) notifyError(err error) {
t.mu.Lock()
defer t.mu.Unlock()
// make sure t.errorChan is closed only once.
if t.state == reachable {
t.state = unreachable
close(t.errorChan)
grpclog.Printf("transport: http2Client.notifyError got notified that the client transport was broken %v.", err)
}
}
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/transport/http2_server.go 0000664 0000000 0000000 00000050001 12603607541 0024515 0 ustar 00root root 0000000 0000000 /*
*
* Copyright 2014, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
package transport
import (
"bytes"
"errors"
"io"
"math"
"net"
"strconv"
"sync"
"golang.org/x/net/context"
"golang.org/x/net/http2"
"golang.org/x/net/http2/hpack"
"golang.org/x/net/trace"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/grpclog"
"google.golang.org/grpc/metadata"
)
// ErrIllegalHeaderWrite indicates that setting header is illegal because of
// the stream's state.
var ErrIllegalHeaderWrite = errors.New("transport: the stream is done or WriteHeader was already called")
// http2Server implements the ServerTransport interface with HTTP2.
type http2Server struct {
conn net.Conn
maxStreamID uint32 // max stream ID ever seen
authInfo credentials.AuthInfo // auth info about the connection
// writableChan synchronizes write access to the transport.
// A writer acquires the write lock by sending a value on writableChan
// and releases it by receiving from writableChan.
writableChan chan int
// shutdownChan is closed when Close is called.
// Blocking operations should select on shutdownChan to avoid
// blocking forever after Close.
shutdownChan chan struct{}
framer *framer
hBuf *bytes.Buffer // the buffer for HPACK encoding
hEnc *hpack.Encoder // HPACK encoder
// The max number of concurrent streams.
maxStreams uint32
// controlBuf delivers all the control related tasks (e.g., window
// updates, reset streams, and various settings) to the controller.
controlBuf *recvBuffer
fc *inFlow
// sendQuotaPool provides flow control to outbound message.
sendQuotaPool *quotaPool
// tracing indicates whether tracing is on for this http2Server transport.
tracing bool
mu sync.Mutex // guard the following
state transportState
activeStreams map[uint32]*Stream
// the per-stream outbound flow control window size set by the peer.
streamSendQuota uint32
}
// newHTTP2Server constructs a ServerTransport based on HTTP2. ConnectionError is
// returned if something goes wrong.
func newHTTP2Server(conn net.Conn, maxStreams uint32, authInfo credentials.AuthInfo, tracing bool) (_ ServerTransport, err error) {
framer := newFramer(conn)
// Send initial settings as connection preface to client.
var settings []http2.Setting
// TODO(zhaoq): Have a better way to signal "no limit" because 0 is
// permitted in the HTTP2 spec.
if maxStreams == 0 {
maxStreams = math.MaxUint32
} else {
settings = append(settings, http2.Setting{http2.SettingMaxConcurrentStreams, maxStreams})
}
if initialWindowSize != defaultWindowSize {
settings = append(settings, http2.Setting{http2.SettingInitialWindowSize, uint32(initialWindowSize)})
}
if err := framer.writeSettings(true, settings...); err != nil {
return nil, ConnectionErrorf("transport: %v", err)
}
// Adjust the connection flow control window if needed.
if delta := uint32(initialConnWindowSize - defaultWindowSize); delta > 0 {
if err := framer.writeWindowUpdate(true, 0, delta); err != nil {
return nil, ConnectionErrorf("transport: %v", err)
}
}
var buf bytes.Buffer
t := &http2Server{
conn: conn,
authInfo: authInfo,
framer: framer,
hBuf: &buf,
hEnc: hpack.NewEncoder(&buf),
maxStreams: maxStreams,
controlBuf: newRecvBuffer(),
fc: &inFlow{limit: initialConnWindowSize},
sendQuotaPool: newQuotaPool(defaultWindowSize),
tracing: tracing,
state: reachable,
writableChan: make(chan int, 1),
shutdownChan: make(chan struct{}),
activeStreams: make(map[uint32]*Stream),
streamSendQuota: defaultWindowSize,
}
go t.controller()
t.writableChan <- 0
return t, nil
}
// operateHeader takes action on the decoded headers. It returns the current
// stream if there are remaining headers on the wire (in the following
// Continuation frame).
func (t *http2Server) operateHeaders(hDec *hpackDecoder, s *Stream, frame headerFrame, endStream bool, handle func(*Stream), wg *sync.WaitGroup) (pendingStream *Stream) {
defer func() {
if pendingStream == nil {
hDec.state = decodeState{}
}
}()
endHeaders, err := hDec.decodeServerHTTP2Headers(frame)
if s == nil {
// s has been closed.
return nil
}
if err != nil {
grpclog.Printf("transport: http2Server.operateHeader found %v", err)
if se, ok := err.(StreamError); ok {
t.controlBuf.put(&resetStream{s.id, statusCodeConvTab[se.Code]})
}
return nil
}
if endStream {
// s is just created by the caller. No lock needed.
s.state = streamReadDone
}
if !endHeaders {
return s
}
t.mu.Lock()
if t.state != reachable {
t.mu.Unlock()
return nil
}
if uint32(len(t.activeStreams)) >= t.maxStreams {
t.mu.Unlock()
t.controlBuf.put(&resetStream{s.id, http2.ErrCodeRefusedStream})
return nil
}
s.sendQuotaPool = newQuotaPool(int(t.streamSendQuota))
t.activeStreams[s.id] = s
t.mu.Unlock()
s.windowHandler = func(n int) {
t.updateWindow(s, uint32(n))
}
if hDec.state.timeoutSet {
s.ctx, s.cancel = context.WithTimeout(context.TODO(), hDec.state.timeout)
} else {
s.ctx, s.cancel = context.WithCancel(context.TODO())
}
// Attach Auth info if there is any.
if t.authInfo != nil {
s.ctx = credentials.NewContext(s.ctx, t.authInfo)
}
// Cache the current stream to the context so that the server application
// can find out. Required when the server wants to send some metadata
// back to the client (unary call only).
s.ctx = newContextWithStream(s.ctx, s)
// Attach the received metadata to the context.
if len(hDec.state.mdata) > 0 {
s.ctx = metadata.NewContext(s.ctx, hDec.state.mdata)
}
s.dec = &recvBufferReader{
ctx: s.ctx,
recv: s.buf,
}
s.method = hDec.state.method
if t.tracing {
s.tr = trace.New("grpc.Recv."+MethodFamily(s.method), s.method)
s.ctx = trace.NewContext(s.ctx, s.tr)
}
wg.Add(1)
go func() {
handle(s)
wg.Done()
}()
return nil
}
// HandleStreams receives incoming streams using the given handler. This is
// typically run in a separate goroutine.
func (t *http2Server) HandleStreams(handle func(*Stream)) {
// Check the validity of client preface.
preface := make([]byte, len(clientPreface))
if _, err := io.ReadFull(t.conn, preface); err != nil {
grpclog.Printf("transport: http2Server.HandleStreams failed to receive the preface from client: %v", err)
t.Close()
return
}
if !bytes.Equal(preface, clientPreface) {
grpclog.Printf("transport: http2Server.HandleStreams received bogus greeting from client: %q", preface)
t.Close()
return
}
frame, err := t.framer.readFrame()
if err != nil {
grpclog.Printf("transport: http2Server.HandleStreams failed to read frame: %v", err)
t.Close()
return
}
sf, ok := frame.(*http2.SettingsFrame)
if !ok {
grpclog.Printf("transport: http2Server.HandleStreams saw invalid preface type %T from client", frame)
t.Close()
return
}
t.handleSettings(sf)
hDec := newHPACKDecoder()
var curStream *Stream
var wg sync.WaitGroup
defer wg.Wait()
for {
frame, err := t.framer.readFrame()
if err != nil {
t.Close()
return
}
switch frame := frame.(type) {
case *http2.HeadersFrame:
id := frame.Header().StreamID
if id%2 != 1 || id <= t.maxStreamID {
// illegal gRPC stream id.
grpclog.Println("transport: http2Server.HandleStreams received an illegal stream id: ", id)
t.Close()
break
}
t.maxStreamID = id
buf := newRecvBuffer()
fc := &inFlow{
limit: initialWindowSize,
conn: t.fc,
}
curStream = &Stream{
id: frame.Header().StreamID,
st: t,
buf: buf,
fc: fc,
}
endStream := frame.Header().Flags.Has(http2.FlagHeadersEndStream)
curStream = t.operateHeaders(hDec, curStream, frame, endStream, handle, &wg)
case *http2.ContinuationFrame:
curStream = t.operateHeaders(hDec, curStream, frame, false, handle, &wg)
case *http2.DataFrame:
t.handleData(frame)
case *http2.RSTStreamFrame:
t.handleRSTStream(frame)
case *http2.SettingsFrame:
t.handleSettings(frame)
case *http2.PingFrame:
t.handlePing(frame)
case *http2.WindowUpdateFrame:
t.handleWindowUpdate(frame)
case *http2.GoAwayFrame:
break
default:
grpclog.Printf("transport: http2Server.HandleStreams found unhandled frame type %v.", frame)
}
}
}
func (t *http2Server) getStream(f http2.Frame) (*Stream, bool) {
t.mu.Lock()
defer t.mu.Unlock()
if t.activeStreams == nil {
// The transport is closing.
return nil, false
}
s, ok := t.activeStreams[f.Header().StreamID]
if !ok {
// The stream is already done.
return nil, false
}
return s, true
}
// updateWindow adjusts the inbound quota for the stream and the transport.
// Window updates will deliver to the controller for sending when
// the cumulative quota exceeds the corresponding threshold.
func (t *http2Server) updateWindow(s *Stream, n uint32) {
swu, cwu := s.fc.onRead(n)
if swu > 0 {
t.controlBuf.put(&windowUpdate{s.id, swu})
}
if cwu > 0 {
t.controlBuf.put(&windowUpdate{0, cwu})
}
}
func (t *http2Server) handleData(f *http2.DataFrame) {
// Select the right stream to dispatch.
s, ok := t.getStream(f)
if !ok {
return
}
size := len(f.Data())
if size > 0 {
if err := s.fc.onData(uint32(size)); err != nil {
if _, ok := err.(ConnectionError); ok {
grpclog.Printf("transport: http2Server %v", err)
t.Close()
return
}
t.closeStream(s)
t.controlBuf.put(&resetStream{s.id, http2.ErrCodeFlowControl})
return
}
// TODO(bradfitz, zhaoq): A copy is required here because there is no
// guarantee f.Data() is consumed before the arrival of next frame.
// Can this copy be eliminated?
data := make([]byte, size)
copy(data, f.Data())
s.write(recvMsg{data: data})
}
if f.Header().Flags.Has(http2.FlagDataEndStream) {
// Received the end of stream from the client.
s.mu.Lock()
if s.state != streamDone {
if s.state == streamWriteDone {
s.state = streamDone
} else {
s.state = streamReadDone
}
}
s.mu.Unlock()
s.write(recvMsg{err: io.EOF})
}
}
func (t *http2Server) handleRSTStream(f *http2.RSTStreamFrame) {
s, ok := t.getStream(f)
if !ok {
return
}
t.closeStream(s)
}
func (t *http2Server) handleSettings(f *http2.SettingsFrame) {
if f.IsAck() {
return
}
var ss []http2.Setting
f.ForeachSetting(func(s http2.Setting) error {
ss = append(ss, s)
return nil
})
// The settings will be applied once the ack is sent.
t.controlBuf.put(&settings{ack: true, ss: ss})
}
func (t *http2Server) handlePing(f *http2.PingFrame) {
t.controlBuf.put(&ping{true})
}
func (t *http2Server) handleWindowUpdate(f *http2.WindowUpdateFrame) {
id := f.Header().StreamID
incr := f.Increment
if id == 0 {
t.sendQuotaPool.add(int(incr))
return
}
if s, ok := t.getStream(f); ok {
s.sendQuotaPool.add(int(incr))
}
}
func (t *http2Server) writeHeaders(s *Stream, b *bytes.Buffer, endStream bool) error {
first := true
endHeaders := false
var err error
// Sends the headers in a single batch.
for !endHeaders {
size := t.hBuf.Len()
if size > http2MaxFrameLen {
size = http2MaxFrameLen
} else {
endHeaders = true
}
if first {
p := http2.HeadersFrameParam{
StreamID: s.id,
BlockFragment: b.Next(size),
EndStream: endStream,
EndHeaders: endHeaders,
}
err = t.framer.writeHeaders(endHeaders, p)
first = false
} else {
err = t.framer.writeContinuation(endHeaders, s.id, endHeaders, b.Next(size))
}
if err != nil {
t.Close()
return ConnectionErrorf("transport: %v", err)
}
}
return nil
}
// WriteHeader sends the header metedata md back to the client.
func (t *http2Server) WriteHeader(s *Stream, md metadata.MD) error {
s.mu.Lock()
if s.headerOk || s.state == streamDone {
s.mu.Unlock()
return ErrIllegalHeaderWrite
}
s.headerOk = true
s.mu.Unlock()
if _, err := wait(s.ctx, t.shutdownChan, t.writableChan); err != nil {
return err
}
t.hBuf.Reset()
t.hEnc.WriteField(hpack.HeaderField{Name: ":status", Value: "200"})
t.hEnc.WriteField(hpack.HeaderField{Name: "content-type", Value: "application/grpc"})
for k, v := range md {
for _, entry := range v {
t.hEnc.WriteField(hpack.HeaderField{Name: k, Value: entry})
}
}
if err := t.writeHeaders(s, t.hBuf, false); err != nil {
return err
}
t.writableChan <- 0
return nil
}
// WriteStatus sends stream status to the client and terminates the stream.
// There is no further I/O operations being able to perform on this stream.
// TODO(zhaoq): Now it indicates the end of entire stream. Revisit if early
// OK is adopted.
func (t *http2Server) WriteStatus(s *Stream, statusCode codes.Code, statusDesc string) error {
s.mu.RLock()
if s.state == streamDone {
s.mu.RUnlock()
return nil
}
s.mu.RUnlock()
if _, err := wait(s.ctx, t.shutdownChan, t.writableChan); err != nil {
return err
}
t.hBuf.Reset()
t.hEnc.WriteField(hpack.HeaderField{Name: ":status", Value: "200"})
t.hEnc.WriteField(
hpack.HeaderField{
Name: "grpc-status",
Value: strconv.Itoa(int(statusCode)),
})
t.hEnc.WriteField(hpack.HeaderField{Name: "grpc-message", Value: statusDesc})
// Attach the trailer metadata.
for k, v := range s.trailer {
for _, entry := range v {
t.hEnc.WriteField(hpack.HeaderField{Name: k, Value: entry})
}
}
if err := t.writeHeaders(s, t.hBuf, true); err != nil {
t.Close()
return err
}
t.closeStream(s)
t.writableChan <- 0
return nil
}
// Write converts the data into HTTP2 data frame and sends it out. Non-nil error
// is returns if it fails (e.g., framing error, transport error).
func (t *http2Server) Write(s *Stream, data []byte, opts *Options) error {
// TODO(zhaoq): Support multi-writers for a single stream.
var writeHeaderFrame bool
s.mu.Lock()
if !s.headerOk {
writeHeaderFrame = true
s.headerOk = true
}
s.mu.Unlock()
if writeHeaderFrame {
if _, err := wait(s.ctx, t.shutdownChan, t.writableChan); err != nil {
return err
}
t.hBuf.Reset()
t.hEnc.WriteField(hpack.HeaderField{Name: ":status", Value: "200"})
t.hEnc.WriteField(hpack.HeaderField{Name: "content-type", Value: "application/grpc"})
p := http2.HeadersFrameParam{
StreamID: s.id,
BlockFragment: t.hBuf.Bytes(),
EndHeaders: true,
}
if err := t.framer.writeHeaders(false, p); err != nil {
t.Close()
return ConnectionErrorf("transport: %v", err)
}
t.writableChan <- 0
}
r := bytes.NewBuffer(data)
for {
if r.Len() == 0 {
return nil
}
size := http2MaxFrameLen
s.sendQuotaPool.add(0)
// Wait until the stream has some quota to send the data.
sq, err := wait(s.ctx, t.shutdownChan, s.sendQuotaPool.acquire())
if err != nil {
return err
}
t.sendQuotaPool.add(0)
// Wait until the transport has some quota to send the data.
tq, err := wait(s.ctx, t.shutdownChan, t.sendQuotaPool.acquire())
if err != nil {
if _, ok := err.(StreamError); ok {
t.sendQuotaPool.cancel()
}
return err
}
if sq < size {
size = sq
}
if tq < size {
size = tq
}
p := r.Next(size)
ps := len(p)
if ps < sq {
// Overbooked stream quota. Return it back.
s.sendQuotaPool.add(sq - ps)
}
if ps < tq {
// Overbooked transport quota. Return it back.
t.sendQuotaPool.add(tq - ps)
}
t.framer.adjustNumWriters(1)
// Got some quota. Try to acquire writing privilege on the
// transport.
if _, err := wait(s.ctx, t.shutdownChan, t.writableChan); err != nil {
if t.framer.adjustNumWriters(-1) == 0 {
// This writer is the last one in this batch and has the
// responsibility to flush the buffered frames. It queues
// a flush request to controlBuf instead of flushing directly
// in order to avoid the race with other writing or flushing.
t.controlBuf.put(&flushIO{})
}
return err
}
var forceFlush bool
if r.Len() == 0 && t.framer.adjustNumWriters(0) == 1 && !opts.Last {
forceFlush = true
}
if err := t.framer.writeData(forceFlush, s.id, false, p); err != nil {
t.Close()
return ConnectionErrorf("transport: %v", err)
}
if t.framer.adjustNumWriters(-1) == 0 {
t.framer.flushWrite()
}
t.writableChan <- 0
}
}
func (t *http2Server) applySettings(ss []http2.Setting) {
for _, s := range ss {
if s.ID == http2.SettingInitialWindowSize {
t.mu.Lock()
defer t.mu.Unlock()
for _, stream := range t.activeStreams {
stream.sendQuotaPool.reset(int(s.Val - t.streamSendQuota))
}
t.streamSendQuota = s.Val
}
}
}
// controller running in a separate goroutine takes charge of sending control
// frames (e.g., window update, reset stream, setting, etc.) to the server.
func (t *http2Server) controller() {
for {
select {
case i := <-t.controlBuf.get():
t.controlBuf.load()
select {
case <-t.writableChan:
switch i := i.(type) {
case *windowUpdate:
t.framer.writeWindowUpdate(true, i.streamID, i.increment)
case *settings:
if i.ack {
t.framer.writeSettingsAck(true)
t.applySettings(i.ss)
} else {
t.framer.writeSettings(true, i.ss...)
}
case *resetStream:
t.framer.writeRSTStream(true, i.streamID, i.code)
case *flushIO:
t.framer.flushWrite()
case *ping:
// TODO(zhaoq): Ack with all-0 data now. will change to some
// meaningful content when this is actually in use.
t.framer.writePing(true, i.ack, [8]byte{})
default:
grpclog.Printf("transport: http2Server.controller got unexpected item type %v\n", i)
}
t.writableChan <- 0
continue
case <-t.shutdownChan:
return
}
case <-t.shutdownChan:
return
}
}
}
// Close starts shutting down the http2Server transport.
// TODO(zhaoq): Now the destruction is not blocked on any pending streams. This
// could cause some resource issue. Revisit this later.
func (t *http2Server) Close() (err error) {
t.mu.Lock()
if t.state == closing {
t.mu.Unlock()
return errors.New("transport: Close() was already called")
}
t.state = closing
streams := t.activeStreams
t.activeStreams = nil
t.mu.Unlock()
close(t.shutdownChan)
err = t.conn.Close()
// Notify all active streams.
for _, s := range streams {
s.write(recvMsg{err: ErrConnClosing})
}
return
}
// closeStream clears the footprint of a stream when the stream is not needed
// any more.
func (t *http2Server) closeStream(s *Stream) {
t.mu.Lock()
delete(t.activeStreams, s.id)
t.mu.Unlock()
if q := s.fc.restoreConn(); q > 0 {
t.controlBuf.put(&windowUpdate{0, q})
}
s.mu.Lock()
if s.state == streamDone {
s.mu.Unlock()
return
}
s.state = streamDone
s.mu.Unlock()
// In case stream sending and receiving are invoked in separate
// goroutines (e.g., bi-directional streaming), the caller needs
// to call cancel on the stream to interrupt the blocking on
// other goroutines.
s.cancel()
}
func (t *http2Server) RemoteAddr() net.Addr {
return t.conn.RemoteAddr()
}
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/transport/http_util.go 0000664 0000000 0000000 00000030244 12603607541 0024111 0 ustar 00root root 0000000 0000000 /*
*
* Copyright 2014, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
package transport
import (
"bufio"
"fmt"
"io"
"net"
"strconv"
"strings"
"sync/atomic"
"time"
"golang.org/x/net/http2"
"golang.org/x/net/http2/hpack"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/grpclog"
"google.golang.org/grpc/metadata"
)
const (
// The primary user agent
primaryUA = "grpc-go/0.11"
// http2MaxFrameLen specifies the max length of a HTTP2 frame.
http2MaxFrameLen = 16384 // 16KB frame
// http://http2.github.io/http2-spec/#SettingValues
http2InitHeaderTableSize = 4096
// http2IOBufSize specifies the buffer size for sending frames.
http2IOBufSize = 32 * 1024
)
var (
clientPreface = []byte(http2.ClientPreface)
http2RSTErrConvTab = map[http2.ErrCode]codes.Code{
http2.ErrCodeNo: codes.Internal,
http2.ErrCodeProtocol: codes.Internal,
http2.ErrCodeInternal: codes.Internal,
http2.ErrCodeFlowControl: codes.ResourceExhausted,
http2.ErrCodeSettingsTimeout: codes.Internal,
http2.ErrCodeFrameSize: codes.Internal,
http2.ErrCodeRefusedStream: codes.Unavailable,
http2.ErrCodeCancel: codes.Canceled,
http2.ErrCodeCompression: codes.Internal,
http2.ErrCodeConnect: codes.Internal,
http2.ErrCodeEnhanceYourCalm: codes.ResourceExhausted,
http2.ErrCodeInadequateSecurity: codes.PermissionDenied,
}
statusCodeConvTab = map[codes.Code]http2.ErrCode{
codes.Internal: http2.ErrCodeInternal,
codes.Canceled: http2.ErrCodeCancel,
codes.Unavailable: http2.ErrCodeRefusedStream,
codes.ResourceExhausted: http2.ErrCodeEnhanceYourCalm,
codes.PermissionDenied: http2.ErrCodeInadequateSecurity,
}
)
// Records the states during HPACK decoding. Must be reset once the
// decoding of the entire headers are finished.
type decodeState struct {
// statusCode caches the stream status received from the trailer
// the server sent. Client side only.
statusCode codes.Code
statusDesc string
// Server side only fields.
timeoutSet bool
timeout time.Duration
method string
// key-value metadata map from the peer.
mdata map[string][]string
}
// An hpackDecoder decodes HTTP2 headers which may span multiple frames.
type hpackDecoder struct {
h *hpack.Decoder
state decodeState
err error // The err when decoding
}
// A headerFrame is either a http2.HeaderFrame or http2.ContinuationFrame.
type headerFrame interface {
Header() http2.FrameHeader
HeaderBlockFragment() []byte
HeadersEnded() bool
}
// isReservedHeader checks whether hdr belongs to HTTP2 headers
// reserved by gRPC protocol. Any other headers are classified as the
// user-specified metadata.
func isReservedHeader(hdr string) bool {
if hdr[0] == ':' {
return true
}
switch hdr {
case "content-type",
"grpc-message-type",
"grpc-encoding",
"grpc-message",
"grpc-status",
"grpc-timeout",
"te":
return true
default:
return false
}
}
func newHPACKDecoder() *hpackDecoder {
d := &hpackDecoder{}
d.h = hpack.NewDecoder(http2InitHeaderTableSize, func(f hpack.HeaderField) {
switch f.Name {
case "content-type":
// TODO(zhaoq): Tentatively disable the check until a bug is fixed.
/*
if !strings.Contains(f.Value, "application/grpc") {
d.err = StreamErrorf(codes.FailedPrecondition, "transport: received the unexpected header")
return
}
*/
case "grpc-status":
code, err := strconv.Atoi(f.Value)
if err != nil {
d.err = StreamErrorf(codes.Internal, "transport: malformed grpc-status: %v", err)
return
}
d.state.statusCode = codes.Code(code)
case "grpc-message":
d.state.statusDesc = f.Value
case "grpc-timeout":
d.state.timeoutSet = true
var err error
d.state.timeout, err = timeoutDecode(f.Value)
if err != nil {
d.err = StreamErrorf(codes.Internal, "transport: malformed time-out: %v", err)
return
}
case ":path":
d.state.method = f.Value
default:
if !isReservedHeader(f.Name) {
if f.Name == "user-agent" {
i := strings.LastIndex(f.Value, " ")
if i == -1 {
// There is no application user agent string being set.
return
}
// Extract the application user agent string.
f.Value = f.Value[:i]
}
if d.state.mdata == nil {
d.state.mdata = make(map[string][]string)
}
k, v, err := metadata.DecodeKeyValue(f.Name, f.Value)
if err != nil {
grpclog.Printf("Failed to decode (%q, %q): %v", f.Name, f.Value, err)
return
}
d.state.mdata[k] = append(d.state.mdata[k], v)
}
}
})
return d
}
func (d *hpackDecoder) decodeClientHTTP2Headers(frame headerFrame) (endHeaders bool, err error) {
d.err = nil
_, err = d.h.Write(frame.HeaderBlockFragment())
if err != nil {
err = StreamErrorf(codes.Internal, "transport: HPACK header decode error: %v", err)
}
if frame.HeadersEnded() {
if closeErr := d.h.Close(); closeErr != nil && err == nil {
err = StreamErrorf(codes.Internal, "transport: HPACK decoder close error: %v", closeErr)
}
endHeaders = true
}
if err == nil && d.err != nil {
err = d.err
}
return
}
func (d *hpackDecoder) decodeServerHTTP2Headers(frame headerFrame) (endHeaders bool, err error) {
d.err = nil
_, err = d.h.Write(frame.HeaderBlockFragment())
if err != nil {
err = StreamErrorf(codes.Internal, "transport: HPACK header decode error: %v", err)
}
if frame.HeadersEnded() {
if closeErr := d.h.Close(); closeErr != nil && err == nil {
err = StreamErrorf(codes.Internal, "transport: HPACK decoder close error: %v", closeErr)
}
endHeaders = true
}
if err == nil && d.err != nil {
err = d.err
}
return
}
type timeoutUnit uint8
const (
hour timeoutUnit = 'H'
minute timeoutUnit = 'M'
second timeoutUnit = 'S'
millisecond timeoutUnit = 'm'
microsecond timeoutUnit = 'u'
nanosecond timeoutUnit = 'n'
)
func timeoutUnitToDuration(u timeoutUnit) (d time.Duration, ok bool) {
switch u {
case hour:
return time.Hour, true
case minute:
return time.Minute, true
case second:
return time.Second, true
case millisecond:
return time.Millisecond, true
case microsecond:
return time.Microsecond, true
case nanosecond:
return time.Nanosecond, true
default:
}
return
}
const maxTimeoutValue int64 = 100000000 - 1
// div does integer division and round-up the result. Note that this is
// equivalent to (d+r-1)/r but has less chance to overflow.
func div(d, r time.Duration) int64 {
if m := d % r; m > 0 {
return int64(d/r + 1)
}
return int64(d / r)
}
// TODO(zhaoq): It is the simplistic and not bandwidth efficient. Improve it.
func timeoutEncode(t time.Duration) string {
if d := div(t, time.Nanosecond); d <= maxTimeoutValue {
return strconv.FormatInt(d, 10) + "n"
}
if d := div(t, time.Microsecond); d <= maxTimeoutValue {
return strconv.FormatInt(d, 10) + "u"
}
if d := div(t, time.Millisecond); d <= maxTimeoutValue {
return strconv.FormatInt(d, 10) + "m"
}
if d := div(t, time.Second); d <= maxTimeoutValue {
return strconv.FormatInt(d, 10) + "S"
}
if d := div(t, time.Minute); d <= maxTimeoutValue {
return strconv.FormatInt(d, 10) + "M"
}
// Note that maxTimeoutValue * time.Hour > MaxInt64.
return strconv.FormatInt(div(t, time.Hour), 10) + "H"
}
func timeoutDecode(s string) (time.Duration, error) {
size := len(s)
if size < 2 {
return 0, fmt.Errorf("transport: timeout string is too short: %q", s)
}
unit := timeoutUnit(s[size-1])
d, ok := timeoutUnitToDuration(unit)
if !ok {
return 0, fmt.Errorf("transport: timeout unit is not recognized: %q", s)
}
t, err := strconv.ParseInt(s[:size-1], 10, 64)
if err != nil {
return 0, err
}
return d * time.Duration(t), nil
}
type framer struct {
numWriters int32
reader io.Reader
writer *bufio.Writer
fr *http2.Framer
}
func newFramer(conn net.Conn) *framer {
f := &framer{
reader: conn,
writer: bufio.NewWriterSize(conn, http2IOBufSize),
}
f.fr = http2.NewFramer(f.writer, f.reader)
return f
}
func (f *framer) adjustNumWriters(i int32) int32 {
return atomic.AddInt32(&f.numWriters, i)
}
// The following writeXXX functions can only be called when the caller gets
// unblocked from writableChan channel (i.e., owns the privilege to write).
func (f *framer) writeContinuation(forceFlush bool, streamID uint32, endHeaders bool, headerBlockFragment []byte) error {
if err := f.fr.WriteContinuation(streamID, endHeaders, headerBlockFragment); err != nil {
return err
}
if forceFlush {
return f.writer.Flush()
}
return nil
}
func (f *framer) writeData(forceFlush bool, streamID uint32, endStream bool, data []byte) error {
if err := f.fr.WriteData(streamID, endStream, data); err != nil {
return err
}
if forceFlush {
return f.writer.Flush()
}
return nil
}
func (f *framer) writeGoAway(forceFlush bool, maxStreamID uint32, code http2.ErrCode, debugData []byte) error {
if err := f.fr.WriteGoAway(maxStreamID, code, debugData); err != nil {
return err
}
if forceFlush {
return f.writer.Flush()
}
return nil
}
func (f *framer) writeHeaders(forceFlush bool, p http2.HeadersFrameParam) error {
if err := f.fr.WriteHeaders(p); err != nil {
return err
}
if forceFlush {
return f.writer.Flush()
}
return nil
}
func (f *framer) writePing(forceFlush, ack bool, data [8]byte) error {
if err := f.fr.WritePing(ack, data); err != nil {
return err
}
if forceFlush {
return f.writer.Flush()
}
return nil
}
func (f *framer) writePriority(forceFlush bool, streamID uint32, p http2.PriorityParam) error {
if err := f.fr.WritePriority(streamID, p); err != nil {
return err
}
if forceFlush {
return f.writer.Flush()
}
return nil
}
func (f *framer) writePushPromise(forceFlush bool, p http2.PushPromiseParam) error {
if err := f.fr.WritePushPromise(p); err != nil {
return err
}
if forceFlush {
return f.writer.Flush()
}
return nil
}
func (f *framer) writeRSTStream(forceFlush bool, streamID uint32, code http2.ErrCode) error {
if err := f.fr.WriteRSTStream(streamID, code); err != nil {
return err
}
if forceFlush {
return f.writer.Flush()
}
return nil
}
func (f *framer) writeSettings(forceFlush bool, settings ...http2.Setting) error {
if err := f.fr.WriteSettings(settings...); err != nil {
return err
}
if forceFlush {
return f.writer.Flush()
}
return nil
}
func (f *framer) writeSettingsAck(forceFlush bool) error {
if err := f.fr.WriteSettingsAck(); err != nil {
return err
}
if forceFlush {
return f.writer.Flush()
}
return nil
}
func (f *framer) writeWindowUpdate(forceFlush bool, streamID, incr uint32) error {
if err := f.fr.WriteWindowUpdate(streamID, incr); err != nil {
return err
}
if forceFlush {
return f.writer.Flush()
}
return nil
}
func (f *framer) flushWrite() error {
return f.writer.Flush()
}
func (f *framer) readFrame() (http2.Frame, error) {
return f.fr.ReadFrame()
}
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/transport/http_util_test.go 0000664 0000000 0000000 00000005554 12603607541 0025156 0 ustar 00root root 0000000 0000000 /*
*
* Copyright 2014, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
package transport
import (
"fmt"
"testing"
"time"
)
func TestTimeoutEncode(t *testing.T) {
for _, test := range []struct {
in string
out string
}{
{"12345678ns", "12345678n"},
{"123456789ns", "123457u"},
{"12345678us", "12345678u"},
{"123456789us", "123457m"},
{"12345678ms", "12345678m"},
{"123456789ms", "123457S"},
{"12345678s", "12345678S"},
{"123456789s", "2057614M"},
{"12345678m", "12345678M"},
{"123456789m", "2057614H"},
} {
d, err := time.ParseDuration(test.in)
if err != nil {
t.Fatalf("failed to parse duration string %s: %v", test.in, err)
}
out := timeoutEncode(d)
if out != test.out {
t.Fatalf("timeoutEncode(%s) = %s, want %s", test.in, out, test.out)
}
}
}
func TestTimeoutDecode(t *testing.T) {
for _, test := range []struct {
// input
s string
// output
d time.Duration
err error
}{
{"1234S", time.Second * 1234, nil},
{"1234x", 0, fmt.Errorf("transport: timeout unit is not recognized: %q", "1234x")},
{"1", 0, fmt.Errorf("transport: timeout string is too short: %q", "1")},
{"", 0, fmt.Errorf("transport: timeout string is too short: %q", "")},
} {
d, err := timeoutDecode(test.s)
if d != test.d || fmt.Sprint(err) != fmt.Sprint(test.err) {
t.Fatalf("timeoutDecode(%q) = %d, %v, want %d, %v", test.s, int64(d), err, int64(test.d), test.err)
}
}
}
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/transport/testdata/ 0000775 0000000 0000000 00000000000 12603607541 0023354 5 ustar 00root root 0000000 0000000 grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/transport/testdata/ca.pem 0000664 0000000 0000000 00000001442 12603607541 0024443 0 ustar 00root root 0000000 0000000 -----BEGIN CERTIFICATE-----
MIICIzCCAYwCCQCFTbF7XNSvvjANBgkqhkiG9w0BAQUFADBWMQswCQYDVQQGEwJB
VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0
cyBQdHkgTHRkMQ8wDQYDVQQDDAZ0ZXN0Y2EwHhcNMTQwNzE3MjMxNzUxWhcNMjQw
NzE0MjMxNzUxWjBWMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEh
MB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMQ8wDQYDVQQDDAZ0ZXN0
Y2EwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMBA3wVeTGHZR1Rye/i+J8a2
cu5gXwFV6TnObzGM7bLFCO5i9v4mLo4iFzPsHmWDUxKS3Y8iXbu0eYBlLoNY0lSv
xDx33O+DuwMmVN+DzSD+Eod9zfvwOWHsazYCZT2PhNxnVWIuJXViY4JAHUGodjx+
QAi6yCAurUZGvYXGgZSBAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAQoQVD8bwdtWJ
AniGBwcCfqYyH+/KpA10AcebJVVTyYbYvI9Q8d6RSVu4PZy9OALHR/QrWBdYTAyz
fNAmc2cmdkSRJzjhIaOstnQy1J+Fk0T9XyvQtq499yFbq9xogUVlEGH62xP6vH0Y
5ukK//dCPAzA11YuX2rnex0JhuTQfcI=
-----END CERTIFICATE-----
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/transport/testdata/server1.key 0000664 0000000 0000000 00000001567 12603607541 0025466 0 ustar 00root root 0000000 0000000 -----BEGIN RSA PRIVATE KEY-----
MIICWwIBAAKBgQDhwxUnKCwlSaWAwzOB2LSHVegJHv7DDWminTg4wzLLsf+LQ8nZ
bpjfn5vgIzxCuRh4Rp9QYM5FhfrJX9wcYawP/HTbJ7p7LVQO2QYAP+akMTHxgKuM
BzVV++3wWToKfVZUjFX8nfTfGMGwWAHJDnlEGnU4tl9UujoCV4ENJtzFoQIDAQAB
AoGAJ+6hpzNr24yTQZtFWQpDpEyFplddKJMOxDya3S9ppK3vTWrIITV2xNcucw7I
ceTbdyrGsyjsU0/HdCcIf9ym2jfmGLUwmyhltKVw0QYcFB0XLkc0nI5YvEYoeVDg
omZIXn1E3EW+sSIWSbkMu9bY2kstKXR2UZmMgWDtmBEPMaECQQD6yT4TAZM5hGBb
ciBKgMUP6PwOhPhOMPIvijO50Aiu6iuCV88l1QIy38gWVhxjNrq6P346j4IBg+kB
9alwpCODAkEA5nSnm9k6ykYeQWNS0fNWiRinCdl23A7usDGSuKKlm019iomJ/Rgd
MKDOp0q/2OostbteOWM2MRFf4jMH3wyVCwJAfAdjJ8szoNKTRSagSbh9vWygnB2v
IByc6l4TTuZQJRGzCveafz9lovuB3WohCABdQRd9ukCXL2CpsEpqzkafOQJAJUjc
USedDlq3zGZwYM1Yw8d8RuirBUFZNqJelYai+nRYClDkRVFgb5yksoYycbq5TxGo
VeqKOvgPpj4RWPHlLwJAGUMk3bqT91xBUCnLRs/vfoCpHpg6eywQTBDAV6xkyz4a
RH3I7/+yj3ZxR2JoWHgUwZ7lZk1VnhffFye7SBXyag==
-----END RSA PRIVATE KEY-----
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/transport/testdata/server1.pem 0000664 0000000 0000000 00000001704 12603607541 0025450 0 ustar 00root root 0000000 0000000 -----BEGIN CERTIFICATE-----
MIICmzCCAgSgAwIBAgIBAzANBgkqhkiG9w0BAQUFADBWMQswCQYDVQQGEwJBVTET
MBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQ
dHkgTHRkMQ8wDQYDVQQDDAZ0ZXN0Y2EwHhcNMTQwNzIyMDYwMDU3WhcNMjQwNzE5
MDYwMDU3WjBkMQswCQYDVQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNV
BAcTB0NoaWNhZ28xFDASBgNVBAoTC0dvb2dsZSBJbmMuMRowGAYDVQQDFBEqLnRl
c3QuZ29vZ2xlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA4cMVJygs
JUmlgMMzgdi0h1XoCR7+ww1pop04OMMyy7H/i0PJ2W6Y35+b4CM8QrkYeEafUGDO
RYX6yV/cHGGsD/x02ye6ey1UDtkGAD/mpDEx8YCrjAc1Vfvt8Fk6Cn1WVIxV/J30
3xjBsFgByQ55RBp1OLZfVLo6AleBDSbcxaECAwEAAaNrMGkwCQYDVR0TBAIwADAL
BgNVHQ8EBAMCBeAwTwYDVR0RBEgwRoIQKi50ZXN0Lmdvb2dsZS5mcoIYd2F0ZXJ6
b29pLnRlc3QuZ29vZ2xlLmJlghIqLnRlc3QueW91dHViZS5jb22HBMCoAQMwDQYJ
KoZIhvcNAQEFBQADgYEAM2Ii0LgTGbJ1j4oqX9bxVcxm+/R5Yf8oi0aZqTJlnLYS
wXcBykxTx181s7WyfJ49WwrYXo78zTDAnf1ma0fPq3e4mpspvyndLh1a+OarHa1e
aT0DIIYk7qeEa1YcVljx2KyLd0r1BBAfrwyGaEPVeJQVYWaOJRU2we/KD4ojf9s=
-----END CERTIFICATE-----
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/transport/transport.go 0000664 0000000 0000000 00000034625 12603607541 0024140 0 ustar 00root root 0000000 0000000 /*
*
* Copyright 2014, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/*
Package transport defines and implements message oriented communication channel
to complete various transactions (e.g., an RPC).
*/
package transport // import "google.golang.org/grpc/transport"
import (
"bytes"
"errors"
"fmt"
"io"
"net"
"strings"
"sync"
"time"
"golang.org/x/net/context"
"golang.org/x/net/trace"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/metadata"
)
// 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
}
// recvMsg represents the received msg from the transport. All transport
// protocol specific info has been removed.
type recvMsg struct {
data []byte
// nil: received some data
// io.EOF: stream is completed. data is nil.
// other non-nil error: transport failure. data is nil.
err error
}
func (recvMsg) isItem() bool {
return true
}
// All items in an out of a recvBuffer should be the same type.
type item interface {
isItem() bool
}
// recvBuffer is an unbounded channel of item.
type recvBuffer struct {
c chan item
mu sync.Mutex
backlog []item
}
func newRecvBuffer() *recvBuffer {
b := &recvBuffer{
c: make(chan item, 1),
}
return b
}
func (b *recvBuffer) put(r item) {
b.mu.Lock()
defer b.mu.Unlock()
b.backlog = append(b.backlog, r)
select {
case b.c <- b.backlog[0]:
b.backlog = b.backlog[1:]
default:
}
}
func (b *recvBuffer) load() {
b.mu.Lock()
defer b.mu.Unlock()
if len(b.backlog) > 0 {
select {
case b.c <- b.backlog[0]:
b.backlog = b.backlog[1:]
default:
}
}
}
// get returns the channel that receives an item in the buffer.
//
// Upon receipt of an item, the caller should call load to send another
// item onto the channel if there is any.
func (b *recvBuffer) get() <-chan item {
return b.c
}
// recvBufferReader implements io.Reader interface to read the data from
// recvBuffer.
type recvBufferReader struct {
ctx context.Context
recv *recvBuffer
last *bytes.Reader // Stores the remaining data in the previous calls.
err error
}
// Read reads the next len(p) bytes from last. If last is drained, it tries to
// read additional data from recv. It blocks if there no additional data available
// in recv. If Read returns any non-nil error, it will continue to return that error.
func (r *recvBufferReader) Read(p []byte) (n int, err error) {
if r.err != nil {
return 0, r.err
}
defer func() { r.err = err }()
if r.last != nil && r.last.Len() > 0 {
// Read remaining data left in last call.
return r.last.Read(p)
}
select {
case <-r.ctx.Done():
return 0, ContextErr(r.ctx.Err())
case i := <-r.recv.get():
r.recv.load()
m := i.(*recvMsg)
if m.err != nil {
return 0, m.err
}
r.last = bytes.NewReader(m.data)
return r.last.Read(p)
}
}
type streamState uint8
const (
streamActive streamState = iota
streamWriteDone // EndStream sent
streamReadDone // EndStream received
streamDone // sendDone and recvDone or RSTStreamFrame is sent or received.
)
// Stream represents an RPC in the transport layer.
type Stream struct {
id uint32
// nil for client side Stream.
st ServerTransport
// ctx is the associated context of the stream.
ctx context.Context
cancel context.CancelFunc
// method records the associated RPC method of the stream.
method string
buf *recvBuffer
dec io.Reader
fc *inFlow
recvQuota uint32
// The accumulated inbound quota pending for window update.
updateQuota uint32
// The handler to control the window update procedure for both this
// particular stream and the associated transport.
windowHandler func(int)
sendQuotaPool *quotaPool
// Close headerChan to indicate the end of reception of header metadata.
headerChan chan struct{}
// header caches the received header metadata.
header metadata.MD
// The key-value map of trailer metadata.
trailer metadata.MD
mu sync.RWMutex // guard the following
// headerOK becomes true from the first header is about to send.
headerOk bool
state streamState
// true iff headerChan is closed. Used to avoid closing headerChan
// multiple times.
headerDone bool
// the status received from the server.
statusCode codes.Code
statusDesc string
// tracing information
tr trace.Trace
}
// Header acquires the key-value pairs of header metadata once it
// is available. It blocks until i) the metadata is ready or ii) there is no
// header metadata or iii) the stream is cancelled/expired.
func (s *Stream) Header() (metadata.MD, error) {
select {
case <-s.ctx.Done():
return nil, ContextErr(s.ctx.Err())
case <-s.headerChan:
return s.header.Copy(), nil
}
}
// Trailer returns the cached trailer metedata. Note that if it is not called
// after the entire stream is done, it could return an empty MD. Client
// side only.
func (s *Stream) Trailer() metadata.MD {
s.mu.RLock()
defer s.mu.RUnlock()
return s.trailer.Copy()
}
// ServerTransport returns the underlying ServerTransport for the stream.
// The client side stream always returns nil.
func (s *Stream) ServerTransport() ServerTransport {
return s.st
}
// Context returns the context of the stream.
func (s *Stream) Context() context.Context {
return s.ctx
}
// Trace returns the trace.Trace of the stream.
func (s *Stream) Trace() trace.Trace {
return s.tr
}
// Method returns the method for the stream.
func (s *Stream) Method() string {
return s.method
}
// StatusCode returns statusCode received from the server.
func (s *Stream) StatusCode() codes.Code {
return s.statusCode
}
// StatusDesc returns statusDesc received from the server.
func (s *Stream) StatusDesc() string {
return s.statusDesc
}
// ErrIllegalTrailerSet indicates that the trailer has already been set or it
// is too late to do so.
var ErrIllegalTrailerSet = errors.New("transport: trailer has been set")
// SetTrailer sets the trailer metadata which will be sent with the RPC status
// by the server. This can only be called at most once. Server side only.
func (s *Stream) SetTrailer(md metadata.MD) error {
s.mu.Lock()
defer s.mu.Unlock()
if s.trailer != nil {
return ErrIllegalTrailerSet
}
s.trailer = md.Copy()
return nil
}
func (s *Stream) write(m recvMsg) {
s.buf.put(&m)
}
// Read reads all the data available for this Stream from the transport and
// passes them into the decoder, which converts them into a gRPC message stream.
// The error is io.EOF when the stream is done or another non-nil error if
// the stream broke.
func (s *Stream) Read(p []byte) (n int, err error) {
n, err = s.dec.Read(p)
if err != nil {
return
}
s.windowHandler(n)
return
}
type key int
// The key to save transport.Stream in the context.
const streamKey = key(0)
// newContextWithStream creates a new context from ctx and attaches stream
// to it.
func newContextWithStream(ctx context.Context, stream *Stream) context.Context {
return context.WithValue(ctx, streamKey, stream)
}
// StreamFromContext returns the stream saved in ctx.
func StreamFromContext(ctx context.Context) (s *Stream, ok bool) {
s, ok = ctx.Value(streamKey).(*Stream)
return
}
// state of transport
type transportState int
const (
reachable transportState = iota
unreachable
closing
)
// NewServerTransport creates a ServerTransport with conn or non-nil error
// if it fails.
func NewServerTransport(protocol string, conn net.Conn, maxStreams uint32, authInfo credentials.AuthInfo, tracing bool) (ServerTransport, error) {
return newHTTP2Server(conn, maxStreams, authInfo, tracing)
}
// ConnectOptions covers all relevant options for dialing a server.
type ConnectOptions struct {
// UserAgent is the application user agent.
UserAgent string
// Dialer specifies how to dial a network address.
Dialer func(string, time.Duration) (net.Conn, error)
// AuthOptions stores the credentials required to setup a client connection and/or issue RPCs.
AuthOptions []credentials.Credentials
// Timeout specifies the timeout for dialing a client connection.
Timeout time.Duration
}
// NewClientTransport establishes the transport with the required ConnectOptions
// and returns it to the caller.
func NewClientTransport(target string, opts *ConnectOptions) (ClientTransport, error) {
return newHTTP2Client(target, opts)
}
// Options provides additional hints and information for message
// transmission.
type Options struct {
// Indicate whether it is the last piece for this stream.
Last bool
// The hint to transport impl whether the data could be buffered for
// batching write. Transport impl can feel free to ignore it.
Delay bool
}
// CallHdr carries the information of a particular RPC.
type CallHdr struct {
Host string // peer host
Method string // the operation to perform on the specified host
}
// ClientTransport is the common interface for all gRPC client side transport
// implementations.
type ClientTransport interface {
// Close tears down this transport. Once it returns, the transport
// should not be accessed any more. The caller must make sure this
// is called only once.
Close() error
// Write sends the data for the given stream. A nil stream indicates
// the write is to be performed on the transport as a whole.
Write(s *Stream, data []byte, opts *Options) error
// NewStream creates a Stream for an RPC.
NewStream(ctx context.Context, callHdr *CallHdr) (*Stream, error)
// CloseStream clears the footprint of a stream when the stream is
// not needed any more. The err indicates the error incurred when
// CloseStream is called. Must be called when a stream is finished
// unless the associated transport is closing.
CloseStream(stream *Stream, err error)
// Error returns a channel that is closed when some I/O error
// happens. Typically the caller should have a goroutine to monitor
// this in order to take action (e.g., close the current transport
// and create a new one) in error case. It should not return nil
// once the transport is initiated.
Error() <-chan struct{}
}
// ServerTransport is the common interface for all gRPC server side transport
// implementations.
type ServerTransport interface {
// WriteStatus sends the status of a stream to the client.
WriteStatus(s *Stream, statusCode codes.Code, statusDesc string) error
// Write sends the data for the given stream.
Write(s *Stream, data []byte, opts *Options) error
// WriteHeader sends the header metedata for the given stream.
WriteHeader(s *Stream, md metadata.MD) error
// HandleStreams receives incoming streams using the given handler.
HandleStreams(func(*Stream))
// Close tears down the transport. Once it is called, the transport
// should not be accessed any more. All the pending streams and their
// handlers will be terminated asynchronously.
Close() error
// RemoteAddr returns the remote network address.
RemoteAddr() net.Addr
}
// StreamErrorf creates an StreamError with the specified error code and description.
func StreamErrorf(c codes.Code, format string, a ...interface{}) StreamError {
return StreamError{
Code: c,
Desc: fmt.Sprintf(format, a...),
}
}
// ConnectionErrorf creates an ConnectionError with the specified error description.
func ConnectionErrorf(format string, a ...interface{}) ConnectionError {
return ConnectionError{
Desc: fmt.Sprintf(format, a...),
}
}
// ConnectionError is an error that results in the termination of the
// entire connection and the retry of all the active streams.
type ConnectionError struct {
Desc string
}
func (e ConnectionError) Error() string {
return fmt.Sprintf("connection error: desc = %q", e.Desc)
}
// Define some common ConnectionErrors.
var ErrConnClosing = ConnectionError{Desc: "transport is closing"}
// StreamError is an error that only affects one stream within a connection.
type StreamError struct {
Code codes.Code
Desc string
}
func (e StreamError) Error() string {
return fmt.Sprintf("stream error: code = %d desc = %q", e.Code, e.Desc)
}
// ContextErr converts the error from context package into a StreamError.
func ContextErr(err error) StreamError {
switch err {
case context.DeadlineExceeded:
return StreamErrorf(codes.DeadlineExceeded, "%v", err)
case context.Canceled:
return StreamErrorf(codes.Canceled, "%v", err)
}
panic(fmt.Sprintf("Unexpected error from context packet: %v", err))
}
// wait blocks until it can receive from ctx.Done, closing, or proceed.
// If it receives from ctx.Done, it returns 0, the StreamError for ctx.Err.
// If it receives from closing, it returns 0, ErrConnClosing.
// If it receives from proceed, it returns the received integer, nil.
func wait(ctx context.Context, closing <-chan struct{}, proceed <-chan int) (int, error) {
select {
case <-ctx.Done():
return 0, ContextErr(ctx.Err())
case <-closing:
return 0, ErrConnClosing
case i := <-proceed:
return i, nil
}
}
grpc-go-3e7b7e58f491074e9577050058fb95d2351a60b0/transport/transport_test.go 0000664 0000000 0000000 00000041336 12603607541 0025174 0 ustar 00root root 0000000 0000000 /*
*
* Copyright 2014, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
package transport
import (
"bytes"
"io"
"math"
"net"
"reflect"
"strconv"
"sync"
"testing"
"time"
"golang.org/x/net/context"
"golang.org/x/net/http2"
"google.golang.org/grpc/codes"
)
type server struct {
lis net.Listener
port string
// channel to signal server is ready to serve.
readyChan chan bool
mu sync.Mutex
conns map[ServerTransport]bool
}
var (
expectedRequest = []byte("ping")
expectedResponse = []byte("pong")
expectedRequestLarge = make([]byte, initialWindowSize*2)
expectedResponseLarge = make([]byte, initialWindowSize*2)
)
type testStreamHandler struct {
t ServerTransport
}
type hType int
const (
normal hType = iota
suspended
misbehaved
)
func (h *testStreamHandler) handleStream(t *testing.T, s *Stream) {
req := expectedRequest
resp := expectedResponse
if s.Method() == "foo.Large" {
req = expectedRequestLarge
resp = expectedResponseLarge
}
p := make([]byte, len(req))
_, err := io.ReadFull(s, p)
if err != nil || !bytes.Equal(p, req) {
if err == ErrConnClosing {
return
}
t.Fatalf("handleStream got error: %v, want ; result: %v, want %v", err, p, req)
}
// send a response back to the client.
h.t.Write(s, resp, &Options{})
// send the trailer to end the stream.
h.t.WriteStatus(s, codes.OK, "")
}
// handleStreamSuspension blocks until s.ctx is canceled.
func (h *testStreamHandler) handleStreamSuspension(s *Stream) {
<-s.ctx.Done()
}
func (h *testStreamHandler) handleStreamMisbehave(t *testing.T, s *Stream) {
conn, ok := s.ServerTransport().(*http2Server)
if !ok {
t.Fatalf("Failed to convert %v to *http2Server", s.ServerTransport())
}
size := 1
if s.Method() == "foo.MaxFrame" {
size = http2MaxFrameLen
}
// Drain the client side stream flow control window.
var sent int
for sent <= initialWindowSize {
<-conn.writableChan
if err := conn.framer.writeData(true, s.id, false, make([]byte, size)); err != nil {
conn.writableChan <- 0
break
}
conn.writableChan <- 0
sent += size
}
}
// start starts server. Other goroutines should block on s.readyChan for futher operations.
func (s *server) start(t *testing.T, port int, maxStreams uint32, ht hType) {
var err error
if port == 0 {
s.lis, err = net.Listen("tcp", ":0")
} else {
s.lis, err = net.Listen("tcp", ":"+strconv.Itoa(port))
}
if err != nil {
t.Fatalf("failed to listen: %v", err)
}
_, p, err := net.SplitHostPort(s.lis.Addr().String())
if err != nil {
t.Fatalf("failed to parse listener address: %v", err)
}
s.port = p
s.conns = make(map[ServerTransport]bool)
if s.readyChan != nil {
close(s.readyChan)
}
for {
conn, err := s.lis.Accept()
if err != nil {
return
}
transport, err := NewServerTransport("http2", conn, maxStreams, nil, false)
if err != nil {
return
}
s.mu.Lock()
if s.conns == nil {
s.mu.Unlock()
transport.Close()
return
}
s.conns[transport] = true
s.mu.Unlock()
h := &testStreamHandler{transport}
switch ht {
case suspended:
go transport.HandleStreams(h.handleStreamSuspension)
case misbehaved:
go transport.HandleStreams(func(s *Stream) {
h.handleStreamMisbehave(t, s)
})
default:
go transport.HandleStreams(func(s *Stream) {
h.handleStream(t, s)
})
}
}
}
func (s *server) wait(t *testing.T, timeout time.Duration) {
select {
case <-s.readyChan:
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, ht hType) (*server, ClientTransport) {
server := &server{readyChan: make(chan bool)}
go server.start(t, port, maxStreams, ht)
server.wait(t, 2*time.Second)
addr := "localhost:" + server.port
var (
ct ClientTransport
connErr error
)
ct, connErr = NewClientTransport(addr, &ConnectOptions{})
if connErr != nil {
t.Fatalf("failed to create transport: %v", connErr)
}
return server, ct
}
func TestClientSendAndReceive(t *testing.T) {
server, ct := setUp(t, 0, math.MaxUint32, normal)
callHdr := &CallHdr{
Host: "localhost",
Method: "foo.Small",
}
s1, err1 := ct.NewStream(context.Background(), callHdr)
if err1 != nil {
t.Fatalf("failed to open stream: %v", err1)
}
if s1.id != 1 {
t.Fatalf("wrong stream id: %d", s1.id)
}
s2, err2 := ct.NewStream(context.Background(), callHdr)
if err2 != nil {
t.Fatalf("failed to open stream: %v", err2)
}
if s2.id != 3 {
t.Fatalf("wrong stream id: %d", s2.id)
}
opts := Options{
Last: true,
Delay: false,
}
if err := ct.Write(s1, expectedRequest, &opts); err != nil {
t.Fatalf("failed to send data: %v", err)
}
p := make([]byte, len(expectedResponse))
_, recvErr := io.ReadFull(s1, p)
if recvErr != nil || !bytes.Equal(p, expectedResponse) {
t.Fatalf("Error: %v, want ; Result: %v, want %v", recvErr, p, expectedResponse)
}
_, recvErr = io.ReadFull(s1, p)
if recvErr != io.EOF {
t.Fatalf("Error: %v; want ", recvErr)
}
ct.Close()
server.stop()
}
func TestClientErrorNotify(t *testing.T) {
server, ct := setUp(t, 0, math.MaxUint32, normal)
go server.stop()
// ct.reader should detect the error and activate ct.Error().
<-ct.Error()
ct.Close()
}
func performOneRPC(ct ClientTransport) {
callHdr := &CallHdr{
Host: "localhost",
Method: "foo.Small",
}
s, err := ct.NewStream(context.Background(), callHdr)
if err != nil {
return
}
opts := Options{
Last: true,
Delay: false,
}
if err := ct.Write(s, expectedRequest, &opts); err == nil {
time.Sleep(5 * time.Millisecond)
// The following s.Recv()'s could error out because the
// underlying transport is gone.
//
// Read response
p := make([]byte, len(expectedResponse))
io.ReadFull(s, p)
// Read io.EOF
io.ReadFull(s, p)
}
}
func TestClientMix(t *testing.T) {
s, ct := setUp(t, 0, math.MaxUint32, normal)
go func(s *server) {
time.Sleep(5 * time.Second)
s.stop()
}(s)
go func(ct ClientTransport) {
<-ct.Error()
ct.Close()
}(ct)
for i := 0; i < 1000; i++ {
time.Sleep(10 * time.Millisecond)
go performOneRPC(ct)
}
}
func TestLargeMessage(t *testing.T) {
server, ct := setUp(t, 0, math.MaxUint32, normal)
callHdr := &CallHdr{
Host: "localhost",
Method: "foo.Large",
}
var wg sync.WaitGroup
for i := 0; i < 2; i++ {
wg.Add(1)
go func() {
defer wg.Done()
s, err := ct.NewStream(context.Background(), callHdr)
if err != nil {
t.Errorf("failed to open stream: %v", err)
}
if err := ct.Write(s, expectedRequestLarge, &Options{Last: true, Delay: false}); err != nil {
t.Errorf("failed to send data: %v", err)
}
p := make([]byte, len(expectedResponseLarge))
_, recvErr := io.ReadFull(s, p)
if recvErr != nil || !bytes.Equal(p, expectedResponseLarge) {
t.Errorf("Error: %v, want ; Result len: %d, want len %d", recvErr, len(p), len(expectedResponseLarge))
}
_, recvErr = io.ReadFull(s, p)
if recvErr != io.EOF {
t.Errorf("Error: %v; want ", recvErr)
}
}()
}
wg.Wait()
ct.Close()
server.stop()
}
func TestLargeMessageSuspension(t *testing.T) {
server, ct := setUp(t, 0, math.MaxUint32, suspended)
callHdr := &CallHdr{
Host: "localhost",
Method: "foo.Large",
}
// Set a long enough timeout for writing a large message out.
ctx, _ := context.WithTimeout(context.Background(), time.Second)
s, err := ct.NewStream(ctx, callHdr)
if err != nil {
t.Fatalf("failed to open stream: %v", err)
}
// Write should not be done successfully due to flow control.
err = ct.Write(s, expectedRequestLarge, &Options{Last: true, Delay: false})
expectedErr := StreamErrorf(codes.DeadlineExceeded, "%v", context.DeadlineExceeded)
if err == nil || err != expectedErr {
t.Fatalf("Write got %v, want %v", err, expectedErr)
}
ct.Close()
server.stop()
}
func TestMaxStreams(t *testing.T) {
server, ct := setUp(t, 0, 1, suspended)
callHdr := &CallHdr{
Host: "localhost",
Method: "foo.Large",
}
// Have a pending stream which takes all streams quota.
s, err := ct.NewStream(context.Background(), callHdr)
if err != nil {
t.Fatalf("Failed to open stream: %v", err)
}
cc, ok := ct.(*http2Client)
if !ok {
t.Fatalf("Failed to convert %v to *http2Client", ct)
}
done := make(chan struct{})
ch := make(chan int)
go func() {
for {
select {
case <-time.After(5 * time.Millisecond):
ch <- 0
case <-time.After(5 * time.Second):
close(done)
return
}
}
}()
for {
select {
case <-ch:
case <-done:
t.Fatalf("Client has not received the max stream setting in 5 seconds.")
}
cc.mu.Lock()
// cc.streamsQuota should be initialized once receiving the 1st setting frame from
// the server.
if cc.streamsQuota != nil {
cc.mu.Unlock()
select {
case <-cc.streamsQuota.acquire():
t.Fatalf("streamsQuota.acquire() becomes readable mistakenly.")
default:
if cc.streamsQuota.quota != 0 {
t.Fatalf("streamsQuota.quota got non-zero quota mistakenly.")
}
}
break
}
cc.mu.Unlock()
}
// Close the pending stream so that the streams quota becomes available for the next new stream.
ct.CloseStream(s, nil)
select {
case i := <-cc.streamsQuota.acquire():
if i != 1 {
t.Fatalf("streamsQuota.acquire() got %d quota, want 1.", i)
}
cc.streamsQuota.add(i)
default:
t.Fatalf("streamsQuota.acquire() is not readable.")
}
if _, err := ct.NewStream(context.Background(), callHdr); err != nil {
t.Fatalf("Failed to open stream: %v", err)
}
ct.Close()
server.stop()
}
func TestServerWithMisbehavedClient(t *testing.T) {
server, ct := setUp(t, 0, math.MaxUint32, suspended)
callHdr := &CallHdr{
Host: "localhost",
Method: "foo",
}
var sc *http2Server
// Wait until the server transport is setup.
for {
server.mu.Lock()
if len(server.conns) == 0 {
server.mu.Unlock()
time.Sleep(time.Millisecond)
continue
}
for k := range server.conns {
var ok bool
sc, ok = k.(*http2Server)
if !ok {
t.Fatalf("Failed to convert %v to *http2Server", k)
}
}
server.mu.Unlock()
break
}
cc, ok := ct.(*http2Client)
if !ok {
t.Fatalf("Failed to convert %v to *http2Client", ct)
}
// Test server behavior for violation of stream flow control window size restriction.
s, err := ct.NewStream(context.Background(), callHdr)
if err != nil {
t.Fatalf("Failed to open stream: %v", err)
}
var sent int
// Drain the stream flow control window
<-cc.writableChan
if err = cc.framer.writeData(true, s.id, false, make([]byte, http2MaxFrameLen)); err != nil {
t.Fatalf("Failed to write data: %v", err)
}
cc.writableChan <- 0
sent += http2MaxFrameLen
// Wait until the server creates the corresponding stream and receive some data.
var ss *Stream
for {
time.Sleep(time.Millisecond)
sc.mu.Lock()
if len(sc.activeStreams) == 0 {
sc.mu.Unlock()
continue
}
ss = sc.activeStreams[s.id]
sc.mu.Unlock()
ss.fc.mu.Lock()
if ss.fc.pendingData > 0 {
ss.fc.mu.Unlock()
break
}
ss.fc.mu.Unlock()
}
if ss.fc.pendingData != http2MaxFrameLen || ss.fc.pendingUpdate != 0 || sc.fc.pendingData != http2MaxFrameLen || sc.fc.pendingUpdate != 0 {
t.Fatalf("Server mistakenly updates inbound flow control params: got %d, %d, %d, %d; want %d, %d, %d, %d", ss.fc.pendingData, ss.fc.pendingUpdate, sc.fc.pendingData, sc.fc.pendingUpdate, http2MaxFrameLen, 0, http2MaxFrameLen, 0)
}
// Keep sending until the server inbound window is drained for that stream.
for sent <= initialWindowSize {
<-cc.writableChan
if err = cc.framer.writeData(true, s.id, false, make([]byte, 1)); err != nil {
t.Fatalf("Failed to write data: %v", err)
}
cc.writableChan <- 0
sent++
}
// Server sent a resetStream for s already.
code := http2RSTErrConvTab[http2.ErrCodeFlowControl]
if _, err := io.ReadFull(s, make([]byte, 1)); err != io.EOF || s.statusCode != code {
t.Fatalf("%v got err %v with statusCode %d, want err with statusCode %d", s, err, s.statusCode, code)
}
if ss.fc.pendingData != 0 || ss.fc.pendingUpdate != 0 || sc.fc.pendingData != 0 || sc.fc.pendingUpdate != initialWindowSize {
t.Fatalf("Server mistakenly resets inbound flow control params: got %d, %d, %d, %d; want 0, 0, 0, %d", ss.fc.pendingData, ss.fc.pendingUpdate, sc.fc.pendingData, sc.fc.pendingUpdate, initialWindowSize)
}
ct.CloseStream(s, nil)
// Test server behavior for violation of connection flow control window size restriction.
//
// Keep creating new streams until the connection window is drained on the server and
// the server tears down the connection.
for {
s, err := ct.NewStream(context.Background(), callHdr)
if err != nil {
// The server tears down the connection.
break
}
<-cc.writableChan
cc.framer.writeData(true, s.id, true, make([]byte, http2MaxFrameLen))
cc.writableChan <- 0
}
ct.Close()
server.stop()
}
func TestClientWithMisbehavedServer(t *testing.T) {
server, ct := setUp(t, 0, math.MaxUint32, misbehaved)
callHdr := &CallHdr{
Host: "localhost",
Method: "foo",
}
conn, ok := ct.(*http2Client)
if !ok {
t.Fatalf("Failed to convert %v to *http2Client", ct)
}
// Test the logic for the violation of stream flow control window size restriction.
s, err := ct.NewStream(context.Background(), callHdr)
if err != nil {
t.Fatalf("Failed to open stream: %v", err)
}
if err := ct.Write(s, expectedRequest, &Options{Last: true, Delay: false}); err != nil {
t.Fatalf("Failed to write: %v", err)
}
// Read without window update.
for {
p := make([]byte, http2MaxFrameLen)
if _, err = s.dec.Read(p); err != nil {
break
}
}
if s.fc.pendingData != initialWindowSize || s.fc.pendingUpdate != 0 || conn.fc.pendingData != initialWindowSize || conn.fc.pendingUpdate != 0 {
t.Fatalf("Client mistakenly updates inbound flow control params: got %d, %d, %d, %d; want %d, %d, %d, %d", s.fc.pendingData, s.fc.pendingUpdate, conn.fc.pendingData, conn.fc.pendingUpdate, initialWindowSize, 0, initialWindowSize, 0)
}
if err != io.EOF || s.statusCode != codes.Internal {
t.Fatalf("Got err %v and the status code %d, want and the code %d", err, s.statusCode, codes.Internal)
}
conn.CloseStream(s, err)
if s.fc.pendingData != 0 || s.fc.pendingUpdate != 0 || conn.fc.pendingData != 0 || conn.fc.pendingUpdate != initialWindowSize {
t.Fatalf("Client mistakenly resets inbound flow control params: got %d, %d, %d, %d; want 0, 0, 0, %d", s.fc.pendingData, s.fc.pendingUpdate, conn.fc.pendingData, conn.fc.pendingUpdate, initialWindowSize)
}
// Test the logic for the violation of the connection flow control window size restriction.
//
// Generate enough streams to drain the connection window.
callHdr = &CallHdr{
Host: "localhost",
Method: "foo.MaxFrame",
}
for i := 0; i < int(initialConnWindowSize/initialWindowSize+10); i++ {
s, err := ct.NewStream(context.Background(), callHdr)
if err != nil {
break
}
if err := ct.Write(s, expectedRequest, &Options{Last: true, Delay: false}); err != nil {
break
}
}
// http2Client.errChan is closed due to connection flow control window size violation.
<-conn.Error()
ct.Close()
server.stop()
}
func TestStreamContext(t *testing.T) {
expectedStream := Stream{}
ctx := newContextWithStream(context.Background(), &expectedStream)
s, ok := StreamFromContext(ctx)
if !ok || !reflect.DeepEqual(expectedStream, *s) {
t.Fatalf("GetStreamFromContext(%v) = %v, %t, want: %v, true", ctx, *s, ok, expectedStream)
}
}