rainbows 0.94.0 → 0.95.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 (84) hide show
  1. data/.document +1 -0
  2. data/.manifest +18 -0
  3. data/ChangeLog +394 -226
  4. data/GIT-VERSION-FILE +1 -1
  5. data/GIT-VERSION-GEN +1 -1
  6. data/GNUmakefile +6 -4
  7. data/NEWS +18 -0
  8. data/README +13 -5
  9. data/Static_Files +71 -0
  10. data/TODO +12 -0
  11. data/Test_Suite +1 -1
  12. data/bin/rainbows +1 -4
  13. data/lib/rainbows/actor_spawn.rb +1 -1
  14. data/lib/rainbows/app_pool.rb +1 -1
  15. data/lib/rainbows/base.rb +79 -89
  16. data/lib/rainbows/byte_slice.rb +17 -0
  17. data/lib/rainbows/configurator.rb +46 -0
  18. data/lib/rainbows/const.rb +2 -2
  19. data/lib/rainbows/dev_fd_response.rb +52 -44
  20. data/lib/rainbows/error.rb +1 -0
  21. data/lib/rainbows/ev_core.rb +3 -2
  22. data/lib/rainbows/event_machine.rb +26 -24
  23. data/lib/rainbows/fiber/base.rb +30 -40
  24. data/lib/rainbows/fiber/body.rb +34 -0
  25. data/lib/rainbows/fiber/io.rb +28 -8
  26. data/lib/rainbows/fiber/queue.rb +1 -0
  27. data/lib/rainbows/fiber/rev.rb +4 -2
  28. data/lib/rainbows/fiber.rb +1 -0
  29. data/lib/rainbows/fiber_pool.rb +2 -2
  30. data/lib/rainbows/fiber_spawn.rb +2 -2
  31. data/lib/rainbows/http_response.rb +20 -31
  32. data/lib/rainbows/http_server.rb +3 -4
  33. data/lib/rainbows/max_body.rb +1 -0
  34. data/lib/rainbows/never_block/event_machine.rb +2 -0
  35. data/lib/rainbows/never_block.rb +5 -4
  36. data/lib/rainbows/queue_pool.rb +1 -0
  37. data/lib/rainbows/response/body.rb +119 -0
  38. data/lib/rainbows/response.rb +43 -0
  39. data/lib/rainbows/rev/client.rb +79 -9
  40. data/lib/rainbows/rev/core.rb +4 -0
  41. data/lib/rainbows/rev/deferred_response.rb +1 -44
  42. data/lib/rainbows/rev/heartbeat.rb +1 -0
  43. data/lib/rainbows/rev/master.rb +1 -0
  44. data/lib/rainbows/rev/sendfile.rb +26 -0
  45. data/lib/rainbows/rev/thread.rb +2 -1
  46. data/lib/rainbows/rev.rb +2 -0
  47. data/lib/rainbows/rev_fiber_spawn.rb +3 -1
  48. data/lib/rainbows/rev_thread_pool.rb +7 -5
  49. data/lib/rainbows/rev_thread_spawn.rb +2 -2
  50. data/lib/rainbows/revactor.rb +146 -146
  51. data/lib/rainbows/sendfile.rb +10 -21
  52. data/lib/rainbows/server_token.rb +39 -0
  53. data/lib/rainbows/stream_file.rb +14 -0
  54. data/lib/rainbows/tee_input.rb +1 -0
  55. data/lib/rainbows/thread_pool.rb +12 -7
  56. data/lib/rainbows/thread_spawn.rb +2 -3
  57. data/lib/rainbows/writer_thread_pool.rb +13 -7
  58. data/lib/rainbows/writer_thread_spawn.rb +12 -9
  59. data/lib/rainbows.rb +16 -45
  60. data/rainbows.gemspec +8 -8
  61. data/t/.gitignore +1 -1
  62. data/t/GNUmakefile +26 -16
  63. data/t/README +1 -1
  64. data/t/async-response-no-autochunk.ru +0 -1
  65. data/t/async-response.ru +0 -1
  66. data/t/cramp/rainsocket.ru +26 -0
  67. data/t/fork-sleep.ru +0 -1
  68. data/t/my-tap-lib.sh +3 -2
  69. data/t/simple-http_ActorSpawn.ru +9 -0
  70. data/t/t0009-broken-app.sh +1 -1
  71. data/t/t0009.ru +0 -1
  72. data/t/t0011-close-on-exec-set.sh +1 -1
  73. data/t/t0015-working_directory.sh +56 -0
  74. data/t/t0016-onenine-encoding-is-tricky.sh +28 -0
  75. data/t/t0016.rb +15 -0
  76. data/t/t0020-large-sendfile-response.sh +141 -0
  77. data/t/t0300-async_sinatra.sh +0 -6
  78. data/t/t0501-cramp-rainsocket.sh +38 -0
  79. data/t/t9001-sendfile-to-path.sh +5 -4
  80. data/t/t9002-server-token.sh +37 -0
  81. data/t/t9002.ru +4 -0
  82. data/t/test-lib.sh +1 -1
  83. data/t/test_isolate.rb +14 -11
  84. metadata +87 -18
