aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClaus Vium <clausvium@gmail.com>2019-11-24 15:27:58 +0100
committerClaus Vium <clausvium@gmail.com>2019-11-24 15:27:58 +0100
commit27e3cf15588f8ab8fe19aa611d79fa2ccd8ecda8 (patch)
treec422f33524f1c9629729a2a9262eef30f6a0b10a
parent111b46599a66e81a8449e777cccc516c06b7548d (diff)
Move appbuilder and service collection to Jellyfin.Server
-rw-r--r--Emby.Server.Implementations/ApplicationHost.cs110
-rw-r--r--Emby.Server.Implementations/HttpServer/HttpListenerHost.cs3
-rw-r--r--Emby.Server.Implementations/Session/SessionWebSocketListener.cs4
-rw-r--r--Jellyfin.Api/Controllers/StartupController.cs2
-rw-r--r--Jellyfin.Api/Models/StartupDtos/StartupConfigurationDto.cs (renamed from Jellyfin.Api/Models/Startup/StartupConfigurationDto.cs)2
-rw-r--r--Jellyfin.Api/Models/StartupDtos/StartupUserDto.cs (renamed from Jellyfin.Api/Models/Startup/StartupUserDto.cs)2
-rw-r--r--Jellyfin.Server/Extensions/ApiApplicationBuilderExtensions.cs (renamed from Jellyfin.Api/Extensions/ApiApplicationBuilderExtensions.cs)2
-rw-r--r--Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs (renamed from Jellyfin.Api/Extensions/ApiServiceCollectionExtensions.cs)11
-rw-r--r--Jellyfin.Server/Jellyfin.Server.csproj4
-rw-r--r--Jellyfin.Server/Program.cs70
-rw-r--r--Jellyfin.Server/Startup.cs81
-rw-r--r--MediaBrowser.Controller/IServerApplicationHost.cs5
12 files changed, 184 insertions, 112 deletions
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index c6cdd4786..3b9ea4121 100644
--- a/Emby.Server.Implementations/ApplicationHost.cs
+++ b/Emby.Server.Implementations/ApplicationHost.cs
@@ -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>
diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
index dc1a56e27..6dd016f8a 100644
--- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
+++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
@@ -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;
diff --git a/Emby.Server.Implementations/Session/SessionWebSocketListener.cs b/Emby.Server.Implementations/Session/SessionWebSocketListener.cs
index 63ec75762..930f2d35d 100644
--- a/Emby.Server.Implementations/Session/SessionWebSocketListener.cs
+++ b/Emby.Server.Implementations/Session/SessionWebSocketListener.cs
@@ -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);
}
diff --git a/Jellyfin.Api/Controllers/StartupController.cs b/Jellyfin.Api/Controllers/StartupController.cs
index 0e7d17a27..50f3dc83c 100644
--- a/Jellyfin.Api/Controllers/StartupController.cs
+++ b/Jellyfin.Api/Controllers/StartupController.cs
@@ -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;
diff --git a/Jellyfin.Api/Models/Startup/StartupConfigurationDto.cs b/Jellyfin.Api/Models/StartupDtos/StartupConfigurationDto.cs
index dac15e412..d048dad0a 100644
--- a/Jellyfin.Api/Models/Startup/StartupConfigurationDto.cs
+++ b/Jellyfin.Api/Models/StartupDtos/StartupConfigurationDto.cs
@@ -1,4 +1,4 @@
-namespace Jellyfin.Api.Models.Startup
+namespace Jellyfin.Api.Models.StartupDtos
{
/// <summary>
/// The startup configuration DTO.
diff --git a/Jellyfin.Api/Models/Startup/StartupUserDto.cs b/Jellyfin.Api/Models/StartupDtos/StartupUserDto.cs
index 7e890d76a..3a9348037 100644
--- a/Jellyfin.Api/Models/Startup/StartupUserDto.cs
+++ b/Jellyfin.Api/Models/StartupDtos/StartupUserDto.cs
@@ -1,4 +1,4 @@
-namespace Jellyfin.Api.Models.Startup
+namespace Jellyfin.Api.Models.StartupDtos
{
/// <summary>
/// The startup user DTO.
diff --git a/Jellyfin.Api/Extensions/ApiApplicationBuilderExtensions.cs b/Jellyfin.Server/Extensions/ApiApplicationBuilderExtensions.cs
index f70466ebe..db06eb455 100644
--- a/Jellyfin.Api/Extensions/ApiApplicationBuilderExtensions.cs
+++ b/Jellyfin.Server/Extensions/ApiApplicationBuilderExtensions.cs
@@ -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.
diff --git a/Jellyfin.Api/Extensions/ApiServiceCollectionExtensions.cs b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs
index 38f5f6d39..e5a8937e8 100644
--- a/Jellyfin.Api/Extensions/ApiServiceCollectionExtensions.cs
+++ b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs
@@ -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())
diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj
index 4238d7fe3..dc784becf 100644
--- a/Jellyfin.Server/Jellyfin.Server.csproj
+++ b/Jellyfin.Server/Jellyfin.Server.csproj
@@ -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" />
diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs
index e8bd0cd30..998f1125f 100644
--- a/Jellyfin.Server/Program.cs
+++ b/Jellyfin.Server/Program.cs
@@ -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%
diff --git a/Jellyfin.Server/Startup.cs b/Jellyfin.Server/Startup.cs
new file mode 100644
index 000000000..3ee5fb8b5
--- /dev/null
+++ b/Jellyfin.Server/Startup.cs
@@ -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);
+ }
+ }
+}
diff --git a/MediaBrowser.Controller/IServerApplicationHost.cs b/MediaBrowser.Controller/IServerApplicationHost.cs
index 61b2c15ae..b3c56bdd5 100644
--- a/MediaBrowser.Controller/IServerApplicationHost.cs
+++ b/MediaBrowser.Controller/IServerApplicationHost.cs
@@ -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);
}
}