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.
Files changed (101) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +84 -1
  3. data/README.md +6 -7
  4. data/ext/binder.cpp +10 -10
  5. data/ext/binder.h +5 -5
  6. data/ext/cmain.cpp +173 -61
  7. data/ext/ed.cpp +262 -127
  8. data/ext/ed.h +50 -30
  9. data/ext/em.cpp +491 -445
  10. data/ext/em.h +101 -36
  11. data/ext/eventmachine.h +67 -51
  12. data/ext/extconf.rb +124 -31
  13. data/ext/fastfilereader/extconf.rb +9 -2
  14. data/ext/fastfilereader/mapper.cpp +3 -1
  15. data/ext/fastfilereader/rubymain.cpp +7 -7
  16. data/ext/kb.cpp +1 -1
  17. data/ext/pipe.cpp +11 -4
  18. data/ext/project.h +26 -6
  19. data/ext/rubymain.cpp +408 -201
  20. data/ext/ssl.cpp +167 -20
  21. data/ext/ssl.h +11 -2
  22. data/java/src/com/rubyeventmachine/EmReactor.java +16 -0
  23. data/java/src/com/rubyeventmachine/EventableChannel.java +2 -0
  24. data/java/src/com/rubyeventmachine/EventableDatagramChannel.java +6 -0
  25. data/java/src/com/rubyeventmachine/EventableSocketChannel.java +55 -10
  26. data/lib/1.9/fastfilereaderext.so +0 -0
  27. data/lib/1.9/rubyeventmachine.so +0 -0
  28. data/lib/2.0/fastfilereaderext.so +0 -0
  29. data/lib/2.0/rubyeventmachine.so +0 -0
  30. data/lib/2.1/fastfilereaderext.so +0 -0
  31. data/lib/2.1/rubyeventmachine.so +0 -0
  32. data/lib/2.2/fastfilereaderext.so +0 -0
  33. data/lib/2.2/rubyeventmachine.so +0 -0
  34. data/lib/2.3/fastfilereaderext.so +0 -0
  35. data/lib/2.3/rubyeventmachine.so +0 -0
  36. data/lib/em/buftok.rb +34 -85
  37. data/lib/em/channel.rb +5 -0
  38. data/lib/em/completion.rb +2 -2
  39. data/lib/em/connection.rb +62 -4
  40. data/lib/em/iterator.rb +30 -48
  41. data/lib/em/pool.rb +1 -1
  42. data/lib/em/protocols/httpclient.rb +31 -11
  43. data/lib/em/protocols/line_and_text.rb +4 -4
  44. data/lib/em/protocols/linetext2.rb +44 -39
  45. data/lib/em/protocols/smtpclient.rb +60 -31
  46. data/lib/em/protocols/smtpserver.rb +32 -9
  47. data/lib/em/pure_ruby.rb +8 -3
  48. data/lib/em/queue.rb +16 -7
  49. data/lib/em/resolver.rb +64 -24
  50. data/lib/em/threaded_resource.rb +2 -2
  51. data/lib/em/tick_loop.rb +19 -19
  52. data/lib/em/version.rb +1 -1
  53. data/lib/eventmachine.rb +96 -49
  54. data/lib/jeventmachine.rb +17 -0
  55. data/rakelib/package.rake +31 -4
  56. data/tests/dhparam.pem +13 -0
  57. data/tests/em_test_helper.rb +87 -0
  58. data/tests/test_attach.rb +25 -0
  59. data/tests/test_basic.rb +27 -38
  60. data/tests/test_channel.rb +14 -1
  61. data/tests/test_completion.rb +1 -0
  62. data/tests/test_connection_count.rb +22 -1
  63. data/tests/test_connection_write.rb +35 -0
  64. data/tests/test_defer.rb +17 -0
  65. data/tests/test_epoll.rb +26 -14
  66. data/tests/test_file_watch.rb +1 -0
  67. data/tests/test_fork.rb +75 -0
  68. data/tests/test_httpclient.rb +43 -0
  69. data/tests/test_idle_connection.rb +6 -4
  70. data/tests/test_ipv4.rb +125 -0
  71. data/tests/test_ipv6.rb +131 -0
  72. data/tests/test_iterator.rb +115 -0
  73. data/tests/test_kb.rb +19 -25
  74. data/tests/test_ltp2.rb +20 -0
  75. data/tests/test_many_fds.rb +22 -0
  76. data/tests/test_pause.rb +29 -0
  77. data/tests/test_pool.rb +2 -0
  78. data/tests/test_process_watch.rb +2 -0
  79. data/tests/test_processes.rb +7 -7
  80. data/tests/test_queue.rb +14 -0
  81. data/tests/test_resolver.rb +56 -7
  82. data/tests/test_set_sock_opt.rb +2 -0
  83. data/tests/test_smtpclient.rb +20 -0
  84. data/tests/test_ssl_args.rb +2 -2
  85. data/tests/test_ssl_dhparam.rb +83 -0
  86. data/tests/test_ssl_ecdh_curve.rb +79 -0
  87. data/tests/test_ssl_extensions.rb +49 -0
  88. data/tests/test_ssl_methods.rb +22 -5
  89. data/tests/test_ssl_protocols.rb +246 -0
  90. data/tests/test_ssl_verify.rb +103 -59
  91. data/tests/test_system.rb +4 -0
  92. data/tests/test_threaded_resource.rb +8 -0
  93. data/tests/test_unbind_reason.rb +5 -1
  94. metadata +173 -107
  95. data/.gitignore +0 -21
  96. data/.travis.yml +0 -12
  97. data/.yardopts +0 -7
  98. data/Gemfile +0 -2
  99. data/Rakefile +0 -20
  100. data/eventmachine.gemspec +0 -36
  101. data/rakelib/cpp.rake_example +0 -77
@@ -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
- bIsServer = is_server;
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
- //SSL_CTX_set_options (pCtx, (SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3));
154
- #ifdef SSL_MODE_RELEASE_BUFFERS
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
- SSL_CTX_set_cipher_list (pCtx, "ALL:!ADH:!LOW:!EXP:!DES-CBC3-SHA:@STRENGTH");
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 (is_server) {
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 unsigned long binding):
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
- SSL_set_verify(pSSL, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, ssl_verify_wrapper);
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 < 0) {
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
- unsigned long binding;
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 = (unsigned long) SSL_get_ex_data(ssl, 0);
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 unsigned long binding);
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
  }
@@ -57,6 +57,8 @@ public interface EventableChannel {
57
57
 
58
58
  public boolean writeOutboundData() throws IOException;
59
59
 
60
+ public long getOutboundDataSize();
61
+
60
62
  public void setCommInactivityTimeout (long seconds);
61
63
 
62
64
  public Object[] getPeerName();
@@ -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
- ByteBuffer b = outboundQ.getFirst();
206
- if (b.remaining() > 0)
207
- channel.write(b);
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 (b.remaining() == 0)
213
- outboundQ.removeFirst();
214
- else
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;