mongo 2.13.0.beta1 → 2.14.0

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 (339) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +1 -5
  4. data/Rakefile +50 -9
  5. data/lib/mongo.rb +13 -2
  6. data/lib/mongo/address.rb +1 -1
  7. data/lib/mongo/address/ipv4.rb +1 -1
  8. data/lib/mongo/address/ipv6.rb +1 -1
  9. data/lib/mongo/auth/aws/request.rb +31 -5
  10. data/lib/mongo/bulk_write.rb +18 -0
  11. data/lib/mongo/caching_cursor.rb +74 -0
  12. data/lib/mongo/client.rb +238 -31
  13. data/lib/mongo/cluster.rb +56 -20
  14. data/lib/mongo/cluster/sdam_flow.rb +13 -10
  15. data/lib/mongo/cluster/topology/replica_set_no_primary.rb +3 -2
  16. data/lib/mongo/cluster/topology/sharded.rb +1 -1
  17. data/lib/mongo/cluster/topology/single.rb +2 -2
  18. data/lib/mongo/collection.rb +66 -24
  19. data/lib/mongo/collection/view.rb +24 -20
  20. data/lib/mongo/collection/view/aggregation.rb +25 -4
  21. data/lib/mongo/collection/view/builder/find_command.rb +38 -18
  22. data/lib/mongo/collection/view/explainable.rb +27 -8
  23. data/lib/mongo/collection/view/iterable.rb +72 -12
  24. data/lib/mongo/collection/view/readable.rb +19 -3
  25. data/lib/mongo/collection/view/writable.rb +55 -5
  26. data/lib/mongo/crypt/encryption_io.rb +6 -6
  27. data/lib/mongo/cursor.rb +16 -3
  28. data/lib/mongo/database.rb +37 -4
  29. data/lib/mongo/database/view.rb +18 -3
  30. data/lib/mongo/distinguishing_semaphore.rb +55 -0
  31. data/lib/mongo/error.rb +5 -0
  32. data/lib/mongo/error/invalid_read_concern.rb +28 -0
  33. data/lib/mongo/error/invalid_server_auth_host.rb +22 -0
  34. data/lib/mongo/error/invalid_session.rb +2 -1
  35. data/lib/mongo/error/operation_failure.rb +11 -5
  36. data/lib/mongo/error/server_certificate_revoked.rb +22 -0
  37. data/lib/mongo/error/sessions_not_supported.rb +35 -0
  38. data/lib/mongo/error/unsupported_option.rb +14 -12
  39. data/lib/mongo/event/base.rb +6 -0
  40. data/lib/mongo/grid/file.rb +5 -0
  41. data/lib/mongo/grid/file/chunk.rb +2 -0
  42. data/lib/mongo/grid/fs_bucket.rb +15 -13
  43. data/lib/mongo/grid/stream/write.rb +9 -3
  44. data/lib/mongo/index/view.rb +3 -0
  45. data/lib/mongo/lint.rb +2 -1
  46. data/lib/mongo/logger.rb +3 -3
  47. data/lib/mongo/monitoring.rb +38 -0
  48. data/lib/mongo/monitoring/command_log_subscriber.rb +10 -2
  49. data/lib/mongo/monitoring/event/command_failed.rb +11 -0
  50. data/lib/mongo/monitoring/event/command_started.rb +37 -2
  51. data/lib/mongo/monitoring/event/command_succeeded.rb +11 -0
  52. data/lib/mongo/monitoring/event/server_closed.rb +1 -1
  53. data/lib/mongo/monitoring/event/server_description_changed.rb +27 -4
  54. data/lib/mongo/monitoring/event/server_heartbeat_failed.rb +9 -2
  55. data/lib/mongo/monitoring/event/server_heartbeat_started.rb +9 -2
  56. data/lib/mongo/monitoring/event/server_heartbeat_succeeded.rb +9 -2
  57. data/lib/mongo/monitoring/event/server_opening.rb +1 -1
  58. data/lib/mongo/monitoring/event/topology_changed.rb +1 -1
  59. data/lib/mongo/monitoring/event/topology_closed.rb +1 -1
  60. data/lib/mongo/monitoring/event/topology_opening.rb +1 -1
  61. data/lib/mongo/monitoring/publishable.rb +6 -3
  62. data/lib/mongo/monitoring/server_description_changed_log_subscriber.rb +9 -1
  63. data/lib/mongo/monitoring/topology_changed_log_subscriber.rb +1 -1
  64. data/lib/mongo/operation.rb +2 -0
  65. data/lib/mongo/operation/aggregate/result.rb +9 -8
  66. data/lib/mongo/operation/collections_info/command.rb +5 -0
  67. data/lib/mongo/operation/collections_info/result.rb +18 -1
  68. data/lib/mongo/operation/delete/bulk_result.rb +2 -0
  69. data/lib/mongo/operation/delete/result.rb +3 -0
  70. data/lib/mongo/operation/explain/command.rb +4 -0
  71. data/lib/mongo/operation/explain/legacy.rb +4 -0
  72. data/lib/mongo/operation/explain/op_msg.rb +6 -0
  73. data/lib/mongo/operation/explain/result.rb +3 -0
  74. data/lib/mongo/operation/find/legacy/result.rb +2 -0
  75. data/lib/mongo/operation/find/result.rb +13 -0
  76. data/lib/mongo/operation/get_more/result.rb +3 -0
  77. data/lib/mongo/operation/indexes/result.rb +5 -0
  78. data/lib/mongo/operation/insert/bulk_result.rb +5 -0
  79. data/lib/mongo/operation/insert/result.rb +5 -0
  80. data/lib/mongo/operation/list_collections/result.rb +5 -0
  81. data/lib/mongo/operation/map_reduce/result.rb +10 -0
  82. data/lib/mongo/operation/parallel_scan/result.rb +4 -0
  83. data/lib/mongo/operation/result.rb +35 -6
  84. data/lib/mongo/operation/shared/bypass_document_validation.rb +1 -0
  85. data/lib/mongo/operation/shared/causal_consistency_supported.rb +1 -0
  86. data/lib/mongo/operation/shared/collections_info_or_list_collections.rb +2 -0
  87. data/lib/mongo/operation/shared/executable.rb +1 -0
  88. data/lib/mongo/operation/shared/idable.rb +2 -1
  89. data/lib/mongo/operation/shared/limited.rb +1 -0
  90. data/lib/mongo/operation/shared/object_id_generator.rb +1 -0
  91. data/lib/mongo/operation/shared/result/aggregatable.rb +1 -0
  92. data/lib/mongo/operation/shared/sessions_supported.rb +1 -0
  93. data/lib/mongo/operation/shared/specifiable.rb +1 -0
  94. data/lib/mongo/operation/shared/write.rb +1 -0
  95. data/lib/mongo/operation/shared/write_concern_supported.rb +1 -0
  96. data/lib/mongo/operation/update/legacy/result.rb +7 -0
  97. data/lib/mongo/operation/update/result.rb +8 -0
  98. data/lib/mongo/operation/users_info/result.rb +3 -0
  99. data/lib/mongo/protocol/message.rb +47 -10
  100. data/lib/mongo/protocol/msg.rb +34 -1
  101. data/lib/mongo/protocol/query.rb +36 -0
  102. data/lib/mongo/protocol/serializers.rb +5 -2
  103. data/lib/mongo/query_cache.rb +242 -0
  104. data/lib/mongo/retryable.rb +8 -1
  105. data/lib/mongo/server.rb +15 -4
  106. data/lib/mongo/server/app_metadata.rb +27 -3
  107. data/lib/mongo/server/connection.rb +4 -4
  108. data/lib/mongo/server/connection_base.rb +38 -12
  109. data/lib/mongo/server/connection_common.rb +2 -2
  110. data/lib/mongo/server/connection_pool.rb +3 -0
  111. data/lib/mongo/server/description.rb +13 -1
  112. data/lib/mongo/server/monitor.rb +76 -44
  113. data/lib/mongo/server/monitor/connection.rb +57 -9
  114. data/lib/mongo/server/pending_connection.rb +14 -4
  115. data/lib/mongo/server/push_monitor.rb +173 -0
  116. data/{spec/runners/transactions/context.rb → lib/mongo/server/push_monitor/connection.rb} +9 -14
  117. data/lib/mongo/server_selector.rb +0 -1
  118. data/lib/mongo/server_selector/base.rb +583 -1
  119. data/lib/mongo/server_selector/nearest.rb +1 -6
  120. data/lib/mongo/server_selector/primary.rb +1 -6
  121. data/lib/mongo/server_selector/primary_preferred.rb +7 -10
  122. data/lib/mongo/server_selector/secondary.rb +1 -6
  123. data/lib/mongo/server_selector/secondary_preferred.rb +1 -7
  124. data/lib/mongo/session.rb +7 -1
  125. data/lib/mongo/socket.rb +26 -12
  126. data/lib/mongo/socket/ocsp_cache.rb +97 -0
  127. data/lib/mongo/socket/ocsp_verifier.rb +368 -0
  128. data/lib/mongo/socket/ssl.rb +46 -25
  129. data/lib/mongo/socket/tcp.rb +1 -1
  130. data/lib/mongo/srv/monitor.rb +7 -13
  131. data/lib/mongo/srv/resolver.rb +14 -10
  132. data/lib/mongo/timeout.rb +2 -0
  133. data/lib/mongo/topology_version.rb +9 -0
  134. data/lib/mongo/uri.rb +21 -390
  135. data/lib/mongo/uri/options_mapper.rb +582 -0
  136. data/lib/mongo/uri/srv_protocol.rb +3 -2
  137. data/lib/mongo/utils.rb +73 -0
  138. data/lib/mongo/version.rb +1 -1
  139. data/spec/NOTES.aws-auth.md +12 -7
  140. data/spec/README.aws-auth.md +2 -2
  141. data/spec/README.md +63 -1
  142. data/spec/integration/awaited_ismaster_spec.rb +28 -0
  143. data/spec/integration/bson_symbol_spec.rb +4 -2
  144. data/spec/integration/bulk_write_spec.rb +67 -0
  145. data/spec/integration/change_stream_examples_spec.rb +6 -2
  146. data/spec/integration/change_stream_spec.rb +1 -1
  147. data/spec/integration/check_clean_slate_spec.rb +16 -0
  148. data/spec/integration/client_authentication_options_spec.rb +92 -28
  149. data/spec/integration/client_construction_spec.rb +1 -0
  150. data/spec/integration/client_side_encryption/auto_encryption_bulk_writes_spec.rb +9 -5
  151. data/spec/integration/connect_single_rs_name_spec.rb +5 -2
  152. data/spec/integration/connection_pool_populator_spec.rb +4 -2
  153. data/spec/integration/connection_spec.rb +7 -4
  154. data/spec/integration/crud_spec.rb +4 -4
  155. data/spec/integration/cursor_reaping_spec.rb +54 -18
  156. data/spec/integration/docs_examples_spec.rb +6 -0
  157. data/spec/integration/fork_reconnect_spec.rb +56 -1
  158. data/spec/integration/grid_fs_bucket_spec.rb +48 -0
  159. data/spec/integration/heartbeat_events_spec.rb +4 -23
  160. data/spec/integration/ocsp_connectivity_spec.rb +26 -0
  161. data/spec/integration/ocsp_verifier_cache_spec.rb +188 -0
  162. data/spec/integration/ocsp_verifier_spec.rb +334 -0
  163. data/spec/integration/query_cache_spec.rb +1045 -0
  164. data/spec/integration/query_cache_transactions_spec.rb +190 -0
  165. data/spec/integration/read_concern_spec.rb +1 -1
  166. data/spec/integration/retryable_errors_spec.rb +1 -1
  167. data/spec/integration/retryable_writes/retryable_writes_40_and_newer_spec.rb +1 -0
  168. data/spec/integration/retryable_writes/shared/performs_legacy_retries.rb +4 -2
  169. data/spec/integration/retryable_writes/shared/performs_modern_retries.rb +3 -3
  170. data/spec/integration/retryable_writes/shared/performs_no_retries.rb +2 -2
  171. data/spec/integration/sdam_error_handling_spec.rb +122 -15
  172. data/spec/integration/sdam_events_spec.rb +80 -6
  173. data/spec/integration/sdam_prose_spec.rb +64 -0
  174. data/spec/integration/server_monitor_spec.rb +25 -1
  175. data/spec/integration/server_selection_spec.rb +36 -0
  176. data/spec/integration/size_limit_spec.rb +23 -5
  177. data/spec/integration/srv_monitoring_spec.rb +38 -3
  178. data/spec/integration/srv_spec.rb +56 -0
  179. data/spec/integration/ssl_uri_options_spec.rb +2 -2
  180. data/spec/integration/transactions_examples_spec.rb +17 -7
  181. data/spec/integration/zlib_compression_spec.rb +25 -0
  182. data/spec/lite_spec_helper.rb +20 -9
  183. data/spec/mongo/address_spec.rb +1 -1
  184. data/spec/mongo/auth/aws/request_region_spec.rb +42 -0
  185. data/spec/mongo/auth/aws/request_spec.rb +76 -0
  186. data/spec/mongo/auth/scram_spec.rb +1 -1
  187. data/spec/mongo/auth/user_spec.rb +1 -1
  188. data/spec/mongo/bulk_write_spec.rb +2 -2
  189. data/spec/mongo/caching_cursor_spec.rb +70 -0
  190. data/spec/mongo/client_construction_spec.rb +386 -3
  191. data/spec/mongo/client_encryption_spec.rb +16 -10
  192. data/spec/mongo/client_spec.rb +85 -3
  193. data/spec/mongo/cluster/topology/replica_set_spec.rb +53 -10
  194. data/spec/mongo/cluster/topology/sharded_spec.rb +1 -1
  195. data/spec/mongo/cluster/topology/single_spec.rb +19 -8
  196. data/spec/mongo/cluster/topology/unknown_spec.rb +1 -1
  197. data/spec/mongo/cluster/topology_spec.rb +1 -1
  198. data/spec/mongo/cluster_spec.rb +37 -35
  199. data/spec/mongo/collection/view/change_stream_resume_spec.rb +7 -7
  200. data/spec/mongo/collection/view/explainable_spec.rb +87 -4
  201. data/spec/mongo/collection/view/map_reduce_spec.rb +2 -0
  202. data/spec/mongo/collection/view/readable_spec.rb +36 -0
  203. data/spec/mongo/collection_spec.rb +572 -0
  204. data/spec/mongo/crypt/auto_decryption_context_spec.rb +1 -1
  205. data/spec/mongo/crypt/auto_encryption_context_spec.rb +1 -1
  206. data/spec/mongo/crypt/binary_spec.rb +1 -6
  207. data/spec/mongo/crypt/binding/binary_spec.rb +1 -6
  208. data/spec/mongo/crypt/binding/context_spec.rb +2 -7
  209. data/spec/mongo/crypt/binding/helpers_spec.rb +1 -6
  210. data/spec/mongo/crypt/binding/mongocrypt_spec.rb +2 -7
  211. data/spec/mongo/crypt/binding/status_spec.rb +1 -6
  212. data/spec/mongo/crypt/binding/version_spec.rb +1 -6
  213. data/spec/mongo/crypt/data_key_context_spec.rb +1 -1
  214. data/spec/mongo/crypt/explicit_decryption_context_spec.rb +1 -1
  215. data/spec/mongo/crypt/explicit_encryption_context_spec.rb +1 -1
  216. data/spec/mongo/crypt/status_spec.rb +1 -6
  217. data/spec/mongo/database_spec.rb +353 -8
  218. data/spec/mongo/distinguishing_semaphore_spec.rb +63 -0
  219. data/spec/mongo/error/no_server_available_spec.rb +1 -1
  220. data/spec/mongo/error/operation_failure_spec.rb +40 -0
  221. data/spec/mongo/index/view_spec.rb +148 -2
  222. data/spec/mongo/logger_spec.rb +13 -11
  223. data/spec/mongo/monitoring/event/server_closed_spec.rb +1 -1
  224. data/spec/mongo/monitoring/event/server_description_changed_spec.rb +1 -4
  225. data/spec/mongo/monitoring/event/server_opening_spec.rb +1 -1
  226. data/spec/mongo/monitoring/event/topology_changed_spec.rb +1 -1
  227. data/spec/mongo/monitoring/event/topology_closed_spec.rb +1 -1
  228. data/spec/mongo/monitoring/event/topology_opening_spec.rb +1 -1
  229. data/spec/mongo/operation/delete/op_msg_spec.rb +3 -3
  230. data/spec/mongo/operation/insert/command_spec.rb +2 -2
  231. data/spec/mongo/operation/insert/op_msg_spec.rb +3 -3
  232. data/spec/mongo/operation/read_preference_op_msg_spec.rb +1 -1
  233. data/spec/mongo/operation/update/command_spec.rb +2 -2
  234. data/spec/mongo/operation/update/op_msg_spec.rb +3 -3
  235. data/spec/mongo/protocol/msg_spec.rb +10 -0
  236. data/spec/mongo/query_cache_spec.rb +280 -0
  237. data/spec/mongo/semaphore_spec.rb +51 -0
  238. data/spec/mongo/server/app_metadata_shared.rb +82 -2
  239. data/spec/mongo/server/connection_auth_spec.rb +2 -2
  240. data/spec/mongo/server/connection_pool_spec.rb +7 -3
  241. data/spec/mongo/server/connection_spec.rb +15 -8
  242. data/spec/mongo/server/description_spec.rb +18 -0
  243. data/spec/mongo/server_selector/nearest_spec.rb +23 -23
  244. data/spec/mongo/server_selector/primary_preferred_spec.rb +26 -26
  245. data/spec/mongo/server_selector/primary_spec.rb +9 -9
  246. data/spec/mongo/server_selector/secondary_preferred_spec.rb +22 -22
  247. data/spec/mongo/server_selector/secondary_spec.rb +18 -18
  248. data/spec/mongo/server_selector_spec.rb +6 -6
  249. data/spec/mongo/session_spec.rb +35 -0
  250. data/spec/mongo/socket/ssl_spec.rb +4 -4
  251. data/spec/mongo/socket_spec.rb +1 -1
  252. data/spec/mongo/uri/srv_protocol_spec.rb +64 -33
  253. data/spec/mongo/uri_option_parsing_spec.rb +11 -11
  254. data/spec/mongo/uri_spec.rb +68 -41
  255. data/spec/mongo/utils_spec.rb +39 -0
  256. data/spec/runners/auth.rb +3 -0
  257. data/spec/runners/change_streams/test.rb +3 -3
  258. data/spec/runners/cmap.rb +1 -1
  259. data/spec/runners/command_monitoring.rb +3 -34
  260. data/spec/runners/connection_string.rb +35 -124
  261. data/spec/runners/crud/context.rb +9 -5
  262. data/spec/runners/crud/operation.rb +59 -27
  263. data/spec/runners/crud/spec.rb +0 -8
  264. data/spec/runners/crud/test.rb +1 -1
  265. data/spec/runners/crud/test_base.rb +0 -19
  266. data/spec/runners/sdam.rb +2 -2
  267. data/spec/runners/server_selection.rb +242 -28
  268. data/spec/runners/transactions.rb +12 -12
  269. data/spec/runners/transactions/operation.rb +151 -25
  270. data/spec/runners/transactions/test.rb +62 -18
  271. data/spec/shared/LICENSE +20 -0
  272. data/spec/shared/lib/mrss/child_process_helper.rb +80 -0
  273. data/spec/shared/lib/mrss/constraints.rb +303 -0
  274. data/spec/shared/lib/mrss/lite_constraints.rb +175 -0
  275. data/spec/shared/lib/mrss/spec_organizer.rb +149 -0
  276. data/spec/spec_helper.rb +3 -1
  277. data/spec/spec_tests/cmap_spec.rb +7 -3
  278. data/spec/spec_tests/command_monitoring_spec.rb +22 -12
  279. data/spec/spec_tests/crud_spec.rb +1 -1
  280. data/spec/spec_tests/data/change_streams/change-streams-errors.yml +4 -9
  281. data/spec/spec_tests/data/change_streams/change-streams-resume-whitelist.yml +66 -0
  282. data/spec/spec_tests/data/change_streams/change-streams.yml +0 -1
  283. data/spec/spec_tests/data/cmap/pool-checkout-connection.yml +6 -2
  284. data/spec/spec_tests/data/cmap/pool-create-min-size.yml +3 -0
  285. data/spec/spec_tests/data/connection_string/valid-warnings.yml +24 -0
  286. data/spec/spec_tests/data/max_staleness/ReplicaSetNoPrimary/MaxStalenessTooSmall.yml +15 -0
  287. data/spec/spec_tests/data/max_staleness/ReplicaSetNoPrimary/NoKnownServers.yml +4 -3
  288. data/spec/spec_tests/data/max_staleness/Unknown/SmallMaxStaleness.yml +1 -0
  289. data/spec/spec_tests/data/sdam_integration/cancel-server-check.yml +96 -0
  290. data/spec/spec_tests/data/sdam_integration/connectTimeoutMS.yml +88 -0
  291. data/spec/spec_tests/data/sdam_integration/find-network-error.yml +83 -0
  292. data/spec/spec_tests/data/sdam_integration/find-shutdown-error.yml +116 -0
  293. data/spec/spec_tests/data/sdam_integration/insert-network-error.yml +86 -0
  294. data/spec/spec_tests/data/sdam_integration/insert-shutdown-error.yml +115 -0
  295. data/spec/spec_tests/data/sdam_integration/isMaster-command-error.yml +168 -0
  296. data/spec/spec_tests/data/sdam_integration/isMaster-network-error.yml +162 -0
  297. data/spec/spec_tests/data/sdam_integration/isMaster-timeout.yml +229 -0
  298. data/spec/spec_tests/data/sdam_integration/rediscover-quickly-after-step-down.yml +87 -0
  299. data/spec/spec_tests/data/sdam_monitoring/discovered_standalone.yml +1 -3
  300. data/spec/spec_tests/data/sdam_monitoring/standalone.yml +2 -2
  301. data/spec/spec_tests/data/sdam_monitoring/standalone_repeated.yml +2 -2
  302. data/spec/spec_tests/data/sdam_monitoring/standalone_suppress_equal_description_changes.yml +2 -2
  303. data/spec/spec_tests/data/sdam_monitoring/standalone_to_rs_with_me_mismatch.yml +2 -2
  304. data/spec/spec_tests/data/uri_options/auth-options.yml +25 -0
  305. data/spec/spec_tests/data/uri_options/compression-options.yml +6 -3
  306. data/spec/spec_tests/data/uri_options/read-preference-options.yml +24 -0
  307. data/spec/spec_tests/data/uri_options/ruby-connection-options.yml +1 -0
  308. data/spec/spec_tests/data/uri_options/tls-options.yml +160 -4
  309. data/spec/spec_tests/dns_seedlist_discovery_spec.rb +9 -1
  310. data/spec/spec_tests/max_staleness_spec.rb +4 -142
  311. data/spec/spec_tests/retryable_reads_spec.rb +2 -2
  312. data/spec/spec_tests/sdam_integration_spec.rb +13 -0
  313. data/spec/spec_tests/sdam_monitoring_spec.rb +1 -2
  314. data/spec/spec_tests/server_selection_spec.rb +4 -116
  315. data/spec/spec_tests/uri_options_spec.rb +31 -33
  316. data/spec/stress/cleanup_spec.rb +17 -2
  317. data/spec/stress/connection_pool_stress_spec.rb +10 -8
  318. data/spec/stress/fork_reconnect_stress_spec.rb +1 -1
  319. data/spec/support/certificates/atlas-ocsp-ca.crt +28 -0
  320. data/spec/support/certificates/atlas-ocsp.crt +41 -0
  321. data/spec/support/client_registry.rb +1 -0
  322. data/spec/support/client_registry_macros.rb +11 -2
  323. data/spec/support/cluster_config.rb +4 -0
  324. data/spec/support/common_shortcuts.rb +45 -0
  325. data/spec/support/constraints.rb +6 -253
  326. data/spec/support/event_subscriber.rb +123 -33
  327. data/spec/support/keyword_struct.rb +26 -0
  328. data/spec/support/matchers.rb +16 -0
  329. data/spec/support/ocsp +1 -0
  330. data/spec/support/session_registry.rb +52 -0
  331. data/spec/support/shared/server_selector.rb +13 -1
  332. data/spec/support/spec_config.rb +60 -13
  333. data/spec/support/spec_setup.rb +1 -1
  334. data/spec/support/utils.rb +84 -1
  335. metadata +1027 -937
  336. metadata.gz.sig +0 -0
  337. data/lib/mongo/server_selector/selectable.rb +0 -560
  338. data/spec/runners/sdam_monitoring.rb +0 -89
  339. data/spec/support/lite_constraints.rb +0 -141
