blob: c1e0cb3ece9a57357cc7bfd277ba5506fa54b693 [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.Net.Security;
23using System.Security.Cryptography.X509Certificates;
24using System.Threading;
25using System.Threading.Tasks;
26using Microsoft.AspNetCore.Builder;
27using Microsoft.AspNetCore.Hosting;
28using Microsoft.Extensions.Configuration;
29using Microsoft.Extensions.DependencyInjection;
30using Microsoft.Extensions.Logging;
31using Thrift;
32using Thrift.Protocol;
33using Thrift.Server;
34using Thrift.Transport;
35using Thrift.Transport.Server;
36using tutorial;
37using shared;
38using Thrift.Processor;
Kyle Smith7b94dd42019-03-23 17:26:56 +010039using System.Diagnostics;
Jens Geyeraa0c8b32019-01-28 23:27:45 +010040
41namespace Server
42{
43 public class Program
44 {
Jens Geyerb11f63c2019-03-14 21:12:38 +010045 private static ServiceCollection ServiceCollection = new ServiceCollection();
46 private static ILogger Logger;
Jens Geyereacd1d42019-11-20 19:03:14 +010047 private static readonly TConfiguration Configuration = null; // new TConfiguration() if needed
Jens Geyeraa0c8b32019-01-28 23:27:45 +010048
49 public static void Main(string[] args)
50 {
51 args = args ?? new string[0];
52
Jens Geyerb11f63c2019-03-14 21:12:38 +010053 ServiceCollection.AddLogging(logging => ConfigureLogging(logging));
54 Logger = ServiceCollection.BuildServiceProvider().GetService<ILoggerFactory>().CreateLogger(nameof(Server));
55
56
Jens Geyeraa0c8b32019-01-28 23:27:45 +010057 if (args.Any(x => x.StartsWith("-help", StringComparison.OrdinalIgnoreCase)))
58 {
59 DisplayHelp();
60 return;
61 }
62
63 using (var source = new CancellationTokenSource())
64 {
65 RunAsync(args, source.Token).GetAwaiter().GetResult();
66
67 Logger.LogInformation("Press any key to stop...");
68
69 Console.ReadLine();
70 source.Cancel();
71 }
72
73 Logger.LogInformation("Server stopped");
74 }
75
Jens Geyerb11f63c2019-03-14 21:12:38 +010076 private static void ConfigureLogging(ILoggingBuilder logging)
77 {
78 logging.SetMinimumLevel(LogLevel.Trace);
79 logging.AddConsole();
80 logging.AddDebug();
81 }
82
Jens Geyeraa0c8b32019-01-28 23:27:45 +010083 private static void DisplayHelp()
84 {
85 Logger.LogInformation(@"
86Usage:
87 Server.exe -help
88 will diplay help information
89
Kyle Smith7b94dd42019-03-23 17:26:56 +010090 Server.exe -tr:<transport> -bf:<buffering> -pr:<protocol>
91 will run server with specified arguments (tcp transport, no buffering, and binary protocol by default)
Jens Geyeraa0c8b32019-01-28 23:27:45 +010092
93Options:
94 -tr (transport):
95 tcp - (default) tcp transport will be used (host - ""localhost"", port - 9090)
Jens Geyeraa0c8b32019-01-28 23:27:45 +010096 namedpipe - namedpipe transport will be used (pipe address - "".test"")
97 http - http transport will be used (http address - ""localhost:9090"")
98 tcptls - tcp transport with tls will be used (host - ""localhost"", port - 9090)
Kyle Smith7b94dd42019-03-23 17:26:56 +010099
100 -bf (buffering):
101 none - (default) no buffering will be used
102 buffered - buffered transport will be used
103 framed - framed transport will be used
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100104
105 -pr (protocol):
106 binary - (default) binary protocol will be used
107 compact - compact protocol will be used
108 json - json protocol will be used
109 multiplexed - multiplexed protocol will be used
110
111Sample:
112 Server.exe -tr:tcp
113");
114 }
115
116 private static async Task RunAsync(string[] args, CancellationToken cancellationToken)
117 {
118 var selectedTransport = GetTransport(args);
Kyle Smith7b94dd42019-03-23 17:26:56 +0100119 var selectedBuffering = GetBuffering(args);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100120 var selectedProtocol = GetProtocol(args);
121
122 if (selectedTransport == Transport.Http)
123 {
124 new HttpServerSample().Run(cancellationToken);
125 }
126 else
127 {
Kyle Smith7b94dd42019-03-23 17:26:56 +0100128 await RunSelectedConfigurationAsync(selectedTransport, selectedBuffering, selectedProtocol, cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100129 }
130 }
131
132 private static Protocol GetProtocol(string[] args)
133 {
134 var transport = args.FirstOrDefault(x => x.StartsWith("-pr"))?.Split(':')?[1];
135
136 Enum.TryParse(transport, true, out Protocol selectedProtocol);
137
138 return selectedProtocol;
139 }
140
Kyle Smith7b94dd42019-03-23 17:26:56 +0100141 private static Buffering GetBuffering(string[] args)
142 {
143 var buffering = args.FirstOrDefault(x => x.StartsWith("-bf"))?.Split(":")?[1];
144
145 Enum.TryParse<Buffering>(buffering, out var selectedBuffering);
146
147 return selectedBuffering;
148 }
149
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100150 private static Transport GetTransport(string[] args)
151 {
152 var transport = args.FirstOrDefault(x => x.StartsWith("-tr"))?.Split(':')?[1];
153
154 Enum.TryParse(transport, true, out Transport selectedTransport);
155
156 return selectedTransport;
157 }
158
Kyle Smith7b94dd42019-03-23 17:26:56 +0100159 private static async Task RunSelectedConfigurationAsync(Transport transport, Buffering buffering, Protocol protocol, CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100160 {
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100161 var handler = new CalculatorAsyncHandler();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100162
163 TServerTransport serverTransport = null;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100164 switch (transport)
165 {
166 case Transport.Tcp:
Jens Geyereacd1d42019-11-20 19:03:14 +0100167 serverTransport = new TServerSocketTransport(9090, Configuration);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100168 break;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100169 case Transport.NamedPipe:
Jens Geyereacd1d42019-11-20 19:03:14 +0100170 serverTransport = new TNamedPipeServerTransport(".test", Configuration);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100171 break;
172 case Transport.TcpTls:
Jens Geyereacd1d42019-11-20 19:03:14 +0100173 serverTransport = new TTlsServerSocketTransport(9090, Configuration,
174 GetCertificate(), ClientCertValidator, LocalCertificateSelectionCallback);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100175 break;
176 }
177
Kyle Smith7b94dd42019-03-23 17:26:56 +0100178 TTransportFactory inputTransportFactory = null;
179 TTransportFactory outputTransportFactory = null;
180 switch (buffering)
181 {
182 case Buffering.Buffered:
183 inputTransportFactory = new TBufferedTransport.Factory();
184 outputTransportFactory = new TBufferedTransport.Factory();
185 break;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100186
Kyle Smith7b94dd42019-03-23 17:26:56 +0100187 case Buffering.Framed:
188 inputTransportFactory = new TFramedTransport.Factory();
189 outputTransportFactory = new TFramedTransport.Factory();
190 break;
191
192 default: // layered transport(s) are optional
193 Debug.Assert(buffering == Buffering.None, "unhandled case");
194 break;
195 }
196
197 TProtocolFactory inputProtocolFactory = null;
198 TProtocolFactory outputProtocolFactory = null;
199 ITAsyncProcessor processor = null;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100200 switch (protocol)
201 {
202 case Protocol.Binary:
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100203 inputProtocolFactory = new TBinaryProtocol.Factory();
204 outputProtocolFactory = new TBinaryProtocol.Factory();
205 processor = new Calculator.AsyncProcessor(handler);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100206 break;
Jens Geyer421444f2019-03-20 22:13:25 +0100207
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100208 case Protocol.Compact:
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100209 inputProtocolFactory = new TCompactProtocol.Factory();
210 outputProtocolFactory = new TCompactProtocol.Factory();
211 processor = new Calculator.AsyncProcessor(handler);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100212 break;
Jens Geyer421444f2019-03-20 22:13:25 +0100213
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100214 case Protocol.Json:
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100215 inputProtocolFactory = new TJsonProtocol.Factory();
216 outputProtocolFactory = new TJsonProtocol.Factory();
217 processor = new Calculator.AsyncProcessor(handler);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100218 break;
Jens Geyer421444f2019-03-20 22:13:25 +0100219
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100220 case Protocol.Multiplexed:
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100221 inputProtocolFactory = new TBinaryProtocol.Factory();
222 outputProtocolFactory = new TBinaryProtocol.Factory();
223
224 var calcHandler = new CalculatorAsyncHandler();
225 var calcProcessor = new Calculator.AsyncProcessor(calcHandler);
226
227 var sharedServiceHandler = new SharedServiceAsyncHandler();
228 var sharedServiceProcessor = new SharedService.AsyncProcessor(sharedServiceHandler);
229
230 var multiplexedProcessor = new TMultiplexedProcessor();
231 multiplexedProcessor.RegisterProcessor(nameof(Calculator), calcProcessor);
232 multiplexedProcessor.RegisterProcessor(nameof(SharedService), sharedServiceProcessor);
233
234 processor = multiplexedProcessor;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100235 break;
Jens Geyer421444f2019-03-20 22:13:25 +0100236
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100237 default:
238 throw new ArgumentOutOfRangeException(nameof(protocol), protocol, null);
239 }
240
Kyle Smith7b94dd42019-03-23 17:26:56 +0100241
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100242 try
243 {
244 Logger.LogInformation(
245 $"Selected TAsyncServer with {serverTransport} transport, {processor} processor and {inputProtocolFactory} protocol factories");
246
Kyle Smith7b94dd42019-03-23 17:26:56 +0100247 var loggerFactory = ServiceCollection.BuildServiceProvider().GetService<ILoggerFactory>();
248
249 var server = new TSimpleAsyncServer(
250 itProcessorFactory: new TSingletonProcessorFactory(processor),
251 serverTransport: serverTransport,
252 inputTransportFactory: inputTransportFactory,
253 outputTransportFactory: outputTransportFactory,
254 inputProtocolFactory: inputProtocolFactory,
255 outputProtocolFactory: outputProtocolFactory,
256 logger: loggerFactory.CreateLogger<TSimpleAsyncServer>());
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100257
258 Logger.LogInformation("Starting the server...");
Kyle Smith7b94dd42019-03-23 17:26:56 +0100259
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100260 await server.ServeAsync(cancellationToken);
261 }
262 catch (Exception x)
263 {
264 Logger.LogInformation(x.ToString());
265 }
266 }
267
268 private static X509Certificate2 GetCertificate()
269 {
270 // due to files location in net core better to take certs from top folder
271 var certFile = GetCertPath(Directory.GetParent(Directory.GetCurrentDirectory()));
272 return new X509Certificate2(certFile, "ThriftTest");
273 }
274
275 private static string GetCertPath(DirectoryInfo di, int maxCount = 6)
276 {
277 var topDir = di;
278 var certFile =
279 topDir.EnumerateFiles("ThriftTest.pfx", SearchOption.AllDirectories)
280 .FirstOrDefault();
281 if (certFile == null)
282 {
283 if (maxCount == 0)
284 throw new FileNotFoundException("Cannot find file in directories");
285 return GetCertPath(di.Parent, maxCount - 1);
286 }
287
288 return certFile.FullName;
289 }
290
291 private static X509Certificate LocalCertificateSelectionCallback(object sender,
292 string targetHost, X509CertificateCollection localCertificates,
293 X509Certificate remoteCertificate, string[] acceptableIssuers)
294 {
295 return GetCertificate();
296 }
297
298 private static bool ClientCertValidator(object sender, X509Certificate certificate,
299 X509Chain chain, SslPolicyErrors sslPolicyErrors)
300 {
301 return true;
302 }
303
304 private enum Transport
305 {
306 Tcp,
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100307 NamedPipe,
308 Http,
309 TcpTls,
Kyle Smith7b94dd42019-03-23 17:26:56 +0100310 }
311
312 private enum Buffering
313 {
314 None,
315 Buffered,
316 Framed,
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100317 }
318
319 private enum Protocol
320 {
321 Binary,
322 Compact,
323 Json,
324 Multiplexed
325 }
326
327 public class HttpServerSample
328 {
329 public void Run(CancellationToken cancellationToken)
330 {
331 var config = new ConfigurationBuilder()
332 .AddEnvironmentVariables(prefix: "ASPNETCORE_")
333 .Build();
334
335 var host = new WebHostBuilder()
336 .UseConfiguration(config)
337 .UseKestrel()
338 .UseUrls("http://localhost:9090")
339 .UseContentRoot(Directory.GetCurrentDirectory())
340 .UseStartup<Startup>()
Jens Geyerb11f63c2019-03-14 21:12:38 +0100341 .ConfigureLogging((ctx,logging) => ConfigureLogging(logging))
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100342 .Build();
343
Jens Geyerb11f63c2019-03-14 21:12:38 +0100344 Logger.LogTrace("test");
345 Logger.LogCritical("test");
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100346 host.RunAsync(cancellationToken).GetAwaiter().GetResult();
347 }
348
349 public class Startup
350 {
Jens Geyerec439542019-11-01 19:19:44 +0100351 public Startup(IWebHostEnvironment env)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100352 {
353 var builder = new ConfigurationBuilder()
354 .SetBasePath(env.ContentRootPath)
355 .AddEnvironmentVariables();
356
357 Configuration = builder.Build();
358 }
359
360 public IConfigurationRoot Configuration { get; }
361
362 // This method gets called by the runtime. Use this method to add services to the container.
363 public void ConfigureServices(IServiceCollection services)
364 {
365 services.AddTransient<Calculator.IAsync, CalculatorAsyncHandler>();
366 services.AddTransient<ITAsyncProcessor, Calculator.AsyncProcessor>();
367 services.AddTransient<THttpServerTransport, THttpServerTransport>();
368 }
369
370 // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
Jens Geyerec439542019-11-01 19:19:44 +0100371 public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100372 {
373 app.UseMiddleware<THttpServerTransport>();
374 }
375 }
376 }
377
378 public class CalculatorAsyncHandler : Calculator.IAsync
379 {
380 private readonly Dictionary<int, SharedStruct> _log = new Dictionary<int, SharedStruct>();
381
382 public CalculatorAsyncHandler()
383 {
384 }
385
386 public async Task<SharedStruct> getStructAsync(int key,
387 CancellationToken cancellationToken)
388 {
389 Logger.LogInformation("GetStructAsync({0})", key);
390 return await Task.FromResult(_log[key]);
391 }
392
393 public async Task pingAsync(CancellationToken cancellationToken)
394 {
395 Logger.LogInformation("PingAsync()");
396 await Task.CompletedTask;
397 }
398
399 public async Task<int> addAsync(int num1, int num2, CancellationToken cancellationToken)
400 {
401 Logger.LogInformation($"AddAsync({num1},{num2})");
402 return await Task.FromResult(num1 + num2);
403 }
404
405 public async Task<int> calculateAsync(int logid, Work w, CancellationToken cancellationToken)
406 {
407 Logger.LogInformation($"CalculateAsync({logid}, [{w.Op},{w.Num1},{w.Num2}])");
408
409 var val = 0;
410 switch (w.Op)
411 {
412 case Operation.ADD:
413 val = w.Num1 + w.Num2;
414 break;
415
416 case Operation.SUBTRACT:
417 val = w.Num1 - w.Num2;
418 break;
419
420 case Operation.MULTIPLY:
421 val = w.Num1 * w.Num2;
422 break;
423
424 case Operation.DIVIDE:
425 if (w.Num2 == 0)
426 {
427 var io = new InvalidOperation
428 {
429 WhatOp = (int) w.Op,
430 Why = "Cannot divide by 0"
431 };
432
433 throw io;
434 }
435 val = w.Num1 / w.Num2;
436 break;
437
438 default:
439 {
440 var io = new InvalidOperation
441 {
442 WhatOp = (int) w.Op,
443 Why = "Unknown operation"
444 };
445
446 throw io;
447 }
448 }
449
450 var entry = new SharedStruct
451 {
452 Key = logid,
453 Value = val.ToString()
454 };
455
456 _log[logid] = entry;
457
458 return await Task.FromResult(val);
459 }
460
461 public async Task zipAsync(CancellationToken cancellationToken)
462 {
463 Logger.LogInformation("ZipAsync() with delay 100mc");
464 await Task.Delay(100, CancellationToken.None);
465 }
466 }
467
468 public class SharedServiceAsyncHandler : SharedService.IAsync
469 {
470 public async Task<SharedStruct> getStructAsync(int key, CancellationToken cancellationToken)
471 {
472 Logger.LogInformation("GetStructAsync({0})", key);
473 return await Task.FromResult(new SharedStruct()
474 {
475 Key = key,
476 Value = "GetStructAsync"
477 });
478 }
479 }
480 }
481}