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