eventmachine 1.0.3-x86-mingw32 → 1.2.0.dev.2-x86-mingw32
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +84 -1
- data/README.md +6 -7
- data/ext/binder.cpp +10 -10
- data/ext/binder.h +5 -5
- data/ext/cmain.cpp +173 -61
- data/ext/ed.cpp +262 -127
- data/ext/ed.h +50 -30
- data/ext/em.cpp +491 -445
- data/ext/em.h +101 -36
- data/ext/eventmachine.h +67 -51
- data/ext/extconf.rb +124 -31
- data/ext/fastfilereader/extconf.rb +9 -2
- data/ext/fastfilereader/mapper.cpp +3 -1
- data/ext/fastfilereader/rubymain.cpp +7 -7
- data/ext/kb.cpp +1 -1
- data/ext/pipe.cpp +11 -4
- data/ext/project.h +26 -6
- data/ext/rubymain.cpp +408 -201
- data/ext/ssl.cpp +167 -20
- data/ext/ssl.h +11 -2
- data/java/src/com/rubyeventmachine/EmReactor.java +16 -0
- data/java/src/com/rubyeventmachine/EventableChannel.java +2 -0
- data/java/src/com/rubyeventmachine/EventableDatagramChannel.java +6 -0
- data/java/src/com/rubyeventmachine/EventableSocketChannel.java +55 -10
- data/lib/1.9/fastfilereaderext.so +0 -0
- data/lib/1.9/rubyeventmachine.so +0 -0
- data/lib/2.0/fastfilereaderext.so +0 -0
- data/lib/2.0/rubyeventmachine.so +0 -0
- data/lib/2.1/fastfilereaderext.so +0 -0
- data/lib/2.1/rubyeventmachine.so +0 -0
- data/lib/2.2/fastfilereaderext.so +0 -0
- data/lib/2.2/rubyeventmachine.so +0 -0
- data/lib/2.3/fastfilereaderext.so +0 -0
- data/lib/2.3/rubyeventmachine.so +0 -0
- data/lib/em/buftok.rb +34 -85
- data/lib/em/channel.rb +5 -0
- data/lib/em/completion.rb +2 -2
- data/lib/em/connection.rb +62 -4
- data/lib/em/iterator.rb +30 -48
- data/lib/em/pool.rb +1 -1
- data/lib/em/protocols/httpclient.rb +31 -11
- data/lib/em/protocols/line_and_text.rb +4 -4
- data/lib/em/protocols/linetext2.rb +44 -39
- data/lib/em/protocols/smtpclient.rb +60 -31
- data/lib/em/protocols/smtpserver.rb +32 -9
- data/lib/em/pure_ruby.rb +8 -3
- data/lib/em/queue.rb +16 -7
- data/lib/em/resolver.rb +64 -24
- data/lib/em/threaded_resource.rb +2 -2
- data/lib/em/tick_loop.rb +19 -19
- data/lib/em/version.rb +1 -1
- data/lib/eventmachine.rb +96 -49
- data/lib/jeventmachine.rb +17 -0
- data/rakelib/package.rake +31 -4
- data/tests/dhparam.pem +13 -0
- data/tests/em_test_helper.rb +87 -0
- data/tests/test_attach.rb +25 -0
- data/tests/test_basic.rb +27 -38
- data/tests/test_channel.rb +14 -1
- data/tests/test_completion.rb +1 -0
- data/tests/test_connection_count.rb +22 -1
- data/tests/test_connection_write.rb +35 -0
- data/tests/test_defer.rb +17 -0
- data/tests/test_epoll.rb +26 -14
- data/tests/test_file_watch.rb +1 -0
- data/tests/test_fork.rb +75 -0
- data/tests/test_httpclient.rb +43 -0
- data/tests/test_idle_connection.rb +6 -4
- data/tests/test_ipv4.rb +125 -0
- data/tests/test_ipv6.rb +131 -0
- data/tests/test_iterator.rb +115 -0
- data/tests/test_kb.rb +19 -25
- data/tests/test_ltp2.rb +20 -0
- data/tests/test_many_fds.rb +22 -0
- data/tests/test_pause.rb +29 -0
- data/tests/test_pool.rb +2 -0
- data/tests/test_process_watch.rb +2 -0
- data/tests/test_processes.rb +7 -7
- data/tests/test_queue.rb +14 -0
- data/tests/test_resolver.rb +56 -7
- data/tests/test_set_sock_opt.rb +2 -0
- data/tests/test_smtpclient.rb +20 -0
- data/tests/test_ssl_args.rb +2 -2
- data/tests/test_ssl_dhparam.rb +83 -0
- data/tests/test_ssl_ecdh_curve.rb +79 -0
- data/tests/test_ssl_extensions.rb +49 -0
- data/tests/test_ssl_methods.rb +22 -5
- data/tests/test_ssl_protocols.rb +246 -0
- data/tests/test_ssl_verify.rb +103 -59
- data/tests/test_system.rb +4 -0
- data/tests/test_threaded_resource.rb +8 -0
- data/tests/test_unbind_reason.rb +5 -1
- metadata +173 -107
- data/.gitignore +0 -21
- data/.travis.yml +0 -12
- data/.yardopts +0 -7
- data/Gemfile +0 -2
- data/Rakefile +0 -20
- data/eventmachine.gemspec +0 -36
- data/rakelib/cpp.rake_example +0 -77
data/ext/ssl.cpp
CHANGED
@@ -82,7 +82,7 @@ static char PrivateMaterials[] = {
|
|
82
82
|
builtin_passwd_cb
|
83
83
|
*****************/
|
84
84
|
|
85
|
-
extern "C" int builtin_passwd_cb (char *buf, int bufsize, int rwflag, void *userdata)
|
85
|
+
extern "C" int builtin_passwd_cb (char *buf UNUSED, int bufsize UNUSED, int rwflag UNUSED, void *userdata UNUSED)
|
86
86
|
{
|
87
87
|
strcpy (buf, "kittycat");
|
88
88
|
return 8;
|
@@ -120,7 +120,8 @@ static void InitializeDefaultCredentials()
|
|
120
120
|
SslContext_t::SslContext_t
|
121
121
|
**************************/
|
122
122
|
|
123
|
-
SslContext_t::SslContext_t (bool is_server, const string &privkeyfile, const string &certchainfile):
|
123
|
+
SslContext_t::SslContext_t (bool is_server, const string &privkeyfile, const string &certchainfile, const string &cipherlist, const string &ecdh_curve, const string &dhparam, int ssl_version) :
|
124
|
+
bIsServer (is_server),
|
124
125
|
pCtx (NULL),
|
125
126
|
PrivateKey (NULL),
|
126
127
|
Certificate (NULL)
|
@@ -144,18 +145,47 @@ SslContext_t::SslContext_t (bool is_server, const string &privkeyfile, const str
|
|
144
145
|
InitializeDefaultCredentials();
|
145
146
|
}
|
146
147
|
|
147
|
-
|
148
|
-
pCtx = SSL_CTX_new (is_server ? SSLv23_server_method() : SSLv23_client_method());
|
148
|
+
pCtx = SSL_CTX_new (bIsServer ? SSLv23_server_method() : SSLv23_client_method());
|
149
149
|
if (!pCtx)
|
150
150
|
throw std::runtime_error ("no SSL context");
|
151
151
|
|
152
152
|
SSL_CTX_set_options (pCtx, SSL_OP_ALL);
|
153
|
-
|
154
|
-
#ifdef
|
153
|
+
|
154
|
+
#ifdef SSL_CTRL_CLEAR_OPTIONS
|
155
|
+
SSL_CTX_clear_options (pCtx, SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_NO_TLSv1);
|
156
|
+
# ifdef SSL_OP_NO_TLSv1_1
|
157
|
+
SSL_CTX_clear_options (pCtx, SSL_OP_NO_TLSv1_1);
|
158
|
+
# endif
|
159
|
+
# ifdef SSL_OP_NO_TLSv1_2
|
160
|
+
SSL_CTX_clear_options (pCtx, SSL_OP_NO_TLSv1_2);
|
161
|
+
# endif
|
162
|
+
#endif
|
163
|
+
|
164
|
+
if (!(ssl_version & EM_PROTO_SSLv2))
|
165
|
+
SSL_CTX_set_options (pCtx, SSL_OP_NO_SSLv2);
|
166
|
+
|
167
|
+
if (!(ssl_version & EM_PROTO_SSLv3))
|
168
|
+
SSL_CTX_set_options (pCtx, SSL_OP_NO_SSLv3);
|
169
|
+
|
170
|
+
if (!(ssl_version & EM_PROTO_TLSv1))
|
171
|
+
SSL_CTX_set_options (pCtx, SSL_OP_NO_TLSv1);
|
172
|
+
|
173
|
+
#ifdef SSL_OP_NO_TLSv1_1
|
174
|
+
if (!(ssl_version & EM_PROTO_TLSv1_1))
|
175
|
+
SSL_CTX_set_options (pCtx, SSL_OP_NO_TLSv1_1);
|
176
|
+
#endif
|
177
|
+
|
178
|
+
#ifdef SSL_OP_NO_TLSv1_2
|
179
|
+
if (!(ssl_version & EM_PROTO_TLSv1_2))
|
180
|
+
SSL_CTX_set_options (pCtx, SSL_OP_NO_TLSv1_2);
|
181
|
+
#endif
|
182
|
+
|
183
|
+
#ifdef SSL_MODE_RELEASE_BUFFERS
|
155
184
|
SSL_CTX_set_mode (pCtx, SSL_MODE_RELEASE_BUFFERS);
|
156
|
-
#endif
|
185
|
+
#endif
|
186
|
+
|
187
|
+
if (bIsServer) {
|
157
188
|
|
158
|
-
if (is_server) {
|
159
189
|
// The SSL_CTX calls here do NOT allocate memory.
|
160
190
|
int e;
|
161
191
|
if (privkeyfile.length() > 0)
|
@@ -171,11 +201,69 @@ SslContext_t::SslContext_t (bool is_server, const string &privkeyfile, const str
|
|
171
201
|
e = SSL_CTX_use_certificate (pCtx, DefaultCertificate);
|
172
202
|
if (e <= 0) ERR_print_errors_fp(stderr);
|
173
203
|
assert (e > 0);
|
204
|
+
|
205
|
+
if (dhparam.length() > 0) {
|
206
|
+
DH *dh;
|
207
|
+
BIO *bio;
|
208
|
+
|
209
|
+
bio = BIO_new_file(dhparam.c_str(), "r");
|
210
|
+
if (bio == NULL) {
|
211
|
+
char buf [500];
|
212
|
+
snprintf (buf, sizeof(buf)-1, "dhparam: BIO_new_file(%s) failed", dhparam.c_str());
|
213
|
+
throw std::runtime_error (buf);
|
214
|
+
}
|
215
|
+
|
216
|
+
dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
|
217
|
+
|
218
|
+
if (dh == NULL) {
|
219
|
+
BIO_free(bio);
|
220
|
+
char buf [500];
|
221
|
+
snprintf (buf, sizeof(buf)-1, "dhparam: PEM_read_bio_DHparams(%s) failed", dhparam.c_str());
|
222
|
+
throw new std::runtime_error(buf);
|
223
|
+
}
|
224
|
+
|
225
|
+
SSL_CTX_set_tmp_dh(pCtx, dh);
|
226
|
+
|
227
|
+
DH_free(dh);
|
228
|
+
BIO_free(bio);
|
229
|
+
}
|
230
|
+
|
231
|
+
if (ecdh_curve.length() > 0) {
|
232
|
+
#if OPENSSL_VERSION_NUMBER >= 0x0090800fL && !defined(OPENSSL_NO_ECDH)
|
233
|
+
int nid;
|
234
|
+
EC_KEY *ecdh;
|
235
|
+
|
236
|
+
nid = OBJ_sn2nid((const char *) ecdh_curve.c_str());
|
237
|
+
if (nid == 0) {
|
238
|
+
char buf [200];
|
239
|
+
snprintf (buf, sizeof(buf)-1, "ecdh_curve: Unknown curve name: %s", ecdh_curve.c_str());
|
240
|
+
throw std::runtime_error (buf);
|
241
|
+
}
|
242
|
+
|
243
|
+
ecdh = EC_KEY_new_by_curve_name(nid);
|
244
|
+
if (ecdh == NULL) {
|
245
|
+
char buf [200];
|
246
|
+
snprintf (buf, sizeof(buf)-1, "ecdh_curve: Unable to create: %s", ecdh_curve.c_str());
|
247
|
+
throw std::runtime_error (buf);
|
248
|
+
}
|
249
|
+
|
250
|
+
SSL_CTX_set_options(pCtx, SSL_OP_SINGLE_ECDH_USE);
|
251
|
+
|
252
|
+
SSL_CTX_set_tmp_ecdh(pCtx, ecdh);
|
253
|
+
|
254
|
+
EC_KEY_free(ecdh);
|
255
|
+
#else
|
256
|
+
throw std::runtime_error ("No openssl ECDH support");
|
257
|
+
#endif
|
258
|
+
}
|
174
259
|
}
|
175
260
|
|
176
|
-
|
261
|
+
if (cipherlist.length() > 0)
|
262
|
+
SSL_CTX_set_cipher_list (pCtx, cipherlist.c_str());
|
263
|
+
else
|
264
|
+
SSL_CTX_set_cipher_list (pCtx, "ALL:!ADH:!LOW:!EXP:!DES-CBC3-SHA:@STRENGTH");
|
177
265
|
|
178
|
-
if (
|
266
|
+
if (bIsServer) {
|
179
267
|
SSL_CTX_sess_set_cache_size (pCtx, 128);
|
180
268
|
SSL_CTX_set_session_id_context (pCtx, (unsigned char*)"eventmachine", 12);
|
181
269
|
}
|
@@ -216,10 +304,11 @@ SslContext_t::~SslContext_t()
|
|
216
304
|
SslBox_t::SslBox_t
|
217
305
|
******************/
|
218
306
|
|
219
|
-
SslBox_t::SslBox_t (bool is_server, const string &privkeyfile, const string &certchainfile, bool verify_peer, const
|
307
|
+
SslBox_t::SslBox_t (bool is_server, const string &privkeyfile, const string &certchainfile, bool verify_peer, bool fail_if_no_peer_cert, const string &snihostname, const string &cipherlist, const string &ecdh_curve, const string &dhparam, int ssl_version, const uintptr_t binding):
|
220
308
|
bIsServer (is_server),
|
221
309
|
bHandshakeCompleted (false),
|
222
310
|
bVerifyPeer (verify_peer),
|
311
|
+
bFailIfNoPeerCert (fail_if_no_peer_cert),
|
223
312
|
pSSL (NULL),
|
224
313
|
pbioRead (NULL),
|
225
314
|
pbioWrite (NULL)
|
@@ -228,7 +317,7 @@ SslBox_t::SslBox_t (bool is_server, const string &privkeyfile, const string &cer
|
|
228
317
|
* a new one every time we come here.
|
229
318
|
*/
|
230
319
|
|
231
|
-
Context = new SslContext_t (bIsServer, privkeyfile, certchainfile);
|
320
|
+
Context = new SslContext_t (bIsServer, privkeyfile, certchainfile, cipherlist, ecdh_curve, dhparam, ssl_version);
|
232
321
|
assert (Context);
|
233
322
|
|
234
323
|
pbioRead = BIO_new (BIO_s_mem());
|
@@ -239,13 +328,22 @@ SslBox_t::SslBox_t (bool is_server, const string &privkeyfile, const string &cer
|
|
239
328
|
|
240
329
|
pSSL = SSL_new (Context->pCtx);
|
241
330
|
assert (pSSL);
|
331
|
+
|
332
|
+
if (snihostname.length() > 0) {
|
333
|
+
SSL_set_tlsext_host_name (pSSL, snihostname.c_str());
|
334
|
+
}
|
335
|
+
|
242
336
|
SSL_set_bio (pSSL, pbioRead, pbioWrite);
|
243
337
|
|
244
338
|
// Store a pointer to the binding signature in the SSL object so we can retrieve it later
|
245
339
|
SSL_set_ex_data(pSSL, 0, (void*) binding);
|
246
340
|
|
247
|
-
if (bVerifyPeer)
|
248
|
-
|
341
|
+
if (bVerifyPeer) {
|
342
|
+
int mode = SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE;
|
343
|
+
if (bFailIfNoPeerCert)
|
344
|
+
mode = mode | SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
|
345
|
+
SSL_set_verify(pSSL, mode, ssl_verify_wrapper);
|
346
|
+
}
|
249
347
|
|
250
348
|
if (!bIsServer)
|
251
349
|
SSL_connect (pSSL);
|
@@ -296,7 +394,7 @@ int SslBox_t::GetPlaintext (char *buf, int bufsize)
|
|
296
394
|
{
|
297
395
|
if (!SSL_is_init_finished (pSSL)) {
|
298
396
|
int e = bIsServer ? SSL_accept (pSSL) : SSL_connect (pSSL);
|
299
|
-
if (e
|
397
|
+
if (e != 1) {
|
300
398
|
int er = SSL_get_error (pSSL, e);
|
301
399
|
if (er != SSL_ERROR_WANT_READ) {
|
302
400
|
// Return -1 for a nonfatal error, -2 for an error that should force the connection down.
|
@@ -312,7 +410,7 @@ int SslBox_t::GetPlaintext (char *buf, int bufsize)
|
|
312
410
|
if (!SSL_is_init_finished (pSSL)) {
|
313
411
|
// We can get here if a browser abandons a handshake.
|
314
412
|
// The user can see a warning dialog and abort the connection.
|
315
|
-
cerr << "<SSL_incomp>";
|
413
|
+
//cerr << "<SSL_incomp>";
|
316
414
|
return 0;
|
317
415
|
}
|
318
416
|
|
@@ -392,13 +490,16 @@ int SslBox_t::PutPlaintext (const char *buf, int bufsize)
|
|
392
490
|
|
393
491
|
bool fatal = false;
|
394
492
|
bool did_work = false;
|
493
|
+
int pending = BIO_pending(pbioWrite);
|
395
494
|
|
396
|
-
while (OutboundQ.HasPages()) {
|
495
|
+
while (OutboundQ.HasPages() && pending < SSLBOX_WRITE_BUFFER_SIZE) {
|
397
496
|
const char *page;
|
398
497
|
int length;
|
399
498
|
OutboundQ.Front (&page, &length);
|
400
499
|
assert (page && (length > 0));
|
401
500
|
int n = SSL_write (pSSL, page, length);
|
501
|
+
pending = BIO_pending(pbioWrite);
|
502
|
+
|
402
503
|
if (n > 0) {
|
403
504
|
did_work = true;
|
404
505
|
OutboundQ.PopFront();
|
@@ -434,14 +535,60 @@ X509 *SslBox_t::GetPeerCert()
|
|
434
535
|
return cert;
|
435
536
|
}
|
436
537
|
|
538
|
+
/**********************
|
539
|
+
SslBox_t::GetCipherBits
|
540
|
+
**********************/
|
541
|
+
|
542
|
+
int SslBox_t::GetCipherBits()
|
543
|
+
{
|
544
|
+
int bits = -1;
|
545
|
+
if (pSSL)
|
546
|
+
SSL_get_cipher_bits(pSSL, &bits);
|
547
|
+
return bits;
|
548
|
+
}
|
549
|
+
|
550
|
+
/**********************
|
551
|
+
SslBox_t::GetCipherName
|
552
|
+
**********************/
|
553
|
+
|
554
|
+
const char *SslBox_t::GetCipherName()
|
555
|
+
{
|
556
|
+
if (pSSL)
|
557
|
+
return SSL_get_cipher_name(pSSL);
|
558
|
+
return NULL;
|
559
|
+
}
|
560
|
+
|
561
|
+
/**********************
|
562
|
+
SslBox_t::GetCipherProtocol
|
563
|
+
**********************/
|
564
|
+
|
565
|
+
const char *SslBox_t::GetCipherProtocol()
|
566
|
+
{
|
567
|
+
if (pSSL)
|
568
|
+
return SSL_get_cipher_version(pSSL);
|
569
|
+
return NULL;
|
570
|
+
}
|
571
|
+
|
572
|
+
/**********************
|
573
|
+
SslBox_t::GetSNIHostname
|
574
|
+
**********************/
|
575
|
+
|
576
|
+
const char *SslBox_t::GetSNIHostname()
|
577
|
+
{
|
578
|
+
#ifdef TLSEXT_NAMETYPE_host_name
|
579
|
+
if (pSSL)
|
580
|
+
return SSL_get_servername (pSSL, TLSEXT_NAMETYPE_host_name);
|
581
|
+
#endif
|
582
|
+
return NULL;
|
583
|
+
}
|
437
584
|
|
438
585
|
/******************
|
439
586
|
ssl_verify_wrapper
|
440
587
|
*******************/
|
441
588
|
|
442
|
-
extern "C" int ssl_verify_wrapper(int preverify_ok, X509_STORE_CTX *ctx)
|
589
|
+
extern "C" int ssl_verify_wrapper(int preverify_ok UNUSED, X509_STORE_CTX *ctx)
|
443
590
|
{
|
444
|
-
|
591
|
+
uintptr_t binding;
|
445
592
|
X509 *cert;
|
446
593
|
SSL *ssl;
|
447
594
|
BUF_MEM *buf;
|
@@ -450,7 +597,7 @@ extern "C" int ssl_verify_wrapper(int preverify_ok, X509_STORE_CTX *ctx)
|
|
450
597
|
|
451
598
|
cert = X509_STORE_CTX_get_current_cert(ctx);
|
452
599
|
ssl = (SSL*) X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
|
453
|
-
binding = (
|
600
|
+
binding = (uintptr_t) SSL_get_ex_data(ssl, 0);
|
454
601
|
|
455
602
|
out = BIO_new(BIO_s_mem());
|
456
603
|
PEM_write_bio_X509(out, cert);
|
data/ext/ssl.h
CHANGED
@@ -33,7 +33,7 @@ class SslContext_t
|
|
33
33
|
class SslContext_t
|
34
34
|
{
|
35
35
|
public:
|
36
|
-
SslContext_t (bool is_server, const string &privkeyfile, const string &certchainfile);
|
36
|
+
SslContext_t (bool is_server, const string &privkeyfile, const string &certchainfile, const string &cipherlist, const string &ecdh_curve, const string &dhparam, int ssl_version);
|
37
37
|
virtual ~SslContext_t();
|
38
38
|
|
39
39
|
private:
|
@@ -54,10 +54,14 @@ class SslContext_t
|
|
54
54
|
class SslBox_t
|
55
55
|
**************/
|
56
56
|
|
57
|
+
#define SSLBOX_INPUT_CHUNKSIZE 2019
|
58
|
+
#define SSLBOX_OUTPUT_CHUNKSIZE 2048
|
59
|
+
#define SSLBOX_WRITE_BUFFER_SIZE 8192 // (SSLBOX_OUTPUT_CHUNKSIZE * 4)
|
60
|
+
|
57
61
|
class SslBox_t
|
58
62
|
{
|
59
63
|
public:
|
60
|
-
SslBox_t (bool is_server, const string &privkeyfile, const string &certchainfile, bool verify_peer, const
|
64
|
+
SslBox_t (bool is_server, const string &privkeyfile, const string &certchainfile, bool verify_peer, bool fail_if_no_peer_cert, const string &snihostname, const string &cipherlist, const string &ecdh_curve, const string &dhparam, int ssl_version, const uintptr_t binding);
|
61
65
|
virtual ~SslBox_t();
|
62
66
|
|
63
67
|
int PutPlaintext (const char*, int);
|
@@ -69,6 +73,10 @@ class SslBox_t
|
|
69
73
|
bool IsHandshakeCompleted() {return bHandshakeCompleted;}
|
70
74
|
|
71
75
|
X509 *GetPeerCert();
|
76
|
+
int GetCipherBits();
|
77
|
+
const char *GetCipherName();
|
78
|
+
const char *GetCipherProtocol();
|
79
|
+
const char *GetSNIHostname();
|
72
80
|
|
73
81
|
void Shutdown();
|
74
82
|
|
@@ -78,6 +86,7 @@ class SslBox_t
|
|
78
86
|
bool bIsServer;
|
79
87
|
bool bHandshakeCompleted;
|
80
88
|
bool bVerifyPeer;
|
89
|
+
bool bFailIfNoPeerCert;
|
81
90
|
SSL *pSSL;
|
82
91
|
BIO *pbioRead;
|
83
92
|
BIO *pbioWrite;
|
@@ -569,6 +569,22 @@ public class EmReactor {
|
|
569
569
|
return Connections.get(sig).isNotifyWritable();
|
570
570
|
}
|
571
571
|
|
572
|
+
public boolean pauseConnection (long sig) {
|
573
|
+
return ((EventableSocketChannel) Connections.get(sig)).pause();
|
574
|
+
}
|
575
|
+
|
576
|
+
public boolean resumeConnection (long sig) {
|
577
|
+
return ((EventableSocketChannel) Connections.get(sig)).resume();
|
578
|
+
}
|
579
|
+
|
580
|
+
public boolean isConnectionPaused (long sig) {
|
581
|
+
return ((EventableSocketChannel) Connections.get(sig)).isPaused();
|
582
|
+
}
|
583
|
+
|
584
|
+
public long getOutboundDataSize (long sig) {
|
585
|
+
return Connections.get(sig).getOutboundDataSize();
|
586
|
+
}
|
587
|
+
|
572
588
|
public int getConnectionCount() {
|
573
589
|
return Connections.size() + Acceptors.size();
|
574
590
|
}
|
@@ -54,6 +54,7 @@ public class EventableDatagramChannel implements EventableChannel {
|
|
54
54
|
Selector selector;
|
55
55
|
boolean bCloseScheduled;
|
56
56
|
LinkedList<Packet> outboundQ;
|
57
|
+
long outboundS;
|
57
58
|
SocketAddress returnAddress;
|
58
59
|
|
59
60
|
|
@@ -63,6 +64,7 @@ public class EventableDatagramChannel implements EventableChannel {
|
|
63
64
|
selector = sel;
|
64
65
|
bCloseScheduled = false;
|
65
66
|
outboundQ = new LinkedList<Packet>();
|
67
|
+
outboundS = 0;
|
66
68
|
|
67
69
|
dc.register(selector, SelectionKey.OP_READ, this);
|
68
70
|
}
|
@@ -71,6 +73,7 @@ public class EventableDatagramChannel implements EventableChannel {
|
|
71
73
|
try {
|
72
74
|
if ((!bCloseScheduled) && (bb.remaining() > 0)) {
|
73
75
|
outboundQ.addLast(new Packet(bb, returnAddress));
|
76
|
+
outboundS += bb.remaining();
|
74
77
|
channel.register(selector, SelectionKey.OP_WRITE | SelectionKey.OP_READ, this);
|
75
78
|
}
|
76
79
|
} catch (ClosedChannelException e) {
|
@@ -82,6 +85,7 @@ public class EventableDatagramChannel implements EventableChannel {
|
|
82
85
|
try {
|
83
86
|
if ((!bCloseScheduled) && (bb.remaining() > 0)) {
|
84
87
|
outboundQ.addLast(new Packet (bb, new InetSocketAddress (recipAddress, recipPort)));
|
88
|
+
outboundS += bb.remaining();
|
85
89
|
channel.register(selector, SelectionKey.OP_WRITE | SelectionKey.OP_READ, this);
|
86
90
|
}
|
87
91
|
} catch (ClosedChannelException e) {
|
@@ -136,6 +140,7 @@ public class EventableDatagramChannel implements EventableChannel {
|
|
136
140
|
try {
|
137
141
|
// With a datagram socket, it's ok to send an empty buffer.
|
138
142
|
written = channel.send(p.bb, p.recipient);
|
143
|
+
outboundS -= written;
|
139
144
|
}
|
140
145
|
catch (IOException e) {
|
141
146
|
return false;
|
@@ -192,4 +197,5 @@ public class EventableDatagramChannel implements EventableChannel {
|
|
192
197
|
public boolean isWatchOnly() { return false; }
|
193
198
|
public boolean isNotifyReadable() { return false; }
|
194
199
|
public boolean isNotifyWritable() { return false; }
|
200
|
+
public long getOutboundDataSize() { return outboundS; }
|
195
201
|
}
|
@@ -54,6 +54,7 @@ public class EventableSocketChannel implements EventableChannel {
|
|
54
54
|
|
55
55
|
long binding;
|
56
56
|
LinkedList<ByteBuffer> outboundQ;
|
57
|
+
long outboundS;
|
57
58
|
|
58
59
|
boolean bCloseScheduled;
|
59
60
|
boolean bConnectPending;
|
@@ -61,6 +62,7 @@ public class EventableSocketChannel implements EventableChannel {
|
|
61
62
|
boolean bAttached;
|
62
63
|
boolean bNotifyReadable;
|
63
64
|
boolean bNotifyWritable;
|
65
|
+
boolean bPaused;
|
64
66
|
|
65
67
|
SSLEngine sslEngine;
|
66
68
|
SSLContext sslContext;
|
@@ -76,6 +78,7 @@ public class EventableSocketChannel implements EventableChannel {
|
|
76
78
|
bNotifyReadable = false;
|
77
79
|
bNotifyWritable = false;
|
78
80
|
outboundQ = new LinkedList<ByteBuffer>();
|
81
|
+
outboundS = 0;
|
79
82
|
}
|
80
83
|
|
81
84
|
public long getBinding() {
|
@@ -164,12 +167,14 @@ public class EventableSocketChannel implements EventableChannel {
|
|
164
167
|
sslEngine.wrap(bb, b);
|
165
168
|
b.flip();
|
166
169
|
outboundQ.addLast(b);
|
170
|
+
outboundS += b.remaining();
|
167
171
|
} catch (SSLException e) {
|
168
172
|
throw new RuntimeException ("ssl error");
|
169
173
|
}
|
170
174
|
}
|
171
175
|
else {
|
172
176
|
outboundQ.addLast(bb);
|
177
|
+
outboundS += bb.remaining();
|
173
178
|
}
|
174
179
|
|
175
180
|
updateEvents();
|
@@ -188,6 +193,8 @@ public class EventableSocketChannel implements EventableChannel {
|
|
188
193
|
throw new IOException ("eof");
|
189
194
|
}
|
190
195
|
|
196
|
+
public long getOutboundDataSize() { return outboundS; }
|
197
|
+
|
191
198
|
/**
|
192
199
|
* Called by the reactor when we have selected writable.
|
193
200
|
* Return false to indicate an error that should cause the connection to close.
|
@@ -196,23 +203,35 @@ public class EventableSocketChannel implements EventableChannel {
|
|
196
203
|
* this code is written, we're depending on a nonblocking write NOT TO CONSUME
|
197
204
|
* the whole outbound buffer in this case, rather than firing an exception.
|
198
205
|
* We should somehow verify that this is indeed Java's defined behavior.
|
199
|
-
* Also TODO, see if we can use gather I/O rather than one write at a time.
|
200
|
-
* Ought to be a big performance enhancer.
|
201
206
|
* @return
|
202
207
|
*/
|
203
208
|
public boolean writeOutboundData() throws IOException {
|
209
|
+
ByteBuffer[] bufs = new ByteBuffer[64];
|
210
|
+
int i;
|
211
|
+
long written, toWrite;
|
204
212
|
while (!outboundQ.isEmpty()) {
|
205
|
-
|
206
|
-
|
207
|
-
|
213
|
+
i = 0;
|
214
|
+
toWrite = 0;
|
215
|
+
written = 0;
|
216
|
+
while (i < 64 && !outboundQ.isEmpty()) {
|
217
|
+
bufs[i] = outboundQ.removeFirst();
|
218
|
+
toWrite += bufs[i].remaining();
|
219
|
+
i++;
|
220
|
+
}
|
221
|
+
if (toWrite > 0)
|
222
|
+
written = channel.write(bufs, 0, i);
|
208
223
|
|
224
|
+
outboundS -= written;
|
209
225
|
// Did we consume the whole outbound buffer? If yes,
|
210
226
|
// pop it off and keep looping. If no, the outbound network
|
211
227
|
// buffers are full, so break out of here.
|
212
|
-
if (
|
213
|
-
|
214
|
-
|
228
|
+
if (written < toWrite) {
|
229
|
+
while (i > 0 && bufs[i-1].remaining() > 0) {
|
230
|
+
outboundQ.addFirst(bufs[i-1]);
|
231
|
+
i--;
|
232
|
+
}
|
215
233
|
break;
|
234
|
+
}
|
216
235
|
}
|
217
236
|
|
218
237
|
if (outboundQ.isEmpty() && !bCloseScheduled) {
|
@@ -244,8 +263,10 @@ public class EventableSocketChannel implements EventableChannel {
|
|
244
263
|
|
245
264
|
public boolean scheduleClose (boolean afterWriting) {
|
246
265
|
// TODO: What the hell happens here if bConnectPending is set?
|
247
|
-
if (!afterWriting)
|
266
|
+
if (!afterWriting) {
|
248
267
|
outboundQ.clear();
|
268
|
+
outboundS = 0;
|
269
|
+
}
|
249
270
|
|
250
271
|
if (outboundQ.isEmpty())
|
251
272
|
return true;
|
@@ -331,6 +352,30 @@ public class EventableSocketChannel implements EventableChannel {
|
|
331
352
|
}
|
332
353
|
public boolean isNotifyWritable() { return bNotifyWritable; }
|
333
354
|
|
355
|
+
public boolean pause() {
|
356
|
+
if (bWatchOnly) {
|
357
|
+
throw new RuntimeException ("cannot pause/resume 'watch only' connections, set notify readable/writable instead");
|
358
|
+
}
|
359
|
+
boolean old = bPaused;
|
360
|
+
bPaused = true;
|
361
|
+
updateEvents();
|
362
|
+
return !old;
|
363
|
+
}
|
364
|
+
|
365
|
+
public boolean resume() {
|
366
|
+
if (bWatchOnly) {
|
367
|
+
throw new RuntimeException ("cannot pause/resume 'watch only' connections, set notify readable/writable instead");
|
368
|
+
}
|
369
|
+
boolean old = bPaused;
|
370
|
+
bPaused = false;
|
371
|
+
updateEvents();
|
372
|
+
return old;
|
373
|
+
}
|
374
|
+
|
375
|
+
public boolean isPaused() {
|
376
|
+
return bPaused;
|
377
|
+
}
|
378
|
+
|
334
379
|
private void updateEvents() {
|
335
380
|
if (channelKey == null)
|
336
381
|
return;
|
@@ -353,7 +398,7 @@ public class EventableSocketChannel implements EventableChannel {
|
|
353
398
|
if (bNotifyWritable)
|
354
399
|
events |= SelectionKey.OP_WRITE;
|
355
400
|
}
|
356
|
-
else
|
401
|
+
else if (!bPaused)
|
357
402
|
{
|
358
403
|
if (bConnectPending)
|
359
404
|
events |= SelectionKey.OP_CONNECT;
|