rainbows 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. data/GIT-VERSION-GEN +1 -1
  2. data/GNUmakefile +12 -6
  3. data/README +3 -2
  4. data/Rakefile +3 -3
  5. data/TODO +2 -9
  6. data/lib/rainbows.rb +1 -0
  7. data/lib/rainbows/app_pool.rb +10 -6
  8. data/lib/rainbows/base.rb +1 -1
  9. data/lib/rainbows/const.rb +1 -1
  10. data/lib/rainbows/ev_core.rb +88 -0
  11. data/lib/rainbows/event_machine.rb +218 -0
  12. data/lib/rainbows/http_server.rb +4 -1
  13. data/lib/rainbows/rev.rb +21 -89
  14. data/lib/rainbows/revactor.rb +4 -10
  15. data/local.mk.sample +13 -7
  16. data/rainbows.gemspec +17 -2
  17. data/t/.gitignore +1 -0
  18. data/t/GNUmakefile +63 -40
  19. data/t/README +6 -2
  20. data/t/async_sinatra.ru +13 -0
  21. data/t/bin/unused_listen +1 -1
  22. data/t/large-file-response.ru +1 -0
  23. data/t/my-tap-lib.sh +200 -0
  24. data/t/simple-http_Base.ru +3 -0
  25. data/t/simple-http_EventMachine.ru +9 -0
  26. data/t/simple-http_Rev.ru +9 -0
  27. data/t/simple-http_Revactor.ru +10 -0
  28. data/t/simple-http_ThreadPool.ru +10 -0
  29. data/t/simple-http_ThreadSpawn.ru +10 -0
  30. data/t/t0000-simple-http.sh +142 -0
  31. data/t/t0001-unix-http.sh +103 -0
  32. data/t/t0002-graceful.sh +32 -0
  33. data/t/t0002-parser-error.sh +31 -0
  34. data/t/t0003-reopen-logs.sh +97 -0
  35. data/t/t0005-large-file-response.sh +83 -0
  36. data/t/t0100-rack-input-hammer.sh +45 -0
  37. data/t/t0101-rack-input-trailer.sh +68 -0
  38. data/t/t0200-async-response.sh +66 -0
  39. data/t/t0201-async-response-no-autochunk.sh +3 -0
  40. data/t/t0300-async_sinatra.sh +65 -0
  41. data/t/t9000-rack-app-pool.sh +45 -33
  42. data/t/test-lib.sh +67 -56
  43. metadata +26 -56
  44. data/t/lib-async-response-no-autochunk.sh +0 -6
  45. data/t/lib-async-response.sh +0 -45
  46. data/t/lib-graceful.sh +0 -40
  47. data/t/lib-input-trailer.sh +0 -63
  48. data/t/lib-large-file-response.sh +0 -45
  49. data/t/lib-parser-error.sh +0 -29
  50. data/t/lib-rack-input-hammer.sh +0 -38
  51. data/t/lib-reopen-logs.sh +0 -60
  52. data/t/lib-simple-http.sh +0 -92
  53. data/t/t0000-basic.sh +0 -2
  54. data/t/t1000-thread-pool-basic.sh +0 -2
  55. data/t/t1002-thread-pool-graceful.sh +0 -2
  56. data/t/t1003-thread-pool-reopen-logs.sh +0 -2
  57. data/t/t1004-thread-pool-async-response.sh +0 -45
  58. data/t/t1005-thread-pool-large-file-response.sh +0 -45
  59. data/t/t1006-thread-pool-async-response-no-autochunk.sh +0 -6
  60. data/t/t1100-thread-pool-rack-input.sh +0 -2
  61. data/t/t1101-thread-pool-input-trailer.sh +0 -2
  62. data/t/t2000-thread-spawn-basic.sh +0 -2
  63. data/t/t2002-thread-spawn-graceful.sh +0 -2
  64. data/t/t2003-thread-spawn-reopen-logs.sh +0 -2
  65. data/t/t2004-thread-spawn-async-response.sh +0 -45
  66. data/t/t2005-thread-spawn-large-file-response.sh +0 -45
  67. data/t/t2006-thread-spawn-async-response-no-autochunk.sh +0 -6
  68. data/t/t2100-thread-spawn-rack-input.sh +0 -2
  69. data/t/t2101-thread-spawn-input-trailer.sh +0 -2
  70. data/t/t3000-revactor-basic.sh +0 -2
  71. data/t/t3002-revactor-graceful.sh +0 -2
  72. data/t/t3003-revactor-reopen-logs.sh +0 -2
  73. data/t/t3004-revactor-async-response.sh +0 -45
  74. data/t/t3005-revactor-large-file-response.sh +0 -2
  75. data/t/t3006-revactor-async-response-no-autochunk.sh +0 -6
  76. data/t/t3100-revactor-rack-input.sh +0 -2
  77. data/t/t3101-revactor-rack-input-trailer.sh +0 -2
  78. data/t/t4000-rev-basic.sh +0 -2
  79. data/t/t4002-rev-graceful.sh +0 -2
  80. data/t/t4003-rev-parser-error.sh +0 -2
  81. data/t/t4003-rev-reopen-logs.sh +0 -2
  82. data/t/t4004-rev-async-response.sh +0 -45
  83. data/t/t4005-rev-large-file-response.sh +0 -2
  84. data/t/t4006-rev-async-response-no-autochunk.sh +0 -6
  85. data/t/t4100-rev-rack-input.sh +0 -2
  86. data/t/t4101-rev-rack-input-trailer.sh +0 -2
