blob: 407d9332c9b006c8710367cd0e0552779d52daf7 [file] [log] [blame]
khenaidooab1f7bd2019-11-14 14:00:27 -05001// 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 (
khenaidood948f772021-08-11 17:49:24 -04009 "context"
10
khenaidooab1f7bd2019-11-14 14:00:27 -050011 "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.
Abhay Kumara2ae5992025-11-10 14:02:24 +000019//
20// While this can be useful in some scenarios, it is generally advisable to use google.golang.org/grpc.ChainUnaryInterceptor directly.
khenaidooab1f7bd2019-11-14 14:00:27 -050021func ChainUnaryServer(interceptors ...grpc.UnaryServerInterceptor) grpc.UnaryServerInterceptor {
22 n := len(interceptors)
23
Abhay Kumara2ae5992025-11-10 14:02:24 +000024 // Dummy interceptor maintained for backward compatibility to avoid returning nil.
25 if n == 0 {
26 return func(ctx context.Context, req interface{}, _ *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
27 return handler(ctx, req)
28 }
29 }
30
31 // The degenerate case, just return the single wrapped interceptor directly.
32 if n == 1 {
33 return interceptors[0]
34 }
35
36 // Return a function which satisfies the interceptor interface, and which is
37 // a closure over the given list of interceptors to be chained.
khenaidood948f772021-08-11 17:49:24 -040038 return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
Abhay Kumara2ae5992025-11-10 14:02:24 +000039 currHandler := handler
40 // Iterate backwards through all interceptors except the first (outermost).
41 // Wrap each one in a function which satisfies the handler interface, but
42 // is also a closure over the `info` and `handler` parameters. Then pass
43 // each pseudo-handler to the next outer interceptor as the handler to be called.
44 for i := n - 1; i > 0; i-- {
45 // Rebind to loop-local vars so they can be closed over.
46 innerHandler, i := currHandler, i
47 currHandler = func(currentCtx context.Context, currentReq interface{}) (interface{}, error) {
48 return interceptors[i](currentCtx, currentReq, info, innerHandler)
khenaidooab1f7bd2019-11-14 14:00:27 -050049 }
khenaidooab1f7bd2019-11-14 14:00:27 -050050 }
Abhay Kumara2ae5992025-11-10 14:02:24 +000051 // Finally return the result of calling the outermost interceptor with the
52 // outermost pseudo-handler created above as its handler.
53 return interceptors[0](ctx, req, info, currHandler)
khenaidooab1f7bd2019-11-14 14:00:27 -050054 }
55}
56
57// ChainStreamServer creates a single interceptor out of a chain of many interceptors.
58//
59// Execution is done in left-to-right order, including passing of context.
60// For example ChainUnaryServer(one, two, three) will execute one before two before three.
61// If you want to pass context between interceptors, use WrapServerStream.
Abhay Kumara2ae5992025-11-10 14:02:24 +000062//
63// While this can be useful in some scenarios, it is generally advisable to use google.golang.org/grpc.ChainStreamInterceptor directly.
khenaidooab1f7bd2019-11-14 14:00:27 -050064func ChainStreamServer(interceptors ...grpc.StreamServerInterceptor) grpc.StreamServerInterceptor {
65 n := len(interceptors)
66
Abhay Kumara2ae5992025-11-10 14:02:24 +000067 // Dummy interceptor maintained for backward compatibility to avoid returning nil.
68 if n == 0 {
69 return func(srv interface{}, stream grpc.ServerStream, _ *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
70 return handler(srv, stream)
71 }
72 }
73
74 if n == 1 {
75 return interceptors[0]
76 }
77
78 return func(srv interface{}, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
79 currHandler := handler
80 for i := n - 1; i > 0; i-- {
81 innerHandler, i := currHandler, i
82 currHandler = func(currentSrv interface{}, currentStream grpc.ServerStream) error {
83 return interceptors[i](currentSrv, currentStream, info, innerHandler)
khenaidooab1f7bd2019-11-14 14:00:27 -050084 }
khenaidooab1f7bd2019-11-14 14:00:27 -050085 }
Abhay Kumara2ae5992025-11-10 14:02:24 +000086 return interceptors[0](srv, stream, info, currHandler)
khenaidooab1f7bd2019-11-14 14:00:27 -050087 }
88}
89
90// ChainUnaryClient creates a single interceptor out of a chain of many interceptors.
91//
92// Execution is done in left-to-right order, including passing of context.
93// For example ChainUnaryClient(one, two, three) will execute one before two before three.
94func ChainUnaryClient(interceptors ...grpc.UnaryClientInterceptor) grpc.UnaryClientInterceptor {
95 n := len(interceptors)
96
Abhay Kumara2ae5992025-11-10 14:02:24 +000097 // Dummy interceptor maintained for backward compatibility to avoid returning nil.
98 if n == 0 {
99 return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
100 return invoker(ctx, method, req, reply, cc, opts...)
101 }
102 }
103
104 if n == 1 {
105 return interceptors[0]
106 }
107
khenaidooab1f7bd2019-11-14 14:00:27 -0500108 return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
Abhay Kumara2ae5992025-11-10 14:02:24 +0000109 currInvoker := invoker
110 for i := n - 1; i > 0; i-- {
111 innerInvoker, i := currInvoker, i
112 currInvoker = func(currentCtx context.Context, currentMethod string, currentReq, currentRepl interface{}, currentConn *grpc.ClientConn, currentOpts ...grpc.CallOption) error {
113 return interceptors[i](currentCtx, currentMethod, currentReq, currentRepl, currentConn, innerInvoker, currentOpts...)
khenaidood948f772021-08-11 17:49:24 -0400114 }
115 }
Abhay Kumara2ae5992025-11-10 14:02:24 +0000116 return interceptors[0](ctx, method, req, reply, cc, currInvoker, opts...)
khenaidooab1f7bd2019-11-14 14:00:27 -0500117 }
118}
119
120// ChainStreamClient creates a single interceptor out of a chain of many interceptors.
121//
122// Execution is done in left-to-right order, including passing of context.
123// For example ChainStreamClient(one, two, three) will execute one before two before three.
124func ChainStreamClient(interceptors ...grpc.StreamClientInterceptor) grpc.StreamClientInterceptor {
125 n := len(interceptors)
126
Abhay Kumara2ae5992025-11-10 14:02:24 +0000127 // Dummy interceptor maintained for backward compatibility to avoid returning nil.
128 if n == 0 {
129 return func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) {
130 return streamer(ctx, desc, cc, method, opts...)
131 }
132 }
133
134 if n == 1 {
135 return interceptors[0]
136 }
137
khenaidooab1f7bd2019-11-14 14:00:27 -0500138 return func(ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption) (grpc.ClientStream, error) {
Abhay Kumara2ae5992025-11-10 14:02:24 +0000139 currStreamer := streamer
140 for i := n - 1; i > 0; i-- {
141 innerStreamer, i := currStreamer, i
142 currStreamer = func(currentCtx context.Context, currentDesc *grpc.StreamDesc, currentConn *grpc.ClientConn, currentMethod string, currentOpts ...grpc.CallOption) (grpc.ClientStream, error) {
143 return interceptors[i](currentCtx, currentDesc, currentConn, currentMethod, innerStreamer, currentOpts...)
khenaidood948f772021-08-11 17:49:24 -0400144 }
145 }
Abhay Kumara2ae5992025-11-10 14:02:24 +0000146 return interceptors[0](ctx, desc, cc, method, currStreamer, opts...)
khenaidooab1f7bd2019-11-14 14:00:27 -0500147 }
148}
149
150// Chain creates a single interceptor out of a chain of many interceptors.
151//
152// WithUnaryServerChain is a grpc.Server config option that accepts multiple unary interceptors.
153// Basically syntactic sugar.
Abhay Kumara2ae5992025-11-10 14:02:24 +0000154//
155// Deprecated: use google.golang.org/grpc.ChainUnaryInterceptor instead.
khenaidooab1f7bd2019-11-14 14:00:27 -0500156func WithUnaryServerChain(interceptors ...grpc.UnaryServerInterceptor) grpc.ServerOption {
Abhay Kumara2ae5992025-11-10 14:02:24 +0000157 return grpc.ChainUnaryInterceptor(interceptors...)
khenaidooab1f7bd2019-11-14 14:00:27 -0500158}
159
160// WithStreamServerChain is a grpc.Server config option that accepts multiple stream interceptors.
161// Basically syntactic sugar.
Abhay Kumara2ae5992025-11-10 14:02:24 +0000162//
163// Deprecated: use google.golang.org/grpc.ChainStreamInterceptor instead.
khenaidooab1f7bd2019-11-14 14:00:27 -0500164func WithStreamServerChain(interceptors ...grpc.StreamServerInterceptor) grpc.ServerOption {
Abhay Kumara2ae5992025-11-10 14:02:24 +0000165 return grpc.ChainStreamInterceptor(interceptors...)
khenaidooab1f7bd2019-11-14 14:00:27 -0500166}