blob: 93175fd46af4fa30138cabcde903aebab7c694cf [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
Jens Geyere26b4a82024-11-12 23:53:04 +010018using Microsoft.Extensions.Logging;
Jens Geyeraa0c8b32019-01-28 23:27:45 +010019using System;
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;
Jens Geyeraa0c8b32019-01-28 23:27:45 +010028using Thrift;
29using Thrift.Protocol;
30using Thrift.Transport;
31using Thrift.Transport.Client;
32using tutorial;
Jens Geyeraa0c8b32019-01-28 23:27:45 +010033
Jens Geyer0d128322021-02-25 09:42:52 +010034#pragma warning disable IDE0057 // substr
35
Jens Geyeraa0c8b32019-01-28 23:27:45 +010036namespace Client
37{
Jens Geyer4c7b9fd2021-12-04 22:48:37 +010038 public static class LoggingHelper
39 {
40 public static ILoggerFactory LogFactory { get; } = LoggerFactory.Create(builder => {
41 ConfigureLogging(builder);
42 });
43
44 public static void ConfigureLogging(ILoggingBuilder logging)
45 {
46 logging.SetMinimumLevel(LogLevel.Trace);
47 logging.AddConsole();
48 logging.AddDebug();
49 }
50
51 public static ILogger<T> CreateLogger<T>() => LogFactory.CreateLogger<T>();
52 }
53
Jens Geyeraa0c8b32019-01-28 23:27:45 +010054 public class Program
55 {
Jens Geyer4c7b9fd2021-12-04 22:48:37 +010056 private static readonly ILogger Logger = LoggingHelper.CreateLogger<Program>();
Jens Geyere26b4a82024-11-12 23:53:04 +010057 private static readonly TConfiguration Configuration = new();
Jens Geyeraa0c8b32019-01-28 23:27:45 +010058
59 private static void DisplayHelp()
60 {
61 Logger.LogInformation(@"
62Usage:
Kengo Sekibee4f2f2019-12-29 17:04:50 +090063 Client -help
Jens Geyeraa0c8b32019-01-28 23:27:45 +010064 will diplay help information
65
Jens Geyer0d128322021-02-25 09:42:52 +010066 Client -tr:<transport> -bf:<buffering> -pr:<protocol> [-mc:<numClients>] [-multiplex]
Jens Geyeraa0c8b32019-01-28 23:27:45 +010067 will run client with specified arguments (tcp transport and binary protocol by default) and with 1 client
68
69Options:
70 -tr (transport):
Jens Geyer0d128322021-02-25 09:42:52 +010071 tcp - (default) tcp transport (localhost:9090)
72 tcptls - tcp tls transport (localhost:9090)
73 namedpipe - namedpipe transport (pipe "".test"")
74 http - http transport (http://localhost:9090)
Kyle Smith7b94dd42019-03-23 17:26:56 +010075
76 -bf (buffering):
Jens Geyer0d128322021-02-25 09:42:52 +010077 none - (default) no buffering
78 buffered - buffered transport
79 framed - framed transport
Jens Geyeraa0c8b32019-01-28 23:27:45 +010080
81 -pr (protocol):
Jens Geyer0d128322021-02-25 09:42:52 +010082 binary - (default) binary protocol
83 compact - compact protocol
84 json - json protocol
85
86 -multiplex - adds multiplexed protocol
Jens Geyeraa0c8b32019-01-28 23:27:45 +010087
88 -mc (multiple clients):
89 <numClients> - number of multiple clients to connect to server (max 100, default 1)
90
91Sample:
Kengo Sekibee4f2f2019-12-29 17:04:50 +090092 Client -tr:tcp -pr:binary
Jens Geyeraa0c8b32019-01-28 23:27:45 +010093");
94 }
95
Jens Geyere26b4a82024-11-12 23:53:04 +010096 public static async Task Main(string[] args)
Jens Geyeraa0c8b32019-01-28 23:27:45 +010097 {
Jens Geyere26b4a82024-11-12 23:53:04 +010098 args ??= [];
Jens Geyeraa0c8b32019-01-28 23:27:45 +010099
Jens Geyere26b4a82024-11-12 23:53:04 +0100100 // -help is rather unusual but we leave it for compatibility
101 if (args.Any(x => x.Equals("-help") || x.Equals("--help") || x.Equals("-h") || x.Equals("-?")))
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100102 {
Jens Geyer4c7b9fd2021-12-04 22:48:37 +0100103 DisplayHelp();
104 return;
105 }
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100106
Jens Geyer4c7b9fd2021-12-04 22:48:37 +0100107 Logger.LogInformation("Starting client...");
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100108
Jens Geyere26b4a82024-11-12 23:53:04 +0100109 using var source = new CancellationTokenSource();
110 await RunAsync(args, source.Token);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100111 }
112
Jens Geyer4c7b9fd2021-12-04 22:48:37 +0100113
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100114 private static async Task RunAsync(string[] args, CancellationToken cancellationToken)
115 {
116 var numClients = GetNumberOfClients(args);
117
Jens Geyer4c7b9fd2021-12-04 22:48:37 +0100118 Logger.LogInformation("Selected # of clients: {numClients}", numClients);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100119
Jens Geyer0d128322021-02-25 09:42:52 +0100120 var transport = GetTransport(args);
Jens Geyer4c7b9fd2021-12-04 22:48:37 +0100121 Logger.LogInformation("Selected client transport: {transport}", transport);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100122
Jens Geyer0d128322021-02-25 09:42:52 +0100123 var protocol = MakeProtocol( args, MakeTransport(args));
Jens Geyer4c7b9fd2021-12-04 22:48:37 +0100124 Logger.LogInformation("Selected client protocol: {GetProtocol(args)}", GetProtocol(args));
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100125
Jens Geyer0d128322021-02-25 09:42:52 +0100126 var mplex = GetMultiplex(args);
Jens Geyer4c7b9fd2021-12-04 22:48:37 +0100127 Logger.LogInformation("Multiplex {mplex}", mplex);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100128
129 var tasks = new Task[numClients];
130 for (int i = 0; i < numClients; i++)
131 {
Jens Geyer0d128322021-02-25 09:42:52 +0100132 var task = RunClientAsync(protocol, mplex, cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100133 tasks[i] = task;
134 }
135
Jens Geyer0d128322021-02-25 09:42:52 +0100136 Task.WaitAll(tasks,cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100137 await Task.CompletedTask;
138 }
139
Jens Geyer0d128322021-02-25 09:42:52 +0100140 private static bool GetMultiplex(string[] args)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100141 {
Jens Geyer0d128322021-02-25 09:42:52 +0100142 var mplex = args.FirstOrDefault(x => x.StartsWith("-multiplex"));
143 return !string.IsNullOrEmpty(mplex);
144 }
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100145
Jens Geyer0d128322021-02-25 09:42:52 +0100146 private static Protocol GetProtocol(string[] args)
147 {
Jens Geyere26b4a82024-11-12 23:53:04 +0100148 var protocol = args.FirstOrDefault(x => x.StartsWith("-pr"))?.Split(':').Skip(1).Take(1).FirstOrDefault();
Jens Geyer0d128322021-02-25 09:42:52 +0100149 if (string.IsNullOrEmpty(protocol))
150 return Protocol.Binary;
151
152 protocol = protocol.Substring(0, 1).ToUpperInvariant() + protocol.Substring(1).ToLowerInvariant();
153 if (Enum.TryParse(protocol, true, out Protocol selectedProtocol))
154 return selectedProtocol;
155 else
156 return Protocol.Binary;
157 }
158
159 private static Buffering GetBuffering(string[] args)
160 {
Jens Geyere26b4a82024-11-12 23:53:04 +0100161 var buffering = args.FirstOrDefault(x => x.StartsWith("-bf"))?.Split(':').Skip(1).Take(1).FirstOrDefault();
Jens Geyer0d128322021-02-25 09:42:52 +0100162 if (string.IsNullOrEmpty(buffering))
163 return Buffering.None;
164
165 buffering = buffering.Substring(0, 1).ToUpperInvariant() + buffering.Substring(1).ToLowerInvariant();
166 if (Enum.TryParse<Buffering>(buffering, out var selectedBuffering))
167 return selectedBuffering;
168 else
169 return Buffering.None;
170 }
171
172 private static Transport GetTransport(string[] args)
173 {
Jens Geyere26b4a82024-11-12 23:53:04 +0100174 var transport = args.FirstOrDefault(x => x.StartsWith("-tr"))?.Split(':').Skip(1).Take(1).FirstOrDefault();
Jens Geyer0d128322021-02-25 09:42:52 +0100175 if (string.IsNullOrEmpty(transport))
176 return Transport.Tcp;
177
178 transport = transport.Substring(0, 1).ToUpperInvariant() + transport.Substring(1).ToLowerInvariant();
179 if (Enum.TryParse(transport, true, out Transport selectedTransport))
180 return selectedTransport;
181 else
182 return Transport.Tcp;
183 }
184
185
186 private static TTransport MakeTransport(string[] args)
187 {
Kyle Smith7b94dd42019-03-23 17:26:56 +0100188 // construct endpoint transport
Jens Geyere26b4a82024-11-12 23:53:04 +0100189 TTransport? transport = null;
Jens Geyer0d128322021-02-25 09:42:52 +0100190 Transport selectedTransport = GetTransport(args);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100191 {
192 switch (selectedTransport)
193 {
194 case Transport.Tcp:
Jens Geyereacd1d42019-11-20 19:03:14 +0100195 transport = new TSocketTransport(IPAddress.Loopback, 9090, Configuration);
Kyle Smith7b94dd42019-03-23 17:26:56 +0100196 break;
197
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100198 case Transport.NamedPipe:
Jens Geyereacd1d42019-11-20 19:03:14 +0100199 transport = new TNamedPipeTransport(".test", Configuration);
Kyle Smith7b94dd42019-03-23 17:26:56 +0100200 break;
201
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100202 case Transport.Http:
Jens Geyereacd1d42019-11-20 19:03:14 +0100203 transport = new THttpTransport(new Uri("http://localhost:9090"), Configuration);
Kyle Smith7b94dd42019-03-23 17:26:56 +0100204 break;
205
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100206 case Transport.TcpTls:
Jens Geyereacd1d42019-11-20 19:03:14 +0100207 transport = new TTlsSocketTransport(IPAddress.Loopback, 9090, Configuration,
208 GetCertificate(), CertValidator, LocalCertificateSelectionCallback);
Kyle Smith7b94dd42019-03-23 17:26:56 +0100209 break;
210
211 default:
212 Debug.Assert(false, "unhandled case");
213 break;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100214 }
215 }
216
Kyle Smith7b94dd42019-03-23 17:26:56 +0100217 // optionally add layered transport(s)
Jens Geyer0d128322021-02-25 09:42:52 +0100218 Buffering selectedBuffering = GetBuffering(args);
219 switch (selectedBuffering)
Kyle Smith7b94dd42019-03-23 17:26:56 +0100220 {
Jens Geyer0d128322021-02-25 09:42:52 +0100221 case Buffering.Buffered:
222 transport = new TBufferedTransport(transport);
223 break;
Kyle Smith7b94dd42019-03-23 17:26:56 +0100224
Jens Geyer0d128322021-02-25 09:42:52 +0100225 case Buffering.Framed:
226 transport = new TFramedTransport(transport);
227 break;
Kyle Smith7b94dd42019-03-23 17:26:56 +0100228
Jens Geyer0d128322021-02-25 09:42:52 +0100229 default: // layered transport(s) are optional
230 Debug.Assert(selectedBuffering == Buffering.None, "unhandled case");
231 break;
Kyle Smith7b94dd42019-03-23 17:26:56 +0100232 }
233
234 return transport;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100235 }
236
237 private static int GetNumberOfClients(string[] args)
238 {
Jens Geyere26b4a82024-11-12 23:53:04 +0100239 var numClients = args.FirstOrDefault(x => x.StartsWith("-mc"))?.Split(':').Skip(1).Take(1).FirstOrDefault();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100240
Jens Geyer4c7b9fd2021-12-04 22:48:37 +0100241 Logger.LogInformation("Selected # of clients: {numClients}", numClients);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100242
Jens Geyer0d128322021-02-25 09:42:52 +0100243 if (int.TryParse(numClients, out int c) && (0 < c) && (c <= 100))
244 return c;
245 else
246 return 1;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100247 }
248
249 private static X509Certificate2 GetCertificate()
250 {
251 // due to files location in net core better to take certs from top folder
Jens Geyere26b4a82024-11-12 23:53:04 +0100252 var dir = Directory.GetParent(Directory.GetCurrentDirectory());
253 if (dir != null)
254 {
255 var certFile = GetCertPath(dir);
256 //return new X509Certificate2(certFile, "ThriftTest");
257 return X509CertificateLoader.LoadPkcs12FromFile(certFile, "ThriftTest");
258 }
259 else
260 {
261 Logger.LogError("Root path of {path} not found", Directory.GetCurrentDirectory());
262 throw new Exception($"Root path of {Directory.GetCurrentDirectory()} not found");
263 }
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100264 }
265
Jens Geyere26b4a82024-11-12 23:53:04 +0100266 private static string GetCertPath(DirectoryInfo? di, int maxCount = 6)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100267 {
268 var topDir = di;
Jens Geyere26b4a82024-11-12 23:53:04 +0100269 var certFile = topDir?.EnumerateFiles("ThriftTest.pfx", SearchOption.AllDirectories).FirstOrDefault();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100270 if (certFile == null)
271 {
272 if (maxCount == 0)
273 throw new FileNotFoundException("Cannot find file in directories");
Jens Geyere26b4a82024-11-12 23:53:04 +0100274 return GetCertPath(di?.Parent, --maxCount);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100275 }
276
277 return certFile.FullName;
278 }
279
Jens Geyere26b4a82024-11-12 23:53:04 +0100280 private static X509Certificate2 LocalCertificateSelectionCallback(object sender,
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100281 string targetHost, X509CertificateCollection localCertificates,
Jens Geyere26b4a82024-11-12 23:53:04 +0100282 X509Certificate? remoteCertificate, string[] acceptableIssuers)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100283 {
284 return GetCertificate();
285 }
286
Jens Geyere26b4a82024-11-12 23:53:04 +0100287 private static bool CertValidator(object sender, X509Certificate? certificate, X509Chain? chain, SslPolicyErrors sslPolicyErrors)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100288 {
289 return true;
290 }
291
Jens Geyer0d128322021-02-25 09:42:52 +0100292 private static TProtocol MakeProtocol(string[] args, TTransport transport)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100293 {
Jens Geyer0d128322021-02-25 09:42:52 +0100294 Protocol selectedProtocol = GetProtocol(args);
Jens Geyer2b2ea622021-04-09 22:55:11 +0200295 return selectedProtocol switch
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100296 {
Jens Geyer2b2ea622021-04-09 22:55:11 +0200297 Protocol.Binary => new TBinaryProtocol(transport),
298 Protocol.Compact => new TCompactProtocol(transport),
299 Protocol.Json => new TJsonProtocol(transport),
300 _ => throw new Exception("unhandled protocol"),
301 };
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100302 }
303
Jens Geyer0d128322021-02-25 09:42:52 +0100304 private static async Task RunClientAsync(TProtocol protocol, bool multiplex, CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100305 {
306 try
307 {
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100308 try
309 {
Jens Geyer0d128322021-02-25 09:42:52 +0100310 if( multiplex)
311 protocol = new TMultiplexedProtocol(protocol, nameof(Calculator));
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100312
Jens Geyer0d128322021-02-25 09:42:52 +0100313 var client = new Calculator.Client(protocol);
314 await ExecuteCalculatorClientOperations(client, cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100315 }
316 catch (Exception ex)
317 {
Jens Geyer4c7b9fd2021-12-04 22:48:37 +0100318 Logger.LogError("{ex}",ex);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100319 }
320 finally
321 {
322 protocol.Transport.Close();
323 }
324 }
325 catch (TApplicationException x)
326 {
Jens Geyer4c7b9fd2021-12-04 22:48:37 +0100327 Logger.LogError("{x}",x);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100328 }
329 }
330
Jens Geyer0d128322021-02-25 09:42:52 +0100331 private static async Task ExecuteCalculatorClientOperations( Calculator.Client client, CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100332 {
333 await client.OpenTransportAsync(cancellationToken);
334
335 // Async version
336
Jens Geyer4c7b9fd2021-12-04 22:48:37 +0100337 Logger.LogInformation("{client.ClientId} Ping()", client.ClientId);
Jens Geyer2b2ea622021-04-09 22:55:11 +0200338 await client.ping(cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100339
Jens Geyer4c7b9fd2021-12-04 22:48:37 +0100340 Logger.LogInformation("{client.ClientId} Add(1,1)", client.ClientId);
Jens Geyer2b2ea622021-04-09 22:55:11 +0200341 var sum = await client.add(1, 1, cancellationToken);
Jens Geyer4c7b9fd2021-12-04 22:48:37 +0100342 Logger.LogInformation("{client.ClientId} Add(1,1)={sum}", client.ClientId, sum);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100343
344 var work = new Work
345 {
346 Op = Operation.DIVIDE,
347 Num1 = 1,
348 Num2 = 0
349 };
350
351 try
352 {
Jens Geyer4c7b9fd2021-12-04 22:48:37 +0100353 Logger.LogInformation("{client.ClientId} Calculate(1)", client.ClientId);
Jens Geyer2b2ea622021-04-09 22:55:11 +0200354 await client.calculate(1, work, cancellationToken);
Jens Geyer4c7b9fd2021-12-04 22:48:37 +0100355 Logger.LogInformation("{client.ClientId} Whoa we can divide by 0", client.ClientId);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100356 }
357 catch (InvalidOperation io)
358 {
Jens Geyer4c7b9fd2021-12-04 22:48:37 +0100359 Logger.LogInformation("{client.ClientId} Invalid operation: {io}", client.ClientId, io);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100360 }
361
362 work.Op = Operation.SUBTRACT;
363 work.Num1 = 15;
364 work.Num2 = 10;
365
366 try
367 {
Jens Geyer4c7b9fd2021-12-04 22:48:37 +0100368 Logger.LogInformation("{client.ClientId} Calculate(1)", client.ClientId);
Jens Geyer2b2ea622021-04-09 22:55:11 +0200369 var diff = await client.calculate(1, work, cancellationToken);
Jens Geyer4c7b9fd2021-12-04 22:48:37 +0100370 Logger.LogInformation("{client.ClientId} 15-10={diff}", client.ClientId, diff);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100371 }
372 catch (InvalidOperation io)
373 {
Jens Geyer4c7b9fd2021-12-04 22:48:37 +0100374 Logger.LogInformation("{client.ClientId} Invalid operation: {io}", client.ClientId, io);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100375 }
376
Jens Geyer4c7b9fd2021-12-04 22:48:37 +0100377 Logger.LogInformation("{client.ClientId} GetStruct(1)", client.ClientId);
Jens Geyer2b2ea622021-04-09 22:55:11 +0200378 var log = await client.getStruct(1, cancellationToken);
Jens Geyer4c7b9fd2021-12-04 22:48:37 +0100379 Logger.LogInformation("{client.ClientId} Check log: {log.Value}", client.ClientId, log.Value);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100380
Jens Geyer4c7b9fd2021-12-04 22:48:37 +0100381 Logger.LogInformation("{client.ClientId} Zip() with delay 100mc on server side", client.ClientId);
Jens Geyer2b2ea622021-04-09 22:55:11 +0200382 await client.zip(cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100383 }
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100384
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,
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100401 }
Kyle Smith7b94dd42019-03-23 17:26:56 +0100402
403 private enum Buffering
404 {
405 None,
406 Buffered,
407 Framed
408 }
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100409 }
410}