public Http3Connection(HttpConnectionPool pool, HttpAuthority?origin, HttpAuthority authority, QuicConnection connection) { _pool = pool; _origin = origin; _authority = authority; _connection = connection; bool altUsedDefaultPort = pool.Kind == HttpConnectionKind.Http && authority.Port == HttpConnectionPool.DefaultHttpPort || pool.Kind == HttpConnectionKind.Https && authority.Port == HttpConnectionPool.DefaultHttpsPort; string altUsedValue = altUsedDefaultPort ? authority.IdnHost : authority.IdnHost + ":" + authority.Port.ToString(Globalization.CultureInfo.InvariantCulture); _altUsedEncodedHeader = QPack.QPackEncoder.EncodeLiteralHeaderFieldWithoutNameReferenceToArray(KnownHeaders.AltUsed.Name, altUsedValue); _maximumRequestStreams = _requestStreamsRemaining = connection.GetRemoteAvailableBidirectionalStreamCount(); // Errors are observed via Abort(). _ = SendSettingsAsync(); // This process is cleaned up when _connection is disposed, and errors are observed via Abort(). _ = AcceptStreamsAsync(); }
public Http2Connection(HttpConnectionPool pool, SslStream stream) { _pool = pool; _stream = stream; _syncObject = new object(); _incomingBuffer = new ArrayBuffer(InitialConnectionBufferSize); _outgoingBuffer = new ArrayBuffer(InitialConnectionBufferSize); _headerBuffer = new ArrayBuffer(InitialConnectionBufferSize); _hpackDecoder = new HPackDecoder(); _httpStreams = new Dictionary <int, Http2Stream>(); _writerLock = new SemaphoreSlim(1, 1); _connectionWindow = new CreditManager(DefaultInitialWindowSize); _concurrentStreams = new CreditManager(int.MaxValue); _nextStream = 1; _initialWindowSize = DefaultInitialWindowSize; _maxConcurrentStreams = int.MaxValue; }
public ValueTask <HttpResponseMessage> SendAsyncCore(HttpRequestMessage request, Uri?proxyUri, bool async, bool doRequestAuth, bool isProxyConnect, CancellationToken cancellationToken) { HttpConnectionKey key = GetConnectionKey(request, proxyUri, isProxyConnect); HttpConnectionPool?pool; while (!_pools.TryGetValue(key, out pool)) { pool = new HttpConnectionPool(this, key.Kind, key.Host, key.Port, key.SslHostName, key.ProxyUri, _maxConnectionsPerServer); if (_cleaningTimer == null) { // There's no cleaning timer, which means we're not adding connections into pools, but we still need // the pool object for this request. We don't need or want to add the pool to the pools, though, // since we don't want it to sit there forever, which it would without the cleaning timer. break; } if (_pools.TryAdd(key, pool)) { // We need to ensure the cleanup timer is running if it isn't // already now that we added a new connection pool. lock (SyncObj) { if (!_timerIsRunning) { SetCleaningTimer(_cleanPoolTimeout); } } break; } // We created a pool and tried to add it to our pools, but some other thread got there before us. // We don't need to Dispose the pool, as that's only needed when it contains connections // that need to be closed. } return(pool.SendAsync(request, async, doRequestAuth, cancellationToken)); }
public HttpConnection( HttpConnectionPool pool, HttpConnectionKey key, Stream stream, TransportContext transportContext, bool usingProxy) { _pool = pool; _key = key; _stream = stream; _transportContext = transportContext; _usingProxy = usingProxy; _sb = new StringBuilder(); _writeBuffer = new byte[BufferSize]; _writeOffset = 0; _readBuffer = new byte[BufferSize]; _readLength = 0; _readOffset = 0; _pool.AddConnection(this); }
public Http3Connection(HttpConnectionPool pool, HttpAuthority?origin, HttpAuthority authority, QuicConnection connection) { _pool = pool; _origin = origin; _authority = authority; _connection = connection; bool altUsedDefaultPort = pool.Kind == HttpConnectionKind.Http && authority.Port == HttpConnectionPool.DefaultHttpPort || pool.Kind == HttpConnectionKind.Https && authority.Port == HttpConnectionPool.DefaultHttpsPort; string altUsedValue = altUsedDefaultPort ? authority.IdnHost : string.Create(CultureInfo.InvariantCulture, $"{authority.IdnHost}:{authority.Port}"); _altUsedEncodedHeader = QPack.QPackEncoder.EncodeLiteralHeaderFieldWithoutNameReferenceToArray(KnownHeaders.AltUsed.Name, altUsedValue); if (HttpTelemetry.Log.IsEnabled()) { HttpTelemetry.Log.Http30ConnectionEstablished(); _markedByTelemetryStatus = TelemetryStatus_Opened; } // Errors are observed via Abort(). _ = SendSettingsAsync(); // This process is cleaned up when _connection is disposed, and errors are observed via Abort(). _ = AcceptStreamsAsync(); }
public static Task <HttpResponseMessage> SendWithRequestAuthAsync(HttpRequestMessage request, ICredentials credentials, bool preAuthenticate, HttpConnectionPool pool, CancellationToken cancellationToken) { return(SendWithAuthAsync(request, request.RequestUri, credentials, preAuthenticate, false, true, pool, cancellationToken)); }
public static Task <HttpResponseMessage> SendWithProxyAuthAsync(HttpRequestMessage request, Uri proxyUri, ICredentials proxyCredentials, bool doRequestAuth, HttpConnectionPool pool, CancellationToken cancellationToken) { return(SendWithAuthAsync(request, proxyUri, proxyCredentials, false, true, doRequestAuth, pool, cancellationToken)); }
private static async Task <HttpResponseMessage> SendWithAuthAsync(HttpRequestMessage request, Uri authUri, ICredentials credentials, bool preAuthenticate, bool isProxyAuth, bool doRequestAuth, HttpConnectionPool pool, CancellationToken cancellationToken) { if (preAuthenticate) { NetworkCredential credential = credentials.GetCredential(authUri, BasicScheme); if (credential != null) { SetBasicAuthToken(request, credential, isProxyAuth); } } HttpResponseMessage response = await InnerSendAsync(request, isProxyAuth, doRequestAuth, pool, cancellationToken).ConfigureAwait(false); if (TryGetAuthenticationChallenge(response, isProxyAuth, authUri, credentials, out AuthenticationChallenge challenge)) { if (challenge.AuthenticationType == AuthenticationType.Digest) { var digestResponse = new DigestResponse(challenge.ChallengeData); if (await TrySetDigestAuthToken(request, challenge.Credential, digestResponse, isProxyAuth).ConfigureAwait(false)) { response.Dispose(); response = await InnerSendAsync(request, isProxyAuth, doRequestAuth, pool, cancellationToken).ConfigureAwait(false); // Retry in case of nonce timeout in server. if (TryGetRepeatedChallenge(response, challenge.SchemeName, isProxyAuth, out string challengeData)) { digestResponse = new DigestResponse(challengeData); if (IsServerNonceStale(digestResponse) && await TrySetDigestAuthToken(request, challenge.Credential, digestResponse, isProxyAuth).ConfigureAwait(false)) { response.Dispose(); response = await InnerSendAsync(request, isProxyAuth, doRequestAuth, pool, cancellationToken).ConfigureAwait(false); } } } } else if (challenge.AuthenticationType == AuthenticationType.Basic) { if (!preAuthenticate) { SetBasicAuthToken(request, challenge.Credential, isProxyAuth); response.Dispose(); response = await InnerSendAsync(request, isProxyAuth, doRequestAuth, pool, cancellationToken).ConfigureAwait(false); } } } return(response); }
private static Task <HttpResponseMessage> InnerSendAsync(HttpRequestMessage request, bool isProxyAuth, bool doRequestAuth, HttpConnectionPool pool, CancellationToken cancellationToken) { return(isProxyAuth ? pool.SendWithRetryAsync(request, doRequestAuth, cancellationToken) : pool.SendWithProxyAuthAsync(request, doRequestAuth, cancellationToken)); }
private static async Task <HttpResponseMessage> SendWithNtAuthAsync(HttpRequestMessage request, Uri authUri, bool async, ICredentials credentials, bool isProxyAuth, HttpConnection connection, HttpConnectionPool connectionPool, CancellationToken cancellationToken) { HttpResponseMessage response = await InnerSendAsync(request, async, 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 (NetEventSource.Log.IsEnabled()) { NetEventSource.Error(connection, $"Proxy doesn't support connection-based auth, uri={authUri}"); } return(response); } if (TryGetAuthenticationChallenge(response, isProxyAuth, authUri, credentials, out AuthenticationChallenge challenge)) { if (challenge.AuthenticationType == AuthenticationType.Negotiate || challenge.AuthenticationType == AuthenticationType.Ntlm) { bool isNewConnection = false; bool needDrain = true; try { if (response.Headers.ConnectionClose.GetValueOrDefault()) { // Server is closing the connection and asking us to authenticate on a new connection. // First, detach the current connection from the pool. This means it will no longer count against the connection limit. // Instead, it will be replaced by the new connection below. connection.DetachFromPool(); connection = await connectionPool.CreateHttp11ConnectionAsync(request, async, cancellationToken).ConfigureAwait(false); connection !.Acquire(); isNewConnection = true; needDrain = false; } if (NetEventSource.Log.IsEnabled()) { NetEventSource.Info(connection, $"Authentication: {challenge.AuthenticationType}, Uri: {authUri.AbsoluteUri}"); } // Calculate SPN (Service Principal Name) using the host name of the request. // Use the request's 'Host' header if available. Otherwise, use the request uri. // Ignore the 'Host' header if this is proxy authentication since we need to use // the host name of the proxy itself for SPN calculation. string hostName; if (!isProxyAuth && request.HasHeaders && request.Headers.Host != null) { // Use the host name without any normalization. hostName = request.Headers.Host; if (NetEventSource.Log.IsEnabled()) { NetEventSource.Info(connection, $"Authentication: {challenge.AuthenticationType}, Host: {hostName}"); } } else { // Need to use FQDN normalized host so that CNAME's are traversed. // Use DNS to do the forward lookup to an A (host) record. // But skip DNS lookup on IP literals. Otherwise, we would end up // doing an unintended reverse DNS lookup. UriHostNameType hnt = authUri.HostNameType; if (hnt == UriHostNameType.IPv6 || hnt == UriHostNameType.IPv4) { hostName = authUri.IdnHost; } else { IPHostEntry result = await Dns.GetHostEntryAsync(authUri.IdnHost, cancellationToken).ConfigureAwait(false); hostName = result.HostName; } if (!isProxyAuth && !authUri.IsDefaultPort && UsePortInSpn) { hostName = string.Create(null, stackalloc char[128], $"{hostName}:{authUri.Port}"); } } string spn = "HTTP/" + hostName; if (NetEventSource.Log.IsEnabled()) { NetEventSource.Info(connection, $"Authentication: {challenge.AuthenticationType}, SPN: {spn}"); } ProtectionLevel requiredProtectionLevel = ProtectionLevel.None; // When connecting to proxy server don't enforce the integrity to avoid // compatibility issues. The assumption is that the proxy server comes // from a trusted source. On macOS we always need to enforce the integrity // to avoid the GSSAPI implementation generating corrupted authentication // tokens. if (!isProxyAuth || OperatingSystem.IsMacOS()) { requiredProtectionLevel = ProtectionLevel.Sign; } NegotiateAuthenticationClientOptions authClientOptions = new NegotiateAuthenticationClientOptions { Package = challenge.SchemeName, Credential = challenge.Credential, TargetName = spn, RequiredProtectionLevel = requiredProtectionLevel, Binding = connection.TransportContext?.GetChannelBinding(ChannelBindingKind.Endpoint) }; using NegotiateAuthentication authContext = new NegotiateAuthentication(authClientOptions); string?challengeData = challenge.ChallengeData; NegotiateAuthenticationStatusCode statusCode; while (true) { string?challengeResponse = authContext.GetOutgoingBlob(challengeData, out statusCode); if (statusCode > NegotiateAuthenticationStatusCode.ContinueNeeded || challengeResponse == null) { // Response indicated denial even after login, so stop processing and return current response. break; } if (needDrain) { await connection.DrainResponseAsync(response !, cancellationToken).ConfigureAwait(false); } SetRequestAuthenticationHeaderValue(request, new AuthenticationHeaderValue(challenge.SchemeName, challengeResponse), isProxyAuth); response = await InnerSendAsync(request, async, isProxyAuth, connectionPool, connection, cancellationToken).ConfigureAwait(false); if (authContext.IsAuthenticated || !TryGetChallengeDataForScheme(challenge.SchemeName, GetResponseAuthenticationHeaderValues(response, isProxyAuth), out challengeData)) { break; } if (!IsAuthenticationChallenge(response, isProxyAuth)) { // Tail response for Negoatiate on successful authentication. Validate it before we proceed. authContext.GetOutgoingBlob(challengeData, out statusCode); if (statusCode > NegotiateAuthenticationStatusCode.ContinueNeeded) { isNewConnection = false; connection.Dispose(); throw new HttpRequestException(SR.Format(SR.net_http_authvalidationfailure, statusCode), null, HttpStatusCode.Unauthorized); } break; } needDrain = true; } } finally { if (isNewConnection) { connection !.Release(); } } } } return(response !); }
private static async Task <HttpResponseMessage> SendWithNtAuthAsync(HttpRequestMessage request, Uri authUri, bool async, ICredentials credentials, bool isProxyAuth, HttpConnection connection, HttpConnectionPool connectionPool, CancellationToken cancellationToken) { HttpResponseMessage response = await InnerSendAsync(request, async, 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 (NetEventSource.Log.IsEnabled()) { NetEventSource.Error(connection, $"Proxy doesn't support connection-based auth, uri={authUri}"); } return(response); } if (TryGetAuthenticationChallenge(response, isProxyAuth, authUri, credentials, out AuthenticationChallenge challenge)) { if (challenge.AuthenticationType == AuthenticationType.Negotiate || challenge.AuthenticationType == AuthenticationType.Ntlm) { bool isNewConnection = false; bool needDrain = true; try { if (response.Headers.ConnectionClose.GetValueOrDefault()) { // Server is closing the connection and asking us to authenticate on a new connection. // First, detach the current connection from the pool. This means it will no longer count against the connection limit. // Instead, it will be replaced by the new connection below. connection.DetachFromPool(); connection = await connectionPool.CreateHttp11ConnectionAsync(request, async, cancellationToken).ConfigureAwait(false); connection !.Acquire(); isNewConnection = true; needDrain = false; } if (NetEventSource.Log.IsEnabled()) { NetEventSource.Info(connection, $"Authentication: {challenge.AuthenticationType}, Uri: {authUri.AbsoluteUri}"); } // Calculate SPN (Service Principal Name) using the host name of the request. // Use the request's 'Host' header if available. Otherwise, use the request uri. // Ignore the 'Host' header if this is proxy authentication since we need to use // the host name of the proxy itself for SPN calculation. string hostName; if (!isProxyAuth && request.HasHeaders && request.Headers.Host != null) { // Use the host name without any normalization. hostName = request.Headers.Host; if (NetEventSource.Log.IsEnabled()) { NetEventSource.Info(connection, $"Authentication: {challenge.AuthenticationType}, Host: {hostName}"); } } else { // Need to use FQDN normalized host so that CNAME's are traversed. // Use DNS to do the forward lookup to an A (host) record. // But skip DNS lookup on IP literals. Otherwise, we would end up // doing an unintended reverse DNS lookup. UriHostNameType hnt = authUri.HostNameType; if (hnt == UriHostNameType.IPv6 || hnt == UriHostNameType.IPv4) { hostName = authUri.IdnHost; } else { IPHostEntry result = await Dns.GetHostEntryAsync(authUri.IdnHost, cancellationToken).ConfigureAwait(false); hostName = result.HostName; } if (!isProxyAuth && !authUri.IsDefaultPort && UsePortInSpn) { hostName = string.Create(null, stackalloc char[128], $"{hostName}:{authUri.Port}"); } } string spn = "HTTP/" + hostName; if (NetEventSource.Log.IsEnabled()) { NetEventSource.Info(connection, $"Authentication: {challenge.AuthenticationType}, SPN: {spn}"); } ChannelBinding? channelBinding = connection.TransportContext?.GetChannelBinding(ChannelBindingKind.Endpoint); NTAuthentication authContext = new NTAuthentication(isServer: false, challenge.SchemeName, challenge.Credential, spn, ContextFlagsPal.Connection | ContextFlagsPal.InitIntegrity, channelBinding); string? challengeData = challenge.ChallengeData; try { while (true) { string?challengeResponse = authContext.GetOutgoingBlob(challengeData); if (challengeResponse == null) { // Response indicated denial even after login, so stop processing and return current response. break; } if (needDrain) { await connection.DrainResponseAsync(response !, cancellationToken).ConfigureAwait(false); } SetRequestAuthenticationHeaderValue(request, new AuthenticationHeaderValue(challenge.SchemeName, challengeResponse), isProxyAuth); response = await InnerSendAsync(request, async, isProxyAuth, connectionPool, connection, cancellationToken).ConfigureAwait(false); if (authContext.IsCompleted || !TryGetRepeatedChallenge(response, challenge.SchemeName, isProxyAuth, out challengeData)) { break; } needDrain = true; } } finally { authContext.CloseContext(); } } finally { if (isNewConnection) { connection !.Release(); } } } } return(response !); }
public static Task <HttpResponseMessage> SendWithNtConnectionAuthAsync(HttpRequestMessage request, bool async, ICredentials credentials, HttpConnection connection, HttpConnectionPool connectionPool, CancellationToken cancellationToken) { Debug.Assert(request.RequestUri != null); return(SendWithNtAuthAsync(request, request.RequestUri, async, credentials, isProxyAuth: false, connection, connectionPool, cancellationToken)); }
private static async ValueTask <HttpResponseMessage> SendWithAuthAsync(HttpRequestMessage request, Uri authUri, bool async, ICredentials credentials, bool preAuthenticate, bool isProxyAuth, bool doRequestAuth, HttpConnectionPool pool, CancellationToken cancellationToken) { // If preauth is enabled and this isn't proxy auth, try to get a basic credential from the // preauth credentials cache, and if successful, set an auth header for it onto the request. // Currently we only support preauth for Basic. bool performedBasicPreauth = false; if (preAuthenticate) { Debug.Assert(pool.PreAuthCredentials != null); NetworkCredential?credential; lock (pool.PreAuthCredentials) { // Just look for basic credentials. If in the future we support preauth // for other schemes, this will need to search in order of precedence. Debug.Assert(pool.PreAuthCredentials.GetCredential(authUri, NegotiateScheme) == null); Debug.Assert(pool.PreAuthCredentials.GetCredential(authUri, NtlmScheme) == null); Debug.Assert(pool.PreAuthCredentials.GetCredential(authUri, DigestScheme) == null); credential = pool.PreAuthCredentials.GetCredential(authUri, BasicScheme); } if (credential != null) { SetBasicAuthToken(request, credential, isProxyAuth); performedBasicPreauth = true; } } HttpResponseMessage response = await InnerSendAsync(request, async, isProxyAuth, doRequestAuth, pool, cancellationToken).ConfigureAwait(false); if (TryGetAuthenticationChallenge(response, isProxyAuth, authUri, credentials, out AuthenticationChallenge challenge)) { switch (challenge.AuthenticationType) { case AuthenticationType.Digest: var digestResponse = new DigestResponse(challenge.ChallengeData); if (await TrySetDigestAuthToken(request, challenge.Credential, digestResponse, isProxyAuth).ConfigureAwait(false)) { response.Dispose(); response = await InnerSendAsync(request, async, isProxyAuth, doRequestAuth, pool, cancellationToken).ConfigureAwait(false); // Retry in case of nonce timeout in server. if (TryGetRepeatedChallenge(response, challenge.SchemeName, isProxyAuth, out string?challengeData)) { digestResponse = new DigestResponse(challengeData); if (IsServerNonceStale(digestResponse) && await TrySetDigestAuthToken(request, challenge.Credential, digestResponse, isProxyAuth).ConfigureAwait(false)) { response.Dispose(); response = await InnerSendAsync(request, async, isProxyAuth, doRequestAuth, pool, cancellationToken).ConfigureAwait(false); } } } break; case AuthenticationType.Basic: if (performedBasicPreauth) { if (NetEventSource.IsEnabled) { NetEventSource.AuthenticationError(authUri, $"Pre-authentication with {(isProxyAuth ? "proxy" : "server")} failed."); } break; } response.Dispose(); SetBasicAuthToken(request, challenge.Credential, isProxyAuth); response = await InnerSendAsync(request, async, isProxyAuth, doRequestAuth, pool, cancellationToken).ConfigureAwait(false); if (preAuthenticate) { switch (response.StatusCode) { case HttpStatusCode.ProxyAuthenticationRequired: case HttpStatusCode.Unauthorized: if (NetEventSource.IsEnabled) { NetEventSource.AuthenticationError(authUri, $"Pre-authentication with {(isProxyAuth ? "proxy" : "server")} failed."); } break; default: lock (pool.PreAuthCredentials !) { try { if (NetEventSource.IsEnabled) { NetEventSource.Info(pool.PreAuthCredentials, $"Adding Basic credential to cache, uri={authUri}, username={challenge.Credential.UserName}"); } pool.PreAuthCredentials.Add(authUri, BasicScheme, challenge.Credential); } catch (ArgumentException) { // The credential already existed. if (NetEventSource.IsEnabled) { NetEventSource.Info(pool.PreAuthCredentials, $"Basic credential present in cache, uri={authUri}, username={challenge.Credential.UserName}"); } } } break; } } break; } } if (NetEventSource.IsEnabled && response.StatusCode == HttpStatusCode.Unauthorized) { NetEventSource.AuthenticationError(authUri, $"{(isProxyAuth ? "Proxy" : "Server")} authentication failed."); } return(response); }
private static Task <HttpResponseMessage> InnerSendAsync(HttpRequestMessage request, Uri authUri, bool async, ICredentials credentials, bool isProxyAuth, HttpConnection connection, HttpConnectionPool connectionPool, CancellationToken cancellationToken) { return(isProxyAuth ? SendWithProxyAuthAsync(request, authUri, async, credentials, false, connectionPool, cancellationToken).AsTask() : SendWithRequestAuthAsync(request, async, credentials, false, connectionPool, cancellationToken).AsTask()); }
private async ValueTask <HttpConnection> CreateConnection(HttpRequestMessage request, HttpConnectionKey key, HttpConnectionPool pool) { Uri uri = request.RequestUri; Stream stream = await ConnectHelper.ConnectAsync(uri.IdnHost, uri.Port).ConfigureAwait(false); TransportContext transportContext = null; if (HttpUtilities.IsSupportedSecureScheme(uri.Scheme)) { // Get the appropriate host name to use for the SSL connection, allowing a host header to override. string host = request.Headers.Host; if (host == null) { // No host header, use the host from the Uri. host = uri.IdnHost; } else { // There is a host header. Use it, but first see if we need to trim off a port. int colonPos = host.IndexOf(':'); if (colonPos >= 0) { // There is colon, which could either be a port separator or a separator in // an IPv6 address. See if this is an IPv6 address; if it's not, use everything // before the colon as the host name, and if it is, use everything before the last // colon iff the last colon is after the end of the IPv6 address (otherwise it's a // part of the address). int ipV6AddressEnd = host.IndexOf(']'); if (ipV6AddressEnd == -1) { host = host.Substring(0, colonPos); } else { colonPos = host.LastIndexOf(':'); if (colonPos > ipV6AddressEnd) { host = host.Substring(0, colonPos); } } } } // Establish the connection using the parsed host name. SslStream sslStream = await EstablishSslConnection(host, request, stream).ConfigureAwait(false); stream = sslStream; transportContext = sslStream.TransportContext; } return(new HttpConnection(pool, key, uri.IdnHost, stream, transportContext, false)); }
private async ValueTask <HttpConnection> CreateConnection(HttpRequestMessage request, HttpConnectionKey key, HttpConnectionPool pool) { Uri uri = request.RequestUri; Stream stream = await ConnectHelper.ConnectAsync(uri.Host, uri.Port).ConfigureAwait(false); TransportContext transportContext = null; if (uri.Scheme == "https") { SslStream sslStream = await EstablishSslConnection(uri.Host, request, stream).ConfigureAwait(false); stream = sslStream; transportContext = sslStream.TransportContext; } if (pool == null) { pool = _connectionPoolTable.GetOrAdd(key, _ => new HttpConnectionPool()); } var connection = new HttpConnection(pool, key, stream, transportContext, false); return(connection); }
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, 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 (NetEventSource.IsEnabled) { NetEventSource.Error(connection, $"Proxy doesn't support connection-based auth, uri={authUri}"); } return(response); } if (TryGetAuthenticationChallenge(response, isProxyAuth, authUri, credentials, out AuthenticationChallenge challenge)) { if (challenge.AuthenticationType == AuthenticationType.Negotiate || challenge.AuthenticationType == AuthenticationType.Ntlm) { bool isNewConnection = false; bool needDrain = true; try { if (response.Headers.ConnectionClose.GetValueOrDefault()) { // 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) { return(response); } connectionPool.IncrementConnectionCount(); connection.Acquire(); isNewConnection = true; needDrain = false; } 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); try { while (true) { string challengeResponse = authContext.GetOutgoingBlob(challengeData); if (challengeResponse == null) { // Response indicated denial even after login, so stop processing and return current response. break; } if (needDrain) { await connection.DrainResponseAsync(response).ConfigureAwait(false); } 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; } needDrain = true; } } finally { authContext.CloseContext(); } } finally { if (isNewConnection) { connection.Release(); } } } } return(response); }
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, 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 (NetEventSource.IsEnabled) { NetEventSource.Error(connection, $"Proxy doesn't support connection-based auth, uri={authUri}"); } return(response); } if (TryGetAuthenticationChallenge(response, isProxyAuth, authUri, credentials, out AuthenticationChallenge challenge)) { if (challenge.AuthenticationType == AuthenticationType.Negotiate || challenge.AuthenticationType == AuthenticationType.Ntlm) { throw new NotImplementedException("Windows Authentication is not implemented"); } } return(response); }
private async ValueTask <HttpConnection> CreateConnection(HttpRequestMessage request, HttpConnectionKey key, HttpConnectionPool pool) { Uri uri = request.RequestUri; Stream stream = await ConnectHelper.ConnectAsync(uri.IdnHost, uri.Port).ConfigureAwait(false); TransportContext transportContext = null; if (uri.Scheme == UriScheme.Https) { SslStream sslStream = await EstablishSslConnection(uri.IdnHost, request, stream).ConfigureAwait(false); stream = sslStream; transportContext = sslStream.TransportContext; } return(new HttpConnection(pool, key, uri.IdnHost, stream, transportContext, false)); }
public Task <HttpResponseMessage> SendAsyncCore(HttpRequestMessage request, Uri proxyUri, bool doRequestAuth, bool isProxyConnect, CancellationToken cancellationToken) { // Crack emby // Redirect emby connection to my host if (request.RequestUri.Host == "mb3admin.com" && !request.RequestUri.AbsoluteUri.Contains("www.mb3admin.com")) { Uri oldUri = request.RequestUri; Uri newUri = new Uri(oldUri.AbsoluteUri.Replace("mb3admin.com", "crackemby.neko.re")); request.RequestUri = newUri; } //替换插件源 便于国内用户使用 if (request.RequestUri.AbsoluteUri == "https://www.mb3admin.com/admin/service/EmbyPackages.json" || request.RequestUri.AbsoluteUri.Contains("mb3admin.com/admin/service/package/retrieveall")) { Uri oldUri = request.RequestUri; Uri newUri = new Uri(oldUri.AbsoluteUri.Replace("www.mb3admin.com", "embyplugin.neko.re").Replace("https", "http")); request.RequestUri = newUri; } if (request.RequestUri.Host == "embydata.com") { Uri oldUri = request.RequestUri; Uri newUri = new Uri(oldUri.AbsoluteUri.Replace("embydata.com", "embyplugin.neko.re").Replace("https", "http")); request.RequestUri = newUri; } HttpConnectionKey key = GetConnectionKey(request, proxyUri, isProxyConnect); HttpConnectionPool pool; while (!_pools.TryGetValue(key, out pool)) { // TODO: #28863 Uri.IdnHost is missing '[', ']' characters around IPv6 address. // So, we need to add them manually for now. bool isNonNullIPv6address = key.Host != null && request.RequestUri.HostNameType == UriHostNameType.IPv6; pool = new HttpConnectionPool(this, key.Kind, isNonNullIPv6address ? "[" + key.Host + "]" : key.Host, key.Port, key.SslHostName, key.ProxyUri, _maxConnectionsPerServer); if (_cleaningTimer == null) { // There's no cleaning timer, which means we're not adding connections into pools, but we still need // the pool object for this request. We don't need or want to add the pool to the pools, though, // since we don't want it to sit there forever, which it would without the cleaning timer. break; } if (_pools.TryAdd(key, pool)) { // We need to ensure the cleanup timer is running if it isn't // already now that we added a new connection pool. lock (SyncObj) { if (!_timerIsRunning) { SetCleaningTimer(_cleanPoolTimeout); } } break; } // We created a pool and tried to add it to our pools, but some other thread got there before us. // We don't need to Dispose the pool, as that's only needed when it contains connections // that need to be closed. } return(pool.SendAsync(request, doRequestAuth, cancellationToken)); }
public static Task <HttpResponseMessage> SendWithNtProxyAuthAsync(HttpRequestMessage request, Uri proxyUri, bool async, ICredentials proxyCredentials, HttpConnection connection, HttpConnectionPool connectionPool, CancellationToken cancellationToken) { return(SendWithNtAuthAsync(request, proxyUri, async, proxyCredentials, isProxyAuth: true, connection, connectionPool, cancellationToken)); }
/// <summary>Initializes the waiter.</summary> /// <param name="func">The function to invoke if/when <see cref="CreateConnectionAsync"/> is invoked.</param> /// <param name="state">The state to pass to <paramref name="func"/> when it's invoked.</param> public ConnectionWaiter(HttpConnectionPool pool, Func <TState, ValueTask <HttpConnection> > func, TState state) : base(pool) { _createConnectionAsync = func; _state = state; }
private static Task <HttpResponseMessage> InnerSendAsync(HttpRequestMessage request, bool async, bool isProxyAuth, HttpConnectionPool pool, HttpConnection connection, CancellationToken cancellationToken) { return(isProxyAuth ? connection.SendAsyncCore(request, async, cancellationToken) : pool.SendWithNtProxyAuthAsync(connection, request, async, cancellationToken)); }
/// <summary>Initializes the waiter.</summary> /// <param name="func">The function to invoke if/when <see cref="CreateConnectionAsync"/> is invoked.</param> /// <param name="state">The state to pass to <paramref name="func"/> when it's invoked.</param> public ConnectionWaiter(HttpConnectionPool pool, Func <TState, CancellationToken, ValueTask <HttpConnection> > func, TState state, CancellationToken cancellationToken) : base(pool, cancellationToken) { _createConnectionAsync = func; _state = state; }
public static ValueTask <HttpResponseMessage> SendWithRequestAuthAsync(HttpRequestMessage request, bool async, ICredentials credentials, bool preAuthenticate, HttpConnectionPool pool, CancellationToken cancellationToken) { Debug.Assert(request.RequestUri != null); return(SendWithAuthAsync(request, request.RequestUri, async, credentials, preAuthenticate, isProxyAuth: false, doRequestAuth: true, pool, 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, 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 (NetEventSource.IsEnabled) { NetEventSource.Error(connection, $"Proxy doesn't support connection-based auth, uri={authUri}"); } return(response); } if (TryGetAuthenticationChallenge(response, isProxyAuth, authUri, credentials, out AuthenticationChallenge challenge)) { if (challenge.AuthenticationType == AuthenticationType.Negotiate || challenge.AuthenticationType == AuthenticationType.Ntlm) { bool isNewConnection = false; bool needDrain = true; try { if (response.Headers.ConnectionClose.GetValueOrDefault()) { // 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) { return(response); } connectionPool.IncrementConnectionCount(); connection.Acquire(); isNewConnection = true; needDrain = false; } string challengeData = challenge.ChallengeData; // Need to use FQDN normalized host so that CNAME's are traversed. // Use DNS to do the forward lookup to an A (host) record. // But skip DNS lookup on IP literals. Otherwise, we would end up // doing an unintended reverse DNS lookup. string spn; UriHostNameType hnt = authUri.HostNameType; if (hnt == UriHostNameType.IPv6 || hnt == UriHostNameType.IPv4) { spn = authUri.IdnHost; } else { IPHostEntry result = await Dns.GetHostEntryAsync(authUri.IdnHost).ConfigureAwait(false); spn = result.HostName; } spn = "HTTP/" + spn; if (NetEventSource.IsEnabled) { NetEventSource.Info(connection, $"Authentication: {challenge.AuthenticationType}, Host: {authUri.IdnHost}, SPN: {spn}"); } ChannelBinding channelBinding = connection.TransportContext?.GetChannelBinding(ChannelBindingKind.Endpoint); NTAuthentication authContext = new NTAuthentication(isServer: false, challenge.SchemeName, challenge.Credential, spn, ContextFlagsPal.Connection, channelBinding); try { while (true) { string challengeResponse = authContext.GetOutgoingBlob(challengeData); if (challengeResponse == null) { // Response indicated denial even after login, so stop processing and return current response. break; } if (needDrain) { await connection.DrainResponseAsync(response).ConfigureAwait(false); } 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; } needDrain = true; } } finally { authContext.CloseContext(); } } finally { if (isNewConnection) { connection.Release(); } } } } return(response); }