forked from vpnhood/VpnHood
-
Notifications
You must be signed in to change notification settings - Fork 0
/
VpnHoodConnect.cs
127 lines (106 loc) · 4.2 KB
/
VpnHoodConnect.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using VpnHood.Client.Device;
using VpnHood.Common;
using VpnHood.Common.Logging;
using VpnHood.Common.Messaging;
namespace VpnHood.Client;
public class VpnHoodConnect : IDisposable
{
private readonly bool _autoDisposePacketCapture;
private readonly Guid _clientId;
private readonly ClientOptions _clientOptions;
private readonly IPacketCapture _packetCapture;
private readonly Token _token;
private DateTime _reconnectTime = DateTime.MinValue;
public bool IsWaiting { get; private set; }
public bool IsDisposed { get; private set; }
public event EventHandler? StateChanged;
public VpnHoodConnect(IPacketCapture packetCapture, Guid clientId, Token token,
ClientOptions? clientOptions = null, ConnectOptions? connectOptions = null)
{
connectOptions ??= new ConnectOptions();
_clientOptions = clientOptions ?? new ClientOptions();
_autoDisposePacketCapture = _clientOptions.AutoDisposePacketCapture;
_packetCapture = packetCapture;
_clientId = clientId;
_token = token;
MaxReconnectCount = connectOptions.MaxReconnectCount;
ReconnectDelay = connectOptions.ReconnectDelay;
//this class Connect change this option temporary and restore it after last attempt
_clientOptions.AutoDisposePacketCapture = false;
_clientOptions.UseUdpChannel = connectOptions.UdpChannelMode == UdpChannelMode.On ||
connectOptions.UdpChannelMode == UdpChannelMode.Auto;
// let always have a Client to access its member after creating VpnHoodConnect
Client = new VpnHoodClient(_packetCapture, _clientId, _token, _clientOptions);
}
public int AttemptCount { get; private set; }
public TimeSpan ReconnectDelay { get; set; }
public int MaxReconnectCount { get; set; }
public VpnHoodClient Client { get; private set; }
public Task Connect()
{
if (IsDisposed)
throw new ObjectDisposedException($"{VhLogger.FormatTypeName(this)} is disposed!");
if (Client.State != ClientState.None && Client.State != ClientState.Disposed)
throw new InvalidOperationException("Connection is already in progress!");
if (Client.State == ClientState.Disposed)
Client = new VpnHoodClient(_packetCapture, _clientId, _token, _clientOptions);
Client.StateChanged += Client_StateChanged;
return Client.Connect();
}
private void Client_StateChanged(object sender, EventArgs e)
{
StateChanged?.Invoke(this, EventArgs.Empty);
if (Client.State == ClientState.Disposed) _ = Reconnect();
}
private async Task Reconnect()
{
if ((DateTime.Now - _reconnectTime).TotalMinutes > 5)
AttemptCount = 0;
// check reconnecting
var reconnect = AttemptCount < MaxReconnectCount &&
Client.SessionStatus.ErrorCode is
SessionErrorCode.GeneralError or
SessionErrorCode.SessionClosed;
if (reconnect)
{
_reconnectTime = DateTime.Now;
AttemptCount++;
// delay
IsWaiting = true;
StateChanged?.Invoke(this, EventArgs.Empty);
await Task.Delay(ReconnectDelay);
IsWaiting = false;
StateChanged?.Invoke(this, EventArgs.Empty);
// connect again
if (IsDisposed) return;
await Connect();
}
else
{
Dispose();
}
}
public void Dispose()
{
if (IsDisposed) return;
IsDisposed = true;
// close client
try
{
Client.Dispose();
Client.StateChanged -= Client_StateChanged; //must be after Client.Dispose to capture dispose event
}
catch (Exception ex)
{
VhLogger.Instance.LogError($"Could not dispose client properly! Error: {ex}");
}
// release _packetCapture
if (_autoDisposePacketCapture)
_packetCapture.Dispose();
// notify state changed
StateChanged?.Invoke(this, EventArgs.Empty);
}
}