1 #region Copyright notice and license
3 // Copyright 2018 gRPC authors.
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
9 // http://www.apache.org/licenses/LICENSE-2.0
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
21 using Grpc.Core.Utils;
23 namespace Grpc.Core.Interceptors
26 /// Extends the CallInvoker class to provide the interceptor facility on the client side.
28 public static class CallInvokerExtensions
31 /// Returns a <see cref="Grpc.Core.CallInvoker" /> instance that intercepts
32 /// the invoker with the given interceptor.
34 /// <param name="invoker">The underlying invoker to intercept.</param>
35 /// <param name="interceptor">The interceptor to intercept calls to the invoker with.</param>
37 /// Multiple interceptors can be added on top of each other by calling
38 /// "invoker.Intercept(a, b, c)". The order of invocation will be "a", "b", and then "c".
39 /// Interceptors can be later added to an existing intercepted CallInvoker, effectively
40 /// building a chain like "invoker.Intercept(c).Intercept(b).Intercept(a)". Note that
41 /// in this case, the last interceptor added will be the first to take control.
43 public static CallInvoker Intercept(this CallInvoker invoker, Interceptor interceptor)
45 return new InterceptingCallInvoker(invoker, interceptor);
49 /// Returns a <see cref="Grpc.Core.CallInvoker" /> instance that intercepts
50 /// the invoker with the given interceptors.
52 /// <param name="invoker">The channel to intercept.</param>
53 /// <param name="interceptors">
54 /// An array of interceptors to intercept the calls to the invoker with.
55 /// Control is passed to the interceptors in the order specified.
58 /// Multiple interceptors can be added on top of each other by calling
59 /// "invoker.Intercept(a, b, c)". The order of invocation will be "a", "b", and then "c".
60 /// Interceptors can be later added to an existing intercepted CallInvoker, effectively
61 /// building a chain like "invoker.Intercept(c).Intercept(b).Intercept(a)". Note that
62 /// in this case, the last interceptor added will be the first to take control.
64 public static CallInvoker Intercept(this CallInvoker invoker, params Interceptor[] interceptors)
66 GrpcPreconditions.CheckNotNull(invoker, nameof(invoker));
67 GrpcPreconditions.CheckNotNull(interceptors, nameof(interceptors));
69 foreach (var interceptor in interceptors.Reverse())
71 invoker = Intercept(invoker, interceptor);
78 /// Returns a <see cref="Grpc.Core.CallInvoker" /> instance that intercepts
79 /// the invoker with the given interceptor.
81 /// <param name="invoker">The underlying invoker to intercept.</param>
82 /// <param name="interceptor">
83 /// An interceptor delegate that takes the request metadata to be sent with an outgoing call
84 /// and returns a <see cref="Grpc.Core.Metadata" /> instance that will replace the existing
85 /// invocation metadata.
88 /// Multiple interceptors can be added on top of each other by
89 /// building a chain like "invoker.Intercept(c).Intercept(b).Intercept(a)". Note that
90 /// in this case, the last interceptor added will be the first to take control.
92 public static CallInvoker Intercept(this CallInvoker invoker, Func<Metadata, Metadata> interceptor)
94 return new InterceptingCallInvoker(invoker, new MetadataInterceptor(interceptor));
97 private class MetadataInterceptor : Interceptor
99 readonly Func<Metadata, Metadata> interceptor;
102 /// Creates a new instance of MetadataInterceptor given the specified interceptor function.
104 public MetadataInterceptor(Func<Metadata, Metadata> interceptor)
106 this.interceptor = GrpcPreconditions.CheckNotNull(interceptor, nameof(interceptor));
109 private ClientInterceptorContext<TRequest, TResponse> GetNewContext<TRequest, TResponse>(ClientInterceptorContext<TRequest, TResponse> context)
110 where TRequest : class
111 where TResponse : class
113 var metadata = context.Options.Headers ?? new Metadata();
114 return new ClientInterceptorContext<TRequest, TResponse>(context.Method, context.Host, context.Options.WithHeaders(interceptor(metadata)));
117 public override TResponse BlockingUnaryCall<TRequest, TResponse>(TRequest request, ClientInterceptorContext<TRequest, TResponse> context, BlockingUnaryCallContinuation<TRequest, TResponse> continuation)
119 return continuation(request, GetNewContext(context));
122 public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(TRequest request, ClientInterceptorContext<TRequest, TResponse> context, AsyncUnaryCallContinuation<TRequest, TResponse> continuation)
124 return continuation(request, GetNewContext(context));
127 public override AsyncServerStreamingCall<TResponse> AsyncServerStreamingCall<TRequest, TResponse>(TRequest request, ClientInterceptorContext<TRequest, TResponse> context, AsyncServerStreamingCallContinuation<TRequest, TResponse> continuation)
129 return continuation(request, GetNewContext(context));
132 public override AsyncClientStreamingCall<TRequest, TResponse> AsyncClientStreamingCall<TRequest, TResponse>(ClientInterceptorContext<TRequest, TResponse> context, AsyncClientStreamingCallContinuation<TRequest, TResponse> continuation)
134 return continuation(GetNewContext(context));
137 public override AsyncDuplexStreamingCall<TRequest, TResponse> AsyncDuplexStreamingCall<TRequest, TResponse>(ClientInterceptorContext<TRequest, TResponse> context, AsyncDuplexStreamingCallContinuation<TRequest, TResponse> continuation)
139 return continuation(GetNewContext(context));