8 "google.golang.org/grpc"
9 "google.golang.org/grpc/metadata"
11 "github.com/go-kit/kit/endpoint"
14 // Client wraps a gRPC connection and provides a method that implements
17 client *grpc.ClientConn
21 dec DecodeResponseFunc
22 grpcReply reflect.Type
23 before []ClientRequestFunc
24 after []ClientResponseFunc
27 // NewClient constructs a usable Client for a single remote endpoint.
28 // Pass an zero-value protobuf message of the RPC response type as
29 // the grpcReply argument.
34 enc EncodeRequestFunc,
35 dec DecodeResponseFunc,
36 grpcReply interface{},
37 options ...ClientOption,
41 method: fmt.Sprintf("/%s/%s", serviceName, method),
44 // We are using reflect.Indirect here to allow both reply structs and
45 // pointers to these reply structs. New consumers of the client should
46 // use structs directly, while existing consumers will not break if they
47 // remain to use pointers to structs.
48 grpcReply: reflect.TypeOf(
50 reflect.ValueOf(grpcReply),
53 before: []ClientRequestFunc{},
54 after: []ClientResponseFunc{},
56 for _, option := range options {
62 // ClientOption sets an optional parameter for clients.
63 type ClientOption func(*Client)
65 // ClientBefore sets the RequestFuncs that are applied to the outgoing gRPC
66 // request before it's invoked.
67 func ClientBefore(before ...ClientRequestFunc) ClientOption {
68 return func(c *Client) { c.before = append(c.before, before...) }
71 // ClientAfter sets the ClientResponseFuncs that are applied to the incoming
72 // gRPC response prior to it being decoded. This is useful for obtaining
73 // response metadata and adding onto the context prior to decoding.
74 func ClientAfter(after ...ClientResponseFunc) ClientOption {
75 return func(c *Client) { c.after = append(c.after, after...) }
78 // Endpoint returns a usable endpoint that will invoke the gRPC specified by the
80 func (c Client) Endpoint() endpoint.Endpoint {
81 return func(ctx context.Context, request interface{}) (interface{}, error) {
82 ctx, cancel := context.WithCancel(ctx)
85 req, err := c.enc(ctx, request)
91 for _, f := range c.before {
94 ctx = metadata.NewOutgoingContext(ctx, *md)
96 var header, trailer metadata.MD
97 grpcReply := reflect.New(c.grpcReply).Interface()
99 ctx, c.method, req, grpcReply, c.client,
100 grpc.Header(&header), grpc.Trailer(&trailer),
105 for _, f := range c.after {
106 ctx = f(ctx, header, trailer)
109 response, err := c.dec(ctx, grpcReply)