// 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;
{
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)
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.
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)
{
- // 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);
}
}
}
}
}
- 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;
}
}
- private void Acquire()
+ internal void Acquire()
{
Debug.Assert(_currentRequest == null);
Debug.Assert(!_inUse);
_inUse = true;
}
- private void Release()
+ internal void Release()
{
Debug.Assert(_inUse);
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);
+ }
}
catch (HttpRequestException e) when (!isNewConnection && e.AllowRetry)
{
}
}
+ 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) &&
}
}
- 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);
_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,