mongo 2.8.0 → 2.9.0.rc0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (276) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/Rakefile +12 -0
  5. data/lib/mongo.rb +15 -1
  6. data/lib/mongo/address/ipv6.rb +0 -2
  7. data/lib/mongo/auth/scram/conversation.rb +0 -3
  8. data/lib/mongo/bulk_write/result_combiner.rb +12 -2
  9. data/lib/mongo/client.rb +59 -6
  10. data/lib/mongo/cluster.rb +19 -8
  11. data/lib/mongo/cluster/reapers/cursor_reaper.rb +0 -2
  12. data/lib/mongo/cluster/reapers/socket_reaper.rb +12 -9
  13. data/lib/mongo/collection.rb +1 -1
  14. data/lib/mongo/collection/view/aggregation.rb +5 -1
  15. data/lib/mongo/collection/view/builder/map_reduce.rb +1 -1
  16. data/lib/mongo/collection/view/change_stream.rb +30 -10
  17. data/lib/mongo/collection/view/iterable.rb +13 -6
  18. data/lib/mongo/collection/view/map_reduce.rb +12 -10
  19. data/lib/mongo/collection/view/readable.rb +19 -14
  20. data/lib/mongo/cursor.rb +12 -8
  21. data/lib/mongo/database.rb +10 -7
  22. data/lib/mongo/database/view.rb +18 -11
  23. data/lib/mongo/error.rb +2 -2
  24. data/lib/mongo/error/connection_check_out_timeout.rb +49 -0
  25. data/lib/mongo/error/operation_failure.rb +9 -9
  26. data/lib/mongo/error/parser.rb +25 -3
  27. data/lib/mongo/error/pool_closed_error.rb +43 -0
  28. data/lib/mongo/error/sdam_error_detection.rb +18 -0
  29. data/lib/mongo/grid/file/chunk.rb +0 -2
  30. data/lib/mongo/grid/fs_bucket.rb +26 -12
  31. data/lib/mongo/grid/stream/read.rb +36 -21
  32. data/lib/mongo/index/view.rb +11 -7
  33. data/lib/mongo/logger.rb +0 -2
  34. data/lib/mongo/monitoring.rb +31 -0
  35. data/lib/mongo/monitoring/cmap_log_subscriber.rb +53 -0
  36. data/lib/mongo/monitoring/event.rb +1 -0
  37. data/lib/mongo/monitoring/event/cmap.rb +25 -0
  38. data/lib/mongo/monitoring/event/cmap/base.rb +28 -0
  39. data/lib/mongo/monitoring/event/cmap/connection_check_out_failed.rb +78 -0
  40. data/lib/mongo/monitoring/event/cmap/connection_check_out_started.rb +56 -0
  41. data/lib/mongo/monitoring/event/cmap/connection_checked_in.rb +63 -0
  42. data/lib/mongo/monitoring/event/cmap/connection_checked_out.rb +64 -0
  43. data/lib/mongo/monitoring/event/cmap/connection_closed.rb +103 -0
  44. data/lib/mongo/monitoring/event/cmap/connection_created.rb +64 -0
  45. data/lib/mongo/monitoring/event/cmap/connection_ready.rb +64 -0
  46. data/lib/mongo/monitoring/event/cmap/pool_cleared.rb +57 -0
  47. data/lib/mongo/monitoring/event/cmap/pool_closed.rb +57 -0
  48. data/lib/mongo/monitoring/event/cmap/pool_created.rb +63 -0
  49. data/lib/mongo/monitoring/event/command_started.rb +12 -3
  50. data/lib/mongo/monitoring/publishable.rb +10 -2
  51. data/lib/mongo/operation.rb +0 -1
  52. data/lib/mongo/operation/find/legacy/result.rb +1 -0
  53. data/lib/mongo/operation/list_collections/result.rb +7 -1
  54. data/lib/mongo/operation/result.rb +10 -1
  55. data/lib/mongo/operation/shared/executable.rb +15 -0
  56. data/lib/mongo/operation/shared/result/use_legacy_error_parser.rb +29 -0
  57. data/lib/mongo/operation/shared/specifiable.rb +0 -16
  58. data/lib/mongo/operation/update/legacy/result.rb +1 -0
  59. data/lib/mongo/protocol/compressed.rb +0 -2
  60. data/lib/mongo/protocol/msg.rb +25 -2
  61. data/lib/mongo/retryable.rb +171 -33
  62. data/lib/mongo/server.rb +26 -7
  63. data/lib/mongo/server/app_metadata.rb +0 -2
  64. data/lib/mongo/server/connectable.rb +8 -2
  65. data/lib/mongo/server/connection.rb +83 -13
  66. data/lib/mongo/server/connection_base.rb +1 -1
  67. data/lib/mongo/server/connection_pool.rb +439 -43
  68. data/lib/mongo/server/monitor/connection.rb +4 -1
  69. data/lib/mongo/session.rb +37 -5
  70. data/lib/mongo/session/session_pool.rb +2 -2
  71. data/lib/mongo/socket.rb +0 -2
  72. data/lib/mongo/socket/ssl.rb +0 -2
  73. data/lib/mongo/uri.rb +127 -66
  74. data/lib/mongo/uri/srv_protocol.rb +35 -13
  75. data/lib/mongo/version.rb +1 -1
  76. data/spec/README.md +190 -63
  77. data/spec/integration/change_stream_spec.rb +64 -0
  78. data/spec/integration/command_spec.rb +0 -7
  79. data/spec/integration/error_detection_spec.rb +39 -0
  80. data/spec/integration/read_concern.rb +83 -0
  81. data/spec/integration/retryable_writes_spec.rb +6 -50
  82. data/spec/integration/sdam_error_handling_spec.rb +60 -7
  83. data/spec/integration/ssl_uri_options_spec.rb +24 -0
  84. data/spec/integration/step_down_spec.rb +197 -0
  85. data/spec/lite_spec_helper.rb +4 -0
  86. data/spec/mongo/client_construction_spec.rb +42 -17
  87. data/spec/mongo/client_spec.rb +32 -1
  88. data/spec/mongo/cluster/socket_reaper_spec.rb +2 -2
  89. data/spec/mongo/cluster_spec.rb +36 -2
  90. data/spec/mongo/collection/view/aggregation_spec.rb +2 -0
  91. data/spec/mongo/collection/view/change_stream_spec.rb +28 -28
  92. data/spec/mongo/collection/view/readable_spec.rb +1 -1
  93. data/spec/mongo/collection/view_spec.rb +3 -1
  94. data/spec/mongo/cursor_spec.rb +5 -5
  95. data/spec/mongo/error/parser_spec.rb +61 -1
  96. data/spec/mongo/grid/stream/read_spec.rb +2 -2
  97. data/spec/mongo/monitoring/event/cmap/connection_check_out_failed_spec.rb +23 -0
  98. data/spec/mongo/monitoring/event/cmap/connection_check_out_started_spec.rb +19 -0
  99. data/spec/mongo/monitoring/event/cmap/connection_checked_in_spec.rb +23 -0
  100. data/spec/mongo/monitoring/event/cmap/connection_checked_out_spec.rb +23 -0
  101. data/spec/mongo/monitoring/event/cmap/connection_closed_spec.rb +27 -0
  102. data/spec/mongo/monitoring/event/cmap/connection_created_spec.rb +24 -0
  103. data/spec/mongo/monitoring/event/cmap/connection_ready_spec.rb +24 -0
  104. data/spec/mongo/monitoring/event/cmap/pool_cleared_spec.rb +19 -0
  105. data/spec/mongo/monitoring/event/cmap/pool_closed_spec.rb +19 -0
  106. data/spec/mongo/monitoring/event/cmap/pool_created_spec.rb +26 -0
  107. data/spec/mongo/operation/delete/bulk_spec.rb +1 -6
  108. data/spec/mongo/operation/delete/command_spec.rb +1 -1
  109. data/spec/mongo/operation/delete/op_msg_spec.rb +1 -1
  110. data/spec/mongo/operation/delete_spec.rb +4 -4
  111. data/spec/mongo/operation/insert/bulk_spec.rb +1 -1
  112. data/spec/mongo/operation/insert/command_spec.rb +1 -1
  113. data/spec/mongo/operation/insert/op_msg_spec.rb +1 -1
  114. data/spec/mongo/operation/update/bulk_spec.rb +1 -1
  115. data/spec/mongo/operation/update/command_spec.rb +2 -2
  116. data/spec/mongo/operation/update/op_msg_spec.rb +2 -2
  117. data/spec/mongo/protocol/msg_spec.rb +11 -0
  118. data/spec/mongo/retryable_spec.rb +78 -25
  119. data/spec/mongo/server/connection_pool_spec.rb +661 -126
  120. data/spec/mongo/server/connection_spec.rb +55 -7
  121. data/spec/mongo/server_spec.rb +5 -0
  122. data/spec/mongo/uri/srv_protocol_spec.rb +135 -2
  123. data/spec/mongo/uri_option_parsing_spec.rb +511 -0
  124. data/spec/mongo/uri_spec.rb +42 -6
  125. data/spec/spec_helper.rb +1 -84
  126. data/spec/spec_tests/cmap_spec.rb +50 -0
  127. data/spec/spec_tests/command_monitoring_spec.rb +7 -18
  128. data/spec/spec_tests/crud_spec.rb +3 -49
  129. data/spec/spec_tests/data/cmap/connection-must-have-id.yml +21 -0
  130. data/spec/spec_tests/data/cmap/connection-must-order-ids.yml +21 -0
  131. data/spec/spec_tests/data/cmap/pool-checkin-destroy-closed.yml +24 -0
  132. data/spec/spec_tests/data/cmap/pool-checkin-destroy-stale.yml +24 -0
  133. data/spec/spec_tests/data/cmap/pool-checkin-make-available.yml +21 -0
  134. data/spec/spec_tests/data/cmap/pool-checkin.yml +18 -0
  135. data/spec/spec_tests/data/cmap/pool-checkout-connection.yml +13 -0
  136. data/spec/spec_tests/data/cmap/pool-checkout-error-closed.yml +28 -0
  137. data/spec/spec_tests/data/cmap/pool-checkout-multiple.yml +34 -0
  138. data/spec/spec_tests/data/cmap/pool-checkout-no-idle.yml +31 -0
  139. data/spec/spec_tests/data/cmap/pool-checkout-no-stale.yml +29 -0
  140. data/spec/spec_tests/data/cmap/pool-close-destroy-conns.yml +26 -0
  141. data/spec/spec_tests/data/cmap/pool-close.yml +11 -0
  142. data/spec/spec_tests/data/cmap/pool-create-max-size.yml +56 -0
  143. data/spec/spec_tests/data/cmap/pool-create-min-size.yml +27 -0
  144. data/spec/spec_tests/data/cmap/pool-create-with-options.yml +20 -0
  145. data/spec/spec_tests/data/cmap/pool-create.yml +12 -0
  146. data/spec/spec_tests/data/cmap/wait-queue-fairness.yml +94 -0
  147. data/spec/spec_tests/data/cmap/wait-queue-timeout.yml +41 -0
  148. data/spec/spec_tests/data/retryable_reads/aggregate-serverErrors.yml +157 -0
  149. data/spec/spec_tests/data/retryable_reads/aggregate.yml +87 -0
  150. data/spec/spec_tests/data/retryable_reads/changeStreams-client.watch-serverErrors.yml +149 -0
  151. data/spec/spec_tests/data/retryable_reads/changeStreams-client.watch.yml +61 -0
  152. data/spec/spec_tests/data/retryable_reads/changeStreams-db.coll.watch-serverErrors.yml +149 -0
  153. data/spec/spec_tests/data/retryable_reads/changeStreams-db.coll.watch.yml +65 -0
  154. data/spec/spec_tests/data/retryable_reads/changeStreams-db.watch-serverErrors.yml +153 -0
  155. data/spec/spec_tests/data/retryable_reads/changeStreams-db.watch.yml +61 -0
  156. data/spec/spec_tests/data/retryable_reads/count-serverErrors.yml +150 -0
  157. data/spec/spec_tests/data/retryable_reads/count.yml +64 -0
  158. data/spec/spec_tests/data/retryable_reads/countDocuments-serverErrors.yml +150 -0
  159. data/spec/spec_tests/data/retryable_reads/countDocuments.yml +64 -0
  160. data/spec/spec_tests/data/retryable_reads/distinct-serverErrors.yml +156 -0
  161. data/spec/spec_tests/data/retryable_reads/distinct.yml +71 -0
  162. data/spec/spec_tests/data/retryable_reads/estimatedDocumentCount-serverErrors.yml +148 -0
  163. data/spec/spec_tests/data/retryable_reads/estimatedDocumentCount.yml +62 -0
  164. data/spec/spec_tests/data/retryable_reads/find-serverErrors.yml +160 -0
  165. data/spec/spec_tests/data/retryable_reads/find.yml +86 -0
  166. data/spec/spec_tests/data/retryable_reads/findOne-serverErrors.yml +154 -0
  167. data/spec/spec_tests/data/retryable_reads/findOne.yml +68 -0
  168. data/spec/spec_tests/data/retryable_reads/gridfs-download-serverErrors.yml +173 -0
  169. data/spec/spec_tests/data/retryable_reads/gridfs-download.yml +79 -0
  170. data/spec/spec_tests/data/retryable_reads/gridfs-downloadByName-serverErrors.yml +174 -0
  171. data/spec/spec_tests/data/retryable_reads/gridfs-downloadByName.yml +79 -0
  172. data/spec/spec_tests/data/retryable_reads/listCollectionNames-serverErrors.yml +143 -0
  173. data/spec/spec_tests/data/retryable_reads/listCollectionNames.yml +59 -0
  174. data/spec/spec_tests/data/retryable_reads/listCollectionObjects-serverErrors.yml +144 -0
  175. data/spec/spec_tests/data/retryable_reads/listCollectionObjects.yml +59 -0
  176. data/spec/spec_tests/data/retryable_reads/listCollections-serverErrors.yml +143 -0
  177. data/spec/spec_tests/data/retryable_reads/listCollections.yml +59 -0
  178. data/spec/spec_tests/data/retryable_reads/listDatabaseNames-serverErrors.yml +143 -0
  179. data/spec/spec_tests/data/retryable_reads/listDatabaseNames.yml +59 -0
  180. data/spec/spec_tests/data/retryable_reads/listDatabaseObjects-serverErrors.yml +144 -0
  181. data/spec/spec_tests/data/retryable_reads/listDatabaseObjects.yml +59 -0
  182. data/spec/spec_tests/data/retryable_reads/listDatabases-serverErrors.yml +144 -0
  183. data/spec/spec_tests/data/retryable_reads/listDatabases.yml +59 -0
  184. data/spec/spec_tests/data/retryable_reads/listIndexNames-serverErrors.yml +144 -0
  185. data/spec/spec_tests/data/retryable_reads/listIndexNames.yml +60 -0
  186. data/spec/spec_tests/data/retryable_reads/listIndexes-serverErrors.yml +145 -0
  187. data/spec/spec_tests/data/retryable_reads/listIndexes.yml +60 -0
  188. data/spec/spec_tests/data/retryable_reads/mapReduce.yml +60 -0
  189. data/spec/spec_tests/data/retryable_writes/bulkWrite-serverErrors.yml +10 -7
  190. data/spec/spec_tests/data/retryable_writes/bulkWrite.yml +15 -22
  191. data/spec/spec_tests/data/retryable_writes/deleteMany.yml +22 -0
  192. data/spec/spec_tests/data/retryable_writes/deleteOne-serverErrors.yml +8 -7
  193. data/spec/spec_tests/data/retryable_writes/deleteOne.yml +5 -8
  194. data/spec/spec_tests/data/retryable_writes/findOneAndDelete-serverErrors.yml +8 -7
  195. data/spec/spec_tests/data/retryable_writes/findOneAndDelete.yml +5 -8
  196. data/spec/spec_tests/data/retryable_writes/findOneAndReplace-serverErrors.yml +8 -7
  197. data/spec/spec_tests/data/retryable_writes/findOneAndReplace.yml +5 -8
  198. data/spec/spec_tests/data/retryable_writes/findOneAndUpdate-serverErrors.yml +8 -7
  199. data/spec/spec_tests/data/retryable_writes/findOneAndUpdate.yml +5 -8
  200. data/spec/spec_tests/data/retryable_writes/insertMany-serverErrors.yml +8 -7
  201. data/spec/spec_tests/data/retryable_writes/insertMany.yml +5 -8
  202. data/spec/spec_tests/data/retryable_writes/insertOne-serverErrors.yml +10 -45
  203. data/spec/spec_tests/data/retryable_writes/insertOne.yml +5 -8
  204. data/spec/spec_tests/data/retryable_writes/replaceOne-serverErrors.yml +8 -7
  205. data/spec/spec_tests/data/retryable_writes/replaceOne.yml +5 -8
  206. data/spec/spec_tests/data/retryable_writes/updateMany.yml +27 -0
  207. data/spec/spec_tests/data/retryable_writes/updateOne-serverErrors.yml +8 -7
  208. data/spec/spec_tests/data/retryable_writes/updateOne.yml +5 -14
  209. data/spec/spec_tests/data/transactions/abort.yml +7 -2
  210. data/spec/spec_tests/data/transactions/bulk.yml +7 -2
  211. data/spec/spec_tests/data/transactions/causal-consistency.yml +11 -4
  212. data/spec/spec_tests/data/transactions/commit.yml +11 -4
  213. data/spec/spec_tests/data/transactions/count.yml +64 -0
  214. data/spec/spec_tests/data/transactions/delete.yml +7 -2
  215. data/spec/spec_tests/data/transactions/error-labels.yml +8 -2
  216. data/spec/spec_tests/data/transactions/errors.yml +7 -2
  217. data/spec/spec_tests/data/transactions/findOneAndDelete.yml +7 -2
  218. data/spec/spec_tests/data/transactions/findOneAndReplace.yml +7 -2
  219. data/spec/spec_tests/data/transactions/findOneAndUpdate.yml +7 -2
  220. data/spec/spec_tests/data/transactions/insert.yml +9 -2
  221. data/spec/spec_tests/data/transactions/isolation.yml +7 -2
  222. data/spec/spec_tests/data/transactions/read-concern.yml +15 -6
  223. data/spec/spec_tests/data/transactions/read-pref.yml +7 -2
  224. data/spec/spec_tests/data/transactions/reads.yml +8 -48
  225. data/spec/spec_tests/data/transactions/retryable-abort.yml +7 -2
  226. data/spec/spec_tests/data/transactions/retryable-commit.yml +7 -2
  227. data/spec/spec_tests/data/transactions/retryable-writes.yml +7 -2
  228. data/spec/spec_tests/data/transactions/run-command.yml +7 -2
  229. data/spec/spec_tests/data/transactions/transaction-options.yml +7 -2
  230. data/spec/spec_tests/data/transactions/update.yml +7 -2
  231. data/spec/spec_tests/data/transactions/write-concern.yml +7 -2
  232. data/spec/spec_tests/data/transactions_api/callback-aborts.yml +6 -1
  233. data/spec/spec_tests/data/transactions_api/callback-commits.yml +6 -1
  234. data/spec/spec_tests/data/transactions_api/callback-retry.yml +6 -1
  235. data/spec/spec_tests/data/transactions_api/commit-retry.yml +6 -1
  236. data/spec/spec_tests/data/transactions_api/commit-transienttransactionerror-4.2.yml +6 -3
  237. data/spec/spec_tests/data/transactions_api/commit-transienttransactionerror.yml +6 -1
  238. data/spec/spec_tests/data/transactions_api/commit-writeconcernerror.yml +6 -1
  239. data/spec/spec_tests/data/transactions_api/commit.yml +6 -1
  240. data/spec/spec_tests/data/transactions_api/transaction-options.yml +6 -1
  241. data/spec/spec_tests/retryable_reads_spec.rb +11 -0
  242. data/spec/spec_tests/retryable_writes_spec.rb +4 -69
  243. data/spec/spec_tests/transactions_api_spec.rb +42 -37
  244. data/spec/spec_tests/transactions_spec.rb +42 -33
  245. data/spec/support/authorization.rb +12 -0
  246. data/spec/support/change_streams/operation.rb +1 -1
  247. data/spec/support/client_registry.rb +20 -0
  248. data/spec/support/cluster_config.rb +16 -15
  249. data/spec/support/cluster_tools.rb +346 -0
  250. data/spec/support/cmap.rb +367 -0
  251. data/spec/support/cmap/verifier.rb +46 -0
  252. data/spec/support/command_monitoring.rb +4 -6
  253. data/spec/support/common_shortcuts.rb +6 -0
  254. data/spec/support/connection_string.rb +2 -2
  255. data/spec/support/crud.rb +171 -184
  256. data/spec/support/crud/operation.rb +43 -0
  257. data/spec/support/crud/outcome.rb +53 -0
  258. data/spec/support/crud/read.rb +102 -12
  259. data/spec/support/crud/requirement.rb +69 -0
  260. data/spec/support/crud/spec.rb +68 -0
  261. data/spec/support/crud/test.rb +141 -0
  262. data/spec/support/crud/verifier.rb +96 -18
  263. data/spec/support/crud/write.rb +18 -3
  264. data/spec/support/event_subscriber.rb +15 -0
  265. data/spec/support/primary_socket.rb +2 -2
  266. data/spec/support/spec_config.rb +89 -20
  267. data/spec/support/transactions.rb +2 -306
  268. data/spec/support/transactions/operation.rb +7 -7
  269. data/spec/support/transactions/spec.rb +28 -0
  270. data/spec/support/transactions/test.rb +191 -0
  271. data/spec/support/utils.rb +123 -0
  272. metadata +202 -9
  273. metadata.gz.sig +0 -0
  274. data/lib/mongo/server/connection_pool/queue.rb +0 -359
  275. data/spec/mongo/server/connection_pool/queue_spec.rb +0 -353
  276. data/spec/support/transactions/verifier.rb +0 -97
