@@ -236,10 +236,9 @@ public HttpConnectionPool(HttpConnectionPoolManager poolManager, HttpConnectionK
236
236
{
237
237
// Precalculate ASCII bytes for Host header
238
238
// Note that if _host is null, this is a (non-tunneled) proxy connection, and we can't cache the hostname.
239
- hostHeader =
240
- ( _originAuthority . Port != ( sslHostName == null ? DefaultHttpPort : DefaultHttpsPort ) ) ?
241
- $ "{ _originAuthority . HostValue } :{ _originAuthority . Port } " :
242
- _originAuthority . HostValue ;
239
+ hostHeader = IsDefaultPort
240
+ ? _originAuthority . HostValue
241
+ : $ "{ _originAuthority . HostValue } :{ _originAuthority . Port } ";
243
242
244
243
// Note the IDN hostname should always be ASCII, since it's already been IDNA encoded.
245
244
byte [ ] hostHeaderLine = new byte [ 6 + hostHeader . Length + 2 ] ; // Host: foo\r\n
@@ -363,6 +362,7 @@ private static SslClientAuthenticationOptions ConstructSslOptions(HttpConnection
363
362
public ICredentials ? ProxyCredentials => _poolManager . ProxyCredentials ;
364
363
public byte [ ] ? HostHeaderLineBytes => _hostHeaderLineBytes ;
365
364
public CredentialCache ? PreAuthCredentials { get ; }
365
+ public bool IsDefaultPort => OriginAuthority . Port == ( IsSecure ? DefaultHttpsPort : DefaultHttpPort ) ;
366
366
367
367
/// <summary>
368
368
/// An ASCII origin string per RFC 6454 Section 6.2, in format <scheme>://<host>[:<port>]
@@ -381,7 +381,7 @@ public byte[] Http2AltSvcOriginUri
381
381
sb . Append ( IsSecure ? "https://" : "http://" )
382
382
. Append ( _originAuthority . IdnHost ) ;
383
383
384
- if ( _originAuthority . Port != ( IsSecure ? DefaultHttpsPort : DefaultHttpPort ) )
384
+ if ( ! IsDefaultPort )
385
385
{
386
386
sb . Append ( CultureInfo . InvariantCulture , $ ":{ _originAuthority . Port } ") ;
387
387
}
@@ -998,16 +998,10 @@ private async ValueTask<Http3Connection> GetHttp3ConnectionAsync(HttpRequestMess
998
998
ThrowGetVersionException ( request , 3 , reasonException ) ;
999
999
}
1000
1000
1001
- long queueStartingTimestamp = HttpTelemetry . Log . IsEnabled ( ) ? Stopwatch . GetTimestamp ( ) : 0 ;
1001
+ long queueStartingTimestamp = HttpTelemetry . Log . IsEnabled ( ) || Settings . _metrics ! . RequestsQueueDuration . Enabled ? Stopwatch . GetTimestamp ( ) : 0 ;
1002
1002
1003
1003
ValueTask < Http3Connection > connectionTask = GetHttp3ConnectionAsync ( request , authority , cancellationToken ) ;
1004
1004
1005
- if ( HttpTelemetry . Log . IsEnabled ( ) && connectionTask . IsCompleted )
1006
- {
1007
- // We avoid logging RequestLeftQueue if a stream was available immediately (synchronously)
1008
- queueStartingTimestamp = 0 ;
1009
- }
1010
-
1011
1005
Http3Connection connection = await connectionTask . ConfigureAwait ( false ) ;
1012
1006
1013
1007
HttpResponseMessage response = await connection . SendAsync ( request , queueStartingTimestamp , cancellationToken ) . ConfigureAwait ( false ) ;
@@ -1089,7 +1083,7 @@ public async ValueTask<HttpResponseMessage> SendWithVersionDetectionAndRetryAsyn
1089
1083
if ( ! TryGetPooledHttp2Connection ( request , out Http2Connection ? connection , out http2ConnectionWaiter ) &&
1090
1084
http2ConnectionWaiter != null )
1091
1085
{
1092
- connection = await http2ConnectionWaiter . WaitForConnectionAsync ( async , cancellationToken ) . ConfigureAwait ( false ) ;
1086
+ connection = await http2ConnectionWaiter . WaitForConnectionAsync ( this , async, cancellationToken ) . ConfigureAwait ( false ) ;
1093
1087
}
1094
1088
1095
1089
Debug . Assert ( connection is not null || ! _http2Enabled ) ;
@@ -1121,7 +1115,7 @@ public async ValueTask<HttpResponseMessage> SendWithVersionDetectionAndRetryAsyn
1121
1115
// Use HTTP/1.x.
1122
1116
if ( ! TryGetPooledHttp11Connection ( request , async , out HttpConnection ? connection , out http11ConnectionWaiter ) )
1123
1117
{
1124
- connection = await http11ConnectionWaiter . WaitForConnectionAsync ( async , cancellationToken ) . ConfigureAwait( false) ;
1118
+ connection = await http11ConnectionWaiter . WaitForConnectionAsync ( this , async, cancellationToken ) . ConfigureAwait ( false ) ;
1125
1119
}
1126
1120
1127
1121
connection. Acquire ( ) ; // In case we are doing Windows (i.e. connection-based) auth, we need to ensure that we hold on to this specific connection while auth is underway.
@@ -1199,6 +1193,7 @@ public async ValueTask<HttpResponseMessage> SendWithVersionDetectionAndRetryAsyn
1199
1193
}
1200
1194
1201
1195
private void CancelIfNecessary< T > ( HttpConnectionWaiter < T > ? waiter , bool requestCancelled )
1196
+ where T : HttpConnectionBase?
1202
1197
{
1203
1198
int timeout = GlobalHttpSettings. SocketsHttpHandler. PendingConnectionTimeoutOnRequestCompletion;
1204
1199
if ( waiter ? . ConnectionCancellationTokenSource is null ||
@@ -2430,6 +2425,7 @@ private void Trace(string? message, [CallerMemberName] string? memberName = null
2430
2425
message ) ; // message
2431
2426
2432
2427
private struct RequestQueue < T >
2428
+ where T : HttpConnectionBase?
2433
2429
{
2434
2430
public struct QueueItem
2435
2431
{
@@ -2599,6 +2595,7 @@ public QueueItem PeekNextRequestForConnectionAttempt()
2599
2595
}
2600
2596
2601
2597
private sealed class HttpConnectionWaiter < T > : TaskCompletionSourceWithCancellation< T >
2598
+ where T : HttpConnectionBase?
2602
2599
{
2603
2600
// When a connection attempt is pending, reference the connection's CTS, so we can tear it down if the initiating request is cancelled
2604
2601
// or completes on a different connection.
@@ -2607,21 +2604,32 @@ private sealed class HttpConnectionWaiter<T> : TaskCompletionSourceWithCancellat
2607
2604
// Distinguish connection cancellation that happens because the initiating request is cancelled or completed on a different connection.
2608
2605
public bool CancelledByOriginatingRequestCompletion { get ; set ; }
2609
2606
2610
- public async ValueTask < T > WaitForConnectionAsync ( bool async , CancellationToken requestCancellationToken )
2607
+ public ValueTask < T > WaitForConnectionAsync ( HttpConnectionPool pool , bool async , CancellationToken requestCancellationToken )
2611
2608
{
2609
+ return HttpTelemetry . Log . IsEnabled ( ) || pool . Settings . _metrics ! . RequestsQueueDuration . Enabled
2610
+ ? WaitForConnectionWithTelemetryAsync ( pool , async , requestCancellationToken )
2611
+ : WaitWithCancellationAsync( async , requestCancellationToken ) ;
2612
+ }
2613
+
2614
+ private async ValueTask < T > WaitForConnectionWithTelemetryAsync ( HttpConnectionPool pool , bool async , CancellationToken requestCancellationToken )
2615
+ {
2616
+ Debug . Assert ( typeof ( T ) == typeof ( HttpConnection ) || typeof ( T ) == typeof ( Http2Connection ) ) ;
2617
+
2612
2618
long startingTimestamp = Stopwatch . GetTimestamp ( ) ;
2613
2619
try
2614
2620
{
2615
2621
return await WaitWithCancellationAsync ( async , requestCancellationToken ) . ConfigureAwait( false) ;
2616
2622
}
2617
2623
finally
2618
2624
{
2625
+ TimeSpan duration = Stopwatch . GetElapsedTime ( startingTimestamp ) ;
2626
+ int versionMajor = typeof ( T ) == typeof ( HttpConnection ) ? 1 : 2 ;
2627
+
2628
+ pool . Settings . _metrics ! . RequestLeftQueue ( pool , duration , versionMajor ) ;
2629
+
2619
2630
if ( HttpTelemetry . Log . IsEnabled ( ) )
2620
2631
{
2621
- if ( typeof ( T ) == typeof ( HttpConnection ) )
2622
- HttpTelemetry . Log . Http11RequestLeftQueue ( Stopwatch . GetElapsedTime ( startingTimestamp ) . TotalMilliseconds ) ;
2623
- else if ( typeof ( T ) == typeof ( Http2Connection ) )
2624
- HttpTelemetry . Log . Http20RequestLeftQueue ( Stopwatch . GetElapsedTime ( startingTimestamp ) . TotalMilliseconds ) ;
2632
+ HttpTelemetry . Log . RequestLeftQueue ( versionMajor , duration ) ;
2625
2633
}
2626
2634
}
2627
2635
}
0 commit comments