@@ -20,7 +20,6 @@ module Mongo
20
20
  #
21
21
  # @since 2.0.0
22
22
  class Nearest < Base
23
- include Selectable
24
23
 
25
24
  # Name of the this read preference in the server's format.
26
25
  #
@@ -92,14 +91,10 @@ module Mongo
92
91
  # Select the near servers taking into account any defined tag sets and
93
92
  # local threshold between the nearest server and other servers.
94
93
  #
95
- # @example Select nearest servers given a list of candidates.
96
- # preference = Mongo::ServerSelector::Nearest.new
97
- # preference.select_server(cluster)
98
- #
99
94
  # @return [ Array ] The nearest servers from the list of candidates.
100
95
  #
101
96
  # @since 2.0.0
102
- def select(candidates)
97
+ def select_in_replica_set(candidates)
103
98
  matching_servers = filter_stale_servers(candidates, primary(candidates).first)
104
99
  matching_servers = match_tag_sets(matching_servers) unless tag_sets.empty?
105
100
  near_servers(matching_servers)
@@ -21,7 +21,6 @@ module Mongo
21
21
  #
22
22
  # @since 2.0.0
23
23
  class Primary < Base
24
- include Selectable
25
24
 
26
25
  # Name of the this read preference in the server's format.
27
26
  #