@@ -179,10 +179,13 @@ module Mongo
179
179
  # @note This method mutates the connection by setting the socket to nil
180
180
  # if the closing succeeded.
181
181
  #
182
+ # @note This method accepts an options argument for compatibility with
183
+ # Server::Connections. However, all options are ignored.
184
+ #
182
185
  # @return [ true ] If the disconnect succeeded.
183
186
  #
184
187
  # @since 2.0.0
185
- def disconnect!
188
+ def disconnect!(options = nil)
186
189
  if socket
187
190
  socket.close
188
191
  @socket = nil
@@ -443,6 +443,26 @@ module Mongo
443
443
  end
444
444
  end
445
445
 
446
+ # Whether reads executed with this session can be retried according to
447
+ # the modern retryable reads specification.
448
+ #
449
+ # If this method returns true, the modern retryable reads have been
450
+ # requested by the application. If the server selected for a read operation
451
+ # supports modern retryable reads, they will be used for that particular
452
+ # operation. If the server selected for a read operation does not support
453
+ # modern retryable reads, the read will not be retried.
454
+ #
455
+ # If this method returns false, legacy retryable reads have been requested
456
+ # by the application. Legacy retryable read logic will be used regardless
457
+ # of server version of the server(s) that the client is connected to.
458
+ # The number of read retries is given by :max_read_retries client option,
459
+ # which is 1 by default and can be set to 0 to disable legacy read retries.
460
+ #
461
+ # @api private
462
+ def retry_reads?
463
+ client.options[:retry_reads] != false
464
+ end
465
+
446
466
  # Will writes executed with this session be retried.