@@ -33,7 +33,10 @@ module Rainbows
33
33
  extend(mod)
34
34
  Const::RACK_DEFAULTS['rainbows.model'] = @use = model
35
35
  Const::RACK_DEFAULTS['rack.multithread'] = !!(/Thread/ =~ model.to_s)
36
- Const::RACK_DEFAULTS['rainbows.autochunk'] = (model.to_s == "Rev")
36
+ case model
37
+ when :Rev, :EventMachine
38
+ Const::RACK_DEFAULTS['rainbows.autochunk'] = true
39
+ end
37
40
  end
38
41
 
39
42
  def worker_connections(*args)
data/lib/rainbows/rev.rb CHANGED
@@ -1,9 +1,7 @@
1
1
  # -*- encoding: binary -*-
2
2
  require 'rev'
3
-
4
- # workaround revactor 0.1.4 still using the old Rev::Buffer
5
- # ref: http://rubyforge.org/pipermail/revactor-talk/2009-October/000034.html
6
- defined?(Rev::Buffer) or Rev::Buffer = IO::Buffer
3
+ Rev::VERSION >= '0.3.0' or abort 'rev >= 0.3.0 is required'
4
+ require 'rainbows/ev_core'
7
5
 
8
6
  module Rainbows
9
7
 
@@ -30,46 +28,22 @@ module Rainbows
30
28
  include Base
31
29
 
32
30
  class Client < ::Rev::IO
33
- include Unicorn
34
- include Rainbows::Const
31
+ include Rainbows::EvCore
35
32
  G = Rainbows::G
36
33
 
37
- # queued, optional response bodies, it should only be unpollable "fast"
38
- # devices where read(2) is uninterruptable. Unfortunately, NFS and ilk
39
- # are also part of this. We'll also stick DeferredResponse bodies in
40
- # here to prevent connections from being closed on us.
41
- attr_reader :deferred_bodies
42
-
43
34
  def initialize(io)
44
35
  G.cur += 1
45
36
  super(io)
46
- @remote_addr = ::TCPSocket === io ? io.peeraddr.last : LOCALHOST
47
- @env = {}
48
- @hp = HttpParser.new
49
- @state = :headers # [ :body [ :trailers ] ] :app_call :close
50
- @buf = ""
51
- @deferred_bodies = [] # for (fast) regular files only
52
- end
53
-
54
- # graceful exit, like SIGQUIT
55
- def quit
56
- @deferred_bodies.clear
57
- @state = :close
37
+ post_init
58
38
  end
59
39
 
60
- def handle_error(e)
61
- quit
62
- msg = case e
63
- when EOFError,Errno::ECONNRESET,Errno::EPIPE,Errno::EINVAL,Errno::EBADF
64
- ERROR_500_RESPONSE
65
- when HttpParserError # try to tell the client they're bad
66
- ERROR_400_RESPONSE
67
- else
68
- G.logger.error "Read error: #{e.inspect}"
69
- G.logger.error e.backtrace.join("\n")
70
- ERROR_500_RESPONSE
71
- end
72
- write(msg)
40
+ # queued, optional response bodies, it should only be unpollable "fast"
41
+ # devices where read(2) is uninterruptable. Unfortunately, NFS and ilk
42
+ # are also part of this. We'll also stick DeferredResponse bodies in
43
+ # here to prevent connections from being closed on us.
44
+ def defer_body(io)
45
+ @deferred_bodies << io
46
+ on_write_complete unless @hp.headers? # triggers a write
73
47
  end
