using System; using MareSynchronos.API; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using MareSynchronosServer.Hubs; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Http.Connections; using Microsoft.AspNetCore.SignalR; using Microsoft.AspNetCore.Authorization; using AspNetCoreRateLimit; using MareSynchronosShared.Authentication; using MareSynchronosShared.Data; using MareSynchronosShared.Protos; using Grpc.Net.Client.Configuration; using Prometheus; using MareSynchronosShared.Metrics; using System.Collections.Generic; using MareSynchronosServer.Services; using MareSynchronosShared.Services; using System.Net.Http; using MareSynchronosServer.Utils; namespace MareSynchronosServer; public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } public void ConfigureServices(IServiceCollection services) { services.AddHttpContextAccessor(); services.Configure(Configuration.GetSection("IpRateLimiting")); services.Configure(Configuration.GetSection("IpRateLimitPolicies")); services.AddMemoryCache(); services.AddInMemoryRateLimiting(); services.AddSingleton(); services.AddSingleton(); services.AddTransient(_ => Configuration); var mareConfig = Configuration.GetRequiredSection("MareSynchronos"); var defaultMethodConfig = new MethodConfig { Names = { MethodName.Default }, RetryPolicy = new RetryPolicy { MaxAttempts = 100, InitialBackoff = TimeSpan.FromSeconds(1), MaxBackoff = TimeSpan.FromSeconds(5), BackoffMultiplier = 1.5, RetryableStatusCodes = { Grpc.Core.StatusCode.Unavailable } } }; services.AddSingleton(new MareMetrics(new List { MetricsAPI.CounterInitializedConnections, MetricsAPI.CounterUserPushData, MetricsAPI.CounterUserPushDataTo, MetricsAPI.CounterUsersRegisteredDeleted, }, new List { MetricsAPI.GaugeAuthorizedConnections, MetricsAPI.GaugeConnections, MetricsAPI.GaugePairs, MetricsAPI.GaugePairsPaused, MetricsAPI.GaugeAvailableIOWorkerThreads, MetricsAPI.GaugeAvailableWorkerThreads })); services.AddGrpcClient(c => { c.Address = new Uri(mareConfig.GetValue("ServiceAddress")); }).ConfigureChannel(c => { c.ServiceConfig = new ServiceConfig { MethodConfigs = { defaultMethodConfig } }; c.HttpHandler = new SocketsHttpHandler() { EnableMultipleHttp2Connections = true }; }); services.AddGrpcClient(c => { c.Address = new Uri(mareConfig.GetValue("StaticFileServiceAddress")); }).ConfigureChannel(c => { c.ServiceConfig = new ServiceConfig { MethodConfigs = { defaultMethodConfig } }; }); services.AddDbContextPool(options => { options.UseNpgsql(Configuration.GetConnectionString("DefaultConnection"), builder => { builder.MigrationsHistoryTable("_efmigrationshistory", "public"); builder.MigrationsAssembly("MareSynchronosShared"); }).UseSnakeCaseNamingConvention(); options.EnableThreadSafetyChecks(false); }, mareConfig.GetValue("DbContextPoolSize", 1024)); services.AddAuthentication(options => { options.DefaultScheme = SecretKeyGrpcAuthenticationHandler.AuthScheme; }).AddScheme(SecretKeyGrpcAuthenticationHandler.AuthScheme, _ => { }); services.AddAuthorization(options => options.FallbackPolicy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build()); services.AddSingleton(); var signalRServiceBuilder = services.AddSignalR(hubOptions => { hubOptions.MaximumReceiveMessageSize = long.MaxValue; hubOptions.EnableDetailedErrors = true; hubOptions.MaximumParallelInvocationsPerClient = 10; hubOptions.StreamBufferCapacity = 200; hubOptions.AddFilter(); }); // add redis related options var redis = mareConfig.GetValue("RedisConnectionString", string.Empty); if (!string.IsNullOrEmpty(redis)) { signalRServiceBuilder.AddStackExchangeRedis(redis, options => { options.Configuration.ChannelPrefix = "MareSynchronos"; }); services.AddStackExchangeRedisCache(opt => { opt.Configuration = redis; opt.InstanceName = "MareSynchronosCache:"; }); services.AddSingleton(); services.AddHostedService(p => p.GetService()); } else { services.AddSingleton(); services.AddHostedService(p => p.GetService()); } services.AddHostedService(provider => provider.GetService()); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseMigrationsEndPoint(); } else { app.UseExceptionHandler("/Error"); app.UseHsts(); } app.UseIpRateLimiting(); app.UseRouting(); app.UseWebSockets(); var metricServer = new KestrelMetricServer(4980); metricServer.Start(); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapHub(Api.Path, options => { options.ApplicationMaxBufferSize = 5242880; options.TransportMaxBufferSize = 5242880; options.Transports = HttpTransportType.WebSockets | HttpTransportType.ServerSentEvents | HttpTransportType.LongPolling; }); }); } }