447
467
  #
448
468
  # @example Will writes be retried.
@@ -874,6 +894,23 @@ module Mongo
874
894
  @client.cluster
875
895
  end
876
896
 
897
+ protected
898
+
899
+ # Get the read concern the session will use when starting a transaction.
900
+ #
901
+ # This is a driver style hash with underscore keys.
902
+ #
903
+ # @example Get the session's transaction read concern.
904
+ # session.txn_read_concern
905
+ #
906
+ # @return [ Hash ] The read concern used for starting transactions.
907
+ #
908
+ # @since 2.9.0
909
+ def txn_read_concern
910
+ # Read concern is inherited from client but not db or collection.
911
+ txn_options && txn_options[:read_concern] || @client.read_concern
912
+ end
913
+
877
914
  private
878
915
 
879
916
  def within_states?(*states)
@@ -891,11 +928,6 @@ module Mongo
891
928
  Mongo::Error::InvalidTransactionOperation::NO_TRANSACTION_STARTED)
892
929
  end
893
930
 
894
- def txn_read_concern
895
- # Read concern is inherited from client but not db or collection.
896
- txn_options && txn_options[:read_concern] || @client.read_concern
897
- end
898
-
899
931
  def txn_write_concern
900
932
  (txn_options && txn_options[:write_concern]) ||
