eventmachine 1.0.5-java → 1.0.6-java

Sign up to get free protection for your applications and to get access to all the features.
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