Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit 7301c4f

Browse files
authored
Merge pull request #31589 from davidsh/port_30327_21branch
[release/2.1] Handle NT auth with Connection: close on initial challenge #31527
2 parents 663b96d + 95f382a commit 7301c4f

File tree

3 files changed

+75
-41
lines changed

3 files changed

+75
-41
lines changed

src/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/AuthenticationHelper.NtAuth.cs

Lines changed: 52 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@ namespace System.Net.Http
1212
{
1313
internal partial class AuthenticationHelper
1414
{
15-
private static Task<HttpResponseMessage> InnerSendAsync(HttpRequestMessage request, bool isProxyAuth, HttpConnection connection, CancellationToken cancellationToken)
15+
private static Task<HttpResponseMessage> InnerSendAsync(HttpRequestMessage request, bool isProxyAuth, HttpConnectionPool pool, HttpConnection connection, CancellationToken cancellationToken)
1616
{
1717
return isProxyAuth ?
1818
connection.SendAsyncCore(request, cancellationToken) :
19-
connection.SendWithNtProxyAuthAsync(request, cancellationToken);
19+
pool.SendWithNtProxyAuthAsync(connection, request, cancellationToken);
2020
}
2121

2222
private static bool ProxySupportsConnectionAuth(HttpResponseMessage response)
@@ -37,9 +37,9 @@ private static bool ProxySupportsConnectionAuth(HttpResponseMessage response)
3737
return false;
3838
}
3939

40-
private static async Task<HttpResponseMessage> SendWithNtAuthAsync(HttpRequestMessage request, Uri authUri, ICredentials credentials, bool isProxyAuth, HttpConnection connection, CancellationToken cancellationToken)
40+
private static async Task<HttpResponseMessage> SendWithNtAuthAsync(HttpRequestMessage request, Uri authUri, ICredentials credentials, bool isProxyAuth, HttpConnection connection, HttpConnectionPool connectionPool, CancellationToken cancellationToken)
4141
{
42-
HttpResponseMessage response = await InnerSendAsync(request, isProxyAuth, connection, cancellationToken).ConfigureAwait(false);
42+
HttpResponseMessage response = await InnerSendAsync(request, isProxyAuth, connectionPool, connection, cancellationToken).ConfigureAwait(false);
4343
if (!isProxyAuth && connection.Kind == HttpConnectionKind.Proxy && !ProxySupportsConnectionAuth(response))
4444
{
4545
// Proxy didn't indicate that it supports connection-based auth, so we can't proceed.
@@ -55,51 +55,80 @@ private static async Task<HttpResponseMessage> SendWithNtAuthAsync(HttpRequestMe
5555
if (challenge.AuthenticationType == AuthenticationType.Negotiate ||
5656
challenge.AuthenticationType == AuthenticationType.Ntlm)
5757
{
58-
string challengeData = challenge.ChallengeData;
59-
60-
string spn = "HTTP/" + authUri.IdnHost;
61-
ChannelBinding channelBinding = connection.TransportContext?.GetChannelBinding(ChannelBindingKind.Endpoint);
62-
NTAuthentication authContext = new NTAuthentication(isServer:false, challenge.SchemeName, challenge.Credential, spn, ContextFlagsPal.Connection, channelBinding);
58+
bool isNewConnection = false;
6359
try
6460
{
65-
while (true)
61+
if (response.Headers.ConnectionClose.GetValueOrDefault())
6662
{
67-
string challengeResponse = authContext.GetOutgoingBlob(challengeData);
68-
if (challengeResponse == null)
63+
// Server is closing the connection and asking us to authenticate on a new connection.
64+
(connection, response) = await connectionPool.CreateConnectionAsync(request, cancellationToken).ConfigureAwait(false);
65+
if (response != null)
6966
{
70-
// Response indicated denial even after login, so stop processing and return current response.
71-
break;
67+
return response;
7268
}
7369

70+
connectionPool.IncrementConnectionCount();
71+
connection.Acquire();
72+
isNewConnection = true;
73+
}
74+
else
75+
{
7476
await connection.DrainResponseAsync(response).ConfigureAwait(false);
77+
}
7578

76-
SetRequestAuthenticationHeaderValue(request, new AuthenticationHeaderValue(challenge.SchemeName, challengeResponse), isProxyAuth);
79+
string challengeData = challenge.ChallengeData;
7780

78-
response = await InnerSendAsync(request, isProxyAuth, connection, cancellationToken).ConfigureAwait(false);
79-
if (authContext.IsCompleted || !TryGetRepeatedChallenge(response, challenge.SchemeName, isProxyAuth, out challengeData))
81+
string spn = "HTTP/" + authUri.IdnHost;
82+
ChannelBinding channelBinding = connection.TransportContext?.GetChannelBinding(ChannelBindingKind.Endpoint);
83+
NTAuthentication authContext = new NTAuthentication(isServer:false, challenge.SchemeName, challenge.Credential, spn, ContextFlagsPal.Connection, channelBinding);
84+
try
85+
{
86+
while (true)
8087
{
81-
break;
88+
string challengeResponse = authContext.GetOutgoingBlob(challengeData);
89+
if (challengeResponse == null)
90+
{
91+
// Response indicated denial even after login, so stop processing and return current response.
92+
break;
93+
}
94+
95+
SetRequestAuthenticationHeaderValue(request, new AuthenticationHeaderValue(challenge.SchemeName, challengeResponse), isProxyAuth);
96+
97+
response = await InnerSendAsync(request, isProxyAuth, connectionPool, connection, cancellationToken).ConfigureAwait(false);
98+
if (authContext.IsCompleted || !TryGetRepeatedChallenge(response, challenge.SchemeName, isProxyAuth, out challengeData))
99+
{
100+
break;
101+
}
102+
103+
await connection.DrainResponseAsync(response).ConfigureAwait(false);
82104
}
83105
}
106+
finally
107+
{
108+
authContext.CloseContext();
109+
}
84110
}
85111
finally
86112
{
87-
authContext.CloseContext();
113+
if (isNewConnection)
114+
{
115+
connection.Release();
116+
}
88117
}
89118
}
90119
}
91120

92121
return response;
93122
}
94123

95-
public static Task<HttpResponseMessage> SendWithNtProxyAuthAsync(HttpRequestMessage request, Uri proxyUri, ICredentials proxyCredentials, HttpConnection connection, CancellationToken cancellationToken)
124+
public static Task<HttpResponseMessage> SendWithNtProxyAuthAsync(HttpRequestMessage request, Uri proxyUri, ICredentials proxyCredentials, HttpConnection connection, HttpConnectionPool connectionPool, CancellationToken cancellationToken)
96125
{
97-
return SendWithNtAuthAsync(request, proxyUri, proxyCredentials, isProxyAuth:true, connection, cancellationToken);
126+
return SendWithNtAuthAsync(request, proxyUri, proxyCredentials, isProxyAuth:true, connection, connectionPool, cancellationToken);
98127
}
99128

100-
public static Task<HttpResponseMessage> SendWithNtConnectionAuthAsync(HttpRequestMessage request, ICredentials credentials, HttpConnection connection, CancellationToken cancellationToken)
129+
public static Task<HttpResponseMessage> SendWithNtConnectionAuthAsync(HttpRequestMessage request, ICredentials credentials, HttpConnection connection, HttpConnectionPool connectionPool, CancellationToken cancellationToken)
101130
{
102-
return SendWithNtAuthAsync(request, request.RequestUri, credentials, isProxyAuth:false, connection, cancellationToken);
131+
return SendWithNtAuthAsync(request, request.RequestUri, credentials, isProxyAuth:false, connection, connectionPool, cancellationToken);
103132
}
104133
}
105134
}

