public Task <HttpResponseMessage> SendAsyncCore(HttpRequestMessage request, Uri proxyUri, 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 (_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) { _cleaningTimer.Change(_cleanPoolTimeout, _cleanPoolTimeout); _timerIsRunning = true; } } break; } pool.Dispose(); } return(pool.SendAsync(request, doRequestAuth, cancellationToken)); }
/// <summary>Initializes the pool.</summary> /// <param name="maxConnections">The maximum number of connections allowed to be associated with the pool at any given time.</param> public HttpConnectionPool(HttpConnectionPools pools, HttpConnectionKey key, int maxConnections = int.MaxValue) // int.MaxValue treated as infinite { _pools = pools; _key = key; _maxConnections = maxConnections; // Precalculate ASCII bytes for header name // We don't do this for proxy connections because the actual host header varies on a proxy connection. if (!pools.UsingProxy) { // CONSIDER: Cache more than just host name -- port, header name, etc // Note the IDN hostname should always be ASCII, since it's already been IDNA encoded. _idnHostAsciiBytes = Encoding.ASCII.GetBytes(key.Host); Debug.Assert(Encoding.ASCII.GetString(_idnHostAsciiBytes) == key.Host); } else { // Proxy connections should never use SSL Debug.Assert(!key.IsSecure); } if (key.IsSecure) { // Precalculate cached SSL options to use for all connections. _sslOptions = _pools.Settings._sslOptions?.ShallowClone() ?? new SslClientAuthenticationOptions(); _sslOptions.ApplicationProtocols = null; // explicitly ignore any ApplicationProtocols set _sslOptions.TargetHost = key.SslHostName; // always use the key's name rather than whatever was specified } }
protected internal override Task <HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { Uri uri = request.RequestUri; HttpConnectionKey key = new HttpConnectionKey(uri.IdnHost, uri.Port, GetSslHostName(request)); HttpConnectionPool pool = _connectionPools.GetOrAddPool(key); return(pool.SendAsync(request, cancellationToken)); }
private async Task <HttpResponseMessage> SendAsyncWithNewConnection( HttpConnectionKey key, HttpConnectionPool pool, HttpRequestMessage request, CancellationToken cancellationToken) { var connection = await CreateConnection(request, key, pool).ConfigureAwait(false); return(await connection.SendAsync(request, cancellationToken).ConfigureAwait(false)); }
private Task <HttpResponseMessage> GetConnectionAndSendAsync(HttpRequestMessage request, Uri proxyUri, CancellationToken cancellationToken) { Debug.Assert(proxyUri.Scheme == UriScheme.Http); var key = new HttpConnectionKey(proxyUri.IdnHost, proxyUri.Port, null); HttpConnectionPool pool = _connectionPools.GetOrAddPool(key); return(pool.SendAsync(request, cancellationToken)); }
private async ValueTask <HttpConnection> CreateConnection( HttpRequestMessage request, HttpConnectionKey key, HttpConnectionPool pool, CancellationToken cancellationToken) { Uri uri = request.RequestUri; Stream stream = await ConnectHelper.ConnectAsync(uri.IdnHost, uri.Port, cancellationToken).ConfigureAwait(false); TransportContext transportContext = null; if (uri.Scheme == UriScheme.Https) { // 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, cancellationToken).ConfigureAwait(false); stream = sslStream; transportContext = sslStream.TransportContext; } return(new HttpConnection(pool, key, uri.IdnHost, stream, transportContext, false)); }
private ValueTask <HttpConnection> GetOrCreateConnection(HttpRequestMessage request, Uri proxyUri, CancellationToken cancellationToken) { var key = new HttpConnectionKey(proxyUri); HttpConnectionPool pool = _connectionPools.GetOrAddPool(key); return(pool.GetConnectionAsync(async(state, ct) => { Stream stream = await ConnectHelper.ConnectAsync(state.proxyUri.IdnHost, state.proxyUri.Port, ct).ConfigureAwait(false); return new HttpConnection(state.pool, state.key, null, stream, null, true); }, (pool: pool, key: key, request: request, proxyUri: proxyUri), cancellationToken)); }
protected internal override Task <HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { var key = new HttpConnectionKey(request.RequestUri); HttpConnectionPool pool = _connectionPools.GetOrAddPool(key); ValueTask <HttpConnection> connectionTask = pool.GetConnectionAsync( state => state.handler.CreateConnection(state.request, state.key, state.pool), (handler: this, request: request, key: key, pool: pool)); return(connectionTask.IsCompletedSuccessfully ? connectionTask.Result.SendAsync(request, cancellationToken) : SendAsyncWithAwaitedConnection(connectionTask, request, cancellationToken)); }
protected internal override Task <HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { HttpConnection connection = null; // Try to get a connection from the connection pool HttpConnectionKey key = new HttpConnectionKey(request.RequestUri); if (_connectionPoolTable.TryGetValue(key, out HttpConnectionPool pool)) { connection = pool.GetConnection(); } return(connection != null? connection.SendAsync(request, cancellationToken) : SendAsyncWithNewConnection(key, pool, request, cancellationToken)); }
public Task <HttpResponseMessage> SendAsyncCore(HttpRequestMessage request, Uri proxyUri, bool doRequestAuth, bool isProxyConnect, CancellationToken cancellationToken) { 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); Debug.Assert((_cleaningTimer == null) == _avoidStoringConnections); 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)); }
/// <summary>Initializes the pool.</summary> /// <param name="maxConnections">The maximum number of connections allowed to be associated with the pool at any given time.</param> public HttpConnectionPool(HttpConnectionPools pools, HttpConnectionKey key, int maxConnections = int.MaxValue) // int.MaxValue treated as infinite { _pools = pools; _key = key; _maxConnections = maxConnections; // Precalculate ASCII bytes for header name // We don't do this for proxy connections because the actual host header varies on a proxy connection. if (!pools.UsingProxy) { // CONSIDER: Cache more than just host name -- port, header name, etc _idnHostAsciiBytes = Encoding.ASCII.GetBytes(key.Host); } else { // Proxy connections should never use SSL Debug.Assert(!key.IsSecure); } }
protected internal override async Task <HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { HttpConnection connection = null; // Try to get a connection from the connection pool HttpConnectionKey key = new HttpConnectionKey(request.RequestUri); if (_connectionPoolTable.TryGetValue(key, out HttpConnectionPool pool)) { connection = pool.GetConnection(); } if (connection == null) { // No connection available in pool. Create a new one. connection = await CreateConnection(request, key, pool).ConfigureAwait(false); } return(await connection.SendAsync(request, cancellationToken).ConfigureAwait(false)); }
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); }
private async ValueTask <HttpConnection> GetOrCreateConnection(HttpRequestMessage request, Uri proxyUri) { HttpConnectionKey key = new HttpConnectionKey(proxyUri); HttpConnectionPool pool; if (_connectionPoolTable.TryGetValue(key, out pool)) { HttpConnection poolConnection = pool.GetConnection(); if (poolConnection != null) { return(poolConnection); } } Stream stream = await ConnectHelper.ConnectAsync(proxyUri.Host, proxyUri.Port).ConfigureAwait(false); if (pool == null) { pool = _connectionPoolTable.GetOrAdd(key, _ => new HttpConnectionPool()); } return(new HttpConnection(pool, key, stream, null, true)); }
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 async ValueTask <Stream> ConnectAsync(HttpConnectionKey key, CancellationToken cancellationToken) { string host = key.Host; int port = key.Port; try { // Rather than creating a new Socket and calling ConnectAsync on it, we use the static // Socket.ConnectAsync with a SocketAsyncEventArgs, as we can then use Socket.CancelConnectAsync // to cancel it if needed. using (var saea = new BuilderAndCancellationTokenSocketAsyncEventArgs(cancellationToken)) { // Configure which server to which to connect. saea.RemoteEndPoint = IPAddress.TryParse(host, out IPAddress address) ? (EndPoint) new IPEndPoint(address, port) : new DnsEndPoint(host, port); // Hook up a callback that'll complete the Task when the operation completes. saea.Completed += (s, e) => { var csaea = (BuilderAndCancellationTokenSocketAsyncEventArgs)e; switch (e.SocketError) { case SocketError.Success: csaea.Builder.SetResult(); break; case SocketError.OperationAborted: case SocketError.ConnectionAborted: if (cancellationToken.IsCancellationRequested) { csaea.Builder.SetException(new OperationCanceledException(csaea.CancellationToken)); break; } goto default; default: csaea.Builder.SetException(new SocketException((int)e.SocketError)); break; } }; // Initiate the connection. if (Socket.ConnectAsync(SocketType.Stream, ProtocolType.Tcp, saea)) { // Connect completing asynchronously. Enable it to be canceled and wait for it. using (cancellationToken.Register(s => Socket.CancelConnectAsync((SocketAsyncEventArgs)s), saea)) { await saea.Builder.Task.ConfigureAwait(false); } } else if (saea.SocketError != SocketError.Success) { // Connect completed synchronously but unsuccessfully. throw new SocketException((int)saea.SocketError); } Debug.Assert(saea.SocketError == SocketError.Success, $"Expected Success, got {saea.SocketError}."); Debug.Assert(saea.ConnectSocket != null, "Expected non-null socket"); Debug.Assert(saea.ConnectSocket.Connected, "Expected socket to be connected"); // Configure the socket and return a stream for it. Socket socket = saea.ConnectSocket; socket.NoDelay = true; return(new NetworkStream(socket, ownsSocket: true)); } } catch (SocketException se) { throw new HttpRequestException(se.Message, se); } }
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); }