74
48
 
75
49
  def app_call
@@ -89,7 +63,7 @@ module Rainbows
89
63
  # keepalive requests are always body-less, so @input is unchanged
90
64
  @hp.headers(@env, @buf) and next
91
65
  else
92
- @state = :close
66
+ quit
93
67
  end
94
68
  return
95
69
  end while true
@@ -104,6 +78,7 @@ module Rainbows
104
78
  rescue EOFError # expected at file EOF
105
79
  @deferred_bodies.shift
106
80
  body.close
81
+ close if :close == @state && @deferred_bodies.empty?
107
82
  end
108
83
  rescue Object => e
109
84
  handle_error(e)
@@ -116,53 +91,6 @@ module Rainbows
116
91
  def on_close
117
92
  G.cur -= 1
118
93
  end
119
-
120
- def tmpio
121
- io = Util.tmpio
122
- def io.size
123
- # already sync=true at creation, so no need to flush before stat
124
- stat.size
125
- end
126
- io
127
- end
128
-
129
- # TeeInput doesn't map too well to this right now...
130
- def on_read(data)
131
- case @state
132
- when :headers
133
- @hp.headers(@env, @buf << data) or return
134
- @state = :body
135
- len = @hp.content_length
136
- if len == 0
137
- @input = HttpRequest::NULL_IO
138
- app_call # common case
139
- else # nil or len > 0
140
- # since we don't do streaming input, we have no choice but
141
- # to take over 100-continue handling from the Rack application
142
- if @env[HTTP_EXPECT] =~ /\A100-continue\z/i
143
- write(EXPECT_100_RESPONSE)
144
- @env.delete(HTTP_EXPECT)
145
- end
146
- @input = len && len <= MAX_BODY ? StringIO.new("") : tmpio
147
- @hp.filter_body(@buf2 = @buf.dup, @buf)
148
- @input << @buf2
149
- on_read("")
150
- end
151
- when :body
152
- if @hp.body_eof?
153
- @state = :trailers
154
- on_read(data)
155
- elsif data.size > 0
156
- @hp.filter_body(@buf2, @buf << data)
157
- @input << @buf2
158
- on_read("")
159
- end
160
- when :trailers
161
- @hp.trailers(@env, @buf << data) and app_call
162
- end
163
- rescue Object => e
164
- handle_error(e)
165
- end
166
94
  end
167
95
 
168
96
  class Server < ::Rev::IO
@@ -172,7 +100,7 @@ module Rainbows
172
100
  return if G.cur >= G.max
173
101
  begin
174
102
  Client.new(@_io.accept_nonblock).attach(::Rev::Loop.default)
175
- rescue Errno::EAGAIN, Errno::ECONNBORTED
103
+ rescue Errno::EAGAIN, Errno::ECONNABORTED
176
104
  end
177
105
  end
178
106
 
@@ -191,7 +119,7 @@ module Rainbows
191
119
  # here since we can't get here without checking to_path first
192
120
  io = body.to_io if body.respond_to?(:to_io)
193
121
  io ||= ::IO.new($1.to_i) if body.to_path =~ %r{\A/dev/fd/(\d+)\z}
194
- io ||= File.open(File.expand_path(body.to_path), 'rb')
122
+ io ||= File.open(body.to_path, 'rb')
195
123
  st = io.stat
196
124
 
197
125
  if st.socket? || st.pipe?
@@ -199,7 +127,11 @@ module Rainbows
199
127
  do_chunk = false if headers.delete('X-Rainbows-Autochunk') == 'no'
200
128
  # too tricky to support keepalive/pipelining when a response can
201
129
  # take an indeterminate amount of time here.
202
- out[0] = CONN_CLOSE
130
+ if out.nil?
131
+ do_chunk = false
132
+ else
133
+ out[0] = CONN_CLOSE
134
+ end
203
135
 
204
136
  io = new(io, client, do_chunk, body).attach(::Rev::Loop.default)
205
137
  elsif st.file?
@@ -208,7 +140,7 @@ module Rainbows
208
140
  else # char/block device, directory, whatever... nobody cares
209
141
  return response
