Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System / net / System / Net / ServicePointManager.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="ServicePointManager.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
6 #if MONO_FEATURE_WEB_STACK
7 namespace System.Net {
8     using Diagnostics;
9     using System.Collections;
10     using System.Diagnostics.CodeAnalysis;
11     using System.Globalization;
12     using System.Net.Configuration;
13     using System.Net.Security;
14     using System.Runtime.CompilerServices;
15     using System.Security.Authentication;
16     using System.Threading;
17
18     //
19     // The ServicePointManager class hands out ServicePoints (may exist or be created
20     // as needed) and makes sure they are garbage collected when they expire.
21     // The ServicePointManager runs in its own thread so that it never
22     //
23
24     /// <devdoc>
25     /// <para>Manages the collection of <see cref='System.Net.ServicePoint'/> instances.</para>
26     /// </devdoc>
27     ///
28     public partial class ServicePointManager {
29
30         /// <devdoc>
31         ///    <para>
32         ///       The number of non-persistent connections allowed on a <see cref='System.Net.ServicePoint'/>.
33         ///    </para>
34         /// </devdoc>
35         public const int DefaultNonPersistentConnectionLimit = 4;
36         /// <devdoc>
37         ///    <para>
38         ///       The default number of persistent connections allowed on a <see cref='System.Net.ServicePoint'/>.
39         ///    </para>
40         /// </devdoc>
41         public const int DefaultPersistentConnectionLimit = 2;
42
43         /// <devdoc>
44         ///    <para>
45         ///       The default number of persistent connections when running under ASP+.
46         ///    </para>
47         /// </devdoc>
48         private const int DefaultAspPersistentConnectionLimit = 10;
49
50
51         internal static readonly string SpecialConnectGroupName = "/.NET/NetClasses/HttpWebRequest/CONNECT__Group$$/";
52         internal static readonly TimerThread.Callback s_IdleServicePointTimeoutDelegate = new TimerThread.Callback(IdleServicePointTimeoutCallback);
53
54         //
55         // data  - only statics used
56         //
57
58         //
59         // s_ServicePointTable - Uri of ServicePoint is the hash key
60         // We provide our own comparer function that knows about Uris
61         //
62
63         //also used as a lock object
64         private static Hashtable s_ServicePointTable = new Hashtable(10);
65
66         // IIS6 has 120 sec for an idle connection timeout, we should have a little bit less.
67         private static volatile TimerThread.Queue s_ServicePointIdlingQueue = TimerThread.GetOrCreateQueue(100 * 1000);
68         private static int s_MaxServicePoints = 0;
69 #if !FEATURE_PAL
70         private static volatile CertPolicyValidationCallback s_CertPolicyValidationCallback = new CertPolicyValidationCallback();
71         private static volatile ServerCertValidationCallback s_ServerCertValidationCallback = null;
72
73         private static SecurityProtocolType s_SecurityProtocolType;
74
75         private static bool s_reusePort;
76         private static bool? s_reusePortSupported = null;
77
78         private static bool s_disableStrongCrypto;
79         private static bool s_disableSendAuxRecord;
80         private static bool s_disableSystemDefaultTlsVersions;
81         private static SslProtocols s_defaultSslProtocols;
82
83 #endif // !FEATURE_PAL
84
85         private static volatile Hashtable s_ConfigTable = null;
86         private static volatile int s_ConnectionLimit = PersistentConnectionLimit;
87
88         internal static volatile bool s_UseTcpKeepAlive = false;
89         internal static volatile int s_TcpKeepAliveTime;
90         internal static volatile int s_TcpKeepAliveInterval;
91
92         //
93         // InternalConnectionLimit -
94         //  set/get Connection Limit on demand, checking config beforehand
95         //
96
97         private static volatile bool s_UserChangedLimit;
98         private static int InternalConnectionLimit {
99             get {
100                 if (s_ConfigTable == null) {
101                     // init config
102                     s_ConfigTable = ConfigTable;
103                 }
104                 return s_ConnectionLimit;
105             }
106             set {
107                 if (s_ConfigTable == null) {
108                     // init config
109                     s_ConfigTable = ConfigTable;
110                 }
111                 s_UserChangedLimit = true;
112                 s_ConnectionLimit = value;
113             }
114         }
115
116         //
117         // PersistentConnectionLimit -
118         //  Determines the correct connection limit based on whether with running with ASP+
119         //  The following order is followed generally for figuring what ConnectionLimit size to use
120         //    1.    If ServicePoint.ConnectionLimit is set, then take that value
121         //    2.    If ServicePoint has a specific config setting, then take that value
122         //    3.    If ServicePoint.DefaultConnectionLimit is set, then take that value
123         //    4.    If ServicePoint is localhost, then set to infinite (TO Should we change this value?)
124         //    5.    If ServicePointManager has a default config connection limit setting, then take that value
125         //    6.    If ServicePoint is running under ASP+, then set value to 10, else set it to 2
126         //
127         private static int PersistentConnectionLimit {
128             get {
129 #if !FEATURE_PAL
130                 if (ComNetOS.IsAspNetServer) {
131                     return DefaultAspPersistentConnectionLimit;
132                 } else
133 #endif
134                 {
135                     return DefaultPersistentConnectionLimit;
136                 }
137             }
138         }
139
140         /* Consider Removing
141         //
142         // InternalServicePointCount -
143         //  Gets the active number of ServicePoints being used
144         //
145         internal static int InternalServicePointCount {
146             get {
147                 return s_ServicePointTable.Count;
148             }
149         }
150         */
151
152         [System.Diagnostics.Conditional("DEBUG")]
153         internal static void DebugMembers(int requestHash) {
154             try {
155                 foreach (WeakReference servicePointReference in  s_ServicePointTable) {
156                     ServicePoint servicePoint;
157                     if (servicePointReference != null && servicePointReference.IsAlive) {
158                         servicePoint = (ServicePoint)servicePointReference.Target;
159                     }
160                     else {
161                         servicePoint = null;
162                     }
163                     if (servicePoint!=null) {
164                         servicePoint.DebugMembers(requestHash);
165                     }
166                 }
167             }
168             catch (Exception e) {
169                 if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) {
170                     throw;
171                 }
172             }
173         }
174
175         //
176         // ConfigTable -
177         // read ConfigTable from Config, or create
178         //  a default on failure
179         //
180
181         private static Hashtable ConfigTable {
182             get {
183                 if (s_ConfigTable == null) {
184                     lock(s_ServicePointTable) {
185                         if (s_ConfigTable == null) {
186                             ConnectionManagementSectionInternal configSection 
187                                 = ConnectionManagementSectionInternal.GetSection();
188                             Hashtable configTable = null;
189                             if (configSection != null)
190                             {
191                                 configTable = configSection.ConnectionManagement;
192                             }
193
194                             if (configTable == null) {
195                                 configTable = new Hashtable();
196                             }
197
198                             // we piggy back loading the ConnectionLimit here
199                             if (configTable.ContainsKey("*") ) {
200                                 int connectionLimit  = (int) configTable["*"];
201                                 if ( connectionLimit < 1 ) {
202                                     connectionLimit = PersistentConnectionLimit;
203                                 }
204                                 s_ConnectionLimit = connectionLimit;
205                             }
206                             s_ConfigTable = configTable;
207                         }
208                     }
209                 }
210                 return s_ConfigTable;
211             }
212         }
213
214
215         internal static TimerThread.Callback IdleServicePointTimeoutDelegate
216         {
217             get
218             {
219                 return s_IdleServicePointTimeoutDelegate;
220             }
221         }
222
223         private static void IdleServicePointTimeoutCallback(TimerThread.Timer timer, int timeNoticed, object context)
224         {
225             ServicePoint servicePoint = (ServicePoint) context;
226
227             if (Logging.On) Logging.PrintInfo(Logging.Web, SR.GetString(SR.net_log_closed_idle,
228                 "ServicePoint", servicePoint.GetHashCode()));
229
230             lock (s_ServicePointTable)
231             {
232                 s_ServicePointTable.Remove(servicePoint.LookupString);
233             }
234
235             servicePoint.ReleaseAllConnectionGroups();
236         }
237
238
239         //
240         // constructors
241         //
242
243         private ServicePointManager() {
244         }
245
246 #if !FEATURE_PAL
247         [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "Reviewed for thread-safety")]
248         public static SecurityProtocolType SecurityProtocol {
249             get {
250                 EnsureConfigurationLoaded();
251                 return s_SecurityProtocolType;
252             }
253             set {
254                 EnsureConfigurationLoaded();
255                 ValidateSecurityProtocol(value);
256                 s_SecurityProtocolType = value;
257             }
258         }
259
260         private static void ValidateSecurityProtocol(SecurityProtocolType value)
261         {
262             // Do not allow Ssl2 (and others) as explicit SSL version request.
263             SecurityProtocolType allowed = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls
264                 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
265
266             Debug.Assert((int)SecurityProtocolType.SystemDefault == (int)SslProtocols.None);
267             Debug.Assert((int)SecurityProtocolType.Ssl3 == (int)SslProtocols.Ssl3);
268             Debug.Assert((int)SecurityProtocolType.Tls == (int)SslProtocols.Tls);
269             Debug.Assert((int)SecurityProtocolType.Tls11 == (int)SslProtocols.Tls11);
270             Debug.Assert((int)SecurityProtocolType.Tls12 == (int)SslProtocols.Tls12);
271
272             if ((value & ~allowed) != 0)
273             {
274                 throw new NotSupportedException(SR.GetString(SR.net_securityprotocolnotsupported));
275             }
276         }
277 #endif // !FEATURE_PAL
278
279         internal static bool DisableStrongCrypto
280         {
281             get
282             {
283                 EnsureConfigurationLoaded();
284                 return s_disableStrongCrypto;
285             }
286         }
287
288         internal static bool DisableSystemDefaultTlsVersions
289         {
290             get
291             {
292                 EnsureConfigurationLoaded();
293                 return s_disableSystemDefaultTlsVersions;
294             }
295         }
296
297         internal static bool DisableSendAuxRecord
298         {
299             get
300             {
301                 EnsureConfigurationLoaded();
302                 return s_disableSendAuxRecord;
303             }
304         }
305
306         internal static SslProtocols DefaultSslProtocols
307         {
308             get
309             {
310                 EnsureConfigurationLoaded();
311                 return s_defaultSslProtocols;
312             }
313         }
314
315         //
316         // accessors
317         //
318
319         /// <devdoc>
320         /// <para>Gets or sets the maximum number of <see cref='System.Net.ServicePoint'/> instances that should be maintained at any
321         ///    time.</para>
322         /// </devdoc>
323         [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "Reviewed for thread-safety")]
324         public static int MaxServicePoints {
325             get {
326                 return s_MaxServicePoints;
327             }
328             set {
329                 ExceptionHelper.WebPermissionUnrestricted.Demand();
330                 if (!ValidationHelper.ValidateRange(value, 0, Int32.MaxValue)) {
331                     throw new ArgumentOutOfRangeException("value");
332                 }
333                 s_MaxServicePoints = value;
334             }
335         }
336
337         /// <devdoc>
338         ///    <para>[To be supplied.]</para>
339         /// </devdoc>
340         public static int DefaultConnectionLimit {
341             get {
342                 return InternalConnectionLimit;
343             }
344             set {
345                 ExceptionHelper.WebPermissionUnrestricted.Demand();
346                 if (value > 0) {
347                     InternalConnectionLimit = value;
348
349                 }
350                 else {
351                     throw new ArgumentOutOfRangeException("value", SR.GetString(SR.net_toosmall));
352                 }
353             }
354         }
355
356
357
358         /// <devdoc>
359         /// <para>Gets or sets the maximum idle time in seconds of a <see cref='System.Net.ServicePoint'/>.</para>
360         /// </devdoc>
361         public static int MaxServicePointIdleTime {
362             get {
363                 return s_ServicePointIdlingQueue.Duration;
364             }
365             set {
366                 ExceptionHelper.WebPermissionUnrestricted.Demand();
367                 if ( !ValidationHelper.ValidateRange(value, Timeout.Infinite, Int32.MaxValue)) {
368                     throw new ArgumentOutOfRangeException("value");
369                 }
370                 if (s_ServicePointIdlingQueue.Duration != value)
371                 {
372                     s_ServicePointIdlingQueue = TimerThread.GetOrCreateQueue(value);
373                 }
374             }
375         }
376
377         /// <devdoc>
378         ///    <para>
379         ///       Gets or sets indication whether use of the Nagling algorithm is desired.
380         ///       Changing this value does not affect existing <see cref='System.Net.ServicePoint'/> instances but only to new ones that are created from that moment on.
381         ///    </para>
382         /// </devdoc>
383         public static bool UseNagleAlgorithm {
384             get {
385                 return SettingsSectionInternal.Section.UseNagleAlgorithm;
386             }
387             set {
388                 SettingsSectionInternal.Section.UseNagleAlgorithm = value;
389             }
390         }
391
392         /// <devdoc>
393         ///    <para>
394         ///       Gets or sets indication whether 100-continue behaviour is desired.
395         ///       Changing this value does not affect existing <see cref='System.Net.ServicePoint'/> instances but only to new ones that are created from that moment on.
396         ///    </para>
397         /// </devdoc>
398         public static bool Expect100Continue {
399             get {
400                 return SettingsSectionInternal.Section.Expect100Continue;
401             }
402             set {
403                 SettingsSectionInternal.Section.Expect100Continue = value;
404             }
405         }
406
407         /// <devdoc>
408         ///    <para>
409         ///         Enables the use of DNS round robin access, meaning a different IP
410         ///         address may be used on each connection, when more than one IP is availble
411         ///    </para>
412         /// </devdoc>
413         public static bool EnableDnsRoundRobin {
414             get {
415                 return SettingsSectionInternal.Section.EnableDnsRoundRobin;
416             }
417             set {
418                 SettingsSectionInternal.Section.EnableDnsRoundRobin = value;
419             }
420         }
421
422         /// <devdoc>
423         ///    <para>
424         ///       Causes us to go back and reresolve addresses through DNS, even when
425         ///       there were no recorded failures.  -1 is infinite.  Time should be in ms
426         ///    </para>
427         /// </devdoc>
428         public static int DnsRefreshTimeout {
429             get {
430                 return SettingsSectionInternal.Section.DnsRefreshTimeout;
431             }
432             set {
433                 if(value < -1){
434                     SettingsSectionInternal.Section.DnsRefreshTimeout = -1;
435                 }
436                 else{
437                     SettingsSectionInternal.Section.DnsRefreshTimeout = value;
438                 }
439             }
440         }
441
442 #if !FEATURE_PAL
443         /// <devdoc>
444         ///    <para>
445         ///       Defines the s_Policy for how to deal with server certificates.
446         ///    </para>
447         /// </devdoc>
448
449
450         [Obsolete("CertificatePolicy is obsoleted for this type, please use ServerCertificateValidationCallback instead. http://go.microsoft.com/fwlink/?linkid=14202")]
451         public static ICertificatePolicy CertificatePolicy {
452             get {
453                 return GetLegacyCertificatePolicy();
454             }
455             set {
456                 //Prevent for an applet to override default Certificate Policy
457                 ExceptionHelper.UnmanagedPermission.Demand();
458                 s_CertPolicyValidationCallback = new CertPolicyValidationCallback(value);
459             }
460         }
461
462         internal static ICertificatePolicy GetLegacyCertificatePolicy(){
463             if (s_CertPolicyValidationCallback == null)
464                 return null;
465             else
466                 return s_CertPolicyValidationCallback.CertificatePolicy;
467         }
468
469         internal static CertPolicyValidationCallback CertPolicyValidationCallback {
470             get {
471                 return s_CertPolicyValidationCallback;
472             }
473         }
474
475
476         public static RemoteCertificateValidationCallback ServerCertificateValidationCallback {
477             get {
478                 if (s_ServerCertValidationCallback == null)
479                     return null;
480                 else
481                     return s_ServerCertValidationCallback.ValidationCallback;
482             }
483             set {
484                 // Prevent an applet from overriding the default Certificate Policy
485                 ExceptionHelper.InfrastructurePermission.Demand();
486                 if (value == null)
487                 {
488                     s_ServerCertValidationCallback = null;
489                 }
490                 else
491                 {
492                     s_ServerCertValidationCallback = new ServerCertValidationCallback(value);
493                 }
494             }
495         }
496
497         internal static ServerCertValidationCallback ServerCertValidationCallback {
498             get {
499                 return s_ServerCertValidationCallback;
500             }
501         }
502
503         public static bool ReusePort {
504             get {
505                 return s_reusePort;
506             }
507             set {
508                 s_reusePort = value;
509             }
510         }
511
512         internal static bool? ReusePortSupported
513         {
514             get
515             {
516                 return s_reusePortSupported;
517             }
518             set
519             {
520                 s_reusePortSupported = value;
521             }
522         }
523 #endif // !FEATURE_PAL
524
525         public static bool CheckCertificateRevocationList {
526             get {
527                 return SettingsSectionInternal.Section.CheckCertificateRevocationList;
528             }
529             set {
530                 //Prevent an applet to override default certificate checking
531                 ExceptionHelper.UnmanagedPermission.Demand();
532                 SettingsSectionInternal.Section.CheckCertificateRevocationList = value;
533             }
534         }
535
536         public static EncryptionPolicy EncryptionPolicy {
537             get {
538                 return SettingsSectionInternal.Section.EncryptionPolicy;
539             }
540         }
541         
542         internal static bool CheckCertificateName {
543             get {
544                 return SettingsSectionInternal.Section.CheckCertificateName;
545             }
546         }
547
548
549         //
550         // class methods
551         //
552
553         //
554         // MakeQueryString - Just a short macro to handle creating the query
555         //  string that we search for host ports in the host list
556         //
557         internal static string MakeQueryString(Uri address) {
558             if (address.IsDefaultPort)
559                 return address.Scheme + "://" + address.DnsSafeHost;
560             else
561                 return address.Scheme + "://" + address.DnsSafeHost + ":" + address.Port.ToString();
562         }
563
564         internal static string MakeQueryString(Uri address1, bool isProxy) {
565            if (isProxy) {
566                return MakeQueryString(address1) + "://proxy";
567            }
568            else {
569                return MakeQueryString(address1);
570            }
571         }
572
573         //
574         // FindServicePoint - Query using an Uri string for a given ServerPoint Object
575         //
576
577         /// <devdoc>
578         /// <para>Finds an existing <see cref='System.Net.ServicePoint'/> or creates a new <see cref='System.Net.ServicePoint'/> to manage communications to the
579         ///    specified Uniform Resource Identifier.</para>
580         /// </devdoc>
581         public static ServicePoint FindServicePoint(Uri address) {
582             return FindServicePoint(address, null);
583         }
584
585
586         /// <devdoc>
587         /// <para>Finds an existing <see cref='System.Net.ServicePoint'/> or creates a new <see cref='System.Net.ServicePoint'/> to manage communications to the
588         ///    specified Uniform Resource Identifier.</para>
589         /// </devdoc>
590         public static ServicePoint FindServicePoint(string uriString, IWebProxy proxy) {
591             Uri uri = new Uri(uriString);
592             return FindServicePoint(uri, proxy);
593         }
594
595
596         //
597         // FindServicePoint - Query using an Uri for a given server point
598         //
599
600         /// <devdoc>
601         /// <para>Findes an existing <see cref='System.Net.ServicePoint'/> or creates a new <see cref='System.Net.ServicePoint'/> to manage communications to the specified <see cref='System.Uri'/>
602         /// instance.</para>
603         /// </devdoc>
604         public static ServicePoint FindServicePoint(Uri address, IWebProxy proxy) {
605             ProxyChain chain;
606             HttpAbortDelegate abortDelegate = null;
607             int abortState = 0;
608             return FindServicePoint(address, proxy, out chain, ref abortDelegate, ref abortState);
609         }
610
611         // If abortState becomes non-zero, the attempt to find a service point has been aborted.
612         internal static ServicePoint FindServicePoint(Uri address, IWebProxy proxy, out ProxyChain chain, ref HttpAbortDelegate abortDelegate, ref int abortState)
613         {
614             if (address==null) {
615                 throw new ArgumentNullException("address");
616             }
617             GlobalLog.Enter("ServicePointManager::FindServicePoint() address:" + address.ToString());
618
619             bool isProxyServicePoint = false;
620             chain = null;
621
622             //
623             // find proxy info, and then switch on proxy
624             //
625             Uri proxyAddress = null;
626             if (proxy!=null  && !address.IsLoopback) {
627                 IAutoWebProxy autoProxy = proxy as IAutoWebProxy;
628                 if (autoProxy != null)
629                 {
630                     chain = autoProxy.GetProxies(address);
631
632                     // Set up our ability to abort this MoveNext call.  Note that the current implementations of ProxyChain will only
633                     // take time on the first call, so this is the only place we do this.  If a new ProxyChain takes time in later
634                     // calls, this logic should be copied to other places MoveNext is called.
635                     GlobalLog.Assert(abortDelegate == null, "ServicePointManager::FindServicePoint()|AbortDelegate already set.");
636                     abortDelegate = chain.HttpAbortDelegate;
637                     try
638                     {
639                         Thread.MemoryBarrier();
640                         if (abortState != 0)
641                         {
642                             Exception exception = new WebException(NetRes.GetWebStatusString(WebExceptionStatus.RequestCanceled), WebExceptionStatus.RequestCanceled);
643                             GlobalLog.LeaveException("ServicePointManager::FindServicePoint() Request aborted before proxy lookup.", exception);
644                             throw exception;
645                         }
646
647                         if (!chain.Enumerator.MoveNext())
648                         {
649                             GlobalLog.Assert("ServicePointManager::FindServicePoint()|GetProxies() returned zero proxies.");
650 /*
651                             Exception exception = new WebException(NetRes.GetWebStatusString(WebExceptionStatus.RequestProhibitedByProxy), WebExceptionStatus.RequestProhibitedByProxy);
652                             GlobalLog.LeaveException("ServicePointManager::FindServicePoint() Proxy prevented request.", exception);
653                             throw exception;
654 */
655                         }
656                         proxyAddress = chain.Enumerator.Current;
657                     }
658                     finally
659                     {
660                         abortDelegate = null;
661                     }
662                 }
663                 else if (!proxy.IsBypassed(address))
664                 {
665                     // use proxy support
666                     // rework address
667                     proxyAddress = proxy.GetProxy(address);
668                 }
669
670                 // null means DIRECT
671                 if (proxyAddress!=null) {
672                     address = proxyAddress;
673                     isProxyServicePoint = true;
674                 }
675             }
676
677             ServicePoint servicePoint = FindServicePointHelper(address, isProxyServicePoint);
678             GlobalLog.Leave("ServicePointManager::FindServicePoint() servicePoint#" + ValidationHelper.HashString(servicePoint));
679             return servicePoint;
680         }
681
682         // Returns null if we get to the end of the chain.
683         internal static ServicePoint FindServicePoint(ProxyChain chain)
684         {
685             GlobalLog.Print("ServicePointManager::FindServicePoint() Calling chained version.");
686             if (!chain.Enumerator.MoveNext())
687             {
688                 return null;
689             }
690
691             Uri proxyAddress = chain.Enumerator.Current;
692             return FindServicePointHelper(proxyAddress == null ? chain.Destination : proxyAddress, proxyAddress != null);
693         }
694
695         private static ServicePoint FindServicePointHelper(Uri address, bool isProxyServicePoint)
696         {
697             GlobalLog.Enter("ServicePointManager::FindServicePointHelper() address:" + address.ToString());
698
699             if (isProxyServicePoint)
700             {
701                 if (address.Scheme != Uri.UriSchemeHttp)
702                 {
703                     // <
704
705
706
707                     Exception exception = new NotSupportedException(SR.GetString(SR.net_proxyschemenotsupported, address.Scheme));
708                     GlobalLog.LeaveException("ServicePointManager::FindServicePointHelper() proxy has unsupported scheme:" + address.Scheme.ToString(), exception);
709                     throw exception;
710                 }
711             }
712
713             //
714             // Search for the correct proxy host,
715             //  then match its acutal host by using ConnectionGroups
716             //  which are located on the actual ServicePoint.
717             //
718             string tempEntry = MakeQueryString(address, isProxyServicePoint);
719
720             // lookup service point in the table
721             ServicePoint servicePoint = null;
722             GlobalLog.Print("ServicePointManager::FindServicePointHelper() locking and looking up tempEntry:[" + tempEntry.ToString() + "]");
723             lock (s_ServicePointTable) {
724                 // once we grab the lock, check if it wasn't already added
725                 WeakReference servicePointReference =  s_ServicePointTable[tempEntry] as WeakReference;
726                 GlobalLog.Print("ServicePointManager::FindServicePointHelper() lookup returned WeakReference#" + ValidationHelper.HashString(servicePointReference));
727                 if ( servicePointReference != null ) {
728                     servicePoint = (ServicePoint)servicePointReference.Target;
729                     GlobalLog.Print("ServicePointManager::FindServicePointHelper() successful lookup returned ServicePoint#" + ValidationHelper.HashString(servicePoint));
730                 }
731                 if (servicePoint==null) {
732                     // lookup failure or timeout, we need to create a new ServicePoint
733                     if (s_MaxServicePoints<=0 || s_ServicePointTable.Count<s_MaxServicePoints) {
734                         // Determine Connection Limit
735                         int connectionLimit = InternalConnectionLimit;
736                         string schemeHostPort = MakeQueryString(address);
737                         bool userDefined = s_UserChangedLimit;
738                         if (ConfigTable.ContainsKey(schemeHostPort) ) {
739                             connectionLimit = (int) ConfigTable[schemeHostPort];
740                             userDefined = true;
741                         }
742                         servicePoint = new ServicePoint(address, s_ServicePointIdlingQueue, connectionLimit, tempEntry, userDefined, isProxyServicePoint);
743                         GlobalLog.Print("ServicePointManager::FindServicePointHelper() created ServicePoint#" + ValidationHelper.HashString(servicePoint));
744                         servicePointReference = new WeakReference(servicePoint);
745                         s_ServicePointTable[tempEntry] = servicePointReference;
746                         GlobalLog.Print("ServicePointManager::FindServicePointHelper() adding entry WeakReference#" + ValidationHelper.HashString(servicePointReference) + " key:[" + tempEntry + "]");
747                     }
748                     else {
749                         Exception exception = new InvalidOperationException(SR.GetString(SR.net_maxsrvpoints));
750                         GlobalLog.LeaveException("ServicePointManager::FindServicePointHelper() reached the limit count:" + s_ServicePointTable.Count.ToString() + " limit:" + s_MaxServicePoints.ToString(), exception);
751                         throw exception;
752                     }
753                 }
754             }
755
756             GlobalLog.Leave("ServicePointManager::FindServicePointHelper() servicePoint#" + ValidationHelper.HashString(servicePoint));
757             return servicePoint;
758         }
759
760         //
761         // FindServicePoint - Query using an Uri for a given server point
762         //
763
764         /// <devdoc>
765         /// <para>Findes an existing <see cref='System.Net.ServicePoint'/> or creates a new <see cref='System.Net.ServicePoint'/> to manage communications to the specified <see cref='System.Uri'/>
766         /// instance.</para>
767         /// </devdoc>
768         internal static ServicePoint FindServicePoint(string host, int port) {
769             if (host==null) {
770                 throw new ArgumentNullException("address");
771             }
772             GlobalLog.Enter("ServicePointManager::FindServicePoint() host:" + host.ToString());
773
774             string tempEntry = null;
775             bool isProxyServicePoint = false;
776
777
778             //
779             // Search for the correct proxy host,
780             //  then match its acutal host by using ConnectionGroups
781             //  which are located on the actual ServicePoint.
782             //
783             tempEntry = "ByHost:"+host+":"+port.ToString(CultureInfo.InvariantCulture);
784             // lookup service point in the table
785             ServicePoint servicePoint = null;
786             GlobalLog.Print("ServicePointManager::FindServicePoint() locking and looking up tempEntry:[" + tempEntry.ToString() + "]");
787             lock (s_ServicePointTable) {
788                 // once we grab the lock, check if it wasn't already added
789                 WeakReference servicePointReference =  s_ServicePointTable[tempEntry] as WeakReference;
790                 GlobalLog.Print("ServicePointManager::FindServicePoint() lookup returned WeakReference#" + ValidationHelper.HashString(servicePointReference));
791                 if ( servicePointReference != null ) {
792                     servicePoint = (ServicePoint)servicePointReference.Target;
793                     GlobalLog.Print("ServicePointManager::FindServicePoint() successfull lookup returned ServicePoint#" + ValidationHelper.HashString(servicePoint));
794                 }
795                 if (servicePoint==null) {
796                     // lookup failure or timeout, we need to create a new ServicePoint
797                     if (s_MaxServicePoints<=0 || s_ServicePointTable.Count<s_MaxServicePoints) {
798                         // Determine Connection Limit
799                         int connectionLimit = InternalConnectionLimit;
800                         bool userDefined = s_UserChangedLimit;
801                         string schemeHostPort =host+":"+port.ToString(CultureInfo.InvariantCulture);
802
803                         if (ConfigTable.ContainsKey(schemeHostPort) ) {
804                             connectionLimit = (int) ConfigTable[schemeHostPort];
805                             userDefined = true;
806                         }
807                         servicePoint = new ServicePoint(host, port, s_ServicePointIdlingQueue, connectionLimit, tempEntry, userDefined, isProxyServicePoint);
808                         GlobalLog.Print("ServicePointManager::FindServicePoint() created ServicePoint#" + ValidationHelper.HashString(servicePoint));
809                         servicePointReference = new WeakReference(servicePoint);
810                         s_ServicePointTable[tempEntry] = servicePointReference;
811                         GlobalLog.Print("ServicePointManager::FindServicePoint() adding entry WeakReference#" + ValidationHelper.HashString(servicePointReference) + " key:[" + tempEntry + "]");
812                     }
813                     else {
814                         Exception exception = new InvalidOperationException(SR.GetString(SR.net_maxsrvpoints));
815                         GlobalLog.LeaveException("ServicePointManager::FindServicePoint() reached the limit count:" + s_ServicePointTable.Count.ToString() + " limit:" + s_MaxServicePoints.ToString(), exception);
816                         throw exception;
817                     }
818                 }
819             }
820
821             GlobalLog.Leave("ServicePointManager::FindServicePoint() servicePoint#" + ValidationHelper.HashString(servicePoint));
822             return servicePoint;
823         }
824
825         [FriendAccessAllowed]
826         internal static void CloseConnectionGroups(string connectionGroupName) {
827             // This method iterates through all service points and closes connection groups with the provided name.
828             ServicePoint servicePoint = null;
829             lock (s_ServicePointTable) {
830                 foreach (DictionaryEntry item in s_ServicePointTable) {
831                     WeakReference servicePointReference = item.Value as WeakReference;
832                     if (servicePointReference != null) {
833                         servicePoint = (ServicePoint)servicePointReference.Target;                    
834                         if (servicePoint != null) {
835                             // We found a service point. Ask the service point to close all internal connection groups 
836                             // with name 'connectionGroupName'.
837                             servicePoint.CloseConnectionGroupInternal(connectionGroupName);
838                         }
839                     }
840                 }
841             }
842         }
843
844         //
845         // SetTcpKeepAlive 
846         //
847         // Enable/Disable the use of TCP keepalive option on ServicePoint
848         // connections. This method does not affect existing ServicePoints.
849         // When a ServicePoint is constructed it will inherit the current 
850         // settings.
851         //
852         // Parameters:
853         //
854         // enabled - if true enables the use of the TCP keepalive option 
855         // for ServicePoint connections.
856         //
857         // keepAliveTime - specifies the timeout, in milliseconds, with no
858         // activity until the first keep-alive packet is sent. Ignored if 
859         // enabled parameter is false.
860         //
861         // keepAliveInterval - specifies the interval, in milliseconds, between
862         // when successive keep-alive packets are sent if no acknowledgement is
863         // received. Ignored if enabled parameter is false.
864         //
865         public static void SetTcpKeepAlive(
866                             bool enabled, 
867                             int keepAliveTime, 
868                             int keepAliveInterval) {
869         
870             GlobalLog.Enter(
871                 "ServicePointManager::SetTcpKeepAlive()" + 
872                 " enabled: " + enabled.ToString() +
873                 " keepAliveTime: " + keepAliveTime.ToString() +
874                 " keepAliveInterval: " + keepAliveInterval.ToString()
875             );
876             if (enabled) {
877                 s_UseTcpKeepAlive = true;
878                 if (keepAliveTime <= 0) {
879                     throw new ArgumentOutOfRangeException("keepAliveTime");
880                 }
881                 if (keepAliveInterval <= 0) {
882                     throw new ArgumentOutOfRangeException("keepAliveInterval");
883                 }
884                 s_TcpKeepAliveTime = keepAliveTime;
885                 s_TcpKeepAliveInterval = keepAliveInterval;
886             } else {
887                 s_UseTcpKeepAlive = false;
888                 s_TcpKeepAliveTime = 0;
889                 s_TcpKeepAliveInterval =0;
890             }
891             GlobalLog.Leave("ServicePointManager::SetTcpKeepAlive()");
892         }
893     }
894 }
895 #endif
896