blob: 3181e8e332a8d2dd13a3be1815db7020d9596cb0 [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));
Kengo Sekibee4f2f2019-12-29 17:04:50 +090054 using (var serviceProvider = ServiceCollection.BuildServiceProvider())
Jens Geyeraa0c8b32019-01-28 23:27:45 +010055 {
Kengo Sekibee4f2f2019-12-29 17:04:50 +090056 Logger = serviceProvider.GetService<ILoggerFactory>().CreateLogger(nameof(Server));
57
58 if (args.Any(x => x.StartsWith("-help", StringComparison.OrdinalIgnoreCase)))
59 {
60 DisplayHelp();
61 return;
62 }
63
64 using (var source = new CancellationTokenSource())
65 {
66 RunAsync(args, source.Token).GetAwaiter().GetResult();
67
68 Logger.LogInformation("Press any key to stop...");
69
70 Console.ReadLine();
71 source.Cancel();
72 }
73
74 Logger.LogInformation("Server stopped");
Jens Geyeraa0c8b32019-01-28 23:27:45 +010075 }
Jens Geyeraa0c8b32019-01-28 23:27:45 +010076 }
77
Jens Geyerb11f63c2019-03-14 21:12:38 +010078 private static void ConfigureLogging(ILoggingBuilder logging)
79 {
80 logging.SetMinimumLevel(LogLevel.Trace);
81 logging.AddConsole();
82 logging.AddDebug();
83 }
84
Jens Geyeraa0c8b32019-01-28 23:27:45 +010085 private static void DisplayHelp()
86 {
87 Logger.LogInformation(@"
88Usage:
Kengo Sekibee4f2f2019-12-29 17:04:50 +090089 Server -help
Jens Geyeraa0c8b32019-01-28 23:27:45 +010090 will diplay help information
91
Kengo Sekibee4f2f2019-12-29 17:04:50 +090092 Server -tr:<transport> -bf:<buffering> -pr:<protocol>
Kyle Smith7b94dd42019-03-23 17:26:56 +010093 will run server with specified arguments (tcp transport, no buffering, and binary protocol by default)
Jens Geyeraa0c8b32019-01-28 23:27:45 +010094
95Options:
96 -tr (transport):
97 tcp - (default) tcp transport will be used (host - ""localhost"", port - 9090)
Jens Geyeraa0c8b32019-01-28 23:27:45 +010098 namedpipe - namedpipe transport will be used (pipe address - "".test"")
99 http - http transport will be used (http address - ""localhost:9090"")
100 tcptls - tcp transport with tls will be used (host - ""localhost"", port - 9090)
Kyle Smith7b94dd42019-03-23 17:26:56 +0100101
102 -bf (buffering):
103 none - (default) no buffering will be used
104 buffered - buffered transport will be used
105 framed - framed transport will be used
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100106
107 -pr (protocol):
108 binary - (default) binary protocol will be used
109 compact - compact protocol will be used
110 json - json protocol will be used
111 multiplexed - multiplexed protocol will be used
112
113Sample:
Kengo Sekibee4f2f2019-12-29 17:04:50 +0900114 Server -tr:tcp
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100115");
116 }
117
118 private static async Task RunAsync(string[] args, CancellationToken cancellationToken)
119 {
120 var selectedTransport = GetTransport(args);
Kyle Smith7b94dd42019-03-23 17:26:56 +0100121 var selectedBuffering = GetBuffering(args);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100122 var selectedProtocol = GetProtocol(args);
123
124 if (selectedTransport == Transport.Http)
125 {
126 new HttpServerSample().Run(cancellationToken);
127 }
128 else
129 {
Kyle Smith7b94dd42019-03-23 17:26:56 +0100130 await RunSelectedConfigurationAsync(selectedTransport, selectedBuffering, selectedProtocol, cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100131 }
132 }
133
134 private static Protocol GetProtocol(string[] args)
135 {
136 var transport = args.FirstOrDefault(x => x.StartsWith("-pr"))?.Split(':')?[1];
137
138 Enum.TryParse(transport, true, out Protocol selectedProtocol);
139
140 return selectedProtocol;
141 }
142
Kyle Smith7b94dd42019-03-23 17:26:56 +0100143 private static Buffering GetBuffering(string[] args)
144 {
145 var buffering = args.FirstOrDefault(x => x.StartsWith("-bf"))?.Split(":")?[1];
146
147 Enum.TryParse<Buffering>(buffering, out var selectedBuffering);
148
149 return selectedBuffering;
150 }
151
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100152 private static Transport GetTransport(string[] args)
153 {
154 var transport = args.FirstOrDefault(x => x.StartsWith("-tr"))?.Split(':')?[1];
155
156 Enum.TryParse(transport, true, out Transport selectedTransport);
157
158 return selectedTransport;
159 }
160
Kyle Smith7b94dd42019-03-23 17:26:56 +0100161 private static async Task RunSelectedConfigurationAsync(Transport transport, Buffering buffering, Protocol protocol, CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100162 {
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100163 var handler = new CalculatorAsyncHandler();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100164
165 TServerTransport serverTransport = null;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100166 switch (transport)
167 {
168 case Transport.Tcp:
Jens Geyereacd1d42019-11-20 19:03:14 +0100169 serverTransport = new TServerSocketTransport(9090, Configuration);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100170 break;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100171 case Transport.NamedPipe:
Jens Geyereacd1d42019-11-20 19:03:14 +0100172 serverTransport = new TNamedPipeServerTransport(".test", Configuration);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100173 break;
174 case Transport.TcpTls:
Jens Geyereacd1d42019-11-20 19:03:14 +0100175 serverTransport = new TTlsServerSocketTransport(9090, Configuration,
176 GetCertificate(), ClientCertValidator, LocalCertificateSelectionCallback);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100177 break;
178 }
179
Kyle Smith7b94dd42019-03-23 17:26:56 +0100180 TTransportFactory inputTransportFactory = null;
181 TTransportFactory outputTransportFactory = null;
182 switch (buffering)
183 {
184 case Buffering.Buffered:
185 inputTransportFactory = new TBufferedTransport.Factory();
186 outputTransportFactory = new TBufferedTransport.Factory();
187 break;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100188
Kyle Smith7b94dd42019-03-23 17:26:56 +0100189 case Buffering.Framed:
190 inputTransportFactory = new TFramedTransport.Factory();
191 outputTransportFactory = new TFramedTransport.Factory();
192 break;
193
194 default: // layered transport(s) are optional
195 Debug.Assert(buffering == Buffering.None, "unhandled case");
196 break;
197 }
198
199 TProtocolFactory inputProtocolFactory = null;
200 TProtocolFactory outputProtocolFactory = null;
201 ITAsyncProcessor processor = null;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100202 switch (protocol)
203 {
204 case Protocol.Binary:
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100205 inputProtocolFactory = new TBinaryProtocol.Factory();
206 outputProtocolFactory = new TBinaryProtocol.Factory();
207 processor = new Calculator.AsyncProcessor(handler);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100208 break;
Jens Geyer421444f2019-03-20 22:13:25 +0100209
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100210 case Protocol.Compact:
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100211 inputProtocolFactory = new TCompactProtocol.Factory();
212 outputProtocolFactory = new TCompactProtocol.Factory();
213 processor = new Calculator.AsyncProcessor(handler);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100214 break;
Jens Geyer421444f2019-03-20 22:13:25 +0100215
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100216 case Protocol.Json:
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100217 inputProtocolFactory = new TJsonProtocol.Factory();
218 outputProtocolFactory = new TJsonProtocol.Factory();
219 processor = new Calculator.AsyncProcessor(handler);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100220 break;
Jens Geyer421444f2019-03-20 22:13:25 +0100221
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100222 case Protocol.Multiplexed:
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100223 inputProtocolFactory = new TBinaryProtocol.Factory();
224 outputProtocolFactory = new TBinaryProtocol.Factory();
225
226 var calcHandler = new CalculatorAsyncHandler();
227 var calcProcessor = new Calculator.AsyncProcessor(calcHandler);
228
229 var sharedServiceHandler = new SharedServiceAsyncHandler();
230 var sharedServiceProcessor = new SharedService.AsyncProcessor(sharedServiceHandler);
231
232 var multiplexedProcessor = new TMultiplexedProcessor();
233 multiplexedProcessor.RegisterProcessor(nameof(Calculator), calcProcessor);
234 multiplexedProcessor.RegisterProcessor(nameof(SharedService), sharedServiceProcessor);
235
236 processor = multiplexedProcessor;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100237 break;
Jens Geyer421444f2019-03-20 22:13:25 +0100238
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100239 default:
240 throw new ArgumentOutOfRangeException(nameof(protocol), protocol, null);
241 }
242
Kyle Smith7b94dd42019-03-23 17:26:56 +0100243
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100244 try
245 {
246 Logger.LogInformation(
247 $"Selected TAsyncServer with {serverTransport} transport, {processor} processor and {inputProtocolFactory} protocol factories");
248
Kyle Smith7b94dd42019-03-23 17:26:56 +0100249 var loggerFactory = ServiceCollection.BuildServiceProvider().GetService<ILoggerFactory>();
250
251 var server = new TSimpleAsyncServer(
252 itProcessorFactory: new TSingletonProcessorFactory(processor),
253 serverTransport: serverTransport,
254 inputTransportFactory: inputTransportFactory,
255 outputTransportFactory: outputTransportFactory,
256 inputProtocolFactory: inputProtocolFactory,
257 outputProtocolFactory: outputProtocolFactory,
258 logger: loggerFactory.CreateLogger<TSimpleAsyncServer>());
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100259
260 Logger.LogInformation("Starting the server...");
Kyle Smith7b94dd42019-03-23 17:26:56 +0100261
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100262 await server.ServeAsync(cancellationToken);
263 }
264 catch (Exception x)
265 {
266 Logger.LogInformation(x.ToString());
267 }
268 }
269
270 private static X509Certificate2 GetCertificate()
271 {
272 // due to files location in net core better to take certs from top folder
273 var certFile = GetCertPath(Directory.GetParent(Directory.GetCurrentDirectory()));
274 return new X509Certificate2(certFile, "ThriftTest");
275 }
276
277 private static string GetCertPath(DirectoryInfo di, int maxCount = 6)
278 {
279 var topDir = di;
280 var certFile =
281 topDir.EnumerateFiles("ThriftTest.pfx", SearchOption.AllDirectories)
282 .FirstOrDefault();
283 if (certFile == null)
284 {
285 if (maxCount == 0)
286 throw new FileNotFoundException("Cannot find file in directories");
287 return GetCertPath(di.Parent, maxCount - 1);
288 }
289
290 return certFile.FullName;
291 }
292
293 private static X509Certificate LocalCertificateSelectionCallback(object sender,
294 string targetHost, X509CertificateCollection localCertificates,
295 X509Certificate remoteCertificate, string[] acceptableIssuers)
296 {
297 return GetCertificate();
298 }
299
300 private static bool ClientCertValidator(object sender, X509Certificate certificate,
301 X509Chain chain, SslPolicyErrors sslPolicyErrors)
302 {
303 return true;
304 }
305
306 private enum Transport
307 {
308 Tcp,
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100309 NamedPipe,
310 Http,
311 TcpTls,
Kyle Smith7b94dd42019-03-23 17:26:56 +0100312 }
313
314 private enum Buffering
315 {
316 None,
317 Buffered,
318 Framed,
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100319 }
320
321 private enum Protocol
322 {
323 Binary,
324 Compact,
325 Json,
326 Multiplexed
327 }
328
329 public class HttpServerSample
330 {
331 public void Run(CancellationToken cancellationToken)
332 {
333 var config = new ConfigurationBuilder()
334 .AddEnvironmentVariables(prefix: "ASPNETCORE_")
335 .Build();
336
337 var host = new WebHostBuilder()
338 .UseConfiguration(config)
339 .UseKestrel()
340 .UseUrls("http://localhost:9090")
341 .UseContentRoot(Directory.GetCurrentDirectory())
342 .UseStartup<Startup>()
Jens Geyerb11f63c2019-03-14 21:12:38 +0100343 .ConfigureLogging((ctx,logging) => ConfigureLogging(logging))
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100344 .Build();
345
Jens Geyerb11f63c2019-03-14 21:12:38 +0100346 Logger.LogTrace("test");
347 Logger.LogCritical("test");
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100348 host.RunAsync(cancellationToken).GetAwaiter().GetResult();
349 }
350
351 public class Startup
352 {
Jens Geyerec439542019-11-01 19:19:44 +0100353 public Startup(IWebHostEnvironment env)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100354 {
355 var builder = new ConfigurationBuilder()
356 .SetBasePath(env.ContentRootPath)
357 .AddEnvironmentVariables();
358
359 Configuration = builder.Build();
360 }
361
362 public IConfigurationRoot Configuration { get; }
363
364 // This method gets called by the runtime. Use this method to add services to the container.
365 public void ConfigureServices(IServiceCollection services)
366 {
367 services.AddTransient<Calculator.IAsync, CalculatorAsyncHandler>();
368 services.AddTransient<ITAsyncProcessor, Calculator.AsyncProcessor>();
369 services.AddTransient<THttpServerTransport, THttpServerTransport>();
370 }
371
372 // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
Jens Geyerec439542019-11-01 19:19:44 +0100373 public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100374 {
375 app.UseMiddleware<THttpServerTransport>();
376 }
377 }
378 }
379
380 public class CalculatorAsyncHandler : Calculator.IAsync
381 {
382 private readonly Dictionary<int, SharedStruct> _log = new Dictionary<int, SharedStruct>();
383
384 public CalculatorAsyncHandler()
385 {
386 }
387
388 public async Task<SharedStruct> getStructAsync(int key,
389 CancellationToken cancellationToken)
390 {
391 Logger.LogInformation("GetStructAsync({0})", key);
392 return await Task.FromResult(_log[key]);
393 }
394
395 public async Task pingAsync(CancellationToken cancellationToken)
396 {
397 Logger.LogInformation("PingAsync()");
398 await Task.CompletedTask;
399 }
400
401 public async Task<int> addAsync(int num1, int num2, CancellationToken cancellationToken)
402 {
403 Logger.LogInformation($"AddAsync({num1},{num2})");
404 return await Task.FromResult(num1 + num2);
405 }
406
407 public async Task<int> calculateAsync(int logid, Work w, CancellationToken cancellationToken)
408 {
409 Logger.LogInformation($"CalculateAsync({logid}, [{w.Op},{w.Num1},{w.Num2}])");
410
411 var val = 0;
412 switch (w.Op)
413 {
414 case Operation.ADD:
415 val = w.Num1 + w.Num2;
416 break;
417
418 case Operation.SUBTRACT:
419 val = w.Num1 - w.Num2;
420 break;
421
422 case Operation.MULTIPLY:
423 val = w.Num1 * w.Num2;
424 break;
425
426 case Operation.DIVIDE:
427 if (w.Num2 == 0)
428 {
429 var io = new InvalidOperation
430 {
431 WhatOp = (int) w.Op,
432 Why = "Cannot divide by 0"
433 };
434
435 throw io;
436 }
437 val = w.Num1 / w.Num2;
438 break;
439
440 default:
441 {
442 var io = new InvalidOperation
443 {
444 WhatOp = (int) w.Op,
445 Why = "Unknown operation"
446 };
447
448 throw io;
449 }
450 }
451
452 var entry = new SharedStruct
453 {
454 Key = logid,
455 Value = val.ToString()
456 };
457
458 _log[logid] = entry;
459
460 return await Task.FromResult(val);
461 }
462
463 public async Task zipAsync(CancellationToken cancellationToken)
464 {
465 Logger.LogInformation("ZipAsync() with delay 100mc");
466 await Task.Delay(100, CancellationToken.None);
467 }
468 }
469
470 public class SharedServiceAsyncHandler : SharedService.IAsync
471 {
472 public async Task<SharedStruct> getStructAsync(int key, CancellationToken cancellationToken)
473 {
474 Logger.LogInformation("GetStructAsync({0})", key);
475 return await Task.FromResult(new SharedStruct()
476 {
477 Key = key,
478 Value = "GetStructAsync"
479 });
480 }
481 }
482 }
483}