This repository has been archived by the owner on Apr 17, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 13
/
NetworkChecker.cs
185 lines (163 loc) · 8.01 KB
/
NetworkChecker.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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
using Kros.Utils;
using System;
using System.Net;
using System.Net.Http;
using System.Net.NetworkInformation;
namespace Kros.Net
{
/// <summary>
/// Class dedicated for simple testing of internet connectivity.
/// </summary>
/// <remarks>
/// It is not sufficient to test connectivity using <see cref="NetworkInterface.GetIsNetworkAvailable()"/>, because that
/// method just checks, if the computer is in some network. It does not check if internet is really available.
/// Internet availability is not checked using ping (<see cref="Ping"/>), because this method is often blocked.
/// The availability is tested using a request to specific service.
/// </remarks>
public class NetworkChecker
{
#region Fields
private static readonly TimeSpan DefaultRequestTimeout = TimeSpan.FromSeconds(1);
private static readonly TimeSpan DefaultResponseCacheExpiration = TimeSpan.FromMinutes(3);
private DateTime _lastSuccessResponseTime;
private readonly Func<HttpMessageHandler> _httpMessageHandlerFactory;
#endregion
#region Constructors
/// <inheritdoc cref="NetworkChecker(Uri, Uri, TimeSpan, TimeSpan)"/>
public NetworkChecker(Uri serviceAddress)
: this(serviceAddress, (Uri) null, DefaultRequestTimeout, DefaultResponseCacheExpiration)
{
}
/// <inheritdoc cref="NetworkChecker(Uri, Uri, TimeSpan, TimeSpan)"/>
public NetworkChecker(Uri serviceAddress, Uri proxyAddress)
: this(serviceAddress, proxyAddress, DefaultRequestTimeout, DefaultResponseCacheExpiration)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="NetworkChecker"/> class.
/// </summary>
/// <param name="serviceAddress">The address for requests checking internet availability. It must be <c>http</c>
/// or <c>https</c> address.</param>
/// <param name="httpMessageHandlerFactory">Factory function to create <see cref="HttpMessageHandler"/>
/// which will be used.</param>
public NetworkChecker(Uri serviceAddress, Func<HttpMessageHandler> httpMessageHandlerFactory)
: this(serviceAddress, httpMessageHandlerFactory, DefaultRequestTimeout, DefaultResponseCacheExpiration)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="NetworkChecker"/> class.
/// </summary>
/// <param name="serviceAddress">The address for requests checking internet availability. It must be <c>http</c>
/// or <c>https</c> address.</param>
/// <param name="httpMessageHandlerFactory">Factory function to create <see cref="HttpMessageHandler"/>
/// which will be used.</param>
/// <param name="requestTimeout">Maximum time for waiting for the response from server. If the response will not
/// came in this time, we consider that the internet is not available.</param>
/// <param name="responseCacheExpiration">Time during which the last response will be remembered
/// and so no requests to <paramref name="serviceAddress"/> will be performed.
/// </param>
public NetworkChecker(
Uri serviceAddress,
Func<HttpMessageHandler> httpMessageHandlerFactory,
TimeSpan requestTimeout,
TimeSpan responseCacheExpiration)
: this(serviceAddress, (Uri) null, requestTimeout, responseCacheExpiration)
{
_httpMessageHandlerFactory = Check.NotNull(httpMessageHandlerFactory, nameof(httpMessageHandlerFactory));
}
/// <inheritdoc cref="NetworkChecker(Uri, Uri, TimeSpan, TimeSpan)"/>
public NetworkChecker(Uri serviceAddress, TimeSpan requestTimeout, TimeSpan responseCacheExpiration)
: this(serviceAddress, (Uri) null, requestTimeout, responseCacheExpiration)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="NetworkChecker"/> with address <paramref name="serviceAddress"/>
/// and aditional parameters.
/// </summary>
/// <param name="serviceAddress">The address for requests checking internet availability. It must be <c>http</c>
/// or <c>https</c> address.</param>
/// <param name="proxyAddress">The address of a proxy server (optional).</param>
/// <param name="requestTimeout">Maximum time for waiting for the response from server. If the response will not
/// came in this time, we consider that the internet is not available.</param>
/// <param name="responseCacheExpiration">Time during which the last response will be remembered
/// and so no requests to <paramref name="serviceAddress"/> will be performed.
/// </param>
public NetworkChecker(
Uri serviceAddress,
Uri proxyAddress,
TimeSpan requestTimeout,
TimeSpan responseCacheExpiration)
{
ServiceAddress = Check.NotNull(serviceAddress, nameof(serviceAddress));
ProxyAddress = proxyAddress;
RequestTimeout = Check.GreaterOrEqualThan(requestTimeout, TimeSpan.Zero, nameof(requestTimeout));
ResponseCacheExpiration = Check.GreaterOrEqualThan(responseCacheExpiration, TimeSpan.Zero,
nameof(responseCacheExpiration));
}
#endregion
#region NetworkChecker
/// <summary>
/// Web address to which requests are made to check internet availability.
/// </summary>
public Uri ServiceAddress { get; }
/// <summary>
/// Address of a proxy server.
/// </summary>
public Uri ProxyAddress { get; }
/// <summary>
/// Maximum time for waiting for the response from server. If the response will not
/// came in this time, we consider that the internet is not available. Default timeout is 1 second.
/// </summary>
public TimeSpan RequestTimeout { get; }
/// <summary>
/// Time during which the last response will be remembered and so no other requests to <see cref="ServiceAddress"/>
/// will be performed. Default value is 3 minutes.
/// </summary>
public TimeSpan ResponseCacheExpiration { get; }
/// <summary>
/// Checks if the internet (specifically the service at the address <see cref="ServiceAddress"/>) is available.
/// Positive response is cached for the time specified in <see cref="ResponseCacheExpiration"/>,
/// so another request to the service is made after this time.
/// </summary>
/// <returns>
/// <see langword="true"/> if internet (service) is available <see langword="false"/> otherwise.
/// </returns>
public bool IsNetworkAvailable() => CheckNetwork() && (HasCachedResponse() || CheckService());
internal virtual bool CheckNetwork() => NetworkInterface.GetIsNetworkAvailable();
private bool HasCachedResponse() =>
_lastSuccessResponseTime.Add(ResponseCacheExpiration) >= DateTimeProvider.Now;
private bool CheckService()
{
try
{
using (var client = new HttpClient(CreateMessageHandler()) { Timeout = RequestTimeout })
using (var stream = client.GetStreamAsync(ServiceAddress).GetAwaiter().GetResult())
{
_lastSuccessResponseTime = DateTimeProvider.Now;
return true;
}
}
catch
{
return false;
}
}
internal virtual HttpMessageHandler CreateMessageHandler()
{
if (_httpMessageHandlerFactory != null)
{
return _httpMessageHandlerFactory();
}
else
{
var handler = new HttpClientHandler();
if (ProxyAddress != null)
{
handler.Proxy = new WebProxy(ProxyAddress);
}
return handler;
}
}
#endregion
}
}