210
142
  end
211
- client.deferred_bodies << io
143
+ client.defer_body(io)
212
144
  [ response.first, headers.to_hash, [] ]
213
145
  end
214
146
 
@@ -1,9 +1,6 @@
1
1
  # -*- encoding: binary -*-
2
2
  require 'revactor'
3
-
4
- # workaround revactor 0.1.4 still using the old Rev::Buffer
5
- # ref: http://rubyforge.org/pipermail/revactor-talk/2009-October/000034.html
6
- defined?(Rev::Buffer) or Rev::Buffer = IO::Buffer
3
+ Revactor::VERSION >= '0.1.5' or abort 'revactor 0.1.5 is required'
7
4
 
8
5
  module Rainbows
9
6
 
@@ -137,16 +134,13 @@ module Rainbows
137
134
 
138
135
  def revactorize_listeners!
139
136
  LISTENERS.map! do |s|
140
- if TCPServer === s
137
+ case s
138
+ when TCPServer
141
139
  ::Revactor::TCP.listen(s, nil)
142
- elsif defined?(::Revactor::UNIX) && UNIXServer === s
140
+ when UNIXServer
143
141
  ::Revactor::UNIX.listen(s)
144
- else
145
- logger.error "your version of Revactor can't handle #{s.inspect}"
146
- nil
147
142
  end
148
143
  end
149
- LISTENERS.compact!
150
144
  end
151
145
 
152
146
  end
data/local.mk.sample CHANGED
@@ -5,18 +5,24 @@
5
5
  # This is depends on a bunch of GNU-isms from bash, sed, touch.
6
6
 
7
7
  DLEXT := so
8
- gems := rev-0.3.1 rack-1.0.0 iobuffer-0.1.1
8
+ gems := rack-1.0.1
9
+ # gems += unicorn-0.93.3 # installed via setup.rb
10
+ gems += rev-0.3.1 iobuffer-0.1.1
11
+ gems += eventmachine-0.12.10
12
+ gems += async_sinatra-0.1.5 sinatra-0.9.4
9
13
 
10
14
  # Avoid loading rubygems to speed up tests because gmake is
11
15
  # fork+exec heavy with Ruby.
16
+ prefix = $(HOME)
12
17
  ifeq ($(r19),)
13
- ruby := $(HOME)/bin/ruby
14
- gem_paths := $(addprefix $(HOME)/lib/ruby/gems/1.8/gems/,$(gems))
18
+ RUBY := $(prefix)/bin/ruby
19
+ gem_paths := $(addprefix $(prefix)/lib/ruby/gems/1.8/gems/,$(gems))
15
20
  else
16
- export PATH := $(HOME)/ruby-1.9/bin:$(PATH)
17
- ruby := $(HOME)/ruby-1.9/bin/ruby --disable-gems
18
- gems := $(gems) case-0.5 revactor-0.1.4
19
- gem_paths := $(addprefix $(HOME)/ruby-1.9/lib/ruby/gems/1.9.1/gems/,$(gems))
21
+ prefix := $(prefix)/ruby-1.9
22
+ export PATH := $(prefix)/bin:$(PATH)
23
+ RUBY := $(prefix)/bin/ruby --disable-gems
24
+ gems += case-0.5 revactor-0.1.5
25
+ gem_paths := $(addprefix $(prefix)/lib/ruby/gems/1.9.1/gems/,$(gems))
20
26
  endif
21
27
 
22
28
  ifdef gem_paths
data/rainbows.gemspec CHANGED
@@ -40,8 +40,23 @@ Gem::Specification.new do |s|
40
40
 
41
41
  s.test_files = test_files
42
42
 
43
- s.add_dependency(%q<rack>)
44
- s.add_dependency(%q<unicorn>, ["~> 0.93.1"])
43
+ # we need Unicorn for the HTTP parser and process management
44
+ s.add_dependency(%q<unicorn>, ["~> 0.93.4"])
45
+
46
+ # Unicorn already depends on Rack
47
+ # s.add_dependency(%q<rack>)
48
+
49
+ # optional runtime dependencies depending on configuration
50
+ # see local.mk.sample for the exact versions we've tested with
51
+ #
52
+ # Revactor >= 0.1.5 includes UNIX domain socket support
53
+ # s.add_dependency(%q<revactor>, [">= 0.1.5"])
54
+ #
55
+ # Revactor depends on Rev, too, 0.3.0 got the ability to attach IOs
56
+ # s.add_dependency(%q<rev>, [">= 0.3.0"])
57
+ #
58
+ # We use the new EM::attach/watch API in 0.12.10
59
+ # s.add_dependency(%q<eventmachine>, ["~> 0.12.10"])
45
60
 
