eventmachine 1.0.3-x86-mingw32 → 1.2.0.dev.2-x86-mingw32

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 (101) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +84 -1
  3. data/README.md +6 -7
  4. data/ext/binder.cpp +10 -10
  5. data/ext/binder.h +5 -5
  6. data/ext/cmain.cpp +173 -61
  7. data/ext/ed.cpp +262 -127
  8. data/ext/ed.h +50 -30
  9. data/ext/em.cpp +491 -445
  10. data/ext/em.h +101 -36
  11. data/ext/eventmachine.h +67 -51
  12. data/ext/extconf.rb +124 -31
  13. data/ext/fastfilereader/extconf.rb +9 -2
  14. data/ext/fastfilereader/mapper.cpp +3 -1
  15. data/ext/fastfilereader/rubymain.cpp +7 -7
  16. data/ext/kb.cpp +1 -1
  17. data/ext/pipe.cpp +11 -4
  18. data/ext/project.h +26 -6
  19. data/ext/rubymain.cpp +408 -201
  20. data/ext/ssl.cpp +167 -20
  21. data/ext/ssl.h +11 -2
  22. data/java/src/com/rubyeventmachine/EmReactor.java +16 -0
  23. data/java/src/com/rubyeventmachine/EventableChannel.java +2 -0
  24. data/java/src/com/rubyeventmachine/EventableDatagramChannel.java +6 -0
  25. data/java/src/com/rubyeventmachine/EventableSocketChannel.java +55 -10
  26. data/lib/1.9/fastfilereaderext.so +0 -0
  27. data/lib/1.9/rubyeventmachine.so +0 -0
  28. data/lib/2.0/fastfilereaderext.so +0 -0
  29. data/lib/2.0/rubyeventmachine.so +0 -0
  30. data/lib/2.1/fastfilereaderext.so +0 -0
  31. data/lib/2.1/rubyeventmachine.so +0 -0
  32. data/lib/2.2/fastfilereaderext.so +0 -0
  33. data/lib/2.2/rubyeventmachine.so +0 -0
  34. data/lib/2.3/fastfilereaderext.so +0 -0
  35. data/lib/2.3/rubyeventmachine.so +0 -0
  36. data/lib/em/buftok.rb +34 -85
  37. data/lib/em/channel.rb +5 -0
  38. data/lib/em/completion.rb +2 -2
  39. data/lib/em/connection.rb +62 -4
  40. data/lib/em/iterator.rb +30 -48
  41. data/lib/em/pool.rb +1 -1
  42. data/lib/em/protocols/httpclient.rb +31 -11
  43. data/lib/em/protocols/line_and_text.rb +4 -4
  44. data/lib/em/protocols/linetext2.rb +44 -39
  45. data/lib/em/protocols/smtpclient.rb +60 -31
  46. data/lib/em/protocols/smtpserver.rb +32 -9
  47. data/lib/em/pure_ruby.rb +8 -3
  48. data/lib/em/queue.rb +16 -7
  49. data/lib/em/resolver.rb +64 -24
  50. data/lib/em/threaded_resource.rb +2 -2
  51. data/lib/em/tick_loop.rb +19 -19
  52. data/lib/em/version.rb +1 -1
  53. data/lib/eventmachine.rb +96 -49
  54. data/lib/jeventmachine.rb +17 -0
  55. data/rakelib/package.rake +31 -4
  56. data/tests/dhparam.pem +13 -0
  57. data/tests/em_test_helper.rb +87 -0
  58. data/tests/test_attach.rb +25 -0
  59. data/tests/test_basic.rb +27 -38
  60. data/tests/test_channel.rb +14 -1
  61. data/tests/test_completion.rb +1 -0
  62. data/tests/test_connection_count.rb +22 -1
  63. data/tests/test_connection_write.rb +35 -0
  64. data/tests/test_defer.rb +17 -0
  65. data/tests/test_epoll.rb +26 -14
  66. data/tests/test_file_watch.rb +1 -0
  67. data/tests/test_fork.rb +75 -0
  68. data/tests/test_httpclient.rb +43 -0
  69. data/tests/test_idle_connection.rb +6 -4
  70. data/tests/test_ipv4.rb +125 -0
  71. data/tests/test_ipv6.rb +131 -0
  72. data/tests/test_iterator.rb +115 -0
  73. data/tests/test_kb.rb +19 -25
  74. data/tests/test_ltp2.rb +20 -0
  75. data/tests/test_many_fds.rb +22 -0
  76. data/tests/test_pause.rb +29 -0
  77. data/tests/test_pool.rb +2 -0
  78. data/tests/test_process_watch.rb +2 -0
  79. data/tests/test_processes.rb +7 -7
  80. data/tests/test_queue.rb +14 -0
  81. data/tests/test_resolver.rb +56 -7
  82. data/tests/test_set_sock_opt.rb +2 -0
  83. data/tests/test_smtpclient.rb +20 -0
  84. data/tests/test_ssl_args.rb +2 -2
  85. data/tests/test_ssl_dhparam.rb +83 -0
  86. data/tests/test_ssl_ecdh_curve.rb +79 -0
  87. data/tests/test_ssl_extensions.rb +49 -0
  88. data/tests/test_ssl_methods.rb +22 -5
  89. data/tests/test_ssl_protocols.rb +246 -0
  90. data/tests/test_ssl_verify.rb +103 -59
  91. data/tests/test_system.rb +4 -0
  92. data/tests/test_threaded_resource.rb +8 -0
  93. data/tests/test_unbind_reason.rb +5 -1
  94. metadata +173 -107
  95. data/.gitignore +0 -21
  96. data/.travis.yml +0 -12
  97. data/.yardopts +0 -7
  98. data/Gemfile +0 -2
  99. data/Rakefile +0 -20
  100. data/eventmachine.gemspec +0 -36
  101. data/rakelib/cpp.rake_example +0 -77
