Пример #1
0
        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));
        }
Пример #2
0
        /// <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
            }
        }
Пример #3
0
        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));
        }
Пример #4
0
        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));
        }
Пример #6
0
        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));
        }
Пример #7
0
        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));
        }
Пример #8
0
        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));
        }
Пример #9
0
        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));
        }
Пример #11
0
        /// <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);
            }
        }
Пример #12
0
        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));
        }
Пример #13
0
        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));
        }
Пример #14
0
        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);
        }
Пример #15
0
        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));
        }
Пример #16
0
        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));
        }
Пример #18
0
        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);
            }
        }
Пример #19
0
        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);
        }