eventmachine 0.12.4 → 0.12.6

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.
Files changed (51) hide show
  1. data/.gitignore +13 -0
  2. data/Rakefile +66 -3
  3. data/docs/ChangeLog +38 -10
  4. data/eventmachine.gemspec +32 -0
  5. data/ext/cmain.cpp +45 -3
  6. data/ext/cplusplus.cpp +21 -0
  7. data/ext/ed.cpp +34 -1
  8. data/ext/ed.h +12 -0
  9. data/ext/em.cpp +23 -3
  10. data/ext/em.h +6 -2
  11. data/ext/eventmachine.h +9 -1
  12. data/ext/eventmachine_cpp.h +1 -0
  13. data/ext/extconf.rb +8 -29
  14. data/ext/fastfilereader/extconf.rb +50 -134
  15. data/ext/fastfilereader/mapper.cpp +12 -0
  16. data/ext/fastfilereader/mapper.h +1 -1
  17. data/ext/kb.cpp +0 -286
  18. data/ext/pipe.cpp +30 -13
  19. data/ext/rubymain.cpp +127 -12
  20. data/ext/ssl.cpp +15 -0
  21. data/ext/ssl.h +4 -0
  22. data/java/.classpath +8 -0
  23. data/java/.project +17 -0
  24. data/lib/em/processes.rb +45 -0
  25. data/lib/eventmachine.rb +260 -102
  26. data/lib/eventmachine_version.rb +1 -1
  27. data/lib/pr_eventmachine.rb +1 -1
  28. data/lib/protocols/httpcli2.rb +10 -1
  29. data/lib/protocols/httpclient.rb +2 -2
  30. data/lib/protocols/memcache.rb +293 -0
  31. data/lib/protocols/smtpserver.rb +1 -1
  32. data/setup.rb +1585 -0
  33. data/tasks/tests.rake +1 -0
  34. data/tests/test_attach.rb +19 -2
  35. data/tests/test_basic.rb +2 -2
  36. data/tests/test_connection_count.rb +45 -0
  37. data/tests/test_error_handler.rb +35 -0
  38. data/tests/test_errors.rb +3 -3
  39. data/tests/test_exc.rb +2 -2
  40. data/tests/test_handler_check.rb +37 -0
  41. data/tests/test_httpclient2.rb +1 -1
  42. data/tests/test_kb.rb +2 -2
  43. data/tests/test_next_tick.rb +7 -0
  44. data/tests/test_processes.rb +39 -0
  45. data/tests/test_pure.rb +2 -2
  46. data/tests/test_send_file.rb +1 -1
  47. data/tests/test_ssl_args.rb +3 -3
  48. data/tests/test_ssl_methods.rb +50 -0
  49. data/tests/test_timers.rb +3 -1
  50. data/web/whatis +7 -0
  51. metadata +88 -84
@@ -210,6 +210,7 @@ SslBox_t::SslBox_t
210
210
 
211
211
  SslBox_t::SslBox_t (bool is_server, const string &privkeyfile, const string &certchainfile):
212
212
  bIsServer (is_server),
213
+ bHandshakeCompleted (false),
213
214
  pSSL (NULL),
214
215
  pbioRead (NULL),
215
216
  pbioWrite (NULL)
@@ -289,6 +290,7 @@ int SslBox_t::GetPlaintext (char *buf, int bufsize)
289
290
  else
290
291
  return 0;
291
292
  }
293
+ bHandshakeCompleted = true;
292
294
  // If handshake finished, FALL THROUGH and return the available plaintext.
293
295
  }
294
296
 
@@ -403,6 +405,19 @@ int SslBox_t::PutPlaintext (const char *buf, int bufsize)
403
405
  return 0;
404
406
  }
405
407
 
408
+ /**********************
409
+ SslBox_t::GetPeerCert
410
+ **********************/
411
+
412
+ X509 *SslBox_t::GetPeerCert()
413
+ {
414
+ X509 *cert = NULL;
415
+
416
+ if (pSSL)
417
+ cert = SSL_get_peer_certificate(pSSL);
418
+
419
+ return cert;
420
+ }
406
421
 