@@ -1,110 +1,59 @@
1
1
  # BufferedTokenizer takes a delimiter upon instantiation, or acts line-based
2
2
  # by default. It allows input to be spoon-fed from some outside source which
3
3
  # receives arbitrary length datagrams which may-or-may-not contain the token
4
- # by which entities are delimited.
5
- #
6
- # By default, new BufferedTokenizers will operate on lines delimited by "\n" by default
7
- # or allow you to specify any delimiter token you so choose, which will then
8
- # be used by String#split to tokenize the input data
9
- #
10
- # @example Using BufferedTokernizer to parse lines out of incoming data
11
- #
12
- # module LineBufferedConnection
13
- # def receive_data(data)
14
- # (@buffer ||= BufferedTokenizer.new).extract(data).each do |line|
15
- # receive_line(line)
16
- # end
17
- # end
18
- # end
19
- #
20
- # @author Tony Arcieri
21
- # @author Martin Emde
4
+ # by which entities are delimited. In this respect it's ideally paired with
5
+ # something like EventMachine (http://rubyeventmachine.com/).
22
6
  class BufferedTokenizer
23
- # @param [String] delimiter
24
- # @param [Integer] size_limit
25
- def initialize(delimiter = "\n", size_limit = nil)
26
- @delimiter = delimiter
27
- @size_limit = size_limit
28
-
29
- # The input buffer is stored as an array. This is by far the most efficient
30
- # approach given language constraints (in C a linked list would be a more
31
- # appropriate data structure). Segments of input data are stored in a list
32
- # which is only joined when a token is reached, substantially reducing the
33
- # number of objects required for the operation.
7
+ # New BufferedTokenizers will operate on lines delimited by a delimiter,
8
+ # which is by default the global input delimiter $/ ("\n").
9
+ #
10
+ # The input buffer is stored as an array. This is by far the most efficient
11
+ # approach given language constraints (in C a linked list would be a more
12
+ # appropriate data structure). Segments of input data are stored in a list
13
+ # which is only joined when a token is reached, substantially reducing the
14
+ # number of objects required for the operation.
15
+ def initialize(delimiter = $/)
16
+ @delimiter = delimiter
34
17
  @input = []
