mongo 2.20.0 → 2.20.2

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 (103) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +37 -1
  3. data/Rakefile +57 -21
  4. data/lib/mongo/config.rb +2 -2
  5. data/lib/mongo/retryable/base_worker.rb +28 -3
  6. data/lib/mongo/retryable/read_worker.rb +9 -8
  7. data/lib/mongo/retryable/write_worker.rb +4 -4
  8. data/lib/mongo/server/pending_connection.rb +19 -6
  9. data/lib/mongo/socket/ssl.rb +35 -5
  10. data/lib/mongo/version.rb +3 -18
  11. data/mongo.gemspec +8 -11
  12. data/spec/integration/client_side_encryption/range_explicit_encryption_prose_spec.rb +3 -0
  13. data/spec/integration/ocsp_verifier_spec.rb +30 -99
  14. data/spec/integration/reconnect_spec.rb +8 -2
  15. data/spec/integration/retryable_reads_errors_spec.rb +35 -23
  16. data/spec/integration/sdam_error_handling_spec.rb +2 -0
  17. data/spec/integration/srv_monitoring_spec.rb +2 -3
  18. data/spec/integration/srv_spec.rb +0 -4
  19. data/spec/lite_spec_helper.rb +0 -10
  20. data/spec/mongo/collection_crud_spec.rb +1 -0
  21. data/spec/mongo/server/connection_spec.rb +22 -0
  22. data/spec/shared/CANDIDATE.md +28 -0
  23. data/spec/shared/lib/mrss/docker_runner.rb +1 -1
  24. data/spec/shared/lib/mrss/release/candidate.rb +281 -0
  25. data/spec/shared/lib/mrss/release/product_data.rb +144 -0
  26. data/spec/shared/lib/mrss/server_version_registry.rb +1 -1
  27. data/spec/shared/lib/tasks/candidate.rake +64 -0
  28. data/spec/shared/share/Dockerfile.erb +4 -34
  29. data/spec/shared/shlib/distro.sh +10 -0
  30. data/spec/shared/shlib/server.sh +33 -27
  31. data/spec/shared/shlib/set_env.sh +6 -42
  32. data/spec/spec_tests/data/client_side_encryption/fle2v2-BypassQueryAnalysis.yml +1 -0
  33. data/spec/spec_tests/data/client_side_encryption/fle2v2-Compact.yml +1 -0
  34. data/spec/spec_tests/data/client_side_encryption/fle2v2-CreateCollection.yml +1 -0
  35. data/spec/spec_tests/data/client_side_encryption/fle2v2-DecryptExistingData.yml +1 -0
  36. data/spec/spec_tests/data/client_side_encryption/fle2v2-Delete.yml +1 -0
  37. data/spec/spec_tests/data/client_side_encryption/fle2v2-EncryptedFields-vs-EncryptedFieldsMap.yml +1 -0
  38. data/spec/spec_tests/data/client_side_encryption/fle2v2-EncryptedFields-vs-jsonSchema.yml +1 -0
  39. data/spec/spec_tests/data/client_side_encryption/fle2v2-EncryptedFieldsMap-defaults.yml +1 -0
  40. data/spec/spec_tests/data/client_side_encryption/fle2v2-FindOneAndUpdate.yml +1 -0
  41. data/spec/spec_tests/data/client_side_encryption/fle2v2-InsertFind-Indexed.yml +1 -0
  42. data/spec/spec_tests/data/client_side_encryption/fle2v2-InsertFind-Unindexed.yml +1 -0
  43. data/spec/spec_tests/data/client_side_encryption/fle2v2-MissingKey.yml +1 -0
  44. data/spec/spec_tests/data/client_side_encryption/fle2v2-NoEncryption.yml +1 -0
  45. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-Aggregate.yml +1 -0
  46. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-Correctness.yml +1 -0
  47. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-Delete.yml +1 -0
  48. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-FindOneAndUpdate.yml +1 -0
  49. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-InsertFind.yml +1 -0
  50. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-Update.yml +1 -0
  51. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-Aggregate.yml +1 -0
  52. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-Correctness.yml +1 -0
  53. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-Delete.yml +1 -0
  54. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-FindOneAndUpdate.yml +1 -0
  55. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-InsertFind.yml +1 -0
  56. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-Update.yml +1 -0
  57. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-Aggregate.yml +1 -0
  58. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-Correctness.yml +1 -0
  59. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-Delete.yml +1 -0
  60. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-FindOneAndUpdate.yml +1 -0
  61. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-InsertFind.yml +1 -0
  62. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-Update.yml +1 -0
  63. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-Aggregate.yml +1 -0
  64. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-Correctness.yml +1 -0
  65. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-Delete.yml +1 -0
  66. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-FindOneAndUpdate.yml +1 -0
  67. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-InsertFind.yml +1 -0
  68. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-Update.yml +1 -0
  69. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-Aggregate.yml +1 -0
  70. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-Correctness.yml +1 -0
  71. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-Delete.yml +1 -0
  72. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-FindOneAndUpdate.yml +1 -0
  73. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-InsertFind.yml +1 -0
  74. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-Update.yml +1 -0
  75. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-Aggregate.yml +1 -0
  76. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-Correctness.yml +1 -0
  77. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-Delete.yml +1 -0
  78. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-FindOneAndUpdate.yml +1 -0
  79. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-InsertFind.yml +1 -0
  80. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-Update.yml +1 -0
  81. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-Aggregate.yml +1 -0
  82. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-Correctness.yml +1 -0
  83. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-Delete.yml +1 -0
  84. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-FindOneAndUpdate.yml +1 -0
  85. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-InsertFind.yml +1 -0
  86. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-Update.yml +1 -0
  87. data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-WrongType.yml +1 -0
  88. data/spec/spec_tests/data/client_side_encryption/fle2v2-Update.yml +1 -0
  89. data/spec/spec_tests/data/client_side_encryption/fle2v2-validatorAndPartialFieldExpression.yml +2 -1
  90. data/spec/spec_tests/data/crud_unified/aggregate-write-readPreference.yml +2 -0
  91. data/spec/spec_tests/data/crud_unified/db-aggregate-write-readPreference.yml +2 -0
  92. data/spec/spec_tests/transactions_unified_spec.rb +2 -1
  93. data/spec/support/certificates/atlas-ocsp-ca.crt +89 -77
  94. data/spec/support/certificates/atlas-ocsp.crt +117 -122
  95. data/spec/support/certificates/retrieve-atlas-cert +1 -1
  96. data/spec/support/common_shortcuts.rb +19 -37
  97. data/spec/support/constraints.rb +10 -0
  98. metadata +24 -47
  99. checksums.yaml.gz.sig +0 -0
  100. data/spec/support/dns.rb +0 -16
  101. data/spec/support/faas/app/aws_lambda/mongodb/Gemfile.lock +0 -19
  102. data.tar.gz.sig +0 -0
  103. metadata.gz.sig +0 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 281c66c9e5d92b0eb8040c342bb0dfcf554c9b5420ec803bc68748472d74a1ae
