diff --git a/Shoko.Plugin.Abstractions/Services/IConnectivityService.cs b/Shoko.Plugin.Abstractions/Services/IConnectivityService.cs index 13847a111..c1acc11b0 100644 --- a/Shoko.Plugin.Abstractions/Services/IConnectivityService.cs +++ b/Shoko.Plugin.Abstractions/Services/IConnectivityService.cs @@ -1,5 +1,6 @@ using System; +using System.Threading.Tasks; using Shoko.Plugin.Abstractions.Enums; namespace Shoko.Plugin.Abstractions.Services @@ -33,5 +34,11 @@ public interface IConnectivityService /// Are we currently banned from using the AniDB UDP API? /// public bool IsAniDBUdpBanned { get; } + + /// + /// Check for network availability now. + /// + /// The updated network availability status. + public Task CheckAvailability(); } } diff --git a/Shoko.Plugin.Abstractions/Shoko.Plugin.Abstractions.csproj b/Shoko.Plugin.Abstractions/Shoko.Plugin.Abstractions.csproj index cdebf3b9b..636bff6e5 100644 --- a/Shoko.Plugin.Abstractions/Shoko.Plugin.Abstractions.csproj +++ b/Shoko.Plugin.Abstractions/Shoko.Plugin.Abstractions.csproj @@ -12,7 +12,7 @@ https://github.com/ShokoAnime/ShokoServer plugins, shoko, anime, metadata, tagging File Events - 2.5.0-alpha1 + 2.5.0-alpha2 Debug;Release AnyCPU;x64 diff --git a/Shoko.Server/Scheduling/Jobs/ConnectivityMonitorJob.cs b/Shoko.Server/Scheduling/Jobs/ConnectivityMonitorJob.cs index 438f8350a..d401b2fcc 100644 --- a/Shoko.Server/Scheduling/Jobs/ConnectivityMonitorJob.cs +++ b/Shoko.Server/Scheduling/Jobs/ConnectivityMonitorJob.cs @@ -3,17 +3,11 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; -using System.Linq; -using System.Net.NetworkInformation; using System.Threading.Tasks; -using Microsoft.Extensions.Logging; using Quartz; using QuartzJobFactory.Attributes; -using Shoko.Plugin.Abstractions.Enums; using Shoko.Plugin.Abstractions.Services; using Shoko.Server.Services.Connectivity; -using Shoko.Server.Settings; namespace Shoko.Server.Scheduling.Jobs; @@ -22,101 +16,25 @@ namespace Shoko.Server.Scheduling.Jobs; [DisallowConcurrentExecution] public class ConnectivityMonitorJob : IJob { - private readonly ISettingsProvider _settingsProvider; - private readonly IConnectivityMonitor[] _connectivityMonitors; - private readonly ILogger _logger; - private readonly ConnectivityService _connectivityService; - public ConnectivityMonitorJob(ISettingsProvider settingsProvider, IEnumerable connectivityMonitors, IConnectivityService connectivityService, ILogger logger) + public ConnectivityMonitorJob(IConnectivityService connectivityService) { - _settingsProvider = settingsProvider; - _connectivityMonitors = connectivityMonitors.ToArray(); _connectivityService = connectivityService as ConnectivityService; - _logger = logger; } protected ConnectivityMonitorJob() { } public async Task Execute(IJobExecutionContext context) { - try + try { - var localNetwork = GetLANConnectivity(); - if (localNetwork != NetworkAvailability.LocalOnly) - { - _connectivityService.NetworkAvailability = localNetwork; - return; - } - - var wideNetwork = await GetWANConnectivity(); - _connectivityService.NetworkAvailability = wideNetwork; - } catch (Exception ex) { - // do you want the job to refire? - throw new JobExecutionException(msg: "", refireImmediately: false, cause: ex); - } - } - - private NetworkAvailability GetLANConnectivity() - { - _logger.LogInformation("Checking LAN Connectivity…"); - // Get all active network interfaces - var networkInterfaces = NetworkInterface.GetAllNetworkInterfaces() - .Where(n => n.OperationalStatus == OperationalStatus.Up) - .ToList(); - - if (!networkInterfaces.Any()) - return NetworkAvailability.NoInterfaces; - - foreach (var netInterface in networkInterfaces) - { - var properties = netInterface.GetIPProperties(); - if (properties == null) - continue; - - var defaultGateway = properties.GatewayAddresses - .Select(g => g.Address) - .FirstOrDefault(); - if (defaultGateway == null) - continue; - - _logger.LogInformation("Found a local gateway to use."); - return NetworkAvailability.LocalOnly; + await _connectivityService.CheckAvailability(); } - - _logger.LogInformation("No local gateway was found."); - return NetworkAvailability.NoGateways; - } - - private async Task GetWANConnectivity() - { - var currentlyDisabledMonitors = _settingsProvider.GetSettings().Connectivity.DisabledMonitorServices - .ToHashSet(); - var monitors = _connectivityMonitors - .Where(monitor => !currentlyDisabledMonitors.Contains(monitor.Service, StringComparer.InvariantCultureIgnoreCase)) - .ToList(); - if (monitors.Count == 0) + catch (Exception ex) { - _logger.LogInformation("Skipped checking WAN Connectivity."); - return NetworkAvailability.Internet; + // do you want the job to refire? + throw new JobExecutionException(msg: "", refireImmediately: false, cause: ex); } - - _logger.LogInformation("Checking WAN Connectivity…"); - await Parallel.ForEachAsync(monitors, async (monitor, token) => - { - await monitor.ExecuteCheckAsync(token); - }); - - var connectedCount = monitors.Count(a => a.HasConnected); - _logger.LogInformation("Successfully connected to {Count}/{Total} internet service endpoints.", connectedCount, - monitors.Count); - - return connectedCount > 0 ? ( - // We managed to connect to WAN, either partially or fully. - connectedCount == monitors.Count ? NetworkAvailability.Internet : NetworkAvailability.PartialInternet - ) : ( - // We didn't manage to connect to WAN, but we reached the gateway - NetworkAvailability.LocalOnly - ); } } diff --git a/Shoko.Server/Services/Connectivity/ConnectivityService.cs b/Shoko.Server/Services/Connectivity/ConnectivityService.cs index 8f69284e5..b0a59abdf 100644 --- a/Shoko.Server/Services/Connectivity/ConnectivityService.cs +++ b/Shoko.Server/Services/Connectivity/ConnectivityService.cs @@ -1,5 +1,9 @@ using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.NetworkInformation; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; using Shoko.Plugin.Abstractions; using Shoko.Plugin.Abstractions.Enums; using Shoko.Plugin.Abstractions.Extensions; @@ -8,17 +12,25 @@ using Shoko.Server.Commands.Generic; using Shoko.Server.Providers.AniDB.Interfaces; +using ISettingsProvider = Shoko.Server.Settings.ISettingsProvider; + namespace Shoko.Server.Services.Connectivity; public class ConnectivityService : IConnectivityService { - private readonly IUDPConnectionHandler AnidbUdpHandler; + private readonly ILogger _logger; + + private readonly ISettingsProvider _settingsProvider; + + private readonly IConnectivityMonitor[] _connectivityMonitors; + + private readonly IUDPConnectionHandler _anidbUdpHandler; - private readonly IHttpConnectionHandler AnidbHttpHandler; + private readonly IHttpConnectionHandler _anidbHttpHandler; - private readonly CommandProcessor GeneralQueue; + private readonly CommandProcessor _generalQueue; - private readonly CommandProcessor ImagesQueue; + private readonly CommandProcessor _imagesQueue; private NetworkAvailability _networkAvailability { get; set; } = NetworkAvailability.NoInterfaces; @@ -29,7 +41,7 @@ public class ConnectivityService : IConnectivityService public NetworkAvailability NetworkAvailability { get => _networkAvailability; - set + private set { var hasChanged = _networkAvailability != value; _networkAvailability = value; @@ -40,22 +52,25 @@ public NetworkAvailability NetworkAvailability /// public bool IsAniDBUdpReachable => - AnidbUdpHandler.IsNetworkAvailable; + _anidbUdpHandler.IsNetworkAvailable; /// public bool IsAniDBHttpBanned => - AnidbHttpHandler.IsBanned; + _anidbHttpHandler.IsBanned; /// public bool IsAniDBUdpBanned => - AnidbUdpHandler.IsBanned; + _anidbUdpHandler.IsBanned; - public ConnectivityService(IUDPConnectionHandler udpHandler, IHttpConnectionHandler httpHandler, CommandProcessorGeneral generalQueue, CommandProcessorImages imagesQueue) + public ConnectivityService(ILogger logger, ISettingsProvider settingsProvider, IEnumerable connectivityMonitors, IUDPConnectionHandler udpHandler, IHttpConnectionHandler httpHandler, CommandProcessorGeneral generalQueue, CommandProcessorImages imagesQueue) { - AnidbUdpHandler = udpHandler; - AnidbHttpHandler = httpHandler; - GeneralQueue = generalQueue; - ImagesQueue = imagesQueue; + _logger = logger; + _settingsProvider = settingsProvider; + _connectivityMonitors = connectivityMonitors.ToArray(); + _anidbUdpHandler = udpHandler; + _anidbHttpHandler = httpHandler; + _generalQueue = generalQueue; + _imagesQueue = imagesQueue; NetworkAvailabilityChanged += OnNetworkAvailabilityChanged; } @@ -64,16 +79,99 @@ public ConnectivityService(IUDPConnectionHandler udpHandler, IHttpConnectionHand NetworkAvailabilityChanged -= OnNetworkAvailabilityChanged; } + public async Task CheckAvailability() + { + try + { + var localNetwork = GetLANConnectivity(); + if (localNetwork != NetworkAvailability.LocalOnly) + { + return NetworkAvailability = localNetwork; + } + + var wideNetwork = await GetWANConnectivity(); + return NetworkAvailability = wideNetwork; + } + catch (Exception ex) + { + _logger.LogError(ex, "Failed to check network availability."); + return NetworkAvailability; + } + } + + private NetworkAvailability GetLANConnectivity() + { + _logger.LogInformation("Checking LAN Connectivity…"); + // Get all active network interfaces + var networkInterfaces = NetworkInterface.GetAllNetworkInterfaces() + .Where(n => n.OperationalStatus == OperationalStatus.Up) + .ToList(); + + if (!networkInterfaces.Any()) + return NetworkAvailability.NoInterfaces; + + foreach (var netInterface in networkInterfaces) + { + var properties = netInterface.GetIPProperties(); + if (properties == null) + continue; + + var defaultGateway = properties.GatewayAddresses + .Select(g => g.Address) + .FirstOrDefault(); + if (defaultGateway == null) + continue; + + _logger.LogInformation("Found a local gateway to use."); + return NetworkAvailability.LocalOnly; + } + + _logger.LogInformation("No local gateway was found."); + return NetworkAvailability.NoGateways; + } + + private async Task GetWANConnectivity() + { + var currentlyDisabledMonitors = _settingsProvider.GetSettings().Connectivity.DisabledMonitorServices + .ToHashSet(); + var monitors = _connectivityMonitors + .Where(monitor => !currentlyDisabledMonitors.Contains(monitor.Service, StringComparer.InvariantCultureIgnoreCase)) + .ToList(); + if (monitors.Count == 0) + { + _logger.LogInformation("Skipped checking WAN Connectivity."); + return NetworkAvailability.Internet; + } + + _logger.LogInformation("Checking WAN Connectivity…"); + await Parallel.ForEachAsync(monitors, async (monitor, token) => + { + await monitor.ExecuteCheckAsync(token); + }); + + var connectedCount = monitors.Count(a => a.HasConnected); + _logger.LogInformation("Successfully connected to {Count}/{Total} internet service endpoints.", connectedCount, + monitors.Count); + + return connectedCount > 0 ? ( + // We managed to connect to WAN, either partially or fully. + connectedCount == monitors.Count ? NetworkAvailability.Internet : NetworkAvailability.PartialInternet + ) : ( + // We didn't manage to connect to WAN, but we reached the gateway + NetworkAvailability.LocalOnly + ); + } + // Notify the queues that they can start again. private void OnNetworkAvailabilityChanged(object sender, NetworkAvailabilityChangedEventArgs eventArgs) { if (!eventArgs.NetworkAvailability.HasInternet()) return; - if (!GeneralQueue.Paused && GeneralQueue.QueueCount > 0) - GeneralQueue.NotifyOfNewCommand(); + if (!_generalQueue.Paused && _generalQueue.QueueCount > 0) + _generalQueue.NotifyOfNewCommand(); - if (!ImagesQueue.Paused && ImagesQueue.QueueCount > 0) - ImagesQueue.NotifyOfNewCommand(); + if (!_imagesQueue.Paused && _imagesQueue.QueueCount > 0) + _imagesQueue.NotifyOfNewCommand(); } }