2 // System.Net.ServicePointManager
5 // Lawrence Pit (loz@cable.a2000.nl)
6 // Gonzalo Paniagua Javier (gonzalo@novell.com)
8 // Copyright (c) 2003-2010 Novell, Inc (http://www.novell.com)
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 using System.Threading;
34 using System.Collections;
35 using System.Collections.Generic;
36 using System.Collections.Specialized;
37 using System.Configuration;
38 using System.Net.Configuration;
39 using System.Security.Cryptography.X509Certificates;
41 using System.Globalization;
42 using System.Net.Security;
43 using System.Diagnostics;
47 // A service point manager manages service points (duh!).
48 // A service point maintains a list of connections (per scheme + authority).
49 // According to HttpWebRequest.ConnectionGroupName each connection group
50 // creates additional connections. therefor, a service point has a hashtable
51 // of connection groups where each value is a list of connections.
53 // when we need to make an HttpWebRequest, we need to do the following:
54 // 1. find service point, given Uri and Proxy
55 // 2. find connection group, given service point and group name
56 // 3. find free connection in connection group, or create one (if ok due to limits)
57 // 4. lease connection
59 // 6. when finished, return connection
65 public partial class ServicePointManager {
67 Uri uri; // schema/host/port
71 public SPKey (Uri uri, Uri proxy, bool use_connect) {
74 this.use_connect = use_connect;
81 public bool UseConnect {
82 get { return use_connect; }
85 public bool UsesProxy {
86 get { return proxy != null; }
89 public override int GetHashCode () {
91 hash = hash * 31 + ((use_connect) ? 1 : 0);
92 hash = hash * 31 + uri.GetHashCode ();
93 hash = hash * 31 + (proxy != null ? proxy.GetHashCode () : 0);
97 public override bool Equals (object obj) {
98 SPKey other = obj as SPKey;
103 if (!uri.Equals (other.uri))
105 if (use_connect != other.use_connect || UsesProxy != other.UsesProxy)
107 if (UsesProxy && !proxy.Equals (other.proxy))
113 private static HybridDictionary servicePoints = new HybridDictionary ();
117 private static ICertificatePolicy policy;
118 private static int defaultConnectionLimit = DefaultPersistentConnectionLimit;
119 private static int maxServicePointIdleTime = 100000; // 100 seconds
120 private static int maxServicePoints = 0;
121 private static int dnsRefreshTimeout = 2 * 60 * 1000;
122 private static bool _checkCRL = false;
123 private static SecurityProtocolType _securityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
125 static bool expectContinue = true;
126 static bool useNagle;
127 static ServerCertValidationCallback server_cert_cb;
128 static bool tcp_keepalive;
129 static int tcp_keepalive_time;
130 static int tcp_keepalive_interval;
134 public const int DefaultNonPersistentConnectionLimit = 4;
136 public const int DefaultPersistentConnectionLimit = 10;
138 public const int DefaultPersistentConnectionLimit = 2;
142 const string configKey = "system.net/connectionManagement";
143 static ConnectionManagementData manager;
146 static ServicePointManager ()
149 #if CONFIGURATION_DEP
150 object cfg = ConfigurationManager.GetSection (configKey);
151 ConnectionManagementSection s = cfg as ConnectionManagementSection;
153 manager = new ConnectionManagementData (null);
154 foreach (ConnectionManagementElement e in s.ConnectionManagement)
155 manager.Add (e.Address, e.MaxConnection);
157 defaultConnectionLimit = (int) manager.GetMaxConnections ("*");
162 #pragma warning disable 618
163 manager = (ConnectionManagementData) ConfigurationSettings.GetConfig (configKey);
164 #pragma warning restore 618
165 if (manager != null) {
166 defaultConnectionLimit = (int) manager.GetMaxConnections ("*");
172 private ServicePointManager ()
178 [Obsolete ("Use ServerCertificateValidationCallback instead", false)]
179 public static ICertificatePolicy CertificatePolicy {
182 Interlocked.CompareExchange (ref policy, new DefaultCertificatePolicy (), null);
185 set { policy = value; }
188 internal static ICertificatePolicy GetLegacyCertificatePolicy ()
193 [MonoTODO("CRL checks not implemented")]
194 public static bool CheckCertificateRevocationList {
195 get { return _checkCRL; }
196 set { _checkCRL = false; } // TODO - don't yet accept true
199 public static int DefaultConnectionLimit {
200 get { return defaultConnectionLimit; }
203 throw new ArgumentOutOfRangeException ("value");
205 defaultConnectionLimit = value;
208 manager.Add ("*", defaultConnectionLimit);
213 static Exception GetMustImplement ()
215 return new NotImplementedException ();
218 public static int DnsRefreshTimeout
221 return dnsRefreshTimeout;
224 dnsRefreshTimeout = Math.Max (-1, value);
229 public static bool EnableDnsRoundRobin
232 throw GetMustImplement ();
235 throw GetMustImplement ();
239 public static int MaxServicePointIdleTime {
241 return maxServicePointIdleTime;
244 if (value < -2 || value > Int32.MaxValue)
245 throw new ArgumentOutOfRangeException ("value");
246 maxServicePointIdleTime = value;
250 public static int MaxServicePoints {
252 return maxServicePoints;
256 throw new ArgumentException ("value");
258 maxServicePoints = value;
263 public static bool ReusePort {
264 get { return false; }
265 set { throw new NotImplementedException (); }
268 public static SecurityProtocolType SecurityProtocol {
269 get { return _securityProtocol; }
270 set { _securityProtocol = value; }
273 internal static ServerCertValidationCallback ServerCertValidationCallback {
274 get { return server_cert_cb; }
277 public static RemoteCertificateValidationCallback ServerCertificateValidationCallback {
279 if (server_cert_cb == null)
281 return server_cert_cb.ValidationCallback;
286 server_cert_cb = null;
288 server_cert_cb = new ServerCertValidationCallback (value);
292 [MonoTODO ("Always returns EncryptionPolicy.RequireEncryption.")]
293 public static EncryptionPolicy EncryptionPolicy {
295 return EncryptionPolicy.RequireEncryption;
299 public static bool Expect100Continue {
300 get { return expectContinue; }
301 set { expectContinue = value; }
304 public static bool UseNagleAlgorithm {
305 get { return useNagle; }
306 set { useNagle = value; }
309 internal static bool DisableStrongCrypto {
310 get { return false; }
313 internal static bool DisableSendAuxRecord {
314 get { return false; }
318 public static void SetTcpKeepAlive (bool enabled, int keepAliveTime, int keepAliveInterval)
321 if (keepAliveTime <= 0)
322 throw new ArgumentOutOfRangeException ("keepAliveTime", "Must be greater than 0");
323 if (keepAliveInterval <= 0)
324 throw new ArgumentOutOfRangeException ("keepAliveInterval", "Must be greater than 0");
327 tcp_keepalive = enabled;
328 tcp_keepalive_time = keepAliveTime;
329 tcp_keepalive_interval = keepAliveInterval;
332 public static ServicePoint FindServicePoint (Uri address)
334 return FindServicePoint (address, null);
337 public static ServicePoint FindServicePoint (string uriString, IWebProxy proxy)
339 return FindServicePoint (new Uri(uriString), proxy);
342 public static ServicePoint FindServicePoint (Uri address, IWebProxy proxy)
345 throw new ArgumentNullException ("address");
347 var origAddress = new Uri (address.Scheme + "://" + address.Authority);
349 bool usesProxy = false;
350 bool useConnect = false;
351 if (proxy != null && !proxy.IsBypassed(address)) {
353 bool isSecure = address.Scheme == "https";
354 address = proxy.GetProxy (address);
355 if (address.Scheme != "http")
356 throw new NotSupportedException ("Proxy scheme not supported.");
358 if (isSecure && address.Scheme == "http")
362 address = new Uri (address.Scheme + "://" + address.Authority);
364 ServicePoint sp = null;
365 SPKey key = new SPKey (origAddress, usesProxy ? address : null, useConnect);
366 lock (servicePoints) {
367 sp = servicePoints [key] as ServicePoint;
371 if (maxServicePoints > 0 && servicePoints.Count >= maxServicePoints)
372 throw new InvalidOperationException ("maximum number of service points reached");
376 limit = defaultConnectionLimit;
378 string addr = address.ToString ();
379 limit = (int) manager.GetMaxConnections (addr);
381 sp = new ServicePoint (address, limit, maxServicePointIdleTime);
382 sp.Expect100Continue = expectContinue;
383 sp.UseNagleAlgorithm = useNagle;
384 sp.UsesProxy = usesProxy;
385 sp.UseConnect = useConnect;
386 sp.SetTcpKeepAlive (tcp_keepalive, tcp_keepalive_time, tcp_keepalive_interval);
387 servicePoints.Add (key, sp);
393 internal static void CloseConnectionGroup (string connectionGroupName)
395 lock (servicePoints) {
396 foreach (ServicePoint sp in servicePoints.Values) {
397 sp.CloseConnectionGroup (connectionGroupName);