/// <summary>Initializes the pools.</summary>
        public HttpConnectionPoolManager(HttpConnectionSettings settings)
        {
            _settings = settings;
            _maxConnectionsPerServer = settings._maxConnectionsPerServer;
            _avoidStoringConnections =
                settings._pooledConnectionIdleTimeout == TimeSpan.Zero ||
                settings._pooledConnectionLifetime == TimeSpan.Zero;
            _pools = new ConcurrentDictionary <HttpConnectionKey, HttpConnectionPool>();

            // Start out with the timer not running, since we have no pools.
            // When it does run, run it with a frequency based on the idle timeout.
            if (!_avoidStoringConnections)
            {
                if (settings._pooledConnectionIdleTimeout == Timeout.InfiniteTimeSpan)
                {
                    const int DefaultScavengeSeconds = 30;
                    _cleanPoolTimeout = TimeSpan.FromSeconds(DefaultScavengeSeconds);
                }
                else
                {
                    const int ScavengesPerIdle   = 4;
                    const int MinScavengeSeconds = 1;
                    TimeSpan  timerPeriod        = settings._pooledConnectionIdleTimeout / ScavengesPerIdle;
                    _cleanPoolTimeout = timerPeriod.TotalSeconds >= MinScavengeSeconds ? timerPeriod : TimeSpan.FromSeconds(MinScavengeSeconds);
                }

                bool restoreFlow = false;
                try
                {
                    // Don't capture the current ExecutionContext and its AsyncLocals onto the timer causing them to live forever
                    if (!ExecutionContext.IsFlowSuppressed())
                    {
                        ExecutionContext.SuppressFlow();
                        restoreFlow = true;
                    }

                    _cleaningTimer = new Timer(s => ((HttpConnectionPoolManager)s).RemoveStalePools(), this, Timeout.Infinite, Timeout.Infinite);
                }
                finally
                {
                    // Restore the current ExecutionContext
                    if (restoreFlow)
                    {
                        ExecutionContext.RestoreFlow();
                    }
                }
            }

            // Figure out proxy stuff.
            if (settings._useProxy)
            {
                _proxy = settings._proxy ?? SystemProxyInfo.ConstructSystemProxy();
                if (_proxy != null)
                {
                    _proxyCredentials = _proxy.Credentials ?? settings._defaultProxyCredentials;
                }
            }
        }
        public HttpProxyConnectionHandler(HttpConnectionSettings settings, HttpMessageHandler innerHandler)
        {
            Debug.Assert(innerHandler != null);

            _innerHandler       = innerHandler;
            _proxy              = (settings._useProxy && settings._proxy != null) ? settings._proxy : new PassthroughWebProxy(s_proxyFromEnvironment.Value);
            _defaultCredentials = settings._defaultProxyCredentials;
            _connectionPools    = new HttpConnectionPools(settings._maxConnectionsPerServer);
        }
        public HttpProxyConnectionHandler(HttpConnectionSettings settings, HttpMessageHandler innerHandler)
        {
            Debug.Assert(innerHandler != null);
            Debug.Assert(settings._useProxy);

            _innerHandler       = innerHandler;
            _proxy              = settings._proxy ?? ConstructSystemProxy();
            _defaultCredentials = settings._defaultProxyCredentials;
            _connectionPools    = new HttpConnectionPools(settings, settings._maxConnectionsPerServer, usingProxy: true);
        }
        /// <summary>Creates a copy of the settings but with some values normalized to suit the implementation.</summary>
        public HttpConnectionSettings CloneAndNormalize()
        {
            // Force creation of the cookie container if needed, so the original and clone share the same instance.
            if (_useCookies && _cookieContainer == null)
            {
                _cookieContainer = new CookieContainer();
            }

            var settings = new HttpConnectionSettings()
            {
                _allowAutoRedirect               = _allowAutoRedirect,
                _automaticDecompression          = _automaticDecompression,
                _cookieContainer                 = _cookieContainer,
                _connectTimeout                  = _connectTimeout,
                _credentials                     = _credentials,
                _defaultProxyCredentials         = _defaultProxyCredentials,
                _defaultCredentialsUsedForProxy  = _defaultCredentialsUsedForProxy,
                _defaultCredentialsUsedForServer = _defaultCredentialsUsedForServer,
                _expect100ContinueTimeout        = _expect100ContinueTimeout,
                _maxAutomaticRedirections        = _maxAutomaticRedirections,
                _maxConnectionsPerServer         = _maxConnectionsPerServer,
                _maxHttpVersion                  = _maxHttpVersion,
                _maxResponseDrainSize            = _maxResponseDrainSize,
                _maxResponseDrainTime            = _maxResponseDrainTime,
                _maxResponseHeadersLength        = _maxResponseHeadersLength,
                _pooledConnectionLifetime        = _pooledConnectionLifetime,
                _pooledConnectionIdleTimeout     = _pooledConnectionIdleTimeout,
                _preAuthenticate                 = _preAuthenticate,
                _properties                     = _properties,
                _proxy                          = _proxy,
                _sslOptions                     = _sslOptions?.ShallowClone(), // shallow clone the options for basic prevention of mutation issues while processing
                _useCookies                     = _useCookies,
                _useProxy                       = _useProxy,
                _keepAlivePingTimeout           = _keepAlivePingTimeout,
                _keepAlivePingDelay             = _keepAlivePingDelay,
                _keepAlivePingPolicy            = _keepAlivePingPolicy,
                _requestHeaderEncodingSelector  = _requestHeaderEncodingSelector,
                _responseHeaderEncodingSelector = _responseHeaderEncodingSelector,
                _enableMultipleHttp2Connections = _enableMultipleHttp2Connections,
                _connectCallback                = _connectCallback,
                _plaintextStreamFilter          = _plaintextStreamFilter,
                _initialHttp2StreamWindowSize   = _initialHttp2StreamWindowSize,
                _activityHeadersPropagator      = _activityHeadersPropagator,
            };

            // TODO: Remove if/when QuicImplementationProvider is removed from System.Net.Quic.
            if (HttpConnectionPool.IsHttp3Supported())
            {
                settings._quicImplementationProvider = _quicImplementationProvider;
            }

            return(settings);
        }
        /// <summary>Creates a copy of the settings but with some values normalized to suit the implementation.</summary>
        public HttpConnectionSettings CloneAndNormalize()
        {
            // Force creation of the cookie container if needed, so the original and clone share the same instance.
            if (_useCookies && _cookieContainer == null)
            {
                _cookieContainer = new CookieContainer();
            }

            var settings = new HttpConnectionSettings()
            {
                _allowAutoRedirect               = _allowAutoRedirect,
                _automaticDecompression          = _automaticDecompression,
                _cookieContainer                 = _cookieContainer,
                _connectTimeout                  = _connectTimeout,
                _credentials                     = _credentials,
                _defaultProxyCredentials         = _defaultProxyCredentials,
                _defaultCredentialsUsedForProxy  = _defaultCredentialsUsedForProxy,
                _defaultCredentialsUsedForServer = _defaultCredentialsUsedForServer,
                _expect100ContinueTimeout        = _expect100ContinueTimeout,
                _maxAutomaticRedirections        = _maxAutomaticRedirections,
                _maxConnectionsPerServer         = _maxConnectionsPerServer,
                _maxHttpVersion                  = _maxHttpVersion,
                _maxResponseDrainSize            = _maxResponseDrainSize,
                _maxResponseDrainTime            = _maxResponseDrainTime,
                _maxResponseHeadersLength        = _maxResponseHeadersLength,
                _pooledConnectionLifetime        = _pooledConnectionLifetime,
                _pooledConnectionIdleTimeout     = _pooledConnectionIdleTimeout,
                _preAuthenticate                 = _preAuthenticate,
                _properties                     = _properties,
                _proxy                          = _proxy,
                _sslOptions                     = _sslOptions?.ShallowClone(), // shallow clone the options for basic prevention of mutation issues while processing
                _useCookies                     = _useCookies,
                _useProxy                       = _useProxy,
                _keepAlivePingTimeout           = _keepAlivePingTimeout,
                _keepAlivePingDelay             = _keepAlivePingDelay,
                _keepAlivePingPolicy            = _keepAlivePingPolicy,
                _requestHeaderEncodingSelector  = _requestHeaderEncodingSelector,
                _responseHeaderEncodingSelector = _responseHeaderEncodingSelector,
                _enableMultipleHttp2Connections = _enableMultipleHttp2Connections,
                _connectCallback                = _connectCallback,
                _plaintextStreamFilter          = _plaintextStreamFilter
            };

            // TODO: Replace with Platform-Guard Assertion Annotations once https://github.com/dotnet/runtime/issues/44922 is finished
            // TODO: Remove if/when QuicImplementationProvider is removed from System.Net.Quic.
            if ((OperatingSystem.IsLinux() && !OperatingSystem.IsAndroid()) || OperatingSystem.IsWindows() || OperatingSystem.IsMacOS())
            {
                settings._quicImplementationProvider = _quicImplementationProvider;
            }

            return(settings);
        }
            public Http2StreamWindowManager(Http2Connection connection, Http2Stream stream)
            {
                HttpConnectionSettings settings = connection._pool.Settings;

                _streamWindowSize = settings._initialHttp2StreamWindowSize;
                _deliveredBytes   = 0;
                _lastWindowUpdate = default;

                if (NetEventSource.Log.IsEnabled())
                {
                    stream.Trace($"[FlowControl] InitialClientStreamWindowSize: {StreamWindowSize}, StreamWindowThreshold: {StreamWindowThreshold}, WindowScaleThresholdMultiplier: {WindowScaleThresholdMultiplier}");
                }
            }
        /// <summary>Initializes the pools.</summary>
        public HttpConnectionPoolManager(HttpConnectionSettings settings)
        {
            _settings = settings;
            _maxConnectionsPerServer = settings._maxConnectionsPerServer;
            _pools = new ConcurrentDictionary <HttpConnectionKey, HttpConnectionPool>();

            // As an optimization, we can sometimes avoid the overheads associated with
            // storing connections.  This is possible when we would immediately terminate
            // connections anyway due to either the idle timeout or the lifetime being
            // set to zero, as in that case the timeout effectively immediately expires.
            // However, we can only do such optimizations if we're not also tracking
            // connections per server, as we use data in the associated data structures
            // to do that tracking.
            bool avoidStoringConnections =
                settings._maxConnectionsPerServer == int.MaxValue &&
                (settings._pooledConnectionIdleTimeout == TimeSpan.Zero ||
                 settings._pooledConnectionLifetime == TimeSpan.Zero);

            // Start out with the timer not running, since we have no pools.
            // When it does run, run it with a frequency based on the idle timeout.
            if (!avoidStoringConnections)
            {
                if (settings._pooledConnectionIdleTimeout == Timeout.InfiniteTimeSpan)
                {
                    const int DefaultScavengeSeconds = 30;
                    _cleanPoolTimeout = TimeSpan.FromSeconds(DefaultScavengeSeconds);
                }
                else
                {
                    const int ScavengesPerIdle   = 4;
                    const int MinScavengeSeconds = 1;
                    TimeSpan  timerPeriod        = settings._pooledConnectionIdleTimeout / ScavengesPerIdle;
                    _cleanPoolTimeout = timerPeriod.TotalSeconds >= MinScavengeSeconds ? timerPeriod : TimeSpan.FromSeconds(MinScavengeSeconds);
                }

                bool restoreFlow = false;
                try
                {
                    // Don't capture the current ExecutionContext and its AsyncLocals onto the timer causing them to live forever
                    if (!ExecutionContext.IsFlowSuppressed())
                    {
                        ExecutionContext.SuppressFlow();
                        restoreFlow = true;
                    }

                    // Create the timer.  Ensure the Timer has a weak reference to this manager; otherwise, it
                    // can introduce a cycle that keeps the HttpConnectionPoolManager rooted by the Timer
                    // implementation until the handler is Disposed (or indefinitely if it's not).
                    var thisRef = new WeakReference <HttpConnectionPoolManager>(this);

                    _cleaningTimer = new Timer(static s =>
        public static byte[] BuildSettingsFrame(HttpConnectionSettings settings)
        {
            Span <byte> buffer = stackalloc byte[4 + VariableLengthIntegerHelper.MaximumEncodedLength];

            int integerLength = VariableLengthIntegerHelper.WriteInteger(buffer.Slice(4), settings._maxResponseHeadersLength * 1024L);
            int payloadLength = 1 + integerLength; // includes the setting ID and the integer value.

            Debug.Assert(payloadLength <= VariableLengthIntegerHelper.OneByteLimit);

            buffer[0] = (byte)Http3StreamType.Control;
            buffer[1] = (byte)Http3FrameType.Settings;
            buffer[2] = (byte)payloadLength;
            buffer[3] = (byte)Http3SettingType.MaxHeaderListSize;

            return(buffer.Slice(4 + integerLength).ToArray());
        }
Beispiel #9
0
        public static async ValueTask <SslStream> EstablishSslConnectionAsync(HttpConnectionSettings settings, string host, HttpRequestMessage request, Stream stream, CancellationToken cancellationToken)
        {
            RemoteCertificateValidationCallback callback = null;

            if (settings._serverCertificateCustomValidationCallback != null)
            {
                callback = (object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) =>
                {
                    try
                    {
                        return(settings._serverCertificateCustomValidationCallback(request, certificate as X509Certificate2, chain, sslPolicyErrors));
                    }
                    catch (Exception e)
                    {
                        throw new HttpRequestException(SR.net_http_ssl_connection_failed, e);
                    }
                };
            }

            var sslStream = new SslStream(stream);

            try
            {
                await sslStream.AuthenticateAsClientAsync(new SslClientAuthenticationOptions
                {
                    TargetHost                          = host,
                    ClientCertificates                  = settings._clientCertificates,
                    EnabledSslProtocols                 = settings._sslProtocols,
                    CertificateRevocationCheckMode      = settings._checkCertificateRevocationList ? X509RevocationMode.Online : X509RevocationMode.NoCheck,
                    RemoteCertificateValidationCallback = callback
                }, cancellationToken).ConfigureAwait(false);
            }
            catch (Exception e)
            {
                sslStream.Dispose();
                if (e is AuthenticationException || e is IOException)
                {
                    throw new HttpRequestException(SR.net_http_ssl_connection_failed, e);
                }
                throw;
            }

            return(sslStream);
        }
Beispiel #10
0
        public static async ValueTask <SslStream> EstablishSslConnectionAsync(HttpConnectionSettings settings, string host, HttpRequestMessage request, Stream stream, CancellationToken cancellationToken)
        {
            // Create the options bag to use.  Since we mutate it, we don't just use the shared instance.
            SslClientAuthenticationOptions sslOptions;

            if (settings._sslOptions != null)
            {
                sslOptions = settings._sslOptions.ShallowClone();
                sslOptions.ApplicationProtocols = null; // explicitly ignore any ApplicationProtocols set
            }
            else
            {
                sslOptions = new SslClientAuthenticationOptions();
            }

            // Use the specified host, regardless of what was provided.
            sslOptions.TargetHost = host;

            // If there's a cert validation callback, and if it came from HttpClientHandler,
            // wrap the original delegate in order to change the sender to be the request message (expected by HttpClientHandler's delegate).
            RemoteCertificateValidationCallback callback = sslOptions.RemoteCertificateValidationCallback;

            if (callback != null && callback.Target is CertificateCallbackMapper mapper)
            {
                Func <HttpRequestMessage, X509Certificate2, X509Chain, SslPolicyErrors, bool> fromHttpClientHandler = mapper.FromHttpClientHandler;
                HttpRequestMessage localRequest = request;
                sslOptions.RemoteCertificateValidationCallback = (object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) =>
                                                                 fromHttpClientHandler(localRequest, certificate as X509Certificate2, chain, sslPolicyErrors);
            }

            // Create the SslStream, authenticate, and return it.
            var sslStream = new SslStream(stream);

            try
            {
                await sslStream.AuthenticateAsClientAsync(sslOptions, cancellationToken).ConfigureAwait(false);
            }
            catch (Exception e)
            {
                sslStream.Dispose();
                throw new HttpRequestException(SR.net_http_ssl_connection_failed, e);
            }
            return(sslStream);
        }
 public HttpConnectionHandler(HttpConnectionSettings settings)
 {
     _settings        = settings;
     _connectionPools = new HttpConnectionPools(settings._maxConnectionsPerServer);
 }
Beispiel #12
0
 public HttpConnectionHandler(HttpConnectionSettings settings)
 {
     _connectionPools = new HttpConnectionPools(settings, usingProxy: false);
 }
Beispiel #13
0
        /// <summary>Initializes the pools.</summary>
        public HttpConnectionPoolManager(HttpConnectionSettings settings)
        {
            _settings = settings;
            _maxConnectionsPerServer = settings._maxConnectionsPerServer;
            _avoidStoringConnections =
                settings._pooledConnectionIdleTimeout == TimeSpan.Zero ||
                settings._pooledConnectionLifetime == TimeSpan.Zero;
            _pools = new ConcurrentDictionary <HttpConnectionKey, HttpConnectionPool>();

            // Start out with the timer not running, since we have no pools.
            // When it does run, run it with a frequency based on the idle timeout.
            if (!_avoidStoringConnections)
            {
                if (settings._pooledConnectionIdleTimeout == Timeout.InfiniteTimeSpan)
                {
                    const int DefaultScavengeSeconds = 30;
                    _cleanPoolTimeout = TimeSpan.FromSeconds(DefaultScavengeSeconds);
                }
                else
                {
                    const int ScavengesPerIdle   = 4;
                    const int MinScavengeSeconds = 1;
                    TimeSpan  timerPeriod        = new TimeSpan(settings._pooledConnectionIdleTimeout.Ticks / ScavengesPerIdle);
                    _cleanPoolTimeout = timerPeriod.TotalSeconds >= MinScavengeSeconds ? timerPeriod : TimeSpan.FromSeconds(MinScavengeSeconds);
                }

                bool restoreFlow = false;
                try
                {
                    // Don't capture the current ExecutionContext and its AsyncLocals onto the timer causing them to live forever
                    if (!ExecutionContext.IsFlowSuppressed())
                    {
                        ExecutionContext.SuppressFlow();
                        restoreFlow = true;
                    }

                    // Create the timer.  Ensure the Timer has a weak reference to this manager; otherwise, it
                    // can introduce a cycle that keeps the HttpConnectionPoolManager rooted by the Timer
                    // implementation until the handler is Disposed (or indefinitely if it's not).
                    _cleaningTimer = new Timer(s =>
                    {
                        var wr = (WeakReference <HttpConnectionPoolManager>)s;
                        if (wr.TryGetTarget(out HttpConnectionPoolManager thisRef))
                        {
                            thisRef.RemoveStalePools();
                        }
                    }, new WeakReference <HttpConnectionPoolManager>(this), Timeout.Infinite, Timeout.Infinite);
                }
                finally
                {
                    // Restore the current ExecutionContext
                    if (restoreFlow)
                    {
                        ExecutionContext.RestoreFlow();
                    }
                }
            }

            // Figure out proxy stuff.
            if (settings._useProxy)
            {
                _proxy = settings._proxy;
                if (_proxy != null)
                {
                    _proxyCredentials = _proxy.Credentials ?? settings._defaultProxyCredentials;
                }
            }
        }
        /// <summary>Initializes the pools.</summary>
        public HttpConnectionPoolManager(HttpConnectionSettings settings)
        {
            _settings = settings;
            _maxConnectionsPerServer = settings._maxConnectionsPerServer;
            _pools = new ConcurrentDictionary <HttpConnectionKey, HttpConnectionPool>();

            // As an optimization, we can sometimes avoid the overheads associated with
            // storing connections.  This is possible when we would immediately terminate
            // connections anyway due to either the idle timeout or the lifetime being
            // set to zero, as in that case the timeout effectively immediately expires.
            // However, we can only do such optimizations if we're not also tracking
            // connections per server, as we use data in the associated data structures
            // to do that tracking.
            bool avoidStoringConnections =
                settings._maxConnectionsPerServer == int.MaxValue &&
                (settings._pooledConnectionIdleTimeout == TimeSpan.Zero ||
                 settings._pooledConnectionLifetime == TimeSpan.Zero);

            // Start out with the timer not running, since we have no pools.
            // When it does run, run it with a frequency based on the idle timeout.
            if (!avoidStoringConnections)
            {
                if (settings._pooledConnectionIdleTimeout == Timeout.InfiniteTimeSpan)
                {
                    const int DefaultScavengeSeconds = 30;
                    _cleanPoolTimeout = TimeSpan.FromSeconds(DefaultScavengeSeconds);
                }
                else
                {
                    const int ScavengesPerIdle   = 4;
                    const int MinScavengeSeconds = 1;
                    TimeSpan  timerPeriod        = settings._pooledConnectionIdleTimeout / ScavengesPerIdle;
                    _cleanPoolTimeout = timerPeriod.TotalSeconds >= MinScavengeSeconds ? timerPeriod : TimeSpan.FromSeconds(MinScavengeSeconds);
                }

                bool restoreFlow = false;
                try
                {
                    // Don't capture the current ExecutionContext and its AsyncLocals onto the timer causing them to live forever
                    if (!ExecutionContext.IsFlowSuppressed())
                    {
                        ExecutionContext.SuppressFlow();
                        restoreFlow = true;
                    }

                    // Create the timer.  Ensure the Timer has a weak reference to this manager; otherwise, it
                    // can introduce a cycle that keeps the HttpConnectionPoolManager rooted by the Timer
                    // implementation until the handler is Disposed (or indefinitely if it's not).
                    _cleaningTimer = new Timer(s =>
                    {
                        var wr = (WeakReference <HttpConnectionPoolManager>)s;
                        if (wr.TryGetTarget(out HttpConnectionPoolManager thisRef))
                        {
                            thisRef.RemoveStalePools();
                        }
                    }, new WeakReference <HttpConnectionPoolManager>(this), Timeout.Infinite, Timeout.Infinite);
                }
                finally
                {
                    // Restore the current ExecutionContext
                    if (restoreFlow)
                    {
                        ExecutionContext.RestoreFlow();
                    }
                }
            }

            // Figure out proxy stuff.
            if (settings._useProxy)
            {
                _proxy = settings._proxy ?? HttpClient.DefaultProxy;
                if (_proxy != null)
                {
                    _proxyCredentials = _proxy.Credentials ?? settings._defaultProxyCredentials;
                }
            }

            // Monitor network changes to invalidate Alt-Svc headers.
            // A weak reference is used to avoid NetworkChange.NetworkAddressChanged keeping a non-disposed connection pool alive.
            var poolsRef = new WeakReference <ConcurrentDictionary <HttpConnectionKey, HttpConnectionPool> >(_pools);
            NetworkAddressChangedEventHandler networkChangedDelegate = null;

            networkChangedDelegate = delegate
            {
                if (poolsRef.TryGetTarget(out ConcurrentDictionary <HttpConnectionKey, HttpConnectionPool> pools))
                {
                    foreach (HttpConnectionPool pool in pools.Values)
                    {
                        pool.OnNetworkChanged();
                    }
                }
                else
                {
                    // Our pools were GCed; user did not dispose the handler.
                    NetworkChange.NetworkAddressChanged -= networkChangedDelegate;
                }
            };

            _networkChangedDelegate = networkChangedDelegate;

            using (ExecutionContext.SuppressFlow())
            {
                NetworkChange.NetworkAddressChanged += networkChangedDelegate;
            }
        }
 public HttpConnectionHandler(HttpConnectionSettings settings)
 {
     _connectionPools = new HttpConnectionPools(settings, settings._maxConnectionsPerServer, usingProxy: false);
 }
        /// <summary>Initializes the pools.</summary>
        public HttpConnectionPoolManager(HttpConnectionSettings settings)
        {
            _settings = settings;
            _maxConnectionsPerServer = settings._maxConnectionsPerServer;
            _pools = new ConcurrentDictionary <HttpConnectionKey, HttpConnectionPool>();

            // As an optimization, we can sometimes avoid the overheads associated with
            // storing connections.  This is possible when we would immediately terminate
            // connections anyway due to either the idle timeout or the lifetime being
            // set to zero, as in that case the timeout effectively immediately expires.
            // However, we can only do such optimizations if we're not also tracking
            // connections per server, as we use data in the associated data structures
            // to do that tracking.
            bool avoidStoringConnections =
                settings._maxConnectionsPerServer == int.MaxValue &&
                (settings._pooledConnectionIdleTimeout == TimeSpan.Zero ||
                 settings._pooledConnectionLifetime == TimeSpan.Zero);

            // Start out with the timer not running, since we have no pools.
            // When it does run, run it with a frequency based on the idle timeout.
            if (!avoidStoringConnections)
            {
                if (settings._pooledConnectionIdleTimeout == Timeout.InfiniteTimeSpan)
                {
                    const int DefaultScavengeSeconds = 30;
                    _cleanPoolTimeout = TimeSpan.FromSeconds(DefaultScavengeSeconds);
                }
                else
                {
                    const int ScavengesPerIdle   = 4;
                    const int MinScavengeSeconds = 1;
                    TimeSpan  timerPeriod        = settings._pooledConnectionIdleTimeout / ScavengesPerIdle;
                    _cleanPoolTimeout = timerPeriod.TotalSeconds >= MinScavengeSeconds ? timerPeriod : TimeSpan.FromSeconds(MinScavengeSeconds);
                }

                bool restoreFlow = false;
                try
                {
                    // Don't capture the current ExecutionContext and its AsyncLocals onto the timer causing them to live forever
                    if (!ExecutionContext.IsFlowSuppressed())
                    {
                        ExecutionContext.SuppressFlow();
                        restoreFlow = true;
                    }

                    _cleaningTimer = new Timer(s => ((HttpConnectionPoolManager)s).RemoveStalePools(), this, Timeout.Infinite, Timeout.Infinite);
                }
                finally
                {
                    // Restore the current ExecutionContext
                    if (restoreFlow)
                    {
                        ExecutionContext.RestoreFlow();
                    }
                }
            }

            // Figure out proxy stuff.
            if (settings._useProxy)
            {
                _proxy = settings._proxy ?? SystemProxyInfo.ConstructSystemProxy();
                if (_proxy != null)
                {
                    _proxyCredentials = _proxy.Credentials ?? settings._defaultProxyCredentials;
                }
            }
        }