/
ServicePointManager.cs
233 lines (201 loc) · 9.84 KB
/
ServicePointManager.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
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Net.Security;
using System.Runtime.Versioning;
using System.Threading;
namespace System.Net
{
public class ServicePointManager
{
public const int DefaultNonPersistentConnectionLimit = 4;
public const int DefaultPersistentConnectionLimit = 2;
private static readonly ConcurrentDictionary<string, WeakReference<ServicePoint>> s_servicePointTable = new ConcurrentDictionary<string, WeakReference<ServicePoint>>();
private static SecurityProtocolType s_securityProtocolType = SecurityProtocolType.SystemDefault;
private static int s_connectionLimit = 2;
private static int s_maxServicePoints;
private static int s_maxServicePointIdleTime = 100 * 1000;
private static int s_dnsRefreshTimeout = 2 * 60 * 1000;
private ServicePointManager() { }
public static SecurityProtocolType SecurityProtocol
{
get { return s_securityProtocolType; }
set
{
ValidateSecurityProtocol(value);
s_securityProtocolType = value;
}
}
private static void ValidateSecurityProtocol(SecurityProtocolType value)
{
const SecurityProtocolType Allowed =
#pragma warning disable CA5364 // Do Not Use Deprecated Security Protocols
SecurityProtocolType.Tls | SecurityProtocolType.Tls11 |
#pragma warning restore CA5364
SecurityProtocolType.Tls12 | SecurityProtocolType.Tls13;
if ((value & ~Allowed) != 0)
{
throw new NotSupportedException(SR.net_securityprotocolnotsupported);
}
}
public static int MaxServicePoints
{
get { return s_maxServicePoints; }
set
{
if (value < 0)
{
throw new ArgumentOutOfRangeException(nameof(value));
}
s_maxServicePoints = value;
}
}
public static int DefaultConnectionLimit
{
get { return s_connectionLimit; }
set
{
if (value <= 0)
{
throw new ArgumentOutOfRangeException(nameof(value));
}
s_connectionLimit = value;
}
}
public static int MaxServicePointIdleTime
{
get { return s_maxServicePointIdleTime; }
set
{
if (value < Timeout.Infinite)
{
throw new ArgumentOutOfRangeException(nameof(value));
}
s_maxServicePointIdleTime = value;
}
}
public static bool UseNagleAlgorithm { get; set; } = true;
public static bool Expect100Continue { get; set; } = true;
public static bool EnableDnsRoundRobin { get; set; }
public static int DnsRefreshTimeout
{
get { return s_dnsRefreshTimeout; }
set { s_dnsRefreshTimeout = Math.Max(-1, value); }
}
public static RemoteCertificateValidationCallback? ServerCertificateValidationCallback { get; set; }
public static bool ReusePort { get; set; }
public static bool CheckCertificateRevocationList { get; set; }
[UnsupportedOSPlatform("browser")]
public static EncryptionPolicy EncryptionPolicy { get; } = EncryptionPolicy.RequireEncryption;
[Obsolete(Obsoletions.WebRequestMessage, DiagnosticId = Obsoletions.WebRequestDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
public static ServicePoint FindServicePoint(Uri address) => FindServicePoint(address, null);
[Obsolete(Obsoletions.WebRequestMessage, DiagnosticId = Obsoletions.WebRequestDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
public static ServicePoint FindServicePoint(string uriString, IWebProxy? proxy) => FindServicePoint(new Uri(uriString), proxy);
[Obsolete(Obsoletions.WebRequestMessage, DiagnosticId = Obsoletions.WebRequestDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
public static ServicePoint FindServicePoint(Uri address, IWebProxy? proxy)
{
ArgumentNullException.ThrowIfNull(address);
// If there's a proxy for this address, get the "real" address.
bool isProxyServicePoint = ProxyAddressIfNecessary(ref address, proxy);
// Create a lookup key to find the service point
string tableKey = MakeQueryString(address, isProxyServicePoint);
// Get an existing service point or create a new one
ServicePoint? sp; // outside of loop to keep references alive from one iteration to the next
while (true)
{
// The table maps lookup key to a weak reference to a service point. If the table
// contains a weak ref for the key and that weak ref points to a valid ServicePoint,
// simply return it (after updating its last used time).
WeakReference<ServicePoint>? wr;
if (s_servicePointTable.TryGetValue(tableKey, out wr) && wr.TryGetTarget(out sp))
{
sp.IdleSince = DateTime.Now;
return sp;
}
// Any time we don't find what we're looking for in the table, take that as an opportunity
// to scavenge the table looking for entries that have lost their service point and removing them.
foreach (KeyValuePair<string, WeakReference<ServicePoint>> entry in s_servicePointTable)
{
if (!entry.Value.TryGetTarget(out _))
{
// Remove the entry from the table if both the key/value in the pair match.
// This avoids a race condition where another thread concurrently sets a new
// weak reference value for the same key, and is why when adding the new
// service point below, we don't use any weak reference object we may already
// have from the initial retrieval above.
s_servicePointTable.TryRemove(entry);
}
}
// There wasn't a service point in the table. Create a new one, and then store
// it back into the table. We create a new weak reference object even if we were
// able to get one above so that when we scavenge the table, we can rely on
// weak reference reference equality to know whether we're removing the same
// weak reference we saw when we enumerated.
sp = new ServicePoint(address)
{
ConnectionLimit = DefaultConnectionLimit,
IdleSince = DateTime.Now,
Expect100Continue = Expect100Continue,
UseNagleAlgorithm = UseNagleAlgorithm
};
s_servicePointTable[tableKey] = new WeakReference<ServicePoint>(sp);
// It's possible there's a race between two threads both updating the table
// at the same time. We don't want to just use GetOrAdd, as with the weak
// reference we may end up getting back a weak ref that no longer has a target.
// So we simply loop around again; in all but the most severe of circumstances, the
// next iteration will find it in the table and return it.
}
}
private static bool ProxyAddressIfNecessary(ref Uri address, IWebProxy? proxy)
{
if (proxy != null && !address.IsLoopback)
{
try
{
Uri? proxyAddress = proxy.GetProxy(address);
if (proxyAddress != null)
{
if (proxyAddress.Scheme != Uri.UriSchemeHttp)
{
throw new NotSupportedException(SR.Format(SR.net_proxyschemenotsupported, address.Scheme));
}
address = proxyAddress;
return true;
}
}
catch (PlatformNotSupportedException)
{
// HttpWebRequest has a dummy SystemWebProxy that's used as a sentinel
// and whose GetProxy method throws a PlatformNotSupportedException.
// For the purposes of this stand-in ServicePointManager implementation,
// we ignore this default "system" proxy for the purposes of mapping
// to a particular ServicePoint instance.
}
}
return false;
}
private static string MakeQueryString(Uri address) => address.IsDefaultPort ?
address.Scheme + "://" + address.DnsSafeHost :
address.Scheme + "://" + address.DnsSafeHost + ":" + address.Port.ToString();
private static string MakeQueryString(Uri address, bool isProxy)
{
string queryString = MakeQueryString(address);
return isProxy ? queryString + "://proxy" : queryString;
}
public static void SetTcpKeepAlive(bool enabled, int keepAliveTime, int keepAliveInterval)
{
if (enabled)
{
if (keepAliveTime <= 0)
{
throw new ArgumentOutOfRangeException(nameof(keepAliveTime));
}
if (keepAliveInterval <= 0)
{
throw new ArgumentOutOfRangeException(nameof(keepAliveInterval));
}
}
}
}
}