35
-
36
- # Size of the input buffer
37
- @input_size = 0
18
+ @tail = ''
19
+ @trim = @delimiter.length - 1
38
20
  end
39
21
 
40
22
  # Extract takes an arbitrary string of input data and returns an array of
41
- # tokenized entities, provided there were any available to extract.
42
- #
43
- # @example
23
+ # tokenized entities, provided there were any available to extract. This
24
+ # makes for easy processing of datagrams using a pattern like:
44
25
  #
45
- # tokenizer.extract(data).
46
- # map { |entity| Decode(entity) }.each { ... }
26
+ # tokenizer.extract(data).map { |entity| Decode(entity) }.each do ...
47
27
  #
48
- # @param [String] data
28
+ # Using -1 makes split to return "" if the token is at the end of
29
+ # the string, meaning the last element is the start of the next chunk.
49
30
  def extract(data)
50
- # Extract token-delimited entities from the input string with the split command.
51
- # There's a bit of craftiness here with the -1 parameter. Normally split would
52
- # behave no differently regardless of if the token lies at the very end of the
53
- # input buffer or not (i.e. a literal edge case) Specifying -1 forces split to
54
- # return "" in this case, meaning that the last entry in the list represents a
55
- # new segment of data where the token has not been encountered
56
- entities = data.split @delimiter, -1
57
-
58
- # Check to see if the buffer has exceeded capacity, if we're imposing a limit
59
- if @size_limit
60
- raise 'input buffer full' if @input_size + entities.first.size > @size_limit
61
- @input_size += entities.first.size
31
+ if @trim > 0
32
+ tail_end = @tail.slice!(-@trim, @trim) # returns nil if string is too short
33
+ data = tail_end + data if tail_end
62
34
  end
63
35
 
64
- # Move the first entry in the resulting array into the input buffer. It represents
65
- # the last segment of a token-delimited entity unless it's the only entry in the list.
66
- @input << entities.shift
67
-
68
- # If the resulting array from the split is empty, the token was not encountered
69
- # (not even at the end of the buffer). Since we've encountered no token-delimited
70
- # entities this go-around, return an empty array.
71
- return [] if entities.empty?
72
-
73
- # At this point, we've hit a token, or potentially multiple tokens. Now we can bring
74
- # together all the data we've buffered from earlier calls without hitting a token,
75
- # and add it to our list of discovered entities.
76
- entities.unshift @input.join
36
+ @input << @tail
37
+ entities = data.split(@delimiter, -1)
38
+ @tail = entities.shift
77
39
 
78
- # Now that we've hit a token, joined the input buffer and added it to the entities
79
- # list, we can go ahead and clear the input buffer. All of the segments that were
80
- # stored before the join can now be garbage collected.
81
- @input.clear
82
-
83
- # The last entity in the list is not token delimited, however, thanks to the -1
84
- # passed to split. It represents the beginning of a new list of as-yet-untokenized
85
- # data, so we add it to the start of the list.
86
- @input << entities.pop
87
-
88
- # Set the new input buffer size, provided we're keeping track
89
- @input_size = @input.first.size if @size_limit
40
+ unless entities.empty?
41
+ @input << @tail
42
+ entities.unshift @input.join
43
+ @input.clear
44
+ @tail = entities.pop
45
+ end
90
46
 
91
- # Now we're left with the list of extracted token-delimited entities we wanted
92
- # in the first place. Hooray!
93
47
  entities
94
48
  end
95
49
 
96
50
  # Flush the contents of the input buffer, i.e. return the input buffer even though
