mongo 2.8.0 → 2.9.0.rc0

Sign up to get free protection for your applications and to get access to all the features.
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