|
//------------------------------------------------------------------------------
// <copyright file="ServicePointManager.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
namespace System.Net {
using Diagnostics;
using System.Collections;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Net.Configuration;
using System.Net.Security;
using System.Runtime.CompilerServices;
using System.Security.Authentication;
using System.Threading;
//
// The ServicePointManager class hands out ServicePoints (may exist or be created
// as needed) and makes sure they are garbage collected when they expire.
// The ServicePointManager runs in its own thread so that it never
//
/// <devdoc>
/// <para>Manages the collection of <see cref='System.Net.ServicePoint'/> instances.</para>
/// </devdoc>
///
public partial class ServicePointManager {
/// <devdoc>
/// <para>
/// The number of non-persistent connections allowed on a <see cref='System.Net.ServicePoint'/>.
/// </para>
/// </devdoc>
public const int DefaultNonPersistentConnectionLimit = 4;
/// <devdoc>
/// <para>
/// The default number of persistent connections allowed on a <see cref='System.Net.ServicePoint'/>.
/// </para>
/// </devdoc>
public const int DefaultPersistentConnectionLimit = 2;
/// <devdoc>
/// <para>
/// The default number of persistent connections when running under ASP+.
/// </para>
/// </devdoc>
private const int DefaultAspPersistentConnectionLimit = 10;
internal static readonly string SpecialConnectGroupName = "/.NET/NetClasses/HttpWebRequest/CONNECT__Group$$/";
internal static readonly TimerThread.Callback s_IdleServicePointTimeoutDelegate = new TimerThread.Callback(IdleServicePointTimeoutCallback);
//
// data - only statics used
//
//
// s_ServicePointTable - Uri of ServicePoint is the hash key
// We provide our own comparer function that knows about Uris
//
//also used as a lock object
private static Hashtable s_ServicePointTable = new Hashtable(10);
// IIS6 has 120 sec for an idle connection timeout, we should have a little bit less.
private static volatile TimerThread.Queue s_ServicePointIdlingQueue = TimerThread.GetOrCreateQueue(100 * 1000);
private static int s_MaxServicePoints = 0;
#if !FEATURE_PAL
private static volatile CertPolicyValidationCallback s_CertPolicyValidationCallback = new CertPolicyValidationCallback();
private static volatile ServerCertValidationCallback s_ServerCertValidationCallback = null;
private static SecurityProtocolType s_SecurityProtocolType;
private static bool s_reusePort;
private static bool? s_reusePortSupported = null;
private static bool s_disableStrongCrypto;
private static bool s_disableSendAuxRecord;
private static bool s_disableSystemDefaultTlsVersions;
private static SslProtocols s_defaultSslProtocols;
private static bool s_disableCertificateEKUs;
private static bool s_useHttpPipeliningAndBufferPooling;
private static bool s_useSafeSynchronousClose;
private static bool s_useStrictRfcInterimResponseHandling;
private static bool s_allowDangerousUnicodeDecompositions;
private static bool s_useStrictIPv6AddressParsing;
private static bool s_allowAllUriEncodingExpansion;
#endif // !FEATURE_PAL
private static volatile Hashtable s_ConfigTable = null;
private static volatile int s_ConnectionLimit = PersistentConnectionLimit;
internal static volatile bool s_UseTcpKeepAlive = false;
internal static volatile int s_TcpKeepAliveTime;
internal static volatile int s_TcpKeepAliveInterval;
//
// InternalConnectionLimit -
// set/get Connection Limit on demand, checking config beforehand
//
private static volatile bool s_UserChangedLimit;
private static int InternalConnectionLimit {
get {
if (s_ConfigTable == null) {
// init config
s_ConfigTable = ConfigTable;
}
return s_ConnectionLimit;
}
set {
if (s_ConfigTable == null) {
// init config
s_ConfigTable = ConfigTable;
}
s_UserChangedLimit = true;
s_ConnectionLimit = value;
}
}
//
// PersistentConnectionLimit -
// Determines the correct connection limit based on whether with running with ASP+
// The following order is followed generally for figuring what ConnectionLimit size to use
// 1. If ServicePoint.ConnectionLimit is set, then take that value
// 2. If ServicePoint has a specific config setting, then take that value
// 3. If ServicePoint.DefaultConnectionLimit is set, then take that value
// 4. If ServicePoint is localhost, then set to infinite (TO Should we change this value?)
// 5. If ServicePointManager has a default config connection limit setting, then take that value
// 6. If ServicePoint is running under ASP+, then set value to 10, else set it to 2
//
private static int PersistentConnectionLimit {
get {
#if !FEATURE_PAL
if (ComNetOS.IsAspNetServer) {
return DefaultAspPersistentConnectionLimit;
} else
#endif
{
return DefaultPersistentConnectionLimit;
}
}
}
/* Consider Removing
//
// InternalServicePointCount -
// Gets the active number of ServicePoints being used
//
internal static int InternalServicePointCount {
get {
return s_ServicePointTable.Count;
}
}
*/
[System.Diagnostics.Conditional("DEBUG")]
internal static void DebugMembers(int requestHash) {
try {
foreach (WeakReference servicePointReference in s_ServicePointTable) {
ServicePoint servicePoint;
if (servicePointReference != null && servicePointReference.IsAlive) {
servicePoint = (ServicePoint)servicePointReference.Target;
}
else {
servicePoint = null;
}
if (servicePoint!=null) {
servicePoint.DebugMembers(requestHash);
}
}
}
catch (Exception e) {
if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) {
throw;
}
}
}
//
// ConfigTable -
// read ConfigTable from Config, or create
// a default on failure
//
private static Hashtable ConfigTable {
get {
if (s_ConfigTable == null) {
lock(s_ServicePointTable) {
if (s_ConfigTable == null) {
ConnectionManagementSectionInternal configSection
= ConnectionManagementSectionInternal.GetSection();
Hashtable configTable = null;
if (configSection != null)
{
configTable = configSection.ConnectionManagement;
}
if (configTable == null) {
configTable = new Hashtable();
}
// we piggy back loading the ConnectionLimit here
if (configTable.ContainsKey("*") ) {
int connectionLimit = (int) configTable["*"];
if ( connectionLimit < 1 ) {
connectionLimit = PersistentConnectionLimit;
}
s_ConnectionLimit = connectionLimit;
}
s_ConfigTable = configTable;
}
}
}
return s_ConfigTable;
}
}
internal static TimerThread.Callback IdleServicePointTimeoutDelegate
{
get
{
return s_IdleServicePointTimeoutDelegate;
}
}
private static void IdleServicePointTimeoutCallback(TimerThread.Timer timer, int timeNoticed, object context)
{
ServicePoint servicePoint = (ServicePoint) context;
if (Logging.On) Logging.PrintInfo(Logging.Web, SR.GetString(SR.net_log_closed_idle,
"ServicePoint", servicePoint.GetHashCode()));
lock (s_ServicePointTable)
{
s_ServicePointTable.Remove(servicePoint.LookupString);
}
servicePoint.ReleaseAllConnectionGroups();
}
//
// constructors
//
private ServicePointManager() {
}
#if !FEATURE_PAL
[SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "Reviewed for thread-safety")]
public static SecurityProtocolType SecurityProtocol {
get {
EnsureConfigurationLoaded();
return s_SecurityProtocolType;
}
set {
EnsureConfigurationLoaded();
ValidateSecurityProtocol(value);
s_SecurityProtocolType = value;
}
}
private static void ValidateSecurityProtocol(SecurityProtocolType value)
{
// Do not allow Ssl2 (and others) as explicit SSL version request.
SecurityProtocolType allowed = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls
| SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12 | SecurityProtocolType.Tls13;
Debug.Assert((int)SecurityProtocolType.SystemDefault == (int)SslProtocols.None);
Debug.Assert((int)SecurityProtocolType.Ssl3 == (int)SslProtocols.Ssl3);
Debug.Assert((int)SecurityProtocolType.Tls == (int)SslProtocols.Tls);
Debug.Assert((int)SecurityProtocolType.Tls11 == (int)SslProtocols.Tls11);
Debug.Assert((int)SecurityProtocolType.Tls12 == (int)SslProtocols.Tls12);
Debug.Assert((int)SecurityProtocolType.Tls13 == (int)SslProtocols.Tls13);
if ((value & ~allowed) != 0)
{
throw new NotSupportedException(SR.GetString(SR.net_securityprotocolnotsupported));
}
}
#endif // !FEATURE_PAL
internal static bool DisableStrongCrypto
{
get
{
EnsureConfigurationLoaded();
return s_disableStrongCrypto;
}
}
internal static bool DisableSystemDefaultTlsVersions
{
get
{
EnsureConfigurationLoaded();
return s_disableSystemDefaultTlsVersions;
}
}
internal static bool DisableSendAuxRecord
{
get
{
EnsureConfigurationLoaded();
return s_disableSendAuxRecord;
}
}
internal static bool DisableCertificateEKUs
{
get
{
EnsureConfigurationLoaded();
return s_disableCertificateEKUs;
}
}
internal static SslProtocols DefaultSslProtocols
{
get
{
EnsureConfigurationLoaded();
return s_defaultSslProtocols;
}
}
internal static bool UseHttpPipeliningAndBufferPooling
{
get
{
EnsureConfigurationLoaded();
return s_useHttpPipeliningAndBufferPooling;
}
}
internal static bool UseSafeSynchronousClose
{
get
{
EnsureConfigurationLoaded();
return s_useSafeSynchronousClose;
}
}
internal static bool UseStrictRfcInterimResponseHandling
{
get
{
EnsureConfigurationLoaded();
return s_useStrictRfcInterimResponseHandling;
}
}
internal static bool AllowDangerousUnicodeDecompositions
{
get
{
EnsureConfigurationLoaded();
return s_allowDangerousUnicodeDecompositions;
}
}
internal static bool UseStrictIPv6AddressParsing
{
get
{
EnsureConfigurationLoaded();
return s_useStrictIPv6AddressParsing;
}
}
internal static bool AllowAllUriEncodingExpansion
{
get
{
EnsureConfigurationLoaded();
return s_allowAllUriEncodingExpansion;
}
}
//
// accessors
//
/// <devdoc>
/// <para>Gets or sets the maximum number of <see cref='System.Net.ServicePoint'/> instances that should be maintained at any
/// time.</para>
/// </devdoc>
[SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "Reviewed for thread-safety")]
public static int MaxServicePoints {
get {
return s_MaxServicePoints;
}
set {
ExceptionHelper.WebPermissionUnrestricted.Demand();
if (!ValidationHelper.ValidateRange(value, 0, Int32.MaxValue)) {
throw new ArgumentOutOfRangeException("value");
}
s_MaxServicePoints = value;
}
}
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
public static int DefaultConnectionLimit {
get {
return InternalConnectionLimit;
}
set {
ExceptionHelper.WebPermissionUnrestricted.Demand();
if (value > 0) {
InternalConnectionLimit = value;
}
else {
throw new ArgumentOutOfRangeException("value", SR.GetString(SR.net_toosmall));
}
}
}
/// <devdoc>
/// <para>Gets or sets the maximum idle time in seconds of a <see cref='System.Net.ServicePoint'/>.</para>
/// </devdoc>
public static int MaxServicePointIdleTime {
get {
return s_ServicePointIdlingQueue.Duration;
}
set {
ExceptionHelper.WebPermissionUnrestricted.Demand();
if ( !ValidationHelper.ValidateRange(value, Timeout.Infinite, Int32.MaxValue)) {
throw new ArgumentOutOfRangeException("value");
}
if (s_ServicePointIdlingQueue.Duration != value)
{
s_ServicePointIdlingQueue = TimerThread.GetOrCreateQueue(value);
}
}
}
/// <devdoc>
/// <para>
/// Gets or sets indication whether use of the Nagling algorithm is desired.
/// 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.
/// </para>
/// </devdoc>
public static bool UseNagleAlgorithm {
get {
return SettingsSectionInternal.Section.UseNagleAlgorithm;
}
set {
SettingsSectionInternal.Section.UseNagleAlgorithm = value;
}
}
/// <devdoc>
/// <para>
/// Gets or sets indication whether 100-continue behaviour is desired.
/// 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.
/// </para>
/// </devdoc>
public static bool Expect100Continue {
get {
return SettingsSectionInternal.Section.Expect100Continue;
}
set {
SettingsSectionInternal.Section.Expect100Continue = value;
}
}
/// <devdoc>
/// <para>
/// Enables the use of DNS round robin access, meaning a different IP
/// address may be used on each connection, when more than one IP is availble
/// </para>
/// </devdoc>
public static bool EnableDnsRoundRobin {
get {
return SettingsSectionInternal.Section.EnableDnsRoundRobin;
}
set {
SettingsSectionInternal.Section.EnableDnsRoundRobin = value;
}
}
/// <devdoc>
/// <para>
/// Causes us to go back and reresolve addresses through DNS, even when
/// there were no recorded failures. -1 is infinite. Time should be in ms
/// </para>
/// </devdoc>
public static int DnsRefreshTimeout {
get {
return SettingsSectionInternal.Section.DnsRefreshTimeout;
}
set {
if(value < -1){
SettingsSectionInternal.Section.DnsRefreshTimeout = -1;
}
else{
SettingsSectionInternal.Section.DnsRefreshTimeout = value;
}
}
}
#if !FEATURE_PAL
/// <devdoc>
/// <para>
/// Defines the s_Policy for how to deal with server certificates.
/// </para>
/// </devdoc>
[Obsolete("CertificatePolicy is obsoleted for this type, please use ServerCertificateValidationCallback instead. http://go.microsoft.com/fwlink/?linkid=14202")]
public static ICertificatePolicy CertificatePolicy {
get {
return GetLegacyCertificatePolicy();
}
set {
//Prevent for an applet to override default Certificate Policy
ExceptionHelper.UnmanagedPermission.Demand();
s_CertPolicyValidationCallback = new CertPolicyValidationCallback(value);
}
}
internal static ICertificatePolicy GetLegacyCertificatePolicy(){
if (s_CertPolicyValidationCallback == null)
return null;
else
return s_CertPolicyValidationCallback.CertificatePolicy;
}
internal static CertPolicyValidationCallback CertPolicyValidationCallback {
get {
return s_CertPolicyValidationCallback;
}
}
public static RemoteCertificateValidationCallback ServerCertificateValidationCallback {
get {
if (s_ServerCertValidationCallback == null)
return null;
else
return s_ServerCertValidationCallback.ValidationCallback;
}
set {
// Prevent an applet from overriding the default Certificate Policy
ExceptionHelper.InfrastructurePermission.Demand();
if (value == null)
{
s_ServerCertValidationCallback = null;
}
else
{
s_ServerCertValidationCallback = new ServerCertValidationCallback(value);
}
}
}
internal static ServerCertValidationCallback ServerCertValidationCallback {
get {
return s_ServerCertValidationCallback;
}
}
public static bool ReusePort {
get {
return s_reusePort;
}
set {
s_reusePort = value;
}
}
internal static bool? ReusePortSupported
{
get
{
return s_reusePortSupported;
}
set
{
s_reusePortSupported = value;
}
}
#endif // !FEATURE_PAL
public static bool CheckCertificateRevocationList {
get {
return SettingsSectionInternal.Section.CheckCertificateRevocationList;
}
set {
//Prevent an applet to override default certificate checking
ExceptionHelper.UnmanagedPermission.Demand();
SettingsSectionInternal.Section.CheckCertificateRevocationList = value;
}
}
public static EncryptionPolicy EncryptionPolicy {
get {
return SettingsSectionInternal.Section.EncryptionPolicy;
}
}
internal static bool CheckCertificateName {
get {
return SettingsSectionInternal.Section.CheckCertificateName;
}
}
//
// class methods
//
//
// MakeQueryString - Just a short macro to handle creating the query
// string that we search for host ports in the host list
//
internal static string MakeQueryString(Uri address) {
if (address.IsDefaultPort)
return address.Scheme + "://" + address.DnsSafeHost;
else
return address.Scheme + "://" + address.DnsSafeHost + ":" + address.Port.ToString();
}
internal static string MakeQueryString(Uri address1, bool isProxy) {
if (isProxy) {
return MakeQueryString(address1) + "://proxy";
}
else {
return MakeQueryString(address1);
}
}
//
// FindServicePoint - Query using an Uri string for a given ServerPoint Object
//
/// <devdoc>
/// <para>Finds an existing <see cref='System.Net.ServicePoint'/> or creates a new <see cref='System.Net.ServicePoint'/> to manage communications to the
/// specified Uniform Resource Identifier.</para>
/// </devdoc>
public static ServicePoint FindServicePoint(Uri address) {
return FindServicePoint(address, null);
}
/// <devdoc>
/// <para>Finds an existing <see cref='System.Net.ServicePoint'/> or creates a new <see cref='System.Net.ServicePoint'/> to manage communications to the
/// specified Uniform Resource Identifier.</para>
/// </devdoc>
public static ServicePoint FindServicePoint(string uriString, IWebProxy proxy) {
Uri uri = new Uri(uriString);
return FindServicePoint(uri, proxy);
}
//
// FindServicePoint - Query using an Uri for a given server point
//
/// <devdoc>
/// <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'/>
/// instance.</para>
/// </devdoc>
public static ServicePoint FindServicePoint(Uri address, IWebProxy proxy) {
ProxyChain chain;
HttpAbortDelegate abortDelegate = null;
int abortState = 0;
return FindServicePoint(address, proxy, out chain, ref abortDelegate, ref abortState);
}
// If abortState becomes non-zero, the attempt to find a service point has been aborted.
internal static ServicePoint FindServicePoint(Uri address, IWebProxy proxy, out ProxyChain chain, ref HttpAbortDelegate abortDelegate, ref int abortState)
{
if (address==null) {
throw new ArgumentNullException("address");
}
GlobalLog.Enter("ServicePointManager::FindServicePoint() address:" + address.ToString());
bool isProxyServicePoint = false;
chain = null;
//
// find proxy info, and then switch on proxy
//
Uri proxyAddress = null;
if (proxy!=null && !address.IsLoopback) {
IAutoWebProxy autoProxy = proxy as IAutoWebProxy;
if (autoProxy != null)
{
chain = autoProxy.GetProxies(address);
// Set up our ability to abort this MoveNext call. Note that the current implementations of ProxyChain will only
// take time on the first call, so this is the only place we do this. If a new ProxyChain takes time in later
// calls, this logic should be copied to other places MoveNext is called.
GlobalLog.Assert(abortDelegate == null, "ServicePointManager::FindServicePoint()|AbortDelegate already set.");
abortDelegate = chain.HttpAbortDelegate;
try
{
Thread.MemoryBarrier();
if (abortState != 0)
{
Exception exception = new WebException(NetRes.GetWebStatusString(WebExceptionStatus.RequestCanceled), WebExceptionStatus.RequestCanceled);
GlobalLog.LeaveException("ServicePointManager::FindServicePoint() Request aborted before proxy lookup.", exception);
throw exception;
}
if (!chain.Enumerator.MoveNext())
{
GlobalLog.Assert("ServicePointManager::FindServicePoint()|GetProxies() returned zero proxies.");
/*
Exception exception = new WebException(NetRes.GetWebStatusString(WebExceptionStatus.RequestProhibitedByProxy), WebExceptionStatus.RequestProhibitedByProxy);
GlobalLog.LeaveException("ServicePointManager::FindServicePoint() Proxy prevented request.", exception);
throw exception;
*/
}
proxyAddress = chain.Enumerator.Current;
}
finally
{
abortDelegate = null;
}
}
else if (!proxy.IsBypassed(address))
{
// use proxy support
// rework address
proxyAddress = proxy.GetProxy(address);
}
// null means DIRECT
if (proxyAddress!=null) {
address = proxyAddress;
isProxyServicePoint = true;
}
}
ServicePoint servicePoint = FindServicePointHelper(address, isProxyServicePoint);
GlobalLog.Leave("ServicePointManager::FindServicePoint() servicePoint#" + ValidationHelper.HashString(servicePoint));
return servicePoint;
}
// Returns null if we get to the end of the chain.
internal static ServicePoint FindServicePoint(ProxyChain chain)
{
GlobalLog.Print("ServicePointManager::FindServicePoint() Calling chained version.");
if (!chain.Enumerator.MoveNext())
{
return null;
}
Uri proxyAddress = chain.Enumerator.Current;
return FindServicePointHelper(proxyAddress == null ? chain.Destination : proxyAddress, proxyAddress != null);
}
private static ServicePoint FindServicePointHelper(Uri address, bool isProxyServicePoint)
{
GlobalLog.Enter("ServicePointManager::FindServicePointHelper() address:" + address.ToString());
if (isProxyServicePoint)
{
if (address.Scheme != Uri.UriSchemeHttp)
{
// <
Exception exception = new NotSupportedException(SR.GetString(SR.net_proxyschemenotsupported, address.Scheme));
GlobalLog.LeaveException("ServicePointManager::FindServicePointHelper() proxy has unsupported scheme:" + address.Scheme.ToString(), exception);
throw exception;
}
}
//
// Search for the correct proxy host,
// then match its acutal host by using ConnectionGroups
// which are located on the actual ServicePoint.
//
string tempEntry = MakeQueryString(address, isProxyServicePoint);
// lookup service point in the table
ServicePoint servicePoint = null;
GlobalLog.Print("ServicePointManager::FindServicePointHelper() locking and looking up tempEntry:[" + tempEntry.ToString() + "]");
lock (s_ServicePointTable) {
// once we grab the lock, check if it wasn't already added
WeakReference servicePointReference = s_ServicePointTable[tempEntry] as WeakReference;
GlobalLog.Print("ServicePointManager::FindServicePointHelper() lookup returned WeakReference#" + ValidationHelper.HashString(servicePointReference));
if ( servicePointReference != null ) {
servicePoint = (ServicePoint)servicePointReference.Target;
GlobalLog.Print("ServicePointManager::FindServicePointHelper() successful lookup returned ServicePoint#" + ValidationHelper.HashString(servicePoint));
}
if (servicePoint==null) {
// lookup failure or timeout, we need to create a new ServicePoint
if (s_MaxServicePoints<=0 || s_ServicePointTable.Count<s_MaxServicePoints) {
// Determine Connection Limit
int connectionLimit = InternalConnectionLimit;
string schemeHostPort = MakeQueryString(address);
bool userDefined = s_UserChangedLimit;
if (ConfigTable.ContainsKey(schemeHostPort) ) {
connectionLimit = (int) ConfigTable[schemeHostPort];
userDefined = true;
}
servicePoint = new ServicePoint(address, s_ServicePointIdlingQueue, connectionLimit, tempEntry, userDefined, isProxyServicePoint);
GlobalLog.Print("ServicePointManager::FindServicePointHelper() created ServicePoint#" + ValidationHelper.HashString(servicePoint));
servicePointReference = new WeakReference(servicePoint);
s_ServicePointTable[tempEntry] = servicePointReference;
GlobalLog.Print("ServicePointManager::FindServicePointHelper() adding entry WeakReference#" + ValidationHelper.HashString(servicePointReference) + " key:[" + tempEntry + "]");
}
else {
Exception exception = new InvalidOperationException(SR.GetString(SR.net_maxsrvpoints));
GlobalLog.LeaveException("ServicePointManager::FindServicePointHelper() reached the limit count:" + s_ServicePointTable.Count.ToString() + " limit:" + s_MaxServicePoints.ToString(), exception);
throw exception;
}
}
}
GlobalLog.Leave("ServicePointManager::FindServicePointHelper() servicePoint#" + ValidationHelper.HashString(servicePoint));
return servicePoint;
}
//
// FindServicePoint - Query using an Uri for a given server point
//
/// <devdoc>
/// <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'/>
/// instance.</para>
/// </devdoc>
internal static ServicePoint FindServicePoint(string host, int port) {
if (host==null) {
throw new ArgumentNullException("address");
}
GlobalLog.Enter("ServicePointManager::FindServicePoint() host:" + host.ToString());
string tempEntry = null;
bool isProxyServicePoint = false;
//
// Search for the correct proxy host,
// then match its acutal host by using ConnectionGroups
// which are located on the actual ServicePoint.
//
tempEntry = "ByHost:"+host+":"+port.ToString(CultureInfo.InvariantCulture);
// lookup service point in the table
ServicePoint servicePoint = null;
GlobalLog.Print("ServicePointManager::FindServicePoint() locking and looking up tempEntry:[" + tempEntry.ToString() + "]");
lock (s_ServicePointTable) {
// once we grab the lock, check if it wasn't already added
WeakReference servicePointReference = s_ServicePointTable[tempEntry] as WeakReference;
GlobalLog.Print("ServicePointManager::FindServicePoint() lookup returned WeakReference#" + ValidationHelper.HashString(servicePointReference));
if ( servicePointReference != null ) {
servicePoint = (ServicePoint)servicePointReference.Target;
GlobalLog.Print("ServicePointManager::FindServicePoint() successfull lookup returned ServicePoint#" + ValidationHelper.HashString(servicePoint));
}
if (servicePoint==null) {
// lookup failure or timeout, we need to create a new ServicePoint
if (s_MaxServicePoints<=0 || s_ServicePointTable.Count<s_MaxServicePoints) {
// Determine Connection Limit
int connectionLimit = InternalConnectionLimit;
bool userDefined = s_UserChangedLimit;
string schemeHostPort =host+":"+port.ToString(CultureInfo.InvariantCulture);
if (ConfigTable.ContainsKey(schemeHostPort) ) {
connectionLimit = (int) ConfigTable[schemeHostPort];
userDefined = true;
}
servicePoint = new ServicePoint(host, port, s_ServicePointIdlingQueue, connectionLimit, tempEntry, userDefined, isProxyServicePoint);
GlobalLog.Print("ServicePointManager::FindServicePoint() created ServicePoint#" + ValidationHelper.HashString(servicePoint));
servicePointReference = new WeakReference(servicePoint);
s_ServicePointTable[tempEntry] = servicePointReference;
GlobalLog.Print("ServicePointManager::FindServicePoint() adding entry WeakReference#" + ValidationHelper.HashString(servicePointReference) + " key:[" + tempEntry + "]");
}
else {
Exception exception = new InvalidOperationException(SR.GetString(SR.net_maxsrvpoints));
GlobalLog.LeaveException("ServicePointManager::FindServicePoint() reached the limit count:" + s_ServicePointTable.Count.ToString() + " limit:" + s_MaxServicePoints.ToString(), exception);
throw exception;
}
}
}
GlobalLog.Leave("ServicePointManager::FindServicePoint() servicePoint#" + ValidationHelper.HashString(servicePoint));
return servicePoint;
}
[FriendAccessAllowed]
internal static void CloseConnectionGroups(string connectionGroupName) {
// This method iterates through all service points and closes connection groups with the provided name.
ServicePoint servicePoint = null;
lock (s_ServicePointTable) {
foreach (DictionaryEntry item in s_ServicePointTable) {
WeakReference servicePointReference = item.Value as WeakReference;
if (servicePointReference != null) {
servicePoint = (ServicePoint)servicePointReference.Target;
if (servicePoint != null) {
// We found a service point. Ask the service point to close all internal connection groups
// with name 'connectionGroupName'.
servicePoint.CloseConnectionGroupInternal(connectionGroupName);
}
}
}
}
}
//
// SetTcpKeepAlive
//
// Enable/Disable the use of TCP keepalive option on ServicePoint
// connections. This method does not affect existing ServicePoints.
// When a ServicePoint is constructed it will inherit the current
// settings.
//
// Parameters:
//
// enabled - if true enables the use of the TCP keepalive option
// for ServicePoint connections.
//
// keepAliveTime - specifies the timeout, in milliseconds, with no
// activity until the first keep-alive packet is sent. Ignored if
// enabled parameter is false.
//
// keepAliveInterval - specifies the interval, in milliseconds, between
// when successive keep-alive packets are sent if no acknowledgement is
// received. Ignored if enabled parameter is false.
//
public static void SetTcpKeepAlive(
bool enabled,
int keepAliveTime,
int keepAliveInterval) {
GlobalLog.Enter(
"ServicePointManager::SetTcpKeepAlive()" +
" enabled: " + enabled.ToString() +
" keepAliveTime: " + keepAliveTime.ToString() +
" keepAliveInterval: " + keepAliveInterval.ToString()
);
if (enabled) {
s_UseTcpKeepAlive = true;
if (keepAliveTime <= 0) {
throw new ArgumentOutOfRangeException("keepAliveTime");
}
if (keepAliveInterval <= 0) {
throw new ArgumentOutOfRangeException("keepAliveInterval");
}
s_TcpKeepAliveTime = keepAliveTime;
s_TcpKeepAliveInterval = keepAliveInterval;
} else {
s_UseTcpKeepAlive = false;
s_TcpKeepAliveTime = 0;
s_TcpKeepAliveInterval =0;
}
GlobalLog.Leave("ServicePointManager::SetTcpKeepAlive()");
}
}
}
|