97
- # a token has not yet been encountered.
98
- #
99
- # @return [String]
51
+ # a token has not yet been encountered
100
52
  def flush
53
+ @input << @tail
101
54
  buffer = @input.join
102
55
  @input.clear
56
+ @tail = "" # @tail.clear is slightly faster, but not supported on 1.8.7
103
57
  buffer
104
58
  end
105
-
106
- # @return [Boolean]
107
- def empty?
108
- @input.empty?
109
- end
110
59
  end
@@ -17,6 +17,11 @@ module EventMachine
17
17
  @uid = 0
18
18
  end
19
19
 
20
+ # Return the number of current subscribers.
21
+ def num_subscribers
22
+ return @subs.size
23
+ end
24
+
20
25
  # Takes any arguments suitable for EM::Callback() and returns a subscriber
21
26
  # id for use when unsubscribing.
22
27
  #
@@ -1,7 +1,7 @@
1
1
  # = EM::Completion
2
2
  #
3
3
  # A completion is a callback container for various states of completion. In
4
- # it's most basic form it has a start state and a finish state.
4
+ # its most basic form it has a start state and a finish state.
5
5
  #
6
6
  # This implementation includes some hold-back from the EM::Deferrable
7
7
  # interface in order to be compatible - but it has a much cleaner
@@ -50,7 +50,7 @@
50
50
  # @completion.fail :unknown, line
51
51
  # end
52
52
  # end
53
- #
53
+ #
54
54
  # def unbind
55
55
  # @completion.fail :disconnected unless @completion.completed?
56
56
  # end
@@ -376,10 +376,21 @@ module EventMachine
376
376
  #
377
377
  # @option args [String] :private_key_file (nil) local path of a readable file that must contain a private key in the [PEM format](http://en.wikipedia.org/wiki/Privacy_Enhanced_Mail).
378
378
  #
379
- # @option args [String] :verify_peer (false) indicates whether a server should request a certificate from a peer, to be verified by user code.
379
+ # @option args [Boolean] :verify_peer (false) indicates whether a server should request a certificate from a peer, to be verified by user code.
380
380
  # If true, the {#ssl_verify_peer} callback on the {EventMachine::Connection} object is called with each certificate
381
381
  # in the certificate chain provided by the peer. See documentation on {#ssl_verify_peer} for how to use this.
382
382
  #
383
+ # @option args [Boolean] :fail_if_no_peer_cert (false) Used in conjunction with verify_peer. If set the SSL handshake will be terminated if the peer does not provide a certificate.
384
+ #
385
+ #
386
+ # @option args [String] :cipher_list ("ALL:!ADH:!LOW:!EXP:!DES-CBC3-SHA:@STRENGTH") indicates the available SSL cipher values. Default value is "ALL:!ADH:!LOW:!EXP:!DES-CBC3-SHA:@STRENGTH". Check the format of the OpenSSL cipher string at http://www.openssl.org/docs/apps/ciphers.html#CIPHER_LIST_FORMAT.
387
+ #
388
+ # @option args [String] :ecdh_curve (nil) The curve for ECDHE ciphers. See available ciphers with 'openssl ecparam -list_curves'
389
+ #
390
+ # @option args [String] :dhparam (nil) The local path of a file containing DH parameters for EDH ciphers in [PEM format](http://en.wikipedia.org/wiki/Privacy_Enhanced_Mail) See: 'openssl dhparam'
391
+ #
392
+ # @option args [Array] :ssl_version (TLSv1 TLSv1_1 TLSv1_2) indicates the allowed SSL/TLS versions. Possible values are: {SSLv2}, {SSLv3}, {TLSv1}, {TLSv1_1}, {TLSv1_2}.
393
+ #
383
394
  # @example Using TLS with EventMachine
384
395
  #
385
396
  # require 'rubygems'
@@ -404,15 +415,47 @@ module EventMachine
404
415
  #
405
416
  # @see #ssl_verify_peer