901
933
  (@client.write_concern && @client.write_concern.options)
@@ -64,9 +64,9 @@ module Mongo
64
64
  "#<Mongo::Session::SessionPool:0x#{object_id} current_size=#{@queue.size}>"
65
65
  end
66
66
 
67
- # Checkout a server session from the pool.
67
+ # Check out a server session from the pool.
68
68
  #
69
- # @example Checkout a session.
69
+ # @example Check out a session.
70
70
  # pool.checkout
71
71
  #
72
72
  # @return [ ServerSession ] The server session.
@@ -12,8 +12,6 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- require 'socket'
16
- require 'timeout'
17
15
  require 'mongo/socket/ssl'
18
16
  require 'mongo/socket/tcp'
19
17
  require 'mongo/socket/unix'
@@ -12,8 +12,6 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- require 'openssl'
16
-
17
15
  module Mongo
18
16
  class Socket
19
17
 
@@ -12,8 +12,6 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- require 'uri'
16
-
17
15
  module Mongo
18
16
 
19
17
  # The URI class provides a way for users to parse the MongoDB uri as
@@ -260,6 +258,9 @@ module Mongo
260
258
  @options = options
261
259
  parsed_scheme, _, remaining = string.partition(SCHEME_DELIM)
262
260
  raise_invalid_error!(INVALID_SCHEME) unless parsed_scheme == scheme
