eventmachine 0.9.0 → 0.10.0

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.
@@ -0,0 +1,121 @@
1
+ # $Id: saslauth.rb 568 2007-11-13 02:36:17Z blackhedd $
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
+
28
+
29
+ module EventMachine
30
+ module Protocols
31
+
32
+ # Implements SASL authd.
33
+ # This is a very, very simple protocol that mimics the one used
34
+ # by saslauthd and pwcheck, two outboard daemons included in the
35
+ # standard SASL library distro.
36
+ # The only thing this is really suitable for is SASL PLAIN
37
+ # (user+password) authentication, but the SASL libs that are
38
+ # linked into standard servers (like imapd and sendmail) implement
39
+ # the other ones.
40
+ #
41
+ # SASL-auth is intended for reasonably fast operation inside a
42
+ # single machine, so it has no transport-security (although there
43
+ # have been multi-machine extensions incorporating transport-layer
44
+ # encryption).
45
+ #
46
+ # The standard saslauthd module generally runs privileged and does
47
+ # its work by referring to the system-account files.
48
+ #
49
+ # This feature was added to EventMachine to enable the development
50
+ # of custom authentication/authorization engines for standard servers.
51
+ #
52
+ # To use SASLauth, include it in a class that subclasses EM::Connection,
53
+ # and reimplement the validate method.
54
+ #
55
+ # The typical way to incorporate this module into an authentication
56
+ # daemon would be to set it as the handler for a UNIX-domain socket.
57
+ # The code might look like this:
58
+ #
59
+ # EM.start_unix_domain_server( "/var/run/saslauthd/mux", MyHandler )
60
+ # File.chmod( 0777, "/var/run/saslauthd/mux")
61
+ #
62
+ # The chmod is probably needed to ensure that unprivileged clients can
63
+ # access the UNIX-domain socket.
64
+ #
65
+ # It's also a very good idea to drop superuser privileges (if any), after
66
+ # the UNIX-domain socket has been opened.
67
+ #--
68
+ # Implementation details: assume the client can send us pipelined requests,
69
+ # and that the client will close the connection.
70
+ #
71
+ # The client sends us four values, each encoded as a two-byte length field in
72
+ # network order followed by the specified number of octets.
73
+ # The fields specify the username, password, service name (such as imap),
74
+ # and the "realm" name. We send back the barest minimum reply, a single
75
+ # field also encoded as a two-octet length in network order, followed by
76
+ # either "NO" or "OK" - simplicity itself.
77
+ #
78
+ # We enforce a maximum field size just as a sanity check.
79
+ # We do NOT automatically time out the connection.
80
+ #
81
+ # The code we use to parse out the values is ugly and probably slow.
82
+ # Improvements welcome.
83
+ #
84
+ module SASLauth
85
+
86
+ MaxFieldSize = 128*1024
87
+ def post_init
88
+ super
89
+ @sasl_data = ""
90
+ @sasl_values = []
91
+ end
92
+
93
+ def receive_data data
94
+ @sasl_data << data
95
+ while @sasl_data.length >= 2
96
+ len = (@sasl_data[0,2].unpack("n")).first
97
+ raise "SASL Max Field Length exceeded" if len > MaxFieldSize
98
+ if @sasl_data.length >= (len + 2)
99
+ @sasl_values << @sasl_data[2,len]
100
+ @sasl_data.slice!(0...(2+len))
101
+ if @sasl_values.length == 4
102
+ send_data( validate(*@sasl_values) ? "\0\002OK" : "\0\002NO" )
103
+ @sasl_values.clear
104
+ end
105
+ else
106
+ break
107
+ end
108
+ end
109
+ end
110
+ def validate username, psw, sysname, realm
111
+ p username
112
+ p psw
113
+ p sysname
114
+ p realm
115
+ true
116
+ end
117
+ end
118
+ end
119
+ end
120
+
121
+
@@ -1,4 +1,4 @@
1
- # $Id: smtpclient.rb 535 2007-09-16 19:35:05Z blackhedd $
1
+ # $Id: smtpclient.rb 549 2007-09-22 00:49:42Z blackhedd $
2
2
  #
