Skip to content

Commit

Permalink
Retry server list from the top on successful ping [VPNWIN-733]; Fix c…
Browse files Browse the repository at this point in the history
…onnection start issues
  • Loading branch information
eaproton committed Jun 23, 2021
1 parent 4ccfe3f commit e6594bc
Show file tree
Hide file tree
Showing 50 changed files with 904 additions and 279 deletions.
9 changes: 6 additions & 3 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ csharp_new_line_between_query_expression_clauses=true
# http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#csharp_prefer_simple_default_expression
csharp_prefer_simple_default_expression=false:error

# IDE0063: Use simple 'using' statement
csharp_prefer_simple_using_statement = false

# http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#csharp_preserve_single_line_blocks
csharp_preserve_single_line_blocks=true

Expand Down Expand Up @@ -131,10 +134,10 @@ csharp_style_expression_bodied_indexers=true:error
csharp_style_expression_bodied_methods=true

# http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#csharp_style_expression_bodied_operators
csharp_style_expression_bodied_operators=true:error
csharp_style_expression_bodied_operators=false:suggestion

# http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#csharp_style_expression_bodied_properties
csharp_style_expression_bodied_properties=true:error
csharp_style_expression_bodied_properties=when_on_single_line:suggestion

# http://kent-boogaart.com/blog/editorconfig-reference-for-c-developers#csharp_style_inlined_variable_declaration
csharp_style_inlined_variable_declaration=true:error
Expand Down Expand Up @@ -257,4 +260,4 @@ dotnet_naming_symbols.constant_fields.required_modifiers=const
dotnet_naming_style.pascal_case_style.capitalization=pascal_case

# ReSharper inspection severities
resharper_arrange_this_qualifier_highlighting=hint
resharper_arrange_this_qualifier_highlighting=hint
5 changes: 1 addition & 4 deletions src/ProtonVPN.App/ConnectingScreen/ConnectingViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,10 @@ internal class ConnectingViewModel : ViewModel, IVpnStateAware
private Server _failedServer;

private readonly IVpnManager _vpnManager;
private readonly IVpnReconnector _vpnReconnector;

public ConnectingViewModel(IVpnManager vpnManager,
IVpnReconnector vpnReconnector)
public ConnectingViewModel(IVpnManager vpnManager)
{
_vpnManager = vpnManager;
_vpnReconnector = vpnReconnector;

DisconnectCommand = new RelayCommand(DisconnectAction);
}
Expand Down
3 changes: 1 addition & 2 deletions src/ProtonVPN.App/Core/Ioc/AppModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -273,14 +273,13 @@ protected override void Load(ContainerBuilder builder)
builder.RegisterType<ReconnectState>().AsImplementedInterfaces().AsSelf().SingleInstance();
builder.RegisterType<SettingsBuilder>().SingleInstance();
builder.RegisterType<SystemState>().As<ISystemState>().SingleInstance();
builder.RegisterType<ReconnectManager>().AsImplementedInterfaces().SingleInstance();
builder.RegisterType<ReconnectManager>().AsImplementedInterfaces().AsSelf().SingleInstance();
builder.Register(c => new VpnInfoChecker(
c.Resolve<Common.Configuration.Config>(),
c.Resolve<IEventAggregator>(),
c.Resolve<IApiClient>(),
c.Resolve<IUserStorage>(),
c.Resolve<IScheduler>())).SingleInstance();
builder.RegisterType<VpnReconnector>().AsImplementedInterfaces().SingleInstance();
builder.RegisterType<ReportFieldProvider>().As<IReportFieldProvider>().SingleInstance();
builder.RegisterType<PlanDowngradeHandler>().AsImplementedInterfaces().AsSelf().SingleInstance();
builder.RegisterType<StreamingServicesUpdater>().AsImplementedInterfaces().AsSelf().SingleInstance();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public SettingsServiceClient(ServiceChannelFactory channelFactory)

