mongo 2.11.4 → 2.12.3

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 (357) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/CONTRIBUTING.md +1 -1
  5. data/README.md +2 -1
  6. data/lib/mongo.rb +3 -0
  7. data/lib/mongo/address.rb +44 -19
  8. data/lib/mongo/auth.rb +1 -0
  9. data/lib/mongo/auth/credential_cache.rb +51 -0
  10. data/lib/mongo/auth/scram/conversation.rb +20 -16
  11. data/lib/mongo/auth/user.rb +0 -8
  12. data/lib/mongo/auth/user/view.rb +4 -4
  13. data/lib/mongo/background_thread.rb +1 -1
  14. data/lib/mongo/bulk_write.rb +5 -5
  15. data/lib/mongo/client.rb +143 -14
  16. data/lib/mongo/client_encryption.rb +103 -0
  17. data/lib/mongo/cluster.rb +8 -4
  18. data/lib/mongo/cluster/reapers/cursor_reaper.rb +18 -6
  19. data/lib/mongo/cluster/sdam_flow.rb +54 -58
  20. data/lib/mongo/collection.rb +3 -3
  21. data/lib/mongo/collection/view.rb +1 -1
  22. data/lib/mongo/collection/view/aggregation.rb +1 -1
  23. data/lib/mongo/collection/view/change_stream.rb +12 -3
  24. data/lib/mongo/collection/view/iterable.rb +14 -5
  25. data/lib/mongo/collection/view/map_reduce.rb +2 -2
  26. data/lib/mongo/collection/view/readable.rb +9 -7
  27. data/lib/mongo/collection/view/writable.rb +7 -7
  28. data/lib/mongo/crypt.rb +33 -0
  29. data/lib/mongo/crypt/auto_decryption_context.rb +40 -0
  30. data/lib/mongo/crypt/auto_encrypter.rb +179 -0
  31. data/lib/mongo/crypt/auto_encryption_context.rb +44 -0
  32. data/lib/mongo/crypt/binary.rb +155 -0
  33. data/lib/mongo/crypt/binding.rb +1229 -0
  34. data/lib/mongo/crypt/context.rb +135 -0
  35. data/lib/mongo/crypt/data_key_context.rb +162 -0
  36. data/lib/mongo/crypt/encryption_io.rb +289 -0
  37. data/lib/mongo/crypt/explicit_decryption_context.rb +40 -0
  38. data/lib/mongo/crypt/explicit_encrypter.rb +117 -0
  39. data/lib/mongo/crypt/explicit_encryption_context.rb +89 -0
  40. data/lib/mongo/crypt/handle.rb +315 -0
  41. data/lib/mongo/crypt/hooks.rb +90 -0
  42. data/lib/mongo/crypt/kms_context.rb +67 -0
  43. data/lib/mongo/crypt/status.rb +131 -0
  44. data/lib/mongo/cursor.rb +64 -32
  45. data/lib/mongo/database.rb +23 -6
  46. data/lib/mongo/database/view.rb +13 -4
  47. data/lib/mongo/dbref.rb +9 -2
  48. data/lib/mongo/error.rb +5 -1
  49. data/lib/mongo/error/bulk_write_error.rb +16 -14
  50. data/lib/mongo/error/crypt_error.rb +31 -0
  51. data/lib/mongo/error/{failed_stringprep_validation.rb → failed_string_prep_validation.rb} +0 -0
  52. data/lib/mongo/error/invalid_cursor_operation.rb +27 -0
  53. data/lib/mongo/error/kms_error.rb +22 -0
  54. data/lib/mongo/error/max_bson_size.rb +14 -3
  55. data/lib/mongo/error/mongocryptd_spawn_error.rb +22 -0
  56. data/lib/mongo/error/no_server_available.rb +8 -3
  57. data/lib/mongo/error/notable.rb +0 -15
  58. data/lib/mongo/error/operation_failure.rb +1 -0
  59. data/lib/mongo/error/parser.rb +1 -1
  60. data/lib/mongo/grid/file.rb +5 -0
  61. data/lib/mongo/grid/file/chunk.rb +2 -0
  62. data/lib/mongo/grid/file/info.rb +3 -2
  63. data/lib/mongo/grid/fs_bucket.rb +15 -13
  64. data/lib/mongo/grid/stream/write.rb +9 -3
  65. data/lib/mongo/index/view.rb +3 -3
  66. data/lib/mongo/monitoring/event/cmap/connection_check_out_failed.rb +1 -1
  67. data/lib/mongo/monitoring/event/command_started.rb +6 -1
  68. data/lib/mongo/operation/collections_info.rb +6 -3
  69. data/lib/mongo/operation/delete/op_msg.rb +1 -1
  70. data/lib/mongo/operation/find/op_msg.rb +4 -1
  71. data/lib/mongo/operation/get_more/op_msg.rb +4 -1
  72. data/lib/mongo/operation/insert/command.rb +3 -2
  73. data/lib/mongo/operation/insert/legacy.rb +3 -2
  74. data/lib/mongo/operation/insert/op_msg.rb +3 -3
  75. data/lib/mongo/operation/result.rb +36 -27
  76. data/lib/mongo/operation/shared/executable.rb +11 -9
  77. data/lib/mongo/operation/shared/executable_no_validate.rb +2 -2
  78. data/lib/mongo/operation/shared/op_msg_or_command.rb +2 -2
  79. data/lib/mongo/operation/shared/op_msg_or_find_command.rb +2 -2
  80. data/lib/mongo/operation/shared/op_msg_or_list_indexes_command.rb +2 -2
  81. data/lib/mongo/operation/shared/read_preference_supported.rb +68 -19
  82. data/lib/mongo/operation/shared/response_handling.rb +1 -1
  83. data/lib/mongo/operation/shared/sessions_supported.rb +44 -3
  84. data/lib/mongo/operation/shared/write.rb +17 -10
  85. data/lib/mongo/operation/update/op_msg.rb +1 -1
  86. data/lib/mongo/protocol/bit_vector.rb +2 -1
  87. data/lib/mongo/protocol/compressed.rb +6 -5
  88. data/lib/mongo/protocol/insert.rb +3 -1
  89. data/lib/mongo/protocol/message.rb +94 -15
  90. data/lib/mongo/protocol/msg.rb +207 -37
  91. data/lib/mongo/protocol/query.rb +7 -9
  92. data/lib/mongo/protocol/serializers.rb +43 -15
  93. data/lib/mongo/retryable.rb +1 -1
  94. data/lib/mongo/server.rb +10 -4
  95. data/lib/mongo/server/connection.rb +20 -9
  96. data/lib/mongo/server/connection_base.rb +118 -18
  97. data/lib/mongo/server/connection_common.rb +61 -0
  98. data/lib/mongo/server/connection_pool.rb +37 -1
  99. data/lib/mongo/server/connection_pool/populator.rb +1 -1
  100. data/lib/mongo/server/description.rb +9 -11
  101. data/lib/mongo/server/monitor.rb +2 -0
  102. data/lib/mongo/server/monitor/connection.rb +3 -18
  103. data/lib/mongo/server/pending_connection.rb +2 -1
  104. data/lib/mongo/session.rb +3 -3
  105. data/lib/mongo/session/session_pool.rb +8 -3
  106. data/lib/mongo/socket.rb +29 -16
  107. data/lib/mongo/socket/ssl.rb +23 -8
  108. data/lib/mongo/socket/tcp.rb +12 -3
  109. data/lib/mongo/srv/monitor.rb +73 -42
  110. data/lib/mongo/srv/result.rb +0 -1
  111. data/lib/mongo/timeout.rb +49 -0
  112. data/lib/mongo/uri.rb +30 -1
  113. data/lib/mongo/uri/srv_protocol.rb +1 -1
  114. data/lib/mongo/version.rb +1 -1
  115. data/mongo.gemspec +1 -3
  116. data/spec/README.md +228 -7
  117. data/spec/integration/auth_spec.rb +53 -0
  118. data/spec/integration/bulk_write_spec.rb +19 -0
  119. data/spec/integration/{client_options_spec.rb → client_authentication_options_spec.rb} +10 -10
  120. data/spec/integration/client_construction_spec.rb +100 -1
  121. data/spec/integration/client_side_encryption/auto_encryption_bulk_writes_spec.rb +353 -0
  122. data/spec/integration/client_side_encryption/auto_encryption_command_monitoring_spec.rb +303 -0
  123. data/spec/integration/client_side_encryption/auto_encryption_mongocryptd_spawn_spec.rb +72 -0
  124. data/spec/integration/client_side_encryption/auto_encryption_old_wire_version_spec.rb +79 -0
  125. data/spec/integration/client_side_encryption/auto_encryption_reconnect_spec.rb +221 -0
  126. data/spec/integration/client_side_encryption/auto_encryption_spec.rb +601 -0
  127. data/spec/integration/client_side_encryption/bson_size_limit_spec.rb +187 -0
  128. data/spec/integration/client_side_encryption/bypass_mongocryptd_spawn_spec.rb +78 -0
  129. data/spec/integration/client_side_encryption/client_close_spec.rb +63 -0
  130. data/spec/integration/client_side_encryption/corpus_spec.rb +233 -0
  131. data/spec/integration/client_side_encryption/custom_endpoint_spec.rb +132 -0
  132. data/spec/integration/client_side_encryption/data_key_spec.rb +165 -0
  133. data/spec/integration/client_side_encryption/explicit_encryption_spec.rb +114 -0
  134. data/spec/integration/client_side_encryption/external_key_vault_spec.rb +141 -0
  135. data/spec/integration/client_side_encryption/views_spec.rb +44 -0
  136. data/spec/integration/client_update_spec.rb +154 -0
  137. data/spec/integration/command_monitoring_spec.rb +3 -1
  138. data/spec/integration/command_spec.rb +44 -10
  139. data/spec/integration/connection_spec.rb +57 -0
  140. data/spec/integration/crud_spec.rb +89 -0
  141. data/spec/integration/grid_fs_bucket_spec.rb +48 -0
  142. data/spec/integration/read_preference_spec.rb +26 -0
  143. data/spec/integration/reconnect_spec.rb +7 -6
  144. data/spec/integration/size_limit_spec.rb +111 -0
  145. data/spec/integration/srv_monitoring_spec.rb +16 -8
  146. data/spec/integration/zlib_compression_spec.rb +25 -0
  147. data/spec/kerberos/kerberos_spec.rb +87 -0
  148. data/spec/lite_spec_helper.rb +34 -29
  149. data/spec/mongo/auth/cr_spec.rb +8 -0
  150. data/spec/mongo/auth/ldap_spec.rb +5 -1
  151. data/spec/mongo/auth/scram/conversation_spec.rb +5 -6
  152. data/spec/mongo/auth/scram/negotiation_spec.rb +74 -75
  153. data/spec/mongo/auth/scram_spec.rb +45 -35
  154. data/spec/mongo/auth/user/view_spec.rb +3 -6
  155. data/spec/mongo/auth/x509_spec.rb +5 -1
  156. data/spec/mongo/bulk_write/result_spec.rb +11 -7
  157. data/spec/mongo/client_construction_spec.rb +206 -2
  158. data/spec/mongo/client_encryption_spec.rb +405 -0
  159. data/spec/mongo/cluster/cursor_reaper_spec.rb +12 -8
  160. data/spec/mongo/cluster/socket_reaper_spec.rb +14 -3
  161. data/spec/mongo/collection/view/aggregation_spec.rb +0 -2
  162. data/spec/mongo/collection/view/change_stream_spec.rb +7 -7
  163. data/spec/mongo/collection/view/map_reduce_spec.rb +3 -3
  164. data/spec/mongo/collection/view_spec.rb +1 -1
  165. data/spec/mongo/collection_spec.rb +28 -9
  166. data/spec/mongo/crypt/auto_decryption_context_spec.rb +90 -0
  167. data/spec/mongo/crypt/auto_encrypter_spec.rb +187 -0
  168. data/spec/mongo/crypt/auto_encryption_context_spec.rb +107 -0
  169. data/spec/mongo/crypt/binary_spec.rb +115 -0
  170. data/spec/mongo/crypt/binding/binary_spec.rb +56 -0
  171. data/spec/mongo/crypt/binding/context_spec.rb +257 -0
  172. data/spec/mongo/crypt/binding/helpers_spec.rb +46 -0
  173. data/spec/mongo/crypt/binding/mongocrypt_spec.rb +144 -0
  174. data/spec/mongo/crypt/binding/status_spec.rb +99 -0
  175. data/spec/mongo/crypt/binding/version_spec.rb +22 -0
  176. data/spec/mongo/crypt/binding_unloaded_spec.rb +20 -0
  177. data/spec/mongo/crypt/data_key_context_spec.rb +213 -0
  178. data/spec/mongo/crypt/encryption_io_spec.rb +136 -0
  179. data/spec/mongo/crypt/explicit_decryption_context_spec.rb +72 -0
  180. data/spec/mongo/crypt/explicit_encryption_context_spec.rb +170 -0
  181. data/spec/mongo/crypt/handle_spec.rb +232 -0
  182. data/spec/mongo/crypt/helpers/mongo_crypt_spec_helper.rb +108 -0
  183. data/spec/mongo/crypt/status_spec.rb +152 -0
  184. data/spec/mongo/cursor_spec.rb +24 -4
  185. data/spec/mongo/database_spec.rb +20 -0
  186. data/spec/mongo/error/bulk_write_error_spec.rb +49 -0
  187. data/spec/mongo/error/crypt_error_spec.rb +26 -0
  188. data/spec/mongo/error/max_bson_size_spec.rb +35 -0
  189. data/spec/mongo/error/no_server_available_spec.rb +11 -1
  190. data/spec/mongo/error/notable_spec.rb +59 -0
  191. data/spec/mongo/error/operation_failure_spec.rb +6 -6
  192. data/spec/mongo/operation/aggregate_spec.rb +1 -1
  193. data/spec/mongo/operation/collections_info_spec.rb +1 -1
  194. data/spec/mongo/operation/command_spec.rb +3 -3
  195. data/spec/mongo/operation/create_index_spec.rb +3 -3
  196. data/spec/mongo/operation/create_user_spec.rb +3 -3
  197. data/spec/mongo/operation/delete/bulk_spec.rb +6 -6
  198. data/spec/mongo/operation/delete/op_msg_spec.rb +1 -6
  199. data/spec/mongo/operation/delete_spec.rb +7 -7
  200. data/spec/mongo/operation/drop_index_spec.rb +2 -2
  201. data/spec/mongo/operation/find/legacy_spec.rb +2 -1
  202. data/spec/mongo/operation/get_more_spec.rb +1 -1
  203. data/spec/mongo/operation/indexes_spec.rb +1 -1
  204. data/spec/mongo/operation/insert/bulk_spec.rb +7 -7
  205. data/spec/mongo/operation/insert/op_msg_spec.rb +3 -6
  206. data/spec/mongo/operation/insert_spec.rb +12 -12
  207. data/spec/mongo/operation/map_reduce_spec.rb +2 -2
  208. data/spec/mongo/operation/read_preference_legacy_spec.rb +351 -0
  209. data/spec/mongo/operation/read_preference_op_msg_spec.rb +194 -0
  210. data/spec/mongo/operation/remove_user_spec.rb +3 -3
  211. data/spec/mongo/operation/update/bulk_spec.rb +6 -6
  212. data/spec/mongo/operation/update/op_msg_spec.rb +3 -6
  213. data/spec/mongo/operation/update_spec.rb +7 -7
  214. data/spec/mongo/operation/update_user_spec.rb +1 -1
  215. data/spec/mongo/protocol/compressed_spec.rb +2 -3
  216. data/spec/mongo/protocol/delete_spec.rb +9 -8
  217. data/spec/mongo/protocol/get_more_spec.rb +9 -8
  218. data/spec/mongo/protocol/insert_spec.rb +9 -8
  219. data/spec/mongo/protocol/kill_cursors_spec.rb +6 -5
  220. data/spec/mongo/protocol/msg_spec.rb +57 -53
  221. data/spec/mongo/protocol/query_spec.rb +12 -12
  222. data/spec/mongo/protocol/registry_spec.rb +1 -1
  223. data/spec/mongo/protocol/reply_spec.rb +1 -1
  224. data/spec/mongo/protocol/update_spec.rb +10 -9
  225. data/spec/mongo/server/connection_pool_spec.rb +1 -1
  226. data/spec/mongo/server/connection_spec.rb +28 -7
  227. data/spec/mongo/socket_spec.rb +1 -1
  228. data/spec/mongo/srv/monitor_spec.rb +88 -69
  229. data/spec/mongo/timeout_spec.rb +85 -0
  230. data/spec/mongo/uri/srv_protocol_spec.rb +2 -2
  231. data/spec/mongo/uri_spec.rb +52 -5
  232. data/spec/mongo/write_concern_spec.rb +13 -1
  233. data/spec/{support → runners}/auth.rb +14 -1
  234. data/spec/{support → runners}/change_streams.rb +1 -1
  235. data/spec/{support → runners}/change_streams/operation.rb +0 -0
  236. data/spec/{support → runners}/cmap.rb +1 -1
  237. data/spec/{support → runners}/cmap/verifier.rb +0 -0
  238. data/spec/{support → runners}/command_monitoring.rb +0 -0
  239. data/spec/runners/connection_string.rb +358 -4
  240. data/spec/{support → runners}/crud.rb +9 -9
  241. data/spec/{support → runners}/crud/context.rb +0 -0
  242. data/spec/{support → runners}/crud/operation.rb +7 -3
  243. data/spec/{support → runners}/crud/outcome.rb +0 -0
  244. data/spec/{support → runners}/crud/requirement.rb +1 -1
  245. data/spec/{support → runners}/crud/spec.rb +12 -1
  246. data/spec/{support → runners}/crud/test.rb +0 -0
  247. data/spec/{support → runners}/crud/test_base.rb +0 -0
  248. data/spec/{support → runners}/crud/verifier.rb +10 -12
  249. data/spec/{support → runners}/gridfs.rb +0 -0
  250. data/spec/{support → runners}/sdam_monitoring.rb +0 -0
  251. data/spec/{support → runners}/server_discovery_and_monitoring.rb +0 -0
  252. data/spec/{support → runners}/server_selection.rb +0 -0
  253. data/spec/{support → runners}/server_selection_rtt.rb +0 -0
  254. data/spec/{support → runners}/transactions.rb +9 -11
  255. data/spec/{support → runners}/transactions/context.rb +0 -0
  256. data/spec/{support → runners}/transactions/operation.rb +0 -0
  257. data/spec/{support → runners}/transactions/spec.rb +0 -0
  258. data/spec/{support → runners}/transactions/test.rb +37 -5
  259. data/spec/spec_helper.rb +0 -5
  260. data/spec/spec_tests/auth_spec.rb +3 -3
  261. data/spec/spec_tests/client_side_encryption_spec.rb +8 -0
  262. data/spec/spec_tests/connection_string_spec.rb +1 -1
  263. data/spec/spec_tests/data/auth/connection-string.yml +13 -0
  264. data/spec/spec_tests/data/client_side_encryption/aggregate.yml +134 -0
  265. data/spec/spec_tests/data/client_side_encryption/badQueries.yml +526 -0
  266. data/spec/spec_tests/data/client_side_encryption/badSchema.yml +73 -0
  267. data/spec/spec_tests/data/client_side_encryption/basic.yml +116 -0
  268. data/spec/spec_tests/data/client_side_encryption/bulk.yml +88 -0
  269. data/spec/spec_tests/data/client_side_encryption/bypassAutoEncryption.yml +100 -0
  270. data/spec/spec_tests/data/client_side_encryption/bypassedCommand.yml +42 -0
  271. data/spec/spec_tests/data/client_side_encryption/count.yml +61 -0
  272. data/spec/spec_tests/data/client_side_encryption/countDocuments.yml +59 -0
  273. data/spec/spec_tests/data/client_side_encryption/delete.yml +105 -0
  274. data/spec/spec_tests/data/client_side_encryption/distinct.yml +73 -0
  275. data/spec/spec_tests/data/client_side_encryption/explain.yml +64 -0
  276. data/spec/spec_tests/data/client_side_encryption/find.yml +119 -0
  277. data/spec/spec_tests/data/client_side_encryption/findOneAndDelete.yml +57 -0
  278. data/spec/spec_tests/data/client_side_encryption/findOneAndReplace.yml +57 -0
  279. data/spec/spec_tests/data/client_side_encryption/findOneAndUpdate.yml +57 -0
  280. data/spec/spec_tests/data/client_side_encryption/getMore.yml +68 -0
  281. data/spec/spec_tests/data/client_side_encryption/insert.yml +102 -0
  282. data/spec/spec_tests/data/client_side_encryption/keyAltName.yml +71 -0
  283. data/spec/spec_tests/data/client_side_encryption/localKMS.yml +54 -0
  284. data/spec/spec_tests/data/client_side_encryption/localSchema.yml +72 -0
  285. data/spec/spec_tests/data/client_side_encryption/malformedCiphertext.yml +69 -0
  286. data/spec/spec_tests/data/client_side_encryption/maxWireVersion.yml +20 -0
  287. data/spec/spec_tests/data/client_side_encryption/missingKey.yml +49 -0
  288. data/spec/spec_tests/data/client_side_encryption/replaceOne.yml +64 -0
  289. data/spec/spec_tests/data/client_side_encryption/types.yml +527 -0
  290. data/spec/spec_tests/data/client_side_encryption/unsupportedCommand.yml +25 -0
  291. data/spec/spec_tests/data/client_side_encryption/updateMany.yml +77 -0
  292. data/spec/spec_tests/data/client_side_encryption/updateOne.yml +171 -0
  293. data/spec/spec_tests/data/read_write_concern/connection-string/write-concern.yml +1 -4
  294. data/spec/spec_tests/data/retryable_writes/insertOne-serverErrors.yml +21 -0
  295. data/spec/spec_tests/data/sdam/rs/incompatible_ghost.yml +2 -4
  296. data/spec/spec_tests/data/sdam/rs/incompatible_other.yml +1 -1
  297. data/spec/spec_tests/data/sdam/rs/primary_mismatched_me_not_removed.yml +73 -0
  298. data/spec/spec_tests/data/sdam/rs/primary_to_no_primary_mismatched_me.yml +1 -2
  299. data/spec/spec_tests/data/sdam/rs/repeated.yml +101 -0
  300. data/spec/spec_tests/data/sdam/rs/{primary_address_change.yml → ruby_primary_address_change.yml} +2 -0
  301. data/spec/spec_tests/data/sdam/rs/{secondary_wrong_set_name_with_primary_second.yml → ruby_secondary_wrong_set_name_with_primary_second.yml} +0 -0
  302. data/spec/spec_tests/data/sdam/sharded/ruby_discovered_single_mongos.yml +27 -0
  303. data/spec/spec_tests/data/sdam/sharded/{primary_address_change.yml → ruby_primary_different_address.yml} +1 -1
  304. data/spec/spec_tests/data/sdam/sharded/{primary_mismatched_me.yml → ruby_primary_mismatched_me.yml} +1 -1
  305. data/spec/spec_tests/data/sdam/single/{primary_address_change.yml → ruby_primary_different_address.yml} +1 -1
  306. data/spec/spec_tests/data/sdam/single/{primary_mismatched_me.yml → ruby_primary_mismatched_me.yml} +1 -1
  307. data/spec/spec_tests/data/sdam_monitoring/{replica_set_with_primary_change.yml → replica_set_primary_address_change.yml} +27 -5
  308. data/spec/spec_tests/data/sdam_monitoring/replica_set_with_me_mismatch.yml +26 -74
  309. data/spec/spec_tests/data/sdam_monitoring/replica_set_with_removal.yml +20 -16
  310. data/spec/spec_tests/data/sdam_monitoring/standalone_suppress_equal_description_changes.yml +73 -0
  311. data/spec/spec_tests/data/transactions/pin-mongos.yml +2 -3
  312. data/spec/spec_tests/data/uri_options/auth-options.yml +10 -0
  313. data/spec/spec_tests/data/uri_options/tls-options.yml +75 -4
  314. data/spec/spec_tests/read_write_concern_connection_string_spec.rb +1 -1
  315. data/spec/spec_tests/uri_options_spec.rb +6 -8
  316. data/spec/stress/connection_pool_timing_spec.rb +6 -3
  317. data/spec/support/certificates/README.md +4 -0
  318. data/spec/support/certificates/server-second-level-bundle.pem +77 -77
  319. data/spec/support/certificates/server-second-level.crt +52 -52
  320. data/spec/support/certificates/server-second-level.key +25 -25
  321. data/spec/support/certificates/server-second-level.pem +77 -77
  322. data/spec/support/client_registry.rb +19 -3
  323. data/spec/support/cluster_config.rb +9 -1
  324. data/spec/support/cluster_tools.rb +6 -1
  325. data/spec/support/common_shortcuts.rb +12 -0
  326. data/spec/support/constraints.rb +16 -0
  327. data/spec/support/crypt.rb +154 -0
  328. data/spec/support/crypt/corpus/corpus-key-aws.json +33 -0
  329. data/spec/support/crypt/corpus/corpus-key-local.json +31 -0
  330. data/spec/support/crypt/corpus/corpus-schema.json +2057 -0
  331. data/spec/support/crypt/corpus/corpus.json +3657 -0
  332. data/spec/support/crypt/corpus/corpus_encrypted.json +4152 -0
  333. data/spec/support/crypt/data_keys/key_document_aws.json +34 -0
  334. data/spec/support/crypt/data_keys/key_document_local.json +31 -0
  335. data/spec/support/crypt/external/external-key.json +31 -0
  336. data/spec/support/crypt/external/external-schema.json +19 -0
  337. data/spec/support/crypt/limits/limits-doc.json +102 -0
  338. data/spec/support/crypt/limits/limits-key.json +31 -0
  339. data/spec/support/crypt/limits/limits-schema.json +1405 -0
  340. data/spec/support/crypt/schema_maps/schema_map_aws.json +17 -0
  341. data/spec/support/crypt/schema_maps/schema_map_aws_key_alt_names.json +12 -0
  342. data/spec/support/crypt/schema_maps/schema_map_local.json +18 -0
  343. data/spec/support/crypt/schema_maps/schema_map_local_key_alt_names.json +12 -0
  344. data/spec/support/lite_constraints.rb +19 -1
  345. data/spec/support/matchers.rb +19 -0
  346. data/spec/support/shared/protocol.rb +2 -0
  347. data/spec/support/spec_config.rb +53 -13
  348. data/spec/support/utils.rb +140 -10
  349. metadata +894 -687
  350. metadata.gz.sig +0 -0
  351. data/lib/mongo/cluster/srv_monitor.rb +0 -127
  352. data/lib/mongo/srv/warning_result.rb +0 -35
  353. data/spec/enterprise_auth/kerberos_spec.rb +0 -58
  354. data/spec/mongo/cluster/srv_monitor_spec.rb +0 -214
  355. data/spec/mongo/operation/read_preference_spec.rb +0 -245
  356. data/spec/spec_tests/data/sdam/sharded/single_mongos.yml +0 -33
  357. data/spec/support/connection_string.rb +0 -354