data/lib/rainbows.rb CHANGED
@@ -1,6 +1,10 @@
1
1
  # -*- encoding: binary -*-
2
2
  require 'unicorn'
3
+ # the value passed to TCP_DEFER_ACCEPT actually matters in Linux 2.6.32+
4
+ Unicorn::SocketHelper::DEFAULTS[:tcp_defer_accept] = 60
5
+
3
6
  require 'rainbows/error'
7
+ require 'rainbows/configurator'
4
8
  require 'fcntl'
5
9
 
6
10
  module Rainbows
@@ -8,6 +12,7 @@ module Rainbows
8
12
  # global vars because class/instance variables are confusing me :<
9
13
  # this struct is only accessed inside workers and thus private to each
10
14
  # G.cur may not be used in the network concurrency model
15
+ # :stopdoc:
11
16
  class State < Struct.new(:alive,:m,:cur,:kato,:server,:tmp,:expire)
12
17
  def tick
13
18
  tmp.chmod(self.m = m == 0 ? 1 : 0)
@@ -22,14 +27,13 @@ module Rainbows
22
27
  false
23
28
  end
24
29
  end
25
- # :stopdoc:
26
30
  G = State.new(true, 0, 0, 5)
27
31
  O = {}
28
32
  # :startdoc:
29
33
 
30
34
  require 'rainbows/const'
31
35
  require 'rainbows/http_server'
32
- require 'rainbows/http_response'
36
+ require 'rainbows/response'
33
37
  require 'rainbows/base'
34
38
  require 'rainbows/tee_input'
35
39
  autoload :Sendfile, 'rainbows/sendfile'
@@ -60,12 +64,12 @@ module Rainbows
60
64
 
61
65
  # runs the Rainbows! HttpServer with +app+ and +options+ and does
62
66
  # not return until the server has exited.
63
- def run(app, options = {})
67
+ def run(app, options = {}) # :nodoc:
64
68
  HttpServer.new(app, options).start.join
65
69
  end
66
70
 
67
71
  # returns nil if accept fails
68
- def sync_accept(sock)
72
+ def sync_accept(sock) # :nodoc:
69
73
  rv = sock.accept
70
74
  rv.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
71
75
  rv
@@ -73,7 +77,7 @@ module Rainbows
73
77
  end
74
78
 
75
79
  # returns nil if accept fails
76
- def accept(sock)
80
+ def accept(sock) # :nodoc:
77
81
  rv = sock.accept_nonblock
78
82
  rv.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
79
83
  rv
@@ -83,50 +87,18 @@ module Rainbows
83
87
  # returns a string representing the address of the given client +io+
84
88
  # For local UNIX domain sockets, this will return a string referred
85
89
  # to by the (non-frozen) Unicorn::HttpRequest::LOCALHOST constant.
86
- def addr(io)
90
+ def addr(io) # :nodoc:
87
91
  io.respond_to?(:peeraddr) ?
88
- io.peeraddr.last : Unicorn::HttpRequest::LOCALHOST
92
+ io.peeraddr[-1] : Unicorn::HttpRequest::LOCALHOST
89
93
  end
90
94
 
95
+ # :stopdoc:
91
96
  # the default max body size is 1 megabyte (1024 * 1024 bytes)
92
97
  @@max_bytes = 1024 * 1024
93
98
 
94
99
  def max_bytes; @@max_bytes; end