407
422
  #endif // WITH_SSL
408
423
 
data/ext/ssl.h CHANGED
@@ -66,6 +66,9 @@ class SslBox_t
66
66
  bool PutCiphertext (const char*, int);
67
67
  bool CanGetCiphertext();
68
68
  int GetCiphertext (char*, int);
69
+ bool IsHandshakeCompleted() {return bHandshakeCompleted;}
70
+
71
+ X509 *GetPeerCert();
69
72
 
70
73
  void Shutdown();
71
74
 
@@ -73,6 +76,7 @@ class SslBox_t
73
76
  SslContext_t *Context;
74
77
 
75
78
  bool bIsServer;
79
+ bool bHandshakeCompleted;
76
80
  SSL *pSSL;
77
81
  BIO *pbioRead;
78
82
  BIO *pbioWrite;
@@ -0,0 +1,8 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <classpath>
3
+ <classpathentry kind="src" path="src"/>
4
+ <classpathentry excluding="src/" kind="src" path=""/>
5
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
6
+ <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
7
+ <classpathentry kind="output" path="src"/>
8
+ </classpath>
@@ -0,0 +1,17 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <projectDescription>
3
+ <name>em_reactor</name>
4
+ <comment></comment>
5
+ <projects>
6
+ </projects>
7
+ <buildSpec>
8
+ <buildCommand>
9
+ <name>org.eclipse.jdt.core.javabuilder</name>
10
+ <arguments>
11
+ </arguments>
12
+ </buildCommand>
13
+ </buildSpec>
14
+ <natures>
15
+ <nature>org.eclipse.jdt.core.javanature</nature>
16
+ </natures>
17
+ </projectDescription>
@@ -63,6 +63,51 @@ module EventMachine
63
63
  succeed( @data.join )
64
64
  end
65
65
  end
66
+
67
+ class SystemCmd < EventMachine::Connection # :nodoc:
68
+ def initialize cb
69
+ @cb = cb
70
+ @output = []
71
+ end
72
+ def receive_data data
73
+ @output << data
74
+ end
75
+ def unbind
76
+ @cb.call @output.join(''), get_status if @cb
77
+ end
78
+ end
79
+
80
+ # EM::system is a simple wrapper for EM::popen. It is similar to Kernel::system, but requires a
81
+ # single string argument for the command and performs no shell expansion.
82
+ #
83
+ # The block or proc passed to EM::system is called with two arguments: the output generated by the command,
84
+ # and a Process::Status that contains information about the command's execution.
85
+ #
86
+ # EM.run{
87
+ # EM.system('ls'){ |output,status| puts output if status.exitstatus == 0 }
88
+ # }
89
+ #
90
+ # You can also supply an additional proc to send some data to the process:
91
+ #
92
+ # EM.run{
93
+ # EM.system('sh', proc{ |process|
94
+ # process.send_data("echo hello\n")
95
+ # process.send_data("exit\n")
96
+ # }, proc{ |out,status|
97
+ # puts(out)
98
+ # })
99
+ # }
100
+ #
101
+ # Like EM::popen, EM::system currently does not work on windows.
102
+ #
103
+ def EventMachine::system cmd, *args, &cb
104
+ cb ||= args.pop if args.last.is_a? Proc
105
+ init = args.pop if args.last.is_a? Proc
106
+
107
+ EM.popen(cmd, SystemCmd, cb) do |c|
108
+ init[c] if init
109
+ end
110
+ end
66
111
  end
67
112
 
68
113
 
@@ -234,6 +234,9 @@ module EventMachine
234
234
  @reactor_running = true
235
235
  initialize_event_machine
236
236
  (b = blk || block) and add_timer(0, b)
237
+ if @next_tick_queue && !@next_tick_queue.empty?
238
+ add_timer(0) { signal_loopbreak }
239
+ end
237
240
  run_machine