@@ -94,14 +93,10 @@ module Mongo
94
93
 
95
94
  # Select the primary server from a list of candidates.
96
95
  #
97
- # @example Select the primary server given a list of candidates.
98
- # preference = Mongo::ServerSelector::Primary.new
99
- # preference.select([candidate_1, candidate_2])
100
- #
101
96
  # @return [ Array ] The primary server from the list of candidates.
102
97
  #
103
98
  # @since 2.0.0
104
- def select(candidates)
99
+ def select_in_replica_set(candidates)
105
100
  primary(candidates)
106
101
  end
107
102
 
@@ -21,7 +21,6 @@ module Mongo
21
21
  #
22
22
  # @since 2.0.0
23
23
  class PrimaryPreferred < Base
24
- include Selectable
25
24
 
26
25
  # Name of the this read preference in the server's format.
27
26
  #
@@ -93,19 +92,17 @@ module Mongo
93
92
  # Select servers taking into account any defined tag sets and
94
93
  # local threshold, with the primary preferred.
95
94
  #
96
- # @example Select servers given a list of candidates,
97
- # with the primary preferred.
98
- # preference = Mongo::ServerSelector::PrimaryPreferred.new
99
- # preference.select([candidate_1, candidate_2])
100
- #
101
95
  # @return [ Array ] A list of servers matching tag sets and acceptable
