wj_eventmachine 1.3.0.dev.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (180) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +179 -0
  3. data/GNU +281 -0
  4. data/LICENSE +60 -0
  5. data/README.md +110 -0
  6. data/docs/DocumentationGuidesIndex.md +27 -0
  7. data/docs/GettingStarted.md +520 -0
  8. data/docs/old/ChangeLog +211 -0
  9. data/docs/old/DEFERRABLES +246 -0
  10. data/docs/old/EPOLL +141 -0
  11. data/docs/old/INSTALL +13 -0
  12. data/docs/old/KEYBOARD +42 -0
  13. data/docs/old/LEGAL +25 -0
  14. data/docs/old/LIGHTWEIGHT_CONCURRENCY +130 -0
  15. data/docs/old/PURE_RUBY +75 -0
  16. data/docs/old/RELEASE_NOTES +94 -0
  17. data/docs/old/SMTP +4 -0
  18. data/docs/old/SPAWNED_PROCESSES +148 -0
  19. data/docs/old/TODO +8 -0
  20. data/examples/guides/getting_started/01_eventmachine_echo_server.rb +18 -0
  21. data/examples/guides/getting_started/02_eventmachine_echo_server_that_recognizes_exit_command.rb +22 -0
  22. data/examples/guides/getting_started/03_simple_chat_server.rb +149 -0
  23. data/examples/guides/getting_started/04_simple_chat_server_step_one.rb +27 -0
  24. data/examples/guides/getting_started/05_simple_chat_server_step_two.rb +43 -0
  25. data/examples/guides/getting_started/06_simple_chat_server_step_three.rb +98 -0
  26. data/examples/guides/getting_started/07_simple_chat_server_step_four.rb +121 -0
  27. data/examples/guides/getting_started/08_simple_chat_server_step_five.rb +141 -0
  28. data/examples/old/ex_channel.rb +43 -0
  29. data/examples/old/ex_queue.rb +2 -0
  30. data/examples/old/ex_tick_loop_array.rb +15 -0
  31. data/examples/old/ex_tick_loop_counter.rb +32 -0
  32. data/examples/old/helper.rb +2 -0
  33. data/ext/binder.cpp +124 -0
  34. data/ext/binder.h +52 -0
  35. data/ext/cmain.cpp +1046 -0
  36. data/ext/ed.cpp +2238 -0
  37. data/ext/ed.h +460 -0
  38. data/ext/em.cpp +2378 -0
  39. data/ext/em.h +266 -0
  40. data/ext/eventmachine.h +152 -0
  41. data/ext/extconf.rb +285 -0
  42. data/ext/fastfilereader/extconf.rb +120 -0
  43. data/ext/fastfilereader/mapper.cpp +214 -0
  44. data/ext/fastfilereader/mapper.h +59 -0
  45. data/ext/fastfilereader/rubymain.cpp +126 -0
  46. data/ext/kb.cpp +79 -0
  47. data/ext/page.cpp +107 -0
  48. data/ext/page.h +51 -0
  49. data/ext/pipe.cpp +354 -0
  50. data/ext/project.h +174 -0
  51. data/ext/rubymain.cpp +1610 -0
  52. data/ext/ssl.cpp +627 -0
  53. data/ext/ssl.h +103 -0
  54. data/ext/wait_for_single_fd.h +36 -0
  55. data/java/.classpath +8 -0
  56. data/java/.project +17 -0
  57. data/java/src/com/rubyeventmachine/EmReactor.java +625 -0
  58. data/java/src/com/rubyeventmachine/EmReactorException.java +40 -0
  59. data/java/src/com/rubyeventmachine/EmReactorInterface.java +70 -0
  60. data/java/src/com/rubyeventmachine/EventableChannel.java +72 -0
  61. data/java/src/com/rubyeventmachine/EventableDatagramChannel.java +201 -0
  62. data/java/src/com/rubyeventmachine/EventableSocketChannel.java +415 -0
  63. data/java/src/com/rubyeventmachine/NullEmReactor.java +157 -0
  64. data/java/src/com/rubyeventmachine/NullEventableChannel.java +81 -0
  65. data/lib/em/buftok.rb +59 -0
  66. data/lib/em/callback.rb +58 -0
  67. data/lib/em/channel.rb +69 -0
  68. data/lib/em/completion.rb +307 -0
  69. data/lib/em/connection.rb +776 -0
  70. data/lib/em/deferrable.rb +210 -0
  71. data/lib/em/deferrable/pool.rb +2 -0
  72. data/lib/em/file_watch.rb +73 -0
  73. data/lib/em/future.rb +61 -0
  74. data/lib/em/io_streamer.rb +68 -0
  75. data/lib/em/iterator.rb +252 -0
  76. data/lib/em/messages.rb +66 -0
  77. data/lib/em/pool.rb +151 -0
  78. data/lib/em/process_watch.rb +45 -0
  79. data/lib/em/processes.rb +123 -0
  80. data/lib/em/protocols.rb +37 -0
  81. data/lib/em/protocols/header_and_content.rb +138 -0
  82. data/lib/em/protocols/httpclient.rb +303 -0
  83. data/lib/em/protocols/httpclient2.rb +602 -0
  84. data/lib/em/protocols/line_and_text.rb +125 -0
  85. data/lib/em/protocols/line_protocol.rb +33 -0
  86. data/lib/em/protocols/linetext2.rb +179 -0
  87. data/lib/em/protocols/memcache.rb +331 -0
  88. data/lib/em/protocols/object_protocol.rb +46 -0
  89. data/lib/em/protocols/postgres3.rb +246 -0
  90. data/lib/em/protocols/saslauth.rb +175 -0
  91. data/lib/em/protocols/smtpclient.rb +394 -0
  92. data/lib/em/protocols/smtpserver.rb +666 -0
  93. data/lib/em/protocols/socks4.rb +66 -0
  94. data/lib/em/protocols/stomp.rb +205 -0
  95. data/lib/em/protocols/tcptest.rb +54 -0
  96. data/lib/em/pure_ruby.rb +1299 -0
  97. data/lib/em/queue.rb +80 -0
  98. data/lib/em/resolver.rb +232 -0
  99. data/lib/em/spawnable.rb +84 -0
  100. data/lib/em/streamer.rb +118 -0
  101. data/lib/em/threaded_resource.rb +90 -0
  102. data/lib/em/tick_loop.rb +85 -0
  103. data/lib/em/timers.rb +61 -0
  104. data/lib/em/version.rb +3 -0
  105. data/lib/eventmachine.rb +1602 -0
  106. data/lib/jeventmachine.rb +318 -0
  107. data/rakelib/package.rake +120 -0
  108. data/rakelib/test.rake +6 -0
  109. data/rakelib/test_pure.rake +11 -0
  110. data/tests/client.crt +31 -0
  111. data/tests/client.key +51 -0
  112. data/tests/dhparam.pem +13 -0
  113. data/tests/em_ssl_handlers.rb +153 -0
  114. data/tests/em_test_helper.rb +198 -0
  115. data/tests/jruby/test_jeventmachine.rb +38 -0
  116. data/tests/test_attach.rb +199 -0
  117. data/tests/test_basic.rb +321 -0
  118. data/tests/test_channel.rb +75 -0
  119. data/tests/test_completion.rb +178 -0
  120. data/tests/test_connection_count.rb +83 -0
  121. data/tests/test_connection_write.rb +35 -0
  122. data/tests/test_defer.rb +35 -0
  123. data/tests/test_deferrable.rb +35 -0
  124. data/tests/test_epoll.rb +141 -0
  125. data/tests/test_error_handler.rb +38 -0
  126. data/tests/test_exc.rb +37 -0
  127. data/tests/test_file_watch.rb +86 -0
  128. data/tests/test_fork.rb +75 -0
  129. data/tests/test_futures.rb +170 -0
  130. data/tests/test_handler_check.rb +35 -0
  131. data/tests/test_hc.rb +155 -0
  132. data/tests/test_httpclient.rb +238 -0
  133. data/tests/test_httpclient2.rb +132 -0
  134. data/tests/test_idle_connection.rb +31 -0
  135. data/tests/test_inactivity_timeout.rb +102 -0
  136. data/tests/test_io_streamer.rb +47 -0
  137. data/tests/test_ipv4.rb +96 -0
  138. data/tests/test_ipv6.rb +107 -0
  139. data/tests/test_iterator.rb +122 -0
  140. data/tests/test_kb.rb +28 -0
  141. data/tests/test_keepalive.rb +113 -0
  142. data/tests/test_line_protocol.rb +33 -0
  143. data/tests/test_ltp.rb +155 -0
  144. data/tests/test_ltp2.rb +332 -0
  145. data/tests/test_many_fds.rb +21 -0
  146. data/tests/test_next_tick.rb +104 -0
  147. data/tests/test_object_protocol.rb +36 -0
  148. data/tests/test_pause.rb +109 -0
  149. data/tests/test_pending_connect_timeout.rb +52 -0
  150. data/tests/test_pool.rb +196 -0
  151. data/tests/test_process_watch.rb +50 -0
  152. data/tests/test_processes.rb +128 -0
  153. data/tests/test_proxy_connection.rb +180 -0
  154. data/tests/test_pure.rb +156 -0
  155. data/tests/test_queue.rb +64 -0
  156. data/tests/test_resolver.rb +129 -0
  157. data/tests/test_running.rb +14 -0
  158. data/tests/test_sasl.rb +46 -0
  159. data/tests/test_send_file.rb +217 -0
  160. data/tests/test_servers.rb +32 -0
  161. data/tests/test_shutdown_hooks.rb +23 -0
  162. data/tests/test_smtpclient.rb +75 -0
  163. data/tests/test_smtpserver.rb +90 -0
  164. data/tests/test_sock_opt.rb +53 -0
  165. data/tests/test_spawn.rb +290 -0
  166. data/tests/test_ssl_args.rb +41 -0
  167. data/tests/test_ssl_dhparam.rb +57 -0
  168. data/tests/test_ssl_ecdh_curve.rb +57 -0
  169. data/tests/test_ssl_extensions.rb +24 -0
  170. data/tests/test_ssl_methods.rb +31 -0
  171. data/tests/test_ssl_protocols.rb +190 -0
  172. data/tests/test_ssl_verify.rb +52 -0
  173. data/tests/test_stomp.rb +38 -0
  174. data/tests/test_system.rb +46 -0
  175. data/tests/test_threaded_resource.rb +68 -0
  176. data/tests/test_tick_loop.rb +58 -0
  177. data/tests/test_timers.rb +150 -0
  178. data/tests/test_ud.rb +8 -0
  179. data/tests/test_unbind_reason.rb +40 -0
  180. metadata +384 -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,70 @@