@@ -35,7 +35,7 @@ module Mongo
35
35
  # moved to the end of the hash for better logging.
36
36
  #
37
37
  # @api private
38
- INTERNAL_KEYS = Set.new(%w($clusterTime lsid signature txnNumber)).freeze
38
+ INTERNAL_KEYS = Set.new(%w($clusterTime $db lsid signature txnNumber)).freeze
39
39
 
40
40
  # Creates a new OP_MSG protocol message
41
41
  #
@@ -44,25 +44,42 @@ module Mongo
44
44
  # { type: 1, payload: { identifier: 'documents', sequence: [..] } })
45
45
  #
46
46
  # @param [ Array<Symbol> ] flags The flag bits. Current supported values
47
- # are :more_to_come and :checksum_present.
48
- # @param [ Hash ] options The options. There are currently no supported
49
- # options, this is a placeholder for the future.
50
- # @param [ BSON::Document, Hash ] global_args The global arguments,
51
- # becomes a section of payload type 0.
52
- # @param [ BSON::Document, Hash ] sections Zero or more sections, in the format
53
- # { type: 1, payload: { identifier: <String>, sequence: <Array<BSON::Document, Hash>> } } or
54
- # { type: 0, payload: <BSON::Document, Hash> }
47
+ # are :more_to_come and :checksum_present.
48
+ # @param [ Hash ] options The options.
49
+ # @param [ BSON::Document, Hash ] main_document The document that will
50
+ # become the payload type 0 section. Can contain global args as they
51
+ # are defined in the OP_MSG specification.
52
+ # @param [ Protocol::Msg::Section1 ] sequences Zero or more payload type 1
53
+ # sections.
55
54
  #
