1// Copyright 2016 Michal Witkowski. All Rights Reserved. 2// See LICENSE for licensing terms. 3 4// gRPC Server Interceptor chaining middleware. 5 6package grpc_middleware 7 8import ( 9 "context" 10 11 "google.golang.org/grpc" 12) 13 14// ChainUnaryServer creates a single interceptor out of a chain of many interceptors. 15// 16// Execution is done in left-to-right order, including passing of context. 17// For example ChainUnaryServer(one, two, three) will execute one before two before three, and three 18// will see context changes of one and two. 19func ChainUnaryServer(interceptors ...grpc.UnaryServerInterceptor) grpc.UnaryServerInterceptor { 20 n := len(interceptors) 21 22 return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { 23 chainer := func(currentInter grpc.UnaryServerInterceptor, currentHandler grpc.UnaryHandler) grpc.UnaryHandler { 24 return func(currentCtx context.Context, currentReq interface{}) (interface{}, error) { 25 return currentInter(currentCtx, currentReq, info, currentHandler) 26 } 27 } 28 29 chainedHandler := handler 30 for i := n - 1; i >= 0; i-- { 31 chainedHandler = chainer(interceptors[i], chainedHandler) 32 } 33 34 return chainedHandler(ctx, req) 35 } 36} 37 38// ChainStreamServer creates a single interceptor out of a chain of many interceptors. 39// 40// Execution is done in left-to-right order, including passing of context. 41// For example ChainUnaryServer(one, two, three) will execute one before two before three. 42// If you want to pass context between interceptors, use WrapServerStream. 43func ChainStreamServer(interceptors ...grpc.StreamServerInterceptor) grpc.StreamServerInterceptor { 44 n := len(interceptors) 45 46 return func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error { 47 chainer := func(currentInter grpc.StreamServerInterceptor, currentHandler grpc.StreamHandler) grpc.StreamHandler { 48 return func(currentSrv interface{}, currentStream grpc.ServerStream) error { 49 return currentInter(currentSrv, currentStream, info, currentHandler) 50 } 51 } 52 53 chainedHandler := handler 54 for i := n - 1; i >= 0; i-- { 55 chainedHandler = chainer(interceptors[i], chainedHandler) 56 } 57 58 return chainedHandler(srv, ss) 59 } 60} 61 62// ChainUnaryClient creates a single interceptor out of a chain of many interceptors. 63// 64// Execution is done in left-to-right order, including passing of context. 65// For example ChainUnaryClient(one, two, three) will execute one before two before three. 66func ChainUnaryClient(interceptors ...grpc.UnaryClientInterceptor) grpc.UnaryClientInterceptor { 67 n := len(interceptors) 68 69 return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { 70 chainer := func(currentInter grpc.UnaryClientInterceptor, currentInvoker grpc.UnaryInvoker) grpc.UnaryInvoker { 71 return func(currentCtx context.Context, currentMethod string, currentReq, currentRepl interface{}, currentConn *grpc.ClientConn, currentOpts ...grpc.CallOption) error { 72 return currentInter(currentCtx, currentMethod, currentReq, currentRepl, currentConn, currentInvoker, currentOpts...) 73 } 74 } 75 76 chainedInvoker := invoker 77 for i := n - 1; i >= 0; i-- { 78 chainedInvoker = chainer(interceptors[i], chainedInvoker) 79 } 80 81 return chainedInvoker(ctx, method, req, reply, cc, opts...) 82 } 83} 84 85// ChainStreamClient creates a single interceptor out of a chain of many interceptors. 86// 87// Execution is done in left-to-right order, including passing of context. 88// For example ChainStreamClient(one, two, three) will execute one before two before three. 89func ChainStreamClient(interceptors ...grpc.StreamClientInterceptor) grpc.StreamClientInterceptor { 90 n := len(interceptors) 91 92 return func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) { 93 chainer := func(currentInter grpc.StreamClientInterceptor, currentStreamer grpc.Streamer) grpc.Streamer { 94 return func(currentCtx context.Context, currentDesc *grpc.StreamDesc, currentConn *grpc.ClientConn, currentMethod string, currentOpts ...grpc.CallOption) (grpc.ClientStream, error) { 95 return currentInter(currentCtx, currentDesc, currentConn, currentMethod, currentStreamer, currentOpts...) 96 } 97 } 98 99 chainedStreamer := streamer 100 for i := n - 1; i >= 0; i-- { 101 chainedStreamer = chainer(interceptors[i], chainedStreamer) 102 } 103 104 return chainedStreamer(ctx, desc, cc, method, opts...) 105 } 106} 107 108// Chain creates a single interceptor out of a chain of many interceptors. 109// 110// WithUnaryServerChain is a grpc.Server config option that accepts multiple unary interceptors. 111// Basically syntactic sugar. 112func WithUnaryServerChain(interceptors ...grpc.UnaryServerInterceptor) grpc.ServerOption { 113 return grpc.UnaryInterceptor(ChainUnaryServer(interceptors...)) 114} 115 116// WithStreamServerChain is a grpc.Server config option that accepts multiple stream interceptors. 117// Basically syntactic sugar. 118func WithStreamServerChain(interceptors ...grpc.StreamServerInterceptor) grpc.ServerOption { 119 return grpc.StreamInterceptor(ChainStreamServer(interceptors...)) 120} 121