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,582 @@
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
+ module EventMachine
27
+ module Protocols
28
+
29
+ # === Usage
30
+ #
31
+ # EM.run{
32
+ # conn = EM::Protocols::HttpClient2.connect 'google.com', 80
33
+ #
34
+ # req = conn.get('/')
35
+ # req.callback{ |response|
36
+ # p(response.status)
37
+ # p(response.headers)
38
+ # p(response.content)
39
+ # }
40
+ # }
41
+ class HttpClient2 < Connection
42
+ include LineText2
43
+
44
+ class Request # :nodoc:
45
+ include Deferrable
46
+
47
+ attr_reader :version
48
+ attr_reader :status
49
+ attr_reader :header_lines
50
+ attr_reader :headers
51
+ attr_reader :content
52
+ attr_reader :internal_error
53
+
54
+ def initialize conn, args
55
+ @conn = conn
56
+ @args = args
57
+ @header_lines = []
58
+ @headers = {}
59
+ @blanks = 0
60
+ end
61
+
62
+ def send_request
63
+ az = @args[:authorization] and az = "Authorization: #{az}\r\n"
64
+
65
+ r = [
66
+ "#{@args[:verb]} #{@args[:uri]} HTTP/#{@args[:version] || "1.1"}\r\n",
67
+ "Host: #{@args[:host_header] || "_"}\r\n",
68
+ az || "",
69
+ "\r\n"
70
+ ]
71
+ @conn.send_data r.join
72
+ end
73
+
74
+
75
+ #--
76
+ #
77
+ def receive_line ln
78
+ if @chunk_trailer
79
+ receive_chunk_trailer(ln)
80
+ elsif @chunking
81
+ receive_chunk_header(ln)
82
+ else
83
+ receive_header_line(ln)
84
+ end
85
+ end
86
+
87
+ #--
88
+ #
89
+ def receive_chunk_trailer ln
90
+ if ln.length == 0
91
+ @conn.pop_request
92
+ succeed(self)
93
+ else
94
+ p "Received chunk trailer line"
95
+ end
96
+ end
97
+
98
+ #--
99
+ # Allow up to ten blank lines before we get a real response line.
100
+ # Allow no more than 100 lines in the header.
101
+ #
102
+ def receive_header_line ln
103
+ if ln.length == 0
104
+ if @header_lines.length > 0
105
+ process_header
106
+ else
107
+ @blanks += 1
108
+ if @blanks > 10
109
+ @conn.close_connection
110
+ end
111
+ end
112
+ else
113
+ @header_lines << ln
114
+ if @header_lines.length > 100
115
+ @internal_error = :bad_header
116
+ @conn.close_connection
117
+ end
118
+ end
119
+ end
120
+
121
+ #--
122
+ # Cf RFC 2616 pgh 3.6.1 for the format of HTTP chunks.
123
+ #
124
+ def receive_chunk_header ln
125
+ if ln.length > 0
126
+ chunksize = ln.to_i(16)
127
+ if chunksize > 0
128
+ @conn.set_text_mode(ln.to_i(16))
129
+ else
130
+ @content = @content ? @content.join : ''
131
+ @chunk_trailer = true
132
+ end
133
+ else
134
+ # We correctly come here after each chunk gets read.
135
+ # p "Got A BLANK chunk line"
136
+ end
137
+
138
+ end
139
+
140
+
141
+ #--
142
+ # We get a single chunk. Append it to the incoming content and switch back to line mode.
143
+ #
144
+ def receive_chunked_text text
145
+ # p "RECEIVED #{text.length} CHUNK"
146
+ (@content ||= []) << text
147
+ end
148
+
149
+
150
+ #--
151
+ # TODO, inefficient how we're handling this. Part of it is done so as to
152
+ # make sure we don't have problems in detecting chunked-encoding, content-length,
153
+ # etc.
154
+ #
155
+ HttpResponseRE = /\AHTTP\/(1.[01]) ([\d]{3})/i
156
+ ClenRE = /\AContent-length:\s*(\d+)/i
157
+ ChunkedRE = /\ATransfer-encoding:\s*chunked/i
158
+ ColonRE = /\:\s*/
159
+
160
+ def process_header
161
+ unless @header_lines.first =~ HttpResponseRE
162
+ @conn.close_connection
163
+ @internal_error = :bad_request
164
+ end
165
+ @version = $1.dup
166
+ @status = $2.dup.to_i
167
+
168
+ clen = nil
169
+ chunks = nil
170
+ @header_lines.each_with_index do |e,ix|
171
+ if ix > 0
172
+ hdr,val = e.split(ColonRE,2)
173
+ (@headers[hdr.downcase] ||= []) << val
174
+ end
175
+
176
+ if clen == nil and e =~ ClenRE
177
+ clen = $1.dup.to_i
178
+ end
179
+ if e =~ ChunkedRE
180
+ chunks = true
181
+ end
182
+ end
183
+
184
+ if clen
185
+ # If the content length is zero we should not call set_text_mode,
186
+ # because a value of zero will make it wait forever, hanging the
187
+ # connection. Just return success instead, with empty content.
188
+ if clen == 0 then
189
+ @content = ""
190
+ @conn.pop_request
191
+ succeed(self)
192
+ else
193
+ @conn.set_text_mode clen
194
+ end
195
+ elsif chunks
196
+ @chunking = true
197
+ else
198
+ # Chunked transfer, multipart, or end-of-connection.
199
+ # For end-of-connection, we need to go the unbind
200
+ # method and suppress its desire to fail us.
201
+ p "NO CLEN"
202
+ p @args[:uri]
203
+ p @header_lines
204
+ @internal_error = :unsupported_clen
205
+ @conn.close_connection
206
+ end
207
+ end
208
+ private :process_header
209
+
210
+
211
+ def receive_text text
212
+ @chunking ? receive_chunked_text(text) : receive_sized_text(text)
213
+ end
214
+
215
+ #--
216
+ # At the present time, we only handle contents that have a length
217
+ # specified by the content-length header.
218
+ #
219
+ def receive_sized_text text
220
+ @content = text
221
+ @conn.pop_request
222
+ succeed(self)
223
+ end
224
+ end
225
+
226
+ # Make a connection to a remote HTTP server.
227
+ # Can take either a pair of arguments (which will be interpreted as
228
+ # a hostname/ip-address and a port), or a hash.
229
+ # If the arguments are a hash, then supported values include:
230
+ # :host => a hostname or ip-address
231
+ # :port => a port number
232
+ # :ssl => true to enable ssl
233
+ def self.connect *args
234
+ if args.length == 2
235
+ args = {:host=>args[0], :port=>args[1]}
236
+ else
237
+ args = args.first
238
+ end
239
+
240
+ h,prt,ssl = args[:host], Integer(args[:port]), (args[:tls] || args[:ssl])
241
+ conn = EM.connect( h, prt, self )
242
+ conn.start_tls if ssl
243
+ conn.set_default_host_header( h, prt, ssl )
244
+ conn
245
+ end
246
+
247
+ # Get a url
248
+ #
249
+ # req = conn.get(:uri => '/')
250
+ # req.callback{|response| puts response.content }
251
+ #
252
+ def get args
253
+ if args.is_a?(String)
254
+ args = {:uri=>args}
255
+ end
256
+ args[:verb] = "GET"
257
+ request args
258
+ end
259
+
260
+ # Post to a url
261
+ #
262
+ # req = conn.post('/data')
263
+ # req.callback{|response| puts response.content }
264
+ #--
265
+ # XXX there's no way to supply a POST body.. wtf?
266
+ def post args
267
+ if args.is_a?(String)
268
+ args = {:uri=>args}
269
+ end
270
+ args[:verb] = "POST"
271
+ request args
272
+ end
273
+
274
+ # :stopdoc:
275
+
276
+ #--
277
+ # Compute and remember a string to be used as the host header in HTTP requests
278
+ # unless the user overrides it with an argument to #request.
279
+ #
280
+ def set_default_host_header host, port, ssl
281
+ if (ssl and port != 443) or (!ssl and port != 80)
282
+ @host_header = "#{host}:#{port}"
283
+ else
284
+ @host_header = host
285
+ end
286
+ end
287
+
288
+
289
+ def post_init
290
+ super
291
+ @connected = EM::DefaultDeferrable.new
292
+ end
293
+
294
+ def connection_completed
295
+ super
296
+ @connected.succeed
297
+ end
298
+
299
+ #--
300
+ # All pending requests, if any, must fail.
301
+ # We might come here without ever passing through connection_completed
302
+ # in case we can't connect to the server. We'll also get here when the
303
+ # connection closes (either because the server closes it, or we close it
304
+ # due to detecting an internal error or security violation).
305
+ # In either case, run down all pending requests, if any, and signal failure
306
+ # on them.
307
+ #
308
+ # Set and remember a flag (@closed) so we can immediately fail any
309
+ # subsequent requests.
310
+ #
311
+ def unbind
312
+ super
313
+ @closed = true
314
+ (@requests || []).each {|r| r.fail}
315
+ end
316
+
317
+ def request args
318
+ args[:host_header] = @host_header unless args.has_key?(:host_header)
319
+ args[:authorization] = @authorization unless args.has_key?(:authorization)
320
+ r = Request.new self, args
321
+ if @closed
322
+ r.fail
323
+ else
324
+ (@requests ||= []).unshift r
325
+ @connected.callback {r.send_request}
326
+ end
327
+ r
328
+ end
329
+
330
+ def receive_line ln
331
+ if req = @requests.last
332
+ req.receive_line ln
333
+ else
334
+ p "??????????"
335
+ p ln
336
+ end
337
+
338
+ end
339
+ def receive_binary_data text
340
+ @requests.last.receive_text text
341
+ end
342
+
343
+ #--
344
+ # Called by a Request object when it completes.
345
+ #
346
+ def pop_request
347
+ @requests.pop
348
+ end
349
+
350
+ # :startdoc:
351
+ end
352
+
353
+
354
+ =begin
355
+ class HttpClient2x < Connection
356
+ include LineText2
357
+
358
+ # TODO: Make this behave appropriate in case a #connect fails.
359
+ # Currently, this produces no errors.
360
+
361
+ # Make a connection to a remote HTTP server.
362
+ # Can take either a pair of arguments (which will be interpreted as
363
+ # a hostname/ip-address and a port), or a hash.
364
+ # If the arguments are a hash, then supported values include:
365
+ # :host => a hostname or ip-address;
366
+ # :port => a port number
367
+ #--
368
+ # TODO, support optional encryption arguments like :ssl
369
+ def self.connect *args
370
+ if args.length == 2
371
+ args = {:host=>args[0], :port=>args[1]}
372
+ else
373
+ args = args.first
374
+ end
375
+
376
+ h,prt = args[:host],Integer(args[:port])
377
+ EM.connect( h, prt, self, h, prt )
378
+ end
379
+
380
+
381
+ #--
382
+ # Sugars a connection that makes a single request and then
383
+ # closes the connection. Matches the behavior and the arguments
384
+ # of the original implementation of class HttpClient.
385
+ #
386
+ # Intended primarily for back compatibility, but the idiom
387
+ # is probably useful so it's not deprecated.
388
+ # We return a Deferrable, as did the original implementation.
389
+ #
390
+ # Because we're improving the way we deal with errors and exceptions
391
+ # (specifically, HTTP response codes other than 2xx will trigger the
392
+ # errback rather than the callback), this may break some existing code.
393
+ #
394
+ def self.request args
395
+ c = connect args
396
+ end
397
+
398
+ #--
399
+ # Requests can be pipelined. When we get a request, add it to the
400
+ # front of a queue as an array. The last element of the @requests
401
+ # array is always the oldest request received. Each element of the
402
+ # @requests array is a two-element array consisting of a hash with
403
+ # the original caller's arguments, and an initially-empty Ostruct
404
+ # containing the data we retrieve from the server's response.
405
+ # Maintain the instance variable @current_response, which is the response
406
+ # of the oldest pending request. That's just to make other code a little
407
+ # easier. If the variable doesn't exist when we come here, we're
408
+ # obviously the first request being made on the connection.
409
+ #
410
+ # The reason for keeping this method private (and requiring use of the
411
+ # convenience methods #get, #post, #head, etc) is to avoid the small
412
+ # performance penalty of canonicalizing the verb.
413
+ #
414
+ def request args
415
+ d = EventMachine::DefaultDeferrable.new
416
+
417
+ if @closed
418
+ d.fail
419
+ return d
420
+ end
421
+
422
+ o = OpenStruct.new
423
+ o.deferrable = d
424
+ (@requests ||= []).unshift [args, o]
425
+ @current_response ||= @requests.last.last
426
+ @connected.callback {
427
+ az = args[:authorization] and az = "Authorization: #{az}\r\n"
428
+
429
+ r = [
430
+ "#{args[:verb]} #{args[:uri]} HTTP/#{args[:version] || "1.1"}\r\n",
431
+ "Host: #{args[:host_header] || @host_header}\r\n",
432
+ az || "",
433
+ "\r\n"
434
+ ]
435
+ p r
436
+ send_data r.join
437
+ }
438
+ o.deferrable
439
+ end
440
+ private :request
441
+
442
+ def get args
443
+ if args.is_a?(String)
444
+ args = {:uri=>args}
445
+ end
446
+ args[:verb] = "GET"
447
+ request args
448
+ end
449
+
450
+ def initialize host, port
451
+ super
452
+ @host_header = "#{host}:#{port}"
453
+ end
454
+ def post_init
455
+ super
456
+ @connected = EM::DefaultDeferrable.new
457
+ end
458
+
459
+
460
+ def connection_completed
461
+ super
462
+ @connected.succeed
463
+ end
464
+
465
+ #--
466
+ # Make sure to throw away any leftover incoming data if we've
467
+ # been closed due to recognizing an error.
468
+ #
469
+ # Generate an internal error if we get an unreasonable number of
470
+ # header lines. It could be malicious.
471
+ #
472
+ def receive_line ln
473
+ p ln
474
+ return if @closed
475
+
476
+ if ln.length > 0
477
+ (@current_response.headers ||= []).push ln
478
+ abort_connection if @current_response.headers.length > 100
479
+ else
480
+ process_received_headers
481
+ end
482
+ end
483
+
484
+ #--
485
+ # We come here when we've seen all the headers for a particular request.
486
+ # What we do next depends on the response line (which should be the
487
+ # first line in the header set), and whether there is content to read.
488
+ # We may transition into a text-reading state to read content, or
489
+ # we may abort the connection, or we may go right back into parsing
490
+ # responses for the next response in the chain.
491
+ #
492
+ # We make an ASSUMPTION that the first line is an HTTP response.
493
+ # Anything else produces an error that aborts the connection.
494
+ # This may not be enough, because it may be that responses to pipelined
495
+ # requests will come with a blank-line delimiter.
496
+ #
497
+ # Any non-2xx response will be treated as a fatal error, and abort the
498
+ # connection. We will set up the status and other response parameters.
499
+ # TODO: we will want to properly support 1xx responses, which some versions
500
+ # of IIS copiously generate.
501
+ # TODO: We need to give the option of not aborting the connection with certain
502
+ # non-200 responses, in order to work with NTLM and other authentication
503
+ # schemes that work at the level of individual connections.
504
+ #
505
+ # Some error responses will get sugarings. For example, we'll return the
506
+ # Location header in the response in case of a 301/302 response.
507
+ #
508
+ # Possible dispositions here:
509
+ # 1) No content to read (either content-length is zero or it's a HEAD request);
510
+ # 2) Switch to text mode to read a specific number of bytes;
511
+ # 3) Read a chunked or multipart response;
512
+ # 4) Read till the server closes the connection.
513
+ #
514
+ # Our reponse to the client can be either to wait till all the content
515
+ # has been read and then to signal caller's deferrable, or else to signal
516
+ # it when we finish the processing the headers and then expect the caller
517
+ # to have given us a block to call as the content comes in. And of course
518
+ # the latter gets stickier with chunks and multiparts.
519
+ #
520
+ HttpResponseRE = /\AHTTP\/(1.[01]) ([\d]{3})/i
521
+ ClenRE = /\AContent-length:\s*(\d+)/i
522
+ def process_received_headers
523
+ abort_connection unless @current_response.headers.first =~ HttpResponseRE
524
+ @current_response.version = $1.dup
525
+ st = $2.dup
526
+ @current_response.status = st.to_i
527
+ abort_connection unless st[0,1] == "2"
528
+
529
+ clen = nil
530
+ @current_response.headers.each do |e|
531
+ if clen == nil and e =~ ClenRE
532
+ clen = $1.dup.to_i
533
+ end
534
+ end
535
+
536
+ if clen
537
+ set_text_mode clen
538
+ end
539
+ end
540
+ private :process_received_headers
541
+
542
+
543
+ def receive_binary_data text
544
+ @current_response.content = text
545
+ @current_response.deferrable.succeed @current_response
546
+ @requests.pop
547
+ @current_response = (@requests.last || []).last
548
+ set_line_mode
549
+ end
550
+
551
+
552
+
553
+ # We've received either a server error or an internal error.
554
+ # Close the connection and abort any pending requests.
555
+ #--
556
+ # When should we call close_connection? It will cause #unbind
557
+ # to be fired. Should the user expect to see #unbind before
558
+ # we call #receive_http_error, or the other way around?
559
+ #
560
+ # Set instance variable @closed. That's used to inhibit further
561
+ # processing of any inbound data after an error has been recognized.
562
+ #
563
+ # We shouldn't have to worry about any leftover outbound data,
564
+ # because we call close_connection (not close_connection_after_writing).
565
+ # That ensures that any pipelined requests received after an error
566
+ # DO NOT get streamed out to the server on this connection.
567
+ # Very important. TODO, write a unit-test to establish that behavior.
568
+ #
569
+ def abort_connection
570
+ close_connection
571
+ @closed = true
572
+ @current_response.deferrable.fail( @current_response )
573
+ end
574
+
575
+
576
+ #------------------------
577
+ # Below here are user-overridable methods.
578
+
579
+ end
580
+ =end
581
+ end
582
+ end