1
+ package com.rubyeventmachine;
2
+
3
+ import java.io.IOException;
4
+ import java.net.InetSocketAddress;
5
+ import java.net.SocketAddress;
6
+ import java.nio.ByteBuffer;
7
+ import java.nio.channels.SocketChannel;
8
+ import java.security.KeyManagementException;
9
+ import java.security.NoSuchAlgorithmException;
10
+
11
+ public interface EmReactorInterface
12
+ {
13
+ void eventCallback (long sig, int eventType, ByteBuffer data, long data2);
14
+
15
+ void eventCallback (long sig, int eventType, ByteBuffer data);
16
+
17
+ void run();
18
+
19
+ void stop();
20
+
21
+ long installOneshotTimer (long milliseconds);
22
+
23
+ long startTcpServer (SocketAddress sa) throws EmReactorException;
24
+
25
+ long startTcpServer (String address, int port) throws EmReactorException;
26
+
27
+ void stopTcpServer (long signature) throws IOException;
28
+
29
+ long openUdpSocket (InetSocketAddress address) throws IOException;
30
+
31
+ long openUdpSocket (String address, int port) throws IOException;
32
+
33
+ void sendData (long sig, ByteBuffer bb) throws IOException;
34
+
35
+ void sendData (long sig, byte[] data) throws IOException;
36
+
37
+ void setCommInactivityTimeout (long sig, long mills);
38
+
39
+ void sendDatagram (long sig, byte[] data, int length, String recipAddress, int recipPort);
40
+
41
+ void sendDatagram (long sig, ByteBuffer bb, String recipAddress, int recipPort);
42
+
43
+ long connectTcpServer (String address, int port);
44
+
45
+ long connectTcpServer (String bindAddr, int bindPort, String address, int port);
46
+
47
+ void closeConnection (long sig, boolean afterWriting);
48
+
49
+ void signalLoopbreak();
50
+
51
+ void startTls (long sig) throws NoSuchAlgorithmException, KeyManagementException;
52
+
53
+ void setTimerQuantum (int mills);
54
+
55
+ Object[] getPeerName (long sig);
56
+
57
+ long attachChannel (SocketChannel sc, boolean watch_mode);
58
+
59
+ SocketChannel detachChannel (long sig);
60
+
61
+ void setNotifyReadable (long sig, boolean mode);
62
+
63
+ void setNotifyWritable (long sig, boolean mode);
64
+
65
+ boolean isNotifyReadable (long sig);
66
+
67
+ boolean isNotifyWritable (long sig);
68
+
69
+ int getConnectionCount();
70
+ }
@@ -0,0 +1,72 @@
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.io.IOException;
34
+ import java.nio.channels.ClosedChannelException;
35
+
36
+ public interface EventableChannel {
37
+
38
+ public void scheduleOutboundData (ByteBuffer bb);
39
+
40
+ public void scheduleOutboundDatagram (ByteBuffer bb, String recipAddress, int recipPort);
41
+
42
+ public boolean scheduleClose (boolean afterWriting);
43
+
44
+ public void startTls();
45
+
46
+ public long getBinding();
47
+
48
+ public void readInboundData (ByteBuffer dst) throws IOException;
49
+
50
+ public void register() throws ClosedChannelException;
51
+
52
+ /**
53
+ * This is called by the reactor after it finishes running.
54
+ * The idea is to free network resources.
55
+ */
56
+ public void close();
57
+
58
+ public boolean writeOutboundData() throws IOException;
59
+
60
+ public long getOutboundDataSize();
61
+
62
+ public void setCommInactivityTimeout (long seconds);
63
+
64
+ public Object[] getPeerName();
65
+ public Object[] getSockName();
66
+
67
+ public boolean isWatchOnly();
68
+
69
+ public boolean isNotifyReadable();
70
+ public boolean isNotifyWritable();
71
+
72
+ }
@@ -0,0 +1,201 @@
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.util.LinkedList;
38
+ import java.io.*;
39
+ import java.net.*;
40
+
41
+ public class EventableDatagramChannel implements EventableChannel {
42
+
43
+ class Packet {
44
+ public ByteBuffer bb;
45
+ public SocketAddress recipient;
46
+ public Packet (ByteBuffer _bb, SocketAddress _recipient) {
47
+ bb = _bb;
48
+ recipient = _recipient;
49
+ }
50
+ }
51
+
52
+ DatagramChannel channel;
53
+ long binding;
54
+ Selector selector;
55
+ boolean bCloseScheduled;
56
+ LinkedList<Packet> outboundQ;
57
+ long outboundS;
58
+ SocketAddress returnAddress;
59
+
60
+
61
+ public EventableDatagramChannel (DatagramChannel dc, long _binding, Selector sel) throws ClosedChannelException {
62
+ channel = dc;
63
+ binding = _binding;
64
+ selector = sel;
65
+ bCloseScheduled = false;
66
+ outboundQ = new LinkedList<Packet>();
67
+ outboundS = 0;
68
+
69
+ dc.register(selector, SelectionKey.OP_READ, this);
70
+ }
71
+
72
+ public void scheduleOutboundData (ByteBuffer bb) {
73
+ try {
74
+ if ((!bCloseScheduled) && (bb.remaining() > 0)) {
75
+ outboundQ.addLast(new Packet(bb, returnAddress));
76
+ outboundS += bb.remaining();
77
+ channel.register(selector, SelectionKey.OP_WRITE | SelectionKey.OP_READ, this);
78
+ }
79
+ } catch (ClosedChannelException e) {
80
+ throw new RuntimeException ("no outbound data");
81
+ }
82
+ }
83
+
84
+ public void scheduleOutboundDatagram (ByteBuffer bb, String recipAddress, int recipPort) {
85
+ try {
86
+ if ((!bCloseScheduled) && (bb.remaining() > 0)) {
87
+ outboundQ.addLast(new Packet (bb, new InetSocketAddress (recipAddress, recipPort)));
88
+ outboundS += bb.remaining();
89
+ channel.register(selector, SelectionKey.OP_WRITE | SelectionKey.OP_READ, this);
90
+ }
91
+ } catch (ClosedChannelException e) {
92
+ throw new RuntimeException ("no outbound data");
93
+ }
94
+ }
95
+
96
+ public boolean scheduleClose (boolean afterWriting) {
97
+ System.out.println ("NOT SCHEDULING CLOSE ON DATAGRAM");
98
+ return false;
99
+ }
100
+
101
+ public void startTls() {
102
+ throw new RuntimeException ("TLS is unimplemented on this Channel");
103
+ }
104
+
105
+ public long getBinding() {
106
+ return binding;
107
+ }
108
+
109
+ public void register() throws ClosedChannelException {
110
+ // TODO
111
+ }
112
+
113
+ /**
114
+ * Terminate with extreme prejudice. Don't assume there will be another pass through
115
+ * the reactor core.
116
+ */
117
+ public void close() {
118
+ try {
119
+ channel.close();
120
+ } catch (IOException e) {
121
+ }
122
+ }
123
+
124
+ public void readInboundData (ByteBuffer dst) {
125
+ returnAddress = null;
126
+ try {
127
+ // If there is no datagram available (we're nonblocking after all),
128
+ // then channel.receive returns null.
129
+ returnAddress = channel.receive(dst);
130
+ } catch (IOException e) {
131
+ // probably a no-op. The caller will see the empty (or even partial) buffer
132
+ // and presumably do the right thing.
133
+ }
134
+ }
135
+
136
+ public boolean writeOutboundData() {
137
+ while (!outboundQ.isEmpty()) {
138
+ Packet p = outboundQ.getFirst();
139
+ int written = 0;
140
+ try {
141
+ // With a datagram socket, it's ok to send an empty buffer.
142
+ written = channel.send(p.bb, p.recipient);
143
+ outboundS -= written;
144
+ }
145
+ catch (IOException e) {
146
+ return false;
147
+ }
148
+
149
+ /* Did we consume the whole outbound buffer? If yes, pop it off and
150
+ * keep looping. If no, the outbound network buffers are full, so break
151
+ * out of here. There's a flaw that affects outbound buffers that are intentionally
152
+ * empty. We can tell whether they got sent or not. So we assume they were.
153
+ * TODO: As implemented, this ALWAYS discards packets if they were at least
154
+ * partially written. This matches the behavior of the C++ EM. My judgment
155
+ * is that this is less surprising than fragmenting the data and sending multiple
156
+ * packets would be. I could be wrong, so this is subject to change.
157
+ */
158
+
159
+ if ((written > 0) || (p.bb.remaining() == 0))
160
+ outboundQ.removeFirst();
161
+ else
162
+ break;
163
+ }
164
+
165
+ if (outboundQ.isEmpty()) {
166
+ try {
167
+ channel.register(selector, SelectionKey.OP_READ, this);
168
+ } catch (ClosedChannelException e) {}
169
+ }
170
+
171
+ // ALWAYS drain the outbound queue before triggering a connection close.
172
+ // If anyone wants to close immediately, they're responsible for clearing
173
+ // the outbound queue.
174
+ return (bCloseScheduled && outboundQ.isEmpty()) ? false : true;
175
+ }
176
+
177
+ public void setCommInactivityTimeout (long seconds) {
178
+ // TODO
179
+ System.out.println ("DATAGRAM: SET COMM INACTIVITY UNIMPLEMENTED " + seconds);
180
+ }
181
+
182
+ public Object[] getPeerName () {
183
+ if (returnAddress != null) {
184
+ InetSocketAddress inetAddr = (InetSocketAddress) returnAddress;
185
+ return new Object[]{ inetAddr.getPort(), inetAddr.getHostName() };
186
+ } else {
187
+ return null;
188
+ }
189
+ }
190
+
191
+ public Object[] getSockName () {
192
+ DatagramSocket socket = channel.socket();
193
+ return new Object[]{ socket.getLocalPort(),
194
+ socket.getLocalAddress().getHostAddress() };
195
+ }
196
+
197
+ public boolean isWatchOnly() { return false; }
198
+ public boolean isNotifyReadable() { return false; }
199
+ public boolean isNotifyWritable() { return false; }
200
+ public long getOutboundDataSize() { return outboundS; }
201
+ }
@@ -0,0 +1,415 @@
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.nio.channels.*;
40
+ import java.nio.*;
41
+ import java.util.*;
42
+ import java.io.*;
43
+ import java.net.Socket;
44
+ import javax.net.ssl.*;
45
+ import javax.net.ssl.SSLEngineResult.*;
46
+ import java.lang.reflect.Field;
47
+
48
+ import java.security.*;
49
+
50
+ public class EventableSocketChannel implements EventableChannel {
51
+ Selector selector;
52
+ SelectionKey channelKey;
53
+ SocketChannel channel;
54
+
55
+ long binding;
56
+ LinkedList<ByteBuffer> outboundQ;
57
+ long outboundS;
58
+
59
+ boolean bCloseScheduled;
60
+ boolean bConnectPending;
61
+ boolean bWatchOnly;
62
+ boolean bAttached;
63
+ boolean bNotifyReadable;
64
+ boolean bNotifyWritable;
65
+ boolean bPaused;
66
+
67
+ SSLEngine sslEngine;
68
+ SSLContext sslContext;
69
+
70
+ public EventableSocketChannel (SocketChannel sc, long _binding, Selector sel) {
71
+ channel = sc;
72
+ binding = _binding;
73
+ selector = sel;
74
+ bCloseScheduled = false;
75
+ bConnectPending = false;
76
+ bWatchOnly = false;
77
+ bAttached = false;
78
+ bNotifyReadable = false;
79
+ bNotifyWritable = false;
80
+ outboundQ = new LinkedList<ByteBuffer>();
81
+ outboundS = 0;
82
+ }
83
+
84
+ public long getBinding() {
85
+ return binding;
86
+ }
87
+
88
+ public SocketChannel getChannel() {
89
+ return channel;
90
+ }
91
+
92
+ public void register() throws ClosedChannelException {
93
+ if (channelKey == null) {
94
+ int events = currentEvents();
95
+ channelKey = channel.register(selector, events, this);
96
+ }
97
+ }
98
+
99
+ /**
100
+ * Terminate with extreme prejudice. Don't assume there will be another pass through
101
+ * the reactor core.
102
+ */
103
+ public void close() {
104
+ if (channelKey != null) {
105
+ channelKey.cancel();
106
+ channelKey = null;
107
+ }
108
+
109
+ if (bAttached) {
110
+ // attached channels are copies, so reset the file descriptor to prevent java from close()ing it
111
+ Field f;
112
+ FileDescriptor fd;
113
+
114
+ try {
115
+ /* do _NOT_ clobber fdVal here, it will break epoll/kqueue on jdk6!
116
+ * channelKey.cancel() above does not occur until the next call to select
117
+ * and if fdVal is gone, we will continue to get events for this fd.
118
+ *
119
+ * instead, remove fdVal in cleanup(), which is processed via DetachedConnections,
120
+ * after UnboundConnections but before NewConnections.
121
+ */
122
+
123
+ f = channel.getClass().getDeclaredField("fd");
124
+ f.setAccessible(true);
125
+ fd = (FileDescriptor) f.get(channel);
126
+
127
+ f = fd.getClass().getDeclaredField("fd");
128
+ f.setAccessible(true);
129
+ f.set(fd, -1);
130
+ } catch (java.lang.NoSuchFieldException e) {
131
+ e.printStackTrace();
132
+ } catch (java.lang.IllegalAccessException e) {
133
+ e.printStackTrace();
134
+ }
135
+
136
+ return;
137
+ }
138
+
139
+ try {
140
+ channel.close();
141
+ } catch (IOException e) {
142
+ }
143
+ }
144
+
145
+ public void cleanup() {
146
+ if (bAttached) {
147
+ Field f;
148
+ try {
149
+ f = channel.getClass().getDeclaredField("fdVal");
150
+ f.setAccessible(true);
151
+ f.set(channel, -1);
152
+ } catch (java.lang.NoSuchFieldException e) {
153
+ e.printStackTrace();
154
+ } catch (java.lang.IllegalAccessException e) {
155
+ e.printStackTrace();
156
+ }
157
+ }
158
+
159
+ channel = null;
160
+ }
161
+
162
+ public void scheduleOutboundData (ByteBuffer bb) {
163
+ if (!bCloseScheduled && bb.remaining() > 0) {
164
+ if (sslEngine != null) {
165
+ try {
166
+ ByteBuffer b = ByteBuffer.allocate(32*1024); // TODO, preallocate this buffer.
167
+ sslEngine.wrap(bb, b);
168
+ b.flip();
169
+ outboundQ.addLast(b);
170
+ outboundS += b.remaining();
171
+ } catch (SSLException e) {
172
+ throw new RuntimeException ("ssl error");
173
+ }
174
+ }
175
+ else {
176
+ outboundQ.addLast(bb);
177
+ outboundS += bb.remaining();
178
+ }
179
+
180
+ updateEvents();
181
+ }
182
+ }
183
+
184
+ public void scheduleOutboundDatagram (ByteBuffer bb, String recipAddress, int recipPort) {
185
+ throw new RuntimeException ("datagram sends not supported on this channel");
186
+ }
187
+
188
+ /**
189
+ * Called by the reactor when we have selected readable.
190
+ */
191
+ public void readInboundData (ByteBuffer bb) throws IOException {
192
+ if (channel.read(bb) == -1)
193
+ throw new IOException ("eof");
194
+ }
195
+
196
+ public long getOutboundDataSize() { return outboundS; }
197
+
198
+ /**
199
+ * Called by the reactor when we have selected writable.
200
+ * Return false to indicate an error that should cause the connection to close.
201
+ * TODO, VERY IMPORTANT: we're here because we selected writable, but it's always
202
+ * possible to become unwritable between the poll and when we get here. The way
203
+ * this code is written, we're depending on a nonblocking write NOT TO CONSUME
204
+ * the whole outbound buffer in this case, rather than firing an exception.
205
+ * We should somehow verify that this is indeed Java's defined behavior.
206
+ * @return
207
+ */
208
+ public boolean writeOutboundData() throws IOException {
209
+ ByteBuffer[] bufs = new ByteBuffer[64];
210
+ int i;
211
+ long written, toWrite;
212
+ while (!outboundQ.isEmpty()) {
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);
223
+
224
+ outboundS -= written;
225
+ // Did we consume the whole outbound buffer? If yes,
226
+ // pop it off and keep looping. If no, the outbound network
227
+ // buffers are full, so break out of here.
228
+ if (written < toWrite) {
229
+ while (i > 0 && bufs[i-1].remaining() > 0) {
230
+ outboundQ.addFirst(bufs[i-1]);
231
+ i--;
232
+ }
233
+ break;
234
+ }
235
+ }
236
+
237
+ if (outboundQ.isEmpty() && !bCloseScheduled) {
238
+ updateEvents();
239
+ }
240
+
241
+ // ALWAYS drain the outbound queue before triggering a connection close.
242
+ // If anyone wants to close immediately, they're responsible for clearing
243
+ // the outbound queue.
244
+ return (bCloseScheduled && outboundQ.isEmpty()) ? false : true;
245
+ }
246
+
247
+ public void setConnectPending() {
248
+ bConnectPending = true;
249
+ updateEvents();
250
+ }
251
+
252
+ /**
253
+ * Called by the reactor when we have selected connectable.
254
+ * Return false to indicate an error that should cause the connection to close.
255
+ */
256
+ public boolean finishConnecting() throws IOException {
257
+ channel.finishConnect();
258
+
259
+ bConnectPending = false;
260
+ updateEvents();
261
+ return true;
262
+ }
263
+
264
+ public boolean scheduleClose (boolean afterWriting) {
265
+ // TODO: What the hell happens here if bConnectPending is set?
266
+ if (!afterWriting) {
267
+ outboundQ.clear();
268
+ outboundS = 0;
269
+ }
270
+
271
+ if (outboundQ.isEmpty())
272
+ return true;
273
+ else {
274
+ updateEvents();
275
+ bCloseScheduled = true;
276
+ return false;
277
+ }
278
+ }
279
+
280
+ public void startTls() {
281
+ if (sslEngine == null) {
282
+ try {
283
+ sslContext = SSLContext.getInstance("TLS");
284
+ sslContext.init(null, null, null); // TODO, fill in the parameters.
285
+ sslEngine = sslContext.createSSLEngine(); // TODO, should use the parameterized version, to get Kerb stuff and session re-use.
286
+ sslEngine.setUseClientMode(false);
287
+ } catch (NoSuchAlgorithmException e) {
288
+ throw new RuntimeException ("unable to start TLS"); // TODO, get rid of this.
289
+ } catch (KeyManagementException e) {
290
+ throw new RuntimeException ("unable to start TLS"); // TODO, get rid of this.
291
+ }
292
+ }
293
+ System.out.println ("Starting TLS");
294
+ }
295
+
296
+ public ByteBuffer dispatchInboundData (ByteBuffer bb) throws SSLException {
297
+ if (sslEngine != null) {
298
+ if (true) throw new RuntimeException ("TLS currently unimplemented");
299
+ System.setProperty("javax.net.debug", "all");
300
+ ByteBuffer w = ByteBuffer.allocate(32*1024); // TODO, WRONG, preallocate this buffer.
301
+ SSLEngineResult res = sslEngine.unwrap(bb, w);
302
+ if (res.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
303
+ Runnable r;
304
+ while ((r = sslEngine.getDelegatedTask()) != null) {
305
+ r.run();
306
+ }
307
+ }
308
+ System.out.println (bb);
309
+ w.flip();
310
+ return w;
311
+ }
312
+ else
313
+ return bb;
314
+ }
315
+
316
+ public void setCommInactivityTimeout (long seconds) {
317
+ // TODO
318
+ System.out.println ("SOCKET: SET COMM INACTIVITY UNIMPLEMENTED " + seconds);
319
+ }
320
+
321
+ public Object[] getPeerName () {
322
+ Socket sock = channel.socket();
323
+ return new Object[]{ sock.getPort(), sock.getInetAddress().getHostAddress() };
324
+ }
325
+
326
+ public Object[] getSockName () {
327
+ Socket sock = channel.socket();
328
+ return new Object[]{ sock.getLocalPort(),
329
+ sock.getLocalAddress().getHostAddress() };
330
+ }
331
+
332
+ public void setWatchOnly() {
333
+ bWatchOnly = true;
334
+ updateEvents();
335
+ }
336
+ public boolean isWatchOnly() { return bWatchOnly; }
337
+
338
+ public void setAttached() {
339
+ bAttached = true;
340
+ }
341
+ public boolean isAttached() { return bAttached; }
342
+
343
+ public void setNotifyReadable (boolean mode) {
344
+ bNotifyReadable = mode;
345
+ updateEvents();
346
+ }
347
+ public boolean isNotifyReadable() { return bNotifyReadable; }
348
+
349
+ public void setNotifyWritable (boolean mode) {
350
+ bNotifyWritable = mode;
351
+ updateEvents();
352
+ }
353
+ public boolean isNotifyWritable() { return bNotifyWritable; }
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
+
379
+ private void updateEvents() {
380
+ if (channelKey == null)
381
+ return;
382
+
383
+ int events = currentEvents();
384
+
385
+ if (channelKey.interestOps() != events) {
386
+ channelKey.interestOps(events);
387
+ }
388
+ }
389
+
390
+ private int currentEvents() {
391
+ int events = 0;
392
+
393
+ if (bWatchOnly)
394
+ {
395
+ if (bNotifyReadable)
396
+ events |= SelectionKey.OP_READ;
397
+
398
+ if (bNotifyWritable)
399
+ events |= SelectionKey.OP_WRITE;
400
+ }
401
+ else if (!bPaused)
402
+ {
403
+ if (bConnectPending)
404
+ events |= SelectionKey.OP_CONNECT;
405
+ else {
406
+ events |= SelectionKey.OP_READ;
407
+
408
+ if (!outboundQ.isEmpty())
409
+ events |= SelectionKey.OP_WRITE;
410
+ }
411
+ }
412
+
413
+ return events;
414
+ }
415
+ }