46
61
  # s.licenses = %w(GPLv2 Ruby) # accessor not compatible with older Rubygems
47
62
  end
data/t/.gitignore CHANGED
@@ -1,3 +1,4 @@
1
1
  /test-results-*
2
2
  /test-bin-*
3
3
  /random_blob
4
+ /.dep+*
data/t/GNUmakefile CHANGED
@@ -2,11 +2,13 @@
2
2
 
3
3
  all::
4
4
 
5
- ruby = ruby
5
+ pid := $(shell echo $$PPID)
6
+
7
+ RUBY = $(ruby)
6
8
  rainbows_lib := $(shell cd ../lib && pwd)
7
9
  -include ../local.mk
8
10
  ifeq ($(RUBY_VERSION),)
9
- RUBY_VERSION := $(shell $(ruby) -e 'puts RUBY_VERSION')
11
+ RUBY_VERSION := $(shell $(RUBY) -e 'puts RUBY_VERSION')
10
12
  endif
11
13
 
12
14
  ifeq ($(RUBYLIB),)
@@ -16,60 +18,81 @@ else
16
18
  endif
17
19
  export RUBYLIB RUBY_VERSION
18
20
 
21
+ models := ThreadPool ThreadSpawn Rev EventMachine
22
+ ifeq ($(RUBY_VERSION),1.9.1) # 1.9.2-preview1 was broken
23
+ models += Revactor
24
+ endif
25
+ all_models := $(models) Base
26
+
19
27
  T = $(wildcard t[0-9][0-9][0-9][0-9]-*.sh)
20
28
 
29
+ MODEL_T := $(foreach m,$(all_models),$(addprefix $(m).,$(T)))
30
+ $(T): MODELS = $(models)
31
+
32
+ # some tests can be run with all models
33
+ t0000-simple-http.sh: MODELS = $(all_models)
34
+ t0001-unix-http.sh: MODELS = $(all_models)
35
+ t0002-graceful.sh: MODELS = $(all_models)
36
+ t0002-parser-error.sh: MODELS = $(all_models)
37
+ t0003-reopen-logs.sh: MODELS = $(all_models)
38
+
39
+ # this test is not compatible with non-Thread models yet
40
+ t9000-rack-app-pool.sh: MODELS = ThreadPool ThreadSpawn
41
+
42
+ # recursively run per-model tests
43
+ # haven't figured out a good way to make make non-recursive here, yet...
44
+ $(T):
45
+ $(MAKE) $(foreach m,$(MODELS),$(addprefix $(m).,$@))
46
+
47
+ $(all_models):
48
+ $(MAKE) $(filter $@.%,$(MODEL_T))
49
+
21
50
  all:: $(T)
22
51
 
23
52
  # can't rely on "set -o pipefail" since we don't require bash or ksh93 :<
24
53
  t_pfx = trash/$@-$(RUBY_VERSION)
25
- t_code = $(t_pfx).code
26
- t_log = $(t_pfx).log
54
+ TEST_OPTS =
27
55
  # TRACER = strace -f -o $(t_pfx).strace -s 100000
28
56
  # TRACER = /usr/bin/time -o $(t_pfx).time
29
- t_run = $(TRACER) $(SHELL) $(SH_TEST_OPTS) $@
30
-
31
- # prefix stdout messages with ':', and stderr messages with '!'
32
- t_wrap = ( ( ( echo 42 > $(t_code); \
33
- $(t_run); \
34
- echo $$? > $(t_code) ) \
35
- | sed 's/^/$(pfx):/' 1>&3 ) 2>&1 \
36
- | sed 's/^/$(pfx)!/' 1>&2 ) 3>&1
37
-
38
- ifndef V
39
- quiet_pre = @echo '* $@';
40
- quiet_post = > $(t_log) 2>&1; exit $$(cat $(t_code))
41
- pfx =
42
- else
57
+
58
+ ifdef V
43
59
  ifeq ($(V),2)
44
- SH_TEST_OPTS += -x
60
+ TEST_OPTS += --trace
61
+ else
62
+ TEST_OPTS += --verbose
45
63
  endif
