blob: e74a042173a82ad731405484c13ffabd741aa4dc [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
169 ITProtocolFactory inputProtocolFactory;
170 ITProtocolFactory outputProtocolFactory;
171
172 switch (protocol)
173 {
174 case Protocol.Binary:
175 {
176 inputProtocolFactory = new TBinaryProtocol.Factory();
177 outputProtocolFactory = new TBinaryProtocol.Factory();
178 processor = new Calculator.AsyncProcessor(handler);
179 }
180 break;
181 case Protocol.Compact:
182 {
183 inputProtocolFactory = new TCompactProtocol.Factory();
184 outputProtocolFactory = new TCompactProtocol.Factory();
185 processor = new Calculator.AsyncProcessor(handler);
186 }
187 break;
188 case Protocol.Json:
189 {
190 inputProtocolFactory = new TJsonProtocol.Factory();
191 outputProtocolFactory = new TJsonProtocol.Factory();
192 processor = new Calculator.AsyncProcessor(handler);
193 }
194 break;
195 case Protocol.Multiplexed:
196 {
197 inputProtocolFactory = new TBinaryProtocol.Factory();
198 outputProtocolFactory = new TBinaryProtocol.Factory();
199
200 var calcHandler = new CalculatorAsyncHandler();
201 var calcProcessor = new Calculator.AsyncProcessor(calcHandler);
202
203 var sharedServiceHandler = new SharedServiceAsyncHandler();
204 var sharedServiceProcessor = new SharedService.AsyncProcessor(sharedServiceHandler);
205
206 var multiplexedProcessor = new TMultiplexedProcessor();
207 multiplexedProcessor.RegisterProcessor(nameof(Calculator), calcProcessor);
208 multiplexedProcessor.RegisterProcessor(nameof(SharedService), sharedServiceProcessor);
209
210 processor = multiplexedProcessor;
211 }
212 break;
213 default:
214 throw new ArgumentOutOfRangeException(nameof(protocol), protocol, null);
215 }
216
217 try
218 {
219 Logger.LogInformation(
220 $"Selected TAsyncServer with {serverTransport} transport, {processor} processor and {inputProtocolFactory} protocol factories");
221
Jens Geyerb11f63c2019-03-14 21:12:38 +0100222 var fabric = ServiceCollection.BuildServiceProvider().GetService<ILoggerFactory>();
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100223 var server = new TSimpleAsyncServer(processor, serverTransport, inputProtocolFactory, outputProtocolFactory, fabric);
224
225 Logger.LogInformation("Starting the server...");
226 await server.ServeAsync(cancellationToken);
227 }
228 catch (Exception x)
229 {
230 Logger.LogInformation(x.ToString());
231 }
232 }
233
234 private static X509Certificate2 GetCertificate()
235 {
236 // due to files location in net core better to take certs from top folder
237 var certFile = GetCertPath(Directory.GetParent(Directory.GetCurrentDirectory()));
238 return new X509Certificate2(certFile, "ThriftTest");
239 }
240
241 private static string GetCertPath(DirectoryInfo di, int maxCount = 6)
242 {
243 var topDir = di;
244 var certFile =
245 topDir.EnumerateFiles("ThriftTest.pfx", SearchOption.AllDirectories)
246 .FirstOrDefault();
247 if (certFile == null)
248 {
249 if (maxCount == 0)
250 throw new FileNotFoundException("Cannot find file in directories");
251 return GetCertPath(di.Parent, maxCount - 1);
252 }
253
254 return certFile.FullName;
255 }
256
257 private static X509Certificate LocalCertificateSelectionCallback(object sender,
258 string targetHost, X509CertificateCollection localCertificates,
259 X509Certificate remoteCertificate, string[] acceptableIssuers)
260 {
261 return GetCertificate();
262 }
263
264 private static bool ClientCertValidator(object sender, X509Certificate certificate,
265 X509Chain chain, SslPolicyErrors sslPolicyErrors)
266 {
267 return true;
268 }
269
270 private enum Transport
271 {
272 Tcp,
273 TcpBuffered,
274 NamedPipe,
275 Http,
276 TcpTls,
277 Framed
278 }
279
280 private enum Protocol
281 {
282 Binary,
283 Compact,
284 Json,
285 Multiplexed
286 }
287
288 public class HttpServerSample
289 {
290 public void Run(CancellationToken cancellationToken)
291 {
292 var config = new ConfigurationBuilder()
293 .AddEnvironmentVariables(prefix: "ASPNETCORE_")
294 .Build();
295
296 var host = new WebHostBuilder()
297 .UseConfiguration(config)
298 .UseKestrel()
299 .UseUrls("http://localhost:9090")
300 .UseContentRoot(Directory.GetCurrentDirectory())
301 .UseStartup<Startup>()
Jens Geyerb11f63c2019-03-14 21:12:38 +0100302 .ConfigureLogging((ctx,logging) => ConfigureLogging(logging))
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100303 .Build();
304
Jens Geyerb11f63c2019-03-14 21:12:38 +0100305 Logger.LogTrace("test");
306 Logger.LogCritical("test");
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100307 host.RunAsync(cancellationToken).GetAwaiter().GetResult();
308 }
309
310 public class Startup
311 {
312 public Startup(IHostingEnvironment env)
313 {
314 var builder = new ConfigurationBuilder()
315 .SetBasePath(env.ContentRootPath)
316 .AddEnvironmentVariables();
317
318 Configuration = builder.Build();
319 }
320
321 public IConfigurationRoot Configuration { get; }
322
323 // This method gets called by the runtime. Use this method to add services to the container.
324 public void ConfigureServices(IServiceCollection services)
325 {
326 services.AddTransient<Calculator.IAsync, CalculatorAsyncHandler>();
327 services.AddTransient<ITAsyncProcessor, Calculator.AsyncProcessor>();
328 services.AddTransient<THttpServerTransport, THttpServerTransport>();
329 }
330
331 // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
Jens Geyerb11f63c2019-03-14 21:12:38 +0100332 public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
Jens Geyeraa0c8b32019-01-28 23:27:45 +0100333 {
334 app.UseMiddleware<THttpServerTransport>();
335 }
336 }
337 }
338
339 public class CalculatorAsyncHandler : Calculator.IAsync
340 {
341 private readonly Dictionary<int, SharedStruct> _log = new Dictionary<int, SharedStruct>();
342
343 public CalculatorAsyncHandler()
344 {
345 }
346
347 public async Task<SharedStruct> getStructAsync(int key,
348 CancellationToken cancellationToken)
349 {
350 Logger.LogInformation("GetStructAsync({0})", key);
351 return await Task.FromResult(_log[key]);
352 }
353
354 public async Task pingAsync(CancellationToken cancellationToken)
355 {
356 Logger.LogInformation("PingAsync()");
357 await Task.CompletedTask;
358 }
359
360 public async Task<int> addAsync(int num1, int num2, CancellationToken cancellationToken)
361 {
362 Logger.LogInformation($"AddAsync({num1},{num2})");
363 return await Task.FromResult(num1 + num2);
364 }
365
366 public async Task<int> calculateAsync(int logid, Work w, CancellationToken cancellationToken)
367 {
368 Logger.LogInformation($"CalculateAsync({logid}, [{w.Op},{w.Num1},{w.Num2}])");
369
370 var val = 0;
371 switch (w.Op)
372 {
373 case Operation.ADD:
374 val = w.Num1 + w.Num2;
375 break;
376
377 case Operation.SUBTRACT:
378 val = w.Num1 - w.Num2;
379 break;
380
381 case Operation.MULTIPLY:
382 val = w.Num1 * w.Num2;
383 break;
384
385 case Operation.DIVIDE:
386 if (w.Num2 == 0)
387 {
388 var io = new InvalidOperation
389 {
390 WhatOp = (int) w.Op,
391 Why = "Cannot divide by 0"
392 };
393
394 throw io;
395 }
396 val = w.Num1 / w.Num2;
397 break;
398
399 default:
400 {
401 var io = new InvalidOperation
402 {
403 WhatOp = (int) w.Op,
404 Why = "Unknown operation"
405 };
406
407 throw io;
408 }
409 }
410
411 var entry = new SharedStruct
412 {
413 Key = logid,
414 Value = val.ToString()
415 };
416
417 _log[logid] = entry;
418
419 return await Task.FromResult(val);
420 }
421
422 public async Task zipAsync(CancellationToken cancellationToken)
423 {
424 Logger.LogInformation("ZipAsync() with delay 100mc");
425 await Task.Delay(100, CancellationToken.None);
426 }
427 }
428
429 public class SharedServiceAsyncHandler : SharedService.IAsync
430 {
431 public async Task<SharedStruct> getStructAsync(int key, CancellationToken cancellationToken)
432 {
433 Logger.LogInformation("GetStructAsync({0})", key);
434 return await Task.FromResult(new SharedStruct()
435 {
436 Key = key,
437 Value = "GetStructAsync"
438 });
439 }
440 }
441 }
442}