mongo 2.20.1 → 2.21.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.
- checksums.yaml +4 -4
- data/README.md +3 -0
- data/Rakefile +2 -2
- data/lib/mongo/address.rb +22 -3
- data/lib/mongo/auth/aws/credentials_retriever.rb +70 -17
- data/lib/mongo/auth/base.rb +1 -1
- data/lib/mongo/bulk_write.rb +35 -2
- data/lib/mongo/client.rb +38 -6
- data/lib/mongo/client_encryption.rb +6 -3
- data/lib/mongo/cluster/reapers/cursor_reaper.rb +6 -1
- data/lib/mongo/cluster/sdam_flow.rb +20 -7
- data/lib/mongo/cluster.rb +14 -4
- data/lib/mongo/collection/helpers.rb +1 -1
- data/lib/mongo/collection/view/aggregation/behavior.rb +131 -0
- data/lib/mongo/collection/view/aggregation.rb +33 -99
- data/lib/mongo/collection/view/builder/aggregation.rb +1 -7
- data/lib/mongo/collection/view/change_stream.rb +80 -27
- data/lib/mongo/collection/view/iterable.rb +76 -60
- data/lib/mongo/collection/view/map_reduce.rb +25 -8
- data/lib/mongo/collection/view/readable.rb +79 -30
- data/lib/mongo/collection/view/writable.rb +109 -48
- data/lib/mongo/collection/view.rb +43 -3
- data/lib/mongo/collection.rb +158 -23
- data/lib/mongo/crypt/auto_encrypter.rb +4 -6
- data/lib/mongo/crypt/binding.rb +4 -4
- data/lib/mongo/crypt/context.rb +20 -14
- data/lib/mongo/crypt/encryption_io.rb +56 -26
- data/lib/mongo/crypt/explicit_encrypter.rb +49 -20
- data/lib/mongo/crypt/explicit_encryption_context.rb +17 -11
- data/lib/mongo/crypt/kms/azure/credentials_retriever.rb +22 -6
- data/lib/mongo/crypt/kms/gcp/credentials_retriever.rb +29 -4
- data/lib/mongo/csot_timeout_holder.rb +119 -0
- data/lib/mongo/cursor/kill_spec.rb +5 -2
- data/lib/mongo/cursor/nontailable.rb +27 -0
- data/lib/mongo/cursor.rb +86 -24
- data/lib/mongo/cursor_host.rb +82 -0
- data/lib/mongo/database/view.rb +81 -14
- data/lib/mongo/database.rb +88 -18
- data/lib/mongo/error/operation_failure.rb +209 -204
- data/lib/mongo/error/server_timeout_error.rb +12 -0
- data/lib/mongo/error/socket_timeout_error.rb +3 -1
- data/lib/mongo/error/timeout_error.rb +23 -0
- data/lib/mongo/error.rb +2 -0
- data/lib/mongo/grid/fs_bucket.rb +45 -12
- data/lib/mongo/grid/stream/read.rb +15 -1
- data/lib/mongo/grid/stream/write.rb +21 -4
- data/lib/mongo/index/view.rb +77 -16
- data/lib/mongo/operation/context.rb +40 -2
- data/lib/mongo/operation/create_search_indexes/op_msg.rb +2 -2
- data/lib/mongo/operation/delete/op_msg.rb +2 -1
- data/lib/mongo/operation/drop_search_index/op_msg.rb +2 -2
- data/lib/mongo/operation/find/op_msg.rb +45 -0
- data/lib/mongo/operation/get_more/op_msg.rb +33 -0
- data/lib/mongo/operation/insert/op_msg.rb +3 -2
- data/lib/mongo/operation/insert/result.rb +4 -2
- data/lib/mongo/operation/list_collections/result.rb +1 -1
- data/lib/mongo/operation/map_reduce/result.rb +1 -1
- data/lib/mongo/operation/op_msg_base.rb +3 -1
- data/lib/mongo/operation/result.rb +26 -5
- data/lib/mongo/operation/shared/executable.rb +12 -1
- data/lib/mongo/operation/shared/op_msg_executable.rb +4 -1
- data/lib/mongo/operation/shared/response_handling.rb +3 -3
- data/lib/mongo/operation/shared/sessions_supported.rb +1 -1
- data/lib/mongo/operation/shared/timed.rb +52 -0
- data/lib/mongo/operation/shared/write.rb +4 -1
- data/lib/mongo/operation/update/op_msg.rb +2 -1
- data/lib/mongo/operation/update_search_index/op_msg.rb +2 -2
- data/lib/mongo/operation.rb +1 -0
- data/lib/mongo/protocol/message.rb +1 -4
- data/lib/mongo/protocol/msg.rb +2 -2
- data/lib/mongo/retryable/read_worker.rb +69 -29
- data/lib/mongo/retryable/write_worker.rb +49 -18
- data/lib/mongo/retryable.rb +8 -2
- data/lib/mongo/server/connection.rb +11 -5
- data/lib/mongo/server/connection_base.rb +22 -2
- data/lib/mongo/server/connection_pool.rb +32 -14
- data/lib/mongo/server/description/features.rb +1 -1
- data/lib/mongo/server/description.rb +18 -5
- data/lib/mongo/server/monitor.rb +7 -4
- data/lib/mongo/server/pending_connection.rb +7 -3
- data/lib/mongo/server/{round_trip_time_averager.rb → round_trip_time_calculator.rb} +25 -7
- data/lib/mongo/server.rb +11 -6
- data/lib/mongo/server_selector/base.rb +25 -9
- data/lib/mongo/session.rb +78 -9
- data/lib/mongo/socket/ssl.rb +109 -17
- data/lib/mongo/socket/tcp.rb +40 -6
- data/lib/mongo/socket.rb +154 -25
- data/lib/mongo/uri/options_mapper.rb +1 -0
- data/lib/mongo/version.rb +1 -1
- data/lib/mongo.rb +1 -0
- data/spec/atlas/atlas_connectivity_spec.rb +4 -0
- data/spec/atlas/operations_spec.rb +4 -0
- data/spec/integration/client_side_encryption/auto_encryption_mongocryptd_spawn_spec.rb +2 -1
- data/spec/integration/client_side_encryption/auto_encryption_spec.rb +494 -487
- data/spec/integration/client_side_encryption/on_demand_aws_credentials_spec.rb +1 -1
- data/spec/integration/client_side_encryption/range_explicit_encryption_prose_spec.rb +66 -22
- data/spec/integration/client_side_operations_timeout/encryption_prose_spec.rb +131 -0
- data/spec/integration/connection_pool_populator_spec.rb +2 -0
- data/spec/integration/cursor_pinning_spec.rb +15 -60
- data/spec/integration/cursor_reaping_spec.rb +1 -1
- data/spec/integration/docs_examples_spec.rb +1 -1
- data/spec/integration/operation_failure_code_spec.rb +1 -1
- data/spec/integration/operation_failure_message_spec.rb +3 -3
- data/spec/integration/retryable_errors_spec.rb +2 -2
- data/spec/integration/sdam_error_handling_spec.rb +2 -1
- data/spec/integration/search_indexes_prose_spec.rb +4 -0
- data/spec/integration/server_spec.rb +4 -3
- data/spec/integration/transactions_api_examples_spec.rb +2 -0
- data/spec/kerberos/kerberos_spec.rb +4 -0
- data/spec/lite_spec_helper.rb +3 -1
- data/spec/mongo/auth/user/view_spec.rb +1 -1
- data/spec/mongo/caching_cursor_spec.rb +1 -1
- data/spec/mongo/client_encryption_spec.rb +1 -0
- data/spec/mongo/client_spec.rb +158 -4
- data/spec/mongo/collection/view/aggregation_spec.rb +14 -39
- data/spec/mongo/collection/view/change_stream_spec.rb +3 -3
- data/spec/mongo/collection_spec.rb +5 -6
- data/spec/mongo/crypt/auto_encrypter_spec.rb +14 -12
- data/spec/mongo/crypt/data_key_context_spec.rb +3 -1
- data/spec/mongo/crypt/explicit_encryption_context_spec.rb +2 -2
- data/spec/mongo/crypt/handle_spec.rb +1 -1
- data/spec/mongo/cursor_spec.rb +26 -9
- data/spec/mongo/error/operation_failure_heavy_spec.rb +2 -2
- data/spec/mongo/operation/context_spec.rb +79 -0
- data/spec/mongo/operation/create/op_msg_spec.rb +106 -110
- data/spec/mongo/operation/delete/op_msg_spec.rb +6 -5
- data/spec/mongo/operation/find/op_msg_spec.rb +66 -0
- data/spec/mongo/operation/get_more/op_msg_spec.rb +65 -0
- data/spec/mongo/operation/insert/op_msg_spec.rb +128 -131
- data/spec/mongo/operation/shared/csot/examples.rb +113 -0
- data/spec/mongo/query_cache_spec.rb +243 -225
- data/spec/mongo/retryable_spec.rb +1 -0
- data/spec/mongo/server/round_trip_time_calculator_spec.rb +120 -0
- data/spec/mongo/socket/ssl_spec.rb +0 -10
- data/spec/runners/change_streams/test.rb +2 -2
- data/spec/runners/crud/operation.rb +1 -1
- data/spec/runners/crud/verifier.rb +3 -1
- data/spec/runners/transactions/operation.rb +4 -6
- data/spec/runners/unified/ambiguous_operations.rb +13 -0
- data/spec/runners/unified/assertions.rb +4 -0
- data/spec/runners/unified/change_stream_operations.rb +14 -24
- data/spec/runners/unified/crud_operations.rb +82 -59
- data/spec/runners/unified/ddl_operations.rb +38 -7
- data/spec/runners/unified/grid_fs_operations.rb +37 -2
- data/spec/runners/unified/support_operations.rb +43 -4
- data/spec/runners/unified/test.rb +22 -10
- data/spec/runners/unified.rb +1 -1
- data/spec/solo/clean_exit_spec.rb +2 -0
- data/spec/spec_tests/client_side_operations_timeout_spec.rb +15 -0
- data/spec/spec_tests/data/change_streams_unified/change-streams-clusterTime.yml +3 -1
- data/spec/spec_tests/data/change_streams_unified/change-streams-disambiguatedPaths.yml +3 -1
- data/spec/spec_tests/data/change_streams_unified/change-streams-errors.yml +3 -1
- data/spec/spec_tests/data/change_streams_unified/change-streams-pre_and_post_images.yml +1 -1
- data/spec/spec_tests/data/change_streams_unified/change-streams-resume-allowlist.yml +1 -1
- data/spec/spec_tests/data/change_streams_unified/change-streams-resume-errorLabels.yml +1 -1
- data/spec/spec_tests/data/change_streams_unified/change-streams-showExpandedEvents.yml +1 -1
- data/spec/spec_tests/data/client_side_encryption/badQueries.yml +2 -1
- data/spec/spec_tests/data/client_side_encryption/timeoutMS.yml +67 -0
- data/spec/spec_tests/data/client_side_operations_timeout/bulkWrite.yml +87 -0
- data/spec/spec_tests/data/client_side_operations_timeout/change-streams.yml +358 -0
- data/spec/spec_tests/data/client_side_operations_timeout/close-cursors.yml +129 -0
- data/spec/spec_tests/data/client_side_operations_timeout/command-execution.yml +250 -0
- data/spec/spec_tests/data/client_side_operations_timeout/convenient-transactions.yml +113 -0
- data/spec/spec_tests/data/client_side_operations_timeout/cursors.yml +70 -0
- data/spec/spec_tests/data/client_side_operations_timeout/deprecated-options.yml +3982 -0
- data/spec/spec_tests/data/client_side_operations_timeout/error-transformations.yml +96 -0
- data/spec/spec_tests/data/client_side_operations_timeout/global-timeoutMS.yml +3236 -0
- data/spec/spec_tests/data/client_side_operations_timeout/gridfs-advanced.yml +207 -0
- data/spec/spec_tests/data/client_side_operations_timeout/gridfs-delete.yml +152 -0
- data/spec/spec_tests/data/client_side_operations_timeout/gridfs-download.yml +182 -0
- data/spec/spec_tests/data/client_side_operations_timeout/gridfs-find.yml +100 -0
- data/spec/spec_tests/data/client_side_operations_timeout/gridfs-upload.yml +249 -0
- data/spec/spec_tests/data/client_side_operations_timeout/legacy-timeouts.yml +204 -0
- data/spec/spec_tests/data/client_side_operations_timeout/non-tailable-cursors.yml +307 -0
- data/spec/spec_tests/data/client_side_operations_timeout/override-collection-timeoutMS.yml +1877 -0
- data/spec/spec_tests/data/client_side_operations_timeout/override-operation-timeoutMS.yml +1918 -0
- data/spec/spec_tests/data/client_side_operations_timeout/retryability-legacy-timeouts.yml +1676 -0
- data/spec/spec_tests/data/client_side_operations_timeout/retryability-timeoutMS.yml +2824 -0
- data/spec/spec_tests/data/client_side_operations_timeout/sessions-inherit-timeoutMS.yml +168 -0
- data/spec/spec_tests/data/client_side_operations_timeout/sessions-override-operation-timeoutMS.yml +171 -0
- data/spec/spec_tests/data/client_side_operations_timeout/sessions-override-timeoutMS.yml +168 -0
- data/spec/spec_tests/data/client_side_operations_timeout/tailable-awaitData.yml +247 -0
- data/spec/spec_tests/data/client_side_operations_timeout/tailable-non-awaitData.yml +181 -0
- data/spec/spec_tests/data/crud_unified/aggregate-write-readPreference.yml +4 -0
- data/spec/spec_tests/data/crud_unified/db-aggregate-write-readPreference.yml +4 -0
- data/spec/spec_tests/data/crud_unified/find-test-all-options.yml +29 -0
- data/spec/spec_tests/server_selection_rtt_spec.rb +6 -6
- data/spec/support/certificates/atlas-ocsp-ca.crt +81 -83
- data/spec/support/certificates/atlas-ocsp.crt +107 -107
- data/spec/support/cluster_tools.rb +3 -3
- data/spec/support/common_shortcuts.rb +2 -2
- data/spec/support/crypt/encrypted_fields/range-encryptedFields-Date.json +1 -1
- data/spec/support/crypt/encrypted_fields/range-encryptedFields-DecimalNoPrecision.json +1 -1
- data/spec/support/crypt/encrypted_fields/range-encryptedFields-DecimalPrecision.json +1 -1
- data/spec/support/crypt/encrypted_fields/range-encryptedFields-DoubleNoPrecision.json +1 -1
- data/spec/support/crypt/encrypted_fields/range-encryptedFields-DoublePrecision.json +1 -1
- data/spec/support/crypt/encrypted_fields/range-encryptedFields-Int.json +1 -1
- data/spec/support/crypt/encrypted_fields/range-encryptedFields-Long.json +1 -1
- data/spec/support/shared/session.rb +2 -2
- data/spec/support/spec_setup.rb +2 -2
- data/spec/support/utils.rb +3 -1
- metadata +78 -91
- data/spec/mongo/server/round_trip_time_averager_spec.rb +0 -48
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-Aggregate.yml +0 -242
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-Correctness.yml +0 -423
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-Delete.yml +0 -183
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-FindOneAndUpdate.yml +0 -240
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-InsertFind.yml +0 -236
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Date-Update.yml +0 -253
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-Aggregate.yml +0 -1688
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-Correctness.yml +0 -294
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-Delete.yml +0 -906
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-FindOneAndUpdate.yml +0 -1685
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-InsertFind.yml +0 -1681
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Decimal-Update.yml +0 -1698
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-Aggregate.yml +0 -330
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-Correctness.yml +0 -425
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-Delete.yml +0 -227
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-FindOneAndUpdate.yml +0 -328
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-InsertFind.yml +0 -320
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DecimalPrecision-Update.yml +0 -337
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-Aggregate.yml +0 -914
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-Correctness.yml +0 -293
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-Delete.yml +0 -519
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-FindOneAndUpdate.yml +0 -912
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-InsertFind.yml +0 -908
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Double-Update.yml +0 -925
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-Aggregate.yml +0 -326
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-Correctness.yml +0 -425
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-Delete.yml +0 -225
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-FindOneAndUpdate.yml +0 -324
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-InsertFind.yml +0 -320
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-DoublePrecision-Update.yml +0 -339
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-Aggregate.yml +0 -242
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-Correctness.yml +0 -424
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-Delete.yml +0 -183
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-FindOneAndUpdate.yml +0 -240
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-InsertFind.yml +0 -236
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Int-Update.yml +0 -255
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-Aggregate.yml +0 -242
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-Correctness.yml +0 -423
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-Delete.yml +0 -183
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-FindOneAndUpdate.yml +0 -240
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-InsertFind.yml +0 -236
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-Long-Update.yml +0 -255
- data/spec/spec_tests/data/client_side_encryption/fle2v2-Range-WrongType.yml +0 -44
data/lib/mongo/socket/ssl.rb
CHANGED
@@ -142,26 +142,37 @@ module Mongo
|
|
142
142
|
#
|
143
143
|
# @since 2.0.0
|
144
144
|
def connect!
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
145
|
+
sockaddr = ::Socket.pack_sockaddr_in(port, host)
|
146
|
+
connect_timeout = options[:connect_timeout]
|
147
|
+
map_exceptions do
|
148
|
+
if connect_timeout && connect_timeout != 0
|
149
|
+
deadline = Utils.monotonic_time + connect_timeout
|
150
|
+
if BSON::Environment.jruby?
|
151
|
+
# We encounter some strange problems with connect_nonblock for
|
152
|
+
# ssl sockets on JRuby. Therefore, we use the old +Timeout.timeout+
|
153
|
+
# solution, even though it is known to be not very reliable.
|
154
|
+
raise Error::SocketTimeoutError, 'connect_timeout expired' if connect_timeout < 0
|
155
|
+
|
156
|
+
Timeout.timeout(connect_timeout, Error::SocketTimeoutError, "The socket took over #{options[:connect_timeout]} seconds to connect") do
|
157
|
+
connect_without_timeout(sockaddr)
|
158
|
+
end
|
159
|
+
else
|
160
|
+
connect_with_timeout(sockaddr, connect_timeout)
|
155
161
|
end
|
162
|
+
remaining_timeout = deadline - Utils.monotonic_time
|
163
|
+
verify_certificate!(@socket)
|
164
|
+
verify_ocsp_endpoint!(@socket, remaining_timeout)
|
165
|
+
else
|
166
|
+
connect_without_timeout(sockaddr)
|
156
167
|
verify_certificate!(@socket)
|
157
168
|
verify_ocsp_endpoint!(@socket)
|
158
|
-
rescue
|
159
|
-
@socket.close
|
160
|
-
@socket = nil
|
161
|
-
raise
|
162
169
|
end
|
163
|
-
self
|
164
170
|
end
|
171
|
+
self
|
172
|
+
rescue
|
173
|
+
@socket&.close
|
174
|
+
@socket = nil
|
175
|
+
raise
|
165
176
|
end
|
166
177
|
private :connect!
|
167
178
|
|
@@ -182,6 +193,87 @@ module Mongo
|
|
182
193
|
|
183
194
|
private
|
184
195
|
|
196
|
+
# Connects the socket without a timeout provided.
|
197
|
+
#
|
198
|
+
# @param [ String ] sockaddr Address to connect to.
|
199
|
+
def connect_without_timeout(sockaddr)
|
200
|
+
@tcp_socket.connect(sockaddr)
|
201
|
+
@socket = OpenSSL::SSL::SSLSocket.new(@tcp_socket, context)
|
202
|
+
@socket.hostname = @host_name
|
203
|
+
@socket.sync_close = true
|
204
|
+
@socket.connect
|
205
|
+
end
|
206
|
+
|
207
|
+
# Connects the socket with the connect timeout. The timeout applies to
|
208
|
+
# connecting both ssl socket and the underlying tcp socket.
|
209
|
+
#
|
210
|
+
# @param [ String ] sockaddr Address to connect to.
|
211
|
+
def connect_with_timeout(sockaddr, connect_timeout)
|
212
|
+
if connect_timeout <= 0
|
213
|
+
raise Error::SocketTimeoutError, "The socket took over #{connect_timeout} seconds to connect"
|
214
|
+
end
|
215
|
+
|
216
|
+
deadline = Utils.monotonic_time + connect_timeout
|
217
|
+
connect_tcp_socket_with_timeout(sockaddr, deadline, connect_timeout)
|
218
|
+
connnect_ssl_socket_with_timeout(deadline, connect_timeout)
|
219
|
+
end
|
220
|
+
|
221
|
+
def connect_tcp_socket_with_timeout(sockaddr, deadline, connect_timeout)
|
222
|
+
if deadline <= Utils.monotonic_time
|
223
|
+
raise Error::SocketTimeoutError, "The socket took over #{connect_timeout} seconds to connect"
|
224
|
+
end
|
225
|
+
begin
|
226
|
+
@tcp_socket.connect_nonblock(sockaddr)
|
227
|
+
rescue IO::WaitWritable
|
228
|
+
with_select_timeout(deadline, connect_timeout) do |select_timeout|
|
229
|
+
IO.select(nil, [@tcp_socket], nil, select_timeout)
|
230
|
+
end
|
231
|
+
retry
|
232
|
+
rescue Errno::EISCONN
|
233
|
+
# Socket is connected, nothing to do.
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
def connnect_ssl_socket_with_timeout(deadline, connect_timeout)
|
238
|
+
if deadline <= Utils.monotonic_time
|
239
|
+
raise Error::SocketTimeoutError, "The socket took over #{connect_timeout} seconds to connect"
|
240
|
+
end
|
241
|
+
@socket = OpenSSL::SSL::SSLSocket.new(@tcp_socket, context)
|
242
|
+
@socket.hostname = @host_name
|
243
|
+
@socket.sync_close = true
|
244
|
+
|
245
|
+
# We still have time, connecting ssl socket.
|
246
|
+
begin
|
247
|
+
@socket.connect_nonblock
|
248
|
+
rescue IO::WaitReadable, OpenSSL::SSL::SSLErrorWaitReadable
|
249
|
+
with_select_timeout(deadline, connect_timeout) do |select_timeout|
|
250
|
+
IO.select([@socket], nil, nil, select_timeout)
|
251
|
+
end
|
252
|
+
retry
|
253
|
+
rescue IO::WaitWritable, OpenSSL::SSL::SSLErrorWaitWritable
|
254
|
+
with_select_timeout(deadline, connect_timeout) do |select_timeout|
|
255
|
+
IO.select(nil, [@socket], nil, select_timeout)
|
256
|
+
end
|
257
|
+
retry
|
258
|
+
rescue Errno::EISCONN
|
259
|
+
# Socket is connected, nothing to do
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
# Raises +Error::SocketTimeoutError+ exception if deadline reached or the
|
264
|
+
# block returns nil. The block should call +IO.select+ with the
|
265
|
+
# +connect_timeout+ value. It returns nil if the +connect_timeout+ expires.
|
266
|
+
def with_select_timeout(deadline, connect_timeout, &block)
|
267
|
+
select_timeout = deadline - Utils.monotonic_time
|
268
|
+
if select_timeout <= 0
|
269
|
+
raise Error::SocketTimeoutError, "The socket took over #{connect_timeout} seconds to connect"
|
270
|
+
end
|
271
|
+
rv = block.call(select_timeout)
|
272
|
+
if rv.nil?
|
273
|
+
raise Error::SocketTimeoutError, "The socket took over #{connect_timeout} seconds to connect"
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
185
277
|
def verify_certificate?
|
186
278
|
# If ssl_verify_certificate is not present, disable only if
|
187
279
|
# ssl_verify is explicitly set to false.
|
@@ -362,7 +454,7 @@ module Mongo
|
|
362
454
|
end
|
363
455
|
end
|
364
456
|
|
365
|
-
def verify_ocsp_endpoint!(socket)
|
457
|
+
def verify_ocsp_endpoint!(socket, timeout = nil)
|
366
458
|
unless verify_ocsp_endpoint?
|
367
459
|
return
|
368
460
|
end
|
@@ -371,7 +463,7 @@ module Mongo
|
|
371
463
|
ca_cert = socket.peer_cert_chain.last
|
372
464
|
|
373
465
|
verifier = OcspVerifier.new(@host_name, cert, ca_cert, context.cert_store,
|
374
|
-
**Utils.shallow_symbolize_keys(options))
|
466
|
+
**Utils.shallow_symbolize_keys(options).merge(timeout: timeout))
|
375
467
|
verifier.verify_with_cache
|
376
468
|
end
|
377
469
|
|
data/lib/mongo/socket/tcp.rb
CHANGED
@@ -79,16 +79,50 @@ module Mongo
|
|
79
79
|
# @return [ TCP ] The connected socket instance.
|
80
80
|
#
|
81
81
|
# @since 2.0.0
|
82
|
+
# @api private
|
82
83
|
def connect!
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
84
|
+
socket.setsockopt(IPPROTO_TCP, TCP_NODELAY, 1)
|
85
|
+
sockaddr = ::Socket.pack_sockaddr_in(port, host)
|
86
|
+
connect_timeout = options[:connect_timeout]
|
87
|
+
map_exceptions do
|
88
|
+
if connect_timeout && connect_timeout != 0
|
89
|
+
connect_with_timeout(sockaddr, connect_timeout)
|
90
|
+
else
|
91
|
+
connect_without_timeout(sockaddr)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
self
|
95
|
+
end
|
96
|
+
|
97
|
+
# @api private
|
98
|
+
def connect_without_timeout(sockaddr)
|
99
|
+
socket.connect(sockaddr)
|
100
|
+
end
|
101
|
+
|
102
|
+
# @api private
|
103
|
+
def connect_with_timeout(sockaddr, connect_timeout)
|
104
|
+
if connect_timeout <= 0
|
105
|
+
raise Error::SocketTimeoutError, "The socket took over #{connect_timeout} seconds to connect"
|
106
|
+
end
|
107
|
+
|
108
|
+
deadline = Utils.monotonic_time + connect_timeout
|
109
|
+
begin
|
110
|
+
socket.connect_nonblock(sockaddr)
|
111
|
+
rescue IO::WaitWritable
|
112
|
+
select_timeout = deadline - Utils.monotonic_time
|
113
|
+
if select_timeout <= 0
|
114
|
+
raise Error::SocketTimeoutError, "The socket took over #{connect_timeout} seconds to connect"
|
115
|
+
end
|
116
|
+
if IO.select(nil, [socket], nil, select_timeout)
|
117
|
+
retry
|
118
|
+
else
|
119
|
+
socket.close
|
120
|
+
raise Error::SocketTimeoutError, "The socket took over #{connect_timeout} seconds to connect"
|
87
121
|
end
|
88
|
-
|
122
|
+
rescue Errno::EISCONN
|
123
|
+
# Socket is connected, nothing more to do
|
89
124
|
end
|
90
125
|
end
|
91
|
-
private :connect!
|
92
126
|
|
93
127
|
private
|
94
128
|
|
data/lib/mongo/socket.rb
CHANGED
@@ -192,27 +192,24 @@ module Mongo
|
|
192
192
|
# socket.read(4096)
|
193
193
|
#
|
194
194
|
# @param [ Integer ] length The number of bytes to read.
|
195
|
-
# @param [ Numeric ]
|
195
|
+
# @param [ Numeric ] socket_timeout The timeout to use for each chunk read,
|
196
|
+
# mutually exclusive to +timeout+.
|
197
|
+
# @param [ Numeric ] timeout The total timeout to the whole read operation,
|
198
|
+
# mutually exclusive to +socket_timeout+.
|
196
199
|
#
|
197
200
|
# @raise [ Mongo::SocketError ] If not all data is returned.
|
198
201
|
#
|
199
202
|
# @return [ Object ] The data from the socket.
|
200
203
|
#
|
201
204
|
# @since 2.0.0
|
202
|
-
def read(length, timeout: nil)
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
unless (chunk.length > 0 || length == 0)
|
211
|
-
raise IOError, "Expected to read > 0 bytes but read 0 bytes"
|
212
|
-
end
|
213
|
-
data << chunk
|
214
|
-
end
|
215
|
-
data
|
205
|
+
def read(length, socket_timeout: nil, timeout: nil)
|
206
|
+
if !socket_timeout.nil? && !timeout.nil?
|
207
|
+
raise ArgumentError, 'Both timeout and socket_timeout cannot be set'
|
208
|
+
end
|
209
|
+
if !socket_timeout.nil? || timeout.nil?
|
210
|
+
read_without_timeout(length, socket_timeout)
|
211
|
+
else
|
212
|
+
read_with_timeout(length, timeout)
|
216
213
|
end
|
217
214
|
end
|
218
215
|
|
@@ -233,15 +230,16 @@ module Mongo
|
|
233
230
|
# Writes data to the socket instance.
|
234
231
|
#
|
235
232
|
# @param [ Array<Object> ] args The data to be written.
|
233
|
+
# @param [ Numeric ] timeout The total timeout to the whole write operation.
|
236
234
|
#
|
237
235
|
# @return [ Integer ] The length of bytes written to the socket.
|
238
236
|
#
|
239
237
|
# @raise [ Error::SocketError | Error::SocketTimeoutError ] When there is a network error during the write.
|
240
238
|
#
|
241
239
|
# @since 2.0.0
|
242
|
-
def write(*args)
|
240
|
+
def write(*args, timeout: nil)
|
243
241
|
map_exceptions do
|
244
|
-
do_write(*args)
|
242
|
+
do_write(*args, timeout: timeout)
|
245
243
|
end
|
246
244
|
end
|
247
245
|
|
@@ -254,7 +252,7 @@ module Mongo
|
|
254
252
|
true
|
255
253
|
end
|
256
254
|
|
257
|
-
# For backwards
|
255
|
+
# For backwards compatibility only, do not use.
|
258
256
|
#
|
259
257
|
# @return [ true ] Always true.
|
260
258
|
#
|
@@ -265,18 +263,76 @@ module Mongo
|
|
265
263
|
|
266
264
|
private
|
267
265
|
|
268
|
-
|
266
|
+
# Reads the +length+ bytes from the socket, the read operation duration is
|
267
|
+
# limited to +timeout+ second.
|
268
|
+
#
|
269
|
+
# @param [ Integer ] length The number of bytes to read.
|
270
|
+
# @param [ Numeric ] timeout The total timeout to the whole read operation.
|
271
|
+
#
|
272
|
+
# @return [ Object ] The data from the socket.
|
273
|
+
def read_with_timeout(length, timeout)
|
274
|
+
deadline = Utils.monotonic_time + timeout
|
275
|
+
map_exceptions do
|
276
|
+
String.new.tap do |data|
|
277
|
+
while data.length < length
|
278
|
+
socket_timeout = deadline - Utils.monotonic_time
|
279
|
+
if socket_timeout <= 0
|
280
|
+
raise Mongo::Error::TimeoutError
|
281
|
+
end
|
282
|
+
chunk = read_from_socket(length - data.length, socket_timeout: socket_timeout, csot: true)
|
283
|
+
unless chunk.length > 0
|
284
|
+
raise IOError, "Expected to read > 0 bytes but read 0 bytes"
|
285
|
+
end
|
286
|
+
data << chunk
|
287
|
+
end
|
288
|
+
end
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
# Reads the +length+ bytes from the socket. The read operation may involve
|
293
|
+
# multiple socket reads, each read is limited to +timeout+ second,
|
294
|
+
# if the parameter is provided.
|
295
|
+
#
|
296
|
+
# @param [ Integer ] length The number of bytes to read.
|
297
|
+
# @param [ Numeric ] socket_timeout The timeout to use for each chunk read.
|
298
|
+
#
|
299
|
+
# @return [ Object ] The data from the socket.
|
300
|
+
def read_without_timeout(length, socket_timeout = nil)
|
301
|
+
map_exceptions do
|
302
|
+
String.new.tap do |data|
|
303
|
+
while data.length < length
|
304
|
+
chunk = read_from_socket(length - data.length, socket_timeout: socket_timeout)
|
305
|
+
unless chunk.length > 0
|
306
|
+
raise IOError, "Expected to read > 0 bytes but read 0 bytes"
|
307
|
+
end
|
308
|
+
data << chunk
|
309
|
+
end
|
310
|
+
end
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
|
315
|
+
# Reads the +length+ bytes from the socket. The read operation may involve
|
316
|
+
# multiple socket reads, each read is limited to +timeout+ second,
|
317
|
+
# if the parameter is provided.
|
318
|
+
#
|
319
|
+
# @param [ Integer ] length The number of bytes to read.
|
320
|
+
# @param [ Numeric ] :socket_timeout The timeout to use for each chunk read.
|
321
|
+
# @param [ true | false ] :csot Whether the CSOT timeout is set for the operation.
|
322
|
+
#
|
323
|
+
# @return [ Object ] The data from the socket.
|
324
|
+
def read_from_socket(length, socket_timeout: nil, csot: false)
|
269
325
|
# Just in case
|
270
326
|
if length == 0
|
271
327
|
return ''.force_encoding('BINARY')
|
272
328
|
end
|
273
329
|
|
274
|
-
_timeout =
|
330
|
+
_timeout = socket_timeout || self.timeout
|
275
331
|
if _timeout
|
276
332
|
if _timeout > 0
|
277
333
|
deadline = Utils.monotonic_time + _timeout
|
278
334
|
elsif _timeout < 0
|
279
|
-
|
335
|
+
raise_timeout_error!("Negative timeout #{_timeout} given to socket", csot)
|
280
336
|
end
|
281
337
|
end
|
282
338
|
|
@@ -331,7 +387,7 @@ module Mongo
|
|
331
387
|
if deadline
|
332
388
|
select_timeout = deadline - Utils.monotonic_time
|
333
389
|
if select_timeout <= 0
|
334
|
-
|
390
|
+
raise_timeout_error!("Took more than #{_timeout} seconds to receive data", csot)
|
335
391
|
end
|
336
392
|
end
|
337
393
|
pipe = options[:pipe]
|
@@ -373,11 +429,11 @@ module Mongo
|
|
373
429
|
if deadline
|
374
430
|
select_timeout = deadline - Utils.monotonic_time
|
375
431
|
if select_timeout <= 0
|
376
|
-
|
432
|
+
raise_timeout_error!("Took more than #{_timeout} seconds to receive data", csot)
|
377
433
|
end
|
378
434
|
end
|
379
435
|
elsif rv.nil?
|
380
|
-
|
436
|
+
raise_timeout_error!("Took more than #{_timeout} seconds to receive data (select call timed out)", csot)
|
381
437
|
end
|
382
438
|
retry
|
383
439
|
end
|
@@ -402,9 +458,23 @@ module Mongo
|
|
402
458
|
# sholud map exceptions.
|
403
459
|
#
|
404
460
|
# @param [ Array<Object> ] args The data to be written.
|
461
|
+
# @param [ Numeric ] :timeout The total timeout to the whole write operation.
|
462
|
+
#
|
463
|
+
# @return [ Integer ] The length of bytes written to the socket.
|
464
|
+
def do_write(*args, timeout: nil)
|
465
|
+
if timeout.nil?
|
466
|
+
write_without_timeout(*args)
|
467
|
+
else
|
468
|
+
write_with_timeout(*args, timeout: timeout)
|
469
|
+
end
|
470
|
+
end
|
471
|
+
|
472
|
+
# Writes data to to the socket.
|
473
|
+
#
|
474
|
+
# @param [ Array<Object> ] args The data to be written.
|
405
475
|
#
|
406
476
|
# @return [ Integer ] The length of bytes written to the socket.
|
407
|
-
def
|
477
|
+
def write_without_timeout(*args)
|
408
478
|
# This method used to forward arguments to @socket.write in a
|
409
479
|
# single call like so:
|
410
480
|
#
|
@@ -428,6 +498,57 @@ module Mongo
|
|
428
498
|
end
|
429
499
|
end
|
430
500
|
|
501
|
+
# Writes data to to the socket, the write duration is limited to +timeout+.
|
502
|
+
#
|
503
|
+
# @param [ Array<Object> ] args The data to be written.
|
504
|
+
# @param [ Numeric ] :timeout The total timeout to the whole write operation.
|
505
|
+
#
|
506
|
+
# @return [ Integer ] The length of bytes written to the socket.
|
507
|
+
def write_with_timeout(*args, timeout:)
|
508
|
+
raise ArgumentError, 'timeout cannot be nil' if timeout.nil?
|
509
|
+
raise_timeout_error!("Negative timeout #{timeout} given to socket", true) if timeout < 0
|
510
|
+
|
511
|
+
written = 0
|
512
|
+
args.each do |buf|
|
513
|
+
buf = buf.to_s
|
514
|
+
i = 0
|
515
|
+
while i < buf.length
|
516
|
+
chunk = buf[i...(i + WRITE_CHUNK_SIZE)]
|
517
|
+
written += write_chunk(chunk, timeout)
|
518
|
+
i += WRITE_CHUNK_SIZE
|
519
|
+
end
|
520
|
+
end
|
521
|
+
written
|
522
|
+
end
|
523
|
+
|
524
|
+
def write_chunk(chunk, timeout)
|
525
|
+
deadline = Utils.monotonic_time + timeout
|
526
|
+
written = 0
|
527
|
+
begin
|
528
|
+
written += @socket.write_nonblock(chunk[written..-1])
|
529
|
+
rescue IO::WaitWritable, Errno::EINTR
|
530
|
+
select_timeout = deadline - Utils.monotonic_time
|
531
|
+
rv = Kernel.select(nil, [@socket], nil, select_timeout)
|
532
|
+
if BSON::Environment.jruby?
|
533
|
+
# Ignore the return value of Kernel.select.
|
534
|
+
# On JRuby, select appears to return nil prior to timeout expiration
|
535
|
+
# (apparently due to a EAGAIN) which then causes us to fail the read
|
536
|
+
# even though we could have retried it.
|
537
|
+
# Check the deadline ourselves.
|
538
|
+
if deadline
|
539
|
+
select_timeout = deadline - Utils.monotonic_time
|
540
|
+
if select_timeout <= 0
|
541
|
+
raise_timeout_error!("Took more than #{timeout} seconds to receive data", true)
|
542
|
+
end
|
543
|
+
end
|
544
|
+
elsif rv.nil?
|
545
|
+
raise_timeout_error!("Took more than #{timeout} seconds to receive data (select call timed out)", true)
|
546
|
+
end
|
547
|
+
retry
|
548
|
+
end
|
549
|
+
written
|
550
|
+
end
|
551
|
+
|
431
552
|
def unix_socket?(sock)
|
432
553
|
defined?(UNIXSocket) && sock.is_a?(UNIXSocket)
|
433
554
|
end
|
@@ -482,5 +603,13 @@ module Mongo
|
|
482
603
|
def human_address
|
483
604
|
raise NotImplementedError
|
484
605
|
end
|
606
|
+
|
607
|
+
def raise_timeout_error!(message = nil, csot = false)
|
608
|
+
if csot
|
609
|
+
raise Mongo::Error::TimeoutError
|
610
|
+
else
|
611
|
+
raise Errno::ETIMEDOUT, message
|
612
|
+
end
|
613
|
+
end
|
485
614
|
end
|
486
615
|
end
|
@@ -272,6 +272,7 @@ module Mongo
|
|
272
272
|
uri_option 'localThresholdMS', :local_threshold, type: :ms
|
273
273
|
uri_option 'heartbeatFrequencyMS', :heartbeat_frequency, type: :ms
|
274
274
|
uri_option 'maxIdleTimeMS', :max_idle_time, type: :ms
|
275
|
+
uri_option 'timeoutMS', :timeout_ms, type: :integer
|
275
276
|
|
276
277
|
# Write Options
|
277
278
|
uri_option 'w', :w, group: :write_concern, type: :w
|
data/lib/mongo/version.rb
CHANGED
data/lib/mongo.rb
CHANGED
@@ -57,7 +57,8 @@ describe 'Auto Encryption' do
|
|
57
57
|
'jsonSchema' => kind_of(Hash),
|
58
58
|
'isRemoteSchema' => false,
|
59
59
|
),
|
60
|
-
{ execution_options: { deserialize_as_bson: true } },
|
60
|
+
{ execution_options: { deserialize_as_bson: true }, timeout_ms: nil },
|
61
|
+
|
61
62
|
)
|
62
63
|
.and_raise(Mongo::Error::NoServerAvailable.new(server_selector, cluster))
|
63
64
|
end
|