blob: 25e7daeedaa14646233250eeec0be82d95294342 [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 Geyeraa0c8b32019-01-28 23:27:45 +010047
48 public static void Main(string[] args)
49 {
50 args = args ?? new string[0];
51
Jens Geyerb11f63c2019-03-14 21:12:38 +010052 ServiceCollection.AddLogging(logging => ConfigureLogging(logging));
53 Logger = ServiceCollection.BuildServiceProvider().GetService<ILoggerFactory>().CreateLogger(nameof(Server));
54
55
Jens Geyeraa0c8b32019-01-28 23:27:45 +010056 if (args.Any(x => x.StartsWith("-help", StringComparison.OrdinalIgnoreCase)))
57 {
58 DisplayHelp();
59 return;
60 }
61
62 using (var source = new CancellationTokenSource())
63 {
64 RunAsync(args, source.Token).GetAwaiter().GetResult();
65
66 Logger.LogInformation("Press any key to stop...");
67
68 Console.ReadLine();
69 source.Cancel();
70 }
71
72 Logger.LogInformation("Server stopped");
73 }
74
Jens Geyerb11f63c2019-03-14 21:12:38 +010075 private static void ConfigureLogging(ILoggingBuilder logging)
76 {
77 logging.SetMinimumLevel(LogLevel.Trace);
78 logging.AddConsole();
79 logging.AddDebug();
80 }
81
Jens Geyeraa0c8b32019-01-28 23:27:45 +010082 private static void DisplayHelp()
83 {
84 Logger.LogInformation(@"
85Usage:
86 Server.exe -help
87 will diplay help information
88
Kyle Smith7b94dd42019-03-23 17:26:56 +010089 Server.exe -tr:<transport> -bf:<buffering> -pr:<protocol>
90 will run server with specified arguments (tcp transport, no buffering, and binary protocol by default)
Jens Geyeraa0c8b32019-01-28 23:27:45 +010091
92Options:
93 -tr (transport):
94 tcp - (default) tcp transport will be used (host - ""localhost"", port - 9090)
Jens Geyeraa0c8b32019-01-28 23:27:45 +010095 namedpipe - namedpipe transport will be used (pipe address - "".test"")
96 http - http transport will be used (http address - ""localhost:9090"")
97 tcptls - tcp transport with tls will be used (host - ""localhost"", port - 9090)
Kyle Smith7b94dd42019-03-23 17:26:56 +010098
99 -bf (buffering):
100 none - (default) no buffering will be used
101 buffered - buffered transport will be used
102 framed - framed transport will be used
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100103
104 -pr (protocol):
105 binary - (default) binary protocol will be used
106 compact - compact protocol will be used
107 json - json protocol will be used
108 multiplexed - multiplexed protocol will be used
109
110Sample:
111 Server.exe -tr:tcp
112");
113 }
114
115 private static async Task RunAsync(string[] args, CancellationToken cancellationToken)
116 {
117 var selectedTransport = GetTransport(args);
Kyle Smith7b94dd42019-03-23 17:26:56 +0100118 var selectedBuffering = GetBuffering(args);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100119 var selectedProtocol = GetProtocol(args);
120
121 if (selectedTransport == Transport.Http)
122 {
123 new HttpServerSample().Run(cancellationToken);
124 }
125 else
126 {
Kyle Smith7b94dd42019-03-23 17:26:56 +0100127 await RunSelectedConfigurationAsync(selectedTransport, selectedBuffering, selectedProtocol, cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100128 }
129 }
130
131 private static Protocol GetProtocol(string[] args)
132 {
133 var transport = args.FirstOrDefault(x => x.StartsWith("-pr"))?.Split(':')?[1];
134
135 Enum.TryParse(transport, true, out Protocol selectedProtocol);
136
137 return selectedProtocol;
138 }
139
Kyle Smith7b94dd42019-03-23 17:26:56 +0100140 private static Buffering GetBuffering(string[] args)
141 {
142 var buffering = args.FirstOrDefault(x => x.StartsWith("-bf"))?.Split(":")?[1];
143
144 Enum.TryParse<Buffering>(buffering, out var selectedBuffering);
145
146 return selectedBuffering;
147 }
148
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100149 private static Transport GetTransport(string[] args)
150 {
151 var transport = args.FirstOrDefault(x => x.StartsWith("-tr"))?.Split(':')?[1];
152
153 Enum.TryParse(transport, true, out Transport selectedTransport);
154
155 return selectedTransport;
156 }
157
Kyle Smith7b94dd42019-03-23 17:26:56 +0100158 private static async Task RunSelectedConfigurationAsync(Transport transport, Buffering buffering, Protocol protocol, CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100159 {
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100160 var handler = new CalculatorAsyncHandler();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100161
162 TServerTransport serverTransport = null;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100163 switch (transport)
164 {
165 case Transport.Tcp:
166 serverTransport = new TServerSocketTransport(9090);
167 break;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100168 case Transport.NamedPipe:
169 serverTransport = new TNamedPipeServerTransport(".test");
170 break;
171 case Transport.TcpTls:
Kyle Smith7b94dd42019-03-23 17:26:56 +0100172 serverTransport = new TTlsServerSocketTransport(9090, GetCertificate(), ClientCertValidator, LocalCertificateSelectionCallback);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100173 break;
174 }
175
Kyle Smith7b94dd42019-03-23 17:26:56 +0100176 TTransportFactory inputTransportFactory = null;
177 TTransportFactory outputTransportFactory = null;
178 switch (buffering)
179 {
180 case Buffering.Buffered:
181 inputTransportFactory = new TBufferedTransport.Factory();
182 outputTransportFactory = new TBufferedTransport.Factory();
183 break;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100184
Kyle Smith7b94dd42019-03-23 17:26:56 +0100185 case Buffering.Framed:
186 inputTransportFactory = new TFramedTransport.Factory();
187 outputTransportFactory = new TFramedTransport.Factory();
188 break;
189
190 default: // layered transport(s) are optional
191 Debug.Assert(buffering == Buffering.None, "unhandled case");
192 break;
193 }
194
195 TProtocolFactory inputProtocolFactory = null;
196 TProtocolFactory outputProtocolFactory = null;
197 ITAsyncProcessor processor = null;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100198 switch (protocol)
199 {
200 case Protocol.Binary:
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100201 inputProtocolFactory = new TBinaryProtocol.Factory();
202 outputProtocolFactory = new TBinaryProtocol.Factory();
203 processor = new Calculator.AsyncProcessor(handler);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100204 break;
Jens Geyer421444f2019-03-20 22:13:25 +0100205
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100206 case Protocol.Compact:
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100207 inputProtocolFactory = new TCompactProtocol.Factory();
208 outputProtocolFactory = new TCompactProtocol.Factory();
209 processor = new Calculator.AsyncProcessor(handler);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100210 break;
Jens Geyer421444f2019-03-20 22:13:25 +0100211
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100212 case Protocol.Json:
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100213 inputProtocolFactory = new TJsonProtocol.Factory();
214 outputProtocolFactory = new TJsonProtocol.Factory();
215 processor = new Calculator.AsyncProcessor(handler);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100216 break;
Jens Geyer421444f2019-03-20 22:13:25 +0100217
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100218 case Protocol.Multiplexed:
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100219 inputProtocolFactory = new TBinaryProtocol.Factory();
220 outputProtocolFactory = new TBinaryProtocol.Factory();
221
222 var calcHandler = new CalculatorAsyncHandler();
223 var calcProcessor = new Calculator.AsyncProcessor(calcHandler);
224
225 var sharedServiceHandler = new SharedServiceAsyncHandler();
226 var sharedServiceProcessor = new SharedService.AsyncProcessor(sharedServiceHandler);
227
228 var multiplexedProcessor = new TMultiplexedProcessor();
229 multiplexedProcessor.RegisterProcessor(nameof(Calculator), calcProcessor);
230 multiplexedProcessor.RegisterProcessor(nameof(SharedService), sharedServiceProcessor);
231
232 processor = multiplexedProcessor;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100233 break;
Jens Geyer421444f2019-03-20 22:13:25 +0100234
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100235 default:
236 throw new ArgumentOutOfRangeException(nameof(protocol), protocol, null);
237 }
238
Kyle Smith7b94dd42019-03-23 17:26:56 +0100239
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100240 try
241 {
242 Logger.LogInformation(
243 $"Selected TAsyncServer with {serverTransport} transport, {processor} processor and {inputProtocolFactory} protocol factories");
244
Kyle Smith7b94dd42019-03-23 17:26:56 +0100245 var loggerFactory = ServiceCollection.BuildServiceProvider().GetService<ILoggerFactory>();
246
247 var server = new TSimpleAsyncServer(
248 itProcessorFactory: new TSingletonProcessorFactory(processor),
249 serverTransport: serverTransport,
250 inputTransportFactory: inputTransportFactory,
251 outputTransportFactory: outputTransportFactory,
252 inputProtocolFactory: inputProtocolFactory,
253 outputProtocolFactory: outputProtocolFactory,
254 logger: loggerFactory.CreateLogger<TSimpleAsyncServer>());
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100255
256 Logger.LogInformation("Starting the server...");
Kyle Smith7b94dd42019-03-23 17:26:56 +0100257
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100258 await server.ServeAsync(cancellationToken);
259 }
260 catch (Exception x)
261 {
262 Logger.LogInformation(x.ToString());
263 }
264 }
265
266 private static X509Certificate2 GetCertificate()
267 {
268 // due to files location in net core better to take certs from top folder
269 var certFile = GetCertPath(Directory.GetParent(Directory.GetCurrentDirectory()));
270 return new X509Certificate2(certFile, "ThriftTest");
271 }
272
273 private static string GetCertPath(DirectoryInfo di, int maxCount = 6)
274 {
275 var topDir = di;
276 var certFile =
277 topDir.EnumerateFiles("ThriftTest.pfx", SearchOption.AllDirectories)
278 .FirstOrDefault();
279 if (certFile == null)
280 {
281 if (maxCount == 0)
282 throw new FileNotFoundException("Cannot find file in directories");
283 return GetCertPath(di.Parent, maxCount - 1);
284 }
285
286 return certFile.FullName;
287 }
288
289 private static X509Certificate LocalCertificateSelectionCallback(object sender,
290 string targetHost, X509CertificateCollection localCertificates,
291 X509Certificate remoteCertificate, string[] acceptableIssuers)
292 {
293 return GetCertificate();
294 }
295
296 private static bool ClientCertValidator(object sender, X509Certificate certificate,
297 X509Chain chain, SslPolicyErrors sslPolicyErrors)
298 {
299 return true;
300 }
301
302 private enum Transport
303 {
304 Tcp,
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100305 NamedPipe,
306 Http,
307 TcpTls,
Kyle Smith7b94dd42019-03-23 17:26:56 +0100308 }
309
310 private enum Buffering
311 {
312 None,
313 Buffered,
314 Framed,
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100315 }
316
317 private enum Protocol
318 {
319 Binary,
320 Compact,
321 Json,
322 Multiplexed
323 }
324
325 public class HttpServerSample
326 {
327 public void Run(CancellationToken cancellationToken)
328 {
329 var config = new ConfigurationBuilder()
330 .AddEnvironmentVariables(prefix: "ASPNETCORE_")
331 .Build();
332
333 var host = new WebHostBuilder()
334 .UseConfiguration(config)
335 .UseKestrel()
336 .UseUrls("http://localhost:9090")
337 .UseContentRoot(Directory.GetCurrentDirectory())
338 .UseStartup<Startup>()
Jens Geyerb11f63c2019-03-14 21:12:38 +0100339 .ConfigureLogging((ctx,logging) => ConfigureLogging(logging))
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100340 .Build();
341
Jens Geyerb11f63c2019-03-14 21:12:38 +0100342 Logger.LogTrace("test");
343 Logger.LogCritical("test");
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100344 host.RunAsync(cancellationToken).GetAwaiter().GetResult();
345 }
346
347 public class Startup
348 {
349 public Startup(IHostingEnvironment env)
350 {
351 var builder = new ConfigurationBuilder()
352 .SetBasePath(env.ContentRootPath)
353 .AddEnvironmentVariables();
354
355 Configuration = builder.Build();
356 }
357
358 public IConfigurationRoot Configuration { get; }
359
360 // This method gets called by the runtime. Use this method to add services to the container.
361 public void ConfigureServices(IServiceCollection services)
362 {
363 services.AddTransient<Calculator.IAsync, CalculatorAsyncHandler>();
364 services.AddTransient<ITAsyncProcessor, Calculator.AsyncProcessor>();
365 services.AddTransient<THttpServerTransport, THttpServerTransport>();
366 }
367
368 // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
Jens Geyerb11f63c2019-03-14 21:12:38 +0100369 public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100370 {
371 app.UseMiddleware<THttpServerTransport>();
372 }
373 }
374 }
375
376 public class CalculatorAsyncHandler : Calculator.IAsync
377 {
378 private readonly Dictionary<int, SharedStruct> _log = new Dictionary<int, SharedStruct>();
379
380 public CalculatorAsyncHandler()
381 {
382 }
383
384 public async Task<SharedStruct> getStructAsync(int key,
385 CancellationToken cancellationToken)
386 {
387 Logger.LogInformation("GetStructAsync({0})", key);
388 return await Task.FromResult(_log[key]);
389 }
390
391 public async Task pingAsync(CancellationToken cancellationToken)
392 {
393 Logger.LogInformation("PingAsync()");
394 await Task.CompletedTask;
395 }
396
397 public async Task<int> addAsync(int num1, int num2, CancellationToken cancellationToken)
398 {
399 Logger.LogInformation($"AddAsync({num1},{num2})");
400 return await Task.FromResult(num1 + num2);
401 }
402
403 public async Task<int> calculateAsync(int logid, Work w, CancellationToken cancellationToken)
404 {
405 Logger.LogInformation($"CalculateAsync({logid}, [{w.Op},{w.Num1},{w.Num2}])");
406
407 var val = 0;
408 switch (w.Op)
409 {
410 case Operation.ADD:
411 val = w.Num1 + w.Num2;
412 break;
413
414 case Operation.SUBTRACT:
415 val = w.Num1 - w.Num2;
416 break;
417
418 case Operation.MULTIPLY:
419 val = w.Num1 * w.Num2;
420 break;
421
422 case Operation.DIVIDE:
423 if (w.Num2 == 0)
424 {
425 var io = new InvalidOperation
426 {
427 WhatOp = (int) w.Op,
428 Why = "Cannot divide by 0"
429 };
430
431 throw io;
432 }
433 val = w.Num1 / w.Num2;
434 break;
435
436 default:
437 {
438 var io = new InvalidOperation
439 {
440 WhatOp = (int) w.Op,
441 Why = "Unknown operation"
442 };
443
444 throw io;
445 }
446 }
447
448 var entry = new SharedStruct
449 {
450 Key = logid,
451 Value = val.ToString()
452 };
453
454 _log[logid] = entry;
455
456 return await Task.FromResult(val);
457 }
458
459 public async Task zipAsync(CancellationToken cancellationToken)
460 {
461 Logger.LogInformation("ZipAsync() with delay 100mc");
462 await Task.Delay(100, CancellationToken.None);
463 }
464 }
465
466 public class SharedServiceAsyncHandler : SharedService.IAsync
467 {
468 public async Task<SharedStruct> getStructAsync(int key, CancellationToken cancellationToken)
469 {
470 Logger.LogInformation("GetStructAsync({0})", key);
471 return await Task.FromResult(new SharedStruct()
472 {
473 Key = key,
474 Value = "GetStructAsync"
475 });
476 }
477 }
478 }
479}