• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #region Copyright notice and license
2 
3 // Copyright 2015-2016 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 Grpc.Core.Interceptors;
21 using Grpc.Core.Internal;
22 using Grpc.Core.Utils;
23 
24 namespace Grpc.Core
25 {
26     /// <summary>
27     /// Generic base class for client-side stubs.
28     /// </summary>
29     public abstract class ClientBase<T> : ClientBase
30         where T : ClientBase<T>
31     {
32         /// <summary>
33         /// Initializes a new instance of <c>ClientBase</c> class that
34         /// throws <c>NotImplementedException</c> upon invocation of any RPC.
35         /// This constructor is only provided to allow creation of test doubles
36         /// for client classes (e.g. mocking requires a parameterless constructor).
37         /// </summary>
ClientBase()38         protected ClientBase() : base()
39         {
40         }
41 
42         /// <summary>
43         /// Initializes a new instance of <c>ClientBase</c> class.
44         /// </summary>
45         /// <param name="configuration">The configuration.</param>
ClientBase(ClientBaseConfiguration configuration)46         protected ClientBase(ClientBaseConfiguration configuration) : base(configuration)
47         {
48         }
49 
50         /// <summary>
51         /// Initializes a new instance of <c>ClientBase</c> class.
52         /// </summary>
53         /// <param name="channel">The channel to use for remote call invocation.</param>
ClientBase(Channel channel)54         public ClientBase(Channel channel) : base(channel)
55         {
56         }
57 
58         /// <summary>
59         /// Initializes a new instance of <c>ClientBase</c> class.
60         /// </summary>
61         /// <param name="callInvoker">The <c>CallInvoker</c> for remote call invocation.</param>
ClientBase(CallInvoker callInvoker)62         public ClientBase(CallInvoker callInvoker) : base(callInvoker)
63         {
64         }
65 
66         /// <summary>
67         /// Creates a new client that sets host field for calls explicitly.
68         /// gRPC supports multiple "hosts" being served by a single server.
69         /// By default (if a client was not created by calling this method),
70         /// host <c>null</c> with the meaning "use default host" is used.
71         /// </summary>
WithHost(string host)72         public T WithHost(string host)
73         {
74             var newConfiguration = this.Configuration.WithHost(host);
75             return NewInstance(newConfiguration);
76         }
77 
78         /// <summary>
79         /// Creates a new instance of client from given <c>ClientBaseConfiguration</c>.
80         /// </summary>
NewInstance(ClientBaseConfiguration configuration)81         protected abstract T NewInstance(ClientBaseConfiguration configuration);
82     }
83 
84     /// <summary>
85     /// Base class for client-side stubs.
86     /// </summary>
87     public abstract class ClientBase
88     {
89         readonly ClientBaseConfiguration configuration;
90         readonly CallInvoker callInvoker;
91 
92         /// <summary>
93         /// Initializes a new instance of <c>ClientBase</c> class that
94         /// throws <c>NotImplementedException</c> upon invocation of any RPC.
95         /// This constructor is only provided to allow creation of test doubles
96         /// for client classes (e.g. mocking requires a parameterless constructor).
97         /// </summary>
ClientBase()98         protected ClientBase() : this(new UnimplementedCallInvoker())
99         {
100         }
101 
102         /// <summary>
103         /// Initializes a new instance of <c>ClientBase</c> class.
104         /// </summary>
105         /// <param name="configuration">The configuration.</param>
ClientBase(ClientBaseConfiguration configuration)106         protected ClientBase(ClientBaseConfiguration configuration)
107         {
108             this.configuration = GrpcPreconditions.CheckNotNull(configuration, "configuration");
109             this.callInvoker = configuration.CreateDecoratedCallInvoker();
110         }
111 
112         /// <summary>
113         /// Initializes a new instance of <c>ClientBase</c> class.
114         /// </summary>
115         /// <param name="channel">The channel to use for remote call invocation.</param>
ClientBase(Channel channel)116         public ClientBase(Channel channel) : this(new DefaultCallInvoker(channel))
117         {
118         }
119 
120         /// <summary>
121         /// Initializes a new instance of <c>ClientBase</c> class.
122         /// </summary>
123         /// <param name="callInvoker">The <c>CallInvoker</c> for remote call invocation.</param>
ClientBase(CallInvoker callInvoker)124         public ClientBase(CallInvoker callInvoker) : this(new ClientBaseConfiguration(callInvoker, null))
125         {
126         }
127 
128         /// <summary>
129         /// Gets the call invoker.
130         /// </summary>
131         protected CallInvoker CallInvoker
132         {
133             get { return this.callInvoker; }
134         }
135 
136         /// <summary>
137         /// Gets the configuration.
138         /// </summary>
139         internal ClientBaseConfiguration Configuration
140         {
141             get { return this.configuration; }
142         }
143 
144         /// <summary>
145         /// Represents configuration of ClientBase. The class itself is visible to
146         /// subclasses, but contents are marked as internal to make the instances opaque.
147         /// The verbose name of this class was chosen to make name clash in generated code
148         /// less likely.
149         /// </summary>
150         protected internal class ClientBaseConfiguration
151         {
152             private class ClientBaseConfigurationInterceptor : Interceptor
153             {
154                 readonly Func<IMethod, string, CallOptions, Tuple<string, CallOptions>> interceptor;
155 
156                 /// <summary>
157                 /// Creates a new instance of ClientBaseConfigurationInterceptor given the specified header and host interceptor function.
158                 /// </summary>
ClientBaseConfigurationInterceptor(Func<IMethod, string, CallOptions, Tuple<string, CallOptions>> interceptor)159                 public ClientBaseConfigurationInterceptor(Func<IMethod, string, CallOptions, Tuple<string, CallOptions>> interceptor)
160                 {
161                     this.interceptor = GrpcPreconditions.CheckNotNull(interceptor, nameof(interceptor));
162                 }
163 
164                 private ClientInterceptorContext<TRequest, TResponse> GetNewContext<TRequest, TResponse>(ClientInterceptorContext<TRequest, TResponse> context)
165                     where TRequest : class
166                     where TResponse : class
167                 {
168                     var newHostAndCallOptions = interceptor(context.Method, context.Host, context.Options);
169                     return new ClientInterceptorContext<TRequest, TResponse>(context.Method, newHostAndCallOptions.Item1, newHostAndCallOptions.Item2);
170                 }
171 
BlockingUnaryCall(TRequest request, ClientInterceptorContext<TRequest, TResponse> context, BlockingUnaryCallContinuation<TRequest, TResponse> continuation)172                 public override TResponse BlockingUnaryCall<TRequest, TResponse>(TRequest request, ClientInterceptorContext<TRequest, TResponse> context, BlockingUnaryCallContinuation<TRequest, TResponse> continuation)
173                 {
174                     return continuation(request, GetNewContext(context));
175                 }
176 
AsyncUnaryCall(TRequest request, ClientInterceptorContext<TRequest, TResponse> context, AsyncUnaryCallContinuation<TRequest, TResponse> continuation)177                 public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(TRequest request, ClientInterceptorContext<TRequest, TResponse> context, AsyncUnaryCallContinuation<TRequest, TResponse> continuation)
178                 {
179                     return continuation(request, GetNewContext(context));
180                 }
181 
AsyncServerStreamingCall(TRequest request, ClientInterceptorContext<TRequest, TResponse> context, AsyncServerStreamingCallContinuation<TRequest, TResponse> continuation)182                 public override AsyncServerStreamingCall<TResponse> AsyncServerStreamingCall<TRequest, TResponse>(TRequest request, ClientInterceptorContext<TRequest, TResponse> context, AsyncServerStreamingCallContinuation<TRequest, TResponse> continuation)
183                 {
184                     return continuation(request, GetNewContext(context));
185                 }
186 
AsyncClientStreamingCall(ClientInterceptorContext<TRequest, TResponse> context, AsyncClientStreamingCallContinuation<TRequest, TResponse> continuation)187                 public override AsyncClientStreamingCall<TRequest, TResponse> AsyncClientStreamingCall<TRequest, TResponse>(ClientInterceptorContext<TRequest, TResponse> context, AsyncClientStreamingCallContinuation<TRequest, TResponse> continuation)
188                 {
189                     return continuation(GetNewContext(context));
190                 }
191 
AsyncDuplexStreamingCall(ClientInterceptorContext<TRequest, TResponse> context, AsyncDuplexStreamingCallContinuation<TRequest, TResponse> continuation)192                 public override AsyncDuplexStreamingCall<TRequest, TResponse> AsyncDuplexStreamingCall<TRequest, TResponse>(ClientInterceptorContext<TRequest, TResponse> context, AsyncDuplexStreamingCallContinuation<TRequest, TResponse> continuation)
193                 {
194                     return continuation(GetNewContext(context));
195                 }
196             }
197 
198             readonly CallInvoker undecoratedCallInvoker;
199             readonly string host;
200 
ClientBaseConfiguration(CallInvoker undecoratedCallInvoker, string host)201             internal ClientBaseConfiguration(CallInvoker undecoratedCallInvoker, string host)
202             {
203                 this.undecoratedCallInvoker = GrpcPreconditions.CheckNotNull(undecoratedCallInvoker);
204                 this.host = host;
205             }
206 
CreateDecoratedCallInvoker()207             internal CallInvoker CreateDecoratedCallInvoker()
208             {
209                 return undecoratedCallInvoker.Intercept(new ClientBaseConfigurationInterceptor((method, host, options) => Tuple.Create(this.host, options)));
210             }
211 
WithHost(string host)212             internal ClientBaseConfiguration WithHost(string host)
213             {
214                 GrpcPreconditions.CheckNotNull(host, nameof(host));
215                 return new ClientBaseConfiguration(this.undecoratedCallInvoker, host);
216             }
217         }
218     }
219 }
220