private Tuple <INatsConnection, IList <IOp> > EstablishConnection(Host host, ConnectionInfo connectionInfo, CancellationToken cancellationToken) { Socket socket = null; BufferedStream writeStream = null; BufferedStream readStream = null; try { socket = _socketFactory.Create(connectionInfo.SocketOptions); socket.Connect( host, connectionInfo.SocketOptions.ConnectTimeoutMs, cancellationToken); readStream = new BufferedStream(socket.CreateReadStream(), socket.ReceiveBufferSize); var reader = new NatsOpStreamReader(readStream); var consumedOps = new List <IOp>(); IOp ReadOne() { var op = reader.ReadOp().FirstOrDefault(); if (op != null) { consumedOps.Add(op); } return(op); } var serverInfo = VerifyConnection(host, connectionInfo, socket, ReadOne); writeStream = new BufferedStream(socket.CreateWriteStream(), socket.SendBufferSize); return(new Tuple <INatsConnection, IList <IOp> >(new NatsConnection( serverInfo, socket, writeStream, readStream, reader, cancellationToken), consumedOps)); } catch { Swallow.Everything( () => { readStream?.Dispose(); readStream = null; }, () => { writeStream?.Dispose(); writeStream = null; }, () => { if (socket == null) { return; } if (socket.Connected) { socket.Shutdown(SocketShutdown.Both); } socket.Dispose(); socket = null; }); throw; } }
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; } }