95
100
  def max_bytes=(nr); @@max_bytes = nr; end
96
- end
97
-
98
- # configures \Rainbows! with a given concurrency model to +use+ and
99
- # a +worker_connections+ upper-bound. This method may be called
100
- # inside a Unicorn/Rainbows configuration file:
101
- #
102
- # Rainbows! do
103
- # use :Revactor # this may also be :ThreadSpawn or :ThreadPool
104
- # worker_connections 400
105
- # keepalive_timeout 0 # zero disables keepalives entirely
106
- # client_max_body_size 5*1024*1024 # 5 megabytes
107
- # end
108
- #
109
- # # the rest of the Unicorn configuration
110
- # worker_processes 8
111
- #
112
- # See the documentation for the respective Revactor, ThreadSpawn,
113
- # and ThreadPool classes for descriptions and recommendations for
114
- # each of them. The total number of clients we're able to serve is
115
- # +worker_processes+ * +worker_connections+, so in the above example
116
- # we can serve 8 * 400 = 3200 clients concurrently.
117
- #
118
- # The default is +keepalive_timeout+ is 5 seconds, which should be
119
- # enough under most conditions for browsers to render the page and
120
- # start retrieving extra elements for. Increasing this beyond 5
121
- # seconds is not recommended. Zero disables keepalive entirely
122
- # (but pipelining fully-formed requests is still works).
123
- #
124
- # The default +client_max_body_size+ is 1 megabyte (1024 * 1024 bytes),
125
- # setting this to +nil+ will disable body size checks and allow any
126
- # size to be specified.
127
- def Rainbows!(&block)
128
- block_given? or raise ArgumentError, "Rainbows! requires a block"
129
- HttpServer.setup(block)
101
+ # :startdoc:
130
102
  end
131
103
 
132
104
  # :stopdoc:
@@ -155,8 +127,7 @@ module Rainbows
155
127
  end
156
128
  # :startdoc:
157
129
  autoload :Fiber, 'rainbows/fiber' # core class
158
-
130
+ autoload :ByteSlice, 'rainbows/byte_slice'
131
+ autoload :StreamFile, 'rainbows/stream_file'
132
+ autoload :HttpResponse, 'rainbows/http_response' # deprecated
159
133
  end
160
-
161
- # inject the Rainbows! method into Unicorn::Configurator
162
- Unicorn::Configurator.class_eval { include Rainbows }
data/rainbows.gemspec CHANGED
@@ -40,17 +40,17 @@ Gem::Specification.new do |s|
40
40
 
41
41
  s.test_files = test_files
42
42
 
43
- # we need Unicorn for the HTTP parser and process management
44
- # The HTTP parser in Unicorn <= 0.97.0 was vulnerable to a remote DoS
45
- # when exposed directly to untrusted clients.
46
- s.add_dependency(%q<unicorn>, [">= 0.97.1", "< 2.0.0"])
47
- s.add_development_dependency(%q<isolate>, "~> 2.0.2")
43
+ # we want a newer Rack for a valid HeaderHash#each
44
+ s.add_dependency(%q<rack>, ['~> 1.1'])
48
45
 
49
- # Unicorn already depends on Rack
50
- # s.add_dependency(%q<rack>)
46
+ # we need Unicorn for the HTTP parser and process management
47
+ # Unicorn 0.991.0 handles config.ru when started outside of
48
+ # the prespecified working_directory
49
+ s.add_dependency(%q<unicorn>, [">= 1.1.0", "< 2.0.0"])
50
+ s.add_development_dependency(%q<isolate>, "~> 2.1.0")
51
51
 
52
52
  # optional runtime dependencies depending on configuration
53
- # see config/isolate.rb for the exact versions we've tested with
53
+ # see t/test_isolate.rb for the exact versions we've tested with
54
54
  #
55
55
  # Revactor >= 0.1.5 includes UNIX domain socket support
56
56
  # s.add_dependency(%q<revactor>, [">= 0.1.5"])
data/t/.gitignore CHANGED
@@ -1,5 +1,5 @@
1
1
  /test-results-*
2
- /test-bin-*
2
+ /bin-*
3
3
  /random_blob
4
4
  /.dep+*
5
5
  /trash
data/t/GNUmakefile CHANGED
@@ -4,6 +4,7 @@ all::
4
4
 