102
96
  # latency with the primary preferred.
103
97
  #
104
98
  # @since 2.0.0
105
- def select(candidates)
106
- primary = primary(candidates)
107
- secondaries = near_servers(secondaries(candidates))
108
- primary.first ? primary : secondaries
99
+ def select_in_replica_set(candidates)
100
+ primaries = primary(candidates)
101
+ if primaries.first
102
+ primaries
103
+ else
104
+ near_servers(secondaries(candidates))
105
+ end
109
106
  end
110
107
 
111
108
  def max_staleness_allowed?
@@ -21,7 +21,6 @@ module Mongo
21
21
  #
22
22
  # @since 2.0.0
23
23
  class Secondary < Base
24
- include Selectable
25
24
 
26
25
  # Name of the this read preference in the server's format.
27
26
  #
@@ -93,14 +92,10 @@ module Mongo
93
92
  # Select the secondary servers taking into account any defined tag sets and
94
93
  # local threshold between the nearest secondary and other secondaries.
95
94
  #
96
- # @example Select secondary servers given a list of candidates.
97
- # preference = Mongo::ServerSelector::Secondary.new
98
- # preference.select([candidate_1, candidate_2])
99
- #
100
95
  # @return [ Array ] The secondary servers from the list of candidates.
101
96
  #
