eventmachine 1.0.5-java → 1.0.6-java

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 909d153b45cd30ecad7ef4f2b91009aa978b70eb
4
- data.tar.gz: 493058213b19e7b4d32c51e72b15a54f2832d1e6
3
+ metadata.gz: 4722f24a61f13ac737b777597b71542a5ea1b0bd
4
+ data.tar.gz: 2bc74d2fe5b8de6e467faeae30916f4cb1a71ddf
5
5
  SHA512:
6
- metadata.gz: 342e305aa3bd8792f115e6c7c1795f3fe482e11d92a1195b7346ff6c2d20c98b1c9462d0a26030d9e1d350682140a79db199c470b4b8fe6cca492b8e7059943d
7
- data.tar.gz: 23c2852e6fcfd419eb5dec0ce005a515b7561cfe75b417d2f93d02817992fc4345b8cc3beff619adea9e7f80656de2180bdc24d5b56a5192293f1a1ebb329a5f
6
+ metadata.gz: f2d16977dff27530edcd046a5503f7597a5f5859576592dc0b8e2f826f9cac227309b0f2fadf87c65348520257ea7f78a34f4f2c892f72876c31181df7b3487b
7
+ data.tar.gz: bdb3a2a7bcf18f803f8e002637b1809a2691e84025bb19ab3e319d82d13e21f39ab35ce1c96a865848e26ba586cc8dbd9be8e880a1bdbdd924498b7df73fdeff
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.0.6 (February 3, 2015)
4
+ * add support for Rubinius Process::Status [#568]
5
+ * small bugfixes for SmtpServer [#449]
6
+ * update buftok.rb [#547]
7
+ * fix assertion on Write() [#525]
8
+ * work around mkmf.rb bug preventing gem installation [#574]
9
+ * add pause/resume support to jruby reactor [#556]
10
+ * fix pure ruby reactor to use 127.0.0.1 instead of localhost [#439]
11
+ * fix compilation under macruby [#243]
12
+ * add chunked encoding to http client [#111]
13
+ * fix errors on win32 when dealing with pipes [1ea45498] [#105]
14
+
3
15
  ## 1.0.5 (February 2, 2015)
4
16
  * use monotonic clocks on Linux, OS X, Solaris, and Windows [#563]
5
17
  * use the rb_fd_* API to get autosized fd_sets [#502]
data/README.md CHANGED
@@ -105,5 +105,4 @@ Copyright: (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
105
105
 
106
106
  ## Alternatives ##
107
107
 
108
- If you are unhappy with EventMachine and want to use Ruby, check out [Cool.io](http://coolio.github.com/).
109
- One caveat: by May 2011, it did not support JRuby and Windows.
108
+ If you are unhappy with EventMachine and want to use Ruby, check out [Celluloid](https://celluloid.io/).
data/ext/em.cpp CHANGED
@@ -938,7 +938,8 @@ void EventMachine_t::_RunSelectOnce()
938
938
  /* 21Sep09: on windows, a non-blocking connect() that fails does not come up as writable.
939
939
  Instead, it is added to the error set. See http://www.mail-archive.com/openssl-users@openssl.org/msg58500.html
940
940
  */
941
- rb_fd_set (sd, &(SelectData.fderrors));
941
+ if (ed->IsConnectPending())
942
+ rb_fd_set (sd, &(SelectData.fderrors));
942
943
  #endif
943
944
 
944
945
  if (SelectData.maxsocket < sd)
@@ -973,8 +974,12 @@ void EventMachine_t::_RunSelectOnce()
973
974
  continue;
974
975
  assert (sd != INVALID_SOCKET);
975
976
 
976
- if (rb_fd_isset (sd, &(SelectData.fdwrites)))
977
- ed->Write();
977
+ if (rb_fd_isset (sd, &(SelectData.fdwrites))) {
978
+ // Double-check SelectForWrite() still returns true. If not, one of the callbacks must have
979
+ // modified some value since we checked SelectForWrite() earlier in this method.
980
+ if (ed->SelectForWrite())
981
+ ed->Write();
982
+ }
978
983
  if (rb_fd_isset (sd, &(SelectData.fdreads)))
979
984
  ed->Read();
980
985
  if (rb_fd_isset (sd, &(SelectData.fderrors)))
@@ -39,6 +39,9 @@ def manual_ssl_config
39
39
  check_libs(libs) and check_heads(heads)
40
40
  end
41
41
 
42
+ # Eager check devs tools
43
+ have_devel? if respond_to?(:have_devel?)
44
+
42
45
  if ENV['CROSS_COMPILING']
43
46
  openssl_version = ENV.fetch("OPENSSL_VERSION", "1.0.1i")
44
47
  openssl_dir = File.expand_path("~/.rake-compiler/builds/openssl-#{openssl_version}/")
@@ -12,6 +12,9 @@ def add_define(name)
12
12
  $defs.push("-D#{name}")
13
13
  end
14
14
 
15
+ # Eager check devs tools
16
+ have_devel? if respond_to?(:have_devel?)
17
+
15
18
  add_define 'BUILD_FOR_RUBY'
16
19
 
17
20
  # Minor platform details between *nix and Windows:
@@ -408,8 +408,22 @@ static VALUE t_get_subprocess_status (VALUE self, VALUE signature)
408
408
  if (evma_get_subprocess_status (NUM2ULONG (signature), &status)) {
409
409
  if (evma_get_subprocess_pid (NUM2ULONG (signature), &pid)) {
410
410
  proc_status = rb_obj_alloc(rb_cProcStatus);
411
+
412
+ /* MRI Ruby uses hidden instance vars */
411
413
  rb_iv_set(proc_status, "status", INT2FIX(status));
412
414
  rb_iv_set(proc_status, "pid", INT2FIX(pid));
415
+
416
+ #ifdef RUBINIUS
417
+ /* Rubinius uses standard instance vars */
418
+ rb_iv_set(proc_status, "@pid", INT2FIX(pid));
419
+ if (WIFEXITED(status)) {
420
+ rb_iv_set(proc_status, "@status", INT2FIX(WEXITSTATUS(status)));
421
+ } else if(WIFSIGNALED(status)) {
422
+ rb_iv_set(proc_status, "@termsig", INT2FIX(WTERMSIG(status)));
423
+ } else if(WIFSTOPPED(status)){
424
+ rb_iv_set(proc_status, "@stopsig", INT2FIX(WSTOPSIG(status)));
425
+ }
426
+ #endif
413
427
  }
414
428
  }
415
429
 
@@ -611,7 +625,7 @@ static VALUE t_set_sock_opt (VALUE self, VALUE signature, VALUE lev, VALUE optna
611
625
  int fd = evma_get_file_descriptor (NUM2ULONG (signature));
612
626
  int level = NUM2INT(lev), option = NUM2INT(optname);
613
627
  int i;
614
- void *v;
628
+ const void *v;
615
629
  socklen_t len;
616
630
 
617
631
  switch (TYPE(optval)) {
@@ -569,6 +569,22 @@ public class EmReactor {
569
569
  return Connections.get(sig).isNotifyWritable();
570
570
  }
571
571
 
572
+ public boolean pauseConnection (long sig) {
573
+ return ((EventableSocketChannel) Connections.get(sig)).pause();
574
+ }
575
+
576
+ public boolean resumeConnection (long sig) {
577
+ return ((EventableSocketChannel) Connections.get(sig)).resume();
578
+ }
579
+
580
+ public boolean isConnectionPaused (long sig) {
581
+ return ((EventableSocketChannel) Connections.get(sig)).isPaused();
582
+ }
583
+
584
+ public long getOutboundDataSize (long sig) {
585
+ return Connections.get(sig).getOutboundDataSize();
586
+ }
587
+
572
588
  public int getConnectionCount() {
573
589
  return Connections.size() + Acceptors.size();
574
590
  }
@@ -57,6 +57,8 @@ public interface EventableChannel {
57
57
 
58
58
  public boolean writeOutboundData() throws IOException;
59
59
 
60
+ public long getOutboundDataSize();
61
+
60
62
  public void setCommInactivityTimeout (long seconds);
61
63
 
62
64
  public Object[] getPeerName();
@@ -54,6 +54,7 @@ public class EventableDatagramChannel implements EventableChannel {
54
54
  Selector selector;
55
55
  boolean bCloseScheduled;
56
56
  LinkedList<Packet> outboundQ;
57
+ long outboundS;
57
58
  SocketAddress returnAddress;
58
59
 
59
60
 
@@ -63,6 +64,7 @@ public class EventableDatagramChannel implements EventableChannel {
63
64
  selector = sel;
64
65
  bCloseScheduled = false;
65
66
  outboundQ = new LinkedList<Packet>();
67
+ outboundS = 0;
66
68
 
67
69
  dc.register(selector, SelectionKey.OP_READ, this);
68
70
  }
@@ -71,6 +73,7 @@ public class EventableDatagramChannel implements EventableChannel {
71
73
  try {
72
74
  if ((!bCloseScheduled) && (bb.remaining() > 0)) {
73
75
  outboundQ.addLast(new Packet(bb, returnAddress));
76
+ outboundS += bb.remaining();
74
77
  channel.register(selector, SelectionKey.OP_WRITE | SelectionKey.OP_READ, this);
75
78
  }
76
79
  } catch (ClosedChannelException e) {
@@ -82,6 +85,7 @@ public class EventableDatagramChannel implements EventableChannel {
82
85
  try {
83
86
  if ((!bCloseScheduled) && (bb.remaining() > 0)) {
84
87
  outboundQ.addLast(new Packet (bb, new InetSocketAddress (recipAddress, recipPort)));
88
+ outboundS += bb.remaining();
85
89
  channel.register(selector, SelectionKey.OP_WRITE | SelectionKey.OP_READ, this);
86
90
  }
87
91
  } catch (ClosedChannelException e) {
@@ -136,6 +140,7 @@ public class EventableDatagramChannel implements EventableChannel {
136
140
  try {
137
141
  // With a datagram socket, it's ok to send an empty buffer.
138
142
  written = channel.send(p.bb, p.recipient);
143
+ outboundS -= written;
139
144
  }
140
145
  catch (IOException e) {
141
146
  return false;
@@ -192,4 +197,5 @@ public class EventableDatagramChannel implements EventableChannel {
192
197
  public boolean isWatchOnly() { return false; }
193
198
  public boolean isNotifyReadable() { return false; }
194
199
  public boolean isNotifyWritable() { return false; }
200
+ public long getOutboundDataSize() { return outboundS; }
195
201
  }
@@ -54,6 +54,7 @@ public class EventableSocketChannel implements EventableChannel {
54
54
 
55
55
  long binding;
56
56
  LinkedList<ByteBuffer> outboundQ;
57
+ long outboundS;
57
58
 
58
59
  boolean bCloseScheduled;
59
60
  boolean bConnectPending;
@@ -61,6 +62,7 @@ public class EventableSocketChannel implements EventableChannel {
61
62
  boolean bAttached;
62
63
  boolean bNotifyReadable;
63
64
  boolean bNotifyWritable;
65
+ boolean bPaused;
64
66
 
65
67
  SSLEngine sslEngine;
66
68
  SSLContext sslContext;
@@ -76,6 +78,7 @@ public class EventableSocketChannel implements EventableChannel {
76
78
  bNotifyReadable = false;
77
79
  bNotifyWritable = false;
78
80
  outboundQ = new LinkedList<ByteBuffer>();
81
+ outboundS = 0;
79
82
  }
80
83
 
81
84
  public long getBinding() {
@@ -164,12 +167,14 @@ public class EventableSocketChannel implements EventableChannel {
164
167
  sslEngine.wrap(bb, b);
165
168
  b.flip();
166
169
  outboundQ.addLast(b);
170
+ outboundS += b.remaining();
167
171
  } catch (SSLException e) {
168
172
  throw new RuntimeException ("ssl error");
169
173
  }
170
174
  }
171
175
  else {
172
176
  outboundQ.addLast(bb);
177
+ outboundS += bb.remaining();
173
178
  }
174
179
 
175
180
  updateEvents();
@@ -188,6 +193,8 @@ public class EventableSocketChannel implements EventableChannel {
188
193
  throw new IOException ("eof");
189
194
  }
190
195
 
196
+ public long getOutboundDataSize() { return outboundS; }
197
+
191
198
  /**
192
199
  * Called by the reactor when we have selected writable.
193
200
  * Return false to indicate an error that should cause the connection to close.
@@ -196,23 +203,35 @@ public class EventableSocketChannel implements EventableChannel {
196
203
  * this code is written, we're depending on a nonblocking write NOT TO CONSUME
197
204
  * the whole outbound buffer in this case, rather than firing an exception.
198
205
  * We should somehow verify that this is indeed Java's defined behavior.
199
- * Also TODO, see if we can use gather I/O rather than one write at a time.
200
- * Ought to be a big performance enhancer.
201
206
  * @return
202
207
  */
203
208
  public boolean writeOutboundData() throws IOException {
209
+ ByteBuffer[] bufs = new ByteBuffer[64];
210
+ int i;
211
+ long written, toWrite;
204
212
  while (!outboundQ.isEmpty()) {
205
- ByteBuffer b = outboundQ.getFirst();
206
- if (b.remaining() > 0)
207
- channel.write(b);
213
+ i = 0;
214
+ toWrite = 0;
215
+ written = 0;
216
+ while (i < 64 && !outboundQ.isEmpty()) {
217
+ bufs[i] = outboundQ.removeFirst();
218
+ toWrite += bufs[i].remaining();
219
+ i++;
220
+ }
221
+ if (toWrite > 0)
222
+ written = channel.write(bufs, 0, i);
208
223
 
224
+ outboundS -= written;
209
225
  // Did we consume the whole outbound buffer? If yes,
210
226
  // pop it off and keep looping. If no, the outbound network
211
227
  // buffers are full, so break out of here.
212
- if (b.remaining() == 0)
213
- outboundQ.removeFirst();
214
- else
228
+ if (written < toWrite) {
229
+ while (i > 0 && bufs[i-1].remaining() > 0) {
230
+ outboundQ.addFirst(bufs[i-1]);
231
+ i--;
232
+ }
215
233
  break;
234
+ }
216
235
  }
217
236
 
218
237
  if (outboundQ.isEmpty() && !bCloseScheduled) {
@@ -244,8 +263,10 @@ public class EventableSocketChannel implements EventableChannel {
244
263
 
245
264
  public boolean scheduleClose (boolean afterWriting) {
246
265
  // TODO: What the hell happens here if bConnectPending is set?
247
- if (!afterWriting)
266
+ if (!afterWriting) {
248
267
  outboundQ.clear();
268
+ outboundS = 0;
269
+ }
249
270
 
250
271
  if (outboundQ.isEmpty())
251
272
  return true;
@@ -331,6 +352,30 @@ public class EventableSocketChannel implements EventableChannel {
331
352
  }
332
353
  public boolean isNotifyWritable() { return bNotifyWritable; }
333
354
 
355
+ public boolean pause() {
356
+ if (bWatchOnly) {
357
+ throw new RuntimeException ("cannot pause/resume 'watch only' connections, set notify readable/writable instead");
358
+ }
359
+ boolean old = bPaused;
360
+ bPaused = true;
361
+ updateEvents();
362
+ return !old;
363
+ }
364
+
365
+ public boolean resume() {
366
+ if (bWatchOnly) {
367
+ throw new RuntimeException ("cannot pause/resume 'watch only' connections, set notify readable/writable instead");
368
+ }
369
+ boolean old = bPaused;
370
+ bPaused = false;
371
+ updateEvents();
372
+ return old;
373
+ }
374
+
375
+ public boolean isPaused() {
376
+ return bPaused;
377
+ }
378
+
334
379
  private void updateEvents() {
335
380
  if (channelKey == null)
336
381
  return;
@@ -353,7 +398,7 @@ public class EventableSocketChannel implements EventableChannel {
353
398
  if (bNotifyWritable)
354
399
  events |= SelectionKey.OP_WRITE;
355
400
  }
356
- else
401
+ else if (!bPaused)
357
402
  {
358
403
  if (bConnectPending)
359
404
  events |= SelectionKey.OP_CONNECT;
@@ -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
@@ -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.
@@ -191,7 +188,7 @@ module EventMachine
191
188
  if ary.length == 2
192
189
  data = ary.last
193
190
  if ary.first == ""
194
- if (@content_length and @content_length > 0) || @connection_close
191
+ if (@content_length and @content_length > 0) || @chunked || @connection_close
195
192
  @read_state = :content
196
193
  else
197
194
  dispatch_response
@@ -211,6 +208,8 @@ module EventMachine
211
208
  @content_length ||= $'.to_i
212
209
  elsif ary.first =~ /\Aconnection:\s*close/i
213
210
  @connection_close = true
211
+ elsif ary.first =~ /\Atransfer-encoding:\s*chunked/i
212
+ @chunked = true
214
213
  end
215
214
  end
216
215
  else
@@ -218,12 +217,32 @@ module EventMachine
218
217
  data = ""
219
218
  end
220
219
  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
220
+ if @chunked && @chunk_length
221
+ bytes_needed = @chunk_length - @chunk_read
222
+ new_data = data[0, bytes_needed]
223
+ @chunk_read += new_data.length
224
+ @content += new_data
225
+ data = data[bytes_needed..-1] || ""
226
+ if @chunk_length == @chunk_read && data[0,2] == "\r\n"
227
+ @chunk_length = nil
228
+ data = data[2..-1]
229
+ end
230
+ elsif @chunked
231
+ if (m = data.match(/\A(\S*)\r\n/m))
232
+ data = data[m[0].length..-1]
233
+ @chunk_length = m[1].to_i(16)
234
+ @chunk_read = 0
235
+ if @chunk_length == 0
236
+ dispatch_response
237
+ @read_state = :base
238
+ end
239
+ end
240
+ elsif @content_length
241
+ # If there was no content-length header, we have to wait until the connection
242
+ # closes. Everything we get until that point is content.
243
+ # TODO: Must impose a content-size limit, and also must implement chunking.
244
+ # Also, must support either temporary files for large content, or calling
245
+ # a content-consumer block supplied by the user.
227
246
  bytes_needed = @content_length - @content.length
228
247
  @content += data[0, bytes_needed]
229
248
  data = data[bytes_needed..-1] || ""
@@ -274,6 +293,5 @@ module EventMachine
274
293
  end
275
294
  end
276
295
  end
277
-
278
296
  end
279
297
  end
@@ -32,7 +32,6 @@ module EventMachine
32
32
  # for a version which is optimized for correctness with regard to binary text blocks
33
33
  # that can switch back to line mode.
34
34
  class LineAndTextProtocol < Connection
35
- MaxLineLength = 16*1024
36
35
  MaxBinaryLength = 32*1024*1024
37
36
 
38
37
  def initialize *args
@@ -42,7 +41,7 @@ module EventMachine
42
41
  def receive_data data
43
42
  if @lbp_mode == :lines
44
43
  begin
45
- @lpb_buffer.extract(data).each do |line|
44
+ @lpb_buffer.extract(data).each do |line|
46
45
  receive_line(line.chomp) if respond_to?(:receive_line)
47
46
  end
48
47
  rescue Exception
@@ -116,7 +115,7 @@ module EventMachine
116
115
  #--
117
116
  # For internal use, establish protocol baseline for handling lines.
118
117
  def lbp_init_line_state
119
- @lpb_buffer = BufferedTokenizer.new("\n", MaxLineLength)
118
+ @lpb_buffer = BufferedTokenizer.new("\n")
120
119
  @lbp_mode = :lines
121
120
  end
122
121
  private :lbp_init_line_state
@@ -37,7 +37,6 @@ module EventMachine
37
37
  # When we get around to that, call #receive_error if the user defined it, otherwise
38
38
  # throw exceptions.
39
39
 
40
- MaxLineLength = 16*1024
41
40
  MaxBinaryLength = 32*1024*1024
42
41
 
43
42
  #--
@@ -227,18 +227,26 @@ module EventMachine
227
227
  process_unknown
228
228
  end
229
229
  end
230
-
230
+
231
231
  # TODO - implement this properly, the implementation is a stub!
232
- def process_vrfy
232
+ def process_help
233
233
  send_data "250 Ok, but unimplemented\r\n"
234
234
  end
235
+
236
+ # RFC2821, 3.5.3 Meaning of VRFY or EXPN Success Response:
237
+ # A server MUST NOT return a 250 code in response to a VRFY or EXPN
238
+ # command unless it has actually verified the address. In particular,
239
+ # a server MUST NOT return 250 if all it has done is to verify that the
240
+ # syntax given is valid. In that case, 502 (Command not implemented)
241
+ # or 500 (Syntax error, command unrecognized) SHOULD be returned.
242
+ #
235
243
  # TODO - implement this properly, the implementation is a stub!
236
- def process_help
237
- send_data "250 Ok, but unimplemented\r\n"
244
+ def process_vrfy
245
+ send_data "502 Command not implemented\r\n"
238
246
  end
239
247
  # TODO - implement this properly, the implementation is a stub!
240
248
  def process_expn
241
- send_data "250 Ok, but unimplemented\r\n"
249
+ send_data "502 Command not implemented\r\n"
242
250
  end
243
251
 
244
252
  #--
@@ -358,12 +366,23 @@ module EventMachine
358
366
  def process_auth_line(line)
359
367
  plain = line.unpack("m").first
360
368
  _,user,psw = plain.split("\000")
361
- if receive_plain_auth user,psw
369
+
370
+ succeeded = proc {
362
371
  send_data "235 authentication ok\r\n"
363
372
  @state << :auth
364
- else
373
+ }
374
+ failed = proc {
365
375
  send_data "535 invalid authentication\r\n"
376
+ }
377
+ auth = receive_plain_auth user,psw
378
+
379
+ if auth.respond_to?(:callback)
380
+ auth.callback(&succeeded)
381
+ auth.errback(&failed)
382
+ else
383
+ (auth ? succeeded : failed).call
366
384
  end
385
+
367
386
  @state.delete :auth_incomplete
368
387
  end
369
388
 
@@ -393,7 +393,7 @@ module EventMachine
393
393
  100.times {
394
394
  @loopbreak_port = rand(10000) + 40000
395
395
  begin
396
- @loopbreak_reader.bind "localhost", @loopbreak_port
396
+ @loopbreak_reader.bind "127.0.0.1", @loopbreak_port
397
397
  bound = true
398
398
  break
399
399
  rescue
@@ -410,7 +410,7 @@ module EventMachine
410
410
 
411
411
  def signal_loopbreak
412
412
  #@loopbreak_writer.write '+' if @loopbreak_writer
413
- @loopbreak_writer.send('+',0,"localhost",@loopbreak_port) if @loopbreak_writer
413
+ @loopbreak_writer.send('+',0,"127.0.0.1",@loopbreak_port) if @loopbreak_writer
414
414
  end
415
415
 
416
416
  def set_timer_quantum interval_in_seconds
@@ -1,3 +1,3 @@
1
1
  module EventMachine
2
- VERSION = "1.0.5"
2
+ VERSION = "1.0.6"
3
3
  end
@@ -268,6 +268,20 @@ module EventMachine
268
268
  @em.getConnectionCount
269
269
  end
270
270
 
271
+ def self.pause_connection(sig)
272
+ @em.pauseConnection(sig)
273
+ end
274
+ def self.resume_connection(sig)
275
+ @em.resumeConnection(sig)
276
+ end
277
+ def self.connection_paused?(sig)
278
+ @em.isConnectionPaused(sig)
279
+ end
280
+ def self._get_outbound_data_size(sig)
281
+ @em.getOutboundDataSize(sig)
282
+ end
283
+
284
+
271
285
  def self.set_tls_parms(sig, params)
272
286
  end
273
287
  def self.start_tls(sig)
@@ -279,6 +293,9 @@ module EventMachine
279
293
  def associate_callback_target sig
280
294
  # No-op for the time being
281
295
  end
296
+ def get_outbound_data_size
297
+ EM._get_outbound_data_size @signature
298
+ end
282
299
  end
283
300
  end
284
301
 
@@ -45,6 +45,10 @@ class Test::Unit::TestCase
45
45
  def jruby?
46
46
  defined? JRUBY_VERSION
47
47
  end
48
+
49
+ def rbx?
50
+ defined?(RUBY_ENGINE) && RUBY_ENGINE == 'rbx'
51
+ end
48
52
  end
49
53
 
50
54
  include PlatformHelper
@@ -246,6 +246,7 @@ class TestBasic < Test::Unit::TestCase
246
246
 
247
247
  def test_fork_safe
248
248
  omit_if(jruby?)
249
+ omit_if(rbx?, 'Omitting test on Rubinius because it hangs for unknown reasons')
249
250
 
250
251
  read, write = IO.pipe
251
252
  EM.run do
@@ -0,0 +1,35 @@
1
+ require 'em_test_helper'
2
+
3
+ class TestConnectionWrite < Test::Unit::TestCase
4
+
5
+ # This test takes advantage of the fact that EM::_RunSelectOnce iterates over the connections twice:
6
+ # - once to determine which ones to call Write() on
7
+ # - and once to call Write() on each of them.
8
+ #
9
+ # But state may change in the meantime before Write() is finally called.
10
+ # And that is what we try to exploit to get Write() to be called when bWatchOnly is true, and bNotifyWritable is false,
11
+ # to cause an assertion failure.
12
+
13
+ module SimpleClient
14
+ def notify_writable
15
+ $conn2.notify_writable = false # Being naughty in callback
16
+ # If this doesn't crash anything, the test passed!
17
+ end
18
+ end
19
+
20
+ def test_with_naughty_callback
21
+ EM.run do
22
+ r1, w1 = IO.pipe
23
+ r2, w2 = IO.pipe
24
+
25
+ # Adding EM.watches
26
+ $conn1 = EM.watch(r1, SimpleClient)
27
+ $conn2 = EM.watch(r2, SimpleClient)
28
+
29
+ $conn1.notify_writable = true
30
+ $conn2.notify_writable = true
31
+
32
+ EM.stop
33
+ end
34
+ end
35
+ end
@@ -187,4 +187,47 @@ class TestHttpClient < Test::Unit::TestCase
187
187
  assert ok
188
188
  end
189
189
 
190
+ #-----------------------------------------
191
+
192
+ # Test a server that returns chunked encoding
193
+ #
194
+ class ChunkedEncodingContent < EventMachine::Connection
195
+ def initialize *args
196
+ super
197
+ end
198
+ def receive_data data
199
+ send_data ["HTTP/1.1 200 OK",
200
+ "Server: nginx/0.7.67",
201
+ "Date: Sat, 23 Oct 2010 16:41:32 GMT",
202
+ "Content-Type: application/json",
203
+ "Transfer-Encoding: chunked",
204
+ "Connection: keep-alive",
205
+ "",
206
+ "1800",
207
+ "chunk1" * 1024,
208
+ "5a",
209
+ "chunk2" * 15,
210
+ "0",
211
+ ""].join("\r\n")
212
+ close_connection_after_writing
213
+ end
214
+ end
215
+
216
+ def test_http_chunked_encoding_content
217
+ ok = false
218
+ EventMachine.run {
219
+ EventMachine.start_server "127.0.0.1", 9701, ChunkedEncodingContent
220
+ c = EventMachine::Protocols::HttpClient.send :request, :host => "127.0.0.1", :port => 9701
221
+ c.callback {|result|
222
+ if result[:content] == "chunk1" * 1024 + "chunk2" * 15
223
+ ok = true
224
+ end
225
+ EventMachine.stop
226
+ }
227
+ }
228
+ assert ok
229
+ end
230
+
190
231
  end
232
+
233
+
@@ -82,14 +82,19 @@ class TestPause < Test::Unit::TestCase
82
82
  end
83
83
  end
84
84
 
85
+ buf = 'a' * 1024
86
+
85
87
  EM.run do
86
88
  EM.start_server "127.0.0.1", @port, test_server
87
89
  cli = EM.connect "127.0.0.1", @port
88
- cli.send_data 'a'*(17*1024)
90
+ 128.times do
91
+ cli.send_data buf
92
+ end
89
93
  end
90
94
 
91
95
  assert_equal 1, incoming.size
92
- assert_equal 16*1024, incoming[0].bytesize
96
+ assert incoming[0].bytesize > buf.bytesize
97
+ assert incoming[0].bytesize < buf.bytesize * 128
93
98
  end
94
99
  else
95
100
  warn "EM.pause_connection not implemented, skipping tests in #{__FILE__}"
@@ -27,6 +27,7 @@ if EM.kqueue?
27
27
  end
28
28
 
29
29
  def test_events
30
+ omit_if(rbx?)
30
31
  omit_if(jruby?)
31
32
  EM.run{
32
33
  # watch ourselves for a fork notification
@@ -27,6 +27,7 @@ class TestSSLMethods < Test::Unit::TestCase
27
27
 
28
28
  def test_ssl_methods
29
29
  omit_unless(EM.ssl?)
30
+ omit_if(rbx?)
30
31
  $server_called_back, $client_called_back = false, false
31
32
  $server_cert_value, $client_cert_value = nil, nil
32
33
 
@@ -54,6 +54,7 @@ class TestSslVerify < Test::Unit::TestCase
54
54
 
55
55
  def test_accept_server
56
56
  omit_unless(EM.ssl?)
57
+ omit_if(rbx?)
57
58
  $client_handshake_completed, $server_handshake_completed = false, false
58
59
  EM.run {
59
60
  EM.start_server("127.0.0.1", 16784, AcceptServer)
@@ -67,6 +68,7 @@ class TestSslVerify < Test::Unit::TestCase
67
68
 
68
69
  def test_deny_server
69
70
  omit_unless(EM.ssl?)
71
+ omit_if(rbx?)
70
72
  $client_handshake_completed, $server_handshake_completed = false, false
71
73
  EM.run {
72
74
  EM.start_server("127.0.0.1", 16784, DenyServer)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: eventmachine
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.5
4
+ version: 1.0.6
5
5
  platform: java
6
6
  authors:
7
7
  - Francis Cianfrocca
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2015-02-02 00:00:00.000000000 Z
12
+ date: 2015-02-04 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: test-unit
@@ -203,6 +203,7 @@ files:
203
203
  - tests/test_channel.rb
204
204
  - tests/test_completion.rb
205
205
  - tests/test_connection_count.rb
206
+ - tests/test_connection_write.rb
206
207
  - tests/test_defer.rb
207
208
  - tests/test_deferrable.rb
208
209
  - tests/test_epoll.rb