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