blob: 29b21d07382fb876ebcc857506b09e1703607c9d [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 IDE0057 // substr
42
Jens Geyeraa0c8b32019-01-28 23:27:45 +010043namespace Server
44{
Jens Geyer4c7b9fd2021-12-04 22:48:37 +010045 public static class LoggingHelper
46 {
47 public static ILoggerFactory LogFactory { get; } = LoggerFactory.Create(builder => {
48 ConfigureLogging(builder);
49 });
50
51 public static void ConfigureLogging(ILoggingBuilder logging)
52 {
53 logging.SetMinimumLevel(LogLevel.Trace);
54 logging.AddConsole();
55 logging.AddDebug();
56 }
57
58 public static ILogger<T> CreateLogger<T>() => LogFactory.CreateLogger<T>();
59 }
60
Jens Geyeraa0c8b32019-01-28 23:27:45 +010061 public class Program
62 {
Jens Geyer4c7b9fd2021-12-04 22:48:37 +010063 private static readonly ILogger Logger = LoggingHelper.CreateLogger<Program>();
Jens Geyereacd1d42019-11-20 19:03:14 +010064 private static readonly TConfiguration Configuration = null; // new TConfiguration() if needed
Jens Geyeraa0c8b32019-01-28 23:27:45 +010065
66 public static void Main(string[] args)
67 {
Jens Geyer0d128322021-02-25 09:42:52 +010068 args ??= Array.Empty<string>();
Jens Geyeraa0c8b32019-01-28 23:27:45 +010069
Jens Geyer4c7b9fd2021-12-04 22:48:37 +010070 if (args.Any(x => x.StartsWith("-help", StringComparison.OrdinalIgnoreCase)))
Jens Geyeraa0c8b32019-01-28 23:27:45 +010071 {
Jens Geyer4c7b9fd2021-12-04 22:48:37 +010072 DisplayHelp();
73 return;
Jens Geyeraa0c8b32019-01-28 23:27:45 +010074 }
Jens Geyer4c7b9fd2021-12-04 22:48:37 +010075
76 using (var source = new CancellationTokenSource())
77 {
78 RunAsync(args, source.Token).GetAwaiter().GetResult();
79
80 Logger.LogInformation("Press any key to stop...");
81
82 Console.ReadLine();
83 source.Cancel();
84 }
85
86 Logger.LogInformation("Server stopped");
Jens Geyeraa0c8b32019-01-28 23:27:45 +010087 }
88
Jens Geyerb11f63c2019-03-14 21:12:38 +010089
Jens Geyeraa0c8b32019-01-28 23:27:45 +010090 private static void DisplayHelp()
91 {
92 Logger.LogInformation(@"
93Usage:
Kengo Sekibee4f2f2019-12-29 17:04:50 +090094 Server -help
Jens Geyeraa0c8b32019-01-28 23:27:45 +010095 will diplay help information
96
Jens Geyer0d128322021-02-25 09:42:52 +010097 Server -tr:<transport> -bf:<buffering> -pr:<protocol> [-multiplex]
Kyle Smith7b94dd42019-03-23 17:26:56 +010098 will run server with specified arguments (tcp transport, no buffering, and binary protocol by default)
Jens Geyeraa0c8b32019-01-28 23:27:45 +010099
100Options:
101 -tr (transport):
Jens Geyer0d128322021-02-25 09:42:52 +0100102 tcp - (default) tcp transport (localhost:9090)
103 tcptls - tcp transport with tls (localhost:9090)
104 namedpipe - namedpipe transport (pipe "".test"")
105 http - http transport (localhost:9090)
Kyle Smith7b94dd42019-03-23 17:26:56 +0100106
107 -bf (buffering):
Jens Geyer0d128322021-02-25 09:42:52 +0100108 none - (default) no buffering
109 buffered - buffered transport
110 framed - framed transport
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100111
112 -pr (protocol):
Jens Geyer0d128322021-02-25 09:42:52 +0100113 binary - (default) binary protocol
114 compact - compact protocol
115 json - json protocol
116
117 -multiplex - adds multiplexed protocol
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100118
119Sample:
Kengo Sekibee4f2f2019-12-29 17:04:50 +0900120 Server -tr:tcp
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100121");
122 }
123
124 private static async Task RunAsync(string[] args, CancellationToken cancellationToken)
125 {
126 var selectedTransport = GetTransport(args);
Kyle Smith7b94dd42019-03-23 17:26:56 +0100127 var selectedBuffering = GetBuffering(args);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100128 var selectedProtocol = GetProtocol(args);
Jens Geyer0d128322021-02-25 09:42:52 +0100129 var multiplex = GetMultiplex(args);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100130
131 if (selectedTransport == Transport.Http)
132 {
Jens Geyer0d128322021-02-25 09:42:52 +0100133 if (multiplex)
Jens Geyer3cac3202022-01-31 18:04:35 +0100134 throw new Exception("This tutorial sample code does not yet allow multiplex over http (although Thrift itself of course does)");
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100135 new HttpServerSample().Run(cancellationToken);
136 }
137 else
138 {
Jens Geyer0d128322021-02-25 09:42:52 +0100139 await RunSelectedConfigurationAsync(selectedTransport, selectedBuffering, selectedProtocol, multiplex, cancellationToken);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100140 }
141 }
142
Jens Geyer0d128322021-02-25 09:42:52 +0100143
144 private static bool GetMultiplex(string[] args)
145 {
146 var mplex = args.FirstOrDefault(x => x.StartsWith("-multiplex"));
147 return !string.IsNullOrEmpty(mplex);
148 }
149
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100150 private static Protocol GetProtocol(string[] args)
151 {
Jens Geyer0d128322021-02-25 09:42:52 +0100152 var protocol = args.FirstOrDefault(x => x.StartsWith("-pr"))?.Split(':')?[1];
153 if (string.IsNullOrEmpty(protocol))
154 return Protocol.Binary;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100155
Jens Geyer0d128322021-02-25 09:42:52 +0100156 protocol = protocol.Substring(0, 1).ToUpperInvariant() + protocol.Substring(1).ToLowerInvariant();
157 if (Enum.TryParse(protocol, true, out Protocol selectedProtocol))
158 return selectedProtocol;
159 else
160 return Protocol.Binary;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100161 }
162
Kyle Smith7b94dd42019-03-23 17:26:56 +0100163 private static Buffering GetBuffering(string[] args)
164 {
165 var buffering = args.FirstOrDefault(x => x.StartsWith("-bf"))?.Split(":")?[1];
Jens Geyer0d128322021-02-25 09:42:52 +0100166 if (string.IsNullOrEmpty(buffering))
167 return Buffering.None;
Kyle Smith7b94dd42019-03-23 17:26:56 +0100168
Jens Geyer0d128322021-02-25 09:42:52 +0100169 buffering = buffering.Substring(0, 1).ToUpperInvariant() + buffering.Substring(1).ToLowerInvariant();
170 if( Enum.TryParse<Buffering>(buffering, out var selectedBuffering))
171 return selectedBuffering;
172 else
173 return Buffering.None;
Kyle Smith7b94dd42019-03-23 17:26:56 +0100174 }
175
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100176 private static Transport GetTransport(string[] args)
177 {
178 var transport = args.FirstOrDefault(x => x.StartsWith("-tr"))?.Split(':')?[1];
Jens Geyer0d128322021-02-25 09:42:52 +0100179 if (string.IsNullOrEmpty(transport))
180 return Transport.Tcp;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100181
Jens Geyer0d128322021-02-25 09:42:52 +0100182 transport = transport.Substring(0, 1).ToUpperInvariant() + transport.Substring(1).ToLowerInvariant();
183 if( Enum.TryParse(transport, true, out Transport selectedTransport))
184 return selectedTransport;
185 else
186 return Transport.Tcp;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100187 }
188
Jens Geyer0d128322021-02-25 09:42:52 +0100189 private static async Task RunSelectedConfigurationAsync(Transport transport, Buffering buffering, Protocol protocol, bool multiplex, CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100190 {
Jens Geyer2b2ea622021-04-09 22:55:11 +0200191 TServerTransport serverTransport = transport switch
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100192 {
Jens Geyer2b2ea622021-04-09 22:55:11 +0200193 Transport.Tcp => new TServerSocketTransport(9090, Configuration),
194 Transport.NamedPipe => new TNamedPipeServerTransport(".test", Configuration, NamedPipeClientFlags.None),
195 Transport.TcpTls => new TTlsServerSocketTransport(9090, Configuration, GetCertificate(), ClientCertValidator, LocalCertificateSelectionCallback),
196 _ => throw new ArgumentException("unsupported value $transport", nameof(transport)),
197 };
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100198
Jens Geyer2b2ea622021-04-09 22:55:11 +0200199 TTransportFactory transportFactory = buffering switch
Kyle Smith7b94dd42019-03-23 17:26:56 +0100200 {
Jens Geyer2b2ea622021-04-09 22:55:11 +0200201 Buffering.Buffered => new TBufferedTransport.Factory(),
202 Buffering.Framed => new TFramedTransport.Factory(),
203 // layered transport(s) are optional
204 Buffering.None => null,
205 _ => throw new ArgumentException("unsupported value $buffering", nameof(buffering)),
206 };
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100207
Jens Geyer2b2ea622021-04-09 22:55:11 +0200208 TProtocolFactory protocolFactory = protocol switch
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100209 {
Jens Geyer2b2ea622021-04-09 22:55:11 +0200210 Protocol.Binary => new TBinaryProtocol.Factory(),
211 Protocol.Compact => new TCompactProtocol.Factory(),
212 Protocol.Json => new TJsonProtocol.Factory(),
213 _ => throw new ArgumentException("unsupported value $protocol", nameof(protocol)),
214 };
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100215
Jens Geyer0d128322021-02-25 09:42:52 +0100216 var handler = new CalculatorAsyncHandler();
217 ITAsyncProcessor processor = new Calculator.AsyncProcessor(handler);
218
219 if (multiplex)
220 {
221 var multiplexedProcessor = new TMultiplexedProcessor();
222 multiplexedProcessor.RegisterProcessor(nameof(Calculator), processor);
223
224 processor = multiplexedProcessor;
225 }
226
Kyle Smith7b94dd42019-03-23 17:26:56 +0100227
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100228 try
229 {
230 Logger.LogInformation(
Jens Geyer4c7b9fd2021-12-04 22:48:37 +0100231 "TSimpleAsyncServer with \n{transport} transport\n{buffering} buffering\nmultiplex = {multiplex}\n{protocol} protocol",
232 transport,
233 buffering,
234 multiplex ? "yes" : "no",
235 protocol
236 );
Kyle Smith7b94dd42019-03-23 17:26:56 +0100237
238 var server = new TSimpleAsyncServer(
239 itProcessorFactory: new TSingletonProcessorFactory(processor),
240 serverTransport: serverTransport,
Jens Geyer0d128322021-02-25 09:42:52 +0100241 inputTransportFactory: transportFactory,
242 outputTransportFactory: transportFactory,
243 inputProtocolFactory: protocolFactory,
244 outputProtocolFactory: protocolFactory,
Jens Geyer4c7b9fd2021-12-04 22:48:37 +0100245 logger: LoggingHelper.CreateLogger<TSimpleAsyncServer >());
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100246
247 Logger.LogInformation("Starting the server...");
Kyle Smith7b94dd42019-03-23 17:26:56 +0100248
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100249 await server.ServeAsync(cancellationToken);
250 }
251 catch (Exception x)
252 {
Jens Geyer4c7b9fd2021-12-04 22:48:37 +0100253 Logger.LogInformation("{x}",x);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100254 }
255 }
256
257 private static X509Certificate2 GetCertificate()
258 {
259 // due to files location in net core better to take certs from top folder
260 var certFile = GetCertPath(Directory.GetParent(Directory.GetCurrentDirectory()));
261 return new X509Certificate2(certFile, "ThriftTest");
262 }
263
264 private static string GetCertPath(DirectoryInfo di, int maxCount = 6)
265 {
266 var topDir = di;
267 var certFile =
268 topDir.EnumerateFiles("ThriftTest.pfx", SearchOption.AllDirectories)
269 .FirstOrDefault();
270 if (certFile == null)
271 {
272 if (maxCount == 0)
273 throw new FileNotFoundException("Cannot find file in directories");
274 return GetCertPath(di.Parent, maxCount - 1);
275 }
276
277 return certFile.FullName;
278 }
279
280 private static X509Certificate LocalCertificateSelectionCallback(object sender,
281 string targetHost, X509CertificateCollection localCertificates,
282 X509Certificate remoteCertificate, string[] acceptableIssuers)
283 {
284 return GetCertificate();
285 }
286
287 private static bool ClientCertValidator(object sender, X509Certificate certificate,
288 X509Chain chain, SslPolicyErrors sslPolicyErrors)
289 {
290 return true;
291 }
292
293 private enum Transport
294 {
295 Tcp,
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100296 NamedPipe,
297 Http,
298 TcpTls,
Kyle Smith7b94dd42019-03-23 17:26:56 +0100299 }
300
301 private enum Buffering
302 {
303 None,
304 Buffered,
305 Framed,
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100306 }
307
308 private enum Protocol
309 {
310 Binary,
311 Compact,
312 Json,
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100313 }
314
315 public class HttpServerSample
316 {
317 public void Run(CancellationToken cancellationToken)
318 {
319 var config = new ConfigurationBuilder()
320 .AddEnvironmentVariables(prefix: "ASPNETCORE_")
321 .Build();
322
323 var host = new WebHostBuilder()
324 .UseConfiguration(config)
325 .UseKestrel()
326 .UseUrls("http://localhost:9090")
327 .UseContentRoot(Directory.GetCurrentDirectory())
328 .UseStartup<Startup>()
Jens Geyer4c7b9fd2021-12-04 22:48:37 +0100329 .ConfigureLogging((ctx,logging) => LoggingHelper.ConfigureLogging(logging))
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100330 .Build();
331
Jens Geyerb11f63c2019-03-14 21:12:38 +0100332 Logger.LogTrace("test");
333 Logger.LogCritical("test");
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100334 host.RunAsync(cancellationToken).GetAwaiter().GetResult();
335 }
336
337 public class Startup
338 {
Jens Geyerec439542019-11-01 19:19:44 +0100339 public Startup(IWebHostEnvironment env)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100340 {
341 var builder = new ConfigurationBuilder()
342 .SetBasePath(env.ContentRootPath)
343 .AddEnvironmentVariables();
344
345 Configuration = builder.Build();
346 }
347
348 public IConfigurationRoot Configuration { get; }
349
350 // This method gets called by the runtime. Use this method to add services to the container.
351 public void ConfigureServices(IServiceCollection services)
352 {
Jens Geyer0d128322021-02-25 09:42:52 +0100353 // NOTE: this is not really the recommended way to do it
354 // because the HTTP server cannot be configured properly to e.g. accept framed or multiplex
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100355 services.AddTransient<Calculator.IAsync, CalculatorAsyncHandler>();
356 services.AddTransient<ITAsyncProcessor, Calculator.AsyncProcessor>();
357 services.AddTransient<THttpServerTransport, THttpServerTransport>();
358 }
359
360 // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
Jens Geyerec439542019-11-01 19:19:44 +0100361 public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100362 {
Jens Geyer0d128322021-02-25 09:42:52 +0100363 _ = env;
364 _ = loggerFactory;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100365 app.UseMiddleware<THttpServerTransport>();
366 }
367 }
368 }
369
370 public class CalculatorAsyncHandler : Calculator.IAsync
371 {
Jens Geyer2b2ea622021-04-09 22:55:11 +0200372 private readonly Dictionary<int, SharedStruct> _log = new();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100373
374 public CalculatorAsyncHandler()
375 {
376 }
377
Jens Geyer2b2ea622021-04-09 22:55:11 +0200378 public async Task<SharedStruct> getStruct(int key,
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100379 CancellationToken cancellationToken)
380 {
Jens Geyer4c7b9fd2021-12-04 22:48:37 +0100381 Logger.LogInformation("GetStruct({key})", key);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100382 return await Task.FromResult(_log[key]);
383 }
384
Jens Geyer2b2ea622021-04-09 22:55:11 +0200385 public async Task ping(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100386 {
Jens Geyer2b2ea622021-04-09 22:55:11 +0200387 Logger.LogInformation("Ping()");
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100388 await Task.CompletedTask;
389 }
390
Jens Geyer2b2ea622021-04-09 22:55:11 +0200391 public async Task<int> add(int num1, int num2, CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100392 {
Jens Geyer4c7b9fd2021-12-04 22:48:37 +0100393 Logger.LogInformation("Add({num1},{num2})", num1, num2);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100394 return await Task.FromResult(num1 + num2);
395 }
396
Jens Geyer2b2ea622021-04-09 22:55:11 +0200397 public async Task<int> calculate(int logid, Work w, CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100398 {
Jens Geyer4c7b9fd2021-12-04 22:48:37 +0100399 Logger.LogInformation("Calculate({logid}, [{w.Op},{w.Num1},{w.Num2}])", logid, w.Op, w.Num1, w.Num2);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100400
Jens Geyer0d128322021-02-25 09:42:52 +0100401 int val;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100402 switch (w.Op)
403 {
404 case Operation.ADD:
405 val = w.Num1 + w.Num2;
406 break;
407
408 case Operation.SUBTRACT:
409 val = w.Num1 - w.Num2;
410 break;
411
412 case Operation.MULTIPLY:
413 val = w.Num1 * w.Num2;
414 break;
415
416 case Operation.DIVIDE:
417 if (w.Num2 == 0)
418 {
419 var io = new InvalidOperation
420 {
421 WhatOp = (int) w.Op,
422 Why = "Cannot divide by 0"
423 };
424
425 throw io;
426 }
427 val = w.Num1 / w.Num2;
428 break;
429
430 default:
431 {
432 var io = new InvalidOperation
433 {
434 WhatOp = (int) w.Op,
435 Why = "Unknown operation"
436 };
437
438 throw io;
439 }
440 }
441
442 var entry = new SharedStruct
443 {
444 Key = logid,
445 Value = val.ToString()
446 };
447
448 _log[logid] = entry;
449
450 return await Task.FromResult(val);
451 }
452
Jens Geyer2b2ea622021-04-09 22:55:11 +0200453 public async Task zip(CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100454 {
Jens Geyer2b2ea622021-04-09 22:55:11 +0200455 Logger.LogInformation("Zip() with delay 100mc");
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100456 await Task.Delay(100, CancellationToken.None);
457 }
458 }
459
460 public class SharedServiceAsyncHandler : SharedService.IAsync
461 {
Jens Geyer2b2ea622021-04-09 22:55:11 +0200462 public async Task<SharedStruct> getStruct(int key, CancellationToken cancellationToken)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100463 {
Jens Geyer4c7b9fd2021-12-04 22:48:37 +0100464 Logger.LogInformation("GetStruct({key})", key);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100465 return await Task.FromResult(new SharedStruct()
466 {
467 Key = key,
Jens Geyer2b2ea622021-04-09 22:55:11 +0200468 Value = "GetStruct"
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100469 });
470 }
471 }
472 }
473}