5
5
  pid := $(shell echo $$PPID)
6
6
 
7
+ MRI = ruby
7
8
  RUBY = ruby
8
9
  rainbows_lib := $(shell cd ../lib && pwd)
9
10
  -include ../local.mk
@@ -15,7 +16,8 @@ ifeq ($(RUBY_VERSION),)
15
16
  $(error unable to detect RUBY_VERSION)
16
17
  endif
17
18
 
18
- export RUBY_VERSION
19
+ RUBY_ENGINE := $(shell $(RUBY) -e 'puts((RUBY_ENGINE rescue "ruby"))')
20
+ export RUBY_VERSION RUBY_ENGINE
19
21
 
20
22
  models += WriterThreadPool
21
23
  models += WriterThreadSpawn
@@ -27,13 +29,19 @@ models += NeverBlock
27
29
  models += RevThreadSpawn
28
30
  models += RevThreadPool
29
31
 
30
- rp := )
31
- ONENINE := $(shell case $(RUBY_VERSION) in 1.9.*$(rp) echo true;;esac)
32
- ifeq ($(ONENINE),true)
33
- models += Revactor
34
- models += FiberSpawn
35
- models += RevFiberSpawn
36
- models += FiberPool
32
+ ifeq ($(RUBY_ENGINE),ruby)
33
+ rp := )
34
+ ONENINE := $(shell case $(RUBY_VERSION) in 1.9.*$(rp) echo true;;esac)
35
+ ifeq ($(ONENINE),true)
36
+ models += Revactor
37
+ models += FiberSpawn
38
+ models += RevFiberSpawn
39
+ models += FiberPool
40
+ endif
41
+ endif
42
+
43
+ ifeq ($(RUBY_ENGINE),rbx)
44
+ models += ActorSpawn
37
45
  endif
38
46
  all_models := $(models) Base
39
47
 
@@ -60,7 +68,7 @@ $(all_models):
60
68
  all:: $(T)
61
69
 
62
70
  # can't rely on "set -o pipefail" since we don't require bash or ksh93 :<
63
- t_pfx = trash/$@-$(RUBY_VERSION)
71
+ t_pfx = trash/$@-$(RUBY_ENGINE)-$(RUBY_VERSION)
64
72
  TEST_OPTS =
65
73
  # TRACER = strace -f -o $(t_pfx).strace -s 100000
66
74
  # TRACER = /usr/bin/time -o $(t_pfx).time
@@ -73,11 +81,13 @@ ifdef V
73
81
  endif
74
82
  endif
75
83
 
76
- test-bin-$(RUBY_VERSION)/rainbows: ruby_bin = $(shell which $(RUBY))
77
- test-bin-$(RUBY_VERSION)/rainbows: ../bin/rainbows
84
+ bindir := $(CURDIR)/bin-$(RUBY_ENGINE)-$(RUBY_VERSION)
85
+ bin_rainbows := $(bindir)/rainbows
86
+ $(bin_rainbows): ruby_bin = $(shell which $(RUBY))
87
+ $(bin_rainbows): ../bin/rainbows
78
88
  mkdir -p $(@D)
79
89
  install -m 755 $^ $@.$(pid)
80
- $(RUBY) -i -p -e '$$_.gsub!(%r{^#!.*$$},"#!$(ruby_bin)")' $@.$(pid)
90
+ $(MRI) -i -p -e '$$_.gsub!(%r{^#!.*$$},"#!$(ruby_bin)")' $@.$(pid)
81
91
  mv $@.$(pid) $@
82
92
 
83
93
  random_blob:
@@ -95,18 +105,18 @@ $(deps):
95
105
  { echo >&2 "E '$(dep_bin)' not found in PATH=$(PATH)"; exit 1; }
96
106
  @mv $@.$(pid) $@
97
107
 
98
- libs := tmp/isolate/ruby-$(RUBY_VERSION)/.libs
108
+ libs := tmp/isolate/$(RUBY_ENGINE)-$(RUBY_VERSION)/.libs
99
109
  $(libs): test_isolate.rb
100
110
  mkdir -p $(@D)
101
111
  $(RUBY) $< > $@+
102
112
  mv $@+ $@
