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.
Files changed (163) hide show
  1. data/.CHANGELOG.old +25 -0
  2. data/.document +19 -0
  3. data/.gitignore +21 -0
  4. data/.mailmap +26 -0
  5. data/CONTRIBUTORS +32 -0
  6. data/COPYING +339 -0
  7. data/DESIGN +105 -0
  8. data/Documentation/.gitignore +5 -0
  9. data/Documentation/GNUmakefile +30 -0
  10. data/Documentation/unicorn.1.txt +171 -0
  11. data/Documentation/unicorn_rails.1.txt +172 -0
  12. data/FAQ +52 -0
  13. data/GIT-VERSION-GEN +40 -0
  14. data/GNUmakefile +292 -0
  15. data/HACKING +116 -0
  16. data/ISSUES +36 -0
  17. data/KNOWN_ISSUES +50 -0
  18. data/LICENSE +55 -0
  19. data/PHILOSOPHY +145 -0
  20. data/README +149 -0
  21. data/Rakefile +191 -0
  22. data/SIGNALS +109 -0
  23. data/Sandbox +78 -0
  24. data/TODO +5 -0
  25. data/TUNING +70 -0
  26. data/bin/unicorn +126 -0
  27. data/bin/unicorn_rails +203 -0
  28. data/examples/big_app_gc.rb +33 -0
  29. data/examples/echo.ru +27 -0
  30. data/examples/git.ru +13 -0
  31. data/examples/init.sh +58 -0
  32. data/examples/logger_mp_safe.rb +25 -0
  33. data/examples/nginx.conf +139 -0
  34. data/examples/unicorn.conf.rb +78 -0
  35. data/ext/unicorn_http/CFLAGS +13 -0
  36. data/ext/unicorn_http/c_util.h +124 -0
  37. data/ext/unicorn_http/common_field_optimization.h +111 -0
  38. data/ext/unicorn_http/ext_help.h +77 -0
  39. data/ext/unicorn_http/extconf.rb +14 -0
  40. data/ext/unicorn_http/global_variables.h +89 -0
  41. data/ext/unicorn_http/unicorn_http.rl +714 -0
  42. data/ext/unicorn_http/unicorn_http_common.rl +75 -0
  43. data/lib/unicorn.rb +847 -0
  44. data/lib/unicorn/app/exec_cgi.rb +150 -0
  45. data/lib/unicorn/app/inetd.rb +109 -0
  46. data/lib/unicorn/app/old_rails.rb +33 -0
  47. data/lib/unicorn/app/old_rails/static.rb +58 -0
  48. data/lib/unicorn/cgi_wrapper.rb +145 -0
  49. data/lib/unicorn/configurator.rb +421 -0
  50. data/lib/unicorn/const.rb +34 -0
  51. data/lib/unicorn/http_request.rb +72 -0
  52. data/lib/unicorn/http_response.rb +75 -0
  53. data/lib/unicorn/launcher.rb +65 -0
  54. data/lib/unicorn/oob_gc.rb +58 -0
  55. data/lib/unicorn/socket_helper.rb +152 -0
  56. data/lib/unicorn/tee_input.rb +217 -0
  57. data/lib/unicorn/util.rb +90 -0
  58. data/local.mk.sample +62 -0
  59. data/setup.rb +1586 -0
  60. data/t/.gitignore +2 -0
  61. data/t/GNUmakefile +67 -0
  62. data/t/README +42 -0
  63. data/t/bin/content-md5-put +36 -0
  64. data/t/bin/sha1sum.rb +23 -0
  65. data/t/bin/unused_listen +40 -0
  66. data/t/bin/utee +12 -0
  67. data/t/env.ru +3 -0
  68. data/t/my-tap-lib.sh +200 -0
  69. data/t/t0000-http-basic.sh +50 -0
  70. data/t/t0001-reload-bad-config.sh +52 -0
  71. data/t/t0002-config-conflict.sh +49 -0
  72. data/t/test-lib.sh +100 -0
  73. data/test/aggregate.rb +15 -0
  74. data/test/benchmark/README +50 -0
  75. data/test/benchmark/dd.ru +18 -0
  76. data/test/exec/README +5 -0
  77. data/test/exec/test_exec.rb +1038 -0
  78. data/test/rails/app-1.2.3/.gitignore +2 -0
  79. data/test/rails/app-1.2.3/Rakefile +7 -0
  80. data/test/rails/app-1.2.3/app/controllers/application.rb +6 -0
  81. data/test/rails/app-1.2.3/app/controllers/foo_controller.rb +36 -0
  82. data/test/rails/app-1.2.3/app/helpers/application_helper.rb +4 -0
  83. data/test/rails/app-1.2.3/config/boot.rb +11 -0
  84. data/test/rails/app-1.2.3/config/database.yml +12 -0
  85. data/test/rails/app-1.2.3/config/environment.rb +13 -0
  86. data/test/rails/app-1.2.3/config/environments/development.rb +9 -0
  87. data/test/rails/app-1.2.3/config/environments/production.rb +5 -0
  88. data/test/rails/app-1.2.3/config/routes.rb +6 -0
  89. data/test/rails/app-1.2.3/db/.gitignore +0 -0
  90. data/test/rails/app-1.2.3/public/404.html +1 -0
  91. data/test/rails/app-1.2.3/public/500.html +1 -0
  92. data/test/rails/app-2.0.2/.gitignore +2 -0
  93. data/test/rails/app-2.0.2/Rakefile +7 -0
  94. data/test/rails/app-2.0.2/app/controllers/application.rb +4 -0
  95. data/test/rails/app-2.0.2/app/controllers/foo_controller.rb +36 -0
  96. data/test/rails/app-2.0.2/app/helpers/application_helper.rb +4 -0
  97. data/test/rails/app-2.0.2/config/boot.rb +11 -0
  98. data/test/rails/app-2.0.2/config/database.yml +12 -0
  99. data/test/rails/app-2.0.2/config/environment.rb +17 -0
  100. data/test/rails/app-2.0.2/config/environments/development.rb +8 -0
  101. data/test/rails/app-2.0.2/config/environments/production.rb +5 -0
  102. data/test/rails/app-2.0.2/config/routes.rb +6 -0
  103. data/test/rails/app-2.0.2/db/.gitignore +0 -0
  104. data/test/rails/app-2.0.2/public/404.html +1 -0
  105. data/test/rails/app-2.0.2/public/500.html +1 -0
  106. data/test/rails/app-2.1.2/.gitignore +2 -0
  107. data/test/rails/app-2.1.2/Rakefile +7 -0
  108. data/test/rails/app-2.1.2/app/controllers/application.rb +4 -0
  109. data/test/rails/app-2.1.2/app/controllers/foo_controller.rb +36 -0
  110. data/test/rails/app-2.1.2/app/helpers/application_helper.rb +4 -0
  111. data/test/rails/app-2.1.2/config/boot.rb +111 -0
  112. data/test/rails/app-2.1.2/config/database.yml +12 -0
  113. data/test/rails/app-2.1.2/config/environment.rb +17 -0
  114. data/test/rails/app-2.1.2/config/environments/development.rb +7 -0
  115. data/test/rails/app-2.1.2/config/environments/production.rb +5 -0
  116. data/test/rails/app-2.1.2/config/routes.rb +6 -0
  117. data/test/rails/app-2.1.2/db/.gitignore +0 -0
  118. data/test/rails/app-2.1.2/public/404.html +1 -0
  119. data/test/rails/app-2.1.2/public/500.html +1 -0
  120. data/test/rails/app-2.2.2/.gitignore +2 -0
  121. data/test/rails/app-2.2.2/Rakefile +7 -0
  122. data/test/rails/app-2.2.2/app/controllers/application.rb +4 -0
  123. data/test/rails/app-2.2.2/app/controllers/foo_controller.rb +36 -0
  124. data/test/rails/app-2.2.2/app/helpers/application_helper.rb +4 -0
  125. data/test/rails/app-2.2.2/config/boot.rb +111 -0
  126. data/test/rails/app-2.2.2/config/database.yml +12 -0
  127. data/test/rails/app-2.2.2/config/environment.rb +17 -0
  128. data/test/rails/app-2.2.2/config/environments/development.rb +7 -0
  129. data/test/rails/app-2.2.2/config/environments/production.rb +5 -0
  130. data/test/rails/app-2.2.2/config/routes.rb +6 -0
  131. data/test/rails/app-2.2.2/db/.gitignore +0 -0
  132. data/test/rails/app-2.2.2/public/404.html +1 -0
  133. data/test/rails/app-2.2.2/public/500.html +1 -0
  134. data/test/rails/app-2.3.5/.gitignore +2 -0
  135. data/test/rails/app-2.3.5/Rakefile +7 -0
  136. data/test/rails/app-2.3.5/app/controllers/application_controller.rb +5 -0
  137. data/test/rails/app-2.3.5/app/controllers/foo_controller.rb +36 -0
  138. data/test/rails/app-2.3.5/app/helpers/application_helper.rb +4 -0
  139. data/test/rails/app-2.3.5/config/boot.rb +109 -0
  140. data/test/rails/app-2.3.5/config/database.yml +12 -0
  141. data/test/rails/app-2.3.5/config/environment.rb +17 -0
  142. data/test/rails/app-2.3.5/config/environments/development.rb +7 -0
  143. data/test/rails/app-2.3.5/config/environments/production.rb +6 -0
  144. data/test/rails/app-2.3.5/config/routes.rb +6 -0
  145. data/test/rails/app-2.3.5/db/.gitignore +0 -0
  146. data/test/rails/app-2.3.5/public/404.html +1 -0
  147. data/test/rails/app-2.3.5/public/500.html +1 -0
  148. data/test/rails/app-2.3.5/public/x.txt +1 -0
  149. data/test/rails/test_rails.rb +280 -0
  150. data/test/test_helper.rb +301 -0
  151. data/test/unit/test_configurator.rb +150 -0
  152. data/test/unit/test_http_parser.rb +555 -0
  153. data/test/unit/test_http_parser_ng.rb +443 -0
  154. data/test/unit/test_request.rb +184 -0
  155. data/test/unit/test_response.rb +110 -0
  156. data/test/unit/test_server.rb +291 -0
  157. data/test/unit/test_signals.rb +206 -0
  158. data/test/unit/test_socket_helper.rb +147 -0
  159. data/test/unit/test_tee_input.rb +257 -0
  160. data/test/unit/test_upload.rb +298 -0
  161. data/test/unit/test_util.rb +96 -0
  162. data/unicorn.gemspec +52 -0
  163. metadata +283 -0