406
417
  def start_tls args={}
407
- priv_key, cert_chain, verify_peer = args.values_at(:private_key_file, :cert_chain_file, :verify_peer)
418
+ priv_key = args[:private_key_file]
419
+ cert_chain = args[:cert_chain_file]
420
+ verify_peer = args[:verify_peer]
421
+ sni_hostname = args[:sni_hostname]
422
+ cipher_list = args[:cipher_list]
423
+ ssl_version = args[:ssl_version]
424
+ ecdh_curve = args[:ecdh_curve]
425
+ dhparam = args[:dhparam]
426
+ fail_if_no_peer_cert = args[:fail_if_no_peer_cert]
408
427
 
409
428
  [priv_key, cert_chain].each do |file|
410
429
  next if file.nil? or file.empty?
411
430
  raise FileNotFoundException,
412
- "Could not find #{file} for start_tls" unless File.exists? file
431
+ "Could not find #{file} for start_tls" unless File.exist? file
432
+ end
433
+
434
+ protocols_bitmask = 0
435
+ if ssl_version.nil?
436
+ protocols_bitmask |= EventMachine::EM_PROTO_TLSv1
437
+ protocols_bitmask |= EventMachine::EM_PROTO_TLSv1_1
438
+ protocols_bitmask |= EventMachine::EM_PROTO_TLSv1_2
439
+ else
440
+ [ssl_version].flatten.each do |p|
441
+ case p.to_s.downcase
442
+ when 'sslv2'
443
+ protocols_bitmask |= EventMachine::EM_PROTO_SSLv2
444
+ when 'sslv3'
445
+ protocols_bitmask |= EventMachine::EM_PROTO_SSLv3
446
+ when 'tlsv1'
447
+ protocols_bitmask |= EventMachine::EM_PROTO_TLSv1
448
+ when 'tlsv1_1'
449
+ protocols_bitmask |= EventMachine::EM_PROTO_TLSv1_1
450
+ when 'tlsv1_2'
451
+ protocols_bitmask |= EventMachine::EM_PROTO_TLSv1_2
452
+ else
453
+ raise("Unrecognized SSL/TLS Protocol: #{p}")
454
+ end
455
+ end
413
456
  end
414
457
 
415
- EventMachine::set_tls_parms(@signature, priv_key || '', cert_chain || '', verify_peer)
458
+ EventMachine::set_tls_parms(@signature, priv_key || '', cert_chain || '', verify_peer, fail_if_no_peer_cert, sni_hostname || '', cipher_list || '', ecdh_curve || '', dhparam || '', protocols_bitmask)
416
459
  EventMachine::start_tls @signature
417
460
  end
418
461
 
@@ -488,6 +531,21 @@ module EventMachine
488
531
  EventMachine::get_peer_cert @signature
489
532
  end
490
533
 
534
+ def get_cipher_bits
535
+ EventMachine::get_cipher_bits @signature
536
+ end
537
+
538
+ def get_cipher_name
539
+ EventMachine::get_cipher_name @signature
540
+ end
541
+
542
+ def get_cipher_protocol
543
+ EventMachine::get_cipher_protocol @signature
544
+ end
545
+
546
+ def get_sni_hostname
547
+ EventMachine::get_sni_hostname @signature
548
+ end
491
549
 
492
550
  # Sends UDP messages.
493
551
  #
@@ -41,6 +41,7 @@ module EventMachine
41
41
  # end
42
42
  #
43
43
  class Iterator
44
+ Stop = "EM::Stop"
44
45
  # Create a new parallel async iterator with specified concurrency.
45
46
  #
46
47
  # i = EM::Iterator.new(1..100, 10)
@@ -48,9 +49,21 @@ module EventMachine
48
49
  # will create an iterator over the range that processes 10 items at a time. Iteration
49
50
  # is started via #each, #map or #inject
50
51
  #
