From dae0dd67556b7bd105d56ccc9a5acb5d7d5e492b Mon Sep 17 00:00:00 2001 From: Gustav Munkby Date: Mon, 2 Apr 2012 19:44:19 +0200 Subject: [PATCH 001/343] Reimplement Queue to avoid shift/push performance problem --- lib/em/queue.rb | 23 ++++++++++++++++------- tests/test_queue.rb | 14 ++++++++++++++ 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/lib/em/queue.rb b/lib/em/queue.rb index 3d9a4d2a2..9096ed029 100644 --- a/lib/em/queue.rb +++ b/lib/em/queue.rb @@ -17,7 +17,8 @@ module EventMachine # class Queue def initialize - @items = [] + @sink = [] + @drain = [] @popq = [] end @@ -29,10 +30,14 @@ def initialize def pop(*a, &b) cb = EM::Callback(*a, &b) EM.schedule do - if @items.empty? + if @drain.empty? + @drain = @sink + @sink = [] + end + if @drain.empty? @popq << cb else - cb.call @items.shift + cb.call @drain.shift end end nil # Always returns nil @@ -43,8 +48,12 @@ def pop(*a, &b) # next reactor tick. def push(*items) EM.schedule do - @items.push(*items) - @popq.shift.call @items.shift until @items.empty? || @popq.empty? + @sink.push(*items) + unless @popq.empty? + @drain = @sink + @sink = [] + @popq.shift.call @drain.shift until @drain.empty? || @popq.empty? + end end end alias :<< :push @@ -52,13 +61,13 @@ def push(*items) # @return [Boolean] # @note This is a peek, it's not thread safe, and may only tend toward accuracy. def empty? - @items.empty? + @drain.empty? && @sink.empty? end # @return [Integer] Queue size # @note This is a peek, it's not thread safe, and may only tend toward accuracy. def size - @items.size + @drain.size + @sink.size end # @return [Integer] Waiting size diff --git a/tests/test_queue.rb b/tests/test_queue.rb index 0acc58a41..34278c05b 100644 --- a/tests/test_queue.rb +++ b/tests/test_queue.rb @@ -47,4 +47,18 @@ def test_num_waiting EM.run { EM.next_tick { EM.stop } } assert_equal many, q.num_waiting end + + def test_big_queue + EM.run do + q = EM::Queue.new + 2000.times do |i| + q.push(*0..1000) + q.pop { |v| assert_equal v, i % 1001 } + end + q.pop do + assert_equal 1_999_999, q.size + EM.stop + end + end + end end From 43d0ae5d0ca8b4ab55dc101ea2cb59466ebea733 Mon Sep 17 00:00:00 2001 From: Jochem Siegel Date: Thu, 3 May 2012 15:04:07 +0200 Subject: [PATCH 002/343] Fix to prevent eventmachine from stopping when a raise is done in an unbind. Eventmachine IS stopped when an exception is raised while eventmachine is stopping. --- .gitignore | 1 + ext/cmain.cpp | 9 +++++++++ ext/em.cpp | 5 ++++- ext/em.h | 1 + ext/eventmachine.h | 1 + ext/rubymain.cpp | 18 ++++++++++++++++++ lib/em/pure_ruby.rb | 7 ++++++- lib/eventmachine.rb | 10 +++++++--- tests/test_basic.rb | 17 +++++++++++++++-- 9 files changed, 62 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index d741ebc92..6e725787b 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ pkg rdoc Makefile +*.idea *.bundle *.dll *.so diff --git a/ext/cmain.cpp b/ext/cmain.cpp index b354d2d97..f2169ddc4 100644 --- a/ext/cmain.cpp +++ b/ext/cmain.cpp @@ -415,6 +415,15 @@ extern "C" void evma_stop_machine() EventMachine->ScheduleHalt(); } +/***************** +evma_stopping +*****************/ + +extern "C" bool evma_stopping() +{ + ensure_eventmachine("evma_stopping"); + return EventMachine->Stopping(); +} /************** evma_start_tls diff --git a/ext/em.cpp b/ext/em.cpp index cc1312f0b..78ccf2c9e 100644 --- a/ext/em.cpp +++ b/ext/em.cpp @@ -189,7 +189,10 @@ void EventMachine_t::ScheduleHalt() bTerminateSignalReceived = true; } - +bool EventMachine_t::Stopping() +{ + return bTerminateSignalReceived; +} /******************************* EventMachine_t::SetTimerQuantum diff --git a/ext/em.h b/ext/em.h index 47d30e2aa..cab9b7240 100644 --- a/ext/em.h +++ b/ext/em.h @@ -76,6 +76,7 @@ class EventMachine_t void Run(); void ScheduleHalt(); + bool Stopping(); void SignalLoopBreaker(); const unsigned long InstallOneshotTimer (int); const unsigned long ConnectToServer (const char *, int, const char *, int); diff --git a/ext/eventmachine.h b/ext/eventmachine.h index 2dd8b1bb1..2c151f6a8 100644 --- a/ext/eventmachine.h +++ b/ext/eventmachine.h @@ -97,6 +97,7 @@ extern "C" { void evma_set_max_timer_count (int); void evma_setuid_string (const char *username); void evma_stop_machine(); + bool evma_stopping(); float evma_get_heartbeat_interval(); int evma_set_heartbeat_interval(float); diff --git a/ext/rubymain.cpp b/ext/rubymain.cpp index 45d4340fd..68ee0c693 100644 --- a/ext/rubymain.cpp +++ b/ext/rubymain.cpp @@ -978,6 +978,23 @@ static VALUE t__ssl_p (VALUE self) #endif } +/******** +t_stopping +********/ + +static VALUE t_stopping () +{ + if (evma_stopping()) + { + return Qtrue; + } + else + { + return Qfalse; + } + +} + /**************** t_send_file_data @@ -1277,6 +1294,7 @@ extern "C" void Init_rubyeventmachine() rb_define_module_function (EmModule, "kqueue?", (VALUE(*)(...))t__kqueue_p, 0); rb_define_module_function (EmModule, "ssl?", (VALUE(*)(...))t__ssl_p, 0); + rb_define_module_function(EmModule, "stopping?",(VALUE(*)(...))t_stopping, 0); rb_define_method (EmConnection, "get_outbound_data_size", (VALUE(*)(...))conn_get_outbound_data_size, 0); rb_define_method (EmConnection, "associate_callback_target", (VALUE(*)(...))conn_associate_callback_target, 1); diff --git a/lib/em/pure_ruby.rb b/lib/em/pure_ruby.rb index b81afb633..0010068d4 100644 --- a/lib/em/pure_ruby.rb +++ b/lib/em/pure_ruby.rb @@ -66,6 +66,11 @@ def run_machine def release_machine end + + def stopping? + return Reactor.instance.stop_scheduled + end + # @private def stop Reactor.instance.stop @@ -273,7 +278,7 @@ class Reactor HeartbeatInterval = 2 - attr_reader :current_loop_time + attr_reader :current_loop_time, :stop_scheduled def initialize initialize_for_run diff --git a/lib/eventmachine.rb b/lib/eventmachine.rb index b9224abd7..86a034459 100644 --- a/lib/eventmachine.rb +++ b/lib/eventmachine.rb @@ -1441,9 +1441,13 @@ def self.event_callback conn_binding, opcode, data rescue Errno::EBADF, IOError end end - rescue - @wrapped_exception = $! - stop + rescue Exception => e + if stopping? + @wrapped_exception = $! + stop + else + raise e + end end elsif c = @acceptors.delete( conn_binding ) # no-op diff --git a/tests/test_basic.rb b/tests/test_basic.rb index d52d4d05a..d89c6eeeb 100644 --- a/tests/test_basic.rb +++ b/tests/test_basic.rb @@ -93,15 +93,28 @@ def unbind end end - def test_unbind_error + def test_unbind_error_during_stop assert_raises( UnbindError::ERR ) { EM.run { EM.start_server "127.0.0.1", @port - EM.connect "127.0.0.1", @port, UnbindError + EM.connect "127.0.0.1", @port, UnbindError do + EM.stop + end } } end + def test_unbind_error + EM.run { + EM.error_handler do |e| + assert(e.is_a?(UnbindError::ERR)) + EM.stop + end + EM.start_server "127.0.0.1", @port + EM.connect "127.0.0.1", @port, UnbindError + } + end + module BrsTestSrv def receive_data data $received << data From 6f356935363712fd14c552d57c88002afc74d85d Mon Sep 17 00:00:00 2001 From: Joshua Rutherford Date: Wed, 8 Jan 2014 10:24:58 -0800 Subject: [PATCH 003/343] Handle deferred exceptions in reactor thread --- lib/eventmachine.rb | 39 ++++++++++++++++++++++++++++----------- tests/test_defer.rb | 17 +++++++++++++++++ 2 files changed, 45 insertions(+), 11 deletions(-) diff --git a/lib/eventmachine.rb b/lib/eventmachine.rb index 3d46fa708..60ad90215 100644 --- a/lib/eventmachine.rb +++ b/lib/eventmachine.rb @@ -981,11 +981,14 @@ def self.run_deferred_callbacks # EventMachine.defer is used for integrating blocking operations into EventMachine's control flow. # The action of {.defer} is to take the block specified in the first parameter (the "operation") # and schedule it for asynchronous execution on an internal thread pool maintained by EventMachine. - # When the operation completes, it will pass the result computed by the block (if any) - # back to the EventMachine reactor. Then, EventMachine calls the block specified in the - # second parameter to {.defer} (the "callback"), as part of its normal event handling loop. - # The result computed by the operation block is passed as a parameter to the callback. - # You may omit the callback parameter if you don't need to execute any code after the operation completes. + # When the operation completes, it will pass the result computed by the block (if any) back to the + # EventMachine reactor. Then, EventMachine calls the block specified in the second parameter to + # {.defer} (the "callback"), as part of its normal event handling loop. The result computed by the + # operation block is passed as a parameter to the callback. You may omit the callback parameter if + # you don't need to execute any code after the operation completes. If the operation raises an + # unhandled exception, the exception will be passed to the third parameter to {.defer} (the + # "errback"), as part of its normal event handling loop. If no errback is provided, the exception + # will be allowed to blow through to the main thread immediately. # # ## Caveats ## # @@ -999,6 +1002,11 @@ def self.run_deferred_callbacks # the number of threads in its pool, so if you do this enough times, your subsequent deferred # operations won't get a chance to run. # + # The threads within the EventMachine's thread pool have abort_on_exception set to true. As a result, + # if an unhandled exception is raised by the deferred operation and an errback is not provided, it + # will blow through to the main thread immediately. If the main thread is within an indiscriminate + # rescue block at that time, the exception could be handled improperly by the main thread. + # # @example # # operation = proc { @@ -1008,14 +1016,18 @@ def self.run_deferred_callbacks # callback = proc {|result| # # do something with result here, such as send it back to a network client. # } + # errback = proc {|error| + # # do something with error here, such as re-raising or logging. + # } # - # EventMachine.defer(operation, callback) + # EventMachine.defer(operation, callback, errback) # # @param [#call] op An operation you want to offload to EventMachine thread pool # @param [#call] callback A callback that will be run on the event loop thread after `operation` finishes. + # @param [#call] errback An errback that will be run on the event loop thread after `operation` raises an exception. # # @see EventMachine.threadpool_size - def self.defer op = nil, callback = nil, &blk + def self.defer op = nil, callback = nil, errback = nil, &blk # OBSERVE that #next_tick hacks into this mechanism, so don't make any changes here # without syncing there. # @@ -1032,7 +1044,7 @@ def self.defer op = nil, callback = nil, &blk spawn_threadpool end - @threadqueue << [op||blk,callback] + @threadqueue << [op||blk,callback,errback] end @@ -1043,13 +1055,18 @@ def self.spawn_threadpool Thread.current.abort_on_exception = true while true begin - op, cback = *@threadqueue.pop + op, cback, eback = *@threadqueue.pop rescue ThreadError $stderr.puts $!.message break # Ruby 2.0 may fail at Queue.pop end - result = op.call - @resultqueue << [result, cback] + begin + result = op.call + @resultqueue << [result, cback] + rescue Exception => error + raise error unless eback + @resultqueue << [error, eback] + end EventMachine.signal_loopbreak end end diff --git a/tests/test_defer.rb b/tests/test_defer.rb index c59c1fa3d..aeca127d8 100644 --- a/tests/test_defer.rb +++ b/tests/test_defer.rb @@ -15,4 +15,21 @@ def test_defers assert_equal( n, n_times ) end + def test_errbacks + iterations = 20 + callback_parameter = rand(100) + callback_parameters = [] + callback_op = proc { callback_parameter } + callback = proc { |result| callback_parameters << result } + errback_parameter = Exception.new + errback_parameters = [] + errback_op = proc { raise errback_parameter } + errback = proc { |error| errback_parameters << error } + EventMachine.run do + (1..iterations).each { |index| EventMachine.defer(index.even? ? callback_op : errback_op, callback, errback) } + EventMachine.add_periodic_timer(0.1) { EventMachine.stop if EventMachine.defers_finished? } + end + assert_equal(callback_parameters.select { |parameter| parameter == callback_parameter }.length, iterations * 0.5) + assert_equal(errback_parameters.select{ |parameter| parameter == errback_parameter }.length, iterations * 0.5) + end end From 459ae6e15f6c0a6cfe2bc6bb9b0599d19bbd431c Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Fri, 7 Aug 2015 09:15:05 -0700 Subject: [PATCH 004/343] Detect the Solaris Studio compiler --- ext/ed.cpp | 2 ++ ext/extconf.rb | 6 +++++- ext/fastfilereader/extconf.rb | 6 +++++- ext/fastfilereader/mapper.cpp | 2 ++ tests/em_test_helper.rb | 4 ++++ tests/test_file_watch.rb | 1 + 6 files changed, 19 insertions(+), 2 deletions(-) diff --git a/ext/ed.cpp b/ext/ed.cpp index 4c7276bd4..5fa191579 100644 --- a/ext/ed.cpp +++ b/ext/ed.cpp @@ -1024,6 +1024,8 @@ void ConnectionDescriptor::_WriteOutboundData() for(int i = 0; i < iovcnt; i++){ OutboundPage *op = &(OutboundPages[i]); #ifdef CC_SUNWspro + // TODO: The void * cast works fine on Solaris 11, but + // I don't know at what point that changed from older Solaris. iov[i].iov_base = (char *)(op->Buffer + op->Offset); #else iov[i].iov_base = (void *)(op->Buffer + op->Offset); diff --git a/ext/extconf.rb b/ext/extconf.rb index 61938cfc5..eac3eea57 100644 --- a/ext/extconf.rb +++ b/ext/extconf.rb @@ -125,13 +125,17 @@ def manual_ssl_config add_define 'OS_SOLARIS8' check_libs(%w[nsl socket], true) - if CONFIG['CC'] == 'cc' and `cc -flags 2>&1` =~ /Sun/ # detect SUNWspro compiler + if CONFIG['CC'] == 'cc' && ( + `cc -flags 2>&1` =~ /Sun/ || # detect SUNWspro compiler + `cc -V 2>&1` =~ /Sun/ # detect Solaris Studio compiler + ) # SUN CHAIN add_define 'CC_SUNWspro' $preload = ["\nCXX = CC"] # hack a CXX= line into the makefile $CFLAGS = CONFIG['CFLAGS'] = "-KPIC" CONFIG['CCDLFLAGS'] = "-KPIC" CONFIG['LDSHARED'] = "$(CXX) -G -KPIC -lCstd" + CONFIG['LDSHAREDXX'] = "$(CXX) -G -KPIC -lCstd" else # GNU CHAIN # on Unix we need a g++ link, not gcc. diff --git a/ext/fastfilereader/extconf.rb b/ext/fastfilereader/extconf.rb index c6d163f08..d6a824ea1 100644 --- a/ext/fastfilereader/extconf.rb +++ b/ext/fastfilereader/extconf.rb @@ -56,13 +56,17 @@ def add_define(name) add_define 'OS_SOLARIS8' check_libs(%w[nsl socket], true) - if CONFIG['CC'] == 'cc' and `cc -flags 2>&1` =~ /Sun/ # detect SUNWspro compiler + if CONFIG['CC'] == 'cc' && ( + `cc -flags 2>&1` =~ /Sun/ || # detect SUNWspro compiler + `cc -V 2>&1` =~ /Sun/ # detect Solaris Studio compiler + ) # SUN CHAIN add_define 'CC_SUNWspro' $preload = ["\nCXX = CC"] # hack a CXX= line into the makefile $CFLAGS = CONFIG['CFLAGS'] = "-KPIC" CONFIG['CCDLFLAGS'] = "-KPIC" CONFIG['LDSHARED'] = "$(CXX) -G -KPIC -lCstd" + CONFIG['LDSHAREDXX'] = "$(CXX) -G -KPIC -lCstd" else # GNU CHAIN # on Unix we need a g++ link, not gcc. diff --git a/ext/fastfilereader/mapper.cpp b/ext/fastfilereader/mapper.cpp index 949103ddb..4c1386577 100644 --- a/ext/fastfilereader/mapper.cpp +++ b/ext/fastfilereader/mapper.cpp @@ -89,6 +89,8 @@ void Mapper_t::Close() // Calls to GetChunk are invalid after a call to Close. if (MapPoint) { #ifdef CC_SUNWspro + // TODO: The void * cast works fine on Solaris 11, but + // I don't know at what point that changed from older Solaris. munmap ((char*)MapPoint, FileSize); #else munmap ((void*)MapPoint, FileSize); diff --git a/tests/em_test_helper.rb b/tests/em_test_helper.rb index 94121f1e1..b68f347a7 100644 --- a/tests/em_test_helper.rb +++ b/tests/em_test_helper.rb @@ -41,6 +41,10 @@ def windows? RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ end + def solaris? + RUBY_PLATFORM =~ /solaris/ + end + # http://stackoverflow.com/questions/1342535/how-can-i-tell-if-im-running-from-jruby-vs-ruby/1685970#1685970 def jruby? defined? JRUBY_VERSION diff --git a/tests/test_file_watch.rb b/tests/test_file_watch.rb index d50b80e23..fab8ecb07 100644 --- a/tests/test_file_watch.rb +++ b/tests/test_file_watch.rb @@ -34,6 +34,7 @@ def teardown end def test_events + omit_if(solaris?) EM.run{ file = Tempfile.new('em-watch') $tmp_path = file.path From 5f0e15154dbc3d569ae033235b1cf130bb3bb511 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Sun, 9 Aug 2015 17:37:11 -0700 Subject: [PATCH 005/343] Use select_large_fdset on Solaris --- ext/em.h | 13 +++++++++++++ ext/extconf.rb | 4 ++++ 2 files changed, 17 insertions(+) diff --git a/ext/em.h b/ext/em.h index 9bbddd5b6..fab992762 100644 --- a/ext/em.h +++ b/ext/em.h @@ -93,6 +93,19 @@ typedef fd_set rb_fdset_t; rb_thread_select(fd_check((n)-1) ? (n) : FD_SETSIZE, (rfds), (wfds), (efds), (timeout)) #endif + +// This Solaris fix is adapted from eval_intern.h in Ruby 1.9.3: +// Solaris sys/select.h switches select to select_large_fdset to support larger +// file descriptors if FD_SETSIZE is larger than 1024 on 32bit environment. +// But Ruby doesn't change FD_SETSIZE because fd_set is allocated dynamically. +// So following definition is required to use select_large_fdset. +#ifdef HAVE_SELECT_LARGE_FDSET +#define select(n, r, w, e, t) select_large_fdset((n), (r), (w), (e), (t)) +extern "C" { + int select_large_fdset(int, fd_set *, fd_set *, fd_set *, struct timeval *); +} +#endif + class EventableDescriptor; class InotifyDescriptor; struct SelectData_t; diff --git a/ext/extconf.rb b/ext/extconf.rb index eac3eea57..d3c9cfdce 100644 --- a/ext/extconf.rb +++ b/ext/extconf.rb @@ -125,6 +125,10 @@ def manual_ssl_config add_define 'OS_SOLARIS8' check_libs(%w[nsl socket], true) + # If Ruby was compiled for 32-bits, then select() can only handle 1024 fds + # There is an alternate function, select_large_fdset, that supports more. + add_define 'HAVE_SELECT_LARGE_FDSET' if have_func('select_large_fdset', 'sys/select.h') + if CONFIG['CC'] == 'cc' && ( `cc -flags 2>&1` =~ /Sun/ || # detect SUNWspro compiler `cc -V 2>&1` =~ /Sun/ # detect Solaris Studio compiler From 6ad97044d0ae4fcd4686b758a48ca2909341c315 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Tue, 3 Feb 2015 16:48:09 -0800 Subject: [PATCH 006/343] Add AppVeyor configuration for Windows CI testing --- .travis.yml | 1 + appveyor.yml | 31 +++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 appveyor.yml diff --git a/.travis.yml b/.travis.yml index bfe22175a..57954719e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,4 @@ +bundler_args: --without documentation script: bundle exec rake compile test env: global: diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 000000000..11d1c22cd --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,31 @@ +--- +version: "{build}" +clone_depth: 10 +install: + - SET PATH=C:\Ruby%ruby_version%\bin;%PATH% + - ruby --version + - gem --version + - gem install bundler --quiet --no-ri --no-rdoc + - bundler --version + - bundle install --without documentation +build_script: + - bundle exec rake compile +test_script: + - bundle exec rake test +environment: + TESTOPTS: -v + matrix: + - ruby_version: "200" + - ruby_version: "200-x64" + - ruby_version: "21" + - ruby_version: "21-x64" +cache: + - vendor + - C:\Ruby200\lib\ruby\gems\2.0.0 + - C:\Ruby200\bin + - C:\Ruby200-x64\lib\ruby\gems\2.0.0 + - C:\Ruby200-x64\bin + - C:\Ruby21\lib\ruby\gems\2.1.0 + - C:\Ruby21\bin + - C:\Ruby21-x64\lib\ruby\gems\2.1.0 + - C:\Ruby21-x64\bin From 9c478d1174408d6decf57e5f77a5e79c1c8d32eb Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Tue, 3 Feb 2015 17:07:15 -0800 Subject: [PATCH 007/343] Move yard and bluecloth to Gemfile instead of development dependency --- Gemfile | 6 ++++++ eventmachine.gemspec | 2 -- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Gemfile b/Gemfile index 851fabc21..0d9a37623 100644 --- a/Gemfile +++ b/Gemfile @@ -1,2 +1,8 @@ source 'https://rubygems.org' + gemspec + +group :documentation do + gem 'yard', '>= 0.8.5.2' + gem 'bluecloth' unless RUBY_PLATFORM =~ /java|mswin|mingw/ +end diff --git a/eventmachine.gemspec b/eventmachine.gemspec index 300937782..96c7f613d 100644 --- a/eventmachine.gemspec +++ b/eventmachine.gemspec @@ -18,8 +18,6 @@ Gem::Specification.new do |s| s.add_development_dependency 'test-unit', '~> 2.0' s.add_development_dependency 'rake-compiler', '~> 0.8.3' - s.add_development_dependency 'yard', ">= 0.8.5.2" - s.add_development_dependency 'bluecloth' unless RUBY_PLATFORM =~ /java/ s.summary = 'Ruby/EventMachine library' s.description = "EventMachine implements a fast, single-threaded engine for arbitrary network From 9c6d1e0c9391fa0542c6d0490341871161d79924 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Tue, 3 Feb 2015 17:17:04 -0800 Subject: [PATCH 008/343] Add task :devkit to compile on Windows --- rakelib/package.rake | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/rakelib/package.rake b/rakelib/package.rake index f12ee2b5d..9e7d5d0a9 100644 --- a/rakelib/package.rake +++ b/rakelib/package.rake @@ -96,3 +96,16 @@ def gem_cmd(action, name, *args) end Rake::Task[:clean].enhance [:clobber_package] + +# DevKit task following the example of Luis Lavena's test-ruby-c-extension +task :devkit do + begin + require "devkit" + rescue LoadError => e + abort "Failed to activate RubyInstaller's DevKit required for compilation." + end +end + +if RUBY_PLATFORM =~ /mingw|mswin/ then + Rake::Task['compile'].prerequisites.unshift 'devkit' +end From 52cccc114534cbf6f741b962508ac5ed66678f48 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Tue, 3 Feb 2015 17:17:56 -0800 Subject: [PATCH 009/343] Bump rake-compiler dependency to 0.9.5 or higher --- eventmachine.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eventmachine.gemspec b/eventmachine.gemspec index 96c7f613d..3c0edf77b 100644 --- a/eventmachine.gemspec +++ b/eventmachine.gemspec @@ -17,7 +17,7 @@ Gem::Specification.new do |s| s.extensions = ["ext/extconf.rb", "ext/fastfilereader/extconf.rb"] s.add_development_dependency 'test-unit', '~> 2.0' - s.add_development_dependency 'rake-compiler', '~> 0.8.3' + s.add_development_dependency 'rake-compiler', '~> 0.9.5' s.summary = 'Ruby/EventMachine library' s.description = "EventMachine implements a fast, single-threaded engine for arbitrary network From df5e00cd32dbbf5d3568ac912439e90198b571fc Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Wed, 29 Jul 2015 16:49:00 -0700 Subject: [PATCH 010/343] Add Ruby 2.2 to the AppVeyor matrix, but allowed to fail for now --- appveyor.yml | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 11d1c22cd..d84b5fe93 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -7,7 +7,7 @@ install: - gem --version - gem install bundler --quiet --no-ri --no-rdoc - bundler --version - - bundle install --without documentation + - bundle install --without documentation --path vendor/bundle build_script: - bundle exec rake compile test_script: @@ -19,13 +19,11 @@ environment: - ruby_version: "200-x64" - ruby_version: "21" - ruby_version: "21-x64" + - ruby_version: "22" + - ruby_version: "22-x64" +matrix: + allow_failures: + - ruby_version: "22" + - ruby_version: "22-x64" cache: - vendor - - C:\Ruby200\lib\ruby\gems\2.0.0 - - C:\Ruby200\bin - - C:\Ruby200-x64\lib\ruby\gems\2.0.0 - - C:\Ruby200-x64\bin - - C:\Ruby21\lib\ruby\gems\2.1.0 - - C:\Ruby21\bin - - C:\Ruby21-x64\lib\ruby\gems\2.1.0 - - C:\Ruby21-x64\bin From 5e0bda52ef240aa8d505f0ac4d59d759e6adba64 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Thu, 30 Jul 2015 19:03:37 -0700 Subject: [PATCH 011/343] Various test and Windows fixes --- ext/rubymain.cpp | 11 +++++------ tests/test_basic.rb | 1 + tests/test_connection_write.rb | 4 ++-- tests/test_epoll.rb | 2 +- tests/test_iterator.rb | 2 ++ tests/test_many_fds.rb | 2 +- tests/test_set_sock_opt.rb | 2 ++ tests/test_system.rb | 4 ++++ tests/test_unbind_reason.rb | 2 +- 9 files changed, 19 insertions(+), 11 deletions(-) diff --git a/ext/rubymain.cpp b/ext/rubymain.cpp index 9efd6c174..dbdf7fe89 100644 --- a/ext/rubymain.cpp +++ b/ext/rubymain.cpp @@ -865,12 +865,11 @@ t_invoke_popen static VALUE t_invoke_popen (VALUE self UNUSED, VALUE cmd) { - // 1.8.7+ - #ifdef RARRAY_LEN - int len = RARRAY_LEN(cmd); - #else - int len = RARRAY (cmd)->len; + #ifdef OS_WIN32 + rb_raise (EM_eUnsupported, "popen is not available on this platform"); #endif + + int len = RARRAY_LEN(cmd); if (len >= 2048) rb_raise (rb_eRuntimeError, "%s", "too many arguments to popen"); char *strings [2048]; @@ -885,7 +884,7 @@ static VALUE t_invoke_popen (VALUE self UNUSED, VALUE cmd) try { f = evma_popen (strings); } catch (std::runtime_error e) { - f = 0; // raise exception below + rb_raise (rb_eRuntimeError, "%s", e.what()); } if (!f) { char *err = strerror (errno); diff --git a/tests/test_basic.rb b/tests/test_basic.rb index 0202d3c6f..7cf0d6088 100644 --- a/tests/test_basic.rb +++ b/tests/test_basic.rb @@ -246,6 +246,7 @@ def c.unbind def test_fork_safe omit_if(jruby?) + omit_if(windows?) omit_if(rbx?, 'Omitting test on Rubinius because it hangs for unknown reasons') read, write = IO.pipe diff --git a/tests/test_connection_write.rb b/tests/test_connection_write.rb index 75199278d..35533b526 100644 --- a/tests/test_connection_write.rb +++ b/tests/test_connection_write.rb @@ -19,8 +19,8 @@ def notify_writable def test_with_naughty_callback EM.run do - r1, w1 = IO.pipe - r2, w2 = IO.pipe + r1, _ = IO.pipe + r2, _ = IO.pipe # Adding EM.watches $conn1 = EM.watch(r1, SimpleClient) diff --git a/tests/test_epoll.rb b/tests/test_epoll.rb index 47240d608..36f5609ab 100644 --- a/tests/test_epoll.rb +++ b/tests/test_epoll.rb @@ -128,7 +128,7 @@ def test_attach_detach EM.run { EM.add_timer(0.01) { EM.stop } - r, w = IO.pipe + r, _ = IO.pipe # This tests a regression where detach in the same tick as attach crashes EM EM.watch(r) do |connection| diff --git a/tests/test_iterator.rb b/tests/test_iterator.rb index 70142726e..a040d5585 100644 --- a/tests/test_iterator.rb +++ b/tests/test_iterator.rb @@ -72,6 +72,8 @@ def test_map end def test_inject + omit_if(windows?) + list = %w[ pwd uptime uname date ] EM.run { EM::Iterator.new(list, 2).inject({}, proc{ |hash,cmd,iter| diff --git a/tests/test_many_fds.rb b/tests/test_many_fds.rb index 74dc92665..7c126dcc7 100644 --- a/tests/test_many_fds.rb +++ b/tests/test_many_fds.rb @@ -9,7 +9,7 @@ def setup def test_connection_class_cache mod = Module.new a = nil - Process.setrlimit(Process::RLIMIT_NOFILE,4096); + Process.setrlimit(Process::RLIMIT_NOFILE, 4096) rescue nil EM.run { EM.start_server '127.0.0.1', @port, mod 1100.times do diff --git a/tests/test_set_sock_opt.rb b/tests/test_set_sock_opt.rb index d2308ab96..262819561 100644 --- a/tests/test_set_sock_opt.rb +++ b/tests/test_set_sock_opt.rb @@ -15,6 +15,8 @@ def teardown #------------------------------------- def test_set_sock_opt + omit_if(windows?) + test = self EM.run do EM.connect 'google.com', 80, Module.new { diff --git a/tests/test_system.rb b/tests/test_system.rb index 9de43b0e2..fbbe2c9e8 100644 --- a/tests/test_system.rb +++ b/tests/test_system.rb @@ -9,6 +9,8 @@ def setup end def test_system + omit_if(windows?) + result = nil status = nil EM.run { @@ -23,6 +25,8 @@ def test_system end def test_system_with_string + omit_if(windows?) + result = nil status = nil EM.run { diff --git a/tests/test_unbind_reason.rb b/tests/test_unbind_reason.rb index 72a99f55f..aad144a71 100644 --- a/tests/test_unbind_reason.rb +++ b/tests/test_unbind_reason.rb @@ -20,7 +20,7 @@ def test_connect_timeout EM.stop end } - conn.pending_connect_timeout = 0.1 + conn.pending_connect_timeout = TIMEOUT_INTERVAL } assert_equal Errno::ETIMEDOUT, error end From 3695703cc60c256441dfcc8c457ec9f7d294a767 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Fri, 31 Jul 2015 13:04:38 -0700 Subject: [PATCH 012/343] Resolv on Windows --- lib/em/resolver.rb | 71 +++++++++++++++++++++++++++++++--------------- 1 file changed, 48 insertions(+), 23 deletions(-) diff --git a/lib/em/resolver.rb b/lib/em/resolver.rb index ce8a1f82e..04f0cab47 100644 --- a/lib/em/resolver.rb +++ b/lib/em/resolver.rb @@ -2,30 +2,31 @@ module EventMachine module DNS class Resolver + def self.windows? + if RUBY_PLATFORM =~ /mswin32|cygwin|mingw|bccwin/ + require 'win32/resolv' + true + else + false + end + end + + HOSTS_FILE = windows? ? Win32::Resolv.get_hosts_path : '/etc/hosts' + + @hosts = nil + @nameservers = nil + @socket = nil + def self.resolve(hostname) Request.new(socket, hostname) end - @socket = @nameservers = nil - def self.socket - if !@socket || (@socket && @socket.error?) + if @socket && @socket.error? @socket = Socket.open - - @hosts = {} - IO.readlines('/etc/hosts').each do |line| - next if line =~ /^#/ - addr, host = line.split(/\s+/) - - if @hosts[host] - @hosts[host] << addr - else - @hosts[host] = [addr] - end - end + else + @socket ||= Socket.open end - - @socket end def self.nameservers=(ns) @@ -33,15 +34,23 @@ def self.nameservers=(ns) end def self.nameservers - if !@nameservers - @nameservers = [] - IO.readlines('/etc/resolv.conf').each do |line| - if line =~ /^nameserver (.+)$/ - @nameservers << $1.split(/\s+/).first - end + return @nameservers if @nameservers + + if windows? + _, ns = Win32::Resolv.get_resolv_info + return @nameservers = ns || [] + end + + @nameservers = [] + IO.readlines('/etc/resolv.conf').each do |line| + if line =~ /^nameserver (.+)$/ + @nameservers << $1.split(/\s+/).first end end + @nameservers + rescue + @nameservers = [] end def self.nameserver @@ -49,7 +58,23 @@ def self.nameserver end def self.hosts + return @hosts if @hosts + + @hosts = {} + IO.readlines(HOSTS_FILE).each do |line| + next if line =~ /^#/ + addr, host = line.split(/\s+/) + + if @hosts[host] + @hosts[host] << addr + else + @hosts[host] = [addr] + end + end + @hosts + rescue + @hosts = {} end end From edc33cf49d3fe24838cd72eb0ee4d98d2238b53a Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Fri, 31 Jul 2015 13:37:32 -0700 Subject: [PATCH 013/343] Resolver tests and a fix for empty lines in /etc/hosts parsing as { nil => [ nil ] } --- lib/em/resolver.rb | 8 +++----- tests/test_resolver.rb | 15 +++++++++++++++ 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/lib/em/resolver.rb b/lib/em/resolver.rb index 04f0cab47..1d2d7aa02 100644 --- a/lib/em/resolver.rb +++ b/lib/em/resolver.rb @@ -65,11 +65,9 @@ def self.hosts next if line =~ /^#/ addr, host = line.split(/\s+/) - if @hosts[host] - @hosts[host] << addr - else - @hosts[host] = [addr] - end + next unless addr && host + @hosts[host] ||= [] + @hosts[host] << addr end @hosts diff --git a/tests/test_resolver.rb b/tests/test_resolver.rb index 8a2e14d0d..7ae1d4a82 100644 --- a/tests/test_resolver.rb +++ b/tests/test_resolver.rb @@ -1,6 +1,21 @@ require 'em_test_helper' class TestResolver < Test::Unit::TestCase + def test_nameserver + assert_kind_of(String, EM::DNS::Resolver.nameserver) + end + + def test_nameservers + assert_kind_of(Array, EM::DNS::Resolver.nameservers) + end + + def test_hosts + assert_kind_of(Hash, EM::DNS::Resolver.hosts) + + # Make sure that blank or comment lines are skipped + refute(EM::DNS::Resolver.hosts.include? nil) + end + def test_a EM.run { d = EM::DNS::Resolver.resolve "google.com" From 281aa7ce683e0eca4c94a23b6b9be9783b9708e5 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Fri, 31 Jul 2015 14:49:03 -0700 Subject: [PATCH 014/343] Remove an ancient Visual Studio pragma warning disable --- ext/project.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ext/project.h b/ext/project.h index 6dcaadf7a..d105b67aa 100644 --- a/ext/project.h +++ b/ext/project.h @@ -22,10 +22,6 @@ See the file COPYING for complete licensing information. #define __Project__H_ -#ifdef OS_WIN32 -#pragma warning(disable:4786) -#endif - #include #include #include From 322a64cad72eae5416ca1f979d68909c33d0dc85 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Fri, 31 Jul 2015 15:29:06 -0700 Subject: [PATCH 015/343] Use SOCKET instead of int to resolve a Windows compiler warning --- ext/ed.cpp | 22 +++++++++++----------- ext/ed.h | 16 ++++++++-------- ext/em.cpp | 37 ++++++++++++++++++------------------- ext/em.h | 10 +++++----- 4 files changed, 42 insertions(+), 43 deletions(-) diff --git a/ext/ed.cpp b/ext/ed.cpp index 5fa191579..4ab2ae10f 100644 --- a/ext/ed.cpp +++ b/ext/ed.cpp @@ -66,7 +66,7 @@ bool SetFdCloexec (int fd) EventableDescriptor::EventableDescriptor ****************************************/ -EventableDescriptor::EventableDescriptor (int sd, EventMachine_t *em): +EventableDescriptor::EventableDescriptor (SOCKET sd, EventMachine_t *em): bCloseNow (false), bCloseAfterWriting (false), MySocket (sd), @@ -387,7 +387,7 @@ uint64_t EventableDescriptor::GetNextHeartbeat() ConnectionDescriptor::ConnectionDescriptor ******************************************/ -ConnectionDescriptor::ConnectionDescriptor (int sd, EventMachine_t *em): +ConnectionDescriptor::ConnectionDescriptor (SOCKET sd, EventMachine_t *em): EventableDescriptor (sd, em), bConnectPending (false), bNotifyReadable (false), @@ -768,7 +768,7 @@ void ConnectionDescriptor::Read() * come here more than once after being closed. (FCianfrocca) */ - int sd = GetSocket(); + SOCKET sd = GetSocket(); //assert (sd != INVALID_SOCKET); (original, removed 22Aug06) if (sd == INVALID_SOCKET) { assert (!bReadAttemptedAfterClose); @@ -1003,7 +1003,7 @@ void ConnectionDescriptor::_WriteOutboundData() * doing it to address some reports of crashing under heavy loads. */ - int sd = GetSocket(); + SOCKET sd = GetSocket(); //assert (sd != INVALID_SOCKET); if (sd == INVALID_SOCKET) { assert (!bWriteAttemptedAfterClose); @@ -1346,7 +1346,7 @@ void ConnectionDescriptor::Heartbeat() LoopbreakDescriptor::LoopbreakDescriptor ****************************************/ -LoopbreakDescriptor::LoopbreakDescriptor (int sd, EventMachine_t *parent_em): +LoopbreakDescriptor::LoopbreakDescriptor (SOCKET sd, EventMachine_t *parent_em): EventableDescriptor (sd, parent_em) { /* This is really bad and ugly. Change someday if possible. @@ -1393,7 +1393,7 @@ void LoopbreakDescriptor::Write() AcceptorDescriptor::AcceptorDescriptor **************************************/ -AcceptorDescriptor::AcceptorDescriptor (int sd, EventMachine_t *parent_em): +AcceptorDescriptor::AcceptorDescriptor (SOCKET sd, EventMachine_t *parent_em): EventableDescriptor (sd, parent_em) { #ifdef HAVE_EPOLL @@ -1454,14 +1454,14 @@ void AcceptorDescriptor::Read() for (int i=0; i < accept_count; i++) { #if defined(HAVE_SOCK_CLOEXEC) && defined(HAVE_ACCEPT4) - int sd = accept4 (GetSocket(), (struct sockaddr*)&pin, &addrlen, SOCK_CLOEXEC); + SOCKET sd = accept4 (GetSocket(), (struct sockaddr*)&pin, &addrlen, SOCK_CLOEXEC); if (sd == INVALID_SOCKET) { // We may be running in a kernel where // SOCK_CLOEXEC is not supported - fall back: sd = accept (GetSocket(), (struct sockaddr*)&pin, &addrlen); } #else - int sd = accept (GetSocket(), (struct sockaddr*)&pin, &addrlen); + SOCKET sd = accept (GetSocket(), (struct sockaddr*)&pin, &addrlen); #endif if (sd == INVALID_SOCKET) { // This breaks the loop when we've accepted everything on the kernel queue, @@ -1557,7 +1557,7 @@ bool AcceptorDescriptor::GetSockname (struct sockaddr *s, socklen_t *len) DatagramDescriptor::DatagramDescriptor **************************************/ -DatagramDescriptor::DatagramDescriptor (int sd, EventMachine_t *parent_em): +DatagramDescriptor::DatagramDescriptor (SOCKET sd, EventMachine_t *parent_em): EventableDescriptor (sd, parent_em), OutboundDataSize (0) { @@ -1625,7 +1625,7 @@ DatagramDescriptor::Read void DatagramDescriptor::Read() { - int sd = GetSocket(); + SOCKET sd = GetSocket(); assert (sd != INVALID_SOCKET); LastActivity = MyEventMachine->GetCurrentLoopTime(); @@ -1702,7 +1702,7 @@ void DatagramDescriptor::Write() * TODO, we are currently suppressing the EMSGSIZE error!!! */ - int sd = GetSocket(); + SOCKET sd = GetSocket(); assert (sd != INVALID_SOCKET); LastActivity = MyEventMachine->GetCurrentLoopTime(); diff --git a/ext/ed.h b/ext/ed.h index c2607b145..367a1e8db 100644 --- a/ext/ed.h +++ b/ext/ed.h @@ -36,10 +36,10 @@ class EventableDescriptor class EventableDescriptor: public Bindable_t { public: - EventableDescriptor (int, EventMachine_t*); + EventableDescriptor (SOCKET, EventMachine_t*); virtual ~EventableDescriptor(); - int GetSocket() {return MySocket;} + SOCKET GetSocket() {return MySocket;} void SetSocketInvalid() { MySocket = INVALID_SOCKET; } void Close(); @@ -108,7 +108,7 @@ class EventableDescriptor: public Bindable_t bool bCloseAfterWriting; protected: - int MySocket; + SOCKET MySocket; bool bAttached; bool bWatchOnly; @@ -151,7 +151,7 @@ class LoopbreakDescriptor class LoopbreakDescriptor: public EventableDescriptor { public: - LoopbreakDescriptor (int, EventMachine_t*); + LoopbreakDescriptor (SOCKET, EventMachine_t*); virtual ~LoopbreakDescriptor() {} virtual void Read(); @@ -170,7 +170,7 @@ class ConnectionDescriptor class ConnectionDescriptor: public EventableDescriptor { public: - ConnectionDescriptor (int, EventMachine_t*); + ConnectionDescriptor (SOCKET, EventMachine_t*); virtual ~ConnectionDescriptor(); int SendOutboundData (const char*, unsigned long); @@ -275,7 +275,7 @@ class DatagramDescriptor class DatagramDescriptor: public EventableDescriptor { public: - DatagramDescriptor (int, EventMachine_t*); + DatagramDescriptor (SOCKET, EventMachine_t*); virtual ~DatagramDescriptor(); virtual void Read(); @@ -321,7 +321,7 @@ class AcceptorDescriptor class AcceptorDescriptor: public EventableDescriptor { public: - AcceptorDescriptor (int, EventMachine_t*); + AcceptorDescriptor (SOCKET, EventMachine_t*); virtual ~AcceptorDescriptor(); virtual void Read(); @@ -344,7 +344,7 @@ class PipeDescriptor class PipeDescriptor: public EventableDescriptor { public: - PipeDescriptor (int, pid_t, EventMachine_t*); + PipeDescriptor (SOCKET, pid_t, EventMachine_t*); virtual ~PipeDescriptor(); virtual void Read(); diff --git a/ext/em.cpp b/ext/em.cpp index d572aa476..dcb3f75df 100644 --- a/ext/em.cpp +++ b/ext/em.cpp @@ -41,13 +41,12 @@ static struct sockaddr *name2address (const char *server, int port, int *family, /* Internal helper to create a socket with SOCK_CLOEXEC set, and fall * back to fcntl'ing it if the headers/runtime don't support it. */ - -int EmSocket (int domain, int type, int protocol) +SOCKET EmSocket (int domain, int type, int protocol) { - int sd; + SOCKET sd; #ifdef HAVE_SOCKET_CLOEXEC sd = socket (domain, type | SOCK_CLOEXEC, protocol); - if (sd < 0) { + if (sd == INVALID_SOCKET) { sd = socket (domain, type, protocol); if (sd < 0) { return sd; @@ -56,7 +55,7 @@ int EmSocket (int domain, int type, int protocol) } #else sd = socket (domain, type, protocol); - if (sd < 0) { + if (sd == INVALID_SOCKET) { return sd; } SetFdCloexec(sd); @@ -114,8 +113,8 @@ EventMachine_t::EventMachine_t (EMCallback event_callback, Poller_t poller): NumCloseScheduled (0), HeartbeatInterval(2000000), EventCallback (event_callback), - LoopBreakerReader (-1), - LoopBreakerWriter (-1), + LoopBreakerReader (INVALID_SOCKET), + LoopBreakerWriter (INVALID_SOCKET), bTerminateSignalReceived (false), Poller (poller), epfd (-1), @@ -353,7 +352,7 @@ void EventMachine_t::_InitializeLoopBreaker() #endif #ifdef OS_WIN32 - int sd = EmSocket (AF_INET, SOCK_DGRAM, 0); + SOCKET sd = EmSocket (AF_INET, SOCK_DGRAM, 0); if (sd == INVALID_SOCKET) throw std::runtime_error ("no loop breaker socket"); SetSocketNonblocking (sd); @@ -975,7 +974,7 @@ void EventMachine_t::_RunSelectOnce() for (i = 0; i < Descriptors.size(); i++) { EventableDescriptor *ed = Descriptors[i]; assert (ed); - int sd = ed->GetSocket(); + SOCKET sd = ed->GetSocket(); if (ed->IsWatchOnly() && sd == INVALID_SOCKET) continue; assert (sd != INVALID_SOCKET); @@ -1020,7 +1019,7 @@ void EventMachine_t::_RunSelectOnce() for (i=0; i < Descriptors.size(); i++) { EventableDescriptor *ed = Descriptors[i]; assert (ed); - int sd = ed->GetSocket(); + SOCKET sd = ed->GetSocket(); if (ed->IsWatchOnly() && sd == INVALID_SOCKET) continue; assert (sd != INVALID_SOCKET); @@ -1069,7 +1068,7 @@ void EventMachine_t::_CleanBadDescriptors() if (ed->ShouldDelete()) continue; - int sd = ed->GetSocket(); + SOCKET sd = ed->GetSocket(); struct timeval tv; tv.tv_sec = 0; @@ -1192,7 +1191,7 @@ const uintptr_t EventMachine_t::ConnectToServer (const char *bind_addr, int bind throw std::runtime_error ("unable to resolve server address"); bind_as = *bind_as_ptr; // copy because name2address points to a static - int sd = EmSocket (family, SOCK_STREAM, 0); + SOCKET sd = EmSocket (family, SOCK_STREAM, 0); if (sd == INVALID_SOCKET) { char buf [200]; snprintf (buf, sizeof(buf)-1, "unable to create new socket: %s", strerror(errno)); @@ -1378,7 +1377,7 @@ const uintptr_t EventMachine_t::ConnectToUnixServer (const char *server) strcpy (pun.sun_path, server); - int fd = EmSocket (AF_LOCAL, SOCK_STREAM, 0); + SOCKET fd = EmSocket (AF_LOCAL, SOCK_STREAM, 0); if (fd == INVALID_SOCKET) return 0; @@ -1417,7 +1416,7 @@ const uintptr_t EventMachine_t::ConnectToUnixServer (const char *server) EventMachine_t::AttachFD ************************/ -const uintptr_t EventMachine_t::AttachFD (int fd, bool watch_mode) +const uintptr_t EventMachine_t::AttachFD (SOCKET fd, bool watch_mode) { #ifdef OS_UNIX if (fcntl(fd, F_GETFL, 0) < 0) @@ -1473,7 +1472,7 @@ int EventMachine_t::DetachFD (EventableDescriptor *ed) if (!ed) throw std::runtime_error ("detaching bad descriptor"); - int fd = ed->GetSocket(); + SOCKET fd = ed->GetSocket(); #ifdef HAVE_EPOLL if (Poller == Poller_Epoll) { @@ -1613,7 +1612,7 @@ const uintptr_t EventMachine_t::CreateTcpServer (const char *server, int port) //struct sockaddr_in sin; - int sd_accept = EmSocket (family, SOCK_STREAM, 0); + SOCKET sd_accept = EmSocket (family, SOCK_STREAM, 0); if (sd_accept == INVALID_SOCKET) { goto fail; } @@ -1664,7 +1663,7 @@ const uintptr_t EventMachine_t::OpenDatagramSocket (const char *address, int por { uintptr_t output_binding = 0; - int sd = EmSocket (AF_INET, SOCK_DGRAM, 0); + SOCKET sd = EmSocket (AF_INET, SOCK_DGRAM, 0); if (sd == INVALID_SOCKET) goto fail; // from here on, early returns must close the socket! @@ -1957,7 +1956,7 @@ const uintptr_t EventMachine_t::CreateUnixDomainServer (const char *filename) struct sockaddr_un s_sun; - int sd_accept = EmSocket (AF_LOCAL, SOCK_STREAM, 0); + SOCKET sd_accept = EmSocket (AF_LOCAL, SOCK_STREAM, 0); if (sd_accept == INVALID_SOCKET) { goto fail; } @@ -2005,7 +2004,7 @@ const uintptr_t EventMachine_t::CreateUnixDomainServer (const char *filename) EventMachine_t::AttachSD **************************************/ -const uintptr_t EventMachine_t::AttachSD (int sd_accept) +const uintptr_t EventMachine_t::AttachSD (SOCKET sd_accept) { uintptr_t output_binding = 0; diff --git a/ext/em.h b/ext/em.h index fab992762..ec239d913 100644 --- a/ext/em.h +++ b/ext/em.h @@ -148,7 +148,7 @@ class EventMachine_t const uintptr_t CreateTcpServer (const char *, int); const uintptr_t OpenDatagramSocket (const char *, int); const uintptr_t CreateUnixDomainServer (const char*); - const uintptr_t AttachSD (int); + const uintptr_t AttachSD (SOCKET); const uintptr_t OpenKeyboard(); //const char *Popen (const char*, const char*); const uintptr_t Socketpair (char* const*); @@ -157,7 +157,7 @@ class EventMachine_t void Modify (EventableDescriptor*); void Deregister (EventableDescriptor*); - const uintptr_t AttachFD (int, bool); + const uintptr_t AttachFD (SOCKET, bool); int DetachFD (EventableDescriptor*); void ArmKqueueWriter (EventableDescriptor*); @@ -241,8 +241,8 @@ class EventMachine_t vector NewDescriptors; set ModifiedDescriptors; - int LoopBreakerReader; - int LoopBreakerWriter; + SOCKET LoopBreakerReader; + SOCKET LoopBreakerWriter; #ifdef OS_WIN32 struct sockaddr_in LoopBreakerTarget; #endif @@ -294,7 +294,7 @@ struct SelectData_t int _Select(); void _Clear(); - int maxsocket; + SOCKET maxsocket; rb_fdset_t fdreads; rb_fdset_t fdwrites; rb_fdset_t fderrors; From e55e51e7b411038ad036f654ac0d3f11c5166f54 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Fri, 31 Jul 2015 15:35:40 -0700 Subject: [PATCH 016/343] Adjust ifdefs for functions that aren't useful or implemented for Windows Surround them with #ifdefs rather than internal ifdefs so that the unused parameters can be marked UNUSED. --- ext/cmain.cpp | 9 ++++--- ext/ed.cpp | 10 +++++--- ext/em.cpp | 68 +++++++++++++++++++++++---------------------------- 3 files changed, 43 insertions(+), 44 deletions(-) diff --git a/ext/cmain.cpp b/ext/cmain.cpp index 981d5ec16..fc81114b4 100644 --- a/ext/cmain.cpp +++ b/ext/cmain.cpp @@ -521,10 +521,10 @@ extern "C" int evma_get_sockname (const uintptr_t binding, struct sockaddr *sa, evma_get_subprocess_pid ***********************/ +#ifdef OS_UNIX extern "C" int evma_get_subprocess_pid (const uintptr_t binding, pid_t *pid) { ensure_eventmachine("evma_get_subprocess_pid"); - #ifdef OS_UNIX PipeDescriptor *pd = dynamic_cast (Bindable_t::GetObject (binding)); if (pd) { return pd->GetSubprocessPid (pid) ? 1 : 0; @@ -535,10 +535,13 @@ extern "C" int evma_get_subprocess_pid (const uintptr_t binding, pid_t *pid) } else return 0; - #else +} +#else +extern "C" int evma_get_subprocess_pid (const uintptr_t binding UNUSED, pid_t *pid UNUSED) +{ return 0; - #endif } +#endif /************************** evma_get_subprocess_status diff --git a/ext/ed.cpp b/ext/ed.cpp index 4ab2ae10f..36c8a6a9c 100644 --- a/ext/ed.cpp +++ b/ext/ed.cpp @@ -49,18 +49,20 @@ bool SetSocketNonblocking (SOCKET sd) SetFdCloexec ************/ +#ifdef OS_UNIX bool SetFdCloexec (int fd) { - #ifdef OS_UNIX int flags = fcntl(fd, F_GETFD, 0); assert (flags >= 0); flags |= FD_CLOEXEC; return (fcntl(fd, F_SETFD, FD_CLOEXEC) == 0) ? true : false; - #else - // TODO: Windows? +} +#else +bool SetFdCloexec (int fd UNUSED) +{ return true; - #endif } +#endif /**************************************** EventableDescriptor::EventableDescriptor diff --git a/ext/em.cpp b/ext/em.cpp index dcb3f75df..c740735d0 100644 --- a/ext/em.cpp +++ b/ext/em.cpp @@ -239,6 +239,7 @@ void EventMachine_t::SetTimerQuantum (int interval) (STATIC) EventMachine_t::SetuidString *************************************/ +#ifdef OS_UNIX void EventMachine_t::SetuidString (const char *username) { /* This method takes a caller-supplied username and tries to setuid @@ -255,7 +256,6 @@ void EventMachine_t::SetuidString (const char *username) * A setuid failure here would be in the latter category. */ - #ifdef OS_UNIX if (!username || !*username) throw std::runtime_error ("setuid_string failed: no username specified"); @@ -267,17 +267,18 @@ void EventMachine_t::SetuidString (const char *username) throw std::runtime_error ("setuid_string failed: no setuid"); // Success. - #endif } - +#else +void EventMachine_t::SetuidString (const char *username UNUSED) { } +#endif /**************************************** (STATIC) EventMachine_t::SetRlimitNofile ****************************************/ +#ifdef OS_UNIX int EventMachine_t::SetRlimitNofile (int nofiles) { - #ifdef OS_UNIX struct rlimit rlim; getrlimit (RLIMIT_NOFILE, &rlim); if (nofiles >= 0) { @@ -290,14 +291,10 @@ int EventMachine_t::SetRlimitNofile (int nofiles) } getrlimit (RLIMIT_NOFILE, &rlim); return rlim.rlim_cur; - #endif - - #ifdef OS_WIN32 - // No meaningful implementation on Windows. - return 0; - #endif } - +#else +int EventMachine_t::SetRlimitNofile (int nofiles UNUSED) { return 0; } +#endif /********************************* EventMachine_t::SignalLoopBreaker @@ -1343,6 +1340,7 @@ const uintptr_t EventMachine_t::ConnectToServer (const char *bind_addr, int bind EventMachine_t::ConnectToUnixServer ***********************************/ +#ifdef OS_UNIX const uintptr_t EventMachine_t::ConnectToUnixServer (const char *server) { /* Connect to a Unix-domain server, which by definition is running @@ -1352,14 +1350,6 @@ const uintptr_t EventMachine_t::ConnectToUnixServer (const char *server) * is always local and can always be fulfilled immediately. */ - #ifdef OS_WIN32 - throw std::runtime_error ("unix-domain connection unavailable on this platform"); - return 0; - #endif - - // The whole rest of this function is only compiled on Unix systems. - #ifdef OS_UNIX - uintptr_t out = 0; if (!server || !*server) @@ -1409,8 +1399,13 @@ const uintptr_t EventMachine_t::ConnectToUnixServer (const char *server) close (fd); return out; - #endif } +#else +const uintptr_t EventMachine_t::ConnectToUnixServer (const char *server UNUSED) +{ + throw std::runtime_error ("unix-domain connection unavailable on this platform"); +} +#endif /************************ EventMachine_t::AttachFD @@ -1938,6 +1933,7 @@ void EventMachine_t::Deregister (EventableDescriptor *ed) EventMachine_t::CreateUnixDomainServer **************************************/ +#ifdef OS_UNIX const uintptr_t EventMachine_t::CreateUnixDomainServer (const char *filename) { /* Create a UNIX-domain acceptor (server) socket and add it to the event machine. @@ -1947,13 +1943,6 @@ const uintptr_t EventMachine_t::CreateUnixDomainServer (const char *filename) * THERE IS NO MEANINGFUL IMPLEMENTATION ON WINDOWS. */ - #ifdef OS_WIN32 - throw std::runtime_error ("unix-domain server unavailable on this platform"); - #endif - - // The whole rest of this function is only compiled on Unix systems. - #ifdef OS_UNIX - struct sockaddr_un s_sun; SOCKET sd_accept = EmSocket (AF_LOCAL, SOCK_STREAM, 0); @@ -1996,8 +1985,13 @@ const uintptr_t EventMachine_t::CreateUnixDomainServer (const char *filename) if (sd_accept != INVALID_SOCKET) close (sd_accept); return 0; - #endif // OS_UNIX } +#else +const uintptr_t EventMachine_t::CreateUnixDomainServer (const char *filename UNUSED) +{ + throw std::runtime_error ("unix-domain server unavailable on this platform"); +} +#endif /************************************** @@ -2039,15 +2033,9 @@ const uintptr_t EventMachine_t::AttachSD (SOCKET sd_accept) EventMachine_t::Socketpair **************************/ -const uintptr_t EventMachine_t::Socketpair (char * const*cmd_strings) +#ifdef OS_UNIX +const uintptr_t EventMachine_t::Socketpair (char * const * cmd_strings) { - #ifdef OS_WIN32 - throw std::runtime_error ("socketpair is currently unavailable on this platform"); - #endif - - // The whole rest of this function is only compiled on Unix systems. - // Eventually we need this functionality (or a full-duplex equivalent) on Windows. - #ifdef OS_UNIX // Make sure the incoming array of command strings is sane. if (!cmd_strings) return 0; @@ -2095,8 +2083,14 @@ const uintptr_t EventMachine_t::Socketpair (char * const*cmd_strings) throw std::runtime_error ("no fork"); return output_binding; - #endif } +#else +const uintptr_t EventMachine_t::Socketpair (char * const * cmd_strings UNUSED) +{ + throw std::runtime_error ("socketpair is currently unavailable on this platform"); +} +#endif + /**************************** From b10252f3ea7bf5f8abf139010a7a15f5f5164da2 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Fri, 31 Jul 2015 16:34:11 -0700 Subject: [PATCH 017/343] Remove the WITHOUT_SSL constant --- ext/ed.cpp | 32 ++++++++++++++++++-------------- ext/extconf.rb | 2 -- ext/rubymain.cpp | 9 +++++++-- 3 files changed, 25 insertions(+), 18 deletions(-) diff --git a/ext/ed.cpp b/ext/ed.cpp index 36c8a6a9c..3fd7ebdf2 100644 --- a/ext/ed.cpp +++ b/ext/ed.cpp @@ -859,9 +859,9 @@ void ConnectionDescriptor::Read() ConnectionDescriptor::_DispatchInboundData ******************************************/ +#ifdef WITH_SSL void ConnectionDescriptor::_DispatchInboundData (const char *buffer, unsigned long size) { - #ifdef WITH_SSL if (SslBox) { SslBox->PutCiphertext (buffer, size); @@ -885,12 +885,13 @@ void ConnectionDescriptor::_DispatchInboundData (const char *buffer, unsigned lo else { _GenericInboundDispatch(buffer, size); } - #endif - - #ifdef WITHOUT_SSL +} +#else +void ConnectionDescriptor::_DispatchInboundData (const char *buffer, unsigned long size) +{ _GenericInboundDispatch(buffer, size); - #endif } +#endif @@ -1169,29 +1170,31 @@ int ConnectionDescriptor::ReportErrorStatus() ConnectionDescriptor::StartTls ******************************/ +#ifdef WITH_SSL void ConnectionDescriptor::StartTls() { - #ifdef WITH_SSL if (SslBox) throw std::runtime_error ("SSL/TLS already running on connection"); SslBox = new SslBox_t (bIsServer, PrivateKeyFilename, CertChainFilename, bSslVerifyPeer, GetBinding()); _DispatchCiphertext(); - #endif - #ifdef WITHOUT_SSL +} +#else +void ConnectionDescriptor::StartTls() +{ throw std::runtime_error ("Encryption not available on this event-machine"); - #endif } +#endif /********************************* ConnectionDescriptor::SetTlsParms *********************************/ +#ifdef WITH_SSL void ConnectionDescriptor::SetTlsParms (const char *privkey_filename, const char *certchain_filename, bool verify_peer) { - #ifdef WITH_SSL if (SslBox) throw std::runtime_error ("call SetTlsParms before calling StartTls"); if (privkey_filename && *privkey_filename) @@ -1199,12 +1202,13 @@ void ConnectionDescriptor::SetTlsParms (const char *privkey_filename, const char if (certchain_filename && *certchain_filename) CertChainFilename = certchain_filename; bSslVerifyPeer = verify_peer; - #endif - - #ifdef WITHOUT_SSL +} +#else +void ConnectionDescriptor::SetTlsParms (const char *privkey_filename UNUSED, const char *certchain_filename UNUSED, bool verify_peer UNUSED) +{ throw std::runtime_error ("Encryption not available on this event-machine"); - #endif } +#endif /********************************* diff --git a/ext/extconf.rb b/ext/extconf.rb index d3c9cfdce..55c720339 100644 --- a/ext/extconf.rb +++ b/ext/extconf.rb @@ -63,8 +63,6 @@ def manual_ssl_config # Try to use pkg_config first, fixes #73 if (!ENV['CROSS_COMPILING'] and pkg_config('openssl')) || manual_ssl_config add_define "WITH_SSL" -else - add_define "WITHOUT_SSL" end add_define 'BUILD_FOR_RUBY' diff --git a/ext/rubymain.cpp b/ext/rubymain.cpp index dbdf7fe89..c03aade63 100644 --- a/ext/rubymain.cpp +++ b/ext/rubymain.cpp @@ -345,11 +345,11 @@ static VALUE t_set_tls_parms (VALUE self UNUSED, VALUE signature, VALUE privkeyf t_get_peer_cert ***************/ +#ifdef WITH_SSL static VALUE t_get_peer_cert (VALUE self UNUSED, VALUE signature) { VALUE ret = Qnil; - #ifdef WITH_SSL X509 *cert = NULL; BUF_MEM *buf; BIO *out; @@ -364,10 +364,15 @@ static VALUE t_get_peer_cert (VALUE self UNUSED, VALUE signature) X509_free(cert); BIO_free(out); } - #endif return ret; } +#else +static VALUE t_get_peer_cert (VALUE self UNUSED, VALUE signature UNUSED) +{ + return Qnil; +} +#endif /************** t_get_peername From c6ac3edfa48e26575861cb83cab74efec7523209 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Fri, 31 Jul 2015 16:34:29 -0700 Subject: [PATCH 018/343] Remove unused rakelib/cpp.rake_example --- rakelib/cpp.rake_example | 77 ---------------------------------------- 1 file changed, 77 deletions(-) delete mode 100644 rakelib/cpp.rake_example diff --git a/rakelib/cpp.rake_example b/rakelib/cpp.rake_example deleted file mode 100644 index c1a895a68..000000000 --- a/rakelib/cpp.rake_example +++ /dev/null @@ -1,77 +0,0 @@ -# EventMachine C++ Rakefile Stab Case -# TODO : track header files as a build dependency... -# TODO : cross platform support -# TODO : configure style functionality -namespace :cpp do - - require 'rake/clean' - - # *nix only atm... - module Cpp - class < [proc { |targ| - targ.sub(%r{^#{EmConfig::Path}/(.*)\.o$}, "#{EmConfig::Path}/\\1.cpp") - }] do |t| - Cpp.compile t.source, t.name, EmConfig::Includes, EmConfig::Flags - end - - file "#{EmConfig::Path}/libeventmachine.a" => EmConfig::Compiled do |t| - Cpp.static t.name, EmConfig::Compiled - end - CLEAN.include("#{EmConfig::Path}/libeventmachine.a") - - module AppConfig - Appname = 'echo_em' - Sources = FileList['*.cpp'] - Compiled = Sources.sub(%r{^(.*)\.cpp}, '\\1.o') - - Flags = ["", EmConfig::Flags].join(' ') - Includes = ["-I. -I#{EmConfig::Path}", EmConfig::Includes].join(' ') - Libs = ["-L#{EmConfig::Path} -leventmachine", EmConfig::Libs].join(' ') - end - CLEAN.include(AppConfig::Compiled) - CLEAN.include(AppConfig::Appname) - - rule %r{^.*\.o$} => [proc { |targ| - targ.sub(%r{^(.*)\.o$}, '\\1.cpp') - }] do |t| - Cpp.compile t.source, t.name, AppConfig::Includes, AppConfig::Flags - end - - file AppConfig::Appname => ["#{EmConfig::Path}/libeventmachine.a", AppConfig::Compiled] do |t| - Cpp.link AppConfig::Compiled, t.name, AppConfig::Libs, AppConfig::Flags - end - - task :build => AppConfig::Appname - - task :run => AppConfig::Appname do - sh "./#{AppConfig::Appname}" - end - -end \ No newline at end of file From 8aeaf67158e8667cd4611e7649727456f1cff636 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Mon, 3 Aug 2015 10:23:43 -0700 Subject: [PATCH 019/343] Add -Wno-address always-true because on Windows, rb_fd_select checks if &fds is non-NULL, which it cannot be --- ext/extconf.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ext/extconf.rb b/ext/extconf.rb index 55c720339..1e041f032 100644 --- a/ext/extconf.rb +++ b/ext/extconf.rb @@ -186,12 +186,14 @@ def manual_ssl_config # deprecated-declarations are used in OS X OpenSSL # ignored-qualifiers are used by the Bindings (would-be void *) # unused-result because GCC 4.6 no longer silences (void) ignore_this(function) +# address because on Windows, rb_fd_select checks if &fds is non-NULL, which it cannot be %w( -Wall -Wextra -Wno-deprecated-declarations -Wno-ignored-qualifiers -Wno-unused-result + -Wno-address ).select do |flag| try_link('int main() {return 0;}', flag) end.each do |flag| From 1fe5f6fef1b8c1c56186913116a3892e1385aa48 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Mon, 3 Aug 2015 00:43:35 -0700 Subject: [PATCH 020/343] New macro PRIFBSIG for printf format for uintptr_t --- ext/rubymain.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/ext/rubymain.cpp b/ext/rubymain.cpp index c03aade63..3d4ede0c5 100644 --- a/ext/rubymain.cpp +++ b/ext/rubymain.cpp @@ -30,9 +30,19 @@ See the file COPYING for complete licensing information. #if SIZEOF_VOIDP == SIZEOF_LONG # define BSIG2NUM(x) (ULONG2NUM((unsigned long)(x))) # define NUM2BSIG(x) (NUM2ULONG(x)) +# ifdef OS_WIN32 +# define PRIFBSIG "I32u" +# else +# define PRIFBSIG "lu" +# endif #else # define BSIG2NUM(x) (ULL2NUM((unsigned long long)(x))) # define NUM2BSIG(x) (NUM2ULL(x)) +# ifdef OS_WIN32 +# define PRIFBSIG "I64u" +# else +# define PRIFBSIG "llu" +# endif #endif /******* @@ -79,7 +89,7 @@ static inline VALUE ensure_conn(const uintptr_t signature) { VALUE conn = rb_hash_aref (EmConnsHash, BSIG2NUM (signature)); if (conn == Qnil) - rb_raise (EM_eConnectionNotBound, "unknown connection: %lu", signature); + rb_raise (EM_eConnectionNotBound, "unknown connection: %" PRIFBSIG, signature); return conn; } @@ -100,7 +110,7 @@ static inline void event_callback (struct em_event* e) { VALUE conn = rb_hash_aref (EmConnsHash, BSIG2NUM (signature)); if (conn == Qnil) - rb_raise (EM_eConnectionNotBound, "received %lu bytes of data for unknown signature: %lu", data_num, signature); + rb_raise (EM_eConnectionNotBound, "received %lu bytes of data for unknown signature: %" PRIFBSIG, data_num, signature); rb_funcall (conn, Intern_receive_data, 1, rb_str_new (data_str, data_num)); return; } From ec195e69836c92698026816b81159f7b4c9971a0 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Mon, 3 Aug 2015 00:19:16 -0700 Subject: [PATCH 021/343] Use a stronger name for the error reason code than just e --- ext/em.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ext/em.cpp b/ext/em.cpp index c740735d0..a478714d0 100644 --- a/ext/em.cpp +++ b/ext/em.cpp @@ -1221,9 +1221,9 @@ const uintptr_t EventMachine_t::ConnectToServer (const char *bind_addr, int bind } uintptr_t out = 0; - int e = 0; #ifdef OS_UNIX + int e_reason = 0; //if (connect (sd, (sockaddr*)&pin, sizeof pin) == 0) { if (connect (sd, &bind_as, bind_size) == 0) { // This is a connect success, which Linux appears @@ -1271,13 +1271,13 @@ const uintptr_t EventMachine_t::ConnectToServer (const char *bind_addr, int bind out = cd->GetBinding(); } else { // Fall through to the !out case below. - e = error; + e_reason = error; } } else { // The error from connect was something other then EINPROGRESS (EHOSTDOWN, etc). // Fall through to the !out case below - e = errno; + e_reason = errno; } if (!out) { @@ -1296,7 +1296,7 @@ const uintptr_t EventMachine_t::ConnectToServer (const char *bind_addr, int bind ConnectionDescriptor *cd = new ConnectionDescriptor (sd, this); if (!cd) throw std::runtime_error ("no connection allocated"); - cd->SetUnbindReasonCode(e); + cd->SetUnbindReasonCode (e_reason); cd->ScheduleClose (false); Add (cd); out = cd->GetBinding(); From 5380301a8cf96ed98a6865d0a3a8c8778c18357c Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Mon, 3 Aug 2015 00:23:13 -0700 Subject: [PATCH 022/343] Move declarations closer to point of use --- ext/em.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/ext/em.cpp b/ext/em.cpp index a478714d0..e531ce4c3 100644 --- a/ext/em.cpp +++ b/ext/em.cpp @@ -1530,15 +1530,10 @@ struct sockaddr *name2address (const char *server, int port, int *family, int *b // Check the more-common cases first. // Return NULL if no resolution. - static struct sockaddr_in in4; - #ifndef __CYGWIN__ - static struct sockaddr_in6 in6; - #endif - struct hostent *hp; - if (!server || !*server) server = "0.0.0.0"; + static struct sockaddr_in in4; memset (&in4, 0, sizeof(in4)); if ( (in4.sin_addr.s_addr = inet_addr (server)) != INADDR_NONE) { if (family) @@ -1551,6 +1546,7 @@ struct sockaddr *name2address (const char *server, int port, int *family, int *b } #if defined(OS_UNIX) && !defined(__CYGWIN__) + static struct sockaddr_in6 in6; memset (&in6, 0, sizeof(in6)); if (inet_pton (AF_INET6, server, in6.sin6_addr.s6_addr) > 0) { if (family) @@ -1571,6 +1567,7 @@ struct sockaddr *name2address (const char *server, int port, int *family, int *b // For the time being, Ipv6 addresses aren't supported on Windows. #endif + struct hostent *hp; hp = gethostbyname ((char*)server); // Windows requires the cast. if (hp) { in4.sin_addr.s_addr = ((in_addr*)(hp->h_addr))->s_addr; From 31906b748d963af10923d96432a61bd191548846 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Mon, 3 Aug 2015 01:13:07 -0700 Subject: [PATCH 023/343] Use these variables --- ext/ed.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ext/ed.cpp b/ext/ed.cpp index 3fd7ebdf2..1e4a9cf4c 100644 --- a/ext/ed.cpp +++ b/ext/ed.cpp @@ -444,6 +444,9 @@ void ConnectionDescriptor::_UpdateEvents(bool read, bool write) if (MySocket == INVALID_SOCKET) return; + if (!read && !write) + return; + #ifdef HAVE_EPOLL unsigned int old = EpollEvent.events; From 8f9b5759dbbd8335e2523a738e4467fafaec0496 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Mon, 3 Aug 2015 11:45:51 -0700 Subject: [PATCH 024/343] Mark pending tests on Windows --- tests/test_basic.rb | 2 ++ tests/test_resolver.rb | 8 ++++++++ tests/test_unbind_reason.rb | 4 ++++ 3 files changed, 14 insertions(+) diff --git a/tests/test_basic.rb b/tests/test_basic.rb index 7cf0d6088..d15175b82 100644 --- a/tests/test_basic.rb +++ b/tests/test_basic.rb @@ -131,6 +131,8 @@ def test_byte_range_send end def test_bind_connect + pend('FIXME: this test is broken on Windows') if windows? + local_ip = UDPSocket.open {|s| s.connect('google.com', 80); s.addr.last } bind_port = next_port diff --git a/tests/test_resolver.rb b/tests/test_resolver.rb index 7ae1d4a82..a69e515e6 100644 --- a/tests/test_resolver.rb +++ b/tests/test_resolver.rb @@ -17,6 +17,8 @@ def test_hosts end def test_a + pend('FIXME: this test is broken on Windows') if windows? + EM.run { d = EM::DNS::Resolver.resolve "google.com" d.errback { assert false } @@ -44,6 +46,8 @@ def test_garbage end def test_a_pair + pend('FIXME: this test is broken on Windows') if windows? + EM.run { d = EM::DNS::Resolver.resolve "yahoo.com" d.errback { |err| assert false, "failed to resolve yahoo.com: #{err}" } @@ -56,6 +60,8 @@ def test_a_pair end def test_localhost + pend('FIXME: this test is broken on Windows') if windows? + EM.run { d = EM::DNS::Resolver.resolve "localhost" d.errback { assert false } @@ -69,6 +75,8 @@ def test_localhost end def test_timer_cleanup + pend('FIXME: this test is broken on Windows') if windows? + EM.run { d = EM::DNS::Resolver.resolve "google.com" d.errback { |err| assert false, "failed to resolve google.com: #{err}" } diff --git a/tests/test_unbind_reason.rb b/tests/test_unbind_reason.rb index aad144a71..e372d2c1d 100644 --- a/tests/test_unbind_reason.rb +++ b/tests/test_unbind_reason.rb @@ -26,6 +26,8 @@ def test_connect_timeout end def test_connect_refused + pend('FIXME: this test is broken on Windows') if windows? + error = nil EM.run { EM.connect '127.0.0.1', 12388, Module.new{ |m| @@ -39,6 +41,8 @@ def test_connect_refused end def test_optional_argument + pend('FIXME: this test is broken on Windows') if windows? + conn = nil EM.run { conn = EM.connect '127.0.0.1', 12388, StubConnection From 5a5403259b86cf32df8f5fc4f34eb58a297b7d03 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Mon, 3 Aug 2015 13:02:41 -0700 Subject: [PATCH 025/343] Handle current_time larger than time_t if time_t is 32-bits --- ext/rubymain.cpp | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/ext/rubymain.cpp b/ext/rubymain.cpp index 3d4ede0c5..54d806f33 100644 --- a/ext/rubymain.cpp +++ b/ext/rubymain.cpp @@ -67,6 +67,7 @@ static VALUE Intern_event_callback; static VALUE Intern_run_deferred_callbacks; static VALUE Intern_delete; static VALUE Intern_call; +static VALUE Intern_at; static VALUE Intern_receive_data; static VALUE Intern_ssl_handshake_completed; static VALUE Intern_ssl_verify_peer; @@ -1144,20 +1145,17 @@ t_get_loop_time static VALUE t_get_loop_time (VALUE self UNUSED) { - #ifndef HAVE_RB_TIME_NEW - static VALUE cTime = rb_path2class("Time"); - static ID at = rb_intern("at"); - #endif - uint64_t current_time = evma_get_current_loop_time(); - if (current_time != 0) { - #ifndef HAVE_RB_TIME_NEW - return rb_funcall(cTime, at, 2, INT2NUM(current_time / 1000000), INT2NUM(current_time % 1000000)); - #else + if (current_time == 0) { + return Qnil; + } + + // Generally the industry has moved to 64-bit time_t, this is just in case we're 32-bit time_t. + if (sizeof(time_t) < 8 && current_time > INT_MAX) { + return rb_funcall(rb_cTime, Intern_at, 2, INT2NUM(current_time / 1000000), INT2NUM(current_time % 1000000)); + } else { return rb_time_new(current_time / 1000000, current_time % 1000000); - #endif } - return Qnil; } @@ -1272,6 +1270,7 @@ extern "C" void Init_rubyeventmachine() Intern_run_deferred_callbacks = rb_intern ("run_deferred_callbacks"); Intern_delete = rb_intern ("delete"); Intern_call = rb_intern ("call"); + Intern_at = rb_intern("at"); Intern_receive_data = rb_intern ("receive_data"); Intern_ssl_handshake_completed = rb_intern ("ssl_handshake_completed"); Intern_ssl_verify_peer = rb_intern ("ssl_verify_peer"); From cd8d3006feef124321a8fc6f86c1d09f8feaff9c Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Mon, 10 Aug 2015 13:56:32 -0700 Subject: [PATCH 026/343] Initialize TickCount on Windows --- ext/em.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ext/em.cpp b/ext/em.cpp index e531ce4c3..a62e66571 100644 --- a/ext/em.cpp +++ b/ext/em.cpp @@ -137,6 +137,11 @@ EventMachine_t::EventMachine_t (EMCallback event_callback, Poller_t poller): (void) mach_timebase_info(&mach_timebase); #endif + #ifdef OS_WIN32 + TickCountTickover = 0; + LastTickCount = 0; + #endif + // Make sure the current loop time is sane, in case we do any initializations of // objects before we start running. _UpdateTime(); From a1fa52a7ca840b59bdf1d849ff47eb72cb7ca73c Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Sun, 2 Aug 2015 22:59:47 -0700 Subject: [PATCH 027/343] Throw a message with strerror included --- ext/em.cpp | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/ext/em.cpp b/ext/em.cpp index a62e66571..973c141eb 100644 --- a/ext/em.cpp +++ b/ext/em.cpp @@ -264,9 +264,17 @@ void EventMachine_t::SetuidString (const char *username) if (!username || !*username) throw std::runtime_error ("setuid_string failed: no username specified"); + errno = 0; struct passwd *p = getpwnam (username); - if (!p) - throw std::runtime_error ("setuid_string failed: unknown username"); + if (!p) { + if (errno) { + char buf[200]; + snprintf (buf, sizeof(buf)-1, "setuid_string failed: %s", strerror(errno)); + throw std::runtime_error (buf); + } else { + throw std::runtime_error ("setuid_string failed: unknown username"); + } + } if (setuid (p->pw_uid) != 0) throw std::runtime_error ("setuid_string failed: no setuid"); @@ -1189,8 +1197,11 @@ const uintptr_t EventMachine_t::ConnectToServer (const char *bind_addr, int bind int family, bind_size; struct sockaddr bind_as, *bind_as_ptr = name2address (server, port, &family, &bind_size); - if (!bind_as_ptr) - throw std::runtime_error ("unable to resolve server address"); + if (!bind_as_ptr) { + char buf [200]; + snprintf (buf, sizeof(buf)-1, "unable to resolve server address: %s", strerror(errno)); + throw std::runtime_error (buf); + } bind_as = *bind_as_ptr; // copy because name2address points to a static SOCKET sd = EmSocket (family, SOCK_STREAM, 0); @@ -1419,8 +1430,13 @@ EventMachine_t::AttachFD const uintptr_t EventMachine_t::AttachFD (SOCKET fd, bool watch_mode) { #ifdef OS_UNIX - if (fcntl(fd, F_GETFL, 0) < 0) - throw std::runtime_error ("invalid file descriptor"); + if (fcntl(fd, F_GETFL, 0) < 0) { + if (errno) { + throw std::runtime_error (strerror(errno)); + } else { + throw std::runtime_error ("invalid file descriptor"); + } + } #endif #ifdef OS_WIN32 From 7b376df0db89147a300e2dee09aaa5b841b132a2 Mon Sep 17 00:00:00 2001 From: Kevin Busby Date: Thu, 24 Sep 2015 23:15:22 +0100 Subject: [PATCH 028/343] Two tiny tweaks for grammar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Hyphenated compound adjective. Corrected “it’s” to “its”. --- README.md | 2 +- lib/em/completion.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c010b0d32..584cca8c0 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ This unique combination makes EventMachine a premier choice for designers of cri applications, including Web servers and proxies, email and IM production systems, authentication/authorization processors, and many more. -EventMachine has been around since the early 2000s and is a mature and battle tested library. +EventMachine has been around since the early 2000s and is a mature and battle-tested library. ## What EventMachine is good for? ## diff --git a/lib/em/completion.rb b/lib/em/completion.rb index 59cee39ca..926535fd2 100644 --- a/lib/em/completion.rb +++ b/lib/em/completion.rb @@ -1,7 +1,7 @@ # = EM::Completion # # A completion is a callback container for various states of completion. In -# it's most basic form it has a start state and a finish state. +# its most basic form it has a start state and a finish state. # # This implementation includes some hold-back from the EM::Deferrable # interface in order to be compatible - but it has a much cleaner @@ -50,7 +50,7 @@ # @completion.fail :unknown, line # end # end -# +# # def unbind # @completion.fail :disconnected unless @completion.completed? # end From 8e803ad97a894ec599e8c182dbc28ef8ff9d0a8b Mon Sep 17 00:00:00 2001 From: Shai Coleman Date: Mon, 28 Sep 2015 22:38:12 +0100 Subject: [PATCH 029/343] Replace Exception class with StandardError --- ext/fastfilereader/rubymain.cpp | 12 ++++++------ lib/em/pool.rb | 2 +- lib/em/protocols/line_and_text.rb | 2 +- lib/em/threaded_resource.rb | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/ext/fastfilereader/rubymain.cpp b/ext/fastfilereader/rubymain.cpp index 73e17c420..ce72ebf04 100644 --- a/ext/fastfilereader/rubymain.cpp +++ b/ext/fastfilereader/rubymain.cpp @@ -50,7 +50,7 @@ static VALUE mapper_new (VALUE self, VALUE filename) { Mapper_t *m = new Mapper_t (StringValueCStr (filename)); if (!m) - rb_raise (rb_eException, "No Mapper Object"); + rb_raise (rb_eStandardError, "No Mapper Object"); VALUE v = Data_Wrap_Struct (Mapper, 0, mapper_dt, (void*)m); return v; } @@ -65,17 +65,17 @@ static VALUE mapper_get_chunk (VALUE self, VALUE start, VALUE length) Mapper_t *m = NULL; Data_Get_Struct (self, Mapper_t, m); if (!m) - rb_raise (rb_eException, "No Mapper Object"); + rb_raise (rb_eStandardError, "No Mapper Object"); // TODO, what if some moron sends us a negative start value? unsigned _start = NUM2INT (start); unsigned _length = NUM2INT (length); if ((_start + _length) > m->GetFileSize()) - rb_raise (rb_eException, "Mapper Range Error"); + rb_raise (rb_eStandardError, "Mapper Range Error"); const char *chunk = m->GetChunk (_start); if (!chunk) - rb_raise (rb_eException, "No Mapper Chunk"); + rb_raise (rb_eStandardError, "No Mapper Chunk"); return rb_str_new (chunk, _length); } @@ -88,7 +88,7 @@ static VALUE mapper_close (VALUE self) Mapper_t *m = NULL; Data_Get_Struct (self, Mapper_t, m); if (!m) - rb_raise (rb_eException, "No Mapper Object"); + rb_raise (rb_eStandardError, "No Mapper Object"); m->Close(); return Qnil; } @@ -102,7 +102,7 @@ static VALUE mapper_size (VALUE self) Mapper_t *m = NULL; Data_Get_Struct (self, Mapper_t, m); if (!m) - rb_raise (rb_eException, "No Mapper Object"); + rb_raise (rb_eStandardError, "No Mapper Object"); return INT2NUM (m->GetFileSize()); } diff --git a/lib/em/pool.rb b/lib/em/pool.rb index 3c7fdde09..2ecdcd2b9 100644 --- a/lib/em/pool.rb +++ b/lib/em/pool.rb @@ -143,7 +143,7 @@ def process work, resource else raise ArgumentError, "deferrable expected from work" end - rescue Exception + rescue failure resource raise end diff --git a/lib/em/protocols/line_and_text.rb b/lib/em/protocols/line_and_text.rb index 09983098b..784daf2fa 100644 --- a/lib/em/protocols/line_and_text.rb +++ b/lib/em/protocols/line_and_text.rb @@ -45,7 +45,7 @@ def receive_data data @lpb_buffer.extract(data).each do |line| receive_line(line.chomp) if respond_to?(:receive_line) end - rescue Exception + rescue receive_error('overlength line') if respond_to?(:receive_error) close_connection return diff --git a/lib/em/threaded_resource.rb b/lib/em/threaded_resource.rb index 11bb5745a..87704d5f5 100644 --- a/lib/em/threaded_resource.rb +++ b/lib/em/threaded_resource.rb @@ -71,7 +71,7 @@ def dispatch begin result = yield @resource completion.succeed result - rescue Exception => e + rescue => e completion.fail e end end @@ -87,4 +87,4 @@ def shutdown end end -end \ No newline at end of file +end From 1c6f52f88362c367966e43676960669f74c425c6 Mon Sep 17 00:00:00 2001 From: Jonathan del Strother Date: Tue, 29 Sep 2015 19:12:41 +0100 Subject: [PATCH 030/343] Support proc-sources in EM::Iterator eg EM::Iterator.new(proc{ queue.pop || EM::Iterator::Stop }) --- lib/em/iterator.rb | 31 ++++++++++++++++++++++++++----- tests/test_iterator.rb | 16 ++++++++++++++++ 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/lib/em/iterator.rb b/lib/em/iterator.rb index 035aa9d01..a30b9dd7f 100644 --- a/lib/em/iterator.rb +++ b/lib/em/iterator.rb @@ -41,6 +41,7 @@ module EventMachine # end # class Iterator + Stop = "EM::Stop" # Create a new parallel async iterator with specified concurrency. # # i = EM::Iterator.new(1..100, 10) @@ -48,10 +49,21 @@ class Iterator # will create an iterator over the range that processes 10 items at a time. Iteration # is started via #each, #map or #inject # + # The list may either be an array-like object, or a proc that returns a new object + # to be processed each time it is called. If a proc is used, it must return + # EventMachine::Iterator::Stop to signal the end of the iterations. + # def initialize(list, concurrency = 1) - raise ArgumentError, 'argument must be an array' unless list.respond_to?(:to_a) raise ArgumentError, 'concurrency must be bigger than zero' unless (concurrency > 0) - @list = list.to_a.dup + if list.respond_to?(:call) + @list = nil + @list_proc = list + elsif list.respond_to?(:to_a) + @list = list.to_a.dup + @list_proc = nil + else + raise ArgumentError, 'argument must be a proc or an array' + end @concurrency = concurrency @started = false @@ -98,12 +110,12 @@ def each(foreach=nil, after=nil, &blk) @process_next = proc{ # p [:process_next, :pending=, @pending, :workers=, @workers, :ended=, @ended, :concurrency=, @concurrency, :list=, @list] unless @ended or @workers > @concurrency - if @list.empty? + item = next_item() + if item.equal?(Stop) @ended = true @workers -= 1 all_done.call else - item = @list.shift @pending += 1 is_done = false @@ -222,10 +234,19 @@ def spawn_workers }) nil end + + # Return the next item from @list or @list_proc. + # Once items have run out, will return EM::Iterator::Stop. Procs must supply this themselves + def next_item + if @list_proc + @list_proc.call + else + @list.empty? ? Stop : @list.shift + end + end end end # TODO: pass in one object instead of two? .each{ |iter| puts iter.current; iter.next } # TODO: support iter.pause/resume/stop/break/continue? # TODO: create some exceptions instead of using RuntimeError -# TODO: support proc instead of enumerable? EM::Iterator.new(proc{ return queue.pop }) diff --git a/tests/test_iterator.rb b/tests/test_iterator.rb index a040d5585..a063634f8 100644 --- a/tests/test_iterator.rb +++ b/tests/test_iterator.rb @@ -21,6 +21,22 @@ def test_default_concurrency assert_equal(list.to_a.sort, items.values.flatten.sort) end + def test_default_concurrency_with_a_proc + items = {} + list = (1..10).to_a + original_list = list.dup + EM.run { + EM::Iterator.new(proc{list.pop || EM::Iterator::Stop}).each( proc {|num,iter| + time = get_time + items[time] ||= [] + items[time] << num + EM::Timer.new(1) {iter.next} + }, proc {EM.stop}) + } + assert_equal(10, items.keys.size) + assert_equal(original_list.to_a.sort, items.values.flatten.sort) + end + def test_concurrency_bigger_than_list_size items = {} list = [1,2,3] From 945c75dea28ec455d4d1be79f52101e5d30bbe2f Mon Sep 17 00:00:00 2001 From: Katherine Daniels Date: Wed, 30 Sep 2015 21:44:53 +0000 Subject: [PATCH 031/343] Add a method to return the number of subscribers to a channel --- lib/em/channel.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/em/channel.rb b/lib/em/channel.rb index dd94aa21d..a919adff4 100644 --- a/lib/em/channel.rb +++ b/lib/em/channel.rb @@ -17,6 +17,11 @@ def initialize @uid = 0 end + # Return the number of current subscribers. + def num_subscribers + return @subs.size + end + # Takes any arguments suitable for EM::Callback() and returns a subscriber # id for use when unsubscribing. # From f852a21401dd7c705199d0cd5c0de47cb97fe911 Mon Sep 17 00:00:00 2001 From: kdaniels Date: Thu, 1 Oct 2015 14:09:55 +0000 Subject: [PATCH 032/343] Adding a unit test for channel.num_subscribers --- tests/test_channel.rb | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/tests/test_channel.rb b/tests/test_channel.rb index bd4604af8..e9a570f98 100644 --- a/tests/test_channel.rb +++ b/tests/test_channel.rb @@ -59,4 +59,17 @@ def test_channel_reactor_thread_callback assert_equal [1,2,3], out end -end \ No newline at end of file + + def test_channel_num_subscribers + subs = 0 + EM.run do + c = EM::Channel.new + c.subscribe { |v| s = v } + c.subscribe { |v| s = v } + EM.next_tick { EM.stop } + subs = c.num_subscribers() + end + + assert_equal subs, 2 + end +end From 277bb0c36a0ecf0566a2012436b65da9f472aef1 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Sat, 17 Oct 2015 14:41:56 -0700 Subject: [PATCH 033/343] getDescriptorByFileno deprecated in JRuby 1.7.x, removed in JRuby 9000 --- lib/eventmachine.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/eventmachine.rb b/lib/eventmachine.rb index f1a490153..f4aea466b 100644 --- a/lib/eventmachine.rb +++ b/lib/eventmachine.rb @@ -754,7 +754,12 @@ def EventMachine::attach_io io, watch_mode, handler=nil, *args end if io.respond_to?(:fileno) - fd = defined?(JRuby) ? JRuby.runtime.getDescriptorByFileno(io.fileno).getChannel : io.fileno + # getDescriptorByFileno deprecated in JRuby 1.7.x, removed in JRuby 9000 + if defined?(JRuby) && JRuby.runtime.respond_to?(:getDescriptorByFileno) + fd = JRuby.runtime.getDescriptorByFileno(io.fileno).getChannel + else + fd = io.fileno + end else fd = io end From 50733781dfea23d36783eb77c340c23498c36d95 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Sun, 18 Oct 2015 06:06:10 -0700 Subject: [PATCH 034/343] Test more JRubies and ruby-head --- .travis.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 57954719e..166d7d74d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,12 +12,16 @@ rvm: - 2.0.0 - 2.1 - 2.2 + - ruby-head - rbx - - jruby + - jruby-1.7 + - jruby-9 matrix: allow_failures: + - rvm: ruby-head - rvm: rbx - - rvm: jruby + - rvm: jruby-1.7 + - rvm: jruby-9 include: - rvm: 2.0.0 os: osx From 955dc44a1208a383d6be02e5e61b55fbc05c3170 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Thu, 6 Aug 2015 23:04:59 -0700 Subject: [PATCH 035/343] Use WSAStringToAddress in lieu of inet_pton for IPv6 address detection on Windows --- ext/em.cpp | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/ext/em.cpp b/ext/em.cpp index 973c141eb..e0d859cf1 100644 --- a/ext/em.cpp +++ b/ext/em.cpp @@ -1555,7 +1555,11 @@ struct sockaddr *name2address (const char *server, int port, int *family, int *b server = "0.0.0.0"; static struct sockaddr_in in4; + static struct sockaddr_in6 in6; + memset (&in4, 0, sizeof(in4)); + memset (&in6, 0, sizeof(in6)); + if ( (in4.sin_addr.s_addr = inet_addr (server)) != INADDR_NONE) { if (family) *family = AF_INET; @@ -1567,8 +1571,6 @@ struct sockaddr *name2address (const char *server, int port, int *family, int *b } #if defined(OS_UNIX) && !defined(__CYGWIN__) - static struct sockaddr_in6 in6; - memset (&in6, 0, sizeof(in6)); if (inet_pton (AF_INET6, server, in6.sin6_addr.s6_addr) > 0) { if (family) *family = AF_INET6; @@ -1578,14 +1580,17 @@ struct sockaddr *name2address (const char *server, int port, int *family, int *b in6.sin6_port = htons (port); return (struct sockaddr*)&in6; } - #endif - - #ifdef OS_WIN32 - // TODO, must complete this branch. Windows doesn't have inet_pton. - // A possible approach is to make a getaddrinfo call with the supplied - // server address, constraining the hints to ipv6 and seeing if we - // get any addresses. - // For the time being, Ipv6 addresses aren't supported on Windows. + #elif defined(OS_WIN32) && !defined(__CYGWIN__) + int len = sizeof(in6); + if (WSAStringToAddress ((char *)server, AF_INET6, NULL, (struct sockaddr *)&in6, &len) == 0) { + if (family) + *family = AF_INET6; + if (bind_size) + *bind_size = sizeof(in6); + in6.sin6_family = AF_INET6; + in6.sin6_port = htons (port); + return (struct sockaddr*)&in6; + } #endif struct hostent *hp; From 468156869b666dac3f83cd928c249d29b7b616fd Mon Sep 17 00:00:00 2001 From: Carsten Bormann Date: Wed, 8 Feb 2012 13:30:20 +0100 Subject: [PATCH 036/343] Fix nasty TCP/IPv6 bug. --- ext/em.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/em.cpp b/ext/em.cpp index e0d859cf1..b8a32b686 100644 --- a/ext/em.cpp +++ b/ext/em.cpp @@ -1196,7 +1196,7 @@ const uintptr_t EventMachine_t::ConnectToServer (const char *bind_addr, int bind throw std::runtime_error ("invalid server or port"); int family, bind_size; - struct sockaddr bind_as, *bind_as_ptr = name2address (server, port, &family, &bind_size); + struct sockaddr_storage bind_as, *bind_as_ptr = (struct sockaddr_storage*)name2address (server, port, &family, &bind_size); if (!bind_as_ptr) { char buf [200]; snprintf (buf, sizeof(buf)-1, "unable to resolve server address: %s", strerror(errno)); @@ -1241,7 +1241,7 @@ const uintptr_t EventMachine_t::ConnectToServer (const char *bind_addr, int bind #ifdef OS_UNIX int e_reason = 0; //if (connect (sd, (sockaddr*)&pin, sizeof pin) == 0) { - if (connect (sd, &bind_as, bind_size) == 0) { + if (connect (sd, (struct sockaddr*)&bind_as, bind_size) == 0) { // This is a connect success, which Linux appears // never to give when the socket is nonblocking, // even if the connection is intramachine or to From 8843d3e6a05d0b54e5af74ab39021863d5e29585 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Mon, 28 Sep 2015 22:08:17 -0700 Subject: [PATCH 037/343] Remove Ruby 2.2.3 from AppVeyor allow_failures --- appveyor.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index d84b5fe93..4360a1a10 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -21,9 +21,5 @@ environment: - ruby_version: "21-x64" - ruby_version: "22" - ruby_version: "22-x64" -matrix: - allow_failures: - - ruby_version: "22" - - ruby_version: "22-x64" cache: - vendor From e1f9af1d208ad99fa286bfe67695625fcb425cac Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Mon, 26 Oct 2015 17:25:07 -0700 Subject: [PATCH 038/343] Restore silent-fail on unsupported EM.epoll and EM.kqueue, raise on unsupported EM.epoll=true and EM.kqueue=true Follows up on 9af9406 and #638 --- ext/rubymain.cpp | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/ext/rubymain.cpp b/ext/rubymain.cpp index 54d806f33..97c131bd6 100644 --- a/ext/rubymain.cpp +++ b/ext/rubymain.cpp @@ -997,10 +997,8 @@ t__epoll static VALUE t__epoll (VALUE self UNUSED) { - if (t__epoll_p(self) == Qfalse) { - rb_warn ("epoll is not supported on this platform"); + if (t__epoll_p(self) == Qfalse) return Qfalse; - } evma_set_epoll (1); return Qtrue; @@ -1012,8 +1010,8 @@ t__epoll_set static VALUE t__epoll_set (VALUE self, VALUE val) { - if (t__epoll_p(self) == Qfalse) - rb_warn ("epoll is not supported on this platform"); + if (t__epoll_p(self) == Qfalse && val == Qtrue) + rb_raise (EM_eUnsupported, "%s", "epoll is not supported on this platform"); evma_set_epoll (val == Qtrue ? 1 : 0); return val; @@ -1039,10 +1037,8 @@ t__kqueue static VALUE t__kqueue (VALUE self UNUSED) { - if (t__kqueue_p(self) == Qfalse) { - rb_warn ("kqueue is not supported on this platform"); + if (t__kqueue_p(self) == Qfalse) return Qfalse; - } evma_set_kqueue (1); return Qtrue; @@ -1054,8 +1050,8 @@ t__kqueue_set static VALUE t__kqueue_set (VALUE self, VALUE val) { - if (t__kqueue_p(self) == Qfalse) - rb_warn ("kqueue is not supported on this platform"); + if (t__kqueue_p(self) == Qfalse && val == Qtrue) + rb_raise (EM_eUnsupported, "%s", "kqueue is not supported on this platform"); evma_set_kqueue (val == Qtrue ? 1 : 0); return val; From 49843e6c26aad15db2080f5b44f367706463fd9f Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Tue, 27 Oct 2015 12:03:32 -0700 Subject: [PATCH 039/343] New method cleanup_machine factors out cleanup code from EM.run Call the new cleanup_machine from EM.fork_reactor to clean up the threadpool from the original reactor. Resolves #425. --- lib/eventmachine.rb | 60 ++++++++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 28 deletions(-) diff --git a/lib/eventmachine.rb b/lib/eventmachine.rb index f4aea466b..284fe393f 100644 --- a/lib/eventmachine.rb +++ b/lib/eventmachine.rb @@ -161,6 +161,7 @@ def self.run blk=nil, tail=nil, &block # Clean up reactor state so a new reactor boots up in this child. stop_event_loop release_machine + cleanup_machine @reactor_running = false end @@ -198,30 +199,8 @@ def self.run blk=nil, tail=nil, &block @tails.pop.call end - begin - release_machine - ensure - if @threadpool - @threadpool.each { |t| t.exit } - @threadpool.each do |t| - next unless t.alive? - begin - # Thread#kill! does not exist on 1.9 or rbx, and raises - # NotImplemented on jruby - t.kill! - rescue NoMethodError, NotImplementedError - t.kill - # XXX t.join here? - end - end - @threadqueue = nil - @resultqueue = nil - @threadpool = nil - @all_threads_spawned = false - end - - @next_tick_queue = [] - end + release_machine + cleanup_machine @reactor_running = false @reactor_thread = nil end @@ -266,12 +245,37 @@ def self.fork_reactor &block # Original patch by Aman Gupta. # Kernel.fork do - if self.reactor_running? - self.stop_event_loop - self.release_machine + if reactor_running? + stop_event_loop + release_machine + cleanup_machine @reactor_running = false + @reactor_thread = nil + end + run block + end + end + + # Clean up Ruby space following a release_machine + def self.cleanup_machine + @next_tick_queue = [] + if @threadpool + @threadpool.each { |t| t.exit } + @threadpool.each do |t| + next unless t.alive? + begin + # Thread#kill! does not exist on 1.9 or rbx, and raises + # NotImplemented on jruby + t.kill! + rescue NoMethodError, NotImplementedError + t.kill + # XXX t.join here? + end end - self.run block + @threadqueue = nil + @resultqueue = nil + @threadpool = nil + @all_threads_spawned = false end end From a2aa7bb58e6576e3a08bd2339fd0be61b410bfc2 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Tue, 27 Oct 2015 16:20:54 -0700 Subject: [PATCH 040/343] Add test_fork_reactor, move it with test_fork_safe to a new file --- tests/test_basic.rb | 25 --------------- tests/test_fork.rb | 75 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 25 deletions(-) create mode 100644 tests/test_fork.rb diff --git a/tests/test_basic.rb b/tests/test_basic.rb index d15175b82..043b1dada 100644 --- a/tests/test_basic.rb +++ b/tests/test_basic.rb @@ -246,31 +246,6 @@ def c.unbind assert_equal 1, num_close_scheduled end - def test_fork_safe - omit_if(jruby?) - omit_if(windows?) - omit_if(rbx?, 'Omitting test on Rubinius because it hangs for unknown reasons') - - read, write = IO.pipe - EM.run do - fork do - write.puts "forked" - EM.run do - EM.next_tick do - write.puts "EM ran" - EM.stop - end - end - end - EM.stop - end - assert_equal "forked\n", read.readline - assert_equal "EM ran\n", read.readline - ensure - read.close rescue nil - write.close rescue nil - end - def test_error_handler_idempotent # issue 185 errors = [] ticks = [] diff --git a/tests/test_fork.rb b/tests/test_fork.rb new file mode 100644 index 000000000..8b15bb586 --- /dev/null +++ b/tests/test_fork.rb @@ -0,0 +1,75 @@ +require 'em_test_helper' + +class TestFork < Test::Unit::TestCase + + def test_fork_safe + omit_if(jruby?) + omit_if(windows?) + + fork_pid = nil + read, write = IO.pipe + EM.run do + fork_pid = fork do + write.puts "forked" + EM.run do + EM.next_tick do + write.puts "EM ran" + EM.stop + end + end + end + EM.stop + end + + sleep 0.1 + begin + Timeout::timeout 1 do + assert_equal "forked\n", read.readline + assert_equal "EM ran\n", read.readline + end + rescue Timeout::Error + Process.kill 'TERM', fork_pid + flunk "Timeout waiting for next_tick in new fork reactor" + end + ensure + read.close rescue nil + write.close rescue nil + end + + def test_fork_reactor + omit_if(jruby?) + omit_if(windows?) + + fork_pid = nil + read, write = IO.pipe + EM.run do + EM.defer do + write.puts Process.pid + EM.defer do + EM.stop + end + end + fork_pid = EM.fork_reactor do + EM.defer do + write.puts Process.pid + EM.stop + end + end + end + + sleep 0.1 + begin + Timeout::timeout 1 do + assert_equal Process.pid.to_s, read.readline.chomp + assert_equal fork_pid.to_s, read.readline.chomp + end + rescue Timeout::Error + Process.kill 'TERM', fork_pid + flunk "Timeout waiting for deferred block in fork_reactor" + end + ensure + read.close rescue nil + write.close rescue nil + end + +end From f470ae870975dda5e0c8964f1a54b4e55faa9ba2 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Tue, 27 Oct 2015 17:21:09 -0700 Subject: [PATCH 041/343] Thread#exit! was removed, so just use Thread#exit in cleanup_machine Note that Thread#exit, #kill, and #terminate are synonyms, so pick one and use it both times to avoid confusing the next person. --- lib/eventmachine.rb | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/lib/eventmachine.rb b/lib/eventmachine.rb index 284fe393f..27c7c2261 100644 --- a/lib/eventmachine.rb +++ b/lib/eventmachine.rb @@ -258,25 +258,17 @@ def self.fork_reactor &block # Clean up Ruby space following a release_machine def self.cleanup_machine - @next_tick_queue = [] - if @threadpool + if @threadpool && !@threadpool.empty? + # Tell the threads to stop @threadpool.each { |t| t.exit } - @threadpool.each do |t| - next unless t.alive? - begin - # Thread#kill! does not exist on 1.9 or rbx, and raises - # NotImplemented on jruby - t.kill! - rescue NoMethodError, NotImplementedError - t.kill - # XXX t.join here? - end - end - @threadqueue = nil - @resultqueue = nil - @threadpool = nil - @all_threads_spawned = false + # Join the threads or bump the stragglers one more time + @threadpool.each { |t| t.join 0.01 || t.exit } end + @threadpool = nil + @threadqueue = nil + @resultqueue = nil + @all_threads_spawned = false + @next_tick_queue = [] end # Adds a block to call as the reactor is shutting down. From 01159e2da42dd88cca8f950a61d96170a6563346 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?I=C3=B1aki=20Baz=20Castillo?= Date: Wed, 10 Sep 2014 18:14:25 +0200 Subject: [PATCH 042/343] Fix SSL error when the server replies a TLS Alert to our ClientHello (do not announce handskale completion). In that case invoke unbind() with error==EPROTO (protocol error). --- ext/ed.cpp | 1 + ext/ssl.cpp | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/ext/ed.cpp b/ext/ed.cpp index 1e4a9cf4c..6a84a683c 100644 --- a/ext/ed.cpp +++ b/ext/ed.cpp @@ -878,6 +878,7 @@ void ConnectionDescriptor::_DispatchInboundData (const char *buffer, unsigned lo // If our SSL handshake had a problem, shut down the connection. if (s == -2) { + UnbindReasonCode = EPROTO; ScheduleClose(false); return; } diff --git a/ext/ssl.cpp b/ext/ssl.cpp index 40246e4af..cad0bca22 100644 --- a/ext/ssl.cpp +++ b/ext/ssl.cpp @@ -296,7 +296,7 @@ int SslBox_t::GetPlaintext (char *buf, int bufsize) { if (!SSL_is_init_finished (pSSL)) { int e = bIsServer ? SSL_accept (pSSL) : SSL_connect (pSSL); - if (e < 0) { + if (e != 1) { int er = SSL_get_error (pSSL, e); if (er != SSL_ERROR_WANT_READ) { // Return -1 for a nonfatal error, -2 for an error that should force the connection down. @@ -312,7 +312,7 @@ int SslBox_t::GetPlaintext (char *buf, int bufsize) if (!SSL_is_init_finished (pSSL)) { // We can get here if a browser abandons a handshake. // The user can see a warning dialog and abort the connection. - cerr << ""; + //cerr << ""; return 0; } From d5aaa383093c8a31bdbd00790f2fdb1a65191f3c Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Tue, 27 Oct 2015 23:16:26 -0700 Subject: [PATCH 043/343] Test compiler flags with the C++ compiler and add them to CXXFLAGS --- ext/extconf.rb | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/ext/extconf.rb b/ext/extconf.rb index 1e041f032..c6c9f2b61 100644 --- a/ext/extconf.rb +++ b/ext/extconf.rb @@ -181,6 +181,18 @@ def manual_ssl_config CONFIG['LDSHARED'] = "$(CXX) -shared" end +# Platform-specific time functions +if have_func('clock_gettime') + # clock_gettime is POSIX, but the monotonic clocks are not + have_const('CLOCK_MONOTONIC_RAW', 'time.h') # Linux + have_const('CLOCK_MONOTONIC', 'time.h') # Linux, Solaris, BSDs +else + have_func('gethrtime') # Older Solaris and HP-UX +end + +# Hack so that try_link will test with a C++ compiler instead of a C compiler +TRY_LINK.sub!('$(CC)', '$(CXX)') + # This is our wishlist. We use whichever flags work on the host. # In the future, add -Werror to make sure all warnings are resolved. # deprecated-declarations are used in OS X OpenSSL @@ -197,23 +209,11 @@ def manual_ssl_config ).select do |flag| try_link('int main() {return 0;}', flag) end.each do |flag| - $CFLAGS << ' ' << flag - $CPPFLAGS << ' ' << flag + CONFIG['CXXFLAGS'] << ' ' << flag end -puts "CFLAGS=#{$CFLAGS}" -puts "CPPFLAGS=#{$CPPFLAGS}" +puts "CXXFLAGS=#{CONFIG['CXXFLAGS']}" -# Platform-specific time functions -if have_func('clock_gettime') - # clock_gettime is POSIX, but the monotonic clocks are not - have_const('CLOCK_MONOTONIC_RAW', 'time.h') # Linux - have_const('CLOCK_MONOTONIC', 'time.h') # Linux, Solaris, BSDs -else - have_func('gethrtime') # Older Solaris and HP-UX -end - -# solaris c++ compiler doesn't have make_pair() -TRY_LINK.sub!('$(CC)', '$(CXX)') +# Solaris C++ compiler doesn't have make_pair() add_define 'HAVE_MAKE_PAIR' if try_link(< using namespace std; From 6e2076c8e44a4063d98260f9f427977bb04d28f2 Mon Sep 17 00:00:00 2001 From: Matt Palmer Date: Wed, 11 Feb 2015 16:14:20 +1100 Subject: [PATCH 044/343] [PATCH] Make name2address take into account IPv6 addresses --- ext/em.cpp | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/ext/em.cpp b/ext/em.cpp index b8a32b686..9aaa3a1eb 100644 --- a/ext/em.cpp +++ b/ext/em.cpp @@ -20,6 +20,10 @@ See the file COPYING for complete licensing information. // THIS ENTIRE FILE WILL EVENTUALLY BE FOR UNIX BUILDS ONLY. //#ifdef OS_UNIX +#include +#include +#include + #include "project.h" /* The numer of max outstanding timers was once a const enum defined in em.h. @@ -1593,17 +1597,24 @@ struct sockaddr *name2address (const char *server, int port, int *family, int *b } #endif - struct hostent *hp; - hp = gethostbyname ((char*)server); // Windows requires the cast. - if (hp) { - in4.sin_addr.s_addr = ((in_addr*)(hp->h_addr))->s_addr; + + struct addrinfo *ai; + if (getaddrinfo (server, NULL, NULL, &ai) == 0) { if (family) - *family = AF_INET; + *family = ai->ai_family; if (bind_size) - *bind_size = sizeof(in4); - in4.sin_family = AF_INET; - in4.sin_port = htons (port); - return (struct sockaddr*)&in4; + *bind_size = ai->ai_addrlen; + + switch (ai->ai_family) { + case AF_INET: + memcpy(&in4, ai->ai_addr, ai->ai_addrlen); + in4.sin_port = htons(port); + return (struct sockaddr*)&in4; + case AF_INET6: + memcpy(&in6, ai->ai_addr, ai->ai_addrlen); + in6.sin6_port = htons(port); + return (struct sockaddr*)&in6; + } } return NULL; From c3abcda1dc8a9494642dc47d6e79130698c358ec Mon Sep 17 00:00:00 2001 From: Matt Palmer Date: Thu, 12 Feb 2015 10:52:16 +1100 Subject: [PATCH 045/343] [PATCH] Free struct addrinfo after use Missed this in the initial patch. Also avoid a compilation error on Cygwin due to not having IPv6 supported there. --- ext/em.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ext/em.cpp b/ext/em.cpp index 9aaa3a1eb..773a24fbc 100644 --- a/ext/em.cpp +++ b/ext/em.cpp @@ -1609,12 +1609,15 @@ struct sockaddr *name2address (const char *server, int port, int *family, int *b case AF_INET: memcpy(&in4, ai->ai_addr, ai->ai_addrlen); in4.sin_port = htons(port); - return (struct sockaddr*)&in4; +#ifndef CYGWIN case AF_INET6: memcpy(&in6, ai->ai_addr, ai->ai_addrlen); in6.sin6_port = htons(port); - return (struct sockaddr*)&in6; +#endif } + + freeaddrinfo(ai); + return (struct sockaddr*)&in4; } return NULL; From e4a87bc0ea0cef2f17b0598cebae5574d049e64e Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Thu, 6 Aug 2015 23:04:59 -0700 Subject: [PATCH 046/343] Use WSAStringToAddress in lieu of inet_pton for IPv6 address detection on Windows --- ext/em.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/ext/em.cpp b/ext/em.cpp index 773a24fbc..fa6b14332 100644 --- a/ext/em.cpp +++ b/ext/em.cpp @@ -1597,7 +1597,6 @@ struct sockaddr *name2address (const char *server, int port, int *family, int *b } #endif - struct addrinfo *ai; if (getaddrinfo (server, NULL, NULL, &ai) == 0) { if (family) From 53d2c01bb61cc9aa9347c250606d578e44e62638 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Thu, 6 Aug 2015 23:06:48 -0700 Subject: [PATCH 047/343] Provide AF_UNSPEC hint to getaddrinfo and some cleanups --- ext/em.cpp | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/ext/em.cpp b/ext/em.cpp index fa6b14332..55de0c103 100644 --- a/ext/em.cpp +++ b/ext/em.cpp @@ -20,10 +20,6 @@ See the file COPYING for complete licensing information. // THIS ENTIRE FILE WILL EVENTUALLY BE FOR UNIX BUILDS ONLY. //#ifdef OS_UNIX -#include -#include -#include - #include "project.h" /* The numer of max outstanding timers was once a const enum defined in em.h. @@ -1597,26 +1593,37 @@ struct sockaddr *name2address (const char *server, int port, int *family, int *b } #endif + int ai_family; struct addrinfo *ai; - if (getaddrinfo (server, NULL, NULL, &ai) == 0) { + struct addrinfo hints; + memset (&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + if (getaddrinfo (server, NULL, &hints, &ai) == 0) { if (family) *family = ai->ai_family; if (bind_size) *bind_size = ai->ai_addrlen; - switch (ai->ai_family) { + ai_family = ai->ai_family; + switch (ai_family) { case AF_INET: - memcpy(&in4, ai->ai_addr, ai->ai_addrlen); + memcpy (&in4, ai->ai_addr, ai->ai_addrlen); in4.sin_port = htons(port); -#ifndef CYGWIN + #ifndef __CYGWIN__ case AF_INET6: - memcpy(&in6, ai->ai_addr, ai->ai_addrlen); + memcpy (&in6, ai->ai_addr, ai->ai_addrlen); in6.sin6_port = htons(port); -#endif + #endif } freeaddrinfo(ai); - return (struct sockaddr*)&in4; + + switch (ai_family) { + case AF_INET: + return (struct sockaddr*)&in4; + case AF_INET6: + return (struct sockaddr*)&in6; + } } return NULL; From 840d350dd30bae3f43def0f9029e89e44bda877c Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Wed, 21 Oct 2015 20:46:53 -0700 Subject: [PATCH 048/343] Switch name2address to use getaddrinfo for everything, with some auto_ptr --- ext/em.cpp | 100 +++++++++++--------------------------------------- ext/project.h | 1 + 2 files changed, 23 insertions(+), 78 deletions(-) diff --git a/ext/em.cpp b/ext/em.cpp index 55de0c103..7ef4e66aa 100644 --- a/ext/em.cpp +++ b/ext/em.cpp @@ -34,7 +34,7 @@ static unsigned int SimultaneousAcceptCount = 10; /* Internal helper to convert strings to internet addresses. IPv6-aware. - * Not reentrant or threadsafe, optimized for speed. + * Must free the return value. */ static struct sockaddr *name2address (const char *server, int port, int *family, int *bind_size); @@ -1196,13 +1196,12 @@ const uintptr_t EventMachine_t::ConnectToServer (const char *bind_addr, int bind throw std::runtime_error ("invalid server or port"); int family, bind_size; - struct sockaddr_storage bind_as, *bind_as_ptr = (struct sockaddr_storage*)name2address (server, port, &family, &bind_size); - if (!bind_as_ptr) { + const std::auto_ptr bind_as (name2address (server, port, &family, &bind_size)); + if (!bind_as.get()) { char buf [200]; snprintf (buf, sizeof(buf)-1, "unable to resolve server address: %s", strerror(errno)); throw std::runtime_error (buf); } - bind_as = *bind_as_ptr; // copy because name2address points to a static SOCKET sd = EmSocket (family, SOCK_STREAM, 0); if (sd == INVALID_SOCKET) { @@ -1225,12 +1224,12 @@ const uintptr_t EventMachine_t::ConnectToServer (const char *bind_addr, int bind if (bind_addr) { int bind_to_size, bind_to_family; - struct sockaddr *bind_to = name2address (bind_addr, bind_port, &bind_to_family, &bind_to_size); - if (!bind_to) { + const std::auto_ptr bind_to (name2address (bind_addr, bind_port, &bind_to_family, &bind_to_size)); + if (!bind_to.get()) { close (sd); throw std::runtime_error ("invalid bind address"); } - if (bind (sd, bind_to, bind_to_size) < 0) { + if (bind (sd, bind_to.get(), bind_to_size) < 0) { close (sd); throw std::runtime_error ("couldn't bind to address"); } @@ -1240,8 +1239,7 @@ const uintptr_t EventMachine_t::ConnectToServer (const char *bind_addr, int bind #ifdef OS_UNIX int e_reason = 0; - //if (connect (sd, (sockaddr*)&pin, sizeof pin) == 0) { - if (connect (sd, (struct sockaddr*)&bind_as, bind_size) == 0) { + if (connect (sd, bind_as.get(), bind_size) == 0) { // This is a connect success, which Linux appears // never to give when the socket is nonblocking, // even if the connection is intramachine or to @@ -1320,8 +1318,7 @@ const uintptr_t EventMachine_t::ConnectToServer (const char *bind_addr, int bind #endif #ifdef OS_WIN32 - //if (connect (sd, (sockaddr*)&pin, sizeof pin) == 0) { - if (connect (sd, &bind_as, bind_size) == 0) { + if (connect (sd, bind_as.get(), bind_size) == 0) { // This is a connect success, which Windows appears // never to give when the socket is nonblocking, // even if the connection is intramachine or to @@ -1547,83 +1544,31 @@ name2address struct sockaddr *name2address (const char *server, int port, int *family, int *bind_size) { - // THIS IS NOT RE-ENTRANT OR THREADSAFE. Optimize for speed. - // Check the more-common cases first. - // Return NULL if no resolution. - if (!server || !*server) server = "0.0.0.0"; - static struct sockaddr_in in4; - static struct sockaddr_in6 in6; - - memset (&in4, 0, sizeof(in4)); - memset (&in6, 0, sizeof(in6)); - - if ( (in4.sin_addr.s_addr = inet_addr (server)) != INADDR_NONE) { - if (family) - *family = AF_INET; - if (bind_size) - *bind_size = sizeof(in4); - in4.sin_family = AF_INET; - in4.sin_port = htons (port); - return (struct sockaddr*)&in4; - } - - #if defined(OS_UNIX) && !defined(__CYGWIN__) - if (inet_pton (AF_INET6, server, in6.sin6_addr.s6_addr) > 0) { - if (family) - *family = AF_INET6; - if (bind_size) - *bind_size = sizeof(in6); - in6.sin6_family = AF_INET6; - in6.sin6_port = htons (port); - return (struct sockaddr*)&in6; - } - #elif defined(OS_WIN32) && !defined(__CYGWIN__) - int len = sizeof(in6); - if (WSAStringToAddress ((char *)server, AF_INET6, NULL, (struct sockaddr *)&in6, &len) == 0) { - if (family) - *family = AF_INET6; - if (bind_size) - *bind_size = sizeof(in6); - in6.sin6_family = AF_INET6; - in6.sin6_port = htons (port); - return (struct sockaddr*)&in6; - } - #endif - - int ai_family; struct addrinfo *ai; struct addrinfo hints; memset (&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; - if (getaddrinfo (server, NULL, &hints, &ai) == 0) { + hints.ai_flags = AI_NUMERICSERV; + + char portstr[12]; + snprintf(portstr, sizeof(portstr), "%u", port); + + if (getaddrinfo (server, portstr, &hints, &ai) == 0) { if (family) *family = ai->ai_family; if (bind_size) *bind_size = ai->ai_addrlen; - ai_family = ai->ai_family; - switch (ai_family) { - case AF_INET: - memcpy (&in4, ai->ai_addr, ai->ai_addrlen); - in4.sin_port = htons(port); - #ifndef __CYGWIN__ - case AF_INET6: - memcpy (&in6, ai->ai_addr, ai->ai_addrlen); - in6.sin6_port = htons(port); - #endif - } + // Use "new" so the caller must "delete" or be an auto_ptr + struct sockaddr *addr = (struct sockaddr *)new struct sockaddr_storage; + assert (ai->ai_addrlen <= sizeof(struct sockaddr_storage)); + memcpy (addr, ai->ai_addr, ai->ai_addrlen); freeaddrinfo(ai); - - switch (ai_family) { - case AF_INET: - return (struct sockaddr*)&in4; - case AF_INET6: - return (struct sockaddr*)&in6; - } + return addr; } return NULL; @@ -1644,8 +1589,8 @@ const uintptr_t EventMachine_t::CreateTcpServer (const char *server, int port) int family, bind_size; - struct sockaddr *bind_here = name2address (server, port, &family, &bind_size); - if (!bind_here) + const std::auto_ptr bind_here (name2address (server, port, &family, &bind_size)); + if (!bind_here.get()) return 0; //struct sockaddr_in sin; @@ -1673,8 +1618,7 @@ const uintptr_t EventMachine_t::CreateTcpServer (const char *server, int port) } - //if (bind (sd_accept, (struct sockaddr*)&sin, sizeof(sin))) { - if (bind (sd_accept, bind_here, bind_size)) { + if (bind (sd_accept, bind_here.get(), bind_size)) { //__warning ("binding failed"); goto fail; } diff --git a/ext/project.h b/ext/project.h index d105b67aa..f1780f8ba 100644 --- a/ext/project.h +++ b/ext/project.h @@ -30,6 +30,7 @@ See the file COPYING for complete licensing information. #include #include #include +#include #ifdef OS_UNIX From 9308e9145789297e56ac1735b36166ca99914100 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Mon, 26 Oct 2015 05:18:39 -0700 Subject: [PATCH 049/343] Support for IPv6 UDP --- ext/ed.cpp | 27 ++++++++------------------ ext/ed.h | 6 +++--- ext/em.cpp | 49 +++++++++++------------------------------------- ext/em.h | 5 +++++ ext/rubymain.cpp | 2 ++ 5 files changed, 29 insertions(+), 60 deletions(-) diff --git a/ext/ed.cpp b/ext/ed.cpp index 6a84a683c..7d2670562 100644 --- a/ext/ed.cpp +++ b/ext/ed.cpp @@ -1725,7 +1725,8 @@ void DatagramDescriptor::Write() OutboundPage *op = &(OutboundPages[0]); // The nasty cast to (char*) is needed because Windows is brain-dead. - int s = sendto (sd, (char*)op->Buffer, op->Length, 0, (struct sockaddr*)&(op->From), sizeof(op->From)); + int s = sendto (sd, (char*)op->Buffer, op->Length, 0, (struct sockaddr*)&(op->From), + (op->From.sin6_family == AF_INET6 ? sizeof (struct sockaddr_in6) : sizeof (struct sockaddr_in))); #ifdef OS_WIN32 int e = WSAGetLastError(); #else @@ -1837,23 +1838,10 @@ int DatagramDescriptor::SendOutboundDatagram (const char *data, unsigned long le if (!address || !*address || !port) return 0; - sockaddr_in pin; - unsigned long HostAddr; - - HostAddr = inet_addr (address); - if (HostAddr == INADDR_NONE) { - // The nasty cast to (char*) is because Windows is brain-dead. - hostent *hp = gethostbyname ((char*)address); - if (!hp) - return 0; - HostAddr = ((in_addr*)(hp->h_addr))->s_addr; - } - - memset (&pin, 0, sizeof(pin)); - pin.sin_family = AF_INET; - pin.sin_addr.s_addr = HostAddr; - pin.sin_port = htons (port); - + int family, addr_size; + struct sockaddr *addr_here = EventMachine_t::name2address (address, port, &family, &addr_size); + if (!addr_here) + return -1; if (!data && (length > 0)) throw std::runtime_error ("bad outbound data"); @@ -1862,8 +1850,9 @@ int DatagramDescriptor::SendOutboundDatagram (const char *data, unsigned long le throw std::runtime_error ("no allocation for outbound data"); memcpy (buffer, data, length); buffer [length] = 0; - OutboundPages.push_back (OutboundPage (buffer, length, pin)); + OutboundPages.push_back (OutboundPage (buffer, length, *(struct sockaddr_in6*)addr_here)); OutboundDataSize += length; + delete addr_here; #ifdef HAVE_EPOLL EpollEvent.events = (EPOLLIN | EPOLLOUT); diff --git a/ext/ed.h b/ext/ed.h index 367a1e8db..6b404e1cd 100644 --- a/ext/ed.h +++ b/ext/ed.h @@ -299,18 +299,18 @@ class DatagramDescriptor: public EventableDescriptor protected: struct OutboundPage { - OutboundPage (const char *b, int l, struct sockaddr_in f, int o=0): Buffer(b), Length(l), Offset(o), From(f) {} + OutboundPage (const char *b, int l, struct sockaddr_in6 f, int o=0): Buffer(b), Length(l), Offset(o), From(f) {} void Free() {if (Buffer) free (const_cast(Buffer)); } const char *Buffer; int Length; int Offset; - struct sockaddr_in From; + struct sockaddr_in6 From; }; deque OutboundPages; int OutboundDataSize; - struct sockaddr_in ReturnAddress; + struct sockaddr_in6 ReturnAddress; }; diff --git a/ext/em.cpp b/ext/em.cpp index 7ef4e66aa..2f551d189 100644 --- a/ext/em.cpp +++ b/ext/em.cpp @@ -32,12 +32,6 @@ static unsigned int MaxOutstandingTimers = 100000; */ static unsigned int SimultaneousAcceptCount = 10; - -/* Internal helper to convert strings to internet addresses. IPv6-aware. - * Must free the return value. - */ -static struct sockaddr *name2address (const char *server, int port, int *family, int *bind_size); - /* Internal helper to create a socket with SOCK_CLOEXEC set, and fall * back to fcntl'ing it if the headers/runtime don't support it. */ @@ -1542,7 +1536,7 @@ int EventMachine_t::DetachFD (EventableDescriptor *ed) name2address ************/ -struct sockaddr *name2address (const char *server, int port, int *family, int *bind_size) +struct sockaddr *EventMachine_t::name2address (const char *server, int port, int *family, int *bind_size) { if (!server || !*server) server = "0.0.0.0"; @@ -1593,8 +1587,6 @@ const uintptr_t EventMachine_t::CreateTcpServer (const char *server, int port) if (!bind_here.get()) return 0; - //struct sockaddr_in sin; - SOCKET sd_accept = EmSocket (family, SOCK_STREAM, 0); if (sd_accept == INVALID_SOCKET) { goto fail; @@ -1645,40 +1637,21 @@ const uintptr_t EventMachine_t::OpenDatagramSocket (const char *address, int por { uintptr_t output_binding = 0; - SOCKET sd = EmSocket (AF_INET, SOCK_DGRAM, 0); + int family, bind_size; + const std::auto_ptr bind_here (name2address (address, port, &family, &bind_size)); + if (!bind_here.get()) + return 0; + + // from here on, early returns must close the socket! + SOCKET sd = EmSocket (family, SOCK_DGRAM, 0); if (sd == INVALID_SOCKET) goto fail; - // from here on, early returns must close the socket! - - - struct sockaddr_in sin; - memset (&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - sin.sin_port = htons (port); - - - if (address && *address) { - sin.sin_addr.s_addr = inet_addr (address); - if (sin.sin_addr.s_addr == INADDR_NONE) { - hostent *hp = gethostbyname ((char*)address); // Windows requires the cast. - if (hp == NULL) - goto fail; - sin.sin_addr.s_addr = ((in_addr*)(hp->h_addr))->s_addr; - } - } - else - sin.sin_addr.s_addr = htonl (INADDR_ANY); - // Set the new socket nonblocking. - { - if (!SetSocketNonblocking (sd)) - //int val = fcntl (sd, F_GETFL, 0); - //if (fcntl (sd, F_SETFL, val | O_NONBLOCK) == -1) - goto fail; - } + if (!SetSocketNonblocking (sd)) + goto fail; - if (bind (sd, (struct sockaddr*)&sin, sizeof(sin)) != 0) + if (bind (sd, bind_here.get(), bind_size) != 0) goto fail; { // Looking good. diff --git a/ext/em.h b/ext/em.h index ec239d913..051cd52ac 100644 --- a/ext/em.h +++ b/ext/em.h @@ -200,6 +200,11 @@ class EventMachine_t Poller_t GetPoller() { return Poller; } + /* Helper to convert strings to internet addresses. IPv6-aware. + * Must delete the return value (or use an auto_ptr). + */ + static struct sockaddr *name2address(const char *server, int port, int *family, int *bind_size); + private: void _RunTimers(); void _UpdateTime(); diff --git a/ext/rubymain.cpp b/ext/rubymain.cpp index 97c131bd6..be24d9f78 100644 --- a/ext/rubymain.cpp +++ b/ext/rubymain.cpp @@ -525,6 +525,8 @@ t_send_datagram static VALUE t_send_datagram (VALUE self UNUSED, VALUE signature, VALUE data, VALUE data_length, VALUE address, VALUE port) { int b = evma_send_datagram (NUM2BSIG (signature), StringValuePtr (data), FIX2INT (data_length), StringValueCStr(address), FIX2INT(port)); + if (b < 0) + rb_raise (EM_eConnectionError, "%s", "error in sending datagram"); // FIXME: this could be more specific. return INT2NUM (b); } From 0d55e5e31877f4761575bb82c5761a2700431ff7 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Mon, 26 Oct 2015 06:17:39 -0700 Subject: [PATCH 050/343] Refactor name2address one more time to take a sockaddr argument and return a bool --- ext/ed.cpp | 9 ++++---- ext/em.cpp | 58 +++++++++++++++++++++++---------------------------- ext/em.h | 5 +---- ext/project.h | 1 - 4 files changed, 31 insertions(+), 42 deletions(-) diff --git a/ext/ed.cpp b/ext/ed.cpp index 7d2670562..f05b8d79e 100644 --- a/ext/ed.cpp +++ b/ext/ed.cpp @@ -1838,9 +1838,9 @@ int DatagramDescriptor::SendOutboundDatagram (const char *data, unsigned long le if (!address || !*address || !port) return 0; - int family, addr_size; - struct sockaddr *addr_here = EventMachine_t::name2address (address, port, &family, &addr_size); - if (!addr_here) + struct sockaddr_in6 addr_here; + size_t addr_here_len = sizeof addr_here; + if (!EventMachine_t::name2address (address, port, (struct sockaddr *)&addr_here, &addr_here_len)) return -1; if (!data && (length > 0)) @@ -1850,9 +1850,8 @@ int DatagramDescriptor::SendOutboundDatagram (const char *data, unsigned long le throw std::runtime_error ("no allocation for outbound data"); memcpy (buffer, data, length); buffer [length] = 0; - OutboundPages.push_back (OutboundPage (buffer, length, *(struct sockaddr_in6*)addr_here)); + OutboundPages.push_back (OutboundPage (buffer, length, addr_here)); OutboundDataSize += length; - delete addr_here; #ifdef HAVE_EPOLL EpollEvent.events = (EPOLLIN | EPOLLOUT); diff --git a/ext/em.cpp b/ext/em.cpp index 2f551d189..3eb1beb55 100644 --- a/ext/em.cpp +++ b/ext/em.cpp @@ -1189,15 +1189,15 @@ const uintptr_t EventMachine_t::ConnectToServer (const char *bind_addr, int bind if (!server || !*server || !port) throw std::runtime_error ("invalid server or port"); - int family, bind_size; - const std::auto_ptr bind_as (name2address (server, port, &family, &bind_size)); - if (!bind_as.get()) { + struct sockaddr_storage bind_as; + size_t bind_as_len = sizeof bind_as; + if (!name2address (server, port, (struct sockaddr *)&bind_as, &bind_as_len)) { char buf [200]; snprintf (buf, sizeof(buf)-1, "unable to resolve server address: %s", strerror(errno)); throw std::runtime_error (buf); } - SOCKET sd = EmSocket (family, SOCK_STREAM, 0); + SOCKET sd = EmSocket (bind_as.ss_family, SOCK_STREAM, 0); if (sd == INVALID_SOCKET) { char buf [200]; snprintf (buf, sizeof(buf)-1, "unable to create new socket: %s", strerror(errno)); @@ -1217,13 +1217,13 @@ const uintptr_t EventMachine_t::ConnectToServer (const char *bind_addr, int bind setsockopt (sd, SOL_SOCKET, SO_REUSEADDR, (char*) &one, sizeof(one)); if (bind_addr) { - int bind_to_size, bind_to_family; - const std::auto_ptr bind_to (name2address (bind_addr, bind_port, &bind_to_family, &bind_to_size)); - if (!bind_to.get()) { + struct sockaddr_storage bind_to; + size_t bind_to_len = sizeof bind_to; + if (!name2address (bind_addr, bind_port, (struct sockaddr *)&bind_to, &bind_to_len)) { close (sd); throw std::runtime_error ("invalid bind address"); } - if (bind (sd, bind_to.get(), bind_to_size) < 0) { + if (bind (sd, (struct sockaddr *)&bind_to, bind_to_len) < 0) { close (sd); throw std::runtime_error ("couldn't bind to address"); } @@ -1233,7 +1233,7 @@ const uintptr_t EventMachine_t::ConnectToServer (const char *bind_addr, int bind #ifdef OS_UNIX int e_reason = 0; - if (connect (sd, bind_as.get(), bind_size) == 0) { + if (connect (sd, (struct sockaddr *)&bind_as, bind_as_len) == 0) { // This is a connect success, which Linux appears // never to give when the socket is nonblocking, // even if the connection is intramachine or to @@ -1312,7 +1312,7 @@ const uintptr_t EventMachine_t::ConnectToServer (const char *bind_addr, int bind #endif #ifdef OS_WIN32 - if (connect (sd, bind_as.get(), bind_size) == 0) { + if (connect (sd, (struct sockaddr *)&bind_as, bind_as_len) == 0) { // This is a connect success, which Windows appears // never to give when the socket is nonblocking, // even if the connection is intramachine or to @@ -1536,7 +1536,7 @@ int EventMachine_t::DetachFD (EventableDescriptor *ed) name2address ************/ -struct sockaddr *EventMachine_t::name2address (const char *server, int port, int *family, int *bind_size) +bool EventMachine_t::name2address (const char *server, int port, struct sockaddr *addr, size_t *addr_len) { if (!server || !*server) server = "0.0.0.0"; @@ -1545,27 +1545,21 @@ struct sockaddr *EventMachine_t::name2address (const char *server, int port, int struct addrinfo hints; memset (&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; - hints.ai_flags = AI_NUMERICSERV; + hints.ai_flags = AI_NUMERICSERV | AI_ADDRCONFIG; char portstr[12]; snprintf(portstr, sizeof(portstr), "%u", port); if (getaddrinfo (server, portstr, &hints, &ai) == 0) { - if (family) - *family = ai->ai_family; - if (bind_size) - *bind_size = ai->ai_addrlen; - - // Use "new" so the caller must "delete" or be an auto_ptr - struct sockaddr *addr = (struct sockaddr *)new struct sockaddr_storage; - assert (ai->ai_addrlen <= sizeof(struct sockaddr_storage)); + assert (ai->ai_addrlen <= *addr_len); memcpy (addr, ai->ai_addr, ai->ai_addrlen); + *addr_len = ai->ai_addrlen; freeaddrinfo(ai); - return addr; + return true; } - return NULL; + return false; } @@ -1582,12 +1576,12 @@ const uintptr_t EventMachine_t::CreateTcpServer (const char *server, int port) */ - int family, bind_size; - const std::auto_ptr bind_here (name2address (server, port, &family, &bind_size)); - if (!bind_here.get()) + struct sockaddr_storage bind_here; + size_t bind_here_len = sizeof bind_here; + if (!name2address (server, port, (struct sockaddr *)&bind_here, &bind_here_len)) return 0; - SOCKET sd_accept = EmSocket (family, SOCK_STREAM, 0); + SOCKET sd_accept = EmSocket (bind_here.ss_family, SOCK_STREAM, 0); if (sd_accept == INVALID_SOCKET) { goto fail; } @@ -1610,7 +1604,7 @@ const uintptr_t EventMachine_t::CreateTcpServer (const char *server, int port) } - if (bind (sd_accept, bind_here.get(), bind_size)) { + if (bind (sd_accept, (struct sockaddr *)&bind_here, bind_here_len)) { //__warning ("binding failed"); goto fail; } @@ -1637,13 +1631,13 @@ const uintptr_t EventMachine_t::OpenDatagramSocket (const char *address, int por { uintptr_t output_binding = 0; - int family, bind_size; - const std::auto_ptr bind_here (name2address (address, port, &family, &bind_size)); - if (!bind_here.get()) + struct sockaddr_storage bind_here; + size_t bind_here_len = sizeof bind_here; + if (!name2address (address, port, (struct sockaddr *)&bind_here, &bind_here_len)) return 0; // from here on, early returns must close the socket! - SOCKET sd = EmSocket (family, SOCK_DGRAM, 0); + SOCKET sd = EmSocket (bind_here.ss_family, SOCK_DGRAM, 0); if (sd == INVALID_SOCKET) goto fail; @@ -1651,7 +1645,7 @@ const uintptr_t EventMachine_t::OpenDatagramSocket (const char *address, int por if (!SetSocketNonblocking (sd)) goto fail; - if (bind (sd, bind_here.get(), bind_size) != 0) + if (bind (sd, (struct sockaddr *)&bind_here, bind_here_len) != 0) goto fail; { // Looking good. diff --git a/ext/em.h b/ext/em.h index 051cd52ac..80cc9710f 100644 --- a/ext/em.h +++ b/ext/em.h @@ -200,10 +200,7 @@ class EventMachine_t Poller_t GetPoller() { return Poller; } - /* Helper to convert strings to internet addresses. IPv6-aware. - * Must delete the return value (or use an auto_ptr). - */ - static struct sockaddr *name2address(const char *server, int port, int *family, int *bind_size); + static bool name2address (const char *server, int port, struct sockaddr *addr, size_t *addr_len); private: void _RunTimers(); diff --git a/ext/project.h b/ext/project.h index f1780f8ba..d105b67aa 100644 --- a/ext/project.h +++ b/ext/project.h @@ -30,7 +30,6 @@ See the file COPYING for complete licensing information. #include #include #include -#include #ifdef OS_UNIX From edbd6394ed70c9a64b0c8e211460aee23412c6b6 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Mon, 26 Oct 2015 06:19:20 -0700 Subject: [PATCH 051/343] Copy over IPv6 tests from EventMachine LE --- tests/em_test_helper.rb | 79 ++++++++++++++++++++++++ tests/test_ipv4.rb | 125 ++++++++++++++++++++++++++++++++++++++ tests/test_ipv6.rb | 131 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 335 insertions(+) create mode 100644 tests/test_ipv4.rb create mode 100644 tests/test_ipv6.rb diff --git a/tests/em_test_helper.rb b/tests/em_test_helper.rb index b68f347a7..6b6470fc4 100644 --- a/tests/em_test_helper.rb +++ b/tests/em_test_helper.rb @@ -31,6 +31,61 @@ def next_port @@port end + # Returns true if the host have a localhost 127.0.0.1 IPv4. + def self.local_ipv4? + return @@has_local_ipv4 if defined?(@@has_local_ipv4) + begin + get_my_ipv4_address "127.0.0.1" + @@has_local_ipv4 = true + rescue + @@has_local_ipv4 = false + end + end + + # Returns true if the host have a public IPv4 and stores it in + # @@public_ipv4. + def self.public_ipv4? + return @@has_public_ipv4 if defined?(@@has_public_ipv4) + begin + @@public_ipv4 = get_my_ipv4_address "1.2.3.4" + @@has_public_ipv4 = true + rescue + @@has_public_ipv4 = false + end + end + + # Returns true if the host have a localhost ::1 IPv6. + def self.local_ipv6? + return @@has_local_ipv6 if defined?(@@has_local_ipv6) + begin + get_my_ipv6_address "::1" + @@has_local_ipv6 = true + rescue + @@has_local_ipv6 = false + end + end + + # Returns true if the host have a public IPv6 and stores it in + # @@public_ipv6. + def self.public_ipv6? + return @@has_public_ipv6 if defined?(@@has_public_ipv6) + begin + @@public_ipv6 = get_my_ipv6_address "2001::1" + @@has_public_ipv6 = true + rescue + @@has_public_ipv6 = false + end + end + + # Returns an array with the localhost addresses (IPv4 and/or IPv6). + def local_ips + return @@local_ips if defined?(@@local_ips) + @@local_ips = [] + @@local_ips << "127.0.0.1" if self.class.local_ipv4? + @@local_ips << "::1" if self.class.local_ipv6? + @@local_ips + end + def exception_class jruby? ? NativeException : RuntimeError end @@ -69,4 +124,28 @@ def silent $VERBOSE = backup end end + + + private + + def self.get_my_ipv4_address ip + orig, Socket.do_not_reverse_lookup = Socket.do_not_reverse_lookup, true # turn off reverse DNS resolution temporarily + UDPSocket.open(Socket::AF_INET) do |s| + s.connect ip, 1 + s.addr.last + end + ensure + Socket.do_not_reverse_lookup = orig + end + + def self.get_my_ipv6_address ip + orig, Socket.do_not_reverse_lookup = Socket.do_not_reverse_lookup, true # turn off reverse DNS resolution temporarily + UDPSocket.open(Socket::AF_INET6) do |s| + s.connect ip, 1 + s.addr.last + end + ensure + Socket.do_not_reverse_lookup = orig + end + end diff --git a/tests/test_ipv4.rb b/tests/test_ipv4.rb new file mode 100644 index 000000000..e132fd359 --- /dev/null +++ b/tests/test_ipv4.rb @@ -0,0 +1,125 @@ +require 'em_test_helper' +require 'socket' + +class TestIPv4 < Test::Unit::TestCase + + if Test::Unit::TestCase.public_ipv4? + + # Tries to connect to www.google.com port 80 via TCP. + # Timeout in 2 seconds. + def test_ipv4_tcp_client + conn = nil + setup_timeout(2) + + EM.run do + conn = EM::connect("www.google.com", 80) do |c| + def c.connected + @connected + end + + def c.connection_completed + @connected = true + EM.stop + end + end + end + + assert conn.connected + end + + # Runs a TCP server in the local IPv4 address, connects to it and sends a specific data. + # Timeout in 2 seconds. + def test_ipv4_tcp_local_server + @@received_data = nil + @local_port = next_port + setup_timeout(2) + + EM.run do + EM::start_server(@@public_ipv4, @local_port) do |s| + def s.receive_data data + @@received_data = data + EM.stop + end + end + + EM::connect(@@public_ipv4, @local_port) do |c| + c.send_data "ipv4/tcp" + end + end + + assert_equal "ipv4/tcp", @@received_data + end + + # Runs a UDP server in the local IPv4 address, connects to it and sends a specific data. + # Timeout in 2 seconds. + def test_ipv4_udp_local_server + @@received_data = nil + @local_port = next_port + setup_timeout(2) + + EM.run do + EM::open_datagram_socket(@@public_ipv4, @local_port) do |s| + def s.receive_data data + @@received_data = data + EM.stop + end + end + + EM::open_datagram_socket(@@public_ipv4, next_port) do |c| + c.send_datagram "ipv4/udp", @@public_ipv4, @local_port + end + end + + assert_equal "ipv4/udp", @@received_data + end + + # Try to connect via TCP to an invalid IPv4. EM.connect should raise + # EM::ConnectionError. + def test_tcp_connect_to_invalid_ipv4 + invalid_ipv4 = "9.9:9" + + EM.run do + begin + error = nil + EM.connect(invalid_ipv4, 1234) + rescue => e + error = e + ensure + EM.stop + assert_equal EM::ConnectionError, (error && error.class) + end + end + end + + # Try to send a UDP datagram to an invalid IPv4. EM.send_datagram should raise + # EM::ConnectionError. + def test_udp_send_datagram_to_invalid_ipv4 + invalid_ipv4 = "9.9:9" + + EM.run do + begin + error = nil + EM.open_datagram_socket(@@public_ipv4, next_port) do |c| + c.send_datagram "hello", invalid_ipv4, 1234 + end + rescue => e + error = e + ensure + EM.stop + assert_equal EM::ConnectionError, (error && error.class) + end + end + end + + + else + warn "no IPv4 in this host, skipping tests in #{__FILE__}" + + # Because some rubies will complain if a TestCase class has no tests + def test_ipv4_unavailable + assert true + end + + end + +end diff --git a/tests/test_ipv6.rb b/tests/test_ipv6.rb new file mode 100644 index 000000000..f3d66c639 --- /dev/null +++ b/tests/test_ipv6.rb @@ -0,0 +1,131 @@ +require 'em_test_helper' + +class TestIPv6 < Test::Unit::TestCase + + if Test::Unit::TestCase.public_ipv6? + + # Tries to connect to ipv6.google.com (2607:f8b0:4010:800::1006) port 80 via TCP. + # Timeout in 6 seconds. + def test_ipv6_tcp_client_with_ipv6_google_com + conn = nil + setup_timeout(6) + + EM.run do + conn = EM::connect("2607:f8b0:4010:800::1006", 80) do |c| + def c.connected + @connected + end + + def c.unbind(reason) + warn "unbind: #{reason.inspect}" if reason # XXX at least find out why it failed + end + + def c.connection_completed + @connected = true + EM.stop + end + end + end + + assert conn.connected + end + + # Runs a TCP server in the local IPv6 address, connects to it and sends a specific data. + # Timeout in 2 seconds. + def test_ipv6_tcp_local_server + @@received_data = nil + @local_port = next_port + setup_timeout(2) + + EM.run do + EM.start_server(@@public_ipv6, @local_port) do |s| + def s.receive_data data + @@received_data = data + EM.stop + end + end + + EM::connect(@@public_ipv6, @local_port) do |c| + def c.unbind(reason) + warn "unbind: #{reason.inspect}" if reason # XXX at least find out why it failed + end + c.send_data "ipv6/tcp" + end + end + + assert_equal "ipv6/tcp", @@received_data + end + + # Runs a UDP server in the local IPv6 address, connects to it and sends a specific data. + # Timeout in 2 seconds. + def test_ipv6_udp_local_server + @@received_data = nil + @local_port = next_port + setup_timeout(2) + + EM.run do + EM.open_datagram_socket(@@public_ipv6, @local_port) do |s| + def s.receive_data data + @@received_data = data + EM.stop + end + end + + EM.open_datagram_socket(@@public_ipv6, next_port) do |c| + c.send_datagram "ipv6/udp", @@public_ipv6, @local_port + end + end + + assert_equal "ipv6/udp", @@received_data + end + + # Try to connect via TCP to an invalid IPv6. EM.connect should raise + # EM::ConnectionError. + def test_tcp_connect_to_invalid_ipv6 + invalid_ipv6 = "1:A" + + EM.run do + begin + error = nil + EM.connect(invalid_ipv6, 1234) + rescue => e + error = e + ensure + EM.stop + assert_equal EM::ConnectionError, (error && error.class) + end + end + end + + # Try to send a UDP datagram to an invalid IPv6. EM.send_datagram should raise + # EM::ConnectionError. + def test_udp_send_datagram_to_invalid_ipv6 + invalid_ipv6 = "1:A" + + EM.run do + begin + error = nil + EM.open_datagram_socket(@@public_ipv6, next_port) do |c| + c.send_datagram "hello", invalid_ipv6, 1234 + end + rescue => e + error = e + ensure + EM.stop + assert_equal EM::ConnectionError, (error && error.class) + end + end + end + + + else + warn "no IPv6 in this host, skipping tests in #{__FILE__}" + + # Because some rubies will complain if a TestCase class has no tests. + def test_ipv6_unavailable + assert true + end + + end + +end From 850a7d449f2e55f8b860f6bc66c5b825c78c064f Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Tue, 3 Nov 2015 20:35:31 -0800 Subject: [PATCH 052/343] Whitespace and parens style --- tests/test_channel.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_channel.rb b/tests/test_channel.rb index e9a570f98..c54bf1da7 100644 --- a/tests/test_channel.rb +++ b/tests/test_channel.rb @@ -67,9 +67,9 @@ def test_channel_num_subscribers c.subscribe { |v| s = v } c.subscribe { |v| s = v } EM.next_tick { EM.stop } - subs = c.num_subscribers() + subs = c.num_subscribers end assert_equal subs, 2 - end + end end From ded5e8dbef35a62a837e01fe6466c869a599c391 Mon Sep 17 00:00:00 2001 From: Fabio Kung Date: Wed, 24 Apr 2013 19:19:39 -0700 Subject: [PATCH 053/343] wrong rdoc type/tag --- lib/em/connection.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/em/connection.rb b/lib/em/connection.rb index 10febf1a5..0edee3ec1 100644 --- a/lib/em/connection.rb +++ b/lib/em/connection.rb @@ -376,7 +376,7 @@ def connection_completed # # @option args [String] :private_key_file (nil) local path of a readable file that must contain a private key in the [PEM format](http://en.wikipedia.org/wiki/Privacy_Enhanced_Mail). # - # @option args [String] :verify_peer (false) indicates whether a server should request a certificate from a peer, to be verified by user code. + # @option args [Boolean] :verify_peer (false) indicates whether a server should request a certificate from a peer, to be verified by user code. # If true, the {#ssl_verify_peer} callback on the {EventMachine::Connection} object is called with each certificate # in the certificate chain provided by the peer. See documentation on {#ssl_verify_peer} for how to use this. # From a5bff1062eac5ba0b9fb569546a2676a8066fddf Mon Sep 17 00:00:00 2001 From: Fabio Kung Date: Wed, 24 Apr 2013 19:14:22 -0700 Subject: [PATCH 054/343] configuration option for SSLv23 or v3 only --- ext/cmain.cpp | 4 +- ext/ed.cpp | 8 ++-- ext/ed.h | 5 ++- ext/eventmachine.h | 2 +- ext/rubymain.cpp | 6 +-- ext/ssl.cpp | 14 ++++-- ext/ssl.h | 4 +- lib/em/connection.rb | 6 ++- tests/test_ssl_force_v3.rb | 91 ++++++++++++++++++++++++++++++++++++++ 9 files changed, 121 insertions(+), 19 deletions(-) create mode 100644 tests/test_ssl_force_v3.rb diff --git a/ext/cmain.cpp b/ext/cmain.cpp index 013428375..5dc5a3056 100644 --- a/ext/cmain.cpp +++ b/ext/cmain.cpp @@ -459,12 +459,12 @@ extern "C" void evma_start_tls (const uintptr_t binding) evma_set_tls_parms ******************/ -extern "C" void evma_set_tls_parms (const uintptr_t binding, const char *privatekey_filename, const char *certchain_filename, int verify_peer) +extern "C" void evma_set_tls_parms (const uintptr_t binding, const char *privatekey_filename, const char *certchain_filename, int verify_peer, int force_ssl_v3) { ensure_eventmachine("evma_set_tls_parms"); EventableDescriptor *ed = dynamic_cast (Bindable_t::GetObject (binding)); if (ed) - ed->SetTlsParms (privatekey_filename, certchain_filename, (verify_peer == 1 ? true : false)); + ed->SetTlsParms (privatekey_filename, certchain_filename, (verify_peer == 1 ? true : false), (force_ssl_v3 == 1 ? true : false)); } /****************** diff --git a/ext/ed.cpp b/ext/ed.cpp index f05b8d79e..f4efd7924 100644 --- a/ext/ed.cpp +++ b/ext/ed.cpp @@ -402,6 +402,7 @@ ConnectionDescriptor::ConnectionDescriptor (SOCKET sd, EventMachine_t *em): bHandshakeSignaled (false), bSslVerifyPeer (false), bSslPeerAccepted(false), + bSslV3Only(false), #endif #ifdef HAVE_KQUEUE bGotExtraKqueueEvent(false), @@ -1180,7 +1181,7 @@ void ConnectionDescriptor::StartTls() if (SslBox) throw std::runtime_error ("SSL/TLS already running on connection"); - SslBox = new SslBox_t (bIsServer, PrivateKeyFilename, CertChainFilename, bSslVerifyPeer, GetBinding()); + SslBox = new SslBox_t (bIsServer, PrivateKeyFilename, CertChainFilename, bSslVerifyPeer, bSslV3Only, GetBinding()); _DispatchCiphertext(); } @@ -1197,7 +1198,7 @@ ConnectionDescriptor::SetTlsParms *********************************/ #ifdef WITH_SSL -void ConnectionDescriptor::SetTlsParms (const char *privkey_filename, const char *certchain_filename, bool verify_peer) +void ConnectionDescriptor::SetTlsParms (const char *privkey_filename, const char *certchain_filename, bool verify_peer, bool force_ssl_v3) { if (SslBox) throw std::runtime_error ("call SetTlsParms before calling StartTls"); @@ -1206,9 +1207,10 @@ void ConnectionDescriptor::SetTlsParms (const char *privkey_filename, const char if (certchain_filename && *certchain_filename) CertChainFilename = certchain_filename; bSslVerifyPeer = verify_peer; + bSslV3Only = force_ssl_v3; } #else -void ConnectionDescriptor::SetTlsParms (const char *privkey_filename UNUSED, const char *certchain_filename UNUSED, bool verify_peer UNUSED) +void ConnectionDescriptor::SetTlsParms (const char *privkey_filename UNUSED, const char *certchain_filename UNUSED, bool verify_peer UNUSED, bool force_ssl_v3 UNUSED) { throw std::runtime_error ("Encryption not available on this event-machine"); } diff --git a/ext/ed.h b/ext/ed.h index 6b404e1cd..e961087fc 100644 --- a/ext/ed.h +++ b/ext/ed.h @@ -69,7 +69,7 @@ class EventableDescriptor: public Bindable_t virtual bool GetSubprocessPid (pid_t*) {return false;} virtual void StartTls() {} - virtual void SetTlsParms (const char *, const char *, bool) {} + virtual void SetTlsParms (const char *, const char *, bool, bool) {} #ifdef WITH_SSL virtual X509 *GetPeerCert() {return NULL;} @@ -201,7 +201,7 @@ class ConnectionDescriptor: public EventableDescriptor virtual int GetOutboundDataSize() {return OutboundDataSize;} virtual void StartTls(); - virtual void SetTlsParms (const char *privkey_filename, const char *certchain_filename, bool verify_peer); + virtual void SetTlsParms (const char *privkey_filename, const char *certchain_filename, bool verify_peer, bool force_ssl_v3=false); #ifdef WITH_SSL virtual X509 *GetPeerCert(); @@ -248,6 +248,7 @@ class ConnectionDescriptor: public EventableDescriptor bool bHandshakeSignaled; bool bSslVerifyPeer; bool bSslPeerAccepted; + bool bSslV3Only; #endif #ifdef HAVE_KQUEUE diff --git a/ext/eventmachine.h b/ext/eventmachine.h index 4c23e8920..291ac6e5f 100644 --- a/ext/eventmachine.h +++ b/ext/eventmachine.h @@ -68,7 +68,7 @@ extern "C" { const uintptr_t evma_attach_sd (int sd); const uintptr_t evma_open_datagram_socket (const char *server, int port); const uintptr_t evma_open_keyboard(); - void evma_set_tls_parms (const uintptr_t binding, const char *privatekey_filename, const char *certchain_filenane, int verify_peer); + void evma_set_tls_parms (const uintptr_t binding, const char *privatekey_filename, const char *certchain_filenane, int verify_peer, int force_ssl_v3); void evma_start_tls (const uintptr_t binding); #ifdef WITH_SSL diff --git a/ext/rubymain.cpp b/ext/rubymain.cpp index 1653ec5b8..cf0192f56 100644 --- a/ext/rubymain.cpp +++ b/ext/rubymain.cpp @@ -341,14 +341,14 @@ static VALUE t_start_tls (VALUE self UNUSED, VALUE signature) t_set_tls_parms ***************/ -static VALUE t_set_tls_parms (VALUE self UNUSED, VALUE signature, VALUE privkeyfile, VALUE certchainfile, VALUE verify_peer) +static VALUE t_set_tls_parms (VALUE self UNUSED, VALUE signature, VALUE privkeyfile, VALUE certchainfile, VALUE verify_peer, VALUE force_ssl_v3) { /* set_tls_parms takes a series of positional arguments for specifying such things * as private keys and certificate chains. * It's expected that the parameter list will grow as we add more supported features. * ALL of these parameters are optional, and can be specified as empty or NULL strings. */ - evma_set_tls_parms (NUM2BSIG (signature), StringValueCStr (privkeyfile), StringValueCStr (certchainfile), (verify_peer == Qtrue ? 1 : 0)); + evma_set_tls_parms (NUM2BSIG (signature), StringValueCStr (privkeyfile), StringValueCStr (certchainfile), (verify_peer == Qtrue ? 1 : 0), (force_ssl_v3 == Qtrue ? 1 : 0)); return Qnil; } @@ -1316,7 +1316,7 @@ extern "C" void Init_rubyeventmachine() rb_define_module_function (EmModule, "stop_tcp_server", (VALUE(*)(...))t_stop_server, 1); rb_define_module_function (EmModule, "start_unix_server", (VALUE(*)(...))t_start_unix_server, 1); rb_define_module_function (EmModule, "attach_sd", (VALUE(*)(...))t_attach_sd, 1); - rb_define_module_function (EmModule, "set_tls_parms", (VALUE(*)(...))t_set_tls_parms, 4); + rb_define_module_function (EmModule, "set_tls_parms", (VALUE(*)(...))t_set_tls_parms, 5); rb_define_module_function (EmModule, "start_tls", (VALUE(*)(...))t_start_tls, 1); rb_define_module_function (EmModule, "get_peer_cert", (VALUE(*)(...))t_get_peer_cert, 1); rb_define_module_function (EmModule, "send_data", (VALUE(*)(...))t_send_data, 3); diff --git a/ext/ssl.cpp b/ext/ssl.cpp index cad0bca22..4c796d923 100644 --- a/ext/ssl.cpp +++ b/ext/ssl.cpp @@ -120,7 +120,7 @@ static void InitializeDefaultCredentials() SslContext_t::SslContext_t **************************/ -SslContext_t::SslContext_t (bool is_server, const string &privkeyfile, const string &certchainfile): +SslContext_t::SslContext_t (bool is_server, const string &privkeyfile, const string &certchainfile, bool force_ssl_v3): pCtx (NULL), PrivateKey (NULL), Certificate (NULL) @@ -145,7 +145,13 @@ SslContext_t::SslContext_t (bool is_server, const string &privkeyfile, const str } bIsServer = is_server; - pCtx = SSL_CTX_new (is_server ? SSLv23_server_method() : SSLv23_client_method()); + SSL_METHOD *method; + if (is_server) { + method = force_ssl_v3 ? SSLv3_server_method() : SSLv23_server_method(); + } else { + method = force_ssl_v3 ? SSLv3_client_method() : SSLv23_client_method(); + } + pCtx = SSL_CTX_new (method); if (!pCtx) throw std::runtime_error ("no SSL context"); @@ -216,7 +222,7 @@ SslContext_t::~SslContext_t() SslBox_t::SslBox_t ******************/ -SslBox_t::SslBox_t (bool is_server, const string &privkeyfile, const string &certchainfile, bool verify_peer, const uintptr_t binding): +SslBox_t::SslBox_t (bool is_server, const string &privkeyfile, const string &certchainfile, bool verify_peer, bool force_ssl_v3, const uintptr_t binding): bIsServer (is_server), bHandshakeCompleted (false), bVerifyPeer (verify_peer), @@ -228,7 +234,7 @@ SslBox_t::SslBox_t (bool is_server, const string &privkeyfile, const string &cer * a new one every time we come here. */ - Context = new SslContext_t (bIsServer, privkeyfile, certchainfile); + Context = new SslContext_t (bIsServer, privkeyfile, certchainfile, force_ssl_v3); assert (Context); pbioRead = BIO_new (BIO_s_mem()); diff --git a/ext/ssl.h b/ext/ssl.h index a6e67acb0..a9bb8e599 100644 --- a/ext/ssl.h +++ b/ext/ssl.h @@ -33,7 +33,7 @@ class SslContext_t class SslContext_t { public: - SslContext_t (bool is_server, const string &privkeyfile, const string &certchainfile); + SslContext_t (bool is_server, const string &privkeyfile, const string &certchainfile, bool force_ssl_v3); virtual ~SslContext_t(); private: @@ -61,7 +61,7 @@ class SslBox_t class SslBox_t { public: - SslBox_t (bool is_server, const string &privkeyfile, const string &certchainfile, bool verify_peer, const uintptr_t binding); + SslBox_t (bool is_server, const string &privkeyfile, const string &certchainfile, bool verify_peer, bool force_ssl_v3, const uintptr_t binding); virtual ~SslBox_t(); int PutPlaintext (const char*, int); diff --git a/lib/em/connection.rb b/lib/em/connection.rb index 0edee3ec1..c1f545b9f 100644 --- a/lib/em/connection.rb +++ b/lib/em/connection.rb @@ -380,6 +380,8 @@ def connection_completed # If true, the {#ssl_verify_peer} callback on the {EventMachine::Connection} object is called with each certificate # in the certificate chain provided by the peer. See documentation on {#ssl_verify_peer} for how to use this. # + # @option args [Boolean] :force_ssl_v3 (false) If true, use SSLv3 only and disable SSLv2. Note that servers forcing SSLv3 require clients with the same setting. + # # @example Using TLS with EventMachine # # require 'rubygems' @@ -404,7 +406,7 @@ def connection_completed # # @see #ssl_verify_peer def start_tls args={} - priv_key, cert_chain, verify_peer = args.values_at(:private_key_file, :cert_chain_file, :verify_peer) + priv_key, cert_chain, verify_peer, force_ssl_v3 = args.values_at(:private_key_file, :cert_chain_file, :verify_peer, :force_ssl_v3) [priv_key, cert_chain].each do |file| next if file.nil? or file.empty? @@ -412,7 +414,7 @@ def start_tls args={} "Could not find #{file} for start_tls" unless File.exist? file end - EventMachine::set_tls_parms(@signature, priv_key || '', cert_chain || '', verify_peer) + EventMachine::set_tls_parms(@signature, priv_key || '', cert_chain || '', verify_peer, force_ssl_v3) EventMachine::start_tls @signature end diff --git a/tests/test_ssl_force_v3.rb b/tests/test_ssl_force_v3.rb new file mode 100644 index 000000000..d0017e339 --- /dev/null +++ b/tests/test_ssl_force_v3.rb @@ -0,0 +1,91 @@ +require 'em_test_helper' + +if EM.ssl? + class TestSslV3Only < Test::Unit::TestCase + + module ClientV23 + def connection_completed + start_tls + end + + def ssl_handshake_completed + $client_handshake_completed = true + close_connection + end + + def unbind + EM.stop_event_loop + end + end + + module ClientV3 + def connection_completed + start_tls(:force_ssl_v3 => true) + end + + def ssl_handshake_completed + $client_handshake_completed = true + close_connection + end + + def unbind + EM.stop_event_loop + end + end + + module ServerV3 + def post_init + start_tls(:force_ssl_v3 => true) + end + + def ssl_handshake_completed + $server_handshake_completed = true + end + end + + module ServerV23 + def post_init + start_tls + end + + def ssl_handshake_completed + $server_handshake_completed = true + end + end + + def test_v23_to_v3 + $client_handshake_completed, $server_handshake_completed = false, false + EM.run { + EM.start_server("127.0.0.1", 16784, ServerV3) + EM.connect("127.0.0.1", 16784, ClientV23) + } + + assert(!$client_handshake_completed) + assert(!$server_handshake_completed) + end + + def test_v3_to_v23 + $client_handshake_completed, $server_handshake_completed = false, false + EM.run { + EM.start_server("127.0.0.1", 16784, ServerV23) + EM.connect("127.0.0.1", 16784, ClientV3) + } + + assert($client_handshake_completed) + assert($server_handshake_completed) + end + + def test_v3_to_v3 + $client_handshake_completed, $server_handshake_completed = false, false + EM.run { + EM.start_server("127.0.0.1", 16784, ServerV3) + EM.connect("127.0.0.1", 16784, ClientV3) + } + + assert($client_handshake_completed) + assert($server_handshake_completed) + end + end +else + warn "EM built without SSL support, skipping tests in #{__FILE__}" +end From d2995806e8cde772d69ad18b244c508f19b189a5 Mon Sep 17 00:00:00 2001 From: Fabio Kung Date: Wed, 24 Apr 2013 19:37:52 -0700 Subject: [PATCH 055/343] avoid relying on the SSL_METHOD const-ness It is apparently different accross openssl versions --- ext/ssl.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/ext/ssl.cpp b/ext/ssl.cpp index 4c796d923..8023738d8 100644 --- a/ext/ssl.cpp +++ b/ext/ssl.cpp @@ -145,13 +145,11 @@ SslContext_t::SslContext_t (bool is_server, const string &privkeyfile, const str } bIsServer = is_server; - SSL_METHOD *method; if (is_server) { - method = force_ssl_v3 ? SSLv3_server_method() : SSLv23_server_method(); + pCtx = SSL_CTX_new (force_ssl_v3 ? SSLv3_server_method() : SSLv23_server_method()); } else { - method = force_ssl_v3 ? SSLv3_client_method() : SSLv23_client_method(); + pCtx = SSL_CTX_new (force_ssl_v3 ? SSLv3_client_method() : SSLv23_client_method()); } - pCtx = SSL_CTX_new (method); if (!pCtx) throw std::runtime_error ("no SSL context"); From 5adb9840fc56dde0b8ae6dd8fbd9db01515f9387 Mon Sep 17 00:00:00 2001 From: Fabio Kung Date: Tue, 7 May 2013 18:29:39 -0700 Subject: [PATCH 056/343] allow multiple versions instead of only forcing sslv3 --- ext/cmain.cpp | 4 +- ext/ed.cpp | 10 +- ext/ed.h | 7 +- ext/eventmachine.h | 2 +- ext/rubymain.cpp | 14 ++- ext/ssl.cpp | 23 ++-- ext/ssl.h | 4 +- lib/em/connection.rb | 7 +- tests/test_ssl_force_v3.rb | 91 --------------- tests/test_ssl_min_version.rb | 205 ++++++++++++++++++++++++++++++++++ 10 files changed, 249 insertions(+), 118 deletions(-) delete mode 100644 tests/test_ssl_force_v3.rb create mode 100644 tests/test_ssl_min_version.rb diff --git a/ext/cmain.cpp b/ext/cmain.cpp index 5dc5a3056..fd5561ac5 100644 --- a/ext/cmain.cpp +++ b/ext/cmain.cpp @@ -459,12 +459,12 @@ extern "C" void evma_start_tls (const uintptr_t binding) evma_set_tls_parms ******************/ -extern "C" void evma_set_tls_parms (const uintptr_t binding, const char *privatekey_filename, const char *certchain_filename, int verify_peer, int force_ssl_v3) +extern "C" void evma_set_tls_parms (const uintptr_t binding, const char *privatekey_filename, const char *certchain_filename, int verify_peer, enum SslMinVersion min_version) { ensure_eventmachine("evma_set_tls_parms"); EventableDescriptor *ed = dynamic_cast (Bindable_t::GetObject (binding)); if (ed) - ed->SetTlsParms (privatekey_filename, certchain_filename, (verify_peer == 1 ? true : false), (force_ssl_v3 == 1 ? true : false)); + ed->SetTlsParms (privatekey_filename, certchain_filename, (verify_peer == 1 ? true : false), min_version); } /****************** diff --git a/ext/ed.cpp b/ext/ed.cpp index f4efd7924..5e6dacfaf 100644 --- a/ext/ed.cpp +++ b/ext/ed.cpp @@ -402,7 +402,7 @@ ConnectionDescriptor::ConnectionDescriptor (SOCKET sd, EventMachine_t *em): bHandshakeSignaled (false), bSslVerifyPeer (false), bSslPeerAccepted(false), - bSslV3Only(false), + eSslMinVersion(SSLv2), #endif #ifdef HAVE_KQUEUE bGotExtraKqueueEvent(false), @@ -1181,7 +1181,7 @@ void ConnectionDescriptor::StartTls() if (SslBox) throw std::runtime_error ("SSL/TLS already running on connection"); - SslBox = new SslBox_t (bIsServer, PrivateKeyFilename, CertChainFilename, bSslVerifyPeer, bSslV3Only, GetBinding()); + SslBox = new SslBox_t (bIsServer, PrivateKeyFilename, CertChainFilename, bSslVerifyPeer, eSslMinVersion, GetBinding()); _DispatchCiphertext(); } @@ -1198,7 +1198,7 @@ ConnectionDescriptor::SetTlsParms *********************************/ #ifdef WITH_SSL -void ConnectionDescriptor::SetTlsParms (const char *privkey_filename, const char *certchain_filename, bool verify_peer, bool force_ssl_v3) +void ConnectionDescriptor::SetTlsParms (const char *privkey_filename, const char *certchain_filename, bool verify_peer, SslMinVersion min_version) { if (SslBox) throw std::runtime_error ("call SetTlsParms before calling StartTls"); @@ -1207,10 +1207,10 @@ void ConnectionDescriptor::SetTlsParms (const char *privkey_filename, const char if (certchain_filename && *certchain_filename) CertChainFilename = certchain_filename; bSslVerifyPeer = verify_peer; - bSslV3Only = force_ssl_v3; + eSslMinVersion = min_version; } #else -void ConnectionDescriptor::SetTlsParms (const char *privkey_filename UNUSED, const char *certchain_filename UNUSED, bool verify_peer UNUSED, bool force_ssl_v3 UNUSED) +void ConnectionDescriptor::SetTlsParms (const char *privkey_filename UNUSED, const char *certchain_filename UNUSED, bool verify_peer UNUSED, SslMinVersion min_version UNUSED) { throw std::runtime_error ("Encryption not available on this event-machine"); } diff --git a/ext/ed.h b/ext/ed.h index e961087fc..77f145c53 100644 --- a/ext/ed.h +++ b/ext/ed.h @@ -24,6 +24,7 @@ See the file COPYING for complete licensing information. class EventMachine_t; // forward reference #ifdef WITH_SSL class SslBox_t; // forward reference +enum SslMinVersion { SSLv2, SSLv3, TLSv1 }; #endif bool SetSocketNonblocking (SOCKET); @@ -69,7 +70,7 @@ class EventableDescriptor: public Bindable_t virtual bool GetSubprocessPid (pid_t*) {return false;} virtual void StartTls() {} - virtual void SetTlsParms (const char *, const char *, bool, bool) {} + virtual void SetTlsParms (const char *, const char *, bool, SslMinVersion) {} #ifdef WITH_SSL virtual X509 *GetPeerCert() {return NULL;} @@ -201,7 +202,7 @@ class ConnectionDescriptor: public EventableDescriptor virtual int GetOutboundDataSize() {return OutboundDataSize;} virtual void StartTls(); - virtual void SetTlsParms (const char *privkey_filename, const char *certchain_filename, bool verify_peer, bool force_ssl_v3=false); + virtual void SetTlsParms (const char *privkey_filename, const char *certchain_filename, bool verify_peer, SslMinVersion min_version=SSLv2); #ifdef WITH_SSL virtual X509 *GetPeerCert(); @@ -248,7 +249,7 @@ class ConnectionDescriptor: public EventableDescriptor bool bHandshakeSignaled; bool bSslVerifyPeer; bool bSslPeerAccepted; - bool bSslV3Only; + SslMinVersion eSslMinVersion; #endif #ifdef HAVE_KQUEUE diff --git a/ext/eventmachine.h b/ext/eventmachine.h index 291ac6e5f..6bfb730d5 100644 --- a/ext/eventmachine.h +++ b/ext/eventmachine.h @@ -68,7 +68,7 @@ extern "C" { const uintptr_t evma_attach_sd (int sd); const uintptr_t evma_open_datagram_socket (const char *server, int port); const uintptr_t evma_open_keyboard(); - void evma_set_tls_parms (const uintptr_t binding, const char *privatekey_filename, const char *certchain_filenane, int verify_peer, int force_ssl_v3); + void evma_set_tls_parms (const uintptr_t binding, const char *privatekey_filename, const char *certchain_filenane, int verify_peer, enum SslMinVersion min_version); void evma_start_tls (const uintptr_t binding); #ifdef WITH_SSL diff --git a/ext/rubymain.cpp b/ext/rubymain.cpp index cf0192f56..98862c52a 100644 --- a/ext/rubymain.cpp +++ b/ext/rubymain.cpp @@ -341,14 +341,24 @@ static VALUE t_start_tls (VALUE self UNUSED, VALUE signature) t_set_tls_parms ***************/ -static VALUE t_set_tls_parms (VALUE self UNUSED, VALUE signature, VALUE privkeyfile, VALUE certchainfile, VALUE verify_peer, VALUE force_ssl_v3) +static VALUE t_set_tls_parms (VALUE self UNUSED, VALUE signature, VALUE privkeyfile, VALUE certchainfile, VALUE verify_peer, VALUE min_version) { /* set_tls_parms takes a series of positional arguments for specifying such things * as private keys and certificate chains. * It's expected that the parameter list will grow as we add more supported features. * ALL of these parameters are optional, and can be specified as empty or NULL strings. */ - evma_set_tls_parms (NUM2BSIG (signature), StringValueCStr (privkeyfile), StringValueCStr (certchainfile), (verify_peer == Qtrue ? 1 : 0), (force_ssl_v3 == Qtrue ? 1 : 0)); + SslMinVersion min; + ID id = SYM2ID(min_version); + if (id == rb_intern("tlsv1")) { + min = TLSv1; + } else if (id == rb_intern("sslv3")) { + min = SSLv3; + } else { + min = SSLv2; + } + + evma_set_tls_parms (NUM2BSIG (signature), StringValueCStr (privkeyfile), StringValueCStr (certchainfile), (verify_peer == Qtrue ? 1 : 0), min); return Qnil; } diff --git a/ext/ssl.cpp b/ext/ssl.cpp index 8023738d8..10b6f666b 100644 --- a/ext/ssl.cpp +++ b/ext/ssl.cpp @@ -120,7 +120,7 @@ static void InitializeDefaultCredentials() SslContext_t::SslContext_t **************************/ -SslContext_t::SslContext_t (bool is_server, const string &privkeyfile, const string &certchainfile, bool force_ssl_v3): +SslContext_t::SslContext_t (bool is_server, const string &privkeyfile, const string &certchainfile, SslMinVersion min_version): pCtx (NULL), PrivateKey (NULL), Certificate (NULL) @@ -145,16 +145,21 @@ SslContext_t::SslContext_t (bool is_server, const string &privkeyfile, const str } bIsServer = is_server; - if (is_server) { - pCtx = SSL_CTX_new (force_ssl_v3 ? SSLv3_server_method() : SSLv23_server_method()); - } else { - pCtx = SSL_CTX_new (force_ssl_v3 ? SSLv3_client_method() : SSLv23_client_method()); - } + pCtx = SSL_CTX_new (is_server ? SSLv23_server_method() : SSLv23_client_method()); if (!pCtx) throw std::runtime_error ("no SSL context"); SSL_CTX_set_options (pCtx, SSL_OP_ALL); - //SSL_CTX_set_options (pCtx, (SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3)); + switch (min_version) { + case TLSv1: + SSL_CTX_set_options (pCtx, SSL_OP_NO_SSLv3); + case SSLv3: + SSL_CTX_set_options (pCtx, SSL_OP_NO_SSLv2); + case SSLv2: + default: + break; + } + #ifdef SSL_MODE_RELEASE_BUFFERS SSL_CTX_set_mode (pCtx, SSL_MODE_RELEASE_BUFFERS); #endif @@ -220,7 +225,7 @@ SslContext_t::~SslContext_t() SslBox_t::SslBox_t ******************/ -SslBox_t::SslBox_t (bool is_server, const string &privkeyfile, const string &certchainfile, bool verify_peer, bool force_ssl_v3, const uintptr_t binding): +SslBox_t::SslBox_t (bool is_server, const string &privkeyfile, const string &certchainfile, bool verify_peer, SslMinVersion min_version, const uintptr_t binding): bIsServer (is_server), bHandshakeCompleted (false), bVerifyPeer (verify_peer), @@ -232,7 +237,7 @@ SslBox_t::SslBox_t (bool is_server, const string &privkeyfile, const string &cer * a new one every time we come here. */ - Context = new SslContext_t (bIsServer, privkeyfile, certchainfile, force_ssl_v3); + Context = new SslContext_t (bIsServer, privkeyfile, certchainfile, min_version); assert (Context); pbioRead = BIO_new (BIO_s_mem()); diff --git a/ext/ssl.h b/ext/ssl.h index a9bb8e599..7ca570ea5 100644 --- a/ext/ssl.h +++ b/ext/ssl.h @@ -33,7 +33,7 @@ class SslContext_t class SslContext_t { public: - SslContext_t (bool is_server, const string &privkeyfile, const string &certchainfile, bool force_ssl_v3); + SslContext_t (bool is_server, const string &privkeyfile, const string &certchainfile, SslMinVersion min_version); virtual ~SslContext_t(); private: @@ -61,7 +61,7 @@ class SslBox_t class SslBox_t { public: - SslBox_t (bool is_server, const string &privkeyfile, const string &certchainfile, bool verify_peer, bool force_ssl_v3, const uintptr_t binding); + SslBox_t (bool is_server, const string &privkeyfile, const string &certchainfile, bool verify_peer, SslMinVersion min_version, const uintptr_t binding); virtual ~SslBox_t(); int PutPlaintext (const char*, int); diff --git a/lib/em/connection.rb b/lib/em/connection.rb index c1f545b9f..a065a9700 100644 --- a/lib/em/connection.rb +++ b/lib/em/connection.rb @@ -380,7 +380,7 @@ def connection_completed # If true, the {#ssl_verify_peer} callback on the {EventMachine::Connection} object is called with each certificate # in the certificate chain provided by the peer. See documentation on {#ssl_verify_peer} for how to use this. # - # @option args [Boolean] :force_ssl_v3 (false) If true, use SSLv3 only and disable SSLv2. Note that servers forcing SSLv3 require clients with the same setting. + # @option args [Symbol] :min_version (:sslv2) Minimum SSL protocol version. Possible values are: {:sslv2} (enables all versions), {:sslv3} (disables SSLv2) and {:tlsv1} (disables all the previous, TLSv1 only). # # @example Using TLS with EventMachine # @@ -406,7 +406,8 @@ def connection_completed # # @see #ssl_verify_peer def start_tls args={} - priv_key, cert_chain, verify_peer, force_ssl_v3 = args.values_at(:private_key_file, :cert_chain_file, :verify_peer, :force_ssl_v3) + priv_key, cert_chain, verify_peer, min_version = args.values_at(:private_key_file, :cert_chain_file, :verify_peer, :min_version) + min_version = min_version.to_s.strip.empty? ? :sslv2 : min_version.to_s.strip.downcase.to_sym [priv_key, cert_chain].each do |file| next if file.nil? or file.empty? @@ -414,7 +415,7 @@ def start_tls args={} "Could not find #{file} for start_tls" unless File.exist? file end - EventMachine::set_tls_parms(@signature, priv_key || '', cert_chain || '', verify_peer, force_ssl_v3) + EventMachine::set_tls_parms(@signature, priv_key || '', cert_chain || '', verify_peer, min_version) EventMachine::start_tls @signature end diff --git a/tests/test_ssl_force_v3.rb b/tests/test_ssl_force_v3.rb deleted file mode 100644 index d0017e339..000000000 --- a/tests/test_ssl_force_v3.rb +++ /dev/null @@ -1,91 +0,0 @@ -require 'em_test_helper' - -if EM.ssl? - class TestSslV3Only < Test::Unit::TestCase - - module ClientV23 - def connection_completed - start_tls - end - - def ssl_handshake_completed - $client_handshake_completed = true - close_connection - end - - def unbind - EM.stop_event_loop - end - end - - module ClientV3 - def connection_completed - start_tls(:force_ssl_v3 => true) - end - - def ssl_handshake_completed - $client_handshake_completed = true - close_connection - end - - def unbind - EM.stop_event_loop - end - end - - module ServerV3 - def post_init - start_tls(:force_ssl_v3 => true) - end - - def ssl_handshake_completed - $server_handshake_completed = true - end - end - - module ServerV23 - def post_init - start_tls - end - - def ssl_handshake_completed - $server_handshake_completed = true - end - end - - def test_v23_to_v3 - $client_handshake_completed, $server_handshake_completed = false, false - EM.run { - EM.start_server("127.0.0.1", 16784, ServerV3) - EM.connect("127.0.0.1", 16784, ClientV23) - } - - assert(!$client_handshake_completed) - assert(!$server_handshake_completed) - end - - def test_v3_to_v23 - $client_handshake_completed, $server_handshake_completed = false, false - EM.run { - EM.start_server("127.0.0.1", 16784, ServerV23) - EM.connect("127.0.0.1", 16784, ClientV3) - } - - assert($client_handshake_completed) - assert($server_handshake_completed) - end - - def test_v3_to_v3 - $client_handshake_completed, $server_handshake_completed = false, false - EM.run { - EM.start_server("127.0.0.1", 16784, ServerV3) - EM.connect("127.0.0.1", 16784, ClientV3) - } - - assert($client_handshake_completed) - assert($server_handshake_completed) - end - end -else - warn "EM built without SSL support, skipping tests in #{__FILE__}" -end diff --git a/tests/test_ssl_min_version.rb b/tests/test_ssl_min_version.rb new file mode 100644 index 000000000..ffe73f913 --- /dev/null +++ b/tests/test_ssl_min_version.rb @@ -0,0 +1,205 @@ +require 'em_test_helper' + +if EM.ssl? + class TestSslMinVersion < Test::Unit::TestCase + + module ClientAny + def connection_completed + start_tls + end + + def ssl_handshake_completed + $client_handshake_completed = true + close_connection + end + + def unbind + EM.stop_event_loop + end + end + + module ClientMinV3 + def connection_completed + start_tls(:min_version => :sslv3) + end + + def ssl_handshake_completed + $client_handshake_completed = true + close_connection + end + + def unbind + EM.stop_event_loop + end + end + + module ServerMinV3 + def post_init + start_tls(:min_version => :sslv3) + end + + def ssl_handshake_completed + $server_handshake_completed = true + end + end + + module ServerTLSv1CaseInsensitive + def post_init + start_tls(:min_version => :TLSv1) + end + + def ssl_handshake_completed + $server_handshake_completed = true + end + end + + module ServerAny + def post_init + start_tls + end + + def ssl_handshake_completed + $server_handshake_completed = true + end + end + + def test_any_to_v3 + $client_handshake_completed, $server_handshake_completed = false, false + EM.run { + EM.start_server("127.0.0.1", 16784, ServerMinV3) + EM.connect("127.0.0.1", 16784, ClientAny) + } + + assert($client_handshake_completed) + assert($server_handshake_completed) + end + + def test_case_insensitivity + $client_handshake_completed, $server_handshake_completed = false, false + EM.run { + EM.start_server("127.0.0.1", 16784, ServerTLSv1CaseInsensitive) + EM.connect("127.0.0.1", 16784, ClientAny) + } + + assert($client_handshake_completed) + assert($server_handshake_completed) + end + + def test_v3_to_any + $client_handshake_completed, $server_handshake_completed = false, false + EM.run { + EM.start_server("127.0.0.1", 16784, ServerAny) + EM.connect("127.0.0.1", 16784, ClientMinV3) + } + + assert($client_handshake_completed) + assert($server_handshake_completed) + end + + def test_v3_to_v3 + $client_handshake_completed, $server_handshake_completed = false, false + EM.run { + EM.start_server("127.0.0.1", 16784, ServerMinV3) + EM.connect("127.0.0.1", 16784, ClientMinV3) + } + + assert($client_handshake_completed) + assert($server_handshake_completed) + end + + module ServerV3StopAfterHandshake + def post_init + start_tls(:min_version => :sslv3) + end + + def ssl_handshake_completed + $server_handshake_completed = true + EM.stop_event_loop + end + end + + module ServerTLSv1StopAfterHandshake + def post_init + start_tls(:min_version => :tlsv1) + end + + def ssl_handshake_completed + $server_handshake_completed = true + EM.stop_event_loop + end + end + + def test_v3_with_external_client + $server_handshake_completed = false + EM.run { + setup_timeout(2) + EM.start_server("127.0.0.1", 16784, ServerV3StopAfterHandshake) + EM.defer { + require "socket" + require "openssl" + sock = TCPSocket.new("127.0.0.1", 16784) + ctx = OpenSSL::SSL::SSLContext.new + ctx.ssl_version = :TLSv1_client + ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx) + ssl.connect + ssl.close rescue nil + sock.close rescue nil + } + } + + assert($server_handshake_completed) + end + + def test_tlsv1_with_external_client + $server_handshake_completed = false + EM.run { + setup_timeout(2) + EM.start_server("127.0.0.1", 16784, ServerTLSv1StopAfterHandshake) + EM.defer { + require "socket" + require "openssl" + sock = TCPSocket.new("127.0.0.1", 16784) + ctx = OpenSSL::SSL::SSLContext.new + ctx.ssl_version = :TLSv1_client + ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx) + ssl.connect + ssl.close rescue nil + sock.close rescue nil + } + } + + assert($server_handshake_completed) + end + + def test_tlsv1_required_with_external_client + $server_handshake_completed = false + + EM.run { + n = 0 + EM.add_periodic_timer(0.5) { + n += 1 + (EM.stop rescue nil) if n == 2 + } + EM.start_server("127.0.0.1", 16784, ServerTLSv1StopAfterHandshake) + EM.defer { + require "socket" + require "openssl" + sock = TCPSocket.new("127.0.0.1", 16784) + ctx = OpenSSL::SSL::SSLContext.new + ctx.ssl_version = :SSLv3_client + ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx) + assert_raise OpenSSL::SSL::SSLError do + ssl.connect + end + ssl.close rescue nil + sock.close rescue nil + EM.stop rescue nil + } + } + + assert(!$server_handshake_completed) + end + end +else + warn "EM built without SSL support, skipping tests in #{__FILE__}" +end From 57144c3cc3f73c3cc098adb750f3e721e8d970e0 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Mon, 2 Nov 2015 00:01:59 -0800 Subject: [PATCH 057/343] Add the cipher_list and protocols options to start_tls --- ext/cmain.cpp | 4 +-- ext/ed.cpp | 11 +++--- ext/ed.h | 8 ++--- ext/eventmachine.h | 9 ++++- ext/rubymain.cpp | 48 ++++++++++++------------- ext/ssl.cpp | 59 ++++++++++++++++++++---------- ext/ssl.h | 4 +-- lib/em/connection.rb | 28 ++++++++++++--- tests/test_ssl_min_version.rb | 67 +++++++++++++++++------------------ 9 files changed, 142 insertions(+), 96 deletions(-) diff --git a/ext/cmain.cpp b/ext/cmain.cpp index fd5561ac5..a7c20ea8d 100644 --- a/ext/cmain.cpp +++ b/ext/cmain.cpp @@ -459,12 +459,12 @@ extern "C" void evma_start_tls (const uintptr_t binding) evma_set_tls_parms ******************/ -extern "C" void evma_set_tls_parms (const uintptr_t binding, const char *privatekey_filename, const char *certchain_filename, int verify_peer, enum SslMinVersion min_version) +extern "C" void evma_set_tls_parms (const uintptr_t binding, const char *privatekey_filename, const char *certchain_filename, int verify_peer, const char *cipherlist, int protocols) { ensure_eventmachine("evma_set_tls_parms"); EventableDescriptor *ed = dynamic_cast (Bindable_t::GetObject (binding)); if (ed) - ed->SetTlsParms (privatekey_filename, certchain_filename, (verify_peer == 1 ? true : false), min_version); + ed->SetTlsParms (privatekey_filename, certchain_filename, (verify_peer == 1 ? true : false), cipherlist, protocols); } /****************** diff --git a/ext/ed.cpp b/ext/ed.cpp index 5e6dacfaf..e77051ff5 100644 --- a/ext/ed.cpp +++ b/ext/ed.cpp @@ -402,7 +402,6 @@ ConnectionDescriptor::ConnectionDescriptor (SOCKET sd, EventMachine_t *em): bHandshakeSignaled (false), bSslVerifyPeer (false), bSslPeerAccepted(false), - eSslMinVersion(SSLv2), #endif #ifdef HAVE_KQUEUE bGotExtraKqueueEvent(false), @@ -1181,7 +1180,7 @@ void ConnectionDescriptor::StartTls() if (SslBox) throw std::runtime_error ("SSL/TLS already running on connection"); - SslBox = new SslBox_t (bIsServer, PrivateKeyFilename, CertChainFilename, bSslVerifyPeer, eSslMinVersion, GetBinding()); + SslBox = new SslBox_t (bIsServer, PrivateKeyFilename, CertChainFilename, bSslVerifyPeer, CipherList, Protocols, GetBinding()); _DispatchCiphertext(); } @@ -1198,7 +1197,7 @@ ConnectionDescriptor::SetTlsParms *********************************/ #ifdef WITH_SSL -void ConnectionDescriptor::SetTlsParms (const char *privkey_filename, const char *certchain_filename, bool verify_peer, SslMinVersion min_version) +void ConnectionDescriptor::SetTlsParms (const char *privkey_filename, const char *certchain_filename, bool verify_peer, const char *cipherlist, int protocols) { if (SslBox) throw std::runtime_error ("call SetTlsParms before calling StartTls"); @@ -1207,10 +1206,12 @@ void ConnectionDescriptor::SetTlsParms (const char *privkey_filename, const char if (certchain_filename && *certchain_filename) CertChainFilename = certchain_filename; bSslVerifyPeer = verify_peer; - eSslMinVersion = min_version; + if (cipherlist && *cipherlist) + CipherList = cipherlist; + Protocols = protocols; } #else -void ConnectionDescriptor::SetTlsParms (const char *privkey_filename UNUSED, const char *certchain_filename UNUSED, bool verify_peer UNUSED, SslMinVersion min_version UNUSED) +void ConnectionDescriptor::SetTlsParms (const char *privkey_filename UNUSED, const char *certchain_filename UNUSED, bool verify_peer UNUSED, const char *cipherlist UNUSED, int protocols UNUSED) { throw std::runtime_error ("Encryption not available on this event-machine"); } diff --git a/ext/ed.h b/ext/ed.h index 77f145c53..38bf71355 100644 --- a/ext/ed.h +++ b/ext/ed.h @@ -24,7 +24,6 @@ See the file COPYING for complete licensing information. class EventMachine_t; // forward reference #ifdef WITH_SSL class SslBox_t; // forward reference -enum SslMinVersion { SSLv2, SSLv3, TLSv1 }; #endif bool SetSocketNonblocking (SOCKET); @@ -70,7 +69,7 @@ class EventableDescriptor: public Bindable_t virtual bool GetSubprocessPid (pid_t*) {return false;} virtual void StartTls() {} - virtual void SetTlsParms (const char *, const char *, bool, SslMinVersion) {} + virtual void SetTlsParms (const char *, const char *, bool, const char *, int) {} #ifdef WITH_SSL virtual X509 *GetPeerCert() {return NULL;} @@ -202,7 +201,7 @@ class ConnectionDescriptor: public EventableDescriptor virtual int GetOutboundDataSize() {return OutboundDataSize;} virtual void StartTls(); - virtual void SetTlsParms (const char *privkey_filename, const char *certchain_filename, bool verify_peer, SslMinVersion min_version=SSLv2); + virtual void SetTlsParms (const char *, const char *, bool, const char *, int); #ifdef WITH_SSL virtual X509 *GetPeerCert(); @@ -246,10 +245,11 @@ class ConnectionDescriptor: public EventableDescriptor SslBox_t *SslBox; std::string CertChainFilename; std::string PrivateKeyFilename; + std::string CipherList; + int Protocols; bool bHandshakeSignaled; bool bSslVerifyPeer; bool bSslPeerAccepted; - SslMinVersion eSslMinVersion; #endif #ifdef HAVE_KQUEUE diff --git a/ext/eventmachine.h b/ext/eventmachine.h index 6bfb730d5..7621b3c5b 100644 --- a/ext/eventmachine.h +++ b/ext/eventmachine.h @@ -37,7 +37,14 @@ extern "C" { EM_SSL_VERIFY = 109, EM_PROXY_TARGET_UNBOUND = 110, EM_PROXY_COMPLETED = 111 + }; + enum { // SSL/TLS Protocols + EM_PROTO_SSLv2 = 2, + EM_PROTO_SSLv3 = 4, + EM_PROTO_TLSv1 = 8, + EM_PROTO_TLSv1_1 = 16, + EM_PROTO_TLSv1_2 = 32 }; void evma_initialize_library (EMCallback); @@ -68,7 +75,7 @@ extern "C" { const uintptr_t evma_attach_sd (int sd); const uintptr_t evma_open_datagram_socket (const char *server, int port); const uintptr_t evma_open_keyboard(); - void evma_set_tls_parms (const uintptr_t binding, const char *privatekey_filename, const char *certchain_filenane, int verify_peer, enum SslMinVersion min_version); + void evma_set_tls_parms (const uintptr_t binding, const char *privatekey_filename, const char *certchain_filenane, int verify_peer, const char *cipherlist, int protocols); void evma_start_tls (const uintptr_t binding); #ifdef WITH_SSL diff --git a/ext/rubymain.cpp b/ext/rubymain.cpp index 98862c52a..446273eb1 100644 --- a/ext/rubymain.cpp +++ b/ext/rubymain.cpp @@ -341,24 +341,14 @@ static VALUE t_start_tls (VALUE self UNUSED, VALUE signature) t_set_tls_parms ***************/ -static VALUE t_set_tls_parms (VALUE self UNUSED, VALUE signature, VALUE privkeyfile, VALUE certchainfile, VALUE verify_peer, VALUE min_version) +static VALUE t_set_tls_parms (VALUE self UNUSED, VALUE signature, VALUE privkeyfile, VALUE certchainfile, VALUE verify_peer, VALUE cipherlist, VALUE protocols) { /* set_tls_parms takes a series of positional arguments for specifying such things * as private keys and certificate chains. * It's expected that the parameter list will grow as we add more supported features. * ALL of these parameters are optional, and can be specified as empty or NULL strings. */ - SslMinVersion min; - ID id = SYM2ID(min_version); - if (id == rb_intern("tlsv1")) { - min = TLSv1; - } else if (id == rb_intern("sslv3")) { - min = SSLv3; - } else { - min = SSLv2; - } - - evma_set_tls_parms (NUM2BSIG (signature), StringValueCStr (privkeyfile), StringValueCStr (certchainfile), (verify_peer == Qtrue ? 1 : 0), min); + evma_set_tls_parms (NUM2BSIG (signature), StringValueCStr (privkeyfile), StringValueCStr (certchainfile), (verify_peer == Qtrue ? 1 : 0), StringValueCStr (cipherlist), NUM2INT (protocols)); return Qnil; } @@ -1326,7 +1316,7 @@ extern "C" void Init_rubyeventmachine() rb_define_module_function (EmModule, "stop_tcp_server", (VALUE(*)(...))t_stop_server, 1); rb_define_module_function (EmModule, "start_unix_server", (VALUE(*)(...))t_start_unix_server, 1); rb_define_module_function (EmModule, "attach_sd", (VALUE(*)(...))t_attach_sd, 1); - rb_define_module_function (EmModule, "set_tls_parms", (VALUE(*)(...))t_set_tls_parms, 5); + rb_define_module_function (EmModule, "set_tls_parms", (VALUE(*)(...))t_set_tls_parms, 6); rb_define_module_function (EmModule, "start_tls", (VALUE(*)(...))t_start_tls, 1); rb_define_module_function (EmModule, "get_peer_cert", (VALUE(*)(...))t_get_peer_cert, 1); rb_define_module_function (EmModule, "send_data", (VALUE(*)(...))t_send_data, 3); @@ -1407,17 +1397,25 @@ extern "C" void Init_rubyeventmachine() rb_define_method (EmConnection, "get_outbound_data_size", (VALUE(*)(...))conn_get_outbound_data_size, 0); rb_define_method (EmConnection, "associate_callback_target", (VALUE(*)(...))conn_associate_callback_target, 1); - rb_define_const (EmModule, "TimerFired", INT2NUM(100)); - rb_define_const (EmModule, "ConnectionData", INT2NUM(101)); - rb_define_const (EmModule, "ConnectionUnbound", INT2NUM(102)); - rb_define_const (EmModule, "ConnectionAccepted", INT2NUM(103)); - rb_define_const (EmModule, "ConnectionCompleted", INT2NUM(104)); - rb_define_const (EmModule, "LoopbreakSignalled", INT2NUM(105)); - - rb_define_const (EmModule, "ConnectionNotifyReadable", INT2NUM(106)); - rb_define_const (EmModule, "ConnectionNotifyWritable", INT2NUM(107)); - - rb_define_const (EmModule, "SslHandshakeCompleted", INT2NUM(108)); - + // Connection states + rb_define_const (EmModule, "TimerFired", INT2NUM(EM_TIMER_FIRED )); + rb_define_const (EmModule, "ConnectionData", INT2NUM(EM_CONNECTION_READ )); + rb_define_const (EmModule, "ConnectionUnbound", INT2NUM(EM_CONNECTION_UNBOUND )); + rb_define_const (EmModule, "ConnectionAccepted", INT2NUM(EM_CONNECTION_ACCEPTED )); + rb_define_const (EmModule, "ConnectionCompleted", INT2NUM(EM_CONNECTION_COMPLETED )); + rb_define_const (EmModule, "LoopbreakSignalled", INT2NUM(EM_LOOPBREAK_SIGNAL )); + rb_define_const (EmModule, "ConnectionNotifyReadable", INT2NUM(EM_CONNECTION_NOTIFY_READABLE)); + rb_define_const (EmModule, "ConnectionNotifyWritable", INT2NUM(EM_CONNECTION_NOTIFY_WRITABLE)); + rb_define_const (EmModule, "SslHandshakeCompleted", INT2NUM(EM_SSL_HANDSHAKE_COMPLETED )); + // EM_SSL_VERIFY = 109, + // EM_PROXY_TARGET_UNBOUND = 110, + // EM_PROXY_COMPLETED = 111 + + // SSL Protocols + rb_define_const (EmModule, "EM_PROTO_SSLv2", INT2NUM(EM_PROTO_SSLv2 )); + rb_define_const (EmModule, "EM_PROTO_SSLv3", INT2NUM(EM_PROTO_SSLv3 )); + rb_define_const (EmModule, "EM_PROTO_TLSv1", INT2NUM(EM_PROTO_TLSv1 )); + rb_define_const (EmModule, "EM_PROTO_TLSv1_1", INT2NUM(EM_PROTO_TLSv1_1)); + rb_define_const (EmModule, "EM_PROTO_TLSv1_2", INT2NUM(EM_PROTO_TLSv1_2)); } diff --git a/ext/ssl.cpp b/ext/ssl.cpp index 10b6f666b..9b547e89a 100644 --- a/ext/ssl.cpp +++ b/ext/ssl.cpp @@ -120,7 +120,8 @@ static void InitializeDefaultCredentials() SslContext_t::SslContext_t **************************/ -SslContext_t::SslContext_t (bool is_server, const string &privkeyfile, const string &certchainfile, SslMinVersion min_version): +SslContext_t::SslContext_t (bool is_server, const string &privkeyfile, const string &certchainfile, const string &cipherlist, int protocols) : + bIsServer (is_server), pCtx (NULL), PrivateKey (NULL), Certificate (NULL) @@ -144,27 +145,46 @@ SslContext_t::SslContext_t (bool is_server, const string &privkeyfile, const str InitializeDefaultCredentials(); } - bIsServer = is_server; - pCtx = SSL_CTX_new (is_server ? SSLv23_server_method() : SSLv23_client_method()); + pCtx = SSL_CTX_new (bIsServer ? SSLv23_server_method() : SSLv23_client_method()); if (!pCtx) throw std::runtime_error ("no SSL context"); SSL_CTX_set_options (pCtx, SSL_OP_ALL); - switch (min_version) { - case TLSv1: - SSL_CTX_set_options (pCtx, SSL_OP_NO_SSLv3); - case SSLv3: + + #ifdef SSL_CTRL_CLEAR_OPTIONS + SSL_CTX_clear_options (pCtx, SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_NO_TLSv1); + # ifdef SSL_OP_NO_TLSv1_1 + SSL_CTX_clear_options (pCtx, SSL_OP_NO_TLSv1_1); + # endif + # ifdef SSL_OP_NO_TLSv1_2 + SSL_CTX_clear_options (pCtx, SSL_OP_NO_TLSv1_2); + # endif + #endif + + if (!(protocols & EM_PROTO_SSLv2)) SSL_CTX_set_options (pCtx, SSL_OP_NO_SSLv2); - case SSLv2: - default: - break; - } -#ifdef SSL_MODE_RELEASE_BUFFERS + if (!(protocols & EM_PROTO_SSLv3)) + SSL_CTX_set_options (pCtx, SSL_OP_NO_SSLv3); + + if (!(protocols & EM_PROTO_TLSv1)) + SSL_CTX_set_options (pCtx, SSL_OP_NO_TLSv1); + + #ifdef SSL_OP_NO_TLSv1_1 + if (!(protocols & EM_PROTO_TLSv1_1)) + SSL_CTX_set_options (pCtx, SSL_OP_NO_TLSv1_1); + #endif + + #ifdef SSL_OP_NO_TLSv1_2 + if (!(protocols & EM_PROTO_TLSv1_2)) + SSL_CTX_set_options (pCtx, SSL_OP_NO_TLSv1_2); + #endif + + #ifdef SSL_MODE_RELEASE_BUFFERS SSL_CTX_set_mode (pCtx, SSL_MODE_RELEASE_BUFFERS); -#endif + #endif - if (is_server) { + if (bIsServer) { // The SSL_CTX calls here do NOT allocate memory. int e; if (privkeyfile.length() > 0) @@ -182,9 +202,12 @@ SslContext_t::SslContext_t (bool is_server, const string &privkeyfile, const str assert (e > 0); } - SSL_CTX_set_cipher_list (pCtx, "ALL:!ADH:!LOW:!EXP:!DES-CBC3-SHA:@STRENGTH"); + if (cipherlist.length() > 0) + SSL_CTX_set_cipher_list (pCtx, cipherlist.c_str()); + else + SSL_CTX_set_cipher_list (pCtx, "ALL:!ADH:!LOW:!EXP:!DES-CBC3-SHA:@STRENGTH"); - if (is_server) { + if (bIsServer) { SSL_CTX_sess_set_cache_size (pCtx, 128); SSL_CTX_set_session_id_context (pCtx, (unsigned char*)"eventmachine", 12); } @@ -225,7 +248,7 @@ SslContext_t::~SslContext_t() SslBox_t::SslBox_t ******************/ -SslBox_t::SslBox_t (bool is_server, const string &privkeyfile, const string &certchainfile, bool verify_peer, SslMinVersion min_version, const uintptr_t binding): +SslBox_t::SslBox_t (bool is_server, const string &privkeyfile, const string &certchainfile, bool verify_peer, const string &cipherlist, int protocols, const uintptr_t binding): bIsServer (is_server), bHandshakeCompleted (false), bVerifyPeer (verify_peer), @@ -237,7 +260,7 @@ SslBox_t::SslBox_t (bool is_server, const string &privkeyfile, const string &cer * a new one every time we come here. */ - Context = new SslContext_t (bIsServer, privkeyfile, certchainfile, min_version); + Context = new SslContext_t (bIsServer, privkeyfile, certchainfile, cipherlist, protocols); assert (Context); pbioRead = BIO_new (BIO_s_mem()); diff --git a/ext/ssl.h b/ext/ssl.h index 7ca570ea5..695bda7ca 100644 --- a/ext/ssl.h +++ b/ext/ssl.h @@ -33,7 +33,7 @@ class SslContext_t class SslContext_t { public: - SslContext_t (bool is_server, const string &privkeyfile, const string &certchainfile, SslMinVersion min_version); + SslContext_t (bool is_server, const string &privkeyfile, const string &certchainfile, const string &cipherlist, int protocols); virtual ~SslContext_t(); private: @@ -61,7 +61,7 @@ class SslBox_t class SslBox_t { public: - SslBox_t (bool is_server, const string &privkeyfile, const string &certchainfile, bool verify_peer, SslMinVersion min_version, const uintptr_t binding); + SslBox_t (bool is_server, const string &privkeyfile, const string &certchainfile, bool verify_peer, const string &cipherlist, int protocols, const uintptr_t binding); virtual ~SslBox_t(); int PutPlaintext (const char*, int); diff --git a/lib/em/connection.rb b/lib/em/connection.rb index a065a9700..a614f45fa 100644 --- a/lib/em/connection.rb +++ b/lib/em/connection.rb @@ -380,7 +380,9 @@ def connection_completed # If true, the {#ssl_verify_peer} callback on the {EventMachine::Connection} object is called with each certificate # in the certificate chain provided by the peer. See documentation on {#ssl_verify_peer} for how to use this. # - # @option args [Symbol] :min_version (:sslv2) Minimum SSL protocol version. Possible values are: {:sslv2} (enables all versions), {:sslv3} (disables SSLv2) and {:tlsv1} (disables all the previous, TLSv1 only). + # @option args [String] :cipher_list ("ALL:!ADH:!LOW:!EXP:!DES-CBC3-SHA:@STRENGTH") indicates the available SSL cipher values. Default value is "ALL:!ADH:!LOW:!EXP:!DES-CBC3-SHA:@STRENGTH". Check the format of the OpenSSL cipher string at http://www.openssl.org/docs/apps/ciphers.html#CIPHER_LIST_FORMAT. + # + # @option args [Array] :protocols (TLSv1 TLSv1.1 TLSv1.2) indicates the allowed SSL/TLS protocols versions. Possible values are: {SSLv2}, {SSLv3}, {TLSv1}, {TLSv1.1}, {TLSv1.2}. # # @example Using TLS with EventMachine # @@ -406,8 +408,7 @@ def connection_completed # # @see #ssl_verify_peer def start_tls args={} - priv_key, cert_chain, verify_peer, min_version = args.values_at(:private_key_file, :cert_chain_file, :verify_peer, :min_version) - min_version = min_version.to_s.strip.empty? ? :sslv2 : min_version.to_s.strip.downcase.to_sym + priv_key, cert_chain, verify_peer, cipher_list, protocols = args.values_at(:private_key_file, :cert_chain_file, :verify_peer, :cipher_list, :protocols) [priv_key, cert_chain].each do |file| next if file.nil? or file.empty? @@ -415,7 +416,26 @@ def start_tls args={} "Could not find #{file} for start_tls" unless File.exist? file end - EventMachine::set_tls_parms(@signature, priv_key || '', cert_chain || '', verify_peer, min_version) + protocols_bitmask = 0 + protocols ||= [] + protocols.each do |p| + case p.downcase + when 'sslv2' + protocols_bitmask |= EventMachine::EM_PROTO_SSLv2 + when 'sslv3' + protocols_bitmask |= EventMachine::EM_PROTO_SSLv3 + when 'tlsv1' + protocols_bitmask |= EventMachine::EM_PROTO_TLSv1 + when 'tlsv1.1' + protocols_bitmask |= EventMachine::EM_PROTO_TLSv1_1 + when 'tlsv1.2' + protocols_bitmask |= EventMachine::EM_PROTO_TLSv1_2 + else + raise("Unrecognized SSL/TLS Protocol: #{p}") + end + end + + EventMachine::set_tls_parms(@signature, priv_key || '', cert_chain || '', verify_peer, cipher_list || '', protocols_bitmask) EventMachine::start_tls @signature end diff --git a/tests/test_ssl_min_version.rb b/tests/test_ssl_min_version.rb index ffe73f913..3e144f09a 100644 --- a/tests/test_ssl_min_version.rb +++ b/tests/test_ssl_min_version.rb @@ -1,5 +1,8 @@ require 'em_test_helper' +require 'socket' +require 'openssl' + if EM.ssl? class TestSslMinVersion < Test::Unit::TestCase @@ -20,7 +23,7 @@ def unbind module ClientMinV3 def connection_completed - start_tls(:min_version => :sslv3) + start_tls(:protocols => %w(SSLv3)) end def ssl_handshake_completed @@ -35,7 +38,7 @@ def unbind module ServerMinV3 def post_init - start_tls(:min_version => :sslv3) + start_tls(:protocols => %w(SSLv3)) end def ssl_handshake_completed @@ -45,7 +48,7 @@ def ssl_handshake_completed module ServerTLSv1CaseInsensitive def post_init - start_tls(:min_version => :TLSv1) + start_tls(:protocols => %w(tlsv1)) end def ssl_handshake_completed @@ -55,7 +58,7 @@ def ssl_handshake_completed module ServerAny def post_init - start_tls + start_tls(:protocols => %w(SSLv2 SSLv3 TLSv1 TLSv1.1 TLSv1.2)) end def ssl_handshake_completed @@ -65,10 +68,10 @@ def ssl_handshake_completed def test_any_to_v3 $client_handshake_completed, $server_handshake_completed = false, false - EM.run { + EM.run do EM.start_server("127.0.0.1", 16784, ServerMinV3) EM.connect("127.0.0.1", 16784, ClientAny) - } + end assert($client_handshake_completed) assert($server_handshake_completed) @@ -76,10 +79,10 @@ def test_any_to_v3 def test_case_insensitivity $client_handshake_completed, $server_handshake_completed = false, false - EM.run { + EM.run do EM.start_server("127.0.0.1", 16784, ServerTLSv1CaseInsensitive) EM.connect("127.0.0.1", 16784, ClientAny) - } + end assert($client_handshake_completed) assert($server_handshake_completed) @@ -87,10 +90,10 @@ def test_case_insensitivity def test_v3_to_any $client_handshake_completed, $server_handshake_completed = false, false - EM.run { + EM.run do EM.start_server("127.0.0.1", 16784, ServerAny) EM.connect("127.0.0.1", 16784, ClientMinV3) - } + end assert($client_handshake_completed) assert($server_handshake_completed) @@ -98,10 +101,10 @@ def test_v3_to_any def test_v3_to_v3 $client_handshake_completed, $server_handshake_completed = false, false - EM.run { + EM.run do EM.start_server("127.0.0.1", 16784, ServerMinV3) EM.connect("127.0.0.1", 16784, ClientMinV3) - } + end assert($client_handshake_completed) assert($server_handshake_completed) @@ -109,7 +112,7 @@ def test_v3_to_v3 module ServerV3StopAfterHandshake def post_init - start_tls(:min_version => :sslv3) + start_tls(:protocols => %w(SSLv3)) end def ssl_handshake_completed @@ -120,7 +123,7 @@ def ssl_handshake_completed module ServerTLSv1StopAfterHandshake def post_init - start_tls(:min_version => :tlsv1) + start_tls(:protocols => %w(TLSv1)) end def ssl_handshake_completed @@ -131,33 +134,29 @@ def ssl_handshake_completed def test_v3_with_external_client $server_handshake_completed = false - EM.run { + EM.run do setup_timeout(2) EM.start_server("127.0.0.1", 16784, ServerV3StopAfterHandshake) - EM.defer { - require "socket" - require "openssl" + EM.defer do sock = TCPSocket.new("127.0.0.1", 16784) ctx = OpenSSL::SSL::SSLContext.new - ctx.ssl_version = :TLSv1_client + ctx.ssl_version = :SSLv3_client ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx) ssl.connect ssl.close rescue nil sock.close rescue nil - } - } + end + end assert($server_handshake_completed) end def test_tlsv1_with_external_client $server_handshake_completed = false - EM.run { + EM.run do setup_timeout(2) EM.start_server("127.0.0.1", 16784, ServerTLSv1StopAfterHandshake) - EM.defer { - require "socket" - require "openssl" + EM.defer do sock = TCPSocket.new("127.0.0.1", 16784) ctx = OpenSSL::SSL::SSLContext.new ctx.ssl_version = :TLSv1_client @@ -165,8 +164,8 @@ def test_tlsv1_with_external_client ssl.connect ssl.close rescue nil sock.close rescue nil - } - } + end + end assert($server_handshake_completed) end @@ -174,16 +173,14 @@ def test_tlsv1_with_external_client def test_tlsv1_required_with_external_client $server_handshake_completed = false - EM.run { + EM.run do n = 0 - EM.add_periodic_timer(0.5) { + EM.add_periodic_timer(0.5) do n += 1 (EM.stop rescue nil) if n == 2 - } + end EM.start_server("127.0.0.1", 16784, ServerTLSv1StopAfterHandshake) - EM.defer { - require "socket" - require "openssl" + EM.defer do sock = TCPSocket.new("127.0.0.1", 16784) ctx = OpenSSL::SSL::SSLContext.new ctx.ssl_version = :SSLv3_client @@ -194,8 +191,8 @@ def test_tlsv1_required_with_external_client ssl.close rescue nil sock.close rescue nil EM.stop rescue nil - } - } + end + end assert(!$server_handshake_completed) end From 54d23b504ccd1e2cf09dfa4daac4dab0e3163498 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Mon, 2 Nov 2015 00:50:19 -0800 Subject: [PATCH 058/343] Default SSL protocols are TLSv1 TLSv1.1 TLSv1.2 --- lib/em/connection.rb | 36 ++++++++------- tests/test_ssl_min_version.rb | 84 +++++++++++++++++++++++------------ 2 files changed, 77 insertions(+), 43 deletions(-) diff --git a/lib/em/connection.rb b/lib/em/connection.rb index a614f45fa..fb47bd66f 100644 --- a/lib/em/connection.rb +++ b/lib/em/connection.rb @@ -417,21 +417,27 @@ def start_tls args={} end protocols_bitmask = 0 - protocols ||= [] - protocols.each do |p| - case p.downcase - when 'sslv2' - protocols_bitmask |= EventMachine::EM_PROTO_SSLv2 - when 'sslv3' - protocols_bitmask |= EventMachine::EM_PROTO_SSLv3 - when 'tlsv1' - protocols_bitmask |= EventMachine::EM_PROTO_TLSv1 - when 'tlsv1.1' - protocols_bitmask |= EventMachine::EM_PROTO_TLSv1_1 - when 'tlsv1.2' - protocols_bitmask |= EventMachine::EM_PROTO_TLSv1_2 - else - raise("Unrecognized SSL/TLS Protocol: #{p}") + if protocols.nil? + protocols_bitmask |= EventMachine::EM_PROTO_TLSv1 + protocols_bitmask |= EventMachine::EM_PROTO_TLSv1_1 + protocols_bitmask |= EventMachine::EM_PROTO_TLSv1_2 + else + protocols ||= [] + protocols.each do |p| + case p.downcase + when 'sslv2' + protocols_bitmask |= EventMachine::EM_PROTO_SSLv2 + when 'sslv3' + protocols_bitmask |= EventMachine::EM_PROTO_SSLv3 + when 'tlsv1' + protocols_bitmask |= EventMachine::EM_PROTO_TLSv1 + when 'tlsv1.1' + protocols_bitmask |= EventMachine::EM_PROTO_TLSv1_1 + when 'tlsv1.2' + protocols_bitmask |= EventMachine::EM_PROTO_TLSv1_2 + else + raise("Unrecognized SSL/TLS Protocol: #{p}") + end end end diff --git a/tests/test_ssl_min_version.rb b/tests/test_ssl_min_version.rb index 3e144f09a..5be511520 100644 --- a/tests/test_ssl_min_version.rb +++ b/tests/test_ssl_min_version.rb @@ -6,11 +6,7 @@ if EM.ssl? class TestSslMinVersion < Test::Unit::TestCase - module ClientAny - def connection_completed - start_tls - end - + module Client def ssl_handshake_completed $client_handshake_completed = true close_connection @@ -21,55 +17,65 @@ def unbind end end - module ClientMinV3 - def connection_completed - start_tls(:protocols => %w(SSLv3)) + module Server + def ssl_handshake_completed + $server_handshake_completed = true end + end - def ssl_handshake_completed - $client_handshake_completed = true - close_connection + module ClientAny + include Client + def connection_completed + start_tls(:protocols => %w(sslv2 sslv3 tlsv1 tlsv1.1 tlsv1.2)) end + end - def unbind - EM.stop_event_loop + module ClientDefault + include Client + def connection_completed + start_tls end end - module ServerMinV3 - def post_init + module ClientSSLv3 + include Client + def connection_completed start_tls(:protocols => %w(SSLv3)) end + end - def ssl_handshake_completed - $server_handshake_completed = true + module ServerSSLv3 + include Server + def post_init + start_tls(:protocols => %w(SSLv3)) end end module ServerTLSv1CaseInsensitive + include Server def post_init start_tls(:protocols => %w(tlsv1)) end - - def ssl_handshake_completed - $server_handshake_completed = true - end end module ServerAny + include Server def post_init - start_tls(:protocols => %w(SSLv2 SSLv3 TLSv1 TLSv1.1 TLSv1.2)) + start_tls(:protocols => %w(sslv2 sslv3 tlsv1 tlsv1.1 tlsv1.2)) end + end - def ssl_handshake_completed - $server_handshake_completed = true + module ServerDefault + include Server + def post_init + start_tls end end def test_any_to_v3 $client_handshake_completed, $server_handshake_completed = false, false EM.run do - EM.start_server("127.0.0.1", 16784, ServerMinV3) + EM.start_server("127.0.0.1", 16784, ServerSSLv3) EM.connect("127.0.0.1", 16784, ClientAny) end @@ -92,7 +98,7 @@ def test_v3_to_any $client_handshake_completed, $server_handshake_completed = false, false EM.run do EM.start_server("127.0.0.1", 16784, ServerAny) - EM.connect("127.0.0.1", 16784, ClientMinV3) + EM.connect("127.0.0.1", 16784, ClientSSLv3) end assert($client_handshake_completed) @@ -102,8 +108,30 @@ def test_v3_to_any def test_v3_to_v3 $client_handshake_completed, $server_handshake_completed = false, false EM.run do - EM.start_server("127.0.0.1", 16784, ServerMinV3) - EM.connect("127.0.0.1", 16784, ClientMinV3) + EM.start_server("127.0.0.1", 16784, ServerSSLv3) + EM.connect("127.0.0.1", 16784, ClientSSLv3) + end + + assert($client_handshake_completed) + assert($server_handshake_completed) + end + + def test_any_to_any + $client_handshake_completed, $server_handshake_completed = false, false + EM.run do + EM.start_server("127.0.0.1", 16784, ServerAny) + EM.connect("127.0.0.1", 16784, ClientAny) + end + + assert($client_handshake_completed) + assert($server_handshake_completed) + end + + def test_default_to_default + $client_handshake_completed, $server_handshake_completed = false, false + EM.run do + EM.start_server("127.0.0.1", 16784, ServerDefault) + EM.connect("127.0.0.1", 16784, ClientDefault) end assert($client_handshake_completed) From 755e343f74174bd1460ef0c334c158d1ce2bdec4 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Mon, 2 Nov 2015 01:31:33 -0800 Subject: [PATCH 059/343] Add methods to get current SSL/TLS connection bits, cipher, and protocol --- ext/cmain.cpp | 45 ++++++++++++++++++++++++++++ ext/ed.cpp | 42 ++++++++++++++++++++++++++ ext/ed.h | 6 ++++ ext/eventmachine.h | 3 ++ ext/rubymain.cpp | 62 +++++++++++++++++++++++++++++++++++++++ ext/ssl.cpp | 33 +++++++++++++++++++++ ext/ssl.h | 3 ++ lib/em/connection.rb | 11 +++++++ tests/test_ssl_methods.rb | 19 ++++++++++++ 9 files changed, 224 insertions(+) diff --git a/ext/cmain.cpp b/ext/cmain.cpp index a7c20ea8d..24e3c20a2 100644 --- a/ext/cmain.cpp +++ b/ext/cmain.cpp @@ -482,6 +482,51 @@ extern "C" X509 *evma_get_peer_cert (const uintptr_t binding) } #endif +/****************** +evma_get_cipher_bits +******************/ + +#ifdef WITH_SSL +extern "C" int evma_get_cipher_bits (const uintptr_t binding) +{ + ensure_eventmachine("evma_get_cipher_bits"); + EventableDescriptor *ed = dynamic_cast (Bindable_t::GetObject (binding)); + if (ed) + return ed->GetCipherBits(); + return -1; +} +#endif + +/****************** +evma_get_cipher_name +******************/ + +#ifdef WITH_SSL +extern "C" const char *evma_get_cipher_name (const uintptr_t binding) +{ + ensure_eventmachine("evma_get_cipher_name"); + EventableDescriptor *ed = dynamic_cast (Bindable_t::GetObject (binding)); + if (ed) + return ed->GetCipherName(); + return NULL; +} +#endif + +/****************** +evma_get_cipher_protocol +******************/ + +#ifdef WITH_SSL +extern "C" const char *evma_get_cipher_protocol (const uintptr_t binding) +{ + ensure_eventmachine("evma_get_cipher_protocol"); + EventableDescriptor *ed = dynamic_cast (Bindable_t::GetObject (binding)); + if (ed) + return ed->GetCipherProtocol(); + return NULL; +} +#endif + /******************** evma_accept_ssl_peer ********************/ diff --git a/ext/ed.cpp b/ext/ed.cpp index e77051ff5..c298a5812 100644 --- a/ext/ed.cpp +++ b/ext/ed.cpp @@ -1232,6 +1232,48 @@ X509 *ConnectionDescriptor::GetPeerCert() #endif +/********************************* +ConnectionDescriptor::GetCipherBits +*********************************/ + +#ifdef WITH_SSL +int ConnectionDescriptor::GetCipherBits() +{ + if (!SslBox) + throw std::runtime_error ("SSL/TLS not running on this connection"); + return SslBox->GetCipherBits(); +} +#endif + + +/********************************* +ConnectionDescriptor::GetCipherName +*********************************/ + +#ifdef WITH_SSL +const char *ConnectionDescriptor::GetCipherName() +{ + if (!SslBox) + throw std::runtime_error ("SSL/TLS not running on this connection"); + return SslBox->GetCipherName(); +} +#endif + + +/********************************* +ConnectionDescriptor::GetCipherProtocol +*********************************/ + +#ifdef WITH_SSL +const char *ConnectionDescriptor::GetCipherProtocol() +{ + if (!SslBox) + throw std::runtime_error ("SSL/TLS not running on this connection"); + return SslBox->GetCipherProtocol(); +} +#endif + + /*********************************** ConnectionDescriptor::VerifySslPeer ***********************************/ diff --git a/ext/ed.h b/ext/ed.h index 38bf71355..9445c3823 100644 --- a/ext/ed.h +++ b/ext/ed.h @@ -73,6 +73,9 @@ class EventableDescriptor: public Bindable_t #ifdef WITH_SSL virtual X509 *GetPeerCert() {return NULL;} + virtual int GetCipherBits() {return -1;} + virtual const char *GetCipherName() {return NULL;} + virtual const char *GetCipherProtocol() {return NULL;} #endif virtual uint64_t GetCommInactivityTimeout() {return 0;} @@ -205,6 +208,9 @@ class ConnectionDescriptor: public EventableDescriptor #ifdef WITH_SSL virtual X509 *GetPeerCert(); + virtual int GetCipherBits(); + virtual const char *GetCipherName(); + virtual const char *GetCipherProtocol(); virtual bool VerifySslPeer(const char*); virtual void AcceptSslPeer(); #endif diff --git a/ext/eventmachine.h b/ext/eventmachine.h index 7621b3c5b..288cc3f8e 100644 --- a/ext/eventmachine.h +++ b/ext/eventmachine.h @@ -80,6 +80,9 @@ extern "C" { #ifdef WITH_SSL X509 *evma_get_peer_cert (const uintptr_t binding); + int evma_get_cipher_bits (const uintptr_t binding); + const char *evma_get_cipher_name (const uintptr_t binding); + const char *evma_get_cipher_protocol (const uintptr_t binding); void evma_accept_ssl_peer (const uintptr_t binding); #endif diff --git a/ext/rubymain.cpp b/ext/rubymain.cpp index 446273eb1..760ab8190 100644 --- a/ext/rubymain.cpp +++ b/ext/rubymain.cpp @@ -385,6 +385,65 @@ static VALUE t_get_peer_cert (VALUE self UNUSED, VALUE signature UNUSED) } #endif +/*************** +t_get_cipher_bits +***************/ + +#ifdef WITH_SSL +static VALUE t_get_cipher_bits (VALUE self UNUSED, VALUE signature) +{ + int bits = evma_get_cipher_bits (NUM2BSIG (signature)); + if (bits == -1) + return Qnil; + return INT2NUM (bits); +} +#else +static VALUE t_get_cipher_bits (VALUE self UNUSED, VALUE signature UNUSED) +{ + return Qnil; +} +#endif + +/*************** +t_get_cipher_name +***************/ + +#ifdef WITH_SSL +static VALUE t_get_cipher_name (VALUE self UNUSED, VALUE signature) +{ + const char *protocol = evma_get_cipher_name (NUM2BSIG (signature)); + if (protocol) + return rb_str_new2 (protocol); + + return Qnil; +} +#else +static VALUE t_get_cipher_bits (VALUE self UNUSED, VALUE signature UNUSED) +{ + return Qnil; +} +#endif + +/*************** +t_get_cipher_protocol +***************/ + +#ifdef WITH_SSL +static VALUE t_get_cipher_protocol (VALUE self UNUSED, VALUE signature) +{ + const char *cipher = evma_get_cipher_protocol (NUM2BSIG (signature)); + if (cipher) + return rb_str_new2 (cipher); + + return Qnil; +} +#else +static VALUE t_get_cipher_bits (VALUE self UNUSED, VALUE signature UNUSED) +{ + return Qnil; +} +#endif + /************** t_get_peername **************/ @@ -1319,6 +1378,9 @@ extern "C" void Init_rubyeventmachine() rb_define_module_function (EmModule, "set_tls_parms", (VALUE(*)(...))t_set_tls_parms, 6); rb_define_module_function (EmModule, "start_tls", (VALUE(*)(...))t_start_tls, 1); rb_define_module_function (EmModule, "get_peer_cert", (VALUE(*)(...))t_get_peer_cert, 1); + rb_define_module_function (EmModule, "get_cipher_bits", (VALUE(*)(...))t_get_cipher_bits, 1); + rb_define_module_function (EmModule, "get_cipher_name", (VALUE(*)(...))t_get_cipher_name, 1); + rb_define_module_function (EmModule, "get_cipher_protocol", (VALUE(*)(...))t_get_cipher_protocol, 1); rb_define_module_function (EmModule, "send_data", (VALUE(*)(...))t_send_data, 3); rb_define_module_function (EmModule, "send_datagram", (VALUE(*)(...))t_send_datagram, 5); rb_define_module_function (EmModule, "close_connection", (VALUE(*)(...))t_close_connection, 2); diff --git a/ext/ssl.cpp b/ext/ssl.cpp index 9b547e89a..28f12753d 100644 --- a/ext/ssl.cpp +++ b/ext/ssl.cpp @@ -469,6 +469,39 @@ X509 *SslBox_t::GetPeerCert() return cert; } +/********************** +SslBox_t::GetCipherBits +**********************/ + +int SslBox_t::GetCipherBits() +{ + int bits = -1; + if (pSSL) + SSL_get_cipher_bits(pSSL, &bits); + return bits; +} + +/********************** +SslBox_t::GetCipherName +**********************/ + +const char *SslBox_t::GetCipherName() +{ + if (pSSL) + return SSL_get_cipher_name(pSSL); + return NULL; +} + +/********************** +SslBox_t::GetCipherProtocol +**********************/ + +const char *SslBox_t::GetCipherProtocol() +{ + if (pSSL) + return SSL_get_cipher_version(pSSL); + return NULL; +} /****************** ssl_verify_wrapper diff --git a/ext/ssl.h b/ext/ssl.h index 695bda7ca..9677c0db7 100644 --- a/ext/ssl.h +++ b/ext/ssl.h @@ -73,6 +73,9 @@ class SslBox_t bool IsHandshakeCompleted() {return bHandshakeCompleted;} X509 *GetPeerCert(); + int GetCipherBits(); + const char *GetCipherName(); + const char *GetCipherProtocol(); void Shutdown(); diff --git a/lib/em/connection.rb b/lib/em/connection.rb index fb47bd66f..f39975580 100644 --- a/lib/em/connection.rb +++ b/lib/em/connection.rb @@ -517,6 +517,17 @@ def get_peer_cert EventMachine::get_peer_cert @signature end + def get_cipher_bits + EventMachine::get_cipher_bits @signature + end + + def get_cipher_name + EventMachine::get_cipher_name @signature + end + + def get_cipher_protocol + EventMachine::get_cipher_protocol @signature + end # Sends UDP messages. # diff --git a/tests/test_ssl_methods.rb b/tests/test_ssl_methods.rb index e4c9077c9..c2e5744d9 100644 --- a/tests/test_ssl_methods.rb +++ b/tests/test_ssl_methods.rb @@ -10,6 +10,9 @@ def post_init def ssl_handshake_completed $server_called_back = true $server_cert_value = get_peer_cert + $server_cipher_bits = get_cipher_bits + $server_cipher_name = get_cipher_name + $server_cipher_protocol = get_cipher_protocol end end @@ -21,6 +24,9 @@ def post_init def ssl_handshake_completed $client_called_back = true $client_cert_value = get_peer_cert + $client_cipher_bits = get_cipher_bits + $client_cipher_name = get_cipher_name + $client_cipher_protocol = get_cipher_protocol EM.stop_event_loop end end @@ -30,6 +36,9 @@ def test_ssl_methods omit_if(rbx?) $server_called_back, $client_called_back = false, false $server_cert_value, $client_cert_value = nil, nil + $server_cipher_bits, $client_cipher_bits = nil, nil + $server_cipher_name, $client_cipher_name = nil, nil + $server_cipher_protocol, $client_cipher_protocol = nil, nil EM.run { EM.start_server("127.0.0.1", 9999, ServerHandler) @@ -41,6 +50,16 @@ def test_ssl_methods assert($server_cert_value.is_a?(NilClass)) assert($client_cert_value.is_a?(String)) + + assert($client_cipher_bits > 0) + assert_equal($client_cipher_bits, $server_cipher_bits) + + assert($client_cipher_name.length > 0) + assert_match(/AES/, $client_cipher_name) + assert_equal($client_cipher_name, $server_cipher_name) + + assert_match(/TLS/, $client_cipher_protocol) + assert_equal($client_cipher_protocol, $server_cipher_protocol) end end From d5694239265e6a57d6cfe41381aa411333961e59 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Mon, 2 Nov 2015 01:44:21 -0800 Subject: [PATCH 060/343] Test that an invalid SSL/TLS protocol raises an exception --- tests/test_ssl_min_version.rb | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/test_ssl_min_version.rb b/tests/test_ssl_min_version.rb index 5be511520..de02f0795 100644 --- a/tests/test_ssl_min_version.rb +++ b/tests/test_ssl_min_version.rb @@ -72,6 +72,22 @@ def post_init end end + module InvalidProtocol + include Client + def post_init + start_tls(:protocols => %w(tlsv1 badinput)) + end + end + + def test_invalid_protocol + assert_raises(RuntimeError, "Unrecognized SSL/TLS Protocol: badinput") do + EM.run do + EM.start_server("127.0.0.1", 16784, InvalidProtocol) + EM.connect("127.0.0.1", 16784, InvalidProtocol) + end + end + end + def test_any_to_v3 $client_handshake_completed, $server_handshake_completed = false, false EM.run do From e61f7a61a6fbc23e43deba31b3676f3b1a6b916b Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Tue, 3 Nov 2015 21:07:29 -0800 Subject: [PATCH 061/343] Rename test_ssl_min_version to test_ssl_protocols --- tests/{test_ssl_min_version.rb => test_ssl_protocols.rb} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename tests/{test_ssl_min_version.rb => test_ssl_protocols.rb} (99%) diff --git a/tests/test_ssl_min_version.rb b/tests/test_ssl_protocols.rb similarity index 99% rename from tests/test_ssl_min_version.rb rename to tests/test_ssl_protocols.rb index de02f0795..310a7fc44 100644 --- a/tests/test_ssl_min_version.rb +++ b/tests/test_ssl_protocols.rb @@ -4,7 +4,7 @@ require 'openssl' if EM.ssl? - class TestSslMinVersion < Test::Unit::TestCase + class TestSslProtocols < Test::Unit::TestCase module Client def ssl_handshake_completed From b78c1aa8bcbbbf6945254ed35d4a2f5b320c643a Mon Sep 17 00:00:00 2001 From: Steve Smith Date: Fri, 24 Apr 2015 18:11:50 +0100 Subject: [PATCH 062/343] Allow sni_hostname to be passed to TLS params This allows an additional parameter called `sni_hostname` to be passed to set_tls_parms. When start_tls is called these parameters are passed during the client connection. This in turn sets `SSL_set_tlsext_host_name` as the SSL box is created. --- ext/cmain.cpp | 4 ++-- ext/ed.cpp | 6 ++++-- ext/ed.h | 5 +++-- ext/eventmachine.h | 2 +- ext/rubymain.cpp | 6 +++--- ext/ssl.cpp | 7 ++++++- ext/ssl.h | 2 +- lib/em/connection.rb | 4 ++-- 8 files changed, 22 insertions(+), 14 deletions(-) diff --git a/ext/cmain.cpp b/ext/cmain.cpp index 24e3c20a2..6137f61d1 100644 --- a/ext/cmain.cpp +++ b/ext/cmain.cpp @@ -459,12 +459,12 @@ extern "C" void evma_start_tls (const uintptr_t binding) evma_set_tls_parms ******************/ -extern "C" void evma_set_tls_parms (const uintptr_t binding, const char *privatekey_filename, const char *certchain_filename, int verify_peer, const char *cipherlist, int protocols) +extern "C" void evma_set_tls_parms (const uintptr_t binding, const char *privatekey_filename, const char *certchain_filename, int verify_peer, const char *sni_hostname, const char *cipherlist, int protocols) { ensure_eventmachine("evma_set_tls_parms"); EventableDescriptor *ed = dynamic_cast (Bindable_t::GetObject (binding)); if (ed) - ed->SetTlsParms (privatekey_filename, certchain_filename, (verify_peer == 1 ? true : false), cipherlist, protocols); + ed->SetTlsParms (privatekey_filename, certchain_filename, (verify_peer == 1 ? true : false), sni_hostname, cipherlist, protocols); } /****************** diff --git a/ext/ed.cpp b/ext/ed.cpp index c298a5812..5bc2a678d 100644 --- a/ext/ed.cpp +++ b/ext/ed.cpp @@ -1180,7 +1180,7 @@ void ConnectionDescriptor::StartTls() if (SslBox) throw std::runtime_error ("SSL/TLS already running on connection"); - SslBox = new SslBox_t (bIsServer, PrivateKeyFilename, CertChainFilename, bSslVerifyPeer, CipherList, Protocols, GetBinding()); + SslBox = new SslBox_t (bIsServer, PrivateKeyFilename, CertChainFilename, bSslVerifyPeer, SniHostName, CipherList, Protocols, GetBinding()); _DispatchCiphertext(); } @@ -1197,7 +1197,7 @@ ConnectionDescriptor::SetTlsParms *********************************/ #ifdef WITH_SSL -void ConnectionDescriptor::SetTlsParms (const char *privkey_filename, const char *certchain_filename, bool verify_peer, const char *cipherlist, int protocols) +void ConnectionDescriptor::SetTlsParms (const char *privkey_filename, const char *certchain_filename, bool verify_peer, const char *sni_hostname, const char *cipherlist, int protocols) { if (SslBox) throw std::runtime_error ("call SetTlsParms before calling StartTls"); @@ -1206,6 +1206,8 @@ void ConnectionDescriptor::SetTlsParms (const char *privkey_filename, const char if (certchain_filename && *certchain_filename) CertChainFilename = certchain_filename; bSslVerifyPeer = verify_peer; + if (sni_hostname && *sni_hostname) + SniHostName = sni_hostname; if (cipherlist && *cipherlist) CipherList = cipherlist; Protocols = protocols; diff --git a/ext/ed.h b/ext/ed.h index 9445c3823..410186fb4 100644 --- a/ext/ed.h +++ b/ext/ed.h @@ -69,7 +69,7 @@ class EventableDescriptor: public Bindable_t virtual bool GetSubprocessPid (pid_t*) {return false;} virtual void StartTls() {} - virtual void SetTlsParms (const char *, const char *, bool, const char *, int) {} + virtual void SetTlsParms (const char *, const char *, bool, const char *, const char *, int) {} #ifdef WITH_SSL virtual X509 *GetPeerCert() {return NULL;} @@ -204,7 +204,7 @@ class ConnectionDescriptor: public EventableDescriptor virtual int GetOutboundDataSize() {return OutboundDataSize;} virtual void StartTls(); - virtual void SetTlsParms (const char *, const char *, bool, const char *, int); + virtual void SetTlsParms (const char *, const char *, bool, const char *, const char *, int); #ifdef WITH_SSL virtual X509 *GetPeerCert(); @@ -255,6 +255,7 @@ class ConnectionDescriptor: public EventableDescriptor int Protocols; bool bHandshakeSignaled; bool bSslVerifyPeer; + std::string SniHostName; bool bSslPeerAccepted; #endif diff --git a/ext/eventmachine.h b/ext/eventmachine.h index 288cc3f8e..582f11297 100644 --- a/ext/eventmachine.h +++ b/ext/eventmachine.h @@ -75,7 +75,7 @@ extern "C" { const uintptr_t evma_attach_sd (int sd); const uintptr_t evma_open_datagram_socket (const char *server, int port); const uintptr_t evma_open_keyboard(); - void evma_set_tls_parms (const uintptr_t binding, const char *privatekey_filename, const char *certchain_filenane, int verify_peer, const char *cipherlist, int protocols); + void evma_set_tls_parms (const uintptr_t binding, const char *privatekey_filename, const char *certchain_filenane, int verify_peer, const char *sni_hostname, const char *cipherlist, int protocols); void evma_start_tls (const uintptr_t binding); #ifdef WITH_SSL diff --git a/ext/rubymain.cpp b/ext/rubymain.cpp index 760ab8190..d499aa8fc 100644 --- a/ext/rubymain.cpp +++ b/ext/rubymain.cpp @@ -341,14 +341,14 @@ static VALUE t_start_tls (VALUE self UNUSED, VALUE signature) t_set_tls_parms ***************/ -static VALUE t_set_tls_parms (VALUE self UNUSED, VALUE signature, VALUE privkeyfile, VALUE certchainfile, VALUE verify_peer, VALUE cipherlist, VALUE protocols) +static VALUE t_set_tls_parms (VALUE self UNUSED, VALUE signature, VALUE privkeyfile, VALUE certchainfile, VALUE verify_peer, VALUE snihostname, VALUE cipherlist, VALUE protocols) { /* set_tls_parms takes a series of positional arguments for specifying such things * as private keys and certificate chains. * It's expected that the parameter list will grow as we add more supported features. * ALL of these parameters are optional, and can be specified as empty or NULL strings. */ - evma_set_tls_parms (NUM2BSIG (signature), StringValueCStr (privkeyfile), StringValueCStr (certchainfile), (verify_peer == Qtrue ? 1 : 0), StringValueCStr (cipherlist), NUM2INT (protocols)); + evma_set_tls_parms (NUM2BSIG (signature), StringValueCStr (privkeyfile), StringValueCStr (certchainfile), (verify_peer == Qtrue ? 1 : 0), StringValueCStr (snihostname), StringValueCStr (cipherlist), NUM2INT (protocols)); return Qnil; } @@ -1375,7 +1375,7 @@ extern "C" void Init_rubyeventmachine() rb_define_module_function (EmModule, "stop_tcp_server", (VALUE(*)(...))t_stop_server, 1); rb_define_module_function (EmModule, "start_unix_server", (VALUE(*)(...))t_start_unix_server, 1); rb_define_module_function (EmModule, "attach_sd", (VALUE(*)(...))t_attach_sd, 1); - rb_define_module_function (EmModule, "set_tls_parms", (VALUE(*)(...))t_set_tls_parms, 6); + rb_define_module_function (EmModule, "set_tls_parms", (VALUE(*)(...))t_set_tls_parms, 7); rb_define_module_function (EmModule, "start_tls", (VALUE(*)(...))t_start_tls, 1); rb_define_module_function (EmModule, "get_peer_cert", (VALUE(*)(...))t_get_peer_cert, 1); rb_define_module_function (EmModule, "get_cipher_bits", (VALUE(*)(...))t_get_cipher_bits, 1); diff --git a/ext/ssl.cpp b/ext/ssl.cpp index 28f12753d..58d7d2619 100644 --- a/ext/ssl.cpp +++ b/ext/ssl.cpp @@ -248,7 +248,7 @@ SslContext_t::~SslContext_t() SslBox_t::SslBox_t ******************/ -SslBox_t::SslBox_t (bool is_server, const string &privkeyfile, const string &certchainfile, bool verify_peer, const string &cipherlist, int protocols, const uintptr_t binding): +SslBox_t::SslBox_t (bool is_server, const string &privkeyfile, const string &certchainfile, bool verify_peer, const string &snihostname, const string &cipherlist, int protocols, const uintptr_t binding): bIsServer (is_server), bHandshakeCompleted (false), bVerifyPeer (verify_peer), @@ -271,6 +271,11 @@ SslBox_t::SslBox_t (bool is_server, const string &privkeyfile, const string &cer pSSL = SSL_new (Context->pCtx); assert (pSSL); + + if (snihostname.length() > 0) { + SSL_set_tlsext_host_name (pSSL, snihostname.c_str()); + } + SSL_set_bio (pSSL, pbioRead, pbioWrite); // Store a pointer to the binding signature in the SSL object so we can retrieve it later diff --git a/ext/ssl.h b/ext/ssl.h index 9677c0db7..ba47e85ab 100644 --- a/ext/ssl.h +++ b/ext/ssl.h @@ -61,7 +61,7 @@ class SslBox_t class SslBox_t { public: - SslBox_t (bool is_server, const string &privkeyfile, const string &certchainfile, bool verify_peer, const string &cipherlist, int protocols, const uintptr_t binding); + SslBox_t (bool is_server, const string &privkeyfile, const string &certchainfile, bool verify_peer, const string &snihostname, const string &cipherlist, int protocols, const uintptr_t binding); virtual ~SslBox_t(); int PutPlaintext (const char*, int); diff --git a/lib/em/connection.rb b/lib/em/connection.rb index f39975580..15ba36a6c 100644 --- a/lib/em/connection.rb +++ b/lib/em/connection.rb @@ -408,7 +408,7 @@ def connection_completed # # @see #ssl_verify_peer def start_tls args={} - priv_key, cert_chain, verify_peer, cipher_list, protocols = args.values_at(:private_key_file, :cert_chain_file, :verify_peer, :cipher_list, :protocols) + priv_key, cert_chain, verify_peer, sni_hostname, cipher_list, protocols = args.values_at(:private_key_file, :cert_chain_file, :verify_peer, :sni_hostname, :cipher_list, :protocols) [priv_key, cert_chain].each do |file| next if file.nil? or file.empty? @@ -441,7 +441,7 @@ def start_tls args={} end end - EventMachine::set_tls_parms(@signature, priv_key || '', cert_chain || '', verify_peer, cipher_list || '', protocols_bitmask) + EventMachine::set_tls_parms(@signature, priv_key || '', cert_chain || '', verify_peer, sni_hostname || '', cipher_list || '', protocols_bitmask) EventMachine::start_tls @signature end From e45a4caef23926106428b95abf21c13b6e9634c2 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Tue, 3 Nov 2015 21:31:06 -0800 Subject: [PATCH 063/343] Add get_sni_hostname so that SSL/TLS servers can retrieve the client-requested SNI hostname --- ext/cmain.cpp | 15 +++++++++++ ext/ed.cpp | 14 +++++++++++ ext/ed.h | 2 ++ ext/eventmachine.h | 1 + ext/rubymain.cpp | 21 ++++++++++++++++ ext/ssl.cpp | 13 ++++++++++ ext/ssl.h | 1 + lib/em/connection.rb | 4 +++ tests/test_ssl_extensions.rb | 49 ++++++++++++++++++++++++++++++++++++ 9 files changed, 120 insertions(+) create mode 100644 tests/test_ssl_extensions.rb diff --git a/ext/cmain.cpp b/ext/cmain.cpp index 6137f61d1..3e520a60e 100644 --- a/ext/cmain.cpp +++ b/ext/cmain.cpp @@ -527,6 +527,21 @@ extern "C" const char *evma_get_cipher_protocol (const uintptr_t binding) } #endif +/****************** +evma_get_sni_hostname +******************/ + +#ifdef WITH_SSL +extern "C" const char *evma_get_sni_hostname (const uintptr_t binding) +{ + ensure_eventmachine("evma_get_sni_hostname"); + EventableDescriptor *ed = dynamic_cast (Bindable_t::GetObject (binding)); + if (ed) + return ed->GetSNIHostname(); + return NULL; +} +#endif + /******************** evma_accept_ssl_peer ********************/ diff --git a/ext/ed.cpp b/ext/ed.cpp index 5bc2a678d..acf3f8a19 100644 --- a/ext/ed.cpp +++ b/ext/ed.cpp @@ -1276,6 +1276,20 @@ const char *ConnectionDescriptor::GetCipherProtocol() #endif +/********************************* +ConnectionDescriptor::GetSNIHostname +*********************************/ + +#ifdef WITH_SSL +const char *ConnectionDescriptor::GetSNIHostname() +{ + if (!SslBox) + throw std::runtime_error ("SSL/TLS not running on this connection"); + return SslBox->GetSNIHostname(); +} +#endif + + /*********************************** ConnectionDescriptor::VerifySslPeer ***********************************/ diff --git a/ext/ed.h b/ext/ed.h index 410186fb4..e1c0197bd 100644 --- a/ext/ed.h +++ b/ext/ed.h @@ -76,6 +76,7 @@ class EventableDescriptor: public Bindable_t virtual int GetCipherBits() {return -1;} virtual const char *GetCipherName() {return NULL;} virtual const char *GetCipherProtocol() {return NULL;} + virtual const char *GetSNIHostname() {return NULL;} #endif virtual uint64_t GetCommInactivityTimeout() {return 0;} @@ -211,6 +212,7 @@ class ConnectionDescriptor: public EventableDescriptor virtual int GetCipherBits(); virtual const char *GetCipherName(); virtual const char *GetCipherProtocol(); + virtual const char *GetSNIHostname(); virtual bool VerifySslPeer(const char*); virtual void AcceptSslPeer(); #endif diff --git a/ext/eventmachine.h b/ext/eventmachine.h index 582f11297..c3a4ebcc7 100644 --- a/ext/eventmachine.h +++ b/ext/eventmachine.h @@ -83,6 +83,7 @@ extern "C" { int evma_get_cipher_bits (const uintptr_t binding); const char *evma_get_cipher_name (const uintptr_t binding); const char *evma_get_cipher_protocol (const uintptr_t binding); + const char *evma_get_sni_hostname (const uintptr_t binding); void evma_accept_ssl_peer (const uintptr_t binding); #endif diff --git a/ext/rubymain.cpp b/ext/rubymain.cpp index d499aa8fc..60a4fa0b1 100644 --- a/ext/rubymain.cpp +++ b/ext/rubymain.cpp @@ -444,6 +444,26 @@ static VALUE t_get_cipher_bits (VALUE self UNUSED, VALUE signature UNUSED) } #endif +/*************** +t_get_sni_hostname +***************/ + +#ifdef WITH_SSL +static VALUE t_get_sni_hostname (VALUE self UNUSED, VALUE signature) +{ + const char *sni_hostname = evma_get_sni_hostname (NUM2BSIG (signature)); + if (sni_hostname) + return rb_str_new2 (sni_hostname); + + return Qnil; +} +#else +static VALUE t_get_sni_hostname (VALUE self UNUSED, VALUE signature UNUSED) +{ + return Qnil; +} +#endif + /************** t_get_peername **************/ @@ -1381,6 +1401,7 @@ extern "C" void Init_rubyeventmachine() rb_define_module_function (EmModule, "get_cipher_bits", (VALUE(*)(...))t_get_cipher_bits, 1); rb_define_module_function (EmModule, "get_cipher_name", (VALUE(*)(...))t_get_cipher_name, 1); rb_define_module_function (EmModule, "get_cipher_protocol", (VALUE(*)(...))t_get_cipher_protocol, 1); + rb_define_module_function (EmModule, "get_sni_hostname", (VALUE(*)(...))t_get_sni_hostname, 1); rb_define_module_function (EmModule, "send_data", (VALUE(*)(...))t_send_data, 3); rb_define_module_function (EmModule, "send_datagram", (VALUE(*)(...))t_send_datagram, 5); rb_define_module_function (EmModule, "close_connection", (VALUE(*)(...))t_close_connection, 2); diff --git a/ext/ssl.cpp b/ext/ssl.cpp index 58d7d2619..73c3bd535 100644 --- a/ext/ssl.cpp +++ b/ext/ssl.cpp @@ -508,6 +508,19 @@ const char *SslBox_t::GetCipherProtocol() return NULL; } +/********************** +SslBox_t::GetSNIHostname +**********************/ + +const char *SslBox_t::GetSNIHostname() +{ + #ifdef TLSEXT_NAMETYPE_host_name + if (pSSL) + return SSL_get_servername (pSSL, TLSEXT_NAMETYPE_host_name); + #endif + return NULL; +} + /****************** ssl_verify_wrapper *******************/ diff --git a/ext/ssl.h b/ext/ssl.h index ba47e85ab..77251e570 100644 --- a/ext/ssl.h +++ b/ext/ssl.h @@ -76,6 +76,7 @@ class SslBox_t int GetCipherBits(); const char *GetCipherName(); const char *GetCipherProtocol(); + const char *GetSNIHostname(); void Shutdown(); diff --git a/lib/em/connection.rb b/lib/em/connection.rb index 15ba36a6c..8eba64ef7 100644 --- a/lib/em/connection.rb +++ b/lib/em/connection.rb @@ -529,6 +529,10 @@ def get_cipher_protocol EventMachine::get_cipher_protocol @signature end + def get_sni_hostname + EventMachine::get_sni_hostname @signature + end + # Sends UDP messages. # # This method may be called from any Connection object that refers diff --git a/tests/test_ssl_extensions.rb b/tests/test_ssl_extensions.rb new file mode 100644 index 000000000..b876f5e2a --- /dev/null +++ b/tests/test_ssl_extensions.rb @@ -0,0 +1,49 @@ +require 'em_test_helper' + +require 'socket' +require 'openssl' + +if EM.ssl? + class TestSslExtensions < Test::Unit::TestCase + + module Client + def ssl_handshake_completed + $client_handshake_completed = true + close_connection + end + + def unbind + EM.stop_event_loop + end + + def connection_completed + start_tls(:protocols => %w(tlsv1), :sni_hostname => 'example.com') + end + end + + module Server + def ssl_handshake_completed + $server_handshake_completed = true + $server_sni_hostname = get_sni_hostname + end + + def post_init + start_tls(:protocols => %w(TLSv1)) + end + end + + def test_tlsext_sni_hostname + $server_handshake_completed = false + + EM.run do + EM.start_server("127.0.0.1", 16784, Server) + EM.connect("127.0.0.1", 16784, Client) + end + + assert($server_handshake_completed) + assert_equal('example.com', $server_sni_hostname) + end + end +else + warn "EM built without SSL support, skipping tests in #{__FILE__}" +end From 8135b8c055b104c5022bd2d5807cca32fd491ecd Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Wed, 4 Nov 2015 00:55:06 -0800 Subject: [PATCH 064/343] Typo fixes for ifndef WITH_SSL --- ext/ed.cpp | 2 +- ext/rubymain.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/ed.cpp b/ext/ed.cpp index acf3f8a19..c75d3b731 100644 --- a/ext/ed.cpp +++ b/ext/ed.cpp @@ -1213,7 +1213,7 @@ void ConnectionDescriptor::SetTlsParms (const char *privkey_filename, const char Protocols = protocols; } #else -void ConnectionDescriptor::SetTlsParms (const char *privkey_filename UNUSED, const char *certchain_filename UNUSED, bool verify_peer UNUSED, const char *cipherlist UNUSED, int protocols UNUSED) +void ConnectionDescriptor::SetTlsParms (const char *privkey_filename UNUSED, const char *certchain_filename UNUSED, bool verify_peer UNUSED, const char *sni_hostname UNUSED, const char *cipherlist UNUSED, int protocols UNUSED) { throw std::runtime_error ("Encryption not available on this event-machine"); } diff --git a/ext/rubymain.cpp b/ext/rubymain.cpp index 60a4fa0b1..e396130cc 100644 --- a/ext/rubymain.cpp +++ b/ext/rubymain.cpp @@ -418,7 +418,7 @@ static VALUE t_get_cipher_name (VALUE self UNUSED, VALUE signature) return Qnil; } #else -static VALUE t_get_cipher_bits (VALUE self UNUSED, VALUE signature UNUSED) +static VALUE t_get_cipher_name (VALUE self UNUSED, VALUE signature UNUSED) { return Qnil; } @@ -438,7 +438,7 @@ static VALUE t_get_cipher_protocol (VALUE self UNUSED, VALUE signature) return Qnil; } #else -static VALUE t_get_cipher_bits (VALUE self UNUSED, VALUE signature UNUSED) +static VALUE t_get_cipher_protocol (VALUE self UNUSED, VALUE signature UNUSED) { return Qnil; } From e8c26795bb3b817689ab13e9a5a4291eedae8670 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Tue, 17 Nov 2015 07:30:24 -0800 Subject: [PATCH 065/343] Older versions of MinGW in the Ruby Dev Kit do not provide the getaddrinfo hint flags --- ext/project.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/ext/project.h b/ext/project.h index d105b67aa..de2f450fa 100644 --- a/ext/project.h +++ b/ext/project.h @@ -96,6 +96,15 @@ typedef int SOCKET; #include #include +// Older versions of MinGW in the Ruby Dev Kit do not provide the getaddrinfo hint flags +#ifndef AI_ADDRCONFIG +#define AI_ADDRCONFIG 0x0400 +#endif + +#ifndef AI_NUMERICSERV +#define AI_NUMERICSERV 0x0008 +#endif + // Use the Win32 wrapper library that Ruby owns to be able to close sockets with the close() function #define RUBY_EXPORT #include From 0de31a845fdb428a98d9c64b4e9593fd95e24683 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Tue, 17 Nov 2015 20:29:09 -0800 Subject: [PATCH 066/343] Use WSAGetLastError in pipe.cpp same as ed.cpp --- ext/pipe.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/ext/pipe.cpp b/ext/pipe.cpp index e3d72f210..190734aad 100644 --- a/ext/pipe.cpp +++ b/ext/pipe.cpp @@ -229,6 +229,11 @@ void PipeDescriptor::Write() assert (GetSocket() != INVALID_SOCKET); int bytes_written = write (GetSocket(), output_buffer, nbytes); +#ifdef OS_WIN32 + int e = WSAGetLastError(); +#else + int e = errno; +#endif if (bytes_written > 0) { OutboundDataSize -= bytes_written; @@ -251,10 +256,10 @@ void PipeDescriptor::Write() } else { #ifdef OS_UNIX - if ((errno != EINPROGRESS) && (errno != EWOULDBLOCK) && (errno != EINTR)) + if ((e != EINPROGRESS) && (e != EWOULDBLOCK) && (e != EINTR)) #endif #ifdef OS_WIN32 - if ((errno != WSAEINPROGRESS) && (errno != WSAEWOULDBLOCK)) + if ((e != WSAEINPROGRESS) && (e != WSAEWOULDBLOCK)) #endif Close(); } From e152bc31edabf32b5abbfea4fa2ae67250a2dff8 Mon Sep 17 00:00:00 2001 From: Mark Chambers Date: Sun, 13 Dec 2015 22:16:44 +0000 Subject: [PATCH 067/343] Add start_tls opts :ecdh_curve, :dhparam and :fail_if_no_peer_cert --- ext/cmain.cpp | 4 +- ext/ed.cpp | 15 +++++-- ext/ed.h | 7 ++- ext/eventmachine.h | 2 +- ext/rubymain.cpp | 6 +-- ext/ssl.cpp | 71 +++++++++++++++++++++++++++--- ext/ssl.h | 5 ++- lib/em/connection.rb | 11 ++++- tests/dhparam.pem | 13 ++++++ tests/test_ssl_dhparam.rb | 83 ++++++++++++++++++++++++++++++++++++ tests/test_ssl_ecdh_curve.rb | 79 ++++++++++++++++++++++++++++++++++ tests/test_ssl_verify.rb | 44 +++++++++++++++++++ 12 files changed, 319 insertions(+), 21 deletions(-) create mode 100644 tests/dhparam.pem create mode 100644 tests/test_ssl_dhparam.rb create mode 100644 tests/test_ssl_ecdh_curve.rb diff --git a/ext/cmain.cpp b/ext/cmain.cpp index 3e520a60e..a98611145 100644 --- a/ext/cmain.cpp +++ b/ext/cmain.cpp @@ -459,12 +459,12 @@ extern "C" void evma_start_tls (const uintptr_t binding) evma_set_tls_parms ******************/ -extern "C" void evma_set_tls_parms (const uintptr_t binding, const char *privatekey_filename, const char *certchain_filename, int verify_peer, const char *sni_hostname, const char *cipherlist, int protocols) +extern "C" void evma_set_tls_parms (const uintptr_t binding, const char *privatekey_filename, const char *certchain_filename, int verify_peer, int fail_if_no_peer_cert, const char *sni_hostname, const char *cipherlist, const char *ecdh_curve, const char *dhparam, int protocols) { ensure_eventmachine("evma_set_tls_parms"); EventableDescriptor *ed = dynamic_cast (Bindable_t::GetObject (binding)); if (ed) - ed->SetTlsParms (privatekey_filename, certchain_filename, (verify_peer == 1 ? true : false), sni_hostname, cipherlist, protocols); + ed->SetTlsParms (privatekey_filename, certchain_filename, (verify_peer == 1 ? true : false), (fail_if_no_peer_cert == 1 ? true : false), sni_hostname, cipherlist, ecdh_curve, dhparam, protocols); } /****************** diff --git a/ext/ed.cpp b/ext/ed.cpp index c75d3b731..e980b5260 100644 --- a/ext/ed.cpp +++ b/ext/ed.cpp @@ -1180,7 +1180,7 @@ void ConnectionDescriptor::StartTls() if (SslBox) throw std::runtime_error ("SSL/TLS already running on connection"); - SslBox = new SslBox_t (bIsServer, PrivateKeyFilename, CertChainFilename, bSslVerifyPeer, SniHostName, CipherList, Protocols, GetBinding()); + SslBox = new SslBox_t (bIsServer, PrivateKeyFilename, CertChainFilename, bSslVerifyPeer, bSslFailIfNoPeerCert, SniHostName, CipherList, EcdhCurve, DhParam, Protocols, GetBinding()); _DispatchCiphertext(); } @@ -1197,7 +1197,7 @@ ConnectionDescriptor::SetTlsParms *********************************/ #ifdef WITH_SSL -void ConnectionDescriptor::SetTlsParms (const char *privkey_filename, const char *certchain_filename, bool verify_peer, const char *sni_hostname, const char *cipherlist, int protocols) +void ConnectionDescriptor::SetTlsParms (const char *privkey_filename, const char *certchain_filename, bool verify_peer, bool fail_if_no_peer_cert, const char *sni_hostname, const char *cipherlist, const char *ecdh_curve, const char *dhparam, int protocols) { if (SslBox) throw std::runtime_error ("call SetTlsParms before calling StartTls"); @@ -1205,15 +1205,22 @@ void ConnectionDescriptor::SetTlsParms (const char *privkey_filename, const char PrivateKeyFilename = privkey_filename; if (certchain_filename && *certchain_filename) CertChainFilename = certchain_filename; - bSslVerifyPeer = verify_peer; + bSslVerifyPeer = verify_peer; + bSslFailIfNoPeerCert = fail_if_no_peer_cert; + if (sni_hostname && *sni_hostname) SniHostName = sni_hostname; if (cipherlist && *cipherlist) CipherList = cipherlist; + if (ecdh_curve && *ecdh_curve) + EcdhCurve = ecdh_curve; + if (dhparam && *dhparam) + DhParam = dhparam; + Protocols = protocols; } #else -void ConnectionDescriptor::SetTlsParms (const char *privkey_filename UNUSED, const char *certchain_filename UNUSED, bool verify_peer UNUSED, const char *sni_hostname UNUSED, const char *cipherlist UNUSED, int protocols UNUSED) +void ConnectionDescriptor::SetTlsParms (const char *privkey_filename UNUSED, const char *certchain_filename UNUSED, bool verify_peer UNUSED, bool fail_if_no_peer_cert UNUSED, const char *sni_hostname UNUSED, const char *cipherlist UNUSED, const char *ecdh_curve UNUSED, const char *dhparam UNUSED, int protocols UNUSED) { throw std::runtime_error ("Encryption not available on this event-machine"); } diff --git a/ext/ed.h b/ext/ed.h index e1c0197bd..2e4b4d048 100644 --- a/ext/ed.h +++ b/ext/ed.h @@ -69,7 +69,7 @@ class EventableDescriptor: public Bindable_t virtual bool GetSubprocessPid (pid_t*) {return false;} virtual void StartTls() {} - virtual void SetTlsParms (const char *, const char *, bool, const char *, const char *, int) {} + virtual void SetTlsParms (const char *, const char *, bool, bool, const char *, const char *, const char *, const char *, int) {} #ifdef WITH_SSL virtual X509 *GetPeerCert() {return NULL;} @@ -205,7 +205,7 @@ class ConnectionDescriptor: public EventableDescriptor virtual int GetOutboundDataSize() {return OutboundDataSize;} virtual void StartTls(); - virtual void SetTlsParms (const char *, const char *, bool, const char *, const char *, int); + virtual void SetTlsParms (const char *, const char *, bool, bool, const char *, const char *, const char *, const char *, int); #ifdef WITH_SSL virtual X509 *GetPeerCert(); @@ -254,9 +254,12 @@ class ConnectionDescriptor: public EventableDescriptor std::string CertChainFilename; std::string PrivateKeyFilename; std::string CipherList; + std::string EcdhCurve; + std::string DhParam; int Protocols; bool bHandshakeSignaled; bool bSslVerifyPeer; + bool bSslFailIfNoPeerCert; std::string SniHostName; bool bSslPeerAccepted; #endif diff --git a/ext/eventmachine.h b/ext/eventmachine.h index c3a4ebcc7..70327b0cf 100644 --- a/ext/eventmachine.h +++ b/ext/eventmachine.h @@ -75,7 +75,7 @@ extern "C" { const uintptr_t evma_attach_sd (int sd); const uintptr_t evma_open_datagram_socket (const char *server, int port); const uintptr_t evma_open_keyboard(); - void evma_set_tls_parms (const uintptr_t binding, const char *privatekey_filename, const char *certchain_filenane, int verify_peer, const char *sni_hostname, const char *cipherlist, int protocols); + void evma_set_tls_parms (const uintptr_t binding, const char *privatekey_filename, const char *certchain_filenane, int verify_peer, int fail_if_no_peer_cert, const char *sni_hostname, const char *cipherlist, const char *ecdh_curve, const char *dhparam, int protocols); void evma_start_tls (const uintptr_t binding); #ifdef WITH_SSL diff --git a/ext/rubymain.cpp b/ext/rubymain.cpp index e396130cc..c6e48e06e 100644 --- a/ext/rubymain.cpp +++ b/ext/rubymain.cpp @@ -341,14 +341,14 @@ static VALUE t_start_tls (VALUE self UNUSED, VALUE signature) t_set_tls_parms ***************/ -static VALUE t_set_tls_parms (VALUE self UNUSED, VALUE signature, VALUE privkeyfile, VALUE certchainfile, VALUE verify_peer, VALUE snihostname, VALUE cipherlist, VALUE protocols) +static VALUE t_set_tls_parms (VALUE self UNUSED, VALUE signature, VALUE privkeyfile, VALUE certchainfile, VALUE verify_peer, VALUE fail_if_no_peer_cert, VALUE snihostname, VALUE cipherlist, VALUE ecdh_curve, VALUE dhparam, VALUE protocols) { /* set_tls_parms takes a series of positional arguments for specifying such things * as private keys and certificate chains. * It's expected that the parameter list will grow as we add more supported features. * ALL of these parameters are optional, and can be specified as empty or NULL strings. */ - evma_set_tls_parms (NUM2BSIG (signature), StringValueCStr (privkeyfile), StringValueCStr (certchainfile), (verify_peer == Qtrue ? 1 : 0), StringValueCStr (snihostname), StringValueCStr (cipherlist), NUM2INT (protocols)); + evma_set_tls_parms (NUM2BSIG (signature), StringValueCStr (privkeyfile), StringValueCStr (certchainfile), (verify_peer == Qtrue ? 1 : 0), (fail_if_no_peer_cert == Qtrue ? 1 : 0), StringValueCStr (snihostname), StringValueCStr (cipherlist), StringValueCStr (ecdh_curve), StringValueCStr (dhparam), NUM2INT (protocols)); return Qnil; } @@ -1395,7 +1395,7 @@ extern "C" void Init_rubyeventmachine() rb_define_module_function (EmModule, "stop_tcp_server", (VALUE(*)(...))t_stop_server, 1); rb_define_module_function (EmModule, "start_unix_server", (VALUE(*)(...))t_start_unix_server, 1); rb_define_module_function (EmModule, "attach_sd", (VALUE(*)(...))t_attach_sd, 1); - rb_define_module_function (EmModule, "set_tls_parms", (VALUE(*)(...))t_set_tls_parms, 7); + rb_define_module_function (EmModule, "set_tls_parms", (VALUE(*)(...))t_set_tls_parms, 10); rb_define_module_function (EmModule, "start_tls", (VALUE(*)(...))t_start_tls, 1); rb_define_module_function (EmModule, "get_peer_cert", (VALUE(*)(...))t_get_peer_cert, 1); rb_define_module_function (EmModule, "get_cipher_bits", (VALUE(*)(...))t_get_cipher_bits, 1); diff --git a/ext/ssl.cpp b/ext/ssl.cpp index 73c3bd535..b00387cbc 100644 --- a/ext/ssl.cpp +++ b/ext/ssl.cpp @@ -120,7 +120,7 @@ static void InitializeDefaultCredentials() SslContext_t::SslContext_t **************************/ -SslContext_t::SslContext_t (bool is_server, const string &privkeyfile, const string &certchainfile, const string &cipherlist, int protocols) : +SslContext_t::SslContext_t (bool is_server, const string &privkeyfile, const string &certchainfile, const string &cipherlist, const string &ecdh_curve, const string &dhparam, int protocols) : bIsServer (is_server), pCtx (NULL), PrivateKey (NULL), @@ -185,6 +185,7 @@ SslContext_t::SslContext_t (bool is_server, const string &privkeyfile, const str #endif if (bIsServer) { + // The SSL_CTX calls here do NOT allocate memory. int e; if (privkeyfile.length() > 0) @@ -200,6 +201,61 @@ SslContext_t::SslContext_t (bool is_server, const string &privkeyfile, const str e = SSL_CTX_use_certificate (pCtx, DefaultCertificate); if (e <= 0) ERR_print_errors_fp(stderr); assert (e > 0); + + if (dhparam.length() > 0) { + DH *dh; + BIO *bio; + + bio = BIO_new_file(dhparam.c_str(), "r"); + if (bio == NULL) { + char buf [500]; + snprintf (buf, sizeof(buf)-1, "dhparam: BIO_new_file(%s) failed", dhparam.c_str()); + throw std::runtime_error (buf); + } + + dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); + + if (dh == NULL) { + BIO_free(bio); + char buf [500]; + snprintf (buf, sizeof(buf)-1, "dhparam: PEM_read_bio_DHparams(%s) failed", dhparam.c_str()); + throw new std::runtime_error(buf); + } + + SSL_CTX_set_tmp_dh(pCtx, dh); + + DH_free(dh); + BIO_free(bio); + } + + if (ecdh_curve.length() > 0) { + #if OPENSSL_VERSION_NUMBER >= 0x0090800fL && !defined(OPENSSL_NO_ECDH) + int nid; + EC_KEY *ecdh; + + nid = OBJ_sn2nid((const char *) ecdh_curve.c_str()); + if (nid == 0) { + char buf [200]; + snprintf (buf, sizeof(buf)-1, "ecdh_curve: Unknown curve name: %s", ecdh_curve.c_str()); + throw std::runtime_error (buf); + } + + ecdh = EC_KEY_new_by_curve_name(nid); + if (ecdh == NULL) { + char buf [200]; + snprintf (buf, sizeof(buf)-1, "ecdh_curve: Unable to create: %s", ecdh_curve.c_str()); + throw std::runtime_error (buf); + } + + SSL_CTX_set_options(pCtx, SSL_OP_SINGLE_ECDH_USE); + + SSL_CTX_set_tmp_ecdh(pCtx, ecdh); + + EC_KEY_free(ecdh); + #else + throw std::runtime_error ("No openssl ECDH support"); + #endif + } } if (cipherlist.length() > 0) @@ -248,10 +304,11 @@ SslContext_t::~SslContext_t() SslBox_t::SslBox_t ******************/ -SslBox_t::SslBox_t (bool is_server, const string &privkeyfile, const string &certchainfile, bool verify_peer, const string &snihostname, const string &cipherlist, int protocols, const uintptr_t binding): +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 protocols, const uintptr_t binding): bIsServer (is_server), bHandshakeCompleted (false), bVerifyPeer (verify_peer), + bFailIfNoPeerCert (fail_if_no_peer_cert), pSSL (NULL), pbioRead (NULL), pbioWrite (NULL) @@ -260,7 +317,7 @@ SslBox_t::SslBox_t (bool is_server, const string &privkeyfile, const string &cer * a new one every time we come here. */ - Context = new SslContext_t (bIsServer, privkeyfile, certchainfile, cipherlist, protocols); + Context = new SslContext_t (bIsServer, privkeyfile, certchainfile, cipherlist, ecdh_curve, dhparam, protocols); assert (Context); pbioRead = BIO_new (BIO_s_mem()); @@ -281,8 +338,12 @@ SslBox_t::SslBox_t (bool is_server, const string &privkeyfile, const string &cer // Store a pointer to the binding signature in the SSL object so we can retrieve it later SSL_set_ex_data(pSSL, 0, (void*) binding); - if (bVerifyPeer) - SSL_set_verify(pSSL, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, ssl_verify_wrapper); + if(bVerifyPeer) { + int mode = SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE; + if(bFailIfNoPeerCert) + mode = mode | SSL_VERIFY_FAIL_IF_NO_PEER_CERT; + SSL_set_verify(pSSL, mode, ssl_verify_wrapper); + } if (!bIsServer) SSL_connect (pSSL); diff --git a/ext/ssl.h b/ext/ssl.h index 77251e570..4044ca548 100644 --- a/ext/ssl.h +++ b/ext/ssl.h @@ -33,7 +33,7 @@ class SslContext_t class SslContext_t { public: - SslContext_t (bool is_server, const string &privkeyfile, const string &certchainfile, const string &cipherlist, int protocols); + SslContext_t (bool is_server, const string &privkeyfile, const string &certchainfile, const string &cipherlist, const string &ecdh_curve, const string &dhparam, int protocols); virtual ~SslContext_t(); private: @@ -61,7 +61,7 @@ class SslBox_t class SslBox_t { public: - SslBox_t (bool is_server, const string &privkeyfile, const string &certchainfile, bool verify_peer, const string &snihostname, const string &cipherlist, int protocols, const uintptr_t binding); + 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 protocols, const uintptr_t binding); virtual ~SslBox_t(); int PutPlaintext (const char*, int); @@ -86,6 +86,7 @@ class SslBox_t bool bIsServer; bool bHandshakeCompleted; bool bVerifyPeer; + bool bFailIfNoPeerCert; SSL *pSSL; BIO *pbioRead; BIO *pbioWrite; diff --git a/lib/em/connection.rb b/lib/em/connection.rb index 8eba64ef7..3c1625332 100644 --- a/lib/em/connection.rb +++ b/lib/em/connection.rb @@ -380,8 +380,15 @@ def connection_completed # If true, the {#ssl_verify_peer} callback on the {EventMachine::Connection} object is called with each certificate # in the certificate chain provided by the peer. See documentation on {#ssl_verify_peer} for how to use this. # + # @option args [Boolean] :fail_if_no_peer_cert (false) Used in conjunction with verify_peer. If set the SSL handshake will be terminated if the peer does not provide a certificate. + # + # # @option args [String] :cipher_list ("ALL:!ADH:!LOW:!EXP:!DES-CBC3-SHA:@STRENGTH") indicates the available SSL cipher values. Default value is "ALL:!ADH:!LOW:!EXP:!DES-CBC3-SHA:@STRENGTH". Check the format of the OpenSSL cipher string at http://www.openssl.org/docs/apps/ciphers.html#CIPHER_LIST_FORMAT. # + # @option args [String] :ecdh_curve (nil) The curve for ECDHE ciphers. See available ciphers with 'openssl ecparam -list_curves' + # + # @option args [String] :dhparam (nil) The local path of a file containing DH parameters for EDH ciphers in [PEM format](http://en.wikipedia.org/wiki/Privacy_Enhanced_Mail) See: 'openssl dhparam' + # # @option args [Array] :protocols (TLSv1 TLSv1.1 TLSv1.2) indicates the allowed SSL/TLS protocols versions. Possible values are: {SSLv2}, {SSLv3}, {TLSv1}, {TLSv1.1}, {TLSv1.2}. # # @example Using TLS with EventMachine @@ -408,7 +415,7 @@ def connection_completed # # @see #ssl_verify_peer def start_tls args={} - priv_key, cert_chain, verify_peer, sni_hostname, cipher_list, protocols = args.values_at(:private_key_file, :cert_chain_file, :verify_peer, :sni_hostname, :cipher_list, :protocols) + priv_key, cert_chain, verify_peer, fail_if_no_peer_cert, sni_hostname, cipher_list, ecdh_curve, dhparam, protocols = args.values_at(:private_key_file, :cert_chain_file, :verify_peer, :fail_if_no_peer_cert, :sni_hostname, :cipher_list, :ecdh_curve, :dhparam, :protocols) [priv_key, cert_chain].each do |file| next if file.nil? or file.empty? @@ -441,7 +448,7 @@ def start_tls args={} end end - EventMachine::set_tls_parms(@signature, priv_key || '', cert_chain || '', verify_peer, sni_hostname || '', cipher_list || '', protocols_bitmask) + EventMachine::set_tls_parms(@signature, priv_key || '', cert_chain || '', verify_peer, fail_if_no_peer_cert, sni_hostname || '', cipher_list || '', ecdh_curve || '', dhparam || '', protocols_bitmask) EventMachine::start_tls @signature end diff --git a/tests/dhparam.pem b/tests/dhparam.pem new file mode 100644 index 000000000..b6464ab83 --- /dev/null +++ b/tests/dhparam.pem @@ -0,0 +1,13 @@ +-----BEGIN DH PARAMETERS----- +MIICCAKCAgEAikiatXa5aAteOtd6hOO33npjCvJByD3dwuM8rWzz0DFZdUH9nFJi +b0VvTVweVECb6XZBsrDNLqGQykCrm43swSk5D9XQCGJLxFERD6yk3b90xaeCm3/a +b0Ek5ZVvV73Cc/YbVmpBiOHoTFpUFJLZ7pLMQUSn8y3qUlNcY9/88HuwFi1s1lRM +ovihSRyZMYAuYWOD4yuOuIcroKVjD6gWFrsW9XrALWny6vUXQrhk8Q3rj+wM6ZtE +5afcB0b6ZJtphrDfk3dFjOVG/zVT37VWgrY8GABrpo2ey0W0WIQJ7rDKLaPaI4kc +voOgC2K8Z3kSARZK+jULnwmBeYECz4EH/FF6FEp3GOKtkL4mqEkvh1n5EAesDOGl +iiX+RZXcUrZliSeifSXBTMJWWFVC0fkGIMb9PTZfZHyAC54lpuxzVki0HIyQG9Fs +41zBJ5e8eEoXXlfUYtduUC35YGy2IxSzYLAJE76rctAZSWghha9xLOCDFoLjMr8h +FosKeHKJcBQ0bc8ymOpRIfrYLWhc0Pz2zkpJ/4eYw9t7NYg7S+jP19IE0gUnuM9v +SpoYMtS28tP9nEdokdwuBKD0D3bJEBBefDlHgfXoMgvy9Hivc9PBGGNTNpyFPpwF +sWVAkfhoNMJMC5V7LZsze+lftiDtzVoLSPDa9bO4BK7b/MgwCxfOhGsCAQI= +-----END DH PARAMETERS----- diff --git a/tests/test_ssl_dhparam.rb b/tests/test_ssl_dhparam.rb new file mode 100644 index 000000000..bfca62355 --- /dev/null +++ b/tests/test_ssl_dhparam.rb @@ -0,0 +1,83 @@ +require 'em_test_helper' + +class TestSslDhParam < Test::Unit::TestCase + def setup + $dir = File.dirname(File.expand_path(__FILE__)) + '/' + $dhparam_file = File.join($dir, 'dhparam.pem') + end + + module Client + def connection_completed + start_tls + end + + def ssl_handshake_completed + $client_handshake_completed = true + $client_cipher_name = get_cipher_name + close_connection + end + + def unbind + EM.stop_event_loop + end + end + + module Server + def post_init + start_tls(:dhparam => $dhparam_file, :cipher_list => "DHE,EDH") + end + + def ssl_handshake_completed + $server_handshake_completed = true + $server_cipher_name = get_cipher_name + end + end + + module NoDhServer + def post_init + start_tls(:cipher_list => "DHE,EDH") + end + + def ssl_handshake_completed + $server_handshake_completed = true + $server_cipher_name = get_cipher_name + end + end + + def test_no_dhparam + omit_unless(EM.ssl?) + omit_if(rbx?) + + $client_handshake_completed, $server_handshake_completed = false, false + $server_cipher_name, $client_cipher_name = nil, nil + + EM.run { + EM.start_server("127.0.0.1", 16784, NoDhServer) + EM.connect("127.0.0.1", 16784, Client) + } + + assert(!$client_handshake_completed) + assert(!$server_handshake_completed) + end + + def test_dhparam + omit_unless(EM.ssl?) + omit_if(rbx?) + + $client_handshake_completed, $server_handshake_completed = false, false + $server_cipher_name, $client_cipher_name = nil, nil + + EM.run { + EM.start_server("127.0.0.1", 16784, Server) + EM.connect("127.0.0.1", 16784, Client) + } + + assert($client_handshake_completed) + assert($server_handshake_completed) + + assert($client_cipher_name.length > 0) + assert_equal($client_cipher_name, $server_cipher_name) + + assert_match(/^(DHE|EDH)/, $client_cipher_name) + end +end diff --git a/tests/test_ssl_ecdh_curve.rb b/tests/test_ssl_ecdh_curve.rb new file mode 100644 index 000000000..78fd59067 --- /dev/null +++ b/tests/test_ssl_ecdh_curve.rb @@ -0,0 +1,79 @@ +require 'em_test_helper' + +class TestSslEcdhCurve < Test::Unit::TestCase + module Client + def connection_completed + start_tls + end + + def ssl_handshake_completed + $client_handshake_completed = true + $client_cipher_name = get_cipher_name + close_connection + end + + def unbind + EM.stop_event_loop + end + end + + module Server + def post_init + start_tls(:ecdh_curve => "prime256v1", :cipher_list => "ECDH") + end + + def ssl_handshake_completed + $server_handshake_completed = true + $server_cipher_name = get_cipher_name + end + end + + module NoCurveServer + def post_init + start_tls(:cipher_list => "ECDH") + end + + def ssl_handshake_completed + $server_handshake_completed = true + $server_cipher_name = get_cipher_name + end + end + + def test_no_ecdh_curve + omit_unless(EM.ssl?) + omit_if(rbx?) + + $client_handshake_completed, $server_handshake_completed = false, false + + EM.run { + EM.start_server("127.0.0.1", 16784, NoCurveServer) + EM.connect("127.0.0.1", 16784, Client) + } + + assert(!$client_handshake_completed) + assert(!$server_handshake_completed) + end + + def test_ecdh_curve + omit_unless(EM.ssl?) + omit_if(rbx?) + + $client_handshake_completed, $server_handshake_completed = false, false + $server_cipher_name, $client_cipher_name = nil, nil + + EM.run { + EM.start_server("127.0.0.1", 16784, Server) + EM.connect("127.0.0.1", 16784, Client) + } + + assert($client_handshake_completed) + assert($server_handshake_completed) + + assert($client_cipher_name.length > 0) + assert_equal($client_cipher_name, $server_cipher_name) + + assert_match(/^(AECDH|ECDHE)/, $client_cipher_name) + end + + +end diff --git a/tests/test_ssl_verify.rb b/tests/test_ssl_verify.rb index 8223ef5a6..87bd7ffb8 100644 --- a/tests/test_ssl_verify.rb +++ b/tests/test_ssl_verify.rb @@ -6,6 +6,21 @@ def setup $cert_from_file = File.read($dir+'client.crt') end + module ClientNoCert + def connection_completed + start_tls() + end + + def ssl_handshake_completed + $client_handshake_completed = true + close_connection + end + + def unbind + EM.stop_event_loop + end + end + module Client def connection_completed start_tls(:private_key_file => $dir+'client.key', :cert_chain_file => $dir+'client.crt') @@ -52,6 +67,35 @@ def ssl_handshake_completed end end + module FailServerNoPeerCert + def post_init + start_tls(:verify_peer => true, :fail_if_no_peer_cert => true) + end + + def ssl_verify_peer(cert) + raise "Verify peer should not get called for a client without a certificate" + end + + def ssl_handshake_completed + $server_handshake_completed = true + end + end + + def test_fail_no_peer_cert + omit_unless(EM.ssl?) + omit_if(rbx?) + + $client_handshake_completed, $server_handshake_completed = false, false + + EM.run { + EM.start_server("127.0.0.1", 16784, FailServerNoPeerCert) + EM.connect("127.0.0.1", 16784, ClientNoCert) + } + + assert(!$client_handshake_completed) + assert(!$server_handshake_completed) + end + def test_accept_server omit_unless(EM.ssl?) omit_if(rbx?) From fdcbec7e91a4db5e63c9d56ac95f3fa796fd679c Mon Sep 17 00:00:00 2001 From: Bezruchkin Date: Wed, 2 Dec 2015 17:23:59 -0800 Subject: [PATCH 068/343] Allow setting bind device for UDP sockets - open_datagram_socket no sets the SO_REUSEADDR flag on the socket - the SO_REUSEADDR flag needs to be set before we call bind on the socket --- ext/em.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ext/em.cpp b/ext/em.cpp index 7367e3671..06ff6cc8d 100644 --- a/ext/em.cpp +++ b/ext/em.cpp @@ -1644,6 +1644,12 @@ const uintptr_t EventMachine_t::OpenDatagramSocket (const char *address, int por if (sd == INVALID_SOCKET) goto fail; + { // set the SO_REUSEADDR on the socket before we bind, otherwise it won't work for a second one + int oval = 1; + if (setsockopt (sd, SOL_SOCKET, SO_REUSEADDR, (char*)&oval, sizeof(oval)) < 0) + goto fail; + } + // Set the new socket nonblocking. if (!SetSocketNonblocking (sd)) goto fail; From c1683ead252fa28bcb929e8c27cdf6a1ebd68a14 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Sun, 3 Jan 2016 00:58:55 -0800 Subject: [PATCH 069/343] Try more ways to detect OpenSSL Try more ways to detect OpenSSL in this order: - explicit option --with-ssl-dir=/path/to/openssl - using pkg-config if present on the system - without any paths at all if OpenSSL is in the ether - trying alternate paths, including defaults for MacPorts and HomeBrew. --- ext/extconf.rb | 76 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 56 insertions(+), 20 deletions(-) diff --git a/ext/extconf.rb b/ext/extconf.rb index c6c9f2b61..f80576283 100644 --- a/ext/extconf.rb +++ b/ext/extconf.rb @@ -1,6 +1,9 @@ require 'fileutils' require 'mkmf' +# Eager check devs tools +have_devel? if respond_to?(:have_devel?) + def check_libs libs = [], fatal = false libs.all? { |lib| have_library(lib) || (abort("could not find library: #{lib}") if fatal) } end @@ -22,34 +25,58 @@ def append_library(libs, lib) libs + " " + format(LIBARG, lib) end -def manual_ssl_config - ssl_libs_heads_args = { - :unix => [%w[ssl crypto], %w[openssl/ssl.h openssl/err.h]], - :mswin => [%w[ssleay32 libeay32], %w[openssl/ssl.h openssl/err.h]], - } +SSL_HEADS = %w(openssl/ssl.h openssl/err.h) +SSL_LIBS = case RUBY_PLATFORM +when /mswin|mingw|bccwin/ ; %w(ssleay32 libeay32) +else ; %w(crypto ssl) +end - dc_flags = ['ssl'] - dc_flags += ["#{ENV['OPENSSL']}/include", ENV['OPENSSL']] if /linux/ =~ RUBY_PLATFORM and ENV['OPENSSL'] +def dir_config_wrapper(pretty_name, name, idefault=nil, ldefault=nil) + inc, lib = dir_config(name, idefault, ldefault) + if inc && lib + # TODO: Remove when 2.0.0 is the minimum supported version + # Ruby versions not incorporating the mkmf fix at + # https://bugs.ruby-lang.org/projects/ruby-trunk/repository/revisions/39717 + # do not properly search for lib directories, and must be corrected + unless lib && lib[-3, 3] == 'lib' + @libdir_basename = 'lib' + inc, lib = dir_config(name, idefault, ldefault) + end + unless idefault && ldefault + abort "-----\nCannot find #{pretty_name} include path #{inc}\n-----" unless inc && inc.split(File::PATH_SEPARATOR).any? { |dir| File.directory?(dir) } + abort "-----\nCannot find #{pretty_name} library path #{lib}\n-----" unless lib && lib.split(File::PATH_SEPARATOR).any? { |dir| File.directory?(dir) } + warn "-----\nUsing #{pretty_name} in path #{File.dirname inc}\n-----" + end + true + end +end - libs, heads = case RUBY_PLATFORM - when /mswin|mingw|bccwin/ ; ssl_libs_heads_args[:mswin] - else ssl_libs_heads_args[:unix] +def dir_config_search(pretty_name, name, paths, &b) + paths.each do |p| + if dir_config_wrapper('OpenSSL', 'ssl', p + '/include', p + '/lib') && yield + warn "-----\nFound #{pretty_name} in path #{p}\n-----" + return true + end end - dir_config(*dc_flags) - check_libs(libs) and check_heads(heads) end -# Eager check devs tools -have_devel? if respond_to?(:have_devel?) +def pkg_config_wrapper(pretty_name, name) + cflags, ldflags, libs = pkg_config(name) + unless [cflags, ldflags, libs].any?(&:nil?) || [cflags, ldflags, libs].any?(&:empty?) + warn "-----\nUsing #{pretty_name} from pkg-config #{cflags} && #{ldflags} && #{libs}\n-----" + true + end +end if ENV['CROSS_COMPILING'] - openssl_version = ENV.fetch("OPENSSL_VERSION", "1.0.1i") + openssl_version = ENV.fetch("OPENSSL_VERSION", "1.0.2e") openssl_dir = File.expand_path("~/.rake-compiler/builds/openssl-#{openssl_version}/") if File.exist?(openssl_dir) FileUtils.mkdir_p Dir.pwd+"/openssl/" FileUtils.cp Dir[openssl_dir+"/include/openssl/*.h"], Dir.pwd+"/openssl/", :verbose => true FileUtils.cp Dir[openssl_dir+"/lib*.a"], Dir.pwd, :verbose => true $INCFLAGS << " -I#{Dir.pwd}" # for the openssl headers + add_define "WITH_SSL" else STDERR.puts STDERR.puts "**************************************************************************************" @@ -58,11 +85,20 @@ def manual_ssl_config STDERR.puts "**************************************************************************************" STDERR.puts end -end - -# Try to use pkg_config first, fixes #73 -if (!ENV['CROSS_COMPILING'] and pkg_config('openssl')) || manual_ssl_config - add_define "WITH_SSL" +elsif dir_config_wrapper('OpenSSL', 'ssl') + # If the user has provided a --with-ssl-dir argument, we must respect it or fail. + add_define 'WITH_SSL' if check_libs(SSL_LIBS) && check_heads(SSL_HEADS) +elsif pkg_config_wrapper('OpenSSL', 'openssl') + # If we can detect OpenSSL by pkg-config, use it as the next-best option + add_define 'WITH_SSL' if check_libs(SSL_LIBS) && check_heads(SSL_HEADS) +elsif check_libs(SSL_LIBS) && check_heads(SSL_HEADS) + # If we don't even need any options to find a usable OpenSSL, go with it + add_define 'WITH_SSL' +elsif dir_config_search('OpenSSL', 'ssl', ['/usr/local', '/opt/local', '/usr/local/opt/openssl']) do + check_libs(SSL_LIBS) && check_heads(SSL_HEADS) + end + # Finally, look for OpenSSL in alternate locations including MacPorts and HomeBrew + add_define 'WITH_SSL' end add_define 'BUILD_FOR_RUBY' From b1eafab6b89459ba0be9105acdcec500b8f6016b Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Sat, 9 Jan 2016 22:00:18 -0800 Subject: [PATCH 070/343] Tidy up the extconf feature detection macros --- ext/ed.cpp | 2 +- ext/em.cpp | 2 +- ext/em.h | 4 ++-- ext/extconf.rb | 31 ++++++++++++++++++------------- 4 files changed, 22 insertions(+), 17 deletions(-) diff --git a/ext/ed.cpp b/ext/ed.cpp index c75d3b731..d04e9c725 100644 --- a/ext/ed.cpp +++ b/ext/ed.cpp @@ -1524,7 +1524,7 @@ void AcceptorDescriptor::Read() int accept_count = EventMachine_t::GetSimultaneousAcceptCount(); for (int i=0; i < accept_count; i++) { -#if defined(HAVE_SOCK_CLOEXEC) && defined(HAVE_ACCEPT4) +#if defined(HAVE_CONST_SOCK_CLOEXEC) && defined(HAVE_ACCEPT4) SOCKET sd = accept4 (GetSocket(), (struct sockaddr*)&pin, &addrlen, SOCK_CLOEXEC); if (sd == INVALID_SOCKET) { // We may be running in a kernel where diff --git a/ext/em.cpp b/ext/em.cpp index 06ff6cc8d..f8ff5407d 100644 --- a/ext/em.cpp +++ b/ext/em.cpp @@ -912,7 +912,7 @@ SelectData_t::~SelectData_t() _SelectDataSelect *****************/ -#if defined(HAVE_TBR) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL) +#if defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL) static VALUE _SelectDataSelect (void *v) { SelectData_t *sd = (SelectData_t*)v; diff --git a/ext/em.h b/ext/em.h index d75186eac..c4b91d36d 100644 --- a/ext/em.h +++ b/ext/em.h @@ -37,7 +37,7 @@ See the file COPYING for complete licensing information. #include #endif - #if defined(HAVE_RBTRAP) + #if defined(HAVE_RB_TRAP_IMMEDIATE) #include #elif defined(HAVE_RB_ENABLE_INTERRUPT) extern "C" { @@ -69,7 +69,7 @@ See the file COPYING for complete licensing information. #define EmSelect select #endif -#if !defined(HAVE_RB_FDSET_T) +#if !defined(HAVE_TYPE_RB_FDSET_T) #define fd_check(n) (((n) < FD_SETSIZE) ? 1 : 0*fprintf(stderr, "fd %d too large for select\n", (n))) // These definitions are cribbed from include/ruby/intern.h in Ruby 1.9.3, // with this change: any macros that read or write the nth element of an diff --git a/ext/extconf.rb b/ext/extconf.rb index f80576283..00cfa992c 100644 --- a/ext/extconf.rb +++ b/ext/extconf.rb @@ -102,22 +102,27 @@ def pkg_config_wrapper(pretty_name, name) end add_define 'BUILD_FOR_RUBY' -add_define 'HAVE_RBTRAP' if have_var('rb_trap_immediate', ['ruby.h', 'rubysig.h']) -add_define "HAVE_TBR" if have_func('rb_thread_blocking_region')# and have_macro('RUBY_UBF_IO', 'ruby.h') -add_define "HAVE_RB_THREAD_CALL_WITHOUT_GVL" if have_header('ruby/thread.h') && have_func('rb_thread_call_without_gvl', 'ruby/thread.h') -add_define "HAVE_INOTIFY" if inotify = have_func('inotify_init', 'sys/inotify.h') -add_define "HAVE_OLD_INOTIFY" if !inotify && have_macro('__NR_inotify_init', 'sys/syscall.h') -add_define 'HAVE_WRITEV' if have_func('writev', 'sys/uio.h') -add_define 'HAVE_RB_THREAD_FD_SELECT' if have_func('rb_thread_fd_select') -add_define 'HAVE_RB_FDSET_T' if have_type('rb_fdset_t', 'ruby/intern.h') -add_define 'HAVE_PIPE2' if have_func('pipe2', 'unistd.h') -add_define 'HAVE_ACCEPT4' if have_func('accept4', 'sys/socket.h') -add_define 'HAVE_SOCK_CLOEXEC' if have_const('SOCK_CLOEXEC', 'sys/socket.h') +# Ruby features: + +have_var('rb_trap_immediate', ['ruby.h', 'rubysig.h']) +have_func('rb_thread_blocking_region') +have_func('rb_thread_call_without_gvl', 'ruby/thread.h') +have_func('rb_thread_fd_select') +have_type('rb_fdset_t', 'ruby/intern.h') have_func('rb_wait_for_single_fd') have_func('rb_enable_interrupt') have_func('rb_time_new') +# System features: + +add_define('HAVE_INOTIFY') if inotify = have_func('inotify_init', 'sys/inotify.h') +add_define('HAVE_OLD_INOTIFY') if !inotify && have_macro('__NR_inotify_init', 'sys/syscall.h') +have_func('writev', 'sys/uio.h') +have_func('pipe2', 'unistd.h') +have_func('accept4', 'sys/socket.h') +have_const('SOCK_CLOEXEC', 'sys/socket.h') + # Minor platform details between *nix and Windows: if RUBY_PLATFORM =~ /(mswin|mingw|bccwin)/ @@ -129,7 +134,7 @@ def pkg_config_wrapper(pretty_name, name) OS_UNIX = true add_define 'OS_UNIX' - add_define "HAVE_KQUEUE" if have_header("sys/event.h") and have_header("sys/queue.h") + add_define "HAVE_KQUEUE" if have_header("sys/event.h") && have_header("sys/queue.h") end # Adjust number of file descriptors (FD) on Windows @@ -161,7 +166,7 @@ def pkg_config_wrapper(pretty_name, name) # If Ruby was compiled for 32-bits, then select() can only handle 1024 fds # There is an alternate function, select_large_fdset, that supports more. - add_define 'HAVE_SELECT_LARGE_FDSET' if have_func('select_large_fdset', 'sys/select.h') + have_func('select_large_fdset', 'sys/select.h') if CONFIG['CC'] == 'cc' && ( `cc -flags 2>&1` =~ /Sun/ || # detect SUNWspro compiler From e72063896e686e6a645efa0ce3a215c0da2a1414 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Sat, 9 Jan 2016 22:00:37 -0800 Subject: [PATCH 071/343] Travis CI updates for new Rubies etc. Add Ruby 2.3 to the matrix Update Mac builds to Mac OS X 10.11 Remove JRuby from tests until it's in better shape Ruby 2.0 consistently fails a few reactor tests --- .travis.yml | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/.travis.yml b/.travis.yml index 166d7d74d..42ed97dc6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,27 +1,37 @@ bundler_args: --without documentation script: bundle exec rake compile test +before_install: + - 'if [[ "$TRAVIS_OS_NAME" = "osx" ]]; then brew install openssl; fi' env: global: - TESTOPTS=-v language: ruby sudo: false rvm: - - 1.8.7 - - 1.9.2 - - 1.9.3 - - 2.0.0 - - 2.1 + - 2.3.0 - 2.2 + - 2.1 + - 2.0.0 + - 1.9.3 + - 1.9.2 + - 1.8.7 - ruby-head - rbx - - jruby-1.7 - - jruby-9 + - rbx-2 +# - jruby-1.7 +# - jruby-9 matrix: allow_failures: - - rvm: ruby-head - rvm: rbx - - rvm: jruby-1.7 - - rvm: jruby-9 + - rvm: rbx-2 + - rvm: ruby-head + - rvm: 2.0.0 + - rvm: 2.0.0 + os: osx + osx_image: xcode7.2 +# - rvm: jruby-1.7 +# - rvm: jruby-9 include: - rvm: 2.0.0 os: osx + osx_image: xcode7.2 From 6ba690d6f74b4d98f22657f1d7e08e44c6fd9754 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Wed, 13 Jan 2016 00:09:19 -0800 Subject: [PATCH 072/343] Changelog for v1.0.9 --- CHANGELOG.md | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f6f6efec..093935210 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Changelog +## 1.0.9 (January 13, 2016) +* Try more ways to detect OpenSSL [#602, #643, #661, #663, #668, #669] +* Use WSAGetLastError in pipe.cpp same as ed.cpp [#659] +* Test compiler flags with the C++ compiler and add them to CXXFLAGS [#634, #651] +* Restore silent-fail on unsupported EM.epoll and EM.kqueue [#638, #649] +* getDescriptorByFileno deprecated in JRuby 1.7.x, removed in JRuby 9000 [#642, #648] +* Add -Wno-address always-true because on Windows rb_fd_select [#578] +* Remove the WITHOUT_SSL constant [#578] +* Fix SSL error when the server replies a TLS Alert to our ClientHello [#544, #653] +* Use WSAStringToAddress in lieu of inet_pton for IPv6 address detection on Windows [#595, #632] +* Fix nasty TCP/IPv6 bug [#595, #632] +* Use select_large_fdset on Solaris [#611, #625] +* Detect the Solaris Studio compiler [#611, #625] +* Throw a message with strerror included [#136, #621] + ## 1.0.8 (August 6, 2015) * fix kqueue assertion failed, postpone ArmKqueueWriter until all events are processed [#51, #176, #372, #401, #619] * fix Rubinius GC, crank the machine from Ruby space when running Rubinius [#201, #202, #617] @@ -10,7 +25,7 @@ * fix for compilation with SSL on windows [#601] * open file descriptors and sockets with O_CLOEXEC where possible [#298, #488, #591] * fix SmtpClient: send second EHLO after STARTTLS. [#589] -* fix nul-terminated strings in C, use StringValueCStr instead of StringValuePtr +* fix nul-terminated strings in C, use StringValueCStr instead of StringValuePtr ## 1.0.7 (February 10, 2015) * fix delay in kqueue/epoll reactor shutdown when timers exist [#587] From 96bdc4931d72bc810ec4d60824b7b44b2a5543ca Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Wed, 13 Jan 2016 06:59:46 -0800 Subject: [PATCH 073/343] Fix bug in OpenSSL path detection Return false from dir_config_search if it exhausts the search paths without finding OpenSSL. --- ext/extconf.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/ext/extconf.rb b/ext/extconf.rb index 00cfa992c..ea0efad17 100644 --- a/ext/extconf.rb +++ b/ext/extconf.rb @@ -58,6 +58,7 @@ def dir_config_search(pretty_name, name, paths, &b) return true end end + false end def pkg_config_wrapper(pretty_name, name) From ff859199ba590389f52471bca833de9e2377dbbf Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Wed, 13 Jan 2016 15:42:56 -0800 Subject: [PATCH 074/343] On Windows, use WSAECONNABORTED instead of EPROTO to indicate connection closure due to SSL handshake error --- ext/ed.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ext/ed.cpp b/ext/ed.cpp index d04e9c725..e7fbc1182 100644 --- a/ext/ed.cpp +++ b/ext/ed.cpp @@ -878,7 +878,12 @@ void ConnectionDescriptor::_DispatchInboundData (const char *buffer, unsigned lo // If our SSL handshake had a problem, shut down the connection. if (s == -2) { + #ifdef OS_UNIX UnbindReasonCode = EPROTO; + #endif + #ifdef OS_WIN32 + UnbindReasonCode = WSAECONNABORTED; + #endif ScheduleClose(false); return; } From cbd3773636272c11447a8f9a81605bdcd42fe400 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Thu, 14 Jan 2016 00:26:06 -0800 Subject: [PATCH 075/343] Changelog for v1.0.9.1 --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 093935210..41d2c39a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 1.0.9.1 (January 14, 2016) +* Fix EPROTO not defined on Windows [#676] +* Fix missing cast to struct sockaddr * [#671] +* Fix bug in OpenSSL path detection [#675] + ## 1.0.9 (January 13, 2016) * Try more ways to detect OpenSSL [#602, #643, #661, #663, #668, #669] * Use WSAGetLastError in pipe.cpp same as ed.cpp [#659] From 5e8606edcf930fbe09cc232fe2880bff9ab3e696 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Sun, 24 Jan 2016 22:45:53 -0800 Subject: [PATCH 076/343] Bump version to 1.2.0.dev.1 --- lib/em/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/em/version.rb b/lib/em/version.rb index b25b3413c..cb119ecc3 100644 --- a/lib/em/version.rb +++ b/lib/em/version.rb @@ -1,3 +1,3 @@ module EventMachine - VERSION = "1.0.8" + VERSION = "1.2.0.dev.1" end From 9f731e27e6cbd54302b7944e5c0559aded7dfa23 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Tue, 11 Aug 2015 00:41:34 -0700 Subject: [PATCH 077/343] Use rake-compiler-dock to cross-compile gems for Windows x86 and x64 --- eventmachine.gemspec | 19 ++++++++++--------- ext/extconf.rb | 4 ++++ rakelib/package.rake | 13 +++++++++++-- 3 files changed, 25 insertions(+), 11 deletions(-) diff --git a/eventmachine.gemspec b/eventmachine.gemspec index 3c0edf77b..01a9c9d4a 100644 --- a/eventmachine.gemspec +++ b/eventmachine.gemspec @@ -1,26 +1,26 @@ -# -*- encoding: utf-8 -*- -$:.unshift File.expand_path("../lib", __FILE__) -require "em/version" - +require File.expand_path('../lib/em/version', __FILE__) Gem::Specification.new do |s| s.name = 'eventmachine' s.version = EventMachine::VERSION s.homepage = 'http://rubyeventmachine.com' - s.rubyforge_project = 'eventmachine' s.licenses = ["Ruby", "GPL"] s.authors = ["Francis Cianfrocca", "Aman Gupta"] s.email = ["garbagecat10@gmail.com", "aman@tmm1.net"] - s.files = `git ls-files`.split("\n") + s.files = `git ls-files README.md CHANGELOG.md GNU LICENSE rakelib ext java lib docs`.split + s.test_files = `git ls-files tests examples`.split + s.extensions = ["ext/extconf.rb", "ext/fastfilereader/extconf.rb"] s.add_development_dependency 'test-unit', '~> 2.0' s.add_development_dependency 'rake-compiler', '~> 0.9.5' + s.add_development_dependency 'rake-compiler-dock', '~> 0.5.1' s.summary = 'Ruby/EventMachine library' - s.description = "EventMachine implements a fast, single-threaded engine for arbitrary network + s.description = <<-EOT +EventMachine implements a fast, single-threaded engine for arbitrary network communications. It's extremely easy to use in Ruby. EventMachine wraps all interactions with IP sockets, allowing programs to concentrate on the implementation of network protocols. It can be used to create both network @@ -29,8 +29,9 @@ to specify the IP address and port, and provide a Module that implements the communications protocol. Implementations of several standard network protocols are provided with the package, primarily to serve as examples. The real goal of EventMachine is to enable programs to easily interface with other programs -using TCP/IP, especially if custom protocols are required." +using TCP/IP, especially if custom protocols are required. +EOT s.rdoc_options = ["--title", "EventMachine", "--main", "README.md", "-x", "lib/em/version", "-x", "lib/jeventmachine"] - s.extra_rdoc_files = ["README.md"] + `git ls-files -- docs/*`.split("\n") + s.extra_rdoc_files = `git ls-files README.md docs`.split end diff --git a/ext/extconf.rb b/ext/extconf.rb index ea0efad17..423d26c2b 100644 --- a/ext/extconf.rb +++ b/ext/extconf.rb @@ -161,6 +161,10 @@ def pkg_config_wrapper(pretty_name, name) $defs.push "-GR" end + # Newer versions of Ruby already define _WIN32_WINNT, which is needed + # to get access to newer POSIX networking functions (e.g. getaddrinfo) + add_define '_WIN32_WINNT=0x0501' unless have_func('getaddrinfo') + when /solaris/ add_define 'OS_SOLARIS8' check_libs(%w[nsl socket], true) diff --git a/rakelib/package.rake b/rakelib/package.rake index 9e7d5d0a9..00419d0bd 100644 --- a/rakelib/package.rake +++ b/rakelib/package.rake @@ -25,7 +25,7 @@ else def setup_cross_compilation(ext) unless RUBY_PLATFORM =~ /mswin|mingw/ ext.cross_compile = true - ext.cross_platform = ['x86-mingw32', 'x86-mswin32-60'] + ext.cross_platform = ['x86-mingw32', 'x64-mingw32'] end end def hack_cross_compilation(ext) @@ -106,6 +106,15 @@ task :devkit do end end -if RUBY_PLATFORM =~ /mingw|mswin/ then +if RUBY_PLATFORM =~ /mingw|mswin/ Rake::Task['compile'].prerequisites.unshift 'devkit' end + +desc "Build binary gems for Windows with rake-compiler-dock" +task 'gem:windows' do + require 'rake_compiler_dock' + RakeCompilerDock.sh <<-EOT + RUBY_CC_VERSION="${RUBY_CC_VERSION//1.8.7/}" + bundle && rake cross native gem + EOT +end From 2426f8667383d07543c2fd11dfd2218abcc4a5f8 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Sun, 7 Feb 2016 19:50:36 -0800 Subject: [PATCH 078/343] The license is Ruby or GPL 2.0 specifically --- eventmachine.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eventmachine.gemspec b/eventmachine.gemspec index 01a9c9d4a..8b88e1fe5 100644 --- a/eventmachine.gemspec +++ b/eventmachine.gemspec @@ -4,7 +4,7 @@ Gem::Specification.new do |s| s.name = 'eventmachine' s.version = EventMachine::VERSION s.homepage = 'http://rubyeventmachine.com' - s.licenses = ["Ruby", "GPL"] + s.licenses = ['Ruby', 'GPL-2.0'] s.authors = ["Francis Cianfrocca", "Aman Gupta"] s.email = ["garbagecat10@gmail.com", "aman@tmm1.net"] From 61cf05db437f344848c65d1eca21ce8befafe204 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Tue, 9 Feb 2016 08:12:15 -0800 Subject: [PATCH 079/343] Whitespace --- ext/rubymain.cpp | 4 ++-- ext/ssl.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ext/rubymain.cpp b/ext/rubymain.cpp index c6e48e06e..220db0cca 100644 --- a/ext/rubymain.cpp +++ b/ext/rubymain.cpp @@ -532,9 +532,9 @@ static VALUE t_get_subprocess_status (VALUE self UNUSED, VALUE signature) rb_iv_set(proc_status, "@pid", INT2FIX(pid)); if (WIFEXITED(status)) { rb_iv_set(proc_status, "@status", INT2FIX(WEXITSTATUS(status))); - } else if(WIFSIGNALED(status)) { + } else if (WIFSIGNALED(status)) { rb_iv_set(proc_status, "@termsig", INT2FIX(WTERMSIG(status))); - } else if(WIFSTOPPED(status)){ + } else if (WIFSTOPPED(status)){ rb_iv_set(proc_status, "@stopsig", INT2FIX(WSTOPSIG(status))); } #endif diff --git a/ext/ssl.cpp b/ext/ssl.cpp index b00387cbc..b4262fff1 100644 --- a/ext/ssl.cpp +++ b/ext/ssl.cpp @@ -338,9 +338,9 @@ SslBox_t::SslBox_t (bool is_server, const string &privkeyfile, const string &cer // Store a pointer to the binding signature in the SSL object so we can retrieve it later SSL_set_ex_data(pSSL, 0, (void*) binding); - if(bVerifyPeer) { + if (bVerifyPeer) { int mode = SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE; - if(bFailIfNoPeerCert) + if (bFailIfNoPeerCert) mode = mode | SSL_VERIFY_FAIL_IF_NO_PEER_CERT; SSL_set_verify(pSSL, mode, ssl_verify_wrapper); } From ec8bf17584acca01b41b972d754c1e88eed1df52 Mon Sep 17 00:00:00 2001 From: Timothy Cyrus Date: Tue, 9 Feb 2016 11:34:38 -0500 Subject: [PATCH 080/343] Update README.md Change PNG Badge to SVG --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 584cca8c0..6339ea1dd 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# About EventMachine [![Code Climate](https://codeclimate.com/github/eventmachine/eventmachine.png)](https://codeclimate.com/github/eventmachine/eventmachine) +# About EventMachine [![Code Climate](https://codeclimate.com/github/eventmachine/eventmachine.svg)](https://codeclimate.com/github/eventmachine/eventmachine) ## What is EventMachine ## From f62bc603879da6fe9aa0b5378a443987a97fefd6 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Tue, 9 Feb 2016 07:18:33 -0800 Subject: [PATCH 081/343] Rename SSL protocols field to ssl_version for consistency with Ruby OpenSSL --- ext/cmain.cpp | 4 ++-- ext/rubymain.cpp | 4 ++-- ext/ssl.cpp | 16 ++++++++-------- ext/ssl.h | 4 ++-- lib/em/connection.rb | 23 +++++++++++++++-------- tests/test_ssl_extensions.rb | 4 ++-- tests/test_ssl_protocols.rb | 20 ++++++++++---------- 7 files changed, 41 insertions(+), 34 deletions(-) diff --git a/ext/cmain.cpp b/ext/cmain.cpp index a98611145..080185417 100644 --- a/ext/cmain.cpp +++ b/ext/cmain.cpp @@ -459,12 +459,12 @@ extern "C" void evma_start_tls (const uintptr_t binding) evma_set_tls_parms ******************/ -extern "C" void evma_set_tls_parms (const uintptr_t binding, const char *privatekey_filename, const char *certchain_filename, int verify_peer, int fail_if_no_peer_cert, const char *sni_hostname, const char *cipherlist, const char *ecdh_curve, const char *dhparam, int protocols) +extern "C" void evma_set_tls_parms (const uintptr_t binding, const char *privatekey_filename, const char *certchain_filename, int verify_peer, int fail_if_no_peer_cert, const char *sni_hostname, const char *cipherlist, const char *ecdh_curve, const char *dhparam, int ssl_version) { ensure_eventmachine("evma_set_tls_parms"); EventableDescriptor *ed = dynamic_cast (Bindable_t::GetObject (binding)); if (ed) - ed->SetTlsParms (privatekey_filename, certchain_filename, (verify_peer == 1 ? true : false), (fail_if_no_peer_cert == 1 ? true : false), sni_hostname, cipherlist, ecdh_curve, dhparam, protocols); + ed->SetTlsParms (privatekey_filename, certchain_filename, (verify_peer == 1 ? true : false), (fail_if_no_peer_cert == 1 ? true : false), sni_hostname, cipherlist, ecdh_curve, dhparam, ssl_version); } /****************** diff --git a/ext/rubymain.cpp b/ext/rubymain.cpp index 220db0cca..54d597ee7 100644 --- a/ext/rubymain.cpp +++ b/ext/rubymain.cpp @@ -341,14 +341,14 @@ static VALUE t_start_tls (VALUE self UNUSED, VALUE signature) t_set_tls_parms ***************/ -static VALUE t_set_tls_parms (VALUE self UNUSED, VALUE signature, VALUE privkeyfile, VALUE certchainfile, VALUE verify_peer, VALUE fail_if_no_peer_cert, VALUE snihostname, VALUE cipherlist, VALUE ecdh_curve, VALUE dhparam, VALUE protocols) +static VALUE t_set_tls_parms (VALUE self UNUSED, VALUE signature, VALUE privkeyfile, VALUE certchainfile, VALUE verify_peer, VALUE fail_if_no_peer_cert, VALUE snihostname, VALUE cipherlist, VALUE ecdh_curve, VALUE dhparam, VALUE ssl_version) { /* set_tls_parms takes a series of positional arguments for specifying such things * as private keys and certificate chains. * It's expected that the parameter list will grow as we add more supported features. * ALL of these parameters are optional, and can be specified as empty or NULL strings. */ - evma_set_tls_parms (NUM2BSIG (signature), StringValueCStr (privkeyfile), StringValueCStr (certchainfile), (verify_peer == Qtrue ? 1 : 0), (fail_if_no_peer_cert == Qtrue ? 1 : 0), StringValueCStr (snihostname), StringValueCStr (cipherlist), StringValueCStr (ecdh_curve), StringValueCStr (dhparam), NUM2INT (protocols)); + evma_set_tls_parms (NUM2BSIG (signature), StringValueCStr (privkeyfile), StringValueCStr (certchainfile), (verify_peer == Qtrue ? 1 : 0), (fail_if_no_peer_cert == Qtrue ? 1 : 0), StringValueCStr (snihostname), StringValueCStr (cipherlist), StringValueCStr (ecdh_curve), StringValueCStr (dhparam), NUM2INT (ssl_version)); return Qnil; } diff --git a/ext/ssl.cpp b/ext/ssl.cpp index b4262fff1..13f5f161e 100644 --- a/ext/ssl.cpp +++ b/ext/ssl.cpp @@ -120,7 +120,7 @@ static void InitializeDefaultCredentials() SslContext_t::SslContext_t **************************/ -SslContext_t::SslContext_t (bool is_server, const string &privkeyfile, const string &certchainfile, const string &cipherlist, const string &ecdh_curve, const string &dhparam, int protocols) : +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) : bIsServer (is_server), pCtx (NULL), PrivateKey (NULL), @@ -161,22 +161,22 @@ SslContext_t::SslContext_t (bool is_server, const string &privkeyfile, const str # endif #endif - if (!(protocols & EM_PROTO_SSLv2)) + if (!(ssl_version & EM_PROTO_SSLv2)) SSL_CTX_set_options (pCtx, SSL_OP_NO_SSLv2); - if (!(protocols & EM_PROTO_SSLv3)) + if (!(ssl_version & EM_PROTO_SSLv3)) SSL_CTX_set_options (pCtx, SSL_OP_NO_SSLv3); - if (!(protocols & EM_PROTO_TLSv1)) + if (!(ssl_version & EM_PROTO_TLSv1)) SSL_CTX_set_options (pCtx, SSL_OP_NO_TLSv1); #ifdef SSL_OP_NO_TLSv1_1 - if (!(protocols & EM_PROTO_TLSv1_1)) + if (!(ssl_version & EM_PROTO_TLSv1_1)) SSL_CTX_set_options (pCtx, SSL_OP_NO_TLSv1_1); #endif #ifdef SSL_OP_NO_TLSv1_2 - if (!(protocols & EM_PROTO_TLSv1_2)) + if (!(ssl_version & EM_PROTO_TLSv1_2)) SSL_CTX_set_options (pCtx, SSL_OP_NO_TLSv1_2); #endif @@ -304,7 +304,7 @@ SslContext_t::~SslContext_t() SslBox_t::SslBox_t ******************/ -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 protocols, const uintptr_t binding): +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): bIsServer (is_server), bHandshakeCompleted (false), bVerifyPeer (verify_peer), @@ -317,7 +317,7 @@ SslBox_t::SslBox_t (bool is_server, const string &privkeyfile, const string &cer * a new one every time we come here. */ - Context = new SslContext_t (bIsServer, privkeyfile, certchainfile, cipherlist, ecdh_curve, dhparam, protocols); + Context = new SslContext_t (bIsServer, privkeyfile, certchainfile, cipherlist, ecdh_curve, dhparam, ssl_version); assert (Context); pbioRead = BIO_new (BIO_s_mem()); diff --git a/ext/ssl.h b/ext/ssl.h index 4044ca548..d8524ed5a 100644 --- a/ext/ssl.h +++ b/ext/ssl.h @@ -33,7 +33,7 @@ class SslContext_t class SslContext_t { public: - SslContext_t (bool is_server, const string &privkeyfile, const string &certchainfile, const string &cipherlist, const string &ecdh_curve, const string &dhparam, int protocols); + SslContext_t (bool is_server, const string &privkeyfile, const string &certchainfile, const string &cipherlist, const string &ecdh_curve, const string &dhparam, int ssl_version); virtual ~SslContext_t(); private: @@ -61,7 +61,7 @@ class SslBox_t class SslBox_t { public: - 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 protocols, const uintptr_t binding); + 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); virtual ~SslBox_t(); int PutPlaintext (const char*, int); diff --git a/lib/em/connection.rb b/lib/em/connection.rb index 3c1625332..267aec2d0 100644 --- a/lib/em/connection.rb +++ b/lib/em/connection.rb @@ -389,7 +389,7 @@ def connection_completed # # @option args [String] :dhparam (nil) The local path of a file containing DH parameters for EDH ciphers in [PEM format](http://en.wikipedia.org/wiki/Privacy_Enhanced_Mail) See: 'openssl dhparam' # - # @option args [Array] :protocols (TLSv1 TLSv1.1 TLSv1.2) indicates the allowed SSL/TLS protocols versions. Possible values are: {SSLv2}, {SSLv3}, {TLSv1}, {TLSv1.1}, {TLSv1.2}. + # @option args [Array] :ssl_version (TLSv1 TLSv1_1 TLSv1_2) indicates the allowed SSL/TLS versions. Possible values are: {SSLv2}, {SSLv3}, {TLSv1}, {TLSv1_1}, {TLSv1_2}. # # @example Using TLS with EventMachine # @@ -415,7 +415,15 @@ def connection_completed # # @see #ssl_verify_peer def start_tls args={} - priv_key, cert_chain, verify_peer, fail_if_no_peer_cert, sni_hostname, cipher_list, ecdh_curve, dhparam, protocols = args.values_at(:private_key_file, :cert_chain_file, :verify_peer, :fail_if_no_peer_cert, :sni_hostname, :cipher_list, :ecdh_curve, :dhparam, :protocols) + priv_key = args[:private_key_file] + cert_chain = args[:cert_chain_file] + verify_peer = args[:verify_peer] + sni_hostname = args[:sni_hostname] + cipher_list = args[:cipher_list] + ssl_version = args[:ssl_version] + ecdh_curve = args[:ecdh_curve] + dhparam = args[:dhparam] + fail_if_no_peer_cert = args[:fail_if_no_peer_cert] [priv_key, cert_chain].each do |file| next if file.nil? or file.empty? @@ -424,23 +432,22 @@ def start_tls args={} end protocols_bitmask = 0 - if protocols.nil? + if ssl_version.nil? protocols_bitmask |= EventMachine::EM_PROTO_TLSv1 protocols_bitmask |= EventMachine::EM_PROTO_TLSv1_1 protocols_bitmask |= EventMachine::EM_PROTO_TLSv1_2 else - protocols ||= [] - protocols.each do |p| - case p.downcase + [ssl_version].flatten.each do |p| + case p.to_s.downcase when 'sslv2' protocols_bitmask |= EventMachine::EM_PROTO_SSLv2 when 'sslv3' protocols_bitmask |= EventMachine::EM_PROTO_SSLv3 when 'tlsv1' protocols_bitmask |= EventMachine::EM_PROTO_TLSv1 - when 'tlsv1.1' + when 'tlsv1_1' protocols_bitmask |= EventMachine::EM_PROTO_TLSv1_1 - when 'tlsv1.2' + when 'tlsv1_2' protocols_bitmask |= EventMachine::EM_PROTO_TLSv1_2 else raise("Unrecognized SSL/TLS Protocol: #{p}") diff --git a/tests/test_ssl_extensions.rb b/tests/test_ssl_extensions.rb index b876f5e2a..549ad7470 100644 --- a/tests/test_ssl_extensions.rb +++ b/tests/test_ssl_extensions.rb @@ -17,7 +17,7 @@ def unbind end def connection_completed - start_tls(:protocols => %w(tlsv1), :sni_hostname => 'example.com') + start_tls(:ssl_version => :tlsv1, :sni_hostname => 'example.com') end end @@ -28,7 +28,7 @@ def ssl_handshake_completed end def post_init - start_tls(:protocols => %w(TLSv1)) + start_tls(:ssl_version => :TLSv1) end end diff --git a/tests/test_ssl_protocols.rb b/tests/test_ssl_protocols.rb index 310a7fc44..9a9308afd 100644 --- a/tests/test_ssl_protocols.rb +++ b/tests/test_ssl_protocols.rb @@ -26,7 +26,7 @@ def ssl_handshake_completed module ClientAny include Client def connection_completed - start_tls(:protocols => %w(sslv2 sslv3 tlsv1 tlsv1.1 tlsv1.2)) + start_tls(:ssl_version => %w(sslv2 sslv3 tlsv1 tlsv1_1 tlsv1_2)) end end @@ -40,28 +40,28 @@ def connection_completed module ClientSSLv3 include Client def connection_completed - start_tls(:protocols => %w(SSLv3)) + start_tls(:ssl_version => %w(SSLv3)) end end module ServerSSLv3 include Server def post_init - start_tls(:protocols => %w(SSLv3)) + start_tls(:ssl_version => %w(SSLv3)) end end module ServerTLSv1CaseInsensitive include Server def post_init - start_tls(:protocols => %w(tlsv1)) + start_tls(:ssl_version => %w(tlsv1)) end end module ServerAny include Server def post_init - start_tls(:protocols => %w(sslv2 sslv3 tlsv1 tlsv1.1 tlsv1.2)) + start_tls(:ssl_version => %w(sslv2 sslv3 tlsv1 tlsv1_1 tlsv1_2)) end end @@ -75,12 +75,12 @@ def post_init module InvalidProtocol include Client def post_init - start_tls(:protocols => %w(tlsv1 badinput)) + start_tls(:ssl_version => %w(tlsv1 badinput)) end end - def test_invalid_protocol - assert_raises(RuntimeError, "Unrecognized SSL/TLS Protocol: badinput") do + def test_invalid_ssl_version + assert_raises(RuntimeError, "Unrecognized SSL/TLS Version: badinput") do EM.run do EM.start_server("127.0.0.1", 16784, InvalidProtocol) EM.connect("127.0.0.1", 16784, InvalidProtocol) @@ -156,7 +156,7 @@ def test_default_to_default module ServerV3StopAfterHandshake def post_init - start_tls(:protocols => %w(SSLv3)) + start_tls(:ssl_version => %w(SSLv3)) end def ssl_handshake_completed @@ -167,7 +167,7 @@ def ssl_handshake_completed module ServerTLSv1StopAfterHandshake def post_init - start_tls(:protocols => %w(TLSv1)) + start_tls(:ssl_version => %w(TLSv1)) end def ssl_handshake_completed From 4dc5cbae7dedd77791155fd9d33f4126a477616a Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Thu, 25 Feb 2016 13:29:52 -0800 Subject: [PATCH 082/343] Bump version to 1.2.0.dev.2 --- lib/em/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/em/version.rb b/lib/em/version.rb index cb119ecc3..866b79eef 100644 --- a/lib/em/version.rb +++ b/lib/em/version.rb @@ -1,3 +1,3 @@ module EventMachine - VERSION = "1.2.0.dev.1" + VERSION = "1.2.0.dev.2" end From 5f941330eb6cc9a7108746eab4296229547c2a7b Mon Sep 17 00:00:00 2001 From: Otto Strassen Date: Fri, 26 Feb 2016 12:18:26 +0100 Subject: [PATCH 083/343] Fix build on OpenBSD If EPROTO does not exist define it as EINTR --- ext/ed.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ext/ed.cpp b/ext/ed.cpp index c63e1de80..1c06005e4 100644 --- a/ext/ed.cpp +++ b/ext/ed.cpp @@ -878,6 +878,9 @@ void ConnectionDescriptor::_DispatchInboundData (const char *buffer, unsigned lo // If our SSL handshake had a problem, shut down the connection. if (s == -2) { + #ifndef EPROTO // OpenBSD does not have EPROTO + #define EPROTO EINTR + #endif #ifdef OS_UNIX UnbindReasonCode = EPROTO; #endif From 7989c80575937ed0dd485f8f71ffcc0b8f3964b7 Mon Sep 17 00:00:00 2001 From: Michael Pye Date: Wed, 2 Jul 2014 16:06:05 +0100 Subject: [PATCH 084/343] Honour requests to close NOW ...even if we have previously asked to close later. Many places in the codebase use ScheduleClose(false) when they have found out that the socket is no longer operational, but they are being ignored if ScheduleClose(true) has been used in the past. As the socket is often not working at this point, we will never get through the send queue and actually do the close. --- ext/ed.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ext/ed.cpp b/ext/ed.cpp index e7fbc1182..5b75a07ef 100644 --- a/ext/ed.cpp +++ b/ext/ed.cpp @@ -235,8 +235,14 @@ EventableDescriptor::ScheduleClose void EventableDescriptor::ScheduleClose (bool after_writing) { - if (IsCloseScheduled()) + if (IsCloseScheduled()) { + if (!after_writing) { + // If closing has become more urgent, then upgrade the scheduled + // after_writing close to one NOW. + bCloseNow = true; + } return; + } MyEventMachine->NumCloseScheduled++; // KEEP THIS SYNCHRONIZED WITH ::IsCloseScheduled. if (after_writing) From c1f36b3ad0cfe96d6234798abf487d5ea80dca04 Mon Sep 17 00:00:00 2001 From: Sean Porter Date: Wed, 9 Mar 2016 16:26:47 -0800 Subject: [PATCH 085/343] [aix-compile] -shared does not work with xlc_r --- ext/extconf.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/extconf.rb b/ext/extconf.rb index 423d26c2b..8bf5a3488 100644 --- a/ext/extconf.rb +++ b/ext/extconf.rb @@ -211,7 +211,7 @@ def pkg_config_wrapper(pretty_name, name) CONFIG['LDSHARED'] = "$(CXX) -shared" when /aix/ - CONFIG['LDSHARED'] = "$(CXX) -shared -Wl,-G -Wl,-brtl" + CONFIG['LDSHARED'] = "$(CXX) -Wl,-bstatic -Wl,-bdynamic -Wl,-G -Wl,-brtl" when /cygwin/ # For rubies built with Cygwin, CXX may be set to CC, which is just From 2d7496452726507d378c6fa9458738ce88f5e741 Mon Sep 17 00:00:00 2001 From: Sean Porter Date: Wed, 9 Mar 2016 19:05:42 -0800 Subject: [PATCH 086/343] [aix-compile] -shared does not work for fastfilereader --- ext/fastfilereader/extconf.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ext/fastfilereader/extconf.rb b/ext/fastfilereader/extconf.rb index d6a824ea1..e4d64e4a2 100644 --- a/ext/fastfilereader/extconf.rb +++ b/ext/fastfilereader/extconf.rb @@ -90,8 +90,7 @@ def add_define(name) CONFIG['LDSHARED'] = "$(CXX) -shared" when /aix/ - # on Unix we need a g++ link, not gcc. - CONFIG['LDSHARED'] = "$(CXX) -shared -Wl,-G" + CONFIG['LDSHARED'] = "$(CXX) -Wl,-bstatic -Wl,-bdynamic -Wl,-G -Wl,-brtl" when /cygwin/ # For rubies built with Cygwin, CXX may be set to CC, which is just From dfe0f79bd56b6a53303c34b71dc694f57ae0d5ce Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Mon, 14 Mar 2016 15:51:08 -0700 Subject: [PATCH 087/343] Changelog for v1.2.0 --- CHANGELOG.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 41d2c39a9..93a34aad0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,28 @@ # Changelog +## 1.2.0 (March 15, 2016) +* Integrate work from the EventMachine-LE 1.1.x versions [#570] +* Add start_tls options :ecdh_curve, :dhparam, :fail_if_no_peer_cert [#195, #275, #399, #665] +* Add start_tls option :ssl_version for choosing SSL/TLS versions and ciphers [#359, #348, #603, #654] +* Add start_tls option :sni_hostname to be passed to TLS params [#593] +* Add method EM::Channel#num_subscribers to get the number of subscribers to a channel [#640] +* Add support for proc-sources in EM::Iterator [#639] +* Factor out method cleanup_machine to cleanup code from EM.run [#650] +* Replace Exception class with StandardError [#637] +* Close socket on close_connection even after close_connection_after_writing [#694] +* Allow reusing of datagram socket/setting bind device [#662] +* Handle deferred exceptions in reactor thread [#486] +* Reimplement Queue to avoid shift/push performance problem [#311] +* Windows: Switch from gethostbyname to getaddrinfo, support IPv6 addresses [#303, #630] +* Windows: Use rake-compiler-dock to cross-compile gems [#627] +* Windows: Add AppVeyor configuration for Windows CI testing [#578] +* Windows: Bump rake-compiler to version 0.9.x [#542] +* Fix compilation on AIX (w/ XLC) [#693] +* Fix build on OpenBSD [#690] +* Fix OpenSSL compile issue on AIX 7.1 [#678] +* Fix EventMachine.fork_reactor keeps the threadpool of the original process [#425] +* Fix to prevent event machine from stopping when a raise is done in an unbind [#327] + ## 1.0.9.1 (January 14, 2016) * Fix EPROTO not defined on Windows [#676] * Fix missing cast to struct sockaddr * [#671] From bc508be9862dfaddebe80612a4c439687684862d Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Mon, 14 Mar 2016 14:26:50 -0700 Subject: [PATCH 088/343] Bump version to 1.2.0 --- lib/em/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/em/version.rb b/lib/em/version.rb index 866b79eef..abeecb49b 100644 --- a/lib/em/version.rb +++ b/lib/em/version.rb @@ -1,3 +1,3 @@ module EventMachine - VERSION = "1.2.0.dev.2" + VERSION = "1.2.0" end From 5adf325ef197177547fed2a440ff6ce9c3dc53e1 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Tue, 15 Mar 2016 12:14:51 -0700 Subject: [PATCH 089/343] Use struct sockaddr_in6 to be able to accept both IPv4 and IPv6 addresses --- ext/ed.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/ed.cpp b/ext/ed.cpp index 4df49335e..2852bd1b5 100644 --- a/ext/ed.cpp +++ b/ext/ed.cpp @@ -1540,7 +1540,7 @@ void AcceptorDescriptor::Read() */ - struct sockaddr_in pin; + struct sockaddr_in6 pin; socklen_t addrlen = sizeof (pin); int accept_count = EventMachine_t::GetSimultaneousAcceptCount(); @@ -1733,7 +1733,7 @@ void DatagramDescriptor::Read() // That's so we can put a guard byte at the end of what we send // to user code. - struct sockaddr_in sin; + struct sockaddr_in6 sin; socklen_t slen = sizeof (sin); memset (&sin, 0, slen); From a2b5b5f3ac7e02c7cccbfeabe90d65f46486e82d Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Tue, 15 Mar 2016 18:40:19 -0700 Subject: [PATCH 090/343] Changelog for v1.2.0.1 --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 93a34aad0..59c137f40 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Changelog +## 1.2.0.1 (March 15, 2016) +* Fix crash when accepting IPv6 connections due to struct sockaddr_in [#698, #699] + ## 1.2.0 (March 15, 2016) * Integrate work from the EventMachine-LE 1.1.x versions [#570] * Add start_tls options :ecdh_curve, :dhparam, :fail_if_no_peer_cert [#195, #275, #399, #665] From 6d02b4abab759e9aeb6366e71ec62047da5762c4 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Tue, 15 Mar 2016 18:40:44 -0700 Subject: [PATCH 091/343] Bump version to 1.2.0.1 --- lib/em/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/em/version.rb b/lib/em/version.rb index abeecb49b..d2f971dd7 100644 --- a/lib/em/version.rb +++ b/lib/em/version.rb @@ -1,3 +1,3 @@ module EventMachine - VERSION = "1.2.0" + VERSION = "1.2.0.1" end From 5a3dc3f23082866d4fe56cbe155216422a272edb Mon Sep 17 00:00:00 2001 From: Aishraj Dahal Date: Sat, 26 Mar 2016 10:48:07 +0530 Subject: [PATCH 092/343] Add syntax highlighting to README.md Used Github flavoured markdown to add syntax highlighting to the example code block on the README.md file. --- README.md | 44 +++++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 6339ea1dd..b8ffc57ed 100644 --- a/README.md +++ b/README.md @@ -61,27 +61,29 @@ For an introduction to EventMachine, check out: Here's a fully-functional echo server written with EventMachine: - require 'eventmachine' - - module EchoServer - def post_init - puts "-- someone connected to the echo server!" - end - - def receive_data data - send_data ">>>you sent: #{data}" - close_connection if data =~ /quit/i - end - - def unbind - puts "-- someone disconnected from the echo server!" - end - end - - # Note that this will block current thread. - EventMachine.run { - EventMachine.start_server "127.0.0.1", 8081, EchoServer - } +```ruby + require 'eventmachine' + + module EchoServer + def post_init + puts "-- someone connected to the echo server!" + end + + def receive_data data + send_data ">>>you sent: #{data}" + close_connection if data =~ /quit/i + end + + def unbind + puts "-- someone disconnected from the echo server!" + end +end + +# Note that this will block current thread. +EventMachine.run { + EventMachine.start_server "127.0.0.1", 8081, EchoServer +} +``` ## EventMachine documentation ## From 249846eb3434936b7cc5eea611cd92607536fd66 Mon Sep 17 00:00:00 2001 From: Omer Katz Date: Sun, 27 Mar 2016 07:03:48 -0700 Subject: [PATCH 093/343] Reflect Ruby 2.3 support for EventMachine. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b8ffc57ed..af81b88a6 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ EventMachine has been around since the early 2000s and is a mature and battle-te ## What platforms are supported by EventMachine? ## -EventMachine supports Ruby >= 1.8.7 and <= 2.2 REE, JRuby and **works well on Windows** as well +EventMachine supports Ruby 1.8.7 through 2.3, REE, JRuby and **works well on Windows** as well as many operating systems from the Unix family (Linux, Mac OS X, BSD flavors). From 93122901ac92066a8edadd10d96d07f7fc03a111 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Mon, 28 Mar 2016 09:44:38 -0700 Subject: [PATCH 094/343] Rake 11.0 no longer supports Ruby 1.8.7 and Ruby 1.9.2 --- Gemfile | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Gemfile b/Gemfile index 0d9a37623..a5ad5da96 100644 --- a/Gemfile +++ b/Gemfile @@ -2,6 +2,13 @@ source 'https://rubygems.org' gemspec +# Rake 11.0 no longer supports Ruby 1.8.7 and Ruby 1.9.2 +if RUBY_VERSION < '1.9.3' + gem 'rake', '< 11' +else + gem 'rake' +end + group :documentation do gem 'yard', '>= 0.8.5.2' gem 'bluecloth' unless RUBY_PLATFORM =~ /java|mswin|mingw/ From db7bb185047392c8a8da39793462cc1ffc6c8ec3 Mon Sep 17 00:00:00 2001 From: Sean Porter Date: Mon, 4 Apr 2016 10:03:33 -0700 Subject: [PATCH 095/343] [pure_ruby] moved schedule_close to selectable, fixes socket close --- lib/em/pure_ruby.rb | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/lib/em/pure_ruby.rb b/lib/em/pure_ruby.rb index a85149684..153528fa7 100644 --- a/lib/em/pure_ruby.rb +++ b/lib/em/pure_ruby.rb @@ -499,6 +499,14 @@ def set_inactivity_timeout tm def heartbeat end + + def schedule_close(after_writing=false) + if after_writing + @close_requested = true + else + @close_scheduled = true + end + end end end @@ -607,16 +615,6 @@ def send_data data end end - # #schedule_close - # The application wants to close the connection. - def schedule_close after_writing - if after_writing - @close_requested = true - else - @close_scheduled = true - end - end - # #get_peername # This is defined in the normal way on connected stream objects. # Return an object that is suitable for passing to Socket#unpack_sockaddr_in or variants. @@ -1005,18 +1003,14 @@ def eventable_read @close_scheduled = true EventMachine::event_callback uuid, ConnectionUnbound, nil end - end - def send_data data send_datagram data, @return_address end - end end # load base EM api on top, now that we have the underlying pure ruby # implementation defined require 'eventmachine' - From 3bed7400d6e86c9cdf89f4b8c8c677acd7a76e04 Mon Sep 17 00:00:00 2001 From: Sean Porter Date: Mon, 4 Apr 2016 11:05:23 -0700 Subject: [PATCH 096/343] [pure_ruby] no-op stub set_pending_connect_timeout --- lib/em/pure_ruby.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/em/pure_ruby.rb b/lib/em/pure_ruby.rb index 153528fa7..a5f7a5c4e 100644 --- a/lib/em/pure_ruby.rb +++ b/lib/em/pure_ruby.rb @@ -223,6 +223,12 @@ def set_comm_inactivity_timeout sig, tm r = Reactor.instance.get_selectable( sig ) or raise "unknown set_comm_inactivity_timeout target" r.set_inactivity_timeout tm end + + # @private + def set_pending_connect_timeout sig, tm + # Needs to be implemented. Currently a no-op stub to allow + # certain software to operate with the EM pure-ruby. + end end end From 0efda921a9207917e29f9359b71276965cca90d6 Mon Sep 17 00:00:00 2001 From: Sean Porter Date: Mon, 4 Apr 2016 11:25:53 -0700 Subject: [PATCH 097/343] [pure_ruby] fixed close_connection - can try to close nonexistent connection, matching c reactor behaviour --- lib/em/pure_ruby.rb | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/em/pure_ruby.rb b/lib/em/pure_ruby.rb index a5f7a5c4e..351fe7e2f 100644 --- a/lib/em/pure_ruby.rb +++ b/lib/em/pure_ruby.rb @@ -92,14 +92,10 @@ def send_data target, data, datalength selectable.send_data data end - - # The extension version does NOT raise any kind of an error if an attempt is made - # to close a non-existent connection. Not sure whether we should. For now, we'll - # raise an error here in that case. # @private def close_connection target, after_writing - selectable = Reactor.instance.get_selectable( target ) or raise "unknown close_connection target" - selectable.schedule_close after_writing + selectable = Reactor.instance.get_selectable( target ) + selectable.schedule_close after_writing if selectable end # @private From 3b24d5fdc411d7b279d83f54a9083f8184857054 Mon Sep 17 00:00:00 2001 From: Tom Aranda Date: Mon, 18 Apr 2016 07:54:12 -0500 Subject: [PATCH 098/343] Add regular expression (regex) support to LineText2 protocol's delimiter. The set_delimiter method will accept a regular expression and set the end-of-line delimiter appropriately. Anything passed to the set_delimiter method other than a regular expression will be converted to a string. --- lib/em/protocols/linetext2.rb | 23 ++++++++++++++++++----- tests/test_ltp2.rb | 24 ++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/lib/em/protocols/linetext2.rb b/lib/em/protocols/linetext2.rb index 23993f20e..9fdf28bd3 100644 --- a/lib/em/protocols/linetext2.rb +++ b/lib/em/protocols/linetext2.rb @@ -56,7 +56,14 @@ def receive_data data while remaining_data.length > 0 if @lt2_mode == :lines - if ix = remaining_data.index( @lt2_delimiter ) + delimiter_string = case @lt2_delimiter + when Regexp + remaining_data.slice(@lt2_delimiter) + else + @lt2_delimiter + end + ix = remaining_data.index(delimiter_string) if delimiter_string + if ix @lt2_linebuffer << remaining_data[0...ix] ln = @lt2_linebuffer.join @lt2_linebuffer.clear @@ -64,7 +71,7 @@ def receive_data data ln.chomp! end receive_line ln - remaining_data = remaining_data[(ix+@lt2_delimiter.length)..-1] + remaining_data = remaining_data[(ix+delimiter_string.length)..-1] else @lt2_linebuffer << remaining_data remaining_data = "" @@ -101,9 +108,16 @@ def receive_data data end end - + # The line delimiter may be a regular expression or a string. Anything + # passed to set_delimiter other than a regular expression will be + # converted to a string. def set_delimiter delim - @lt2_delimiter = delim.to_s + @lt2_delimiter = case delim + when Regexp + delim + else + delim.to_s + end end # Called internally but also exposed to user code, for the case in which @@ -163,4 +177,3 @@ def receive_end_of_binary_data end end end - diff --git a/tests/test_ltp2.rb b/tests/test_ltp2.rb index a227367b1..220fcbe1c 100644 --- a/tests/test_ltp2.rb +++ b/tests/test_ltp2.rb @@ -72,6 +72,30 @@ def test_change_delimiter assert_equal( ["Linea", "Lineb", "Linec", "Lined"], a.lines ) end + class RegexDelimiter + include EM::Protocols::LineText2 + attr_reader :lines + def initialize *args + super + @delim = /[A-D]/ + set_delimiter @delim + end + def receive_line line + (@lines ||= []) << line + end + end + + def test_regex_delimiter + testdata = %Q(LineaALinebBLinecCLinedD) + + a = RegexDelimiter.new + a.receive_data testdata + assert_equal( ["Linea", "Lineb", "Linec", "Lined"], a.lines ) + + a = RegexDelimiter.new + testdata.length.times {|i| a.receive_data( testdata[i...i+1] ) } + assert_equal( ["Linea", "Lineb", "Linec", "Lined"], a.lines ) + end #-- # Test two lines followed by an empty line, ten bytes of binary data, then From adafc7bb31cc1b847617c51692e177f6e12b8256 Mon Sep 17 00:00:00 2001 From: Sean Porter Date: Thu, 14 Apr 2016 09:49:36 -0700 Subject: [PATCH 099/343] Pure Ruby EM start_tls() [pure_ruby_tls] initial implementation [pure_ruby_tls] closer to working start_tls [pure_ruby_tls] working start_tls implementation [pure_ruby_tls] fixed connection completed [pure_ruby_tls] debugging handshake errors when certs are used [pure_ruby_tls] fully operational battle station [pure_ruby_tls] implemented fail_if_no_peer_cert, cipher_list, and protocols_bitmask [pure_ruby_tls] support testing pure ruby reactor [pure_ruby_tls] ssl/tls tcp support support [pure_ruby_tls] working tls server [pure_ruby_tls] delete dup tests [pure_ruby_tls] fixed tcp client ready?() for aix and solaris [pure_ruby_tls] only use Socket::TCP_INFO on linux [pure_ruby_tls] working ssl_handshake_completed [pure_ruby_tls] default cert/key, error classes, working unbind, fix blocking eventable_write, and tests! [pure_ruby_tls] changed default cert subject to include EM, removed verify_peer == 1 [pure_ruby_tls] working ssl verify with peer cert [pure_ruby_tls] fixed ssl options, OP_ALL [pure_ruby_tls] working get peer cert and cipher [pure_ruby_tls] fixed ssl_verify_peer connection close, working sni_hostname [pure_ruby_tls] working dhparam [pure_ruby_tls] working ecdh curve on ruby 2.3 [pure_ruby_tls] omit verify peer tests that do not pass due to default server chain [pure_ruby_tls] support older versions of ruby and openssl [pure_ruby_tls] fixed ssl wait readable/writable error handling on older rubies [pure_ruby_tls] older rubies missing default dh keys [pure_ruby_tls] removed duplicate default cert creator --- .gitignore | 2 +- ext/rubymain.cpp | 2 +- lib/em/pure_ruby.rb | 304 +++++++++++++++++++++++++++++++---- lib/eventmachine.rb | 6 + rakelib/test_pure.rake | 13 ++ tests/em_test_helper.rb | 3 + tests/test_pure.rb | 51 ++++++ tests/test_ssl_dhparam.rb | 3 +- tests/test_ssl_ecdh_curve.rb | 3 +- tests/test_ssl_extensions.rb | 2 +- tests/test_ssl_protocols.rb | 6 +- tests/test_ssl_verify.rb | 2 + 12 files changed, 358 insertions(+), 39 deletions(-) create mode 100644 rakelib/test_pure.rake diff --git a/.gitignore b/.gitignore index 6e725787b..bc7c25d1b 100644 --- a/.gitignore +++ b/.gitignore @@ -19,4 +19,4 @@ Gemfile.lock .yardoc/* doc/* - +tmp/* diff --git a/ext/rubymain.cpp b/ext/rubymain.cpp index 54d597ee7..430564335 100644 --- a/ext/rubymain.cpp +++ b/ext/rubymain.cpp @@ -1490,7 +1490,7 @@ extern "C" void Init_rubyeventmachine() rb_define_const (EmModule, "ConnectionNotifyReadable", INT2NUM(EM_CONNECTION_NOTIFY_READABLE)); rb_define_const (EmModule, "ConnectionNotifyWritable", INT2NUM(EM_CONNECTION_NOTIFY_WRITABLE)); rb_define_const (EmModule, "SslHandshakeCompleted", INT2NUM(EM_SSL_HANDSHAKE_COMPLETED )); - // EM_SSL_VERIFY = 109, + rb_define_const (EmModule, "SslVerify", INT2NUM(EM_SSL_VERIFY )); // EM_PROXY_TARGET_UNBOUND = 110, // EM_PROXY_COMPLETED = 111 diff --git a/lib/em/pure_ruby.rb b/lib/em/pure_ruby.rb index 351fe7e2f..81c5f1456 100644 --- a/lib/em/pure_ruby.rb +++ b/lib/em/pure_ruby.rb @@ -33,6 +33,88 @@ require 'socket' require 'fcntl' require 'set' +require 'openssl' + +module EventMachine + # @private + class Error < Exception; end + # @private + class UnknownTimerFired < RuntimeError; end + # @private + class Unsupported < RuntimeError; end + # @private + class ConnectionError < RuntimeError; end + # @private + class ConnectionNotBound < RuntimeError; end + + # Older versions of Ruby may not provide the SSLErrorWaitReadable + # OpenSSL class. Create an error class to act as a "proxy". + if defined?(OpenSSL::SSL::SSLErrorWaitReadable) + SSLConnectionWaitReadable = OpenSSL::SSL::SSLErrorWaitReadable + else + SSLConnectionWaitReadable = IO::WaitReadable + end + + # Older versions of Ruby may not provide the SSLErrorWaitWritable + # OpenSSL class. Create an error class to act as a "proxy". + if defined?(OpenSSL::SSL::SSLErrorWaitWritable) + SSLConnectionWaitWritable = OpenSSL::SSL::SSLErrorWaitWritable + else + SSLConnectionWaitWritable = IO::WaitWritable + end +end + +module EventMachine + class CertificateCreator + attr_reader :cert, :key + + def initialize + @key = OpenSSL::PKey::RSA.new(1024) + public_key = @key.public_key + subject = "/C=EventMachine/O=EventMachine/OU=EventMachine/CN=EventMachine" + @cert = OpenSSL::X509::Certificate.new + @cert.subject = @cert.issuer = OpenSSL::X509::Name.parse(subject) + @cert.not_before = Time.now + @cert.not_after = Time.now + 365 * 24 * 60 * 60 + @cert.public_key = public_key + @cert.serial = 0x0 + @cert.version = 2 + factory = OpenSSL::X509::ExtensionFactory.new + factory.subject_certificate = @cert + factory.issuer_certificate = @cert + @cert.extensions = [ + factory.create_extension("basicConstraints","CA:TRUE", true), + factory.create_extension("subjectKeyIdentifier", "hash") + ] + @cert.add_extension factory.create_extension("authorityKeyIdentifier", "keyid:always,issuer:always") + @cert.sign(@key, OpenSSL::Digest::SHA1.new) + end + end + + # @private + DefaultCertificate = CertificateCreator.new + + # @private + DefaultDHKey1024 = OpenSSL::PKey::DH.new <<-_end_of_pem_ +-----BEGIN DH PARAMETERS----- +MIGHAoGBAJ0lOVy0VIr/JebWn0zDwY2h+rqITFOpdNr6ugsgvkDXuucdcChhYExJ +AV/ZD2AWPbrTqV76mGRgJg4EddgT1zG0jq3rnFdMj2XzkBYx3BVvfR0Arnby0RHR +T4h7KZ/2zmjvV+eF8kBUHBJAojUlzxKj4QeO2x20FP9X5xmNUXeDAgEC +-----END DH PARAMETERS----- + _end_of_pem_ + + # @private + DefaultDHKey2048 = OpenSSL::PKey::DH.new <<-_end_of_pem_ +-----BEGIN DH PARAMETERS----- +MIIBCAKCAQEA7E6kBrYiyvmKAMzQ7i8WvwVk9Y/+f8S7sCTN712KkK3cqd1jhJDY +JbrYeNV3kUIKhPxWHhObHKpD1R84UpL+s2b55+iMd6GmL7OYmNIT/FccKhTcveab +VBmZT86BZKYyf45hUF9FOuUM9xPzuK3Vd8oJQvfYMCd7LPC0taAEljQLR4Edf8E6 +YoaOffgTf5qxiwkjnlVZQc3whgnEt9FpVMvQ9eknyeGB5KHfayAc3+hUAvI3/Cr3 +1bNveX5wInh5GDx1FGhKBZ+s1H+aedudCm7sCgRwv8lKWYGiHzObSma8A86KG+MD +7Lo5JquQ3DlBodj3IDyPrxIv96lvRPFtAwIBAg== +-----END DH PARAMETERS----- + _end_of_pem_ +end # @private module EventMachine @@ -159,10 +241,127 @@ def set_timer_quantum interval def epoll end - # This method is not implemented for pure-Ruby implementation # @private def ssl? - false + true + end + + def tls_parm_set?(parm) + !(parm.nil? || parm.empty?) + end + + # This method takes a series of positional arguments for specifying such + # things as private keys and certificate chains. It's expected that the + # parameter list will grow as we add more supported features. ALL of these + # parameters are optional, and can be specified as empty or nil strings. + # @private + def set_tls_parms signature, priv_key, cert_chain, verify_peer, fail_if_no_peer_cert, sni_hostname, cipher_list, ecdh_curve, dhparam, protocols_bitmask + bitmask = protocols_bitmask + ssl_options = OpenSSL::SSL::OP_ALL + ssl_options |= OpenSSL::SSL::OP_NO_SSLv2 if defined?(OpenSSL::SSL::OP_NO_SSLv2) && EM_PROTO_SSLv2 & bitmask == 0 + ssl_options |= OpenSSL::SSL::OP_NO_SSLv3 if defined?(OpenSSL::SSL::OP_NO_SSLv3) && EM_PROTO_SSLv3 & bitmask == 0 + ssl_options |= OpenSSL::SSL::OP_NO_TLSv1 if defined?(OpenSSL::SSL::OP_NO_TLSv1) && EM_PROTO_TLSv1 & bitmask == 0 + ssl_options |= OpenSSL::SSL::OP_NO_TLSv1_1 if defined?(OpenSSL::SSL::OP_NO_TLSv1_1) && EM_PROTO_TLSv1_1 & bitmask == 0 + ssl_options |= OpenSSL::SSL::OP_NO_TLSv1_2 if defined?(OpenSSL::SSL::OP_NO_TLSv1_2) && EM_PROTO_TLSv1_2 & bitmask == 0 + @tls_parms ||= {} + @tls_parms[signature] = { + :verify_peer => verify_peer, + :fail_if_no_peer_cert => fail_if_no_peer_cert, + :ssl_options => ssl_options + } + @tls_parms[signature][:priv_key] = File.read(priv_key) if tls_parm_set?(priv_key) + @tls_parms[signature][:cert_chain] = File.read(cert_chain) if tls_parm_set?(cert_chain) + @tls_parms[signature][:sni_hostname] = sni_hostname if tls_parm_set?(sni_hostname) + @tls_parms[signature][:cipher_list] = cipher_list.gsub(/,\s*/, ':') if tls_parm_set?(cipher_list) + @tls_parms[signature][:dhparam] = File.read(dhparam) if tls_parm_set?(dhparam) + @tls_parms[signature][:ecdh_curve] = ecdh_curve if tls_parm_set?(ecdh_curve) + end + + def start_tls signature + selectable = Reactor.instance.get_selectable(signature) or raise "unknown io selectable for start_tls" + tls_parms = @tls_parms[signature] + ctx = OpenSSL::SSL::SSLContext.new + ctx.options = tls_parms[:ssl_options] + ctx.cert = DefaultCertificate.cert + ctx.key = DefaultCertificate.key + ctx.cert_store = OpenSSL::X509::Store.new + ctx.cert_store.set_default_paths + ctx.cert = OpenSSL::X509::Certificate.new(tls_parms[:cert_chain]) if tls_parms[:cert_chain] + ctx.key = OpenSSL::PKey::RSA.new(tls_parms[:priv_key]) if tls_parms[:priv_key] + verify_mode = OpenSSL::SSL::VERIFY_NONE + if tls_parms[:verify_peer] + verify_mode |= OpenSSL::SSL::VERIFY_PEER + end + if tls_parms[:fail_if_no_peer_cert] + verify_mode |= OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT + end + ctx.verify_mode = verify_mode + ctx.servername_cb = Proc.new do |_, server_name| + tls_parms[:server_name] = server_name + nil + end + ctx.ciphers = tls_parms[:cipher_list] if tls_parms[:cipher_list] + if selectable.is_server + ctx.tmp_dh_callback = Proc.new do |_, _, key_length| + if tls_parms[:dhparam] + OpenSSL::PKey::DH.new(tls_parms[:dhparam]) + else + case key_length + when 1024 then DefaultDHKey1024 + when 2048 then DefaultDHKey2048 + else + nil + end + end + end + if tls_parms[:ecdh_curve] && ctx.respond_to?(:tmp_ecdh_callback) + ctx.tmp_ecdh_callback = Proc.new do + OpenSSL::PKey::EC.new(tls_parms[:ecdh_curve]) + end + end + end + ssl_io = OpenSSL::SSL::SSLSocket.new(selectable, ctx) + ssl_io.sync_close = true + if tls_parms[:sni_hostname] + ssl_io.hostname = tls_parms[:sni_hostname] if ssl_io.respond_to?(:hostname=) + end + begin + selectable.is_server ? ssl_io.accept_nonblock : ssl_io.connect_nonblock + rescue; end + selectable.io = ssl_io + end + + def get_peer_cert signature + selectable = Reactor.instance.get_selectable(signature) or raise "unknown get_peer_cert target" + if selectable.io.respond_to?(:peer_cert) && selectable.io.peer_cert + selectable.io.peer_cert.to_pem + else + nil + end + end + + def get_cipher_name signature + selectable = Reactor.instance.get_selectable(signature) or raise "unknown get_cipher_name target" + selectable.io.respond_to?(:cipher) ? selectable.io.cipher[0] : nil + end + + def get_cipher_protocol signature + selectable = Reactor.instance.get_selectable(signature) or raise "unknown get_cipher_protocol target" + selectable.io.respond_to?(:cipher) ? selectable.io.cipher[1] : nil + end + + def get_cipher_bits signature + selectable = Reactor.instance.get_selectable(signature) or raise "unknown get_cipher_bits target" + selectable.io.respond_to?(:cipher) ? selectable.io.cipher[2] : nil + end + + def get_sni_hostname signature + @tls_parms ||= {} + if @tls_parms[signature] + @tls_parms[signature][:server_name] + else + nil + end end # This method is a no-op in the pure-Ruby implementation. We simply return Ruby's built-in @@ -180,13 +379,13 @@ def set_max_timer_count n # @private def get_sock_opt signature, level, optname - selectable = Reactor.instance.get_selectable( signature ) or raise "unknown get_peername target" + selectable = Reactor.instance.get_selectable( signature ) or raise "unknown get_sock_opt target" selectable.getsockopt level, optname end # @private def set_sock_opt signature, level, optname, optval - selectable = Reactor.instance.get_selectable( signature ) or raise "unknown get_peername target" + selectable = Reactor.instance.get_selectable( signature ) or raise "unknown set_sock_opt target" selectable.setsockopt level, optname, optval end @@ -228,12 +427,6 @@ def set_pending_connect_timeout sig, tm end end - -module EventMachine - # @private - class Error < Exception; end -end - module EventMachine # @private class Connection @@ -271,6 +464,24 @@ module EventMachine ConnectionCompleted = 104 # @private LoopbreakSignalled = 105 + # @private + ConnectionNotifyReadable = 106 + # @private + ConnectionNotifyWritable = 107 + # @private + SslHandshakeCompleted = 108 + # @private + SslVerify = 109 + # @private + EM_PROTO_SSLv2 = 2 + # @private + EM_PROTO_SSLv3 = 4 + # @private + EM_PROTO_TLSv1 = 8 + # @private + EM_PROTO_TLSv1_1 = 16 + # @private + EM_PROTO_TLSv1_2 = 32 end module EventMachine @@ -378,6 +589,9 @@ def crank_selectables @selectables.delete_if {|k,io| if io.close_scheduled? io.close + begin + EventMachine::event_callback io.uuid, ConnectionUnbound, nil + rescue ConnectionNotBound; end true end } @@ -416,8 +630,9 @@ def close_loopbreaker end def signal_loopbreak - #@loopbreak_writer.write '+' if @loopbreak_writer - @loopbreak_writer.send('+',0,"127.0.0.1",@loopbreak_port) if @loopbreak_writer + begin + @loopbreak_writer.send('+',0,"127.0.0.1",@loopbreak_port) if @loopbreak_writer + rescue IOError; end end def set_timer_quantum interval_in_seconds @@ -437,6 +652,8 @@ class IO def_delegator :@my_selectable, :eventable_read def_delegator :@my_selectable, :eventable_write def_delegator :@my_selectable, :uuid + def_delegator :@my_selectable, :is_server + def_delegator :@my_selectable, :is_server= def_delegator :@my_selectable, :send_data def_delegator :@my_selectable, :schedule_close def_delegator :@my_selectable, :get_peername @@ -444,17 +661,21 @@ class IO def_delegator :@my_selectable, :get_outbound_data_size def_delegator :@my_selectable, :set_inactivity_timeout def_delegator :@my_selectable, :heartbeat + def_delegator :@my_selectable, :io + def_delegator :@my_selectable, :io= end module EventMachine # @private class Selectable - attr_reader :io, :uuid + attr_accessor :io, :is_server + attr_reader :uuid def initialize io - @uuid = UuidGenerator.generate @io = io + @uuid = UuidGenerator.generate + @is_server = false @last_activity = Reactor.instance.current_loop_time if defined?(Fcntl::F_GETFL) @@ -562,9 +783,9 @@ def eventable_read data = io.sysread(4096) EventMachine::event_callback uuid, ConnectionData, data end - rescue Errno::EAGAIN, Errno::EWOULDBLOCK + rescue Errno::EAGAIN, Errno::EWOULDBLOCK, SSLConnectionWaitReadable # no-op - rescue Errno::ECONNRESET, Errno::ECONNREFUSED, EOFError + rescue Errno::ECONNRESET, Errno::ECONNREFUSED, EOFError, Errno::EPIPE, OpenSSL::SSL::SSLError @close_scheduled = true EventMachine::event_callback uuid, ConnectionUnbound, nil end @@ -599,9 +820,10 @@ def eventable_write @outbound_q.unshift data[w..-1] break end - rescue Errno::EAGAIN + rescue Errno::EAGAIN, SSLConnectionWaitReadable, SSLConnectionWaitWritable @outbound_q.unshift data - rescue EOFError, Errno::ECONNRESET, Errno::ECONNREFUSED + break + rescue EOFError, Errno::ECONNRESET, Errno::ECONNREFUSED, Errno::EPIPE, OpenSSL::SSL::SSLError @close_scheduled = true @outbound_q.clear end @@ -662,34 +884,53 @@ def self.connect bind_addr, bind_port, host, port EvmaTCPClient.new sd end - def initialize io super @pending = true + @handshake_complete = false end - - def select_for_writing? - @pending ? true : super + def ready? + if RUBY_PLATFORM =~ /linux/ + io.getsockopt(Socket::SOL_TCP, Socket::TCP_INFO).unpack("i").first == 1 # TCP_ESTABLISHED + else + io.getsockopt(Socket::SOL_SOCKET, Socket::SO_ERROR).unpack("i").first == 0 # NO ERROR + end end - def select_for_reading? - @pending ? false : super + def handshake_complete? + if !@handshake_complete && io.respond_to?(:state) + if io.state =~ /^SSLOK/ + @handshake_complete = true + EventMachine::event_callback uuid, SslHandshakeCompleted, "" + EventMachine::event_callback uuid, SslVerify, io.peer_cert.to_pem if io.peer_cert + end + else + @handshake_complete = true + end + @handshake_complete end - def eventable_write + def pending? + handshake_complete? if @pending - @pending = false - if 0 == io.getsockopt(Socket::SOL_SOCKET, Socket::SO_ERROR).unpack("i").first + if ready? + @pending = false EventMachine::event_callback uuid, ConnectionCompleted, "" end - else - super end + @pending end + def select_for_writing? + pending? + super + end - + def select_for_reading? + pending? + super + end end end @@ -809,7 +1050,8 @@ def eventable_read begin 10.times { descriptor,peername = io.accept_nonblock - sd = StreamObject.new descriptor + sd = EvmaTCPClient.new descriptor + sd.is_server = true EventMachine::event_callback uuid, ConnectionAccepted, sd.uuid } rescue Errno::EWOULDBLOCK, Errno::EAGAIN diff --git a/lib/eventmachine.rb b/lib/eventmachine.rb index 3b7a8e9d2..bf54c63fc 100644 --- a/lib/eventmachine.rb +++ b/lib/eventmachine.rb @@ -1521,6 +1521,12 @@ def self.event_callback conn_binding, opcode, data elsif opcode == ConnectionCompleted c = @conns[conn_binding] or raise ConnectionNotBound, "received ConnectionCompleted for unknown signature: #{conn_binding}" c.connection_completed + elsif opcode == SslHandshakeCompleted + c = @conns[conn_binding] or raise ConnectionNotBound, "received SslHandshakeCompleted for unknown signature: #{conn_binding}" + c.ssl_handshake_completed + elsif opcode == SslVerify + c = @conns[conn_binding] or raise ConnectionNotBound, "received SslVerify for unknown signature: #{conn_binding}" + c.close_connection if c.ssl_verify_peer(data) == false elsif opcode == TimerFired t = @timers.delete( data ) return if t == false # timer cancelled diff --git a/rakelib/test_pure.rake b/rakelib/test_pure.rake new file mode 100644 index 000000000..5a84ded20 --- /dev/null +++ b/rakelib/test_pure.rake @@ -0,0 +1,13 @@ +require 'rake/testtask' + +Rake::TestTask.new(:test_pure) do |t| + t.libs << 'tests' + t.libs << 'lib' + t.test_files = Dir.glob('tests/**/test_pure*.rb') + Dir.glob('tests/**/test_ssl*.rb') + t.warning = true +end + +task :test_em_pure_ruby do + ENV['EM_PURE_RUBY'] = 'true' + Rake::Task['test_pure'].execute +end diff --git a/tests/em_test_helper.rb b/tests/em_test_helper.rb index 6b6470fc4..20a3e59a6 100644 --- a/tests/em_test_helper.rb +++ b/tests/em_test_helper.rb @@ -1,8 +1,11 @@ +require 'em/pure_ruby' if ENV['EM_PURE_RUBY'] require 'eventmachine' require 'test/unit' require 'rbconfig' require 'socket' +puts "EM Library Type: #{EM.library_type}" + class Test::Unit::TestCase class EMTestTimeout < StandardError ; end diff --git a/tests/test_pure.rb b/tests/test_pure.rb index a870df40c..8863a8d10 100644 --- a/tests/test_pure.rb +++ b/tests/test_pure.rb @@ -85,4 +85,55 @@ def test_reactor_running assert a end + module TLSServer + def post_init + start_tls + end + + def ssl_handshake_completed + $server_handshake_completed = true + end + + def receive_data(data) + $server_received_data = data + send_data(data) + end + end + + module TLSClient + def post_init + start_tls + end + + def ssl_handshake_completed + $client_handshake_completed = true + end + + def connection_completed + send_data('Hello World!') + end + + def receive_data(data) + $client_received_data = data + close_connection + end + + def unbind + EM.stop_event_loop + end + end + + def test_start_tls + $client_handshake_completed, $server_handshake_completed = false, false + $client_received_data, $server_received_data = nil, nil + EM.run do + EM.start_server("127.0.0.1", 16789, TLSServer) + EM.connect("127.0.0.1", 16789, TLSClient) + end + + assert($client_handshake_completed) + assert($server_handshake_completed) + assert($client_received_data == "Hello World!") + assert($server_received_data == "Hello World!") + end end diff --git a/tests/test_ssl_dhparam.rb b/tests/test_ssl_dhparam.rb index bfca62355..85f52d2f1 100644 --- a/tests/test_ssl_dhparam.rb +++ b/tests/test_ssl_dhparam.rb @@ -7,7 +7,7 @@ def setup end module Client - def connection_completed + def post_init start_tls end @@ -46,6 +46,7 @@ def ssl_handshake_completed def test_no_dhparam omit_unless(EM.ssl?) + omit_if(EM.library_type == :pure_ruby) # DH will work with defaults omit_if(rbx?) $client_handshake_completed, $server_handshake_completed = false, false diff --git a/tests/test_ssl_ecdh_curve.rb b/tests/test_ssl_ecdh_curve.rb index 78fd59067..8dc167b67 100644 --- a/tests/test_ssl_ecdh_curve.rb +++ b/tests/test_ssl_ecdh_curve.rb @@ -2,7 +2,7 @@ class TestSslEcdhCurve < Test::Unit::TestCase module Client - def connection_completed + def post_init start_tls end @@ -56,6 +56,7 @@ def test_no_ecdh_curve def test_ecdh_curve omit_unless(EM.ssl?) + omit_if(EM.library_type == :pure_ruby && RUBY_VERSION < "2.3.0") omit_if(rbx?) $client_handshake_completed, $server_handshake_completed = false, false diff --git a/tests/test_ssl_extensions.rb b/tests/test_ssl_extensions.rb index 549ad7470..0610ba88f 100644 --- a/tests/test_ssl_extensions.rb +++ b/tests/test_ssl_extensions.rb @@ -16,7 +16,7 @@ def unbind EM.stop_event_loop end - def connection_completed + def post_init start_tls(:ssl_version => :tlsv1, :sni_hostname => 'example.com') end end diff --git a/tests/test_ssl_protocols.rb b/tests/test_ssl_protocols.rb index 9a9308afd..bcb682461 100644 --- a/tests/test_ssl_protocols.rb +++ b/tests/test_ssl_protocols.rb @@ -25,21 +25,21 @@ def ssl_handshake_completed module ClientAny include Client - def connection_completed + def post_init start_tls(:ssl_version => %w(sslv2 sslv3 tlsv1 tlsv1_1 tlsv1_2)) end end module ClientDefault include Client - def connection_completed + def post_init start_tls end end module ClientSSLv3 include Client - def connection_completed + def post_init start_tls(:ssl_version => %w(SSLv3)) end end diff --git a/tests/test_ssl_verify.rb b/tests/test_ssl_verify.rb index 87bd7ffb8..a6d8fca77 100644 --- a/tests/test_ssl_verify.rb +++ b/tests/test_ssl_verify.rb @@ -98,6 +98,7 @@ def test_fail_no_peer_cert def test_accept_server omit_unless(EM.ssl?) + omit_if(EM.library_type == :pure_ruby) # Server has a default cert chain omit_if(rbx?) $client_handshake_completed, $server_handshake_completed = false, false EM.run { @@ -112,6 +113,7 @@ def test_accept_server def test_deny_server omit_unless(EM.ssl?) + omit_if(EM.library_type == :pure_ruby) # Server has a default cert chain omit_if(rbx?) $client_handshake_completed, $server_handshake_completed = false, false EM.run { From ce76a06987491f92d043fdb5a32bb2e7b37cdaca Mon Sep 17 00:00:00 2001 From: Peter Shih Date: Mon, 6 Jun 2016 09:20:18 +0800 Subject: [PATCH 100/343] Fix incorrect tutorial link in README.md The old link redirects to `http://everburning.com/news/eventmachine-introductions/.html` --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index af81b88a6..0b8bad3c2 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ or add this to your Gemfile if you use [Bundler](http://gembundler.com/): For an introduction to EventMachine, check out: * [blog post about EventMachine by Ilya Grigorik](http://www.igvita.com/2008/05/27/ruby-eventmachine-the-speed-demon/). - * [EventMachine Introductions by Dan Sinclair](http://everburning.com/news/eventmachine-introductions/). + * [EventMachine Introductions by Dan Sinclair](http://everburning.com/news/eventmachine-introductions.html). ### Server example: Echo server ### From c680424707c047ff9e0f87960c19a84c6b2bd840 Mon Sep 17 00:00:00 2001 From: Luis Fonseca Date: Mon, 8 Aug 2016 17:45:11 +0100 Subject: [PATCH 101/343] Fix deregistering descriptor when using KQUEUE It was possible that a write was planned to happen but the socket got closed in the meantime. This would like to a serious error as it would then attempt to write on closed socket (-1 socket) and crash. --- ext/em.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ext/em.cpp b/ext/em.cpp index f8ff5407d..c56e26cae 100644 --- a/ext/em.cpp +++ b/ext/em.cpp @@ -1889,6 +1889,14 @@ void EventMachine_t::Deregister (EventableDescriptor *ed) ModifiedDescriptors.erase(ed); } #endif + + #ifdef HAVE_KQUEUE + if (Poller == Poller_Kqueue) { + assert (ed->GetSocket() != INVALID_SOCKET); + + ModifiedDescriptors.erase(ed); + } + #endif } From 3ab671f24d67768f75878e4d45e77bd23143063a Mon Sep 17 00:00:00 2001 From: YusakuOmasa Date: Mon, 29 Aug 2016 09:08:48 +0900 Subject: [PATCH 102/343] Fix to enable to work an example code in EM::Pool --- lib/em/pool.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/em/pool.rb b/lib/em/pool.rb index 2ecdcd2b9..b21978528 100644 --- a/lib/em/pool.rb +++ b/lib/em/pool.rb @@ -6,6 +6,8 @@ module EventMachine # # Example: # + # require 'em-http-request' + # # EM.run do # pool = EM::Pool.new # spawn = lambda { pool.add EM::HttpRequest.new('http://example.org') } @@ -21,7 +23,8 @@ module EventMachine # # pool.on_error { |conn| spawn[] } # - # 100.times do + # 100.times do |i| + # scheduled += 1 # pool.perform do |conn| # req = conn.get :path => '/', :keepalive => true # From d3c9468b3149f4fd172acd0293b68e9c91b985b6 Mon Sep 17 00:00:00 2001 From: YusakuOmasa Date: Mon, 29 Aug 2016 09:27:25 +0900 Subject: [PATCH 103/343] Rewrite comments to yard style --- lib/em/pool.rb | 71 ++++++++++++++++++++++++-------------------------- 1 file changed, 34 insertions(+), 37 deletions(-) diff --git a/lib/em/pool.rb b/lib/em/pool.rb index b21978528..2cb3662d0 100644 --- a/lib/em/pool.rb +++ b/lib/em/pool.rb @@ -1,44 +1,41 @@ module EventMachine - # = EventMachine::Pool - # # A simple async resource pool based on a resource and work queue. Resources # are enqueued and work waits for resources to become available. # - # Example: + # @example + # require 'em-http-request' + # + # EM.run do + # pool = EM::Pool.new + # spawn = lambda { pool.add EM::HttpRequest.new('http://example.org') } + # 10.times { spawn[] } + # done, scheduled = 0, 0 + # + # check = lambda do + # done += 1 + # if done >= scheduled + # EM.stop + # end + # end + # + # pool.on_error { |conn| spawn[] } + # + # 100.times do |i| + # scheduled += 1 + # pool.perform do |conn| + # req = conn.get :path => '/', :keepalive => true + # + # req.callback do + # p [:success, conn.object_id, i, req.response.size] + # check[] + # end # - # require 'em-http-request' + # req.errback { check[] } # - # EM.run do - # pool = EM::Pool.new - # spawn = lambda { pool.add EM::HttpRequest.new('http://example.org') } - # 10.times { spawn[] } - # done, scheduled = 0, 0 - # - # check = lambda do - # done += 1 - # if done >= scheduled - # EM.stop - # end - # end - # - # pool.on_error { |conn| spawn[] } - # - # 100.times do |i| - # scheduled += 1 - # pool.perform do |conn| - # req = conn.get :path => '/', :keepalive => true - # - # req.callback do - # p [:success, conn.object_id, i, req.response.size] - # check[] - # end - # - # req.errback { check[] } - # - # req - # end - # end - # end + # req + # end + # end + # end # # Resources are expected to be controlled by an object responding to a # deferrable/completion style API with callback and errback blocks. @@ -67,8 +64,8 @@ def remove resource # example use case is periodic statistics collection against a set of # connection resources. # - # For example: - # pool.contents.inject(0) { |sum, connection| connection.num_bytes } + # @example + # pool.contents.inject(0) { |sum, connection| connection.num_bytes } def contents @contents.dup end From 1b57c982f4efd93d8e53f7b1f06a1cb252c358a5 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Fri, 28 Oct 2016 02:42:33 -0700 Subject: [PATCH 104/343] Drop Rubinius from Travis CI matrix --- .travis.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 42ed97dc6..4305de074 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,14 +16,14 @@ rvm: - 1.9.2 - 1.8.7 - ruby-head - - rbx - - rbx-2 +# - rbx +# - rbx-2 # - jruby-1.7 # - jruby-9 matrix: allow_failures: - - rvm: rbx - - rvm: rbx-2 +# - rvm: rbx +# - rvm: rbx-2 - rvm: ruby-head - rvm: 2.0.0 - rvm: 2.0.0 From 71a138f63f50063545099513553aa9e1d0bafa98 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Sun, 30 Oct 2016 00:10:55 -0700 Subject: [PATCH 105/343] Whitespace --- ext/em.cpp | 2 +- ext/rubymain.cpp | 18 +++++++----------- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/ext/em.cpp b/ext/em.cpp index c56e26cae..2b6a2ce35 100644 --- a/ext/em.cpp +++ b/ext/em.cpp @@ -217,7 +217,7 @@ void EventMachine_t::ScheduleHalt() bool EventMachine_t::Stopping() { - return bTerminateSignalReceived; + return bTerminateSignalReceived; } /******************************* diff --git a/ext/rubymain.cpp b/ext/rubymain.cpp index 430564335..46195b92c 100644 --- a/ext/rubymain.cpp +++ b/ext/rubymain.cpp @@ -534,7 +534,7 @@ static VALUE t_get_subprocess_status (VALUE self UNUSED, VALUE signature) rb_iv_set(proc_status, "@status", INT2FIX(WEXITSTATUS(status))); } else if (WIFSIGNALED(status)) { rb_iv_set(proc_status, "@termsig", INT2FIX(WTERMSIG(status))); - } else if (WIFSTOPPED(status)){ + } else if (WIFSTOPPED(status)) { rb_iv_set(proc_status, "@stopsig", INT2FIX(WSTOPSIG(status))); } #endif @@ -1158,15 +1158,11 @@ t_stopping static VALUE t_stopping () { - if (evma_stopping()) - { - return Qtrue; - } - else - { - return Qfalse; - } - + if (evma_stopping()) { + return Qtrue; + } else { + return Qfalse; + } } @@ -1490,7 +1486,7 @@ extern "C" void Init_rubyeventmachine() rb_define_const (EmModule, "ConnectionNotifyReadable", INT2NUM(EM_CONNECTION_NOTIFY_READABLE)); rb_define_const (EmModule, "ConnectionNotifyWritable", INT2NUM(EM_CONNECTION_NOTIFY_WRITABLE)); rb_define_const (EmModule, "SslHandshakeCompleted", INT2NUM(EM_SSL_HANDSHAKE_COMPLETED )); - rb_define_const (EmModule, "SslVerify", INT2NUM(EM_SSL_VERIFY )); + rb_define_const (EmModule, "SslVerify", INT2NUM(EM_SSL_VERIFY )); // EM_PROXY_TARGET_UNBOUND = 110, // EM_PROXY_COMPLETED = 111 From 7781236314a48fb8566832802c0aaabff29f6ad4 Mon Sep 17 00:00:00 2001 From: Donovan Lampa Date: Tue, 8 Nov 2016 12:12:00 -0600 Subject: [PATCH 106/343] Use Acceptors to get peer and sock names if not present in Connections (#743) * Get Peername and sockname from acceptors if connection not available * Add missing Constant for jeventmachine --- java/src/com/rubyeventmachine/EmReactor.java | 20 ++++++++++++++++++-- lib/jeventmachine.rb | 2 ++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/java/src/com/rubyeventmachine/EmReactor.java b/java/src/com/rubyeventmachine/EmReactor.java index a31aa2296..02755f217 100644 --- a/java/src/com/rubyeventmachine/EmReactor.java +++ b/java/src/com/rubyeventmachine/EmReactor.java @@ -521,11 +521,27 @@ public void setTimerQuantum (int mills) { } public Object[] getPeerName (long sig) { - return Connections.get(sig).getPeerName(); + EventableChannel channel = Connections.get(sig); + if (channel != null) { + return Connections.get(sig).getPeerName(); + } + else { + ServerSocketChannel acceptor = Acceptors.get(sig); + return new Object[] { acceptor.socket().getLocalPort(), + acceptor.socket().getInetAddress().getHostAddress() }; + } } public Object[] getSockName (long sig) { - return Connections.get(sig).getSockName(); + EventableChannel channel = Connections.get(sig); + if (channel != null) { + return Connections.get(sig).getSockName(); + } + else { + ServerSocketChannel acceptor = Acceptors.get(sig); + return new Object[] { acceptor.socket().getLocalPort(), + acceptor.socket().getInetAddress().getHostAddress() }; + } } public long attachChannel (SocketChannel sc, boolean watch_mode) { diff --git a/lib/jeventmachine.rb b/lib/jeventmachine.rb index 383e748d4..426167246 100644 --- a/lib/jeventmachine.rb +++ b/lib/jeventmachine.rb @@ -77,6 +77,8 @@ module EventMachine ConnectionNotifyWritable = 107 # @private SslHandshakeCompleted = 108 + # @private + SslVerify = 109 # Exceptions that are defined in rubymain.cpp class ConnectionError < RuntimeError; end From 1c086ee01b626660f64533f385e26dd52554c819 Mon Sep 17 00:00:00 2001 From: Sean Porter Date: Tue, 8 Nov 2016 10:13:20 -0800 Subject: [PATCH 107/343] Pure Ruby EM, rescue ECONNREFUSED on initial TCP connect (#741) --- lib/em/pure_ruby.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/em/pure_ruby.rb b/lib/em/pure_ruby.rb index 81c5f1456..4ea03300c 100644 --- a/lib/em/pure_ruby.rb +++ b/lib/em/pure_ruby.rb @@ -879,7 +879,7 @@ def self.connect bind_addr, bind_port, host, port # TODO, this assumes a current Ruby snapshot. # We need to degrade to a nonblocking connect otherwise. sd.connect_nonblock( Socket.pack_sockaddr_in( port, host )) - rescue Errno::EINPROGRESS + rescue Errno::ECONNREFUSED, Errno::EINPROGRESS end EvmaTCPClient.new sd end From cad30d4c7d2ddd96d0de36524b1ea8267227c128 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Tue, 8 Nov 2016 12:08:43 -0800 Subject: [PATCH 108/343] Use gai_strerror to get the failure string from getaddrinfo --- ext/ed.cpp | 2 +- ext/em.cpp | 25 ++++++++++++++----------- ext/em.h | 2 +- tests/test_basic.rb | 32 ++++++++++++++++++++++++++++++++ 4 files changed, 48 insertions(+), 13 deletions(-) diff --git a/ext/ed.cpp b/ext/ed.cpp index 2852bd1b5..c577c10fe 100644 --- a/ext/ed.cpp +++ b/ext/ed.cpp @@ -1922,7 +1922,7 @@ int DatagramDescriptor::SendOutboundDatagram (const char *data, unsigned long le struct sockaddr_in6 addr_here; size_t addr_here_len = sizeof addr_here; - if (!EventMachine_t::name2address (address, port, (struct sockaddr *)&addr_here, &addr_here_len)) + if (0 != EventMachine_t::name2address (address, port, (struct sockaddr *)&addr_here, &addr_here_len)) return -1; if (!data && (length > 0)) diff --git a/ext/em.cpp b/ext/em.cpp index 2b6a2ce35..18f9f5135 100644 --- a/ext/em.cpp +++ b/ext/em.cpp @@ -1194,9 +1194,10 @@ const uintptr_t EventMachine_t::ConnectToServer (const char *bind_addr, int bind struct sockaddr_storage bind_as; size_t bind_as_len = sizeof bind_as; - if (!name2address (server, port, (struct sockaddr *)&bind_as, &bind_as_len)) { + int gai = name2address (server, port, (struct sockaddr *)&bind_as, &bind_as_len); + if (gai != 0) { char buf [200]; - snprintf (buf, sizeof(buf)-1, "unable to resolve server address: %s", strerror(errno)); + snprintf (buf, sizeof(buf)-1, "unable to resolve address: %s", gai_strerror(gai)); throw std::runtime_error (buf); } @@ -1222,9 +1223,12 @@ const uintptr_t EventMachine_t::ConnectToServer (const char *bind_addr, int bind if (bind_addr) { struct sockaddr_storage bind_to; size_t bind_to_len = sizeof bind_to; - if (!name2address (bind_addr, bind_port, (struct sockaddr *)&bind_to, &bind_to_len)) { + gai = name2address (bind_addr, bind_port, (struct sockaddr *)&bind_to, &bind_to_len); + if (gai != 0) { close (sd); - throw std::runtime_error ("invalid bind address"); + char buf [200]; + snprintf (buf, sizeof(buf)-1, "invalid bind address: %s", gai_strerror(gai)); + throw std::runtime_error (buf); } if (bind (sd, (struct sockaddr *)&bind_to, bind_to_len) < 0) { close (sd); @@ -1539,7 +1543,7 @@ int EventMachine_t::DetachFD (EventableDescriptor *ed) name2address ************/ -bool EventMachine_t::name2address (const char *server, int port, struct sockaddr *addr, size_t *addr_len) +int EventMachine_t::name2address (const char *server, int port, struct sockaddr *addr, size_t *addr_len) { if (!server || !*server) server = "0.0.0.0"; @@ -1553,16 +1557,15 @@ bool EventMachine_t::name2address (const char *server, int port, struct sockaddr char portstr[12]; snprintf(portstr, sizeof(portstr), "%u", port); - if (getaddrinfo (server, portstr, &hints, &ai) == 0) { + int gai = getaddrinfo (server, portstr, &hints, &ai); + if (gai == 0) { assert (ai->ai_addrlen <= *addr_len); memcpy (addr, ai->ai_addr, ai->ai_addrlen); *addr_len = ai->ai_addrlen; - freeaddrinfo(ai); - return true; } - return false; + return gai; } @@ -1581,7 +1584,7 @@ const uintptr_t EventMachine_t::CreateTcpServer (const char *server, int port) struct sockaddr_storage bind_here; size_t bind_here_len = sizeof bind_here; - if (!name2address (server, port, (struct sockaddr *)&bind_here, &bind_here_len)) + if (0 != name2address (server, port, (struct sockaddr *)&bind_here, &bind_here_len)) return 0; SOCKET sd_accept = EmSocket (bind_here.ss_family, SOCK_STREAM, 0); @@ -1636,7 +1639,7 @@ const uintptr_t EventMachine_t::OpenDatagramSocket (const char *address, int por struct sockaddr_storage bind_here; size_t bind_here_len = sizeof bind_here; - if (!name2address (address, port, (struct sockaddr *)&bind_here, &bind_here_len)) + if (0 != name2address (address, port, (struct sockaddr *)&bind_here, &bind_here_len)) return 0; // from here on, early returns must close the socket! diff --git a/ext/em.h b/ext/em.h index c4b91d36d..b76877106 100644 --- a/ext/em.h +++ b/ext/em.h @@ -201,7 +201,7 @@ class EventMachine_t Poller_t GetPoller() { return Poller; } - static bool name2address (const char *server, int port, struct sockaddr *addr, size_t *addr_len); + static int name2address (const char *server, int port, struct sockaddr *addr, size_t *addr_len); private: void _RunTimers(); diff --git a/tests/test_basic.rb b/tests/test_basic.rb index 0fb2b579e..21a8c41a8 100644 --- a/tests/test_basic.rb +++ b/tests/test_basic.rb @@ -171,6 +171,38 @@ def test_bind_connect assert_equal local_ip, ip end + def test_invalid_address_bind_connect_dst + e = nil + EM.run do + begin + EM.bind_connect('localhost', nil, 'invalid.invalid', 80) + rescue Exception => e + # capture the exception + ensure + EM.stop + end + end + + assert_kind_of(EventMachine::ConnectionError, e) + assert_match(/unable to resolve address:.*not known/, e.message) + end + + def test_invalid_address_bind_connect_src + e = nil + EM.run do + begin + EM.bind_connect('invalid.invalid', nil, 'localhost', 80) + rescue Exception => e + # capture the exception + ensure + EM.stop + end + end + + assert_kind_of(EventMachine::ConnectionError, e) + assert_match(/invalid bind address:.*not known/, e.message) + end + def test_reactor_thread? assert !EM.reactor_thread? EM.run { assert EM.reactor_thread?; EM.stop } From aac3fad2c107993a69fb3af41e2601661bbc84ad Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Tue, 8 Nov 2016 14:11:38 -0800 Subject: [PATCH 109/343] Travis CI updates: Trusty and Xcode 8 --- .travis.yml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4305de074..b3e9e4794 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,20 +7,20 @@ env: - TESTOPTS=-v language: ruby sudo: false +dist: trusty rvm: - 2.3.0 - 2.2 - 2.1 - 2.0.0 - 1.9.3 - - 1.9.2 - - 1.8.7 - ruby-head # - rbx # - rbx-2 # - jruby-1.7 # - jruby-9 matrix: + fast_finish: true allow_failures: # - rvm: rbx # - rvm: rbx-2 @@ -28,10 +28,14 @@ matrix: - rvm: 2.0.0 - rvm: 2.0.0 os: osx - osx_image: xcode7.2 + osx_image: xcode8 # - rvm: jruby-1.7 # - rvm: jruby-9 include: + - rvm: 1.8.7 + dist: precise + - rvm: 1.9.2 + dist: precise - rvm: 2.0.0 os: osx - osx_image: xcode7.2 + osx_image: xcode8 From cc0270a16afaa7b8649ebc2f7a53eaa3b348eb4c Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Tue, 9 Feb 2016 07:17:49 -0800 Subject: [PATCH 110/343] Throw strerror(errno) when getsockname or getpeername fail --- ext/ed.cpp | 32 ++++++++++++++++++++------------ ext/rubymain.cpp | 16 ++++++++++++---- 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/ext/ed.cpp b/ext/ed.cpp index c577c10fe..69135aac1 100644 --- a/ext/ed.cpp +++ b/ext/ed.cpp @@ -1956,13 +1956,17 @@ ConnectionDescriptor::GetPeername bool ConnectionDescriptor::GetPeername (struct sockaddr *s, socklen_t *len) { - bool ok = false; - if (s) { - int gp = getpeername (GetSocket(), s, len); - if (gp == 0) - ok = true; + if (!s) + return false; + + int gp = getpeername (GetSocket(), s, len); + if (gp == -1) { + char buf[200]; + snprintf (buf, sizeof(buf)-1, "unable to get peer name: %s", strerror(errno)); + throw std::runtime_error (buf); } - return ok; + + return true; } /********************************* @@ -1971,13 +1975,17 @@ ConnectionDescriptor::GetSockname bool ConnectionDescriptor::GetSockname (struct sockaddr *s, socklen_t *len) { - bool ok = false; - if (s) { - int gp = getsockname (GetSocket(), s, len); - if (gp == 0) - ok = true; + if (!s) + return false; + + int gp = getsockname (GetSocket(), s, len); + if (gp == -1) { + char buf[200]; + snprintf (buf, sizeof(buf)-1, "unable to get sock name: %s", strerror(errno)); + throw std::runtime_error (buf); } - return ok; + + return true; } diff --git a/ext/rubymain.cpp b/ext/rubymain.cpp index 46195b92c..11e77839b 100644 --- a/ext/rubymain.cpp +++ b/ext/rubymain.cpp @@ -472,8 +472,12 @@ static VALUE t_get_peername (VALUE self UNUSED, VALUE signature) { char buf[1024]; socklen_t len = sizeof buf; - if (evma_get_peername (NUM2BSIG (signature), (struct sockaddr*)buf, &len)) { - return rb_str_new (buf, len); + try { + if (evma_get_peername (NUM2BSIG (signature), (struct sockaddr*)buf, &len)) { + return rb_str_new (buf, len); + } + } catch (std::runtime_error e) { + rb_raise (rb_eRuntimeError, "%s", e.what()); } return Qnil; @@ -487,8 +491,12 @@ static VALUE t_get_sockname (VALUE self UNUSED, VALUE signature) { char buf[1024]; socklen_t len = sizeof buf; - if (evma_get_sockname (NUM2BSIG (signature), (struct sockaddr*)buf, &len)) { - return rb_str_new (buf, len); + try { + if (evma_get_sockname (NUM2BSIG (signature), (struct sockaddr*)buf, &len)) { + return rb_str_new (buf, len); + } + } catch (std::runtime_error e) { + rb_raise (rb_eRuntimeError, "%s", e.what()); } return Qnil; From 5602a6eee58f8775591ed266ee96bb9a5f97da34 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Wed, 24 Feb 2016 23:31:21 -0800 Subject: [PATCH 111/343] Use a single concrete implementation of getpeername/getsockname, the rest pure virtuals --- ext/ed.cpp | 111 +++++++++++++++++++---------------------------------- ext/ed.h | 31 +++++++++++---- 2 files changed, 62 insertions(+), 80 deletions(-) diff --git a/ext/ed.cpp b/ext/ed.cpp index 69135aac1..4c609f20e 100644 --- a/ext/ed.cpp +++ b/ext/ed.cpp @@ -340,6 +340,45 @@ void EventableDescriptor::_GenericInboundDispatch(const char *buf, unsigned long } +/********************************* +EventableDescriptor::_GenericGetPeername +*********************************/ + +bool EventableDescriptor::_GenericGetPeername (struct sockaddr *s, socklen_t *len) +{ + if (!s) + return false; + + int gp = getpeername (GetSocket(), s, len); + if (gp == -1) { + char buf[200]; + snprintf (buf, sizeof(buf)-1, "unable to get peer name: %s", strerror(errno)); + throw std::runtime_error (buf); + } + + return true; +} + +/********************************* +EventableDescriptor::_GenericGetSockname +*********************************/ + +bool EventableDescriptor::_GenericGetSockname (struct sockaddr *s, socklen_t *len) +{ + if (!s) + return false; + + int gp = getsockname (GetSocket(), s, len); + if (gp == -1) { + char buf[200]; + snprintf (buf, sizeof(buf)-1, "unable to get sock name: %s", strerror(errno)); + throw std::runtime_error (buf); + } + + return true; +} + + /********************************************* EventableDescriptor::GetPendingConnectTimeout *********************************************/ @@ -1628,23 +1667,6 @@ void AcceptorDescriptor::Heartbeat() } -/******************************* -AcceptorDescriptor::GetSockname -*******************************/ - -bool AcceptorDescriptor::GetSockname (struct sockaddr *s, socklen_t *len) -{ - bool ok = false; - if (s) { - int gp = getsockname (GetSocket(), s, len); - if (gp == 0) - ok = true; - } - return ok; -} - - - /************************************** DatagramDescriptor::DatagramDescriptor **************************************/ @@ -1950,45 +1972,6 @@ int DatagramDescriptor::SendOutboundDatagram (const char *data, unsigned long le } -/********************************* -ConnectionDescriptor::GetPeername -*********************************/ - -bool ConnectionDescriptor::GetPeername (struct sockaddr *s, socklen_t *len) -{ - if (!s) - return false; - - int gp = getpeername (GetSocket(), s, len); - if (gp == -1) { - char buf[200]; - snprintf (buf, sizeof(buf)-1, "unable to get peer name: %s", strerror(errno)); - throw std::runtime_error (buf); - } - - return true; -} - -/********************************* -ConnectionDescriptor::GetSockname -*********************************/ - -bool ConnectionDescriptor::GetSockname (struct sockaddr *s, socklen_t *len) -{ - if (!s) - return false; - - int gp = getsockname (GetSocket(), s, len); - if (gp == -1) { - char buf[200]; - snprintf (buf, sizeof(buf)-1, "unable to get sock name: %s", strerror(errno)); - throw std::runtime_error (buf); - } - - return true; -} - - /********************************************** ConnectionDescriptor::GetCommInactivityTimeout **********************************************/ @@ -2026,22 +2009,6 @@ bool DatagramDescriptor::GetPeername (struct sockaddr *s, socklen_t *len) return ok; } -/******************************* -DatagramDescriptor::GetSockname -*******************************/ - -bool DatagramDescriptor::GetSockname (struct sockaddr *s, socklen_t *len) -{ - bool ok = false; - if (s) { - int gp = getsockname (GetSocket(), s, len); - if (gp == 0) - ok = true; - } - return ok; -} - - /******************************************** DatagramDescriptor::GetCommInactivityTimeout diff --git a/ext/ed.h b/ext/ed.h index 2e4b4d048..9a3f5af79 100644 --- a/ext/ed.h +++ b/ext/ed.h @@ -64,8 +64,8 @@ class EventableDescriptor: public Bindable_t void SetEventCallback (EMCallback); - virtual bool GetPeername (struct sockaddr*, socklen_t*) {return false;} - virtual bool GetSockname (struct sockaddr*, socklen_t*) {return false;} + virtual bool GetPeername (struct sockaddr*, socklen_t*) = 0; + virtual bool GetSockname (struct sockaddr*, socklen_t*) = 0; virtual bool GetSubprocessPid (pid_t*) {return false;} virtual void StartTls() {} @@ -117,7 +117,9 @@ class EventableDescriptor: public Bindable_t bool bWatchOnly; EMCallback EventCallback; - void _GenericInboundDispatch(const char *buffer, unsigned long size); + void _GenericInboundDispatch (const char *buffer, unsigned long size); + bool _GenericGetPeername (struct sockaddr*, socklen_t*); + bool _GenericGetSockname (struct sockaddr*, socklen_t*); uint64_t CreatedAt; bool bCallbackUnbind; @@ -164,6 +166,9 @@ class LoopbreakDescriptor: public EventableDescriptor virtual bool SelectForRead() {return true;} virtual bool SelectForWrite() {return false;} + + virtual bool GetPeername (struct sockaddr* s, socklen_t* len) { return _GenericGetPeername (s, len); } + virtual bool GetSockname (struct sockaddr* s, socklen_t* len) { return _GenericGetSockname (s, len); } }; @@ -219,8 +224,8 @@ class ConnectionDescriptor: public EventableDescriptor void SetServerMode() {bIsServer = true;} - virtual bool GetPeername (struct sockaddr*, socklen_t*); - virtual bool GetSockname (struct sockaddr*, socklen_t*); + virtual bool GetPeername (struct sockaddr* s, socklen_t* len) { return _GenericGetPeername (s, len); } + virtual bool GetSockname (struct sockaddr* s, socklen_t* len) { return _GenericGetSockname (s, len); } virtual uint64_t GetCommInactivityTimeout(); virtual int SetCommInactivityTimeout (uint64_t value); @@ -305,8 +310,8 @@ class DatagramDescriptor: public EventableDescriptor // Do we have any data to write? This is used by ShouldDelete. virtual int GetOutboundDataSize() {return OutboundDataSize;} - virtual bool GetPeername (struct sockaddr*, socklen_t*); - virtual bool GetSockname (struct sockaddr*, socklen_t*); + virtual bool GetPeername (struct sockaddr* s, socklen_t* len); + virtual bool GetSockname (struct sockaddr* s, socklen_t* len) { return _GenericGetSockname (s, len); }; virtual uint64_t GetCommInactivityTimeout(); virtual int SetCommInactivityTimeout (uint64_t value); @@ -345,7 +350,8 @@ class AcceptorDescriptor: public EventableDescriptor virtual bool SelectForRead() {return true;} virtual bool SelectForWrite() {return false;} - virtual bool GetSockname (struct sockaddr*, socklen_t*); + virtual bool GetPeername (struct sockaddr* s, socklen_t* len) { return _GenericGetPeername (s, len); } + virtual bool GetSockname (struct sockaddr* s, socklen_t* len) { return _GenericGetSockname (s, len); }; static void StopAcceptor (const uintptr_t binding); }; @@ -371,6 +377,9 @@ class PipeDescriptor: public EventableDescriptor int SendOutboundData (const char*, unsigned long); virtual int GetOutboundDataSize() {return OutboundDataSize;} + virtual bool GetPeername (struct sockaddr* s, socklen_t* len) { return _GenericGetPeername (s, len); } + virtual bool GetSockname (struct sockaddr* s, socklen_t* len) { return _GenericGetSockname (s, len); } + virtual bool GetSubprocessPid (pid_t*); protected: @@ -413,6 +422,9 @@ class KeyboardDescriptor: public EventableDescriptor virtual bool SelectForRead() {return true;} virtual bool SelectForWrite() {return false;} + virtual bool GetPeername (struct sockaddr* s, socklen_t* len) { return _GenericGetPeername (s, len); } + virtual bool GetSockname (struct sockaddr* s, socklen_t* len) { return _GenericGetSockname (s, len); } + protected: bool bReadAttemptedAfterClose; @@ -437,6 +449,9 @@ class InotifyDescriptor: public EventableDescriptor virtual void Heartbeat() {} virtual bool SelectForRead() {return true;} virtual bool SelectForWrite() {return false;} + + virtual bool GetPeername (struct sockaddr* s, socklen_t* len) { return false; } + virtual bool GetSockname (struct sockaddr* s, socklen_t* len) { return false; } }; #endif // __EventableDescriptor__H_ From 9a409af8b9a304c80980f24ff08b8cb1d53637a4 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Wed, 16 Nov 2016 23:28:56 -0800 Subject: [PATCH 112/343] Changelog for v1.2.1 --- CHANGELOG.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 59c137f40..29cd58870 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # Changelog +## 1.2.1 (November 15, 2016) +* Throw strerror(errno) when getsockname or getpeername fail [#683] +* Use a single concrete implementation of getpeername/getsockname, the rest pure virtuals [#683] +* Use gai_strerror to get the failure string from getaddrinfo [#744] +* Fix deregistering descriptor when using KQUEUE [#728] +* Fix to enable to work an example code in EM::Pool [#731] +* LineText2: Add regular expression delimiter support [#706] +* Pure Ruby: EM rescue ECONNREFUSED on initial TCP connect [#741] +* Pure Ruby: EM SSL (working start_tls) [#712] +* Pure Ruby: EM fixes [#707] +* Java: Use Acceptors to get peer and sock names if not present in Connections [#743] + ## 1.2.0.1 (March 15, 2016) * Fix crash when accepting IPv6 connections due to struct sockaddr_in [#698, #699] From 98eca51d7904cdae5f2f4f7580998908dabd73a7 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Wed, 16 Nov 2016 23:29:28 -0800 Subject: [PATCH 113/343] Bump version to 1.2.1 --- lib/em/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/em/version.rb b/lib/em/version.rb index d2f971dd7..e29d984df 100644 --- a/lib/em/version.rb +++ b/lib/em/version.rb @@ -1,3 +1,3 @@ module EventMachine - VERSION = "1.2.0.1" + VERSION = "1.2.1" end From 3eb6d4bed42dd506cf4854395e23982108e792c9 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Sat, 19 Nov 2016 20:02:47 +0000 Subject: [PATCH 114/343] Set the socktype field for getaddrinfo This is required on Solaris for correct lookups. --- ext/ed.cpp | 2 +- ext/em.cpp | 11 ++++++----- ext/em.h | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/ext/ed.cpp b/ext/ed.cpp index 4c609f20e..c163630e0 100644 --- a/ext/ed.cpp +++ b/ext/ed.cpp @@ -1944,7 +1944,7 @@ int DatagramDescriptor::SendOutboundDatagram (const char *data, unsigned long le struct sockaddr_in6 addr_here; size_t addr_here_len = sizeof addr_here; - if (0 != EventMachine_t::name2address (address, port, (struct sockaddr *)&addr_here, &addr_here_len)) + if (0 != EventMachine_t::name2address (address, port, SOCK_DGRAM, (struct sockaddr *)&addr_here, &addr_here_len)) return -1; if (!data && (length > 0)) diff --git a/ext/em.cpp b/ext/em.cpp index 18f9f5135..5c1a25d6d 100644 --- a/ext/em.cpp +++ b/ext/em.cpp @@ -1194,7 +1194,7 @@ const uintptr_t EventMachine_t::ConnectToServer (const char *bind_addr, int bind struct sockaddr_storage bind_as; size_t bind_as_len = sizeof bind_as; - int gai = name2address (server, port, (struct sockaddr *)&bind_as, &bind_as_len); + int gai = name2address (server, port, SOCK_STREAM, (struct sockaddr *)&bind_as, &bind_as_len); if (gai != 0) { char buf [200]; snprintf (buf, sizeof(buf)-1, "unable to resolve address: %s", gai_strerror(gai)); @@ -1223,7 +1223,7 @@ const uintptr_t EventMachine_t::ConnectToServer (const char *bind_addr, int bind if (bind_addr) { struct sockaddr_storage bind_to; size_t bind_to_len = sizeof bind_to; - gai = name2address (bind_addr, bind_port, (struct sockaddr *)&bind_to, &bind_to_len); + gai = name2address (bind_addr, bind_port, SOCK_STREAM, (struct sockaddr *)&bind_to, &bind_to_len); if (gai != 0) { close (sd); char buf [200]; @@ -1543,7 +1543,7 @@ int EventMachine_t::DetachFD (EventableDescriptor *ed) name2address ************/ -int EventMachine_t::name2address (const char *server, int port, struct sockaddr *addr, size_t *addr_len) +int EventMachine_t::name2address (const char *server, int port, int socktype, struct sockaddr *addr, size_t *addr_len) { if (!server || !*server) server = "0.0.0.0"; @@ -1551,6 +1551,7 @@ int EventMachine_t::name2address (const char *server, int port, struct sockaddr struct addrinfo *ai; struct addrinfo hints; memset (&hints, 0, sizeof(hints)); + hints.ai_socktype = socktype; hints.ai_family = AF_UNSPEC; hints.ai_flags = AI_NUMERICSERV | AI_ADDRCONFIG; @@ -1584,7 +1585,7 @@ const uintptr_t EventMachine_t::CreateTcpServer (const char *server, int port) struct sockaddr_storage bind_here; size_t bind_here_len = sizeof bind_here; - if (0 != name2address (server, port, (struct sockaddr *)&bind_here, &bind_here_len)) + if (0 != name2address (server, port, SOCK_STREAM, (struct sockaddr *)&bind_here, &bind_here_len)) return 0; SOCKET sd_accept = EmSocket (bind_here.ss_family, SOCK_STREAM, 0); @@ -1639,7 +1640,7 @@ const uintptr_t EventMachine_t::OpenDatagramSocket (const char *address, int por struct sockaddr_storage bind_here; size_t bind_here_len = sizeof bind_here; - if (0 != name2address (address, port, (struct sockaddr *)&bind_here, &bind_here_len)) + if (0 != name2address (address, port, SOCK_DGRAM, (struct sockaddr *)&bind_here, &bind_here_len)) return 0; // from here on, early returns must close the socket! diff --git a/ext/em.h b/ext/em.h index b76877106..8cf596a91 100644 --- a/ext/em.h +++ b/ext/em.h @@ -201,7 +201,7 @@ class EventMachine_t Poller_t GetPoller() { return Poller; } - static int name2address (const char *server, int port, struct sockaddr *addr, size_t *addr_len); + static int name2address (const char *server, int port, int socktype, struct sockaddr *addr, size_t *addr_len); private: void _RunTimers(); From cafeba9d8bfa5d5e1c6b099a430e3cfd14a4f1a1 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Mon, 21 Nov 2016 23:33:52 -0800 Subject: [PATCH 115/343] Set initial value for EventableDescriptor NextHeartbeat --- ext/ed.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/ext/ed.cpp b/ext/ed.cpp index c163630e0..2d86d714a 100644 --- a/ext/ed.cpp +++ b/ext/ed.cpp @@ -84,6 +84,7 @@ EventableDescriptor::EventableDescriptor (SOCKET sd, EventMachine_t *em): MyEventMachine (em), PendingConnectTimeout(20000000), InactivityTimeout (0), + NextHeartbeat (0), bPaused (false) { /* There are three ways to close a socket, all of which should From c9a4af711db6b4a972f3cc0a31cda8db70f874f2 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Mon, 21 Nov 2016 23:34:13 -0800 Subject: [PATCH 116/343] Code alignment --- ext/ed.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/ed.cpp b/ext/ed.cpp index 2d86d714a..1c5a16130 100644 --- a/ext/ed.cpp +++ b/ext/ed.cpp @@ -113,12 +113,12 @@ EventableDescriptor::EventableDescriptor (SOCKET sd, EventMachine_t *em): if (MyEventMachine == NULL) throw std::runtime_error ("bad em in eventable descriptor"); CreatedAt = MyEventMachine->GetCurrentLoopTime(); + LastActivity = MyEventMachine->GetCurrentLoopTime(); #ifdef HAVE_EPOLL EpollEvent.events = 0; EpollEvent.data.ptr = this; #endif - LastActivity = MyEventMachine->GetCurrentLoopTime(); } From a167e7447799441c2a3fd7edc25bc04f25e5a411 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Mon, 21 Nov 2016 23:34:58 -0800 Subject: [PATCH 117/343] Travis CI update Ruby 2.3 series --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index b3e9e4794..93653af3b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,7 @@ language: ruby sudo: false dist: trusty rvm: - - 2.3.0 + - 2.3.2 - 2.2 - 2.1 - 2.0.0 From 0c0fc3f83d5ec58230612c71e1cc89565f9a7024 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Tue, 22 Nov 2016 11:54:59 -0800 Subject: [PATCH 118/343] Consolidate get_sock_opt and set_sock_opt tests, remove google.com dependency --- tests/test_get_sock_opt.rb | 37 -------------------------- tests/test_set_sock_opt.rb | 39 --------------------------- tests/test_sock_opt.rb | 54 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 76 deletions(-) delete mode 100644 tests/test_get_sock_opt.rb delete mode 100644 tests/test_set_sock_opt.rb create mode 100644 tests/test_sock_opt.rb diff --git a/tests/test_get_sock_opt.rb b/tests/test_get_sock_opt.rb deleted file mode 100644 index 9ef7e45e9..000000000 --- a/tests/test_get_sock_opt.rb +++ /dev/null @@ -1,37 +0,0 @@ -require 'em_test_helper' -require 'socket' - -class TestGetSockOpt < Test::Unit::TestCase - - if EM.respond_to? :get_sock_opt - def setup - assert(!EM.reactor_running?) - end - - def teardown - assert(!EM.reactor_running?) - end - - #------------------------------------- - - def test_get_sock_opt - test = self - EM.run do - EM.connect 'google.com', 80, Module.new { - define_method :connection_completed do - val = get_sock_opt Socket::SOL_SOCKET, Socket::SO_ERROR - test.assert_equal "\0\0\0\0", val - EM.stop - end - } - end - end - else - warn "EM.get_sock_opt not implemented, skipping tests in #{__FILE__}" - - # Because some rubies will complain if a TestCase class has no tests - def test_em_get_sock_opt_unsupported - assert true - end - end -end diff --git a/tests/test_set_sock_opt.rb b/tests/test_set_sock_opt.rb deleted file mode 100644 index 262819561..000000000 --- a/tests/test_set_sock_opt.rb +++ /dev/null @@ -1,39 +0,0 @@ -require 'em_test_helper' -require 'socket' - -class TestSetSockOpt < Test::Unit::TestCase - - if EM.respond_to? :set_sock_opt - def setup - assert(!EM.reactor_running?) - end - - def teardown - assert(!EM.reactor_running?) - end - - #------------------------------------- - - def test_set_sock_opt - omit_if(windows?) - - test = self - EM.run do - EM.connect 'google.com', 80, Module.new { - define_method :post_init do - val = set_sock_opt Socket::SOL_SOCKET, Socket::SO_BROADCAST, true - test.assert_equal 0, val - EM.stop - end - } - end - end - else - warn "EM.set_sock_opt not implemented, skipping tests in #{__FILE__}" - - # Because some rubies will complain if a TestCase class has no tests - def test_em_set_sock_opt_unsupported - assert true - end - end -end diff --git a/tests/test_sock_opt.rb b/tests/test_sock_opt.rb new file mode 100644 index 000000000..60fba351e --- /dev/null +++ b/tests/test_sock_opt.rb @@ -0,0 +1,54 @@ +require 'em_test_helper' +require 'socket' + +class TestSockOpt < Test::Unit::TestCase + def setup + assert(!EM.reactor_running?) + @port = next_port + end + + def teardown + assert(!EM.reactor_running?) + end + + def test_set_sock_opt + omit_if(windows?) + omit_if(!EM.respond_to?(:set_sock_opt)) + + val = nil + test_module = Module.new do + define_method :post_init do + val = set_sock_opt Socket::SOL_SOCKET, Socket::SO_BROADCAST, true + EM.stop + end + end + + EM.run do + EM.start_server '127.0.0.1', @port + EM.connect '127.0.0.1', @port, test_module + end + + assert_equal 0, val + end + + def test_get_sock_opt + omit_if(windows?) + omit_if(!EM.respond_to?(:set_sock_opt)) + + val = nil + test_module = Module.new do + define_method :connection_completed do + val = get_sock_opt Socket::SOL_SOCKET, Socket::SO_ERROR + EM.stop + end + end + + EM.run do + EM.start_server '127.0.0.1', @port + EM.connect '127.0.0.1', @port, test_module + end + + assert_equal "\0\0\0\0", val + end + +end From 57f99b891e4b59d94dd3ff2cf51ee04b9e97ba21 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Tue, 22 Nov 2016 12:49:27 -0800 Subject: [PATCH 119/343] Remove IPv6 test that connects to Google --- tests/test_ipv6.rb | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/tests/test_ipv6.rb b/tests/test_ipv6.rb index f3d66c639..15cb7eab1 100644 --- a/tests/test_ipv6.rb +++ b/tests/test_ipv6.rb @@ -4,32 +4,6 @@ class TestIPv6 < Test::Unit::TestCase if Test::Unit::TestCase.public_ipv6? - # Tries to connect to ipv6.google.com (2607:f8b0:4010:800::1006) port 80 via TCP. - # Timeout in 6 seconds. - def test_ipv6_tcp_client_with_ipv6_google_com - conn = nil - setup_timeout(6) - - EM.run do - conn = EM::connect("2607:f8b0:4010:800::1006", 80) do |c| - def c.connected - @connected - end - - def c.unbind(reason) - warn "unbind: #{reason.inspect}" if reason # XXX at least find out why it failed - end - - def c.connection_completed - @connected = true - EM.stop - end - end - end - - assert conn.connected - end - # Runs a TCP server in the local IPv6 address, connects to it and sends a specific data. # Timeout in 2 seconds. def test_ipv6_tcp_local_server From ca5d6e5c9147598c3edd8c1cf700bcdeb74398a3 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Tue, 22 Nov 2016 19:01:36 -0800 Subject: [PATCH 120/343] Use a well-known black-hole address range instead of Google --- tests/test_unbind_reason.rb | 40 +++++++++++++------------------------ 1 file changed, 14 insertions(+), 26 deletions(-) diff --git a/tests/test_unbind_reason.rb b/tests/test_unbind_reason.rb index e372d2c1d..ff9193e01 100644 --- a/tests/test_unbind_reason.rb +++ b/tests/test_unbind_reason.rb @@ -1,5 +1,4 @@ require 'em_test_helper' -require 'socket' class TestUnbindReason < Test::Unit::TestCase @@ -11,42 +10,31 @@ def unbind(reason = nil) end end + # RFC 5737 Address Blocks Reserved for Documentation def test_connect_timeout - error = nil - EM.run { - conn = EM.connect 'google.com', 81, Module.new{ |m| - m.send(:define_method, :unbind) do |reason| - error = reason - EM.stop - end - } - conn.pending_connect_timeout = TIMEOUT_INTERVAL - } - assert_equal Errno::ETIMEDOUT, error + conn = nil + EM.run do + conn = EM.connect '192.0.2.0', 80, StubConnection + conn.pending_connect_timeout = 1 + end + assert_equal Errno::ETIMEDOUT, conn.error end def test_connect_refused pend('FIXME: this test is broken on Windows') if windows? - - error = nil - EM.run { - EM.connect '127.0.0.1', 12388, Module.new{ |m| - m.send(:define_method, :unbind) do |reason| - error = reason - EM.stop - end - } - } - assert_equal Errno::ECONNREFUSED, error + conn = nil + EM.run do + conn = EM.connect '127.0.0.1', 12388, StubConnection + end + assert_equal Errno::ECONNREFUSED, conn.error end def test_optional_argument pend('FIXME: this test is broken on Windows') if windows? - conn = nil - EM.run { + EM.run do conn = EM.connect '127.0.0.1', 12388, StubConnection - } + end assert_equal Errno::ECONNREFUSED, conn.error end end From 8eabefc051c88f5eab9fc34d4f1748c171b72534 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Tue, 22 Nov 2016 21:57:46 -0800 Subject: [PATCH 121/343] Use a well-known black-hole address range, use EM.current_time instead of Time.now --- tests/test_pending_connect_timeout.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_pending_connect_timeout.rb b/tests/test_pending_connect_timeout.rb index 9c8203b20..a3f7fa4d8 100644 --- a/tests/test_pending_connect_timeout.rb +++ b/tests/test_pending_connect_timeout.rb @@ -25,7 +25,7 @@ def test_for_real timeout_handler = Module.new do define_method :unbind do - finish = Time.now + finish = EM.current_time EM.stop end end @@ -33,8 +33,8 @@ def test_for_real EM.run { setup_timeout EM.heartbeat_interval = 0.1 - start = Time.now - c = EM.connect("1.2.3.4", 54321, timeout_handler) + start = EM.current_time + c = EM.connect('192.0.2.0', 54321, timeout_handler) c.pending_connect_timeout = 0.2 } From a89d5177c6ec0e3ee8cd8ab0790f0b304593dbfe Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Tue, 22 Nov 2016 22:01:24 -0800 Subject: [PATCH 122/343] Use localhost instead of google.com --- tests/test_basic.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_basic.rb b/tests/test_basic.rb index 21a8c41a8..b8f625803 100644 --- a/tests/test_basic.rb +++ b/tests/test_basic.rb @@ -146,7 +146,7 @@ def test_byte_range_send def test_bind_connect pend('FIXME: this test is broken on Windows') if windows? - local_ip = UDPSocket.open {|s| s.connect('google.com', 80); s.addr.last } + local_ip = UDPSocket.open {|s| s.connect('localhost', 80); s.addr.last } bind_port = next_port From fc62e3bdcd385bbfe9cd45b320b928579def9d59 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Tue, 22 Nov 2016 22:19:47 -0800 Subject: [PATCH 123/343] Use local server and tighter timing for test get_idle_time --- tests/test_idle_connection.rb | 42 ++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/tests/test_idle_connection.rb b/tests/test_idle_connection.rb index 995463a18..bfc57cd99 100644 --- a/tests/test_idle_connection.rb +++ b/tests/test_idle_connection.rb @@ -1,25 +1,31 @@ require 'em_test_helper' class TestIdleConnection < Test::Unit::TestCase - if EM.respond_to?(:get_idle_time) - def test_idle_time - EM.run{ - conn = EM.connect 'www.google.com', 80 - EM.add_timer(3){ - $idle_time = conn.get_idle_time - conn.send_data "GET / HTTP/1.0\r\n\r\n" - EM.next_tick{ - EM.next_tick{ - $idle_time_after_send = conn.get_idle_time - conn.close_connection - EM.stop - } - } - } - } + def setup + @port = next_port + end + + def test_idle_time + omit_if(!EM.respond_to?(:get_idle_time)) - assert_in_delta 3, $idle_time, 0.2 - assert_in_delta 0, $idle_time_after_send, 0.1 + a, b = nil, nil + EM.run do + EM.start_server '127.0.0.1', @port, Module.new + conn = EM.connect '127.0.0.1', @port + EM.add_timer(0.3) do + a = conn.get_idle_time + conn.send_data 'a' + EM.next_tick do + EM.next_tick do + b = conn.get_idle_time + conn.close_connection + EM.stop + end + end + end end + + assert_in_delta 0.3, a, 0.1 + assert_in_delta 0, b, 0.1 end end From e18ff62b1ccff639d0edc0c79585e50436823960 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Tue, 22 Nov 2016 22:50:16 -0800 Subject: [PATCH 124/343] Remove IPv4 test that connects to Google --- tests/test_ipv4.rb | 170 +++++++++++++++++++-------------------------- 1 file changed, 70 insertions(+), 100 deletions(-) diff --git a/tests/test_ipv4.rb b/tests/test_ipv4.rb index e132fd359..bd11bbfe3 100644 --- a/tests/test_ipv4.rb +++ b/tests/test_ipv4.rb @@ -1,125 +1,95 @@ require 'em_test_helper' -require 'socket' class TestIPv4 < Test::Unit::TestCase - - if Test::Unit::TestCase.public_ipv4? - - # Tries to connect to www.google.com port 80 via TCP. - # Timeout in 2 seconds. - def test_ipv4_tcp_client - conn = nil - setup_timeout(2) - - EM.run do - conn = EM::connect("www.google.com", 80) do |c| - def c.connected - @connected - end - - def c.connection_completed - @connected = true - EM.stop - end + # Runs a TCP server in the local IPv4 address, connects to it and sends a specific data. + # Timeout in 2 seconds. + def test_ipv4_tcp_local_server + omit_if(!Test::Unit::TestCase.public_ipv4?) + + @@received_data = nil + @local_port = next_port + setup_timeout(2) + + EM.run do + EM::start_server(@@public_ipv4, @local_port) do |s| + def s.receive_data data + @@received_data = data + EM.stop end end - assert conn.connected - end - - # Runs a TCP server in the local IPv4 address, connects to it and sends a specific data. - # Timeout in 2 seconds. - def test_ipv4_tcp_local_server - @@received_data = nil - @local_port = next_port - setup_timeout(2) - - EM.run do - EM::start_server(@@public_ipv4, @local_port) do |s| - def s.receive_data data - @@received_data = data - EM.stop - end - end - - EM::connect(@@public_ipv4, @local_port) do |c| - c.send_data "ipv4/tcp" - end + EM::connect(@@public_ipv4, @local_port) do |c| + c.send_data "ipv4/tcp" end - - assert_equal "ipv4/tcp", @@received_data end - # Runs a UDP server in the local IPv4 address, connects to it and sends a specific data. - # Timeout in 2 seconds. - def test_ipv4_udp_local_server - @@received_data = nil - @local_port = next_port - setup_timeout(2) - - EM.run do - EM::open_datagram_socket(@@public_ipv4, @local_port) do |s| - def s.receive_data data - @@received_data = data - EM.stop - end - end + assert_equal "ipv4/tcp", @@received_data + end - EM::open_datagram_socket(@@public_ipv4, next_port) do |c| - c.send_datagram "ipv4/udp", @@public_ipv4, @local_port - end - end + # Runs a UDP server in the local IPv4 address, connects to it and sends a specific data. + # Timeout in 2 seconds. + def test_ipv4_udp_local_server + omit_if(!Test::Unit::TestCase.public_ipv4?) - assert_equal "ipv4/udp", @@received_data - end + @@received_data = nil + @local_port = next_port + setup_timeout(2) - # Try to connect via TCP to an invalid IPv4. EM.connect should raise - # EM::ConnectionError. - def test_tcp_connect_to_invalid_ipv4 - invalid_ipv4 = "9.9:9" - - EM.run do - begin - error = nil - EM.connect(invalid_ipv4, 1234) - rescue => e - error = e - ensure + EM.run do + EM::open_datagram_socket(@@public_ipv4, @local_port) do |s| + def s.receive_data data + @@received_data = data EM.stop - assert_equal EM::ConnectionError, (error && error.class) end end + + EM::open_datagram_socket(@@public_ipv4, next_port) do |c| + c.send_datagram "ipv4/udp", @@public_ipv4, @local_port + end end - # Try to send a UDP datagram to an invalid IPv4. EM.send_datagram should raise - # EM::ConnectionError. - def test_udp_send_datagram_to_invalid_ipv4 - invalid_ipv4 = "9.9:9" - - EM.run do - begin - error = nil - EM.open_datagram_socket(@@public_ipv4, next_port) do |c| - c.send_datagram "hello", invalid_ipv4, 1234 - end - rescue => e - error = e - ensure - EM.stop - assert_equal EM::ConnectionError, (error && error.class) - end + assert_equal "ipv4/udp", @@received_data + end + + # Try to connect via TCP to an invalid IPv4. EM.connect should raise + # EM::ConnectionError. + def test_tcp_connect_to_invalid_ipv4 + omit_if(!Test::Unit::TestCase.public_ipv4?) + + invalid_ipv4 = "9.9:9" + + EM.run do + begin + error = nil + EM.connect(invalid_ipv4, 1234) + rescue => e + error = e + ensure + EM.stop + assert_equal EM::ConnectionError, (error && error.class) end end + end + # Try to send a UDP datagram to an invalid IPv4. EM.send_datagram should raise + # EM::ConnectionError. + def test_udp_send_datagram_to_invalid_ipv4 + omit_if(!Test::Unit::TestCase.public_ipv4?) - else - warn "no IPv4 in this host, skipping tests in #{__FILE__}" + invalid_ipv4 = "9.9:9" - # Because some rubies will complain if a TestCase class has no tests - def test_ipv4_unavailable - assert true + EM.run do + begin + error = nil + EM.open_datagram_socket(@@public_ipv4, next_port) do |c| + c.send_datagram "hello", invalid_ipv4, 1234 + end + rescue => e + error = e + ensure + EM.stop + assert_equal EM::ConnectionError, (error && error.class) + end end - end - end From fe663e43e7d8a62303dbd55d740e9f0087b134e5 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Tue, 22 Nov 2016 22:51:36 -0800 Subject: [PATCH 125/343] Switch resolver tests from google.com to example.com But there isn't a public DNS record like 'example.com' with an A rrset, so we'll continue to use yahoo.com for that. --- tests/test_resolver.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/test_resolver.rb b/tests/test_resolver.rb index a69e515e6..58ed5f574 100644 --- a/tests/test_resolver.rb +++ b/tests/test_resolver.rb @@ -20,7 +20,7 @@ def test_a pend('FIXME: this test is broken on Windows') if windows? EM.run { - d = EM::DNS::Resolver.resolve "google.com" + d = EM::DNS::Resolver.resolve "example.com" d.errback { assert false } d.callback { |r| assert r @@ -45,6 +45,7 @@ def test_garbage } end + # There isn't a public DNS entry like 'example.com' with an A rrset def test_a_pair pend('FIXME: this test is broken on Windows') if windows? @@ -78,8 +79,8 @@ def test_timer_cleanup pend('FIXME: this test is broken on Windows') if windows? EM.run { - d = EM::DNS::Resolver.resolve "google.com" - d.errback { |err| assert false, "failed to resolve google.com: #{err}" } + d = EM::DNS::Resolver.resolve "example.com" + d.errback { |err| assert false, "failed to resolve example.com: #{err}" } d.callback { |r| # This isn't a great test, but it's hard to get more canonical # confirmation that the timer is cancelled From 79b091e583acec61c2d95d84fff5c31993faad66 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Wed, 23 Nov 2016 00:13:57 -0800 Subject: [PATCH 126/343] Make the iterator tests faster and more robust --- tests/test_iterator.rb | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/tests/test_iterator.rb b/tests/test_iterator.rb index a063634f8..6ec4e4642 100644 --- a/tests/test_iterator.rb +++ b/tests/test_iterator.rb @@ -2,8 +2,12 @@ class TestIterator < Test::Unit::TestCase - def get_time - EM.current_time.strftime('%H:%M:%S') + # By default, format the time with tenths-of-seconds. + # Some tests should ask for extra decimal places to ensure + # that delays between iterations will receive a changed time. + def get_time(n=1) + time = EM.current_time + time.strftime('%H:%M:%S.') + time.tv_usec.to_s[0, n] end def test_default_concurrency @@ -11,10 +15,10 @@ def test_default_concurrency list = 1..10 EM.run { EM::Iterator.new(list).each( proc {|num,iter| - time = get_time + time = get_time(3) items[time] ||= [] items[time] << num - EM::Timer.new(1) {iter.next} + EM::Timer.new(0.02) {iter.next} }, proc {EM.stop}) } assert_equal(10, items.keys.size) @@ -27,10 +31,10 @@ def test_default_concurrency_with_a_proc original_list = list.dup EM.run { EM::Iterator.new(proc{list.pop || EM::Iterator::Stop}).each( proc {|num,iter| - time = get_time + time = get_time(3) items[time] ||= [] items[time] << num - EM::Timer.new(1) {iter.next} + EM::Timer.new(0.02) {iter.next} }, proc {EM.stop}) } assert_equal(10, items.keys.size) @@ -52,26 +56,25 @@ def test_concurrency_bigger_than_list_size assert_equal(list.to_a.sort, items.values.flatten.sort) end - def test_changing_concurrency_affects_active_iteration items = {} list = 1..25 + seen = 0 EM.run { - i = EM::Iterator.new(list,5) + i = EM::Iterator.new(list,1) i.each(proc {|num,iter| time = get_time items[time] ||= [] items[time] << num - EM::Timer.new(1) {iter.next} + if (seen += 1) == 5 + # The first 5 items will be distinct times + # The next 20 items will happen in 2 bursts + i.concurrency = 10 + end + EM::Timer.new(0.2) {iter.next} }, proc {EM.stop}) - EM.add_timer(1){ - i.concurrency = 1 - } - EM.add_timer(3){ - i.concurrency = 3 - } } - assert_equal(9, items.keys.size) + assert_in_delta(7, items.keys.size, 1) assert_equal(list.to_a.sort, items.values.flatten.sort) end From 592e55926b2cc270cb15dbe1a4dc9f09b27c3172 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Wed, 23 Nov 2016 11:52:33 -0800 Subject: [PATCH 127/343] Tests updates for EM::P::HttpClient * Minor syntax updates, avoid trailing semicolons * Close connections to avoid hanging on keep-alive HTTP * Use next_port helper instead of hardcoded port numbers --- tests/test_httpclient.rb | 61 ++++++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 28 deletions(-) diff --git a/tests/test_httpclient.rb b/tests/test_httpclient.rb index 326d96889..572a8022b 100644 --- a/tests/test_httpclient.rb +++ b/tests/test_httpclient.rb @@ -2,13 +2,8 @@ class TestHttpClient < Test::Unit::TestCase - Localhost = "127.0.0.1" - Localport = 9801 - def setup - end - - def teardown + @port = next_port end #------------------------------------- @@ -19,6 +14,7 @@ def test_http_client c = silent { EM::P::HttpClient.send :request, :host => "www.google.com", :port => 80 } c.callback { ok = true + c.close_connection EM.stop } c.errback {EM.stop} # necessary, otherwise a failure blocks the test suite forever. @@ -32,7 +28,11 @@ def test_http_client_1 ok = false EM.run { c = silent { EM::P::HttpClient.send :request, :host => "www.google.com", :port => 80 } - c.callback {ok = true; EM.stop} + c.callback { + ok = true + c.close_connection + EM.stop + } c.errback {EM.stop} } assert ok @@ -44,8 +44,9 @@ def test_http_client_2 ok = false EM.run { c = silent { EM::P::HttpClient.send :request, :host => "www.google.com", :port => 80 } - c.callback {|result| - ok = true; + c.callback { + ok = true + c.close_connection EM.stop } c.errback {EM.stop} @@ -74,10 +75,11 @@ def receive_data data def test_http_empty_content ok = false EM.run { - EM.start_server "127.0.0.1", 9701, EmptyContent - c = silent { EM::P::HttpClient.send :request, :host => "127.0.0.1", :port => 9701 } - c.callback {|result| + EM.start_server "127.0.0.1", @port, EmptyContent + c = silent { EM::P::HttpClient.send :request, :host => "127.0.0.1", :port => @port } + c.callback { ok = true + c.close_connection EM.stop } } @@ -132,15 +134,15 @@ def send_response def test_post response = nil EM.run { - EM.start_server Localhost, Localport, PostContent + EM.start_server '127.0.0.1', @port, PostContent setup_timeout(2) c = silent { EM::P::HttpClient.request( - :host=>Localhost, - :port=>Localport, - :method=>:post, - :request=>"/aaa", - :content=>"XYZ", - :content_type=>"text/plain" + :host => '127.0.0.1', + :port => @port, + :method => :post, + :request => "/aaa", + :content => "XYZ", + :content_type => "text/plain" )} c.callback {|r| response = r @@ -159,8 +161,9 @@ def test_cookie ok = false EM.run { c = silent { EM::Protocols::HttpClient.send :request, :host => "www.google.com", :port => 80, :cookie=>"aaa=bbb" } - c.callback {|result| - ok = true; + c.callback { + ok = true + c.close_connection EM.stop } c.errback {EM.stop} @@ -178,8 +181,9 @@ def test_version_1_0 :port => 80, :version => "1.0" )} - c.callback {|result| - ok = true; + c.callback { + ok = true + c.close_connection EM.stop } c.errback {EM.stop} @@ -215,14 +219,15 @@ def receive_data data def test_http_chunked_encoding_content ok = false - EventMachine.run { - EventMachine.start_server "127.0.0.1", 9701, ChunkedEncodingContent - c = EventMachine::Protocols::HttpClient.send :request, :host => "127.0.0.1", :port => 9701 - c.callback {|result| + EM.run { + EM.start_server "127.0.0.1", @port, ChunkedEncodingContent + c = silent { EM::P::HttpClient.send :request, :host => "127.0.0.1", :port => @port } + c.callback { |result| if result[:content] == "chunk1" * 1024 + "chunk2" * 15 ok = true end - EventMachine.stop + c.close_connection + EM.stop } } assert ok From d63b6d33646d0cb3691834c0ec5a3d2a584c941a Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Wed, 23 Nov 2016 16:58:51 -0800 Subject: [PATCH 128/343] Test updates for EM::P::HttpClient2 * Add EM timeouts * Use HTTP/1.0 to prevent connection keep-alives * Use next_port helper instead of hardcoded port number --- tests/test_httpclient2.rb | 52 +++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/tests/test_httpclient2.rb b/tests/test_httpclient2.rb index 634c7a372..a00fcbcb4 100644 --- a/tests/test_httpclient2.rb +++ b/tests/test_httpclient2.rb @@ -1,17 +1,11 @@ require 'em_test_helper' class TestHttpClient2 < Test::Unit::TestCase - Localhost = "127.0.0.1" - Localport = 9801 - - def setup - end - - def teardown + class TestServer < EM::Connection end - - class TestServer < EM::Connection + def setup + @port = next_port end # #connect returns an object which has made a connection to an HTTP server @@ -21,21 +15,22 @@ class TestServer < EM::Connection # def test_connect EM.run { - EM.start_server Localhost, Localport, TestServer + setup_timeout(1) + EM.start_server '127.0.0.1', @port, TestServer silent do - EM::P::HttpClient2.connect Localhost, Localport - EM::P::HttpClient2.connect( :host=>Localhost, :port=>Localport ) + EM::P::HttpClient2.connect '127.0.0.1', @port + EM::P::HttpClient2.connect( :host=>'127.0.0.1', :port=>@port ) end EM.stop } end - def test_bad_port EM.run { - EM.start_server Localhost, Localport, TestServer + setup_timeout(1) + EM.start_server '127.0.0.1', @port, TestServer assert_raises( ArgumentError ) { - silent { EM::P::HttpClient2.connect Localhost, "xxx" } + silent { EM::P::HttpClient2.connect '127.0.0.1', "xxx" } } EM.stop } @@ -44,7 +39,8 @@ def test_bad_port def test_bad_server err = nil EM.run { - http = silent { EM::P::HttpClient2.connect Localhost, 9999 } + setup_timeout(1) + http = silent { EM::P::HttpClient2.connect '127.0.0.1', 9999 } d = http.get "/" d.errback { err = true; d.internal_error; EM.stop } } @@ -54,7 +50,8 @@ def test_bad_server def test_get content = nil EM.run { - http = silent { EM::P::HttpClient2.connect "google.com", 80 } + setup_timeout(1) + http = silent { EM::P::HttpClient2.connect :host => "google.com", :port => 80, :version => '1.0' } d = http.get "/" d.callback { content = d.content @@ -70,7 +67,8 @@ def test_get def _test_get_multiple content = nil EM.run { - http = silent { EM::P::HttpClient2.connect "google.com", 80 } + setup_timeout(1) + http = silent { EM::P::HttpClient2.connect "google.com", :version => '1.0' } d = http.get "/" d.callback { e = http.get "/" @@ -86,6 +84,7 @@ def _test_get_multiple def test_get_pipeline headers, headers2 = nil, nil EM.run { + setup_timeout(1) http = silent { EM::P::HttpClient2.connect "google.com", 80 } d = http.get("/") d.callback { @@ -102,11 +101,11 @@ def test_get_pipeline assert(headers2) end - def test_authheader EM.run { - EM.start_server Localhost, Localport, TestServer - http = silent { EM::P::HttpClient2.connect Localhost, 18842 } + setup_timeout(1) + EM.start_server '127.0.0.1', @port, TestServer + http = silent { EM::P::HttpClient2.connect '127.0.0.1', 18842 } d = http.get :url=>"/", :authorization=>"Basic xxx" d.callback {EM.stop} d.errback {EM.stop} @@ -114,15 +113,16 @@ def test_authheader end def test_https_get + omit_unless(EM.ssl?) d = nil EM.run { - http = silent { EM::P::HttpClient2.connect :host => 'www.apple.com', :port => 443, :ssl => true } + setup_timeout(1) + http = silent { EM::P::HttpClient2.connect :host => 'www.google.com', :port => 443, :ssl => true, :version => '1.0' } d = http.get "/" - d.callback { - EM.stop - } + d.callback {EM.stop} + d.errback {EM.stop} } assert_equal(200, d.status) - end if EM.ssl? + end end From 488d1d978672187a62aa162032cb206b3b72600c Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Wed, 23 Nov 2016 17:39:19 -0800 Subject: [PATCH 129/343] Allow Ruby 2.3.x to float --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 93653af3b..64783ba01 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,7 @@ language: ruby sudo: false dist: trusty rvm: - - 2.3.2 + - 2.3 - 2.2 - 2.1 - 2.0.0 From 8280f8dd080162f89c11e100bacfe97702ec7f00 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Wed, 23 Nov 2016 19:09:31 -0800 Subject: [PATCH 130/343] Avoid use of instance_eval in unit tests --- tests/test_stomp.rb | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/tests/test_stomp.rb b/tests/test_stomp.rb index 931a797df..53c0502ab 100644 --- a/tests/test_stomp.rb +++ b/tests/test_stomp.rb @@ -9,19 +9,20 @@ def bytesize(str) size || str.size end - def test_content_length_in_bytes - connection = Object.new - connection.instance_eval do - extend EM::P::Stomp + class TStomp + include EM::P::Stomp - def last_sent_content_length - @sent && Integer(@sent[CONTENT_LENGTH_REGEX, 1]) - end + def last_sent_content_length + @sent && Integer(@sent[CONTENT_LENGTH_REGEX, 1]) + end - def send_data(string) - @sent = string - end + def send_data(string) + @sent = string end + end + + def test_content_length_in_bytes + connection = TStomp.new queue = "queue" failure_message = "header content-length is not the byte size of last sent body" From 41c595e6b56588ca2da26750b7659bd4062ebcea Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Wed, 23 Nov 2016 20:39:09 -0800 Subject: [PATCH 131/343] The tests for LineAndTextProtocol actually test now --- tests/test_ltp.rb | 39 ++++++++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/tests/test_ltp.rb b/tests/test_ltp.rb index 6449829d7..06c268514 100644 --- a/tests/test_ltp.rb +++ b/tests/test_ltp.rb @@ -2,7 +2,14 @@ class TestLineAndTextProtocol < Test::Unit::TestCase - class SimpleLineTest < EM::P::LineAndTextProtocol + class TLP_LineBuffer < EM::P::LineAndTextProtocol + attr_reader :line_buffer + + def initialize + super + @line_buffer = [] + end + def receive_line line @line_buffer << line end @@ -27,10 +34,10 @@ def setup end def test_simple_lines - lines_received = [] + conn = nil EM.run { - EM.start_server( "127.0.0.1", @port, SimpleLineTest ) do |conn| - conn.instance_eval "@line_buffer = lines_received" + EM.start_server( "127.0.0.1", @port, TLP_LineBuffer ) do |c| + conn = c end setup_timeout @@ -39,25 +46,35 @@ def test_simple_lines c.close_connection_after_writing end } - assert_equal( %w(aaa bbb ccc), lines_received ) + assert_equal( %w(aaa bbb ccc), conn.line_buffer) end #-------------------------------------------------------------------- - class SimpleLineTest < EM::P::LineAndTextProtocol + class TLP_ErrorMessage < EM::P::LineAndTextProtocol + attr_reader :error_message + + def initialize + super + @error_message = [] + end + + def receive_line text + raise + end + def receive_error text @error_message << text end end def test_overlength_lines - lines_received = [] + conn = nil EM.run { - EM.start_server( "127.0.0.1", @port, SimpleLineTest ) do |conn| - conn.instance_eval "@error_message = lines_received" + EM.start_server( "127.0.0.1", @port, TLP_ErrorMessage ) do |c| + conn = c end setup_timeout - EM.connect "127.0.0.1", @port, StopClient do |c| c.send_data "a" * (16*1024 + 1) c.send_data "\n" @@ -65,7 +82,7 @@ def test_overlength_lines end } - assert_equal( ["overlength line"], lines_received ) + assert_equal( ["overlength line"], conn.error_message ) end From 5d17b61a482cc2d9dd02419b482b969dc2ce41c8 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Wed, 23 Nov 2016 20:39:42 -0800 Subject: [PATCH 132/343] Fix uninitialized variable warning in EM::P::HttpClient --- lib/em/protocols/httpclient.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/em/protocols/httpclient.rb b/lib/em/protocols/httpclient.rb index 344f2c0a8..38b175ce2 100644 --- a/lib/em/protocols/httpclient.rb +++ b/lib/em/protocols/httpclient.rb @@ -62,6 +62,7 @@ class HttpClient < Connection def initialize warn "HttpClient is deprecated and will be removed. EM-Http-Request should be used instead." + @connected = false end # @param args [Hash] The request arguments From 339b98bf4a6e7e4fc42742ae00b67baaf4cb9347 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Sun, 27 Nov 2016 20:40:19 -0800 Subject: [PATCH 133/343] Subtract one from the connection count because of the loopbreak descriptor --- ext/em.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ext/em.cpp b/ext/em.cpp index 5c1a25d6d..1f3113dee 100644 --- a/ext/em.cpp +++ b/ext/em.cpp @@ -2088,7 +2088,12 @@ EventMachine_t::GetConnectionCount int EventMachine_t::GetConnectionCount () { - return Descriptors.size() + NewDescriptors.size(); + int i = 0; + // Subtract one for epoll or kqueue because of the LoopbreakDescriptor + if (Poller == Poller_Epoll || Poller == Poller_Kqueue) + i = 1; + + return Descriptors.size() + NewDescriptors.size() - i; } From dc3f88acb44a489562ff19629cba5789f5853358 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Sun, 27 Nov 2016 22:26:36 -0800 Subject: [PATCH 134/343] Add tests for connection counts under epoll and kqueue --- tests/test_connection_count.rb | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/tests/test_connection_count.rb b/tests/test_connection_count.rb index 416a301b5..350c41774 100644 --- a/tests/test_connection_count.rb +++ b/tests/test_connection_count.rb @@ -1,13 +1,42 @@ require 'em_test_helper' class TestConnectionCount < Test::Unit::TestCase + def teardown + EM.epoll = false + EM.kqueue = false + end + def test_idle_connection_count + count = nil EM.run { - $count = EM.connection_count + count = EM.connection_count EM.stop_event_loop } + assert_equal(0, count) + end + + # Run this again with epoll enabled (if available) + def test_idle_connection_count_epoll + EM.epoll if EM.epoll? - assert_equal(0, $count) + count = nil + EM.run { + count = EM.connection_count + EM.stop_event_loop + } + assert_equal(0, count) + end + + # Run this again with kqueue enabled (if available) + def test_idle_connection_count_kqueue + EM.kqueue if EM.kqueue? + + count = nil + EM.run { + count = EM.connection_count + EM.stop_event_loop + } + assert_equal(0, count) end module Client From ced2935e0c7c66ad82925491e9c169a0fdee80bb Mon Sep 17 00:00:00 2001 From: utilum Date: Wed, 18 Jan 2017 19:35:01 +0100 Subject: [PATCH 135/343] Fix Fixnum deprecated warning in Ruby 2.4+ (#759) --- lib/jeventmachine.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/jeventmachine.rb b/lib/jeventmachine.rb index 426167246..ec563b206 100644 --- a/lib/jeventmachine.rb +++ b/lib/jeventmachine.rb @@ -226,12 +226,12 @@ def self.attach_fd fileno, watch_mode field.setAccessible(true) fileno = field.get(fileno) else - raise ArgumentError, 'attach_fd requires Java Channel or POSIX fileno' unless fileno.is_a? Fixnum + raise ArgumentError, 'attach_fd requires Java Channel or POSIX fileno' unless fileno.is_a? Integer end if fileno == 0 raise "can't open STDIN as selectable in Java =(" - elsif fileno.is_a? Fixnum + elsif fileno.is_a? Integer # 8Aug09: The following code is specific to the sun jvm's SocketChannelImpl. Is there a cross-platform # way of implementing this? If so, also remember to update EventableSocketChannel#close and #cleanup fd = FileDescriptor.new From 6215f37ffc8e8a9edbd285438dbffabc95128f14 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Wed, 18 Jan 2017 10:43:12 -0800 Subject: [PATCH 136/343] Travis CI add Ruby 2.4 to the matrix --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 64783ba01..936197fc7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,6 +9,7 @@ language: ruby sudo: false dist: trusty rvm: + - 2.4 - 2.3 - 2.2 - 2.1 From 05df74403cb85dd64218e9b6a12b927fe8e2f4b8 Mon Sep 17 00:00:00 2001 From: Surendran Mahendran Date: Mon, 23 Jan 2017 12:29:16 +0530 Subject: [PATCH 137/343] Raise a ruby exception for InvalidWatchSignature (#757) * Raise a ruby exception for InvalidWatchSignature addresses #512 * Add unit test to check if exception is raised on invalid signature --- ext/rubymain.cpp | 9 ++++++++- tests/test_file_watch.rb | 19 +++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/ext/rubymain.cpp b/ext/rubymain.cpp index 11e77839b..c6fbdec61 100644 --- a/ext/rubymain.cpp +++ b/ext/rubymain.cpp @@ -58,6 +58,7 @@ static VALUE EM_eConnectionError; static VALUE EM_eUnknownTimerFired; static VALUE EM_eConnectionNotBound; static VALUE EM_eUnsupported; +static VALUE EM_eInvalidSignature; static VALUE Intern_at_signature; static VALUE Intern_at_timers; @@ -1036,7 +1037,12 @@ t_unwatch_filename static VALUE t_unwatch_filename (VALUE self UNUSED, VALUE sig) { - evma_unwatch_filename(NUM2BSIG (sig)); + try { + evma_unwatch_filename(NUM2BSIG (sig)); + } catch (std::runtime_error e) { + rb_raise (EM_eInvalidSignature, "%s", e.what()); + } + return Qnil; } @@ -1389,6 +1395,7 @@ extern "C" void Init_rubyeventmachine() EM_eConnectionNotBound = rb_define_class_under (EmModule, "ConnectionNotBound", rb_eRuntimeError); EM_eUnknownTimerFired = rb_define_class_under (EmModule, "UnknownTimerFired", rb_eRuntimeError); EM_eUnsupported = rb_define_class_under (EmModule, "Unsupported", rb_eRuntimeError); + EM_eInvalidSignature = rb_define_class_under (EmModule, "InvalidSignature", rb_eRuntimeError); rb_define_module_function (EmModule, "initialize_event_machine", (VALUE(*)(...))t_initialize_event_machine, 0); rb_define_module_function (EmModule, "run_machine_once", (VALUE(*)(...))t_run_machine_once, 0); diff --git a/tests/test_file_watch.rb b/tests/test_file_watch.rb index fab8ecb07..560207174 100644 --- a/tests/test_file_watch.rb +++ b/tests/test_file_watch.rb @@ -55,6 +55,25 @@ def test_events assert($deleted) assert($unbind) end + + # Refer: https://github.com/eventmachine/eventmachine/issues/512 + def test_invalid_signature + # This works fine with kqueue, only fails with linux inotify. + omit_if(EM.kqueue?) + + EM.run { + file = Tempfile.new('foo') + + w1 = EventMachine.watch_file(file.path) + w2 = EventMachine.watch_file(file.path) + + assert_raise EventMachine::InvalidSignature do + w2.stop_watching + end + + EM.stop + } + end else warn "EM.watch_file not implemented, skipping tests in #{__FILE__}" From 9de50c56c280a0bbb76ba0c0568a95384f6d482b Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Wed, 18 Jan 2017 23:55:17 -0800 Subject: [PATCH 138/343] Changelog for v1.2.2 --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 29cd58870..af0c52a46 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 1.2.2 (January 23, 2016) +* Java: Fix Fixnum deprecated warning in Ruby 2.4+ [#759] +* Fix uncaught C++ exception in file watcher and raise InvalidSignature [#512, #757] +* Fix connection count off-by-one for epoll and kqueue [#750] +* Fix uninitialized variable warning in EM::P::HttpClient [#749] +* Fix missing initial value for EventableDescriptor NextHeartbeat [#748] +* Fix hostname resolution on Solaris, Ilumos, SmartOS, et al [#745, #746] +* Improve reliability of tests, reduce public Internet accesses in tests [#656, #666, #749] + ## 1.2.1 (November 15, 2016) * Throw strerror(errno) when getsockname or getpeername fail [#683] * Use a single concrete implementation of getpeername/getsockname, the rest pure virtuals [#683] From bf3730f32f29b8ddb63faff2253763f536d1ffe5 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Mon, 23 Jan 2017 07:03:45 -0800 Subject: [PATCH 139/343] Bump version to 1.2.2 --- lib/em/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/em/version.rb b/lib/em/version.rb index e29d984df..72b226af3 100644 --- a/lib/em/version.rb +++ b/lib/em/version.rb @@ -1,3 +1,3 @@ module EventMachine - VERSION = "1.2.1" + VERSION = "1.2.2" end From 3d40f7bd50b1db45fa2b030cea63a0386d54b3ed Mon Sep 17 00:00:00 2001 From: Adrien Date: Fri, 10 Feb 2017 13:44:30 +0100 Subject: [PATCH 140/343] Update CHANGELOG.md (#769) --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index af0c52a46..6cc74e41c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## 1.2.2 (January 23, 2016) +## 1.2.2 (January 23, 2017) * Java: Fix Fixnum deprecated warning in Ruby 2.4+ [#759] * Fix uncaught C++ exception in file watcher and raise InvalidSignature [#512, #757] * Fix connection count off-by-one for epoll and kqueue [#750] From 64e04fe841b8a6d6924d7e381d891b205c6c96dd Mon Sep 17 00:00:00 2001 From: Peter-Levine Date: Mon, 13 Feb 2017 13:06:43 -0500 Subject: [PATCH 141/343] Allow destructors to throw when compiling in >= C++11 (#767) Explicitly mark destructors that throw as "noexcept(false)" when compiling in a post C++11 dialect. --- ext/binder.cpp | 2 +- ext/binder.h | 8 +++++++- ext/ed.cpp | 2 +- ext/ed.h | 4 ++-- ext/pipe.cpp | 2 +- 5 files changed, 12 insertions(+), 6 deletions(-) diff --git a/ext/binder.cpp b/ext/binder.cpp index cb39fe56e..bdce05517 100644 --- a/ext/binder.cpp +++ b/ext/binder.cpp @@ -116,7 +116,7 @@ Bindable_t::Bindable_t() Bindable_t::~Bindable_t ***********************/ -Bindable_t::~Bindable_t() +Bindable_t::~Bindable_t() NO_EXCEPT_FALSE { BindingBag.erase (Binding); } diff --git a/ext/binder.h b/ext/binder.h index fb09902b1..203bc90b0 100644 --- a/ext/binder.h +++ b/ext/binder.h @@ -21,6 +21,12 @@ See the file COPYING for complete licensing information. #define __ObjectBindings__H_ +#if __cplusplus >= 201103L +#define NO_EXCEPT_FALSE noexcept(false) +#else +#define NO_EXCEPT_FALSE +#endif + class Bindable_t { public: @@ -30,7 +36,7 @@ class Bindable_t public: Bindable_t(); - virtual ~Bindable_t(); + virtual ~Bindable_t() NO_EXCEPT_FALSE; const uintptr_t GetBinding() {return Binding;} diff --git a/ext/ed.cpp b/ext/ed.cpp index 1c5a16130..a3b24b50c 100644 --- a/ext/ed.cpp +++ b/ext/ed.cpp @@ -126,7 +126,7 @@ EventableDescriptor::EventableDescriptor (SOCKET sd, EventMachine_t *em): EventableDescriptor::~EventableDescriptor *****************************************/ -EventableDescriptor::~EventableDescriptor() +EventableDescriptor::~EventableDescriptor() NO_EXCEPT_FALSE { if (NextHeartbeat) MyEventMachine->ClearHeartbeat(NextHeartbeat, this); diff --git a/ext/ed.h b/ext/ed.h index 9a3f5af79..e660626c7 100644 --- a/ext/ed.h +++ b/ext/ed.h @@ -37,7 +37,7 @@ class EventableDescriptor: public Bindable_t { public: EventableDescriptor (SOCKET, EventMachine_t*); - virtual ~EventableDescriptor(); + virtual ~EventableDescriptor() NO_EXCEPT_FALSE; SOCKET GetSocket() {return MySocket;} void SetSocketInvalid() { MySocket = INVALID_SOCKET; } @@ -365,7 +365,7 @@ class PipeDescriptor: public EventableDescriptor { public: PipeDescriptor (SOCKET, pid_t, EventMachine_t*); - virtual ~PipeDescriptor(); + virtual ~PipeDescriptor() NO_EXCEPT_FALSE; virtual void Read(); virtual void Write(); diff --git a/ext/pipe.cpp b/ext/pipe.cpp index 190734aad..a5f0d9f14 100644 --- a/ext/pipe.cpp +++ b/ext/pipe.cpp @@ -46,7 +46,7 @@ PipeDescriptor::PipeDescriptor (int fd, pid_t subpid, EventMachine_t *parent_em) PipeDescriptor::~PipeDescriptor *******************************/ -PipeDescriptor::~PipeDescriptor() +PipeDescriptor::~PipeDescriptor() NO_EXCEPT_FALSE { // Run down any stranded outbound data. for (size_t i=0; i < OutboundPages.size(); i++) From 3621c0480803bf5aca5cbfd6ea032f050bd2fa75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arthur=20D=27Andr=C3=A9a?= Date: Fri, 17 Feb 2017 14:14:57 -0200 Subject: [PATCH 142/343] Fix segfault when an Exception is raised from unbind callback (#766) --- ext/em.cpp | 21 +++++++++++++++------ ext/em.h | 1 + tests/test_exc.rb | 15 +++++++++++++++ 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/ext/em.cpp b/ext/em.cpp index 1f3113dee..40f2514cf 100644 --- a/ext/em.cpp +++ b/ext/em.cpp @@ -164,6 +164,8 @@ EventMachine_t::~EventMachine_t() { // Run down descriptors size_t i; + for (i = 0; i < DescriptorsToDelete.size(); i++) + delete DescriptorsToDelete[i]; for (i = 0; i < NewDescriptors.size(); i++) delete NewDescriptors[i]; for (i = 0; i < Descriptors.size(); i++) @@ -840,6 +842,17 @@ void EventMachine_t::_CleanupSockets() EventableDescriptor *ed = Descriptors[i]; assert (ed); if (ed->ShouldDelete()) { + DescriptorsToDelete.push_back(ed); + } + else + Descriptors [j++] = ed; + } + while ((size_t)j < Descriptors.size()) + Descriptors.pop_back(); + + nSockets = DescriptorsToDelete.size(); + for (i=0; i < nSockets; i++) { + EventableDescriptor *ed = DescriptorsToDelete[i]; #ifdef HAVE_EPOLL if (Poller == Poller_Epoll) { assert (epfd != -1); @@ -855,13 +868,9 @@ void EventMachine_t::_CleanupSockets() ModifiedDescriptors.erase(ed); } #endif - delete ed; - } - else - Descriptors [j++] = ed; + delete ed; } - while ((size_t)j < Descriptors.size()) - Descriptors.pop_back(); + DescriptorsToDelete.clear(); } /********************************* diff --git a/ext/em.h b/ext/em.h index 8cf596a91..7ac72c768 100644 --- a/ext/em.h +++ b/ext/em.h @@ -242,6 +242,7 @@ class EventMachine_t map Pids; vector Descriptors; vector NewDescriptors; + vector DescriptorsToDelete; set ModifiedDescriptors; SOCKET LoopBreakerReader; diff --git a/tests/test_exc.rb b/tests/test_exc.rb index f8384b57e..d9c860aed 100644 --- a/tests/test_exc.rb +++ b/tests/test_exc.rb @@ -1,6 +1,13 @@ require 'em_test_helper' class TestSomeExceptions < Test::Unit::TestCase + class DoomedConnectionError < StandardError + end + class DoomedConnection < EventMachine::Connection + def unbind + raise DoomedConnectionError + end + end # Read the commentary in EM#run. # This test exercises the ensure block in #run that makes sure @@ -25,4 +32,12 @@ def test_b } end + def test_exception_on_unbind + assert_raises(DoomedConnectionError) { + EM.run { + EM.connect("localhost", 8888, DoomedConnection) + } + } + end + end From 48c98efed15ef434029a4cc785ec82d1443e859d Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Wed, 22 Feb 2017 17:17:14 +0000 Subject: [PATCH 143/343] Add get_sockname to Pure Ruby Reactor (#772) --- lib/em/pure_ruby.rb | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/lib/em/pure_ruby.rb b/lib/em/pure_ruby.rb index 4ea03300c..4f56aee20 100644 --- a/lib/em/pure_ruby.rb +++ b/lib/em/pure_ruby.rb @@ -214,6 +214,12 @@ def get_peername sig selectable.get_peername end + # @private + def get_sockname sig + selectable = Reactor.instance.get_selectable( sig ) or raise "unknown get_sockname target" + selectable.get_sockname + end + # @private def open_udp_socket host, port EvmaUDPSocket.create(host, port).uuid @@ -657,6 +663,7 @@ class IO def_delegator :@my_selectable, :send_data def_delegator :@my_selectable, :schedule_close def_delegator :@my_selectable, :get_peername + def_delegator :@my_selectable, :get_sockname def_delegator :@my_selectable, :send_datagram def_delegator :@my_selectable, :get_outbound_data_size def_delegator :@my_selectable, :set_inactivity_timeout @@ -716,6 +723,10 @@ def get_peername nil end + def get_sockname + nil + end + def set_inactivity_timeout tm @inactivity_timeout = tm end @@ -847,6 +858,14 @@ def get_peername io.getpeername end + # #get_sockname + # This is defined in the normal way on connected stream objects. + # Return an object that is suitable for passing to Socket#unpack_sockaddr_in or variants. + # We could also use a convenience method that did the unpacking automatically. + def get_sockname + io.getsockname + end + # #get_outbound_data_size def get_outbound_data_size @outbound_q.inject(0) {|memo,obj| memo += (obj || "").length} From b16f612765a3715eaa487f929185c38225af4db5 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Wed, 22 Feb 2017 21:23:59 -0800 Subject: [PATCH 144/343] Changelog for v1.2.3 --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6cc74e41c..caa305e3f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 1.2.3 (February 22, 2017) +* Pure Ruby: Add get_sockname [#308, #772] +* Fix segfault when an Exception is raised from unbind callback [#765, #766] +* Allow destructors to throw when compiling in >= C++11 [#767] + ## 1.2.2 (January 23, 2017) * Java: Fix Fixnum deprecated warning in Ruby 2.4+ [#759] * Fix uncaught C++ exception in file watcher and raise InvalidSignature [#512, #757] From e57bb695a2ae68f7fc176a186deef234a1459e68 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Wed, 22 Feb 2017 21:24:24 -0800 Subject: [PATCH 145/343] Bump version to 1.2.3 --- lib/em/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/em/version.rb b/lib/em/version.rb index 72b226af3..5c61709ce 100644 --- a/lib/em/version.rb +++ b/lib/em/version.rb @@ -1,3 +1,3 @@ module EventMachine - VERSION = "1.2.2" + VERSION = "1.2.3" end From 469744a76528df84d281aa3a1ad71d24c2333f1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ondruch?= Date: Thu, 23 Mar 2017 14:36:00 +0100 Subject: [PATCH 146/343] Require 'em_test_helper' (#776) Fixes: ~~~ $ ruby -Itests tests/test_threaded_resource.rb tests/test_threaded_resource.rb:1:in `
': uninitialized constant Test (NameError) ~~~ --- tests/test_threaded_resource.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_threaded_resource.rb b/tests/test_threaded_resource.rb index dbbd776e6..9bb39c6e0 100644 --- a/tests/test_threaded_resource.rb +++ b/tests/test_threaded_resource.rb @@ -1,3 +1,5 @@ +require 'em_test_helper' + class TestThreadedResource < Test::Unit::TestCase def object @object ||= {} From 0c3e9e241b1e0f65a5060a33b2625d23e0e7d326 Mon Sep 17 00:00:00 2001 From: MSP-Greg Date: Mon, 22 May 2017 16:06:46 -0500 Subject: [PATCH 147/343] Update extconf.rb to allow MinGW builds with OpenSSL 1.1.0 (#785) --- ext/extconf.rb | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/ext/extconf.rb b/ext/extconf.rb index 8bf5a3488..676b4c426 100644 --- a/ext/extconf.rb +++ b/ext/extconf.rb @@ -26,10 +26,10 @@ def append_library(libs, lib) end SSL_HEADS = %w(openssl/ssl.h openssl/err.h) -SSL_LIBS = case RUBY_PLATFORM -when /mswin|mingw|bccwin/ ; %w(ssleay32 libeay32) -else ; %w(crypto ssl) -end +SSL_LIBS = %w(crypto ssl) +# OpenSSL 1.1.0 and above for Windows use the Unix library names +# OpenSSL 0.9.8 and 1.0.x for Windows use the *eay32 library names +SSL_LIBS_WIN = RUBY_PLATFORM =~ /mswin|mingw|bccwin/ ? %w(ssleay32 libeay32) : [] def dir_config_wrapper(pretty_name, name, idefault=nil, ldefault=nil) inc, lib = dir_config(name, idefault, ldefault) @@ -88,15 +88,15 @@ def pkg_config_wrapper(pretty_name, name) end elsif dir_config_wrapper('OpenSSL', 'ssl') # If the user has provided a --with-ssl-dir argument, we must respect it or fail. - add_define 'WITH_SSL' if check_libs(SSL_LIBS) && check_heads(SSL_HEADS) + add_define 'WITH_SSL' if (check_libs(SSL_LIBS) || check_libs(SSL_LIBS_WIN)) && check_heads(SSL_HEADS) elsif pkg_config_wrapper('OpenSSL', 'openssl') # If we can detect OpenSSL by pkg-config, use it as the next-best option - add_define 'WITH_SSL' if check_libs(SSL_LIBS) && check_heads(SSL_HEADS) -elsif check_libs(SSL_LIBS) && check_heads(SSL_HEADS) + add_define 'WITH_SSL' if (check_libs(SSL_LIBS) || check_libs(SSL_LIBS_WIN)) && check_heads(SSL_HEADS) +elsif (check_libs(SSL_LIBS) || check_libs(SSL_LIBS_WIN)) && check_heads(SSL_HEADS) # If we don't even need any options to find a usable OpenSSL, go with it add_define 'WITH_SSL' elsif dir_config_search('OpenSSL', 'ssl', ['/usr/local', '/opt/local', '/usr/local/opt/openssl']) do - check_libs(SSL_LIBS) && check_heads(SSL_HEADS) + (check_libs(SSL_LIBS) || check_libs(SSL_LIBS_WIN)) && check_heads(SSL_HEADS) end # Finally, look for OpenSSL in alternate locations including MacPorts and HomeBrew add_define 'WITH_SSL' From 7bc6076487389f45bd1763d98ac9baf3ebf66aa8 Mon Sep 17 00:00:00 2001 From: Cameron Johnston Date: Mon, 12 Jun 2017 11:18:45 -0600 Subject: [PATCH 148/343] Allow for larger values in oneshot timer intervals (#784) * Promote InstallOneshotTimer arg from int to uint64_t in native extension * Promote InstallOneshotTimer arg from int to BigInteger in Java class * Use FIX2LONG in lieu of FIX2INT for interval type in t_add_oneshot_timer * Add test for oneshot timer with large future value --- ext/em.cpp | 2 +- ext/em.h | 2 +- ext/rubymain.cpp | 2 +- java/src/com/rubyeventmachine/EmReactor.java | 2 +- tests/test_timers.rb | 7 +++++++ 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/ext/em.cpp b/ext/em.cpp index 40f2514cf..eba5b3b6a 100644 --- a/ext/em.cpp +++ b/ext/em.cpp @@ -1151,7 +1151,7 @@ void EventMachine_t::_RunTimers() EventMachine_t::InstallOneshotTimer ***********************************/ -const uintptr_t EventMachine_t::InstallOneshotTimer (int milliseconds) +const uintptr_t EventMachine_t::InstallOneshotTimer (uint64_t milliseconds) { if (Timers.size() > MaxOutstandingTimers) return false; diff --git a/ext/em.h b/ext/em.h index 7ac72c768..e84b59545 100644 --- a/ext/em.h +++ b/ext/em.h @@ -142,7 +142,7 @@ class EventMachine_t void ScheduleHalt(); bool Stopping(); void SignalLoopBreaker(); - const uintptr_t InstallOneshotTimer (int); + const uintptr_t InstallOneshotTimer (uint64_t); const uintptr_t ConnectToServer (const char *, int, const char *, int); const uintptr_t ConnectToUnixServer (const char *); diff --git a/ext/rubymain.cpp b/ext/rubymain.cpp index c6fbdec61..305f9cbd6 100644 --- a/ext/rubymain.cpp +++ b/ext/rubymain.cpp @@ -262,7 +262,7 @@ t_add_oneshot_timer static VALUE t_add_oneshot_timer (VALUE self UNUSED, VALUE interval) { - const uintptr_t f = evma_install_oneshot_timer (FIX2INT (interval)); + const uintptr_t f = evma_install_oneshot_timer (FIX2LONG (interval)); if (!f) rb_raise (rb_eRuntimeError, "%s", "ran out of timers; use #set_max_timers to increase limit"); return BSIG2NUM (f); diff --git a/java/src/com/rubyeventmachine/EmReactor.java b/java/src/com/rubyeventmachine/EmReactor.java index 02755f217..15b17c541 100644 --- a/java/src/com/rubyeventmachine/EmReactor.java +++ b/java/src/com/rubyeventmachine/EmReactor.java @@ -373,7 +373,7 @@ void runTimers() { } } - public long installOneshotTimer (int milliseconds) { + public long installOneshotTimer (BigInteger milliseconds) { long s = createBinding(); long deadline = new Date().getTime() + milliseconds; diff --git a/tests/test_timers.rb b/tests/test_timers.rb index d774d178b..88b3b781d 100644 --- a/tests/test_timers.rb +++ b/tests/test_timers.rb @@ -93,6 +93,13 @@ def test_periodic_timer_self_cancel assert_equal 4, x end + def test_oneshot_timer_large_future_value + large_value = 11948602000 + EM.run { + EM.add_timer(large_value) { EM.stop } + EM.add_timer(0.02) { EM.stop } + } + end # This test is only applicable to compiled versions of the reactor. # Pure ruby and java versions have no built-in limit on the number of outstanding timers. From b550288c80a7a5b27183d730281fdddb4cc93800 Mon Sep 17 00:00:00 2001 From: Manabu Sonoda Date: Sun, 9 Jul 2017 15:11:34 +0900 Subject: [PATCH 149/343] fix ipv6 udp get_peername (#788) --- ext/ed.cpp | 4 ++-- tests/test_ipv6.rb | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/ext/ed.cpp b/ext/ed.cpp index a3b24b50c..6542bec84 100644 --- a/ext/ed.cpp +++ b/ext/ed.cpp @@ -2002,8 +2002,8 @@ bool DatagramDescriptor::GetPeername (struct sockaddr *s, socklen_t *len) { bool ok = false; if (s) { - *len = sizeof(struct sockaddr); - memset (s, 0, sizeof(struct sockaddr)); + *len = sizeof(ReturnAddress); + memset (s, 0, sizeof(ReturnAddress)); memcpy (s, &ReturnAddress, sizeof(ReturnAddress)); ok = true; } diff --git a/tests/test_ipv6.rb b/tests/test_ipv6.rb index 15cb7eab1..b4fff67a8 100644 --- a/tests/test_ipv6.rb +++ b/tests/test_ipv6.rb @@ -35,11 +35,13 @@ def c.unbind(reason) def test_ipv6_udp_local_server @@received_data = nil @local_port = next_port + @@remote_ip = nil setup_timeout(2) EM.run do EM.open_datagram_socket(@@public_ipv6, @local_port) do |s| def s.receive_data data + _port, @@remote_ip = Socket.unpack_sockaddr_in(s.get_peername) @@received_data = data EM.stop end @@ -49,7 +51,7 @@ def s.receive_data data c.send_datagram "ipv6/udp", @@public_ipv6, @local_port end end - + assert_equal @@remote_ip, @@public_ipv6 assert_equal "ipv6/udp", @@received_data end From a0536df1a541b229651f491eed454147f545a08c Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Sat, 8 Jul 2017 23:36:43 -0700 Subject: [PATCH 150/343] Add EM_PROTO_SSL/TLS definitions to the Java Reactor (#791) --- java/src/com/rubyeventmachine/EmReactor.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/java/src/com/rubyeventmachine/EmReactor.java b/java/src/com/rubyeventmachine/EmReactor.java index 15b17c541..ddbe12a64 100644 --- a/java/src/com/rubyeventmachine/EmReactor.java +++ b/java/src/com/rubyeventmachine/EmReactor.java @@ -48,7 +48,13 @@ public class EmReactor { public final int EM_SSL_HANDSHAKE_COMPLETED = 108; public final int EM_SSL_VERIFY = 109; public final int EM_PROXY_TARGET_UNBOUND = 110; - public final int EM_PROXY_COMPLETED = 111; + public final int EM_PROXY_COMPLETED = 111; + + public final int EM_PROTO_SSLv2 = 2; + public final int EM_PROTO_SSLv3 = 4; + public final int EM_PROTO_TLSv1 = 8; + public final int EM_PROTO_TLSv1_1 = 16; + public final int EM_PROTO_TLSv1_2 = 32; private Selector mySelector; private TreeMap> Timers; From f345e2b283a0476689c7f4b7e8778e23167a805a Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Thu, 27 Jul 2017 20:41:42 -0700 Subject: [PATCH 151/343] Expand the integer type in the intermediate function for large timers (#793) Updates #784 / 7bc607648 --- ext/cmain.cpp | 4 ++-- ext/eventmachine.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/cmain.cpp b/ext/cmain.cpp index 080185417..f58c2cde4 100644 --- a/ext/cmain.cpp +++ b/ext/cmain.cpp @@ -100,10 +100,10 @@ extern "C" void evma_run_machine() evma_install_oneshot_timer **************************/ -extern "C" const uintptr_t evma_install_oneshot_timer (int seconds) +extern "C" const uintptr_t evma_install_oneshot_timer (uint64_t milliseconds) { ensure_eventmachine("evma_install_oneshot_timer"); - return EventMachine->InstallOneshotTimer (seconds); + return EventMachine->InstallOneshotTimer (milliseconds); } diff --git a/ext/eventmachine.h b/ext/eventmachine.h index 70327b0cf..5100e20d7 100644 --- a/ext/eventmachine.h +++ b/ext/eventmachine.h @@ -51,7 +51,7 @@ extern "C" { bool evma_run_machine_once(); void evma_run_machine(); void evma_release_library(); - const uintptr_t evma_install_oneshot_timer (int seconds); + const uintptr_t evma_install_oneshot_timer (uint64_t milliseconds); const uintptr_t evma_connect_to_server (const char *bind_addr, int bind_port, const char *server, int port); const uintptr_t evma_connect_to_unix_server (const char *server); From 25a28287bcc38270887e53f1b205fb248df1f217 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Thu, 27 Jul 2017 00:51:07 -0700 Subject: [PATCH 152/343] Changelog for v1.2.4 --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index caa305e3f..61cb96cf9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 1.2.4 (July 27, 2017) +* Java: Add EM_PROTO_SSL/TLS definitions [#773, #791] +* Fix IPv6 UDP get_peername [#788] +* Allow for larger values in oneshot timer intervals [#784] +* Update extconf.rb to allow MinGW builds with OpenSSL 1.1.0 [#785] + ## 1.2.3 (February 22, 2017) * Pure Ruby: Add get_sockname [#308, #772] * Fix segfault when an Exception is raised from unbind callback [#765, #766] From 0c3d9fd616c8f6ff9d54be3e5e674dab18999651 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Thu, 27 Jul 2017 00:51:25 -0700 Subject: [PATCH 153/343] Bump version to 1.2.4 --- lib/em/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/em/version.rb b/lib/em/version.rb index 5c61709ce..3e824b163 100644 --- a/lib/em/version.rb +++ b/lib/em/version.rb @@ -1,3 +1,3 @@ module EventMachine - VERSION = "1.2.3" + VERSION = "1.2.4" end From 5e7f6011b3a876811875f927035edbf7f6b59405 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Thu, 27 Jul 2017 23:40:51 -0700 Subject: [PATCH 154/343] Use long for larger values in oneshot timer intervals (#794) Updates #784 / 7bc6076 --- java/src/com/rubyeventmachine/EmReactor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/src/com/rubyeventmachine/EmReactor.java b/java/src/com/rubyeventmachine/EmReactor.java index ddbe12a64..31642f3f1 100644 --- a/java/src/com/rubyeventmachine/EmReactor.java +++ b/java/src/com/rubyeventmachine/EmReactor.java @@ -379,7 +379,7 @@ void runTimers() { } } - public long installOneshotTimer (BigInteger milliseconds) { + public long installOneshotTimer (long milliseconds) { long s = createBinding(); long deadline = new Date().getTime() + milliseconds; From 1a858226fb2681f0e128c7f4fbbbbd7a6fed2ec5 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Thu, 27 Jul 2017 23:41:28 -0700 Subject: [PATCH 155/343] Changelog for v1.2.5 --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 61cb96cf9..962574d25 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,12 @@ # Changelog +## 1.2.5 (July 27, 2017) +* Java: Use long for larger values in oneshot timer intervals [#784, #794] + ## 1.2.4 (July 27, 2017) * Java: Add EM_PROTO_SSL/TLS definitions [#773, #791] * Fix IPv6 UDP get_peername [#788] -* Allow for larger values in oneshot timer intervals [#784] +* Allow for larger values in oneshot timer intervals [#784, #793] * Update extconf.rb to allow MinGW builds with OpenSSL 1.1.0 [#785] ## 1.2.3 (February 22, 2017) From 9f9b05ca70b98b40d7648468263b50f90752a968 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Thu, 27 Jul 2017 23:41:53 -0700 Subject: [PATCH 156/343] Bump version to 1.2.5 --- lib/em/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/em/version.rb b/lib/em/version.rb index 3e824b163..353a589cb 100644 --- a/lib/em/version.rb +++ b/lib/em/version.rb @@ -1,3 +1,3 @@ module EventMachine - VERSION = "1.2.4" + VERSION = "1.2.5" end From 6f22a86950525b014820a64b628f352dcc7035b7 Mon Sep 17 00:00:00 2001 From: MSP-Greg Date: Tue, 1 Aug 2017 00:54:27 -0500 Subject: [PATCH 157/343] OpenSSL 1.1.0 test updates (#789) --- tests/test_ssl_protocols.rb | 127 ++++++++++++++++++++++++++++++++---- 1 file changed, 114 insertions(+), 13 deletions(-) diff --git a/tests/test_ssl_protocols.rb b/tests/test_ssl_protocols.rb index bcb682461..e7b16370e 100644 --- a/tests/test_ssl_protocols.rb +++ b/tests/test_ssl_protocols.rb @@ -6,6 +6,20 @@ if EM.ssl? class TestSslProtocols < Test::Unit::TestCase + # equal to base METHODS, downcased, like ["tlsv1, "tlsv1_1", "tlsv1_2"] + if RUBY_VERSION == "1.8.7" + SSL_AVAIL = ["sslv3", "tlsv1"] + else + SSL_AVAIL = ::OpenSSL::SSL::SSLContext::METHODS.select { |i| i =~ /[^\d]\d\z/ }.map { |i| i.to_s.downcase } + end + + libr_vers = OpenSSL.const_defined?(:OPENSSL_LIBRARY_VERSION) ? + OpenSSL::OPENSSL_VERSION : 'na' + + puts "OPENSSL_LIBRARY_VERSION: #{libr_vers}\n" \ + " OPENSSL_VERSION: #{OpenSSL::OPENSSL_VERSION}\n" \ + " SSL_AVAIL: #{SSL_AVAIL.join(' ')}" + module Client def ssl_handshake_completed $client_handshake_completed = true @@ -26,7 +40,7 @@ def ssl_handshake_completed module ClientAny include Client def post_init - start_tls(:ssl_version => %w(sslv2 sslv3 tlsv1 tlsv1_1 tlsv1_2)) + start_tls(:ssl_version => SSL_AVAIL) end end @@ -51,6 +65,20 @@ def post_init end end + module ClientTLSv1_2 + include Client + def post_init + start_tls(:ssl_version => %w(TLSv1_2)) + end + end + + module ServerTLSv1_2 + include Server + def post_init + start_tls(:ssl_version => %w(TLSv1_2)) + end + end + module ServerTLSv1CaseInsensitive include Server def post_init @@ -61,7 +89,7 @@ def post_init module ServerAny include Server def post_init - start_tls(:ssl_version => %w(sslv2 sslv3 tlsv1 tlsv1_1 tlsv1_2)) + start_tls(:ssl_version => SSL_AVAIL) end end @@ -89,6 +117,7 @@ def test_invalid_ssl_version end def test_any_to_v3 + omit("SSLv3 is (correctly) unavailable") unless SSL_AVAIL.include? "sslv3" $client_handshake_completed, $server_handshake_completed = false, false EM.run do EM.start_server("127.0.0.1", 16784, ServerSSLv3) @@ -99,6 +128,18 @@ def test_any_to_v3 assert($server_handshake_completed) end + def test_any_to_tlsv1_2 + omit("TLSv1_2 is unavailable") unless SSL_AVAIL.include? "tlsv1_2" + $client_handshake_completed, $server_handshake_completed = false, false + EM.run do + EM.start_server("127.0.0.1", 16784, ServerTLSv1_2) + EM.connect("127.0.0.1", 16784, ClientAny) + end + + assert($client_handshake_completed) + assert($server_handshake_completed) + end + def test_case_insensitivity $client_handshake_completed, $server_handshake_completed = false, false EM.run do @@ -111,6 +152,7 @@ def test_case_insensitivity end def test_v3_to_any + omit("SSLv3 is (correctly) unavailable") unless SSL_AVAIL.include? "sslv3" $client_handshake_completed, $server_handshake_completed = false, false EM.run do EM.start_server("127.0.0.1", 16784, ServerAny) @@ -121,7 +163,20 @@ def test_v3_to_any assert($server_handshake_completed) end + def test_tlsv1_2_to_any + omit("TLSv1_2 is unavailable") unless SSL_AVAIL.include? "tlsv1_2" + $client_handshake_completed, $server_handshake_completed = false, false + EM.run do + EM.start_server("127.0.0.1", 16784, ServerAny) + EM.connect("127.0.0.1", 16784, ClientTLSv1_2) + end + + assert($client_handshake_completed) + assert($server_handshake_completed) + end + def test_v3_to_v3 + omit("SSLv3 is (correctly) unavailable") unless SSL_AVAIL.include? "sslv3" $client_handshake_completed, $server_handshake_completed = false, false EM.run do EM.start_server("127.0.0.1", 16784, ServerSSLv3) @@ -132,6 +187,18 @@ def test_v3_to_v3 assert($server_handshake_completed) end + def test_tlsv1_2_to_tlsv1_2 + omit("TLSv1_2 is unavailable") unless SSL_AVAIL.include? "tlsv1_2" + $client_handshake_completed, $server_handshake_completed = false, false + EM.run do + EM.start_server("127.0.0.1", 16784, ServerTLSv1_2) + EM.connect("127.0.0.1", 16784, ClientTLSv1_2) + end + + assert($client_handshake_completed) + assert($server_handshake_completed) + end + def test_any_to_any $client_handshake_completed, $server_handshake_completed = false, false EM.run do @@ -165,9 +232,20 @@ def ssl_handshake_completed end end - module ServerTLSv1StopAfterHandshake + module ServerTLSv1_2StopAfterHandshake def post_init - start_tls(:ssl_version => %w(TLSv1)) + start_tls(:ssl_version => %w(TLSv1_2)) + end + + def ssl_handshake_completed + $server_handshake_completed = true + EM.stop_event_loop + end + end + + module ServerAnyStopAfterHandshake + def post_init + start_tls(:ssl_version => SSL_AVAIL) end def ssl_handshake_completed @@ -177,6 +255,7 @@ def ssl_handshake_completed end def test_v3_with_external_client + omit("SSLv3 is (correctly) unavailable") unless SSL_AVAIL.include? "sslv3" $server_handshake_completed = false EM.run do setup_timeout(2) @@ -195,15 +274,17 @@ def test_v3_with_external_client assert($server_handshake_completed) end - def test_tlsv1_with_external_client + # Fixed Server + def test_tlsv1_2_with_external_client + omit("TLSv1_2 is unavailable") unless SSL_AVAIL.include? "tlsv1_2" $server_handshake_completed = false EM.run do setup_timeout(2) - EM.start_server("127.0.0.1", 16784, ServerTLSv1StopAfterHandshake) + EM.start_server("127.0.0.1", 16784, ServerTLSv1_2StopAfterHandshake) EM.defer do sock = TCPSocket.new("127.0.0.1", 16784) ctx = OpenSSL::SSL::SSLContext.new - ctx.ssl_version = :TLSv1_client + ctx.ssl_version = :SSLv23_client ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx) ssl.connect ssl.close rescue nil @@ -214,24 +295,44 @@ def test_tlsv1_with_external_client assert($server_handshake_completed) end - def test_tlsv1_required_with_external_client + # Fixed Client + def test_any_with_external_client_tlsv1_2 + omit("TLSv1_2 is unavailable") unless SSL_AVAIL.include? "tlsv1_2" $server_handshake_completed = false + EM.run do + setup_timeout(2) + EM.start_server("127.0.0.1", 16784, ServerAnyStopAfterHandshake) + EM.defer do + sock = TCPSocket.new("127.0.0.1", 16784) + ctx = OpenSSL::SSL::SSLContext.new + ctx.ssl_version = :TLSv1_2_client + ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx) + ssl.connect + ssl.close rescue nil + sock.close rescue nil + end + end + + assert($server_handshake_completed) + end + # Refuse a client? + def test_tlsv1_2_required_with_external_client + omit("TLSv1_2 is unavailable") unless SSL_AVAIL.include? "tlsv1_2" + $server_handshake_completed = false EM.run do n = 0 EM.add_periodic_timer(0.5) do n += 1 (EM.stop rescue nil) if n == 2 end - EM.start_server("127.0.0.1", 16784, ServerTLSv1StopAfterHandshake) + EM.start_server("127.0.0.1", 16784, ServerTLSv1_2StopAfterHandshake) EM.defer do sock = TCPSocket.new("127.0.0.1", 16784) ctx = OpenSSL::SSL::SSLContext.new - ctx.ssl_version = :SSLv3_client + ctx.ssl_version = :TLSv1_client ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx) - assert_raise OpenSSL::SSL::SSLError do - ssl.connect - end + assert_raise(OpenSSL::SSL::SSLError) { ssl.connect } ssl.close rescue nil sock.close rescue nil EM.stop rescue nil From a439d72aad1006003ed59f72360e03b5dde25c2f Mon Sep 17 00:00:00 2001 From: prashantjois Date: Fri, 1 Sep 2017 16:53:52 -0600 Subject: [PATCH 158/343] Connection::error? calls report_connection_error_status in Ruby reactor (#801) This method exists in the cpp version but not in the pure ruby version. When there is an undelivered message for example, it will attempt to check the socket status and fail saying that the method report_connection_error_status does not exist. This is in turn causes the connection to fail. It will not return the socket status. --- lib/em/pure_ruby.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/em/pure_ruby.rb b/lib/em/pure_ruby.rb index 4f56aee20..94fd17548 100644 --- a/lib/em/pure_ruby.rb +++ b/lib/em/pure_ruby.rb @@ -430,6 +430,11 @@ def set_pending_connect_timeout sig, tm # Needs to be implemented. Currently a no-op stub to allow # certain software to operate with the EM pure-ruby. end + + # @private + def report_connection_error_status signature + get_sock_opt(signature, Socket::SOL_SOCKET, Socket::SO_ERROR).int + end end end From 208027e3ea808ddf3b297e2578c148017db2a27f Mon Sep 17 00:00:00 2001 From: MSP-Greg Date: Mon, 2 Oct 2017 16:20:17 -0500 Subject: [PATCH 159/343] Add gemspec metadata for MinGW builds (#808) --- eventmachine.gemspec | 1 + 1 file changed, 1 insertion(+) diff --git a/eventmachine.gemspec b/eventmachine.gemspec index 8b88e1fe5..be5f99f86 100644 --- a/eventmachine.gemspec +++ b/eventmachine.gemspec @@ -13,6 +13,7 @@ Gem::Specification.new do |s| s.test_files = `git ls-files tests examples`.split s.extensions = ["ext/extconf.rb", "ext/fastfilereader/extconf.rb"] + s.metadata["msys2_mingw_dependencies"] = "openssl" s.add_development_dependency 'test-unit', '~> 2.0' s.add_development_dependency 'rake-compiler', '~> 0.9.5' From d05c412790099741b3c39544d509491e34833dc6 Mon Sep 17 00:00:00 2001 From: MSP-Greg Date: Thu, 5 Oct 2017 09:32:29 -0500 Subject: [PATCH 160/343] Fix for old RubyGems (#810) --- eventmachine.gemspec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/eventmachine.gemspec b/eventmachine.gemspec index be5f99f86..47de3e8ce 100644 --- a/eventmachine.gemspec +++ b/eventmachine.gemspec @@ -13,7 +13,10 @@ Gem::Specification.new do |s| s.test_files = `git ls-files tests examples`.split s.extensions = ["ext/extconf.rb", "ext/fastfilereader/extconf.rb"] - s.metadata["msys2_mingw_dependencies"] = "openssl" + + if s.respond_to?(:metadata=) + s.metadata = { "msys2_mingw_dependencies" => "openssl" } + end s.add_development_dependency 'test-unit', '~> 2.0' s.add_development_dependency 'rake-compiler', '~> 0.9.5' From e9f08d97a4450ce06341d4bae4cad0f0d06a5d27 Mon Sep 17 00:00:00 2001 From: Alastair Pharo Date: Fri, 6 Oct 2017 01:39:23 +1100 Subject: [PATCH 161/343] JRuby: return zero when sending data to a closed connection (#804) Fixes #475 --- lib/jeventmachine.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/jeventmachine.rb b/lib/jeventmachine.rb index ec563b206..45f9e2aa0 100644 --- a/lib/jeventmachine.rb +++ b/lib/jeventmachine.rb @@ -127,6 +127,8 @@ def self.start_unix_server filename end def self.send_data sig, data, length @em.sendData sig, data.to_java_bytes + rescue java.lang.NullPointerException + 0 end def self.send_datagram sig, data, length, address, port @em.sendDatagram sig, data.to_java_bytes, length, address, port From dcec4a7890d2cec53cbf397e9561ad7ef0f48b21 Mon Sep 17 00:00:00 2001 From: Pavel Nakonechnyi Date: Thu, 5 Oct 2017 19:00:11 +0200 Subject: [PATCH 162/343] ssl: c++: be verbose about SSL connection errors (#807) --- ext/ssl.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ext/ssl.cpp b/ext/ssl.cpp index 13f5f161e..963a55cc4 100644 --- a/ext/ssl.cpp +++ b/ext/ssl.cpp @@ -345,8 +345,11 @@ SslBox_t::SslBox_t (bool is_server, const string &privkeyfile, const string &cer SSL_set_verify(pSSL, mode, ssl_verify_wrapper); } - if (!bIsServer) - SSL_connect (pSSL); + if (!bIsServer) { + int e = SSL_connect (pSSL); + if (e != 1) + ERR_print_errors_fp(stderr); + } } @@ -397,6 +400,7 @@ int SslBox_t::GetPlaintext (char *buf, int bufsize) if (e != 1) { int er = SSL_get_error (pSSL, e); if (er != SSL_ERROR_WANT_READ) { + ERR_print_errors_fp(stderr); // Return -1 for a nonfatal error, -2 for an error that should force the connection down. return (er == SSL_ERROR_SSL) ? (-2) : (-1); } From be753238cae1f72606e561aacbb0c614cf406c16 Mon Sep 17 00:00:00 2001 From: Jun Aruga Date: Wed, 6 Dec 2017 19:18:59 +0100 Subject: [PATCH 163/343] Update badge URLs on README. (#813) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0b8bad3c2..d25016d4e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# About EventMachine [![Code Climate](https://codeclimate.com/github/eventmachine/eventmachine.svg)](https://codeclimate.com/github/eventmachine/eventmachine) +# About EventMachine [![Build Status](https://travis-ci.org/eventmachine/eventmachine.svg?branch=master)](https://travis-ci.org/eventmachine/eventmachine) [![Code Climate Maintainability](https://api.codeclimate.com/v1/badges/e9b0603462905d5b9118/maintainability)](https://codeclimate.com/github/eventmachine/eventmachine/maintainability) ## What is EventMachine ## From 77d9285a2f1baeab0b8c628be031c5710ba78eed Mon Sep 17 00:00:00 2001 From: MSP-Greg Date: Wed, 6 Dec 2017 12:23:02 -0600 Subject: [PATCH 164/343] Correct EM::Completion doc location (#811) --- lib/em/completion.rb | 325 +++++++++++++-------------- lib/eventmachine.rb | 515 ++++++++++++++++++++++--------------------- 2 files changed, 422 insertions(+), 418 deletions(-) diff --git a/lib/em/completion.rb b/lib/em/completion.rb index 926535fd2..d5be47365 100644 --- a/lib/em/completion.rb +++ b/lib/em/completion.rb @@ -1,166 +1,169 @@ -# = EM::Completion -# -# A completion is a callback container for various states of completion. In -# its most basic form it has a start state and a finish state. -# -# This implementation includes some hold-back from the EM::Deferrable -# interface in order to be compatible - but it has a much cleaner -# implementation. -# -# In general it is preferred that this implementation be used as a state -# callback container than EM::DefaultDeferrable or other classes including -# EM::Deferrable. This is because it is generally more sane to keep this level -# of state in a dedicated state-back container. This generally leads to more -# malleable interfaces and software designs, as well as eradicating nasty bugs -# that result from abstraction leakage. -# -# == Basic Usage -# -# As already mentioned, the basic usage of a Completion is simply for its two -# final states, :succeeded and :failed. -# -# An asynchronous operation will complete at some future point in time, and -# users often want to react to this event. API authors will want to expose -# some common interface to react to these events. -# -# In the following example, the user wants to know when a short lived -# connection has completed its exchange with the remote server. The simple -# protocol just waits for an ack to its message. -# -# class Protocol < EM::Connection -# include EM::P::LineText2 -# -# def initialize(message, completion) -# @message, @completion = message, completion -# @completion.completion { close_connection } -# @completion.timeout(1, :timeout) -# end -# -# def post_init -# send_data(@message) -# end -# -# def receive_line(line) -# case line -# when /ACK/i -# @completion.succeed line -# when /ERR/i -# @completion.fail :error, line -# else -# @completion.fail :unknown, line -# end -# end -# -# def unbind -# @completion.fail :disconnected unless @completion.completed? -# end -# end -# -# class API -# attr_reader :host, :port -# -# def initialize(host = 'example.org', port = 8000) -# @host, @port = host, port -# end -# -# def request(message) -# completion = EM::Deferrable::Completion.new -# EM.connect(host, port, Protocol, message, completion) -# completion -# end -# end -# -# api = API.new -# completion = api.request('stuff') -# completion.callback do |line| -# puts "API responded with: #{line}" -# end -# completion.errback do |type, line| -# case type -# when :error -# puts "API error: #{line}" -# when :unknown -# puts "API returned unknown response: #{line}" -# when :disconnected -# puts "API server disconnected prematurely" -# when :timeout -# puts "API server did not respond in a timely fashion" -# end -# end -# -# == Advanced Usage -# -# This completion implementation also supports more state callbacks and -# arbitrary states (unlike the original Deferrable API). This allows for basic -# stateful process encapsulation. One might use this to setup state callbacks -# for various states in an exchange like in the basic usage example, except -# where the applicaiton could be made to react to "connected" and -# "disconnected" states additionally. -# -# class Protocol < EM::Connection -# def initialize(completion) -# @response = [] -# @completion = completion -# @completion.stateback(:disconnected) do -# @completion.succeed @response.join -# end -# end -# -# def connection_completed -# @host, @port = Socket.unpack_sockaddr_in get_peername -# @completion.change_state(:connected, @host, @port) -# send_data("GET http://example.org/ HTTP/1.0\r\n\r\n") -# end -# -# def receive_data(data) -# @response << data -# end -# -# def unbind -# @completion.change_state(:disconnected, @host, @port) -# end -# end -# -# completion = EM::Deferrable::Completion.new -# completion.stateback(:connected) do |host, port| -# puts "Connected to #{host}:#{port}" -# end -# completion.stateback(:disconnected) do |host, port| -# puts "Disconnected from #{host}:#{port}" -# end -# completion.callback do |response| -# puts response -# end -# -# EM.connect('example.org', 80, Protocol, completion) -# -# == Timeout -# -# The Completion also has a timeout. The timeout is global and is not aware of -# states apart from completion states. The timeout is only engaged if #timeout -# is called, and it will call fail if it is reached. -# -# == Completion states -# -# By default there are two completion states, :succeeded and :failed. These -# states can be modified by subclassing and overrding the #completion_states -# method. Completion states are special, in that callbacks for all completion -# states are explcitly cleared when a completion state is entered. This -# prevents errors that could arise from accidental unterminated timeouts, and -# other such user errors. -# -# == Other notes -# -# Several APIs have been carried over from EM::Deferrable for compatibility -# reasons during a transitionary period. Specifically cancel_errback and -# cancel_callback are implemented, but their usage is to be strongly -# discouraged. Due to the already complex nature of reaction systems, dynamic -# callback deletion only makes the problem much worse. It is always better to -# add correct conditionals to the callback code, or use more states, than to -# address such implementaiton issues with conditional callbacks. - module EventMachine + ## + # An EM::Completion instance is a callback container for various states of + # completion. In its most basic form it has a start state and a finish state. + # + # This implementation includes some hold-back from the EM::Deferrable + # interface in order to be compatible - but it has a much cleaner + # implementation. + # + # In general it is preferred that this implementation be used as a state + # callback container than EM::DefaultDeferrable or other classes including + # EM::Deferrable. This is because it is generally more sane to keep this level + # of state in a dedicated state-back container. This generally leads to more + # malleable interfaces and software designs, as well as eradicating nasty bugs + # that result from abstraction leakage. + # + # == Basic Usage + # + # As already mentioned, the basic usage of a Completion is simply for its two + # final states, :succeeded and :failed. + # + # An asynchronous operation will complete at some future point in time, and + # users often want to react to this event. API authors will want to expose + # some common interface to react to these events. + # + # In the following example, the user wants to know when a short lived + # connection has completed its exchange with the remote server. The simple + # protocol just waits for an ack to its message. + # + # ```ruby + # class Protocol < EM::Connection + # include EM::P::LineText2 + # + # def initialize(message, completion) + # @message, @completion = message, completion + # @completion.completion { close_connection } + # @completion.timeout(1, :timeout) + # end + # + # def post_init + # send_data(@message) + # end + # + # def receive_line(line) + # case line + # when /ACK/i + # @completion.succeed line + # when /ERR/i + # @completion.fail :error, line + # else + # @completion.fail :unknown, line + # end + # end + # + # def unbind + # @completion.fail :disconnected unless @completion.completed? + # end + # end + # + # class API + # attr_reader :host, :port + # + # def initialize(host = 'example.org', port = 8000) + # @host, @port = host, port + # end + # + # def request(message) + # completion = EM::Deferrable::Completion.new + # EM.connect(host, port, Protocol, message, completion) + # completion + # end + # end + # + # api = API.new + # completion = api.request('stuff') + # completion.callback do |line| + # puts "API responded with: #{line}" + # end + # completion.errback do |type, line| + # case type + # when :error + # puts "API error: #{line}" + # when :unknown + # puts "API returned unknown response: #{line}" + # when :disconnected + # puts "API server disconnected prematurely" + # when :timeout + # puts "API server did not respond in a timely fashion" + # end + # end + # ``` + # + # == Advanced Usage + # + # This completion implementation also supports more state callbacks and + # arbitrary states (unlike the original Deferrable API). This allows for basic + # stateful process encapsulation. One might use this to setup state callbacks + # for various states in an exchange like in the basic usage example, except + # where the applicaiton could be made to react to "connected" and + # "disconnected" states additionally. + # + # ```ruby + # class Protocol < EM::Connection + # def initialize(completion) + # @response = [] + # @completion = completion + # @completion.stateback(:disconnected) do + # @completion.succeed @response.join + # end + # end + # + # def connection_completed + # @host, @port = Socket.unpack_sockaddr_in get_peername + # @completion.change_state(:connected, @host, @port) + # send_data("GET http://example.org/ HTTP/1.0\r\n\r\n") + # end + # + # def receive_data(data) + # @response << data + # end + # + # def unbind + # @completion.change_state(:disconnected, @host, @port) + # end + # end + # + # completion = EM::Deferrable::Completion.new + # completion.stateback(:connected) do |host, port| + # puts "Connected to #{host}:#{port}" + # end + # completion.stateback(:disconnected) do |host, port| + # puts "Disconnected from #{host}:#{port}" + # end + # completion.callback do |response| + # puts response + # end + # + # EM.connect('example.org', 80, Protocol, completion) + # ``` + # + # == Timeout + # + # The Completion also has a timeout. The timeout is global and is not aware of + # states apart from completion states. The timeout is only engaged if #timeout + # is called, and it will call fail if it is reached. + # + # == Completion states + # + # By default there are two completion states, :succeeded and :failed. These + # states can be modified by subclassing and overrding the #completion_states + # method. Completion states are special, in that callbacks for all completion + # states are explcitly cleared when a completion state is entered. This + # prevents errors that could arise from accidental unterminated timeouts, and + # other such user errors. + # + # == Other notes + # + # Several APIs have been carried over from EM::Deferrable for compatibility + # reasons during a transitionary period. Specifically cancel_errback and + # cancel_callback are implemented, but their usage is to be strongly + # discouraged. Due to the already complex nature of reaction systems, dynamic + # callback deletion only makes the problem much worse. It is always better to + # add correct conditionals to the callback code, or use more states, than to + # address such implementaiton issues with conditional callbacks. + class Completion # This is totally not used (re-implemented), it's here in case people check # for kind_of? diff --git a/lib/eventmachine.rb b/lib/eventmachine.rb index bf54c63fc..0fe18762c 100644 --- a/lib/eventmachine.rb +++ b/lib/eventmachine.rb @@ -38,6 +38,7 @@ require 'thread' require 'resolv' +## # Top-level EventMachine namespace. If you are looking for EventMachine examples, see {file:docs/GettingStarted.md EventMachine tutorial}. # # ## Key methods ## @@ -107,7 +108,7 @@ class << self # Once event loop is running, it is perfectly possible to start multiple servers and clients simultaneously: content-aware # proxies like [Proxymachine](https://github.com/mojombo/proxymachine) do just that. # - # ## Using EventMachine with Ruby on Rails and other Web application frameworks ## + # ## Using EventMachine with Ruby on Rails and other Web application frameworks # # Standalone applications often run event loop on the main thread, thus blocking for their entire lifespan. In case of Web applications, # if you are running an EventMachine-based app server such as [Thin](http://code.macournoyer.com/thin/) or [Goliath](https://github.com/postrank-labs/goliath/), @@ -119,16 +120,16 @@ class << self # @example Starting EventMachine event loop in the current thread to run the "Hello, world"-like Echo server example # # #!/usr/bin/env ruby - # + # # require 'rubygems' # or use Bundler.setup # require 'eventmachine' - # + # # class EchoServer < EM::Connection # def receive_data(data) # send_data(data) # end # end - # + # # EventMachine.run do # EventMachine.start_server("0.0.0.0", 10000, EchoServer) # end @@ -310,11 +311,11 @@ def self.add_shutdown_hook &block # # @example Setting a one-shot timer with EventMachine # - # EventMachine.run { - # puts "Starting the run now: #{Time.now}" - # EventMachine.add_timer 5, proc { puts "Executing timer event: #{Time.now}" } - # EventMachine.add_timer(10) { puts "Executing timer event: #{Time.now}" } - # } + # EventMachine.run { + # puts "Starting the run now: #{Time.now}" + # EventMachine.add_timer 5, proc { puts "Executing timer event: #{Time.now}" } + # EventMachine.add_timer(10) { puts "Executing timer event: #{Time.now}" } + # } # # @param [Integer] delay Delay in seconds # @see EventMachine::Timer @@ -338,9 +339,9 @@ def self.add_timer *args, &block # # @example Write a dollar-sign to stderr every five seconds, without blocking # - # EventMachine.run { - # EventMachine.add_periodic_timer( 5 ) { $stderr.write "$" } - # } + # EventMachine.run { + # EventMachine.add_periodic_timer(5) { $stderr.write "$" } + # } # # @param [Integer] delay Delay in seconds # @@ -377,40 +378,40 @@ def self.cancel_timer timer_or_sig # # @example Stopping a running EventMachine event loop # - # require 'rubygems' - # require 'eventmachine' - # - # module Redmond - # def post_init - # puts "We're sending a dumb HTTP request to the remote peer." - # send_data "GET / HTTP/1.1\r\nHost: www.microsoft.com\r\n\r\n" - # end - # - # def receive_data data - # puts "We received #{data.length} bytes from the remote peer." - # puts "We're going to stop the event loop now." - # EventMachine::stop_event_loop - # end - # - # def unbind - # puts "A connection has terminated." - # end - # end - # - # puts "We're starting the event loop now." - # EventMachine.run { - # EventMachine.connect "www.microsoft.com", 80, Redmond - # } - # puts "The event loop has stopped." - # - # # This program will produce approximately the following output: - # # - # # We're starting the event loop now. - # # We're sending a dumb HTTP request to the remote peer. - # # We received 1440 bytes from the remote peer. - # # We're going to stop the event loop now. - # # A connection has terminated. - # # The event loop has stopped. + # require 'rubygems' + # require 'eventmachine' + # + # module Redmond + # def post_init + # puts "We're sending a dumb HTTP request to the remote peer." + # send_data "GET / HTTP/1.1\r\nHost: www.microsoft.com\r\n\r\n" + # end + # + # def receive_data data + # puts "We received #{data.length} bytes from the remote peer." + # puts "We're going to stop the event loop now." + # EventMachine::stop_event_loop + # end + # + # def unbind + # puts "A connection has terminated." + # end + # end + # + # puts "We're starting the event loop now." + # EventMachine.run { + # EventMachine.connect "www.microsoft.com", 80, Redmond + # } + # puts "The event loop has stopped." + # + # # This program will produce approximately the following output: + # # + # # We're starting the event loop now. + # # We're sending a dumb HTTP request to the remote peer. + # # We received 1440 bytes from the remote peer. + # # We're going to stop the event loop now. + # # A connection has terminated. + # # The event loop has stopped. # # def self.stop_event_loop @@ -467,43 +468,43 @@ def self.stop_event_loop # # @example # - # require 'rubygems' - # require 'eventmachine' - # - # # Here is an example of a server that counts lines of input from the remote - # # peer and sends back the total number of lines received, after each line. - # # Try the example with more than one client connection opened via telnet, - # # and you will see that the line count increments independently on each - # # of the client connections. Also very important to note, is that the - # # handler for the receive_data function, which our handler redefines, may - # # not assume that the data it receives observes any kind of message boundaries. - # # Also, to use this example, be sure to change the server and port parameters - # # to the start_server call to values appropriate for your environment. - # module LineCounter - # MaxLinesPerConnection = 10 - # - # def post_init - # puts "Received a new connection" - # @data_received = "" - # @line_count = 0 - # end - # - # def receive_data data - # @data_received << data - # while @data_received.slice!( /^[^\n]*[\n]/m ) - # @line_count += 1 - # send_data "received #{@line_count} lines so far\r\n" - # @line_count == MaxLinesPerConnection and close_connection_after_writing - # end - # end - # end - # - # EventMachine.run { - # host, port = "192.168.0.100", 8090 - # EventMachine.start_server host, port, LineCounter - # puts "Now accepting connections on address #{host}, port #{port}..." - # EventMachine.add_periodic_timer(10) { $stderr.write "*" } - # } + # require 'rubygems' + # require 'eventmachine' + # + # # Here is an example of a server that counts lines of input from the remote + # # peer and sends back the total number of lines received, after each line. + # # Try the example with more than one client connection opened via telnet, + # # and you will see that the line count increments independently on each + # # of the client connections. Also very important to note, is that the + # # handler for the receive_data function, which our handler redefines, may + # # not assume that the data it receives observes any kind of message boundaries. + # # Also, to use this example, be sure to change the server and port parameters + # # to the start_server call to values appropriate for your environment. + # module LineCounter + # MaxLinesPerConnection = 10 + # + # def post_init + # puts "Received a new connection" + # @data_received = "" + # @line_count = 0 + # end + # + # def receive_data data + # @data_received << data + # while @data_received.slice!( /^[^\n]*[\n]/m ) + # @line_count += 1 + # send_data "received #{@line_count} lines so far\r\n" + # @line_count == MaxLinesPerConnection and close_connection_after_writing + # end + # end + # end + # + # EventMachine.run { + # host, port = "192.168.0.100", 8090 + # EventMachine.start_server host, port, LineCounter + # puts "Now accepting connections on address #{host}, port #{port}..." + # EventMachine.add_periodic_timer(10) { $stderr.write "*" } + # } # # @param [String] server Host to bind to. # @param [Integer] port Port to bind to. @@ -575,50 +576,50 @@ def self.start_unix_domain_server filename, *args, &block # # @example # - # # Here's a program which connects to a web server, sends a naive - # # request, parses the HTTP header of the response, and then - # # (antisocially) ends the event loop, which automatically drops the connection - # # (and incidentally calls the connection's unbind method). - # module DumbHttpClient - # def post_init - # send_data "GET / HTTP/1.1\r\nHost: _\r\n\r\n" - # @data = "" - # @parsed = false - # end - # - # def receive_data data - # @data << data - # if !@parsed and @data =~ /[\n][\r]*[\n]/m - # @parsed = true - # puts "RECEIVED HTTP HEADER:" - # $`.each {|line| puts ">>> #{line}" } - # - # puts "Now we'll terminate the loop, which will also close the connection" - # EventMachine::stop_event_loop - # end - # end - # - # def unbind - # puts "A connection has terminated" - # end - # end - # - # EventMachine.run { - # EventMachine.connect "www.bayshorenetworks.com", 80, DumbHttpClient - # } - # puts "The event loop has ended" + # # Here's a program which connects to a web server, sends a naive + # # request, parses the HTTP header of the response, and then + # # (antisocially) ends the event loop, which automatically drops the connection + # # (and incidentally calls the connection's unbind method). + # module DumbHttpClient + # def post_init + # send_data "GET / HTTP/1.1\r\nHost: _\r\n\r\n" + # @data = "" + # @parsed = false + # end + # + # def receive_data data + # @data << data + # if !@parsed and @data =~ /[\n][\r]*[\n]/m + # @parsed = true + # puts "RECEIVED HTTP HEADER:" + # $`.each {|line| puts ">>> #{line}" } + # + # puts "Now we'll terminate the loop, which will also close the connection" + # EventMachine::stop_event_loop + # end + # end + # + # def unbind + # puts "A connection has terminated" + # end + # end + # + # EventMachine.run { + # EventMachine.connect "www.bayshorenetworks.com", 80, DumbHttpClient + # } + # puts "The event loop has ended" # # # @example Defining protocol handler as a class # - # class MyProtocolHandler < EventMachine::Connection - # def initialize *args - # super - # # whatever else you want to do here - # end + # class MyProtocolHandler < EventMachine::Connection + # def initialize *args + # super + # # whatever else you want to do here + # end # - # # ... - # end + # # ... + # end # # # @param [String] server Host to connect to @@ -699,32 +700,32 @@ def self.bind_connect bind_addr, bind_port, server, port=nil, handler=nil, *args # # @example # - # module SimpleHttpClient - # def notify_readable - # header = @io.readline - # - # if header == "\r\n" - # # detach returns the file descriptor number (fd == @io.fileno) - # fd = detach - # end - # rescue EOFError - # detach - # end - # - # def unbind - # EM.next_tick do - # # socket is detached from the eventloop, but still open - # data = @io.read - # end - # end - # end - # - # EventMachine.run { - # sock = TCPSocket.new('site.com', 80) - # sock.write("GET / HTTP/1.0\r\n\r\n") - # conn = EventMachine.watch(sock, SimpleHttpClient) - # conn.notify_readable = true - # } + # module SimpleHttpClient + # def notify_readable + # header = @io.readline + # + # if header == "\r\n" + # # detach returns the file descriptor number (fd == @io.fileno) + # fd = detach + # end + # rescue EOFError + # detach + # end + # + # def unbind + # EM.next_tick do + # # socket is detached from the eventloop, but still open + # data = @io.read + # end + # end + # end + # + # EventMachine.run { + # sock = TCPSocket.new('site.com', 80) + # sock.write("GET / HTTP/1.0\r\n\r\n") + # conn = EventMachine.watch(sock, SimpleHttpClient) + # conn.notify_readable = true + # } # # @author Riham Aldakkak (eSpace Technologies) def EventMachine::watch io, handler=nil, *args, &blk @@ -930,25 +931,25 @@ def self.get_max_timers # # @example # - # EventMachine.run { - # EventMachine.connect("rubyeventmachine.com", 80) - # # count will be 0 in this case, because connection is not - # # established yet - # count = EventMachine.connection_count - # } + # EventMachine.run { + # EventMachine.connect("rubyeventmachine.com", 80) + # # count will be 0 in this case, because connection is not + # # established yet + # count = EventMachine.connection_count + # } # # # @example # - # EventMachine.run { - # EventMachine.connect("rubyeventmachine.com", 80) + # EventMachine.run { + # EventMachine.connect("rubyeventmachine.com", 80) # - # EventMachine.next_tick { - # # In this example, count will be 1 since the connection has been established in - # # the next loop of the reactor. - # count = EventMachine.connection_count - # } - # } + # EventMachine.next_tick { + # # In this example, count will be 1 since the connection has been established in + # # the next loop of the reactor. + # count = EventMachine.connection_count + # } + # } # # @return [Integer] Number of connections currently held by the reactor. def self.connection_count @@ -1021,18 +1022,18 @@ def self.run_deferred_callbacks # # @example # - # operation = proc { - # # perform a long-running operation here, such as a database query. - # "result" # as usual, the last expression evaluated in the block will be the return value. - # } - # callback = proc {|result| - # # do something with result here, such as send it back to a network client. - # } - # errback = proc {|error| - # # do something with error here, such as re-raising or logging. - # } + # operation = proc { + # # perform a long-running operation here, such as a database query. + # "result" # as usual, the last expression evaluated in the block will be the return value. + # } + # callback = proc {|result| + # # do something with result here, such as send it back to a network client. + # } + # errback = proc {|error| + # # do something with error here, such as re-raising or logging. + # } # - # EventMachine.defer(operation, callback, errback) + # EventMachine.defer(operation, callback, errback) # # @param [#call] op An operation you want to offload to EventMachine thread pool # @param [#call] callback A callback that will be run on the event loop thread after `operation` finishes. @@ -1174,22 +1175,22 @@ def self.set_descriptor_table_size n_descriptors=nil # # @example # - # module RubyCounter - # def post_init - # # count up to 5 - # send_data "5\n" - # end - # def receive_data data - # puts "ruby sent me: #{data}" - # end - # def unbind - # puts "ruby died with exit status: #{get_status.exitstatus}" - # end - # end - # - # EventMachine.run { - # EventMachine.popen("ruby -e' $stdout.sync = true; gets.to_i.times{ |i| puts i+1; sleep 1 } '", RubyCounter) - # } + # module RubyCounter + # def post_init + # # count up to 5 + # send_data "5\n" + # end + # def receive_data data + # puts "ruby sent me: #{data}" + # end + # def unbind + # puts "ruby died with exit status: #{get_status.exitstatus}" + # end + # end + # + # EventMachine.run { + # EventMachine.popen("ruby -e' $stdout.sync = true; gets.to_i.times{ |i| puts i+1; sleep 1 } '", RubyCounter) + # } # # @note This method is not supported on Microsoft Windows # @see EventMachine::DeferrableChildProcess @@ -1268,37 +1269,37 @@ def self.open_keyboard handler=nil, *args # # @example # - # # Before running this example, make sure we have a file to monitor: - # # $ echo "bar" > /tmp/foo + # # Before running this example, make sure we have a file to monitor: + # # $ echo "bar" > /tmp/foo # - # module Handler - # def file_modified - # puts "#{path} modified" - # end + # module Handler + # def file_modified + # puts "#{path} modified" + # end # - # def file_moved - # puts "#{path} moved" - # end + # def file_moved + # puts "#{path} moved" + # end # - # def file_deleted - # puts "#{path} deleted" - # end + # def file_deleted + # puts "#{path} deleted" + # end # - # def unbind - # puts "#{path} monitoring ceased" - # end - # end + # def unbind + # puts "#{path} monitoring ceased" + # end + # end # - # # for efficient file watching, use kqueue on Mac OS X - # EventMachine.kqueue = true if EventMachine.kqueue? + # # for efficient file watching, use kqueue on Mac OS X + # EventMachine.kqueue = true if EventMachine.kqueue? # - # EventMachine.run { - # EventMachine.watch_file("/tmp/foo", Handler) - # } + # EventMachine.run { + # EventMachine.watch_file("/tmp/foo", Handler) + # } # - # # $ echo "baz" >> /tmp/foo => "/tmp/foo modified" - # # $ mv /tmp/foo /tmp/oof => "/tmp/foo moved" - # # $ rm /tmp/oof => "/tmp/foo deleted" + # # $ echo "baz" >> /tmp/foo => "/tmp/foo modified" + # # $ mv /tmp/foo /tmp/oof => "/tmp/foo moved" + # # $ rm /tmp/oof => "/tmp/foo deleted" # # @note The ability to pick up on the new filename after a rename is not yet supported. # Calling #path will always return the filename you originally used. @@ -1321,18 +1322,18 @@ def self.watch_file(filename, handler=nil, *args) # # @example # - # module ProcessWatcher - # def process_exited - # put 'the forked child died!' - # end - # end + # module ProcessWatcher + # def process_exited + # put 'the forked child died!' + # end + # end # - # pid = fork{ sleep } + # pid = fork{ sleep } # - # EventMachine.run { - # EventMachine.watch_process(pid, ProcessWatcher) - # EventMachine.add_timer(1){ Process.kill('TERM', pid) } - # } + # EventMachine.run { + # EventMachine.watch_process(pid, ProcessWatcher) + # EventMachine.add_timer(1){ Process.kill('TERM', pid) } + # } # # @param [Integer] pid PID of the process to watch. # @param [Class, Module] handler A class or module that implements event handlers associated with the file. @@ -1385,40 +1386,40 @@ def self.error_handler cb = nil, &blk # # @example # - # module ProxyConnection - # def initialize(client, request) - # @client, @request = client, request - # end - # - # def post_init - # EM::enable_proxy(self, @client) - # end - # - # def connection_completed - # send_data @request - # end - # - # def proxy_target_unbound - # close_connection - # end - # - # def unbind - # @client.close_connection_after_writing - # end - # end - # - # module ProxyServer - # def receive_data(data) - # (@buf ||= "") << data - # if @buf =~ /\r\n\r\n/ # all http headers received - # EventMachine.connect("10.0.0.15", 80, ProxyConnection, self, data) - # end - # end - # end - # - # EventMachine.run { - # EventMachine.start_server("127.0.0.1", 8080, ProxyServer) - # } + # module ProxyConnection + # def initialize(client, request) + # @client, @request = client, request + # end + # + # def post_init + # EM::enable_proxy(self, @client) + # end + # + # def connection_completed + # send_data @request + # end + # + # def proxy_target_unbound + # close_connection + # end + # + # def unbind + # @client.close_connection_after_writing + # end + # end + # + # module ProxyServer + # def receive_data(data) + # (@buf ||= "") << data + # if @buf =~ /\r\n\r\n/ # all http headers received + # EventMachine.connect("10.0.0.15", 80, ProxyConnection, self, data) + # end + # end + # end + # + # EventMachine.run { + # EventMachine.start_server("127.0.0.1", 8080, ProxyServer) + # } # # @param [EventMachine::Connection] from Source of data to be proxies/streamed. # @param [EventMachine::Connection] to Destination of data to be proxies/streamed. From bc9e2faefafb7c13c3b4baeacb9763878a26a348 Mon Sep 17 00:00:00 2001 From: Jun Aruga Date: Wed, 6 Dec 2017 19:54:41 +0100 Subject: [PATCH 165/343] Add Ruby 2.5 on Travis CI. (#812) --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 936197fc7..20d6dc0c4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,6 +2,7 @@ bundler_args: --without documentation script: bundle exec rake compile test before_install: - 'if [[ "$TRAVIS_OS_NAME" = "osx" ]]; then brew install openssl; fi' + - gem update bundler env: global: - TESTOPTS=-v @@ -9,6 +10,7 @@ language: ruby sudo: false dist: trusty rvm: + - 2.5 - 2.4 - 2.3 - 2.2 From 49348382b48ef0f509dc031f16e512eb487dc104 Mon Sep 17 00:00:00 2001 From: Jun Aruga Date: Thu, 7 Dec 2017 17:15:37 +0100 Subject: [PATCH 166/343] Update .travis.yml to pass tests. (#815) * Ruby 1.9.2 on trusty is available. * rbx-3 on trusty is available. Leaving it commented out. --- .travis.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 20d6dc0c4..a4281fc35 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,9 +17,11 @@ rvm: - 2.1 - 2.0.0 - 1.9.3 + - 1.9.2 - ruby-head # - rbx # - rbx-2 +# - rbx-3 # - jruby-1.7 # - jruby-9 matrix: @@ -27,6 +29,7 @@ matrix: allow_failures: # - rvm: rbx # - rvm: rbx-2 +# - rvm: rbx-3 - rvm: ruby-head - rvm: 2.0.0 - rvm: 2.0.0 @@ -37,8 +40,6 @@ matrix: include: - rvm: 1.8.7 dist: precise - - rvm: 1.9.2 - dist: precise - rvm: 2.0.0 os: osx osx_image: xcode8 From 569c55ea3d58b8675e5fab045bccaea84d6f1215 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Tue, 23 Jan 2018 06:42:33 -0800 Subject: [PATCH 167/343] EventMachine is tested and working on Ruby 2.4 and 2.5 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d25016d4e..87c126e16 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ EventMachine has been around since the early 2000s and is a mature and battle-te ## What platforms are supported by EventMachine? ## -EventMachine supports Ruby 1.8.7 through 2.3, REE, JRuby and **works well on Windows** as well +EventMachine supports Ruby 1.8.7 through 2.5, REE, JRuby and **works well on Windows** as well as many operating systems from the Unix family (Linux, Mac OS X, BSD flavors). From 2f34bad342b0f1e96df100dcd4c9b596563aaf88 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Wed, 24 Jan 2018 04:23:34 -0800 Subject: [PATCH 168/343] Travis CI update Rubygems to 2.7.4 due to recent bug with Bundler 1.16 (#819) --- .travis.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index a4281fc35..0e5e5124e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,14 +1,16 @@ +sudo: false +dist: trusty +language: ruby bundler_args: --without documentation script: bundle exec rake compile test +# Pin Rubygems to a working version. Sometimes it breaks upstream. Update now and then. before_install: - 'if [[ "$TRAVIS_OS_NAME" = "osx" ]]; then brew install openssl; fi' + - gem update --system 2.7.4 - gem update bundler env: global: - TESTOPTS=-v -language: ruby -sudo: false -dist: trusty rvm: - 2.5 - 2.4 From 98ff494aac2279af2675fd8dd49cc5f130f8b236 Mon Sep 17 00:00:00 2001 From: James Harrison Fisher Date: Wed, 28 Mar 2018 17:17:20 +0100 Subject: [PATCH 169/343] Expose outstanding timer count (#828) Expose outstanding timer count as EM.get_timer_count. Remove support for Ruby 1.8.7 because it does not provide SIZET2NUM macro. --- .travis.yml | 2 -- Gemfile | 2 +- README.md | 2 +- docs/GettingStarted.md | 1 - ext/cmain.cpp | 9 +++++++++ ext/em.cpp | 8 ++++++++ ext/em.h | 1 + ext/eventmachine.h | 1 + ext/rubymain.cpp | 9 +++++++++ lib/em/pure_ruby.rb | 4 ++++ rakelib/package.rake | 2 +- tests/test_ssl_protocols.rb | 6 +----- tests/test_timers.rb | 20 ++++++++++++++++++++ 13 files changed, 56 insertions(+), 11 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0e5e5124e..63502845c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -40,8 +40,6 @@ matrix: # - rvm: jruby-1.7 # - rvm: jruby-9 include: - - rvm: 1.8.7 - dist: precise - rvm: 2.0.0 os: osx osx_image: xcode8 diff --git a/Gemfile b/Gemfile index a5ad5da96..73331827a 100644 --- a/Gemfile +++ b/Gemfile @@ -2,7 +2,7 @@ source 'https://rubygems.org' gemspec -# Rake 11.0 no longer supports Ruby 1.8.7 and Ruby 1.9.2 +# Rake 11.0 no longer supports Ruby 1.9.2 if RUBY_VERSION < '1.9.3' gem 'rake', '< 11' else diff --git a/README.md b/README.md index 87c126e16..e8064e890 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ EventMachine has been around since the early 2000s and is a mature and battle-te ## What platforms are supported by EventMachine? ## -EventMachine supports Ruby 1.8.7 through 2.5, REE, JRuby and **works well on Windows** as well +EventMachine supports Ruby 1.9.2 through 2.5, REE, JRuby and **works well on Windows** as well as many operating systems from the Unix family (Linux, Mac OS X, BSD flavors). diff --git a/docs/GettingStarted.md b/docs/GettingStarted.md index 63acbb78b..0b27975e4 100644 --- a/docs/GettingStarted.md +++ b/docs/GettingStarted.md @@ -34,7 +34,6 @@ we recommend you to use [JRuby](http://jruby.org) when running these examples. This guide assumes you have one of the supported Ruby implementations installed: - * Ruby 1.8.7 * Ruby 1.9.2 * [JRuby](http://jruby.org) (we recommend 1.6) * [Rubinius](http://rubini.us) 1.2 or higher diff --git a/ext/cmain.cpp b/ext/cmain.cpp index f58c2cde4..76a3aa8d8 100644 --- a/ext/cmain.cpp +++ b/ext/cmain.cpp @@ -95,6 +95,15 @@ extern "C" void evma_run_machine() EventMachine->Run(); } +/******************************** +evma_get_timer_count +********************************/ + +extern "C" const size_t evma_get_timer_count () +{ + ensure_eventmachine("evma_get_timer_count"); + return EventMachine->GetTimerCount(); +} /************************** evma_install_oneshot_timer diff --git a/ext/em.cpp b/ext/em.cpp index eba5b3b6a..6e5fe279e 100644 --- a/ext/em.cpp +++ b/ext/em.cpp @@ -1145,6 +1145,14 @@ void EventMachine_t::_RunTimers() } } +/********************************************* +EventMachine_t::_GetNumberOfOutstandingTimers +*********************************************/ + +size_t EventMachine_t::GetTimerCount() +{ + return Timers.size(); +} /*********************************** diff --git a/ext/em.h b/ext/em.h index e84b59545..c01433f75 100644 --- a/ext/em.h +++ b/ext/em.h @@ -142,6 +142,7 @@ class EventMachine_t void ScheduleHalt(); bool Stopping(); void SignalLoopBreaker(); + size_t GetTimerCount(); const uintptr_t InstallOneshotTimer (uint64_t); const uintptr_t ConnectToServer (const char *, int, const char *, int); const uintptr_t ConnectToUnixServer (const char *); diff --git a/ext/eventmachine.h b/ext/eventmachine.h index 5100e20d7..ffc49f68a 100644 --- a/ext/eventmachine.h +++ b/ext/eventmachine.h @@ -51,6 +51,7 @@ extern "C" { bool evma_run_machine_once(); void evma_run_machine(); void evma_release_library(); + const size_t evma_get_timer_count (); const uintptr_t evma_install_oneshot_timer (uint64_t milliseconds); const uintptr_t evma_connect_to_server (const char *bind_addr, int bind_port, const char *server, int port); const uintptr_t evma_connect_to_unix_server (const char *server); diff --git a/ext/rubymain.cpp b/ext/rubymain.cpp index 305f9cbd6..2fefae356 100644 --- a/ext/rubymain.cpp +++ b/ext/rubymain.cpp @@ -255,6 +255,14 @@ static VALUE t_run_machine (VALUE self UNUSED) return Qnil; } +/***************************** +t_get_timer_count +*****************************/ + +static VALUE t_get_timer_count () +{ + return SIZET2NUM (evma_get_timer_count ()); +} /******************* t_add_oneshot_timer @@ -1401,6 +1409,7 @@ extern "C" void Init_rubyeventmachine() rb_define_module_function (EmModule, "run_machine_once", (VALUE(*)(...))t_run_machine_once, 0); rb_define_module_function (EmModule, "run_machine", (VALUE(*)(...))t_run_machine, 0); rb_define_module_function (EmModule, "run_machine_without_threads", (VALUE(*)(...))t_run_machine, 0); + rb_define_module_function (EmModule, "get_timer_count", (VALUE(*)(...))t_get_timer_count, 0); rb_define_module_function (EmModule, "add_oneshot_timer", (VALUE(*)(...))t_add_oneshot_timer, 1); rb_define_module_function (EmModule, "start_tcp_server", (VALUE(*)(...))t_start_server, 2); rb_define_module_function (EmModule, "stop_tcp_server", (VALUE(*)(...))t_stop_server, 1); diff --git a/lib/em/pure_ruby.rb b/lib/em/pure_ruby.rb index 94fd17548..109e1cc99 100644 --- a/lib/em/pure_ruby.rb +++ b/lib/em/pure_ruby.rb @@ -508,6 +508,10 @@ def initialize initialize_for_run end + def get_timer_count + @timers.size + end + def install_oneshot_timer interval uuid = UuidGenerator::generate #@timers << [Time.now + interval, uuid] diff --git a/rakelib/package.rake b/rakelib/package.rake index 00419d0bd..1444d6804 100644 --- a/rakelib/package.rake +++ b/rakelib/package.rake @@ -114,7 +114,7 @@ desc "Build binary gems for Windows with rake-compiler-dock" task 'gem:windows' do require 'rake_compiler_dock' RakeCompilerDock.sh <<-EOT - RUBY_CC_VERSION="${RUBY_CC_VERSION//1.8.7/}" + RUBY_CC_VERSION="${RUBY_CC_VERSION//1.9.2/}" bundle && rake cross native gem EOT end diff --git a/tests/test_ssl_protocols.rb b/tests/test_ssl_protocols.rb index e7b16370e..966f56f1b 100644 --- a/tests/test_ssl_protocols.rb +++ b/tests/test_ssl_protocols.rb @@ -7,11 +7,7 @@ class TestSslProtocols < Test::Unit::TestCase # equal to base METHODS, downcased, like ["tlsv1, "tlsv1_1", "tlsv1_2"] - if RUBY_VERSION == "1.8.7" - SSL_AVAIL = ["sslv3", "tlsv1"] - else - SSL_AVAIL = ::OpenSSL::SSL::SSLContext::METHODS.select { |i| i =~ /[^\d]\d\z/ }.map { |i| i.to_s.downcase } - end + SSL_AVAIL = ::OpenSSL::SSL::SSLContext::METHODS.select { |i| i =~ /[^\d]\d\z/ }.map { |i| i.to_s.downcase } libr_vers = OpenSSL.const_defined?(:OPENSSL_LIBRARY_VERSION) ? OpenSSL::OPENSSL_VERSION : 'na' diff --git a/tests/test_timers.rb b/tests/test_timers.rb index 88b3b781d..4c9c81463 100644 --- a/tests/test_timers.rb +++ b/tests/test_timers.rb @@ -101,6 +101,26 @@ def test_oneshot_timer_large_future_value } end + def test_add_timer_increments_timer_count + EM.run { + n = EM.get_timer_count + EM::Timer.new(0.01) { + EM.stop + } + assert_equal(n+1, EM.get_timer_count) + } + end + + def test_timer_run_decrements_timer_count + EM.run { + n = EM.get_timer_count + EM::Timer.new(0.01) { + assert_equal(n, EM.get_timer_count) + EM.stop + } + } + end + # This test is only applicable to compiled versions of the reactor. # Pure ruby and java versions have no built-in limit on the number of outstanding timers. unless [:pure_ruby, :java].include? EM.library_type From 5e36f6ea921b75f5b373698246fda12bb710faea Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Tue, 17 Apr 2018 18:50:24 -0700 Subject: [PATCH 170/343] Remove using namespace std; and be explicit with std:: prefixes (#831) This resolves the conflict of socket bind() vs std::bind(), as more std:: functions may be introduced in future versions of C++. Resolves #830 --- ext/binder.cpp | 4 ++-- ext/binder.h | 2 +- ext/ed.cpp | 4 ++-- ext/ed.h | 6 +++--- ext/em.cpp | 38 ++++++++++++++++----------------- ext/em.h | 16 +++++++------- ext/fastfilereader/mapper.cpp | 18 +++++++--------- ext/fastfilereader/mapper.h | 2 +- ext/fastfilereader/rubymain.cpp | 1 - ext/page.cpp | 2 +- ext/page.h | 2 +- ext/project.h | 2 -- ext/ssl.cpp | 6 +++--- ext/ssl.h | 4 ++-- 14 files changed, 51 insertions(+), 56 deletions(-) diff --git a/ext/binder.cpp b/ext/binder.cpp index bdce05517..5b90876e5 100644 --- a/ext/binder.cpp +++ b/ext/binder.cpp @@ -22,7 +22,7 @@ See the file COPYING for complete licensing information. #define DEV_URANDOM "/dev/urandom" -map Bindable_t::BindingBag; +std::map Bindable_t::BindingBag; /******************************** @@ -92,7 +92,7 @@ STATIC: Bindable_t::GetObject Bindable_t *Bindable_t::GetObject (const uintptr_t binding) { - map::const_iterator i = BindingBag.find (binding); + std::map::const_iterator i = BindingBag.find (binding); if (i != BindingBag.end()) return i->second; else diff --git a/ext/binder.h b/ext/binder.h index 203bc90b0..dd32c8d4a 100644 --- a/ext/binder.h +++ b/ext/binder.h @@ -32,7 +32,7 @@ class Bindable_t public: static uintptr_t CreateBinding(); static Bindable_t *GetObject (const uintptr_t); - static map BindingBag; + static std::map BindingBag; public: Bindable_t(); diff --git a/ext/ed.cpp b/ext/ed.cpp index 6542bec84..a469dff04 100644 --- a/ext/ed.cpp +++ b/ext/ed.cpp @@ -320,7 +320,7 @@ void EventableDescriptor::_GenericInboundDispatch(const char *buf, unsigned long if (ProxyTarget) { if (BytesToProxy > 0) { - unsigned long proxied = min(BytesToProxy, size); + unsigned long proxied = std::min(BytesToProxy, size); ProxyTarget->SendOutboundData(buf, proxied); ProxiedBytes += (unsigned long) proxied; BytesToProxy -= proxied; @@ -1148,7 +1148,7 @@ void ConnectionDescriptor::_WriteOutboundData() #ifdef HAVE_WRITEV if (!err) { unsigned int sent = bytes_written; - deque::iterator op = OutboundPages.begin(); + std::deque::iterator op = OutboundPages.begin(); for (int i = 0; i < iovcnt; i++) { if (iov[i].iov_len <= sent) { diff --git a/ext/ed.h b/ext/ed.h index e660626c7..4d7f7d4e3 100644 --- a/ext/ed.h +++ b/ext/ed.h @@ -251,7 +251,7 @@ class ConnectionDescriptor: public EventableDescriptor bool bReadAttemptedAfterClose; bool bWriteAttemptedAfterClose; - deque OutboundPages; + std::deque OutboundPages; int OutboundDataSize; #ifdef WITH_SSL @@ -326,7 +326,7 @@ class DatagramDescriptor: public EventableDescriptor struct sockaddr_in6 From; }; - deque OutboundPages; + std::deque OutboundPages; int OutboundDataSize; struct sockaddr_in6 ReturnAddress; @@ -394,7 +394,7 @@ class PipeDescriptor: public EventableDescriptor protected: bool bReadAttemptedAfterClose; - deque OutboundPages; + std::deque OutboundPages; int OutboundDataSize; pid_t SubprocessPid; diff --git a/ext/em.cpp b/ext/em.cpp index 6e5fe279e..4227dce61 100644 --- a/ext/em.cpp +++ b/ext/em.cpp @@ -176,7 +176,7 @@ EventMachine_t::~EventMachine_t() // Remove any file watch descriptors while(!Files.empty()) { - map::iterator f = Files.begin(); + std::map::iterator f = Files.begin(); UnwatchFile (f->first); } @@ -506,7 +506,7 @@ void EventMachine_t::_DispatchHeartbeats() const EventableDescriptor *head = NULL; while (true) { - multimap::iterator i = Heartbeats.begin(); + std::multimap::iterator i = Heartbeats.begin(); if (i == Heartbeats.end()) break; if (i->first > MyCurrentLoopTime) @@ -534,9 +534,9 @@ void EventMachine_t::QueueHeartbeat(EventableDescriptor *ed) if (heartbeat) { #ifndef HAVE_MAKE_PAIR - Heartbeats.insert (multimap::value_type (heartbeat, ed)); + Heartbeats.insert (std::multimap::value_type (heartbeat, ed)); #else - Heartbeats.insert (make_pair (heartbeat, ed)); + Heartbeats.insert (std::make_pair (heartbeat, ed)); #endif } } @@ -547,8 +547,8 @@ EventMachine_t::ClearHeartbeat void EventMachine_t::ClearHeartbeat(uint64_t key, EventableDescriptor* ed) { - multimap::iterator it; - pair::iterator,multimap::iterator> ret; + std::multimap::iterator it; + std::pair::iterator,std::multimap::iterator> ret; ret = Heartbeats.equal_range (key); for (it = ret.first; it != ret.second; ++it) { if (it->second == ed) { @@ -747,7 +747,7 @@ void EventMachine_t::_RunKqueueOnce() else if (ke->filter == EVFILT_WRITE) ed->Write(); else - cerr << "Discarding unknown kqueue event " << ke->filter << endl; + std::cerr << "Discarding unknown kqueue event " << ke->filter << std::endl; break; } @@ -786,12 +786,12 @@ timeval EventMachine_t::_TimeTilNextEvent() uint64_t current_time = GetRealTime(); if (!Heartbeats.empty()) { - multimap::iterator heartbeats = Heartbeats.begin(); + std::multimap::iterator heartbeats = Heartbeats.begin(); next_event = heartbeats->first; } if (!Timers.empty()) { - multimap::iterator timers = Timers.begin(); + std::multimap::iterator timers = Timers.begin(); if (next_event == 0 || timers->first < next_event) next_event = timers->first; } @@ -1134,7 +1134,7 @@ void EventMachine_t::_RunTimers() // one that hasn't expired yet. while (true) { - multimap::iterator i = Timers.begin(); + std::multimap::iterator i = Timers.begin(); if (i == Timers.end()) break; if (i->first > MyCurrentLoopTime) @@ -1169,9 +1169,9 @@ const uintptr_t EventMachine_t::InstallOneshotTimer (uint64_t milliseconds) Timer_t t; #ifndef HAVE_MAKE_PAIR - multimap::iterator i = Timers.insert (multimap::value_type (fire_at, t)); + std::multimap::iterator i = Timers.insert (std::multimap::value_type (fire_at, t)); #else - multimap::iterator i = Timers.insert (make_pair (fire_at, t)); + std::multimap::iterator i = Timers.insert (std::make_pair (fire_at, t)); #endif return i->second.GetBinding(); } @@ -1848,7 +1848,7 @@ void EventMachine_t::_ModifyDescriptors() #ifdef HAVE_EPOLL if (Poller == Poller_Epoll) { - set::iterator i = ModifiedDescriptors.begin(); + std::set::iterator i = ModifiedDescriptors.begin(); while (i != ModifiedDescriptors.end()) { assert (*i); _ModifyEpollEvent (*i); @@ -1859,7 +1859,7 @@ void EventMachine_t::_ModifyDescriptors() #ifdef HAVE_KQUEUE if (Poller == Poller_Kqueue) { - set::iterator i = ModifiedDescriptors.begin(); + std::set::iterator i = ModifiedDescriptors.begin(); while (i != ModifiedDescriptors.end()) { assert (*i); if ((*i)->GetKqueueArmWrite()) @@ -2137,7 +2137,7 @@ const uintptr_t EventMachine_t::WatchPid (int pid) throw std::runtime_error(errbuf); } Bindable_t* b = new Bindable_t(); - Pids.insert(make_pair (pid, b)); + Pids.insert(std::make_pair (pid, b)); return b->GetBinding(); } @@ -2174,7 +2174,7 @@ void EventMachine_t::UnwatchPid (int pid) void EventMachine_t::UnwatchPid (const uintptr_t sig) { - for(map::iterator i=Pids.begin(); i != Pids.end(); i++) + for(std::map::iterator i=Pids.begin(); i != Pids.end(); i++) { if (i->second->GetBinding() == sig) { UnwatchPid (i->first); @@ -2236,7 +2236,7 @@ const uintptr_t EventMachine_t::WatchFile (const char *fpath) if (wd != -1) { Bindable_t* b = new Bindable_t(); - Files.insert(make_pair (wd, b)); + Files.insert(std::make_pair (wd, b)); return b->GetBinding(); } @@ -2270,7 +2270,7 @@ void EventMachine_t::UnwatchFile (int wd) void EventMachine_t::UnwatchFile (const uintptr_t sig) { - for(map::iterator i=Files.begin(); i != Files.end(); i++) + for(std::map::iterator i=Files.begin(); i != Files.end(); i++) { if (i->second->GetBinding() == sig) { UnwatchFile (i->first); @@ -2301,7 +2301,7 @@ void EventMachine_t::_ReadInotifyEvents() int current = 0; while (current < returned) { struct inotify_event* event = (struct inotify_event*)(buffer+current); - map::const_iterator bindable = Files.find(event->wd); + std::map::const_iterator bindable = Files.find(event->wd); if (bindable != Files.end()) { if (event->mask & (IN_MODIFY | IN_CREATE | IN_DELETE | IN_MOVE)){ (*EventCallback)(bindable->second->GetBinding(), EM_CONNECTION_READ, "modified", 8); diff --git a/ext/em.h b/ext/em.h index c01433f75..799a51cad 100644 --- a/ext/em.h +++ b/ext/em.h @@ -237,14 +237,14 @@ class EventMachine_t class Timer_t: public Bindable_t { }; - multimap Timers; - multimap Heartbeats; - map Files; - map Pids; - vector Descriptors; - vector NewDescriptors; - vector DescriptorsToDelete; - set ModifiedDescriptors; + std::multimap Timers; + std::multimap Heartbeats; + std::map Files; + std::map Pids; + std::vector Descriptors; + std::vector NewDescriptors; + std::vector DescriptorsToDelete; + std::set ModifiedDescriptors; SOCKET LoopBreakerReader; SOCKET LoopBreakerWriter; diff --git a/ext/fastfilereader/mapper.cpp b/ext/fastfilereader/mapper.cpp index 4c1386577..1bbd50491 100644 --- a/ext/fastfilereader/mapper.cpp +++ b/ext/fastfilereader/mapper.cpp @@ -30,13 +30,12 @@ See the file COPYING for complete licensing information. #include #include #include +#include #include -#include "unistd.h" #include #include #include -using namespace std; #include "mapper.h" @@ -44,7 +43,7 @@ using namespace std; Mapper_t::Mapper_t ******************/ -Mapper_t::Mapper_t (const string &filename) +Mapper_t::Mapper_t (const std::string &filename) { /* We ASSUME we can open the file. * (More precisely, we assume someone else checked before we got here.) @@ -52,11 +51,11 @@ Mapper_t::Mapper_t (const string &filename) Fd = open (filename.c_str(), O_RDONLY); if (Fd < 0) - throw runtime_error (strerror (errno)); + throw std::runtime_error (strerror (errno)); struct stat st; if (fstat (Fd, &st)) - throw runtime_error (strerror (errno)); + throw std::runtime_error (strerror (errno)); FileSize = st.st_size; #ifdef OS_WIN32 @@ -65,7 +64,7 @@ Mapper_t::Mapper_t (const string &filename) MapPoint = (const char*) mmap (0, FileSize, PROT_READ, MAP_SHARED, Fd, 0); #endif if (MapPoint == MAP_FAILED) - throw runtime_error (strerror (errno)); + throw std::runtime_error (strerror (errno)); } @@ -128,7 +127,6 @@ const char *Mapper_t::GetChunk (unsigned start) #include #include #include -using namespace std; #include "mapper.h" @@ -150,7 +148,7 @@ Mapper_t::Mapper_t (const string &filename) hFile = CreateFile (filename.c_str(), GENERIC_READ|GENERIC_WRITE, FILE_SHARE_DELETE|FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) - throw runtime_error ("File not found"); + throw std::runtime_error ("File not found"); BY_HANDLE_FILE_INFORMATION i; if (GetFileInformationByHandle (hFile, &i)) @@ -158,7 +156,7 @@ Mapper_t::Mapper_t (const string &filename) hMapping = CreateFileMapping (hFile, NULL, PAGE_READWRITE, 0, 0, NULL); if (!hMapping) - throw runtime_error ("File not mapped"); + throw std::runtime_error ("File not mapped"); #ifdef OS_WIN32 MapPoint = (char*) MapViewOfFile (hMapping, FILE_MAP_WRITE, 0, 0, 0); @@ -166,7 +164,7 @@ Mapper_t::Mapper_t (const string &filename) MapPoint = (const char*) MapViewOfFile (hMapping, FILE_MAP_WRITE, 0, 0, 0); #endif if (!MapPoint) - throw runtime_error ("Mappoint not read"); + throw std::runtime_error ("Mappoint not read"); } diff --git a/ext/fastfilereader/mapper.h b/ext/fastfilereader/mapper.h index 36d16ae40..3db0eeade 100644 --- a/ext/fastfilereader/mapper.h +++ b/ext/fastfilereader/mapper.h @@ -29,7 +29,7 @@ class Mapper_t class Mapper_t { public: - Mapper_t (const string&); + Mapper_t (const std::string&); virtual ~Mapper_t(); const char *GetChunk (unsigned); diff --git a/ext/fastfilereader/rubymain.cpp b/ext/fastfilereader/rubymain.cpp index ce72ebf04..b962bf023 100644 --- a/ext/fastfilereader/rubymain.cpp +++ b/ext/fastfilereader/rubymain.cpp @@ -21,7 +21,6 @@ See the file COPYING for complete licensing information. #include #include -using namespace std; #include #include "mapper.h" diff --git a/ext/page.cpp b/ext/page.cpp index b3d66dace..a3a234006 100644 --- a/ext/page.cpp +++ b/ext/page.cpp @@ -95,7 +95,7 @@ void PageList::Push (const char *buf, int size) if (buf && (size > 0)) { char *copy = (char*) malloc (size); if (!copy) - throw runtime_error ("no memory in pagelist"); + throw std::runtime_error ("no memory in pagelist"); memcpy (copy, buf, size); Pages.push_back (Page (copy, size)); } diff --git a/ext/page.h b/ext/page.h index d3f701e41..969fc9169 100644 --- a/ext/page.h +++ b/ext/page.h @@ -44,7 +44,7 @@ class PageList void PopFront(); private: - deque Pages; + std::deque Pages; }; diff --git a/ext/project.h b/ext/project.h index de2f450fa..757f7011e 100644 --- a/ext/project.h +++ b/ext/project.h @@ -115,8 +115,6 @@ typedef int SOCKET; #include #endif -using namespace std; - #ifdef WITH_SSL #include #include diff --git a/ext/ssl.cpp b/ext/ssl.cpp index 963a55cc4..8d5e038a9 100644 --- a/ext/ssl.cpp +++ b/ext/ssl.cpp @@ -120,7 +120,7 @@ static void InitializeDefaultCredentials() SslContext_t::SslContext_t **************************/ -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) : +SslContext_t::SslContext_t (bool is_server, const std::string &privkeyfile, const std::string &certchainfile, const std::string &cipherlist, const std::string &ecdh_curve, const std::string &dhparam, int ssl_version) : bIsServer (is_server), pCtx (NULL), PrivateKey (NULL), @@ -219,7 +219,7 @@ SslContext_t::SslContext_t (bool is_server, const string &privkeyfile, const str BIO_free(bio); char buf [500]; snprintf (buf, sizeof(buf)-1, "dhparam: PEM_read_bio_DHparams(%s) failed", dhparam.c_str()); - throw new std::runtime_error(buf); + throw std::runtime_error (buf); } SSL_CTX_set_tmp_dh(pCtx, dh); @@ -304,7 +304,7 @@ SslContext_t::~SslContext_t() SslBox_t::SslBox_t ******************/ -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): +SslBox_t::SslBox_t (bool is_server, const std::string &privkeyfile, const std::string &certchainfile, bool verify_peer, bool fail_if_no_peer_cert, const std::string &snihostname, const std::string &cipherlist, const std::string &ecdh_curve, const std::string &dhparam, int ssl_version, const uintptr_t binding): bIsServer (is_server), bHandshakeCompleted (false), bVerifyPeer (verify_peer), diff --git a/ext/ssl.h b/ext/ssl.h index d8524ed5a..64ff6e18c 100644 --- a/ext/ssl.h +++ b/ext/ssl.h @@ -33,7 +33,7 @@ class SslContext_t class SslContext_t { public: - SslContext_t (bool is_server, const string &privkeyfile, const string &certchainfile, const string &cipherlist, const string &ecdh_curve, const string &dhparam, int ssl_version); + SslContext_t (bool is_server, const std::string &privkeyfile, const std::string &certchainfile, const std::string &cipherlist, const std::string &ecdh_curve, const std::string &dhparam, int ssl_version); virtual ~SslContext_t(); private: @@ -61,7 +61,7 @@ class SslBox_t class SslBox_t { public: - 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); + SslBox_t (bool is_server, const std::string &privkeyfile, const std::string &certchainfile, bool verify_peer, bool fail_if_no_peer_cert, const std::string &snihostname, const std::string &cipherlist, const std::string &ecdh_curve, const std::string &dhparam, int ssl_version, const uintptr_t binding); virtual ~SslBox_t(); int PutPlaintext (const char*, int); From ce443b15bc73d43b103b0ad5b5c822da484fc00a Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Thu, 19 Apr 2018 04:50:27 -0700 Subject: [PATCH 171/343] Switch from epoll_create to epoll_create1 (#832) The original epoll_create took a size argument as a hint for how much kernel space to allocate for events. Since Linux 2.6.8, the size argument is ignored, but must be greater than zero. Since we also need to set CLOEXEC on the epoll fd, there is a small window between epoll_create and fcntl as they are separate function calls. Since Linux 2.6.27 and glibc 2.9, epoll_create1 accepts the CLOEXEC argument at creation time to eliminate this gap. This drops support for epoll on RHEL 5 / CentOS 5, EOL was March 2017. Resolves #770 --- ext/em.cpp | 6 +----- ext/em.h | 1 - ext/extconf.rb | 3 ++- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/ext/em.cpp b/ext/em.cpp index 4227dce61..862ef9005 100644 --- a/ext/em.cpp +++ b/ext/em.cpp @@ -386,16 +386,12 @@ void EventMachine_t::_InitializeLoopBreaker() #ifdef HAVE_EPOLL if (Poller == Poller_Epoll) { - epfd = epoll_create (MaxEpollDescriptors); + epfd = epoll_create1(EPOLL_CLOEXEC); if (epfd == -1) { char buf[200]; snprintf (buf, sizeof(buf)-1, "unable to create epoll descriptor: %s", strerror(errno)); throw std::runtime_error (buf); } - int cloexec = fcntl (epfd, F_GETFD, 0); - assert (cloexec >= 0); - cloexec |= FD_CLOEXEC; - fcntl (epfd, F_SETFD, cloexec); assert (LoopBreakerReader >= 0); LoopbreakDescriptor *ld = new LoopbreakDescriptor (LoopBreakerReader, this); diff --git a/ext/em.h b/ext/em.h index 799a51cad..a01336a08 100644 --- a/ext/em.h +++ b/ext/em.h @@ -228,7 +228,6 @@ class EventMachine_t private: enum { - MaxEpollDescriptors = 64*1024, MaxEvents = 4096 }; int HeartbeatInterval; diff --git a/ext/extconf.rb b/ext/extconf.rb index 676b4c426..effe70fea 100644 --- a/ext/extconf.rb +++ b/ext/extconf.rb @@ -205,7 +205,8 @@ def pkg_config_wrapper(pretty_name, name) CONFIG['LDSHARED'] = "$(CXX) " + CONFIG['LDSHARED'].split[1..-1].join(' ') when /linux/ - add_define 'HAVE_EPOLL' if have_func('epoll_create', 'sys/epoll.h') + # epoll_create1 was added in Linux 2.6.27 and glibc 2.9 + add_define 'HAVE_EPOLL' if have_func('epoll_create1', 'sys/epoll.h') # on Unix we need a g++ link, not gcc. CONFIG['LDSHARED'] = "$(CXX) -shared" From 6e507f9fca26176be09503e84b636307a0d7436f Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Thu, 19 Apr 2018 04:51:55 -0700 Subject: [PATCH 172/343] Update rake-compile versions and Travis CI environments (#833) This will drop Ruby 1.8.7 and 1.9.3 from the Windows binary builds. --- .travis.yml | 7 +++---- eventmachine.gemspec | 4 ++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 63502845c..970b7757c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,6 +12,7 @@ env: global: - TESTOPTS=-v rvm: + - 2.6 - 2.5 - 2.4 - 2.3 @@ -34,12 +35,10 @@ matrix: # - rvm: rbx-3 - rvm: ruby-head - rvm: 2.0.0 - - rvm: 2.0.0 + - rvm: 2.3 os: osx - osx_image: xcode8 # - rvm: jruby-1.7 # - rvm: jruby-9 include: - - rvm: 2.0.0 + - rvm: 2.3 os: osx - osx_image: xcode8 diff --git a/eventmachine.gemspec b/eventmachine.gemspec index 47de3e8ce..eb57217ee 100644 --- a/eventmachine.gemspec +++ b/eventmachine.gemspec @@ -19,8 +19,8 @@ Gem::Specification.new do |s| end s.add_development_dependency 'test-unit', '~> 2.0' - s.add_development_dependency 'rake-compiler', '~> 0.9.5' - s.add_development_dependency 'rake-compiler-dock', '~> 0.5.1' + s.add_development_dependency 'rake-compiler', '~> 1.0' + s.add_development_dependency 'rake-compiler-dock', '~> 0.6.3' s.summary = 'Ruby/EventMachine library' s.description = <<-EOT From 2034061bf50a3fcad23e023634bd449ecfa7e816 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Sat, 21 Apr 2018 18:32:19 -0700 Subject: [PATCH 173/343] Fix forgotten std::string in Windows --- ext/fastfilereader/mapper.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/fastfilereader/mapper.cpp b/ext/fastfilereader/mapper.cpp index 1bbd50491..f226f2d5e 100644 --- a/ext/fastfilereader/mapper.cpp +++ b/ext/fastfilereader/mapper.cpp @@ -134,7 +134,7 @@ const char *Mapper_t::GetChunk (unsigned start) Mapper_t::Mapper_t ******************/ -Mapper_t::Mapper_t (const string &filename) +Mapper_t::Mapper_t (const std::string &filename) { /* We ASSUME we can open the file. * (More precisely, we assume someone else checked before we got here.) From 438a125a527c03ee5e787729b0fe5824cbb687c4 Mon Sep 17 00:00:00 2001 From: Greg Brockman Date: Fri, 25 Mar 2011 07:52:49 +0000 Subject: [PATCH 174/343] Make EventMachine support keepalive --- ext/cmain.cpp | 39 +++++++++++++++++++++++++++++++++++++++ ext/ed.cpp | 33 +++++++++++++++++++++++++++++++++ ext/ed.h | 3 +++ ext/eventmachine.h | 2 ++ ext/rubymain.cpp | 25 +++++++++++++++++++++++++ 5 files changed, 102 insertions(+) diff --git a/ext/cmain.cpp b/ext/cmain.cpp index 76a3aa8d8..f2b5e9706 100644 --- a/ext/cmain.cpp +++ b/ext/cmain.cpp @@ -806,6 +806,45 @@ extern "C" int evma_get_outbound_data_size (const uintptr_t binding) return ed ? ed->GetOutboundDataSize() : 0; } +/********************* +evma_enable_keepalive +*********************/ + +extern "C" int evma_enable_keepalive (const unsigned long binding, int idle, int intvl, int cnt) +{ + ensure_eventmachine("evma_enable_keepalive"); + EventableDescriptor *ed = dynamic_cast (Bindable_t::GetObject (binding)); + if (ed) { + int res = ed->EnableKeepalive(idle, intvl, cnt); + if (res < 0) + rb_raise(rb_eRuntimeError, "Could not enable keepalive: %s", strerror(errno)); + return res; + } else { + rb_raise(rb_eRuntimeError, "Attempted to enable keepalive on invalid binding"); + // dead code + return -1; + } +} + +/********************** +evma_disable_keepalive +**********************/ + +extern "C" int evma_disable_keepalive (const unsigned long binding) +{ + ensure_eventmachine("evma_disable_keepalive"); + EventableDescriptor *ed = dynamic_cast (Bindable_t::GetObject (binding)); + if (ed) { + int res = ed->DisableKeepalive(); + if (res < 0) + rb_raise(rb_eRuntimeError, "Could not disable keepalive: %s", strerror(errno)); + return res; + } else { + rb_raise(rb_eRuntimeError, "Attempted to disable keepalive on invalid binding"); + // dead code + return -1; + } +} /************** evma_set_epoll diff --git a/ext/ed.cpp b/ext/ed.cpp index a469dff04..d289a87d1 100644 --- a/ext/ed.cpp +++ b/ext/ed.cpp @@ -264,6 +264,39 @@ bool EventableDescriptor::IsCloseScheduled() } +/*********************************** +EventableDescriptor::EnableKeepalive +***********************************/ + +int EventableDescriptor::EnableKeepalive(int idle, int intvl, int cnt) +{ + int ret; + int val = 1; + // interval between last data pkt and first keepalive pkt + if ((ret = setsockopt(MySocket, SOL_TCP, TCP_KEEPIDLE, &idle, sizeof(idle))) < 0) + goto done; + // interval between keepalives + if ((ret = setsockopt(MySocket, SOL_TCP, TCP_KEEPINTVL, &intvl, sizeof(intvl))) < 0) + goto done; + // number of dropped probes before disconnect + if ((ret = setsockopt(MySocket, SOL_TCP, TCP_KEEPCNT, &cnt, sizeof(cnt))) < 0) + goto done; + if ((ret = setsockopt(MySocket, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val))) < 0) + goto done; +done: + return ret; +} + +/*********************************** +EventableDescriptor::DisableKeepalive +***********************************/ +int EventableDescriptor::DisableKeepalive() +{ + int val = 0; + return setsockopt(MySocket, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)); +} + + /******************************* EventableDescriptor::StartProxy *******************************/ diff --git a/ext/ed.h b/ext/ed.h index 4d7f7d4e3..be1b08135 100644 --- a/ext/ed.h +++ b/ext/ed.h @@ -62,6 +62,9 @@ class EventableDescriptor: public Bindable_t bool IsCloseScheduled(); virtual void HandleError(){ ScheduleClose (false); } + int EnableKeepalive(int idle, int intvl, int cnt); + int DisableKeepalive(); + void SetEventCallback (EMCallback); virtual bool GetPeername (struct sockaddr*, socklen_t*) = 0; diff --git a/ext/eventmachine.h b/ext/eventmachine.h index ffc49f68a..d8a5726c9 100644 --- a/ext/eventmachine.h +++ b/ext/eventmachine.h @@ -92,6 +92,8 @@ extern "C" { int evma_get_sockname (const uintptr_t binding, struct sockaddr*, socklen_t*); int evma_get_subprocess_pid (const uintptr_t binding, pid_t*); int evma_get_subprocess_status (const uintptr_t binding, int*); + int evma_enable_keepalive (const uintptr_t binding, int idle, int intvl, int cnt); + int evma_disable_keepalive (const uintptr_t binding); int evma_get_connection_count(); int evma_send_data_to_connection (const uintptr_t binding, const char *data, int data_length); int evma_send_datagram (const uintptr_t binding, const char *data, int data_length, const char *address, int port); diff --git a/ext/rubymain.cpp b/ext/rubymain.cpp index 2fefae356..acc87e891 100644 --- a/ext/rubymain.cpp +++ b/ext/rubymain.cpp @@ -1251,6 +1251,29 @@ static VALUE conn_associate_callback_target (VALUE self UNUSED, VALUE sig UNUSED } +/****************** +t_enable_keepalive +******************/ + +static VALUE t_enable_keepalive (VALUE self, VALUE idle, VALUE intvl, VALUE cnt) +{ + VALUE sig = rb_ivar_get (self, Intern_at_signature); + int i_idle = NUM2INT(idle); + int i_intvl = NUM2INT(intvl); + int i_cnt = NUM2INT(cnt); + return INT2NUM (evma_enable_keepalive(NUM2ULONG(sig), i_idle, i_intvl, i_cnt)); +} + +/****************** +t_disable_keepalive +******************/ + +static VALUE t_disable_keepalive (VALUE self) +{ + VALUE sig = rb_ivar_get (self, Intern_at_signature); + return INT2NUM (evma_disable_keepalive(NUM2ULONG(sig))); +} + /*************** t_get_loop_time ****************/ @@ -1499,6 +1522,8 @@ extern "C" void Init_rubyeventmachine() rb_define_method (EmConnection, "get_outbound_data_size", (VALUE(*)(...))conn_get_outbound_data_size, 0); rb_define_method (EmConnection, "associate_callback_target", (VALUE(*)(...))conn_associate_callback_target, 1); + rb_define_method (EmConnection, "enable_keepalive", (VALUE(*)(...))t_enable_keepalive, 3); + rb_define_method (EmConnection, "disable_keepalive", (VALUE(*)(...))t_disable_keepalive, 0); // Connection states rb_define_const (EmModule, "TimerFired", INT2NUM(EM_TIMER_FIRED )); From 087339bf40042e93ba58179caea9cf3435dd267d Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Sat, 21 Apr 2018 16:26:25 -0700 Subject: [PATCH 175/343] Portability for keepalive support * Add Windows and Mac OS X keepalive support * Use try/catch error handling * Throw specific error messages * Linux requires positive values --- ext/cmain.cpp | 38 +++++++-------- ext/ed.cpp | 123 +++++++++++++++++++++++++++++++++++++++++------ ext/rubymain.cpp | 33 +++++++++---- 3 files changed, 150 insertions(+), 44 deletions(-) diff --git a/ext/cmain.cpp b/ext/cmain.cpp index f2b5e9706..f6291c3e7 100644 --- a/ext/cmain.cpp +++ b/ext/cmain.cpp @@ -814,16 +814,15 @@ extern "C" int evma_enable_keepalive (const unsigned long binding, int idle, int { ensure_eventmachine("evma_enable_keepalive"); EventableDescriptor *ed = dynamic_cast (Bindable_t::GetObject (binding)); - if (ed) { - int res = ed->EnableKeepalive(idle, intvl, cnt); - if (res < 0) - rb_raise(rb_eRuntimeError, "Could not enable keepalive: %s", strerror(errno)); - return res; - } else { - rb_raise(rb_eRuntimeError, "Attempted to enable keepalive on invalid binding"); - // dead code - return -1; - } + if (ed) + return ed->EnableKeepalive(idle, intvl, cnt); + else + #ifdef BUILD_FOR_RUBY + rb_raise(rb_eRuntimeError, "invalid binding to enable keepalive"); + #else + throw std::runtime_error ("invalid binding to enable keepalive"); + #endif + return -1; } /********************** @@ -834,16 +833,15 @@ extern "C" int evma_disable_keepalive (const unsigned long binding) { ensure_eventmachine("evma_disable_keepalive"); EventableDescriptor *ed = dynamic_cast (Bindable_t::GetObject (binding)); - if (ed) { - int res = ed->DisableKeepalive(); - if (res < 0) - rb_raise(rb_eRuntimeError, "Could not disable keepalive: %s", strerror(errno)); - return res; - } else { - rb_raise(rb_eRuntimeError, "Attempted to disable keepalive on invalid binding"); - // dead code - return -1; - } + if (ed) + return ed->DisableKeepalive(); + else + #ifdef BUILD_FOR_RUBY + rb_raise(rb_eRuntimeError, "invalid binding to enable keepalive"); + #else + throw std::runtime_error ("invalid binding to enable keepalive"); + #endif + return -1; } /************** diff --git a/ext/ed.cpp b/ext/ed.cpp index d289a87d1..848b53bef 100644 --- a/ext/ed.cpp +++ b/ext/ed.cpp @@ -267,23 +267,90 @@ bool EventableDescriptor::IsCloseScheduled() /*********************************** EventableDescriptor::EnableKeepalive ***********************************/ - int EventableDescriptor::EnableKeepalive(int idle, int intvl, int cnt) { int ret; + +#ifdef OS_WIN32 + struct tcp_keepalive args; + args.onoff = 1; + + args.keepalivetime = idle; + args.keepaliveinterval = intvl; + + ret = WSAIoctl(MySocket, SIO_KEEPALIVE_VALS, &args, sizeof(args), NULL, 0, &len, NULL, NULL); + if (ret < 0) { + int err = WSAGetLastError(); + char buf[200]; + buf[0] = '\0'; + + FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, err, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), buf, sizeof(buf), NULL); + + if (! buf[0]) + snprintf (buf, sizeof(buf)-1, "unable to enable keepalive: %d", err); + + throw std::runtime_error (buf); + } + +#else int val = 1; - // interval between last data pkt and first keepalive pkt - if ((ret = setsockopt(MySocket, SOL_TCP, TCP_KEEPIDLE, &idle, sizeof(idle))) < 0) - goto done; - // interval between keepalives - if ((ret = setsockopt(MySocket, SOL_TCP, TCP_KEEPINTVL, &intvl, sizeof(intvl))) < 0) - goto done; - // number of dropped probes before disconnect - if ((ret = setsockopt(MySocket, SOL_TCP, TCP_KEEPCNT, &cnt, sizeof(cnt))) < 0) - goto done; - if ((ret = setsockopt(MySocket, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val))) < 0) - goto done; -done: + + // All the Unixen + ret = setsockopt(MySocket, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)); + if (ret < 0) { + char buf[200]; + snprintf (buf, sizeof(buf)-1, "unable to enable keepalive: %s", strerror(errno)); + throw std::runtime_error (buf); + } + + #ifdef TCP_KEEPALIVE + // BSDs and Mac OS X: idle time used when SO_KEEPALIVE is enabled + // 0 means use the system default value, so we let it through + if (idle >= 0) { + ret = setsockopt(MySocket, IPPROTO_TCP, TCP_KEEPALIVE, &idle, sizeof(idle)); + if (ret < 0) { + char buf[200]; + snprintf (buf, sizeof(buf)-1, "unable set keepalive idle: %s", strerror(errno)); + throw std::runtime_error (buf); + } + } + #endif + #ifdef TCP_KEEPIDLE + // Linux: interval between last data pkt and first keepalive pkt + if (idle > 0) { + ret = setsockopt(MySocket, IPPROTO_TCP, TCP_KEEPIDLE, &idle, sizeof(idle)); + if (ret < 0) { + char buf[200]; + snprintf (buf, sizeof(buf)-1, "unable set keepalive idle: %s", strerror(errno)); + throw std::runtime_error (buf); + } + } + #endif + #ifdef TCP_KEEPINTVL + // Linux (and recent BSDs, Mac OS X, Solaris): interval between keepalives + if (intvl > 0) { + ret = setsockopt(MySocket, IPPROTO_TCP, TCP_KEEPINTVL, &intvl, sizeof(intvl)); + if (ret < 0) { + char buf[200]; + snprintf (buf, sizeof(buf)-1, "unable set keepalive interval: %s", strerror(errno)); + throw std::runtime_error (buf); + } + } + #endif + #ifdef TCP_KEEPCNT + // Linux (and recent BSDs, Mac OS X, Solaris): number of dropped probes before disconnect + if (cnt > 0) { + ret = setsockopt(MySocket, IPPROTO_TCP, TCP_KEEPCNT, &cnt, sizeof(cnt)); + if (ret < 0) { + char buf[200]; + snprintf (buf, sizeof(buf)-1, "unable set keepalive count: %s", strerror(errno)); + throw std::runtime_error (buf); + } + } + #endif +#endif + return ret; } @@ -292,8 +359,36 @@ EventableDescriptor::DisableKeepalive ***********************************/ int EventableDescriptor::DisableKeepalive() { + int ret; + +#ifdef OS_WIN32 + struct tcp_keepalive args; + args.onoff = 0; + ret = WSAIoctl(MySocket, SIO_KEEPALIVE_VALS, &args, sizeof(args), NULL, 0, &len, NULL, NULL); + if (ret < 0) { + int err = WSAGetLastError(); + char buf[200]; + buf[0] = '\0'; + + FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, err, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), buf, sizeof(buf), NULL); + + if (! buf[0]) + snprintf (buf, sizeof(buf)-1, "unable to enable keepalive: %d", err); + + throw std::runtime_error (buf); + } +#else int val = 0; - return setsockopt(MySocket, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)); + ret = setsockopt(MySocket, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)); + if (ret < 0) { + char buf[200]; + snprintf (buf, sizeof(buf)-1, "unable to disable keepalive: %s", strerror(errno)); + throw std::runtime_error (buf); + } +#endif + + return ret; } diff --git a/ext/rubymain.cpp b/ext/rubymain.cpp index acc87e891..b96c39dc8 100644 --- a/ext/rubymain.cpp +++ b/ext/rubymain.cpp @@ -261,7 +261,7 @@ t_get_timer_count static VALUE t_get_timer_count () { - return SIZET2NUM (evma_get_timer_count ()); + return SIZET2NUM (evma_get_timer_count ()); } /******************* @@ -1255,13 +1255,22 @@ static VALUE conn_associate_callback_target (VALUE self UNUSED, VALUE sig UNUSED t_enable_keepalive ******************/ -static VALUE t_enable_keepalive (VALUE self, VALUE idle, VALUE intvl, VALUE cnt) +static VALUE t_enable_keepalive (int argc, VALUE *argv, VALUE self) { - VALUE sig = rb_ivar_get (self, Intern_at_signature); - int i_idle = NUM2INT(idle); - int i_intvl = NUM2INT(intvl); - int i_cnt = NUM2INT(cnt); - return INT2NUM (evma_enable_keepalive(NUM2ULONG(sig), i_idle, i_intvl, i_cnt)); + VALUE idle, intvl, cnt; + rb_scan_args(argc, argv, "03", &idle, &intvl, &cnt); + + // In ed.cpp, skip 0 values before calling setsockopt + int i_idle = NIL_P(idle) ? 0 : NUM2INT(idle); + int i_intvl = NIL_P(intvl) ? 0 : NUM2INT(intvl); + int i_cnt = NIL_P(cnt) ? 0 : NUM2INT(cnt); + + VALUE sig = rb_ivar_get (self, Intern_at_signature); + try { + return INT2NUM (evma_enable_keepalive(NUM2ULONG(sig), i_idle, i_intvl, i_cnt)); + } catch (std::runtime_error e) { + rb_raise (rb_eRuntimeError, "%s", e.what()); + } } /****************** @@ -1270,8 +1279,12 @@ t_disable_keepalive static VALUE t_disable_keepalive (VALUE self) { - VALUE sig = rb_ivar_get (self, Intern_at_signature); - return INT2NUM (evma_disable_keepalive(NUM2ULONG(sig))); + VALUE sig = rb_ivar_get (self, Intern_at_signature); + try { + return INT2NUM (evma_disable_keepalive(NUM2ULONG(sig))); + } catch (std::runtime_error e) { + rb_raise (rb_eRuntimeError, "%s", e.what()); + } } /*************** @@ -1522,7 +1535,7 @@ extern "C" void Init_rubyeventmachine() rb_define_method (EmConnection, "get_outbound_data_size", (VALUE(*)(...))conn_get_outbound_data_size, 0); rb_define_method (EmConnection, "associate_callback_target", (VALUE(*)(...))conn_associate_callback_target, 1); - rb_define_method (EmConnection, "enable_keepalive", (VALUE(*)(...))t_enable_keepalive, 3); + rb_define_method (EmConnection, "enable_keepalive", (VALUE(*)(...))t_enable_keepalive, -1); rb_define_method (EmConnection, "disable_keepalive", (VALUE(*)(...))t_disable_keepalive, 0); // Connection states From e7a4250cb747937a38ca8995ebf3c7e66626ccab Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Sat, 21 Apr 2018 17:46:32 -0700 Subject: [PATCH 176/343] Tests for enable/disable keepalive --- tests/test_keepalive.rb | 105 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 tests/test_keepalive.rb diff --git a/tests/test_keepalive.rb b/tests/test_keepalive.rb new file mode 100644 index 000000000..89bce2722 --- /dev/null +++ b/tests/test_keepalive.rb @@ -0,0 +1,105 @@ +require 'em_test_helper' +require 'socket' + +class TestKeepalive < Test::Unit::TestCase + def setup + assert(!EM.reactor_running?) + @port = next_port + end + + def teardown + assert(!EM.reactor_running?) + end + + def test_enable_keepalive + omit_if(!EM.respond_to?(:get_sock_opt)) + + val = nil + test_module = Module.new do + define_method :post_init do + enable_keepalive + val = get_sock_opt Socket::SOL_SOCKET, Socket::SO_KEEPALIVE + EM.stop + end + end + + EM.run do + EM.start_server '127.0.0.1', @port + EM.connect '127.0.0.1', @port, test_module + end + + # Enabled isn't 1 on all platforms - Mac OS seems to be 8 + # Docs say any non-zero value indicates keepalive is enabled + assert_not_equal 0, val.unpack('i').first + end + + def test_enable_keepalive_values + omit_if(!EM.respond_to?(:get_sock_opt)) + + val, val_idle, val_intvl, val_cnt = nil + test_module = Module.new do + define_method :post_init do + enable_keepalive(5, 10, 15) + val = get_sock_opt Socket::SOL_SOCKET, Socket::SO_KEEPALIVE + + if defined?(Socket::TCP_KEEPALIVE) + val_idle = get_sock_opt Socket::IPPROTO_TCP, Socket::TCP_KEEPALIVE + end + if defined?(Socket::TCP_KEEPIDLE) + val_idle = get_sock_opt Socket::IPPROTO_TCP, Socket::TCP_KEEPIDLE + end + if defined?(Socket::TCP_KEEPINTVL) + val_intvl = get_sock_opt Socket::IPPROTO_TCP, Socket::TCP_KEEPINTVL + end + if defined?(Socket::TCP_KEEPCNT) + val_cnt = get_sock_opt Socket::IPPROTO_TCP, Socket::TCP_KEEPCNT + end + + EM.stop + end + end + + EM.run do + EM.start_server '127.0.0.1', @port + EM.connect '127.0.0.1', @port, test_module + end + + # Enabled isn't 1 on all platforms - Mac OS seems to be 8 + # Docs say any non-zero value indicates keepalive is enabled + assert_not_equal 0, val.unpack('i').first + + # Make sure each of the individual settings was set + if defined?(Socket::TCP_KEEPIDLE) || defined?(Socket::TCP_KEEPALIVE) + assert_equal 5, val_idle.unpack('i').first + end + + if defined?(Socket::TCP_KEEPINTVL) + assert_equal 10, val_intvl.unpack('i').first + end + + if defined?(Socket::TCP_KEEPCNT) + assert_equal 15, val_cnt.unpack('i').first + end + end + + def test_disable_keepalive + omit_if(!EM.respond_to?(:get_sock_opt)) + + val = nil + test_module = Module.new do + define_method :post_init do + enable_keepalive + disable_keepalive + val = get_sock_opt Socket::SOL_SOCKET, Socket::SO_KEEPALIVE + EM.stop + end + end + + EM.run do + EM.start_server '127.0.0.1', @port + EM.connect '127.0.0.1', @port, test_module + end + + assert_equal 0, val.unpack('i').first + end +end From 8d645e1f959ba61aadeb5179ed7673f3bded7dd9 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Sun, 22 Apr 2018 11:27:43 -0700 Subject: [PATCH 177/343] Fix mismatched declaration for enable/disable keepalive methods --- ext/cmain.cpp | 4 ++-- ext/ed.cpp | 9 +++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/ext/cmain.cpp b/ext/cmain.cpp index f6291c3e7..63cb1e959 100644 --- a/ext/cmain.cpp +++ b/ext/cmain.cpp @@ -810,7 +810,7 @@ extern "C" int evma_get_outbound_data_size (const uintptr_t binding) evma_enable_keepalive *********************/ -extern "C" int evma_enable_keepalive (const unsigned long binding, int idle, int intvl, int cnt) +extern "C" int evma_enable_keepalive (const uintptr_t binding, int idle, int intvl, int cnt) { ensure_eventmachine("evma_enable_keepalive"); EventableDescriptor *ed = dynamic_cast (Bindable_t::GetObject (binding)); @@ -829,7 +829,7 @@ extern "C" int evma_enable_keepalive (const unsigned long binding, int idle, int evma_disable_keepalive **********************/ -extern "C" int evma_disable_keepalive (const unsigned long binding) +extern "C" int evma_disable_keepalive (const uintptr_t binding) { ensure_eventmachine("evma_disable_keepalive"); EventableDescriptor *ed = dynamic_cast (Bindable_t::GetObject (binding)); diff --git a/ext/ed.cpp b/ext/ed.cpp index 848b53bef..46de55ad8 100644 --- a/ext/ed.cpp +++ b/ext/ed.cpp @@ -272,11 +272,15 @@ int EventableDescriptor::EnableKeepalive(int idle, int intvl, int cnt) int ret; #ifdef OS_WIN32 + DWORD len; struct tcp_keepalive args; + args.onoff = 1; - args.keepalivetime = idle; - args.keepaliveinterval = intvl; + if (idle > 0) + args.keepalivetime = idle * 1000; + if (intvl > 0) + args.keepaliveinterval = intvl * 1000; ret = WSAIoctl(MySocket, SIO_KEEPALIVE_VALS, &args, sizeof(args), NULL, 0, &len, NULL, NULL); if (ret < 0) { @@ -362,6 +366,7 @@ int EventableDescriptor::DisableKeepalive() int ret; #ifdef OS_WIN32 + DWORD len; struct tcp_keepalive args; args.onoff = 0; ret = WSAIoctl(MySocket, SIO_KEEPALIVE_VALS, &args, sizeof(args), NULL, 0, &len, NULL, NULL); From 6d770f598ab43a9df78232e902bebe01e67f3541 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Tue, 24 Apr 2018 00:34:47 -0700 Subject: [PATCH 178/343] Whitespace --- java/src/com/rubyeventmachine/EmReactor.java | 18 +++++++++--------- .../EventableDatagramChannel.java | 8 ++++---- .../EventableSocketChannel.java | 2 +- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/java/src/com/rubyeventmachine/EmReactor.java b/java/src/com/rubyeventmachine/EmReactor.java index 31642f3f1..5ba26bb32 100644 --- a/java/src/com/rubyeventmachine/EmReactor.java +++ b/java/src/com/rubyeventmachine/EmReactor.java @@ -539,15 +539,15 @@ public Object[] getPeerName (long sig) { } public Object[] getSockName (long sig) { - EventableChannel channel = Connections.get(sig); - if (channel != null) { - return Connections.get(sig).getSockName(); - } - else { - ServerSocketChannel acceptor = Acceptors.get(sig); - return new Object[] { acceptor.socket().getLocalPort(), - acceptor.socket().getInetAddress().getHostAddress() }; - } + EventableChannel channel = Connections.get(sig); + if (channel != null) { + return Connections.get(sig).getSockName(); + } + else { + ServerSocketChannel acceptor = Acceptors.get(sig); + return new Object[] { acceptor.socket().getLocalPort(), + acceptor.socket().getInetAddress().getHostAddress() }; + } } public long attachChannel (SocketChannel sc, boolean watch_mode) { diff --git a/java/src/com/rubyeventmachine/EventableDatagramChannel.java b/java/src/com/rubyeventmachine/EventableDatagramChannel.java index df1c9fd26..d8b83ebdf 100644 --- a/java/src/com/rubyeventmachine/EventableDatagramChannel.java +++ b/java/src/com/rubyeventmachine/EventableDatagramChannel.java @@ -70,11 +70,11 @@ public EventableDatagramChannel (DatagramChannel dc, long _binding, Selector sel } public void scheduleOutboundData (ByteBuffer bb) { - try { + try { if ((!bCloseScheduled) && (bb.remaining() > 0)) { outboundQ.addLast(new Packet(bb, returnAddress)); outboundS += bb.remaining(); - channel.register(selector, SelectionKey.OP_WRITE | SelectionKey.OP_READ, this); + channel.register(selector, SelectionKey.OP_WRITE | SelectionKey.OP_READ, this); } } catch (ClosedChannelException e) { throw new RuntimeException ("no outbound data"); @@ -82,11 +82,11 @@ public void scheduleOutboundData (ByteBuffer bb) { } public void scheduleOutboundDatagram (ByteBuffer bb, String recipAddress, int recipPort) { - try { + try { if ((!bCloseScheduled) && (bb.remaining() > 0)) { outboundQ.addLast(new Packet (bb, new InetSocketAddress (recipAddress, recipPort))); outboundS += bb.remaining(); - channel.register(selector, SelectionKey.OP_WRITE | SelectionKey.OP_READ, this); + channel.register(selector, SelectionKey.OP_WRITE | SelectionKey.OP_READ, this); } } catch (ClosedChannelException e) { throw new RuntimeException ("no outbound data"); diff --git a/java/src/com/rubyeventmachine/EventableSocketChannel.java b/java/src/com/rubyeventmachine/EventableSocketChannel.java index 2905ec6cf..5b4d7ff92 100644 --- a/java/src/com/rubyeventmachine/EventableSocketChannel.java +++ b/java/src/com/rubyeventmachine/EventableSocketChannel.java @@ -242,7 +242,7 @@ public boolean writeOutboundData() throws IOException { // If anyone wants to close immediately, they're responsible for clearing // the outbound queue. return (bCloseScheduled && outboundQ.isEmpty()) ? false : true; - } + } public void setConnectPending() { bConnectPending = true; From df2441e795e39c0b0442ebc2656b125e1aa4df09 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Tue, 24 Apr 2018 00:47:49 -0700 Subject: [PATCH 179/343] Whitespace --- ext/cmain.cpp | 4 ++-- ext/ed.cpp | 6 +++--- ext/em.cpp | 4 ++-- java/src/com/rubyeventmachine/EmReactor.java | 18 +++++++++--------- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/ext/cmain.cpp b/ext/cmain.cpp index 63cb1e959..d23dd9bc5 100644 --- a/ext/cmain.cpp +++ b/ext/cmain.cpp @@ -101,8 +101,8 @@ evma_get_timer_count extern "C" const size_t evma_get_timer_count () { - ensure_eventmachine("evma_get_timer_count"); - return EventMachine->GetTimerCount(); + ensure_eventmachine("evma_get_timer_count"); + return EventMachine->GetTimerCount(); } /************************** diff --git a/ext/ed.cpp b/ext/ed.cpp index 46de55ad8..cc61c41a6 100644 --- a/ext/ed.cpp +++ b/ext/ed.cpp @@ -192,7 +192,7 @@ void EventableDescriptor::Close() * the fd associated with this EventableDescriptor is * closing. * - * EventMachine also never closes fds for STDIN, STDOUT and + * EventMachine also never closes fds for STDIN, STDOUT and * STDERR (0, 1 & 2) */ @@ -1192,7 +1192,7 @@ void ConnectionDescriptor::_WriteOutboundData() * and when we get here. So this condition is not an error. * * 20Jul07, added the same kind of protection against an invalid socket - * that is at the top of ::Read. Not entirely how this could happen in + * that is at the top of ::Read. Not entirely how this could happen in * real life (connection-reset from the remote peer, perhaps?), but I'm * doing it to address some reports of crashing under heavy loads. */ @@ -1555,7 +1555,7 @@ void ConnectionDescriptor::_DispatchCiphertext() // try to put plaintext. INCOMPLETE, doesn't belong here? // In SendOutboundData, we're spooling plaintext directly // into SslBox. That may be wrong, we may need to buffer it - // up here! + // up here! /* const char *ptr; int ptr_length; diff --git a/ext/em.cpp b/ext/em.cpp index 862ef9005..1b1cd8175 100644 --- a/ext/em.cpp +++ b/ext/em.cpp @@ -165,7 +165,7 @@ EventMachine_t::~EventMachine_t() // Run down descriptors size_t i; for (i = 0; i < DescriptorsToDelete.size(); i++) - delete DescriptorsToDelete[i]; + delete DescriptorsToDelete[i]; for (i = 0; i < NewDescriptors.size(); i++) delete NewDescriptors[i]; for (i = 0; i < Descriptors.size(); i++) @@ -1147,7 +1147,7 @@ EventMachine_t::_GetNumberOfOutstandingTimers size_t EventMachine_t::GetTimerCount() { - return Timers.size(); + return Timers.size(); } diff --git a/java/src/com/rubyeventmachine/EmReactor.java b/java/src/com/rubyeventmachine/EmReactor.java index 5ba26bb32..f7474cc6c 100644 --- a/java/src/com/rubyeventmachine/EmReactor.java +++ b/java/src/com/rubyeventmachine/EmReactor.java @@ -527,15 +527,15 @@ public void setTimerQuantum (int mills) { } public Object[] getPeerName (long sig) { - EventableChannel channel = Connections.get(sig); - if (channel != null) { - return Connections.get(sig).getPeerName(); - } - else { - ServerSocketChannel acceptor = Acceptors.get(sig); - return new Object[] { acceptor.socket().getLocalPort(), - acceptor.socket().getInetAddress().getHostAddress() }; - } + EventableChannel channel = Connections.get(sig); + if (channel != null) { + return Connections.get(sig).getPeerName(); + } + else { + ServerSocketChannel acceptor = Acceptors.get(sig); + return new Object[] { acceptor.socket().getLocalPort(), + acceptor.socket().getInetAddress().getHostAddress() }; + } } public Object[] getSockName (long sig) { From 1c996691fe1a7697abd9141505dbc63fed2355f6 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Sat, 28 Apr 2018 16:20:17 -0700 Subject: [PATCH 180/343] Avoid race condition while initializing the machine (#834) Fixes #756 --- lib/eventmachine.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/eventmachine.rb b/lib/eventmachine.rb index 0fe18762c..3812dc1e2 100644 --- a/lib/eventmachine.rb +++ b/lib/eventmachine.rb @@ -178,14 +178,15 @@ def self.run blk=nil, tail=nil, &block @next_tick_queue ||= [] @tails ||= [] begin + initialize_event_machine @reactor_pid = Process.pid + @reactor_thread = Thread.current @reactor_running = true - initialize_event_machine + (b = blk || block) and add_timer(0, b) if @next_tick_queue && !@next_tick_queue.empty? add_timer(0) { signal_loopbreak } end - @reactor_thread = Thread.current # Rubinius needs to come back into "Ruby space" for GC to work, # so we'll crank the machine here. From 20dd5d4c9a5552d72d44122f553c770a82423ae1 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Mon, 23 Apr 2018 08:12:59 -0700 Subject: [PATCH 181/343] Partially revent #327: raising from an unbind crashes and must be prevented --- lib/eventmachine.rb | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/lib/eventmachine.rb b/lib/eventmachine.rb index 3812dc1e2..d8037e35d 100644 --- a/lib/eventmachine.rb +++ b/lib/eventmachine.rb @@ -1492,13 +1492,9 @@ def self.event_callback conn_binding, opcode, data rescue Errno::EBADF, IOError end end - rescue Exception => e - if stopping? - @wrapped_exception = $! - stop - else - raise e - end + rescue + @wrapped_exception = $! + stop end elsif c = @acceptors.delete( conn_binding ) # no-op From 9eb3fb6f01e6fbb9cd82caf011a0c305a965868f Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Sat, 28 Apr 2018 16:04:42 -0700 Subject: [PATCH 182/343] Revert "Fix segfault when an Exception is raised from unbind callback (#766)" This reverts commit 3621c0480803bf5aca5cbfd6ea032f050bd2fa75. --- ext/em.cpp | 21 ++++++--------------- ext/em.h | 1 - 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/ext/em.cpp b/ext/em.cpp index 1b1cd8175..f2e28b114 100644 --- a/ext/em.cpp +++ b/ext/em.cpp @@ -164,8 +164,6 @@ EventMachine_t::~EventMachine_t() { // Run down descriptors size_t i; - for (i = 0; i < DescriptorsToDelete.size(); i++) - delete DescriptorsToDelete[i]; for (i = 0; i < NewDescriptors.size(); i++) delete NewDescriptors[i]; for (i = 0; i < Descriptors.size(); i++) @@ -838,17 +836,6 @@ void EventMachine_t::_CleanupSockets() EventableDescriptor *ed = Descriptors[i]; assert (ed); if (ed->ShouldDelete()) { - DescriptorsToDelete.push_back(ed); - } - else - Descriptors [j++] = ed; - } - while ((size_t)j < Descriptors.size()) - Descriptors.pop_back(); - - nSockets = DescriptorsToDelete.size(); - for (i=0; i < nSockets; i++) { - EventableDescriptor *ed = DescriptorsToDelete[i]; #ifdef HAVE_EPOLL if (Poller == Poller_Epoll) { assert (epfd != -1); @@ -864,9 +851,13 @@ void EventMachine_t::_CleanupSockets() ModifiedDescriptors.erase(ed); } #endif - delete ed; + delete ed; + } + else + Descriptors [j++] = ed; } - DescriptorsToDelete.clear(); + while ((size_t)j < Descriptors.size()) + Descriptors.pop_back(); } /********************************* diff --git a/ext/em.h b/ext/em.h index a01336a08..3b11ea014 100644 --- a/ext/em.h +++ b/ext/em.h @@ -242,7 +242,6 @@ class EventMachine_t std::map Pids; std::vector Descriptors; std::vector NewDescriptors; - std::vector DescriptorsToDelete; std::set ModifiedDescriptors; SOCKET LoopBreakerReader; From d5cb4dcfe2e5c3622d600412070f26666f0c118f Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Sat, 28 Apr 2018 16:03:08 -0700 Subject: [PATCH 183/343] The unbind callback must not raise exceptions The unbind callback absolutely must not raise an exception or the reactor will crash. If there is no EM.error_handler, or if the error_handler retrows, then stop the reactor, stash the exception in $wrapped_exception, and the exception will be raised after the reactor is cleaned up (see the last line of self.run). This fixes many crash reports in the 1.2 series since #327 was merged. --- lib/eventmachine.rb | 20 +++++++++++++++++--- tests/test_basic.rb | 6 ++++++ tests/test_pool.rb | 2 +- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/lib/eventmachine.rb b/lib/eventmachine.rb index d8037e35d..6f32c58f0 100644 --- a/lib/eventmachine.rb +++ b/lib/eventmachine.rb @@ -1492,9 +1492,23 @@ def self.event_callback conn_binding, opcode, data rescue Errno::EBADF, IOError end end - rescue - @wrapped_exception = $! - stop + # As noted above, unbind absolutely must not raise an exception or the reactor will crash. + # If there is no EM.error_handler, or if the error_handler retrows, then stop the reactor, + # stash the exception in $wrapped_exception, and the exception will be raised after the + # reactor is cleaned up (see the last line of self.run). + rescue Exception => error + if instance_variable_defined? :@error_handler + begin + @error_handler.call error + # No need to stop unless error_handler rethrows + rescue Exception => error + @wrapped_exception = error + stop + end + else + @wrapped_exception = error + stop + end end elsif c = @acceptors.delete( conn_binding ) # no-op diff --git a/tests/test_basic.rb b/tests/test_basic.rb index b8f625803..4d7252400 100644 --- a/tests/test_basic.rb +++ b/tests/test_basic.rb @@ -113,6 +113,9 @@ def test_unbind_error EM.start_server "127.0.0.1", @port EM.connect "127.0.0.1", @port, UnbindError } + + # Remove the error handler before the next test + EM.error_handler(nil) end module BrsTestSrv @@ -309,6 +312,9 @@ def test_error_handler_idempotent # issue 185 EM.add_timer(0.001) { EM.stop } end + # Remove the error handler before the next test + EM.error_handler(nil) + assert_equal 1, errors.size assert_equal [:first, :second], ticks end diff --git a/tests/test_pool.rb b/tests/test_pool.rb index 6bd117e7a..f859a9d10 100644 --- a/tests/test_pool.rb +++ b/tests/test_pool.rb @@ -56,7 +56,7 @@ def test_reques_resources_on_error assert_equal pooled_res, pooled_res2 end - def test_supports_custom_error_handler + def test_supports_custom_on_error eres = nil pool.on_error do |res| eres = res From 366dc45ce2d81fed4de168483f55dc24d2479dfc Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Tue, 24 Apr 2018 00:19:42 -0700 Subject: [PATCH 184/343] Small test fix --- tests/test_ipv6.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_ipv6.rb b/tests/test_ipv6.rb index b4fff67a8..b52fef1f8 100644 --- a/tests/test_ipv6.rb +++ b/tests/test_ipv6.rb @@ -41,7 +41,7 @@ def test_ipv6_udp_local_server EM.run do EM.open_datagram_socket(@@public_ipv6, @local_port) do |s| def s.receive_data data - _port, @@remote_ip = Socket.unpack_sockaddr_in(s.get_peername) + _port, @@remote_ip = Socket.unpack_sockaddr_in(get_peername) @@received_data = data EM.stop end From 31fec273d484f3dd34ca001ae4a63ee7f9059dc1 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Sat, 28 Apr 2018 15:35:00 -0700 Subject: [PATCH 185/343] Avoid explicitly calling class methods when in class scope --- lib/eventmachine.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/eventmachine.rb b/lib/eventmachine.rb index 6f32c58f0..2d0221831 100644 --- a/lib/eventmachine.rb +++ b/lib/eventmachine.rb @@ -986,7 +986,7 @@ def self.run_deferred_callbacks # do some work during the next_tick. The only mechanism we have from the # ruby side is next_tick itself, although ideally, we'd just drop a byte # on the loopback descriptor. - EM.next_tick {} if exception_raised + next_tick {} if exception_raised end end end @@ -1081,7 +1081,7 @@ def self.spawn_threadpool raise error unless eback @resultqueue << [error, eback] end - EventMachine.signal_loopbreak + signal_loopbreak end end @threadpool << thread @@ -1515,7 +1515,7 @@ def self.event_callback conn_binding, opcode, data else if $! # Bubble user generated errors. @wrapped_exception = $! - EM.stop + stop else raise ConnectionNotBound, "received ConnectionUnbound for an unknown signature: #{conn_binding}" end From 24f96e68360334037fb14c723c44e3376450557a Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Sat, 28 Apr 2018 16:55:29 -0700 Subject: [PATCH 186/343] Abstract out an EmReactorInterface and create a NullEmReactor and (#572) NullEventableChannel to use when the reactor should not be running. This is a rebase of PR #124. Fixes #123 Fixes #124 --- java/src/com/rubyeventmachine/EmReactor.java | 38 +++-- .../rubyeventmachine/EmReactorInterface.java | 70 ++++++++ .../com/rubyeventmachine/NullEmReactor.java | 157 ++++++++++++++++++ .../NullEventableChannel.java | 81 +++++++++ lib/jeventmachine.rb | 6 +- tests/jruby/test_jeventmachine.rb | 38 +++++ 6 files changed, 375 insertions(+), 15 deletions(-) create mode 100644 java/src/com/rubyeventmachine/EmReactorInterface.java create mode 100644 java/src/com/rubyeventmachine/NullEmReactor.java create mode 100644 java/src/com/rubyeventmachine/NullEventableChannel.java create mode 100644 tests/jruby/test_jeventmachine.rb diff --git a/java/src/com/rubyeventmachine/EmReactor.java b/java/src/com/rubyeventmachine/EmReactor.java index f7474cc6c..09e22e729 100644 --- a/java/src/com/rubyeventmachine/EmReactor.java +++ b/java/src/com/rubyeventmachine/EmReactor.java @@ -36,7 +36,9 @@ import java.util.concurrent.atomic.*; import java.security.*; -public class EmReactor { +public class EmReactor implements EmReactorInterface +{ + private static final NullEventableChannel NULL_EVENTABLE_CHANNEL = new NullEventableChannel(); public final int EM_TIMER_FIRED = 100; public final int EM_CONNECTION_READ = 101; public final int EM_CONNECTION_UNBOUND = 102; @@ -132,7 +134,7 @@ void addNewConnections() { while (iter2.hasNext()) { long b = iter2.next(); - EventableChannel ec = Connections.get(b); + EventableChannel ec = getConnection(b); if (ec != null) { try { ec.register(); @@ -437,7 +439,17 @@ public long openUdpSocket (String address, int port) throws IOException { } public void sendData (long sig, ByteBuffer bb) throws IOException { - Connections.get(sig).scheduleOutboundData( bb ); + getConnection(sig).scheduleOutboundData(bb); + } + + private EventableChannel getConnection(long sig) + { + EventableChannel channel = Connections.get(sig); + if (channel == null) + { + channel = NULL_EVENTABLE_CHANNEL; + } + return channel; } public void sendData (long sig, byte[] data) throws IOException { @@ -445,7 +457,7 @@ public void sendData (long sig, byte[] data) throws IOException { } public void setCommInactivityTimeout (long sig, long mills) { - Connections.get(sig).setCommInactivityTimeout (mills); + getConnection(sig).setCommInactivityTimeout(mills); } public void sendDatagram (long sig, byte[] data, int length, String recipAddress, int recipPort) { @@ -453,7 +465,7 @@ public void sendDatagram (long sig, byte[] data, int length, String recipAddress } public void sendDatagram (long sig, ByteBuffer bb, String recipAddress, int recipPort) { - (Connections.get(sig)).scheduleOutboundDatagram( bb, recipAddress, recipPort); + (getConnection(sig)).scheduleOutboundDatagram( bb, recipAddress, recipPort); } public long connectTcpServer (String address, int port) { @@ -500,7 +512,7 @@ public long connectTcpServer (String bindAddr, int bindPort, String address, int } public void closeConnection (long sig, boolean afterWriting) { - EventableChannel ec = Connections.get(sig); + EventableChannel ec = getConnection(sig); if (ec != null) if (ec.scheduleClose (afterWriting)) UnboundConnections.add (sig); @@ -517,7 +529,7 @@ public void signalLoopbreak() { } public void startTls (long sig) throws NoSuchAlgorithmException, KeyManagementException { - Connections.get(sig).startTls(); + getConnection(sig).startTls(); } public void setTimerQuantum (int mills) { @@ -566,7 +578,7 @@ public long attachChannel (SocketChannel sc, boolean watch_mode) { } public SocketChannel detachChannel (long sig) { - EventableSocketChannel ec = (EventableSocketChannel) Connections.get (sig); + EventableSocketChannel ec = (EventableSocketChannel) getConnection(sig); if (ec != null) { UnboundConnections.add (sig); return ec.getChannel(); @@ -576,19 +588,19 @@ public SocketChannel detachChannel (long sig) { } public void setNotifyReadable (long sig, boolean mode) { - ((EventableSocketChannel) Connections.get(sig)).setNotifyReadable(mode); + ((EventableSocketChannel) getConnection(sig)).setNotifyReadable(mode); } public void setNotifyWritable (long sig, boolean mode) { - ((EventableSocketChannel) Connections.get(sig)).setNotifyWritable(mode); + ((EventableSocketChannel) getConnection(sig)).setNotifyWritable(mode); } public boolean isNotifyReadable (long sig) { - return Connections.get(sig).isNotifyReadable(); + return getConnection(sig).isNotifyReadable(); } public boolean isNotifyWritable (long sig) { - return Connections.get(sig).isNotifyWritable(); + return getConnection(sig).isNotifyWritable(); } public boolean pauseConnection (long sig) { @@ -608,6 +620,6 @@ public long getOutboundDataSize (long sig) { } public int getConnectionCount() { - return Connections.size() + Acceptors.size(); + return Connections.size() + Acceptors.size(); } } diff --git a/java/src/com/rubyeventmachine/EmReactorInterface.java b/java/src/com/rubyeventmachine/EmReactorInterface.java new file mode 100644 index 000000000..85b4d06bd --- /dev/null +++ b/java/src/com/rubyeventmachine/EmReactorInterface.java @@ -0,0 +1,70 @@ +package com.rubyeventmachine; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.SocketChannel; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; + +public interface EmReactorInterface +{ + void eventCallback (long sig, int eventType, ByteBuffer data, long data2); + + void eventCallback (long sig, int eventType, ByteBuffer data); + + void run(); + + void stop(); + + long installOneshotTimer (long milliseconds); + + long startTcpServer (SocketAddress sa) throws EmReactorException; + + long startTcpServer (String address, int port) throws EmReactorException; + + void stopTcpServer (long signature) throws IOException; + + long openUdpSocket (InetSocketAddress address) throws IOException; + + long openUdpSocket (String address, int port) throws IOException; + + void sendData (long sig, ByteBuffer bb) throws IOException; + + void sendData (long sig, byte[] data) throws IOException; + + void setCommInactivityTimeout (long sig, long mills); + + void sendDatagram (long sig, byte[] data, int length, String recipAddress, int recipPort); + + void sendDatagram (long sig, ByteBuffer bb, String recipAddress, int recipPort); + + long connectTcpServer (String address, int port); + + long connectTcpServer (String bindAddr, int bindPort, String address, int port); + + void closeConnection (long sig, boolean afterWriting); + + void signalLoopbreak(); + + void startTls (long sig) throws NoSuchAlgorithmException, KeyManagementException; + + void setTimerQuantum (int mills); + + Object[] getPeerName (long sig); + + long attachChannel (SocketChannel sc, boolean watch_mode); + + SocketChannel detachChannel (long sig); + + void setNotifyReadable (long sig, boolean mode); + + void setNotifyWritable (long sig, boolean mode); + + boolean isNotifyReadable (long sig); + + boolean isNotifyWritable (long sig); + + int getConnectionCount(); +} diff --git a/java/src/com/rubyeventmachine/NullEmReactor.java b/java/src/com/rubyeventmachine/NullEmReactor.java new file mode 100644 index 000000000..9c6bf40ba --- /dev/null +++ b/java/src/com/rubyeventmachine/NullEmReactor.java @@ -0,0 +1,157 @@ +package com.rubyeventmachine; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.SocketChannel; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; + +public class NullEmReactor implements EmReactorInterface +{ + public void eventCallback(long sig, int eventType, ByteBuffer data, long data2) + { + + } + + public void eventCallback(long sig, int eventType, ByteBuffer data) + { + + } + + public void run() + { + + } + + public void stop() + { + + } + + public long installOneshotTimer(long milliseconds) + { + return 0; + } + + public long startTcpServer(SocketAddress sa) throws EmReactorException + { + return 0; + } + + public long startTcpServer(String address, int port) throws EmReactorException + { + return 0; + } + + public void stopTcpServer(long signature) throws IOException + { + + } + + public long openUdpSocket(InetSocketAddress address) throws IOException + { + return 0; + } + + public long openUdpSocket(String address, int port) throws IOException + { + return 0; + } + + public void sendData(long sig, ByteBuffer bb) throws IOException + { + + } + + public void sendData(long sig, byte[] data) throws IOException + { + + } + + public void setCommInactivityTimeout(long sig, long mills) + { + + } + + public void sendDatagram(long sig, byte[] data, int length, String recipAddress, int recipPort) + { + + } + + public void sendDatagram(long sig, ByteBuffer bb, String recipAddress, int recipPort) + { + + } + + public long connectTcpServer(String address, int port) + { + return 0; + } + + public long connectTcpServer(String bindAddr, int bindPort, String address, int port) + { + return 0; + } + + public void closeConnection(long sig, boolean afterWriting) + { + + } + + public void signalLoopbreak() + { + + } + + public void startTls(long sig) throws NoSuchAlgorithmException, KeyManagementException + { + + } + + public void setTimerQuantum(int mills) + { + + } + + public Object[] getPeerName(long sig) + { + return new Object[0]; + } + + public long attachChannel(SocketChannel sc, boolean watch_mode) + { + return 0; + } + + public SocketChannel detachChannel(long sig) + { + return null; + } + + public void setNotifyReadable(long sig, boolean mode) + { + + } + + public void setNotifyWritable(long sig, boolean mode) + { + + } + + public boolean isNotifyReadable(long sig) + { + return false; + } + + public boolean isNotifyWritable(long sig) + { + return false; + } + + public int getConnectionCount() + { + return 0; + } +} diff --git a/java/src/com/rubyeventmachine/NullEventableChannel.java b/java/src/com/rubyeventmachine/NullEventableChannel.java new file mode 100644 index 000000000..73dea2657 --- /dev/null +++ b/java/src/com/rubyeventmachine/NullEventableChannel.java @@ -0,0 +1,81 @@ +package com.rubyeventmachine; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.ClosedChannelException; + +public class NullEventableChannel implements EventableChannel +{ + public void scheduleOutboundData(ByteBuffer bb) + { + } + + public void scheduleOutboundDatagram(ByteBuffer bb, String recipAddress, int recipPort) + { + } + + public boolean scheduleClose(boolean afterWriting) + { + return false; + } + + public void startTls() + { + } + + public long getBinding() + { + return 0; + } + + public void readInboundData(ByteBuffer dst) throws IOException + { + } + + public void register() throws ClosedChannelException + { + } + + public void close() + { + } + + public boolean writeOutboundData() throws IOException + { + return false; + } + + public void setCommInactivityTimeout(long seconds) + { + } + + public Object[] getPeerName() + { + return new Object[0]; + } + + public Object[] getSockName() + { + return new Object[0]; + } + + public boolean isWatchOnly() + { + return false; + } + + public boolean isNotifyReadable() + { + return false; + } + + public boolean isNotifyWritable() + { + return false; + } + + public long getOutboundDataSize () + { + return 0; + } +} diff --git a/lib/jeventmachine.rb b/lib/jeventmachine.rb index 45f9e2aa0..5e10da039 100644 --- a/lib/jeventmachine.rb +++ b/lib/jeventmachine.rb @@ -80,6 +80,9 @@ module EventMachine # @private SslVerify = 109 + NULL_EM_REACTOR = com.rubyeventmachine.NullEmReactor.new + @em ||= NULL_EM_REACTOR + # Exceptions that are defined in rubymain.cpp class ConnectionError < RuntimeError; end class ConnectionNotBound < RuntimeError; end @@ -104,7 +107,7 @@ def self.initialize_event_machine @em = JEM.new end def self.release_machine - @em = nil + @em = NULL_EM_REACTOR end def self.add_oneshot_timer interval @em.installOneshotTimer interval @@ -302,4 +305,3 @@ def get_outbound_data_size end end end - diff --git a/tests/jruby/test_jeventmachine.rb b/tests/jruby/test_jeventmachine.rb new file mode 100644 index 000000000..cbd0e3b2e --- /dev/null +++ b/tests/jruby/test_jeventmachine.rb @@ -0,0 +1,38 @@ +if !(RUBY_PLATFORM =~ /java/) + puts "Ignorming tests in #{__FILE__}. They must be run in JRuby " +else + require 'test/unit' + require 'jeventmachine' + + class TestJEventmachine < Test::Unit::TestCase + + def setup + EventMachine.instance_variable_set("@em", nil) + load 'jeventmachine.rb' + end + + def test_can_make_calls_without_errors_before_initialization + assert_equal nil, EventMachine.signal_loopbreak + assert_equal nil, EventMachine.stop_tcp_server(123) + assert_equal nil, EventMachine.send_data(123, "rewr", 4) + assert_equal nil, EventMachine.close_connection(332, nil) + end + + def test_create + EventMachine::initialize_event_machine + em = EventMachine::instance_variable_get("@em") + assert_equal true, em.is_a?(Java::com.rubyeventmachine.EmReactor) + end + + def test_can_make_calls_without_errors_after_release + EventMachine.initialize_event_machine + EventMachine.release_machine + + assert_equal nil, EventMachine.signal_loopbreak + assert_equal nil, EventMachine.stop_tcp_server(123) + assert_equal nil, EventMachine.send_data(123, "rewr", 4) + assert_equal nil, EventMachine.close_connection(332, nil) + end + + end +end From 2691b2a561909bdcc828a6172a71f37ba88f3364 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Sat, 28 Apr 2018 17:03:16 -0700 Subject: [PATCH 187/343] Add more Ruby versions to Appveyor --- appveyor.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/appveyor.yml b/appveyor.yml index 4360a1a10..3d1348113 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -21,5 +21,11 @@ environment: - ruby_version: "21-x64" - ruby_version: "22" - ruby_version: "22-x64" + - ruby_version: "23" + - ruby_version: "23-x64" + - ruby_version: "24" + - ruby_version: "24-x64" + - ruby_version: "25" + - ruby_version: "25-x64" cache: - vendor From ef3f153947a83d458f7af5ce3a00425cee1133a4 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Sat, 28 Apr 2018 17:39:22 -0700 Subject: [PATCH 188/343] Add missing EM_PROTO_* variables to Java Reactor Fixes #773 Fixes #791 Fixes #797 --- lib/jeventmachine.rb | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/jeventmachine.rb b/lib/jeventmachine.rb index 5e10da039..54e5b1ebf 100644 --- a/lib/jeventmachine.rb +++ b/lib/jeventmachine.rb @@ -80,6 +80,17 @@ module EventMachine # @private SslVerify = 109 + # @private + EM_PROTO_SSLv2 = 2 + # @private + EM_PROTO_SSLv3 = 4 + # @private + EM_PROTO_TLSv1 = 8 + # @private + EM_PROTO_TLSv1_1 = 16 + # @private + EM_PROTO_TLSv1_2 = 32 + NULL_EM_REACTOR = com.rubyeventmachine.NullEmReactor.new @em ||= NULL_EM_REACTOR From cf9613f1f85f35689141496f22920c2ba65f7521 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Sat, 28 Apr 2018 18:14:07 -0700 Subject: [PATCH 189/343] Doc fixes for EM::Protocols::HttpClient & HttpClient2 Thanks @MSP-Greg --- lib/em/protocols/httpclient.rb | 55 +++++++++++++++++---------------- lib/em/protocols/httpclient2.rb | 36 +++++++++++---------- 2 files changed, 48 insertions(+), 43 deletions(-) diff --git a/lib/em/protocols/httpclient.rb b/lib/em/protocols/httpclient.rb index 38b175ce2..80d217524 100644 --- a/lib/em/protocols/httpclient.rb +++ b/lib/em/protocols/httpclient.rb @@ -23,38 +23,41 @@ # # + module EventMachine module Protocols - # Note: This class is deprecated and will be removed. Please use EM-HTTP-Request instead. # # @example - # EventMachine.run { - # http = EventMachine::Protocols::HttpClient.request( - # :host => server, - # :port => 80, - # :request => "/index.html", - # :query_string => "parm1=value1&parm2=value2" - # ) - # http.callback {|response| - # puts response[:status] - # puts response[:headers] - # puts response[:content] - # } - # } + # + # EventMachine.run { + # http = EventMachine::Protocols::HttpClient.request( + # :host => server, + # :port => 80, + # :request => "/index.html", + # :query_string => "parm1=value1&parm2=value2" + # ) + # http.callback {|response| + # puts response[:status] + # puts response[:headers] + # puts response[:content] + # } + # } #-- - # TODO: - # Add streaming so we can support enormous POSTs. Current max is 20meg. - # Timeout for connections that run too long or hang somewhere in the middle. - # Persistent connections (HTTP/1.1), may need a associated delegate object. - # DNS: Some way to cache DNS lookups for hostnames we connect to. Ruby's - # DNS lookups are unbelievably slow. - # HEAD requests. - # Convenience methods for requests. get, post, url, etc. - # SSL. - # Handle status codes like 304, 100, etc. - # Refactor this code so that protocol errors all get handled one way (an exception?), - # instead of sprinkling set_deferred_status :failed calls everywhere. + # @todo Add streaming so we can support enormous POSTs. Current max is 20meg. + # Timeout for connections that run too long or hang somewhere in the middle. + # Persistent connections (HTTP/1.1), may need a associated delegate object. + # DNS: Some way to cache DNS lookups for hostnames we connect to. Ruby's + # DNS lookups are unbelievably slow. + # HEAD requests. + # Convenience methods for requests. get, post, url, etc. + # SSL. + # Handle status codes like 304, 100, etc. + # Refactor this code so that protocol errors all get handled one way (an exception?), + # instead of sprinkling set_deferred_status :failed calls everywhere. + # + # @deprecated Please use [EM-HTTP-Request](https://github.com/igrigorik/em-http-request) instead. + # class HttpClient < Connection include EventMachine::Deferrable diff --git a/lib/em/protocols/httpclient2.rb b/lib/em/protocols/httpclient2.rb index 0fb64e804..d8dfdba53 100644 --- a/lib/em/protocols/httpclient2.rb +++ b/lib/em/protocols/httpclient2.rb @@ -26,23 +26,26 @@ module EventMachine module Protocols - # Note: This class is deprecated and will be removed. Please use EM-HTTP-Request instead. + # ### Usage # - # === Usage + # ```ruby + # EM.run{ + # conn = EM::Protocols::HttpClient2.connect 'google.com', 80 # - # EM.run{ - # conn = EM::Protocols::HttpClient2.connect 'google.com', 80 + # req = conn.get('/') + # req.callback{ |response| + # p(response.status) + # p(response.headers) + # p(response.content) + # } + # } + # ``` + # + # @deprecated Please use [EM-HTTP-Request](https://github.com/igrigorik/em-http-request) instead. # - # req = conn.get('/') - # req.callback{ |response| - # p(response.status) - # p(response.headers) - # p(response.content) - # } - # } class HttpClient2 < Connection include LineText2 - + def initialize warn "HttpClient2 is deprecated and will be removed. EM-Http-Request should be used instead." @@ -84,7 +87,6 @@ def send_request @conn.send_data r.join end - #-- # def receive_line ln @@ -259,8 +261,8 @@ def self.connect *args # Get a url # - # req = conn.get(:uri => '/') - # req.callback{|response| puts response.content } + # req = conn.get(:uri => '/') + # req.callback{|response| puts response.content } # def get args if args.is_a?(String) @@ -272,8 +274,8 @@ def get args # Post to a url # - # req = conn.post('/data') - # req.callback{|response| puts response.content } + # req = conn.post('/data') + # req.callback{|response| puts response.content } #-- # XXX there's no way to supply a POST body.. wtf? def post args From 90271746e5efaba82627e7c33bf654887139fb1d Mon Sep 17 00:00:00 2001 From: MSP-Greg Date: Sun, 24 Dec 2017 12:37:54 -0600 Subject: [PATCH 190/343] Commit misc changes for fork --- Gemfile | 4 +-- appveyor.yml | 48 +++++++++++++++++++++++++++++------- eventmachine.gemspec | 3 ++- tests/em_test_helper.rb | 4 +-- tests/test_basic.rb | 21 ++++++++-------- tests/test_channel.rb | 4 +-- tests/test_exc.rb | 12 +++------ tests/test_file_watch.rb | 2 +- tests/test_httpclient2.rb | 28 +++++++++++---------- tests/test_pure.rb | 1 + tests/test_resolver.rb | 6 ++--- tests/test_ssl_dhparam.rb | 4 +-- tests/test_ssl_ecdh_curve.rb | 13 ++++++++-- tests/test_ssl_methods.rb | 2 +- tests/test_ssl_protocols.rb | 9 +++++-- tests/test_ssl_verify.rb | 6 ++--- 16 files changed, 104 insertions(+), 63 deletions(-) diff --git a/Gemfile b/Gemfile index 73331827a..e0b2f0883 100644 --- a/Gemfile +++ b/Gemfile @@ -2,7 +2,7 @@ source 'https://rubygems.org' gemspec -# Rake 11.0 no longer supports Ruby 1.9.2 +# Rake 11.0 no longer supports Ruby 1.8.7 and Ruby 1.9.2 if RUBY_VERSION < '1.9.3' gem 'rake', '< 11' else @@ -11,5 +11,5 @@ end group :documentation do gem 'yard', '>= 0.8.5.2' - gem 'bluecloth' unless RUBY_PLATFORM =~ /java|mswin|mingw/ + gem 'redcarpet' unless RUBY_PLATFORM =~ /java|mswin/ end diff --git a/appveyor.yml b/appveyor.yml index 3d1348113..04a4597a2 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,31 +1,61 @@ --- version: "{build}" clone_depth: 10 +init: + - set PATH=C:\ruby%ruby_version%\bin;C:\Program Files\7-Zip;C:\Program Files\AppVeyor\BuildAgent;C:\Program Files\Git\cmd;C:\Windows\system32 + - if %ruby_version%==_trunk ( + appveyor DownloadFile https://ci.appveyor.com/api/projects/MSP-Greg/ruby-loco/artifacts/ruby_trunk.7z -FileName C:\ruby_trunk.7z & + 7z x C:\ruby_trunk.7z -oC:\ruby_trunk & + C:\ruby_trunk\trunk_pkgs.cmd + ) install: - - SET PATH=C:\Ruby%ruby_version%\bin;%PATH% + # RI DevKit is only installed in Ruby23 and Ruby23-x64 folders + # need to install OpenSSL since standard DevKit doesn't contain it + - if "%ri_file%"=="x86" ( + appveyor DownloadFile https://dl.bintray.com/oneclick/OpenKnapsack/x86/openssl-1.0.2j-x86-windows.tar.lzma & + 7z e openssl-1.0.2j-x86-windows.tar.lzma & + 7z x -y openssl-1.0.2j-x86-windows.tar -oC:\ruby23\DevKit\mingw & + set b_config="--with-ssl-dir=C:/ruby23/DevKit/mingw --with-opt-include=C:/ruby23/DevKit/mingw/include" & + set SSL_CERT_FILE=C:/ruby24-x64/ssl/cert.pem + ) + - if "%ri_file%"=="x64" ( + appveyor DownloadFile https://dl.bintray.com/oneclick/OpenKnapsack/x64/openssl-1.0.2j-x64-windows.tar.lzma & + 7z e openssl-1.0.2j-x64-windows.tar.lzma & + 7z x -y openssl-1.0.2j-x64-windows.tar -oC:\ruby23-x64\DevKit\mingw & + set b_config="--with-ssl-dir=C:/ruby23-x64/DevKit/mingw --with-opt-include=C:/ruby23-x64/DevKit/mingw/include" & + set SSL_CERT_FILE=C:/ruby24-x64/ssl/cert.pem + ) - ruby --version - - gem --version - - gem install bundler --quiet --no-ri --no-rdoc + - gem --version + - gem install bundler --quiet --no-ri --no-rdoc --no-document - bundler --version - - bundle install --without documentation --path vendor/bundle + - bundle install --without documentation --path vendor/%ruby_version%/bundle build_script: - - bundle exec rake compile + - bundle config build.eventmachine --no-document --env-shebang %b_config% + - bundle exec rake -rdevkit compile -- %b_config% test_script: - - bundle exec rake test + - bundle exec rake -rdevkit test +on_finish: + - ruby -v environment: - TESTOPTS: -v + TESTOPTS: -v --no-show-detail-immediately + ri_file: none matrix: - ruby_version: "200" - ruby_version: "200-x64" - ruby_version: "21" - ruby_version: "21-x64" - ruby_version: "22" + ri_file: x86 - ruby_version: "22-x64" + ri_file: x64 - ruby_version: "23" + ri_file: x86 - ruby_version: "23-x64" - - ruby_version: "24" + ri_file: x64 - ruby_version: "24-x64" - - ruby_version: "25" + b_config: "--use-system-libraries" - ruby_version: "25-x64" + b_config: "--use-system-libraries" cache: - vendor diff --git a/eventmachine.gemspec b/eventmachine.gemspec index eb57217ee..c3834013c 100644 --- a/eventmachine.gemspec +++ b/eventmachine.gemspec @@ -15,7 +15,8 @@ Gem::Specification.new do |s| s.extensions = ["ext/extconf.rb", "ext/fastfilereader/extconf.rb"] if s.respond_to?(:metadata=) - s.metadata = { "msys2_mingw_dependencies" => "openssl" } + s.metadata ||= {} + s.metadata["msys2_mingw_dependencies"] = "openssl" end s.add_development_dependency 'test-unit', '~> 2.0' diff --git a/tests/em_test_helper.rb b/tests/em_test_helper.rb index 20a3e59a6..b313e8bac 100644 --- a/tests/em_test_helper.rb +++ b/tests/em_test_helper.rb @@ -116,8 +116,8 @@ def rbx? include PlatformHelper extend PlatformHelper - # Tests run significantly slower on windows. YMMV - TIMEOUT_INTERVAL = windows? ? 1 : 0.25 + # Tests may run slower on windows or Appveyor. YMMV + TIMEOUT_INTERVAL = windows? ? 0.25 : 0.25 def silent backup, $VERBOSE = $VERBOSE, nil diff --git a/tests/test_basic.rb b/tests/test_basic.rb index 4d7252400..977094653 100644 --- a/tests/test_basic.rb +++ b/tests/test_basic.rb @@ -6,6 +6,8 @@ def setup @port = next_port end + INVALID = "(not known|no data of the requested|No such host is known)" + def test_connection_class_cache mod = Module.new a, b = nil, nil @@ -21,7 +23,6 @@ def test_connection_class_cache #------------------------------------- - def test_em assert_nothing_raised do EM.run { @@ -147,8 +148,6 @@ def test_byte_range_send end def test_bind_connect - pend('FIXME: this test is broken on Windows') if windows? - local_ip = UDPSocket.open {|s| s.connect('localhost', 80); s.addr.last } bind_port = next_port @@ -187,7 +186,7 @@ def test_invalid_address_bind_connect_dst end assert_kind_of(EventMachine::ConnectionError, e) - assert_match(/unable to resolve address:.*not known/, e.message) + assert_match(/unable to resolve address:.*#{INVALID}/, e.message) end def test_invalid_address_bind_connect_src @@ -203,7 +202,7 @@ def test_invalid_address_bind_connect_src end assert_kind_of(EventMachine::ConnectionError, e) - assert_match(/invalid bind address:.*not known/, e.message) + assert_match(/invalid bind address:.*#{INVALID}/, e.message) end def test_reactor_thread? @@ -220,7 +219,7 @@ def test_schedule_on_reactor_thread end assert x end - + def test_schedule_from_thread x = false EM.run do @@ -239,14 +238,14 @@ def test_set_heartbeat_interval } assert_equal(interval, $interval) end - + module PostInitRaiser ERR = Class.new(StandardError) def post_init raise ERR end end - + def test_bubble_errors_from_post_init assert_raises(PostInitRaiser::ERR) do EM.run do @@ -255,14 +254,14 @@ def test_bubble_errors_from_post_init end end end - + module InitializeRaiser ERR = Class.new(StandardError) def initialize raise ERR end end - + def test_bubble_errors_from_initialize assert_raises(InitializeRaiser::ERR) do EM.run do @@ -271,7 +270,7 @@ def test_bubble_errors_from_initialize end end end - + def test_schedule_close omit_if(jruby?) localhost, port = '127.0.0.1', 9000 diff --git a/tests/test_channel.rb b/tests/test_channel.rb index c54bf1da7..872b1109a 100644 --- a/tests/test_channel.rb +++ b/tests/test_channel.rb @@ -64,8 +64,8 @@ def test_channel_num_subscribers subs = 0 EM.run do c = EM::Channel.new - c.subscribe { |v| s = v } - c.subscribe { |v| s = v } + c.subscribe { |v| } + c.subscribe { |v| } EM.next_tick { EM.stop } subs = c.num_subscribers end diff --git a/tests/test_exc.rb b/tests/test_exc.rb index d9c860aed..501636b5f 100644 --- a/tests/test_exc.rb +++ b/tests/test_exc.rb @@ -18,25 +18,19 @@ def unbind def test_a assert_raises(RuntimeError) { - EM.run { - raise "some exception" - } + EM.run { raise "some exception" } } end def test_b assert_raises(RuntimeError) { - EM.run { - raise "some exception" - } + EM.run { raise "some exception" } } end def test_exception_on_unbind assert_raises(DoomedConnectionError) { - EM.run { - EM.connect("localhost", 8888, DoomedConnection) - } + EM.run { EM.connect("localhost", 8888, DoomedConnection) } } end diff --git a/tests/test_file_watch.rb b/tests/test_file_watch.rb index 560207174..86eb14288 100644 --- a/tests/test_file_watch.rb +++ b/tests/test_file_watch.rb @@ -70,7 +70,7 @@ def test_invalid_signature assert_raise EventMachine::InvalidSignature do w2.stop_watching end - + w1.stop_watching rescue nil EM.stop } end diff --git a/tests/test_httpclient2.rb b/tests/test_httpclient2.rb index a00fcbcb4..9a3d242f3 100644 --- a/tests/test_httpclient2.rb +++ b/tests/test_httpclient2.rb @@ -4,6 +4,8 @@ class TestHttpClient2 < Test::Unit::TestCase class TestServer < EM::Connection end + TIMEOUT = (windows? ? 1.5 : 1) + def setup @port = next_port end @@ -15,7 +17,7 @@ def setup # def test_connect EM.run { - setup_timeout(1) + setup_timeout(TIMEOUT_INTERVAL) EM.start_server '127.0.0.1', @port, TestServer silent do EM::P::HttpClient2.connect '127.0.0.1', @port @@ -27,7 +29,7 @@ def test_connect def test_bad_port EM.run { - setup_timeout(1) + setup_timeout(TIMEOUT_INTERVAL) EM.start_server '127.0.0.1', @port, TestServer assert_raises( ArgumentError ) { silent { EM::P::HttpClient2.connect '127.0.0.1', "xxx" } @@ -39,7 +41,7 @@ def test_bad_port def test_bad_server err = nil EM.run { - setup_timeout(1) + setup_timeout(TIMEOUT) http = silent { EM::P::HttpClient2.connect '127.0.0.1', 9999 } d = http.get "/" d.errback { err = true; d.internal_error; EM.stop } @@ -50,8 +52,8 @@ def test_bad_server def test_get content = nil EM.run { - setup_timeout(1) - http = silent { EM::P::HttpClient2.connect :host => "google.com", :port => 80, :version => '1.0' } + setup_timeout(TIMEOUT_INTERVAL) + http = silent { EM::P::HttpClient2.connect :host => "google.com", :port => 80 } d = http.get "/" d.callback { content = d.content @@ -67,8 +69,8 @@ def test_get def _test_get_multiple content = nil EM.run { - setup_timeout(1) - http = silent { EM::P::HttpClient2.connect "google.com", :version => '1.0' } + setup_timeout(TIMEOUT_INTERVAL) + http = silent { EM::P::HttpClient2.connect "www.google.com" } d = http.get "/" d.callback { e = http.get "/" @@ -84,8 +86,8 @@ def _test_get_multiple def test_get_pipeline headers, headers2 = nil, nil EM.run { - setup_timeout(1) - http = silent { EM::P::HttpClient2.connect "google.com", 80 } + setup_timeout(TIMEOUT) + http = silent { EM::P::HttpClient2.connect "www.google.com", 80 } d = http.get("/") d.callback { headers = d.headers @@ -103,7 +105,7 @@ def test_get_pipeline def test_authheader EM.run { - setup_timeout(1) + setup_timeout(TIMEOUT) EM.start_server '127.0.0.1', @port, TestServer http = silent { EM::P::HttpClient2.connect '127.0.0.1', 18842 } d = http.get :url=>"/", :authorization=>"Basic xxx" @@ -113,11 +115,11 @@ def test_authheader end def test_https_get - omit_unless(EM.ssl?) + omit("No SSL") unless EM.ssl? d = nil EM.run { - setup_timeout(1) - http = silent { EM::P::HttpClient2.connect :host => 'www.google.com', :port => 443, :ssl => true, :version => '1.0' } + setup_timeout(windows? ? 3.5 : 1) + http = silent { EM::P::HttpClient2.connect :host => 'www.google.com', :port => 443, :tls => true } d = http.get "/" d.callback {EM.stop} d.errback {EM.stop} diff --git a/tests/test_pure.rb b/tests/test_pure.rb index 8863a8d10..a41f00100 100644 --- a/tests/test_pure.rb +++ b/tests/test_pure.rb @@ -124,6 +124,7 @@ def unbind end def test_start_tls + omit("No SSL") unless EM.ssl? $client_handshake_completed, $server_handshake_completed = false, false $client_received_data, $server_received_data = nil, nil EM.run do diff --git a/tests/test_resolver.rb b/tests/test_resolver.rb index 58ed5f574..c8a0513b4 100644 --- a/tests/test_resolver.rb +++ b/tests/test_resolver.rb @@ -17,7 +17,7 @@ def test_hosts end def test_a - pend('FIXME: this test is broken on Windows') if windows? + pend('FIXME: this test is broken on Windows') if windows? && RUBY_VERSION < "2.4" EM.run { d = EM::DNS::Resolver.resolve "example.com" @@ -47,7 +47,7 @@ def test_garbage # There isn't a public DNS entry like 'example.com' with an A rrset def test_a_pair - pend('FIXME: this test is broken on Windows') if windows? + pend('FIXME: this test is broken on Windows') if windows? && RUBY_VERSION < "2.4" EM.run { d = EM::DNS::Resolver.resolve "yahoo.com" @@ -76,7 +76,7 @@ def test_localhost end def test_timer_cleanup - pend('FIXME: this test is broken on Windows') if windows? + pend('FIXME: this test is broken on Windows') if windows? && RUBY_VERSION < "2.4" EM.run { d = EM::DNS::Resolver.resolve "example.com" diff --git a/tests/test_ssl_dhparam.rb b/tests/test_ssl_dhparam.rb index 85f52d2f1..760e8967b 100644 --- a/tests/test_ssl_dhparam.rb +++ b/tests/test_ssl_dhparam.rb @@ -45,7 +45,7 @@ def ssl_handshake_completed end def test_no_dhparam - omit_unless(EM.ssl?) + omit("No SSL") unless EM.ssl? omit_if(EM.library_type == :pure_ruby) # DH will work with defaults omit_if(rbx?) @@ -62,7 +62,7 @@ def test_no_dhparam end def test_dhparam - omit_unless(EM.ssl?) + omit("No SSL") unless EM.ssl? omit_if(rbx?) $client_handshake_completed, $server_handshake_completed = false, false diff --git a/tests/test_ssl_ecdh_curve.rb b/tests/test_ssl_ecdh_curve.rb index 8dc167b67..c1885a6a4 100644 --- a/tests/test_ssl_ecdh_curve.rb +++ b/tests/test_ssl_ecdh_curve.rb @@ -1,6 +1,14 @@ require 'em_test_helper' class TestSslEcdhCurve < Test::Unit::TestCase + + if EM.ssl? + require 'openssl' + OPENSSL_GT_1_0 = ((OpenSSL::OPENSSL_VERSION[/ (\d+\.\d+\.\d+)[a-z]/,1] || "0.0") >= "1.1") or + (OpenSSL.const_defined?(:OPENSSL_LIBRARY_VERSION) ? + ((OpenSSL::OPENSSL_LIBRARY_VERSION[/ (\d+\.\d+\.\d+)[a-z]/,1] || "0.0") >= "1.1") : false) + end + module Client def post_init start_tls @@ -40,8 +48,9 @@ def ssl_handshake_completed end def test_no_ecdh_curve - omit_unless(EM.ssl?) + omit("No SSL") unless EM.ssl? omit_if(rbx?) + omit("OpenSSL 1.1.x (and later) auto selects curve") if OPENSSL_GT_1_0 $client_handshake_completed, $server_handshake_completed = false, false @@ -55,7 +64,7 @@ def test_no_ecdh_curve end def test_ecdh_curve - omit_unless(EM.ssl?) + omit("No SSL") unless EM.ssl? omit_if(EM.library_type == :pure_ruby && RUBY_VERSION < "2.3.0") omit_if(rbx?) diff --git a/tests/test_ssl_methods.rb b/tests/test_ssl_methods.rb index c2e5744d9..c48f08eb5 100644 --- a/tests/test_ssl_methods.rb +++ b/tests/test_ssl_methods.rb @@ -32,7 +32,7 @@ def ssl_handshake_completed end def test_ssl_methods - omit_unless(EM.ssl?) + omit("No SSL") unless EM.ssl? omit_if(rbx?) $server_called_back, $client_called_back = false, false $server_cert_value, $client_cert_value = nil, nil diff --git a/tests/test_ssl_protocols.rb b/tests/test_ssl_protocols.rb index 966f56f1b..1f693e075 100644 --- a/tests/test_ssl_protocols.rb +++ b/tests/test_ssl_protocols.rb @@ -6,8 +6,13 @@ if EM.ssl? class TestSslProtocols < Test::Unit::TestCase - # equal to base METHODS, downcased, like ["tlsv1, "tlsv1_1", "tlsv1_2"] - SSL_AVAIL = ::OpenSSL::SSL::SSLContext::METHODS.select { |i| i =~ /[^\d]\d\z/ }.map { |i| i.to_s.downcase } + if ::OpenSSL::VERSION >= "2.1" + # Assume no SSLv3 in OpenSSL, OpenSSL::SSL::SSLContext::METHODS deprecated + SSL_AVAIL = ["tlsv1", "tlsv1_1", "tlsv1_2"] + else + # Equal to base METHODS, downcased, like ["tlsv1, "tlsv1_1", "tlsv1_2"] + SSL_AVAIL = ::OpenSSL::SSL::SSLContext::METHODS.select { |i| i =~ /[^\d]\d\z/ }.map { |i| i.to_s.downcase } + end libr_vers = OpenSSL.const_defined?(:OPENSSL_LIBRARY_VERSION) ? OpenSSL::OPENSSL_VERSION : 'na' diff --git a/tests/test_ssl_verify.rb b/tests/test_ssl_verify.rb index a6d8fca77..721be0719 100644 --- a/tests/test_ssl_verify.rb +++ b/tests/test_ssl_verify.rb @@ -82,7 +82,7 @@ def ssl_handshake_completed end def test_fail_no_peer_cert - omit_unless(EM.ssl?) + omit("No SSL") unless EM.ssl? omit_if(rbx?) $client_handshake_completed, $server_handshake_completed = false, false @@ -97,7 +97,7 @@ def test_fail_no_peer_cert end def test_accept_server - omit_unless(EM.ssl?) + omit("No SSL") unless EM.ssl? omit_if(EM.library_type == :pure_ruby) # Server has a default cert chain omit_if(rbx?) $client_handshake_completed, $server_handshake_completed = false, false @@ -112,7 +112,7 @@ def test_accept_server end def test_deny_server - omit_unless(EM.ssl?) + omit("No SSL") unless EM.ssl? omit_if(EM.library_type == :pure_ruby) # Server has a default cert chain omit_if(rbx?) $client_handshake_completed, $server_handshake_completed = false, false From de23d368dd753e3a9f4873709bfec8e934397d14 Mon Sep 17 00:00:00 2001 From: MSP-Greg Date: Sun, 24 Dec 2017 13:39:44 -0600 Subject: [PATCH 191/343] Clear cache when appveyor.yml changes --- appveyor.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 04a4597a2..730f1b2d2 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -27,7 +27,6 @@ install: ) - ruby --version - gem --version - - gem install bundler --quiet --no-ri --no-rdoc --no-document - bundler --version - bundle install --without documentation --path vendor/%ruby_version%/bundle build_script: @@ -58,4 +57,4 @@ environment: - ruby_version: "25-x64" b_config: "--use-system-libraries" cache: - - vendor + - vendor -> appveyor.yml From 19adf1ccb95e9e125e568c8db76db4c85f721e89 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Sat, 28 Apr 2018 17:29:05 -0700 Subject: [PATCH 192/343] Skip keepalive tests on Windows These all bomb out with "An operation was attempted on something that is not a socket" and it's not clear if the implementation for Windows is incorrect or if the tests are incorrect. --- tests/test_keepalive.rb | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/test_keepalive.rb b/tests/test_keepalive.rb index 89bce2722..17c8cffad 100644 --- a/tests/test_keepalive.rb +++ b/tests/test_keepalive.rb @@ -14,6 +14,9 @@ def teardown def test_enable_keepalive omit_if(!EM.respond_to?(:get_sock_opt)) + # I don't know why "An operation was attempted on something that is not a socket." + pend('FIXME: this test is broken on Windows') if windows? + val = nil test_module = Module.new do define_method :post_init do @@ -36,6 +39,9 @@ def test_enable_keepalive def test_enable_keepalive_values omit_if(!EM.respond_to?(:get_sock_opt)) + # I don't know why "An operation was attempted on something that is not a socket." + pend('FIXME: this test is broken on Windows') if windows? + val, val_idle, val_intvl, val_cnt = nil test_module = Module.new do define_method :post_init do @@ -85,6 +91,9 @@ def test_enable_keepalive_values def test_disable_keepalive omit_if(!EM.respond_to?(:get_sock_opt)) + # I don't know why "An operation was attempted on something that is not a socket." + pend('FIXME: this test is broken on Windows') if windows? + val = nil test_module = Module.new do define_method :post_init do From b430a52b1e5166d1d1f5d388cb1f92a4f15b7bf1 Mon Sep 17 00:00:00 2001 From: Genki Takiuchi Date: Sun, 29 Apr 2018 15:55:25 +0900 Subject: [PATCH 193/343] EM::Connection#watch_only? (#762) Exposes the internal IsWatchOnly value, so a caller iterating over all of the open connections can determine whether to close the connection using `close` or using `stop_watching`. --- ext/cmain.cpp | 13 +++++++++++++ ext/eventmachine.h | 1 + ext/rubymain.cpp | 11 +++++++++++ lib/em/connection.rb | 5 +++++ tests/test_process_watch.rb | 1 + 5 files changed, 31 insertions(+) diff --git a/ext/cmain.cpp b/ext/cmain.cpp index d23dd9bc5..068a69a43 100644 --- a/ext/cmain.cpp +++ b/ext/cmain.cpp @@ -369,6 +369,19 @@ extern "C" void evma_unwatch_pid (const uintptr_t sig) EventMachine->UnwatchPid(sig); } +/***************** +evma_is_watch_only +*****************/ + +extern "C" int evma_is_watch_only (const uintptr_t binding) +{ + EventableDescriptor *cd = dynamic_cast (Bindable_t::GetObject (binding)); + if (cd) + return cd->IsWatchOnly() ? 1 : 0; + + return 0; +} + /**************************** evma_send_data_to_connection ****************************/ diff --git a/ext/eventmachine.h b/ext/eventmachine.h index d8a5726c9..2861276bd 100644 --- a/ext/eventmachine.h +++ b/ext/eventmachine.h @@ -126,6 +126,7 @@ extern "C" { const uintptr_t evma_watch_pid (int); void evma_unwatch_pid (const uintptr_t binding); + int evma_is_watch_only(const uintptr_t binding); void evma_start_proxy(const uintptr_t from, const uintptr_t to, const unsigned long bufsize, const unsigned long length); void evma_stop_proxy(const uintptr_t from); diff --git a/ext/rubymain.cpp b/ext/rubymain.cpp index b96c39dc8..c5ea628e2 100644 --- a/ext/rubymain.cpp +++ b/ext/rubymain.cpp @@ -1081,6 +1081,16 @@ static VALUE t_unwatch_pid (VALUE self UNUSED, VALUE sig) } +/************* +t_watch_only_p +*************/ + +static VALUE t_watch_only_p (VALUE self UNUSED, VALUE signature) +{ + return evma_is_watch_only(NUM2BSIG (signature)) ? Qtrue : Qfalse; +} + + /********** t__epoll_p **********/ @@ -1490,6 +1500,7 @@ extern "C" void Init_rubyeventmachine() rb_define_module_function (EmModule, "watch_pid", (VALUE (*)(...))t_watch_pid, 1); rb_define_module_function (EmModule, "unwatch_pid", (VALUE (*)(...))t_unwatch_pid, 1); + rb_define_module_function (EmModule, "watch_only?", (VALUE (*)(...))t_watch_only_p, 1); rb_define_module_function (EmModule, "current_time", (VALUE(*)(...))t_get_loop_time, 0); diff --git a/lib/em/connection.rb b/lib/em/connection.rb index 267aec2d0..7d45e92fc 100644 --- a/lib/em/connection.rb +++ b/lib/em/connection.rb @@ -766,5 +766,10 @@ def resume def paused? EventMachine::connection_paused? @signature end + + # @return [Boolean] true if the connect was watch only + def watch_only? + EventMachine::watch_only? @signature + end end end diff --git a/tests/test_process_watch.rb b/tests/test_process_watch.rb index 6e0c49a75..386a5ceb9 100644 --- a/tests/test_process_watch.rb +++ b/tests/test_process_watch.rb @@ -34,6 +34,7 @@ def test_events EM.watch_process(Process.pid, ParentProcessWatcher) $fork_pid = fork{ sleep } child = EM.watch_process($fork_pid, ChildProcessWatcher) + assert_equal(child.watch_only?, true) $pid = child.pid EM.add_timer(0.2){ From 168e406c0eacfb65903df3ce7c0efbd14ab899d4 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Sun, 29 Apr 2018 23:38:58 -0700 Subject: [PATCH 194/343] Tests for watch_only? --- ext/cmain.cpp | 3 +-- tests/test_attach.rb | 48 +++++++++++++++++++++++++++++++++++++ tests/test_process_watch.rb | 1 - 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/ext/cmain.cpp b/ext/cmain.cpp index 068a69a43..7ae0c5a88 100644 --- a/ext/cmain.cpp +++ b/ext/cmain.cpp @@ -378,8 +378,7 @@ extern "C" int evma_is_watch_only (const uintptr_t binding) EventableDescriptor *cd = dynamic_cast (Bindable_t::GetObject (binding)); if (cd) return cd->IsWatchOnly() ? 1 : 0; - - return 0; + return -1; } /**************************** diff --git a/tests/test_attach.rb b/tests/test_attach.rb index 4a55017c9..4d066751a 100644 --- a/tests/test_attach.rb +++ b/tests/test_attach.rb @@ -148,4 +148,52 @@ def test_read_write_pipe ensure [r,w].each {|io| io.close rescue nil } end + + # This test shows that watch_only? is true for EM.watch + def test_watch_only + r, w = IO.pipe + $watch_only = nil + + EM.run do + EM.watch r do |c| + assert_true(c.watch_only?) + c.notify_readable = true + def c.receive_data data + fail('this method should not be called') + end + def c.notify_readable + $watch_only = watch_only? + end + end + w.write 'hello' + EM.next_tick { EM.stop } + end + + assert_true($watch_only) + end + + # This test shows that watch_only? is false for EM.attach + def test_attach_data + r, w = IO.pipe + $watch_only = nil + $read = [] + + EM.run do + EM.attach r do |c| + assert_false(c.watch_only?) + def c.receive_data data + $watch_only = watch_only? + $read << data + end + def c.notify_readable + fail('this method should not be called') + end + end + w.write 'world' + EM.next_tick { EM.stop } + end + + assert_false($watch_only) + assert_equal('world', $read.first) + end end diff --git a/tests/test_process_watch.rb b/tests/test_process_watch.rb index 386a5ceb9..6e0c49a75 100644 --- a/tests/test_process_watch.rb +++ b/tests/test_process_watch.rb @@ -34,7 +34,6 @@ def test_events EM.watch_process(Process.pid, ParentProcessWatcher) $fork_pid = fork{ sleep } child = EM.watch_process($fork_pid, ChildProcessWatcher) - assert_equal(child.watch_only?, true) $pid = child.pid EM.add_timer(0.2){ From 7feb4c533a7f7c4cd535ed9727094155b3b76ed6 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Mon, 30 Apr 2018 01:03:26 -0700 Subject: [PATCH 195/343] SmtpServer: support multiple messages per one connection Closes #288 and #628 --- lib/em/protocols/smtpserver.rb | 2 +- tests/test_smtpserver.rb | 41 ++++++++++++++++++++++++++++++---- 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/lib/em/protocols/smtpserver.rb b/lib/em/protocols/smtpserver.rb index e0f1f05d9..1d0575f99 100644 --- a/lib/em/protocols/smtpserver.rb +++ b/lib/em/protocols/smtpserver.rb @@ -564,7 +564,7 @@ def process_data_line ln (d ? succeeded : failed).call end - @state.delete :data + @state -= [:data, :mail_from, :rcpt] else # slice off leading . if any ln.slice!(0...1) if ln[0] == ?. diff --git a/tests/test_smtpserver.rb b/tests/test_smtpserver.rb index 18c50febd..75518dbaf 100644 --- a/tests/test_smtpserver.rb +++ b/tests/test_smtpserver.rb @@ -1,3 +1,4 @@ +require 'net/smtp' require 'em_test_helper' class TestSmtpServer < Test::Unit::TestCase @@ -13,34 +14,51 @@ class TestSmtpServer < Test::Unit::TestCase # class Mailserver < EM::Protocols::SmtpServer - attr_reader :my_msg_body, :my_sender, :my_recipients + attr_reader :my_msg_body, :my_sender, :my_recipients, :messages_count def initialize *args super end + def receive_sender sender @my_sender = sender #p sender true end + def receive_recipient rcpt @my_recipients ||= [] @my_recipients << rcpt true end + def receive_data_chunk c @my_msg_body = c.last end + + def receive_message + @messages_count ||= 0 + @messages_count += 1 + true + end + def connection_ended EM.stop end end - def test_mail + def run_server c = nil EM.run { EM.start_server( Localhost, Localport, Mailserver ) {|conn| c = conn} EM::Timer.new(2) {EM.stop} # prevent hanging the test suite in case of error + yield if block_given? + } + c + end + + def test_mail + c = run_server do EM::Protocols::SmtpClient.send :host=>Localhost, :port=>Localport, :domain=>"bogus", @@ -48,10 +66,25 @@ def test_mail :to=>"you@example.com", :header=> {"Subject"=>"Email subject line", "Reply-to"=>"me@example.com"}, :body=>"Not much of interest here." - - } + end assert_equal( "Not much of interest here.", c.my_msg_body ) assert_equal( "", c.my_sender ) assert_equal( [""], c.my_recipients ) end + + + + def test_multiple_messages_per_connection + c = run_server do + Thread.new do + Net::SMTP.start( Localhost, Localport, Localhost ) do |smtp| + 2.times do + smtp.send_message "This is a test e-mail message.", 'me@fromdomain.com', 'test@todomain.com' + end + end + end + end + + assert_equal( 2, c.messages_count ) + end end From c328010b1925818af6a7120f55f2089584d9604b Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Mon, 30 Apr 2018 02:42:25 -0700 Subject: [PATCH 196/343] Changelog for v1.2.6 --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 962574d25..043fc90d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## 1.2.6 (April 30, 2018) +* *Fix segfault when an Exception is raised from unbind callback (for real this time!)* +* Fix race condition while initializing the machine [#756] +* Fix for newer compilers where bind() and std::bind() conflict [#830, #831] +* Be verbose about SSL connection errors [#807] +* Avoid explicitly calling class methods when in class scope +* Java: Add EM_PROTO_SSL/TLS definitions [#773, #791] +* Java: return zero when sending data to a closed connection [#475, #804] +* Pure Ruby: Connection::error? calls report_connection_error_status [#801] + ## 1.2.5 (July 27, 2017) * Java: Use long for larger values in oneshot timer intervals [#784, #794] From c562189469f2f4c266eceffc773c8e3ef1c4afce Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Sun, 6 May 2018 00:12:30 -0700 Subject: [PATCH 197/343] Fix crashes on large numbers of connections (#843) All three fd_set arguments to select must be large enough to modify according to the length given in the first argument to select. This can be done by calling rb_fd_resize on each of the fd_set arguments, which happens to be exactly what rb_fd_select does! --- ext/em.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/em.cpp b/ext/em.cpp index f2e28b114..3c1f29af6 100644 --- a/ext/em.cpp +++ b/ext/em.cpp @@ -912,7 +912,7 @@ _SelectDataSelect static VALUE _SelectDataSelect (void *v) { SelectData_t *sd = (SelectData_t*)v; - sd->nSockets = select (sd->maxsocket+1, rb_fd_ptr(&(sd->fdreads)), rb_fd_ptr(&(sd->fdwrites)), rb_fd_ptr(&(sd->fderrors)), &(sd->tv)); + sd->nSockets = rb_fd_select (sd->maxsocket+1, &(sd->fdreads), &(sd->fdwrites), &(sd->fderrors), &(sd->tv)); return Qnil; } #endif From 033244a29a327dcd11cc2aab467284b5fdfa1f47 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Sat, 12 May 2018 06:10:33 -0700 Subject: [PATCH 198/343] Changelog for v1.2.7 --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 043fc90d1..cd42c39fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Changelog +## 1.2.7 (May 12, 2018) +* Fix segfault on large numbers of connections [#843] + ## 1.2.6 (April 30, 2018) * *Fix segfault when an Exception is raised from unbind callback (for real this time!)* * Fix race condition while initializing the machine [#756] From 537127fa22a058e3d7b248a214df210e7c77dc95 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Wed, 30 May 2018 04:27:01 -0700 Subject: [PATCH 199/343] Bump version to 1.3.0.dev.1 --- lib/em/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/em/version.rb b/lib/em/version.rb index 353a589cb..b5e0f2592 100644 --- a/lib/em/version.rb +++ b/lib/em/version.rb @@ -1,3 +1,3 @@ module EventMachine - VERSION = "1.2.5" + VERSION = "1.3.0.dev.1" end From 33eb5032d0e7b0bac2cb1c1cc13d988ce78ac76a Mon Sep 17 00:00:00 2001 From: MSP-Greg Date: Sat, 23 Jun 2018 19:33:52 -0500 Subject: [PATCH 200/343] Appveyor - Create Windows pre-compiled gems and test --- .gitignore | 9 +++++ appveyor.yml | 71 ++++++++--------------------------- win_gem_test/Rakefile_wintest | 12 ++++++ win_gem_test/eventmachine.ps1 | 60 +++++++++++++++++++++++++++++ win_gem_test/package_gem.rb | 20 ++++++++++ 5 files changed, 117 insertions(+), 55 deletions(-) create mode 100644 win_gem_test/Rakefile_wintest create mode 100644 win_gem_test/eventmachine.ps1 create mode 100644 win_gem_test/package_gem.rb diff --git a/.gitignore b/.gitignore index bc7c25d1b..22049a633 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,12 @@ Gemfile.lock .yardoc/* doc/* tmp/* + +# windows local build artifacts +/win_gem_test/shared/ +/win_gem_test/packages/ +/win_gem_test/test_logs/ +/Rakefile_wintest +*.gem +/lib/fastfilereaderext.rb +/lib/rubyeventmachine.rb diff --git a/appveyor.yml b/appveyor.yml index 730f1b2d2..50935a51f 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,60 +1,21 @@ --- -version: "{build}" -clone_depth: 10 -init: - - set PATH=C:\ruby%ruby_version%\bin;C:\Program Files\7-Zip;C:\Program Files\AppVeyor\BuildAgent;C:\Program Files\Git\cmd;C:\Windows\system32 - - if %ruby_version%==_trunk ( - appveyor DownloadFile https://ci.appveyor.com/api/projects/MSP-Greg/ruby-loco/artifacts/ruby_trunk.7z -FileName C:\ruby_trunk.7z & - 7z x C:\ruby_trunk.7z -oC:\ruby_trunk & - C:\ruby_trunk\trunk_pkgs.cmd - ) install: - # RI DevKit is only installed in Ruby23 and Ruby23-x64 folders - # need to install OpenSSL since standard DevKit doesn't contain it - - if "%ri_file%"=="x86" ( - appveyor DownloadFile https://dl.bintray.com/oneclick/OpenKnapsack/x86/openssl-1.0.2j-x86-windows.tar.lzma & - 7z e openssl-1.0.2j-x86-windows.tar.lzma & - 7z x -y openssl-1.0.2j-x86-windows.tar -oC:\ruby23\DevKit\mingw & - set b_config="--with-ssl-dir=C:/ruby23/DevKit/mingw --with-opt-include=C:/ruby23/DevKit/mingw/include" & - set SSL_CERT_FILE=C:/ruby24-x64/ssl/cert.pem - ) - - if "%ri_file%"=="x64" ( - appveyor DownloadFile https://dl.bintray.com/oneclick/OpenKnapsack/x64/openssl-1.0.2j-x64-windows.tar.lzma & - 7z e openssl-1.0.2j-x64-windows.tar.lzma & - 7z x -y openssl-1.0.2j-x64-windows.tar -oC:\ruby23-x64\DevKit\mingw & - set b_config="--with-ssl-dir=C:/ruby23-x64/DevKit/mingw --with-opt-include=C:/ruby23-x64/DevKit/mingw/include" & - set SSL_CERT_FILE=C:/ruby24-x64/ssl/cert.pem - ) - - ruby --version - - gem --version - - bundler --version - - bundle install --without documentation --path vendor/%ruby_version%/bundle + # download shared script files + - ps: >- + if ( !(Test-Path -Path ./shared -PathType Container) ) { + $uri = 'https://ci.appveyor.com/api/projects/MSP-Greg/av-gem-build-test/artifacts/shared.7z' + $7z = 'C:\Program Files\7-Zip\7z.exe' + $fn = "$env:TEMP\shared.7z" + (New-Object System.Net.WebClient).DownloadFile($uri, $fn) + &$7z x $fn -owin_gem_test 1> $null + Remove-Item -LiteralPath $fn -Force + Write-Host "Downloaded shared files" -ForegroundColor Yellow + } + build_script: - - bundle config build.eventmachine --no-document --env-shebang %b_config% - - bundle exec rake -rdevkit compile -- %b_config% -test_script: - - bundle exec rake -rdevkit test -on_finish: - - ruby -v + - ps: .\win_gem_test\eventmachine.ps1 $env:gem_bits + environment: - TESTOPTS: -v --no-show-detail-immediately - ri_file: none matrix: - - ruby_version: "200" - - ruby_version: "200-x64" - - ruby_version: "21" - - ruby_version: "21-x64" - - ruby_version: "22" - ri_file: x86 - - ruby_version: "22-x64" - ri_file: x64 - - ruby_version: "23" - ri_file: x86 - - ruby_version: "23-x64" - ri_file: x64 - - ruby_version: "24-x64" - b_config: "--use-system-libraries" - - ruby_version: "25-x64" - b_config: "--use-system-libraries" -cache: - - vendor -> appveyor.yml + - gem_bits: 64 + - gem_bits: 32 diff --git a/win_gem_test/Rakefile_wintest b/win_gem_test/Rakefile_wintest new file mode 100644 index 000000000..afc1704d5 --- /dev/null +++ b/win_gem_test/Rakefile_wintest @@ -0,0 +1,12 @@ +# rake -f Rakefile_wintest -N -R norakelib + +require "rake/testtask" + +Rake::TestTask.new(:win_test) do |t| + t.libs << "tests" + t.test_files = FileList['tests/**/test_*.rb'] + t.warning = true + t.options = '--verbose --no-show-detail-immediately' +end + +task :default => [:win_test] diff --git a/win_gem_test/eventmachine.ps1 b/win_gem_test/eventmachine.ps1 new file mode 100644 index 000000000..4d83255d8 --- /dev/null +++ b/win_gem_test/eventmachine.ps1 @@ -0,0 +1,60 @@ +# PowerShell script for building & testing SQLite3-Ruby fat binary gem +# Code by MSP-Greg, see https://github.com/MSP-Greg/av-gem-build-test + +# load utility functions, pass 64 or 32 +. $PSScriptRoot\shared\appveyor_setup.ps1 $args[0] +if ($LastExitCode) { exit } + +# above is required code +#———————————————————————————————————————————————————————————————— above for all repos + +Make-Const gem_name 'eventmachine' +Make-Const repo_name 'eventmachine' +Make-Const url_repo 'https://github.com/eventmachine/eventmachine.git' + +#———————————————————————————————————————————————————————————————— lowest ruby version +Make-Const ruby_vers_low 20 +# null = don't compile; false = compile, ignore test (allow failure); +# true = compile & test +Make-Const trunk $false ; Make-Const trunk_x64 $false +Make-Const trunk_JIT $null ; Make-Const trunk_x64_JIT $null + +#———————————————————————————————————————————————————————————————— make info +Make-Const dest_so 'lib' +Make-Const exts @( + @{ 'conf' = 'ext/extconf.rb' ; 'so' = 'rubyeventmachine' }, + @{ 'conf' = 'ext/fastfilereader/extconf.rb' ; 'so' = 'fastfilereaderext' } +) +Make-Const write_so_require $true + +# $msys_full = $true # Uncomment for full msys2 update + +#———————————————————————————————————————————————————————————————— pre compile +# runs before compiling starts on every ruby version +function Pre-Compile { + Check-OpenSSL + Write-Host Compiling With $env:SSL_VERS +} + +#———————————————————————————————————————————————————————————————— Run-Tests +function Run-Tests { + # call with comma separated list of gems to install or update + Update-Gems rake, test-unit + rake -f Rakefile_wintest -N -R norakelib | Set-Content -Path $log_name -PassThru -Encoding UTF8 + # add info after test results + $(ruby -ropenssl -e "STDOUT.puts $/ + OpenSSL::OPENSSL_LIBRARY_VERSION") | + Add-Content -Path $log_name -PassThru -Encoding UTF8 + test_unit +} + +#———————————————————————————————————————————————————————————————— below for all repos +# below is required code + +Make-Const dir_gem $(Convert-Path $PSScriptRoot\..) +Make-Const dir_ps $PSScriptRoot + +Push-Location $PSScriptRoot +.\shared\make.ps1 +.\shared\test.ps1 +Pop-Location +exit $ttl_errors_fails + $exit_code diff --git a/win_gem_test/package_gem.rb b/win_gem_test/package_gem.rb new file mode 100644 index 000000000..3ee35fa8c --- /dev/null +++ b/win_gem_test/package_gem.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +require 'rubygems' +require 'rubygems/package' + +spec = Gem::Specification.load("./eventmachine.gemspec") + +spec.files.concat ['Rakefile_wintest', 'lib/fastfilereaderext.rb', 'lib/rubyeventmachine.rb'] +spec.files.concat Dir['lib/**/*.so'] + +# below lines are required and not gem specific +spec.platform = ARGV[0] +spec.required_ruby_version = [">= #{ARGV[1]}", "< #{ARGV[2]}"] +spec.extensions = [] +if spec.respond_to?(:metadata=) + spec.metadata.delete("msys2_mingw_dependencies") + spec.metadata['commit'] = ENV['commit_info'] +end + +Gem::Package.build(spec) From 8b80e34b02d3aebf94cff4e344b0eb31e9590976 Mon Sep 17 00:00:00 2001 From: MSP-Greg Date: Tue, 31 Jul 2018 17:05:38 -0500 Subject: [PATCH 201/343] Update Tests for Windows --- tests/em_test_helper.rb | 2 +- tests/test_attach.rb | 1 + tests/test_basic.rb | 2 ++ tests/test_file_watch.rb | 1 + tests/test_httpclient2.rb | 2 +- tests/test_ipv4.rb | 3 ++- tests/test_ssl_protocols.rb | 2 +- 7 files changed, 9 insertions(+), 4 deletions(-) diff --git a/tests/em_test_helper.rb b/tests/em_test_helper.rb index b313e8bac..eb3224820 100644 --- a/tests/em_test_helper.rb +++ b/tests/em_test_helper.rb @@ -4,7 +4,7 @@ require 'rbconfig' require 'socket' -puts "EM Library Type: #{EM.library_type}" +puts "\nEM.library_type #{EM.library_type.to_s.ljust(12)} EM.ssl? #{EM.ssl?}" class Test::Unit::TestCase class EMTestTimeout < StandardError ; end diff --git a/tests/test_attach.rb b/tests/test_attach.rb index 4d066751a..821aae5cc 100644 --- a/tests/test_attach.rb +++ b/tests/test_attach.rb @@ -174,6 +174,7 @@ def c.notify_readable # This test shows that watch_only? is false for EM.attach def test_attach_data + pend("\nFIXME: Freezes Windows testing as of 2018-07-31") if windows? r, w = IO.pipe $watch_only = nil $read = [] diff --git a/tests/test_basic.rb b/tests/test_basic.rb index 977094653..a93763cbe 100644 --- a/tests/test_basic.rb +++ b/tests/test_basic.rb @@ -174,6 +174,7 @@ def test_bind_connect end def test_invalid_address_bind_connect_dst + pend("\nFIXME: Windows as of 2018-06-23 on 32 bit >= 2.4 (#{RUBY_VERSION} #{RUBY_PLATFORM})") if RUBY_PLATFORM[/i386-mingw/] && RUBY_VERSION >= '2.4' e = nil EM.run do begin @@ -190,6 +191,7 @@ def test_invalid_address_bind_connect_dst end def test_invalid_address_bind_connect_src + pend("\nFIXME: Windows as of 2018-06-23 on 32 bit >= 2.4 (#{RUBY_VERSION} #{RUBY_PLATFORM})") if RUBY_PLATFORM[/i386-mingw/] && RUBY_VERSION >= '2.4' e = nil EM.run do begin diff --git a/tests/test_file_watch.rb b/tests/test_file_watch.rb index 86eb14288..326b5ec13 100644 --- a/tests/test_file_watch.rb +++ b/tests/test_file_watch.rb @@ -4,6 +4,7 @@ class TestFileWatch < Test::Unit::TestCase if windows? def test_watch_file_raises_unsupported_error + pend("\nFIXME: Windows as of 2018-06-23 on 32 bit >= 2.4 (#{RUBY_VERSION} #{RUBY_PLATFORM})") if RUBY_PLATFORM[/i386-mingw/] && RUBY_VERSION >= '2.4' assert_raises(EM::Unsupported) do EM.run do file = Tempfile.new("fake_file") diff --git a/tests/test_httpclient2.rb b/tests/test_httpclient2.rb index 9a3d242f3..648529626 100644 --- a/tests/test_httpclient2.rb +++ b/tests/test_httpclient2.rb @@ -118,7 +118,7 @@ def test_https_get omit("No SSL") unless EM.ssl? d = nil EM.run { - setup_timeout(windows? ? 3.5 : 1) + setup_timeout(windows? ? 6 : 1) http = silent { EM::P::HttpClient2.connect :host => 'www.google.com', :port => 443, :tls => true } d = http.get "/" d.callback {EM.stop} diff --git a/tests/test_ipv4.rb b/tests/test_ipv4.rb index bd11bbfe3..b68d92748 100644 --- a/tests/test_ipv4.rb +++ b/tests/test_ipv4.rb @@ -55,7 +55,8 @@ def s.receive_data data # EM::ConnectionError. def test_tcp_connect_to_invalid_ipv4 omit_if(!Test::Unit::TestCase.public_ipv4?) - + pend("\nFIXME: Windows as of 2018-06-23 on 32 bit >= 2.4 (#{RUBY_VERSION} #{RUBY_PLATFORM})") if RUBY_PLATFORM[/i386-mingw/] && RUBY_VERSION >= '2.4' + invalid_ipv4 = "9.9:9" EM.run do diff --git a/tests/test_ssl_protocols.rb b/tests/test_ssl_protocols.rb index 1f693e075..d338c35d5 100644 --- a/tests/test_ssl_protocols.rb +++ b/tests/test_ssl_protocols.rb @@ -19,7 +19,7 @@ class TestSslProtocols < Test::Unit::TestCase puts "OPENSSL_LIBRARY_VERSION: #{libr_vers}\n" \ " OPENSSL_VERSION: #{OpenSSL::OPENSSL_VERSION}\n" \ - " SSL_AVAIL: #{SSL_AVAIL.join(' ')}" + " SSL_AVAIL: #{SSL_AVAIL.sort.join(' ')}" module Client def ssl_handshake_completed From ed4800b541c3c97b51a6b25bfbc90c5472d6c003 Mon Sep 17 00:00:00 2001 From: Steve Sloan Date: Thu, 9 Aug 2018 11:46:50 -0700 Subject: [PATCH 202/343] Added support for streaming IO objects (#855) --- lib/em/io_streamer.rb | 68 +++++++++++++++++++++++++++++++++++++++ tests/test_io_streamer.rb | 36 +++++++++++++++++++++ 2 files changed, 104 insertions(+) create mode 100644 lib/em/io_streamer.rb create mode 100644 tests/test_io_streamer.rb diff --git a/lib/em/io_streamer.rb b/lib/em/io_streamer.rb new file mode 100644 index 000000000..e957fcffc --- /dev/null +++ b/lib/em/io_streamer.rb @@ -0,0 +1,68 @@ +require 'em/streamer' + +# Streams an IO object over a given connection. Streaming begins once the object is +# instantiated. Typically IOStreamer instances are not reused. IOStreamer includes +# a 16K buffer. If streaming from a file, FileStreamer is more efficient. +# +# @example +# +# module FileSender +# def post_init +# @example +# +# module SocketSender +# socket = TCPSocket.new('localhost', 2000) +# streamer = EventMachine::IOStreamer.new(self, socket) +# streamer.callback{ +# # all data was sent successfully +# close_connection_after_writing +# } +# end +# end + +# Stream from any IO object, similar to FileStreamer +module EventMachine + class IOStreamer + include Deferrable + CHUNK_SIZE = 16384 + + # @param [EventMachine::Connection] connection + # @param [IO] io Data source + # + # @option opts [Boolean] :http_chunks (false) Use HTTP 1.1 style chunked-encoding semantics. + def initialize(connection, io, opts = {}) + @connection = connection + @io = io + @http_chunks = opts[:http_chunks] + + @buff = String.new + @io.binmode if @io.respond_to?(:binmode) + stream_one_chunk + end + + private + + # Used internally to stream one chunk at a time over multiple reactor ticks + # @private + def stream_one_chunk + loop do + if @io.eof? + @connection.send_data "0\r\n\r\n" if @http_chunks + succeed + break + end + + if @connection.respond_to?(:get_outbound_data_size) && (@connection.get_outbound_data_size > FileStreamer::BackpressureLevel) + EventMachine::next_tick { stream_one_chunk } + break + end + + if @io.read(CHUNK_SIZE, @buff) + @connection.send_data("#{@buff.length.to_s(16)}\r\n") if @http_chunks + @connection.send_data(@buff) + @connection.send_data("\r\n") if @http_chunks + end + end + end + end +end diff --git a/tests/test_io_streamer.rb b/tests/test_io_streamer.rb new file mode 100644 index 000000000..0a8c0e437 --- /dev/null +++ b/tests/test_io_streamer.rb @@ -0,0 +1,36 @@ +require 'em_test_helper' +require 'em/io_streamer' + +EM::IOStreamer::CHUNK_SIZE = 2 + +class TestIOStreamer < Test::Unit::TestCase + class StreamServer < EM::Connection + TEST_STRING = 'this is a test'.freeze + def post_init + io = StringIO.new(TEST_STRING) + EM::IOStreamer.new(self, io).callback { close_connection_after_writing } + end + end + + class StreamClient < EM::Connection + def initialize(received) + @received = received + end + def receive_data data + @received << data + end + def unbind + EM.stop + end + end + + def test_io_stream + received = '' + EM.run do + port = next_port + EM.start_server '127.0.0.1', port, StreamServer + EM.connect '127.0.0.1', port, StreamClient, received + end + assert_equal(StreamServer::TEST_STRING, received) + end +end From 0d31d12ba337eeec376f94745ba2ba6b36a788e5 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Sat, 5 May 2018 23:28:42 -0700 Subject: [PATCH 203/343] Remove compatibility shims for Ruby 1.8.x and Ruby 1.9.x Also fixes longstanding Mac OS X crash bug by using rb_thread_fd_select in the SelectData_t::_Select() method, because on Mac OS all three arguments to select() must be large enough to modify 'n' sockets; rb_fd_select and rb_thread_fd_select do exactly this. --- ext/em.cpp | 93 ++++++++++++++++++++++-------------------------------- ext/em.h | 73 +++--------------------------------------- 2 files changed, 41 insertions(+), 125 deletions(-) diff --git a/ext/em.cpp b/ext/em.cpp index 3c1f29af6..11eea3056 100644 --- a/ext/em.cpp +++ b/ext/em.cpp @@ -599,6 +599,21 @@ bool EventMachine_t::RunOnce() } +#ifdef HAVE_EPOLL +typedef struct { + int epfd; + struct epoll_event *events; + int maxevents; + int timeout; +} epoll_args_t; + +static void *nogvl_epoll_wait(void *args) +{ + epoll_args_t *a = (epoll_args_t *)args; + return (void *) (uintptr_t) epoll_wait (a->epfd, a->events, a->maxevents, a->timeout); +} +#endif + /***************************** EventMachine_t::_RunEpollOnce *****************************/ @@ -614,16 +629,7 @@ void EventMachine_t::_RunEpollOnce() #ifdef BUILD_FOR_RUBY int ret = 0; - #ifdef HAVE_RB_WAIT_FOR_SINGLE_FD if ((ret = rb_wait_for_single_fd(epfd, RB_WAITFD_IN|RB_WAITFD_PRI, &tv)) < 1) { - #else - fd_set fdreads; - - FD_ZERO(&fdreads); - FD_SET(epfd, &fdreads); - - if ((ret = rb_thread_select(epfd + 1, &fdreads, NULL, NULL, &tv)) < 1) { - #endif if (ret == -1) { assert(errno != EINVAL); assert(errno != EBADF); @@ -631,9 +637,8 @@ void EventMachine_t::_RunEpollOnce() return; } - TRAP_BEG; - s = epoll_wait (epfd, epoll_events, MaxEvents, 0); - TRAP_END; + epoll_args_t epoll_args = { epfd, epoll_events, MaxEvents, 0 }; + s = (uintptr_t) rb_thread_call_without_gvl (nogvl_epoll_wait, &epoll_args, RUBY_UBF_IO, 0); #else int duration = 0; duration = duration + (tv.tv_sec * 1000); @@ -672,6 +677,23 @@ void EventMachine_t::_RunEpollOnce() } +#ifdef HAVE_KQUEUE +typedef struct { + int kqfd; + const struct kevent *changelist; + int nchanges; + struct kevent *eventlist; + int nevents; + const struct timespec *timeout; +} kevent_args_t; + +static void *nogvl_kevent(void *args) +{ + kevent_args_t *a = (kevent_args_t *)args; + return (void *) (uintptr_t) kevent (a->kqfd, a->changelist, a->nchanges, a->eventlist, a->nevents, a->timeout); +} +#endif + /****************************** EventMachine_t::_RunKqueueOnce ******************************/ @@ -691,16 +713,7 @@ void EventMachine_t::_RunKqueueOnce() #ifdef BUILD_FOR_RUBY int ret = 0; - #ifdef HAVE_RB_WAIT_FOR_SINGLE_FD if ((ret = rb_wait_for_single_fd(kqfd, RB_WAITFD_IN|RB_WAITFD_PRI, &tv)) < 1) { - #else - fd_set fdreads; - - FD_ZERO(&fdreads); - FD_SET(kqfd, &fdreads); - - if ((ret = rb_thread_select(kqfd + 1, &fdreads, NULL, NULL, &tv)) < 1) { - #endif if (ret == -1) { assert(errno != EINVAL); assert(errno != EBADF); @@ -708,10 +721,9 @@ void EventMachine_t::_RunKqueueOnce() return; } - TRAP_BEG; ts.tv_sec = ts.tv_nsec = 0; - k = kevent (kqfd, NULL, 0, Karray, MaxEvents, &ts); - TRAP_END; + kevent_args_t kevent_args = { kqfd, NULL, 0, Karray, MaxEvents, &ts }; + k = (uintptr_t) rb_thread_call_without_gvl (nogvl_kevent, &kevent_args, RUBY_UBF_IO, 0); #else k = kevent (kqfd, NULL, 0, Karray, MaxEvents, &ts); #endif @@ -750,7 +762,6 @@ void EventMachine_t::_RunKqueueOnce() ++ke; } - // TODO, replace this with rb_thread_blocking_region for 1.9 builds. #ifdef BUILD_FOR_RUBY if (!rb_thread_alone()) { rb_thread_schedule(); @@ -903,39 +914,14 @@ SelectData_t::~SelectData_t() rb_fd_term (&fderrors); } -#ifdef BUILD_FOR_RUBY -/***************** -_SelectDataSelect -*****************/ - -#if defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL) -static VALUE _SelectDataSelect (void *v) -{ - SelectData_t *sd = (SelectData_t*)v; - sd->nSockets = rb_fd_select (sd->maxsocket+1, &(sd->fdreads), &(sd->fdwrites), &(sd->fderrors), &(sd->tv)); - return Qnil; -} -#endif - /********************* SelectData_t::_Select *********************/ int SelectData_t::_Select() { - #if defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL) - // added in ruby 1.9.3 - rb_thread_call_without_gvl ((void *(*)(void *))_SelectDataSelect, (void*)this, RUBY_UBF_IO, 0); - return nSockets; - #elif defined(HAVE_TBR) - // added in ruby 1.9.1, deprecated in ruby 2.0.0 - rb_thread_blocking_region (_SelectDataSelect, (void*)this, RUBY_UBF_IO, 0); - return nSockets; - #else - return EmSelect (maxsocket+1, &fdreads, &fdwrites, &fderrors, &tv); - #endif + return EmSelect (maxsocket + 1, &fdreads, &fdwrites, &fderrors, &tv); } -#endif void SelectData_t::_Clear() { @@ -1001,13 +987,8 @@ void EventMachine_t::_RunSelectOnce() { // read and write the sockets - //timeval tv = {1, 0}; // Solaris fails if the microseconds member is >= 1000000. - //timeval tv = Quantum; SelectData->tv = _TimeTilNextEvent(); int s = SelectData->_Select(); - //rb_thread_blocking_region(xxx,(void*)&SelectData,RUBY_UBF_IO,0); - //int s = EmSelect (SelectData.maxsocket+1, &(SelectData.fdreads), &(SelectData.fdwrites), NULL, &(SelectData.tv)); - //int s = SelectData.nSockets; if (s > 0) { /* Changed 01Jun07. We used to handle the Loop-breaker right here. * Now we do it AFTER all the regular descriptors. There's an diff --git a/ext/em.h b/ext/em.h index 3b11ea014..ab6411355 100644 --- a/ext/em.h +++ b/ext/em.h @@ -22,78 +22,14 @@ See the file COPYING for complete licensing information. #ifdef BUILD_FOR_RUBY #include - #ifdef HAVE_RB_THREAD_FD_SELECT - #define EmSelect rb_thread_fd_select - #else - // ruby 1.9.1 and below - #define EmSelect rb_thread_select - #endif - - #ifdef HAVE_RB_THREAD_CALL_WITHOUT_GVL - #include - #endif - - #ifdef HAVE_RB_WAIT_FOR_SINGLE_FD - #include - #endif - - #if defined(HAVE_RB_TRAP_IMMEDIATE) - #include - #elif defined(HAVE_RB_ENABLE_INTERRUPT) - extern "C" { - void rb_enable_interrupt(void); - void rb_disable_interrupt(void); - } - - #define TRAP_BEG rb_enable_interrupt() - #define TRAP_END do { rb_disable_interrupt(); rb_thread_check_ints(); } while(0) - #else - #define TRAP_BEG - #define TRAP_END - #endif - - // 1.9.0 compat - #ifndef RUBY_UBF_IO - #define RUBY_UBF_IO RB_UBF_DFL - #endif - #ifndef RSTRING_PTR - #define RSTRING_PTR(str) RSTRING(str)->ptr - #endif - #ifndef RSTRING_LEN - #define RSTRING_LEN(str) RSTRING(str)->len - #endif - #ifndef RSTRING_LENINT - #define RSTRING_LENINT(str) RSTRING_LEN(str) - #endif + #include + #include + + #define EmSelect rb_thread_fd_select #else #define EmSelect select #endif -#if !defined(HAVE_TYPE_RB_FDSET_T) -#define fd_check(n) (((n) < FD_SETSIZE) ? 1 : 0*fprintf(stderr, "fd %d too large for select\n", (n))) -// These definitions are cribbed from include/ruby/intern.h in Ruby 1.9.3, -// with this change: any macros that read or write the nth element of an -// fdset first call fd_check to make sure n is in bounds. -typedef fd_set rb_fdset_t; -#define rb_fd_zero(f) FD_ZERO(f) -#define rb_fd_set(n, f) do { if (fd_check(n)) FD_SET((n), (f)); } while(0) -#define rb_fd_clr(n, f) do { if (fd_check(n)) FD_CLR((n), (f)); } while(0) -#define rb_fd_isset(n, f) (fd_check(n) ? FD_ISSET((n), (f)) : 0) -#define rb_fd_copy(d, s, n) (*(d) = *(s)) -#define rb_fd_dup(d, s) (*(d) = *(s)) -#define rb_fd_resize(n, f) ((void)(f)) -#define rb_fd_ptr(f) (f) -#define rb_fd_init(f) FD_ZERO(f) -#define rb_fd_init_copy(d, s) (*(d) = *(s)) -#define rb_fd_term(f) ((void)(f)) -#define rb_fd_max(f) FD_SETSIZE -#define rb_fd_select(n, rfds, wfds, efds, timeout) \ - select(fd_check((n)-1) ? (n) : FD_SETSIZE, (rfds), (wfds), (efds), (timeout)) -#define rb_thread_fd_select(n, rfds, wfds, efds, timeout) \ - rb_thread_select(fd_check((n)-1) ? (n) : FD_SETSIZE, (rfds), (wfds), (efds), (timeout)) -#endif - - // This Solaris fix is adapted from eval_intern.h in Ruby 1.9.3: // Solaris sys/select.h switches select to select_large_fdset to support larger // file descriptors if FD_SETSIZE is larger than 1024 on 32bit environment. @@ -302,7 +238,6 @@ struct SelectData_t rb_fdset_t fdwrites; rb_fdset_t fderrors; timeval tv; - int nSockets; }; #endif // __EventMachine__H_ From 514dc86cf687ebae2051958cc7747f34cd9c72b1 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Sun, 6 May 2018 00:03:13 -0700 Subject: [PATCH 204/343] No need to test for Ruby methods that are required --- ext/extconf.rb | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/ext/extconf.rb b/ext/extconf.rb index effe70fea..8f809371e 100644 --- a/ext/extconf.rb +++ b/ext/extconf.rb @@ -104,17 +104,6 @@ def pkg_config_wrapper(pretty_name, name) add_define 'BUILD_FOR_RUBY' -# Ruby features: - -have_var('rb_trap_immediate', ['ruby.h', 'rubysig.h']) -have_func('rb_thread_blocking_region') -have_func('rb_thread_call_without_gvl', 'ruby/thread.h') -have_func('rb_thread_fd_select') -have_type('rb_fdset_t', 'ruby/intern.h') -have_func('rb_wait_for_single_fd') -have_func('rb_enable_interrupt') -have_func('rb_time_new') - # System features: add_define('HAVE_INOTIFY') if inotify = have_func('inotify_init', 'sys/inotify.h') From 0595a5be59b289fe7fc3fbb01c4bca14e3dc8d68 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Sun, 6 May 2018 00:03:49 -0700 Subject: [PATCH 205/343] Remove Ruby 1.9 --- .travis.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 970b7757c..6df22bffd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,8 +19,6 @@ rvm: - 2.2 - 2.1 - 2.0.0 - - 1.9.3 - - 1.9.2 - ruby-head # - rbx # - rbx-2 @@ -34,7 +32,6 @@ matrix: # - rvm: rbx-2 # - rvm: rbx-3 - rvm: ruby-head - - rvm: 2.0.0 - rvm: 2.3 os: osx # - rvm: jruby-1.7 From 8244f66645d113b413f4211cd4b58c816b8e9506 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Sun, 6 May 2018 00:04:19 -0700 Subject: [PATCH 206/343] Drop support for Ruby 1.9 and REE --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e8064e890..f0e708046 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ EventMachine has been around since the early 2000s and is a mature and battle-te ## What platforms are supported by EventMachine? ## -EventMachine supports Ruby 1.9.2 through 2.5, REE, JRuby and **works well on Windows** as well +EventMachine supports Ruby 2.0.0 through 2.6, JRuby and **works well on Windows** as well as many operating systems from the Unix family (Linux, Mac OS X, BSD flavors). From ce059e5cc1529f9284e9ad6556b76df429be8faa Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Wed, 24 Aug 2011 14:58:04 -0700 Subject: [PATCH 207/343] use rb_wait_for_single_fd() if available rb_thread_select() is deprecated under Ruby 1.9.x (and currently broken ([ruby-core:39095)] in trunk/1.9.3 as of 20110824) in favor of rb_thread_fd_select(). Ruby 1.9.3+ also offers the rb_wait_for_single_fd() API which is easier-to-use and (transparently) provides a minor performance improvement under Linux where the ppoll() syscall is available. Ruby 1.9.3 will fall back to the same logic used in rb_thread_fd_select() on non-Linux platforms when using rb_wait_for_single_fd(). Emulation using rb_thread_select() for older platforms is provided. This patch is tested on Ruby trunk r33022, 1.9.2-p290, and 1.8.7-p334. Full disclosure: I co-implemented rb_wait_for_single_fd() for Ruby 1.9.3 with Motohiro Kosaki. --- ext/wait_for_single_fd.h | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 ext/wait_for_single_fd.h diff --git a/ext/wait_for_single_fd.h b/ext/wait_for_single_fd.h new file mode 100644 index 000000000..c99eec76f --- /dev/null +++ b/ext/wait_for_single_fd.h @@ -0,0 +1,36 @@ +/* + * backwards compatibility for pre-1.9.3 C API + * + * Ruby 1.9.3 provides this API which allows the use of ppoll() on Linux + * to minimize select() and malloc() overhead on high-numbered FDs. + */ +#ifdef HAVE_RB_WAIT_FOR_SINGLE_FD +# include +#else +# define RB_WAITFD_IN 0x001 +# define RB_WAITFD_PRI 0x002 +# define RB_WAITFD_OUT 0x004 + +static int my_wait_for_single_fd(int fd, int events, struct timeval *tvp) +{ + fd_set fdset; + fd_set *rfds = NULL; + fd_set *wfds = NULL; + fd_set *efds = NULL; + + FD_ZERO(&fdset); + FD_SET(fd, &fdset); + + if (events & RB_WAITFD_IN) + rfds = &fdset; + if (events & RB_WAITFD_OUT) + wfds = &fdset; + if (events & RB_WAITFD_PRI) + efds = &fdset; + + return rb_thread_select(fd + 1, rfds, wfds, efds, tvp); +} + +#define rb_wait_for_single_fd(fd,events,tvp) \ + my_wait_for_single_fd((fd),(events),(tvp)) +#endif From 2170b14f568ba834f1148e21789a45cc1220240e Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Sat, 12 May 2018 08:58:12 -0700 Subject: [PATCH 208/343] Workarounds for Rubinius missing rb_fd functions --- ext/em.cpp | 15 +-------------- ext/em.h | 24 +++++++++++++++++++++++- ext/extconf.rb | 5 +++++ 3 files changed, 29 insertions(+), 15 deletions(-) diff --git a/ext/em.cpp b/ext/em.cpp index 11eea3056..8160d94f3 100644 --- a/ext/em.cpp +++ b/ext/em.cpp @@ -1052,20 +1052,7 @@ void EventMachine_t::_CleanBadDescriptors() if (ed->ShouldDelete()) continue; - SOCKET sd = ed->GetSocket(); - - struct timeval tv; - tv.tv_sec = 0; - tv.tv_usec = 0; - - rb_fdset_t fds; - rb_fd_init(&fds); - rb_fd_set(sd, &fds); - - int ret = rb_fd_select(sd + 1, &fds, NULL, NULL, &tv); - rb_fd_term(&fds); - - if (ret == -1) { + if (rb_wait_for_single_fd(ed->GetSocket(), RB_WAITFD_PRI, NULL) < 0) { if (errno == EBADF) ed->ScheduleClose(false); } diff --git a/ext/em.h b/ext/em.h index ab6411355..e5b826bfd 100644 --- a/ext/em.h +++ b/ext/em.h @@ -25,7 +25,29 @@ See the file COPYING for complete licensing information. #include #include - #define EmSelect rb_thread_fd_select + #ifndef HAVE_RB_WAIT_FOR_SINGLE_FD + #include "wait_for_single_fd.h" + #endif + +#if !defined(HAVE_TYPE_RB_FDSET_T) +#define fd_check(n) (((n) < FD_SETSIZE) ? 1 : 0*fprintf(stderr, "fd %d too large for select\n", (n))) +// These definitions are cribbed from include/ruby/intern.h in Ruby 1.9.3, +// with this change: any macros that read or write the nth element of an +// fdset first call fd_check to make sure n is in bounds. +typedef fd_set rb_fdset_t; +#define rb_fd_zero(f) FD_ZERO(f) +#define rb_fd_set(n, f) do { if (fd_check(n)) FD_SET((n), (f)); } while(0) +#define rb_fd_clr(n, f) do { if (fd_check(n)) FD_CLR((n), (f)); } while(0) +#define rb_fd_isset(n, f) (fd_check(n) ? FD_ISSET((n), (f)) : 0) +#define rb_fd_init(f) FD_ZERO(f) +#define rb_fd_term(f) ((void)(f)) +#endif + + #ifdef HAVE_RB_THREAD_FD_SELECT + #define EmSelect rb_thread_fd_select + #else + #define EmSelect select + #endif #else #define EmSelect select #endif diff --git a/ext/extconf.rb b/ext/extconf.rb index 8f809371e..a3999ea0a 100644 --- a/ext/extconf.rb +++ b/ext/extconf.rb @@ -104,6 +104,11 @@ def pkg_config_wrapper(pretty_name, name) add_define 'BUILD_FOR_RUBY' +# Rubinius workarounds: +have_type('rb_fdset_t', 'ruby/intern.h') +have_func('rb_wait_for_single_fd') +have_func('rb_thread_fd_select') + # System features: add_define('HAVE_INOTIFY') if inotify = have_func('inotify_init', 'sys/inotify.h') From 0904385936ef4ecae4519f4f7b8f829a3608afcd Mon Sep 17 00:00:00 2001 From: MSP-Greg Date: Sun, 13 Jan 2019 08:47:36 -0600 Subject: [PATCH 209/343] Update runtime files for TLS 1.3, no SSL, OpenSSL lib info Add several constants related to OpenSSL & TLS 1.3 EM::OPENSSL_LIBRARY_VERSION EM::OPENSSL_VERSION EM_PROTO_TLSv1_3 EM::OPENSSL_NO_SSL2 EM::OPENSSL_NO_SSL3 --- ext/eventmachine.h | 5 +++++ ext/rubymain.cpp | 28 ++++++++++++++++++++++++++++ ext/ssl.cpp | 5 +++++ lib/em/connection.rb | 5 +++++ 4 files changed, 43 insertions(+) diff --git a/ext/eventmachine.h b/ext/eventmachine.h index 2861276bd..788f1ca4e 100644 --- a/ext/eventmachine.h +++ b/ext/eventmachine.h @@ -44,7 +44,12 @@ extern "C" { EM_PROTO_SSLv3 = 4, EM_PROTO_TLSv1 = 8, EM_PROTO_TLSv1_1 = 16, +#ifdef TLS1_3_VERSION + EM_PROTO_TLSv1_2 = 32, + EM_PROTO_TLSv1_3 = 64 +#else EM_PROTO_TLSv1_2 = 32 +#endif }; void evma_initialize_library (EMCallback); diff --git a/ext/rubymain.cpp b/ext/rubymain.cpp index c5ea628e2..0194aaeaa 100644 --- a/ext/rubymain.cpp +++ b/ext/rubymain.cpp @@ -1569,5 +1569,33 @@ extern "C" void Init_rubyeventmachine() rb_define_const (EmModule, "EM_PROTO_TLSv1", INT2NUM(EM_PROTO_TLSv1 )); rb_define_const (EmModule, "EM_PROTO_TLSv1_1", INT2NUM(EM_PROTO_TLSv1_1)); rb_define_const (EmModule, "EM_PROTO_TLSv1_2", INT2NUM(EM_PROTO_TLSv1_2)); +#ifdef TLS1_3_VERSION + rb_define_const (EmModule, "EM_PROTO_TLSv1_3", INT2NUM(EM_PROTO_TLSv1_3)); +#endif + +#ifdef OPENSSL_NO_SSL3 + /* True if SSL3 is not available */ + rb_define_const (EmModule, "OPENSSL_NO_SSL3", Qtrue); + rb_define_const (EmModule, "OPENSSL_NO_SSL2", Qtrue); +#else + rb_define_const (EmModule, "OPENSSL_NO_SSL3", Qfalse); +#ifdef OPENSSL_NO_SSL2 + rb_define_const (EmModule, "OPENSSL_NO_SSL2", Qtrue); +#else + rb_define_const (EmModule, "OPENSSL_NO_SSL2", Qfalse); +#endif +#endif + + // OpenSSL Build / Runtime/Load versions + + /* Version of OpenSSL that EventMachine was compiled with */ + rb_define_const(EmModule, "OPENSSL_VERSION", rb_str_new2(OPENSSL_VERSION_TEXT)); + +#if !defined(LIBRESSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10100000 + /* Version of OpenSSL that EventMachine loaded with */ + rb_define_const(EmModule, "OPENSSL_LIBRARY_VERSION", rb_str_new2(OpenSSL_version(OPENSSL_VERSION))); +#else + rb_define_const(EmModule, "OPENSSL_LIBRARY_VERSION", rb_str_new2(SSLeay_version(SSLEAY_VERSION))); +#endif } diff --git a/ext/ssl.cpp b/ext/ssl.cpp index 8d5e038a9..c83e5b09f 100644 --- a/ext/ssl.cpp +++ b/ext/ssl.cpp @@ -180,6 +180,11 @@ SslContext_t::SslContext_t (bool is_server, const std::string &privkeyfile, cons SSL_CTX_set_options (pCtx, SSL_OP_NO_TLSv1_2); #endif + #ifdef SSL_OP_NO_TLSv1_3 + if (!(ssl_version & EM_PROTO_TLSv1_3)) + SSL_CTX_set_options (pCtx, SSL_OP_NO_TLSv1_3); + #endif + #ifdef SSL_MODE_RELEASE_BUFFERS SSL_CTX_set_mode (pCtx, SSL_MODE_RELEASE_BUFFERS); #endif diff --git a/lib/em/connection.rb b/lib/em/connection.rb index 7d45e92fc..b5f8b0ad7 100644 --- a/lib/em/connection.rb +++ b/lib/em/connection.rb @@ -436,6 +436,9 @@ def start_tls args={} protocols_bitmask |= EventMachine::EM_PROTO_TLSv1 protocols_bitmask |= EventMachine::EM_PROTO_TLSv1_1 protocols_bitmask |= EventMachine::EM_PROTO_TLSv1_2 + if EventMachine.const_defined? :EM_PROTO_TLSv1_3 + protocols_bitmask |= EventMachine::EM_PROTO_TLSv1_3 + end else [ssl_version].flatten.each do |p| case p.to_s.downcase @@ -449,6 +452,8 @@ def start_tls args={} protocols_bitmask |= EventMachine::EM_PROTO_TLSv1_1 when 'tlsv1_2' protocols_bitmask |= EventMachine::EM_PROTO_TLSv1_2 + when 'tlsv1_3' + protocols_bitmask |= EventMachine::EM_PROTO_TLSv1_3 else raise("Unrecognized SSL/TLS Protocol: #{p}") end From 34759f5862db43714cc63170c88388cdef93aa16 Mon Sep 17 00:00:00 2001 From: MSP-Greg Date: Sun, 13 Jan 2019 10:14:11 -0600 Subject: [PATCH 210/343] Update all tests to require_relative --- tests/test_attach.rb | 3 +-- tests/test_basic.rb | 3 +-- tests/test_channel.rb | 2 +- tests/test_completion.rb | 2 +- tests/test_connection_count.rb | 2 +- tests/test_connection_write.rb | 2 +- tests/test_defer.rb | 2 +- tests/test_deferrable.rb | 2 +- tests/test_epoll.rb | 3 +-- tests/test_error_handler.rb | 2 +- tests/test_exc.rb | 2 +- tests/test_file_watch.rb | 2 +- tests/test_fork.rb | 2 +- tests/test_futures.rb | 2 +- tests/test_handler_check.rb | 2 +- tests/test_hc.rb | 2 +- tests/test_httpclient.rb | 2 +- tests/test_httpclient2.rb | 2 +- tests/test_idle_connection.rb | 2 +- tests/test_inactivity_timeout.rb | 2 +- tests/test_io_streamer.rb | 2 +- tests/test_ipv4.rb | 2 +- tests/test_ipv6.rb | 2 +- tests/test_iterator.rb | 2 +- tests/test_kb.rb | 2 +- tests/test_keepalive.rb | 3 +-- tests/test_line_protocol.rb | 2 +- tests/test_ltp.rb | 2 +- tests/test_ltp2.rb | 2 +- tests/test_many_fds.rb | 3 +-- tests/test_next_tick.rb | 2 +- tests/test_object_protocol.rb | 2 +- tests/test_pause.rb | 2 +- tests/test_pending_connect_timeout.rb | 2 +- tests/test_pool.rb | 2 +- tests/test_process_watch.rb | 2 +- tests/test_processes.rb | 2 +- tests/test_proxy_connection.rb | 2 +- tests/test_pure.rb | 2 +- tests/test_queue.rb | 2 +- tests/test_resolver.rb | 2 +- tests/test_running.rb | 2 +- tests/test_sasl.rb | 3 +-- tests/test_send_file.rb | 2 +- tests/test_servers.rb | 3 +-- tests/test_shutdown_hooks.rb | 2 +- tests/test_smtpclient.rb | 2 +- tests/test_smtpserver.rb | 2 +- tests/test_sock_opt.rb | 3 +-- tests/test_spawn.rb | 5 +---- tests/test_ssl_args.rb | 4 +--- tests/test_ssl_dhparam.rb | 2 +- tests/test_ssl_ecdh_curve.rb | 2 +- tests/test_ssl_extensions.rb | 3 +-- tests/test_ssl_methods.rb | 2 +- tests/test_ssl_protocols.rb | 3 +-- tests/test_ssl_verify.rb | 2 +- tests/test_stomp.rb | 2 +- tests/test_system.rb | 2 +- tests/test_threaded_resource.rb | 2 +- tests/test_tick_loop.rb | 3 +-- tests/test_timers.rb | 2 +- tests/test_ud.rb | 2 +- tests/test_unbind_reason.rb | 2 +- 64 files changed, 64 insertions(+), 80 deletions(-) diff --git a/tests/test_attach.rb b/tests/test_attach.rb index 821aae5cc..340cbe87f 100644 --- a/tests/test_attach.rb +++ b/tests/test_attach.rb @@ -1,5 +1,4 @@ -require 'em_test_helper' -require 'socket' +require_relative 'em_test_helper' class TestAttach < Test::Unit::TestCase class EchoServer < EM::Connection diff --git a/tests/test_basic.rb b/tests/test_basic.rb index a93763cbe..0e609c72a 100644 --- a/tests/test_basic.rb +++ b/tests/test_basic.rb @@ -1,5 +1,4 @@ -require 'em_test_helper' -require 'socket' +require_relative 'em_test_helper' class TestBasic < Test::Unit::TestCase def setup diff --git a/tests/test_channel.rb b/tests/test_channel.rb index 872b1109a..90da43d5c 100644 --- a/tests/test_channel.rb +++ b/tests/test_channel.rb @@ -1,4 +1,4 @@ -require 'em_test_helper' +require_relative 'em_test_helper' class TestEMChannel < Test::Unit::TestCase def test_channel_subscribe diff --git a/tests/test_completion.rb b/tests/test_completion.rb index 1bd9a8f4e..ce1489a7a 100644 --- a/tests/test_completion.rb +++ b/tests/test_completion.rb @@ -1,4 +1,4 @@ -require 'em_test_helper' +require_relative 'em_test_helper' require 'em/completion' class TestCompletion < Test::Unit::TestCase diff --git a/tests/test_connection_count.rb b/tests/test_connection_count.rb index 350c41774..9ef482a38 100644 --- a/tests/test_connection_count.rb +++ b/tests/test_connection_count.rb @@ -1,4 +1,4 @@ -require 'em_test_helper' +require_relative 'em_test_helper' class TestConnectionCount < Test::Unit::TestCase def teardown diff --git a/tests/test_connection_write.rb b/tests/test_connection_write.rb index 35533b526..e5fb39341 100644 --- a/tests/test_connection_write.rb +++ b/tests/test_connection_write.rb @@ -1,4 +1,4 @@ -require 'em_test_helper' +require_relative 'em_test_helper' class TestConnectionWrite < Test::Unit::TestCase diff --git a/tests/test_defer.rb b/tests/test_defer.rb index aeca127d8..c7ebd0b65 100644 --- a/tests/test_defer.rb +++ b/tests/test_defer.rb @@ -1,4 +1,4 @@ -require 'em_test_helper' +require_relative 'em_test_helper' class TestDefer < Test::Unit::TestCase diff --git a/tests/test_deferrable.rb b/tests/test_deferrable.rb index 5f286a77e..0138e0202 100644 --- a/tests/test_deferrable.rb +++ b/tests/test_deferrable.rb @@ -1,4 +1,4 @@ -require 'em_test_helper' +require_relative 'em_test_helper' class TestDeferrable < Test::Unit::TestCase class Later diff --git a/tests/test_epoll.rb b/tests/test_epoll.rb index 36f5609ab..64a431c8c 100644 --- a/tests/test_epoll.rb +++ b/tests/test_epoll.rb @@ -1,5 +1,4 @@ -require 'em_test_helper' - +require_relative 'em_test_helper' class TestEpoll < Test::Unit::TestCase diff --git a/tests/test_error_handler.rb b/tests/test_error_handler.rb index 23c23f74b..5cef72e76 100644 --- a/tests/test_error_handler.rb +++ b/tests/test_error_handler.rb @@ -1,4 +1,4 @@ -require 'em_test_helper' +require_relative 'em_test_helper' class TestErrorHandler < Test::Unit::TestCase def setup diff --git a/tests/test_exc.rb b/tests/test_exc.rb index 501636b5f..713880f2e 100644 --- a/tests/test_exc.rb +++ b/tests/test_exc.rb @@ -1,4 +1,4 @@ -require 'em_test_helper' +require_relative 'em_test_helper' class TestSomeExceptions < Test::Unit::TestCase class DoomedConnectionError < StandardError diff --git a/tests/test_file_watch.rb b/tests/test_file_watch.rb index 326b5ec13..c27ca2c9e 100644 --- a/tests/test_file_watch.rb +++ b/tests/test_file_watch.rb @@ -1,4 +1,4 @@ -require 'em_test_helper' +require_relative 'em_test_helper' require 'tempfile' class TestFileWatch < Test::Unit::TestCase diff --git a/tests/test_fork.rb b/tests/test_fork.rb index 8b15bb586..d5a40bfc1 100644 --- a/tests/test_fork.rb +++ b/tests/test_fork.rb @@ -1,4 +1,4 @@ -require 'em_test_helper' +require_relative 'em_test_helper' class TestFork < Test::Unit::TestCase diff --git a/tests/test_futures.rb b/tests/test_futures.rb index b4948567d..aa00823f7 100644 --- a/tests/test_futures.rb +++ b/tests/test_futures.rb @@ -1,4 +1,4 @@ -require 'em_test_helper' +require_relative 'em_test_helper' class TestFutures < Test::Unit::TestCase diff --git a/tests/test_handler_check.rb b/tests/test_handler_check.rb index c4176941b..ee0322228 100644 --- a/tests/test_handler_check.rb +++ b/tests/test_handler_check.rb @@ -1,4 +1,4 @@ -require 'em_test_helper' +require_relative 'em_test_helper' class TestHandlerCheck < Test::Unit::TestCase diff --git a/tests/test_hc.rb b/tests/test_hc.rb index 28e32c9ce..9474ee0ea 100644 --- a/tests/test_hc.rb +++ b/tests/test_hc.rb @@ -1,4 +1,4 @@ -require 'em_test_helper' +require_relative 'em_test_helper' class TestHeaderAndContentProtocol < Test::Unit::TestCase diff --git a/tests/test_httpclient.rb b/tests/test_httpclient.rb index 572a8022b..1ddd5fedc 100644 --- a/tests/test_httpclient.rb +++ b/tests/test_httpclient.rb @@ -1,4 +1,4 @@ -require 'em_test_helper' +require_relative 'em_test_helper' class TestHttpClient < Test::Unit::TestCase diff --git a/tests/test_httpclient2.rb b/tests/test_httpclient2.rb index 648529626..68c1ad62d 100644 --- a/tests/test_httpclient2.rb +++ b/tests/test_httpclient2.rb @@ -1,4 +1,4 @@ -require 'em_test_helper' +require_relative 'em_test_helper' class TestHttpClient2 < Test::Unit::TestCase class TestServer < EM::Connection diff --git a/tests/test_idle_connection.rb b/tests/test_idle_connection.rb index bfc57cd99..aaeac7c32 100644 --- a/tests/test_idle_connection.rb +++ b/tests/test_idle_connection.rb @@ -1,4 +1,4 @@ -require 'em_test_helper' +require_relative 'em_test_helper' class TestIdleConnection < Test::Unit::TestCase def setup diff --git a/tests/test_inactivity_timeout.rb b/tests/test_inactivity_timeout.rb index 15e39a2f7..e342193bc 100644 --- a/tests/test_inactivity_timeout.rb +++ b/tests/test_inactivity_timeout.rb @@ -1,4 +1,4 @@ -require 'em_test_helper' +require_relative 'em_test_helper' class TestInactivityTimeout < Test::Unit::TestCase diff --git a/tests/test_io_streamer.rb b/tests/test_io_streamer.rb index 0a8c0e437..ac8da0ff2 100644 --- a/tests/test_io_streamer.rb +++ b/tests/test_io_streamer.rb @@ -1,4 +1,4 @@ -require 'em_test_helper' +require_relative 'em_test_helper' require 'em/io_streamer' EM::IOStreamer::CHUNK_SIZE = 2 diff --git a/tests/test_ipv4.rb b/tests/test_ipv4.rb index b68d92748..9ffc8c348 100644 --- a/tests/test_ipv4.rb +++ b/tests/test_ipv4.rb @@ -1,4 +1,4 @@ -require 'em_test_helper' +require_relative 'em_test_helper' class TestIPv4 < Test::Unit::TestCase # Runs a TCP server in the local IPv4 address, connects to it and sends a specific data. diff --git a/tests/test_ipv6.rb b/tests/test_ipv6.rb index b52fef1f8..595460ea3 100644 --- a/tests/test_ipv6.rb +++ b/tests/test_ipv6.rb @@ -1,4 +1,4 @@ -require 'em_test_helper' +require_relative 'em_test_helper' class TestIPv6 < Test::Unit::TestCase diff --git a/tests/test_iterator.rb b/tests/test_iterator.rb index 6ec4e4642..3b7df982c 100644 --- a/tests/test_iterator.rb +++ b/tests/test_iterator.rb @@ -1,4 +1,4 @@ -require 'em_test_helper' +require_relative 'em_test_helper' class TestIterator < Test::Unit::TestCase diff --git a/tests/test_kb.rb b/tests/test_kb.rb index 9c31f5fbc..febd28f11 100644 --- a/tests/test_kb.rb +++ b/tests/test_kb.rb @@ -1,4 +1,4 @@ -require 'em_test_helper' +require_relative 'em_test_helper' class TestKeyboardEvents < Test::Unit::TestCase diff --git a/tests/test_keepalive.rb b/tests/test_keepalive.rb index 17c8cffad..bd6202a53 100644 --- a/tests/test_keepalive.rb +++ b/tests/test_keepalive.rb @@ -1,5 +1,4 @@ -require 'em_test_helper' -require 'socket' +require_relative 'em_test_helper' class TestKeepalive < Test::Unit::TestCase def setup diff --git a/tests/test_line_protocol.rb b/tests/test_line_protocol.rb index 2067a72e6..9f91b6306 100644 --- a/tests/test_line_protocol.rb +++ b/tests/test_line_protocol.rb @@ -1,4 +1,4 @@ -require 'em_test_helper' +require_relative 'em_test_helper' class TestLineProtocol < Test::Unit::TestCase class LineProtocolTestClass diff --git a/tests/test_ltp.rb b/tests/test_ltp.rb index 06c268514..9e974d92b 100644 --- a/tests/test_ltp.rb +++ b/tests/test_ltp.rb @@ -1,4 +1,4 @@ -require 'em_test_helper' +require_relative 'em_test_helper' class TestLineAndTextProtocol < Test::Unit::TestCase diff --git a/tests/test_ltp2.rb b/tests/test_ltp2.rb index 220fcbe1c..71781ce70 100644 --- a/tests/test_ltp2.rb +++ b/tests/test_ltp2.rb @@ -1,4 +1,4 @@ -require 'em_test_helper' +require_relative 'em_test_helper' # TODO!!! Need tests for overlength headers and text bodies. diff --git a/tests/test_many_fds.rb b/tests/test_many_fds.rb index 7c126dcc7..78732ad87 100644 --- a/tests/test_many_fds.rb +++ b/tests/test_many_fds.rb @@ -1,5 +1,4 @@ -require 'em_test_helper' -require 'socket' +require_relative 'em_test_helper' class TestManyFDs < Test::Unit::TestCase def setup diff --git a/tests/test_next_tick.rb b/tests/test_next_tick.rb index 9b60359c3..df3e2a106 100644 --- a/tests/test_next_tick.rb +++ b/tests/test_next_tick.rb @@ -1,4 +1,4 @@ -require 'em_test_helper' +require_relative 'em_test_helper' class TestNextTick < Test::Unit::TestCase diff --git a/tests/test_object_protocol.rb b/tests/test_object_protocol.rb index b1287ea81..3c40104f3 100644 --- a/tests/test_object_protocol.rb +++ b/tests/test_object_protocol.rb @@ -1,4 +1,4 @@ -require 'em_test_helper' +require_relative 'em_test_helper' class TestObjectProtocol < Test::Unit::TestCase module Server diff --git a/tests/test_pause.rb b/tests/test_pause.rb index d078a77fa..1c0743ec3 100644 --- a/tests/test_pause.rb +++ b/tests/test_pause.rb @@ -1,4 +1,4 @@ -require 'em_test_helper' +require_relative 'em_test_helper' class TestPause < Test::Unit::TestCase if EM.respond_to? :pause_connection diff --git a/tests/test_pending_connect_timeout.rb b/tests/test_pending_connect_timeout.rb index a3f7fa4d8..d382bca1e 100644 --- a/tests/test_pending_connect_timeout.rb +++ b/tests/test_pending_connect_timeout.rb @@ -1,4 +1,4 @@ -require 'em_test_helper' +require_relative 'em_test_helper' class TestPendingConnectTimeout < Test::Unit::TestCase diff --git a/tests/test_pool.rb b/tests/test_pool.rb index f859a9d10..83ba165b9 100644 --- a/tests/test_pool.rb +++ b/tests/test_pool.rb @@ -1,4 +1,4 @@ -require 'em_test_helper' +require_relative 'em_test_helper' class TestPool < Test::Unit::TestCase def pool diff --git a/tests/test_process_watch.rb b/tests/test_process_watch.rb index 6e0c49a75..2ae960a26 100644 --- a/tests/test_process_watch.rb +++ b/tests/test_process_watch.rb @@ -1,4 +1,4 @@ -require 'em_test_helper' +require_relative 'em_test_helper' if EM.kqueue? class TestProcessWatch < Test::Unit::TestCase diff --git a/tests/test_processes.rb b/tests/test_processes.rb index dd03cf02d..eb21a6cec 100644 --- a/tests/test_processes.rb +++ b/tests/test_processes.rb @@ -1,4 +1,4 @@ -require 'em_test_helper' +require_relative 'em_test_helper' class TestProcesses < Test::Unit::TestCase diff --git a/tests/test_proxy_connection.rb b/tests/test_proxy_connection.rb index 11c0fb410..2b2eaa76e 100644 --- a/tests/test_proxy_connection.rb +++ b/tests/test_proxy_connection.rb @@ -1,4 +1,4 @@ -require 'em_test_helper' +require_relative 'em_test_helper' class TestProxyConnection < Test::Unit::TestCase diff --git a/tests/test_pure.rb b/tests/test_pure.rb index a41f00100..9fb9407f6 100644 --- a/tests/test_pure.rb +++ b/tests/test_pure.rb @@ -1,4 +1,4 @@ -require 'em_test_helper' +require_relative 'em_test_helper' class TestPure < Test::Unit::TestCase diff --git a/tests/test_queue.rb b/tests/test_queue.rb index 34278c05b..a8dfdbeb0 100644 --- a/tests/test_queue.rb +++ b/tests/test_queue.rb @@ -1,4 +1,4 @@ -require 'em_test_helper' +require_relative 'em_test_helper' class TestEMQueue < Test::Unit::TestCase def test_queue_push diff --git a/tests/test_resolver.rb b/tests/test_resolver.rb index c8a0513b4..7b9428439 100644 --- a/tests/test_resolver.rb +++ b/tests/test_resolver.rb @@ -1,4 +1,4 @@ -require 'em_test_helper' +require_relative 'em_test_helper' class TestResolver < Test::Unit::TestCase def test_nameserver diff --git a/tests/test_running.rb b/tests/test_running.rb index 693b3901a..e318ccc61 100644 --- a/tests/test_running.rb +++ b/tests/test_running.rb @@ -1,4 +1,4 @@ -require 'em_test_helper' +require_relative 'em_test_helper' class TestRunning < Test::Unit::TestCase def test_running diff --git a/tests/test_sasl.rb b/tests/test_sasl.rb index e80f6571a..7982109c7 100644 --- a/tests/test_sasl.rb +++ b/tests/test_sasl.rb @@ -1,5 +1,4 @@ -require 'em_test_helper' - +require_relative 'em_test_helper' class TestSASL < Test::Unit::TestCase diff --git a/tests/test_send_file.rb b/tests/test_send_file.rb index a784b505d..2ef1f7fde 100644 --- a/tests/test_send_file.rb +++ b/tests/test_send_file.rb @@ -1,4 +1,4 @@ -require 'em_test_helper' +require_relative 'em_test_helper' require 'tempfile' class TestSendFile < Test::Unit::TestCase diff --git a/tests/test_servers.rb b/tests/test_servers.rb index 2a170504d..8fd501a8f 100644 --- a/tests/test_servers.rb +++ b/tests/test_servers.rb @@ -1,5 +1,4 @@ -require 'em_test_helper' -require 'socket' +require_relative 'em_test_helper' class TestServers < Test::Unit::TestCase diff --git a/tests/test_shutdown_hooks.rb b/tests/test_shutdown_hooks.rb index b0e0c5cc5..81ee8d878 100644 --- a/tests/test_shutdown_hooks.rb +++ b/tests/test_shutdown_hooks.rb @@ -1,4 +1,4 @@ -require 'em_test_helper' +require_relative 'em_test_helper' class TestShutdownHooks < Test::Unit::TestCase def test_shutdown_hooks diff --git a/tests/test_smtpclient.rb b/tests/test_smtpclient.rb index 71ed5844a..bb49e5e6d 100644 --- a/tests/test_smtpclient.rb +++ b/tests/test_smtpclient.rb @@ -1,4 +1,4 @@ -require 'em_test_helper' +require_relative 'em_test_helper' class TestSmtpClient < Test::Unit::TestCase diff --git a/tests/test_smtpserver.rb b/tests/test_smtpserver.rb index 75518dbaf..4d526d5e5 100644 --- a/tests/test_smtpserver.rb +++ b/tests/test_smtpserver.rb @@ -1,5 +1,5 @@ +require_relative 'em_test_helper' require 'net/smtp' -require 'em_test_helper' class TestSmtpServer < Test::Unit::TestCase diff --git a/tests/test_sock_opt.rb b/tests/test_sock_opt.rb index 60fba351e..dedf3c955 100644 --- a/tests/test_sock_opt.rb +++ b/tests/test_sock_opt.rb @@ -1,5 +1,4 @@ -require 'em_test_helper' -require 'socket' +require_relative 'em_test_helper' class TestSockOpt < Test::Unit::TestCase def setup diff --git a/tests/test_spawn.rb b/tests/test_spawn.rb index ab0a92f77..342b8848a 100644 --- a/tests/test_spawn.rb +++ b/tests/test_spawn.rb @@ -1,7 +1,4 @@ - -require 'em_test_helper' - - +require_relative 'em_test_helper' class TestSpawn < Test::Unit::TestCase diff --git a/tests/test_ssl_args.rb b/tests/test_ssl_args.rb index d337628c7..8f78fec2d 100644 --- a/tests/test_ssl_args.rb +++ b/tests/test_ssl_args.rb @@ -1,8 +1,6 @@ -require "test/unit" +require_relative 'em_test_helper' require 'tempfile' -require 'em_test_helper' - module EM def self._set_mocks class < Date: Sun, 13 Jan 2019 10:21:51 -0600 Subject: [PATCH 211/343] Update rake files for test task 'lib' is included by default in Rake::TestTask#libs 'tests' is not needed due to require_relative --- rakelib/test.rake | 2 -- rakelib/test_pure.rake | 2 -- 2 files changed, 4 deletions(-) diff --git a/rakelib/test.rake b/rakelib/test.rake index 1185ac70c..b3a462b78 100644 --- a/rakelib/test.rake +++ b/rakelib/test.rake @@ -1,8 +1,6 @@ require 'rake/testtask' Rake::TestTask.new(:test) do |t| - t.libs << "tests" - t.libs << "lib" t.pattern = 'tests/**/test_*.rb' t.warning = true end diff --git a/rakelib/test_pure.rake b/rakelib/test_pure.rake index 5a84ded20..296add967 100644 --- a/rakelib/test_pure.rake +++ b/rakelib/test_pure.rake @@ -1,8 +1,6 @@ require 'rake/testtask' Rake::TestTask.new(:test_pure) do |t| - t.libs << 'tests' - t.libs << 'lib' t.test_files = Dir.glob('tests/**/test_pure*.rb') + Dir.glob('tests/**/test_ssl*.rb') t.warning = true end From fc95df7a31ae5694f6a762c0c3d4f5c79c3ee40b Mon Sep 17 00:00:00 2001 From: MSP-Greg Date: Sun, 13 Jan 2019 16:10:30 -0600 Subject: [PATCH 212/343] Remove some old Ruby version code, Travis to xenial (only TLS, no SSL) Move console 'SSL Info' code to em_test_helper.rb Prev code assumed SSLv3 was available, newer OpenSSL versions are often built without --- .travis.yml | 25 +++++++++++++------- Gemfile | 7 +----- eventmachine.gemspec | 2 +- tests/em_test_helper.rb | 27 +++++++++++++++++++-- tests/test_ssl_protocols.rb | 47 +++++++++++++------------------------ 5 files changed, 60 insertions(+), 48 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6df22bffd..c8f0b22cd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,16 +1,25 @@ -sudo: false -dist: trusty +dist: xenial language: ruby bundler_args: --without documentation -script: bundle exec rake compile test -# Pin Rubygems to a working version. Sometimes it breaks upstream. Update now and then. + before_install: - - 'if [[ "$TRAVIS_OS_NAME" = "osx" ]]; then brew install openssl; fi' - - gem update --system 2.7.4 - - gem update bundler + # rubygems 2.7.8 and greater include bundler + # remove 2.7.0 code when Travis removes rubygems 2.7.8 from ruby-head build + - | + rv="$(ruby -e 'STDOUT.write RUBY_VERSION')"; + if [ "$rv" \< "2.3" ]; then gem update --system 2.7.8 --no-document + elif [ "$rv" \< "2.7" ]; then gem update --system --no-document --conservative + fi + +before_script: + - bundle exec rake compile + +script: + - bundle exec rake test + env: global: - - TESTOPTS=-v + - TESTOPTS="-v --no-show-detail-immediately" rvm: - 2.6 - 2.5 diff --git a/Gemfile b/Gemfile index e0b2f0883..702c8b3b3 100644 --- a/Gemfile +++ b/Gemfile @@ -2,12 +2,7 @@ source 'https://rubygems.org' gemspec -# Rake 11.0 no longer supports Ruby 1.8.7 and Ruby 1.9.2 -if RUBY_VERSION < '1.9.3' - gem 'rake', '< 11' -else - gem 'rake' -end +gem 'rake' group :documentation do gem 'yard', '>= 0.8.5.2' diff --git a/eventmachine.gemspec b/eventmachine.gemspec index c3834013c..1d2d79a67 100644 --- a/eventmachine.gemspec +++ b/eventmachine.gemspec @@ -19,7 +19,7 @@ Gem::Specification.new do |s| s.metadata["msys2_mingw_dependencies"] = "openssl" end - s.add_development_dependency 'test-unit', '~> 2.0' + s.add_development_dependency 'test-unit', '~> 3.2' s.add_development_dependency 'rake-compiler', '~> 1.0' s.add_development_dependency 'rake-compiler-dock', '~> 0.6.3' diff --git a/tests/em_test_helper.rb b/tests/em_test_helper.rb index eb3224820..2682849c7 100644 --- a/tests/em_test_helper.rb +++ b/tests/em_test_helper.rb @@ -4,9 +4,32 @@ require 'rbconfig' require 'socket' -puts "\nEM.library_type #{EM.library_type.to_s.ljust(12)} EM.ssl? #{EM.ssl?}" - class Test::Unit::TestCase + + # below outputs in to console on load + # SSL_AVAIL is used by SSL tests + puts "", RUBY_DESCRIPTION + puts "\nEM.library_type #{EM.library_type.to_s.ljust(16)} EM.ssl? #{EM.ssl?}" + if EM.ssl? + require 'openssl' + ssl_lib_vers = OpenSSL.const_defined?(:OPENSSL_LIBRARY_VERSION) ? + OpenSSL::OPENSSL_LIBRARY_VERSION : 'na' + puts "OpenSSL OPENSSL_LIBRARY_VERSION: #{ssl_lib_vers}\n" \ + " OPENSSL_VERSION: #{OpenSSL::OPENSSL_VERSION}\n" \ + " EM OPENSSL_LIBRARY_VERSION: #{EM::OPENSSL_LIBRARY_VERSION}\n" \ + " OPENSSL_VERSION: #{EM::OPENSSL_VERSION}" + + # assumes all 2.x versions include support for TLSv1_2 + temp = [] + temp << 'SSLv2' unless EM::OPENSSL_NO_SSL2 + temp << 'SSLv3' unless EM::OPENSSL_NO_SSL3 + temp += %w[TLSv1 TLSv1_1 TLSv1_2] + temp << 'TLSv1_3' if EM.const_defined? :EM_PROTO_TLSv1_3 + temp.sort! + puts " SSL_AVAIL: #{temp.join(' ')}", "" + SSL_AVAIL = temp.freeze + end + class EMTestTimeout < StandardError ; end def setup_timeout(timeout = TIMEOUT_INTERVAL) diff --git a/tests/test_ssl_protocols.rb b/tests/test_ssl_protocols.rb index 181abbc30..96c57c236 100644 --- a/tests/test_ssl_protocols.rb +++ b/tests/test_ssl_protocols.rb @@ -5,21 +5,6 @@ if EM.ssl? class TestSslProtocols < Test::Unit::TestCase - if ::OpenSSL::VERSION >= "2.1" - # Assume no SSLv3 in OpenSSL, OpenSSL::SSL::SSLContext::METHODS deprecated - SSL_AVAIL = ["tlsv1", "tlsv1_1", "tlsv1_2"] - else - # Equal to base METHODS, downcased, like ["tlsv1, "tlsv1_1", "tlsv1_2"] - SSL_AVAIL = ::OpenSSL::SSL::SSLContext::METHODS.select { |i| i =~ /[^\d]\d\z/ }.map { |i| i.to_s.downcase } - end - - libr_vers = OpenSSL.const_defined?(:OPENSSL_LIBRARY_VERSION) ? - OpenSSL::OPENSSL_VERSION : 'na' - - puts "OPENSSL_LIBRARY_VERSION: #{libr_vers}\n" \ - " OPENSSL_VERSION: #{OpenSSL::OPENSSL_VERSION}\n" \ - " SSL_AVAIL: #{SSL_AVAIL.sort.join(' ')}" - module Client def ssl_handshake_completed $client_handshake_completed = true @@ -40,7 +25,7 @@ def ssl_handshake_completed module ClientAny include Client def post_init - start_tls(:ssl_version => SSL_AVAIL) + start_tls(:ssl_version => TestSslProtocols::SSL_AVAIL) end end @@ -89,7 +74,7 @@ def post_init module ServerAny include Server def post_init - start_tls(:ssl_version => SSL_AVAIL) + start_tls(:ssl_version => TestSslProtocols::SSL_AVAIL) end end @@ -117,7 +102,7 @@ def test_invalid_ssl_version end def test_any_to_v3 - omit("SSLv3 is (correctly) unavailable") unless SSL_AVAIL.include? "sslv3" + omit("SSLv3 is (correctly) unavailable") if EM::OPENSSL_NO_SSL3 $client_handshake_completed, $server_handshake_completed = false, false EM.run do EM.start_server("127.0.0.1", 16784, ServerSSLv3) @@ -129,7 +114,7 @@ def test_any_to_v3 end def test_any_to_tlsv1_2 - omit("TLSv1_2 is unavailable") unless SSL_AVAIL.include? "tlsv1_2" + omit("TLSv1_2 is unavailable") unless SSL_AVAIL.include? "TLSv1_2" $client_handshake_completed, $server_handshake_completed = false, false EM.run do EM.start_server("127.0.0.1", 16784, ServerTLSv1_2) @@ -152,7 +137,7 @@ def test_case_insensitivity end def test_v3_to_any - omit("SSLv3 is (correctly) unavailable") unless SSL_AVAIL.include? "sslv3" + omit("SSLv3 is (correctly) unavailable") if EM::OPENSSL_NO_SSL3 $client_handshake_completed, $server_handshake_completed = false, false EM.run do EM.start_server("127.0.0.1", 16784, ServerAny) @@ -164,7 +149,7 @@ def test_v3_to_any end def test_tlsv1_2_to_any - omit("TLSv1_2 is unavailable") unless SSL_AVAIL.include? "tlsv1_2" + omit("TLSv1_2 is unavailable") unless SSL_AVAIL.include? "TLSv1_2" $client_handshake_completed, $server_handshake_completed = false, false EM.run do EM.start_server("127.0.0.1", 16784, ServerAny) @@ -176,7 +161,7 @@ def test_tlsv1_2_to_any end def test_v3_to_v3 - omit("SSLv3 is (correctly) unavailable") unless SSL_AVAIL.include? "sslv3" + omit("SSLv3 is (correctly) unavailable") if EM::OPENSSL_NO_SSL3 $client_handshake_completed, $server_handshake_completed = false, false EM.run do EM.start_server("127.0.0.1", 16784, ServerSSLv3) @@ -188,7 +173,7 @@ def test_v3_to_v3 end def test_tlsv1_2_to_tlsv1_2 - omit("TLSv1_2 is unavailable") unless SSL_AVAIL.include? "tlsv1_2" + omit("TLSv1_2 is unavailable") unless SSL_AVAIL.include? "TLSv1_2" $client_handshake_completed, $server_handshake_completed = false, false EM.run do EM.start_server("127.0.0.1", 16784, ServerTLSv1_2) @@ -245,7 +230,7 @@ def ssl_handshake_completed module ServerAnyStopAfterHandshake def post_init - start_tls(:ssl_version => SSL_AVAIL) + start_tls(:ssl_version => TestSslProtocols::SSL_AVAIL) end def ssl_handshake_completed @@ -255,7 +240,7 @@ def ssl_handshake_completed end def test_v3_with_external_client - omit("SSLv3 is (correctly) unavailable") unless SSL_AVAIL.include? "sslv3" + omit("SSLv3 is (correctly) unavailable") if EM::OPENSSL_NO_SSL3 $server_handshake_completed = false EM.run do setup_timeout(2) @@ -276,7 +261,7 @@ def test_v3_with_external_client # Fixed Server def test_tlsv1_2_with_external_client - omit("TLSv1_2 is unavailable") unless SSL_AVAIL.include? "tlsv1_2" + omit("TLSv1_2 is unavailable") unless SSL_AVAIL.include? "TLSv1_2" $server_handshake_completed = false EM.run do setup_timeout(2) @@ -284,7 +269,7 @@ def test_tlsv1_2_with_external_client EM.defer do sock = TCPSocket.new("127.0.0.1", 16784) ctx = OpenSSL::SSL::SSLContext.new - ctx.ssl_version = :SSLv23_client + ctx.ssl_version = :SSLv23 ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx) ssl.connect ssl.close rescue nil @@ -297,7 +282,7 @@ def test_tlsv1_2_with_external_client # Fixed Client def test_any_with_external_client_tlsv1_2 - omit("TLSv1_2 is unavailable") unless SSL_AVAIL.include? "tlsv1_2" + omit("TLSv1_2 is unavailable") unless SSL_AVAIL.include? "TLSv1_2" $server_handshake_completed = false EM.run do setup_timeout(2) @@ -305,7 +290,7 @@ def test_any_with_external_client_tlsv1_2 EM.defer do sock = TCPSocket.new("127.0.0.1", 16784) ctx = OpenSSL::SSL::SSLContext.new - ctx.ssl_version = :TLSv1_2_client + ctx.ssl_version = :TLSv1_2 ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx) ssl.connect ssl.close rescue nil @@ -318,7 +303,7 @@ def test_any_with_external_client_tlsv1_2 # Refuse a client? def test_tlsv1_2_required_with_external_client - omit("TLSv1_2 is unavailable") unless SSL_AVAIL.include? "tlsv1_2" + omit("TLSv1_2 is unavailable") unless SSL_AVAIL.include? "TLSv1_2" $server_handshake_completed = false EM.run do n = 0 @@ -330,7 +315,7 @@ def test_tlsv1_2_required_with_external_client EM.defer do sock = TCPSocket.new("127.0.0.1", 16784) ctx = OpenSSL::SSL::SSLContext.new - ctx.ssl_version = :TLSv1_client + ctx.ssl_version = :TLSv1 ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx) assert_raise(OpenSSL::SSL::SSLError) { ssl.connect } ssl.close rescue nil From 9fc75ec173ecc2ab5feb10edc2ba0613ec9fa7e5 Mon Sep 17 00:00:00 2001 From: MSP-Greg Date: Sun, 13 Jan 2019 18:54:38 -0600 Subject: [PATCH 213/343] Update non SSL tests --- tests/em_test_helper.rb | 11 +++++++++++ tests/test_basic.rb | 2 +- tests/test_httpclient.rb | 2 +- tests/test_httpclient2.rb | 16 ++++++++-------- tests/test_inactivity_timeout.rb | 16 +++++++++++----- tests/test_ltp.rb | 8 ++++---- tests/test_pending_connect_timeout.rb | 2 +- 7 files changed, 37 insertions(+), 20 deletions(-) diff --git a/tests/em_test_helper.rb b/tests/em_test_helper.rb index 2682849c7..4df5fcc62 100644 --- a/tests/em_test_helper.rb +++ b/tests/em_test_helper.rb @@ -142,6 +142,17 @@ def rbx? # Tests may run slower on windows or Appveyor. YMMV TIMEOUT_INTERVAL = windows? ? 0.25 : 0.25 + module EMTestCasePrepend + def setup + EM.stop while EM.reactor_running? + end + + def teardown + EM.cleanup_machine + end + end + prepend EMTestCasePrepend + def silent backup, $VERBOSE = $VERBOSE, nil begin diff --git a/tests/test_basic.rb b/tests/test_basic.rb index 0e609c72a..3ce5db51c 100644 --- a/tests/test_basic.rb +++ b/tests/test_basic.rb @@ -38,7 +38,7 @@ def test_em def test_timer assert_nothing_raised do EM.run { - setup_timeout + setup_timeout 0.4 n = 0 EM.add_periodic_timer(0.1) { n += 1 diff --git a/tests/test_httpclient.rb b/tests/test_httpclient.rb index 1ddd5fedc..4ff31cec2 100644 --- a/tests/test_httpclient.rb +++ b/tests/test_httpclient.rb @@ -135,7 +135,7 @@ def test_post response = nil EM.run { EM.start_server '127.0.0.1', @port, PostContent - setup_timeout(2) + setup_timeout 2 c = silent { EM::P::HttpClient.request( :host => '127.0.0.1', :port => @port, diff --git a/tests/test_httpclient2.rb b/tests/test_httpclient2.rb index 68c1ad62d..add5cd4ff 100644 --- a/tests/test_httpclient2.rb +++ b/tests/test_httpclient2.rb @@ -17,7 +17,7 @@ def setup # def test_connect EM.run { - setup_timeout(TIMEOUT_INTERVAL) + setup_timeout EM.start_server '127.0.0.1', @port, TestServer silent do EM::P::HttpClient2.connect '127.0.0.1', @port @@ -29,7 +29,7 @@ def test_connect def test_bad_port EM.run { - setup_timeout(TIMEOUT_INTERVAL) + setup_timeout EM.start_server '127.0.0.1', @port, TestServer assert_raises( ArgumentError ) { silent { EM::P::HttpClient2.connect '127.0.0.1', "xxx" } @@ -52,8 +52,8 @@ def test_bad_server def test_get content = nil EM.run { - setup_timeout(TIMEOUT_INTERVAL) - http = silent { EM::P::HttpClient2.connect :host => "google.com", :port => 80 } + setup_timeout TIMEOUT + http = silent { EM::P::HttpClient2.connect :host => "www.google.com", :port => 80 } d = http.get "/" d.callback { content = d.content @@ -69,7 +69,7 @@ def test_get def _test_get_multiple content = nil EM.run { - setup_timeout(TIMEOUT_INTERVAL) + setup_timeout http = silent { EM::P::HttpClient2.connect "www.google.com" } d = http.get "/" d.callback { @@ -86,7 +86,7 @@ def _test_get_multiple def test_get_pipeline headers, headers2 = nil, nil EM.run { - setup_timeout(TIMEOUT) + setup_timeout TIMEOUT http = silent { EM::P::HttpClient2.connect "www.google.com", 80 } d = http.get("/") d.callback { @@ -105,7 +105,7 @@ def test_get_pipeline def test_authheader EM.run { - setup_timeout(TIMEOUT) + setup_timeout TIMEOUT EM.start_server '127.0.0.1', @port, TestServer http = silent { EM::P::HttpClient2.connect '127.0.0.1', 18842 } d = http.get :url=>"/", :authorization=>"Basic xxx" @@ -118,7 +118,7 @@ def test_https_get omit("No SSL") unless EM.ssl? d = nil EM.run { - setup_timeout(windows? ? 6 : 1) + setup_timeout(windows? ? 6 : TIMEOUT) http = silent { EM::P::HttpClient2.connect :host => 'www.google.com', :port => 443, :tls => true } d = http.get "/" d.callback {EM.stop} diff --git a/tests/test_inactivity_timeout.rb b/tests/test_inactivity_timeout.rb index e342193bc..bd91459e5 100644 --- a/tests/test_inactivity_timeout.rb +++ b/tests/test_inactivity_timeout.rb @@ -23,6 +23,12 @@ def test_set_and_get def test_for_real start, finish = nil + timeout_start = Module.new do + define_method :post_init do + start = Time.now + end + end + timeout_handler = Module.new do define_method :unbind do finish = Time.now @@ -31,17 +37,17 @@ def test_for_real end EM.run { - setup_timeout + setup_timeout 0.4 EM.heartbeat_interval = 0.01 - EM.start_server("127.0.0.1", 12345) + EM.start_server("127.0.0.1", 12345, timeout_start) EM.add_timer(0.01) { - start = Time.now c = EM.connect("127.0.0.1", 12345, timeout_handler) c.comm_inactivity_timeout = 0.02 } } - - assert_in_delta(0.02, (finish - start), 0.02) + # busy Travis intermittently saw a bit over 0.09, but often the whole + # test only took a bit over 0.02 + assert_in_delta(0.06, (finish - start), 0.04) end else warn "EM.comm_inactivity_timeout not implemented, skipping tests in #{__FILE__}" diff --git a/tests/test_ltp.rb b/tests/test_ltp.rb index 9e974d92b..ec09e7b08 100644 --- a/tests/test_ltp.rb +++ b/tests/test_ltp.rb @@ -39,7 +39,7 @@ def test_simple_lines EM.start_server( "127.0.0.1", @port, TLP_LineBuffer ) do |c| conn = c end - setup_timeout + setup_timeout 0.4 EM.connect "127.0.0.1", @port, StopClient do |c| c.send_data "aaa\nbbb\r\nccc\n" @@ -74,7 +74,7 @@ def test_overlength_lines EM.start_server( "127.0.0.1", @port, TLP_ErrorMessage ) do |c| conn = c end - setup_timeout + setup_timeout 0.4 EM.connect "127.0.0.1", @port, StopClient do |c| c.send_data "a" * (16*1024 + 1) c.send_data "\n" @@ -106,7 +106,7 @@ def test_lines_and_text output = '' EM.run { EM.start_server( "127.0.0.1", @port, LineAndTextTest ) - setup_timeout + setup_timeout 0.4 EM.connect "127.0.0.1", @port, StopClient do |c| c.set_receive_data { |data| output << data } @@ -140,7 +140,7 @@ def test_binary_text output = '' EM.run { EM.start_server( "127.0.0.1", @port, BinaryTextTest ) - setup_timeout + setup_timeout 0.4 EM.connect "127.0.0.1", @port, StopClient do |c| c.set_receive_data { |data| output << data } diff --git a/tests/test_pending_connect_timeout.rb b/tests/test_pending_connect_timeout.rb index d382bca1e..1db308065 100644 --- a/tests/test_pending_connect_timeout.rb +++ b/tests/test_pending_connect_timeout.rb @@ -31,7 +31,7 @@ def test_for_real end EM.run { - setup_timeout + setup_timeout 0.4 EM.heartbeat_interval = 0.1 start = EM.current_time c = EM.connect('192.0.2.0', 54321, timeout_handler) From dd6cec8d5278e11f2a1752aa7b4a712d53b1f1d3 Mon Sep 17 00:00:00 2001 From: MSP-Greg Date: Sun, 13 Jan 2019 21:23:03 -0600 Subject: [PATCH 214/343] Update SSL tests, 1.1.x & TLSv1_3 Add Ruby 2.6 to Travis osx for OpenSSL 1.1.0 Note that order of 'ssl_handshake_completed' callbacks in TLSv1_3 are reversed --- .travis.yml | 6 +- tests/test_ssl_dhparam.rb | 6 +- tests/test_ssl_ecdh_curve.rb | 52 +++++- tests/test_ssl_extensions.rb | 58 ++++--- tests/test_ssl_methods.rb | 3 +- tests/test_ssl_protocols.rb | 324 ++++++++++++----------------------- tests/test_ssl_verify.rb | 110 +++++++----- 7 files changed, 268 insertions(+), 291 deletions(-) diff --git a/.travis.yml b/.travis.yml index c8f0b22cd..1c03537ac 100644 --- a/.travis.yml +++ b/.travis.yml @@ -37,14 +37,14 @@ rvm: matrix: fast_finish: true allow_failures: + - rvm: ruby-head # - rvm: rbx # - rvm: rbx-2 # - rvm: rbx-3 - - rvm: ruby-head - - rvm: 2.3 - os: osx # - rvm: jruby-1.7 # - rvm: jruby-9 include: + - rvm: 2.6 + os: osx - rvm: 2.3 os: osx diff --git a/tests/test_ssl_dhparam.rb b/tests/test_ssl_dhparam.rb index 0a7f01da5..e5165caf6 100644 --- a/tests/test_ssl_dhparam.rb +++ b/tests/test_ssl_dhparam.rb @@ -8,7 +8,7 @@ def setup module Client def post_init - start_tls + start_tls(:ssl_version => %w(TLSv1_2)) end def ssl_handshake_completed @@ -24,7 +24,7 @@ def unbind module Server def post_init - start_tls(:dhparam => $dhparam_file, :cipher_list => "DHE,EDH") + start_tls(:dhparam => $dhparam_file, :cipher_list => "DHE,EDH", :ssl_version => %w(TLSv1_2)) end def ssl_handshake_completed @@ -35,7 +35,7 @@ def ssl_handshake_completed module NoDhServer def post_init - start_tls(:cipher_list => "DHE,EDH") + start_tls(:cipher_list => "DHE,EDH", :ssl_version => %w(TLSv1_2)) end def ssl_handshake_completed diff --git a/tests/test_ssl_ecdh_curve.rb b/tests/test_ssl_ecdh_curve.rb index c4148ce9d..53b8ae24f 100644 --- a/tests/test_ssl_ecdh_curve.rb +++ b/tests/test_ssl_ecdh_curve.rb @@ -3,10 +3,8 @@ class TestSslEcdhCurve < Test::Unit::TestCase if EM.ssl? - require 'openssl' - OPENSSL_GT_1_0 = ((OpenSSL::OPENSSL_VERSION[/ (\d+\.\d+\.\d+)[a-z]/,1] || "0.0") >= "1.1") or - (OpenSSL.const_defined?(:OPENSSL_LIBRARY_VERSION) ? - ((OpenSSL::OPENSSL_LIBRARY_VERSION[/ (\d+\.\d+\.\d+)[a-z]/,1] || "0.0") >= "1.1") : false) + SSL_LIB_VERS = EM::OPENSSL_LIBRARY_VERSION[/OpenSSL (\d+\.\d+\.\d+)/, 1] + .split('.').map(&:to_i) end module Client @@ -17,7 +15,7 @@ def post_init def ssl_handshake_completed $client_handshake_completed = true $client_cipher_name = get_cipher_name - close_connection + close_connection unless /TLSv1\.3/i =~ get_cipher_protocol end def unbind @@ -27,7 +25,11 @@ def unbind module Server def post_init - start_tls(:ecdh_curve => "prime256v1", :cipher_list => "ECDH") + if (SSL_LIB_VERS <=> [1, 1]) == 1 + start_tls(:cipher_list => "ECDH", :ssl_version => %w(TLSv1_2)) + else + start_tls(:ecdh_curve => "prime256v1", :cipher_list => "ECDH", :ssl_version => %w(TLSv1_2)) + end end def ssl_handshake_completed @@ -36,9 +38,21 @@ def ssl_handshake_completed end end + module Server1_3 + def post_init + start_tls(:cipher_list => "ECDH", :ssl_version => %w(TLSv1_3)) + end + + def ssl_handshake_completed + $server_handshake_completed = true + $server_cipher_name = get_cipher_name + close_connection if /TLSv1\.3/i =~ get_cipher_protocol + end + end + module NoCurveServer def post_init - start_tls(:cipher_list => "ECDH") + start_tls(:cipher_list => "ECDH", :ssl_version => %w(TLSv1_2)) end def ssl_handshake_completed @@ -50,7 +64,7 @@ def ssl_handshake_completed def test_no_ecdh_curve omit("No SSL") unless EM.ssl? omit_if(rbx?) - omit("OpenSSL 1.1.x (and later) auto selects curve") if OPENSSL_GT_1_0 + omit("OpenSSL 1.1.x (and later) auto selects curve") if (SSL_LIB_VERS <=> [1, 1]) == 1 $client_handshake_completed, $server_handshake_completed = false, false @@ -85,5 +99,27 @@ def test_ecdh_curve assert_match(/^(AECDH|ECDHE)/, $client_cipher_name) end + def test_ecdh_curve_tlsv1_3 + omit("No SSL") unless EM.ssl? + omit_if(EM.library_type == :pure_ruby && RUBY_VERSION < "2.3.0") + omit_if(rbx?) + omit("TLSv1_3 is unavailable") unless EM.const_defined? :EM_PROTO_TLSv1_3 + $client_handshake_completed, $server_handshake_completed = false, false + $server_cipher_name, $client_cipher_name = nil, nil + + EM.run { + EM.start_server("127.0.0.1", 16784, Server1_3) + EM.connect("127.0.0.1", 16784, Client) + } + + assert($client_handshake_completed) + assert($server_handshake_completed) + + assert($client_cipher_name.length > 0) + assert_equal($client_cipher_name, $server_cipher_name) + # see https://wiki.openssl.org/index.php/TLS1.3#Ciphersuites + # may depend on OpenSSL build options + assert_equal("TLS_AES_256_GCM_SHA384", $client_cipher_name) + end end diff --git a/tests/test_ssl_extensions.rb b/tests/test_ssl_extensions.rb index 594feb951..93f4ef5af 100644 --- a/tests/test_ssl_extensions.rb +++ b/tests/test_ssl_extensions.rb @@ -1,47 +1,59 @@ require_relative 'em_test_helper' -require 'socket' -require 'openssl' if EM.ssl? class TestSslExtensions < Test::Unit::TestCase + IP, PORT = "127.0.0.1", 16784 + module Client - def ssl_handshake_completed - $client_handshake_completed = true - close_connection - end - - def unbind - EM.stop_event_loop + def self.ssl_vers=(val = nil) + @@ssl_vers = val end def post_init - start_tls(:ssl_version => :tlsv1, :sni_hostname => 'example.com') + start_tls(:sni_hostname => 'example.com', :ssl_version => @@ssl_vers) end end module Server - def ssl_handshake_completed - $server_handshake_completed = true - $server_sni_hostname = get_sni_hostname - end + @@handshake_completed = false + @@sni_hostname = 'Not set' + + def self.handshake_completed? ; !!@@handshake_completed end + def self.sni_hostname ; @@sni_hostname end def post_init - start_tls(:ssl_version => :TLSv1) + start_tls end - end - def test_tlsext_sni_hostname - $server_handshake_completed = false + def ssl_handshake_completed + @@handshake_completed = true + @@sni_hostname = get_sni_hostname + end + end + def client_server(client = nil) EM.run do - EM.start_server("127.0.0.1", 16784, Server) - EM.connect("127.0.0.1", 16784, Client) + Client.ssl_vers = client + EM.start_server IP, PORT, Server + EM.connect IP, PORT, Client + EM.add_timer(0.3) { EM.stop_event_loop } end - - assert($server_handshake_completed) - assert_equal('example.com', $server_sni_hostname) end + + def test_tlsext_sni_hostname_1_2 + client_server %w(TLSv1_2) + assert Server.handshake_completed? + assert_equal 'example.com', Server.sni_hostname + end + + def test_tlsext_sni_hostname_1_3 + omit("TLSv1_3 is unavailable") unless SSL_AVAIL.include? "tlsv1_3" + client_server %w(TLSv1_3) + assert Server.handshake_completed? + assert_equal 'example.com', Server.sni_hostname + end + end else warn "EM built without SSL support, skipping tests in #{__FILE__}" diff --git a/tests/test_ssl_methods.rb b/tests/test_ssl_methods.rb index 3c45ee5db..022844fdb 100644 --- a/tests/test_ssl_methods.rb +++ b/tests/test_ssl_methods.rb @@ -13,6 +13,7 @@ def ssl_handshake_completed $server_cipher_bits = get_cipher_bits $server_cipher_name = get_cipher_name $server_cipher_protocol = get_cipher_protocol + EM.stop_event_loop if /TLSv1\.3/ =~ get_cipher_protocol end end @@ -27,7 +28,7 @@ def ssl_handshake_completed $client_cipher_bits = get_cipher_bits $client_cipher_name = get_cipher_name $client_cipher_protocol = get_cipher_protocol - EM.stop_event_loop + EM.stop_event_loop if /TLSv1\.3/ !~ get_cipher_protocol end end diff --git a/tests/test_ssl_protocols.rb b/tests/test_ssl_protocols.rb index 96c57c236..6b10eb6bb 100644 --- a/tests/test_ssl_protocols.rb +++ b/tests/test_ssl_protocols.rb @@ -1,321 +1,224 @@ require_relative 'em_test_helper' -require 'socket' -require 'openssl' if EM.ssl? + class TestSslProtocols < Test::Unit::TestCase + IP, PORT = "127.0.0.1", 16784 + RUBY_SSL_GE_2_1 = OpenSSL::VERSION >= '2.1' + module Client - def ssl_handshake_completed - $client_handshake_completed = true - close_connection - end + @@handshake_completed = false - def unbind - EM.stop_event_loop + def self.ssl_vers=(val = nil) + @@ssl_vers = val end - end - module Server - def ssl_handshake_completed - $server_handshake_completed = true + def self.handshake_completed? + @@handshake_completed end - end - module ClientAny - include Client def post_init - start_tls(:ssl_version => TestSslProtocols::SSL_AVAIL) + @@handshake_completed = false + if @@ssl_vers + start_tls(:ssl_version => @@ssl_vers) + else + start_tls + end end - end - module ClientDefault - include Client - def post_init - start_tls - end - end - - module ClientSSLv3 - include Client - def post_init - start_tls(:ssl_version => %w(SSLv3)) + def ssl_handshake_completed + @@handshake_completed = true end end - module ServerSSLv3 - include Server - def post_init - start_tls(:ssl_version => %w(SSLv3)) - end - end + module Server + @@handshake_completed = false - module ClientTLSv1_2 - include Client - def post_init - start_tls(:ssl_version => %w(TLSv1_2)) + def self.ssl_vers=(val = nil) + @@ssl_vers = val end - end - module ServerTLSv1_2 - include Server - def post_init - start_tls(:ssl_version => %w(TLSv1_2)) - end - end + def self.handshake_completed? ; @@handshake_completed end - module ServerTLSv1CaseInsensitive - include Server def post_init - start_tls(:ssl_version => %w(tlsv1)) - end - end - - module ServerAny - include Server - def post_init - start_tls(:ssl_version => TestSslProtocols::SSL_AVAIL) + @@handshake_completed = false + if @@ssl_vers + start_tls(:ssl_version => @@ssl_vers) + else + start_tls + end end - end - module ServerDefault - include Server - def post_init - start_tls + def ssl_handshake_completed + @@handshake_completed = true end end - module InvalidProtocol - include Client - def post_init - start_tls(:ssl_version => %w(tlsv1 badinput)) + def client_server(client = nil, server = nil) + EM.run do + Client.ssl_vers, Server.ssl_vers = client, server + EM.start_server IP, PORT, Server + EM.connect IP, PORT, Client + EM.add_timer(0.3) { EM.stop_event_loop } end end def test_invalid_ssl_version assert_raises(RuntimeError, "Unrecognized SSL/TLS Version: badinput") do - EM.run do - EM.start_server("127.0.0.1", 16784, InvalidProtocol) - EM.connect("127.0.0.1", 16784, InvalidProtocol) - end + client_server %w(tlsv1 badinput), %w(tlsv1 badinput) end end def test_any_to_v3 omit("SSLv3 is (correctly) unavailable") if EM::OPENSSL_NO_SSL3 - $client_handshake_completed, $server_handshake_completed = false, false - EM.run do - EM.start_server("127.0.0.1", 16784, ServerSSLv3) - EM.connect("127.0.0.1", 16784, ClientAny) - end - - assert($client_handshake_completed) - assert($server_handshake_completed) + client_server SSL_AVAIL, %w(SSLv3) + assert Client.handshake_completed? + assert Server.handshake_completed? end def test_any_to_tlsv1_2 - omit("TLSv1_2 is unavailable") unless SSL_AVAIL.include? "TLSv1_2" - $client_handshake_completed, $server_handshake_completed = false, false - EM.run do - EM.start_server("127.0.0.1", 16784, ServerTLSv1_2) - EM.connect("127.0.0.1", 16784, ClientAny) - end - - assert($client_handshake_completed) - assert($server_handshake_completed) + client_server SSL_AVAIL, %w(TLSv1_2) + assert Client.handshake_completed? + assert Server.handshake_completed? end def test_case_insensitivity - $client_handshake_completed, $server_handshake_completed = false, false - EM.run do - EM.start_server("127.0.0.1", 16784, ServerTLSv1CaseInsensitive) - EM.connect("127.0.0.1", 16784, ClientAny) - end - - assert($client_handshake_completed) - assert($server_handshake_completed) + lower_case = SSL_AVAIL.map { |p| p.downcase } + client_server %w(tlsv1), lower_case + assert Client.handshake_completed? + assert Server.handshake_completed? end def test_v3_to_any omit("SSLv3 is (correctly) unavailable") if EM::OPENSSL_NO_SSL3 - $client_handshake_completed, $server_handshake_completed = false, false - EM.run do - EM.start_server("127.0.0.1", 16784, ServerAny) - EM.connect("127.0.0.1", 16784, ClientSSLv3) - end - - assert($client_handshake_completed) - assert($server_handshake_completed) + client_server %w(SSLv3), SSL_AVAIL + assert Client.handshake_completed? + assert Server.handshake_completed? end def test_tlsv1_2_to_any - omit("TLSv1_2 is unavailable") unless SSL_AVAIL.include? "TLSv1_2" - $client_handshake_completed, $server_handshake_completed = false, false - EM.run do - EM.start_server("127.0.0.1", 16784, ServerAny) - EM.connect("127.0.0.1", 16784, ClientTLSv1_2) - end - - assert($client_handshake_completed) - assert($server_handshake_completed) + client_server %w(TLSv1_2), SSL_AVAIL + assert Client.handshake_completed? + assert Server.handshake_completed? end def test_v3_to_v3 omit("SSLv3 is (correctly) unavailable") if EM::OPENSSL_NO_SSL3 - $client_handshake_completed, $server_handshake_completed = false, false - EM.run do - EM.start_server("127.0.0.1", 16784, ServerSSLv3) - EM.connect("127.0.0.1", 16784, ClientSSLv3) - end - - assert($client_handshake_completed) - assert($server_handshake_completed) + client_server %w(SSLv3), %w(SSLv3) + assert Client.handshake_completed? + assert Server.handshake_completed? end def test_tlsv1_2_to_tlsv1_2 - omit("TLSv1_2 is unavailable") unless SSL_AVAIL.include? "TLSv1_2" - $client_handshake_completed, $server_handshake_completed = false, false - EM.run do - EM.start_server("127.0.0.1", 16784, ServerTLSv1_2) - EM.connect("127.0.0.1", 16784, ClientTLSv1_2) - end + client_server %w(TLSv1_2), %w(TLSv1_2) + assert Client.handshake_completed? + assert Server.handshake_completed? + end - assert($client_handshake_completed) - assert($server_handshake_completed) + def test_tlsv1_3_to_tlsv1_3 + omit("TLSv1_3 is unavailable") unless EM.const_defined? :EM_PROTO_TLSv1_3 + client_server %w(TLSv1_3), %w(TLSv1_3) + assert Client.handshake_completed? + assert Server.handshake_completed? end def test_any_to_any - $client_handshake_completed, $server_handshake_completed = false, false - EM.run do - EM.start_server("127.0.0.1", 16784, ServerAny) - EM.connect("127.0.0.1", 16784, ClientAny) - end - - assert($client_handshake_completed) - assert($server_handshake_completed) + client_server SSL_AVAIL, SSL_AVAIL + assert Client.handshake_completed? + assert Server.handshake_completed? end def test_default_to_default - $client_handshake_completed, $server_handshake_completed = false, false - EM.run do - EM.start_server("127.0.0.1", 16784, ServerDefault) - EM.connect("127.0.0.1", 16784, ClientDefault) - end - - assert($client_handshake_completed) - assert($server_handshake_completed) - end - - module ServerV3StopAfterHandshake - def post_init - start_tls(:ssl_version => %w(SSLv3)) - end - - def ssl_handshake_completed - $server_handshake_completed = true - EM.stop_event_loop - end + client_server + assert Client.handshake_completed? + assert Server.handshake_completed? end - module ServerTLSv1_2StopAfterHandshake - def post_init - start_tls(:ssl_version => %w(TLSv1_2)) + module ServerStopAfterHandshake + def self.ssl_vers=(val) + @@ssl_vers = val end - def ssl_handshake_completed - $server_handshake_completed = true - EM.stop_event_loop - end - end + def self.handshake_completed? ; @@handshake_completed end - module ServerAnyStopAfterHandshake def post_init - start_tls(:ssl_version => TestSslProtocols::SSL_AVAIL) + @@handshake_completed = false + start_tls(:ssl_version => @@ssl_vers) end def ssl_handshake_completed - $server_handshake_completed = true + @@handshake_completed = true EM.stop_event_loop end end - def test_v3_with_external_client - omit("SSLv3 is (correctly) unavailable") if EM::OPENSSL_NO_SSL3 - $server_handshake_completed = false + def external_client(ext_min, ext_max, ext_ssl, server) EM.run do setup_timeout(2) - EM.start_server("127.0.0.1", 16784, ServerV3StopAfterHandshake) + ServerStopAfterHandshake.ssl_vers = server + EM.start_server(IP, PORT, ServerStopAfterHandshake) EM.defer do - sock = TCPSocket.new("127.0.0.1", 16784) + sock = TCPSocket.new(IP, PORT) ctx = OpenSSL::SSL::SSLContext.new - ctx.ssl_version = :SSLv3_client + if RUBY_SSL_GE_2_1 + ctx.min_version = ext_min if ext_min + ctx.max_version = ext_max if ext_max + else + ctx.ssl_version = ext_ssl + end ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx) ssl.connect ssl.close rescue nil sock.close rescue nil end end + assert ServerStopAfterHandshake.handshake_completed? + end - assert($server_handshake_completed) + def test_v3_with_external_client + omit("SSLv3 is (correctly) unavailable") if EM::OPENSSL_NO_SSL3 + external_client nil, nil, :SSLv3_client, %w(SSLv3) end # Fixed Server def test_tlsv1_2_with_external_client - omit("TLSv1_2 is unavailable") unless SSL_AVAIL.include? "TLSv1_2" - $server_handshake_completed = false - EM.run do - setup_timeout(2) - EM.start_server("127.0.0.1", 16784, ServerTLSv1_2StopAfterHandshake) - EM.defer do - sock = TCPSocket.new("127.0.0.1", 16784) - ctx = OpenSSL::SSL::SSLContext.new - ctx.ssl_version = :SSLv23 - ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx) - ssl.connect - ssl.close rescue nil - sock.close rescue nil - end - end + external_client nil, nil, :SSLv23_client, %w(TLSv1_2) + end - assert($server_handshake_completed) + def test_tlsv1_3_with_external_client + omit("TLSv1_3 is unavailable") unless EM.const_defined? :EM_PROTO_TLSv1_3 + external_client nil, nil, :SSLv23_client, %w(TLSv1_3) end # Fixed Client def test_any_with_external_client_tlsv1_2 - omit("TLSv1_2 is unavailable") unless SSL_AVAIL.include? "TLSv1_2" - $server_handshake_completed = false - EM.run do - setup_timeout(2) - EM.start_server("127.0.0.1", 16784, ServerAnyStopAfterHandshake) - EM.defer do - sock = TCPSocket.new("127.0.0.1", 16784) - ctx = OpenSSL::SSL::SSLContext.new - ctx.ssl_version = :TLSv1_2 - ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx) - ssl.connect - ssl.close rescue nil - sock.close rescue nil - end - end + external_client :TLS1_2, :TLS1_2, :TLSv1_2_client, SSL_AVAIL + end - assert($server_handshake_completed) + def test_any_with_external_client_tlsv1_3 + omit("TLSv1_3 is unavailable") unless EM.const_defined? :EM_PROTO_TLSv1_3 + external_client :TLS1_3, :TLS1_3, :TLSv1_2_client, SSL_AVAIL end # Refuse a client? def test_tlsv1_2_required_with_external_client - omit("TLSv1_2 is unavailable") unless SSL_AVAIL.include? "TLSv1_2" - $server_handshake_completed = false EM.run do n = 0 EM.add_periodic_timer(0.5) do n += 1 (EM.stop rescue nil) if n == 2 end - EM.start_server("127.0.0.1", 16784, ServerTLSv1_2StopAfterHandshake) + ServerStopAfterHandshake.ssl_vers = %w(TLSv1_2) + EM.start_server(IP, PORT, ServerStopAfterHandshake) EM.defer do - sock = TCPSocket.new("127.0.0.1", 16784) + sock = TCPSocket.new(IP, PORT) ctx = OpenSSL::SSL::SSLContext.new - ctx.ssl_version = :TLSv1 + if RUBY_SSL_GE_2_1 + ctx.max_version = :TLS1_1 + else + ctx.ssl_version = :TLSv1_client + end ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx) assert_raise(OpenSSL::SSL::SSLError) { ssl.connect } ssl.close rescue nil @@ -323,10 +226,9 @@ def test_tlsv1_2_required_with_external_client EM.stop rescue nil end end - - assert(!$server_handshake_completed) + assert !ServerStopAfterHandshake.handshake_completed? end end else warn "EM built without SSL support, skipping tests in #{__FILE__}" -end +end \ No newline at end of file diff --git a/tests/test_ssl_verify.rb b/tests/test_ssl_verify.rb index 00ec14669..0c84e97e3 100644 --- a/tests/test_ssl_verify.rb +++ b/tests/test_ssl_verify.rb @@ -1,74 +1,100 @@ require_relative 'em_test_helper' class TestSslVerify < Test::Unit::TestCase - def setup - $dir = File.dirname(File.expand_path(__FILE__)) + '/' - $cert_from_file = File.read($dir+'client.crt') - end + + DIR = File.dirname(File.expand_path(__FILE__)) + '/' + CERT_FROM_FILE = File.read(DIR+'client.crt') + + IP, PORT = "127.0.0.1", 16784 module ClientNoCert - def connection_completed + @@handshake_completed = nil + def self.handshake_completed? ; !!@@handshake_completed end + + def post_init + @@handshake_completed = false start_tls() end def ssl_handshake_completed - $client_handshake_completed = true - close_connection + @@handshake_completed = true end def unbind - EM.stop_event_loop + @@handshake_completed = false end end module Client - def connection_completed - start_tls(:private_key_file => $dir+'client.key', :cert_chain_file => $dir+'client.crt') + @@handshake_completed = nil + def self.handshake_completed? ; !!@@handshake_completed end + def self.timer=(val) ; @@timer = val end + + def post_init #G connection_completed + @client_closed = false + @@handshake_completed = nil + @@timer = false + start_tls(:private_key_file => DIR+'client.key', :cert_chain_file => DIR+'client.crt') end def ssl_handshake_completed - $client_handshake_completed = true - close_connection + @@handshake_completed = true end def unbind - EM.stop_event_loop + @@handshake_completed = false unless @@timer end end module AcceptServer + @@handshake_completed = nil + def self.handshake_completed? ; !!@@handshake_completed end + def self.cert ; @@cert end + def post_init + @@cert = nil + @@handshake_completed = false start_tls(:verify_peer => true) end def ssl_verify_peer(cert) - $cert_from_server = cert + @@cert = cert true end def ssl_handshake_completed - $server_handshake_completed = true + @@handshake_completed = true end end module DenyServer + @@handshake_completed = nil + def self.handshake_completed? ; !!@@handshake_completed end + def self.cert ; @@cert end + def post_init + @@cert = nil + @@handshake_completed = nil start_tls(:verify_peer => true) end def ssl_verify_peer(cert) - $cert_from_server = cert + @@cert = cert # Do not accept the peer. This should now cause the connection to shut down without the SSL handshake being completed. false end def ssl_handshake_completed - $server_handshake_completed = true + @@handshake_completed = true end end module FailServerNoPeerCert + @@handshake_completed = nil + def self.handshake_completed? ; !!@@handshake_completed end + def post_init + @@handshake_completed = false start_tls(:verify_peer => true, :fail_if_no_peer_cert => true) end @@ -77,52 +103,52 @@ def ssl_verify_peer(cert) end def ssl_handshake_completed - $server_handshake_completed = true + @@handshake_completed = true end end + def em_run(server, client) + EM.run { + EM.start_server IP, PORT, server + EM.connect IP, PORT, client + EM.add_timer(0.3) { + Client.timer = true + EM.stop_event_loop + } + } + end + def test_fail_no_peer_cert omit("No SSL") unless EM.ssl? omit_if(rbx?) - $client_handshake_completed, $server_handshake_completed = false, false + em_run FailServerNoPeerCert, ClientNoCert - EM.run { - EM.start_server("127.0.0.1", 16784, FailServerNoPeerCert) - EM.connect("127.0.0.1", 16784, ClientNoCert) - } - - assert(!$client_handshake_completed) - assert(!$server_handshake_completed) + assert !ClientNoCert.handshake_completed? + assert !FailServerNoPeerCert.handshake_completed? end def test_accept_server omit("No SSL") unless EM.ssl? omit_if(EM.library_type == :pure_ruby) # Server has a default cert chain omit_if(rbx?) - $client_handshake_completed, $server_handshake_completed = false, false - EM.run { - EM.start_server("127.0.0.1", 16784, AcceptServer) - EM.connect("127.0.0.1", 16784, Client).instance_variable_get("@signature") - } - assert_equal($cert_from_file, $cert_from_server) - assert($client_handshake_completed) - assert($server_handshake_completed) + em_run AcceptServer, Client + + assert_equal CERT_FROM_FILE, AcceptServer.cert + assert Client.handshake_completed? + assert AcceptServer.handshake_completed? end def test_deny_server omit("No SSL") unless EM.ssl? omit_if(EM.library_type == :pure_ruby) # Server has a default cert chain omit_if(rbx?) - $client_handshake_completed, $server_handshake_completed = false, false - EM.run { - EM.start_server("127.0.0.1", 16784, DenyServer) - EM.connect("127.0.0.1", 16784, Client) - } - assert_equal($cert_from_file, $cert_from_server) - assert(!$client_handshake_completed) - assert(!$server_handshake_completed) + em_run DenyServer, Client + + assert_equal CERT_FROM_FILE, DenyServer.cert + assert !Client.handshake_completed? + assert !DenyServer.handshake_completed? end end From 9cd39b6120cdd16265ba5031d1200e5ef46f87a5 Mon Sep 17 00:00:00 2001 From: MSP-Greg Date: Thu, 17 Jan 2019 17:25:16 -0600 Subject: [PATCH 215/343] Fix intermittent tests test_httpclient2.rb - Fix intermittent issue with Windows & OpenSSL 1.0.2 and earlier test_iterator.rb - use relative time instead of absolute test_resolver.rb - Appveyor intermittent - notify on Appveyor DNS lookup retries error test_threaded_resource.rb - Travis MacOS intermittent notify Other files have assert_in_delta parameters adjusted for variances on Travis & Appveyor --- tests/test_httpclient2.rb | 6 ++++-- tests/test_inactivity_timeout.rb | 5 ++--- tests/test_iterator.rb | 10 +++++++--- tests/test_pending_connect_timeout.rb | 4 ++-- tests/test_resolver.rb | 28 ++++++++++++++++++++++++--- tests/test_threaded_resource.rb | 9 +++++++-- 6 files changed, 47 insertions(+), 15 deletions(-) diff --git a/tests/test_httpclient2.rb b/tests/test_httpclient2.rb index add5cd4ff..69450da27 100644 --- a/tests/test_httpclient2.rb +++ b/tests/test_httpclient2.rb @@ -4,7 +4,7 @@ class TestHttpClient2 < Test::Unit::TestCase class TestServer < EM::Connection end - TIMEOUT = (windows? ? 1.5 : 1) + TIMEOUT = (windows? ? 2.0 : 1) def setup @port = next_port @@ -117,8 +117,10 @@ def test_authheader def test_https_get omit("No SSL") unless EM.ssl? d = nil + # below is actually due to an issue with OpenSSL 1.0.2 and earlier with Windows + ci_windows_old = windows? and RUBY_VERSION < '2.5' EM.run { - setup_timeout(windows? ? 6 : TIMEOUT) + setup_timeout(ci_windows_old ? 9 : TIMEOUT) http = silent { EM::P::HttpClient2.connect :host => 'www.google.com', :port => 443, :tls => true } d = http.get "/" d.callback {EM.stop} diff --git a/tests/test_inactivity_timeout.rb b/tests/test_inactivity_timeout.rb index bd91459e5..41b99a88c 100644 --- a/tests/test_inactivity_timeout.rb +++ b/tests/test_inactivity_timeout.rb @@ -45,9 +45,8 @@ def test_for_real c.comm_inactivity_timeout = 0.02 } } - # busy Travis intermittently saw a bit over 0.09, but often the whole - # test only took a bit over 0.02 - assert_in_delta(0.06, (finish - start), 0.04) + # Travis can vary from 0.02 to 0.17, Appveyor maybe as low as 0.01 + assert_in_delta(0.09, (finish - start), 0.08) end else warn "EM.comm_inactivity_timeout not implemented, skipping tests in #{__FILE__}" diff --git a/tests/test_iterator.rb b/tests/test_iterator.rb index 3b7df982c..ec7724dbf 100644 --- a/tests/test_iterator.rb +++ b/tests/test_iterator.rb @@ -2,12 +2,16 @@ class TestIterator < Test::Unit::TestCase + def setup + @time0 = nil + end + # By default, format the time with tenths-of-seconds. # Some tests should ask for extra decimal places to ensure # that delays between iterations will receive a changed time. - def get_time(n=1) - time = EM.current_time - time.strftime('%H:%M:%S.') + time.tv_usec.to_s[0, n] + def get_time(n = 1) + @time0 = EM.current_time unless @time0 + sprintf "%07.#{n}f", EM.current_time - @time0 end def test_default_concurrency diff --git a/tests/test_pending_connect_timeout.rb b/tests/test_pending_connect_timeout.rb index 1db308065..4bd8f8d3b 100644 --- a/tests/test_pending_connect_timeout.rb +++ b/tests/test_pending_connect_timeout.rb @@ -37,8 +37,8 @@ def test_for_real c = EM.connect('192.0.2.0', 54321, timeout_handler) c.pending_connect_timeout = 0.2 } - - assert_in_delta(0.2, (finish - start), 0.1) + # Travis can vary from 0.10 to 0.40 + assert_in_delta(0.25, (finish - start), 0.15) end else warn "EM.pending_connect_timeout not implemented, skipping tests in #{__FILE__}" diff --git a/tests/test_resolver.rb b/tests/test_resolver.rb index 7b9428439..75155f592 100644 --- a/tests/test_resolver.rb +++ b/tests/test_resolver.rb @@ -1,6 +1,19 @@ require_relative 'em_test_helper' class TestResolver < Test::Unit::TestCase + + CI_WINDOWS = windows? and ENV['CI'].casecmp('true').zero? + + def ci_windows_retries(err) + if CI_WINDOWS and err.is_a? String and err[/retries exceeded/] + EM.stop + notify 'Intermittent Appveyor DNS error: retries exceeded' + true + else + false + end + end + def test_nameserver assert_kind_of(String, EM::DNS::Resolver.nameserver) end @@ -21,7 +34,10 @@ def test_a EM.run { d = EM::DNS::Resolver.resolve "example.com" - d.errback { assert false } + d.errback { |err| + return if ci_windows_retries err + assert false, "failed to resolve example.com: #{err}" + } d.callback { |r| assert r EM.stop @@ -51,7 +67,10 @@ def test_a_pair EM.run { d = EM::DNS::Resolver.resolve "yahoo.com" - d.errback { |err| assert false, "failed to resolve yahoo.com: #{err}" } + d.errback { |err| + return if ci_windows_retries err + assert false, "failed to resolve yahoo.com: #{err}" + } d.callback { |r| assert_kind_of(Array, r) assert r.size > 1, "returned #{r.size} results: #{r.inspect}" @@ -80,7 +99,10 @@ def test_timer_cleanup EM.run { d = EM::DNS::Resolver.resolve "example.com" - d.errback { |err| assert false, "failed to resolve example.com: #{err}" } + d.errback { |err| + return if ci_windows_retries err + assert false, "failed to resolve example.com: #{err}" + } d.callback { |r| # This isn't a great test, but it's hard to get more canonical # confirmation that the timer is cancelled diff --git a/tests/test_threaded_resource.rb b/tests/test_threaded_resource.rb index 2411c59f9..d55b7df66 100644 --- a/tests/test_threaded_resource.rb +++ b/tests/test_threaded_resource.rb @@ -19,7 +19,12 @@ def test_dispatch_completion EM.run do EM.add_timer(3) do EM.stop - fail 'Resource dispatch timed out' + if ENV['CI'].casecmp('true').zero? and RUBY_PLATFORM[/darwin/] + notify "Intermittent Travis MacOS: Resource dispatch timed out" + return + else + assert false, 'Resource dispatch timed out' + end end completion = resource.dispatch do |o| o[:foo] = :bar @@ -31,7 +36,7 @@ def test_dispatch_completion end completion.errback do |error| EM.stop - fail "Unexpected error: #{error.message}" + assert false, "Unexpected error: #{error.message}" end end assert_equal :bar, object[:foo] From a7da18ed78a60f25162c944f497154f7769f08f0 Mon Sep 17 00:00:00 2001 From: MSP-Greg Date: Wed, 16 Jan 2019 10:19:32 -0600 Subject: [PATCH 216/343] Add test/em_ssl_handlers.rb 1. Remove global variables 2. All ssl tests use handlers in em_ssl_handlers.rb 3. Utilize native EM parameter passing to handlers 4. Add more asserts for cipher_name where applicable --- tests/em_ssl_handlers.rb | 152 +++++++++++++++ tests/test_ssl_args.rb | 79 +++----- tests/test_ssl_dhparam.rb | 95 ++++------ tests/test_ssl_ecdh_curve.rb | 120 +++--------- tests/test_ssl_extensions.rb | 74 ++------ tests/test_ssl_methods.rb | 71 ++----- tests/test_ssl_protocols.rb | 349 +++++++++++++++-------------------- tests/test_ssl_verify.rb | 152 +++------------ 8 files changed, 447 insertions(+), 645 deletions(-) create mode 100644 tests/em_ssl_handlers.rb diff --git a/tests/em_ssl_handlers.rb b/tests/em_ssl_handlers.rb new file mode 100644 index 000000000..eecb94e00 --- /dev/null +++ b/tests/em_ssl_handlers.rb @@ -0,0 +1,152 @@ +# frozen_string_literal: true + +## +# EMSSLHandlers has two classes, `Client` and `Server`, and one method, +# `client_server`, which is a wrapper around +# +# EM.run do +# EM.start_server IP, PORT, s_hndlr, server +# EM.connect IP, PORT, c_hndlr, client +# end +# +# It also passes parameters to the `start_tls` call within the `post_init` +# callbacks of Client and Server. +# +# Client and Server have most standard callbacks and many 'get_*' function values +# as attributes. +# +# `Client` has a `:client_unbind` parameter, which when set to true, calls +# `EM.stop_event_loop` in the `unbind` callback. +# +# `Server` has two additional paramters. +# +# `:ssl_verify_result`, which is normally set to true/false for the +# `ssl_verify_peer` return value. If it is set to a String starting with "|RAISE|", +# the remaing string will be raised. +# +# `:stop_after_handshake`, when set to true, will close the connection and then +# call `EM.stop_event_loop`. +# +module EMSSLHandlers + + IP, PORT = "127.0.0.1", 16784 + + # is OpenSSL version >= 1.1.0 + IS_SSL_GE_1_1 = (EM::OPENSSL_LIBRARY_VERSION[/OpenSSL (\d+\.\d+\.\d+)/, 1] + .split('.').map(&:to_i) <=> [1,1]) == 1 + + # common start_tls parameters + SSL_3 = { ssl_version: %w(SSLv3) } + TLS_1_2 = { ssl_version: %w(TLSv1_2) } + TLS_1_3 = { ssl_version: %w(TLSv1_3) } + TLS_ALL = { ssl_version: Test::Unit::TestCase::SSL_AVAIL } + + module Client + def initialize(tls = nil) + @@tls = tls ? tls.dup : tls + @@handshake_completed = false + @@cert_value = nil + @@cipher_bits = nil + @@cipher_name = nil + @@cipher_protocol = nil + @@client_unbind = @@tls ? @@tls.delete(:client_unbind) : nil + end + + def self.cert_value ; @@cert_value end + def self.cipher_bits ; @@cipher_bits end + def self.cipher_name ; @@cipher_name end + def self.cipher_protocol ; @@cipher_protocol end + def self.handshake_completed? ; !!@@handshake_completed end + + def post_init + if @@tls + start_tls @@tls + else + start_tls + end + end + + def ssl_handshake_completed + @@handshake_completed = true + @@cert_value = get_peer_cert + @@cipher_bits = get_cipher_bits + @@cipher_name = get_cipher_name + @@cipher_protocol = get_cipher_protocol + + if "TLSv1.3" != @@cipher_protocol + close_connection + EM.stop_event_loop + end + end + + def unbind + EM.stop_event_loop if @@client_unbind + end + end + + module Server + def initialize(tls = nil) + @@tls = tls ? tls.dup : tls + @@handshake_completed = false + @@cert_value = nil + @@cipher_bits = nil + @@cipher_name = nil + @@cipher_protocol = nil + @@sni_hostname = "not set" + @@ssl_verify_result = @@tls ? @@tls.delete(:ssl_verify_result) : nil + @@stop_after_handshake = @@tls ? @@tls.delete(:stop_after_handshake) : nil + end + + def self.cert ; @@cert end + def self.cert_value ; @@cert_value end + def self.cipher_bits ; @@cipher_bits end + def self.cipher_name ; @@cipher_name end + def self.cipher_protocol ; @@cipher_protocol end + def self.handshake_completed? ; !!@@handshake_completed end + def self.sni_hostname ; @@sni_hostname end + + def post_init + if @@tls + start_tls @@tls + else + start_tls + end + end + + def ssl_verify_peer(cert) + @@cert = cert + if @@ssl_verify_result.is_a?(String) && @@ssl_verify_result.start_with?("|RAISE|") + raise @@ssl_verify_result.sub('|RAISE|', '') + else + @@ssl_verify_result + end + end + + def ssl_handshake_completed + @@handshake_completed = true + @@cert_value = get_peer_cert + @@cipher_bits = get_cipher_bits + @@cipher_name = get_cipher_name + @@cipher_protocol = get_cipher_protocol + + @@sni_hostname = get_sni_hostname + if ("TLSv1.3" == @@cipher_protocol) || @@stop_after_handshake + close_connection + EM.stop_event_loop + end + end + + def unbind + EM.stop_event_loop unless @@handshake_completed + end + end + + def client_server(c_hndlr = Client, s_hndlr = Server, client: nil, server: nil) + EM.run do + EM.start_server IP, PORT, s_hndlr, server + EM.connect IP, PORT, c_hndlr, client + # fail safe stop + EM.add_timer(3.0) { EM.stop_event_loop } + end + end +end if EM.ssl? \ No newline at end of file diff --git a/tests/test_ssl_args.rb b/tests/test_ssl_args.rb index 8f78fec2d..de35badeb 100644 --- a/tests/test_ssl_args.rb +++ b/tests/test_ssl_args.rb @@ -1,76 +1,41 @@ +# frozen_string_literal: true + require_relative 'em_test_helper' require 'tempfile' -module EM - def self._set_mocks - class < priv_file) + + assert_raises EM::FileNotFoundException do + client_server client: { private_key_file: priv_file } end - assert_raises(EM::FileNotFoundException) do - conn.start_tls(:cert_chain_file => cert_file) + + assert_raises EM::FileNotFoundException do + client_server client: { cert_chain_file: cert_file } end - assert_raises(EM::FileNotFoundException) do - conn.start_tls(:private_key_file => priv_file, :cert_chain_file => cert_file) + + assert_raises EM::FileNotFoundException do + client_server client: { private_key_file: priv_file, cert_chain_file: cert_file } end end - - def test_tls_params_file_does_exist - priv_file = Tempfile.new('em_test') - cert_file = Tempfile.new('em_test') - priv_file_path = priv_file.path - cert_file_path = cert_file.path - conn = EM::Connection.new('foo') - params = {:private_key_file => priv_file_path, :cert_chain_file => cert_file_path} + + def _test_tls_params_file_improper + priv_file_path = Tempfile.new('em_test').path + cert_file_path = Tempfile.new('em_test').path + params = { private_key_file: priv_file_path, + cert_chain_file: cert_file_path } begin - conn.start_tls params + client_server client: params rescue Object - assert(false, 'should not have raised an exception') + assert false, 'should not have raised an exception' end end end if EM.ssl? diff --git a/tests/test_ssl_dhparam.rb b/tests/test_ssl_dhparam.rb index e5165caf6..58d834b14 100644 --- a/tests/test_ssl_dhparam.rb +++ b/tests/test_ssl_dhparam.rb @@ -1,84 +1,57 @@ +# frozen_string_literal: true + require_relative 'em_test_helper' class TestSslDhParam < Test::Unit::TestCase - def setup - $dir = File.dirname(File.expand_path(__FILE__)) + '/' - $dhparam_file = File.join($dir, 'dhparam.pem') - end - - module Client - def post_init - start_tls(:ssl_version => %w(TLSv1_2)) - end - def ssl_handshake_completed - $client_handshake_completed = true - $client_cipher_name = get_cipher_name - close_connection - end + require_relative 'em_ssl_handlers' + include EMSSLHandlers - def unbind - EM.stop_event_loop - end - end + DH_PARAM_FILE = File.join(__dir__, 'dhparam.pem') - module Server - def post_init - start_tls(:dhparam => $dhparam_file, :cipher_list => "DHE,EDH", :ssl_version => %w(TLSv1_2)) - end + DH_1_2 = { cipher_list: "DHE,EDH", ssl_version: %w(TLSv1_2) } + CLIENT_1_2 = { client_unbind: true, ssl_version: %w(TLSv1_2) } - def ssl_handshake_completed - $server_handshake_completed = true - $server_cipher_name = get_cipher_name - end - end + def test_no_dhparam + omit_if(EM.library_type == :pure_ruby) # DH will work with defaults + omit_if(rbx?) - module NoDhServer - def post_init - start_tls(:cipher_list => "DHE,EDH", :ssl_version => %w(TLSv1_2)) - end + client_server client: CLIENT_1_2, server: DH_1_2 - def ssl_handshake_completed - $server_handshake_completed = true - $server_cipher_name = get_cipher_name - end + refute Client.handshake_completed? + refute Server.handshake_completed? end - def test_no_dhparam - omit("No SSL") unless EM.ssl? - omit_if(EM.library_type == :pure_ruby) # DH will work with defaults + def test_dhparam_1_2 omit_if(rbx?) - $client_handshake_completed, $server_handshake_completed = false, false - $server_cipher_name, $client_cipher_name = nil, nil + client_server client: CLIENT_1_2, server: DH_1_2.merge(dhparam: DH_PARAM_FILE) + + assert Client.handshake_completed? + assert Server.handshake_completed? - EM.run { - EM.start_server("127.0.0.1", 16784, NoDhServer) - EM.connect("127.0.0.1", 16784, Client) - } + assert Client.cipher_name.length > 0 + assert_equal Client.cipher_name, Server.cipher_name - assert(!$client_handshake_completed) - assert(!$server_handshake_completed) + assert_match(/^(DHE|EDH)/, Client.cipher_name) end - def test_dhparam - omit("No SSL") unless EM.ssl? + def test_dhparam_1_3 omit_if(rbx?) + omit("TLSv1_3 is unavailable") unless EM.const_defined? :EM_PROTO_TLSv1_3 - $client_handshake_completed, $server_handshake_completed = false, false - $server_cipher_name, $client_cipher_name = nil, nil - - EM.run { - EM.start_server("127.0.0.1", 16784, Server) - EM.connect("127.0.0.1", 16784, Client) - } + client = { client_unbind: true, ssl_version: %w(TLSv1_3) } + server = { dhparam: DH_PARAM_FILE, cipher_list: "DHE,EDH", ssl_version: %w(TLSv1_3) } + client_server client: client, server: server - assert($client_handshake_completed) - assert($server_handshake_completed) + assert Client.handshake_completed? + assert Server.handshake_completed? - assert($client_cipher_name.length > 0) - assert_equal($client_cipher_name, $server_cipher_name) + assert Client.cipher_name.length > 0 + assert_equal Client.cipher_name, Server.cipher_name - assert_match(/^(DHE|EDH)/, $client_cipher_name) + # see https://wiki.openssl.org/index.php/TLS1.3#Ciphersuites + # may depend on OpenSSL build options + assert_equal "TLS_AES_256_GCM_SHA384", Client.cipher_name end -end +end if EM.ssl? diff --git a/tests/test_ssl_ecdh_curve.rb b/tests/test_ssl_ecdh_curve.rb index 53b8ae24f..98384d1be 100644 --- a/tests/test_ssl_ecdh_curve.rb +++ b/tests/test_ssl_ecdh_curve.rb @@ -1,125 +1,57 @@ +# frozen_string_literal: true + require_relative 'em_test_helper' class TestSslEcdhCurve < Test::Unit::TestCase - if EM.ssl? - SSL_LIB_VERS = EM::OPENSSL_LIBRARY_VERSION[/OpenSSL (\d+\.\d+\.\d+)/, 1] - .split('.').map(&:to_i) - end - - module Client - def post_init - start_tls - end - - def ssl_handshake_completed - $client_handshake_completed = true - $client_cipher_name = get_cipher_name - close_connection unless /TLSv1\.3/i =~ get_cipher_protocol - end - - def unbind - EM.stop_event_loop - end - end - - module Server - def post_init - if (SSL_LIB_VERS <=> [1, 1]) == 1 - start_tls(:cipher_list => "ECDH", :ssl_version => %w(TLSv1_2)) - else - start_tls(:ecdh_curve => "prime256v1", :cipher_list => "ECDH", :ssl_version => %w(TLSv1_2)) - end - end - - def ssl_handshake_completed - $server_handshake_completed = true - $server_cipher_name = get_cipher_name - end - end - - module Server1_3 - def post_init - start_tls(:cipher_list => "ECDH", :ssl_version => %w(TLSv1_3)) - end - - def ssl_handshake_completed - $server_handshake_completed = true - $server_cipher_name = get_cipher_name - close_connection if /TLSv1\.3/i =~ get_cipher_protocol - end - end - - module NoCurveServer - def post_init - start_tls(:cipher_list => "ECDH", :ssl_version => %w(TLSv1_2)) - end - - def ssl_handshake_completed - $server_handshake_completed = true - $server_cipher_name = get_cipher_name - end - end + require_relative 'em_ssl_handlers' + include EMSSLHandlers def test_no_ecdh_curve - omit("No SSL") unless EM.ssl? omit_if(rbx?) - omit("OpenSSL 1.1.x (and later) auto selects curve") if (SSL_LIB_VERS <=> [1, 1]) == 1 + omit("OpenSSL 1.1.x (and later) auto selects curve") if IS_SSL_GE_1_1 - $client_handshake_completed, $server_handshake_completed = false, false + client_server server: { cipher_list: "ECDH", ssl_version: %w(TLSv1_2) } - EM.run { - EM.start_server("127.0.0.1", 16784, NoCurveServer) - EM.connect("127.0.0.1", 16784, Client) - } - - assert(!$client_handshake_completed) - assert(!$server_handshake_completed) + refute Client.handshake_completed? + refute Server.handshake_completed? end - def test_ecdh_curve - omit("No SSL") unless EM.ssl? + def test_ecdh_curve_tlsv1_2 omit_if(EM.library_type == :pure_ruby && RUBY_VERSION < "2.3.0") omit_if(rbx?) - $client_handshake_completed, $server_handshake_completed = false, false - $server_cipher_name, $client_cipher_name = nil, nil + server = { cipher_list: "ECDH", ssl_version: %w(TLSv1_2) } + server.merge!(ecdh_curve: "prime256v1") unless IS_SSL_GE_1_1 - EM.run { - EM.start_server("127.0.0.1", 16784, Server) - EM.connect("127.0.0.1", 16784, Client) - } + client_server server: server - assert($client_handshake_completed) - assert($server_handshake_completed) + assert Client.handshake_completed? + assert Server.handshake_completed? - assert($client_cipher_name.length > 0) - assert_equal($client_cipher_name, $server_cipher_name) + assert Client.cipher_name.length > 0 + assert_equal Client.cipher_name, Server.cipher_name - assert_match(/^(AECDH|ECDHE)/, $client_cipher_name) + assert_match(/^(AECDH|ECDHE)/, Client.cipher_name) end def test_ecdh_curve_tlsv1_3 - omit("No SSL") unless EM.ssl? omit_if(EM.library_type == :pure_ruby && RUBY_VERSION < "2.3.0") omit_if(rbx?) omit("TLSv1_3 is unavailable") unless EM.const_defined? :EM_PROTO_TLSv1_3 - $client_handshake_completed, $server_handshake_completed = false, false - $server_cipher_name, $client_cipher_name = nil, nil + tls = { cipher_list: "ECDH", ssl_version: %w(TLSv1_3) } + + client_server server: tls - EM.run { - EM.start_server("127.0.0.1", 16784, Server1_3) - EM.connect("127.0.0.1", 16784, Client) - } + assert Client.handshake_completed? + assert Server.handshake_completed? - assert($client_handshake_completed) - assert($server_handshake_completed) + assert Client.cipher_name.length > 0 + assert_equal Client.cipher_name, Server.cipher_name - assert($client_cipher_name.length > 0) - assert_equal($client_cipher_name, $server_cipher_name) # see https://wiki.openssl.org/index.php/TLS1.3#Ciphersuites # may depend on OpenSSL build options - assert_equal("TLS_AES_256_GCM_SHA384", $client_cipher_name) + assert_equal "TLS_AES_256_GCM_SHA384", Client.cipher_name end -end +end if EM.ssl? diff --git a/tests/test_ssl_extensions.rb b/tests/test_ssl_extensions.rb index 93f4ef5af..2c1a877b9 100644 --- a/tests/test_ssl_extensions.rb +++ b/tests/test_ssl_extensions.rb @@ -1,60 +1,24 @@ -require_relative 'em_test_helper' - -if EM.ssl? - class TestSslExtensions < Test::Unit::TestCase - - IP, PORT = "127.0.0.1", 16784 - - module Client - def self.ssl_vers=(val = nil) - @@ssl_vers = val - end +# frozen_string_literal: true - def post_init - start_tls(:sni_hostname => 'example.com', :ssl_version => @@ssl_vers) - end - end - - module Server - @@handshake_completed = false - @@sni_hostname = 'Not set' - - def self.handshake_completed? ; !!@@handshake_completed end - def self.sni_hostname ; @@sni_hostname end +require_relative 'em_test_helper' - def post_init - start_tls - end +class TestSslExtensions < Test::Unit::TestCase - def ssl_handshake_completed - @@handshake_completed = true - @@sni_hostname = get_sni_hostname - end - end + require_relative 'em_ssl_handlers' + include EMSSLHandlers - def client_server(client = nil) - EM.run do - Client.ssl_vers = client - EM.start_server IP, PORT, Server - EM.connect IP, PORT, Client - EM.add_timer(0.3) { EM.stop_event_loop } - end - end - - def test_tlsext_sni_hostname_1_2 - client_server %w(TLSv1_2) - assert Server.handshake_completed? - assert_equal 'example.com', Server.sni_hostname - end - - def test_tlsext_sni_hostname_1_3 - omit("TLSv1_3 is unavailable") unless SSL_AVAIL.include? "tlsv1_3" - client_server %w(TLSv1_3) - assert Server.handshake_completed? - assert_equal 'example.com', Server.sni_hostname - end - + def test_tlsext_sni_hostname_1_2 + client = { sni_hostname: 'example.com', ssl_version: %w(TLSv1_2) } + client_server client: client + assert Server.handshake_completed? + assert_equal 'example.com', Server.sni_hostname + end + + def test_tlsext_sni_hostname_1_3 + omit("TLSv1_3 is unavailable") unless EM.const_defined? :EM_PROTO_TLSv1_3 + client = { sni_hostname: 'example.com', ssl_version: %w(TLSv1_3) } + client_server client: client + assert Server.handshake_completed? + assert_equal 'example.com', Server.sni_hostname end -else - warn "EM built without SSL support, skipping tests in #{__FILE__}" -end +end if EM.ssl? diff --git a/tests/test_ssl_methods.rb b/tests/test_ssl_methods.rb index 022844fdb..a1793d87b 100644 --- a/tests/test_ssl_methods.rb +++ b/tests/test_ssl_methods.rb @@ -1,66 +1,31 @@ +# frozen_string_literal: true + require_relative 'em_test_helper' class TestSSLMethods < Test::Unit::TestCase - module ServerHandler - def post_init - start_tls - end - - def ssl_handshake_completed - $server_called_back = true - $server_cert_value = get_peer_cert - $server_cipher_bits = get_cipher_bits - $server_cipher_name = get_cipher_name - $server_cipher_protocol = get_cipher_protocol - EM.stop_event_loop if /TLSv1\.3/ =~ get_cipher_protocol - end - end - - module ClientHandler - def post_init - start_tls - end - - def ssl_handshake_completed - $client_called_back = true - $client_cert_value = get_peer_cert - $client_cipher_bits = get_cipher_bits - $client_cipher_name = get_cipher_name - $client_cipher_protocol = get_cipher_protocol - EM.stop_event_loop if /TLSv1\.3/ !~ get_cipher_protocol - end - end + require_relative 'em_ssl_handlers' + include EMSSLHandlers def test_ssl_methods - omit("No SSL") unless EM.ssl? omit_if(rbx?) - $server_called_back, $client_called_back = false, false - $server_cert_value, $client_cert_value = nil, nil - $server_cipher_bits, $client_cipher_bits = nil, nil - $server_cipher_name, $client_cipher_name = nil, nil - $server_cipher_protocol, $client_cipher_protocol = nil, nil - EM.run { - EM.start_server("127.0.0.1", 9999, ServerHandler) - EM.connect("127.0.0.1", 9999, ClientHandler) - } + client_server + + assert Server.handshake_completed? + assert Client.handshake_completed? - assert($server_called_back) - assert($client_called_back) + assert Server.cert_value.is_a? NilClass + assert Client.cert_value.is_a? String - assert($server_cert_value.is_a?(NilClass)) - assert($client_cert_value.is_a?(String)) + assert Client.cipher_bits > 0 + assert_equal Client.cipher_bits, Server.cipher_bits - assert($client_cipher_bits > 0) - assert_equal($client_cipher_bits, $server_cipher_bits) + assert Client.cipher_name.length > 0 + assert_match(/AES/, Client.cipher_name) + assert_equal Client.cipher_name, Server.cipher_name - assert($client_cipher_name.length > 0) - assert_match(/AES/, $client_cipher_name) - assert_equal($client_cipher_name, $server_cipher_name) - - assert_match(/TLS/, $client_cipher_protocol) - assert_equal($client_cipher_protocol, $server_cipher_protocol) + assert Client.cipher_protocol.start_with? "TLS" + assert_equal Client.cipher_protocol, Server.cipher_protocol end - -end +end if EM.ssl? diff --git a/tests/test_ssl_protocols.rb b/tests/test_ssl_protocols.rb index 6b10eb6bb..9c4bd011c 100644 --- a/tests/test_ssl_protocols.rb +++ b/tests/test_ssl_protocols.rb @@ -1,234 +1,187 @@ -require_relative 'em_test_helper' - -if EM.ssl? - - class TestSslProtocols < Test::Unit::TestCase - - IP, PORT = "127.0.0.1", 16784 - RUBY_SSL_GE_2_1 = OpenSSL::VERSION >= '2.1' - - module Client - @@handshake_completed = false - - def self.ssl_vers=(val = nil) - @@ssl_vers = val - end - - def self.handshake_completed? - @@handshake_completed - end - - def post_init - @@handshake_completed = false - if @@ssl_vers - start_tls(:ssl_version => @@ssl_vers) - else - start_tls - end - end - - def ssl_handshake_completed - @@handshake_completed = true - end - end - - module Server - @@handshake_completed = false +# frozen_string_literal: true - def self.ssl_vers=(val = nil) - @@ssl_vers = val - end - - def self.handshake_completed? ; @@handshake_completed end +require_relative 'em_test_helper' - def post_init - @@handshake_completed = false - if @@ssl_vers - start_tls(:ssl_version => @@ssl_vers) - else - start_tls - end - end +# For OpenSSL 1.1.0 and later, cipher_protocol returns single cipher, older +# versions return "TLSv1/SSLv3" +# TLSv1.1 handshake_completed Server/Client callback order is reversed from +# older protocols - def ssl_handshake_completed - @@handshake_completed = true - end - end +class TestSslProtocols < Test::Unit::TestCase - def client_server(client = nil, server = nil) - EM.run do - Client.ssl_vers, Server.ssl_vers = client, server - EM.start_server IP, PORT, Server - EM.connect IP, PORT, Client - EM.add_timer(0.3) { EM.stop_event_loop } - end - end + require_relative 'em_ssl_handlers' + include EMSSLHandlers - def test_invalid_ssl_version - assert_raises(RuntimeError, "Unrecognized SSL/TLS Version: badinput") do - client_server %w(tlsv1 badinput), %w(tlsv1 badinput) - end - end + RUBY_SSL_GE_2_1 = OpenSSL::VERSION >= '2.1' - def test_any_to_v3 - omit("SSLv3 is (correctly) unavailable") if EM::OPENSSL_NO_SSL3 - client_server SSL_AVAIL, %w(SSLv3) - assert Client.handshake_completed? - assert Server.handshake_completed? + def test_invalid_ssl_version + assert_raises(RuntimeError, "Unrecognized SSL/TLS Version: badinput") do + client = { ssl_version: %w(tlsv1 badinput) } + server = { ssl_version: %w(tlsv1 badinput) } + client_server client: client, server: server end + end - def test_any_to_tlsv1_2 - client_server SSL_AVAIL, %w(TLSv1_2) - assert Client.handshake_completed? - assert Server.handshake_completed? - end + def test_any_to_v3 + omit("SSLv3 is (correctly) unavailable") if EM::OPENSSL_NO_SSL3 + client_server client: TLS_ALL, server: SSL_3 + assert Client.handshake_completed? + assert Server.handshake_completed? + end - def test_case_insensitivity - lower_case = SSL_AVAIL.map { |p| p.downcase } - client_server %w(tlsv1), lower_case - assert Client.handshake_completed? - assert Server.handshake_completed? + def test_any_to_tlsv1_2 + client_server client: TLS_ALL, server: TLS_1_2 + assert Client.handshake_completed? + assert Server.handshake_completed? + if IS_SSL_GE_1_1 + assert_equal "TLSv1.2", Client.cipher_protocol end + end - def test_v3_to_any - omit("SSLv3 is (correctly) unavailable") if EM::OPENSSL_NO_SSL3 - client_server %w(SSLv3), SSL_AVAIL - assert Client.handshake_completed? - assert Server.handshake_completed? - end + def test_case_insensitivity + lower_case = SSL_AVAIL.map { |p| p.downcase } + client = { ssl_version: %w(tlsv1_2) } + server = { ssl_version: lower_case } + client_server client: client, server: server + assert Client.handshake_completed? + assert Server.handshake_completed? + end - def test_tlsv1_2_to_any - client_server %w(TLSv1_2), SSL_AVAIL - assert Client.handshake_completed? - assert Server.handshake_completed? - end + def test_v3_to_any + omit("SSLv3 is (correctly) unavailable") if EM::OPENSSL_NO_SSL3 + client_server client: SSL_3, server: TLS_ALL + assert Client.handshake_completed? + assert Server.handshake_completed? + end - def test_v3_to_v3 - omit("SSLv3 is (correctly) unavailable") if EM::OPENSSL_NO_SSL3 - client_server %w(SSLv3), %w(SSLv3) - assert Client.handshake_completed? - assert Server.handshake_completed? + def test_tlsv1_2_to_any + client_server client: TLS_1_2, server: TLS_ALL + assert Client.handshake_completed? + assert Server.handshake_completed? + if IS_SSL_GE_1_1 + assert_equal "TLSv1.2", Server.cipher_protocol end + end - def test_tlsv1_2_to_tlsv1_2 - client_server %w(TLSv1_2), %w(TLSv1_2) - assert Client.handshake_completed? - assert Server.handshake_completed? - end + def test_v3_to_v3 + omit("SSLv3 is (correctly) unavailable") if EM::OPENSSL_NO_SSL3 + client_server client: SSL_3, server: SSL_3 + assert Client.handshake_completed? + assert Server.handshake_completed? + end - def test_tlsv1_3_to_tlsv1_3 - omit("TLSv1_3 is unavailable") unless EM.const_defined? :EM_PROTO_TLSv1_3 - client_server %w(TLSv1_3), %w(TLSv1_3) - assert Client.handshake_completed? - assert Server.handshake_completed? + def test_tlsv1_2_to_tlsv1_2 + client_server client: TLS_1_2, server: TLS_1_2 + assert Client.handshake_completed? + assert Server.handshake_completed? + if IS_SSL_GE_1_1 + assert_equal "TLSv1.2", Client.cipher_protocol + assert_equal "TLSv1.2", Server.cipher_protocol end + end - def test_any_to_any - client_server SSL_AVAIL, SSL_AVAIL - assert Client.handshake_completed? - assert Server.handshake_completed? - end + def test_tlsv1_3_to_tlsv1_3 + omit("TLSv1_3 is unavailable") unless EM.const_defined? :EM_PROTO_TLSv1_3 + client_server client: TLS_1_3, server: TLS_1_3 + assert Client.handshake_completed? + assert Server.handshake_completed? + assert_equal "TLSv1.3", Client.cipher_protocol + assert_equal "TLSv1.3", Server.cipher_protocol + end - def test_default_to_default - client_server - assert Client.handshake_completed? - assert Server.handshake_completed? + def test_any_to_any + client_server client: TLS_ALL, server: TLS_ALL + assert Client.handshake_completed? + assert Server.handshake_completed? + if IS_SSL_GE_1_1 + best_protocol = SSL_AVAIL.last.tr "_", "." + assert_equal best_protocol, Client.cipher_protocol + assert_equal best_protocol, Server.cipher_protocol end + end - module ServerStopAfterHandshake - def self.ssl_vers=(val) - @@ssl_vers = val - end - - def self.handshake_completed? ; @@handshake_completed end - - def post_init - @@handshake_completed = false - start_tls(:ssl_version => @@ssl_vers) - end - - def ssl_handshake_completed - @@handshake_completed = true - EM.stop_event_loop - end + def test_default_to_default + client_server + assert Client.handshake_completed? + assert Server.handshake_completed? + if IS_SSL_GE_1_1 + best_protocol = SSL_AVAIL.last.tr "_", "." + assert_equal best_protocol, Client.cipher_protocol + assert_equal best_protocol, Server.cipher_protocol end + end - def external_client(ext_min, ext_max, ext_ssl, server) - EM.run do - setup_timeout(2) - ServerStopAfterHandshake.ssl_vers = server - EM.start_server(IP, PORT, ServerStopAfterHandshake) - EM.defer do - sock = TCPSocket.new(IP, PORT) - ctx = OpenSSL::SSL::SSLContext.new - if RUBY_SSL_GE_2_1 - ctx.min_version = ext_min if ext_min - ctx.max_version = ext_max if ext_max - else - ctx.ssl_version = ext_ssl - end - ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx) - ssl.connect - ssl.close rescue nil - sock.close rescue nil + def external_client(ext_min, ext_max, ext_ssl, server) + EM.run do +# setup_timeout 2 + EM.start_server IP, PORT, Server, server.merge( { stop_after_handshake: true} ) + EM.defer do + sock = TCPSocket.new IP, PORT + ctx = OpenSSL::SSL::SSLContext.new + if RUBY_SSL_GE_2_1 + ctx.min_version = ext_min if ext_min + ctx.max_version = ext_max if ext_max + else + ctx.ssl_version = ext_ssl end + ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx) + ssl.connect + ssl.close rescue nil + sock.close rescue nil end - assert ServerStopAfterHandshake.handshake_completed? end + assert Server.handshake_completed? + end - def test_v3_with_external_client - omit("SSLv3 is (correctly) unavailable") if EM::OPENSSL_NO_SSL3 - external_client nil, nil, :SSLv3_client, %w(SSLv3) - end + def test_v3_with_external_client + omit("SSLv3 is (correctly) unavailable") if EM::OPENSSL_NO_SSL3 + external_client nil, nil, :SSLv3_client, SSL_3 + end - # Fixed Server - def test_tlsv1_2_with_external_client - external_client nil, nil, :SSLv23_client, %w(TLSv1_2) - end + # Fixed Server + def test_tlsv1_2_with_external_client + external_client nil, nil, :SSLv23_client, TLS_1_2 + end - def test_tlsv1_3_with_external_client - omit("TLSv1_3 is unavailable") unless EM.const_defined? :EM_PROTO_TLSv1_3 - external_client nil, nil, :SSLv23_client, %w(TLSv1_3) - end + def test_tlsv1_3_with_external_client + omit("TLSv1_3 is unavailable") unless EM.const_defined? :EM_PROTO_TLSv1_3 + external_client nil, nil, :SSLv23_client, TLS_1_3 + end - # Fixed Client - def test_any_with_external_client_tlsv1_2 - external_client :TLS1_2, :TLS1_2, :TLSv1_2_client, SSL_AVAIL - end + # Fixed Client + def test_any_with_external_client_tlsv1_2 + external_client :TLS1_2, :TLS1_2, :TLSv1_2_client, TLS_ALL + end - def test_any_with_external_client_tlsv1_3 - omit("TLSv1_3 is unavailable") unless EM.const_defined? :EM_PROTO_TLSv1_3 - external_client :TLS1_3, :TLS1_3, :TLSv1_2_client, SSL_AVAIL - end + def test_any_with_external_client_tlsv1_3 + omit("TLSv1_3 is unavailable") unless EM.const_defined? :EM_PROTO_TLSv1_3 + external_client :TLS1_3, :TLS1_3, :TLSv1_2_client, TLS_ALL + end - # Refuse a client? - def test_tlsv1_2_required_with_external_client - EM.run do - n = 0 - EM.add_periodic_timer(0.5) do - n += 1 - (EM.stop rescue nil) if n == 2 - end - ServerStopAfterHandshake.ssl_vers = %w(TLSv1_2) - EM.start_server(IP, PORT, ServerStopAfterHandshake) - EM.defer do - sock = TCPSocket.new(IP, PORT) - ctx = OpenSSL::SSL::SSLContext.new - if RUBY_SSL_GE_2_1 - ctx.max_version = :TLS1_1 - else - ctx.ssl_version = :TLSv1_client - end - ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx) - assert_raise(OpenSSL::SSL::SSLError) { ssl.connect } - ssl.close rescue nil - sock.close rescue nil - EM.stop rescue nil + # Refuse a client? + def test_tlsv1_2_required_with_external_client + EM.run do + n = 0 + EM.add_periodic_timer(0.5) do + n += 1 + (EM.stop rescue nil) if n == 2 + end + EM.start_server IP, PORT, Server, TLS_1_2.merge( { stop_after_handshake: true} ) + EM.defer do + sock = TCPSocket.new IP, PORT + ctx = OpenSSL::SSL::SSLContext.new + if RUBY_SSL_GE_2_1 + ctx.max_version = :TLS1_1 + else + ctx.ssl_version = :TLSv1_client end + ssl = OpenSSL::SSL::SSLSocket.new sock, ctx + assert_raise(OpenSSL::SSL::SSLError) { ssl.connect } + ssl.close rescue nil + sock.close rescue nil + EM.stop rescue nil end - assert !ServerStopAfterHandshake.handshake_completed? end + refute Server.handshake_completed? end -else - warn "EM built without SSL support, skipping tests in #{__FILE__}" -end \ No newline at end of file +end if EM.ssl? diff --git a/tests/test_ssl_verify.rb b/tests/test_ssl_verify.rb index 0c84e97e3..31d51f7a1 100644 --- a/tests/test_ssl_verify.rb +++ b/tests/test_ssl_verify.rb @@ -1,154 +1,52 @@ +# frozen_string_literal: true + require_relative 'em_test_helper' class TestSslVerify < Test::Unit::TestCase - DIR = File.dirname(File.expand_path(__FILE__)) + '/' - CERT_FROM_FILE = File.read(DIR+'client.crt') - - IP, PORT = "127.0.0.1", 16784 - - module ClientNoCert - @@handshake_completed = nil - def self.handshake_completed? ; !!@@handshake_completed end + require_relative 'em_ssl_handlers' + include EMSSLHandlers - def post_init - @@handshake_completed = false - start_tls() - end - - def ssl_handshake_completed - @@handshake_completed = true - end - - def unbind - @@handshake_completed = false - end - end + CERT_FROM_FILE = File.read "#{__dir__}/client.crt" - module Client - @@handshake_completed = nil - def self.handshake_completed? ; !!@@handshake_completed end - def self.timer=(val) ; @@timer = val end - - def post_init #G connection_completed - @client_closed = false - @@handshake_completed = nil - @@timer = false - start_tls(:private_key_file => DIR+'client.key', :cert_chain_file => DIR+'client.crt') - end - - def ssl_handshake_completed - @@handshake_completed = true - end - - def unbind - @@handshake_completed = false unless @@timer - end - end - - module AcceptServer - @@handshake_completed = nil - def self.handshake_completed? ; !!@@handshake_completed end - def self.cert ; @@cert end - - def post_init - @@cert = nil - @@handshake_completed = false - start_tls(:verify_peer => true) - end - - def ssl_verify_peer(cert) - @@cert = cert - true - end - - def ssl_handshake_completed - @@handshake_completed = true - end - end - - module DenyServer - @@handshake_completed = nil - def self.handshake_completed? ; !!@@handshake_completed end - def self.cert ; @@cert end - - def post_init - @@cert = nil - @@handshake_completed = nil - start_tls(:verify_peer => true) - end - - def ssl_verify_peer(cert) - @@cert = cert - # Do not accept the peer. This should now cause the connection to shut down without the SSL handshake being completed. - false - end - - def ssl_handshake_completed - @@handshake_completed = true - end - end - - module FailServerNoPeerCert - @@handshake_completed = nil - def self.handshake_completed? ; !!@@handshake_completed end - - def post_init - @@handshake_completed = false - start_tls(:verify_peer => true, :fail_if_no_peer_cert => true) - end - - def ssl_verify_peer(cert) - raise "Verify peer should not get called for a client without a certificate" - end - - def ssl_handshake_completed - @@handshake_completed = true - end - end - - def em_run(server, client) - EM.run { - EM.start_server IP, PORT, server - EM.connect IP, PORT, client - EM.add_timer(0.3) { - Client.timer = true - EM.stop_event_loop - } - } - end + CLIENT_CERT = { private_key_file: "#{__dir__}/client.key", + cert_chain_file: "#{__dir__}/client.crt" } def test_fail_no_peer_cert - omit("No SSL") unless EM.ssl? omit_if(rbx?) - em_run FailServerNoPeerCert, ClientNoCert + server = { verify_peer: true, fail_if_no_peer_cert: true, + ssl_verify_result: "|RAISE|Verify peer should not get called for a client without a certificate" } + + client_server Client, Server, server: server - assert !ClientNoCert.handshake_completed? - assert !FailServerNoPeerCert.handshake_completed? + refute Client.handshake_completed? unless "TLSv1.3" == Client.cipher_protocol + refute Server.handshake_completed? end def test_accept_server - omit("No SSL") unless EM.ssl? omit_if(EM.library_type == :pure_ruby) # Server has a default cert chain omit_if(rbx?) - em_run AcceptServer, Client + server = { verify_peer: true, ssl_verify_result: true } - assert_equal CERT_FROM_FILE, AcceptServer.cert + client_server Client, Server, client: CLIENT_CERT, server: server + + assert_equal CERT_FROM_FILE, Server.cert assert Client.handshake_completed? - assert AcceptServer.handshake_completed? + assert Server.handshake_completed? end def test_deny_server - omit("No SSL") unless EM.ssl? omit_if(EM.library_type == :pure_ruby) # Server has a default cert chain omit_if(rbx?) - em_run DenyServer, Client + server = { verify_peer: true, ssl_verify_result: false } + + client_server Client, Server, client: CLIENT_CERT, server: server - assert_equal CERT_FROM_FILE, DenyServer.cert - assert !Client.handshake_completed? - assert !DenyServer.handshake_completed? + assert_equal CERT_FROM_FILE, Server.cert + refute Client.handshake_completed? unless "TLSv1.3" == Client.cipher_protocol + refute Server.handshake_completed? end -end +end if EM.ssl? From c535d3c6a6ec752d15a53a7f1a9a2e9785102517 Mon Sep 17 00:00:00 2001 From: MSP-Greg Date: Wed, 16 Jan 2019 21:25:40 -0600 Subject: [PATCH 217/343] Remove warnings in tests, add SSL message --- tests/em_test_helper.rb | 12 +++++++++--- tests/test_io_streamer.rb | 23 +++++++++++++++++------ 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/tests/em_test_helper.rb b/tests/em_test_helper.rb index 4df5fcc62..eab3038b3 100644 --- a/tests/em_test_helper.rb +++ b/tests/em_test_helper.rb @@ -1,12 +1,16 @@ require 'em/pure_ruby' if ENV['EM_PURE_RUBY'] require 'eventmachine' -require 'test/unit' require 'rbconfig' require 'socket' +# verbose fun is to stop warnings when loading test-unit 3.2.9 in trunk +verbose, $VERBOSE = $VERBOSE, nil +require 'test/unit' +$VERBOSE = verbose + class Test::Unit::TestCase - # below outputs in to console on load + # below outputs to console on load # SSL_AVAIL is used by SSL tests puts "", RUBY_DESCRIPTION puts "\nEM.library_type #{EM.library_type.to_s.ljust(16)} EM.ssl? #{EM.ssl?}" @@ -28,6 +32,9 @@ class Test::Unit::TestCase temp.sort! puts " SSL_AVAIL: #{temp.join(' ')}", "" SSL_AVAIL = temp.freeze + else + puts "\nEventMachine is not built with OpenSSL support, skipping tests in", + "files tests/test_ssl_*.rb" end class EMTestTimeout < StandardError ; end @@ -184,5 +191,4 @@ def self.get_my_ipv6_address ip ensure Socket.do_not_reverse_lookup = orig end - end diff --git a/tests/test_io_streamer.rb b/tests/test_io_streamer.rb index ac8da0ff2..a536fc308 100644 --- a/tests/test_io_streamer.rb +++ b/tests/test_io_streamer.rb @@ -1,13 +1,21 @@ +# frozen_string_literal: true + require_relative 'em_test_helper' require 'em/io_streamer' -EM::IOStreamer::CHUNK_SIZE = 2 +# below to stop 'already initialized constant' warning +EM::IOStreamer.__send__ :remove_const, :CHUNK_SIZE +EM::IOStreamer.const_set :CHUNK_SIZE, 2 class TestIOStreamer < Test::Unit::TestCase + class StreamServer < EM::Connection - TEST_STRING = 'this is a test'.freeze + def initialize(sent) + @sent = sent + end + def post_init - io = StringIO.new(TEST_STRING) + io = StringIO.new @sent EM::IOStreamer.new(self, io).callback { close_connection_after_writing } end end @@ -16,21 +24,24 @@ class StreamClient < EM::Connection def initialize(received) @received = received end + def receive_data data @received << data end + def unbind EM.stop end end def test_io_stream - received = '' + sent = 'this is a test' + received = ''.dup EM.run do port = next_port - EM.start_server '127.0.0.1', port, StreamServer + EM.start_server '127.0.0.1', port, StreamServer, sent EM.connect '127.0.0.1', port, StreamClient, received end - assert_equal(StreamServer::TEST_STRING, received) + assert_equal sent, received end end From 32f8895c4d31941378f0524d2289b72339c527c6 Mon Sep 17 00:00:00 2001 From: MSP-Greg Date: Wed, 16 Jan 2019 21:26:56 -0600 Subject: [PATCH 218/343] Change 'class TestSsl' to 'class TestSSL' --- tests/test_ssl_args.rb | 2 +- tests/test_ssl_dhparam.rb | 2 +- tests/test_ssl_ecdh_curve.rb | 2 +- tests/test_ssl_extensions.rb | 2 +- tests/test_ssl_protocols.rb | 2 +- tests/test_ssl_verify.rb | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/test_ssl_args.rb b/tests/test_ssl_args.rb index de35badeb..077685250 100644 --- a/tests/test_ssl_args.rb +++ b/tests/test_ssl_args.rb @@ -3,7 +3,7 @@ require_relative 'em_test_helper' require 'tempfile' -class TestSslArgs < Test::Unit::TestCase +class TestSSLArgs < Test::Unit::TestCase require_relative 'em_ssl_handlers' include EMSSLHandlers diff --git a/tests/test_ssl_dhparam.rb b/tests/test_ssl_dhparam.rb index 58d834b14..aece10fe1 100644 --- a/tests/test_ssl_dhparam.rb +++ b/tests/test_ssl_dhparam.rb @@ -2,7 +2,7 @@ require_relative 'em_test_helper' -class TestSslDhParam < Test::Unit::TestCase +class TestSSLDhParam < Test::Unit::TestCase require_relative 'em_ssl_handlers' include EMSSLHandlers diff --git a/tests/test_ssl_ecdh_curve.rb b/tests/test_ssl_ecdh_curve.rb index 98384d1be..6e353d9b9 100644 --- a/tests/test_ssl_ecdh_curve.rb +++ b/tests/test_ssl_ecdh_curve.rb @@ -2,7 +2,7 @@ require_relative 'em_test_helper' -class TestSslEcdhCurve < Test::Unit::TestCase +class TestSSLEcdhCurve < Test::Unit::TestCase require_relative 'em_ssl_handlers' include EMSSLHandlers diff --git a/tests/test_ssl_extensions.rb b/tests/test_ssl_extensions.rb index 2c1a877b9..efda27fcd 100644 --- a/tests/test_ssl_extensions.rb +++ b/tests/test_ssl_extensions.rb @@ -2,7 +2,7 @@ require_relative 'em_test_helper' -class TestSslExtensions < Test::Unit::TestCase +class TestSSLExtensions < Test::Unit::TestCase require_relative 'em_ssl_handlers' include EMSSLHandlers diff --git a/tests/test_ssl_protocols.rb b/tests/test_ssl_protocols.rb index 9c4bd011c..a8cd5886c 100644 --- a/tests/test_ssl_protocols.rb +++ b/tests/test_ssl_protocols.rb @@ -7,7 +7,7 @@ # TLSv1.1 handshake_completed Server/Client callback order is reversed from # older protocols -class TestSslProtocols < Test::Unit::TestCase +class TestSSLProtocols < Test::Unit::TestCase require_relative 'em_ssl_handlers' include EMSSLHandlers diff --git a/tests/test_ssl_verify.rb b/tests/test_ssl_verify.rb index 31d51f7a1..6780c42ed 100644 --- a/tests/test_ssl_verify.rb +++ b/tests/test_ssl_verify.rb @@ -2,7 +2,7 @@ require_relative 'em_test_helper' -class TestSslVerify < Test::Unit::TestCase +class TestSSLVerify < Test::Unit::TestCase require_relative 'em_ssl_handlers' include EMSSLHandlers From 1a1d4e1e9a8cfa2ed2ae6a40c429d5b87326f815 Mon Sep 17 00:00:00 2001 From: Thomas Hollstegge Date: Mon, 21 Jan 2019 22:31:35 +0100 Subject: [PATCH 219/343] pure_ruby: Allow sub-second intervals for timers `EventMachine::add_timer` passes the interval as int, leading to integer division and loss of sub-second intervals in the pure ruby implementation. Fix this by forcing float division. --- lib/em/pure_ruby.rb | 2 +- tests/test_pure.rb | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/lib/em/pure_ruby.rb b/lib/em/pure_ruby.rb index 109e1cc99..f5a93675d 100644 --- a/lib/em/pure_ruby.rb +++ b/lib/em/pure_ruby.rb @@ -136,7 +136,7 @@ def initialize_event_machine # processor still wants them in seconds. # @private def add_oneshot_timer interval - Reactor.instance.install_oneshot_timer(interval / 1000) + Reactor.instance.install_oneshot_timer(interval.to_f / 1000) end # @private diff --git a/tests/test_pure.rb b/tests/test_pure.rb index 9fb9407f6..00dceb5e2 100644 --- a/tests/test_pure.rb +++ b/tests/test_pure.rb @@ -137,4 +137,20 @@ def test_start_tls assert($client_received_data == "Hello World!") assert($server_received_data == "Hello World!") end + + def test_periodic_timer + x = 0 + start, finish = nil + + EM.run { + start = Time.now.to_f + EM::PeriodicTimer.new(0.2) do + x += 1 + finish = Time.now.to_f + EM.stop if x == 4 + end + } + assert_in_delta 0.8, (finish - start), 0.2 + assert_equal 4, x + end end From 62a42eeade215865db3abe0425a2521d1e4da96a Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Tue, 25 Dec 2018 12:04:06 -0800 Subject: [PATCH 220/343] Travis CI add Ruby 2.6 with JIT --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index 1c03537ac..393ea4a23 100644 --- a/.travis.yml +++ b/.travis.yml @@ -48,3 +48,6 @@ matrix: os: osx - rvm: 2.3 os: osx + - rvm: 2.6 + env: + - RUBYOPT=--jit From a220540e2a6a4ee29f11f5728fd528e8d7f70c93 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Sat, 19 Jan 2019 09:08:18 -0800 Subject: [PATCH 221/343] Minimum supported Ruby version is 2.0.0 --- eventmachine.gemspec | 1 + 1 file changed, 1 insertion(+) diff --git a/eventmachine.gemspec b/eventmachine.gemspec index 1d2d79a67..49015bdcd 100644 --- a/eventmachine.gemspec +++ b/eventmachine.gemspec @@ -5,6 +5,7 @@ Gem::Specification.new do |s| s.version = EventMachine::VERSION s.homepage = 'http://rubyeventmachine.com' s.licenses = ['Ruby', 'GPL-2.0'] + s.required_ruby_version = '>= 2.0.0' s.authors = ["Francis Cianfrocca", "Aman Gupta"] s.email = ["garbagecat10@gmail.com", "aman@tmm1.net"] From df5c3509eb2fc0f3ed09bf73a037cca2cfe4f0c5 Mon Sep 17 00:00:00 2001 From: Shved Eugene Date: Wed, 23 Jan 2019 00:15:38 +0300 Subject: [PATCH 222/343] Update README.md (#876) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f0e708046..94dfa1b3d 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ Install it with [RubyGems](https://rubygems.org/) or add this to your Gemfile if you use [Bundler](http://gembundler.com/): - gem "eventmachine" + gem 'eventmachine' From 875be4b8ddbcc177e38a1dff132247a1d62af130 Mon Sep 17 00:00:00 2001 From: MSP-Greg Date: Tue, 22 Jan 2019 15:16:47 -0600 Subject: [PATCH 223/343] Misc test fixups (#875) em_ssl_handlers - EMSSLHandlers 1. use setup_timeout from em_test_helper.rb in #client_server 2. Add timeout: KWARG to #client_server Add more time to some tests in test_httpclient2.rb for older Windows intermittent errors. --- tests/em_ssl_handlers.rb | 9 +++++---- tests/test_httpclient2.rb | 12 ++++++------ 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/tests/em_ssl_handlers.rb b/tests/em_ssl_handlers.rb index eecb94e00..e14036a8e 100644 --- a/tests/em_ssl_handlers.rb +++ b/tests/em_ssl_handlers.rb @@ -18,7 +18,7 @@ # `Client` has a `:client_unbind` parameter, which when set to true, calls # `EM.stop_event_loop` in the `unbind` callback. # -# `Server` has two additional paramters. +# `Server` has two additional parameters. # # `:ssl_verify_result`, which is normally set to true/false for the # `ssl_verify_peer` return value. If it is set to a String starting with "|RAISE|", @@ -141,12 +141,13 @@ def unbind end end - def client_server(c_hndlr = Client, s_hndlr = Server, client: nil, server: nil) + def client_server(c_hndlr = Client, s_hndlr = Server, + client: nil, server: nil, timeout: 3.0) EM.run do + # fail safe stop + setup_timeout timeout EM.start_server IP, PORT, s_hndlr, server EM.connect IP, PORT, c_hndlr, client - # fail safe stop - EM.add_timer(3.0) { EM.stop_event_loop } end end end if EM.ssl? \ No newline at end of file diff --git a/tests/test_httpclient2.rb b/tests/test_httpclient2.rb index 69450da27..fe7b0c0b9 100644 --- a/tests/test_httpclient2.rb +++ b/tests/test_httpclient2.rb @@ -5,7 +5,9 @@ class TestServer < EM::Connection end TIMEOUT = (windows? ? 2.0 : 1) - + # below may be due to an issue with OpenSSL 1.0.2 and earlier with Windows + CI_WINDOWS_OLD = windows? and RUBY_VERSION < '2.5' + def setup @port = next_port end @@ -41,7 +43,7 @@ def test_bad_port def test_bad_server err = nil EM.run { - setup_timeout(TIMEOUT) + setup_timeout TIMEOUT http = silent { EM::P::HttpClient2.connect '127.0.0.1', 9999 } d = http.get "/" d.errback { err = true; d.internal_error; EM.stop } @@ -52,7 +54,7 @@ def test_bad_server def test_get content = nil EM.run { - setup_timeout TIMEOUT + setup_timeout(CI_WINDOWS_OLD ? 9 : TIMEOUT) http = silent { EM::P::HttpClient2.connect :host => "www.google.com", :port => 80 } d = http.get "/" d.callback { @@ -117,10 +119,8 @@ def test_authheader def test_https_get omit("No SSL") unless EM.ssl? d = nil - # below is actually due to an issue with OpenSSL 1.0.2 and earlier with Windows - ci_windows_old = windows? and RUBY_VERSION < '2.5' EM.run { - setup_timeout(ci_windows_old ? 9 : TIMEOUT) + setup_timeout(CI_WINDOWS_OLD ? 9 : TIMEOUT) http = silent { EM::P::HttpClient2.connect :host => 'www.google.com', :port => 443, :tls => true } d = http.get "/" d.callback {EM.stop} From d2b00045b9545bc554da2fe520a09303469e4451 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Sun, 17 Feb 2019 19:37:37 +0100 Subject: [PATCH 224/343] Use a separate variable for int and VALUE in t_set_rlimit_nofile() * On TruffleRuby, VALUE is typed as void*. This fixes compilation. Note that linking does not succeed currently. --- ext/rubymain.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/ext/rubymain.cpp b/ext/rubymain.cpp index 0194aaeaa..06539434c 100644 --- a/ext/rubymain.cpp +++ b/ext/rubymain.cpp @@ -1050,7 +1050,7 @@ static VALUE t_unwatch_filename (VALUE self UNUSED, VALUE sig) } catch (std::runtime_error e) { rb_raise (EM_eInvalidSignature, "%s", e.what()); } - + return Qnil; } @@ -1235,8 +1235,8 @@ t_set_rlimit_nofile static VALUE t_set_rlimit_nofile (VALUE self UNUSED, VALUE arg) { - arg = (NIL_P(arg)) ? -1 : NUM2INT (arg); - return INT2NUM (evma_set_rlimit_nofile (arg)); + int arg_int = (NIL_P(arg)) ? -1 : NUM2INT (arg); + return INT2NUM (evma_set_rlimit_nofile (arg_int)); } /*************************** @@ -1598,4 +1598,3 @@ extern "C" void Init_rubyeventmachine() rb_define_const(EmModule, "OPENSSL_LIBRARY_VERSION", rb_str_new2(SSLeay_version(SSLEAY_VERSION))); #endif } - From e2ddc06e6b317b8292a82688548fa0f9f21412a5 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Sun, 17 Feb 2019 20:12:23 +0100 Subject: [PATCH 225/343] Keep the original linker command on TruffleRuby, as linking is done on bitcode --- ext/extconf.rb | 7 +++++++ ext/fastfilereader/extconf.rb | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/ext/extconf.rb b/ext/extconf.rb index a3999ea0a..d4762f6dd 100644 --- a/ext/extconf.rb +++ b/ext/extconf.rb @@ -143,6 +143,8 @@ def pkg_config_wrapper(pretty_name, name) # Main platform invariances: +ldshared = CONFIG['LDSHARED'] + case RUBY_PLATFORM when /mswin32/, /mingw32/, /bccwin32/ check_heads(%w[windows.h winsock.h], true) @@ -222,6 +224,11 @@ def pkg_config_wrapper(pretty_name, name) CONFIG['LDSHARED'] = "$(CXX) -shared" end +if RUBY_ENGINE == "truffleruby" + # Keep the original LDSHARED on TruffleRuby, as linking is done on bitcode + CONFIG['LDSHARED'] = ldshared +end + # Platform-specific time functions if have_func('clock_gettime') # clock_gettime is POSIX, but the monotonic clocks are not diff --git a/ext/fastfilereader/extconf.rb b/ext/fastfilereader/extconf.rb index e4d64e4a2..d2a5241e8 100644 --- a/ext/fastfilereader/extconf.rb +++ b/ext/fastfilereader/extconf.rb @@ -40,6 +40,8 @@ def add_define(name) # Main platform invariances: +ldshared = CONFIG['LDSHARED'] + case RUBY_PLATFORM when /mswin32/, /mingw32/, /bccwin32/ check_heads(%w[windows.h winsock.h], true) @@ -106,4 +108,9 @@ def add_define(name) CONFIG['LDSHARED'] = "$(CXX) -shared" end +if RUBY_ENGINE == "truffleruby" + # Keep the original LDSHARED on TruffleRuby, as linking is done on bitcode + CONFIG['LDSHARED'] = ldshared +end + create_makefile "fastfilereaderext" From 058552b70bc919aa686c8cd4c89be49298bbe27c Mon Sep 17 00:00:00 2001 From: David Margery Date: Thu, 9 May 2019 16:07:20 +0200 Subject: [PATCH 226/343] Add support for :cert & :private_key to start_tls. - :private_key_pass is also supported This allows specifying cert and private_key as strings, and not only as file paths, and allows the usage of encoded private keys, whether from strings of from path. Add tests for certs and keys passed as strings, and a file to test encoded private keys --- ext/cmain.cpp | 4 +- ext/ed.cpp | 13 ++-- ext/ed.h | 7 +- ext/eventmachine.h | 2 +- ext/rubymain.cpp | 14 ++-- ext/ssl.cpp | 48 ++++++++++++-- ext/ssl.h | 4 +- lib/em/connection.rb | 54 ++++++++++++---- lib/em/pure_ruby.rb | 16 +++-- lib/jeventmachine.rb | 1 + tests/encoded_client.key | 54 ++++++++++++++++ tests/test_ssl_args.rb | 29 +++++++++ tests/test_ssl_inline_cert.rb | 116 ++++++++++++++++++++++++++++++++++ tests/test_ssl_verify.rb | 17 +++++ 14 files changed, 339 insertions(+), 40 deletions(-) create mode 100644 tests/encoded_client.key create mode 100644 tests/test_ssl_inline_cert.rb diff --git a/ext/cmain.cpp b/ext/cmain.cpp index 7ae0c5a88..0454111ed 100644 --- a/ext/cmain.cpp +++ b/ext/cmain.cpp @@ -480,12 +480,12 @@ extern "C" void evma_start_tls (const uintptr_t binding) evma_set_tls_parms ******************/ -extern "C" void evma_set_tls_parms (const uintptr_t binding, const char *privatekey_filename, const char *certchain_filename, int verify_peer, int fail_if_no_peer_cert, const char *sni_hostname, const char *cipherlist, const char *ecdh_curve, const char *dhparam, int ssl_version) +extern "C" void evma_set_tls_parms (const uintptr_t binding, const char *privatekey_filename, const char *privatekey, const char *privatekeypass, const char *certchain_filename, const char *cert, int verify_peer, int fail_if_no_peer_cert, const char *sni_hostname, const char *cipherlist, const char *ecdh_curve, const char *dhparam, int ssl_version) { ensure_eventmachine("evma_set_tls_parms"); EventableDescriptor *ed = dynamic_cast (Bindable_t::GetObject (binding)); if (ed) - ed->SetTlsParms (privatekey_filename, certchain_filename, (verify_peer == 1 ? true : false), (fail_if_no_peer_cert == 1 ? true : false), sni_hostname, cipherlist, ecdh_curve, dhparam, ssl_version); + ed->SetTlsParms (privatekey_filename, privatekey, privatekeypass, certchain_filename, cert, (verify_peer == 1 ? true : false), (fail_if_no_peer_cert == 1 ? true : false), sni_hostname, cipherlist, ecdh_curve, dhparam, ssl_version); } /****************** diff --git a/ext/ed.cpp b/ext/ed.cpp index cc61c41a6..48db336ef 100644 --- a/ext/ed.cpp +++ b/ext/ed.cpp @@ -1367,7 +1367,7 @@ void ConnectionDescriptor::StartTls() if (SslBox) throw std::runtime_error ("SSL/TLS already running on connection"); - SslBox = new SslBox_t (bIsServer, PrivateKeyFilename, CertChainFilename, bSslVerifyPeer, bSslFailIfNoPeerCert, SniHostName, CipherList, EcdhCurve, DhParam, Protocols, GetBinding()); + SslBox = new SslBox_t (bIsServer, PrivateKeyFilename, PrivateKey, PrivateKeyPass, CertChainFilename, Cert, bSslVerifyPeer, bSslFailIfNoPeerCert, SniHostName, CipherList, EcdhCurve, DhParam, Protocols, GetBinding()); _DispatchCiphertext(); } @@ -1384,14 +1384,20 @@ ConnectionDescriptor::SetTlsParms *********************************/ #ifdef WITH_SSL -void ConnectionDescriptor::SetTlsParms (const char *privkey_filename, const char *certchain_filename, bool verify_peer, bool fail_if_no_peer_cert, const char *sni_hostname, const char *cipherlist, const char *ecdh_curve, const char *dhparam, int protocols) +void ConnectionDescriptor::SetTlsParms (const char *privkey_filename, const char *privkey, const char *privkeypass, const char *certchain_filename, const char *cert, bool verify_peer, bool fail_if_no_peer_cert, const char *sni_hostname, const char *cipherlist, const char *ecdh_curve, const char *dhparam, int protocols) { if (SslBox) throw std::runtime_error ("call SetTlsParms before calling StartTls"); if (privkey_filename && *privkey_filename) PrivateKeyFilename = privkey_filename; + if (privkey && *privkey) + PrivateKey = privkey; + if (privkeypass && *privkeypass) + PrivateKeyPass = privkeypass; if (certchain_filename && *certchain_filename) CertChainFilename = certchain_filename; + if (cert && *cert) + Cert = cert; bSslVerifyPeer = verify_peer; bSslFailIfNoPeerCert = fail_if_no_peer_cert; @@ -1403,11 +1409,10 @@ void ConnectionDescriptor::SetTlsParms (const char *privkey_filename, const char EcdhCurve = ecdh_curve; if (dhparam && *dhparam) DhParam = dhparam; - Protocols = protocols; } #else -void ConnectionDescriptor::SetTlsParms (const char *privkey_filename UNUSED, const char *certchain_filename UNUSED, bool verify_peer UNUSED, bool fail_if_no_peer_cert UNUSED, const char *sni_hostname UNUSED, const char *cipherlist UNUSED, const char *ecdh_curve UNUSED, const char *dhparam UNUSED, int protocols UNUSED) +void ConnectionDescriptor::SetTlsParms (const char *privkey_filename UNUSED, const char *privkey UNUSED, const char *privkeypass UNUSED, const char *certchain_filename UNUSED, const char *cert UNUSED, bool verify_peer UNUSED, bool fail_if_no_peer_cert UNUSED, const char *sni_hostname UNUSED, const char *cipherlist UNUSED, const char *ecdh_curve UNUSED, const char *dhparam UNUSED, int protocols UNUSED) { throw std::runtime_error ("Encryption not available on this event-machine"); } diff --git a/ext/ed.h b/ext/ed.h index be1b08135..7d5bc01dc 100644 --- a/ext/ed.h +++ b/ext/ed.h @@ -72,7 +72,7 @@ class EventableDescriptor: public Bindable_t virtual bool GetSubprocessPid (pid_t*) {return false;} virtual void StartTls() {} - virtual void SetTlsParms (const char *, const char *, bool, bool, const char *, const char *, const char *, const char *, int) {} + virtual void SetTlsParms (const char *, const char *, const char *, const char *, const char *, bool, bool, const char *, const char *, const char *, const char *, int) {} #ifdef WITH_SSL virtual X509 *GetPeerCert() {return NULL;} @@ -213,7 +213,7 @@ class ConnectionDescriptor: public EventableDescriptor virtual int GetOutboundDataSize() {return OutboundDataSize;} virtual void StartTls(); - virtual void SetTlsParms (const char *, const char *, bool, bool, const char *, const char *, const char *, const char *, int); + virtual void SetTlsParms (const char *, const char *, const char *, const char *, const char *, bool, bool, const char *, const char *, const char *, const char *, int); #ifdef WITH_SSL virtual X509 *GetPeerCert(); @@ -260,7 +260,10 @@ class ConnectionDescriptor: public EventableDescriptor #ifdef WITH_SSL SslBox_t *SslBox; std::string CertChainFilename; + std::string Cert; std::string PrivateKeyFilename; + std::string PrivateKey; + std::string PrivateKeyPass; std::string CipherList; std::string EcdhCurve; std::string DhParam; diff --git a/ext/eventmachine.h b/ext/eventmachine.h index 788f1ca4e..f9af26248 100644 --- a/ext/eventmachine.h +++ b/ext/eventmachine.h @@ -81,7 +81,7 @@ extern "C" { const uintptr_t evma_attach_sd (int sd); const uintptr_t evma_open_datagram_socket (const char *server, int port); const uintptr_t evma_open_keyboard(); - void evma_set_tls_parms (const uintptr_t binding, const char *privatekey_filename, const char *certchain_filenane, int verify_peer, int fail_if_no_peer_cert, const char *sni_hostname, const char *cipherlist, const char *ecdh_curve, const char *dhparam, int protocols); + void evma_set_tls_parms (const uintptr_t binding, const char *privatekey_filename, const char *privatekey, const char *privatekeypass, const char *certchain_filename, const char *cert, int verify_peer, int fail_if_no_peer_cert, const char *sni_hostname, const char *cipherlist, const char *ecdh_curve, const char *dhparam, int protocols); void evma_start_tls (const uintptr_t binding); #ifdef WITH_SSL diff --git a/ext/rubymain.cpp b/ext/rubymain.cpp index 06539434c..89d7e1d2f 100644 --- a/ext/rubymain.cpp +++ b/ext/rubymain.cpp @@ -59,6 +59,7 @@ static VALUE EM_eUnknownTimerFired; static VALUE EM_eConnectionNotBound; static VALUE EM_eUnsupported; static VALUE EM_eInvalidSignature; +static VALUE EM_eInvalidPrivateKey; static VALUE Intern_at_signature; static VALUE Intern_at_timers; @@ -342,7 +343,11 @@ t_start_tls static VALUE t_start_tls (VALUE self UNUSED, VALUE signature) { - evma_start_tls (NUM2BSIG (signature)); + try { + evma_start_tls (NUM2BSIG (signature)); + } catch (const std::runtime_error& e) { + rb_raise (EM_eInvalidPrivateKey, e.what(), signature); + } return Qnil; } @@ -350,14 +355,14 @@ static VALUE t_start_tls (VALUE self UNUSED, VALUE signature) t_set_tls_parms ***************/ -static VALUE t_set_tls_parms (VALUE self UNUSED, VALUE signature, VALUE privkeyfile, VALUE certchainfile, VALUE verify_peer, VALUE fail_if_no_peer_cert, VALUE snihostname, VALUE cipherlist, VALUE ecdh_curve, VALUE dhparam, VALUE ssl_version) +static VALUE t_set_tls_parms (VALUE self UNUSED, VALUE signature, VALUE privkeyfile, VALUE privkey, VALUE privkeypass, VALUE certchainfile, VALUE cert, VALUE verify_peer, VALUE fail_if_no_peer_cert, VALUE snihostname, VALUE cipherlist, VALUE ecdh_curve, VALUE dhparam, VALUE ssl_version) { /* set_tls_parms takes a series of positional arguments for specifying such things * as private keys and certificate chains. * It's expected that the parameter list will grow as we add more supported features. * ALL of these parameters are optional, and can be specified as empty or NULL strings. */ - evma_set_tls_parms (NUM2BSIG (signature), StringValueCStr (privkeyfile), StringValueCStr (certchainfile), (verify_peer == Qtrue ? 1 : 0), (fail_if_no_peer_cert == Qtrue ? 1 : 0), StringValueCStr (snihostname), StringValueCStr (cipherlist), StringValueCStr (ecdh_curve), StringValueCStr (dhparam), NUM2INT (ssl_version)); + evma_set_tls_parms (NUM2BSIG (signature), StringValueCStr (privkeyfile), StringValueCStr (privkey), StringValueCStr (privkeypass), StringValueCStr (certchainfile), StringValueCStr (cert), (verify_peer == Qtrue ? 1 : 0), (fail_if_no_peer_cert == Qtrue ? 1 : 0), StringValueCStr (snihostname), StringValueCStr (cipherlist), StringValueCStr (ecdh_curve), StringValueCStr (dhparam), NUM2INT (ssl_version)); return Qnil; } @@ -1450,6 +1455,7 @@ extern "C" void Init_rubyeventmachine() EM_eUnknownTimerFired = rb_define_class_under (EmModule, "UnknownTimerFired", rb_eRuntimeError); EM_eUnsupported = rb_define_class_under (EmModule, "Unsupported", rb_eRuntimeError); EM_eInvalidSignature = rb_define_class_under (EmModule, "InvalidSignature", rb_eRuntimeError); + EM_eInvalidPrivateKey = rb_define_class_under (EmModule, "InvalidPrivateKey", rb_eRuntimeError); rb_define_module_function (EmModule, "initialize_event_machine", (VALUE(*)(...))t_initialize_event_machine, 0); rb_define_module_function (EmModule, "run_machine_once", (VALUE(*)(...))t_run_machine_once, 0); @@ -1461,7 +1467,7 @@ extern "C" void Init_rubyeventmachine() rb_define_module_function (EmModule, "stop_tcp_server", (VALUE(*)(...))t_stop_server, 1); rb_define_module_function (EmModule, "start_unix_server", (VALUE(*)(...))t_start_unix_server, 1); rb_define_module_function (EmModule, "attach_sd", (VALUE(*)(...))t_attach_sd, 1); - rb_define_module_function (EmModule, "set_tls_parms", (VALUE(*)(...))t_set_tls_parms, 10); + rb_define_module_function (EmModule, "set_tls_parms", (VALUE(*)(...))t_set_tls_parms, 13); rb_define_module_function (EmModule, "start_tls", (VALUE(*)(...))t_start_tls, 1); rb_define_module_function (EmModule, "get_peer_cert", (VALUE(*)(...))t_get_peer_cert, 1); rb_define_module_function (EmModule, "get_cipher_bits", (VALUE(*)(...))t_get_cipher_bits, 1); diff --git a/ext/ssl.cpp b/ext/ssl.cpp index c83e5b09f..35cabd306 100644 --- a/ext/ssl.cpp +++ b/ext/ssl.cpp @@ -103,7 +103,6 @@ static void InitializeDefaultCredentials() DefaultPrivateKey = NULL; } PEM_read_bio_PrivateKey (bio, &DefaultPrivateKey, builtin_passwd_cb, 0); - if (DefaultCertificate) { // we may come here in a restart. X509_free (DefaultCertificate); @@ -120,7 +119,7 @@ static void InitializeDefaultCredentials() SslContext_t::SslContext_t **************************/ -SslContext_t::SslContext_t (bool is_server, const std::string &privkeyfile, const std::string &certchainfile, const std::string &cipherlist, const std::string &ecdh_curve, const std::string &dhparam, int ssl_version) : +SslContext_t::SslContext_t (bool is_server, const std::string &privkeyfile, const std::string &privkey, const std::string &privkeypass, const std::string &certchainfile, const std::string &cert, const std::string &cipherlist, const std::string &ecdh_curve, const std::string &dhparam, int ssl_version) : bIsServer (is_server), pCtx (NULL), PrivateKey (NULL), @@ -274,14 +273,49 @@ SslContext_t::SslContext_t (bool is_server, const std::string &privkeyfile, cons } else { int e; + // As indicated in man(3) ssl_ctx_use_privatekey_file + // To change a certificate, private key pair the new certificate needs to be set with + // SSL_use_certificate() or SSL_CTX_use_certificate() before setting the private key with SSL_CTX_use_PrivateKey() or SSL_use_PrivateKey(). + if (certchainfile.length() > 0) { + e = SSL_CTX_use_certificate_chain_file (pCtx, certchainfile.c_str()); + if (e <= 0) ERR_print_errors_fp(stderr); + assert (e > 0); + } + if (cert.length() > 0) { + BIO *bio = BIO_new_mem_buf (cert.c_str(), -1); + BIO_set_mem_eof_return(bio, 0); + X509 * clientCertificate = PEM_read_bio_X509 (bio, NULL, NULL, 0); + e = SSL_CTX_use_certificate (pCtx, clientCertificate); + X509_free(clientCertificate); + BIO_free (bio); + if (e <= 0) ERR_print_errors_fp(stderr); + assert (e > 0); + } if (privkeyfile.length() > 0) { + if (privkeypass.length() > 0) { + SSL_CTX_set_default_passwd_cb_userdata(pCtx, const_cast(privkeypass.c_str())); + } e = SSL_CTX_use_PrivateKey_file (pCtx, privkeyfile.c_str(), SSL_FILETYPE_PEM); if (e <= 0) ERR_print_errors_fp(stderr); assert (e > 0); } - if (certchainfile.length() > 0) { - e = SSL_CTX_use_certificate_chain_file (pCtx, certchainfile.c_str()); - if (e <= 0) ERR_print_errors_fp(stderr); + if (privkey.length() > 0) { + BIO *bio = BIO_new_mem_buf (privkey.c_str(), -1); + BIO_set_mem_eof_return(bio, 0); + EVP_PKEY * clientPrivateKey = PEM_read_bio_PrivateKey (bio, NULL, NULL, const_cast(privkeypass.c_str())); + e = SSL_CTX_use_PrivateKey (pCtx, clientPrivateKey); + EVP_PKEY_free(clientPrivateKey); + BIO_free (bio); + if (e <= 0) { + BIO *bio_err = BIO_new(BIO_s_mem()); + ERR_print_errors(bio_err); + char* buf; + long size = BIO_get_mem_data(bio_err, &buf); + std::string error_msg; + error_msg.assign(buf,size); + BIO_free(bio_err); + throw std::runtime_error (error_msg); + } assert (e > 0); } } @@ -309,7 +343,7 @@ SslContext_t::~SslContext_t() SslBox_t::SslBox_t ******************/ -SslBox_t::SslBox_t (bool is_server, const std::string &privkeyfile, const std::string &certchainfile, bool verify_peer, bool fail_if_no_peer_cert, const std::string &snihostname, const std::string &cipherlist, const std::string &ecdh_curve, const std::string &dhparam, int ssl_version, const uintptr_t binding): +SslBox_t::SslBox_t (bool is_server, const std::string &privkeyfile, const std::string &privkey, const std::string &privkeypass, const std::string &certchainfile, const std::string &cert, bool verify_peer, bool fail_if_no_peer_cert, const std::string &snihostname, const std::string &cipherlist, const std::string &ecdh_curve, const std::string &dhparam, int ssl_version, const uintptr_t binding): bIsServer (is_server), bHandshakeCompleted (false), bVerifyPeer (verify_peer), @@ -322,7 +356,7 @@ SslBox_t::SslBox_t (bool is_server, const std::string &privkeyfile, const std::s * a new one every time we come here. */ - Context = new SslContext_t (bIsServer, privkeyfile, certchainfile, cipherlist, ecdh_curve, dhparam, ssl_version); + Context = new SslContext_t (bIsServer, privkeyfile, privkey, privkeypass, certchainfile, cert, cipherlist, ecdh_curve, dhparam, ssl_version); assert (Context); pbioRead = BIO_new (BIO_s_mem()); diff --git a/ext/ssl.h b/ext/ssl.h index 64ff6e18c..baf47f9f0 100644 --- a/ext/ssl.h +++ b/ext/ssl.h @@ -33,7 +33,7 @@ class SslContext_t class SslContext_t { public: - SslContext_t (bool is_server, const std::string &privkeyfile, const std::string &certchainfile, const std::string &cipherlist, const std::string &ecdh_curve, const std::string &dhparam, int ssl_version); + SslContext_t (bool is_server, const std::string &privkeyfile, const std::string &privkey, const std::string &privkeypass, const std::string &certchainfile, const std::string &cert, const std::string &cipherlist, const std::string &ecdh_curve, const std::string &dhparam, int ssl_version); virtual ~SslContext_t(); private: @@ -61,7 +61,7 @@ class SslBox_t class SslBox_t { public: - SslBox_t (bool is_server, const std::string &privkeyfile, const std::string &certchainfile, bool verify_peer, bool fail_if_no_peer_cert, const std::string &snihostname, const std::string &cipherlist, const std::string &ecdh_curve, const std::string &dhparam, int ssl_version, const uintptr_t binding); + SslBox_t (bool is_server, const std::string &privkeyfile, const std::string &privkey, const std::string &privkeypass, const std::string &certchainfile, const std::string &cert, bool verify_peer, bool fail_if_no_peer_cert, const std::string &snihostname, const std::string &cipherlist, const std::string &ecdh_curve, const std::string &dhparam, int ssl_version, const uintptr_t binding); virtual ~SslBox_t(); int PutPlaintext (const char*, int); diff --git a/lib/em/connection.rb b/lib/em/connection.rb index b5f8b0ad7..7cb27596a 100644 --- a/lib/em/connection.rb +++ b/lib/em/connection.rb @@ -1,6 +1,8 @@ module EventMachine - class FileNotFoundException < Exception - end + class FileNotFoundException < Exception ; end + class BadParams < Exception ; end + class BadCertParams < BadParams ; end + class BadPrivateKeyParams < BadParams ;end # EventMachine::Connection is a class that is instantiated # by EventMachine's processing loop whenever a new connection @@ -372,9 +374,16 @@ def connection_completed # @option args [String] :cert_chain_file (nil) local path of a readable file that contants a chain of X509 certificates in # the [PEM format](http://en.wikipedia.org/wiki/Privacy_Enhanced_Mail), # with the most-resolved certificate at the top of the file, successive intermediate - # certs in the middle, and the root (or CA) cert at the bottom. + # certs in the middle, and the root (or CA) cert at the bottom. If both + # :cert_chain_file and :cert are used, BadCertParams will be raised. + # + # @option args [String] :cert (nil) a string with the client certificate to use, complete with header and footer. If a cert chain is required, you will have to use the :cert_chain_file option. If both :cert_chain_file and :cert are used, BadCertParams will be raised. + # + # @option args [String] :private_key_file (nil) local path of a readable file that must contain a private key in the [PEM format](http://en.wikipedia.org/wiki/Privacy_Enhanced_Mail). If both :private_key_file and :private_key are used, BadPrivateKeyParams will be raised. If the Private Key does not match the certificate, InvalidPrivateKey will be raised. # - # @option args [String] :private_key_file (nil) local path of a readable file that must contain a private key in the [PEM format](http://en.wikipedia.org/wiki/Privacy_Enhanced_Mail). + # @option args [String] :private_key (nil) a string, complete with header and footer, that must contain a private key in the [PEM format](http://en.wikipedia.org/wiki/Privacy_Enhanced_Mail). If both :private_key_file and :private_key are used, BadPrivateKeyParams will be raised. If the Private Key does not match the certificate, InvalidPrivateKey will be raised. + # + # @option args [String] :private_key_pass (nil) a string to use as password to decode :private_key or :private_key_file # # @option args [Boolean] :verify_peer (false) indicates whether a server should request a certificate from a peer, to be verified by user code. # If true, the {#ssl_verify_peer} callback on the {EventMachine::Connection} object is called with each certificate @@ -415,22 +424,39 @@ def connection_completed # # @see #ssl_verify_peer def start_tls args={} - priv_key = args[:private_key_file] - cert_chain = args[:cert_chain_file] - verify_peer = args[:verify_peer] - sni_hostname = args[:sni_hostname] - cipher_list = args[:cipher_list] - ssl_version = args[:ssl_version] - ecdh_curve = args[:ecdh_curve] - dhparam = args[:dhparam] + priv_key_path = args[:private_key_file] + priv_key = args[:private_key] + priv_key_pass = args[:private_key_pass] + cert_chain_path = args[:cert_chain_file] + cert = args[:cert] + verify_peer = args[:verify_peer] + sni_hostname = args[:sni_hostname] + cipher_list = args[:cipher_list] + ssl_version = args[:ssl_version] + ecdh_curve = args[:ecdh_curve] + dhparam = args[:dhparam] fail_if_no_peer_cert = args[:fail_if_no_peer_cert] - [priv_key, cert_chain].each do |file| + [priv_key_path, cert_chain_path].each do |file| next if file.nil? or file.empty? raise FileNotFoundException, "Could not find #{file} for start_tls" unless File.exist? file end + if !priv_key_path.nil? && !priv_key_path.empty? && !priv_key.nil? && !priv_key.empty? + raise BadPrivateKeyParams, "Specifying both private_key and private_key_file not allowed" + end + + if !cert_chain_path.nil? && !cert_chain_path.empty? && !cert.nil? && !cert.empty? + raise BadCertParams, "Specifying both cert and cert_chain_file not allowed" + end + + if (!priv_key_path.nil? && !priv_key_path.empty?) || (!priv_key.nil? && !priv_key.empty?) + if (cert_chain_path.nil? || cert_chain_path.empty?) && (cert.nil? || cert.empty?) + raise BadParams, "You have specified a private key to use, but not the related cert" + end + end + protocols_bitmask = 0 if ssl_version.nil? protocols_bitmask |= EventMachine::EM_PROTO_TLSv1 @@ -460,7 +486,7 @@ def start_tls args={} end end - EventMachine::set_tls_parms(@signature, priv_key || '', cert_chain || '', verify_peer, fail_if_no_peer_cert, sni_hostname || '', cipher_list || '', ecdh_curve || '', dhparam || '', protocols_bitmask) + EventMachine::set_tls_parms(@signature, priv_key_path || '', priv_key || '', priv_key_pass || '', cert_chain_path || '', cert || '', verify_peer, fail_if_no_peer_cert, sni_hostname || '', cipher_list || '', ecdh_curve || '', dhparam || '', protocols_bitmask) EventMachine::start_tls @signature end diff --git a/lib/em/pure_ruby.rb b/lib/em/pure_ruby.rb index f5a93675d..3b9b0ac63 100644 --- a/lib/em/pure_ruby.rb +++ b/lib/em/pure_ruby.rb @@ -46,6 +46,7 @@ class Unsupported < RuntimeError; end class ConnectionError < RuntimeError; end # @private class ConnectionNotBound < RuntimeError; end + class InvalidPrivateKey < RuntimeError; end # Older versions of Ruby may not provide the SSLErrorWaitReadable # OpenSSL class. Create an error class to act as a "proxy". @@ -261,7 +262,7 @@ def tls_parm_set?(parm) # parameter list will grow as we add more supported features. ALL of these # parameters are optional, and can be specified as empty or nil strings. # @private - def set_tls_parms signature, priv_key, cert_chain, verify_peer, fail_if_no_peer_cert, sni_hostname, cipher_list, ecdh_curve, dhparam, protocols_bitmask + def set_tls_parms signature, priv_key_path, priv_key, priv_key_pass, cert_chain_path, cert, verify_peer, fail_if_no_peer_cert, sni_hostname, cipher_list, ecdh_curve, dhparam, protocols_bitmask bitmask = protocols_bitmask ssl_options = OpenSSL::SSL::OP_ALL ssl_options |= OpenSSL::SSL::OP_NO_SSLv2 if defined?(OpenSSL::SSL::OP_NO_SSLv2) && EM_PROTO_SSLv2 & bitmask == 0 @@ -275,8 +276,11 @@ def set_tls_parms signature, priv_key, cert_chain, verify_peer, fail_if_no_peer_ :fail_if_no_peer_cert => fail_if_no_peer_cert, :ssl_options => ssl_options } - @tls_parms[signature][:priv_key] = File.read(priv_key) if tls_parm_set?(priv_key) - @tls_parms[signature][:cert_chain] = File.read(cert_chain) if tls_parm_set?(cert_chain) + @tls_parms[signature][:priv_key] = File.read(priv_key_path) if tls_parm_set?(priv_key_path) + @tls_parms[signature][:priv_key] = priv_key if tls_parm_set?(priv_key) + @tls_parms[signature][:priv_key_pass] = priv_key_pass if tls_parm_set?(priv_key_pass) + @tls_parms[signature][:cert_chain] = File.read(cert_chain_path) if tls_parm_set?(cert_chain_path) + @tls_parms[signature][:cert_chain] = cert if tls_parm_set?(cert_chain) @tls_parms[signature][:sni_hostname] = sni_hostname if tls_parm_set?(sni_hostname) @tls_parms[signature][:cipher_list] = cipher_list.gsub(/,\s*/, ':') if tls_parm_set?(cipher_list) @tls_parms[signature][:dhparam] = File.read(dhparam) if tls_parm_set?(dhparam) @@ -293,7 +297,11 @@ def start_tls signature ctx.cert_store = OpenSSL::X509::Store.new ctx.cert_store.set_default_paths ctx.cert = OpenSSL::X509::Certificate.new(tls_parms[:cert_chain]) if tls_parms[:cert_chain] - ctx.key = OpenSSL::PKey::RSA.new(tls_parms[:priv_key]) if tls_parms[:priv_key] + if tls_parms[:priv_key_pass]!=nil + ctx.key = OpenSSL::PKey::RSA.new(tls_parms[:priv_key],tls_parms[:priv_key_pass]) if tls_parms[:priv_key] + else + ctx.key = OpenSSL::PKey::RSA.new(tls_parms[:priv_key]) if tls_parms[:priv_key] + end verify_mode = OpenSSL::SSL::VERIFY_NONE if tls_parms[:verify_peer] verify_mode |= OpenSSL::SSL::VERIFY_PEER diff --git a/lib/jeventmachine.rb b/lib/jeventmachine.rb index 54e5b1ebf..e651c7ff3 100644 --- a/lib/jeventmachine.rb +++ b/lib/jeventmachine.rb @@ -99,6 +99,7 @@ class ConnectionError < RuntimeError; end class ConnectionNotBound < RuntimeError; end class UnknownTimerFired < RuntimeError; end class Unsupported < RuntimeError; end + class InvalidPrivateKey < RuntimeError; end # This thunk class used to be called EM, but that caused conflicts with # the alias "EM" for module EventMachine. (FC, 20Jun08) diff --git a/tests/encoded_client.key b/tests/encoded_client.key new file mode 100644 index 000000000..4d19b766a --- /dev/null +++ b/tests/encoded_client.key @@ -0,0 +1,54 @@ +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-128-CBC,4BFAC39AEF67B2BED03FB8D0E34BDB6E + +KXJGir0Kro0c4qoFCsyCyYQgBS0ul4FinsCei3haYwz3Vd/uvZD8wBoedzVho/Mz +99nZXZ52lHuwewGVvBeAnSXTrAtWCV2S9ZivYLZNqsEyxRlUF0SYat31bD2OImh8 +kXNC89HA9CEpRp3Zc0DU9JqqaRMv6SloMZA9AM/ioeHGj2o8Ajf4Vi0jg766cVFc +CyxHy+IOJww8FSBwGaghBixNS4rhtUonJUDeInB7C0jFqkoHQG1JCSGTzbK66bDf +kDhvUzxJnKUTezXGl5sKTRznla7aurWY5kFjz0b/api8ZCm/LvvsDipZbwwOHm31 +wMj9LPLzd0Sz2c3TyoPc/oCS9KVFnszndjaT6ydRLvHgF2XpPbL+xJz0HJwsvkis +kls0NHw69GvzTj6kZfz9a5XLpwjoGpeeMNBHw7SDRUNJnOEFwTecJL3NhCVSVgIy +owYTWDmPR8rcyNd007uhCio2+VxkZE2Hdmk0drz69mp0CNQbxe0qF8XKHTBT74I/ +L3KQITmMpSDNEn8bY/KmKpB6iuVBURvTpz5ZAPStsp4hcp0LlmPuhZZYGWC4Hox5 +svsp95iE5esN67RNUI3/twZhtJu8e5tIKy6qEWMWdrVeI0oVoLmfK+g5uQJaMu+Z +M/SLeTHjVOSq7r4Tg5Xmd8nzEQ8EyJKZCJshD7LZxjA34bOLID9Vhz0j/uE4IhNI +APO6gllYsWAGO4hADZH24i8ZvS2dX4ZR2YtLhbwjOXyVK4NYNGUeU5W0StMV8rlx +VLObQhN762YUFtm5nuP4z2GGcXgA4zCIRklbBw8N6+UP8RB54nrC4Mu6FyPolS3j +c0cVQG9q1YU5sgokMZwDJSO4AmBYUqckgNFMIRH06qGEqPoKqpi55myrLiZJmgTK +LuiaREGmyatjqqRm60sHUYrFpY+q/jsClmhvplQwOzAEX8MHwyyBp3tKokmdEniK +ULF8I4XP70U3jpLOdlPmVbYZ69Wna+5OQNdpvboAoe1l9kB+ZLq9exen0BN/beL8 +hr17q8pZUSPehVlaKgv8/3uQT/Tgi4x+KkAghjwEa97gFsQth50B2gInBK7KcveC +lRzyYM5CrIx1bXbsw3rgxODLUfX9eNRmXsznDeZZv0X0O18eVcwpp7OYZb4RnmTw +VkjjW1dfb+ZY/wLfx/LDE/enj3hbnPqjZ48V5UpTiaV3XGMEnMdXmjR1dlsPRJ2D +os3bzMT/Oo8CHu0VX73+JUrOCGQt3RpDslieQRdW6GEh0oxRo9AL8euDNyWBI1m9 +H8RRyaPwzY9GwTbpwXEtXoywHsuOhbQumng4QFOSDyAOiZoOt0q2MdmfZbyizDIQ +qIXFsnQG/NI4fUquTwIvmlnkoAtEWlwc41nvW0nEB5LNtUgJ5dvnmvQ4CqewbiMt +lm9AM47Bo3M/MjBg5NoNalEl1Wqx+H2nm6TslLA0CwNdsCKb/XSipXKwp8iWUZdT +4TrBk9PO++7+LZkfpokSqjvwQFjGTRwQsTYqNWpMawxxGX9xQPX2xr9HqgGR9/wq +gQ/GQJmfpOasKCXxUYfZ6RkLm/nkxryRdfdlqQ1l3PaBwSWL4kJaOxJbKQcRUIAb +HbPzxS1fykZY56Nk8mUtLBVTTSNPLo4VQtqZngEE8FqnAoet2eHnAE/vnMwiVu8p +lV+WeElUJ83q5UGKPtF6u3w+f989lMY2RA1vpIhN2cNmbuDIsutJjXNHRTHcmAvF +pnBZingBhr1MVSFIGk5QUTVFkznB/FqBd/gc5HbRRQgg6cym8XaAVLDGuFhBqzbr +B/bC7d4Qvn7/+G0zus04t1uGP/kGQnufFLO6IZocwGnhR1GdzyCJdfzeF7w3sNAT +SSx4GPCp5ewRPvoZap81TppRu9MbKQ00CKQMSqTwcFSQ253e5G1a0BSlmV9ElzVd +6voj4DVV5Lbr+IHIzUPbvZ7PQd6h/EurL1oieITaRm4V3L5o/nlt7V0w7e+PTLOg +SgupvYl72r405ds8lqlzJ5X2iQ/7qm5QX2DnWSuYNBjUJUxGCi4k3Scc1ZFhxU2j ++SFYrIXhW3BWO5lNij24v9UPmpP4dxRuTYcZfz7c3K9qDgjobKw39OtCHwUGN9Cb +dsxOQKYYijfNhg0c+uFAjDXaq2Mpn1GM4CUUOdIFEElYeboOmpEQg84O74RrR2LC +U4mN1/2YuZRiAtht23J07wJr4vMx+yDsTaMvzvNJLT9uV1VChWprfMB/uyW9MLcE +Sc6xMQsx5shBP5y3q3PX2mODIjg5xSXfWgyI12qK92X8vgzhERzidE64LbtRPio+ +SonNtkQFEbR2O6YmpPQ1lIrmj9+r1vjQGzMMn4317aK0EC0ntbCgX6nanvIvjbAT +S2YUE0W2Xyx4LO8/d8lxp7LoRzs8ejLUHr9XOeq6qGdPHtTtTyqtqzukRyTdjtK3 +4fZbSeNfCiC/PCbxEqVdGFHZHNKjm2vEQ+vqdP3lvVlnxKGGrQ86Vg44VcF5c+Vp +6wF2K1+6a0jarc5nAjpRCO0tlvIEmZ4bb0/jelrLhq7fD5689nE043mzNbuBzTGQ +k+Oe9kcr4wRJfjN1+F3mb94O/WxJSimCniXKEJ5WlJma82jMChyitwVsSTvct8e+ +JXJNNPbX2UdXhpU3GIjp2NKD5p1tjNA1j4lw/aGYcuLQUEu7u3/hdyGbRInI+FVy +LflkKR3N2uq1HbUomJbt2OyeQalHux652YGU5cshR2AHIn6iB0oIb2oYVzlzthNs +j+EEIMgZRQzrW1aEf2BTYlwCjRqCYoE0P6fc6RrPMYS+fLmFkjEikrC9mduoWItc +FtTLD/OY0xhXb5lF9UFFNmQSqMvagfI4GQ9vGg78vatSODw+Js88MqaQa3lbzcP3 +so5Bz2hBMY7bKjppUvseJtZETjSF4pjOVDGdVGpXis1iZj1DkP0IyXgt+u2LkvhQ +rYtYZHxEUVwjJyEtd0kvXDq5g5jkEO6A9aOMBUU4B4cbrRNVSIfpXPJ7koZpIRwd +A02+K21gS+DEkE8wBES+Qsuv8lTkTBpSFh60M8snrmJZ/2nJmX0SQ+kMakaCuh0h +q5orm9wFUiOPOfWJe7JNHJ1pH9Yi+elbQEr4yKc2tuRuLXVNRlMNSWyaEjCUqpi1 +-----END RSA PRIVATE KEY----- diff --git a/tests/test_ssl_args.rb b/tests/test_ssl_args.rb index 077685250..8e7804f1a 100644 --- a/tests/test_ssl_args.rb +++ b/tests/test_ssl_args.rb @@ -27,6 +27,35 @@ def test_tls_params_file_doesnt_exist end end + def test_tls_cert_not_defined_twice + cert_file_path="#{__dir__}/client.crt" + cert=File.read "#{__dir__}/client.crt" + + assert_raises EM::BadCertParams do + client_server client: {cert: cert, cert_chain_file: cert_file_path} + end + end + + def test_tls_key_not_defined_twice + cert_file_path="#{__dir__}/client.crt" + key_file_path="#{__dir__}/client.key" + key=File.read "#{__dir__}/client.key" + + assert_raises EM::BadPrivateKeyParams do + client_server client: {private_key_file: key_file_path, private_key: key, cert_chain_file: cert_file_path} + end + end + + def test_tls_key_requires_cert + #specifying a key but no cert will generate an error at SSL level + #with a misleading text + #140579476657920:error:1417A0C1:SSL routines:tls_post_process_client_hello:no shared cipher + + assert_raises EM::BadParams do + client_server client: {private_key_file: "#{__dir__}/client.key"} + end + end + def _test_tls_params_file_improper priv_file_path = Tempfile.new('em_test').path cert_file_path = Tempfile.new('em_test').path diff --git a/tests/test_ssl_inline_cert.rb b/tests/test_ssl_inline_cert.rb new file mode 100644 index 000000000..0db751c83 --- /dev/null +++ b/tests/test_ssl_inline_cert.rb @@ -0,0 +1,116 @@ +# frozen_string_literal: true + +require_relative 'em_test_helper' + +class TestSSLInlineCert < Test::Unit::TestCase + + require_relative 'em_ssl_handlers' + include EMSSLHandlers + + CERT_FILE="#{__dir__}/client.crt" + PRIVATE_KEY_FILE="#{__dir__}/client.key" + ENCODED_KEY_FILE="#{__dir__}/encoded_client.key" + + CERT = File.read CERT_FILE + PRIVATE_KEY = File.read PRIVATE_KEY_FILE + ENCODED_KEY = File.read ENCODED_KEY_FILE + + ENCODED_KEY_PASS = 'nicercat' + + def test_proper_key_required + # an assert in ssl.ccp code make this fail + # with no way of catching the error + omit_if(rbx?) + + bad_key=PRIVATE_KEY.dup + assert(bad_key[100]!=4) + bad_key[100]='4' + + server = { verify_peer: true, ssl_verify_result: true } + + assert_raises EM::InvalidPrivateKey do + client_server Client, Server, + client: { private_key: bad_key, + cert: CERT }, + server: server + end + refute Client.handshake_completed? + end + + def test_accept_server_key_inline_cert_inlince + omit_if(rbx?) + + server = { verify_peer: true, ssl_verify_result: true } + + client_server Client, Server, + client: { private_key: PRIVATE_KEY, + cert: CERT }, + server: server + + assert Client.handshake_completed? + assert Server.handshake_completed? + assert_equal CERT, Server.cert + end + + def test_accept_server_encoded_key_inline_cert_inlince + omit_if(rbx?) + + server = { verify_peer: true, ssl_verify_result: true } + + client_server Client, Server, + client: { private_key: ENCODED_KEY, + private_key_pass: ENCODED_KEY_PASS, + cert: CERT }, + server: server + + assert Client.handshake_completed? + assert Server.handshake_completed? + assert_equal CERT, Server.cert + end + + def test_accept_server_key_from_file_cert_inline + omit_if(rbx?) + + server = { verify_peer: true, ssl_verify_result: true } + + client_server Client, Server, + client: { private_key_file: PRIVATE_KEY_FILE, + cert: CERT }, + server: server + + assert Client.handshake_completed? + assert Server.handshake_completed? + assert_equal CERT, Server.cert + end + + def test_accept_server_key_inline_cert_from_file + omit_if(rbx?) + + server = { verify_peer: true, ssl_verify_result: true } + + client_server Client, Server, + client: { private_key: PRIVATE_KEY, + cert_chain_file: CERT_FILE }, + server: server + + assert Client.handshake_completed? + assert Server.handshake_completed? + assert_equal CERT, Server.cert + end + + def test_accept_server_encoded_key_inline_cert_from_file + omit_if(rbx?) + + server = { verify_peer: true, ssl_verify_result: true } + + client_server Client, Server, + client: { private_key: ENCODED_KEY, + private_key_pass: ENCODED_KEY_PASS, + cert_chain_file: CERT_FILE }, + server: server + + assert Client.handshake_completed? + assert Server.handshake_completed? + assert_equal CERT, Server.cert + end +end if EM.ssl? diff --git a/tests/test_ssl_verify.rb b/tests/test_ssl_verify.rb index 6780c42ed..b93e11411 100644 --- a/tests/test_ssl_verify.rb +++ b/tests/test_ssl_verify.rb @@ -12,6 +12,10 @@ class TestSSLVerify < Test::Unit::TestCase CLIENT_CERT = { private_key_file: "#{__dir__}/client.key", cert_chain_file: "#{__dir__}/client.crt" } + ENCODED_CLIENT_CERT = { private_key_file: "#{__dir__}/encoded_client.key", + private_key_pass: 'nicercat', + cert_chain_file: "#{__dir__}/client.crt" } + def test_fail_no_peer_cert omit_if(rbx?) @@ -37,6 +41,19 @@ def test_accept_server assert Server.handshake_completed? end + def test_encoded_accept_server + omit_if(EM.library_type == :pure_ruby) # Server has a default cert chain + omit_if(rbx?) + + server = { verify_peer: true, ssl_verify_result: true } + + client_server Client, Server, client: ENCODED_CLIENT_CERT, server: server + + assert Client.handshake_completed? + assert Server.handshake_completed? + assert_equal CERT_FROM_FILE, Server.cert + end + def test_deny_server omit_if(EM.library_type == :pure_ruby) # Server has a default cert chain omit_if(rbx?) From 5e01f584098d93ddba73c03af4f5033c44e04e78 Mon Sep 17 00:00:00 2001 From: David Margery Date: Fri, 10 May 2019 16:25:54 +0200 Subject: [PATCH 227/343] Extend handling cert & key as ssl params to server This gets us closer to fixing the TODO in ssl.ccp: TODO: We need a full-blown capability to work with user-supplied keypairs and properly-signed certificates. --- ext/ssl.cpp | 120 ++++++++++++++--------------- tests/em_ssl_handlers.rb | 14 +++- tests/test_ssl_inline_cert.rb | 140 +++++++++++++++++++++++++++++----- 3 files changed, 195 insertions(+), 79 deletions(-) diff --git a/ext/ssl.cpp b/ext/ssl.cpp index 35cabd306..d8ab09dcb 100644 --- a/ext/ssl.cpp +++ b/ext/ssl.cpp @@ -188,24 +188,70 @@ SslContext_t::SslContext_t (bool is_server, const std::string &privkeyfile, cons SSL_CTX_set_mode (pCtx, SSL_MODE_RELEASE_BUFFERS); #endif - if (bIsServer) { - - // The SSL_CTX calls here do NOT allocate memory. - int e; - if (privkeyfile.length() > 0) - e = SSL_CTX_use_PrivateKey_file (pCtx, privkeyfile.c_str(), SSL_FILETYPE_PEM); - else - e = SSL_CTX_use_PrivateKey (pCtx, DefaultPrivateKey); + int e; + // As indicated in man(3) ssl_ctx_use_privatekey_file + // To change a certificate, private key pair the new certificate needs to be set with + // SSL_use_certificate() or SSL_CTX_use_certificate() before setting the private key with SSL_CTX_use_PrivateKey() or SSL_use_PrivateKey(). + if (certchainfile.length() > 0) { + e = SSL_CTX_use_certificate_chain_file (pCtx, certchainfile.c_str()); if (e <= 0) ERR_print_errors_fp(stderr); assert (e > 0); - - if (certchainfile.length() > 0) - e = SSL_CTX_use_certificate_chain_file (pCtx, certchainfile.c_str()); - else - e = SSL_CTX_use_certificate (pCtx, DefaultCertificate); + } + if (cert.length() > 0) { + BIO *bio = BIO_new_mem_buf (cert.c_str(), -1); + assert(bio); + BIO_set_mem_eof_return(bio, 0); + X509 * clientCertificate = PEM_read_bio_X509 (bio, NULL, NULL, 0); + e = SSL_CTX_use_certificate (pCtx, clientCertificate); + X509_free(clientCertificate); + BIO_free (bio); + if (e <= 0) ERR_print_errors_fp(stderr); + assert (e > 0); + } + if (privkeyfile.length() > 0) { + if (privkeypass.length() > 0) { + SSL_CTX_set_default_passwd_cb_userdata(pCtx, const_cast(privkeypass.c_str())); + } + e = SSL_CTX_use_PrivateKey_file (pCtx, privkeyfile.c_str(), SSL_FILETYPE_PEM); if (e <= 0) ERR_print_errors_fp(stderr); assert (e > 0); + } + if (privkey.length() > 0) { + BIO *bio = BIO_new_mem_buf (privkey.c_str(), -1); + assert(bio); + BIO_set_mem_eof_return(bio, 0); + EVP_PKEY * clientPrivateKey = PEM_read_bio_PrivateKey (bio, NULL, NULL, const_cast(privkeypass.c_str())); + e = SSL_CTX_use_PrivateKey (pCtx, clientPrivateKey); + EVP_PKEY_free(clientPrivateKey); + BIO_free (bio); + if (e <= 0) { + BIO *bio_err = BIO_new(BIO_s_mem()); + std::string error_msg; + if (bio_err != NULL) { + ERR_print_errors(bio_err); + char* buf; + long size = BIO_get_mem_data(bio_err, &buf); + error_msg.assign(buf,size); + BIO_free(bio_err); + } + throw std::runtime_error (error_msg); + } + assert (e > 0); + } + if (bIsServer) { + if (certchainfile.length() == 0 && cert.length() == 0) { + // ensure default private material is configured for ssl + e = SSL_CTX_use_certificate (pCtx, DefaultCertificate); + if (e <= 0) ERR_print_errors_fp(stderr); + assert (e > 0); + } + if (privkeyfile.length() == 0 && privkey.length() == 0) { + // ensure default private material is configured for ssl + e = SSL_CTX_use_PrivateKey (pCtx, DefaultPrivateKey); + if (e <= 0) ERR_print_errors_fp(stderr); + assert (e > 0); + } if (dhparam.length() > 0) { DH *dh; BIO *bio; @@ -271,54 +317,6 @@ SslContext_t::SslContext_t (bool is_server, const std::string &privkeyfile, cons SSL_CTX_sess_set_cache_size (pCtx, 128); SSL_CTX_set_session_id_context (pCtx, (unsigned char*)"eventmachine", 12); } - else { - int e; - // As indicated in man(3) ssl_ctx_use_privatekey_file - // To change a certificate, private key pair the new certificate needs to be set with - // SSL_use_certificate() or SSL_CTX_use_certificate() before setting the private key with SSL_CTX_use_PrivateKey() or SSL_use_PrivateKey(). - if (certchainfile.length() > 0) { - e = SSL_CTX_use_certificate_chain_file (pCtx, certchainfile.c_str()); - if (e <= 0) ERR_print_errors_fp(stderr); - assert (e > 0); - } - if (cert.length() > 0) { - BIO *bio = BIO_new_mem_buf (cert.c_str(), -1); - BIO_set_mem_eof_return(bio, 0); - X509 * clientCertificate = PEM_read_bio_X509 (bio, NULL, NULL, 0); - e = SSL_CTX_use_certificate (pCtx, clientCertificate); - X509_free(clientCertificate); - BIO_free (bio); - if (e <= 0) ERR_print_errors_fp(stderr); - assert (e > 0); - } - if (privkeyfile.length() > 0) { - if (privkeypass.length() > 0) { - SSL_CTX_set_default_passwd_cb_userdata(pCtx, const_cast(privkeypass.c_str())); - } - e = SSL_CTX_use_PrivateKey_file (pCtx, privkeyfile.c_str(), SSL_FILETYPE_PEM); - if (e <= 0) ERR_print_errors_fp(stderr); - assert (e > 0); - } - if (privkey.length() > 0) { - BIO *bio = BIO_new_mem_buf (privkey.c_str(), -1); - BIO_set_mem_eof_return(bio, 0); - EVP_PKEY * clientPrivateKey = PEM_read_bio_PrivateKey (bio, NULL, NULL, const_cast(privkeypass.c_str())); - e = SSL_CTX_use_PrivateKey (pCtx, clientPrivateKey); - EVP_PKEY_free(clientPrivateKey); - BIO_free (bio); - if (e <= 0) { - BIO *bio_err = BIO_new(BIO_s_mem()); - ERR_print_errors(bio_err); - char* buf; - long size = BIO_get_mem_data(bio_err, &buf); - std::string error_msg; - error_msg.assign(buf,size); - BIO_free(bio_err); - throw std::runtime_error (error_msg); - } - assert (e > 0); - } - } } diff --git a/tests/em_ssl_handlers.rb b/tests/em_ssl_handlers.rb index e14036a8e..fd6ac5b9a 100644 --- a/tests/em_ssl_handlers.rb +++ b/tests/em_ssl_handlers.rb @@ -45,13 +45,16 @@ module Client def initialize(tls = nil) @@tls = tls ? tls.dup : tls @@handshake_completed = false + @@cert = nil @@cert_value = nil @@cipher_bits = nil @@cipher_name = nil @@cipher_protocol = nil + @@ssl_verify_result = @@tls ? @@tls.delete(:ssl_verify_result) : nil @@client_unbind = @@tls ? @@tls.delete(:client_unbind) : nil end + def self.cert ; @@cert end def self.cert_value ; @@cert_value end def self.cipher_bits ; @@cipher_bits end def self.cipher_name ; @@cipher_name end @@ -66,6 +69,15 @@ def post_init end end + def ssl_verify_peer(cert) + @@cert = cert + if @@ssl_verify_result.is_a?(String) && @@ssl_verify_result.start_with?("|RAISE|") + raise @@ssl_verify_result.sub('|RAISE|', '') + else + @@ssl_verify_result + end + end + def ssl_handshake_completed @@handshake_completed = true @@cert_value = get_peer_cert @@ -150,4 +162,4 @@ def client_server(c_hndlr = Client, s_hndlr = Server, EM.connect IP, PORT, c_hndlr, client end end -end if EM.ssl? \ No newline at end of file +end if EM.ssl? diff --git a/tests/test_ssl_inline_cert.rb b/tests/test_ssl_inline_cert.rb index 0db751c83..d258181f6 100644 --- a/tests/test_ssl_inline_cert.rb +++ b/tests/test_ssl_inline_cert.rb @@ -17,7 +17,7 @@ class TestSSLInlineCert < Test::Unit::TestCase ENCODED_KEY_PASS = 'nicercat' - def test_proper_key_required + def test_proper_key_required_for_client # an assert in ssl.ccp code make this fail # with no way of catching the error omit_if(rbx?) @@ -25,7 +25,7 @@ def test_proper_key_required bad_key=PRIVATE_KEY.dup assert(bad_key[100]!=4) bad_key[100]='4' - + server = { verify_peer: true, ssl_verify_result: true } assert_raises EM::InvalidPrivateKey do @@ -37,14 +37,67 @@ def test_proper_key_required refute Client.handshake_completed? end - def test_accept_server_key_inline_cert_inlince + def test_proper_key_required_for_server + # an assert in ssl.ccp code make this fail + # with no way of catching the error + omit_if(rbx?) + + bad_key=PRIVATE_KEY.dup + assert(bad_key[100]!=4) + bad_key[100]='4' + + server = { verify_peer: true, ssl_verify_result: true, + private_key: bad_key, cert: CERT } + + assert_raises EM::InvalidPrivateKey do + client_server Client, Server, + server: server + end + refute Server.handshake_completed? + end + + def test_accept_client_key_inline_cert_inline + omit_if(rbx?) + + server = { verify_peer: true, ssl_verify_result: true } + client = { private_key: PRIVATE_KEY, + cert: CERT } + + client_server Client, Server, + client: client, + server: server + + assert Client.handshake_completed? + assert Server.handshake_completed? + assert_equal CERT, Server.cert + end + + def test_accept_server_key_inline_cert_inline + omit_if(rbx?) + + client = { verify_peer: true, ssl_verify_result: true } + server = { private_key: PRIVATE_KEY, + cert: CERT } + + client_server Client, Server, + client: client, + server: server + + assert Client.handshake_completed? + assert Server.handshake_completed? + assert_equal CERT, Client.cert + end + + def test_accept_client_encoded_key_inline_cert_inlince omit_if(rbx?) server = { verify_peer: true, ssl_verify_result: true } + client = { private_key: ENCODED_KEY, + private_key_pass: ENCODED_KEY_PASS, + cert: CERT } client_server Client, Server, - client: { private_key: PRIVATE_KEY, - cert: CERT }, + client: client, server: server assert Client.handshake_completed? @@ -55,12 +108,29 @@ def test_accept_server_key_inline_cert_inlince def test_accept_server_encoded_key_inline_cert_inlince omit_if(rbx?) + client = { verify_peer: true, ssl_verify_result: true } + server = { private_key: ENCODED_KEY, + private_key_pass: ENCODED_KEY_PASS, + cert: CERT } + + client_server Client, Server, + client: client, + server: server + + assert Client.handshake_completed? + assert Server.handshake_completed? + assert_equal CERT, Client.cert + end + + def test_accept_client_key_from_file_cert_inline + omit_if(rbx?) + server = { verify_peer: true, ssl_verify_result: true } + client = { private_key_file: PRIVATE_KEY_FILE, + cert: CERT } client_server Client, Server, - client: { private_key: ENCODED_KEY, - private_key_pass: ENCODED_KEY_PASS, - cert: CERT }, + client: client, server: server assert Client.handshake_completed? @@ -71,11 +141,28 @@ def test_accept_server_encoded_key_inline_cert_inlince def test_accept_server_key_from_file_cert_inline omit_if(rbx?) + client = { verify_peer: true, ssl_verify_result: true } + server = { private_key_file: PRIVATE_KEY_FILE, + cert: CERT } + + client_server Client, Server, + client: client, + server: server + + assert Client.handshake_completed? + assert Server.handshake_completed? + assert_equal CERT, Client.cert + end + + def test_accept_client_key_inline_cert_from_file + omit_if(rbx?) + server = { verify_peer: true, ssl_verify_result: true } + client = { private_key: PRIVATE_KEY, + cert_chain_file: CERT_FILE } client_server Client, Server, - client: { private_key_file: PRIVATE_KEY_FILE, - cert: CERT }, + client: client, server: server assert Client.handshake_completed? @@ -86,11 +173,29 @@ def test_accept_server_key_from_file_cert_inline def test_accept_server_key_inline_cert_from_file omit_if(rbx?) + client = { verify_peer: true, ssl_verify_result: true } + server = { private_key: PRIVATE_KEY, + cert_chain_file: CERT_FILE } + + client_server Client, Server, + client: client, + server: server + + assert Client.handshake_completed? + assert Server.handshake_completed? + assert_equal CERT, Client.cert + end + + def test_accept_client_encoded_key_inline_cert_from_file + omit_if(rbx?) + server = { verify_peer: true, ssl_verify_result: true } + client = { private_key: ENCODED_KEY, + private_key_pass: ENCODED_KEY_PASS, + cert_chain_file: CERT_FILE } client_server Client, Server, - client: { private_key: PRIVATE_KEY, - cert_chain_file: CERT_FILE }, + client: client, server: server assert Client.handshake_completed? @@ -101,16 +206,17 @@ def test_accept_server_key_inline_cert_from_file def test_accept_server_encoded_key_inline_cert_from_file omit_if(rbx?) - server = { verify_peer: true, ssl_verify_result: true } + client = { verify_peer: true, ssl_verify_result: true} + server = { private_key: ENCODED_KEY, + private_key_pass: ENCODED_KEY_PASS, + cert_chain_file: CERT_FILE } client_server Client, Server, - client: { private_key: ENCODED_KEY, - private_key_pass: ENCODED_KEY_PASS, - cert_chain_file: CERT_FILE }, + client: client, server: server assert Client.handshake_completed? assert Server.handshake_completed? - assert_equal CERT, Server.cert + assert_equal CERT, Client.cert end end if EM.ssl? From 14a521ac73462fba425775615b4729da45b7fa1d Mon Sep 17 00:00:00 2001 From: David Margery Date: Fri, 10 May 2019 16:34:40 +0200 Subject: [PATCH 228/343] Update TODO comment as some needed work was done Specifically, the usage of the specified private-key and cert-chain filenames no longer only applies to client-side connections at this point. Server connections currently use the default materials only when no other is given --- ext/ssl.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/ext/ssl.cpp b/ext/ssl.cpp index d8ab09dcb..f16b93a2c 100644 --- a/ext/ssl.cpp +++ b/ext/ssl.cpp @@ -125,10 +125,7 @@ SslContext_t::SslContext_t (bool is_server, const std::string &privkeyfile, cons PrivateKey (NULL), Certificate (NULL) { - /* TODO: the usage of the specified private-key and cert-chain filenames only applies to - * client-side connections at this point. Server connections currently use the default materials. - * That needs to be fixed asap. - * Also, in this implementation, server-side connections use statically defined X-509 defaults. + /* TODO: Also, in this implementation, server-side connections use statically defined X-509 defaults. * One thing I'm really not clear on is whether or not you have to explicitly free X509 and EVP_PKEY * objects when we call our destructor, or whether just calling SSL_CTX_free is enough. */ From c0e642cb14652367f2b8719f5d2f22fe609b4556 Mon Sep 17 00:00:00 2001 From: David Margery Date: Fri, 10 May 2019 17:13:29 +0200 Subject: [PATCH 229/343] Add tests when server cert and key in files --- tests/test_ssl_verify.rb | 49 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 5 deletions(-) diff --git a/tests/test_ssl_verify.rb b/tests/test_ssl_verify.rb index b93e11411..fa5604873 100644 --- a/tests/test_ssl_verify.rb +++ b/tests/test_ssl_verify.rb @@ -9,10 +9,10 @@ class TestSSLVerify < Test::Unit::TestCase CERT_FROM_FILE = File.read "#{__dir__}/client.crt" - CLIENT_CERT = { private_key_file: "#{__dir__}/client.key", + CERT_CONFIG = { private_key_file: "#{__dir__}/client.key", cert_chain_file: "#{__dir__}/client.crt" } - ENCODED_CLIENT_CERT = { private_key_file: "#{__dir__}/encoded_client.key", + ENCODED_CERT_CONFIG = { private_key_file: "#{__dir__}/encoded_client.key", private_key_pass: 'nicercat', cert_chain_file: "#{__dir__}/client.crt" } @@ -34,36 +34,75 @@ def test_accept_server server = { verify_peer: true, ssl_verify_result: true } - client_server Client, Server, client: CLIENT_CERT, server: server + client_server Client, Server, client: CERT_CONFIG, server: server assert_equal CERT_FROM_FILE, Server.cert assert Client.handshake_completed? assert Server.handshake_completed? end + def test_accept_client + omit_if(EM.library_type == :pure_ruby) # Server has a default cert chain + omit_if(rbx?) + + client = { verify_peer: true, ssl_verify_result: true } + + client_server Client, Server, server: CERT_CONFIG, client: client + + assert_equal CERT_FROM_FILE, Client.cert + assert Client.handshake_completed? + assert Server.handshake_completed? + end + def test_encoded_accept_server omit_if(EM.library_type == :pure_ruby) # Server has a default cert chain omit_if(rbx?) server = { verify_peer: true, ssl_verify_result: true } - client_server Client, Server, client: ENCODED_CLIENT_CERT, server: server + client_server Client, Server, client: ENCODED_CERT_CONFIG, server: server assert Client.handshake_completed? assert Server.handshake_completed? assert_equal CERT_FROM_FILE, Server.cert end + def test_encoded_accept_client + omit_if(EM.library_type == :pure_ruby) # Server has a default cert chain + omit_if(rbx?) + + client = { verify_peer: true, ssl_verify_result: true } + + client_server Client, Server, server: ENCODED_CERT_CONFIG, client: client + + assert Client.handshake_completed? + assert Server.handshake_completed? + assert_equal CERT_FROM_FILE, Client.cert + end + def test_deny_server omit_if(EM.library_type == :pure_ruby) # Server has a default cert chain omit_if(rbx?) server = { verify_peer: true, ssl_verify_result: false } - client_server Client, Server, client: CLIENT_CERT, server: server + client_server Client, Server, client: CERT_CONFIG, server: server assert_equal CERT_FROM_FILE, Server.cert refute Client.handshake_completed? unless "TLSv1.3" == Client.cipher_protocol refute Server.handshake_completed? end + + def test_deny_client + omit_if(EM.library_type == :pure_ruby) # Server has a default cert chain + omit_if(rbx?) + + client = { verify_peer: true, ssl_verify_result: false } + + client_server Client, Server, server: CERT_CONFIG, client: client + + refute Client.handshake_completed? unless "TLSv1.3" == Client.cipher_protocol + refute Server.handshake_completed? + assert_equal CERT_FROM_FILE, Client.cert + end end if EM.ssl? From cff3b48974a486237d6631c05247a0e2d8a0c442 Mon Sep 17 00:00:00 2001 From: jolan Date: Fri, 24 May 2019 14:50:17 -0500 Subject: [PATCH 230/343] add failing test where comm_inactivity_timeout takes too long (#889) --- tests/test_inactivity_timeout.rb | 45 +++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/tests/test_inactivity_timeout.rb b/tests/test_inactivity_timeout.rb index 41b99a88c..eccd6a788 100644 --- a/tests/test_inactivity_timeout.rb +++ b/tests/test_inactivity_timeout.rb @@ -21,7 +21,7 @@ def test_set_and_get end def test_for_real - start, finish = nil + start, finish, reason = nil timeout_start = Module.new do define_method :post_init do @@ -47,6 +47,49 @@ def test_for_real } # Travis can vary from 0.02 to 0.17, Appveyor maybe as low as 0.01 assert_in_delta(0.09, (finish - start), 0.08) + + # simplified reproducer for comm_inactivity_timeout taking twice as long + # as requested -- https://github.com/eventmachine/eventmachine/issues/554 + timeout_start_tls = Module.new do + define_method :post_init do + start = Time.now + start_tls + end + define_method :receive_data do |data| + send_data ">>>you sent: #{data}" + end + end + + timeout_handler_tls = Module.new do + define_method :connection_completed do + start_tls + end + + define_method :ssl_handshake_completed do + @timer = EM::PeriodicTimer.new(0.05) do + #puts "get_idle_time: #{get_idle_time} inactivity: #{comm_inactivity_timeout}" + end + send_data "hello world" + end + + define_method :unbind do |r| + finish = Time.now + reason = r + EM.stop + end + end + + EM.run { + setup_timeout 1.4 + EM.start_server("127.0.0.1", 12345, timeout_start_tls) + c = EM.connect("127.0.0.1", 12345, timeout_handler_tls) + c.comm_inactivity_timeout = 0.15 + } + + # .30 is double the timeout and not acceptable + assert_in_delta(0.15, (finish - start), 0.14) + # make sure it was a timeout and not a TLS error + assert_equal Errno::ETIMEDOUT, reason end else warn "EM.comm_inactivity_timeout not implemented, skipping tests in #{__FILE__}" From 287a65c8553adb94045cd018df30ca39f2d8026d Mon Sep 17 00:00:00 2001 From: jolan Date: Fri, 24 May 2019 14:50:39 -0500 Subject: [PATCH 231/343] adjust inactivity comparison so it will match nearly equal values (#890) --- ext/ed.cpp | 11 ++++++++++- ext/em.cpp | 10 ++++++++++ ext/em.h | 1 + 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/ext/ed.cpp b/ext/ed.cpp index cc61c41a6..f73c7fdb3 100644 --- a/ext/ed.cpp +++ b/ext/ed.cpp @@ -1584,6 +1584,15 @@ ConnectionDescriptor::Heartbeat void ConnectionDescriptor::Heartbeat() { + /* When TLS is enabled, it can skew the delivery of heartbeats and + * the LastActivity time-keeping by hundreds of microseconds on fast + * machines up to tens of thousands of microseconds on very slow + * machines. To prevent failing to timeout in a timely fashion we use + * the timer-quantum to compensate for the discrepancy so the + * comparisons are more likely to match when they are nearly equal. + */ + uint64_t skew = MyEventMachine->GetTimerQuantum(); + /* Only allow a certain amount of time to go by while waiting * for a pending connect. If it expires, then kill the socket. * For a connected socket, close it if its inactivity timer @@ -1598,7 +1607,7 @@ void ConnectionDescriptor::Heartbeat() } } else { - if (InactivityTimeout && ((MyEventMachine->GetCurrentLoopTime() - LastActivity) >= InactivityTimeout)) { + if (InactivityTimeout && ((skew + MyEventMachine->GetCurrentLoopTime() - LastActivity) >= InactivityTimeout)) { UnbindReasonCode = ETIMEDOUT; ScheduleClose (false); //bCloseNow = true; diff --git a/ext/em.cpp b/ext/em.cpp index 8160d94f3..7c953e800 100644 --- a/ext/em.cpp +++ b/ext/em.cpp @@ -220,6 +220,16 @@ bool EventMachine_t::Stopping() return bTerminateSignalReceived; } +/******************************* +EventMachine_t::GetTimerQuantum +*******************************/ + +uint64_t EventMachine_t::GetTimerQuantum() +{ + /* Convert timer-quantum to microseconds */ + return (Quantum.tv_sec * 1000 + (Quantum.tv_usec + 500) / 1000) * 1000; +} + /******************************* EventMachine_t::SetTimerQuantum *******************************/ diff --git a/ext/em.h b/ext/em.h index e5b826bfd..2728cb171 100644 --- a/ext/em.h +++ b/ext/em.h @@ -123,6 +123,7 @@ class EventMachine_t void ArmKqueueWriter (EventableDescriptor*); void ArmKqueueReader (EventableDescriptor*); + uint64_t GetTimerQuantum(); void SetTimerQuantum (int); static void SetuidString (const char*); static int SetRlimitNofile (int); From e6b46c3da9093904db9cc901d267149cb580e026 Mon Sep 17 00:00:00 2001 From: MSP-Greg Date: Tue, 9 Jul 2019 12:18:19 -0500 Subject: [PATCH 232/343] ext/ssl.cpp & ext/extconf.rb - update for TLS_server_method --- ext/extconf.rb | 6 ++++++ ext/ssl.cpp | 5 ++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/ext/extconf.rb b/ext/extconf.rb index d4762f6dd..188f1abcc 100644 --- a/ext/extconf.rb +++ b/ext/extconf.rb @@ -238,6 +238,12 @@ def pkg_config_wrapper(pretty_name, name) have_func('gethrtime') # Older Solaris and HP-UX end +# OpenSSL version checks +# below are yes for 1.1.0 & later, may need to check func rather than macro +# with versions after 1.1.1 +have_func "TLS_server_method" , "openssl/ssl.h" +have_macro "SSL_CTX_set_min_proto_version", "openssl/ssl.h" + # Hack so that try_link will test with a C++ compiler instead of a C compiler TRY_LINK.sub!('$(CC)', '$(CXX)') diff --git a/ext/ssl.cpp b/ext/ssl.cpp index c83e5b09f..516cd7b52 100644 --- a/ext/ssl.cpp +++ b/ext/ssl.cpp @@ -144,8 +144,11 @@ SslContext_t::SslContext_t (bool is_server, const std::string &privkeyfile, cons InitializeDefaultCredentials(); } - + #ifdef HAVE_TLS_SERVER_METHOD + pCtx = SSL_CTX_new (bIsServer ? TLS_server_method() : TLS_client_method()); + #else pCtx = SSL_CTX_new (bIsServer ? SSLv23_server_method() : SSLv23_client_method()); + #endif if (!pCtx) throw std::runtime_error ("no SSL context"); From 0d7bfa7d89d2a7e17aff766bc0fb478855dbfa64 Mon Sep 17 00:00:00 2001 From: MSP-Greg Date: Tue, 9 Jul 2019 12:19:00 -0500 Subject: [PATCH 233/343] travis.yml - update to test agains OpenSSL 1.0.1 thru 1.1.1 --- .travis.yml | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/.travis.yml b/.travis.yml index 393ea4a23..e2ac6ff26 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,6 @@ dist: xenial language: ruby +cache: bundler bundler_args: --without documentation before_install: @@ -7,8 +8,8 @@ before_install: # remove 2.7.0 code when Travis removes rubygems 2.7.8 from ruby-head build - | rv="$(ruby -e 'STDOUT.write RUBY_VERSION')"; - if [ "$rv" \< "2.3" ]; then gem update --system 2.7.8 --no-document - elif [ "$rv" \< "2.7" ]; then gem update --system --no-document --conservative + if [ "$rv" \< "2.3" ]; then gem update --system 2.7.9 --no-document + elif [ "$rv" \< "2.6" ]; then gem update --system --no-document --conservative fi before_script: @@ -19,6 +20,7 @@ script: env: global: + - env: OS="xenial 16.04" - TESTOPTS="-v --no-show-detail-immediately" rvm: - 2.6 @@ -29,25 +31,34 @@ rvm: - 2.1 - 2.0.0 - ruby-head -# - rbx -# - rbx-2 -# - rbx-3 -# - jruby-1.7 -# - jruby-9 + matrix: fast_finish: true allow_failures: - rvm: ruby-head -# - rvm: rbx -# - rvm: rbx-2 -# - rvm: rbx-3 -# - rvm: jruby-1.7 -# - rvm: jruby-9 + - rvm: ruby-head + env: + - RUBYOPT=--jit +# - rvm: jruby-9.2.7.0 +# - rvm: jruby-head include: + - rvm: 2.3 + dist: trusty + env: OS="trusty 14.04" + - rvm: 2.6.3 + dist: bionic + env: OS="bionic 18.04" - rvm: 2.6 os: osx + osx_image: xcode10.2 + env: OS="osx xcode 10.2" - rvm: 2.3 os: osx + env: OS="osx" - rvm: 2.6 + env: RUBYOPT=--jit + - rvm: ruby-head env: - RUBYOPT=--jit +# - rvm: jruby-9.2.7.0 +# - rvm: jruby-head \ No newline at end of file From 3d3414b8d3befe7125359ebf7dfb6e13ba19b162 Mon Sep 17 00:00:00 2001 From: MSP-Greg Date: Tue, 9 Jul 2019 13:16:32 -0500 Subject: [PATCH 234/343] update tests for bionic 18.04 --- tests/test_basic.rb | 2 +- tests/test_resolver.rb | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/test_basic.rb b/tests/test_basic.rb index 3ce5db51c..529436837 100644 --- a/tests/test_basic.rb +++ b/tests/test_basic.rb @@ -5,7 +5,7 @@ def setup @port = next_port end - INVALID = "(not known|no data of the requested|No such host is known)" + INVALID = "(not known|no data of the requested|No such host is known|Temporary failure in name resolution)" def test_connection_class_cache mod = Module.new diff --git a/tests/test_resolver.rb b/tests/test_resolver.rb index 75155f592..daeccf25a 100644 --- a/tests/test_resolver.rb +++ b/tests/test_resolver.rb @@ -86,7 +86,8 @@ def test_localhost d = EM::DNS::Resolver.resolve "localhost" d.errback { assert false } d.callback { |r| - assert_include(["127.0.0.1", "::1"], r.first) + # "127.0.1.1" added for testing on bionic 18.04 + assert_include(["127.0.0.1", "127.0.1.1", "::1"], r.first) assert_kind_of(Array, r) EM.stop From 3d217d92f2dd5319a991775c31ca3218001d0bc4 Mon Sep 17 00:00:00 2001 From: MSP-Greg Date: Tue, 9 Jul 2019 13:53:04 -0500 Subject: [PATCH 235/343] travis.yml - move jit to after rvm commands --- .travis.yml | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index e2ac6ff26..25a39e6c7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,6 +13,7 @@ before_install: fi before_script: + - if [ "$jit" == "yes" ]; then export RUBYOPT=--jit ; fi ; echo RUBYOPT is $RUBYOPT - bundle exec rake compile script: @@ -20,8 +21,8 @@ script: env: global: - - env: OS="xenial 16.04" - TESTOPTS="-v --no-show-detail-immediately" + rvm: - 2.6 - 2.5 @@ -37,28 +38,29 @@ matrix: allow_failures: - rvm: ruby-head - rvm: ruby-head - env: - - RUBYOPT=--jit + env: jit=yes # - rvm: jruby-9.2.7.0 # - rvm: jruby-head include: - rvm: 2.3 dist: trusty env: OS="trusty 14.04" - - rvm: 2.6.3 + - rvm: 2.6 dist: bionic env: OS="bionic 18.04" - rvm: 2.6 os: osx osx_image: xcode10.2 env: OS="osx xcode 10.2" + - rvm: 2.6 + os: osx + env: OS="osx" - rvm: 2.3 os: osx env: OS="osx" - rvm: 2.6 - env: RUBYOPT=--jit + env: jit=yes - rvm: ruby-head - env: - - RUBYOPT=--jit + env: jit=yes # - rvm: jruby-9.2.7.0 # - rvm: jruby-head \ No newline at end of file From 10fb0c47c9a030b716dacd4a8df7e34b2445169b Mon Sep 17 00:00:00 2001 From: Michel Boaventura Date: Tue, 20 Aug 2019 17:29:05 -0300 Subject: [PATCH 236/343] Improve line_protocol speed (#894) I'm having problems with current line_protocol implementation, causing up to 60% of my overall run time to be spent slicing the message buffer. I've change it to use ``String#each_line`` instead of ``slice`` which proves to be 200x faster: ```Ruby require 'benchmark' def new_version(size) buffer = Array.new(size, 'foo 123').join("\n") buffer.each_line do |line| if line[-1] == "\n" else buffer = line end end end def original(size) buffer = Array.new(size, 'foo 123').join("\n") while buffer.slice!(/(.*?)\r?\n/) do end end Benchmark.bmbm do |x| x.report('original') { original(100_000) } x.report('new_version') { new_version(100_000) } end ``` ``` Rehearsal ----------------------------------------------- original 4.891919 0.103960 4.995879 ( 4.993582) new_version 0.026254 0.000005 0.026259 ( 0.026250) -------------------------------------- total: 5.022138sec user system total real original 4.922760 0.031001 4.953761 ( 4.951560) new_version 0.026114 0.000000 0.026114 ( 0.026103) ``` The only downside is that ``each_line`` always returns the last part of the string, so ``"foo\nbar".each_line`` will yield both ``foo`` and ``bar``. To fix this I just use the fact that ``each_line`` actually returns the line terminators so, if I returns me a string without a new line I know the buffer has ended and the last "line" is the content of the new buffer. --- lib/em/protocols/line_protocol.rb | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/em/protocols/line_protocol.rb b/lib/em/protocols/line_protocol.rb index dfddae8c0..0bdef93a3 100644 --- a/lib/em/protocols/line_protocol.rb +++ b/lib/em/protocols/line_protocol.rb @@ -15,8 +15,12 @@ module LineProtocol def receive_data data (@buf ||= '') << data - while @buf.slice!(/(.*?)\r?\n/) - receive_line($1) + @buf.each_line do |line| + if line[-1] == "\n" + receive_line(line.chomp) + else + @buf = line + end end end From 8f9d51bc7370c60ed96e6573fe0206e3d5c75d2f Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Sat, 29 Feb 2020 15:42:58 -0800 Subject: [PATCH 237/343] Typo fix --- tests/jruby/test_jeventmachine.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/jruby/test_jeventmachine.rb b/tests/jruby/test_jeventmachine.rb index cbd0e3b2e..164caa520 100644 --- a/tests/jruby/test_jeventmachine.rb +++ b/tests/jruby/test_jeventmachine.rb @@ -1,5 +1,5 @@ if !(RUBY_PLATFORM =~ /java/) - puts "Ignorming tests in #{__FILE__}. They must be run in JRuby " + puts "Ignoring tests in #{__FILE__}. They must be run in JRuby " else require 'test/unit' require 'jeventmachine' From c594a877647b2758d8d9ae0867f03e4697d65f97 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Sat, 29 Feb 2020 15:44:46 -0800 Subject: [PATCH 238/343] Updates for Ruby 2.7 ANYARGS changes in rb_rescue --- ext/rubymain.cpp | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/ext/rubymain.cpp b/ext/rubymain.cpp index 06539434c..a354a0476 100644 --- a/ext/rubymain.cpp +++ b/ext/rubymain.cpp @@ -100,8 +100,9 @@ static inline VALUE ensure_conn(const uintptr_t signature) t_event_callback ****************/ -static inline void event_callback (struct em_event* e) +static inline VALUE event_callback (VALUE e_value) { + struct em_event *e = (struct em_event *)e_value; const uintptr_t signature = e->signature; int event = e->event; const char *data_str = e->data_str; @@ -114,40 +115,40 @@ static inline void event_callback (struct em_event* e) if (conn == Qnil) rb_raise (EM_eConnectionNotBound, "received %lu bytes of data for unknown signature: %" PRIFBSIG, data_num, signature); rb_funcall (conn, Intern_receive_data, 1, rb_str_new (data_str, data_num)); - return; + return Qnil; } case EM_CONNECTION_ACCEPTED: { rb_funcall (EmModule, Intern_event_callback, 3, BSIG2NUM(signature), INT2FIX(event), ULONG2NUM(data_num)); - return; + return Qnil; } case EM_CONNECTION_UNBOUND: { rb_funcall (EmModule, Intern_event_callback, 3, BSIG2NUM(signature), INT2FIX(event), ULONG2NUM(data_num)); - return; + return Qnil; } case EM_CONNECTION_COMPLETED: { VALUE conn = ensure_conn(signature); rb_funcall (conn, Intern_connection_completed, 0); - return; + return Qnil; } case EM_CONNECTION_NOTIFY_READABLE: { VALUE conn = ensure_conn(signature); rb_funcall (conn, Intern_notify_readable, 0); - return; + return Qnil; } case EM_CONNECTION_NOTIFY_WRITABLE: { VALUE conn = ensure_conn(signature); rb_funcall (conn, Intern_notify_writable, 0); - return; + return Qnil; } case EM_LOOPBREAK_SIGNAL: { rb_funcall (EmModule, Intern_run_deferred_callbacks, 0); - return; + return Qnil; } case EM_TIMER_FIRED: { @@ -159,14 +160,14 @@ static inline void event_callback (struct em_event* e) } else { rb_funcall (timer, Intern_call, 0); } - return; + return Qnil; } #ifdef WITH_SSL case EM_SSL_HANDSHAKE_COMPLETED: { VALUE conn = ensure_conn(signature); rb_funcall (conn, Intern_ssl_handshake_completed, 0); - return; + return Qnil; } case EM_SSL_VERIFY: { @@ -174,32 +175,35 @@ static inline void event_callback (struct em_event* e) VALUE should_accept = rb_funcall (conn, Intern_ssl_verify_peer, 1, rb_str_new(data_str, data_num)); if (RTEST(should_accept)) evma_accept_ssl_peer (signature); - return; + return Qnil; } #endif case EM_PROXY_TARGET_UNBOUND: { VALUE conn = ensure_conn(signature); rb_funcall (conn, Intern_proxy_target_unbound, 0); - return; + return Qnil; } case EM_PROXY_COMPLETED: { VALUE conn = ensure_conn(signature); rb_funcall (conn, Intern_proxy_completed, 0); - return; + return Qnil; } } + + return Qnil; } /******************* event_error_handler *******************/ -static void event_error_handler(VALUE self UNUSED, VALUE err) +static VALUE event_error_handler(VALUE self UNUSED, VALUE err) { VALUE error_handler = rb_ivar_get(EmModule, Intern_at_error_handler); rb_funcall (error_handler, Intern_call, 1, err); + return Qnil; } /********************** @@ -215,9 +219,9 @@ static void event_callback_wrapper (const uintptr_t signature, int event, const e.data_num = data_num; if (!rb_ivar_defined(EmModule, Intern_at_error_handler)) - event_callback(&e); + event_callback((VALUE)&e); else - rb_rescue((VALUE (*)(ANYARGS))event_callback, (VALUE)&e, (VALUE (*)(ANYARGS))event_error_handler, Qnil); + rb_rescue(event_callback, (VALUE)&e, event_error_handler, Qnil); } /************************** From c7bfa95f07eeae3ba9a17cca3e9ef3d161cd118d Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Sun, 1 Mar 2020 09:03:36 -0800 Subject: [PATCH 239/343] Adapt Ruby 2.7 compat ifdefs from the SWIG project --- ext/rubymain.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/ext/rubymain.cpp b/ext/rubymain.cpp index a354a0476..6067aff05 100644 --- a/ext/rubymain.cpp +++ b/ext/rubymain.cpp @@ -45,6 +45,16 @@ See the file COPYING for complete licensing information. # endif #endif +/* Adapted from SWIG's changes for Ruby 2.7 compatibility. + * Before Ruby 2.7, rb_rescue takes (VALUE (*))(ANYARGS) + * whereas in Ruby 2.7, rb_rescue takes (VALUE (*))(VALUE) + * */ +#if defined(__cplusplus) && !defined(RB_METHOD_DEFINITION_DECL) +# define VALUEFUNC(f) ((VALUE (*)(ANYARGS)) f) +#else +# define VALUEFUNC(f) (f) +#endif + /******* Statics *******/ @@ -221,7 +231,7 @@ static void event_callback_wrapper (const uintptr_t signature, int event, const if (!rb_ivar_defined(EmModule, Intern_at_error_handler)) event_callback((VALUE)&e); else - rb_rescue(event_callback, (VALUE)&e, event_error_handler, Qnil); + rb_rescue(VALUEFUNC(event_callback), (VALUE)&e, VALUEFUNC(event_error_handler), Qnil); } /************************** From d0cf7c42ba4c924e70482d03172eb63c7e7823e7 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Sun, 1 Mar 2020 09:07:27 -0800 Subject: [PATCH 240/343] Add Travis CI Ruby 2.7 --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 25a39e6c7..986fc4879 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,6 +24,7 @@ env: - TESTOPTS="-v --no-show-detail-immediately" rvm: + - 2.7 - 2.6 - 2.5 - 2.4 From a15fe657740949235dbf13e4fa4c98b0104311b1 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Sun, 1 Mar 2020 09:11:07 -0800 Subject: [PATCH 241/343] RFLOAT_VALUE is now available in all supported Ruby versions --- ext/rubymain.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ext/rubymain.cpp b/ext/rubymain.cpp index 6067aff05..d00e046c6 100644 --- a/ext/rubymain.cpp +++ b/ext/rubymain.cpp @@ -21,10 +21,6 @@ See the file COPYING for complete licensing information. #include "eventmachine.h" #include -#ifndef RFLOAT_VALUE -#define RFLOAT_VALUE(arg) RFLOAT(arg)->value -#endif - /* Adapted from NUM2BSIG / BSIG2NUM in ext/fiddle/conversions.h, * we'll call it a BSIG for Binding Signature here. */ #if SIZEOF_VOIDP == SIZEOF_LONG From 298271e8c215c16e50d58587aec2ca86ce3998ea Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Sun, 1 Mar 2020 10:01:16 -0800 Subject: [PATCH 242/343] Update Travis CI macOS Xcode versions --- .travis.yml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 986fc4879..73a99f57d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -51,14 +51,12 @@ matrix: env: OS="bionic 18.04" - rvm: 2.6 os: osx - osx_image: xcode10.2 - env: OS="osx xcode 10.2" + osx_image: xcode10.3 + env: OS="osx xcode 10.3" - rvm: 2.6 os: osx - env: OS="osx" - - rvm: 2.3 - os: osx - env: OS="osx" + osx_image: xcode11.3 + env: OS="osx xcode 11.3" - rvm: 2.6 env: jit=yes - rvm: ruby-head From cdb6784844927f512d4e1cb9e9d890a36fdf957b Mon Sep 17 00:00:00 2001 From: Iain Barnett Date: Mon, 2 Mar 2020 03:44:58 +0900 Subject: [PATCH 243/343] Website URL is no longer registered (#891) --- eventmachine.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eventmachine.gemspec b/eventmachine.gemspec index 49015bdcd..52e53257e 100644 --- a/eventmachine.gemspec +++ b/eventmachine.gemspec @@ -3,7 +3,7 @@ require File.expand_path('../lib/em/version', __FILE__) Gem::Specification.new do |s| s.name = 'eventmachine' s.version = EventMachine::VERSION - s.homepage = 'http://rubyeventmachine.com' + s.homepage = 'https://github.com/eventmachine/eventmachine' s.licenses = ['Ruby', 'GPL-2.0'] s.required_ruby_version = '>= 2.0.0' From 681bc4e995a3c860d08a22c48723eef2b7719ae4 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Sun, 1 Mar 2020 11:17:00 -0800 Subject: [PATCH 244/343] Remove debug puts from base class of EM::Connection (#911) --- lib/em/connection.rb | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/em/connection.rb b/lib/em/connection.rb index b5f8b0ad7..f2362cecd 100644 --- a/lib/em/connection.rb +++ b/lib/em/connection.rb @@ -100,9 +100,6 @@ def post_init # in your redefined implementation of receive_data. For a better understanding # of this, read through the examples of specific protocol handlers in EventMachine::Protocols # - # The base-class implementation (which will be invoked only if you didn't override it in your protocol handler) - # simply prints incoming data packet size to stdout. - # # @param [String] data Opaque incoming data. # @note Depending on the protocol, buffer sizes and OS networking stack configuration, incoming data may or may not be "a complete message". # It is up to this handler to detect content boundaries to determine whether all the content (for example, full HTTP request) @@ -114,7 +111,6 @@ def post_init # @see #send_data # @see file:docs/GettingStarted.md EventMachine tutorial def receive_data data - puts "............>>>#{data.length}" end # Called by EventMachine when the SSL/TLS handshake has From 40272bca9155628a7e8849ffe3e74e1f63e4a23a Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Sun, 1 Mar 2020 12:02:27 -0800 Subject: [PATCH 245/343] Add Ruby 2.7 to the README file --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 94dfa1b3d..3d102ba4b 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ EventMachine has been around since the early 2000s and is a mature and battle-te ## What platforms are supported by EventMachine? ## -EventMachine supports Ruby 2.0.0 through 2.6, JRuby and **works well on Windows** as well +EventMachine supports Ruby 2.0.0 through 2.7, JRuby and **works well on Windows** as well as many operating systems from the Unix family (Linux, Mac OS X, BSD flavors). From e242a4f1edeba3f09ebedb285830187bef798104 Mon Sep 17 00:00:00 2001 From: MSP-Greg Date: Sun, 1 Mar 2020 20:29:21 -0600 Subject: [PATCH 246/343] extconf.rb updates Some Windows specific, others are update & cleanup for OpenSSL checks --- ext/extconf.rb | 45 +++++++++++++++++++---------------- ext/fastfilereader/extconf.rb | 2 ++ 2 files changed, 27 insertions(+), 20 deletions(-) diff --git a/ext/extconf.rb b/ext/extconf.rb index 188f1abcc..cd9f516c1 100644 --- a/ext/extconf.rb +++ b/ext/extconf.rb @@ -25,23 +25,9 @@ def append_library(libs, lib) libs + " " + format(LIBARG, lib) end -SSL_HEADS = %w(openssl/ssl.h openssl/err.h) -SSL_LIBS = %w(crypto ssl) -# OpenSSL 1.1.0 and above for Windows use the Unix library names -# OpenSSL 0.9.8 and 1.0.x for Windows use the *eay32 library names -SSL_LIBS_WIN = RUBY_PLATFORM =~ /mswin|mingw|bccwin/ ? %w(ssleay32 libeay32) : [] - def dir_config_wrapper(pretty_name, name, idefault=nil, ldefault=nil) inc, lib = dir_config(name, idefault, ldefault) if inc && lib - # TODO: Remove when 2.0.0 is the minimum supported version - # Ruby versions not incorporating the mkmf fix at - # https://bugs.ruby-lang.org/projects/ruby-trunk/repository/revisions/39717 - # do not properly search for lib directories, and must be corrected - unless lib && lib[-3, 3] == 'lib' - @libdir_basename = 'lib' - inc, lib = dir_config(name, idefault, ldefault) - end unless idefault && ldefault abort "-----\nCannot find #{pretty_name} include path #{inc}\n-----" unless inc && inc.split(File::PATH_SEPARATOR).any? { |dir| File.directory?(dir) } abort "-----\nCannot find #{pretty_name} library path #{lib}\n-----" unless lib && lib.split(File::PATH_SEPARATOR).any? { |dir| File.directory?(dir) } @@ -53,7 +39,7 @@ def dir_config_wrapper(pretty_name, name, idefault=nil, ldefault=nil) def dir_config_search(pretty_name, name, paths, &b) paths.each do |p| - if dir_config_wrapper('OpenSSL', 'ssl', p + '/include', p + '/lib') && yield + if dir_config_wrapper(pretty_name, name, p + '/include', p + '/lib') && yield warn "-----\nFound #{pretty_name} in path #{p}\n-----" return true end @@ -69,6 +55,20 @@ def pkg_config_wrapper(pretty_name, name) end end +def find_openssl_library + if $mswin || $mingw + # required for static OpenSSL libraries + have_library("gdi32") # OpenSSL <= 1.0.2 (for RAND_screen()) + have_library("crypt32") + end + + return false unless have_header("openssl/ssl.h") && have_header("openssl/err.h") + + ret = have_library("crypto", "CRYPTO_malloc") && + have_library("ssl", "SSL_new") + return ret if ret +end + if ENV['CROSS_COMPILING'] openssl_version = ENV.fetch("OPENSSL_VERSION", "1.0.2e") openssl_dir = File.expand_path("~/.rake-compiler/builds/openssl-#{openssl_version}/") @@ -86,17 +86,20 @@ def pkg_config_wrapper(pretty_name, name) STDERR.puts "**************************************************************************************" STDERR.puts end +elsif dir_config_wrapper('OpenSSL', 'openssl') + # If the user has provided a --with-openssl-dir argument, we must respect it or fail. + add_define 'WITH_SSL' if find_openssl_library elsif dir_config_wrapper('OpenSSL', 'ssl') # If the user has provided a --with-ssl-dir argument, we must respect it or fail. - add_define 'WITH_SSL' if (check_libs(SSL_LIBS) || check_libs(SSL_LIBS_WIN)) && check_heads(SSL_HEADS) + add_define 'WITH_SSL' if find_openssl_librar elsif pkg_config_wrapper('OpenSSL', 'openssl') # If we can detect OpenSSL by pkg-config, use it as the next-best option - add_define 'WITH_SSL' if (check_libs(SSL_LIBS) || check_libs(SSL_LIBS_WIN)) && check_heads(SSL_HEADS) -elsif (check_libs(SSL_LIBS) || check_libs(SSL_LIBS_WIN)) && check_heads(SSL_HEADS) + add_define 'WITH_SSL' if find_openssl_library +elsif find_openssl_library # If we don't even need any options to find a usable OpenSSL, go with it add_define 'WITH_SSL' -elsif dir_config_search('OpenSSL', 'ssl', ['/usr/local', '/opt/local', '/usr/local/opt/openssl']) do - (check_libs(SSL_LIBS) || check_libs(SSL_LIBS_WIN)) && check_heads(SSL_HEADS) +elsif dir_config_search('OpenSSL', 'openssl', ['/usr/local', '/opt/local', '/usr/local/opt/openssl']) do + find_openssl_library end # Finally, look for OpenSSL in alternate locations including MacPorts and HomeBrew add_define 'WITH_SSL' @@ -139,6 +142,8 @@ def pkg_config_wrapper(pretty_name, name) any? { |v| v.include?("FD_SETSIZE") } add_define "FD_SETSIZE=32767" unless found + # needed for new versions of headers-git & crt-git + append_ldflags "-l:libssp.a -fstack-protector" end # Main platform invariances: diff --git a/ext/fastfilereader/extconf.rb b/ext/fastfilereader/extconf.rb index d2a5241e8..f887913b5 100644 --- a/ext/fastfilereader/extconf.rb +++ b/ext/fastfilereader/extconf.rb @@ -36,6 +36,8 @@ def add_define(name) any? { |v| v.include?("FD_SETSIZE") } add_define "FD_SETSIZE=32767" unless found + # needed for new versions of headers-git & crt-git + append_ldflags "-l:libssp.a -fstack-protector" end # Main platform invariances: From aaef104ce44069fc40bc26e863837aa1e010d1ed Mon Sep 17 00:00:00 2001 From: MSP-Greg Date: Sun, 1 Mar 2020 20:30:13 -0600 Subject: [PATCH 247/343] package.rake - remove devkit on GitHub Actions --- rakelib/package.rake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rakelib/package.rake b/rakelib/package.rake index 1444d6804..d1c5d8b79 100644 --- a/rakelib/package.rake +++ b/rakelib/package.rake @@ -106,7 +106,7 @@ task :devkit do end end -if RUBY_PLATFORM =~ /mingw|mswin/ +if RUBY_PLATFORM =~ /mingw|mswin/ && ENV['GITHUB_ACTIONS'].nil? Rake::Task['compile'].prerequisites.unshift 'devkit' end From ea2f0777a67f94ac1f05bd6cf3f5eecaaabb5af8 Mon Sep 17 00:00:00 2001 From: MSP-Greg Date: Sun, 1 Mar 2020 20:32:00 -0600 Subject: [PATCH 248/343] eventmachine.gemspec - s.metadata["msys2_mingw_dependencies"] The metadata tag updates the MinGW OpenSSL package, but the current package (1.1.1) is incompatible with Ruby 2.4, which uses 1.0.2 --- eventmachine.gemspec | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/eventmachine.gemspec b/eventmachine.gemspec index 52e53257e..278351c20 100644 --- a/eventmachine.gemspec +++ b/eventmachine.gemspec @@ -17,11 +17,13 @@ Gem::Specification.new do |s| if s.respond_to?(:metadata=) s.metadata ||= {} - s.metadata["msys2_mingw_dependencies"] = "openssl" + if RbConfig::CONFIG['ruby_version'] >= '2.5' + s.metadata["msys2_mingw_dependencies"] = "openssl" + end end s.add_development_dependency 'test-unit', '~> 3.2' - s.add_development_dependency 'rake-compiler', '~> 1.0' + s.add_development_dependency 'rake-compiler', '~> 1.1' s.add_development_dependency 'rake-compiler-dock', '~> 0.6.3' s.summary = 'Ruby/EventMachine library' From becd3c62f624a370ea65a89ee0fb6fa409d9398d Mon Sep 17 00:00:00 2001 From: MSP-Greg Date: Sun, 1 Mar 2020 20:37:18 -0600 Subject: [PATCH 249/343] test adjustments - darwin, some windows --- tests/em_test_helper.rb | 4 ++++ tests/test_basic.rb | 4 ++-- tests/test_idle_connection.rb | 2 +- tests/test_inactivity_timeout.rb | 2 +- tests/test_ipv4.rb | 2 +- tests/test_pause.rb | 6 ++++-- tests/test_pure.rb | 2 +- tests/test_resolver.rb | 3 ++- tests/test_send_file.rb | 2 +- tests/test_ssl_protocols.rb | 5 +++-- 10 files changed, 20 insertions(+), 12 deletions(-) diff --git a/tests/em_test_helper.rb b/tests/em_test_helper.rb index eab3038b3..7366c2999 100644 --- a/tests/em_test_helper.rb +++ b/tests/em_test_helper.rb @@ -129,6 +129,10 @@ def windows? RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ end + def darwin? + RUBY_PLATFORM =~ /darwin/ + end + def solaris? RUBY_PLATFORM =~ /solaris/ end diff --git a/tests/test_basic.rb b/tests/test_basic.rb index 529436837..d7cb7b5d2 100644 --- a/tests/test_basic.rb +++ b/tests/test_basic.rb @@ -38,7 +38,7 @@ def test_em def test_timer assert_nothing_raised do EM.run { - setup_timeout 0.4 + setup_timeout(darwin? ? 0.6 : 0.4) n = 0 EM.add_periodic_timer(0.1) { n += 1 @@ -163,7 +163,7 @@ def test_bind_connect end EM.run do - setup_timeout + setup_timeout(darwin? ? 0.3 : nil) EM.start_server "127.0.0.1", @port, bound_server EM.bind_connect local_ip, bind_port, "127.0.0.1", @port end diff --git a/tests/test_idle_connection.rb b/tests/test_idle_connection.rb index aaeac7c32..168fbb540 100644 --- a/tests/test_idle_connection.rb +++ b/tests/test_idle_connection.rb @@ -25,7 +25,7 @@ def test_idle_time end end - assert_in_delta 0.3, a, 0.1 + assert_in_delta 0.3, a, (darwin? ? 0.2 : 0.1) assert_in_delta 0, b, 0.1 end end diff --git a/tests/test_inactivity_timeout.rb b/tests/test_inactivity_timeout.rb index eccd6a788..f2765af32 100644 --- a/tests/test_inactivity_timeout.rb +++ b/tests/test_inactivity_timeout.rb @@ -87,7 +87,7 @@ def test_for_real } # .30 is double the timeout and not acceptable - assert_in_delta(0.15, (finish - start), 0.14) + assert_in_delta 0.15, (finish - start), (darwin? ? 0.20 : 0.14) # make sure it was a timeout and not a TLS error assert_equal Errno::ETIMEDOUT, reason end diff --git a/tests/test_ipv4.rb b/tests/test_ipv4.rb index 9ffc8c348..1e5d76fdf 100644 --- a/tests/test_ipv4.rb +++ b/tests/test_ipv4.rb @@ -33,7 +33,7 @@ def test_ipv4_udp_local_server @@received_data = nil @local_port = next_port - setup_timeout(2) + setup_timeout(darwin? ? 4 : 2) EM.run do EM::open_datagram_socket(@@public_ipv4, @local_port) do |s| diff --git a/tests/test_pause.rb b/tests/test_pause.rb index 1c0743ec3..628034d01 100644 --- a/tests/test_pause.rb +++ b/tests/test_pause.rb @@ -48,7 +48,9 @@ def post_init EM.start_server "127.0.0.1", @port, test_server EM.connect "127.0.0.1", @port, test_client - EM.add_timer(0.05) do + tmr = darwin? ? 0.10 : 0.05 + + EM.add_timer(tmr) do assert_equal 1, s_rx assert_equal 0, c_rx assert server.paused? @@ -58,7 +60,7 @@ def post_init assert !server.paused? - EM.add_timer(0.05) do + EM.add_timer(tmr) do assert server.paused? assert s_rx > 1 assert c_rx > 0 diff --git a/tests/test_pure.rb b/tests/test_pure.rb index 00dceb5e2..1782fe7d4 100644 --- a/tests/test_pure.rb +++ b/tests/test_pure.rb @@ -150,7 +150,7 @@ def test_periodic_timer EM.stop if x == 4 end } - assert_in_delta 0.8, (finish - start), 0.2 + assert_in_delta 0.8, (finish - start), (darwin? ? 0.6 : 0.2) assert_equal 4, x end end diff --git a/tests/test_resolver.rb b/tests/test_resolver.rb index daeccf25a..7cdfa3ba9 100644 --- a/tests/test_resolver.rb +++ b/tests/test_resolver.rb @@ -2,7 +2,8 @@ class TestResolver < Test::Unit::TestCase - CI_WINDOWS = windows? and ENV['CI'].casecmp('true').zero? + # always true unless set + CI_WINDOWS = windows? && ENV.fetch('CI', 'true').casecmp('true').zero? def ci_windows_retries(err) if CI_WINDOWS and err.is_a? String and err[/retries exceeded/] diff --git a/tests/test_send_file.rb b/tests/test_send_file.rb index 2ef1f7fde..573cdd6ae 100644 --- a/tests/test_send_file.rb +++ b/tests/test_send_file.rb @@ -179,7 +179,7 @@ def test_stream_large_file_data EM.run { EM.start_server "127.0.0.1", @port, StreamTestModule, @filename - setup_timeout + setup_timeout(darwin? ? 0.5 : 0.25) EM.connect "127.0.0.1", @port, TestClient do |c| c.data_to { |d| data << d } end diff --git a/tests/test_ssl_protocols.rb b/tests/test_ssl_protocols.rb index a8cd5886c..86499dfa4 100644 --- a/tests/test_ssl_protocols.rb +++ b/tests/test_ssl_protocols.rb @@ -12,7 +12,7 @@ class TestSSLProtocols < Test::Unit::TestCase require_relative 'em_ssl_handlers' include EMSSLHandlers - RUBY_SSL_GE_2_1 = OpenSSL::VERSION >= '2.1' + RUBY_SSL_GE_2_1 = OpenSSL::SSL::SSLContext.instance_methods(false).include?(:min_version=) def test_invalid_ssl_version assert_raises(RuntimeError, "Unrecognized SSL/TLS Version: badinput") do @@ -144,7 +144,8 @@ def test_tlsv1_2_with_external_client end def test_tlsv1_3_with_external_client - omit("TLSv1_3 is unavailable") unless EM.const_defined? :EM_PROTO_TLSv1_3 + omit("TLSv1_3 is unavailable") unless EM.const_defined?(:EM_PROTO_TLSv1_3) && + OpenSSL::SSL.const_defined?(:TLS1_3_VERSION) external_client nil, nil, :SSLv23_client, TLS_1_3 end From 5165e5ba7940601f308db31218abfe576838dc36 Mon Sep 17 00:00:00 2001 From: MSP-Greg Date: Thu, 5 Mar 2020 18:33:23 -0600 Subject: [PATCH 250/343] 3 fixes needed for older Windows Rubies Windows 2.2 & 2.3 Rubies --- ext/extconf.rb | 4 +++- ext/fastfilereader/extconf.rb | 4 +++- tests/test_basic.rb | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/ext/extconf.rb b/ext/extconf.rb index cd9f516c1..635f2bfb6 100644 --- a/ext/extconf.rb +++ b/ext/extconf.rb @@ -143,7 +143,9 @@ def find_openssl_library add_define "FD_SETSIZE=32767" unless found # needed for new versions of headers-git & crt-git - append_ldflags "-l:libssp.a -fstack-protector" + if RbConfig::CONFIG["ruby_version"] >= "2.4" + append_ldflags "-l:libssp.a -fstack-protector" + end end # Main platform invariances: diff --git a/ext/fastfilereader/extconf.rb b/ext/fastfilereader/extconf.rb index f887913b5..c5d74fe36 100644 --- a/ext/fastfilereader/extconf.rb +++ b/ext/fastfilereader/extconf.rb @@ -37,7 +37,9 @@ def add_define(name) add_define "FD_SETSIZE=32767" unless found # needed for new versions of headers-git & crt-git - append_ldflags "-l:libssp.a -fstack-protector" + if RbConfig::CONFIG["ruby_version"] >= "2.4" + append_ldflags "-l:libssp.a -fstack-protector" + end end # Main platform invariances: diff --git a/tests/test_basic.rb b/tests/test_basic.rb index d7cb7b5d2..04def7964 100644 --- a/tests/test_basic.rb +++ b/tests/test_basic.rb @@ -163,7 +163,7 @@ def test_bind_connect end EM.run do - setup_timeout(darwin? ? 0.3 : nil) + darwin? ? setup_timeout(0.3) : setup_timeout EM.start_server "127.0.0.1", @port, bound_server EM.bind_connect local_ip, bind_port, "127.0.0.1", @port end From 9e1501b4d508916a55bd18bf50785deb54e3d250 Mon Sep 17 00:00:00 2001 From: MSP-Greg Date: Thu, 5 Mar 2020 18:33:50 -0600 Subject: [PATCH 251/343] Add GitHub Actions workflow.yml --- .github/workflows/workflow.yml | 78 ++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 .github/workflows/workflow.yml diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml new file mode 100644 index 000000000..f0785f4c8 --- /dev/null +++ b/.github/workflows/workflow.yml @@ -0,0 +1,78 @@ +name: CI + +on: [push, pull_request] + +jobs: + build: + name: >- + ${{ matrix.os }} ${{ matrix.ruby }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ ubuntu-18.04, macos ] + ruby: [ 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, head ] + steps: + - name: repo checkout + uses: actions/checkout@v2 + + - name: load ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + + - name: macOS disable firewall + if: startsWith(matrix.os, 'macos') + run: | + sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate off + sudo /usr/libexec/ApplicationFirewall/socketfilterfw --getglobalstate + + - name: bundle install + run: bundle install --jobs 4 --retry 3 --without=documentation + + - name: compile + run: bundle exec rake compile + + - name: test + run: bundle exec rake test + env: + CI: true + TESTOPTS: -v --no-show-detail-immediately + + win32: + name: >- + ${{ matrix.os }} ${{ matrix.ruby }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ windows-latest ] + ruby: [ 2.4, 2.5, 2.6, 2.7, mingw ] + steps: + - name: repo checkout + uses: actions/checkout@v2 + + - name: load ruby, update MSYS2, openssl + uses: MSP-Greg/actions-ruby@v1 + with: + base: update + mingw: openssl + ruby-version: ${{ matrix.ruby }} + + - name: bundle install + run: bundle install --jobs 4 --retry 3 --without=documentation + + - name: compile + if: matrix.ruby >= '2.4' + run: rake compile + + # Ruby 2.3 uses a OpenSSL package that is not MSYS2 + - name: compile 2.3 + if: matrix.ruby == '2.3' + run: rake compile -- --with-openssl-dir=C:/openssl-win + + - name: test + run: rake test + env: + CI: true + TESTOPTS: -v --no-show-detail-immediately From 55d82da3ae93f0856aca233318ac39ae0b29a6ae Mon Sep 17 00:00:00 2001 From: MSP-Greg Date: Mon, 9 Mar 2020 21:41:00 -0500 Subject: [PATCH 252/343] JRuby test fix, extconf.rb typo --- ext/extconf.rb | 2 +- tests/test_ssl_protocols.rb | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/ext/extconf.rb b/ext/extconf.rb index 635f2bfb6..5ec18fd30 100644 --- a/ext/extconf.rb +++ b/ext/extconf.rb @@ -91,7 +91,7 @@ def find_openssl_library add_define 'WITH_SSL' if find_openssl_library elsif dir_config_wrapper('OpenSSL', 'ssl') # If the user has provided a --with-ssl-dir argument, we must respect it or fail. - add_define 'WITH_SSL' if find_openssl_librar + add_define 'WITH_SSL' if find_openssl_library elsif pkg_config_wrapper('OpenSSL', 'openssl') # If we can detect OpenSSL by pkg-config, use it as the next-best option add_define 'WITH_SSL' if find_openssl_library diff --git a/tests/test_ssl_protocols.rb b/tests/test_ssl_protocols.rb index 86499dfa4..01c22ed1a 100644 --- a/tests/test_ssl_protocols.rb +++ b/tests/test_ssl_protocols.rb @@ -12,7 +12,9 @@ class TestSSLProtocols < Test::Unit::TestCase require_relative 'em_ssl_handlers' include EMSSLHandlers - RUBY_SSL_GE_2_1 = OpenSSL::SSL::SSLContext.instance_methods(false).include?(:min_version=) + # We're checking for whether Context min_version= & max_version= are defined, but + # JRuby has a bug where they're defined, but the private method they call isn't + RUBY_SSL_GE_2_1 = OpenSSL::SSL::SSLContext.private_instance_methods(false).include?(:set_minmax_proto_version) def test_invalid_ssl_version assert_raises(RuntimeError, "Unrecognized SSL/TLS Version: badinput") do From fb1ed5ba16cbc636813731716d39575cdf0c01e8 Mon Sep 17 00:00:00 2001 From: MSP-Greg Date: Tue, 10 Mar 2020 08:44:59 -0500 Subject: [PATCH 253/343] test_inactivity_timeout.rb - darwin delta fix --- tests/test_inactivity_timeout.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_inactivity_timeout.rb b/tests/test_inactivity_timeout.rb index f2765af32..e42ef579b 100644 --- a/tests/test_inactivity_timeout.rb +++ b/tests/test_inactivity_timeout.rb @@ -46,7 +46,7 @@ def test_for_real } } # Travis can vary from 0.02 to 0.17, Appveyor maybe as low as 0.01 - assert_in_delta(0.09, (finish - start), 0.08) + assert_in_delta 0.09, (finish - start), (darwin? ? 0.10 : 0.08) # simplified reproducer for comm_inactivity_timeout taking twice as long # as requested -- https://github.com/eventmachine/eventmachine/issues/554 From d922aae253ff85674926ca95d4f5334923f5578f Mon Sep 17 00:00:00 2001 From: MSP-Greg Date: Mon, 16 Mar 2020 15:26:57 -0500 Subject: [PATCH 254/343] Add Windows Ruby 2.2 and 2.3 to Actions CI, reorg workflow --- .github/workflows/workflow.yml | 51 ++++++---------------------------- 1 file changed, 9 insertions(+), 42 deletions(-) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index f0785f4c8..615dbd732 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -6,20 +6,25 @@ jobs: build: name: >- ${{ matrix.os }} ${{ matrix.ruby }} - runs-on: ${{ matrix.os }} + runs-on: ${{ matrix.os }}-latest strategy: fail-fast: false matrix: - os: [ ubuntu-18.04, macos ] + os: [ ubuntu, macos, windows ] ruby: [ 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, head ] + include: + - { os: windows, ruby: mingw } + exclude: + - { os: windows, ruby: head } steps: - name: repo checkout uses: actions/checkout@v2 - - name: load ruby - uses: ruby/setup-ruby@v1 + - name: load ruby, openssl + uses: MSP-Greg/setup-ruby-pkgs@v1 with: ruby-version: ${{ matrix.ruby }} + mingw: _upgrade_ openssl - name: macOS disable firewall if: startsWith(matrix.os, 'macos') @@ -38,41 +43,3 @@ jobs: env: CI: true TESTOPTS: -v --no-show-detail-immediately - - win32: - name: >- - ${{ matrix.os }} ${{ matrix.ruby }} - runs-on: ${{ matrix.os }} - strategy: - fail-fast: false - matrix: - os: [ windows-latest ] - ruby: [ 2.4, 2.5, 2.6, 2.7, mingw ] - steps: - - name: repo checkout - uses: actions/checkout@v2 - - - name: load ruby, update MSYS2, openssl - uses: MSP-Greg/actions-ruby@v1 - with: - base: update - mingw: openssl - ruby-version: ${{ matrix.ruby }} - - - name: bundle install - run: bundle install --jobs 4 --retry 3 --without=documentation - - - name: compile - if: matrix.ruby >= '2.4' - run: rake compile - - # Ruby 2.3 uses a OpenSSL package that is not MSYS2 - - name: compile 2.3 - if: matrix.ruby == '2.3' - run: rake compile -- --with-openssl-dir=C:/openssl-win - - - name: test - run: rake test - env: - CI: true - TESTOPTS: -v --no-show-detail-immediately From e29ccdefbf4b753dfec513c2df17b3bf1b827425 Mon Sep 17 00:00:00 2001 From: wordjelly Date: Sun, 6 Sep 2020 10:02:31 +0530 Subject: [PATCH 255/343] fix: em/pure_ruby intra-iteration element delete bug (#929) --- lib/em/pure_ruby.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/em/pure_ruby.rb b/lib/em/pure_ruby.rb index f5a93675d..70b28fd6b 100644 --- a/lib/em/pure_ruby.rb +++ b/lib/em/pure_ruby.rb @@ -569,14 +569,18 @@ def run end def run_timers + timers_to_delete = [] @timers.each {|t| if t.first <= @current_loop_time - @timers.delete t + #@timers.delete t + timers_to_delete << t EventMachine::event_callback "", TimerFired, t.last else break end } + timers_to_delete.map{|c| @timers.delete c} + timers_to_delete = nil #while @timers.length > 0 and @timers.first.first <= now # t = @timers.shift # EventMachine::event_callback "", TimerFired, t.last From 15513b53d712fac003ddb6edf9b9433adf5520ec Mon Sep 17 00:00:00 2001 From: MSP-Greg Date: Fri, 18 Sep 2020 16:31:37 -0500 Subject: [PATCH 256/343] Update ssl.cpp --- ext/ssl.cpp | 68 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 39 insertions(+), 29 deletions(-) diff --git a/ext/ssl.cpp b/ext/ssl.cpp index 516cd7b52..9666ad195 100644 --- a/ext/ssl.cpp +++ b/ext/ssl.cpp @@ -33,42 +33,52 @@ static X509 *DefaultCertificate = NULL; static char PrivateMaterials[] = { "-----BEGIN RSA PRIVATE KEY-----\n" -"MIICXAIBAAKBgQDCYYhcw6cGRbhBVShKmbWm7UVsEoBnUf0cCh8AX+MKhMxwVDWV\n" -"Igdskntn3cSJjRtmgVJHIK0lpb/FYHQB93Ohpd9/Z18pDmovfFF9nDbFF0t39hJ/\n" -"AqSzFB3GiVPoFFZJEE1vJqh+3jzsSF5K56bZ6azz38VlZgXeSozNW5bXkQIDAQAB\n" -"AoGALA89gIFcr6BIBo8N5fL3aNHpZXjAICtGav+kTUpuxSiaym9cAeTHuAVv8Xgk\n" -"H2Wbq11uz+6JMLpkQJH/WZ7EV59DPOicXrp0Imr73F3EXBfR7t2EQDYHPMthOA1D\n" -"I9EtCzvV608Ze90hiJ7E3guGrGppZfJ+eUWCPgy8CZH1vRECQQDv67rwV/oU1aDo\n" -"6/+d5nqjeW6mWkGqTnUU96jXap8EIw6B+0cUKskwx6mHJv+tEMM2748ZY7b0yBlg\n" -"w4KDghbFAkEAz2h8PjSJG55LwqmXih1RONSgdN9hjB12LwXL1CaDh7/lkEhq0PlK\n" -"PCAUwQSdM17Sl0Xxm2CZiekTSlwmHrtqXQJAF3+8QJwtV2sRJp8u2zVe37IeH1cJ\n" -"xXeHyjTzqZ2803fnjN2iuZvzNr7noOA1/Kp+pFvUZUU5/0G2Ep8zolPUjQJAFA7k\n" -"xRdLkzIx3XeNQjwnmLlncyYPRv+qaE3FMpUu7zftuZBnVCJnvXzUxP3vPgKTlzGa\n" -"dg5XivDRfsV+okY5uQJBAMV4FesUuLQVEKb6lMs7rzZwpeGQhFDRfywJzfom2TLn\n" -"2RdJQQ3dcgnhdVDgt5o1qkmsqQh8uJrJ9SdyLIaZQIc=\n" +"MIIEpAIBAAKCAQEA25ZTHopLfMKYeVwZoWtDwBd1RqCJhIL7wgDy/05jzIYm8JFf\n" +"xwFI+pau/3mLVHYPrl9rXeM5ZBONeLwewDJAsIYVtyZ8O9pICK6uz4sogfwST8Uc\n" +"/sumdd/mq2vIw4zEGjcaXdmMZYrJOxlvx32Fsrc5M6iNIMQYFnLPU3KsJhvOAZiM\n" +"6jEn2wIszhq5X/5HupaOuCaZqCxOtaXaWguJF4SGFW6tBMxaY4ryiLyUwC0EGPoH\n" +"C2f34DxeXpRQ9TNS3H//L90wTok6slrqZQ/4zXU64+0pd5q35FqnS7gU7H6SQ9yj\n" +"TWPPe/gNTVPWoQd9SJJmB7Q1BYukcfDnh69NBwIDAQABAoIBABErG6yTm3tRq6Ix\n" +"dT+Np2ppax3uh1H4+74bXORhOKRRCNJeS2K/0vjktyH6Ws5rvKYhh797eI0+ih0a\n" +"eD0GsNAca646MBRt6JvlLH3Fn5EqKDRccPvq6ETnEJ3ue2/unZZ/IGyeCcAWrc0V\n" +"HAw44C8s7CgB0abyLf/zUgpwOM1x0etDpIiMBEUxpV+aG5wsAAcj3Q0rkxUhfIgS\n" +"QB1eWGgN/irfaUqPOwXZ85cWBVQlck1eCKeWJJkHffcqfqfcuy2d8W5PN0DeD3kS\n" +"QW/+hXnxSU39/DymsomsjKnrB4IwW6lVGxRXyVhMiz3bxmEmRK7YdkCzsDO8VWBa\n" +"bcctQgECgYEA/jIzxNwmmhwAgDNhnjgMi55zyEQdAdvNz/2VYbE2b5ImudWc53md\n" +"UFCVDS4aTYN0uIkGI/bLuAydw6I766tnmu8WXE2k4tAahBRRrsXGahY7Aom1FjfQ\n" +"kvMjgnGTunO+r1bGJ+KbXi6x7yt1gucp4qh4msrry5D1pUva2SA7CsECgYEA3SU/\n" +"nuCmMPVEsYnDIzxZC0qBSrLB1h1HB7vsFyecQV6tOYVJRSU8v+DARAc38Mveivja\n" +"mw5GgdEEYAx1roNhpjlMG2B9K7dShLMiFonn9c/euq/JpmcDI+yqIyFNQAgLVcB7\n" +"+3HVjEbI1qsCr87RH0QdiTsWTk4wa6EzIIJ7MccCgYBWgYEqqn0cjxEAj/vVm19x\n" +"mE/wxHVWr5XgBX1zzJoo6ATz0yVdhP6rWXEQFjNvU6BCOKd1T8TOcsSx0iEwN5m/\n" +"mUPzz5ygb4/GiR+vKbE3Yy9b0r9ku0Po7oOUHdDXcBJhm1c+NZkIOT3mldSc4sxX\n" +"TVwV2Z7bHQ7r3N+yaoyNQQKBgQCC2B8kadbq8LOMN+51Uqd8vsBw6gM2JGx6bv3p\n" +"VU5mfxYPCoWnm7it7tTTa1H17ynlIAh35aJh/MGR8s1OS/3i09Pr/tMQoo74ZOSu\n" +"YToVfsBRxOCSzDBXeRfRYUrLr/bE7fZtd5TaQqdiHByi2MNytGKlZ4hzHGAZzm7p\n" +"tUoe0QKBgQD3/KR7XFplvrceBMi2fqdO0UWP6vqOfKd315TlvntTcn3cdgab4mKk\n" +"UOh3S4lyrW0uU/LQh91+ulGbJejdjiCphj3PMpd8MXzcUVd4WQmPMVOP2R+86rGh\n" +"1EsiZHUMNRsCzwJtPDN48OmELxx9MikVgecj7OLvi4uelDSgVkFqdA==\n" "-----END RSA PRIVATE KEY-----\n" "-----BEGIN CERTIFICATE-----\n" -"MIID6TCCA1KgAwIBAgIJANm4W/Tzs+s+MA0GCSqGSIb3DQEBBQUAMIGqMQswCQYD\n" +"MIIDajCCAtOgAwIBAgIJANm4W/Tzs+s+MA0GCSqGSIb3DQEBCwUAMIGqMQswCQYD\n" "VQQGEwJVUzERMA8GA1UECBMITmV3IFlvcmsxETAPBgNVBAcTCE5ldyBZb3JrMRYw\n" "FAYDVQQKEw1TdGVhbWhlYXQubmV0MRQwEgYDVQQLEwtFbmdpbmVlcmluZzEdMBsG\n" "A1UEAxMUb3BlbmNhLnN0ZWFtaGVhdC5uZXQxKDAmBgkqhkiG9w0BCQEWGWVuZ2lu\n" -"ZWVyaW5nQHN0ZWFtaGVhdC5uZXQwHhcNMDYwNTA1MTcwNjAzWhcNMjQwMjIwMTcw\n" -"NjAzWjCBqjELMAkGA1UEBhMCVVMxETAPBgNVBAgTCE5ldyBZb3JrMREwDwYDVQQH\n" +"ZWVyaW5nQHN0ZWFtaGVhdC5uZXQwHhcNMjAwOTAxMDAwMDAwWhcNMzAwOTAxMDAw\n" +"MDAwWjCBqjELMAkGA1UEBhMCVVMxETAPBgNVBAgTCE5ldyBZb3JrMREwDwYDVQQH\n" "EwhOZXcgWW9yazEWMBQGA1UEChMNU3RlYW1oZWF0Lm5ldDEUMBIGA1UECxMLRW5n\n" "aW5lZXJpbmcxHTAbBgNVBAMTFG9wZW5jYS5zdGVhbWhlYXQubmV0MSgwJgYJKoZI\n" -"hvcNAQkBFhllbmdpbmVlcmluZ0BzdGVhbWhlYXQubmV0MIGfMA0GCSqGSIb3DQEB\n" -"AQUAA4GNADCBiQKBgQDCYYhcw6cGRbhBVShKmbWm7UVsEoBnUf0cCh8AX+MKhMxw\n" -"VDWVIgdskntn3cSJjRtmgVJHIK0lpb/FYHQB93Ohpd9/Z18pDmovfFF9nDbFF0t3\n" -"9hJ/AqSzFB3GiVPoFFZJEE1vJqh+3jzsSF5K56bZ6azz38VlZgXeSozNW5bXkQID\n" -"AQABo4IBEzCCAQ8wHQYDVR0OBBYEFPJvPd1Fcmd8o/Tm88r+NjYPICCkMIHfBgNV\n" -"HSMEgdcwgdSAFPJvPd1Fcmd8o/Tm88r+NjYPICCkoYGwpIGtMIGqMQswCQYDVQQG\n" -"EwJVUzERMA8GA1UECBMITmV3IFlvcmsxETAPBgNVBAcTCE5ldyBZb3JrMRYwFAYD\n" -"VQQKEw1TdGVhbWhlYXQubmV0MRQwEgYDVQQLEwtFbmdpbmVlcmluZzEdMBsGA1UE\n" -"AxMUb3BlbmNhLnN0ZWFtaGVhdC5uZXQxKDAmBgkqhkiG9w0BCQEWGWVuZ2luZWVy\n" -"aW5nQHN0ZWFtaGVhdC5uZXSCCQDZuFv087PrPjAMBgNVHRMEBTADAQH/MA0GCSqG\n" -"SIb3DQEBBQUAA4GBAC1CXey/4UoLgJiwcEMDxOvW74plks23090iziFIlGgcIhk0\n" -"Df6hTAs7H3MWww62ddvR8l07AWfSzSP5L6mDsbvq7EmQsmPODwb6C+i2aF3EDL8j\n" -"uw73m4YIGI0Zw2XdBpiOGkx2H56Kya6mJJe/5XORZedh1wpI7zki01tHYbcy\n" +"hvcNAQkBFhllbmdpbmVlcmluZ0BzdGVhbWhlYXQubmV0MIIBIjANBgkqhkiG9w0B\n" +"AQEFAAOCAQ8AMIIBCgKCAQEA25ZTHopLfMKYeVwZoWtDwBd1RqCJhIL7wgDy/05j\n" +"zIYm8JFfxwFI+pau/3mLVHYPrl9rXeM5ZBONeLwewDJAsIYVtyZ8O9pICK6uz4so\n" +"gfwST8Uc/sumdd/mq2vIw4zEGjcaXdmMZYrJOxlvx32Fsrc5M6iNIMQYFnLPU3Ks\n" +"JhvOAZiM6jEn2wIszhq5X/5HupaOuCaZqCxOtaXaWguJF4SGFW6tBMxaY4ryiLyU\n" +"wC0EGPoHC2f34DxeXpRQ9TNS3H//L90wTok6slrqZQ/4zXU64+0pd5q35FqnS7gU\n" +"7H6SQ9yjTWPPe/gNTVPWoQd9SJJmB7Q1BYukcfDnh69NBwIDAQABoxIwEDAOBgNV\n" +"HQ8BAf8EBAMCBLAwDQYJKoZIhvcNAQELBQADgYEAc4mWHK3HHAwWXIsJztUOCEaT\n" +"yDpzqt5nnDqg5Q3/1HhiM4wsWoam9ixTcZk25+5xcMsvuSoDvzAJzyd5wpBkOq/z\n" +"UeWxZmLYOzOghrT62TLJVxAqh0AdEP8jMWOAeWrrOXnXx8AvG1+R8n4Rf5/koSa8\n" +"wJrrW4j7WAEsY5kG4hU=\n" "-----END CERTIFICATE-----\n"}; /* These private materials were made with: From 682da83d5c38bc2694b2fc5a1f8f011c0e9a1100 Mon Sep 17 00:00:00 2001 From: MSP-Greg Date: Fri, 18 Sep 2020 18:49:14 -0500 Subject: [PATCH 257/343] Update extconf.rb --- ext/extconf.rb | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/ext/extconf.rb b/ext/extconf.rb index 5ec18fd30..0fb654104 100644 --- a/ext/extconf.rb +++ b/ext/extconf.rb @@ -64,8 +64,8 @@ def find_openssl_library return false unless have_header("openssl/ssl.h") && have_header("openssl/err.h") - ret = have_library("crypto", "CRYPTO_malloc") && - have_library("ssl", "SSL_new") + ret = %w'crypto libeay32'.find {|crypto| have_library(crypto, 'BIO_read')} and + %w'ssl ssleay32'.find {|ssl| have_library(ssl, 'SSL_CTX_new')} return ret if ret end @@ -86,6 +86,9 @@ def find_openssl_library STDERR.puts "**************************************************************************************" STDERR.puts end +elsif $mingw && RUBY_VERSION < '2.4' && find_openssl_library + # Workaround for old MSYS OpenSSL builds + add_define 'WITH_SSL' elsif dir_config_wrapper('OpenSSL', 'openssl') # If the user has provided a --with-openssl-dir argument, we must respect it or fail. add_define 'WITH_SSL' if find_openssl_library @@ -124,7 +127,7 @@ def find_openssl_library # Minor platform details between *nix and Windows: if RUBY_PLATFORM =~ /(mswin|mingw|bccwin)/ - GNU_CHAIN = ENV['CROSS_COMPILING'] || $1 == 'mingw' + GNU_CHAIN = ENV['CROSS_COMPILING'] || $mingw OS_WIN32 = true add_define "OS_WIN32" else From 9fc27ce585dd42d676d06573005fc7dd69d9811f Mon Sep 17 00:00:00 2001 From: MSP-Greg Date: Tue, 1 Sep 2020 10:34:14 -0500 Subject: [PATCH 258/343] Actions - use Ubuntu 20.04 --- .github/workflows/workflow.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 615dbd732..1b0264516 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -6,16 +6,16 @@ jobs: build: name: >- ${{ matrix.os }} ${{ matrix.ruby }} - runs-on: ${{ matrix.os }}-latest + runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: - os: [ ubuntu, macos, windows ] + os: [ ubuntu-20.04, macos-10.15, windows-2019 ] ruby: [ 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, head ] include: - - { os: windows, ruby: mingw } + - { os: windows-2019, ruby: mingw } exclude: - - { os: windows, ruby: head } + - { os: windows-2019, ruby: head } steps: - name: repo checkout uses: actions/checkout@v2 From 9e4d56b5dc2244338e6ae2a9d7084c6ae5a77759 Mon Sep 17 00:00:00 2001 From: Gabriel Sim Date: Wed, 30 Sep 2020 02:20:17 +0800 Subject: [PATCH 259/343] Update GettingStarted.md (#931) --- docs/GettingStarted.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/GettingStarted.md b/docs/GettingStarted.md index 0b27975e4..a99ed2466 100644 --- a/docs/GettingStarted.md +++ b/docs/GettingStarted.md @@ -143,7 +143,7 @@ main thread and it finishes execution, and our little program exits as the resul To summarize this first example: - * Subclass {EventMachine::Connection} and override {EventMachine::Connection#send_data} to handle incoming data. + * Subclass {EventMachine::Connection} and override {EventMachine::Connection#receive_data} to handle incoming data. * Use {EventMachine.run} to start EventMachine event loop and then bind echo server with {EventMachine.start_server}. * To stop the event loop, use {EventMachine.stop_event_loop} (aliased as {EventMachine.stop}) From 485665872ca566ec8a8131386a8c10fa33c8d76b Mon Sep 17 00:00:00 2001 From: Eddie A Tejeda Date: Mon, 5 Oct 2020 18:03:45 -0400 Subject: [PATCH 260/343] celluloid.io does not exist. Link to github repo instead (#933) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3d102ba4b..4c67d106a 100644 --- a/README.md +++ b/README.md @@ -107,4 +107,4 @@ Copyright: (C) 2006-07 by Francis Cianfrocca. All Rights Reserved. ## Alternatives ## -If you are unhappy with EventMachine and want to use Ruby, check out [Celluloid](https://celluloid.io/). +If you are unhappy with EventMachine and want to use Ruby, check out [Celluloid](https://github.com/celluloid/celluloid). From b50c135dfdd4e7b20c8e0b7ce1d8fe7176d4d14d Mon Sep 17 00:00:00 2001 From: Nikita Fedyashev Date: Tue, 20 Oct 2020 23:24:12 +0300 Subject: [PATCH 261/343] Fix outdated link to Thin in README.md (#935) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4c67d106a..b954f4616 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ EventMachine has been around since the early 2000s and is a mature and battle-te ## What EventMachine is good for? ## - * Scalable event-driven servers. Examples: [Thin](http://code.macournoyer.com/thin/) or [Goliath](https://github.com/postrank-labs/goliath/). + * Scalable event-driven servers. Examples: [Thin](https://github.com/macournoyer/thin/) or [Goliath](https://github.com/postrank-labs/goliath/). * Scalable asynchronous clients for various protocols, RESTful APIs and so on. Examples: [em-http-request](https://github.com/igrigorik/em-http-request) or [amqp gem](https://github.com/ruby-amqp/amqp). * Efficient network proxies with custom logic. Examples: [Proxymachine](https://github.com/mojombo/proxymachine/). * File and network monitoring tools. Examples: [eventmachine-tail](https://github.com/jordansissel/eventmachine-tail) and [logstash](https://github.com/logstash/logstash). From 77b6ca307c9174d014caf15d182345e81ecfb4dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ondruch?= Date: Tue, 4 Aug 2020 13:14:18 +0200 Subject: [PATCH 262/343] Extend certificate length. The 1024 bits certificates are not considered secure anymore and are rejected on various places already. Use 4096 bits certificates instead. --- ext/ssl.cpp | 125 +++++++++++++++++++++++++++++++++------------------- 1 file changed, 80 insertions(+), 45 deletions(-) diff --git a/ext/ssl.cpp b/ext/ssl.cpp index 9666ad195..d6fed235c 100644 --- a/ext/ssl.cpp +++ b/ext/ssl.cpp @@ -33,56 +33,91 @@ static X509 *DefaultCertificate = NULL; static char PrivateMaterials[] = { "-----BEGIN RSA PRIVATE KEY-----\n" -"MIIEpAIBAAKCAQEA25ZTHopLfMKYeVwZoWtDwBd1RqCJhIL7wgDy/05jzIYm8JFf\n" -"xwFI+pau/3mLVHYPrl9rXeM5ZBONeLwewDJAsIYVtyZ8O9pICK6uz4sogfwST8Uc\n" -"/sumdd/mq2vIw4zEGjcaXdmMZYrJOxlvx32Fsrc5M6iNIMQYFnLPU3KsJhvOAZiM\n" -"6jEn2wIszhq5X/5HupaOuCaZqCxOtaXaWguJF4SGFW6tBMxaY4ryiLyUwC0EGPoH\n" -"C2f34DxeXpRQ9TNS3H//L90wTok6slrqZQ/4zXU64+0pd5q35FqnS7gU7H6SQ9yj\n" -"TWPPe/gNTVPWoQd9SJJmB7Q1BYukcfDnh69NBwIDAQABAoIBABErG6yTm3tRq6Ix\n" -"dT+Np2ppax3uh1H4+74bXORhOKRRCNJeS2K/0vjktyH6Ws5rvKYhh797eI0+ih0a\n" -"eD0GsNAca646MBRt6JvlLH3Fn5EqKDRccPvq6ETnEJ3ue2/unZZ/IGyeCcAWrc0V\n" -"HAw44C8s7CgB0abyLf/zUgpwOM1x0etDpIiMBEUxpV+aG5wsAAcj3Q0rkxUhfIgS\n" -"QB1eWGgN/irfaUqPOwXZ85cWBVQlck1eCKeWJJkHffcqfqfcuy2d8W5PN0DeD3kS\n" -"QW/+hXnxSU39/DymsomsjKnrB4IwW6lVGxRXyVhMiz3bxmEmRK7YdkCzsDO8VWBa\n" -"bcctQgECgYEA/jIzxNwmmhwAgDNhnjgMi55zyEQdAdvNz/2VYbE2b5ImudWc53md\n" -"UFCVDS4aTYN0uIkGI/bLuAydw6I766tnmu8WXE2k4tAahBRRrsXGahY7Aom1FjfQ\n" -"kvMjgnGTunO+r1bGJ+KbXi6x7yt1gucp4qh4msrry5D1pUva2SA7CsECgYEA3SU/\n" -"nuCmMPVEsYnDIzxZC0qBSrLB1h1HB7vsFyecQV6tOYVJRSU8v+DARAc38Mveivja\n" -"mw5GgdEEYAx1roNhpjlMG2B9K7dShLMiFonn9c/euq/JpmcDI+yqIyFNQAgLVcB7\n" -"+3HVjEbI1qsCr87RH0QdiTsWTk4wa6EzIIJ7MccCgYBWgYEqqn0cjxEAj/vVm19x\n" -"mE/wxHVWr5XgBX1zzJoo6ATz0yVdhP6rWXEQFjNvU6BCOKd1T8TOcsSx0iEwN5m/\n" -"mUPzz5ygb4/GiR+vKbE3Yy9b0r9ku0Po7oOUHdDXcBJhm1c+NZkIOT3mldSc4sxX\n" -"TVwV2Z7bHQ7r3N+yaoyNQQKBgQCC2B8kadbq8LOMN+51Uqd8vsBw6gM2JGx6bv3p\n" -"VU5mfxYPCoWnm7it7tTTa1H17ynlIAh35aJh/MGR8s1OS/3i09Pr/tMQoo74ZOSu\n" -"YToVfsBRxOCSzDBXeRfRYUrLr/bE7fZtd5TaQqdiHByi2MNytGKlZ4hzHGAZzm7p\n" -"tUoe0QKBgQD3/KR7XFplvrceBMi2fqdO0UWP6vqOfKd315TlvntTcn3cdgab4mKk\n" -"UOh3S4lyrW0uU/LQh91+ulGbJejdjiCphj3PMpd8MXzcUVd4WQmPMVOP2R+86rGh\n" -"1EsiZHUMNRsCzwJtPDN48OmELxx9MikVgecj7OLvi4uelDSgVkFqdA==\n" +"MIIJRAIBADANBgkqhkiG9w0BAQEFAASCCS4wggkqAgEAAoICAQDQmOnboooYGgbU\n" +"rNr+O7xQRrEn/pFdVfWTmW/vCbWWL7BYerxs9Uh7E1PmQpIovdw1DClFx4vMkdvb\n" +"RHzkILY4+mgN05FLdwncxF8X+za4p7nhvJWu2TIinRmkmHRIiXncMU4yqreKnjAX\n" +"9kacWxPMglvnge7CUsywWmaN7qyFT+ywcuN/EGoCiFU7Dzen/HqTgPGeq4gOJ9wl\n" +"ImFuaAiA7696K7UwBI/QEN76QmYOG/iXdZNnp1DDY9h2pA2fmJTmKUzzkk8XXz+Z\n" +"Q4/NHOdzLvl7znRrlI2Y6m4LEr1cCdn7mWNESo5dkif8LVX1j/RDOP6Dtv+oYscG\n" +"TPSSR+Wlcw/0K4tAOILtDs1IVAGhcfZbTXM3EQS66Zx84yrlqkno6JKaGEKvtF9h\n" +"qEYT7lxHP/kIsyxZvXAhJQ8A1ajTDcqetyzphQiaKqeWTEmobibD8JtqshggTVv5\n" +"DtvdU62AfrDfOXub51/+WtdhjCe30aIrLpAaXOTktqYW1tv5Vj986Aj2JPBu7cQZ\n" +"Zxq1KG6KwfeB4EQTxJ5Nt+qJlPC8QPGoep1XejCSgShW6/NjK76C+dXvYFy1Poj+\n" +"4iddW385y1MB+7AwjXEpEQHv5XZ+lkXSk8qtQkgGgQjies6tHKdNv1cfmXMrk0zv\n" +"c+YOQZxCqIUyI0nqyiCA8+2FNYW7PwIDAQABAoICAQCXgxoJsAvB6dWgUFVYaCcl\n" +"39L5i8wmETOom8BTzaeZiNX7zlpigd69lpJQI3ZqJU13Mngf+Qqv8hnRL/PO93uj\n" +"8y31LQDR4YrGUdQIZS2f/iPjtMi8EYJ65cUkap+7uC9NInr8Dkf2ZWPlY7pyAy1k\n" +"VCNRCm1TtDR8u4zV9tBUnHL8ztYzCscVQ9U0ap8wYxDdZsEZUNon/gfG6Sv/t4zF\n" +"qlK42FpooEedB0QOXoAmK2brDDmfBkaBRVqLAinrDDbK3qDIIjNUdJiLSCmBAEeU\n" +"wD/yD0k8gtA+i7iWTmxAF9+/AfC6P7UcffaREpTnIkJ3OUSUgy07L1QEXY0fWx2P\n" +"OFy/ZwUJBvmVCL6dJkDZyBHjDwiu9V09sbdQ9dU+eM8XeaYq1DxWtfuVYnCvId1b\n" +"i6kEZTSAW2IVMazcbZA7GYH+yrYt7Gmhyy/9fR1Kovf6bouJFOhK0oBNNBGf3rZj\n" +"VfZyVJ6U1gGx7DGKGeWHIUswtXEBjpfAZ436k6ruKKyDfneeb82uCf4jp/vFVNN3\n" +"CxiAsCoicULdtKj4U4EmxN9HInGPpLBT32hfHLUnpNzFmoAN6dVRjA++4kzq9Q3Q\n" +"qFgoV7pXP/A2nyZv+QD5GJ218a7B/QThmWsAEEaaNYyNzKmowDckv6cGwTiBv3zD\n" +"7wAQ2n5Vh4bStbiTqRbroQKCAQEA+PRzSPIwlhU0iDhTqTec+RxyYOuQMEizwJHr\n" +"+kgJlvmhUVQ3ALQKzcTRrkz6VAgO/MvoF2gUj6bVLcEo/jqHrc7IC83L4+B7xBFh\n" +"M7dELCvIiETIPivwVSW5vgLY51O2aiJdsZRr7L0jyjQP1uMoc304JegXAC7SxwqH\n" +"+gmsmGMlUfB2I4NYRR12+so7paGqGYgjHaki6e1oNKaWk/8W8FJWh7Vqa9RTEkFD\n" +"oog0JM6yT1ykm2fRdsPaar2lcYbfXAdPuEMpTE+3pQ+au62ZS7vdFGx1FL5ffZyS\n" +"UvmxywJZBvW8Al++PbGuX39AJ948WM/riTt1M2N+AOOsJ32f+QKCAQEA1oAX64id\n" +"eMoXjUjekektTp1hcDRTipF7npjnxI1DUhDJTWgeAUlLzC+RDUpJl64vVF8yEGM2\n" +"N9R1TVQ+B9QglC0OQzpp0h6nCeTcfn12SzzlqsyKzx/07Sucg2VRIdUzpad9gKCR\n" +"Qza5v96rGl0yN7kDrjN9WK511wzLgYdKFkqsvC/bW62HFKkDbfEKqy8qTNy3Haus\n" +"dgfc9uMeqLzuC73bHqVxkRvOdIbRhQw1KGggpnw3Jrs94qydMJu3MYZPfsTbeDvC\n" +"44O83dsrVjOKFXGVTOtZRluHKeeArdtmfUmZaENUXwyaSiGU89Y7AOo+vOFHXMjm\n" +"r/V6fKnVbo/y9wKCAQEAx3NIvWNTK5p3iL7fv81PVIDG3gE7doN4h0og7VYzYKJD\n" +"7J10p3qWwT3y4xrG3vXJ1BwkqEP5XRFC7zI2fl8z/jqRKGvK8pkRbwahgkZMNrsp\n" +"IItChhS7qevcgG9ViRcXKLa5q6CGSpdJiiDlo7o/2S60AiKL8tiQg2hbgiWoAjpE\n" +"Vv44F8GNwWmWvduxp8P6PBRGVegAkbti5fOk5ZLTtNuyeW0NgrALka952UgXxnlW\n" +"f6BwPBUTynukjCm911M/tUIiSzR7bKjdLz9uLvgovXUX7Nnrfx/57u+2hwWGvGb4\n" +"HkxXQOulxVWJpvaS1p4EaP7C7CIXhoEqHNpKPSU3OQKCAQEArJs9JGK13Ro6o42M\n" +"1LtfoxBP9VuWEj6JzJDciDTohGRPqMNsyboyjWeFgL1TxQP8wBcukTNU0M5dalGs\n" +"7N3NLY+oF38s4lGaNwL8T6kkBN1HLw8TcCMWE7fxZWalR+VpfxbtjhEnc3/ZL0W+\n" +"SCPQojh2drqmVjNlThzUsjGs8405vOGB0h8sQPrUcKbz39a/YkSF8hFQYVZogB85\n" +"b61AnSA08E9PuOY4V1qZxUeSiyZnh7ETLE6mOP6QKypS2z5qP+end/QXGr/Kvnh8\n" +"QgyNRD43V0NXfp9uf9DzonOX4J/WG6l6flYE3jxxwVmV92GIBLP/mfFseRG/dAuy\n" +"XRrm9wKCAQAFRj1X8h3ePt7sCUUZXN2XBsEPx7W+hVzl+STu4oDmPMcCL8tL6Ndd\n" +"eUZChT+fZbgSk+rw7OYnNGi5+ES3qRQwXdIJKP8Niu0cHCFPaikWn5rC3Yu8ngsg\n" +"XsrVCNsvfDZkfRtd73s8LFp0+pmTe1AlWVxcDnBZOsoezppDxikHgoRdNbPjGGrO\n" +"T/J8XCPS5aT5TOr1tywKgruqLuZ7v7W6zLDBeImqKGDbH7D5+8vMYUu6d1hoXETp\n" +"DuBmagv/t80FQda1p6b7V0ICvp7GuqGhMjgBFDDszs3cdDZa8sZvheMPOog56EYd\n" +"Rnvuj8XvBcSE6pVTMgkCw06w2fpef7T7\n" "-----END RSA PRIVATE KEY-----\n" "-----BEGIN CERTIFICATE-----\n" -"MIIDajCCAtOgAwIBAgIJANm4W/Tzs+s+MA0GCSqGSIb3DQEBCwUAMIGqMQswCQYD\n" -"VQQGEwJVUzERMA8GA1UECBMITmV3IFlvcmsxETAPBgNVBAcTCE5ldyBZb3JrMRYw\n" -"FAYDVQQKEw1TdGVhbWhlYXQubmV0MRQwEgYDVQQLEwtFbmdpbmVlcmluZzEdMBsG\n" -"A1UEAxMUb3BlbmNhLnN0ZWFtaGVhdC5uZXQxKDAmBgkqhkiG9w0BCQEWGWVuZ2lu\n" -"ZWVyaW5nQHN0ZWFtaGVhdC5uZXQwHhcNMjAwOTAxMDAwMDAwWhcNMzAwOTAxMDAw\n" -"MDAwWjCBqjELMAkGA1UEBhMCVVMxETAPBgNVBAgTCE5ldyBZb3JrMREwDwYDVQQH\n" -"EwhOZXcgWW9yazEWMBQGA1UEChMNU3RlYW1oZWF0Lm5ldDEUMBIGA1UECxMLRW5n\n" -"aW5lZXJpbmcxHTAbBgNVBAMTFG9wZW5jYS5zdGVhbWhlYXQubmV0MSgwJgYJKoZI\n" -"hvcNAQkBFhllbmdpbmVlcmluZ0BzdGVhbWhlYXQubmV0MIIBIjANBgkqhkiG9w0B\n" -"AQEFAAOCAQ8AMIIBCgKCAQEA25ZTHopLfMKYeVwZoWtDwBd1RqCJhIL7wgDy/05j\n" -"zIYm8JFfxwFI+pau/3mLVHYPrl9rXeM5ZBONeLwewDJAsIYVtyZ8O9pICK6uz4so\n" -"gfwST8Uc/sumdd/mq2vIw4zEGjcaXdmMZYrJOxlvx32Fsrc5M6iNIMQYFnLPU3Ks\n" -"JhvOAZiM6jEn2wIszhq5X/5HupaOuCaZqCxOtaXaWguJF4SGFW6tBMxaY4ryiLyU\n" -"wC0EGPoHC2f34DxeXpRQ9TNS3H//L90wTok6slrqZQ/4zXU64+0pd5q35FqnS7gU\n" -"7H6SQ9yjTWPPe/gNTVPWoQd9SJJmB7Q1BYukcfDnh69NBwIDAQABoxIwEDAOBgNV\n" -"HQ8BAf8EBAMCBLAwDQYJKoZIhvcNAQELBQADgYEAc4mWHK3HHAwWXIsJztUOCEaT\n" -"yDpzqt5nnDqg5Q3/1HhiM4wsWoam9ixTcZk25+5xcMsvuSoDvzAJzyd5wpBkOq/z\n" -"UeWxZmLYOzOghrT62TLJVxAqh0AdEP8jMWOAeWrrOXnXx8AvG1+R8n4Rf5/koSa8\n" -"wJrrW4j7WAEsY5kG4hU=\n" +"MIIFZTCCA02gAwIBAgIUMAJUww8HOXGFlyZvieX9rzd+1x4wDQYJKoZIhvcNAQEL\n" +"BQAwQjELMAkGA1UEBhMCWFgxFTATBgNVBAcMDERlZmF1bHQgQ2l0eTEcMBoGA1UE\n" +"CgwTRGVmYXVsdCBDb21wYW55IEx0ZDAeFw0yMDA4MDQxMDQxMzRaFw0zODA1MjIx\n" +"MDQxMzRaMEIxCzAJBgNVBAYTAlhYMRUwEwYDVQQHDAxEZWZhdWx0IENpdHkxHDAa\n" +"BgNVBAoME0RlZmF1bHQgQ29tcGFueSBMdGQwggIiMA0GCSqGSIb3DQEBAQUAA4IC\n" +"DwAwggIKAoICAQDQmOnboooYGgbUrNr+O7xQRrEn/pFdVfWTmW/vCbWWL7BYerxs\n" +"9Uh7E1PmQpIovdw1DClFx4vMkdvbRHzkILY4+mgN05FLdwncxF8X+za4p7nhvJWu\n" +"2TIinRmkmHRIiXncMU4yqreKnjAX9kacWxPMglvnge7CUsywWmaN7qyFT+ywcuN/\n" +"EGoCiFU7Dzen/HqTgPGeq4gOJ9wlImFuaAiA7696K7UwBI/QEN76QmYOG/iXdZNn\n" +"p1DDY9h2pA2fmJTmKUzzkk8XXz+ZQ4/NHOdzLvl7znRrlI2Y6m4LEr1cCdn7mWNE\n" +"So5dkif8LVX1j/RDOP6Dtv+oYscGTPSSR+Wlcw/0K4tAOILtDs1IVAGhcfZbTXM3\n" +"EQS66Zx84yrlqkno6JKaGEKvtF9hqEYT7lxHP/kIsyxZvXAhJQ8A1ajTDcqetyzp\n" +"hQiaKqeWTEmobibD8JtqshggTVv5DtvdU62AfrDfOXub51/+WtdhjCe30aIrLpAa\n" +"XOTktqYW1tv5Vj986Aj2JPBu7cQZZxq1KG6KwfeB4EQTxJ5Nt+qJlPC8QPGoep1X\n" +"ejCSgShW6/NjK76C+dXvYFy1Poj+4iddW385y1MB+7AwjXEpEQHv5XZ+lkXSk8qt\n" +"QkgGgQjies6tHKdNv1cfmXMrk0zvc+YOQZxCqIUyI0nqyiCA8+2FNYW7PwIDAQAB\n" +"o1MwUTAdBgNVHQ4EFgQUWE9IXPXnQXqYKQYcDSjAxNSwejowHwYDVR0jBBgwFoAU\n" +"WE9IXPXnQXqYKQYcDSjAxNSwejowDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0B\n" +"AQsFAAOCAgEAu94JzOvp/NQ+/OPaJw6cilSu5E+S1mcLJiPWmkv41Gwnl86rDfS1\n" +"eVmR58jJhKKypeahNgCMq1dvlIrlIrQEF6hi2JBMjYDPNCWPcWzCqVbOSXfNRKWg\n" +"78nzAuSj0Kp3WEsw95ACmGbJODEW3Ga+AzRIPe4vw35sv06eZsUJJ3QD4mTjOEV9\n" +"UQvVozP5FUCY2VLBjjWT6dDykDiYKTc/xaE2SUgRgykY4nJxihEN6QMLghlEuPyY\n" +"mOKKXXMQDyZalGc9V/VtUY3qNnrbIhcBQeZgKXGRPEqnbTw0H7Q+fSc7xj5bFAmr\n" +"ufjQSWCqqbPNPvgt9NytOUrCzNeKk2x/BUDyI0kHUBj17HtBNo9o4ongcSM2Qs/Z\n" +"kfi/lr/UpqpelIlreC8IJsAY5cgjeChebAwEhf8uGh41grJwmowjVSDqFb0rINTO\n" +"imUEABpFQ/zBGdF1ZG/y07N7mvgEE0UwcH8Si1wSIxWlXw36dED1yoUROKgTXG4k\n" +"ChJmWyPwZoxjBtR86UwIyVgC2Z8pya8h7uvp2wKtlSNUqpXmCvsl+X/zib2YRwI/\n" +"QvxbM4J50AGyIiqXzuULCg2ap9t7Zpc9/+hg0t5BEbym+RbcNLy+lh4ZjrEwH3Xe\n" +"LNIU1lM0Xyg0SY6o1hfH3eiRukedhlametaIGwYG6n5gzYgh7T4W+uI=\n" "-----END CERTIFICATE-----\n"}; /* These private materials were made with: - * openssl req -new -x509 -keyout cakey.pem -out cacert.pem -nodes -days 6500 + * openssl req -new -x509 -keyout cakey.pem -out cacert.pem -nodes -days 6500 -pkeyopt rsa_keygen_bits:4096 * TODO: We need a full-blown capability to work with user-supplied * keypairs and properly-signed certificates. */ From 581cb7c2de315ea2ae3e8dbdf47197930d8c1182 Mon Sep 17 00:00:00 2001 From: MSP-Greg Date: Wed, 19 May 2021 15:49:15 -0500 Subject: [PATCH 263/343] GitHub Actions - add Ruby 3.0 --- .github/workflows/workflow.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 1b0264516..5119be7f0 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -1,6 +1,6 @@ name: CI -on: [push, pull_request] +on: [push, pull_request, workflow_dispatch] jobs: build: @@ -11,7 +11,7 @@ jobs: fail-fast: false matrix: os: [ ubuntu-20.04, macos-10.15, windows-2019 ] - ruby: [ 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, head ] + ruby: [ 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, '3.0', head ] include: - { os: windows-2019, ruby: mingw } exclude: @@ -26,7 +26,7 @@ jobs: ruby-version: ${{ matrix.ruby }} mingw: _upgrade_ openssl - - name: macOS disable firewall + - name: macOS disable firewall if: startsWith(matrix.os, 'macos') run: | sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate off From d7b568a5d1aec733b6fd2c0def670fcb4b42cb50 Mon Sep 17 00:00:00 2001 From: MSP-Greg Date: Thu, 20 May 2021 08:58:35 -0500 Subject: [PATCH 264/343] Windows pre-compiled gem - minimum Ruby version set to 2.2 --- appveyor.yml | 6 ++++++ win_gem_test/eventmachine.ps1 | 7 ++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 50935a51f..9487a29d8 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,10 @@ --- +image: +- Visual Studio 2019 + +build: off +deploy: off + install: # download shared script files - ps: >- diff --git a/win_gem_test/eventmachine.ps1 b/win_gem_test/eventmachine.ps1 index 4d83255d8..d1ad80f9f 100644 --- a/win_gem_test/eventmachine.ps1 +++ b/win_gem_test/eventmachine.ps1 @@ -1,4 +1,5 @@ -# PowerShell script for building & testing SQLite3-Ruby fat binary gem +# PowerShell script for building & testing EventMachine pre-compiled gem +# builds EM with ssl support # Code by MSP-Greg, see https://github.com/MSP-Greg/av-gem-build-test # load utility functions, pass 64 or 32 @@ -13,10 +14,10 @@ Make-Const repo_name 'eventmachine' Make-Const url_repo 'https://github.com/eventmachine/eventmachine.git' #———————————————————————————————————————————————————————————————— lowest ruby version -Make-Const ruby_vers_low 20 +Make-Const ruby_vers_low 22 # null = don't compile; false = compile, ignore test (allow failure); # true = compile & test -Make-Const trunk $false ; Make-Const trunk_x64 $false +Make-Const trunk $null ; Make-Const trunk_x64 $null Make-Const trunk_JIT $null ; Make-Const trunk_x64_JIT $null #———————————————————————————————————————————————————————————————— make info From e97d63e0f586e8f8a071195434a7a88a209e6ed9 Mon Sep 17 00:00:00 2001 From: MSP-Greg Date: Fri, 21 May 2021 09:28:34 -0500 Subject: [PATCH 265/343] .gitignore - add vendor/bundle/ --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 22049a633..398e335d6 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,7 @@ java/src/.project *.rbc Gemfile.lock +vendor/bundle/ .yardoc/* doc/* tmp/* From caf9ac1f02fe6251f71f557fc392d13c81d088fb Mon Sep 17 00:00:00 2001 From: MSP-Greg Date: Fri, 21 May 2021 09:29:05 -0500 Subject: [PATCH 266/343] eventmachine.gemspec - remove incorrect metadata code --- eventmachine.gemspec | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/eventmachine.gemspec b/eventmachine.gemspec index 278351c20..1c561fa05 100644 --- a/eventmachine.gemspec +++ b/eventmachine.gemspec @@ -17,9 +17,7 @@ Gem::Specification.new do |s| if s.respond_to?(:metadata=) s.metadata ||= {} - if RbConfig::CONFIG['ruby_version'] >= '2.5' - s.metadata["msys2_mingw_dependencies"] = "openssl" - end + s.metadata["msys2_mingw_dependencies"] = "openssl" end s.add_development_dependency 'test-unit', '~> 3.2' From ded6e8ffca7fd00b38c18dc968e0efd2d17b75f3 Mon Sep 17 00:00:00 2001 From: MSP-Greg Date: Fri, 21 May 2021 09:29:26 -0500 Subject: [PATCH 267/343] test_io_streamer.rb - require 'stringio' --- tests/test_io_streamer.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_io_streamer.rb b/tests/test_io_streamer.rb index a536fc308..bfc24ba45 100644 --- a/tests/test_io_streamer.rb +++ b/tests/test_io_streamer.rb @@ -2,6 +2,7 @@ require_relative 'em_test_helper' require 'em/io_streamer' +require 'stringio' # below to stop 'already initialized constant' warning EM::IOStreamer.__send__ :remove_const, :CHUNK_SIZE From ec27b6250c2a00b4f2720abd86669e194ca016ac Mon Sep 17 00:00:00 2001 From: MSP-Greg Date: Fri, 21 May 2021 11:24:15 -0500 Subject: [PATCH 268/343] Update test_processes.rb --- tests/test_processes.rb | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/tests/test_processes.rb b/tests/test_processes.rb index eb21a6cec..9546022e7 100644 --- a/tests/test_processes.rb +++ b/tests/test_processes.rb @@ -32,23 +32,42 @@ def setup end def test_em_system + out, status = nil, nil + EM.run{ - EM.system('ls'){ |out,status| $out, $status = out, status; EM.stop } + EM.system('ls'){ |_out,_status| out, status = _out, _status; EM.stop } } - assert( $out.length > 0 ) - assert_equal(0, $status.exitstatus) - assert_kind_of(Process::Status, $status) + assert(out.length > 0 ) + assert_kind_of(Process::Status, status) + assert_equal(0, status.exitstatus) + end + + def test_em_system_bad_exitstatus + status = nil + sys_pid = nil + + EM.run{ + sys_pid = EM.system('exit 1'){ |_out,_status| status = _status; EM.stop } + } + + assert_kind_of(Process::Status, status) + refute_equal(0, status.exitstatus) + assert_equal sys_pid, status.pid end def test_em_system_pid - $pids = [] + status = nil + sys_pid = nil EM.run{ - $pids << EM.system('echo hi', proc{ |out,status|$pids << status.pid; EM.stop }) + sys_pid = EM.system('echo hi', proc{ |_out,_status| status = _status; EM.stop }) } - assert_equal $pids[0], $pids[1] + refute_equal(0, sys_pid) + assert_kind_of(Process::Status, status) + refute_equal(0, status.pid) + assert_equal sys_pid, status.pid end def test_em_system_with_proc @@ -57,8 +76,8 @@ def test_em_system_with_proc } assert( $out.length > 0 ) - assert_equal(0, $status.exitstatus) assert_kind_of(Process::Status, $status) + assert_equal(0, $status.exitstatus) end def test_em_system_with_two_procs From daeb121fb5600cc0f4fc604fbf7d742578f26483 Mon Sep 17 00:00:00 2001 From: MSP-Greg Date: Sat, 12 Jun 2021 22:37:37 -0500 Subject: [PATCH 269/343] Fixes for Process::Status changes in Ruby 3 --- ext/extconf.rb | 3 +++ ext/rubymain.cpp | 36 ++++++++++++++++++++++++++++++------ 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/ext/extconf.rb b/ext/extconf.rb index 0fb654104..ce83c0028 100644 --- a/ext/extconf.rb +++ b/ext/extconf.rb @@ -138,6 +138,9 @@ def find_openssl_library add_define "HAVE_KQUEUE" if have_header("sys/event.h") && have_header("sys/queue.h") end +# Add for changes to Process::Status in Ruby 3 +add_define("IS_RUBY_3_OR_LATER") if RUBY_VERSION > "3.0" + # Adjust number of file descriptors (FD) on Windows if RbConfig::CONFIG["host_os"] =~ /mingw/ diff --git a/ext/rubymain.cpp b/ext/rubymain.cpp index 5da5c8b25..36018c63d 100644 --- a/ext/rubymain.cpp +++ b/ext/rubymain.cpp @@ -85,7 +85,24 @@ static VALUE Intern_proxy_target_unbound; static VALUE Intern_proxy_completed; static VALUE Intern_connection_completed; -static VALUE rb_cProcStatus; +static VALUE rb_cProcessStatus; + +#ifdef IS_RUBY_3_OR_LATER +struct rb_process_status { + rb_pid_t pid; + int status; + int error; +}; + +static const rb_data_type_t rb_process_status_type = { + .wrap_struct_name = "Process::Status", + .function = { + .dfree = RUBY_DEFAULT_FREE, + }, + .data = NULL, + .flags = RUBY_TYPED_FREE_IMMEDIATELY, +}; +#endif struct em_event { uintptr_t signature; @@ -553,11 +570,18 @@ static VALUE t_get_subprocess_status (VALUE self UNUSED, VALUE signature) if (evma_get_subprocess_status (NUM2BSIG (signature), &status)) { if (evma_get_subprocess_pid (NUM2BSIG (signature), &pid)) { - proc_status = rb_obj_alloc(rb_cProcStatus); +#ifdef IS_RUBY_3_OR_LATER + struct rb_process_status *data = NULL; + proc_status = TypedData_Make_Struct(rb_cProcessStatus, struct rb_process_status, &rb_process_status_type, data); + data->pid = pid; + data->status = status; +#else + proc_status = rb_obj_alloc(rb_cProcessStatus); /* MRI Ruby uses hidden instance vars */ - rb_iv_set(proc_status, "status", INT2FIX(status)); - rb_iv_set(proc_status, "pid", INT2FIX(pid)); + rb_ivar_set(proc_status, rb_intern_const("status"), INT2FIX(status)); + rb_ivar_set(proc_status, rb_intern_const("pid"), INT2FIX(pid)); +#endif #ifdef RUBINIUS /* Rubinius uses standard instance vars */ @@ -572,7 +596,7 @@ static VALUE t_get_subprocess_status (VALUE self UNUSED, VALUE signature) #endif } } - + rb_obj_freeze(proc_status); return proc_status; } @@ -1431,7 +1455,7 @@ extern "C" void Init_rubyeventmachine() { // Lookup Process::Status for get_subprocess_status VALUE rb_mProcess = rb_const_get(rb_cObject, rb_intern("Process")); - rb_cProcStatus = rb_const_get(rb_mProcess, rb_intern("Status")); + rb_cProcessStatus = rb_const_get(rb_mProcess, rb_intern("Status")); // Tuck away some symbol values so we don't have to look 'em up every time we need 'em. Intern_at_signature = rb_intern ("@signature"); From e70b16d6d07b1fd4126b5dae074e3abdeb0b97c0 Mon Sep 17 00:00:00 2001 From: MSP-Greg Date: Sat, 12 Jun 2021 22:46:56 -0500 Subject: [PATCH 270/343] Update workflow.yml --- .github/workflows/workflow.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 5119be7f0..13333ace8 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -7,10 +7,11 @@ jobs: name: >- ${{ matrix.os }} ${{ matrix.ruby }} runs-on: ${{ matrix.os }} + timeout-minutes: 10 strategy: fail-fast: false matrix: - os: [ ubuntu-20.04, macos-10.15, windows-2019 ] + os: [ ubuntu-20.04, ubuntu-18.04, macos-10.15, windows-2019 ] ruby: [ 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, '3.0', head ] include: - { os: windows-2019, ruby: mingw } From 330ba20d369427a7be09050f67a3625637987710 Mon Sep 17 00:00:00 2001 From: MSP-Greg Date: Sat, 12 Jun 2021 22:55:31 -0500 Subject: [PATCH 271/343] Ruby 3.1 - use bundler to install net-smtp, now a bundled gem, not a default gem --- Gemfile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Gemfile b/Gemfile index 702c8b3b3..170d211b3 100644 --- a/Gemfile +++ b/Gemfile @@ -4,6 +4,10 @@ gemspec gem 'rake' +install_if -> { RUBY_VERSION > '3.1' } do + gem "net-smtp" +end + group :documentation do gem 'yard', '>= 0.8.5.2' gem 'redcarpet' unless RUBY_PLATFORM =~ /java|mswin/ From 88be06efc88f98f59a9efa632dd87aa7977c8713 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Sat, 3 Jul 2021 13:16:24 -0700 Subject: [PATCH 272/343] Switch from static to runtime structure definition Reported to be implemented by GCC 8 and higher, this workaround handles older versions. Note that a definition like this is fine in C99. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=55606 --- ext/rubymain.cpp | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/ext/rubymain.cpp b/ext/rubymain.cpp index 36018c63d..37c944bfa 100644 --- a/ext/rubymain.cpp +++ b/ext/rubymain.cpp @@ -88,20 +88,12 @@ static VALUE Intern_connection_completed; static VALUE rb_cProcessStatus; #ifdef IS_RUBY_3_OR_LATER +/* Structure definition from MRI Ruby 3.0 process.c */ struct rb_process_status { rb_pid_t pid; int status; int error; }; - -static const rb_data_type_t rb_process_status_type = { - .wrap_struct_name = "Process::Status", - .function = { - .dfree = RUBY_DEFAULT_FREE, - }, - .data = NULL, - .flags = RUBY_TYPED_FREE_IMMEDIATELY, -}; #endif struct em_event { @@ -573,6 +565,17 @@ static VALUE t_get_subprocess_status (VALUE self UNUSED, VALUE signature) #ifdef IS_RUBY_3_OR_LATER struct rb_process_status *data = NULL; + + /* Defined to match static definition from MRI Ruby 3.0 process.c + * + * Older C++ compilers before GCC 8 don't allow static initialization of a + * struct without every field specified, so the definition here is at runtime + */ + static rb_data_type_t rb_process_status_type; + rb_process_status_type.wrap_struct_name = "Process::Status"; + rb_process_status_type.function.dfree = RUBY_DEFAULT_FREE; + rb_process_status_type.flags = RUBY_TYPED_FREE_IMMEDIATELY; + proc_status = TypedData_Make_Struct(rb_cProcessStatus, struct rb_process_status, &rb_process_status_type, data); data->pid = pid; data->status = status; From 8e1d6b11fd8400593af035a7a0d203d24c10c9b0 Mon Sep 17 00:00:00 2001 From: Naoto Ono Date: Sat, 18 Sep 2021 12:18:36 +0900 Subject: [PATCH 273/343] Fix several typos in GettingStarted.md. (#953) --- docs/GettingStarted.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/GettingStarted.md b/docs/GettingStarted.md index a99ed2466..5ff512045 100644 --- a/docs/GettingStarted.md +++ b/docs/GettingStarted.md @@ -56,7 +56,7 @@ To install the EventMachine gem do ### Verifying your installation ### -Lets verify your installation with this quick IRB session: +Let's verify your installation with this quick IRB session: irb -rubygems @@ -68,7 +68,7 @@ Lets verify your installation with this quick IRB session: ## An Echo Server Example ## -Lets begin with the classic "Hello, world"-like example, an echo server. The echo server responds clients with the +Let's begin with the classic "Hello, world"-like example, an echo server. The echo server responds clients with the same data that was provided. First, here's the code: {include:file:examples/guides/getting\_started/01\_eventmachine\_echo_server.rb} @@ -100,7 +100,7 @@ the same string: It works! Congratulations, you now can tell your Node.js-loving friends that you "have done some event-driven programming, too". Oh, and to stop Telnet, hit Control + Shift + ] and then Control + C. -Lets walk this example line by line and see what's going on. These lines +Let's walk this example line by line and see what's going on. These lines require 'rubygems' # or use Bundler.setup require 'eventmachine' @@ -115,7 +115,7 @@ Next: end end -Is the implementation of our echo server. We define a class that inherits from {EventMachine::Connection} +are the implementation of our echo server. We define a class that inherits from {EventMachine::Connection} and a handler (aka callback) for one event: when we receive data from a client. EventMachine handles the connection setup, receiving data and passing it to our handler, {EventMachine::Connection#receive_data}. @@ -123,7 +123,7 @@ EventMachine handles the connection setup, receiving data and passing it to our Then we implement our protocol logic, which in the case of Echo is pretty trivial: we send back whatever we receive. To do so, we're using {EventMachine::Connection#send_data}. -Lets modify the example to recognize `exit` command: +Let's modify the example to recognize `exit` command: {include:file:examples/guides/getting\_started/02\_eventmachine\_echo_server\_that\_recognizes\_exit\_command.rb} @@ -147,7 +147,7 @@ To summarize this first example: * Use {EventMachine.run} to start EventMachine event loop and then bind echo server with {EventMachine.start_server}. * To stop the event loop, use {EventMachine.stop_event_loop} (aliased as {EventMachine.stop}) -Lets move on to a slightly more sophisticated example that will introduce several more features and methods +Let's move on to a slightly more sophisticated example that will introduce several more features and methods EventMachine has to offer. @@ -198,7 +198,7 @@ In the example above we use a @@class_variable to keep track of connected client methods so we can add new connections to the list from `SimpleChatServer#post_init` and remove them in `SimpleChatServer#unbind`. We can also filter connections by some criteria, as `SimpleChatServer#other_peers demonstrates`. -So, we keep track of connections but how do we identify them? For a chat app, it's pretty common to use usernames for that. Lets ask our clients +So, we keep track of connections but how do we identify them? For a chat app, it's pretty common to use usernames for that. Let's ask our clients to enter usernames when they connect. @@ -214,7 +214,7 @@ Here is one way to do it: {include:file:examples/guides/getting\_started/06\_simple\_chat\_server\_step\_three.rb} -This is quite an update so lets take a look at each method individually. First, `SimpleChatServer#post_init`: +This is quite an update so let's take a look at each method individually. First, `SimpleChatServer#post_init`: def post_init @username = nil @@ -226,7 +226,7 @@ To keep track of username we ask chat participants for, we add @username instanc instances are just Ruby objects associated with a particular connected peer, so using @ivars is very natural. To make username value accessible to other objects, we added a reader method that was not shown on the snippet above. -Lets dig into `SimpleChatServer#ask_username`: +Let's dig into `SimpleChatServer#ask_username`: def ask_username self.send_line("[info] Enter your username:") @@ -264,7 +264,7 @@ Finally, handler of chat messages is not yet implemented: raise NotImplementedError end -Lets try this example out using Telnet: +Let's try this example out using Telnet: ~ telnet localhost 10000 Trying 127.0.0.1... @@ -289,7 +289,7 @@ It is annoying, so why don't we add the same `exit` command to our chat server? TBD -Lets test-drive this version. Client A: +Let's test-drive this version. Client A: ~ telnet localhost 10000 Trying 127.0.0.1... From 008359dc4b5dd4b381e9a2dd6b9b48e9948b84b1 Mon Sep 17 00:00:00 2001 From: Olle Jonsson Date: Thu, 24 Feb 2022 17:45:24 +0100 Subject: [PATCH 274/343] CI: Add Ruby 3.1 to matrix (#962) --- .github/workflows/workflow.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 13333ace8..f92ede6a9 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -12,7 +12,7 @@ jobs: fail-fast: false matrix: os: [ ubuntu-20.04, ubuntu-18.04, macos-10.15, windows-2019 ] - ruby: [ 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, '3.0', head ] + ruby: [ 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, '3.0', 3.1, head ] include: - { os: windows-2019, ruby: mingw } exclude: From 46b3f200a30a0ec530b09a3138551eda0c9d58c9 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Thu, 24 Feb 2022 08:47:52 -0800 Subject: [PATCH 275/343] CI: Quote all Ruby versions for consistency as strings not FPs --- .github/workflows/workflow.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index f92ede6a9..b498c7fe9 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -1,6 +1,6 @@ name: CI -on: [push, pull_request, workflow_dispatch] +on: [ push, pull_request, workflow_dispatch ] jobs: build: @@ -12,7 +12,7 @@ jobs: fail-fast: false matrix: os: [ ubuntu-20.04, ubuntu-18.04, macos-10.15, windows-2019 ] - ruby: [ 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, '3.0', 3.1, head ] + ruby: [ '2.2', '2.3', '2.4', '2.5', '2.6', '2.7', '3.0', '3.1', head ] include: - { os: windows-2019, ruby: mingw } exclude: From df4ab0068e5e9f504096584093a74510d0dac6c8 Mon Sep 17 00:00:00 2001 From: Keith Bennett Date: Tue, 15 Mar 2022 03:32:45 +0700 Subject: [PATCH 276/343] Remove mention of Ruby version upper bound from readme. (#964) --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b954f4616..46ec6e1a1 100644 --- a/README.md +++ b/README.md @@ -32,8 +32,9 @@ EventMachine has been around since the early 2000s and is a mature and battle-te ## What platforms are supported by EventMachine? ## -EventMachine supports Ruby 2.0.0 through 2.7, JRuby and **works well on Windows** as well -as many operating systems from the Unix family (Linux, Mac OS X, BSD flavors). +EventMachine supports Ruby 2.0.0 and later (see tested versions at +[.github/workflows/workflow.yml](.github/workflows/workflow.yml)). It runs on JRuby and **works well on Windows** +as well as many operating systems from the Unix family (Linux, Mac OS X, BSD flavors). From 03ffbcf378df05772007e44158ddd2b584ecd11e Mon Sep 17 00:00:00 2001 From: Holger Just Date: Mon, 12 Sep 2022 20:58:11 +0200 Subject: [PATCH 277/343] Add missing SSL constants to pure ruby reactor --- lib/em/pure_ruby.rb | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/em/pure_ruby.rb b/lib/em/pure_ruby.rb index 26ee530f4..6389c2863 100644 --- a/lib/em/pure_ruby.rb +++ b/lib/em/pure_ruby.rb @@ -501,6 +501,17 @@ module EventMachine EM_PROTO_TLSv1_1 = 16 # @private EM_PROTO_TLSv1_2 = 32 + # @private + EM_PROTO_TLSv1_3 = 64 if OpenSSL::SSL.const_defined?(:TLS1_3_VERSION) + + # @private + OPENSSL_LIBRARY_VERSION = OpenSSL::OPENSSL_LIBRARY_VERSION + # @private + OPENSSL_VERSION = OpenSSL::OPENSSL_VERSION + # @private + OPENSSL_NO_SSL2 = false + # @private + OPENSSL_NO_SSL3 = false end module EventMachine From f31fc57ad83b11f54626b6a8419cc944a14ce625 Mon Sep 17 00:00:00 2001 From: Holger Just Date: Mon, 12 Sep 2022 21:00:22 +0200 Subject: [PATCH 278/343] Fix cert usage in EM.set_tls_parms with pure ruby --- lib/em/pure_ruby.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/em/pure_ruby.rb b/lib/em/pure_ruby.rb index 6389c2863..fb365ffd6 100644 --- a/lib/em/pure_ruby.rb +++ b/lib/em/pure_ruby.rb @@ -280,7 +280,7 @@ def set_tls_parms signature, priv_key_path, priv_key, priv_key_pass, cert_chain_ @tls_parms[signature][:priv_key] = priv_key if tls_parm_set?(priv_key) @tls_parms[signature][:priv_key_pass] = priv_key_pass if tls_parm_set?(priv_key_pass) @tls_parms[signature][:cert_chain] = File.read(cert_chain_path) if tls_parm_set?(cert_chain_path) - @tls_parms[signature][:cert_chain] = cert if tls_parm_set?(cert_chain) + @tls_parms[signature][:cert_chain] = cert if tls_parm_set?(cert) @tls_parms[signature][:sni_hostname] = sni_hostname if tls_parm_set?(sni_hostname) @tls_parms[signature][:cipher_list] = cipher_list.gsub(/,\s*/, ':') if tls_parm_set?(cipher_list) @tls_parms[signature][:dhparam] = File.read(dhparam) if tls_parm_set?(dhparam) From 6a0a3685fcb25885022bf6a3b29585710ed3e3f1 Mon Sep 17 00:00:00 2001 From: Holger Just Date: Mon, 12 Sep 2022 20:20:04 +0200 Subject: [PATCH 279/343] Increase keysize to 2048 bits in EventMachine::CertificateCreator OpenSSL with a security level >= 2 requires RSA keys to be at least 2048 bits. Since the default cert and key are always set first on a new SSLContext, using a smaller key size causes an exception when starting a TLS connection using the pure Ruby reactor. --- lib/em/pure_ruby.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/em/pure_ruby.rb b/lib/em/pure_ruby.rb index fb365ffd6..99d0df9af 100644 --- a/lib/em/pure_ruby.rb +++ b/lib/em/pure_ruby.rb @@ -70,7 +70,7 @@ class CertificateCreator attr_reader :cert, :key def initialize - @key = OpenSSL::PKey::RSA.new(1024) + @key = OpenSSL::PKey::RSA.new(2048) public_key = @key.public_key subject = "/C=EventMachine/O=EventMachine/OU=EventMachine/CN=EventMachine" @cert = OpenSSL::X509::Certificate.new From 80b4884cb13a65a440936843b71331703405085f Mon Sep 17 00:00:00 2001 From: Holger Just Date: Thu, 15 Sep 2022 12:15:27 +0200 Subject: [PATCH 280/343] Add test_em_pure_ruby step to CI workflow --- .github/workflows/workflow.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index b498c7fe9..bb0ba7870 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -44,3 +44,9 @@ jobs: env: CI: true TESTOPTS: -v --no-show-detail-immediately + + - name: test_em_pure_ruby + run: bundle exec rake test_em_pure_ruby + env: + CI: true + TESTOPTS: -v --no-show-detail-immediately From 42374129ab73c799688e4f5483e9872e7f175bed Mon Sep 17 00:00:00 2001 From: Kuba Suder Date: Mon, 17 Jul 2023 22:23:54 +0200 Subject: [PATCH 281/343] update Rubydoc documentation URL in the readme (#982) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 46ec6e1a1..d7cac9de9 100644 --- a/README.md +++ b/README.md @@ -89,7 +89,7 @@ EventMachine.run { ## EventMachine documentation ## -Currently we only have [reference documentation](http://rdoc.info/github/eventmachine/eventmachine/frames) and a [wiki](https://github.com/eventmachine/eventmachine/wiki). +Currently we only have [reference documentation](http://rubydoc.info/github/eventmachine/eventmachine/frames) and a [wiki](https://github.com/eventmachine/eventmachine/wiki). ## Community and where to get help ## From 901e7183cca69ee8cbf526c0f02a493a1561c7f4 Mon Sep 17 00:00:00 2001 From: nick evans Date: Tue, 3 Sep 2024 09:35:40 -0400 Subject: [PATCH 282/343] Drop unsupported ubuntu-18.04 runner from CI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GitHub Actions dropped support for ubuntu-18.04 on 2023-04-03: - https://github.com/actions/runner-images/issues/6002 Although there are benefits to testing with older OS versions—especially given EventMachine's legacy status and the types of projects which are most likely to depend on it—there are also significant costs to maintaining CI for them once GitHub Actions has dropped support. --- .github/workflows/workflow.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index b498c7fe9..b9d8e4bdf 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -11,7 +11,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ ubuntu-20.04, ubuntu-18.04, macos-10.15, windows-2019 ] + os: [ ubuntu-20.04, macos-10.15, windows-2019 ] ruby: [ '2.2', '2.3', '2.4', '2.5', '2.6', '2.7', '3.0', '3.1', head ] include: - { os: windows-2019, ruby: mingw } From 5de0c2399d294d1aeeb465bfb58c87f1aaa46f71 Mon Sep 17 00:00:00 2001 From: nick evans Date: Tue, 3 Sep 2024 09:49:07 -0400 Subject: [PATCH 283/343] Add newer Ubuntu runners to CI Each Ubuntu LTS comes with a different version of OpenSSL, Linux Kernel, etc. And peopler are likely to deploy EventMachine applications to any of these versions. So there is some benefit to testing every Ubuntu LTS release that is supported by GitHub Actions. --- .github/workflows/workflow.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index b9d8e4bdf..210a61c96 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -11,7 +11,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ ubuntu-20.04, macos-10.15, windows-2019 ] + os: [ ubuntu-24.04, ubuntu-22.04, ubuntu-20.04, macos-10.15, windows-2019 ] ruby: [ '2.2', '2.3', '2.4', '2.5', '2.6', '2.7', '3.0', '3.1', head ] include: - { os: windows-2019, ruby: mingw } From 6941226e11aaed5ba483fc74e7812cb4c2c4a67d Mon Sep 17 00:00:00 2001 From: nick evans Date: Tue, 3 Sep 2024 10:39:30 -0400 Subject: [PATCH 284/343] Exclude ubuntu-22.04 / ruby 2.2 from CI This avoids the following error: Bundler 2 requires Ruby 2.3+, using Bundler 1 on Ruby <= 2.2 /opt/hostedtoolcache/Ruby/2.2.10/x64/bin/gem install bundler -v ~> 1.0 ERROR: While executing gem ... (RuntimeError) Marshal.load reentered at marshal_load See https://github.com/ruby/setup-ruby/issues/496 This works fine on ubuntu 20.04 and 24.04. --- .github/workflows/workflow.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 210a61c96..477394fd7 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -17,6 +17,18 @@ jobs: - { os: windows-2019, ruby: mingw } exclude: - { os: windows-2019, ruby: head } + + # Avoids the following error: + # Bundler 2 requires Ruby 2.3+, using Bundler 1 on Ruby <= 2.2 + # /opt/hostedtoolcache/Ruby/2.2.10/x64/bin/gem install bundler -v ~> 1.0 + # ERROR: While executing gem ... (RuntimeError) + # Marshal.load reentered at marshal_load + # + # See https://github.com/ruby/setup-ruby/issues/496 + # + # This works fine on ubuntu 20.04 and 24.04. + - { os: ubuntu-22.04, ruby: '2.2' } + steps: - name: repo checkout uses: actions/checkout@v2 From 76310f37a8714b2da15c425107de4948fbebe677 Mon Sep 17 00:00:00 2001 From: nick evans Date: Tue, 3 Sep 2024 12:58:56 -0400 Subject: [PATCH 285/343] Switch CI to use macos-latest runner GitHub Actions dropped support for macos-10.15 on 2022-12-01: - https://github.com/actions/runner-images/issues/5583 Under the presumption that MacOS is primarily used for development and not for deployment, I've switched the macos GitHub Actions runners to use the "latest" release version for ruby >= 2.6. Earlier ruby's don't support arm64, so they are using the macos-13 runner (which is amd64). On the one hand, this is less explicit and is no longer "pinned", so it may result in CI failing when GitHub Actions updates these runners. On the other hand, we also won't need another PR to update these every time an old version is dropped or a new version is added. I attempted to make the same change from windows-2019 to windows-latest. But I saw three tests that reliably pass under windows-2019 and reliably fail under windows-2022. Since I don't have a Windows OS dev env at my disposal, I'm opting to just let windows CI continue under windows-2019, for now. --- .github/workflows/workflow.yml | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 477394fd7..df8132df6 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -11,13 +11,27 @@ jobs: strategy: fail-fast: false matrix: - os: [ ubuntu-24.04, ubuntu-22.04, ubuntu-20.04, macos-10.15, windows-2019 ] + os: [ ubuntu-24.04, ubuntu-22.04, ubuntu-20.04, windows-2019, macos-latest ] ruby: [ '2.2', '2.3', '2.4', '2.5', '2.6', '2.7', '3.0', '3.1', head ] include: - { os: windows-2019, ruby: mingw } + + # CRuby < 2.6 does not support macos-arm64, so these use amd64 + - { os: macos-13, ruby: "2.2" } + - { os: macos-13, ruby: "2.3" } + - { os: macos-13, ruby: "2.4" } + - { os: macos-13, ruby: "2.5" } + exclude: + - { os: macos-latest, ruby: head } - { os: windows-2019, ruby: head } + # CRuby < 2.6 does not support macos-arm64, so these use amd64 + - { os: macos-latest, ruby: "2.2" } + - { os: macos-latest, ruby: "2.3" } + - { os: macos-latest, ruby: "2.4" } + - { os: macos-latest, ruby: "2.5" } + # Avoids the following error: # Bundler 2 requires Ruby 2.3+, using Bundler 1 on Ruby <= 2.2 # /opt/hostedtoolcache/Ruby/2.2.10/x64/bin/gem install bundler -v ~> 1.0 From abe437892392b317b08ab8e94695300849ba260f Mon Sep 17 00:00:00 2001 From: nick evans Date: Tue, 3 Sep 2024 10:07:55 -0400 Subject: [PATCH 286/343] Add newer ruby versions to the CI matrix Ruby 3.2 should pass in CI. However, ruby 3.3 (and head) will not pass until GH-988 (or something like it) is merged. Windows mingw is likewise excluded; it builds ruby HEAD. --- .github/workflows/workflow.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index df8132df6..21dd97597 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -12,9 +12,11 @@ jobs: fail-fast: false matrix: os: [ ubuntu-24.04, ubuntu-22.04, ubuntu-20.04, windows-2019, macos-latest ] - ruby: [ '2.2', '2.3', '2.4', '2.5', '2.6', '2.7', '3.0', '3.1', head ] + ruby: [ '2.2', '2.3', '2.4', '2.5', '2.6', '2.7', '3.0', '3.1', '3.2', '3.3', head ] include: - - { os: windows-2019, ruby: mingw } + + # GH-988 must be merged to support ruby >= 3.3 + # - { os: windows-2019, ruby: mingw } # CRuby < 2.6 does not support macos-arm64, so these use amd64 - { os: macos-13, ruby: "2.2" } @@ -26,6 +28,10 @@ jobs: - { os: macos-latest, ruby: head } - { os: windows-2019, ruby: head } + # GH-988 must be merged to support ruby >= 3.3 + - { ruby: 3.3 } + - { ruby: head } + # CRuby < 2.6 does not support macos-arm64, so these use amd64 - { os: macos-latest, ruby: "2.2" } - { os: macos-latest, ruby: "2.3" } From a7f8621d2da42095fd067f0c666ef473b84a5f15 Mon Sep 17 00:00:00 2001 From: nick evans Date: Tue, 3 Sep 2024 10:08:55 -0400 Subject: [PATCH 287/343] Update actions/checkout to v4 actions/checkout@v2 has been deprecated for some time now. --- .github/workflows/workflow.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 21dd97597..653cf2643 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -51,7 +51,7 @@ jobs: steps: - name: repo checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: load ruby, openssl uses: MSP-Greg/setup-ruby-pkgs@v1 From 8913ac5816d0b4dbe517ef9ff7d513fd2fe1e397 Mon Sep 17 00:00:00 2001 From: nick evans Date: Tue, 3 Sep 2024 12:16:53 -0400 Subject: [PATCH 288/343] Update ruby/setup-ruby-pkgs to ruby organization It looks like ruby/setup-ruby-pkgs contains all of the commits in MSP-Greg/setup-ruby-pkgs. I'm assuming that the ruby repository is now the official version and that new updates will be pushed there. So this switches to that repo. --- .github/workflows/workflow.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 653cf2643..d635319f2 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -54,7 +54,7 @@ jobs: uses: actions/checkout@v4 - name: load ruby, openssl - uses: MSP-Greg/setup-ruby-pkgs@v1 + uses: ruby/setup-ruby-pkgs@v1 with: ruby-version: ${{ matrix.ruby }} mingw: _upgrade_ openssl From ccdaca084eda311f35c1690f429f10c7ae9ea95d Mon Sep 17 00:00:00 2001 From: nick evans Date: Tue, 3 Sep 2024 10:56:43 -0400 Subject: [PATCH 289/343] Raise CI timeout intervals for windows and macos Several of these tests appeared to be flaky in CI, but only for macos or windows. So I went through and raised many of them, for those OSes only. In many cases, I changed the timeouts to be a multiple of TIMEOUT_INTERVAL, so that individual tests don't each need to check if they are running on `windows? || darwin?`. --- tests/em_test_helper.rb | 2 +- tests/test_basic.rb | 4 ++-- tests/test_httpclient2.rb | 2 +- tests/test_idle_connection.rb | 2 +- tests/test_inactivity_timeout.rb | 4 ++-- tests/test_ipv4.rb | 2 +- tests/test_pause.rb | 2 +- tests/test_pure.rb | 2 +- tests/test_send_file.rb | 2 +- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/em_test_helper.rb b/tests/em_test_helper.rb index 7366c2999..cb5d38cd4 100644 --- a/tests/em_test_helper.rb +++ b/tests/em_test_helper.rb @@ -151,7 +151,7 @@ def rbx? extend PlatformHelper # Tests may run slower on windows or Appveyor. YMMV - TIMEOUT_INTERVAL = windows? ? 0.25 : 0.25 + TIMEOUT_INTERVAL = windows? || darwin? ? 0.50 : 0.25 module EMTestCasePrepend def setup diff --git a/tests/test_basic.rb b/tests/test_basic.rb index 04def7964..a8dcdceca 100644 --- a/tests/test_basic.rb +++ b/tests/test_basic.rb @@ -38,7 +38,7 @@ def test_em def test_timer assert_nothing_raised do EM.run { - setup_timeout(darwin? ? 0.6 : 0.4) + setup_timeout(TIMEOUT_INTERVAL * 2) n = 0 EM.add_periodic_timer(0.1) { n += 1 @@ -163,7 +163,7 @@ def test_bind_connect end EM.run do - darwin? ? setup_timeout(0.3) : setup_timeout + setup_timeout EM.start_server "127.0.0.1", @port, bound_server EM.bind_connect local_ip, bind_port, "127.0.0.1", @port end diff --git a/tests/test_httpclient2.rb b/tests/test_httpclient2.rb index fe7b0c0b9..cf6d8cdc8 100644 --- a/tests/test_httpclient2.rb +++ b/tests/test_httpclient2.rb @@ -4,7 +4,7 @@ class TestHttpClient2 < Test::Unit::TestCase class TestServer < EM::Connection end - TIMEOUT = (windows? ? 2.0 : 1) + TIMEOUT = TIMEOUT_INTERVAL * 4.0 # below may be due to an issue with OpenSSL 1.0.2 and earlier with Windows CI_WINDOWS_OLD = windows? and RUBY_VERSION < '2.5' diff --git a/tests/test_idle_connection.rb b/tests/test_idle_connection.rb index 168fbb540..f445b911b 100644 --- a/tests/test_idle_connection.rb +++ b/tests/test_idle_connection.rb @@ -25,7 +25,7 @@ def test_idle_time end end - assert_in_delta 0.3, a, (darwin? ? 0.2 : 0.1) + assert_in_delta 0.3, a, (windows? || darwin? ? 0.3 : 0.1) assert_in_delta 0, b, 0.1 end end diff --git a/tests/test_inactivity_timeout.rb b/tests/test_inactivity_timeout.rb index e42ef579b..2cd02e4a9 100644 --- a/tests/test_inactivity_timeout.rb +++ b/tests/test_inactivity_timeout.rb @@ -46,7 +46,7 @@ def test_for_real } } # Travis can vary from 0.02 to 0.17, Appveyor maybe as low as 0.01 - assert_in_delta 0.09, (finish - start), (darwin? ? 0.10 : 0.08) + assert_in_delta 0.09, (finish - start), (darwin? ? 0.20 : 0.08) # simplified reproducer for comm_inactivity_timeout taking twice as long # as requested -- https://github.com/eventmachine/eventmachine/issues/554 @@ -87,7 +87,7 @@ def test_for_real } # .30 is double the timeout and not acceptable - assert_in_delta 0.15, (finish - start), (darwin? ? 0.20 : 0.14) + assert_in_delta 0.15, (finish - start), (darwin? ? 0.30 : 0.14) # make sure it was a timeout and not a TLS error assert_equal Errno::ETIMEDOUT, reason end diff --git a/tests/test_ipv4.rb b/tests/test_ipv4.rb index 1e5d76fdf..723dd6884 100644 --- a/tests/test_ipv4.rb +++ b/tests/test_ipv4.rb @@ -33,7 +33,7 @@ def test_ipv4_udp_local_server @@received_data = nil @local_port = next_port - setup_timeout(darwin? ? 4 : 2) + setup_timeout(TIMEOUT_INTERVAL * 8) EM.run do EM::open_datagram_socket(@@public_ipv4, @local_port) do |s| diff --git a/tests/test_pause.rb b/tests/test_pause.rb index 628034d01..3526ebd37 100644 --- a/tests/test_pause.rb +++ b/tests/test_pause.rb @@ -48,7 +48,7 @@ def post_init EM.start_server "127.0.0.1", @port, test_server EM.connect "127.0.0.1", @port, test_client - tmr = darwin? ? 0.10 : 0.05 + tmr = darwin? ? 0.25 : 0.05 EM.add_timer(tmr) do assert_equal 1, s_rx diff --git a/tests/test_pure.rb b/tests/test_pure.rb index 1782fe7d4..3c4934d90 100644 --- a/tests/test_pure.rb +++ b/tests/test_pure.rb @@ -150,7 +150,7 @@ def test_periodic_timer EM.stop if x == 4 end } - assert_in_delta 0.8, (finish - start), (darwin? ? 0.6 : 0.2) + assert_in_delta 0.8, (finish - start), TIMEOUT_INTERVAL assert_equal 4, x end end diff --git a/tests/test_send_file.rb b/tests/test_send_file.rb index 573cdd6ae..2ef1f7fde 100644 --- a/tests/test_send_file.rb +++ b/tests/test_send_file.rb @@ -179,7 +179,7 @@ def test_stream_large_file_data EM.run { EM.start_server "127.0.0.1", @port, StreamTestModule, @filename - setup_timeout(darwin? ? 0.5 : 0.25) + setup_timeout EM.connect "127.0.0.1", @port, TestClient do |c| c.data_to { |d| data << d } end From 227abbedd324271650c630169459fd3eb9046920 Mon Sep 17 00:00:00 2001 From: nick evans Date: Tue, 3 Sep 2024 11:13:32 -0400 Subject: [PATCH 290/343] Increase timeout for test_dispatch_completion I'm seeing intermittent failures for this test even in ubuntu runners. So I've increased the time from 3 seconds to 5 seconds, and I've made it a multiple of TIMEOUT_INTERVAL (so it'll be 10 seconds on MacOS and Windows). --- tests/test_threaded_resource.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_threaded_resource.rb b/tests/test_threaded_resource.rb index d55b7df66..eca0ad5c8 100644 --- a/tests/test_threaded_resource.rb +++ b/tests/test_threaded_resource.rb @@ -17,7 +17,7 @@ def teardown def test_dispatch_completion EM.run do - EM.add_timer(3) do + EM.add_timer(TIMEOUT_INTERVAL * 20) do EM.stop if ENV['CI'].casecmp('true').zero? and RUBY_PLATFORM[/darwin/] notify "Intermittent Travis MacOS: Resource dispatch timed out" From 3fa584d8ae733785bf53336bd716ac320352c00b Mon Sep 17 00:00:00 2001 From: nick evans Date: Tue, 3 Sep 2024 12:11:31 -0400 Subject: [PATCH 291/343] Double test_periodic_timer timeout (in TestPure) This timeout was flaky in CI, so I doubled the allowed delta. --- tests/test_pure.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_pure.rb b/tests/test_pure.rb index 3c4934d90..1cf774283 100644 --- a/tests/test_pure.rb +++ b/tests/test_pure.rb @@ -150,7 +150,7 @@ def test_periodic_timer EM.stop if x == 4 end } - assert_in_delta 0.8, (finish - start), TIMEOUT_INTERVAL + assert_in_delta 0.8, (finish - start), TIMEOUT_INTERVAL * 2 assert_equal 4, x end end From 3052028a7536f4455a4999c0444c834bf7f2e365 Mon Sep 17 00:00:00 2001 From: nick evans Date: Tue, 3 Sep 2024 14:44:30 -0400 Subject: [PATCH 292/343] Increase CI timeout for windows and macos --- .github/workflows/workflow.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index d635319f2..2326e6289 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -7,7 +7,7 @@ jobs: name: >- ${{ matrix.os }} ${{ matrix.ruby }} runs-on: ${{ matrix.os }} - timeout-minutes: 10 + timeout-minutes: ${{ (startsWith(matrix.os, 'macos') || startsWith(matrix.os, 'windows')) && 15 || 10 }} strategy: fail-fast: false matrix: From 4851031d37360925da373b791b18ddd7988280ce Mon Sep 17 00:00:00 2001 From: "nicholas a. evans" Date: Fri, 12 Nov 2021 22:18:47 -0500 Subject: [PATCH 293/343] Generate test CA and certificates in rake As part of tidying up the SSL implementation, I'm updating all of the tests to use secure defaults. This commit doesn't enable certificate verification or identity verification, yet. This just adds a little bit of infrastructure for quickly testing several different SSL scenarios. Currently, it's just a single CA which has signed a single "localhost" certificate. I'll update it to use intermediate certs and multiple hostnames, etc, in future PRs. --- rakelib/test.rake | 220 ++++++++++++++++++++++++++++- rakelib/test_pure.rake | 2 + tests/client.crt | 31 ---- tests/client.key | 51 ------- tests/em_ssl_handlers.rb | 28 +++- tests/encoded_client.key | 54 ------- tests/fixtures/.gitignore | 7 + tests/fixtures/em-localhost.yml | 15 ++ tests/fixtures/eventmachine-ca.yml | 17 +++ tests/test_httpclient2.rb | 2 +- tests/test_ssl_args.rb | 15 +- tests/test_ssl_inline_cert.rb | 79 +++++------ tests/test_ssl_verify.rb | 30 ++-- 13 files changed, 342 insertions(+), 209 deletions(-) delete mode 100644 tests/client.crt delete mode 100644 tests/client.key delete mode 100644 tests/encoded_client.key create mode 100644 tests/fixtures/.gitignore create mode 100644 tests/fixtures/em-localhost.yml create mode 100644 tests/fixtures/eventmachine-ca.yml diff --git a/rakelib/test.rake b/rakelib/test.rake index b3a462b78..e2299e6e6 100644 --- a/rakelib/test.rake +++ b/rakelib/test.rake @@ -1,6 +1,224 @@ -require 'rake/testtask' +require "rake/testtask" +require "rake/clean" +require "yaml" +require "openssl" Rake::TestTask.new(:test) do |t| t.pattern = 'tests/**/test_*.rb' t.warning = true end + +directory "tests/fixtures" + +namespace "test" do + + namespace "fixtures" do + + CLEAN_FIXTURES = ::Rake::FileList[ + "tests/fixtures/*.csr" + ] + CLOBBER_FIXTURES = ::Rake::FileList[ + "tests/fixtures/*.aes-key", + "tests/fixtures/*.crt", + "tests/fixtures/*.key", + "tests/fixtures/*.pass", + "tests/fixtures/*.pem", + "tests/fixtures/*.pub", + ] + + desc "Remove temporary test fixture files" + task :clean do + Rake::Cleaner.cleanup_files(CLEAN_FIXTURES) + end + + desc "Remove all generated test fixture files" + task clobber: %i[clean] do + Rake::Cleaner.cleanup_files(CLOBBER_FIXTURES) + end + + desc "Remove certs which may have expired" + task :expire do + any_expired = CLOBBER_FIXTURES.ext('crt') + .map {|f| load_cert(f) } + .any? {|c| c.not_after < Time.now + 60 } + Rake::Task["test:fixtures:clobber"].invoke if any_expired + end + + desc "Generate all certificates" + task certs: :expire + + def write_pem(obj, file, passphrase: nil) + cipher = OpenSSL::Cipher.new 'AES-128-CBC' + open(file, "w") do |io| + io << ( + passphrase ? obj.to_pem(cipher, passphrase) : obj.to_pem + ) + end + end + + def x509_subject(cfg) + subject = cfg.fetch("subject") + subject.respond_to?(:to_a) ? + OpenSSL::X509::Name.new(subject.to_a) : + OpenSSL::X509::Name.parse(subject.to_str) + end + + def x509_not_after(cfg) + Time.now + (cfg.fetch("ttl_hours", 24) * 60 * 60) + end + + def x509_make_csr(cfg, key) + csr = OpenSSL::X509::Request.new + csr.subject = x509_subject(cfg) + csr.version = cfg.fetch("version", 2) # 2 == v3 + csr.public_key = key.public_key + csr.sign key, OpenSSL::Digest::SHA256.new + csr + end + + def x509_random_serial + rand(1..(2**159-1)) + end + + def load_cfg(f) + YAML.load(File.read(f)) + end + + def load_cert(f) + open(f) {|io| OpenSSL::X509::Certificate.new(io.read) } + end + + def load_key(f, passfile=nil) + passphrase = File.read(passfile) if passfile + open(f) {|io| OpenSSL::PKey.read(io, passphrase) } + end + + def get_ca_crt_and_key(ca) + ca_crtfile = "tests/fixtures/#{ca}.crt" + ca_keyfile = "tests/fixtures/#{ca}.key" + ca_crt = open(ca_crtfile) {|io| OpenSSL::X509::Certificate.new(io.read) } + ca_key = open(ca_keyfile) {|io| OpenSSL::PKey.read(io) } + [ca_crt, ca_key] + end + + # Following the example from the stdlib openssl gem + def x509_make_ca_crt(cfg, key) + crt = OpenSSL::X509::Certificate.new + crt.version = cfg.fetch("version", 3) + crt.serial = x509_random_serial + crt.not_before = Time.now + crt.not_after = x509_not_after(cfg) + crt.public_key = key.public_key + crt.subject = x509_subject(cfg) + crt.issuer = crt.subject # CA cert is self-signed! + + xf = OpenSSL::X509::ExtensionFactory.new + xf.subject_certificate = crt + xf.issuer_certificate = crt + crt.add_extension xf.create_extension("subjectKeyIdentifier", "hash") + crt.add_extension xf.create_extension("basicConstraints", "CA:TRUE", true) + crt.add_extension xf.create_extension("keyUsage", "cRLSign,keyCertSign", true) + + crt.sign key, OpenSSL::Digest::SHA256.new + crt + end + + def x509_issue_crt_from_csr(cfg, csr) + ca_crt, ca_key = get_ca_crt_and_key(cfg.fetch("ca")) + + crt = OpenSSL::X509::Certificate.new + crt.version = cfg.fetch("version", 3) + crt.serial = x509_random_serial + crt.not_before = Time.now + crt.not_after = x509_not_after(cfg) + crt.public_key = csr.public_key + crt.subject = csr.subject + crt.issuer = ca_crt.subject + + xf = OpenSSL::X509::ExtensionFactory.new + xf.subject_certificate = crt + xf.issuer_certificate = ca_crt + crt.add_extension xf.create_extension("subjectKeyIdentifier", "hash") + crt.add_extension xf.create_extension("basicConstraints", "CA:FALSE") + crt.add_extension xf.create_extension( + "keyUsage", "keyEncipherment,dataEncipherment,digitalSignature" + ) + + crt.sign ca_key, OpenSSL::Digest::SHA256.new + crt + end + + Rake::FileList["tests/fixtures/*.yml"].each do |f| + cfg = load_cfg(f) + fcrt = f.ext('.crt') + ca = cfg.fetch("ca") + if ca == true + # Generating a CA certificate + fkey = f.ext('.key') + file fcrt => [fkey, f] do |t| + key = open(fkey) {|io| OpenSSL::PKey.read(io) } + crt = x509_make_ca_crt(cfg, key) + write_pem(crt, fcrt) + end + elsif cfg.fetch("csr") == true + # Generating a certificate from a CSR + fcsr = f.ext(".csr") + fcacrt = f.pathmap("%d/#{ca}.crt") + fcakey = f.pathmap("%d/#{ca}.key") + file fcrt => [fcsr, fcacrt, fcakey, f] do |t| + ca_crt = open(fcacrt) {|io| OpenSSL::X509::Certificate.new(io.read) } + ca_key = open(fcakey) {|io| OpenSSL::PKey.read(io) } + csr = OpenSSL::X509::Request.new(File.read(fcsr)) + crt = x509_issue_crt_from_csr(cfg, csr) + write_pem(crt, fcrt) + end + else + raise "unhandled config format: #{f}" + end + + task certs: fcrt + end + + rule ".csr" => [".key", ".yml"] do |t| + key = load_key(t.source) + cfg = load_cfg(t.source.ext(".yml")) + csr = x509_make_csr(cfg, key) + write_pem(csr, t.name) + end + + rule ".pub" => [".key", ".yml"] do |t| + key = load_key(t.source) + write_pem(key.public_key, t.name) + end + + rule ".key" => [".aes-key", ".yml"] do |t| + key = load_key(t.source, t.source.ext(".pass")) + write_pem(key, t.name) + end + + rule ".aes-key" => [".pass", ".yml"] do |t| + cipher = OpenSSL::Cipher.new 'AES-128-CBC' + passphrase = File.read(t.source) + key = OpenSSL::PKey::RSA.new 2048 + write_pem(key, t.name, passphrase: passphrase) + end + + rule ".pass" => ".yml" do |t| + require "securerandom" + open t.name, "w" do |io| + io << SecureRandom.urlsafe_base64(128) + end + end + + rule "tests/fixtures/*" => "tests/fixtures" + + end + + task fixtures: "fixtures:certs" + +end + +task clean: "test:fixtures:clean" +task clobber: "test:fixtures:clobber" + +task test: "test:fixtures" diff --git a/rakelib/test_pure.rake b/rakelib/test_pure.rake index 296add967..0567a46e1 100644 --- a/rakelib/test_pure.rake +++ b/rakelib/test_pure.rake @@ -9,3 +9,5 @@ task :test_em_pure_ruby do ENV['EM_PURE_RUBY'] = 'true' Rake::Task['test_pure'].execute end + +task test_em_pure_ruby: "test:fixtures" diff --git a/tests/client.crt b/tests/client.crt deleted file mode 100644 index 1919d976c..000000000 --- a/tests/client.crt +++ /dev/null @@ -1,31 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFRDCCAywCAQEwDQYJKoZIhvcNAQEFBQAwaDELMAkGA1UEBhMCRU0xFTATBgNV -BAgTDEV2ZW50TWFjaGluZTEVMBMGA1UEChMMRXZlbnRNYWNoaW5lMRQwEgYDVQQL -EwtEZXZlbG9wbWVudDEVMBMGA1UEAxMMRXZlbnRNYWNoaW5lMB4XDTA5MDMyOTAy -MzE0NloXDTEwMDMyOTAyMzE0NlowaDELMAkGA1UEBhMCRU0xFTATBgNVBAgTDEV2 -ZW50TWFjaGluZTEVMBMGA1UEChMMRXZlbnRNYWNoaW5lMRQwEgYDVQQLEwtEZXZl -bG9wbWVudDEVMBMGA1UEAxMMRXZlbnRNYWNoaW5lMIICIjANBgkqhkiG9w0BAQEF -AAOCAg8AMIICCgKCAgEAv1FSOIX1z7CQtVBFlrB0A3/V29T+22STKKmiRWYkKL5b -+hkrp9IZ5J4phZHgUVM2VDPOO2Oc2PU6dlGGZISg+UPERunTogxQKezCV0vcE9cK -OwzxCFDRvv5rK8aKMscfBLbNKocAXywuRRQmdxPiVRzbyPrl+qCr/EDLXAX3D77l -S8n2AwDg19VyI+IgFUE+Dy5e1eLoY6nV+Mq+vNXdn3ttF3t+ngac5pj5Q9h+pD5p -67baDHSnf/7cy2fa/LKrLolVHQR9G2K6cEfeM99NtcsMbkoPs4iI3FA05OVTQHXg -C8C8cRxrb9APl95I/ep65OIaCJgcdYxJ3QD3qOtQo6/NQsGnjbyiUxaEpjfqyT1N -uzWD81Q8uXGNS8yD6dDynt/lseBjyp2nfC3uQ5fY18VdIcu0MJ9pezBUKrNuhlsy -XXEZ2DXj4sY8QOvIcBqSB/zmS1nGEK55xrtkaiaNrY8fe8wRVpcPLxy+P225NFw+ -B69FJRA0Lj6Jt9BM4hV/3MSIEWwTVhuw4E02ywDYTzz1wq3ITf0tsbIPn0hXQMxD -ohhAoKioM6u+yHtqsxD0eYaAWmHTVn5oDvOSGpvCpBfWHyA7FP5UQak0fKABEAgK -iQYEnb294AXwXymJttfGTIV/Ne4tLN5dIpNma8UO8rlThlcr6xnTQDbR3gkTDRsC -AwEAATANBgkqhkiG9w0BAQUFAAOCAgEAj7J8fy1LUWoVWnrXDAC9jwJ1nI/YjoSU -6ywke3o04+nZC5S+dPnuVy+HAwsU940CoNvP6RStI/bH6JL+NIqEFmwM3M8xIEWV -MYVPkfvQUxxGvDnaY7vv93u+6Q77HV3qlhAQBHChyuXyO7TG3+WzsiT9AnBNtAP0 -4jClt5kCAQXLO/p0SFEZQ8Ru9SM8d1i73Z0VDVzs8jYWlBhiherSgbw1xK4wBOpJ -43XmjZsBSrDpiAXd07Ak3UL2GjfT7eStgebL3UIe39ThE/s/+l43bh0M6WbOBvyQ -i/rZ50kd1GvN0xnZhtv07hIJWO85FGWi7Oet8AzdUZJ17v1Md/f2vdhPVTFN9q+w -mQ6LxjackqCvaJaQfBEbqsn2Tklxk4tZuDioiQbOElT2e6vljQVJWIfNx38Ny2LM -aiXQPQu+4CI7meAh5gXM5nyJGbZvRPsxj89CqYzyHCYs5HBP3AsviBvn26ziOF+c -544VmHd9HkIv8UTC29hh+R64RlgMQQQdaXFaUrFPTs/do0k8n/c2bPc0iTdfi5Q2 -gq6Vi8q6Ay5wGgTtRRbn/mWKuCFjEh94z6pF9Xr06NX0PuEOdf+Ls9vI5vz6G0w6 -0Li7devEN7EKBY+7Mcjg918yq9i5tEiMkUgT68788t3fTC+4iUQ5fDtdrHsaOlIR -8bs/XQVNE/s= ------END CERTIFICATE----- diff --git a/tests/client.key b/tests/client.key deleted file mode 100644 index 87a25311c..000000000 --- a/tests/client.key +++ /dev/null @@ -1,51 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIJKAIBAAKCAgEAv1FSOIX1z7CQtVBFlrB0A3/V29T+22STKKmiRWYkKL5b+hkr -p9IZ5J4phZHgUVM2VDPOO2Oc2PU6dlGGZISg+UPERunTogxQKezCV0vcE9cKOwzx -CFDRvv5rK8aKMscfBLbNKocAXywuRRQmdxPiVRzbyPrl+qCr/EDLXAX3D77lS8n2 -AwDg19VyI+IgFUE+Dy5e1eLoY6nV+Mq+vNXdn3ttF3t+ngac5pj5Q9h+pD5p67ba -DHSnf/7cy2fa/LKrLolVHQR9G2K6cEfeM99NtcsMbkoPs4iI3FA05OVTQHXgC8C8 -cRxrb9APl95I/ep65OIaCJgcdYxJ3QD3qOtQo6/NQsGnjbyiUxaEpjfqyT1NuzWD -81Q8uXGNS8yD6dDynt/lseBjyp2nfC3uQ5fY18VdIcu0MJ9pezBUKrNuhlsyXXEZ -2DXj4sY8QOvIcBqSB/zmS1nGEK55xrtkaiaNrY8fe8wRVpcPLxy+P225NFw+B69F -JRA0Lj6Jt9BM4hV/3MSIEWwTVhuw4E02ywDYTzz1wq3ITf0tsbIPn0hXQMxDohhA -oKioM6u+yHtqsxD0eYaAWmHTVn5oDvOSGpvCpBfWHyA7FP5UQak0fKABEAgKiQYE -nb294AXwXymJttfGTIV/Ne4tLN5dIpNma8UO8rlThlcr6xnTQDbR3gkTDRsCAwEA -AQKCAgB495RDRQB9x6hX3F+DviI8rDGug+h5FAiwJ0IBG2o1kNdbNVsTC5dvpEmg -uPHaugCaEP+PMZbU34mNklKlb+7QbPbH18UGqz5so9TlmYOXz9oaKD6nAWL9nqRo -02pCXQDR3DuxbhbgFnFTIECJ/jqXkl2toGaVp83W+6kZkHP8srkMyLASihWgosc+ -xRWAGvaAZtNz7br+eT5fxuH/SEKPOl1qAZ23kXrXm1XQfizk8MnMTptkUMYv+hfl -TM98BASUsiTs6g+opy43HFn09naOQcqkWZO/8s6Gbvhi2lVfZqi5Ba6g3lVYJ3gU -kGoako4N9qB7WqJz+LYjVR9C4TbkkJ9OD6ArwGAx5IIzC3XKSxCyY/pUn4YumPhY -fjvY/km54TBtx/isS1TAgjSgDUxbzrfbkh7afOXSOniy9bWJMgNqHF61dqxWxmUg -F5Tch9zH3qFFVkXpYzDU/R8ZV+CRouCvhn0eZYDh8IqIAwjH0VjkxjPyQtrdrMd3 -gDKMVKoY31EOMLZzv8a0prjpr15A+uw30tT336qb3fofks4pZKUJw8ru9jJVir2p -+RML6iUHCmIeceF7/N1meooSMLPJe0xgKeMb9M4Wtd/et2UNVtP8nCDG622rf2a0 -F/EudXuFgc3FB8nXRw9TCkw9xKQff38edG5xPFUEgqObbVl5YQKCAQEA5DDKGOmp -EO5Zuf/kZfG6/AMMYwAuv1HrYTV2w/HnI3tyQ34Xkeqo+I/OqmRk68Ztxw4Kx1So -SRavkotrlWhhDpl2+Yn1BjkHktSoOdf9gJ9z9llkLmbOkBjmupig1NUB7fq/4y2k -MdqJXDy3uVKHJ97gxdIheMTyHiKuMJPnuT5lZtlT210Ig82P7sLQb/sgCfKVFTr0 -Z3haQ5/tBNKjq+igT4nMBWupOTD1q2GeZLIZACnmnUIhvu+3/bm0l+wiCB0DqF0T -Wy9tlL3fqQSCqzevL7/k5Lg6tJTaP/XYePB73TsOtAXgIaoltXgRBsBUeE1eaODx -kMT6E1PPtn7EqQKCAQEA1qImmTWGqhKICrwje40awPufFtZ/qXKVCN/V+zYsrJV1 -EnZpUDM+zfitlQCugnrQVHSpgfekI6mmVkmogO3fkNjUFTq+neg7IHOUHnqotx+3 -NMqIsyFInGstu9mfPd26fzZjUtx5wKF38LDTIJJAEJ83U3UpPBfpwKmiOGDXOa54 -2i4em/bb/hrQR6JySruZYLi0fXnGI5ZOfpkHgC/KOFkKNKAg2oh4B9qo7ACyiSNk -yojb2mmn6g1OLPxi7wGUSrkS1HQq4an6RZ+eUO0HXVWag0QStdQ91M9IrIHgSBBG -0e86Ar6jtD579gqsbz4ySpI/FqEI9obTC+E1/b0aIwKCAQAGz334qGCnZLXA22ZR -tJlEFEM2YTcD9snzqMjWqE2hvXl3kjfZ3wsUABbG9yAb+VwlaMHhmSE8rTSoRwj6 -+JaM/P+UCw4JFYKoWzh6IXwrbpbjb1+SEvdvTY71WsDSGVlpZOZ9PUt9QWyAGD/T -hCcMhZZn0RG2rQoc5CQWxxNPcBFOtIXQMkKizGvTUHUwImqeYWMZsxzASdNH2WoV -jsPbyaGfPhmcv83ZKyDp8IvtrXMZkiaT4vlm3Xi8VeKR9jY9z7/gMob1XcEDg3c9 -cCkGOy87WZrXSLhX02mAJzJCycqom66gqNw7pPxjIiY/8VWUEZsTvkL3cymTkhjM -9ZOhAoIBAGUaNqJe01NTrV+ZJgGyAxM6s8LXQYV5IvjuL2bJKxwUvvP2cT9FFGWD -qYiRrKJr5ayS07IUC+58oIzu33/0DSa27JgfduD9HrT3nKMK1mSEfRFSAjiXChQc -bIubRGapBoub/AdxMazqoovvT1R9b84kobQfcVAMV6DYh0CVZWyXYfgsV2DSVOiK -iufjfoDzg5lLCEI+1XW3/LunrB/W4yPN1X/amf8234ublYyt+2ucD4NUGnP05xLa -N6P7M0MwdEEKkvMe0YBBSFH5kWK/dIOjqkgBDes20fVnuuz/tL1dZW7IiIP4dzaV -ZGEOwBEatCfqYetv6b/u3IUxDfS7Wg8CggEBALoOwkn5LGdQg+bpdZAKJspGnJWL -Kyr9Al2tvgc69rxfpZqS5eDLkYYCzWPpspSt0Axm1O7xOUDQDt42luaLNGJzHZ2Q -Hn0ZNMhyHpe8d8mIQngRjD+nuLI/uFUglPzabDOCOln2aycjg1mA6ecXP1XMEVbu -0RB/0IE36XTMfZ+u9+TRjkBLpmUaX1FdIQQWfwUou/LfaXotoQlhSGAcprLrncuJ -T44UATYEgO/q9pMM33bdE3eBYZHoT9mSvqoLCN4s0LuwOYItIxLKUj0GulL0VQOI -SZi+0A1c8cVDXgApkBrWPDQIR9JS4de0gW4hnDoUvHtUc2TYPRnz6N9MtFY= ------END RSA PRIVATE KEY----- diff --git a/tests/em_ssl_handlers.rb b/tests/em_ssl_handlers.rb index fd6ac5b9a..11d85eb09 100644 --- a/tests/em_ssl_handlers.rb +++ b/tests/em_ssl_handlers.rb @@ -7,7 +7,7 @@ # EM.run do # EM.start_server IP, PORT, s_hndlr, server # EM.connect IP, PORT, c_hndlr, client -# end +# end # # It also passes parameters to the `start_tls` call within the `post_init` # callbacks of Client and Server. @@ -17,18 +17,37 @@ # # `Client` has a `:client_unbind` parameter, which when set to true, calls # `EM.stop_event_loop` in the `unbind` callback. -# +# # `Server` has two additional parameters. # # `:ssl_verify_result`, which is normally set to true/false for the # `ssl_verify_peer` return value. If it is set to a String starting with "|RAISE|", # the remaing string will be raised. -# +# # `:stop_after_handshake`, when set to true, will close the connection and then # call `EM.stop_event_loop`. # +# `:ssl_old_verify_peer` when set to true will setup `ssl_verify_peer` to only +# accept one argument, to test for compatibility with the original API. +# module EMSSLHandlers + CERTS_DIR = "#{__dir__}/fixtures" + + CA_NAME = "eventmachine-ca" + CERT_NAME = "em-localhost" + + CA_FILE = "#{CERTS_DIR}/#{CA_NAME}.crt" + CERT_FILE = "#{CERTS_DIR}/#{CERT_NAME}.crt" + ENCODED_KEY_FILE = "#{CERTS_DIR}/#{CERT_NAME}.aes-key" + PRIVATE_KEY_FILE = "#{CERTS_DIR}/#{CERT_NAME}.key" + ENCODED_PASSFILE = "#{CERTS_DIR}/#{CERT_NAME}.pass" + CA_PEM = File.read(CA_FILE).freeze + CERT_PEM = File.read(CERT_FILE).freeze + PRIVATE_KEY_PEM = File.read(PRIVATE_KEY_FILE).freeze + ENCODED_KEY_PEM = File.read(ENCODED_KEY_FILE).freeze + ENCODED_KEY_PASS = File.read(ENCODED_PASSFILE).freeze + IP, PORT = "127.0.0.1", 16784 # is OpenSSL version >= 1.1.0 @@ -100,6 +119,7 @@ module Server def initialize(tls = nil) @@tls = tls ? tls.dup : tls @@handshake_completed = false + @@cert = nil @@cert_value = nil @@cipher_bits = nil @@cipher_name = nil @@ -133,7 +153,7 @@ def ssl_verify_peer(cert) @@ssl_verify_result end end - + def ssl_handshake_completed @@handshake_completed = true @@cert_value = get_peer_cert diff --git a/tests/encoded_client.key b/tests/encoded_client.key deleted file mode 100644 index 4d19b766a..000000000 --- a/tests/encoded_client.key +++ /dev/null @@ -1,54 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -Proc-Type: 4,ENCRYPTED -DEK-Info: AES-128-CBC,4BFAC39AEF67B2BED03FB8D0E34BDB6E - -KXJGir0Kro0c4qoFCsyCyYQgBS0ul4FinsCei3haYwz3Vd/uvZD8wBoedzVho/Mz -99nZXZ52lHuwewGVvBeAnSXTrAtWCV2S9ZivYLZNqsEyxRlUF0SYat31bD2OImh8 -kXNC89HA9CEpRp3Zc0DU9JqqaRMv6SloMZA9AM/ioeHGj2o8Ajf4Vi0jg766cVFc -CyxHy+IOJww8FSBwGaghBixNS4rhtUonJUDeInB7C0jFqkoHQG1JCSGTzbK66bDf -kDhvUzxJnKUTezXGl5sKTRznla7aurWY5kFjz0b/api8ZCm/LvvsDipZbwwOHm31 -wMj9LPLzd0Sz2c3TyoPc/oCS9KVFnszndjaT6ydRLvHgF2XpPbL+xJz0HJwsvkis -kls0NHw69GvzTj6kZfz9a5XLpwjoGpeeMNBHw7SDRUNJnOEFwTecJL3NhCVSVgIy -owYTWDmPR8rcyNd007uhCio2+VxkZE2Hdmk0drz69mp0CNQbxe0qF8XKHTBT74I/ -L3KQITmMpSDNEn8bY/KmKpB6iuVBURvTpz5ZAPStsp4hcp0LlmPuhZZYGWC4Hox5 -svsp95iE5esN67RNUI3/twZhtJu8e5tIKy6qEWMWdrVeI0oVoLmfK+g5uQJaMu+Z -M/SLeTHjVOSq7r4Tg5Xmd8nzEQ8EyJKZCJshD7LZxjA34bOLID9Vhz0j/uE4IhNI -APO6gllYsWAGO4hADZH24i8ZvS2dX4ZR2YtLhbwjOXyVK4NYNGUeU5W0StMV8rlx -VLObQhN762YUFtm5nuP4z2GGcXgA4zCIRklbBw8N6+UP8RB54nrC4Mu6FyPolS3j -c0cVQG9q1YU5sgokMZwDJSO4AmBYUqckgNFMIRH06qGEqPoKqpi55myrLiZJmgTK -LuiaREGmyatjqqRm60sHUYrFpY+q/jsClmhvplQwOzAEX8MHwyyBp3tKokmdEniK -ULF8I4XP70U3jpLOdlPmVbYZ69Wna+5OQNdpvboAoe1l9kB+ZLq9exen0BN/beL8 -hr17q8pZUSPehVlaKgv8/3uQT/Tgi4x+KkAghjwEa97gFsQth50B2gInBK7KcveC -lRzyYM5CrIx1bXbsw3rgxODLUfX9eNRmXsznDeZZv0X0O18eVcwpp7OYZb4RnmTw -VkjjW1dfb+ZY/wLfx/LDE/enj3hbnPqjZ48V5UpTiaV3XGMEnMdXmjR1dlsPRJ2D -os3bzMT/Oo8CHu0VX73+JUrOCGQt3RpDslieQRdW6GEh0oxRo9AL8euDNyWBI1m9 -H8RRyaPwzY9GwTbpwXEtXoywHsuOhbQumng4QFOSDyAOiZoOt0q2MdmfZbyizDIQ -qIXFsnQG/NI4fUquTwIvmlnkoAtEWlwc41nvW0nEB5LNtUgJ5dvnmvQ4CqewbiMt -lm9AM47Bo3M/MjBg5NoNalEl1Wqx+H2nm6TslLA0CwNdsCKb/XSipXKwp8iWUZdT -4TrBk9PO++7+LZkfpokSqjvwQFjGTRwQsTYqNWpMawxxGX9xQPX2xr9HqgGR9/wq -gQ/GQJmfpOasKCXxUYfZ6RkLm/nkxryRdfdlqQ1l3PaBwSWL4kJaOxJbKQcRUIAb -HbPzxS1fykZY56Nk8mUtLBVTTSNPLo4VQtqZngEE8FqnAoet2eHnAE/vnMwiVu8p -lV+WeElUJ83q5UGKPtF6u3w+f989lMY2RA1vpIhN2cNmbuDIsutJjXNHRTHcmAvF -pnBZingBhr1MVSFIGk5QUTVFkznB/FqBd/gc5HbRRQgg6cym8XaAVLDGuFhBqzbr -B/bC7d4Qvn7/+G0zus04t1uGP/kGQnufFLO6IZocwGnhR1GdzyCJdfzeF7w3sNAT -SSx4GPCp5ewRPvoZap81TppRu9MbKQ00CKQMSqTwcFSQ253e5G1a0BSlmV9ElzVd -6voj4DVV5Lbr+IHIzUPbvZ7PQd6h/EurL1oieITaRm4V3L5o/nlt7V0w7e+PTLOg -SgupvYl72r405ds8lqlzJ5X2iQ/7qm5QX2DnWSuYNBjUJUxGCi4k3Scc1ZFhxU2j -+SFYrIXhW3BWO5lNij24v9UPmpP4dxRuTYcZfz7c3K9qDgjobKw39OtCHwUGN9Cb -dsxOQKYYijfNhg0c+uFAjDXaq2Mpn1GM4CUUOdIFEElYeboOmpEQg84O74RrR2LC -U4mN1/2YuZRiAtht23J07wJr4vMx+yDsTaMvzvNJLT9uV1VChWprfMB/uyW9MLcE -Sc6xMQsx5shBP5y3q3PX2mODIjg5xSXfWgyI12qK92X8vgzhERzidE64LbtRPio+ -SonNtkQFEbR2O6YmpPQ1lIrmj9+r1vjQGzMMn4317aK0EC0ntbCgX6nanvIvjbAT -S2YUE0W2Xyx4LO8/d8lxp7LoRzs8ejLUHr9XOeq6qGdPHtTtTyqtqzukRyTdjtK3 -4fZbSeNfCiC/PCbxEqVdGFHZHNKjm2vEQ+vqdP3lvVlnxKGGrQ86Vg44VcF5c+Vp -6wF2K1+6a0jarc5nAjpRCO0tlvIEmZ4bb0/jelrLhq7fD5689nE043mzNbuBzTGQ -k+Oe9kcr4wRJfjN1+F3mb94O/WxJSimCniXKEJ5WlJma82jMChyitwVsSTvct8e+ -JXJNNPbX2UdXhpU3GIjp2NKD5p1tjNA1j4lw/aGYcuLQUEu7u3/hdyGbRInI+FVy -LflkKR3N2uq1HbUomJbt2OyeQalHux652YGU5cshR2AHIn6iB0oIb2oYVzlzthNs -j+EEIMgZRQzrW1aEf2BTYlwCjRqCYoE0P6fc6RrPMYS+fLmFkjEikrC9mduoWItc -FtTLD/OY0xhXb5lF9UFFNmQSqMvagfI4GQ9vGg78vatSODw+Js88MqaQa3lbzcP3 -so5Bz2hBMY7bKjppUvseJtZETjSF4pjOVDGdVGpXis1iZj1DkP0IyXgt+u2LkvhQ -rYtYZHxEUVwjJyEtd0kvXDq5g5jkEO6A9aOMBUU4B4cbrRNVSIfpXPJ7koZpIRwd -A02+K21gS+DEkE8wBES+Qsuv8lTkTBpSFh60M8snrmJZ/2nJmX0SQ+kMakaCuh0h -q5orm9wFUiOPOfWJe7JNHJ1pH9Yi+elbQEr4yKc2tuRuLXVNRlMNSWyaEjCUqpi1 ------END RSA PRIVATE KEY----- diff --git a/tests/fixtures/.gitignore b/tests/fixtures/.gitignore new file mode 100644 index 000000000..1038dbd2f --- /dev/null +++ b/tests/fixtures/.gitignore @@ -0,0 +1,7 @@ +*.pass +*.aes-key +*.key +*.pub +*.ca-crt +*.csr +*.crt diff --git a/tests/fixtures/em-localhost.yml b/tests/fixtures/em-localhost.yml new file mode 100644 index 000000000..6ccbb756a --- /dev/null +++ b/tests/fixtures/em-localhost.yml @@ -0,0 +1,15 @@ +--- + +csr: true + +subject: + C: EM + ST: EventMachine + O: EventMachine + OU: Development + CN: localhost +version: 3 + +ca: eventmachine-ca + +certificate: {} diff --git a/tests/fixtures/eventmachine-ca.yml b/tests/fixtures/eventmachine-ca.yml new file mode 100644 index 000000000..66972fba3 --- /dev/null +++ b/tests/fixtures/eventmachine-ca.yml @@ -0,0 +1,17 @@ +--- + +ca: true + +subject: + C: RB + ST: EM + L: Devenv + O: EventMachine Developers + OU: Development + CN: EventMachine Testing CA + emailAddress: eventmachine@example.com +version: 3 + +ttl_hours: 24 + +certificate: {} diff --git a/tests/test_httpclient2.rb b/tests/test_httpclient2.rb index fe7b0c0b9..3656cd3e1 100644 --- a/tests/test_httpclient2.rb +++ b/tests/test_httpclient2.rb @@ -7,7 +7,7 @@ class TestServer < EM::Connection TIMEOUT = (windows? ? 2.0 : 1) # below may be due to an issue with OpenSSL 1.0.2 and earlier with Windows CI_WINDOWS_OLD = windows? and RUBY_VERSION < '2.5' - + def setup @port = next_port end diff --git a/tests/test_ssl_args.rb b/tests/test_ssl_args.rb index 8e7804f1a..fbd1b0cc7 100644 --- a/tests/test_ssl_args.rb +++ b/tests/test_ssl_args.rb @@ -28,21 +28,16 @@ def test_tls_params_file_doesnt_exist end def test_tls_cert_not_defined_twice - cert_file_path="#{__dir__}/client.crt" - cert=File.read "#{__dir__}/client.crt" - assert_raises EM::BadCertParams do - client_server client: {cert: cert, cert_chain_file: cert_file_path} + client_server client: { cert: CERT_PEM, cert_chain_file: CERT_FILE } end end def test_tls_key_not_defined_twice - cert_file_path="#{__dir__}/client.crt" - key_file_path="#{__dir__}/client.key" - key=File.read "#{__dir__}/client.key" - assert_raises EM::BadPrivateKeyParams do - client_server client: {private_key_file: key_file_path, private_key: key, cert_chain_file: cert_file_path} + client_server client: { private_key_file: PRIVATE_KEY_FILE, + private_key: PRIVATE_KEY_PEM, + cert_chain_file: CERT_FILE } end end @@ -52,7 +47,7 @@ def test_tls_key_requires_cert #140579476657920:error:1417A0C1:SSL routines:tls_post_process_client_hello:no shared cipher assert_raises EM::BadParams do - client_server client: {private_key_file: "#{__dir__}/client.key"} + client_server client: { private_key_file: PRIVATE_KEY_FILE } end end diff --git a/tests/test_ssl_inline_cert.rb b/tests/test_ssl_inline_cert.rb index d258181f6..aebe3b46b 100644 --- a/tests/test_ssl_inline_cert.rb +++ b/tests/test_ssl_inline_cert.rb @@ -7,31 +7,24 @@ class TestSSLInlineCert < Test::Unit::TestCase require_relative 'em_ssl_handlers' include EMSSLHandlers - CERT_FILE="#{__dir__}/client.crt" - PRIVATE_KEY_FILE="#{__dir__}/client.key" - ENCODED_KEY_FILE="#{__dir__}/encoded_client.key" - - CERT = File.read CERT_FILE - PRIVATE_KEY = File.read PRIVATE_KEY_FILE - ENCODED_KEY = File.read ENCODED_KEY_FILE - - ENCODED_KEY_PASS = 'nicercat' - + # changing two bytes, just in case one is a newline. + # using rot13.5.1, aka rot32 (13*2 + 5 + 1). + BAD_KEY = PRIVATE_KEY_PEM.dup.tap {|bad_key| + bad_key[100,2] = bad_key[100,2] + .tr("A-Za-z0-9/+", "N-ZA-Mn-za-m5-90-4+/") + }.freeze + def test_proper_key_required_for_client # an assert in ssl.ccp code make this fail # with no way of catching the error omit_if(rbx?) - bad_key=PRIVATE_KEY.dup - assert(bad_key[100]!=4) - bad_key[100]='4' - server = { verify_peer: true, ssl_verify_result: true } assert_raises EM::InvalidPrivateKey do client_server Client, Server, - client: { private_key: bad_key, - cert: CERT }, + client: { private_key: BAD_KEY, + cert: CERT_PEM }, server: server end refute Client.handshake_completed? @@ -42,12 +35,8 @@ def test_proper_key_required_for_server # with no way of catching the error omit_if(rbx?) - bad_key=PRIVATE_KEY.dup - assert(bad_key[100]!=4) - bad_key[100]='4' - server = { verify_peer: true, ssl_verify_result: true, - private_key: bad_key, cert: CERT } + private_key: BAD_KEY, cert: CERT_PEM } assert_raises EM::InvalidPrivateKey do client_server Client, Server, @@ -60,8 +49,8 @@ def test_accept_client_key_inline_cert_inline omit_if(rbx?) server = { verify_peer: true, ssl_verify_result: true } - client = { private_key: PRIVATE_KEY, - cert: CERT } + client = { private_key: PRIVATE_KEY_PEM, + cert: CERT_PEM } client_server Client, Server, client: client, @@ -69,15 +58,15 @@ def test_accept_client_key_inline_cert_inline assert Client.handshake_completed? assert Server.handshake_completed? - assert_equal CERT, Server.cert + assert_equal CERT_PEM, Server.cert end def test_accept_server_key_inline_cert_inline omit_if(rbx?) client = { verify_peer: true, ssl_verify_result: true } - server = { private_key: PRIVATE_KEY, - cert: CERT } + server = { private_key: PRIVATE_KEY_PEM, + cert: CERT_PEM } client_server Client, Server, client: client, @@ -85,16 +74,16 @@ def test_accept_server_key_inline_cert_inline assert Client.handshake_completed? assert Server.handshake_completed? - assert_equal CERT, Client.cert + assert_equal CERT_PEM, Client.cert end def test_accept_client_encoded_key_inline_cert_inlince omit_if(rbx?) server = { verify_peer: true, ssl_verify_result: true } - client = { private_key: ENCODED_KEY, + client = { private_key: ENCODED_KEY_PEM, private_key_pass: ENCODED_KEY_PASS, - cert: CERT } + cert: CERT_PEM } client_server Client, Server, client: client, @@ -102,16 +91,16 @@ def test_accept_client_encoded_key_inline_cert_inlince assert Client.handshake_completed? assert Server.handshake_completed? - assert_equal CERT, Server.cert + assert_equal CERT_PEM, Server.cert end def test_accept_server_encoded_key_inline_cert_inlince omit_if(rbx?) client = { verify_peer: true, ssl_verify_result: true } - server = { private_key: ENCODED_KEY, + server = { private_key: ENCODED_KEY_PEM, private_key_pass: ENCODED_KEY_PASS, - cert: CERT } + cert: CERT_PEM } client_server Client, Server, client: client, @@ -119,7 +108,7 @@ def test_accept_server_encoded_key_inline_cert_inlince assert Client.handshake_completed? assert Server.handshake_completed? - assert_equal CERT, Client.cert + assert_equal CERT_PEM, Client.cert end def test_accept_client_key_from_file_cert_inline @@ -127,7 +116,7 @@ def test_accept_client_key_from_file_cert_inline server = { verify_peer: true, ssl_verify_result: true } client = { private_key_file: PRIVATE_KEY_FILE, - cert: CERT } + cert: CERT_PEM } client_server Client, Server, client: client, @@ -135,7 +124,7 @@ def test_accept_client_key_from_file_cert_inline assert Client.handshake_completed? assert Server.handshake_completed? - assert_equal CERT, Server.cert + assert_equal CERT_PEM, Server.cert end def test_accept_server_key_from_file_cert_inline @@ -143,7 +132,7 @@ def test_accept_server_key_from_file_cert_inline client = { verify_peer: true, ssl_verify_result: true } server = { private_key_file: PRIVATE_KEY_FILE, - cert: CERT } + cert: CERT_PEM } client_server Client, Server, client: client, @@ -151,14 +140,14 @@ def test_accept_server_key_from_file_cert_inline assert Client.handshake_completed? assert Server.handshake_completed? - assert_equal CERT, Client.cert + assert_equal CERT_PEM, Client.cert end def test_accept_client_key_inline_cert_from_file omit_if(rbx?) server = { verify_peer: true, ssl_verify_result: true } - client = { private_key: PRIVATE_KEY, + client = { private_key: PRIVATE_KEY_PEM, cert_chain_file: CERT_FILE } client_server Client, Server, @@ -167,14 +156,14 @@ def test_accept_client_key_inline_cert_from_file assert Client.handshake_completed? assert Server.handshake_completed? - assert_equal CERT, Server.cert + assert_equal CERT_PEM, Server.cert end def test_accept_server_key_inline_cert_from_file omit_if(rbx?) client = { verify_peer: true, ssl_verify_result: true } - server = { private_key: PRIVATE_KEY, + server = { private_key: PRIVATE_KEY_PEM, cert_chain_file: CERT_FILE } client_server Client, Server, @@ -183,14 +172,14 @@ def test_accept_server_key_inline_cert_from_file assert Client.handshake_completed? assert Server.handshake_completed? - assert_equal CERT, Client.cert + assert_equal CERT_PEM, Client.cert end def test_accept_client_encoded_key_inline_cert_from_file omit_if(rbx?) server = { verify_peer: true, ssl_verify_result: true } - client = { private_key: ENCODED_KEY, + client = { private_key: ENCODED_KEY_PEM, private_key_pass: ENCODED_KEY_PASS, cert_chain_file: CERT_FILE } @@ -200,14 +189,14 @@ def test_accept_client_encoded_key_inline_cert_from_file assert Client.handshake_completed? assert Server.handshake_completed? - assert_equal CERT, Server.cert + assert_equal CERT_PEM, Server.cert end def test_accept_server_encoded_key_inline_cert_from_file omit_if(rbx?) client = { verify_peer: true, ssl_verify_result: true} - server = { private_key: ENCODED_KEY, + server = { private_key: ENCODED_KEY_PEM, private_key_pass: ENCODED_KEY_PASS, cert_chain_file: CERT_FILE } @@ -217,6 +206,6 @@ def test_accept_server_encoded_key_inline_cert_from_file assert Client.handshake_completed? assert Server.handshake_completed? - assert_equal CERT, Client.cert + assert_equal CERT_PEM, Client.cert end end if EM.ssl? diff --git a/tests/test_ssl_verify.rb b/tests/test_ssl_verify.rb index fa5604873..e9643cbc4 100644 --- a/tests/test_ssl_verify.rb +++ b/tests/test_ssl_verify.rb @@ -7,14 +7,20 @@ class TestSSLVerify < Test::Unit::TestCase require_relative 'em_ssl_handlers' include EMSSLHandlers - CERT_FROM_FILE = File.read "#{__dir__}/client.crt" + CERT_CONFIG = { + # ca_file: "#{CERTS_DIR}/eventmachine-ca.crt", + private_key_file: PRIVATE_KEY_FILE, + cert_chain_file: "#{CERTS_DIR}/em-localhost.crt", + } + + ENCODED_CERT_CONFIG = { + # ca_file: "#{CERTS_DIR}/eventmachine-ca.crt", + private_key_pass: ENCODED_KEY_PASS, + private_key_file: ENCODED_KEY_FILE, + cert_chain_file: "#{CERTS_DIR}/em-localhost.crt", + } - CERT_CONFIG = { private_key_file: "#{__dir__}/client.key", - cert_chain_file: "#{__dir__}/client.crt" } - ENCODED_CERT_CONFIG = { private_key_file: "#{__dir__}/encoded_client.key", - private_key_pass: 'nicercat', - cert_chain_file: "#{__dir__}/client.crt" } def test_fail_no_peer_cert omit_if(rbx?) @@ -36,7 +42,7 @@ def test_accept_server client_server Client, Server, client: CERT_CONFIG, server: server - assert_equal CERT_FROM_FILE, Server.cert + assert_equal CERT_PEM, Server.cert assert Client.handshake_completed? assert Server.handshake_completed? end @@ -49,7 +55,7 @@ def test_accept_client client_server Client, Server, server: CERT_CONFIG, client: client - assert_equal CERT_FROM_FILE, Client.cert + assert_equal CERT_PEM, Client.cert assert Client.handshake_completed? assert Server.handshake_completed? end @@ -64,7 +70,7 @@ def test_encoded_accept_server assert Client.handshake_completed? assert Server.handshake_completed? - assert_equal CERT_FROM_FILE, Server.cert + assert_equal CERT_PEM, Server.cert end def test_encoded_accept_client @@ -77,7 +83,7 @@ def test_encoded_accept_client assert Client.handshake_completed? assert Server.handshake_completed? - assert_equal CERT_FROM_FILE, Client.cert + assert_equal CERT_PEM, Client.cert end def test_deny_server @@ -88,7 +94,7 @@ def test_deny_server client_server Client, Server, client: CERT_CONFIG, server: server - assert_equal CERT_FROM_FILE, Server.cert + assert_equal CERT_PEM, Server.cert refute Client.handshake_completed? unless "TLSv1.3" == Client.cipher_protocol refute Server.handshake_completed? end @@ -103,6 +109,6 @@ def test_deny_client refute Client.handshake_completed? unless "TLSv1.3" == Client.cipher_protocol refute Server.handshake_completed? - assert_equal CERT_FROM_FILE, Client.cert + assert_equal CERT_PEM, Client.cert end end if EM.ssl? From 3980847f4463bcaf80c06f53e95b7eb1dddd2404 Mon Sep 17 00:00:00 2001 From: Antonio Terceiro Date: Thu, 5 Sep 2024 12:10:49 -0300 Subject: [PATCH 294/343] Fix usage of Process::Status on ruby3.3 (#988) --- ext/extconf.rb | 1 + ext/rubymain.cpp | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/ext/extconf.rb b/ext/extconf.rb index ce83c0028..16b0b5d86 100644 --- a/ext/extconf.rb +++ b/ext/extconf.rb @@ -140,6 +140,7 @@ def find_openssl_library # Add for changes to Process::Status in Ruby 3 add_define("IS_RUBY_3_OR_LATER") if RUBY_VERSION > "3.0" +add_define("IS_RUBY_3_3_OR_LATER") if RUBY_VERSION > "3.3" # Adjust number of file descriptors (FD) on Windows diff --git a/ext/rubymain.cpp b/ext/rubymain.cpp index 37c944bfa..356ee1e73 100644 --- a/ext/rubymain.cpp +++ b/ext/rubymain.cpp @@ -563,7 +563,13 @@ static VALUE t_get_subprocess_status (VALUE self UNUSED, VALUE signature) if (evma_get_subprocess_status (NUM2BSIG (signature), &status)) { if (evma_get_subprocess_pid (NUM2BSIG (signature), &pid)) { -#ifdef IS_RUBY_3_OR_LATER +#if defined(IS_RUBY_3_3_OR_LATER) + proc_status = rb_obj_alloc(rb_cProcessStatus); + struct rb_process_status *data = NULL; + data = (rb_process_status*)RTYPEDDATA_GET_DATA(proc_status); + data->pid = pid; + data->status = status; +#elif defined(IS_RUBY_3_OR_LATER) struct rb_process_status *data = NULL; /* Defined to match static definition from MRI Ruby 3.0 process.c From ecba994cd55ee21a74fc215eb876f38465265e6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20K=C3=BCthe?= Date: Thu, 5 Sep 2024 17:13:18 +0200 Subject: [PATCH 295/343] Set TLS params: Don't use deprecated functions anymore (#979) * ecdh callback is deprecated The callback is deprecated. This does not work with recent versions of OpenSSL. Use #ecdh_curves= instead. see https://ruby-doc.org/stdlib-2.7.0/libdoc/openssl/rdoc/OpenSSL/SSL/SSLContext.html callback is removed in 3.0.0 * tmp_dh_callback is deprecated in ruby 3 --- lib/em/pure_ruby.rb | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/lib/em/pure_ruby.rb b/lib/em/pure_ruby.rb index 26ee530f4..21236c345 100644 --- a/lib/em/pure_ruby.rb +++ b/lib/em/pure_ruby.rb @@ -316,22 +316,18 @@ def start_tls signature end ctx.ciphers = tls_parms[:cipher_list] if tls_parms[:cipher_list] if selectable.is_server - ctx.tmp_dh_callback = Proc.new do |_, _, key_length| - if tls_parms[:dhparam] - OpenSSL::PKey::DH.new(tls_parms[:dhparam]) + ctx.tmp_dh = if tls_parms[:dhparam] + OpenSSL::PKey::DH.new(tls_parms[:dhparam]) + else + case key_length + when 1024 then DefaultDHKey1024 + when 2048 then DefaultDHKey2048 else - case key_length - when 1024 then DefaultDHKey1024 - when 2048 then DefaultDHKey2048 - else - nil - end + nil end end - if tls_parms[:ecdh_curve] && ctx.respond_to?(:tmp_ecdh_callback) - ctx.tmp_ecdh_callback = Proc.new do - OpenSSL::PKey::EC.new(tls_parms[:ecdh_curve]) - end + if tls_parms[:ecdh_curve] + ctx.ecdh_curves = tls_parms[:ecdh_curve] end end ssl_io = OpenSSL::SSL::SSLSocket.new(selectable, ctx) From cf8329fe1d97b5fde7c1f860ff0cb0f39341159c Mon Sep 17 00:00:00 2001 From: "nicholas a. evans" Date: Thu, 5 Sep 2024 11:45:13 -0400 Subject: [PATCH 296/343] Fix pure ruby Reactor#install_oneshot_timer (#991) Because @timers is a SortedSet, which is implemented using rbtree, it can't be modified during iteration. Some work had already been done to fix deletion of timers during iteration, but this solves the addition of new oneshot timers. --- lib/em/pure_ruby.rb | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/em/pure_ruby.rb b/lib/em/pure_ruby.rb index 21236c345..633776940 100644 --- a/lib/em/pure_ruby.rb +++ b/lib/em/pure_ruby.rb @@ -518,9 +518,7 @@ def get_timer_count def install_oneshot_timer interval uuid = UuidGenerator::generate - #@timers << [Time.now + interval, uuid] - #@timers.sort! {|a,b| a.first <=> b.first} - @timers.add([Time.now + interval, uuid]) + (@timers_to_add || @timers) << [Time.now + interval, uuid] uuid end @@ -531,7 +529,9 @@ def initialize_for_run @running = false @stop_scheduled = false @selectables ||= {}; @selectables.clear - @timers = SortedSet.new # [] + @timers = SortedSet.new + @timers_to_add = SortedSet.new + @timers_iterating = false # only set while iterating @timers set_timer_quantum(0.1) @current_loop_time = Time.now @next_heartbeat = @current_loop_time + HeartbeatInterval @@ -574,21 +574,21 @@ def run def run_timers timers_to_delete = [] + @timers_iterating = true @timers.each {|t| if t.first <= @current_loop_time - #@timers.delete t timers_to_delete << t EventMachine::event_callback "", TimerFired, t.last else break end } + ensure timers_to_delete.map{|c| @timers.delete c} timers_to_delete = nil - #while @timers.length > 0 and @timers.first.first <= now - # t = @timers.shift - # EventMachine::event_callback "", TimerFired, t.last - #end + @timers_to_add.each do |t| @timers << t end + @timers_to_add.clear + @timers_iterating = false end def run_heartbeats From 5a9f705ee95ba1058f1255fce5769d0f594e9dc9 Mon Sep 17 00:00:00 2001 From: "nicholas a. evans" Date: Thu, 5 Sep 2024 11:47:12 -0400 Subject: [PATCH 297/343] Fix missing OpenSSL constants in pure ruby impl (#992) All of these constants are needed to even start running the tests. Apparently, there is no good way to test for support of protocol versions other than trying each of them. That may be okay for tests (see the openssl gem's tests for an example), but it's too much for load-time constants. So I'm cheating by simply checking the version numbers. I think SSL2 may have been disabled by default prior to 1.1.0, but I wasn't able to find that in any Changelogs or documentation. SSL3 code is still in the OpenSSL repo, but it's been disabled by default since 1.1.0. --- lib/em/pure_ruby.rb | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/lib/em/pure_ruby.rb b/lib/em/pure_ruby.rb index 633776940..5db5cf288 100644 --- a/lib/em/pure_ruby.rb +++ b/lib/em/pure_ruby.rb @@ -63,6 +63,29 @@ class InvalidPrivateKey < RuntimeError; end else SSLConnectionWaitWritable = IO::WaitWritable end + + if defined?(OpenSSL::OPENSSL_VERSION) + OPENSSL_VERSION = OpenSSL::OPENSSL_VERSION + else + OPENSSL_VERSION = "not loaded" + end + if defined?(OpenSSL::OPENSSL_LIBRARY_VERSION) + OPENSSL_LIBRARY_VERSION = OpenSSL::OPENSSL_LIBRARY_VERSION + else + OPENSSL_LIBRARY_VERSION = "not loaded" + end + + openssl_version_gt = ->(maj, min, pat) { + if defined?(OpenSSL::OPENSSL_VERSION_NUMBER) + OpenSSL::OPENSSL_VERSION_NUMBER >= (maj << 28) | (min << 20) | (pat << 12) + else + false + end + } + # OpenSSL 1.1.0 removed support for SSLv2 + OPENSSL_NO_SSL2 = openssl_version_gt.(1, 1, 0) + # OpenSSL 1.1.0 disabled support for SSLv3 (by default) + OPENSSL_NO_SSL3 = openssl_version_gt.(1, 1, 0) end module EventMachine From 529851321a773910c7cbeaecb271fa43f88e3d12 Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Thu, 5 Sep 2024 09:07:06 -0700 Subject: [PATCH 298/343] Switch CI badge from Travis to GitHub Actions --- .travis.yml | 65 ---------------------------------------------------- README.md | 2 +- appveyor.yml | 27 ---------------------- 3 files changed, 1 insertion(+), 93 deletions(-) delete mode 100644 .travis.yml delete mode 100644 appveyor.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 73a99f57d..000000000 --- a/.travis.yml +++ /dev/null @@ -1,65 +0,0 @@ -dist: xenial -language: ruby -cache: bundler -bundler_args: --without documentation - -before_install: - # rubygems 2.7.8 and greater include bundler - # remove 2.7.0 code when Travis removes rubygems 2.7.8 from ruby-head build - - | - rv="$(ruby -e 'STDOUT.write RUBY_VERSION')"; - if [ "$rv" \< "2.3" ]; then gem update --system 2.7.9 --no-document - elif [ "$rv" \< "2.6" ]; then gem update --system --no-document --conservative - fi - -before_script: - - if [ "$jit" == "yes" ]; then export RUBYOPT=--jit ; fi ; echo RUBYOPT is $RUBYOPT - - bundle exec rake compile - -script: - - bundle exec rake test - -env: - global: - - TESTOPTS="-v --no-show-detail-immediately" - -rvm: - - 2.7 - - 2.6 - - 2.5 - - 2.4 - - 2.3 - - 2.2 - - 2.1 - - 2.0.0 - - ruby-head - -matrix: - fast_finish: true - allow_failures: - - rvm: ruby-head - - rvm: ruby-head - env: jit=yes -# - rvm: jruby-9.2.7.0 -# - rvm: jruby-head - include: - - rvm: 2.3 - dist: trusty - env: OS="trusty 14.04" - - rvm: 2.6 - dist: bionic - env: OS="bionic 18.04" - - rvm: 2.6 - os: osx - osx_image: xcode10.3 - env: OS="osx xcode 10.3" - - rvm: 2.6 - os: osx - osx_image: xcode11.3 - env: OS="osx xcode 11.3" - - rvm: 2.6 - env: jit=yes - - rvm: ruby-head - env: jit=yes -# - rvm: jruby-9.2.7.0 -# - rvm: jruby-head \ No newline at end of file diff --git a/README.md b/README.md index d7cac9de9..e7ae84a68 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# About EventMachine [![Build Status](https://travis-ci.org/eventmachine/eventmachine.svg?branch=master)](https://travis-ci.org/eventmachine/eventmachine) [![Code Climate Maintainability](https://api.codeclimate.com/v1/badges/e9b0603462905d5b9118/maintainability)](https://codeclimate.com/github/eventmachine/eventmachine/maintainability) +# About EventMachine [![Build Status](https://github.com/github/docs/actions/workflows/workflow.yml/badge.svg)](https://github.com/eventmachine/eventmachine/actions) [![Code Climate Maintainability](https://api.codeclimate.com/v1/badges/e9b0603462905d5b9118/maintainability)](https://codeclimate.com/github/eventmachine/eventmachine/maintainability) ## What is EventMachine ## diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index 9487a29d8..000000000 --- a/appveyor.yml +++ /dev/null @@ -1,27 +0,0 @@ ---- -image: -- Visual Studio 2019 - -build: off -deploy: off - -install: - # download shared script files - - ps: >- - if ( !(Test-Path -Path ./shared -PathType Container) ) { - $uri = 'https://ci.appveyor.com/api/projects/MSP-Greg/av-gem-build-test/artifacts/shared.7z' - $7z = 'C:\Program Files\7-Zip\7z.exe' - $fn = "$env:TEMP\shared.7z" - (New-Object System.Net.WebClient).DownloadFile($uri, $fn) - &$7z x $fn -owin_gem_test 1> $null - Remove-Item -LiteralPath $fn -Force - Write-Host "Downloaded shared files" -ForegroundColor Yellow - } - -build_script: - - ps: .\win_gem_test\eventmachine.ps1 $env:gem_bits - -environment: - matrix: - - gem_bits: 64 - - gem_bits: 32 From dba79963430339f62c37efaa68e8ca80d7c153ed Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Thu, 5 Sep 2024 09:14:40 -0700 Subject: [PATCH 299/343] Remove Ruby 2.2 from CI matrix --- .github/workflows/workflow.yml | 26 ++++++++++---------------- Gemfile | 2 +- README.md | 2 +- 3 files changed, 12 insertions(+), 18 deletions(-) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 2326e6289..2a4127660 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -12,31 +12,25 @@ jobs: fail-fast: false matrix: os: [ ubuntu-24.04, ubuntu-22.04, ubuntu-20.04, windows-2019, macos-latest ] - ruby: [ '2.2', '2.3', '2.4', '2.5', '2.6', '2.7', '3.0', '3.1', '3.2', '3.3', head ] + ruby: [ '2.3', '2.4', '2.5', '2.6', '2.7', '3.0', '3.1', '3.2', '3.3', head ] include: - # GH-988 must be merged to support ruby >= 3.3 - # - { os: windows-2019, ruby: mingw } + # GH-988 added support for ruby >= 3.3 + - { os: windows-2019, ruby: mingw } # CRuby < 2.6 does not support macos-arm64, so these use amd64 - - { os: macos-13, ruby: "2.2" } - - { os: macos-13, ruby: "2.3" } - - { os: macos-13, ruby: "2.4" } - - { os: macos-13, ruby: "2.5" } + - { os: macos-13, ruby: '2.3' } + - { os: macos-13, ruby: '2.4' } + - { os: macos-13, ruby: '2.5' } exclude: - { os: macos-latest, ruby: head } - { os: windows-2019, ruby: head } - # GH-988 must be merged to support ruby >= 3.3 - - { ruby: 3.3 } - - { ruby: head } - # CRuby < 2.6 does not support macos-arm64, so these use amd64 - - { os: macos-latest, ruby: "2.2" } - - { os: macos-latest, ruby: "2.3" } - - { os: macos-latest, ruby: "2.4" } - - { os: macos-latest, ruby: "2.5" } + - { os: macos-latest, ruby: '2.3' } + - { os: macos-latest, ruby: '2.4' } + - { os: macos-latest, ruby: '2.5' } # Avoids the following error: # Bundler 2 requires Ruby 2.3+, using Bundler 1 on Ruby <= 2.2 @@ -47,7 +41,7 @@ jobs: # See https://github.com/ruby/setup-ruby/issues/496 # # This works fine on ubuntu 20.04 and 24.04. - - { os: ubuntu-22.04, ruby: '2.2' } + # - { os: ubuntu-22.04, ruby: '2.2' } steps: - name: repo checkout diff --git a/Gemfile b/Gemfile index 170d211b3..06c34daa1 100644 --- a/Gemfile +++ b/Gemfile @@ -5,7 +5,7 @@ gemspec gem 'rake' install_if -> { RUBY_VERSION > '3.1' } do - gem "net-smtp" + gem 'net-smtp' end group :documentation do diff --git a/README.md b/README.md index e7ae84a68..0d2f49b6d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# About EventMachine [![Build Status](https://github.com/github/docs/actions/workflows/workflow.yml/badge.svg)](https://github.com/eventmachine/eventmachine/actions) [![Code Climate Maintainability](https://api.codeclimate.com/v1/badges/e9b0603462905d5b9118/maintainability)](https://codeclimate.com/github/eventmachine/eventmachine/maintainability) +# About EventMachine [![Build Status](https://github.com/eventmachine/eventmachine/actions/workflows/workflow.yml/badge.svg)](https://github.com/eventmachine/eventmachine/actions) [![Code Climate Maintainability](https://api.codeclimate.com/v1/badges/e9b0603462905d5b9118/maintainability)](https://codeclimate.com/github/eventmachine/eventmachine/maintainability) ## What is EventMachine ## From 3f0daf93c96a0b2fb2d97352f5c75382219e1927 Mon Sep 17 00:00:00 2001 From: "nicholas a. evans" Date: Thu, 5 Sep 2024 13:00:18 -0400 Subject: [PATCH 300/343] Add .editorconfig file for rb, cpp, h files (#959) Since the project uses tabs in .cpp and .h files, it seems useful to add an .editorconfig for them. --- .editorconfig | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..019dfe594 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,16 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true + +[*.{cpp,h}] +indent_style = tab +indent_size = 4 +trim_trailing_whitespace = true + +[*.rb] +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true From f0d225110c3341813d4e0517a42d77264e4f5731 Mon Sep 17 00:00:00 2001 From: nick evans Date: Thu, 29 Aug 2024 10:23:30 -0400 Subject: [PATCH 301/343] Fix pure ruby start_tls cert, key, and chain_certs * Fix "cert" vs "cert_chain" variable name * Only load DefaultCertificate (and key) for servers. * Use OpenSSL::X509::Certificate.load (instead of .new), which can handle extra chain certs. --- lib/em/pure_ruby.rb | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/lib/em/pure_ruby.rb b/lib/em/pure_ruby.rb index 107c8389b..89a8b0fbe 100644 --- a/lib/em/pure_ruby.rb +++ b/lib/em/pure_ruby.rb @@ -315,17 +315,21 @@ def start_tls signature tls_parms = @tls_parms[signature] ctx = OpenSSL::SSL::SSLContext.new ctx.options = tls_parms[:ssl_options] - ctx.cert = DefaultCertificate.cert - ctx.key = DefaultCertificate.key ctx.cert_store = OpenSSL::X509::Store.new ctx.cert_store.set_default_paths - ctx.cert = OpenSSL::X509::Certificate.new(tls_parms[:cert_chain]) if tls_parms[:cert_chain] - if tls_parms[:priv_key_pass]!=nil - ctx.key = OpenSSL::PKey::RSA.new(tls_parms[:priv_key],tls_parms[:priv_key_pass]) if tls_parms[:priv_key] - else - ctx.key = OpenSSL::PKey::RSA.new(tls_parms[:priv_key]) if tls_parms[:priv_key] - end - verify_mode = OpenSSL::SSL::VERIFY_NONE + cert, *extra_chain_cert = + if (cert_chain = tls_parms[:cert_chain]) + OpenSSL::X509::Certificate.load(cert_chain) + elsif selectable.is_server + [DefaultCertificate.cert] + end + key = + if tls_parms[:priv_key] + OpenSSL::PKey::RSA.new(tls_parms[:priv_key], tls_parms[:priv_key_pass]) + elsif selectable.is_server + DefaultCertificate.key + end + ctx.cert, ctx.key, ctx.extra_chain_cert = cert, key, extra_chain_cert if tls_parms[:verify_peer] verify_mode |= OpenSSL::SSL::VERIFY_PEER end From 17091b2c90d9de09f7fca3c458c50dc6ed43d4aa Mon Sep 17 00:00:00 2001 From: nick evans Date: Fri, 30 Aug 2024 10:37:29 -0400 Subject: [PATCH 302/343] Use File.binread for pure ruby cert and key files This allows DER files in addition to PEM files --- lib/em/pure_ruby.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/em/pure_ruby.rb b/lib/em/pure_ruby.rb index 89a8b0fbe..4bc8d88c8 100644 --- a/lib/em/pure_ruby.rb +++ b/lib/em/pure_ruby.rb @@ -299,10 +299,10 @@ def set_tls_parms signature, priv_key_path, priv_key, priv_key_pass, cert_chain_ :fail_if_no_peer_cert => fail_if_no_peer_cert, :ssl_options => ssl_options } - @tls_parms[signature][:priv_key] = File.read(priv_key_path) if tls_parm_set?(priv_key_path) + @tls_parms[signature][:priv_key] = File.binread(priv_key_path) if tls_parm_set?(priv_key_path) @tls_parms[signature][:priv_key] = priv_key if tls_parm_set?(priv_key) @tls_parms[signature][:priv_key_pass] = priv_key_pass if tls_parm_set?(priv_key_pass) - @tls_parms[signature][:cert_chain] = File.read(cert_chain_path) if tls_parm_set?(cert_chain_path) + @tls_parms[signature][:cert_chain] = File.binread(cert_chain_path) if tls_parm_set?(cert_chain_path) @tls_parms[signature][:cert_chain] = cert if tls_parm_set?(cert) @tls_parms[signature][:sni_hostname] = sni_hostname if tls_parm_set?(sni_hostname) @tls_parms[signature][:cipher_list] = cipher_list.gsub(/,\s*/, ':') if tls_parm_set?(cipher_list) From 720d14bf030f1ef58927dd73983f3139337f0033 Mon Sep 17 00:00:00 2001 From: nick evans Date: Fri, 30 Aug 2024 18:02:59 -0400 Subject: [PATCH 303/343] Fix pure ruby cert_chain for ruby 2.5 `OpenSSL::X509::Certificate.load` was added in v3.0 of the gem, which was released with ruby 2.6. --- lib/em/pure_ruby.rb | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/lib/em/pure_ruby.rb b/lib/em/pure_ruby.rb index 4bc8d88c8..0cd2256d7 100644 --- a/lib/em/pure_ruby.rb +++ b/lib/em/pure_ruby.rb @@ -310,6 +310,13 @@ def set_tls_parms signature, priv_key_path, priv_key, priv_key_pass, cert_chain_ @tls_parms[signature][:ecdh_curve] = ecdh_curve if tls_parm_set?(ecdh_curve) end + PEM_CERTIFICATE = / + ^-----BEGIN CERTIFICATE-----\n + .*?\n + -----END CERTIFICATE-----\n + /mx + private_constant :PEM_CERTIFICATE + def start_tls signature selectable = Reactor.instance.get_selectable(signature) or raise "unknown io selectable for start_tls" tls_parms = @tls_parms[signature] @@ -319,7 +326,15 @@ def start_tls signature ctx.cert_store.set_default_paths cert, *extra_chain_cert = if (cert_chain = tls_parms[:cert_chain]) - OpenSSL::X509::Certificate.load(cert_chain) + if OpenSSL::X509::Certificate.respond_to?(:load) + OpenSSL::X509::Certificate.load(cert_chain) + elsif cert_chain[PEM_CERTIFICATE] + # compatibility with openssl gem < 3.0 (ruby < 2.6) + cert_chain.scan(PEM_CERTIFICATE) + .map {|pem| OpenSSL::X509::Certificate.new(pem) } + else + [OpenSSL::X509::Certificate.new(cert_chain)] + end elsif selectable.is_server [DefaultCertificate.cert] end From 546e4d7ba45b749b2143489449e27bc2a40ad86b Mon Sep 17 00:00:00 2001 From: nick evans Date: Thu, 29 Aug 2024 13:56:14 -0400 Subject: [PATCH 304/343] Fix missing TLSv1.3 support for pure ruby impl Most of the necessary changes were made in #974. --- lib/em/pure_ruby.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/em/pure_ruby.rb b/lib/em/pure_ruby.rb index 0cd2256d7..cec1f543a 100644 --- a/lib/em/pure_ruby.rb +++ b/lib/em/pure_ruby.rb @@ -293,6 +293,7 @@ def set_tls_parms signature, priv_key_path, priv_key, priv_key_pass, cert_chain_ ssl_options |= OpenSSL::SSL::OP_NO_TLSv1 if defined?(OpenSSL::SSL::OP_NO_TLSv1) && EM_PROTO_TLSv1 & bitmask == 0 ssl_options |= OpenSSL::SSL::OP_NO_TLSv1_1 if defined?(OpenSSL::SSL::OP_NO_TLSv1_1) && EM_PROTO_TLSv1_1 & bitmask == 0 ssl_options |= OpenSSL::SSL::OP_NO_TLSv1_2 if defined?(OpenSSL::SSL::OP_NO_TLSv1_2) && EM_PROTO_TLSv1_2 & bitmask == 0 + ssl_options |= OpenSSL::SSL::OP_NO_TLSv1_3 if defined?(OpenSSL::SSL::OP_NO_TLSv1_3) && EM_PROTO_TLSv1_3 & bitmask == 0 @tls_parms ||= {} @tls_parms[signature] = { :verify_peer => verify_peer, From 1ee412e1cb550ee8991111f9a068775dc970821c Mon Sep 17 00:00:00 2001 From: nick evans Date: Fri, 30 Aug 2024 11:48:11 -0400 Subject: [PATCH 305/343] Fix protocol version options in pure ruby impl Before this fix, versions were being added but not removed from the default options. This brings the ruby implementation of `start_tls(ssl_version:)` to parity with the cpp implementation. Even better would be to use something like the openssl gem's min_version, max_version, and set_minmax_proto_version. This style of setting versions allows gaps, and it has been deprecated by both the OpenSSL library and by the openssl ruby gem. --- lib/em/pure_ruby.rb | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/lib/em/pure_ruby.rb b/lib/em/pure_ruby.rb index cec1f543a..a7c37b615 100644 --- a/lib/em/pure_ruby.rb +++ b/lib/em/pure_ruby.rb @@ -288,12 +288,30 @@ def tls_parm_set?(parm) def set_tls_parms signature, priv_key_path, priv_key, priv_key_pass, cert_chain_path, cert, verify_peer, fail_if_no_peer_cert, sni_hostname, cipher_list, ecdh_curve, dhparam, protocols_bitmask bitmask = protocols_bitmask ssl_options = OpenSSL::SSL::OP_ALL - ssl_options |= OpenSSL::SSL::OP_NO_SSLv2 if defined?(OpenSSL::SSL::OP_NO_SSLv2) && EM_PROTO_SSLv2 & bitmask == 0 - ssl_options |= OpenSSL::SSL::OP_NO_SSLv3 if defined?(OpenSSL::SSL::OP_NO_SSLv3) && EM_PROTO_SSLv3 & bitmask == 0 - ssl_options |= OpenSSL::SSL::OP_NO_TLSv1 if defined?(OpenSSL::SSL::OP_NO_TLSv1) && EM_PROTO_TLSv1 & bitmask == 0 - ssl_options |= OpenSSL::SSL::OP_NO_TLSv1_1 if defined?(OpenSSL::SSL::OP_NO_TLSv1_1) && EM_PROTO_TLSv1_1 & bitmask == 0 - ssl_options |= OpenSSL::SSL::OP_NO_TLSv1_2 if defined?(OpenSSL::SSL::OP_NO_TLSv1_2) && EM_PROTO_TLSv1_2 & bitmask == 0 - ssl_options |= OpenSSL::SSL::OP_NO_TLSv1_3 if defined?(OpenSSL::SSL::OP_NO_TLSv1_3) && EM_PROTO_TLSv1_3 & bitmask == 0 + if defined?(OpenSSL::SSL::OP_NO_SSLv2) + ssl_options &= ~OpenSSL::SSL::OP_NO_SSLv2 + ssl_options |= OpenSSL::SSL::OP_NO_SSLv2 if EM_PROTO_SSLv2 & bitmask == 0 + end + if defined?(OpenSSL::SSL::OP_NO_SSLv3) + ssl_options &= ~OpenSSL::SSL::OP_NO_SSLv3 + ssl_options |= OpenSSL::SSL::OP_NO_SSLv3 if EM_PROTO_SSLv3 & bitmask == 0 + end + if defined?(OpenSSL::SSL::OP_NO_TLSv1) + ssl_options &= ~OpenSSL::SSL::OP_NO_TLSv1 + ssl_options |= OpenSSL::SSL::OP_NO_TLSv1 if EM_PROTO_TLSv1 & bitmask == 0 + end + if defined?(OpenSSL::SSL::OP_NO_TLSv1_1) + ssl_options &= ~OpenSSL::SSL::OP_NO_TLSv1_1 + ssl_options |= OpenSSL::SSL::OP_NO_TLSv1_1 if EM_PROTO_TLSv1_1 & bitmask == 0 + end + if defined?(OpenSSL::SSL::OP_NO_TLSv1_2) + ssl_options &= ~OpenSSL::SSL::OP_NO_TLSv1_2 + ssl_options |= OpenSSL::SSL::OP_NO_TLSv1_2 if EM_PROTO_TLSv1_2 & bitmask == 0 + end + if defined?(OpenSSL::SSL::OP_NO_TLSv1_3) + ssl_options &= ~OpenSSL::SSL::OP_NO_TLSv1_3 + ssl_options |= OpenSSL::SSL::OP_NO_TLSv1_3 if EM_PROTO_TLSv1_3 & bitmask == 0 + end @tls_parms ||= {} @tls_parms[signature] = { :verify_peer => verify_peer, From a4fa0620129b5af69a5b51863e77f77b5ce4473e Mon Sep 17 00:00:00 2001 From: nick evans Date: Thu, 29 Aug 2024 18:15:42 -0400 Subject: [PATCH 306/343] Rename @my_selectable to @eventmachine_selectable If we're going to set ivars on IO objects, let's at least namespace it properly! --- lib/em/pure_ruby.rb | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/lib/em/pure_ruby.rb b/lib/em/pure_ruby.rb index a7c37b615..bd053ea0c 100644 --- a/lib/em/pure_ruby.rb +++ b/lib/em/pure_ruby.rb @@ -741,24 +741,24 @@ def set_timer_quantum interval_in_seconds # @private class IO extend Forwardable - def_delegator :@my_selectable, :close_scheduled? - def_delegator :@my_selectable, :select_for_reading? - def_delegator :@my_selectable, :select_for_writing? - def_delegator :@my_selectable, :eventable_read - def_delegator :@my_selectable, :eventable_write - def_delegator :@my_selectable, :uuid - def_delegator :@my_selectable, :is_server - def_delegator :@my_selectable, :is_server= - def_delegator :@my_selectable, :send_data - def_delegator :@my_selectable, :schedule_close - def_delegator :@my_selectable, :get_peername - def_delegator :@my_selectable, :get_sockname - def_delegator :@my_selectable, :send_datagram - def_delegator :@my_selectable, :get_outbound_data_size - def_delegator :@my_selectable, :set_inactivity_timeout - def_delegator :@my_selectable, :heartbeat - def_delegator :@my_selectable, :io - def_delegator :@my_selectable, :io= + def_delegator :@eventmachine_selectable, :close_scheduled? + def_delegator :@eventmachine_selectable, :select_for_reading? + def_delegator :@eventmachine_selectable, :select_for_writing? + def_delegator :@eventmachine_selectable, :eventable_read + def_delegator :@eventmachine_selectable, :eventable_write + def_delegator :@eventmachine_selectable, :uuid + def_delegator :@eventmachine_selectable, :is_server + def_delegator :@eventmachine_selectable, :is_server= + def_delegator :@eventmachine_selectable, :send_data + def_delegator :@eventmachine_selectable, :schedule_close + def_delegator :@eventmachine_selectable, :get_peername + def_delegator :@eventmachine_selectable, :get_sockname + def_delegator :@eventmachine_selectable, :send_datagram + def_delegator :@eventmachine_selectable, :get_outbound_data_size + def_delegator :@eventmachine_selectable, :set_inactivity_timeout + def_delegator :@eventmachine_selectable, :heartbeat + def_delegator :@eventmachine_selectable, :io + def_delegator :@eventmachine_selectable, :io= end module EventMachine @@ -792,7 +792,7 @@ def initialize io @close_scheduled = false @close_requested = false - se = self; @io.instance_eval { @my_selectable = se } + se = self; @io.instance_eval { @eventmachine_selectable = se } Reactor.instance.add_selectable @io end From 0867216e9d3c2fc6bbd6b375ca33f18bebb7b307 Mon Sep 17 00:00:00 2001 From: nick evans Date: Thu, 29 Aug 2024 18:17:56 -0400 Subject: [PATCH 307/343] Fix pure ruby ssl_handshake_completed When using TLSv1.3, checking SSLSocket#status for /^SSLOK/ would get stuck on "early data". Fortunately, it's okay to re-run accept_nonblock or connect_nonblock as many times as needed, until it returns successfully. That should be a good indicator that the handshake has completed, at least according to the man pages. This changes the implementation to no longer check_handshake_complete during `pending?`, but instead it check every time it calls eventable_read and eventable_write. These methods will only attempt to read from or write to the SSLSocket after the non-blocking handshake method has completed successfully. --- lib/em/pure_ruby.rb | 50 ++++++++++++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/lib/em/pure_ruby.rb b/lib/em/pure_ruby.rb index bd053ea0c..01121259c 100644 --- a/lib/em/pure_ruby.rb +++ b/lib/em/pure_ruby.rb @@ -396,10 +396,7 @@ def start_tls signature if tls_parms[:sni_hostname] ssl_io.hostname = tls_parms[:sni_hostname] if ssl_io.respond_to?(:hostname=) end - begin - selectable.is_server ? ssl_io.accept_nonblock : ssl_io.connect_nonblock - rescue; end - selectable.io = ssl_io + selectable._evma_start_tls(ssl_io) end def get_peer_cert signature @@ -759,6 +756,7 @@ class IO def_delegator :@eventmachine_selectable, :heartbeat def_delegator :@eventmachine_selectable, :io def_delegator :@eventmachine_selectable, :io= + def_delegator :@eventmachine_selectable, :start_tls, :_evma_start_tls end module EventMachine @@ -978,6 +976,7 @@ def heartbeat module EventMachine # @private class EvmaTCPClient < StreamObject + attr_reader :ssl_handshake_state def self.connect bind_addr, bind_port, host, port sd = Socket.new( Socket::AF_INET, Socket::SOCK_STREAM, 0 ) @@ -995,7 +994,7 @@ def self.connect bind_addr, bind_port, host, port def initialize io super @pending = true - @handshake_complete = false + @ssl_handshake_state = nil end def ready? @@ -1006,21 +1005,38 @@ def ready? end end - def handshake_complete? - if !@handshake_complete && io.respond_to?(:state) - if io.state =~ /^SSLOK/ - @handshake_complete = true - EventMachine::event_callback uuid, SslHandshakeCompleted, "" - EventMachine::event_callback uuid, SslVerify, io.peer_cert.to_pem if io.peer_cert - end - else - @handshake_complete = true - end - @handshake_complete + def eventable_read + check_handshake_complete and super + end + + def eventable_write + check_handshake_complete and super + end + + def start_tls(ssl_io) + self.io = ssl_io + @ssl_handshake_state = :init + check_handshake_complete + end + + def check_handshake_complete + return true if ssl_handshake_state.nil? || ssl_handshake_state == :done + is_server ? io.accept_nonblock : io.connect_nonblock + @ssl_handshake_state = :done + EventMachine::event_callback uuid, SslHandshakeCompleted, "" + EventMachine::event_callback uuid, SslVerify, io.peer_cert.to_pem if io.peer_cert + true + rescue SSLConnectionWaitReadable + @ssl_handshake_state = :wait_readable + false + rescue SSLConnectionWaitWritable + @ssl_handshake_state = :wait_writable + false + rescue => error + @ssl_handshake_state = error end def pending? - handshake_complete? if @pending if ready? @pending = false From 02872344ef2539f5e7ea67faf84d213bfc774557 Mon Sep 17 00:00:00 2001 From: nick evans Date: Fri, 30 Aug 2024 12:11:32 -0400 Subject: [PATCH 308/343] Print exceptions in pure ruby check_handshake_complete --- lib/em/pure_ruby.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/em/pure_ruby.rb b/lib/em/pure_ruby.rb index 01121259c..a61919012 100644 --- a/lib/em/pure_ruby.rb +++ b/lib/em/pure_ruby.rb @@ -1032,7 +1032,11 @@ def check_handshake_complete rescue SSLConnectionWaitWritable @ssl_handshake_state = :wait_writable false + rescue OpenSSL::SSL::SSLError => error + warn "SSL Error in EventMachine check_handshake_complete: #{error}" + @ssl_handshake_state = error rescue => error + warn "#{error.class} in EventMachine check_handshake_complete: #{error}" @ssl_handshake_state = error end From 0c0c715f2e61c3187d9efbd85b85cfd2e05bbe31 Mon Sep 17 00:00:00 2001 From: nick evans Date: Fri, 30 Aug 2024 12:02:59 -0400 Subject: [PATCH 309/343] Fix pure ruby verify_mode (parity, not security) This achieves parity with the cpp implementation, by disabling peer verification when verify_mode hasn't been set. This is INSECURE and the behavior should be updated to verify by default, but that should be done for both implementations together (which will require updating all of the relevant tests). --- lib/em/pure_ruby.rb | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/em/pure_ruby.rb b/lib/em/pure_ruby.rb index a61919012..8cfbe0d49 100644 --- a/lib/em/pure_ruby.rb +++ b/lib/em/pure_ruby.rb @@ -365,12 +365,14 @@ def start_tls signature end ctx.cert, ctx.key, ctx.extra_chain_cert = cert, key, extra_chain_cert if tls_parms[:verify_peer] - verify_mode |= OpenSSL::SSL::VERIFY_PEER - end - if tls_parms[:fail_if_no_peer_cert] - verify_mode |= OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT + ctx.verify_mode = + OpenSSL::SSL::VERIFY_PEER | OpenSSL::SSL::VERIFY_CLIENT_ONCE + if tls_parms[:fail_if_no_peer_cert] + ctx.verify_mode |= OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT + end + else + ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE end - ctx.verify_mode = verify_mode ctx.servername_cb = Proc.new do |_, server_name| tls_parms[:server_name] = server_name nil From 80aad9cb36a17cbb89c05e207a59055fabbecae0 Mon Sep 17 00:00:00 2001 From: nick evans Date: Fri, 30 Aug 2024 12:32:40 -0400 Subject: [PATCH 310/343] Fix pure ruby ssl_verify_peer callback The pure ruby version was only calling SslVerify once, after SslHandshakeCompleted, which is out of order and won't allow the connection to see the entire certificate chain. This adds the SSLContext#verify_callback to trigger the `ssl_verify_peer` method, to provide feature parity with the cpp implementation. To properly handle the callback, we need to pass the correct arguments, but that's out of scope for this (a fix for that is in a different PR). --- lib/em/pure_ruby.rb | 5 ++++- lib/eventmachine.rb | 3 ++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/em/pure_ruby.rb b/lib/em/pure_ruby.rb index 8cfbe0d49..108a92f7c 100644 --- a/lib/em/pure_ruby.rb +++ b/lib/em/pure_ruby.rb @@ -370,6 +370,10 @@ def start_tls signature if tls_parms[:fail_if_no_peer_cert] ctx.verify_mode |= OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT end + ctx.verify_callback = ->(preverify_ok, store_ctx) { + current_cert = store_ctx.current_cert.to_pem + EventMachine::event_callback selectable.uuid, SslVerify, current_cert + } else ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE end @@ -1026,7 +1030,6 @@ def check_handshake_complete is_server ? io.accept_nonblock : io.connect_nonblock @ssl_handshake_state = :done EventMachine::event_callback uuid, SslHandshakeCompleted, "" - EventMachine::event_callback uuid, SslVerify, io.peer_cert.to_pem if io.peer_cert true rescue SSLConnectionWaitReadable @ssl_handshake_state = :wait_readable diff --git a/lib/eventmachine.rb b/lib/eventmachine.rb index 2d0221831..6d31639dd 100644 --- a/lib/eventmachine.rb +++ b/lib/eventmachine.rb @@ -1538,7 +1538,8 @@ def self.event_callback conn_binding, opcode, data c.ssl_handshake_completed elsif opcode == SslVerify c = @conns[conn_binding] or raise ConnectionNotBound, "received SslVerify for unknown signature: #{conn_binding}" - c.close_connection if c.ssl_verify_peer(data) == false + result = c.ssl_verify_peer(data) or c.close_connection + result elsif opcode == TimerFired t = @timers.delete( data ) return if t == false # timer cancelled From fd7ff7fd89cd796aef524880044295c393c748d5 Mon Sep 17 00:00:00 2001 From: nick evans Date: Fri, 30 Aug 2024 14:28:43 -0400 Subject: [PATCH 311/343] Make pure ruby exception compatible Wrapping _all_ errors, as the cpp implementation currently does, seems wrong. It would be better to just use the original OpenSSL exception (or whatever class it is). So, to make the tests pass, this only wraps the one error type. --- lib/em/pure_ruby.rb | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/em/pure_ruby.rb b/lib/em/pure_ruby.rb index 108a92f7c..56286d1fe 100644 --- a/lib/em/pure_ruby.rb +++ b/lib/em/pure_ruby.rb @@ -397,6 +397,16 @@ def start_tls signature ctx.ecdh_curves = tls_parms[:ecdh_curve] end end + begin + ctx.freeze + rescue OpenSSL::SSL::SSLError => err + if err.message.include?("SSL_CTX_use_PrivateKey") || + err.message.include?("key values mismatch") + raise InvalidPrivateKey, err.message + end + raise + end + ssl_io = OpenSSL::SSL::SSLSocket.new(selectable, ctx) ssl_io.sync_close = true if tls_parms[:sni_hostname] From 215c1799c2965ae82cf20e2f7ffbc4f2eab46dbd Mon Sep 17 00:00:00 2001 From: nick evans Date: Fri, 30 Aug 2024 15:13:51 -0400 Subject: [PATCH 312/343] Split pure ruby tests into their own CI job Although the regular "build" job still has support for ruby 2.3 (and hasn't yet added support for 3.2 and 3.3), I've only added back to ruby 2.7 into the build matrix. This still tests EOL ruby versions, while still narrowing the scope of the different versions of OpenSSL and ruby that will be tested by CI. --- .github/workflows/workflow.yml | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index cb83bcc8a..fc494bdea 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -71,8 +71,41 @@ jobs: CI: true TESTOPTS: -v --no-show-detail-immediately + pure_ruby: + name: >- + pure ruby (${{ matrix.os }} ${{ matrix.ruby }}) + runs-on: ${{ matrix.os }} + timeout-minutes: 10 + strategy: + fail-fast: false + matrix: + os: [ ubuntu-22.04, ubuntu-24.04, macos-14, windows-2022 ] + ruby: [ '2.7', '3.0', '3.1', '3.2', '3.3', head ] + include: + - { os: windows-2022, ruby: mingw } + exclude: + - { os: windows-2022, ruby: head } + steps: + - name: repo checkout + uses: actions/checkout@v4 + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + bundler-cache: true + + - name: macOS disable firewall + if: startsWith(matrix.os, 'macos') + run: | + sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate off + sudo /usr/libexec/ApplicationFirewall/socketfilterfw --getglobalstate + + - name: bundle install + run: bundle install --jobs 4 --retry 3 --without=documentation + - name: test_em_pure_ruby run: bundle exec rake test_em_pure_ruby env: CI: true + EM_PURE_RUBY: true TESTOPTS: -v --no-show-detail-immediately From cc96d4deeed82a904b709caf6d10232a09ffb30f Mon Sep 17 00:00:00 2001 From: nick evans Date: Fri, 30 Aug 2024 18:13:36 -0400 Subject: [PATCH 313/343] Add sorted_set gem to Gemfile for ruby >= 3.0 This is needed by the pure ruby implementation. --- Gemfile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Gemfile b/Gemfile index 06c34daa1..0fc5f0f04 100644 --- a/Gemfile +++ b/Gemfile @@ -8,6 +8,10 @@ install_if -> { RUBY_VERSION > '3.1' } do gem 'net-smtp' end +install_if -> { RUBY_VERSION >= '3.0' } do + gem "sorted_set" +end + group :documentation do gem 'yard', '>= 0.8.5.2' gem 'redcarpet' unless RUBY_PLATFORM =~ /java|mswin/ From 1fe31c23c37f792a379490313c2b666c587b67c8 Mon Sep 17 00:00:00 2001 From: nick evans Date: Tue, 3 Sep 2024 16:22:18 -0400 Subject: [PATCH 314/343] Fix bundle install for ruby 2.2 We can't use `install_if` under ruby 2.2 because the version of bundler used with ruby 2.2 can't handle that `sorted_set` requires ruby >= 2.3. --- Gemfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 0fc5f0f04..bc3307fc0 100644 --- a/Gemfile +++ b/Gemfile @@ -8,7 +8,8 @@ install_if -> { RUBY_VERSION > '3.1' } do gem 'net-smtp' end -install_if -> { RUBY_VERSION >= '3.0' } do +# switch to install_if when ruby 2.2 support is dropped +if RUBY_VERSION >= '3.0' gem "sorted_set" end From 654648246090f0a8323296e461945b28b2187397 Mon Sep 17 00:00:00 2001 From: nick evans Date: Wed, 4 Sep 2024 10:15:12 -0400 Subject: [PATCH 315/343] Use macos-latest for pure_ruby CI --- .github/workflows/workflow.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index fc494bdea..d433365cf 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -79,7 +79,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ ubuntu-22.04, ubuntu-24.04, macos-14, windows-2022 ] + os: [ ubuntu-22.04, ubuntu-24.04, macos-latest, windows-2022 ] ruby: [ '2.7', '3.0', '3.1', '3.2', '3.3', head ] include: - { os: windows-2022, ruby: mingw } From 950f01a6936bfdfce998bd9629e0b21897181f98 Mon Sep 17 00:00:00 2001 From: nick evans Date: Wed, 4 Sep 2024 10:15:35 -0400 Subject: [PATCH 316/343] WIP: Attempt (and fail) to fix macos text_connrefused This converts the conditional for platform specific code from a regexp on RUBY_PLATFORM to instead check for all of the relevant consts. I thought that this would allow us to see the tcpi_state for both Linux and MacOS... but on the MacOS machines I tested, Socket::TCP_CONNECTION_INFO was missing. --- lib/em/pure_ruby.rb | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/lib/em/pure_ruby.rb b/lib/em/pure_ruby.rb index 56286d1fe..4e42e917e 100644 --- a/lib/em/pure_ruby.rb +++ b/lib/em/pure_ruby.rb @@ -1013,11 +1013,30 @@ def initialize io @ssl_handshake_state = nil end + TCP_ESTABLISHED = 1 # why isn't this already a const in Socket? + def ready? - if RUBY_PLATFORM =~ /linux/ - io.getsockopt(Socket::SOL_TCP, Socket::TCP_INFO).unpack("i").first == 1 # TCP_ESTABLISHED + if defined?(Socket::SOL_TCP) && defined?(Socket::TCP_INFO) + # Linux: tcpi_state is an unsigned char + # struct tcp_info { + # __u8 tcpi_state; + # ... + # } + sockinfo = io.getsockopt(Socket::SOL_TCP, Socket::TCP_INFO) + sockinfo.unpack("C").first == TCP_ESTABLISHED + elsif defined?(Socket::IPPROTO_TCP) && defined?(Socket::TCP_CONNECTION_INFO) + # NOTE: the following doesn't seem to work (according to GH actions) + # + # MacOS: tcpi_state is an unsigned char + # struct tcp_connection_info { + # u_int8_t tcpi_state; /* connection state */ + # ... + # } + sockinfo = io.getsockopt(Socket::IPPROTO_TCP, Socket::TCP_CONNECTION_INFO) + sockinfo.unpack("C").first == TCP_ESTABLISHED else - io.getsockopt(Socket::SOL_SOCKET, Socket::SO_ERROR).unpack("i").first == 0 # NO ERROR + sockerr = io.getsockopt(Socket::SOL_SOCKET, Socket::SO_ERROR) + sockerr.unpack("i").first == 0 # NO ERROR end end From 34e0ddedc56b85945079a72af42bd779526d236d Mon Sep 17 00:00:00 2001 From: nick evans Date: Wed, 4 Sep 2024 14:52:18 -0400 Subject: [PATCH 317/343] Disable pure ruby builds for Windows and MacOS There are several significant outstanding issues to get the pure ruby mode working under windows and macos. For now, these are simply disabled in CI. --- .github/workflows/workflow.yml | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index d433365cf..6d3382ade 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -79,12 +79,8 @@ jobs: strategy: fail-fast: false matrix: - os: [ ubuntu-22.04, ubuntu-24.04, macos-latest, windows-2022 ] + os: [ ubuntu-22.04, ubuntu-24.04 ] ruby: [ '2.7', '3.0', '3.1', '3.2', '3.3', head ] - include: - - { os: windows-2022, ruby: mingw } - exclude: - - { os: windows-2022, ruby: head } steps: - name: repo checkout uses: actions/checkout@v4 @@ -94,12 +90,6 @@ jobs: ruby-version: ${{ matrix.ruby }} bundler-cache: true - - name: macOS disable firewall - if: startsWith(matrix.os, 'macos') - run: | - sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate off - sudo /usr/libexec/ApplicationFirewall/socketfilterfw --getglobalstate - - name: bundle install run: bundle install --jobs 4 --retry 3 --without=documentation From 1b8f0058b83f8ea881c200037ea7d42342ca8f85 Mon Sep 17 00:00:00 2001 From: nick evans Date: Wed, 4 Sep 2024 14:53:41 -0400 Subject: [PATCH 318/343] Add ubuntu-20.04 to pure ruby CI --- .github/workflows/workflow.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 6d3382ade..8091faaa3 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -79,7 +79,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ ubuntu-22.04, ubuntu-24.04 ] + os: [ ubuntu-20.04, ubuntu-22.04, ubuntu-24.04 ] ruby: [ '2.7', '3.0', '3.1', '3.2', '3.3', head ] steps: - name: repo checkout From 530b0e3f9e9e8c538c9c4bb68c5ae900fb8b25ec Mon Sep 17 00:00:00 2001 From: nick evans Date: Thu, 5 Sep 2024 10:20:16 -0400 Subject: [PATCH 319/343] Fix misspelled tests "inlince" => "inline" :) --- tests/test_ssl_inline_cert.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_ssl_inline_cert.rb b/tests/test_ssl_inline_cert.rb index aebe3b46b..9bcd3bcf8 100644 --- a/tests/test_ssl_inline_cert.rb +++ b/tests/test_ssl_inline_cert.rb @@ -77,7 +77,7 @@ def test_accept_server_key_inline_cert_inline assert_equal CERT_PEM, Client.cert end - def test_accept_client_encoded_key_inline_cert_inlince + def test_accept_client_encoded_key_inline_cert_inline omit_if(rbx?) server = { verify_peer: true, ssl_verify_result: true } @@ -94,7 +94,7 @@ def test_accept_client_encoded_key_inline_cert_inlince assert_equal CERT_PEM, Server.cert end - def test_accept_server_encoded_key_inline_cert_inlince + def test_accept_server_encoded_key_inline_cert_inline omit_if(rbx?) client = { verify_peer: true, ssl_verify_result: true } From a018815a5d8dfb40289a042baea97c19a26493e7 Mon Sep 17 00:00:00 2001 From: nick evans Date: Thu, 5 Sep 2024 14:17:35 -0400 Subject: [PATCH 320/343] Fix dhparam for different versions This also copies the default DH parameters from the stdlib OpenSSL gem, which was in copied their default from RFC7919, Appendix A.1: ffdhe2048. --- lib/em/pure_ruby.rb | 41 ++++++++++++++++++----------------------- 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/lib/em/pure_ruby.rb b/lib/em/pure_ruby.rb index 4e42e917e..39ba8a622 100644 --- a/lib/em/pure_ruby.rb +++ b/lib/em/pure_ruby.rb @@ -119,25 +119,19 @@ def initialize DefaultCertificate = CertificateCreator.new # @private - DefaultDHKey1024 = OpenSSL::PKey::DH.new <<-_end_of_pem_ + # Defined by RFC7919, Appendix A.1, and copied from + # OpenSSL::SSL::SSLContext::DH_ffdhe2048. + DH_ffdhe2048 = OpenSSL::PKey::DH.new <<-_end_of_pem_ -----BEGIN DH PARAMETERS----- -MIGHAoGBAJ0lOVy0VIr/JebWn0zDwY2h+rqITFOpdNr6ugsgvkDXuucdcChhYExJ -AV/ZD2AWPbrTqV76mGRgJg4EddgT1zG0jq3rnFdMj2XzkBYx3BVvfR0Arnby0RHR -T4h7KZ/2zmjvV+eF8kBUHBJAojUlzxKj4QeO2x20FP9X5xmNUXeDAgEC ------END DH PARAMETERS----- - _end_of_pem_ - - # @private - DefaultDHKey2048 = OpenSSL::PKey::DH.new <<-_end_of_pem_ ------BEGIN DH PARAMETERS----- -MIIBCAKCAQEA7E6kBrYiyvmKAMzQ7i8WvwVk9Y/+f8S7sCTN712KkK3cqd1jhJDY -JbrYeNV3kUIKhPxWHhObHKpD1R84UpL+s2b55+iMd6GmL7OYmNIT/FccKhTcveab -VBmZT86BZKYyf45hUF9FOuUM9xPzuK3Vd8oJQvfYMCd7LPC0taAEljQLR4Edf8E6 -YoaOffgTf5qxiwkjnlVZQc3whgnEt9FpVMvQ9eknyeGB5KHfayAc3+hUAvI3/Cr3 -1bNveX5wInh5GDx1FGhKBZ+s1H+aedudCm7sCgRwv8lKWYGiHzObSma8A86KG+MD -7Lo5JquQ3DlBodj3IDyPrxIv96lvRPFtAwIBAg== +MIIBCAKCAQEA//////////+t+FRYortKmq/cViAnPTzx2LnFg84tNpWp4TZBFGQz ++8yTnc4kmz75fS/jY2MMddj2gbICrsRhetPfHtXV/WVhJDP1H18GbtCFY2VVPe0a +87VXE15/V8k1mE8McODmi3fipona8+/och3xWKE2rec1MKzKT0g6eXq8CrGCsyT7 +YdEIqUuyyOP7uWrat2DX9GgdT0Kj3jlN9K5W7edjcrsZCwenyO4KbXCeAvzhzffi +7MA0BM0oNC9hkXL+nOmFg/+OTxIy7vKBg8P+OxtMb61zO7X8vC7CIAXFjvGDfRaD +ssbzSibBsu/6iGtCOGEoXJf//////////wIBAg== -----END DH PARAMETERS----- _end_of_pem_ + private_constant :DH_ffdhe2048 end # @private @@ -383,15 +377,16 @@ def start_tls signature end ctx.ciphers = tls_parms[:cipher_list] if tls_parms[:cipher_list] if selectable.is_server - ctx.tmp_dh = if tls_parms[:dhparam] + dhparam = if tls_parms[:dhparam] OpenSSL::PKey::DH.new(tls_parms[:dhparam]) else - case key_length - when 1024 then DefaultDHKey1024 - when 2048 then DefaultDHKey2048 - else - nil - end + DH_ffdhe2048 + end + if ctx.respond_to?(:tmp_dh=) + # openssl gem 3.1+ (shipped with ruby 3.2, compatible with ruby 2.6+) + ctx.tmp_dh = dhparam + else + ctx.tmp_dh_callback = proc { dhparam } end if tls_parms[:ecdh_curve] ctx.ecdh_curves = tls_parms[:ecdh_curve] From a7eaf1a5188078e7e01be94a67bb290f4cfa6c56 Mon Sep 17 00:00:00 2001 From: nick evans Date: Thu, 5 Sep 2024 14:30:12 -0400 Subject: [PATCH 321/343] Fix clobbered OPENSSL_NO_SSL{2,3} constants These were clobbered by later definitions that came from a different PR. --- lib/em/pure_ruby.rb | 37 ++++++++++++------------------------- 1 file changed, 12 insertions(+), 25 deletions(-) diff --git a/lib/em/pure_ruby.rb b/lib/em/pure_ruby.rb index 39ba8a622..0074ebaa7 100644 --- a/lib/em/pure_ruby.rb +++ b/lib/em/pure_ruby.rb @@ -63,29 +63,6 @@ class InvalidPrivateKey < RuntimeError; end else SSLConnectionWaitWritable = IO::WaitWritable end - - if defined?(OpenSSL::OPENSSL_VERSION) - OPENSSL_VERSION = OpenSSL::OPENSSL_VERSION - else - OPENSSL_VERSION = "not loaded" - end - if defined?(OpenSSL::OPENSSL_LIBRARY_VERSION) - OPENSSL_LIBRARY_VERSION = OpenSSL::OPENSSL_LIBRARY_VERSION - else - OPENSSL_LIBRARY_VERSION = "not loaded" - end - - openssl_version_gt = ->(maj, min, pat) { - if defined?(OpenSSL::OPENSSL_VERSION_NUMBER) - OpenSSL::OPENSSL_VERSION_NUMBER >= (maj << 28) | (min << 20) | (pat << 12) - else - false - end - } - # OpenSSL 1.1.0 removed support for SSLv2 - OPENSSL_NO_SSL2 = openssl_version_gt.(1, 1, 0) - # OpenSSL 1.1.0 disabled support for SSLv3 (by default) - OPENSSL_NO_SSL3 = openssl_version_gt.(1, 1, 0) end module EventMachine @@ -573,10 +550,20 @@ module EventMachine OPENSSL_LIBRARY_VERSION = OpenSSL::OPENSSL_LIBRARY_VERSION # @private OPENSSL_VERSION = OpenSSL::OPENSSL_VERSION + + openssl_version_gt = ->(maj, min, pat) { + if defined?(OpenSSL::OPENSSL_VERSION_NUMBER) + OpenSSL::OPENSSL_VERSION_NUMBER >= (maj << 28) | (min << 20) | (pat << 12) + else + false + end + } # @private - OPENSSL_NO_SSL2 = false + # OpenSSL 1.1.0 removed support for SSLv2 + OPENSSL_NO_SSL2 = openssl_version_gt.(1, 1, 0) # @private - OPENSSL_NO_SSL3 = false + # OpenSSL 1.1.0 disabled support for SSLv3 (by default) + OPENSSL_NO_SSL3 = openssl_version_gt.(1, 1, 0) end module EventMachine From 2ca45eee68f6be68b9e9d9a566b695b8f753cd21 Mon Sep 17 00:00:00 2001 From: nick evans Date: Thu, 5 Sep 2024 14:35:54 -0400 Subject: [PATCH 322/343] Only warn on SSLError if $VERBOSE or $DEBUG is set --- lib/em/pure_ruby.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/em/pure_ruby.rb b/lib/em/pure_ruby.rb index 0074ebaa7..c2ad37460 100644 --- a/lib/em/pure_ruby.rb +++ b/lib/em/pure_ruby.rb @@ -1049,7 +1049,9 @@ def check_handshake_complete @ssl_handshake_state = :wait_writable false rescue OpenSSL::SSL::SSLError => error - warn "SSL Error in EventMachine check_handshake_complete: #{error}" + if $VERBOSE || $DEBUG + warn "SSL Error in EventMachine check_handshake_complete: #{error}" + end @ssl_handshake_state = error rescue => error warn "#{error.class} in EventMachine check_handshake_complete: #{error}" From 10d203684bcf96e360a6e6407f4879b41bdce4a2 Mon Sep 17 00:00:00 2001 From: nick evans Date: Thu, 5 Sep 2024 16:05:01 -0400 Subject: [PATCH 323/343] Silence unused variable warning in pure ruby mode --- lib/em/pure_ruby.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/em/pure_ruby.rb b/lib/em/pure_ruby.rb index c2ad37460..53854cb3d 100644 --- a/lib/em/pure_ruby.rb +++ b/lib/em/pure_ruby.rb @@ -1195,7 +1195,7 @@ def select_for_reading? def eventable_read begin 10.times { - descriptor,peername = io.accept_nonblock + descriptor, _peername = io.accept_nonblock sd = EvmaTCPClient.new descriptor sd.is_server = true EventMachine::event_callback uuid, ConnectionAccepted, sd.uuid @@ -1252,7 +1252,7 @@ def select_for_reading? def eventable_read begin 10.times { - descriptor,peername = io.accept_nonblock + descriptor, _peername = io.accept_nonblock sd = StreamObject.new descriptor EventMachine::event_callback uuid, ConnectionAccepted, sd.uuid } From 4c7eeef717956887d7d3f5d6250f716d907bbcab Mon Sep 17 00:00:00 2001 From: Aaron Stone Date: Thu, 5 Sep 2024 14:42:05 -0700 Subject: [PATCH 324/343] Gemfile nit --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index bc3307fc0..3870258b2 100644 --- a/Gemfile +++ b/Gemfile @@ -10,7 +10,7 @@ end # switch to install_if when ruby 2.2 support is dropped if RUBY_VERSION >= '3.0' - gem "sorted_set" + gem 'sorted_set' end group :documentation do From 77d6980d4dac95229de7cfa422dc5d8f2a52838d Mon Sep 17 00:00:00 2001 From: Borna Novak Date: Thu, 5 Sep 2024 23:46:42 +0200 Subject: [PATCH 325/343] Fix for #477 (#478) Resolves #477, IO waits in Java code checkIO are reduced to at most timerQuantum miliseconds --- java/src/com/rubyeventmachine/EmReactor.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/java/src/com/rubyeventmachine/EmReactor.java b/java/src/com/rubyeventmachine/EmReactor.java index 09e22e729..6eb9afe8c 100644 --- a/java/src/com/rubyeventmachine/EmReactor.java +++ b/java/src/com/rubyeventmachine/EmReactor.java @@ -177,9 +177,9 @@ void checkIO() { if (diff <= 0) timeout = -1; // don't wait, just poll once else - timeout = diff; + timeout = diff > timerQuantum ? timerQuantum : diff; } else { - timeout = 0; // wait indefinitely + timeout = timerQuantum; } try { From a32652bb0cee6a8152c1d976c2050e9669d4d31b Mon Sep 17 00:00:00 2001 From: MSP-Greg Date: Fri, 6 Sep 2024 13:51:47 -0500 Subject: [PATCH 326/343] test files - fix Ruby head 'warning: literal string will be frozen in the future' (#1001) --- tests/em_test_helper.rb | 2 ++ tests/test_attach.rb | 4 +++- tests/test_basic.rb | 4 +++- tests/test_ltp.rb | 6 ++++-- tests/test_ltp2.rb | 6 ++++-- tests/test_send_file.rb | 16 +++++++++------- 6 files changed, 25 insertions(+), 13 deletions(-) diff --git a/tests/em_test_helper.rb b/tests/em_test_helper.rb index cb5d38cd4..6ecfb6335 100644 --- a/tests/em_test_helper.rb +++ b/tests/em_test_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'em/pure_ruby' if ENV['EM_PURE_RUBY'] require 'eventmachine' require 'rbconfig' diff --git a/tests/test_attach.rb b/tests/test_attach.rb index 340cbe87f..051a83cce 100644 --- a/tests/test_attach.rb +++ b/tests/test_attach.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require_relative 'em_test_helper' class TestAttach < Test::Unit::TestCase @@ -31,7 +33,7 @@ def unbind def setup @port = next_port $read, $r, $w, $fd = nil - $received_data = "" + $received_data = "".dup end def teardown diff --git a/tests/test_basic.rb b/tests/test_basic.rb index a8dcdceca..b0fca1c14 100644 --- a/tests/test_basic.rb +++ b/tests/test_basic.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require_relative 'em_test_helper' class TestBasic < Test::Unit::TestCase @@ -135,7 +137,7 @@ def post_init # From ticket #50 def test_byte_range_send - $received = '' + $received = ''.dup $sent = (0..255).to_a.pack('C*') EM::run { EM::start_server "127.0.0.1", @port, BrsTestSrv diff --git a/tests/test_ltp.rb b/tests/test_ltp.rb index ec09e7b08..1a4b7c322 100644 --- a/tests/test_ltp.rb +++ b/tests/test_ltp.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require_relative 'em_test_helper' class TestLineAndTextProtocol < Test::Unit::TestCase @@ -103,7 +105,7 @@ def receive_binary_data text end def test_lines_and_text - output = '' + output = ''.dup EM.run { EM.start_server( "127.0.0.1", @port, LineAndTextTest ) setup_timeout 0.4 @@ -137,7 +139,7 @@ def receive_binary_data text end def test_binary_text - output = '' + output = ''.dup EM.run { EM.start_server( "127.0.0.1", @port, BinaryTextTest ) setup_timeout 0.4 diff --git a/tests/test_ltp2.rb b/tests/test_ltp2.rb index 71781ce70..1144884dc 100644 --- a/tests/test_ltp2.rb +++ b/tests/test_ltp2.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require_relative 'em_test_helper' # TODO!!! Need tests for overlength headers and text bodies. @@ -51,7 +53,7 @@ class ChangeDelimiter attr_reader :lines def initialize *args super - @delim = "A" + @delim = "A".dup set_delimiter @delim end def receive_line line @@ -153,7 +155,7 @@ def initialize *args def receive_binary_data data @n_calls ||= 0 @n_calls += 1 - (@body ||= "") << data + (@body ||= "".dup) << data end end diff --git a/tests/test_send_file.rb b/tests/test_send_file.rb index 2ef1f7fde..8cb67b7e8 100644 --- a/tests/test_send_file.rb +++ b/tests/test_send_file.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require_relative 'em_test_helper' require 'tempfile' @@ -40,7 +42,7 @@ def test_send_file f << ("A" * 5000) } - data = '' + data = ''.dup EM.run { EM.start_server "127.0.0.1", @port, TestModule, @filename @@ -60,7 +62,7 @@ def test_send_large_file f << ("A" * 1000000) } - data = '' + data = ''.dup assert_raises(RuntimeError) { EM.run { @@ -102,7 +104,7 @@ def test_stream_file_data f << ("A" * 1000) } - data = '' + data = ''.dup EM.run { EM.start_server "127.0.0.1", @port, StreamTestModule, @filename @@ -120,7 +122,7 @@ def test_stream_chunked_file_data f << ("A" * 1000) } - data = '' + data = ''.dup EM.run { EM.start_server "127.0.0.1", @port, ChunkStreamTestModule, @filename @@ -147,7 +149,7 @@ def post_init end end def test_stream_bad_file - data = '' + data = ''.dup EM.run { EM.start_server "127.0.0.1", @port, BadFileTestModule, @filename setup_timeout(5) @@ -175,7 +177,7 @@ def test_stream_large_file_data f << ("A" * 10000) } - data = '' + data = ''.dup EM.run { EM.start_server "127.0.0.1", @port, StreamTestModule, @filename @@ -193,7 +195,7 @@ def test_stream_large_chunked_file_data f << ("A" * 100000) } - data = '' + data = ''.dup EM.run { EM.start_server "127.0.0.1", @port, ChunkStreamTestModule, @filename From 7c6ea32459427fc1da3b83dc9473f34afde7184c Mon Sep 17 00:00:00 2001 From: MSP-Greg Date: Fri, 6 Sep 2024 13:54:58 -0500 Subject: [PATCH 327/343] workflow.yml - use setup-ruby's `bundler-cache: true`, misc (#1000) Run all Rubies on macOS amd64, use macos-14 --- .github/workflows/workflow.yml | 39 ++++++++++++++-------------------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 8091faaa3..9fee68d4b 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -8,29 +8,22 @@ jobs: ${{ matrix.os }} ${{ matrix.ruby }} runs-on: ${{ matrix.os }} timeout-minutes: ${{ (startsWith(matrix.os, 'macos') || startsWith(matrix.os, 'windows')) && 15 || 10 }} + env: + BUNDLE_WITHOUT: documentation strategy: fail-fast: false matrix: - os: [ ubuntu-24.04, ubuntu-22.04, ubuntu-20.04, windows-2019, macos-latest ] + os: [ ubuntu-24.04, ubuntu-22.04, ubuntu-20.04, windows-2022, macos-14, macos-13 ] ruby: [ '2.3', '2.4', '2.5', '2.6', '2.7', '3.0', '3.1', '3.2', '3.3', head ] include: - - # GH-988 added support for ruby >= 3.3 - - { os: windows-2019, ruby: mingw } - - # CRuby < 2.6 does not support macos-arm64, so these use amd64 - - { os: macos-13, ruby: '2.3' } - - { os: macos-13, ruby: '2.4' } - - { os: macos-13, ruby: '2.5' } - + - { os: windows-2022, ruby: ucrt } exclude: - - { os: macos-latest, ruby: head } - - { os: windows-2019, ruby: head } + - { os: windows-2022, ruby: head } # CRuby < 2.6 does not support macos-arm64, so these use amd64 - - { os: macos-latest, ruby: '2.3' } - - { os: macos-latest, ruby: '2.4' } - - { os: macos-latest, ruby: '2.5' } + - { os: macos-14, ruby: '2.3' } + - { os: macos-14, ruby: '2.4' } + - { os: macos-14, ruby: '2.5' } # Avoids the following error: # Bundler 2 requires Ruby 2.3+, using Bundler 1 on Ruby <= 2.2 @@ -47,11 +40,13 @@ jobs: - name: repo checkout uses: actions/checkout@v4 - - name: load ruby, openssl + - name: load ruby, run bundle install uses: ruby/setup-ruby-pkgs@v1 with: ruby-version: ${{ matrix.ruby }} - mingw: _upgrade_ openssl + bundler-cache: true + rubygems: ${{ (matrix.ruby < '2.7') && 'latest' || 'default' }} + mingw: ${{ (matrix.ruby < '2.4') && '_upgrade_ openssl' || '' }} - name: macOS disable firewall if: startsWith(matrix.os, 'macos') @@ -59,9 +54,6 @@ jobs: sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate off sudo /usr/libexec/ApplicationFirewall/socketfilterfw --getglobalstate - - name: bundle install - run: bundle install --jobs 4 --retry 3 --without=documentation - - name: compile run: bundle exec rake compile @@ -76,6 +68,8 @@ jobs: pure ruby (${{ matrix.os }} ${{ matrix.ruby }}) runs-on: ${{ matrix.os }} timeout-minutes: 10 + env: + BUNDLE_WITHOUT: documentation strategy: fail-fast: false matrix: @@ -84,14 +78,13 @@ jobs: steps: - name: repo checkout uses: actions/checkout@v4 + - name: Set up Ruby uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby }} bundler-cache: true - - - name: bundle install - run: bundle install --jobs 4 --retry 3 --without=documentation + rubygems: ${{ (matrix.ruby < '2.7') && 'latest' || 'default' }} - name: test_em_pure_ruby run: bundle exec rake test_em_pure_ruby From 1abfb0d92b1f0ca5a8535db99d813c7ce5c425bf Mon Sep 17 00:00:00 2001 From: nick evans Date: Wed, 4 Sep 2024 11:18:55 -0400 Subject: [PATCH 328/343] Remove most EOL rubies from the build matrix Most deployments will be to linux servers, and EventMachine *is* still maintaining *some* support for old versions of ruby. So this still keeps up to four EOL ruby builds for ubuntu, but _only_ for ruby versions that went EOL _after_ that version of ubuntu was first released. --- .github/workflows/workflow.yml | 52 +++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 9fee68d4b..3d4ea8a37 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -14,27 +14,42 @@ jobs: fail-fast: false matrix: os: [ ubuntu-24.04, ubuntu-22.04, ubuntu-20.04, windows-2022, macos-14, macos-13 ] - ruby: [ '2.3', '2.4', '2.5', '2.6', '2.7', '3.0', '3.1', '3.2', '3.3', head ] + # We currently support four EOL versions of ruby, but only on linux + # versions that were released before that version of ruby went EOL. + ruby: [ '2.5', '2.6', '2.7', '3.0', '3.1', '3.2', '3.3', head ] include: - - { os: windows-2022, ruby: ucrt } + - { os: windows-2022, ruby: ucrt } # used instead of "head" exclude: - - { os: windows-2022, ruby: head } + - { os: windows-2022, ruby: head } # uses "ucrt" instead - # CRuby < 2.6 does not support macos-arm64, so these use amd64 - - { os: macos-14, ruby: '2.3' } - - { os: macos-14, ruby: '2.4' } + # Ubuntu 20.04 was released just prior to ruby 2.4's EOL + # (no exclusions) + + # Ubuntu 22.04 was released just prior to ruby 2.6's EOL + - { os: ubuntu-22.04, ruby: '2.5' } + + # Ubuntu 24.04 was released just prior to ruby 3.0's EOL + - { os: ubuntu-24.04, ruby: '2.5' } + - { os: ubuntu-24.04, ruby: '2.6' } + - { os: ubuntu-24.04, ruby: '2.7' } + + # No EOL versions for macos-13 + - { os: macos-13, ruby: '2.5' } + - { os: macos-13, ruby: '2.6' } + - { os: macos-13, ruby: '2.7' } + - { os: macos-13, ruby: '3.0' } + + # No EOL versions for macos-14 - { os: macos-14, ruby: '2.5' } + - { os: macos-14, ruby: '2.6' } + - { os: macos-14, ruby: '2.7' } + - { os: macos-14, ruby: '3.0' } - # Avoids the following error: - # Bundler 2 requires Ruby 2.3+, using Bundler 1 on Ruby <= 2.2 - # /opt/hostedtoolcache/Ruby/2.2.10/x64/bin/gem install bundler -v ~> 1.0 - # ERROR: While executing gem ... (RuntimeError) - # Marshal.load reentered at marshal_load - # - # See https://github.com/ruby/setup-ruby/issues/496 - # - # This works fine on ubuntu 20.04 and 24.04. - # - { os: ubuntu-22.04, ruby: '2.2' } + # No EOL versions for windows-2022 + - { os: windows-2022, ruby: '2.5' } + - { os: windows-2022, ruby: '2.6' } + - { os: windows-2022, ruby: '2.7' } + - { os: windows-2022, ruby: '3.0' } steps: - name: repo checkout @@ -46,7 +61,6 @@ jobs: ruby-version: ${{ matrix.ruby }} bundler-cache: true rubygems: ${{ (matrix.ruby < '2.7') && 'latest' || 'default' }} - mingw: ${{ (matrix.ruby < '2.4') && '_upgrade_ openssl' || '' }} - name: macOS disable firewall if: startsWith(matrix.os, 'macos') @@ -73,8 +87,9 @@ jobs: strategy: fail-fast: false matrix: + # TODO: Fix macos-13, macos-14, windows-2022 os: [ ubuntu-20.04, ubuntu-22.04, ubuntu-24.04 ] - ruby: [ '2.7', '3.0', '3.1', '3.2', '3.3', head ] + ruby: [ '3.1', '3.2', '3.3', head ] steps: - name: repo checkout uses: actions/checkout@v4 @@ -84,7 +99,6 @@ jobs: with: ruby-version: ${{ matrix.ruby }} bundler-cache: true - rubygems: ${{ (matrix.ruby < '2.7') && 'latest' || 'default' }} - name: test_em_pure_ruby run: bundle exec rake test_em_pure_ruby From 35802d46d25324a432efcf7b38bf3f1edf7bfe19 Mon Sep 17 00:00:00 2001 From: nick evans Date: Fri, 6 Sep 2024 16:20:55 -0400 Subject: [PATCH 329/343] Convert to ruby/setup-ruby (from setup-ruby-pkgs) From @MSP-Greg's comment on #998: > Since Ruby 2.3 & 2.4 are removed, we can change the workflow's line 46 > from uses: ruby/setup-ruby-pkgs@v1 to uses: ruby/setup-ruby@v1. Co-authored-by: MSP-Greg --- .github/workflows/workflow.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 3d4ea8a37..f8ea2cf15 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -56,7 +56,7 @@ jobs: uses: actions/checkout@v4 - name: load ruby, run bundle install - uses: ruby/setup-ruby-pkgs@v1 + uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby }} bundler-cache: true From acdd60bb9a41354503593842daf56404a05181ab Mon Sep 17 00:00:00 2001 From: nick evans Date: Fri, 6 Sep 2024 12:51:14 -0400 Subject: [PATCH 330/343] Bump required ruby version from 2.0+ to 2.5+ This change still supports four EOL versions: 2.5, 2.6, 2.7, and 3.0. Practically speaking, 2.0, 2.1, and 2.2 have already been removed from CI. ruby 2.5 was released with v2.1.0 of the openssl gem, and v2.1.0 of that gem dropped support for OpenSSL libary versions 0.9.8 to 1.0.0. So, by raising our ruby version, we also raise our minimum supported OpenSSL version to 1.0.1 (or LibreSSL 2.5). --- eventmachine.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eventmachine.gemspec b/eventmachine.gemspec index 1c561fa05..08fc8d5ea 100644 --- a/eventmachine.gemspec +++ b/eventmachine.gemspec @@ -5,7 +5,7 @@ Gem::Specification.new do |s| s.version = EventMachine::VERSION s.homepage = 'https://github.com/eventmachine/eventmachine' s.licenses = ['Ruby', 'GPL-2.0'] - s.required_ruby_version = '>= 2.0.0' + s.required_ruby_version = '>= 2.5.0' s.authors = ["Francis Cianfrocca", "Aman Gupta"] s.email = ["garbagecat10@gmail.com", "aman@tmm1.net"] From ee9a5b699b0a4e244f121341f53b51e0d80f9b87 Mon Sep 17 00:00:00 2001 From: nick evans Date: Fri, 6 Sep 2024 16:49:28 -0400 Subject: [PATCH 331/343] Sort the builds in (mostly) alphanumeric order Ubuntu builds sort first (20.04, 22.04, 24.04), then macos-13, macos-14, and windows-2022. This also sorts the windows ucrt build immediately after the other windows builds. --- .github/workflows/workflow.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index f8ea2cf15..77f4a12a3 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -13,10 +13,18 @@ jobs: strategy: fail-fast: false matrix: - os: [ ubuntu-24.04, ubuntu-22.04, ubuntu-20.04, windows-2022, macos-14, macos-13 ] + os: + - ubuntu-20.04 + - ubuntu-22.04 + - ubuntu-24.04 + - macos-13 # amd64 + - macos-14 # arm64 + - windows-2022 + # We currently support four EOL versions of ruby, but only on linux # versions that were released before that version of ruby went EOL. ruby: [ '2.5', '2.6', '2.7', '3.0', '3.1', '3.2', '3.3', head ] + include: - { os: windows-2022, ruby: ucrt } # used instead of "head" exclude: From 3e34685db78ad32b18427b9a5fff681254cac63d Mon Sep 17 00:00:00 2001 From: MSP-Greg Date: Fri, 6 Sep 2024 20:48:09 -0500 Subject: [PATCH 332/343] lib files - fix Ruby head 'warning: literal string will be frozen... (#1003) --- lib/em/protocols/httpclient.rb | 4 +++- lib/em/protocols/line_protocol.rb | 4 +++- lib/em/protocols/object_protocol.rb | 4 +++- lib/em/protocols/saslauth.rb | 6 ++++-- 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/lib/em/protocols/httpclient.rb b/lib/em/protocols/httpclient.rb index 80d217524..1344641a3 100644 --- a/lib/em/protocols/httpclient.rb +++ b/lib/em/protocols/httpclient.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + #-- # # Author:: Francis Cianfrocca (gmail: blackhedd) @@ -180,7 +182,7 @@ def receive_data data case @read_state when :base # Perform any per-request initialization here and don't consume any data. - @data = "" + @data = "".dup @headers = [] @content_length = nil # not zero @content = "" diff --git a/lib/em/protocols/line_protocol.rb b/lib/em/protocols/line_protocol.rb index 0bdef93a3..d2c55fa96 100644 --- a/lib/em/protocols/line_protocol.rb +++ b/lib/em/protocols/line_protocol.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module EventMachine module Protocols # LineProtocol will parse out newline terminated strings from a receive_data stream @@ -13,7 +15,7 @@ module Protocols module LineProtocol # @private def receive_data data - (@buf ||= '') << data + (@buf ||= ''.dup) << data @buf.each_line do |line| if line[-1] == "\n" diff --git a/lib/em/protocols/object_protocol.rb b/lib/em/protocols/object_protocol.rb index ec79cb4a6..d858f0c73 100644 --- a/lib/em/protocols/object_protocol.rb +++ b/lib/em/protocols/object_protocol.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module EventMachine module Protocols # ObjectProtocol allows for easy communication using marshaled ruby objects @@ -19,7 +21,7 @@ def serializer # @private def receive_data data - (@buf ||= '') << data + (@buf ||= ''.dup) << data while @buf.size >= 4 if @buf.size >= 4+(size=@buf.unpack('N').first) diff --git a/lib/em/protocols/saslauth.rb b/lib/em/protocols/saslauth.rb index 9cabc518e..c4f4a9a08 100644 --- a/lib/em/protocols/saslauth.rb +++ b/lib/em/protocols/saslauth.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + #-- # # Author:: Francis Cianfrocca (gmail: blackhedd) @@ -84,7 +86,7 @@ module SASLauth MaxFieldSize = 128*1024 def post_init super - @sasl_data = "" + @sasl_data = "".dup @sasl_values = [] end @@ -149,7 +151,7 @@ def validate? username, psw, sysname=nil, realm=nil end def post_init - @sasl_data = "" + @sasl_data = "".dup @queries = [] end From 00e215cc1dafeaa726c63ec89d0ca2766001d713 Mon Sep 17 00:00:00 2001 From: MSP-Greg Date: Fri, 6 Sep 2024 20:52:27 -0500 Subject: [PATCH 333/343] [CI] Windows test fixes - test_httpclient2.rb & test_pure.rb (#999) --- tests/test_httpclient2.rb | 28 ++++++++++++++++------------ tests/test_pure.rb | 17 ++++++++++------- 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/tests/test_httpclient2.rb b/tests/test_httpclient2.rb index 73bc63790..af4cae3d1 100644 --- a/tests/test_httpclient2.rb +++ b/tests/test_httpclient2.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require_relative 'em_test_helper' class TestHttpClient2 < Test::Unit::TestCase @@ -9,6 +11,7 @@ class TestServer < EM::Connection CI_WINDOWS_OLD = windows? and RUBY_VERSION < '2.5' def setup + @host = '127.0.0.1' @port = next_port end @@ -20,10 +23,10 @@ def setup def test_connect EM.run { setup_timeout - EM.start_server '127.0.0.1', @port, TestServer + EM.start_server @host, @port, TestServer silent do - EM::P::HttpClient2.connect '127.0.0.1', @port - EM::P::HttpClient2.connect( :host=>'127.0.0.1', :port=>@port ) + EM::P::HttpClient2.connect @host, @port + EM::P::HttpClient2.connect( :host=>@host, :port=>@port ) end EM.stop } @@ -32,9 +35,9 @@ def test_connect def test_bad_port EM.run { setup_timeout - EM.start_server '127.0.0.1', @port, TestServer + EM.start_server @host, @port, TestServer assert_raises( ArgumentError ) { - silent { EM::P::HttpClient2.connect '127.0.0.1', "xxx" } + silent { EM::P::HttpClient2.connect @host, "xxx" } } EM.stop } @@ -43,8 +46,9 @@ def test_bad_port def test_bad_server err = nil EM.run { - setup_timeout TIMEOUT - http = silent { EM::P::HttpClient2.connect '127.0.0.1', 9999 } + + setup_timeout(windows? ? 4 : TIMEOUT) + http = silent { EM::P::HttpClient2.connect @host, 9999 } d = http.get "/" d.errback { err = true; d.internal_error; EM.stop } } @@ -54,7 +58,7 @@ def test_bad_server def test_get content = nil EM.run { - setup_timeout(CI_WINDOWS_OLD ? 9 : TIMEOUT) + setup_timeout(CI_WINDOWS_OLD ? 4 : TIMEOUT) http = silent { EM::P::HttpClient2.connect :host => "www.google.com", :port => 80 } d = http.get "/" d.callback { @@ -107,9 +111,9 @@ def test_get_pipeline def test_authheader EM.run { - setup_timeout TIMEOUT - EM.start_server '127.0.0.1', @port, TestServer - http = silent { EM::P::HttpClient2.connect '127.0.0.1', 18842 } + setup_timeout(windows? ? 4 : TIMEOUT) + EM.start_server @host, @port, TestServer + http = silent { EM::P::HttpClient2.connect @host, 18842 } d = http.get :url=>"/", :authorization=>"Basic xxx" d.callback {EM.stop} d.errback {EM.stop} @@ -120,7 +124,7 @@ def test_https_get omit("No SSL") unless EM.ssl? d = nil EM.run { - setup_timeout(CI_WINDOWS_OLD ? 9 : TIMEOUT) + setup_timeout(CI_WINDOWS_OLD ? 4 : TIMEOUT) http = silent { EM::P::HttpClient2.connect :host => 'www.google.com', :port => 443, :tls => true } d = http.get "/" d.callback {EM.stop} diff --git a/tests/test_pure.rb b/tests/test_pure.rb index 1cf774283..db5cb9fc3 100644 --- a/tests/test_pure.rb +++ b/tests/test_pure.rb @@ -1,8 +1,11 @@ +# frozen_string_literal: true + require_relative 'em_test_helper' class TestPure < Test::Unit::TestCase def setup + @host = '127.0.0.1' @port = next_port end @@ -25,7 +28,7 @@ def test_exception_handling_releases_resources 2.times do assert_raises(exception) do EM.run do - EM.start_server "127.0.0.1", @port + EM.start_server @host, @port raise exception end end @@ -51,8 +54,8 @@ def connection_completed def test_connrefused assert_nothing_raised do EM.run { - setup_timeout(2) - EM.connect "127.0.0.1", @port, TestConnrefused + setup_timeout(windows? ? 4 : 2) + EM.connect @host, @port, TestConnrefused } end end @@ -69,8 +72,8 @@ def connection_completed def test_connaccepted assert_nothing_raised do EM.run { - EM.start_server "127.0.0.1", @port - EM.connect "127.0.0.1", @port, TestConnaccepted + EM.start_server @host, @port + EM.connect @host, @port, TestConnaccepted setup_timeout(1) } end @@ -128,8 +131,8 @@ def test_start_tls $client_handshake_completed, $server_handshake_completed = false, false $client_received_data, $server_received_data = nil, nil EM.run do - EM.start_server("127.0.0.1", 16789, TLSServer) - EM.connect("127.0.0.1", 16789, TLSClient) + EM.start_server(@host, 16789, TLSServer) + EM.connect(@host, 16789, TLSClient) end assert($client_handshake_completed) From bfa64f77905f48daee694bd0eb6263b31155e338 Mon Sep 17 00:00:00 2001 From: MSP-Greg Date: Fri, 6 Sep 2024 20:52:37 -0500 Subject: [PATCH 334/343] [CI] test_httpclient2.rb - fix intermittent failures on GHA (#1002) Friday afternoon is a busy time for GHA... --- tests/test_httpclient2.rb | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/tests/test_httpclient2.rb b/tests/test_httpclient2.rb index af4cae3d1..eaae34a03 100644 --- a/tests/test_httpclient2.rb +++ b/tests/test_httpclient2.rb @@ -92,16 +92,11 @@ def _test_get_multiple def test_get_pipeline headers, headers2 = nil, nil EM.run { - setup_timeout TIMEOUT + # intermittent CI failures, external server w/two requests? + setup_timeout (TIMEOUT * 2) http = silent { EM::P::HttpClient2.connect "www.google.com", 80 } - d = http.get("/") - d.callback { - headers = d.headers - } - e = http.get("/") - e.callback { - headers2 = e.headers - } + http.get("/").callback { |resp| headers = resp.headers } + http.get("/").callback { |resp| headers2 = resp.headers } EM.tick_loop { EM.stop if headers && headers2 } EM.add_timer(1) { EM.stop } } From 5971d0efbfa4416f57061d16b942b5eeb4b1ef23 Mon Sep 17 00:00:00 2001 From: MSP-Greg Date: Fri, 13 May 2022 18:08:05 -0500 Subject: [PATCH 335/343] Ruby 3+ & OpenSSL 3 fixes --- ext/extconf.rb | 3 +++ ext/fastfilereader/rubymain.cpp | 3 +++ ext/ssl.cpp | 4 ++++ 3 files changed, 10 insertions(+) diff --git a/ext/extconf.rb b/ext/extconf.rb index 16b0b5d86..52b92216f 100644 --- a/ext/extconf.rb +++ b/ext/extconf.rb @@ -258,6 +258,9 @@ def find_openssl_library have_func "TLS_server_method" , "openssl/ssl.h" have_macro "SSL_CTX_set_min_proto_version", "openssl/ssl.h" +# below is yes for 3.0.0 & later +have_func "SSL_get1_peer_certificate" , "openssl/ssl.h" + # Hack so that try_link will test with a C++ compiler instead of a C compiler TRY_LINK.sub!('$(CC)', '$(CXX)') diff --git a/ext/fastfilereader/rubymain.cpp b/ext/fastfilereader/rubymain.cpp index b962bf023..48e6e766a 100644 --- a/ext/fastfilereader/rubymain.cpp +++ b/ext/fastfilereader/rubymain.cpp @@ -115,6 +115,9 @@ extern "C" void Init_fastfilereaderext() EmModule = rb_define_module ("EventMachine"); FastFileReader = rb_define_class_under (EmModule, "FastFileReader", rb_cObject); Mapper = rb_define_class_under (FastFileReader, "Mapper", rb_cObject); + // fixes lib/em/streamer.rb:70: warning: undefining the allocator of + // T_DATA class EventMachine::FastFileReader::Mapper + rb_undef_alloc_func(Mapper); rb_define_module_function (Mapper, "new", (VALUE(*)(...))mapper_new, 1); rb_define_method (Mapper, "size", (VALUE(*)(...))mapper_size, 0); diff --git a/ext/ssl.cpp b/ext/ssl.cpp index 46a144608..492a88ddc 100644 --- a/ext/ssl.cpp +++ b/ext/ssl.cpp @@ -616,7 +616,11 @@ X509 *SslBox_t::GetPeerCert() X509 *cert = NULL; if (pSSL) +#ifdef HAVE_SSL_GET1_PEER_CERTIFICATE + cert = SSL_get1_peer_certificate(pSSL); +#else cert = SSL_get_peer_certificate(pSSL); +#endif return cert; } From f21200d84e82fa3c32d50ca3fc65b8ed78993593 Mon Sep 17 00:00:00 2001 From: MSP-Greg Date: Fri, 13 May 2022 18:12:11 -0500 Subject: [PATCH 336/343] Use SSL_CTX_set_dh_auto if needed and available --- ext/extconf.rb | 3 +++ ext/ssl.cpp | 9 +++++++++ tests/test_ssl_dhparam.rb | 34 +++++++++++++++++++++++++++++----- 3 files changed, 41 insertions(+), 5 deletions(-) diff --git a/ext/extconf.rb b/ext/extconf.rb index 52b92216f..8a7e588a9 100644 --- a/ext/extconf.rb +++ b/ext/extconf.rb @@ -258,6 +258,9 @@ def find_openssl_library have_func "TLS_server_method" , "openssl/ssl.h" have_macro "SSL_CTX_set_min_proto_version", "openssl/ssl.h" +# below exists in 1.1.0 and later, but isn't documented until 3.0.0 +have_func "SSL_CTX_set_dh_auto(NULL, 0)" , "openssl/ssl.h" + # below is yes for 3.0.0 & later have_func "SSL_get1_peer_certificate" , "openssl/ssl.h" diff --git a/ext/ssl.cpp b/ext/ssl.cpp index 492a88ddc..61314c6d2 100644 --- a/ext/ssl.cpp +++ b/ext/ssl.cpp @@ -297,6 +297,7 @@ SslContext_t::SslContext_t (bool is_server, const std::string &privkeyfile, cons if (e <= 0) ERR_print_errors_fp(stderr); assert (e > 0); } + if (dhparam.length() > 0) { DH *dh; BIO *bio; @@ -321,6 +322,14 @@ SslContext_t::SslContext_t (bool is_server, const std::string &privkeyfile, cons DH_free(dh); BIO_free(bio); + } else { +#ifdef HAVE_SSL_CTX_SET_DH_AUTO + // https://www.openssl.org/docs/man3.0/man3/SSL_CTX_set_dh_auto.html + // printf("\nUsing SSL_CTX_set_dh_auto\n"); + SSL_CTX_set_dh_auto(pCtx, 1); +#else + // noop +#endif } if (ecdh_curve.length() > 0) { diff --git a/tests/test_ssl_dhparam.rb b/tests/test_ssl_dhparam.rb index aece10fe1..5b653c260 100644 --- a/tests/test_ssl_dhparam.rb +++ b/tests/test_ssl_dhparam.rb @@ -12,17 +12,22 @@ class TestSSLDhParam < Test::Unit::TestCase DH_1_2 = { cipher_list: "DHE,EDH", ssl_version: %w(TLSv1_2) } CLIENT_1_2 = { client_unbind: true, ssl_version: %w(TLSv1_2) } - def test_no_dhparam + def test_dhparam_1_2_auto omit_if(EM.library_type == :pure_ruby) # DH will work with defaults omit_if(rbx?) client_server client: CLIENT_1_2, server: DH_1_2 - refute Client.handshake_completed? - refute Server.handshake_completed? + if EMSSLHandlers::IS_SSL_GE_1_1 + assert Client.handshake_completed? + assert Server.handshake_completed? + else + refute Client.handshake_completed? + refute Server.handshake_completed? + end end - def test_dhparam_1_2 + def test_dhparam_1_2_supplied omit_if(rbx?) client_server client: CLIENT_1_2, server: DH_1_2.merge(dhparam: DH_PARAM_FILE) @@ -36,7 +41,7 @@ def test_dhparam_1_2 assert_match(/^(DHE|EDH)/, Client.cipher_name) end - def test_dhparam_1_3 + def test_dhparam_1_3_supplied omit_if(rbx?) omit("TLSv1_3 is unavailable") unless EM.const_defined? :EM_PROTO_TLSv1_3 @@ -54,4 +59,23 @@ def test_dhparam_1_3 # may depend on OpenSSL build options assert_equal "TLS_AES_256_GCM_SHA384", Client.cipher_name end + + def test_dhparam_1_3_auto + omit_if(rbx?) + omit("TLSv1_3 is unavailable") unless EM.const_defined? :EM_PROTO_TLSv1_3 + + client = { client_unbind: true, ssl_version: %w(TLSv1_3) } + server = { cipher_list: "DHE,EDH", ssl_version: %w(TLSv1_3) } + client_server client: client, server: server + + assert Client.handshake_completed? + assert Server.handshake_completed? + + assert Client.cipher_name.length > 0 + assert_equal Client.cipher_name, Server.cipher_name + + # see https://wiki.openssl.org/index.php/TLS1.3#Ciphersuites + # may depend on OpenSSL build options + assert_equal "TLS_AES_256_GCM_SHA384", Client.cipher_name + end end if EM.ssl? From 26e840b138c588c23659a4805a3b0d76b91b07b3 Mon Sep 17 00:00:00 2001 From: nick evans Date: Mon, 9 Sep 2024 10:46:35 -0400 Subject: [PATCH 337/343] Add str.dup to httpclient, for consistency In practice, I don't think either of these really matter: * `@data` will just be reassigned in `#receive_data`'s `:base` clause * `@content` should be reassigned (to non-frozen) by the earlier if/eslif clauses, prior to being mutated by the `else` clause. But, for consistency, documentation, and in case this is subclassed, we should ensure that these vars are always mutable buffers. --- lib/em/protocols/httpclient.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/em/protocols/httpclient.rb b/lib/em/protocols/httpclient.rb index 1344641a3..feef1f59b 100644 --- a/lib/em/protocols/httpclient.rb +++ b/lib/em/protocols/httpclient.rb @@ -91,7 +91,7 @@ def self.request( args = {} ) def post_init @start_time = Time.now - @data = "" + @data = "".dup @read_state = :base end @@ -185,7 +185,7 @@ def receive_data data @data = "".dup @headers = [] @content_length = nil # not zero - @content = "" + @content = "".dup @status = nil @chunked = false @chunk_length = nil From 84662ca0a51ff64d88b2ee5ede7ea28e6476d94e Mon Sep 17 00:00:00 2001 From: "nicholas a. evans" Date: Mon, 9 Sep 2024 13:28:15 -0400 Subject: [PATCH 338/343] Adjust flaky timeouts (#1004) * Add `#ci?` for tests to adjust themselves in CI * Update TIMEOUT_INTERVAL based on both OS and CI Linux, local: 0.25 Windows, local: 0.5 MacOS, local: 0.5 Linux, in CI: 1.0 Windows, in CI: 2.0 MacOS, in CI: 2.0 And, of course, each test may include its own multiplier or increment. * Raise timeouts for all HttpClient2 tests ...but especially for test_get_pipeline. With the new TEST_INTERVAL, this will be 5s in CI for Linux, and 10s in CI for Windows/MacOS. * Add EM.stop in test errback * Remove one second timeout from test This seems to conflict with the setup_timeout at the top of the block. * Fix casecmp backward compatible to ruby < 2.5 I forgot that we haven't dropped ruby versions prior to 2.5, yet! * Apply pure_ruby multiplier to TIMEOUT_INTERVAL The pure_ruby tests also tend to be much slower than running the standard C++ reactor. * Update tests/test_httpclient2.rb --- tests/em_test_helper.rb | 21 +++++++++++++++++---- tests/test_httpclient2.rb | 15 ++++++--------- tests/test_resolver.rb | 2 +- tests/test_threaded_resource.rb | 2 +- 4 files changed, 25 insertions(+), 15 deletions(-) diff --git a/tests/em_test_helper.rb b/tests/em_test_helper.rb index 6ecfb6335..5b7b7e7bc 100644 --- a/tests/em_test_helper.rb +++ b/tests/em_test_helper.rb @@ -32,7 +32,7 @@ class Test::Unit::TestCase temp += %w[TLSv1 TLSv1_1 TLSv1_2] temp << 'TLSv1_3' if EM.const_defined? :EM_PROTO_TLSv1_3 temp.sort! - puts " SSL_AVAIL: #{temp.join(' ')}", "" + puts " SSL_AVAIL: #{temp.join(' ')}" SSL_AVAIL = temp.freeze else puts "\nEventMachine is not built with OpenSSL support, skipping tests in", @@ -147,13 +147,25 @@ def jruby? def rbx? defined?(RUBY_ENGINE) && RUBY_ENGINE == 'rbx' end + + def ci? + %w[1 t true y yes].any? {|t| t.casecmp(ENV.fetch("CI", "")).zero? } + end + + def pure_ruby_mode? + EM.library_type == :pure_ruby + end end include PlatformHelper extend PlatformHelper - # Tests may run slower on windows or Appveyor. YMMV - TIMEOUT_INTERVAL = windows? || darwin? ? 0.50 : 0.25 + # Tests may run slower on windows or CI. YMMV + ci_multiplier = ci? ? 4 : 1 + os_multiplier = (windows? || darwin?) ? 2 : 1 + rb_multiplier = pure_ruby_mode? ? 2 : 1 + TIMEOUT_INTERVAL = 0.25 * ci_multiplier * os_multiplier * rb_multiplier + puts " TIMEOUT_INTERVAL: #{TIMEOUT_INTERVAL}" module EMTestCasePrepend def setup @@ -175,7 +187,6 @@ def silent end end - private def self.get_my_ipv4_address ip @@ -198,3 +209,5 @@ def self.get_my_ipv6_address ip Socket.do_not_reverse_lookup = orig end end + +puts # empty line between debug output and test output diff --git a/tests/test_httpclient2.rb b/tests/test_httpclient2.rb index eaae34a03..1905ac990 100644 --- a/tests/test_httpclient2.rb +++ b/tests/test_httpclient2.rb @@ -6,9 +6,7 @@ class TestHttpClient2 < Test::Unit::TestCase class TestServer < EM::Connection end - TIMEOUT = TIMEOUT_INTERVAL * 4.0 - # below may be due to an issue with OpenSSL 1.0.2 and earlier with Windows - CI_WINDOWS_OLD = windows? and RUBY_VERSION < '2.5' + TIMEOUT = TIMEOUT_INTERVAL * 5.0 def setup @host = '127.0.0.1' @@ -58,7 +56,7 @@ def test_bad_server def test_get content = nil EM.run { - setup_timeout(CI_WINDOWS_OLD ? 4 : TIMEOUT) + setup_timeout(TIMEOUT) http = silent { EM::P::HttpClient2.connect :host => "www.google.com", :port => 80 } d = http.get "/" d.callback { @@ -93,12 +91,11 @@ def test_get_pipeline headers, headers2 = nil, nil EM.run { # intermittent CI failures, external server w/two requests? - setup_timeout (TIMEOUT * 2) + setup_timeout TIMEOUT * 2.5 http = silent { EM::P::HttpClient2.connect "www.google.com", 80 } - http.get("/").callback { |resp| headers = resp.headers } - http.get("/").callback { |resp| headers2 = resp.headers } + http.get("/").callback { |resp| headers = resp.headers }.errback { EM.stop } + http.get("/").callback { |resp| headers2 = resp.headers }.errback { EM.stop } EM.tick_loop { EM.stop if headers && headers2 } - EM.add_timer(1) { EM.stop } } assert(headers) assert(headers2) @@ -119,7 +116,7 @@ def test_https_get omit("No SSL") unless EM.ssl? d = nil EM.run { - setup_timeout(CI_WINDOWS_OLD ? 4 : TIMEOUT) + setup_timeout(TIMEOUT) http = silent { EM::P::HttpClient2.connect :host => 'www.google.com', :port => 443, :tls => true } d = http.get "/" d.callback {EM.stop} diff --git a/tests/test_resolver.rb b/tests/test_resolver.rb index 7cdfa3ba9..a9cd29bf0 100644 --- a/tests/test_resolver.rb +++ b/tests/test_resolver.rb @@ -3,7 +3,7 @@ class TestResolver < Test::Unit::TestCase # always true unless set - CI_WINDOWS = windows? && ENV.fetch('CI', 'true').casecmp('true').zero? + CI_WINDOWS = windows? && ci? def ci_windows_retries(err) if CI_WINDOWS and err.is_a? String and err[/retries exceeded/] diff --git a/tests/test_threaded_resource.rb b/tests/test_threaded_resource.rb index eca0ad5c8..2f0b23e07 100644 --- a/tests/test_threaded_resource.rb +++ b/tests/test_threaded_resource.rb @@ -19,7 +19,7 @@ def test_dispatch_completion EM.run do EM.add_timer(TIMEOUT_INTERVAL * 20) do EM.stop - if ENV['CI'].casecmp('true').zero? and RUBY_PLATFORM[/darwin/] + if ci? && darwin? notify "Intermittent Travis MacOS: Resource dispatch timed out" return else From 7df8ecda9c77335e7ec99c349039ef52c8bec038 Mon Sep 17 00:00:00 2001 From: nick evans Date: Tue, 10 Sep 2024 18:18:44 -0400 Subject: [PATCH 339/343] Mark failing tests as pending for pure ruby mode --- tests/test_attach.rb | 7 +++++ tests/test_basic.rb | 6 +++++ tests/test_completion.rb | 2 ++ tests/test_connection_count.rb | 5 ++++ tests/test_connection_write.rb | 1 + tests/test_deferrable.rb | 2 ++ tests/test_epoll.rb | 1 + tests/test_error_handler.rb | 1 + tests/test_exc.rb | 1 + tests/test_file_watch.rb | 2 ++ tests/test_inactivity_timeout.rb | 3 +++ tests/test_io_streamer.rb | 1 + tests/test_ipv4.rb | 4 ++- tests/test_iterator.rb | 5 ++++ tests/test_keepalive.rb | 3 +++ tests/test_pause.rb | 2 ++ tests/test_pending_connect_timeout.rb | 3 +++ tests/test_pool.rb | 4 +++ tests/test_processes.rb | 9 +++++++ tests/test_proxy_connection.rb | 4 +++ tests/test_resolver.rb | 6 +++++ tests/test_sock_opt.rb | 1 + tests/test_system.rb | 2 ++ tests/test_timers.rb | 38 ++++++++++++++------------- tests/test_unbind_reason.rb | 3 +++ 25 files changed, 97 insertions(+), 19 deletions(-) diff --git a/tests/test_attach.rb b/tests/test_attach.rb index 051a83cce..27a8f7784 100644 --- a/tests/test_attach.rb +++ b/tests/test_attach.rb @@ -44,6 +44,7 @@ def teardown end def test_attach + pend('FIXME: EM.attach_fd is broken in pure ruby mode') if pure_ruby_mode? socket = nil EM.run { @@ -68,6 +69,7 @@ def notify_readable end def test_attach_server + pend('FIXME: EM.attach_sd is broken in pure ruby mode') if pure_ruby_mode? omit_if(jruby?) $before = TCPServer.new("127.0.0.1", @port) sig = nil @@ -90,6 +92,7 @@ def initialize end def test_attach_pipe + pend('FIXME: EM.attach_fd is broken in pure ruby mode') if pure_ruby_mode? EM.run{ $r, $w = IO.pipe EM.watch $r, PipeWatch do |c| @@ -102,6 +105,7 @@ def test_attach_pipe end def test_set_readable + pend('FIXME: EM.attach_fd is broken in pure ruby mode') if pure_ruby_mode? before, after = nil EM.run{ @@ -125,6 +129,7 @@ def test_set_readable end def test_read_write_pipe + pend('FIXME: EM.attach_fd is broken in pure ruby mode') if pure_ruby_mode? result = nil pipe_reader = Module.new do @@ -152,6 +157,7 @@ def test_read_write_pipe # This test shows that watch_only? is true for EM.watch def test_watch_only + pend('FIXME: EM.attach_fd is broken in pure ruby mode') if pure_ruby_mode? r, w = IO.pipe $watch_only = nil @@ -175,6 +181,7 @@ def c.notify_readable # This test shows that watch_only? is false for EM.attach def test_attach_data + pend('FIXME: EM.attach_fd is broken in pure ruby mode') if pure_ruby_mode? pend("\nFIXME: Freezes Windows testing as of 2018-07-31") if windows? r, w = IO.pipe $watch_only = nil diff --git a/tests/test_basic.rb b/tests/test_basic.rb index b0fca1c14..ff543cdc0 100644 --- a/tests/test_basic.rb +++ b/tests/test_basic.rb @@ -96,6 +96,7 @@ def unbind end def test_unbind_error_during_stop + pend('FIXME: This test is broken in pure ruby mode') if pure_ruby_mode? assert_raises( UnbindError::ERR ) { EM.run { EM.start_server "127.0.0.1", @port @@ -175,6 +176,7 @@ def test_bind_connect end def test_invalid_address_bind_connect_dst + pend('FIXME: A different error is raised in pure ruby mode') if pure_ruby_mode? pend("\nFIXME: Windows as of 2018-06-23 on 32 bit >= 2.4 (#{RUBY_VERSION} #{RUBY_PLATFORM})") if RUBY_PLATFORM[/i386-mingw/] && RUBY_VERSION >= '2.4' e = nil EM.run do @@ -192,6 +194,7 @@ def test_invalid_address_bind_connect_dst end def test_invalid_address_bind_connect_src + pend('FIXME: A different error is raised in pure ruby mode') if pure_ruby_mode? pend("\nFIXME: Windows as of 2018-06-23 on 32 bit >= 2.4 (#{RUBY_VERSION} #{RUBY_PLATFORM})") if RUBY_PLATFORM[/i386-mingw/] && RUBY_VERSION >= '2.4' e = nil EM.run do @@ -232,6 +235,7 @@ def test_schedule_from_thread end def test_set_heartbeat_interval + pend('FIXME: EM.set_heartbeat_interval is broken in pure ruby mode') if pure_ruby_mode? omit_if(jruby?) interval = 0.5 EM.run { @@ -275,6 +279,7 @@ def test_bubble_errors_from_initialize end def test_schedule_close + pend('FIXME: EM.num_close_scheduled is broken in pure ruby mode') if pure_ruby_mode? omit_if(jruby?) localhost, port = '127.0.0.1', 9000 timer_ran = false @@ -297,6 +302,7 @@ def c.unbind end def test_error_handler_idempotent # issue 185 + pend('FIXME: EM.error_handler is broken in pure ruby mode') if pure_ruby_mode? errors = [] ticks = [] EM.error_handler do |e| diff --git a/tests/test_completion.rb b/tests/test_completion.rb index ce1489a7a..8adeea5fb 100644 --- a/tests/test_completion.rb +++ b/tests/test_completion.rb @@ -7,6 +7,7 @@ def completion end def crank + pend("FIXME: pure ruby mode next_tick queue is broken for EM.stop") if pure_ruby_mode? # This is a slow solution, but this just executes the next tick queue # once. It's the easiest way for now. EM.run { EM.stop } @@ -156,6 +157,7 @@ def test_latent_completion end def test_timeout + pend("FIXME: This test is broken in pure ruby mode") if pure_ruby_mode? args = [1, 2, 3] EM.run do completion.timeout(0.0001, *args) diff --git a/tests/test_connection_count.rb b/tests/test_connection_count.rb index 9ef482a38..e745a7121 100644 --- a/tests/test_connection_count.rb +++ b/tests/test_connection_count.rb @@ -7,6 +7,7 @@ def teardown end def test_idle_connection_count + pend('FIXME: EM.connection_count is broken in pure ruby mode') if pure_ruby_mode? count = nil EM.run { count = EM.connection_count @@ -17,6 +18,7 @@ def test_idle_connection_count # Run this again with epoll enabled (if available) def test_idle_connection_count_epoll + pend('FIXME: EM.connection_count is broken in pure ruby mode') if pure_ruby_mode? EM.epoll if EM.epoll? count = nil @@ -29,6 +31,7 @@ def test_idle_connection_count_epoll # Run this again with kqueue enabled (if available) def test_idle_connection_count_kqueue + pend('FIXME: EM.connection_count is broken in pure ruby mode') if pure_ruby_mode? EM.kqueue if EM.kqueue? count = nil @@ -47,6 +50,7 @@ def connection_completed end def test_with_some_connections + pend('FIXME: EM.connection_count is broken in pure ruby mode') if pure_ruby_mode? EM.run { $client_conns = 0 $initial_conns = EM.connection_count @@ -72,6 +76,7 @@ def unbind end def test_num_close_scheduled + pend('FIXME: EM.num_close_scheduled is broken in pure ruby mode') if pure_ruby_mode? omit_if(jruby?) EM.run { assert_equal(0, EM.num_close_scheduled) diff --git a/tests/test_connection_write.rb b/tests/test_connection_write.rb index e5fb39341..934ed750b 100644 --- a/tests/test_connection_write.rb +++ b/tests/test_connection_write.rb @@ -18,6 +18,7 @@ def notify_writable end def test_with_naughty_callback + pend('FIXME: EM.attach_fd is broken in pure ruby mode') if pure_ruby_mode? EM.run do r1, _ = IO.pipe r2, _ = IO.pipe diff --git a/tests/test_deferrable.rb b/tests/test_deferrable.rb index 0138e0202..bfde23a35 100644 --- a/tests/test_deferrable.rb +++ b/tests/test_deferrable.rb @@ -6,6 +6,7 @@ class Later end def test_timeout_without_args + pend('FIXME: this test is broken in pure ruby mode') if pure_ruby_mode? assert_nothing_raised do EM.run { df = Later.new @@ -17,6 +18,7 @@ def test_timeout_without_args end def test_timeout_with_args + pend('FIXME: this test is broken in pure ruby mode') if pure_ruby_mode? args = nil EM.run { diff --git a/tests/test_epoll.rb b/tests/test_epoll.rb index 64a431c8c..2b438e822 100644 --- a/tests/test_epoll.rb +++ b/tests/test_epoll.rb @@ -123,6 +123,7 @@ def _test_unix_domain end def test_attach_detach + pend('FIXME: EM.attach_fd is broken in pure ruby mode') if pure_ruby_mode? EM.epoll EM.run { EM.add_timer(0.01) { EM.stop } diff --git a/tests/test_error_handler.rb b/tests/test_error_handler.rb index 5cef72e76..59cffa7e6 100644 --- a/tests/test_error_handler.rb +++ b/tests/test_error_handler.rb @@ -6,6 +6,7 @@ def setup end def test_error_handler + pend('FIXME: this test is broken in pure ruby mode') if pure_ruby_mode? error = nil EM.error_handler{ |e| diff --git a/tests/test_exc.rb b/tests/test_exc.rb index 713880f2e..0ab521454 100644 --- a/tests/test_exc.rb +++ b/tests/test_exc.rb @@ -29,6 +29,7 @@ def test_b end def test_exception_on_unbind + pend('FIXME: this test is broken in pure ruby mode') if pure_ruby_mode? assert_raises(DoomedConnectionError) { EM.run { EM.connect("localhost", 8888, DoomedConnection) } } diff --git a/tests/test_file_watch.rb b/tests/test_file_watch.rb index c27ca2c9e..de55fd286 100644 --- a/tests/test_file_watch.rb +++ b/tests/test_file_watch.rb @@ -35,6 +35,7 @@ def teardown end def test_events + pend('FIXME: EM.watch_filename is broken in pure ruby mode') if pure_ruby_mode? omit_if(solaris?) EM.run{ file = Tempfile.new('em-watch') @@ -59,6 +60,7 @@ def test_events # Refer: https://github.com/eventmachine/eventmachine/issues/512 def test_invalid_signature + pend('FIXME: EM.watch_filename is broken in pure ruby mode') if pure_ruby_mode? # This works fine with kqueue, only fails with linux inotify. omit_if(EM.kqueue?) diff --git a/tests/test_inactivity_timeout.rb b/tests/test_inactivity_timeout.rb index 2cd02e4a9..ab42cec7e 100644 --- a/tests/test_inactivity_timeout.rb +++ b/tests/test_inactivity_timeout.rb @@ -4,6 +4,7 @@ class TestInactivityTimeout < Test::Unit::TestCase if EM.respond_to? :get_comm_inactivity_timeout def test_default + pend('FIXME: EM.get_comm_inactivity_timeout is broken in pure ruby mode') if pure_ruby_mode? EM.run { c = EM.connect("127.0.0.1", 54321) assert_equal 0.0, c.comm_inactivity_timeout @@ -12,6 +13,7 @@ def test_default end def test_set_and_get + pend('FIXME: EM.get_comm_inactivity_timeout is broken in pure ruby mode') if pure_ruby_mode? EM.run { c = EM.connect("127.0.0.1", 54321) c.comm_inactivity_timeout = 2.5 @@ -21,6 +23,7 @@ def test_set_and_get end def test_for_real + pend('FIXME: EM.set_heartbeat_interval is broken in pure ruby mode') if pure_ruby_mode? start, finish, reason = nil timeout_start = Module.new do diff --git a/tests/test_io_streamer.rb b/tests/test_io_streamer.rb index bfc24ba45..2fefb2703 100644 --- a/tests/test_io_streamer.rb +++ b/tests/test_io_streamer.rb @@ -36,6 +36,7 @@ def unbind end def test_io_stream + pend('FIXME: this test is broken in pure ruby mode') if pure_ruby_mode? sent = 'this is a test' received = ''.dup EM.run do diff --git a/tests/test_ipv4.rb b/tests/test_ipv4.rb index 723dd6884..2aa03f90e 100644 --- a/tests/test_ipv4.rb +++ b/tests/test_ipv4.rb @@ -54,9 +54,10 @@ def s.receive_data data # Try to connect via TCP to an invalid IPv4. EM.connect should raise # EM::ConnectionError. def test_tcp_connect_to_invalid_ipv4 + pend('FIXME: pure ruby mode should raise EM::ConnectionError') if pure_ruby_mode? omit_if(!Test::Unit::TestCase.public_ipv4?) pend("\nFIXME: Windows as of 2018-06-23 on 32 bit >= 2.4 (#{RUBY_VERSION} #{RUBY_PLATFORM})") if RUBY_PLATFORM[/i386-mingw/] && RUBY_VERSION >= '2.4' - + invalid_ipv4 = "9.9:9" EM.run do @@ -75,6 +76,7 @@ def test_tcp_connect_to_invalid_ipv4 # Try to send a UDP datagram to an invalid IPv4. EM.send_datagram should raise # EM::ConnectionError. def test_udp_send_datagram_to_invalid_ipv4 + pend('FIXME: pure ruby mode should raise EM::ConnectionError') if pure_ruby_mode? omit_if(!Test::Unit::TestCase.public_ipv4?) invalid_ipv4 = "9.9:9" diff --git a/tests/test_iterator.rb b/tests/test_iterator.rb index ec7724dbf..e967a06fe 100644 --- a/tests/test_iterator.rb +++ b/tests/test_iterator.rb @@ -15,6 +15,7 @@ def get_time(n = 1) end def test_default_concurrency + pend('FIXME: EM.current_time is broken in pure ruby mode') if pure_ruby_mode? items = {} list = 1..10 EM.run { @@ -30,6 +31,7 @@ def test_default_concurrency end def test_default_concurrency_with_a_proc + pend('FIXME: EM.current_time is broken in pure ruby mode') if pure_ruby_mode? items = {} list = (1..10).to_a original_list = list.dup @@ -46,6 +48,7 @@ def test_default_concurrency_with_a_proc end def test_concurrency_bigger_than_list_size + pend('FIXME: EM.current_time is broken in pure ruby mode') if pure_ruby_mode? items = {} list = [1,2,3] EM.run { @@ -61,6 +64,7 @@ def test_concurrency_bigger_than_list_size end def test_changing_concurrency_affects_active_iteration + pend('FIXME: EM.current_time is broken in pure ruby mode') if pure_ruby_mode? items = {} list = 1..25 seen = 0 @@ -95,6 +99,7 @@ def test_map end def test_inject + pend('FIXME: EM.invoke_popen is broken in pure ruby mode') if pure_ruby_mode? omit_if(windows?) list = %w[ pwd uptime uname date ] diff --git a/tests/test_keepalive.rb b/tests/test_keepalive.rb index bd6202a53..2713971eb 100644 --- a/tests/test_keepalive.rb +++ b/tests/test_keepalive.rb @@ -11,6 +11,7 @@ def teardown end def test_enable_keepalive + pend('FIXME: this test is broken in pure ruby mode') if pure_ruby_mode? omit_if(!EM.respond_to?(:get_sock_opt)) # I don't know why "An operation was attempted on something that is not a socket." @@ -36,6 +37,7 @@ def test_enable_keepalive end def test_enable_keepalive_values + pend('FIXME: this test is broken in pure ruby mode') if pure_ruby_mode? omit_if(!EM.respond_to?(:get_sock_opt)) # I don't know why "An operation was attempted on something that is not a socket." @@ -88,6 +90,7 @@ def test_enable_keepalive_values end def test_disable_keepalive + pend('FIXME: this test is broken in pure ruby mode') if pure_ruby_mode? omit_if(!EM.respond_to?(:get_sock_opt)) # I don't know why "An operation was attempted on something that is not a socket." diff --git a/tests/test_pause.rb b/tests/test_pause.rb index 3526ebd37..c87abe309 100644 --- a/tests/test_pause.rb +++ b/tests/test_pause.rb @@ -11,6 +11,7 @@ def teardown end def test_pause_resume + pend('FIXME: EM.pause_connection is broken in pure ruby mode') if pure_ruby_mode? server = nil s_rx = c_rx = 0 @@ -71,6 +72,7 @@ def post_init end def test_pause_in_receive_data + pend('FIXME: EM.pause_connection is broken in pure ruby mode') if pure_ruby_mode? incoming = [] test_server = Module.new do diff --git a/tests/test_pending_connect_timeout.rb b/tests/test_pending_connect_timeout.rb index 4bd8f8d3b..271c07947 100644 --- a/tests/test_pending_connect_timeout.rb +++ b/tests/test_pending_connect_timeout.rb @@ -4,6 +4,7 @@ class TestPendingConnectTimeout < Test::Unit::TestCase if EM.respond_to? :get_pending_connect_timeout def test_default + pend('FIXME: EM.get_pending_connect_timeout is broken in pure ruby mode') if pure_ruby_mode? EM.run { c = EM.connect("127.0.0.1", 54321) assert_equal 20.0, c.pending_connect_timeout @@ -12,6 +13,7 @@ def test_default end def test_set_and_get + pend('FIXME: EM.get_pending_connect_timeout is broken in pure ruby mode') if pure_ruby_mode? EM.run { c = EM.connect("127.0.0.1", 54321) c.pending_connect_timeout = 2.5 @@ -21,6 +23,7 @@ def test_set_and_get end def test_for_real + pend('FIXME: EM.get_pending_connect_timeout is broken in pure ruby mode') if pure_ruby_mode? start, finish = nil timeout_handler = Module.new do diff --git a/tests/test_pool.rb b/tests/test_pool.rb index 83ba165b9..0a04a243d 100644 --- a/tests/test_pool.rb +++ b/tests/test_pool.rb @@ -35,6 +35,7 @@ def test_supports_more_work_than_resources end def test_reques_resources_on_error + pend('FIXME: this test is broken in pure ruby mode') if pure_ruby_mode? pooled_res, pooled_res2 = nil pool.add :res go do @@ -57,6 +58,7 @@ def test_reques_resources_on_error end def test_supports_custom_on_error + pend('FIXME: this test is broken in pure ruby mode') if pure_ruby_mode? eres = nil pool.on_error do |res| eres = res @@ -82,6 +84,7 @@ def test_supports_custom_on_error end def test_catches_successful_deferrables + pend('FIXME: this test is broken in pure ruby mode') if pure_ruby_mode? performs = [] pool.add :res go do @@ -96,6 +99,7 @@ def test_catches_successful_deferrables end def test_prunes_locked_and_removed_resources + pend('FIXME: this test is broken in pure ruby mode') if pure_ruby_mode? performs = [] pool.add :res deferrable.succeed diff --git a/tests/test_processes.rb b/tests/test_processes.rb index 9546022e7..2c0779da1 100644 --- a/tests/test_processes.rb +++ b/tests/test_processes.rb @@ -15,6 +15,7 @@ class TestProcesses < Test::Unit::TestCase # wrote to stdout. # def test_deferrable_child_process + pend('FIXME: this test is broken in pure ruby mode') if pure_ruby_mode? ls = "" EM.run { d = EM::DeferrableChildProcess.open( "ls -ltr" ) @@ -32,6 +33,7 @@ def setup end def test_em_system + pend('FIXME: this test is broken in pure ruby mode') if pure_ruby_mode? out, status = nil, nil EM.run{ @@ -44,6 +46,7 @@ def test_em_system end def test_em_system_bad_exitstatus + pend('FIXME: this test is broken in pure ruby mode') if pure_ruby_mode? status = nil sys_pid = nil @@ -57,6 +60,7 @@ def test_em_system_bad_exitstatus end def test_em_system_pid + pend('FIXME: this test is broken in pure ruby mode') if pure_ruby_mode? status = nil sys_pid = nil @@ -71,6 +75,7 @@ def test_em_system_pid end def test_em_system_with_proc + pend('FIXME: this test is broken in pure ruby mode') if pure_ruby_mode? EM.run{ EM.system('ls', proc{ |out,status| $out, $status = out, status; EM.stop }) } @@ -81,6 +86,7 @@ def test_em_system_with_proc end def test_em_system_with_two_procs + pend('FIXME: this test is broken in pure ruby mode') if pure_ruby_mode? EM.run{ EM.system('sh', proc{ |process| process.send_data("echo hello\n") @@ -96,6 +102,7 @@ def test_em_system_with_two_procs end def test_em_system_cmd_arguments + pend('FIXME: this test is broken in pure ruby mode') if pure_ruby_mode? EM.run{ EM.system('echo', '1', '2', 'version', proc{ |process| }, proc{ |out,status| @@ -109,6 +116,7 @@ def test_em_system_cmd_arguments end def test_em_system_spaced_arguments + pend('FIXME: this test is broken in pure ruby mode') if pure_ruby_mode? EM.run{ EM.system('ruby', '-e', 'puts "hello"', proc{ |out,status| $out = out @@ -120,6 +128,7 @@ def test_em_system_spaced_arguments end def test_em_popen_pause_resume + pend('FIXME: this test is broken in pure ruby mode') if pure_ruby_mode? c_rx = 0 test_client = Module.new do diff --git a/tests/test_proxy_connection.rb b/tests/test_proxy_connection.rb index 2b2eaa76e..38513bd26 100644 --- a/tests/test_proxy_connection.rb +++ b/tests/test_proxy_connection.rb @@ -126,6 +126,7 @@ def setup end def test_proxy_connection + pend('FIXME: EM.start_proxy is broken in pure ruby mode') if pure_ruby_mode? EM.run { EM.start_server("127.0.0.1", @port, Server) EM.start_server("127.0.0.1", @proxy_port, ProxyServer, @port) @@ -136,6 +137,7 @@ def test_proxy_connection end def test_proxied_bytes + pend('FIXME: EM.start_proxy is broken in pure ruby mode') if pure_ruby_mode? EM.run { EM.start_server("127.0.0.1", @port, Server) EM.start_server("127.0.0.1", @proxy_port, ProxyServer, @port) @@ -147,6 +149,7 @@ def test_proxied_bytes end def test_partial_proxy_connection + pend('FIXME: EM.start_proxy is broken in pure ruby mode') if pure_ruby_mode? EM.run { EM.start_server("127.0.0.1", @port, Server) EM.start_server("127.0.0.1", @proxy_port, PartialProxyServer, @port) @@ -159,6 +162,7 @@ def test_partial_proxy_connection end def test_early_close + pend('FIXME: EM.start_proxy is broken in pure ruby mode') if pure_ruby_mode? $client_data = nil EM.run { EM.start_server("127.0.0.1", @port, Server) diff --git a/tests/test_resolver.rb b/tests/test_resolver.rb index a9cd29bf0..d36c0d47e 100644 --- a/tests/test_resolver.rb +++ b/tests/test_resolver.rb @@ -31,6 +31,7 @@ def test_hosts end def test_a + pend('FIXME: this test is broken in pure ruby mode') if pure_ruby_mode? pend('FIXME: this test is broken on Windows') if windows? && RUBY_VERSION < "2.4" EM.run { @@ -47,6 +48,7 @@ def test_a end def test_bad_host + pend('FIXME: this test is broken in pure ruby mode') if pure_ruby_mode? EM.run { d = EM::DNS::Resolver.resolve "asdfasasdf" d.callback { assert false } @@ -55,6 +57,7 @@ def test_bad_host end def test_garbage + pend('FIXME: this test is broken in pure ruby mode') if pure_ruby_mode? assert_raises( ArgumentError ) { EM.run { EM::DNS::Resolver.resolve 123 @@ -82,6 +85,7 @@ def test_a_pair def test_localhost pend('FIXME: this test is broken on Windows') if windows? + pend('FIXME: this test is broken in pure ruby mode') if pure_ruby_mode? EM.run { d = EM::DNS::Resolver.resolve "localhost" @@ -97,6 +101,7 @@ def test_localhost end def test_timer_cleanup + pend('FIXME: this test is broken in pure ruby mode') if pure_ruby_mode? pend('FIXME: this test is broken on Windows') if windows? && RUBY_VERSION < "2.4" EM.run { @@ -116,6 +121,7 @@ def test_timer_cleanup end def test_failure_timer_cleanup + pend('FIXME: this test is broken in pure ruby mode') if pure_ruby_mode? EM.run { d = EM::DNS::Resolver.resolve "asdfasdf" d.callback { assert false } diff --git a/tests/test_sock_opt.rb b/tests/test_sock_opt.rb index dedf3c955..f59b7c460 100644 --- a/tests/test_sock_opt.rb +++ b/tests/test_sock_opt.rb @@ -31,6 +31,7 @@ def test_set_sock_opt end def test_get_sock_opt + pend('FIXME: this test is broken in pure ruby mode') if pure_ruby_mode? omit_if(windows?) omit_if(!EM.respond_to?(:set_sock_opt)) diff --git a/tests/test_system.rb b/tests/test_system.rb index 184f47bed..97e055f8d 100644 --- a/tests/test_system.rb +++ b/tests/test_system.rb @@ -9,6 +9,7 @@ def setup end def test_system + pend('FIXME: this test is broken in pure ruby mode') if pure_ruby_mode? omit_if(windows?) result = nil @@ -25,6 +26,7 @@ def test_system end def test_system_with_string + pend('FIXME: this test is broken in pure ruby mode') if pure_ruby_mode? omit_if(windows?) result = nil diff --git a/tests/test_timers.rb b/tests/test_timers.rb index 7c5d4017f..f980c1be7 100644 --- a/tests/test_timers.rb +++ b/tests/test_timers.rb @@ -102,6 +102,7 @@ def test_oneshot_timer_large_future_value end def test_add_timer_increments_timer_count + pend('FIXME: this test is broken in pure ruby mode') if pure_ruby_mode? EM.run { n = EM.get_timer_count EM::Timer.new(0.01) { @@ -112,6 +113,7 @@ def test_add_timer_increments_timer_count end def test_timer_run_decrements_timer_count + pend('FIXME: this test is broken in pure ruby mode') if pure_ruby_mode? EM.run { n = EM.get_timer_count EM::Timer.new(0.01) { @@ -123,28 +125,28 @@ def test_timer_run_decrements_timer_count # This test is only applicable to compiled versions of the reactor. # Pure ruby and java versions have no built-in limit on the number of outstanding timers. - unless [:pure_ruby, :java].include? EM.library_type - def test_timer_change_max_outstanding - defaults = EM.get_max_timers - EM.set_max_timers(100) - - one_hundred_one_timers = lambda do - 101.times { EM.add_timer(0.01) {} } - EM.stop - end + def test_timer_change_max_outstanding + omit_if jruby? + pend('FIXME: this test is broken in pure ruby mode') if pure_ruby_mode? + defaults = EM.get_max_timers + EM.set_max_timers(100) + + one_hundred_one_timers = lambda do + 101.times { EM.add_timer(0.01) {} } + EM.stop + end - assert_raises(RuntimeError) do - EM.run( &one_hundred_one_timers ) - end + assert_raises(RuntimeError) do + EM.run( &one_hundred_one_timers ) + end - EM.set_max_timers( 101 ) + EM.set_max_timers( 101 ) - assert_nothing_raised do - EM.run( &one_hundred_one_timers ) - end - ensure - EM.set_max_timers(defaults) + assert_nothing_raised do + EM.run( &one_hundred_one_timers ) end + ensure + EM.set_max_timers(defaults) end end diff --git a/tests/test_unbind_reason.rb b/tests/test_unbind_reason.rb index 76029e827..22f5b1da6 100644 --- a/tests/test_unbind_reason.rb +++ b/tests/test_unbind_reason.rb @@ -12,6 +12,7 @@ def unbind(reason = nil) # RFC 5737 Address Blocks Reserved for Documentation def test_connect_timeout + pend('FIXME: this test is broken in pure ruby mode') if pure_ruby_mode? conn = nil EM.run do conn = EM.connect '192.0.2.0', 80, StubConnection @@ -22,6 +23,7 @@ def test_connect_timeout def test_connect_refused pend('FIXME: this test is broken on Windows') if windows? + pend('FIXME: this test is broken in pure ruby mode') if pure_ruby_mode? conn = nil EM.run do conn = EM.connect '127.0.0.1', 12388, StubConnection @@ -31,6 +33,7 @@ def test_connect_refused def test_optional_argument pend('FIXME: this test is broken on Windows') if windows? + pend('FIXME: this test is broken in pure ruby mode') if pure_ruby_mode? conn = nil EM.run do conn = EM.connect '127.0.0.1', 12388, StubConnection From fbec14beaa161d9375033ed51d28b1a6610f105c Mon Sep 17 00:00:00 2001 From: nick evans Date: Wed, 11 Sep 2024 10:45:55 -0400 Subject: [PATCH 340/343] Change test_em_pure_ruby to execute *all* tests The tests have been updated with "pend" or "omit", so the test suite should still pass (albeit with a long "TODO list" in the output). --- rakelib/test_pure.rake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rakelib/test_pure.rake b/rakelib/test_pure.rake index 0567a46e1..a2db72d40 100644 --- a/rakelib/test_pure.rake +++ b/rakelib/test_pure.rake @@ -7,7 +7,7 @@ end task :test_em_pure_ruby do ENV['EM_PURE_RUBY'] = 'true' - Rake::Task['test_pure'].execute + Rake::Task['test'].execute end task test_em_pure_ruby: "test:fixtures" From 94921975e52591556e956f202b506fa43dc0a334 Mon Sep 17 00:00:00 2001 From: nick evans Date: Wed, 11 Sep 2024 10:47:15 -0400 Subject: [PATCH 341/343] Drop `rake test_pure` There's no longer any special reason to single out just these tests, so this rake task is obsolete. --- rakelib/test_pure.rake | 5 ----- 1 file changed, 5 deletions(-) diff --git a/rakelib/test_pure.rake b/rakelib/test_pure.rake index a2db72d40..642f24e3c 100644 --- a/rakelib/test_pure.rake +++ b/rakelib/test_pure.rake @@ -1,10 +1,5 @@ require 'rake/testtask' -Rake::TestTask.new(:test_pure) do |t| - t.test_files = Dir.glob('tests/**/test_pure*.rb') + Dir.glob('tests/**/test_ssl*.rb') - t.warning = true -end - task :test_em_pure_ruby do ENV['EM_PURE_RUBY'] = 'true' Rake::Task['test'].execute From a6cbdf17f1d40cbc8d6de5484bc2e849fe090c5b Mon Sep 17 00:00:00 2001 From: nick evans Date: Thu, 12 Sep 2024 12:39:21 -0400 Subject: [PATCH 342/343] Add stub epoll/kqueue methods for pure ruby mode This stubbing is just enough to get the rest of the test suite running. It should be safe, as `EM.kqueue?` and `EM.epoll?` already exist for feature detection. The implementation here basically mimics the implementation in ext/rubymain.cpp. --- lib/em/pure_ruby.rb | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/lib/em/pure_ruby.rb b/lib/em/pure_ruby.rb index 53854cb3d..2e419c6fb 100644 --- a/lib/em/pure_ruby.rb +++ b/lib/em/pure_ruby.rb @@ -242,6 +242,26 @@ def set_timer_quantum interval def epoll end + # Pure ruby mode does not allow setting epoll + # @private + def epoll=(bool) + bool and raise Unsupported, "EM.epoll is not supported in pure_ruby mode" + end + + # Pure ruby mode does not support epoll + # @private + def epoll?; false end + + # Pure ruby mode does not support kqueue + # @private + def kqueue?; false end + + # Pure ruby mode does not allow setting kqueue + # @private + def kqueue=(bool) + bool and raise Unsupported, "EM.kqueue is not supported in pure_ruby mode" + end + # @private def ssl? true From e7320417cf291cc6a69471a64ecae5ddb5367715 Mon Sep 17 00:00:00 2001 From: "nicholas a. evans" Date: Mon, 16 Sep 2024 15:37:09 -0400 Subject: [PATCH 343/343] Run SSL verify tests in pure ruby CI (#1007) These all pass now. I believe they were fixed by 0c0c715 in #993. --- tests/test_ssl_verify.rb | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tests/test_ssl_verify.rb b/tests/test_ssl_verify.rb index e9643cbc4..31dc4b564 100644 --- a/tests/test_ssl_verify.rb +++ b/tests/test_ssl_verify.rb @@ -35,7 +35,6 @@ def test_fail_no_peer_cert end def test_accept_server - omit_if(EM.library_type == :pure_ruby) # Server has a default cert chain omit_if(rbx?) server = { verify_peer: true, ssl_verify_result: true } @@ -48,7 +47,6 @@ def test_accept_server end def test_accept_client - omit_if(EM.library_type == :pure_ruby) # Server has a default cert chain omit_if(rbx?) client = { verify_peer: true, ssl_verify_result: true } @@ -61,7 +59,6 @@ def test_accept_client end def test_encoded_accept_server - omit_if(EM.library_type == :pure_ruby) # Server has a default cert chain omit_if(rbx?) server = { verify_peer: true, ssl_verify_result: true } @@ -74,7 +71,6 @@ def test_encoded_accept_server end def test_encoded_accept_client - omit_if(EM.library_type == :pure_ruby) # Server has a default cert chain omit_if(rbx?) client = { verify_peer: true, ssl_verify_result: true } @@ -87,7 +83,6 @@ def test_encoded_accept_client end def test_deny_server - omit_if(EM.library_type == :pure_ruby) # Server has a default cert chain omit_if(rbx?) server = { verify_peer: true, ssl_verify_result: false } @@ -100,7 +95,6 @@ def test_deny_server end def test_deny_client - omit_if(EM.library_type == :pure_ruby) # Server has a default cert chain omit_if(rbx?) client = { verify_peer: true, ssl_verify_result: false }