unicorn 0.91.0 → 0.92.0
Sign up to get free protection for your applications and to get access to all the features.
- data/{CHANGELOG → .CHANGELOG.old} +0 -0
- data/.document +4 -0
- data/.gitignore +5 -0
- data/.mailmap +26 -0
- data/CONTRIBUTORS +1 -1
- data/Documentation/.gitignore +5 -0
- data/Documentation/GNUmakefile +30 -0
- data/Documentation/unicorn.1.txt +158 -0
- data/Documentation/unicorn_rails.1.txt +150 -0
- data/GIT-VERSION-GEN +40 -0
- data/GNUmakefile +102 -14
- data/README +3 -2
- data/Rakefile +104 -32
- data/SIGNALS +2 -4
- data/TODO +3 -9
- data/bin/unicorn +5 -2
- data/bin/unicorn_rails +5 -3
- data/ext/unicorn_http/c_util.h +2 -2
- data/ext/unicorn_http/common_field_optimization.h +2 -1
- data/ext/unicorn_http/ext_help.h +29 -4
- data/ext/unicorn_http/extconf.rb +5 -0
- data/ext/unicorn_http/unicorn_http.rl +131 -76
- data/lib/unicorn.rb +6 -2
- data/lib/unicorn/app/exec_cgi.rb +3 -1
- data/lib/unicorn/app/inetd.rb +2 -0
- data/lib/unicorn/app/old_rails.rb +2 -0
- data/lib/unicorn/app/old_rails/static.rb +3 -1
- data/lib/unicorn/cgi_wrapper.rb +3 -1
- data/lib/unicorn/configurator.rb +2 -0
- data/lib/unicorn/const.rb +8 -6
- data/lib/unicorn/http_request.rb +6 -5
- data/lib/unicorn/http_response.rb +4 -2
- data/lib/unicorn/launcher.rb +6 -0
- data/lib/unicorn/socket_helper.rb +5 -5
- data/lib/unicorn/tee_input.rb +2 -0
- data/lib/unicorn/util.rb +2 -0
- data/local.mk.sample +4 -2
- data/setup.rb +1 -0
- data/test/aggregate.rb +2 -0
- data/test/exec/test_exec.rb +157 -0
- data/test/rails/app-1.2.3/app/controllers/application.rb +2 -0
- data/test/rails/app-1.2.3/app/controllers/foo_controller.rb +2 -0
- data/test/rails/app-1.2.3/app/helpers/application_helper.rb +2 -0
- data/test/rails/app-1.2.3/config/boot.rb +2 -0
- data/test/rails/app-1.2.3/config/environment.rb +2 -0
- data/test/rails/app-1.2.3/config/environments/development.rb +2 -0
- data/test/rails/app-1.2.3/config/environments/production.rb +2 -0
- data/test/rails/app-1.2.3/config/routes.rb +2 -0
- data/test/rails/app-2.0.2/app/controllers/application.rb +2 -0
- data/test/rails/app-2.0.2/app/controllers/foo_controller.rb +2 -0
- data/test/rails/app-2.0.2/app/helpers/application_helper.rb +2 -0
- data/test/rails/app-2.0.2/config/boot.rb +2 -0
- data/test/rails/app-2.0.2/config/environment.rb +2 -0
- data/test/rails/app-2.0.2/config/environments/development.rb +2 -0
- data/test/rails/app-2.0.2/config/environments/production.rb +2 -0
- data/test/rails/app-2.0.2/config/routes.rb +2 -0
- data/test/rails/app-2.1.2/app/controllers/application.rb +2 -0
- data/test/rails/app-2.1.2/app/controllers/foo_controller.rb +2 -0
- data/test/rails/app-2.1.2/app/helpers/application_helper.rb +2 -0
- data/test/rails/app-2.1.2/config/boot.rb +2 -0
- data/test/rails/app-2.1.2/config/environment.rb +2 -0
- data/test/rails/app-2.1.2/config/environments/development.rb +2 -0
- data/test/rails/app-2.1.2/config/environments/production.rb +2 -0
- data/test/rails/app-2.1.2/config/routes.rb +2 -0
- data/test/rails/app-2.2.2/app/controllers/application.rb +2 -0
- data/test/rails/app-2.2.2/app/controllers/foo_controller.rb +2 -0
- data/test/rails/app-2.2.2/app/helpers/application_helper.rb +2 -0
- data/test/rails/app-2.2.2/config/boot.rb +2 -0
- data/test/rails/app-2.2.2/config/environment.rb +2 -0
- data/test/rails/app-2.2.2/config/environments/development.rb +2 -0
- data/test/rails/app-2.2.2/config/environments/production.rb +2 -0
- data/test/rails/app-2.2.2/config/routes.rb +2 -0
- data/test/rails/app-2.3.3.1/app/controllers/application_controller.rb +2 -0
- data/test/rails/app-2.3.3.1/app/controllers/foo_controller.rb +2 -0
- data/test/rails/app-2.3.3.1/app/helpers/application_helper.rb +2 -0
- data/test/rails/app-2.3.3.1/config/boot.rb +2 -0
- data/test/rails/app-2.3.3.1/config/environment.rb +2 -0
- data/test/rails/app-2.3.3.1/config/environments/development.rb +2 -0
- data/test/rails/app-2.3.3.1/config/environments/production.rb +2 -0
- data/test/rails/app-2.3.3.1/config/routes.rb +2 -0
- data/test/rails/test_rails.rb +2 -0
- data/test/test_helper.rb +8 -0
- data/test/unit/test_configurator.rb +2 -0
- data/test/unit/test_http_parser.rb +13 -0
- data/test/unit/test_http_parser_ng.rb +2 -0
- data/test/unit/test_request.rb +2 -0
- data/test/unit/test_response.rb +2 -0
- data/test/unit/test_server.rb +2 -0
- data/test/unit/test_signals.rb +2 -0
- data/test/unit/test_socket_helper.rb +2 -0
- data/test/unit/test_tee_input.rb +2 -1
- data/test/unit/test_upload.rb +2 -0
- data/test/unit/test_util.rb +2 -0
- data/unicorn.gemspec +38 -28
- metadata +38 -42
- data/Manifest +0 -137
data/lib/unicorn/app/exec_cgi.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
|
1
3
|
require 'unicorn'
|
2
4
|
|
3
5
|
module Unicorn::App
|
@@ -24,7 +26,7 @@ module Unicorn::App
|
|
24
26
|
SERVER_PORT
|
25
27
|
SERVER_PROTOCOL
|
26
28
|
SERVER_SOFTWARE
|
27
|
-
).map { |x| x.freeze }
|
29
|
+
).map { |x| x.freeze } # frozen strings are faster for Hash assignments
|
28
30
|
|
29
31
|
# Intializes the app, example of usage in a config.ru
|
30
32
|
# map "/cgit" do
|
data/lib/unicorn/app/inetd.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
|
1
3
|
# This code is based on the original Rails handler in Mongrel
|
2
4
|
# Copyright (c) 2005 Zed A. Shaw
|
3
5
|
# Copyright (c) 2009 Eric Wong
|
@@ -18,7 +20,7 @@
|
|
18
20
|
# with Unicorn and you should see a decent speed boost (but not as
|
19
21
|
# fast as if you use a static server like nginx).
|
20
22
|
class Unicorn::App::OldRails::Static < Struct.new(:app, :root, :file_server)
|
21
|
-
FILE_METHODS = { 'GET' => true, 'HEAD' => true }
|
23
|
+
FILE_METHODS = { 'GET' => true, 'HEAD' => true }
|
22
24
|
REQUEST_METHOD = 'REQUEST_METHOD'.freeze
|
23
25
|
REQUEST_URI = 'REQUEST_URI'.freeze
|
24
26
|
PATH_INFO = 'PATH_INFO'.freeze
|
data/lib/unicorn/cgi_wrapper.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
|
1
3
|
# This code is based on the original CGIWrapper from Mongrel
|
2
4
|
# Copyright (c) 2005 Zed A. Shaw
|
3
5
|
# Copyright (c) 2009 Eric Wong
|
@@ -44,7 +46,7 @@ class Unicorn::CGIWrapper < ::CGI
|
|
44
46
|
'language' => 'Content-Language'.freeze,
|
45
47
|
'expires' => 'Expires'.freeze,
|
46
48
|
'length' => CONTENT_LENGTH,
|
47
|
-
}
|
49
|
+
}
|
48
50
|
|
49
51
|
# Takes an a Rackable environment, plus any additional CGI.new
|
50
52
|
# arguments These are used internally to create a wrapper around the
|
data/lib/unicorn/configurator.rb
CHANGED
data/lib/unicorn/const.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
|
1
3
|
module Unicorn
|
2
4
|
|
3
5
|
# Frequently used constants when constructing requests or responses. Many times
|
@@ -5,11 +7,11 @@ module Unicorn
|
|
5
7
|
# gave about a 3% to 10% performance improvement over using the strings directly.
|
6
8
|
# Symbols did not really improve things much compared to constants.
|
7
9
|
module Const
|
8
|
-
UNICORN_VERSION="0.
|
10
|
+
UNICORN_VERSION="0.92.0"
|
9
11
|
|
10
|
-
DEFAULT_HOST = "0.0.0.0"
|
11
|
-
DEFAULT_PORT = "8080"
|
12
|
-
DEFAULT_LISTEN = "#{DEFAULT_HOST}:#{DEFAULT_PORT}"
|
12
|
+
DEFAULT_HOST = "0.0.0.0" # default TCP listen host address
|
13
|
+
DEFAULT_PORT = "8080" # default TCP listen port
|
14
|
+
DEFAULT_LISTEN = "#{DEFAULT_HOST}:#{DEFAULT_PORT}"
|
13
15
|
|
14
16
|
# The basic max request size we'll try to read.
|
15
17
|
CHUNK_SIZE=(16 * 1024)
|
@@ -22,8 +24,8 @@ module Unicorn
|
|
22
24
|
MAX_BODY=MAX_HEADER
|
23
25
|
|
24
26
|
# common errors we'll send back
|
25
|
-
ERROR_400_RESPONSE = "HTTP/1.1 400 Bad Request\r\n\r\n"
|
26
|
-
ERROR_500_RESPONSE = "HTTP/1.1 500 Internal Server Error\r\n\r\n"
|
27
|
+
ERROR_400_RESPONSE = "HTTP/1.1 400 Bad Request\r\n\r\n"
|
28
|
+
ERROR_500_RESPONSE = "HTTP/1.1 500 Internal Server Error\r\n\r\n"
|
27
29
|
EXPECT_100_RESPONSE = "HTTP/1.1 100 Continue\r\n\r\n"
|
28
30
|
|
29
31
|
# A frozen format for this is about 15% faster
|
data/lib/unicorn/http_request.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
#
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
|
2
3
|
require 'stringio'
|
3
4
|
require 'unicorn_http'
|
4
5
|
|
@@ -11,15 +12,15 @@ module Unicorn
|
|
11
12
|
"rack.multiprocess" => true,
|
12
13
|
"rack.multithread" => false,
|
13
14
|
"rack.run_once" => false,
|
14
|
-
"rack.version" => [1, 0]
|
15
|
-
"SCRIPT_NAME" => ""
|
15
|
+
"rack.version" => [1, 0],
|
16
|
+
"SCRIPT_NAME" => "",
|
16
17
|
|
17
18
|
# this is not in the Rack spec, but some apps may rely on it
|
18
|
-
"SERVER_SOFTWARE" => "Unicorn #{Const::UNICORN_VERSION}"
|
19
|
+
"SERVER_SOFTWARE" => "Unicorn #{Const::UNICORN_VERSION}"
|
19
20
|
}
|
20
21
|
|
21
22
|
NULL_IO = StringIO.new(Z)
|
22
|
-
LOCALHOST = '127.0.0.1'
|
23
|
+
LOCALHOST = '127.0.0.1'
|
23
24
|
|
24
25
|
# Being explicitly single-threaded, we have certain advantages in
|
25
26
|
# not having to worry about variables being clobbered :)
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
|
1
3
|
require 'time'
|
2
4
|
|
3
5
|
module Unicorn
|
@@ -30,7 +32,7 @@ module Unicorn
|
|
30
32
|
# Rack does not set/require a Date: header. We always override the
|
31
33
|
# Connection: and Date: headers no matter what (if anything) our
|
32
34
|
# Rack application sent us.
|
33
|
-
SKIP = { 'connection' => true, 'date' => true, 'status' => true }
|
35
|
+
SKIP = { 'connection' => true, 'date' => true, 'status' => true }
|
34
36
|
OUT = [] # :nodoc
|
35
37
|
|
36
38
|
def self.write_header(socket, status, headers)
|
@@ -67,7 +69,7 @@ module Unicorn
|
|
67
69
|
body.each { |chunk| socket.write(chunk) }
|
68
70
|
socket.close # flushes and uncorks the socket immediately
|
69
71
|
ensure
|
70
|
-
body.respond_to?(:close) and body.close
|
72
|
+
body.respond_to?(:close) and body.close
|
71
73
|
end
|
72
74
|
|
73
75
|
end
|
data/lib/unicorn/launcher.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
|
1
3
|
require 'socket'
|
2
4
|
|
3
5
|
module Unicorn
|
@@ -18,10 +20,8 @@ module Unicorn
|
|
18
20
|
# Use the HTTP accept filter if available.
|
19
21
|
# The struct made by pack() is defined in /usr/include/sys/socket.h
|
20
22
|
# as accept_filter_arg
|
21
|
-
# We won't be seupportin the "dataready" filter unlike nginx
|
22
|
-
# since we only support HTTP and no other protocols
|
23
23
|
unless `/sbin/sysctl -nq net.inet.accf.http`.empty?
|
24
|
-
|
24
|
+
FILTER_ARG = ['httpready', nil].pack('a16a240')
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
@@ -44,8 +44,8 @@ module Unicorn
|
|
44
44
|
# No good reason to ever have deferred accepts off
|
45
45
|
if defined?(TCP_DEFER_ACCEPT)
|
46
46
|
sock.setsockopt(SOL_TCP, TCP_DEFER_ACCEPT, 1) rescue nil
|
47
|
-
elsif defined?(SO_ACCEPTFILTER) && defined?(
|
48
|
-
sock.setsockopt(SOL_SOCKET, SO_ACCEPTFILTER,
|
47
|
+
elsif defined?(SO_ACCEPTFILTER) && defined?(FILTER_ARG)
|
48
|
+
sock.setsockopt(SOL_SOCKET, SO_ACCEPTFILTER, FILTER_ARG) rescue nil
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
data/lib/unicorn/tee_input.rb
CHANGED
data/lib/unicorn/util.rb
CHANGED
data/local.mk.sample
CHANGED
@@ -31,6 +31,7 @@ test-19:
|
|
31
31
|
publish_doc:
|
32
32
|
-git set-file-times
|
33
33
|
$(MAKE) doc
|
34
|
+
awk 'BEGIN{RS="=== ";ORS=""}NR==2{ print RS""$$0 }' NEWS > doc/LATEST
|
34
35
|
$(MAKE) doc_gz
|
35
36
|
rsync -av --delete doc/ dcvr:/srv/unicorn/
|
36
37
|
git ls-files | xargs touch
|
@@ -38,7 +39,8 @@ publish_doc:
|
|
38
39
|
# Create gzip variants of the same timestamp as the original so nginx
|
39
40
|
# "gzip_static on" can serve the gzipped versions directly.
|
40
41
|
doc_gz: suf := html js css
|
41
|
-
doc_gz: docs = $(shell find doc
|
42
|
+
doc_gz: docs = $(shell find doc -type f ! -regex '^.*\.\(gif\|jpg\|png\|gz\)$$')
|
42
43
|
doc_gz:
|
44
|
+
touch doc/NEWS.atom.xml -d "$$(awk 'NR==1{print $$4,$$5,$$6}' NEWS)"
|
43
45
|
for i in $(docs); do \
|
44
|
-
gzip --rsyncable < $$i > $$i.gz; touch -r $$i $$i.gz; done
|
46
|
+
gzip --rsyncable -9 < $$i > $$i.gz; touch -r $$i $$i.gz; done
|
data/setup.rb
CHANGED
data/test/aggregate.rb
CHANGED
data/test/exec/test_exec.rb
CHANGED
@@ -1,4 +1,7 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
|
1
3
|
# Copyright (c) 2009 Eric Wong
|
4
|
+
FLOCK_PATH = File.expand_path(__FILE__)
|
2
5
|
require 'test/test_helper'
|
3
6
|
|
4
7
|
do_test = true
|
@@ -695,4 +698,158 @@ end
|
|
695
698
|
wait_for_death(pid)
|
696
699
|
end
|
697
700
|
|
701
|
+
def hup_test_common(preload)
|
702
|
+
File.open("config.ru", "wb") { |fp| fp.syswrite(HI.gsub("HI", '#$$')) }
|
703
|
+
pid_file = Tempfile.new('pid')
|
704
|
+
ucfg = Tempfile.new('unicorn_test_config')
|
705
|
+
ucfg.syswrite("listen '#@addr:#@port'\n")
|
706
|
+
ucfg.syswrite("pid '#{pid_file.path}'\n")
|
707
|
+
ucfg.syswrite("preload_app true\n") if preload
|
708
|
+
ucfg.syswrite("stderr_path 'test_stderr.#$$.log'\n")
|
709
|
+
ucfg.syswrite("stdout_path 'test_stdout.#$$.log'\n")
|
710
|
+
pid = xfork {
|
711
|
+
redirect_test_io { exec($unicorn_bin, "-D", "-c", ucfg.path) }
|
712
|
+
}
|
713
|
+
_, status = Process.waitpid2(pid)
|
714
|
+
assert status.success?
|
715
|
+
wait_master_ready("test_stderr.#$$.log")
|
716
|
+
wait_workers_ready("test_stderr.#$$.log", 1)
|
717
|
+
uri = URI.parse("http://#@addr:#@port/")
|
718
|
+
pids = Tempfile.new('worker_pids')
|
719
|
+
hitter = fork {
|
720
|
+
bodies = Hash.new(0)
|
721
|
+
at_exit { pids.syswrite(bodies.inspect) }
|
722
|
+
trap(:TERM) { exit(0) }
|
723
|
+
loop {
|
724
|
+
rv = Net::HTTP.get(uri)
|
725
|
+
pid = rv.to_i
|
726
|
+
exit!(1) if pid <= 0
|
727
|
+
bodies[pid] += 1
|
728
|
+
}
|
729
|
+
}
|
730
|
+
sleep 1 # racy
|
731
|
+
daemon_pid = pid_file.read.to_i
|
732
|
+
assert daemon_pid > 0
|
733
|
+
Process.kill(:HUP, daemon_pid)
|
734
|
+
sleep 1 # racy
|
735
|
+
assert_nothing_raised { Process.kill(:TERM, hitter) }
|
736
|
+
_, hitter_status = Process.waitpid2(hitter)
|
737
|
+
assert hitter_status.success?
|
738
|
+
pids.sysseek(0)
|
739
|
+
pids = eval(pids.read)
|
740
|
+
assert_kind_of(Hash, pids)
|
741
|
+
assert_equal 2, pids.size
|
742
|
+
pids.keys.each { |x|
|
743
|
+
assert_kind_of(Integer, x)
|
744
|
+
assert x > 0
|
745
|
+
assert pids[x] > 0
|
746
|
+
}
|
747
|
+
assert_nothing_raised { Process.kill(:QUIT, daemon_pid) }
|
748
|
+
wait_for_death(daemon_pid)
|
749
|
+
end
|
750
|
+
|
751
|
+
def test_preload_app_hup
|
752
|
+
hup_test_common(true)
|
753
|
+
end
|
754
|
+
|
755
|
+
def test_hup
|
756
|
+
hup_test_common(false)
|
757
|
+
end
|
758
|
+
|
759
|
+
def test_default_listen_hup_holds_listener
|
760
|
+
default_listen_lock do
|
761
|
+
res, pid_path = default_listen_setup
|
762
|
+
daemon_pid = File.read(pid_path).to_i
|
763
|
+
assert_nothing_raised { Process.kill(:HUP, daemon_pid) }
|
764
|
+
wait_workers_ready("test_stderr.#$$.log", 1)
|
765
|
+
res2 = hit(["http://#{Unicorn::Const::DEFAULT_LISTEN}/"])
|
766
|
+
assert_match %r{\d+}, res2.first
|
767
|
+
assert res2.first != res.first
|
768
|
+
assert_nothing_raised { Process.kill(:QUIT, daemon_pid) }
|
769
|
+
wait_for_death(daemon_pid)
|
770
|
+
end
|
771
|
+
end
|
772
|
+
|
773
|
+
def test_default_listen_upgrade_holds_listener
|
774
|
+
default_listen_lock do
|
775
|
+
res, pid_path = default_listen_setup
|
776
|
+
daemon_pid = File.read(pid_path).to_i
|
777
|
+
assert_nothing_raised {
|
778
|
+
Process.kill(:USR2, daemon_pid)
|
779
|
+
wait_for_file("#{pid_path}.oldbin")
|
780
|
+
wait_for_file(pid_path)
|
781
|
+
Process.kill(:QUIT, daemon_pid)
|
782
|
+
wait_for_death(daemon_pid)
|
783
|
+
}
|
784
|
+
daemon_pid = File.read(pid_path).to_i
|
785
|
+
wait_workers_ready("test_stderr.#$$.log", 1)
|
786
|
+
File.truncate("test_stderr.#$$.log", 0)
|
787
|
+
|
788
|
+
res2 = hit(["http://#{Unicorn::Const::DEFAULT_LISTEN}/"])
|
789
|
+
assert_match %r{\d+}, res2.first
|
790
|
+
assert res2.first != res.first
|
791
|
+
|
792
|
+
assert_nothing_raised { Process.kill(:HUP, daemon_pid) }
|
793
|
+
wait_workers_ready("test_stderr.#$$.log", 1)
|
794
|
+
File.truncate("test_stderr.#$$.log", 0)
|
795
|
+
res3 = hit(["http://#{Unicorn::Const::DEFAULT_LISTEN}/"])
|
796
|
+
assert res2.first != res3.first
|
797
|
+
|
798
|
+
assert_nothing_raised { Process.kill(:QUIT, daemon_pid) }
|
799
|
+
wait_for_death(daemon_pid)
|
800
|
+
end
|
801
|
+
end
|
802
|
+
|
803
|
+
def default_listen_setup
|
804
|
+
File.open("config.ru", "wb") { |fp| fp.syswrite(HI.gsub("HI", '#$$')) }
|
805
|
+
pid_path = (tmp = Tempfile.new('pid')).path
|
806
|
+
tmp.close!
|
807
|
+
ucfg = Tempfile.new('unicorn_test_config')
|
808
|
+
ucfg.syswrite("pid '#{pid_path}'\n")
|
809
|
+
ucfg.syswrite("stderr_path 'test_stderr.#$$.log'\n")
|
810
|
+
ucfg.syswrite("stdout_path 'test_stdout.#$$.log'\n")
|
811
|
+
pid = xfork {
|
812
|
+
redirect_test_io { exec($unicorn_bin, "-D", "-c", ucfg.path) }
|
813
|
+
}
|
814
|
+
_, status = Process.waitpid2(pid)
|
815
|
+
assert status.success?
|
816
|
+
wait_master_ready("test_stderr.#$$.log")
|
817
|
+
wait_workers_ready("test_stderr.#$$.log", 1)
|
818
|
+
File.truncate("test_stderr.#$$.log", 0)
|
819
|
+
res = hit(["http://#{Unicorn::Const::DEFAULT_LISTEN}/"])
|
820
|
+
assert_match %r{\d+}, res.first
|
821
|
+
[ res, pid_path ]
|
822
|
+
end
|
823
|
+
|
824
|
+
# we need to flock() something to prevent these tests from running
|
825
|
+
def default_listen_lock(&block)
|
826
|
+
fp = File.open(FLOCK_PATH, "rb")
|
827
|
+
begin
|
828
|
+
fp.flock(File::LOCK_EX)
|
829
|
+
begin
|
830
|
+
TCPServer.new(Unicorn::Const::DEFAULT_HOST,
|
831
|
+
Unicorn::Const::DEFAULT_PORT).close
|
832
|
+
rescue Errno::EADDRINUSE, Errno::EACCES
|
833
|
+
warn "can't bind to #{Unicorn::Const::DEFAULT_LISTEN}"
|
834
|
+
return false
|
835
|
+
end
|
836
|
+
|
837
|
+
# unused_port should never take this, but we may run an environment
|
838
|
+
# where tests are being run against older unicorns...
|
839
|
+
lock_path = "#{Dir::tmpdir}/unicorn_test." \
|
840
|
+
"#{Unicorn::Const::DEFAULT_LISTEN}.lock"
|
841
|
+
begin
|
842
|
+
lock = File.open(lock_path, File::WRONLY|File::CREAT|File::EXCL, 0600)
|
843
|
+
yield
|
844
|
+
rescue Errno::EEXIST
|
845
|
+
lock_path = nil
|
846
|
+
return false
|
847
|
+
ensure
|
848
|
+
File.unlink(lock_path) if lock_path
|
849
|
+
end
|
850
|
+
ensure
|
851
|
+
fp.flock(File::LOCK_UN)
|
852
|
+
end
|
853
|
+
end
|
854
|
+
|
698
855
|
end if do_test
|