52
+ # The list may either be an array-like object, or a proc that returns a new object
53
+ # to be processed each time it is called. If a proc is used, it must return
54
+ # EventMachine::Iterator::Stop to signal the end of the iterations.
55
+ #
51
56
  def initialize(list, concurrency = 1)
52
- raise ArgumentError, 'argument must be an array' unless list.respond_to?(:to_a)
53
- @list = list.to_a.dup
57
+ raise ArgumentError, 'concurrency must be bigger than zero' unless (concurrency > 0)
58
+ if list.respond_to?(:call)
59
+ @list = nil
60
+ @list_proc = list
61
+ elsif list.respond_to?(:to_a)
62
+ @list = list.to_a.dup
63
+ @list_proc = nil
64
+ else
65
+ raise ArgumentError, 'argument must be a proc or an array'
66
+ end
54
67
  @concurrency = concurrency
55
68
 
56
69
  @started = false
@@ -97,12 +110,12 @@ module EventMachine
97
110
  @process_next = proc{
98
111
  # p [:process_next, :pending=, @pending, :workers=, @workers, :ended=, @ended, :concurrency=, @concurrency, :list=, @list]
99
112
  unless @ended or @workers > @concurrency
100
- if @list.empty?
113
+ item = next_item()
114
+ if item.equal?(Stop)
101
115
  @ended = true
102
116
  @workers -= 1
103
117
  all_done.call
104
118
  else
105
- item = @list.shift
106
119
  @pending += 1
107
120
 
108
121
  is_done = false
@@ -221,50 +234,19 @@ module EventMachine
221
234
  })
222
235
  nil
223
236
  end
237
+
238
+ # Return the next item from @list or @list_proc.
239
+ # Once items have run out, will return EM::Iterator::Stop. Procs must supply this themselves
240
+ def next_item
241
+ if @list_proc
242
+ @list_proc.call
243
+ else
244
+ @list.empty? ? Stop : @list.shift
245
+ end
246
+ end
224
247
  end
225
248
  end
226
249
 
227
- if __FILE__ == $0
228
- $:.unshift File.join(File.dirname(__FILE__), '..')
229
- require 'eventmachine'
230
-
231
- # TODO: real tests
232
- # TODO: pass in one object instead of two? .each{ |iter| puts iter.current; iter.next }
233
- # TODO: support iter.pause/resume/stop/break/continue?
234
- # TODO: create some exceptions instead of using RuntimeError
235
- # TODO: support proc instead of enumerable? EM::Iterator.new(proc{ return queue.pop })
236
-
237
- EM.run{
238
- EM::Iterator.new(1..50).each{ |num,iter| p num; iter.next }
239
- EM::Iterator.new([1,2,3], 10).each{ |num,iter| p num; iter.next }
240
-
241
- i = EM::Iterator.new(1..100, 5)
242
- i.each(proc{|num,iter|
243
- p num.to_s
244
- iter.next
245
- }, proc{
246
- p :done
247
- })
248
- EM.add_timer(0.03){
249
- i.concurrency = 1
250
- }
251
- EM.add_timer(0.04){
252
- i.concurrency = 3
253
- }
254
-
255
- EM::Iterator.new(100..150).map(proc{ |num,iter|
256
- EM.add_timer(0.01){ iter.return(num) }
257
- }, proc{ |results|
258
- p results
259
- })
260
-
261
- EM::Iterator.new(%w[ pwd uptime uname date ], 2).inject({}, proc{ |hash,cmd,iter|
262
- EM.system(cmd){ |output,status|
263
- hash[cmd] = status.exitstatus == 0 ? output.strip : nil
264
- iter.return(hash)
265
- }
266
- }, proc{ |results|
267
- p results
268
- })
269
- }
270
- end
250
+ # TODO: pass in one object instead of two? .each{ |iter| puts iter.current; iter.next }
251
+ # TODO: support iter.pause/resume/stop/break/continue?
252
+ # TODO: create some exceptions instead of using RuntimeError
@@ -143,7 +143,7 @@ module EventMachine
143
143
  else