238
241
  ensure
239
242
  begin
@@ -535,12 +538,15 @@ module EventMachine
535
538
  begin
536
539
  port = Integer(port)
537
540
  rescue ArgumentError, TypeError
541
+ # there was no port, so server must be a unix domain socket
542
+ # the port argument is actually the handler, and the handler is one of the args
538
543
  args.unshift handler if handler
539
544
  handler = port
540
545
  port = nil
541
546
  end if port
542
547
 
543
548
  klass = if (handler and handler.is_a?(Class))
549
+ raise ArgumentError, 'must provide module or subclass of EventMachine::Connection' unless Connection > handler
544
550
  handler
545
551
  else
546
552
  Class.new( Connection ) {handler and include handler}
@@ -676,12 +682,15 @@ module EventMachine
676
682
  begin
677
683
  port = Integer(port)
678
684
  rescue ArgumentError, TypeError
685
+ # there was no port, so server must be a unix domain socket
686
+ # the port argument is actually the handler, and the handler is one of the args
679
687
  args.unshift handler if handler
680
688
  handler = port
681
689
  port = nil
682
690
  end if port
683
691
 
684
692
  klass = if (handler and handler.is_a?(Class))
693
+ raise ArgumentError, 'must provide module or subclass of EventMachine::Connection' unless Connection > handler
685
694
  handler
686
695
  else
687
696
  Class.new( Connection ) {handler and include handler}
@@ -749,6 +758,7 @@ module EventMachine
749
758
  # Thanks to Riham Aldakkak (eSpace Technologies) for the initial patch
750
759
  def EventMachine::attach io, handler=nil, *args
751
760
  klass = if (handler and handler.is_a?(Class))
761
+ raise ArgumentError, 'must provide module or subclass of EventMachine::Connection' unless Connection > handler
752
762
  handler
753
763
  else
754
764
  Class.new( Connection ) {handler and include handler}
@@ -880,6 +890,7 @@ module EventMachine
880
890
  #
881
891
  def self::open_datagram_socket address, port, handler=nil, *args
882
892
  klass = if (handler and handler.is_a?(Class))
893
+ raise ArgumentError, 'must provide module or subclass of EventMachine::Connection' unless Connection > handler
883
894
  handler
884
895
  else
885
896
  Class.new( Connection ) {handler and include handler}
@@ -924,6 +935,35 @@ module EventMachine
924
935
  set_max_timer_count ct
925
936
  end
926
937
 
938
+ # Gets the current maximum number of allowed timers
939
+ #
940
+ def self::get_max_timers
941
+ get_max_timer_count
942
+ end
943
+
944
+ # Returns the total number of connections (file descriptors) currently held by the reactor.
945
+ # Note that a tick must pass after the 'initiation' of a connection for this number to increment.
946
+ #
947
+ # For example, $count will be 0 in this case:
948
+ #
949
+ # EM.run {
950
+ # EM.connect("rubyeventmachine.com", 80)
951
+ # $count = EM.connection_count
952
+ # }
953
+ #
954
+ # In this example, $count will be 1 since the connection has been established in the next loop of the reactor.
955
+ #
956
+ # EM.run {
957
+ # EM.connect("rubyeventmachine.com", 80)
958
+ # EM.next_tick {
959
+ # $count = EM.connection_count
960
+ # }
961
+ # }
962
+ #
963
+ def self::connection_count
964
+ self::get_connection_count
965
+ end
966
+
927
967
  #--
928
968
  # The is the responder for the loopback-signalled event.
929
969
  # It can be fired either by code running on a separate thread (EM#defer) or on
@@ -1050,7 +1090,7 @@ module EventMachine
1050
1090
  def self::next_tick pr=nil, &block
1051
1091
  raise "no argument or block given" unless ((pr && pr.respond_to?(:call)) or block)
1052
1092
  (@next_tick_queue ||= []) << ( pr || block )
