blob: 4b68cee4aedd256c87baa37475e959554cff76be [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;
Jens Geyeraa0c8b32019-01-28 23:27:45 +010035
36namespace Client
37{
38 public class Program
39 {
Jens Geyerb11f63c2019-03-14 21:12:38 +010040 private static ServiceCollection ServiceCollection = new ServiceCollection();
41 private static ILogger Logger;
Jens Geyeraa0c8b32019-01-28 23:27:45 +010042
43 private static void DisplayHelp()
44 {
45 Logger.LogInformation(@"
46Usage:
47 Client.exe -help
48 will diplay help information
49
50 Client.exe -tr:<transport> -pr:<protocol> -mc:<numClients>
51 will run client with specified arguments (tcp transport and binary protocol by default) and with 1 client
52
53Options:
54 -tr (transport):
55 tcp - (default) tcp transport will be used (host - ""localhost"", port - 9090)
56 tcpbuffered - buffered transport over tcp will be used (host - ""localhost"", port - 9090)
57 namedpipe - namedpipe transport will be used (pipe address - "".test"")
58 http - http transport will be used (address - ""http://localhost:9090"")
59 tcptls - tcp tls transport will be used (host - ""localhost"", port - 9090)
60 framed - tcp framed transport will be used (host - ""localhost"", port - 9090)
61
62 -pr (protocol):
63 binary - (default) binary protocol will be used
64 compact - compact protocol will be used
65 json - json protocol will be used
66 multiplexed - multiplexed protocol will be used
67
68 -mc (multiple clients):
69 <numClients> - number of multiple clients to connect to server (max 100, default 1)
70
71Sample:
72 Client.exe -tr:tcp -p:binary
73");
74 }
75
76 public static void Main(string[] args)
77 {
78 args = args ?? new string[0];
79
Jens Geyerb11f63c2019-03-14 21:12:38 +010080 ServiceCollection.AddLogging(logging => ConfigureLogging(logging));
81 Logger = ServiceCollection.BuildServiceProvider().GetService<ILoggerFactory>().CreateLogger(nameof(Client));
82
Jens Geyeraa0c8b32019-01-28 23:27:45 +010083 if (args.Any(x => x.StartsWith("-help", StringComparison.OrdinalIgnoreCase)))
84 {
85 DisplayHelp();
86 return;
87 }
88
89 Logger.LogInformation("Starting client...");
90
91 using (var source = new CancellationTokenSource())
92 {
93 RunAsync(args, source.Token).GetAwaiter().GetResult();
94 }
95 }
96
Jens Geyerb11f63c2019-03-14 21:12:38 +010097 private static void ConfigureLogging(ILoggingBuilder logging)
98 {
99 logging.SetMinimumLevel(LogLevel.Trace);
100 logging.AddConsole();
101 logging.AddDebug();
102 }
103
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100104 private static async Task RunAsync(string[] args, CancellationToken cancellationToken)
105 {
106 var numClients = GetNumberOfClients(args);
107
108 Logger.LogInformation($"Selected # of clients: {numClients}");
109
110 var transports = new TTransport[numClients];
111 for (int i = 0; i < numClients; i++)
112 {
113 var t = GetTransport(args);
114 transports[i] = t;
115 }
116
117 Logger.LogInformation($"Selected client transport: {transports[0]}");
118
119 var protocols = new Tuple<Protocol, TProtocol>[numClients];
120 for (int i = 0; i < numClients; i++)
121 {
122 var p = GetProtocol(args, transports[i]);
123 protocols[i] = p;
124 }
125
126 Logger.LogInformation($"Selected client protocol: {protocols[0].Item1}");
127
128 var tasks = new Task[numClients];
129 for (int i = 0; i < numClients; i++)
130 {
131 var task = RunClientAsync(protocols[i], cancellationToken);
132 tasks[i] = task;
133 }
134
135 Task.WaitAll(tasks);
136
137 await Task.CompletedTask;
138 }
139
140 private static TTransport GetTransport(string[] args)
141 {
142 var transport = args.FirstOrDefault(x => x.StartsWith("-tr"))?.Split(':')?[1];
143
144 Transport selectedTransport;
145 if (Enum.TryParse(transport, true, out selectedTransport))
146 {
147 switch (selectedTransport)
148 {
149 case Transport.Tcp:
150 return new TSocketTransport(IPAddress.Loopback, 9090);
151 case Transport.NamedPipe:
152 return new TNamedPipeTransport(".test");
153 case Transport.Http:
154 return new THttpTransport(new Uri("http://localhost:9090"), null);
155 case Transport.TcpBuffered:
156 return new TBufferedTransport(new TSocketTransport(IPAddress.Loopback, 9090));
157 case Transport.TcpTls:
158 return new TTlsSocketTransport(IPAddress.Loopback, 9090, GetCertificate(), CertValidator, LocalCertificateSelectionCallback);
159 case Transport.Framed:
160 return new TFramedTransport(new TSocketTransport(IPAddress.Loopback, 9090));
161 }
162 }
163
164 return new TSocketTransport(IPAddress.Loopback, 9090);
165 }
166
167 private static int GetNumberOfClients(string[] args)
168 {
169 var numClients = args.FirstOrDefault(x => x.StartsWith("-mc"))?.Split(':')?[1];
170
171 Logger.LogInformation($"Selected # of clients: {numClients}");
172
173 int c;
174 if( int.TryParse(numClients, out c) && (0 < c) && (c <= 100))
175 return c;
176 else
177 return 1;
178 }
179
180 private static X509Certificate2 GetCertificate()
181 {
182 // due to files location in net core better to take certs from top folder
183 var certFile = GetCertPath(Directory.GetParent(Directory.GetCurrentDirectory()));
184 return new X509Certificate2(certFile, "ThriftTest");
185 }
186
187 private static string GetCertPath(DirectoryInfo di, int maxCount = 6)
188 {
189 var topDir = di;
190 var certFile =
191 topDir.EnumerateFiles("ThriftTest.pfx", SearchOption.AllDirectories)
192 .FirstOrDefault();
193 if (certFile == null)
194 {
195 if (maxCount == 0)
196 throw new FileNotFoundException("Cannot find file in directories");
197 return GetCertPath(di.Parent, maxCount - 1);
198 }
199
200 return certFile.FullName;
201 }
202
203 private static X509Certificate LocalCertificateSelectionCallback(object sender,
204 string targetHost, X509CertificateCollection localCertificates,
205 X509Certificate remoteCertificate, string[] acceptableIssuers)
206 {
207 return GetCertificate();
208 }
209
210 private static bool CertValidator(object sender, X509Certificate certificate,
211 X509Chain chain, SslPolicyErrors sslPolicyErrors)
212 {
213 return true;
214 }
215
216 private static Tuple<Protocol, TProtocol> GetProtocol(string[] args, TTransport transport)
217 {
218 var protocol = args.FirstOrDefault(x => x.StartsWith("-pr"))?.Split(':')?[1];
219
220 Protocol selectedProtocol;
221 if (Enum.TryParse(protocol, true, out selectedProtocol))
222 {
223 switch (selectedProtocol)
224 {
225 case Protocol.Binary:
226 return new Tuple<Protocol, TProtocol>(selectedProtocol, new TBinaryProtocol(transport));
227 case Protocol.Compact:
228 return new Tuple<Protocol, TProtocol>(selectedProtocol, new TCompactProtocol(transport));
229 case Protocol.Json:
230 return new Tuple<Protocol, TProtocol>(selectedProtocol, new TJsonProtocol(transport));
231 case Protocol.Multiplexed:
232 // it returns BinaryProtocol to avoid making wrapped protocol as public in TProtocolDecorator (in RunClientAsync it will be wrapped into Multiplexed protocol)
233 return new Tuple<Protocol, TProtocol>(selectedProtocol, new TBinaryProtocol(transport));
234 }
235 }
236
237 return new Tuple<Protocol, TProtocol>(selectedProtocol, new TBinaryProtocol(transport));
238 }
239
240 private static async Task RunClientAsync(Tuple<Protocol, TProtocol> protocolTuple, CancellationToken cancellationToken)
241 {
242 try
243 {
244 var protocol = protocolTuple.Item2;
245 var protocolType = protocolTuple.Item1;
246
247 TBaseClient client = null;
248
249 try
250 {
251 if (protocolType != Protocol.Multiplexed)
252 {
253
254 client = new Calculator.Client(protocol);
255 await ExecuteCalculatorClientOperations(cancellationToken, (Calculator.Client)client);
256 }
257 else
258 {
259 // it uses binary protocol there to create Multiplexed protocols
260 var multiplex = new TMultiplexedProtocol(protocol, nameof(Calculator));
261 client = new Calculator.Client(multiplex);
262 await ExecuteCalculatorClientOperations(cancellationToken, (Calculator.Client)client);
263
264 multiplex = new TMultiplexedProtocol(protocol, nameof(SharedService));
265 client = new SharedService.Client(multiplex);
266 await ExecuteSharedServiceClientOperations(cancellationToken, (SharedService.Client)client);
267 }
268 }
269 catch (Exception ex)
270 {
271 Logger.LogError($"{client?.ClientId} " + ex);
272 }
273 finally
274 {
275 protocol.Transport.Close();
276 }
277 }
278 catch (TApplicationException x)
279 {
280 Logger.LogError(x.ToString());
281 }
282 }
283
284 private static async Task ExecuteCalculatorClientOperations(CancellationToken cancellationToken, Calculator.Client client)
285 {
286 await client.OpenTransportAsync(cancellationToken);
287
288 // Async version
289
290 Logger.LogInformation($"{client.ClientId} PingAsync()");
291 await client.pingAsync(cancellationToken);
292
293 Logger.LogInformation($"{client.ClientId} AddAsync(1,1)");
294 var sum = await client.addAsync(1, 1, cancellationToken);
295 Logger.LogInformation($"{client.ClientId} AddAsync(1,1)={sum}");
296
297 var work = new Work
298 {
299 Op = Operation.DIVIDE,
300 Num1 = 1,
301 Num2 = 0
302 };
303
304 try
305 {
306 Logger.LogInformation($"{client.ClientId} CalculateAsync(1)");
307 await client.calculateAsync(1, work, cancellationToken);
308 Logger.LogInformation($"{client.ClientId} Whoa we can divide by 0");
309 }
310 catch (InvalidOperation io)
311 {
312 Logger.LogInformation($"{client.ClientId} Invalid operation: " + io);
313 }
314
315 work.Op = Operation.SUBTRACT;
316 work.Num1 = 15;
317 work.Num2 = 10;
318
319 try
320 {
321 Logger.LogInformation($"{client.ClientId} CalculateAsync(1)");
322 var diff = await client.calculateAsync(1, work, cancellationToken);
323 Logger.LogInformation($"{client.ClientId} 15-10={diff}");
324 }
325 catch (InvalidOperation io)
326 {
327 Logger.LogInformation($"{client.ClientId} Invalid operation: " + io);
328 }
329
330 Logger.LogInformation($"{client.ClientId} GetStructAsync(1)");
331 var log = await client.getStructAsync(1, cancellationToken);
332 Logger.LogInformation($"{client.ClientId} Check log: {log.Value}");
333
334 Logger.LogInformation($"{client.ClientId} ZipAsync() with delay 100mc on server side");
335 await client.zipAsync(cancellationToken);
336 }
337 private static async Task ExecuteSharedServiceClientOperations(CancellationToken cancellationToken, SharedService.Client client)
338 {
339 await client.OpenTransportAsync(cancellationToken);
340
341 // Async version
342
343 Logger.LogInformation($"{client.ClientId} SharedService GetStructAsync(1)");
344 var log = await client.getStructAsync(1, cancellationToken);
345 Logger.LogInformation($"{client.ClientId} SharedService Value: {log.Value}");
346 }
347
348
349 private enum Transport
350 {
351 Tcp,
352 NamedPipe,
353 Http,
354 TcpBuffered,
355 Framed,
356 TcpTls
357 }
358
359 private enum Protocol
360 {
361 Binary,
362 Compact,
363 Json,
364 Multiplexed
365 }
366 }
367}