sonixlabs-eventmachine-java 1.0.0.rc.7-java → 1.0.3.1-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 -12
- data/.travis.yml +12 -0
- data/CHANGELOG.md +33 -0
- data/Gemfile +0 -1
- data/README.md +2 -2
- data/README_JP.md +10 -2
- data/eventmachine.gemspec +10 -7
- data/ext/binder.cpp +1 -1
- data/ext/cmain.cpp +11 -0
- data/ext/ed.cpp +22 -7
- data/ext/ed.h +5 -5
- data/ext/em.cpp +64 -66
- data/ext/em.h +10 -5
- data/ext/eventmachine.h +1 -0
- data/ext/extconf.rb +4 -3
- data/ext/fastfilereader/extconf.rb +1 -1
- data/ext/rubymain.cpp +22 -1
- data/ext/ssl.cpp +1 -1
- data/java/.classpath +1 -3
- data/java/.gitignore +1 -0
- data/java/src/com/rubyeventmachine/DatagramPacket.java +13 -0
- data/java/src/com/rubyeventmachine/EmReactor.java +502 -561
- 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 +102 -42
- data/java/src/com/rubyeventmachine/EventableDatagramChannel.java +146 -161
- data/java/src/com/rubyeventmachine/EventableSocketChannel.java +371 -334
- data/java/src/com/rubyeventmachine/SslBox.java +310 -0
- data/lib/em/iterator.rb +5 -44
- data/lib/em/processes.rb +1 -1
- data/lib/em/protocols/httpclient.rb +2 -2
- data/lib/em/protocols/line_protocol.rb +2 -2
- data/lib/em/protocols/smtpclient.rb +1 -1
- data/lib/em/protocols/smtpserver.rb +10 -7
- data/lib/em/protocols/stomp.rb +5 -2
- data/lib/em/protocols.rb +1 -0
- data/lib/em/version.rb +1 -1
- data/lib/eventmachine.rb +35 -14
- data/lib/jeventmachine.rb +65 -18
- data/rakelib/package.rake +15 -18
- data/tests/server.crt +36 -0
- data/tests/server.key +51 -0
- data/tests/test_attach.rb +24 -0
- data/tests/test_connection_count.rb +21 -1
- data/tests/test_epoll.rb +15 -0
- data/tests/test_httpclient2.rb +5 -0
- data/tests/test_idle_connection.rb +6 -4
- data/tests/test_iterator.rb +97 -0
- data/tests/test_line_protocol.rb +33 -0
- data/tests/test_pause.rb +24 -0
- data/tests/test_set_sock_opt.rb +1 -1
- data/tests/test_ssl_echo_data.rb +60 -0
- data/tests/test_ssl_methods.rb +15 -7
- data/tests/test_ssl_verify.rb +4 -4
- data/tests/test_stomp.rb +37 -0
- data/tests/test_system.rb +42 -0
- metadata +45 -42
- data/lib/rubyeventmachine.jar +0 -0
@@ -1,33 +1,33 @@
|
|
1
1
|
/**
|
2
2
|
* $Id$
|
3
|
-
*
|
3
|
+
*
|
4
4
|
* Author:: Francis Cianfrocca (gmail: blackhedd)
|
5
5
|
* Homepage:: http://rubyeventmachine.com
|
6
6
|
* Date:: 15 Jul 2007
|
7
|
-
*
|
7
|
+
*
|
8
8
|
* See EventMachine and EventMachine::Connection for documentation and
|
9
9
|
* usage examples.
|
10
|
-
*
|
10
|
+
*
|
11
11
|
*
|
12
12
|
*----------------------------------------------------------------------------
|
13
13
|
*
|
14
14
|
* Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
|
15
15
|
* Gmail: blackhedd
|
16
|
-
*
|
16
|
+
*
|
17
17
|
* This program is free software; you can redistribute it and/or modify
|
18
18
|
* it under the terms of either: 1) the GNU General Public License
|
19
19
|
* as published by the Free Software Foundation; either version 2 of the
|
20
20
|
* License, or (at your option) any later version; or 2) Ruby's License.
|
21
|
-
*
|
21
|
+
*
|
22
22
|
* See the file COPYING for complete licensing information.
|
23
23
|
*
|
24
24
|
*---------------------------------------------------------------------------
|
25
25
|
*
|
26
|
-
*
|
26
|
+
*
|
27
27
|
*/
|
28
28
|
|
29
29
|
/**
|
30
|
-
*
|
30
|
+
*
|
31
31
|
*/
|
32
32
|
package com.rubyeventmachine;
|
33
33
|
|
@@ -36,333 +36,370 @@ package com.rubyeventmachine;
|
|
36
36
|
*
|
37
37
|
*/
|
38
38
|
|
39
|
-
import java.
|
40
|
-
import java.
|
41
|
-
import java.util.*;
|
42
|
-
import java.io.*;
|
43
|
-
import java.net.Socket;
|
44
|
-
import javax.net.ssl.*;
|
45
|
-
import javax.net.ssl.SSLEngineResult.*;
|
39
|
+
import java.io.FileDescriptor;
|
40
|
+
import java.io.IOException;
|
46
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
|
+
|
47
404
|
|
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
|
-
|
58
|
-
boolean bCloseScheduled;
|
59
|
-
boolean bConnectPending;
|
60
|
-
boolean bWatchOnly;
|
61
|
-
boolean bAttached;
|
62
|
-
boolean bNotifyReadable;
|
63
|
-
boolean bNotifyWritable;
|
64
|
-
|
65
|
-
SSLEngine sslEngine;
|
66
|
-
SSLContext sslContext;
|
67
|
-
|
68
|
-
public EventableSocketChannel (SocketChannel sc, long _binding, Selector sel) {
|
69
|
-
channel = sc;
|
70
|
-
binding = _binding;
|
71
|
-
selector = sel;
|
72
|
-
bCloseScheduled = false;
|
73
|
-
bConnectPending = false;
|
74
|
-
bWatchOnly = false;
|
75
|
-
bAttached = false;
|
76
|
-
bNotifyReadable = false;
|
77
|
-
bNotifyWritable = false;
|
78
|
-
outboundQ = new LinkedList<ByteBuffer>();
|
79
|
-
}
|
80
|
-
|
81
|
-
public long getBinding() {
|
82
|
-
return binding;
|
83
|
-
}
|
84
|
-
|
85
|
-
public SocketChannel getChannel() {
|
86
|
-
return channel;
|
87
|
-
}
|
88
|
-
|
89
|
-
public void register() throws ClosedChannelException {
|
90
|
-
if (channelKey == null) {
|
91
|
-
int events = currentEvents();
|
92
|
-
channelKey = channel.register(selector, events, this);
|
93
|
-
}
|
94
|
-
}
|
95
|
-
|
96
|
-
/**
|
97
|
-
* Terminate with extreme prejudice. Don't assume there will be another pass through
|
98
|
-
* the reactor core.
|
99
|
-
*/
|
100
|
-
public void close() {
|
101
|
-
if (channelKey != null) {
|
102
|
-
channelKey.cancel();
|
103
|
-
channelKey = null;
|
104
|
-
}
|
105
|
-
|
106
|
-
if (bAttached) {
|
107
|
-
// attached channels are copies, so reset the file descriptor to prevent java from close()ing it
|
108
|
-
Field f;
|
109
|
-
FileDescriptor fd;
|
110
|
-
|
111
|
-
try {
|
112
|
-
/* do _NOT_ clobber fdVal here, it will break epoll/kqueue on jdk6!
|
113
|
-
* channelKey.cancel() above does not occur until the next call to select
|
114
|
-
* and if fdVal is gone, we will continue to get events for this fd.
|
115
|
-
*
|
116
|
-
* instead, remove fdVal in cleanup(), which is processed via DetachedConnections,
|
117
|
-
* after UnboundConnections but before NewConnections.
|
118
|
-
*/
|
119
|
-
|
120
|
-
f = channel.getClass().getDeclaredField("fd");
|
121
|
-
f.setAccessible(true);
|
122
|
-
fd = (FileDescriptor) f.get(channel);
|
123
|
-
|
124
|
-
f = fd.getClass().getDeclaredField("fd");
|
125
|
-
f.setAccessible(true);
|
126
|
-
f.set(fd, -1);
|
127
|
-
} catch (java.lang.NoSuchFieldException e) {
|
128
|
-
e.printStackTrace();
|
129
|
-
} catch (java.lang.IllegalAccessException e) {
|
130
|
-
e.printStackTrace();
|
131
|
-
}
|
132
|
-
|
133
|
-
return;
|
134
|
-
}
|
135
|
-
|
136
|
-
try {
|
137
|
-
channel.close();
|
138
|
-
} catch (IOException e) {
|
139
|
-
}
|
140
|
-
}
|
141
|
-
|
142
|
-
public void cleanup() {
|
143
|
-
if (bAttached) {
|
144
|
-
Field f;
|
145
|
-
try {
|
146
|
-
f = channel.getClass().getDeclaredField("fdVal");
|
147
|
-
f.setAccessible(true);
|
148
|
-
f.set(channel, -1);
|
149
|
-
} catch (java.lang.NoSuchFieldException e) {
|
150
|
-
e.printStackTrace();
|
151
|
-
} catch (java.lang.IllegalAccessException e) {
|
152
|
-
e.printStackTrace();
|
153
|
-
}
|
154
|
-
}
|
155
|
-
|
156
|
-
channel = null;
|
157
|
-
}
|
158
|
-
|
159
|
-
public void scheduleOutboundData (ByteBuffer bb) {
|
160
|
-
if (!bCloseScheduled && bb.remaining() > 0) {
|
161
|
-
if (sslEngine != null) {
|
162
|
-
try {
|
163
|
-
ByteBuffer b = ByteBuffer.allocate(32*1024); // TODO, preallocate this buffer.
|
164
|
-
sslEngine.wrap(bb, b);
|
165
|
-
b.flip();
|
166
|
-
outboundQ.addLast(b);
|
167
|
-
} catch (SSLException e) {
|
168
|
-
throw new RuntimeException ("ssl error");
|
169
|
-
}
|
170
|
-
}
|
171
|
-
else {
|
172
|
-
outboundQ.addLast(bb);
|
173
|
-
}
|
174
|
-
|
175
|
-
updateEvents();
|
176
|
-
}
|
177
|
-
}
|
178
|
-
|
179
|
-
public void scheduleOutboundDatagram (ByteBuffer bb, String recipAddress, int recipPort) {
|
180
|
-
throw new RuntimeException ("datagram sends not supported on this channel");
|
181
|
-
}
|
182
|
-
|
183
|
-
/**
|
184
|
-
* Called by the reactor when we have selected readable.
|
185
|
-
*/
|
186
|
-
public void readInboundData (ByteBuffer bb) throws IOException {
|
187
|
-
if (channel.read(bb) == -1)
|
188
|
-
throw new IOException ("eof");
|
189
|
-
}
|
190
|
-
|
191
|
-
/**
|
192
|
-
* Called by the reactor when we have selected writable.
|
193
|
-
* Return false to indicate an error that should cause the connection to close.
|
194
|
-
* TODO, VERY IMPORTANT: we're here because we selected writable, but it's always
|
195
|
-
* possible to become unwritable between the poll and when we get here. The way
|
196
|
-
* this code is written, we're depending on a nonblocking write NOT TO CONSUME
|
197
|
-
* the whole outbound buffer in this case, rather than firing an exception.
|
198
|
-
* 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
|
-
* @return
|
202
|
-
*/
|
203
|
-
synchronized public boolean writeOutboundData() throws IOException {
|
204
|
-
try {
|
205
|
-
while (!outboundQ.isEmpty()) {
|
206
|
-
ByteBuffer b = outboundQ.getFirst();
|
207
|
-
while (b.remaining() > 0) {
|
208
|
-
channel.write(b);
|
209
|
-
}
|
210
|
-
outboundQ.removeFirst();
|
211
|
-
}
|
212
|
-
} catch (IOException e) {
|
213
|
-
System.out.println("IOException in [eventmachine] EventableSocketChannel#writeOutboundData");
|
214
|
-
}
|
215
|
-
|
216
|
-
if (outboundQ.isEmpty() && !bCloseScheduled) {
|
217
|
-
updateEvents();
|
218
|
-
}
|
219
|
-
|
220
|
-
// ALWAYS drain the outbound queue before triggering a connection close.
|
221
|
-
// If anyone wants to close immediately, they're responsible for clearing
|
222
|
-
// the outbound queue.
|
223
|
-
return (bCloseScheduled && outboundQ.isEmpty()) ? false : true;
|
224
|
-
}
|
225
|
-
|
226
|
-
public void setConnectPending() {
|
227
|
-
bConnectPending = true;
|
228
|
-
updateEvents();
|
229
|
-
}
|
230
|
-
|
231
|
-
/**
|
232
|
-
* Called by the reactor when we have selected connectable.
|
233
|
-
* Return false to indicate an error that should cause the connection to close.
|
234
|
-
*/
|
235
|
-
public boolean finishConnecting() throws IOException {
|
236
|
-
channel.finishConnect();
|
237
|
-
|
238
|
-
bConnectPending = false;
|
239
|
-
updateEvents();
|
240
|
-
return true;
|
241
|
-
}
|
242
|
-
|
243
|
-
public boolean scheduleClose (boolean afterWriting) {
|
244
|
-
// TODO: What the hell happens here if bConnectPending is set?
|
245
|
-
if (!afterWriting)
|
246
|
-
outboundQ.clear();
|
247
|
-
|
248
|
-
if (outboundQ.isEmpty())
|
249
|
-
return true;
|
250
|
-
else {
|
251
|
-
updateEvents();
|
252
|
-
bCloseScheduled = true;
|
253
|
-
return false;
|
254
|
-
}
|
255
|
-
}
|
256
|
-
|
257
|
-
public void startTls() {
|
258
|
-
if (sslEngine == null) {
|
259
|
-
try {
|
260
|
-
sslContext = SSLContext.getInstance("TLS");
|
261
|
-
sslContext.init(null, null, null); // TODO, fill in the parameters.
|
262
|
-
sslEngine = sslContext.createSSLEngine(); // TODO, should use the parameterized version, to get Kerb stuff and session re-use.
|
263
|
-
sslEngine.setUseClientMode(false);
|
264
|
-
} catch (NoSuchAlgorithmException e) {
|
265
|
-
throw new RuntimeException ("unable to start TLS"); // TODO, get rid of this.
|
266
|
-
} catch (KeyManagementException e) {
|
267
|
-
throw new RuntimeException ("unable to start TLS"); // TODO, get rid of this.
|
268
|
-
}
|
269
|
-
}
|
270
|
-
System.out.println ("Starting TLS");
|
271
|
-
}
|
272
|
-
|
273
|
-
public ByteBuffer dispatchInboundData (ByteBuffer bb) throws SSLException {
|
274
|
-
if (sslEngine != null) {
|
275
|
-
if (true) throw new RuntimeException ("TLS currently unimplemented");
|
276
|
-
System.setProperty("javax.net.debug", "all");
|
277
|
-
ByteBuffer w = ByteBuffer.allocate(32*1024); // TODO, WRONG, preallocate this buffer.
|
278
|
-
SSLEngineResult res = sslEngine.unwrap(bb, w);
|
279
|
-
if (res.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
|
280
|
-
Runnable r;
|
281
|
-
while ((r = sslEngine.getDelegatedTask()) != null) {
|
282
|
-
r.run();
|
283
|
-
}
|
284
|
-
}
|
285
|
-
System.out.println (bb);
|
286
|
-
w.flip();
|
287
|
-
return w;
|
288
|
-
}
|
289
|
-
else
|
290
|
-
return bb;
|
291
|
-
}
|
292
|
-
|
293
|
-
public void setCommInactivityTimeout (long seconds) {
|
294
|
-
// TODO
|
295
|
-
System.out.println ("SOCKET: SET COMM INACTIVITY UNIMPLEMENTED " + seconds);
|
296
|
-
}
|
297
|
-
|
298
|
-
public Object[] getPeerName () {
|
299
|
-
Socket sock = channel.socket();
|
300
|
-
return new Object[]{ sock.getPort(), sock.getInetAddress().getHostAddress() };
|
301
|
-
}
|
302
|
-
|
303
|
-
public Object[] getSockName () {
|
304
|
-
Socket sock = channel.socket();
|
305
|
-
return new Object[]{ sock.getLocalPort(),
|
306
|
-
sock.getLocalAddress().getHostAddress() };
|
307
|
-
}
|
308
|
-
|
309
|
-
public void setWatchOnly() {
|
310
|
-
bWatchOnly = true;
|
311
|
-
updateEvents();
|
312
|
-
}
|
313
|
-
public boolean isWatchOnly() { return bWatchOnly; }
|
314
|
-
|
315
|
-
public void setAttached() {
|
316
|
-
bAttached = true;
|
317
|
-
}
|
318
|
-
public boolean isAttached() { return bAttached; }
|
319
|
-
|
320
|
-
public void setNotifyReadable (boolean mode) {
|
321
|
-
bNotifyReadable = mode;
|
322
|
-
updateEvents();
|
323
|
-
}
|
324
|
-
public boolean isNotifyReadable() { return bNotifyReadable; }
|
325
|
-
|
326
|
-
public void setNotifyWritable (boolean mode) {
|
327
|
-
bNotifyWritable = mode;
|
328
|
-
updateEvents();
|
329
|
-
}
|
330
|
-
public boolean isNotifyWritable() { return bNotifyWritable; }
|
331
|
-
|
332
|
-
private void updateEvents() {
|
333
|
-
if (channelKey == null)
|
334
|
-
return;
|
335
|
-
|
336
|
-
int events = currentEvents();
|
337
|
-
|
338
|
-
if (channelKey.interestOps() != events) {
|
339
|
-
channelKey.interestOps(events);
|
340
|
-
}
|
341
|
-
}
|
342
|
-
|
343
|
-
private int currentEvents() {
|
344
|
-
int events = 0;
|
345
|
-
|
346
|
-
if (bWatchOnly)
|
347
|
-
{
|
348
|
-
if (bNotifyReadable)
|
349
|
-
events |= SelectionKey.OP_READ;
|
350
|
-
|
351
|
-
if (bNotifyWritable)
|
352
|
-
events |= SelectionKey.OP_WRITE;
|
353
|
-
}
|
354
|
-
else
|
355
|
-
{
|
356
|
-
if (bConnectPending)
|
357
|
-
events |= SelectionKey.OP_CONNECT;
|
358
|
-
else {
|
359
|
-
events |= SelectionKey.OP_READ;
|
360
|
-
|
361
|
-
if (!outboundQ.isEmpty())
|
362
|
-
events |= SelectionKey.OP_WRITE;
|
363
|
-
}
|
364
|
-
}
|
365
|
-
|
366
|
-
return events;
|
367
|
-
}
|
368
405
|
}
|