261
+ if remaining.empty?
262
+ raise_invalid_error!('No hosts in the URI')
263
+ end
263
264
  parse!(remaining)
264
265
 
265
266
  # The URI options spec requires that we raise an error if there are conflicting values of
@@ -283,6 +284,12 @@ module Mongo
283
284
  raise_invalid_error_no_fmt!("tlsInsecure' and 'tlsAllowInvalidHostnames' cannot both be specified")
284
285
  end
285
286
  end
287
+
288
+ # Since we know that the only URI option that sets :ssl_cert is "tlsCertificateKeyFile", any
289
+ # value set for :ssl_cert must also be set for :ssl_key.
290
+ if @uri_options[:ssl_cert]
291
+ @uri_options[:ssl_key] = @uri_options[:ssl_cert]
292
+ end
286
293
  end
287
294
 
288
295
  # Get the credentials provided in the URI.
@@ -317,17 +324,38 @@ module Mongo
317
324
  MONGODB_SCHEME
318
325
  end
319
326
 
320
- def parse_creds_hosts!(string)
321
- hosts, creds = split_creds_hosts(string)
327
+ def parse!(remaining)
328
+ hosts_and_db, options = remaining.split('?', 2)
329
+ if options && options.index('?')
330
+ raise_invalid_error!("Options contain an unescaped question mark (?), or the database name contains a question mark and was not escaped")
331
+ end
332
+
333
+ if options && !hosts_and_db.index('/')
334
+ raise_invalid_error!("MongoDB URI must have a slash (/) after the hosts if options are given")
335
+ end
336
+
337
+ hosts, db = hosts_and_db.split('/', 2)
338
+ if db && db.index('/')
339
+ raise_invalid_error!("Database name contains an unescaped slash (/): #{db}")
340
+ end
341
+
342
+ if hosts.index('@')
343
+ creds, hosts = hosts.split('@', 2)
344
+ if hosts.empty?
345
+ raise_invalid_error!("Empty hosts list")
346
+ end
347
+ if hosts.index('@')
348
+ raise_invalid_error!("Unescaped @ in auth info")
349
+ end
350
+ end
351
+
322
352
  @servers = parse_servers!(hosts)