56
- # @option options [ true, false ] validating_keys Whether keys should be validated.
55
+ # @option options [ true, false ] validating_keys Whether keys should be
56
+ # validated for being valid document keys (i.e. not begin with $ and
57
+ # not contain dots).
57
58
  #
58
59
  # @api private
59
60
  #
60
61
  # @since 2.5.0
61
- def initialize(flags, options, global_args, *sections)
62
+ def initialize(flags, options, main_document, *sequences)
62
63
  @flags = flags || []
63
64
  @options = options
64
- @global_args = global_args
65
- @sections = [ { type: 0, payload: global_args } ] + sections
65
+ unless main_document.is_a?(Hash)
66
+ raise ArgumentError, "Main document must be a Hash, given: #{main_document.class}"
67
+ end
68
+ @main_document = main_document
69
+ sequences.each_with_index do |section, index|
70
+ unless section.is_a?(Section1)
71
+ raise ArgumentError, "All sequences must be Section1 instances, got: #{section} at index #{index}"
72
+ end
73
+ end
74
+ @sequences = sequences
75
+ @sections = [
76
+ {type: 0, payload: @main_document}
77
+ ] + @sequences.map do |section|
78
+ {type: 1, payload: {
79
+ identifier: section.identifier,
80
+ sequence: section.documents,
81
+ }}
82
+ end
66
83
  @request_id = nil
