unicorn-fotopedia 0.99.1
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.
- data/.CHANGELOG.old +25 -0
- data/.document +19 -0
- data/.gitignore +21 -0
- data/.mailmap +26 -0
- data/CONTRIBUTORS +32 -0
- data/COPYING +339 -0
- data/DESIGN +105 -0
- data/Documentation/.gitignore +5 -0
- data/Documentation/GNUmakefile +30 -0
- data/Documentation/unicorn.1.txt +171 -0
- data/Documentation/unicorn_rails.1.txt +172 -0
- data/FAQ +52 -0
- data/GIT-VERSION-GEN +40 -0
- data/GNUmakefile +292 -0
- data/HACKING +116 -0
- data/ISSUES +36 -0
- data/KNOWN_ISSUES +50 -0
- data/LICENSE +55 -0
- data/PHILOSOPHY +145 -0
- data/README +149 -0
- data/Rakefile +191 -0
- data/SIGNALS +109 -0
- data/Sandbox +78 -0
- data/TODO +5 -0
- data/TUNING +70 -0
- data/bin/unicorn +126 -0
- data/bin/unicorn_rails +203 -0
- data/examples/big_app_gc.rb +33 -0
- data/examples/echo.ru +27 -0
- data/examples/git.ru +13 -0
- data/examples/init.sh +58 -0
- data/examples/logger_mp_safe.rb +25 -0
- data/examples/nginx.conf +139 -0
- data/examples/unicorn.conf.rb +78 -0
- data/ext/unicorn_http/CFLAGS +13 -0
- data/ext/unicorn_http/c_util.h +124 -0
- data/ext/unicorn_http/common_field_optimization.h +111 -0
- data/ext/unicorn_http/ext_help.h +77 -0
- data/ext/unicorn_http/extconf.rb +14 -0
- data/ext/unicorn_http/global_variables.h +89 -0
- data/ext/unicorn_http/unicorn_http.rl +714 -0
- data/ext/unicorn_http/unicorn_http_common.rl +75 -0
- data/lib/unicorn.rb +847 -0
- data/lib/unicorn/app/exec_cgi.rb +150 -0
- data/lib/unicorn/app/inetd.rb +109 -0
- data/lib/unicorn/app/old_rails.rb +33 -0
- data/lib/unicorn/app/old_rails/static.rb +58 -0
- data/lib/unicorn/cgi_wrapper.rb +145 -0
- data/lib/unicorn/configurator.rb +421 -0
- data/lib/unicorn/const.rb +34 -0
- data/lib/unicorn/http_request.rb +72 -0
- data/lib/unicorn/http_response.rb +75 -0
- data/lib/unicorn/launcher.rb +65 -0
- data/lib/unicorn/oob_gc.rb +58 -0
- data/lib/unicorn/socket_helper.rb +152 -0
- data/lib/unicorn/tee_input.rb +217 -0
- data/lib/unicorn/util.rb +90 -0
- data/local.mk.sample +62 -0
- data/setup.rb +1586 -0
- data/t/.gitignore +2 -0
- data/t/GNUmakefile +67 -0
- data/t/README +42 -0
- data/t/bin/content-md5-put +36 -0
- data/t/bin/sha1sum.rb +23 -0
- data/t/bin/unused_listen +40 -0
- data/t/bin/utee +12 -0
- data/t/env.ru +3 -0
- data/t/my-tap-lib.sh +200 -0
- data/t/t0000-http-basic.sh +50 -0
- data/t/t0001-reload-bad-config.sh +52 -0
- data/t/t0002-config-conflict.sh +49 -0
- data/t/test-lib.sh +100 -0
- data/test/aggregate.rb +15 -0
- data/test/benchmark/README +50 -0
- data/test/benchmark/dd.ru +18 -0
- data/test/exec/README +5 -0
- data/test/exec/test_exec.rb +1038 -0
- data/test/rails/app-1.2.3/.gitignore +2 -0
- data/test/rails/app-1.2.3/Rakefile +7 -0
- data/test/rails/app-1.2.3/app/controllers/application.rb +6 -0
- data/test/rails/app-1.2.3/app/controllers/foo_controller.rb +36 -0
- data/test/rails/app-1.2.3/app/helpers/application_helper.rb +4 -0
- data/test/rails/app-1.2.3/config/boot.rb +11 -0
- data/test/rails/app-1.2.3/config/database.yml +12 -0
- data/test/rails/app-1.2.3/config/environment.rb +13 -0
- data/test/rails/app-1.2.3/config/environments/development.rb +9 -0
- data/test/rails/app-1.2.3/config/environments/production.rb +5 -0
- data/test/rails/app-1.2.3/config/routes.rb +6 -0
- data/test/rails/app-1.2.3/db/.gitignore +0 -0
- data/test/rails/app-1.2.3/public/404.html +1 -0
- data/test/rails/app-1.2.3/public/500.html +1 -0
- data/test/rails/app-2.0.2/.gitignore +2 -0
- data/test/rails/app-2.0.2/Rakefile +7 -0
- data/test/rails/app-2.0.2/app/controllers/application.rb +4 -0
- data/test/rails/app-2.0.2/app/controllers/foo_controller.rb +36 -0
- data/test/rails/app-2.0.2/app/helpers/application_helper.rb +4 -0
- data/test/rails/app-2.0.2/config/boot.rb +11 -0
- data/test/rails/app-2.0.2/config/database.yml +12 -0
- data/test/rails/app-2.0.2/config/environment.rb +17 -0
- data/test/rails/app-2.0.2/config/environments/development.rb +8 -0
- data/test/rails/app-2.0.2/config/environments/production.rb +5 -0
- data/test/rails/app-2.0.2/config/routes.rb +6 -0
- data/test/rails/app-2.0.2/db/.gitignore +0 -0
- data/test/rails/app-2.0.2/public/404.html +1 -0
- data/test/rails/app-2.0.2/public/500.html +1 -0
- data/test/rails/app-2.1.2/.gitignore +2 -0
- data/test/rails/app-2.1.2/Rakefile +7 -0
- data/test/rails/app-2.1.2/app/controllers/application.rb +4 -0
- data/test/rails/app-2.1.2/app/controllers/foo_controller.rb +36 -0
- data/test/rails/app-2.1.2/app/helpers/application_helper.rb +4 -0
- data/test/rails/app-2.1.2/config/boot.rb +111 -0
- data/test/rails/app-2.1.2/config/database.yml +12 -0
- data/test/rails/app-2.1.2/config/environment.rb +17 -0
- data/test/rails/app-2.1.2/config/environments/development.rb +7 -0
- data/test/rails/app-2.1.2/config/environments/production.rb +5 -0
- data/test/rails/app-2.1.2/config/routes.rb +6 -0
- data/test/rails/app-2.1.2/db/.gitignore +0 -0
- data/test/rails/app-2.1.2/public/404.html +1 -0
- data/test/rails/app-2.1.2/public/500.html +1 -0
- data/test/rails/app-2.2.2/.gitignore +2 -0
- data/test/rails/app-2.2.2/Rakefile +7 -0
- data/test/rails/app-2.2.2/app/controllers/application.rb +4 -0
- data/test/rails/app-2.2.2/app/controllers/foo_controller.rb +36 -0
- data/test/rails/app-2.2.2/app/helpers/application_helper.rb +4 -0
- data/test/rails/app-2.2.2/config/boot.rb +111 -0
- data/test/rails/app-2.2.2/config/database.yml +12 -0
- data/test/rails/app-2.2.2/config/environment.rb +17 -0
- data/test/rails/app-2.2.2/config/environments/development.rb +7 -0
- data/test/rails/app-2.2.2/config/environments/production.rb +5 -0
- data/test/rails/app-2.2.2/config/routes.rb +6 -0
- data/test/rails/app-2.2.2/db/.gitignore +0 -0
- data/test/rails/app-2.2.2/public/404.html +1 -0
- data/test/rails/app-2.2.2/public/500.html +1 -0
- data/test/rails/app-2.3.5/.gitignore +2 -0
- data/test/rails/app-2.3.5/Rakefile +7 -0
- data/test/rails/app-2.3.5/app/controllers/application_controller.rb +5 -0
- data/test/rails/app-2.3.5/app/controllers/foo_controller.rb +36 -0
- data/test/rails/app-2.3.5/app/helpers/application_helper.rb +4 -0
- data/test/rails/app-2.3.5/config/boot.rb +109 -0
- data/test/rails/app-2.3.5/config/database.yml +12 -0
- data/test/rails/app-2.3.5/config/environment.rb +17 -0
- data/test/rails/app-2.3.5/config/environments/development.rb +7 -0
- data/test/rails/app-2.3.5/config/environments/production.rb +6 -0
- data/test/rails/app-2.3.5/config/routes.rb +6 -0
- data/test/rails/app-2.3.5/db/.gitignore +0 -0
- data/test/rails/app-2.3.5/public/404.html +1 -0
- data/test/rails/app-2.3.5/public/500.html +1 -0
- data/test/rails/app-2.3.5/public/x.txt +1 -0
- data/test/rails/test_rails.rb +280 -0
- data/test/test_helper.rb +301 -0
- data/test/unit/test_configurator.rb +150 -0
- data/test/unit/test_http_parser.rb +555 -0
- data/test/unit/test_http_parser_ng.rb +443 -0
- data/test/unit/test_request.rb +184 -0
- data/test/unit/test_response.rb +110 -0
- data/test/unit/test_server.rb +291 -0
- data/test/unit/test_signals.rb +206 -0
- data/test/unit/test_socket_helper.rb +147 -0
- data/test/unit/test_tee_input.rb +257 -0
- data/test/unit/test_upload.rb +298 -0
- data/test/unit/test_util.rb +96 -0
- data/unicorn.gemspec +52 -0
- metadata +283 -0
@@ -0,0 +1,17 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
|
3
|
+
unless defined? RAILS_GEM_VERSION
|
4
|
+
RAILS_GEM_VERSION = ENV['UNICORN_RAILS_VERSION']
|
5
|
+
end
|
6
|
+
|
7
|
+
# Bootstrap the Rails environment, frameworks, and default configuration
|
8
|
+
require File.join(File.dirname(__FILE__), 'boot')
|
9
|
+
|
10
|
+
Rails::Initializer.run do |config|
|
11
|
+
config.frameworks -= [ :active_resource, :action_mailer ]
|
12
|
+
config.action_controller.session_store = :active_record_store
|
13
|
+
config.action_controller.session = {
|
14
|
+
:session_key => "_unicorn_rails_test.#{rand}",
|
15
|
+
:secret => "#{rand}#{rand}#{rand}#{rand}",
|
16
|
+
}
|
17
|
+
end
|
File without changes
|
@@ -0,0 +1 @@
|
|
1
|
+
404 Not Found
|
@@ -0,0 +1 @@
|
|
1
|
+
500 Internal Server Error
|
@@ -0,0 +1 @@
|
|
1
|
+
HELLO
|
@@ -0,0 +1,280 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
|
3
|
+
# Copyright (c) 2009 Eric Wong
|
4
|
+
require 'test/test_helper'
|
5
|
+
|
6
|
+
# don't call exit(0) since it may be run under rake (but gmake is recommended)
|
7
|
+
do_test = true
|
8
|
+
|
9
|
+
$unicorn_rails_bin = ENV['UNICORN_RAILS_TEST_BIN'] || "unicorn_rails"
|
10
|
+
redirect_test_io { do_test = system($unicorn_rails_bin, '-v') }
|
11
|
+
|
12
|
+
unless do_test
|
13
|
+
warn "#$unicorn_rails_bin not found in PATH=#{ENV['PATH']}, " \
|
14
|
+
"skipping this test"
|
15
|
+
end
|
16
|
+
|
17
|
+
unless which('git')
|
18
|
+
warn "git not found in PATH=#{ENV['PATH']}, skipping this test"
|
19
|
+
do_test = false
|
20
|
+
end
|
21
|
+
|
22
|
+
if RAILS_GIT_REPO = ENV['RAILS_GIT_REPO']
|
23
|
+
unless File.directory?(RAILS_GIT_REPO)
|
24
|
+
warn "#{RAILS_GIT_REPO} not found, create it with:\n" \
|
25
|
+
"\tgit clone --mirror git://github.com/rails/rails #{RAILS_GIT_REPO}" \
|
26
|
+
"skipping this test for now"
|
27
|
+
do_test = false
|
28
|
+
end
|
29
|
+
else
|
30
|
+
warn "RAILS_GIT_REPO not defined, don't know where to git clone from"
|
31
|
+
do_test = false
|
32
|
+
end
|
33
|
+
|
34
|
+
unless UNICORN_RAILS_TEST_VERSION = ENV['UNICORN_RAILS_TEST_VERSION']
|
35
|
+
warn 'UNICORN_RAILS_TEST_VERSION not defined in environment, ' \
|
36
|
+
'skipping this test'
|
37
|
+
do_test = false
|
38
|
+
end
|
39
|
+
|
40
|
+
RAILS_ROOT = "#{File.dirname(__FILE__)}/app-#{UNICORN_RAILS_TEST_VERSION}"
|
41
|
+
unless File.directory?(RAILS_ROOT)
|
42
|
+
warn "unsupported UNICORN_RAILS_TEST_VERSION=#{UNICORN_RAILS_TEST_VERSION}"
|
43
|
+
do_test = false
|
44
|
+
end
|
45
|
+
|
46
|
+
ROR_V = UNICORN_RAILS_TEST_VERSION.split(/\./).map { |x| x.to_i }
|
47
|
+
RB_V = RUBY_VERSION.split(/\./).map { |x| x.to_i }
|
48
|
+
if RB_V[0] >= 1 && RB_V[1] >= 9
|
49
|
+
unless ROR_V[0] >= 2 && ROR_V[1] >= 3
|
50
|
+
warn "skipping Ruby >=1.9 test with Rails <2.3"
|
51
|
+
do_test = false
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class RailsTest < Test::Unit::TestCase
|
56
|
+
trap(:QUIT, 'IGNORE')
|
57
|
+
|
58
|
+
COMMON_TMP = Tempfile.new('unicorn_tmp') unless defined?(COMMON_TMP)
|
59
|
+
|
60
|
+
HEAVY_CFG = <<-EOS
|
61
|
+
worker_processes 2
|
62
|
+
timeout 30
|
63
|
+
logger Logger.new('#{COMMON_TMP.path}')
|
64
|
+
EOS
|
65
|
+
|
66
|
+
def setup
|
67
|
+
@pwd = Dir.pwd
|
68
|
+
@tmpfile = Tempfile.new('unicorn_rails_test')
|
69
|
+
@tmpdir = @tmpfile.path
|
70
|
+
@tmpfile.close!
|
71
|
+
assert_nothing_raised do
|
72
|
+
FileUtils.cp_r(RAILS_ROOT, @tmpdir, :preserve => true)
|
73
|
+
end
|
74
|
+
Dir.chdir(@tmpdir)
|
75
|
+
system('git', 'clone', '-nsq', RAILS_GIT_REPO, 'vendor/rails')
|
76
|
+
Dir.chdir("#@tmpdir/vendor/rails") do
|
77
|
+
system('git', 'reset', '-q', '--hard', "v#{UNICORN_RAILS_TEST_VERSION}")
|
78
|
+
end
|
79
|
+
|
80
|
+
assert(system('rake', 'db:sessions:create'))
|
81
|
+
assert(system('rake', 'db:migrate'))
|
82
|
+
|
83
|
+
@addr = ENV['UNICORN_TEST_ADDR'] || '127.0.0.1'
|
84
|
+
@port = unused_port(@addr)
|
85
|
+
@start_pid = $$
|
86
|
+
@pid = nil
|
87
|
+
end
|
88
|
+
|
89
|
+
def test_launcher
|
90
|
+
tmp_dirs = %w(cache pids sessions sockets)
|
91
|
+
tmp_dirs.each { |dir| assert(! File.exist?("tmp/#{dir}")) }
|
92
|
+
redirect_test_io { @pid = fork { exec 'unicorn_rails', "-l#@addr:#@port" } }
|
93
|
+
wait_master_ready("test_stderr.#$$.log")
|
94
|
+
|
95
|
+
# basic GET
|
96
|
+
res = Net::HTTP.get_response(URI.parse("http://#@addr:#@port/foo"))
|
97
|
+
assert_equal "FOO\n", res.body
|
98
|
+
assert_match %r{^text/html\b}, res['Content-Type']
|
99
|
+
assert_equal "4", res['Content-Length']
|
100
|
+
assert_equal "200 OK", res['Status']
|
101
|
+
|
102
|
+
# temp dirs exist
|
103
|
+
tmp_dirs.each { |dir| assert(File.directory?("tmp/#{dir}")) }
|
104
|
+
|
105
|
+
# can we set cookies?
|
106
|
+
res = Net::HTTP.get_response(URI.parse("http://#@addr:#@port/foo/xcookie"))
|
107
|
+
assert_equal "200", res.code
|
108
|
+
assert_equal "200 OK", res['Status']
|
109
|
+
cookies = res.get_fields('Set-Cookie')
|
110
|
+
assert_equal 2, cookies.size
|
111
|
+
assert_equal 1, cookies.grep(/\A_unicorn_rails_test\./).size
|
112
|
+
assert_equal 1, cookies.grep(/\Afoo=cookie/).size
|
113
|
+
|
114
|
+
# how about just a session?
|
115
|
+
res = Net::HTTP.get_response(URI.parse("http://#@addr:#@port/foo/xnotice"))
|
116
|
+
assert_equal "200", res.code
|
117
|
+
assert_equal "200 OK", res['Status']
|
118
|
+
cookies = res.get_fields('Set-Cookie')
|
119
|
+
assert_equal 1, cookies.size
|
120
|
+
assert_equal 1, cookies.grep(/\A_unicorn_rails_test\./).size
|
121
|
+
|
122
|
+
# posting forms?
|
123
|
+
uri = URI.parse("http://#@addr:#@port/foo/xpost")
|
124
|
+
wait_master_ready("test_stderr.#$$.log")
|
125
|
+
res = Net::HTTP.post_form(uri, {"a" => "b", "c"=>"d"})
|
126
|
+
assert_equal "200", res.code
|
127
|
+
params = res.body.split(/\n/).grep(/^params:/)
|
128
|
+
assert_equal 1, params.size
|
129
|
+
params = eval(params[0].gsub!(/\Aparams:/, ''))
|
130
|
+
assert_equal Hash, params.class
|
131
|
+
assert_equal 'b', params['a']
|
132
|
+
assert_equal 'd', params['c']
|
133
|
+
assert_equal "200 OK", res['Status']
|
134
|
+
|
135
|
+
# try uploading a big file
|
136
|
+
tmp = Tempfile.new('random')
|
137
|
+
sha1 = Digest::SHA1.new
|
138
|
+
assert_nothing_raised do
|
139
|
+
File.open("/dev/urandom", "rb") do |fp|
|
140
|
+
256.times do
|
141
|
+
buf = fp.sysread(4096)
|
142
|
+
sha1.update(buf)
|
143
|
+
tmp.syswrite(buf)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
# fixed in Rack commit 44ed4640f077504a49b7f1cabf8d6ad7a13f6441,
|
149
|
+
# no released version of Rails or Rack has this fix
|
150
|
+
if RB_V[0] >= 1 && RB_V[1] >= 9
|
151
|
+
warn "multipart broken with Rack 1.0.0 and Rails 2.3.2.1 under 1.9"
|
152
|
+
else
|
153
|
+
resp = `curl -isSfN -Ffile=@#{tmp.path} http://#@addr:#@port/foo/xpost`
|
154
|
+
assert $?.success?
|
155
|
+
resp = resp.split(/\r?\n/)
|
156
|
+
grepped = resp.grep(/^sha1: (.{40})/)
|
157
|
+
assert_equal 1, grepped.size
|
158
|
+
assert_equal(sha1.hexdigest, /^sha1: (.{40})/.match(grepped.first)[1])
|
159
|
+
|
160
|
+
grepped = resp.grep(/^Content-Type:\s+(.+)/i)
|
161
|
+
assert_equal 1, grepped.size
|
162
|
+
assert_match %r{^text/plain}, grepped.first.split(/\s*:\s*/)[1]
|
163
|
+
assert_equal 1, resp.grep(/^Status:/i).size
|
164
|
+
end
|
165
|
+
|
166
|
+
# make sure we can get 403 responses, too
|
167
|
+
uri = URI.parse("http://#@addr:#@port/foo/xpost")
|
168
|
+
wait_master_ready("test_stderr.#$$.log")
|
169
|
+
res = Net::HTTP.get_response(uri)
|
170
|
+
assert_equal "403", res.code
|
171
|
+
assert_equal "403 Forbidden", res['Status']
|
172
|
+
|
173
|
+
# non existent controller
|
174
|
+
uri = URI.parse("http://#@addr:#@port/asdf")
|
175
|
+
res = Net::HTTP.get_response(uri)
|
176
|
+
assert_equal "404", res.code
|
177
|
+
assert_equal "404 Not Found", res['Status']
|
178
|
+
|
179
|
+
# static files
|
180
|
+
|
181
|
+
# ensure file we're about to serve is not there yet
|
182
|
+
res = Net::HTTP.get_response(URI.parse("http://#@addr:#@port/pid.txt"))
|
183
|
+
assert_equal "404 Not Found", res['Status']
|
184
|
+
assert_equal '404', res.code
|
185
|
+
|
186
|
+
# can we serve text files based on suffix?
|
187
|
+
File.open("public/pid.txt", "wb") { |fp| fp.syswrite("#$$\n") }
|
188
|
+
res = Net::HTTP.get_response(URI.parse("http://#@addr:#@port/pid.txt"))
|
189
|
+
assert_equal '200', res.code
|
190
|
+
assert_equal "200 OK", res['Status']
|
191
|
+
assert_match %r{^text/plain}, res['Content-Type']
|
192
|
+
assert_equal "#$$\n", res.body
|
193
|
+
|
194
|
+
# can we serve HTML files based on suffix?
|
195
|
+
assert File.exist?("public/500.html")
|
196
|
+
res = Net::HTTP.get_response(URI.parse("http://#@addr:#@port/500.html"))
|
197
|
+
assert_equal '200', res.code
|
198
|
+
assert_equal '200 OK', res['Status']
|
199
|
+
assert_match %r{^text/html}, res['Content-Type']
|
200
|
+
five_hundred_body = res.body
|
201
|
+
|
202
|
+
# lets try pretending 500 is a controller that got cached
|
203
|
+
assert ! File.exist?("public/500")
|
204
|
+
assert_equal five_hundred_body, File.read("public/500.html")
|
205
|
+
res = Net::HTTP.get_response(URI.parse("http://#@addr:#@port/500"))
|
206
|
+
assert_equal '200', res.code
|
207
|
+
assert_equal '200 OK', res['Status']
|
208
|
+
assert_match %r{^text/html}, res['Content-Type']
|
209
|
+
assert_equal five_hundred_body, res.body
|
210
|
+
end
|
211
|
+
|
212
|
+
def test_alt_url_root
|
213
|
+
# cbf to actually work on this since I never use this feature (ewong)
|
214
|
+
return unless ROR_V[0] >= 2 && ROR_V[1] >= 3
|
215
|
+
redirect_test_io do
|
216
|
+
@pid = fork { exec 'unicorn_rails', "-l#@addr:#@port", '-P/poo' }
|
217
|
+
end
|
218
|
+
wait_master_ready("test_stderr.#$$.log")
|
219
|
+
res = Net::HTTP.get_response(URI.parse("http://#@addr:#@port/poo/foo"))
|
220
|
+
# p res
|
221
|
+
# p res.body
|
222
|
+
# system 'cat', 'log/development.log'
|
223
|
+
assert_equal "200", res.code
|
224
|
+
assert_equal '200 OK', res['Status']
|
225
|
+
assert_equal "FOO\n", res.body
|
226
|
+
assert_match %r{^text/html\b}, res['Content-Type']
|
227
|
+
assert_equal "4", res['Content-Length']
|
228
|
+
|
229
|
+
res = Net::HTTP.get_response(URI.parse("http://#@addr:#@port/foo"))
|
230
|
+
assert_equal "404", res.code
|
231
|
+
assert_equal '404 Not Found', res['Status']
|
232
|
+
end
|
233
|
+
|
234
|
+
def test_alt_url_root_config_env
|
235
|
+
# cbf to actually work on this since I never use this feature (ewong)
|
236
|
+
return unless ROR_V[0] >= 2 && ROR_V[1] >= 3
|
237
|
+
tmp = Tempfile.new('')
|
238
|
+
tmp.syswrite("ENV['RAILS_RELATIVE_URL_ROOT'] = '/poo'\n")
|
239
|
+
redirect_test_io do
|
240
|
+
@pid = fork { exec 'unicorn_rails', "-l#@addr:#@port", "-c", tmp.path }
|
241
|
+
end
|
242
|
+
wait_master_ready("test_stderr.#$$.log")
|
243
|
+
res = Net::HTTP.get_response(URI.parse("http://#@addr:#@port/poo/foo"))
|
244
|
+
assert_equal "200", res.code
|
245
|
+
assert_equal '200 OK', res['Status']
|
246
|
+
assert_equal "FOO\n", res.body
|
247
|
+
assert_match %r{^text/html\b}, res['Content-Type']
|
248
|
+
assert_equal "4", res['Content-Length']
|
249
|
+
|
250
|
+
res = Net::HTTP.get_response(URI.parse("http://#@addr:#@port/foo"))
|
251
|
+
assert_equal "404", res.code
|
252
|
+
assert_equal '404 Not Found', res['Status']
|
253
|
+
|
254
|
+
res = Net::HTTP.get_response(URI.parse("http://#@addr:#@port/poo/x.txt"))
|
255
|
+
assert_equal "200", res.code
|
256
|
+
assert_equal "HELLO\n", res.body
|
257
|
+
end
|
258
|
+
|
259
|
+
def teardown
|
260
|
+
return if @start_pid != $$
|
261
|
+
|
262
|
+
if @pid
|
263
|
+
Process.kill(:QUIT, @pid)
|
264
|
+
pid2, status = Process.waitpid2(@pid)
|
265
|
+
assert status.success?
|
266
|
+
end
|
267
|
+
|
268
|
+
Dir.chdir(@pwd)
|
269
|
+
FileUtils.rmtree(@tmpdir)
|
270
|
+
loop do
|
271
|
+
Process.kill('-QUIT', 0)
|
272
|
+
begin
|
273
|
+
Process.waitpid(-1, Process::WNOHANG) or break
|
274
|
+
rescue Errno::ECHILD
|
275
|
+
break
|
276
|
+
end
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
end if do_test
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,301 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
|
3
|
+
# Copyright (c) 2005 Zed A. Shaw
|
4
|
+
# You can redistribute it and/or modify it under the same terms as Ruby.
|
5
|
+
#
|
6
|
+
# Additional work donated by contributors. See http://mongrel.rubyforge.org/attributions.html
|
7
|
+
# for more information.
|
8
|
+
|
9
|
+
STDIN.sync = STDOUT.sync = STDERR.sync = true # buffering makes debugging hard
|
10
|
+
|
11
|
+
# Some tests watch a log file or a pid file to spring up to check state
|
12
|
+
# Can't rely on inotify on non-Linux and logging to a pipe makes things
|
13
|
+
# more complicated
|
14
|
+
DEFAULT_TRIES = 1000
|
15
|
+
DEFAULT_RES = 0.2
|
16
|
+
|
17
|
+
HERE = File.dirname(__FILE__) unless defined?(HERE)
|
18
|
+
%w(lib ext).each do |dir|
|
19
|
+
$LOAD_PATH.unshift "#{HERE}/../#{dir}"
|
20
|
+
end
|
21
|
+
|
22
|
+
require 'test/unit'
|
23
|
+
require 'net/http'
|
24
|
+
require 'digest/sha1'
|
25
|
+
require 'uri'
|
26
|
+
require 'stringio'
|
27
|
+
require 'pathname'
|
28
|
+
require 'tempfile'
|
29
|
+
require 'fileutils'
|
30
|
+
require 'logger'
|
31
|
+
require 'unicorn'
|
32
|
+
require 'unicorn_http'
|
33
|
+
|
34
|
+
if ENV['DEBUG']
|
35
|
+
require 'ruby-debug'
|
36
|
+
Debugger.start
|
37
|
+
end
|
38
|
+
|
39
|
+
def redirect_test_io
|
40
|
+
orig_err = STDERR.dup
|
41
|
+
orig_out = STDOUT.dup
|
42
|
+
STDERR.reopen("test_stderr.#{$$}.log", "a")
|
43
|
+
STDOUT.reopen("test_stdout.#{$$}.log", "a")
|
44
|
+
STDERR.sync = STDOUT.sync = true
|
45
|
+
|
46
|
+
at_exit do
|
47
|
+
File.unlink("test_stderr.#{$$}.log") rescue nil
|
48
|
+
File.unlink("test_stdout.#{$$}.log") rescue nil
|
49
|
+
end
|
50
|
+
|
51
|
+
begin
|
52
|
+
yield
|
53
|
+
ensure
|
54
|
+
STDERR.reopen(orig_err)
|
55
|
+
STDOUT.reopen(orig_out)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# which(1) exit codes cannot be trusted on some systems
|
60
|
+
# We use UNIX shell utilities in some tests because we don't trust
|
61
|
+
# ourselves to write Ruby 100% correctly :)
|
62
|
+
def which(bin)
|
63
|
+
ex = ENV['PATH'].split(/:/).detect do |x|
|
64
|
+
x << "/#{bin}"
|
65
|
+
File.executable?(x)
|
66
|
+
end or warn "`#{bin}' not found in PATH=#{ENV['PATH']}"
|
67
|
+
ex
|
68
|
+
end
|
69
|
+
|
70
|
+
# Either takes a string to do a get request against, or a tuple of [URI, HTTP] where
|
71
|
+
# HTTP is some kind of Net::HTTP request object (POST, HEAD, etc.)
|
72
|
+
def hit(uris)
|
73
|
+
results = []
|
74
|
+
uris.each do |u|
|
75
|
+
res = nil
|
76
|
+
|
77
|
+
if u.kind_of? String
|
78
|
+
res = Net::HTTP.get(URI.parse(u))
|
79
|
+
else
|
80
|
+
url = URI.parse(u[0])
|
81
|
+
res = Net::HTTP.new(url.host, url.port).start {|h| h.request(u[1]) }
|
82
|
+
end
|
83
|
+
|
84
|
+
assert res != nil, "Didn't get a response: #{u}"
|
85
|
+
results << res
|
86
|
+
end
|
87
|
+
|
88
|
+
return results
|
89
|
+
end
|
90
|
+
|
91
|
+
# unused_port provides an unused port on +addr+ usable for TCP that is
|
92
|
+
# guaranteed to be unused across all unicorn builds on that system. It
|
93
|
+
# prevents race conditions by using a lock file other unicorn builds
|
94
|
+
# will see. This is required if you perform several builds in parallel
|
95
|
+
# with a continuous integration system or run tests in parallel via
|
96
|
+
# gmake. This is NOT guaranteed to be race-free if you run other
|
97
|
+
# processes that bind to random ports for testing (but the window
|
98
|
+
# for a race condition is very small). You may also set UNICORN_TEST_ADDR
|
99
|
+
# to override the default test address (127.0.0.1).
|
100
|
+
def unused_port(addr = '127.0.0.1')
|
101
|
+
retries = 100
|
102
|
+
base = 5000
|
103
|
+
port = sock = nil
|
104
|
+
begin
|
105
|
+
begin
|
106
|
+
port = base + rand(32768 - base)
|
107
|
+
while port == Unicorn::Const::DEFAULT_PORT
|
108
|
+
port = base + rand(32768 - base)
|
109
|
+
end
|
110
|
+
|
111
|
+
sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
|
112
|
+
sock.bind(Socket.pack_sockaddr_in(port, addr))
|
113
|
+
sock.listen(5)
|
114
|
+
rescue Errno::EADDRINUSE, Errno::EACCES
|
115
|
+
sock.close rescue nil
|
116
|
+
retry if (retries -= 1) >= 0
|
117
|
+
end
|
118
|
+
|
119
|
+
# since we'll end up closing the random port we just got, there's a race
|
120
|
+
# condition could allow the random port we just chose to reselect itself
|
121
|
+
# when running tests in parallel with gmake. Create a lock file while
|
122
|
+
# we have the port here to ensure that does not happen .
|
123
|
+
lock_path = "#{Dir::tmpdir}/unicorn_test.#{addr}:#{port}.lock"
|
124
|
+
lock = File.open(lock_path, File::WRONLY|File::CREAT|File::EXCL, 0600)
|
125
|
+
at_exit { File.unlink(lock_path) rescue nil }
|
126
|
+
rescue Errno::EEXIST
|
127
|
+
sock.close rescue nil
|
128
|
+
retry
|
129
|
+
end
|
130
|
+
sock.close rescue nil
|
131
|
+
port
|
132
|
+
end
|
133
|
+
|
134
|
+
def try_require(lib)
|
135
|
+
begin
|
136
|
+
require lib
|
137
|
+
true
|
138
|
+
rescue LoadError
|
139
|
+
false
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
# sometimes the server may not come up right away
|
144
|
+
def retry_hit(uris = [])
|
145
|
+
tries = DEFAULT_TRIES
|
146
|
+
begin
|
147
|
+
hit(uris)
|
148
|
+
rescue Errno::EINVAL, Errno::ECONNREFUSED => err
|
149
|
+
if (tries -= 1) > 0
|
150
|
+
sleep DEFAULT_RES
|
151
|
+
retry
|
152
|
+
end
|
153
|
+
raise err
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def assert_shutdown(pid)
|
158
|
+
wait_master_ready("test_stderr.#{pid}.log")
|
159
|
+
assert_nothing_raised { Process.kill(:QUIT, pid) }
|
160
|
+
status = nil
|
161
|
+
assert_nothing_raised { pid, status = Process.waitpid2(pid) }
|
162
|
+
assert status.success?, "exited successfully"
|
163
|
+
end
|
164
|
+
|
165
|
+
def wait_workers_ready(path, nr_workers)
|
166
|
+
tries = DEFAULT_TRIES
|
167
|
+
lines = []
|
168
|
+
while (tries -= 1) > 0
|
169
|
+
begin
|
170
|
+
lines = File.readlines(path).grep(/worker=\d+ ready/)
|
171
|
+
lines.size == nr_workers and return
|
172
|
+
rescue Errno::ENOENT
|
173
|
+
end
|
174
|
+
sleep DEFAULT_RES
|
175
|
+
end
|
176
|
+
raise "#{nr_workers} workers never became ready:" \
|
177
|
+
"\n\t#{lines.join("\n\t")}\n"
|
178
|
+
end
|
179
|
+
|
180
|
+
def wait_master_ready(master_log)
|
181
|
+
tries = DEFAULT_TRIES
|
182
|
+
while (tries -= 1) > 0
|
183
|
+
begin
|
184
|
+
File.readlines(master_log).grep(/master process ready/)[0] and return
|
185
|
+
rescue Errno::ENOENT
|
186
|
+
end
|
187
|
+
sleep DEFAULT_RES
|
188
|
+
end
|
189
|
+
raise "master process never became ready"
|
190
|
+
end
|
191
|
+
|
192
|
+
def reexec_usr2_quit_test(pid, pid_file)
|
193
|
+
assert File.exist?(pid_file), "pid file OK"
|
194
|
+
assert ! File.exist?("#{pid_file}.oldbin"), "oldbin pid file"
|
195
|
+
assert_nothing_raised { Process.kill(:USR2, pid) }
|
196
|
+
assert_nothing_raised { retry_hit(["http://#{@addr}:#{@port}/"]) }
|
197
|
+
wait_for_file("#{pid_file}.oldbin")
|
198
|
+
wait_for_file(pid_file)
|
199
|
+
|
200
|
+
old_pid = File.read("#{pid_file}.oldbin").to_i
|
201
|
+
new_pid = File.read(pid_file).to_i
|
202
|
+
|
203
|
+
# kill old master process
|
204
|
+
assert_not_equal pid, new_pid
|
205
|
+
assert_equal pid, old_pid
|
206
|
+
assert_nothing_raised { Process.kill(:QUIT, old_pid) }
|
207
|
+
assert_nothing_raised { retry_hit(["http://#{@addr}:#{@port}/"]) }
|
208
|
+
wait_for_death(old_pid)
|
209
|
+
assert_equal new_pid, File.read(pid_file).to_i
|
210
|
+
assert_nothing_raised { retry_hit(["http://#{@addr}:#{@port}/"]) }
|
211
|
+
assert_nothing_raised { Process.kill(:QUIT, new_pid) }
|
212
|
+
end
|
213
|
+
|
214
|
+
def reexec_basic_test(pid, pid_file)
|
215
|
+
results = retry_hit(["http://#{@addr}:#{@port}/"])
|
216
|
+
assert_equal String, results[0].class
|
217
|
+
assert_nothing_raised { Process.kill(0, pid) }
|
218
|
+
master_log = "#{@tmpdir}/test_stderr.#{pid}.log"
|
219
|
+
wait_master_ready(master_log)
|
220
|
+
File.truncate(master_log, 0)
|
221
|
+
nr = 50
|
222
|
+
kill_point = 2
|
223
|
+
assert_nothing_raised do
|
224
|
+
nr.times do |i|
|
225
|
+
hit(["http://#{@addr}:#{@port}/#{i}"])
|
226
|
+
i == kill_point and Process.kill(:HUP, pid)
|
227
|
+
end
|
228
|
+
end
|
229
|
+
wait_master_ready(master_log)
|
230
|
+
assert File.exist?(pid_file), "pid=#{pid_file} exists"
|
231
|
+
new_pid = File.read(pid_file).to_i
|
232
|
+
assert_not_equal pid, new_pid
|
233
|
+
assert_nothing_raised { Process.kill(0, new_pid) }
|
234
|
+
assert_nothing_raised { Process.kill(:QUIT, new_pid) }
|
235
|
+
end
|
236
|
+
|
237
|
+
def wait_for_file(path)
|
238
|
+
tries = DEFAULT_TRIES
|
239
|
+
while (tries -= 1) > 0 && ! File.exist?(path)
|
240
|
+
sleep DEFAULT_RES
|
241
|
+
end
|
242
|
+
assert File.exist?(path), "path=#{path} exists #{caller.inspect}"
|
243
|
+
end
|
244
|
+
|
245
|
+
def xfork(&block)
|
246
|
+
fork do
|
247
|
+
ObjectSpace.each_object(Tempfile) do |tmp|
|
248
|
+
ObjectSpace.undefine_finalizer(tmp)
|
249
|
+
end
|
250
|
+
yield
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
# can't waitpid on detached processes
|
255
|
+
def wait_for_death(pid)
|
256
|
+
tries = DEFAULT_TRIES
|
257
|
+
while (tries -= 1) > 0
|
258
|
+
begin
|
259
|
+
Process.kill(0, pid)
|
260
|
+
begin
|
261
|
+
Process.waitpid(pid, Process::WNOHANG)
|
262
|
+
rescue Errno::ECHILD
|
263
|
+
end
|
264
|
+
sleep(DEFAULT_RES)
|
265
|
+
rescue Errno::ESRCH
|
266
|
+
return
|
267
|
+
end
|
268
|
+
end
|
269
|
+
raise "PID:#{pid} never died!"
|
270
|
+
end
|
271
|
+
|
272
|
+
# executes +cmd+ and chunks its STDOUT
|
273
|
+
def chunked_spawn(stdout, *cmd)
|
274
|
+
fork {
|
275
|
+
crd, cwr = IO.pipe
|
276
|
+
crd.binmode
|
277
|
+
cwr.binmode
|
278
|
+
crd.sync = cwr.sync = true
|
279
|
+
|
280
|
+
pid = fork {
|
281
|
+
STDOUT.reopen(cwr)
|
282
|
+
crd.close
|
283
|
+
cwr.close
|
284
|
+
exec(*cmd)
|
285
|
+
}
|
286
|
+
cwr.close
|
287
|
+
begin
|
288
|
+
buf = crd.readpartial(16384)
|
289
|
+
stdout.write("#{'%x' % buf.size}\r\n#{buf}")
|
290
|
+
rescue EOFError
|
291
|
+
stdout.write("0\r\n")
|
292
|
+
pid, status = Process.waitpid(pid)
|
293
|
+
exit status.exitstatus
|
294
|
+
end while true
|
295
|
+
}
|
296
|
+
end
|
297
|
+
|
298
|
+
def reset_sig_handlers
|
299
|
+
sigs = %w(CHLD).concat(Unicorn::HttpServer::QUEUE_SIGS)
|
300
|
+
sigs.each { |sig| trap(sig, "DEFAULT") }
|
301
|
+
end
|