102
97
  # @since 2.0.0
103
- def select(candidates)
98
+ def select_in_replica_set(candidates)
104
99
  near_servers(secondaries(candidates))
105
100
  end
106
101
 
@@ -21,7 +21,6 @@ module Mongo
21
21
  #
22
22
  # @since 2.0.0
23
23
  class SecondaryPreferred < Base
24
- include Selectable
25
24
 
26
25
  # Name of the this read preference in the server's format.
27
26
  #
@@ -101,16 +100,11 @@ module Mongo
101
100
  # Select servers taking into account any defined tag sets and
102
101
  # local threshold, with secondaries.
103
102
  #
104
- # @example Select servers given a list of candidates,
105
- # with secondaries preferred.
106
- # preference = Mongo::ServerSelector::SecondaryPreferred.new
107
- # preference.select([candidate_1, candidate_2])
108
- #
109
103
  # @return [ Array ] A list of servers matching tag sets and acceptable
110
104
  # latency with secondaries preferred.
111
105
  #
112
106
  # @since 2.0.0
113
- def select(candidates)
107
+ def select_in_replica_set(candidates)
114
108
  near_servers(secondaries(candidates)) + primary(candidates)
115
109
  end
116
110
 
@@ -227,7 +227,9 @@ module Mongo
227
227
  # Error message describing that sessions are not supported by the server version.
228
228
  #
229
229
  # @since 2.5.0
230
+ # @deprecated
230
231
  SESSIONS_NOT_SUPPORTED = 'Sessions are not supported by the connected servers.'.freeze
232
+ # Note: SESSIONS_NOT_SUPPORTED is used by Mongoid - do not remove from driver.
231
233
 
232
234
  # The state of a session in which the last operation was not related to
233
235
  # any transaction or no operations have yet occurred.
@@ -377,6 +379,7 @@ module Mongo
377
379
  rv = yield self
378
380
  rescue Exception => e
379
381
  if within_states?(STARTING_TRANSACTION_STATE, TRANSACTION_IN_PROGRESS_STATE)
382
+ log_warn("Aborting transaction due to #{e.class}: #{e}")
380
383
  abort_transaction
381
384
  transaction_in_progress = false
382
385
  end
@@ -441,7 +444,7 @@ module Mongo
441
444
  true
442
445
  ensure
443
446
  if transaction_in_progress
444
- log_warn('with_transaction callback altered with_transaction loop, aborting transaction')
447
+ log_warn('with_transaction callback broke out of with_transaction loop, aborting transaction')
445
448
  begin
446
449
  abort_transaction
447
450
  rescue Error::OperationFailure, Error::InvalidTransactionOperation
@@ -532,6 +535,7 @@ module Mongo
532
535
  #
533
536
  # @since 2.6.0
534
537
  def commit_transaction(options=nil)
538
+ QueryCache.clear
535
539
  check_if_ended!
536
540
  check_if_no_transaction!
537
541
 
@@ -600,6 +604,8 @@ module Mongo
600
604
  #
601
605
  # @since 2.6.0
602
606
  def abort_transaction
607
+ QueryCache.clear
608
+
603
609
  check_if_ended!
604
610
  check_if_no_transaction!
605
611
 
@@ -15,19 +15,22 @@
15
15
  require 'mongo/socket/ssl'
16
16
  require 'mongo/socket/tcp'
17
17
  require 'mongo/socket/unix'
18
+ require 'mongo/socket/ocsp_verifier'
19
+ require 'mongo/socket/ocsp_cache'
18
20
 
19
21
  module Mongo
20
22
 
21
23
  # Provides additional data around sockets for the driver's use.
22
24
  #
23
25
  # @since 2.0.0