67
84
  super
68
85
  end
@@ -88,11 +105,11 @@ module Mongo
88
105
  #
89
106
  # @since 2.5.0
90
107
  def payload
91
- # Reorder keys in global_args for better logging - see
108
+ # Reorder keys in main_document for better logging - see
92
109
  # https://jira.mongodb.org/browse/RUBY-1591.
93
110
  # Note that even without the reordering, the payload is not an exact
94
111
  # match to what is sent over the wire because the command as used in
95
- # the published eent combines keys from multiple sections of the
112
+ # the published event combines keys from multiple sections of the
96
113
  # payload sent over the wire.
97
114
  ordered_command = {}
98
115
  skipped_command = {}
@@ -107,10 +124,10 @@ module Mongo
107
124
 
108
125
  BSON::Document.new(
109
126
  command_name: ordered_command.keys.first.to_s,
110
- database_name: global_args[DATABASE_IDENTIFIER],
127
+ database_name: @main_document[DATABASE_IDENTIFIER],
111
128
  command: ordered_command,
112
129
  request_id: request_id,
113
- reply: sections[0]
130
+ reply: @main_document,
114
131
  )
115
132
  end
116
133
 
@@ -123,40 +140,163 @@ module Mongo
123
140
  #
124
141
  # @since 2.5.0
