/*
* Copyright (c) 2024 Proton AG
*
* This file is part of ProtonVPN.
*
* ProtonVPN is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ProtonVPN is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ProtonVPN. If not, see .
*/
using Grpc.Net.Client;
using ProtoBuf.Grpc.Client;
using ProtonVPN.Client.Settings.Contracts;
using ProtonVPN.IssueReporting.Contracts;
using ProtonVPN.Logging.Contracts;
using ProtonVPN.Logging.Contracts.Events.ProcessCommunicationLogs;
using ProtonVPN.ProcessCommunication.Contracts;
using ProtonVPN.ProcessCommunication.Contracts.Controllers;
namespace ProtonVPN.ProcessCommunication.Client;
public class GrpcClient : IGrpcClient
{
private readonly INamedPipesConnectionFactory _namedPipesConnectionFactory;
private readonly ILogger _logger;
private readonly IIssueReporter _issueReporter;
private readonly ISettings _settings;
private readonly object _lock = new();
private GrpcChannel _channel;
public IClientController ClientController { get; private set; }
public IUpdateController UpdateController { get; private set; }
public IVpnController VpnController { get; private set; }
public event EventHandler InvokingClientRestart;
public GrpcClient(INamedPipesConnectionFactory namedPipesConnectionFactory,
ILogger logger,
IIssueReporter issueReporter,
ISettings settings)
{
_namedPipesConnectionFactory = namedPipesConnectionFactory;
_logger = logger;
_issueReporter = issueReporter;
_settings = settings;
}
public void Stop()
{
_namedPipesConnectionFactory.Stop();
}
public void CreateIfPipeNameChanged()
{
if (_namedPipesConnectionFactory.HasPipeNameChanged())
{
Create();
}
}
public void Create()
{
lock (_lock)
{
_channel?.Dispose();
_channel = CreateChannel();
ClientController = _channel.CreateGrpcService();
UpdateController = _channel.CreateGrpcService();
VpnController = _channel.CreateGrpcService();
}
}
private GrpcChannel CreateChannel()
{
SocketsHttpHandler socketsHttpHandler = new()
{
ConnectCallback = _namedPipesConnectionFactory.ConnectAsync,
UseProxy = false,
Proxy = null
};
return GrpcChannel.ForAddress("http://localhost", new GrpcChannelOptions
{
HttpHandler = new ResponseHandler(_logger, _issueReporter, _settings, InvokingClientRestart, socketsHttpHandler)
});
}
public async Task GetServiceControllerOrThrowAsync(TimeSpan timeout) where T : IServiceController
{
CancellationTokenSource cts = new(timeout);
IServiceController serviceController = GetServiceController();
try
{
while (serviceController is null && !cts.IsCancellationRequested)
{
await Task.Delay(TimeSpan.FromMilliseconds(200), cts.Token);
serviceController = GetServiceController();
}
}
catch
{
}
if (serviceController is null)
{
string errorMessage = $"Failed to get the Service Controller within the allotted time '{timeout}'.";
_logger.Error(errorMessage);
throw new TimeoutException(errorMessage);
}
return (T)serviceController;
}
public IServiceController GetServiceController()
{
if (typeof(T).IsAssignableFrom(typeof(IVpnController)))
{
return VpnController;
}
if (typeof(T).IsAssignableFrom(typeof(IUpdateController)))
{
return UpdateController;
}
throw new NotImplementedException($"Controller of type {typeof(T)} is not supported.");
}
}