Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ namespace System.Net.Http
{
internal partial class AuthenticationHelper
{
private static Task<HttpResponseMessage> InnerSendAsync(HttpRequestMessage request, bool isProxyAuth, HttpConnection connection, CancellationToken cancellationToken)
private static Task<HttpResponseMessage> InnerSendAsync(HttpRequestMessage request, bool isProxyAuth, HttpConnectionPool pool, HttpConnection connection, CancellationToken cancellationToken)
{
return isProxyAuth ?
connection.SendAsyncCore(request, cancellationToken) :
connection.SendWithNtProxyAuthAsync(request, cancellationToken);
pool.SendWithNtProxyAuthAsync(connection, request, cancellationToken);
}

private static bool ProxySupportsConnectionAuth(HttpResponseMessage response)
Expand All @@ -37,9 +37,9 @@ private static bool ProxySupportsConnectionAuth(HttpResponseMessage response)
return false;
}

private static async Task<HttpResponseMessage> SendWithNtAuthAsync(HttpRequestMessage request, Uri authUri, ICredentials credentials, bool isProxyAuth, HttpConnection connection, CancellationToken cancellationToken)
private static async Task<HttpResponseMessage> SendWithNtAuthAsync(HttpRequestMessage request, Uri authUri, ICredentials credentials, bool isProxyAuth, HttpConnection connection, HttpConnectionPool connectionPool, CancellationToken cancellationToken)
{
HttpResponseMessage response = await InnerSendAsync(request, isProxyAuth, connection, cancellationToken).ConfigureAwait(false);
HttpResponseMessage response = await InnerSendAsync(request, isProxyAuth, connectionPool, connection, cancellationToken).ConfigureAwait(false);
if (!isProxyAuth && connection.Kind == HttpConnectionKind.Proxy && !ProxySupportsConnectionAuth(response))
{
// Proxy didn't indicate that it supports connection-based auth, so we can't proceed.
Expand All @@ -55,51 +55,80 @@ private static async Task<HttpResponseMessage> SendWithNtAuthAsync(HttpRequestMe
if (challenge.AuthenticationType == AuthenticationType.Negotiate ||
challenge.AuthenticationType == AuthenticationType.Ntlm)
{
string challengeData = challenge.ChallengeData;

string spn = "HTTP/" + authUri.IdnHost;
ChannelBinding channelBinding = connection.TransportContext?.GetChannelBinding(ChannelBindingKind.Endpoint);
NTAuthentication authContext = new NTAuthentication(isServer:false, challenge.SchemeName, challenge.Credential, spn, ContextFlagsPal.Connection, channelBinding);
bool isNewConnection = false;
try
{
while (true)
if (response.Headers.ConnectionClose.GetValueOrDefault())
{
string challengeResponse = authContext.GetOutgoingBlob(challengeData);
if (challengeResponse == null)
// Server is closing the connection and asking us to authenticate on a new connection.
(connection, response) = await connectionPool.CreateConnectionAsync(request, cancellationToken).ConfigureAwait(false);
if (response != null)
{
// Response indicated denial even after login, so stop processing and return current response.
break;
return response;
}

connectionPool.IncrementConnectionCount();
connection.Acquire();
isNewConnection = true;
}
else
{
await connection.DrainResponseAsync(response).ConfigureAwait(false);
}

SetRequestAuthenticationHeaderValue(request, new AuthenticationHeaderValue(challenge.SchemeName, challengeResponse), isProxyAuth);
string challengeData = challenge.ChallengeData;

response = await InnerSendAsync(request, isProxyAuth, connection, cancellationToken).ConfigureAwait(false);
if (authContext.IsCompleted || !TryGetRepeatedChallenge(response, challenge.SchemeName, isProxyAuth, out challengeData))
string spn = "HTTP/" + authUri.IdnHost;
ChannelBinding channelBinding = connection.TransportContext?.GetChannelBinding(ChannelBindingKind.Endpoint);
NTAuthentication authContext = new NTAuthentication(isServer:false, challenge.SchemeName, challenge.Credential, spn, ContextFlagsPal.Connection, channelBinding);
try
{
while (true)
{
break;
string challengeResponse = authContext.GetOutgoingBlob(challengeData);
if (challengeResponse == null)
{
// Response indicated denial even after login, so stop processing and return current response.
break;
}

SetRequestAuthenticationHeaderValue(request, new AuthenticationHeaderValue(challenge.SchemeName, challengeResponse), isProxyAuth);

response = await InnerSendAsync(request, isProxyAuth, connectionPool, connection, cancellationToken).ConfigureAwait(false);
if (authContext.IsCompleted || !TryGetRepeatedChallenge(response, challenge.SchemeName, isProxyAuth, out challengeData))
{
break;
}

await connection.DrainResponseAsync(response).ConfigureAwait(false);
}
}
finally
{
authContext.CloseContext();
}
}
finally
{
authContext.CloseContext();
if (isNewConnection)
{
connection.Release();
}
}
}
}

return response;
}

public static Task<HttpResponseMessage> SendWithNtProxyAuthAsync(HttpRequestMessage request, Uri proxyUri, ICredentials proxyCredentials, HttpConnection connection, CancellationToken cancellationToken)
public static Task<HttpResponseMessage> SendWithNtProxyAuthAsync(HttpRequestMessage request, Uri proxyUri, ICredentials proxyCredentials, HttpConnection connection, HttpConnectionPool connectionPool, CancellationToken cancellationToken)
{
return SendWithNtAuthAsync(request, proxyUri, proxyCredentials, isProxyAuth:true, connection, cancellationToken);
return SendWithNtAuthAsync(request, proxyUri, proxyCredentials, isProxyAuth:true, connection, connectionPool, cancellationToken);
}

public static Task<HttpResponseMessage> SendWithNtConnectionAuthAsync(HttpRequestMessage request, ICredentials credentials, HttpConnection connection, CancellationToken cancellationToken)
public static Task<HttpResponseMessage> SendWithNtConnectionAuthAsync(HttpRequestMessage request, ICredentials credentials, HttpConnection connection, HttpConnectionPool connectionPool, CancellationToken cancellationToken)
{
return SendWithNtAuthAsync(request, request.RequestUri, credentials, isProxyAuth:false, connection, cancellationToken);
return SendWithNtAuthAsync(request, request.RequestUri, credentials, isProxyAuth:false, connection, connectionPool, cancellationToken);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -697,26 +697,11 @@ public async Task<HttpResponseMessage> SendAsyncCore(HttpRequestMessage request,
}
}

public Task<HttpResponseMessage> SendWithNtProxyAuthAsync(HttpRequestMessage request, CancellationToken cancellationToken)
public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
if (_pool.AnyProxyKind && _pool.ProxyCredentials != null)
{
return AuthenticationHelper.SendWithNtProxyAuthAsync(request, _pool.ProxyUri, _pool.ProxyCredentials, this, cancellationToken);
}

return SendAsyncCore(request, cancellationToken);
}

public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, bool doRequestAuth, CancellationToken cancellationToken)
{
if (doRequestAuth && _pool.Settings._credentials != null)
{
return AuthenticationHelper.SendWithNtConnectionAuthAsync(request, _pool.Settings._credentials, this, cancellationToken);
}

return SendWithNtProxyAuthAsync(request, cancellationToken);
}