125
142
  def serialize(buffer = BSON::ByteBuffer.new, max_bson_size = nil)
143
+ validate_document_size!(max_bson_size)
144
+
126
145
  super
127
146
  add_check_sum(buffer)
128
147
  buffer
129
148
  end
130
149
 
131
- # Compress this message.
150
+ # Compress the message, if the command being sent permits compression.
151
+ # Otherwise returns self.
132
152
  #
133
153
  # @param [ String, Symbol ] compressor The compressor to use.
134
154
  # @param [ Integer ] zlib_compression_level The zlib compression level to use.
135
155
  #
136
- # @return [ Compressed, self ] A Protocol::Compressed message or self, depending on whether
137
- # this message can be compressed.
156
+ # @return [ Message ] A Protocol::Compressed message or self,
157
+ # depending on whether this message can be compressed.
138
158
  #
139
159
  # @since 2.5.0
140
- def compress!(compressor, zlib_compression_level = nil)
141
- if compressor && compression_allowed?(command.keys.first)
142
- Compressed.new(self, compressor, zlib_compression_level)
160
+ # @api private
161
+ def maybe_compress(compressor, zlib_compression_level = nil)
162
+ compress_if_possible(command.keys.first, compressor, zlib_compression_level)
163
+ end
164
+
165
+ # Reverse-populates the instance variables after deserialization sets
166
+ # the @sections instance variable to the list of documents.
167
+ #
168
+ # TODO fix deserialization so that this method is not needed.
169
+ #
170
+ # @api private
171
+ def fix_after_deserialization
172
+ if @sections.nil?
173
+ raise NotImplementedError, "After deserializations @sections should have been initialized"
174
+ end
175
+ if @sections.length != 1
176
+ raise NotImplementedError, "Deserialization must have produced exactly one section, but it produced #{sections.length} sections"
177
+ end
178
+ @main_document = @sections.first
179
+ @sequences = []
180
+ @sections = [{type: 0, payload: @main_document}]
181
+ end
182
+
183
+ def documents
184
+ [@main_document]
185
+ end
186
+
187
+ # Possibly encrypt this message with libmongocrypt. Message will only be
188
+ # encrypted if the specified client exists, that client has been given
189
+ # auto-encryption options, the client has not been instructed to bypass
190
+ # auto-encryption, and mongocryptd determines that this message is
191
+ # eligible for encryption. A message is eligible for encryption if it
192
+ # represents one of the command types whitelisted by libmongocrypt and it
193
+ # contains data that is required to be encrypted by a local or remote json schema.
194
+ #
195
+ # @param [ Mongo::Client | nil ] client The client used to make the original
196
+ # command. This client may have auto-encryption options specified.
197
+ #
198
+ # @return [ Mongo::Protocol::Msg ] The encrypted message, or the original
199
+ # message if encryption was not possible or necessary.
200
+ def maybe_encrypt(server, client)
201
+ # TODO verify compression happens later, i.e. when this method runs
202
+ # the message is not compressed.
203
+ if client && client.encrypter && client.encrypter.encrypt?
204
+ if server.max_wire_version < 8
205
+ raise Error::CryptError.new(
206
+ "Cannot perform encryption against a MongoDB server older than " +
207
+ "4.2 (wire version less than 8). Currently connected to server " +
208
+ "with max wire version #{server.max_wire_version}} " +
209
+ "(Auto-encryption requires a minimum MongoDB version of 4.2)"
210
+ )
211
+ end
212
+
213
+ db_name = @main_document[DATABASE_IDENTIFIER]
214
+ cmd = merge_sections
215
+ enc_cmd = client.encrypter.encrypt(db_name, cmd)
216
+ if cmd.key?('$db') && !enc_cmd.key?('$db')
217
+ enc_cmd['$db'] = cmd['$db']
218
+ end
219
+
220
+ Msg.new(@flags, @options, enc_cmd)
221
+ else
222
+ self
223
+ end
224
+ end
225
+
226
+ # Possibly decrypt this message with libmongocrypt. Message will only be
227
+ # decrypted if the specified client exists, that client has been given
228
+ # auto-encryption options, and this message is eligible for decryption.
229
+ # A message is eligible for decryption if it represents one of the command
230
+ # types whitelisted by libmongocrypt and it contains data that is required
231
+ # to be encrypted by a local or remote json schema.
232
+ #
233
+ # @param [ Mongo::Client | nil ] client The client used to make the original
234
+ # command. This client may have auto-encryption options specified.
235
+ #
236
+ # @return [ Mongo::Protocol::Msg ] The decryption message, or the original
237
+ # message if decryption was not possible or necessary.
238
+ def maybe_decrypt(client)
239
+ if client && client.encrypter
240
+ cmd = merge_sections
241
+ enc_cmd = client.encrypter.decrypt(cmd)
242
+ Msg.new(@flags, @options, enc_cmd)
143
243
  else