src/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnection.cs

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -697,26 +697,11 @@ public async Task<HttpResponseMessage> SendAsyncCore(HttpRequestMessage request,
697697
}
698698
}
699699

700-
public Task<HttpResponseMessage> SendWithNtProxyAuthAsync(HttpRequestMessage request, CancellationToken cancellationToken)
700+
public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
701701
{
702-
if (_pool.AnyProxyKind && _pool.ProxyCredentials != null)
703-
{
704-
return AuthenticationHelper.SendWithNtProxyAuthAsync(request, _pool.ProxyUri, _pool.ProxyCredentials, this, cancellationToken);
705-
}
706-
707702
return SendAsyncCore(request, cancellationToken);
708703
}
709704

710-
public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, bool doRequestAuth, CancellationToken cancellationToken)
711-
{
712-
if (doRequestAuth && _pool.Settings._credentials != null)
713-
{
714-
return AuthenticationHelper.SendWithNtConnectionAuthAsync(request, _pool.Settings._credentials, this, cancellationToken);
715-
}
716-
717-
return SendWithNtProxyAuthAsync(request, cancellationToken);
718-
}
719-
720705
private HttpContentWriteStream CreateRequestContentStream(HttpRequestMessage request)
721706
{
722707
bool requestTransferEncodingChunked = request.HasHeaders && request.Headers.TransferEncodingChunked == true;

src/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnectionPool.cs

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ public async Task<HttpResponseMessage> SendWithRetryAsync(HttpRequestMessage req
284284
connection.Acquire();
285285
try
286286
{
287-
return await connection.SendAsync(request, doRequestAuth, cancellationToken).ConfigureAwait(false);
287+
return await SendWithNtConnectionAuthAsync((HttpConnection)connection, request, doRequestAuth, cancellationToken).ConfigureAwait(false);
288288
}
289289
catch (HttpRequestException e) when (!isNewConnection && e.InnerException is IOException && connection.CanRetry)
290290
{
@@ -297,6 +297,26 @@ public async Task<HttpResponseMessage> SendWithRetryAsync(HttpRequestMessage req
297297
}
298298
}
299299

300+
private async Task<HttpResponseMessage> SendWithNtConnectionAuthAsync(HttpConnection connection, HttpRequestMessage request, bool doRequestAuth, CancellationToken cancellationToken)
301+
{
302+
if (doRequestAuth && Settings._credentials != null)
303+
{
304+
return await AuthenticationHelper.SendWithNtConnectionAuthAsync(request, Settings._credentials, connection, this, cancellationToken).ConfigureAwait(false);
305+
}
306+
307+
return await SendWithNtProxyAuthAsync(connection, request, cancellationToken).ConfigureAwait(false);
308+
}
309+
310+
public Task<HttpResponseMessage> SendWithNtProxyAuthAsync(HttpConnection connection, HttpRequestMessage request, CancellationToken cancellationToken)
311+
{
312+
if (AnyProxyKind && ProxyCredentials != null)
313+
{
314+
return AuthenticationHelper.SendWithNtProxyAuthAsync(request, ProxyUri, ProxyCredentials, connection, this, cancellationToken);
315+
}
316+
317+
return connection.SendAsync(request, cancellationToken);
318+
}
319+
300320
public Task<HttpResponseMessage> SendWithProxyAuthAsync(HttpRequestMessage request, bool doRequestAuth, CancellationToken cancellationToken)
301321
{
302322
if ((_kind == HttpConnectionKind.Proxy || _kind == HttpConnectionKind.ProxyConnect) &&
@@ -318,7 +338,7 @@ public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, bool doRe
318338
return SendWithProxyAuthAsync(request, doRequestAuth, cancellationToken);
319339
}
320340

321-
private async ValueTask<(HttpConnection, HttpResponseMessage)> CreateConnectionAsync(HttpRequestMessage request, CancellationToken cancellationToken)
341+
internal async ValueTask<(HttpConnection, HttpResponseMessage)> CreateConnectionAsync(HttpRequestMessage request, CancellationToken cancellationToken)
322342
{
323343
// If a non-infinite connect timeout has been set, create and use a new CancellationToken that'll be canceled
324344
// when either the original token is canceled or a connect timeout occurs.

0 commit comments

Comments
 (0)