144
144
  raise ArgumentError, "deferrable expected from work"
145
145
  end
146
- rescue Exception
146
+ rescue
147
147
  failure resource
148
148
  raise
149
149
  end
@@ -23,8 +23,6 @@
23
23
  #
24
24
  #
25
25
 
26
-
27
-
28
26
  module EventMachine
29
27
  module Protocols
30
28
 
@@ -52,7 +50,6 @@ module EventMachine
52
50
  # DNS: Some way to cache DNS lookups for hostnames we connect to. Ruby's
53
51
  # DNS lookups are unbelievably slow.
54
52
  # HEAD requests.
55
- # Chunked transfer encoding.
56
53
  # Convenience methods for requests. get, post, url, etc.
57
54
  # SSL.
58
55
  # Handle status codes like 304, 100, etc.
@@ -184,6 +181,8 @@ module EventMachine
184
181
  @content_length = nil # not zero
185
182
  @content = ""
186
183
  @status = nil
184
+ @chunked = false
185
+ @chunk_length = nil
187
186
  @read_state = :header
188
187
  @connection_close = nil
189
188
  when :header
@@ -191,7 +190,7 @@ module EventMachine
191
190
  if ary.length == 2
192
191
  data = ary.last
193
192
  if ary.first == ""
194
- if (@content_length and @content_length > 0) || @connection_close
193
+ if (@content_length and @content_length > 0) || @chunked || @connection_close
195
194
  @read_state = :content
196
195
  else
197
196
  dispatch_response
@@ -211,6 +210,8 @@ module EventMachine
211
210
  @content_length ||= $'.to_i
212
211
  elsif ary.first =~ /\Aconnection:\s*close/i
213
212
  @connection_close = true
213
+ elsif ary.first =~ /\Atransfer-encoding:\s*chunked/i
214
+ @chunked = true
214
215
  end
215
216
  end
216
217
  else
@@ -218,12 +219,32 @@ module EventMachine
218
219
  data = ""
219
220
  end
220
221
  when :content
221
- # If there was no content-length header, we have to wait until the connection
222
- # closes. Everything we get until that point is content.
223
- # TODO: Must impose a content-size limit, and also must implement chunking.
224
- # Also, must support either temporary files for large content, or calling
225
- # a content-consumer block supplied by the user.
226
- if @content_length
222
+ if @chunked && @chunk_length
223
+ bytes_needed = @chunk_length - @chunk_read
224
+ new_data = data[0, bytes_needed]
225
+ @chunk_read += new_data.length
226
+ @content += new_data
227
+ data = data[bytes_needed..-1] || ""
228
+ if @chunk_length == @chunk_read && data[0,2] == "\r\n"
229
+ @chunk_length = nil
230
+ data = data[2..-1]
231
+ end
232
+ elsif @chunked
233
+ if (m = data.match(/\A(\S*)\r\n/m))
234
+ data = data[m[0].length..-1]
235
+ @chunk_length = m[1].to_i(16)
236
+ @chunk_read = 0
237
+ if @chunk_length == 0
238
+ dispatch_response
239
+ @read_state = :base
240
+ end
241
+ end
242
+ elsif @content_length
243
+ # If there was no content-length header, we have to wait until the connection
244
+ # closes. Everything we get until that point is content.
245
+ # TODO: Must impose a content-size limit, and also must implement chunking.
246
+ # Also, must support either temporary files for large content, or calling
247
+ # a content-consumer block supplied by the user.
227
248
  bytes_needed = @content_length - @content.length
228
249
  @content += data[0, bytes_needed]
229
250
  data = data[bytes_needed..-1] || ""
@@ -274,6 +295,5 @@ module EventMachine
274
295
  end
275
296
  end
276
297
  end
277
-
278
298
  end
279
299
  end