├── examples ├── ex_queue.rb ├── helper.rb └── ex_channel.rb ├── lib ├── em │ ├── version.rb │ ├── callback.rb │ ├── protocols │ │ ├── object_protocol.rb │ │ ├── tcptest.rb │ │ └── header_and_content.rb │ ├── process_watch.rb │ ├── timers.rb │ ├── protocols.rb │ ├── channel.rb │ ├── test.rb │ ├── queue.rb │ ├── file_watch.rb │ ├── future.rb │ ├── server.rb │ ├── spawnable.rb │ ├── delegate_connection.rb │ ├── messages.rb │ ├── processes.rb │ └── streamer.rb ├── evma.rb └── evma │ ├── callback.rb │ ├── reactor.rb │ ├── protocol.rb │ ├── container.rb │ └── factory.rb ├── .gitignore ├── docs ├── SMTP ├── TODO ├── INSTALL ├── LEGAL ├── KEYBOARD ├── COPYING ├── RELEASE_NOTES └── PURE_RUBY ├── java ├── .classpath ├── .project └── src │ └── com │ └── rubyeventmachine │ ├── ConnectionFactory.java │ ├── PeriodicTimer.java │ ├── EmReactorException.java │ ├── tests │ ├── TestDatagrams.java │ ├── TestServers.java │ ├── EMTest.java │ ├── TestTimers.java │ ├── ApplicationTest.java │ └── ConnectTest.java │ ├── DefaultConnectionFactory.java │ ├── Timer.java │ ├── Connection.java │ └── EventableChannel.java ├── web └── whatis ├── tests ├── testem.rb ├── test_get_sock_opt.rb ├── test_error_handler.rb ├── test_handler_check.rb ├── test_object_protocol.rb ├── test_connection_count.rb ├── test_queue.rb ├── test_file_watch.rb ├── test_ud.rb ├── test_process_watch.rb ├── test_pending_connect_timeout.rb ├── test_ssl_methods.rb ├── test_inactivity_timeout.rb ├── test_running.rb ├── test_defer.rb ├── test_channel.rb ├── test_exc.rb ├── test_pause.rb ├── test_kb.rb ├── client.crt ├── test_sasl.rb ├── test_proxy_connection.rb ├── test_ssl_verify.rb ├── test_errors.rb ├── test_ssl_args.rb ├── test_smtpclient.rb ├── test_servers.rb ├── test_smtpserver.rb ├── test_server.rb ├── test_delegate_connection.rb ├── test_attach.rb ├── client.key ├── test_processes.rb ├── test_next_tick.rb ├── test_pure.rb ├── test_timers.rb └── test_httpclient2.rb ├── ext ├── epoll.h ├── epoll.cpp ├── sigs.h ├── binder.h ├── page.h ├── fastfilereader │ ├── mapper.h │ ├── extconf.rb │ └── rubymain.cpp ├── files.h ├── kb.cpp ├── sigs.cpp ├── ssl.h ├── page.cpp ├── files.cpp ├── emwin.h ├── eventmachine_cpp.h ├── binder.cpp └── project.h ├── tasks ├── cpp.rake └── project.rake └── README /examples/ex_queue.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/helper' 2 | 3 | -------------------------------------------------------------------------------- /lib/em/version.rb: -------------------------------------------------------------------------------- 1 | module EventMachine 2 | VERSION = "0.12.9" 3 | end 4 | -------------------------------------------------------------------------------- /examples/helper.rb: -------------------------------------------------------------------------------- 1 | $:.unshift File.expand_path(File.dirname(__FILE__) + '/../lib') 2 | require 'eventmachine' -------------------------------------------------------------------------------- /.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 | java/src/.project 15 | -------------------------------------------------------------------------------- /docs/SMTP: -------------------------------------------------------------------------------- 1 | This note details the usage of EventMachine's built-in support for SMTP. EM supports both client and server connections, which will be described in separate sections. 2 | 3 | -------------------------------------------------------------------------------- /docs/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 | -------------------------------------------------------------------------------- /java/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /docs/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 | -------------------------------------------------------------------------------- /web/whatis: -------------------------------------------------------------------------------- 1 | EventMachine is a library for Ruby, C++, and Java programs. It provides event-driven I/O using the Reactor pattern. 2 | 3 | EventMachine is designed to simultaneously meet two key needs: 4 | - Extremely high scalability, performance and stability for the most demanding production environments; and 5 | - An API that eliminates the complexities of high-performance threaded network programming, allowing engineers to concentrate on their application logic. 6 | 7 | This unique combination makes EventMachine a premier choice for designers of critical networked applications, including web servers and proxies, email and IM production systems, authentication/authorization processors, and many more. 8 | -------------------------------------------------------------------------------- /tests/testem.rb: -------------------------------------------------------------------------------- 1 | # $Id$ 2 | 3 | require 'test/unit' 4 | 5 | module EmTestRunner 6 | @em_root = File.expand_path(File.dirname(__FILE__) + '/../') 7 | @lib_dir = File.join(@em_root, 'lib') 8 | @ext_dir = File.join(@em_root, 'ext') 9 | @java_dir = File.join(@em_root, 'java') 10 | 11 | def self.run(glob = 'test_*.rb') 12 | $:.unshift(@lib_dir) 13 | $:.unshift(@ext_dir) 14 | $:.unshift(@java_dir) 15 | 16 | case glob 17 | when Array 18 | files = glob 19 | else 20 | files = Dir[File.dirname(__FILE__) + '/' + glob] 21 | end 22 | 23 | files.each do |tc| 24 | require tc 25 | end 26 | end 27 | end 28 | 29 | if __FILE__ == $0 30 | EmTestRunner.run 31 | end 32 | -------------------------------------------------------------------------------- /ext/epoll.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | 3 | $Id$ 4 | 5 | File: epoll.h 6 | Date: 06Jun07 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 | #ifdef HAVE_EPOLL 22 | 23 | 24 | #endif // HAVE_EPOLL 25 | 26 | -------------------------------------------------------------------------------- /tests/test_get_sock_opt.rb: -------------------------------------------------------------------------------- 1 | $:.unshift File.expand_path(File.dirname(__FILE__) + "/../lib") 2 | require 'eventmachine' 3 | require 'socket' 4 | require 'test/unit' 5 | 6 | class TestGetSockOpt < Test::Unit::TestCase 7 | 8 | def setup 9 | assert(!EM.reactor_running?) 10 | end 11 | 12 | def teardown 13 | assert(!EM.reactor_running?) 14 | end 15 | 16 | #------------------------------------- 17 | 18 | def test_get_sock_opt 19 | test = self 20 | EM.run do 21 | EM.connect 'google.com', 80, Module.new { 22 | define_method :connection_completed do 23 | val = get_sock_opt Socket::SOL_SOCKET, Socket::SO_ERROR 24 | test.assert_equal "\0\0\0\0", val 25 | EM.stop 26 | end 27 | } 28 | end 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /tests/test_error_handler.rb: -------------------------------------------------------------------------------- 1 | $:.unshift "../lib" 2 | require 'eventmachine' 3 | require 'test/unit' 4 | 5 | class TestErrorHandler < Test::Unit::TestCase 6 | def test_error_handler 7 | error = nil 8 | EM.error_handler{ |e| 9 | error = e 10 | EM.error_handler(nil) 11 | EM.stop 12 | } 13 | 14 | assert_nothing_raised do 15 | EM.run{ 16 | EM.add_timer(0){ 17 | raise 'test' 18 | } 19 | } 20 | end 21 | 22 | assert_equal error.class, RuntimeError 23 | assert_equal error.message, 'test' 24 | end 25 | 26 | def test_without_error_handler 27 | assert_raise RuntimeError do 28 | EM.run{ 29 | EM.add_timer(0){ 30 | raise 'test' 31 | } 32 | } 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /ext/epoll.cpp: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | 3 | $Id$ 4 | 5 | File: epoll.cpp 6 | Date: 06Jun07 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 | #ifdef HAVE_EPOLL 22 | 23 | #include "project.h" 24 | 25 | #endif // HAVE_EPOLL 26 | 27 | -------------------------------------------------------------------------------- /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 | # cb = EM.Callback{ |msg| puts(msg) } 7 | # cb.call('hello world') 8 | # 9 | # cb = EM.Callback(Object, :puts) 10 | # cb.call('hello world') 11 | # 12 | # cb = EM.Callback(proc{ |msg| puts(msg) }) 13 | # cb.call('hello world') 14 | # 15 | def self.Callback(object = nil, method = nil, &blk) 16 | if object && method 17 | lambda { |*args| object.send method, *args } 18 | else 19 | if object.respond_to? :call 20 | object 21 | else 22 | blk || raise(ArgumentError) 23 | end 24 | end 25 | end 26 | end -------------------------------------------------------------------------------- /tests/test_handler_check.rb: -------------------------------------------------------------------------------- 1 | $:.unshift "../lib" 2 | require 'eventmachine' 3 | require 'test/unit' 4 | 5 | class TestHandlerCheck < Test::Unit::TestCase 6 | 7 | class Foo < EM::Connection; end; 8 | module TestModule; end; 9 | 10 | def test_with_correct_class 11 | assert_nothing_raised do 12 | EM.run { 13 | EM.connect("127.0.0.1", 80, Foo) 14 | EM.stop_event_loop 15 | } 16 | end 17 | end 18 | 19 | def test_with_incorrect_class 20 | assert_raise(ArgumentError) do 21 | EM.run { 22 | EM.connect("127.0.0.1", 80, String) 23 | EM.stop_event_loop 24 | } 25 | end 26 | end 27 | 28 | def test_with_module 29 | assert_nothing_raised do 30 | EM.run { 31 | EM.connect("127.0.0.1", 80, TestModule) 32 | EM.stop_event_loop 33 | } 34 | end 35 | end 36 | 37 | end -------------------------------------------------------------------------------- /tests/test_object_protocol.rb: -------------------------------------------------------------------------------- 1 | $:.unshift "../lib" 2 | require 'eventmachine' 3 | require 'test/unit' 4 | 5 | class TestObjectProtocol < Test::Unit::TestCase 6 | Host = "127.0.0.1" 7 | Port = 9550 8 | 9 | module Server 10 | include EM::P::ObjectProtocol 11 | def post_init 12 | send_object :hello=>'world' 13 | end 14 | def receive_object obj 15 | $server = obj 16 | EM.stop 17 | end 18 | end 19 | 20 | module Client 21 | include EM::P::ObjectProtocol 22 | def receive_object obj 23 | $client = obj 24 | send_object 'you_said'=>obj 25 | end 26 | end 27 | 28 | def test_send_receive 29 | EM.run{ 30 | EM.start_server Host, Port, Server 31 | EM.connect Host, Port, Client 32 | } 33 | 34 | assert($client == {:hello=>'world'}) 35 | assert($server == {'you_said'=>{:hello=>'world'}}) 36 | end 37 | end -------------------------------------------------------------------------------- /docs/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 | -------------------------------------------------------------------------------- /tests/test_connection_count.rb: -------------------------------------------------------------------------------- 1 | $:.unshift "../lib" 2 | require 'eventmachine' 3 | require 'test/unit' 4 | 5 | class TestConnectionCount < Test::Unit::TestCase 6 | def test_idle_connection_count 7 | EM.run { 8 | $count = EM.connection_count 9 | EM.stop_event_loop 10 | } 11 | 12 | assert_equal(0, $count) 13 | end 14 | 15 | module Client 16 | def connection_completed 17 | $client_conns += 1 18 | EM.stop if $client_conns == 3 19 | end 20 | end 21 | 22 | def test_with_some_connections 23 | EM.run { 24 | $client_conns = 0 25 | $initial_conns = EM.connection_count 26 | EM.start_server("127.0.0.1", 9999) 27 | $server_conns = EM.connection_count 28 | 3.times { EM.connect("127.0.0.1", 9999, Client) } 29 | } 30 | 31 | assert_equal(0, $initial_conns) 32 | assert_equal(1, $server_conns) 33 | assert_equal(4, $client_conns + $server_conns) 34 | end 35 | end -------------------------------------------------------------------------------- /ext/sigs.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | 3 | $Id$ 4 | 5 | File: sigs.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 __Signals__H_ 22 | #define __Signals__H_ 23 | 24 | void InstallSignalHandlers(); 25 | extern bool gTerminateSignalReceived; 26 | 27 | #ifdef OS_WIN32 28 | void HookControlC (bool); 29 | #endif 30 | 31 | #endif // __Signals__H_ 32 | 33 | -------------------------------------------------------------------------------- /lib/evma.rb: -------------------------------------------------------------------------------- 1 | #-- 2 | # 3 | # Author:: Francis Cianfrocca (gmail: blackhedd) 4 | # Homepage:: http://rubyeventmachine.com 5 | # Date:: 8 Apr 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 | require 'rubyeventmachine' 27 | require 'evma/reactor' 28 | require 'evma/callback' 29 | require 'evma/protocol' 30 | require 'evma/factory' 31 | require 'evma/container' 32 | 33 | -------------------------------------------------------------------------------- /tests/test_queue.rb: -------------------------------------------------------------------------------- 1 | $:.unshift "../lib" 2 | require 'eventmachine' 3 | require 'test/unit' 4 | 5 | class TestEventMachineQueue < Test::Unit::TestCase 6 | def test_queue_push 7 | s = 0 8 | EM.run do 9 | q = EM::Queue.new 10 | q.push(1) 11 | EM.next_tick { s = q.size; EM.stop } 12 | end 13 | assert_equal 1, s 14 | end 15 | 16 | def test_queue_pop 17 | x,y,z = nil 18 | EM.run do 19 | q = EM::Queue.new 20 | q.push(1,2,3) 21 | q.pop { |v| x = v } 22 | q.pop { |v| y = v } 23 | q.pop { |v| z = v; EM.stop } 24 | end 25 | assert_equal 1, x 26 | assert_equal 2, y 27 | assert_equal 3, z 28 | end 29 | 30 | def test_queue_reactor_thread 31 | q = EM::Queue.new 32 | 33 | Thread.new { q.push(1,2,3) }.join 34 | assert q.empty? 35 | EM.run { EM.next_tick { EM.stop } } 36 | assert_equal 3, q.size 37 | 38 | x = nil 39 | Thread.new { q.pop { |v| x = v } }.join 40 | assert_equal nil, x 41 | EM.run { EM.next_tick { EM.stop } } 42 | assert_equal 1, x 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /lib/evma/callback.rb: -------------------------------------------------------------------------------- 1 | # $Id$ 2 | # 3 | # Author:: Francis Cianfrocca (gmail: blackhedd) 4 | # Homepage:: http://rubyeventmachine.com 5 | # Date:: 19 May 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 | 28 | def self.event_callback target, opcode, data 29 | Evma::Container.callback target, opcode, data 30 | end 31 | 32 | end # module EventMachine 33 | -------------------------------------------------------------------------------- /tests/test_file_watch.rb: -------------------------------------------------------------------------------- 1 | $:.unshift "../lib" 2 | require 'eventmachine' 3 | require 'test/unit' 4 | 5 | class TestFileWatch < Test::Unit::TestCase 6 | module FileWatcher 7 | def file_modified 8 | $modified = true 9 | end 10 | def file_deleted 11 | $deleted = true 12 | end 13 | def unbind 14 | $unbind = true 15 | EM.stop 16 | end 17 | end 18 | 19 | def setup 20 | EM.kqueue = true if EM.kqueue? 21 | end 22 | 23 | def teardown 24 | EM.kqueue = false if EM.kqueue? 25 | end 26 | 27 | def test_events 28 | EM.run{ 29 | require 'tempfile' 30 | file = Tempfile.new('em-watch') 31 | $tmp_path = file.path 32 | 33 | # watch it 34 | watch = EM.watch_file(file.path, FileWatcher) 35 | $path = watch.path 36 | 37 | # modify it 38 | File.open(file.path, 'w'){ |f| f.puts 'hi' } 39 | 40 | # delete it 41 | EM.add_timer(0.25){ file.close; file.delete } 42 | } 43 | 44 | assert_equal($path, $tmp_path) 45 | assert($modified) 46 | assert($deleted) 47 | assert($unbind) 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /tests/test_ud.rb: -------------------------------------------------------------------------------- 1 | # $Id$ 2 | # 3 | # Author:: Francis Cianfrocca (gmail: blackhedd) 4 | # Homepage:: http://rubyeventmachine.com 5 | # Date:: 8 April 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 | $:.unshift "../lib" 28 | require 'eventmachine' 29 | require 'test/unit' 30 | 31 | class TestUserDefinedEvents < Test::Unit::TestCase 32 | 33 | def test_a 34 | end 35 | 36 | end 37 | -------------------------------------------------------------------------------- /tests/test_process_watch.rb: -------------------------------------------------------------------------------- 1 | $:.unshift "../lib" 2 | require 'eventmachine' 3 | require 'test/unit' 4 | 5 | class TestProcessWatch < Test::Unit::TestCase 6 | module ParentProcessWatcher 7 | def process_forked 8 | $forked = true 9 | end 10 | end 11 | 12 | module ChildProcessWatcher 13 | def process_exited 14 | $exited = true 15 | end 16 | def unbind 17 | $unbind = true 18 | EM.stop 19 | end 20 | end 21 | 22 | def setup 23 | EM.kqueue = true if EM.kqueue? 24 | end 25 | 26 | def teardown 27 | EM.kqueue = false if EM.kqueue? 28 | end 29 | 30 | def test_events 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.5){ 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 -------------------------------------------------------------------------------- /tests/test_pending_connect_timeout.rb: -------------------------------------------------------------------------------- 1 | $:.unshift "../lib" 2 | require 'eventmachine' 3 | require 'test/unit' 4 | 5 | class TestPendingConnectTimeout < Test::Unit::TestCase 6 | 7 | def test_default 8 | $timeout = nil 9 | EM.run { 10 | c = EM.connect("127.0.0.1", 54321) 11 | $timeout = c.pending_connect_timeout 12 | EM.stop 13 | } 14 | 15 | assert_equal(20.0, $timeout) 16 | end 17 | 18 | def test_set_and_get 19 | $timeout = nil 20 | EM.run { 21 | c = EM.connect("1.2.3.4", 54321) 22 | c.pending_connect_timeout = 2.5 23 | $timeout = c.pending_connect_timeout 24 | EM.stop 25 | } 26 | 27 | assert_equal(2.5, $timeout) 28 | end 29 | 30 | module TimeoutHandler 31 | def unbind 32 | EM.stop 33 | end 34 | end 35 | 36 | def test_for_real 37 | $timeout = nil 38 | EM.run { 39 | EM.heartbeat_interval = 0.1 40 | $start = Time.now 41 | c = EM.connect("1.2.3.4", 54321, TimeoutHandler) 42 | c.pending_connect_timeout = 5 43 | } 44 | 45 | assert_in_delta(5, (Time.now - $start), 0.3) 46 | end 47 | 48 | end 49 | -------------------------------------------------------------------------------- /java/src/com/rubyeventmachine/ConnectionFactory.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 | 31 | package com.rubyeventmachine; 32 | 33 | //import com.rubyeventmachine.*; 34 | 35 | public interface ConnectionFactory { 36 | public Connection connection(); 37 | } -------------------------------------------------------------------------------- /tests/test_ssl_methods.rb: -------------------------------------------------------------------------------- 1 | $:.unshift "../lib" 2 | require 'eventmachine' 3 | require 'test/unit' 4 | 5 | class TestSSLMethods < Test::Unit::TestCase 6 | 7 | module ServerHandler 8 | 9 | def post_init 10 | start_tls 11 | end 12 | 13 | def ssl_handshake_completed 14 | $server_called_back = true 15 | $server_cert_value = get_peer_cert 16 | end 17 | 18 | end 19 | 20 | module ClientHandler 21 | 22 | def post_init 23 | start_tls 24 | end 25 | 26 | def ssl_handshake_completed 27 | $client_called_back = true 28 | $client_cert_value = get_peer_cert 29 | EM.stop_event_loop 30 | end 31 | 32 | end 33 | 34 | def test_ssl_methods 35 | $server_called_back, $client_called_back = false, false 36 | $server_cert_value, $client_cert_value = nil, nil 37 | 38 | EM.run { 39 | EM.start_server("127.0.0.1", 9999, ServerHandler) 40 | EM.connect("127.0.0.1", 9999, ClientHandler) 41 | } 42 | 43 | assert($server_called_back) 44 | assert($client_called_back) 45 | 46 | assert($server_cert_value.is_a?(NilClass)) 47 | assert($client_cert_value.is_a?(String)) 48 | end 49 | 50 | end if EM.ssl? -------------------------------------------------------------------------------- /tests/test_inactivity_timeout.rb: -------------------------------------------------------------------------------- 1 | $:.unshift "../lib" 2 | require 'eventmachine' 3 | require 'test/unit' 4 | 5 | class TestInactivityTimeout < Test::Unit::TestCase 6 | 7 | def test_default 8 | $timeout = nil 9 | EM.run { 10 | c = EM.connect("127.0.0.1", 54321) 11 | $timeout = c.comm_inactivity_timeout 12 | EM.stop 13 | } 14 | 15 | assert_equal(0.0, $timeout) 16 | end 17 | 18 | def test_set_and_get 19 | $timeout = nil 20 | EM.run { 21 | c = EM.connect("127.0.0.1", 54321) 22 | c.comm_inactivity_timeout = 2.5 23 | $timeout = c.comm_inactivity_timeout 24 | EM.stop 25 | } 26 | 27 | assert_equal(2.5, $timeout) 28 | end 29 | 30 | module TimeoutHandler 31 | def unbind 32 | EM.stop 33 | end 34 | end 35 | 36 | def test_for_real 37 | EM.run { 38 | EM.heartbeat_interval = 0.1 39 | EM.start_server("127.0.0.1", 12345) 40 | EM.add_timer(0.2) { 41 | $start = Time.now 42 | c = EM.connect("127.0.0.1", 12345, TimeoutHandler) 43 | c.comm_inactivity_timeout = 2.5 44 | } 45 | } 46 | 47 | assert_in_delta(2.5, (Time.now - $start), 0.3) 48 | end 49 | 50 | end 51 | -------------------------------------------------------------------------------- /java/src/com/rubyeventmachine/PeriodicTimer.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 | public class PeriodicTimer extends Timer { 33 | 34 | public void _fire() { 35 | fire(); 36 | application.addTimer(interval, this); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/evma/reactor.rb: -------------------------------------------------------------------------------- 1 | # $Id$ 2 | # 3 | # Author:: Francis Cianfrocca (gmail: blackhedd) 4 | # Homepage:: http://rubyeventmachine.com 5 | # Date:: 18 May 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 | require 'singleton' 28 | 29 | module Evma 30 | class Reactor 31 | include Singleton 32 | 33 | #-- 34 | def initialize 35 | EventMachine.initialize_event_machine 36 | end 37 | 38 | #-- 39 | # 40 | def run 41 | EventMachine.run_machine 42 | end 43 | 44 | end # class Reactor 45 | end # module Evma 46 | 47 | 48 | 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 | -------------------------------------------------------------------------------- /tests/test_running.rb: -------------------------------------------------------------------------------- 1 | # $Id$ 2 | # 3 | # Author:: Francis Cianfrocca (gmail: blackhedd) 4 | # Homepage:: http://rubyeventmachine.com 5 | # Date:: 8 April 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 | $:.unshift "../lib" 28 | require 'eventmachine' 29 | require 'test/unit' 30 | 31 | class TestRunning < Test::Unit::TestCase 32 | def test_running 33 | assert_equal( false, EM::reactor_running? ) 34 | r = false 35 | EM.run { 36 | r = EM::reactor_running? 37 | EM.stop 38 | } 39 | assert_equal( true, r ) 40 | end 41 | end 42 | 43 | -------------------------------------------------------------------------------- /examples/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 -------------------------------------------------------------------------------- /java/src/com/rubyeventmachine/tests/TestDatagrams.java: -------------------------------------------------------------------------------- 1 | package com.rubyeventmachine.tests; 2 | 3 | import com.rubyeventmachine.*; 4 | import java.net.*; 5 | import java.nio.*; 6 | 7 | import org.junit.After; 8 | import org.junit.AfterClass; 9 | import org.junit.Before; 10 | import org.junit.BeforeClass; 11 | import org.junit.Test; 12 | 13 | 14 | public class TestDatagrams { 15 | 16 | @BeforeClass 17 | public static void setUpBeforeClass() throws Exception { 18 | } 19 | 20 | @AfterClass 21 | public static void tearDownAfterClass() throws Exception { 22 | } 23 | 24 | @Before 25 | public void setUp() throws Exception { 26 | } 27 | 28 | @After 29 | public void tearDown() throws Exception { 30 | } 31 | 32 | class A extends Connection { 33 | public void receiveData (ByteBuffer bb) { 34 | application.stop(); 35 | } 36 | } 37 | class B extends Connection { 38 | public void postInit() { 39 | this.sendDatagram(ByteBuffer.wrap(new String("ABC").getBytes()), new InetSocketAddress ("127.0.0.1", 9550)); 40 | } 41 | 42 | } 43 | @Test 44 | public final void testA() { 45 | final Application a = new Application(); 46 | a.run (new Runnable() { 47 | public void run() { 48 | a.openDatagramSocket( new InetSocketAddress ("0.0.0.0", 9550), new A() ); 49 | a.openDatagramSocket( new B() ); 50 | } 51 | }); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /docs/KEYBOARD: -------------------------------------------------------------------------------- 1 | EventMachine (EM) can respond to keyboard events. This gives your event-driven programs the ability to respond to input from local users. 2 | 3 | Programming EM to handle keyboard input in Ruby is simplicity itself. Just use EventMachine#open_keyboard, and supply the name of a Ruby module or class that will receive the input: 4 | 5 | require 'rubygems' 6 | require 'eventmachine' 7 | 8 | module MyKeyboardHandler 9 | def receive_data keystrokes 10 | puts "I received the following data from the keyboard: #{keystrokes}" 11 | end 12 | end 13 | 14 | EM.run { 15 | EM.open_keyboard(MyKeyboardHandler) 16 | } 17 | 18 | 19 | If you want EM to send line-buffered keyboard input to your program, just include the LineText2 protocol module in your handler class or module: 20 | 21 | 22 | 23 | require 'rubygems' 24 | require 'eventmachine' 25 | 26 | module MyKeyboardHandler 27 | include EM::Protocols::LineText2 28 | def receive_line data 29 | puts "I received the following line from the keyboard: #{data}" 30 | end 31 | end 32 | 33 | EM.run { 34 | EM.open_keyboard(MyKeyboardHandler) 35 | } 36 | 37 | As we said, simplicity itself. You can call EventMachine#open_keyboard at any time while the EM reactor loop is running. In other words, the method invocation may appear anywhere in an EventMachine#run block, or in any code invoked in the #run block. 38 | 39 | -------------------------------------------------------------------------------- /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 | def receive_data data # :nodoc: 21 | (@buf ||= '') << data 22 | 23 | while @buf.size >= 4 24 | if @buf.size >= 4+(size=@buf.unpack('N').first) 25 | @buf.slice!(0,4) 26 | receive_object serializer.load(@buf.slice!(0,size)) 27 | else 28 | break 29 | end 30 | end 31 | end 32 | 33 | # Invoked with ruby objects received over the network 34 | def receive_object obj 35 | # stub 36 | end 37 | 38 | # Sends a ruby object over the network 39 | def send_object obj 40 | data = serializer.dump(obj) 41 | send_data [data.respond_to?(:bytesize) ? data.bytesize : data.size, data].pack('Na*') 42 | end 43 | end 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /tests/test_defer.rb: -------------------------------------------------------------------------------- 1 | # $Id$ 2 | # 3 | # Author:: Francis Cianfrocca (gmail: blackhedd) 4 | # Homepage:: http://rubyeventmachine.com 5 | # Date:: 8 April 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 | $:.unshift "../lib" 28 | require 'eventmachine' 29 | require 'test/unit' 30 | 31 | class TestDeferUsage < Test::Unit::TestCase 32 | 33 | def test_defers 34 | n = 0 35 | n_times = 20 36 | EM.run { 37 | n_times.times { 38 | work_proc = proc { n += 1 } 39 | callback = proc { EM.stop if n == n_times } 40 | EM.defer work_proc, callback 41 | } 42 | } 43 | assert_equal( n, n_times ) 44 | end unless RUBY_VERSION >= '1.9.0' 45 | 46 | end 47 | 48 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | # :stopdoc: 7 | Cfork = 'fork'.freeze 8 | Cexit = 'exit'.freeze 9 | # :startdoc: 10 | 11 | def receive_data(data) # :nodoc: 12 | case data 13 | when Cfork 14 | process_forked 15 | when Cexit 16 | process_exited 17 | end 18 | end 19 | 20 | # Returns the pid that EventMachine::watch_process was originally called with. 21 | def pid 22 | @pid 23 | end 24 | 25 | # Should be redefined with the user's custom callback that will be fired when the prcess is forked. 26 | # 27 | # There is currently not an easy way to get the pid of the forked child. 28 | def process_forked 29 | end 30 | 31 | # Should be redefined with the user's custom callback that will be fired when the process exits. 32 | # 33 | # stop_watching is called automatically after this callback 34 | def process_exited 35 | end 36 | 37 | # Discontinue monitoring of the process. 38 | # This will be called automatically when a process dies. User code may call it as well. 39 | def stop_watching 40 | EventMachine::unwatch_pid(@signature) 41 | end 42 | end 43 | 44 | end -------------------------------------------------------------------------------- /java/src/com/rubyeventmachine/DefaultConnectionFactory.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 | 31 | package com.rubyeventmachine; 32 | 33 | import com.rubyeventmachine.ConnectionFactory; 34 | 35 | public class DefaultConnectionFactory implements ConnectionFactory { 36 | 37 | /** 38 | * Convenience class. Its connection() method returns an instance of class 39 | * Connection, which is usually overridden. This class is probably most 40 | * useful for unit testing. 41 | */ 42 | public Connection connection() { 43 | return new Connection(); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /tests/test_channel.rb: -------------------------------------------------------------------------------- 1 | $:.unshift "../lib" 2 | require 'eventmachine' 3 | require 'test/unit' 4 | 5 | class TestEventMachineChannel < Test::Unit::TestCase 6 | def test_channel_subscribe 7 | s = 0 8 | EM.run do 9 | c = EM::Channel.new 10 | c.subscribe { |v| s = v; EM.stop } 11 | c << 1 12 | end 13 | assert_equal 1, s 14 | end 15 | 16 | def test_channel_unsubscribe 17 | s = 0 18 | EM.run do 19 | c = EM::Channel.new 20 | subscription = c.subscribe { |v| s = v } 21 | c.unsubscribe(subscription) 22 | c << 1 23 | EM.next_tick { EM.stop } 24 | end 25 | assert_not_equal 1, s 26 | end 27 | 28 | def test_channel_pop 29 | s = 0 30 | EM.run do 31 | c = EM::Channel.new 32 | c.pop{ |v| s = v } 33 | c << 1 34 | c << 2 35 | EM.next_tick { EM.stop } 36 | end 37 | assert_equal 1, s 38 | end 39 | 40 | def test_channel_reactor_thread_push 41 | out = [] 42 | c = EM::Channel.new 43 | c.subscribe { |v| out << v } 44 | Thread.new { c.push(1,2,3) }.join 45 | assert out.empty? 46 | 47 | EM.run { EM.next_tick { EM.stop } } 48 | 49 | assert_equal [1,2,3], out 50 | end 51 | 52 | def test_channel_reactor_thread_callback 53 | out = [] 54 | c = EM::Channel.new 55 | Thread.new { c.subscribe { |v| out << v } }.join 56 | c.push(1,2,3) 57 | assert out.empty? 58 | 59 | EM.run { EM.next_tick { EM.stop } } 60 | 61 | assert_equal [1,2,3], out 62 | end 63 | end -------------------------------------------------------------------------------- /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 | # n = 0 24 | # timer = EventMachine::PeriodicTimer.new(5) do 25 | # puts "the time is #{Time.now}" 26 | # timer.cancel if (n+=1) > 5 27 | # end 28 | # 29 | class PeriodicTimer 30 | # Create a new periodic timer that executes every interval seconds 31 | def initialize interval, callback=nil, &block 32 | @interval = interval 33 | @code = callback || block 34 | @cancelled = false 35 | schedule 36 | end 37 | 38 | # Cancel the periodic timer 39 | def cancel 40 | @cancelled = true 41 | end 42 | 43 | # Fire the timer every interval seconds 44 | attr_accessor :interval 45 | 46 | def schedule # :nodoc: 47 | EventMachine::add_timer @interval, proc {self.fire} 48 | end 49 | def fire # :nodoc: 50 | unless @cancelled 51 | @code.call 52 | schedule 53 | end 54 | end 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /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 | # - 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 | end 35 | end 36 | -------------------------------------------------------------------------------- /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 | class TcpConnectTester < Connection # :nodoc: 31 | include EventMachine::Deferrable 32 | 33 | def self.test( host, port ) 34 | EventMachine.connect( host, port, self ) 35 | end 36 | 37 | def post_init 38 | @start_time = Time.now 39 | end 40 | 41 | def connection_completed 42 | @completed = true 43 | set_deferred_status :succeeded, (Time.now - @start_time) 44 | close_connection 45 | end 46 | 47 | def unbind 48 | set_deferred_status :failed, (Time.now - @start_time) unless @completed 49 | end 50 | end 51 | 52 | end 53 | end -------------------------------------------------------------------------------- /java/src/com/rubyeventmachine/Timer.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 | public class Timer { 33 | /** 34 | * User code is expected to call a method on a controlling Application, 35 | * which will fill in this field so subsequent user code can access it. 36 | */ 37 | public Application application; 38 | public double interval; 39 | 40 | /** 41 | * The reactor calls here, and it may be overridden in subclasses. 42 | * User code should never call this method. 43 | */ 44 | public void _fire() { 45 | fire(); 46 | } 47 | 48 | /** 49 | * User code is expected to override this method. 50 | */ 51 | public void fire() { 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /lib/em/channel.rb: -------------------------------------------------------------------------------- 1 | module EventMachine 2 | # Provides a simple interface to push items to a number of subscribers. The 3 | # channel will schedule all operations on the main reactor thread for thread 4 | # safe reactor operations. 5 | # 6 | # This provides a convenient way for connections to consume messages from 7 | # long running code in defer, without threading issues. 8 | # 9 | # channel = EM::Channel.new 10 | # sid = channel.subscribe{ |msg| p [:got, msg] } 11 | # channel.push('hello world') 12 | # channel.unsubscribe(sid) 13 | # 14 | # See examples/ex_channel.rb for a detailed example. 15 | class Channel 16 | # Create a new channel 17 | def initialize 18 | @subs = {} 19 | @uid = 0 20 | end 21 | 22 | # Takes any arguments suitable for EM::Callback() and returns a subscriber 23 | # id for use when unsubscribing. 24 | def subscribe(*a, &b) 25 | name = gen_id 26 | EM.schedule { @subs[name] = EM::Callback(*a, &b) } 27 | name 28 | end 29 | 30 | # Removes this subscriber from the list. 31 | def unsubscribe(name) 32 | EM.schedule { @subs.delete name } 33 | end 34 | 35 | # Add items to the channel, which are pushed out to all subscribers. 36 | def push(*items) 37 | items = items.dup 38 | EM.schedule { @subs.values.each { |s| items.each { |i| s.call i } } } 39 | end 40 | alias << push 41 | 42 | # Receive exactly one message from the channel. 43 | def pop(*a, &b) 44 | EM.schedule { 45 | name = subscribe do |*args| 46 | unsubscribe(name) 47 | EM::Callback(*a, &b).call(*args) 48 | end 49 | } 50 | end 51 | 52 | private 53 | def gen_id # :nodoc: 54 | @uid += 1 55 | end 56 | end 57 | end -------------------------------------------------------------------------------- /tests/test_exc.rb: -------------------------------------------------------------------------------- 1 | # $Id$ 2 | # 3 | # Author:: Francis Cianfrocca (gmail: blackhedd) 4 | # Homepage:: http://rubyeventmachine.com 5 | # Date:: 8 April 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 | $:.unshift "../lib" 27 | require 'eventmachine' 28 | require 'test/unit' 29 | 30 | class TestSomeExceptions < Test::Unit::TestCase 31 | 32 | # Read the commentary in EventMachine#run. 33 | # This test exercises the ensure block in #run that makes sure 34 | # EventMachine#release_machine gets called even if an exception is 35 | # thrown within the user code. Without the ensured call to release_machine, 36 | # the second call to EventMachine#run will fail with a C++ exception 37 | # because the machine wasn't cleaned up properly. 38 | 39 | def test_a 40 | assert_raises(RuntimeError) { 41 | EventMachine.run { 42 | raise "some exception" 43 | } 44 | } 45 | end 46 | 47 | def test_b 48 | assert_raises(RuntimeError) { 49 | EventMachine.run { 50 | raise "some exception" 51 | } 52 | } 53 | end 54 | 55 | end 56 | -------------------------------------------------------------------------------- /ext/files.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | 3 | $Id$ 4 | 5 | File: files.h 6 | Date: 26Aug06 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 __FileStreamDescriptor__H_ 22 | #define __FileStreamDescriptor__H_ 23 | 24 | 25 | 26 | /************************** 27 | class FileStreamDescriptor 28 | **************************/ 29 | 30 | class FileStreamDescriptor: public EventableDescriptor 31 | { 32 | public: 33 | FileStreamDescriptor (int, EventMachine_t*); 34 | virtual ~FileStreamDescriptor(); 35 | 36 | virtual void Read(); 37 | virtual void Write(); 38 | virtual void Heartbeat(); 39 | 40 | virtual bool SelectForRead(); 41 | virtual bool SelectForWrite(); 42 | 43 | // Do we have any data to write? This is used by ShouldDelete. 44 | virtual int GetOutboundDataSize() {return OutboundDataSize;} 45 | 46 | protected: 47 | struct OutboundPage { 48 | OutboundPage (const char *b, int l, int o=0): Buffer(b), Length(l), Offset(o) {} 49 | void Free() {if (Buffer) free ((char*)Buffer); } 50 | const char *Buffer; 51 | int Length; 52 | int Offset; 53 | }; 54 | 55 | protected: 56 | deque OutboundPages; 57 | int OutboundDataSize; 58 | 59 | private: 60 | 61 | }; 62 | 63 | 64 | #endif // __FileStreamDescriptor__H_ 65 | 66 | -------------------------------------------------------------------------------- /tests/test_pause.rb: -------------------------------------------------------------------------------- 1 | $:.unshift File.expand_path(File.dirname(__FILE__) + "/../lib") 2 | require 'eventmachine' 3 | require 'socket' 4 | require 'test/unit' 5 | 6 | class TestPause < Test::Unit::TestCase 7 | TestHost = "127.0.0.1" 8 | TestPort = 9070 9 | 10 | def setup 11 | assert(!EM.reactor_running?) 12 | end 13 | 14 | def teardown 15 | assert(!EM.reactor_running?) 16 | end 17 | 18 | #------------------------------------- 19 | 20 | def test_pause_resume 21 | test = self 22 | server = nil 23 | 24 | s_rx = c_rx = 0 25 | 26 | EM.run do 27 | EM.start_server TestHost, TestPort, Module.new { 28 | define_method :post_init do 29 | server = self 30 | end 31 | 32 | define_method :receive_data do |data| 33 | s_rx += 1 34 | 35 | EM.add_periodic_timer(0.01) { send_data 'hi' } 36 | send_data 'hi' 37 | 38 | # pause server, now no outgoing data will actually 39 | # be sent and no more incoming data will be received 40 | pause 41 | end 42 | } 43 | 44 | c = EM.connect TestHost, TestPort, Module.new { 45 | define_method :receive_data do |data| 46 | c_rx += 1 47 | end 48 | } 49 | EM.add_periodic_timer(0.01) { c.send_data 'hi' } 50 | 51 | EM.add_timer(1) do 52 | test.assert_equal 1, s_rx 53 | test.assert_equal 0, c_rx 54 | test.assert server.paused? 55 | 56 | # resume server, queued outgoing and incoming data will be flushed 57 | server.resume 58 | 59 | test.assert ! server.paused? 60 | 61 | EM.add_timer(1) do 62 | test.assert server.paused? 63 | test.assert_equal 2, s_rx 64 | test.assert c_rx >= 1 65 | EM.stop_event_loop 66 | end 67 | end 68 | end 69 | end 70 | end 71 | -------------------------------------------------------------------------------- /lib/em/test.rb: -------------------------------------------------------------------------------- 1 | module EventMachine 2 | # TODO : examples 3 | module Test 4 | 5 | # Calls EM.next_tick { EM.stop } when it is unbound. 6 | module UnbindStopper 7 | def unbind 8 | EM.next_tick { EM.stop } 9 | end 10 | end 11 | 12 | # Immediately closes it's connection when connection_completed is called. 13 | module ImmediateCloser 14 | def connection_completed 15 | close_connection 16 | end 17 | end 18 | 19 | # Immediately closes it's connection when connection_completed is called, 20 | # then calls EM.next_tick { EM.stop } when unbound. 21 | module ClosingStopper 22 | include ImmediateCloser 23 | include UnbindStopper 24 | end 25 | 26 | # Schedule a job to perform when the reactor is running. 27 | def job(&blk) 28 | EM.next_tick(&blk) 29 | end 30 | 31 | # For use to run a callback after a number of reactor ticks. Reactor ticks 32 | # may vary in their runtime, and as such this should not be used as a time 33 | # delay. This method is best suited to achieve relatively rapid non-sleep 34 | # based assertions "in some time", for example when calling 35 | # EM::Server#stop or EM.stop_server, which will complete asynchronously in 36 | # a short timeframe. 37 | def in_ticks(n = 3, &b) 38 | if n == 0 39 | yield 40 | else 41 | job { in_ticks(n - 1, &b) } 42 | end 43 | end 44 | 45 | # Run the reactor, the optional block will be scheduled to run after all 46 | # other scheduled jobs. 47 | def go(timeout = 1, &blk) 48 | job(&blk) if blk 49 | success = true 50 | EM.run do 51 | EM.add_timer(timeout) do 52 | EM.stop 53 | success = false 54 | end 55 | end 56 | assert success, "Timedout after #{timeout} seconds" 57 | end 58 | 59 | end 60 | end -------------------------------------------------------------------------------- /tests/test_kb.rb: -------------------------------------------------------------------------------- 1 | # $Id$ 2 | # 3 | # Author:: Francis Cianfrocca (gmail: blackhedd) 4 | # Homepage:: http://rubyeventmachine.com 5 | # Date:: 8 April 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 | $:.unshift "../lib" 28 | require 'eventmachine' 29 | require 'test/unit' 30 | 31 | class TestKeyboardEvents < Test::Unit::TestCase 32 | 33 | def setup 34 | end 35 | 36 | def teardown 37 | end 38 | 39 | module KbHandler 40 | include EM::Protocols::LineText2 41 | def receive_line d 42 | EM::stop if d == "STOP" 43 | end 44 | end 45 | 46 | # This test doesn't actually do anything useful but is here to 47 | # illustrate the usage. If you removed the timer and ran this test 48 | # by itself on a console, and then typed into the console, it would 49 | # work. 50 | # I don't know how to get the test harness to simulate actual keystrokes. 51 | # When someone figures that out, then we can make this a real test. 52 | # 53 | def test_kb 54 | EM.run { 55 | EM.open_keyboard KbHandler 56 | EM::Timer.new(1) { EM.stop } 57 | } if $stdout.tty? # don't run the test unless it stands a chance of validity. 58 | end 59 | 60 | end 61 | -------------------------------------------------------------------------------- /lib/em/queue.rb: -------------------------------------------------------------------------------- 1 | module EventMachine 2 | # A cross thread, reactor scheduled, linear queue. 3 | # 4 | # This class provides a simple "Queue" like abstraction on top of the reactor 5 | # scheduler. It services two primary purposes: 6 | # * API sugar for stateful protocols 7 | # * Pushing processing onto the same thread as the reactor 8 | # 9 | # See examples/ex_queue.rb for a detailed example. 10 | # 11 | # q = EM::Queue.new 12 | # q.push('one', 'two', 'three') 13 | # 3.times do 14 | # q.pop{ |msg| puts(msg) } 15 | # end 16 | # 17 | class Queue 18 | # Create a new 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 | def pop(*a, &b) 28 | cb = EM::Callback(*a, &b) 29 | EM.schedule do 30 | if @items.empty? 31 | @popq << cb 32 | else 33 | cb.call @items.shift 34 | end 35 | end 36 | nil # Always returns nil 37 | end 38 | 39 | # Push items onto the queue in the reactor thread. The items will not appear 40 | # in the queue immediately, but will be scheduled for addition during the 41 | # next reactor tick. 42 | def push(*items) 43 | EM.schedule do 44 | @items.push(*items) 45 | @popq.shift.call @items.shift until @items.empty? || @popq.empty? 46 | end 47 | end 48 | 49 | # N.B. This is a peek, it's not thread safe, and may only tend toward 50 | # accuracy. 51 | def empty? 52 | @items.empty? 53 | end 54 | 55 | # N.B. This is a peek, it's not thread safe, and may only tend toward 56 | # accuracy. 57 | def size 58 | @items.size 59 | end 60 | end 61 | end -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/em/file_watch.rb: -------------------------------------------------------------------------------- 1 | module EventMachine 2 | 3 | # This is subclassed from EventMachine::Connection for use with the file monitoring API. Read the 4 | # documentation on the instance methods of this class, and for a full explanation see EventMachine.watch_file. 5 | class FileWatch < Connection 6 | # :stopdoc: 7 | Cmodified = 'modified'.freeze 8 | Cdeleted = 'deleted'.freeze 9 | Cmoved = 'moved'.freeze 10 | # :startdoc: 11 | 12 | def receive_data(data) #:nodoc: 13 | case data 14 | when Cmodified 15 | file_modified 16 | when Cdeleted 17 | file_deleted 18 | when Cmoved 19 | file_moved 20 | end 21 | end 22 | 23 | # Returns the path that EventMachine::watch_file was originally called with. The current implementation 24 | # does not pick up on the new filename after a rename occurs. 25 | def path 26 | @path 27 | end 28 | 29 | # Should be redefined with the user's custom callback that will be fired when the file is modified. 30 | def file_modified 31 | end 32 | 33 | # Should be redefined with the user's custom callback that will be fired when the file is deleted. 34 | # When the file is deleted, stop_watching will be called after this to make sure everything is 35 | # cleaned up correctly. 36 | # 37 | # Note that on linux (with inotify), file_deleted will not be called until all open file descriptors to 38 | # the file have been closed. 39 | def file_deleted 40 | end 41 | 42 | # Should be redefined with the user's custom callback that will be fired when the file is moved or renamed. 43 | def file_moved 44 | end 45 | 46 | # Discontinue monitoring of the file. 47 | # This involves cleaning up the underlying monitoring details with kqueue/inotify, and in turn firing unbind. 48 | # This will be called automatically when a file is deleted. User code may call it as well. 49 | def stop_watching 50 | EventMachine::unwatch_filename(@signature) 51 | end 52 | end 53 | 54 | end -------------------------------------------------------------------------------- /java/src/com/rubyeventmachine/Connection.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 | 31 | package com.rubyeventmachine; 32 | 33 | //import java.io.*; 34 | import java.nio.*; 35 | import java.net.*; 36 | //import java.nio.channels.*; 37 | 38 | public class Connection { 39 | 40 | public Application application; 41 | public long signature; 42 | 43 | public void postInit() {} 44 | public void connectionCompleted() {} 45 | public void unbind() {} 46 | public void receiveData (ByteBuffer bytebuffer) {} 47 | 48 | 49 | /** 50 | * Called by user code. 51 | * @param bytebuffer 52 | */ 53 | public void sendData (ByteBuffer b) { 54 | application.sendData(signature, b); 55 | } 56 | 57 | /** 58 | * This is called by user code. 59 | * TODO: don't expose the exception here. 60 | */ 61 | public void close() { 62 | application.closeConnection(signature, false); 63 | } 64 | /** 65 | * This is called by user code/ 66 | */ 67 | public void closeAfterWriting() { 68 | application.closeConnection(signature, true); 69 | } 70 | 71 | public void sendDatagram (ByteBuffer bb, InetSocketAddress addr) { 72 | application.sendDatagram (signature, bb, addr); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /java/src/com/rubyeventmachine/tests/TestServers.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.tests; 31 | 32 | 33 | import com.rubyeventmachine.*; 34 | import java.net.*; 35 | import org.junit.After; 36 | import org.junit.AfterClass; 37 | import org.junit.Before; 38 | import org.junit.BeforeClass; 39 | import org.junit.Test; 40 | import org.junit.Assert; 41 | 42 | public class TestServers { 43 | 44 | @BeforeClass 45 | public static void setUpBeforeClass() throws Exception { 46 | } 47 | 48 | @AfterClass 49 | public static void tearDownAfterClass() throws Exception { 50 | } 51 | 52 | @Before 53 | public void setUp() throws Exception { 54 | } 55 | 56 | @After 57 | public void tearDown() throws Exception { 58 | } 59 | 60 | 61 | @Test 62 | public void testBadServerAddress() { 63 | final Application a = new Application(); 64 | a.run (new Runnable() { 65 | public void run() { 66 | try { 67 | a.startServer(new InetSocketAddress ("100.100.100.100", 100), new DefaultConnectionFactory()); 68 | Assert.fail ("was supposed to throw a reactor exception"); 69 | } catch (EmReactorException e) {} 70 | a.stop(); 71 | } 72 | }); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /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 | 64 | public boolean isWatchOnly(); 65 | 66 | public boolean isNotifyReadable(); 67 | public boolean isNotifyWritable(); 68 | 69 | } 70 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /tests/test_sasl.rb: -------------------------------------------------------------------------------- 1 | # $Id$ 2 | # 3 | # Author:: Francis Cianfrocca (gmail: blackhedd) 4 | # Homepage:: http://rubyeventmachine.com 5 | # Date:: 8 April 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 | $:.unshift "../lib" 28 | require 'eventmachine' 29 | require 'test/unit' 30 | 31 | 32 | class TestSASL < Test::Unit::TestCase 33 | 34 | # SASL authentication is usually done with UNIX-domain sockets, but 35 | # we'll use TCP so this test will work on Windows. As far as the 36 | # protocol handlers are concerned, there's no difference. 37 | 38 | Host,Port = "127.0.0.1",9560 39 | TestUser,TestPsw = "someone", "password" 40 | 41 | class SaslServer < EM::Connection 42 | include EM::Protocols::SASLauth 43 | def validate usr, psw, sys, realm 44 | usr == TestUser and psw == TestPsw 45 | end 46 | end 47 | 48 | class SaslClient < EM::Connection 49 | include EM::Protocols::SASLauthclient 50 | end 51 | 52 | def test_sasl 53 | resp = nil 54 | EM.run { 55 | EM.start_server( Host, Port, SaslServer ) 56 | 57 | c = EM.connect( Host, Port, SaslClient ) 58 | d = c.validate?( TestUser, TestPsw ) 59 | d.timeout 2 60 | d.callback { 61 | resp = true 62 | EM.stop 63 | } 64 | d.errback { 65 | resp = false 66 | EM.stop 67 | } 68 | } 69 | assert_equal( true, resp ) 70 | end 71 | 72 | end 73 | -------------------------------------------------------------------------------- /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 | LastIo (gCurrentLoopTime), 31 | InactivityTimeout (0) 32 | { 33 | #ifdef HAVE_EPOLL 34 | EpollEvent.events = EPOLLIN; 35 | #endif 36 | #ifdef HAVE_KQUEUE 37 | MyEventMachine->ArmKqueueReader (this); 38 | #endif 39 | } 40 | 41 | 42 | /*************************************** 43 | KeyboardDescriptor::~KeyboardDescriptor 44 | ***************************************/ 45 | 46 | KeyboardDescriptor::~KeyboardDescriptor() 47 | { 48 | } 49 | 50 | 51 | /************************* 52 | KeyboardDescriptor::Write 53 | *************************/ 54 | 55 | void KeyboardDescriptor::Write() 56 | { 57 | // Why are we here? 58 | throw std::runtime_error ("bad code path in keyboard handler"); 59 | } 60 | 61 | 62 | /***************************** 63 | KeyboardDescriptor::Heartbeat 64 | *****************************/ 65 | 66 | void KeyboardDescriptor::Heartbeat() 67 | { 68 | // no-op 69 | } 70 | 71 | 72 | /************************ 73 | KeyboardDescriptor::Read 74 | ************************/ 75 | 76 | void KeyboardDescriptor::Read() 77 | { 78 | char c; 79 | read (GetSocket(), &c, 1); 80 | _GenericInboundDispatch(&c, 1); 81 | } 82 | -------------------------------------------------------------------------------- /java/src/com/rubyeventmachine/tests/EMTest.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.tests; 31 | 32 | //import static org.junit.Assert.*; 33 | 34 | import org.junit.After; 35 | import org.junit.AfterClass; 36 | import org.junit.Before; 37 | import org.junit.BeforeClass; 38 | import org.junit.Test; 39 | 40 | import com.rubyeventmachine.EmReactor; 41 | 42 | import java.io.*; 43 | import java.nio.*; 44 | 45 | 46 | public class EMTest { 47 | 48 | class ShortTimer extends EmReactor { 49 | public void eventCallback (String sig, int eventCode, ByteBuffer data) { 50 | System.out.println ("Short Callback "+sig+" "+eventCode+" "+data); 51 | this.stop(); 52 | } 53 | } 54 | 55 | @BeforeClass 56 | public static void setUpBeforeClass() throws Exception { 57 | } 58 | 59 | @AfterClass 60 | public static void tearDownAfterClass() throws Exception { 61 | } 62 | 63 | @Before 64 | public void setUp() throws Exception { 65 | } 66 | 67 | @After 68 | public void tearDown() throws Exception { 69 | } 70 | 71 | 72 | @Test 73 | public final void testOneShort() throws IOException { 74 | EmReactor em = new ShortTimer(); 75 | em.installOneshotTimer(1050); 76 | em.run(); 77 | } 78 | 79 | 80 | } 81 | -------------------------------------------------------------------------------- /tests/test_proxy_connection.rb: -------------------------------------------------------------------------------- 1 | $:.unshift "../lib" 2 | require 'eventmachine' 3 | require 'test/unit' 4 | 5 | class TestProxyConnection < Test::Unit::TestCase 6 | 7 | module ProxyConnection 8 | def initialize(client, request) 9 | @client, @request = client, request 10 | end 11 | 12 | def post_init 13 | EM::enable_proxy(self, @client) 14 | end 15 | 16 | def connection_completed 17 | EM.next_tick { 18 | send_data @request 19 | } 20 | end 21 | 22 | def proxy_target_unbound 23 | $unbound_early = true 24 | EM.stop 25 | end 26 | 27 | def unbind 28 | @client.close_connection_after_writing 29 | end 30 | end 31 | 32 | module Client 33 | def connection_completed 34 | send_data "EventMachine rocks!" 35 | end 36 | 37 | def receive_data(data) 38 | $client_data = data 39 | end 40 | 41 | def unbind 42 | EM.stop 43 | end 44 | end 45 | 46 | module Client2 47 | include Client 48 | def unbind; end 49 | end 50 | 51 | module Server 52 | def receive_data(data) 53 | send_data "I know!" if data == "EventMachine rocks!" 54 | close_connection_after_writing 55 | end 56 | end 57 | 58 | module ProxyServer 59 | def receive_data(data) 60 | EM.connect("127.0.0.1", 54321, ProxyConnection, self, data) 61 | end 62 | end 63 | 64 | module EarlyClosingProxy 65 | def receive_data(data) 66 | EM.connect("127.0.0.1", 54321, ProxyConnection, self, data) 67 | close_connection 68 | end 69 | end 70 | 71 | def test_proxy_connection 72 | EM.run { 73 | EM.start_server("127.0.0.1", 54321, Server) 74 | EM.start_server("127.0.0.1", 12345, ProxyServer) 75 | EM.connect("127.0.0.1", 12345, Client) 76 | } 77 | 78 | assert_equal("I know!", $client_data) 79 | end 80 | 81 | def test_early_close 82 | $client_data = nil 83 | EM.run { 84 | EM.start_server("127.0.0.1", 54321, Server) 85 | EM.start_server("127.0.0.1", 12345, EarlyClosingProxy) 86 | EM.connect("127.0.0.1", 12345, Client2) 87 | } 88 | 89 | assert($unbound_early) 90 | end 91 | 92 | end 93 | -------------------------------------------------------------------------------- /lib/evma/protocol.rb: -------------------------------------------------------------------------------- 1 | # $Id$ 2 | # 3 | # Author:: Francis Cianfrocca (gmail: blackhedd) 4 | # Homepage:: http://rubyeventmachine.com 5 | # Date:: 19 May 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 Evma 27 | class Protocol 28 | 29 | attr_reader :signature 30 | 31 | def initialize sig 32 | @signature = sig 33 | end 34 | 35 | def unbind 36 | end 37 | 38 | def close 39 | Evma::Reactor.instance # ensure initialized 40 | EventMachine.close_connection signature, false 41 | end 42 | 43 | def close_after_writing 44 | Evma::Reactor.instance # ensure initialized 45 | EventMachine.close_connection signature, true 46 | end 47 | 48 | end # class Protocol 49 | end # module Evma 50 | 51 | 52 | ########################################### 53 | 54 | module Evma 55 | class StreamProtocol < Protocol 56 | 57 | def initialize sig 58 | super 59 | end 60 | 61 | def send_data data 62 | Evma::Reactor.instance # ensure initialized 63 | EventMachine.send_data signature, data, data.length 64 | end 65 | 66 | end # class Protocol 67 | end # module Evma 68 | 69 | 70 | ########################################### 71 | 72 | module Evma 73 | class DatagramProtocol < Protocol 74 | 75 | def initialize sig 76 | super 77 | end 78 | 79 | def send_message data 80 | Evma::Reactor.instance # ensure initialized 81 | raise "unimplemented" 82 | end 83 | 84 | end # class Protocol 85 | end # module Evma 86 | 87 | 88 | -------------------------------------------------------------------------------- /lib/evma/container.rb: -------------------------------------------------------------------------------- 1 | # $Id$ 2 | # 3 | # Author:: Francis Cianfrocca (gmail: blackhedd) 4 | # Homepage:: http://rubyeventmachine.com 5 | # Date:: 19 May 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 | require 'singleton' 28 | 29 | module Evma 30 | 31 | class ContainerHasObject < Exception; end 32 | class UnsupportedCallback < Exception; end 33 | class UnknownTarget < Exception; end 34 | 35 | class Container 36 | include Singleton 37 | 38 | def initialize 39 | @objects = {} 40 | end 41 | 42 | def self.store obj 43 | instance.store obj 44 | end 45 | 46 | def self.callback target, opcode, data 47 | instance.callback target, opcode, data 48 | end 49 | 50 | def store obj 51 | sig = obj.signature 52 | raise ContainerHasObject.new(sig) if @objects.has_key?(sig) 53 | @objects[sig] = obj 54 | end 55 | 56 | def callback target, opcode, data 57 | case opcode 58 | when 101 # received data 59 | obj = @objects[target] or raise UnknownTarget.new( target ) 60 | obj.receive_data data 61 | when 102 # unbind 62 | obj = @objects[target] or raise UnknownTarget.new( target ) 63 | obj.unbind 64 | @objects.delete obj.signature 65 | when 103 # accept 66 | obj = @objects[target] or raise UnknownTarget.new( target ) 67 | obj.accept data 68 | else 69 | raise UnsupportedCallback.new( opcode.to_s ) 70 | end 71 | end 72 | 73 | end # class Container 74 | end # module Evma 75 | 76 | -------------------------------------------------------------------------------- /ext/sigs.cpp: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | 3 | $Id$ 4 | 5 | File: sigs.cpp 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 | #include "project.h" 21 | 22 | 23 | bool gTerminateSignalReceived; 24 | 25 | 26 | /************** 27 | SigtermHandler 28 | **************/ 29 | 30 | void SigtermHandler (int sig) 31 | { 32 | // This is a signal-handler, don't do anything frisky. Interrupts are disabled. 33 | // Set the terminate flag WITHOUT trying to lock a mutex- otherwise we can easily 34 | // self-deadlock, especially if the event machine is looping quickly. 35 | gTerminateSignalReceived = true; 36 | } 37 | 38 | 39 | /********************* 40 | InstallSignalHandlers 41 | *********************/ 42 | 43 | void InstallSignalHandlers() 44 | { 45 | #ifdef OS_UNIX 46 | static bool bInstalled = false; 47 | if (!bInstalled) { 48 | bInstalled = true; 49 | signal (SIGINT, SigtermHandler); 50 | signal (SIGTERM, SigtermHandler); 51 | signal (SIGPIPE, SIG_IGN); 52 | } 53 | #endif 54 | } 55 | 56 | 57 | 58 | /******************* 59 | WintelSignalHandler 60 | *******************/ 61 | 62 | #ifdef OS_WIN32 63 | BOOL WINAPI WintelSignalHandler (DWORD control) 64 | { 65 | if (control == CTRL_C_EVENT) 66 | gTerminateSignalReceived = true; 67 | return TRUE; 68 | } 69 | #endif 70 | 71 | /************ 72 | HookControlC 73 | ************/ 74 | 75 | #ifdef OS_WIN32 76 | void HookControlC (bool hook) 77 | { 78 | if (hook) { 79 | // INSTALL hook 80 | SetConsoleCtrlHandler (WintelSignalHandler, TRUE); 81 | } 82 | else { 83 | // UNINSTALL hook 84 | SetConsoleCtrlHandler (WintelSignalHandler, FALSE); 85 | } 86 | } 87 | #endif 88 | 89 | 90 | -------------------------------------------------------------------------------- /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 | class SslBox_t 58 | { 59 | public: 60 | SslBox_t (bool is_server, const string &privkeyfile, const string &certchainfile, bool verify_peer, const unsigned long binding); 61 | virtual ~SslBox_t(); 62 | 63 | int PutPlaintext (const char*, int); 64 | int GetPlaintext (char*, int); 65 | 66 | bool PutCiphertext (const char*, int); 67 | bool CanGetCiphertext(); 68 | int GetCiphertext (char*, int); 69 | bool IsHandshakeCompleted() {return bHandshakeCompleted;} 70 | 71 | X509 *GetPeerCert(); 72 | 73 | void Shutdown(); 74 | 75 | protected: 76 | SslContext_t *Context; 77 | 78 | bool bIsServer; 79 | bool bHandshakeCompleted; 80 | bool bVerifyPeer; 81 | SSL *pSSL; 82 | BIO *pbioRead; 83 | BIO *pbioWrite; 84 | 85 | PageList OutboundQ; 86 | }; 87 | 88 | extern "C" int ssl_verify_wrapper(int, X509_STORE_CTX*); 89 | 90 | #endif // WITH_SSL 91 | 92 | 93 | #endif // __SslBox__H_ 94 | 95 | -------------------------------------------------------------------------------- /lib/em/server.rb: -------------------------------------------------------------------------------- 1 | module EventMachine 2 | # An object oriented approach at EventMachine servers. This server class 3 | # provides a convenient handle to EventMachine server bindings. The delegate 4 | # parameter defines the connection module, or class that will be used to 5 | # process connections. Optionally, users may pass a block which will be used 6 | # as if it were passed to Module.new and passed in. The delegate paramenter 7 | # will also accept an already constructed object to which it will delegate 8 | # the callbacks for all connections to that object using 9 | # EventMachine::DelegateConnection. 10 | class Server 11 | 12 | # The host and port parameters define the listen side of a server binding. 13 | # The delegate parameter defines the object, module, or class that will be 14 | # utilised. Users may also pass a block instead of a delegate object which 15 | # will define a module for the connections. 16 | def initialize(host, port, delegate = nil, *delegate_args, &blk) 17 | @host, @port = host, port 18 | set_delegate(delegate || blk) 19 | @delegate_args = delegate_args 20 | end 21 | 22 | # Schedule the start of the server listener. If the reactor is not yet 23 | # running, then this method will simply schedule listening for the reactor 24 | # start. 25 | def listen 26 | args = @delegate + @delegate_args 27 | EM.schedule { @signature = EM.start_server(@host, @port, *args) } 28 | self 29 | end 30 | 31 | # Schedule the asynchronous shutdown of the server binding. 32 | def stop 33 | EM.schedule { EM.stop_server(@signature) if @signature } 34 | self 35 | end 36 | 37 | # Start the reactor. If the reactor is not already running, calling this 38 | # method will block until EM.stop is called. 39 | def run 40 | EM.run 41 | end 42 | 43 | private 44 | def set_delegate(delegate) 45 | @delegate = case delegate 46 | when Class 47 | [delegate] 48 | when Module 49 | [Class.new(EM::Connection) { include delegate }] 50 | when Proc 51 | [Module.new(&delegate)] 52 | # when nil # TODO 53 | # LoggerConnection 54 | else 55 | [DelegateConnection, delegate] 56 | end 57 | end 58 | end 59 | end -------------------------------------------------------------------------------- /tests/test_ssl_verify.rb: -------------------------------------------------------------------------------- 1 | $:.unshift "../lib" 2 | require 'eventmachine' 3 | require 'test/unit' 4 | 5 | class TestSslVerify < Test::Unit::TestCase 6 | 7 | def setup 8 | $dir = File.dirname(File.expand_path(__FILE__)) + '/' 9 | $cert_from_file = File.read($dir+'client.crt') 10 | end 11 | 12 | module Client 13 | def connection_completed 14 | start_tls(:private_key_file => $dir+'client.key', :cert_chain_file => $dir+'client.crt') 15 | end 16 | 17 | def ssl_handshake_completed 18 | $client_handshake_completed = true 19 | close_connection 20 | end 21 | 22 | def unbind 23 | EM.stop_event_loop 24 | end 25 | end 26 | 27 | module AcceptServer 28 | def post_init 29 | start_tls(:verify_peer => true) 30 | end 31 | 32 | def ssl_verify_peer(cert) 33 | $cert_from_server = cert 34 | true 35 | end 36 | 37 | def ssl_handshake_completed 38 | $server_handshake_completed = true 39 | end 40 | end 41 | 42 | module DenyServer 43 | def post_init 44 | start_tls(:verify_peer => true) 45 | end 46 | 47 | def ssl_verify_peer(cert) 48 | $cert_from_server = cert 49 | # Do not accept the peer. This should now cause the connection to shut down without the SSL handshake being completed. 50 | false 51 | end 52 | 53 | def ssl_handshake_completed 54 | $server_handshake_completed = true 55 | end 56 | end 57 | 58 | def test_accept_server 59 | $client_handshake_completed, $server_handshake_completed = false, false 60 | EM.run { 61 | EM.start_server("127.0.0.1", 16784, AcceptServer) 62 | EM.connect("127.0.0.1", 16784, Client).instance_variable_get("@signature") 63 | } 64 | 65 | assert_equal($cert_from_file, $cert_from_server) 66 | assert($client_handshake_completed) 67 | assert($server_handshake_completed) 68 | end 69 | 70 | def test_deny_server 71 | $client_handshake_completed, $server_handshake_completed = false, false 72 | EM.run { 73 | EM.start_server("127.0.0.1", 16784, DenyServer) 74 | EM.connect("127.0.0.1", 16784, Client) 75 | } 76 | 77 | assert_equal($cert_from_file, $cert_from_server) 78 | assert(!$client_handshake_completed) 79 | assert(!$server_handshake_completed) 80 | end 81 | 82 | end 83 | -------------------------------------------------------------------------------- /tests/test_errors.rb: -------------------------------------------------------------------------------- 1 | # $Id$ 2 | # 3 | # Author:: Francis Cianfrocca (gmail: blackhedd) 4 | # Homepage:: http://rubyeventmachine.com 5 | # Date:: 8 April 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 | 28 | ###### THIS TEST IS NOW OBSOLETE. 29 | ###### As of 27Dec07, the hookable error handling is obsolete because 30 | ###### of its performance impact. 31 | 32 | 33 | $:.unshift "../lib" 34 | require 'eventmachine' 35 | require 'test/unit' 36 | 37 | class TestErrors < Test::Unit::TestCase 38 | 39 | Localhost = "127.0.0.1" 40 | Localport = 9801 41 | 42 | def setup 43 | end 44 | 45 | def obsolete_teardown 46 | # Calling #set_runtime_error_hook with no block restores the 47 | # default handling of runtime_errors. 48 | # 49 | EM.set_runtime_error_hook 50 | end 51 | 52 | def test_no_tests_stub 53 | end 54 | 55 | # EM has a default handler for RuntimeErrors that are emitted from 56 | # user written code. You can override the handler if you wish, but it's 57 | # easier to call #set_runtime_error_hook. 58 | # Ordinarily, an error in user code invoked by the reactor aborts the 59 | # run. 60 | # 61 | def obsolete_test_unhandled_error 62 | assert_raises( RuntimeError ) { 63 | EM.run { 64 | EM.add_timer(0) {raise "AAA"} 65 | } 66 | } 67 | 68 | end 69 | 70 | def obsolete_test_handled_error 71 | err = nil 72 | EM.run { 73 | EM.set_runtime_error_hook { 74 | err = true 75 | EM.stop 76 | } 77 | EM.add_timer(0) {raise "AAA"} 78 | } 79 | assert err 80 | end 81 | end 82 | 83 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /java/src/com/rubyeventmachine/tests/TestTimers.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.tests; 31 | 32 | import com.rubyeventmachine.*; 33 | import java.io.*; 34 | 35 | import org.junit.Assert; 36 | import org.junit.After; 37 | import org.junit.AfterClass; 38 | import org.junit.Before; 39 | import org.junit.BeforeClass; 40 | import org.junit.Test; 41 | 42 | 43 | public class TestTimers { 44 | 45 | @BeforeClass 46 | public static void setUpBeforeClass() throws Exception { 47 | } 48 | 49 | @AfterClass 50 | public static void tearDownAfterClass() throws Exception { 51 | } 52 | 53 | @Before 54 | public void setUp() throws Exception { 55 | } 56 | 57 | @After 58 | public void tearDown() throws Exception { 59 | } 60 | 61 | 62 | 63 | @Test 64 | public final void test2() throws IOException { 65 | Application a = new Application(); 66 | a.addTimer(0, new Timer() { 67 | public void fire() { 68 | application.stop(); 69 | } 70 | }); 71 | a.run(); 72 | Assert.assertEquals (1, 1); // just to make sure the reactor halts. 73 | } 74 | 75 | @Test 76 | public final void test3() throws IOException { 77 | Application a = new Application(); 78 | a.addTimer (0.1, new PeriodicTimer() { 79 | int n = 0; 80 | public void fire() { 81 | n++; 82 | if (n == 5) 83 | application.stop(); 84 | } 85 | }); 86 | a.run(); 87 | Assert.assertEquals(1, 1); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /tests/test_ssl_args.rb: -------------------------------------------------------------------------------- 1 | require "test/unit" 2 | require 'tempfile' 3 | 4 | $:.unshift File.expand_path(File.dirname(__FILE__) + '/../lib') 5 | require "eventmachine" 6 | 7 | module EventMachine 8 | def self._set_mocks 9 | class < priv_file) 57 | end 58 | assert_raises(EventMachine::FileNotFoundException) do 59 | conn.start_tls(:cert_chain_file => cert_file) 60 | end 61 | assert_raises(EventMachine::FileNotFoundException) do 62 | conn.start_tls(:private_key_file => priv_file, :cert_chain_file => cert_file) 63 | end 64 | end 65 | 66 | def test_tls_params_file_does_exist 67 | priv_file = Tempfile.new('em_test') 68 | cert_file = Tempfile.new('em_test') 69 | priv_file_path = priv_file.path 70 | cert_file_path = cert_file.path 71 | conn = EventMachine::Connection.new('foo') 72 | params = {:private_key_file => priv_file_path, :cert_chain_file => cert_file_path} 73 | begin 74 | conn.start_tls params 75 | rescue Object 76 | assert(false, 'should not have raised an exception') 77 | end 78 | end 79 | end if EM.ssl? -------------------------------------------------------------------------------- /ext/files.cpp: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | 3 | $Id$ 4 | 5 | File: files.cpp 6 | Date: 26Aug06 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 | FileStreamDescriptor::FileStreamDescriptor 25 | ******************************************/ 26 | 27 | FileStreamDescriptor::FileStreamDescriptor (int fd, EventMachine_t *em): 28 | EventableDescriptor (fd, em), 29 | OutboundDataSize (0) 30 | { 31 | cerr << "#####"; 32 | } 33 | 34 | 35 | /******************************************* 36 | FileStreamDescriptor::~FileStreamDescriptor 37 | *******************************************/ 38 | 39 | FileStreamDescriptor::~FileStreamDescriptor() 40 | { 41 | // Run down any stranded outbound data. 42 | for (size_t i=0; i < OutboundPages.size(); i++) 43 | OutboundPages[i].Free(); 44 | } 45 | 46 | 47 | /************************** 48 | FileStreamDescriptor::Read 49 | **************************/ 50 | 51 | void FileStreamDescriptor::Read() 52 | { 53 | } 54 | 55 | /*************************** 56 | FileStreamDescriptor::Write 57 | ***************************/ 58 | 59 | void FileStreamDescriptor::Write() 60 | { 61 | } 62 | 63 | 64 | /******************************* 65 | FileStreamDescriptor::Heartbeat 66 | *******************************/ 67 | 68 | void FileStreamDescriptor::Heartbeat() 69 | { 70 | } 71 | 72 | 73 | /*********************************** 74 | FileStreamDescriptor::SelectForRead 75 | ***********************************/ 76 | 77 | bool FileStreamDescriptor::SelectForRead() 78 | { 79 | cerr << "R?"; 80 | return false; 81 | } 82 | 83 | 84 | /************************************ 85 | FileStreamDescriptor::SelectForWrite 86 | ************************************/ 87 | 88 | bool FileStreamDescriptor::SelectForWrite() 89 | { 90 | cerr << "W?"; 91 | return false; 92 | } 93 | 94 | 95 | -------------------------------------------------------------------------------- /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 = $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 | # Main platform invariances: 30 | 31 | case RUBY_PLATFORM 32 | when /mswin32/, /mingw32/, /bccwin32/ 33 | check_heads(%w[windows.h winsock.h], true) 34 | check_libs(%w[kernel32 rpcrt4 gdi32], true) 35 | 36 | if GNU_CHAIN 37 | CONFIG['LDSHARED'] = "$(CXX) -shared -lstdc++" 38 | else 39 | $defs.push "-EHs" 40 | $defs.push "-GR" 41 | end 42 | 43 | when /solaris/ 44 | check_libs(%w[nsl socket], true) 45 | 46 | add_define 'OS_SOLARIS8' 47 | 48 | # Patch by Tim Pease, fixes SUNWspro compile problems. 49 | if CONFIG['CC'] == 'cc' 50 | # SUN CHAIN 51 | $CFLAGS = CONFIG['CFLAGS'] = "-KPIC -G" 52 | CONFIG['CCDLFLAGS'] = "-KPIC" 53 | else 54 | # GNU CHAIN 55 | # on Unix we need a g++ link, not gcc. 56 | CONFIG['LDSHARED'] = "$(CXX) -shared" 57 | end 58 | 59 | when /openbsd/ 60 | # OpenBSD branch contributed by Guillaume Sellier. 61 | 62 | # on Unix we need a g++ link, not gcc. On OpenBSD, linking against libstdc++ have to be explicitly done for shared libs 63 | CONFIG['LDSHARED'] = "$(CXX) -shared -lstdc++ -fPIC" 64 | CONFIG['LDSHAREDXX'] = "$(CXX) -shared -lstdc++ -fPIC" 65 | 66 | when /darwin/ 67 | # on Unix we need a g++ link, not gcc. 68 | # Ff line contributed by Daniel Harple. 69 | CONFIG['LDSHARED'] = "$(CXX) " + CONFIG['LDSHARED'].split[1..-1].join(' ') 70 | 71 | when /linux/ 72 | # on Unix we need a g++ link, not gcc. 73 | CONFIG['LDSHARED'] = "$(CXX) -shared" 74 | 75 | when /aix/ 76 | # on Unix we need a g++ link, not gcc. 77 | CONFIG['LDSHARED'] = "$(CXX) -shared -Wl,-G" 78 | 79 | else 80 | # on Unix we need a g++ link, not gcc. 81 | CONFIG['LDSHARED'] = "$(CXX) -shared" 82 | end 83 | 84 | create_makefile "fastfilereaderext" -------------------------------------------------------------------------------- /tests/test_smtpclient.rb: -------------------------------------------------------------------------------- 1 | # $Id$ 2 | # 3 | # Author:: Francis Cianfrocca (gmail: blackhedd) 4 | # Homepage:: http://rubyeventmachine.com 5 | # Date:: 8 April 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 | $:.unshift "../lib" 28 | require 'eventmachine' 29 | require 'test/unit' 30 | 31 | class TestSmtpClient < Test::Unit::TestCase 32 | 33 | Localhost = "127.0.0.1" 34 | Localport = 9801 35 | 36 | def setup 37 | end 38 | 39 | def teardown 40 | end 41 | 42 | def test_a 43 | # No real tests until we have a server implementation to test against. 44 | # This is what the call looks like, though: 45 | err = nil 46 | EM.run { 47 | d = EM::Protocols::SmtpClient.send :domain=>"example.com", 48 | :host=>Localhost, 49 | :port=>Localport, # optional, defaults 25 50 | :starttls=>true, 51 | :from=>"sender@example.com", 52 | :to=> ["to_1@example.com", "to_2@example.com"], 53 | :header=> {"Subject" => "This is a subject line"}, 54 | :body=> "This is the body of the email", 55 | :verbose=>true 56 | d.errback {|e| 57 | err = e 58 | EM.stop 59 | } 60 | } 61 | assert(err) 62 | end 63 | 64 | def test_content 65 | err = nil 66 | EM.run { 67 | d = EM::Protocols::SmtpClient.send :domain=>"example.com", 68 | :host=>Localhost, 69 | :port=>Localport, # optional, defaults 25 70 | :starttls=>true, 71 | :from=>"sender@example.com", 72 | :to=> ["to_1@example.com", "to_2@example.com"], 73 | :content => ["Subject: xxx\r\n\r\ndata\r\n.\r\n"], 74 | :verbose=>true 75 | d.errback {|e| 76 | err = e 77 | EM.stop 78 | } 79 | } 80 | assert(err) 81 | end 82 | 83 | end 84 | -------------------------------------------------------------------------------- /lib/evma/factory.rb: -------------------------------------------------------------------------------- 1 | # $Id$ 2 | # 3 | # Author:: Francis Cianfrocca (gmail: blackhedd) 4 | # Homepage:: http://rubyeventmachine.com 5 | # Date:: 19 May 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 Evma 27 | class ProtocolFactory < Protocol 28 | 29 | #-- 30 | # default implementation raises an exception. 31 | # we expect subclasses to override this. 32 | # we can't do anything reasonable here because 33 | def accept new_object 34 | # don't bother calling Evma::Reactor.instance, since only Reactor can call accept 35 | Evma::Container.store Evma::Protocol.new( new_object ) 36 | EventMachine.close_connection new_object, false 37 | end 38 | 39 | 40 | end # class ProtocolFactory 41 | end # module Evma 42 | 43 | ###################################### 44 | 45 | module Evma 46 | class TcpSocket 47 | 48 | def self.connect server, port, protocol_handler = Evma::Protocol 49 | Evma::Reactor.instance # ensure initialization 50 | sig = EventMachine.connect_server server, port 51 | Evma::Container.store protocol_handler.new( sig ) 52 | end 53 | 54 | end 55 | end # module Evma 56 | 57 | ###################################### 58 | 59 | module Evma 60 | class TcpServerFactory < Evma::ProtocolFactory 61 | 62 | def initialize server, port, protocol_handler = Evma::Protocol 63 | Evma::Reactor.instance # ensure initialization 64 | sig = EventMachine.start_tcp_server server, port 65 | super sig 66 | @protocol_handler = protocol_handler 67 | Evma::Container.store self 68 | end 69 | 70 | def accept new_obj 71 | # don't bother calling Evma::Reactor.instance, since only Reactor can call accept 72 | Evma::Container.store @protocol_handler.new( new_obj ) 73 | end 74 | 75 | end 76 | end # module Evma 77 | 78 | -------------------------------------------------------------------------------- /ext/emwin.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | 3 | $Id$ 4 | 5 | File: emwin.h 6 | Date: 05May06 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 | // THIS ENTIRE FILE IS FOR WINDOWS BUILDS ONLY. 22 | // INCOMPLETE AND DISABLED FOR NOW. 23 | #ifdef xOS_WIN32 24 | 25 | #ifndef __EventMachine__H_ 26 | #define __EventMachine__H_ 27 | 28 | 29 | extern time_t gCurrentLoopTime; 30 | 31 | class EventableDescriptor; 32 | 33 | 34 | /******************** 35 | class EventMachine_t 36 | ********************/ 37 | 38 | class EventMachine_t 39 | { 40 | public: 41 | EventMachine_t (void(*event_callback)(const char*, int, const char*, int)); 42 | virtual ~EventMachine_t(); 43 | 44 | void Run(); 45 | void ScheduleHalt(); 46 | const char *InstallOneshotTimer (int); 47 | const char *ConnectToServer (const char *, int); 48 | const char *CreateTcpServer (const char *, int); 49 | const char *OpenDatagramSocket (const char *, int); 50 | 51 | void Add (EventableDescriptor*); 52 | 53 | public: 54 | enum { // Event names 55 | TIMER_FIRED = 100, 56 | CONNECTION_READ = 101, 57 | CONNECTION_UNBOUND = 102, 58 | CONNECTION_ACCEPTED = 103, 59 | CONNECTION_COMPLETED = 104, 60 | LOOPBREAK_SIGNAL = 105 61 | }; 62 | 63 | private: 64 | HANDLE Iocp; 65 | 66 | private: 67 | bool _RunOnce(); 68 | bool _RunTimers(); 69 | void _AddNewDescriptors(); 70 | 71 | private: 72 | enum { 73 | MaxOutstandingTimers = 40, 74 | HeartbeatInterval = 2 75 | }; 76 | void (*EventCallback)(const char*, int, const char*, int); 77 | 78 | class Timer_t: public Bindable_t { 79 | }; 80 | 81 | multimap Timers; 82 | vector Descriptors; 83 | vector NewDescriptors; 84 | 85 | time_t NextHeartbeatTime; 86 | }; 87 | 88 | 89 | 90 | 91 | #endif // __EventMachine__H_ 92 | 93 | #endif // OS_WIN32 94 | 95 | -------------------------------------------------------------------------------- /tests/test_servers.rb: -------------------------------------------------------------------------------- 1 | # $Id$ 2 | # 3 | # Author:: Francis Cianfrocca (gmail: blackhedd) 4 | # Homepage:: http://rubyeventmachine.com 5 | # Date:: 8 April 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 | $:.unshift "../lib" 28 | require 'eventmachine' 29 | require 'socket' 30 | require 'test/unit' 31 | 32 | class TestServers < Test::Unit::TestCase 33 | 34 | Host = "127.0.0.1" 35 | Port = 9555 36 | 37 | module NetstatHelper 38 | GlobalUdp4Rexp = /udp.*\s+(?:\*|(?:0\.){3}0)[:.](\d+)\s/i 39 | GlobalTcp4Rexp = /tcp.*\s+(?:\*|(?:0\.){3}0)[:.](\d+)\s/i 40 | LocalUdpRexp = /udp.*\s+(?:127\.0\.0\.1|::1)[:.](\d+)\s/i 41 | LocalTcpRexp = /tcp.*\s+(?:127\.0\.0\.1|::1)[:.](\d+)\s/i 42 | def grep_netstat(pattern) 43 | `netstat -an`.scan(/^.*$/).grep(pattern) 44 | end 45 | end 46 | include NetstatHelper 47 | 48 | class TestStopServer < EM::Connection 49 | def initialize *args 50 | super 51 | end 52 | def post_init 53 | # TODO,sucks that this isn't OOPy enough. 54 | EM.stop_server @server_instance 55 | end 56 | end 57 | 58 | def run_test_stop_server 59 | EM.run { 60 | sig = EM.start_server(Host, Port) 61 | assert(grep_netstat(LocalTcpRexp).grep(%r(#{Port})).size >= 1, "Server didn't start") 62 | EM.stop_server sig 63 | # Give the server some time to shutdown. 64 | EM.add_timer(0.1) { 65 | assert(grep_netstat(LocalTcpRexp).grep(%r(#{Port})).empty?, "Servers didn't stop") 66 | EM.stop 67 | } 68 | } 69 | end 70 | def test_stop_server 71 | assert(grep_netstat(LocalTcpRexp).grep(Port).empty?, "Port already in use") 72 | 5.times {run_test_stop_server} 73 | assert(grep_netstat(LocalTcpRexp).grep(%r(#{Port})).empty?, "Servers didn't stop") 74 | end 75 | 76 | end 77 | -------------------------------------------------------------------------------- /tasks/cpp.rake: -------------------------------------------------------------------------------- 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 -------------------------------------------------------------------------------- /ext/eventmachine_cpp.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | 3 | $Id$ 4 | 5 | File: eventmachine_cpp.h 6 | Date: 27Jul07 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 __EVMA_EventmachineCpp__H_ 22 | #define __EVMA_EventmachineCpp__H_ 23 | 24 | 25 | // This material is only for directly integrating EM into C++ programs. 26 | 27 | namespace EM { 28 | 29 | void Callback (const unsigned long sig, int event, const char *data, const unsigned long length); 30 | void Run (void(*)(void)); 31 | void AddTimer (int, void(*)()); 32 | void StopReactor(); 33 | 34 | /*************** 35 | class Eventable 36 | ***************/ 37 | 38 | class Eventable { 39 | public: 40 | Eventable() {} 41 | virtual ~Eventable() {} 42 | 43 | unsigned long Signature; 44 | 45 | // Called by the framework 46 | virtual void ReceiveData (const char *data, int length) {} 47 | virtual void ConnectionCompleted() {} 48 | virtual void Accept (const unsigned long) {} 49 | virtual void Unbind() {} 50 | virtual void PostInit() {} 51 | virtual void SslHandshakeCompleted() {} 52 | 53 | void StopReactor() {EM::StopReactor();} 54 | }; 55 | 56 | /**************** 57 | class Connection 58 | ****************/ 59 | 60 | class Connection: public Eventable { 61 | public: 62 | Connection() {} 63 | virtual ~Connection() {} 64 | 65 | virtual void Connect (const char*, int); 66 | virtual void BindConnect (const char *, int, const char*, int); 67 | 68 | void SendData (const char *data); 69 | void SendData (const char *data, int length); 70 | void Close (bool afterWriting); 71 | }; 72 | 73 | 74 | /************** 75 | class Acceptor 76 | **************/ 77 | 78 | class Acceptor: public Eventable { 79 | public: 80 | Acceptor() {PostInit();} 81 | virtual ~Acceptor() {} 82 | 83 | void Start (const char*, int); 84 | void Accept (const unsigned long); 85 | 86 | virtual Connection *MakeConnection() {return new Connection();} 87 | }; 88 | 89 | 90 | }; 91 | 92 | 93 | 94 | 95 | 96 | #endif // __EVMA_EventmachineCpp__H_ 97 | -------------------------------------------------------------------------------- /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 | #attr_accessor :receiver 49 | 50 | #-- 51 | # I know I'm missing something stupid, but the inside of class << s 52 | # can't see locally-bound values. It can see globals, though. 53 | def set_receiver blk 54 | $em______tmpglobal = blk 55 | class << self 56 | define_method :call, $em______tmpglobal.dup 57 | end 58 | end 59 | 60 | end 61 | 62 | class YieldBlockFromSpawnedProcess # :nodoc: 63 | def initialize block, notify 64 | @block = [block,notify] 65 | end 66 | def pull_out_yield_block 67 | @block 68 | end 69 | end 70 | 71 | # Spawn an erlang-style process 72 | def self.spawn &block 73 | s = SpawnedProcess.new 74 | s.set_receiver block 75 | s 76 | end 77 | 78 | def self.yield &block # :nodoc: 79 | return YieldBlockFromSpawnedProcess.new( block, false ) 80 | end 81 | 82 | def self.yield_and_notify &block # :nodoc: 83 | return YieldBlockFromSpawnedProcess.new( block, true ) 84 | end 85 | end 86 | -------------------------------------------------------------------------------- /lib/em/delegate_connection.rb: -------------------------------------------------------------------------------- 1 | require 'socket' 2 | 3 | module EventMachine 4 | # A simple delegate provider that will pass on all callbacks the connection 5 | # receives to the given delegate object, prepended with a reference to 6 | # itself. 7 | # 8 | # Example Usage: 9 | # 10 | # The following is an example of a central logging delegate. Only one 11 | # instance of this delegate needs to exist, as such users may more easily 12 | # aggregate less stated connection events. 13 | # 14 | # my_delegate = Class.new do 15 | # def method_missing(name, connection, *args) 16 | # puts [ 17 | # Time.now, connection.signature, connection.ip_port, name, *args 18 | # ].inspect 19 | # end 20 | # end 21 | # EM.connect(host, port, DelegateConnection, my_delegate) 22 | # 23 | # A Note on the Init Callback: 24 | # 25 | # Delegates must respond to the callback methods or an error will be 26 | # raised at runtime. One additional callback has been added from initialize, 27 | # in order to not clobber the API from normal ruby class construction, when 28 | # a new connection is initialized #init will be called on the delegate. 29 | class DelegateConnection < Connection 30 | 31 | # The given delegate will be used, init will be called each time a new 32 | # connection is opened, passing in the connection object. 33 | def initialize(delegate) 34 | @delegate = delegate 35 | @delegate.init(self) 36 | @ip, @port = nil, nil 37 | end 38 | 39 | # Retreive the connections ip and port from the reactor. 40 | def address_tuple 41 | @port, @ip = Socket.unpack_sockaddr_in(get_peername) 42 | end 43 | 44 | # The remote IP. 45 | def ip 46 | @ip || address_tuple && @ip 47 | end 48 | 49 | # The remote Port. 50 | def port 51 | @port || address_tuple && @port 52 | end 53 | 54 | # A consistent, frozen string containing ip:port, well suited for a hash 55 | # key. 56 | def ip_port 57 | @ip_port ||= "#{@ip}:#{@port}".freeze 58 | end 59 | 60 | def post_init 61 | # TODO pass in connection details 62 | @delegate.post_init(self) 63 | end 64 | 65 | def receive_data(data) 66 | @delegate.receive_data(self, data) 67 | end 68 | 69 | def ssl_handshake_completed 70 | @delegate.ssl_handshake_completed(self) 71 | end 72 | 73 | def ssl_verify_peer(cert) 74 | @delegate.ssl_verify_peer(self, cert) 75 | end 76 | 77 | def unbind 78 | @delegate.unbind(self) 79 | end 80 | 81 | def proxy_target_unbound 82 | @delegate.proxy_target_unbound(self) 83 | end 84 | 85 | def connection_completed 86 | @delegate.connection_completed(self) 87 | end 88 | end 89 | end -------------------------------------------------------------------------------- /tests/test_smtpserver.rb: -------------------------------------------------------------------------------- 1 | # $Id$ 2 | # 3 | # Author:: Francis Cianfrocca (gmail: blackhedd) 4 | # Homepage:: http://rubyeventmachine.com 5 | # Date:: 8 April 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 | $:.unshift "../lib" 28 | require 'eventmachine' 29 | require 'test/unit' 30 | 31 | class TestSmtpServer < Test::Unit::TestCase 32 | 33 | # Don't test on port 25. It requires superuser and there's probably 34 | # a mail server already running there anyway. 35 | Localhost = "127.0.0.1" 36 | Localport = 25001 37 | 38 | # This class is an example of what you need to write in order 39 | # to implement a mail server. You override the methods you are 40 | # interested in. Some, but not all, of these are illustrated here. 41 | # 42 | class Mailserver < EM::Protocols::SmtpServer 43 | 44 | attr_reader :my_msg_body, :my_sender, :my_recipients 45 | 46 | def initialize *args 47 | super 48 | end 49 | def receive_sender sender 50 | @my_sender = sender 51 | #p sender 52 | true 53 | end 54 | def receive_recipient rcpt 55 | @my_recipients ||= [] 56 | @my_recipients << rcpt 57 | true 58 | end 59 | def receive_data_chunk c 60 | @my_msg_body = c.last 61 | end 62 | def connection_ended 63 | EM.stop 64 | end 65 | end 66 | 67 | def test_mail 68 | c = nil 69 | EM.run { 70 | EM.start_server( Localhost, Localport, Mailserver ) {|conn| c = conn} 71 | EM::Timer.new(2) {EM.stop} # prevent hanging the test suite in case of error 72 | EM::Protocols::SmtpClient.send :host=>Localhost, 73 | :port=>Localport, 74 | :domain=>"bogus", 75 | :from=>"me@example.com", 76 | :to=>"you@example.com", 77 | :header=> {"Subject"=>"Email subject line", "Reply-to"=>"me@example.com"}, 78 | :body=>"Not much of interest here." 79 | 80 | } 81 | assert_equal( "Not much of interest here.", c.my_msg_body ) 82 | assert_equal( "", c.my_sender ) 83 | assert_equal( [""], c.my_recipients ) 84 | end 85 | end 86 | -------------------------------------------------------------------------------- /tests/test_server.rb: -------------------------------------------------------------------------------- 1 | require "test/unit" 2 | 3 | require "eventmachine" 4 | 5 | class TestEventmachineServer < Test::Unit::TestCase 6 | include EM::Test 7 | 8 | attr_reader :localhost, :port 9 | 10 | def setup 11 | @localhost, @port = '127.0.0.1', 10000 + rand(1000) 12 | end 13 | 14 | def test_initialize 15 | assert EM::Server.new(localhost, port) 16 | end 17 | 18 | def test_listen_with_module 19 | go do 20 | EM::Server.new(localhost, port, UnbindStopper).listen 21 | EM.connect localhost, port, ImmediateCloser 22 | end 23 | end 24 | 25 | def test_listen_with_class 26 | klass = Class.new(EM::Connection) { include UnbindStopper } 27 | go do 28 | EM::Server.new(localhost, port, klass).listen 29 | EM.connect localhost, port, ImmediateCloser 30 | end 31 | end 32 | 33 | def test_listen_with_block 34 | go do 35 | server = EM::Server.new(localhost, port) { include UnbindStopper } 36 | server.listen 37 | EM.connect localhost, port, ImmediateCloser 38 | end 39 | end 40 | 41 | def test_listen_without_reactor_running_schedules 42 | EM::Server.new(localhost, port, UnbindStopper).listen 43 | go { EM.connect localhost, port, ImmediateCloser } 44 | end 45 | 46 | def test_run 47 | server = EM::Server.new(localhost, port, UnbindStopper).listen 48 | job { EM.connect localhost, port, ImmediateCloser } 49 | server.run 50 | end 51 | 52 | def test_stop 53 | server = EM::Server.new(localhost, port, EM::Connection).listen.stop 54 | # We have to belay this a few ticks to allow the machine to asynchronously 55 | # complete the closing of the listen socket 56 | go { in_ticks(5) { EM.connect(localhost, port, UnbindStopper) } } 57 | end 58 | 59 | class Aggregator 60 | attr_reader :table 61 | 62 | def initialize 63 | @table = Hash.new { |h,k| h[k] = [] } 64 | end 65 | 66 | def method_missing(name, *args) 67 | @table[name] << args 68 | end 69 | end 70 | 71 | def test_delegate_connection 72 | aggregator = Aggregator.new 73 | server = EM::Server.new(localhost, port, aggregator).listen 74 | 2.times do 75 | job { EM.connect localhost, port, ImmediateCloser } 76 | end 77 | go { EM.connect localhost, port, ClosingStopper } 78 | assert_equal 3, aggregator.table[:init].size 79 | assert_equal 3, aggregator.table[:unbind].size 80 | end 81 | 82 | module ArgCaller 83 | def initialize(*args) 84 | args.each { |a| a.call } 85 | end 86 | end 87 | 88 | def test_delegate_arguments 89 | arg1_ran, arg2_ran = false, false 90 | arg1, arg2 = lambda { arg1_ran = true }, lambda { arg2_ran = true } 91 | server = EM::Server.new(localhost, port, EM::DelegateConnection, arg1, arg2) 92 | go { EM.connect localhost, port, ClosingStopper } 93 | end 94 | end -------------------------------------------------------------------------------- /docs/COPYING: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /java/src/com/rubyeventmachine/tests/ApplicationTest.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 | 31 | package com.rubyeventmachine.tests; 32 | 33 | 34 | import org.junit.After; 35 | import org.junit.AfterClass; 36 | import org.junit.Before; 37 | import org.junit.BeforeClass; 38 | import org.junit.Test; 39 | import org.junit.Assert; 40 | import java.net.*; 41 | import java.io.*; 42 | import java.nio.*; 43 | 44 | import com.rubyeventmachine.*; 45 | 46 | public class ApplicationTest { 47 | 48 | @BeforeClass 49 | public static void setUpBeforeClass() throws Exception { 50 | } 51 | 52 | @AfterClass 53 | public static void tearDownAfterClass() throws Exception { 54 | } 55 | 56 | @Before 57 | public void setUp() throws Exception { 58 | } 59 | 60 | @After 61 | public void tearDown() throws Exception { 62 | } 63 | 64 | @Test 65 | public void testRunnableArgument() { 66 | final Application a = new Application(); 67 | a.run (new Runnable() { 68 | public void run() { 69 | a.stop(); 70 | } 71 | }); 72 | } 73 | 74 | 75 | 76 | class F implements ConnectionFactory { 77 | public Connection connection() { 78 | return new Connection() { 79 | public void receiveData (ByteBuffer bb) { 80 | application.stop(); 81 | } 82 | }; 83 | } 84 | 85 | }; 86 | 87 | @Test 88 | public void testTcpServer() throws EmReactorException { 89 | final Application a = new Application(); 90 | final SocketAddress saddr = new InetSocketAddress ("127.0.0.1", 9008); 91 | a.run (new Runnable() { 92 | public void run() { 93 | try { 94 | a.startServer (saddr, new F()); 95 | } catch (EmReactorException e) { Assert.fail(); } 96 | new Thread() { 97 | public void run() { 98 | try { 99 | Socket s = new Socket ("127.0.0.1", 9008); 100 | s.getOutputStream().write(new String ("boo").getBytes()); 101 | } catch (UnknownHostException e) { 102 | } catch (IOException e) {} 103 | } 104 | }.start(); 105 | } 106 | }); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /tasks/project.rake: -------------------------------------------------------------------------------- 1 | require 'rake/gempackagetask' 2 | require 'rake/rdoctask' 3 | require 'rake/clean' 4 | 5 | # monkey bitchin' for windows stuffs... 6 | module FileUtils 7 | # If any of these methods ever clobber, try removing them. 8 | # Hopefully they'll do something semantically similar. 9 | abort "Err: #{__FILE__}:#{__LINE__} monkey patch windows? clobbers!" unless instance_methods.grep(/windows\?/).empty? 10 | abort "Err: #{__FILE__}:#{__LINE__} monkey patch sudo clobbers!" unless instance_methods.grep(/sudo/).empty? 11 | abort "Err: #{__FILE__}:#{__LINE__} monkey patch gem_cmd clobbers!" unless instance_methods.grep(/gem_cmd/).empty? 12 | def windows?; RUBY_PLATFORM =~ /mswin|mingw/; end 13 | def sudo(cmd) 14 | if windows? || (require 'etc'; Etc.getpwuid.uid == 0) 15 | sh cmd 16 | else 17 | sh "sudo #{cmd}" 18 | end 19 | end 20 | def gem_cmd(action, name, *args) 21 | rb = Gem.ruby rescue nil 22 | rb ||= (require 'rbconfig'; File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name'])) 23 | sudo "#{rb} -r rubygems -e 'require %{rubygems/gem_runner}; Gem::GemRunner.new.run(%w{#{action} #{name} #{args.join(' ')}})'" 24 | end 25 | end 26 | 27 | 28 | # Setup our packaging tasks, we're just jacking the builtins. 29 | Rake::GemPackageTask.new(Spec) do |pkg| 30 | pkg.need_tar, pkg.need_tar_gz, pkg.need_zip = true, true, true if Package 31 | pkg.gem_spec = Spec 32 | end 33 | Rake::Task[:clean].enhance [:clobber_package] 34 | 35 | # Only generate rdoc if the spec says so, again, jack the builtins. 36 | if Spec.has_rdoc 37 | Rake::RDocTask.new do |rd| 38 | rd.title = Spec.name 39 | rd.rdoc_dir = 'rdoc' 40 | rd.main = "README" 41 | rd.rdoc_files.include("lib/**/*.rb", *Spec.extra_rdoc_files) 42 | rd.rdoc_files.exclude(*%w(lib/em/version lib/emva lib/evma/ lib/pr_eventmachine lib/jeventmachine)) 43 | end 44 | Rake::Task[:clean].enhance [:clobber_rdoc] 45 | 46 | desc 'Generate and open documentation' 47 | task :docs => :rdoc do 48 | case RUBY_PLATFORM 49 | when /darwin/ ; sh 'open rdoc/index.html' 50 | when /mswin|mingw/ ; sh 'start rdoc\index.html' 51 | else 52 | sh 'firefox rdoc/index.html' 53 | end 54 | end 55 | end 56 | 57 | if Spec.default_executable 58 | desc "Run #{Spec.default_executable}" 59 | task :run do ruby File.join(Spec.bindir, Spec.default_executable) end 60 | end 61 | 62 | require 'rubygems' 63 | 64 | desc 'Install gem (and sudo if required)' 65 | task :install => :package do 66 | gem_cmd(:install, "pkg/#{Spec.name}-#{Spec.version}.gem") 67 | end 68 | 69 | desc 'Uninstall gem (and sudo if required)' 70 | task :uninstall do 71 | gem_cmd(:uninstall, "#{Spec.name}", "-v=#{Spec.version}") 72 | end 73 | 74 | # Find an scm's store directory, if we do, make a task to commit to it only 75 | # after running all the tests (successfully). 76 | if scm = %w(git svn bzr hg).find { |d| File.directory? ".#{d}" } 77 | desc "Run tests then commit to #{scm}" 78 | task :commit => :test do sh "#{scm} commit" end 79 | end -------------------------------------------------------------------------------- /tests/test_delegate_connection.rb: -------------------------------------------------------------------------------- 1 | require "test/unit" 2 | require "eventmachine" 3 | 4 | class TestDelegateConnection < Test::Unit::TestCase 5 | include EM::Test 6 | 7 | class Aggregator 8 | attr_reader :table 9 | 10 | def initialize 11 | @table = Hash.new { |h,k| h[k] = [] } 12 | end 13 | 14 | def method_missing(name, *args) 15 | @table[name] << args 16 | end 17 | end 18 | 19 | def test_delegate_connection_with_server 20 | localhost, port = '127.0.0.1', 10000+rand(500) 21 | aggregator = Aggregator.new 22 | server = EM::Server.new(localhost, port, aggregator).listen 23 | 2.times do 24 | job { EM.connect localhost, port, ImmediateCloser } 25 | end 26 | go { EM.connect localhost, port, ClosingStopper } 27 | assert_equal 3, aggregator.table[:init].size 28 | assert_equal 3, aggregator.table[:unbind].size 29 | end 30 | 31 | def test_parameterless_callbacks 32 | signature = '12345678' 33 | 34 | tracker = Aggregator.new 35 | 36 | # EM::Connection.new overrides new, and requires a signature parameter 37 | conn = EM::DelegateConnection.new(signature, tracker) 38 | 39 | assert_equal 1, tracker.table[:init].size 40 | 41 | # Automatically called by EM::Connection.new 42 | assert_equal 1, tracker.table[:post_init].size 43 | 44 | parameterless_callbacks = %w( 45 | ssl_handshake_completed unbind proxy_target_unbound connection_completed 46 | ) 47 | parameterless_callbacks.each do |method| 48 | conn.__send__ method 49 | assert_equal 1, tracker.table[method.to_sym].size 50 | end 51 | 52 | end 53 | 54 | def test_parametered_callbacks 55 | signature = '12345678' 56 | 57 | tracker = Aggregator.new 58 | 59 | # EM::Connection.new overrides new, and requires a signature parameter 60 | conn = EM::DelegateConnection.new(signature, tracker) 61 | 62 | conn.receive_data("rubyfoo") 63 | assert_equal "rubyfoo", tracker.table[:receive_data].first.last 64 | 65 | conn.ssl_verify_peer("some cert") 66 | assert_equal "some cert", tracker.table[:ssl_verify_peer].first.last 67 | end 68 | 69 | def test_ip_port 70 | localhost, port = '127.0.0.1', 10000+rand(500) 71 | aggregator = Aggregator.new 72 | server = EM::Server.new(localhost, port, aggregator).listen 73 | 74 | assertions = lambda { 75 | assert_equal localhost, aggregator.table[:init].first.first.ip 76 | assert_kind_of Integer, aggregator.table[:init].first.first.port 77 | ip = localhost 78 | prt = aggregator.table[:init].first.first.port 79 | assert_equal "#{ip}:#{prt}", aggregator.table[:init].first.first.ip_port 80 | } 81 | 82 | go do 83 | EM.connect localhost, port, Module.new { 84 | define_method(:connection_completed) do 85 | # Allow reactor callbacks for server side of connection by waiting 86 | # another tick. 87 | EM.next_tick { 88 | assertions.call 89 | EM.stop 90 | } 91 | end 92 | } 93 | end 94 | end 95 | end -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /tests/test_attach.rb: -------------------------------------------------------------------------------- 1 | # $Id$ 2 | # 3 | #---------------------------------------------------------------------------- 4 | # 5 | # Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved. 6 | # Gmail: blackhedd 7 | # 8 | # This program is free software; you can redistribute it and/or modify 9 | # it under the terms of either: 1) the GNU General Public License 10 | # as published by the Free Software Foundation; either version 2 of the 11 | # License, or (at your option) any later version; or 2) Ruby's License. 12 | # 13 | # See the file COPYING for complete licensing information. 14 | # 15 | #--------------------------------------------------------------------------- 16 | # 17 | 18 | $:.unshift "../lib" 19 | require 'eventmachine' 20 | require 'socket' 21 | require 'test/unit' 22 | 23 | 24 | class TestAttach < Test::Unit::TestCase 25 | 26 | Host = "127.0.0.1" 27 | Port = 9550 28 | 29 | class EchoServer < EM::Connection 30 | def receive_data data 31 | send_data data 32 | end 33 | end 34 | 35 | class EchoClient < EM::Connection 36 | def initialize 37 | self.notify_readable = true 38 | $sock.write("abc\n") 39 | end 40 | 41 | def notify_readable 42 | $read = $sock.readline 43 | $fd = detach 44 | end 45 | 46 | def unbind 47 | EM.next_tick do 48 | $sock.write("def\n") 49 | EM.add_timer(0.5){ EM.stop } 50 | end 51 | end 52 | end 53 | 54 | def test_attach 55 | EM.run{ 56 | EM.start_server Host, Port, EchoServer 57 | $sock = TCPSocket.new Host, Port 58 | EM.watch $sock, EchoClient 59 | } 60 | 61 | assert_equal $read, "abc\n" 62 | unless defined? JRuby # jruby filenos are not real 63 | assert_equal $fd, $sock.fileno 64 | end 65 | assert_equal false, $sock.closed? 66 | assert_equal $sock.readline, "def\n" 67 | end 68 | 69 | module PipeWatch 70 | def notify_readable 71 | $read = $r.readline 72 | EM.stop 73 | end 74 | end 75 | 76 | def test_attach_pipe 77 | EM.run{ 78 | $r, $w = IO.pipe 79 | EM.watch $r, PipeWatch do |c| 80 | c.notify_readable = true 81 | end 82 | $w.write("ghi\n") 83 | } 84 | 85 | assert_equal $read, "ghi\n" 86 | end 87 | 88 | def test_set_readable 89 | EM.run{ 90 | $r, $w = IO.pipe 91 | c = EM.watch $r, PipeWatch do |c| 92 | c.notify_readable = false 93 | end 94 | 95 | EM.next_tick{ 96 | $before = c.notify_readable? 97 | c.notify_readable = true 98 | $after = c.notify_readable? 99 | } 100 | 101 | $w.write("jkl\n") 102 | } 103 | 104 | assert !$before 105 | assert $after 106 | assert_equal $read, "jkl\n" 107 | end 108 | 109 | module PipeReader 110 | def receive_data data 111 | $read = data 112 | EM.stop 113 | end 114 | end 115 | 116 | def test_read_write_pipe 117 | EM.run{ 118 | $r, $w = IO.pipe 119 | EM.attach $r, PipeReader 120 | writer = EM.attach($w) 121 | writer.send_data 'ghi' 122 | } 123 | 124 | assert_equal $read, "ghi" 125 | end 126 | end -------------------------------------------------------------------------------- /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 | // XXX use atomic_t to prevent thread-safety issues 35 | static unsigned long num = 0; 36 | while(BindingBag[++num]); 37 | return num; 38 | } 39 | 40 | #if 0 41 | string Bindable_t::CreateBinding() 42 | { 43 | static int index = 0; 44 | static string seed; 45 | 46 | if ((index >= 1000000) || (seed.length() == 0)) { 47 | #ifdef OS_UNIX 48 | int fd = open (DEV_URANDOM, O_RDONLY); 49 | if (fd < 0) 50 | throw std::runtime_error ("No entropy device"); 51 | 52 | unsigned char u[16]; 53 | size_t r = read (fd, u, sizeof(u)); 54 | if (r < sizeof(u)) 55 | throw std::runtime_error ("Unable to read entropy device"); 56 | 57 | unsigned char *u1 = (unsigned char*)u; 58 | char u2 [sizeof(u) * 2 + 1]; 59 | 60 | for (size_t i=0; i < sizeof(u); i++) 61 | sprintf (u2 + (i * 2), "%02x", u1[i]); 62 | 63 | seed = string (u2); 64 | #endif 65 | 66 | 67 | #ifdef OS_WIN32 68 | UUID uuid; 69 | UuidCreate (&uuid); 70 | unsigned char *uuidstring = NULL; 71 | UuidToString (&uuid, &uuidstring); 72 | if (!uuidstring) 73 | throw std::runtime_error ("Unable to read uuid"); 74 | seed = string ((const char*)uuidstring); 75 | 76 | RpcStringFree (&uuidstring); 77 | #endif 78 | 79 | index = 0; 80 | 81 | 82 | } 83 | 84 | stringstream ss; 85 | ss << seed << (++index); 86 | return ss.str(); 87 | } 88 | #endif 89 | 90 | /***************************** 91 | STATIC: Bindable_t::GetObject 92 | *****************************/ 93 | 94 | Bindable_t *Bindable_t::GetObject (const unsigned long binding) 95 | { 96 | map::const_iterator i = BindingBag.find (binding); 97 | if (i != BindingBag.end()) 98 | return i->second; 99 | else 100 | return NULL; 101 | } 102 | 103 | 104 | /********************** 105 | Bindable_t::Bindable_t 106 | **********************/ 107 | 108 | Bindable_t::Bindable_t() 109 | { 110 | Binding = Bindable_t::CreateBinding(); 111 | BindingBag [Binding] = this; 112 | } 113 | 114 | 115 | 116 | /*********************** 117 | Bindable_t::~Bindable_t 118 | ***********************/ 119 | 120 | Bindable_t::~Bindable_t() 121 | { 122 | BindingBag.erase (Binding); 123 | } 124 | 125 | 126 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /docs/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 | -------------------------------------------------------------------------------- /tests/test_processes.rb: -------------------------------------------------------------------------------- 1 | # $Id$ 2 | # 3 | # Author:: Francis Cianfrocca (gmail: blackhedd) 4 | # Homepage:: http://rubyeventmachine.com 5 | # Date:: 8 April 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 | $:.unshift "../lib" 28 | require 'eventmachine' 29 | require 'test/unit' 30 | 31 | class TestProcesses < Test::Unit::TestCase 32 | 33 | # EM::DeferrableChildProcess is a sugaring of a common use-case 34 | # involving EM::popen. 35 | # Call the #open method on EM::DeferrableChildProcess, passing 36 | # a command-string. #open immediately returns an EM::Deferrable 37 | # object. It also schedules the forking of a child process, which 38 | # will execute the command passed to #open. 39 | # When the forked child terminates, the Deferrable will be signalled 40 | # and execute its callbacks, passing the data that the child process 41 | # wrote to stdout. 42 | # 43 | def test_deferrable_child_process 44 | ls = "" 45 | EM.run { 46 | d = EM::DeferrableChildProcess.open( "ls -ltr" ) 47 | d.callback {|data_from_child| 48 | ls = data_from_child 49 | EM.stop 50 | } 51 | } 52 | assert( ls.length > 0) 53 | end 54 | 55 | def setup 56 | $out = nil 57 | $status = nil 58 | end 59 | 60 | def test_em_system 61 | EM.run{ 62 | EM.system('ls'){ |out,status| $out, $status = out, status; EM.stop } 63 | } 64 | 65 | assert( $out.length > 0 ) 66 | assert_equal($status.exitstatus, 0) 67 | assert_equal($status.class, Process::Status) 68 | end 69 | 70 | def test_em_system_pid 71 | $pids = [] 72 | 73 | EM.run{ 74 | $pids << EM.system('echo hi', proc{ |out,status|$pids << status.pid; EM.stop }) 75 | } 76 | 77 | assert_equal $pids[0], $pids[1] 78 | end 79 | 80 | def test_em_system_with_proc 81 | EM.run{ 82 | EM.system('ls', proc{ |out,status| $out, $status = out, status; EM.stop }) 83 | } 84 | 85 | assert( $out.length > 0 ) 86 | assert_equal($status.exitstatus, 0) 87 | assert_equal($status.class, Process::Status) 88 | end 89 | 90 | def test_em_system_with_two_procs 91 | EM.run{ 92 | EM.system('sh', proc{ |process| 93 | process.send_data("echo hello\n") 94 | process.send_data("exit\n") 95 | }, proc{ |out,status| 96 | $out = out 97 | $status = status 98 | EM.stop 99 | }) 100 | } 101 | 102 | assert_equal("hello\n", $out) 103 | end 104 | 105 | def test_em_system_cmd_arguments 106 | EM.run{ 107 | EM.system('sh', '--version', proc{ |process| 108 | }, proc{ |out,status| 109 | $out = out 110 | $status = status 111 | EM.stop 112 | }) 113 | } 114 | 115 | assert_match(/version/i, $out) 116 | end 117 | 118 | def test_em_system_spaced_arguments 119 | EM.run{ 120 | EM.system('ruby', '-e', 'puts "hello"', proc{ |out,status| 121 | $out = out 122 | EM.stop 123 | }) 124 | } 125 | 126 | assert_equal("hello\n", $out) 127 | end 128 | end 129 | -------------------------------------------------------------------------------- /tests/test_next_tick.rb: -------------------------------------------------------------------------------- 1 | # $Id$ 2 | # 3 | # Author:: Francis Cianfrocca (gmail: blackhedd) 4 | # Homepage:: http://rubyeventmachine.com 5 | # Date:: 8 April 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 | 28 | $:.unshift "../lib" 29 | require 'eventmachine' 30 | require 'test/unit' 31 | 32 | class TestNextTick < Test::Unit::TestCase 33 | 34 | def test_tick_arg 35 | pr = proc {EM.stop} 36 | EM.run { 37 | EM.next_tick pr 38 | } 39 | assert true 40 | end 41 | 42 | def test_tick_block 43 | EM.run { 44 | EM.next_tick {EM.stop} 45 | } 46 | assert true 47 | end 48 | 49 | # This illustrates the solution to a long-standing problem. 50 | # It's now possible to correctly nest calls to EM#run. 51 | # See the source code commentary for EM#run for more info. 52 | # 53 | def test_run_run 54 | EM.run { 55 | EM.run { 56 | EM.next_tick {EM.stop} 57 | } 58 | } 59 | end 60 | 61 | def test_pre_run_queue 62 | x = false 63 | EM.next_tick { EM.stop; x = true } 64 | EM.run { EM.add_timer(0.2) { EM.stop } } 65 | assert x 66 | end 67 | 68 | def test_cleanup_after_stop 69 | x = true 70 | EM.run{ 71 | EM.next_tick{ 72 | EM.stop 73 | EM.next_tick{ x=false } 74 | } 75 | } 76 | EM.run{ 77 | EM.next_tick{ EM.stop } 78 | } 79 | assert x 80 | end 81 | 82 | # We now support an additional parameter for EM#run. 83 | # You can pass two procs to EM#run now. The first is executed as the normal 84 | # run block. The second (if given) is scheduled for execution after the 85 | # reactor loop completes. 86 | # The reason for supporting this is subtle. There has always been an expectation 87 | # that EM#run doesn't return until after the reactor loop ends. But now it's 88 | # possible to nest calls to EM#run, which means that a nested call WILL 89 | # RETURN. In order to write code that will run correctly either way, it's 90 | # recommended to put any code which must execute after the reactor completes 91 | # in the second parameter. 92 | # 93 | def test_run_run_2 94 | a = proc {EM.stop} 95 | b = proc {assert true} 96 | EM.run a, b 97 | end 98 | 99 | 100 | # This illustrates that EM#run returns when it's called nested. 101 | # This isn't a feature, rather it's something to be wary of when writing code 102 | # that must run correctly even if EM#run is called while a reactor is already 103 | # running. 104 | def test_run_run_3 105 | a = [] 106 | EM.run { 107 | EM.run proc {EM.stop}, proc {a << 2} 108 | a << 1 109 | } 110 | assert_equal( [1,2], a ) 111 | end 112 | 113 | 114 | def test_schedule_on_reactor_thread 115 | x = false 116 | EM.run do 117 | EM.schedule { x = true } 118 | EM.stop 119 | end 120 | assert x 121 | end 122 | 123 | def test_schedule_from_thread 124 | x = false 125 | EM.run do 126 | Thread.new { EM.schedule { x = true } }.join 127 | assert !x 128 | EM.next_tick { EM.stop } 129 | end 130 | assert x 131 | end 132 | 133 | end 134 | -------------------------------------------------------------------------------- /docs/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 | -------------------------------------------------------------------------------- /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 | typedef int SOCKET; 59 | #define closesocket close 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 | #endif /* OS_UNIX */ 82 | 83 | #ifdef OS_WIN32 84 | // 21Sep09: windows limits select() to 64 sockets by default, we increase it to 1024 here (before including winsock2.h) 85 | #define FD_SETSIZE 1024 86 | 87 | #define WIN32_LEAN_AND_MEAN 88 | #include 89 | #include 90 | #include 91 | #include 92 | #include 93 | #include 94 | 95 | typedef int socklen_t; 96 | typedef int pid_t; 97 | #endif 98 | 99 | 100 | using namespace std; 101 | 102 | #ifdef WITH_SSL 103 | #include 104 | #include 105 | #endif 106 | 107 | #ifdef HAVE_EPOLL 108 | #include 109 | #endif 110 | 111 | #ifdef HAVE_KQUEUE 112 | #include 113 | #include 114 | #endif 115 | 116 | #ifdef HAVE_INOTIFY 117 | #include 118 | #endif 119 | 120 | #ifdef HAVE_OLD_INOTIFY 121 | #include 122 | #include 123 | static inline int inotify_init (void) { return syscall (__NR_inotify_init); } 124 | static inline int inotify_add_watch (int fd, const char *name, __u32 mask) { return syscall (__NR_inotify_add_watch, fd, name, mask); } 125 | static inline int inotify_rm_watch (int fd, __u32 wd) { return syscall (__NR_inotify_rm_watch, fd, wd); } 126 | #define HAVE_INOTIFY 1 127 | #endif 128 | 129 | #ifdef HAVE_INOTIFY 130 | #define INOTIFY_EVENT_SIZE (sizeof(struct inotify_event)) 131 | #endif 132 | 133 | #ifdef HAVE_WRITEV 134 | #include 135 | #endif 136 | 137 | #include "binder.h" 138 | #include "em.h" 139 | #include "epoll.h" 140 | #include "sigs.h" 141 | #include "ed.h" 142 | #include "files.h" 143 | #include "page.h" 144 | #include "ssl.h" 145 | #include "eventmachine.h" 146 | #include "eventmachine_cpp.h" 147 | 148 | 149 | 150 | 151 | #endif // __Project__H_ 152 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | = RUBY/EventMachine 2 | 3 | Homepage:: http://rubyeventmachine.com 4 | Rubyforge Page:: http://rubyforge.org/projects/eventmachine 5 | Google Group:: http://groups.google.com/group/eventmachine 6 | Mailing List:: http://rubyforge.org/pipermail/eventmachine-talk 7 | RDoc:: http://eventmachine.rubyforge.org 8 | IRC:: ##eventmachine on irc.freenode.net 9 | Copyright:: (C) 2006-07 by Francis Cianfrocca. All Rights Reserved. 10 | Email:: gmail address: garbagecat10 11 | 12 | EventMachine is copyrighted free software made available under the terms 13 | of either the GPL or Ruby's License. See the file COPYING for full licensing 14 | information. 15 | See EventMachine and EventMachine::Connection for documentation and 16 | usage examples. 17 | 18 | EventMachine implements a fast, single-threaded engine for arbitrary network 19 | communications. It's extremely easy to use in Ruby. EventMachine wraps all 20 | interactions with IP sockets, allowing programs to concentrate on the 21 | implementation of network protocols. It can be used to create both network 22 | servers and clients. To create a server or client, a Ruby program only needs 23 | to specify the IP address and port, and provide a Module that implements the 24 | communications protocol. Implementations of several standard network protocols 25 | are provided with the package, primarily to serve as examples. The real goal 26 | of EventMachine is to enable programs to easily interface with other programs 27 | using TCP/IP, especially if custom protocols are required. 28 | 29 | A Ruby program uses EventMachine by registering the addresses and ports of 30 | network servers and clients, and then entering an event-handling loop. 31 | EventMachine contains glue code in Ruby which will execute callbacks to 32 | user-supplied code for all significant events occurring in the clients 33 | and servers. These events include connection acceptance, startup, data-receipt, 34 | shutdown, and timer events. Arbitrary processing can be performed by user code 35 | during event callbacks, including sending data to one or more remote network 36 | peers, startup and shutdown of network connections, and installation of new 37 | event handlers. 38 | 39 | The EventMachine implements a very familiar model for network programming. 40 | It emphasizes: 1) the maximum possible isolation of user code from network 41 | objects like sockets; 2) maximum performance and scalability; and 3) extreme 42 | ease-of-use for user code. It attempts to provide a higher-level interface 43 | than similar projects which expose a variety of low-level event-handling 44 | and networking objects to Ruby programs. 45 | 46 | The design and implementation of EventMachine grows out of nearly ten years 47 | of experience writing high-performance, high-scaling network server applications. 48 | We have taken particular account of the challenges and lessons described as 49 | the "C10K problem" by Dan Kegel and others. 50 | 51 | EventMachine consists of an extension library written in C++ (which can be 52 | accessed from languages other than Ruby), and a Ruby module which can be dropped 53 | into user programs. On most platforms, EventMachine uses the 54 | select(2) system call, 55 | so it will run on a large range of Unix-like systems and on Microsoft 56 | Windows with good performance and scalability. On Linux 2.6 kernels, EventMachine 57 | automatically configures itself to use epoll(4) instead of 58 | select(2), so scalability on that platform can be significantly 59 | improved. 60 | 61 | Here's a fully-functional echo server written with EventMachine: 62 | 63 | require 'eventmachine' 64 | 65 | module EchoServer 66 | def post_init 67 | puts "-- someone connected to the echo server!" 68 | end 69 | 70 | def receive_data data 71 | send_data ">>>you sent: #{data}" 72 | close_connection if data =~ /quit/i 73 | end 74 | 75 | def unbind 76 | puts "-- someone disconnected from the echo server!" 77 | end 78 | end 79 | 80 | EventMachine::run { 81 | EventMachine::start_server "127.0.0.1", 8081, EchoServer 82 | } -------------------------------------------------------------------------------- /java/src/com/rubyeventmachine/tests/ConnectTest.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.tests; 31 | 32 | 33 | import org.junit.After; 34 | import org.junit.AfterClass; 35 | import org.junit.Before; 36 | import org.junit.BeforeClass; 37 | import org.junit.Test; 38 | import java.io.*; 39 | import java.nio.*; 40 | import java.nio.channels.*; 41 | 42 | import com.rubyeventmachine.*; 43 | 44 | public class ConnectTest { 45 | 46 | @BeforeClass 47 | public static void setUpBeforeClass() throws Exception { 48 | } 49 | 50 | @AfterClass 51 | public static void tearDownAfterClass() throws Exception { 52 | } 53 | 54 | @Before 55 | public void setUp() throws Exception { 56 | } 57 | 58 | @After 59 | public void tearDown() throws Exception { 60 | } 61 | 62 | @Test 63 | public final void test1() throws IOException, ClosedChannelException { 64 | Application a = new Application(); 65 | a.addTimer(0, new Timer() { 66 | public void fire() { 67 | application.connect("www.bayshorenetworks.com", 80, new Connection() { 68 | public void connectionCompleted() { 69 | close(); 70 | } 71 | public void unbind() { 72 | application.stop(); 73 | } 74 | }); 75 | } 76 | }); 77 | a.run(); 78 | } 79 | 80 | @Test 81 | public final void test2() throws IOException { 82 | class Bays extends Connection { 83 | public void connectionCompleted() { 84 | sendData (ByteBuffer.wrap( new String ("GET / HTTP/1.1\r\nHost: _\r\n\r\n").getBytes())); 85 | } 86 | public void receiveData (ByteBuffer b) { 87 | System.out.println (new String(b.array())); 88 | application.stop(); 89 | } 90 | }; 91 | 92 | Application a = new Application(); 93 | a.addTimer(0, new Timer() { 94 | public void fire() { 95 | application.connect("www.bayshorenetworks.com", 80, new Bays()); 96 | } 97 | }); 98 | a.run(); 99 | } 100 | 101 | public final void testBindConnect() throws IOException { 102 | class Server extends Connection { 103 | public void postInit() { 104 | // TODO: get peername here and check if the port is 33333 105 | // doesnt seem like peername is impl yet? 106 | System.out.println("post init!"); 107 | } 108 | }; 109 | 110 | Application a = new Application(); 111 | a.addTimer(0, new Timer() { 112 | public void fire() { 113 | application.startServer(new InetSocketAddress("localhost", 20000), new Server()); 114 | } 115 | }); 116 | a.addTimer(500, new Timer() { 117 | public void fire() { 118 | application.bindConnect("localhost", 33333, "localhost", 20000); 119 | } 120 | }); 121 | 122 | a.run(); 123 | } 124 | 125 | class C1 extends Connection { 126 | Application application; 127 | public C1 (Application a) { 128 | application = a; 129 | } 130 | public void postInit() { 131 | application.stop(); 132 | } 133 | } 134 | @Test 135 | public final void test3() { 136 | final Application a = new Application(); 137 | a.run (new Runnable() { 138 | public void run() { 139 | a.connect("www.bayshorenetworks.com", 80, new C1(a)); 140 | } 141 | }); 142 | } 143 | 144 | 145 | 146 | } 147 | -------------------------------------------------------------------------------- /tests/test_pure.rb: -------------------------------------------------------------------------------- 1 | # $Id$ 2 | # 3 | # Author:: Francis Cianfrocca (gmail: blackhedd) 4 | # Homepage:: http://rubyeventmachine.com 5 | # Date:: 8 April 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 | $:.unshift "../lib" 28 | require 'eventmachine' 29 | require 'test/unit' 30 | 31 | class TestPure < Test::Unit::TestCase 32 | 33 | 34 | Host,Port = "0.0.0.0", 9060 35 | 36 | 37 | # These tests are intended to exercise problems that come up in the 38 | # pure-Ruby implementation. However, we DON'T constrain them such that 39 | # they only run in pure-Ruby. These tests need to work identically in 40 | # any implementation. 41 | 42 | def setup 43 | end 44 | 45 | def teardown 46 | end 47 | 48 | #------------------------------------- 49 | 50 | # The EM reactor needs to run down open connections and release other resources 51 | # when it stops running. Make sure this happens even if user code throws a Ruby 52 | # exception. 53 | # One way to see this is to run identical tests that open a TCP server and throw 54 | # an exception. (We do this twice because an exception aborts a test. We make the 55 | # two tests identical except for the method name because we can't predict the order 56 | # in which the test harness will run them.) 57 | # If exception handling is incorrect, the second test will fail with a no-bind error 58 | # because the TCP server opened in the first test will not have been closed. 59 | # 60 | def run_exception 61 | EM.run { 62 | EM.start_server Host, Port 63 | raise "an exception" 64 | } 65 | end 66 | def test_exception_1 67 | assert_raises( RuntimeError ) { run_exception } 68 | end 69 | def test_exception_2 70 | ex_class = RUBY_PLATFORM == 'java' ? NativeException : RuntimeError 71 | assert_raises( ex_class ) { run_exception } 72 | end 73 | 74 | 75 | # Under some circumstances, the pure Ruby library would emit an Errno::ECONNREFUSED 76 | # exception on certain kinds of TCP connect-errors. 77 | # It's always been something of an open question whether EM should throw an exception 78 | # in these cases but the defined answer has always been to catch it the unbind method. 79 | # With a connect failure, the latter will always fire, but connection_completed will 80 | # never fire. So even though the point is arguable, it's incorrect for the pure Ruby 81 | # version to throw an exception. 82 | module TestConnrefused 83 | def unbind 84 | EM.stop 85 | end 86 | def connection_completed 87 | raise "should never get here" 88 | end 89 | end 90 | def test_connrefused 91 | EM.run { 92 | EM.connect "0.0.0.0", 60001, TestConnrefused 93 | } 94 | end 95 | 96 | 97 | # Make sure connection_completed gets called as expected with TCP clients. This is the 98 | # opposite of test_connrefused. 99 | # If the test fails, it will hang because EM.stop never gets called. 100 | # 101 | module TestConnaccepted 102 | def connection_completed 103 | EM.stop 104 | end 105 | end 106 | def test_connaccepted 107 | timeout = false 108 | EM.run { 109 | EM.start_server "0.0.0.0", 60002 110 | EM.connect "0.0.0.0", 60002, TestConnaccepted 111 | EM::Timer.new(1) {timeout = true; EM.stop} 112 | } 113 | assert_equal( false, timeout ) 114 | end 115 | 116 | def test_reactor_running 117 | a = false 118 | EM.run { 119 | a = EM.reactor_running? 120 | EM.next_tick {EM.stop} 121 | } 122 | assert a 123 | end 124 | 125 | end 126 | -------------------------------------------------------------------------------- /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 | def initialize # :nodoc: 43 | super 44 | @data = [] 45 | end 46 | 47 | # Sugars a common use-case involving forked child processes. 48 | # #open takes a String argument containing an shell command 49 | # string (including arguments if desired). #open immediately 50 | # returns an EventMachine::Deferrable object, without blocking. 51 | # 52 | # It also invokes EventMachine#popen to run the passed-in 53 | # command in a forked child process. 54 | # 55 | # When the forked child terminates, the Deferrable that 56 | # #open calls its callbacks, passing the data returned 57 | # from the child process. 58 | # 59 | def self.open cmd 60 | EventMachine.popen( cmd, DeferrableChildProcess ) 61 | end 62 | 63 | def receive_data data # :nodoc: 64 | @data << data 65 | end 66 | 67 | def unbind # :nodoc: 68 | succeed( @data.join ) 69 | end 70 | end 71 | 72 | class SystemCmd < EventMachine::Connection # :nodoc: 73 | def initialize cb 74 | @cb = cb 75 | @output = [] 76 | end 77 | def receive_data data 78 | @output << data 79 | end 80 | def unbind 81 | @cb.call @output.join(''), get_status if @cb 82 | end 83 | end 84 | 85 | # EM::system is a simple wrapper for EM::popen. It is similar to Kernel::system, but requires a 86 | # single string argument for the command and performs no shell expansion. 87 | # 88 | # The block or proc passed to EM::system is called with two arguments: the output generated by the command, 89 | # and a Process::Status that contains information about the command's execution. 90 | # 91 | # EM.run{ 92 | # EM.system('ls'){ |output,status| puts output if status.exitstatus == 0 } 93 | # } 94 | # 95 | # You can also supply an additional proc to send some data to the process: 96 | # 97 | # EM.run{ 98 | # EM.system('sh', proc{ |process| 99 | # process.send_data("echo hello\n") 100 | # process.send_data("exit\n") 101 | # }, proc{ |out,status| 102 | # puts(out) 103 | # }) 104 | # } 105 | # 106 | # Like EventMachine.popen, EventMachine.system currently does not work on windows. 107 | # It returns the pid of the spawned process. 108 | def EventMachine::system cmd, *args, &cb 109 | cb ||= args.pop if args.last.is_a? Proc 110 | init = args.pop if args.last.is_a? Proc 111 | 112 | # merge remaining arguments into the command 113 | cmd = ([cmd] + args.map{|a|a.to_s.dump}).join(' ') 114 | 115 | EM.get_subprocess_pid(EM.popen(cmd, SystemCmd, cb) do |c| 116 | init[c] if init 117 | end.signature) 118 | end 119 | end 120 | -------------------------------------------------------------------------------- /tests/test_timers.rb: -------------------------------------------------------------------------------- 1 | # $Id$ 2 | # 3 | # Author:: Francis Cianfrocca (gmail: blackhedd) 4 | # Homepage:: http://rubyeventmachine.com 5 | # Date:: 8 April 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 | 28 | $:.unshift "../lib" 29 | require 'eventmachine' 30 | require 'test/unit' 31 | 32 | class TestTimers < Test::Unit::TestCase 33 | 34 | def test_timer_with_block 35 | x = false 36 | EventMachine.run { 37 | EventMachine::Timer.new(0.25) { 38 | x = true 39 | EventMachine.stop 40 | } 41 | } 42 | assert x 43 | end 44 | 45 | def test_timer_with_proc 46 | x = false 47 | EventMachine.run { 48 | EventMachine::Timer.new(0.25, proc { 49 | x = true 50 | EventMachine.stop 51 | }) 52 | } 53 | assert x 54 | end 55 | 56 | def test_timer_cancel 57 | x = true 58 | EventMachine.run { 59 | timer = EventMachine::Timer.new(0.25, proc { x = false }) 60 | timer.cancel 61 | EventMachine::Timer.new(0.5, proc {EventMachine.stop}) 62 | } 63 | assert x 64 | end 65 | 66 | def test_periodic_timer 67 | x = 0 68 | EventMachine.run { 69 | EventMachine::PeriodicTimer.new(0.1) do 70 | x += 1 71 | EventMachine.stop if x == 4 72 | end 73 | } 74 | assert( x == 4 ) 75 | end 76 | 77 | def test_add_periodic_timer 78 | x = 0 79 | EM.run { 80 | t = EM.add_periodic_timer(0.1) do 81 | x += 1 82 | EM.stop if x == 4 83 | end 84 | assert t.respond_to?(:cancel) 85 | } 86 | end 87 | 88 | def test_periodic_timer_cancel 89 | x = 0 90 | EventMachine.run { 91 | pt = EventMachine::PeriodicTimer.new(0.25, proc { x += 1 }) 92 | pt.cancel 93 | EventMachine::Timer.new(0.5) {EventMachine.stop} 94 | } 95 | assert( x == 0 ) 96 | end 97 | 98 | def test_add_periodic_timer_cancel 99 | x = 0 100 | EventMachine.run { 101 | pt = EM.add_periodic_timer(0.25) { x += 1 } 102 | EM.cancel_timer(pt) 103 | EM.add_timer(0.5) { EM.stop } 104 | } 105 | assert( x == 0 ) 106 | end 107 | 108 | def test_periodic_timer_self_cancel 109 | x = 0 110 | EventMachine.run { 111 | pt = EventMachine::PeriodicTimer.new(0.1) { 112 | x += 1 113 | if x == 4 114 | pt.cancel 115 | EventMachine.stop 116 | end 117 | } 118 | } 119 | assert( x == 4 ) 120 | end 121 | 122 | 123 | # This test is only applicable to compiled versions of the reactor. 124 | # Pure ruby and java versions have no built-in limit on the number of outstanding timers. 125 | # 126 | def test_timer_change_max_outstanding 127 | ten_thousand_timers = proc { 128 | 10001.times { 129 | EM.add_timer(5) {} 130 | } 131 | } 132 | 133 | EM.run { 134 | if EM.library_type == :pure_ruby 135 | ten_thousand_timers.call 136 | elsif EM.library_type == :java 137 | ten_thousand_timers.call 138 | else 139 | begin 140 | assert_raises( RuntimeError ) { 141 | ten_thousand_timers.call 142 | } 143 | rescue Object 144 | p $! 145 | assert(false, $!.message) 146 | end 147 | end 148 | EM.stop 149 | } 150 | 151 | assert(!EM.reactor_running?, 'Reactor running when it should not be.') 152 | assert( EM.get_max_timers != 10001 ) 153 | EM.set_max_timers( 10001 ) 154 | assert( EM.get_max_timers == 10001 ) 155 | 156 | EM.run { 157 | ten_thousand_timers.call 158 | EM.stop 159 | } 160 | end 161 | 162 | end 163 | -------------------------------------------------------------------------------- /tests/test_httpclient2.rb: -------------------------------------------------------------------------------- 1 | # $Id$ 2 | # 3 | # Author:: Francis Cianfrocca (gmail: blackhedd) 4 | # Homepage:: http://rubyeventmachine.com 5 | # Date:: 8 April 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 | $:.unshift "../lib" 28 | require 'eventmachine' 29 | require 'test/unit' 30 | 31 | class TestHttpClient2 < Test::Unit::TestCase 32 | Localhost = "127.0.0.1" 33 | Localport = 9801 34 | 35 | def setup 36 | end 37 | 38 | def teardown 39 | end 40 | 41 | 42 | class TestServer < EM::Connection 43 | end 44 | 45 | # #connect returns an object which has made a connection to an HTTP server 46 | # and exposes methods for making HTTP requests on that connection. 47 | # #connect can take either a pair of parameters (a host and a port), 48 | # or a single parameter which is a Hash. 49 | # 50 | def test_connect 51 | EM.run { 52 | EM.start_server Localhost, Localport, TestServer 53 | http1 = EM::P::HttpClient2.connect Localhost, Localport 54 | http2 = EM::P::HttpClient2.connect( :host=>Localhost, :port=>Localport ) 55 | EM.stop 56 | } 57 | end 58 | 59 | 60 | def test_bad_port 61 | EM.run { 62 | EM.start_server Localhost, Localport, TestServer 63 | assert_raises( ArgumentError ) { 64 | EM::P::HttpClient2.connect Localhost, "xxx" 65 | } 66 | EM.stop 67 | } 68 | end 69 | 70 | def test_bad_server 71 | err = nil 72 | EM.run { 73 | http = EM::P::HttpClient2.connect Localhost, 9999 74 | d = http.get "/" 75 | d.errback { err = true; d.internal_error; EM.stop } 76 | } 77 | assert(err) 78 | end 79 | 80 | def test_get 81 | content = nil 82 | EM.run { 83 | http = EM::P::HttpClient2.connect "www.bayshorenetworks.com", 80 84 | d = http.get "/" 85 | d.callback { 86 | content = d.content 87 | EM.stop 88 | } 89 | } 90 | assert(content) 91 | end 92 | 93 | # Not a pipelined request because we wait for one response before we request the next. 94 | # XXX this test is broken because it sends the second request to the first connection 95 | # XXX right before the connection closes 96 | def _test_get_multiple 97 | content = nil 98 | EM.run { 99 | http = EM::P::HttpClient2.connect "www.bayshorenetworks.com", 80 100 | d = http.get "/" 101 | d.callback { 102 | e = http.get "/" 103 | e.callback { 104 | content = e.content 105 | EM.stop 106 | } 107 | } 108 | } 109 | assert(content) 110 | end 111 | 112 | def test_get_pipeline 113 | headers, headers2 = nil, nil 114 | EM.run { 115 | http = EM::P::HttpClient2.connect "www.microsoft.com", 80 116 | d = http.get("/") 117 | d.callback { 118 | headers = d.headers 119 | } 120 | e = http.get("/") 121 | e.callback { 122 | headers2 = e.headers 123 | } 124 | EM::Timer.new(1) {EM.stop} 125 | } 126 | assert(headers) 127 | assert(headers2) 128 | end 129 | 130 | 131 | def test_authheader 132 | EM.run { 133 | EM.start_server Localhost, Localport, TestServer 134 | http = EM::P::HttpClient2.connect Localhost, 18842 135 | d = http.get :url=>"/", :authorization=>"Basic xxx" 136 | d.callback {EM.stop} 137 | d.errback {EM.stop} 138 | } 139 | end 140 | 141 | def test_https_get 142 | d = nil 143 | EM.run { 144 | http = EM::P::HttpClient2.connect :host => 'www.amazon.com', :port => 443, :ssl => true 145 | d = http.get "/" 146 | d.callback { 147 | EM.stop 148 | } 149 | } 150 | assert_equal(200, d.status) 151 | end if EM.ssl? 152 | 153 | end 154 | -------------------------------------------------------------------------------- /lib/em/streamer.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 | module EventMachine 28 | class FileStreamer 29 | include Deferrable 30 | 31 | # Use mapped streamer for files bigger than 16k 32 | MappingThreshold = 16384 33 | # Wait until next tick to send more data when 50k is still in the outgoing buffer 34 | BackpressureLevel = 50000 35 | # Send 16k chunks at a time 36 | ChunkSize = 16384 37 | 38 | # Stream a file over a given connection. An optional :http_chunks => true argument will 39 | # use HTTP 1.1 style chunked-encoding semantics. 40 | # 41 | # module FileSender 42 | # def post_init 43 | # streamer = EventMachine::FileStreamer.new(self, '/tmp/bigfile.tar') 44 | # streamer.callback{ 45 | # # file was sent successfully 46 | # close_connection_after_writing 47 | # } 48 | # end 49 | # end 50 | # 51 | def initialize connection, filename, args = {} 52 | @connection = connection 53 | @http_chunks = args[:http_chunks] 54 | 55 | if File.exist?(filename) 56 | @size = File.size?(filename) 57 | if @size <= MappingThreshold 58 | stream_without_mapping filename 59 | else 60 | stream_with_mapping filename 61 | end 62 | else 63 | fail "file not found" 64 | end 65 | end 66 | 67 | def stream_without_mapping filename # :nodoc: 68 | if @http_chunks 69 | @connection.send_data "#{@size.to_s(16)}\r\n" 70 | @connection.send_file_data filename 71 | @connection.send_data "\r\n0\r\n\r\n" 72 | else 73 | @connection.send_file_data filename 74 | end 75 | succeed 76 | end 77 | private :stream_without_mapping 78 | 79 | def stream_with_mapping filename # :nodoc: 80 | ensure_mapping_extension_is_present 81 | 82 | @position = 0 83 | @mapping = EventMachine::FastFileReader::Mapper.new filename 84 | stream_one_chunk 85 | end 86 | private :stream_with_mapping 87 | 88 | # Used internally to stream one chunk at a time over multiple reactor ticks 89 | def stream_one_chunk 90 | loop { 91 | if @position < @size 92 | if @connection.get_outbound_data_size > BackpressureLevel 93 | EventMachine::next_tick {stream_one_chunk} 94 | break 95 | else 96 | len = @size - @position 97 | len = ChunkSize if (len > ChunkSize) 98 | 99 | @connection.send_data( "#{len.to_s(16)}\r\n" ) if @http_chunks 100 | @connection.send_data( @mapping.get_chunk( @position, len )) 101 | @connection.send_data("\r\n") if @http_chunks 102 | 103 | @position += len 104 | end 105 | else 106 | @connection.send_data "0\r\n\r\n" if @http_chunks 107 | @mapping.close 108 | succeed 109 | break 110 | end 111 | } 112 | end 113 | 114 | #-- 115 | # We use an outboard extension class to get memory-mapped files. 116 | # It's outboard to avoid polluting the core distro, but that means 117 | # there's a "hidden" dependency on it. The first time we get here in 118 | # any run, try to load up the dependency extension. User code will see 119 | # a LoadError if it's not available, but code that doesn't require 120 | # mapped files will work fine without it. This is a somewhat difficult 121 | # compromise between usability and proper modularization. 122 | # 123 | def ensure_mapping_extension_is_present # :nodoc: 124 | @@fastfilereader ||= (require 'fastfilereaderext') 125 | end 126 | private :ensure_mapping_extension_is_present 127 | 128 | end 129 | end 130 | 131 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------