/* 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; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using MongoDB.Bson; using MongoDB.Driver.Core.Configuration; namespace MongoDB.Driver { /// /// Represents an immutable URL style connection string. See also MongoUrlBuilder. /// #if NET452 [Serializable] #endif public class MongoUrl : IEquatable { // private static fields private static object __staticLock = new object(); private static Dictionary __cache = new Dictionary(); // private fields private readonly string _applicationName; private readonly string _authenticationMechanism; private readonly IEnumerable> _authenticationMechanismProperties; private readonly string _authenticationSource; private readonly ConnectionMode _connectionMode; private readonly TimeSpan _connectTimeout; private readonly string _databaseName; private readonly bool? _fsync; private readonly GuidRepresentation _guidRepresentation; private readonly TimeSpan _heartbeatInterval; private readonly TimeSpan _heartbeatTimeout; private readonly bool _ipv6; private readonly bool? _journal; private readonly TimeSpan _maxConnectionIdleTime; private readonly TimeSpan _maxConnectionLifeTime; private readonly int _maxConnectionPoolSize; private readonly int _minConnectionPoolSize; private readonly string _password; private readonly ReadConcernLevel? _readConcernLevel; private readonly ReadPreference _readPreference; private readonly string _replicaSetName; private readonly bool? _retryWrites; private readonly TimeSpan _localThreshold; private readonly ConnectionStringScheme _scheme; private readonly IEnumerable _servers; private readonly TimeSpan _serverSelectionTimeout; private readonly TimeSpan _socketTimeout; private readonly string _username; private readonly bool _useSsl; private readonly bool _verifySslCertificate; private readonly WriteConcern.WValue _w; private readonly double _waitQueueMultiple; private readonly int _waitQueueSize; private readonly TimeSpan _waitQueueTimeout; private readonly TimeSpan? _wTimeout; private readonly string _url; private readonly string _originalUrl; // constructors /// /// Creates a new instance of MongoUrl. /// /// The URL containing the settings. public MongoUrl(string url) { _originalUrl = url; var builder = new MongoUrlBuilder(url); // parses url _applicationName = builder.ApplicationName; _authenticationMechanism = builder.AuthenticationMechanism; _authenticationMechanismProperties = builder.AuthenticationMechanismProperties; _authenticationSource = builder.AuthenticationSource; _connectionMode = builder.ConnectionMode; _connectTimeout = builder.ConnectTimeout; _databaseName = builder.DatabaseName; _fsync = builder.FSync; _guidRepresentation = builder.GuidRepresentation; _heartbeatInterval = builder.HeartbeatInterval; _heartbeatTimeout = builder.HeartbeatTimeout; _ipv6 = builder.IPv6; _journal = builder.Journal; _localThreshold = builder.LocalThreshold; _maxConnectionIdleTime = builder.MaxConnectionIdleTime; _maxConnectionLifeTime = builder.MaxConnectionLifeTime; _maxConnectionPoolSize = builder.MaxConnectionPoolSize; _minConnectionPoolSize = builder.MinConnectionPoolSize; _password = builder.Password; _readConcernLevel = builder.ReadConcernLevel; _readPreference = builder.ReadPreference; _replicaSetName = builder.ReplicaSetName; _retryWrites = builder.RetryWrites; _scheme = builder.Scheme; _servers = builder.Servers; _serverSelectionTimeout = builder.ServerSelectionTimeout; _socketTimeout = builder.SocketTimeout; _username = builder.Username; _useSsl = builder.UseSsl; _verifySslCertificate = builder.VerifySslCertificate; _w = builder.W; _waitQueueMultiple = builder.WaitQueueMultiple; _waitQueueSize = builder.WaitQueueSize; _waitQueueTimeout = builder.WaitQueueTimeout; _wTimeout = builder.WTimeout; _url = builder.ToString(); // keep canonical form } // public properties /// /// Gets the application name. /// public string ApplicationName { get { return _applicationName; } } /// /// Gets the authentication mechanism. /// public string AuthenticationMechanism { get { return _authenticationMechanism; } } /// /// Gets the authentication mechanism properties. /// public IEnumerable> AuthenticationMechanismProperties { get { return _authenticationMechanismProperties; } } /// /// Gets the authentication source. /// public string AuthenticationSource { get { return _authenticationSource; } } /// /// Gets the actual wait queue size (either WaitQueueSize or WaitQueueMultiple x MaxConnectionPoolSize). /// public int ComputedWaitQueueSize { get { if (_waitQueueMultiple == 0.0) { return _waitQueueSize; } else { return (int)(_waitQueueMultiple * _maxConnectionPoolSize); } } } /// /// Gets the connection mode. /// public ConnectionMode ConnectionMode { get { return _connectionMode; } } /// /// Gets the connect timeout. /// public TimeSpan ConnectTimeout { get { return _connectTimeout; } } /// /// Gets the optional database name. /// public string DatabaseName { get { return _databaseName; } } /// /// Gets the FSync component of the write concern. /// public bool? FSync { get { return _fsync; } } /// /// Gets the representation to use for Guids. /// public GuidRepresentation GuidRepresentation { get { return _guidRepresentation; } } /// /// Gets a value indicating whether this instance has authentication settings. /// public bool HasAuthenticationSettings { get { return _username != null || _password != null || _authenticationMechanism != null || _authenticationSource != null; } } /// /// Gets the heartbeat interval. /// public TimeSpan HeartbeatInterval { get { return _heartbeatInterval; } } /// /// Gets the heartbeat timeout. /// public TimeSpan HeartbeatTimeout { get { return _heartbeatTimeout; } } /// /// Gets a value indicating whether to use IPv6. /// public bool IPv6 { get { return _ipv6; } } /// /// Gets the Journal component of the write concern. /// public bool? Journal { get { return _journal; } } /// /// Gets the local threshold. /// public TimeSpan LocalThreshold { get { return _localThreshold; } } /// /// Gets the max connection idle time. /// public TimeSpan MaxConnectionIdleTime { get { return _maxConnectionIdleTime; } } /// /// Gets the max connection life time. /// public TimeSpan MaxConnectionLifeTime { get { return _maxConnectionLifeTime; } } /// /// Gets the max connection pool size. /// public int MaxConnectionPoolSize { get { return _maxConnectionPoolSize; } } /// /// Gets the min connection pool size. /// public int MinConnectionPoolSize { get { return _minConnectionPoolSize; } } /// /// Gets the password. /// public string Password { get { return _password; } } /// /// Gets the read concern level. /// public ReadConcernLevel? ReadConcernLevel { get { return _readConcernLevel; } } /// /// Gets the read preference. /// public ReadPreference ReadPreference { get { return _readPreference; } } /// /// Gets the name of the replica set. /// public string ReplicaSetName { get { return _replicaSetName; } } /// /// Gets whether writes will be retried. /// public bool? RetryWrites { get { return _retryWrites; } } /// /// Gets the scheme. /// public ConnectionStringScheme Scheme { get { return _scheme; } } /// /// Gets the address of the server (see also Servers if using more than one address). /// public MongoServerAddress Server { get { return (_servers == null) ? null : _servers.Single(); } } /// /// Gets the list of server addresses (see also Server if using only one address). /// public IEnumerable Servers { get { return _servers; } } /// /// Gets the server selection timeout. /// public TimeSpan ServerSelectionTimeout { get { return _serverSelectionTimeout; } } /// /// Gets the socket timeout. /// public TimeSpan SocketTimeout { get { return _socketTimeout; } } /// /// Gets the URL (in canonical form). /// public string Url { get { return _url; } } /// /// Gets the username. /// public string Username { get { return _username; } } /// /// Gets a value indicating whether to use SSL. /// public bool UseSsl { get { return _useSsl; } } /// /// Gets a value indicating whether to verify an SSL certificate. /// public bool VerifySslCertificate { get { return _verifySslCertificate; } } /// /// Gets the W component of the write concern. /// public WriteConcern.WValue W { get { return _w; } } /// /// Gets the wait queue multiple (the actual wait queue size will be WaitQueueMultiple x MaxConnectionPoolSize). /// public double WaitQueueMultiple { get { return _waitQueueMultiple; } } /// /// Gets the wait queue size. /// public int WaitQueueSize { get { return _waitQueueSize; } } /// /// Gets the wait queue timeout. /// public TimeSpan WaitQueueTimeout { get { return _waitQueueTimeout; } } /// /// Gets the WTimeout component of the write concern. /// public TimeSpan? WTimeout { get { return _wTimeout; } } // public operators /// /// Compares two MongoUrls. /// /// The first URL. /// The other URL. /// True if the two URLs are equal (or both null). public static bool operator ==(MongoUrl lhs, MongoUrl rhs) { return object.Equals(lhs, rhs); } /// /// Compares two MongoUrls. /// /// The first URL. /// The other URL. /// True if the two URLs are not equal (or one is null and the other is not). public static bool operator !=(MongoUrl lhs, MongoUrl rhs) { return !(lhs == rhs); } // public static methods /// /// Clears the URL cache. When a URL is parsed it is stored in the cache so that it doesn't have to be /// parsed again. There is rarely a need to call this method. /// public static void ClearCache() { __cache.Clear(); } /// /// Creates an instance of MongoUrl (might be an existing existence if the same URL has been used before). /// /// The URL containing the settings. /// An instance of MongoUrl. public static MongoUrl Create(string url) { // cache previously seen urls to avoid repeated parsing lock (__staticLock) { MongoUrl mongoUrl; if (!__cache.TryGetValue(url, out mongoUrl)) { mongoUrl = new MongoUrl(url); var canonicalUrl = mongoUrl.ToString(); if (canonicalUrl != url) { if (__cache.ContainsKey(canonicalUrl)) { mongoUrl = __cache[canonicalUrl]; // use existing MongoUrl } else { __cache[canonicalUrl] = mongoUrl; // cache under canonicalUrl also } } __cache[url] = mongoUrl; } return mongoUrl; } } // public methods /// /// Compares two MongoUrls. /// /// The other URL. /// True if the two URLs are equal. public bool Equals(MongoUrl rhs) { if (object.ReferenceEquals(rhs, null) || GetType() != rhs.GetType()) { return false; } return _url == rhs._url; // this works because URL is in canonical form } /// /// Compares two MongoUrls. /// /// The other URL. /// True if the two URLs are equal. public override bool Equals(object obj) { return Equals(obj as MongoUrl); // works even if obj is null or of a different type } /// /// Gets the credential. /// /// The credential (or null if the URL has not authentication settings). public MongoCredential GetCredential() { if (HasAuthenticationSettings) { return MongoCredential.FromComponents( _authenticationMechanism, _authenticationSource ?? _databaseName, _username, _password); } else { return null; } } /// /// Gets the hash code. /// /// The hash code. public override int GetHashCode() { return _url.GetHashCode(); // this works because URL is in canonical form } /// /// Returns a WriteConcern value based on this instance's settings and a default enabled value. /// /// The default enabled value. /// A WriteConcern. public WriteConcern GetWriteConcern(bool enabledDefault) { if (_w == null && !_wTimeout.HasValue && !_fsync.HasValue && !_journal.HasValue) { return enabledDefault ? WriteConcern.Acknowledged : WriteConcern.Unacknowledged; } return new WriteConcern(_w, _wTimeout, _fsync, _journal); } /// /// Resolves a connection string. If the connection string indicates more information is available /// in the DNS system, it will acquire that information as well. /// /// A resolved MongoURL. public MongoUrl Resolve() { if (_scheme == ConnectionStringScheme.MongoDB) { return this; } var connectionString = new ConnectionString(_originalUrl); var resolved = connectionString.Resolve(); return new MongoUrl(resolved.ToString()); } /// /// Resolves a connection string. If the connection string indicates more information is available /// in the DNS system, it will acquire that information as well. /// /// The cancellation token. /// A resolved MongoURL. public async Task ResolveAsync(CancellationToken cancellationToken = default(CancellationToken)) { if (_scheme == ConnectionStringScheme.MongoDB) { return this; } var connectionString = new ConnectionString(_originalUrl); var resolved = await connectionString.ResolveAsync(cancellationToken).ConfigureAwait(false); return new MongoUrl(resolved.ToString()); } /// /// Returns the canonical URL based on the settings in this MongoUrlBuilder. /// /// The canonical URL. public override string ToString() { return _url; } // private methods private bool AnyWriteConcernSettingsAreSet() { return _fsync != null || _journal != null || _w != null || _wTimeout != null; } } }