46
- quiet_pre = @echo '* $@';
47
- quiet_post = 2>&1 | ./bin/utee $(t_log); exit $$(cat $(t_code))
48
- pfx = $@
49
64
  endif
50
65
 
51
- run_test = $(quiet_pre) ( $(t_wrap) ) $(quiet_post)
52
-
53
- test-bin-$(RUBY_VERSION)/rainbows: ruby_bin = $(shell which $(ruby))
66
+ test-bin-$(RUBY_VERSION)/rainbows: ruby_bin = $(shell which $(RUBY))
54
67
  test-bin-$(RUBY_VERSION)/rainbows: ../bin/rainbows
55
68
  mkdir -p $(@D)
56
- install -m 755 $^ $@+
57
- $(ruby) -i -p -e '$$_.gsub!(%r{^#!.*$$},"#!$(ruby_bin)")' $@+
58
- cmp $@+ $@ 2>/dev/null || mv $@+ $@
59
- $(RM) $@+
69
+ install -m 755 $^ $@.$(pid)
70
+ $(RUBY) -i -p -e '$$_.gsub!(%r{^#!.*$$},"#!$(ruby_bin)")' $@.$(pid)
71
+ mv $@.$(pid) $@
60
72
 
61
- req_random_blob := $(wildcard t?1??-*.sh)
62
73
  random_blob:
63
- dd if=/dev/urandom bs=1M count=10 of=$@+
64
- mv $@+ $@
65
-
66
- $(req_random_blob): random_blob
67
-
68
- $(T): trash/.gitignore
69
- $(T): export ruby := $(ruby)
70
- $(T): export PATH := $(CURDIR)/test-bin-$(RUBY_VERSION):$(PATH)
71
- $(T): test-bin-$(RUBY_VERSION)/rainbows
72
- $(run_test)
74
+ dd if=/dev/urandom bs=1M count=30 of=$@.$(pid)
75
+ mv $@.$(pid) $@
76
+
77
+ $(T): random_blob
78
+
79
+ dependencies := socat curl
80
+ deps := $(addprefix .dep+,$(dependencies))
81
+ $(deps): dep_bin = $(lastword $(subst +, ,$@))
82
+ $(deps):
83
+ @which $(dep_bin) > $@.$(pid) 2>/dev/null || :
84
+ @test -s $@.$(pid) || \
85
+ { echo >&2 "E `$(dep_bin)' not found in PATH=$(PATH)"; exit 1; }
86
+ @mv $@.$(pid) $@
87
+ dep: $(deps)
88
+
89
+ $(MODEL_T): export model = $(firstword $(subst ., ,$@))
90
+ $(MODEL_T): script = $(subst $(model).,,$@)
91
+ $(MODEL_T): trash/.gitignore
92
+ $(MODEL_T): export RUBY := $(RUBY)
93
+ $(MODEL_T): export PATH := $(CURDIR)/test-bin-$(RUBY_VERSION):$(PATH)
94
+ $(MODEL_T): test-bin-$(RUBY_VERSION)/rainbows dep
95
+ $(TRACER) $(SHELL) $(SH_TEST_OPTS) $(script) $(TEST_OPTS)
73
96
 
74
97
  trash/.gitignore:
75
98
  mkdir -p $(@D)
data/t/README CHANGED
@@ -28,9 +28,13 @@ To run the entire test suite with 8 tests running at once:
28
28
 
29
29
  make -j8
30
30
 
31
- To run one individual test:
31
+ To run one individual test for all concurrency models:
32
32
 
33
- make t0000-basic.sh
33
+ make t0000-simple-http.sh
34
+
35
+ To run one individual test for one concurrency model:
36
+
37
+ make Revactor.t0000-simple-http.sh
34
38
 
35
39
  You may also increase verbosity by setting the "V" variable for
36
40
  GNU make. To disable trapping of stdout/stderr:
@@ -0,0 +1,13 @@
1
+ # See http://github.com/raggi/async_sinatra
2
+ # gem install async_sinatra -v0.1.5
3
+ require 'sinatra/async'
4
+
5
+ class AsyncTest < Sinatra::Base
6
+ register Sinatra::Async
7
+
8
+ aget '/:n' do |n|
9
+ EM.add_timer(n.to_i) { body { "delayed for #{n} seconds\n" } }
10
+ end
11
+ end
12
+
13
+ run AsyncTest.new