eventmachine 0.12.6 → 0.12.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (116) hide show
  1. data/{docs/README → README} +21 -13
  2. data/Rakefile +14 -4
  3. data/docs/DEFERRABLES +0 -5
  4. data/docs/INSTALL +2 -4
  5. data/docs/LEGAL +1 -1
  6. data/docs/LIGHTWEIGHT_CONCURRENCY +0 -2
  7. data/docs/PURE_RUBY +0 -2
  8. data/docs/RELEASE_NOTES +0 -2
  9. data/docs/SMTP +0 -7
  10. data/docs/SPAWNED_PROCESSES +0 -4
  11. data/docs/TODO +0 -2
  12. data/eventmachine.gemspec +17 -8
  13. data/examples/ex_channel.rb +43 -0
  14. data/examples/ex_queue.rb +2 -0
  15. data/examples/helper.rb +2 -0
  16. data/ext/cmain.cpp +119 -20
  17. data/ext/cplusplus.cpp +15 -6
  18. data/ext/ed.cpp +303 -93
  19. data/ext/ed.h +49 -22
  20. data/ext/em.cpp +368 -42
  21. data/ext/em.h +43 -6
  22. data/ext/eventmachine.h +21 -8
  23. data/ext/eventmachine_cpp.h +1 -0
  24. data/ext/extconf.rb +4 -0
  25. data/ext/kb.cpp +1 -2
  26. data/ext/pipe.cpp +1 -3
  27. data/ext/project.h +21 -0
  28. data/ext/rubymain.cpp +232 -32
  29. data/ext/ssl.cpp +38 -1
  30. data/ext/ssl.h +5 -1
  31. data/java/src/com/rubyeventmachine/Application.java +7 -3
  32. data/java/src/com/rubyeventmachine/EmReactor.java +16 -1
  33. data/java/src/com/rubyeventmachine/tests/ConnectTest.java +25 -3
  34. data/lib/{protocols → em}/buftok.rb +16 -5
  35. data/lib/em/callback.rb +26 -0
  36. data/lib/em/channel.rb +57 -0
  37. data/lib/em/connection.rb +505 -0
  38. data/lib/em/deferrable.rb +144 -165
  39. data/lib/em/file_watch.rb +54 -0
  40. data/lib/em/future.rb +24 -25
  41. data/lib/em/messages.rb +1 -1
  42. data/lib/em/process_watch.rb +44 -0
  43. data/lib/em/processes.rb +58 -52
  44. data/lib/em/protocols.rb +35 -0
  45. data/lib/em/protocols/header_and_content.rb +138 -0
  46. data/lib/em/protocols/httpclient.rb +263 -0
  47. data/lib/em/protocols/httpclient2.rb +582 -0
  48. data/lib/{protocols → em/protocols}/line_and_text.rb +2 -2
  49. data/lib/em/protocols/linetext2.rb +160 -0
  50. data/lib/{protocols → em/protocols}/memcache.rb +37 -7
  51. data/lib/em/protocols/object_protocol.rb +39 -0
  52. data/lib/em/protocols/postgres3.rb +247 -0
  53. data/lib/em/protocols/saslauth.rb +175 -0
  54. data/lib/em/protocols/smtpclient.rb +331 -0
  55. data/lib/em/protocols/smtpserver.rb +547 -0
  56. data/lib/em/protocols/stomp.rb +200 -0
  57. data/lib/{protocols → em/protocols}/tcptest.rb +21 -25
  58. data/lib/em/queue.rb +61 -0
  59. data/lib/em/spawnable.rb +53 -56
  60. data/lib/em/streamer.rb +92 -74
  61. data/lib/em/timers.rb +55 -0
  62. data/lib/em/version.rb +3 -0
  63. data/lib/eventmachine.rb +1008 -1298
  64. data/lib/evma.rb +1 -1
  65. data/lib/jeventmachine.rb +106 -101
  66. data/lib/pr_eventmachine.rb +47 -36
  67. data/tasks/project.rake +2 -1
  68. data/tests/client.crt +31 -0
  69. data/tests/client.key +51 -0
  70. data/tests/test_attach.rb +18 -0
  71. data/tests/test_basic.rb +108 -54
  72. data/tests/test_channel.rb +63 -0
  73. data/tests/test_connection_count.rb +2 -2
  74. data/tests/test_epoll.rb +109 -110
  75. data/tests/test_errors.rb +36 -36
  76. data/tests/test_exc.rb +22 -25
  77. data/tests/test_file_watch.rb +49 -0
  78. data/tests/test_futures.rb +77 -93
  79. data/tests/test_hc.rb +2 -2
  80. data/tests/test_httpclient.rb +55 -52
  81. data/tests/test_httpclient2.rb +110 -112
  82. data/tests/test_inactivity_timeout.rb +30 -0
  83. data/tests/test_kb.rb +8 -9
  84. data/tests/test_ltp2.rb +274 -277
  85. data/tests/test_next_tick.rb +91 -65
  86. data/tests/test_object_protocol.rb +37 -0
  87. data/tests/test_process_watch.rb +48 -0
  88. data/tests/test_processes.rb +56 -23
  89. data/tests/test_proxy_connection.rb +92 -0
  90. data/tests/test_pure.rb +1 -5
  91. data/tests/test_queue.rb +44 -0
  92. data/tests/test_running.rb +9 -14
  93. data/tests/test_sasl.rb +32 -34
  94. data/tests/test_send_file.rb +175 -176
  95. data/tests/test_servers.rb +37 -41
  96. data/tests/test_smtpserver.rb +47 -55
  97. data/tests/test_spawn.rb +284 -291
  98. data/tests/test_ssl_args.rb +1 -1
  99. data/tests/test_ssl_methods.rb +1 -1
  100. data/tests/test_ssl_verify.rb +82 -0
  101. data/tests/test_timers.rb +81 -88
  102. data/tests/test_ud.rb +0 -7
  103. data/tests/testem.rb +1 -1
  104. metadata +68 -39
  105. data/lib/em/eventable.rb +0 -39
  106. data/lib/eventmachine_version.rb +0 -31
  107. data/lib/protocols/header_and_content.rb +0 -129
  108. data/lib/protocols/httpcli2.rb +0 -803
  109. data/lib/protocols/httpclient.rb +0 -270
  110. data/lib/protocols/linetext2.rb +0 -161
  111. data/lib/protocols/postgres.rb +0 -261
  112. data/lib/protocols/saslauth.rb +0 -179
  113. data/lib/protocols/smtpclient.rb +0 -308
  114. data/lib/protocols/smtpserver.rb +0 -556
  115. data/lib/protocols/stomp.rb +0 -153
  116. data/tests/test_eventables.rb +0 -77
