blob: 80205d54fc153bee962494b00213eaf6a9b9d3b9 [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
Jens Geyer0d128322021-02-25 09:42:52 +010041#pragma warning disable IDE0063 // using
42#pragma warning disable IDE0057 // substr
43
Jens Geyeraa0c8b32019-01-28 23:27:45 +010044namespace Server
45{
46 public class Program
47 {
Jens Geyer2b2ea622021-04-09 22:55:11 +020048 private static readonly ServiceCollection ServiceCollection = new();
Jens Geyerb11f63c2019-03-14 21:12:38 +010049 private static ILogger Logger;
Jens Geyereacd1d42019-11-20 19:03:14 +010050 private static readonly TConfiguration Configuration = null; // new TConfiguration() if needed
Jens Geyeraa0c8b32019-01-28 23:27:45 +010051
52 public static void Main(string[] args)
53 {
Jens Geyer0d128322021-02-25 09:42:52 +010054 args ??= Array.Empty<string>();
Jens Geyeraa0c8b32019-01-28 23:27:45 +010055
Jens Geyerb11f63c2019-03-14 21:12:38 +010056 ServiceCollection.AddLogging(logging => ConfigureLogging(logging));
Kengo Sekibee4f2f2019-12-29 17:04:50 +090057 using (var serviceProvider = ServiceCollection.BuildServiceProvider())
Jens Geyeraa0c8b32019-01-28 23:27:45 +010058 {
Kengo Sekibee4f2f2019-12-29 17:04:50 +090059 Logger = serviceProvider.GetService<ILoggerFactory>().CreateLogger(nameof(Server));
60
61 if (args.Any(x => x.StartsWith("-help", StringComparison.OrdinalIgnoreCase)))
62 {
63 DisplayHelp();
64 return;
65 }
66
67 using (var source = new CancellationTokenSource())
68 {
69 RunAsync(args, source.Token).GetAwaiter().GetResult();
70
71 Logger.LogInformation("Press any key to stop...");
72
73 Console.ReadLine();
74 source.Cancel();
75 }
76
77 Logger.LogInformation("Server stopped");
Jens Geyeraa0c8b32019-01-28 23:27:45 +010078 }
Jens Geyeraa0c8b32019-01-28 23:27:45 +010079 }
80
Jens Geyerb11f63c2019-03-14 21:12:38 +010081 private static void ConfigureLogging(ILoggingBuilder logging)
82 {
83 logging.SetMinimumLevel(LogLevel.Trace);
84 logging.AddConsole();
85 logging.AddDebug();
86 }
87
Jens Geyeraa0c8b32019-01-28 23:27:45 +010088 private static void DisplayHelp()
89 {
90 Logger.LogInformation(@"
91Usage:
Kengo Sekibee4f2f2019-12-29 17:04:50 +090092 Server -help
Jens Geyeraa0c8b32019-01-28 23:27:45 +010093 will diplay help information
94
Jens Geyer0d128322021-02-25 09:42:52 +010095 Server -tr:<transport> -bf:<buffering> -pr:<protocol> [-multiplex]
Kyle Smith7b94dd42019-03-23 17:26:56 +010096 will run server with specified arguments (tcp transport, no buffering, and binary protocol by default)
Jens Geyeraa0c8b32019-01-28 23:27:45 +010097
98Options:
99 -tr (transport):
Jens Geyer0d128322021-02-25 09:42:52 +0100100 tcp - (default) tcp transport (localhost:9090)
101 tcptls - tcp transport with tls (localhost:9090)
102 namedpipe - namedpipe transport (pipe "".test"")
103 http - http transport (localhost:9090)
Kyle Smith7b94dd42019-03-23 17:26:56 +0100104
105 -bf (buffering):
Jens Geyer0d128322021-02-25 09:42:52 +0100106 none - (default) no buffering
107 buffered - buffered transport
108 framed - framed transport
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100109
110 -pr (protocol):
Jens Geyer0d128322021-02-25 09:42:52 +0100111 binary - (default) binary protocol
112 compact - compact protocol
113 json - json protocol
114
115 -multiplex - adds multiplexed protocol
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100116
117Sample:
Kengo Sekibee4f2f2019-12-29 17:04:50 +0900118 Server -tr:tcp
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100119");
120 }
121
122 private static async Task RunAsync(string[] args, CancellationToken cancellationToken)
123 {
124 var selectedTransport = GetTransport(args);
Kyle Smith7b94dd42019-03-23 17:26:56 +0100125 var selectedBuffering = GetBuffering(args);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100126 var selectedProtocol = GetProtocol(args);
Jens Geyer0d128322021-02-25 09:42:52 +0100127 var multiplex = GetMultiplex(args);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100128
129 if (selectedTransport == Transport.Http)
130 {
Jens Geyer0d128322021-02-25 09:42:52 +0100131 if (multiplex)
132 throw new Exception("This tutorial semple code does not yet allow multiplex over http (although Thrift itself of course does)");
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100133 new HttpServerSample().Run(cancellationToken);
134 }
135 else
136 {
Jens Geyer0d128322021-02-25 09:42:52 +0100137 await RunSelectedConfigurationAsync(selectedTransport, selectedBuffering, selectedProtocol, multiplex, cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100138 }
139 }
140
Jens Geyer0d128322021-02-25 09:42:52 +0100141
142 private static bool GetMultiplex(string[] args)
143 {
144 var mplex = args.FirstOrDefault(x => x.StartsWith("-multiplex"));
145 return !string.IsNullOrEmpty(mplex);
146 }
147
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100148 private static Protocol GetProtocol(string[] args)
149 {
Jens Geyer0d128322021-02-25 09:42:52 +0100150 var protocol = args.FirstOrDefault(x => x.StartsWith("-pr"))?.Split(':')?[1];
151 if (string.IsNullOrEmpty(protocol))
152 return Protocol.Binary;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100153
Jens Geyer0d128322021-02-25 09:42:52 +0100154 protocol = protocol.Substring(0, 1).ToUpperInvariant() + protocol.Substring(1).ToLowerInvariant();
155 if (Enum.TryParse(protocol, true, out Protocol selectedProtocol))
156 return selectedProtocol;
157 else
158 return Protocol.Binary;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100159 }
160
Kyle Smith7b94dd42019-03-23 17:26:56 +0100161 private static Buffering GetBuffering(string[] args)
162 {
163 var buffering = args.FirstOrDefault(x => x.StartsWith("-bf"))?.Split(":")?[1];
Jens Geyer0d128322021-02-25 09:42:52 +0100164 if (string.IsNullOrEmpty(buffering))
165 return Buffering.None;
Kyle Smith7b94dd42019-03-23 17:26:56 +0100166
Jens Geyer0d128322021-02-25 09:42:52 +0100167 buffering = buffering.Substring(0, 1).ToUpperInvariant() + buffering.Substring(1).ToLowerInvariant();
168 if( Enum.TryParse<Buffering>(buffering, out var selectedBuffering))
169 return selectedBuffering;
170 else
171 return Buffering.None;
Kyle Smith7b94dd42019-03-23 17:26:56 +0100172 }
173
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100174 private static Transport GetTransport(string[] args)
175 {
176 var transport = args.FirstOrDefault(x => x.StartsWith("-tr"))?.Split(':')?[1];
Jens Geyer0d128322021-02-25 09:42:52 +0100177 if (string.IsNullOrEmpty(transport))
178 return Transport.Tcp;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100179
Jens Geyer0d128322021-02-25 09:42:52 +0100180 transport = transport.Substring(0, 1).ToUpperInvariant() + transport.Substring(1).ToLowerInvariant();
181 if( Enum.TryParse(transport, true, out Transport selectedTransport))
182 return selectedTransport;
183 else
184 return Transport.Tcp;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100185 }
186
Jens Geyer0d128322021-02-25 09:42:52 +0100187 private static async Task RunSelectedConfigurationAsync(Transport transport, Buffering buffering, Protocol protocol, bool multiplex, CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100188 {
Jens Geyer2b2ea622021-04-09 22:55:11 +0200189 TServerTransport serverTransport = transport switch
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100190 {
Jens Geyer2b2ea622021-04-09 22:55:11 +0200191 Transport.Tcp => new TServerSocketTransport(9090, Configuration),
192 Transport.NamedPipe => new TNamedPipeServerTransport(".test", Configuration, NamedPipeClientFlags.None),
193 Transport.TcpTls => new TTlsServerSocketTransport(9090, Configuration, GetCertificate(), ClientCertValidator, LocalCertificateSelectionCallback),
194 _ => throw new ArgumentException("unsupported value $transport", nameof(transport)),
195 };
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100196
Jens Geyer2b2ea622021-04-09 22:55:11 +0200197 TTransportFactory transportFactory = buffering switch
Kyle Smith7b94dd42019-03-23 17:26:56 +0100198 {
Jens Geyer2b2ea622021-04-09 22:55:11 +0200199 Buffering.Buffered => new TBufferedTransport.Factory(),
200 Buffering.Framed => new TFramedTransport.Factory(),
201 // layered transport(s) are optional
202 Buffering.None => null,
203 _ => throw new ArgumentException("unsupported value $buffering", nameof(buffering)),
204 };
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100205
Jens Geyer2b2ea622021-04-09 22:55:11 +0200206 TProtocolFactory protocolFactory = protocol switch
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100207 {
Jens Geyer2b2ea622021-04-09 22:55:11 +0200208 Protocol.Binary => new TBinaryProtocol.Factory(),
209 Protocol.Compact => new TCompactProtocol.Factory(),
210 Protocol.Json => new TJsonProtocol.Factory(),
211 _ => throw new ArgumentException("unsupported value $protocol", nameof(protocol)),
212 };
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100213
Jens Geyer0d128322021-02-25 09:42:52 +0100214 var handler = new CalculatorAsyncHandler();
215 ITAsyncProcessor processor = new Calculator.AsyncProcessor(handler);
216
217 if (multiplex)
218 {
219 var multiplexedProcessor = new TMultiplexedProcessor();
220 multiplexedProcessor.RegisterProcessor(nameof(Calculator), processor);
221
222 processor = multiplexedProcessor;
223 }
224
Kyle Smith7b94dd42019-03-23 17:26:56 +0100225
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100226 try
227 {
228 Logger.LogInformation(
Jens Geyer0d128322021-02-25 09:42:52 +0100229 string.Format(
230 "TSimpleAsyncServer with \n{0} transport\n{1} buffering\nmultiplex = {2}\n{3} protocol",
231 transport,
232 buffering,
233 multiplex ? "yes" : "no",
234 protocol
235 ));
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100236
Kyle Smith7b94dd42019-03-23 17:26:56 +0100237 var loggerFactory = ServiceCollection.BuildServiceProvider().GetService<ILoggerFactory>();
238
239 var server = new TSimpleAsyncServer(
240 itProcessorFactory: new TSingletonProcessorFactory(processor),
241 serverTransport: serverTransport,
Jens Geyer0d128322021-02-25 09:42:52 +0100242 inputTransportFactory: transportFactory,
243 outputTransportFactory: transportFactory,
244 inputProtocolFactory: protocolFactory,
245 outputProtocolFactory: protocolFactory,
Kyle Smith7b94dd42019-03-23 17:26:56 +0100246 logger: loggerFactory.CreateLogger<TSimpleAsyncServer>());
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100247
248 Logger.LogInformation("Starting the server...");
Kyle Smith7b94dd42019-03-23 17:26:56 +0100249
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100250 await server.ServeAsync(cancellationToken);
251 }
252 catch (Exception x)
253 {
254 Logger.LogInformation(x.ToString());
255 }
256 }
257
258 private static X509Certificate2 GetCertificate()
259 {
260 // due to files location in net core better to take certs from top folder
261 var certFile = GetCertPath(Directory.GetParent(Directory.GetCurrentDirectory()));
262 return new X509Certificate2(certFile, "ThriftTest");
263 }
264
265 private static string GetCertPath(DirectoryInfo di, int maxCount = 6)
266 {
267 var topDir = di;
268 var certFile =
269 topDir.EnumerateFiles("ThriftTest.pfx", SearchOption.AllDirectories)
270 .FirstOrDefault();
271 if (certFile == null)
272 {
273 if (maxCount == 0)
274 throw new FileNotFoundException("Cannot find file in directories");
275 return GetCertPath(di.Parent, maxCount - 1);
276 }
277
278 return certFile.FullName;
279 }
280
281 private static X509Certificate LocalCertificateSelectionCallback(object sender,
282 string targetHost, X509CertificateCollection localCertificates,
283 X509Certificate remoteCertificate, string[] acceptableIssuers)
284 {
285 return GetCertificate();
286 }
287
288 private static bool ClientCertValidator(object sender, X509Certificate certificate,
289 X509Chain chain, SslPolicyErrors sslPolicyErrors)
290 {
291 return true;
292 }
293
294 private enum Transport
295 {
296 Tcp,
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100297 NamedPipe,
298 Http,
299 TcpTls,
Kyle Smith7b94dd42019-03-23 17:26:56 +0100300 }
301
302 private enum Buffering
303 {
304 None,
305 Buffered,
306 Framed,
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100307 }
308
309 private enum Protocol
310 {
311 Binary,
312 Compact,
313 Json,
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100314 }
315
316 public class HttpServerSample
317 {
318 public void Run(CancellationToken cancellationToken)
319 {
320 var config = new ConfigurationBuilder()
321 .AddEnvironmentVariables(prefix: "ASPNETCORE_")
322 .Build();
323
324 var host = new WebHostBuilder()
325 .UseConfiguration(config)
326 .UseKestrel()
327 .UseUrls("http://localhost:9090")
328 .UseContentRoot(Directory.GetCurrentDirectory())
329 .UseStartup<Startup>()
Jens Geyerb11f63c2019-03-14 21:12:38 +0100330 .ConfigureLogging((ctx,logging) => ConfigureLogging(logging))
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100331 .Build();
332
Jens Geyerb11f63c2019-03-14 21:12:38 +0100333 Logger.LogTrace("test");
334 Logger.LogCritical("test");
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100335 host.RunAsync(cancellationToken).GetAwaiter().GetResult();
336 }
337
338 public class Startup
339 {
Jens Geyerec439542019-11-01 19:19:44 +0100340 public Startup(IWebHostEnvironment env)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100341 {
342 var builder = new ConfigurationBuilder()
343 .SetBasePath(env.ContentRootPath)
344 .AddEnvironmentVariables();
345
346 Configuration = builder.Build();
347 }
348
349 public IConfigurationRoot Configuration { get; }
350
351 // This method gets called by the runtime. Use this method to add services to the container.
352 public void ConfigureServices(IServiceCollection services)
353 {
Jens Geyer0d128322021-02-25 09:42:52 +0100354 // NOTE: this is not really the recommended way to do it
355 // because the HTTP server cannot be configured properly to e.g. accept framed or multiplex
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100356 services.AddTransient<Calculator.IAsync, CalculatorAsyncHandler>();
357 services.AddTransient<ITAsyncProcessor, Calculator.AsyncProcessor>();
358 services.AddTransient<THttpServerTransport, THttpServerTransport>();
359 }
360
361 // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
Jens Geyerec439542019-11-01 19:19:44 +0100362 public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100363 {
Jens Geyer0d128322021-02-25 09:42:52 +0100364 _ = env;
365 _ = loggerFactory;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100366 app.UseMiddleware<THttpServerTransport>();
367 }
368 }
369 }
370
371 public class CalculatorAsyncHandler : Calculator.IAsync
372 {
Jens Geyer2b2ea622021-04-09 22:55:11 +0200373 private readonly Dictionary<int, SharedStruct> _log = new();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100374
375 public CalculatorAsyncHandler()
376 {
377 }
378
Jens Geyer2b2ea622021-04-09 22:55:11 +0200379 public async Task<SharedStruct> getStruct(int key,
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100380 CancellationToken cancellationToken)
381 {
Jens Geyer2b2ea622021-04-09 22:55:11 +0200382 Logger.LogInformation("GetStruct({0})", key);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100383 return await Task.FromResult(_log[key]);
384 }
385
Jens Geyer2b2ea622021-04-09 22:55:11 +0200386 public async Task ping(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100387 {
Jens Geyer2b2ea622021-04-09 22:55:11 +0200388 Logger.LogInformation("Ping()");
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100389 await Task.CompletedTask;
390 }
391
Jens Geyer2b2ea622021-04-09 22:55:11 +0200392 public async Task<int> add(int num1, int num2, CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100393 {
Jens Geyer2b2ea622021-04-09 22:55:11 +0200394 Logger.LogInformation($"Add({num1},{num2})");
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100395 return await Task.FromResult(num1 + num2);
396 }
397
Jens Geyer2b2ea622021-04-09 22:55:11 +0200398 public async Task<int> calculate(int logid, Work w, CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100399 {
Jens Geyer2b2ea622021-04-09 22:55:11 +0200400 Logger.LogInformation($"Calculate({logid}, [{w.Op},{w.Num1},{w.Num2}])");
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100401
Jens Geyer0d128322021-02-25 09:42:52 +0100402 int val;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100403 switch (w.Op)
404 {
405 case Operation.ADD:
406 val = w.Num1 + w.Num2;
407 break;
408
409 case Operation.SUBTRACT:
410 val = w.Num1 - w.Num2;
411 break;
412
413 case Operation.MULTIPLY:
414 val = w.Num1 * w.Num2;
415 break;
416
417 case Operation.DIVIDE:
418 if (w.Num2 == 0)
419 {
420 var io = new InvalidOperation
421 {
422 WhatOp = (int) w.Op,
423 Why = "Cannot divide by 0"
424 };
425
426 throw io;
427 }
428 val = w.Num1 / w.Num2;
429 break;
430
431 default:
432 {
433 var io = new InvalidOperation
434 {
435 WhatOp = (int) w.Op,
436 Why = "Unknown operation"
437 };
438
439 throw io;
440 }
441 }
442
443 var entry = new SharedStruct
444 {
445 Key = logid,
446 Value = val.ToString()
447 };
448
449 _log[logid] = entry;
450
451 return await Task.FromResult(val);
452 }
453
Jens Geyer2b2ea622021-04-09 22:55:11 +0200454 public async Task zip(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100455 {
Jens Geyer2b2ea622021-04-09 22:55:11 +0200456 Logger.LogInformation("Zip() with delay 100mc");
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100457 await Task.Delay(100, CancellationToken.None);
458 }
459 }
460
461 public class SharedServiceAsyncHandler : SharedService.IAsync
462 {
Jens Geyer2b2ea622021-04-09 22:55:11 +0200463 public async Task<SharedStruct> getStruct(int key, CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100464 {
Jens Geyer2b2ea622021-04-09 22:55:11 +0200465 Logger.LogInformation("GetStruct({0})", key);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100466 return await Task.FromResult(new SharedStruct()
467 {
468 Key = key,
Jens Geyer2b2ea622021-04-09 22:55:11 +0200469 Value = "GetStruct"
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100470 });
471 }
472 }
473 }
474}