144
244
  self
145
245
  end
146
246
  end
147
247
 
248
+ # Whether this message represents a bulk write. A bulk write is an insert,
249
+ # update, or delete operation that encompasses multiple operations of
250
+ # the same type.
251
+ #
252
+ # @return [ Boolean ] Whether this message represents a bulk write.
253
+ #
254
+ # @note This method was written to support client-side encryption
255
+ # functionality. It is not recommended that this method be used in
256
+ # service of any other feature or behavior.
257
+ #
258
+ # @api private
259
+ def bulk_write?
260
+ inserts = @main_document['documents']
261
+ updates = @main_document['updates']
262
+ deletes = @main_document['deletes']
263
+
264
+ num_inserts = inserts && inserts.length || 0
265
+ num_updates = updates && updates.length || 0
266
+ num_deletes = deletes && deletes.length || 0
267
+
268
+ num_inserts > 1 || num_updates > 1 || num_deletes > 1
269
+ end
270
+
148
271
  private
149
272
 
273
+ # Validate that the documents in this message are all smaller than the
274
+ # maxBsonObjectSize. If not, raise an exception.
275
+ def validate_document_size!(max_bson_size)
276
+ max_bson_size ||= Mongo::Server::ConnectionBase::DEFAULT_MAX_BSON_OBJECT_SIZE
277
+
278
+ contains_too_large_document = @sections.any? do |section|
279
+ section[:type] == 1 &&
280
+ section[:payload][:sequence].any? do |document|
281
+ document.to_bson.length > max_bson_size
282
+ end
283
+ end
284
+
285
+ if contains_too_large_document
286
+ raise Error::MaxBSONSize.new('The document exceeds maximum allowed BSON object size after serialization')
287
+ end
288
+ end
289
+
150
290
  def command
