@@ -567,6 +567,7 @@ private static bool IsUsableHttp11Connection(HttpConnection connection, long now
567567 }
568568
569569 // Try to establish an HTTP2 connection
570+ Socket ? socket = null ;
570571 Stream ? stream = null ;
571572 SslStream ? sslStream = null ;
572573 TransportContext ? transportContext = null ;
@@ -591,7 +592,7 @@ private static bool IsUsableHttp11Connection(HttpConnection connection, long now
591592
592593 HttpResponseMessage ? failureResponse ;
593594
594- ( stream , transportContext , failureResponse ) =
595+ ( socket , stream , transportContext , failureResponse ) =
595596 await ConnectAsync ( request , async , cancellationToken ) . ConfigureAwait ( false ) ;
596597
597598 if ( failureResponse != null )
@@ -681,7 +682,7 @@ private static bool IsUsableHttp11Connection(HttpConnection connection, long now
681682
682683 if ( canUse )
683684 {
684- return ( await ConstructHttp11ConnectionAsync ( async , stream ! , transportContext , request , cancellationToken ) . ConfigureAwait( false) , true , null ) ;
685+ return ( await ConstructHttp11ConnectionAsync ( async , socket , stream ! , transportContext , request , cancellationToken ) . ConfigureAwait( false) , true , null ) ;
685686 }
686687 else
687688 {
@@ -1213,7 +1214,7 @@ public ValueTask<HttpResponseMessage> SendAsync(HttpRequestMessage request, bool
12131214 return SendWithProxyAuthAsync ( request , async , doRequestAuth , cancellationToken ) ;
12141215 }
12151216
1216- private async ValueTask< ( Stream? , TransportContext? , HttpResponseMessage? ) > ConnectAsync( HttpRequestMessage request, bool async, CancellationToken cancellationToken)
1217+ private async ValueTask< ( Socket ? , Stream? , TransportContext? , HttpResponseMessage? ) > ConnectAsync( HttpRequestMessage request, bool async, CancellationToken cancellationToken)
12171218 {
12181219 // If a non-infinite connect timeout has been set, create and use a new CancellationToken that will be canceled
12191220 // when either the original token is canceled or a connect timeout occurs.
@@ -1228,17 +1229,18 @@ public ValueTask<HttpResponseMessage> SendAsync(HttpRequestMessage request, bool
12281229 try
12291230 {
12301231 Stream? stream = null ;
1232+ Socket? socket = null ;
12311233 switch ( _kind)
12321234 {
12331235 case HttpConnectionKind. Http:
12341236 case HttpConnectionKind. Https:
12351237 case HttpConnectionKind. ProxyConnect:
12361238 Debug. Assert( _originAuthority != null ) ;
1237- stream = await ConnectToTcpHostAsync( _originAuthority. IdnHost, _originAuthority. Port, request, async, cancellationToken) . ConfigureAwait( false) ;
1239+ ( socket , stream) = await ConnectToTcpHostAsync( _originAuthority. IdnHost, _originAuthority. Port, request, async, cancellationToken) . ConfigureAwait( false) ;
12381240 break ;
12391241
12401242 case HttpConnectionKind. Proxy:
1241- stream = await ConnectToTcpHostAsync( _proxyUri! . IdnHost, _proxyUri. Port, request, async, cancellationToken) . ConfigureAwait( false) ;
1243+ ( socket , stream) = await ConnectToTcpHostAsync( _proxyUri! . IdnHost, _proxyUri. Port, request, async, cancellationToken) . ConfigureAwait( false) ;
12421244 break ;
12431245
12441246 case HttpConnectionKind. ProxyTunnel:
@@ -1249,12 +1251,18 @@ public ValueTask<HttpResponseMessage> SendAsync(HttpRequestMessage request, bool
12491251 {
12501252 // Return non-success response from proxy.
12511253 response. RequestMessage = request;
1252- return ( null , null , response) ;
1254+ return ( null , null , null , response) ;
12531255 }
12541256 break ;
12551257 }
12561258
12571259 Debug. Assert( stream != null ) ;
1260+ if ( socket is null && stream is NetworkStream ns)
1261+ {
1262+ // We weren't handed a socket directly. But if we're able to extract one, do so.
1263+ // Most likely case here is a ConnectCallback was used and returned a NetworkStream.
1264+ socket = ns. Socket;
1265+ }
12581266
12591267 TransportContext? transportContext = null ;
12601268 if ( IsSecure)
@@ -1264,20 +1272,21 @@ public ValueTask<HttpResponseMessage> SendAsync(HttpRequestMessage request, bool
12641272 stream = sslStream;
12651273 }
12661274
1267- return ( stream, transportContext, null ) ;
1275+ return ( socket , stream, transportContext, null ) ;
12681276 }
12691277 finally
12701278 {
12711279 cancellationWithConnectTimeout? . Dispose( ) ;
12721280 }
12731281 }
12741282
1275- private async ValueTask< Stream> ConnectToTcpHostAsync( string host , int port , HttpRequestMessage initialRequest , bool async , CancellationToken cancellationToken )
1283+ private async ValueTask< ( Socket ? , Stream) > ConnectToTcpHostAsync( string host , int port , HttpRequestMessage initialRequest , bool async , CancellationToken cancellationToken )
12761284 {
12771285 cancellationToken . ThrowIfCancellationRequested ( ) ;
12781286
12791287 var endPoint = new DnsEndPoint ( host , port ) ;
12801288 Socket ? socket = null ;
1289+ Stream ? stream = null ;
12811290 try
12821291 {
12831292 // If a ConnectCallback was supplied, use that to establish the connection.
@@ -1294,7 +1303,7 @@ private async ValueTask<Stream> ConnectToTcpHostAsync(string host, int port, Htt
12941303 Trace ( $ "{ nameof ( SocketsHttpHandler . ConnectCallback ) } completing asynchronously for a synchronous request.") ;
12951304 }
12961305
1297- return await streamTask. ConfigureAwait ( false ) ?? throw new HttpRequestException ( SR . net_http_null_from_connect_callback ) ;
1306+ stream = await streamTask. ConfigureAwait ( false ) ?? throw new HttpRequestException ( SR . net_http_null_from_connect_callback ) ;
12981307 }
12991308 else
13001309 {
@@ -1313,8 +1322,10 @@ private async ValueTask<Stream> ConnectToTcpHostAsync(string host, int port, Htt
13131322 }
13141323 }
13151324
1316- return new NetworkStream ( socket , ownsSocket : true ) ;
1325+ stream = new NetworkStream ( socket , ownsSocket : true ) ;
13171326 }
1327+
1328+ return ( socket , stream ) ;
13181329 }
13191330 catch ( Exception ex )
13201331 {
@@ -1327,15 +1338,15 @@ private async ValueTask<Stream> ConnectToTcpHostAsync(string host, int port, Htt
13271338
13281339 internal async ValueTask < ( HttpConnection ? , HttpResponseMessage ? ) > CreateHttp11ConnectionAsync ( HttpRequestMessage request , bool async , CancellationToken cancellationToken )
13291340 {
1330- ( Stream ? stream , TransportContext ? transportContext , HttpResponseMessage ? failureResponse ) =
1341+ ( Socket ? socket , Stream ? stream , TransportContext ? transportContext , HttpResponseMessage ? failureResponse ) =
13311342 await ConnectAsync ( request , async , cancellationToken ) . ConfigureAwait( false) ;
13321343
13331344 if ( failureResponse != null )
13341345 {
13351346 return ( null , failureResponse ) ;
13361347 }
13371348
1338- return ( await ConstructHttp11ConnectionAsync ( async , stream ! , transportContext , request , cancellationToken ) . ConfigureAwait ( false ) , null ) ;
1349+ return ( await ConstructHttp11ConnectionAsync ( async , socket , stream ! , transportContext , request , cancellationToken ) . ConfigureAwait ( false ) , null ) ;
13391350 }
13401351
13411352 private SslClientAuthenticationOptions GetSslOptionsForRequest( HttpRequestMessage request)
@@ -1393,10 +1404,15 @@ private async ValueTask<Stream> ApplyPlaintextFilterAsync(bool async, Stream str
13931404 return newStream;
13941405 }
13951406
1396- private async ValueTask< HttpConnection > ConstructHttp11ConnectionAsync ( bool async , Stream stream , TransportContext ? transportContext , HttpRequestMessage request , CancellationToken cancellationToken )
1407+ private async ValueTask< HttpConnection > ConstructHttp11ConnectionAsync ( bool async , Socket ? socket , Stream stream , TransportContext ? transportContext , HttpRequestMessage request , CancellationToken cancellationToken )
13971408 {
1398- stream = await ApplyPlaintextFilterAsync ( async , stream , HttpVersion . Version11 , request , cancellationToken ) . ConfigureAwait( false) ;
1399- return new HttpConnection ( this , stream , transportContext ) ;
1409+ Stream newStream = await ApplyPlaintextFilterAsync ( async , stream , HttpVersion . Version11 , request , cancellationToken ) . ConfigureAwait( false) ;
1410+ if ( newStream != stream)
1411+ {
1412+ // If a plaintext filter created a new stream, we can't trust that the socket is still applicable.
1413+ socket = null ;
1414+ }
1415+ return new HttpConnection( this , socket , newStream , transportContext ) ;
14001416 }
14011417
14021418 private async ValueTask < Http2Connection > ConstructHttp2ConnectionAsync ( Stream stream , HttpRequestMessage request , CancellationToken cancellationToken )
0 commit comments