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