< Summary - Jellyfin

Information
Class: Jellyfin.Server.Startup
Assembly: jellyfin
File(s): /srv/git/jellyfin/Jellyfin.Server/Startup.cs
Line coverage
100%
Covered lines: 153
Uncovered lines: 0
Coverable lines: 153
Total lines: 238
Line coverage: 100%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100 7/22/2025 - 12:11:20 AM Line coverage: 100% (154/154) Total lines: 24310/28/2025 - 12:11:27 AM Line coverage: 100% (153/153) Total lines: 238

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
ConfigureServices(...)100%11100%
Configure(...)100%11100%

File(s)

/srv/git/jellyfin/Jellyfin.Server/Startup.cs

#LineLine coverage
 1using System;
 2using System.IO;
 3using System.Net;
 4using System.Net.Http;
 5using System.Net.Http.Headers;
 6using System.Net.Mime;
 7using System.Text;
 8using Emby.Server.Implementations.EntryPoints;
 9using Jellyfin.Api.Middleware;
 10using Jellyfin.Database.Implementations;
 11using Jellyfin.LiveTv.Extensions;
 12using Jellyfin.LiveTv.Recordings;
 13using Jellyfin.MediaEncoding.Hls.Extensions;
 14using Jellyfin.Networking;
 15using Jellyfin.Networking.HappyEyeballs;
 16using Jellyfin.Server.Extensions;
 17using Jellyfin.Server.HealthChecks;
 18using Jellyfin.Server.Implementations.Extensions;
 19using MediaBrowser.Common.Net;
 20using MediaBrowser.Controller.Configuration;
 21using MediaBrowser.Controller.Extensions;
 22using MediaBrowser.XbmcMetadata;
 23using Microsoft.AspNetCore.Builder;
 24using Microsoft.AspNetCore.Hosting;
 25using Microsoft.AspNetCore.StaticFiles;
 26using Microsoft.Extensions.Configuration;
 27using Microsoft.Extensions.DependencyInjection;
 28using Microsoft.Extensions.FileProviders;
 29using Microsoft.Extensions.Hosting;
 30using Microsoft.Extensions.Primitives;
 31using Prometheus;
 32
 33namespace Jellyfin.Server
 34{
 35    /// <summary>
 36    /// Startup configuration for the Kestrel webhost.
 37    /// </summary>
 38    public class Startup
 39    {
 40        private readonly CoreAppHost _serverApplicationHost;
 41        private readonly IConfiguration _configuration;
 42        private readonly IServerConfigurationManager _serverConfigurationManager;
 43
 44        /// <summary>
 45        /// Initializes a new instance of the <see cref="Startup" /> class.
 46        /// </summary>
 47        /// <param name="appHost">The server application host.</param>
 48        /// <param name="configuration">The used Configuration.</param>
 49        public Startup(CoreAppHost appHost, IConfiguration configuration)
 50        {
 2151            _serverApplicationHost = appHost;
 2152            _configuration = configuration;
 2153            _serverConfigurationManager = appHost.ConfigurationManager;
 2154        }
 55
 56        /// <summary>
 57        /// Configures the service collection for the webhost.
 58        /// </summary>
 59        /// <param name="services">The service collection.</param>
 60        public void ConfigureServices(IServiceCollection services)
 61        {
 2162            services.AddResponseCompression();
 2163            services.AddHttpContextAccessor();
 2164            services.AddHttpsRedirection(options =>
 2165            {
 2166                options.HttpsPort = _serverApplicationHost.HttpsPort;
 2167            });
 68
 2169            services.AddJellyfinApi(_serverApplicationHost.GetApiPluginAssemblies(), _serverConfigurationManager.GetNetw
 2170            services.AddJellyfinDbContext(_serverApplicationHost.ConfigurationManager, _configuration);
 2171            services.AddJellyfinApiSwagger();
 72
 73            // configure custom legacy authentication
 2174            services.AddCustomAuthentication();
 75
 2176            services.AddJellyfinApiAuthorization();
 77
 2178            var productHeader = new ProductInfoHeaderValue(
 2179                _serverApplicationHost.Name.Replace(' ', '-'),
 2180                _serverApplicationHost.ApplicationVersionString);
 2181            var acceptJsonHeader = new MediaTypeWithQualityHeaderValue(MediaTypeNames.Application.Json, 1.0);
 2182            var acceptXmlHeader = new MediaTypeWithQualityHeaderValue(MediaTypeNames.Application.Xml, 0.9);
 2183            var acceptAnyHeader = new MediaTypeWithQualityHeaderValue("*/*", 0.8);
 2184            Func<IServiceProvider, HttpMessageHandler> eyeballsHttpClientHandlerDelegate = (_) => new SocketsHttpHandler
 2185            {
 2186                AutomaticDecompression = DecompressionMethods.All,
 2187                RequestHeaderEncodingSelector = (_, _) => Encoding.UTF8,
 2188                ConnectCallback = HttpClientExtension.OnConnect
 2189            };
 90
 2191            Func<IServiceProvider, HttpMessageHandler> defaultHttpClientHandlerDelegate = (_) => new SocketsHttpHandler(
 2192            {
 2193                AutomaticDecompression = DecompressionMethods.All,
 2194                RequestHeaderEncodingSelector = (_, _) => Encoding.UTF8
 2195            };
 96
 2197            services.AddHttpClient(NamedClient.Default, c =>
 2198                {
 2199                    c.DefaultRequestHeaders.UserAgent.Add(productHeader);
 21100                    c.DefaultRequestHeaders.Accept.Add(acceptJsonHeader);
 21101                    c.DefaultRequestHeaders.Accept.Add(acceptXmlHeader);
 21102                    c.DefaultRequestHeaders.Accept.Add(acceptAnyHeader);
 21103                })
 21104                .ConfigurePrimaryHttpMessageHandler(eyeballsHttpClientHandlerDelegate);
 105
 21106            services.AddHttpClient(NamedClient.MusicBrainz, c =>
 21107                {
 21108                    c.DefaultRequestHeaders.UserAgent.Add(productHeader);
 21109                    c.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue($"({_serverApplicationHost.Applicat
 21110                    c.DefaultRequestHeaders.Accept.Add(acceptXmlHeader);
 21111                    c.DefaultRequestHeaders.Accept.Add(acceptAnyHeader);
 21112                })
 21113                .ConfigurePrimaryHttpMessageHandler(eyeballsHttpClientHandlerDelegate);
 114
 21115            services.AddHttpClient(NamedClient.DirectIp, c =>
 21116                {
 21117                    c.DefaultRequestHeaders.UserAgent.Add(productHeader);
 21118                    c.DefaultRequestHeaders.Accept.Add(acceptJsonHeader);
 21119                    c.DefaultRequestHeaders.Accept.Add(acceptXmlHeader);
 21120                    c.DefaultRequestHeaders.Accept.Add(acceptAnyHeader);
 21121                })
 21122                .ConfigurePrimaryHttpMessageHandler(defaultHttpClientHandlerDelegate);
 123
 21124            services.AddHealthChecks()
 21125                .AddCheck<DbContextFactoryHealthCheck<JellyfinDbContext>>(nameof(JellyfinDbContext));
 126
 21127            services.AddHlsPlaylistGenerator();
 21128            services.AddLiveTvServices();
 129
 21130            services.AddHostedService<RecordingsHost>();
 21131            services.AddHostedService<AutoDiscoveryHost>();
 21132            services.AddHostedService<NfoUserDataSaver>();
 21133            services.AddHostedService<LibraryChangedNotifier>();
 21134            services.AddHostedService<UserDataChangeNotifier>();
 21135            services.AddHostedService<RecordingNotifier>();
 21136        }
 137
 138        /// <summary>
 139        /// Configures the app builder for the webhost.
 140        /// </summary>
 141        /// <param name="app">The application builder.</param>
 142        /// <param name="env">The webhost environment.</param>
 143        /// <param name="appConfig">The application config.</param>
 144        public void Configure(
 145            IApplicationBuilder app,
 146            IWebHostEnvironment env,
 147            IConfiguration appConfig)
 148        {
 21149            app.UseBaseUrlRedirection();
 150
 151            // Wrap rest of configuration so everything only listens on BaseUrl.
 21152            var config = _serverConfigurationManager.GetNetworkConfiguration();
 21153            app.Map(config.BaseUrl, mainApp =>
 21154            {
 21155                if (env.IsDevelopment())
 21156                {
 21157                    mainApp.UseDeveloperExceptionPage();
 21158                }
 21159
 21160                mainApp.UseForwardedHeaders();
 21161                mainApp.UseMiddleware<ExceptionMiddleware>();
 21162
 21163                mainApp.UseMiddleware<ResponseTimeMiddleware>();
 21164
 21165                mainApp.UseWebSockets();
 21166
 21167                mainApp.UseResponseCompression();
 21168
 21169                mainApp.UseCors();
 21170
 21171                if (config.RequireHttps && _serverApplicationHost.ListenWithHttps)
 21172                {
 21173                    mainApp.UseHttpsRedirection();
 21174                }
 21175
 21176                // This must be injected before any path related middleware.
 21177                mainApp.UsePathTrim();
 21178
 21179                if (appConfig.HostWebClient())
 21180                {
 21181                    var extensionProvider = new FileExtensionContentTypeProvider();
 21182
 21183                    // subtitles octopus requires .data, .mem files.
 21184                    extensionProvider.Mappings.Add(".data", MediaTypeNames.Application.Octet);
 21185                    extensionProvider.Mappings.Add(".mem", MediaTypeNames.Application.Octet);
 21186                    mainApp.UseDefaultFiles(new DefaultFilesOptions
 21187                    {
 21188                        FileProvider = new PhysicalFileProvider(_serverConfigurationManager.ApplicationPaths.WebPath),
 21189                        RequestPath = "/web"
 21190                    });
 21191                    mainApp.UseStaticFiles(new StaticFileOptions
 21192                    {
 21193                        FileProvider = new PhysicalFileProvider(_serverConfigurationManager.ApplicationPaths.WebPath),
 21194                        RequestPath = "/web",
 21195                        ContentTypeProvider = extensionProvider,
 21196                        OnPrepareResponse = (context) =>
 21197                        {
 21198                            if (Path.GetFileName(context.File.Name).Equals("index.html", StringComparison.Ordinal))
 21199                            {
 21200                                context.Context.Response.Headers.CacheControl = new StringValues("no-cache");
 21201                            }
 21202                        }
 21203                    });
 21204
 21205                    mainApp.UseRobotsRedirection();
 21206                }
 21207
 21208                mainApp.UseStaticFiles();
 21209                mainApp.UseAuthentication();
 21210                mainApp.UseJellyfinApiSwagger(_serverConfigurationManager);
 21211                mainApp.UseQueryStringDecoding();
 21212                mainApp.UseRouting();
 21213                mainApp.UseAuthorization();
 21214
 21215                mainApp.UseIPBasedAccessValidation();
 21216                mainApp.UseWebSocketHandler();
 21217                mainApp.UseServerStartupMessage();
 21218
 21219                if (_serverConfigurationManager.Configuration.EnableMetrics)
 21220                {
 21221                    // Must be registered after any middleware that could change HTTP response codes or the data will be
 21222                    mainApp.UseHttpMetrics();
 21223                }
 21224
 21225                mainApp.UseEndpoints(endpoints =>
 21226                {
 21227                    endpoints.MapControllers();
 21228                    if (_serverConfigurationManager.Configuration.EnableMetrics)
 21229                    {
 21230                        endpoints.MapMetrics();
 21231                    }
 21232
 21233                    endpoints.MapHealthChecks("/health");
 21234                });
 21235            });
 21236        }
 237    }
 238}