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