using System.Net.Http.Headers ;
using System.Net.Mime ;
using Jellyfin.Networking.Configuration ;
using Jellyfin.Server.Extensions ;
using Jellyfin.Server.Implementations ;
using Jellyfin.Server.Middleware ;
using MediaBrowser.Common.Net ;
using MediaBrowser.Controller ;
using MediaBrowser.Controller.Configuration ;
using MediaBrowser.Controller.Extensions ;
using Microsoft.AspNetCore.Builder ;
using Microsoft.AspNetCore.Hosting ;
using Microsoft.AspNetCore.StaticFiles ;
using Microsoft.Extensions.Configuration ;
using Microsoft.Extensions.DependencyInjection ;
using Microsoft.Extensions.FileProviders ;
using Microsoft.Extensions.Hosting ;
using Prometheus ;
namespace Jellyfin.Server
{
/// <summary>
/// Startup configuration for the Kestrel webhost.
/// </summary>
public class Startup
{
private readonly IServerConfigurationManager _serverConfigurationManager ;
private readonly IServerApplicationHost _serverApplicationHost ;
/// <summary>
/// Initializes a new instance of the <see cref="Startup" /> class.
/// </summary>
/// <param name="serverConfigurationManager">The server configuration manager.</param>
/// <param name="serverApplicationHost">The server application host.</param>
public Startup (
IServerConfigurationManager serverConfigurationManager ,
IServerApplicationHost serverApplicationHost )
{
_serverConfigurationManager = serverConfigurationManager ;
_serverApplicationHost = serverApplicationHost ;
}
/// <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 . AddHttpsRedirection ( options = >
{
options . HttpsPort = _serverApplicationHost . HttpsPort ;
} ) ;
services . AddJellyfinApi ( _serverApplicationHost . GetApiPluginAssemblies ( ) , _serverConfigurationManager . GetNetworkConfiguration ( ) ) ;
services . AddJellyfinApiSwagger ( ) ;
// configure custom legacy authentication
services . AddCustomAuthentication ( ) ;
services . AddJellyfinApiAuthorization ( ) ;
var productHeader = new ProductInfoHeaderValue (
_serverApplicationHost . Name . Replace ( ' ' , '-' ) ,
_serverApplicationHost . ApplicationVersionString ) ;
var acceptJsonHeader = new MediaTypeWithQualityHeaderValue ( MediaTypeNames . Application . Json , 1.0 ) ;
var acceptXmlHeader = new MediaTypeWithQualityHeaderValue ( MediaTypeNames . Application . Xml , 0.9 ) ;
var acceptAnyHeader = new MediaTypeWithQualityHeaderValue ( "*/*" , 0.8 ) ;
services
. AddHttpClient ( NamedClient . Default , c = >
{
c . DefaultRequestHeaders . UserAgent . Add ( productHeader ) ;
c . DefaultRequestHeaders . Accept . Add ( acceptJsonHeader ) ;
c . DefaultRequestHeaders . Accept . Add ( acceptXmlHeader ) ;
c . DefaultRequestHeaders . Accept . Add ( acceptAnyHeader ) ;
} )
. ConfigurePrimaryHttpMessageHandler ( x = > new DefaultHttpClientHandler ( ) ) ;
services . AddHttpClient ( NamedClient . MusicBrainz , c = >
{
c . DefaultRequestHeaders . UserAgent . Add ( productHeader ) ;
c . DefaultRequestHeaders . UserAgent . Add ( new ProductInfoHeaderValue ( $"({_serverApplicationHost.ApplicationUserAgentAddress})" ) ) ;
c . DefaultRequestHeaders . Accept . Add ( acceptXmlHeader ) ;
c . DefaultRequestHeaders . Accept . Add ( acceptAnyHeader ) ;
} )
. ConfigurePrimaryHttpMessageHandler ( x = > new DefaultHttpClientHandler ( ) ) ;
services . AddHealthChecks ( )
. AddDbContextCheck < JellyfinDb > ( ) ;
}
/// <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="appConfig">The application config.</param>
public void Configure (
IApplicationBuilder app ,
IWebHostEnvironment env ,
IConfiguration appConfig )
{
app . UseBaseUrlRedirection ( ) ;
// Wrap rest of configuration so everything only listens on BaseUrl.
var config = _serverConfigurationManager . GetNetworkConfiguration ( ) ;
app . Map ( config . BaseUrl , mainApp = >
{
if ( env . IsDevelopment ( ) )
{
mainApp . UseDeveloperExceptionPage ( ) ;
}
mainApp . UseForwardedHeaders ( ) ;
mainApp . UseMiddleware < ExceptionMiddleware > ( ) ;
mainApp . UseMiddleware < ResponseTimeMiddleware > ( ) ;
mainApp . UseWebSockets ( ) ;
mainApp . UseResponseCompression ( ) ;
mainApp . UseCors ( ) ;
if ( config . RequireHttps & & _serverApplicationHost . ListenWithHttps )
{
mainApp . UseHttpsRedirection ( ) ;
}
// This must be injected before any path related middleware.
mainApp . UsePathTrim ( ) ;
mainApp . UseStaticFiles ( ) ;
if ( appConfig . HostWebClient ( ) )
{
var extensionProvider = new FileExtensionContentTypeProvider ( ) ;
// subtitles octopus requires .data, .mem files.
extensionProvider . Mappings . Add ( ".data" , MediaTypeNames . Application . Octet ) ;
extensionProvider . Mappings . Add ( ".mem" , MediaTypeNames . Application . Octet ) ;
mainApp . UseStaticFiles ( new StaticFileOptions
{
FileProvider = new PhysicalFileProvider ( _serverConfigurationManager . ApplicationPaths . WebPath ) ,
RequestPath = "/web" ,
ContentTypeProvider = extensionProvider
} ) ;
mainApp . UseRobotsRedirection ( ) ;
}
mainApp . UseAuthentication ( ) ;
mainApp . UseJellyfinApiSwagger ( _serverConfigurationManager ) ;
mainApp . UseRouting ( ) ;
mainApp . UseAuthorization ( ) ;
mainApp . UseLanFiltering ( ) ;
mainApp . UseIpBasedAccessValidation ( ) ;
mainApp . UseWebSocketHandler ( ) ;
mainApp . UseServerStartupMessage ( ) ;
if ( _serverConfigurationManager . Configuration . EnableMetrics )
{
// Must be registered after any middleware that could change HTTP response codes or the data will be bad
mainApp . UseHttpMetrics ( ) ;
}
mainApp . UseEndpoints ( endpoints = >
{
endpoints . MapControllers ( ) ;
if ( _serverConfigurationManager . Configuration . EnableMetrics )
{
endpoints . MapMetrics ( "/metrics" ) ;
}
endpoints . MapHealthChecks ( "/health" ) ;
} ) ;
} ) ;
}
}
}