blob: 8a7410f95fc4599845e70b20506ba71fc0eef9a3 [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.Security.Authentication;
23using System.Security.Cryptography.X509Certificates;
24using System.Text;
25using System.Threading;
26using System.Threading.Tasks;
27using Microsoft.Extensions.Logging;
28using Thrift;
29using Thrift.Collections;
30using Thrift.Processor;
31using Thrift.Protocol;
32using Thrift.Server;
33using Thrift.Transport;
34using Thrift.Transport.Server;
35
36namespace ThriftTest
37{
38 internal class ServerParam
39 {
40 internal bool useBufferedSockets = false;
41 internal bool useFramed = false;
42 internal bool useEncryption = false;
43 internal bool compact = false;
44 internal bool json = false;
45 internal int port = 9090;
46 internal string pipe = null;
47
48 internal void Parse(List<string> args)
49 {
50 for (var i = 0; i < args.Count; i++)
51 {
52 if (args[i].StartsWith("--pipe="))
53 {
54 pipe = args[i].Substring(args[i].IndexOf("=") + 1);
55 }
56 else if (args[i].StartsWith("--port="))
57 {
58 port = int.Parse(args[i].Substring(args[i].IndexOf("=") + 1));
59 }
60 else if (args[i] == "-b" || args[i] == "--buffered" || args[i] == "--transport=buffered")
61 {
62 useBufferedSockets = true;
63 }
64 else if (args[i] == "-f" || args[i] == "--framed" || args[i] == "--transport=framed")
65 {
66 useFramed = true;
67 }
68 else if (args[i] == "--binary" || args[i] == "--protocol=binary")
69 {
70 // nothing needed
71 }
72 else if (args[i] == "--compact" || args[i] == "--protocol=compact")
73 {
74 compact = true;
75 }
76 else if (args[i] == "--json" || args[i] == "--protocol=json")
77 {
78 json = true;
79 }
80 else if (args[i] == "--threaded" || args[i] == "--server-type=threaded")
81 {
82 throw new NotImplementedException(args[i]);
83 }
84 else if (args[i] == "--threadpool" || args[i] == "--server-type=threadpool")
85 {
86 throw new NotImplementedException(args[i]);
87 }
88 else if (args[i] == "--prototype" || args[i] == "--processor=prototype")
89 {
90 throw new NotImplementedException(args[i]);
91 }
92 else if (args[i] == "--ssl")
93 {
94 useEncryption = true;
95 }
96 else
97 {
98 //throw new ArgumentException(args[i]);
99 }
100 }
101
102 }
103 }
104
105 public class TestServer
106 {
107 public static int _clientID = -1;
108 public delegate void TestLogDelegate(string msg, params object[] values);
109
110 public class MyServerEventHandler : TServerEventHandler
111 {
112 public int callCount = 0;
113
114 public Task PreServeAsync(CancellationToken cancellationToken)
115 {
116 callCount++;
117 return Task.CompletedTask;
118 }
119
120 public Task<object> CreateContextAsync(TProtocol input, TProtocol output, CancellationToken cancellationToken)
121 {
122 callCount++;
123 return Task.FromResult<object>(null);
124 }
125
126 public Task DeleteContextAsync(object serverContext, TProtocol input, TProtocol output, CancellationToken cancellationToken)
127 {
128 callCount++;
129 return Task.CompletedTask;
130 }
131
132 public Task ProcessContextAsync(object serverContext, TTransport transport, CancellationToken cancellationToken)
133 {
134 callCount++;
135 return Task.CompletedTask;
136 }
137 }
138
139 public class TestHandlerAsync : ThriftTest.IAsync
140 {
141 public TServer server { get; set; }
142 private int handlerID;
143 private StringBuilder sb = new StringBuilder();
144 private TestLogDelegate logger;
145
146 public TestHandlerAsync()
147 {
148 handlerID = Interlocked.Increment(ref _clientID);
149 logger += testConsoleLogger;
150 logger.Invoke("New TestHandler instance created");
151 }
152
153 public void testConsoleLogger(string msg, params object[] values)
154 {
155 sb.Clear();
156 sb.AppendFormat("handler{0:D3}:", handlerID);
157 sb.AppendFormat(msg, values);
158 sb.AppendLine();
159 Console.Write(sb.ToString());
160 }
161
162 public Task testVoidAsync(CancellationToken cancellationToken)
163 {
164 logger.Invoke("testVoid()");
165 return Task.CompletedTask;
166 }
167
168 public Task<string> testStringAsync(string thing, CancellationToken cancellationToken)
169 {
170 logger.Invoke("testString({0})", thing);
171 return Task.FromResult(thing);
172 }
173
174 public Task<bool> testBoolAsync(bool thing, CancellationToken cancellationToken)
175 {
176 logger.Invoke("testBool({0})", thing);
177 return Task.FromResult(thing);
178 }
179
180 public Task<sbyte> testByteAsync(sbyte thing, CancellationToken cancellationToken)
181 {
182 logger.Invoke("testByte({0})", thing);
183 return Task.FromResult(thing);
184 }
185
186 public Task<int> testI32Async(int thing, CancellationToken cancellationToken)
187 {
188 logger.Invoke("testI32({0})", thing);
189 return Task.FromResult(thing);
190 }
191
192 public Task<long> testI64Async(long thing, CancellationToken cancellationToken)
193 {
194 logger.Invoke("testI64({0})", thing);
195 return Task.FromResult(thing);
196 }
197
198 public Task<double> testDoubleAsync(double thing, CancellationToken cancellationToken)
199 {
200 logger.Invoke("testDouble({0})", thing);
201 return Task.FromResult(thing);
202 }
203
204 public Task<byte[]> testBinaryAsync(byte[] thing, CancellationToken cancellationToken)
205 {
206 var hex = BitConverter.ToString(thing).Replace("-", string.Empty);
207 logger.Invoke("testBinary({0:X})", hex);
208 return Task.FromResult(thing);
209 }
210
211 public Task<Xtruct> testStructAsync(Xtruct thing, CancellationToken cancellationToken)
212 {
213 logger.Invoke("testStruct({{\"{0}\", {1}, {2}, {3}}})", thing.String_thing, thing.Byte_thing, thing.I32_thing, thing.I64_thing);
214 return Task.FromResult(thing);
215 }
216
217 public Task<Xtruct2> testNestAsync(Xtruct2 nest, CancellationToken cancellationToken)
218 {
219 var thing = nest.Struct_thing;
220 logger.Invoke("testNest({{{0}, {{\"{1}\", {2}, {3}, {4}, {5}}}}})",
221 nest.Byte_thing,
222 thing.String_thing,
223 thing.Byte_thing,
224 thing.I32_thing,
225 thing.I64_thing,
226 nest.I32_thing);
227 return Task.FromResult(nest);
228 }
229
230 public Task<Dictionary<int, int>> testMapAsync(Dictionary<int, int> thing, CancellationToken cancellationToken)
231 {
232 sb.Clear();
233 sb.Append("testMap({{");
234 var first = true;
235 foreach (var key in thing.Keys)
236 {
237 if (first)
238 {
239 first = false;
240 }
241 else
242 {
243 sb.Append(", ");
244 }
245 sb.AppendFormat("{0} => {1}", key, thing[key]);
246 }
247 sb.Append("}})");
248 logger.Invoke(sb.ToString());
249 return Task.FromResult(thing);
250 }
251
252 public Task<Dictionary<string, string>> testStringMapAsync(Dictionary<string, string> thing, CancellationToken cancellationToken)
253 {
254 sb.Clear();
255 sb.Append("testStringMap({{");
256 var first = true;
257 foreach (var key in thing.Keys)
258 {
259 if (first)
260 {
261 first = false;
262 }
263 else
264 {
265 sb.Append(", ");
266 }
267 sb.AppendFormat("{0} => {1}", key, thing[key]);
268 }
269 sb.Append("}})");
270 logger.Invoke(sb.ToString());
271 return Task.FromResult(thing);
272 }
273
274 public Task<THashSet<int>> testSetAsync(THashSet<int> thing, CancellationToken cancellationToken)
275 {
276 sb.Clear();
277 sb.Append("testSet({{");
278 var first = true;
279 foreach (int elem in thing)
280 {
281 if (first)
282 {
283 first = false;
284 }
285 else
286 {
287 sb.Append(", ");
288 }
289 sb.AppendFormat("{0}", elem);
290 }
291 sb.Append("}})");
292 logger.Invoke(sb.ToString());
293 return Task.FromResult(thing);
294 }
295
296 public Task<List<int>> testListAsync(List<int> thing, CancellationToken cancellationToken)
297 {
298 sb.Clear();
299 sb.Append("testList({{");
300 var first = true;
301 foreach (var elem in thing)
302 {
303 if (first)
304 {
305 first = false;
306 }
307 else
308 {
309 sb.Append(", ");
310 }
311 sb.AppendFormat("{0}", elem);
312 }
313 sb.Append("}})");
314 logger.Invoke(sb.ToString());
315 return Task.FromResult(thing);
316 }
317
318 public Task<Numberz> testEnumAsync(Numberz thing, CancellationToken cancellationToken)
319 {
320 logger.Invoke("testEnum({0})", thing);
321 return Task.FromResult(thing);
322 }
323
324 public Task<long> testTypedefAsync(long thing, CancellationToken cancellationToken)
325 {
326 logger.Invoke("testTypedef({0})", thing);
327 return Task.FromResult(thing);
328 }
329
330 public Task<Dictionary<int, Dictionary<int, int>>> testMapMapAsync(int hello, CancellationToken cancellationToken)
331 {
332 logger.Invoke("testMapMap({0})", hello);
333 var mapmap = new Dictionary<int, Dictionary<int, int>>();
334
335 var pos = new Dictionary<int, int>();
336 var neg = new Dictionary<int, int>();
337 for (var i = 1; i < 5; i++)
338 {
339 pos[i] = i;
340 neg[-i] = -i;
341 }
342
343 mapmap[4] = pos;
344 mapmap[-4] = neg;
345
346 return Task.FromResult(mapmap);
347 }
348
349 public Task<Dictionary<long, Dictionary<Numberz, Insanity>>> testInsanityAsync(Insanity argument, CancellationToken cancellationToken)
350 {
351 logger.Invoke("testInsanity()");
352
353 /** from ThriftTest.thrift:
354 * So you think you've got this all worked, out eh?
355 *
356 * Creates a the returned map with these values and prints it out:
357 * { 1 => { 2 => argument,
358 * 3 => argument,
359 * },
360 * 2 => { 6 => <empty Insanity struct>, },
361 * }
362 * @return map<UserId, map<Numberz,Insanity>> - a map with the above values
363 */
364
365 var first_map = new Dictionary<Numberz, Insanity>();
366 var second_map = new Dictionary<Numberz, Insanity>(); ;
367
368 first_map[Numberz.TWO] = argument;
369 first_map[Numberz.THREE] = argument;
370
371 second_map[Numberz.SIX] = new Insanity();
372
373 var insane = new Dictionary<long, Dictionary<Numberz, Insanity>>
374 {
375 [1] = first_map,
376 [2] = second_map
377 };
378
379 return Task.FromResult(insane);
380 }
381
382 public Task<Xtruct> testMultiAsync(sbyte arg0, int arg1, long arg2, Dictionary<short, string> arg3, Numberz arg4, long arg5,
383 CancellationToken cancellationToken)
384 {
385 logger.Invoke("testMulti()");
386
387 var hello = new Xtruct(); ;
388 hello.String_thing = "Hello2";
389 hello.Byte_thing = arg0;
390 hello.I32_thing = arg1;
391 hello.I64_thing = arg2;
392 return Task.FromResult(hello);
393 }
394
395 public Task testExceptionAsync(string arg, CancellationToken cancellationToken)
396 {
397 logger.Invoke("testException({0})", arg);
398 if (arg == "Xception")
399 {
400 var x = new Xception
401 {
402 ErrorCode = 1001,
403 Message = arg
404 };
405 throw x;
406 }
407 if (arg == "TException")
408 {
409 throw new TException();
410 }
411 return Task.CompletedTask;
412 }
413
414 public Task<Xtruct> testMultiExceptionAsync(string arg0, string arg1, CancellationToken cancellationToken)
415 {
416 logger.Invoke("testMultiException({0}, {1})", arg0, arg1);
417 if (arg0 == "Xception")
418 {
419 var x = new Xception
420 {
421 ErrorCode = 1001,
422 Message = "This is an Xception"
423 };
424 throw x;
425 }
426
427 if (arg0 == "Xception2")
428 {
429 var x = new Xception2
430 {
431 ErrorCode = 2002,
432 Struct_thing = new Xtruct { String_thing = "This is an Xception2" }
433 };
434 throw x;
435 }
436
437 var result = new Xtruct { String_thing = arg1 };
438 return Task.FromResult(result);
439 }
440
441 public Task testOnewayAsync(int secondsToSleep, CancellationToken cancellationToken)
442 {
443 logger.Invoke("testOneway({0}), sleeping...", secondsToSleep);
444 Task.Delay(secondsToSleep * 1000, cancellationToken).GetAwaiter().GetResult();
445 logger.Invoke("testOneway finished");
446
447 return Task.CompletedTask;
448 }
449 }
450
451 internal static void PrintOptionsHelp()
452 {
453 Console.WriteLine("Server options:");
454 Console.WriteLine(" --pipe=<pipe name>");
455 Console.WriteLine(" --port=<port number>");
456 Console.WriteLine(" --transport=<transport name> one of buffered,framed (defaults to none)");
457 Console.WriteLine(" --protocol=<protocol name> one of compact,json (defaults to binary)");
458 Console.WriteLine(" --server-type=<type> one of threaded,threadpool (defaults to simple)");
459 Console.WriteLine(" --processor=<prototype>");
460 Console.WriteLine(" --ssl");
461 Console.WriteLine();
462 }
463
464 private static X509Certificate2 GetServerCert()
465 {
466 var serverCertName = "server.p12";
467 var possiblePaths = new List<string>
468 {
469 "../../../keys/",
470 "../../keys/",
471 "../keys/",
472 "keys/",
473 };
474
475 string existingPath = null;
476 foreach (var possiblePath in possiblePaths)
477 {
478 var path = Path.GetFullPath(possiblePath + serverCertName);
479 if (File.Exists(path))
480 {
481 existingPath = path;
482 break;
483 }
484 }
485
486 if (string.IsNullOrEmpty(existingPath))
487 {
488 throw new FileNotFoundException($"Cannot find file: {serverCertName}");
489 }
490
491 var cert = new X509Certificate2(existingPath, "thrift");
492
493 return cert;
494 }
495
496 public static int Execute(List<string> args)
497 {
498 var loggerFactory = new LoggerFactory();//.AddConsole().AddDebug();
499 var logger = new LoggerFactory().CreateLogger("Test");
500
501 try
502 {
503 var param = new ServerParam();
504
505 try
506 {
507 param.Parse(args);
508 }
509 catch (Exception ex)
510 {
511 Console.WriteLine("*** FAILED ***");
512 Console.WriteLine("Error while parsing arguments");
513 Console.WriteLine(ex.Message + " ST: " + ex.StackTrace);
514 return 1;
515 }
516
517
518 // Transport
519 TServerTransport trans;
520 if (param.pipe != null)
521 {
522 trans = new TNamedPipeServerTransport(param.pipe);
523 }
524// else if (param.useFramed)
525// {
526// trans = new TServerFramedTransport(param.port);
527// }
528 else
529 {
530 if (param.useEncryption)
531 {
532 var cert = GetServerCert();
533
534 if (cert == null || !cert.HasPrivateKey)
535 {
536 throw new InvalidOperationException("Certificate doesn't contain private key");
537 }
538
539 trans = new TTlsServerSocketTransport(param.port, param.useBufferedSockets, param.useFramed, cert,
540 (sender, certificate, chain, errors) => true,
541 null, SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12);
542 }
543 else
544 {
545 trans = new TServerSocketTransport(param.port, 0, param.useBufferedSockets, param.useFramed);
546 }
547 }
548
549 ITProtocolFactory proto;
550 if (param.compact)
551 proto = new TCompactProtocol.Factory();
552 else if (param.json)
553 proto = new TJsonProtocol.Factory();
554 else
555 proto = new TBinaryProtocol.Factory();
556
557 ITProcessorFactory processorFactory;
558
559 // Processor
560 var testHandler = new TestHandlerAsync();
561 var testProcessor = new ThriftTest.AsyncProcessor(testHandler);
562 processorFactory = new TSingletonProcessorFactory(testProcessor);
563
564 TTransportFactory transFactory = new TTransportFactory();
565
566 TServer serverEngine = new TSimpleAsyncServer(processorFactory, trans, transFactory, transFactory, proto, proto, logger);
567
568 //Server event handler
569 var serverEvents = new MyServerEventHandler();
570 serverEngine.SetEventHandler(serverEvents);
571
572 // Run it
573 var where = (! string.IsNullOrEmpty(param.pipe)) ? "on pipe " + param.pipe : "on port " + param.port;
574 Console.WriteLine("Starting the AsyncBaseServer " + where +
575 " with processor TPrototypeProcessorFactory prototype factory " +
576 (param.useBufferedSockets ? " with buffered socket" : "") +
577 (param.useFramed ? " with framed transport" : "") +
578 (param.useEncryption ? " with encryption" : "") +
579 (param.compact ? " with compact protocol" : "") +
580 (param.json ? " with json protocol" : "") +
581 "...");
582 serverEngine.ServeAsync(CancellationToken.None).GetAwaiter().GetResult();
583 Console.ReadLine();
584 }
585 catch (Exception x)
586 {
587 Console.Error.Write(x);
588 return 1;
589 }
590 Console.WriteLine("done.");
591 return 0;
592 }
593 }
594
595}