Girish Kumar | 2ed051b | 2020-07-28 16:35:25 +0000 | [diff] [blame] | 1 | // Copyright 2016 Michal Witkowski. All Rights Reserved. |
| 2 | // See LICENSE for licensing terms. |
| 3 | |
| 4 | /* |
| 5 | `grpc_middleware` is a collection of gRPC middleware packages: interceptors, helpers and tools. |
| 6 | |
| 7 | Middleware |
| 8 | |
| 9 | gRPC is a fantastic RPC middleware, which sees a lot of adoption in the Golang world. However, the |
| 10 | upstream gRPC codebase is relatively bare bones. |
| 11 | |
| 12 | This package, and most of its child packages provides commonly needed middleware for gRPC: |
| 13 | client-side interceptors for retires, server-side interceptors for input validation and auth, |
| 14 | functions for chaining said interceptors, metadata convenience methods and more. |
| 15 | |
| 16 | Chaining |
| 17 | |
| 18 | By default, gRPC doesn't allow one to have more than one interceptor either on the client nor on |
| 19 | the server side. `grpc_middleware` provides convenient chaining methods |
| 20 | |
| 21 | Simple way of turning a multiple interceptors into a single interceptor. Here's an example for |
| 22 | server chaining: |
| 23 | |
| 24 | myServer := grpc.NewServer( |
| 25 | grpc.StreamInterceptor(grpc_middleware.ChainStreamServer(loggingStream, monitoringStream, authStream)), |
David K. Bainbridge | e05cf0c | 2021-08-19 03:16:50 +0000 | [diff] [blame] | 26 | grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer(loggingUnary, monitoringUnary, authUnary)), |
Girish Kumar | 2ed051b | 2020-07-28 16:35:25 +0000 | [diff] [blame] | 27 | ) |
| 28 | |
| 29 | These interceptors will be executed from left to right: logging, monitoring and auth. |
| 30 | |
| 31 | Here's an example for client side chaining: |
| 32 | |
| 33 | clientConn, err = grpc.Dial( |
| 34 | address, |
| 35 | grpc.WithUnaryInterceptor(grpc_middleware.ChainUnaryClient(monitoringClientUnary, retryUnary)), |
| 36 | grpc.WithStreamInterceptor(grpc_middleware.ChainStreamClient(monitoringClientStream, retryStream)), |
| 37 | ) |
| 38 | client = pb_testproto.NewTestServiceClient(clientConn) |
| 39 | resp, err := client.PingEmpty(s.ctx, &myservice.Request{Msg: "hello"}) |
| 40 | |
| 41 | These interceptors will be executed from left to right: monitoring and then retry logic. |
| 42 | |
| 43 | The retry interceptor will call every interceptor that follows it whenever when a retry happens. |
| 44 | |
| 45 | Writing Your Own |
| 46 | |
| 47 | Implementing your own interceptor is pretty trivial: there are interfaces for that. But the interesting |
| 48 | bit exposing common data to handlers (and other middleware), similarly to HTTP Middleware design. |
| 49 | For example, you may want to pass the identity of the caller from the auth interceptor all the way |
| 50 | to the handling function. |
| 51 | |
| 52 | For example, a client side interceptor example for auth looks like: |
| 53 | |
| 54 | func FakeAuthUnaryInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { |
| 55 | newCtx := context.WithValue(ctx, "user_id", "john@example.com") |
| 56 | return handler(newCtx, req) |
| 57 | } |
| 58 | |
| 59 | Unfortunately, it's not as easy for streaming RPCs. These have the `context.Context` embedded within |
| 60 | the `grpc.ServerStream` object. To pass values through context, a wrapper (`WrappedServerStream`) is |
| 61 | needed. For example: |
| 62 | |
| 63 | func FakeAuthStreamingInterceptor(srv interface{}, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error { |
| 64 | newStream := grpc_middleware.WrapServerStream(stream) |
| 65 | newStream.WrappedContext = context.WithValue(ctx, "user_id", "john@example.com") |
David K. Bainbridge | e05cf0c | 2021-08-19 03:16:50 +0000 | [diff] [blame] | 66 | return handler(srv, newStream) |
Girish Kumar | 2ed051b | 2020-07-28 16:35:25 +0000 | [diff] [blame] | 67 | } |
| 68 | */ |
| 69 | package grpc_middleware |