wj_eventmachine 1.3.0.dev.1

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.
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'