/* Copyright 2010-present MongoDB Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Security; using System.Net.Sockets; using System.Security.Cryptography.X509Certificates; using MongoDB.Driver.Authentication; using MongoDB.Driver.Core.Clusters; using MongoDB.Driver.Core.Configuration; using MongoDB.Driver.Core.Misc; namespace MongoDB.Driver { /// /// Represents a registry of already created clusters. /// public class ClusterRegistry { #region static // static fields private static readonly ClusterRegistry __instance = new ClusterRegistry(); // static properties /// /// Gets the default cluster registry. /// /// /// The default cluster registry. /// public static ClusterRegistry Instance { get { return __instance; } } #endregion // fields private readonly object _lock = new object(); private readonly Dictionary _registry = new Dictionary(); // methods private IClusterInternal CreateCluster(ClusterKey clusterKey) { var builder = new ClusterBuilder() .ConfigureCluster(settings => ConfigureCluster(settings, clusterKey)) .ConfigureServer(settings => ConfigureServer(settings, clusterKey)) .ConfigureConnectionPool(settings => ConfigureConnectionPool(settings, clusterKey)) .ConfigureConnection(settings => ConfigureConnection(settings, clusterKey)) .ConfigureTcp(settings => ConfigureTcp(settings, clusterKey)) .ConfigureLoggingSettings(_ => clusterKey.LoggingSettings) .ConfigureTracingOptions(_ => clusterKey.TracingOptions); if (clusterKey.UseTls) { builder.ConfigureSsl(settings => ConfigureSsl(settings, clusterKey)); } if (clusterKey.Socks5ProxySettings != null) { builder.ConfigureSocks5Proxy(settings => ConfigureSocks5Proxy(settings, clusterKey)); } if (clusterKey.ClusterConfigurator != null) { clusterKey.ClusterConfigurator(builder); } var cluster = builder.BuildClusterInternal(); cluster.Initialize(); return cluster; } private ClusterSettings ConfigureCluster(ClusterSettings settings, ClusterKey clusterKey) { var endPoints = clusterKey.Servers.Select(s => EndPointHelper.Parse(s.ToString())); return settings.With( directConnection: clusterKey.DirectConnection, cryptClientSettings: clusterKey.CryptClientSettings, endPoints: Optional.Enumerable(endPoints), loadBalanced: clusterKey.LoadBalanced, localThreshold: clusterKey.LocalThreshold, replicaSetName: clusterKey.ReplicaSetName, maxServerSelectionWaitQueueSize: clusterKey.WaitQueueSize, serverApi: clusterKey.ServerApi, serverSelectionTimeout: clusterKey.ServerSelectionTimeout, scheme: clusterKey.Scheme) .WithSrvAllowedHostsSuffix(clusterKey.SrvAllowedHostsSuffix); } private ConnectionPoolSettings ConfigureConnectionPool(ConnectionPoolSettings settings, ClusterKey clusterKey) { return settings.With( // maintenanceInterval: TODO: should this be configurable? maxConnecting: clusterKey.MaxConnecting, maxConnections: clusterKey.MaxConnectionPoolSize, minConnections: clusterKey.MinConnectionPoolSize, waitQueueSize: clusterKey.WaitQueueSize, waitQueueTimeout: clusterKey.WaitQueueTimeout); } private ConnectionSettings ConfigureConnection(ConnectionSettings settings, ClusterKey clusterKey) { var endPoints = clusterKey.Servers.Select(s => new DnsEndPoint(s.Host, s.Port)).ToArray(); IAuthenticatorFactory authenticatorFactory = null; if (clusterKey.Credential != null) { authenticatorFactory = new AuthenticatorFactory(() => clusterKey.Credential.ToAuthenticator(endPoints, clusterKey.ServerApi)); } return settings.WithInternal( authenticatorFactory: Optional.Create(authenticatorFactory), compressors: Optional.Enumerable(clusterKey.Compressors), libraryInfo: clusterKey.LibraryInfo, loadBalanced: clusterKey.LoadBalanced, maxIdleTime: clusterKey.MaxConnectionIdleTime, maxLifeTime: clusterKey.MaxConnectionLifeTime, applicationName: clusterKey.ApplicationName); } private ServerSettings ConfigureServer(ServerSettings settings, ClusterKey clusterKey) { return settings.With( heartbeatInterval: clusterKey.HeartbeatInterval, heartbeatTimeout: clusterKey.HeartbeatTimeout, serverMonitoringMode: clusterKey.ServerMonitoringMode); } private SslStreamSettings ConfigureSsl(SslStreamSettings settings, ClusterKey clusterKey) { if (clusterKey.UseTls) { var sslSettings = clusterKey.SslSettings ?? new SslSettings(); var validationCallback = sslSettings.ServerCertificateValidationCallback; if (validationCallback == null && clusterKey.AllowInsecureTls) { validationCallback = AcceptAnySslCertificate; } return settings.With( clientCertificates: Optional.Enumerable(sslSettings.ClientCertificates ?? Enumerable.Empty()), checkCertificateRevocation: sslSettings.CheckCertificateRevocation, clientCertificateSelectionCallback: sslSettings.ClientCertificateSelectionCallback, enabledProtocols: sslSettings.EnabledSslProtocols, serverCertificateValidationCallback: validationCallback); } return settings; } private TcpStreamSettings ConfigureTcp(TcpStreamSettings settings, ClusterKey clusterKey) { if (clusterKey.IPv6) { settings = settings.With(addressFamily: AddressFamily.InterNetworkV6); } return settings.With( connectTimeout: clusterKey.ConnectTimeout, readTimeout: clusterKey.SocketTimeout, receiveBufferSize: clusterKey.ReceiveBufferSize, sendBufferSize: clusterKey.SendBufferSize, writeTimeout: clusterKey.SocketTimeout); } private Socks5ProxyStreamSettings ConfigureSocks5Proxy(Socks5ProxyStreamSettings settings, ClusterKey clusterKey) { return settings.With( clusterKey.Socks5ProxySettings); } internal IClusterInternal GetOrCreateCluster(ClusterKey clusterKey) { lock (_lock) { IClusterInternal cluster; if (!_registry.TryGetValue(clusterKey, out cluster)) { cluster = CreateCluster(clusterKey); _registry.Add(clusterKey, cluster); } return cluster; } } private static bool AcceptAnySslCertificate( object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors ) { return true; } /// /// Unregisters and disposes the cluster. /// /// The cluster. public void UnregisterAndDisposeCluster(ICluster cluster) { Ensure.IsNotNull(cluster, nameof(cluster)); lock (_lock) { ClusterKey clusterKey = null; foreach (var keyValuePair in _registry) { if (object.ReferenceEquals(keyValuePair.Value, cluster)) { clusterKey = keyValuePair.Key; break; } } if (clusterKey != null) { _registry.Remove(clusterKey); } } cluster.Dispose(); } } }