blob: f1c5236e1af40addf2920dc291bcfad3668b9934 [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{
Jens Geyer4c7b9fd2021-12-04 22:48:37 +010042 public static class LoggingHelper
43 {
44 public static ILoggerFactory LogFactory { get; } = LoggerFactory.Create(builder => {
45 ConfigureLogging(builder);
46 });
47
48 public static void ConfigureLogging(ILoggingBuilder logging)
49 {
50 logging.SetMinimumLevel(LogLevel.Trace);
51 logging.AddConsole();
52 logging.AddDebug();
53 }
54
55 public static ILogger<T> CreateLogger<T>() => LogFactory.CreateLogger<T>();
56 }
57
Jens Geyeraa0c8b32019-01-28 23:27:45 +010058 public class Program
59 {
Jens Geyer4c7b9fd2021-12-04 22:48:37 +010060 private static readonly ILogger Logger = LoggingHelper.CreateLogger<Program>();
Jens Geyereacd1d42019-11-20 19:03:14 +010061 private static readonly TConfiguration Configuration = null; // new TConfiguration() if needed
Jens Geyeraa0c8b32019-01-28 23:27:45 +010062
63 private static void DisplayHelp()
64 {
65 Logger.LogInformation(@"
66Usage:
Kengo Sekibee4f2f2019-12-29 17:04:50 +090067 Client -help
Jens Geyeraa0c8b32019-01-28 23:27:45 +010068 will diplay help information
69
Jens Geyer0d128322021-02-25 09:42:52 +010070 Client -tr:<transport> -bf:<buffering> -pr:<protocol> [-mc:<numClients>] [-multiplex]
Jens Geyeraa0c8b32019-01-28 23:27:45 +010071 will run client with specified arguments (tcp transport and binary protocol by default) and with 1 client
72
73Options:
74 -tr (transport):
Jens Geyer0d128322021-02-25 09:42:52 +010075 tcp - (default) tcp transport (localhost:9090)
76 tcptls - tcp tls transport (localhost:9090)
77 namedpipe - namedpipe transport (pipe "".test"")
78 http - http transport (http://localhost:9090)
Kyle Smith7b94dd42019-03-23 17:26:56 +010079
80 -bf (buffering):
Jens Geyer0d128322021-02-25 09:42:52 +010081 none - (default) no buffering
82 buffered - buffered transport
83 framed - framed transport
Jens Geyeraa0c8b32019-01-28 23:27:45 +010084
85 -pr (protocol):
Jens Geyer0d128322021-02-25 09:42:52 +010086 binary - (default) binary protocol
87 compact - compact protocol
88 json - json protocol
89
90 -multiplex - adds multiplexed protocol
Jens Geyeraa0c8b32019-01-28 23:27:45 +010091
92 -mc (multiple clients):
93 <numClients> - number of multiple clients to connect to server (max 100, default 1)
94
95Sample:
Kengo Sekibee4f2f2019-12-29 17:04:50 +090096 Client -tr:tcp -pr:binary
Jens Geyeraa0c8b32019-01-28 23:27:45 +010097");
98 }
99
100 public static void Main(string[] args)
101 {
Jens Geyer0d128322021-02-25 09:42:52 +0100102 args ??= Array.Empty<string>();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100103
Jens Geyer4c7b9fd2021-12-04 22:48:37 +0100104 if (args.Any(x => x.StartsWith("-help", StringComparison.OrdinalIgnoreCase)))
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100105 {
Jens Geyer4c7b9fd2021-12-04 22:48:37 +0100106 DisplayHelp();
107 return;
108 }
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100109
Jens Geyer4c7b9fd2021-12-04 22:48:37 +0100110 Logger.LogInformation("Starting client...");
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100111
Jens Geyer4c7b9fd2021-12-04 22:48:37 +0100112 using (var source = new CancellationTokenSource())
113 {
114 RunAsync(args, source.Token).GetAwaiter().GetResult();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100115 }
116 }
117
Jens Geyer4c7b9fd2021-12-04 22:48:37 +0100118
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100119 private static async Task RunAsync(string[] args, CancellationToken cancellationToken)
120 {
121 var numClients = GetNumberOfClients(args);
122
Jens Geyer4c7b9fd2021-12-04 22:48:37 +0100123 Logger.LogInformation("Selected # of clients: {numClients}", numClients);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100124
Jens Geyer0d128322021-02-25 09:42:52 +0100125 var transport = GetTransport(args);
Jens Geyer4c7b9fd2021-12-04 22:48:37 +0100126 Logger.LogInformation("Selected client transport: {transport}", transport);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100127
Jens Geyer0d128322021-02-25 09:42:52 +0100128 var protocol = MakeProtocol( args, MakeTransport(args));
Jens Geyer4c7b9fd2021-12-04 22:48:37 +0100129 Logger.LogInformation("Selected client protocol: {GetProtocol(args)}", GetProtocol(args));
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100130
Jens Geyer0d128322021-02-25 09:42:52 +0100131 var mplex = GetMultiplex(args);
Jens Geyer4c7b9fd2021-12-04 22:48:37 +0100132 Logger.LogInformation("Multiplex {mplex}", mplex);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100133
134 var tasks = new Task[numClients];
135 for (int i = 0; i < numClients; i++)
136 {
Jens Geyer0d128322021-02-25 09:42:52 +0100137 var task = RunClientAsync(protocol, mplex, cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100138 tasks[i] = task;
139 }
140
Jens Geyer0d128322021-02-25 09:42:52 +0100141 Task.WaitAll(tasks,cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100142 await Task.CompletedTask;
143 }
144
Jens Geyer0d128322021-02-25 09:42:52 +0100145 private static bool GetMultiplex(string[] args)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100146 {
Jens Geyer0d128322021-02-25 09:42:52 +0100147 var mplex = args.FirstOrDefault(x => x.StartsWith("-multiplex"));
148 return !string.IsNullOrEmpty(mplex);
149 }
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100150
Jens Geyer0d128322021-02-25 09:42:52 +0100151 private static Protocol GetProtocol(string[] args)
152 {
153 var protocol = args.FirstOrDefault(x => x.StartsWith("-pr"))?.Split(':')?[1];
154 if (string.IsNullOrEmpty(protocol))
155 return Protocol.Binary;
156
157 protocol = protocol.Substring(0, 1).ToUpperInvariant() + protocol.Substring(1).ToLowerInvariant();
158 if (Enum.TryParse(protocol, true, out Protocol selectedProtocol))
159 return selectedProtocol;
160 else
161 return Protocol.Binary;
162 }
163
164 private static Buffering GetBuffering(string[] args)
165 {
166 var buffering = args.FirstOrDefault(x => x.StartsWith("-bf"))?.Split(":")?[1];
167 if (string.IsNullOrEmpty(buffering))
168 return Buffering.None;
169
170 buffering = buffering.Substring(0, 1).ToUpperInvariant() + buffering.Substring(1).ToLowerInvariant();
171 if (Enum.TryParse<Buffering>(buffering, out var selectedBuffering))
172 return selectedBuffering;
173 else
174 return Buffering.None;
175 }
176
177 private static Transport GetTransport(string[] args)
178 {
179 var transport = args.FirstOrDefault(x => x.StartsWith("-tr"))?.Split(':')?[1];
180 if (string.IsNullOrEmpty(transport))
181 return Transport.Tcp;
182
183 transport = transport.Substring(0, 1).ToUpperInvariant() + transport.Substring(1).ToLowerInvariant();
184 if (Enum.TryParse(transport, true, out Transport selectedTransport))
185 return selectedTransport;
186 else
187 return Transport.Tcp;
188 }
189
190
191 private static TTransport MakeTransport(string[] args)
192 {
Kyle Smith7b94dd42019-03-23 17:26:56 +0100193 // construct endpoint transport
Jens Geyer0d128322021-02-25 09:42:52 +0100194 TTransport transport = null;
195 Transport selectedTransport = GetTransport(args);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100196 {
197 switch (selectedTransport)
198 {
199 case Transport.Tcp:
Jens Geyereacd1d42019-11-20 19:03:14 +0100200 transport = new TSocketTransport(IPAddress.Loopback, 9090, Configuration);
Kyle Smith7b94dd42019-03-23 17:26:56 +0100201 break;
202
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100203 case Transport.NamedPipe:
Jens Geyereacd1d42019-11-20 19:03:14 +0100204 transport = new TNamedPipeTransport(".test", Configuration);
Kyle Smith7b94dd42019-03-23 17:26:56 +0100205 break;
206
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100207 case Transport.Http:
Jens Geyereacd1d42019-11-20 19:03:14 +0100208 transport = new THttpTransport(new Uri("http://localhost:9090"), Configuration);
Kyle Smith7b94dd42019-03-23 17:26:56 +0100209 break;
210
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100211 case Transport.TcpTls:
Jens Geyereacd1d42019-11-20 19:03:14 +0100212 transport = new TTlsSocketTransport(IPAddress.Loopback, 9090, Configuration,
213 GetCertificate(), CertValidator, LocalCertificateSelectionCallback);
Kyle Smith7b94dd42019-03-23 17:26:56 +0100214 break;
215
216 default:
217 Debug.Assert(false, "unhandled case");
218 break;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100219 }
220 }
221
Kyle Smith7b94dd42019-03-23 17:26:56 +0100222 // optionally add layered transport(s)
Jens Geyer0d128322021-02-25 09:42:52 +0100223 Buffering selectedBuffering = GetBuffering(args);
224 switch (selectedBuffering)
Kyle Smith7b94dd42019-03-23 17:26:56 +0100225 {
Jens Geyer0d128322021-02-25 09:42:52 +0100226 case Buffering.Buffered:
227 transport = new TBufferedTransport(transport);
228 break;
Kyle Smith7b94dd42019-03-23 17:26:56 +0100229
Jens Geyer0d128322021-02-25 09:42:52 +0100230 case Buffering.Framed:
231 transport = new TFramedTransport(transport);
232 break;
Kyle Smith7b94dd42019-03-23 17:26:56 +0100233
Jens Geyer0d128322021-02-25 09:42:52 +0100234 default: // layered transport(s) are optional
235 Debug.Assert(selectedBuffering == Buffering.None, "unhandled case");
236 break;
Kyle Smith7b94dd42019-03-23 17:26:56 +0100237 }
238
239 return transport;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100240 }
241
242 private static int GetNumberOfClients(string[] args)
243 {
244 var numClients = args.FirstOrDefault(x => x.StartsWith("-mc"))?.Split(':')?[1];
245
Jens Geyer4c7b9fd2021-12-04 22:48:37 +0100246 Logger.LogInformation("Selected # of clients: {numClients}", numClients);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100247
Jens Geyer0d128322021-02-25 09:42:52 +0100248 if (int.TryParse(numClients, out int c) && (0 < c) && (c <= 100))
249 return c;
250 else
251 return 1;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100252 }
253
254 private static X509Certificate2 GetCertificate()
255 {
256 // due to files location in net core better to take certs from top folder
257 var certFile = GetCertPath(Directory.GetParent(Directory.GetCurrentDirectory()));
258 return new X509Certificate2(certFile, "ThriftTest");
259 }
260
261 private static string GetCertPath(DirectoryInfo di, int maxCount = 6)
262 {
263 var topDir = di;
264 var certFile =
265 topDir.EnumerateFiles("ThriftTest.pfx", SearchOption.AllDirectories)
266 .FirstOrDefault();
267 if (certFile == null)
268 {
269 if (maxCount == 0)
270 throw new FileNotFoundException("Cannot find file in directories");
271 return GetCertPath(di.Parent, maxCount - 1);
272 }
273
274 return certFile.FullName;
275 }
276
277 private static X509Certificate LocalCertificateSelectionCallback(object sender,
278 string targetHost, X509CertificateCollection localCertificates,
279 X509Certificate remoteCertificate, string[] acceptableIssuers)
280 {
281 return GetCertificate();
282 }
283
284 private static bool CertValidator(object sender, X509Certificate certificate,
285 X509Chain chain, SslPolicyErrors sslPolicyErrors)
286 {
287 return true;
288 }
289
Jens Geyer0d128322021-02-25 09:42:52 +0100290 private static TProtocol MakeProtocol(string[] args, TTransport transport)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100291 {
Jens Geyer0d128322021-02-25 09:42:52 +0100292 Protocol selectedProtocol = GetProtocol(args);
Jens Geyer2b2ea622021-04-09 22:55:11 +0200293 return selectedProtocol switch
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100294 {
Jens Geyer2b2ea622021-04-09 22:55:11 +0200295 Protocol.Binary => new TBinaryProtocol(transport),
296 Protocol.Compact => new TCompactProtocol(transport),
297 Protocol.Json => new TJsonProtocol(transport),
298 _ => throw new Exception("unhandled protocol"),
299 };
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100300 }
301
Jens Geyer0d128322021-02-25 09:42:52 +0100302 private static async Task RunClientAsync(TProtocol protocol, bool multiplex, CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100303 {
304 try
305 {
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100306 try
307 {
Jens Geyer0d128322021-02-25 09:42:52 +0100308 if( multiplex)
309 protocol = new TMultiplexedProtocol(protocol, nameof(Calculator));
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100310
Jens Geyer0d128322021-02-25 09:42:52 +0100311 var client = new Calculator.Client(protocol);
312 await ExecuteCalculatorClientOperations(client, cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100313 }
314 catch (Exception ex)
315 {
Jens Geyer4c7b9fd2021-12-04 22:48:37 +0100316 Logger.LogError("{ex}",ex);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100317 }
318 finally
319 {
320 protocol.Transport.Close();
321 }
322 }
323 catch (TApplicationException x)
324 {
Jens Geyer4c7b9fd2021-12-04 22:48:37 +0100325 Logger.LogError("{x}",x);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100326 }
327 }
328
Jens Geyer0d128322021-02-25 09:42:52 +0100329 private static async Task ExecuteCalculatorClientOperations( Calculator.Client client, CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100330 {
331 await client.OpenTransportAsync(cancellationToken);
332
333 // Async version
334
Jens Geyer4c7b9fd2021-12-04 22:48:37 +0100335 Logger.LogInformation("{client.ClientId} Ping()", client.ClientId);
Jens Geyer2b2ea622021-04-09 22:55:11 +0200336 await client.ping(cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100337
Jens Geyer4c7b9fd2021-12-04 22:48:37 +0100338 Logger.LogInformation("{client.ClientId} Add(1,1)", client.ClientId);
Jens Geyer2b2ea622021-04-09 22:55:11 +0200339 var sum = await client.add(1, 1, cancellationToken);
Jens Geyer4c7b9fd2021-12-04 22:48:37 +0100340 Logger.LogInformation("{client.ClientId} Add(1,1)={sum}", client.ClientId, sum);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100341
342 var work = new Work
343 {
344 Op = Operation.DIVIDE,
345 Num1 = 1,
346 Num2 = 0
347 };
348
349 try
350 {
Jens Geyer4c7b9fd2021-12-04 22:48:37 +0100351 Logger.LogInformation("{client.ClientId} Calculate(1)", client.ClientId);
Jens Geyer2b2ea622021-04-09 22:55:11 +0200352 await client.calculate(1, work, cancellationToken);
Jens Geyer4c7b9fd2021-12-04 22:48:37 +0100353 Logger.LogInformation("{client.ClientId} Whoa we can divide by 0", client.ClientId);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100354 }
355 catch (InvalidOperation io)
356 {
Jens Geyer4c7b9fd2021-12-04 22:48:37 +0100357 Logger.LogInformation("{client.ClientId} Invalid operation: {io}", client.ClientId, io);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100358 }
359
360 work.Op = Operation.SUBTRACT;
361 work.Num1 = 15;
362 work.Num2 = 10;
363
364 try
365 {
Jens Geyer4c7b9fd2021-12-04 22:48:37 +0100366 Logger.LogInformation("{client.ClientId} Calculate(1)", client.ClientId);
Jens Geyer2b2ea622021-04-09 22:55:11 +0200367 var diff = await client.calculate(1, work, cancellationToken);
Jens Geyer4c7b9fd2021-12-04 22:48:37 +0100368 Logger.LogInformation("{client.ClientId} 15-10={diff}", client.ClientId, diff);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100369 }
370 catch (InvalidOperation io)
371 {
Jens Geyer4c7b9fd2021-12-04 22:48:37 +0100372 Logger.LogInformation("{client.ClientId} Invalid operation: {io}", client.ClientId, io);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100373 }
374
Jens Geyer4c7b9fd2021-12-04 22:48:37 +0100375 Logger.LogInformation("{client.ClientId} GetStruct(1)", client.ClientId);
Jens Geyer2b2ea622021-04-09 22:55:11 +0200376 var log = await client.getStruct(1, cancellationToken);
Jens Geyer4c7b9fd2021-12-04 22:48:37 +0100377 Logger.LogInformation("{client.ClientId} Check log: {log.Value}", client.ClientId, log.Value);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100378
Jens Geyer4c7b9fd2021-12-04 22:48:37 +0100379 Logger.LogInformation("{client.ClientId} Zip() with delay 100mc on server side", client.ClientId);
Jens Geyer2b2ea622021-04-09 22:55:11 +0200380 await client.zip(cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100381 }
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100382
383
384 private enum Transport
385 {
386 Tcp,
387 NamedPipe,
388 Http,
389 TcpBuffered,
390 Framed,
391 TcpTls
392 }
393
394 private enum Protocol
395 {
396 Binary,
397 Compact,
398 Json,
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100399 }
Kyle Smith7b94dd42019-03-23 17:26:56 +0100400
401 private enum Buffering
402 {
403 None,
404 Buffered,
405 Framed
406 }
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100407 }
408}