blob: 82b36ebadf7dc0c47019759445bc17edc99543cc [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 {
249 var hex = BitConverter.ToString(thing).Replace("-", string.Empty);
250 logger.Invoke("testBinary({0:X})", hex);
251 return Task.FromResult(thing);
252 }
253
254 public Task<Xtruct> testStructAsync(Xtruct thing, CancellationToken cancellationToken)
255 {
256 logger.Invoke("testStruct({{\"{0}\", {1}, {2}, {3}}})", thing.String_thing, thing.Byte_thing, thing.I32_thing, thing.I64_thing);
257 return Task.FromResult(thing);
258 }
259
260 public Task<Xtruct2> testNestAsync(Xtruct2 nest, CancellationToken cancellationToken)
261 {
262 var thing = nest.Struct_thing;
263 logger.Invoke("testNest({{{0}, {{\"{1}\", {2}, {3}, {4}, {5}}}}})",
264 nest.Byte_thing,
265 thing.String_thing,
266 thing.Byte_thing,
267 thing.I32_thing,
268 thing.I64_thing,
269 nest.I32_thing);
270 return Task.FromResult(nest);
271 }
272
273 public Task<Dictionary<int, int>> testMapAsync(Dictionary<int, int> thing, CancellationToken cancellationToken)
274 {
275 sb.Clear();
276 sb.Append("testMap({{");
277 var first = true;
278 foreach (var key in thing.Keys)
279 {
280 if (first)
281 {
282 first = false;
283 }
284 else
285 {
286 sb.Append(", ");
287 }
288 sb.AppendFormat("{0} => {1}", key, thing[key]);
289 }
290 sb.Append("}})");
291 logger.Invoke(sb.ToString());
292 return Task.FromResult(thing);
293 }
294
295 public Task<Dictionary<string, string>> testStringMapAsync(Dictionary<string, string> thing, CancellationToken cancellationToken)
296 {
297 sb.Clear();
298 sb.Append("testStringMap({{");
299 var first = true;
300 foreach (var key in thing.Keys)
301 {
302 if (first)
303 {
304 first = false;
305 }
306 else
307 {
308 sb.Append(", ");
309 }
310 sb.AppendFormat("{0} => {1}", key, thing[key]);
311 }
312 sb.Append("}})");
313 logger.Invoke(sb.ToString());
314 return Task.FromResult(thing);
315 }
316
317 public Task<THashSet<int>> testSetAsync(THashSet<int> thing, CancellationToken cancellationToken)
318 {
319 sb.Clear();
320 sb.Append("testSet({{");
321 var first = true;
322 foreach (int elem in thing)
323 {
324 if (first)
325 {
326 first = false;
327 }
328 else
329 {
330 sb.Append(", ");
331 }
332 sb.AppendFormat("{0}", elem);
333 }
334 sb.Append("}})");
335 logger.Invoke(sb.ToString());
336 return Task.FromResult(thing);
337 }
338
339 public Task<List<int>> testListAsync(List<int> thing, CancellationToken cancellationToken)
340 {
341 sb.Clear();
342 sb.Append("testList({{");
343 var first = true;
344 foreach (var elem in thing)
345 {
346 if (first)
347 {
348 first = false;
349 }
350 else
351 {
352 sb.Append(", ");
353 }
354 sb.AppendFormat("{0}", elem);
355 }
356 sb.Append("}})");
357 logger.Invoke(sb.ToString());
358 return Task.FromResult(thing);
359 }
360
361 public Task<Numberz> testEnumAsync(Numberz thing, CancellationToken cancellationToken)
362 {
363 logger.Invoke("testEnum({0})", thing);
364 return Task.FromResult(thing);
365 }
366
367 public Task<long> testTypedefAsync(long thing, CancellationToken cancellationToken)
368 {
369 logger.Invoke("testTypedef({0})", thing);
370 return Task.FromResult(thing);
371 }
372
373 public Task<Dictionary<int, Dictionary<int, int>>> testMapMapAsync(int hello, CancellationToken cancellationToken)
374 {
375 logger.Invoke("testMapMap({0})", hello);
376 var mapmap = new Dictionary<int, Dictionary<int, int>>();
377
378 var pos = new Dictionary<int, int>();
379 var neg = new Dictionary<int, int>();
380 for (var i = 1; i < 5; i++)
381 {
382 pos[i] = i;
383 neg[-i] = -i;
384 }
385
386 mapmap[4] = pos;
387 mapmap[-4] = neg;
388
389 return Task.FromResult(mapmap);
390 }
391
392 public Task<Dictionary<long, Dictionary<Numberz, Insanity>>> testInsanityAsync(Insanity argument, CancellationToken cancellationToken)
393 {
394 logger.Invoke("testInsanity()");
395
396 /** from ThriftTest.thrift:
397 * So you think you've got this all worked, out eh?
398 *
399 * Creates a the returned map with these values and prints it out:
400 * { 1 => { 2 => argument,
401 * 3 => argument,
402 * },
403 * 2 => { 6 => <empty Insanity struct>, },
404 * }
405 * @return map<UserId, map<Numberz,Insanity>> - a map with the above values
406 */
407
408 var first_map = new Dictionary<Numberz, Insanity>();
409 var second_map = new Dictionary<Numberz, Insanity>(); ;
410
411 first_map[Numberz.TWO] = argument;
412 first_map[Numberz.THREE] = argument;
413
414 second_map[Numberz.SIX] = new Insanity();
415
416 var insane = new Dictionary<long, Dictionary<Numberz, Insanity>>
417 {
418 [1] = first_map,
419 [2] = second_map
420 };
421
422 return Task.FromResult(insane);
423 }
424
425 public Task<Xtruct> testMultiAsync(sbyte arg0, int arg1, long arg2, Dictionary<short, string> arg3, Numberz arg4, long arg5,
426 CancellationToken cancellationToken)
427 {
428 logger.Invoke("testMulti()");
429
430 var hello = new Xtruct(); ;
431 hello.String_thing = "Hello2";
432 hello.Byte_thing = arg0;
433 hello.I32_thing = arg1;
434 hello.I64_thing = arg2;
435 return Task.FromResult(hello);
436 }
437
438 public Task testExceptionAsync(string arg, CancellationToken cancellationToken)
439 {
440 logger.Invoke("testException({0})", arg);
441 if (arg == "Xception")
442 {
443 var x = new Xception
444 {
445 ErrorCode = 1001,
446 Message = arg
447 };
448 throw x;
449 }
450 if (arg == "TException")
451 {
452 throw new TException();
453 }
454 return Task.CompletedTask;
455 }
456
457 public Task<Xtruct> testMultiExceptionAsync(string arg0, string arg1, CancellationToken cancellationToken)
458 {
459 logger.Invoke("testMultiException({0}, {1})", arg0, arg1);
460 if (arg0 == "Xception")
461 {
462 var x = new Xception
463 {
464 ErrorCode = 1001,
465 Message = "This is an Xception"
466 };
467 throw x;
468 }
469
470 if (arg0 == "Xception2")
471 {
472 var x = new Xception2
473 {
474 ErrorCode = 2002,
475 Struct_thing = new Xtruct { String_thing = "This is an Xception2" }
476 };
477 throw x;
478 }
479
480 var result = new Xtruct { String_thing = arg1 };
481 return Task.FromResult(result);
482 }
483
484 public Task testOnewayAsync(int secondsToSleep, CancellationToken cancellationToken)
485 {
486 logger.Invoke("testOneway({0}), sleeping...", secondsToSleep);
487 Task.Delay(secondsToSleep * 1000, cancellationToken).GetAwaiter().GetResult();
488 logger.Invoke("testOneway finished");
489
490 return Task.CompletedTask;
491 }
492 }
493
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100494
495 private static X509Certificate2 GetServerCert()
496 {
497 var serverCertName = "server.p12";
498 var possiblePaths = new List<string>
499 {
500 "../../../keys/",
501 "../../keys/",
502 "../keys/",
503 "keys/",
504 };
505
506 string existingPath = null;
507 foreach (var possiblePath in possiblePaths)
508 {
509 var path = Path.GetFullPath(possiblePath + serverCertName);
510 if (File.Exists(path))
511 {
512 existingPath = path;
513 break;
514 }
515 }
516
517 if (string.IsNullOrEmpty(existingPath))
518 {
519 throw new FileNotFoundException($"Cannot find file: {serverCertName}");
520 }
521
522 var cert = new X509Certificate2(existingPath, "thrift");
523
524 return cert;
525 }
526
527 public static int Execute(List<string> args)
528 {
529 var loggerFactory = new LoggerFactory();//.AddConsole().AddDebug();
530 var logger = new LoggerFactory().CreateLogger("Test");
531
532 try
533 {
534 var param = new ServerParam();
535
536 try
537 {
538 param.Parse(args);
539 }
540 catch (Exception ex)
541 {
542 Console.WriteLine("*** FAILED ***");
543 Console.WriteLine("Error while parsing arguments");
544 Console.WriteLine(ex.Message + " ST: " + ex.StackTrace);
545 return 1;
546 }
547
548
Kyle Smith7b94dd42019-03-23 17:26:56 +0100549 // Endpoint transport (mandatory)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100550 TServerTransport trans;
Jens Geyeradde44b2019-02-05 01:00:02 +0100551 switch (param.transport)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100552 {
Jens Geyeradde44b2019-02-05 01:00:02 +0100553 case TransportChoice.NamedPipe:
554 Debug.Assert(param.pipe != null);
555 trans = new TNamedPipeServerTransport(param.pipe);
556 break;
557
558
559 case TransportChoice.TlsSocket:
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100560 var cert = GetServerCert();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100561 if (cert == null || !cert.HasPrivateKey)
562 {
563 throw new InvalidOperationException("Certificate doesn't contain private key");
564 }
Jens Geyeradde44b2019-02-05 01:00:02 +0100565
Kyle Smith7b94dd42019-03-23 17:26:56 +0100566 trans = new TTlsServerSocketTransport( param.port, cert,
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100567 (sender, certificate, chain, errors) => true,
568 null, SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12);
Jens Geyeradde44b2019-02-05 01:00:02 +0100569 break;
570
571 case TransportChoice.Socket:
572 default:
Kyle Smith7b94dd42019-03-23 17:26:56 +0100573 trans = new TServerSocketTransport(param.port, 0);
Jens Geyeradde44b2019-02-05 01:00:02 +0100574 break;
575 }
576
Kyle Smith7b94dd42019-03-23 17:26:56 +0100577 // Layered transport (mandatory)
578 TTransportFactory transFactory = null;
579 switch (param.buffering)
Jens Geyeradde44b2019-02-05 01:00:02 +0100580 {
Kyle Smith7b94dd42019-03-23 17:26:56 +0100581 case BufferChoice.Framed:
582 transFactory = new TFramedTransport.Factory();
583 break;
584 case BufferChoice.Buffered:
585 transFactory = new TBufferedTransport.Factory();
586 break;
587 default:
588 Debug.Assert(param.buffering == BufferChoice.None, "unhandled case");
589 transFactory = null; // no layered transprt
590 break;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100591 }
592
Kyle Smith7b94dd42019-03-23 17:26:56 +0100593 // Protocol (mandatory)
Jens Geyer421444f2019-03-20 22:13:25 +0100594 TProtocolFactory proto;
Jens Geyeradde44b2019-02-05 01:00:02 +0100595 switch (param.protocol)
596 {
597 case ProtocolChoice.Compact:
598 proto = new TCompactProtocol.Factory();
599 break;
600 case ProtocolChoice.Json:
601 proto = new TJsonProtocol.Factory();
602 break;
603 case ProtocolChoice.Binary:
604 default:
605 proto = new TBinaryProtocol.Factory();
606 break;
607 }
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100608
609 // Processor
610 var testHandler = new TestHandlerAsync();
611 var testProcessor = new ThriftTest.AsyncProcessor(testHandler);
Jens Geyeradde44b2019-02-05 01:00:02 +0100612 var processorFactory = new TSingletonProcessorFactory(testProcessor);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100613
614 TServer serverEngine = new TSimpleAsyncServer(processorFactory, trans, transFactory, transFactory, proto, proto, logger);
615
616 //Server event handler
617 var serverEvents = new MyServerEventHandler();
618 serverEngine.SetEventHandler(serverEvents);
619
620 // Run it
621 var where = (! string.IsNullOrEmpty(param.pipe)) ? "on pipe " + param.pipe : "on port " + param.port;
622 Console.WriteLine("Starting the AsyncBaseServer " + where +
623 " with processor TPrototypeProcessorFactory prototype factory " +
Kyle Smith7b94dd42019-03-23 17:26:56 +0100624 (param.buffering == BufferChoice.Buffered ? " with buffered transport" : "") +
625 (param.buffering == BufferChoice.Framed ? " with framed transport" : "") +
Jens Geyeradde44b2019-02-05 01:00:02 +0100626 (param.transport == TransportChoice.TlsSocket ? " with encryption" : "") +
627 (param.protocol == ProtocolChoice.Compact ? " with compact protocol" : "") +
628 (param.protocol == ProtocolChoice.Json ? " with json protocol" : "") +
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100629 "...");
630 serverEngine.ServeAsync(CancellationToken.None).GetAwaiter().GetResult();
631 Console.ReadLine();
632 }
633 catch (Exception x)
634 {
635 Console.Error.Write(x);
636 return 1;
637 }
638 Console.WriteLine("done.");
639 return 0;
640 }
641 }
642
643}