103
- t_deps := $(libs) $(deps) test-bin-$(RUBY_VERSION)/rainbows trash/.gitignore
113
+ t_deps := $(libs) $(deps) $(bin_rainbows) trash/.gitignore
104
114
  $(T): $(t_deps)
105
115
 
106
116
  $(MODEL_T): export model = $(firstword $(subst ., ,$@))
107
117
  $(MODEL_T): script = $(subst $(model).,,$@)
108
118
  $(MODEL_T): export RUBY := $(RUBY)
109
- $(MODEL_T): export PATH := $(CURDIR)/test-bin-$(RUBY_VERSION):$(PATH)
119
+ $(MODEL_T): export PATH := $(bindir):$(PATH)
110
120
  $(MODEL_T): $(t_deps)
111
121
  RUBYLIB=$(rainbows_lib):$$(cat $(libs)) \
112
122
  $(TRACER) $(SHELL) $(SH_TEST_OPTS) $(script) $(TEST_OPTS)
@@ -116,6 +126,6 @@ trash/.gitignore:
116
126
  echo '*' > $@
117
127
 
118
128
  clean:
119
- $(RM) -r trash/*.log trash/*.code test-bin-$(RUBY_VERSION)
129
+ $(RM) -r trash/*.log trash/*.code $(bindir)
120
130
 
121
131
  .PHONY: $(T) clean
data/t/README CHANGED
@@ -12,7 +12,7 @@ easily portable to non-Ruby web servers.
12
12
  == Requirements
13
13
 
14
14
  * {Ruby 1.8 or 1.9}[http://www.ruby-lang.org/] (duh!)
15
- * {isolate ~> 2.0.2}[http://github.com/jbarnette/isolate] - for dependencies
15
+ * {isolate ~> 2.1.0}[http://github.com/jbarnette/isolate] - for dependencies
16
16
  * {GNU make}[http://www.gnu.org/software/make/]
17
17
  * {socat}[http://www.dest-unreach.org/socat/]
18
18
  * {curl >= 7.18.0}[http://curl.haxx.se/]
@@ -12,7 +12,6 @@ EOF
12
12
  run lambda { |env|
13
13
  env['rainbows.autochunk'] = false
14
14
  io = IO.popen(script, 'rb')
15
- io.sync = true
16
15
  [
17
16
  200,
18
17
  {
data/t/async-response.ru CHANGED
@@ -2,7 +2,6 @@ use Rack::Chunked
2
2
  use Rainbows::DevFdResponse
3
3
  run lambda { |env|
4
4
  io = IO.popen('for i in 0 1 2 3 4 5 6 7 8 9; do date; sleep 1; done', 'rb')
5
- io.sync = true
6
5
  [
7
6
  200,
8
7
  {
@@ -0,0 +1,26 @@
1
+ # based on examples/rainsocket.ru git://github.com/lifo/cramp
2
+ # Rack::Lint does not like async + EM stuff, so disable it:
3
+ #\ -E deployment
4
+ require 'cramp/controller'
5
+
6
+ Cramp::Controller::Websocket.backend = :rainbows
7
+
8
+ class WelcomeController < Cramp::Controller::Websocket
9
+ periodic_timer :send_hello_world, :every => 2
10
+ on_data :received_data
11
+
12
+ def received_data(data)
13
+ if data =~ /fuck/
14
+ render "You cant say fuck in here"
15
+ finish
16
+ else
17
+ render "Got your #{data}"
18
+ end
19
+ end
20
+
21
+ def send_hello_world
22
+ render "Hello from the Server!\n"
23
+ end
24
+ end
25
+
26
+ run WelcomeController
data/t/fork-sleep.ru CHANGED
@@ -1,4 +1,3 @@
1
- #\-E none
2
1
  # we do not want Rack::Lint or anything to protect us
3
2
  use Rack::ContentLength
4
3
  use Rack::ContentType, "text/plain"
data/t/my-tap-lib.sh CHANGED
@@ -188,9 +188,10 @@ then
188
188
 
189
189
  (
190
190
  # use a subshell so seds are not waitable
191
- $SED -e 's/^/#: /' $t_stdout &
192
- $SED -e 's/^/#! /' $t_stderr &
191
+ $SED -e 's/^/#: /' < $t_stdout &
192
+ $SED -e 's/^/#! /' < $t_stderr &
193
193
  ) &
194
+ wait
194
195
  exec > $t_stdout 2> $t_stderr
195
196
  else
196
197
  exec > /dev/null 2> /dev/null
@@ -0,0 +1,9 @@
1
+ use Rack::ContentLength
2
+ use Rack::ContentType
3
+ run lambda { |env|
4
+ if env['rack.multithread'] && env['rainbows.model'] == :ActorSpawn
5
+ [ 200, {}, [ Actor.current.inspect << "\n" ] ]
6
+ else
7
+ raise "rack.multithread is not true"
8
+ end
9
+ }
@@ -5,7 +5,7 @@ t_plan 9 "graceful handling of broken apps for $model"
5
5
 
6
6
  t_begin "setup and start" && {
7
7
  rainbows_setup $model 1
8
- rainbows -D t0009.ru -c $unicorn_config
8
+ rainbows -E none -D t0009.ru -c $unicorn_config
9
9
  rainbows_wait_start
10
10
  }
11
11
 
data/t/t0009.ru CHANGED
@@ -1,4 +1,3 @@
1
- #\-E none
2
1
  # we do not want Rack::Lint or anything to protect us
3
2
  use Rack::ContentLength
4
3
  use Rack::ContentType, "text/plain"
@@ -6,7 +6,7 @@ t_plan 7 "ensure close-on-exec flag is set for $model"
6
6
 
7
7
  t_begin "setup and start" && {
8
8
  rainbows_setup $model 1 1
9
- nr=$nr rainbows -D fork-sleep.ru -c $unicorn_config
9
+ nr=$nr rainbows -E none -D fork-sleep.ru -c $unicorn_config
10
10
  rainbows_wait_start
11
11
  }
12
12
 
@@ -0,0 +1,56 @@
1
+ #!/bin/sh
2
+ if test -n "$RBX_SKIP"
3
+ then
4
+ echo "$0 is broken under Rubinius for now"
5
+ exit 0
6
+ fi
7
+ . ./test-lib.sh
8
+
9
+ t_plan 4 "config.ru inside alt working_directory"
10
+
11
+ t_begin "setup and start" && {
12
+ rainbows_setup
13
+ rtmpfiles unicorn_config_tmp
14
+ rm -rf $t_pfx.app
15
+ mkdir $t_pfx.app
16
+
17
+ cat > $t_pfx.app/config.ru <<EOF
18
+ #\--daemonize --listen $listen
19
+ use Rack::ContentLength
20
+ use Rack::ContentType, "text/plain"
21
+ run lambda { |env| [ 200, {}, [ "#{\$master_ppid}\\n" ] ] }
22
+ EOF
23
+ # we have --host/--port in config.ru instead
24
+ grep -v ^listen $unicorn_config > $unicorn_config_tmp
25
+
26
+ # the whole point of this exercise
27
+ echo "working_directory '$t_pfx.app'" >> $unicorn_config_tmp
28
+
29
+ # allows ppid to be 1 in before_fork
30
+ echo "preload_app true" >> $unicorn_config_tmp
31
+ cat >> $unicorn_config_tmp <<\EOF
32
+ before_fork do |server,worker|
33
+ $master_ppid = Process.ppid # should be zero to detect daemonization
34
+ end
35
+ EOF
36
+
37
+ mv $unicorn_config_tmp $unicorn_config
38
+
39
+ # rely on --daemonize switch, no & or -D
40
+ rainbows -c $unicorn_config
41
+ rainbows_wait_start
42
+ }
43
+
44
+ t_begin "hit with curl" && {
45
+ body=$(curl -sSf http://$listen/)
46
+ }
47
+
48
+ t_begin "killing succeeds" && {
49
+ kill $rainbows_pid
50
+ }
51
+
52
+ t_begin "response body ppid == 1 (daemonized)" && {
53
+ test "$body" -eq 1
54
+ }
55
+
56
+ t_done
@@ -0,0 +1,28 @@
1
+ #!/bin/sh
2
+ . ./test-lib.sh
3
+ t_plan 4 "proper handling of onenine encoding for $model"
4
+
5
+ t_begin "setup and startup" && {
6
+ rainbows_setup $model
7
+ rainbows -D ./t0016.rb -c $unicorn_config
8
+ rainbows_wait_start
9
+ expect_sha1=8ff79d8115f9fe38d18be858c66aa08a1cc27a66
10
+ }
11
+
12
+ t_begin "response matches expected" && {
13
+ rm -f $ok
14
+ (
15
+ curl -sSf http://$listen/ && echo ok > $ok
16
+ ) | rsha1 > $tmp
17
+ test x$expect_sha1 = x"$(cat $tmp)"
18
+ }
19
+
20
+ t_begin "shutdown server" && {
21
+ kill -QUIT $rainbows_pid
22
+ }
23
+
24
+ dbgcat r_err
25
+
26
+ t_begin "check stderr" && check_stderr
27
+
28
+ t_done
data/t/t0016.rb ADDED
@@ -0,0 +1,15 @@
1
+ # -*- encoding: utf-8 -*-
2
+ module T0016
3
+ CHUNK = '©' * 1024 * 1024
4
+ BODY = (1..50).map { CHUNK }
5
+ HEADER = {
6
+ # BODY.inject(0) { |m,c| m += c.bytesize }.to_s,
7
+ 'Content-Length' => '104857600',
8
+ 'Content-Type' => 'text/plain',
9
+ }
10
+
11
+ def self.call(env)
12
+ [ 200, HEADER, BODY ]
13
+ end
14
+ end
15
+ $0 == __FILE__ and T0016::BODY.each { |x| $stdout.syswrite(x) }
@@ -0,0 +1,141 @@
1
+ #!/bin/sh
2
+ . ./test-lib.sh
3
+ test -r random_blob || die "random_blob required, run with 'make $0'"
4
+ case $RUBY_ENGINE in
5
+ ruby) ;;
6
+ *)
7
+ t_info "skipping $T since it can't load the sendfile gem, yet"
8
+ exit 0
9
+ ;;
10
+ esac
11
+
12
+ t_plan 12 "large sendfile response for $model"
13
+
14
+ t_begin "setup and startup" && {
15
+ rtmpfiles curl_out a b c slow_a slow_b
16
+ rainbows_setup $model
17
+ echo 'require "sendfile"' >> $unicorn_config
18
+ echo 'def (::IO).copy_stream(*x); abort "NO"; end' >> $unicorn_config
19
+
20
+ # can't load Rack::Lint here since it clobbers body#to_path
21
+ rainbows -E none -D large-file-response.ru -c $unicorn_config
22
+ rainbows_wait_start
23
+ }
24
+
25
+ t_begin "read random blob sha1" && {
26
+ random_blob_sha1=$(rsha1 < random_blob)
27
+ three_sha1=$(cat random_blob random_blob random_blob | rsha1)
28
+ }
29
+
30
+ t_begin "send keepalive HTTP/1.1 requests in parallel" && {
31
+ for i in $a $b $c $slow_a $slow_b
32
+ do
33
+ curl -sSf http://$listen/random_blob \
34
+ http://$listen/random_blob \
35
+ http://$listen/random_blob | rsha1 > $i &
36
+ done
37
+ wait
38
+ for i in $a $b $c $slow_a $slow_b
39
+ do
40
+ test x$(cat $i) = x$three_sha1
41
+ done
42
+ }
43
+
44
+ t_begin "send a batch of abortive HTTP/1.1 requests in parallel" && {
45
+ for i in $a $b $c $slow_a $slow_b
46
+ do
47
+ rm -f $i
48
+ (
49
+ curl -sSf --max-time 5 --limit-rate 1K \
50
+ http://$listen/random_blob >/dev/null || echo ok > $i
51
+ ) &
52
+ done
53
+ wait
54
+ }
55
+
56
+ t_begin "all requests timed out" && {
57
+ for i in $a $b $c $slow_a $slow_b
58
+ do
59
+ test x$(cat $i) = xok
60
+ done
61
+ }
62
+
63
+ s='$NF ~ /worker_connections=[0-9]+/{gsub(/[^0-9]/,"",$3); print $3; exit}'
64
+ t_begin "check proc to ensure file is closed properly (Linux only)" && {
65
+ worker_pid=$(awk "$s" < $r_err)
66
+ test -n "$worker_pid"
67
+ if test -d /proc/$worker_pid/fd
68
+ then
69
+ if ls -l /proc/$worker_pid/fd | grep random_blob
70
+ then
71
+ t_info "random_blob file is open ($model)"
72
+ fi
73
+ else
74
+ t_info "/proc/$worker_pid/fd not found"
75
+ fi
76
+ }
77
+
78
+ t_begin "send a bunch of HTTP/1.1 requests in parallel" && {
79
+ (
80
+ curl -sSf --limit-rate 1M http://$listen/random_blob | \
81
+ rsha1 > $slow_a
82
+ ) &
83
+ (
84
+ curl -sSf --limit-rate 750K http://$listen/random_blob | \
85
+ rsha1 > $slow_b
86
+ ) &
87
+ for i in $a $b $c
88
+ do
89
+ (
90
+ curl -sSf http://$listen/random_blob | rsha1 > $i
91
+ ) &
92
+ done
93
+ wait
94
+ for i in $a $b $c $slow_a $slow_b
95
+ do
96
+ test x$(cat $i) = x$random_blob_sha1
97
+ done
98
+ }
99
+
100
+ # this was a problem during development
101
+ t_begin "HTTP/1.0 test" && {
102
+ sha1=$( (curl -0 -sSf http://$listen/random_blob &&
103
+ echo ok >$ok) | rsha1)
104
+ test $sha1 = $random_blob_sha1
105
+ test xok = x$(cat $ok)
106
+ }
107
+
108
+ t_begin "HTTP/0.9 test" && {
109
+ (
110
+ printf 'GET /random_blob\r\n'
111
+ rsha1 < $fifo > $tmp &
112
+ wait
113
+ echo ok > $ok
114
+ ) | socat - TCP:$listen > $fifo
115
+ test $(cat $tmp) = $random_blob_sha1
116
+ test xok = x$(cat $ok)
117
+ }
118
+
119
+ t_begin "check proc to ensure file is closed properly (Linux only)" && {
120
+ worker_pid=$(awk "$s" < $r_err)
121
+ test -n "$worker_pid"
122
+ if test -d /proc/$worker_pid/fd
123
+ then
124
+ if ls -l /proc/$worker_pid/fd | grep random_blob
125
+ then
126
+ t_info "random_blob file is open ($model)"
127
+ fi
128
+ else
129
+ t_info "/proc/$worker_pid/fd not found"
130
+ fi
131
+ }
132
+
133
+ t_begin "shutdown server" && {
134
+ kill -QUIT $rainbows_pid
135
+ }
136
+
137
+ dbgcat r_err
138
+
139
+ t_begin "check stderr" && check_stderr
140
+
141
+ t_done
@@ -1,11 +1,5 @@
1
1
  #!/bin/sh
2
2
  . ./test-lib.sh
3
- case $RUBY_VERSION in
4
- 1.9.2)
5
- t_info "RUBY_VERSION=$RUBY_VERSION not supported with async_sinatra"
6
- exit 0
7
- ;;
8
- esac
9
3
 
10
4
  # n - number of seconds to sleep
11
5
  n=10
@@ -0,0 +1,38 @@
1
+ #!/bin/sh
2
+ . ./test-lib.sh
3
+ case $model in
4
+ EventMachine) ;;
5
+ *)
6
+ t_info "skipping $T since it's not compatible with $model"
7
+ exit 0
8
+ ;;
9
+ esac
10
+ require_check cramp Cramp::VERSION
11
+
12
+ t_plan 4 "WebSocket monkey patch validity test for Cramp"
13
+
14
+ CONFIG_RU=cramp/rainsocket.ru
15
+
16
+ t_begin "setup and start" && {
17
+ rainbows_setup
18
+ rtmpfiles curl_err
19
+
20
+ # Like the rest of the EM/async stuff, it's not Rack::Lint compatible
21
+ rainbows -E deployment -D $CONFIG_RU -c $unicorn_config
22
+ rainbows_wait_start
23
+ }
24
+
25
+ t_begin "wait for server to say hello to us" && {
26
+ ok=$((curl --no-buffer -sS http://$listen/ || :) | \
27
+ awk '/Hello from the Server/ { print "ok"; exit 0 }')
28
+
29
+ test x"$ok" = xok
30
+ }
31
+
32
+ t_begin "termination signal sent" && {
33
+ kill $rainbows_pid
34
+ }
35
+
36
+ t_begin "no errors in stderr" && check_stderr
37
+
38
+ t_done