323
353
  @user = parse_user!(creds)
324
354
  @password = parse_password!(creds)
325
- end
326
-
327
- def parse!(remaining)
328
- creds_hosts, db_opts = extract_db_opts!(remaining)
329
- parse_creds_hosts!(creds_hosts)
330
- parse_db_opts!(db_opts)
355
+ @uri_options = Options::Redacted.new(parse_uri_options!(options))
356
+ if db
357
+ @database = parse_database!(db)
358
+ end
331
359
  end
332
360
 
333
361
  def extract_db_opts!(string)
@@ -339,28 +367,23 @@ module Mongo
339
367
  [ creds_hosts, db_opts ].map { |s| s.reverse }
340
368
  end
341
369
 
342
- def split_creds_hosts(string)
343
- hosts, _, creds = string.reverse.partition(AUTH_DELIM)
344
- hosts, creds = creds, hosts if hosts.empty?
345
- [ hosts, creds ].map { |s| s.reverse }
346
- end
347
-
348
- def parse_db_opts!(string)
349
- auth_db, _, uri_opts = string.partition(URI_OPTS_DELIM)
350
- @uri_options = Options::Redacted.new(parse_uri_options!(uri_opts))
351
- @database = parse_database!(auth_db)
352
- end
353
-
354
370
  def parse_uri_options!(string)
355
371
  return {} unless string
356
372
  string.split(INDIV_URI_OPTS_DELIM).reduce({}) do |uri_options, opt|
357
- raise_invalid_error!(INVALID_OPTS_VALUE_DELIM) unless opt.index(URI_OPTS_VALUE_DELIM)
358
- key, value = opt.split(URI_OPTS_VALUE_DELIM)
373
+ key, value = opt.split('=', 2)
374
+ if value.nil?
375
+ raise_invalid_error!("Option #{key} has no value")
376
+ end
377
+ if value.index('=')
378
+ raise_invalid_error!("Value for option #{key} contains the key/value delimiter (=): #{value}")
379
+ end
380
+ key = ::URI.decode(key)
359
381
  strategy = URI_OPTION_MAP[key.downcase]
360
382
  if strategy.nil?
361
383
  log_warn("Unsupported URI option '#{key}' on URI '#{@string}'. It will be ignored.")
362
384
  else
363
- add_uri_option(strategy, value, uri_options)
385
+ value = ::URI.decode(value)
386
+ add_uri_option(key, strategy, value, uri_options)
364
387
  end
365
388
  uri_options
366
389
  end
@@ -465,10 +488,10 @@ module Mongo
465
488
  uri_option 'maxidletimems', :max_idle_time, :type => :max_idle_time
466
489
 
467
490
  # Write Options
468
- uri_option 'w', :w, :group => :write
491
+ uri_option 'w', :w, :group => :write, type: :w
469
492
  uri_option 'journal', :j, :group => :write, :type => :journal
470
- uri_option 'fsync', :fsync, :group => :write
471
- uri_option 'wtimeoutms', :timeout, :group => :write, :type => :wtimeout
493
+ uri_option 'fsync', :fsync, :group => :write, type: :bool
494
+ uri_option 'wtimeoutms', :wtimeout, :group => :write, :type => :wtimeout
472
495
 
473
496
  # Read Options
474
497
  uri_option 'readpreference', :mode, :group => :read, :type => :read_mode
@@ -493,7 +516,7 @@ module Mongo
493
516
  uri_option 'tlsinsecure', :ssl_verify, :type => :ssl_verify
494
517
 
495
518
  # Topology options
496
- uri_option 'connect', :connect
519
+ uri_option 'connect', :connect, type: :symbol
497
520
 
498
521
  # Auth Options
499
522
  uri_option 'authsource', :auth_source, :type => :auth_source