1053
- EventMachine.signal_loopbreak
1093
+ signal_loopbreak if reactor_running?
1054
1094
  =begin
1055
1095
  (@next_tick_procs ||= []) << (pr || block)
1056
1096
  if @next_tick_procs.length == 1
@@ -1099,13 +1139,33 @@ module EventMachine
1099
1139
 
1100
1140
 
1101
1141
 
1102
- # TODO, must document popen. At this moment, it's only available on Unix.
1103
- # This limitation is expected to go away.
1142
+ # Run an external process. This does not currently work on Windows.
1143
+ #
1144
+ # module RubyCounter
1145
+ # def post_init
1146
+ # # count up to 5
1147
+ # send_data "5\n"
1148
+ # end
1149
+ # def receive_data data
1150
+ # puts "ruby sent me: #{data}"
1151
+ # end
1152
+ # def unbind
1153
+ # puts "ruby died with exit status: #{get_status.exitstatus}"
1154
+ # end
1155
+ # end
1156
+ #
1157
+ # EM.run{
1158
+ # EM.popen("ruby -e' $stdout.sync = true; gets.to_i.times{ |i| puts i+1; sleep 1 } '", RubyCounter)
1159
+ # }
1160
+ #
1161
+ # Also see EM::DeferrableChildProcess and EM::system
1104
1162
  #--
1163
+ # At this moment, it's only available on Unix.
1105
1164
  # Perhaps misnamed since the underlying function uses socketpair and is full-duplex.
1106
1165
  #
1107
1166
  def self::popen cmd, handler=nil, *args
1108
1167
  klass = if (handler and handler.is_a?(Class))
1168
+ raise ArgumentError, 'must provide module or subclass of EventMachine::Connection' unless Connection > handler
1109
1169
  handler
1110
1170
  else
1111
1171
  Class.new( Connection ) {handler and include handler}
@@ -1141,6 +1201,7 @@ module EventMachine
1141
1201
  #
1142
1202
  def EventMachine::open_keyboard handler=nil, *args
1143
1203
  klass = if (handler and handler.is_a?(Class))
1204
+ raise ArgumentError, 'must provide module or subclass of EventMachine::Connection' unless Connection > handler
1144
1205
  handler
1145
1206
  else
1146
1207
  Class.new( Connection ) {handler and include handler}
@@ -1159,7 +1220,19 @@ module EventMachine
1159
1220
  c
1160
1221
  end
1161
1222
 
1162
-
1223
+ # Catch-all for errors raised during event loop callbacks.
1224
+ #
1225
+ # EM.error_handler{ |e|
1226
+ # puts "Error raised during event loop: #{e.message}"
1227
+ # }
1228
+ #
1229
+ def EventMachine::error_handler cb = nil, &blk
1230
+ if cb or blk
1231
+ @error_handler = cb || blk
1232
+ elsif instance_variable_defined? :@error_handler
1233
+ remove_instance_variable :@error_handler
1234
+ end
1235
+ end
1163
1236
 
1164
1237
  private
1165
1238
  def EventMachine::event_callback conn_binding, opcode, data
@@ -1177,10 +1250,7 @@ module EventMachine
1177
1250
  # runs down open connections). It should go on the other calls to user
1178
1251
  # code, but the performance impact may be too large.
1179
1252
  #
1180
- if opcode == ConnectionData
1181
- c = @conns[conn_binding] or raise ConnectionNotBound, "received data #{data} for unknown signature: #{conn_binding}"
1182
- c.receive_data data
1183
- elsif opcode == ConnectionUnbound
1253
+ if opcode == ConnectionUnbound
1184
1254
  if c = @conns.delete( conn_binding )
1185
1255
  begin
1186
1256
  c.unbind
@@ -1200,12 +1270,17 @@ module EventMachine
1200
1270
  @conns[data] = c
1201
1271
  blk and blk.call(c)
1202
1272
  c # (needed?)
