sensu-em 2.4.0-x86-mingw32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +21 -0
- data/.travis.yml +12 -0
- data/.yardopts +7 -0
- data/CHANGELOG.md +33 -0
- data/GNU +281 -0
- data/Gemfile +2 -0
- data/LICENSE +60 -0
- data/README.md +109 -0
- data/Rakefile +20 -0
- data/docs/DocumentationGuidesIndex.md +27 -0
- data/docs/GettingStarted.md +521 -0
- data/docs/old/ChangeLog +211 -0
- data/docs/old/DEFERRABLES +246 -0
- data/docs/old/EPOLL +141 -0
- data/docs/old/INSTALL +13 -0
- data/docs/old/KEYBOARD +42 -0
- data/docs/old/LEGAL +25 -0
- data/docs/old/LIGHTWEIGHT_CONCURRENCY +130 -0
- data/docs/old/PURE_RUBY +75 -0
- data/docs/old/RELEASE_NOTES +94 -0
- data/docs/old/SMTP +4 -0
- data/docs/old/SPAWNED_PROCESSES +148 -0
- data/docs/old/TODO +8 -0
- data/eventmachine.gemspec +37 -0
- data/examples/guides/getting_started/01_eventmachine_echo_server.rb +18 -0
- data/examples/guides/getting_started/02_eventmachine_echo_server_that_recognizes_exit_command.rb +22 -0
- data/examples/guides/getting_started/03_simple_chat_server.rb +149 -0
- data/examples/guides/getting_started/04_simple_chat_server_step_one.rb +27 -0
- data/examples/guides/getting_started/05_simple_chat_server_step_two.rb +43 -0
- data/examples/guides/getting_started/06_simple_chat_server_step_three.rb +98 -0
- data/examples/guides/getting_started/07_simple_chat_server_step_four.rb +121 -0
- data/examples/guides/getting_started/08_simple_chat_server_step_five.rb +141 -0
- data/examples/old/ex_channel.rb +43 -0
- data/examples/old/ex_queue.rb +2 -0
- data/examples/old/ex_tick_loop_array.rb +15 -0
- data/examples/old/ex_tick_loop_counter.rb +32 -0
- data/examples/old/helper.rb +2 -0
- data/ext/binder.cpp +124 -0
- data/ext/binder.h +46 -0
- data/ext/cmain.cpp +887 -0
- data/ext/ed.cpp +1992 -0
- data/ext/ed.h +424 -0
- data/ext/em.cpp +2352 -0
- data/ext/em.h +253 -0
- data/ext/eventmachine.h +128 -0
- data/ext/extconf.rb +179 -0
- data/ext/fastfilereader/extconf.rb +103 -0
- data/ext/fastfilereader/mapper.cpp +214 -0
- data/ext/fastfilereader/mapper.h +59 -0
- data/ext/fastfilereader/rubymain.cpp +127 -0
- data/ext/kb.cpp +79 -0
- data/ext/page.cpp +107 -0
- data/ext/page.h +51 -0
- data/ext/pipe.cpp +347 -0
- data/ext/project.h +161 -0
- data/ext/rubymain.cpp +1318 -0
- data/ext/ssl.cpp +476 -0
- data/ext/ssl.h +95 -0
- data/java/.classpath +6 -0
- data/java/.gitignore +1 -0
- data/java/.project +17 -0
- data/java/src/com/rubyeventmachine/DatagramPacket.java +13 -0
- data/java/src/com/rubyeventmachine/EmReactor.java +531 -0
- data/java/src/com/rubyeventmachine/EmReactorException.java +40 -0
- data/java/src/com/rubyeventmachine/EventCallback.java +7 -0
- data/java/src/com/rubyeventmachine/EventCode.java +26 -0
- data/java/src/com/rubyeventmachine/EventableChannel.java +130 -0
- data/java/src/com/rubyeventmachine/EventableDatagramChannel.java +179 -0
- data/java/src/com/rubyeventmachine/EventableSocketChannel.java +405 -0
- data/java/src/com/rubyeventmachine/SslBox.java +311 -0
- data/lib/em/buftok.rb +110 -0
- data/lib/em/callback.rb +58 -0
- data/lib/em/channel.rb +64 -0
- data/lib/em/completion.rb +304 -0
- data/lib/em/connection.rb +716 -0
- data/lib/em/deferrable.rb +210 -0
- data/lib/em/deferrable/pool.rb +2 -0
- data/lib/em/file_watch.rb +73 -0
- data/lib/em/future.rb +61 -0
- data/lib/em/iterator.rb +231 -0
- data/lib/em/messages.rb +66 -0
- data/lib/em/pool.rb +151 -0
- data/lib/em/process_watch.rb +45 -0
- data/lib/em/processes.rb +123 -0
- data/lib/em/protocols.rb +37 -0
- data/lib/em/protocols/header_and_content.rb +138 -0
- data/lib/em/protocols/httpclient.rb +279 -0
- data/lib/em/protocols/httpclient2.rb +600 -0
- data/lib/em/protocols/line_and_text.rb +125 -0
- data/lib/em/protocols/line_protocol.rb +29 -0
- data/lib/em/protocols/linetext2.rb +161 -0
- data/lib/em/protocols/memcache.rb +331 -0
- data/lib/em/protocols/object_protocol.rb +46 -0
- data/lib/em/protocols/postgres3.rb +246 -0
- data/lib/em/protocols/saslauth.rb +175 -0
- data/lib/em/protocols/smtpclient.rb +365 -0
- data/lib/em/protocols/smtpserver.rb +643 -0
- data/lib/em/protocols/socks4.rb +66 -0
- data/lib/em/protocols/stomp.rb +205 -0
- data/lib/em/protocols/tcptest.rb +54 -0
- data/lib/em/pure_ruby.rb +1017 -0
- data/lib/em/queue.rb +71 -0
- data/lib/em/resolver.rb +209 -0
- data/lib/em/spawnable.rb +84 -0
- data/lib/em/streamer.rb +118 -0
- data/lib/em/threaded_resource.rb +90 -0
- data/lib/em/tick_loop.rb +85 -0
- data/lib/em/timers.rb +61 -0
- data/lib/em/version.rb +3 -0
- data/lib/eventmachine.rb +1553 -0
- data/lib/fastfilereaderext.rb +2 -0
- data/lib/jeventmachine.rb +321 -0
- data/lib/rubyeventmachine.rb +2 -0
- data/rakelib/cpp.rake_example +77 -0
- data/rakelib/package.rake +98 -0
- data/rakelib/test.rake +8 -0
- data/tests/client.crt +31 -0
- data/tests/client.key +51 -0
- data/tests/em_test_helper.rb +64 -0
- data/tests/server.crt +36 -0
- data/tests/server.key +51 -0
- data/tests/test_attach.rb +150 -0
- data/tests/test_basic.rb +294 -0
- data/tests/test_channel.rb +62 -0
- data/tests/test_completion.rb +177 -0
- data/tests/test_connection_count.rb +53 -0
- data/tests/test_defer.rb +18 -0
- data/tests/test_deferrable.rb +35 -0
- data/tests/test_epoll.rb +145 -0
- data/tests/test_error_handler.rb +38 -0
- data/tests/test_exc.rb +28 -0
- data/tests/test_file_watch.rb +65 -0
- data/tests/test_futures.rb +170 -0
- data/tests/test_get_sock_opt.rb +37 -0
- data/tests/test_handler_check.rb +35 -0
- data/tests/test_hc.rb +155 -0
- data/tests/test_httpclient.rb +190 -0
- data/tests/test_httpclient2.rb +133 -0
- data/tests/test_idle_connection.rb +25 -0
- data/tests/test_inactivity_timeout.rb +54 -0
- data/tests/test_iterator.rb +97 -0
- data/tests/test_kb.rb +34 -0
- data/tests/test_line_protocol.rb +33 -0
- data/tests/test_ltp.rb +138 -0
- data/tests/test_ltp2.rb +288 -0
- data/tests/test_next_tick.rb +104 -0
- data/tests/test_object_protocol.rb +36 -0
- data/tests/test_pause.rb +102 -0
- data/tests/test_pending_connect_timeout.rb +52 -0
- data/tests/test_pool.rb +194 -0
- data/tests/test_process_watch.rb +48 -0
- data/tests/test_processes.rb +128 -0
- data/tests/test_proxy_connection.rb +180 -0
- data/tests/test_pure.rb +88 -0
- data/tests/test_queue.rb +50 -0
- data/tests/test_resolver.rb +55 -0
- data/tests/test_running.rb +14 -0
- data/tests/test_sasl.rb +47 -0
- data/tests/test_send_file.rb +217 -0
- data/tests/test_servers.rb +33 -0
- data/tests/test_set_sock_opt.rb +37 -0
- data/tests/test_shutdown_hooks.rb +23 -0
- data/tests/test_smtpclient.rb +55 -0
- data/tests/test_smtpserver.rb +57 -0
- data/tests/test_spawn.rb +293 -0
- data/tests/test_ssl_args.rb +78 -0
- data/tests/test_ssl_echo_data.rb +60 -0
- data/tests/test_ssl_methods.rb +56 -0
- data/tests/test_ssl_verify.rb +82 -0
- data/tests/test_stomp.rb +37 -0
- data/tests/test_system.rb +42 -0
- data/tests/test_threaded_resource.rb +53 -0
- data/tests/test_tick_loop.rb +59 -0
- data/tests/test_timers.rb +123 -0
- data/tests/test_ud.rb +8 -0
- data/tests/test_unbind_reason.rb +48 -0
- metadata +300 -0
@@ -0,0 +1,40 @@
|
|
1
|
+
/**
|
2
|
+
* $Id$
|
3
|
+
*
|
4
|
+
* Author:: Francis Cianfrocca (gmail: blackhedd)
|
5
|
+
* Homepage:: http://rubyeventmachine.com
|
6
|
+
* Date:: 15 Jul 2007
|
7
|
+
*
|
8
|
+
* See EventMachine and EventMachine::Connection for documentation and
|
9
|
+
* usage examples.
|
10
|
+
*
|
11
|
+
*
|
12
|
+
*----------------------------------------------------------------------------
|
13
|
+
*
|
14
|
+
* Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
|
15
|
+
* Gmail: blackhedd
|
16
|
+
*
|
17
|
+
* This program is free software; you can redistribute it and/or modify
|
18
|
+
* it under the terms of either: 1) the GNU General Public License
|
19
|
+
* as published by the Free Software Foundation; either version 2 of the
|
20
|
+
* License, or (at your option) any later version; or 2) Ruby's License.
|
21
|
+
*
|
22
|
+
* See the file COPYING for complete licensing information.
|
23
|
+
*
|
24
|
+
*---------------------------------------------------------------------------
|
25
|
+
*
|
26
|
+
*
|
27
|
+
*/
|
28
|
+
|
29
|
+
package com.rubyeventmachine;
|
30
|
+
|
31
|
+
/**
|
32
|
+
* @author francis
|
33
|
+
*
|
34
|
+
*/
|
35
|
+
public class EmReactorException extends Exception {
|
36
|
+
static final long serialVersionUID = 0;
|
37
|
+
public EmReactorException (String msg) {
|
38
|
+
super (msg);
|
39
|
+
}
|
40
|
+
}
|
@@ -0,0 +1,26 @@
|
|
1
|
+
package com.rubyeventmachine;
|
2
|
+
|
3
|
+
public enum EventCode {
|
4
|
+
EM_TIMER_FIRED(100),
|
5
|
+
EM_CONNECTION_READ(101),
|
6
|
+
EM_CONNECTION_UNBOUND(102),
|
7
|
+
EM_CONNECTION_ACCEPTED(103),
|
8
|
+
EM_CONNECTION_COMPLETED(104),
|
9
|
+
EM_LOOPBREAK_SIGNAL(105),
|
10
|
+
EM_CONNECTION_NOTIFY_READABLE(106),
|
11
|
+
EM_CONNECTION_NOTIFY_WRITABLE(107),
|
12
|
+
EM_SSL_HANDSHAKE_COMPLETED(108),
|
13
|
+
EM_SSL_VERIFY(109),
|
14
|
+
EM_PROXY_TARGET_UNBOUND(110),
|
15
|
+
EM_PROXY_COMPLETED(111);
|
16
|
+
|
17
|
+
private final int intValue;
|
18
|
+
|
19
|
+
EventCode(int intValue) {
|
20
|
+
this.intValue = intValue;
|
21
|
+
}
|
22
|
+
|
23
|
+
public int intValue() {
|
24
|
+
return intValue;
|
25
|
+
}
|
26
|
+
}
|
@@ -0,0 +1,130 @@
|
|
1
|
+
/**
|
2
|
+
* $Id$
|
3
|
+
*
|
4
|
+
* Author:: Francis Cianfrocca (gmail: blackhedd)
|
5
|
+
* Homepage:: http://rubyeventmachine.com
|
6
|
+
* Date:: 15 Jul 2007
|
7
|
+
*
|
8
|
+
* See EventMachine and EventMachine::Connection for documentation and
|
9
|
+
* usage examples.
|
10
|
+
*
|
11
|
+
*
|
12
|
+
*----------------------------------------------------------------------------
|
13
|
+
*
|
14
|
+
* Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
|
15
|
+
* Gmail: blackhedd
|
16
|
+
*
|
17
|
+
* This program is free software; you can redistribute it and/or modify
|
18
|
+
* it under the terms of either: 1) the GNU General Public License
|
19
|
+
* as published by the Free Software Foundation; either version 2 of the
|
20
|
+
* License, or (at your option) any later version; or 2) Ruby's License.
|
21
|
+
*
|
22
|
+
* See the file COPYING for complete licensing information.
|
23
|
+
*
|
24
|
+
*---------------------------------------------------------------------------
|
25
|
+
*
|
26
|
+
*
|
27
|
+
*/
|
28
|
+
|
29
|
+
package com.rubyeventmachine;
|
30
|
+
|
31
|
+
import java.nio.ByteBuffer;
|
32
|
+
import java.io.IOException;
|
33
|
+
import java.nio.channels.ClosedChannelException;
|
34
|
+
import java.nio.channels.Selector;
|
35
|
+
import java.util.LinkedList;
|
36
|
+
|
37
|
+
public abstract class EventableChannel<OutboundPacketType> {
|
38
|
+
protected final long binding;
|
39
|
+
protected final Selector selector;
|
40
|
+
protected final LinkedList<OutboundPacketType> outboundQ;
|
41
|
+
protected final EventCallback callback;
|
42
|
+
private final ByteBuffer readBuffer;
|
43
|
+
|
44
|
+
public EventableChannel(long binding, Selector selector,
|
45
|
+
EventCallback callback) {
|
46
|
+
this.binding = binding;
|
47
|
+
this.selector = selector;
|
48
|
+
this.callback = callback;
|
49
|
+
this.outboundQ = new LinkedList<OutboundPacketType>();
|
50
|
+
this.readBuffer = ByteBuffer.allocate(32*1024); // don't use a direct buffer. Ruby doesn't seem to like them.
|
51
|
+
}
|
52
|
+
|
53
|
+
public abstract void scheduleOutboundData(ByteBuffer bb);
|
54
|
+
|
55
|
+
public abstract void scheduleOutboundDatagram(ByteBuffer bb,
|
56
|
+
String recipAddress, int recipPort);
|
57
|
+
|
58
|
+
public abstract boolean scheduleClose(boolean afterWriting);
|
59
|
+
|
60
|
+
public abstract void startTls();
|
61
|
+
|
62
|
+
public long getBinding() {
|
63
|
+
return binding;
|
64
|
+
}
|
65
|
+
|
66
|
+
public abstract void register() throws ClosedChannelException;
|
67
|
+
|
68
|
+
/**
|
69
|
+
* This is called by the reactor after it finishes running. The idea is to
|
70
|
+
* free network resources.
|
71
|
+
*/
|
72
|
+
public abstract void close();
|
73
|
+
|
74
|
+
public void setCommInactivityTimeout (long seconds) {
|
75
|
+
// TODO
|
76
|
+
System.out.println ("SET COMM INACTIVITY UNIMPLEMENTED IN JRUBY" + seconds);
|
77
|
+
}
|
78
|
+
|
79
|
+
public abstract Object[] getPeerName();
|
80
|
+
|
81
|
+
public abstract Object[] getSockName();
|
82
|
+
|
83
|
+
public abstract boolean isWatchOnly();
|
84
|
+
|
85
|
+
public abstract boolean isNotifyReadable();
|
86
|
+
|
87
|
+
public abstract boolean isNotifyWritable();
|
88
|
+
|
89
|
+
protected abstract boolean handshakeNeeded();
|
90
|
+
protected abstract boolean performHandshake();
|
91
|
+
|
92
|
+
protected abstract void readInboundData(ByteBuffer dst) throws IOException;
|
93
|
+
|
94
|
+
public boolean read() {
|
95
|
+
if (handshakeNeeded()) {
|
96
|
+
return performHandshake();
|
97
|
+
} else if (isWatchOnly() && isNotifyReadable()) {
|
98
|
+
callback.trigger(binding, EventCode.EM_CONNECTION_NOTIFY_READABLE, null, 0);
|
99
|
+
} else {
|
100
|
+
readBuffer.clear();
|
101
|
+
|
102
|
+
try {
|
103
|
+
readInboundData(readBuffer);
|
104
|
+
readBuffer.flip();
|
105
|
+
if (readBuffer.limit() > 0)
|
106
|
+
callback.trigger(binding, EventCode.EM_CONNECTION_READ, readBuffer, 0);
|
107
|
+
} catch (IOException e) {
|
108
|
+
return false;
|
109
|
+
}
|
110
|
+
}
|
111
|
+
return true;
|
112
|
+
}
|
113
|
+
|
114
|
+
protected abstract boolean writeOutboundData() throws IOException;
|
115
|
+
|
116
|
+
public boolean write() {
|
117
|
+
if (handshakeNeeded()) {
|
118
|
+
return performHandshake();
|
119
|
+
} else if (isWatchOnly() || isNotifyWritable()) {
|
120
|
+
callback.trigger(binding, EventCode.EM_CONNECTION_NOTIFY_WRITABLE, null, 0);
|
121
|
+
} else {
|
122
|
+
try {
|
123
|
+
return writeOutboundData();
|
124
|
+
} catch (IOException e) {
|
125
|
+
return false;
|
126
|
+
}
|
127
|
+
}
|
128
|
+
return true;
|
129
|
+
}
|
130
|
+
}
|
@@ -0,0 +1,179 @@
|
|
1
|
+
/**
|
2
|
+
* $Id$
|
3
|
+
*
|
4
|
+
* Author:: Francis Cianfrocca (gmail: blackhedd)
|
5
|
+
* Homepage:: http://rubyeventmachine.com
|
6
|
+
* Date:: 15 Jul 2007
|
7
|
+
*
|
8
|
+
* See EventMachine and EventMachine::Connection for documentation and
|
9
|
+
* usage examples.
|
10
|
+
*
|
11
|
+
*
|
12
|
+
*----------------------------------------------------------------------------
|
13
|
+
*
|
14
|
+
* Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
|
15
|
+
* Gmail: blackhedd
|
16
|
+
*
|
17
|
+
* This program is free software; you can redistribute it and/or modify
|
18
|
+
* it under the terms of either: 1) the GNU General Public License
|
19
|
+
* as published by the Free Software Foundation; either version 2 of the
|
20
|
+
* License, or (at your option) any later version; or 2) Ruby's License.
|
21
|
+
*
|
22
|
+
* See the file COPYING for complete licensing information.
|
23
|
+
*
|
24
|
+
*---------------------------------------------------------------------------
|
25
|
+
*
|
26
|
+
*
|
27
|
+
*/
|
28
|
+
|
29
|
+
|
30
|
+
package com.rubyeventmachine;
|
31
|
+
|
32
|
+
import java.nio.ByteBuffer;
|
33
|
+
import java.nio.channels.ClosedChannelException;
|
34
|
+
import java.nio.channels.SelectionKey;
|
35
|
+
import java.nio.channels.Selector;
|
36
|
+
import java.nio.channels.DatagramChannel;
|
37
|
+
import java.io.*;
|
38
|
+
import java.net.*;
|
39
|
+
|
40
|
+
public class EventableDatagramChannel extends EventableChannel<DatagramPacket> {
|
41
|
+
|
42
|
+
DatagramChannel channel;
|
43
|
+
boolean bCloseScheduled;
|
44
|
+
SocketAddress returnAddress;
|
45
|
+
|
46
|
+
public EventableDatagramChannel (DatagramChannel dc, long _binding, Selector sel, EventCallback callback) throws ClosedChannelException {
|
47
|
+
super(_binding, sel, callback);
|
48
|
+
channel = dc;
|
49
|
+
bCloseScheduled = false;
|
50
|
+
|
51
|
+
dc.register(selector, SelectionKey.OP_READ, this);
|
52
|
+
}
|
53
|
+
|
54
|
+
public void scheduleOutboundData (ByteBuffer bb) {
|
55
|
+
try {
|
56
|
+
if ((!bCloseScheduled) && (bb.remaining() > 0)) {
|
57
|
+
outboundQ.addLast(new DatagramPacket(bb, returnAddress));
|
58
|
+
channel.register(selector, SelectionKey.OP_WRITE | SelectionKey.OP_READ, this);
|
59
|
+
}
|
60
|
+
} catch (ClosedChannelException e) {
|
61
|
+
throw new RuntimeException ("no outbound data");
|
62
|
+
}
|
63
|
+
}
|
64
|
+
|
65
|
+
public void scheduleOutboundDatagram (ByteBuffer bb, String recipAddress, int recipPort) {
|
66
|
+
try {
|
67
|
+
if ((!bCloseScheduled) && (bb.remaining() > 0)) {
|
68
|
+
outboundQ.addLast(new DatagramPacket (bb, new InetSocketAddress (recipAddress, recipPort)));
|
69
|
+
channel.register(selector, SelectionKey.OP_WRITE | SelectionKey.OP_READ, this);
|
70
|
+
}
|
71
|
+
} catch (ClosedChannelException e) {
|
72
|
+
throw new RuntimeException ("no outbound data");
|
73
|
+
}
|
74
|
+
}
|
75
|
+
|
76
|
+
public boolean scheduleClose (boolean afterWriting) {
|
77
|
+
return false;
|
78
|
+
}
|
79
|
+
|
80
|
+
public void startTls() {
|
81
|
+
throw new RuntimeException ("TLS is unimplemented on this Channel");
|
82
|
+
}
|
83
|
+
|
84
|
+
public void register() throws ClosedChannelException {
|
85
|
+
// TODO
|
86
|
+
}
|
87
|
+
|
88
|
+
/**
|
89
|
+
* Terminate with extreme prejudice. Don't assume there will be another pass through
|
90
|
+
* the reactor core.
|
91
|
+
*/
|
92
|
+
public void close() {
|
93
|
+
try {
|
94
|
+
channel.close();
|
95
|
+
} catch (IOException e) {
|
96
|
+
}
|
97
|
+
}
|
98
|
+
|
99
|
+
public void readInboundData (ByteBuffer dst) {
|
100
|
+
returnAddress = null;
|
101
|
+
try {
|
102
|
+
// If there is no datagram available (we're nonblocking after all),
|
103
|
+
// then channel.receive returns null.
|
104
|
+
returnAddress = channel.receive(dst);
|
105
|
+
} catch (IOException e) {
|
106
|
+
// probably a no-op. The caller will see the empty (or even partial) buffer
|
107
|
+
// and presumably do the right thing.
|
108
|
+
}
|
109
|
+
}
|
110
|
+
|
111
|
+
protected boolean writeOutboundData() {
|
112
|
+
while (!outboundQ.isEmpty()) {
|
113
|
+
DatagramPacket p = outboundQ.getFirst();
|
114
|
+
int written = 0;
|
115
|
+
try {
|
116
|
+
// With a datagram socket, it's ok to send an empty buffer.
|
117
|
+
written = channel.send(p.bb, p.recipient);
|
118
|
+
}
|
119
|
+
catch (IOException e) {
|
120
|
+
return false;
|
121
|
+
}
|
122
|
+
|
123
|
+
/* Did we consume the whole outbound buffer? If yes, pop it off and
|
124
|
+
* keep looping. If no, the outbound network buffers are full, so break
|
125
|
+
* out of here. There's a flaw that affects outbound buffers that are intentionally
|
126
|
+
* empty. We can tell whether they got sent or not. So we assume they were.
|
127
|
+
* TODO: As implemented, this ALWAYS discards packets if they were at least
|
128
|
+
* partially written. This matches the behavior of the C++ EM. My judgment
|
129
|
+
* is that this is less surprising than fragmenting the data and sending multiple
|
130
|
+
* packets would be. I could be wrong, so this is subject to change.
|
131
|
+
*/
|
132
|
+
|
133
|
+
if ((written > 0) || (p.bb.remaining() == 0))
|
134
|
+
outboundQ.removeFirst();
|
135
|
+
else
|
136
|
+
break;
|
137
|
+
}
|
138
|
+
|
139
|
+
if (outboundQ.isEmpty()) {
|
140
|
+
try {
|
141
|
+
channel.register(selector, SelectionKey.OP_READ, this);
|
142
|
+
} catch (ClosedChannelException e) {}
|
143
|
+
}
|
144
|
+
|
145
|
+
// ALWAYS drain the outbound queue before triggering a connection close.
|
146
|
+
// If anyone wants to close immediately, they're responsible for clearing
|
147
|
+
// the outbound queue.
|
148
|
+
return (bCloseScheduled && outboundQ.isEmpty()) ? false : true;
|
149
|
+
}
|
150
|
+
|
151
|
+
public Object[] getPeerName () {
|
152
|
+
if (returnAddress != null) {
|
153
|
+
InetSocketAddress inetAddr = (InetSocketAddress) returnAddress;
|
154
|
+
return new Object[]{ inetAddr.getPort(), inetAddr.getHostName() };
|
155
|
+
} else {
|
156
|
+
return null;
|
157
|
+
}
|
158
|
+
}
|
159
|
+
|
160
|
+
public Object[] getSockName () {
|
161
|
+
DatagramSocket socket = channel.socket();
|
162
|
+
return new Object[]{ socket.getLocalPort(),
|
163
|
+
socket.getLocalAddress().getHostAddress() };
|
164
|
+
}
|
165
|
+
|
166
|
+
public boolean isWatchOnly() { return false; }
|
167
|
+
public boolean isNotifyReadable() { return false; }
|
168
|
+
public boolean isNotifyWritable() { return false; }
|
169
|
+
|
170
|
+
@Override
|
171
|
+
protected boolean handshakeNeeded() {
|
172
|
+
return false;
|
173
|
+
}
|
174
|
+
|
175
|
+
@Override
|
176
|
+
protected boolean performHandshake() {
|
177
|
+
return true;
|
178
|
+
}
|
179
|
+
}
|
@@ -0,0 +1,405 @@
|
|
1
|
+
/**
|
2
|
+
* $Id$
|
3
|
+
*
|
4
|
+
* Author:: Francis Cianfrocca (gmail: blackhedd)
|
5
|
+
* Homepage:: http://rubyeventmachine.com
|
6
|
+
* Date:: 15 Jul 2007
|
7
|
+
*
|
8
|
+
* See EventMachine and EventMachine::Connection for documentation and
|
9
|
+
* usage examples.
|
10
|
+
*
|
11
|
+
*
|
12
|
+
*----------------------------------------------------------------------------
|
13
|
+
*
|
14
|
+
* Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
|
15
|
+
* Gmail: blackhedd
|
16
|
+
*
|
17
|
+
* This program is free software; you can redistribute it and/or modify
|
18
|
+
* it under the terms of either: 1) the GNU General Public License
|
19
|
+
* as published by the Free Software Foundation; either version 2 of the
|
20
|
+
* License, or (at your option) any later version; or 2) Ruby's License.
|
21
|
+
*
|
22
|
+
* See the file COPYING for complete licensing information.
|
23
|
+
*
|
24
|
+
*---------------------------------------------------------------------------
|
25
|
+
*
|
26
|
+
*
|
27
|
+
*/
|
28
|
+
|
29
|
+
/**
|
30
|
+
*
|
31
|
+
*/
|
32
|
+
package com.rubyeventmachine;
|
33
|
+
|
34
|
+
/**
|
35
|
+
* @author francis
|
36
|
+
*
|
37
|
+
*/
|
38
|
+
|
39
|
+
import java.io.FileDescriptor;
|
40
|
+
import java.io.IOException;
|
41
|
+
import java.lang.reflect.Field;
|
42
|
+
import java.net.Socket;
|
43
|
+
import java.nio.ByteBuffer;
|
44
|
+
import java.nio.channels.ClosedChannelException;
|
45
|
+
import java.nio.channels.SelectionKey;
|
46
|
+
import java.nio.channels.Selector;
|
47
|
+
import java.nio.channels.SocketChannel;
|
48
|
+
import java.security.KeyStore;
|
49
|
+
import java.security.cert.CertificateException;
|
50
|
+
import java.security.cert.X509Certificate;
|
51
|
+
|
52
|
+
import javax.net.ssl.X509TrustManager;
|
53
|
+
|
54
|
+
public class EventableSocketChannel extends EventableChannel<ByteBuffer> {
|
55
|
+
SelectionKey channelKey;
|
56
|
+
SocketChannel channel;
|
57
|
+
|
58
|
+
boolean bCloseScheduled;
|
59
|
+
boolean bConnectPending;
|
60
|
+
boolean bWatchOnly;
|
61
|
+
boolean bAttached;
|
62
|
+
boolean bNotifyReadable;
|
63
|
+
boolean bNotifyWritable;
|
64
|
+
|
65
|
+
SslBox sslBox;
|
66
|
+
private KeyStore keyStore;
|
67
|
+
private boolean verifyPeer;
|
68
|
+
private boolean bIsServer;
|
69
|
+
private boolean shouldAcceptSslPeer = false;
|
70
|
+
|
71
|
+
public EventableSocketChannel (SocketChannel sc, long _binding, Selector sel, EventCallback callback) {
|
72
|
+
super(_binding, sel, callback);
|
73
|
+
channel = sc;
|
74
|
+
bCloseScheduled = false;
|
75
|
+
bConnectPending = false;
|
76
|
+
bWatchOnly = false;
|
77
|
+
bAttached = false;
|
78
|
+
bNotifyReadable = false;
|
79
|
+
bNotifyWritable = false;
|
80
|
+
bIsServer = false;
|
81
|
+
}
|
82
|
+
|
83
|
+
public SocketChannel getChannel() {
|
84
|
+
return channel;
|
85
|
+
}
|
86
|
+
|
87
|
+
public void register() throws ClosedChannelException {
|
88
|
+
if (channelKey == null) {
|
89
|
+
int events = currentEvents();
|
90
|
+
channelKey = channel.register(selector, events, this);
|
91
|
+
}
|
92
|
+
}
|
93
|
+
|
94
|
+
/**
|
95
|
+
* Terminate with extreme prejudice. Don't assume there will be another pass through
|
96
|
+
* the reactor core.
|
97
|
+
*/
|
98
|
+
public void close() {
|
99
|
+
if (channelKey != null) {
|
100
|
+
channelKey.cancel();
|
101
|
+
channelKey = null;
|
102
|
+
}
|
103
|
+
|
104
|
+
if (bAttached) {
|
105
|
+
// attached channels are copies, so reset the file descriptor to prevent java from close()ing it
|
106
|
+
Field f;
|
107
|
+
FileDescriptor fd;
|
108
|
+
|
109
|
+
try {
|
110
|
+
/* do _NOT_ clobber fdVal here, it will break epoll/kqueue on jdk6!
|
111
|
+
* channelKey.cancel() above does not occur until the next call to select
|
112
|
+
* and if fdVal is gone, we will continue to get events for this fd.
|
113
|
+
*
|
114
|
+
* instead, remove fdVal in cleanup(), which is processed via DetachedConnections,
|
115
|
+
* after UnboundConnections but before NewConnections.
|
116
|
+
*/
|
117
|
+
|
118
|
+
f = channel.getClass().getDeclaredField("fd");
|
119
|
+
f.setAccessible(true);
|
120
|
+
fd = (FileDescriptor) f.get(channel);
|
121
|
+
|
122
|
+
f = fd.getClass().getDeclaredField("fd");
|
123
|
+
f.setAccessible(true);
|
124
|
+
f.set(fd, -1);
|
125
|
+
} catch (java.lang.NoSuchFieldException e) {
|
126
|
+
e.printStackTrace();
|
127
|
+
} catch (java.lang.IllegalAccessException e) {
|
128
|
+
e.printStackTrace();
|
129
|
+
}
|
130
|
+
|
131
|
+
return;
|
132
|
+
}
|
133
|
+
|
134
|
+
try {
|
135
|
+
channel.close();
|
136
|
+
} catch (IOException e) {
|
137
|
+
}
|
138
|
+
}
|
139
|
+
|
140
|
+
public void cleanup() {
|
141
|
+
if (bAttached) {
|
142
|
+
Field f;
|
143
|
+
try {
|
144
|
+
f = channel.getClass().getDeclaredField("fdVal");
|
145
|
+
f.setAccessible(true);
|
146
|
+
f.set(channel, -1);
|
147
|
+
} catch (java.lang.NoSuchFieldException e) {
|
148
|
+
e.printStackTrace();
|
149
|
+
} catch (java.lang.IllegalAccessException e) {
|
150
|
+
e.printStackTrace();
|
151
|
+
}
|
152
|
+
}
|
153
|
+
|
154
|
+
channel = null;
|
155
|
+
}
|
156
|
+
|
157
|
+
public void scheduleOutboundData (ByteBuffer bb) {
|
158
|
+
if (!bCloseScheduled && bb.remaining() > 0) {
|
159
|
+
outboundQ.addLast( bb );
|
160
|
+
updateEvents();
|
161
|
+
}
|
162
|
+
}
|
163
|
+
|
164
|
+
public void scheduleOutboundDatagram (ByteBuffer bb, String recipAddress, int recipPort) {
|
165
|
+
throw new RuntimeException ("datagram sends not supported on this channel");
|
166
|
+
}
|
167
|
+
|
168
|
+
/**
|
169
|
+
* Called by the reactor when we have selected readable.
|
170
|
+
*/
|
171
|
+
public void readInboundData (ByteBuffer bb) throws IOException {
|
172
|
+
int bytesRead = (sslBox != null) ? sslBox.read(bb) : channel.read(bb);
|
173
|
+
if (bytesRead == -1)
|
174
|
+
throw new IOException ("eof");
|
175
|
+
}
|
176
|
+
|
177
|
+
/**
|
178
|
+
* Called by the reactor when we have selected writable.
|
179
|
+
* Return false to indicate an error that should cause the connection to close.
|
180
|
+
* TODO, VERY IMPORTANT: we're here because we selected writable, but it's always
|
181
|
+
* possible to become unwritable between the poll and when we get here. The way
|
182
|
+
* this code is written, we're depending on a nonblocking write NOT TO CONSUME
|
183
|
+
* the whole outbound buffer in this case, rather than firing an exception.
|
184
|
+
* We should somehow verify that this is indeed Java's defined behavior.
|
185
|
+
* Also TODO, see if we can use gather I/O rather than one write at a time.
|
186
|
+
* Ought to be a big performance enhancer.
|
187
|
+
* @return
|
188
|
+
*/
|
189
|
+
protected boolean writeOutboundData() throws IOException {
|
190
|
+
while (!outboundQ.isEmpty()) {
|
191
|
+
ByteBuffer b = outboundQ.getFirst();
|
192
|
+
if (b.remaining() > 0) {
|
193
|
+
if (sslBox != null)
|
194
|
+
sslBox.write(b);
|
195
|
+
else
|
196
|
+
channel.write(b);
|
197
|
+
}
|
198
|
+
|
199
|
+
// Did we consume the whole outbound buffer? If yes,
|
200
|
+
// pop it off and keep looping. If no, the outbound network
|
201
|
+
// buffers are full, so break out of here.
|
202
|
+
if (b.remaining() == 0)
|
203
|
+
outboundQ.removeFirst();
|
204
|
+
else
|
205
|
+
break;
|
206
|
+
}
|
207
|
+
|
208
|
+
if (outboundQ.isEmpty() && !bCloseScheduled) {
|
209
|
+
updateEvents();
|
210
|
+
}
|
211
|
+
|
212
|
+
// ALWAYS drain the outbound queue before triggering a connection close.
|
213
|
+
// If anyone wants to close immediately, they're responsible for clearing
|
214
|
+
// the outbound queue.
|
215
|
+
return (bCloseScheduled && outboundQ.isEmpty()) ? false : true;
|
216
|
+
}
|
217
|
+
|
218
|
+
public void setConnectPending() {
|
219
|
+
bConnectPending = true;
|
220
|
+
updateEvents();
|
221
|
+
}
|
222
|
+
|
223
|
+
/**
|
224
|
+
* Called by the reactor when we have selected connectable.
|
225
|
+
* Return false to indicate an error that should cause the connection to close.
|
226
|
+
*/
|
227
|
+
public boolean finishConnecting() {
|
228
|
+
try {
|
229
|
+
channel.finishConnect();
|
230
|
+
bConnectPending = false;
|
231
|
+
updateEvents();
|
232
|
+
callback.trigger(binding, EventCode.EM_CONNECTION_COMPLETED, null, 0);
|
233
|
+
return true;
|
234
|
+
} catch (IOException e) {
|
235
|
+
return false;
|
236
|
+
}
|
237
|
+
}
|
238
|
+
|
239
|
+
public boolean scheduleClose (boolean afterWriting) {
|
240
|
+
// TODO: What the hell happens here if bConnectPending is set?
|
241
|
+
if (!afterWriting)
|
242
|
+
outboundQ.clear();
|
243
|
+
|
244
|
+
if (outboundQ.isEmpty())
|
245
|
+
return true;
|
246
|
+
else {
|
247
|
+
updateEvents();
|
248
|
+
bCloseScheduled = true;
|
249
|
+
return false;
|
250
|
+
}
|
251
|
+
}
|
252
|
+
|
253
|
+
public void setTlsParms(KeyStore keyStore, boolean verifyPeer) {
|
254
|
+
this.keyStore = keyStore;
|
255
|
+
this.verifyPeer = verifyPeer;
|
256
|
+
}
|
257
|
+
|
258
|
+
public void startTls() {
|
259
|
+
if (sslBox == null) {
|
260
|
+
Object[] peerName = getPeerName();
|
261
|
+
int port = (Integer) peerName[0];
|
262
|
+
String host = (String) peerName[1];
|
263
|
+
X509TrustManager tm = new CallbackBasedTrustManager();
|
264
|
+
sslBox = new SslBox(bIsServer, channel, keyStore, tm, verifyPeer, host, port);
|
265
|
+
updateEvents();
|
266
|
+
}
|
267
|
+
}
|
268
|
+
|
269
|
+
public Object[] getPeerName () {
|
270
|
+
Socket sock = channel.socket();
|
271
|
+
return new Object[]{ sock.getPort(), sock.getInetAddress().getHostAddress() };
|
272
|
+
}
|
273
|
+
|
274
|
+
public Object[] getSockName () {
|
275
|
+
Socket sock = channel.socket();
|
276
|
+
return new Object[]{ sock.getLocalPort(),
|
277
|
+
sock.getLocalAddress().getHostAddress() };
|
278
|
+
}
|
279
|
+
|
280
|
+
public void setWatchOnly() {
|
281
|
+
bWatchOnly = true;
|
282
|
+
updateEvents();
|
283
|
+
}
|
284
|
+
public boolean isWatchOnly() { return bWatchOnly; }
|
285
|
+
|
286
|
+
public void setAttached() {
|
287
|
+
bAttached = true;
|
288
|
+
}
|
289
|
+
public boolean isAttached() { return bAttached; }
|
290
|
+
|
291
|
+
public void setNotifyReadable (boolean mode) {
|
292
|
+
bNotifyReadable = mode;
|
293
|
+
updateEvents();
|
294
|
+
}
|
295
|
+
public boolean isNotifyReadable() { return bNotifyReadable; }
|
296
|
+
|
297
|
+
public void setNotifyWritable (boolean mode) {
|
298
|
+
bNotifyWritable = mode;
|
299
|
+
updateEvents();
|
300
|
+
}
|
301
|
+
public boolean isNotifyWritable() { return bNotifyWritable; }
|
302
|
+
|
303
|
+
private void updateEvents() {
|
304
|
+
if (channelKey == null)
|
305
|
+
return;
|
306
|
+
|
307
|
+
int events = currentEvents();
|
308
|
+
|
309
|
+
if (channelKey.interestOps() != events) {
|
310
|
+
channelKey.interestOps(events);
|
311
|
+
}
|
312
|
+
}
|
313
|
+
|
314
|
+
private int currentEvents() {
|
315
|
+
int events = 0;
|
316
|
+
|
317
|
+
if (bWatchOnly)
|
318
|
+
{
|
319
|
+
if (bNotifyReadable)
|
320
|
+
events |= SelectionKey.OP_READ;
|
321
|
+
|
322
|
+
if (bNotifyWritable)
|
323
|
+
events |= SelectionKey.OP_WRITE;
|
324
|
+
}
|
325
|
+
else
|
326
|
+
{
|
327
|
+
if (bConnectPending)
|
328
|
+
events |= SelectionKey.OP_CONNECT;
|
329
|
+
else {
|
330
|
+
events |= SelectionKey.OP_READ;
|
331
|
+
|
332
|
+
if (!outboundQ.isEmpty() || (sslBox != null && sslBox.handshakeNeeded()))
|
333
|
+
events |= SelectionKey.OP_WRITE;
|
334
|
+
}
|
335
|
+
}
|
336
|
+
|
337
|
+
return events;
|
338
|
+
}
|
339
|
+
|
340
|
+
public void setServerMode() {
|
341
|
+
bIsServer = true;
|
342
|
+
}
|
343
|
+
|
344
|
+
@Override
|
345
|
+
protected boolean handshakeNeeded() {
|
346
|
+
return sslBox != null && sslBox.handshakeNeeded();
|
347
|
+
}
|
348
|
+
|
349
|
+
@Override
|
350
|
+
protected boolean performHandshake() {
|
351
|
+
if (sslBox == null) return true;
|
352
|
+
|
353
|
+
if (sslBox.handshake(channelKey)) {
|
354
|
+
if (!sslBox.handshakeNeeded()) {
|
355
|
+
callback.trigger(binding, EventCode.EM_SSL_HANDSHAKE_COMPLETED, null, 0);
|
356
|
+
updateEvents();
|
357
|
+
}
|
358
|
+
return true;
|
359
|
+
}
|
360
|
+
return false;
|
361
|
+
}
|
362
|
+
|
363
|
+
public void acceptSslPeer() {
|
364
|
+
this.shouldAcceptSslPeer = true;
|
365
|
+
}
|
366
|
+
|
367
|
+
public class CallbackBasedTrustManager implements X509TrustManager {
|
368
|
+
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
|
369
|
+
if (verifyPeer) fireEvent(chain[0]);
|
370
|
+
}
|
371
|
+
|
372
|
+
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
|
373
|
+
if (verifyPeer) fireEvent(chain[0]);
|
374
|
+
}
|
375
|
+
|
376
|
+
public X509Certificate[] getAcceptedIssuers() {
|
377
|
+
return new X509Certificate[0];
|
378
|
+
}
|
379
|
+
|
380
|
+
private void fireEvent(X509Certificate cert) throws CertificateException {
|
381
|
+
|
382
|
+
ByteBuffer data = ByteBuffer.wrap(cert.getEncoded());
|
383
|
+
|
384
|
+
callback.trigger(binding, EventCode.EM_SSL_VERIFY, data, 0);
|
385
|
+
|
386
|
+
// If we should accept, the trigger will ultimately call our acceptSslPeer method.
|
387
|
+
if (! shouldAcceptSslPeer) {
|
388
|
+
throw new CertificateException("JRuby trigger was not fired");
|
389
|
+
}
|
390
|
+
}
|
391
|
+
}
|
392
|
+
|
393
|
+
public byte[] getPeerCert() {
|
394
|
+
if (sslBox != null) {
|
395
|
+
try {
|
396
|
+
javax.security.cert.X509Certificate peerCert = sslBox.getPeerCert();
|
397
|
+
return peerCert.getEncoded();
|
398
|
+
} catch (Exception e) {
|
399
|
+
}
|
400
|
+
}
|
401
|
+
return null;
|
402
|
+
}
|
403
|
+
|
404
|
+
|
405
|
+
}
|