@@ -503,38 +526,25 @@ module Mongo
503
526
  # Client Options
504
527
  uri_option 'appname', :app_name
505
528
  uri_option 'compressors', :compressors, :type => :array
506
- uri_option 'readconcernlevel', :read_concern
529
+ uri_option 'readconcernlevel', :level, group: :read_concern
507
530
  uri_option 'retrywrites', :retry_writes, :type => :retry_writes
508
531
  uri_option 'zlibcompressionlevel', :zlib_compression_level, :type => :zlib_compression_level
509
532
 
510
- # Casts option values that do not have a specifically provided
511
- # transformation to the appropriate type.
512
- #
513
- # @param value [String] The value to be cast.
514
- #
515
- # @return [true, false, Fixnum, Symbol] The cast value.
516
- def cast(value)
517
- if value == 'true'
518
- true
519
- elsif value == 'false'
520
- false
521
- elsif value =~ /\A[\d]\z/
522
- value.to_i
523
- else
524
- decode(value).to_sym
525
- end
526
- end
527
-
528
533
  # Applies URI value transformation by either using the default cast
529
534
  # or a transformation appropriate for the given type.
530
535
  #
536
+ # @param key [String] URI option name.
531
537
  # @param value [String] The value to be transformed.
532
538
  # @param type [Symbol] The transform method.
533
- def apply_transform(value, type = nil)
539
+ def apply_transform(key, value, type)
534
540
  if type
535
- send(type, value)
541
+ if respond_to?("convert_#{type}", true)
542
+ send("convert_#{type}", key, value)
543
+ else
544
+ send(type, value)
545
+ end
536
546
  else
537
- cast(value)
547
+ value
538
548
  end
539
549
  end
540
550
 
@@ -581,12 +591,13 @@ module Mongo
581
591
  # Transforms the value.
582
592
  # Merges the option into the target.
583
593
  #
594
+ # @param key [String] URI option name.
584
595
  # @param strategy [Symbol] The strategy for this option.
585
596
  # @param value [String] The value of the option.
586
597
  # @param uri_options [Hash] The base option target.
587
- def add_uri_option(strategy, value, uri_options)
598
+ def add_uri_option(key, strategy, value, uri_options)
588
599
  target = select_target(uri_options, strategy[:group])
589
- value = apply_transform(value, strategy[:type])
600
+ value = apply_transform(key, value, strategy[:type])
590
601
  merge_uri_option(target, value, strategy[:name])
591
602
  end
592
603
 
@@ -656,7 +667,7 @@ module Mongo
656
667
  properties = hash_extractor('authMechanismProperties', value)
657
668
  if properties[:canonicalize_host_name]
658
669
  properties.merge!(canonicalize_host_name:
659
- properties[:canonicalize_host_name] == 'true')
670
+ %w(true TRUE).include?(properties[:canonicalize_host_name]))
660
671
  end
661
672
  properties
662
673
  end
@@ -718,7 +729,7 @@ module Mongo
718
729
  # @return [ true | false | nil ] The journal value parsed out, otherwise nil (and a warning
719
730
  # will be logged).
720
731
  def journal(value)
721
- bool('journal', value)
732
+ convert_bool('journal', value)
722
733
  end
723
734
 
724
735
  # Parses the ssl value from the URI.
@@ -728,7 +739,7 @@ module Mongo
728
739
  # @return [ Array<true | false> ] The ssl value parsed out (stored in an array to facilitate
729
740
  # keeping track of all values).
730
741
  def ssl(value)
731
- [bool('ssl', value)]
742
+ [convert_bool('ssl', value)]
732
743
  end
733
744
 
734
745
  # Parses the tls value from the URI.
@@ -738,7 +749,7 @@ module Mongo
738
749
  # @return [ Array<true | false> ] The tls value parsed out (stored in an array to facilitate
739
750
  # keeping track of all values).
740
751
  def tls(value)
741
- [bool('tls', value)]
752
+ [convert_bool('tls', value)]
742
753
  end
743
754
 
744
755
  # Parses the ssl_verify value from the tlsInsecure URI value. Note that this will be the inverse
@@ -781,20 +792,70 @@ module Mongo
781
792
  # @return [ true | false | nil ] The boolean value parsed out, otherwise nil (and a warning
782
793
  # will be logged).
783
794
  def retry_writes(value)
784
- bool('retryWrites', value)
795
+ convert_bool('retryWrites', value)
785
796
  end
786
797
 
787
- # Parses a boolean value.
798
+ # Converts +value+ into an integer.
788
799
  #
789
- # @param value [ String ] The URI option value.
800
+ # If the value is not a valid integer, warns and returns nil.
790
801
  #
