Imported Upstream version 1.22.0
[platform/upstream/grpc.git] / src / csharp / Grpc.Core.Api / Interceptors / CallInvokerExtensions.cs
1 #region Copyright notice and license
2
3 // Copyright 2018 gRPC authors.
4 //
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
8 //
9 //     http://www.apache.org/licenses/LICENSE-2.0
10 //
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.
16
17 #endregion
18
19 using System;
20 using System.Linq;
21 using Grpc.Core.Utils;
22
23 namespace Grpc.Core.Interceptors
24 {
25     /// <summary>
26     /// Extends the CallInvoker class to provide the interceptor facility on the client side.
27     /// </summary>
28     public static class CallInvokerExtensions
29     {
30         /// <summary>
31         /// Returns a <see cref="Grpc.Core.CallInvoker" /> instance that intercepts
32         /// the invoker with the given interceptor.
33         /// </summary>
34         /// <param name="invoker">The underlying invoker to intercept.</param>
35         /// <param name="interceptor">The interceptor to intercept calls to the invoker with.</param>
36         /// <remarks>
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.
42         /// </remarks>
43         public static CallInvoker Intercept(this CallInvoker invoker, Interceptor interceptor)
44         {
45             return new InterceptingCallInvoker(invoker, interceptor);
46         }
47
48         /// <summary>
49         /// Returns a <see cref="Grpc.Core.CallInvoker" /> instance that intercepts
50         /// the invoker with the given interceptors.
51         /// </summary>
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.
56         /// </param>
57         /// <remarks>
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.
63         /// </remarks>
64         public static CallInvoker Intercept(this CallInvoker invoker, params Interceptor[] interceptors)
65         {
66             GrpcPreconditions.CheckNotNull(invoker, nameof(invoker));
67             GrpcPreconditions.CheckNotNull(interceptors, nameof(interceptors));
68
69             foreach (var interceptor in interceptors.Reverse())
70             {
71                 invoker = Intercept(invoker, interceptor);
72             }
73
74             return invoker;
75         }
76
77         /// <summary>
78         /// Returns a <see cref="Grpc.Core.CallInvoker" /> instance that intercepts
79         /// the invoker with the given interceptor.
80         /// </summary>
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.
86         /// </param>
87         /// <remarks>
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.
91         /// </remarks>
92         public static CallInvoker Intercept(this CallInvoker invoker, Func<Metadata, Metadata> interceptor)
93         {
94             return new InterceptingCallInvoker(invoker, new MetadataInterceptor(interceptor));
95         }
96
97         private class MetadataInterceptor : Interceptor
98         {
99             readonly Func<Metadata, Metadata> interceptor;
100
101             /// <summary>
102             /// Creates a new instance of MetadataInterceptor given the specified interceptor function.
103             /// </summary>
104             public MetadataInterceptor(Func<Metadata, Metadata> interceptor)
105             {
106                 this.interceptor = GrpcPreconditions.CheckNotNull(interceptor, nameof(interceptor));
107             }
108
109             private ClientInterceptorContext<TRequest, TResponse> GetNewContext<TRequest, TResponse>(ClientInterceptorContext<TRequest, TResponse> context)
110                 where TRequest : class
111                 where TResponse : class
112             {
113                 var metadata = context.Options.Headers ?? new Metadata();
114                 return new ClientInterceptorContext<TRequest, TResponse>(context.Method, context.Host, context.Options.WithHeaders(interceptor(metadata)));
115             }
116
117             public override TResponse BlockingUnaryCall<TRequest, TResponse>(TRequest request, ClientInterceptorContext<TRequest, TResponse> context, BlockingUnaryCallContinuation<TRequest, TResponse> continuation)
118             {
119                 return continuation(request, GetNewContext(context));
120             }
121
122             public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(TRequest request, ClientInterceptorContext<TRequest, TResponse> context, AsyncUnaryCallContinuation<TRequest, TResponse> continuation)
123             {
124                 return continuation(request, GetNewContext(context));
125             }
126
127             public override AsyncServerStreamingCall<TResponse> AsyncServerStreamingCall<TRequest, TResponse>(TRequest request, ClientInterceptorContext<TRequest, TResponse> context, AsyncServerStreamingCallContinuation<TRequest, TResponse> continuation)
128             {
129                 return continuation(request, GetNewContext(context));
130             }
131
132             public override AsyncClientStreamingCall<TRequest, TResponse> AsyncClientStreamingCall<TRequest, TResponse>(ClientInterceptorContext<TRequest, TResponse> context, AsyncClientStreamingCallContinuation<TRequest, TResponse> continuation)
133             {
134                 return continuation(GetNewContext(context));
135             }
136
137             public override AsyncDuplexStreamingCall<TRequest, TResponse> AsyncDuplexStreamingCall<TRequest, TResponse>(ClientInterceptorContext<TRequest, TResponse> context, AsyncDuplexStreamingCallContinuation<TRequest, TResponse> continuation)
138             {
139                 return continuation(GetNewContext(context));
140             }
141         }
142     }
143 }