eventmachine-eventmachine 0.12.3
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.
- data/Rakefile +169 -0
- data/docs/COPYING +60 -0
- data/docs/ChangeLog +183 -0
- data/docs/DEFERRABLES +138 -0
- data/docs/EPOLL +141 -0
- data/docs/GNU +281 -0
- data/docs/INSTALL +15 -0
- data/docs/KEYBOARD +38 -0
- data/docs/LEGAL +25 -0
- data/docs/LIGHTWEIGHT_CONCURRENCY +72 -0
- data/docs/PURE_RUBY +77 -0
- data/docs/README +74 -0
- data/docs/RELEASE_NOTES +96 -0
- data/docs/SMTP +9 -0
- data/docs/SPAWNED_PROCESSES +93 -0
- data/docs/TODO +10 -0
- data/ext/binder.cpp +126 -0
- data/ext/binder.h +48 -0
- data/ext/cmain.cpp +530 -0
- data/ext/cplusplus.cpp +172 -0
- data/ext/ed.cpp +1473 -0
- data/ext/ed.h +361 -0
- data/ext/em.cpp +1895 -0
- data/ext/em.h +170 -0
- data/ext/emwin.cpp +300 -0
- data/ext/emwin.h +94 -0
- data/ext/epoll.cpp +26 -0
- data/ext/epoll.h +25 -0
- data/ext/eventmachine.h +90 -0
- data/ext/eventmachine_cpp.h +94 -0
- data/ext/extconf.rb +150 -0
- data/ext/files.cpp +94 -0
- data/ext/files.h +65 -0
- data/ext/kb.cpp +368 -0
- data/ext/page.cpp +107 -0
- data/ext/page.h +51 -0
- data/ext/pipe.cpp +327 -0
- data/ext/project.h +119 -0
- data/ext/rubymain.cpp +683 -0
- data/ext/sigs.cpp +89 -0
- data/ext/sigs.h +32 -0
- data/ext/ssl.cpp +408 -0
- data/ext/ssl.h +86 -0
- data/java/src/com/rubyeventmachine/Application.java +196 -0
- data/java/src/com/rubyeventmachine/Connection.java +74 -0
- data/java/src/com/rubyeventmachine/ConnectionFactory.java +37 -0
- data/java/src/com/rubyeventmachine/DefaultConnectionFactory.java +46 -0
- data/java/src/com/rubyeventmachine/EmReactor.java +408 -0
- data/java/src/com/rubyeventmachine/EmReactorException.java +40 -0
- data/java/src/com/rubyeventmachine/EventableChannel.java +57 -0
- data/java/src/com/rubyeventmachine/EventableDatagramChannel.java +171 -0
- data/java/src/com/rubyeventmachine/EventableSocketChannel.java +244 -0
- data/java/src/com/rubyeventmachine/PeriodicTimer.java +38 -0
- data/java/src/com/rubyeventmachine/Timer.java +54 -0
- data/java/src/com/rubyeventmachine/tests/ApplicationTest.java +108 -0
- data/java/src/com/rubyeventmachine/tests/ConnectTest.java +124 -0
- data/java/src/com/rubyeventmachine/tests/EMTest.java +80 -0
- data/java/src/com/rubyeventmachine/tests/TestDatagrams.java +53 -0
- data/java/src/com/rubyeventmachine/tests/TestServers.java +74 -0
- data/java/src/com/rubyeventmachine/tests/TestTimers.java +89 -0
- data/lib/em/deferrable.rb +208 -0
- data/lib/em/eventable.rb +39 -0
- data/lib/em/future.rb +62 -0
- data/lib/em/messages.rb +66 -0
- data/lib/em/processes.rb +68 -0
- data/lib/em/spawnable.rb +88 -0
- data/lib/em/streamer.rb +112 -0
- data/lib/eventmachine.rb +1763 -0
- data/lib/eventmachine_version.rb +31 -0
- data/lib/evma.rb +32 -0
- data/lib/evma/callback.rb +32 -0
- data/lib/evma/container.rb +75 -0
- data/lib/evma/factory.rb +77 -0
- data/lib/evma/protocol.rb +87 -0
- data/lib/evma/reactor.rb +48 -0
- data/lib/jeventmachine.rb +137 -0
- data/lib/pr_eventmachine.rb +1011 -0
- data/lib/protocols/buftok.rb +127 -0
- data/lib/protocols/header_and_content.rb +129 -0
- data/lib/protocols/httpcli2.rb +794 -0
- data/lib/protocols/httpclient.rb +270 -0
- data/lib/protocols/line_and_text.rb +122 -0
- data/lib/protocols/linetext2.rb +163 -0
- data/lib/protocols/postgres.rb +261 -0
- data/lib/protocols/saslauth.rb +179 -0
- data/lib/protocols/smtpclient.rb +308 -0
- data/lib/protocols/smtpserver.rb +556 -0
- data/lib/protocols/stomp.rb +130 -0
- data/lib/protocols/tcptest.rb +57 -0
- data/tasks/cpp.rake +77 -0
- data/tasks/project.rake +78 -0
- data/tasks/tests.rake +192 -0
- data/tests/test_attach.rb +66 -0
- data/tests/test_basic.rb +231 -0
- data/tests/test_defer.rb +47 -0
- data/tests/test_epoll.rb +161 -0
- data/tests/test_errors.rb +82 -0
- data/tests/test_eventables.rb +78 -0
- data/tests/test_exc.rb +58 -0
- data/tests/test_futures.rb +214 -0
- data/tests/test_hc.rb +218 -0
- data/tests/test_httpclient.rb +215 -0
- data/tests/test_httpclient2.rb +133 -0
- data/tests/test_kb.rb +61 -0
- data/tests/test_ltp.rb +192 -0
- data/tests/test_ltp2.rb +320 -0
- data/tests/test_next_tick.rb +102 -0
- data/tests/test_processes.rb +56 -0
- data/tests/test_pure.rb +129 -0
- data/tests/test_running.rb +47 -0
- data/tests/test_sasl.rb +74 -0
- data/tests/test_send_file.rb +245 -0
- data/tests/test_servers.rb +80 -0
- data/tests/test_smtpclient.rb +81 -0
- data/tests/test_smtpserver.rb +93 -0
- data/tests/test_spawn.rb +329 -0
- data/tests/test_ssl_args.rb +68 -0
- data/tests/test_timers.rb +146 -0
- data/tests/test_ud.rb +43 -0
- data/tests/testem.rb +31 -0
- metadata +197 -0
data/ext/ssl.h
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/*****************************************************************************
|
|
2
|
+
|
|
3
|
+
$Id$
|
|
4
|
+
|
|
5
|
+
File: ssl.h
|
|
6
|
+
Date: 30Apr06
|
|
7
|
+
|
|
8
|
+
Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
|
|
9
|
+
Gmail: blackhedd
|
|
10
|
+
|
|
11
|
+
This program is free software; you can redistribute it and/or modify
|
|
12
|
+
it under the terms of either: 1) the GNU General Public License
|
|
13
|
+
as published by the Free Software Foundation; either version 2 of the
|
|
14
|
+
License, or (at your option) any later version; or 2) Ruby's License.
|
|
15
|
+
|
|
16
|
+
See the file COPYING for complete licensing information.
|
|
17
|
+
|
|
18
|
+
*****************************************************************************/
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
#ifndef __SslBox__H_
|
|
22
|
+
#define __SslBox__H_
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
#ifdef WITH_SSL
|
|
28
|
+
|
|
29
|
+
/******************
|
|
30
|
+
class SslContext_t
|
|
31
|
+
******************/
|
|
32
|
+
|
|
33
|
+
class SslContext_t
|
|
34
|
+
{
|
|
35
|
+
public:
|
|
36
|
+
SslContext_t (bool is_server, const string &privkeyfile, const string &certchainfile);
|
|
37
|
+
virtual ~SslContext_t();
|
|
38
|
+
|
|
39
|
+
private:
|
|
40
|
+
static bool bLibraryInitialized;
|
|
41
|
+
|
|
42
|
+
private:
|
|
43
|
+
bool bIsServer;
|
|
44
|
+
SSL_CTX *pCtx;
|
|
45
|
+
|
|
46
|
+
EVP_PKEY *PrivateKey;
|
|
47
|
+
X509 *Certificate;
|
|
48
|
+
|
|
49
|
+
friend class SslBox_t;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
/**************
|
|
54
|
+
class SslBox_t
|
|
55
|
+
**************/
|
|
56
|
+
|
|
57
|
+
class SslBox_t
|
|
58
|
+
{
|
|
59
|
+
public:
|
|
60
|
+
SslBox_t (bool is_server, const string &privkeyfile, const string &certchainfile);
|
|
61
|
+
virtual ~SslBox_t();
|
|
62
|
+
|
|
63
|
+
int PutPlaintext (const char*, int);
|
|
64
|
+
int GetPlaintext (char*, int);
|
|
65
|
+
|
|
66
|
+
bool PutCiphertext (const char*, int);
|
|
67
|
+
bool CanGetCiphertext();
|
|
68
|
+
int GetCiphertext (char*, int);
|
|
69
|
+
|
|
70
|
+
void Shutdown();
|
|
71
|
+
|
|
72
|
+
protected:
|
|
73
|
+
SslContext_t *Context;
|
|
74
|
+
|
|
75
|
+
bool bIsServer;
|
|
76
|
+
SSL *pSSL;
|
|
77
|
+
BIO *pbioRead;
|
|
78
|
+
BIO *pbioWrite;
|
|
79
|
+
|
|
80
|
+
PageList OutboundQ;
|
|
81
|
+
};
|
|
82
|
+
#endif // WITH_SSL
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
#endif // __SslBox__H_
|
|
86
|
+
|
|
@@ -0,0 +1,196 @@
|
|
|
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
|
+
import java.nio.ByteBuffer;
|
|
35
|
+
import java.nio.channels.*;
|
|
36
|
+
import java.util.*;
|
|
37
|
+
import java.io.*;
|
|
38
|
+
import java.net.*;
|
|
39
|
+
import java.net.SocketAddress;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* @author francis
|
|
43
|
+
*
|
|
44
|
+
*/
|
|
45
|
+
public class Application {
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
public class Reactor extends EmReactor {
|
|
49
|
+
|
|
50
|
+
private Application application;
|
|
51
|
+
private TreeMap<String, Timer> timers;
|
|
52
|
+
private TreeMap<String, Connection> connections;
|
|
53
|
+
private TreeMap<String, ConnectionFactory> acceptors;
|
|
54
|
+
/**
|
|
55
|
+
*
|
|
56
|
+
*/
|
|
57
|
+
public Reactor (Application app) {
|
|
58
|
+
application = app;
|
|
59
|
+
timers = new TreeMap<String, Timer>();
|
|
60
|
+
connections = new TreeMap<String, Connection>();
|
|
61
|
+
acceptors = new TreeMap<String, ConnectionFactory>();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
public void eventCallback (String sig, int eventType, ByteBuffer data) {
|
|
66
|
+
if (eventType == EM_TIMER_FIRED) {
|
|
67
|
+
String timersig = new String (data.array());
|
|
68
|
+
//System.out.println ("EVSIG "+sig + "..."+new String(data.array()));
|
|
69
|
+
Timer r = timers.remove(timersig);
|
|
70
|
+
if (r != null)
|
|
71
|
+
r._fire();
|
|
72
|
+
else
|
|
73
|
+
throw new RuntimeException ("unable to run unknown timer");
|
|
74
|
+
}
|
|
75
|
+
else if (eventType == EM_CONNECTION_COMPLETED) {
|
|
76
|
+
Connection c = connections.get(sig);
|
|
77
|
+
if (c != null) {
|
|
78
|
+
c.connectionCompleted();
|
|
79
|
+
}
|
|
80
|
+
else
|
|
81
|
+
throw new RuntimeException ("connection completed to unknown object");
|
|
82
|
+
|
|
83
|
+
}
|
|
84
|
+
else if (eventType == EM_CONNECTION_UNBOUND) {
|
|
85
|
+
Connection c = connections.get(sig);
|
|
86
|
+
if (c != null) {
|
|
87
|
+
c.unbind();
|
|
88
|
+
}
|
|
89
|
+
else
|
|
90
|
+
throw new RuntimeException ("unbind received on unknown object");
|
|
91
|
+
}
|
|
92
|
+
else if (eventType == EM_CONNECTION_ACCEPTED) {
|
|
93
|
+
ConnectionFactory f = acceptors.get(sig);
|
|
94
|
+
if (f != null) {
|
|
95
|
+
Connection c = f.connection();
|
|
96
|
+
c.signature = new String (data.array());
|
|
97
|
+
c.application = application;
|
|
98
|
+
connections.put(c.signature, c);
|
|
99
|
+
c.postInit();
|
|
100
|
+
//System.out.println (sig+"..."+new String(data.array()));
|
|
101
|
+
}
|
|
102
|
+
else
|
|
103
|
+
throw new RuntimeException ("received connection on unknown acceptor");
|
|
104
|
+
}
|
|
105
|
+
else if (eventType == EM_CONNECTION_READ) {
|
|
106
|
+
Connection c = connections.get(sig);
|
|
107
|
+
if (c != null) {
|
|
108
|
+
c.receiveData(data);
|
|
109
|
+
}
|
|
110
|
+
else throw new RuntimeException ("received data on unknown object");
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
System.out.println ("unknown event type: " + eventType);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
Reactor reactor;
|
|
120
|
+
|
|
121
|
+
public Application() {
|
|
122
|
+
reactor = new Reactor (this);
|
|
123
|
+
}
|
|
124
|
+
public void addTimer (double seconds, Timer t) {
|
|
125
|
+
t.application = this;
|
|
126
|
+
t.interval = seconds;
|
|
127
|
+
String s = reactor.installOneshotTimer ((int)(seconds * 1000));
|
|
128
|
+
reactor.timers.put(s, t);
|
|
129
|
+
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
public void connect (String host, int port, Connection c) {
|
|
133
|
+
try {
|
|
134
|
+
String s = reactor.connectTcpServer(host, port);
|
|
135
|
+
c.application = this;
|
|
136
|
+
c.signature = s;
|
|
137
|
+
reactor.connections.put(s, c);
|
|
138
|
+
c.postInit();
|
|
139
|
+
} catch (ClosedChannelException e) {}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
public void startServer (SocketAddress sa, ConnectionFactory f) throws EmReactorException {
|
|
143
|
+
String s = reactor.startTcpServer(sa);
|
|
144
|
+
reactor.acceptors.put(s, f);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
public void stop() {
|
|
148
|
+
reactor.stop();
|
|
149
|
+
}
|
|
150
|
+
public void run() {
|
|
151
|
+
try {
|
|
152
|
+
reactor.run();
|
|
153
|
+
} catch (IOException e) {}
|
|
154
|
+
}
|
|
155
|
+
public void run (final Runnable r) {
|
|
156
|
+
addTimer(0, new Timer() {
|
|
157
|
+
public void fire() {
|
|
158
|
+
r.run();
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
run();
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
public void sendData (String sig, ByteBuffer bb) {
|
|
165
|
+
try {
|
|
166
|
+
reactor.sendData(sig, bb);
|
|
167
|
+
} catch (IOException e) {}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
public void sendDatagram (String sig, ByteBuffer bb, InetSocketAddress target) {
|
|
171
|
+
reactor.sendDatagram(sig, bb, target.getHostName(), target.getPort());
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
public void closeConnection (String sig, boolean afterWriting) {
|
|
175
|
+
try {
|
|
176
|
+
reactor.closeConnection(sig, afterWriting);
|
|
177
|
+
} catch (ClosedChannelException e) {}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
public void openDatagramSocket (Connection c) {
|
|
181
|
+
openDatagramSocket (new InetSocketAddress ("0.0.0.0", 0), c);
|
|
182
|
+
}
|
|
183
|
+
public void openDatagramSocket (InetSocketAddress addr, Connection c) {
|
|
184
|
+
try {
|
|
185
|
+
String s = reactor.openUdpSocket(addr);
|
|
186
|
+
c.application = this;
|
|
187
|
+
c.signature = s;
|
|
188
|
+
reactor.connections.put(s, c);
|
|
189
|
+
c.postInit();
|
|
190
|
+
} catch (ClosedChannelException e) {
|
|
191
|
+
} catch (IOException e) {
|
|
192
|
+
System.out.println ("Bad Datagram socket "+e+" "+addr);
|
|
193
|
+
/* TODO, can't catch this here, because it can happen on a bad address */
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
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
|
+
package com.rubyeventmachine;
|
|
32
|
+
|
|
33
|
+
//import java.io.*;
|
|
34
|
+
import java.nio.*;
|
|
35
|
+
import java.net.*;
|
|
36
|
+
//import java.nio.channels.*;
|
|
37
|
+
|
|
38
|
+
public class Connection {
|
|
39
|
+
|
|
40
|
+
public Application application;
|
|
41
|
+
public String signature;
|
|
42
|
+
|
|
43
|
+
public void postInit() {}
|
|
44
|
+
public void connectionCompleted() {}
|
|
45
|
+
public void unbind() {}
|
|
46
|
+
public void receiveData (ByteBuffer bytebuffer) {}
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Called by user code.
|
|
51
|
+
* @param bytebuffer
|
|
52
|
+
*/
|
|
53
|
+
public void sendData (ByteBuffer b) {
|
|
54
|
+
application.sendData(signature, b);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* This is called by user code.
|
|
59
|
+
* TODO: don't expose the exception here.
|
|
60
|
+
*/
|
|
61
|
+
public void close() {
|
|
62
|
+
application.closeConnection(signature, false);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* This is called by user code/
|
|
66
|
+
*/
|
|
67
|
+
public void closeAfterWriting() {
|
|
68
|
+
application.closeConnection(signature, true);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
public void sendDatagram (ByteBuffer bb, InetSocketAddress addr) {
|
|
72
|
+
application.sendDatagram (signature, bb, addr);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
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
|
+
package com.rubyeventmachine;
|
|
32
|
+
|
|
33
|
+
//import com.rubyeventmachine.*;
|
|
34
|
+
|
|
35
|
+
public interface ConnectionFactory {
|
|
36
|
+
public Connection connection();
|
|
37
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
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
|
+
package com.rubyeventmachine;
|
|
32
|
+
|
|
33
|
+
import com.rubyeventmachine.ConnectionFactory;
|
|
34
|
+
|
|
35
|
+
public class DefaultConnectionFactory implements ConnectionFactory {
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Convenience class. Its connection() method returns an instance of class
|
|
39
|
+
* Connection, which is usually overridden. This class is probably most
|
|
40
|
+
* useful for unit testing.
|
|
41
|
+
*/
|
|
42
|
+
public Connection connection() {
|
|
43
|
+
return new Connection();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
}
|
|
@@ -0,0 +1,408 @@
|
|
|
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.io.*;
|
|
32
|
+
import java.nio.channels.*;
|
|
33
|
+
import java.util.*;
|
|
34
|
+
import java.nio.*;
|
|
35
|
+
import java.net.*;
|
|
36
|
+
import java.util.concurrent.atomic.*;
|
|
37
|
+
import java.security.*;
|
|
38
|
+
|
|
39
|
+
public class EmReactor {
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
public final int EM_TIMER_FIRED = 100;
|
|
43
|
+
public final int EM_CONNECTION_READ = 101;
|
|
44
|
+
public final int EM_CONNECTION_UNBOUND = 102;
|
|
45
|
+
public final int EM_CONNECTION_ACCEPTED = 103;
|
|
46
|
+
public final int EM_CONNECTION_COMPLETED = 104;
|
|
47
|
+
public final int EM_LOOPBREAK_SIGNAL = 105;
|
|
48
|
+
|
|
49
|
+
private Selector mySelector;
|
|
50
|
+
private TreeMap<Long, String> Timers;
|
|
51
|
+
private TreeMap<String, EventableChannel> Connections;
|
|
52
|
+
private TreeMap<String, ServerSocketChannel> Acceptors;
|
|
53
|
+
|
|
54
|
+
private boolean bRunReactor;
|
|
55
|
+
private long BindingIndex;
|
|
56
|
+
private ByteBuffer EmptyByteBuffer;
|
|
57
|
+
private AtomicBoolean loopBreaker;
|
|
58
|
+
private ByteBuffer myReadBuffer;
|
|
59
|
+
private int timerQuantum;
|
|
60
|
+
|
|
61
|
+
public EmReactor() {
|
|
62
|
+
Timers = new TreeMap<Long, String>();
|
|
63
|
+
Connections = new TreeMap<String, EventableChannel>();
|
|
64
|
+
Acceptors = new TreeMap<String, ServerSocketChannel>();
|
|
65
|
+
|
|
66
|
+
BindingIndex = 100000;
|
|
67
|
+
EmptyByteBuffer = ByteBuffer.allocate(0);
|
|
68
|
+
loopBreaker = new AtomicBoolean();
|
|
69
|
+
loopBreaker.set(false);
|
|
70
|
+
myReadBuffer = ByteBuffer.allocate(32*1024); // don't use a direct buffer. Ruby doesn't seem to like them.
|
|
71
|
+
timerQuantum = 98;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Intended to be overridden in languages (like Ruby) that can't handle ByteBuffer. This is a stub.
|
|
76
|
+
* Obsolete now that I figured out how to make Ruby deal with ByteBuffers.
|
|
77
|
+
* @param sig
|
|
78
|
+
* @param eventType
|
|
79
|
+
* @param data
|
|
80
|
+
*/
|
|
81
|
+
/*
|
|
82
|
+
public void stringEventCallback (String sig, int eventType, String data) {
|
|
83
|
+
System.out.println ("Default event callback: " + sig + " " + eventType + " " + data);
|
|
84
|
+
}
|
|
85
|
+
*/
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* This is a no-op stub, intended to be overridden in user code.
|
|
89
|
+
* @param sig
|
|
90
|
+
* @param eventType
|
|
91
|
+
* @param data
|
|
92
|
+
*/
|
|
93
|
+
public void eventCallback (String sig, int eventType, ByteBuffer data) {
|
|
94
|
+
System.out.println ("Default callback: "+sig+" "+eventType+" "+data);
|
|
95
|
+
//stringEventCallback (sig, eventType, new String (data.array(), data.position(), data.limit()));
|
|
96
|
+
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
public void run() throws IOException {
|
|
100
|
+
mySelector = Selector.open();
|
|
101
|
+
bRunReactor = true;
|
|
102
|
+
|
|
103
|
+
//int n = 0;
|
|
104
|
+
for (;;) {
|
|
105
|
+
//System.out.println ("loop "+ (n++));
|
|
106
|
+
if (!bRunReactor) break;
|
|
107
|
+
runLoopbreaks();
|
|
108
|
+
if (!bRunReactor) break;
|
|
109
|
+
runTimers();
|
|
110
|
+
if (!bRunReactor) break;
|
|
111
|
+
mySelector.select(timerQuantum);
|
|
112
|
+
|
|
113
|
+
Iterator<SelectionKey> it = mySelector.selectedKeys().iterator();
|
|
114
|
+
while (it.hasNext()) {
|
|
115
|
+
SelectionKey k = it.next();
|
|
116
|
+
it.remove();
|
|
117
|
+
|
|
118
|
+
try {
|
|
119
|
+
if (k.isAcceptable()) {
|
|
120
|
+
ServerSocketChannel ss = (ServerSocketChannel) k.channel();
|
|
121
|
+
SocketChannel sn;
|
|
122
|
+
while ((sn = ss.accept()) != null) {
|
|
123
|
+
sn.configureBlocking(false);
|
|
124
|
+
String b = createBinding();
|
|
125
|
+
EventableSocketChannel ec = new EventableSocketChannel (sn, b, mySelector);
|
|
126
|
+
Connections.put(b, ec);
|
|
127
|
+
eventCallback ((String)k.attachment(), EM_CONNECTION_ACCEPTED, ByteBuffer.wrap(b.getBytes()));
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (k.isReadable()) {
|
|
132
|
+
EventableChannel ec = (EventableChannel)k.attachment();
|
|
133
|
+
myReadBuffer.clear();
|
|
134
|
+
ec.readInboundData (myReadBuffer);
|
|
135
|
+
myReadBuffer.flip();
|
|
136
|
+
String b = ec.getBinding();
|
|
137
|
+
if (myReadBuffer.limit() > 0) {
|
|
138
|
+
eventCallback (b, EM_CONNECTION_READ, myReadBuffer);
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
eventCallback (b, EM_CONNECTION_UNBOUND, EmptyByteBuffer);
|
|
142
|
+
Connections.remove(b);
|
|
143
|
+
k.channel().close();
|
|
144
|
+
}
|
|
145
|
+
/*
|
|
146
|
+
System.out.println ("READABLE");
|
|
147
|
+
SocketChannel sn = (SocketChannel) k.channel();
|
|
148
|
+
//ByteBuffer bb = ByteBuffer.allocate(16 * 1024);
|
|
149
|
+
// Obviously not thread-safe, since we're using the same buffer for every connection.
|
|
150
|
+
// This should minimize the production of garbage, though.
|
|
151
|
+
// TODO, we need somehow to make a call to the EventableChannel, so we can pass the
|
|
152
|
+
// inbound data through an SSLEngine. Hope that won't break the strategy of using one
|
|
153
|
+
// global read-buffer.
|
|
154
|
+
myReadBuffer.clear();
|
|
155
|
+
int r = sn.read(myReadBuffer);
|
|
156
|
+
if (r > 0) {
|
|
157
|
+
myReadBuffer.flip();
|
|
158
|
+
//bb = ((EventableChannel)k.attachment()).dispatchInboundData (bb);
|
|
159
|
+
eventCallback (((EventableChannel)k.attachment()).getBinding(), EM_CONNECTION_READ, myReadBuffer);
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
// TODO. Figure out if a socket that selects readable can ever return 0 bytes
|
|
163
|
+
// without it being indicative of an error condition. If Java is like C, the answer is no.
|
|
164
|
+
String b = ((EventableChannel)k.attachment()).getBinding();
|
|
165
|
+
eventCallback (b, EM_CONNECTION_UNBOUND, EmptyByteBuffer);
|
|
166
|
+
Connections.remove(b);
|
|
167
|
+
sn.close();
|
|
168
|
+
}
|
|
169
|
+
*/
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
if (k.isWritable()) {
|
|
174
|
+
EventableChannel ec = (EventableChannel)k.attachment();
|
|
175
|
+
if (!ec.writeOutboundData()) {
|
|
176
|
+
eventCallback (ec.getBinding(), EM_CONNECTION_UNBOUND, EmptyByteBuffer);
|
|
177
|
+
Connections.remove (ec.getBinding());
|
|
178
|
+
k.channel().close();
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (k.isConnectable()) {
|
|
183
|
+
EventableSocketChannel ec = (EventableSocketChannel)k.attachment();
|
|
184
|
+
if (ec.finishConnecting()) {
|
|
185
|
+
eventCallback (ec.getBinding(), EM_CONNECTION_COMPLETED, EmptyByteBuffer);
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
Connections.remove (ec.getBinding());
|
|
189
|
+
k.channel().close();
|
|
190
|
+
eventCallback (ec.getBinding(), EM_CONNECTION_UNBOUND, EmptyByteBuffer);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
catch (CancelledKeyException e) {
|
|
195
|
+
// No-op. We can come here if a read-handler closes a socket before we fall through
|
|
196
|
+
// to call isWritable.
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
close();
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
void close() throws IOException {
|
|
206
|
+
mySelector.close();
|
|
207
|
+
mySelector = null;
|
|
208
|
+
|
|
209
|
+
// run down open connections and sockets.
|
|
210
|
+
Iterator<ServerSocketChannel> i = Acceptors.values().iterator();
|
|
211
|
+
while (i.hasNext()) {
|
|
212
|
+
i.next().close();
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
Iterator<EventableChannel> i2 = Connections.values().iterator();
|
|
216
|
+
while (i2.hasNext())
|
|
217
|
+
i2.next().close();
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
void runLoopbreaks() {
|
|
221
|
+
if (loopBreaker.getAndSet(false)) {
|
|
222
|
+
eventCallback ("", EM_LOOPBREAK_SIGNAL, EmptyByteBuffer);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
public void stop() {
|
|
227
|
+
bRunReactor = false;
|
|
228
|
+
signalLoopbreak();
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
void runTimers() {
|
|
232
|
+
long now = new Date().getTime();
|
|
233
|
+
while (!Timers.isEmpty()) {
|
|
234
|
+
long k = Timers.firstKey();
|
|
235
|
+
//System.out.println (k - now);
|
|
236
|
+
if (k > now)
|
|
237
|
+
break;
|
|
238
|
+
String s = Timers.remove(k);
|
|
239
|
+
eventCallback ("", EM_TIMER_FIRED, ByteBuffer.wrap(s.getBytes()));
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
public String installOneshotTimer (int milliseconds) {
|
|
244
|
+
BindingIndex++;
|
|
245
|
+
String s = createBinding();
|
|
246
|
+
Timers.put(new Date().getTime() + milliseconds, s);
|
|
247
|
+
return s;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
public String startTcpServer (SocketAddress sa) throws EmReactorException {
|
|
251
|
+
try {
|
|
252
|
+
ServerSocketChannel server = ServerSocketChannel.open();
|
|
253
|
+
server.configureBlocking(false);
|
|
254
|
+
server.socket().bind (sa);
|
|
255
|
+
String s = createBinding();
|
|
256
|
+
Acceptors.put(s, server);
|
|
257
|
+
server.register(mySelector, SelectionKey.OP_ACCEPT, s);
|
|
258
|
+
return s;
|
|
259
|
+
} catch (IOException e) {
|
|
260
|
+
// TODO, should parameterize this exception better.
|
|
261
|
+
throw new EmReactorException ("unable to open socket acceptor");
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
public String startTcpServer (String address, int port) throws EmReactorException {
|
|
266
|
+
return startTcpServer (new InetSocketAddress (address, port));
|
|
267
|
+
/*
|
|
268
|
+
ServerSocketChannel server = ServerSocketChannel.open();
|
|
269
|
+
server.configureBlocking(false);
|
|
270
|
+
server.socket().bind(new java.net.InetSocketAddress(address, port));
|
|
271
|
+
String s = createBinding();
|
|
272
|
+
Acceptors.put(s, server);
|
|
273
|
+
server.register(mySelector, SelectionKey.OP_ACCEPT, s);
|
|
274
|
+
return s;
|
|
275
|
+
*/
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
public void stopTcpServer (String signature) throws IOException {
|
|
279
|
+
ServerSocketChannel server = Acceptors.remove(signature);
|
|
280
|
+
if (server != null)
|
|
281
|
+
server.close();
|
|
282
|
+
else
|
|
283
|
+
throw new RuntimeException ("failed to close unknown acceptor");
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
public String openUdpSocket (String address, int port) throws IOException {
|
|
288
|
+
return openUdpSocket (new InetSocketAddress (address, port));
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
*
|
|
292
|
+
* @param address
|
|
293
|
+
* @param port
|
|
294
|
+
* @return
|
|
295
|
+
* @throws IOException
|
|
296
|
+
*/
|
|
297
|
+
public String openUdpSocket (InetSocketAddress address) throws IOException {
|
|
298
|
+
// TODO, don't throw an exception out of here.
|
|
299
|
+
DatagramChannel dg = DatagramChannel.open();
|
|
300
|
+
dg.configureBlocking(false);
|
|
301
|
+
dg.socket().bind(address);
|
|
302
|
+
String b = createBinding();
|
|
303
|
+
EventableChannel ec = new EventableDatagramChannel (dg, b, mySelector);
|
|
304
|
+
dg.register(mySelector, SelectionKey.OP_READ, ec);
|
|
305
|
+
Connections.put(b, ec);
|
|
306
|
+
return b;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
public void sendData (String sig, ByteBuffer bb) throws IOException {
|
|
310
|
+
(Connections.get(sig)).scheduleOutboundData( bb );
|
|
311
|
+
}
|
|
312
|
+
public void sendData (String sig, byte[] data) throws IOException {
|
|
313
|
+
sendData (sig, ByteBuffer.wrap(data));
|
|
314
|
+
//(Connections.get(sig)).scheduleOutboundData( ByteBuffer.wrap(data.getBytes()));
|
|
315
|
+
}
|
|
316
|
+
public void setCommInactivityTimeout (String sig, long mills) {
|
|
317
|
+
(Connections.get(sig)).setCommInactivityTimeout (mills);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
*
|
|
322
|
+
* @param sig
|
|
323
|
+
* @param data
|
|
324
|
+
* @param length
|
|
325
|
+
* @param recipAddress
|
|
326
|
+
* @param recipPort
|
|
327
|
+
*/
|
|
328
|
+
public void sendDatagram (String sig, String data, int length, String recipAddress, int recipPort) {
|
|
329
|
+
sendDatagram (sig, ByteBuffer.wrap(data.getBytes()), recipAddress, recipPort);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
*
|
|
334
|
+
* @param sig
|
|
335
|
+
* @param bb
|
|
336
|
+
* @param recipAddress
|
|
337
|
+
* @param recipPort
|
|
338
|
+
*/
|
|
339
|
+
public void sendDatagram (String sig, ByteBuffer bb, String recipAddress, int recipPort) {
|
|
340
|
+
(Connections.get(sig)).scheduleOutboundDatagram( bb, recipAddress, recipPort);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
*
|
|
346
|
+
* @param address
|
|
347
|
+
* @param port
|
|
348
|
+
* @return
|
|
349
|
+
* @throws ClosedChannelException
|
|
350
|
+
*/
|
|
351
|
+
public String connectTcpServer (String address, int port) throws ClosedChannelException {
|
|
352
|
+
String b = createBinding();
|
|
353
|
+
|
|
354
|
+
try {
|
|
355
|
+
SocketChannel sc = SocketChannel.open();
|
|
356
|
+
sc.configureBlocking(false);
|
|
357
|
+
EventableSocketChannel ec = new EventableSocketChannel (sc, b, mySelector);
|
|
358
|
+
|
|
359
|
+
if (sc.connect (new InetSocketAddress (address, port))) {
|
|
360
|
+
// Connection returned immediately. Can happen with localhost connections.
|
|
361
|
+
// WARNING, this code is untested due to lack of available test conditions.
|
|
362
|
+
// Ought to be be able to come here from a localhost connection, but that
|
|
363
|
+
// doesn't happen on Linux. (Maybe on FreeBSD?)
|
|
364
|
+
// The reason for not handling this until we can test it is that we
|
|
365
|
+
// really need to return from this function WITHOUT triggering any EM events.
|
|
366
|
+
// That's because until the user code has seen the signature we generated here,
|
|
367
|
+
// it won't be able to properly dispatch them. The C++ EM deals with this
|
|
368
|
+
// by setting pending mode as a flag in ALL eventable descriptors and making
|
|
369
|
+
// the descriptor select for writable. Then, it can send UNBOUND and
|
|
370
|
+
// CONNECTION_COMPLETED on the next pass through the loop, because writable will
|
|
371
|
+
// fire.
|
|
372
|
+
throw new RuntimeException ("immediate-connect unimplemented");
|
|
373
|
+
}
|
|
374
|
+
else {
|
|
375
|
+
Connections.put (b, ec);
|
|
376
|
+
ec.setConnectPending();
|
|
377
|
+
}
|
|
378
|
+
} catch (IOException e) {
|
|
379
|
+
// Can theoretically come here if a connect failure can be determined immediately.
|
|
380
|
+
// I don't know how to make that happen for testing purposes.
|
|
381
|
+
throw new RuntimeException ("immediate-connect unimplemented");
|
|
382
|
+
}
|
|
383
|
+
return b;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
public void closeConnection (String sig, boolean afterWriting) throws ClosedChannelException {
|
|
387
|
+
Connections.get(sig).scheduleClose (afterWriting);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
String createBinding() {
|
|
391
|
+
return new String ("BND_" + (++BindingIndex));
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
public void signalLoopbreak() {
|
|
395
|
+
loopBreaker.set(true);
|
|
396
|
+
mySelector.wakeup();
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
public void startTls (String sig) throws NoSuchAlgorithmException, KeyManagementException {
|
|
400
|
+
Connections.get(sig).startTls();
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
public void setTimerQuantum (int mills) {
|
|
404
|
+
if (mills < 5 || mills > 2500)
|
|
405
|
+
throw new RuntimeException ("attempt to set invalid timer-quantum value: "+mills);
|
|
406
|
+
timerQuantum = mills;
|
|
407
|
+
}
|
|
408
|
+
}
|