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 @@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.

using System.Collections.Generic;
using System.Diagnostics;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
Expand All @@ -12,11 +13,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 +38,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 +56,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.CreateHttp11ConnectionAsync(request, cancellationToken).ConfigureAwait(false);
if (response != null)
{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Assert that connection is null? If the logic in CreateHttp11ConnectionAsync changes such that both connection and response could be non-null, then the connection pool's count tracking will end up being incorrect.

// 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 @@ -673,7 +673,7 @@ private async Task SendFramesAsync(Memory<byte> frame)

// Note that this is safe to be called concurrently by multiple threads.

public sealed override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, bool doRequestAuth, CancellationToken cancellationToken)
public sealed override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
Http2Stream http2Stream = new Http2Stream(this);
try
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -682,39 +682,11 @@ public async Task<HttpResponseMessage> SendAsyncCore(HttpRequestMessage request,
}
}

public Task<HttpResponseMessage> SendWithNtProxyAuthAsync(HttpRequestMessage request, CancellationToken cancellationToken)
public sealed override 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);
}

private Task<HttpResponseMessage> SendAsyncInternal(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);
}

public sealed override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, bool doRequestAuth, CancellationToken cancellationToken)
{
Acquire();
try
{
return await SendAsyncInternal(request, doRequestAuth, cancellationToken).ConfigureAwait(false);
}
finally
{
Release();
}
}

private HttpContentWriteStream CreateRequestContentStream(HttpRequestMessage request)
{
bool requestTransferEncodingChunked = request.HasHeaders && request.Headers.TransferEncodingChunked == true;
Expand Down Expand Up @@ -1470,15 +1442,15 @@ private async Task CopyToExactLengthAsync(Stream destination, ulong length, Canc
}
}

private void Acquire()
internal void Acquire()
{
Debug.Assert(_currentRequest == null);
Debug.Assert(!_inUse);

_inUse = true;
}

private void Release()
internal void Release()
{
Debug.Assert(_inUse);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ namespace System.Net.Http
{
internal abstract class HttpConnectionBase
{
public abstract Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, bool doRequestAuth, CancellationToken cancellationToken);
public abstract Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -464,7 +464,14 @@ public async Task<HttpResponseMessage> SendWithRetryAsync(HttpRequestMessage req

try
{
return await connection.SendAsync(request, doRequestAuth, cancellationToken).ConfigureAwait(false);
if (connection is HttpConnection)
{
return await SendWithNtConnectionAuthAsync((HttpConnection)connection, request, doRequestAuth, cancellationToken).ConfigureAwait(false);
}
else
{
return await connection.SendAsync(request, cancellationToken).ConfigureAwait(false);
}
Copy link
Member

@stephentoub stephentoub Sep 24, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: FWIW, code gen would end up being a bit better if this were instead written to have a single await instead of two, e.g.

Task<HttpResponseMessage> resp = connection is HttpConnection httpConnection ?
    SendWithNtConnectionAuthAsync(httpConnection, request, doRequestAuth, cancellationToken) :
    connection.SendAsync(request, cancellationToken);
return await resp.ConfigureAwait(false);

}
catch (HttpRequestException e) when (!isNewConnection && e.AllowRetry)
{
Expand All @@ -478,6 +485,35 @@ public async Task<HttpResponseMessage> SendWithRetryAsync(HttpRequestMessage req
}
}

public async Task<HttpResponseMessage> SendWithNtConnectionAuthAsync(HttpConnection connection, HttpRequestMessage request, bool doRequestAuth, CancellationToken cancellationToken)
{
connection.Acquire();
try
{
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);
}
finally
{
connection.Release();
}
}

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 Down Expand Up @@ -556,7 +592,7 @@ public Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, bool doRe
}
}

private async ValueTask<(HttpConnection, HttpResponseMessage)> CreateHttp11ConnectionAsync(HttpRequestMessage request, CancellationToken cancellationToken)
internal async ValueTask<(HttpConnection, HttpResponseMessage)> CreateHttp11ConnectionAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
(Socket socket, Stream stream, TransportContext transportContext, HttpResponseMessage failureResponse) =
await ConnectAsync(request, false, cancellationToken).ConfigureAwait(false);
Expand Down Expand Up @@ -706,6 +742,15 @@ private void IncrementConnectionCountNoLock()
_associatedConnectionCount++;
}

internal void IncrementConnectionCount()
{
lock (SyncObj)
{
IncrementConnectionCountNoLock();
}
}


/// <summary>
/// Decrements the number of connections associated with the pool.
/// If there are waiters on the pool due to having reached the maximum,
Expand Down