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; }
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; }
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); }
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; } }