moped 1.3.2 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of moped might be problematic. Click here for more details.
- data/CHANGELOG.md +39 -0
- data/LICENSE +1 -1
- data/README.md +1 -1
- data/lib/moped/bson.rb +4 -4
- data/lib/moped/bson/binary.rb +96 -30
- data/lib/moped/bson/code.rb +81 -32
- data/lib/moped/bson/document.rb +25 -0
- data/lib/moped/bson/extensions/array.rb +14 -24
- data/lib/moped/bson/extensions/boolean.rb +3 -1
- data/lib/moped/bson/extensions/false_class.rb +2 -1
- data/lib/moped/bson/extensions/float.rb +7 -7
- data/lib/moped/bson/extensions/hash.rb +16 -25
- data/lib/moped/bson/extensions/integer.rb +8 -7
- data/lib/moped/bson/extensions/nil_class.rb +6 -6
- data/lib/moped/bson/extensions/object.rb +2 -4
- data/lib/moped/bson/extensions/regexp.rb +13 -14
- data/lib/moped/bson/extensions/string.rb +8 -9
- data/lib/moped/bson/extensions/symbol.rb +8 -8
- data/lib/moped/bson/extensions/time.rb +9 -7
- data/lib/moped/bson/extensions/true_class.rb +2 -1
- data/lib/moped/bson/max_key.rb +34 -3
- data/lib/moped/bson/min_key.rb +34 -3
- data/lib/moped/bson/object_id.rb +223 -77
- data/lib/moped/bson/timestamp.rb +27 -4
- data/lib/moped/bson/types.rb +5 -5
- data/lib/moped/cluster.rb +21 -4
- data/lib/moped/collection.rb +15 -5
- data/lib/moped/database.rb +1 -1
- data/lib/moped/errors.rb +19 -5
- data/lib/moped/node.rb +23 -31
- data/lib/moped/protocol/query.rb +15 -0
- data/lib/moped/query.rb +31 -9
- data/lib/moped/session.rb +22 -8
- data/lib/moped/sockets/connectable.rb +58 -7
- data/lib/moped/version.rb +1 -1
- metadata +4 -4
data/lib/moped/collection.rb
CHANGED
@@ -13,6 +13,18 @@ module Moped
|
|
13
13
|
# @attribute [r] name The collection name.
|
14
14
|
attr_reader :database, :name
|
15
15
|
|
16
|
+
# Return whether or not this collection is a capped collection.
|
17
|
+
#
|
18
|
+
# @example Is the collection capped?
|
19
|
+
# collection.capped?
|
20
|
+
#
|
21
|
+
# @return [ true, false ] If the collection is capped.
|
22
|
+
#
|
23
|
+
# @since 1.4.0
|
24
|
+
def capped?
|
25
|
+
database.command(collstats: name)["capped"]
|
26
|
+
end
|
27
|
+
|
16
28
|
# Drop the collection.
|
17
29
|
#
|
18
30
|
# @example Drop the collection.
|
@@ -107,12 +119,10 @@ module Moped
|
|
107
119
|
# @return [ Hash ] containing the result of aggregation
|
108
120
|
#
|
109
121
|
# @since 1.3.0
|
110
|
-
def aggregate(pipeline)
|
111
|
-
pipeline
|
122
|
+
def aggregate(*pipeline)
|
123
|
+
pipeline.flatten!
|
112
124
|
command = { aggregate: name.to_s, pipeline: pipeline }
|
113
|
-
database.session.
|
114
|
-
sess.command(command)["result"]
|
115
|
-
end
|
125
|
+
database.session.command(command)["result"]
|
116
126
|
end
|
117
127
|
end
|
118
128
|
end
|
data/lib/moped/database.rb
CHANGED
@@ -54,7 +54,7 @@ module Moped
|
|
54
54
|
#
|
55
55
|
# @since 1.0.0
|
56
56
|
def collection_names
|
57
|
-
namespaces = Collection.new(self, "system.namespaces").find(name: { "$not" => /#{name}\.system
|
57
|
+
namespaces = Collection.new(self, "system.namespaces").find(name: { "$not" => /#{name}\.system\.|\$/ })
|
58
58
|
namespaces.map do |doc|
|
59
59
|
_name = doc["name"]
|
60
60
|
_name[name.length + 1, _name.length]
|
data/lib/moped/errors.rb
CHANGED
@@ -93,18 +93,32 @@ module Moped
|
|
93
93
|
end
|
94
94
|
end
|
95
95
|
|
96
|
+
# Classes of errors that should not disconnect connections.
|
97
|
+
class DoNotDisconnect < MongoError; end
|
98
|
+
|
99
|
+
# Classes of errors that could be caused by a replica set reconfiguration.
|
100
|
+
class PotentialReconfiguration < MongoError
|
101
|
+
|
102
|
+
# Replica set reconfigurations can be either in the form of an operation
|
103
|
+
# error with code 13435, or with an error message stating the server is
|
104
|
+
# not a master. (This encapsulates codes 10054, 10056, 10058)
|
105
|
+
def reconfiguring_replica_set?
|
106
|
+
details["code"] == 13435 || details["err"] == "not master"
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
96
110
|
# Exception raised when authentication fails.
|
97
|
-
class AuthenticationFailure <
|
111
|
+
class AuthenticationFailure < DoNotDisconnect; end
|
98
112
|
|
99
113
|
# Exception class for exceptions generated as a direct result of an
|
100
114
|
# operation, such as a failed insert or an invalid command.
|
101
|
-
class OperationFailure <
|
115
|
+
class OperationFailure < PotentialReconfiguration; end
|
102
116
|
|
103
117
|
# Exception raised on invalid queries.
|
104
|
-
class QueryFailure <
|
118
|
+
class QueryFailure < PotentialReconfiguration; end
|
105
119
|
|
106
120
|
# Exception raised if the cursor could not be found.
|
107
|
-
class CursorNotFound <
|
121
|
+
class CursorNotFound < DoNotDisconnect
|
108
122
|
def initialize(operation, cursor_id)
|
109
123
|
super(operation, {"errmsg" => "cursor #{cursor_id} not found"})
|
110
124
|
end
|
@@ -114,7 +128,7 @@ module Moped
|
|
114
128
|
#
|
115
129
|
# Internal exception raised by Node#ensure_primary and captured by
|
116
130
|
# Cluster#with_primary.
|
117
|
-
class ReplicaSetReconfigured <
|
131
|
+
class ReplicaSetReconfigured < DoNotDisconnect; end
|
118
132
|
|
119
133
|
# Tag applied to unhandled exceptions on a node.
|
120
134
|
module SocketError; end
|
data/lib/moped/node.rb
CHANGED
@@ -127,32 +127,17 @@ module Moped
|
|
127
127
|
begin
|
128
128
|
connect unless connected?
|
129
129
|
yield
|
130
|
-
rescue Errors::
|
131
|
-
|
132
|
-
# reconfiguration exception bubble up.
|
133
|
-
raise
|
134
|
-
rescue Errors::QueryFailure => e
|
135
|
-
# We might have a replica set change with:
|
136
|
-
# "failed with error 13435: "not master and slaveOk=false"
|
137
|
-
if e.details['code'] == 13435
|
130
|
+
rescue Errors::PotentialReconfiguration => e
|
131
|
+
if e.reconfiguring_replica_set?
|
138
132
|
raise Errors::ReplicaSetReconfigured
|
139
133
|
end
|
140
134
|
raise
|
141
|
-
rescue Errors::
|
142
|
-
# We might have a replica set change with:
|
143
|
-
# MongoDB uses 3 different error codes for "not master", [10054, 10056, 10058]
|
144
|
-
# thus it is easier to capture the "err"
|
145
|
-
if e.details["err"] == "not master"
|
146
|
-
raise Errors::ReplicaSetReconfigured
|
147
|
-
end
|
148
|
-
raise
|
149
|
-
rescue Errors::AuthenticationFailure, Errors::CursorNotFound
|
135
|
+
rescue Errors::DoNotDisconnect
|
150
136
|
# These exceptions are "expected" in the normal course of events, and
|
151
137
|
# don't necessitate disconnecting.
|
152
138
|
raise
|
153
139
|
rescue Errors::ConnectionFailure
|
154
140
|
disconnect
|
155
|
-
|
156
141
|
if retry_on_failure
|
157
142
|
# Maybe there was a hiccup -- try reconnecting one more time
|
158
143
|
retry_on_failure = false
|
@@ -387,7 +372,7 @@ module Moped
|
|
387
372
|
# node.refresh
|
388
373
|
#
|
389
374
|
# @raise [ ConnectionFailure ] If the node cannot be reached.
|
390
|
-
|
375
|
+
#
|
391
376
|
# @raise [ ReplicaSetReconfigured ] If the node is no longer a primary node and
|
392
377
|
# refresh was called within an +#ensure_primary+ block.
|
393
378
|
#
|
@@ -398,18 +383,11 @@ module Moped
|
|
398
383
|
if resolve_address
|
399
384
|
begin
|
400
385
|
info = command("admin", ismaster: 1)
|
401
|
-
|
402
386
|
@refreshed_at = Time.now
|
403
|
-
primary = true
|
387
|
+
primary = true if info["ismaster"]
|
404
388
|
secondary = true if info["secondary"]
|
389
|
+
generate_peers(info)
|
405
390
|
|
406
|
-
peers = []
|
407
|
-
peers.push(info["primary"]) if info["primary"]
|
408
|
-
peers.concat(info["hosts"]) if info["hosts"]
|
409
|
-
peers.concat(info["passives"]) if info["passives"]
|
410
|
-
peers.concat(info["arbiters"]) if info["arbiters"]
|
411
|
-
|
412
|
-
@peers = peers.map { |peer| Node.new(peer, options) }
|
413
391
|
@primary, @secondary = primary, secondary
|
414
392
|
@arbiter = info["arbiterOnly"]
|
415
393
|
@passive = info["passive"]
|
@@ -475,28 +453,42 @@ module Moped
|
|
475
453
|
process(Protocol::Update.new(database, collection, selector, change, options))
|
476
454
|
end
|
477
455
|
|
456
|
+
# Get the node as a nice formatted string.
|
457
|
+
#
|
458
|
+
# @example Inspect the node.
|
459
|
+
# node.inspect
|
460
|
+
#
|
461
|
+
# @return [ String ] The string inspection.
|
462
|
+
#
|
463
|
+
# @since 1.0.0
|
478
464
|
def inspect
|
479
465
|
"<#{self.class.name} resolved_address=#{@resolved_address.inspect}>"
|
480
466
|
end
|
481
467
|
|
482
|
-
|
483
468
|
private
|
484
469
|
|
485
470
|
def auth
|
486
471
|
@auth ||= {}
|
487
472
|
end
|
488
473
|
|
474
|
+
def generate_peers(info)
|
475
|
+
peers = []
|
476
|
+
peers.push(info["primary"]) if info["primary"]
|
477
|
+
peers.concat(info["hosts"]) if info["hosts"]
|
478
|
+
peers.concat(info["passives"]) if info["passives"]
|
479
|
+
peers.concat(info["arbiters"]) if info["arbiters"]
|
480
|
+
@peers = peers.map { |peer| Node.new(peer, options) }.uniq
|
481
|
+
end
|
482
|
+
|
489
483
|
def login(database, username, password)
|
490
484
|
getnonce = Protocol::Command.new(database, getnonce: 1)
|
491
485
|
connection.write [getnonce]
|
492
486
|
result = connection.read.documents.first
|
493
487
|
raise Errors::OperationFailure.new(getnonce, result) unless result["ok"] == 1
|
494
|
-
|
495
488
|
authenticate = Protocol::Commands::Authenticate.new(database, username, password, result["nonce"])
|
496
489
|
connection.write [authenticate]
|
497
490
|
result = connection.read.documents.first
|
498
491
|
raise Errors::AuthenticationFailure.new(authenticate, result) unless result["ok"] == 1
|
499
|
-
|
500
492
|
auth[database] = [username, password]
|
501
493
|
end
|
502
494
|
|
data/lib/moped/protocol/query.rb
CHANGED
@@ -134,6 +134,21 @@ module Moped
|
|
134
134
|
f.join(" ") % v
|
135
135
|
end
|
136
136
|
|
137
|
+
# Get the basic selector.
|
138
|
+
#
|
139
|
+
# @example Get the basic selector.
|
140
|
+
# query.basic_selector
|
141
|
+
#
|
142
|
+
# @note Sometimes, like in cases of deletion we need this since MongoDB
|
143
|
+
# does not understand $query in operations like DELETE.
|
144
|
+
#
|
145
|
+
# @return [ Hash ] The basic selector.
|
146
|
+
#
|
147
|
+
# @since 2.0.0
|
148
|
+
def basic_selector
|
149
|
+
selector["$query"] || selector
|
150
|
+
end
|
151
|
+
|
137
152
|
# Receive replies to the message.
|
138
153
|
#
|
139
154
|
# @example Receive replies.
|
data/lib/moped/query.rb
CHANGED
@@ -32,11 +32,10 @@ module Moped
|
|
32
32
|
# @return [ Integer ] The number of documents that match the selector.
|
33
33
|
#
|
34
34
|
# @since 1.0.0
|
35
|
-
def count
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
)
|
35
|
+
def count(limit = false)
|
36
|
+
command = { count: collection.name, query: selector }
|
37
|
+
command.merge!(skip: operation.skip, limit: operation.limit) if limit
|
38
|
+
result = collection.database.command(command)
|
40
39
|
result["n"].to_i
|
41
40
|
end
|
42
41
|
|
@@ -93,12 +92,14 @@ module Moped
|
|
93
92
|
explanation = operation.selector.dup
|
94
93
|
hint = explanation["$hint"]
|
95
94
|
sort = explanation["$orderby"]
|
95
|
+
max_scan = explanation["$maxScan"]
|
96
96
|
explanation = {
|
97
97
|
"$query" => selector,
|
98
98
|
"$explain" => true,
|
99
99
|
}
|
100
100
|
explanation["$orderby"] = sort if sort
|
101
101
|
explanation["$hint"] = hint if hint
|
102
|
+
explanation["$maxScan"] = max_scan if max_scan
|
102
103
|
Query.new(collection, explanation).limit(-(operation.limit.abs)).each { |doc| return doc }
|
103
104
|
end
|
104
105
|
|
@@ -135,11 +136,27 @@ module Moped
|
|
135
136
|
#
|
136
137
|
# @since 1.0.0
|
137
138
|
def hint(hint)
|
138
|
-
|
139
|
+
upgrade_to_advanced_selector
|
139
140
|
operation.selector["$hint"] = hint
|
140
141
|
self
|
141
142
|
end
|
142
143
|
|
144
|
+
# Apply a max scan limit to the query.
|
145
|
+
#
|
146
|
+
# @example Limit the query to only scan up to 100 documents
|
147
|
+
# db[:people].find.max_scan(100)
|
148
|
+
#
|
149
|
+
# @param [ Integer ] max The maximum number of documents to scan
|
150
|
+
#
|
151
|
+
# @return [ Query ] self
|
152
|
+
#
|
153
|
+
# @since 1.4.0
|
154
|
+
def max_scan(max)
|
155
|
+
upgrade_to_advanced_selector
|
156
|
+
operation.selector["$maxScan"] = max
|
157
|
+
self
|
158
|
+
end
|
159
|
+
|
143
160
|
# Initialize the query.
|
144
161
|
#
|
145
162
|
# @example Initialize the query.
|
@@ -256,7 +273,7 @@ module Moped
|
|
256
273
|
session.context.remove(
|
257
274
|
operation.database,
|
258
275
|
operation.collection,
|
259
|
-
operation.
|
276
|
+
operation.basic_selector,
|
260
277
|
flags: [ :remove_first ]
|
261
278
|
)
|
262
279
|
end
|
@@ -275,7 +292,7 @@ module Moped
|
|
275
292
|
session.context.remove(
|
276
293
|
operation.database,
|
277
294
|
operation.collection,
|
278
|
-
operation.
|
295
|
+
operation.basic_selector
|
279
296
|
)
|
280
297
|
end
|
281
298
|
end
|
@@ -321,7 +338,8 @@ module Moped
|
|
321
338
|
#
|
322
339
|
# @since 1.0.0
|
323
340
|
def sort(sort)
|
324
|
-
|
341
|
+
upgrade_to_advanced_selector
|
342
|
+
operation.selector["$orderby"] = sort
|
325
343
|
self
|
326
344
|
end
|
327
345
|
|
@@ -404,5 +422,9 @@ module Moped
|
|
404
422
|
def session
|
405
423
|
collection.database.session
|
406
424
|
end
|
425
|
+
|
426
|
+
def upgrade_to_advanced_selector
|
427
|
+
operation.selector = { "$query" => selector } unless operation.selector["$query"]
|
428
|
+
end
|
407
429
|
end
|
408
430
|
end
|
data/lib/moped/session.rb
CHANGED
@@ -130,6 +130,18 @@ module Moped
|
|
130
130
|
current_database.drop
|
131
131
|
end
|
132
132
|
|
133
|
+
# Provide a string inspection for the session.
|
134
|
+
#
|
135
|
+
# @example Inspect the session.
|
136
|
+
# session.inspect
|
137
|
+
#
|
138
|
+
# @return [ String ] The string inspection.
|
139
|
+
#
|
140
|
+
# @since 1.4.0
|
141
|
+
def inspect
|
142
|
+
"<#{self.class.name} seeds=#{cluster.seeds} database=#{current_database_name}>"
|
143
|
+
end
|
144
|
+
|
133
145
|
# Log in with +username+ and +password+ on the current database.
|
134
146
|
#
|
135
147
|
# @param (see Moped::Database#login)
|
@@ -229,7 +241,7 @@ module Moped
|
|
229
241
|
session = with(options)
|
230
242
|
session.instance_variable_set(:@cluster, cluster.dup)
|
231
243
|
if block_given?
|
232
|
-
yield
|
244
|
+
yield(session)
|
233
245
|
else
|
234
246
|
session
|
235
247
|
end
|
@@ -271,7 +283,7 @@ module Moped
|
|
271
283
|
# @since 1.0.0
|
272
284
|
def use(database)
|
273
285
|
options[:database] = database
|
274
|
-
set_current_database
|
286
|
+
set_current_database(database)
|
275
287
|
end
|
276
288
|
|
277
289
|
# Create a new session with +options+ reusing existing connections.
|
@@ -309,7 +321,7 @@ module Moped
|
|
309
321
|
session = dup
|
310
322
|
session.options.update(options)
|
311
323
|
if block_given?
|
312
|
-
yield
|
324
|
+
yield(session)
|
313
325
|
else
|
314
326
|
session
|
315
327
|
end
|
@@ -338,8 +350,7 @@ module Moped
|
|
338
350
|
private
|
339
351
|
|
340
352
|
def current_database
|
341
|
-
return @current_database if defined?
|
342
|
-
|
353
|
+
return @current_database if defined?(@current_database)
|
343
354
|
if database = options[:database]
|
344
355
|
set_current_database(database)
|
345
356
|
else
|
@@ -347,12 +358,15 @@ module Moped
|
|
347
358
|
end
|
348
359
|
end
|
349
360
|
|
361
|
+
def current_database_name
|
362
|
+
defined?(@current_database) ? current_database.name : :none
|
363
|
+
end
|
364
|
+
|
350
365
|
def initialize_copy(_)
|
351
366
|
@context = Context.new(self)
|
352
367
|
@options = @options.dup
|
353
|
-
|
354
|
-
|
355
|
-
remove_instance_variable :@current_database
|
368
|
+
if defined?(@current_database)
|
369
|
+
remove_instance_variable(:@current_database)
|
356
370
|
end
|
357
371
|
end
|
358
372
|
|
@@ -13,7 +13,7 @@ module Moped
|
|
13
13
|
#
|
14
14
|
# @since 1.0.0
|
15
15
|
def alive?
|
16
|
-
if Kernel::select([ self ], nil,
|
16
|
+
if Kernel::select([ self ], nil, [ self ], 0)
|
17
17
|
!eof? rescue false
|
18
18
|
else
|
19
19
|
true
|
@@ -42,6 +42,7 @@ module Moped
|
|
42
42
|
#
|
43
43
|
# @since 1.2.0
|
44
44
|
def read(length)
|
45
|
+
check_if_alive!
|
45
46
|
handle_socket_errors { super }
|
46
47
|
end
|
47
48
|
|
@@ -56,30 +57,77 @@ module Moped
|
|
56
57
|
#
|
57
58
|
# @since 1.0.0
|
58
59
|
def write(*args)
|
59
|
-
|
60
|
+
check_if_alive!
|
60
61
|
handle_socket_errors { super }
|
61
62
|
end
|
62
63
|
|
63
64
|
private
|
64
65
|
|
66
|
+
# Before performing a read or write operating, ping the server to check
|
67
|
+
# if it is alive.
|
68
|
+
#
|
69
|
+
# @api private
|
70
|
+
#
|
71
|
+
# @example Check if the connection is alive.
|
72
|
+
# connectable.check_if_alive!
|
73
|
+
#
|
74
|
+
# @raise [ ConnectionFailure ] If the connectable is not alive.
|
75
|
+
#
|
76
|
+
# @since 1.4.0
|
77
|
+
def check_if_alive!
|
78
|
+
unless alive?
|
79
|
+
raise Errors::ConnectionFailure, "Socket connection was closed by remote host"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Generate the message for the connection failure based of the system
|
84
|
+
# call error, with some added information.
|
85
|
+
#
|
86
|
+
# @api private
|
87
|
+
#
|
88
|
+
# @example Generate the error message.
|
89
|
+
# connectable.generate_message(error)
|
90
|
+
#
|
91
|
+
# @param [ SystemCallError ] error The error.
|
92
|
+
#
|
93
|
+
# @return [ String ] The error message.
|
94
|
+
#
|
95
|
+
# @since 1.4.0
|
96
|
+
def generate_message(error)
|
97
|
+
"#{host}:#{port}: #{error.class.name} (#{error.errno}): #{error.message}"
|
98
|
+
end
|
99
|
+
|
65
100
|
# Handle the potential socket errors that can occur.
|
66
101
|
#
|
67
102
|
# @api private
|
68
103
|
#
|
69
104
|
# @example Handle the socket errors while executing the block.
|
70
105
|
# handle_socket_errors do
|
71
|
-
#
|
106
|
+
# socket.read(128)
|
72
107
|
# end
|
73
108
|
#
|
109
|
+
# @raise [ Moped::Errors::ConnectionFailure ] If a system call error or
|
110
|
+
# IOError occured which can be retried.
|
111
|
+
# @raise [ Moped::Errors::Unrecoverable ] If a system call error occured
|
112
|
+
# which cannot be retried and should be re-raised.
|
113
|
+
#
|
74
114
|
# @return [ Object ] The result of the yield.
|
75
115
|
#
|
76
116
|
# @since 1.0.0
|
77
117
|
def handle_socket_errors
|
78
118
|
yield
|
79
|
-
rescue Errno::ECONNREFUSED
|
80
|
-
raise Errors::ConnectionFailure,
|
81
|
-
rescue Errno::
|
82
|
-
raise Errors::ConnectionFailure,
|
119
|
+
rescue Errno::ECONNREFUSED => e
|
120
|
+
raise Errors::ConnectionFailure, generate_message(e)
|
121
|
+
rescue Errno::EHOSTUNREACH => e
|
122
|
+
raise Errors::ConnectionFailure, generate_message(e)
|
123
|
+
rescue Errno::EPIPE => e
|
124
|
+
raise Errors::ConnectionFailure, generate_message(e)
|
125
|
+
rescue Errno::ECONNRESET => e
|
126
|
+
raise Errors::ConnectionFailure, generate_message(e)
|
127
|
+
rescue Errno::ETIMEDOUT => e
|
128
|
+
raise Errors::ConnectionFailure, generate_message(e)
|
129
|
+
rescue IOError
|
130
|
+
raise Errors::ConnectionFailure, "Connection timed out to Mongo on #{host}:#{port}"
|
83
131
|
rescue OpenSSL::SSL::SSLError => e
|
84
132
|
raise Errors::ConnectionFailure, "SSL Error '#{e.to_s}' for connection to Mongo on #{host}:#{port}"
|
85
133
|
end
|
@@ -103,7 +151,10 @@ module Moped
|
|
103
151
|
Timeout::timeout(timeout) do
|
104
152
|
sock = new(host, port)
|
105
153
|
sock.set_encoding('binary')
|
154
|
+
timeout_val = [ timeout, 0 ].pack("l_2")
|
106
155
|
sock.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
156
|
+
sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, timeout_val)
|
157
|
+
sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_SNDTIMEO, timeout_val)
|
107
158
|
sock
|
108
159
|
end
|
109
160
|
rescue Timeout::Error
|