├── Gemfile ├── lib └── em │ ├── version.rb │ ├── deferrable │ └── pool.rb │ ├── protocols │ ├── line_protocol.rb │ ├── object_protocol.rb │ ├── tcptest.rb │ ├── socks4.rb │ └── header_and_content.rb │ ├── process_watch.rb │ ├── timers.rb │ ├── protocols.rb │ ├── channel.rb │ ├── future.rb │ ├── queue.rb │ ├── file_watch.rb │ ├── callback.rb │ ├── tick_loop.rb │ ├── spawnable.rb │ ├── messages.rb │ ├── threaded_resource.rb │ ├── streamer.rb │ ├── processes.rb │ └── buftok.rb ├── examples ├── old │ ├── ex_queue.rb │ ├── helper.rb │ ├── ex_tick_loop_array.rb │ ├── ex_tick_loop_counter.rb │ └── ex_channel.rb └── guides │ └── getting_started │ ├── 01_eventmachine_echo_server.rb │ ├── 02_eventmachine_echo_server_that_recognizes_exit_command.rb │ ├── 04_simple_chat_server_step_one.rb │ ├── 05_simple_chat_server_step_two.rb │ ├── 06_simple_chat_server_step_three.rb │ ├── 07_simple_chat_server_step_four.rb │ ├── 08_simple_chat_server_step_five.rb │ └── 03_simple_chat_server.rb ├── tests ├── test_ud.rb ├── test_running.rb ├── test_defer.rb ├── test_shutdown_hooks.rb ├── test_many_fds.rb ├── test_idle_connection.rb ├── test_exc.rb ├── test_line_protocol.rb ├── test_handler_check.rb ├── test_servers.rb ├── test_error_handler.rb ├── test_deferrable.rb ├── test_object_protocol.rb ├── test_kb.rb ├── test_set_sock_opt.rb ├── test_get_sock_opt.rb ├── test_system.rb ├── test_stomp.rb ├── test_ssl_methods.rb ├── test_process_watch.rb ├── test_sasl.rb ├── test_queue.rb ├── test_unbind_reason.rb ├── test_pending_connect_timeout.rb ├── test_channel.rb ├── test_smtpclient.rb ├── test_connection_count.rb ├── test_inactivity_timeout.rb ├── em_test_helper.rb ├── test_threaded_resource.rb ├── test_tick_loop.rb ├── test_file_watch.rb ├── test_smtpserver.rb ├── client.crt ├── test_resolver.rb ├── test_ssl_args.rb ├── test_ssl_verify.rb ├── test_iterator.rb ├── test_pure.rb ├── test_pause.rb ├── test_next_tick.rb ├── test_timers.rb ├── client.key ├── test_httpclient2.rb ├── test_processes.rb ├── test_attach.rb ├── test_ltp.rb ├── test_epoll.rb └── test_proxy_connection.rb ├── .yardopts ├── docs ├── old │ ├── SMTP │ ├── TODO │ ├── INSTALL │ ├── LEGAL │ ├── KEYBOARD │ ├── RELEASE_NOTES │ └── PURE_RUBY └── DocumentationGuidesIndex.md ├── rakelib ├── test.rake ├── cpp.rake_example └── package.rake ├── .gitignore ├── .travis.yml ├── java ├── .classpath ├── .project └── src │ └── com │ └── rubyeventmachine │ ├── EmReactorException.java │ └── EventableChannel.java ├── Rakefile ├── ext ├── binder.h ├── page.h ├── fastfilereader │ ├── mapper.h │ ├── extconf.rb │ └── rubymain.cpp ├── kb.cpp ├── page.cpp ├── ssl.h ├── binder.cpp └── project.h ├── eventmachine.gemspec ├── CHANGELOG.md ├── LICENSE └── README.md /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | gemspec 3 | -------------------------------------------------------------------------------- /lib/em/version.rb: -------------------------------------------------------------------------------- 1 | module EventMachine 2 | VERSION = "1.0.4" 3 | end 4 | -------------------------------------------------------------------------------- /examples/old/ex_queue.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/helper' 2 | 3 | -------------------------------------------------------------------------------- /examples/old/helper.rb: -------------------------------------------------------------------------------- 1 | $:.unshift File.expand_path(File.dirname(__FILE__) + '/../lib') 2 | require 'eventmachine' -------------------------------------------------------------------------------- /lib/em/deferrable/pool.rb: -------------------------------------------------------------------------------- 1 | warn "EM::Deferrable::Pool is deprecated, please use EM::Pool" 2 | EM::Deferrable::Pool = EM::Pool -------------------------------------------------------------------------------- /tests/test_ud.rb: -------------------------------------------------------------------------------- 1 | require 'em_test_helper' 2 | 3 | class TestUserDefinedEvents < Test::Unit::TestCase 4 | 5 | def test_a 6 | end 7 | 8 | end 9 | -------------------------------------------------------------------------------- /.yardopts: -------------------------------------------------------------------------------- 1 | --no-private 2 | --protected 3 | --markup="markdown" lib/**/*.rb 4 | --main README.md 5 | --exclude jeventmachine --exclude pure_ruby 6 | - 7 | docs/*.md -------------------------------------------------------------------------------- /docs/old/SMTP: -------------------------------------------------------------------------------- 1 | This note details the usage of EventMachine's built-in support for SMTP. EM 2 | supports both client and server connections, which will be described in 3 | separate sections. 4 | 5 | -------------------------------------------------------------------------------- /rakelib/test.rake: -------------------------------------------------------------------------------- 1 | require 'rake/testtask' 2 | 3 | Rake::TestTask.new(:test) do |t| 4 | t.libs << "tests" 5 | t.libs << "lib" 6 | t.pattern = 'tests/**/test_*.rb' 7 | t.warning = true 8 | end 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | pkg 2 | rdoc 3 | Makefile 4 | 5 | *.bundle 6 | *.dll 7 | *.so 8 | *.jar 9 | *.class 10 | *.o 11 | *.log 12 | *.def 13 | *.pdb 14 | *.dSYM 15 | java/src/.project 16 | *.rbc 17 | Gemfile.lock 18 | 19 | .yardoc/* 20 | doc/* 21 | 22 | -------------------------------------------------------------------------------- /examples/old/ex_tick_loop_array.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/helper' 2 | 3 | EM.run do 4 | array = (1..100).to_a 5 | 6 | tickloop = EM.tick_loop do 7 | if array.empty? 8 | :stop 9 | else 10 | puts array.shift 11 | end 12 | end 13 | 14 | tickloop.on_stop { EM.stop } 15 | end -------------------------------------------------------------------------------- /tests/test_running.rb: -------------------------------------------------------------------------------- 1 | require 'em_test_helper' 2 | 3 | class TestRunning < Test::Unit::TestCase 4 | def test_running 5 | assert_equal( false, EM::reactor_running? ) 6 | r = false 7 | EM.run { 8 | r = EM::reactor_running? 9 | EM.stop 10 | } 11 | assert_equal( true, r ) 12 | end 13 | end 14 | 15 | -------------------------------------------------------------------------------- /docs/old/TODO: -------------------------------------------------------------------------------- 1 | TODO List: 2 | 3 | 12Aug06: Noticed by Don Stocks. A TCP connect-request that results 4 | in a failed DNS resolution fires a fatal error back to user code. 5 | Uuuuuugly. We should probably cause an unbind event to get fired 6 | instead, and add some parameterization so the caller can detect 7 | the nature of the failure. 8 | 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | script: bundle exec rake compile test 2 | env: 3 | global: 4 | - TESTOPTS=-v 5 | language: ruby 6 | sudo: false 7 | rvm: 8 | - 1.8.7 9 | - 1.9.3 10 | - 2.0.0 11 | - 2.1 12 | - 2.2 13 | - rbx 14 | - jruby 15 | matrix: 16 | allow_failures: 17 | - rvm: rbx 18 | - rvm: jruby 19 | include: 20 | - rvm: 2.0.0 21 | os: osx 22 | -------------------------------------------------------------------------------- /java/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /tests/test_defer.rb: -------------------------------------------------------------------------------- 1 | require 'em_test_helper' 2 | 3 | class TestDefer < Test::Unit::TestCase 4 | 5 | def test_defers 6 | n = 0 7 | n_times = 20 8 | EM.run { 9 | n_times.times { 10 | work_proc = proc { n += 1 } 11 | callback = proc { EM.stop if n == n_times } 12 | EM.defer work_proc, callback 13 | } 14 | } 15 | assert_equal( n, n_times ) 16 | end 17 | 18 | end 19 | -------------------------------------------------------------------------------- /java/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | em_reactor 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | 15 | org.eclipse.jdt.core.javanature 16 | 17 | 18 | -------------------------------------------------------------------------------- /examples/guides/getting_started/01_eventmachine_echo_server.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'rubygems' # or use Bundler.setup 4 | require 'eventmachine' 5 | 6 | class EchoServer < EM::Connection 7 | def receive_data(data) 8 | send_data(data) 9 | end 10 | end 11 | 12 | EventMachine.run do 13 | # hit Control + C to stop 14 | Signal.trap("INT") { EventMachine.stop } 15 | Signal.trap("TERM") { EventMachine.stop } 16 | 17 | EventMachine.start_server("0.0.0.0", 10000, EchoServer) 18 | end -------------------------------------------------------------------------------- /tests/test_shutdown_hooks.rb: -------------------------------------------------------------------------------- 1 | require 'em_test_helper' 2 | 3 | class TestShutdownHooks < Test::Unit::TestCase 4 | def test_shutdown_hooks 5 | r = false 6 | EM.run { 7 | EM.add_shutdown_hook { r = true } 8 | EM.stop 9 | } 10 | assert_equal( true, r ) 11 | end 12 | 13 | def test_hook_order 14 | r = [] 15 | EM.run { 16 | EM.add_shutdown_hook { r << 2 } 17 | EM.add_shutdown_hook { r << 1 } 18 | EM.stop 19 | } 20 | assert_equal( [1, 2], r ) 21 | end 22 | end 23 | 24 | -------------------------------------------------------------------------------- /tests/test_many_fds.rb: -------------------------------------------------------------------------------- 1 | require 'em_test_helper' 2 | require 'socket' 3 | 4 | class TestManyFDs < Test::Unit::TestCase 5 | def setup 6 | @port = next_port 7 | end 8 | 9 | def test_connection_class_cache 10 | mod = Module.new 11 | a = nil 12 | Process.setrlimit(Process::RLIMIT_NOFILE,4096); 13 | EM.run { 14 | EM.start_server '127.0.0.1', @port, mod 15 | 1100.times do 16 | a = EM.connect '127.0.0.1', @port, mod 17 | assert_kind_of EM::Connection, a 18 | end 19 | EM.stop 20 | } 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /docs/old/INSTALL: -------------------------------------------------------------------------------- 1 | If you have obtained an EventMachine source-tarball (.tar.gz): 2 | unzip and untar the tarball, and enter the directory that is 3 | created. In that directory, say: 4 | ruby setup.rb 5 | (You may need to be root to execute this command.) 6 | 7 | To create documentation for EventMachine, simply type: 8 | rake rdoc 9 | in the distro directory. Rdocs will be created in subdirectory rdoc. 10 | 11 | If you have obtained a gem version of EventMachine, install it in the 12 | usual way (gem install eventmachine). You may need superuser privileges 13 | to execute this command. 14 | -------------------------------------------------------------------------------- /examples/guides/getting_started/02_eventmachine_echo_server_that_recognizes_exit_command.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'rubygems' # or use Bundler.setup 4 | require 'eventmachine' 5 | 6 | class EchoServer < EM::Connection 7 | def receive_data(data) 8 | if data.strip =~ /exit$/i 9 | EventMachine.stop 10 | else 11 | send_data(data) 12 | end 13 | end 14 | end 15 | 16 | EventMachine.run do 17 | # hit Control + C to stop 18 | Signal.trap("INT") { EventMachine.stop } 19 | Signal.trap("TERM") { EventMachine.stop } 20 | 21 | EventMachine.start_server("0.0.0.0", 10000, EchoServer) 22 | end 23 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | GEMSPEC = Gem::Specification.load('eventmachine.gemspec') 3 | 4 | require 'rake/clean' 5 | task :clobber => :clean 6 | 7 | desc "Build eventmachine, then run tests." 8 | task :default => [:compile, :test] 9 | 10 | desc 'Generate documentation' 11 | begin 12 | require 'yard' 13 | YARD::Rake::YardocTask.new do |t| 14 | t.files = ['lib/**/*.rb', '-', 'docs/*.md'] 15 | t.options = ['--main', 'README.md', '--no-private'] 16 | t.options = ['--exclude', 'lib/jeventmachine', '--exclude', 'lib/pr_eventmachine'] 17 | end 18 | rescue LoadError 19 | task :yard do puts "Please install yard first!"; end 20 | end 21 | -------------------------------------------------------------------------------- /examples/guides/getting_started/04_simple_chat_server_step_one.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'rubygems' # or use Bundler.setup 4 | require 'eventmachine' 5 | 6 | class SimpleChatServer < EM::Connection 7 | 8 | # 9 | # EventMachine handlers 10 | # 11 | 12 | def post_init 13 | puts "A client has connected..." 14 | end 15 | 16 | def unbind 17 | puts "A client has left..." 18 | end 19 | end 20 | 21 | EventMachine.run do 22 | # hit Control + C to stop 23 | Signal.trap("INT") { EventMachine.stop } 24 | Signal.trap("TERM") { EventMachine.stop } 25 | 26 | EventMachine.start_server("0.0.0.0", 10000, SimpleChatServer) 27 | end 28 | -------------------------------------------------------------------------------- /examples/old/ex_tick_loop_counter.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/helper' 2 | 3 | class TickCounter 4 | attr_reader :start_time, :count 5 | 6 | def initialize 7 | reset 8 | @tick_loop = EM.tick_loop(method(:tick)) 9 | end 10 | 11 | def reset 12 | @count = 0 13 | @start_time = EM.current_time 14 | end 15 | 16 | def tick 17 | @count += 1 18 | end 19 | 20 | def rate 21 | @count / (EM.current_time - @start_time) 22 | end 23 | end 24 | 25 | period = 5 26 | EM.run do 27 | counter = TickCounter.new 28 | EM.add_periodic_timer(period) do 29 | puts "Ticks per second: #{counter.rate} (mean of last #{period}s)" 30 | counter.reset 31 | end 32 | end -------------------------------------------------------------------------------- /tests/test_idle_connection.rb: -------------------------------------------------------------------------------- 1 | require 'em_test_helper' 2 | 3 | class TestIdleConnection < Test::Unit::TestCase 4 | if EM.respond_to?(:get_idle_time) 5 | def test_idle_time 6 | EM.run{ 7 | conn = EM.connect 'www.google.com', 80 8 | EM.add_timer(3){ 9 | $idle_time = conn.get_idle_time 10 | conn.send_data "GET / HTTP/1.0\r\n\r\n" 11 | EM.next_tick{ 12 | EM.next_tick{ 13 | $idle_time_after_send = conn.get_idle_time 14 | conn.close_connection 15 | EM.stop 16 | } 17 | } 18 | } 19 | } 20 | 21 | assert_in_delta 3, $idle_time, 0.2 22 | assert_in_delta 0, $idle_time_after_send, 0.1 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/em/protocols/line_protocol.rb: -------------------------------------------------------------------------------- 1 | module EventMachine 2 | module Protocols 3 | # LineProtocol will parse out newline terminated strings from a receive_data stream 4 | # 5 | # module Server 6 | # include EM::P::LineProtocol 7 | # 8 | # def receive_line(line) 9 | # send_data("you said: #{line}") 10 | # end 11 | # end 12 | # 13 | module LineProtocol 14 | # @private 15 | def receive_data data 16 | (@buf ||= '') << data 17 | 18 | while @buf.slice!(/(.*?)\r?\n/) 19 | receive_line($1) 20 | end 21 | end 22 | 23 | # Invoked with lines received over the network 24 | def receive_line(line) 25 | # stub 26 | end 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /tests/test_exc.rb: -------------------------------------------------------------------------------- 1 | require 'em_test_helper' 2 | 3 | class TestSomeExceptions < Test::Unit::TestCase 4 | 5 | # Read the commentary in EM#run. 6 | # This test exercises the ensure block in #run that makes sure 7 | # EM#release_machine gets called even if an exception is 8 | # thrown within the user code. Without the ensured call to release_machine, 9 | # the second call to EM#run will fail with a C++ exception 10 | # because the machine wasn't cleaned up properly. 11 | 12 | def test_a 13 | assert_raises(RuntimeError) { 14 | EM.run { 15 | raise "some exception" 16 | } 17 | } 18 | end 19 | 20 | def test_b 21 | assert_raises(RuntimeError) { 22 | EM.run { 23 | raise "some exception" 24 | } 25 | } 26 | end 27 | 28 | end 29 | -------------------------------------------------------------------------------- /tests/test_line_protocol.rb: -------------------------------------------------------------------------------- 1 | require 'em_test_helper' 2 | 3 | class TestLineProtocol < Test::Unit::TestCase 4 | class LineProtocolTestClass 5 | include EM::Protocols::LineProtocol 6 | 7 | def lines 8 | @lines ||= [] 9 | end 10 | 11 | def receive_line(line) 12 | lines << line 13 | end 14 | end 15 | 16 | def setup 17 | @proto = LineProtocolTestClass.new 18 | end 19 | 20 | def test_simple_split_line 21 | @proto.receive_data("this is") 22 | assert_equal([], @proto.lines) 23 | 24 | @proto.receive_data(" a test\n") 25 | assert_equal(["this is a test"], @proto.lines) 26 | end 27 | 28 | def test_simple_lines 29 | @proto.receive_data("aaa\nbbb\r\nccc\nddd") 30 | assert_equal(%w(aaa bbb ccc), @proto.lines) 31 | end 32 | 33 | end 34 | -------------------------------------------------------------------------------- /tests/test_handler_check.rb: -------------------------------------------------------------------------------- 1 | require 'em_test_helper' 2 | 3 | class TestHandlerCheck < Test::Unit::TestCase 4 | 5 | class Foo < EM::Connection; end; 6 | module TestModule; end; 7 | 8 | def test_with_correct_class 9 | assert_nothing_raised do 10 | EM.run { 11 | EM.connect("127.0.0.1", 80, Foo) 12 | EM.stop_event_loop 13 | } 14 | end 15 | end 16 | 17 | def test_with_incorrect_class 18 | assert_raise(ArgumentError) do 19 | EM.run { 20 | EM.connect("127.0.0.1", 80, String) 21 | EM.stop_event_loop 22 | } 23 | end 24 | end 25 | 26 | def test_with_module 27 | assert_nothing_raised do 28 | EM.run { 29 | EM.connect("127.0.0.1", 80, TestModule) 30 | EM.stop_event_loop 31 | } 32 | end 33 | end 34 | 35 | end -------------------------------------------------------------------------------- /tests/test_servers.rb: -------------------------------------------------------------------------------- 1 | require 'em_test_helper' 2 | require 'socket' 3 | 4 | class TestServers < Test::Unit::TestCase 5 | 6 | def setup 7 | @port = next_port 8 | end 9 | 10 | def server_alive? 11 | port_in_use?(@port) 12 | end 13 | 14 | def run_test_stop_server 15 | EM.run { 16 | sig = EM.start_server("127.0.0.1", @port) 17 | assert server_alive?, "Server didn't start" 18 | EM.stop_server sig 19 | # Give the server some time to shutdown. 20 | EM.add_timer(0.1) { 21 | assert !server_alive?, "Server didn't stop" 22 | EM.stop 23 | } 24 | } 25 | end 26 | 27 | def test_stop_server 28 | assert !server_alive?, "Port already in use" 29 | 2.times { run_test_stop_server } 30 | assert !server_alive?, "Servers didn't stop" 31 | end 32 | 33 | end 34 | -------------------------------------------------------------------------------- /tests/test_error_handler.rb: -------------------------------------------------------------------------------- 1 | require 'em_test_helper' 2 | 3 | class TestErrorHandler < Test::Unit::TestCase 4 | def setup 5 | @exception = Class.new(StandardError) 6 | end 7 | 8 | def test_error_handler 9 | error = nil 10 | 11 | EM.error_handler{ |e| 12 | error = e 13 | EM.error_handler(nil) 14 | EM.stop 15 | } 16 | 17 | assert_nothing_raised do 18 | EM.run{ 19 | EM.add_timer(0){ 20 | raise @exception, 'test' 21 | } 22 | } 23 | end 24 | 25 | assert_equal error.class, @exception 26 | assert_equal error.message, 'test' 27 | end 28 | 29 | def test_without_error_handler 30 | assert_raise @exception do 31 | EM.run{ 32 | EM.add_timer(0){ 33 | raise @exception, 'test' 34 | } 35 | } 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /tests/test_deferrable.rb: -------------------------------------------------------------------------------- 1 | require 'em_test_helper' 2 | 3 | class TestDeferrable < Test::Unit::TestCase 4 | class Later 5 | include EM::Deferrable 6 | end 7 | 8 | def test_timeout_without_args 9 | assert_nothing_raised do 10 | EM.run { 11 | df = Later.new 12 | df.timeout(0) 13 | df.errback { EM.stop } 14 | EM.add_timer(0.01) { flunk "Deferrable was not timed out." } 15 | } 16 | end 17 | end 18 | 19 | def test_timeout_with_args 20 | args = nil 21 | 22 | EM.run { 23 | df = Later.new 24 | df.timeout(0, :timeout, :foo) 25 | df.errback do |type, name| 26 | args = [type, name] 27 | EM.stop 28 | end 29 | 30 | EM.add_timer(0.01) { flunk "Deferrable was not timed out." } 31 | } 32 | 33 | assert_equal [:timeout, :foo], args 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /tests/test_object_protocol.rb: -------------------------------------------------------------------------------- 1 | require 'em_test_helper' 2 | 3 | class TestObjectProtocol < Test::Unit::TestCase 4 | module Server 5 | include EM::P::ObjectProtocol 6 | def post_init 7 | send_object :hello=>'world' 8 | end 9 | def receive_object obj 10 | $server = obj 11 | EM.stop 12 | end 13 | end 14 | 15 | module Client 16 | include EM::P::ObjectProtocol 17 | def receive_object obj 18 | $client = obj 19 | send_object 'you_said'=>obj 20 | end 21 | end 22 | 23 | def setup 24 | @port = next_port 25 | end 26 | 27 | def test_send_receive 28 | EM.run{ 29 | EM.start_server "127.0.0.1", @port, Server 30 | EM.connect "127.0.0.1", @port, Client 31 | } 32 | 33 | assert($client == {:hello=>'world'}) 34 | assert($server == {'you_said'=>{:hello=>'world'}}) 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /tests/test_kb.rb: -------------------------------------------------------------------------------- 1 | require 'em_test_helper' 2 | 3 | class TestKeyboardEvents < Test::Unit::TestCase 4 | 5 | module KbHandler 6 | include EM::Protocols::LineText2 7 | def receive_line d 8 | EM::stop if d == "STOP" 9 | end 10 | end 11 | 12 | # This test doesn't actually do anything useful but is here to 13 | # illustrate the usage. If you removed the timer and ran this test 14 | # by itself on a console, and then typed into the console, it would 15 | # work. 16 | # I don't know how to get the test harness to simulate actual keystrokes. 17 | # When someone figures that out, then we can make this a real test. 18 | # 19 | def test_kb 20 | omit_if(jruby?) 21 | omit_if(!$stdout.tty?) # don't run the test unless it stands a chance of validity. 22 | EM.run do 23 | EM.open_keyboard KbHandler 24 | EM::Timer.new(1) { EM.stop } 25 | end 26 | end 27 | 28 | end 29 | -------------------------------------------------------------------------------- /docs/old/LEGAL: -------------------------------------------------------------------------------- 1 | LEGAL NOTICE INFORMATION 2 | ------------------------ 3 | 4 | EventMachine is Copyright (C) 2006-07 by Francis Cianfrocca. 5 | 6 | EventMachine is copyrighted software owned by Francis Cianfrocca 7 | (blackhedd ... gmail.com). You may redistribute and/or modify this 8 | software as long as you comply with either the terms of the GPL 9 | (see the file GPL), or Ruby's license (see the file COPYING). 10 | 11 | Your use of all the files in this distribution is controlled by these 12 | license terms, except for those files specifically mentioned below: 13 | 14 | 15 | 16 | setup.rb 17 | This file is Copyright (C) 2000-2005 by Minero Aoki 18 | You can distribute/modify this file under the terms of 19 | the GNU LGPL, Lesser General Public License version 2.1. 20 | 21 | 22 | lib/em/buftok.rb 23 | This file is Copyright (C) 2007 by Tony Arcieri. This file is 24 | covered by the terms of Ruby's License (see the file COPYING). 25 | 26 | -------------------------------------------------------------------------------- /examples/guides/getting_started/05_simple_chat_server_step_two.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'rubygems' # or use Bundler.setup 4 | require 'eventmachine' 5 | 6 | class SimpleChatServer < EM::Connection 7 | 8 | @@connected_clients = Array.new 9 | 10 | 11 | # 12 | # EventMachine handlers 13 | # 14 | 15 | def post_init 16 | @@connected_clients.push(self) 17 | puts "A client has connected..." 18 | end 19 | 20 | def unbind 21 | @@connected_clients.delete(self) 22 | puts "A client has left..." 23 | end 24 | 25 | 26 | 27 | 28 | # 29 | # Helpers 30 | # 31 | 32 | def other_peers 33 | @@connected_clients.reject { |c| self == c } 34 | end # other_peers 35 | end 36 | 37 | EventMachine.run do 38 | # hit Control + C to stop 39 | Signal.trap("INT") { EventMachine.stop } 40 | Signal.trap("TERM") { EventMachine.stop } 41 | 42 | EventMachine.start_server("0.0.0.0", 10000, SimpleChatServer) 43 | end 44 | -------------------------------------------------------------------------------- /tests/test_set_sock_opt.rb: -------------------------------------------------------------------------------- 1 | require 'em_test_helper' 2 | require 'socket' 3 | 4 | class TestSetSockOpt < Test::Unit::TestCase 5 | 6 | if EM.respond_to? :set_sock_opt 7 | def setup 8 | assert(!EM.reactor_running?) 9 | end 10 | 11 | def teardown 12 | assert(!EM.reactor_running?) 13 | end 14 | 15 | #------------------------------------- 16 | 17 | def test_set_sock_opt 18 | test = self 19 | EM.run do 20 | EM.connect 'google.com', 80, Module.new { 21 | define_method :post_init do 22 | val = set_sock_opt Socket::SOL_SOCKET, Socket::SO_BROADCAST, true 23 | test.assert_equal 0, val 24 | EM.stop 25 | end 26 | } 27 | end 28 | end 29 | else 30 | warn "EM.set_sock_opt not implemented, skipping tests in #{__FILE__}" 31 | 32 | # Because some rubies will complain if a TestCase class has no tests 33 | def test_em_set_sock_opt_unsupported 34 | assert true 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /tests/test_get_sock_opt.rb: -------------------------------------------------------------------------------- 1 | require 'em_test_helper' 2 | require 'socket' 3 | 4 | class TestGetSockOpt < Test::Unit::TestCase 5 | 6 | if EM.respond_to? :get_sock_opt 7 | def setup 8 | assert(!EM.reactor_running?) 9 | end 10 | 11 | def teardown 12 | assert(!EM.reactor_running?) 13 | end 14 | 15 | #------------------------------------- 16 | 17 | def test_get_sock_opt 18 | test = self 19 | EM.run do 20 | EM.connect 'google.com', 80, Module.new { 21 | define_method :connection_completed do 22 | val = get_sock_opt Socket::SOL_SOCKET, Socket::SO_ERROR 23 | test.assert_equal "\0\0\0\0", val 24 | EM.stop 25 | end 26 | } 27 | end 28 | end 29 | else 30 | warn "EM.get_sock_opt not implemented, skipping tests in #{__FILE__}" 31 | 32 | # Because some rubies will complain if a TestCase class has no tests 33 | def test_em_get_sock_opt_unsupported 34 | assert true 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /tests/test_system.rb: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | require 'em_test_helper' 3 | 4 | class TestSystem < Test::Unit::TestCase 5 | def setup 6 | @filename = File.expand_path("../я манал dump.txt", __FILE__) 7 | @test_data = 'a' * 100 8 | File.open(@filename, 'w'){|f| f.write(@test_data)} 9 | end 10 | 11 | def test_system 12 | result = nil 13 | status = nil 14 | EM.run { 15 | EM.system('cat', @filename){|out, state| 16 | result = out 17 | status = state.exitstatus 18 | EM.stop 19 | } 20 | } 21 | assert_equal(0, status) 22 | assert_equal(@test_data, result) 23 | end 24 | 25 | def test_system_with_string 26 | result = nil 27 | status = nil 28 | EM.run { 29 | EM.system("cat '#@filename'"){|out, state| 30 | result = out 31 | status = state.exitstatus 32 | EM.stop 33 | } 34 | } 35 | assert_equal(0, status) 36 | assert_equal(@test_data, result) 37 | end 38 | 39 | def teardown 40 | File.unlink(@filename) 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /tests/test_stomp.rb: -------------------------------------------------------------------------------- 1 | require 'em_test_helper' 2 | 3 | class TestStomp < Test::Unit::TestCase 4 | CONTENT_LENGTH_REGEX = /^content-length: (\d+)$/ 5 | 6 | def bytesize(str) 7 | str = str.to_s 8 | size = str.bytesize if str.respond_to?(:bytesize) # bytesize added in 1.9 9 | size || str.size 10 | end 11 | 12 | def test_content_length_in_bytes 13 | connection = Object.new 14 | connection.instance_eval do 15 | extend EM::P::Stomp 16 | 17 | def last_sent_content_length 18 | @sent && Integer(@sent[CONTENT_LENGTH_REGEX, 1]) 19 | end 20 | 21 | def send_data(string) 22 | @sent = string 23 | end 24 | end 25 | 26 | queue = "queue" 27 | failure_message = "header content-length is not the byte size of last sent body" 28 | 29 | body = "test" 30 | connection.send queue, body 31 | assert_equal bytesize(body), connection.last_sent_content_length, failure_message 32 | 33 | body = "test\u221A" 34 | connection.send queue, body 35 | assert_equal bytesize(body), connection.last_sent_content_length, failure_message 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /tests/test_ssl_methods.rb: -------------------------------------------------------------------------------- 1 | require 'em_test_helper' 2 | 3 | class TestSSLMethods < Test::Unit::TestCase 4 | 5 | module ServerHandler 6 | def post_init 7 | start_tls 8 | end 9 | 10 | def ssl_handshake_completed 11 | $server_called_back = true 12 | $server_cert_value = get_peer_cert 13 | end 14 | end 15 | 16 | module ClientHandler 17 | def post_init 18 | start_tls 19 | end 20 | 21 | def ssl_handshake_completed 22 | $client_called_back = true 23 | $client_cert_value = get_peer_cert 24 | EM.stop_event_loop 25 | end 26 | end 27 | 28 | def test_ssl_methods 29 | omit_unless(EM.ssl?) 30 | $server_called_back, $client_called_back = false, false 31 | $server_cert_value, $client_cert_value = nil, nil 32 | 33 | EM.run { 34 | EM.start_server("127.0.0.1", 9999, ServerHandler) 35 | EM.connect("127.0.0.1", 9999, ClientHandler) 36 | } 37 | 38 | assert($server_called_back) 39 | assert($client_called_back) 40 | 41 | assert($server_cert_value.is_a?(NilClass)) 42 | assert($client_cert_value.is_a?(String)) 43 | end 44 | 45 | end 46 | -------------------------------------------------------------------------------- /tests/test_process_watch.rb: -------------------------------------------------------------------------------- 1 | require 'em_test_helper' 2 | 3 | if EM.kqueue? 4 | class TestProcessWatch < Test::Unit::TestCase 5 | module ParentProcessWatcher 6 | def process_forked 7 | $forked = true 8 | end 9 | end 10 | 11 | module ChildProcessWatcher 12 | def process_exited 13 | $exited = true 14 | end 15 | def unbind 16 | $unbind = true 17 | EM.stop 18 | end 19 | end 20 | 21 | def setup 22 | EM.kqueue = true 23 | end 24 | 25 | def teardown 26 | EM.kqueue = false 27 | end 28 | 29 | def test_events 30 | omit_if(jruby?) 31 | EM.run{ 32 | # watch ourselves for a fork notification 33 | EM.watch_process(Process.pid, ParentProcessWatcher) 34 | $fork_pid = fork{ sleep } 35 | child = EM.watch_process($fork_pid, ChildProcessWatcher) 36 | $pid = child.pid 37 | 38 | EM.add_timer(0.2){ 39 | Process.kill('TERM', $fork_pid) 40 | } 41 | } 42 | 43 | assert_equal($pid, $fork_pid) 44 | assert($forked) 45 | assert($exited) 46 | assert($unbind) 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /tests/test_sasl.rb: -------------------------------------------------------------------------------- 1 | require 'em_test_helper' 2 | 3 | 4 | class TestSASL < Test::Unit::TestCase 5 | 6 | # SASL authentication is usually done with UNIX-domain sockets, but 7 | # we'll use TCP so this test will work on Windows. As far as the 8 | # protocol handlers are concerned, there's no difference. 9 | 10 | TestUser,TestPsw = "someone", "password" 11 | 12 | class SaslServer < EM::Connection 13 | include EM::Protocols::SASLauth 14 | def validate usr, psw, sys, realm 15 | usr == TestUser and psw == TestPsw 16 | end 17 | end 18 | 19 | class SaslClient < EM::Connection 20 | include EM::Protocols::SASLauthclient 21 | end 22 | 23 | def setup 24 | @port = next_port 25 | end 26 | 27 | def test_sasl 28 | resp = nil 29 | EM.run { 30 | EM.start_server( "127.0.0.1", @port, SaslServer ) 31 | 32 | c = EM.connect( "127.0.0.1", @port, SaslClient ) 33 | d = c.validate?( TestUser, TestPsw ) 34 | d.timeout 1 35 | d.callback { 36 | resp = true 37 | EM.stop 38 | } 39 | d.errback { 40 | resp = false 41 | EM.stop 42 | } 43 | } 44 | assert_equal( true, resp ) 45 | end 46 | 47 | end 48 | -------------------------------------------------------------------------------- /tests/test_queue.rb: -------------------------------------------------------------------------------- 1 | require 'em_test_helper' 2 | 3 | class TestEMQueue < Test::Unit::TestCase 4 | def test_queue_push 5 | s = 0 6 | EM.run do 7 | q = EM::Queue.new 8 | q.push(1) 9 | EM.next_tick { s = q.size; EM.stop } 10 | end 11 | assert_equal 1, s 12 | end 13 | 14 | def test_queue_pop 15 | x,y,z = nil 16 | EM.run do 17 | q = EM::Queue.new 18 | q.push(1,2,3) 19 | q.pop { |v| x = v } 20 | q.pop { |v| y = v } 21 | q.pop { |v| z = v; EM.stop } 22 | end 23 | assert_equal 1, x 24 | assert_equal 2, y 25 | assert_equal 3, z 26 | end 27 | 28 | def test_queue_reactor_thread 29 | q = EM::Queue.new 30 | 31 | Thread.new { q.push(1,2,3) }.join 32 | assert q.empty? 33 | EM.run { EM.next_tick { EM.stop } } 34 | assert_equal 3, q.size 35 | 36 | x = nil 37 | Thread.new { q.pop { |v| x = v } }.join 38 | assert_equal nil, x 39 | EM.run { EM.next_tick { EM.stop } } 40 | assert_equal 1, x 41 | end 42 | 43 | def test_num_waiting 44 | q = EM::Queue.new 45 | many = 3 46 | many.times { q.pop {} } 47 | EM.run { EM.next_tick { EM.stop } } 48 | assert_equal many, q.num_waiting 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /ext/binder.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | 3 | $Id$ 4 | 5 | File: binder.h 6 | Date: 07Apr06 7 | 8 | Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved. 9 | Gmail: blackhedd 10 | 11 | This program is free software; you can redistribute it and/or modify 12 | it under the terms of either: 1) the GNU General Public License 13 | as published by the Free Software Foundation; either version 2 of the 14 | License, or (at your option) any later version; or 2) Ruby's License. 15 | 16 | See the file COPYING for complete licensing information. 17 | 18 | *****************************************************************************/ 19 | 20 | #ifndef __ObjectBindings__H_ 21 | #define __ObjectBindings__H_ 22 | 23 | 24 | class Bindable_t 25 | { 26 | public: 27 | static unsigned long CreateBinding(); 28 | static Bindable_t *GetObject (const unsigned long); 29 | static map BindingBag; 30 | 31 | public: 32 | Bindable_t(); 33 | virtual ~Bindable_t(); 34 | 35 | const unsigned long GetBinding() {return Binding;} 36 | 37 | private: 38 | unsigned long Binding; 39 | }; 40 | 41 | 42 | 43 | 44 | 45 | #endif // __ObjectBindings__H_ 46 | 47 | -------------------------------------------------------------------------------- /tests/test_unbind_reason.rb: -------------------------------------------------------------------------------- 1 | require 'em_test_helper' 2 | require 'socket' 3 | 4 | class TestUnbindReason < Test::Unit::TestCase 5 | 6 | class StubConnection < EM::Connection 7 | attr_reader :error 8 | def unbind(reason = nil) 9 | @error = reason 10 | EM.stop 11 | end 12 | end 13 | 14 | def test_connect_timeout 15 | error = nil 16 | EM.run { 17 | conn = EM.connect 'google.com', 81, Module.new{ |m| 18 | m.send(:define_method, :unbind) do |reason| 19 | error = reason 20 | EM.stop 21 | end 22 | } 23 | conn.pending_connect_timeout = 0.1 24 | } 25 | assert_equal Errno::ETIMEDOUT, error 26 | end 27 | 28 | def test_connect_refused 29 | error = nil 30 | EM.run { 31 | EM.connect '127.0.0.1', 12388, Module.new{ |m| 32 | m.send(:define_method, :unbind) do |reason| 33 | error = reason 34 | EM.stop 35 | end 36 | } 37 | } 38 | assert_equal Errno::ECONNREFUSED, error 39 | end 40 | 41 | def test_optional_argument 42 | conn = nil 43 | EM.run { 44 | conn = EM.connect '127.0.0.1', 12388, StubConnection 45 | } 46 | assert_equal Errno::ECONNREFUSED, conn.error 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /java/src/com/rubyeventmachine/EmReactorException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * $Id$ 3 | * 4 | * Author:: Francis Cianfrocca (gmail: blackhedd) 5 | * Homepage:: http://rubyeventmachine.com 6 | * Date:: 15 Jul 2007 7 | * 8 | * See EventMachine and EventMachine::Connection for documentation and 9 | * usage examples. 10 | * 11 | * 12 | *---------------------------------------------------------------------------- 13 | * 14 | * Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved. 15 | * Gmail: blackhedd 16 | * 17 | * This program is free software; you can redistribute it and/or modify 18 | * it under the terms of either: 1) the GNU General Public License 19 | * as published by the Free Software Foundation; either version 2 of the 20 | * License, or (at your option) any later version; or 2) Ruby's License. 21 | * 22 | * See the file COPYING for complete licensing information. 23 | * 24 | *--------------------------------------------------------------------------- 25 | * 26 | * 27 | */ 28 | 29 | package com.rubyeventmachine; 30 | 31 | /** 32 | * @author francis 33 | * 34 | */ 35 | public class EmReactorException extends Exception { 36 | static final long serialVersionUID = 0; 37 | public EmReactorException (String msg) { 38 | super (msg); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /ext/page.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | 3 | $Id$ 4 | 5 | File: page.h 6 | Date: 30Apr06 7 | 8 | Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved. 9 | Gmail: blackhedd 10 | 11 | This program is free software; you can redistribute it and/or modify 12 | it under the terms of either: 1) the GNU General Public License 13 | as published by the Free Software Foundation; either version 2 of the 14 | License, or (at your option) any later version; or 2) Ruby's License. 15 | 16 | See the file COPYING for complete licensing information. 17 | 18 | *****************************************************************************/ 19 | 20 | 21 | #ifndef __PageManager__H_ 22 | #define __PageManager__H_ 23 | 24 | 25 | /************** 26 | class PageList 27 | **************/ 28 | 29 | class PageList 30 | { 31 | struct Page { 32 | Page (const char *b, size_t s): Buffer(b), Size(s) {} 33 | const char *Buffer; 34 | size_t Size; 35 | }; 36 | 37 | public: 38 | PageList(); 39 | virtual ~PageList(); 40 | 41 | void Push (const char*, int); 42 | bool HasPages(); 43 | void Front (const char**, int*); 44 | void PopFront(); 45 | 46 | private: 47 | deque Pages; 48 | }; 49 | 50 | 51 | #endif // __PageManager__H_ 52 | -------------------------------------------------------------------------------- /examples/old/ex_channel.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/helper' 2 | 3 | EM.run do 4 | 5 | # Create a channel to push data to, this could be stocks... 6 | RandChannel = EM::Channel.new 7 | 8 | # The server simply subscribes client connections to the channel on connect, 9 | # and unsubscribes them on disconnect. 10 | class Server < EM::Connection 11 | def self.start(host = '127.0.0.1', port = 8000) 12 | EM.start_server(host, port, self) 13 | end 14 | 15 | def post_init 16 | @sid = RandChannel.subscribe { |m| send_data "#{m.inspect}\n" } 17 | end 18 | 19 | def unbind 20 | RandChannel.unsubscribe @sid 21 | end 22 | end 23 | Server.start 24 | 25 | # Two client connections, that just print what they receive. 26 | 2.times do 27 | EM.connect('127.0.0.1', 8000) do |c| 28 | c.extend EM::P::LineText2 29 | def c.receive_line(line) 30 | puts "Subscriber: #{signature} got #{line}" 31 | end 32 | EM.add_timer(2) { c.close_connection } 33 | end 34 | end 35 | 36 | # This part of the example is more fake, but imagine sleep was in fact a 37 | # long running calculation to achieve the value. 38 | 40.times do 39 | EM.defer lambda { v = sleep(rand * 2); RandChannel << [Time.now, v] } 40 | end 41 | 42 | EM.add_timer(5) { EM.stop } 43 | end 44 | -------------------------------------------------------------------------------- /docs/old/KEYBOARD: -------------------------------------------------------------------------------- 1 | EventMachine (EM) can respond to keyboard events. This gives your event-driven 2 | programs the ability to respond to input from local users. 3 | 4 | Programming EM to handle keyboard input in Ruby is simplicity itself. Just use 5 | EventMachine#open_keyboard, and supply the name of a Ruby module or class that 6 | will receive the input: 7 | 8 | require 'rubygems' 9 | require 'eventmachine' 10 | 11 | module MyKeyboardHandler 12 | def receive_data keystrokes 13 | puts "I received the following data from the keyboard: #{keystrokes}" 14 | end 15 | end 16 | 17 | EM.run { 18 | EM.open_keyboard(MyKeyboardHandler) 19 | } 20 | 21 | If you want EM to send line-buffered keyboard input to your program, just 22 | include the LineText2 protocol module in your handler class or module: 23 | 24 | require 'rubygems' 25 | require 'eventmachine' 26 | 27 | module MyKeyboardHandler 28 | include EM::Protocols::LineText2 29 | def receive_line data 30 | puts "I received the following line from the keyboard: #{data}" 31 | end 32 | end 33 | 34 | EM.run { 35 | EM.open_keyboard(MyKeyboardHandler) 36 | } 37 | 38 | As we said, simplicity itself. You can call EventMachine#open_keyboard at any 39 | time while the EM reactor loop is running. In other words, the method 40 | invocation may appear anywhere in an EventMachine#run block, or in any code 41 | invoked in the #run block. 42 | 43 | -------------------------------------------------------------------------------- /lib/em/protocols/object_protocol.rb: -------------------------------------------------------------------------------- 1 | module EventMachine 2 | module Protocols 3 | # ObjectProtocol allows for easy communication using marshaled ruby objects 4 | # 5 | # module RubyServer 6 | # include EM::P::ObjectProtocol 7 | # 8 | # def receive_object obj 9 | # send_object({'you said' => obj}) 10 | # end 11 | # end 12 | # 13 | module ObjectProtocol 14 | # By default returns Marshal, override to return JSON or YAML, or any 15 | # other serializer/deserializer responding to #dump and #load. 16 | def serializer 17 | Marshal 18 | end 19 | 20 | # @private 21 | def receive_data data 22 | (@buf ||= '') << data 23 | 24 | while @buf.size >= 4 25 | if @buf.size >= 4+(size=@buf.unpack('N').first) 26 | @buf.slice!(0,4) 27 | receive_object serializer.load(@buf.slice!(0,size)) 28 | else 29 | break 30 | end 31 | end 32 | end 33 | 34 | # Invoked with ruby objects received over the network 35 | def receive_object obj 36 | # stub 37 | end 38 | 39 | # Sends a ruby object over the network 40 | def send_object obj 41 | data = serializer.dump(obj) 42 | send_data [data.respond_to?(:bytesize) ? data.bytesize : data.size, data].pack('Na*') 43 | end 44 | end 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /ext/fastfilereader/mapper.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | 3 | $Id: mapper.h 4529 2007-07-04 11:32:22Z francis $ 4 | 5 | File: mapper.h 6 | Date: 02Jul07 7 | 8 | Copyright (C) 2007 by Francis Cianfrocca. All Rights Reserved. 9 | Gmail: garbagecat10 10 | 11 | This program is free software; you can redistribute it and/or modify 12 | it under the terms of either: 1) the GNU General Public License 13 | as published by the Free Software Foundation; either version 2 of the 14 | License, or (at your option) any later version; or 2) Ruby's License. 15 | 16 | See the file COPYING for complete licensing information. 17 | 18 | *****************************************************************************/ 19 | 20 | 21 | #ifndef __Mapper__H_ 22 | #define __Mapper__H_ 23 | 24 | 25 | /************** 26 | class Mapper_t 27 | **************/ 28 | 29 | class Mapper_t 30 | { 31 | public: 32 | Mapper_t (const string&); 33 | virtual ~Mapper_t(); 34 | 35 | const char *GetChunk (unsigned); 36 | void Close(); 37 | size_t GetFileSize() {return FileSize;} 38 | 39 | private: 40 | size_t FileSize; 41 | 42 | #ifdef OS_UNIX 43 | private: 44 | int Fd; 45 | const char *MapPoint; 46 | #endif // OS_UNIX 47 | 48 | #ifdef OS_WIN32 49 | private: 50 | HANDLE hFile; 51 | HANDLE hMapping; 52 | char *MapPoint; 53 | #endif // OS_WIN32 54 | 55 | }; 56 | 57 | 58 | #endif // __Mapper__H_ 59 | 60 | -------------------------------------------------------------------------------- /tests/test_pending_connect_timeout.rb: -------------------------------------------------------------------------------- 1 | require 'em_test_helper' 2 | 3 | class TestPendingConnectTimeout < Test::Unit::TestCase 4 | 5 | if EM.respond_to? :get_pending_connect_timeout 6 | def test_default 7 | EM.run { 8 | c = EM.connect("127.0.0.1", 54321) 9 | assert_equal 20.0, c.pending_connect_timeout 10 | EM.stop 11 | } 12 | end 13 | 14 | def test_set_and_get 15 | EM.run { 16 | c = EM.connect("127.0.0.1", 54321) 17 | c.pending_connect_timeout = 2.5 18 | assert_equal 2.5, c.pending_connect_timeout 19 | EM.stop 20 | } 21 | end 22 | 23 | def test_for_real 24 | start, finish = nil 25 | 26 | timeout_handler = Module.new do 27 | define_method :unbind do 28 | finish = Time.now 29 | EM.stop 30 | end 31 | end 32 | 33 | EM.run { 34 | setup_timeout 35 | EM.heartbeat_interval = 0.1 36 | start = Time.now 37 | c = EM.connect("1.2.3.4", 54321, timeout_handler) 38 | c.pending_connect_timeout = 0.2 39 | } 40 | 41 | assert_in_delta(0.2, (finish - start), 0.1) 42 | end 43 | else 44 | warn "EM.pending_connect_timeout not implemented, skipping tests in #{__FILE__}" 45 | 46 | # Because some rubies will complain if a TestCase class has no tests 47 | def test_em_pending_connect_timeout_not_implemented 48 | assert true 49 | end 50 | end 51 | 52 | end 53 | -------------------------------------------------------------------------------- /lib/em/process_watch.rb: -------------------------------------------------------------------------------- 1 | module EventMachine 2 | 3 | # This is subclassed from EventMachine::Connection for use with the process monitoring API. Read the 4 | # documentation on the instance methods of this class, and for a full explanation see EventMachine.watch_process. 5 | class ProcessWatch < Connection 6 | # @private 7 | Cfork = 'fork'.freeze 8 | # @private 9 | Cexit = 'exit'.freeze 10 | 11 | # @private 12 | def receive_data(data) 13 | case data 14 | when Cfork 15 | process_forked 16 | when Cexit 17 | process_exited 18 | end 19 | end 20 | 21 | # Returns the pid that EventMachine::watch_process was originally called with. 22 | def pid 23 | @pid 24 | end 25 | 26 | # Should be redefined with the user's custom callback that will be fired when the prcess is forked. 27 | # 28 | # There is currently not an easy way to get the pid of the forked child. 29 | def process_forked 30 | end 31 | 32 | # Should be redefined with the user's custom callback that will be fired when the process exits. 33 | # 34 | # stop_watching is called automatically after this callback 35 | def process_exited 36 | end 37 | 38 | # Discontinue monitoring of the process. 39 | # This will be called automatically when a process dies. User code may call it as well. 40 | def stop_watching 41 | EventMachine::unwatch_pid(@signature) 42 | end 43 | end 44 | 45 | end 46 | -------------------------------------------------------------------------------- /tests/test_channel.rb: -------------------------------------------------------------------------------- 1 | require 'em_test_helper' 2 | 3 | class TestEMChannel < Test::Unit::TestCase 4 | def test_channel_subscribe 5 | s = 0 6 | EM.run do 7 | c = EM::Channel.new 8 | c.subscribe { |v| s = v; EM.stop } 9 | c << 1 10 | end 11 | assert_equal 1, s 12 | end 13 | 14 | def test_channel_unsubscribe 15 | s = 0 16 | EM.run do 17 | c = EM::Channel.new 18 | subscription = c.subscribe { |v| s = v } 19 | c.unsubscribe(subscription) 20 | c << 1 21 | EM.next_tick { EM.stop } 22 | end 23 | assert_not_equal 1, s 24 | end 25 | 26 | def test_channel_pop 27 | s = 0 28 | EM.run do 29 | c = EM::Channel.new 30 | c.pop{ |v| s = v } 31 | c.push(1,2,3) 32 | c << 4 33 | c << 5 34 | EM.next_tick { EM.stop } 35 | end 36 | assert_equal 1, s 37 | end 38 | 39 | def test_channel_reactor_thread_push 40 | out = [] 41 | c = EM::Channel.new 42 | c.subscribe { |v| out << v } 43 | Thread.new { c.push(1,2,3) }.join 44 | assert out.empty? 45 | 46 | EM.run { EM.next_tick { EM.stop } } 47 | 48 | assert_equal [1,2,3], out 49 | end 50 | 51 | def test_channel_reactor_thread_callback 52 | out = [] 53 | c = EM::Channel.new 54 | Thread.new { c.subscribe { |v| out << v } }.join 55 | c.push(1,2,3) 56 | assert out.empty? 57 | 58 | EM.run { EM.next_tick { EM.stop } } 59 | 60 | assert_equal [1,2,3], out 61 | end 62 | end -------------------------------------------------------------------------------- /tests/test_smtpclient.rb: -------------------------------------------------------------------------------- 1 | require 'em_test_helper' 2 | 3 | class TestSmtpClient < Test::Unit::TestCase 4 | 5 | Localhost = "127.0.0.1" 6 | Localport = 9801 7 | 8 | def setup 9 | end 10 | 11 | def teardown 12 | end 13 | 14 | def test_a 15 | # No real tests until we have a server implementation to test against. 16 | # This is what the call looks like, though: 17 | err = nil 18 | EM.run { 19 | d = EM::Protocols::SmtpClient.send :domain=>"example.com", 20 | :host=>Localhost, 21 | :port=>Localport, # optional, defaults 25 22 | :starttls=>true, 23 | :from=>"sender@example.com", 24 | :to=> ["to_1@example.com", "to_2@example.com"], 25 | :header=> {"Subject" => "This is a subject line"}, 26 | :body=> "This is the body of the email", 27 | :verbose=>true 28 | d.errback {|e| 29 | err = e 30 | EM.stop 31 | } 32 | } 33 | assert(err) 34 | end 35 | 36 | def test_content 37 | err = nil 38 | EM.run { 39 | d = EM::Protocols::SmtpClient.send :domain=>"example.com", 40 | :host=>Localhost, 41 | :port=>Localport, # optional, defaults 25 42 | :starttls=>true, 43 | :from=>"sender@example.com", 44 | :to=> ["to_1@example.com", "to_2@example.com"], 45 | :content => ["Subject: xxx\r\n\r\ndata\r\n.\r\n"], 46 | :verbose=>true 47 | d.errback {|e| 48 | err = e 49 | EM.stop 50 | } 51 | } 52 | assert(err) 53 | end 54 | 55 | end 56 | -------------------------------------------------------------------------------- /tests/test_connection_count.rb: -------------------------------------------------------------------------------- 1 | require 'em_test_helper' 2 | 3 | class TestConnectionCount < Test::Unit::TestCase 4 | def test_idle_connection_count 5 | EM.run { 6 | $count = EM.connection_count 7 | EM.stop_event_loop 8 | } 9 | 10 | assert_equal(0, $count) 11 | end 12 | 13 | module Client 14 | def connection_completed 15 | $client_conns += 1 16 | EM.stop if $client_conns == 3 17 | end 18 | end 19 | 20 | def test_with_some_connections 21 | EM.run { 22 | $client_conns = 0 23 | $initial_conns = EM.connection_count 24 | EM.start_server("127.0.0.1", 9999) 25 | $server_conns = EM.connection_count 26 | 3.times { EM.connect("127.0.0.1", 9999, Client) } 27 | } 28 | 29 | assert_equal(0, $initial_conns) 30 | assert_equal(1, $server_conns) 31 | assert_equal(4, $client_conns + $server_conns) 32 | end 33 | 34 | module DoubleCloseClient 35 | def unbind 36 | close_connection 37 | $num_close_scheduled_1 = EM.num_close_scheduled 38 | EM.next_tick do 39 | $num_close_scheduled_2 = EM.num_close_scheduled 40 | EM.stop 41 | end 42 | end 43 | end 44 | 45 | def test_num_close_scheduled 46 | omit_if(jruby?) 47 | EM.run { 48 | assert_equal(0, EM.num_close_scheduled) 49 | EM.connect("127.0.0.1", 9999, DoubleCloseClient) # nothing listening on 9999 50 | } 51 | assert_equal(1, $num_close_scheduled_1) 52 | assert_equal(0, $num_close_scheduled_2) 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /tests/test_inactivity_timeout.rb: -------------------------------------------------------------------------------- 1 | require 'em_test_helper' 2 | 3 | class TestInactivityTimeout < Test::Unit::TestCase 4 | 5 | if EM.respond_to? :get_comm_inactivity_timeout 6 | def test_default 7 | EM.run { 8 | c = EM.connect("127.0.0.1", 54321) 9 | assert_equal 0.0, c.comm_inactivity_timeout 10 | EM.stop 11 | } 12 | end 13 | 14 | def test_set_and_get 15 | EM.run { 16 | c = EM.connect("127.0.0.1", 54321) 17 | c.comm_inactivity_timeout = 2.5 18 | assert_equal 2.5, c.comm_inactivity_timeout 19 | EM.stop 20 | } 21 | end 22 | 23 | def test_for_real 24 | start, finish = nil 25 | 26 | timeout_handler = Module.new do 27 | define_method :unbind do 28 | finish = Time.now 29 | EM.stop 30 | end 31 | end 32 | 33 | EM.run { 34 | setup_timeout 35 | EM.heartbeat_interval = 0.01 36 | EM.start_server("127.0.0.1", 12345) 37 | EM.add_timer(0.01) { 38 | start = Time.now 39 | c = EM.connect("127.0.0.1", 12345, timeout_handler) 40 | c.comm_inactivity_timeout = 0.02 41 | } 42 | } 43 | 44 | assert_in_delta(0.02, (finish - start), 0.02) 45 | end 46 | else 47 | warn "EM.comm_inactivity_timeout not implemented, skipping tests in #{__FILE__}" 48 | 49 | # Because some rubies will complain if a TestCase class has no tests 50 | def test_em_comm_inactivity_timeout_not_implemented 51 | assert true 52 | end 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /lib/em/timers.rb: -------------------------------------------------------------------------------- 1 | module EventMachine 2 | # Creates a one-time timer 3 | # 4 | # timer = EventMachine::Timer.new(5) do 5 | # # this will never fire because we cancel it 6 | # end 7 | # timer.cancel 8 | # 9 | class Timer 10 | # Create a new timer that fires after a given number of seconds 11 | def initialize interval, callback=nil, &block 12 | @signature = EventMachine::add_timer(interval, callback || block) 13 | end 14 | 15 | # Cancel the timer 16 | def cancel 17 | EventMachine.send :cancel_timer, @signature 18 | end 19 | end 20 | 21 | # Creates a periodic timer 22 | # 23 | # @example 24 | # n = 0 25 | # timer = EventMachine::PeriodicTimer.new(5) do 26 | # puts "the time is #{Time.now}" 27 | # timer.cancel if (n+=1) > 5 28 | # end 29 | # 30 | class PeriodicTimer 31 | # Create a new periodic timer that executes every interval seconds 32 | def initialize interval, callback=nil, &block 33 | @interval = interval 34 | @code = callback || block 35 | @cancelled = false 36 | @work = method(:fire) 37 | schedule 38 | end 39 | 40 | # Cancel the periodic timer 41 | def cancel 42 | @cancelled = true 43 | end 44 | 45 | # Fire the timer every interval seconds 46 | attr_accessor :interval 47 | 48 | # @private 49 | def schedule 50 | EventMachine::add_timer @interval, @work 51 | end 52 | 53 | # @private 54 | def fire 55 | unless @cancelled 56 | @code.call 57 | schedule 58 | end 59 | end 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /tests/em_test_helper.rb: -------------------------------------------------------------------------------- 1 | require 'eventmachine' 2 | require 'test/unit' 3 | require 'rbconfig' 4 | require 'socket' 5 | 6 | class Test::Unit::TestCase 7 | class EMTestTimeout < StandardError ; end 8 | 9 | def setup_timeout(timeout = TIMEOUT_INTERVAL) 10 | EM.schedule { 11 | EM.add_timer(timeout) { 12 | raise EMTestTimeout, "Test was cancelled after #{timeout} seconds." 13 | } 14 | } 15 | end 16 | 17 | def port_in_use?(port, host="127.0.0.1") 18 | s = TCPSocket.new(host, port) 19 | s.close 20 | s 21 | rescue Errno::ECONNREFUSED 22 | false 23 | end 24 | 25 | def next_port 26 | @@port ||= 9000 27 | begin 28 | @@port += 1 29 | end while port_in_use?(@@port) 30 | 31 | @@port 32 | end 33 | 34 | def exception_class 35 | jruby? ? NativeException : RuntimeError 36 | end 37 | 38 | module PlatformHelper 39 | # http://blog.emptyway.com/2009/11/03/proper-way-to-detect-windows-platform-in-ruby/ 40 | def windows? 41 | RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ 42 | end 43 | 44 | # http://stackoverflow.com/questions/1342535/how-can-i-tell-if-im-running-from-jruby-vs-ruby/1685970#1685970 45 | def jruby? 46 | defined? JRUBY_VERSION 47 | end 48 | end 49 | 50 | include PlatformHelper 51 | extend PlatformHelper 52 | 53 | # Tests run significantly slower on windows. YMMV 54 | TIMEOUT_INTERVAL = windows? ? 1 : 0.25 55 | 56 | def silent 57 | backup, $VERBOSE = $VERBOSE, nil 58 | begin 59 | yield 60 | ensure 61 | $VERBOSE = backup 62 | end 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /tests/test_threaded_resource.rb: -------------------------------------------------------------------------------- 1 | class TestThreadedResource < Test::Unit::TestCase 2 | def object 3 | @object ||= {} 4 | end 5 | 6 | def resource 7 | @resource = EM::ThreadedResource.new do 8 | object 9 | end 10 | end 11 | 12 | def teardown 13 | resource.shutdown 14 | end 15 | 16 | def test_dispatch_completion 17 | EM.run do 18 | EM.add_timer(3) do 19 | EM.stop 20 | fail 'Resource dispatch timed out' 21 | end 22 | completion = resource.dispatch do |o| 23 | o[:foo] = :bar 24 | :foo 25 | end 26 | completion.callback do |result| 27 | assert_equal :foo, result 28 | EM.stop 29 | end 30 | completion.errback do |error| 31 | EM.stop 32 | fail "Unexpected error: #{error.message}" 33 | end 34 | end 35 | assert_equal :bar, object[:foo] 36 | end 37 | 38 | def test_dispatch_failure 39 | completion = resource.dispatch do |o| 40 | raise 'boom' 41 | end 42 | completion.errback do |error| 43 | assert_kind_of RuntimeError, error 44 | assert_equal 'boom', error.message 45 | end 46 | end 47 | 48 | def test_dispatch_threading 49 | main = Thread.current 50 | resource.dispatch do |o| 51 | o[:dispatch_thread] = Thread.current 52 | end 53 | assert_not_equal main, object[:dispatch_thread] 54 | end 55 | 56 | def test_shutdown 57 | # This test should get improved sometime. The method returning thread is 58 | # NOT an api that will be maintained. 59 | assert !resource.shutdown.alive? 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /docs/DocumentationGuidesIndex.md: -------------------------------------------------------------------------------- 1 | # EventMachine documentation guides # 2 | 3 | Welcome to the documentation guides for [EventMachine](http://github.com/eventmachine/eventmachine), 4 | a fast and simple event-processing library for Ruby programs (à la JBoss Netty, Twisted, Node.js 5 | and so on). 6 | 7 | ## Guide list ## 8 | 9 | * {file:docs/GettingStarted.md Getting started with EventMachine} 10 | * {file:docs/EventDrivenServers.md Writing event-driven servers} 11 | * {file:docs/EventDrivenClients.md Writing event-driven clients} 12 | * {file:docs/ConnectionFailureAndRecovery.md Connection Failure and Recovery} 13 | * {file:docs/TLS.md TLS (aka SSL)} 14 | * {file:docs/Ecosystem.md EventMachine ecosystem}: Thin, Goliath, em-http-request, em-websockets, Proxymachine and beyond 15 | * {file:docs/BlockingEventLoop.md On blocking the event loop: why it is harmful for performance and how to avoid it} 16 | * {file:docs/LightweightConcurrency.md Lightweight concurrency with EventMachine} 17 | * {file:docs/Deferrables.md Deferrables} 18 | * {file:docs/ModernKernelInputOutputAPIs.md Brief introduction to epoll, kqueue, select} 19 | * {file:docs/WorkingWithOtherIOSources.md Working with other IO sources such as the keyboard} 20 | 21 | 22 | ## Tell us what you think! ## 23 | 24 | Please take a moment and tell us what you think about this guide on the [EventMachine mailing list](http://bit.ly/jW3cR3) 25 | or in the #eventmachine channel on irc.freenode.net: what was unclear? What wasn't covered? 26 | Maybe you don't like the guide style or the grammar and spelling are incorrect? Reader feedback is 27 | key to making documentation better. 28 | -------------------------------------------------------------------------------- /lib/em/protocols/tcptest.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # 3 | # Author:: Francis Cianfrocca (gmail: blackhedd) 4 | # Homepage:: http://rubyeventmachine.com 5 | # Date:: 16 July 2006 6 | # 7 | # See EventMachine and EventMachine::Connection for documentation and 8 | # usage examples. 9 | # 10 | #---------------------------------------------------------------------------- 11 | # 12 | # Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved. 13 | # Gmail: blackhedd 14 | # 15 | # This program is free software; you can redistribute it and/or modify 16 | # it under the terms of either: 1) the GNU General Public License 17 | # as published by the Free Software Foundation; either version 2 of the 18 | # License, or (at your option) any later version; or 2) Ruby's License. 19 | # 20 | # See the file COPYING for complete licensing information. 21 | # 22 | #--------------------------------------------------------------------------- 23 | # 24 | # 25 | # 26 | 27 | module EventMachine 28 | module Protocols 29 | 30 | # @private 31 | class TcpConnectTester < Connection 32 | include EventMachine::Deferrable 33 | 34 | def self.test( host, port ) 35 | EventMachine.connect( host, port, self ) 36 | end 37 | 38 | def post_init 39 | @start_time = Time.now 40 | end 41 | 42 | def connection_completed 43 | @completed = true 44 | set_deferred_status :succeeded, (Time.now - @start_time) 45 | close_connection 46 | end 47 | 48 | def unbind 49 | set_deferred_status :failed, (Time.now - @start_time) unless @completed 50 | end 51 | end 52 | 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /tests/test_tick_loop.rb: -------------------------------------------------------------------------------- 1 | require "test/unit" 2 | require 'em_test_helper' 3 | 4 | class TestEmTickLoop < Test::Unit::TestCase 5 | def test_em_tick_loop 6 | i = 0 7 | EM.tick_loop { i += 1; EM.stop if i == 10 } 8 | EM.run { EM.add_timer(1) { EM.stop } } 9 | assert_equal i, 10 10 | end 11 | 12 | def test_tick_loop_on_stop 13 | t = nil 14 | tick_loop = EM.tick_loop { :stop } 15 | tick_loop.on_stop { t = true } 16 | EM.run { EM.next_tick { EM.stop } } 17 | assert t 18 | end 19 | 20 | def test_start_twice 21 | i = 0 22 | s = 0 23 | tick_loop = EM.tick_loop { i += 1; :stop } 24 | tick_loop.on_stop { s += 1; EM.stop } 25 | EM.run { EM.next_tick { EM.stop } } 26 | assert_equal 1, i 27 | assert_equal 1, s 28 | tick_loop.start 29 | EM.run { EM.next_tick { EM.stop } } 30 | assert_equal 2, i 31 | assert_equal 1, s # stop callbacks are only called once 32 | end 33 | 34 | def test_stop 35 | i, s = 0, 0 36 | tick_loop = EM.tick_loop { i += 1 } 37 | tick_loop.on_stop { s += 1 } 38 | EM.run { EM.next_tick { tick_loop.stop; EM.next_tick { EM.stop } } } 39 | assert tick_loop.stopped? 40 | assert_equal 1, i 41 | assert_equal 1, s 42 | end 43 | 44 | def test_immediate_stops 45 | s = 0 46 | tick_loop = EM::TickLoop.new { } 47 | tick_loop.on_stop { s += 1 } 48 | tick_loop.on_stop { s += 1 } 49 | assert_equal 2, s 50 | end 51 | 52 | def test_stopped 53 | tick_loop = EM::TickLoop.new { } 54 | assert tick_loop.stopped? 55 | tick_loop.start 56 | assert !tick_loop.stopped? 57 | end 58 | 59 | end -------------------------------------------------------------------------------- /lib/em/protocols.rb: -------------------------------------------------------------------------------- 1 | module EventMachine 2 | # This module contains various protocol implementations, including: 3 | # - HttpClient and HttpClient2 4 | # - Stomp 5 | # - Memcache 6 | # - SmtpClient and SmtpServer 7 | # - SASLauth and SASLauthclient 8 | # - LineProtocol, LineAndTextProtocol and LineText2 9 | # - HeaderAndContentProtocol 10 | # - Postgres3 11 | # - ObjectProtocol 12 | # 13 | # The protocol implementations live in separate files in the protocols/ subdirectory, 14 | # but are auto-loaded when they are first referenced in your application. 15 | # 16 | # EventMachine::Protocols is also aliased to EM::P for easier usage. 17 | # 18 | module Protocols 19 | # TODO : various autotools are completely useless with the lack of naming 20 | # convention, we need to correct that! 21 | autoload :TcpConnectTester, 'em/protocols/tcptest' 22 | autoload :HttpClient, 'em/protocols/httpclient' 23 | autoload :HttpClient2, 'em/protocols/httpclient2' 24 | autoload :LineAndTextProtocol, 'em/protocols/line_and_text' 25 | autoload :HeaderAndContentProtocol, 'em/protocols/header_and_content' 26 | autoload :LineText2, 'em/protocols/linetext2' 27 | autoload :Stomp, 'em/protocols/stomp' 28 | autoload :SmtpClient, 'em/protocols/smtpclient' 29 | autoload :SmtpServer, 'em/protocols/smtpserver' 30 | autoload :SASLauth, 'em/protocols/saslauth' 31 | autoload :Memcache, 'em/protocols/memcache' 32 | autoload :Postgres3, 'em/protocols/postgres3' 33 | autoload :ObjectProtocol, 'em/protocols/object_protocol' 34 | autoload :Socks4, 'em/protocols/socks4' 35 | autoload :LineProtocol, 'em/protocols/line_protocol' 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /tests/test_file_watch.rb: -------------------------------------------------------------------------------- 1 | require 'em_test_helper' 2 | require 'tempfile' 3 | 4 | class TestFileWatch < Test::Unit::TestCase 5 | if windows? 6 | def test_watch_file_raises_unsupported_error 7 | assert_raises(EM::Unsupported) do 8 | EM.run do 9 | file = Tempfile.new("fake_file") 10 | EM.watch_file(file.path) 11 | end 12 | end 13 | end 14 | elsif EM.respond_to? :watch_filename 15 | module FileWatcher 16 | def file_modified 17 | $modified = true 18 | end 19 | def file_deleted 20 | $deleted = true 21 | end 22 | def unbind 23 | $unbind = true 24 | EM.stop 25 | end 26 | end 27 | 28 | def setup 29 | EM.kqueue = true if EM.kqueue? 30 | end 31 | 32 | def teardown 33 | EM.kqueue = false if EM.kqueue? 34 | end 35 | 36 | def test_events 37 | EM.run{ 38 | file = Tempfile.new('em-watch') 39 | $tmp_path = file.path 40 | 41 | # watch it 42 | watch = EM.watch_file(file.path, FileWatcher) 43 | $path = watch.path 44 | 45 | # modify it 46 | File.open(file.path, 'w'){ |f| f.puts 'hi' } 47 | 48 | # delete it 49 | EM.add_timer(0.01){ file.close; file.delete } 50 | } 51 | 52 | assert_equal($path, $tmp_path) 53 | assert($modified) 54 | assert($deleted) 55 | assert($unbind) 56 | end 57 | else 58 | warn "EM.watch_file not implemented, skipping tests in #{__FILE__}" 59 | 60 | # Because some rubies will complain if a TestCase class has no tests 61 | def test_em_watch_file_unsupported 62 | assert true 63 | end 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /lib/em/channel.rb: -------------------------------------------------------------------------------- 1 | module EventMachine 2 | # Provides a simple thread-safe way to transfer data between (typically) long running 3 | # tasks in {EventMachine.defer} and event loop thread. 4 | # 5 | # @example 6 | # 7 | # channel = EventMachine::Channel.new 8 | # sid = channel.subscribe { |msg| p [:got, msg] } 9 | # 10 | # channel.push('hello world') 11 | # channel.unsubscribe(sid) 12 | # 13 | # 14 | class Channel 15 | def initialize 16 | @subs = {} 17 | @uid = 0 18 | end 19 | 20 | # Takes any arguments suitable for EM::Callback() and returns a subscriber 21 | # id for use when unsubscribing. 22 | # 23 | # @return [Integer] Subscribe identifier 24 | # @see #unsubscribe 25 | def subscribe(*a, &b) 26 | name = gen_id 27 | EM.schedule { @subs[name] = EM::Callback(*a, &b) } 28 | 29 | name 30 | end 31 | 32 | # Removes subscriber from the list. 33 | # 34 | # @param [Integer] Subscriber identifier 35 | # @see #subscribe 36 | def unsubscribe(name) 37 | EM.schedule { @subs.delete name } 38 | end 39 | 40 | # Add items to the channel, which are pushed out to all subscribers. 41 | def push(*items) 42 | items = items.dup 43 | EM.schedule { items.each { |i| @subs.values.each { |s| s.call i } } } 44 | end 45 | alias << push 46 | 47 | # Fetches one message from the channel. 48 | def pop(*a, &b) 49 | EM.schedule { 50 | name = subscribe do |*args| 51 | unsubscribe(name) 52 | EM::Callback(*a, &b).call(*args) 53 | end 54 | } 55 | end 56 | 57 | private 58 | 59 | # @private 60 | def gen_id 61 | @uid += 1 62 | end 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /lib/em/protocols/socks4.rb: -------------------------------------------------------------------------------- 1 | module EventMachine 2 | module Protocols 3 | # Basic SOCKS v4 client implementation 4 | # 5 | # Use as you would any regular connection: 6 | # 7 | # class MyConn < EM::P::Socks4 8 | # def post_init 9 | # send_data("sup") 10 | # end 11 | # 12 | # def receive_data(data) 13 | # send_data("you said: #{data}") 14 | # end 15 | # end 16 | # 17 | # EM.connect socks_host, socks_port, MyConn, host, port 18 | # 19 | class Socks4 < Connection 20 | def initialize(host, port) 21 | @host = Socket.gethostbyname(host).last 22 | @port = port 23 | @socks_error_code = nil 24 | @buffer = '' 25 | setup_methods 26 | end 27 | 28 | def setup_methods 29 | class << self 30 | def post_init; socks_post_init; end 31 | def receive_data(*a); socks_receive_data(*a); end 32 | end 33 | end 34 | 35 | def restore_methods 36 | class << self 37 | remove_method :post_init 38 | remove_method :receive_data 39 | end 40 | end 41 | 42 | def socks_post_init 43 | header = [4, 1, @port, @host, 0].flatten.pack("CCnA4C") 44 | send_data(header) 45 | end 46 | 47 | def socks_receive_data(data) 48 | @buffer << data 49 | return if @buffer.size < 8 50 | 51 | header_resp = @buffer.slice! 0, 8 52 | _, r = header_resp.unpack("cc") 53 | if r != 90 54 | @socks_error_code = r 55 | close_connection 56 | return 57 | end 58 | 59 | restore_methods 60 | 61 | post_init 62 | receive_data(@buffer) unless @buffer.empty? 63 | end 64 | end 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /tests/test_smtpserver.rb: -------------------------------------------------------------------------------- 1 | require 'em_test_helper' 2 | 3 | class TestSmtpServer < Test::Unit::TestCase 4 | 5 | # Don't test on port 25. It requires superuser and there's probably 6 | # a mail server already running there anyway. 7 | Localhost = "127.0.0.1" 8 | Localport = 25001 9 | 10 | # This class is an example of what you need to write in order 11 | # to implement a mail server. You override the methods you are 12 | # interested in. Some, but not all, of these are illustrated here. 13 | # 14 | class Mailserver < EM::Protocols::SmtpServer 15 | 16 | attr_reader :my_msg_body, :my_sender, :my_recipients 17 | 18 | def initialize *args 19 | super 20 | end 21 | def receive_sender sender 22 | @my_sender = sender 23 | #p sender 24 | true 25 | end 26 | def receive_recipient rcpt 27 | @my_recipients ||= [] 28 | @my_recipients << rcpt 29 | true 30 | end 31 | def receive_data_chunk c 32 | @my_msg_body = c.last 33 | end 34 | def connection_ended 35 | EM.stop 36 | end 37 | end 38 | 39 | def test_mail 40 | c = nil 41 | EM.run { 42 | EM.start_server( Localhost, Localport, Mailserver ) {|conn| c = conn} 43 | EM::Timer.new(2) {EM.stop} # prevent hanging the test suite in case of error 44 | EM::Protocols::SmtpClient.send :host=>Localhost, 45 | :port=>Localport, 46 | :domain=>"bogus", 47 | :from=>"me@example.com", 48 | :to=>"you@example.com", 49 | :header=> {"Subject"=>"Email subject line", "Reply-to"=>"me@example.com"}, 50 | :body=>"Not much of interest here." 51 | 52 | } 53 | assert_equal( "Not much of interest here.", c.my_msg_body ) 54 | assert_equal( "", c.my_sender ) 55 | assert_equal( [""], c.my_recipients ) 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /eventmachine.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | $:.unshift File.expand_path("../lib", __FILE__) 3 | require "em/version" 4 | 5 | 6 | Gem::Specification.new do |s| 7 | s.name = 'eventmachine' 8 | s.version = EventMachine::VERSION 9 | s.homepage = 'http://rubyeventmachine.com' 10 | s.rubyforge_project = 'eventmachine' 11 | s.licenses = ["Ruby", "GPL"] 12 | 13 | s.authors = ["Francis Cianfrocca", "Aman Gupta"] 14 | s.email = ["garbagecat10@gmail.com", "aman@tmm1.net"] 15 | 16 | s.files = `git ls-files`.split("\n") 17 | s.extensions = ["ext/extconf.rb", "ext/fastfilereader/extconf.rb"] 18 | 19 | s.add_development_dependency 'test-unit', '~> 2.0' 20 | s.add_development_dependency 'rake-compiler', '~> 0.8.3' 21 | s.add_development_dependency 'yard', ">= 0.8.5.2" 22 | s.add_development_dependency 'bluecloth' unless RUBY_PLATFORM =~ /java/ 23 | 24 | s.summary = 'Ruby/EventMachine library' 25 | s.description = "EventMachine implements a fast, single-threaded engine for arbitrary network 26 | communications. It's extremely easy to use in Ruby. EventMachine wraps all 27 | interactions with IP sockets, allowing programs to concentrate on the 28 | implementation of network protocols. It can be used to create both network 29 | servers and clients. To create a server or client, a Ruby program only needs 30 | to specify the IP address and port, and provide a Module that implements the 31 | communications protocol. Implementations of several standard network protocols 32 | are provided with the package, primarily to serve as examples. The real goal 33 | of EventMachine is to enable programs to easily interface with other programs 34 | using TCP/IP, especially if custom protocols are required." 35 | 36 | s.rdoc_options = ["--title", "EventMachine", "--main", "README.md", "-x", "lib/em/version", "-x", "lib/jeventmachine"] 37 | s.extra_rdoc_files = ["README.md"] + `git ls-files -- docs/*`.split("\n") 38 | end 39 | -------------------------------------------------------------------------------- /tests/client.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFRDCCAywCAQEwDQYJKoZIhvcNAQEFBQAwaDELMAkGA1UEBhMCRU0xFTATBgNV 3 | BAgTDEV2ZW50TWFjaGluZTEVMBMGA1UEChMMRXZlbnRNYWNoaW5lMRQwEgYDVQQL 4 | EwtEZXZlbG9wbWVudDEVMBMGA1UEAxMMRXZlbnRNYWNoaW5lMB4XDTA5MDMyOTAy 5 | MzE0NloXDTEwMDMyOTAyMzE0NlowaDELMAkGA1UEBhMCRU0xFTATBgNVBAgTDEV2 6 | ZW50TWFjaGluZTEVMBMGA1UEChMMRXZlbnRNYWNoaW5lMRQwEgYDVQQLEwtEZXZl 7 | bG9wbWVudDEVMBMGA1UEAxMMRXZlbnRNYWNoaW5lMIICIjANBgkqhkiG9w0BAQEF 8 | AAOCAg8AMIICCgKCAgEAv1FSOIX1z7CQtVBFlrB0A3/V29T+22STKKmiRWYkKL5b 9 | +hkrp9IZ5J4phZHgUVM2VDPOO2Oc2PU6dlGGZISg+UPERunTogxQKezCV0vcE9cK 10 | OwzxCFDRvv5rK8aKMscfBLbNKocAXywuRRQmdxPiVRzbyPrl+qCr/EDLXAX3D77l 11 | S8n2AwDg19VyI+IgFUE+Dy5e1eLoY6nV+Mq+vNXdn3ttF3t+ngac5pj5Q9h+pD5p 12 | 67baDHSnf/7cy2fa/LKrLolVHQR9G2K6cEfeM99NtcsMbkoPs4iI3FA05OVTQHXg 13 | C8C8cRxrb9APl95I/ep65OIaCJgcdYxJ3QD3qOtQo6/NQsGnjbyiUxaEpjfqyT1N 14 | uzWD81Q8uXGNS8yD6dDynt/lseBjyp2nfC3uQ5fY18VdIcu0MJ9pezBUKrNuhlsy 15 | XXEZ2DXj4sY8QOvIcBqSB/zmS1nGEK55xrtkaiaNrY8fe8wRVpcPLxy+P225NFw+ 16 | B69FJRA0Lj6Jt9BM4hV/3MSIEWwTVhuw4E02ywDYTzz1wq3ITf0tsbIPn0hXQMxD 17 | ohhAoKioM6u+yHtqsxD0eYaAWmHTVn5oDvOSGpvCpBfWHyA7FP5UQak0fKABEAgK 18 | iQYEnb294AXwXymJttfGTIV/Ne4tLN5dIpNma8UO8rlThlcr6xnTQDbR3gkTDRsC 19 | AwEAATANBgkqhkiG9w0BAQUFAAOCAgEAj7J8fy1LUWoVWnrXDAC9jwJ1nI/YjoSU 20 | 6ywke3o04+nZC5S+dPnuVy+HAwsU940CoNvP6RStI/bH6JL+NIqEFmwM3M8xIEWV 21 | MYVPkfvQUxxGvDnaY7vv93u+6Q77HV3qlhAQBHChyuXyO7TG3+WzsiT9AnBNtAP0 22 | 4jClt5kCAQXLO/p0SFEZQ8Ru9SM8d1i73Z0VDVzs8jYWlBhiherSgbw1xK4wBOpJ 23 | 43XmjZsBSrDpiAXd07Ak3UL2GjfT7eStgebL3UIe39ThE/s/+l43bh0M6WbOBvyQ 24 | i/rZ50kd1GvN0xnZhtv07hIJWO85FGWi7Oet8AzdUZJ17v1Md/f2vdhPVTFN9q+w 25 | mQ6LxjackqCvaJaQfBEbqsn2Tklxk4tZuDioiQbOElT2e6vljQVJWIfNx38Ny2LM 26 | aiXQPQu+4CI7meAh5gXM5nyJGbZvRPsxj89CqYzyHCYs5HBP3AsviBvn26ziOF+c 27 | 544VmHd9HkIv8UTC29hh+R64RlgMQQQdaXFaUrFPTs/do0k8n/c2bPc0iTdfi5Q2 28 | gq6Vi8q6Ay5wGgTtRRbn/mWKuCFjEh94z6pF9Xr06NX0PuEOdf+Ls9vI5vz6G0w6 29 | 0Li7devEN7EKBY+7Mcjg918yq9i5tEiMkUgT68788t3fTC+4iUQ5fDtdrHsaOlIR 30 | 8bs/XQVNE/s= 31 | -----END CERTIFICATE----- 32 | -------------------------------------------------------------------------------- /tests/test_resolver.rb: -------------------------------------------------------------------------------- 1 | require 'em_test_helper' 2 | 3 | class TestResolver < Test::Unit::TestCase 4 | def test_a 5 | EM.run { 6 | d = EM::DNS::Resolver.resolve "google.com" 7 | d.errback { assert false } 8 | d.callback { |r| 9 | assert r 10 | EM.stop 11 | } 12 | } 13 | end 14 | 15 | def test_bad_host 16 | EM.run { 17 | d = EM::DNS::Resolver.resolve "asdfasasdf" 18 | d.callback { assert false } 19 | d.errback { assert true; EM.stop } 20 | } 21 | end 22 | 23 | def test_garbage 24 | assert_raises( ArgumentError ) { 25 | EM.run { 26 | EM::DNS::Resolver.resolve 123 27 | } 28 | } 29 | end 30 | 31 | def test_a_pair 32 | EM.run { 33 | d = EM::DNS::Resolver.resolve "google.com" 34 | d.errback { assert false } 35 | d.callback { |r| 36 | assert_kind_of(Array, r) 37 | assert r.size > 1 38 | EM.stop 39 | } 40 | } 41 | end 42 | 43 | def test_localhost 44 | EM.run { 45 | d = EM::DNS::Resolver.resolve "localhost" 46 | d.errback { assert false } 47 | d.callback { |r| 48 | assert_include(["127.0.0.1", "::1"], r.first) 49 | assert_kind_of(Array, r) 50 | 51 | EM.stop 52 | } 53 | } 54 | end 55 | 56 | def test_timer_cleanup 57 | EM.run { 58 | d = EM::DNS::Resolver.resolve "google.com" 59 | d.errback { assert false } 60 | d.callback { |r| 61 | # This isn't a great test, but it's hard to get more canonical 62 | # confirmation that the timer is cancelled 63 | assert_nil(EM::DNS::Resolver.socket.instance_variable_get(:@timer)) 64 | 65 | EM.stop 66 | } 67 | } 68 | end 69 | 70 | def test_failure_timer_cleanup 71 | EM.run { 72 | d = EM::DNS::Resolver.resolve "asdfasdf" 73 | d.callback { assert false } 74 | d.errback { 75 | assert_nil(EM::DNS::Resolver.socket.instance_variable_get(:@timer)) 76 | 77 | EM.stop 78 | } 79 | } 80 | end 81 | end 82 | -------------------------------------------------------------------------------- /ext/kb.cpp: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | 3 | $Id$ 4 | 5 | File: kb.cpp 6 | Date: 24Aug07 7 | 8 | Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved. 9 | Gmail: blackhedd 10 | 11 | This program is free software; you can redistribute it and/or modify 12 | it under the terms of either: 1) the GNU General Public License 13 | as published by the Free Software Foundation; either version 2 of the 14 | License, or (at your option) any later version; or 2) Ruby's License. 15 | 16 | See the file COPYING for complete licensing information. 17 | 18 | *****************************************************************************/ 19 | 20 | #include "project.h" 21 | 22 | 23 | /************************************** 24 | KeyboardDescriptor::KeyboardDescriptor 25 | **************************************/ 26 | 27 | KeyboardDescriptor::KeyboardDescriptor (EventMachine_t *parent_em): 28 | EventableDescriptor (0, parent_em), 29 | bReadAttemptedAfterClose (false) 30 | { 31 | #ifdef HAVE_EPOLL 32 | EpollEvent.events = EPOLLIN; 33 | #endif 34 | #ifdef HAVE_KQUEUE 35 | MyEventMachine->ArmKqueueReader (this); 36 | #endif 37 | } 38 | 39 | 40 | /*************************************** 41 | KeyboardDescriptor::~KeyboardDescriptor 42 | ***************************************/ 43 | 44 | KeyboardDescriptor::~KeyboardDescriptor() 45 | { 46 | } 47 | 48 | 49 | /************************* 50 | KeyboardDescriptor::Write 51 | *************************/ 52 | 53 | void KeyboardDescriptor::Write() 54 | { 55 | // Why are we here? 56 | throw std::runtime_error ("bad code path in keyboard handler"); 57 | } 58 | 59 | 60 | /***************************** 61 | KeyboardDescriptor::Heartbeat 62 | *****************************/ 63 | 64 | void KeyboardDescriptor::Heartbeat() 65 | { 66 | // no-op 67 | } 68 | 69 | 70 | /************************ 71 | KeyboardDescriptor::Read 72 | ************************/ 73 | 74 | void KeyboardDescriptor::Read() 75 | { 76 | char c; 77 | read (GetSocket(), &c, 1); 78 | _GenericInboundDispatch(&c, 1); 79 | } 80 | -------------------------------------------------------------------------------- /lib/em/future.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # 3 | # Author:: Francis Cianfrocca (gmail: blackhedd) 4 | # Homepage:: http://rubyeventmachine.com 5 | # Date:: 16 Jul 2006 6 | # 7 | # See EventMachine and EventMachine::Connection for documentation and 8 | # usage examples. 9 | # 10 | #---------------------------------------------------------------------------- 11 | # 12 | # Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved. 13 | # Gmail: blackhedd 14 | # 15 | # This program is free software; you can redistribute it and/or modify 16 | # it under the terms of either: 1) the GNU General Public License 17 | # as published by the Free Software Foundation; either version 2 of the 18 | # License, or (at your option) any later version; or 2) Ruby's License. 19 | # 20 | # See the file COPYING for complete licensing information. 21 | # 22 | #--------------------------------------------------------------------------- 23 | # 24 | # 25 | 26 | #-- 27 | # This defines EventMachine::Deferrable#future, which requires 28 | # that the rest of EventMachine::Deferrable has already been seen. 29 | # (It's in deferrable.rb.) 30 | 31 | module EventMachine 32 | module Deferrable 33 | 34 | # A future is a sugaring of a typical deferrable usage. 35 | #-- 36 | # Evaluate arg (which may be an expression or a block). 37 | # What's the class of arg? 38 | # If arg is an ordinary expression, then return it. 39 | # If arg is deferrable (responds to :set_deferred_status), 40 | # then look at the arguments. If either callback or errback 41 | # are defined, then use them. If neither are defined, then 42 | # use the supplied block (if any) as the callback. 43 | # Then return arg. 44 | def self.future arg, cb=nil, eb=nil, &blk 45 | arg = arg.call if arg.respond_to?(:call) 46 | 47 | if arg.respond_to?(:set_deferred_status) 48 | if cb || eb 49 | arg.callback(&cb) if cb 50 | arg.errback(&eb) if eb 51 | else 52 | arg.callback(&blk) if blk 53 | end 54 | end 55 | 56 | arg 57 | end 58 | 59 | end 60 | end 61 | 62 | -------------------------------------------------------------------------------- /java/src/com/rubyeventmachine/EventableChannel.java: -------------------------------------------------------------------------------- 1 | /** 2 | * $Id$ 3 | * 4 | * Author:: Francis Cianfrocca (gmail: blackhedd) 5 | * Homepage:: http://rubyeventmachine.com 6 | * Date:: 15 Jul 2007 7 | * 8 | * See EventMachine and EventMachine::Connection for documentation and 9 | * usage examples. 10 | * 11 | * 12 | *---------------------------------------------------------------------------- 13 | * 14 | * Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved. 15 | * Gmail: blackhedd 16 | * 17 | * This program is free software; you can redistribute it and/or modify 18 | * it under the terms of either: 1) the GNU General Public License 19 | * as published by the Free Software Foundation; either version 2 of the 20 | * License, or (at your option) any later version; or 2) Ruby's License. 21 | * 22 | * See the file COPYING for complete licensing information. 23 | * 24 | *--------------------------------------------------------------------------- 25 | * 26 | * 27 | */ 28 | 29 | 30 | package com.rubyeventmachine; 31 | 32 | import java.nio.ByteBuffer; 33 | import java.io.IOException; 34 | import java.nio.channels.ClosedChannelException; 35 | 36 | public interface EventableChannel { 37 | 38 | public void scheduleOutboundData (ByteBuffer bb); 39 | 40 | public void scheduleOutboundDatagram (ByteBuffer bb, String recipAddress, int recipPort); 41 | 42 | public boolean scheduleClose (boolean afterWriting); 43 | 44 | public void startTls(); 45 | 46 | public long getBinding(); 47 | 48 | public void readInboundData (ByteBuffer dst) throws IOException; 49 | 50 | public void register() throws ClosedChannelException; 51 | 52 | /** 53 | * This is called by the reactor after it finishes running. 54 | * The idea is to free network resources. 55 | */ 56 | public void close(); 57 | 58 | public boolean writeOutboundData() throws IOException; 59 | 60 | public void setCommInactivityTimeout (long seconds); 61 | 62 | public Object[] getPeerName(); 63 | public Object[] getSockName(); 64 | 65 | public boolean isWatchOnly(); 66 | 67 | public boolean isNotifyReadable(); 68 | public boolean isNotifyWritable(); 69 | 70 | } 71 | -------------------------------------------------------------------------------- /examples/guides/getting_started/06_simple_chat_server_step_three.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'rubygems' # or use Bundler.setup 4 | require 'eventmachine' 5 | 6 | class SimpleChatServer < EM::Connection 7 | 8 | @@connected_clients = Array.new 9 | 10 | 11 | attr_reader :username 12 | 13 | 14 | # 15 | # EventMachine handlers 16 | # 17 | 18 | def post_init 19 | @username = nil 20 | 21 | puts "A client has connected..." 22 | ask_username 23 | end 24 | 25 | def unbind 26 | @@connected_clients.delete(self) 27 | puts "A client has left..." 28 | end 29 | 30 | def receive_data(data) 31 | if entered_username? 32 | handle_chat_message(data.strip) 33 | else 34 | handle_username(data.strip) 35 | end 36 | end 37 | 38 | 39 | 40 | 41 | # 42 | # Username handling 43 | # 44 | 45 | def entered_username? 46 | !@username.nil? && !@username.empty? 47 | end # entered_username? 48 | 49 | def handle_username(input) 50 | if input.empty? 51 | send_line("Blank usernames are not allowed. Try again.") 52 | ask_username 53 | else 54 | @username = input 55 | @@connected_clients.push(self) 56 | self.other_peers.each { |c| c.send_data("#{@username} has joined the room\n") } 57 | puts "#{@username} has joined" 58 | 59 | self.send_line("[info] Ohai, #{@username}") 60 | end 61 | end # handle_username(input) 62 | 63 | def ask_username 64 | self.send_line("[info] Enter your username:") 65 | end # ask_username 66 | 67 | 68 | 69 | # 70 | # Message handling 71 | # 72 | 73 | def handle_chat_message(msg) 74 | raise NotImplementedError 75 | end 76 | 77 | 78 | 79 | # 80 | # Helpers 81 | # 82 | 83 | def other_peers 84 | @@connected_clients.reject { |c| self == c } 85 | end # other_peers 86 | 87 | def send_line(line) 88 | self.send_data("#{line}\n") 89 | end # send_line(line) 90 | end 91 | 92 | EventMachine.run do 93 | # hit Control + C to stop 94 | Signal.trap("INT") { EventMachine.stop } 95 | Signal.trap("TERM") { EventMachine.stop } 96 | 97 | EventMachine.start_server("0.0.0.0", 10000, SimpleChatServer) 98 | end 99 | -------------------------------------------------------------------------------- /lib/em/queue.rb: -------------------------------------------------------------------------------- 1 | module EventMachine 2 | # A cross thread, reactor scheduled, linear queue. 3 | # 4 | # This class provides a simple queue abstraction on top of the reactor 5 | # scheduler. It services two primary purposes: 6 | # 7 | # * API sugar for stateful protocols 8 | # * Pushing processing onto the reactor thread 9 | # 10 | # @example 11 | # 12 | # q = EM::Queue.new 13 | # q.push('one', 'two', 'three') 14 | # 3.times do 15 | # q.pop { |msg| puts(msg) } 16 | # end 17 | # 18 | class Queue 19 | def initialize 20 | @items = [] 21 | @popq = [] 22 | end 23 | 24 | # Pop items off the queue, running the block on the reactor thread. The pop 25 | # will not happen immediately, but at some point in the future, either in 26 | # the next tick, if the queue has data, or when the queue is populated. 27 | # 28 | # @return [NilClass] nil 29 | def pop(*a, &b) 30 | cb = EM::Callback(*a, &b) 31 | EM.schedule do 32 | if @items.empty? 33 | @popq << cb 34 | else 35 | cb.call @items.shift 36 | end 37 | end 38 | nil # Always returns nil 39 | end 40 | 41 | # Push items onto the queue in the reactor thread. The items will not appear 42 | # in the queue immediately, but will be scheduled for addition during the 43 | # next reactor tick. 44 | def push(*items) 45 | EM.schedule do 46 | @items.push(*items) 47 | @popq.shift.call @items.shift until @items.empty? || @popq.empty? 48 | end 49 | end 50 | alias :<< :push 51 | 52 | # @return [Boolean] 53 | # @note This is a peek, it's not thread safe, and may only tend toward accuracy. 54 | def empty? 55 | @items.empty? 56 | end 57 | 58 | # @return [Integer] Queue size 59 | # @note This is a peek, it's not thread safe, and may only tend toward accuracy. 60 | def size 61 | @items.size 62 | end 63 | 64 | # @return [Integer] Waiting size 65 | # @note This is a peek at the number of jobs that are currently waiting on the Queue 66 | def num_waiting 67 | @popq.size 68 | end 69 | 70 | end # Queue 71 | end # EventMachine 72 | -------------------------------------------------------------------------------- /tests/test_ssl_args.rb: -------------------------------------------------------------------------------- 1 | require "test/unit" 2 | require 'tempfile' 3 | 4 | require 'em_test_helper' 5 | 6 | module EM 7 | def self._set_mocks 8 | class < priv_file) 56 | end 57 | assert_raises(EM::FileNotFoundException) do 58 | conn.start_tls(:cert_chain_file => cert_file) 59 | end 60 | assert_raises(EM::FileNotFoundException) do 61 | conn.start_tls(:private_key_file => priv_file, :cert_chain_file => cert_file) 62 | end 63 | end 64 | 65 | def test_tls_params_file_does_exist 66 | priv_file = Tempfile.new('em_test') 67 | cert_file = Tempfile.new('em_test') 68 | priv_file_path = priv_file.path 69 | cert_file_path = cert_file.path 70 | conn = EM::Connection.new('foo') 71 | params = {:private_key_file => priv_file_path, :cert_chain_file => cert_file_path} 72 | begin 73 | conn.start_tls params 74 | rescue Object 75 | assert(false, 'should not have raised an exception') 76 | end 77 | end 78 | end if EM.ssl? 79 | -------------------------------------------------------------------------------- /tests/test_ssl_verify.rb: -------------------------------------------------------------------------------- 1 | require 'em_test_helper' 2 | 3 | class TestSslVerify < Test::Unit::TestCase 4 | def setup 5 | $dir = File.dirname(File.expand_path(__FILE__)) + '/' 6 | $cert_from_file = File.read($dir+'client.crt') 7 | end 8 | 9 | module Client 10 | def connection_completed 11 | start_tls(:private_key_file => $dir+'client.key', :cert_chain_file => $dir+'client.crt') 12 | end 13 | 14 | def ssl_handshake_completed 15 | $client_handshake_completed = true 16 | close_connection 17 | end 18 | 19 | def unbind 20 | EM.stop_event_loop 21 | end 22 | end 23 | 24 | module AcceptServer 25 | def post_init 26 | start_tls(:verify_peer => true) 27 | end 28 | 29 | def ssl_verify_peer(cert) 30 | $cert_from_server = cert 31 | true 32 | end 33 | 34 | def ssl_handshake_completed 35 | $server_handshake_completed = true 36 | end 37 | end 38 | 39 | module DenyServer 40 | def post_init 41 | start_tls(:verify_peer => true) 42 | end 43 | 44 | def ssl_verify_peer(cert) 45 | $cert_from_server = cert 46 | # Do not accept the peer. This should now cause the connection to shut down without the SSL handshake being completed. 47 | false 48 | end 49 | 50 | def ssl_handshake_completed 51 | $server_handshake_completed = true 52 | end 53 | end 54 | 55 | def test_accept_server 56 | omit_unless(EM.ssl?) 57 | $client_handshake_completed, $server_handshake_completed = false, false 58 | EM.run { 59 | EM.start_server("127.0.0.1", 16784, AcceptServer) 60 | EM.connect("127.0.0.1", 16784, Client).instance_variable_get("@signature") 61 | } 62 | 63 | assert_equal($cert_from_file, $cert_from_server) 64 | assert($client_handshake_completed) 65 | assert($server_handshake_completed) 66 | end 67 | 68 | def test_deny_server 69 | omit_unless(EM.ssl?) 70 | $client_handshake_completed, $server_handshake_completed = false, false 71 | EM.run { 72 | EM.start_server("127.0.0.1", 16784, DenyServer) 73 | EM.connect("127.0.0.1", 16784, Client) 74 | } 75 | 76 | assert_equal($cert_from_file, $cert_from_server) 77 | assert(!$client_handshake_completed) 78 | assert(!$server_handshake_completed) 79 | end 80 | end 81 | -------------------------------------------------------------------------------- /ext/page.cpp: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | 3 | $Id$ 4 | 5 | File: page.cpp 6 | Date: 30Apr06 7 | 8 | Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved. 9 | Gmail: blackhedd 10 | 11 | This program is free software; you can redistribute it and/or modify 12 | it under the terms of either: 1) the GNU General Public License 13 | as published by the Free Software Foundation; either version 2 of the 14 | License, or (at your option) any later version; or 2) Ruby's License. 15 | 16 | See the file COPYING for complete licensing information. 17 | 18 | *****************************************************************************/ 19 | 20 | 21 | #include "project.h" 22 | 23 | 24 | /****************** 25 | PageList::PageList 26 | ******************/ 27 | 28 | PageList::PageList() 29 | { 30 | } 31 | 32 | 33 | /******************* 34 | PageList::~PageList 35 | *******************/ 36 | 37 | PageList::~PageList() 38 | { 39 | while (HasPages()) 40 | PopFront(); 41 | } 42 | 43 | 44 | /*************** 45 | PageList::Front 46 | ***************/ 47 | 48 | void PageList::Front (const char **page, int *length) 49 | { 50 | assert (page && length); 51 | 52 | if (HasPages()) { 53 | Page p = Pages.front(); 54 | *page = p.Buffer; 55 | *length = p.Size; 56 | } 57 | else { 58 | *page = NULL; 59 | *length = 0; 60 | } 61 | } 62 | 63 | 64 | /****************** 65 | PageList::PopFront 66 | ******************/ 67 | 68 | void PageList::PopFront() 69 | { 70 | if (HasPages()) { 71 | Page p = Pages.front(); 72 | Pages.pop_front(); 73 | if (p.Buffer) 74 | free ((void*)p.Buffer); 75 | } 76 | } 77 | 78 | 79 | /****************** 80 | PageList::HasPages 81 | ******************/ 82 | 83 | bool PageList::HasPages() 84 | { 85 | return (Pages.size() > 0) ? true : false; 86 | } 87 | 88 | 89 | /************** 90 | PageList::Push 91 | **************/ 92 | 93 | void PageList::Push (const char *buf, int size) 94 | { 95 | if (buf && (size > 0)) { 96 | char *copy = (char*) malloc (size); 97 | if (!copy) 98 | throw runtime_error ("no memory in pagelist"); 99 | memcpy (copy, buf, size); 100 | Pages.push_back (Page (copy, size)); 101 | } 102 | } 103 | 104 | 105 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /lib/em/file_watch.rb: -------------------------------------------------------------------------------- 1 | module EventMachine 2 | # Utility class that is useful for file monitoring. Supported events are 3 | # 4 | # * File is modified 5 | # * File is deleted 6 | # * File is moved 7 | # 8 | # @note On Mac OS X, file watching only works when kqueue is enabled 9 | # 10 | # @see EventMachine.watch_file 11 | class FileWatch < Connection 12 | # @private 13 | Cmodified = 'modified'.freeze 14 | # @private 15 | Cdeleted = 'deleted'.freeze 16 | # @private 17 | Cmoved = 'moved'.freeze 18 | 19 | 20 | # @private 21 | def receive_data(data) 22 | case data 23 | when Cmodified 24 | file_modified 25 | when Cdeleted 26 | file_deleted 27 | when Cmoved 28 | file_moved 29 | end 30 | end 31 | 32 | # Returns the path that is being monitored. 33 | # 34 | # @note Current implementation does not pick up on the new filename after a rename occurs. 35 | # 36 | # @return [String] 37 | # @see EventMachine.watch_file 38 | def path 39 | @path 40 | end 41 | 42 | # Will be called when the file is modified. Supposed to be redefined by subclasses. 43 | # 44 | # @abstract 45 | def file_modified 46 | end 47 | 48 | # Will be called when the file is deleted. Supposed to be redefined by subclasses. 49 | # When the file is deleted, stop_watching will be called after this to make sure everything is 50 | # cleaned up correctly. 51 | # 52 | # @note On Linux (with {http://en.wikipedia.org/wiki/Inotify inotify}), this method will not be called until *all* open file descriptors to 53 | # the file have been closed. 54 | # 55 | # @abstract 56 | def file_deleted 57 | end 58 | 59 | # Will be called when the file is moved or renamed. Supposed to be redefined by subclasses. 60 | # 61 | # @abstract 62 | def file_moved 63 | end 64 | 65 | # Discontinue monitoring of the file. 66 | # 67 | # This involves cleaning up the underlying monitoring details with kqueue/inotify, and in turn firing {EventMachine::Connection#unbind}. 68 | # This will be called automatically when a file is deleted. User code may call it as well. 69 | def stop_watching 70 | EventMachine::unwatch_filename(@signature) 71 | end # stop_watching 72 | end # FileWatch 73 | end # EventMachine 74 | -------------------------------------------------------------------------------- /ext/ssl.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | 3 | $Id$ 4 | 5 | File: ssl.h 6 | Date: 30Apr06 7 | 8 | Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved. 9 | Gmail: blackhedd 10 | 11 | This program is free software; you can redistribute it and/or modify 12 | it under the terms of either: 1) the GNU General Public License 13 | as published by the Free Software Foundation; either version 2 of the 14 | License, or (at your option) any later version; or 2) Ruby's License. 15 | 16 | See the file COPYING for complete licensing information. 17 | 18 | *****************************************************************************/ 19 | 20 | 21 | #ifndef __SslBox__H_ 22 | #define __SslBox__H_ 23 | 24 | 25 | 26 | 27 | #ifdef WITH_SSL 28 | 29 | /****************** 30 | class SslContext_t 31 | ******************/ 32 | 33 | class SslContext_t 34 | { 35 | public: 36 | SslContext_t (bool is_server, const string &privkeyfile, const string &certchainfile); 37 | virtual ~SslContext_t(); 38 | 39 | private: 40 | static bool bLibraryInitialized; 41 | 42 | private: 43 | bool bIsServer; 44 | SSL_CTX *pCtx; 45 | 46 | EVP_PKEY *PrivateKey; 47 | X509 *Certificate; 48 | 49 | friend class SslBox_t; 50 | }; 51 | 52 | 53 | /************** 54 | class SslBox_t 55 | **************/ 56 | 57 | #define SSLBOX_INPUT_CHUNKSIZE 2019 58 | #define SSLBOX_OUTPUT_CHUNKSIZE 2048 59 | #define SSLBOX_WRITE_BUFFER_SIZE 8192 // (SSLBOX_OUTPUT_CHUNKSIZE * 4) 60 | 61 | class SslBox_t 62 | { 63 | public: 64 | SslBox_t (bool is_server, const string &privkeyfile, const string &certchainfile, bool verify_peer, const unsigned long binding); 65 | virtual ~SslBox_t(); 66 | 67 | int PutPlaintext (const char*, int); 68 | int GetPlaintext (char*, int); 69 | 70 | bool PutCiphertext (const char*, int); 71 | bool CanGetCiphertext(); 72 | int GetCiphertext (char*, int); 73 | bool IsHandshakeCompleted() {return bHandshakeCompleted;} 74 | 75 | X509 *GetPeerCert(); 76 | 77 | void Shutdown(); 78 | 79 | protected: 80 | SslContext_t *Context; 81 | 82 | bool bIsServer; 83 | bool bHandshakeCompleted; 84 | bool bVerifyPeer; 85 | SSL *pSSL; 86 | BIO *pbioRead; 87 | BIO *pbioWrite; 88 | 89 | PageList OutboundQ; 90 | }; 91 | 92 | extern "C" int ssl_verify_wrapper(int, X509_STORE_CTX*); 93 | 94 | #endif // WITH_SSL 95 | 96 | 97 | #endif // __SslBox__H_ 98 | 99 | -------------------------------------------------------------------------------- /lib/em/callback.rb: -------------------------------------------------------------------------------- 1 | module EventMachine 2 | # Utility method for coercing arguments to an object that responds to :call. 3 | # Accepts an object and a method name to send to, or a block, or an object 4 | # that responds to :call. 5 | # 6 | # @example EventMachine.Callback used with a block. Returns that block. 7 | # 8 | # cb = EventMachine.Callback do |msg| 9 | # puts(msg) 10 | # end 11 | # # returned object is a callable 12 | # cb.call('hello world') 13 | # 14 | # 15 | # @example EventMachine.Callback used with an object (to be more specific, class object) and a method name, returns an object that responds to #call 16 | # 17 | # cb = EventMachine.Callback(Object, :puts) 18 | # # returned object is a callable that delegates to Kernel#puts (in this case Object.puts) 19 | # cb.call('hello world') 20 | # 21 | # 22 | # @example EventMachine.Callback used with an object that responds to #call. Returns the argument. 23 | # 24 | # cb = EventMachine.Callback(proc{ |msg| puts(msg) }) 25 | # # returned object is a callable 26 | # cb.call('hello world') 27 | # 28 | # 29 | # @overload Callback(object, method) 30 | # Wraps `method` invocation on `object` into an object that responds to #call that proxies all the arguments to that method 31 | # @param [Object] Object to invoke method on 32 | # @param [Symbol] Method name 33 | # @return [<#call>] An object that responds to #call that takes any number of arguments and invokes method on object with those arguments 34 | # 35 | # @overload Callback(object) 36 | # Returns callable object as is, without any coercion 37 | # @param [<#call>] An object that responds to #call 38 | # @return [<#call>] Its argument 39 | # 40 | # @overload Callback(&block) 41 | # Returns block passed to it without any coercion 42 | # @return [<#call>] Block passed to this method 43 | # 44 | # @raise [ArgumentError] When argument doesn't respond to #call, method name is missing or when invoked without arguments and block isn't given 45 | # 46 | # @return [<#call>] 47 | def self.Callback(object = nil, method = nil, &blk) 48 | if object && method 49 | lambda { |*args| object.__send__ method, *args } 50 | else 51 | if object.respond_to? :call 52 | object 53 | else 54 | blk || raise(ArgumentError) 55 | end # if 56 | end # if 57 | end # self.Callback 58 | end # EventMachine 59 | -------------------------------------------------------------------------------- /lib/em/tick_loop.rb: -------------------------------------------------------------------------------- 1 | module EventMachine 2 | # Creates and immediately starts an EventMachine::TickLoop 3 | def self.tick_loop(*a, &b) 4 | TickLoop.new(*a, &b).start 5 | end 6 | 7 | # A TickLoop is useful when one needs to distribute amounts of work 8 | # throughout ticks in order to maintain response times. It is also useful for 9 | # simple repeated checks and metrics. 10 | # @example 11 | # # Here we run through an array one item per tick until it is empty, 12 | # # printing each element. 13 | # # When the array is empty, we return :stop from the callback, and the 14 | # # loop will terminate. 15 | # # When the loop terminates, the on_stop callbacks will be called. 16 | # EM.run do 17 | # array = (1..100).to_a 18 | # 19 | # tickloop = EM.tick_loop do 20 | # if array.empty? 21 | # :stop 22 | # else 23 | # puts array.shift 24 | # end 25 | # end 26 | # 27 | # tickloop.on_stop { EM.stop } 28 | # end 29 | # 30 | class TickLoop 31 | 32 | # Arguments: A callback (EM::Callback) to call each tick. If the call 33 | # returns +:stop+ then the loop will be stopped. Any other value is 34 | # ignored. 35 | def initialize(*a, &b) 36 | @work = EM::Callback(*a, &b) 37 | @stops = [] 38 | @stopped = true 39 | end 40 | 41 | # Arguments: A callback (EM::Callback) to call once on the next stop (or 42 | # immediately if already stopped). 43 | def on_stop(*a, &b) 44 | if @stopped 45 | EM::Callback(*a, &b).call 46 | else 47 | @stops << EM::Callback(*a, &b) 48 | end 49 | end 50 | 51 | # Stop the tick loop immediately, and call it's on_stop callbacks. 52 | def stop 53 | @stopped = true 54 | until @stops.empty? 55 | @stops.shift.call 56 | end 57 | end 58 | 59 | # Query if the loop is stopped. 60 | def stopped? 61 | @stopped 62 | end 63 | 64 | # Start the tick loop, will raise argument error if the loop is already 65 | # running. 66 | def start 67 | raise ArgumentError, "double start" unless @stopped 68 | @stopped = false 69 | schedule 70 | end 71 | 72 | private 73 | def schedule 74 | EM.next_tick do 75 | next if @stopped 76 | if @work.call == :stop 77 | stop 78 | else 79 | schedule 80 | end 81 | end 82 | self 83 | end 84 | end 85 | end -------------------------------------------------------------------------------- /lib/em/spawnable.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # 3 | # Author:: Francis Cianfrocca (gmail: blackhedd) 4 | # Homepage:: http://rubyeventmachine.com 5 | # Date:: 25 Aug 2007 6 | # 7 | # See EventMachine and EventMachine::Connection for documentation and 8 | # usage examples. 9 | # 10 | #---------------------------------------------------------------------------- 11 | # 12 | # Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved. 13 | # Gmail: blackhedd 14 | # 15 | # This program is free software; you can redistribute it and/or modify 16 | # it under the terms of either: 1) the GNU General Public License 17 | # as published by the Free Software Foundation; either version 2 of the 18 | # License, or (at your option) any later version; or 2) Ruby's License. 19 | # 20 | # See the file COPYING for complete licensing information. 21 | # 22 | #--------------------------------------------------------------------------- 23 | # 24 | # 25 | 26 | module EventMachine 27 | # Support for Erlang-style processes. 28 | # 29 | class SpawnedProcess 30 | # Send a message to the spawned process 31 | def notify *x 32 | me = self 33 | EM.next_tick { 34 | # A notification executes in the context of this 35 | # SpawnedProcess object. That makes self and notify 36 | # work as one would expect. 37 | # 38 | y = me.call(*x) 39 | if y and y.respond_to?(:pull_out_yield_block) 40 | a,b = y.pull_out_yield_block 41 | set_receiver a 42 | self.notify if b 43 | end 44 | } 45 | end 46 | alias_method :resume, :notify 47 | alias_method :run, :notify # for formulations like (EM.spawn {xxx}).run 48 | 49 | def set_receiver blk 50 | (class << self ; self ; end).class_eval do 51 | remove_method :call if method_defined? :call 52 | define_method :call, blk 53 | end 54 | end 55 | 56 | end 57 | 58 | # @private 59 | class YieldBlockFromSpawnedProcess 60 | def initialize block, notify 61 | @block = [block,notify] 62 | end 63 | def pull_out_yield_block 64 | @block 65 | end 66 | end 67 | 68 | # Spawn an erlang-style process 69 | def self.spawn &block 70 | s = SpawnedProcess.new 71 | s.set_receiver block 72 | s 73 | end 74 | 75 | # @private 76 | def self.yield &block 77 | return YieldBlockFromSpawnedProcess.new( block, false ) 78 | end 79 | 80 | # @private 81 | def self.yield_and_notify &block 82 | return YieldBlockFromSpawnedProcess.new( block, true ) 83 | end 84 | end 85 | -------------------------------------------------------------------------------- /rakelib/cpp.rake_example: -------------------------------------------------------------------------------- 1 | # EventMachine C++ Rakefile Stab Case 2 | # TODO : track header files as a build dependency... 3 | # TODO : cross platform support 4 | # TODO : configure style functionality 5 | namespace :cpp do 6 | 7 | require 'rake/clean' 8 | 9 | # *nix only atm... 10 | module Cpp 11 | class < [proc { |targ| 39 | targ.sub(%r{^#{EmConfig::Path}/(.*)\.o$}, "#{EmConfig::Path}/\\1.cpp") 40 | }] do |t| 41 | Cpp.compile t.source, t.name, EmConfig::Includes, EmConfig::Flags 42 | end 43 | 44 | file "#{EmConfig::Path}/libeventmachine.a" => EmConfig::Compiled do |t| 45 | Cpp.static t.name, EmConfig::Compiled 46 | end 47 | CLEAN.include("#{EmConfig::Path}/libeventmachine.a") 48 | 49 | module AppConfig 50 | Appname = 'echo_em' 51 | Sources = FileList['*.cpp'] 52 | Compiled = Sources.sub(%r{^(.*)\.cpp}, '\\1.o') 53 | 54 | Flags = ["", EmConfig::Flags].join(' ') 55 | Includes = ["-I. -I#{EmConfig::Path}", EmConfig::Includes].join(' ') 56 | Libs = ["-L#{EmConfig::Path} -leventmachine", EmConfig::Libs].join(' ') 57 | end 58 | CLEAN.include(AppConfig::Compiled) 59 | CLEAN.include(AppConfig::Appname) 60 | 61 | rule %r{^.*\.o$} => [proc { |targ| 62 | targ.sub(%r{^(.*)\.o$}, '\\1.cpp') 63 | }] do |t| 64 | Cpp.compile t.source, t.name, AppConfig::Includes, AppConfig::Flags 65 | end 66 | 67 | file AppConfig::Appname => ["#{EmConfig::Path}/libeventmachine.a", AppConfig::Compiled] do |t| 68 | Cpp.link AppConfig::Compiled, t.name, AppConfig::Libs, AppConfig::Flags 69 | end 70 | 71 | task :build => AppConfig::Appname 72 | 73 | task :run => AppConfig::Appname do 74 | sh "./#{AppConfig::Appname}" 75 | end 76 | 77 | end -------------------------------------------------------------------------------- /tests/test_iterator.rb: -------------------------------------------------------------------------------- 1 | require 'em_test_helper' 2 | 3 | class TestIterator < Test::Unit::TestCase 4 | 5 | def get_time 6 | EM.current_time.strftime('%H:%M:%S') 7 | end 8 | 9 | def test_default_concurrency 10 | items = {} 11 | list = 1..10 12 | EM.run { 13 | EM::Iterator.new(list).each( proc {|num,iter| 14 | time = get_time 15 | items[time] ||= [] 16 | items[time] << num 17 | EM::Timer.new(1) {iter.next} 18 | }, proc {EM.stop}) 19 | } 20 | assert_equal(10, items.keys.size) 21 | assert_equal(list.to_a.sort, items.values.flatten.sort) 22 | end 23 | 24 | def test_concurrency_bigger_than_list_size 25 | items = {} 26 | list = [1,2,3] 27 | EM.run { 28 | EM::Iterator.new(list,10).each(proc {|num,iter| 29 | time = get_time 30 | items[time] ||= [] 31 | items[time] << num 32 | EM::Timer.new(1) {iter.next} 33 | }, proc {EM.stop}) 34 | } 35 | assert_equal(1, items.keys.size) 36 | assert_equal(list.to_a.sort, items.values.flatten.sort) 37 | end 38 | 39 | 40 | def test_changing_concurrency_affects_active_iteration 41 | items = {} 42 | list = 1..25 43 | EM.run { 44 | i = EM::Iterator.new(list,5) 45 | i.each(proc {|num,iter| 46 | time = get_time 47 | items[time] ||= [] 48 | items[time] << num 49 | EM::Timer.new(1) {iter.next} 50 | }, proc {EM.stop}) 51 | EM.add_timer(1){ 52 | i.concurrency = 1 53 | } 54 | EM.add_timer(3){ 55 | i.concurrency = 3 56 | } 57 | } 58 | assert_equal(9, items.keys.size) 59 | assert_equal(list.to_a.sort, items.values.flatten.sort) 60 | end 61 | 62 | def test_map 63 | list = 100..150 64 | EM.run { 65 | EM::Iterator.new(list).map(proc{ |num,iter| 66 | EM.add_timer(0.01){ iter.return(num) } 67 | }, proc{ |results| 68 | assert_equal(list.to_a.size, results.size) 69 | EM.stop 70 | }) 71 | } 72 | end 73 | 74 | def test_inject 75 | list = %w[ pwd uptime uname date ] 76 | EM.run { 77 | EM::Iterator.new(list, 2).inject({}, proc{ |hash,cmd,iter| 78 | EM.system(cmd){ |output,status| 79 | hash[cmd] = status.exitstatus == 0 ? output.strip : nil 80 | iter.return(hash) 81 | } 82 | }, proc{ |results| 83 | assert_equal(results.keys.sort, list.sort) 84 | EM.stop 85 | }) 86 | } 87 | end 88 | 89 | def test_concurrency_is_0 90 | EM.run { 91 | assert_raise ArgumentError do 92 | EM::Iterator.new(1..5,0) 93 | end 94 | EM.stop 95 | } 96 | end 97 | end 98 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 1.0.5 4 | * use monotonic clocks on Linux, OS X, Solaris, and Windows [#563] 5 | * use the rb_fd_* API to get autosized fd_sets [#502] 6 | * add basic tests that the DNS resolver isn't leaking timers [#571] 7 | * update to test-unit 2.x and improve various unit tests [#551] 8 | * remove EventMachine_t::Popen code marked by ifdef OBSOLETE [#551] 9 | * ruby 2.0 may fail at Queue.pop, so rescue and complain to $stderr [#551] 10 | * set file handle to INVALID_HANDLE_VALUE after closing the file [#565] 11 | * use `defined?` instead of rescuing NameError for flow control [#535] 12 | * fix closing files and sockets on Windows [#564] 13 | * fix file uploads in Windows [#562] 14 | * catch failure to fork [#539] 15 | * use chunks for SSL write [#545] 16 | 17 | ## 1.0.4 (December 19, 2014) 18 | * add starttls_options to smtp server [#552] 19 | * fix closesocket on windows [#497] 20 | * fix build on ruby 2.2 [#503] 21 | * fix build error on ruby 1.9 [#508] 22 | * fix timer leak during dns resolution [#489] 23 | * add concurrency validation to EM::Iterator [#468] 24 | * add get_file_descriptor to get fd for a signature [#467] 25 | * add EM.attach_server and EM.attach_socket_server [#465, #466] 26 | * calling pause from receive_data takes effect immediately [#464] 27 | * reactor_running? returns false after fork [#455] 28 | * fix infinite loop on double close [edc4d0e6, #441, #445] 29 | * fix compilation issue on llvm [#433] 30 | * fix socket error codes on win32 [ff811a81] 31 | * fix EM.stop latency when timers exist [8b613d05, #426] 32 | * fix infinite loop when system time changes [1427a2c80, #428] 33 | * fix crash when callin attach/detach in the same tick [#427] 34 | * fix compilation issue on solaris [#416] 35 | 36 | ## 1.0.3 (March 8, 2013) 37 | * EM.system was broken in 1.0.2 release [#413] 38 | 39 | ## 1.0.2 (March 8, 2013) 40 | * binary win32 gems now include fastfilereader shim [#222] 41 | * fix long-standing connection timeout issues [27fdd5b, igrigorik/em-http-request#222] 42 | * http and line protocol cleanups [#193, #151] 43 | * reactor return value cleanup [#225] 44 | * fix double require from gemspec [#284] 45 | * fix smtp server reset behavior [#351] 46 | * fix EM.system argument handling [#322] 47 | * ruby 1.9 compat in smtp server and stomp protocols [#349, #315] 48 | * fix pause from post_init [#380] 49 | 50 | ## 1.0.1 (February 27, 2013) 51 | * use rb_wait_for_single_fd() on ruby 2.0 to fix rb_thread_select() deprecation [#363] 52 | * fix epoll/kqueue mode in ruby 2.0 by removing calls to rb_enable_interrupt() [#248, #389] 53 | * fix memory leak when verifying ssl cerificates [#403] 54 | * fix initial connection delay [#393, #374] 55 | * fix build on windows [#371] 56 | -------------------------------------------------------------------------------- /tests/test_pure.rb: -------------------------------------------------------------------------------- 1 | require 'em_test_helper' 2 | 3 | class TestPure < Test::Unit::TestCase 4 | 5 | def setup 6 | @port = next_port 7 | end 8 | 9 | # These tests are intended to exercise problems that come up in the 10 | # pure-Ruby implementation. However, we DON'T constrain them such that 11 | # they only run in pure-Ruby. These tests need to work identically in 12 | # any implementation. 13 | 14 | #------------------------------------- 15 | 16 | # The EM reactor needs to run down open connections and release other resources 17 | # when it stops running. Make sure this happens even if user code throws a Ruby 18 | # exception. 19 | # If exception handling is incorrect, the second test will fail with a no-bind error 20 | # because the TCP server opened in the first test will not have been closed. 21 | 22 | def test_exception_handling_releases_resources 23 | exception = Class.new(StandardError) 24 | 25 | 2.times do 26 | assert_raises(exception) do 27 | EM.run do 28 | EM.start_server "127.0.0.1", @port 29 | raise exception 30 | end 31 | end 32 | end 33 | end 34 | 35 | # Under some circumstances, the pure Ruby library would emit an Errno::ECONNREFUSED 36 | # exception on certain kinds of TCP connect-errors. 37 | # It's always been something of an open question whether EM should throw an exception 38 | # in these cases but the defined answer has always been to catch it the unbind method. 39 | # With a connect failure, the latter will always fire, but connection_completed will 40 | # never fire. So even though the point is arguable, it's incorrect for the pure Ruby 41 | # version to throw an exception. 42 | module TestConnrefused 43 | def unbind 44 | EM.stop 45 | end 46 | def connection_completed 47 | raise "should never get here" 48 | end 49 | end 50 | 51 | def test_connrefused 52 | assert_nothing_raised do 53 | EM.run { 54 | setup_timeout(2) 55 | EM.connect "127.0.0.1", @port, TestConnrefused 56 | } 57 | end 58 | end 59 | 60 | # Make sure connection_completed gets called as expected with TCP clients. This is the 61 | # opposite of test_connrefused. 62 | # If the test fails, it will hang because EM.stop never gets called. 63 | # 64 | module TestConnaccepted 65 | def connection_completed 66 | EM.stop 67 | end 68 | end 69 | def test_connaccepted 70 | assert_nothing_raised do 71 | EM.run { 72 | EM.start_server "127.0.0.1", @port 73 | EM.connect "127.0.0.1", @port, TestConnaccepted 74 | setup_timeout(1) 75 | } 76 | end 77 | end 78 | 79 | def test_reactor_running 80 | a = false 81 | EM.run { 82 | a = EM.reactor_running? 83 | EM.next_tick {EM.stop} 84 | } 85 | assert a 86 | end 87 | 88 | end 89 | -------------------------------------------------------------------------------- /tests/test_pause.rb: -------------------------------------------------------------------------------- 1 | require 'em_test_helper' 2 | 3 | class TestPause < Test::Unit::TestCase 4 | if EM.respond_to? :pause_connection 5 | def setup 6 | @port = next_port 7 | end 8 | 9 | def teardown 10 | assert(!EM.reactor_running?) 11 | end 12 | 13 | def test_pause_resume 14 | server = nil 15 | 16 | s_rx = c_rx = 0 17 | 18 | test_server = Module.new do 19 | define_method :post_init do 20 | server = self 21 | end 22 | 23 | define_method :receive_data do |data| 24 | s_rx += 1 25 | 26 | EM.add_periodic_timer(0.01) { send_data 'hi' } 27 | send_data 'hi' 28 | 29 | # pause server, now no outgoing data will actually 30 | # be sent and no more incoming data will be received 31 | pause 32 | end 33 | end 34 | 35 | test_client = Module.new do 36 | def post_init 37 | EM.add_periodic_timer(0.01) do 38 | send_data 'hello' 39 | end 40 | end 41 | 42 | define_method :receive_data do |data| 43 | c_rx += 1 44 | end 45 | end 46 | 47 | EM.run do 48 | EM.start_server "127.0.0.1", @port, test_server 49 | EM.connect "127.0.0.1", @port, test_client 50 | 51 | EM.add_timer(0.05) do 52 | assert_equal 1, s_rx 53 | assert_equal 0, c_rx 54 | assert server.paused? 55 | 56 | # resume server, queued outgoing and incoming data will be flushed 57 | server.resume 58 | 59 | assert !server.paused? 60 | 61 | EM.add_timer(0.05) do 62 | assert server.paused? 63 | assert s_rx > 1 64 | assert c_rx > 0 65 | EM.stop 66 | end 67 | end 68 | end 69 | end 70 | 71 | def test_pause_in_receive_data 72 | incoming = [] 73 | 74 | test_server = Module.new do 75 | define_method(:receive_data) do |data| 76 | incoming << data 77 | pause 78 | EM.add_timer(0.5){ close_connection } 79 | end 80 | define_method(:unbind) do 81 | EM.stop 82 | end 83 | end 84 | 85 | EM.run do 86 | EM.start_server "127.0.0.1", @port, test_server 87 | cli = EM.connect "127.0.0.1", @port 88 | cli.send_data 'a'*(17*1024) 89 | end 90 | 91 | assert_equal 1, incoming.size 92 | assert_equal 16*1024, incoming[0].bytesize 93 | end 94 | else 95 | warn "EM.pause_connection not implemented, skipping tests in #{__FILE__}" 96 | 97 | # Because some rubies will complain if a TestCase class has no tests 98 | def test_em_pause_connection_not_implemented 99 | assert true 100 | end 101 | end 102 | end 103 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | EventMachine is copyrighted free software owned by Francis Cianfrocca 2 | (blackhedd ... gmail.com). The Owner of this software permits you to 3 | redistribute and/or modify the software under either the terms of the GPL 4 | version 2 (see the file GPL), or the conditions below ("Ruby License"): 5 | 6 | 1. You may make and give away verbatim copies of the source form of this 7 | software without restriction, provided that you retain ALL of the 8 | original copyright notices and associated disclaimers. 9 | 10 | 2. You may modify your copy of the software in any way, provided that 11 | you do at least ONE of the following: 12 | 13 | a) place your modifications in the Public Domain or otherwise 14 | make them Freely Available, such as by posting said 15 | modifications to Usenet or an equivalent medium, or by allowing 16 | the author to include your modifications in the software. 17 | 18 | b) use the modified software only within your corporation or 19 | organization. 20 | 21 | c) give non-standard binaries non-standard names, with 22 | instructions on where to get the original software distribution. 23 | 24 | d) make other distribution arrangements with the Owner. 25 | 26 | 3. You may distribute the software in object code or binary form, 27 | provided that you do at least ONE of the following: 28 | 29 | a) distribute the binaries and library files of the software, 30 | together with instructions (in a manual page or equivalent) 31 | on where to get the original distribution. 32 | 33 | b) accompany the distribution with the machine-readable source of 34 | the software. 35 | 36 | c) give non-standard binaries non-standard names, with 37 | instructions on where to get the original software distribution. 38 | 39 | d) make other distribution arrangements with the Owner. 40 | 41 | 4. You may modify and include parts of the software into any other 42 | software (possibly commercial), provided you comply with the terms in 43 | Sections 1, 2, and 3 above. But some files in the distribution 44 | are not written by the Owner, so they may be made available to you 45 | under different terms. 46 | 47 | For the list of those files and their copying conditions, see the 48 | file LEGAL. 49 | 50 | 5. The scripts and library files supplied as input to or produced as 51 | output from the software do not automatically fall under the 52 | copyright of the software, but belong to whoever generated them, 53 | and may be sold commercially, and may be aggregated with this 54 | software. 55 | 56 | 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR 57 | IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 58 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 59 | PURPOSE. 60 | 61 | -------------------------------------------------------------------------------- /examples/guides/getting_started/07_simple_chat_server_step_four.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'rubygems' # or use Bundler.setup 4 | require 'eventmachine' 5 | 6 | class SimpleChatServer < EM::Connection 7 | 8 | @@connected_clients = Array.new 9 | 10 | 11 | attr_reader :username 12 | 13 | 14 | # 15 | # EventMachine handlers 16 | # 17 | 18 | def post_init 19 | @username = nil 20 | 21 | puts "A client has connected..." 22 | ask_username 23 | end 24 | 25 | def unbind 26 | @@connected_clients.delete(self) 27 | puts "[info] #{@username} has left" if entered_username? 28 | end 29 | 30 | def receive_data(data) 31 | if entered_username? 32 | handle_chat_message(data.strip) 33 | else 34 | handle_username(data.strip) 35 | end 36 | end 37 | 38 | 39 | 40 | 41 | # 42 | # Username handling 43 | # 44 | 45 | def entered_username? 46 | !@username.nil? && !@username.empty? 47 | end # entered_username? 48 | 49 | def handle_username(input) 50 | if input.empty? 51 | send_line("Blank usernames are not allowed. Try again.") 52 | ask_username 53 | else 54 | @username = input 55 | @@connected_clients.push(self) 56 | self.other_peers.each { |c| c.send_data("#{@username} has joined the room\n") } 57 | puts "#{@username} has joined" 58 | 59 | self.send_line("[info] Ohai, #{@username}") 60 | end 61 | end # handle_username(input) 62 | 63 | def ask_username 64 | self.send_line("[info] Enter your username:") 65 | end # ask_username 66 | 67 | 68 | 69 | # 70 | # Message handling 71 | # 72 | 73 | def handle_chat_message(msg) 74 | if command?(msg) 75 | self.handle_command(msg) 76 | else 77 | self.announce(msg, "#{@username}:") 78 | end 79 | end 80 | 81 | 82 | # 83 | # Commands handling 84 | # 85 | 86 | def command?(input) 87 | input =~ /exit$/i 88 | end # command?(input) 89 | 90 | def handle_command(cmd) 91 | case cmd 92 | when /exit$/i then self.close_connection 93 | end 94 | end # handle_command(cmd) 95 | 96 | 97 | 98 | # 99 | # Helpers 100 | # 101 | 102 | def announce(msg = nil, prefix = "[chat server]") 103 | @@connected_clients.each { |c| c.send_line("#{prefix} #{msg}") } unless msg.empty? 104 | end # announce(msg) 105 | 106 | def other_peers 107 | @@connected_clients.reject { |c| self == c } 108 | end # other_peers 109 | 110 | def send_line(line) 111 | self.send_data("#{line}\n") 112 | end # send_line(line) 113 | end 114 | 115 | EventMachine.run do 116 | # hit Control + C to stop 117 | Signal.trap("INT") { EventMachine.stop } 118 | Signal.trap("TERM") { EventMachine.stop } 119 | 120 | EventMachine.start_server("0.0.0.0", 10000, SimpleChatServer) 121 | end 122 | -------------------------------------------------------------------------------- /tests/test_next_tick.rb: -------------------------------------------------------------------------------- 1 | require 'em_test_helper' 2 | 3 | class TestNextTick < Test::Unit::TestCase 4 | 5 | def test_tick_arg 6 | pr = proc {EM.stop} 7 | EM.run { 8 | EM.next_tick pr 9 | } 10 | assert true 11 | end 12 | 13 | def test_tick_block 14 | EM.run { 15 | EM.next_tick {EM.stop} 16 | } 17 | assert true 18 | end 19 | 20 | # This illustrates the solution to a long-standing problem. 21 | # It's now possible to correctly nest calls to EM#run. 22 | # See the source code commentary for EM#run for more info. 23 | # 24 | def test_run_run 25 | EM.run { 26 | EM.run { 27 | EM.next_tick {EM.stop} 28 | } 29 | } 30 | end 31 | 32 | def test_pre_run_queue 33 | x = false 34 | EM.next_tick { EM.stop; x = true } 35 | EM.run { EM.add_timer(0.01) { EM.stop } } 36 | assert x 37 | end 38 | 39 | def test_cleanup_after_stop 40 | x = true 41 | EM.run{ 42 | EM.next_tick{ 43 | EM.stop 44 | EM.next_tick{ x=false } 45 | } 46 | } 47 | EM.run{ 48 | EM.next_tick{ EM.stop } 49 | } 50 | assert x 51 | end 52 | 53 | # We now support an additional parameter for EM#run. 54 | # You can pass two procs to EM#run now. The first is executed as the normal 55 | # run block. The second (if given) is scheduled for execution after the 56 | # reactor loop completes. 57 | # The reason for supporting this is subtle. There has always been an expectation 58 | # that EM#run doesn't return until after the reactor loop ends. But now it's 59 | # possible to nest calls to EM#run, which means that a nested call WILL 60 | # RETURN. In order to write code that will run correctly either way, it's 61 | # recommended to put any code which must execute after the reactor completes 62 | # in the second parameter. 63 | # 64 | def test_run_run_2 65 | a = proc {EM.stop} 66 | b = proc {assert true} 67 | EM.run a, b 68 | end 69 | 70 | 71 | # This illustrates that EM#run returns when it's called nested. 72 | # This isn't a feature, rather it's something to be wary of when writing code 73 | # that must run correctly even if EM#run is called while a reactor is already 74 | # running. 75 | def test_run_run_3 76 | a = [] 77 | EM.run { 78 | EM.run proc {EM.stop}, proc {a << 2} 79 | a << 1 80 | } 81 | assert_equal( [1,2], a ) 82 | end 83 | 84 | 85 | def test_schedule_on_reactor_thread 86 | x = false 87 | EM.run do 88 | EM.schedule { x = true } 89 | EM.stop 90 | end 91 | assert x 92 | end 93 | 94 | def test_schedule_from_thread 95 | x = false 96 | EM.run do 97 | Thread.new { EM.schedule { x = true } }.join 98 | assert !x 99 | EM.next_tick { EM.stop } 100 | end 101 | assert x 102 | end 103 | 104 | end 105 | -------------------------------------------------------------------------------- /tests/test_timers.rb: -------------------------------------------------------------------------------- 1 | require 'em_test_helper' 2 | 3 | class TestTimers < Test::Unit::TestCase 4 | 5 | def test_timer_with_block 6 | x = false 7 | EM.run { 8 | EM::Timer.new(0) { 9 | x = true 10 | EM.stop 11 | } 12 | } 13 | assert x 14 | end 15 | 16 | def test_timer_with_proc 17 | x = false 18 | EM.run { 19 | EM::Timer.new(0, proc { 20 | x = true 21 | EM.stop 22 | }) 23 | } 24 | assert x 25 | end 26 | 27 | def test_timer_cancel 28 | assert_nothing_raised do 29 | EM.run { 30 | timer = EM::Timer.new(0.01) { flunk "Timer was not cancelled." } 31 | timer.cancel 32 | 33 | EM.add_timer(0.02) { EM.stop } 34 | } 35 | end 36 | end 37 | 38 | def test_periodic_timer 39 | x = 0 40 | EM.run { 41 | EM::PeriodicTimer.new(0.01) do 42 | x += 1 43 | EM.stop if x == 4 44 | end 45 | } 46 | 47 | assert_equal 4, x 48 | end 49 | 50 | def test_add_periodic_timer 51 | x = 0 52 | EM.run { 53 | t = EM.add_periodic_timer(0.01) do 54 | x += 1 55 | EM.stop if x == 4 56 | end 57 | assert t.respond_to?(:cancel) 58 | } 59 | assert_equal 4, x 60 | end 61 | 62 | def test_periodic_timer_cancel 63 | x = 0 64 | EM.run { 65 | pt = EM::PeriodicTimer.new(0.01) { x += 1 } 66 | pt.cancel 67 | EM::Timer.new(0.02) { EM.stop } 68 | } 69 | assert_equal 0, x 70 | end 71 | 72 | def test_add_periodic_timer_cancel 73 | x = 0 74 | EM.run { 75 | pt = EM.add_periodic_timer(0.01) { x += 1 } 76 | EM.cancel_timer(pt) 77 | EM.add_timer(0.02) { EM.stop } 78 | } 79 | assert_equal 0, x 80 | end 81 | 82 | def test_periodic_timer_self_cancel 83 | x = 0 84 | EM.run { 85 | pt = EM::PeriodicTimer.new(0) { 86 | x += 1 87 | if x == 4 88 | pt.cancel 89 | EM.stop 90 | end 91 | } 92 | } 93 | assert_equal 4, x 94 | end 95 | 96 | 97 | # This test is only applicable to compiled versions of the reactor. 98 | # Pure ruby and java versions have no built-in limit on the number of outstanding timers. 99 | unless [:pure_ruby, :java].include? EM.library_type 100 | def test_timer_change_max_outstanding 101 | defaults = EM.get_max_timers 102 | EM.set_max_timers(100) 103 | 104 | one_hundred_one_timers = lambda do 105 | 101.times { EM.add_timer(0.01) {} } 106 | EM.stop 107 | end 108 | 109 | assert_raises(RuntimeError) do 110 | EM.run( &one_hundred_one_timers ) 111 | end 112 | 113 | EM.set_max_timers( 101 ) 114 | 115 | assert_nothing_raised do 116 | EM.run( &one_hundred_one_timers ) 117 | end 118 | ensure 119 | EM.set_max_timers(defaults) 120 | end 121 | end 122 | 123 | end 124 | -------------------------------------------------------------------------------- /lib/em/messages.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # 3 | # Author:: Francis Cianfrocca (gmail: blackhedd) 4 | # Homepage:: http://rubyeventmachine.com 5 | # Date:: 16 Jul 2006 6 | # 7 | # See EventMachine and EventMachine::Connection for documentation and 8 | # usage examples. 9 | # 10 | #---------------------------------------------------------------------------- 11 | # 12 | # Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved. 13 | # Gmail: blackhedd 14 | # 15 | # This program is free software; you can redistribute it and/or modify 16 | # it under the terms of either: 1) the GNU General Public License 17 | # as published by the Free Software Foundation; either version 2 of the 18 | # License, or (at your option) any later version; or 2) Ruby's License. 19 | # 20 | # See the file COPYING for complete licensing information. 21 | # 22 | #--------------------------------------------------------------------------- 23 | # 24 | # 25 | 26 | =begin 27 | 28 | Message Routing in EventMachine. 29 | 30 | The goal here is to enable "routing points," objects that can send and receive 31 | "messages," which are delimited streams of bytes. The boundaries of a message 32 | are preserved as it passes through the reactor system. 33 | 34 | There will be several module methods defined in EventMachine to create route-point 35 | objects (which will probably have a base class of EventMachine::MessageRouter 36 | until someone suggests a better name). 37 | 38 | As with I/O objects, routing objects will receive events by having the router 39 | core call methods on them. And of course user code can and will define handlers 40 | to deal with events of interest. 41 | 42 | The message router base class only really needs a receive_message method. There will 43 | be an EM module-method to send messages, in addition to the module methods to create 44 | the various kinds of message receivers. 45 | 46 | The simplest kind of message receiver object can receive messages by being named 47 | explicitly in a parameter to EM#send_message. More sophisticated receivers can define 48 | pub-sub selectors and message-queue names. And they can also define channels for 49 | route-points in other processes or even on other machines. 50 | 51 | A message is NOT a marshallable entity. Rather, it's a chunk of flat content more like 52 | an Erlang message. Initially, all content submitted for transmission as a message will 53 | have the to_s method called on it. Eventually, we'll be able to transmit certain structured 54 | data types (XML and YAML documents, Structs within limits) and have them reconstructed 55 | on the other end. 56 | 57 | A fundamental goal of the message-routing capability is to interoperate seamlessly with 58 | external systems, including non-Ruby systems like ActiveMQ. We will define various protocol 59 | handlers for things like Stomp and possibly AMQP, but these will be wrapped up and hidden 60 | from the users of the basic routing capability. 61 | 62 | As with Erlang, a critical goal is for programs that are built to use message-passing to work 63 | WITHOUT CHANGE when the code is re-based on a multi-process system. 64 | 65 | =end 66 | 67 | -------------------------------------------------------------------------------- /rakelib/package.rake: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'rubygems/package_task' 3 | 4 | begin 5 | require 'rake/extensiontask' 6 | require 'rake/javaextensiontask' 7 | rescue LoadError => e 8 | puts <<-MSG 9 | rake-compiler gem seems to be missing. Please install it with 10 | 11 | gem install rake-compiler 12 | 13 | (add sudo if necessary). 14 | MSG 15 | end 16 | 17 | Gem::PackageTask.new(GEMSPEC) do |pkg| 18 | end 19 | 20 | if RUBY_PLATFORM =~ /java/ 21 | Rake::JavaExtensionTask.new("rubyeventmachine", GEMSPEC) do |ext| 22 | ext.ext_dir = 'java/src' 23 | end 24 | else 25 | def setup_cross_compilation(ext) 26 | unless RUBY_PLATFORM =~ /mswin|mingw/ 27 | ext.cross_compile = true 28 | ext.cross_platform = ['x86-mingw32', 'x86-mswin32-60'] 29 | end 30 | end 31 | def hack_cross_compilation(ext) 32 | # inject 1.8/1.9 pure-ruby entry point 33 | # HACK: add these dependencies to the task instead of using cross_compiling 34 | if ext.cross_platform.is_a?(Array) 35 | ext.cross_platform.each do |platform| 36 | task = "native:#{GEMSPEC.name}:#{platform}" 37 | if Rake::Task.task_defined?(task) 38 | Rake::Task[task].prerequisites.unshift "lib/#{ext.name}.rb" 39 | end 40 | end 41 | end 42 | end 43 | 44 | em = Rake::ExtensionTask.new("rubyeventmachine", GEMSPEC) do |ext| 45 | ext.ext_dir = 'ext' 46 | ext.source_pattern = '*.{h,c,cpp}' 47 | setup_cross_compilation(ext) 48 | end 49 | hack_cross_compilation em 50 | 51 | ff = Rake::ExtensionTask.new("fastfilereaderext", GEMSPEC) do |ext| 52 | ext.ext_dir = 'ext/fastfilereader' 53 | ext.source_pattern = '*.{h,c,cpp}' 54 | setup_cross_compilation(ext) 55 | end 56 | hack_cross_compilation ff 57 | end 58 | 59 | # Setup shim files that require 1.8 vs 1.9 extensions in win32 bin gems 60 | %w[ rubyeventmachine fastfilereaderext ].each do |filename| 61 | file("lib/#{filename}.rb") do |t| 62 | File.open(t.name, 'wb') do |f| 63 | f.write <<-eoruby 64 | RUBY_VERSION =~ /(\\d+.\\d+)/ 65 | require "\#{$1}/#{File.basename(t.name, '.rb')}" 66 | eoruby 67 | end 68 | at_exit{ FileUtils.rm t.name if File.exist?(t.name) } 69 | end 70 | end 71 | 72 | task :cross_cxx do 73 | ENV['CROSS_COMPILING'] = 'yes' 74 | require 'rake/extensioncompiler' 75 | ENV['CXX'] = "#{Rake::ExtensionCompiler.mingw_host}-g++" 76 | end 77 | 78 | if Rake::Task.task_defined?(:cross) 79 | task :cross => 'lib/rubyeventmachine.rb' 80 | task :cross => 'lib/fastfilereaderext.rb' 81 | task :cross => :cross_cxx 82 | end 83 | 84 | def windows?; RUBY_PLATFORM =~ /mswin|mingw/; end 85 | def sudo(cmd) 86 | if windows? || (require 'etc'; Etc.getpwuid.uid == 0) 87 | sh cmd 88 | else 89 | sh "sudo #{cmd}" 90 | end 91 | end 92 | def gem_cmd(action, name, *args) 93 | rb = Gem.ruby rescue nil 94 | rb ||= (require 'rbconfig'; File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name'])) 95 | sudo "#{rb} -r rubygems -e 'require %{rubygems/gem_runner}; Gem::GemRunner.new.run(%w{#{action} #{name} #{args.join(' ')}})'" 96 | end 97 | 98 | Rake::Task[:clean].enhance [:clobber_package] 99 | -------------------------------------------------------------------------------- /ext/binder.cpp: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | 3 | $Id$ 4 | 5 | File: binder.cpp 6 | Date: 07Apr06 7 | 8 | Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved. 9 | Gmail: blackhedd 10 | 11 | This program is free software; you can redistribute it and/or modify 12 | it under the terms of either: 1) the GNU General Public License 13 | as published by the Free Software Foundation; either version 2 of the 14 | License, or (at your option) any later version; or 2) Ruby's License. 15 | 16 | See the file COPYING for complete licensing information. 17 | 18 | *****************************************************************************/ 19 | 20 | #include "project.h" 21 | 22 | #define DEV_URANDOM "/dev/urandom" 23 | 24 | 25 | map Bindable_t::BindingBag; 26 | 27 | 28 | /******************************** 29 | STATIC Bindable_t::CreateBinding 30 | ********************************/ 31 | 32 | unsigned long Bindable_t::CreateBinding() 33 | { 34 | static unsigned long num = 0; 35 | while(BindingBag[++num]) {} 36 | return num; 37 | } 38 | 39 | #if 0 40 | string Bindable_t::CreateBinding() 41 | { 42 | static int index = 0; 43 | static string seed; 44 | 45 | if ((index >= 1000000) || (seed.length() == 0)) { 46 | #ifdef OS_UNIX 47 | int fd = open (DEV_URANDOM, O_RDONLY); 48 | if (fd < 0) 49 | throw std::runtime_error ("No entropy device"); 50 | 51 | unsigned char u[16]; 52 | size_t r = read (fd, u, sizeof(u)); 53 | if (r < sizeof(u)) 54 | throw std::runtime_error ("Unable to read entropy device"); 55 | 56 | unsigned char *u1 = (unsigned char*)u; 57 | char u2 [sizeof(u) * 2 + 1]; 58 | 59 | for (size_t i=0; i < sizeof(u); i++) 60 | sprintf (u2 + (i * 2), "%02x", u1[i]); 61 | 62 | seed = string (u2); 63 | #endif 64 | 65 | 66 | #ifdef OS_WIN32 67 | UUID uuid; 68 | UuidCreate (&uuid); 69 | unsigned char *uuidstring = NULL; 70 | UuidToString (&uuid, &uuidstring); 71 | if (!uuidstring) 72 | throw std::runtime_error ("Unable to read uuid"); 73 | seed = string ((const char*)uuidstring); 74 | 75 | RpcStringFree (&uuidstring); 76 | #endif 77 | 78 | index = 0; 79 | 80 | 81 | } 82 | 83 | stringstream ss; 84 | ss << seed << (++index); 85 | return ss.str(); 86 | } 87 | #endif 88 | 89 | /***************************** 90 | STATIC: Bindable_t::GetObject 91 | *****************************/ 92 | 93 | Bindable_t *Bindable_t::GetObject (const unsigned long binding) 94 | { 95 | map::const_iterator i = BindingBag.find (binding); 96 | if (i != BindingBag.end()) 97 | return i->second; 98 | else 99 | return NULL; 100 | } 101 | 102 | 103 | /********************** 104 | Bindable_t::Bindable_t 105 | **********************/ 106 | 107 | Bindable_t::Bindable_t() 108 | { 109 | Binding = Bindable_t::CreateBinding(); 110 | BindingBag [Binding] = this; 111 | } 112 | 113 | 114 | 115 | /*********************** 116 | Bindable_t::~Bindable_t 117 | ***********************/ 118 | 119 | Bindable_t::~Bindable_t() 120 | { 121 | BindingBag.erase (Binding); 122 | } 123 | 124 | 125 | -------------------------------------------------------------------------------- /lib/em/threaded_resource.rb: -------------------------------------------------------------------------------- 1 | module EventMachine 2 | # = EventMachine::ThreadedResource 3 | # 4 | # A threaded resource is a "quick and dirty" wrapper around the concept of 5 | # wiring up synchronous code into a standard EM::Pool. This is useful to keep 6 | # interfaces coherent and provide a simple approach at "making an interface 7 | # async-ish". 8 | # 9 | # General usage is to wrap libraries that do not support EventMachine, or to 10 | # have a specific number of dedicated high-cpu worker resources. 11 | # 12 | # == Basic Usage example 13 | # 14 | # This example requires the cassandra gem. The cassandra gem contains an 15 | # EventMachine interface, but it's sadly Fiber based and thus only works on 16 | # 1.9. It also requires (potentially) complex stack switching logic to reach 17 | # completion of nested operations. By contrast this approach provides a block 18 | # in which normal synchronous code can occur, but makes no attempt to wire the 19 | # IO into EventMachines C++ IO implementations, instead relying on the reactor 20 | # pattern in rb_thread_select. 21 | # 22 | # cassandra_dispatcher = ThreadedResource.new do 23 | # Cassandra.new('allthethings', '127.0.0.1:9160') 24 | # end 25 | # 26 | # pool = EM::Pool.new 27 | # 28 | # pool.add cassandra_dispatcher 29 | # 30 | # # If we don't care about the result: 31 | # pool.perform do |dispatcher| 32 | # # The following block executes inside a dedicated thread, and should not 33 | # # access EventMachine things: 34 | # dispatcher.dispatch do |cassandra| 35 | # cassandra.insert(:Things, '10', 'stuff' => 'things') 36 | # end 37 | # end 38 | # 39 | # # Example where we care about the result: 40 | # pool.perform do |dispatcher| 41 | # # The dispatch block is executed in the resources thread. 42 | # completion = dispatcher.dispatch do |cassandra| 43 | # cassandra.get(:Things, '10', 'stuff') 44 | # end 45 | # 46 | # # This block will be yielded on the EM thread: 47 | # completion.callback do |result| 48 | # EM.do_something_with(result) 49 | # end 50 | # 51 | # completion 52 | # end 53 | class ThreadedResource 54 | 55 | # The block should return the resource that will be yielded in a dispatch. 56 | def initialize 57 | @resource = yield 58 | 59 | @running = true 60 | @queue = ::Queue.new 61 | @thread = Thread.new do 62 | @queue.pop.call while @running 63 | end 64 | end 65 | 66 | # Called on the EM thread, generally in a perform block to return a 67 | # completion for the work. 68 | def dispatch 69 | completion = EM::Completion.new 70 | @queue << lambda do 71 | begin 72 | result = yield @resource 73 | completion.succeed result 74 | rescue Exception => e 75 | completion.fail e 76 | end 77 | end 78 | completion 79 | end 80 | 81 | # Kill the internal thread. should only be used to cleanup - generally 82 | # only required for tests. 83 | def shutdown 84 | @running = false 85 | @queue << lambda {} 86 | @thread.join 87 | end 88 | 89 | end 90 | end -------------------------------------------------------------------------------- /ext/fastfilereader/extconf.rb: -------------------------------------------------------------------------------- 1 | require 'mkmf' 2 | 3 | def check_libs libs = [], fatal = false 4 | libs.all? { |lib| have_library(lib) || (abort("could not find library: #{lib}") if fatal) } 5 | end 6 | 7 | def check_heads heads = [], fatal = false 8 | heads.all? { |head| have_header(head) || (abort("could not find header: #{head}") if fatal)} 9 | end 10 | 11 | def add_define(name) 12 | $defs.push("-D#{name}") 13 | end 14 | 15 | add_define 'BUILD_FOR_RUBY' 16 | 17 | # Minor platform details between *nix and Windows: 18 | 19 | if RUBY_PLATFORM =~ /(mswin|mingw|bccwin)/ 20 | GNU_CHAIN = ENV['CROSS_COMPILING'] || $1 == 'mingw' 21 | OS_WIN32 = true 22 | add_define "OS_WIN32" 23 | else 24 | GNU_CHAIN = true 25 | OS_UNIX = true 26 | add_define 'OS_UNIX' 27 | end 28 | 29 | # Adjust number of file descriptors (FD) on Windows 30 | 31 | if RbConfig::CONFIG["host_os"] =~ /mingw/ 32 | found = RbConfig::CONFIG.values_at("CFLAGS", "CPPFLAGS"). 33 | any? { |v| v.include?("FD_SETSIZE") } 34 | 35 | add_define "FD_SETSIZE=32767" unless found 36 | end 37 | 38 | # Main platform invariances: 39 | 40 | case RUBY_PLATFORM 41 | when /mswin32/, /mingw32/, /bccwin32/ 42 | check_heads(%w[windows.h winsock.h], true) 43 | check_libs(%w[kernel32 rpcrt4 gdi32], true) 44 | 45 | if GNU_CHAIN 46 | CONFIG['LDSHAREDXX'] = "$(CXX) -shared -static-libgcc -static-libstdc++" 47 | else 48 | $defs.push "-EHs" 49 | $defs.push "-GR" 50 | end 51 | 52 | when /solaris/ 53 | add_define 'OS_SOLARIS8' 54 | check_libs(%w[nsl socket], true) 55 | 56 | if CONFIG['CC'] == 'cc' and `cc -flags 2>&1` =~ /Sun/ # detect SUNWspro compiler 57 | # SUN CHAIN 58 | add_define 'CC_SUNWspro' 59 | $preload = ["\nCXX = CC"] # hack a CXX= line into the makefile 60 | $CFLAGS = CONFIG['CFLAGS'] = "-KPIC" 61 | CONFIG['CCDLFLAGS'] = "-KPIC" 62 | CONFIG['LDSHARED'] = "$(CXX) -G -KPIC -lCstd" 63 | else 64 | # GNU CHAIN 65 | # on Unix we need a g++ link, not gcc. 66 | CONFIG['LDSHARED'] = "$(CXX) -shared" 67 | end 68 | 69 | when /openbsd/ 70 | # OpenBSD branch contributed by Guillaume Sellier. 71 | 72 | # on Unix we need a g++ link, not gcc. On OpenBSD, linking against libstdc++ have to be explicitly done for shared libs 73 | CONFIG['LDSHARED'] = "$(CXX) -shared -lstdc++ -fPIC" 74 | CONFIG['LDSHAREDXX'] = "$(CXX) -shared -lstdc++ -fPIC" 75 | 76 | when /darwin/ 77 | # on Unix we need a g++ link, not gcc. 78 | # Ff line contributed by Daniel Harple. 79 | CONFIG['LDSHARED'] = "$(CXX) " + CONFIG['LDSHARED'].split[1..-1].join(' ') 80 | 81 | when /linux/ 82 | # on Unix we need a g++ link, not gcc. 83 | CONFIG['LDSHARED'] = "$(CXX) -shared" 84 | 85 | when /aix/ 86 | # on Unix we need a g++ link, not gcc. 87 | CONFIG['LDSHARED'] = "$(CXX) -shared -Wl,-G" 88 | 89 | when /cygwin/ 90 | # For rubies built with Cygwin, CXX may be set to CC, which is just 91 | # a wrapper for gcc. 92 | # This will compile, but it will not link to the C++ std library. 93 | # Explicitly set CXX to use g++. 94 | CONFIG['CXX'] = "g++" 95 | # on Unix we need a g++ link, not gcc. 96 | CONFIG['LDSHARED'] = "$(CXX) -shared" 97 | 98 | else 99 | # on Unix we need a g++ link, not gcc. 100 | CONFIG['LDSHARED'] = "$(CXX) -shared" 101 | end 102 | 103 | create_makefile "fastfilereaderext" 104 | -------------------------------------------------------------------------------- /tests/client.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIJKAIBAAKCAgEAv1FSOIX1z7CQtVBFlrB0A3/V29T+22STKKmiRWYkKL5b+hkr 3 | p9IZ5J4phZHgUVM2VDPOO2Oc2PU6dlGGZISg+UPERunTogxQKezCV0vcE9cKOwzx 4 | CFDRvv5rK8aKMscfBLbNKocAXywuRRQmdxPiVRzbyPrl+qCr/EDLXAX3D77lS8n2 5 | AwDg19VyI+IgFUE+Dy5e1eLoY6nV+Mq+vNXdn3ttF3t+ngac5pj5Q9h+pD5p67ba 6 | DHSnf/7cy2fa/LKrLolVHQR9G2K6cEfeM99NtcsMbkoPs4iI3FA05OVTQHXgC8C8 7 | cRxrb9APl95I/ep65OIaCJgcdYxJ3QD3qOtQo6/NQsGnjbyiUxaEpjfqyT1NuzWD 8 | 81Q8uXGNS8yD6dDynt/lseBjyp2nfC3uQ5fY18VdIcu0MJ9pezBUKrNuhlsyXXEZ 9 | 2DXj4sY8QOvIcBqSB/zmS1nGEK55xrtkaiaNrY8fe8wRVpcPLxy+P225NFw+B69F 10 | JRA0Lj6Jt9BM4hV/3MSIEWwTVhuw4E02ywDYTzz1wq3ITf0tsbIPn0hXQMxDohhA 11 | oKioM6u+yHtqsxD0eYaAWmHTVn5oDvOSGpvCpBfWHyA7FP5UQak0fKABEAgKiQYE 12 | nb294AXwXymJttfGTIV/Ne4tLN5dIpNma8UO8rlThlcr6xnTQDbR3gkTDRsCAwEA 13 | AQKCAgB495RDRQB9x6hX3F+DviI8rDGug+h5FAiwJ0IBG2o1kNdbNVsTC5dvpEmg 14 | uPHaugCaEP+PMZbU34mNklKlb+7QbPbH18UGqz5so9TlmYOXz9oaKD6nAWL9nqRo 15 | 02pCXQDR3DuxbhbgFnFTIECJ/jqXkl2toGaVp83W+6kZkHP8srkMyLASihWgosc+ 16 | xRWAGvaAZtNz7br+eT5fxuH/SEKPOl1qAZ23kXrXm1XQfizk8MnMTptkUMYv+hfl 17 | TM98BASUsiTs6g+opy43HFn09naOQcqkWZO/8s6Gbvhi2lVfZqi5Ba6g3lVYJ3gU 18 | kGoako4N9qB7WqJz+LYjVR9C4TbkkJ9OD6ArwGAx5IIzC3XKSxCyY/pUn4YumPhY 19 | fjvY/km54TBtx/isS1TAgjSgDUxbzrfbkh7afOXSOniy9bWJMgNqHF61dqxWxmUg 20 | F5Tch9zH3qFFVkXpYzDU/R8ZV+CRouCvhn0eZYDh8IqIAwjH0VjkxjPyQtrdrMd3 21 | gDKMVKoY31EOMLZzv8a0prjpr15A+uw30tT336qb3fofks4pZKUJw8ru9jJVir2p 22 | +RML6iUHCmIeceF7/N1meooSMLPJe0xgKeMb9M4Wtd/et2UNVtP8nCDG622rf2a0 23 | F/EudXuFgc3FB8nXRw9TCkw9xKQff38edG5xPFUEgqObbVl5YQKCAQEA5DDKGOmp 24 | EO5Zuf/kZfG6/AMMYwAuv1HrYTV2w/HnI3tyQ34Xkeqo+I/OqmRk68Ztxw4Kx1So 25 | SRavkotrlWhhDpl2+Yn1BjkHktSoOdf9gJ9z9llkLmbOkBjmupig1NUB7fq/4y2k 26 | MdqJXDy3uVKHJ97gxdIheMTyHiKuMJPnuT5lZtlT210Ig82P7sLQb/sgCfKVFTr0 27 | Z3haQ5/tBNKjq+igT4nMBWupOTD1q2GeZLIZACnmnUIhvu+3/bm0l+wiCB0DqF0T 28 | Wy9tlL3fqQSCqzevL7/k5Lg6tJTaP/XYePB73TsOtAXgIaoltXgRBsBUeE1eaODx 29 | kMT6E1PPtn7EqQKCAQEA1qImmTWGqhKICrwje40awPufFtZ/qXKVCN/V+zYsrJV1 30 | EnZpUDM+zfitlQCugnrQVHSpgfekI6mmVkmogO3fkNjUFTq+neg7IHOUHnqotx+3 31 | NMqIsyFInGstu9mfPd26fzZjUtx5wKF38LDTIJJAEJ83U3UpPBfpwKmiOGDXOa54 32 | 2i4em/bb/hrQR6JySruZYLi0fXnGI5ZOfpkHgC/KOFkKNKAg2oh4B9qo7ACyiSNk 33 | yojb2mmn6g1OLPxi7wGUSrkS1HQq4an6RZ+eUO0HXVWag0QStdQ91M9IrIHgSBBG 34 | 0e86Ar6jtD579gqsbz4ySpI/FqEI9obTC+E1/b0aIwKCAQAGz334qGCnZLXA22ZR 35 | tJlEFEM2YTcD9snzqMjWqE2hvXl3kjfZ3wsUABbG9yAb+VwlaMHhmSE8rTSoRwj6 36 | +JaM/P+UCw4JFYKoWzh6IXwrbpbjb1+SEvdvTY71WsDSGVlpZOZ9PUt9QWyAGD/T 37 | hCcMhZZn0RG2rQoc5CQWxxNPcBFOtIXQMkKizGvTUHUwImqeYWMZsxzASdNH2WoV 38 | jsPbyaGfPhmcv83ZKyDp8IvtrXMZkiaT4vlm3Xi8VeKR9jY9z7/gMob1XcEDg3c9 39 | cCkGOy87WZrXSLhX02mAJzJCycqom66gqNw7pPxjIiY/8VWUEZsTvkL3cymTkhjM 40 | 9ZOhAoIBAGUaNqJe01NTrV+ZJgGyAxM6s8LXQYV5IvjuL2bJKxwUvvP2cT9FFGWD 41 | qYiRrKJr5ayS07IUC+58oIzu33/0DSa27JgfduD9HrT3nKMK1mSEfRFSAjiXChQc 42 | bIubRGapBoub/AdxMazqoovvT1R9b84kobQfcVAMV6DYh0CVZWyXYfgsV2DSVOiK 43 | iufjfoDzg5lLCEI+1XW3/LunrB/W4yPN1X/amf8234ublYyt+2ucD4NUGnP05xLa 44 | N6P7M0MwdEEKkvMe0YBBSFH5kWK/dIOjqkgBDes20fVnuuz/tL1dZW7IiIP4dzaV 45 | ZGEOwBEatCfqYetv6b/u3IUxDfS7Wg8CggEBALoOwkn5LGdQg+bpdZAKJspGnJWL 46 | Kyr9Al2tvgc69rxfpZqS5eDLkYYCzWPpspSt0Axm1O7xOUDQDt42luaLNGJzHZ2Q 47 | Hn0ZNMhyHpe8d8mIQngRjD+nuLI/uFUglPzabDOCOln2aycjg1mA6ecXP1XMEVbu 48 | 0RB/0IE36XTMfZ+u9+TRjkBLpmUaX1FdIQQWfwUou/LfaXotoQlhSGAcprLrncuJ 49 | T44UATYEgO/q9pMM33bdE3eBYZHoT9mSvqoLCN4s0LuwOYItIxLKUj0GulL0VQOI 50 | SZi+0A1c8cVDXgApkBrWPDQIR9JS4de0gW4hnDoUvHtUc2TYPRnz6N9MtFY= 51 | -----END RSA PRIVATE KEY----- 52 | -------------------------------------------------------------------------------- /ext/fastfilereader/rubymain.cpp: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | 3 | $Id: rubymain.cpp 4529 2007-07-04 11:32:22Z francis $ 4 | 5 | File: rubymain.cpp 6 | Date: 02Jul07 7 | 8 | Copyright (C) 2007 by Francis Cianfrocca. All Rights Reserved. 9 | Gmail: garbagecat10 10 | 11 | This program is free software; you can redistribute it and/or modify 12 | it under the terms of either: 1) the GNU General Public License 13 | as published by the Free Software Foundation; either version 2 of the 14 | License, or (at your option) any later version; or 2) Ruby's License. 15 | 16 | See the file COPYING for complete licensing information. 17 | 18 | *****************************************************************************/ 19 | 20 | 21 | 22 | #include 23 | #include 24 | using namespace std; 25 | 26 | #include 27 | #include "mapper.h" 28 | 29 | static VALUE EmModule; 30 | static VALUE FastFileReader; 31 | static VALUE Mapper; 32 | 33 | 34 | 35 | /********* 36 | mapper_dt 37 | *********/ 38 | 39 | static void mapper_dt (void *ptr) 40 | { 41 | if (ptr) 42 | delete (Mapper_t*) ptr; 43 | } 44 | 45 | /********** 46 | mapper_new 47 | **********/ 48 | 49 | static VALUE mapper_new (VALUE self, VALUE filename) 50 | { 51 | Mapper_t *m = new Mapper_t (StringValuePtr (filename)); 52 | if (!m) 53 | rb_raise (rb_eException, "No Mapper Object"); 54 | VALUE v = Data_Wrap_Struct (Mapper, 0, mapper_dt, (void*)m); 55 | return v; 56 | } 57 | 58 | 59 | /**************** 60 | mapper_get_chunk 61 | ****************/ 62 | 63 | static VALUE mapper_get_chunk (VALUE self, VALUE start, VALUE length) 64 | { 65 | Mapper_t *m = NULL; 66 | Data_Get_Struct (self, Mapper_t, m); 67 | if (!m) 68 | rb_raise (rb_eException, "No Mapper Object"); 69 | 70 | // TODO, what if some moron sends us a negative start value? 71 | unsigned _start = NUM2INT (start); 72 | unsigned _length = NUM2INT (length); 73 | if ((_start + _length) > m->GetFileSize()) 74 | rb_raise (rb_eException, "Mapper Range Error"); 75 | 76 | const char *chunk = m->GetChunk (_start); 77 | if (!chunk) 78 | rb_raise (rb_eException, "No Mapper Chunk"); 79 | return rb_str_new (chunk, _length); 80 | } 81 | 82 | /************ 83 | mapper_close 84 | ************/ 85 | 86 | static VALUE mapper_close (VALUE self) 87 | { 88 | Mapper_t *m = NULL; 89 | Data_Get_Struct (self, Mapper_t, m); 90 | if (!m) 91 | rb_raise (rb_eException, "No Mapper Object"); 92 | m->Close(); 93 | return Qnil; 94 | } 95 | 96 | /*********** 97 | mapper_size 98 | ***********/ 99 | 100 | static VALUE mapper_size (VALUE self) 101 | { 102 | Mapper_t *m = NULL; 103 | Data_Get_Struct (self, Mapper_t, m); 104 | if (!m) 105 | rb_raise (rb_eException, "No Mapper Object"); 106 | return INT2NUM (m->GetFileSize()); 107 | } 108 | 109 | 110 | /********************** 111 | Init_fastfilereaderext 112 | **********************/ 113 | 114 | extern "C" void Init_fastfilereaderext() 115 | { 116 | EmModule = rb_define_module ("EventMachine"); 117 | FastFileReader = rb_define_class_under (EmModule, "FastFileReader", rb_cObject); 118 | Mapper = rb_define_class_under (FastFileReader, "Mapper", rb_cObject); 119 | 120 | rb_define_module_function (Mapper, "new", (VALUE(*)(...))mapper_new, 1); 121 | rb_define_method (Mapper, "size", (VALUE(*)(...))mapper_size, 0); 122 | rb_define_method (Mapper, "close", (VALUE(*)(...))mapper_close, 0); 123 | rb_define_method (Mapper, "get_chunk", (VALUE(*)(...))mapper_get_chunk, 2); 124 | } 125 | 126 | 127 | 128 | -------------------------------------------------------------------------------- /tests/test_httpclient2.rb: -------------------------------------------------------------------------------- 1 | require 'em_test_helper' 2 | 3 | class TestHttpClient2 < Test::Unit::TestCase 4 | Localhost = "127.0.0.1" 5 | Localport = 9801 6 | 7 | def setup 8 | end 9 | 10 | def teardown 11 | end 12 | 13 | 14 | class TestServer < EM::Connection 15 | end 16 | 17 | # #connect returns an object which has made a connection to an HTTP server 18 | # and exposes methods for making HTTP requests on that connection. 19 | # #connect can take either a pair of parameters (a host and a port), 20 | # or a single parameter which is a Hash. 21 | # 22 | def test_connect 23 | EM.run { 24 | EM.start_server Localhost, Localport, TestServer 25 | silent do 26 | EM::P::HttpClient2.connect Localhost, Localport 27 | EM::P::HttpClient2.connect( :host=>Localhost, :port=>Localport ) 28 | end 29 | EM.stop 30 | } 31 | end 32 | 33 | 34 | def test_bad_port 35 | EM.run { 36 | EM.start_server Localhost, Localport, TestServer 37 | assert_raises( ArgumentError ) { 38 | silent { EM::P::HttpClient2.connect Localhost, "xxx" } 39 | } 40 | EM.stop 41 | } 42 | end 43 | 44 | def test_bad_server 45 | err = nil 46 | EM.run { 47 | http = silent { EM::P::HttpClient2.connect Localhost, 9999 } 48 | d = http.get "/" 49 | d.errback { err = true; d.internal_error; EM.stop } 50 | } 51 | assert(err) 52 | end 53 | 54 | def test_get 55 | content = nil 56 | EM.run { 57 | http = silent { EM::P::HttpClient2.connect "google.com", 80 } 58 | d = http.get "/" 59 | d.callback { 60 | content = d.content 61 | EM.stop 62 | } 63 | } 64 | assert(content) 65 | end 66 | 67 | # Not a pipelined request because we wait for one response before we request the next. 68 | # XXX this test is broken because it sends the second request to the first connection 69 | # XXX right before the connection closes 70 | def _test_get_multiple 71 | content = nil 72 | EM.run { 73 | http = silent { EM::P::HttpClient2.connect "google.com", 80 } 74 | d = http.get "/" 75 | d.callback { 76 | e = http.get "/" 77 | e.callback { 78 | content = e.content 79 | EM.stop 80 | } 81 | } 82 | } 83 | assert(content) 84 | end 85 | 86 | def test_get_pipeline 87 | headers, headers2 = nil, nil 88 | EM.run { 89 | http = silent { EM::P::HttpClient2.connect "google.com", 80 } 90 | d = http.get("/") 91 | d.callback { 92 | headers = d.headers 93 | } 94 | e = http.get("/") 95 | e.callback { 96 | headers2 = e.headers 97 | } 98 | EM.tick_loop { EM.stop if headers && headers2 } 99 | EM.add_timer(1) { EM.stop } 100 | } 101 | assert(headers) 102 | assert(headers2) 103 | end 104 | 105 | 106 | def test_authheader 107 | EM.run { 108 | EM.start_server Localhost, Localport, TestServer 109 | http = silent { EM::P::HttpClient2.connect Localhost, 18842 } 110 | d = http.get :url=>"/", :authorization=>"Basic xxx" 111 | d.callback {EM.stop} 112 | d.errback {EM.stop} 113 | } 114 | end 115 | 116 | def test_https_get 117 | d = nil 118 | EM.run { 119 | http = silent { EM::P::HttpClient2.connect :host => 'www.apple.com', :port => 443, :ssl => true } 120 | d = http.get "/" 121 | d.callback { 122 | EM.stop 123 | } 124 | } 125 | assert_equal(200, d.status) 126 | end if EM.ssl? 127 | 128 | end 129 | -------------------------------------------------------------------------------- /tests/test_processes.rb: -------------------------------------------------------------------------------- 1 | require 'em_test_helper' 2 | 3 | class TestProcesses < Test::Unit::TestCase 4 | 5 | if !windows? && !jruby? 6 | 7 | # EM::DeferrableChildProcess is a sugaring of a common use-case 8 | # involving EM::popen. 9 | # Call the #open method on EM::DeferrableChildProcess, passing 10 | # a command-string. #open immediately returns an EM::Deferrable 11 | # object. It also schedules the forking of a child process, which 12 | # will execute the command passed to #open. 13 | # When the forked child terminates, the Deferrable will be signalled 14 | # and execute its callbacks, passing the data that the child process 15 | # wrote to stdout. 16 | # 17 | def test_deferrable_child_process 18 | ls = "" 19 | EM.run { 20 | d = EM::DeferrableChildProcess.open( "ls -ltr" ) 21 | d.callback {|data_from_child| 22 | ls = data_from_child 23 | EM.stop 24 | } 25 | } 26 | assert( ls.length > 0) 27 | end 28 | 29 | def setup 30 | $out = nil 31 | $status = nil 32 | end 33 | 34 | def test_em_system 35 | EM.run{ 36 | EM.system('ls'){ |out,status| $out, $status = out, status; EM.stop } 37 | } 38 | 39 | assert( $out.length > 0 ) 40 | assert_equal(0, $status.exitstatus) 41 | assert_kind_of(Process::Status, $status) 42 | end 43 | 44 | def test_em_system_pid 45 | $pids = [] 46 | 47 | EM.run{ 48 | $pids << EM.system('echo hi', proc{ |out,status|$pids << status.pid; EM.stop }) 49 | } 50 | 51 | assert_equal $pids[0], $pids[1] 52 | end 53 | 54 | def test_em_system_with_proc 55 | EM.run{ 56 | EM.system('ls', proc{ |out,status| $out, $status = out, status; EM.stop }) 57 | } 58 | 59 | assert( $out.length > 0 ) 60 | assert_equal(0, $status.exitstatus) 61 | assert_kind_of(Process::Status, $status) 62 | end 63 | 64 | def test_em_system_with_two_procs 65 | EM.run{ 66 | EM.system('sh', proc{ |process| 67 | process.send_data("echo hello\n") 68 | process.send_data("exit\n") 69 | }, proc{ |out,status| 70 | $out = out 71 | $status = status 72 | EM.stop 73 | }) 74 | } 75 | 76 | assert_equal("hello\n", $out) 77 | end 78 | 79 | def test_em_system_cmd_arguments 80 | EM.run{ 81 | EM.system('echo', '1', '2', 'version', proc{ |process| 82 | }, proc{ |out,status| 83 | $out = out 84 | $status = status 85 | EM.stop 86 | }) 87 | } 88 | 89 | assert_match(/1 2 version/i, $out) 90 | end 91 | 92 | def test_em_system_spaced_arguments 93 | EM.run{ 94 | EM.system('ruby', '-e', 'puts "hello"', proc{ |out,status| 95 | $out = out 96 | EM.stop 97 | }) 98 | } 99 | 100 | assert_equal("hello\n", $out) 101 | end 102 | 103 | def test_em_popen_pause_resume 104 | c_rx = 0 105 | 106 | test_client = Module.new do 107 | define_method :receive_data do |data| 108 | c_rx += 1 109 | pause 110 | EM.add_timer(0.5) { EM.stop } 111 | end 112 | end 113 | 114 | EM.run do 115 | EM.popen('echo 1', test_client) 116 | end 117 | 118 | assert_equal 1, c_rx 119 | end 120 | else 121 | warn "EM.popen not implemented, skipping tests in #{__FILE__}" 122 | 123 | # Because some rubies will complain if a TestCase class has no tests 124 | def test_em_popen_unsupported 125 | assert true 126 | end 127 | end 128 | end 129 | -------------------------------------------------------------------------------- /tests/test_attach.rb: -------------------------------------------------------------------------------- 1 | require 'em_test_helper' 2 | require 'socket' 3 | 4 | class TestAttach < Test::Unit::TestCase 5 | class EchoServer < EM::Connection 6 | def receive_data data 7 | $received_data << data 8 | send_data data 9 | end 10 | end 11 | 12 | class EchoClient < EM::Connection 13 | def initialize socket 14 | self.notify_readable = true 15 | @socket = socket 16 | @socket.write("abc\n") 17 | end 18 | 19 | def notify_readable 20 | $read = @socket.readline 21 | $fd = detach 22 | end 23 | 24 | def unbind 25 | EM.next_tick do 26 | @socket.write("def\n") 27 | EM.add_timer(0.1) { EM.stop } 28 | end 29 | end 30 | end 31 | 32 | def setup 33 | @port = next_port 34 | $read, $r, $w, $fd = nil 35 | $received_data = "" 36 | end 37 | 38 | def teardown 39 | [$r, $w].each do |io| 40 | io.close rescue nil 41 | end 42 | $received_data = nil 43 | end 44 | 45 | def test_attach 46 | socket = nil 47 | 48 | EM.run { 49 | EM.start_server "127.0.0.1", @port, EchoServer 50 | socket = TCPSocket.new "127.0.0.1", @port 51 | EM.watch socket, EchoClient, socket 52 | } 53 | 54 | assert_equal $read, "abc\n" 55 | unless jruby? # jruby filenos are not real 56 | assert_equal $fd, socket.fileno 57 | end 58 | assert_equal false, socket.closed? 59 | assert_equal socket.readline, "def\n" 60 | end 61 | 62 | module PipeWatch 63 | def notify_readable 64 | $read = $r.readline 65 | EM.stop 66 | end 67 | end 68 | 69 | def test_attach_server 70 | omit_if(jruby?) 71 | $before = TCPServer.new("127.0.0.1", @port) 72 | sig = nil 73 | EM.run { 74 | sig = EM.attach_server $before, EchoServer 75 | 76 | handler = Class.new(EM::Connection) do 77 | def initialize 78 | send_data "hello world" 79 | close_connection_after_writing 80 | EM.add_timer(0.1) { EM.stop } 81 | end 82 | end 83 | EM.connect("127.0.0.1", @port, handler) 84 | } 85 | 86 | assert_equal false, $before.closed? 87 | assert_equal "hello world", $received_data 88 | assert sig.is_a?(Integer) 89 | end 90 | 91 | def test_attach_pipe 92 | EM.run{ 93 | $r, $w = IO.pipe 94 | EM.watch $r, PipeWatch do |c| 95 | c.notify_readable = true 96 | end 97 | $w.write("ghi\n") 98 | } 99 | 100 | assert_equal $read, "ghi\n" 101 | end 102 | 103 | def test_set_readable 104 | before, after = nil 105 | 106 | EM.run{ 107 | $r, $w = IO.pipe 108 | c = EM.watch $r, PipeWatch do |con| 109 | con.notify_readable = false 110 | end 111 | 112 | EM.next_tick{ 113 | before = c.notify_readable? 114 | c.notify_readable = true 115 | after = c.notify_readable? 116 | } 117 | 118 | $w.write("jkl\n") 119 | } 120 | 121 | assert !before 122 | assert after 123 | assert_equal $read, "jkl\n" 124 | end 125 | 126 | def test_read_write_pipe 127 | result = nil 128 | 129 | pipe_reader = Module.new do 130 | define_method :receive_data do |data| 131 | result = data 132 | EM.stop 133 | end 134 | end 135 | 136 | r,w = IO.pipe 137 | 138 | EM.run { 139 | EM.attach r, pipe_reader 140 | writer = EM.attach(w) 141 | writer.send_data 'ghi' 142 | 143 | # XXX: Process will hang in Windows without this line 144 | writer.close_connection_after_writing 145 | } 146 | 147 | assert_equal "ghi", result 148 | ensure 149 | [r,w].each {|io| io.close rescue nil } 150 | end 151 | end 152 | -------------------------------------------------------------------------------- /docs/old/RELEASE_NOTES: -------------------------------------------------------------------------------- 1 | RUBY/EventMachine RELEASE NOTES 2 | 3 | -------------------------------------------------- 4 | Version: 0.9.0, released xxXXX07 5 | Added Erlang-like distributed-computing features 6 | 7 | -------------------------------------------------- 8 | Version: 0.8.0, released 23Jun07 9 | Added an epoll implementation for Linux 2.6 kernels. 10 | Added evented #popen. 11 | 12 | -------------------------------------------------- 13 | Version: 0.7.3, released 22May07 14 | Added a large variety of small features. See the ChangeLog. 15 | 16 | -------------------------------------------------- 17 | Version: 0.7.1, released xxNov06 18 | Added protocol handlers for line-oriented protocols. 19 | Various bug fixes. 20 | 21 | -------------------------------------------------- 22 | Version: 0.7.0, released 20Nov06 23 | Added a fix in em.cpp/ConnectToServer to fix a fatal exception that 24 | occurred in FreeBSD when connecting successfully to a remote server. 25 | 26 | -------------------------------------------------- 27 | Version: 0.6.0, released xxJul06 28 | Added deferred operations, suggested by Don Stocks, amillionhitpoints@yahoo.com. 29 | 30 | -------------------------------------------------- 31 | Version: 0.5.4, released xxJun06 32 | Added get_peername support for streams and datagrams. 33 | 34 | -------------------------------------------------- 35 | Version: 0.5.3, released 17May06 36 | Fixed bugs in extconf.rb, thanks to Daniel Harple, dharple@generalconsumption.org. 37 | Added proper setup.rb and rake tasks, thanks to Austin Ziegler. 38 | Fixed a handful of reported problems with builds on various platforms. 39 | 40 | -------------------------------------------------- 41 | Version: 0.5.2, released 05May06 42 | Made several nonvisible improvements to the Windows 43 | implementation. 44 | Added an exception-handling patch contributed by Jeff Rose, jeff@rosejn.net. 45 | Added a dir-config patch contributed anonymously. 46 | Supported builds on Solaris. 47 | 48 | -------------------------------------------------- 49 | Version: 0.5.1, released 05May06 50 | Made it possible to pass a Class rather than a Module 51 | to a protocol handler. 52 | Added Windows port. 53 | 54 | -------------------------------------------------- 55 | Version: 0.5.0, released 30Apr06 56 | Added a preliminary SSL/TLS extension. This will probably 57 | change over the next few releases. 58 | 59 | -------------------------------------------------- 60 | Version: 0.4.5, released 29Apr06 61 | Changed ext files so the ruby.h is installed after unistd.h 62 | otherwise it doesn't compile on gcc 4.1 63 | 64 | -------------------------------------------------- 65 | Version: 0.4.2, released 19Apr06 66 | Changed the Ruby-glue so the extension will play nicer 67 | in the sandbox with Ruby threads. 68 | Added an EventMachine::run_without_threads API to 69 | switch off the thread-awareness for better performance 70 | in programs that do not spin any Ruby threads. 71 | 72 | -------------------------------------------------- 73 | Version: 0.4.1, released 15Apr06 74 | Reworked the shared-object interface to make it easier to 75 | use EventMachine from languages other than Ruby. 76 | 77 | -------------------------------------------------- 78 | Version: 0.3.2, released 12Apr06 79 | Added support for a user-supplied block in EventMachine#connect. 80 | 81 | -------------------------------------------------- 82 | Version: 0.3.1, released 11Apr06 83 | Fixed bug that prevented EventMachine from being run multiple 84 | times in a single process. 85 | 86 | -------------------------------------------------- 87 | Version: 0.3.0, released 10Apr06 88 | Added method EventHandler::Connection::post_init 89 | 90 | -------------------------------------------------- 91 | Version: 0.2.0, released 10Apr06 92 | Added method EventHandler::stop 93 | 94 | 95 | -------------------------------------------------------------------------------- /examples/guides/getting_started/08_simple_chat_server_step_five.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'rubygems' # or use Bundler.setup 4 | require 'eventmachine' 5 | 6 | class SimpleChatServer < EM::Connection 7 | 8 | @@connected_clients = Array.new 9 | DM_REGEXP = /^@([a-zA-Z0-9]+)\s*:?\s+(.+)/.freeze 10 | 11 | attr_reader :username 12 | 13 | 14 | # 15 | # EventMachine handlers 16 | # 17 | 18 | def post_init 19 | @username = nil 20 | 21 | puts "A client has connected..." 22 | ask_username 23 | end 24 | 25 | def unbind 26 | @@connected_clients.delete(self) 27 | puts "[info] #{@username} has left" if entered_username? 28 | end 29 | 30 | def receive_data(data) 31 | if entered_username? 32 | handle_chat_message(data.strip) 33 | else 34 | handle_username(data.strip) 35 | end 36 | end 37 | 38 | 39 | # 40 | # Username handling 41 | # 42 | 43 | def entered_username? 44 | !@username.nil? && !@username.empty? 45 | end # entered_username? 46 | 47 | def handle_username(input) 48 | if input.empty? 49 | send_line("Blank usernames are not allowed. Try again.") 50 | ask_username 51 | else 52 | @username = input 53 | @@connected_clients.push(self) 54 | self.other_peers.each { |c| c.send_data("#{@username} has joined the room\n") } 55 | puts "#{@username} has joined" 56 | 57 | self.send_line("[info] Ohai, #{@username}") 58 | end 59 | end # handle_username(input) 60 | 61 | def ask_username 62 | self.send_line("[info] Enter your username:") 63 | end # ask_username 64 | 65 | 66 | # 67 | # Message handling 68 | # 69 | 70 | def handle_chat_message(msg) 71 | if command?(msg) 72 | self.handle_command(msg) 73 | else 74 | if direct_message?(msg) 75 | self.handle_direct_message(msg) 76 | else 77 | self.announce(msg, "#{@username}:") 78 | end 79 | end 80 | end # handle_chat_message(msg) 81 | 82 | def direct_message?(input) 83 | input =~ DM_REGEXP 84 | end # direct_message?(input) 85 | 86 | def handle_direct_message(input) 87 | username, message = parse_direct_message(input) 88 | 89 | if connection = @@connected_clients.find { |c| c.username == username } 90 | puts "[dm] @#{@username} => @#{username}" 91 | connection.send_line("[dm] @#{@username}: #{message}") 92 | else 93 | send_line "@#{username} is not in the room. Here's who is: #{usernames.join(', ')}" 94 | end 95 | end # handle_direct_message(input) 96 | 97 | def parse_direct_message(input) 98 | return [$1, $2] if input =~ DM_REGEXP 99 | end # parse_direct_message(input) 100 | 101 | 102 | # 103 | # Commands handling 104 | # 105 | 106 | def command?(input) 107 | input =~ /(exit|status)$/i 108 | end # command?(input) 109 | 110 | def handle_command(cmd) 111 | case cmd 112 | when /exit$/i then self.close_connection 113 | when /status$/i then self.send_line("[chat server] It's #{Time.now.strftime('%H:%M')} and there are #{self.number_of_connected_clients} people in the room") 114 | end 115 | end # handle_command(cmd) 116 | 117 | 118 | # 119 | # Helpers 120 | # 121 | 122 | def announce(msg = nil, prefix = "[chat server]") 123 | @@connected_clients.each { |c| c.send_line("#{prefix} #{msg}") } unless msg.empty? 124 | end # announce(msg) 125 | 126 | def other_peers 127 | @@connected_clients.reject { |c| self == c } 128 | end # other_peers 129 | 130 | def send_line(line) 131 | self.send_data("#{line}\n") 132 | end # send_line(line) 133 | end 134 | 135 | EventMachine.run do 136 | # hit Control + C to stop 137 | Signal.trap("INT") { EventMachine.stop } 138 | Signal.trap("TERM") { EventMachine.stop } 139 | 140 | EventMachine.start_server("0.0.0.0", 10000, SimpleChatServer) 141 | end 142 | -------------------------------------------------------------------------------- /docs/old/PURE_RUBY: -------------------------------------------------------------------------------- 1 | EventMachine is supplied in three alternative versions. 2 | 3 | 1) A version that includes a Ruby extension written in C++. This version requires compilation; 4 | 2) A version for JRuby that contains a precompiled JAR file written in Java; 5 | 3) A pure Ruby version that has no external dependencies and can run in any Ruby environment. 6 | 7 | The Java version of EventMachine is packaged in a distinct manner and must be installed using a 8 | special procedure. This version is described fully in a different document, and not considered 9 | further here. 10 | 11 | The C++ and pure-Ruby versions, however, are shipped in the same distribution. You use the same 12 | files (either tarball or Ruby gem) to install both of these versions. 13 | 14 | If you intend to use the C++ version, you must successfully compile EventMachine after you install it. 15 | (The gem installation attempts to perform this step automatically.) 16 | 17 | If you choose not to compile the EventMachine C++ extension, or if your compilation fails for any 18 | reason, you still have a fully-functional installation of the pure-Ruby version of EM. 19 | 20 | However, for technical reasons, a default EM installation (whether or not the compilation succeeds) 21 | will always assume that the compiled ("extension") implementation should be used. 22 | 23 | If you want your EM program to use the pure Ruby version, you must specifically request it. There 24 | are two ways to do this: by setting either a Ruby global variable, or an environment string. 25 | 26 | The following code will invoke the pure-Ruby implementation of EM: 27 | 28 | $eventmachine_library = :pure_ruby 29 | require 'eventmachine' 30 | 31 | EM.library_type #=> "pure_ruby" 32 | 33 | Notice that this requires a code change and is not the preferred way to select pure Ruby, unless 34 | for some reason you are absolutely sure you will never want the compiled implementation. 35 | 36 | Setting the following environment string has the same effect: 37 | 38 | export EVENTMACHINE_LIBRARY="pure_ruby" 39 | 40 | This technique gives you the flexibility to select either version at runtime with no code changes. 41 | 42 | Support 43 | 44 | The EventMachine development team has committed to support precisely the same APIs for all the 45 | various implementations of EM. 46 | 47 | This means that you can expect any EM program to behave identically, whether you use pure Ruby, 48 | the compiled C++ extension, or JRuby. Deviations from this behavior are to be considered bugs 49 | and should be reported as such. 50 | 51 | There is a small number of exceptions to this rule, which arise from underlying platform 52 | distinctions. Notably, EM#epoll is a silent no-op in the pure Ruby implementation. 53 | 54 | 55 | When Should You Use the Pure-Ruby Implementation of EM? 56 | 57 | 58 | Use the pure Ruby implementation of EM when you must support a platform for which no C++ compiler 59 | is available, or on which the standard EM C++ code can't be compiled. 60 | 61 | Keep in mind that you don't need a C++ compiler in order to deploy EM applications that rely on 62 | the compiled version, so long as appropriate C++ runtime libraries are available on the target platform. 63 | 64 | In extreme cases, you may find that you can develop software with the compiled EM version, but are 65 | not allowed to install required runtime libraries on the deployment system(s). This would be another 66 | case in which the pure Ruby implementation can be useful. 67 | 68 | In general you should avoid the pure Ruby version of EM when performance and scalability are important. 69 | EM in pure Ruby will necessarily run slower than the compiled version. Depending on your application 70 | this may or may not be a key issue. 71 | 72 | Also, since EPOLL is not supported in pure Ruby, your applications will be affected by Ruby's built-in 73 | limit of 1024 file and socket descriptors that may be open in a single process. For maximum scalability 74 | and performance, always use EPOLL if possible. 75 | 76 | -------------------------------------------------------------------------------- /tests/test_ltp.rb: -------------------------------------------------------------------------------- 1 | require 'em_test_helper' 2 | 3 | class TestLineAndTextProtocol < Test::Unit::TestCase 4 | 5 | class SimpleLineTest < EM::P::LineAndTextProtocol 6 | def receive_line line 7 | @line_buffer << line 8 | end 9 | end 10 | 11 | module StopClient 12 | def set_receive_data(&blk) 13 | @rdb = blk 14 | end 15 | 16 | def receive_data data 17 | @rdb.call(data) if @rdb 18 | end 19 | 20 | def unbind 21 | EM.add_timer(0.1) { EM.stop } 22 | end 23 | end 24 | 25 | def setup 26 | @port = next_port 27 | end 28 | 29 | def test_simple_lines 30 | lines_received = [] 31 | EM.run { 32 | EM.start_server( "127.0.0.1", @port, SimpleLineTest ) do |conn| 33 | conn.instance_eval "@line_buffer = lines_received" 34 | end 35 | setup_timeout 36 | 37 | EM.connect "127.0.0.1", @port, StopClient do |c| 38 | c.send_data "aaa\nbbb\r\nccc\n" 39 | c.close_connection_after_writing 40 | end 41 | } 42 | assert_equal( %w(aaa bbb ccc), lines_received ) 43 | end 44 | 45 | #-------------------------------------------------------------------- 46 | 47 | class SimpleLineTest < EM::P::LineAndTextProtocol 48 | def receive_error text 49 | @error_message << text 50 | end 51 | end 52 | 53 | def test_overlength_lines 54 | lines_received = [] 55 | EM.run { 56 | EM.start_server( "127.0.0.1", @port, SimpleLineTest ) do |conn| 57 | conn.instance_eval "@error_message = lines_received" 58 | end 59 | setup_timeout 60 | 61 | EM.connect "127.0.0.1", @port, StopClient do |c| 62 | c.send_data "a" * (16*1024 + 1) 63 | c.send_data "\n" 64 | c.close_connection_after_writing 65 | end 66 | 67 | } 68 | assert_equal( ["overlength line"], lines_received ) 69 | end 70 | 71 | 72 | #-------------------------------------------------------------------- 73 | 74 | class LineAndTextTest < EM::P::LineAndTextProtocol 75 | def receive_line line 76 | if line =~ /content-length:\s*(\d+)/i 77 | @content_length = $1.to_i 78 | elsif line.length == 0 79 | set_binary_mode @content_length 80 | end 81 | end 82 | def receive_binary_data text 83 | send_data "received #{text.length} bytes" 84 | close_connection_after_writing 85 | end 86 | end 87 | 88 | def test_lines_and_text 89 | output = '' 90 | EM.run { 91 | EM.start_server( "127.0.0.1", @port, LineAndTextTest ) 92 | setup_timeout 93 | 94 | EM.connect "127.0.0.1", @port, StopClient do |c| 95 | c.set_receive_data { |data| output << data } 96 | c.send_data "Content-length: 400\n" 97 | c.send_data "\n" 98 | c.send_data "A" * 400 99 | EM.add_timer(0.1) { c.close_connection_after_writing } 100 | end 101 | } 102 | assert_equal( "received 400 bytes", output ) 103 | end 104 | 105 | #-------------------------------------------------------------------- 106 | 107 | 108 | class BinaryTextTest < EM::P::LineAndTextProtocol 109 | def receive_line line 110 | if line =~ /content-length:\s*(\d+)/i 111 | set_binary_mode $1.to_i 112 | else 113 | raise "protocol error" 114 | end 115 | end 116 | def receive_binary_data text 117 | send_data "received #{text.length} bytes" 118 | close_connection_after_writing 119 | end 120 | end 121 | 122 | def test_binary_text 123 | output = '' 124 | EM.run { 125 | EM.start_server( "127.0.0.1", @port, BinaryTextTest ) 126 | setup_timeout 127 | 128 | EM.connect "127.0.0.1", @port, StopClient do |c| 129 | c.set_receive_data { |data| output << data } 130 | c.send_data "Content-length: 10000\n" 131 | c.send_data "A" * 10000 132 | EM.add_timer(0.1) { c.close_connection_after_writing } 133 | end 134 | } 135 | assert_equal( "received 10000 bytes", output ) 136 | end 137 | 138 | end 139 | -------------------------------------------------------------------------------- /examples/guides/getting_started/03_simple_chat_server.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'rubygems' # or use Bundler.setup 4 | require 'eventmachine' 5 | 6 | class SimpleChatServer < EM::Connection 7 | 8 | @@connected_clients = Array.new 9 | DM_REGEXP = /^@([a-zA-Z0-9]+)\s*:?\s*(.+)/.freeze 10 | 11 | attr_reader :username 12 | 13 | 14 | # 15 | # EventMachine handlers 16 | # 17 | 18 | def post_init 19 | @username = nil 20 | 21 | puts "A client has connected..." 22 | ask_username 23 | end 24 | 25 | def unbind 26 | @@connected_clients.delete(self) 27 | puts "[info] #{@username} has left" if entered_username? 28 | end 29 | 30 | def receive_data(data) 31 | if entered_username? 32 | handle_chat_message(data.strip) 33 | else 34 | handle_username(data.strip) 35 | end 36 | end 37 | 38 | 39 | # 40 | # Username handling 41 | # 42 | 43 | def entered_username? 44 | !@username.nil? && !@username.empty? 45 | end # entered_username? 46 | 47 | def handle_username(input) 48 | if input.empty? 49 | send_line("Blank usernames are not allowed. Try again.") 50 | ask_username 51 | else 52 | @username = input 53 | @@connected_clients.push(self) 54 | self.other_peers.each { |c| c.send_data("#{@username} has joined the room\n") } 55 | puts "#{@username} has joined" 56 | 57 | self.send_line("[info] Ohai, #{@username}") 58 | end 59 | end # handle_username(input) 60 | 61 | def ask_username 62 | self.send_line("[info] Enter your username:") 63 | end # ask_username 64 | 65 | 66 | # 67 | # Message handling 68 | # 69 | 70 | def handle_chat_message(msg) 71 | if command?(msg) 72 | self.handle_command(msg) 73 | else 74 | if direct_message?(msg) 75 | self.handle_direct_message(msg) 76 | else 77 | self.announce(msg, "#{@username}:") 78 | end 79 | end 80 | end # handle_chat_message(msg) 81 | 82 | def direct_message?(input) 83 | input =~ DM_REGEXP 84 | end # direct_message?(input) 85 | 86 | def handle_direct_message(input) 87 | username, message = parse_direct_message(input) 88 | 89 | if connection = @@connected_clients.find { |c| c.username == username } 90 | puts "[dm] @#{@username} => @#{username}" 91 | connection.send_line("[dm] @#{@username}: #{message}") 92 | else 93 | send_line "@#{username} is not in the room. Here's who is: #{usernames.join(', ')}" 94 | end 95 | end # handle_direct_message(input) 96 | 97 | def parse_direct_message(input) 98 | return [$1, $2] if input =~ DM_REGEXP 99 | end # parse_direct_message(input) 100 | 101 | 102 | # 103 | # Commands handling 104 | # 105 | 106 | def command?(input) 107 | input =~ /(exit|status)$/i 108 | end # command?(input) 109 | 110 | def handle_command(cmd) 111 | case cmd 112 | when /exit$/i then self.close_connection 113 | when /status$/i then self.send_line("[chat server] It's #{Time.now.strftime('%H:%M')} and there are #{self.number_of_connected_clients} people in the room") 114 | end 115 | end # handle_command(cmd) 116 | 117 | 118 | # 119 | # Helpers 120 | # 121 | 122 | def announce(msg = nil, prefix = "[chat server]") 123 | @@connected_clients.each { |c| c.send_line("#{prefix} #{msg}") } unless msg.empty? 124 | end # announce(msg) 125 | 126 | def number_of_connected_clients 127 | @@connected_clients.size 128 | end # number_of_connected_clients 129 | 130 | def other_peers 131 | @@connected_clients.reject { |c| self == c } 132 | end # other_peers 133 | 134 | def send_line(line) 135 | self.send_data("#{line}\n") 136 | end # send_line(line) 137 | 138 | def usernames 139 | @@connected_clients.map { |c| c.username } 140 | end # usernames 141 | end 142 | 143 | EventMachine.run do 144 | # hit Control + C to stop 145 | Signal.trap("INT") { EventMachine.stop } 146 | Signal.trap("TERM") { EventMachine.stop } 147 | 148 | EventMachine.start_server("0.0.0.0", 10000, SimpleChatServer) 149 | end 150 | -------------------------------------------------------------------------------- /tests/test_epoll.rb: -------------------------------------------------------------------------------- 1 | require 'em_test_helper' 2 | 3 | 4 | class TestEpoll < Test::Unit::TestCase 5 | 6 | module TestEchoServer 7 | def receive_data data 8 | send_data data 9 | close_connection_after_writing 10 | end 11 | end 12 | 13 | module TestEchoClient 14 | def connection_completed 15 | send_data "ABCDE" 16 | $max += 1 17 | end 18 | def receive_data data 19 | raise "bad response" unless data == "ABCDE" 20 | end 21 | def unbind 22 | $n -= 1 23 | EM.stop if $n == 0 24 | end 25 | end 26 | 27 | 28 | # We can set the rlimit/nofile of a process but we can only set it 29 | # higher if we're running as root. 30 | # On most systems, the default value is 1024. 31 | def test_rlimit 32 | omit_if(windows? || jruby?) 33 | unless EM.set_descriptor_table_size >= 1024 34 | a = EM.set_descriptor_table_size 35 | assert( a <= 1024 ) 36 | a = EM.set_descriptor_table_size( 1024 ) 37 | assert( a == 1024 ) 38 | end 39 | end 40 | 41 | # Run a high-volume version of this test by kicking the number of connections 42 | # up past 512. (Each connection uses two sockets, a client and a server.) 43 | # (Will require running the test as root) 44 | # This test exercises TCP clients and servers. 45 | # 46 | # XXX this test causes all sort of weird issues on OSX (when run as part of the suite) 47 | def _test_descriptors 48 | EM.epoll 49 | EM.set_descriptor_table_size 60000 50 | EM.run { 51 | EM.start_server "127.0.0.1", 9800, TestEchoServer 52 | $n = 0 53 | $max = 0 54 | 100.times { 55 | EM.connect("127.0.0.1", 9800, TestEchoClient) {$n += 1} 56 | } 57 | } 58 | assert_equal(0, $n) 59 | assert_equal(100, $max) 60 | end 61 | 62 | def setup 63 | @port = next_port 64 | end 65 | 66 | module TestDatagramServer 67 | def receive_data dgm 68 | $in = dgm 69 | send_data "abcdefghij" 70 | end 71 | end 72 | module TestDatagramClient 73 | def initialize port 74 | @port = port 75 | end 76 | 77 | def post_init 78 | send_datagram "1234567890", "127.0.0.1", @port 79 | end 80 | 81 | def receive_data dgm 82 | $out = dgm 83 | EM.stop 84 | end 85 | end 86 | 87 | def test_datagrams 88 | $in = $out = "" 89 | EM.run { 90 | EM.open_datagram_socket "127.0.0.1", @port, TestDatagramServer 91 | EM.open_datagram_socket "127.0.0.1", 0, TestDatagramClient, @port 92 | } 93 | assert_equal( "1234567890", $in ) 94 | assert_equal( "abcdefghij", $out ) 95 | end 96 | 97 | # XXX this test fails randomly... 98 | def _test_unix_domain 99 | fn = "/tmp/xxx.chain" 100 | EM.epoll 101 | EM.set_descriptor_table_size 60000 102 | EM.run { 103 | # The pure-Ruby version won't let us open the socket if the node already exists. 104 | # Not sure, that actually may be correct and the compiled version is wrong. 105 | # Pure Ruby also oddly won't let us make that many connections. This test used 106 | # to run 100 times. Not sure where that lower connection-limit is coming from in 107 | # pure Ruby. 108 | # Let's not sweat the Unix-ness of the filename, since this test can't possibly 109 | # work on Windows anyway. 110 | # 111 | File.unlink(fn) if File.exist?(fn) 112 | EM.start_unix_domain_server fn, TestEchoServer 113 | $n = 0 114 | $max = 0 115 | 50.times { 116 | EM.connect_unix_domain(fn, TestEchoClient) {$n += 1} 117 | } 118 | EM::add_timer(1) { $stderr.puts("test_unix_domain timed out!"); EM::stop } 119 | } 120 | assert_equal(0, $n) 121 | assert_equal(50, $max) 122 | ensure 123 | File.unlink(fn) if File.exist?(fn) 124 | end 125 | 126 | def test_attach_detach 127 | EM.epoll 128 | EM.run { 129 | EM.add_timer(0.01) { EM.stop } 130 | 131 | r, w = IO.pipe 132 | 133 | # This tests a regression where detach in the same tick as attach crashes EM 134 | EM.watch(r) do |connection| 135 | connection.detach 136 | end 137 | } 138 | 139 | assert true 140 | end 141 | end 142 | 143 | -------------------------------------------------------------------------------- /lib/em/streamer.rb: -------------------------------------------------------------------------------- 1 | module EventMachine 2 | # Streams a file over a given connection. Streaming begins once the object is 3 | # instantiated. Typically FileStreamer instances are not reused. 4 | # 5 | # Streaming uses buffering for files larger than 16K and uses so-called fast file reader (a C++ extension) 6 | # if available (it is part of eventmachine gem itself). 7 | # 8 | # @example 9 | # 10 | # module FileSender 11 | # def post_init 12 | # streamer = EventMachine::FileStreamer.new(self, '/tmp/bigfile.tar') 13 | # streamer.callback{ 14 | # # file was sent successfully 15 | # close_connection_after_writing 16 | # } 17 | # end 18 | # end 19 | # 20 | # 21 | # @author Francis Cianfrocca 22 | class FileStreamer 23 | include Deferrable 24 | 25 | # Use mapped streamer for files bigger than 16k 26 | MappingThreshold = 16384 27 | # Wait until next tick to send more data when 50k is still in the outgoing buffer 28 | BackpressureLevel = 50000 29 | # Send 16k chunks at a time 30 | ChunkSize = 16384 31 | 32 | # @param [EventMachine::Connection] connection 33 | # @param [String] filename File path 34 | # 35 | # @option args [Boolean] :http_chunks (false) Use HTTP 1.1 style chunked-encoding semantics. 36 | def initialize connection, filename, args = {} 37 | @connection = connection 38 | @http_chunks = args[:http_chunks] 39 | 40 | if File.exist?(filename) 41 | @size = File.size(filename) 42 | if @size <= MappingThreshold 43 | stream_without_mapping filename 44 | else 45 | stream_with_mapping filename 46 | end 47 | else 48 | fail "file not found" 49 | end 50 | end 51 | 52 | # @private 53 | def stream_without_mapping filename 54 | if @http_chunks 55 | @connection.send_data "#{@size.to_s(16)}\r\n" 56 | @connection.send_file_data filename 57 | @connection.send_data "\r\n0\r\n\r\n" 58 | else 59 | @connection.send_file_data filename 60 | end 61 | succeed 62 | end 63 | private :stream_without_mapping 64 | 65 | # @private 66 | def stream_with_mapping filename 67 | ensure_mapping_extension_is_present 68 | 69 | @position = 0 70 | @mapping = EventMachine::FastFileReader::Mapper.new filename 71 | stream_one_chunk 72 | end 73 | private :stream_with_mapping 74 | 75 | # Used internally to stream one chunk at a time over multiple reactor ticks 76 | # @private 77 | def stream_one_chunk 78 | loop { 79 | if @position < @size 80 | if @connection.get_outbound_data_size > BackpressureLevel 81 | EventMachine::next_tick {stream_one_chunk} 82 | break 83 | else 84 | len = @size - @position 85 | len = ChunkSize if (len > ChunkSize) 86 | 87 | @connection.send_data( "#{len.to_s(16)}\r\n" ) if @http_chunks 88 | @connection.send_data( @mapping.get_chunk( @position, len )) 89 | @connection.send_data("\r\n") if @http_chunks 90 | 91 | @position += len 92 | end 93 | else 94 | @connection.send_data "0\r\n\r\n" if @http_chunks 95 | @mapping.close 96 | succeed 97 | break 98 | end 99 | } 100 | end 101 | 102 | # 103 | # We use an outboard extension class to get memory-mapped files. 104 | # It's outboard to avoid polluting the core distro, but that means 105 | # there's a "hidden" dependency on it. The first time we get here in 106 | # any run, try to load up the dependency extension. User code will see 107 | # a LoadError if it's not available, but code that doesn't require 108 | # mapped files will work fine without it. This is a somewhat difficult 109 | # compromise between usability and proper modularization. 110 | # 111 | # @private 112 | def ensure_mapping_extension_is_present 113 | @@fastfilereader ||= (require 'fastfilereaderext') 114 | end 115 | private :ensure_mapping_extension_is_present 116 | 117 | end # FileStreamer 118 | end # EventMachine 119 | -------------------------------------------------------------------------------- /lib/em/processes.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # 3 | # Author:: Francis Cianfrocca (gmail: blackhedd) 4 | # Homepage:: http://rubyeventmachine.com 5 | # Date:: 13 Dec 07 6 | # 7 | # See EventMachine and EventMachine::Connection for documentation and 8 | # usage examples. 9 | # 10 | #---------------------------------------------------------------------------- 11 | # 12 | # Copyright (C) 2006-08 by Francis Cianfrocca. All Rights Reserved. 13 | # Gmail: blackhedd 14 | # 15 | # This program is free software; you can redistribute it and/or modify 16 | # it under the terms of either: 1) the GNU General Public License 17 | # as published by the Free Software Foundation; either version 2 of the 18 | # License, or (at your option) any later version; or 2) Ruby's License. 19 | # 20 | # See the file COPYING for complete licensing information. 21 | # 22 | #--------------------------------------------------------------------------- 23 | # 24 | # 25 | 26 | 27 | module EventMachine 28 | 29 | # EM::DeferrableChildProcess is a sugaring of a common use-case 30 | # involving EM::popen. 31 | # Call the #open method on EM::DeferrableChildProcess, passing 32 | # a command-string. #open immediately returns an EM::Deferrable 33 | # object. It also schedules the forking of a child process, which 34 | # will execute the command passed to #open. 35 | # When the forked child terminates, the Deferrable will be signalled 36 | # and execute its callbacks, passing the data that the child process 37 | # wrote to stdout. 38 | # 39 | class DeferrableChildProcess < EventMachine::Connection 40 | include EventMachine::Deferrable 41 | 42 | # @private 43 | def initialize 44 | super 45 | @data = [] 46 | end 47 | 48 | # Sugars a common use-case involving forked child processes. 49 | # #open takes a String argument containing an shell command 50 | # string (including arguments if desired). #open immediately 51 | # returns an EventMachine::Deferrable object, without blocking. 52 | # 53 | # It also invokes EventMachine#popen to run the passed-in 54 | # command in a forked child process. 55 | # 56 | # When the forked child terminates, the Deferrable that 57 | # #open calls its callbacks, passing the data returned 58 | # from the child process. 59 | # 60 | def self.open cmd 61 | EventMachine.popen( cmd, DeferrableChildProcess ) 62 | end 63 | 64 | # @private 65 | def receive_data data 66 | @data << data 67 | end 68 | 69 | # @private 70 | def unbind 71 | succeed( @data.join ) 72 | end 73 | end 74 | 75 | # @private 76 | class SystemCmd < EventMachine::Connection 77 | def initialize cb 78 | @cb = cb 79 | @output = [] 80 | end 81 | def receive_data data 82 | @output << data 83 | end 84 | def unbind 85 | @cb.call @output.join(''), get_status if @cb 86 | end 87 | end 88 | 89 | # EM::system is a simple wrapper for EM::popen. It is similar to Kernel::system, but requires a 90 | # single string argument for the command and performs no shell expansion. 91 | # 92 | # The block or proc passed to EM::system is called with two arguments: the output generated by the command, 93 | # and a Process::Status that contains information about the command's execution. 94 | # 95 | # EM.run{ 96 | # EM.system('ls'){ |output,status| puts output if status.exitstatus == 0 } 97 | # } 98 | # 99 | # You can also supply an additional proc to send some data to the process: 100 | # 101 | # EM.run{ 102 | # EM.system('sh', proc{ |process| 103 | # process.send_data("echo hello\n") 104 | # process.send_data("exit\n") 105 | # }, proc{ |out,status| 106 | # puts(out) 107 | # }) 108 | # } 109 | # 110 | # Like EventMachine.popen, EventMachine.system currently does not work on windows. 111 | # It returns the pid of the spawned process. 112 | def EventMachine::system cmd, *args, &cb 113 | cb ||= args.pop if args.last.is_a? Proc 114 | init = args.pop if args.last.is_a? Proc 115 | 116 | # merge remaining arguments into the command 117 | cmd = [cmd, *args] if args.any? 118 | 119 | EM.get_subprocess_pid(EM.popen(cmd, SystemCmd, cb) do |c| 120 | init[c] if init 121 | end.signature) 122 | end 123 | end 124 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # About EventMachine [![Code Climate](https://codeclimate.com/github/eventmachine/eventmachine.png)](https://codeclimate.com/github/eventmachine/eventmachine) 2 | 3 | 4 | ## What is EventMachine ## 5 | 6 | EventMachine is an event-driven I/O and lightweight concurrency library for Ruby. 7 | It provides event-driven I/O using the [Reactor pattern](http://en.wikipedia.org/wiki/Reactor_pattern), 8 | much like [JBoss Netty](http://www.jboss.org/netty), [Apache MINA](http://mina.apache.org/), 9 | Python's [Twisted](http://twistedmatrix.com), [Node.js](http://nodejs.org), libevent and libev. 10 | 11 | EventMachine is designed to simultaneously meet two key needs: 12 | 13 | * Extremely high scalability, performance and stability for the most demanding production environments. 14 | * An API that eliminates the complexities of high-performance threaded network programming, 15 | allowing engineers to concentrate on their application logic. 16 | 17 | This unique combination makes EventMachine a premier choice for designers of critical networked 18 | applications, including Web servers and proxies, email and IM production systems, authentication/authorization 19 | processors, and many more. 20 | 21 | EventMachine has been around since the early 2000s and is a mature and battle tested library. 22 | 23 | 24 | ## What EventMachine is good for? ## 25 | 26 | * Scalable event-driven servers. Examples: [Thin](http://code.macournoyer.com/thin/) or [Goliath](https://github.com/postrank-labs/goliath/). 27 | * 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). 28 | * Efficient network proxies with custom logic. Examples: [Proxymachine](https://github.com/mojombo/proxymachine/). 29 | * File and network monitoring tools. Examples: [eventmachine-tail](https://github.com/jordansissel/eventmachine-tail) and [logstash](https://github.com/logstash/logstash). 30 | 31 | 32 | 33 | ## What platforms are supported by EventMachine? ## 34 | 35 | EventMachine supports Ruby 1.8.7, 1.9.2, REE, JRuby and **works well on Windows** as well 36 | as many operating systems from the Unix family (Linux, Mac OS X, BSD flavors). 37 | 38 | 39 | 40 | ## Install the gem ## 41 | 42 | Install it with [RubyGems](https://rubygems.org/) 43 | 44 | gem install eventmachine 45 | 46 | or add this to your Gemfile if you use [Bundler](http://gembundler.com/): 47 | 48 | gem "eventmachine" 49 | 50 | 51 | 52 | ## Getting started ## 53 | 54 | For an introduction to EventMachine, check out: 55 | 56 | * [blog post about EventMachine by Ilya Grigorik](http://www.igvita.com/2008/05/27/ruby-eventmachine-the-speed-demon/). 57 | * [EventMachine Introductions by Dan Sinclair](http://everburning.com/news/eventmachine-introductions/). 58 | 59 | 60 | ### Server example: Echo server ### 61 | 62 | Here's a fully-functional echo server written with EventMachine: 63 | 64 | require 'eventmachine' 65 | 66 | module EchoServer 67 | def post_init 68 | puts "-- someone connected to the echo server!" 69 | end 70 | 71 | def receive_data data 72 | send_data ">>>you sent: #{data}" 73 | close_connection if data =~ /quit/i 74 | end 75 | 76 | def unbind 77 | puts "-- someone disconnected from the echo server!" 78 | end 79 | end 80 | 81 | # Note that this will block current thread. 82 | EventMachine.run { 83 | EventMachine.start_server "127.0.0.1", 8081, EchoServer 84 | } 85 | 86 | 87 | ## EventMachine documentation ## 88 | 89 | Currently we only have [reference documentation](http://rdoc.info/github/eventmachine/eventmachine/frames) and a [wiki](https://github.com/eventmachine/eventmachine/wiki). 90 | 91 | 92 | ## Community and where to get help ## 93 | 94 | * Join the [mailing list](http://groups.google.com/group/eventmachine) (Google Group) 95 | * Join IRC channel #eventmachine on irc.freenode.net 96 | 97 | 98 | ## License and copyright ## 99 | 100 | EventMachine is copyrighted free software made available under the terms 101 | of either the GPL or Ruby's License. 102 | 103 | Copyright: (C) 2006-07 by Francis Cianfrocca. All Rights Reserved. 104 | 105 | 106 | ## Alternatives ## 107 | 108 | If you are unhappy with EventMachine and want to use Ruby, check out [Cool.io](http://coolio.github.com/). 109 | One caveat: by May 2011, it did not support JRuby and Windows. 110 | -------------------------------------------------------------------------------- /ext/project.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | 3 | $Id$ 4 | 5 | File: project.h 6 | Date: 06Apr06 7 | 8 | Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved. 9 | Gmail: blackhedd 10 | 11 | This program is free software; you can redistribute it and/or modify 12 | it under the terms of either: 1) the GNU General Public License 13 | as published by the Free Software Foundation; either version 2 of the 14 | License, or (at your option) any later version; or 2) Ruby's License. 15 | 16 | See the file COPYING for complete licensing information. 17 | 18 | *****************************************************************************/ 19 | 20 | 21 | #ifndef __Project__H_ 22 | #define __Project__H_ 23 | 24 | 25 | #ifdef OS_WIN32 26 | #pragma warning(disable:4786) 27 | #endif 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | 39 | #ifdef OS_UNIX 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | #include 57 | #include 58 | #include 59 | typedef int SOCKET; 60 | #define INVALID_SOCKET -1 61 | #define SOCKET_ERROR -1 62 | #ifdef OS_SOLARIS8 63 | #include 64 | #include 65 | #ifndef AF_LOCAL 66 | #define AF_LOCAL AF_UNIX 67 | #endif 68 | // INADDR_NONE is undefined on Solaris < 8. Thanks to Brett Eisenberg and Tim Pease. 69 | #ifndef INADDR_NONE 70 | #define INADDR_NONE ((unsigned long)-1) 71 | #endif 72 | #endif /* OS_SOLARIS8 */ 73 | 74 | #ifdef _AIX 75 | #include 76 | #ifndef AF_LOCAL 77 | #define AF_LOCAL AF_UNIX 78 | #endif 79 | #endif /* _AIX */ 80 | 81 | #ifdef OS_DARWIN 82 | #include 83 | #include 84 | #endif /* OS_DARWIN */ 85 | 86 | #endif /* OS_UNIX */ 87 | 88 | #ifdef OS_WIN32 89 | // 21Sep09: windows limits select() to 64 sockets by default, we increase it to 1024 here (before including winsock2.h) 90 | // 18Jun12: fd_setsize must be changed in the ruby binary (not in this extension). redefining it also causes segvs, see eventmachine/eventmachine#333 91 | //#define FD_SETSIZE 1024 92 | 93 | // WIN32_LEAN_AND_MEAN excludes APIs such as Cryptography, DDE, RPC, Shell, and Windows Sockets. 94 | #define WIN32_LEAN_AND_MEAN 95 | 96 | #include 97 | #include 98 | #include 99 | #include 100 | #include 101 | #include 102 | 103 | // Use the Win32 wrapper library that Ruby owns to be able to close sockets with the close() function 104 | #define RUBY_EXPORT 105 | #include 106 | #include 107 | #endif /* OS_WIN32 */ 108 | 109 | #if !defined(_MSC_VER) || _MSC_VER > 1500 110 | #include 111 | #endif 112 | 113 | using namespace std; 114 | 115 | #ifdef WITH_SSL 116 | #include 117 | #include 118 | #endif 119 | 120 | #ifdef HAVE_EPOLL 121 | #include 122 | #endif 123 | 124 | #ifdef HAVE_KQUEUE 125 | #include 126 | #include 127 | #endif 128 | 129 | #ifdef HAVE_INOTIFY 130 | #include 131 | #endif 132 | 133 | #ifdef HAVE_OLD_INOTIFY 134 | #include 135 | #include 136 | static inline int inotify_init (void) { return syscall (__NR_inotify_init); } 137 | static inline int inotify_add_watch (int fd, const char *name, __u32 mask) { return syscall (__NR_inotify_add_watch, fd, name, mask); } 138 | static inline int inotify_rm_watch (int fd, __u32 wd) { return syscall (__NR_inotify_rm_watch, fd, wd); } 139 | #define HAVE_INOTIFY 1 140 | #endif 141 | 142 | #ifdef HAVE_INOTIFY 143 | #define INOTIFY_EVENT_SIZE (sizeof(struct inotify_event)) 144 | #endif 145 | 146 | #ifdef HAVE_WRITEV 147 | #include 148 | #endif 149 | 150 | #if __cplusplus 151 | extern "C" { 152 | #endif 153 | typedef void (*EMCallback)(const unsigned long, int, const char*, const unsigned long); 154 | #if __cplusplus 155 | } 156 | #endif 157 | 158 | #include "binder.h" 159 | #include "em.h" 160 | #include "ed.h" 161 | #include "page.h" 162 | #include "ssl.h" 163 | #include "eventmachine.h" 164 | 165 | #endif // __Project__H_ 166 | -------------------------------------------------------------------------------- /lib/em/buftok.rb: -------------------------------------------------------------------------------- 1 | # BufferedTokenizer takes a delimiter upon instantiation, or acts line-based 2 | # by default. It allows input to be spoon-fed from some outside source which 3 | # receives arbitrary length datagrams which may-or-may-not contain the token 4 | # by which entities are delimited. 5 | # 6 | # By default, new BufferedTokenizers will operate on lines delimited by "\n" by default 7 | # or allow you to specify any delimiter token you so choose, which will then 8 | # be used by String#split to tokenize the input data 9 | # 10 | # @example Using BufferedTokernizer to parse lines out of incoming data 11 | # 12 | # module LineBufferedConnection 13 | # def receive_data(data) 14 | # (@buffer ||= BufferedTokenizer.new).extract(data).each do |line| 15 | # receive_line(line) 16 | # end 17 | # end 18 | # end 19 | # 20 | # @author Tony Arcieri 21 | # @author Martin Emde 22 | class BufferedTokenizer 23 | # @param [String] delimiter 24 | # @param [Integer] size_limit 25 | def initialize(delimiter = "\n", size_limit = nil) 26 | @delimiter = delimiter 27 | @size_limit = size_limit 28 | 29 | # The input buffer is stored as an array. This is by far the most efficient 30 | # approach given language constraints (in C a linked list would be a more 31 | # appropriate data structure). Segments of input data are stored in a list 32 | # which is only joined when a token is reached, substantially reducing the 33 | # number of objects required for the operation. 34 | @input = [] 35 | 36 | # Size of the input buffer 37 | @input_size = 0 38 | end 39 | 40 | # Extract takes an arbitrary string of input data and returns an array of 41 | # tokenized entities, provided there were any available to extract. 42 | # 43 | # @example 44 | # 45 | # tokenizer.extract(data). 46 | # map { |entity| Decode(entity) }.each { ... } 47 | # 48 | # @param [String] data 49 | def extract(data) 50 | # Extract token-delimited entities from the input string with the split command. 51 | # There's a bit of craftiness here with the -1 parameter. Normally split would 52 | # behave no differently regardless of if the token lies at the very end of the 53 | # input buffer or not (i.e. a literal edge case) Specifying -1 forces split to 54 | # return "" in this case, meaning that the last entry in the list represents a 55 | # new segment of data where the token has not been encountered 56 | entities = data.split @delimiter, -1 57 | 58 | # Check to see if the buffer has exceeded capacity, if we're imposing a limit 59 | if @size_limit 60 | raise 'input buffer full' if @input_size + entities.first.size > @size_limit 61 | @input_size += entities.first.size 62 | end 63 | 64 | # Move the first entry in the resulting array into the input buffer. It represents 65 | # the last segment of a token-delimited entity unless it's the only entry in the list. 66 | @input << entities.shift 67 | 68 | # If the resulting array from the split is empty, the token was not encountered 69 | # (not even at the end of the buffer). Since we've encountered no token-delimited 70 | # entities this go-around, return an empty array. 71 | return [] if entities.empty? 72 | 73 | # At this point, we've hit a token, or potentially multiple tokens. Now we can bring 74 | # together all the data we've buffered from earlier calls without hitting a token, 75 | # and add it to our list of discovered entities. 76 | entities.unshift @input.join 77 | 78 | # Now that we've hit a token, joined the input buffer and added it to the entities 79 | # list, we can go ahead and clear the input buffer. All of the segments that were 80 | # stored before the join can now be garbage collected. 81 | @input.clear 82 | 83 | # The last entity in the list is not token delimited, however, thanks to the -1 84 | # passed to split. It represents the beginning of a new list of as-yet-untokenized 85 | # data, so we add it to the start of the list. 86 | @input << entities.pop 87 | 88 | # Set the new input buffer size, provided we're keeping track 89 | @input_size = @input.first.size if @size_limit 90 | 91 | # Now we're left with the list of extracted token-delimited entities we wanted 92 | # in the first place. Hooray! 93 | entities 94 | end 95 | 96 | # Flush the contents of the input buffer, i.e. return the input buffer even though 97 | # a token has not yet been encountered. 98 | # 99 | # @return [String] 100 | def flush 101 | buffer = @input.join 102 | @input.clear 103 | buffer 104 | end 105 | 106 | # @return [Boolean] 107 | def empty? 108 | @input.empty? 109 | end 110 | end 111 | -------------------------------------------------------------------------------- /lib/em/protocols/header_and_content.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # 3 | # Author:: Francis Cianfrocca (gmail: blackhedd) 4 | # Homepage:: http://rubyeventmachine.com 5 | # Date:: 15 Nov 2006 6 | # 7 | # See EventMachine and EventMachine::Connection for documentation and 8 | # usage examples. 9 | # 10 | #---------------------------------------------------------------------------- 11 | # 12 | # Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved. 13 | # Gmail: blackhedd 14 | # 15 | # This program is free software; you can redistribute it and/or modify 16 | # it under the terms of either: 1) the GNU General Public License 17 | # as published by the Free Software Foundation; either version 2 of the 18 | # License, or (at your option) any later version; or 2) Ruby's License. 19 | # 20 | # See the file COPYING for complete licensing information. 21 | # 22 | #--------------------------------------------------------------------------- 23 | # 24 | # 25 | 26 | module EventMachine 27 | module Protocols 28 | 29 | # === Usage 30 | # 31 | # class RequestHandler < EM::P::HeaderAndContentProtocol 32 | # def receive_request headers, content 33 | # p [:request, headers, content] 34 | # end 35 | # end 36 | # 37 | # EM.run{ 38 | # EM.start_server 'localhost', 80, RequestHandler 39 | # } 40 | # 41 | #-- 42 | # Originally, this subclassed LineAndTextProtocol, which in 43 | # turn relies on BufferedTokenizer, which doesn't gracefully 44 | # handle the transitions between lines and binary text. 45 | # Changed 13Sep08 by FCianfrocca. 46 | class HeaderAndContentProtocol < Connection 47 | include LineText2 48 | 49 | ContentLengthPattern = /Content-length:\s*(\d+)/i 50 | 51 | def initialize *args 52 | super 53 | init_for_request 54 | end 55 | 56 | def receive_line line 57 | case @hc_mode 58 | when :discard_blanks 59 | unless line == "" 60 | @hc_mode = :headers 61 | receive_line line 62 | end 63 | when :headers 64 | if line == "" 65 | raise "unrecognized state" unless @hc_headers.length > 0 66 | if respond_to?(:receive_headers) 67 | receive_headers @hc_headers 68 | end 69 | # @hc_content_length will be nil, not 0, if there was no content-length header. 70 | if @hc_content_length.to_i > 0 71 | set_binary_mode @hc_content_length 72 | else 73 | dispatch_request 74 | end 75 | else 76 | @hc_headers << line 77 | if ContentLengthPattern =~ line 78 | # There are some attacks that rely on sending multiple content-length 79 | # headers. This is a crude protection, but needs to become tunable. 80 | raise "extraneous content-length header" if @hc_content_length 81 | @hc_content_length = $1.to_i 82 | end 83 | if @hc_headers.length == 1 and respond_to?(:receive_first_header_line) 84 | receive_first_header_line line 85 | end 86 | end 87 | else 88 | raise "internal error, unsupported mode" 89 | end 90 | end 91 | 92 | def receive_binary_data text 93 | @hc_content = text 94 | dispatch_request 95 | end 96 | 97 | def dispatch_request 98 | if respond_to?(:receive_request) 99 | receive_request @hc_headers, @hc_content 100 | end 101 | init_for_request 102 | end 103 | private :dispatch_request 104 | 105 | def init_for_request 106 | @hc_mode = :discard_blanks 107 | @hc_headers = [] 108 | # originally was @hc_headers ||= []; @hc_headers.clear to get a performance 109 | # boost, but it's counterproductive because a subclassed handler will have to 110 | # call dup to use the header array we pass in receive_headers. 111 | 112 | @hc_content_length = nil 113 | @hc_content = "" 114 | end 115 | private :init_for_request 116 | 117 | # Basically a convenience method. We might create a subclass that does this 118 | # automatically. But it's such a performance killer. 119 | def headers_2_hash hdrs 120 | self.class.headers_2_hash hdrs 121 | end 122 | 123 | class << self 124 | def headers_2_hash hdrs 125 | hash = {} 126 | hdrs.each {|h| 127 | if /\A([^\s:]+)\s*:\s*/ =~ h 128 | tail = $'.dup 129 | hash[ $1.downcase.gsub(/-/,"_").intern ] = tail 130 | end 131 | } 132 | hash 133 | end 134 | end 135 | 136 | end 137 | end 138 | end 139 | -------------------------------------------------------------------------------- /tests/test_proxy_connection.rb: -------------------------------------------------------------------------------- 1 | require 'em_test_helper' 2 | 3 | class TestProxyConnection < Test::Unit::TestCase 4 | 5 | if EM.respond_to?(:start_proxy) 6 | module ProxyConnection 7 | def initialize(client, request) 8 | @client, @request = client, request 9 | end 10 | 11 | def post_init 12 | EM::enable_proxy(self, @client) 13 | end 14 | 15 | def connection_completed 16 | EM.next_tick { 17 | send_data @request 18 | } 19 | end 20 | 21 | def proxy_target_unbound 22 | $unbound_early = true 23 | EM.stop 24 | end 25 | 26 | def unbind 27 | $proxied_bytes = self.get_proxied_bytes 28 | @client.close_connection_after_writing 29 | end 30 | end 31 | 32 | module PartialProxyConnection 33 | def initialize(client, request, length) 34 | @client, @request, @length = client, request, length 35 | end 36 | 37 | def post_init 38 | EM::enable_proxy(self, @client, 0, @length) 39 | end 40 | 41 | def receive_data(data) 42 | $unproxied_data = data 43 | @client.send_data(data) 44 | end 45 | 46 | def connection_completed 47 | EM.next_tick { 48 | send_data @request 49 | } 50 | end 51 | 52 | def proxy_target_unbound 53 | $unbound_early = true 54 | EM.stop 55 | end 56 | 57 | def proxy_completed 58 | $proxy_completed = true 59 | end 60 | 61 | def unbind 62 | @client.close_connection_after_writing 63 | end 64 | end 65 | 66 | module Client 67 | def connection_completed 68 | send_data "EM rocks!" 69 | end 70 | 71 | def receive_data(data) 72 | $client_data = data 73 | end 74 | 75 | def unbind 76 | EM.stop 77 | end 78 | end 79 | 80 | module Client2 81 | include Client 82 | def unbind; end 83 | end 84 | 85 | module Server 86 | def receive_data(data) 87 | send_data "I know!" if data == "EM rocks!" 88 | close_connection_after_writing 89 | end 90 | end 91 | 92 | module ProxyServer 93 | def initialize port 94 | @port = port 95 | end 96 | 97 | def receive_data(data) 98 | @proxy = EM.connect("127.0.0.1", @port, ProxyConnection, self, data) 99 | end 100 | end 101 | 102 | module PartialProxyServer 103 | def initialize port 104 | @port = port 105 | end 106 | 107 | def receive_data(data) 108 | EM.connect("127.0.0.1", @port, PartialProxyConnection, self, data, 1) 109 | end 110 | end 111 | 112 | module EarlyClosingProxy 113 | def initialize port 114 | @port = port 115 | end 116 | 117 | def receive_data(data) 118 | EM.connect("127.0.0.1", @port, ProxyConnection, self, data) 119 | close_connection 120 | end 121 | end 122 | 123 | def setup 124 | @port = next_port 125 | @proxy_port = next_port 126 | end 127 | 128 | def test_proxy_connection 129 | EM.run { 130 | EM.start_server("127.0.0.1", @port, Server) 131 | EM.start_server("127.0.0.1", @proxy_port, ProxyServer, @port) 132 | EM.connect("127.0.0.1", @proxy_port, Client) 133 | } 134 | 135 | assert_equal("I know!", $client_data) 136 | end 137 | 138 | def test_proxied_bytes 139 | EM.run { 140 | EM.start_server("127.0.0.1", @port, Server) 141 | EM.start_server("127.0.0.1", @proxy_port, ProxyServer, @port) 142 | EM.connect("127.0.0.1", @proxy_port, Client) 143 | } 144 | 145 | assert_equal("I know!", $client_data) 146 | assert_equal("I know!".bytesize, $proxied_bytes) 147 | end 148 | 149 | def test_partial_proxy_connection 150 | EM.run { 151 | EM.start_server("127.0.0.1", @port, Server) 152 | EM.start_server("127.0.0.1", @proxy_port, PartialProxyServer, @port) 153 | EM.connect("127.0.0.1", @proxy_port, Client) 154 | } 155 | 156 | assert_equal("I know!", $client_data) 157 | assert_equal(" know!", $unproxied_data) 158 | assert($proxy_completed) 159 | end 160 | 161 | def test_early_close 162 | $client_data = nil 163 | EM.run { 164 | EM.start_server("127.0.0.1", @port, Server) 165 | EM.start_server("127.0.0.1", @proxy_port, EarlyClosingProxy, @port) 166 | EM.connect("127.0.0.1", @proxy_port, Client2) 167 | } 168 | 169 | assert($unbound_early) 170 | end 171 | else 172 | warn "EM.start_proxy not implemented, skipping tests in #{__FILE__}" 173 | 174 | # Because some rubies will complain if a TestCase class has no tests 175 | def test_em_start_proxy_not_implemented 176 | assert !EM.respond_to?(:start_proxy) 177 | end 178 | end 179 | 180 | end 181 | --------------------------------------------------------------------------------