wj_eventmachine 1.3.0.dev.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (180) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +179 -0
  3. data/GNU +281 -0
  4. data/LICENSE +60 -0
  5. data/README.md +110 -0
  6. data/docs/DocumentationGuidesIndex.md +27 -0
  7. data/docs/GettingStarted.md +520 -0
  8. data/docs/old/ChangeLog +211 -0
  9. data/docs/old/DEFERRABLES +246 -0
  10. data/docs/old/EPOLL +141 -0
  11. data/docs/old/INSTALL +13 -0
  12. data/docs/old/KEYBOARD +42 -0
  13. data/docs/old/LEGAL +25 -0
  14. data/docs/old/LIGHTWEIGHT_CONCURRENCY +130 -0
  15. data/docs/old/PURE_RUBY +75 -0
  16. data/docs/old/RELEASE_NOTES +94 -0
  17. data/docs/old/SMTP +4 -0
  18. data/docs/old/SPAWNED_PROCESSES +148 -0
  19. data/docs/old/TODO +8 -0
  20. data/examples/guides/getting_started/01_eventmachine_echo_server.rb +18 -0
  21. data/examples/guides/getting_started/02_eventmachine_echo_server_that_recognizes_exit_command.rb +22 -0
  22. data/examples/guides/getting_started/03_simple_chat_server.rb +149 -0
  23. data/examples/guides/getting_started/04_simple_chat_server_step_one.rb +27 -0
  24. data/examples/guides/getting_started/05_simple_chat_server_step_two.rb +43 -0
  25. data/examples/guides/getting_started/06_simple_chat_server_step_three.rb +98 -0
  26. data/examples/guides/getting_started/07_simple_chat_server_step_four.rb +121 -0
  27. data/examples/guides/getting_started/08_simple_chat_server_step_five.rb +141 -0
  28. data/examples/old/ex_channel.rb +43 -0
  29. data/examples/old/ex_queue.rb +2 -0
  30. data/examples/old/ex_tick_loop_array.rb +15 -0
  31. data/examples/old/ex_tick_loop_counter.rb +32 -0
  32. data/examples/old/helper.rb +2 -0
  33. data/ext/binder.cpp +124 -0
  34. data/ext/binder.h +52 -0
  35. data/ext/cmain.cpp +1046 -0
  36. data/ext/ed.cpp +2238 -0
  37. data/ext/ed.h +460 -0
  38. data/ext/em.cpp +2378 -0
  39. data/ext/em.h +266 -0
  40. data/ext/eventmachine.h +152 -0
  41. data/ext/extconf.rb +285 -0
  42. data/ext/fastfilereader/extconf.rb +120 -0
  43. data/ext/fastfilereader/mapper.cpp +214 -0
  44. data/ext/fastfilereader/mapper.h +59 -0
  45. data/ext/fastfilereader/rubymain.cpp +126 -0
  46. data/ext/kb.cpp +79 -0
  47. data/ext/page.cpp +107 -0
  48. data/ext/page.h +51 -0
  49. data/ext/pipe.cpp +354 -0
  50. data/ext/project.h +174 -0
  51. data/ext/rubymain.cpp +1610 -0
  52. data/ext/ssl.cpp +627 -0
  53. data/ext/ssl.h +103 -0
  54. data/ext/wait_for_single_fd.h +36 -0
  55. data/java/.classpath +8 -0
  56. data/java/.project +17 -0
  57. data/java/src/com/rubyeventmachine/EmReactor.java +625 -0
  58. data/java/src/com/rubyeventmachine/EmReactorException.java +40 -0
  59. data/java/src/com/rubyeventmachine/EmReactorInterface.java +70 -0
  60. data/java/src/com/rubyeventmachine/EventableChannel.java +72 -0
  61. data/java/src/com/rubyeventmachine/EventableDatagramChannel.java +201 -0
  62. data/java/src/com/rubyeventmachine/EventableSocketChannel.java +415 -0
  63. data/java/src/com/rubyeventmachine/NullEmReactor.java +157 -0
  64. data/java/src/com/rubyeventmachine/NullEventableChannel.java +81 -0
  65. data/lib/em/buftok.rb +59 -0
  66. data/lib/em/callback.rb +58 -0
  67. data/lib/em/channel.rb +69 -0
  68. data/lib/em/completion.rb +307 -0
  69. data/lib/em/connection.rb +776 -0
  70. data/lib/em/deferrable.rb +210 -0
  71. data/lib/em/deferrable/pool.rb +2 -0
  72. data/lib/em/file_watch.rb +73 -0
  73. data/lib/em/future.rb +61 -0
  74. data/lib/em/io_streamer.rb +68 -0
  75. data/lib/em/iterator.rb +252 -0
  76. data/lib/em/messages.rb +66 -0
  77. data/lib/em/pool.rb +151 -0
  78. data/lib/em/process_watch.rb +45 -0
  79. data/lib/em/processes.rb +123 -0
  80. data/lib/em/protocols.rb +37 -0
  81. data/lib/em/protocols/header_and_content.rb +138 -0
  82. data/lib/em/protocols/httpclient.rb +303 -0
  83. data/lib/em/protocols/httpclient2.rb +602 -0
  84. data/lib/em/protocols/line_and_text.rb +125 -0
  85. data/lib/em/protocols/line_protocol.rb +33 -0
  86. data/lib/em/protocols/linetext2.rb +179 -0
  87. data/lib/em/protocols/memcache.rb +331 -0
  88. data/lib/em/protocols/object_protocol.rb +46 -0
  89. data/lib/em/protocols/postgres3.rb +246 -0
  90. data/lib/em/protocols/saslauth.rb +175 -0
  91. data/lib/em/protocols/smtpclient.rb +394 -0
  92. data/lib/em/protocols/smtpserver.rb +666 -0
  93. data/lib/em/protocols/socks4.rb +66 -0
  94. data/lib/em/protocols/stomp.rb +205 -0
  95. data/lib/em/protocols/tcptest.rb +54 -0
  96. data/lib/em/pure_ruby.rb +1299 -0
  97. data/lib/em/queue.rb +80 -0
  98. data/lib/em/resolver.rb +232 -0
  99. data/lib/em/spawnable.rb +84 -0
  100. data/lib/em/streamer.rb +118 -0
  101. data/lib/em/threaded_resource.rb +90 -0
  102. data/lib/em/tick_loop.rb +85 -0
  103. data/lib/em/timers.rb +61 -0
  104. data/lib/em/version.rb +3 -0
  105. data/lib/eventmachine.rb +1602 -0
  106. data/lib/jeventmachine.rb +318 -0
  107. data/rakelib/package.rake +120 -0
  108. data/rakelib/test.rake +6 -0
  109. data/rakelib/test_pure.rake +11 -0
  110. data/tests/client.crt +31 -0
  111. data/tests/client.key +51 -0
  112. data/tests/dhparam.pem +13 -0
  113. data/tests/em_ssl_handlers.rb +153 -0
  114. data/tests/em_test_helper.rb +198 -0
  115. data/tests/jruby/test_jeventmachine.rb +38 -0
  116. data/tests/test_attach.rb +199 -0
  117. data/tests/test_basic.rb +321 -0
  118. data/tests/test_channel.rb +75 -0
  119. data/tests/test_completion.rb +178 -0
  120. data/tests/test_connection_count.rb +83 -0
  121. data/tests/test_connection_write.rb +35 -0
  122. data/tests/test_defer.rb +35 -0
  123. data/tests/test_deferrable.rb +35 -0
  124. data/tests/test_epoll.rb +141 -0
  125. data/tests/test_error_handler.rb +38 -0
  126. data/tests/test_exc.rb +37 -0
  127. data/tests/test_file_watch.rb +86 -0
  128. data/tests/test_fork.rb +75 -0
  129. data/tests/test_futures.rb +170 -0
  130. data/tests/test_handler_check.rb +35 -0
  131. data/tests/test_hc.rb +155 -0
  132. data/tests/test_httpclient.rb +238 -0
  133. data/tests/test_httpclient2.rb +132 -0
  134. data/tests/test_idle_connection.rb +31 -0
  135. data/tests/test_inactivity_timeout.rb +102 -0
  136. data/tests/test_io_streamer.rb +47 -0
  137. data/tests/test_ipv4.rb +96 -0
  138. data/tests/test_ipv6.rb +107 -0
  139. data/tests/test_iterator.rb +122 -0
  140. data/tests/test_kb.rb +28 -0
  141. data/tests/test_keepalive.rb +113 -0
  142. data/tests/test_line_protocol.rb +33 -0
  143. data/tests/test_ltp.rb +155 -0
  144. data/tests/test_ltp2.rb +332 -0
  145. data/tests/test_many_fds.rb +21 -0
  146. data/tests/test_next_tick.rb +104 -0
  147. data/tests/test_object_protocol.rb +36 -0
  148. data/tests/test_pause.rb +109 -0
  149. data/tests/test_pending_connect_timeout.rb +52 -0
  150. data/tests/test_pool.rb +196 -0
  151. data/tests/test_process_watch.rb +50 -0
  152. data/tests/test_processes.rb +128 -0
  153. data/tests/test_proxy_connection.rb +180 -0
  154. data/tests/test_pure.rb +156 -0
  155. data/tests/test_queue.rb +64 -0
  156. data/tests/test_resolver.rb +129 -0
  157. data/tests/test_running.rb +14 -0
  158. data/tests/test_sasl.rb +46 -0
  159. data/tests/test_send_file.rb +217 -0
  160. data/tests/test_servers.rb +32 -0
  161. data/tests/test_shutdown_hooks.rb +23 -0
  162. data/tests/test_smtpclient.rb +75 -0
  163. data/tests/test_smtpserver.rb +90 -0
  164. data/tests/test_sock_opt.rb +53 -0
  165. data/tests/test_spawn.rb +290 -0
  166. data/tests/test_ssl_args.rb +41 -0
  167. data/tests/test_ssl_dhparam.rb +57 -0
  168. data/tests/test_ssl_ecdh_curve.rb +57 -0
  169. data/tests/test_ssl_extensions.rb +24 -0
  170. data/tests/test_ssl_methods.rb +31 -0
  171. data/tests/test_ssl_protocols.rb +190 -0
  172. data/tests/test_ssl_verify.rb +52 -0
  173. data/tests/test_stomp.rb +38 -0
  174. data/tests/test_system.rb +46 -0
  175. data/tests/test_threaded_resource.rb +68 -0
  176. data/tests/test_tick_loop.rb +58 -0
  177. data/tests/test_timers.rb +150 -0
  178. data/tests/test_ud.rb +8 -0
  179. data/tests/test_unbind_reason.rb +40 -0
  180. metadata +384 -0