26
+ # @api private
24
27
  class Socket
25
28
  include ::Socket::Constants
26
29
 
27
- # Error message for SSL related exceptions.
30
+ # Error message for TLS related exceptions.
28
31
  #
29
32
  # @since 2.0.0
30
- SSL_ERROR = 'MongoDB may not be configured with SSL support'.freeze
33
+ SSL_ERROR = 'MongoDB may not be configured with TLS support'.freeze
31
34
 
32
35
  # Error message for timeouts on socket calls.
33
36
  #
@@ -105,9 +108,14 @@ module Mongo
105
108
  def summary
106
109
  fileno = @socket&.fileno rescue '<no socket>' || '<no socket>'
107
110
  if monitor?
108
- "#{connection_address}:m #{fileno}"
111
+ indicator = if options[:push]
112
+ 'pm'
113
+ else
114
+ 'm'
115
+ end
116
+ "#{connection_address};#{indicator};fd=#{fileno}"
109
117
  else
110
- "#{connection_address}:c:#{connection_generation} #{fileno}"
118
+ "#{connection_address};c:#{connection_generation};fd=#{fileno}"
111
119
  end
112
120
  end
113
121
 
@@ -123,7 +131,7 @@ module Mongo
123
131
  sock_arr = [ @socket ]
124
132
  if Kernel::select(sock_arr, nil, sock_arr, 0)
125
133
  # The eof? call is supposed to return immediately since select
126
- # indicated the socket is readable. However, if @socket is an SSL
134
+ # indicated the socket is readable. However, if @socket is a TLS
127
135
  # socket, eof? can block anyway - see RUBY-2140.
128
136
  begin
129
137
  Timeout.timeout(0.1) do
@@ -173,20 +181,21 @@ module Mongo
173
181
  # socket.read(4096)
174
182
  #
175
183
  # @param [ Integer ] length The number of bytes to read.
184
+ # @param [ Numeric ] timeout The timeout to use for each chunk read.
176
185
  #
177
186
  # @raise [ Mongo::SocketError ] If not all data is returned.
178
187
  #
179
188
  # @return [ Object ] The data from the socket.
180
189
  #
181
190
  # @since 2.0.0
182
- def read(length)
191
+ def read(length, timeout: nil)
183
192
  map_exceptions do
184
- data = read_from_socket(length)
193
+ data = read_from_socket(length, timeout: timeout)
185
194
  unless (data.length > 0 || length == 0)
186
195
  raise IOError, "Expected to read > 0 bytes but read 0 bytes"
187
196
  end
188
197
  while data.length < length
189
- chunk = read_from_socket(length - data.length)
198
+ chunk = read_from_socket(length - data.length, timeout: timeout)
190
199
  unless (chunk.length > 0 || length == 0)
191
200
  raise IOError, "Expected to read > 0 bytes but read 0 bytes"
192
201
  end
@@ -243,14 +252,19 @@ module Mongo
243
252
 
244
253
  private
245
254
 
246
- def read_from_socket(length)
255
+ def read_from_socket(length, timeout: nil)
247
256
  # Just in case
248
257
  if length == 0
249
258
  return ''.force_encoding('BINARY')
250
259
  end
251
260
 
252
- if _timeout = self.timeout
253
- deadline = Time.now + _timeout
261
+ _timeout = timeout || self.timeout
262
+ if _timeout
263
+ if _timeout > 0
264
+ deadline = Time.now + _timeout
265
+ elsif _timeout < 0
266
+ raise Errno::ETIMEDOUT, "Negative timeout #{_timeout} given to socket"
267
+ end
254
268
  end
255
269
 
256
270
  # We want to have a fixed and reasonably small size buffer for reads
@@ -330,7 +344,7 @@ module Mongo
330
344
  end
331
345
 
332
346
  def read_buffer_size
333
- # Buffer size for non-SSL reads
347
+ # Buffer size for non-TLS reads
334
348
  # 64kb
335
349
  65536
336
350
  end
