mirror of
https://github.com/netbirdio/dex.git
synced 2026-05-22 18:43:53 -07:00
@@ -46,13 +46,6 @@ lint:
|
||||
golint $$package; \
|
||||
done
|
||||
|
||||
# TODO(ericchiang): Grab protoc as well.
|
||||
grpc: bin/protoc-gen-go
|
||||
@protoc --go_out=plugins=grpc:. ./api/apipb/*.proto
|
||||
|
||||
bin/protoc-gen-go:
|
||||
@go install ${REPO_PATH}/vendor/github.com/golang/protobuf/protoc-gen-go
|
||||
|
||||
clean:
|
||||
@rm bin/*
|
||||
|
||||
@@ -60,4 +53,4 @@ testall: testrace vet fmt lint
|
||||
|
||||
FORCE:
|
||||
|
||||
.PHONY: test testrace vet fmt lint testall grpc
|
||||
.PHONY: test testrace vet fmt lint testall
|
||||
|
||||
-136
@@ -1,136 +0,0 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/coreos/poke/api/apipb"
|
||||
"github.com/coreos/poke/storage"
|
||||
)
|
||||
|
||||
// NewServer returns a gRPC server for talking to a storage.
|
||||
func NewServer(s storage.Storage) apipb.StorageServer {
|
||||
return &server{s}
|
||||
}
|
||||
|
||||
type server struct {
|
||||
storage storage.Storage
|
||||
}
|
||||
|
||||
func fromPBClient(client *apipb.Client) storage.Client {
|
||||
return storage.Client{
|
||||
ID: client.Id,
|
||||
Secret: client.Secret,
|
||||
RedirectURIs: client.RedirectUris,
|
||||
TrustedPeers: client.TrustedPeers,
|
||||
Public: client.Public,
|
||||
Name: client.Name,
|
||||
LogoURL: client.LogoUrl,
|
||||
}
|
||||
}
|
||||
|
||||
func toPBClient(client storage.Client) *apipb.Client {
|
||||
return &apipb.Client{
|
||||
Id: client.ID,
|
||||
Secret: client.Secret,
|
||||
RedirectUris: client.RedirectURIs,
|
||||
TrustedPeers: client.TrustedPeers,
|
||||
Public: client.Public,
|
||||
Name: client.Name,
|
||||
LogoUrl: client.LogoURL,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *server) CreateClient(ctx context.Context, req *apipb.CreateClientReq) (*apipb.CreateClientResp, error) {
|
||||
// TODO(ericchiang): Create a more centralized strategy for creating client IDs
|
||||
// and secrets which are restricted based on the storage.
|
||||
client := fromPBClient(req.Client)
|
||||
if client.ID == "" {
|
||||
client.ID = storage.NewID()
|
||||
}
|
||||
if client.Secret == "" {
|
||||
client.Secret = storage.NewID() + storage.NewID()
|
||||
}
|
||||
|
||||
if err := s.storage.CreateClient(client); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &apipb.CreateClientResp{Client: toPBClient(client)}, nil
|
||||
}
|
||||
|
||||
func (s *server) UpdateClient(ctx context.Context, req *apipb.UpdateClientReq) (*apipb.UpdateClientResp, error) {
|
||||
switch {
|
||||
case req.Id == "":
|
||||
return nil, errors.New("no ID supplied")
|
||||
case req.MakePublic && req.MakePrivate:
|
||||
return nil, errors.New("cannot both make public and private")
|
||||
case req.MakePublic && len(req.RedirectUris) != 0:
|
||||
return nil, errors.New("redirect uris supplied for a public client")
|
||||
}
|
||||
|
||||
var client *storage.Client
|
||||
updater := func(old storage.Client) (storage.Client, error) {
|
||||
if req.MakePublic {
|
||||
old.Public = true
|
||||
}
|
||||
if req.MakePrivate {
|
||||
old.Public = false
|
||||
}
|
||||
if req.Secret != "" {
|
||||
old.Secret = req.Secret
|
||||
}
|
||||
if req.Name != "" {
|
||||
old.Name = req.Name
|
||||
}
|
||||
if req.LogoUrl != "" {
|
||||
old.LogoURL = req.LogoUrl
|
||||
}
|
||||
if len(req.RedirectUris) != 0 {
|
||||
if old.Public {
|
||||
return old, errors.New("public clients cannot have redirect URIs")
|
||||
}
|
||||
old.RedirectURIs = req.RedirectUris
|
||||
}
|
||||
client = &old
|
||||
return old, nil
|
||||
}
|
||||
|
||||
if err := s.storage.UpdateClient(req.Id, updater); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &apipb.UpdateClientResp{Client: toPBClient(*client)}, nil
|
||||
}
|
||||
|
||||
func (s *server) DeleteClient(ctx context.Context, req *apipb.DeleteClientReq) (*apipb.DeleteClientReq, error) {
|
||||
if req.Id == "" {
|
||||
return nil, errors.New("no client ID supplied")
|
||||
}
|
||||
if err := s.storage.DeleteClient(req.Id); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &apipb.DeleteClientReq{}, nil
|
||||
}
|
||||
|
||||
func (s *server) ListClients(ctx context.Context, req *apipb.ListClientsReq) (*apipb.ListClientsResp, error) {
|
||||
clients, err := s.storage.ListClients()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp := make([]*apipb.Client, len(clients))
|
||||
for i, client := range clients {
|
||||
resp[i] = toPBClient(client)
|
||||
}
|
||||
return &apipb.ListClientsResp{Clients: resp}, nil
|
||||
}
|
||||
|
||||
func (s *server) GetClient(ctx context.Context, req *apipb.GetClientReq) (*apipb.GetClientResp, error) {
|
||||
if req.Id == "" {
|
||||
return nil, errors.New("no client ID supplied")
|
||||
}
|
||||
client, err := s.storage.GetClient(req.Id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &apipb.GetClientResp{Client: toPBClient(client)}, nil
|
||||
}
|
||||
@@ -1,443 +0,0 @@
|
||||
// Code generated by protoc-gen-go.
|
||||
// source: api/apipb/api.proto
|
||||
// DO NOT EDIT!
|
||||
|
||||
/*
|
||||
Package apipb is a generated protocol buffer package.
|
||||
|
||||
It is generated from these files:
|
||||
api/apipb/api.proto
|
||||
|
||||
It has these top-level messages:
|
||||
Client
|
||||
CreateClientReq
|
||||
CreateClientResp
|
||||
UpdateClientReq
|
||||
UpdateClientResp
|
||||
ListClientsReq
|
||||
ListClientsResp
|
||||
DeleteClientReq
|
||||
DeleteClientResp
|
||||
GetClientReq
|
||||
GetClientResp
|
||||
*/
|
||||
package apipb
|
||||
|
||||
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
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||
|
||||
type Client struct {
|
||||
Id string `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"`
|
||||
Secret string `protobuf:"bytes,2,opt,name=secret" json:"secret,omitempty"`
|
||||
RedirectUris []string `protobuf:"bytes,3,rep,name=redirect_uris,json=redirectUris" json:"redirect_uris,omitempty"`
|
||||
TrustedPeers []string `protobuf:"bytes,4,rep,name=trusted_peers,json=trustedPeers" json:"trusted_peers,omitempty"`
|
||||
Public bool `protobuf:"varint,5,opt,name=public" json:"public,omitempty"`
|
||||
Name string `protobuf:"bytes,6,opt,name=name" json:"name,omitempty"`
|
||||
LogoUrl string `protobuf:"bytes,7,opt,name=logo_url,json=logoUrl" json:"logo_url,omitempty"`
|
||||
}
|
||||
|
||||
func (m *Client) Reset() { *m = Client{} }
|
||||
func (m *Client) String() string { return proto.CompactTextString(m) }
|
||||
func (*Client) ProtoMessage() {}
|
||||
func (*Client) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
|
||||
|
||||
type CreateClientReq struct {
|
||||
Client *Client `protobuf:"bytes,1,opt,name=client" json:"client,omitempty"`
|
||||
}
|
||||
|
||||
func (m *CreateClientReq) Reset() { *m = CreateClientReq{} }
|
||||
func (m *CreateClientReq) String() string { return proto.CompactTextString(m) }
|
||||
func (*CreateClientReq) ProtoMessage() {}
|
||||
func (*CreateClientReq) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
|
||||
|
||||
func (m *CreateClientReq) GetClient() *Client {
|
||||
if m != nil {
|
||||
return m.Client
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type CreateClientResp struct {
|
||||
Client *Client `protobuf:"bytes,1,opt,name=client" json:"client,omitempty"`
|
||||
}
|
||||
|
||||
func (m *CreateClientResp) Reset() { *m = CreateClientResp{} }
|
||||
func (m *CreateClientResp) String() string { return proto.CompactTextString(m) }
|
||||
func (*CreateClientResp) ProtoMessage() {}
|
||||
func (*CreateClientResp) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} }
|
||||
|
||||
func (m *CreateClientResp) GetClient() *Client {
|
||||
if m != nil {
|
||||
return m.Client
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type UpdateClientReq struct {
|
||||
Id string `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"`
|
||||
// Empty strings indicate that string fields should not be updated.
|
||||
Secret string `protobuf:"bytes,2,opt,name=secret" json:"secret,omitempty"`
|
||||
Name string `protobuf:"bytes,3,opt,name=name" json:"name,omitempty"`
|
||||
LogoUrl string `protobuf:"bytes,4,opt,name=logo_url,json=logoUrl" json:"logo_url,omitempty"`
|
||||
MakePublic bool `protobuf:"varint,5,opt,name=make_public,json=makePublic" json:"make_public,omitempty"`
|
||||
MakePrivate bool `protobuf:"varint,6,opt,name=make_private,json=makePrivate" json:"make_private,omitempty"`
|
||||
// If no redirect URIs are specified, the current redirect URIs are preserved.
|
||||
RedirectUris []string `protobuf:"bytes,7,rep,name=redirect_uris,json=redirectUris" json:"redirect_uris,omitempty"`
|
||||
}
|
||||
|
||||
func (m *UpdateClientReq) Reset() { *m = UpdateClientReq{} }
|
||||
func (m *UpdateClientReq) String() string { return proto.CompactTextString(m) }
|
||||
func (*UpdateClientReq) ProtoMessage() {}
|
||||
func (*UpdateClientReq) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} }
|
||||
|
||||
type UpdateClientResp struct {
|
||||
Client *Client `protobuf:"bytes,1,opt,name=client" json:"client,omitempty"`
|
||||
}
|
||||
|
||||
func (m *UpdateClientResp) Reset() { *m = UpdateClientResp{} }
|
||||
func (m *UpdateClientResp) String() string { return proto.CompactTextString(m) }
|
||||
func (*UpdateClientResp) ProtoMessage() {}
|
||||
func (*UpdateClientResp) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} }
|
||||
|
||||
func (m *UpdateClientResp) GetClient() *Client {
|
||||
if m != nil {
|
||||
return m.Client
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ListClientsReq struct {
|
||||
}
|
||||
|
||||
func (m *ListClientsReq) Reset() { *m = ListClientsReq{} }
|
||||
func (m *ListClientsReq) String() string { return proto.CompactTextString(m) }
|
||||
func (*ListClientsReq) ProtoMessage() {}
|
||||
func (*ListClientsReq) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} }
|
||||
|
||||
type ListClientsResp struct {
|
||||
Clients []*Client `protobuf:"bytes,1,rep,name=clients" json:"clients,omitempty"`
|
||||
}
|
||||
|
||||
func (m *ListClientsResp) Reset() { *m = ListClientsResp{} }
|
||||
func (m *ListClientsResp) String() string { return proto.CompactTextString(m) }
|
||||
func (*ListClientsResp) ProtoMessage() {}
|
||||
func (*ListClientsResp) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} }
|
||||
|
||||
func (m *ListClientsResp) GetClients() []*Client {
|
||||
if m != nil {
|
||||
return m.Clients
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type DeleteClientReq struct {
|
||||
Id string `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"`
|
||||
}
|
||||
|
||||
func (m *DeleteClientReq) Reset() { *m = DeleteClientReq{} }
|
||||
func (m *DeleteClientReq) String() string { return proto.CompactTextString(m) }
|
||||
func (*DeleteClientReq) ProtoMessage() {}
|
||||
func (*DeleteClientReq) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} }
|
||||
|
||||
type DeleteClientResp struct {
|
||||
}
|
||||
|
||||
func (m *DeleteClientResp) Reset() { *m = DeleteClientResp{} }
|
||||
func (m *DeleteClientResp) String() string { return proto.CompactTextString(m) }
|
||||
func (*DeleteClientResp) ProtoMessage() {}
|
||||
func (*DeleteClientResp) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8} }
|
||||
|
||||
type GetClientReq struct {
|
||||
Id string `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"`
|
||||
}
|
||||
|
||||
func (m *GetClientReq) Reset() { *m = GetClientReq{} }
|
||||
func (m *GetClientReq) String() string { return proto.CompactTextString(m) }
|
||||
func (*GetClientReq) ProtoMessage() {}
|
||||
func (*GetClientReq) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{9} }
|
||||
|
||||
type GetClientResp struct {
|
||||
Client *Client `protobuf:"bytes,1,opt,name=client" json:"client,omitempty"`
|
||||
}
|
||||
|
||||
func (m *GetClientResp) Reset() { *m = GetClientResp{} }
|
||||
func (m *GetClientResp) String() string { return proto.CompactTextString(m) }
|
||||
func (*GetClientResp) ProtoMessage() {}
|
||||
func (*GetClientResp) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{10} }
|
||||
|
||||
func (m *GetClientResp) GetClient() *Client {
|
||||
if m != nil {
|
||||
return m.Client
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*Client)(nil), "apipb.Client")
|
||||
proto.RegisterType((*CreateClientReq)(nil), "apipb.CreateClientReq")
|
||||
proto.RegisterType((*CreateClientResp)(nil), "apipb.CreateClientResp")
|
||||
proto.RegisterType((*UpdateClientReq)(nil), "apipb.UpdateClientReq")
|
||||
proto.RegisterType((*UpdateClientResp)(nil), "apipb.UpdateClientResp")
|
||||
proto.RegisterType((*ListClientsReq)(nil), "apipb.ListClientsReq")
|
||||
proto.RegisterType((*ListClientsResp)(nil), "apipb.ListClientsResp")
|
||||
proto.RegisterType((*DeleteClientReq)(nil), "apipb.DeleteClientReq")
|
||||
proto.RegisterType((*DeleteClientResp)(nil), "apipb.DeleteClientResp")
|
||||
proto.RegisterType((*GetClientReq)(nil), "apipb.GetClientReq")
|
||||
proto.RegisterType((*GetClientResp)(nil), "apipb.GetClientResp")
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ context.Context
|
||||
var _ grpc.ClientConn
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
const _ = grpc.SupportPackageIsVersion3
|
||||
|
||||
// Client API for Storage service
|
||||
|
||||
type StorageClient interface {
|
||||
GetClient(ctx context.Context, in *GetClientReq, opts ...grpc.CallOption) (*GetClientResp, error)
|
||||
CreateClient(ctx context.Context, in *CreateClientReq, opts ...grpc.CallOption) (*CreateClientResp, error)
|
||||
UpdateClient(ctx context.Context, in *UpdateClientReq, opts ...grpc.CallOption) (*UpdateClientResp, error)
|
||||
DeleteClient(ctx context.Context, in *DeleteClientReq, opts ...grpc.CallOption) (*DeleteClientReq, error)
|
||||
ListClients(ctx context.Context, in *ListClientsReq, opts ...grpc.CallOption) (*ListClientsResp, error)
|
||||
}
|
||||
|
||||
type storageClient struct {
|
||||
cc *grpc.ClientConn
|
||||
}
|
||||
|
||||
func NewStorageClient(cc *grpc.ClientConn) StorageClient {
|
||||
return &storageClient{cc}
|
||||
}
|
||||
|
||||
func (c *storageClient) GetClient(ctx context.Context, in *GetClientReq, opts ...grpc.CallOption) (*GetClientResp, error) {
|
||||
out := new(GetClientResp)
|
||||
err := grpc.Invoke(ctx, "/apipb.Storage/GetClient", in, out, c.cc, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *storageClient) CreateClient(ctx context.Context, in *CreateClientReq, opts ...grpc.CallOption) (*CreateClientResp, error) {
|
||||
out := new(CreateClientResp)
|
||||
err := grpc.Invoke(ctx, "/apipb.Storage/CreateClient", in, out, c.cc, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *storageClient) UpdateClient(ctx context.Context, in *UpdateClientReq, opts ...grpc.CallOption) (*UpdateClientResp, error) {
|
||||
out := new(UpdateClientResp)
|
||||
err := grpc.Invoke(ctx, "/apipb.Storage/UpdateClient", in, out, c.cc, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *storageClient) DeleteClient(ctx context.Context, in *DeleteClientReq, opts ...grpc.CallOption) (*DeleteClientReq, error) {
|
||||
out := new(DeleteClientReq)
|
||||
err := grpc.Invoke(ctx, "/apipb.Storage/DeleteClient", in, out, c.cc, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *storageClient) ListClients(ctx context.Context, in *ListClientsReq, opts ...grpc.CallOption) (*ListClientsResp, error) {
|
||||
out := new(ListClientsResp)
|
||||
err := grpc.Invoke(ctx, "/apipb.Storage/ListClients", in, out, c.cc, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Server API for Storage service
|
||||
|
||||
type StorageServer interface {
|
||||
GetClient(context.Context, *GetClientReq) (*GetClientResp, error)
|
||||
CreateClient(context.Context, *CreateClientReq) (*CreateClientResp, error)
|
||||
UpdateClient(context.Context, *UpdateClientReq) (*UpdateClientResp, error)
|
||||
DeleteClient(context.Context, *DeleteClientReq) (*DeleteClientReq, error)
|
||||
ListClients(context.Context, *ListClientsReq) (*ListClientsResp, error)
|
||||
}
|
||||
|
||||
func RegisterStorageServer(s *grpc.Server, srv StorageServer) {
|
||||
s.RegisterService(&_Storage_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _Storage_GetClient_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetClientReq)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(StorageServer).GetClient(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/apipb.Storage/GetClient",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(StorageServer).GetClient(ctx, req.(*GetClientReq))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Storage_CreateClient_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(CreateClientReq)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(StorageServer).CreateClient(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/apipb.Storage/CreateClient",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(StorageServer).CreateClient(ctx, req.(*CreateClientReq))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Storage_UpdateClient_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(UpdateClientReq)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(StorageServer).UpdateClient(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/apipb.Storage/UpdateClient",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(StorageServer).UpdateClient(ctx, req.(*UpdateClientReq))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Storage_DeleteClient_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(DeleteClientReq)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(StorageServer).DeleteClient(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/apipb.Storage/DeleteClient",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(StorageServer).DeleteClient(ctx, req.(*DeleteClientReq))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Storage_ListClients_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ListClientsReq)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(StorageServer).ListClients(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/apipb.Storage/ListClients",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(StorageServer).ListClients(ctx, req.(*ListClientsReq))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
var _Storage_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "apipb.Storage",
|
||||
HandlerType: (*StorageServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "GetClient",
|
||||
Handler: _Storage_GetClient_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "CreateClient",
|
||||
Handler: _Storage_CreateClient_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "UpdateClient",
|
||||
Handler: _Storage_UpdateClient_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "DeleteClient",
|
||||
Handler: _Storage_DeleteClient_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ListClients",
|
||||
Handler: _Storage_ListClients_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: fileDescriptor0,
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("api/apipb/api.proto", fileDescriptor0) }
|
||||
|
||||
var fileDescriptor0 = []byte{
|
||||
// 459 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x8c, 0x54, 0x41, 0x8f, 0xd2, 0x40,
|
||||
0x14, 0xa6, 0xc0, 0xb6, 0xf0, 0x28, 0x0b, 0x79, 0xab, 0x6b, 0xe5, 0xa0, 0xec, 0x18, 0x23, 0x27,
|
||||
0x4c, 0xd6, 0xc4, 0xac, 0x1e, 0x8c, 0x66, 0x4d, 0xbc, 0x78, 0x20, 0x35, 0x9c, 0x49, 0xa1, 0x2f,
|
||||
0x9b, 0x89, 0xdd, 0xed, 0x30, 0x33, 0xf8, 0xff, 0x3c, 0x7b, 0xf2, 0x1f, 0x99, 0xce, 0xb4, 0x4d,
|
||||
0x07, 0x64, 0xc3, 0x85, 0xf0, 0xbe, 0xf9, 0xde, 0x7b, 0xf9, 0xbe, 0xef, 0xa5, 0x70, 0x91, 0x08,
|
||||
0xfe, 0x36, 0x11, 0x5c, 0xac, 0x8b, 0xdf, 0xb9, 0x90, 0xb9, 0xce, 0xf1, 0xcc, 0x00, 0xec, 0xb7,
|
||||
0x07, 0xfe, 0x6d, 0xc6, 0xe9, 0x41, 0xe3, 0x39, 0xb4, 0x79, 0x1a, 0x79, 0x53, 0x6f, 0xd6, 0x8f,
|
||||
0xdb, 0x3c, 0xc5, 0x4b, 0xf0, 0x15, 0x6d, 0x24, 0xe9, 0xa8, 0x6d, 0xb0, 0xb2, 0xc2, 0x57, 0x30,
|
||||
0x94, 0x94, 0x72, 0x49, 0x1b, 0xbd, 0xda, 0x49, 0xae, 0xa2, 0xce, 0xb4, 0x33, 0xeb, 0xc7, 0x61,
|
||||
0x05, 0x2e, 0x25, 0x57, 0x05, 0x49, 0xcb, 0x9d, 0xd2, 0x94, 0xae, 0x04, 0x91, 0x54, 0x51, 0xd7,
|
||||
0x92, 0x4a, 0x70, 0x51, 0x60, 0xc5, 0x06, 0xb1, 0x5b, 0x67, 0x7c, 0x13, 0x9d, 0x4d, 0xbd, 0x59,
|
||||
0x2f, 0x2e, 0x2b, 0x44, 0xe8, 0x3e, 0x24, 0xf7, 0x14, 0xf9, 0x66, 0xaf, 0xf9, 0x8f, 0xcf, 0xa1,
|
||||
0x97, 0xe5, 0x77, 0xf9, 0x6a, 0x27, 0xb3, 0x28, 0x30, 0x78, 0x50, 0xd4, 0x4b, 0x99, 0xb1, 0x1b,
|
||||
0x18, 0xdd, 0x4a, 0x4a, 0x34, 0x59, 0x21, 0x31, 0x6d, 0xf1, 0x35, 0xf8, 0x1b, 0x53, 0x18, 0x3d,
|
||||
0x83, 0xeb, 0xe1, 0xdc, 0xc8, 0x9d, 0x97, 0x8c, 0xf2, 0x91, 0x7d, 0x80, 0xb1, 0xdb, 0xa9, 0xc4,
|
||||
0xa9, 0xad, 0x7f, 0x3d, 0x18, 0x2d, 0x45, 0xea, 0x6c, 0x3d, 0xd5, 0xc1, 0x4a, 0x5f, 0xe7, 0x88,
|
||||
0xbe, 0xae, 0xa3, 0x0f, 0x5f, 0xc2, 0xe0, 0x3e, 0xf9, 0x49, 0x2b, 0xc7, 0x2b, 0x28, 0xa0, 0x85,
|
||||
0xf5, 0xeb, 0x0a, 0x42, 0x4b, 0x90, 0xfc, 0x57, 0xa2, 0xad, 0x6f, 0xbd, 0xd8, 0x34, 0x2d, 0x2c,
|
||||
0x74, 0x18, 0x5a, 0x70, 0x18, 0x5a, 0x61, 0x87, 0x2b, 0xe9, 0x74, 0x3b, 0xc6, 0x70, 0xfe, 0x9d,
|
||||
0x2b, 0x6d, 0x51, 0x15, 0xd3, 0x96, 0x7d, 0x84, 0x91, 0x83, 0x28, 0x81, 0x6f, 0x20, 0xb0, 0x74,
|
||||
0x15, 0x79, 0xd3, 0xce, 0xe1, 0xb0, 0xea, 0x95, 0x5d, 0xc1, 0xe8, 0x2b, 0x65, 0xf4, 0x88, 0xb7,
|
||||
0x0c, 0x61, 0xec, 0x52, 0x94, 0x60, 0x2f, 0x20, 0xfc, 0x46, 0xfa, 0x78, 0xcf, 0x7b, 0x18, 0x36,
|
||||
0xde, 0x4f, 0x16, 0x77, 0xfd, 0xa7, 0x0d, 0xc1, 0x0f, 0x9d, 0xcb, 0xe4, 0x8e, 0xf0, 0x06, 0xfa,
|
||||
0xf5, 0x0c, 0xbc, 0x28, 0xf9, 0xcd, 0xad, 0x93, 0x27, 0x87, 0xa0, 0x12, 0xac, 0x85, 0x5f, 0x20,
|
||||
0x6c, 0x1e, 0x1b, 0x5e, 0x56, 0xcb, 0xdc, 0xdb, 0x9d, 0x3c, 0xfb, 0x2f, 0x5e, 0x8d, 0x68, 0x06,
|
||||
0x54, 0x8f, 0xd8, 0x3b, 0xc4, 0x7a, 0xc4, 0x7e, 0x9a, 0xac, 0x85, 0x9f, 0x21, 0x6c, 0xfa, 0x56,
|
||||
0x8f, 0xd8, 0xf3, 0x7b, 0x72, 0x04, 0x67, 0x2d, 0xfc, 0x04, 0x83, 0x46, 0xb0, 0xf8, 0xb4, 0x24,
|
||||
0xba, 0xf1, 0xd7, 0xfd, 0x7b, 0x37, 0xc0, 0x5a, 0x6b, 0xdf, 0x7c, 0x80, 0xde, 0xfd, 0x0b, 0x00,
|
||||
0x00, 0xff, 0xff, 0x76, 0xa0, 0x50, 0x77, 0x97, 0x04, 0x00, 0x00,
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
syntax = "proto3";
|
||||
|
||||
// Run `make grpc` at the top level directory to regenerate Go source code.
|
||||
|
||||
package apipb;
|
||||
|
||||
message Client {
|
||||
string id = 1;
|
||||
string secret = 2;
|
||||
|
||||
repeated string redirect_uris = 3;
|
||||
repeated string trusted_peers = 4;
|
||||
|
||||
bool public = 5;
|
||||
|
||||
string name = 6;
|
||||
string logo_url = 7;
|
||||
}
|
||||
|
||||
message CreateClientReq {
|
||||
Client client = 1;
|
||||
}
|
||||
|
||||
message CreateClientResp {
|
||||
Client client = 1;
|
||||
}
|
||||
|
||||
message UpdateClientReq {
|
||||
string id = 1;
|
||||
|
||||
// Empty strings indicate that string fields should not be updated.
|
||||
string secret = 2;
|
||||
string name = 3;
|
||||
string logo_url = 4;
|
||||
|
||||
bool make_public = 5;
|
||||
bool make_private = 6;
|
||||
|
||||
// If no redirect URIs are specified, the current redirect URIs are preserved.
|
||||
repeated string redirect_uris = 7;
|
||||
}
|
||||
|
||||
message UpdateClientResp {
|
||||
Client client = 1;
|
||||
}
|
||||
|
||||
message ListClientsReq {
|
||||
}
|
||||
|
||||
message ListClientsResp {
|
||||
repeated Client clients = 1;
|
||||
}
|
||||
|
||||
message DeleteClientReq {
|
||||
string id = 1;
|
||||
}
|
||||
|
||||
message DeleteClientResp {}
|
||||
|
||||
message GetClientReq {
|
||||
string id = 1;
|
||||
}
|
||||
|
||||
message GetClientResp {
|
||||
Client client = 1;
|
||||
}
|
||||
|
||||
service Storage {
|
||||
rpc CreateClient(CreateClientReq) returns (CreateClientResp) {}
|
||||
rpc DeleteClient(DeleteClientReq) returns (DeleteClientReq) {}
|
||||
rpc GetClient(GetClientReq) returns (GetClientResp) {}
|
||||
rpc ListClients(ListClientsReq) returns (ListClientsResp) {}
|
||||
rpc UpdateClient(UpdateClientReq) returns (UpdateClientResp) {}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
// Package api implements a gRPC interface for interacting with a storage.
|
||||
package api
|
||||
@@ -1,24 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "pokectl",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func init() {}
|
||||
|
||||
func main() {
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(2)
|
||||
}
|
||||
}
|
||||
Generated
+2
-18
@@ -1,5 +1,5 @@
|
||||
hash: 2b694ffd26e854f519064b43f437545dfb7cfdc437b3890552dc1f6e7955a2b5
|
||||
updated: 2016-08-03T23:07:24.402853601-07:00
|
||||
hash: 4442a097b81856345ae5f80101ad1a692a0b4e5d9b7627f5ad09cd20926122f4
|
||||
updated: 2016-08-05T09:58:15.61704222-07:00
|
||||
imports:
|
||||
- name: github.com/ericchiang/oidc
|
||||
version: 69fec81d167d815f4f455c741b2a94ffaf547ed2
|
||||
@@ -40,11 +40,6 @@ imports:
|
||||
version: 6a513affb38dc9788b449d59ffed099b8de18fa0
|
||||
subpackages:
|
||||
- context
|
||||
- http2
|
||||
- http2/hpack
|
||||
- trace
|
||||
- lex/httplex
|
||||
- internal/timeseries
|
||||
- name: golang.org/x/oauth2
|
||||
version: 08c8d727d2392d18286f9f88ad775ad98f09ab33
|
||||
subpackages:
|
||||
@@ -60,17 +55,6 @@ imports:
|
||||
- internal/datastore
|
||||
- internal/log
|
||||
- internal/remote_api
|
||||
- name: google.golang.org/grpc
|
||||
version: 13edeeffdea7a41d5aad96c28deb4c7bd01a9397
|
||||
subpackages:
|
||||
- codes
|
||||
- credentials
|
||||
- grpclog
|
||||
- internal
|
||||
- metadata
|
||||
- naming
|
||||
- transport
|
||||
- peer
|
||||
- name: gopkg.in/asn1-ber.v1
|
||||
version: 4e86f4367175e39f69d9358a5f17b4dda270378d
|
||||
- name: gopkg.in/ldap.v2
|
||||
|
||||
+5
-21
@@ -22,6 +22,11 @@ import:
|
||||
- cipher
|
||||
- json
|
||||
|
||||
- package: golang.org/x/net
|
||||
version: 6a513affb38dc9788b449d59ffed099b8de18fa0
|
||||
subpackages:
|
||||
- context
|
||||
|
||||
- package: gopkg.in/yaml.v2
|
||||
version: a83829b6f1293c91addabc89d0571c246397bbf4
|
||||
|
||||
@@ -67,27 +72,6 @@ import:
|
||||
- package: github.com/mitchellh/go-homedir
|
||||
verison: 756f7b183b7ab78acdbbee5c7f392838ed459dda
|
||||
|
||||
- package: google.golang.org/grpc
|
||||
version: v1.0.0
|
||||
subpackages:
|
||||
- codes
|
||||
- credentials
|
||||
- grpclog
|
||||
- internal
|
||||
- metadata
|
||||
- naming
|
||||
- transport
|
||||
- peer
|
||||
- package: golang.org/x/net
|
||||
version: 6a513affb38dc9788b449d59ffed099b8de18fa0
|
||||
subpackages:
|
||||
- context # context is also imported directly by this repo.
|
||||
- http2
|
||||
- http2/hpack
|
||||
- trace
|
||||
- lex/httplex
|
||||
- internal/timeseries
|
||||
|
||||
- package: github.com/kylelemons/godebug
|
||||
subpackages:
|
||||
- diff
|
||||
|
||||
-17
@@ -1,17 +0,0 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.5.3
|
||||
- 1.6
|
||||
|
||||
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
|
||||
-46
@@ -1,46 +0,0 @@
|
||||
# How to contribute
|
||||
|
||||
We definitely welcome patches and contribution to grpc! Here are some guidelines
|
||||
and information about how to do so.
|
||||
|
||||
## Sending patches
|
||||
|
||||
### Getting started
|
||||
|
||||
1. Check out the code:
|
||||
|
||||
$ go get google.golang.org/grpc
|
||||
$ cd $GOPATH/src/google.golang.org/grpc
|
||||
|
||||
1. Create a fork of the grpc-go repository.
|
||||
1. Add your fork as a remote:
|
||||
|
||||
$ git remote add fork git@github.com:$YOURGITHUBUSERNAME/grpc-go.git
|
||||
|
||||
1. Make changes, commit them.
|
||||
1. Run the test suite:
|
||||
|
||||
$ make test
|
||||
|
||||
1. Push your changes to your fork:
|
||||
|
||||
$ git push fork ...
|
||||
|
||||
1. Open a pull request.
|
||||
|
||||
## 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.
|
||||
-41
@@ -1,41 +0,0 @@
|
||||
# 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)))
|
||||
```
|
||||
|
||||
-201
@@ -1,201 +0,0 @@
|
||||
# Metadata
|
||||
|
||||
gRPC supports sending metadata between client and server.
|
||||
This doc shows how to send and receive metadata in gRPC-go.
|
||||
|
||||
## Background
|
||||
|
||||
Four kinds of service method:
|
||||
|
||||
- [Unary RPC](http://www.grpc.io/docs/guides/concepts.html#unary-rpc)
|
||||
- [Server streaming RPC](http://www.grpc.io/docs/guides/concepts.html#server-streaming-rpc)
|
||||
- [Client streaming RPC](http://www.grpc.io/docs/guides/concepts.html#client-streaming-rpc)
|
||||
- [Bidirectional streaming RPC](http://www.grpc.io/docs/guides/concepts.html#bidirectional-streaming-rpc)
|
||||
|
||||
And concept of [metadata](http://www.grpc.io/docs/guides/concepts.html#metadata).
|
||||
|
||||
## Constructing metadata
|
||||
|
||||
A metadata can be created using package [metadata](https://godoc.org/google.golang.org/grpc/metadata).
|
||||
The type MD is actually a map from string to a list of strings:
|
||||
|
||||
```go
|
||||
type MD map[string][]string
|
||||
```
|
||||
|
||||
Metadata can be read like a normal map.
|
||||
Note that the value type of this map is `[]string`,
|
||||
so that users can attach multiple values using a single key.
|
||||
|
||||
### Creating a new metadata
|
||||
|
||||
A metadata can be created from a `map[string]string` using function `New`:
|
||||
|
||||
```go
|
||||
md := metadata.New(map[string]string{"key1": "val1", "key2": "val2"})
|
||||
```
|
||||
|
||||
Another way is to use `Pairs`.
|
||||
Values with the same key will be merged into a list:
|
||||
|
||||
```go
|
||||
md := metadata.Pairs(
|
||||
"key1", "val1",
|
||||
"key1", "val1-2", // "key1" will have map value []string{"val1", "val1-2"}
|
||||
"key2", "val2",
|
||||
)
|
||||
```
|
||||
|
||||
__Note:__ all the keys will be automatically converted to lowercase,
|
||||
so "key1" and "kEy1" will be the same key and their values will be merged into the same list.
|
||||
This happens for both `New` and `Pairs`.
|
||||
|
||||
### Storing binary data in metadata
|
||||
|
||||
In metadata, keys are always strings. But values can be strings or binary data.
|
||||
To store binary data value in metadata, simply add "-bin" surfix to the key.
|
||||
The values with "-bin" surffixed keys will be encoded when creating the metadata:
|
||||
|
||||
```go
|
||||
md := metadata.Pairs(
|
||||
"key", "string value",
|
||||
"key-bin", string([]byte{96, 102}), // this binary data will be encoded (base64) before sending
|
||||
// and will be decoded after being transferred.
|
||||
)
|
||||
```
|
||||
|
||||
## Retrieving metadata from context
|
||||
|
||||
Metadata can be retrieved from context using `FromContext`:
|
||||
|
||||
```go
|
||||
func (s *server) SomeRPC(ctx context.Context, in *pb.SomeRequest) (*pb.SomeResponse, err) {
|
||||
md, ok := metadata.FromContext(ctx)
|
||||
// do something with metadata
|
||||
}
|
||||
```
|
||||
|
||||
## Sending and receiving metadata - client side
|
||||
|
||||
[//]: # "TODO: uncomment next line after example source added"
|
||||
[//]: # "Real metadata sending and receiving examples are available [here](TODO:example_dir)."
|
||||
|
||||
### Sending metadata
|
||||
|
||||
To send metadata to server, the client can wrap the metadata into a context using `NewContext`, and make the RPC with this context:
|
||||
|
||||
```go
|
||||
md := metadata.Pairs("key", "val")
|
||||
|
||||
// create a new context with this metadata
|
||||
ctx := metadata.NewContext(context.Background(), md)
|
||||
|
||||
// make unary RPC
|
||||
response, err := client.SomeRPC(ctx, someRequest)
|
||||
|
||||
// or make streaming RPC
|
||||
stream, err := client.SomeStreamingRPC(ctx)
|
||||
```
|
||||
### Receiving metadata
|
||||
|
||||
Metadata that a client can receive includes header and trailer.
|
||||
|
||||
#### Unary call
|
||||
|
||||
Header and trailer sent along with a unary call can be retrieved using function [Header](https://godoc.org/google.golang.org/grpc#Header) and [Trailer](https://godoc.org/google.golang.org/grpc#Trailer) in [CallOption](https://godoc.org/google.golang.org/grpc#CallOption):
|
||||
|
||||
```go
|
||||
var header, trailer metadata.MD // variable to store header and trailer
|
||||
r, err := client.SomeRPC(
|
||||
ctx,
|
||||
someRequest,
|
||||
grpc.Header(&header), // will retrieve header
|
||||
grpc.Trailer(&trailer), // will retrieve trailer
|
||||
)
|
||||
|
||||
// do something with header and trailer
|
||||
```
|
||||
|
||||
#### Streaming call
|
||||
|
||||
For streaming calls including:
|
||||
|
||||
- Server streaming RPC
|
||||
- Client streaming RPC
|
||||
- Bidirectional streaming RPC
|
||||
|
||||
Header and trailer can be retrieved from the returned stream using function `Header` and `Trailer` in interface [ClientStream](https://godoc.org/google.golang.org/grpc#ClientStream):
|
||||
|
||||
```go
|
||||
stream, err := client.SomeStreamingRPC(ctx)
|
||||
|
||||
// retrieve header
|
||||
header, err := stream.Header()
|
||||
|
||||
// retrieve trailer
|
||||
trailer := stream.Trailer()
|
||||
|
||||
```
|
||||
|
||||
## Sending and receiving metadata - server side
|
||||
|
||||
[//]: # "TODO: uncomment next line after example source added"
|
||||
[//]: # "Real metadata sending and receiving examples are available [here](TODO:example_dir)."
|
||||
|
||||
### Receiving metadata
|
||||
|
||||
To read metadata sent by the client, the server needs to retrieve it from RPC context.
|
||||
If it is a unary call, the RPC handler's context can be used.
|
||||
For streaming calls, the server needs to get context from the stream.
|
||||
|
||||
#### Unary call
|
||||
|
||||
```go
|
||||
func (s *server) SomeRPC(ctx context.Context, in *pb.someRequest) (*pb.someResponse, error) {
|
||||
md, ok := metadata.FromContext(ctx)
|
||||
// do something with metadata
|
||||
}
|
||||
```
|
||||
|
||||
#### Streaming call
|
||||
|
||||
```go
|
||||
func (s *server) SomeStreamingRPC(stream pb.Service_SomeStreamingRPCServer) error {
|
||||
md, ok := metadata.FromContext(stream.Context()) // get context from stream
|
||||
// do something with metadata
|
||||
}
|
||||
```
|
||||
|
||||
### Sending metadata
|
||||
|
||||
#### Unary call
|
||||
|
||||
To send header and trailer to client in unary call, the server can call [SendHeader](https://godoc.org/google.golang.org/grpc#SendHeader) and [SetTrailer](https://godoc.org/google.golang.org/grpc#SetTrailer) functions in module [grpc](https://godoc.org/google.golang.org/grpc).
|
||||
These two functions take a context as the first parameter.
|
||||
It should be the RPC handler's context or one derived from it:
|
||||
|
||||
```go
|
||||
func (s *server) SomeRPC(ctx context.Context, in *pb.someRequest) (*pb.someResponse, error) {
|
||||
// create and send header
|
||||
header := metadata.Pairs("header-key", "val")
|
||||
grpc.SendHeader(ctx, header)
|
||||
// create and set trailer
|
||||
trailer := metadata.Pairs("trailer-key", "val")
|
||||
grpc.SetTrailer(ctx, trailer)
|
||||
}
|
||||
```
|
||||
|
||||
#### Streaming call
|
||||
|
||||
For streaming calls, header and trailer can be sent using function `SendHeader` and `SetTrailer` in interface [ServerStream](https://godoc.org/google.golang.org/grpc#ServerStream):
|
||||
|
||||
```go
|
||||
func (s *server) SomeStreamingRPC(stream pb.Service_SomeStreamingRPCServer) error {
|
||||
// create and send header
|
||||
header := metadata.Pairs("header-key", "val")
|
||||
stream.SendHeader(header)
|
||||
// create and set trailer
|
||||
trailer := metadata.Pairs("trailer-key", "val")
|
||||
stream.SetTrailer(trailer)
|
||||
}
|
||||
```
|
||||
-28
@@ -1,28 +0,0 @@
|
||||
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.
|
||||
-52
@@ -1,52 +0,0 @@
|
||||
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 -u -v github.com/golang/protobuf/protoc-gen-go
|
||||
# use $$dir as the root for all proto files in the same directory
|
||||
for dir in $$(git ls-files '*.proto' | xargs -n1 dirname | uniq); do \
|
||||
protoc -I $$dir --go_out=plugins=grpc:$$dir $$dir/*.proto; \
|
||||
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 -i google.golang.org/grpc/...
|
||||
|
||||
coverage: testdeps
|
||||
./coverage.sh --coveralls
|
||||
|
||||
.PHONY: \
|
||||
all \
|
||||
deps \
|
||||
updatedeps \
|
||||
testdeps \
|
||||
updatetestdeps \
|
||||
build \
|
||||
proto \
|
||||
test \
|
||||
testrace \
|
||||
clean \
|
||||
coverage
|
||||
-22
@@ -1,22 +0,0 @@
|
||||
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.
|
||||
-32
@@ -1,32 +0,0 @@
|
||||
#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 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.5 or later .
|
||||
|
||||
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
|
||||
|
||||
-80
@@ -1,80 +0,0 @@
|
||||
package grpc
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
// DefaultBackoffConfig uses values specified for backoff in
|
||||
// https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md.
|
||||
var (
|
||||
DefaultBackoffConfig = BackoffConfig{
|
||||
MaxDelay: 120 * time.Second,
|
||||
baseDelay: 1.0 * time.Second,
|
||||
factor: 1.6,
|
||||
jitter: 0.2,
|
||||
}
|
||||
)
|
||||
|
||||
// backoffStrategy defines the methodology for backing off after a grpc
|
||||
// connection failure.
|
||||
//
|
||||
// This is unexported until the gRPC project decides whether or not to allow
|
||||
// alternative backoff strategies. Once a decision is made, this type and its
|
||||
// method may be exported.
|
||||
type backoffStrategy interface {
|
||||
// backoff returns the amount of time to wait before the next retry given
|
||||
// the number of consecutive failures.
|
||||
backoff(retries int) time.Duration
|
||||
}
|
||||
|
||||
// BackoffConfig defines the parameters for the default gRPC backoff strategy.
|
||||
type BackoffConfig struct {
|
||||
// MaxDelay is the upper bound of backoff delay.
|
||||
MaxDelay time.Duration
|
||||
|
||||
// TODO(stevvooe): The following fields are not exported, as allowing
|
||||
// changes would violate the current gRPC specification for backoff. If
|
||||
// gRPC decides to allow more interesting backoff strategies, these fields
|
||||
// may be opened up in the future.
|
||||
|
||||
// baseDelay is the amount of time to wait before retrying after the first
|
||||
// failure.
|
||||
baseDelay time.Duration
|
||||
|
||||
// factor is applied to the backoff after each retry.
|
||||
factor float64
|
||||
|
||||
// jitter provides a range to randomize backoff delays.
|
||||
jitter float64
|
||||
}
|
||||
|
||||
func setDefaults(bc *BackoffConfig) {
|
||||
md := bc.MaxDelay
|
||||
*bc = DefaultBackoffConfig
|
||||
|
||||
if md > 0 {
|
||||
bc.MaxDelay = md
|
||||
}
|
||||
}
|
||||
|
||||
func (bc BackoffConfig) backoff(retries int) (t time.Duration) {
|
||||
if retries == 0 {
|
||||
return bc.baseDelay
|
||||
}
|
||||
backoff, max := float64(bc.baseDelay), float64(bc.MaxDelay)
|
||||
for backoff < max && retries > 0 {
|
||||
backoff *= bc.factor
|
||||
retries--
|
||||
}
|
||||
if backoff > max {
|
||||
backoff = max
|
||||
}
|
||||
// Randomize backoff delays so that if a cluster of requests start at
|
||||
// the same time, they won't operate in lockstep.
|
||||
backoff *= 1 + bc.jitter*(rand.Float64()*2-1)
|
||||
if backoff < 0 {
|
||||
return 0
|
||||
}
|
||||
return time.Duration(backoff)
|
||||
}
|
||||
-11
@@ -1,11 +0,0 @@
|
||||
package grpc
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestBackoffConfigDefaults(t *testing.T) {
|
||||
b := BackoffConfig{}
|
||||
setDefaults(&b)
|
||||
if b != DefaultBackoffConfig {
|
||||
t.Fatalf("expected BackoffConfig to pickup default parameters: %v != %v", b, DefaultBackoffConfig)
|
||||
}
|
||||
}
|
||||
-385
@@ -1,385 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* Copyright 2016, 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 (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc/grpclog"
|
||||
"google.golang.org/grpc/naming"
|
||||
)
|
||||
|
||||
// Address represents a server the client connects to.
|
||||
// This is the EXPERIMENTAL API and may be changed or extended in the future.
|
||||
type Address struct {
|
||||
// Addr is the server address on which a connection will be established.
|
||||
Addr string
|
||||
// Metadata is the information associated with Addr, which may be used
|
||||
// to make load balancing decision.
|
||||
Metadata interface{}
|
||||
}
|
||||
|
||||
// BalancerGetOptions configures a Get call.
|
||||
// This is the EXPERIMENTAL API and may be changed or extended in the future.
|
||||
type BalancerGetOptions struct {
|
||||
// BlockingWait specifies whether Get should block when there is no
|
||||
// connected address.
|
||||
BlockingWait bool
|
||||
}
|
||||
|
||||
// Balancer chooses network addresses for RPCs.
|
||||
// This is the EXPERIMENTAL API and may be changed or extended in the future.
|
||||
type Balancer interface {
|
||||
// Start does the initialization work to bootstrap a Balancer. For example,
|
||||
// this function may start the name resolution and watch the updates. It will
|
||||
// be called when dialing.
|
||||
Start(target string) error
|
||||
// Up informs the Balancer that gRPC has a connection to the server at
|
||||
// addr. It returns down which is called once the connection to addr gets
|
||||
// lost or closed.
|
||||
// TODO: It is not clear how to construct and take advantage the meaningful error
|
||||
// parameter for down. Need realistic demands to guide.
|
||||
Up(addr Address) (down func(error))
|
||||
// Get gets the address of a server for the RPC corresponding to ctx.
|
||||
// i) If it returns a connected address, gRPC internals issues the RPC on the
|
||||
// connection to this address;
|
||||
// ii) If it returns an address on which the connection is under construction
|
||||
// (initiated by Notify(...)) but not connected, gRPC internals
|
||||
// * fails RPC if the RPC is fail-fast and connection is in the TransientFailure or
|
||||
// Shutdown state;
|
||||
// or
|
||||
// * issues RPC on the connection otherwise.
|
||||
// iii) If it returns an address on which the connection does not exist, gRPC
|
||||
// internals treats it as an error and will fail the corresponding RPC.
|
||||
//
|
||||
// Therefore, the following is the recommended rule when writing a custom Balancer.
|
||||
// If opts.BlockingWait is true, it should return a connected address or
|
||||
// block if there is no connected address. It should respect the timeout or
|
||||
// cancellation of ctx when blocking. If opts.BlockingWait is false (for fail-fast
|
||||
// RPCs), it should return an address it has notified via Notify(...) immediately
|
||||
// instead of blocking.
|
||||
//
|
||||
// The function returns put which is called once the rpc has completed or failed.
|
||||
// put can collect and report RPC stats to a remote load balancer.
|
||||
//
|
||||
// This function should only return the errors Balancer cannot recover by itself.
|
||||
// gRPC internals will fail the RPC if an error is returned.
|
||||
Get(ctx context.Context, opts BalancerGetOptions) (addr Address, put func(), err error)
|
||||
// Notify returns a channel that is used by gRPC internals to watch the addresses
|
||||
// gRPC needs to connect. The addresses might be from a name resolver or remote
|
||||
// load balancer. gRPC internals will compare it with the existing connected
|
||||
// addresses. If the address Balancer notified is not in the existing connected
|
||||
// addresses, gRPC starts to connect the address. If an address in the existing
|
||||
// connected addresses is not in the notification list, the corresponding connection
|
||||
// is shutdown gracefully. Otherwise, there are no operations to take. Note that
|
||||
// the Address slice must be the full list of the Addresses which should be connected.
|
||||
// It is NOT delta.
|
||||
Notify() <-chan []Address
|
||||
// Close shuts down the balancer.
|
||||
Close() error
|
||||
}
|
||||
|
||||
// downErr implements net.Error. It is constructed by gRPC internals and passed to the down
|
||||
// call of Balancer.
|
||||
type downErr struct {
|
||||
timeout bool
|
||||
temporary bool
|
||||
desc string
|
||||
}
|
||||
|
||||
func (e downErr) Error() string { return e.desc }
|
||||
func (e downErr) Timeout() bool { return e.timeout }
|
||||
func (e downErr) Temporary() bool { return e.temporary }
|
||||
|
||||
func downErrorf(timeout, temporary bool, format string, a ...interface{}) downErr {
|
||||
return downErr{
|
||||
timeout: timeout,
|
||||
temporary: temporary,
|
||||
desc: fmt.Sprintf(format, a...),
|
||||
}
|
||||
}
|
||||
|
||||
// RoundRobin returns a Balancer that selects addresses round-robin. It uses r to watch
|
||||
// the name resolution updates and updates the addresses available correspondingly.
|
||||
func RoundRobin(r naming.Resolver) Balancer {
|
||||
return &roundRobin{r: r}
|
||||
}
|
||||
|
||||
type addrInfo struct {
|
||||
addr Address
|
||||
connected bool
|
||||
}
|
||||
|
||||
type roundRobin struct {
|
||||
r naming.Resolver
|
||||
w naming.Watcher
|
||||
addrs []*addrInfo // all the addresses the client should potentially connect
|
||||
mu sync.Mutex
|
||||
addrCh chan []Address // the channel to notify gRPC internals the list of addresses the client should connect to.
|
||||
next int // index of the next address to return for Get()
|
||||
waitCh chan struct{} // the channel to block when there is no connected address available
|
||||
done bool // The Balancer is closed.
|
||||
}
|
||||
|
||||
func (rr *roundRobin) watchAddrUpdates() error {
|
||||
updates, err := rr.w.Next()
|
||||
if err != nil {
|
||||
grpclog.Printf("grpc: the naming watcher stops working due to %v.\n", err)
|
||||
return err
|
||||
}
|
||||
rr.mu.Lock()
|
||||
defer rr.mu.Unlock()
|
||||
for _, update := range updates {
|
||||
addr := Address{
|
||||
Addr: update.Addr,
|
||||
Metadata: update.Metadata,
|
||||
}
|
||||
switch update.Op {
|
||||
case naming.Add:
|
||||
var exist bool
|
||||
for _, v := range rr.addrs {
|
||||
if addr == v.addr {
|
||||
exist = true
|
||||
grpclog.Println("grpc: The name resolver wanted to add an existing address: ", addr)
|
||||
break
|
||||
}
|
||||
}
|
||||
if exist {
|
||||
continue
|
||||
}
|
||||
rr.addrs = append(rr.addrs, &addrInfo{addr: addr})
|
||||
case naming.Delete:
|
||||
for i, v := range rr.addrs {
|
||||
if addr == v.addr {
|
||||
copy(rr.addrs[i:], rr.addrs[i+1:])
|
||||
rr.addrs = rr.addrs[:len(rr.addrs)-1]
|
||||
break
|
||||
}
|
||||
}
|
||||
default:
|
||||
grpclog.Println("Unknown update.Op ", update.Op)
|
||||
}
|
||||
}
|
||||
// Make a copy of rr.addrs and write it onto rr.addrCh so that gRPC internals gets notified.
|
||||
open := make([]Address, len(rr.addrs))
|
||||
for i, v := range rr.addrs {
|
||||
open[i] = v.addr
|
||||
}
|
||||
if rr.done {
|
||||
return ErrClientConnClosing
|
||||
}
|
||||
rr.addrCh <- open
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rr *roundRobin) Start(target string) error {
|
||||
if rr.r == nil {
|
||||
// If there is no name resolver installed, it is not needed to
|
||||
// do name resolution. In this case, target is added into rr.addrs
|
||||
// as the only address available and rr.addrCh stays nil.
|
||||
rr.addrs = append(rr.addrs, &addrInfo{addr: Address{Addr: target}})
|
||||
return nil
|
||||
}
|
||||
w, err := rr.r.Resolve(target)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rr.w = w
|
||||
rr.addrCh = make(chan []Address)
|
||||
go func() {
|
||||
for {
|
||||
if err := rr.watchAddrUpdates(); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Up sets the connected state of addr and sends notification if there are pending
|
||||
// Get() calls.
|
||||
func (rr *roundRobin) Up(addr Address) func(error) {
|
||||
rr.mu.Lock()
|
||||
defer rr.mu.Unlock()
|
||||
var cnt int
|
||||
for _, a := range rr.addrs {
|
||||
if a.addr == addr {
|
||||
if a.connected {
|
||||
return nil
|
||||
}
|
||||
a.connected = true
|
||||
}
|
||||
if a.connected {
|
||||
cnt++
|
||||
}
|
||||
}
|
||||
// addr is only one which is connected. Notify the Get() callers who are blocking.
|
||||
if cnt == 1 && rr.waitCh != nil {
|
||||
close(rr.waitCh)
|
||||
rr.waitCh = nil
|
||||
}
|
||||
return func(err error) {
|
||||
rr.down(addr, err)
|
||||
}
|
||||
}
|
||||
|
||||
// down unsets the connected state of addr.
|
||||
func (rr *roundRobin) down(addr Address, err error) {
|
||||
rr.mu.Lock()
|
||||
defer rr.mu.Unlock()
|
||||
for _, a := range rr.addrs {
|
||||
if addr == a.addr {
|
||||
a.connected = false
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get returns the next addr in the rotation.
|
||||
func (rr *roundRobin) Get(ctx context.Context, opts BalancerGetOptions) (addr Address, put func(), err error) {
|
||||
var ch chan struct{}
|
||||
rr.mu.Lock()
|
||||
if rr.done {
|
||||
rr.mu.Unlock()
|
||||
err = ErrClientConnClosing
|
||||
return
|
||||
}
|
||||
|
||||
if len(rr.addrs) > 0 {
|
||||
if rr.next >= len(rr.addrs) {
|
||||
rr.next = 0
|
||||
}
|
||||
next := rr.next
|
||||
for {
|
||||
a := rr.addrs[next]
|
||||
next = (next + 1) % len(rr.addrs)
|
||||
if a.connected {
|
||||
addr = a.addr
|
||||
rr.next = next
|
||||
rr.mu.Unlock()
|
||||
return
|
||||
}
|
||||
if next == rr.next {
|
||||
// Has iterated all the possible address but none is connected.
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if !opts.BlockingWait {
|
||||
if len(rr.addrs) == 0 {
|
||||
rr.mu.Unlock()
|
||||
err = fmt.Errorf("there is no address available")
|
||||
return
|
||||
}
|
||||
// Returns the next addr on rr.addrs for failfast RPCs.
|
||||
addr = rr.addrs[rr.next].addr
|
||||
rr.next++
|
||||
rr.mu.Unlock()
|
||||
return
|
||||
}
|
||||
// Wait on rr.waitCh for non-failfast RPCs.
|
||||
if rr.waitCh == nil {
|
||||
ch = make(chan struct{})
|
||||
rr.waitCh = ch
|
||||
} else {
|
||||
ch = rr.waitCh
|
||||
}
|
||||
rr.mu.Unlock()
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
err = ctx.Err()
|
||||
return
|
||||
case <-ch:
|
||||
rr.mu.Lock()
|
||||
if rr.done {
|
||||
rr.mu.Unlock()
|
||||
err = ErrClientConnClosing
|
||||
return
|
||||
}
|
||||
|
||||
if len(rr.addrs) > 0 {
|
||||
if rr.next >= len(rr.addrs) {
|
||||
rr.next = 0
|
||||
}
|
||||
next := rr.next
|
||||
for {
|
||||
a := rr.addrs[next]
|
||||
next = (next + 1) % len(rr.addrs)
|
||||
if a.connected {
|
||||
addr = a.addr
|
||||
rr.next = next
|
||||
rr.mu.Unlock()
|
||||
return
|
||||
}
|
||||
if next == rr.next {
|
||||
// Has iterated all the possible address but none is connected.
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
// The newly added addr got removed by Down() again.
|
||||
if rr.waitCh == nil {
|
||||
ch = make(chan struct{})
|
||||
rr.waitCh = ch
|
||||
} else {
|
||||
ch = rr.waitCh
|
||||
}
|
||||
rr.mu.Unlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (rr *roundRobin) Notify() <-chan []Address {
|
||||
return rr.addrCh
|
||||
}
|
||||
|
||||
func (rr *roundRobin) Close() error {
|
||||
rr.mu.Lock()
|
||||
defer rr.mu.Unlock()
|
||||
rr.done = true
|
||||
if rr.w != nil {
|
||||
rr.w.Close()
|
||||
}
|
||||
if rr.waitCh != nil {
|
||||
close(rr.waitCh)
|
||||
rr.waitCh = nil
|
||||
}
|
||||
if rr.addrCh != nil {
|
||||
close(rr.addrCh)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
-322
@@ -1,322 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* Copyright 2016, 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 (
|
||||
"fmt"
|
||||
"math"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/naming"
|
||||
)
|
||||
|
||||
type testWatcher struct {
|
||||
// the channel to receives name resolution updates
|
||||
update chan *naming.Update
|
||||
// the side channel to get to know how many updates in a batch
|
||||
side chan int
|
||||
// the channel to notifiy update injector that the update reading is done
|
||||
readDone chan int
|
||||
}
|
||||
|
||||
func (w *testWatcher) Next() (updates []*naming.Update, err error) {
|
||||
n := <-w.side
|
||||
if n == 0 {
|
||||
return nil, fmt.Errorf("w.side is closed")
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
u := <-w.update
|
||||
if u != nil {
|
||||
updates = append(updates, u)
|
||||
}
|
||||
}
|
||||
w.readDone <- 0
|
||||
return
|
||||
}
|
||||
|
||||
func (w *testWatcher) Close() {
|
||||
}
|
||||
|
||||
// Inject naming resolution updates to the testWatcher.
|
||||
func (w *testWatcher) inject(updates []*naming.Update) {
|
||||
w.side <- len(updates)
|
||||
for _, u := range updates {
|
||||
w.update <- u
|
||||
}
|
||||
<-w.readDone
|
||||
}
|
||||
|
||||
type testNameResolver struct {
|
||||
w *testWatcher
|
||||
addr string
|
||||
}
|
||||
|
||||
func (r *testNameResolver) Resolve(target string) (naming.Watcher, error) {
|
||||
r.w = &testWatcher{
|
||||
update: make(chan *naming.Update, 1),
|
||||
side: make(chan int, 1),
|
||||
readDone: make(chan int),
|
||||
}
|
||||
r.w.side <- 1
|
||||
r.w.update <- &naming.Update{
|
||||
Op: naming.Add,
|
||||
Addr: r.addr,
|
||||
}
|
||||
go func() {
|
||||
<-r.w.readDone
|
||||
}()
|
||||
return r.w, nil
|
||||
}
|
||||
|
||||
func startServers(t *testing.T, numServers int, maxStreams uint32) ([]*server, *testNameResolver) {
|
||||
var servers []*server
|
||||
for i := 0; i < numServers; i++ {
|
||||
s := newTestServer()
|
||||
servers = append(servers, s)
|
||||
go s.start(t, 0, maxStreams)
|
||||
s.wait(t, 2*time.Second)
|
||||
}
|
||||
// Point to server[0]
|
||||
addr := "127.0.0.1:" + servers[0].port
|
||||
return servers, &testNameResolver{
|
||||
addr: addr,
|
||||
}
|
||||
}
|
||||
|
||||
func TestNameDiscovery(t *testing.T) {
|
||||
// Start 2 servers on 2 ports.
|
||||
numServers := 2
|
||||
servers, r := startServers(t, numServers, math.MaxUint32)
|
||||
cc, err := Dial("foo.bar.com", WithBalancer(RoundRobin(r)), WithBlock(), WithInsecure(), WithCodec(testCodec{}))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create ClientConn: %v", err)
|
||||
}
|
||||
req := "port"
|
||||
var reply string
|
||||
if err := Invoke(context.Background(), "/foo/bar", &req, &reply, cc); err == nil || ErrorDesc(err) != servers[0].port {
|
||||
t.Fatalf("grpc.Invoke(_, _, _, _, _) = %v, want %s", err, servers[0].port)
|
||||
}
|
||||
// Inject the name resolution change to remove servers[0] and add servers[1].
|
||||
var updates []*naming.Update
|
||||
updates = append(updates, &naming.Update{
|
||||
Op: naming.Delete,
|
||||
Addr: "127.0.0.1:" + servers[0].port,
|
||||
})
|
||||
updates = append(updates, &naming.Update{
|
||||
Op: naming.Add,
|
||||
Addr: "127.0.0.1:" + servers[1].port,
|
||||
})
|
||||
r.w.inject(updates)
|
||||
// Loop until the rpcs in flight talks to servers[1].
|
||||
for {
|
||||
if err := Invoke(context.Background(), "/foo/bar", &req, &reply, cc); err != nil && ErrorDesc(err) == servers[1].port {
|
||||
break
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
cc.Close()
|
||||
for i := 0; i < numServers; i++ {
|
||||
servers[i].stop()
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmptyAddrs(t *testing.T) {
|
||||
servers, r := startServers(t, 1, math.MaxUint32)
|
||||
cc, err := Dial("foo.bar.com", WithBalancer(RoundRobin(r)), WithBlock(), WithInsecure(), WithCodec(testCodec{}))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create ClientConn: %v", err)
|
||||
}
|
||||
var reply string
|
||||
if err := Invoke(context.Background(), "/foo/bar", &expectedRequest, &reply, cc); err != nil || reply != expectedResponse {
|
||||
t.Fatalf("grpc.Invoke(_, _, _, _, _) = %v, reply = %q, want %q, <nil>", err, reply, expectedResponse)
|
||||
}
|
||||
// Inject name resolution change to remove the server so that there is no address
|
||||
// available after that.
|
||||
u := &naming.Update{
|
||||
Op: naming.Delete,
|
||||
Addr: "127.0.0.1:" + servers[0].port,
|
||||
}
|
||||
r.w.inject([]*naming.Update{u})
|
||||
// Loop until the above updates apply.
|
||||
for {
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
ctx, _ := context.WithTimeout(context.Background(), 10*time.Millisecond)
|
||||
if err := Invoke(ctx, "/foo/bar", &expectedRequest, &reply, cc); err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
cc.Close()
|
||||
servers[0].stop()
|
||||
}
|
||||
|
||||
func TestRoundRobin(t *testing.T) {
|
||||
// Start 3 servers on 3 ports.
|
||||
numServers := 3
|
||||
servers, r := startServers(t, numServers, math.MaxUint32)
|
||||
cc, err := Dial("foo.bar.com", WithBalancer(RoundRobin(r)), WithBlock(), WithInsecure(), WithCodec(testCodec{}))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create ClientConn: %v", err)
|
||||
}
|
||||
// Add servers[1] to the service discovery.
|
||||
u := &naming.Update{
|
||||
Op: naming.Add,
|
||||
Addr: "127.0.0.1:" + servers[1].port,
|
||||
}
|
||||
r.w.inject([]*naming.Update{u})
|
||||
req := "port"
|
||||
var reply string
|
||||
// Loop until servers[1] is up
|
||||
for {
|
||||
if err := Invoke(context.Background(), "/foo/bar", &req, &reply, cc); err != nil && ErrorDesc(err) == servers[1].port {
|
||||
break
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
// Add server2[2] to the service discovery.
|
||||
u = &naming.Update{
|
||||
Op: naming.Add,
|
||||
Addr: "127.0.0.1:" + servers[2].port,
|
||||
}
|
||||
r.w.inject([]*naming.Update{u})
|
||||
// Loop until both servers[2] are up.
|
||||
for {
|
||||
if err := Invoke(context.Background(), "/foo/bar", &req, &reply, cc); err != nil && ErrorDesc(err) == servers[2].port {
|
||||
break
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
// Check the incoming RPCs served in a round-robin manner.
|
||||
for i := 0; i < 10; i++ {
|
||||
if err := Invoke(context.Background(), "/foo/bar", &req, &reply, cc); err == nil || ErrorDesc(err) != servers[i%numServers].port {
|
||||
t.Fatalf("Index %d: Invoke(_, _, _, _, _) = %v, want %s", i, err, servers[i%numServers].port)
|
||||
}
|
||||
}
|
||||
cc.Close()
|
||||
for i := 0; i < numServers; i++ {
|
||||
servers[i].stop()
|
||||
}
|
||||
}
|
||||
|
||||
func TestCloseWithPendingRPC(t *testing.T) {
|
||||
servers, r := startServers(t, 1, math.MaxUint32)
|
||||
cc, err := Dial("foo.bar.com", WithBalancer(RoundRobin(r)), WithBlock(), WithInsecure(), WithCodec(testCodec{}))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create ClientConn: %v", err)
|
||||
}
|
||||
var reply string
|
||||
if err := Invoke(context.Background(), "/foo/bar", &expectedRequest, &reply, cc, FailFast(false)); err != nil {
|
||||
t.Fatalf("grpc.Invoke(_, _, _, _, _) = %v, want %s", err, servers[0].port)
|
||||
}
|
||||
// Remove the server.
|
||||
updates := []*naming.Update{&naming.Update{
|
||||
Op: naming.Delete,
|
||||
Addr: "127.0.0.1:" + servers[0].port,
|
||||
}}
|
||||
r.w.inject(updates)
|
||||
// Loop until the above update applies.
|
||||
for {
|
||||
ctx, _ := context.WithTimeout(context.Background(), 10*time.Millisecond)
|
||||
if err := Invoke(ctx, "/foo/bar", &expectedRequest, &reply, cc, FailFast(false)); Code(err) == codes.DeadlineExceeded {
|
||||
break
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
// Issue 2 RPCs which should be completed with error status once cc is closed.
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(2)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
var reply string
|
||||
if err := Invoke(context.Background(), "/foo/bar", &expectedRequest, &reply, cc, FailFast(false)); err == nil {
|
||||
t.Errorf("grpc.Invoke(_, _, _, _, _) = %v, want not nil", err)
|
||||
}
|
||||
}()
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
var reply string
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
if err := Invoke(context.Background(), "/foo/bar", &expectedRequest, &reply, cc, FailFast(false)); err == nil {
|
||||
t.Errorf("grpc.Invoke(_, _, _, _, _) = %v, want not nil", err)
|
||||
}
|
||||
}()
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
cc.Close()
|
||||
wg.Wait()
|
||||
servers[0].stop()
|
||||
}
|
||||
|
||||
func TestGetOnWaitChannel(t *testing.T) {
|
||||
servers, r := startServers(t, 1, math.MaxUint32)
|
||||
cc, err := Dial("foo.bar.com", WithBalancer(RoundRobin(r)), WithBlock(), WithInsecure(), WithCodec(testCodec{}))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create ClientConn: %v", err)
|
||||
}
|
||||
// Remove all servers so that all upcoming RPCs will block on waitCh.
|
||||
updates := []*naming.Update{&naming.Update{
|
||||
Op: naming.Delete,
|
||||
Addr: "127.0.0.1:" + servers[0].port,
|
||||
}}
|
||||
r.w.inject(updates)
|
||||
for {
|
||||
var reply string
|
||||
ctx, _ := context.WithTimeout(context.Background(), 10*time.Millisecond)
|
||||
if err := Invoke(ctx, "/foo/bar", &expectedRequest, &reply, cc, FailFast(false)); Code(err) == codes.DeadlineExceeded {
|
||||
break
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
var reply string
|
||||
if err := Invoke(context.Background(), "/foo/bar", &expectedRequest, &reply, cc, FailFast(false)); err != nil {
|
||||
t.Errorf("grpc.Invoke(_, _, _, _, _) = %v, want <nil>", err)
|
||||
}
|
||||
}()
|
||||
// Add a connected server to get the above RPC through.
|
||||
updates = []*naming.Update{&naming.Update{
|
||||
Op: naming.Add,
|
||||
Addr: "127.0.0.1:" + servers[0].port,
|
||||
}}
|
||||
r.w.inject(updates)
|
||||
// Wait until the above RPC succeeds.
|
||||
wg.Wait()
|
||||
cc.Close()
|
||||
servers[0].stop()
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user