< Summary - Jellyfin

Information
Class: Jellyfin.Server.ServerSetupApp.SetupServer
Assembly: jellyfin
File(s): /srv/git/jellyfin/Jellyfin.Server/ServerSetupApp/SetupServer.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 8
Coverable lines: 8
Total lines: 172
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 4
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
Dispose()0%2040%
ThrowIfDisposed()100%210%
CheckHealthAsync(...)100%210%

File(s)

/srv/git/jellyfin/Jellyfin.Server/ServerSetupApp/SetupServer.cs

#LineLine coverage
 1using System;
 2using System.IO;
 3using System.Linq;
 4using System.Net;
 5using System.Threading;
 6using System.Threading.Tasks;
 7using MediaBrowser.Common.Configuration;
 8using MediaBrowser.Common.Net;
 9using MediaBrowser.Controller;
 10using MediaBrowser.Model.System;
 11using Microsoft.AspNetCore.Builder;
 12using Microsoft.AspNetCore.Hosting;
 13using Microsoft.AspNetCore.Http;
 14using Microsoft.Extensions.DependencyInjection;
 15using Microsoft.Extensions.Diagnostics.HealthChecks;
 16using Microsoft.Extensions.Hosting;
 17
 18namespace Jellyfin.Server.ServerSetupApp;
 19
 20/// <summary>
 21/// Creates a fake application pipeline that will only exist for as long as the main app is not started.
 22/// </summary>
 23public sealed class SetupServer : IDisposable
 24{
 25    private IHost? _startupServer;
 26    private bool _disposed;
 27
 28    /// <summary>
 29    /// Starts the Bind-All Setup aspcore server to provide a reflection on the current core setup.
 30    /// </summary>
 31    /// <param name="networkManagerFactory">The networkmanager.</param>
 32    /// <param name="applicationPaths">The application paths.</param>
 33    /// <param name="serverApplicationHost">The servers application host.</param>
 34    /// <returns>A Task.</returns>
 35    public async Task RunAsync(
 36        Func<INetworkManager?> networkManagerFactory,
 37        IApplicationPaths applicationPaths,
 38        Func<IServerApplicationHost?> serverApplicationHost)
 39    {
 40        ThrowIfDisposed();
 41        _startupServer = Host.CreateDefaultBuilder()
 42            .UseConsoleLifetime()
 43            .ConfigureServices(serv =>
 44            {
 45                serv.AddHealthChecks()
 46                    .AddCheck<SetupHealthcheck>("StartupCheck");
 47            })
 48            .ConfigureWebHostDefaults(webHostBuilder =>
 49                    {
 50                        webHostBuilder
 51                                .UseKestrel()
 52                                .Configure(app =>
 53                                {
 54                                    app.UseHealthChecks("/health");
 55
 56                                    app.Map("/startup/logger", loggerRoute =>
 57                                    {
 58                                        loggerRoute.Run(async context =>
 59                                        {
 60                                            var networkManager = networkManagerFactory();
 61                                            if (context.Connection.RemoteIpAddress is null || networkManager is null || 
 62                                            {
 63                                                context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
 64                                                return;
 65                                            }
 66
 67                                            var logFilePath = new DirectoryInfo(applicationPaths.LogDirectoryPath)
 68                                                .EnumerateFiles()
 69                                                .OrderBy(f => f.CreationTimeUtc)
 70                                                .FirstOrDefault()
 71                                                ?.FullName;
 72                                            if (logFilePath is not null)
 73                                            {
 74                                                await context.Response.SendFileAsync(logFilePath, CancellationToken.None
 75                                            }
 76                                        });
 77                                    });
 78
 79                                    app.Map("/System/Info/Public", systemRoute =>
 80                                    {
 81                                        systemRoute.Run(async context =>
 82                                        {
 83                                            var jfApplicationHost = serverApplicationHost();
 84
 85                                            var retryCounter = 0;
 86                                            while (jfApplicationHost is null && retryCounter < 5)
 87                                            {
 88                                                await Task.Delay(500).ConfigureAwait(false);
 89                                                jfApplicationHost = serverApplicationHost();
 90                                                retryCounter++;
 91                                            }
 92
 93                                            if (jfApplicationHost is null)
 94                                            {
 95                                                context.Response.StatusCode = (int)HttpStatusCode.ServiceUnavailable;
 96                                                context.Response.Headers.RetryAfter = new Microsoft.Extensions.Primitive
 97                                                return;
 98                                            }
 99
 100                                            var sysInfo = new PublicSystemInfo
 101                                            {
 102                                                Version = jfApplicationHost.ApplicationVersionString,
 103                                                ProductName = jfApplicationHost.Name,
 104                                                Id = jfApplicationHost.SystemId,
 105                                                ServerName = jfApplicationHost.FriendlyName,
 106                                                LocalAddress = jfApplicationHost.GetSmartApiUrl(context.Request),
 107                                                StartupWizardCompleted = false
 108                                            };
 109
 110                                            await context.Response.WriteAsJsonAsync(sysInfo).ConfigureAwait(false);
 111                                        });
 112                                    });
 113
 114                                    app.Run((context) =>
 115                                    {
 116                                        context.Response.StatusCode = (int)HttpStatusCode.ServiceUnavailable;
 117                                        context.Response.Headers.RetryAfter = new Microsoft.Extensions.Primitives.String
 118                                        context.Response.WriteAsync("<p>Jellyfin Server still starting. Please wait.</p>
 119                                        var networkManager = networkManagerFactory();
 120                                        if (networkManager is not null && context.Connection.RemoteIpAddress is not null
 121                                        {
 122                                            context.Response.WriteAsync("<p>You can download the current logfiles <a hre
 123                                        }
 124
 125                                        return Task.CompletedTask;
 126                                    });
 127                                });
 128                    })
 129                    .Build();
 130        await _startupServer.StartAsync().ConfigureAwait(false);
 131    }
 132
 133    /// <summary>
 134    /// Stops the Setup server.
 135    /// </summary>
 136    /// <returns>A task. Duh.</returns>
 137    public async Task StopAsync()
 138    {
 139        ThrowIfDisposed();
 140        if (_startupServer is null)
 141        {
 142            throw new InvalidOperationException("Tried to stop a non existing startup server");
 143        }
 144
 145        await _startupServer.StopAsync().ConfigureAwait(false);
 146    }
 147
 148    /// <inheritdoc/>
 149    public void Dispose()
 150    {
 0151        if (_disposed)
 152        {
 0153            return;
 154        }
 155
 0156        _disposed = true;
 0157        _startupServer?.Dispose();
 0158    }
 159
 160    private void ThrowIfDisposed()
 161    {
 0162        ObjectDisposedException.ThrowIf(_disposed, this);
 0163    }
 164
 165    private class SetupHealthcheck : IHealthCheck
 166    {
 167        public Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken 
 168        {
 0169            return Task.FromResult(HealthCheckResult.Degraded("Server is still starting up."));
 170        }
 171    }
 172}