Move appbuilder and service collection to Jellyfin.Server
This commit is contained in:
parent
111b46599a
commit
27e3cf1558
|
@ -47,7 +47,6 @@ using Emby.Server.Implementations.Session;
|
|||
using Emby.Server.Implementations.SocketSharp;
|
||||
using Emby.Server.Implementations.TV;
|
||||
using Emby.Server.Implementations.Updates;
|
||||
using Jellyfin.Api.Extensions;
|
||||
using MediaBrowser.Api;
|
||||
using MediaBrowser.Common;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
|
@ -232,7 +231,7 @@ namespace Emby.Server.Implementations
|
|||
}
|
||||
}
|
||||
|
||||
protected IServiceProvider _serviceProvider;
|
||||
public IServiceProvider ServiceProvider;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the server configuration manager.
|
||||
|
@ -461,7 +460,7 @@ namespace Emby.Server.Implementations
|
|||
/// <param name="type">The type.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object CreateInstance(Type type)
|
||||
=> ActivatorUtilities.CreateInstance(_serviceProvider, type);
|
||||
=> ActivatorUtilities.CreateInstance(ServiceProvider, type);
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance of type and resolves all constructor dependencies
|
||||
|
@ -469,7 +468,7 @@ namespace Emby.Server.Implementations
|
|||
/// /// <typeparam name="T">The type.</typeparam>
|
||||
/// <returns>T.</returns>
|
||||
public T CreateInstance<T>()
|
||||
=> ActivatorUtilities.CreateInstance<T>(_serviceProvider);
|
||||
=> ActivatorUtilities.CreateInstance<T>(ServiceProvider);
|
||||
|
||||
/// <summary>
|
||||
/// Creates the instance safe.
|
||||
|
@ -481,7 +480,7 @@ namespace Emby.Server.Implementations
|
|||
try
|
||||
{
|
||||
Logger.LogDebug("Creating instance of {Type}", type);
|
||||
return ActivatorUtilities.CreateInstance(_serviceProvider, type);
|
||||
return ActivatorUtilities.CreateInstance(ServiceProvider, type);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -495,7 +494,7 @@ namespace Emby.Server.Implementations
|
|||
/// </summary>
|
||||
/// <typeparam name="T">The type</typeparam>
|
||||
/// <returns>``0.</returns>
|
||||
public T Resolve<T>() => _serviceProvider.GetService<T>();
|
||||
public T Resolve<T>() => ServiceProvider.GetService<T>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the export types.
|
||||
|
@ -611,93 +610,14 @@ namespace Emby.Server.Implementations
|
|||
|
||||
await RegisterResources(serviceCollection).ConfigureAwait(false);
|
||||
|
||||
string contentRoot = ServerConfigurationManager.Configuration.DashboardSourcePath;
|
||||
if (string.IsNullOrEmpty(contentRoot))
|
||||
ContentRoot = ServerConfigurationManager.Configuration.DashboardSourcePath;
|
||||
if (string.IsNullOrEmpty(ContentRoot))
|
||||
{
|
||||
contentRoot = ServerConfigurationManager.ApplicationPaths.WebPath;
|
||||
}
|
||||
|
||||
var host = new WebHostBuilder()
|
||||
.UseKestrel(options =>
|
||||
{
|
||||
var addresses = ServerConfigurationManager
|
||||
.Configuration
|
||||
.LocalNetworkAddresses
|
||||
.Select(NormalizeConfiguredLocalAddress)
|
||||
.Where(i => i != null)
|
||||
.ToList();
|
||||
if (addresses.Any())
|
||||
{
|
||||
foreach (var address in addresses)
|
||||
{
|
||||
Logger.LogInformation("Kestrel listening on {ipaddr}", address);
|
||||
options.Listen(address, HttpPort);
|
||||
|
||||
if (EnableHttps && Certificate != null)
|
||||
{
|
||||
options.Listen(address, HttpsPort, listenOptions => listenOptions.UseHttps(Certificate));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.LogInformation("Kestrel listening on all interfaces");
|
||||
options.ListenAnyIP(HttpPort);
|
||||
|
||||
if (EnableHttps && Certificate != null)
|
||||
{
|
||||
options.ListenAnyIP(HttpsPort, listenOptions => listenOptions.UseHttps(Certificate));
|
||||
}
|
||||
}
|
||||
})
|
||||
.UseContentRoot(contentRoot)
|
||||
.ConfigureServices(services =>
|
||||
{
|
||||
services.AddResponseCompression();
|
||||
services.AddHttpContextAccessor();
|
||||
services.AddJellyfinApi(ServerConfigurationManager.Configuration.BaseUrl.TrimStart('/'));
|
||||
|
||||
services.AddJellyfinApiSwagger();
|
||||
|
||||
// configure custom legacy authentication
|
||||
services.AddCustomAuthentication();
|
||||
|
||||
services.AddJellyfinApiAuthorization();
|
||||
|
||||
// Merge the external ServiceCollection into ASP.NET DI
|
||||
services.TryAdd(serviceCollection);
|
||||
})
|
||||
.Configure(app =>
|
||||
{
|
||||
app.UseWebSockets();
|
||||
|
||||
app.UseResponseCompression();
|
||||
|
||||
// TODO app.UseMiddleware<WebSocketMiddleware>();
|
||||
app.Use(ExecuteWebsocketHandlerAsync);
|
||||
|
||||
// TODO use when old API is removed: app.UseAuthentication();
|
||||
app.UseJellyfinApiSwagger();
|
||||
app.UseMvc();
|
||||
app.Use(ExecuteHttpHandlerAsync);
|
||||
})
|
||||
.Build();
|
||||
|
||||
_serviceProvider = host.Services;
|
||||
FindParts();
|
||||
|
||||
try
|
||||
{
|
||||
await host.StartAsync().ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
Logger.LogError("Kestrel failed to start! This is most likely due to an invalid address or port bind - correct your bind configuration in system.xml and try again.");
|
||||
throw;
|
||||
ContentRoot = ServerConfigurationManager.ApplicationPaths.WebPath;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ExecuteWebsocketHandlerAsync(HttpContext context, Func<Task> next)
|
||||
public async Task ExecuteWebsocketHandlerAsync(HttpContext context, Func<Task> next)
|
||||
{
|
||||
if (!context.WebSockets.IsWebSocketRequest)
|
||||
{
|
||||
|
@ -708,7 +628,7 @@ namespace Emby.Server.Implementations
|
|||
await HttpServer.ProcessWebSocketRequest(context).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task ExecuteHttpHandlerAsync(HttpContext context, Func<Task> next)
|
||||
public async Task ExecuteHttpHandlerAsync(HttpContext context, Func<Task> next)
|
||||
{
|
||||
if (context.WebSockets.IsWebSocketRequest)
|
||||
{
|
||||
|
@ -1090,9 +1010,9 @@ namespace Emby.Server.Implementations
|
|||
/// <summary>
|
||||
/// Finds the parts.
|
||||
/// </summary>
|
||||
protected void FindParts()
|
||||
public void FindParts()
|
||||
{
|
||||
InstallationManager = _serviceProvider.GetService<IInstallationManager>();
|
||||
InstallationManager = ServiceProvider.GetService<IInstallationManager>();
|
||||
InstallationManager.PluginInstalled += PluginInstalled;
|
||||
|
||||
if (!ServerConfigurationManager.Configuration.IsPortAuthorized)
|
||||
|
@ -1221,7 +1141,7 @@ namespace Emby.Server.Implementations
|
|||
|
||||
private CertificateInfo CertificateInfo { get; set; }
|
||||
|
||||
protected X509Certificate2 Certificate { get; private set; }
|
||||
public X509Certificate2 Certificate { get; private set; }
|
||||
|
||||
private IEnumerable<string> GetUrlPrefixes()
|
||||
{
|
||||
|
@ -1605,7 +1525,7 @@ namespace Emby.Server.Implementations
|
|||
return resultList;
|
||||
}
|
||||
|
||||
private IPAddress NormalizeConfiguredLocalAddress(string address)
|
||||
public IPAddress NormalizeConfiguredLocalAddress(string address)
|
||||
{
|
||||
var index = address.Trim('/').IndexOf('/');
|
||||
|
||||
|
@ -1685,6 +1605,8 @@ namespace Emby.Server.Implementations
|
|||
|
||||
public int HttpsPort { get; private set; }
|
||||
|
||||
public string ContentRoot { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Shuts down.
|
||||
/// </summary>
|
||||
|
|
|
@ -18,7 +18,6 @@ using MediaBrowser.Model.Events;
|
|||
using MediaBrowser.Model.Serialization;
|
||||
using MediaBrowser.Model.Services;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Internal;
|
||||
using Microsoft.AspNetCore.WebUtilities;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
@ -164,7 +163,7 @@ namespace Emby.Server.Implementations.HttpServer
|
|||
{
|
||||
OnReceive = ProcessWebSocketMessageReceived,
|
||||
Url = e.Url,
|
||||
QueryString = e.QueryString ?? new QueryCollection()
|
||||
QueryString = e.QueryString
|
||||
};
|
||||
|
||||
connection.Closed += OnConnectionClosed;
|
||||
|
|
|
@ -4,7 +4,6 @@ using MediaBrowser.Controller.Net;
|
|||
using MediaBrowser.Controller.Session;
|
||||
using MediaBrowser.Model.Events;
|
||||
using MediaBrowser.Model.Serialization;
|
||||
using MediaBrowser.Model.Services;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
|
@ -67,7 +66,7 @@ namespace Emby.Server.Implementations.Session
|
|||
{
|
||||
if (queryString == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(queryString));
|
||||
return null;
|
||||
}
|
||||
|
||||
var token = queryString["api_key"];
|
||||
|
@ -75,6 +74,7 @@ namespace Emby.Server.Implementations.Session
|
|||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var deviceId = queryString["deviceId"];
|
||||
return _sessionManager.GetSessionByAuthenticationToken(token, deviceId, remoteEndpoint);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Jellyfin.Api.Models.Startup;
|
||||
using Jellyfin.Api.Models.StartupDtos;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
namespace Jellyfin.Api.Models.Startup
|
||||
namespace Jellyfin.Api.Models.StartupDtos
|
||||
{
|
||||
/// <summary>
|
||||
/// The startup configuration DTO.
|
|
@ -1,4 +1,4 @@
|
|||
namespace Jellyfin.Api.Models.Startup
|
||||
namespace Jellyfin.Api.Models.StartupDtos
|
||||
{
|
||||
/// <summary>
|
||||
/// The startup user DTO.
|
|
@ -1,6 +1,6 @@
|
|||
using Microsoft.AspNetCore.Builder;
|
||||
|
||||
namespace Jellyfin.Api.Extensions
|
||||
namespace Jellyfin.Server.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Extensions for adding API specific functionality to the application pipeline.
|
|
@ -1,15 +1,14 @@
|
|||
using Jellyfin.Api;
|
||||
using Jellyfin.Api.Auth;
|
||||
using Jellyfin.Api.Auth.FirstTimeSetupOrElevatedPolicy;
|
||||
using Jellyfin.Api.Auth.RequiresElevationPolicy;
|
||||
using Jellyfin.Api.Controllers;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Authorization;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.OpenApi.Models;
|
||||
|
||||
namespace Jellyfin.Api.Extensions
|
||||
namespace Jellyfin.Server.Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// API specific extensions for the service collection.
|
||||
|
@ -65,14 +64,8 @@ namespace Jellyfin.Api.Extensions
|
|||
{
|
||||
return serviceCollection.AddMvc(opts =>
|
||||
{
|
||||
var policy = new AuthorizationPolicyBuilder()
|
||||
.RequireAuthenticatedUser()
|
||||
.Build();
|
||||
opts.Filters.Add(new AuthorizeFilter(policy));
|
||||
opts.EnableEndpointRouting = false;
|
||||
opts.UseGeneralRoutePrefix(baseUrl);
|
||||
})
|
||||
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
|
||||
|
||||
// Clear app parts to avoid other assemblies being picked up
|
||||
.ConfigureApplicationPartManager(a => a.ApplicationParts.Clear())
|
|
@ -20,6 +20,10 @@
|
|||
<EmbeddedResource Include="Resources/Configuration/*" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Code analyzers-->
|
||||
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.4" />
|
||||
|
|
|
@ -18,8 +18,10 @@ using Jellyfin.Drawing.Skia;
|
|||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Controller.Drawing;
|
||||
using MediaBrowser.Model.Globalization;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Serilog;
|
||||
using Serilog.Extensions.Logging;
|
||||
|
@ -164,7 +166,24 @@ namespace Jellyfin.Server
|
|||
appConfig);
|
||||
try
|
||||
{
|
||||
await appHost.InitAsync(new ServiceCollection()).ConfigureAwait(false);
|
||||
ServiceCollection serviceCollection = new ServiceCollection();
|
||||
await appHost.InitAsync(serviceCollection).ConfigureAwait(false);
|
||||
|
||||
var host = CreateWebHostBuilder(appHost, serviceCollection).Build();
|
||||
|
||||
// A bit hacky to re-use service provider since ASP.NET doesn't allow a custom service collection.
|
||||
appHost.ServiceProvider = host.Services;
|
||||
appHost.FindParts();
|
||||
|
||||
try
|
||||
{
|
||||
await host.StartAsync().ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
_logger.LogError("Kestrel failed to start! This is most likely due to an invalid address or port bind - correct your bind configuration in system.xml and try again.");
|
||||
throw;
|
||||
}
|
||||
|
||||
appHost.ImageProcessor.ImageEncoder = GetImageEncoder(appPaths, appHost.LocalizationManager);
|
||||
|
||||
|
@ -196,6 +215,55 @@ namespace Jellyfin.Server
|
|||
}
|
||||
}
|
||||
|
||||
private static IWebHostBuilder CreateWebHostBuilder(ApplicationHost appHost, IServiceCollection serviceCollection)
|
||||
{
|
||||
return new WebHostBuilder()
|
||||
.UseKestrel(options =>
|
||||
{
|
||||
var addresses = appHost.ServerConfigurationManager
|
||||
.Configuration
|
||||
.LocalNetworkAddresses
|
||||
.Select(appHost.NormalizeConfiguredLocalAddress)
|
||||
.Where(i => i != null)
|
||||
.ToList();
|
||||
if (addresses.Any())
|
||||
{
|
||||
foreach (var address in addresses)
|
||||
{
|
||||
_logger.LogInformation("Kestrel listening on {ipaddr}", address);
|
||||
options.Listen(address, appHost.HttpPort);
|
||||
|
||||
if (appHost.EnableHttps && appHost.Certificate != null)
|
||||
{
|
||||
options.Listen(
|
||||
address,
|
||||
appHost.HttpsPort,
|
||||
listenOptions => listenOptions.UseHttps(appHost.Certificate));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogInformation("Kestrel listening on all interfaces");
|
||||
options.ListenAnyIP(appHost.HttpPort);
|
||||
|
||||
if (appHost.EnableHttps && appHost.Certificate != null)
|
||||
{
|
||||
options.ListenAnyIP(
|
||||
appHost.HttpsPort,
|
||||
listenOptions => listenOptions.UseHttps(appHost.Certificate));
|
||||
}
|
||||
}
|
||||
})
|
||||
.UseContentRoot(appHost.ContentRoot)
|
||||
.ConfigureServices(services =>
|
||||
{
|
||||
// Merge the external ServiceCollection into ASP.NET DI
|
||||
services.TryAdd(serviceCollection);
|
||||
})
|
||||
.UseStartup<Startup>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create the data, config and log paths from the variety of inputs(command line args,
|
||||
/// environment variables) or decide on what default to use. For Windows it's %AppPath%
|
||||
|
|
81
Jellyfin.Server/Startup.cs
Normal file
81
Jellyfin.Server/Startup.cs
Normal file
|
@ -0,0 +1,81 @@
|
|||
using Jellyfin.Server.Extensions;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
namespace Jellyfin.Server
|
||||
{
|
||||
/// <summary>
|
||||
/// Startup configuration for the Kestrel webhost.
|
||||
/// </summary>
|
||||
public class Startup
|
||||
{
|
||||
private readonly IServerConfigurationManager _serverConfigurationManager;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Startup" /> class.
|
||||
/// </summary>
|
||||
/// <param name="serverConfigurationManager">The server configuration manager.</param>
|
||||
public Startup(IServerConfigurationManager serverConfigurationManager)
|
||||
{
|
||||
_serverConfigurationManager = serverConfigurationManager;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configures the service collection for the webhost.
|
||||
/// </summary>
|
||||
/// <param name="services">The service collection.</param>
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services.AddResponseCompression();
|
||||
services.AddHttpContextAccessor();
|
||||
services.AddJellyfinApi(_serverConfigurationManager.Configuration.BaseUrl.TrimStart('/'));
|
||||
|
||||
services.AddJellyfinApiSwagger();
|
||||
|
||||
// configure custom legacy authentication
|
||||
services.AddCustomAuthentication();
|
||||
|
||||
services.AddJellyfinApiAuthorization();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configures the app builder for the webhost.
|
||||
/// </summary>
|
||||
/// <param name="app">The application builder.</param>
|
||||
/// <param name="env">The webhost environment.</param>
|
||||
/// <param name="serverApplicationHost">The server application host.</param>
|
||||
public void Configure(
|
||||
IApplicationBuilder app,
|
||||
IWebHostEnvironment env,
|
||||
IServerApplicationHost serverApplicationHost)
|
||||
{
|
||||
if (env.IsDevelopment())
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
}
|
||||
|
||||
app.UseWebSockets();
|
||||
|
||||
app.UseResponseCompression();
|
||||
|
||||
// TODO app.UseMiddleware<WebSocketMiddleware>();
|
||||
app.Use(serverApplicationHost.ExecuteWebsocketHandlerAsync);
|
||||
|
||||
// TODO use when old API is removed: app.UseAuthentication();
|
||||
app.UseJellyfinApiSwagger();
|
||||
app.UseRouting();
|
||||
app.UseAuthorization();
|
||||
app.UseEndpoints(endpoints =>
|
||||
{
|
||||
endpoints.MapControllers();
|
||||
});
|
||||
|
||||
app.Use(serverApplicationHost.ExecuteHttpHandlerAsync);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@ using System.Threading;
|
|||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Common;
|
||||
using MediaBrowser.Model.System;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace MediaBrowser.Controller
|
||||
{
|
||||
|
@ -87,5 +88,9 @@ namespace MediaBrowser.Controller
|
|||
|
||||
string ExpandVirtualPath(string path);
|
||||
string ReverseVirtualPath(string path);
|
||||
|
||||
Task ExecuteHttpHandlerAsync(HttpContext context, Func<Task> next);
|
||||
|
||||
Task ExecuteWebsocketHandlerAsync(HttpContext context, Func<Task> next);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user