@@ -0,0 +1,12 @@
1
+ development:
2
+ adapter: sqlite3
3
+ database: db/development.sqlite3
4
+ timeout: 5000
5
+ test:
6
+ adapter: sqlite3
7
+ database: db/test.sqlite3
8
+ timeout: 5000
9
+ production:
10
+ adapter: sqlite3
11
+ database: db/production.sqlite3
12
+ timeout: 5000
@@ -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
@@ -0,0 +1,7 @@
1
+ # -*- encoding: binary -*-
2
+
3
+ config.cache_classes = false
4
+ config.whiny_nils = true
5
+ config.action_controller.consider_all_requests_local = true
6
+ config.action_view.debug_rjs = true
7
+ config.action_controller.perform_caching = false
@@ -0,0 +1,6 @@
1
+ # -*- encoding: binary -*-
2
+
3
+ config.cache_classes = true
4
+ config.action_controller.consider_all_requests_local = false
5
+ config.action_controller.perform_caching = true
6
+ config.action_view.cache_template_loading = true
@@ -0,0 +1,6 @@
1
+ # -*- encoding: binary -*-
2
+
3
+ ActionController::Routing::Routes.draw do |map|
4
+ map.connect ':controller/:action/:id'
5
+ map.connect ':controller/:action/:id.:format'
6
+ 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
@@ -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