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