Exemple #1
0
        private async ValueTask <HttpConnection> CreateConnectionAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            // If a non-infinite connect timeout has been set, create and use a new CancellationToken that'll be canceled
            // when either the original token is canceled or a connect timeout occurs.
            CancellationTokenSource cancellationWithConnectTimeout = null;

            if (Settings._connectTimeout != Timeout.InfiniteTimeSpan)
            {
                cancellationWithConnectTimeout = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, default);
                cancellationWithConnectTimeout.CancelAfter(Settings._connectTimeout);
                cancellationToken = cancellationWithConnectTimeout.Token;
            }

            try
            {
                Stream stream = await
                                    (_proxyUri == null ?
                                    ConnectHelper.ConnectAsync(_host, _port, cancellationToken) :
                                    (_sslOptions == null ?
                                     ConnectHelper.ConnectAsync(_proxyUri.IdnHost, _proxyUri.Port, cancellationToken) :
                                     EstablishProxyTunnel(cancellationToken))).ConfigureAwait(false);

                TransportContext transportContext = null;
                if (_sslOptions != null)
                {
                    // TODO #25206 and #24430: Register/IsCancellationRequested should be removable once SslStream auth and sockets respect cancellation.
                    CancellationTokenRegistration ctr = cancellationToken.Register(s => ((Stream)s).Dispose(), stream);
                    try
                    {
                        SslStream sslStream = await ConnectHelper.EstablishSslConnectionAsync(_sslOptions, request, stream, cancellationToken).ConfigureAwait(false);

                        stream           = sslStream;
                        transportContext = sslStream.TransportContext;
                        cancellationToken.ThrowIfCancellationRequested(); // to handle race condition where stream is dispose of by cancellation after auth
                    }
                    catch (Exception exc)
                    {
                        stream.Dispose(); // in case cancellation occurs after successful SSL auth
                        if (HttpConnection.ShouldWrapInOperationCanceledException(exc, cancellationToken))
                        {
                            throw HttpConnection.CreateOperationCanceledException(exc, cancellationToken);
                        }
                        throw;
                    }
                    finally
                    {
                        ctr.Dispose();
                    }
                }

                return(_maxConnections == int.MaxValue ?
                       new HttpConnection(this, stream, transportContext) :
                       new HttpConnectionWithFinalizer(this, stream, transportContext)); // finalizer needed to signal the pool when a connection is dropped
            }
            finally
            {
                cancellationWithConnectTimeout?.Dispose();
            }
        }
Exemple #2
0
        private static async ValueTask <SslStream> EstablishSslConnectionAsyncCore(Stream stream, SslClientAuthenticationOptions sslOptions, CancellationToken cancellationToken)
        {
            SslStream sslStream = new SslStream(stream);

            // TODO #25206 and #24430: Register/IsCancellationRequested should be removable once SslStream auth and sockets respect cancellation.
            CancellationTokenRegistration ctr = cancellationToken.Register(s => ((Stream)s).Dispose(), stream);

            try
            {
                await sslStream.AuthenticateAsClientAsync(sslOptions, cancellationToken).ConfigureAwait(false);
            }
            catch (Exception e)
            {
                sslStream.Dispose();

                if (HttpConnection.ShouldWrapInOperationCanceledException(e, cancellationToken))
                {
                    throw HttpConnection.CreateOperationCanceledException(e, cancellationToken);
                }

                throw new HttpRequestException(SR.net_http_ssl_connection_failed, e);
            }
            finally
            {
                ctr.Dispose();
            }

            // Handle race condition if cancellation happens after SSL auth completes but before the registration is disposed
            if (cancellationToken.IsCancellationRequested)
            {
                sslStream.Dispose();
                throw new OperationCanceledException(cancellationToken);
            }

            return(sslStream);
        }
Exemple #3
0
        public static async ValueTask <Stream> ConnectAsync(string host, int port, CancellationToken cancellationToken)
        {
            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 (csaea.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");

                    // Configure the socket and return a stream for it.
                    Socket socket = saea.ConnectSocket;
                    socket.NoDelay = true;
                    return(new NetworkStream(socket, ownsSocket: true));
                }
            }
            catch (Exception error)
            {
                throw HttpConnection.ShouldWrapInOperationCanceledException(error, cancellationToken) ?
                      HttpConnection.CreateOperationCanceledException(error, cancellationToken) :
                      new HttpRequestException(error.Message, error);
            }
        }