eventmachine-eventmachine 0.12.5 → 0.12.6

Sign up to get free protection for your applications and to get access to all the features.
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
data/ext/ssl.cpp CHANGED
@@ -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;
data/java/.classpath ADDED
@@ -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>
data/java/.project ADDED
@@ -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>
data/lib/em/processes.rb CHANGED
@@ -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
 
data/lib/eventmachine.rb CHANGED
@@ -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