예제 #1
0
        private static NatsServerInfo VerifyConnection(Host host, ConnectionInfo connectionInfo, Socket socket, Func <IOp> readOne)
        {
            if (!socket.Connected)
            {
                throw NatsException.FailedToConnectToHost(host, "No connection could be established.");
            }

            var op = readOne();

            if (op == null)
            {
                throw NatsException.FailedToConnectToHost(host, "Expected to get INFO after establishing connection. Got nothing.");
            }

            var infoOp = op as InfoOp;

            if (infoOp == null)
            {
                throw NatsException.FailedToConnectToHost(host, $"Expected to get INFO after establishing connection. Got {op.GetType().Name}.");
            }

            Logger.Debug($"Got INFO during connect. {infoOp.GetAsString()}");

            var serverInfo  = NatsServerInfo.Parse(infoOp.Message);
            var credentials = host.HasNonEmptyCredentials() ? host.Credentials : connectionInfo.Credentials;

            if (serverInfo.AuthRequired && (credentials == null || credentials == Credentials.Empty))
            {
                throw NatsException.MissingCredentials(host);
            }

            socket.Send(ConnectCmd.Generate(connectionInfo.Verbose, credentials));
            socket.Send(PingCmd.Generate());

            op = readOne();
            if (op == null)
            {
                throw NatsException.FailedToConnectToHost(host, "Expected to read something after CONNECT and PING. Got nothing.");
            }

            if (op is ErrOp)
            {
                throw NatsException.FailedToConnectToHost(host, $"Expected to get PONG after sending CONNECT and PING. Got {op.GetAsString()}.");
            }

            if (!socket.Connected)
            {
                throw NatsException.FailedToConnectToHost(host, "No connection could be established.");
            }

            return(serverInfo);
        }
        private async Task <(INatsConnection connection, IList <IOp> consumedOps)> EstablishConnectionAsync(
            Host host,
            ConnectionInfo connectionInfo,
            CancellationToken cancellationToken)
        {
            var serverCertificateValidation = connectionInfo.ServerCertificateValidation ?? DefaultServerCertificateValidation;

            bool RemoteCertificateValidationCallback(object _, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors)
            => serverCertificateValidation(certificate, chain, errors);

            var    consumedOps = new List <IOp>();
            Socket socket      = null;
            Stream stream      = null;

            try
            {
                socket = _socketFactory.Create(connectionInfo.SocketOptions);
                await socket.ConnectAsync(
                    host,
                    connectionInfo.SocketOptions.ConnectTimeoutMs,
                    cancellationToken).ConfigureAwait(false);

                stream = socket.CreateReadWriteStream();
                var reader = new NatsOpStreamReader(stream);

                var op = reader.ReadOneOp();
                if (op == null)
                {
                    throw NatsException.FailedToConnectToHost(host,
                                                              "Expected to get INFO after establishing connection. Got nothing.");
                }

                if (!(op is InfoOp infoOp))
                {
                    throw NatsException.FailedToConnectToHost(host,
                                                              $"Expected to get INFO after establishing connection. Got {op.GetType().Name}.");
                }

                var serverInfo  = NatsServerInfo.Parse(infoOp.Message);
                var credentials = host.HasNonEmptyCredentials() ? host.Credentials : connectionInfo.Credentials;
                if (serverInfo.AuthRequired && (credentials == null || credentials == Credentials.Empty))
                {
                    throw NatsException.MissingCredentials(host);
                }

                if (serverInfo.TlsVerify && connectionInfo.ClientCertificates.Count == 0)
                {
                    throw NatsException.MissingClientCertificates(host);
                }

                consumedOps.Add(op);

                if (serverInfo.TlsRequired)
                {
                    await stream.DisposeAsync();

                    stream = new SslStream(socket.CreateReadWriteStream(), false, RemoteCertificateValidationCallback, null, EncryptionPolicy.RequireEncryption);
                    var ssl = (SslStream)stream;

                    var clientAuthOptions = new SslClientAuthenticationOptions
                    {
                        RemoteCertificateValidationCallback = RemoteCertificateValidationCallback,
                        AllowRenegotiation             = true,
                        CertificateRevocationCheckMode = X509RevocationMode.Online,
                        ClientCertificates             = connectionInfo.ClientCertificates,
                        EnabledSslProtocols            = SslProtocols.Tls12,
                        EncryptionPolicy = EncryptionPolicy.RequireEncryption,
                        TargetHost       = host.Address
                    };

                    await ssl.AuthenticateAsClientAsync(clientAuthOptions, cancellationToken).ConfigureAwait(false);

                    reader = new NatsOpStreamReader(ssl);
                }

                stream.Write(ConnectCmd.Generate(connectionInfo.Verbose, credentials, connectionInfo.Name));
                stream.Write(PingCmd.Bytes.Span);
                await stream.FlushAsync(cancellationToken).ConfigureAwait(false);

                op = reader.ReadOneOp();
                if (op == null)
                {
                    throw NatsException.FailedToConnectToHost(host,
                                                              "Expected to read something after CONNECT and PING. Got nothing.");
                }

                if (op is ErrOp)
                {
                    throw NatsException.FailedToConnectToHost(host,
                                                              $"Expected to get PONG after sending CONNECT and PING. Got {op.GetAsString()}.");
                }

                if (!socket.Connected)
                {
                    throw NatsException.FailedToConnectToHost(host, "No connection could be established.");
                }

                consumedOps.Add(op);

                return(
                    new NatsConnection(
                        serverInfo,
                        socket,
                        stream,
                        cancellationToken),
                    consumedOps);
            }
            catch
            {
                Swallow.Everything(
                    () =>
                {
                    stream?.Dispose();
                    stream = null;
                },
                    () =>
                {
                    if (socket == null)
                    {
                        return;
                    }

                    if (socket.Connected)
                    {
                        socket.Shutdown(SocketShutdown.Both);
                    }

                    socket.Dispose();
                    socket = null;
                });

                throw;
            }
        }