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 +4 -4
- data/CHANGELOG.md +12 -0
- data/README.md +1 -2
- data/ext/em.cpp +8 -3
- data/ext/extconf.rb +3 -0
- data/ext/fastfilereader/extconf.rb +3 -0
- data/ext/rubymain.cpp +15 -1
- data/java/src/com/rubyeventmachine/EmReactor.java +16 -0
- data/java/src/com/rubyeventmachine/EventableChannel.java +2 -0
- data/java/src/com/rubyeventmachine/EventableDatagramChannel.java +6 -0
- data/java/src/com/rubyeventmachine/EventableSocketChannel.java +55 -10
- data/lib/em/buftok.rb +34 -85
- data/lib/em/protocols/httpclient.rb +29 -11
- data/lib/em/protocols/line_and_text.rb +2 -3
- data/lib/em/protocols/linetext2.rb +0 -1
- data/lib/em/protocols/smtpserver.rb +26 -7
- data/lib/em/pure_ruby.rb +2 -2
- data/lib/em/version.rb +1 -1
- data/lib/jeventmachine.rb +17 -0
- data/tests/em_test_helper.rb +4 -0
- data/tests/test_basic.rb +1 -0
- data/tests/test_connection_write.rb +35 -0
- data/tests/test_httpclient.rb +43 -0
- data/tests/test_pause.rb +7 -2
- data/tests/test_process_watch.rb +1 -0
- data/tests/test_ssl_methods.rb +1 -0
- data/tests/test_ssl_verify.rb +2 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4722f24a61f13ac737b777597b71542a5ea1b0bd
|
4
|
+
data.tar.gz: 2bc74d2fe5b8de6e467faeae30916f4cb1a71ddf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f2d16977dff27530edcd046a5503f7597a5f5859576592dc0b8e2f826f9cac227309b0f2fadf87c65348520257ea7f78a34f4f2c892f72876c31181df7b3487b
|
7
|
+
data.tar.gz: bdb3a2a7bcf18f803f8e002637b1809a2691e84025bb19ab3e319d82d13e21f39ab35ce1c96a865848e26ba586cc8dbd9be8e880a1bdbdd924498b7df73fdeff
|
data/CHANGELOG.md
CHANGED
@@ -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 [
|
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
|
-
|
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
|
-
|
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)))
|
data/ext/extconf.rb
CHANGED
@@ -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}/")
|
data/ext/rubymain.cpp
CHANGED
@@ -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
|
}
|
@@ -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
|
-
|
206
|
-
|
207
|
-
|
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 (
|
213
|
-
|
214
|
-
|
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;
|
data/lib/em/buftok.rb
CHANGED
@@ -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
|
-
#
|
24
|
-
#
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
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
|
-
|
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
|
-
#
|
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
|
-
|
51
|
-
|
52
|
-
|
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
|
-
|
65
|
-
|
66
|
-
@
|
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
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
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
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
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"
|
118
|
+
@lpb_buffer = BufferedTokenizer.new("\n")
|
120
119
|
@lbp_mode = :lines
|
121
120
|
end
|
122
121
|
private :lbp_init_line_state
|
@@ -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
|
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
|
237
|
-
send_data "
|
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 "
|
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
|
-
|
369
|
+
|
370
|
+
succeeded = proc {
|
362
371
|
send_data "235 authentication ok\r\n"
|
363
372
|
@state << :auth
|
364
|
-
|
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
|
|
data/lib/em/pure_ruby.rb
CHANGED
@@ -393,7 +393,7 @@ module EventMachine
|
|
393
393
|
100.times {
|
394
394
|
@loopbreak_port = rand(10000) + 40000
|
395
395
|
begin
|
396
|
-
@loopbreak_reader.bind "
|
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,"
|
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
|
data/lib/em/version.rb
CHANGED
data/lib/jeventmachine.rb
CHANGED
@@ -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
|
|
data/tests/em_test_helper.rb
CHANGED
data/tests/test_basic.rb
CHANGED
@@ -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
|
data/tests/test_httpclient.rb
CHANGED
@@ -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
|
+
|
data/tests/test_pause.rb
CHANGED
@@ -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
|
-
|
90
|
+
128.times do
|
91
|
+
cli.send_data buf
|
92
|
+
end
|
89
93
|
end
|
90
94
|
|
91
95
|
assert_equal 1, incoming.size
|
92
|
-
|
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__}"
|
data/tests/test_process_watch.rb
CHANGED
data/tests/test_ssl_methods.rb
CHANGED
data/tests/test_ssl_verify.rb
CHANGED
@@ -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.
|
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-
|
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
|