3
3
  # Author:: Francis Cianfrocca (gmail: blackhedd)
4
4
  # Homepage:: http://rubyeventmachine.com
@@ -25,6 +25,7 @@
25
25
 
26
26
 
27
27
  require 'base64'
28
+ require 'ostruct'
28
29
 
29
30
  module EventMachine
30
31
  module Protocols
@@ -78,6 +79,11 @@ module Protocols
78
79
  def self.send args={}
79
80
  args[:port] ||= 25
80
81
  args[:body] ||= ""
82
+
83
+ =begin
84
+ (I don't think it's possible for EM#connect to throw an exception under normal
85
+ circumstances, so this original code is stubbed out. A connect-failure will result
86
+ in the #unbind method being called without calling #connection_completed.)
81
87
  begin
82
88
  EventMachine.connect( args[:host], args[:port], self) {|c|
83
89
  # According to the EM docs, we will get here AFTER post_init is called.
@@ -93,13 +99,20 @@ module Protocols
93
99
  d.set_deferred_status(:failed, {:error=>[:connect, 500, "unable to connect to server"]})
94
100
  d
95
101
  end
102
+ =end
103
+ EventMachine.connect( args[:host], args[:port], self) {|c|
104
+ # According to the EM docs, we will get here AFTER post_init is called.
105
+ c.args = args
106
+ c.set_comm_inactivity_timeout 60
107
+ }
108
+
96
109
  end
97
110
 
98
111
  attr_writer :args
99
112
 
100
113
  def post_init
101
- @return_values = {}
102
- @return_values[:start_time] = Time.now
114
+ @return_values = OpenStruct.new
115
+ @return_values.start_time = Time.now
103
116
  end
104
117
 
105
118
  def connection_completed
@@ -114,8 +127,10 @@ module Protocols
114
127
  #
115
128
  def unbind
116
129
  unless @succeeded
117
- @return_values[:elapsed_time] = Time.now - @return_values[:start_time]
118
- @return_values[:error] = [@responder, @code, @msg]
130
+ @return_values.elapsed_time = Time.now - @return_values.start_time
131
+ @return_values.responder = @responder
132
+ @return_values.code = @code
133
+ @return_values.message = @msg
119
134
  set_deferred_status(:failed, @return_values)
120
135
  end
121
136
  end
@@ -136,7 +151,11 @@ module Protocols
136
151
  # Use the error and message the server returned.
137
152
  #
138
153
  def invoke_error
139
- set_deferred_status :failed, @responder, @code, @msg
154
+ @return_values.elapsed_time = Time.now - @return_values.start_time
155
+ @return_values.responder = @responder
156
+ @return_values.code = @code
157
+ @return_values.message = @msg
158
+ set_deferred_status :failed, @return_values
140
159
  send_data "QUIT\r\n"
141
160
  close_connection_after_writing
142
161
  end
@@ -145,7 +164,11 @@ module Protocols
145
164
  # Use an extra-protocol error code (900) and use the message from the caller.
146
165
  #
147
166
  def invoke_internal_error msg = "???"
148
- set_deferred_status :failed, @responder, 900, msg
167
+ @return_values.elapsed_time = Time.now - @return_values.start_time
168
+ @return_values.responder = @responder
169
+ @return_values.code = 900
170
+ @return_values.message = msg
171
+ set_deferred_status :failed, @return_values
149
172
  send_data "QUIT\r\n"
150
173
  close_connection_after_writing
151
174
  end
@@ -221,11 +244,16 @@ module Protocols
221
244
  send_data "RCPT TO: <#{to[l]}>\r\n"
222
245
  @responder = :receive_rcpt_to_response
223
246
  else
224
- invoke_data
247
+ e = @rcpt_responses.select {|rr| rr.last == 2}
248
+ if e and e.length > 0
249
+ invoke_data
250
+ else
251
+ invoke_error
252
+ end
225
253
  end
226
254
  end
227
255
  def receive_rcpt_to_response
228
- @rcpt_responses << [@code, @msg]
256
+ @rcpt_responses << [@code, @msg, @range]
229
257
  invoke_rcpt_to
230
258
  end
231
259
 
@@ -267,7 +295,10 @@ module Protocols
267
295
  send_data "QUIT\r\n"
268
296
  close_connection_after_writing
269
297
  @succeeded = true
270
- @return_values[:elapsed_time] = Time.now - @return_values[:start_time]
298
+ @return_values.elapsed_time = Time.now - @return_values.start_time
299
+ @return_values.responder = @responder
300
+ @return_values.code = @code
301
+ @return_values.message = @msg
271
302
  set_deferred_status :succeeded, @return_values
272
303
  end
273
304
  end
@@ -1,4 +1,4 @@
1
- # $Id: smtpserver.rb 532 2007-09-13 21:44:23Z blackhedd $
1
+ # $Id: smtpserver.rb 545 2007-09-19 21:07:28Z blackhedd $
2
2
  #
3
3
  # Author:: Francis Cianfrocca (gmail: blackhedd)
4
4
  # Homepage:: http://rubyeventmachine.com
@@ -299,6 +299,7 @@ module Protocols
299
299
 
300
300
  def process_rset
301
301
  reset_protocol_state
302
+ receive_reset
302
303
  send_data "250 Ok\r\n"
303
304
  end
304
305
 
@@ -365,16 +366,37 @@ module Protocols
365
366
  # We might want to make sure that a given recipient is only seen once, but
366
367
  # for now we'll let that be the user's problem.
367
368
  #
369
+ # User-written code can return a deferrable from receive_recipient.
370
+ #
368
371
  def process_rcpt_to rcpt
369
372
  unless @state.include?(:mail_from)
370
373
  send_data "503 MAIL is required before RCPT\r\n"
371
374
  else
375
+ succeeded = proc {
376
+ send_data "250 Ok\r\n"
377
+ @state << :rcpt unless @state.include?(:rcpt)
378
+ }
379
+ failed = proc {
380
+ send_data "550 recipient is unacceptable\r\n"
381
+ }
382
+
383
+ d = receive_recipient rcpt
384
+
385
+ if d.respond_to?(:set_deferred_status)
386
+ d.callback &succeeded
387
+ d.errback &failed
388
+ else
389
+ (d ? succeeded : failed).call
390
+ end
391
+
392
+ =begin
372
393
  unless receive_recipient rcpt
373
394
  send_data "550 recipient is unacceptable\r\n"
374
395
  else
375
396
  send_data "250 Ok\r\n"
376
397
  @state << :rcpt unless @state.include?(:rcpt)
377
398
  end
399
+ =end
378
400
  end
379
401
  end
380
402
 
@@ -470,6 +492,13 @@ module Protocols
470
492
  true
471
493
  end
472
494
 
495
+ # Sent when the remote peer issues the RSET command.
496
+ # Since RSET is not allowed to fail (according to the protocol),
497
+ # we ignore any return value from user overrides of this method.
498
+ #
499
+ def receive_reset
500
+ end
501
+
473
502
  # Sent when the remote peer has ended the connection.
474
503
  #
475
504
  def connection_ended
@@ -1,4 +1,4 @@
1
- # $Id: test_basic.rb 500 2007-08-17 09:45:20Z blackhedd $
1
+ # $Id: test_basic.rb 607 2007-12-09 20:59:12Z blackhedd $
2
2
  #
3
3
  # Author:: Francis Cianfrocca (gmail: blackhedd)
4
4
  # Homepage:: http://rubyeventmachine.com
@@ -39,7 +39,7 @@ class TestBasic < Test::Unit::TestCase
39
39
 
40
40
  def test_libtype
41
41
  lt = EventMachine.library_type
42
- case $eventmachine_library
42
+ case (ENV["EVENTMACHINE_LIBRARY"] || $eventmachine_library || :xxx).to_sym
43
43
  when :pure_ruby
44
44
  assert_equal( :pure_ruby, lt )
45
45
  when :extension
@@ -105,6 +105,37 @@ class TestBasic < Test::Unit::TestCase
105
105
 
106
106
  #--------------------------------------
107
107
 
108
+ # TODO! This is an unfinished edge case.
109
+ # EM mishandles uncaught Ruby exceptions that fire from within #unbind handlers.
110
+ # A uncaught Ruby exception results in a call to EM::release_machine (which is in an ensure
111
+ # block in EM::run). But if EM is processing an unbind request, the release_machine call
112
+ # will cause a segmentation fault.
113
+ #
114
+
115
+ TestHost = "127.0.0.1"
116
+ TestPort = 9070
117
+
118
+ class UnbindError < EM::Connection
119
+ def initialize *args
120
+ super
121
+ end
122
+ def connection_completed
123
+ close_connection_after_writing
124
+ end
125
+ def unbind
126
+ raise "Blooey"
127
+ end
128
+ end
129
+
130
+ def xxx_test_unbind_error
131
+ assert_raise( RuntimeError ) {
132
+ EM.run {
133
+ EM.start_server TestHost, TestPort
134
+ EM.connect TestHost, TestPort, UnbindError
135
+ }
136
+ }
137
+ end
138
+
108
139
  end
109
140
 
110
141
 
@@ -1,4 +1,4 @@
1
- # $Id: test_epoll.rb 449 2007-07-21 00:15:30Z blackhedd $
1
+ # $Id: test_epoll.rb 599 2007-12-05 14:24:11Z blackhedd $
2
2
  #
3
3
  # Author:: Francis Cianfrocca (gmail: blackhedd)
4
4
  # Homepage:: http://rubyeventmachine.com
@@ -143,15 +143,21 @@ class TestEpoll < Test::Unit::TestCase
143
143
  EM.epoll
144
144
  s = EM.set_descriptor_table_size 60000
145
145
  EM.run {
146
+ # The pure-Ruby version won't let us open the socket if the node already exists.
147
+ # Not sure, that actually may be correct and the compiled version is wrong.
148
+ # Pure Ruby also oddly won't let us make that many connections. This test used
149
+ # to run 100 times. Not sure where that lower connection-limit is coming from in
150
+ # pure Ruby.
151
+ File.unlink("./xxx.chain")
146
152
  EM.start_unix_domain_server "./xxx.chain", TestEchoServer
147
153
  $n = 0
148
154
  $max = 0
149
- 100.times {
155
+ 50.times {
150
156
  EM.connect_unix_domain("./xxx.chain", TestEchoClient) {$n += 1}
151
157
  }
152
158
  }
153
159
  assert_equal(0, $n)
154
- assert_equal(100, $max)
160
+ assert_equal(50, $max)
155
161
  end
156
162
 
157
163
  end
@@ -0,0 +1,72 @@
1
+ # $Id: test_errors.rb 557 2007-10-03 07:21:02Z blackhedd $
2
+ #
3
+ # Author:: Francis Cianfrocca (gmail: blackhedd)
4
+ # Homepage:: http://rubyeventmachine.com
5
+ # Date:: 8 April 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
+ $:.unshift "../lib"
28
+ require 'eventmachine'
29
+
30
+ class TestErrors < Test::Unit::TestCase
31
+
32
+ Localhost = "127.0.0.1"
33
+ Localport = 9801
34
+
35
+ def setup
36
+ end
37
+
38
+ def teardown
39
+ # Calling #set_runtime_error_hook with no block restores the
40
+ # default handling of runtime_errors.
41
+ #
42
+ EM.set_runtime_error_hook
43
+ end
44
+
45
+ # EM has a default handler for RuntimeErrors that are emitted from
46
+ # user written code. You can override the handler if you wish, but it's
47
+ # easier to call #set_runtime_error_hook.
48
+ # Ordinarily, an error in user code invoked by the reactor aborts the
49
+ # run.
50
+ #
51
+ def test_unhandled_error
52
+ assert_raise( RuntimeError ) {
53
+ EM.run {
54
+ EM.add_timer(0) {raise "AAA"}
55
+ }
56
+ }
57
+
58
+ end
59
+
60
+ def test_handled_error
61
+ err = nil
62
+ EM.run {
63
+ EM.set_runtime_error_hook {
64
+ err = true
65
+ EM.stop
66
+ }
67
+ EM.add_timer(0) {raise "AAA"}
68
+ }
69
+ assert err
70
+ end
71
+ end
72
+