├── Gemfile
├── 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
├── 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
│ ├── pool.rb
│ └── resolver.rb
├── tests
├── test_ud.rb
├── test_running.rb
├── test_defer.rb
├── test_shutdown_hooks.rb
├── test_idle_connection.rb
├── test_exc.rb
├── test_handler_check.rb
├── test_servers.rb
├── test_error_handler.rb
├── test_deferrable.rb
├── test_object_protocol.rb
├── test_connection_count.rb
├── test_set_sock_opt.rb
├── test_get_sock_opt.rb
├── test_ssl_methods.rb
├── test_process_watch.rb
├── test_kb.rb
├── test_sasl.rb
├── test_queue.rb
├── test_unbind_reason.rb
├── test_resolver.rb
├── test_threaded_resource.rb
├── test_pending_connect_timeout.rb
├── test_channel.rb
├── test_smtpclient.rb
├── test_inactivity_timeout.rb
├── em_test_helper.rb
├── test_tick_loop.rb
├── test_file_watch.rb
├── test_smtpserver.rb
├── client.crt
├── test_pause.rb
├── test_ssl_args.rb
├── test_ssl_verify.rb
├── test_pure.rb
├── test_next_tick.rb
├── test_timers.rb
├── test_attach.rb
├── client.key
├── test_httpclient2.rb
├── test_processes.rb
├── test_epoll.rb
├── test_ltp.rb
├── test_proxy_connection.rb
└── test_hc.rb
├── .yardopts
├── docs
├── old
│ ├── SMTP
│ ├── TODO
│ ├── INSTALL
│ ├── LEGAL
│ ├── KEYBOARD
│ ├── RELEASE_NOTES
│ └── PURE_RUBY
└── DocumentationGuidesIndex.md
├── rakelib
├── test.rake
├── cpp.rake_example
└── package.rake
├── .gitignore
├── 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
├── ssl.h
├── page.cpp
├── binder.cpp
└── project.h
├── eventmachine.gemspec
├── LICENSE
└── README.md
/Gemfile:
--------------------------------------------------------------------------------
1 | source :rubygems
2 | gemspec
3 |
4 |
--------------------------------------------------------------------------------
/examples/old/ex_queue.rb:
--------------------------------------------------------------------------------
1 | require File.dirname(__FILE__) + '/helper'
2 |
3 |
--------------------------------------------------------------------------------
/lib/em/version.rb:
--------------------------------------------------------------------------------
1 | module EventMachine
2 | VERSION = "1.0.0.rc.4"
3 | end
4 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 | $idle_time_after_send = conn.get_idle_time
13 | conn.close_connection
14 | EM.stop
15 | }
16 | }
17 | }
18 |
19 | assert_in_delta 3, $idle_time, 0.2
20 | assert_equal 0, $idle_time_after_send
21 | end
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/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 line = @buf.slice!(/(.*)\r?\n/)
19 | receive_line(line)
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_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_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 | end
--------------------------------------------------------------------------------
/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_DEBUG, 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_ssl_methods.rb:
--------------------------------------------------------------------------------
1 | require 'em_test_helper'
2 |
3 | class TestSSLMethods < Test::Unit::TestCase
4 |
5 | module ServerHandler
6 |
7 | def post_init
8 | start_tls
9 | end
10 |
11 | def ssl_handshake_completed
12 | $server_called_back = true
13 | $server_cert_value = get_peer_cert
14 | end
15 |
16 | end
17 |
18 | module ClientHandler
19 |
20 | def post_init
21 | start_tls
22 | end
23 |
24 | def ssl_handshake_completed
25 | $client_called_back = true
26 | $client_cert_value = get_peer_cert
27 | EM.stop_event_loop
28 | end
29 |
30 | end
31 |
32 | def test_ssl_methods
33 | $server_called_back, $client_called_back = false, false
34 | $server_cert_value, $client_cert_value = nil, nil
35 |
36 | EM.run {
37 | EM.start_server("127.0.0.1", 9999, ServerHandler)
38 | EM.connect("127.0.0.1", 9999, ClientHandler)
39 | }
40 |
41 | assert($server_called_back)
42 | assert($client_called_back)
43 |
44 | assert($server_cert_value.is_a?(NilClass))
45 | assert($client_cert_value.is_a?(String))
46 | end
47 |
48 | end if EM.ssl?
--------------------------------------------------------------------------------
/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 | EM.run{
31 | # watch ourselves for a fork notification
32 | EM.watch_process(Process.pid, ParentProcessWatcher)
33 | $fork_pid = fork{ sleep }
34 | child = EM.watch_process($fork_pid, ChildProcessWatcher)
35 | $pid = child.pid
36 |
37 | EM.add_timer(0.2){
38 | Process.kill('TERM', $fork_pid)
39 | }
40 | }
41 |
42 | assert_equal($pid, $fork_pid)
43 | assert($forked)
44 | assert($exited)
45 | assert($unbind)
46 | end
47 | end
48 | end
49 |
--------------------------------------------------------------------------------
/tests/test_kb.rb:
--------------------------------------------------------------------------------
1 | require 'em_test_helper'
2 |
3 | class TestKeyboardEvents < Test::Unit::TestCase
4 |
5 | if !jruby?
6 | module KbHandler
7 | include EM::Protocols::LineText2
8 | def receive_line d
9 | EM::stop if d == "STOP"
10 | end
11 | end
12 |
13 | # This test doesn't actually do anything useful but is here to
14 | # illustrate the usage. If you removed the timer and ran this test
15 | # by itself on a console, and then typed into the console, it would
16 | # work.
17 | # I don't know how to get the test harness to simulate actual keystrokes.
18 | # When someone figures that out, then we can make this a real test.
19 | #
20 | def test_kb
21 | EM.run {
22 | EM.open_keyboard KbHandler
23 | EM::Timer.new(1) { EM.stop }
24 | } if $stdout.tty? # don't run the test unless it stands a chance of validity.
25 | end
26 | else
27 | warn "EM.open_keyboard not implemented, skipping tests in #{__FILE__}"
28 |
29 | # Because some rubies will complain if a TestCase class has no tests
30 | def test_em_open_keyboard_unsupported
31 | assert true
32 | end
33 | end
34 | end
35 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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_equal(Array, r.class)
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_equal("127.0.0.1", r.first)
49 | assert_equal(Array, r.class)
50 |
51 | EM.stop
52 | }
53 | }
54 | end
55 | end
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 | completion = resource.dispatch do |o|
19 | o[:foo] = :bar
20 | :foo
21 | end
22 | completion.callback do |result|
23 | assert_equal :foo, result
24 | EM.stop
25 | end
26 | end
27 | assert_equal :bar, object[:foo]
28 | end
29 |
30 | def test_dispatch_failure
31 | completion = resource.dispatch do |o|
32 | raise 'boom'
33 | end
34 | completion.errback do |error|
35 | assert_kind_of RuntimeError, error
36 | assert_equal 'boom', error.message
37 | end
38 | end
39 |
40 | def test_dispatch_threading
41 | main = Thread.current
42 | resource.dispatch do |o|
43 | o[:dispatch_thread] = Thread.current
44 | end
45 | assert_not_equal main, object[:dispatch_thread]
46 | end
47 |
48 | def test_shutdown
49 | # This test should get improved sometime. The method returning thread is
50 | # NOT an api that will be maintained.
51 | assert !resource.shutdown.alive?
52 | end
53 | end
54 |
--------------------------------------------------------------------------------
/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_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 |
--------------------------------------------------------------------------------
/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.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 | end
36 | end
37 |
--------------------------------------------------------------------------------
/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
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/eventmachine.gemspec:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | require File.expand_path('../lib/em/version', __FILE__)
3 |
4 | Gem::Specification.new do |s|
5 | s.name = 'eventmachine'
6 | s.version = EventMachine::VERSION
7 | s.homepage = 'http://rubyeventmachine.com'
8 | s.rubyforge_project = 'eventmachine'
9 |
10 | s.authors = ["Francis Cianfrocca", "Aman Gupta"]
11 | s.email = ["garbagecat10@gmail.com", "aman@tmm1.net"]
12 |
13 | s.files = `git ls-files`.split("\n")
14 | s.extensions = ["ext/extconf.rb", "ext/fastfilereader/extconf.rb"]
15 |
16 | s.add_development_dependency 'rake-compiler', '~> 0.8.1'
17 | s.add_development_dependency 'yard', ">= 0.7.2"
18 | s.add_development_dependency 'bluecloth'
19 |
20 | s.summary = 'Ruby/EventMachine library'
21 | s.description = "EventMachine implements a fast, single-threaded engine for arbitrary network
22 | communications. It's extremely easy to use in Ruby. EventMachine wraps all
23 | interactions with IP sockets, allowing programs to concentrate on the
24 | implementation of network protocols. It can be used to create both network
25 | servers and clients. To create a server or client, a Ruby program only needs
26 | to specify the IP address and port, and provide a Module that implements the
27 | communications protocol. Implementations of several standard network protocols
28 | are provided with the package, primarily to serve as examples. The real goal
29 | of EventMachine is to enable programs to easily interface with other programs
30 | using TCP/IP, especially if custom protocols are required."
31 |
32 | s.rdoc_options = ["--title", "EventMachine", "--main", "README.md", "-x", "lib/em/version", "-x", "lib/jeventmachine"]
33 | s.extra_rdoc_files = ["README.md"] + `git ls-files -- docs/*`.split("\n")
34 | end
35 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 | else
71 | warn "EM.pause_connection not implemented, skipping tests in #{__FILE__}"
72 |
73 | # Because some rubies will complain if a TestCase class has no tests
74 | def test_em_pause_connection_not_implemented
75 | assert true
76 | end
77 | end
78 | end
79 |
--------------------------------------------------------------------------------
/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?
--------------------------------------------------------------------------------
/ext/ssl.h:
--------------------------------------------------------------------------------
1 | /*****************************************************************************
2 |
3 | $Id$
4 |
5 | File: ssl.h
6 | Date: 30Apr06
7 |
8 | Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
9 | Gmail: blackhedd
10 |
11 | This program is free software; you can redistribute it and/or modify
12 | it under the terms of either: 1) the GNU General Public License
13 | as published by the Free Software Foundation; either version 2 of the
14 | License, or (at your option) any later version; or 2) Ruby's License.
15 |
16 | See the file COPYING for complete licensing information.
17 |
18 | *****************************************************************************/
19 |
20 |
21 | #ifndef __SslBox__H_
22 | #define __SslBox__H_
23 |
24 |
25 |
26 |
27 | #ifdef WITH_SSL
28 |
29 | /******************
30 | class SslContext_t
31 | ******************/
32 |
33 | class SslContext_t
34 | {
35 | public:
36 | SslContext_t (bool is_server, const string &privkeyfile, const string &certchainfile);
37 | virtual ~SslContext_t();
38 |
39 | private:
40 | static bool bLibraryInitialized;
41 |
42 | private:
43 | bool bIsServer;
44 | SSL_CTX *pCtx;
45 |
46 | EVP_PKEY *PrivateKey;
47 | X509 *Certificate;
48 |
49 | friend class SslBox_t;
50 | };
51 |
52 |
53 | /**************
54 | class SslBox_t
55 | **************/
56 |
57 | class SslBox_t
58 | {
59 | public:
60 | SslBox_t (bool is_server, const string &privkeyfile, const string &certchainfile, bool verify_peer, const unsigned long binding);
61 | virtual ~SslBox_t();
62 |
63 | int PutPlaintext (const char*, int);
64 | int GetPlaintext (char*, int);
65 |
66 | bool PutCiphertext (const char*, int);
67 | bool CanGetCiphertext();
68 | int GetCiphertext (char*, int);
69 | bool IsHandshakeCompleted() {return bHandshakeCompleted;}
70 |
71 | X509 *GetPeerCert();
72 |
73 | void Shutdown();
74 |
75 | protected:
76 | SslContext_t *Context;
77 |
78 | bool bIsServer;
79 | bool bHandshakeCompleted;
80 | bool bVerifyPeer;
81 | SSL *pSSL;
82 | BIO *pbioRead;
83 | BIO *pbioWrite;
84 |
85 | PageList OutboundQ;
86 | };
87 |
88 | extern "C" int ssl_verify_wrapper(int, X509_STORE_CTX*);
89 |
90 | #endif // WITH_SSL
91 |
92 |
93 | #endif // __SslBox__H_
94 |
95 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 | #
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
--------------------------------------------------------------------------------
/tests/test_ssl_verify.rb:
--------------------------------------------------------------------------------
1 | require 'em_test_helper'
2 |
3 | if EM.ssl?
4 | class TestSslVerify < Test::Unit::TestCase
5 | def setup
6 | $dir = File.dirname(File.expand_path(__FILE__)) + '/'
7 | $cert_from_file = File.read($dir+'client.crt')
8 | end
9 |
10 | module Client
11 | def connection_completed
12 | start_tls(:private_key_file => $dir+'client.key', :cert_chain_file => $dir+'client.crt')
13 | end
14 |
15 | def ssl_handshake_completed
16 | $client_handshake_completed = true
17 | close_connection
18 | end
19 |
20 | def unbind
21 | EM.stop_event_loop
22 | end
23 | end
24 |
25 | module AcceptServer
26 | def post_init
27 | start_tls(:verify_peer => true)
28 | end
29 |
30 | def ssl_verify_peer(cert)
31 | $cert_from_server = cert
32 | true
33 | end
34 |
35 | def ssl_handshake_completed
36 | $server_handshake_completed = true
37 | end
38 | end
39 |
40 | module DenyServer
41 | def post_init
42 | start_tls(:verify_peer => true)
43 | end
44 |
45 | def ssl_verify_peer(cert)
46 | $cert_from_server = cert
47 | # Do not accept the peer. This should now cause the connection to shut down without the SSL handshake being completed.
48 | false
49 | end
50 |
51 | def ssl_handshake_completed
52 | $server_handshake_completed = true
53 | end
54 | end
55 |
56 | def test_accept_server
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 | $client_handshake_completed, $server_handshake_completed = false, false
70 | EM.run {
71 | EM.start_server("127.0.0.1", 16784, DenyServer)
72 | EM.connect("127.0.0.1", 16784, Client)
73 | }
74 |
75 | assert_equal($cert_from_file, $cert_from_server)
76 | assert(!$client_handshake_completed)
77 | assert(!$server_handshake_completed)
78 | end
79 | end
80 | else
81 | warn "EM built without SSL support, skipping tests in #{__FILE__}"
82 | end
83 |
--------------------------------------------------------------------------------
/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_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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 | send_data data
8 | end
9 | end
10 |
11 | class EchoClient < EM::Connection
12 | def initialize socket
13 | self.notify_readable = true
14 | @socket = socket
15 | @socket.write("abc\n")
16 | end
17 |
18 | def notify_readable
19 | $read = @socket.readline
20 | $fd = detach
21 | end
22 |
23 | def unbind
24 | EM.next_tick do
25 | @socket.write("def\n")
26 | EM.add_timer(0.1) { EM.stop }
27 | end
28 | end
29 | end
30 |
31 | def setup
32 | @port = next_port
33 | $read, $r, $w, $fd = nil
34 | end
35 |
36 | def teardown
37 | [$r, $w].each do |io|
38 | io.close rescue nil
39 | end
40 | end
41 |
42 | def test_attach
43 | socket = nil
44 |
45 | EM.run {
46 | EM.start_server "127.0.0.1", @port, EchoServer
47 | socket = TCPSocket.new "127.0.0.1", @port
48 | EM.watch socket, EchoClient, socket
49 | }
50 |
51 | assert_equal $read, "abc\n"
52 | unless jruby? # jruby filenos are not real
53 | assert_equal $fd, socket.fileno
54 | end
55 | assert_equal false, socket.closed?
56 | assert_equal socket.readline, "def\n"
57 | end
58 |
59 | module PipeWatch
60 | def notify_readable
61 | $read = $r.readline
62 | EM.stop
63 | end
64 | end
65 |
66 | def test_attach_pipe
67 | EM.run{
68 | $r, $w = IO.pipe
69 | EM.watch $r, PipeWatch do |c|
70 | c.notify_readable = true
71 | end
72 | $w.write("ghi\n")
73 | }
74 |
75 | assert_equal $read, "ghi\n"
76 | end
77 |
78 | def test_set_readable
79 | before, after = nil
80 |
81 | EM.run{
82 | $r, $w = IO.pipe
83 | c = EM.watch $r, PipeWatch do |con|
84 | con.notify_readable = false
85 | end
86 |
87 | EM.next_tick{
88 | before = c.notify_readable?
89 | c.notify_readable = true
90 | after = c.notify_readable?
91 | }
92 |
93 | $w.write("jkl\n")
94 | }
95 |
96 | assert !before
97 | assert after
98 | assert_equal $read, "jkl\n"
99 | end
100 |
101 | def test_read_write_pipe
102 | result = nil
103 |
104 | pipe_reader = Module.new do
105 | define_method :receive_data do |data|
106 | result = data
107 | EM.stop
108 | end
109 | end
110 |
111 | r,w = IO.pipe
112 |
113 | EM.run {
114 | EM.attach r, pipe_reader
115 | writer = EM.attach(w)
116 | writer.send_data 'ghi'
117 |
118 | # XXX: Process will hang in Windows without this line
119 | writer.close_connection_after_writing
120 | }
121 |
122 | assert_equal "ghi", result
123 | ensure
124 | [r,w].each {|io| io.close rescue nil }
125 | end
126 | end
127 |
--------------------------------------------------------------------------------
/rakelib/package.rake:
--------------------------------------------------------------------------------
1 | require 'rake/gempackagetask'
2 | begin
3 | require 'rake/extensiontask'
4 | require 'rake/javaextensiontask'
5 | rescue LoadError => e
6 | puts <<-MSG
7 | rake-compiler gem seems to be missing. Please install it with
8 |
9 | gem install rake-compiler
10 |
11 | (add sudo if necessary).
12 | MSG
13 | end
14 |
15 | Rake::GemPackageTask.new(GEMSPEC) do |pkg|
16 | end
17 |
18 | if RUBY_PLATFORM =~ /java/
19 | Rake::JavaExtensionTask.new("rubyeventmachine", GEMSPEC) do |ext|
20 | ext.ext_dir = 'java/src'
21 | end
22 | else
23 | def setup_cross_compilation(ext)
24 | unless RUBY_PLATFORM =~ /mswin|mingw/
25 | ext.cross_compile = true
26 | ext.cross_platform = ['x86-mingw32', 'x86-mswin32-60']
27 |
28 | # inject 1.8/1.9 pure-ruby entry point
29 | ext.cross_compiling do |spec|
30 | spec.files += ["lib/#{ext.name}.rb"]
31 | end
32 | end
33 | end
34 |
35 | Rake::ExtensionTask.new("rubyeventmachine", GEMSPEC) do |ext|
36 | ext.ext_dir = 'ext'
37 | ext.source_pattern = '*.{h,c,cpp}'
38 | setup_cross_compilation(ext)
39 | end
40 | Rake::ExtensionTask.new("fastfilereaderext", GEMSPEC) do |ext|
41 | ext.ext_dir = 'ext/fastfilereader'
42 | ext.source_pattern = '*.{h,c,cpp}'
43 | setup_cross_compilation(ext)
44 | end
45 | end
46 |
47 | # Setup shim files that require 1.8 vs 1.9 extensions in win32 bin gems
48 | %w[ rubyeventmachine fastfilereaderext ].each do |filename|
49 | file("lib/#{filename}.rb") do |t|
50 | File.open(t.name, 'wb') do |f|
51 | f.write <<-eoruby
52 | RUBY_VERSION =~ /(\\d+.\\d+)/
53 | require "\#{$1}/#{File.basename(t.name, '.rb')}"
54 | eoruby
55 | end
56 | at_exit{ FileUtils.rm t.name if File.exists?(t.name) }
57 | end
58 | end
59 |
60 | task :cross_cxx do
61 | ENV['CROSS_COMPILING'] = 'yes'
62 | require 'rake/extensioncompiler'
63 | ENV['CXX'] = "#{Rake::ExtensionCompiler.mingw_host}-g++"
64 | end
65 |
66 | if Rake::Task.task_defined?(:cross)
67 | task :cross => 'lib/rubyeventmachine.rb'
68 | task :cross => 'lib/fastfilereaderext.rb'
69 | task :cross => :cross_cxx
70 | end
71 |
72 | def windows?; RUBY_PLATFORM =~ /mswin|mingw/; end
73 | def sudo(cmd)
74 | if windows? || (require 'etc'; Etc.getpwuid.uid == 0)
75 | sh cmd
76 | else
77 | sh "sudo #{cmd}"
78 | end
79 | end
80 | def gem_cmd(action, name, *args)
81 | rb = Gem.ruby rescue nil
82 | rb ||= (require 'rbconfig'; File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name']))
83 | sudo "#{rb} -r rubygems -e 'require %{rubygems/gem_runner}; Gem::GemRunner.new.run(%w{#{action} #{name} #{args.join(' ')}})'"
84 | end
85 |
86 | Rake::Task[:clean].enhance [:clobber_package]
87 |
88 | namespace :gem do
89 | desc 'Install gem (and sudo if required)'
90 | task :install => :package do
91 | gem_cmd(:install, "pkg/#{GEMSPEC.name}-#{GEMSPEC.version}.gem")
92 | end
93 |
94 | desc 'Uninstall gem (and sudo if required)'
95 | task :uninstall do
96 | gem_cmd(:uninstall, "#{GEMSPEC.name}", "-v=#{GEMSPEC.version}")
97 | end
98 | end
99 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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'] or $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['LDSHARED'] = "$(CXX) -shared -lstdc++"
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($status.exitstatus, 0)
41 | assert_equal($status.class, Process::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($status.exitstatus, 0)
61 | assert_equal($status.class, Process::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{
115 | EM.popen('cat /dev/random', test_client)
116 | }
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 |
--------------------------------------------------------------------------------
/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_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 | if windows? || jruby?
29 | warn "EM.set_descriptor_table_size not implemented, skipping test in #{__FILE__}"
30 | else
31 | # We can set the rlimit/nofile of a process but we can only set it
32 | # higher if we're running as root.
33 | # On most systems, the default value is 1024.
34 | def test_rlimit
35 | unless EM.set_descriptor_table_size >= 1024
36 | a = EM.set_descriptor_table_size
37 | assert( a <= 1024 )
38 | a = EM.set_descriptor_table_size( 1024 )
39 | assert( a == 1024 )
40 | end
41 | end
42 | end
43 |
44 | # Run a high-volume version of this test by kicking the number of connections
45 | # up past 512. (Each connection uses two sockets, a client and a server.)
46 | # (Will require running the test as root)
47 | # This test exercises TCP clients and servers.
48 | #
49 | # XXX this test causes all sort of weird issues on OSX (when run as part of the suite)
50 | def _test_descriptors
51 | EM.epoll
52 | EM.set_descriptor_table_size 60000
53 | EM.run {
54 | EM.start_server "127.0.0.1", 9800, TestEchoServer
55 | $n = 0
56 | $max = 0
57 | 100.times {
58 | EM.connect("127.0.0.1", 9800, TestEchoClient) {$n += 1}
59 | }
60 | }
61 | assert_equal(0, $n)
62 | assert_equal(100, $max)
63 | end
64 |
65 | def setup
66 | @port = next_port
67 | end
68 |
69 | module TestDatagramServer
70 | def receive_data dgm
71 | $in = dgm
72 | send_data "abcdefghij"
73 | end
74 | end
75 | module TestDatagramClient
76 | def initialize port
77 | @port = port
78 | end
79 |
80 | def post_init
81 | send_datagram "1234567890", "127.0.0.1", @port
82 | end
83 |
84 | def receive_data dgm
85 | $out = dgm
86 | EM.stop
87 | end
88 | end
89 |
90 | def test_datagrams
91 | $in = $out = ""
92 | EM.run {
93 | EM.open_datagram_socket "127.0.0.1", @port, TestDatagramServer
94 | EM.open_datagram_socket "127.0.0.1", 0, TestDatagramClient, @port
95 | }
96 | assert_equal( "1234567890", $in )
97 | assert_equal( "abcdefghij", $out )
98 | end
99 |
100 | # XXX this test fails randomly..
101 | def _test_unix_domain
102 | fn = "/tmp/xxx.chain"
103 | EM.epoll
104 | EM.set_descriptor_table_size 60000
105 | EM.run {
106 | # The pure-Ruby version won't let us open the socket if the node already exists.
107 | # Not sure, that actually may be correct and the compiled version is wrong.
108 | # Pure Ruby also oddly won't let us make that many connections. This test used
109 | # to run 100 times. Not sure where that lower connection-limit is coming from in
110 | # pure Ruby.
111 | # Let's not sweat the Unix-ness of the filename, since this test can't possibly
112 | # work on Windows anyway.
113 | #
114 | File.unlink(fn) if File.exist?(fn)
115 | EM.start_unix_domain_server fn, TestEchoServer
116 | $n = 0
117 | $max = 0
118 | 50.times {
119 | EM.connect_unix_domain(fn, TestEchoClient) {$n += 1}
120 | }
121 | EM::add_timer(1) { $stderr.puts("test_unix_domain timed out!"); EM::stop }
122 | }
123 | assert_equal(0, $n)
124 | assert_equal(50, $max)
125 | ensure
126 | File.unlink(fn) if File.exist?(fn)
127 | end
128 |
129 | end
130 |
131 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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