@@ -17,8 +17,10 @@ namespace System.Net.Http
1717 /// <summary>Provides a pool of connections to the same endpoint.</summary>
1818 internal sealed class HttpConnectionPool : IDisposable
1919 {
20- private readonly HttpConnectionPools _pools ;
21- private readonly HttpConnectionKey _key ;
20+ private readonly HttpConnectionPoolManager _poolManager ;
21+ private readonly string _host ;
22+ private readonly int _port ;
23+ private readonly Uri _proxyUri ;
2224
2325 /// <summary>List of idle connections stored in the pool.</summary>
2426 private readonly List < CachedConnection > _idleConnections = new List < CachedConnection > ( ) ;
@@ -44,39 +46,38 @@ internal sealed class HttpConnectionPool : IDisposable
4446
4547 /// <summary>Initializes the pool.</summary>
4648 /// <param name="maxConnections">The maximum number of connections allowed to be associated with the pool at any given time.</param>
47- public HttpConnectionPool ( HttpConnectionPools pools , HttpConnectionKey key , int maxConnections = int . MaxValue ) // int.MaxValue treated as infinite
49+ ///
50+ public HttpConnectionPool ( HttpConnectionPoolManager poolManager , string host , int port , string sslHostName , Uri proxyUri , int maxConnections )
4851 {
49- _pools = pools ;
50- _key = key ;
52+ _poolManager = poolManager ;
53+ _host = host ;
54+ _port = port ;
55+ _proxyUri = proxyUri ;
5156 _maxConnections = maxConnections ;
5257
53- // Precalculate ASCII bytes for header name
54- // We don't do this for proxy connections because the actual host header varies on a proxy connection.
55- if ( ! pools . UsingProxy )
58+ if ( sslHostName != null )
5659 {
57- // CONSIDER: Cache more than just host name -- port, header name, etc
58-
59- // Note the IDN hostname should always be ASCII, since it's already been IDNA encoded.
60- _idnHostAsciiBytes = Encoding . ASCII . GetBytes ( key . Host ) ;
61- Debug . Assert ( Encoding . ASCII . GetString ( _idnHostAsciiBytes ) == key . Host ) ;
62- }
63- else
64- {
65- // Proxy connections should never use SSL
66- Debug . Assert ( ! key . IsSecure ) ;
60+ // Precalculate cached SSL options to use for all connections.
61+ _sslOptions = _poolManager . Settings . _sslOptions ? . ShallowClone ( ) ?? new SslClientAuthenticationOptions ( ) ;
62+ _sslOptions . ApplicationProtocols = null ; // explicitly ignore any ApplicationProtocols set
63+ _sslOptions . TargetHost = sslHostName ; // always use the key's name rather than whatever was specified
6764 }
6865
69- if ( key . IsSecure )
66+ if ( _host != null )
7067 {
71- // Precalculate cached SSL options to use for all connections.
72- _sslOptions = _pools . Settings . _sslOptions ? . ShallowClone ( ) ?? new SslClientAuthenticationOptions ( ) ;
73- _sslOptions . ApplicationProtocols = null ; // explicitly ignore any ApplicationProtocols set
74- _sslOptions . TargetHost = key . SslHostName ; // always use the key's name rather than whatever was specified
68+ // Precalculate ASCII bytes for header name
69+ // Note that if _host is null, this is a (non-tunneled) proxy connection, and we can't cache the hostname.
70+ // CONSIDER: Cache more than just host name -- port, header name, etc
71+
72+ // Note the IDN hostname should always be ASCII, since it's already been IDNA encoded.
73+ _idnHostAsciiBytes = Encoding . ASCII . GetBytes ( _host ) ;
74+ Debug . Assert ( Encoding . ASCII . GetString ( _idnHostAsciiBytes ) == _host ) ;
7575 }
7676 }
7777
78- public HttpConnectionKey Key => _key ;
79- public HttpConnectionPools Pools => _pools ;
78+ public HttpConnectionSettings Settings => _poolManager . Settings ;
79+ public bool IsSecure => _sslOptions != null ;
80+ public bool UsingProxy => ( _proxyUri != null && ! IsSecure ) ; // Tunnel doesn't count, only direct proxy usage
8081 public byte [ ] IdnHostAsciiBytes => _idnHostAsciiBytes ;
8182
8283 /// <summary>Object used to synchronize access to state in the pool.</summary>
@@ -89,8 +90,8 @@ private ValueTask<HttpConnection> GetConnectionAsync(HttpRequestMessage request,
8990 return new ValueTask < HttpConnection > ( Task . FromCanceled < HttpConnection > ( cancellationToken ) ) ;
9091 }
9192
92- TimeSpan pooledConnectionLifetime = _pools . Settings . _pooledConnectionLifetime ;
93- TimeSpan pooledConnectionIdleTimeout = _pools . Settings . _pooledConnectionIdleTimeout ;
93+ TimeSpan pooledConnectionLifetime = _poolManager . Settings . _pooledConnectionLifetime ;
94+ TimeSpan pooledConnectionIdleTimeout = _poolManager . Settings . _pooledConnectionIdleTimeout ;
9495 DateTimeOffset now = DateTimeOffset . UtcNow ;
9596 List < CachedConnection > list = _idleConnections ;
9697 lock ( SyncObj )
@@ -199,10 +200,15 @@ public async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, Can
199200
200201 private async ValueTask < HttpConnection > CreateConnectionAsync ( HttpRequestMessage request , CancellationToken cancellationToken )
201202 {
202- Stream stream = await ConnectHelper . ConnectAsync ( _key , cancellationToken ) . ConfigureAwait ( false ) ;
203+ Stream stream =
204+ _proxyUri != null ?
205+ ( _sslOptions != null ?
206+ throw new NotSupportedException ( "SSL Proxy tunneling not currently supported" ) :
207+ await ConnectHelper . ConnectAsync ( _proxyUri . IdnHost , _proxyUri . Port , cancellationToken ) ) :
208+ await ConnectHelper . ConnectAsync ( _host , _port , cancellationToken ) ;
203209
204210 TransportContext transportContext = null ;
205- if ( _key . IsSecure )
211+ if ( _sslOptions != null )
206212 {
207213 SslStream sslStream = await ConnectHelper . EstablishSslConnectionAsync ( _sslOptions , request , stream , cancellationToken ) . ConfigureAwait ( false ) ;
208214 stream = sslStream ;
@@ -473,8 +479,8 @@ public void Dispose()
473479 /// </returns>
474480 public bool CleanCacheAndDisposeIfUnused ( )
475481 {
476- TimeSpan pooledConnectionLifetime = _pools . Settings . _pooledConnectionLifetime ;
477- TimeSpan pooledConnectionIdleTimeout = _pools . Settings . _pooledConnectionIdleTimeout ;
482+ TimeSpan pooledConnectionLifetime = _poolManager . Settings . _pooledConnectionLifetime ;
483+ TimeSpan pooledConnectionIdleTimeout = _poolManager . Settings . _pooledConnectionIdleTimeout ;
478484
479485 List < CachedConnection > list = _idleConnections ;
480486 List < HttpConnection > toDispose = null ;
@@ -560,7 +566,16 @@ public bool CleanCacheAndDisposeIfUnused()
560566 return false ;
561567 }
562568
563- public override string ToString ( ) => $ "{ nameof ( HttpConnectionPool ) } (Connections:{ _associatedConnectionCount } )"; // Description for diagnostic purposes
569+ // For diagnostic purposes
570+ public override string ToString ( ) =>
571+ $ "{ nameof ( HttpConnectionPool ) } " +
572+ ( _proxyUri == null ?
573+ ( _sslOptions == null ?
574+ $ "http://{ _host } :{ _port } " :
575+ $ "https://{ _host } :{ _port } " + ( _sslOptions . TargetHost != _host ? $ ", SSL TargetHost={ _sslOptions . TargetHost } " : null ) ) :
576+ ( _sslOptions == null ?
577+ $ "Proxy { _proxyUri } " :
578+ $ "https://{ _host } :{ _port } / tunnelled via Proxy { _proxyUri } " + ( _sslOptions . TargetHost != _host ? $ ", SSL TargetHost={ _sslOptions . TargetHost } " : null ) ) ) ;
564579
565580 private void Trace ( string message , [ CallerMemberName ] string memberName = null ) =>
566581 NetEventSource . Log . HandlerMessage (
0 commit comments