@@ -0,0 +1,175 @@
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 SASL authd.
31
+ # This is a very, very simple protocol that mimics the one used
32
+ # by saslauthd and pwcheck, two outboard daemons included in the
33
+ # standard SASL library distro.
34
+ # The only thing this is really suitable for is SASL PLAIN
35
+ # (user+password) authentication, but the SASL libs that are
36
+ # linked into standard servers (like imapd and sendmail) implement
37
+ # the other ones.
38
+ #
39
+ # SASL-auth is intended for reasonably fast operation inside a
40
+ # single machine, so it has no transport-security (although there
41
+ # have been multi-machine extensions incorporating transport-layer
42
+ # encryption).
43
+ #
44
+ # The standard saslauthd module generally runs privileged and does
45
+ # its work by referring to the system-account files.
46
+ #
47
+ # This feature was added to EventMachine to enable the development
48
+ # of custom authentication/authorization engines for standard servers.
49
+ #
50
+ # To use SASLauth, include it in a class that subclasses EM::Connection,
51
+ # and reimplement the validate method.
52
+ #
53
+ # The typical way to incorporate this module into an authentication
54
+ # daemon would be to set it as the handler for a UNIX-domain socket.
55
+ # The code might look like this:
56
+ #
57
+ # EM.start_unix_domain_server( "/var/run/saslauthd/mux", MyHandler )
58
+ # File.chmod( 0777, "/var/run/saslauthd/mux")
59
+ #
60
+ # The chmod is probably needed to ensure that unprivileged clients can
61
+ # access the UNIX-domain socket.
62
+ #
63
+ # It's also a very good idea to drop superuser privileges (if any), after
64
+ # the UNIX-domain socket has been opened.
65
+ #--
66
+ # Implementation details: assume the client can send us pipelined requests,
67
+ # and that the client will close the connection.
68
+ #
69
+ # The client sends us four values, each encoded as a two-byte length field in
70
+ # network order followed by the specified number of octets.
71
+ # The fields specify the username, password, service name (such as imap),
72
+ # and the "realm" name. We send back the barest minimum reply, a single
73
+ # field also encoded as a two-octet length in network order, followed by
74
+ # either "NO" or "OK" - simplicity itself.
75
+ #
76
+ # We enforce a maximum field size just as a sanity check.
77
+ # We do NOT automatically time out the connection.
78
+ #
79
+ # The code we use to parse out the values is ugly and probably slow.
80
+ # Improvements welcome.
81
+ #
82
+ module SASLauth
83
+
84
+ MaxFieldSize = 128*1024
85
+ def post_init
86
+ super
87
+ @sasl_data = ""
88
+ @sasl_values = []
89
+ end
90
+
91
+ def receive_data data
92
+ @sasl_data << data
93
+ while @sasl_data.length >= 2
94
+ len = (@sasl_data[0,2].unpack("n")).first
95
+ raise "SASL Max Field Length exceeded" if len > MaxFieldSize
96
+ if @sasl_data.length >= (len + 2)
97
+ @sasl_values << @sasl_data[2,len]
98
+ @sasl_data.slice!(0...(2+len))
99
+ if @sasl_values.length == 4
100
+ send_data( validate(*@sasl_values) ? "\0\002OK" : "\0\002NO" )
101
+ @sasl_values.clear
102
+ end
103
+ else
104
+ break
105
+ end
106
+ end
107
+ end
108
+
109
+ def validate username, psw, sysname, realm
110
+ p username
111
+ p psw
112
+ p sysname
113
+ p realm
114
+ true
115
+ end
116
+ end
117
+
118
+ # Implements the SASL authd client protocol.
119
+ # This is a very, very simple protocol that mimics the one used
120
+ # by saslauthd and pwcheck, two outboard daemons included in the
121
+ # standard SASL library distro.
122
+ # The only thing this is really suitable for is SASL PLAIN
123
+ # (user+password) authentication, but the SASL libs that are
124
+ # linked into standard servers (like imapd and sendmail) implement
125
+ # the other ones.
126
+ #
127
+ # You can use this module directly as a handler for EM Connections,
128
+ # or include it in a module or handler class of your own.
129
+ #
130
+ # First connect to a SASL server (it's probably a TCP server, or more
131
+ # likely a Unix-domain socket). Then call the #validate? method,
132
+ # passing at least a username and a password. #validate? returns
133
+ # a Deferrable which will either succeed or fail, depending
134
+ # on the status of the authentication operation.
135
+ #
136
+ module SASLauthclient
137
+ MaxFieldSize = 128*1024
138
+
139
+ def validate? username, psw, sysname=nil, realm=nil
140
+
141
+ str = [username, psw, sysname, realm].map {|m|
142
+ [(m || "").length, (m || "")]
143
+ }.flatten.pack( "nA*" * 4 )
144
+ send_data str
145
+
146
+ d = EM::DefaultDeferrable.new
147
+ @queries.unshift d
148
+ d
149
+ end
150
+
151
+ def post_init
152
+ @sasl_data = ""
153
+ @queries = []
154
+ end
155
+
156
+ def receive_data data
157
+ @sasl_data << data
158
+
159
+ while @sasl_data.length > 2
160
+ len = (@sasl_data[0,2].unpack("n")).first
161
+ raise "SASL Max Field Length exceeded" if len > MaxFieldSize
162
+ if @sasl_data.length >= (len + 2)
163
+ val = @sasl_data[2,len]
164
+ @sasl_data.slice!(0...(2+len))
165
+ q = @queries.pop
166
+ (val == "NO") ? q.fail : q.succeed
167
+ else
168
+ break
169
+ end
170
+ end
171
+ end
172
+ end
173
+
174
+ end
175
+ end
@@ -0,0 +1,331 @@
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
+ require 'ostruct'
27
+
28
+ module EventMachine
29
+ module Protocols
30
+
31
+ # Simple SMTP client
32
+ #
33
+ # email = EM::Protocols::SmtpClient.send(
34
+ # :domain=>"example.com",
35
+ # :host=>'localhost',
36
+ # :port=>25, # optional, defaults 25
37
+ # :starttls=>true, # use ssl
38
+ # :from=>"sender@example.com",
39
+ # :to=> ["to_1@example.com", "to_2@example.com"],
40
+ # :header=> {"Subject" => "This is a subject line"},
41
+ # :body=> "This is the body of the email"
42
+ # )
43
+ # email.callback{
44
+ # puts 'Email sent!'
45
+ # }
46
+ # email.errback{ |e|
47
+ # puts 'Email failed!'
48
+ # }
49
+ #
50
+ class SmtpClient < Connection
51
+ include EventMachine::Deferrable
52
+ include EventMachine::Protocols::LineText2
53
+
54
+ # :host => required String
55
+ # a string containing the IP address or host name of the SMTP server to connect to.
56
+ # :port => optional
57
+ # defaults to 25.
58
+ # :domain => required String
59
+ # This is passed as the argument to the EHLO command.
60
+ # :starttls => optional Boolean
61
+ # If it evaluates true, then the client will initiate STARTTLS with
62
+ # the server, and abort the connection if the negotiation doesn't succeed.
63
+ # TODO, need to be able to pass certificate parameters with this option.
64
+ # :auth => optional Hash of auth parameters
65
+ # If not given, then no auth will be attempted.
66
+ # (In that case, the connection will be aborted if the server requires auth.)
67
+ # Specify the hash value :type to determine the auth type, along with additional parameters
68
+ # depending on the type.
69
+ # Currently only :type => :plain is supported. Pass additional parameters :username (String),
70
+ # and :password (either a String or a Proc that will be called at auth-time).
71
+ # Example: :auth => {:type=>:plain, :username=>"mickey@disney.com", :password=>"mouse"}
72
+ # :from => required String
73
+ # Specifies the sender of the message. Will be passed as the argument
74
+ # to the MAIL FROM. Do NOT enclose the argument in angle-bracket (<>) characters.
75
+ # The connection will abort if the server rejects the value.
76
+ # :to => required String or Array of Strings
77
+ # The recipient(s) of the message. Do NOT enclose
78
+ # any of the values in angle-brackets (<>) characters. It's NOT a fatal error if one or more
79
+ # recipients are rejected by the server. (Of course, if ALL of them are, the server will most
80
+ # likely trigger an error when we try to send data.) An array of codes containing the status
81
+ # of each requested recipient is available after the call completes. TODO, we should define
82
+ # an overridable stub that will be called on rejection of a recipient or a sender, giving
83
+ # user code the chance to try again or abort the connection.
84
+ # :header => Required hash of values to be transmitted in the header of the message.
85
+ # The hash keys are the names of the headers (do NOT append a trailing colon), and the values are strings
86
+ # containing the header values. TODO, support Arrays of header values, which would cause us to
87
+ # send that specific header line more than once.
88
+ #
89
+ # Example: :header => {"Subject" => "Bogus", "CC" => "myboss@example.com"}
90
+ # :body => Optional string, defaults blank.
91
+ # This will be passed as the body of the email message.
92
+ # TODO, this needs to be significantly beefed up. As currently written, this requires the caller
93
+ # to properly format the input into CRLF-delimited lines of 7-bit characters in the standard
94
+ # SMTP transmission format. We need to be able to automatically convert binary data, and add
95
+ # correct line-breaks to text data. I think the :body parameter should remain as it is, and we
96
+ # should add a :content parameter that contains autoconversions and/or conversion parameters.
97
+ # Then we can check if either :body or :content is present and do the right thing.
98
+ # :verbose => Optional.
99
+ # If true, will cause a lot of information (including the server-side of the
100
+ # conversation) to be dumped to $>.
101
+ #
102
+ def self.send args={}
103
+ args[:port] ||= 25
104
+ args[:body] ||= ""
105
+
106
+ =begin
107
+ (I don't think it's possible for EM#connect to throw an exception under normal
108
+ circumstances, so this original code is stubbed out. A connect-failure will result
109
+ in the #unbind method being called without calling #connection_completed.)
110
+ begin
111
+ EventMachine.connect( args[:host], args[:port], self) {|c|
112
+ # According to the EM docs, we will get here AFTER post_init is called.
113
+ c.args = args
114
+ c.set_comm_inactivity_timeout 60
115
+ }
116
+ rescue
117
+ # We'll get here on a connect error. This code mimics the effect
118
+ # of a call to invoke_internal_error. Would be great to DRY this up.
119
+ # (Actually, it may be that we never get here, if EM#connect catches
120
+ # its errors internally.)
121
+ d = EM::DefaultDeferrable.new
122
+ d.set_deferred_status(:failed, {:error=>[:connect, 500, "unable to connect to server"]})
123
+ d
124
+ end
125
+ =end
126
+ EventMachine.connect( args[:host], args[:port], self) {|c|
127
+ # According to the EM docs, we will get here AFTER post_init is called.
128
+ c.args = args
129
+ c.set_comm_inactivity_timeout 60
130
+ }
131
+ end
132
+
133
+ # :stopdoc:
134
+
135
+ attr_writer :args
136
+
137
+ def post_init
138
+ @return_values = OpenStruct.new
139
+ @return_values.start_time = Time.now
140
+ end
141
+
142
+ def connection_completed
143
+ @responder = :receive_signon
144
+ @msg = []
145
+ end
146
+
147
+ # We can get here in a variety of ways, all of them being failures unless
148
+ # the @succeeded flag is set. If a protocol success was recorded, then don't
149
+ # set a deferred success because the caller will already have done it
150
+ # (no need to wait until the connection closes to invoke the callbacks).
151
+ #
152
+ def unbind
153
+ unless @succeeded
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)
159
+ end
160
+ end
161
+
162
+ def receive_line ln
163
+ $>.puts ln if @args[:verbose]
164
+ @range = ln[0...1].to_i
165
+ @code = ln[0...3].to_i
166
+ @msg << ln[4..-1]
167
+ unless ln[3...4] == '-'
168
+ $>.puts @responder if @args[:verbose]
169
+ send @responder
170
+ @msg.clear
171
+ end
172
+ end
173
+
174
+ # We encountered an error from the server and will close the connection.
175
+ # Use the error and message the server returned.
176
+ #
177
+ def invoke_error
178
+ @return_values.elapsed_time = Time.now - @return_values.start_time
179
+ @return_values.responder = @responder
180
+ @return_values.code = @code
181
+ @return_values.message = @msg
182
+ set_deferred_status :failed, @return_values
183
+ send_data "QUIT\r\n"
184
+ close_connection_after_writing
185
+ end
186
+
187
+ # We encountered an error on our side of the protocol and will close the connection.
188
+ # Use an extra-protocol error code (900) and use the message from the caller.
189
+ #
190
+ def invoke_internal_error msg = "???"
191
+ @return_values.elapsed_time = Time.now - @return_values.start_time
192
+ @return_values.responder = @responder
193
+ @return_values.code = 900
194
+ @return_values.message = msg
195
+ set_deferred_status :failed, @return_values
196
+ send_data "QUIT\r\n"
197
+ close_connection_after_writing
198
+ end
199
+
200
+ def receive_signon
201
+ return invoke_error unless @range == 2
202
+ send_data "EHLO #{@args[:domain]}\r\n"
203
+ @responder = :receive_ehlo_response
204
+ end
205
+
206
+ def receive_ehlo_response
207
+ return invoke_error unless @range == 2
208
+ @server_caps = @msg
209
+ invoke_starttls
210
+ end
211
+
212
+ def invoke_starttls
213
+ if @args[:starttls]
214
+ # It would be more sociable to first ask if @server_caps contains
215
+ # the string "STARTTLS" before we invoke it, but hey, life's too short.
216
+ send_data "STARTTLS\r\n"
217
+ @responder = :receive_starttls_response
218
+ else
219
+ invoke_auth
220
+ end
221
+ end
222
+ def receive_starttls_response
223
+ return invoke_error unless @range == 2
224
+ start_tls
225
+ invoke_auth
226
+ end
227
+
228
+ # Perform an authentication. If the caller didn't request one, then fall through
229
+ # to the mail-from state.
230
+ def invoke_auth
231
+ if @args[:auth]
232
+ if @args[:auth][:type] == :plain
233
+ psw = @args[:auth][:password]
234
+ if psw.respond_to?(:call)
235
+ psw = psw.call
236
+ end
237
+ #str = Base64::encode64("\0#{@args[:auth][:username]}\0#{psw}").chomp
238
+ str = ["\0#{@args[:auth][:username]}\0#{psw}"].pack("m").chomp
239
+ send_data "AUTH PLAIN #{str}\r\n"
240
+ @responder = :receive_auth_response
241
+ else
242
+ return invoke_internal_error("unsupported auth type")
243
+ end
244
+ else
245
+ invoke_mail_from
246
+ end
247
+ end
248
+ def receive_auth_response
249
+ return invoke_error unless @range == 2
250
+ invoke_mail_from
251
+ end
252
+
253
+ def invoke_mail_from
254
+ send_data "MAIL FROM: <#{@args[:from]}>\r\n"
255
+ @responder = :receive_mail_from_response
256
+ end
257
+ def receive_mail_from_response
258
+ return invoke_error unless @range == 2
259
+ invoke_rcpt_to
260
+ end
261
+
262
+ def invoke_rcpt_to
263
+ @rcpt_responses ||= []
264
+ l = @rcpt_responses.length
265
+ to = @args[:to].is_a?(Array) ? @args[:to] : [@args[:to].to_s]
266
+ if l < to.length
267
+ send_data "RCPT TO: <#{to[l]}>\r\n"
268
+ @responder = :receive_rcpt_to_response
269
+ else
270
+ e = @rcpt_responses.select {|rr| rr.last == 2}
271
+ if e and e.length > 0
272
+ invoke_data
273
+ else
274
+ invoke_error
275
+ end
276
+ end
277
+ end
278
+ def receive_rcpt_to_response
279
+ @rcpt_responses << [@code, @msg, @range]
280
+ invoke_rcpt_to
281
+ end
282
+
283
+ def invoke_data
284
+ send_data "DATA\r\n"
285
+ @responder = :receive_data_response
286
+ end
287
+ def receive_data_response
288
+ return invoke_error unless @range == 3
289
+
290
+ # The data to send can be given either in @args[:content] (an array or string of raw data
291
+ # which MUST be in correct SMTP body format, including a trailing dot line), or a header and
292
+ # body given in @args[:header] and @args[:body].
293
+ #
294
+ if @args[:content]
295
+ send_data @args[:content].to_s
296
+ else
297
+ # The header can be a hash or an array.
298
+ if @args[:header].is_a?(Hash)
299
+ (@args[:header] || {}).each {|k,v| send_data "#{k}: #{v}\r\n" }
300
+ else
301
+ send_data @args[:header].to_s
302
+ end
303
+ send_data "\r\n"
304
+
305
+ if @args[:body].is_a?(Array)
306
+ @args[:body].each {|e| send_data e}
307
+ else
308
+ send_data @args[:body].to_s
309
+ end
310
+
311
+ send_data "\r\n.\r\n"
312
+ end
313
+
314
+ @responder = :receive_message_response
315
+ end
316
+ def receive_message_response
317
+ return invoke_error unless @range == 2
318
+ send_data "QUIT\r\n"
319
+ close_connection_after_writing
320
+ @succeeded = true
321
+ @return_values.elapsed_time = Time.now - @return_values.start_time
322
+ @return_values.responder = @responder
323
+ @return_values.code = @code
324
+ @return_values.message = @msg
325
+ set_deferred_status :succeeded, @return_values
326
+ end
327
+
328
+ # :startdoc:
329
+ end
330
+ end
331
+ end