blob: df8f390aa0ff63ce3cd08cf36d3e4ae65d53044b [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 Geyer0d128322021-02-25 09:42:52 +010048 private static readonly ServiceCollection ServiceCollection = new ServiceCollection();
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 Geyeraa0c8b32019-01-28 23:27:45 +0100189 TServerTransport serverTransport = null;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100190 switch (transport)
191 {
192 case Transport.Tcp:
Jens Geyereacd1d42019-11-20 19:03:14 +0100193 serverTransport = new TServerSocketTransport(9090, Configuration);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100194 break;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100195 case Transport.NamedPipe:
Jens Geyereacd1d42019-11-20 19:03:14 +0100196 serverTransport = new TNamedPipeServerTransport(".test", Configuration);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100197 break;
198 case Transport.TcpTls:
Jens Geyereacd1d42019-11-20 19:03:14 +0100199 serverTransport = new TTlsServerSocketTransport(9090, Configuration,
200 GetCertificate(), ClientCertValidator, LocalCertificateSelectionCallback);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100201 break;
202 }
203
Jens Geyer0d128322021-02-25 09:42:52 +0100204 TTransportFactory transportFactory = null;
Kyle Smith7b94dd42019-03-23 17:26:56 +0100205 switch (buffering)
206 {
207 case Buffering.Buffered:
Jens Geyer0d128322021-02-25 09:42:52 +0100208 transportFactory = new TBufferedTransport.Factory();
Kyle Smith7b94dd42019-03-23 17:26:56 +0100209 break;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100210
Kyle Smith7b94dd42019-03-23 17:26:56 +0100211 case Buffering.Framed:
Jens Geyer0d128322021-02-25 09:42:52 +0100212 transportFactory = new TFramedTransport.Factory();
Kyle Smith7b94dd42019-03-23 17:26:56 +0100213 break;
214
215 default: // layered transport(s) are optional
216 Debug.Assert(buffering == Buffering.None, "unhandled case");
217 break;
218 }
219
Jens Geyer0d128322021-02-25 09:42:52 +0100220 TProtocolFactory protocolFactory = null;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100221 switch (protocol)
222 {
223 case Protocol.Binary:
Jens Geyer0d128322021-02-25 09:42:52 +0100224 protocolFactory = new TBinaryProtocol.Factory();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100225 break;
Jens Geyer421444f2019-03-20 22:13:25 +0100226
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100227 case Protocol.Compact:
Jens Geyer0d128322021-02-25 09:42:52 +0100228 protocolFactory = new TCompactProtocol.Factory();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100229 break;
Jens Geyer421444f2019-03-20 22:13:25 +0100230
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100231 case Protocol.Json:
Jens Geyer0d128322021-02-25 09:42:52 +0100232 protocolFactory = new TJsonProtocol.Factory();
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
Jens Geyer0d128322021-02-25 09:42:52 +0100239 var handler = new CalculatorAsyncHandler();
240 ITAsyncProcessor processor = new Calculator.AsyncProcessor(handler);
241
242 if (multiplex)
243 {
244 var multiplexedProcessor = new TMultiplexedProcessor();
245 multiplexedProcessor.RegisterProcessor(nameof(Calculator), processor);
246
247 processor = multiplexedProcessor;
248 }
249
Kyle Smith7b94dd42019-03-23 17:26:56 +0100250
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100251 try
252 {
253 Logger.LogInformation(
Jens Geyer0d128322021-02-25 09:42:52 +0100254 string.Format(
255 "TSimpleAsyncServer with \n{0} transport\n{1} buffering\nmultiplex = {2}\n{3} protocol",
256 transport,
257 buffering,
258 multiplex ? "yes" : "no",
259 protocol
260 ));
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100261
Kyle Smith7b94dd42019-03-23 17:26:56 +0100262 var loggerFactory = ServiceCollection.BuildServiceProvider().GetService<ILoggerFactory>();
263
264 var server = new TSimpleAsyncServer(
265 itProcessorFactory: new TSingletonProcessorFactory(processor),
266 serverTransport: serverTransport,
Jens Geyer0d128322021-02-25 09:42:52 +0100267 inputTransportFactory: transportFactory,
268 outputTransportFactory: transportFactory,
269 inputProtocolFactory: protocolFactory,
270 outputProtocolFactory: protocolFactory,
Kyle Smith7b94dd42019-03-23 17:26:56 +0100271 logger: loggerFactory.CreateLogger<TSimpleAsyncServer>());
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100272
273 Logger.LogInformation("Starting the server...");
Kyle Smith7b94dd42019-03-23 17:26:56 +0100274
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100275 await server.ServeAsync(cancellationToken);
276 }
277 catch (Exception x)
278 {
279 Logger.LogInformation(x.ToString());
280 }
281 }
282
283 private static X509Certificate2 GetCertificate()
284 {
285 // due to files location in net core better to take certs from top folder
286 var certFile = GetCertPath(Directory.GetParent(Directory.GetCurrentDirectory()));
287 return new X509Certificate2(certFile, "ThriftTest");
288 }
289
290 private static string GetCertPath(DirectoryInfo di, int maxCount = 6)
291 {
292 var topDir = di;
293 var certFile =
294 topDir.EnumerateFiles("ThriftTest.pfx", SearchOption.AllDirectories)
295 .FirstOrDefault();
296 if (certFile == null)
297 {
298 if (maxCount == 0)
299 throw new FileNotFoundException("Cannot find file in directories");
300 return GetCertPath(di.Parent, maxCount - 1);
301 }
302
303 return certFile.FullName;
304 }
305
306 private static X509Certificate LocalCertificateSelectionCallback(object sender,
307 string targetHost, X509CertificateCollection localCertificates,
308 X509Certificate remoteCertificate, string[] acceptableIssuers)
309 {
310 return GetCertificate();
311 }
312
313 private static bool ClientCertValidator(object sender, X509Certificate certificate,
314 X509Chain chain, SslPolicyErrors sslPolicyErrors)
315 {
316 return true;
317 }
318
319 private enum Transport
320 {
321 Tcp,
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100322 NamedPipe,
323 Http,
324 TcpTls,
Kyle Smith7b94dd42019-03-23 17:26:56 +0100325 }
326
327 private enum Buffering
328 {
329 None,
330 Buffered,
331 Framed,
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100332 }
333
334 private enum Protocol
335 {
336 Binary,
337 Compact,
338 Json,
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100339 }
340
341 public class HttpServerSample
342 {
343 public void Run(CancellationToken cancellationToken)
344 {
345 var config = new ConfigurationBuilder()
346 .AddEnvironmentVariables(prefix: "ASPNETCORE_")
347 .Build();
348
349 var host = new WebHostBuilder()
350 .UseConfiguration(config)
351 .UseKestrel()
352 .UseUrls("http://localhost:9090")
353 .UseContentRoot(Directory.GetCurrentDirectory())
354 .UseStartup<Startup>()
Jens Geyerb11f63c2019-03-14 21:12:38 +0100355 .ConfigureLogging((ctx,logging) => ConfigureLogging(logging))
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100356 .Build();
357
Jens Geyerb11f63c2019-03-14 21:12:38 +0100358 Logger.LogTrace("test");
359 Logger.LogCritical("test");
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100360 host.RunAsync(cancellationToken).GetAwaiter().GetResult();
361 }
362
363 public class Startup
364 {
Jens Geyerec439542019-11-01 19:19:44 +0100365 public Startup(IWebHostEnvironment env)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100366 {
367 var builder = new ConfigurationBuilder()
368 .SetBasePath(env.ContentRootPath)
369 .AddEnvironmentVariables();
370
371 Configuration = builder.Build();
372 }
373
374 public IConfigurationRoot Configuration { get; }
375
376 // This method gets called by the runtime. Use this method to add services to the container.
377 public void ConfigureServices(IServiceCollection services)
378 {
Jens Geyer0d128322021-02-25 09:42:52 +0100379 // NOTE: this is not really the recommended way to do it
380 // because the HTTP server cannot be configured properly to e.g. accept framed or multiplex
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100381 services.AddTransient<Calculator.IAsync, CalculatorAsyncHandler>();
382 services.AddTransient<ITAsyncProcessor, Calculator.AsyncProcessor>();
383 services.AddTransient<THttpServerTransport, THttpServerTransport>();
384 }
385
386 // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
Jens Geyerec439542019-11-01 19:19:44 +0100387 public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100388 {
Jens Geyer0d128322021-02-25 09:42:52 +0100389 _ = env;
390 _ = loggerFactory;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100391 app.UseMiddleware<THttpServerTransport>();
392 }
393 }
394 }
395
396 public class CalculatorAsyncHandler : Calculator.IAsync
397 {
398 private readonly Dictionary<int, SharedStruct> _log = new Dictionary<int, SharedStruct>();
399
400 public CalculatorAsyncHandler()
401 {
402 }
403
404 public async Task<SharedStruct> getStructAsync(int key,
405 CancellationToken cancellationToken)
406 {
407 Logger.LogInformation("GetStructAsync({0})", key);
408 return await Task.FromResult(_log[key]);
409 }
410
411 public async Task pingAsync(CancellationToken cancellationToken)
412 {
413 Logger.LogInformation("PingAsync()");
414 await Task.CompletedTask;
415 }
416
417 public async Task<int> addAsync(int num1, int num2, CancellationToken cancellationToken)
418 {
419 Logger.LogInformation($"AddAsync({num1},{num2})");
420 return await Task.FromResult(num1 + num2);
421 }
422
423 public async Task<int> calculateAsync(int logid, Work w, CancellationToken cancellationToken)
424 {
425 Logger.LogInformation($"CalculateAsync({logid}, [{w.Op},{w.Num1},{w.Num2}])");
426
Jens Geyer0d128322021-02-25 09:42:52 +0100427 int val;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100428 switch (w.Op)
429 {
430 case Operation.ADD:
431 val = w.Num1 + w.Num2;
432 break;
433
434 case Operation.SUBTRACT:
435 val = w.Num1 - w.Num2;
436 break;
437
438 case Operation.MULTIPLY:
439 val = w.Num1 * w.Num2;
440 break;
441
442 case Operation.DIVIDE:
443 if (w.Num2 == 0)
444 {
445 var io = new InvalidOperation
446 {
447 WhatOp = (int) w.Op,
448 Why = "Cannot divide by 0"
449 };
450
451 throw io;
452 }
453 val = w.Num1 / w.Num2;
454 break;
455
456 default:
457 {
458 var io = new InvalidOperation
459 {
460 WhatOp = (int) w.Op,
461 Why = "Unknown operation"
462 };
463
464 throw io;
465 }
466 }
467
468 var entry = new SharedStruct
469 {
470 Key = logid,
471 Value = val.ToString()
472 };
473
474 _log[logid] = entry;
475
476 return await Task.FromResult(val);
477 }
478
479 public async Task zipAsync(CancellationToken cancellationToken)
480 {
481 Logger.LogInformation("ZipAsync() with delay 100mc");
482 await Task.Delay(100, CancellationToken.None);
483 }
484 }
485
486 public class SharedServiceAsyncHandler : SharedService.IAsync
487 {
488 public async Task<SharedStruct> getStructAsync(int key, CancellationToken cancellationToken)
489 {
490 Logger.LogInformation("GetStructAsync({0})", key);
491 return await Task.FromResult(new SharedStruct()
492 {
493 Key = key,
494 Value = "GetStructAsync"
495 });
496 }
497 }
498 }
499}