1203
- elsif opcode == TimerFired
1204
- t = @timers.delete( data ) or raise UnknownTimerFired, "timer data: #{data}"
1205
- t.call
1206
1273
  elsif opcode == ConnectionCompleted
1207
1274
  c = @conns[conn_binding] or raise ConnectionNotBound, "received ConnectionCompleted for unknown signature: #{conn_binding}"
1208
1275
  c.connection_completed
1276
+ ##
1277
+ # The remaining code is a fallback for the pure ruby reactor. Usually these events are handled in the C event_callback() in rubymain.cpp
1278
+ elsif opcode == TimerFired
1279
+ t = @timers.delete( data ) or raise UnknownTimerFired, "timer data: #{data}"
1280
+ t.call
1281
+ elsif opcode == ConnectionData
1282
+ c = @conns[conn_binding] or raise ConnectionNotBound, "received data #{data} for unknown signature: #{conn_binding}"
1283
+ c.receive_data data
1209
1284
  elsif opcode == LoopbreakSignalled
1210
1285
  run_deferred_callbacks
1211
1286
  elsif opcode == ConnectionNotifyReadable
@@ -1217,98 +1292,102 @@ module EventMachine
1217
1292
  end
1218
1293
  end
1219
1294
 
1220
- private
1221
- def EventMachine::original_event_callback conn_binding, opcode, data
1222
- #
1223
- # Added 03Oct07: Any code path that invokes user-written code must
1224
- # wrap itself in a begin/rescue for RuntimeErrors, that calls the
1225
- # user-overridable class method #handle_runtime_error.
1226
- #
1227
- if opcode == ConnectionData
1228
- c = @conns[conn_binding] or raise ConnectionNotBound
1229
- begin
1230
- c.receive_data data
1231
- rescue
1232
- EventMachine.handle_runtime_error
1233
- end
1234
- elsif opcode == ConnectionUnbound
1235
- if c = @conns.delete( conn_binding )
1236
- begin
1237
- c.unbind
1238
- rescue
1239
- EventMachine.handle_runtime_error
1240
- end
1241
- elsif c = @acceptors.delete( conn_binding )
1242
- # no-op
1243
- else
1244
- raise ConnectionNotBound
1245
- end
1246
- elsif opcode == ConnectionAccepted
1247
- accep,args,blk = @acceptors[conn_binding]
1248
- raise NoHandlerForAcceptedConnection unless accep
1249
- c = accep.new data, *args
1250
- @conns[data] = c
1251
- begin
1252
- blk and blk.call(c)
1253
- rescue
1254
- EventMachine.handle_runtime_error
1255
- end
1256
- c # (needed?)
1257
- elsif opcode == TimerFired
1258
- t = @timers.delete( data ) or raise UnknownTimerFired
1259
- begin
1260
- t.call
1261
- rescue
1262
- EventMachine.handle_runtime_error
1263
- end
1264
- elsif opcode == ConnectionCompleted
1265
- c = @conns[conn_binding] or raise ConnectionNotBound
1266
- begin
1267
- c.connection_completed
1268
- rescue
1269
- EventMachine.handle_runtime_error
1270
- end
1271
- elsif opcode == LoopbreakSignalled
1272
- begin
1273
- run_deferred_callbacks
1274
- rescue
1275
- EventMachine.handle_runtime_error
1276
- end
1277
- end
1278
- end
1279
-
1280
-
1281
- # Default handler for RuntimeErrors that are raised in user code.
1282
- # The default behavior is to re-raise the error, which ends your program.
1283
- # To override the default behavior, re-implement this method in your code.
1284
- # For example:
1285
- #
1286
- # module EventMachine
1287
- # def self.handle_runtime_error
1288
- # $>.puts $!
1289
- # end
1290
- # end
1291
- #
1292
1295
  #--