4
- data.tar.gz: 423f31b24accb3ce674ea28a7aa8fcaf028730eb1370187c23f5304ba30698f6
3
+ metadata.gz: 8ccd08d09b5ee60c5da82105ce4bb165f1324e475e9f476f956d08a677a503b9
4
+ data.tar.gz: d055b854eb8f822e37ca7002bef5e3625c6ddf7052c06113c0568b0ca275eae6
5
5
  SHA512:
6
- metadata.gz: 4f414d30bf15f7c32ac9181e25e707caa80447975690fc6c0eb9d8fa37a784f8c4d21125b385d5a6d7efdb4bd751ee2c9c4bea313adbf78d20de3b019c25ac8a
7
- data.tar.gz: 7a83ba71f6a36d886769d755fb5ea45e6d1fae37bd92b490c58e9f643f51c25d5a489ed79928d67975f067d258c738f54cc3e5caecca6eb0bad2e2bf49d81613
6
+ metadata.gz: 291abbb2324d615f2061a7f924e31c7020c7da0541ff5a5b115a9c3edb7b4a74a921ff3e226a50bb6c74fb9f8f79f8343f14ea17f701b6796fde2dc774f0c915
7
+ data.tar.gz: 792ed0dd04ed391c9b644fa13e65d4282182cac3e54af1f1a4830a20d6ed30c6f6eecef0c0e8d3cbab0c02bef907065a70568fa44b346bb8d947cc02a6a5abb6
data/README.md CHANGED
@@ -5,7 +5,43 @@ MongoDB Ruby Driver
5
5
 
