Skip to content

Commit

Permalink
feat: expose method to re-check connectivity in plugin abstraction
Browse files Browse the repository at this point in the history
  • Loading branch information
revam committed Nov 12, 2023
1 parent 8ae8f18 commit 0b9f4a3
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 106 deletions.
7 changes: 7 additions & 0 deletions Shoko.Plugin.Abstractions/Services/IConnectivityService.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@

using System;
using System.Threading.Tasks;
using Shoko.Plugin.Abstractions.Enums;

namespace Shoko.Plugin.Abstractions.Services
Expand Down Expand Up @@ -33,5 +34,11 @@ public interface IConnectivityService
/// Are we currently banned from using the AniDB UDP API?
/// </summary>
public bool IsAniDBUdpBanned { get; }

/// <summary>
/// Check for network availability now.
/// </summary>
/// <returns>The updated network availability status.</returns>
public Task<NetworkAvailability> CheckAvailability();
}
}
2 changes: 1 addition & 1 deletion Shoko.Plugin.Abstractions/Shoko.Plugin.Abstractions.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<RepositoryUrl>https://github.com/ShokoAnime/ShokoServer</RepositoryUrl>
<PackageTags>plugins, shoko, anime, metadata, tagging</PackageTags>
<PackageReleaseNotes>File Events</PackageReleaseNotes>
<Version>2.5.0-alpha1</Version>
<Version>2.5.0-alpha2</Version>
<Configurations>Debug;Release</Configurations>
<Platforms>AnyCPU;x64</Platforms>
</PropertyGroup>
Expand Down
94 changes: 6 additions & 88 deletions Shoko.Server/Scheduling/Jobs/ConnectivityMonitorJob.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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<ConnectivityMonitorJob> _logger;

private readonly ConnectivityService _connectivityService;

public ConnectivityMonitorJob(ISettingsProvider settingsProvider, IEnumerable<IConnectivityMonitor> connectivityMonitors, IConnectivityService connectivityService, ILogger<ConnectivityMonitorJob> 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<NetworkAvailability> 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
);
}
}
132 changes: 115 additions & 17 deletions Shoko.Server/Services/Connectivity/ConnectivityService.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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<ConnectivityService> _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;

Expand All @@ -29,7 +41,7 @@ public class ConnectivityService : IConnectivityService
public NetworkAvailability NetworkAvailability
{
get => _networkAvailability;
set
private set
{
var hasChanged = _networkAvailability != value;
_networkAvailability = value;
Expand All @@ -40,22 +52,25 @@ public NetworkAvailability NetworkAvailability

/// <inheritdoc/>
public bool IsAniDBUdpReachable =>
AnidbUdpHandler.IsNetworkAvailable;
_anidbUdpHandler.IsNetworkAvailable;

/// <inheritdoc/>
public bool IsAniDBHttpBanned =>
AnidbHttpHandler.IsBanned;
_anidbHttpHandler.IsBanned;

/// <inheritdoc/>
public bool IsAniDBUdpBanned =>
AnidbUdpHandler.IsBanned;
_anidbUdpHandler.IsBanned;

public ConnectivityService(IUDPConnectionHandler udpHandler, IHttpConnectionHandler httpHandler, CommandProcessorGeneral generalQueue, CommandProcessorImages imagesQueue)
public ConnectivityService(ILogger<ConnectivityService> logger, ISettingsProvider settingsProvider, IEnumerable<IConnectivityMonitor> 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;
}

Expand All @@ -64,16 +79,99 @@ public ConnectivityService(IUDPConnectionHandler udpHandler, IHttpConnectionHand
NetworkAvailabilityChanged -= OnNetworkAvailabilityChanged;
}

public async Task<NetworkAvailability> 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<NetworkAvailability> 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();
}
}

0 comments on commit 0b9f4a3

Please sign in to comment.