1293
- # We need to ensure that any code path which invokes user code rescues RuntimeError
1294
- # and calls this method. The obvious place to do that is in #event_callback,
1295
- # but, scurrilously, it turns out that we need to be finer grained that that.
1296
- # Periodic timers, in particular, wrap their invocations of user code inside
1297
- # procs that do other stuff we can't not do, like schedule the next invocation.
1298
- # This is a potential non-robustness, since we need to remember to hook in the
1299
- # error handler whenever and wherever we change how user code is invoked.
1300
- #
1301
- def EventMachine::handle_runtime_error
1302
- @runtime_error_hook ? @runtime_error_hook.call : raise
1303
- end
1304
-
1305
- # Sets a handler for RuntimeErrors that are raised in user code.
1306
- # Pass a block with no parameters. You can also call this method without a block,
1307
- # which restores the default behavior (see #handle_runtime_error).
1308
- #
1309
- def EventMachine::set_runtime_error_hook &blk
1310
- @runtime_error_hook = blk
1311
- end
1296
+ # The original event_callback below handled runtime errors in ruby and degraded performance significantly.
1297
+ # An optional C-based error handler is now available via EM::error_handler
1298
+ #
1299
+ # private
1300
+ # def EventMachine::original_event_callback conn_binding, opcode, data
1301
+ # #
1302
+ # # Added 03Oct07: Any code path that invokes user-written code must
1303
+ # # wrap itself in a begin/rescue for RuntimeErrors, that calls the
1304
+ # # user-overridable class method #handle_runtime_error.
1305
+ # #
1306
+ # if opcode == ConnectionData
1307
+ # c = @conns[conn_binding] or raise ConnectionNotBound
1308
+ # begin
1309
+ # c.receive_data data
1310
+ # rescue
1311
+ # EventMachine.handle_runtime_error
1312
+ # end
1313
+ # elsif opcode == ConnectionUnbound
1314
+ # if c = @conns.delete( conn_binding )
1315
+ # begin
1316
+ # c.unbind
1317
+ # rescue
1318
+ # EventMachine.handle_runtime_error
1319
+ # end
1320
+ # elsif c = @acceptors.delete( conn_binding )
1321
+ # # no-op
1322
+ # else
1323
+ # raise ConnectionNotBound
1324
+ # end
1325
+ # elsif opcode == ConnectionAccepted
1326
+ # accep,args,blk = @acceptors[conn_binding]
1327
+ # raise NoHandlerForAcceptedConnection unless accep
1328
+ # c = accep.new data, *args
1329
+ # @conns[data] = c
1330
+ # begin
1331
+ # blk and blk.call(c)
1332
+ # rescue
1333
+ # EventMachine.handle_runtime_error
1334
+ # end
1335
+ # c # (needed?)
1336
+ # elsif opcode == TimerFired
1337
+ # t = @timers.delete( data ) or raise UnknownTimerFired
1338
+ # begin
1339
+ # t.call
1340
+ # rescue
1341
+ # EventMachine.handle_runtime_error
1342
+ # end
1343
+ # elsif opcode == ConnectionCompleted
1344
+ # c = @conns[conn_binding] or raise ConnectionNotBound
1345
+ # begin
1346
+ # c.connection_completed
1347
+ # rescue
1348
+ # EventMachine.handle_runtime_error
1349
+ # end
1350
+ # elsif opcode == LoopbreakSignalled
1351
+ # begin
1352
+ # run_deferred_callbacks
1353
+ # rescue
1354
+ # EventMachine.handle_runtime_error
1355
+ # end
1356
+ # end
1357
+ # end
1358
+ #
1359
+ #
1360
+ # # Default handler for RuntimeErrors that are raised in user code.
1361
+ # # The default behavior is to re-raise the error, which ends your program.
1362
+ # # To override the default behavior, re-implement this method in your code.
1363
+ # # For example:
1364
+ # #
1365
+ # # module EventMachine
1366
+ # # def self.handle_runtime_error
1367
+ # # $>.puts $!
1368
+ # # end
1369
+ # # end
1370
+ # #
1371
+ # #--
1372
+ # # We need to ensure that any code path which invokes user code rescues RuntimeError
1373
+ # # and calls this method. The obvious place to do that is in #event_callback,
1374
+ # # but, scurrilously, it turns out that we need to be finer grained that that.
1375
+ # # Periodic timers, in particular, wrap their invocations of user code inside
1376
+ # # procs that do other stuff we can't not do, like schedule the next invocation.
1377
+ # # This is a potential non-robustness, since we need to remember to hook in the
1378
+ # # error handler whenever and wherever we change how user code is invoked.
1379
+ # #
1380
+ # def EventMachine::handle_runtime_error
1381
+ # @runtime_error_hook ? @runtime_error_hook.call : raise
1382
+ # end
1383
+ #
1384
+ # # Sets a handler for RuntimeErrors that are raised in user code.
1385
+ # # Pass a block with no parameters. You can also call this method without a block,
1386
+ # # which restores the default behavior (see #handle_runtime_error).
1387
+ # #
1388
+ # def EventMachine::set_runtime_error_hook &blk
1389
+ # @runtime_error_hook = blk
1390
+ # end
1312
1391
 