public void Apply(SettingsContract settings)
{
using (var channel = _channelFactory.Create<ISettingsContract>(Endpoint))
using (ServiceChannel<ISettingsContract> channel = _channelFactory.Create<ISettingsContract>(Endpoint))
{
channel.Proxy.Apply(settings);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
* along with ProtonVPN. If not, see <https://www.gnu.org/licenses/>.
*/

using System.Collections.Generic;
using ProtonVPN.Core.Profiles;
using ProtonVPN.Core.Servers;
using ProtonVPN.Core.Servers.Models;
Expand All @@ -26,5 +27,6 @@ namespace ProtonVPN.Core.Service.Vpn
public interface ISimilarServerCandidatesGenerator
{
ServerCandidates Generate(bool isToIncludeOriginalServer, Server originalServer = null, Profile baseProfile = null);
IList<Server> GenerateList(bool isToIncludeOriginalServer, Server originalServer = null, Profile baseProfile = null);
}
}
3 changes: 3 additions & 0 deletions src/ProtonVPN.App/Core/Service/Vpn/IVpnConnector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
*/

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using ProtonVPN.Core.Profiles;
using ProtonVPN.Core.Servers;
Expand All @@ -33,6 +34,8 @@ public interface IVpnConnector
Server LastServer { get; }
VpnState State { get; }
bool NetworkBlocked { get; }

Task<IList<Server>> GetSortedAndValidQuickConnectServersAsync(int? maxServers = null);

Task QuickConnectAsync(int? maxServers = null);
Task ConnectToBestProfileAsync(Profile profile, Profile fallbackProfile = null, int? maxServers = null);
Expand Down
32 changes: 22 additions & 10 deletions src/ProtonVPN.App/Core/Service/Vpn/ReconnectManager.cs
Original file line number Diff line number Diff line change
@@ -1,43 +1,43 @@
using System;
using System.Net.Http;
using System.Threading.Tasks;
using ProtonVPN.Common.Logging;
using ProtonVPN.Common.Threading;
using ProtonVPN.Common.Vpn;
using ProtonVPN.Core.Api;
using ProtonVPN.Core.Api.Contracts;
using ProtonVPN.Core.Profiles;
using ProtonVPN.Core.Servers;
using ProtonVPN.Core.Servers.Models;
using ProtonVPN.Core.Settings;
using ProtonVPN.Core.Vpn;

namespace ProtonVPN.Core.Service.Vpn
{
internal class ReconnectManager : IVpnStateAware
public class ReconnectManager : IVpnStateAware
{
private VpnState _state;
private readonly IApiClient _apiClient;
private readonly ProfileManager _profileManager;
private readonly IVpnManager _vpnManager;
private readonly ISchedulerTimer _timer;
private readonly ServerManager _serverManager;
private readonly IServerUpdater _serverUpdater;
private readonly ILogger _logger;
private readonly IAppSettings _appSettings;

public ReconnectManager(
IAppSettings appSettings,
IApiClient apiClient,
ProfileManager profileManager,
ServerManager serverManager,
IVpnManager vpnManager,
IScheduler scheduler,
IServerUpdater serverUpdater)
IServerUpdater serverUpdater,
ILogger logger)
{
_appSettings = appSettings;
_serverUpdater = serverUpdater;
_logger = logger;
_serverManager = serverManager;
_vpnManager = vpnManager;
_profileManager = profileManager;
_apiClient = apiClient;

_timer = scheduler.Timer();
Expand Down Expand Up @@ -74,13 +74,19 @@ public Task OnVpnStateChanged(VpnStateChangedEventArgs e)

private async void OnTimerTick(object sender, EventArgs e)
{
if (!await ServerOffline())
await CheckIfCurrentServerIsOnlineAsync();
}

public async Task CheckIfCurrentServerIsOnlineAsync()
{
if (!_timer.IsEnabled || !await ServerOffline())
{
return;
}

_serverManager.MarkServerUnderMaintenance(_state.Server.ExitIp);
await _serverUpdater.Update();
_logger.Info($"Reconnecting due to server {_state.Server.Name} ({_state.Server.ExitIp}) being no longer available.");
await _vpnManager.ReconnectAsync(new VpnReconnectionSettings { IsToReconnectIfDisconnected = true, IsToExcludeLastServer = true });
}

Expand All @@ -89,7 +95,7 @@ private async Task<bool> ServerOffline()
PhysicalServer server = _serverManager.GetPhysicalServerByServer(_state.Server);
if (server == null)
{
//Server removed from api
_logger.Info($"The server {_state.Server.Name} ({_state.Server.ExitIp}) was removed from the API.");
return true;
}

Expand All @@ -101,7 +107,13 @@ private async Task<bool> ServerOffline()
return false;
}

return result.Value.Server.Status == 0;
bool isServerUnderMaintenance = result.Value.Server.Status == 0;
if (isServerUnderMaintenance)
{
_logger.Info($"The server {_state.Server.Name} ({_state.Server.ExitIp}) is under maintenance.");
}

return isServerUnderMaintenance;
}
catch (HttpRequestException)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,21 @@ public SimilarServerCandidatesGenerator(
_appSettings = appSettings;
}

public ServerCandidates Generate(bool isToIncludeOriginalServer, Server originalServer = null, Profile baseProfile = null)
public ServerCandidates Generate(bool isToIncludeOriginalServer,
Server originalServer = null, Profile baseProfile = null)
{
IList<Server> similarServers = GenerateList(isToIncludeOriginalServer, originalServer, baseProfile);
return CreateServerCandidates(similarServers);
}

public IList<Server> GenerateList(bool isToIncludeOriginalServer,
Server originalServer = null, Profile baseProfile = null)
{
if (originalServer == null || originalServer.IsEmpty())
{
if (baseProfile == null)
{
return null;
return new List<Server>();
}

if (baseProfile.ProfileType == ProfileType.Custom)
Expand All @@ -66,8 +74,8 @@ public ServerCandidates Generate(bool isToIncludeOriginalServer, Server original
}

baseProfile = ValidateProfile(baseProfile, originalServer);

return GetServerCandidates(isToIncludeOriginalServer, originalServer, baseProfile);
return GetSimilarServers(isToIncludeOriginalServer, originalServer, baseProfile);
}

private Profile ValidateProfile(Profile profile, Server originalServer)
Expand All @@ -93,7 +101,7 @@ private Profile ValidateProfile(Profile profile, Server originalServer)

private Profile CreateProfile(Server originalServer)
{
return new Profile()
return new()
{
Protocol = Protocol.Auto,
ProfileType = ProfileType.Fastest,
Expand All @@ -105,7 +113,7 @@ private Profile CreateProfile(Server originalServer)
};
}

private ServerCandidates GetServerCandidates(bool isToIncludeOriginalServer, Server originalServer, Profile baseProfile)
private IList<Server> GetSimilarServers(bool isToIncludeOriginalServer, Server originalServer, Profile baseProfile)
{
IList<Server> servers = new List<Server>();
servers.AddIfNotNull(GetOriginalServerIfRequired(isToIncludeOriginalServer, originalServer));
Expand All @@ -119,24 +127,26 @@ private ServerCandidates GetServerCandidates(bool isToIncludeOriginalServer, Ser
else
{
servers.AddIfNotNull(GetBestServerForSameCityTierFeatures(originalServer, servers, baseProfile));
servers.AddIfNotNull(GetBestServerForSameCountryTierFeatures(originalServer, servers, baseProfile));
servers.AddIfNotNull(GetBestServerForSameCountryFeaturesAndDifferentCity(originalServer, servers, baseProfile));
if (baseProfile.Features > 0)
{
servers.AddIfNotNull(GetBestServerForSameFeaturesAndDifferentCountry(originalServer, servers, baseProfile));
servers.AddIfNotNull(GetBestServerForSameCityAndNoFeatures(originalServer, servers, baseProfile));
servers.AddIfNotNull(GetBestServerForSameCountryAndNoFeaturesAndDifferentCity(originalServer, servers, baseProfile));
}
servers.AddIfNotNull(GetBestServerForSameCityAndNoFeatures(originalServer, servers, baseProfile));
servers.AddIfNotNull(GetBestServerForSameCountryAndNoFeaturesAndDifferentCity(originalServer, servers, baseProfile));
servers.AddIfNotNull(GetBestServerForNoFeaturesAndDifferentCountry(originalServer, servers, baseProfile));
}

return _serverCandidatesFactory.ServerCandidates((IReadOnlyCollection<Server>)servers);
return servers;
}

private Server GetOriginalServerIfRequired(bool isToIncludeOriginalServer, Server originalServer)
{
return isToIncludeOriginalServer &&
originalServer != null &&
originalServer.IsSecureCore() == _appSettings.SecureCore
!originalServer.IsNullOrEmpty() &&
originalServer.IsSecureCore() == _appSettings.SecureCore &&
originalServer.Online()
? originalServer
: null;
}
Expand Down Expand Up @@ -203,6 +213,18 @@ private Server GetBestServerForSameCityTierFeatures(Server originalServer, IList
return GetBestServer(originalServer, excludedServers, profile);
}

private Server GetBestServerForSameCountryTierFeatures(Server originalServer, IList<Server> excludedServers, Profile baseProfile)
{
Profile profile = new()
{
ProfileType = ProfileType.Fastest,
Features = baseProfile.Features,
CountryCode = baseProfile.CountryCode,
ExactTier = baseProfile.ExactTier
};
return GetBestServer(originalServer, excludedServers, profile);
}

private Server GetBestServerForSameCountryFeaturesAndDifferentCity(Server originalServer, IList<Server> excludedServers, Profile baseProfile)
{
Profile profile = new()
Expand Down Expand Up @@ -256,5 +278,10 @@ private Server GetBestServerForNoFeaturesAndDifferentCountry(Server originalServ
};
return GetBestServer(originalServer, excludedServers, profile, s => s.ExitCountry != baseProfile.CountryCode);
}

private ServerCandidates CreateServerCandidates(IList<Server> servers)
{
return _serverCandidatesFactory.ServerCandidates((IReadOnlyCollection<Server>)servers);
}
}
}
18 changes: 18 additions & 0 deletions src/ProtonVPN.App/Core/Service/Vpn/VpnConnector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ProtonVPN.Common.OS.Net;
using ProtonVPN.Common.OS.Net.NetworkInterface;
Expand Down Expand Up @@ -78,6 +79,23 @@ public VpnConnector(
LastServerCandidates = _profileConnector.ServerCandidates(null);
}

public async Task<IList<Server>> GetSortedAndValidQuickConnectServersAsync(int? maxServers = null)
{
Profile profile = await GetQuickConnectProfileAsync();
IList<Profile> profiles = CreateProfilePreferenceList(profile);
VpnManagerProfileCandidates profileCandidates = GetBestProfileCandidates(profiles);

IEnumerable<Server> sortedServers = _profileConnector.SortServers(
_profileConnector.Servers(profileCandidates.Candidates),
profileCandidates.Profile.ProfileType);
if (maxServers.HasValue)
{
sortedServers = sortedServers.Take(maxServers.Value);
}

return sortedServers.ToList();
}

public async Task QuickConnectAsync(int? maxServers = null)
{
Profile profile = await GetQuickConnectProfileAsync();
Expand Down
Loading

0 comments on commit e6594bc

Please sign in to comment.