blob: 857b3e808362782c08a71f7d823b0b20c47c91d7 [file] [log] [blame]
Jens Geyeraa0c8b32019-01-28 23:27:45 +01001// Licensed to the Apache Software Foundation(ASF) under one
2// or more contributor license agreements.See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License. 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,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied. See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18using System;
19using System.Collections.Generic;
20using System.IO;
21using System.Linq;
22using System.Net;
23using System.Net.Security;
24using System.Security.Cryptography.X509Certificates;
25using System.Threading;
26using System.Threading.Tasks;
27using Microsoft.Extensions.Logging;
28using Thrift;
29using Thrift.Protocol;
30using Thrift.Transport;
31using Thrift.Transport.Client;
32using tutorial;
33using shared;
Jens Geyerb11f63c2019-03-14 21:12:38 +010034using Microsoft.Extensions.DependencyInjection;
Kyle Smith7b94dd42019-03-23 17:26:56 +010035using System.Diagnostics;
Jens Geyeraa0c8b32019-01-28 23:27:45 +010036
37namespace Client
38{
39 public class Program
40 {
Jens Geyerb11f63c2019-03-14 21:12:38 +010041 private static ServiceCollection ServiceCollection = new ServiceCollection();
42 private static ILogger Logger;
Jens Geyereacd1d42019-11-20 19:03:14 +010043 private static readonly TConfiguration Configuration = null; // new TConfiguration() if needed
Jens Geyeraa0c8b32019-01-28 23:27:45 +010044
45 private static void DisplayHelp()
46 {
47 Logger.LogInformation(@"
48Usage:
49 Client.exe -help
50 will diplay help information
51
Kyle Smith7b94dd42019-03-23 17:26:56 +010052 Client.exe -tr:<transport> -bf:<buffering> -pr:<protocol> -mc:<numClients>
Jens Geyeraa0c8b32019-01-28 23:27:45 +010053 will run client with specified arguments (tcp transport and binary protocol by default) and with 1 client
54
55Options:
56 -tr (transport):
57 tcp - (default) tcp transport will be used (host - ""localhost"", port - 9090)
Jens Geyeraa0c8b32019-01-28 23:27:45 +010058 namedpipe - namedpipe transport will be used (pipe address - "".test"")
59 http - http transport will be used (address - ""http://localhost:9090"")
60 tcptls - tcp tls transport will be used (host - ""localhost"", port - 9090)
Kyle Smith7b94dd42019-03-23 17:26:56 +010061
62 -bf (buffering):
63 none - (default) no buffering will be used
64 buffered - buffered transport will be used
65 framed - framed transport will be used
Jens Geyeraa0c8b32019-01-28 23:27:45 +010066
67 -pr (protocol):
68 binary - (default) binary protocol will be used
69 compact - compact protocol will be used
70 json - json protocol will be used
71 multiplexed - multiplexed protocol will be used
72
73 -mc (multiple clients):
74 <numClients> - number of multiple clients to connect to server (max 100, default 1)
75
76Sample:
77 Client.exe -tr:tcp -p:binary
78");
79 }
80
81 public static void Main(string[] args)
82 {
83 args = args ?? new string[0];
84
Jens Geyerb11f63c2019-03-14 21:12:38 +010085 ServiceCollection.AddLogging(logging => ConfigureLogging(logging));
86 Logger = ServiceCollection.BuildServiceProvider().GetService<ILoggerFactory>().CreateLogger(nameof(Client));
87
Jens Geyeraa0c8b32019-01-28 23:27:45 +010088 if (args.Any(x => x.StartsWith("-help", StringComparison.OrdinalIgnoreCase)))
89 {
90 DisplayHelp();
91 return;
92 }
93
94 Logger.LogInformation("Starting client...");
95
96 using (var source = new CancellationTokenSource())
97 {
98 RunAsync(args, source.Token).GetAwaiter().GetResult();
99 }
100 }
101
Jens Geyerb11f63c2019-03-14 21:12:38 +0100102 private static void ConfigureLogging(ILoggingBuilder logging)
103 {
104 logging.SetMinimumLevel(LogLevel.Trace);
105 logging.AddConsole();
106 logging.AddDebug();
107 }
108
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100109 private static async Task RunAsync(string[] args, CancellationToken cancellationToken)
110 {
111 var numClients = GetNumberOfClients(args);
112
113 Logger.LogInformation($"Selected # of clients: {numClients}");
114
115 var transports = new TTransport[numClients];
116 for (int i = 0; i < numClients; i++)
117 {
118 var t = GetTransport(args);
119 transports[i] = t;
120 }
121
122 Logger.LogInformation($"Selected client transport: {transports[0]}");
123
124 var protocols = new Tuple<Protocol, TProtocol>[numClients];
125 for (int i = 0; i < numClients; i++)
126 {
127 var p = GetProtocol(args, transports[i]);
128 protocols[i] = p;
129 }
130
131 Logger.LogInformation($"Selected client protocol: {protocols[0].Item1}");
132
133 var tasks = new Task[numClients];
134 for (int i = 0; i < numClients; i++)
135 {
136 var task = RunClientAsync(protocols[i], cancellationToken);
137 tasks[i] = task;
138 }
139
140 Task.WaitAll(tasks);
141
142 await Task.CompletedTask;
143 }
144
145 private static TTransport GetTransport(string[] args)
146 {
Jens Geyereacd1d42019-11-20 19:03:14 +0100147 TTransport transport = new TSocketTransport(IPAddress.Loopback, 9090, Configuration);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100148
Kyle Smith7b94dd42019-03-23 17:26:56 +0100149 // construct endpoint transport
150 var transportArg = args.FirstOrDefault(x => x.StartsWith("-tr"))?.Split(':')?[1];
151 if (Enum.TryParse(transportArg, true, out Transport selectedTransport))
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100152 {
153 switch (selectedTransport)
154 {
155 case Transport.Tcp:
Jens Geyereacd1d42019-11-20 19:03:14 +0100156 transport = new TSocketTransport(IPAddress.Loopback, 9090, Configuration);
Kyle Smith7b94dd42019-03-23 17:26:56 +0100157 break;
158
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100159 case Transport.NamedPipe:
Jens Geyereacd1d42019-11-20 19:03:14 +0100160 transport = new TNamedPipeTransport(".test", Configuration);
Kyle Smith7b94dd42019-03-23 17:26:56 +0100161 break;
162
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100163 case Transport.Http:
Jens Geyereacd1d42019-11-20 19:03:14 +0100164 transport = new THttpTransport(new Uri("http://localhost:9090"), Configuration);
Kyle Smith7b94dd42019-03-23 17:26:56 +0100165 break;
166
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100167 case Transport.TcpTls:
Jens Geyereacd1d42019-11-20 19:03:14 +0100168 transport = new TTlsSocketTransport(IPAddress.Loopback, 9090, Configuration,
169 GetCertificate(), CertValidator, LocalCertificateSelectionCallback);
Kyle Smith7b94dd42019-03-23 17:26:56 +0100170 break;
171
172 default:
173 Debug.Assert(false, "unhandled case");
174 break;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100175 }
176 }
177
Kyle Smith7b94dd42019-03-23 17:26:56 +0100178 // optionally add layered transport(s)
179 var bufferingArg = args.FirstOrDefault(x => x.StartsWith("-bf"))?.Split(':')?[1];
180 if (Enum.TryParse<Buffering>(bufferingArg, out var selectedBuffering))
181 {
182 switch (selectedBuffering)
183 {
184 case Buffering.Buffered:
185 transport = new TBufferedTransport(transport);
186 break;
187
188 case Buffering.Framed:
189 transport = new TFramedTransport(transport);
190 break;
191
192 default: // layered transport(s) are optional
193 Debug.Assert(selectedBuffering == Buffering.None, "unhandled case");
194 break;
195 }
196 }
197
198 return transport;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100199 }
200
201 private static int GetNumberOfClients(string[] args)
202 {
203 var numClients = args.FirstOrDefault(x => x.StartsWith("-mc"))?.Split(':')?[1];
204
205 Logger.LogInformation($"Selected # of clients: {numClients}");
206
207 int c;
208 if( int.TryParse(numClients, out c) && (0 < c) && (c <= 100))
209 return c;
210 else
211 return 1;
212 }
213
214 private static X509Certificate2 GetCertificate()
215 {
216 // due to files location in net core better to take certs from top folder
217 var certFile = GetCertPath(Directory.GetParent(Directory.GetCurrentDirectory()));
218 return new X509Certificate2(certFile, "ThriftTest");
219 }
220
221 private static string GetCertPath(DirectoryInfo di, int maxCount = 6)
222 {
223 var topDir = di;
224 var certFile =
225 topDir.EnumerateFiles("ThriftTest.pfx", SearchOption.AllDirectories)
226 .FirstOrDefault();
227 if (certFile == null)
228 {
229 if (maxCount == 0)
230 throw new FileNotFoundException("Cannot find file in directories");
231 return GetCertPath(di.Parent, maxCount - 1);
232 }
233
234 return certFile.FullName;
235 }
236
237 private static X509Certificate LocalCertificateSelectionCallback(object sender,
238 string targetHost, X509CertificateCollection localCertificates,
239 X509Certificate remoteCertificate, string[] acceptableIssuers)
240 {
241 return GetCertificate();
242 }
243
244 private static bool CertValidator(object sender, X509Certificate certificate,
245 X509Chain chain, SslPolicyErrors sslPolicyErrors)
246 {
247 return true;
248 }
249
250 private static Tuple<Protocol, TProtocol> GetProtocol(string[] args, TTransport transport)
251 {
252 var protocol = args.FirstOrDefault(x => x.StartsWith("-pr"))?.Split(':')?[1];
253
254 Protocol selectedProtocol;
255 if (Enum.TryParse(protocol, true, out selectedProtocol))
256 {
257 switch (selectedProtocol)
258 {
259 case Protocol.Binary:
260 return new Tuple<Protocol, TProtocol>(selectedProtocol, new TBinaryProtocol(transport));
261 case Protocol.Compact:
262 return new Tuple<Protocol, TProtocol>(selectedProtocol, new TCompactProtocol(transport));
263 case Protocol.Json:
264 return new Tuple<Protocol, TProtocol>(selectedProtocol, new TJsonProtocol(transport));
265 case Protocol.Multiplexed:
266 // it returns BinaryProtocol to avoid making wrapped protocol as public in TProtocolDecorator (in RunClientAsync it will be wrapped into Multiplexed protocol)
267 return new Tuple<Protocol, TProtocol>(selectedProtocol, new TBinaryProtocol(transport));
Kyle Smith7b94dd42019-03-23 17:26:56 +0100268 default:
269 Debug.Assert(false, "unhandled case");
270 break;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100271 }
272 }
273
274 return new Tuple<Protocol, TProtocol>(selectedProtocol, new TBinaryProtocol(transport));
275 }
276
277 private static async Task RunClientAsync(Tuple<Protocol, TProtocol> protocolTuple, CancellationToken cancellationToken)
278 {
279 try
280 {
281 var protocol = protocolTuple.Item2;
282 var protocolType = protocolTuple.Item1;
283
284 TBaseClient client = null;
285
286 try
287 {
288 if (protocolType != Protocol.Multiplexed)
289 {
290
291 client = new Calculator.Client(protocol);
292 await ExecuteCalculatorClientOperations(cancellationToken, (Calculator.Client)client);
293 }
294 else
295 {
296 // it uses binary protocol there to create Multiplexed protocols
297 var multiplex = new TMultiplexedProtocol(protocol, nameof(Calculator));
298 client = new Calculator.Client(multiplex);
299 await ExecuteCalculatorClientOperations(cancellationToken, (Calculator.Client)client);
300
301 multiplex = new TMultiplexedProtocol(protocol, nameof(SharedService));
302 client = new SharedService.Client(multiplex);
303 await ExecuteSharedServiceClientOperations(cancellationToken, (SharedService.Client)client);
304 }
305 }
306 catch (Exception ex)
307 {
308 Logger.LogError($"{client?.ClientId} " + ex);
309 }
310 finally
311 {
312 protocol.Transport.Close();
313 }
314 }
315 catch (TApplicationException x)
316 {
317 Logger.LogError(x.ToString());
318 }
319 }
320
321 private static async Task ExecuteCalculatorClientOperations(CancellationToken cancellationToken, Calculator.Client client)
322 {
323 await client.OpenTransportAsync(cancellationToken);
324
325 // Async version
326
327 Logger.LogInformation($"{client.ClientId} PingAsync()");
328 await client.pingAsync(cancellationToken);
329
330 Logger.LogInformation($"{client.ClientId} AddAsync(1,1)");
331 var sum = await client.addAsync(1, 1, cancellationToken);
332 Logger.LogInformation($"{client.ClientId} AddAsync(1,1)={sum}");
333
334 var work = new Work
335 {
336 Op = Operation.DIVIDE,
337 Num1 = 1,
338 Num2 = 0
339 };
340
341 try
342 {
343 Logger.LogInformation($"{client.ClientId} CalculateAsync(1)");
344 await client.calculateAsync(1, work, cancellationToken);
345 Logger.LogInformation($"{client.ClientId} Whoa we can divide by 0");
346 }
347 catch (InvalidOperation io)
348 {
349 Logger.LogInformation($"{client.ClientId} Invalid operation: " + io);
350 }
351
352 work.Op = Operation.SUBTRACT;
353 work.Num1 = 15;
354 work.Num2 = 10;
355
356 try
357 {
358 Logger.LogInformation($"{client.ClientId} CalculateAsync(1)");
359 var diff = await client.calculateAsync(1, work, cancellationToken);
360 Logger.LogInformation($"{client.ClientId} 15-10={diff}");
361 }
362 catch (InvalidOperation io)
363 {
364 Logger.LogInformation($"{client.ClientId} Invalid operation: " + io);
365 }
366
367 Logger.LogInformation($"{client.ClientId} GetStructAsync(1)");
368 var log = await client.getStructAsync(1, cancellationToken);
369 Logger.LogInformation($"{client.ClientId} Check log: {log.Value}");
370
371 Logger.LogInformation($"{client.ClientId} ZipAsync() with delay 100mc on server side");
372 await client.zipAsync(cancellationToken);
373 }
374 private static async Task ExecuteSharedServiceClientOperations(CancellationToken cancellationToken, SharedService.Client client)
375 {
376 await client.OpenTransportAsync(cancellationToken);
377
378 // Async version
379
380 Logger.LogInformation($"{client.ClientId} SharedService GetStructAsync(1)");
381 var log = await client.getStructAsync(1, cancellationToken);
382 Logger.LogInformation($"{client.ClientId} SharedService Value: {log.Value}");
383 }
384
385
386 private enum Transport
387 {
388 Tcp,
389 NamedPipe,
390 Http,
391 TcpBuffered,
392 Framed,
393 TcpTls
394 }
395
396 private enum Protocol
397 {
398 Binary,
399 Compact,
400 Json,
401 Multiplexed
402 }
Kyle Smith7b94dd42019-03-23 17:26:56 +0100403
404 private enum Buffering
405 {
406 None,
407 Buffered,
408 Framed
409 }
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100410 }
411}