1313
1392
  # Documentation stub
1314
1393
  #--
@@ -1317,6 +1396,7 @@ module EventMachine
1317
1396
  class << self
1318
1397
  def _open_file_for_writing filename, handler=nil
1319
1398
  klass = if (handler and handler.is_a?(Class))
1399
+ raise ArgumentError, 'must provide module or subclass of EventMachine::Connection' unless Connection > handler
1320
1400
  handler
1321
1401
  else
1322
1402
  Class.new( Connection ) {handler and include handler}
@@ -1426,6 +1506,16 @@ class Connection
1426
1506
  puts "............>>>#{data.length}"
1427
1507
  end
1428
1508
 
1509
+ # #ssl_handshake_completed is called by EventMachine when the SSL/TLS handshake has
1510
+ # been completed, as a result of calling #start_tls to initiate SSL/TLS on the connection.
1511
+ #
1512
+ # This callback exists because #post_init and #connection_completed are <b>not</b> reliable
1513
+ # for indicating when an SSL/TLS connection is ready to have it's certificate queried for.
1514
+ #
1515
+ # See #get_peer_cert for application and example.
1516
+ def ssl_handshake_completed
1517
+ end
1518
+
1429
1519
  # EventMachine::Connection#unbind is called by the framework whenever a connection
1430
1520
  # (either a server or client connection) is closed. The close can occur because
1431
1521
  # your code intentionally closes it (see close_connection and close_connection_after_writing),
@@ -1572,6 +1662,73 @@ class Connection
1572
1662
  EventMachine::start_tls @signature
1573
1663
  end
1574
1664
 
