blob: c5e26d12d08b400ab4987d82878aa1e0e1a86f7f [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;
39
40namespace Server
41{
42 public class Program
43 {
Jens Geyerb11f63c2019-03-14 21:12:38 +010044 private static ServiceCollection ServiceCollection = new ServiceCollection();
45 private static ILogger Logger;
Jens Geyeraa0c8b32019-01-28 23:27:45 +010046
47 public static void Main(string[] args)
48 {
49 args = args ?? new string[0];
50
Jens Geyerb11f63c2019-03-14 21:12:38 +010051 ServiceCollection.AddLogging(logging => ConfigureLogging(logging));
52 Logger = ServiceCollection.BuildServiceProvider().GetService<ILoggerFactory>().CreateLogger(nameof(Server));
53
54
Jens Geyeraa0c8b32019-01-28 23:27:45 +010055 if (args.Any(x => x.StartsWith("-help", StringComparison.OrdinalIgnoreCase)))
56 {
57 DisplayHelp();
58 return;
59 }
60
61 using (var source = new CancellationTokenSource())
62 {
63 RunAsync(args, source.Token).GetAwaiter().GetResult();
64
65 Logger.LogInformation("Press any key to stop...");
66
67 Console.ReadLine();
68 source.Cancel();
69 }
70
71 Logger.LogInformation("Server stopped");
72 }
73
Jens Geyerb11f63c2019-03-14 21:12:38 +010074 private static void ConfigureLogging(ILoggingBuilder logging)
75 {
76 logging.SetMinimumLevel(LogLevel.Trace);
77 logging.AddConsole();
78 logging.AddDebug();
79 }
80
Jens Geyeraa0c8b32019-01-28 23:27:45 +010081 private static void DisplayHelp()
82 {
83 Logger.LogInformation(@"
84Usage:
85 Server.exe -help
86 will diplay help information
87
88 Server.exe -tr:<transport> -pr:<protocol>
89 will run server with specified arguments (tcp transport and binary protocol by default)
90
91Options:
92 -tr (transport):
93 tcp - (default) tcp transport will be used (host - ""localhost"", port - 9090)
94 tcpbuffered - tcp buffered transport will be used (host - ""localhost"", port - 9090)
95 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)
98 framed - tcp framed transport will be used (host - ""localhost"", port - 9090)
99
100 -pr (protocol):
101 binary - (default) binary protocol will be used
102 compact - compact protocol will be used
103 json - json protocol will be used
104 multiplexed - multiplexed protocol will be used
105
106Sample:
107 Server.exe -tr:tcp
108");
109 }
110
111 private static async Task RunAsync(string[] args, CancellationToken cancellationToken)
112 {
113 var selectedTransport = GetTransport(args);
114 var selectedProtocol = GetProtocol(args);
115
116 if (selectedTransport == Transport.Http)
117 {
118 new HttpServerSample().Run(cancellationToken);
119 }
120 else
121 {
122 await RunSelectedConfigurationAsync(selectedTransport, selectedProtocol, cancellationToken);
123 }
124 }
125
126 private static Protocol GetProtocol(string[] args)
127 {
128 var transport = args.FirstOrDefault(x => x.StartsWith("-pr"))?.Split(':')?[1];
129
130 Enum.TryParse(transport, true, out Protocol selectedProtocol);
131
132 return selectedProtocol;
133 }
134
135 private static Transport GetTransport(string[] args)
136 {
137 var transport = args.FirstOrDefault(x => x.StartsWith("-tr"))?.Split(':')?[1];
138
139 Enum.TryParse(transport, true, out Transport selectedTransport);
140
141 return selectedTransport;
142 }
143
144 private static async Task RunSelectedConfigurationAsync(Transport transport, Protocol protocol, CancellationToken cancellationToken)
145 {
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100146 var handler = new CalculatorAsyncHandler();
147 ITAsyncProcessor processor = null;
148
149 TServerTransport serverTransport = null;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100150 switch (transport)
151 {
152 case Transport.Tcp:
153 serverTransport = new TServerSocketTransport(9090);
154 break;
155 case Transport.TcpBuffered:
Jens Geyerbf276372019-03-14 21:42:16 +0100156 serverTransport = new TServerSocketTransport(port: 9090, clientTimeout: 10000, buffering: Buffering.BufferedTransport);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100157 break;
158 case Transport.NamedPipe:
159 serverTransport = new TNamedPipeServerTransport(".test");
160 break;
161 case Transport.TcpTls:
Kyle Smith6378ff62019-03-15 07:27:15 -0400162 serverTransport = new TTlsServerSocketTransport(9090, GetCertificate(), Buffering.None, ClientCertValidator, LocalCertificateSelectionCallback);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100163 break;
164 case Transport.Framed:
165 serverTransport = new TServerFramedTransport(9090);
166 break;
167 }
168
Jens Geyer421444f2019-03-20 22:13:25 +0100169 TProtocolFactory inputProtocolFactory;
170 TProtocolFactory outputProtocolFactory;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100171
172 switch (protocol)
173 {
174 case Protocol.Binary:
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100175 inputProtocolFactory = new TBinaryProtocol.Factory();
176 outputProtocolFactory = new TBinaryProtocol.Factory();
177 processor = new Calculator.AsyncProcessor(handler);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100178 break;
Jens Geyer421444f2019-03-20 22:13:25 +0100179
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100180 case Protocol.Compact:
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100181 inputProtocolFactory = new TCompactProtocol.Factory();
182 outputProtocolFactory = new TCompactProtocol.Factory();
183 processor = new Calculator.AsyncProcessor(handler);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100184 break;
Jens Geyer421444f2019-03-20 22:13:25 +0100185
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100186 case Protocol.Json:
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100187 inputProtocolFactory = new TJsonProtocol.Factory();
188 outputProtocolFactory = new TJsonProtocol.Factory();
189 processor = new Calculator.AsyncProcessor(handler);
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100190 break;
Jens Geyer421444f2019-03-20 22:13:25 +0100191
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100192 case Protocol.Multiplexed:
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100193 inputProtocolFactory = new TBinaryProtocol.Factory();
194 outputProtocolFactory = new TBinaryProtocol.Factory();
195
196 var calcHandler = new CalculatorAsyncHandler();
197 var calcProcessor = new Calculator.AsyncProcessor(calcHandler);
198
199 var sharedServiceHandler = new SharedServiceAsyncHandler();
200 var sharedServiceProcessor = new SharedService.AsyncProcessor(sharedServiceHandler);
201
202 var multiplexedProcessor = new TMultiplexedProcessor();
203 multiplexedProcessor.RegisterProcessor(nameof(Calculator), calcProcessor);
204 multiplexedProcessor.RegisterProcessor(nameof(SharedService), sharedServiceProcessor);
205
206 processor = multiplexedProcessor;
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100207 break;
Jens Geyer421444f2019-03-20 22:13:25 +0100208
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100209 default:
210 throw new ArgumentOutOfRangeException(nameof(protocol), protocol, null);
211 }
212
213 try
214 {
215 Logger.LogInformation(
216 $"Selected TAsyncServer with {serverTransport} transport, {processor} processor and {inputProtocolFactory} protocol factories");
217
Jens Geyerb11f63c2019-03-14 21:12:38 +0100218 var fabric = ServiceCollection.BuildServiceProvider().GetService<ILoggerFactory>();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100219 var server = new TSimpleAsyncServer(processor, serverTransport, inputProtocolFactory, outputProtocolFactory, fabric);
220
221 Logger.LogInformation("Starting the server...");
222 await server.ServeAsync(cancellationToken);
223 }
224 catch (Exception x)
225 {
226 Logger.LogInformation(x.ToString());
227 }
228 }
229
230 private static X509Certificate2 GetCertificate()
231 {
232 // due to files location in net core better to take certs from top folder
233 var certFile = GetCertPath(Directory.GetParent(Directory.GetCurrentDirectory()));
234 return new X509Certificate2(certFile, "ThriftTest");
235 }
236
237 private static string GetCertPath(DirectoryInfo di, int maxCount = 6)
238 {
239 var topDir = di;
240 var certFile =
241 topDir.EnumerateFiles("ThriftTest.pfx", SearchOption.AllDirectories)
242 .FirstOrDefault();
243 if (certFile == null)
244 {
245 if (maxCount == 0)
246 throw new FileNotFoundException("Cannot find file in directories");
247 return GetCertPath(di.Parent, maxCount - 1);
248 }
249
250 return certFile.FullName;
251 }
252
253 private static X509Certificate LocalCertificateSelectionCallback(object sender,
254 string targetHost, X509CertificateCollection localCertificates,
255 X509Certificate remoteCertificate, string[] acceptableIssuers)
256 {
257 return GetCertificate();
258 }
259
260 private static bool ClientCertValidator(object sender, X509Certificate certificate,
261 X509Chain chain, SslPolicyErrors sslPolicyErrors)
262 {
263 return true;
264 }
265
266 private enum Transport
267 {
268 Tcp,
269 TcpBuffered,
270 NamedPipe,
271 Http,
272 TcpTls,
273 Framed
274 }
275
276 private enum Protocol
277 {
278 Binary,
279 Compact,
280 Json,
281 Multiplexed
282 }
283
284 public class HttpServerSample
285 {
286 public void Run(CancellationToken cancellationToken)
287 {
288 var config = new ConfigurationBuilder()
289 .AddEnvironmentVariables(prefix: "ASPNETCORE_")
290 .Build();
291
292 var host = new WebHostBuilder()
293 .UseConfiguration(config)
294 .UseKestrel()
295 .UseUrls("http://localhost:9090")
296 .UseContentRoot(Directory.GetCurrentDirectory())
297 .UseStartup<Startup>()
Jens Geyerb11f63c2019-03-14 21:12:38 +0100298 .ConfigureLogging((ctx,logging) => ConfigureLogging(logging))
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100299 .Build();
300
Jens Geyerb11f63c2019-03-14 21:12:38 +0100301 Logger.LogTrace("test");
302 Logger.LogCritical("test");
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100303 host.RunAsync(cancellationToken).GetAwaiter().GetResult();
304 }
305
306 public class Startup
307 {
308 public Startup(IHostingEnvironment env)
309 {
310 var builder = new ConfigurationBuilder()
311 .SetBasePath(env.ContentRootPath)
312 .AddEnvironmentVariables();
313
314 Configuration = builder.Build();
315 }
316
317 public IConfigurationRoot Configuration { get; }
318
319 // This method gets called by the runtime. Use this method to add services to the container.
320 public void ConfigureServices(IServiceCollection services)
321 {
322 services.AddTransient<Calculator.IAsync, CalculatorAsyncHandler>();
323 services.AddTransient<ITAsyncProcessor, Calculator.AsyncProcessor>();
324 services.AddTransient<THttpServerTransport, THttpServerTransport>();
325 }
326
327 // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
Jens Geyerb11f63c2019-03-14 21:12:38 +0100328 public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100329 {
330 app.UseMiddleware<THttpServerTransport>();
331 }
332 }
333 }
334
335 public class CalculatorAsyncHandler : Calculator.IAsync
336 {
337 private readonly Dictionary<int, SharedStruct> _log = new Dictionary<int, SharedStruct>();
338
339 public CalculatorAsyncHandler()
340 {
341 }
342
343 public async Task<SharedStruct> getStructAsync(int key,
344 CancellationToken cancellationToken)
345 {
346 Logger.LogInformation("GetStructAsync({0})", key);
347 return await Task.FromResult(_log[key]);
348 }
349
350 public async Task pingAsync(CancellationToken cancellationToken)
351 {
352 Logger.LogInformation("PingAsync()");
353 await Task.CompletedTask;
354 }
355
356 public async Task<int> addAsync(int num1, int num2, CancellationToken cancellationToken)
357 {
358 Logger.LogInformation($"AddAsync({num1},{num2})");
359 return await Task.FromResult(num1 + num2);
360 }
361
362 public async Task<int> calculateAsync(int logid, Work w, CancellationToken cancellationToken)
363 {
364 Logger.LogInformation($"CalculateAsync({logid}, [{w.Op},{w.Num1},{w.Num2}])");
365
366 var val = 0;
367 switch (w.Op)
368 {
369 case Operation.ADD:
370 val = w.Num1 + w.Num2;
371 break;
372
373 case Operation.SUBTRACT:
374 val = w.Num1 - w.Num2;
375 break;
376
377 case Operation.MULTIPLY:
378 val = w.Num1 * w.Num2;
379 break;
380
381 case Operation.DIVIDE:
382 if (w.Num2 == 0)
383 {
384 var io = new InvalidOperation
385 {
386 WhatOp = (int) w.Op,
387 Why = "Cannot divide by 0"
388 };
389
390 throw io;
391 }
392 val = w.Num1 / w.Num2;
393 break;
394
395 default:
396 {
397 var io = new InvalidOperation
398 {
399 WhatOp = (int) w.Op,
400 Why = "Unknown operation"
401 };
402
403 throw io;
404 }
405 }
406
407 var entry = new SharedStruct
408 {
409 Key = logid,
410 Value = val.ToString()
411 };
412
413 _log[logid] = entry;
414
415 return await Task.FromResult(val);
416 }
417
418 public async Task zipAsync(CancellationToken cancellationToken)
419 {
420 Logger.LogInformation("ZipAsync() with delay 100mc");
421 await Task.Delay(100, CancellationToken.None);
422 }
423 }
424
425 public class SharedServiceAsyncHandler : SharedService.IAsync
426 {
427 public async Task<SharedStruct> getStructAsync(int key, CancellationToken cancellationToken)
428 {
429 Logger.LogInformation("GetStructAsync({0})", key);
430 return await Task.FromResult(new SharedStruct()
431 {
432 Key = key,
433 Value = "GetStructAsync"
434 });
435 }
436 }
437 }
438}