mongo 1.6.1 → 1.6.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.
- data/README.md +17 -16
- data/Rakefile +30 -24
- data/docs/HISTORY.md +10 -0
- data/docs/RELEASES.md +7 -0
- data/docs/REPLICA_SETS.md +2 -2
- data/docs/TUTORIAL.md +181 -84
- data/lib/mongo.rb +0 -3
- data/lib/mongo/collection.rb +1 -1
- data/lib/mongo/connection.rb +28 -20
- data/lib/mongo/db.rb +5 -3
- data/lib/mongo/exceptions.rb +1 -1
- data/lib/mongo/gridfs/grid_file_system.rb +1 -1
- data/lib/mongo/networking.rb +18 -18
- data/lib/mongo/repl_set_connection.rb +24 -3
- data/lib/mongo/util/node.rb +6 -16
- data/lib/mongo/util/pool.rb +8 -11
- data/lib/mongo/util/ssl_socket.rb +34 -12
- data/lib/mongo/util/tcp_socket.rb +92 -3
- data/lib/mongo/util/uri_parser.rb +2 -2
- data/lib/mongo/version.rb +1 -1
- data/test/auxillary/repl_set_auth_test.rb +19 -5
- data/test/bson/bson_test.rb +23 -21
- data/test/bson/json_test.rb +1 -1
- data/test/collection_test.rb +14 -4
- data/test/connection_test.rb +14 -11
- data/test/cursor_test.rb +4 -4
- data/test/grid_file_system_test.rb +3 -1
- data/test/grid_io_test.rb +2 -2
- data/test/grid_test.rb +13 -6
- data/test/pool_test.rb +0 -2
- data/test/replica_sets/basic_test.rb +1 -1
- data/test/replica_sets/complex_connect_test.rb +5 -4
- data/test/replica_sets/connect_test.rb +14 -6
- data/test/replica_sets/pooled_insert_test.rb +1 -1
- data/test/replica_sets/query_test.rb +2 -2
- data/test/replica_sets/read_preference_test.rb +1 -2
- data/test/replica_sets/refresh_test.rb +4 -2
- data/test/replica_sets/refresh_with_threads_test.rb +7 -13
- data/test/replica_sets/rs_test_helper.rb +1 -1
- data/test/test_helper.rb +6 -4
- data/test/threading/threading_with_large_pool_test.rb +1 -1
- data/test/threading_test.rb +2 -2
- data/test/timeout_test.rb +40 -0
- data/test/tools/repl_set_manager.rb +9 -8
- data/test/unit/connection_test.rb +6 -7
- data/test/unit/node_test.rb +1 -0
- data/test/unit/pool_manager_test.rb +2 -1
- data/test/uri_test.rb +1 -2
- metadata +13 -6
data/lib/mongo.rb
CHANGED
data/lib/mongo/collection.rb
CHANGED
@@ -961,7 +961,7 @@ module Mongo
|
|
961
961
|
begin
|
962
962
|
message.put_binary(BSON::BSON_CODER.serialize(doc, check_keys, true, @connection.max_bson_size).to_s)
|
963
963
|
true
|
964
|
-
rescue StandardError
|
964
|
+
rescue StandardError # StandardError will be replaced with BSONError
|
965
965
|
doc.delete(:_id)
|
966
966
|
error_docs << doc
|
967
967
|
false
|
data/lib/mongo/connection.rb
CHANGED
@@ -26,7 +26,7 @@ module Mongo
|
|
26
26
|
include Mongo::Logging
|
27
27
|
include Mongo::Networking
|
28
28
|
|
29
|
-
TCPSocket = ::TCPSocket
|
29
|
+
TCPSocket = Mongo::TCPSocket
|
30
30
|
Mutex = ::Mutex
|
31
31
|
ConditionVariable = ::ConditionVariable
|
32
32
|
|
@@ -67,7 +67,7 @@ module Mongo
|
|
67
67
|
# logging negatively impacts performance; therefore, it should not be used for high-performance apps.
|
68
68
|
# @option opts [Integer] :pool_size (1) The maximum number of socket self.connections allowed per
|
69
69
|
# connection pool. Note: this setting is relevant only for multi-threaded applications.
|
70
|
-
# @option opts [Float] :
|
70
|
+
# @option opts [Float] :timeout (5.0) When all of the self.connections a pool are checked out,
|
71
71
|
# this is the number of seconds to wait for a new connection to be released before throwing an exception.
|
72
72
|
# Note: this setting is relevant only for multi-threaded applications (which in Ruby are rare).
|
73
73
|
# @option opts [Float] :op_timeout (nil) The number of seconds to wait for a read operation to time out.
|
@@ -480,6 +480,12 @@ module Mongo
|
|
480
480
|
@max_bson_size
|
481
481
|
end
|
482
482
|
|
483
|
+
# Prefer primary pool but fall back to secondary
|
484
|
+
def checkout_best
|
485
|
+
connect unless connected?
|
486
|
+
@primary_pool.checkout
|
487
|
+
end
|
488
|
+
|
483
489
|
# Checkout a socket for reading (i.e., a secondary node).
|
484
490
|
# Note: this is overridden in ReplSetConnection.
|
485
491
|
def checkout_reader
|
@@ -513,6 +519,19 @@ module Mongo
|
|
513
519
|
end
|
514
520
|
end
|
515
521
|
|
522
|
+
# Excecutes block with the best available socket
|
523
|
+
def best_available_socket
|
524
|
+
socket = nil
|
525
|
+
begin
|
526
|
+
socket = checkout_best
|
527
|
+
yield socket
|
528
|
+
ensure
|
529
|
+
if socket
|
530
|
+
socket.pool.checkin(socket)
|
531
|
+
end
|
532
|
+
end
|
533
|
+
end
|
534
|
+
|
516
535
|
protected
|
517
536
|
|
518
537
|
def valid_opts
|
@@ -603,27 +622,16 @@ module Mongo
|
|
603
622
|
socket = nil
|
604
623
|
config = nil
|
605
624
|
|
606
|
-
|
607
|
-
|
608
|
-
socket = @socket_class.new(host, port)
|
609
|
-
socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
610
|
-
end
|
611
|
-
else
|
612
|
-
socket = @socket_class.new(host, port)
|
613
|
-
socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
614
|
-
end
|
625
|
+
socket = @socket_class.new(host, port, @op_timeout, @connect_timeout)
|
626
|
+
socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
615
627
|
|
616
|
-
|
617
|
-
|
618
|
-
config = self['admin'].command({:ismaster => 1}, :socket => socket)
|
619
|
-
end
|
620
|
-
else
|
621
|
-
config = self['admin'].command({:ismaster => 1}, :socket => socket)
|
622
|
-
end
|
623
|
-
rescue OperationFailure, SocketError, SystemCallError, IOError => ex
|
628
|
+
config = self['admin'].command({:ismaster => 1}, :socket => socket)
|
629
|
+
rescue OperationFailure, SocketError, SystemCallError, IOError
|
624
630
|
close
|
625
631
|
ensure
|
626
|
-
|
632
|
+
if socket
|
633
|
+
socket.close unless socket.closed?
|
634
|
+
end
|
627
635
|
end
|
628
636
|
|
629
637
|
config
|
data/lib/mongo/db.rb
CHANGED
@@ -17,7 +17,6 @@
|
|
17
17
|
# ++
|
18
18
|
|
19
19
|
require 'socket'
|
20
|
-
require 'timeout'
|
21
20
|
require 'thread'
|
22
21
|
|
23
22
|
module Mongo
|
@@ -111,10 +110,13 @@ module Mongo
|
|
111
110
|
if !save_auth
|
112
111
|
raise MongoArgumentError, "If using connection pooling, :save_auth must be set to true."
|
113
112
|
end
|
114
|
-
@connection.authenticate_pools
|
115
113
|
end
|
116
114
|
|
117
|
-
|
115
|
+
@connection.best_available_socket do |socket|
|
116
|
+
issue_authentication(username, password, save_auth, :socket => socket)
|
117
|
+
end
|
118
|
+
|
119
|
+
@connection.authenticate_pools
|
118
120
|
end
|
119
121
|
|
120
122
|
def issue_authentication(username, password, save_auth=true, opts={})
|
data/lib/mongo/exceptions.rb
CHANGED
@@ -71,7 +71,7 @@ module Mongo
|
|
71
71
|
class OperationFailure < MongoDBError; end
|
72
72
|
|
73
73
|
# Raised when a socket read operation times out.
|
74
|
-
class OperationTimeout <
|
74
|
+
class OperationTimeout < SocketError; end
|
75
75
|
|
76
76
|
# Raised when a client attempts to perform an invalid operation.
|
77
77
|
class InvalidOperation < MongoDBError; end
|
@@ -115,7 +115,7 @@ module Mongo
|
|
115
115
|
id = file.close
|
116
116
|
if versions
|
117
117
|
self.delete do
|
118
|
-
@files.find({'filename' => filename, '_id' => {'$ne' => id}}, :fields => ['_id'], :sort => ['uploadDate', -1], :skip => (versions -1))
|
118
|
+
@files.find({'filename' => filename, '_id' => {'$ne' => id}}, :fields => ['_id'], :sort => ['uploadDate', -1], :skip => (versions - 1))
|
119
119
|
end
|
120
120
|
end
|
121
121
|
end
|
data/lib/mongo/networking.rb
CHANGED
@@ -186,7 +186,10 @@ module Mongo
|
|
186
186
|
|
187
187
|
def receive_header(sock, expected_response, exhaust=false)
|
188
188
|
header = receive_message_on_socket(16, sock)
|
189
|
-
|
189
|
+
|
190
|
+
# unpacks to size, request_id, response_to
|
191
|
+
response_to = header.unpack('VVV')[2]
|
192
|
+
|
190
193
|
if !exhaust && expected_response != response_to
|
191
194
|
raise Mongo::ConnectionFailure, "Expected response #{expected_response} but got #{response_to}"
|
192
195
|
end
|
@@ -204,7 +207,10 @@ module Mongo
|
|
204
207
|
raise "Short read for DB response header; " +
|
205
208
|
"expected #{RESPONSE_HEADER_SIZE} bytes, saw #{header_buf.length}"
|
206
209
|
end
|
207
|
-
|
210
|
+
|
211
|
+
# unpacks to flags, cursor_id_a, cursor_id_b, starting_from, number_remaining
|
212
|
+
flags, cursor_id_a, cursor_id_b, _, number_remaining = header_buf.unpack('VVVVV')
|
213
|
+
|
208
214
|
check_response_flags(flags)
|
209
215
|
cursor_id = (cursor_id_b << 32) + cursor_id_a
|
210
216
|
[number_remaining, cursor_id]
|
@@ -294,11 +300,11 @@ module Mongo
|
|
294
300
|
# @return [Integer] number of bytes sent
|
295
301
|
def send_message_on_socket(packed_message, socket)
|
296
302
|
begin
|
297
|
-
total_bytes_sent = socket.send(packed_message
|
303
|
+
total_bytes_sent = socket.send(packed_message)
|
298
304
|
if total_bytes_sent != packed_message.size
|
299
305
|
packed_message.slice!(0, total_bytes_sent)
|
300
306
|
while packed_message.size > 0
|
301
|
-
byte_sent = socket.send(packed_message
|
307
|
+
byte_sent = socket.send(packed_message)
|
302
308
|
total_bytes_sent += byte_sent
|
303
309
|
packed_message.slice!(0, byte_sent)
|
304
310
|
end
|
@@ -314,22 +320,15 @@ module Mongo
|
|
314
320
|
# Requires length and an available socket.
|
315
321
|
def receive_message_on_socket(length, socket)
|
316
322
|
begin
|
317
|
-
if @op_timeout
|
318
|
-
message = nil
|
319
|
-
Mongo::TimeoutHandler.timeout(@op_timeout, OperationTimeout) do
|
320
|
-
message = receive_data(length, socket)
|
321
|
-
end
|
322
|
-
else
|
323
323
|
message = receive_data(length, socket)
|
324
|
-
|
325
|
-
|
326
|
-
close
|
324
|
+
rescue OperationTimeout, ConnectionFailure => ex
|
325
|
+
close
|
327
326
|
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
327
|
+
if ex.class == OperationTimeout
|
328
|
+
raise OperationTimeout, "Timed out waiting on socket read."
|
329
|
+
else
|
330
|
+
raise ConnectionFailure, "Operation failed with the following exception: #{ex}"
|
331
|
+
end
|
333
332
|
end
|
334
333
|
message
|
335
334
|
end
|
@@ -337,6 +336,7 @@ module Mongo
|
|
337
336
|
def receive_data(length, socket)
|
338
337
|
message = new_binary_string
|
339
338
|
socket.read(length, message)
|
339
|
+
|
340
340
|
raise ConnectionFailure, "connection closed" unless message && message.length > 0
|
341
341
|
if message.length < length
|
342
342
|
chunk = new_binary_string
|
@@ -287,14 +287,18 @@ module Mongo
|
|
287
287
|
end
|
288
288
|
|
289
289
|
def authenticate_pools
|
290
|
-
primary_pool
|
290
|
+
if primary_pool
|
291
|
+
primary_pool.authenticate_existing
|
292
|
+
end
|
291
293
|
secondary_pools.each do |pool|
|
292
294
|
pool.authenticate_existing
|
293
295
|
end
|
294
296
|
end
|
295
297
|
|
296
298
|
def logout_pools(db)
|
297
|
-
primary_pool
|
299
|
+
if primary_pool
|
300
|
+
primary_pool.logout_existing(db)
|
301
|
+
end
|
298
302
|
secondary_pools.each do |pool|
|
299
303
|
pool.logout_existing(db)
|
300
304
|
end
|
@@ -323,6 +327,19 @@ module Mongo
|
|
323
327
|
raise ConnectionFailure.new("Could not checkout a socket.")
|
324
328
|
end
|
325
329
|
end
|
330
|
+
|
331
|
+
# Checkout best available socket by trying primary
|
332
|
+
# pool first and then falling back to secondary.
|
333
|
+
def checkout_best
|
334
|
+
checkout do
|
335
|
+
socket = get_socket_from_pool(:primary)
|
336
|
+
if !socket
|
337
|
+
connect
|
338
|
+
socket = get_socket_from_pool(:secondary)
|
339
|
+
end
|
340
|
+
socket
|
341
|
+
end
|
342
|
+
end
|
326
343
|
|
327
344
|
# Checkout a socket for reading (i.e., a secondary node).
|
328
345
|
# Note that @read_pool might point to the primary pool
|
@@ -459,7 +476,11 @@ module Mongo
|
|
459
476
|
|
460
477
|
# Refresh
|
461
478
|
@refresh_mode = opts.fetch(:refresh_mode, false)
|
462
|
-
@refresh_interval = opts
|
479
|
+
@refresh_interval = opts.fetch(:refresh_interval, 90)
|
480
|
+
|
481
|
+
if @refresh_mode && @refresh_interval < 60
|
482
|
+
@refresh_interval = 60 unless ENV['TEST_MODE'] = 'TRUE'
|
483
|
+
end
|
463
484
|
|
464
485
|
if @refresh_mode == :async
|
465
486
|
warn ":async refresh mode has been deprecated. Refresh
|
data/lib/mongo/util/node.rb
CHANGED
@@ -36,20 +36,16 @@ module Mongo
|
|
36
36
|
def connect
|
37
37
|
begin
|
38
38
|
socket = nil
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
end
|
43
|
-
else
|
44
|
-
socket = @connection.socket_class.new(@host, @port)
|
45
|
-
end
|
39
|
+
socket = @connection.socket_class.new(@host, @port,
|
40
|
+
@connection.op_timeout, @connection.connect_timeout
|
41
|
+
)
|
46
42
|
|
47
43
|
if socket.nil?
|
48
44
|
return nil
|
49
45
|
else
|
50
46
|
socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
51
47
|
end
|
52
|
-
rescue OperationTimeout, OperationFailure, SocketError, SystemCallError, IOError => ex
|
48
|
+
rescue OperationTimeout, ConnectionFailure, OperationFailure, SocketError, SystemCallError, IOError => ex
|
53
49
|
@connection.log(:debug, "Failed connection to #{host_string} with #{ex.class}, #{ex.message}.")
|
54
50
|
socket.close if socket
|
55
51
|
return nil
|
@@ -74,7 +70,7 @@ module Mongo
|
|
74
70
|
begin
|
75
71
|
result = @connection['admin'].command({:ping => 1}, :socket => @socket)
|
76
72
|
return result['ok'] == 1
|
77
|
-
rescue OperationFailure, SocketError, SystemCallError, IOError
|
73
|
+
rescue OperationFailure, SocketError, SystemCallError, IOError
|
78
74
|
return nil
|
79
75
|
end
|
80
76
|
end
|
@@ -84,13 +80,7 @@ module Mongo
|
|
84
80
|
# matches with the name provided.
|
85
81
|
def set_config
|
86
82
|
begin
|
87
|
-
|
88
|
-
Mongo::TimeoutHandler.timeout(@connection.connect_timeout, OperationTimeout) do
|
89
|
-
@config = @connection['admin'].command({:ismaster => 1}, :socket => @socket)
|
90
|
-
end
|
91
|
-
else
|
92
|
-
@config = @connection['admin'].command({:ismaster => 1}, :socket => @socket)
|
93
|
-
end
|
83
|
+
@config = @connection['admin'].command({:ismaster => 1}, :socket => @socket)
|
94
84
|
|
95
85
|
if @config['msg'] && @logger
|
96
86
|
@connection.log(:warn, "#{config['msg']}")
|
data/lib/mongo/util/pool.rb
CHANGED
@@ -133,7 +133,7 @@ module Mongo
|
|
133
133
|
def ping
|
134
134
|
begin
|
135
135
|
return self.connection['admin'].command({:ping => 1}, :socket => @node.socket)
|
136
|
-
rescue OperationFailure, SocketError, SystemCallError, IOError
|
136
|
+
rescue OperationFailure, SocketError, SystemCallError, IOError
|
137
137
|
return false
|
138
138
|
end
|
139
139
|
end
|
@@ -156,7 +156,7 @@ module Mongo
|
|
156
156
|
# therefore, it runs within a mutex.
|
157
157
|
def checkout_new_socket
|
158
158
|
begin
|
159
|
-
socket =
|
159
|
+
socket = @connection.socket_class.new(@host, @port, @connection.op_timeout)
|
160
160
|
socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
161
161
|
socket.pool = self
|
162
162
|
rescue => ex
|
@@ -217,7 +217,9 @@ module Mongo
|
|
217
217
|
if @pids[socket] != Process.pid
|
218
218
|
@pids[socket] = nil
|
219
219
|
@sockets.delete(socket)
|
220
|
-
|
220
|
+
if socket
|
221
|
+
socket.close unless socket.closed?
|
222
|
+
end
|
221
223
|
checkout_new_socket
|
222
224
|
else
|
223
225
|
@checked_out << socket
|
@@ -227,15 +229,10 @@ module Mongo
|
|
227
229
|
end
|
228
230
|
|
229
231
|
def prune_thread_socket_hash
|
230
|
-
|
231
|
-
Thread.list.each do |t|
|
232
|
-
map[t] = 1
|
233
|
-
end
|
232
|
+
current_threads = Set[*Thread.list]
|
234
233
|
|
235
|
-
@threads_to_sockets.
|
236
|
-
|
237
|
-
@threads_to_sockets.delete(key)
|
238
|
-
end
|
234
|
+
@threads_to_sockets.delete_if do |thread, socket|
|
235
|
+
!current_threads.include?(thread)
|
239
236
|
end
|
240
237
|
end
|
241
238
|
|
@@ -1,4 +1,6 @@
|
|
1
|
+
require 'socket'
|
1
2
|
require 'openssl'
|
3
|
+
require 'timeout'
|
2
4
|
|
3
5
|
module Mongo
|
4
6
|
|
@@ -9,31 +11,51 @@ module Mongo
|
|
9
11
|
|
10
12
|
attr_accessor :pool
|
11
13
|
|
12
|
-
def initialize(host, port)
|
13
|
-
@
|
14
|
+
def initialize(host, port, op_timeout=nil, connect_timeout=nil)
|
15
|
+
@op_timeout = op_timeout
|
16
|
+
@connect_timeout = connect_timeout
|
17
|
+
|
18
|
+
@socket = ::TCPSocket.new(host, port)
|
14
19
|
@ssl = OpenSSL::SSL::SSLSocket.new(@socket)
|
15
20
|
@ssl.sync_close = true
|
16
|
-
|
21
|
+
|
22
|
+
connect
|
17
23
|
end
|
18
24
|
|
19
|
-
def
|
20
|
-
@
|
25
|
+
def connect
|
26
|
+
if @connect_timeout
|
27
|
+
Timeout::timeout(@connect_timeout, ConnectionTimeoutError) do
|
28
|
+
@ssl.connect
|
29
|
+
end
|
30
|
+
else
|
31
|
+
@ssl.connect
|
32
|
+
end
|
21
33
|
end
|
22
34
|
|
23
|
-
|
24
|
-
|
25
|
-
# @param buffer a buffer to send.
|
26
|
-
# @param flags socket flags. Because Ruby's SSL
|
27
|
-
def send(buffer, flags=0)
|
28
|
-
@ssl.syswrite(buffer)
|
35
|
+
def send(data)
|
36
|
+
@ssl.syswrite(data)
|
29
37
|
end
|
30
38
|
|
31
39
|
def read(length, buffer)
|
32
|
-
@
|
40
|
+
if @op_timeout
|
41
|
+
Timeout::timeout(@op_timeout, OperationTimeout) do
|
42
|
+
@ssl.sysread(length, buffer)
|
43
|
+
end
|
44
|
+
else
|
45
|
+
@ssl.sysread(length, buffer)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def setsockopt(key, value, n)
|
50
|
+
@ssl.setsockopt(key, value, n)
|
33
51
|
end
|
34
52
|
|
35
53
|
def close
|
36
54
|
@ssl.close
|
37
55
|
end
|
56
|
+
|
57
|
+
def closed?
|
58
|
+
@ssl.closed?
|
59
|
+
end
|
38
60
|
end
|
39
61
|
end
|
@@ -1,6 +1,95 @@
|
|
1
|
+
require 'socket'
|
2
|
+
|
1
3
|
module Mongo
|
2
|
-
class
|
3
|
-
|
4
|
+
# Wrapper class for Socket
|
5
|
+
#
|
6
|
+
# Emulates TCPSocket with operation and connection timeout
|
7
|
+
# sans Timeout::timeout
|
8
|
+
#
|
9
|
+
class TCPSocket
|
10
|
+
attr_accessor :pool
|
11
|
+
|
12
|
+
def initialize(host, port, op_timeout=nil, connect_timeout=nil)
|
13
|
+
@op_timeout = op_timeout
|
14
|
+
@connect_timeout = connect_timeout
|
15
|
+
|
16
|
+
# TODO: Prefer ipv6 if server is ipv6 enabled
|
17
|
+
@host = Socket.getaddrinfo(host, nil, Socket::AF_INET).first[3]
|
18
|
+
@port = port
|
19
|
+
@socket_address = Socket.pack_sockaddr_in(@port, @host)
|
20
|
+
@socket = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
|
21
|
+
|
22
|
+
connect
|
23
|
+
end
|
24
|
+
|
25
|
+
def connect
|
26
|
+
# Connect nonblock is broken in current versions of JRuby
|
27
|
+
if RUBY_PLATFORM == 'java'
|
28
|
+
require 'timeout'
|
29
|
+
if @connect_timeout
|
30
|
+
Timeout::timeout(@connect_timeout, OperationTimeout) do
|
31
|
+
@socket.connect(@socket_address)
|
32
|
+
end
|
33
|
+
else
|
34
|
+
@socket.connect(@socket_address)
|
35
|
+
end
|
36
|
+
else
|
37
|
+
# Try to connect for @connect_timeout seconds
|
38
|
+
begin
|
39
|
+
@socket.connect_nonblock(@socket_address)
|
40
|
+
rescue Errno::EINPROGRESS
|
41
|
+
# Block until there is a response or error
|
42
|
+
resp = IO.select([@socket], [@socket], [@socket], @connect_timeout)
|
43
|
+
if resp.nil?
|
44
|
+
raise ConnectionTimeoutError
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# If there was a failure this will raise an Error
|
49
|
+
begin
|
50
|
+
@socket.connect_nonblock(@socket_address)
|
51
|
+
rescue Errno::EISCONN
|
52
|
+
# Successfully connected
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def send(data)
|
58
|
+
@socket.write(data)
|
59
|
+
end
|
60
|
+
|
61
|
+
def read(maxlen, buffer)
|
62
|
+
# Block on data to read for @op_timeout seconds
|
63
|
+
begin
|
64
|
+
ready = IO.select([@socket], nil, [@socket], @op_timeout)
|
65
|
+
rescue IOError
|
66
|
+
raise OperationFailure
|
67
|
+
end
|
68
|
+
if ready
|
69
|
+
begin
|
70
|
+
@socket.readpartial(maxlen, buffer)
|
71
|
+
rescue EOFError
|
72
|
+
return ConnectionError
|
73
|
+
rescue Errno::ENOTCONN, Errno::EBADF, Errno::ECONNRESET, Errno::EPIPE
|
74
|
+
raise ConnectionFailure
|
75
|
+
rescue Errno::EINTR, Errno::EIO, IOError
|
76
|
+
raise OperationFailure
|
77
|
+
end
|
78
|
+
else
|
79
|
+
raise OperationTimeout
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def setsockopt(key, value, n)
|
84
|
+
@socket.setsockopt(key, value, n)
|
85
|
+
end
|
86
|
+
|
87
|
+
def close
|
88
|
+
@socket.close
|
89
|
+
end
|
4
90
|
|
5
|
-
|
91
|
+
def closed?
|
92
|
+
@socket.closed?
|
93
|
+
end
|
94
|
+
end
|
6
95
|
end
|