1665
+ # If SSL/TLS is active on the connection, #get_peer_cert returns the remote X509 certificate
1666
+ # as a String, in the popular PEM format. This can then be used for arbitrary validation
1667
+ # of a peer's certificate in your code.
1668
+ #
1669
+ # This should be called in/after the #ssl_handshake_completed callback, which indicates
1670
+ # that SSL/TLS is active. Using this callback is important, because the certificate may not
1671
+ # be available until the time it is executed. Using #post_init or #connection_completed is
1672
+ # not adequate, because the SSL handshake may still be taking place.
1673
+ #
1674
+ # #get_peer_cert will return <b>nil</b> if:
1675
+ #
1676
+ # * EventMachine is not built with OpenSSL support
1677
+ # * SSL/TLS is not active on the connection
1678
+ # * SSL/TLS handshake is not yet complete
1679
+ # * Remote peer for any other reason has not presented a certificate
1680
+ #
1681
+ # === Example:
1682
+ #
1683
+ # module Handler
1684
+ #
1685
+ # def post_init
1686
+ # puts "Starting TLS"
1687
+ # start_tls
1688
+ # end
1689
+ #
1690
+ # def ssl_handshake_completed
1691
+ # puts get_peer_cert
1692
+ # close_connection
1693
+ # end
1694
+ #
1695
+ # def unbind
1696
+ # EventMachine::stop_event_loop
1697
+ # end
1698
+ #
1699
+ # end
1700
+ #
1701
+ # EM.run {
1702
+ # EventMachine::connect "mail.google.com", 443, Handler
1703
+ # }
1704
+ #
1705
+ # Output:
1706
+ # -----BEGIN CERTIFICATE-----
1707
+ # MIIDIjCCAougAwIBAgIQbldpChBPqv+BdPg4iwgN8TANBgkqhkiG9w0BAQUFADBM
1708
+ # MQswCQYDVQQGEwJaQTElMCMGA1UEChMcVGhhd3RlIENvbnN1bHRpbmcgKFB0eSkg
1709
+ # THRkLjEWMBQGA1UEAxMNVGhhd3RlIFNHQyBDQTAeFw0wODA1MDIxNjMyNTRaFw0w
1710
+ # OTA1MDIxNjMyNTRaMGkxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlh
1711
+ # MRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRMwEQYDVQQKEwpHb29nbGUgSW5jMRgw
1712
+ # FgYDVQQDEw9tYWlsLmdvb2dsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJ
1713
+ # AoGBALlkxdh2QXegdElukCSOV2+8PKiONIS+8Tu9K7MQsYpqtLNC860zwOPQ2NLI
1714
+ # 3Zp4jwuXVTrtzGuiqf5Jioh35Ig3CqDXtLyZoypjZUQcq4mlLzHlhIQ4EhSjDmA7
1715
+ # Ffw9y3ckSOQgdBQWNLbquHh9AbEUjmhkrYxIqKXeCnRKhv6nAgMBAAGjgecwgeQw
1716
+ # KAYDVR0lBCEwHwYIKwYBBQUHAwEGCCsGAQUFBwMCBglghkgBhvhCBAEwNgYDVR0f
1717
+ # BC8wLTAroCmgJ4YlaHR0cDovL2NybC50aGF3dGUuY29tL1RoYXd0ZVNHQ0NBLmNy
1718
+ # bDByBggrBgEFBQcBAQRmMGQwIgYIKwYBBQUHMAGGFmh0dHA6Ly9vY3NwLnRoYXd0
1719
+ # ZS5jb20wPgYIKwYBBQUHMAKGMmh0dHA6Ly93d3cudGhhd3RlLmNvbS9yZXBvc2l0
1720
+ # b3J5L1RoYXd0ZV9TR0NfQ0EuY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQEF
1721
+ # BQADgYEAsRwpLg1dgCR1gYDK185MFGukXMeQFUvhGqF8eT/CjpdvezyKVuz84gSu
1722
+ # 6ccMXgcPQZGQN/F4Xug+Q01eccJjRSVfdvR5qwpqCj+6BFl5oiKDBsveSkrmL5dz
1723
+ # s2bn7TdTSYKcLeBkjXxDLHGBqLJ6TNCJ3c4/cbbG5JhGvoema94=
1724
+ # -----END CERTIFICATE-----
1725
+ #
1726
+ # You can do whatever you want with the certificate String, such as load it
1727
+ # as a certificate object using the OpenSSL library, and check it's fields.
1728
+ def get_peer_cert
1729
+ EventMachine::get_peer_cert @signature
1730
+ end
1731
+
1575
1732
 
1576
1733
  # send_datagram is for sending UDP messages.
1577
1734
  # This method may be called from any Connection object that refers
@@ -1755,6 +1912,7 @@ module Protocols
1755
1912
  autoload :SmtpClient, 'protocols/smtpclient'
1756
1913
  autoload :SmtpServer, 'protocols/smtpserver'
1757
1914
  autoload :SASLauth, 'protocols/saslauth'
1915
+ autoload :Memcache, 'protocols/memcache'
1758
1916
 
1759
1917
  #require 'protocols/postgres' UNCOMMENT THIS LINE WHEN THE POSTGRES CODE IS READY FOR PRIME TIME.
1760
1918
  end