Beispiel #1
0
        internal NatsConnection(
            NatsServerInfo serverInfo,
            Socket socket,
            Stream stream,
            CancellationToken cancellationToken)
        {
            ServerInfo = serverInfo ?? throw new ArgumentNullException(nameof(serverInfo));

            _socket = socket ?? throw new ArgumentNullException(nameof(socket));
            if (!socket.Connected)
            {
                throw new ArgumentException("Socket is not connected.", nameof(socket));
            }

            _stream            = stream ?? throw new ArgumentNullException(nameof(stream));
            _writeStream       = new BufferedStream(_stream, socket.SendBufferSize);
            _readStream        = new BufferedStream(_stream, socket.ReceiveBufferSize);
            _cancellationToken = cancellationToken;
            _writeStreamSync   = new SemaphoreSlim(1, 1);
            _writer            = new NatsStreamWriter(_writeStream, _cancellationToken);
            _reader            = new NatsOpStreamReader(_readStream);

            _socketIsConnected = () => _socket?.Connected == true;
            _canRead           = () => _socket?.Connected == true && _stream != null && _stream.CanRead && !_cancellationToken.IsCancellationRequested;
        }
Beispiel #2
0
        internal NatsConnection(
            NatsServerInfo serverInfo,
            Socket socket,
            BufferedStream writeStream,
            BufferedStream readStream,
            NatsOpStreamReader reader,
            CancellationToken cancellationToken)
        {
            EnsureArg.IsNotNull(serverInfo, nameof(serverInfo));
            EnsureArg.IsNotNull(socket, nameof(socket));
            EnsureArg.IsNotNull(writeStream, nameof(writeStream));
            EnsureArg.IsNotNull(readStream, nameof(readStream));
            EnsureArg.IsNotNull(reader, nameof(reader));

            if (!socket.Connected)
            {
                throw new ArgumentException("Socket is not connected.", nameof(socket));
            }

            ServerInfo = serverInfo;

            _socket            = socket;
            _writeStreamSync   = new Locker();
            _writeStream       = writeStream;
            _readStream        = readStream;
            _reader            = reader;
            _cancellationToken = cancellationToken;

            _writer = new NatsStreamWriter(_writeStream, ServerInfo.MaxPayload, _cancellationToken);

            _socketIsConnected = () => _socket != null && _socket.Connected;
            _canRead           = () => _socketIsConnected() && _readStream != null && _readStream.CanRead && !_cancellationToken.IsCancellationRequested;
        }
Beispiel #3
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);
        }
        public static NatsServerInfo Parse(ReadOnlyMemory <char> data)
        {
            var result = new NatsServerInfo();

            using var doc = JsonDocument.Parse(data, new JsonDocumentOptions { AllowTrailingCommas = true });

            var root = doc.RootElement;

            if (root.TryGetProperty("server_id", out var el))
            {
                result.ServerId = el.GetString();
            }

            if (root.TryGetProperty("version", out el))
            {
                result.Version = el.GetString();
            }

            if (root.TryGetProperty("go", out el))
            {
                result.Go = el.GetString();
            }

            if (root.TryGetProperty("host", out el))
            {
                result.Host = el.GetString();
            }

            if (root.TryGetProperty("port", out el))
            {
                result.Port = el.GetInt32();
            }

            if (root.TryGetProperty("auth_required", out el))
            {
                result.AuthRequired = el.GetBoolean();
            }

            if (root.TryGetProperty("tls_required", out el))
            {
                result.TlsRequired = el.GetBoolean();
            }

            if (root.TryGetProperty("tls_verify", out el))
            {
                result.TlsVerify = el.GetBoolean();
            }

            if (root.TryGetProperty("headers", out el))
            {
                result.Headers = el.GetBoolean();
            }

            if (root.TryGetProperty("max_payload", out el))
            {
                result.MaxPayload = el.GetInt32();
            }

            if (root.TryGetProperty("connect_urls", out el))
            {
                result.ConnectUrls.AddRange(el.EnumerateArray().Select(i => i.GetString()));
            }

            if (root.TryGetProperty("ip", out el))
            {
                result.Ip = el.GetString();
            }

            return(result);
        }
Beispiel #5
0
        public static NatsServerInfo Parse(string data)
        {
            var parts = SplitToKeyValues(data);

            var    result = new NatsServerInfo();
            string tmp;

            if (parts.TryGetValue("server_id", out tmp))
            {
                result.ServerId = tmp;
            }

            if (parts.TryGetValue("version", out tmp))
            {
                result.Version = tmp;
            }

            if (parts.TryGetValue("go", out tmp))
            {
                result.Go = tmp;
            }

            if (parts.TryGetValue("host", out tmp))
            {
                result.Host = tmp;
            }

            if (parts.TryGetValue("port", out tmp))
            {
                result.Port = int.Parse(tmp);
            }

            if (parts.TryGetValue("auth_required", out tmp))
            {
                result.AuthRequired = tmp == "true";
            }

            if (parts.TryGetValue("ssl_required", out tmp))
            {
                result.SslRequired = tmp == "true";
            }

            if (parts.TryGetValue("tls_required", out tmp))
            {
                result.TlsRequired = tmp == "true";
            }

            if (parts.TryGetValue("tls_verify", out tmp))
            {
                result.TlsVerify = tmp == "true";
            }

            if (parts.TryGetValue("max_payload", out tmp))
            {
                result.MaxPayload = int.Parse(tmp);
            }

            if (parts.TryGetValue("connect_urls", out tmp) && !string.IsNullOrWhiteSpace(tmp))
            {
                var urls = tmp.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                result.ConnectUrls.AddRange(urls);
            }

            return(result);
        }
        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;
            }
        }