@@ -0,0 +1,66 @@
1
+ module EventMachine
2
+ module Protocols
3
+ # Basic SOCKS v4 client implementation
4
+ #
5
+ # Use as you would any regular connection:
6
+ #
7
+ # class MyConn < EM::P::Socks4
8
+ # def post_init
9
+ # send_data("sup")
10
+ # end
11
+ #
12
+ # def receive_data(data)
13
+ # send_data("you said: #{data}")
14
+ # end
15
+ # end
16
+ #
17
+ # EM.connect socks_host, socks_port, MyConn, host, port
18
+ #
19
+ class Socks4 < Connection
20
+ def initialize(host, port)
21
+ @host = Socket.gethostbyname(host).last
22
+ @port = port
23
+ @socks_error_code = nil
24
+ @buffer = ''
25
+ setup_methods
26
+ end
27
+
28
+ def setup_methods
29
+ class << self
30
+ def post_init; socks_post_init; end
31
+ def receive_data(*a); socks_receive_data(*a); end
32
+ end
33
+ end
34
+
35
+ def restore_methods
36
+ class << self
37
+ remove_method :post_init
38
+ remove_method :receive_data
39
+ end
40
+ end
41
+
42
+ def socks_post_init
43
+ header = [4, 1, @port, @host, 0].flatten.pack("CCnA4C")
44
+ send_data(header)
45
+ end
46
+
47
+ def socks_receive_data(data)
48
+ @buffer << data
49
+ return if @buffer.size < 8
50
+
51
+ header_resp = @buffer.slice! 0, 8
52
+ _, r = header_resp.unpack("cc")
53
+ if r != 90
54
+ @socks_error_code = r
55
+ close_connection
56
+ return
57
+ end
58
+
59
+ restore_methods
60
+
61
+ post_init
62
+ receive_data(@buffer) unless @buffer.empty?
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,205 @@
1
+ #--
2
+ #
3
+ # Author:: Francis Cianfrocca (gmail: blackhedd)
4
+ # Homepage:: http://rubyeventmachine.com
5
+ # Date:: 15 November 2006
6
+ #
7
+ # See EventMachine and EventMachine::Connection for documentation and
8
+ # usage examples.
9
+ #
10
+ #----------------------------------------------------------------------------
11
+ #
12
+ # Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
13
+ # Gmail: blackhedd
14
+ #
15
+ # This program is free software; you can redistribute it and/or modify
16
+ # it under the terms of either: 1) the GNU General Public License
17
+ # as published by the Free Software Foundation; either version 2 of the
18
+ # License, or (at your option) any later version; or 2) Ruby's License.
19
+ #
20
+ # See the file COPYING for complete licensing information.
21
+ #
22
+ #---------------------------------------------------------------------------
23
+ #
24
+ #
25
+ #
26
+
27
+ module EventMachine
28
+ module Protocols
29
+
30
+ # Implements Stomp (http://docs.codehaus.org/display/STOMP/Protocol).
31
+ #
32
+ # == Usage example
33
+ #
34
+ # module StompClient
35
+ # include EM::Protocols::Stomp
36
+ #
37
+ # def connection_completed
38
+ # connect :login => 'guest', :passcode => 'guest'
39
+ # end
40
+ #
41
+ # def receive_msg msg
42
+ # if msg.command == "CONNECTED"
43
+ # subscribe '/some/topic'
44
+ # else
45
+ # p ['got a message', msg]
46
+ # puts msg.body
47
+ # end
48
+ # end
49
+ # end
50
+ #
51
+ # EM.run{
52
+ # EM.connect 'localhost', 61613, StompClient
53
+ # }
54
+ #
55
+ module Stomp
56
+ include LineText2
57
+
58
+ class Message
59
+ # The command associated with the message, usually 'CONNECTED' or 'MESSAGE'
60
+ attr_accessor :command
61
+ # Hash containing headers such as destination and message-id
62
+ attr_accessor :header
63
+ alias :headers :header
64
+ # Body of the message
65
+ attr_accessor :body
66
+
67
+ # @private
68
+ def initialize
69
+ @header = {}
70
+ @state = :precommand
71
+ @content_length = nil
72
+ end
73
+ # @private
74
+ def consume_line line
75
+ if @state == :precommand
76
+ unless line =~ /\A\s*\Z/
77
+ @command = line
78
+ @state = :headers
79
+ end
80
+ elsif @state == :headers
81
+ if line == ""
82
+ if @content_length
83
+ yield( [:sized_text, @content_length+1] )
84
+ else
85
+ @state = :body
86
+ yield( [:unsized_text] )
87
+ end
88
+ elsif line =~ /\A([^:]+):(.+)\Z/
89
+ k = $1.dup.strip
90
+ v = $2.dup.strip
91
+ @header[k] = v
92
+ if k == "content-length"
93
+ @content_length = v.to_i
94
+ end
95
+ else
96
+ # This is a protocol error. How to signal it?
97
+ end
98
+ elsif @state == :body
99
+ @body = line
100
+ yield( [:dispatch] )
101
+ end
102
+ end
103
+ end
104
+
105
+ # @private
106
+ def send_frame verb, headers={}, body=""
107
+ body = body.to_s
108
+ ary = [verb, "\n"]
109
+ body_bytesize = body.bytesize if body.respond_to? :bytesize
110
+ body_bytesize ||= body.size
111
+ headers.each {|k,v| ary << "#{k}:#{v}\n" }
112
+ ary << "content-length: #{body_bytesize}\n"
113
+ ary << "content-type: text/plain; charset=UTF-8\n" unless headers.has_key? 'content-type'
114
+ ary << "\n"
115
+ ary << body
116
+ ary << "\0"
117
+ send_data ary.join
118
+ end
119
+
120
+ # @private
121
+ def receive_line line
122
+ @stomp_initialized || init_message_reader
123
+ @stomp_message.consume_line(line) {|outcome|
124
+ if outcome.first == :sized_text
125
+ set_text_mode outcome[1]
126
+ elsif outcome.first == :unsized_text
127
+ set_delimiter "\0"
128
+ elsif outcome.first == :dispatch
129
+ receive_msg(@stomp_message) if respond_to?(:receive_msg)
130
+ init_message_reader
131
+ end
132
+ }
133
+ end
134
+
135
+ # @private
136
+ def receive_binary_data data
137
+ @stomp_message.body = data[0..-2]
138
+ receive_msg(@stomp_message) if respond_to?(:receive_msg)
139
+ init_message_reader
140
+ end
141
+
142
+ # @private
143
+ def init_message_reader
144
+ @stomp_initialized = true
145
+ set_delimiter "\n"
146
+ set_line_mode
147
+ @stomp_message = Message.new
148
+ end
149
+
150
+ # Invoked with an incoming Stomp::Message received from the STOMP server
151
+ def receive_msg msg
152
+ # stub, overwrite this in your handler
153
+ end
154
+
155
+ # CONNECT command, for authentication
156
+ #
157
+ # connect :login => 'guest', :passcode => 'guest'
158
+ #
159
+ def connect parms={}
160
+ send_frame "CONNECT", parms
161
+ end
162
+
163
+ # SEND command, for publishing messages to a topic
164
+ #
165
+ # send '/topic/name', 'some message here'
166
+ #
167
+ def send destination, body, parms={}
168
+ send_frame "SEND", parms.merge( :destination=>destination ), body.to_s
169
+ end
170
+
171
+ # SUBSCRIBE command, for subscribing to topics
172
+ #
173
+ # subscribe '/topic/name', false
174
+ #
175
+ def subscribe dest, ack=false
176
+ send_frame "SUBSCRIBE", {:destination=>dest, :ack=>(ack ? "client" : "auto")}
177
+ end
178
+
179
+ # ACK command, for acknowledging receipt of messages
180
+ #
181
+ # module StompClient
182
+ # include EM::P::Stomp
183
+ #
184
+ # def connection_completed
185
+ # connect :login => 'guest', :passcode => 'guest'
186
+ # # subscribe with ack mode
187
+ # subscribe '/some/topic', true
188
+ # end
189
+ #
190
+ # def receive_msg msg
191
+ # if msg.command == "MESSAGE"
192
+ # ack msg.headers['message-id']
193
+ # puts msg.body
194
+ # end
195
+ # end
196
+ # end
197
+ #
198
+ def ack msgid
199
+ send_frame "ACK", 'message-id'=> msgid
200
+ end
201
+
202
+ end
203
+ end
204
+ end
205
+
@@ -0,0 +1,54 @@
1
+ #--
2
+ #
3
+ # Author:: Francis Cianfrocca (gmail: blackhedd)
4
+ # Homepage:: http://rubyeventmachine.com
5
+ # Date:: 16 July 2006
6
+ #
7
+ # See EventMachine and EventMachine::Connection for documentation and
8
+ # usage examples.
9
+ #
10
+ #----------------------------------------------------------------------------
11
+ #
12
+ # Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
13
+ # Gmail: blackhedd
14
+ #
15
+ # This program is free software; you can redistribute it and/or modify
16
+ # it under the terms of either: 1) the GNU General Public License
17
+ # as published by the Free Software Foundation; either version 2 of the
18
+ # License, or (at your option) any later version; or 2) Ruby's License.
19
+ #
20
+ # See the file COPYING for complete licensing information.
21
+ #
22
+ #---------------------------------------------------------------------------
23
+ #
24
+ #
25
+ #
26
+
27
+ module EventMachine
28
+ module Protocols
29
+
30
+ # @private
31
+ class TcpConnectTester < Connection
32
+ include EventMachine::Deferrable
33
+
34
+ def self.test( host, port )
35
+ EventMachine.connect( host, port, self )
36
+ end
37
+
38
+ def post_init
39
+ @start_time = Time.now
40
+ end
41
+
42
+ def connection_completed
43
+ @completed = true
44
+ set_deferred_status :succeeded, (Time.now - @start_time)
45
+ close_connection
46
+ end
47
+
48
+ def unbind
49
+ set_deferred_status :failed, (Time.now - @start_time) unless @completed
50
+ end
51
+ end
52
+
53
+ end
54
+ end
@@ -0,0 +1,1299 @@
1
+ #--
2
+ #
3
+ # Author:: Francis Cianfrocca (gmail: blackhedd)
4
+ # Homepage:: http://rubyeventmachine.com
5
+ # Date:: 8 Apr 2006
6
+ #
7
+ # See EventMachine and EventMachine::Connection for documentation and
8
+ # usage examples.
9
+ #
10
+ #----------------------------------------------------------------------------
11
+ #
12
+ # Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
13
+ # Gmail: blackhedd
14
+ #
15
+ # This program is free software; you can redistribute it and/or modify
16
+ # it under the terms of either: 1) the GNU General Public License
17
+ # as published by the Free Software Foundation; either version 2 of the
18
+ # License, or (at your option) any later version; or 2) Ruby's License.
19
+ #
20
+ # See the file COPYING for complete licensing information.
21
+ #
22
+ #-------------------------------------------------------------------
23
+ #
24
+ #
25
+
26
+ # TODO List:
27
+ # TCP-connects currently assume non-blocking connect is available- need to
28
+ # degrade automatically on versions of Ruby prior to June 2006.
29
+ #
30
+
31
+ require 'singleton'
32
+ require 'forwardable'
33
+ require 'socket'
34
+ require 'fcntl'
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
118
+
119
+ # @private
120
+ module EventMachine
121
+ class << self
122
+ # This is mostly useful for automated tests.
123
+ # Return a distinctive symbol so the caller knows whether he's dealing
124
+ # with an extension or with a pure-Ruby library.
125
+ # @private
126
+ def library_type
127
+ :pure_ruby
128
+ end
129
+
130
+ # @private
131
+ def initialize_event_machine
132
+ Reactor.instance.initialize_for_run
133
+ end
134
+
135
+ # Changed 04Oct06: intervals from the caller are now in milliseconds, but our native-ruby
136
+ # processor still wants them in seconds.
137
+ # @private
138
+ def add_oneshot_timer interval
139
+ Reactor.instance.install_oneshot_timer(interval.to_f / 1000)
140
+ end
141
+
142
+ # @private
143
+ def run_machine
144
+ Reactor.instance.run
145
+ end
146
+
147
+ # @private
148
+ def release_machine
149
+ end
150
+
151
+
152
+ def stopping?
153
+ return Reactor.instance.stop_scheduled
154
+ end
155
+
156
+ # @private
157
+ def stop
158
+ Reactor.instance.stop
159
+ end
160
+
161
+ # @private
162
+ def connect_server host, port
163
+ bind_connect_server nil, nil, host, port
164
+ end
165
+
166
+ # @private
167
+ def bind_connect_server bind_addr, bind_port, host, port
168
+ EvmaTCPClient.connect(bind_addr, bind_port, host, port).uuid
169
+ end
170
+
171
+ # @private
172
+ def send_data target, data, datalength
173
+ selectable = Reactor.instance.get_selectable( target ) or raise "unknown send_data target"
174
+ selectable.send_data data
175
+ end
176
+
177
+ # @private
178
+ def close_connection target, after_writing
179
+ selectable = Reactor.instance.get_selectable( target )
180
+ selectable.schedule_close after_writing if selectable
181
+ end
182
+
183
+ # @private
184
+ def start_tcp_server host, port
185
+ (s = EvmaTCPServer.start_server host, port) or raise "no acceptor"
186
+ s.uuid
187
+ end
188
+
189
+ # @private
190
+ def stop_tcp_server sig
191
+ #exit(1)
192
+ s = Reactor.instance.get_selectable(sig)
193
+ #puts "is there an instance :#{!s.blank?}"
194
+ unless s.nil?
195
+ s.schedule_close
196
+ end
197
+ end
198
+
199
+ # @private
200
+ def start_unix_server chain
201
+ (s = EvmaUNIXServer.start_server chain) or raise "no acceptor"
202
+ s.uuid
203
+ end
204
+
205
+ # @private
206
+ def connect_unix_server chain
207
+ EvmaUNIXClient.connect(chain).uuid
208
+ end
209
+
210
+ # @private
211
+ def signal_loopbreak
212
+ Reactor.instance.signal_loopbreak
213
+ end
214
+
215
+ # @private
216
+ def get_peername sig
217
+ selectable = Reactor.instance.get_selectable( sig ) or raise "unknown get_peername target"
218
+ selectable.get_peername
219
+ end
220
+
221
+ # @private
222
+ def get_sockname sig
223
+ selectable = Reactor.instance.get_selectable( sig ) or raise "unknown get_sockname target"
224
+ selectable.get_sockname
225
+ end
226
+
227
+ # @private
228
+ def open_udp_socket host, port
229
+ EvmaUDPSocket.create(host, port).uuid
230
+ end
231
+
232
+ # This is currently only for UDP!
233
+ # We need to make it work with unix-domain sockets as well.
234
+ # @private
235
+ def send_datagram target, data, datalength, host, port
236
+ selectable = Reactor.instance.get_selectable( target ) or raise "unknown send_data target"
237
+ selectable.send_datagram data, Socket::pack_sockaddr_in(port, host)
238
+ end
239
+
240
+
241
+ # Sets reactor quantum in milliseconds. The underlying Reactor function wants a (possibly
242
+ # fractional) number of seconds.
243
+ # @private
244
+ def set_timer_quantum interval
245
+ Reactor.instance.set_timer_quantum(( 1.0 * interval) / 1000.0)
246
+ end
247
+
248
+ # This method is a harmless no-op in the pure-Ruby implementation. This is intended to ensure
249
+ # that user code behaves properly across different EM implementations.
250
+ # @private
251
+ def epoll
252
+ end
253
+
254
+ # @private
255
+ def ssl?
256
+ true
257
+ end
258
+
259
+ def tls_parm_set?(parm)
260
+ !(parm.nil? || parm.empty?)
261
+ end
262
+
263
+ # This method takes a series of positional arguments for specifying such
264
+ # things as private keys and certificate chains. It's expected that the
265
+ # parameter list will grow as we add more supported features. ALL of these
266
+ # parameters are optional, and can be specified as empty or nil strings.
267
+ # @private
268
+ 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
269
+ bitmask = protocols_bitmask
270
+ ssl_options = OpenSSL::SSL::OP_ALL
271
+ ssl_options |= OpenSSL::SSL::OP_NO_SSLv2 if defined?(OpenSSL::SSL::OP_NO_SSLv2) && EM_PROTO_SSLv2 & bitmask == 0
272
+ ssl_options |= OpenSSL::SSL::OP_NO_SSLv3 if defined?(OpenSSL::SSL::OP_NO_SSLv3) && EM_PROTO_SSLv3 & bitmask == 0
273
+ ssl_options |= OpenSSL::SSL::OP_NO_TLSv1 if defined?(OpenSSL::SSL::OP_NO_TLSv1) && EM_PROTO_TLSv1 & bitmask == 0
274
+ ssl_options |= OpenSSL::SSL::OP_NO_TLSv1_1 if defined?(OpenSSL::SSL::OP_NO_TLSv1_1) && EM_PROTO_TLSv1_1 & bitmask == 0
275
+ ssl_options |= OpenSSL::SSL::OP_NO_TLSv1_2 if defined?(OpenSSL::SSL::OP_NO_TLSv1_2) && EM_PROTO_TLSv1_2 & bitmask == 0
276
+ @tls_parms ||= {}
277
+ @tls_parms[signature] = {
278
+ :verify_peer => verify_peer,
279
+ :fail_if_no_peer_cert => fail_if_no_peer_cert,
280
+ :ssl_options => ssl_options
281
+ }
282
+ @tls_parms[signature][:priv_key] = File.read(priv_key) if tls_parm_set?(priv_key)
283
+ @tls_parms[signature][:cert_chain] = File.read(cert_chain) if tls_parm_set?(cert_chain)
284
+ @tls_parms[signature][:sni_hostname] = sni_hostname if tls_parm_set?(sni_hostname)
285
+ @tls_parms[signature][:cipher_list] = cipher_list.gsub(/,\s*/, ':') if tls_parm_set?(cipher_list)
286
+ @tls_parms[signature][:dhparam] = File.read(dhparam) if tls_parm_set?(dhparam)
287
+ @tls_parms[signature][:ecdh_curve] = ecdh_curve if tls_parm_set?(ecdh_curve)
288
+ end
289
+
290
+ def start_tls signature
291
+ selectable = Reactor.instance.get_selectable(signature) or raise "unknown io selectable for start_tls"
292
+ tls_parms = @tls_parms[signature]
293
+ ctx = OpenSSL::SSL::SSLContext.new
294
+ ctx.options = tls_parms[:ssl_options]
295
+ ctx.cert = DefaultCertificate.cert
296
+ ctx.key = DefaultCertificate.key
297
+ ctx.cert_store = OpenSSL::X509::Store.new
298
+ ctx.cert_store.set_default_paths
299
+ ctx.cert = OpenSSL::X509::Certificate.new(tls_parms[:cert_chain]) if tls_parms[:cert_chain]
300
+ ctx.key = OpenSSL::PKey::RSA.new(tls_parms[:priv_key]) if tls_parms[:priv_key]
301
+ verify_mode = OpenSSL::SSL::VERIFY_NONE
302
+ if tls_parms[:verify_peer]
303
+ verify_mode |= OpenSSL::SSL::VERIFY_PEER
304
+ end
305
+ if tls_parms[:fail_if_no_peer_cert]
306
+ verify_mode |= OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
307
+ end
308
+ ctx.verify_mode = verify_mode
309
+ ctx.servername_cb = Proc.new do |_, server_name|
310
+ tls_parms[:server_name] = server_name
311
+ nil
312
+ end
313
+ ctx.ciphers = tls_parms[:cipher_list] if tls_parms[:cipher_list]
314
+ if selectable.is_server
315
+ ctx.tmp_dh_callback = Proc.new do |_, _, key_length|
316
+ if tls_parms[:dhparam]
317
+ OpenSSL::PKey::DH.new(tls_parms[:dhparam])
318
+ else
319
+ case key_length
320
+ when 1024 then DefaultDHKey1024
321
+ when 2048 then DefaultDHKey2048
322
+ else
323
+ nil
324
+ end
325
+ end
326
+ end
327
+ if tls_parms[:ecdh_curve] && ctx.respond_to?(:tmp_ecdh_callback)
328
+ ctx.tmp_ecdh_callback = Proc.new do
329
+ OpenSSL::PKey::EC.new(tls_parms[:ecdh_curve])
330
+ end
331
+ end
332
+ end
333
+ ssl_io = OpenSSL::SSL::SSLSocket.new(selectable, ctx)
334
+ ssl_io.sync_close = true
335
+ if tls_parms[:sni_hostname]
336
+ ssl_io.hostname = tls_parms[:sni_hostname] if ssl_io.respond_to?(:hostname=)
337
+ end
338
+ begin
339
+ selectable.is_server ? ssl_io.accept_nonblock : ssl_io.connect_nonblock
340
+ rescue; end
341
+ selectable.io = ssl_io
342
+ end
343
+
344
+ def get_peer_cert signature
345
+ selectable = Reactor.instance.get_selectable(signature) or raise "unknown get_peer_cert target"
346
+ if selectable.io.respond_to?(:peer_cert) && selectable.io.peer_cert
347
+ selectable.io.peer_cert.to_pem
348
+ else
349
+ nil
350
+ end
351
+ end
352
+
353
+ def get_cipher_name signature
354
+ selectable = Reactor.instance.get_selectable(signature) or raise "unknown get_cipher_name target"
355
+ selectable.io.respond_to?(:cipher) ? selectable.io.cipher[0] : nil
356
+ end
357
+
358
+ def get_cipher_protocol signature
359
+ selectable = Reactor.instance.get_selectable(signature) or raise "unknown get_cipher_protocol target"
360
+ selectable.io.respond_to?(:cipher) ? selectable.io.cipher[1] : nil
361
+ end
362
+
363
+ def get_cipher_bits signature
364
+ selectable = Reactor.instance.get_selectable(signature) or raise "unknown get_cipher_bits target"
365
+ selectable.io.respond_to?(:cipher) ? selectable.io.cipher[2] : nil
366
+ end
367
+
368
+ def get_sni_hostname signature
369
+ @tls_parms ||= {}
370
+ if @tls_parms[signature]
371
+ @tls_parms[signature][:server_name]
372
+ else
373
+ nil
374
+ end
375
+ end
376
+
377
+ # This method is a no-op in the pure-Ruby implementation. We simply return Ruby's built-in
378
+ # per-process file-descriptor limit.
379
+ # @private
380
+ def set_rlimit_nofile n
381
+ 1024
382
+ end
383
+
384
+ # This method is a harmless no-op in pure Ruby, which doesn't have a built-in limit
385
+ # on the number of available timers.
386
+ # @private
387
+ def set_max_timer_count n
388
+ end
389
+
390
+ # @private
391
+ def get_sock_opt signature, level, optname
392
+ selectable = Reactor.instance.get_selectable( signature ) or raise "unknown get_sock_opt target"
393
+ selectable.getsockopt level, optname
394
+ end
395
+
396
+ # @private
397
+ def set_sock_opt signature, level, optname, optval
398
+ selectable = Reactor.instance.get_selectable( signature ) or raise "unknown set_sock_opt target"
399
+ selectable.setsockopt level, optname, optval
400
+ end
401
+
402
+ # @private
403
+ def send_file_data sig, filename
404
+ sz = File.size(filename)
405
+ raise "file too large" if sz > 32*1024
406
+ data =
407
+ begin
408
+ File.read filename
409
+ rescue
410
+ ""
411
+ end
412
+ send_data sig, data, data.length
413
+ end
414
+
415
+ # @private
416
+ def get_outbound_data_size sig
417
+ r = Reactor.instance.get_selectable( sig ) or raise "unknown get_outbound_data_size target"
418
+ r.get_outbound_data_size
419
+ end
420
+
421
+ # @private
422
+ def read_keyboard
423
+ EvmaKeyboard.open.uuid
424
+ end
425
+
426
+ # @private
427
+ def set_comm_inactivity_timeout sig, tm
428
+ r = Reactor.instance.get_selectable( sig ) or raise "unknown set_comm_inactivity_timeout target"
429
+ r.set_inactivity_timeout tm
430
+ end
431
+
432
+ # @private
433
+ def set_pending_connect_timeout sig, tm
434
+ # Needs to be implemented. Currently a no-op stub to allow
435
+ # certain software to operate with the EM pure-ruby.
436
+ end
437
+
438
+ # @private
439
+ def report_connection_error_status signature
440
+ get_sock_opt(signature, Socket::SOL_SOCKET, Socket::SO_ERROR).int
441
+ end
442
+ end
443
+ end
444
+
445
+ module EventMachine
446
+ # @private
447
+ class Connection
448
+ # @private
449
+ def get_outbound_data_size
450
+ EventMachine::get_outbound_data_size @signature
451
+ end
452
+ end
453
+ end
454
+
455
+ module EventMachine
456
+
457
+ # Factored out so we can substitute other implementations
458
+ # here if desired, such as the one in ActiveRBAC.
459
+ # @private
460
+ module UuidGenerator
461
+ def self.generate
462
+ @ix ||= 0
463
+ @ix += 1
464
+ end
465
+ end
466
+ end
467
+
468
+
469
+ module EventMachine
470
+ # @private
471
+ TimerFired = 100
472
+ # @private
473
+ ConnectionData = 101
474
+ # @private
475
+ ConnectionUnbound = 102
476
+ # @private
477
+ ConnectionAccepted = 103
478
+ # @private
479
+ ConnectionCompleted = 104
480
+ # @private
481
+ LoopbreakSignalled = 105
482
+ # @private
483
+ ConnectionNotifyReadable = 106
484
+ # @private
485
+ ConnectionNotifyWritable = 107
486
+ # @private
487
+ SslHandshakeCompleted = 108
488
+ # @private
489
+ SslVerify = 109
490
+ # @private
491
+ EM_PROTO_SSLv2 = 2
492
+ # @private
493
+ EM_PROTO_SSLv3 = 4
494
+ # @private
495
+ EM_PROTO_TLSv1 = 8
496
+ # @private
497
+ EM_PROTO_TLSv1_1 = 16
498
+ # @private
499
+ EM_PROTO_TLSv1_2 = 32
500
+ end
501
+
502
+ module EventMachine
503
+ # @private
504
+ class Reactor
505
+ include Singleton
506
+
507
+ HeartbeatInterval = 2
508
+
509
+ attr_reader :current_loop_time, :stop_scheduled
510
+
511
+ def initialize
512
+ initialize_for_run
513
+ end
514
+
515
+ def get_timer_count
516
+ @timers.size
517
+ end
518
+
519
+ def install_oneshot_timer interval
520
+ uuid = UuidGenerator::generate
521
+ #@timers << [Time.now + interval, uuid]
522
+ #@timers.sort! {|a,b| a.first <=> b.first}
523
+ @timers.add([Time.now + interval, uuid])
524
+ uuid
525
+ end
526
+
527
+ # Called before run, this is a good place to clear out arrays
528
+ # with cruft that may be left over from a previous run.
529
+ # @private
530
+ def initialize_for_run
531
+ @running = false
532
+ @stop_scheduled = false
533
+ @selectables ||= {}; @selectables.clear
534
+ @timers = SortedSet.new # []
535
+ set_timer_quantum(0.1)
536
+ @current_loop_time = Time.now
537
+ @next_heartbeat = @current_loop_time + HeartbeatInterval
538
+ end
539
+
540
+ def add_selectable io
541
+ @selectables[io.uuid] = io
542
+ end
543
+
544
+ def get_selectable uuid
545
+ #raise Error.new("selectable does not exist") if @selectables[uuid].nil?
546
+ #puts "selectables are #{@selectables}"
547
+ @selectables[uuid]
548
+ end
549
+
550
+ def run
551
+ raise Error.new( "already running" ) if @running
552
+ puts "-------- RUNNING ------------ "
553
+ @running = true
554
+
555
+ begin
556
+ open_loopbreaker
557
+
558
+ loop {
559
+ @current_loop_time = Time.now
560
+
561
+ break if @stop_scheduled
562
+ run_timers
563
+ break if @stop_scheduled
564
+ crank_selectables
565
+ break if @stop_scheduled
566
+ run_heartbeats
567
+ }
568
+ ensure
569
+ close_loopbreaker
570
+ @selectables.each {|k, io| io.close}
571
+ @selectables.clear
572
+
573
+ @running = false
574
+ end
575
+
576
+ end
577
+
578
+ def run_timers
579
+ timers_to_delete = []
580
+ @timers.each {|t|
581
+ if t.first <= @current_loop_time
582
+ #@timers.delete t
583
+ timers_to_delete << t
584
+ EventMachine::event_callback "", TimerFired, t.last
585
+ else
586
+ break
587
+ end
588
+ }
589
+ timers_to_delete.map{|c| @timers.delete c}
590
+ timers_to_delete = nil
591
+ #while @timers.length > 0 and @timers.first.first <= now
592
+ # t = @timers.shift
593
+ # EventMachine::event_callback "", TimerFired, t.last
594
+ #end
595
+ end
596
+
597
+ def run_heartbeats
598
+ if @next_heartbeat <= @current_loop_time
599
+ @next_heartbeat = @current_loop_time + HeartbeatInterval
600
+ @selectables.each {|k,io| io.heartbeat}
601
+ end
602
+ end
603
+
604
+ def crank_selectables
605
+ #$stderr.write 'R'
606
+
607
+ readers = @selectables.values.select {|io| io.select_for_reading?}
608
+ writers = @selectables.values.select {|io| io.select_for_writing?}
609
+
610
+ s = select( readers, writers, nil, @timer_quantum)
611
+
612
+ s and s[1] and s[1].each {|w| w.eventable_write }
613
+ s and s[0] and s[0].each {|r| r.eventable_read }
614
+
615
+ @selectables.delete_if {|k,io|
616
+ if io.close_scheduled?
617
+ io.close
618
+ begin
619
+ EventMachine::event_callback io.uuid, ConnectionUnbound, nil
620
+ rescue ConnectionNotBound; end
621
+ true
622
+ end
623
+ }
624
+ end
625
+
626
+ # #stop
627
+ def stop
628
+ raise Error.new( "not running") unless @running
629
+ @stop_scheduled = true
630
+ end
631
+
632
+ def open_loopbreaker
633
+ # Can't use an IO.pipe because they can't be set nonselectable in Windows.
634
+ # Pick a random localhost UDP port.
635
+ #@loopbreak_writer.close if @loopbreak_writer
636
+ #rd,@loopbreak_writer = IO.pipe
637
+ @loopbreak_reader = UDPSocket.new
638
+ @loopbreak_writer = UDPSocket.new
639
+ bound = false
640
+ 100.times {
641
+ @loopbreak_port = rand(10000) + 40000
642
+ begin
643
+ @loopbreak_reader.bind "127.0.0.1", @loopbreak_port
644
+ bound = true
645
+ break
646
+ rescue
647
+ end
648
+ }
649
+ raise "Unable to bind Loopbreaker" unless bound
650
+ LoopbreakReader.new(@loopbreak_reader)
651
+ end
652
+
653
+ def close_loopbreaker
654
+ @loopbreak_writer.close
655
+ @loopbreak_writer = nil
656
+ end
657
+
658
+ def signal_loopbreak
659
+ begin
660
+ @loopbreak_writer.send('+',0,"127.0.0.1",@loopbreak_port) if @loopbreak_writer
661
+ rescue IOError; end
662
+ end
663
+
664
+ def set_timer_quantum interval_in_seconds
665
+ @timer_quantum = interval_in_seconds
666
+ end
667
+
668
+ end
669
+
670
+ end
671
+
672
+ # @private
673
+ class IO
674
+ extend Forwardable
675
+ def_delegator :@my_selectable, :close_scheduled?
676
+ def_delegator :@my_selectable, :select_for_reading?
677
+ def_delegator :@my_selectable, :select_for_writing?
678
+ def_delegator :@my_selectable, :eventable_read
679
+ def_delegator :@my_selectable, :eventable_write
680
+ def_delegator :@my_selectable, :uuid
681
+ def_delegator :@my_selectable, :is_server
682
+ def_delegator :@my_selectable, :is_server=
683
+ def_delegator :@my_selectable, :send_data
684
+ def_delegator :@my_selectable, :schedule_close
685
+ def_delegator :@my_selectable, :get_peername
686
+ def_delegator :@my_selectable, :get_sockname
687
+ def_delegator :@my_selectable, :send_datagram
688
+ def_delegator :@my_selectable, :get_outbound_data_size
689
+ def_delegator :@my_selectable, :set_inactivity_timeout
690
+ def_delegator :@my_selectable, :heartbeat
691
+ def_delegator :@my_selectable, :io
692
+ def_delegator :@my_selectable, :io=
693
+ end
694
+
695
+ module EventMachine
696
+ # @private
697
+ class Selectable
698
+
699
+ attr_accessor :io, :is_server
700
+ attr_reader :uuid
701
+
702
+ def initialize io
703
+ @io = io
704
+ @uuid = UuidGenerator.generate
705
+ @is_server = false
706
+ @last_activity = Reactor.instance.current_loop_time
707
+
708
+ if defined?(Fcntl::F_GETFL)
709
+ m = @io.fcntl(Fcntl::F_GETFL, 0)
710
+ @io.fcntl(Fcntl::F_SETFL, Fcntl::O_NONBLOCK | m)
711
+ else
712
+ # Windows doesn't define F_GETFL.
713
+ # It's not very reliable about setting descriptors nonblocking either.
714
+ begin
715
+ s = Socket.for_fd(@io.fileno)
716
+ s.fcntl( Fcntl::F_SETFL, Fcntl::O_NONBLOCK )
717
+ rescue Errno::EINVAL, Errno::EBADF
718
+ warn "Serious error: unable to set descriptor non-blocking"
719
+ end
720
+ end
721
+ # TODO, should set CLOEXEC on Unix?
722
+
723
+ @close_scheduled = false
724
+ @close_requested = false
725
+
726
+ se = self; @io.instance_eval { @my_selectable = se }
727
+ Reactor.instance.add_selectable @io
728
+ end
729
+
730
+ def close_scheduled?
731
+ @close_scheduled
732
+ end
733
+
734
+ def select_for_reading?
735
+ false
736
+ end
737
+
738
+ def select_for_writing?
739
+ false
740
+ end
741
+
742
+ def get_peername
743
+ nil
744
+ end
745
+
746
+ def get_sockname
747
+ nil
748
+ end
749
+
750
+ def set_inactivity_timeout tm
751
+ @inactivity_timeout = tm
752
+ end
753
+
754
+ def heartbeat
755
+ end
756
+
757
+ def schedule_close(after_writing=false)
758
+ if after_writing
759
+ @close_requested = true
760
+ else
761
+ @close_scheduled = true
762
+ end
763
+ end
764
+ end
765
+
766
+ end
767
+
768
+ module EventMachine
769
+ # @private
770
+ class StreamObject < Selectable
771
+ def initialize io
772
+ super io
773
+ @outbound_q = []
774
+ end
775
+
776
+ # If we have to close, or a close-after-writing has been requested,
777
+ # then don't read any more data.
778
+ def select_for_reading?
779
+ true unless (@close_scheduled || @close_requested)
780
+ end
781
+
782
+ # If we have to close, don't select for writing.
783
+ # Otherwise, see if the protocol is ready to close.
784
+ # If not, see if he has data to send.
785
+ # If a close-after-writing has been requested and the outbound queue
786
+ # is empty, convert the status to close_scheduled.
787
+ def select_for_writing?
788
+ unless @close_scheduled
789
+ if @outbound_q.empty?
790
+ @close_scheduled = true if @close_requested
791
+ false
792
+ else
793
+ true
794
+ end
795
+ end
796
+ end
797
+
798
+ # Proper nonblocking I/O was added to Ruby 1.8.4 in May 2006.
799
+ # If we have it, then we can read multiple times safely to improve
800
+ # performance.
801
+ # The last-activity clock ASSUMES that we only come here when we
802
+ # have selected readable.
803
+ # TODO, coalesce multiple reads into a single event.
804
+ # TODO, do the function check somewhere else and cache it.
805
+ def eventable_read
806
+ @last_activity = Reactor.instance.current_loop_time
807
+ begin
808
+ if io.respond_to?(:read_nonblock)
809
+ 10.times {
810
+ data = io.read_nonblock(4096)
811
+ EventMachine::event_callback uuid, ConnectionData, data
812
+ }
813
+ else
814
+ data = io.sysread(4096)
815
+ EventMachine::event_callback uuid, ConnectionData, data
816
+ end
817
+ rescue Errno::EAGAIN, Errno::EWOULDBLOCK, SSLConnectionWaitReadable
818
+ # no-op
819
+ rescue Errno::ECONNRESET, Errno::ECONNREFUSED, EOFError, Errno::EPIPE, OpenSSL::SSL::SSLError
820
+ @close_scheduled = true
821
+ EventMachine::event_callback uuid, ConnectionUnbound, nil
822
+ end
823
+
824
+ end
825
+
826
+ # Provisional implementation. Will be re-implemented in subclasses.
827
+ # TODO: Complete this implementation. As it stands, this only writes
828
+ # a single packet per cycle. Highly inefficient, but required unless
829
+ # we're running on a Ruby with proper nonblocking I/O (Ruby 1.8.4
830
+ # built from sources from May 25, 2006 or newer).
831
+ # We need to improve the loop so it writes multiple times, however
832
+ # not more than a certain number of bytes per cycle, otherwise
833
+ # one busy connection could hog output buffers and slow down other
834
+ # connections. Also we should coalesce small writes.
835
+ # URGENT TODO: Coalesce small writes. They are a performance killer.
836
+ # The last-activity recorder ASSUMES we'll only come here if we've
837
+ # selected writable.
838
+ def eventable_write
839
+ # coalesce the outbound array here, perhaps
840
+ @last_activity = Reactor.instance.current_loop_time
841
+ while data = @outbound_q.shift do
842
+ begin
843
+ data = data.to_s
844
+ w = if io.respond_to?(:write_nonblock)
845
+ io.write_nonblock data
846
+ else
847
+ io.syswrite data
848
+ end
849
+
850
+ if w < data.length
851
+ @outbound_q.unshift data[w..-1]
852
+ break
853
+ end
854
+ rescue Errno::EAGAIN, SSLConnectionWaitReadable, SSLConnectionWaitWritable
855
+ @outbound_q.unshift data
856
+ break
857
+ rescue EOFError, Errno::ECONNRESET, Errno::ECONNREFUSED, Errno::EPIPE, OpenSSL::SSL::SSLError
858
+ @close_scheduled = true
859
+ @outbound_q.clear
860
+ end
861
+ end
862
+
863
+ end
864
+
865
+ # #send_data
866
+ def send_data data
867
+ # TODO, coalesce here perhaps by being smarter about appending to @outbound_q.last?
868
+ unless @close_scheduled or @close_requested or !data or data.length <= 0
869
+ @outbound_q << data.to_s
870
+ end
871
+ end
872
+
873
+ # #get_peername
874
+ # This is defined in the normal way on connected stream objects.
875
+ # Return an object that is suitable for passing to Socket#unpack_sockaddr_in or variants.
876
+ # We could also use a convenience method that did the unpacking automatically.
877
+ def get_peername
878
+ io.getpeername
879
+ end
880
+
881
+ # #get_sockname
882
+ # This is defined in the normal way on connected stream objects.
883
+ # Return an object that is suitable for passing to Socket#unpack_sockaddr_in or variants.
884
+ # We could also use a convenience method that did the unpacking automatically.
885
+ def get_sockname
886
+ io.getsockname
887
+ end
888
+
889
+ # #get_outbound_data_size
890
+ def get_outbound_data_size
891
+ @outbound_q.inject(0) {|memo,obj| memo += (obj || "").length}
892
+ end
893
+
894
+ def heartbeat
895
+ if @inactivity_timeout and @inactivity_timeout > 0 and (@last_activity + @inactivity_timeout) < Reactor.instance.current_loop_time
896
+ schedule_close true
897
+ end
898
+ end
899
+ end
900
+
901
+
902
+ end
903
+
904
+
905
+ #--------------------------------------------------------------
906
+
907
+
908
+
909
+ module EventMachine
910
+ # @private
911
+ class EvmaTCPClient < StreamObject
912
+
913
+ def self.connect bind_addr, bind_port, host, port
914
+ sd = Socket.new( Socket::AF_INET, Socket::SOCK_STREAM, 0 )
915
+ sd.bind( Socket.pack_sockaddr_in( bind_port, bind_addr )) if bind_addr
916
+
917
+ begin
918
+ # TODO, this assumes a current Ruby snapshot.
919
+ # We need to degrade to a nonblocking connect otherwise.
920
+ sd.connect_nonblock( Socket.pack_sockaddr_in( port, host ))
921
+ rescue Errno::ECONNREFUSED, Errno::EINPROGRESS
922
+ end
923
+ EvmaTCPClient.new sd
924
+ end
925
+
926
+ def initialize io
927
+ super
928
+ @pending = true
929
+ @handshake_complete = false
930
+ end
931
+
932
+ def ready?
933
+ if RUBY_PLATFORM =~ /linux/
934
+ io.getsockopt(Socket::SOL_TCP, Socket::TCP_INFO).unpack("i").first == 1 # TCP_ESTABLISHED
935
+ else
936
+ io.getsockopt(Socket::SOL_SOCKET, Socket::SO_ERROR).unpack("i").first == 0 # NO ERROR
937
+ end
938
+ end
939
+
940
+ def handshake_complete?
941
+ if !@handshake_complete && io.respond_to?(:state)
942
+ if io.state =~ /^SSLOK/
943
+ @handshake_complete = true
944
+ EventMachine::event_callback uuid, SslHandshakeCompleted, ""
945
+ EventMachine::event_callback uuid, SslVerify, io.peer_cert.to_pem if io.peer_cert
946
+ end
947
+ else
948
+ @handshake_complete = true
949
+ end
950
+ @handshake_complete
951
+ end
952
+
953
+ def pending?
954
+ handshake_complete?
955
+ if @pending
956
+ if ready?
957
+ @pending = false
958
+ EventMachine::event_callback uuid, ConnectionCompleted, ""
959
+ end
960
+ end
961
+ @pending
962
+ end
963
+
964
+ def select_for_writing?
965
+ pending?
966
+ super
967
+ end
968
+
969
+ def select_for_reading?
970
+ pending?
971
+ super
972
+ end
973
+ end
974
+ end
975
+
976
+
977
+
978
+ module EventMachine
979
+ # @private
980
+ class EvmaKeyboard < StreamObject
981
+
982
+ def self.open
983
+ EvmaKeyboard.new STDIN
984
+ end
985
+
986
+
987
+ def initialize io
988
+ super
989
+ end
990
+
991
+
992
+ def select_for_writing?
993
+ false
994
+ end
995
+
996
+ def select_for_reading?
997
+ true
998
+ end
999
+
1000
+
1001
+ end
1002
+ end
1003
+
1004
+
1005
+
1006
+ module EventMachine
1007
+ # @private
1008
+ class EvmaUNIXClient < StreamObject
1009
+
1010
+ def self.connect chain
1011
+ sd = Socket.new( Socket::AF_LOCAL, Socket::SOCK_STREAM, 0 )
1012
+ begin
1013
+ # TODO, this assumes a current Ruby snapshot.
1014
+ # We need to degrade to a nonblocking connect otherwise.
1015
+ sd.connect_nonblock( Socket.pack_sockaddr_un( chain ))
1016
+ rescue Errno::EINPROGRESS
1017
+ end
1018
+ EvmaUNIXClient.new sd
1019
+ end
1020
+
1021
+
1022
+ def initialize io
1023
+ super
1024
+ @pending = true
1025
+ end
1026
+
1027
+
1028
+ def select_for_writing?
1029
+ @pending ? true : super
1030
+ end
1031
+
1032
+ def select_for_reading?
1033
+ @pending ? false : super
1034
+ end
1035
+
1036
+ def eventable_write
1037
+ if @pending
1038
+ @pending = false
1039
+ if 0 == io.getsockopt(Socket::SOL_SOCKET, Socket::SO_ERROR).unpack("i").first
1040
+ EventMachine::event_callback uuid, ConnectionCompleted, ""
1041
+ end
1042
+ else
1043
+ super
1044
+ end
1045
+ end
1046
+
1047
+
1048
+
1049
+ end
1050
+ end
1051
+
1052
+
1053
+ #--------------------------------------------------------------
1054
+
1055
+ module EventMachine
1056
+ # @private
1057
+ class EvmaTCPServer < Selectable
1058
+
1059
+ # TODO, refactor and unify with EvmaUNIXServer.
1060
+
1061
+ class << self
1062
+ # Versions of ruby 1.8.4 later than May 26 2006 will work properly
1063
+ # with an object of type TCPServer. Prior versions won't so we
1064
+ # play it safe and just build a socket.
1065
+ #
1066
+ def start_server host, port
1067
+ sd = Socket.new( Socket::AF_INET, Socket::SOCK_STREAM, 0 )
1068
+ sd.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true )
1069
+ sd.bind( Socket.pack_sockaddr_in( port, host ))
1070
+ sd.listen( 50 ) # 5 is what you see in all the books. Ain't enough.
1071
+ EvmaTCPServer.new sd
1072
+ end
1073
+ end
1074
+
1075
+ def initialize io
1076
+ super io
1077
+ end
1078
+
1079
+
1080
+ def select_for_reading?
1081
+ true
1082
+ end
1083
+
1084
+ #--
1085
+ # accept_nonblock returns an array consisting of the accepted
1086
+ # socket and a sockaddr_in which names the peer.
1087
+ # Don't accept more than 10 at a time.
1088
+ def eventable_read
1089
+ begin
1090
+ 10.times {
1091
+ descriptor,peername = io.accept_nonblock
1092
+ sd = EvmaTCPClient.new descriptor
1093
+ sd.is_server = true
1094
+ EventMachine::event_callback uuid, ConnectionAccepted, sd.uuid
1095
+ }
1096
+ rescue Errno::EWOULDBLOCK, Errno::EAGAIN
1097
+ end
1098
+ end
1099
+
1100
+ #--
1101
+ #
1102
+ def schedule_close
1103
+ @close_scheduled = true
1104
+ end
1105
+
1106
+ end
1107
+ end
1108
+
1109
+
1110
+ #--------------------------------------------------------------
1111
+
1112
+ module EventMachine
1113
+ # @private
1114
+ class EvmaUNIXServer < Selectable
1115
+
1116
+ # TODO, refactor and unify with EvmaTCPServer.
1117
+
1118
+ class << self
1119
+ # Versions of ruby 1.8.4 later than May 26 2006 will work properly
1120
+ # with an object of type TCPServer. Prior versions won't so we
1121
+ # play it safe and just build a socket.
1122
+ #
1123
+ def start_server chain
1124
+ sd = Socket.new( Socket::AF_LOCAL, Socket::SOCK_STREAM, 0 )
1125
+ sd.setsockopt( Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true )
1126
+ sd.bind( Socket.pack_sockaddr_un( chain ))
1127
+ sd.listen( 50 ) # 5 is what you see in all the books. Ain't enough.
1128
+ EvmaUNIXServer.new sd
1129
+ end
1130
+ end
1131
+
1132
+ def initialize io
1133
+ super io
1134
+ end
1135
+
1136
+
1137
+ def select_for_reading?
1138
+ true
1139
+ end
1140
+
1141
+ #--
1142
+ # accept_nonblock returns an array consisting of the accepted
1143
+ # socket and a sockaddr_in which names the peer.
1144
+ # Don't accept more than 10 at a time.
1145
+ def eventable_read
1146
+ begin
1147
+ 10.times {
1148
+ descriptor,peername = io.accept_nonblock
1149
+ sd = StreamObject.new descriptor
1150
+ EventMachine::event_callback uuid, ConnectionAccepted, sd.uuid
1151
+ }
1152
+ rescue Errno::EWOULDBLOCK, Errno::EAGAIN
1153
+ end
1154
+ end
1155
+
1156
+ #--
1157
+ #
1158
+ def schedule_close
1159
+ @close_scheduled = true
1160
+ end
1161
+
1162
+ end
1163
+ end
1164
+
1165
+
1166
+
1167
+ #--------------------------------------------------------------
1168
+
1169
+ module EventMachine
1170
+ # @private
1171
+ class LoopbreakReader < Selectable
1172
+
1173
+ def select_for_reading?
1174
+ true
1175
+ end
1176
+
1177
+ def eventable_read
1178
+ io.sysread(128)
1179
+ EventMachine::event_callback "", LoopbreakSignalled, ""
1180
+ end
1181
+
1182
+ end
1183
+ end
1184
+
1185
+
1186
+
1187
+ # @private
1188
+ module EventMachine
1189
+ # @private
1190
+ class DatagramObject < Selectable
1191
+ def initialize io
1192
+ super io
1193
+ @outbound_q = []
1194
+ end
1195
+
1196
+ # #send_datagram
1197
+ def send_datagram data, target
1198
+ # TODO, coalesce here perhaps by being smarter about appending to @outbound_q.last?
1199
+ unless @close_scheduled or @close_requested
1200
+ @outbound_q << [data.to_s, target]
1201
+ end
1202
+ end
1203
+
1204
+ # #select_for_writing?
1205
+ def select_for_writing?
1206
+ unless @close_scheduled
1207
+ if @outbound_q.empty?
1208
+ @close_scheduled = true if @close_requested
1209
+ false
1210
+ else
1211
+ true
1212
+ end
1213
+ end
1214
+ end
1215
+
1216
+ # #select_for_reading?
1217
+ def select_for_reading?
1218
+ true
1219
+ end
1220
+
1221
+ # #get_outbound_data_size
1222
+ def get_outbound_data_size
1223
+ @outbound_q.inject(0) {|memo,obj| memo += (obj || "").length}
1224
+ end
1225
+
1226
+
1227
+ end
1228
+
1229
+
1230
+ end
1231
+
1232
+
1233
+ module EventMachine
1234
+ # @private
1235
+ class EvmaUDPSocket < DatagramObject
1236
+
1237
+ class << self
1238
+ def create host, port
1239
+ sd = Socket.new( Socket::AF_INET, Socket::SOCK_DGRAM, 0 )
1240
+ sd.bind Socket::pack_sockaddr_in( port, host )
1241
+ EvmaUDPSocket.new sd
1242
+ end
1243
+ end
1244
+
1245
+ # #eventable_write
1246
+ # This really belongs in DatagramObject, but there is some UDP-specific stuff.
1247
+ def eventable_write
1248
+ 40.times {
1249
+ break if @outbound_q.empty?
1250
+ begin
1251
+ data,target = @outbound_q.first
1252
+
1253
+ # This damn better be nonblocking.
1254
+ io.send data.to_s, 0, target
1255
+
1256
+ @outbound_q.shift
1257
+ rescue Errno::EAGAIN
1258
+ # It's not been observed in testing that we ever get here.
1259
+ # True to the definition, packets will be accepted and quietly dropped
1260
+ # if the system is under pressure.
1261
+ break
1262
+ rescue EOFError, Errno::ECONNRESET
1263
+ @close_scheduled = true
1264
+ @outbound_q.clear
1265
+ end
1266
+ }
1267
+ end
1268
+
1269
+ # Proper nonblocking I/O was added to Ruby 1.8.4 in May 2006.
1270
+ # If we have it, then we can read multiple times safely to improve
1271
+ # performance.
1272
+ def eventable_read
1273
+ begin
1274
+ if io.respond_to?(:recvfrom_nonblock)
1275
+ 40.times {
1276
+ data,@return_address = io.recvfrom_nonblock(16384)
1277
+ EventMachine::event_callback uuid, ConnectionData, data
1278
+ @return_address = nil
1279
+ }
1280
+ else
1281
+ raise "unimplemented datagram-read operation on this Ruby"
1282
+ end
1283
+ rescue Errno::EAGAIN
1284
+ # no-op
1285
+ rescue Errno::ECONNRESET, EOFError
1286
+ @close_scheduled = true
1287
+ EventMachine::event_callback uuid, ConnectionUnbound, nil
1288
+ end
1289
+ end
1290
+
1291
+ def send_data data
1292
+ send_datagram data, @return_address
1293
+ end
1294
+ end
1295
+ end
1296
+
1297
+ # load base EM api on top, now that we have the underlying pure ruby
1298
+ # implementation defined
1299
+ require 'eventmachine'