eventmachine 1.2.0.1 → 1.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -0
- data/README.md +25 -23
- data/ext/ed.cpp +40 -65
- data/ext/ed.h +23 -8
- data/ext/em.cpp +23 -12
- data/ext/em.h +1 -1
- data/ext/rubymain.cpp +19 -15
- data/java/src/com/rubyeventmachine/EmReactor.java +18 -2
- data/lib/em/pool.rb +35 -35
- data/lib/em/protocols/linetext2.rb +18 -5
- data/lib/em/pure_ruby.rb +290 -52
- data/lib/em/version.rb +1 -1
- data/lib/eventmachine.rb +6 -0
- data/lib/jeventmachine.rb +2 -0
- data/rakelib/test_pure.rake +13 -0
- data/tests/em_test_helper.rb +3 -0
- data/tests/test_basic.rb +32 -0
- data/tests/test_ltp2.rb +24 -0
- data/tests/test_pure.rb +51 -0
- data/tests/test_ssl_dhparam.rb +2 -1
- data/tests/test_ssl_ecdh_curve.rb +2 -1
- data/tests/test_ssl_extensions.rb +1 -1
- data/tests/test_ssl_protocols.rb +3 -3
- data/tests/test_ssl_verify.rb +2 -0
- metadata +29 -28
@@ -521,11 +521,27 @@ public class EmReactor {
|
|
521
521
|
}
|
522
522
|
|
523
523
|
public Object[] getPeerName (long sig) {
|
524
|
-
|
524
|
+
EventableChannel channel = Connections.get(sig);
|
525
|
+
if (channel != null) {
|
526
|
+
return Connections.get(sig).getPeerName();
|
527
|
+
}
|
528
|
+
else {
|
529
|
+
ServerSocketChannel acceptor = Acceptors.get(sig);
|
530
|
+
return new Object[] { acceptor.socket().getLocalPort(),
|
531
|
+
acceptor.socket().getInetAddress().getHostAddress() };
|
532
|
+
}
|
525
533
|
}
|
526
534
|
|
527
535
|
public Object[] getSockName (long sig) {
|
528
|
-
|
536
|
+
EventableChannel channel = Connections.get(sig);
|
537
|
+
if (channel != null) {
|
538
|
+
return Connections.get(sig).getSockName();
|
539
|
+
}
|
540
|
+
else {
|
541
|
+
ServerSocketChannel acceptor = Acceptors.get(sig);
|
542
|
+
return new Object[] { acceptor.socket().getLocalPort(),
|
543
|
+
acceptor.socket().getInetAddress().getHostAddress() };
|
544
|
+
}
|
529
545
|
}
|
530
546
|
|
531
547
|
public long attachChannel (SocketChannel sc, boolean watch_mode) {
|
data/lib/em/pool.rb
CHANGED
@@ -1,41 +1,41 @@
|
|
1
1
|
module EventMachine
|
2
|
-
# = EventMachine::Pool
|
3
|
-
#
|
4
2
|
# A simple async resource pool based on a resource and work queue. Resources
|
5
3
|
# are enqueued and work waits for resources to become available.
|
6
4
|
#
|
7
|
-
#
|
5
|
+
# @example
|
6
|
+
# require 'em-http-request'
|
7
|
+
#
|
8
|
+
# EM.run do
|
9
|
+
# pool = EM::Pool.new
|
10
|
+
# spawn = lambda { pool.add EM::HttpRequest.new('http://example.org') }
|
11
|
+
# 10.times { spawn[] }
|
12
|
+
# done, scheduled = 0, 0
|
13
|
+
#
|
14
|
+
# check = lambda do
|
15
|
+
# done += 1
|
16
|
+
# if done >= scheduled
|
17
|
+
# EM.stop
|
18
|
+
# end
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# pool.on_error { |conn| spawn[] }
|
22
|
+
#
|
23
|
+
# 100.times do |i|
|
24
|
+
# scheduled += 1
|
25
|
+
# pool.perform do |conn|
|
26
|
+
# req = conn.get :path => '/', :keepalive => true
|
27
|
+
#
|
28
|
+
# req.callback do
|
29
|
+
# p [:success, conn.object_id, i, req.response.size]
|
30
|
+
# check[]
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# req.errback { check[] }
|
8
34
|
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
# done, scheduled = 0, 0
|
14
|
-
#
|
15
|
-
# check = lambda do
|
16
|
-
# done += 1
|
17
|
-
# if done >= scheduled
|
18
|
-
# EM.stop
|
19
|
-
# end
|
20
|
-
# end
|
21
|
-
#
|
22
|
-
# pool.on_error { |conn| spawn[] }
|
23
|
-
#
|
24
|
-
# 100.times do
|
25
|
-
# pool.perform do |conn|
|
26
|
-
# req = conn.get :path => '/', :keepalive => true
|
27
|
-
#
|
28
|
-
# req.callback do
|
29
|
-
# p [:success, conn.object_id, i, req.response.size]
|
30
|
-
# check[]
|
31
|
-
# end
|
32
|
-
#
|
33
|
-
# req.errback { check[] }
|
34
|
-
#
|
35
|
-
# req
|
36
|
-
# end
|
37
|
-
# end
|
38
|
-
# end
|
35
|
+
# req
|
36
|
+
# end
|
37
|
+
# end
|
38
|
+
# end
|
39
39
|
#
|
40
40
|
# Resources are expected to be controlled by an object responding to a
|
41
41
|
# deferrable/completion style API with callback and errback blocks.
|
@@ -64,8 +64,8 @@ module EventMachine
|
|
64
64
|
# example use case is periodic statistics collection against a set of
|
65
65
|
# connection resources.
|
66
66
|
#
|
67
|
-
#
|
68
|
-
#
|
67
|
+
# @example
|
68
|
+
# pool.contents.inject(0) { |sum, connection| connection.num_bytes }
|
69
69
|
def contents
|
70
70
|
@contents.dup
|
71
71
|
end
|
@@ -56,7 +56,14 @@ module EventMachine
|
|
56
56
|
|
57
57
|
while remaining_data.length > 0
|
58
58
|
if @lt2_mode == :lines
|
59
|
-
|
59
|
+
delimiter_string = case @lt2_delimiter
|
60
|
+
when Regexp
|
61
|
+
remaining_data.slice(@lt2_delimiter)
|
62
|
+
else
|
63
|
+
@lt2_delimiter
|
64
|
+
end
|
65
|
+
ix = remaining_data.index(delimiter_string) if delimiter_string
|
66
|
+
if ix
|
60
67
|
@lt2_linebuffer << remaining_data[0...ix]
|
61
68
|
ln = @lt2_linebuffer.join
|
62
69
|
@lt2_linebuffer.clear
|
@@ -64,7 +71,7 @@ module EventMachine
|
|
64
71
|
ln.chomp!
|
65
72
|
end
|
66
73
|
receive_line ln
|
67
|
-
remaining_data = remaining_data[(ix
|
74
|
+
remaining_data = remaining_data[(ix+delimiter_string.length)..-1]
|
68
75
|
else
|
69
76
|
@lt2_linebuffer << remaining_data
|
70
77
|
remaining_data = ""
|
@@ -101,9 +108,16 @@ module EventMachine
|
|
101
108
|
end
|
102
109
|
end
|
103
110
|
|
104
|
-
|
111
|
+
# The line delimiter may be a regular expression or a string. Anything
|
112
|
+
# passed to set_delimiter other than a regular expression will be
|
113
|
+
# converted to a string.
|
105
114
|
def set_delimiter delim
|
106
|
-
@lt2_delimiter = delim
|
115
|
+
@lt2_delimiter = case delim
|
116
|
+
when Regexp
|
117
|
+
delim
|
118
|
+
else
|
119
|
+
delim.to_s
|
120
|
+
end
|
107
121
|
end
|
108
122
|
|
109
123
|
# Called internally but also exposed to user code, for the case in which
|
@@ -163,4 +177,3 @@ module EventMachine
|
|
163
177
|
end
|
164
178
|
end
|
165
179
|
end
|
166
|
-
|
data/lib/em/pure_ruby.rb
CHANGED
@@ -33,6 +33,88 @@ require 'forwardable'
|
|
33
33
|
require 'socket'
|
34
34
|
require 'fcntl'
|
35
35
|
require 'set'
|
36
|
+
require 'openssl'
|
37
|
+
|
38
|
+
module EventMachine
|
39
|
+
# @private
|
40
|
+
class Error < Exception; end
|
41
|
+
# @private
|
42
|
+
class UnknownTimerFired < RuntimeError; end
|
43
|
+
# @private
|
44
|
+
class Unsupported < RuntimeError; end
|
45
|
+
# @private
|
46
|
+
class ConnectionError < RuntimeError; end
|
47
|
+
# @private
|
48
|
+
class ConnectionNotBound < RuntimeError; end
|
49
|
+
|
50
|
+
# Older versions of Ruby may not provide the SSLErrorWaitReadable
|
51
|
+
# OpenSSL class. Create an error class to act as a "proxy".
|
52
|
+
if defined?(OpenSSL::SSL::SSLErrorWaitReadable)
|
53
|
+
SSLConnectionWaitReadable = OpenSSL::SSL::SSLErrorWaitReadable
|
54
|
+
else
|
55
|
+
SSLConnectionWaitReadable = IO::WaitReadable
|
56
|
+
end
|
57
|
+
|
58
|
+
# Older versions of Ruby may not provide the SSLErrorWaitWritable
|
59
|
+
# OpenSSL class. Create an error class to act as a "proxy".
|
60
|
+
if defined?(OpenSSL::SSL::SSLErrorWaitWritable)
|
61
|
+
SSLConnectionWaitWritable = OpenSSL::SSL::SSLErrorWaitWritable
|
62
|
+
else
|
63
|
+
SSLConnectionWaitWritable = IO::WaitWritable
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
module EventMachine
|
68
|
+
class CertificateCreator
|
69
|
+
attr_reader :cert, :key
|
70
|
+
|
71
|
+
def initialize
|
72
|
+
@key = OpenSSL::PKey::RSA.new(1024)
|
73
|
+
public_key = @key.public_key
|
74
|
+
subject = "/C=EventMachine/O=EventMachine/OU=EventMachine/CN=EventMachine"
|
75
|
+
@cert = OpenSSL::X509::Certificate.new
|
76
|
+
@cert.subject = @cert.issuer = OpenSSL::X509::Name.parse(subject)
|
77
|
+
@cert.not_before = Time.now
|
78
|
+
@cert.not_after = Time.now + 365 * 24 * 60 * 60
|
79
|
+
@cert.public_key = public_key
|
80
|
+
@cert.serial = 0x0
|
81
|
+
@cert.version = 2
|
82
|
+
factory = OpenSSL::X509::ExtensionFactory.new
|
83
|
+
factory.subject_certificate = @cert
|
84
|
+
factory.issuer_certificate = @cert
|
85
|
+
@cert.extensions = [
|
86
|
+
factory.create_extension("basicConstraints","CA:TRUE", true),
|
87
|
+
factory.create_extension("subjectKeyIdentifier", "hash")
|
88
|
+
]
|
89
|
+
@cert.add_extension factory.create_extension("authorityKeyIdentifier", "keyid:always,issuer:always")
|
90
|
+
@cert.sign(@key, OpenSSL::Digest::SHA1.new)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# @private
|
95
|
+
DefaultCertificate = CertificateCreator.new
|
96
|
+
|
97
|
+
# @private
|
98
|
+
DefaultDHKey1024 = OpenSSL::PKey::DH.new <<-_end_of_pem_
|
99
|
+
-----BEGIN DH PARAMETERS-----
|
100
|
+
MIGHAoGBAJ0lOVy0VIr/JebWn0zDwY2h+rqITFOpdNr6ugsgvkDXuucdcChhYExJ
|
101
|
+
AV/ZD2AWPbrTqV76mGRgJg4EddgT1zG0jq3rnFdMj2XzkBYx3BVvfR0Arnby0RHR
|
102
|
+
T4h7KZ/2zmjvV+eF8kBUHBJAojUlzxKj4QeO2x20FP9X5xmNUXeDAgEC
|
103
|
+
-----END DH PARAMETERS-----
|
104
|
+
_end_of_pem_
|
105
|
+
|
106
|
+
# @private
|
107
|
+
DefaultDHKey2048 = OpenSSL::PKey::DH.new <<-_end_of_pem_
|
108
|
+
-----BEGIN DH PARAMETERS-----
|
109
|
+
MIIBCAKCAQEA7E6kBrYiyvmKAMzQ7i8WvwVk9Y/+f8S7sCTN712KkK3cqd1jhJDY
|
110
|
+
JbrYeNV3kUIKhPxWHhObHKpD1R84UpL+s2b55+iMd6GmL7OYmNIT/FccKhTcveab
|
111
|
+
VBmZT86BZKYyf45hUF9FOuUM9xPzuK3Vd8oJQvfYMCd7LPC0taAEljQLR4Edf8E6
|
112
|
+
YoaOffgTf5qxiwkjnlVZQc3whgnEt9FpVMvQ9eknyeGB5KHfayAc3+hUAvI3/Cr3
|
113
|
+
1bNveX5wInh5GDx1FGhKBZ+s1H+aedudCm7sCgRwv8lKWYGiHzObSma8A86KG+MD
|
114
|
+
7Lo5JquQ3DlBodj3IDyPrxIv96lvRPFtAwIBAg==
|
115
|
+
-----END DH PARAMETERS-----
|
116
|
+
_end_of_pem_
|
117
|
+
end
|
36
118
|
|
37
119
|
# @private
|
38
120
|
module EventMachine
|
@@ -92,14 +174,10 @@ module EventMachine
|
|
92
174
|
selectable.send_data data
|
93
175
|
end
|
94
176
|
|
95
|
-
|
96
|
-
# The extension version does NOT raise any kind of an error if an attempt is made
|
97
|
-
# to close a non-existent connection. Not sure whether we should. For now, we'll
|
98
|
-
# raise an error here in that case.
|
99
177
|
# @private
|
100
178
|
def close_connection target, after_writing
|
101
|
-
selectable = Reactor.instance.get_selectable( target )
|
102
|
-
selectable.schedule_close after_writing
|
179
|
+
selectable = Reactor.instance.get_selectable( target )
|
180
|
+
selectable.schedule_close after_writing if selectable
|
103
181
|
end
|
104
182
|
|
105
183
|
# @private
|
@@ -163,10 +241,127 @@ module EventMachine
|
|
163
241
|
def epoll
|
164
242
|
end
|
165
243
|
|
166
|
-
# This method is not implemented for pure-Ruby implementation
|
167
244
|
# @private
|
168
245
|
def ssl?
|
169
|
-
|
246
|
+
true
|
247
|
+
end
|
248
|
+
|
249
|
+
def tls_parm_set?(parm)
|
250
|
+
!(parm.nil? || parm.empty?)
|
251
|
+
end
|
252
|
+
|
253
|
+
# This method takes a series of positional arguments for specifying such
|
254
|
+
# things as private keys and certificate chains. It's expected that the
|
255
|
+
# parameter list will grow as we add more supported features. ALL of these
|
256
|
+
# parameters are optional, and can be specified as empty or nil strings.
|
257
|
+
# @private
|
258
|
+
def set_tls_parms signature, priv_key, cert_chain, verify_peer, fail_if_no_peer_cert, sni_hostname, cipher_list, ecdh_curve, dhparam, protocols_bitmask
|
259
|
+
bitmask = protocols_bitmask
|
260
|
+
ssl_options = OpenSSL::SSL::OP_ALL
|
261
|
+
ssl_options |= OpenSSL::SSL::OP_NO_SSLv2 if defined?(OpenSSL::SSL::OP_NO_SSLv2) && EM_PROTO_SSLv2 & bitmask == 0
|
262
|
+
ssl_options |= OpenSSL::SSL::OP_NO_SSLv3 if defined?(OpenSSL::SSL::OP_NO_SSLv3) && EM_PROTO_SSLv3 & bitmask == 0
|
263
|
+
ssl_options |= OpenSSL::SSL::OP_NO_TLSv1 if defined?(OpenSSL::SSL::OP_NO_TLSv1) && EM_PROTO_TLSv1 & bitmask == 0
|
264
|
+
ssl_options |= OpenSSL::SSL::OP_NO_TLSv1_1 if defined?(OpenSSL::SSL::OP_NO_TLSv1_1) && EM_PROTO_TLSv1_1 & bitmask == 0
|
265
|
+
ssl_options |= OpenSSL::SSL::OP_NO_TLSv1_2 if defined?(OpenSSL::SSL::OP_NO_TLSv1_2) && EM_PROTO_TLSv1_2 & bitmask == 0
|
266
|
+
@tls_parms ||= {}
|
267
|
+
@tls_parms[signature] = {
|
268
|
+
:verify_peer => verify_peer,
|
269
|
+
:fail_if_no_peer_cert => fail_if_no_peer_cert,
|
270
|
+
:ssl_options => ssl_options
|
271
|
+
}
|
272
|
+
@tls_parms[signature][:priv_key] = File.read(priv_key) if tls_parm_set?(priv_key)
|
273
|
+
@tls_parms[signature][:cert_chain] = File.read(cert_chain) if tls_parm_set?(cert_chain)
|
274
|
+
@tls_parms[signature][:sni_hostname] = sni_hostname if tls_parm_set?(sni_hostname)
|
275
|
+
@tls_parms[signature][:cipher_list] = cipher_list.gsub(/,\s*/, ':') if tls_parm_set?(cipher_list)
|
276
|
+
@tls_parms[signature][:dhparam] = File.read(dhparam) if tls_parm_set?(dhparam)
|
277
|
+
@tls_parms[signature][:ecdh_curve] = ecdh_curve if tls_parm_set?(ecdh_curve)
|
278
|
+
end
|
279
|
+
|
280
|
+
def start_tls signature
|
281
|
+
selectable = Reactor.instance.get_selectable(signature) or raise "unknown io selectable for start_tls"
|
282
|
+
tls_parms = @tls_parms[signature]
|
283
|
+
ctx = OpenSSL::SSL::SSLContext.new
|
284
|
+
ctx.options = tls_parms[:ssl_options]
|
285
|
+
ctx.cert = DefaultCertificate.cert
|
286
|
+
ctx.key = DefaultCertificate.key
|
287
|
+
ctx.cert_store = OpenSSL::X509::Store.new
|
288
|
+
ctx.cert_store.set_default_paths
|
289
|
+
ctx.cert = OpenSSL::X509::Certificate.new(tls_parms[:cert_chain]) if tls_parms[:cert_chain]
|
290
|
+
ctx.key = OpenSSL::PKey::RSA.new(tls_parms[:priv_key]) if tls_parms[:priv_key]
|
291
|
+
verify_mode = OpenSSL::SSL::VERIFY_NONE
|
292
|
+
if tls_parms[:verify_peer]
|
293
|
+
verify_mode |= OpenSSL::SSL::VERIFY_PEER
|
294
|
+
end
|
295
|
+
if tls_parms[:fail_if_no_peer_cert]
|
296
|
+
verify_mode |= OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
|
297
|
+
end
|
298
|
+
ctx.verify_mode = verify_mode
|
299
|
+
ctx.servername_cb = Proc.new do |_, server_name|
|
300
|
+
tls_parms[:server_name] = server_name
|
301
|
+
nil
|
302
|
+
end
|
303
|
+
ctx.ciphers = tls_parms[:cipher_list] if tls_parms[:cipher_list]
|
304
|
+
if selectable.is_server
|
305
|
+
ctx.tmp_dh_callback = Proc.new do |_, _, key_length|
|
306
|
+
if tls_parms[:dhparam]
|
307
|
+
OpenSSL::PKey::DH.new(tls_parms[:dhparam])
|
308
|
+
else
|
309
|
+
case key_length
|
310
|
+
when 1024 then DefaultDHKey1024
|
311
|
+
when 2048 then DefaultDHKey2048
|
312
|
+
else
|
313
|
+
nil
|
314
|
+
end
|
315
|
+
end
|
316
|
+
end
|
317
|
+
if tls_parms[:ecdh_curve] && ctx.respond_to?(:tmp_ecdh_callback)
|
318
|
+
ctx.tmp_ecdh_callback = Proc.new do
|
319
|
+
OpenSSL::PKey::EC.new(tls_parms[:ecdh_curve])
|
320
|
+
end
|
321
|
+
end
|
322
|
+
end
|
323
|
+
ssl_io = OpenSSL::SSL::SSLSocket.new(selectable, ctx)
|
324
|
+
ssl_io.sync_close = true
|
325
|
+
if tls_parms[:sni_hostname]
|
326
|
+
ssl_io.hostname = tls_parms[:sni_hostname] if ssl_io.respond_to?(:hostname=)
|
327
|
+
end
|
328
|
+
begin
|
329
|
+
selectable.is_server ? ssl_io.accept_nonblock : ssl_io.connect_nonblock
|
330
|
+
rescue; end
|
331
|
+
selectable.io = ssl_io
|
332
|
+
end
|
333
|
+
|
334
|
+
def get_peer_cert signature
|
335
|
+
selectable = Reactor.instance.get_selectable(signature) or raise "unknown get_peer_cert target"
|
336
|
+
if selectable.io.respond_to?(:peer_cert) && selectable.io.peer_cert
|
337
|
+
selectable.io.peer_cert.to_pem
|
338
|
+
else
|
339
|
+
nil
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
def get_cipher_name signature
|
344
|
+
selectable = Reactor.instance.get_selectable(signature) or raise "unknown get_cipher_name target"
|
345
|
+
selectable.io.respond_to?(:cipher) ? selectable.io.cipher[0] : nil
|
346
|
+
end
|
347
|
+
|
348
|
+
def get_cipher_protocol signature
|
349
|
+
selectable = Reactor.instance.get_selectable(signature) or raise "unknown get_cipher_protocol target"
|
350
|
+
selectable.io.respond_to?(:cipher) ? selectable.io.cipher[1] : nil
|
351
|
+
end
|
352
|
+
|
353
|
+
def get_cipher_bits signature
|
354
|
+
selectable = Reactor.instance.get_selectable(signature) or raise "unknown get_cipher_bits target"
|
355
|
+
selectable.io.respond_to?(:cipher) ? selectable.io.cipher[2] : nil
|
356
|
+
end
|
357
|
+
|
358
|
+
def get_sni_hostname signature
|
359
|
+
@tls_parms ||= {}
|
360
|
+
if @tls_parms[signature]
|
361
|
+
@tls_parms[signature][:server_name]
|
362
|
+
else
|
363
|
+
nil
|
364
|
+
end
|
170
365
|
end
|
171
366
|
|
172
367
|
# This method is a no-op in the pure-Ruby implementation. We simply return Ruby's built-in
|
@@ -184,13 +379,13 @@ module EventMachine
|
|
184
379
|
|
185
380
|
# @private
|
186
381
|
def get_sock_opt signature, level, optname
|
187
|
-
selectable = Reactor.instance.get_selectable( signature ) or raise "unknown
|
382
|
+
selectable = Reactor.instance.get_selectable( signature ) or raise "unknown get_sock_opt target"
|
188
383
|
selectable.getsockopt level, optname
|
189
384
|
end
|
190
385
|
|
191
386
|
# @private
|
192
387
|
def set_sock_opt signature, level, optname, optval
|
193
|
-
selectable = Reactor.instance.get_selectable( signature ) or raise "unknown
|
388
|
+
selectable = Reactor.instance.get_selectable( signature ) or raise "unknown set_sock_opt target"
|
194
389
|
selectable.setsockopt level, optname, optval
|
195
390
|
end
|
196
391
|
|
@@ -223,13 +418,13 @@ module EventMachine
|
|
223
418
|
r = Reactor.instance.get_selectable( sig ) or raise "unknown set_comm_inactivity_timeout target"
|
224
419
|
r.set_inactivity_timeout tm
|
225
420
|
end
|
226
|
-
end
|
227
|
-
end
|
228
|
-
|
229
421
|
|
230
|
-
|
231
|
-
|
232
|
-
|
422
|
+
# @private
|
423
|
+
def set_pending_connect_timeout sig, tm
|
424
|
+
# Needs to be implemented. Currently a no-op stub to allow
|
425
|
+
# certain software to operate with the EM pure-ruby.
|
426
|
+
end
|
427
|
+
end
|
233
428
|
end
|
234
429
|
|
235
430
|
module EventMachine
|
@@ -269,6 +464,24 @@ module EventMachine
|
|
269
464
|
ConnectionCompleted = 104
|
270
465
|
# @private
|
271
466
|
LoopbreakSignalled = 105
|
467
|
+
# @private
|
468
|
+
ConnectionNotifyReadable = 106
|
469
|
+
# @private
|
470
|
+
ConnectionNotifyWritable = 107
|
471
|
+
# @private
|
472
|
+
SslHandshakeCompleted = 108
|
473
|
+
# @private
|
474
|
+
SslVerify = 109
|
475
|
+
# @private
|
476
|
+
EM_PROTO_SSLv2 = 2
|
477
|
+
# @private
|
478
|
+
EM_PROTO_SSLv3 = 4
|
479
|
+
# @private
|
480
|
+
EM_PROTO_TLSv1 = 8
|
481
|
+
# @private
|
482
|
+
EM_PROTO_TLSv1_1 = 16
|
483
|
+
# @private
|
484
|
+
EM_PROTO_TLSv1_2 = 32
|
272
485
|
end
|
273
486
|
|
274
487
|
module EventMachine
|
@@ -376,6 +589,9 @@ module EventMachine
|
|
376
589
|
@selectables.delete_if {|k,io|
|
377
590
|
if io.close_scheduled?
|
378
591
|
io.close
|
592
|
+
begin
|
593
|
+
EventMachine::event_callback io.uuid, ConnectionUnbound, nil
|
594
|
+
rescue ConnectionNotBound; end
|
379
595
|
true
|
380
596
|
end
|
381
597
|
}
|
@@ -414,8 +630,9 @@ module EventMachine
|
|
414
630
|
end
|
415
631
|
|
416
632
|
def signal_loopbreak
|
417
|
-
|
418
|
-
|
633
|
+
begin
|
634
|
+
@loopbreak_writer.send('+',0,"127.0.0.1",@loopbreak_port) if @loopbreak_writer
|
635
|
+
rescue IOError; end
|
419
636
|
end
|
420
637
|
|
421
638
|
def set_timer_quantum interval_in_seconds
|
@@ -435,6 +652,8 @@ class IO
|
|
435
652
|
def_delegator :@my_selectable, :eventable_read
|
436
653
|
def_delegator :@my_selectable, :eventable_write
|
437
654
|
def_delegator :@my_selectable, :uuid
|
655
|
+
def_delegator :@my_selectable, :is_server
|
656
|
+
def_delegator :@my_selectable, :is_server=
|
438
657
|
def_delegator :@my_selectable, :send_data
|
439
658
|
def_delegator :@my_selectable, :schedule_close
|
440
659
|
def_delegator :@my_selectable, :get_peername
|
@@ -442,17 +661,21 @@ class IO
|
|
442
661
|
def_delegator :@my_selectable, :get_outbound_data_size
|
443
662
|
def_delegator :@my_selectable, :set_inactivity_timeout
|
444
663
|
def_delegator :@my_selectable, :heartbeat
|
664
|
+
def_delegator :@my_selectable, :io
|
665
|
+
def_delegator :@my_selectable, :io=
|
445
666
|
end
|
446
667
|
|
447
668
|
module EventMachine
|
448
669
|
# @private
|
449
670
|
class Selectable
|
450
671
|
|
451
|
-
|
672
|
+
attr_accessor :io, :is_server
|
673
|
+
attr_reader :uuid
|
452
674
|
|
453
675
|
def initialize io
|
454
|
-
@uuid = UuidGenerator.generate
|
455
676
|
@io = io
|
677
|
+
@uuid = UuidGenerator.generate
|
678
|
+
@is_server = false
|
456
679
|
@last_activity = Reactor.instance.current_loop_time
|
457
680
|
|
458
681
|
if defined?(Fcntl::F_GETFL)
|
@@ -499,6 +722,14 @@ module EventMachine
|
|
499
722
|
|
500
723
|
def heartbeat
|
501
724
|
end
|
725
|
+
|
726
|
+
def schedule_close(after_writing=false)
|
727
|
+
if after_writing
|
728
|
+
@close_requested = true
|
729
|
+
else
|
730
|
+
@close_scheduled = true
|
731
|
+
end
|
732
|
+
end
|
502
733
|
end
|
503
734
|
|
504
735
|
end
|
@@ -552,9 +783,9 @@ module EventMachine
|
|
552
783
|
data = io.sysread(4096)
|
553
784
|
EventMachine::event_callback uuid, ConnectionData, data
|
554
785
|
end
|
555
|
-
rescue Errno::EAGAIN, Errno::EWOULDBLOCK
|
786
|
+
rescue Errno::EAGAIN, Errno::EWOULDBLOCK, SSLConnectionWaitReadable
|
556
787
|
# no-op
|
557
|
-
rescue Errno::ECONNRESET, Errno::ECONNREFUSED, EOFError
|
788
|
+
rescue Errno::ECONNRESET, Errno::ECONNREFUSED, EOFError, Errno::EPIPE, OpenSSL::SSL::SSLError
|
558
789
|
@close_scheduled = true
|
559
790
|
EventMachine::event_callback uuid, ConnectionUnbound, nil
|
560
791
|
end
|
@@ -589,9 +820,10 @@ module EventMachine
|
|
589
820
|
@outbound_q.unshift data[w..-1]
|
590
821
|
break
|
591
822
|
end
|
592
|
-
rescue Errno::EAGAIN
|
823
|
+
rescue Errno::EAGAIN, SSLConnectionWaitReadable, SSLConnectionWaitWritable
|
593
824
|
@outbound_q.unshift data
|
594
|
-
|
825
|
+
break
|
826
|
+
rescue EOFError, Errno::ECONNRESET, Errno::ECONNREFUSED, Errno::EPIPE, OpenSSL::SSL::SSLError
|
595
827
|
@close_scheduled = true
|
596
828
|
@outbound_q.clear
|
597
829
|
end
|
@@ -607,16 +839,6 @@ module EventMachine
|
|
607
839
|
end
|
608
840
|
end
|
609
841
|
|
610
|
-
# #schedule_close
|
611
|
-
# The application wants to close the connection.
|
612
|
-
def schedule_close after_writing
|
613
|
-
if after_writing
|
614
|
-
@close_requested = true
|
615
|
-
else
|
616
|
-
@close_scheduled = true
|
617
|
-
end
|
618
|
-
end
|
619
|
-
|
620
842
|
# #get_peername
|
621
843
|
# This is defined in the normal way on connected stream objects.
|
622
844
|
# Return an object that is suitable for passing to Socket#unpack_sockaddr_in or variants.
|
@@ -657,39 +879,58 @@ module EventMachine
|
|
657
879
|
# TODO, this assumes a current Ruby snapshot.
|
658
880
|
# We need to degrade to a nonblocking connect otherwise.
|
659
881
|
sd.connect_nonblock( Socket.pack_sockaddr_in( port, host ))
|
660
|
-
rescue Errno::EINPROGRESS
|
882
|
+
rescue Errno::ECONNREFUSED, Errno::EINPROGRESS
|
661
883
|
end
|
662
884
|
EvmaTCPClient.new sd
|
663
885
|
end
|
664
886
|
|
665
|
-
|
666
887
|
def initialize io
|
667
888
|
super
|
668
889
|
@pending = true
|
890
|
+
@handshake_complete = false
|
669
891
|
end
|
670
892
|
|
671
|
-
|
672
|
-
|
673
|
-
|
893
|
+
def ready?
|
894
|
+
if RUBY_PLATFORM =~ /linux/
|
895
|
+
io.getsockopt(Socket::SOL_TCP, Socket::TCP_INFO).unpack("i").first == 1 # TCP_ESTABLISHED
|
896
|
+
else
|
897
|
+
io.getsockopt(Socket::SOL_SOCKET, Socket::SO_ERROR).unpack("i").first == 0 # NO ERROR
|
898
|
+
end
|
674
899
|
end
|
675
900
|
|
676
|
-
def
|
677
|
-
|
901
|
+
def handshake_complete?
|
902
|
+
if !@handshake_complete && io.respond_to?(:state)
|
903
|
+
if io.state =~ /^SSLOK/
|
904
|
+
@handshake_complete = true
|
905
|
+
EventMachine::event_callback uuid, SslHandshakeCompleted, ""
|
906
|
+
EventMachine::event_callback uuid, SslVerify, io.peer_cert.to_pem if io.peer_cert
|
907
|
+
end
|
908
|
+
else
|
909
|
+
@handshake_complete = true
|
910
|
+
end
|
911
|
+
@handshake_complete
|
678
912
|
end
|
679
913
|
|
680
|
-
def
|
914
|
+
def pending?
|
915
|
+
handshake_complete?
|
681
916
|
if @pending
|
682
|
-
|
683
|
-
|
917
|
+
if ready?
|
918
|
+
@pending = false
|
684
919
|
EventMachine::event_callback uuid, ConnectionCompleted, ""
|
685
920
|
end
|
686
|
-
else
|
687
|
-
super
|
688
921
|
end
|
922
|
+
@pending
|
689
923
|
end
|
690
924
|
|
925
|
+
def select_for_writing?
|
926
|
+
pending?
|
927
|
+
super
|
928
|
+
end
|
691
929
|
|
692
|
-
|
930
|
+
def select_for_reading?
|
931
|
+
pending?
|
932
|
+
super
|
933
|
+
end
|
693
934
|
end
|
694
935
|
end
|
695
936
|
|
@@ -809,7 +1050,8 @@ module EventMachine
|
|
809
1050
|
begin
|
810
1051
|
10.times {
|
811
1052
|
descriptor,peername = io.accept_nonblock
|
812
|
-
sd =
|
1053
|
+
sd = EvmaTCPClient.new descriptor
|
1054
|
+
sd.is_server = true
|
813
1055
|
EventMachine::event_callback uuid, ConnectionAccepted, sd.uuid
|
814
1056
|
}
|
815
1057
|
rescue Errno::EWOULDBLOCK, Errno::EAGAIN
|
@@ -1005,18 +1247,14 @@ module EventMachine
|
|
1005
1247
|
@close_scheduled = true
|
1006
1248
|
EventMachine::event_callback uuid, ConnectionUnbound, nil
|
1007
1249
|
end
|
1008
|
-
|
1009
1250
|
end
|
1010
1251
|
|
1011
|
-
|
1012
1252
|
def send_data data
|
1013
1253
|
send_datagram data, @return_address
|
1014
1254
|
end
|
1015
|
-
|
1016
1255
|
end
|
1017
1256
|
end
|
1018
1257
|
|
1019
1258
|
# load base EM api on top, now that we have the underlying pure ruby
|
1020
1259
|
# implementation defined
|
1021
1260
|
require 'eventmachine'
|
1022
|
-
|