blob: f3e93ceb0b47d81e8e34c4a811d6b7449d15b622 [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;
Mario Emmenlauer47e49232020-04-07 18:43:46 +020020using System.Diagnostics;
Jens Geyeraa0c8b32019-01-28 23:27:45 +010021using System.IO;
22using System.Linq;
23using System.Net;
24using System.Net.Security;
25using System.Security.Cryptography.X509Certificates;
26using System.Threading;
27using System.Threading.Tasks;
28using Microsoft.Extensions.Logging;
Mario Emmenlauer47e49232020-04-07 18:43:46 +020029using Microsoft.Extensions.DependencyInjection;
Jens Geyeraa0c8b32019-01-28 23:27:45 +010030using Thrift;
31using Thrift.Protocol;
32using Thrift.Transport;
33using Thrift.Transport.Client;
34using tutorial;
35using shared;
36
Jens Geyer0d128322021-02-25 09:42:52 +010037#pragma warning disable IDE0063 // using
38#pragma warning disable IDE0057 // substr
39
Jens Geyeraa0c8b32019-01-28 23:27:45 +010040namespace Client
41{
42 public class Program
43 {
Jens Geyer0d128322021-02-25 09:42:52 +010044 private static readonly ServiceCollection ServiceCollection = new ServiceCollection();
Jens Geyerb11f63c2019-03-14 21:12:38 +010045 private static ILogger Logger;
Jens Geyereacd1d42019-11-20 19:03:14 +010046 private static readonly TConfiguration Configuration = null; // new TConfiguration() if needed
Jens Geyeraa0c8b32019-01-28 23:27:45 +010047
48 private static void DisplayHelp()
49 {
50 Logger.LogInformation(@"
51Usage:
Kengo Sekibee4f2f2019-12-29 17:04:50 +090052 Client -help
Jens Geyeraa0c8b32019-01-28 23:27:45 +010053 will diplay help information
54
Jens Geyer0d128322021-02-25 09:42:52 +010055 Client -tr:<transport> -bf:<buffering> -pr:<protocol> [-mc:<numClients>] [-multiplex]
Jens Geyeraa0c8b32019-01-28 23:27:45 +010056 will run client with specified arguments (tcp transport and binary protocol by default) and with 1 client
57
58Options:
59 -tr (transport):
Jens Geyer0d128322021-02-25 09:42:52 +010060 tcp - (default) tcp transport (localhost:9090)
61 tcptls - tcp tls transport (localhost:9090)
62 namedpipe - namedpipe transport (pipe "".test"")
63 http - http transport (http://localhost:9090)
Kyle Smith7b94dd42019-03-23 17:26:56 +010064
65 -bf (buffering):
Jens Geyer0d128322021-02-25 09:42:52 +010066 none - (default) no buffering
67 buffered - buffered transport
68 framed - framed transport
Jens Geyeraa0c8b32019-01-28 23:27:45 +010069
70 -pr (protocol):
Jens Geyer0d128322021-02-25 09:42:52 +010071 binary - (default) binary protocol
72 compact - compact protocol
73 json - json protocol
74
75 -multiplex - adds multiplexed protocol
Jens Geyeraa0c8b32019-01-28 23:27:45 +010076
77 -mc (multiple clients):
78 <numClients> - number of multiple clients to connect to server (max 100, default 1)
79
80Sample:
Kengo Sekibee4f2f2019-12-29 17:04:50 +090081 Client -tr:tcp -pr:binary
Jens Geyeraa0c8b32019-01-28 23:27:45 +010082");
83 }
84
85 public static void Main(string[] args)
86 {
Jens Geyer0d128322021-02-25 09:42:52 +010087 args ??= Array.Empty<string>();
Jens Geyeraa0c8b32019-01-28 23:27:45 +010088
Jens Geyerb11f63c2019-03-14 21:12:38 +010089 ServiceCollection.AddLogging(logging => ConfigureLogging(logging));
Kengo Sekibee4f2f2019-12-29 17:04:50 +090090 using (var serviceProvider = ServiceCollection.BuildServiceProvider())
Jens Geyeraa0c8b32019-01-28 23:27:45 +010091 {
Kengo Sekibee4f2f2019-12-29 17:04:50 +090092 Logger = serviceProvider.GetService<ILoggerFactory>().CreateLogger(nameof(Client));
Jens Geyeraa0c8b32019-01-28 23:27:45 +010093
Kengo Sekibee4f2f2019-12-29 17:04:50 +090094 if (args.Any(x => x.StartsWith("-help", StringComparison.OrdinalIgnoreCase)))
95 {
96 DisplayHelp();
97 return;
98 }
Jens Geyeraa0c8b32019-01-28 23:27:45 +010099
Kengo Sekibee4f2f2019-12-29 17:04:50 +0900100 Logger.LogInformation("Starting client...");
101
102 using (var source = new CancellationTokenSource())
103 {
104 RunAsync(args, source.Token).GetAwaiter().GetResult();
105 }
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100106 }
107 }
108
Jens Geyerb11f63c2019-03-14 21:12:38 +0100109 private static void ConfigureLogging(ILoggingBuilder logging)
110 {
111 logging.SetMinimumLevel(LogLevel.Trace);
112 logging.AddConsole();
113 logging.AddDebug();
114 }
115
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100116 private static async Task RunAsync(string[] args, CancellationToken cancellationToken)
117 {
118 var numClients = GetNumberOfClients(args);
119
120 Logger.LogInformation($"Selected # of clients: {numClients}");
121
Jens Geyer0d128322021-02-25 09:42:52 +0100122 var transport = GetTransport(args);
123 Logger.LogInformation($"Selected client transport: {transport}");
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100124
Jens Geyer0d128322021-02-25 09:42:52 +0100125 var protocol = MakeProtocol( args, MakeTransport(args));
126 Logger.LogInformation($"Selected client protocol: {GetProtocol(args)}");
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100127
Jens Geyer0d128322021-02-25 09:42:52 +0100128 var mplex = GetMultiplex(args);
129 Logger.LogInformation("Multiplex " + (mplex ? "yes" : "no"));
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100130
131 var tasks = new Task[numClients];
132 for (int i = 0; i < numClients; i++)
133 {
Jens Geyer0d128322021-02-25 09:42:52 +0100134 var task = RunClientAsync(protocol, mplex, cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100135 tasks[i] = task;
136 }
137
Jens Geyer0d128322021-02-25 09:42:52 +0100138 Task.WaitAll(tasks,cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100139 await Task.CompletedTask;
140 }
141
Jens Geyer0d128322021-02-25 09:42:52 +0100142 private static bool GetMultiplex(string[] args)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100143 {
Jens Geyer0d128322021-02-25 09:42:52 +0100144 var mplex = args.FirstOrDefault(x => x.StartsWith("-multiplex"));
145 return !string.IsNullOrEmpty(mplex);
146 }
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100147
Jens Geyer0d128322021-02-25 09:42:52 +0100148 private static Protocol GetProtocol(string[] args)
149 {
150 var protocol = args.FirstOrDefault(x => x.StartsWith("-pr"))?.Split(':')?[1];
151 if (string.IsNullOrEmpty(protocol))
152 return Protocol.Binary;
153
154 protocol = protocol.Substring(0, 1).ToUpperInvariant() + protocol.Substring(1).ToLowerInvariant();
155 if (Enum.TryParse(protocol, true, out Protocol selectedProtocol))
156 return selectedProtocol;
157 else
158 return Protocol.Binary;
159 }
160
161 private static Buffering GetBuffering(string[] args)
162 {
163 var buffering = args.FirstOrDefault(x => x.StartsWith("-bf"))?.Split(":")?[1];
164 if (string.IsNullOrEmpty(buffering))
165 return Buffering.None;
166
167 buffering = buffering.Substring(0, 1).ToUpperInvariant() + buffering.Substring(1).ToLowerInvariant();
168 if (Enum.TryParse<Buffering>(buffering, out var selectedBuffering))
169 return selectedBuffering;
170 else
171 return Buffering.None;
172 }
173
174 private static Transport GetTransport(string[] args)
175 {
176 var transport = args.FirstOrDefault(x => x.StartsWith("-tr"))?.Split(':')?[1];
177 if (string.IsNullOrEmpty(transport))
178 return Transport.Tcp;
179
180 transport = transport.Substring(0, 1).ToUpperInvariant() + transport.Substring(1).ToLowerInvariant();
181 if (Enum.TryParse(transport, true, out Transport selectedTransport))
182 return selectedTransport;
183 else
184 return Transport.Tcp;
185 }
186
187
188 private static TTransport MakeTransport(string[] args)
189 {
Kyle Smith7b94dd42019-03-23 17:26:56 +0100190 // construct endpoint transport
Jens Geyer0d128322021-02-25 09:42:52 +0100191 TTransport transport = null;
192 Transport selectedTransport = GetTransport(args);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100193 {
194 switch (selectedTransport)
195 {
196 case Transport.Tcp:
Jens Geyereacd1d42019-11-20 19:03:14 +0100197 transport = new TSocketTransport(IPAddress.Loopback, 9090, Configuration);
Kyle Smith7b94dd42019-03-23 17:26:56 +0100198 break;
199
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100200 case Transport.NamedPipe:
Jens Geyereacd1d42019-11-20 19:03:14 +0100201 transport = new TNamedPipeTransport(".test", Configuration);
Kyle Smith7b94dd42019-03-23 17:26:56 +0100202 break;
203
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100204 case Transport.Http:
Jens Geyereacd1d42019-11-20 19:03:14 +0100205 transport = new THttpTransport(new Uri("http://localhost:9090"), Configuration);
Kyle Smith7b94dd42019-03-23 17:26:56 +0100206 break;
207
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100208 case Transport.TcpTls:
Jens Geyereacd1d42019-11-20 19:03:14 +0100209 transport = new TTlsSocketTransport(IPAddress.Loopback, 9090, Configuration,
210 GetCertificate(), CertValidator, LocalCertificateSelectionCallback);
Kyle Smith7b94dd42019-03-23 17:26:56 +0100211 break;
212
213 default:
214 Debug.Assert(false, "unhandled case");
215 break;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100216 }
217 }
218
Kyle Smith7b94dd42019-03-23 17:26:56 +0100219 // optionally add layered transport(s)
Jens Geyer0d128322021-02-25 09:42:52 +0100220 Buffering selectedBuffering = GetBuffering(args);
221 switch (selectedBuffering)
Kyle Smith7b94dd42019-03-23 17:26:56 +0100222 {
Jens Geyer0d128322021-02-25 09:42:52 +0100223 case Buffering.Buffered:
224 transport = new TBufferedTransport(transport);
225 break;
Kyle Smith7b94dd42019-03-23 17:26:56 +0100226
Jens Geyer0d128322021-02-25 09:42:52 +0100227 case Buffering.Framed:
228 transport = new TFramedTransport(transport);
229 break;
Kyle Smith7b94dd42019-03-23 17:26:56 +0100230
Jens Geyer0d128322021-02-25 09:42:52 +0100231 default: // layered transport(s) are optional
232 Debug.Assert(selectedBuffering == Buffering.None, "unhandled case");
233 break;
Kyle Smith7b94dd42019-03-23 17:26:56 +0100234 }
235
236 return transport;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100237 }
238
239 private static int GetNumberOfClients(string[] args)
240 {
241 var numClients = args.FirstOrDefault(x => x.StartsWith("-mc"))?.Split(':')?[1];
242
243 Logger.LogInformation($"Selected # of clients: {numClients}");
244
Jens Geyer0d128322021-02-25 09:42:52 +0100245 if (int.TryParse(numClients, out int c) && (0 < c) && (c <= 100))
246 return c;
247 else
248 return 1;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100249 }
250
251 private static X509Certificate2 GetCertificate()
252 {
253 // due to files location in net core better to take certs from top folder
254 var certFile = GetCertPath(Directory.GetParent(Directory.GetCurrentDirectory()));
255 return new X509Certificate2(certFile, "ThriftTest");
256 }
257
258 private static string GetCertPath(DirectoryInfo di, int maxCount = 6)
259 {
260 var topDir = di;
261 var certFile =
262 topDir.EnumerateFiles("ThriftTest.pfx", SearchOption.AllDirectories)
263 .FirstOrDefault();
264 if (certFile == null)
265 {
266 if (maxCount == 0)
267 throw new FileNotFoundException("Cannot find file in directories");
268 return GetCertPath(di.Parent, maxCount - 1);
269 }
270
271 return certFile.FullName;
272 }
273
274 private static X509Certificate LocalCertificateSelectionCallback(object sender,
275 string targetHost, X509CertificateCollection localCertificates,
276 X509Certificate remoteCertificate, string[] acceptableIssuers)
277 {
278 return GetCertificate();
279 }
280
281 private static bool CertValidator(object sender, X509Certificate certificate,
282 X509Chain chain, SslPolicyErrors sslPolicyErrors)
283 {
284 return true;
285 }
286
Jens Geyer0d128322021-02-25 09:42:52 +0100287 private static TProtocol MakeProtocol(string[] args, TTransport transport)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100288 {
Jens Geyer0d128322021-02-25 09:42:52 +0100289 Protocol selectedProtocol = GetProtocol(args);
290 switch (selectedProtocol)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100291 {
Jens Geyer0d128322021-02-25 09:42:52 +0100292 case Protocol.Binary:
293 return new TBinaryProtocol(transport);
294 case Protocol.Compact:
295 return new TCompactProtocol(transport);
296 case Protocol.Json:
297 return new TJsonProtocol(transport);
298 default:
299 throw new Exception("unhandled protocol");
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100300 }
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100301 }
302
Jens Geyer0d128322021-02-25 09:42:52 +0100303 private static async Task RunClientAsync(TProtocol protocol, bool multiplex, CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100304 {
305 try
306 {
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100307 try
308 {
Jens Geyer0d128322021-02-25 09:42:52 +0100309 if( multiplex)
310 protocol = new TMultiplexedProtocol(protocol, nameof(Calculator));
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100311
Jens Geyer0d128322021-02-25 09:42:52 +0100312 var client = new Calculator.Client(protocol);
313 await ExecuteCalculatorClientOperations(client, cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100314 }
315 catch (Exception ex)
316 {
Jens Geyer0d128322021-02-25 09:42:52 +0100317 Logger.LogError(ex.ToString());
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100318 }
319 finally
320 {
321 protocol.Transport.Close();
322 }
323 }
324 catch (TApplicationException x)
325 {
326 Logger.LogError(x.ToString());
327 }
328 }
329
Jens Geyer0d128322021-02-25 09:42:52 +0100330 private static async Task ExecuteCalculatorClientOperations( Calculator.Client client, CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100331 {
332 await client.OpenTransportAsync(cancellationToken);
333
334 // Async version
335
336 Logger.LogInformation($"{client.ClientId} PingAsync()");
337 await client.pingAsync(cancellationToken);
338
339 Logger.LogInformation($"{client.ClientId} AddAsync(1,1)");
340 var sum = await client.addAsync(1, 1, cancellationToken);
341 Logger.LogInformation($"{client.ClientId} AddAsync(1,1)={sum}");
342
343 var work = new Work
344 {
345 Op = Operation.DIVIDE,
346 Num1 = 1,
347 Num2 = 0
348 };
349
350 try
351 {
352 Logger.LogInformation($"{client.ClientId} CalculateAsync(1)");
353 await client.calculateAsync(1, work, cancellationToken);
354 Logger.LogInformation($"{client.ClientId} Whoa we can divide by 0");
355 }
356 catch (InvalidOperation io)
357 {
358 Logger.LogInformation($"{client.ClientId} Invalid operation: " + io);
359 }
360
361 work.Op = Operation.SUBTRACT;
362 work.Num1 = 15;
363 work.Num2 = 10;
364
365 try
366 {
367 Logger.LogInformation($"{client.ClientId} CalculateAsync(1)");
368 var diff = await client.calculateAsync(1, work, cancellationToken);
369 Logger.LogInformation($"{client.ClientId} 15-10={diff}");
370 }
371 catch (InvalidOperation io)
372 {
373 Logger.LogInformation($"{client.ClientId} Invalid operation: " + io);
374 }
375
376 Logger.LogInformation($"{client.ClientId} GetStructAsync(1)");
377 var log = await client.getStructAsync(1, cancellationToken);
378 Logger.LogInformation($"{client.ClientId} Check log: {log.Value}");
379
380 Logger.LogInformation($"{client.ClientId} ZipAsync() with delay 100mc on server side");
381 await client.zipAsync(cancellationToken);
382 }
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100383
384
385 private enum Transport
386 {
387 Tcp,
388 NamedPipe,
389 Http,
390 TcpBuffered,
391 Framed,
392 TcpTls
393 }
394
395 private enum Protocol
396 {
397 Binary,
398 Compact,
399 Json,
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100400 }
Kyle Smith7b94dd42019-03-23 17:26:56 +0100401
402 private enum Buffering
403 {
404 None,
405 Buffered,
406 Framed
407 }
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100408 }
409}