6
6
  The officially supported Ruby driver for [MongoDB](https://www.mongodb.org/).
7
7
 
8
- The Ruby driver supports Ruby 2.5-3.0 and JRuby 9.2.
8
+ The Ruby driver supports Ruby 2.7-3.3 and JRuby 9.3-9.4.
9
+
10
+ ## Installation
11
+
12
+ Install via RubyGems, either via the command-line for ad-hoc uses:
13
+
14
+ $ gem install mongo
15
+
16
+ Or via a Gemfile for more general use:
17
+
18
+ gem 'mongo'
19
+
20
+ ### Release Integrity
21
+
22
+ Each release of the MongoDB Ruby driver after version 2.20.0 has been automatically built and signed using the team's GPG key.
23
+
24
+ To verify the driver's gem file:
25
+
26
+ 1. [Download the GPG key](https://pgp.mongodb.com/ruby-driver.asc).
27
+ 2. Import the key into your GPG keyring with `gpg --import ruby-driver.asc`.
28
+ 3. Download the gem file (if you don't already have it). You can download it from RubyGems with `gem fetch mongo`, or you can download it from the [releases page](https://github.com/mongodb/mongo-ruby-driver/releases) on GitHub.
29
+ 4. Download the corresponding detached signature file from the [same release](https://github.com/mongodb/mongo-ruby-driver/releases). Look at the bottom of the release that corresponds to the gem file, under the 'Assets' list, for a `.sig` file with the same version number as the gem you wish to install.
30
+ 5. Verify the gem with `gpg --verify mongo-X.Y.Z.gem.sig mongo-X.Y.Z.gem` (replacing `X.Y.Z` with the actual version number).
31
+
32
+ You are looking for text like "Good signature from "MongoDB Ruby Driver Release Signing Key <packaging@mongodb.com>" in the output. If you see that, the signature was found to correspond to the given gem file.
33
+
34
+ (Note that other output, like "This key is not certified with a trusted signature!", is related to *web of trust* and depends on how strongly you, personally, trust the `ruby-driver.asc` key that you downloaded from us. To learn more, see https://www.gnupg.org/gph/en/manual/x334.html)
35
+
36
+ ### Why not use RubyGems' gem-signing functionality?
37
+
38
+ RubyGems' own gem signing is problematic, most significantly because there is no established chain of trust related to the keys used to sign gems. RubyGems' own documentation admits that "this method of signing gems is not widely used" (see https://guides.rubygems.org/security/). Discussions about this in the RubyGems community have been off-and-on for more than a decade, and while a solution will eventually arrive, we have settled on using GPG instead for the following reasons:
39
+
40
+ 1. Many of the other driver teams at MongoDB are using GPG to sign their product releases. Consistency with the other teams means that we can reuse existing tooling for our own product releases.
41
+ 2. GPG is widely available and has existing tools and procedures for dealing with web of trust (though they are admittedly quite arcane and intimidating to the uninitiated, unfortunately).
42
+
43
+ Ultimately, most users do not bother to verify gems, and will not be impacted by our choice of GPG over RubyGems' native method.
44
+
9
45
 
10
46
  ## Documentation
11
47
 
data/Rakefile CHANGED
@@ -2,17 +2,16 @@
2
2
  # rubocop:todo all
3
3
 
4
4
  require 'bundler'
5
- require 'bundler/gem_tasks'
6
5
  require 'rspec/core/rake_task'
7
- # TODO move the mongo require into the individual tasks that actually need it
8
- require 'mongo'
6
+
7
+ if File.exist?('./spec/shared/lib/tasks/candidate.rake')
8
+ load 'spec/shared/lib/tasks/candidate.rake'
9
+ end
9
10
 
10
11
  ROOT = File.expand_path(File.join(File.dirname(__FILE__)))
11
12
 
12
13
  $: << File.join(ROOT, 'spec/shared/lib')
13
14
 
14
- require 'mrss/spec_organizer'
15
-
16
15
  CLASSIFIERS = [
17
16
  [%r,^mongo/server,, :unit_server],
18
17
  [%r,^mongo,, :unit],
@@ -33,18 +32,63 @@ RUN_PRIORITY = %i(
33
32
  spec spec_sdam_integration
34
33
  )
35
34
 
36
- tasks = Rake.application.instance_variable_get('@tasks')
37
- tasks['release:do'] = tasks.delete('release')
38
-
39
35
  RSpec::Core::RakeTask.new(:spec) do |t|
40
36
  #t.rspec_opts = "--profile 5" if ENV['CI']
41
37
  end
42
38
 
43
39
  task :default => ['spec:prepare', :spec]
44
40
 
41
+ desc 'Build the gem'
42
+ task :build do
43
+ command = %w[ gem build ]
44
+ command << "--output=#{ENV['GEM_FILE_NAME']}" if ENV['GEM_FILE_NAME']
45
+ command << (ENV['GEMSPEC'] || 'mongo.gemspec')
46
+ system(*command)
47
+ end
48
+
49
+ # `rake version` is used by the deployment system so get the release version
50
+ # of the product beng deployed. It must do nothing more than just print the
51
+ # product version number.
52
+ #
53
+ # See the mongodb-labs/driver-github-tools/ruby/publish Github action.
54
+ desc "Print the current value of Mongo::VERSION"
55
+ task :version do
56
+ require 'mongo/version'
57
+
58
+ puts Mongo::VERSION
59
+ end
60
+
61
+ # overrides the default Bundler-provided `release` task, which also
62
+ # builds the gem. Our release process assumes the gem has already
63
+ # been built (and signed via GPG), so we just need `rake release` to
64
+ # push the gem to rubygems.
65
+ task :release do
66
+ require 'mongo/version'
67
+
68
+ if ENV['GITHUB_ACTION'].nil?
69
+ abort <<~WARNING
70
+ `rake release` must be invoked from the `Driver Release` GitHub action,
71
+ and must not be invoked locally. This ensures the gem is properly signed
72
+ and distributed by the appropriate user.
73
+
74
+ Note that it is the `rubygems/release-gem@v1` step in the `Driver Release`
75
+ action that invokes this task. Do not rename or remove this task, or the
76
+ release-gem step will fail. Reimplement this task with caution.
77
+
78
+ mongo-#{Mongo::VERSION}.gem was NOT pushed to RubyGems.
79
+ WARNING
80
+ end
81
+
82
+ system 'gem', 'push', "mongo-#{Mongo::VERSION}.gem"
83
+ end
84
+
85
+ task :mongo do
86
+ require 'mongo'
87
+ end
88
+
45
89
  namespace :spec do
46
90
  desc 'Creates necessary user accounts in the cluster'
47
- task :prepare do
91
+ task prepare: :mongo do
48
92
  $: << File.join(File.dirname(__FILE__), 'spec')
49
93
 
50
94
  require 'support/utils'
@@ -53,7 +97,7 @@ namespace :spec do
53
97
  end
54
98
 
55
99
  desc 'Waits for sessions to be available in the deployment'
56
- task :wait_for_sessions do
100
+ task wait_for_sessions: :mongo do
57
101
  $: << File.join(File.dirname(__FILE__), 'spec')
58
102
 
59
103
  require 'support/utils'
@@ -77,7 +121,7 @@ namespace :spec do
77
121
  end
78
122
 
79
123
  desc 'Prints configuration used by the test suite'
80
- task :config do
124
+ task config: :mongo do
81
125
  $: << File.join(File.dirname(__FILE__), 'spec')
82
126
 
83
127
  # Since this task is usually used for troubleshooting of test suite
@@ -90,6 +134,8 @@ namespace :spec do
90
134
  end
91
135
 
92
136
  def spec_organizer
137
+ require 'mrss/spec_organizer'
138
+
93
139
  Mrss::SpecOrganizer.new(
94
140
  root: ROOT,
95
141
  classifiers: CLASSIFIERS,
@@ -109,16 +155,6 @@ namespace :spec do
109
155
  end
110
156
  end
111
157
 
112
- namespace :release do
113
- task :check_private_key do
114
- unless File.exist?('gem-private_key.pem')
115
- raise "No private key present, cannot release"
116
- end
117
- end
118
- end
119
-
120
- task :release => ['release:check_private_key', 'release:do']
121
-
122
158
  desc 'Build and validate the evergreen config'
123
159
  task eg: %w[ eg:build eg:validate ]
124
160
 
data/lib/mongo/config.rb CHANGED
@@ -16,7 +16,7 @@ module Mongo
16
16
 
17
17
  # When this flag is off, an aggregation done on a view will be executed over
18
18
  # the documents included in that view, instead of all documents in the
19
- # collection. When this flag is on, the view fiter is ignored.
19
+ # collection. When this flag is on, the view filter is ignored.
20
20
  option :broken_view_aggregate, default: true
21
21
 
22
22
  # When this flag is set to false, the view options will be correctly
@@ -24,7 +24,7 @@ module Mongo
24
24
  option :broken_view_options, default: true
25
25
 
26
26
  # When this flag is set to true, the update and replace methods will
27
- # validate the paramters and raise an error if they are invalid.
27
+ # validate the parameters and raise an error if they are invalid.
28
28
  option :validate_update_replace, default: false
29
29
 
30
30
  # Set the configuration options.
@@ -49,7 +49,8 @@ module Mongo
49
49
 
50
50
  private
51
51
 
52
- # Indicate which exception classes that are generally retryable.
52
+ # Indicate which exception classes that are generally retryable
53
+ # when using modern retries mechanism.
53
54
  #
54
55
  # @return [ Array<Mongo:Error> ] Array of exception classes that are
55
56
  # considered retryable.
@@ -58,18 +59,42 @@ module Mongo
58
59
  Error::ConnectionPerished,
59
60
  Error::ServerNotUsable,
60
61
  Error::SocketError,
61
- Error::SocketTimeoutError
62
+ Error::SocketTimeoutError,
62
63
  ].freeze
63
64
  end
64
65
 
66
+ # Indicate which exception classes that are generally retryable
67
+ # when using legacy retries mechanism.
68
+ #
69
+ # @return [ Array<Mongo:Error> ] Array of exception classes that are
70
+ # considered retryable.
71
+ def legacy_retryable_exceptions
72
+ [
73
+ Error::ConnectionPerished,
74
+ Error::ServerNotUsable,
75
+ Error::SocketError,
76
+ Error::SocketTimeoutError,
77
+ Error::PoolClearedError,
78
+ Error::PoolPausedError,
79
+ ].freeze
80
+ end
81
+
82
+
65
83
  # Tests to see if the given exception instance is of a type that can
66
- # be retried.
84
+ # be retried with modern retry mechanism.
67
85
  #
68
86
  # @return [ true | false ] true if the exception is retryable.
69
87
  def is_retryable_exception?(e)
70
88
  retryable_exceptions.any? { |klass| klass === e }
71
89
  end
72
90
 
91
+ # Tests to see if the given exception instance is of a type that can
92
+ # be retried with legacy retry mechanism.
93
+ #
94
+ # @return [ true | false ] true if the exception is retryable.
95
+ def is_legacy_retryable_exception?(e)
96
+ legacy_retryable_exceptions.any? { |klass| klass === e }
97
+ end
73
98
  # Logs the given deprecation warning the first time it is called for a
74
99
  # given key; after that, it does nothing when given the same key.
75
100
  def deprecation_warning(key, warning)
@@ -198,7 +198,7 @@ module Mongo
198
198
  raise e if !is_retryable_exception?(e) && !e.write_retryable?
199
199
  retry_read(e, session, server_selector, failed_server: server, &block)
200
200
  end
201
-
201
+
202
202
  # Attempts to do a "legacy" read with retry. The operation will be
203
203
  # attempted multiple times, up to the client's `max_read_retries`
204
204
  # setting.
@@ -213,17 +213,18 @@ module Mongo
213
213
  def legacy_read_with_retry(session, server_selector, &block)
214
214
  attempt = attempt ? attempt + 1 : 1
215
215
  yield select_server(cluster, server_selector, session)
216
- rescue *retryable_exceptions, Error::OperationFailure, Error::PoolError => e
216
+ rescue *legacy_retryable_exceptions, Error::OperationFailure => e
217
217
  e.add_notes('legacy retry', "attempt #{attempt}")
218
-
219
- if is_retryable_exception?(e)
218
+
219
+ if is_legacy_retryable_exception?(e)
220
+
220
221
  raise e if attempt > client.max_read_retries || session&.in_transaction?
221
222
  elsif e.retryable? && !session&.in_transaction?
222
223
  raise e if attempt > client.max_read_retries
223
224
  else
224
225
  raise e
225
226
  end
226
-
227
+
227
228
  log_retry(e, message: 'Legacy read retry')
228
229
  sleep(client.read_retry_interval) unless is_retryable_exception?(e)
229
230
  retry
@@ -261,7 +262,7 @@ module Mongo
261
262
  # @param [ Mongo::Server ] failed_server The server on which the original
262
263
  # operation failed.
263
264
  # @param [ Proc ] block The block to execute.
264
- #
265
+ #
265
266
  # @return [ Result ] The result of the operation.
266
267
  def retry_read(original_error, session, server_selector, failed_server: nil, &block)
267
268
  begin
@@ -270,9 +271,9 @@ module Mongo
270
271
  original_error.add_note("later retry failed: #{e.class}: #{e}")
271
272
  raise original_error
272
273
  end
273
-
274
+
274
275
  log_retry(original_error, message: 'Read retry')
275
-
276
+
276
277
  begin
277
278
  yield server, true
278
279
  rescue *retryable_exceptions => e
@@ -104,7 +104,7 @@ module Mongo
104
104
  session = context.session
105
105
  server = select_server(cluster, ServerSelector.primary, session)
106
106
  options = session&.client&.options || {}
107
-
107
+
108
108
  if options[:retry_writes]
109
109
  begin
110
110
  server.with_connection(connection_global_id: context.connection_global_id) do |connection|
@@ -219,7 +219,7 @@ module Mongo
219
219
  def modern_write_with_retry(session, server, context, &block)
220
220
  txn_num = nil
221
221
  connection_succeeded = false
222
-
222
+
223
223
  server.with_connection(connection_global_id: context.connection_global_id) do |connection|
224
224
  connection_succeeded = true
225
225
 
@@ -264,7 +264,7 @@ module Mongo
264
264
  # a socket error or a not master error should have marked the respective
265
265
  # server unknown). Here we just need to wait for server selection.
266
266
  server = select_server(cluster, ServerSelector.primary, session, failed_server)
267
-
267
+
268
268
  unless server.retry_writes?
269
269
  # Do not need to add "modern retry" here, it should already be on
270
270
  # the first exception.
@@ -278,7 +278,7 @@ module Mongo
278
278
  # special marker class to bypass the ordinarily applicable rescues.
279
279
  raise Error::RaiseOriginalError
280
280
  end
281
-
281
+
282
282
  log_retry(original_error, message: 'Write retry')
283
283
  server.with_connection(connection_global_id: context.connection_global_id) do |connection|
284
284
  yield(connection, txn_num, context)
@@ -110,6 +110,24 @@ module Mongo
110
110
 
111
111
  private
112
112
 
113
+ # Sends the hello command to the server, then receive and deserialize
114
+ # the response.
115
+ #
116
+ # This method is extracted to be mocked in the tests.
117
+ #
118
+ # @param [ Protocol::Message ] Command that should be sent to a server
119
+ # for handshake purposes.
120
+ #
121
+ # @return [ Mongo::Protocol::Reply ] Deserialized server response.
122
+ def get_handshake_response(hello_command)
123
+ @server.round_trip_time_averager.measure do
124
+ add_server_diagnostics do
125
+ socket.write(hello_command.serialize.to_s)
126
+ Protocol::Message.deserialize(socket, Protocol::Message::MAX_MESSAGE_SIZE)
127
+ end
128
+ end
129
+ end
130
+
113
131
  # @param [ BSON::Document | nil ] speculative_auth_doc The document to
114
132
  # provide in speculativeAuthenticate field of handshake command.
115
133
  #
@@ -131,12 +149,7 @@ module Mongo
131
149
  doc = nil
132
150
  @server.handle_handshake_failure! do
133
151
  begin
134
- response = @server.round_trip_time_averager.measure do
135
- add_server_diagnostics do
136
- socket.write(hello_command.serialize.to_s)
137
- Protocol::Message.deserialize(socket, Protocol::Message::MAX_MESSAGE_SIZE)
138
- end
139
- end
152
+ response = get_handshake_response(hello_command)
140
153
  result = Operation::Result.new([response])
141
154
  result.validate!
142
155
  doc = result.documents.first
@@ -23,6 +23,7 @@ module Mongo
23
23
  # @since 2.0.0
24
24
  class SSL < Socket
25
25
  include OpenSSL
26
+ include Loggable
26
27
 
27
28
  # Initializes a new TLS socket.
28
29
  #
@@ -287,7 +288,7 @@ module Mongo
287
288
  # for instance, if there is no newline between two certificates
288
289
  # this code will extract them both but OpenSSL fails in this situation.
289
290
  if cert_text
290
- certs = cert_text.scan(/-----BEGIN CERTIFICATE-----(?:.|\n)+?-----END CERTIFICATE-----/)
291
+ certs = extract_certs(cert_text)
291
292
  if certs.length > 1
292
293
  context.cert = OpenSSL::X509::Certificate.new(certs.shift)
293
294
  context.extra_chain_cert = certs.map do |cert|
@@ -363,12 +364,15 @@ module Mongo
363
364
  end
364
365
 
365
366
  def verify_ocsp_endpoint!(socket)
366
- unless verify_ocsp_endpoint?
367
- return
368
- end
367
+ return unless verify_ocsp_endpoint?
369
368
 
370
369
  cert = socket.peer_cert
371
- ca_cert = socket.peer_cert_chain.last
370
+ ca_cert = find_issuer(cert, socket.peer_cert_chain)
371
+
372
+ unless ca_cert
373
+ log_warn("TLS certificate of '#{host_name}' could not be definitively verified via OCSP: issuer certificate not found in the chain.")
374
+ return
375
+ end
372
376
 
373
377
  verifier = OcspVerifier.new(@host_name, cert, ca_cert, context.cert_store,
374
378
  **Utils.shallow_symbolize_keys(options))
@@ -390,6 +394,32 @@ module Mongo
390
394
  hook.call(@context)
391
395
  end
392
396
  end
397
+
398
+ BEGIN_CERT = "-----BEGIN CERTIFICATE-----"
399
+ END_CERT = "-----END CERTIFICATE-----"
400
+
401
+ # This was originally a scan + regex, but the regex was particularly
402
+ # inefficient and was flagged as a concern by static analysis.
403
+ def extract_certs(text)
404
+ [].tap do |list|
405
+ pos = 0
406
+
407
+ while (begin_idx = text.index(BEGIN_CERT, pos))
408
+ end_idx = text.index(END_CERT, begin_idx)
409
+ break unless end_idx
410
+
411
+ end_idx += END_CERT.length
412
+ list.push(text[begin_idx...end_idx])
413
+
414
+ pos = end_idx
415
+ end
416
+ end
417
+ end
418
+
419
+ # Find the issuer certificate in the chain.
420
+ def find_issuer(cert, cert_chain)
421
+ cert_chain.find { |c| c.subject == cert.issuer }
422
+ end
393
423
  end
394
424
  end
395
425
  end
data/lib/mongo/version.rb CHANGED
@@ -1,24 +1,9 @@
1
1
  # frozen_string_literal: true
2
- # rubocop:todo all
3
-
4
- # Copyright (C) 2014-2020 MongoDB Inc.
5
- #
6
- # Licensed under the Apache License, Version 2.0 (the "License");
7
- # you may not use this file except in compliance with the License.
8
- # You may obtain a copy of the License at
9
- #
10
- # http://www.apache.org/licenses/LICENSE-2.0
11
- #
12
- # Unless required by applicable law or agreed to in writing, software
13
- # distributed under the License is distributed on an "AS IS" BASIS,
14
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
- # See the License for the specific language governing permissions and
16
- # limitations under the License.
17
2
 
18
3
  module Mongo
19
-
20
4
  # The current version of the driver.
21
5
  #
22
- # @since 2.0.0
23
- VERSION = '2.20.0'.freeze
6
+ # Note that this file is automatically updated via `rake candidate:create`.
7
+ # Manual changes to this file will be overwritten by that rake task.
8
+ VERSION = '2.20.2'
24
9
  end
data/mongo.gemspec CHANGED
@@ -9,28 +9,25 @@ Gem::Specification.new do |s|
9
9
  s.name = 'mongo'
10
10
  s.version = Mongo::VERSION
11
11
  s.platform = Gem::Platform::RUBY
12
- s.authors = ["The MongoDB Ruby Team"]
13
- s.email = "dbx-ruby@mongodb.com"
12
+ s.authors = [ 'The MongoDB Ruby Team' ]
13
+ s.email = 'dbx-ruby@mongodb.com'
14
14
  s.homepage = 'https://mongodb.com/docs/ruby-driver/'
15
15
  s.summary = 'Ruby driver for MongoDB'
16
- s.description = 'A Ruby driver for MongoDB'
17
16
  s.license = 'Apache-2.0'
17
+ s.description = <<~DESC
18
+ A pure-Ruby driver for connecting to, querying, and manipulating MongoDB
19
+ databases. Officially developed and supported by MongoDB, with love for
20
+ the Ruby community.
21
+ DESC
18
22
 
19
23
  s.metadata = {
20
24
  'bug_tracker_uri' => 'https://jira.mongodb.org/projects/RUBY',
21
25
  'changelog_uri' => 'https://github.com/mongodb/mongo-ruby-driver/releases',
22
- 'documentation_uri' => 'https://mongodb.com/docs/ruby-driver/',
23
26
  'homepage_uri' => 'https://mongodb.com/docs/ruby-driver/',
27
+ 'documentation_uri' => 'https://mongodb.com/docs/ruby-driver/current/tutorials/quick-start/',
24
28
  'source_code_uri' => 'https://github.com/mongodb/mongo-ruby-driver',
25
29
  }
26
30
 
27
- if File.exist?('gem-private_key.pem')
28
- s.signing_key = 'gem-private_key.pem'
29
- s.cert_chain = ['gem-public_cert.pem']
30
- else
31
- warn "[#{s.name}] Warning: No private key present, creating unsigned gem."
32
- end
33
-
34
31
  s.files = Dir.glob('{bin,lib,spec}/**/*')
35
32
  s.files += %w[mongo.gemspec LICENSE README.md CONTRIBUTING.md Rakefile]
36
33
  s.test_files = Dir.glob('spec/**/*')
@@ -7,6 +7,9 @@ require 'spec_helper'
7
7
  # rubocop:disable RSpec/ExampleLength
8
8
  describe 'Range Explicit Encryption' do
9
9
  min_server_version '7.0.0-rc0'
10
+ # https://jira.mongodb.org/browse/RUBY-3457
11
+ max_server_version '7.99.99'
12
+
10
13
  require_libmongocrypt
11
14
  include_context 'define shared FLE helpers'
12
15