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