MongoUrlBuilder.cs 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955
  1. /* Copyright 2010-present MongoDB Inc.
  2. *
  3. * Licensed under the Apache License, Version 2.0 (the "License");
  4. * you may not use this file except in compliance with the License.
  5. * You may obtain a copy of the License at
  6. *
  7. * http://www.apache.org/licenses/LICENSE-2.0
  8. *
  9. * Unless required by applicable law or agreed to in writing, software
  10. * distributed under the License is distributed on an "AS IS" BASIS,
  11. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. * See the License for the specific language governing permissions and
  13. * limitations under the License.
  14. */
  15. using System;
  16. using System.Collections.Generic;
  17. using System.Linq;
  18. using System.Net;
  19. using System.Text;
  20. using System.Text.RegularExpressions;
  21. using System.Threading;
  22. using MongoDB.Bson;
  23. using MongoDB.Bson.IO;
  24. using MongoDB.Driver.Core.Clusters;
  25. using MongoDB.Driver.Core.Configuration;
  26. using MongoDB.Driver.Core.Misc;
  27. using MongoDB.Shared;
  28. namespace MongoDB.Driver
  29. {
  30. /// <summary>
  31. /// Represents URL-style connection strings.
  32. /// </summary>
  33. #if NET452
  34. [Serializable]
  35. #endif
  36. public class MongoUrlBuilder
  37. {
  38. // private fields
  39. private string _applicationName;
  40. private string _authenticationMechanism;
  41. private Dictionary<string, string> _authenticationMechanismProperties;
  42. private string _authenticationSource;
  43. private ConnectionMode _connectionMode;
  44. private TimeSpan _connectTimeout;
  45. private string _databaseName;
  46. private bool? _fsync;
  47. private GuidRepresentation _guidRepresentation;
  48. private TimeSpan _heartbeatInterval;
  49. private TimeSpan _heartbeatTimeout;
  50. private bool _ipv6;
  51. private bool? _journal;
  52. private TimeSpan _localThreshold;
  53. private TimeSpan _maxConnectionIdleTime;
  54. private TimeSpan _maxConnectionLifeTime;
  55. private int _maxConnectionPoolSize;
  56. private int _minConnectionPoolSize;
  57. private string _password;
  58. private ReadConcernLevel? _readConcernLevel;
  59. private ReadPreference _readPreference;
  60. private string _replicaSetName;
  61. private bool? _retryWrites;
  62. private ConnectionStringScheme _scheme;
  63. private IEnumerable<MongoServerAddress> _servers;
  64. private TimeSpan _serverSelectionTimeout;
  65. private TimeSpan _socketTimeout;
  66. private string _username;
  67. private bool _useSsl;
  68. private bool _verifySslCertificate;
  69. private WriteConcern.WValue _w;
  70. private double _waitQueueMultiple;
  71. private int _waitQueueSize;
  72. private TimeSpan _waitQueueTimeout;
  73. private TimeSpan? _wTimeout;
  74. // constructors
  75. /// <summary>
  76. /// Creates a new instance of MongoUrlBuilder.
  77. /// </summary>
  78. public MongoUrlBuilder()
  79. {
  80. _applicationName = null;
  81. _authenticationMechanism = MongoDefaults.AuthenticationMechanism;
  82. _authenticationMechanismProperties = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
  83. _authenticationSource = null;
  84. _connectionMode = ConnectionMode.Automatic;
  85. _connectTimeout = MongoDefaults.ConnectTimeout;
  86. _databaseName = null;
  87. _fsync = null;
  88. _guidRepresentation = MongoDefaults.GuidRepresentation;
  89. _heartbeatInterval = ServerSettings.DefaultHeartbeatInterval;
  90. _heartbeatTimeout = ServerSettings.DefaultHeartbeatTimeout;
  91. _ipv6 = false;
  92. _journal = null;
  93. _maxConnectionIdleTime = MongoDefaults.MaxConnectionIdleTime;
  94. _maxConnectionLifeTime = MongoDefaults.MaxConnectionLifeTime;
  95. _maxConnectionPoolSize = MongoDefaults.MaxConnectionPoolSize;
  96. _minConnectionPoolSize = MongoDefaults.MinConnectionPoolSize;
  97. _password = null;
  98. _readConcernLevel = null;
  99. _readPreference = null;
  100. _replicaSetName = null;
  101. _retryWrites = null;
  102. _localThreshold = MongoDefaults.LocalThreshold;
  103. _servers = new[] { new MongoServerAddress("localhost", 27017) };
  104. _serverSelectionTimeout = MongoDefaults.ServerSelectionTimeout;
  105. _socketTimeout = MongoDefaults.SocketTimeout;
  106. _username = null;
  107. _useSsl = false;
  108. _verifySslCertificate = true;
  109. _w = null;
  110. _waitQueueMultiple = MongoDefaults.WaitQueueMultiple;
  111. _waitQueueSize = MongoDefaults.WaitQueueSize;
  112. _waitQueueTimeout = MongoDefaults.WaitQueueTimeout;
  113. _wTimeout = null;
  114. }
  115. /// <summary>
  116. /// Creates a new instance of MongoUrlBuilder.
  117. /// </summary>
  118. /// <param name="url">The initial settings.</param>
  119. public MongoUrlBuilder(string url)
  120. : this()
  121. {
  122. Parse(url);
  123. }
  124. // public properties
  125. /// <summary>
  126. /// Gets or sets the application name.
  127. /// </summary>
  128. public string ApplicationName
  129. {
  130. get { return _applicationName; }
  131. set { _applicationName = ApplicationNameHelper.EnsureApplicationNameIsValid(value, nameof(value)); }
  132. }
  133. /// <summary>
  134. /// Gets or sets the authentication mechanism.
  135. /// </summary>
  136. public string AuthenticationMechanism
  137. {
  138. get { return _authenticationMechanism; }
  139. set { _authenticationMechanism = value; }
  140. }
  141. /// <summary>
  142. /// Gets or sets the authentication mechanism properties.
  143. /// </summary>
  144. public IEnumerable<KeyValuePair<string, string>> AuthenticationMechanismProperties
  145. {
  146. get { return _authenticationMechanismProperties; }
  147. set
  148. {
  149. if (value == null)
  150. {
  151. throw new ArgumentNullException("value");
  152. }
  153. _authenticationMechanismProperties = value.ToDictionary(x => x.Key, x => x.Value, StringComparer.OrdinalIgnoreCase);
  154. }
  155. }
  156. /// <summary>
  157. /// Gets or sets the authentication source.
  158. /// </summary>
  159. public string AuthenticationSource
  160. {
  161. get { return _authenticationSource; }
  162. set { _authenticationSource = value; }
  163. }
  164. /// <summary>
  165. /// Gets the actual wait queue size (either WaitQueueSize or WaitQueueMultiple x MaxConnectionPoolSize).
  166. /// </summary>
  167. public int ComputedWaitQueueSize
  168. {
  169. get
  170. {
  171. if (_waitQueueMultiple == 0.0)
  172. {
  173. return _waitQueueSize;
  174. }
  175. else
  176. {
  177. return (int)(_waitQueueMultiple * _maxConnectionPoolSize);
  178. }
  179. }
  180. }
  181. /// <summary>
  182. /// Gets or sets the connection mode.
  183. /// </summary>
  184. public ConnectionMode ConnectionMode
  185. {
  186. get { return _connectionMode; }
  187. set { _connectionMode = value; }
  188. }
  189. /// <summary>
  190. /// Gets or sets the connect timeout.
  191. /// </summary>
  192. public TimeSpan ConnectTimeout
  193. {
  194. get { return _connectTimeout; }
  195. set
  196. {
  197. if (value < TimeSpan.Zero)
  198. {
  199. throw new ArgumentOutOfRangeException("value", "ConnectTimeout must be greater than or equal to zero.");
  200. }
  201. _connectTimeout = value;
  202. }
  203. }
  204. /// <summary>
  205. /// Gets or sets the optional database name.
  206. /// </summary>
  207. public string DatabaseName
  208. {
  209. get { return _databaseName; }
  210. set { _databaseName = value; }
  211. }
  212. /// <summary>
  213. /// Gets or sets the FSync component of the write concern.
  214. /// </summary>
  215. public bool? FSync
  216. {
  217. get { return _fsync; }
  218. set
  219. {
  220. _fsync = value;
  221. }
  222. }
  223. /// <summary>
  224. /// Gets or sets the representation to use for Guids.
  225. /// </summary>
  226. public GuidRepresentation GuidRepresentation
  227. {
  228. get { return _guidRepresentation; }
  229. set { _guidRepresentation = value; }
  230. }
  231. /// <summary>
  232. /// Gets or sets the heartbeat interval.
  233. /// </summary>
  234. public TimeSpan HeartbeatInterval
  235. {
  236. get { return _heartbeatInterval; }
  237. set
  238. {
  239. if (value < TimeSpan.Zero)
  240. {
  241. throw new ArgumentOutOfRangeException("value", "HeartbeatInterval must be greater than or equal to zero.");
  242. }
  243. _heartbeatInterval = value;
  244. }
  245. }
  246. /// <summary>
  247. /// Gets or sets the heartbeat timeout.
  248. /// </summary>
  249. public TimeSpan HeartbeatTimeout
  250. {
  251. get { return _heartbeatTimeout; }
  252. set
  253. {
  254. if (value < TimeSpan.Zero && value != Timeout.InfiniteTimeSpan)
  255. {
  256. throw new ArgumentOutOfRangeException("value", "HeartbeatTimeout must be greater than or equal to zero.");
  257. }
  258. _heartbeatTimeout = value;
  259. }
  260. }
  261. /// <summary>
  262. /// Gets or sets a value indicating whether to use IPv6.
  263. /// </summary>
  264. public bool IPv6
  265. {
  266. get { return _ipv6; }
  267. set { _ipv6 = value; }
  268. }
  269. /// <summary>
  270. /// Gets or sets the Journal component of the write concern.
  271. /// </summary>
  272. public bool? Journal
  273. {
  274. get { return _journal; }
  275. set
  276. {
  277. _journal = value;
  278. }
  279. }
  280. /// <summary>
  281. /// Gets or sets the local threshold.
  282. /// </summary>
  283. public TimeSpan LocalThreshold
  284. {
  285. get { return _localThreshold; }
  286. set
  287. {
  288. if (value < TimeSpan.Zero)
  289. {
  290. throw new ArgumentOutOfRangeException("value", "LocalThreshold must be greater than or equal to zero.");
  291. }
  292. _localThreshold = value;
  293. }
  294. }
  295. /// <summary>
  296. /// Gets or sets the max connection idle time.
  297. /// </summary>
  298. public TimeSpan MaxConnectionIdleTime
  299. {
  300. get { return _maxConnectionIdleTime; }
  301. set
  302. {
  303. if (value < TimeSpan.Zero)
  304. {
  305. throw new ArgumentOutOfRangeException("value", "MaxConnectionIdleTime must be greater than or equal to zero.");
  306. }
  307. _maxConnectionIdleTime = value;
  308. }
  309. }
  310. /// <summary>
  311. /// Gets or sets the max connection life time.
  312. /// </summary>
  313. public TimeSpan MaxConnectionLifeTime
  314. {
  315. get { return _maxConnectionLifeTime; }
  316. set
  317. {
  318. if (value < TimeSpan.Zero)
  319. {
  320. throw new ArgumentOutOfRangeException("value", "MaxConnectionLifeTime must be greater than or equal to zero.");
  321. }
  322. _maxConnectionLifeTime = value;
  323. }
  324. }
  325. /// <summary>
  326. /// Gets or sets the max connection pool size.
  327. /// </summary>
  328. public int MaxConnectionPoolSize
  329. {
  330. get { return _maxConnectionPoolSize; }
  331. set
  332. {
  333. if (value <= 0)
  334. {
  335. throw new ArgumentOutOfRangeException("value", "MaxConnectionPoolSize must be greater than zero.");
  336. }
  337. _maxConnectionPoolSize = value;
  338. }
  339. }
  340. /// <summary>
  341. /// Gets or sets the min connection pool size.
  342. /// </summary>
  343. public int MinConnectionPoolSize
  344. {
  345. get { return _minConnectionPoolSize; }
  346. set
  347. {
  348. if (value < 0)
  349. {
  350. throw new ArgumentOutOfRangeException("value", "MinConnectionPoolSize must be greater than or equal to zero.");
  351. }
  352. _minConnectionPoolSize = value;
  353. }
  354. }
  355. /// <summary>
  356. /// Gets or sets the password.
  357. /// </summary>
  358. public string Password
  359. {
  360. get { return _password; }
  361. set { _password = value; }
  362. }
  363. /// <summary>
  364. /// Gets or sets the read concern level.
  365. /// </summary>
  366. public ReadConcernLevel? ReadConcernLevel
  367. {
  368. get { return _readConcernLevel; }
  369. set { _readConcernLevel = value; }
  370. }
  371. /// <summary>
  372. /// Gets or sets the read preference.
  373. /// </summary>
  374. public ReadPreference ReadPreference
  375. {
  376. get
  377. {
  378. if (_readPreference != null)
  379. {
  380. return _readPreference;
  381. }
  382. else
  383. {
  384. return null;
  385. }
  386. }
  387. set { _readPreference = value; }
  388. }
  389. /// <summary>
  390. /// Gets or sets the name of the replica set.
  391. /// </summary>
  392. public string ReplicaSetName
  393. {
  394. get { return _replicaSetName; }
  395. set { _replicaSetName = value; }
  396. }
  397. /// <summary>
  398. /// Gets or sets whether to retry writes.
  399. /// </summary>
  400. public bool? RetryWrites
  401. {
  402. get { return _retryWrites; }
  403. set { _retryWrites = value; }
  404. }
  405. /// <summary>
  406. /// The scheme used to connect with mongodb.
  407. /// </summary>
  408. public ConnectionStringScheme Scheme
  409. {
  410. get { return _scheme; }
  411. set { _scheme = value; }
  412. }
  413. /// <summary>
  414. /// Gets or sets the address of the server (see also Servers if using more than one address).
  415. /// </summary>
  416. public MongoServerAddress Server
  417. {
  418. get { return (_servers == null) ? null : _servers.Single(); }
  419. set { _servers = (value == null) ? null : new[] { value }; }
  420. }
  421. /// <summary>
  422. /// Gets or sets the list of server addresses (see also Server if using only one address).
  423. /// </summary>
  424. public IEnumerable<MongoServerAddress> Servers
  425. {
  426. get { return _servers; }
  427. set { _servers = value; }
  428. }
  429. /// <summary>
  430. /// Gets or sets the server selection timeout.
  431. /// </summary>
  432. public TimeSpan ServerSelectionTimeout
  433. {
  434. get { return _serverSelectionTimeout; }
  435. set
  436. {
  437. if (value < TimeSpan.Zero)
  438. {
  439. throw new ArgumentOutOfRangeException("value", "ServerSelectionTimeout must be greater than or equal to zero.");
  440. }
  441. _serverSelectionTimeout = value;
  442. }
  443. }
  444. /// <summary>
  445. /// Gets or sets the socket timeout.
  446. /// </summary>
  447. public TimeSpan SocketTimeout
  448. {
  449. get { return _socketTimeout; }
  450. set
  451. {
  452. if (value < TimeSpan.Zero)
  453. {
  454. throw new ArgumentOutOfRangeException("value", "SocketTimeout must be greater than or equal to zero.");
  455. }
  456. _socketTimeout = value;
  457. }
  458. }
  459. /// <summary>
  460. /// Gets or sets the username.
  461. /// </summary>
  462. public string Username
  463. {
  464. get { return _username; }
  465. set { _username = value; }
  466. }
  467. /// <summary>
  468. /// Gets or sets a value indicating whether to use SSL.
  469. /// </summary>
  470. public bool UseSsl
  471. {
  472. get { return _useSsl; }
  473. set { _useSsl = value; }
  474. }
  475. /// <summary>
  476. /// Gets or sets a value indicating whether to verify an SSL certificate.
  477. /// </summary>
  478. public bool VerifySslCertificate
  479. {
  480. get { return _verifySslCertificate; }
  481. set { _verifySslCertificate = value; }
  482. }
  483. /// <summary>
  484. /// Gets or sets the W component of the write concern.
  485. /// </summary>
  486. public WriteConcern.WValue W
  487. {
  488. get { return _w; }
  489. set
  490. {
  491. _w = value;
  492. }
  493. }
  494. /// <summary>
  495. /// Gets or sets the wait queue multiple (the actual wait queue size will be WaitQueueMultiple x MaxConnectionPoolSize).
  496. /// </summary>
  497. public double WaitQueueMultiple
  498. {
  499. get { return _waitQueueMultiple; }
  500. set
  501. {
  502. if (value <= 0.0)
  503. {
  504. throw new ArgumentOutOfRangeException("value", "WaitQueueMultiple must be greater than zero.");
  505. }
  506. _waitQueueMultiple = value;
  507. _waitQueueSize = 0;
  508. }
  509. }
  510. /// <summary>
  511. /// Gets or sets the wait queue size.
  512. /// </summary>
  513. public int WaitQueueSize
  514. {
  515. get { return _waitQueueSize; }
  516. set
  517. {
  518. if (value <= 0)
  519. {
  520. throw new ArgumentOutOfRangeException("value", "WaitQueueSize must be greater than zero.");
  521. }
  522. _waitQueueSize = value;
  523. _waitQueueMultiple = 0.0;
  524. }
  525. }
  526. /// <summary>
  527. /// Gets or sets the wait queue timeout.
  528. /// </summary>
  529. public TimeSpan WaitQueueTimeout
  530. {
  531. get { return _waitQueueTimeout; }
  532. set
  533. {
  534. if (value < TimeSpan.Zero)
  535. {
  536. throw new ArgumentOutOfRangeException("value", "WaitQueueTimeout must be greater than or equal to zero.");
  537. }
  538. _waitQueueTimeout = value;
  539. }
  540. }
  541. /// <summary>
  542. /// Gets or sets the WTimeout component of the write concern.
  543. /// </summary>
  544. public TimeSpan? WTimeout
  545. {
  546. get { return _wTimeout; }
  547. set
  548. {
  549. if (value != null && value.Value < TimeSpan.Zero)
  550. {
  551. throw new ArgumentOutOfRangeException("value", "WTimeout must be greater than or equal to zero.");
  552. }
  553. _wTimeout = value;
  554. }
  555. }
  556. // public methods
  557. /// <summary>
  558. /// Returns a WriteConcern value based on this instance's settings and a default enabled value.
  559. /// </summary>
  560. /// <param name="enabledDefault">The default enabled value.</param>
  561. /// <returns>A WriteConcern.</returns>
  562. public WriteConcern GetWriteConcern(bool enabledDefault)
  563. {
  564. if (_w == null && !_wTimeout.HasValue && !_fsync.HasValue && !_journal.HasValue)
  565. {
  566. return enabledDefault ? WriteConcern.Acknowledged : WriteConcern.Unacknowledged;
  567. }
  568. return new WriteConcern(_w, _wTimeout, _fsync, _journal);
  569. }
  570. /// <summary>
  571. /// Parses a URL and sets all settings to match the URL.
  572. /// </summary>
  573. /// <param name="url">The URL.</param>
  574. public void Parse(string url)
  575. {
  576. var connectionString = new ConnectionString(url);
  577. _applicationName = connectionString.ApplicationName;
  578. _authenticationMechanism = connectionString.AuthMechanism;
  579. _authenticationMechanismProperties = connectionString.AuthMechanismProperties.ToDictionary(x => x.Key, x => x.Value, StringComparer.OrdinalIgnoreCase);
  580. _authenticationSource = connectionString.AuthSource;
  581. switch (connectionString.Connect)
  582. {
  583. case ClusterConnectionMode.Direct:
  584. _connectionMode = Driver.ConnectionMode.Direct;
  585. break;
  586. case ClusterConnectionMode.ReplicaSet:
  587. _connectionMode = Driver.ConnectionMode.ReplicaSet;
  588. break;
  589. case ClusterConnectionMode.Sharded:
  590. _connectionMode = Driver.ConnectionMode.ShardRouter;
  591. break;
  592. case ClusterConnectionMode.Standalone:
  593. _connectionMode = Driver.ConnectionMode.Standalone;
  594. break;
  595. default:
  596. _connectionMode = Driver.ConnectionMode.Automatic;
  597. break;
  598. }
  599. _connectTimeout = connectionString.ConnectTimeout.GetValueOrDefault(MongoDefaults.ConnectTimeout);
  600. _databaseName = connectionString.DatabaseName;
  601. _fsync = connectionString.FSync;
  602. _guidRepresentation = connectionString.UuidRepresentation.GetValueOrDefault(MongoDefaults.GuidRepresentation);
  603. _heartbeatInterval = connectionString.HeartbeatInterval ?? ServerSettings.DefaultHeartbeatInterval;
  604. _heartbeatTimeout = connectionString.HeartbeatTimeout ?? ServerSettings.DefaultHeartbeatTimeout;
  605. _ipv6 = connectionString.Ipv6.GetValueOrDefault(false);
  606. _journal = connectionString.Journal;
  607. _maxConnectionIdleTime = connectionString.MaxIdleTime.GetValueOrDefault(MongoDefaults.MaxConnectionIdleTime);
  608. _maxConnectionLifeTime = connectionString.MaxLifeTime.GetValueOrDefault(MongoDefaults.MaxConnectionLifeTime);
  609. _maxConnectionPoolSize = connectionString.MaxPoolSize.GetValueOrDefault(MongoDefaults.MaxConnectionPoolSize);
  610. _minConnectionPoolSize = connectionString.MinPoolSize.GetValueOrDefault(MongoDefaults.MinConnectionPoolSize);
  611. _password = connectionString.Password;
  612. _readConcernLevel = connectionString.ReadConcernLevel;
  613. if (connectionString.ReadPreference.HasValue || connectionString.ReadPreferenceTags != null || connectionString.MaxStaleness.HasValue)
  614. {
  615. if (!connectionString.ReadPreference.HasValue)
  616. {
  617. throw new MongoConfigurationException("readPreference mode is required when using tag sets or max staleness.");
  618. }
  619. _readPreference = new ReadPreference(connectionString.ReadPreference.Value, connectionString.ReadPreferenceTags, connectionString.MaxStaleness);
  620. }
  621. _replicaSetName = connectionString.ReplicaSet;
  622. _retryWrites = connectionString.RetryWrites;
  623. _localThreshold = connectionString.LocalThreshold.GetValueOrDefault(MongoDefaults.LocalThreshold);
  624. _scheme = connectionString.Scheme;
  625. _servers = connectionString.Hosts.Select(endPoint =>
  626. {
  627. DnsEndPoint dnsEndPoint;
  628. IPEndPoint ipEndPoint;
  629. if ((dnsEndPoint = endPoint as DnsEndPoint) != null)
  630. {
  631. return new MongoServerAddress(dnsEndPoint.Host, dnsEndPoint.Port);
  632. }
  633. else if ((ipEndPoint = endPoint as IPEndPoint) != null)
  634. {
  635. var address = ipEndPoint.Address.ToString();
  636. if (ipEndPoint.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6)
  637. {
  638. address = "[" + address + "]";
  639. }
  640. return new MongoServerAddress(address, ipEndPoint.Port);
  641. }
  642. else
  643. {
  644. throw new NotSupportedException("Only DnsEndPoint and IPEndPoints are supported in the connection string.");
  645. }
  646. });
  647. _serverSelectionTimeout = connectionString.ServerSelectionTimeout.GetValueOrDefault(MongoDefaults.ServerSelectionTimeout);
  648. _socketTimeout = connectionString.SocketTimeout.GetValueOrDefault(MongoDefaults.SocketTimeout);
  649. _username = connectionString.Username;
  650. _useSsl = connectionString.Ssl.GetValueOrDefault(false);
  651. _verifySslCertificate = connectionString.SslVerifyCertificate.GetValueOrDefault(true);
  652. _w = connectionString.W;
  653. if (connectionString.WaitQueueSize != null)
  654. {
  655. _waitQueueSize = connectionString.WaitQueueSize.Value;
  656. _waitQueueMultiple = 0.0;
  657. }
  658. else if (connectionString.WaitQueueMultiple != null)
  659. {
  660. _waitQueueMultiple = connectionString.WaitQueueMultiple.Value;
  661. _waitQueueSize = 0;
  662. }
  663. _waitQueueTimeout = connectionString.WaitQueueTimeout.GetValueOrDefault(MongoDefaults.WaitQueueTimeout);
  664. _wTimeout = connectionString.WTimeout;
  665. }
  666. /// <summary>
  667. /// Creates a new instance of MongoUrl based on the settings in this MongoUrlBuilder.
  668. /// </summary>
  669. /// <returns>A new instance of MongoUrl.</returns>
  670. public MongoUrl ToMongoUrl()
  671. {
  672. return MongoUrl.Create(ToString());
  673. }
  674. /// <summary>
  675. /// Returns the canonical URL based on the settings in this MongoUrlBuilder.
  676. /// </summary>
  677. /// <returns>The canonical URL.</returns>
  678. public override string ToString()
  679. {
  680. StringBuilder url = new StringBuilder();
  681. if (_scheme == ConnectionStringScheme.MongoDB)
  682. {
  683. url.Append("mongodb://");
  684. }
  685. else
  686. {
  687. url.Append("mongodb+srv://");
  688. }
  689. if (!string.IsNullOrEmpty(_username))
  690. {
  691. url.Append(Uri.EscapeDataString(_username));
  692. if (_password != null)
  693. {
  694. url.AppendFormat(":{0}", Uri.EscapeDataString(_password));
  695. }
  696. url.Append("@");
  697. }
  698. else if (_password != null)
  699. {
  700. // this would be weird and we really shouldn't be here...
  701. url.AppendFormat(":{0}@", _password);
  702. }
  703. if (_servers != null)
  704. {
  705. bool firstServer = true;
  706. foreach (MongoServerAddress server in _servers)
  707. {
  708. if (!firstServer) { url.Append(","); }
  709. if (server.Port == 27017)
  710. {
  711. url.Append(server.Host);
  712. }
  713. else
  714. {
  715. url.AppendFormat("{0}:{1}", server.Host, server.Port);
  716. }
  717. firstServer = false;
  718. }
  719. }
  720. if (_databaseName != null)
  721. {
  722. url.Append("/");
  723. url.Append(_databaseName);
  724. }
  725. var query = new StringBuilder();
  726. if (_authenticationMechanism != null)
  727. {
  728. query.AppendFormat("authMechanism={0};", _authenticationMechanism);
  729. }
  730. if (_authenticationMechanismProperties.Any())
  731. {
  732. query.AppendFormat(
  733. "authMechanismProperties={0};",
  734. string.Join(",", _authenticationMechanismProperties
  735. .Select(x => string.Format("{0}:{1}", x.Key, x.Value)).ToArray()));
  736. }
  737. if (_authenticationSource != null)
  738. {
  739. query.AppendFormat("authSource={0};", _authenticationSource);
  740. }
  741. if (_applicationName != null)
  742. {
  743. query.AppendFormat("appname={0};", _applicationName);
  744. }
  745. if (_ipv6)
  746. {
  747. query.AppendFormat("ipv6=true;");
  748. }
  749. if (_useSsl)
  750. {
  751. query.AppendFormat("ssl=true;");
  752. }
  753. if (!_verifySslCertificate)
  754. {
  755. query.AppendFormat("sslVerifyCertificate=false;");
  756. }
  757. if (_connectionMode != ConnectionMode.Automatic)
  758. {
  759. query.AppendFormat("connect={0};", MongoUtils.ToCamelCase(_connectionMode.ToString()));
  760. }
  761. if (!string.IsNullOrEmpty(_replicaSetName))
  762. {
  763. query.AppendFormat("replicaSet={0};", _replicaSetName);
  764. }
  765. if (_readConcernLevel != null)
  766. {
  767. query.AppendFormat("readConcernLevel={0};", MongoUtils.ToCamelCase(_readConcernLevel.Value.ToString()));
  768. }
  769. if (_readPreference != null)
  770. {
  771. query.AppendFormat("readPreference={0};", MongoUtils.ToCamelCase(_readPreference.ReadPreferenceMode.ToString()));
  772. if (_readPreference.TagSets != null)
  773. {
  774. foreach (var tagSet in _readPreference.TagSets)
  775. {
  776. query.AppendFormat("readPreferenceTags={0};", string.Join(",", tagSet.Tags.Select(t => string.Format("{0}:{1}", t.Name, t.Value)).ToArray()));
  777. }
  778. }
  779. if (_readPreference.MaxStaleness.HasValue)
  780. {
  781. query.AppendFormat("maxStaleness={0};", FormatTimeSpan(_readPreference.MaxStaleness.Value));
  782. }
  783. }
  784. if (_fsync != null)
  785. {
  786. query.AppendFormat("fsync={0};", JsonConvert.ToString(_fsync.Value));
  787. }
  788. if (_journal != null)
  789. {
  790. query.AppendFormat("journal={0};", JsonConvert.ToString(_journal.Value));
  791. }
  792. if (_w != null)
  793. {
  794. query.AppendFormat("w={0};", _w);
  795. }
  796. if (_wTimeout != null)
  797. {
  798. query.AppendFormat("wtimeout={0};", FormatTimeSpan(_wTimeout.Value));
  799. }
  800. if (_connectTimeout != MongoDefaults.ConnectTimeout)
  801. {
  802. query.AppendFormat("connectTimeout={0};", FormatTimeSpan(_connectTimeout));
  803. }
  804. if (_heartbeatInterval != ServerSettings.DefaultHeartbeatInterval)
  805. {
  806. query.AppendFormat("heartbeatInterval={0};", FormatTimeSpan(_heartbeatInterval));
  807. }
  808. if (_heartbeatTimeout != ServerSettings.DefaultHeartbeatTimeout)
  809. {
  810. query.AppendFormat("heartbeatTimeout={0};", FormatTimeSpan(_heartbeatTimeout));
  811. }
  812. if (_maxConnectionIdleTime != MongoDefaults.MaxConnectionIdleTime)
  813. {
  814. query.AppendFormat("maxIdleTime={0};", FormatTimeSpan(_maxConnectionIdleTime));
  815. }
  816. if (_maxConnectionLifeTime != MongoDefaults.MaxConnectionLifeTime)
  817. {
  818. query.AppendFormat("maxLifeTime={0};", FormatTimeSpan(_maxConnectionLifeTime));
  819. }
  820. if (_maxConnectionPoolSize != MongoDefaults.MaxConnectionPoolSize)
  821. {
  822. query.AppendFormat("maxPoolSize={0};", _maxConnectionPoolSize);
  823. }
  824. if (_minConnectionPoolSize != MongoDefaults.MinConnectionPoolSize)
  825. {
  826. query.AppendFormat("minPoolSize={0};", _minConnectionPoolSize);
  827. }
  828. if (_localThreshold != MongoDefaults.LocalThreshold)
  829. {
  830. query.AppendFormat("localThreshold={0};", FormatTimeSpan(_localThreshold));
  831. }
  832. if (_serverSelectionTimeout != MongoDefaults.ServerSelectionTimeout)
  833. {
  834. query.AppendFormat("serverSelectionTimeout={0};", FormatTimeSpan(_serverSelectionTimeout));
  835. }
  836. if (_socketTimeout != MongoDefaults.SocketTimeout)
  837. {
  838. query.AppendFormat("socketTimeout={0};", FormatTimeSpan(_socketTimeout));
  839. }
  840. if (_waitQueueMultiple != 0.0 && _waitQueueMultiple != MongoDefaults.WaitQueueMultiple)
  841. {
  842. query.AppendFormat("waitQueueMultiple={0};", _waitQueueMultiple);
  843. }
  844. if (_waitQueueSize != 0 && _waitQueueSize != MongoDefaults.WaitQueueSize)
  845. {
  846. query.AppendFormat("waitQueueSize={0};", _waitQueueSize);
  847. }
  848. if (_waitQueueTimeout != MongoDefaults.WaitQueueTimeout)
  849. {
  850. query.AppendFormat("waitQueueTimeout={0};", FormatTimeSpan(WaitQueueTimeout));
  851. }
  852. if (_guidRepresentation != MongoDefaults.GuidRepresentation)
  853. {
  854. query.AppendFormat("uuidRepresentation={0};", (_guidRepresentation == GuidRepresentation.CSharpLegacy) ? "csharpLegacy" : MongoUtils.ToCamelCase(_guidRepresentation.ToString()));
  855. }
  856. if (_retryWrites.GetValueOrDefault(false))
  857. {
  858. query.AppendFormat("retryWrites=true;");
  859. }
  860. if (query.Length != 0)
  861. {
  862. query.Length = query.Length - 1; // remove trailing ";"
  863. if (_databaseName == null)
  864. {
  865. url.Append("/");
  866. }
  867. url.Append("?");
  868. url.Append(query.ToString());
  869. }
  870. return url.ToString();
  871. }
  872. // private methods
  873. private bool AnyWriteConcernSettingsAreSet()
  874. {
  875. return _fsync != null || _journal != null || _w != null || _wTimeout != null;
  876. }
  877. private string FormatTimeSpan(TimeSpan value)
  878. {
  879. const int msInOneSecond = 1000; // milliseconds
  880. const int msInOneMinute = 60 * msInOneSecond;
  881. const int msInOneHour = 60 * msInOneMinute;
  882. var ms = (int)value.TotalMilliseconds;
  883. if ((ms % msInOneHour) == 0)
  884. {
  885. return string.Format("{0}h", ms / msInOneHour);
  886. }
  887. else if ((ms % msInOneMinute) == 0 && ms < msInOneHour)
  888. {
  889. return string.Format("{0}m", ms / msInOneMinute);
  890. }
  891. else if ((ms % msInOneSecond) == 0 && ms < msInOneMinute)
  892. {
  893. return string.Format("{0}s", ms / msInOneSecond);
  894. }
  895. else if (ms < 1000)
  896. {
  897. return string.Format("{0}ms", ms);
  898. }
  899. else
  900. {
  901. return value.ToString();
  902. }
  903. }
  904. }
  905. }