private HttpContentWriteStream CreateRequestContentStream(HttpRequestMessage request)
{
bool requestTransferEncodingChunked = request.HasHeaders && request.Headers.TransferEncodingChunked == true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ public async Task<HttpResponseMessage> SendWithRetryAsync(HttpRequestMessage req
connection.Acquire();
try
{
return await connection.SendAsync(request, doRequestAuth, cancellationToken).ConfigureAwait(false);
return await SendWithNtConnectionAuthAsync((HttpConnection)connection, request, doRequestAuth, cancellationToken).ConfigureAwait(false);
}
catch (HttpRequestException e) when (!isNewConnection && e.InnerException is IOException && connection.CanRetry)
{
Expand All @@ -298,6 +298,26 @@ public async Task<HttpResponseMessage> SendWithRetryAsync(HttpRequestMessage req
}
}

private async Task<HttpResponseMessage> SendWithNtConnectionAuthAsync(HttpConnection connection, HttpRequestMessage request, bool doRequestAuth, CancellationToken cancellationToken)
{
if (doRequestAuth && Settings._credentials != null)
{
return await AuthenticationHelper.SendWithNtConnectionAuthAsync(request, Settings._credentials, connection, this, cancellationToken).ConfigureAwait(false);
}

return await SendWithNtProxyAuthAsync(connection, request, cancellationToken).ConfigureAwait(false);
}

public Task<HttpResponseMessage> SendWithNtProxyAuthAsync(HttpConnection connection, HttpRequestMessage request, CancellationToken cancellationToken)
{
if (AnyProxyKind && ProxyCredentials != null)
{
return AuthenticationHelper.SendWithNtProxyAuthAsync(request, ProxyUri, ProxyCredentials, connection, this, cancellationToken);
}

return connection.SendAsync(request, cancellationToken);
}

public Task<HttpResponseMessage> SendWithProxyAuthAsync(HttpRequestMessage request, bool doRequestAuth, CancellationToken cancellationToken)
{
if ((_kind == HttpConnectionKind.Proxy || _kind == HttpConnectionKind.ProxyConnect) &&
Expand All @@ -319,7 +339,7 @@ public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, bool doRe
return SendWithProxyAuthAsync(request, doRequestAuth, cancellationToken);
}

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