blob: 9c47450e7c9c4865496cc8aa48d0ba6db3a014bf [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 Geyer2b2ea622021-04-09 22:55:11 +020044 private static readonly ServiceCollection ServiceCollection = new();
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);
Jens Geyer2b2ea622021-04-09 22:55:11 +0200290 return selectedProtocol switch
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100291 {
Jens Geyer2b2ea622021-04-09 22:55:11 +0200292 Protocol.Binary => new TBinaryProtocol(transport),
293 Protocol.Compact => new TCompactProtocol(transport),
294 Protocol.Json => new TJsonProtocol(transport),
295 _ => throw new Exception("unhandled protocol"),
296 };
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100297 }
298
Jens Geyer0d128322021-02-25 09:42:52 +0100299 private static async Task RunClientAsync(TProtocol protocol, bool multiplex, CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100300 {
301 try
302 {
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100303 try
304 {
Jens Geyer0d128322021-02-25 09:42:52 +0100305 if( multiplex)
306 protocol = new TMultiplexedProtocol(protocol, nameof(Calculator));
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100307
Jens Geyer0d128322021-02-25 09:42:52 +0100308 var client = new Calculator.Client(protocol);
309 await ExecuteCalculatorClientOperations(client, cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100310 }
311 catch (Exception ex)
312 {
Jens Geyer0d128322021-02-25 09:42:52 +0100313 Logger.LogError(ex.ToString());
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100314 }
315 finally
316 {
317 protocol.Transport.Close();
318 }
319 }
320 catch (TApplicationException x)
321 {
322 Logger.LogError(x.ToString());
323 }
324 }
325
Jens Geyer0d128322021-02-25 09:42:52 +0100326 private static async Task ExecuteCalculatorClientOperations( Calculator.Client client, CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100327 {
328 await client.OpenTransportAsync(cancellationToken);
329
330 // Async version
331
Jens Geyer2b2ea622021-04-09 22:55:11 +0200332 Logger.LogInformation($"{client.ClientId} Ping()");
333 await client.ping(cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100334
Jens Geyer2b2ea622021-04-09 22:55:11 +0200335 Logger.LogInformation($"{client.ClientId} Add(1,1)");
336 var sum = await client.add(1, 1, cancellationToken);
337 Logger.LogInformation($"{client.ClientId} Add(1,1)={sum}");
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100338
339 var work = new Work
340 {
341 Op = Operation.DIVIDE,
342 Num1 = 1,
343 Num2 = 0
344 };
345
346 try
347 {
Jens Geyer2b2ea622021-04-09 22:55:11 +0200348 Logger.LogInformation($"{client.ClientId} Calculate(1)");
349 await client.calculate(1, work, cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100350 Logger.LogInformation($"{client.ClientId} Whoa we can divide by 0");
351 }
352 catch (InvalidOperation io)
353 {
354 Logger.LogInformation($"{client.ClientId} Invalid operation: " + io);
355 }
356
357 work.Op = Operation.SUBTRACT;
358 work.Num1 = 15;
359 work.Num2 = 10;
360
361 try
362 {
Jens Geyer2b2ea622021-04-09 22:55:11 +0200363 Logger.LogInformation($"{client.ClientId} Calculate(1)");
364 var diff = await client.calculate(1, work, cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100365 Logger.LogInformation($"{client.ClientId} 15-10={diff}");
366 }
367 catch (InvalidOperation io)
368 {
369 Logger.LogInformation($"{client.ClientId} Invalid operation: " + io);
370 }
371
Jens Geyer2b2ea622021-04-09 22:55:11 +0200372 Logger.LogInformation($"{client.ClientId} GetStruct(1)");
373 var log = await client.getStruct(1, cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100374 Logger.LogInformation($"{client.ClientId} Check log: {log.Value}");
375
Jens Geyer2b2ea622021-04-09 22:55:11 +0200376 Logger.LogInformation($"{client.ClientId} Zip() with delay 100mc on server side");
377 await client.zip(cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100378 }
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100379
380
381 private enum Transport
382 {
383 Tcp,
384 NamedPipe,
385 Http,
386 TcpBuffered,
387 Framed,
388 TcpTls
389 }
390
391 private enum Protocol
392 {
393 Binary,
394 Compact,
395 Json,
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100396 }
Kyle Smith7b94dd42019-03-23 17:26:56 +0100397
398 private enum Buffering
399 {
400 None,
401 Buffered,
402 Framed
403 }
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100404 }
405}