791
- # @return [ true | false | nil ] The boolean value parsed out, otherwise nil (and a warning
792
- # will be logged).
793
- def bool(name, value)
802
+ # @param name [ String ] Name of the URI option being processed.
803
+ # @param value [ String ] URI option value.
804
+ #
805
+ # @return [ nil | Integer ] Converted value.
806
+ def convert_integer(name, value)
807
+ unless /\A\d+\z/ =~ value
808
+ log_warn("#{value} is not a valid integer for #{name}")
809
+ return nil
810
+ end
811
+
812
+ value.to_i
813
+ end
814
+
815
+ # Converts +value+ into a symbol.
816
+ #
817
+ # @param name [ String ] Name of the URI option being processed.
818
+ # @param value [ String ] URI option value.
819
+ #
820
+ # @return [ Symbol ] Converted value.
821
+ def convert_symbol(name, value)
822
+ value.to_sym
823
+ end
824
+
825
+ # Converts +value+ as a write concern.
826
+ #
827
+ # If +value+ is the word "majority", returns the symbol :majority.
828
+ # If +value+ is a number, returns the number as an integer.
829
+ # Otherwise returns the string +value+ unchanged.
830
+ #
831
+ # @param name [ String ] Name of the URI option being processed.
832
+ # @param value [ String ] URI option value.
833
+ #
834
+ # @return [ Integer | Symbol | String ] Converted value.
835
+ def convert_w(name, value)
836
+ case value
837
+ when 'majority'
838
+ :majority
839
+ when /\A[0-9]+\z/
840
+ value.to_i
841
+ else
842
+ value
843
+ end
844
+ end
845
+
846
+ # Converts +value+ to a boolean.
847
+ #
848
+ # Returns true for 'true', false for 'false', otherwise nil.
849
+ #
850
+ # @param name [ String ] Name of the URI option being processed.
851
+ # @param value [ String ] URI option value.
852
+ #
853
+ # @return [ true | false | nil ] Converted value.
854
+ def convert_bool(name, value)
794
855
  case value
795
- when "true"
856
+ when "true", 'TRUE'
796
857
  true
797
- when "false"
858
+ when "false", 'FALSE'
798
859
  false
799
860
  else
800
861
  log_warn("invalid boolean option for #{name}: #{value}")
@@ -809,7 +870,7 @@ module Mongo
809
870
  # @return [ true | false | nil ] The inverse of the boolean value parsed out, otherwise nil
810
871
  # (and a warning will be logged).
811
872
  def inverse_bool(name, value)
812
- b = bool(name, value)
873
+ b = convert_bool(name, value)
813
874
 
814
875
  if b.nil?
815
876
  nil
@@ -12,8 +12,6 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- require 'resolv'
16
-
17
15
  module Mongo
18
16
 
19
17
  class URI
@@ -65,6 +63,7 @@ module Mongo
65
63
  INVALID_PORT = "It is not allowed to specify a port in a connection string with the " +
66
64
  "'#{MONGODB_SRV_SCHEME}' protocol.".freeze
67
65
 
66
+ # @deprecated
68
67
  INVALID_DOMAIN = "The domain name must consist of at least two parts: the domain name, " +
69
68
  "and a TLD.".freeze
70
69
 
@@ -93,22 +92,45 @@ module Mongo
93
92
  @resolver ||= Resolv::DNS.new
94
93
  end
95
94
 
96
- def parse_creds_hosts!(string)
97
- hostname, creds = split_creds_hosts(string)
98
- validate_hostname!(hostname)
95
+ def parse!(remaining)
96
+ super
97
+
98
+ if @servers.length != 1
99
+ raise_invalid_error!(INVALID_HOST)
100
+ end
101
+ hostname = @servers.first
102
+ validate_hostname(hostname)
103
+
99
104
  records = get_records(hostname)
100
105
  @txt_options = get_txt_opts(hostname) || {}
101
106
  @servers = parse_servers!(records.join(','))
102
- @user = parse_user!(creds)
103
- @password = parse_password!(creds)
104
107
  end
105
108
 
106
- def validate_hostname!(hostname)
107
- raise_invalid_error!(INVALID_HOST) if hostname.empty?
108
- raise_invalid_error!(INVALID_HOST) if hostname.include?(HOST_DELIM)
109
+ # Validates the hostname used in an SRV URI.
110
+ #
111
+ # The hostname cannot include a port.
112
+ #
113
+ # The hostname must not begin with a dot, end with a dot, or have
114
+ # consecutive dots. The hostname must have a minimum of 3 total
115
+ # components (foo.bar.tld).
116
+ #
117
+ # Raises Error::InvalidURI if validation fails.
118
+ def validate_hostname(hostname)
109
119
  raise_invalid_error!(INVALID_PORT) if hostname.include?(HOST_PORT_DELIM)
110
- _, _, domain = hostname.partition(DOT_PARTITION)
111
- raise_invalid_error!(INVALID_DOMAIN) unless domain.include?(DOT_PARTITION)
120
+
121
+ if hostname.start_with?('.')
122
+ raise_invalid_error!("Hostname cannot start with a dot: #{hostname}")
123
+ end
124
+ if hostname.end_with?('.')
125
+ raise_invalid_error!("Hostname cannot end with a dot: #{hostname}")
126
+ end
127
+ parts = hostname.split('.')
128
+ if parts.any?(&:empty?)
129
+ raise_invalid_error!("Hostname cannot have consecutive dots: #{hostname}")
130
+ end
131
+ if parts.length < 3
132
+ raise_invalid_error!("Hostname must have a minimum of 3 components (foo.bar.tld): #{hostname}")
133
+ end
112
134
  end
113
135
 
114
136
  def get_records(hostname)
@@ -149,7 +171,7 @@ module Mongo
149
171
  key, value = opt.split(URI_OPTS_VALUE_DELIM)
150
172
  raise Error::InvalidTXTRecord.new(INVALID_TXT_RECORD_OPTION) unless VALID_TXT_OPTIONS.include?(key.downcase)
151
173
  strategy = URI_OPTION_MAP[key.downcase]
152
- add_uri_option(strategy, value, txt_options)
174
+ add_uri_option(key, strategy, value, txt_options)
153
175
  txt_options
154
176
  end
155
177
  end