151
- @command ||= global_args.dup.tap do |cmd|
152
- cmd.delete(DATABASE_IDENTIFIER)
153
- sections.each do |section|
154
- if section[:type] == 1
155
- identifier = section[:payload][:identifier]
156
- cmd[identifier] ||= []
157
- cmd[identifier] += section[:payload][:sequence]
291
+ @command ||= if @main_document
292
+ @main_document.dup.tap do |cmd|
293
+ @sequences.each do |section|
294
+ cmd[section.identifier] ||= []
295
+ cmd[section.identifier] += section.documents
158
296
  end
159
297
  end
298
+ else
299
+ documents.first
160
300
  end
161
301
  end
162
302
 
@@ -166,8 +306,24 @@ module Mongo
166
306
  end
167
307
  end
168
308
 
169
- def global_args
170
- @global_args ||= (sections[0] || {})
309
+ # Encapsulates a type 1 OP_MSG section.
310
+ #
311
+ # @see https://github.com/mongodb/specifications/blob/master/source/message/OP_MSG.rst#sections
312
+ #
313
+ # @api private
314
+ class Section1
315
+ def initialize(identifier, documents)
316
+ @identifier, @documents = identifier, documents
317
+ end
318
+
319
+ attr_reader :identifier, :documents
320
+
321
+ def ==(other)
322
+ other.is_a?(Section1) &&
323
+ identifier == other.identifier && documents == other.documents
324
+ end
325
+
326
+ alias :eql? :==
171
327
  end
172
328
 
173
329
  # The operation code required to specify a OP_MSG message.
@@ -177,19 +333,33 @@ module Mongo
177
333
  OP_CODE = 2013
178
334
 
179
335
  # Available flags for a OP_MSG message.
180
- FLAGS = Array.new(16).tap { |arr|
336
+ FLAGS = Array.new(16).tap do |arr|
181
337
  arr[0] = :checksum_present
182
338
  arr[1] = :more_to_come
183
- }
339
+ end.freeze
184
340
 
185
341
  # @!attribute
186
342
  # @return [Array<Symbol>] The flags for this message.
187
343
  field :flags, BitVector.new(FLAGS)
188
344
 
189
- # @!attribute
190
- # @return [Hash] The sections of payload type 1 or 0.
345
+ # The sections that will be serialized, or the documents have been
346
+ # deserialized.
347
+ #
348
+ # Usually the sections contain OP_MSG-compliant sections derived
349
+ # from @main_document and @sequences. The information in @main_document
350
+ # and @sequences is duplicated in the sections.
351
+ #
352
+ # When deserializing Msg instances, sections temporarily is an array
353
+ # of documents returned in the type 0 section of the OP_MSG wire
354
+ # protocol message. #fix_after_deserialization method mutates this
355
+ # object to have sections, @main_document and @sequences be what
356
+ # they would have been had the Msg instance been constructed using
357
+ # the constructor (rather than having been deserialized).
358
+ #
359
+ # @return [ Array<Hash> | Array<BSON::Document> ] The sections of
360
+ # payload type 1 or 0.
361
+ # @api private
191
362
  field :sections, Sections
192
- alias :documents :sections
193
363
 
194
364
  Registry.register(OP_CODE, self)
195
365
  end
@@ -100,21 +100,19 @@ module Mongo
100
100
  true
101
101
  end
102
102
 
103
- # Compress this message.
103
+ # Compress the message, if the command being sent permits compression.
104
+ # Otherwise returns self.
104
105
  #
105
106
  # @param [ String, Symbol ] compressor The compressor to use.
106
107
  # @param [ Integer ] zlib_compression_level The zlib compression level to use.
107
108
  #
108
- # @return [ Compressed, self ] A Protocol::Compressed message or self, depending on whether
109
- # this message can be compressed.
109
+ # @return [ Message ] A Protocol::Compressed message or self,
110
+ # depending on whether this message can be compressed.
110
111
  #
111
112
  # @since 2.5.0
112
- def compress!(compressor, zlib_compression_level = nil)
113
- if compressor && compression_allowed?(selector.keys.first)
114
- Compressed.new(self, compressor, zlib_compression_level)
115
- else
116
- self
117
- end
113
+ # @api private
114
+ def maybe_compress(compressor, zlib_compression_level = nil)
115
+ compress_if_possible(selector.keys.first, compressor, zlib_compression_level)
118
116
  end
119
117
 
120
118
  protected
@@ -57,10 +57,11 @@ module Mongo
57
57
  # Deserializes the header value from the IO stream
58
58
  #
59
59
  # @param [ String ] buffer Buffer containing the message header.
60
+ # @param [ Hash ] options This method currently accepts no options.
60
61
  #
61
62
  # @return [ Array<Fixnum> ] Array consisting of the deserialized
62
63
  # length, request id, response id, and op code.
63
- def self.deserialize(buffer)
64
+ def self.deserialize(buffer, options = {})
64
65
  buffer.get_bytes(16).unpack(HEADER_PACK)
65
66
  end
66
67
  end
@@ -123,9 +124,10 @@ module Mongo
123
124
  # Deserializes a 32-bit Fixnum from the IO stream
124
125
  #
125
126
  # @param [ String ] buffer Buffer containing the 32-bit integer
127
+ # @param [ Hash ] options This method currently accepts no options.
126
128
  #
127
129
  # @return [ Fixnum ] Deserialized Int32
128
- def self.deserialize(buffer)
130
+ def self.deserialize(buffer, options = {})
129
131
  buffer.get_int32
