rainbows 0.94.0 → 0.95.0

Sign up to get free protection for your applications and to get access to all the features.
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