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")]
DebugMembers(int requestHash)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 
IdleServicePointTimeoutCallback(TimerThread.Timer timer, int timeNoticed, object context)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 
ServicePointManager()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 
ValidateSecurityProtocol(SecurityProtocolType value)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 
GetLegacyCertificatePolicy()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         //
MakeQueryString(Uri address)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 
MakeQueryString(Uri address1, bool isProxy)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>
FindServicePoint(Uri address)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>
FindServicePoint(string uriString, IWebProxy proxy)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>
FindServicePoint(Uri address, IWebProxy proxy)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.
FindServicePoint(Uri address, IWebProxy proxy, out ProxyChain chain, ref HttpAbortDelegate abortDelegate, ref int abortState)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.
FindServicePoint(ProxyChain 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 
FindServicePointHelper(Uri address, bool isProxyServicePoint)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>
FindServicePoint(string host, int port)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]
CloseConnectionGroups(string connectionGroupName)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         //
SetTcpKeepAlive( bool enabled, int keepAliveTime, int keepAliveInterval)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 
897