@@ -0,0 +1,97 @@
1
+ # Copyright (C) 2020 MongoDB Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module Mongo
16
+ class Socket
17
+
18
+ # This module caches OCSP responses for their indicated validity time.
19
+ #
20
+ # The key is the CertificateId used for the OCSP request.
21
+ # The value is the SingleResponse on Ruby 2.4+, or the OpenStruct
22
+ # emulation of it on Ruby 2.3.
23
+ #
24
+ # @api private
25
+ module OcspCache
26
+ module_function def set(cert_id, response)
27
+ delete(cert_id)
28
+ responses << response
29
+ end
30
+
31
+ # Retrieves a cached SingleResponse for the specified CertificateId.
32
+ #
33
+ # This method may return expired responses if they are revoked.
34
+ # Such responses were valid when they were first received.
35
+ #
36
+ # This method may also return responses that are valid but that may
37
+ # expire by the time caller uses them. The caller should not perform
38
+ # update time checks on the returned response.
39
+ #
40
+ # @return [ OpenSSL::OCSP::SingleResponse | OpenStruct ] The previously
41
+ # retrieved response.
42
+ module_function def get(cert_id)
43
+ resp = responses.detect do |resp|
44
+ resp.certid.cmp(cert_id)
45
+ end
46
+ if resp
47
+ # Only expire responses with good status.
48
+ # Once a certificate is revoked, it should stay revoked forever,
49
+ # hence we should be able to cache revoked responses indefinitely.
50
+ if resp.cert_status == OpenSSL::OCSP::V_CERTSTATUS_GOOD &&
51
+ resp.next_update < Time.now
52
+ then
53
+ responses.delete(resp)
54
+ resp = nil
55
+ end
56
+ end
57
+
58
+ # If we have connected to a server and cached the OCSP response for it,
59
+ # and then never connect to that server again, the cached OCSP response
60
+ # is going to remain in memory indefinitely. Periodically remove all
61
+ # expired OCSP responses, not just the ones matching the certificate id
62
+ # we are querying by.
63
+ if rand < 0.01
64
+ responses.delete_if do |resp|
65
+ resp.next_update < Time.now
66
+ end
67
+ end
68
+
69
+ resp
70
+ end
71
+
72
+ module_function def delete(cert_id)
73
+ responses.delete_if do |resp|
74
+ resp.certid.cmp(cert_id)
75
+ end
76
+ end
77
+
78
+ # Clears the driver's OCSP response cache.
79
+ #
80
+ # @note Use Mongo.clear_ocsp_cache from applications instead of invoking
81
+ # this method directly.
82
+ module_function def clear
83
+ responses.replace([])
84
+ end
85
+
86
+ private
87
+
88
+ LOCK = Mutex.new
89
+
90
+ module_function def responses
91
+ LOCK.synchronize do
92
+ @responses ||= []
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,368 @@
1
+ # Copyright (C) 2020 MongoDB Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module Net
16
+ autoload :HTTP, 'net/http'
17
+ end
18
+
19
+ module Mongo
20
+ class Socket
21
+
22
+ # OCSP endpoint verifier.
23
+ #
24
+ # After a TLS connection is established, this verifier inspects the
25
+ # certificate presented by the server, and if the certificate contains
26
+ # an OCSP URI, performs the OCSP status request to the specified URI
27
+ # (following up to 5 redirects) to verify the certificate status.
28
+ #
29
+ # @see https://ruby-doc.org/stdlib/libdoc/openssl/rdoc/OpenSSL/OCSP.html
30
+ #
31
+ # @api private
32
+ class OcspVerifier
33
+ include Loggable
34
+
35
+ # @param [ String ] host_name The host name being verified, for
36
+ # diagnostic output.
37
+ # @param [ OpenSSL::X509::Certificate ] cert The certificate presented by
38
+ # the server at host_name.
39
+ # @param [ OpenSSL::X509::Certificate ] ca_cert The CA certificate
40
+ # presented by the server or resolved locally from the server
41
+ # certificate.
42
+ # @param [ OpenSSL::X509::Store ] cert_store The certificate store to
43
+ # use for verifying OCSP response. This should be the same store as
44
+ # used in SSLContext used with the SSLSocket that we are verifying the
45
+ # certificate for. This must NOT be the CA certificate provided by
46
+ # the server (i.e. anything taken out of peer_cert) - otherwise the
47
+ # server would dictate which CA authorities the client trusts.
48
+ def initialize(host_name, cert, ca_cert, cert_store, **opts)
49
+ @host_name = host_name
50
+ @cert = cert
51
+ @ca_cert = ca_cert
52
+ @cert_store = cert_store
53
+ @options = opts
54
+ end
55
+
56
+ attr_reader :host_name
57
+ attr_reader :cert
58
+ attr_reader :ca_cert
59
+ attr_reader :cert_store
60
+ attr_reader :options
61
+
62
+ def timeout
63
+ options[:timeout] || 5
64
+ end
65
+
66
+ # @return [ Array<String> ] OCSP URIs in the specified server certificate.
67
+ def ocsp_uris
68
+ @ocsp_uris ||= begin
69
+ # https://tools.ietf.org/html/rfc3546#section-2.3
70
+ # prohibits multiple extensions with the same oid.
71
+ ext = cert.extensions.detect do |ext|
72
+ ext.oid == 'authorityInfoAccess'
73
+ end
74
+
75
+ if ext
76
+ # Our test certificates have multiple OCSP URIs.
77
+ ext.value.split("\n").select do |line|
78
+ line.start_with?('OCSP - URI:')
79
+ end.map do |line|
80
+ line.split(':', 2).last
81
+ end
82
+ else
83
+ []
84
+ end
85
+ end
86
+ end
87
+
88
+ def cert_id
89
+ @cert_id ||= OpenSSL::OCSP::CertificateId.new(
90
+ cert,
91
+ ca_cert,
92
+ OpenSSL::Digest::SHA1.new,
93
+ )
94
+ end
95
+
96
+ def verify_with_cache
97
+ handle_exceptions do
98
+ return false if ocsp_uris.empty?
99
+
100
+ resp = OcspCache.get(cert_id)
101
+ if resp
102
+ return return_ocsp_response(resp)
103
+ end
104
+
105
+ resp, errors = do_verify
106
+
107
+ if resp
108
+ OcspCache.set(cert_id, resp)
109
+ end
110
+
111
+ return_ocsp_response(resp, errors)
112
+ end
113
+ end
114
+
115
+ # @return [ true | false ] Whether the certificate was verified.
116
+ #
117
+ # @raise [ Error::ServerCertificateRevoked ] If the certificate was
118
+ # definitively revoked.
119
+ def verify
120
+ handle_exceptions do
121
+ return false if ocsp_uris.empty?
122
+
123
+ resp, errors = do_verify
124
+ return_ocsp_response(resp, errors)
125
+ end
126
+ end
127
+
128
+ private
129
+
130
+ def do_verify
131
+ # This synchronized array contains definitive pass/fail responses
132
+ # obtained from the responders. We'll take the first one but due to
133
+ # concurrency multiple responses may be produced and queued.
134
+ @resp_queue = Queue.new
135
+
136
+ # This synchronized array contains strings, one per responder, that
137
+ # explain why each responder hasn't produced a definitive response.
138
+ # These are concatenated and logged if none of the responders produced
139
+ # a definitive respnose, or if the main thread times out waiting for
140
+ # a definitive response (in which case some of the worker threads'
141
+ # diagnostics may be logged and some may not).
142
+ @resp_errors = Queue.new
143
+
144
+ @req = OpenSSL::OCSP::Request.new
145
+ @req.add_certid(cert_id)
146
+ @req.add_nonce
147
+ @serialized_req = @req.to_der
148
+
149
+ @outstanding_requests = ocsp_uris.count
150
+ @outstanding_requests_lock = Mutex.new
151
+
152
+ threads = ocsp_uris.map do |uri|
153
+ Thread.new do
154
+ verify_one_responder(uri)
155
+ end
156
+ end
157
+
158
+ resp = begin
159
+ ::Timeout.timeout(timeout) do
160
+ @resp_queue.shift
161
+ end
162
+ rescue ::Timeout::Error
163
+ nil
164
+ end
165
+
166
+ threads.map(&:kill)
167
+ threads.map(&:join)
168
+
169
+ [resp, @resp_errors]
170
+ end
171
+
172
+ def verify_one_responder(uri)
173
+ original_uri = uri
174
+ redirect_count = 0
175
+ http_response = nil
176
+ loop do
177
+ http_response = begin
178
+ uri = URI(uri)
179
+ Net::HTTP.start(uri.hostname, uri.port) do |http|
180
+ path = uri.path
181
+ if path.empty?
182
+ path = '/'
183
+ end
184
+ http.post(path, @serialized_req,
185
+ 'content-type' => 'application/ocsp-request')
186
+ end
187
+ rescue IOError, SystemCallError => e
188
+ @resp_errors << "OCSP request to #{report_uri(original_uri, uri)} failed: #{e.class}: #{e}"
189
+ return false
190
+ end
191
+
192
+ code = http_response.code.to_i
193
+ if (300..399).include?(code)
194
+ redirected_uri = http_response.header['location']
195
+ uri = ::URI.join(uri, redirected_uri)
196
+ redirect_count += 1
197
+ if redirect_count > 5
198
+ @resp_errors << "OCSP request to #{report_uri(original_uri, uri)} failed: too many redirects (6)"
199
+ return false
200
+ end
201
+ next
202
+ end
203
+
204
+ if code >= 400
205
+ @resp_errors << "OCSP request to #{report_uri(original_uri, uri)} failed with HTTP status code #{http_response.code}" + report_response_body(http_response.body)
206
+ return false
207
+ end
208
+
209
+ if code != 200
210
+ # There must be a body provided with the response, if one isn't
211
+ # provided the response cannot be verified.
212
+ @resp_errors << "OCSP request to #{report_uri(original_uri, uri)} failed with unexpected HTTP status code #{http_response.code}" + report_response_body(http_response.body)
213
+ return false
214
+ end
215
+
216
+ break
217
+ end
218
+
219
+ resp = OpenSSL::OCSP::Response.new(http_response.body).basic
220
+ unless resp.verify([ca_cert], cert_store)
221
+ # Ruby's OpenSSL binding discards error information - see
222
+ # https://github.com/ruby/openssl/issues/395
223
+ @resp_errors << "OCSP response from #{report_uri(original_uri, uri)} failed signature verification; set `OpenSSL.debug = true` to see why"
224
+ return false
225
+ end
226
+
227
+ if @req.check_nonce(resp) == 0
228
+ @resp_errors << "OCSP response from #{report_uri(original_uri, uri)} included invalid nonce"
229
+ return false
230
+ end
231
+
232
+ if resp.respond_to?(:find_response)
233
+ # Ruby 2.4+
234
+ resp = resp.find_response(cert_id)
235
+ # TODO make a new class instead of patching the stdlib one?
236
+ resp.instance_variable_set('@uri', uri)
237
+ resp.instance_variable_set('@original_uri', original_uri)
238
+ class << resp
239
+ attr_reader :uri, :original_uri
240
+ end
241
+ else
242
+ # Ruby 2.3
243
+ found = nil
244
+ resp.status.each do |_cert_id, cert_status, revocation_reason, revocation_time, this_update, next_update, extensions|
245
+ if _cert_id.cmp(cert_id)
246
+ found = OpenStruct.new(
247
+ cert_status: cert_status,
248
+ certid: _cert_id,
249
+ next_update: next_update,
250
+ this_update: this_update,
251
+ revocation_reason: revocation_reason,
252
+ revocation_time: revocation_time,
253
+ extensions: extensions,
254
+ uri: uri,
255
+ original_uri: original_uri,
256
+ )
257
+ class << found
258
+ # Unlike the stdlib method, this one doesn't accept
259
+ # any arguments.
260
+ def check_validity
261
+ now = Time.now
262
+ this_update <= now && next_update >= now
263
+ end
264
+ end
265
+ break
266
+ end
267
+ end
268
+ resp = found
269
+ end
270
+
271
+ unless resp
272
+ @resp_errors << "OCSP response from #{report_uri(original_uri, uri)} did not include information about the requested certificate"
273
+ return false
274
+ end
275
+
276
+ unless resp.check_validity
277
+ @resp_errors << "OCSP response from #{report_uri(original_uri, uri)} was invalid: this_update was in the future or next_update time has passed"
278
+ return false
279
+ end
280
+
281
+ unless [
282
+ OpenSSL::OCSP::V_CERTSTATUS_GOOD,
283
+ OpenSSL::OCSP::V_CERTSTATUS_REVOKED,
284
+ ].include?(resp.cert_status)
285
+ @resp_errors << "OCSP response from #{report_uri(original_uri, uri)} had a non-definitive status: #{resp.cert_status}"
286
+ return false
287
+ end
288
+
289
+ # Note this returns the redirected URI
290
+ @resp_queue << resp
291
+ rescue => exc
292
+ Utils.warn_bg_exception("Error performing OCSP verification for '#{host_name}' via '#{uri}'", exc,
293
+ logger: options[:logger],
294
+ log_prefix: options[:log_prefix],
295
+ bg_error_backtrace: options[:bg_error_backtrace],
296
+ )
297
+ false
298
+ ensure
299
+ @outstanding_requests_lock.synchronize do
300
+ @outstanding_requests -= 1
301
+ if @outstanding_requests == 0
302
+ @resp_queue << nil
303
+ end
304
+ end
305
+ end
306
+
307
+ def return_ocsp_response(resp, errors = nil)
308
+ if resp
309
+ if resp.cert_status == OpenSSL::OCSP::V_CERTSTATUS_REVOKED
310
+ raise_revoked_error(resp)
311
+ end
312
+ true
313
+ else
314
+ reasons = []
315
+ errors.length.times do
316
+ reasons << errors.shift
317
+ end
318
+ if reasons.empty?
319
+ msg = "No responses from responders: #{ocsp_uris.join(', ')} within #{timeout} seconds"
320
+ else
321
+ msg = "For responders #{ocsp_uris.join(', ')} with a timeout of #{timeout} seconds: #{reasons.join(', ')}"
322
+ end
323
+ log_warn("TLS certificate of '#{host_name}' could not be definitively verified via OCSP: #{msg}")
324
+ false
325
+ end
326
+ end
327
+
328
+ def handle_exceptions
329
+ begin
330
+ yield
331
+ rescue Error::ServerCertificateRevoked
332
+ raise
333
+ rescue => exc
334
+ Utils.warn_bg_exception(
335
+ "Error performing OCSP verification for '#{host_name}'",
336
+ exc,
337
+ **options)
338
+ false
339
+ end
340
+ end
341
+
342
+ def raise_revoked_error(resp)
343
+ if resp.uri == resp.original_uri
344
+ redirect = ''
345
+ else
346
+ redirect = " (redirected from #{resp.original_uri})"
347
+ end
348
+ raise Error::ServerCertificateRevoked, "TLS certificate of '#{host_name}' has been revoked according to '#{resp.uri}'#{redirect} for reason '#{resp.revocation_reason}' at '#{resp.revocation_time}'"
349
+ end
350
+
351
+ def report_uri(original_uri, uri)
352
+ if URI(uri) == URI(original_uri)
353
+ uri
354
+ else
355
+ "#{original_uri} (redirected to #{uri})"
356
+ end
357
+ end
358
+
359
+ def report_response_body(body)
360
+ if body
361
+ ": #{body}"
362
+ else
363
+ ''
364
+ end
365
+ end
366
+ end
367
+ end
368
+ end