130
132
  end
131
133
  end
@@ -156,9 +158,10 @@ module Mongo
156
158
  # Deserializes a 64-bit Fixnum from the IO stream
157
159
  #
158
160
  # @param [ String ] buffer Buffer containing the 64-bit integer.
161
+ # @param [ Hash ] options This method currently accepts no options.
159
162
  #
160
163
  # @return [Fixnum] Deserialized Int64.
161
- def self.deserialize(buffer)
164
+ def self.deserialize(buffer, options = {})
162
165
  buffer.get_int64
163
166
  end
164
167
  end
@@ -198,19 +201,24 @@ module Mongo
198
201
  # Deserializes a section of an OP_MSG from the IO stream.
199
202
  #
200
203
  # @param [ BSON::ByteBuffer ] buffer Buffer containing the sections.
204
+ # @param [ Hash ] options
205
+ #
206
+ # @option options [ Boolean ] :deserialize_as_bson Whether to perform
207
+ # section deserialization using BSON types instead of native Ruby types
208
+ # wherever possible.
201
209
  #
202
210
  # @return [ Array<BSON::Document> ] Deserialized sections.
203
211
  #
204
212
  # @since 2.5.0
205
- def self.deserialize(buffer)
213
+ def self.deserialize(buffer, options = {})
206
214
  end_length = (@flag_bits & Msg::FLAGS.index(:checksum_present)) == 1 ? 32 : 0
207
215
  sections = []
208
216
  until buffer.length == end_length
209
217
  case byte = buffer.get_byte
210
218
  when PayloadZero::TYPE_BYTE
211
- sections << PayloadZero.deserialize(buffer)
219
+ sections << PayloadZero.deserialize(buffer, options)
212
220
  when PayloadOne::TYPE_BYTE
213
- sections += PayloadOne.deserialize(buffer)
221
+ sections += PayloadOne.deserialize(buffer, options)
214
222
  else
215
223
  raise Error::UnknownPayloadType.new(byte)
216
224
  end
@@ -260,12 +268,18 @@ module Mongo
260
268
  # Deserializes a section of payload type 0 of an OP_MSG from the IO stream.
261
269
  #
262
270
  # @param [ BSON::ByteBuffer ] buffer Buffer containing the sections.
271
+ # @param [ Hash ] options
272
+ #
273
+ # @option options [ Boolean ] :deserialize_as_bson Whether to perform
274
+ # section deserialization using BSON types instead of native Ruby types
275
+ # wherever possible.
263
276
  #
264
277
  # @return [ Array<BSON::Document> ] Deserialized section.
265
278
  #
266
279
  # @since 2.5.0
267
- def self.deserialize(buffer)
268
- BSON::Document.from_bson(buffer)
280
+ def self.deserialize(buffer, options = {})
281
+ mode = options[:deserialize_as_bson] ? :bson : nil
282
+ BSON::Document.from_bson(buffer, **{ mode: mode })
269
283
  end
270
284
  end
271
285
 
@@ -313,6 +327,8 @@ module Mongo
313
327
  #
314
328
  # @since 2.5.0
315
329
  def self.deserialize(buffer)
330
+ raise NotImplementedError
331
+
316
332
  start_size = buffer.length
317
333
  section_size = buffer.get_int32 # get the size
318
334
  end_size = start_size - section_size
@@ -340,18 +356,26 @@ module Mongo
340
356
  def self.serialize(buffer, value, max_bson_size = nil, validating_keys = BSON::Config.validating_keys?)
341
357
  start_size = buffer.length
342
358
  value.to_bson(buffer, validating_keys)
343
- if max_bson_size && buffer.length - start_size > max_bson_size
344
- raise Error::MaxBSONSize.new(max_bson_size)
359
+ serialized_size = buffer.length - start_size
360
+ if max_bson_size && serialized_size > max_bson_size
361
+ raise Error::MaxBSONSize,
362
+ "The document exceeds maximum allowed BSON object size after serialization. Serialized size: #{serialized_size} bytes, maximum allowed size: #{max_bson_size} bytes"
345
363
  end
346
364
  end
347
365
 
348
366
  # Deserializes a document from the IO stream
349
367
  #
350
368
  # @param [ String ] buffer Buffer containing the BSON encoded document.
369
+ # @param [ Hash ] options
370
+ #
371
+ # @option options [ Boolean ] :deserialize_as_bson Whether to perform
372
+ # section deserialization using BSON types instead of native Ruby types
373
+ # wherever possible.
351
374
  #
352
375
  # @return [ Hash ] The decoded BSON document.
353
- def self.deserialize(buffer)
354
- BSON::Document.from_bson(buffer)
376
+ def self.deserialize(buffer, options = {})
377
+ mode = options[:deserialize_as_bson] ? :bson : nil
378
+ BSON::Document.from_bson(buffer, **{ mode: mode })
355
379
  end
356
380
 
357
381
  # Whether there can be a size limit on this type after serialization.
@@ -385,11 +409,12 @@ module Mongo
385
409
  # Deserializes a byte from the byte buffer.
386
410
  #
387
411
  # @param [ BSON::ByteBuffer ] buffer Buffer containing the value to read.
412
+ # @param [ Hash ] options This method currently accepts no options.
388
413
  #
389
414
  # @return [ String ] The byte.
390
415
  #
391
416
  # @since 2.5.0
392
- def self.deserialize(buffer)
417
+ def self.deserialize(buffer, options = {})
393
418
  buffer.get_byte
394
419
  end
395
420
  end
@@ -415,12 +440,15 @@ module Mongo
415
440
  # Deserializes bytes from the byte buffer.
416
441
  #
417
442
  # @param [ BSON::ByteBuffer ] buffer Buffer containing the value to read.
418
- # @param [ Integer ] num_bytes Number of bytes to read.
443
+ # @param [ Hash ] options The method options.
444
+ #
445
+ # @option options [ Integer ] num_bytes Number of bytes to read.
419
446
  #
420
447
  # @return [ String ] The bytes.
421
448
  #
422
449
  # @since 2.5.0
423
- def self.deserialize(buffer, num_bytes = nil)
450
+ def self.deserialize(buffer, options = {})
451
+ num_bytes = options[:num_bytes]
424
452
  buffer.get_bytes(num_bytes || buffer.length)
425
453
  end
426
454
  end