unicorn 0.4.1 → 0.4.2
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +1 -0
- data/GNUmakefile +7 -3
- data/Manifest +25 -0
- data/PHILOSOPHY +139 -0
- data/bin/unicorn +17 -23
- data/bin/unicorn_rails +19 -30
- data/lib/unicorn.rb +32 -12
- data/lib/unicorn/const.rb +1 -1
- data/lib/unicorn/socket.rb +0 -5
- data/local.mk.sample +43 -0
- data/test/exec/test_exec.rb +95 -0
- data/test/rails/app-1.2.3/Rakefile +7 -0
- data/test/rails/app-1.2.3/config/environment.rb +1 -0
- data/test/rails/app-2.0.2/Rakefile +7 -0
- data/test/rails/app-2.0.2/config/environment.rb +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 +2 -0
- data/test/rails/app-2.1.2/app/controllers/foo_controller.rb +34 -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 +109 -0
- data/test/rails/app-2.1.2/config/database.yml +12 -0
- data/test/rails/app-2.1.2/config/environment.rb +15 -0
- data/test/rails/app-2.1.2/config/environments/development.rb +5 -0
- data/test/rails/app-2.1.2/config/environments/production.rb +3 -0
- data/test/rails/app-2.1.2/config/routes.rb +4 -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/Rakefile +7 -0
- data/test/rails/app-2.2.2/config/environment.rb +1 -0
- data/test/rails/app-2.3.2.1/Rakefile +7 -0
- data/test/rails/app-2.3.2.1/config/environment.rb +2 -1
- data/test/rails/test_rails.rb +4 -0
- data/test/unit/test_upload.rb +6 -0
- data/unicorn.gemspec +3 -3
- metadata +27 -2
data/CHANGELOG
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
v0.4.2 - fix Rails ARStore, FD leak prevention, descriptive proctitles
|
1
2
|
v0.4.1 - Rails support, per-listener backlog and {snd,rcv}buf
|
2
3
|
v0.2.3 - Unlink Tempfiles after use (they were closed, just not unlinked)
|
3
4
|
v0.2.2 - small bug fixes, fix Rack multi-value headers (Set-Cookie:)
|
data/GNUmakefile
CHANGED
@@ -84,11 +84,15 @@ $(T): export RUBYLIB := $(test_prefix)/lib:$(RUBYLIB)
|
|
84
84
|
$(T): $(test_prefix)/.stamp
|
85
85
|
$(run_test)
|
86
86
|
|
87
|
-
install: bin/unicorn
|
87
|
+
install: bin/unicorn bin/unicorn_rails
|
88
88
|
$(prep_setup_rb)
|
89
|
-
|
89
|
+
$(RM) -r .install-tmp
|
90
|
+
mkdir .install-tmp
|
91
|
+
cp -p $^ .install-tmp
|
90
92
|
$(ruby) setup.rb all
|
91
|
-
|
93
|
+
$(RM) $^
|
94
|
+
mv $(addprefix .install-tmp/,$(^F)) bin/
|
95
|
+
$(RM) -r .install-tmp
|
92
96
|
$(prep_setup_rb)
|
93
97
|
|
94
98
|
clean-http11:
|
data/Manifest
CHANGED
@@ -6,6 +6,7 @@ DESIGN
|
|
6
6
|
GNUmakefile
|
7
7
|
LICENSE
|
8
8
|
Manifest
|
9
|
+
PHILOSOPHY
|
9
10
|
README
|
10
11
|
Rakefile
|
11
12
|
SIGNALS
|
@@ -31,6 +32,7 @@ lib/unicorn/http_response.rb
|
|
31
32
|
lib/unicorn/launcher.rb
|
32
33
|
lib/unicorn/socket.rb
|
33
34
|
lib/unicorn/util.rb
|
35
|
+
local.mk.sample
|
34
36
|
setup.rb
|
35
37
|
test/aggregate.rb
|
36
38
|
test/benchmark/README
|
@@ -41,6 +43,7 @@ test/benchmark/response.rb
|
|
41
43
|
test/exec/README
|
42
44
|
test/exec/test_exec.rb
|
43
45
|
test/rails/app-1.2.3/.gitignore
|
46
|
+
test/rails/app-1.2.3/Rakefile
|
44
47
|
test/rails/app-1.2.3/app/controllers/application.rb
|
45
48
|
test/rails/app-1.2.3/app/controllers/foo_controller.rb
|
46
49
|
test/rails/app-1.2.3/app/helpers/application_helper.rb
|
@@ -51,9 +54,11 @@ test/rails/app-1.2.3/config/environments/development.rb
|
|
51
54
|
test/rails/app-1.2.3/config/environments/production.rb
|
52
55
|
test/rails/app-1.2.3/config/routes.rb
|
53
56
|
test/rails/app-1.2.3/db/.gitignore
|
57
|
+
test/rails/app-1.2.3/log/.gitignore
|
54
58
|
test/rails/app-1.2.3/public/404.html
|
55
59
|
test/rails/app-1.2.3/public/500.html
|
56
60
|
test/rails/app-2.0.2/.gitignore
|
61
|
+
test/rails/app-2.0.2/Rakefile
|
57
62
|
test/rails/app-2.0.2/app/controllers/application.rb
|
58
63
|
test/rails/app-2.0.2/app/controllers/foo_controller.rb
|
59
64
|
test/rails/app-2.0.2/app/helpers/application_helper.rb
|
@@ -64,9 +69,26 @@ test/rails/app-2.0.2/config/environments/development.rb
|
|
64
69
|
test/rails/app-2.0.2/config/environments/production.rb
|
65
70
|
test/rails/app-2.0.2/config/routes.rb
|
66
71
|
test/rails/app-2.0.2/db/.gitignore
|
72
|
+
test/rails/app-2.0.2/log/.gitignore
|
67
73
|
test/rails/app-2.0.2/public/404.html
|
68
74
|
test/rails/app-2.0.2/public/500.html
|
75
|
+
test/rails/app-2.1.2/.gitignore
|
76
|
+
test/rails/app-2.1.2/Rakefile
|
77
|
+
test/rails/app-2.1.2/app/controllers/application.rb
|
78
|
+
test/rails/app-2.1.2/app/controllers/foo_controller.rb
|
79
|
+
test/rails/app-2.1.2/app/helpers/application_helper.rb
|
80
|
+
test/rails/app-2.1.2/config/boot.rb
|
81
|
+
test/rails/app-2.1.2/config/database.yml
|
82
|
+
test/rails/app-2.1.2/config/environment.rb
|
83
|
+
test/rails/app-2.1.2/config/environments/development.rb
|
84
|
+
test/rails/app-2.1.2/config/environments/production.rb
|
85
|
+
test/rails/app-2.1.2/config/routes.rb
|
86
|
+
test/rails/app-2.1.2/db/.gitignore
|
87
|
+
test/rails/app-2.1.2/log/.gitignore
|
88
|
+
test/rails/app-2.1.2/public/404.html
|
89
|
+
test/rails/app-2.1.2/public/500.html
|
69
90
|
test/rails/app-2.2.2/.gitignore
|
91
|
+
test/rails/app-2.2.2/Rakefile
|
70
92
|
test/rails/app-2.2.2/app/controllers/application.rb
|
71
93
|
test/rails/app-2.2.2/app/controllers/foo_controller.rb
|
72
94
|
test/rails/app-2.2.2/app/helpers/application_helper.rb
|
@@ -77,9 +99,11 @@ test/rails/app-2.2.2/config/environments/development.rb
|
|
77
99
|
test/rails/app-2.2.2/config/environments/production.rb
|
78
100
|
test/rails/app-2.2.2/config/routes.rb
|
79
101
|
test/rails/app-2.2.2/db/.gitignore
|
102
|
+
test/rails/app-2.2.2/log/.gitignore
|
80
103
|
test/rails/app-2.2.2/public/404.html
|
81
104
|
test/rails/app-2.2.2/public/500.html
|
82
105
|
test/rails/app-2.3.2.1/.gitignore
|
106
|
+
test/rails/app-2.3.2.1/Rakefile
|
83
107
|
test/rails/app-2.3.2.1/app/controllers/application_controller.rb
|
84
108
|
test/rails/app-2.3.2.1/app/controllers/foo_controller.rb
|
85
109
|
test/rails/app-2.3.2.1/app/helpers/application_helper.rb
|
@@ -90,6 +114,7 @@ test/rails/app-2.3.2.1/config/environments/development.rb
|
|
90
114
|
test/rails/app-2.3.2.1/config/environments/production.rb
|
91
115
|
test/rails/app-2.3.2.1/config/routes.rb
|
92
116
|
test/rails/app-2.3.2.1/db/.gitignore
|
117
|
+
test/rails/app-2.3.2.1/log/.gitignore
|
93
118
|
test/rails/app-2.3.2.1/public/404.html
|
94
119
|
test/rails/app-2.3.2.1/public/500.html
|
95
120
|
test/rails/test_rails.rb
|
data/PHILOSOPHY
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
= The Philosophy Behind Unicorn
|
2
|
+
|
3
|
+
Being a server that only runs on Unix-like platforms, Unicorn is
|
4
|
+
strongly tied to the Unix philosophy of doing one thing and (hopefully)
|
5
|
+
doing it well. Despite using HTTP, Unicorn is strictly a _backend_
|
6
|
+
application server for running Rack-based Ruby applications.
|
7
|
+
|
8
|
+
== Avoid Complexity
|
9
|
+
|
10
|
+
Instead of attempting to be efficient at serving slow clients, Unicorn
|
11
|
+
relies on a buffering reverse proxy to efficiently deal with slow
|
12
|
+
clients.
|
13
|
+
|
14
|
+
Unicorn uses an old-fashioned preforking worker model with blocking I/O.
|
15
|
+
Our processing model is the antithesis of more modern (and theoretically
|
16
|
+
more efficient) server processing models using threads or non-blocking
|
17
|
+
I/O with events.
|
18
|
+
|
19
|
+
=== Threads and Events Are Hard
|
20
|
+
|
21
|
+
...to many developers. Reasons for this is beyond the scope of this
|
22
|
+
document. Unicorn avoids concurrency within each worker process so you
|
23
|
+
have fewer things to worry about when developing your application. Of
|
24
|
+
course Unicorn can use multiple worker processes to utilize multiple
|
25
|
+
CPUs or spindles. Applications can still use threads internally, however.
|
26
|
+
|
27
|
+
== Slow Clients Are Problematic
|
28
|
+
|
29
|
+
Most benchmarks we've seen don't tell you this, and Unicorn doesn't
|
30
|
+
care about slow clients... but <i>you</i> should.
|
31
|
+
|
32
|
+
A "slow client" can be any client outside of your datacenter. Network
|
33
|
+
traffic within a local network is always faster than traffic that
|
34
|
+
crosses outside of it. The laws of physics do not allow otherwise.
|
35
|
+
|
36
|
+
Persistent connections were introduced in HTTP/1.1 reduce latency from
|
37
|
+
connection establishment and TCP slow start. They also waste server
|
38
|
+
resources when clients are idle.
|
39
|
+
|
40
|
+
Persistent connections mean one of the Unicorn worker processes
|
41
|
+
(depending on your application, it can be very memory hungry) would
|
42
|
+
spend a significant amount of its time idle keeping the connection alive
|
43
|
+
<i>and not doing anything else</i>. Being single-threaded and using
|
44
|
+
blocking I/O, a worker cannot serve other clients while keeping a
|
45
|
+
connection alive. Thus Unicorn does not implement persistent
|
46
|
+
connections.
|
47
|
+
|
48
|
+
If your application responses are larger than the socket buffer or if
|
49
|
+
you're handling large requests (uploads), worker processes will also be
|
50
|
+
bottlenecked by the speed of the *client* connection. You should
|
51
|
+
not allow Unicorn to serve clients outside of your local network.
|
52
|
+
|
53
|
+
== Application Concurrency != Network Concurrency
|
54
|
+
|
55
|
+
Performance is asymmetric across the different subsystems of the machine
|
56
|
+
and parts of the network. CPUs and main memory can process gigabytes of
|
57
|
+
data in a second; clients on the Internet are usually only capable of a
|
58
|
+
tiny fraction of that. Unicorn deployments should avoid dealing with
|
59
|
+
slow clients directly and instead rely on a reverse proxy to shield it
|
60
|
+
from the effects of slow I/O.
|
61
|
+
|
62
|
+
== Improved Performance Through Reverse Proxying
|
63
|
+
|
64
|
+
By acting as a buffer to shield Unicorn from slow I/O, a reverse proxy
|
65
|
+
will inevitably incur overhead in the form of extra data copies.
|
66
|
+
However, as I/O within a local network is fast (and faster still
|
67
|
+
with local sockets), this overhead is neglible for the vast majority
|
68
|
+
of HTTP requests and responses.
|
69
|
+
|
70
|
+
The ideal reverse proxy complements the weaknesses of Unicorn.
|
71
|
+
A reverse proxy for Unicorn should meet the following requirements:
|
72
|
+
|
73
|
+
1. It should fully buffer all HTTP requests (and large responses).
|
74
|
+
Each request should be "corked" in the reverse proxy and sent
|
75
|
+
as fast as possible to the backend Unicorn processes. This is
|
76
|
+
the most important feature to look for when choosing a
|
77
|
+
reverse proxy for Unicorn.
|
78
|
+
|
79
|
+
2. It should spend minimal time in userspace. Network (and disk) I/O
|
80
|
+
are system-level tasks and usually managed by the kernel.
|
81
|
+
This may change if userspace TCP stacks become more popular in the
|
82
|
+
future; but the reverse proxy should not waste time with
|
83
|
+
application-level logic. These concerns should be separated
|
84
|
+
|
85
|
+
3. It should avoid context switches and CPU scheduling overhead.
|
86
|
+
In many (most?) cases, network devices and their interrupts are
|
87
|
+
only be handled by one CPU at a time. It should avoid contention
|
88
|
+
within the system by serializing all network I/O into one (or few)
|
89
|
+
userspace procceses. Network I/O is not a CPU-intensive task and
|
90
|
+
it is not helpful to use multiple CPU cores (at least not for GigE).
|
91
|
+
|
92
|
+
4. It should efficiently manage persistent connections (and
|
93
|
+
pipelining) to slow clients. If you care to serve slow clients
|
94
|
+
outside your network, then these features of HTTP/1.1 will help.
|
95
|
+
|
96
|
+
5. It should (optionally) serve static files. If you have static
|
97
|
+
files on your site (especially large ones), they are far more
|
98
|
+
efficiently served with as few data copies as possible (e.g. with
|
99
|
+
sendfile() to completely avoid copying the data to userspace).
|
100
|
+
|
101
|
+
nginx is the only (Free) solution we know of that meets the above
|
102
|
+
requirements.
|
103
|
+
|
104
|
+
Indeed, the author of Unicorn has deployed nginx as a reverse-proxy not
|
105
|
+
only for Ruby applications, but also for production applications running
|
106
|
+
Apache/mod_perl, Apache/mod_php and Apache Tomcat. In every single
|
107
|
+
case, performance improved because application servers were able to use
|
108
|
+
backend resources more efficiently and spend less time waiting on slow
|
109
|
+
I/O.
|
110
|
+
|
111
|
+
== Worse Is Better
|
112
|
+
|
113
|
+
Requirements and scope for applications change frequently and
|
114
|
+
drastically. Thus languages like Ruby and frameworks like Rails were
|
115
|
+
built to give developers fewer things to worry about in the face of
|
116
|
+
rapid change.
|
117
|
+
|
118
|
+
On the other hand, stable protocols which host your applications (HTTP
|
119
|
+
and TCP) only change rarely. This is why we recommend you NOT tie your
|
120
|
+
rapidly-changing application logic directly into the processes that deal
|
121
|
+
with the stable outside world. Instead, use HTTP as a common RPC
|
122
|
+
protocol to communicate between your frontend and backend.
|
123
|
+
|
124
|
+
In short: separate your concerns.
|
125
|
+
|
126
|
+
Of course a theoretical "perfect" solution would combine the pieces
|
127
|
+
and _maybe_ give you better performance at the end of the day, but
|
128
|
+
that is not the Unix way.
|
129
|
+
|
130
|
+
== Just Worse in Some Cases
|
131
|
+
|
132
|
+
Unicorn is not suited for all applications. Unicorn is optimized for
|
133
|
+
applications that are CPU/memory/disk intensive and spend little time
|
134
|
+
waiting on external resources (e.g. a database server or external API).
|
135
|
+
|
136
|
+
Unicorn is highly inefficient for Comet/reverse-HTTP/push applications
|
137
|
+
where the HTTP connection spends a large amount of time idle.
|
138
|
+
Nevertheless, the ease of troubleshooting, debugging, and management of
|
139
|
+
Unicorn may still outweigh the drawbacks for these applications.
|
data/bin/unicorn
CHANGED
@@ -117,40 +117,35 @@ end
|
|
117
117
|
|
118
118
|
require 'pp' if $DEBUG
|
119
119
|
|
120
|
-
|
121
|
-
# in
|
122
|
-
|
123
|
-
|
124
|
-
inner_app = case config
|
125
|
-
when /\.ru$/
|
126
|
-
|
127
|
-
|
128
|
-
else
|
129
|
-
lambda do ||
|
120
|
+
app = lambda do ||
|
121
|
+
# require Rack as late as possible in case $LOAD_PATH is modified
|
122
|
+
# in config.ru or command-line
|
123
|
+
require 'rack'
|
124
|
+
inner_app = case config
|
125
|
+
when /\.ru$/
|
126
|
+
raw = File.open(config, "rb") { |fp| fp.sysread(fp.stat.size) }
|
127
|
+
eval("Rack::Builder.new {(#{raw}\n)}.to_app", nil, config)
|
128
|
+
else
|
130
129
|
require config
|
131
130
|
Object.const_get(File.basename(config, '.rb').capitalize)
|
132
131
|
end
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
when "development"
|
137
|
-
lambda do ||
|
132
|
+
pp({ :inner_app => inner_app }) if $DEBUG
|
133
|
+
case env
|
134
|
+
when "development"
|
138
135
|
Rack::Builder.new do
|
139
136
|
use Rack::CommonLogger, $stderr
|
140
137
|
use Rack::ShowExceptions
|
141
138
|
use Rack::Lint
|
142
|
-
run inner_app
|
139
|
+
run inner_app
|
143
140
|
end.to_app
|
144
|
-
|
145
|
-
when "deployment"
|
146
|
-
lambda do ||
|
141
|
+
when "deployment"
|
147
142
|
Rack::Builder.new do
|
148
143
|
use Rack::CommonLogger, $stderr
|
149
|
-
run inner_app
|
144
|
+
run inner_app
|
150
145
|
end.to_app
|
146
|
+
else
|
147
|
+
inner_app
|
151
148
|
end
|
152
|
-
else
|
153
|
-
inner_app
|
154
149
|
end
|
155
150
|
|
156
151
|
listeners << "#{host}:#{port}" if set_listener
|
@@ -159,7 +154,6 @@ if $DEBUG
|
|
159
154
|
pp({
|
160
155
|
:unicorn_options => options,
|
161
156
|
:app => app,
|
162
|
-
:inner_app => inner_app,
|
163
157
|
:daemonize => daemonize,
|
164
158
|
})
|
165
159
|
end
|
data/bin/unicorn_rails
CHANGED
@@ -121,10 +121,6 @@ require 'pp' if $DEBUG
|
|
121
121
|
rails_loader = lambda do ||
|
122
122
|
begin
|
123
123
|
require 'config/boot'
|
124
|
-
defined?(::RAILS_ROOT) or abort "RAILS_ROOT not defined by config/boot"
|
125
|
-
defined?(::RAILS_ENV) or abort "RAILS_ENV not defined by config/boot"
|
126
|
-
defined?(::Rails::VERSION::STRING) or
|
127
|
-
abort "Rails::VERSION::STRING not defined by config/boot"
|
128
124
|
rescue LoadError => err
|
129
125
|
abort "#$0 must be run inside RAILS_ROOT: #{err.inspect}"
|
130
126
|
end
|
@@ -135,43 +131,36 @@ rails_loader = lambda do ||
|
|
135
131
|
|
136
132
|
case config
|
137
133
|
when nil
|
138
|
-
|
139
|
-
require 'config/environment'
|
140
|
-
|
141
|
-
# it seems Rails >=2.2 support Rack, but only >=2.3 requires it
|
142
|
-
old_rails = case ::Rails::VERSION::MAJOR
|
143
|
-
when 0, 1 then true
|
144
|
-
when 2 then Rails::VERSION::MINOR < 3 ? true : false
|
145
|
-
else
|
146
|
-
false
|
147
|
-
end
|
134
|
+
require 'config/environment'
|
148
135
|
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
136
|
+
# it seems Rails >=2.2 support Rack, but only >=2.3 requires it
|
137
|
+
old_rails = case ::Rails::VERSION::MAJOR
|
138
|
+
when 0, 1 then true
|
139
|
+
when 2 then Rails::VERSION::MINOR < 3 ? true : false
|
140
|
+
else
|
141
|
+
false
|
142
|
+
end
|
143
|
+
|
144
|
+
if old_rails
|
145
|
+
require 'rack'
|
146
|
+
require 'unicorn/app/old_rails'
|
147
|
+
Unicorn::App::OldRails.new
|
148
|
+
else
|
149
|
+
ActionController::Dispatcher.new
|
156
150
|
end
|
157
151
|
when /\.ru$/
|
158
152
|
raw = File.open(config, "rb") { |fp| fp.sysread(fp.stat.size) }
|
159
|
-
|
153
|
+
eval("Rack::Builder.new {(#{raw}\n)}.to_app", nil, config)
|
160
154
|
else
|
161
|
-
|
162
|
-
|
163
|
-
Object.const_get(File.basename(config, '.rb').capitalize)
|
164
|
-
end
|
155
|
+
require config
|
156
|
+
Object.const_get(File.basename(config, '.rb').capitalize)
|
165
157
|
end
|
166
158
|
end
|
167
159
|
|
168
160
|
# this won't run until after forking if preload_app is false
|
169
161
|
app = lambda do ||
|
170
|
-
inner_app = rails_loader.call
|
171
|
-
require 'active_support'
|
172
|
-
require 'action_controller'
|
173
162
|
map_path ||= '/'
|
174
|
-
inner_app =
|
163
|
+
inner_app = rails_loader.call
|
175
164
|
Rack::Builder.new do
|
176
165
|
if inner_app.class.to_s == "Unicorn::App::OldRails"
|
177
166
|
if map_path != '/'
|
data/lib/unicorn.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'logger'
|
2
|
+
require 'fcntl'
|
2
3
|
|
3
4
|
require 'unicorn/socket'
|
4
5
|
require 'unicorn/const'
|
@@ -93,8 +94,8 @@ module Unicorn
|
|
93
94
|
raise ArgumentError, "no listeners" if @listeners.empty?
|
94
95
|
self.pid = @config[:pid]
|
95
96
|
build_app! if @preload_app
|
96
|
-
|
97
|
-
|
97
|
+
File.open(@stderr_path, "a") { |fp| $stderr.reopen(fp) } if @stderr_path
|
98
|
+
File.open(@stdout_path, "a") { |fp| $stdout.reopen(fp) } if @stdout_path
|
98
99
|
$stderr.sync = $stdout.sync = true
|
99
100
|
spawn_missing_workers
|
100
101
|
self
|
@@ -166,7 +167,7 @@ module Unicorn
|
|
166
167
|
|
167
168
|
QUEUE_SIGS.each { |sig| trap_deferred(sig) }
|
168
169
|
trap(:CHLD) { |sig_nr| awaken_master }
|
169
|
-
|
170
|
+
proc_name 'master'
|
170
171
|
logger.info "master process ready" # test_exec.rb relies on this message
|
171
172
|
begin
|
172
173
|
loop do
|
@@ -285,7 +286,7 @@ module Unicorn
|
|
285
286
|
logger.error "reaped #{status.inspect} exec()-ed"
|
286
287
|
@reexec_pid = 0
|
287
288
|
self.pid = @pid.chomp('.oldbin') if @pid
|
288
|
-
|
289
|
+
proc_name 'master'
|
289
290
|
else
|
290
291
|
worker = @workers.delete(pid)
|
291
292
|
worker.tempfile.close rescue nil
|
@@ -325,20 +326,29 @@ module Unicorn
|
|
325
326
|
end
|
326
327
|
|
327
328
|
@reexec_pid = fork do
|
328
|
-
@rd_sig.close if @rd_sig
|
329
|
-
@wr_sig.close if @wr_sig
|
330
|
-
@workers.values.each { |other| other.tempfile.close rescue nil }
|
331
|
-
|
332
329
|
ENV.replace(@start_ctx[:environ])
|
333
|
-
|
330
|
+
listener_fds = @listeners.map { |sock| sock.fileno }
|
331
|
+
ENV['UNICORN_FD'] = listener_fds.join(',')
|
334
332
|
File.umask(@start_ctx[:umask])
|
335
333
|
Dir.chdir(@start_ctx[:cwd])
|
336
334
|
cmd = [ @start_ctx[:zero] ] + @start_ctx[:argv]
|
335
|
+
|
336
|
+
# avoid leaking FDs we don't know about, but let before_exec
|
337
|
+
# unset FD_CLOEXEC, if anything else in the app eventually
|
338
|
+
# relies on FD inheritence.
|
339
|
+
purgatory = [] # prevent GC of IO objects
|
340
|
+
(3..1024).each do |io|
|
341
|
+
next if listener_fds.include?(io)
|
342
|
+
io = IO.for_fd(io) rescue nil
|
343
|
+
io or next
|
344
|
+
purgatory << io
|
345
|
+
io.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
|
346
|
+
end
|
337
347
|
logger.info "executing #{cmd.inspect} (in #{Dir.pwd})"
|
338
348
|
@before_exec.call(self) if @before_exec
|
339
349
|
exec(*cmd)
|
340
350
|
end
|
341
|
-
|
351
|
+
proc_name 'master (old)'
|
342
352
|
end
|
343
353
|
|
344
354
|
# forcibly terminate all workers that haven't checked in in @timeout
|
@@ -418,16 +428,17 @@ module Unicorn
|
|
418
428
|
QUEUE_SIGS.each { |sig| trap(sig, 'IGNORE') }
|
419
429
|
trap(:CHLD, 'DEFAULT')
|
420
430
|
|
421
|
-
|
431
|
+
proc_name "worker[#{worker.nr}]"
|
422
432
|
@rd_sig.close if @rd_sig
|
423
433
|
@wr_sig.close if @wr_sig
|
424
434
|
@workers.values.each { |other| other.tempfile.close rescue nil }
|
425
435
|
@workers.clear
|
426
436
|
@start_ctx.clear
|
427
437
|
@start_ctx = @workers = @rd_sig = @wr_sig = nil
|
428
|
-
@listeners.each { |sock|
|
438
|
+
@listeners.each { |sock| sock.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) }
|
429
439
|
ENV.delete('UNICORN_FD')
|
430
440
|
@after_fork.call(self, worker.nr) if @after_fork
|
441
|
+
worker.tempfile.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
|
431
442
|
@request = HttpRequest.new(logger)
|
432
443
|
end
|
433
444
|
|
@@ -447,6 +458,8 @@ module Unicorn
|
|
447
458
|
@listeners.each { |sock| sock.close rescue nil } # break IO.select
|
448
459
|
end
|
449
460
|
reopen_logs, (rd, wr) = false, IO.pipe
|
461
|
+
rd.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
|
462
|
+
wr.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
|
450
463
|
trap(:USR1) { reopen_logs = true; rd.close rescue nil } # break IO.select
|
451
464
|
@logger.info "worker=#{worker.nr} ready"
|
452
465
|
|
@@ -458,6 +471,8 @@ module Unicorn
|
|
458
471
|
@logger.info "worker=#{worker.nr} done rotating logs"
|
459
472
|
wr.close rescue nil
|
460
473
|
rd, wr = IO.pipe
|
474
|
+
rd.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
|
475
|
+
wr.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
|
461
476
|
end
|
462
477
|
# we're a goner in @timeout seconds anyways if tempfile.chmod
|
463
478
|
# breaks, so don't trap the exception. Using fchmod() since
|
@@ -573,5 +588,10 @@ module Unicorn
|
|
573
588
|
@app = @app.call if @app.respond_to?(:arity) && @app.arity == 0
|
574
589
|
end
|
575
590
|
|
591
|
+
def proc_name(tag)
|
592
|
+
$0 = ([ File.basename(@start_ctx[:zero]), tag ] +
|
593
|
+
@start_ctx[:argv]).join(' ')
|
594
|
+
end
|
595
|
+
|
576
596
|
end
|
577
597
|
end
|
data/lib/unicorn/const.rb
CHANGED
data/lib/unicorn/socket.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'fcntl'
|
2
1
|
require 'socket'
|
3
2
|
require 'io/nonblock'
|
4
3
|
|
@@ -48,10 +47,6 @@ module Unicorn
|
|
48
47
|
sock.setsockopt(SOL_TCP, TCP_CORK, 1) if defined?(TCP_CORK)
|
49
48
|
end
|
50
49
|
|
51
|
-
def set_cloexec(io)
|
52
|
-
io.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) if defined?(Fcntl::FD_CLOEXEC)
|
53
|
-
end
|
54
|
-
|
55
50
|
def set_server_sockopt(sock)
|
56
51
|
if defined?(TCP_DEFER_ACCEPT)
|
57
52
|
sock.setsockopt(SOL_TCP, TCP_DEFER_ACCEPT, 1) rescue nil
|
data/local.mk.sample
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
# this is the local.mk file used by Eric Wong on his dev boxes.
|
2
|
+
# GNUmakefile will source local.mk in the top-level source tree
|
3
|
+
# if it is present.
|
4
|
+
#
|
5
|
+
# This is depends on a bunch of GNU-isms from bash, sed, touch.
|
6
|
+
|
7
|
+
DLEXT := so
|
8
|
+
rack_ver := 0.9.1
|
9
|
+
|
10
|
+
# Avoid loading rubygems to speed up tests because gmake is
|
11
|
+
# fork+exec heavy with Ruby.
|
12
|
+
ifeq ($(r19),)
|
13
|
+
ruby := $(HOME)/bin/ruby
|
14
|
+
RUBYLIB := $(HOME)/lib/ruby/gems/1.8/gems/rack-$(rack_ver)/lib
|
15
|
+
else
|
16
|
+
export PATH := $(HOME)/ruby-1.9/bin:$(PATH)
|
17
|
+
ruby := $(HOME)/ruby-1.9/bin/ruby --disable-gems
|
18
|
+
RUBYLIB := $(HOME)/ruby-1.9/lib/ruby/gems/1.9.1/gems/rack-$(rack_ver)/lib
|
19
|
+
endif
|
20
|
+
|
21
|
+
# pipefail is THE reason to use bash (v3+)
|
22
|
+
SHELL := /bin/bash -e -o pipefail
|
23
|
+
|
24
|
+
full-test: test-18 test-19
|
25
|
+
test-18:
|
26
|
+
$(MAKE) test 2>&1 | sed -u -e 's!^!1.8 !'
|
27
|
+
test-19:
|
28
|
+
$(MAKE) test r19=1 2>&1 | sed -u -e 's!^!1.9 !'
|
29
|
+
|
30
|
+
# publishes docs to http://unicorn.bogomips.org
|
31
|
+
publish_doc:
|
32
|
+
-git set-file-times
|
33
|
+
$(MAKE) doc
|
34
|
+
$(MAKE) doc_gz
|
35
|
+
rsync -av --delete doc/ dcvr:/srv/unicorn/
|
36
|
+
|
37
|
+
# Create gzip variants of the same timestamp as the original so nginx
|
38
|
+
# "gzip_static on" can serve the gzipped versions directly.
|
39
|
+
doc_gz: suf := html js css
|
40
|
+
doc_gz: globs := $(addprefix doc/*.,$(suf)) $(addprefix doc/*/*.,$(suf))
|
41
|
+
doc_gz: docs := $(wildcard $(globs))
|
42
|
+
doc_gz:
|
43
|
+
for i in $(docs); do gzip < $$i > $$i.gz; touch -r $$i $$i.gz; done
|
data/test/exec/test_exec.rb
CHANGED
@@ -245,6 +245,38 @@ end
|
|
245
245
|
assert_shutdown(pid)
|
246
246
|
end
|
247
247
|
|
248
|
+
def test_unicorn_config_per_worker_listen
|
249
|
+
port2 = unused_port
|
250
|
+
pid_spit = 'use Rack::ContentLength;' \
|
251
|
+
'run proc { |e| [ 200, {"Content-Type"=>"text/plain"}, ["#$$\\n"] ] }'
|
252
|
+
File.open("config.ru", "wb") { |fp| fp.syswrite(pid_spit) }
|
253
|
+
tmp = Tempfile.new('test.socket')
|
254
|
+
File.unlink(tmp.path)
|
255
|
+
ucfg = Tempfile.new('unicorn_test_config')
|
256
|
+
ucfg.syswrite("listen '#@addr:#@port'\n")
|
257
|
+
ucfg.syswrite("before_fork { |s,nr|\n")
|
258
|
+
ucfg.syswrite(" s.listen('#{tmp.path}', :backlog => 5, :sndbuf => 8192)\n")
|
259
|
+
ucfg.syswrite(" s.listen('#@addr:#{port2}', :rcvbuf => 8192)\n")
|
260
|
+
ucfg.syswrite("\n}\n")
|
261
|
+
pid = xfork do
|
262
|
+
redirect_test_io { exec($unicorn_bin, "-c#{ucfg.path}") }
|
263
|
+
end
|
264
|
+
results = retry_hit(["http://#{@addr}:#{@port}/"])
|
265
|
+
assert_equal String, results[0].class
|
266
|
+
worker_pid = results[0].to_i
|
267
|
+
assert_not_equal pid, worker_pid
|
268
|
+
s = UNIXSocket.new(tmp.path)
|
269
|
+
s.syswrite("GET / HTTP/1.0\r\n\r\n")
|
270
|
+
results = ''
|
271
|
+
loop { results << s.sysread(4096) } rescue nil
|
272
|
+
assert_nothing_raised { s.close }
|
273
|
+
assert_equal worker_pid, results.split(/\r\n/).last.to_i
|
274
|
+
results = hit(["http://#@addr:#{port2}/"])
|
275
|
+
assert_equal String, results[0].class
|
276
|
+
assert_equal worker_pid, results[0].to_i
|
277
|
+
assert_shutdown(pid)
|
278
|
+
end
|
279
|
+
|
248
280
|
def test_unicorn_config_listen_augments_cli
|
249
281
|
port2 = unused_port(@addr)
|
250
282
|
File.open("config.ru", "wb") { |fp| fp.syswrite(HI) }
|
@@ -467,4 +499,67 @@ end
|
|
467
499
|
reexec_usr2_quit_test(new_pid, pid_file)
|
468
500
|
end
|
469
501
|
|
502
|
+
def test_reexec_fd_leak
|
503
|
+
unless RUBY_PLATFORM =~ /linux/ # Solaris may work, too, but I forget...
|
504
|
+
warn "FD leak test only works on Linux at the moment"
|
505
|
+
return
|
506
|
+
end
|
507
|
+
pid_file = "#{@tmpdir}/test.pid"
|
508
|
+
log = Tempfile.new('unicorn_test_log')
|
509
|
+
log.sync = true
|
510
|
+
ucfg = Tempfile.new('unicorn_test_config')
|
511
|
+
ucfg.syswrite("pid \"#{pid_file}\"\n")
|
512
|
+
ucfg.syswrite("logger Logger.new('#{log.path}')\n")
|
513
|
+
ucfg.syswrite("stderr_path '#{log.path}'\n")
|
514
|
+
ucfg.syswrite("stdout_path '#{log.path}'\n")
|
515
|
+
ucfg.close
|
516
|
+
|
517
|
+
File.open("config.ru", "wb") { |fp| fp.syswrite(HI) }
|
518
|
+
pid = xfork do
|
519
|
+
redirect_test_io do
|
520
|
+
exec($unicorn_bin, "-D", "-l#{@addr}:#{@port}", "-c#{ucfg.path}")
|
521
|
+
end
|
522
|
+
end
|
523
|
+
|
524
|
+
wait_master_ready(log.path)
|
525
|
+
wait_for_file(pid_file)
|
526
|
+
orig_pid = pid = File.read(pid_file).to_i
|
527
|
+
orig_fds = `ls -l /proc/#{pid}/fd`.split(/\n/)
|
528
|
+
assert $?.success?
|
529
|
+
expect_size = orig_fds.size
|
530
|
+
|
531
|
+
assert_nothing_raised do
|
532
|
+
Process.kill(:USR2, pid)
|
533
|
+
wait_for_file("#{pid_file}.oldbin")
|
534
|
+
Process.kill(:QUIT, pid)
|
535
|
+
end
|
536
|
+
wait_for_death(pid)
|
537
|
+
|
538
|
+
wait_for_file(pid_file)
|
539
|
+
pid = File.read(pid_file).to_i
|
540
|
+
assert_not_equal orig_pid, pid
|
541
|
+
curr_fds = `ls -l /proc/#{pid}/fd`.split(/\n/)
|
542
|
+
assert $?.success?
|
543
|
+
|
544
|
+
# we could've inherited descriptors the first time around
|
545
|
+
assert expect_size >= curr_fds.size
|
546
|
+
expect_size = curr_fds.size
|
547
|
+
|
548
|
+
assert_nothing_raised do
|
549
|
+
Process.kill(:USR2, pid)
|
550
|
+
wait_for_file("#{pid_file}.oldbin")
|
551
|
+
Process.kill(:QUIT, pid)
|
552
|
+
end
|
553
|
+
wait_for_death(pid)
|
554
|
+
|
555
|
+
wait_for_file(pid_file)
|
556
|
+
pid = File.read(pid_file).to_i
|
557
|
+
curr_fds = `ls -l /proc/#{pid}/fd`.split(/\n/)
|
558
|
+
assert $?.success?
|
559
|
+
assert_equal expect_size, curr_fds.size
|
560
|
+
|
561
|
+
Process.kill(:QUIT, pid)
|
562
|
+
wait_for_death(pid)
|
563
|
+
end
|
564
|
+
|
470
565
|
end if do_test
|
@@ -7,6 +7,7 @@ require File.join(File.dirname(__FILE__), 'boot')
|
|
7
7
|
|
8
8
|
Rails::Initializer.run do |config|
|
9
9
|
config.frameworks -= [ :action_web_service, :action_mailer ]
|
10
|
+
config.action_controller.session_store = :active_record_store
|
10
11
|
config.action_controller.session = {
|
11
12
|
:session_key => "_unicorn_rails_test.#{rand}",
|
12
13
|
:secret => "#{rand}#{rand}#{rand}#{rand}",
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'digest/sha1'
|
2
|
+
class FooController < ApplicationController
|
3
|
+
def index
|
4
|
+
render :text => "FOO\n"
|
5
|
+
end
|
6
|
+
|
7
|
+
def xcookie
|
8
|
+
cookies["foo"] = "cookie #$$"
|
9
|
+
render :text => ""
|
10
|
+
end
|
11
|
+
|
12
|
+
def xnotice
|
13
|
+
flash[:notice] = "session #$$"
|
14
|
+
render :text => ""
|
15
|
+
end
|
16
|
+
|
17
|
+
def xpost
|
18
|
+
if request.post?
|
19
|
+
digest = Digest::SHA1.new
|
20
|
+
out = "params: #{params.inspect}\n"
|
21
|
+
if file = params[:file]
|
22
|
+
loop do
|
23
|
+
buf = file.read(4096) or break
|
24
|
+
digest.update(buf)
|
25
|
+
end
|
26
|
+
out << "sha1: #{digest.to_s}\n"
|
27
|
+
end
|
28
|
+
headers['content-type'] = 'text/plain'
|
29
|
+
render :text => out
|
30
|
+
else
|
31
|
+
render :status => 403, :text => "need post\n"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
# Don't change this file!
|
2
|
+
# Configure your app in config/environment.rb and config/environments/*.rb
|
3
|
+
|
4
|
+
RAILS_ROOT = "#{File.dirname(__FILE__)}/.." unless defined?(RAILS_ROOT)
|
5
|
+
|
6
|
+
module Rails
|
7
|
+
class << self
|
8
|
+
def boot!
|
9
|
+
unless booted?
|
10
|
+
preinitialize
|
11
|
+
pick_boot.run
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def booted?
|
16
|
+
defined? Rails::Initializer
|
17
|
+
end
|
18
|
+
|
19
|
+
def pick_boot
|
20
|
+
(vendor_rails? ? VendorBoot : GemBoot).new
|
21
|
+
end
|
22
|
+
|
23
|
+
def vendor_rails?
|
24
|
+
File.exist?("#{RAILS_ROOT}/vendor/rails")
|
25
|
+
end
|
26
|
+
|
27
|
+
def preinitialize
|
28
|
+
load(preinitializer_path) if File.exist?(preinitializer_path)
|
29
|
+
end
|
30
|
+
|
31
|
+
def preinitializer_path
|
32
|
+
"#{RAILS_ROOT}/config/preinitializer.rb"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class Boot
|
37
|
+
def run
|
38
|
+
load_initializer
|
39
|
+
Rails::Initializer.run(:set_load_path)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class VendorBoot < Boot
|
44
|
+
def load_initializer
|
45
|
+
require "#{RAILS_ROOT}/vendor/rails/railties/lib/initializer"
|
46
|
+
Rails::Initializer.run(:install_gem_spec_stubs)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
class GemBoot < Boot
|
51
|
+
def load_initializer
|
52
|
+
self.class.load_rubygems
|
53
|
+
load_rails_gem
|
54
|
+
require 'initializer'
|
55
|
+
end
|
56
|
+
|
57
|
+
def load_rails_gem
|
58
|
+
if version = self.class.gem_version
|
59
|
+
gem 'rails', version
|
60
|
+
else
|
61
|
+
gem 'rails'
|
62
|
+
end
|
63
|
+
rescue Gem::LoadError => load_error
|
64
|
+
$stderr.puts %(Missing the Rails #{version} gem. Please `gem install -v=#{version} rails`, update your RAILS_GEM_VERSION setting in config/environment.rb for the Rails version you do have installed, or comment out RAILS_GEM_VERSION to use the latest version installed.)
|
65
|
+
exit 1
|
66
|
+
end
|
67
|
+
|
68
|
+
class << self
|
69
|
+
def rubygems_version
|
70
|
+
Gem::RubyGemsVersion rescue nil
|
71
|
+
end
|
72
|
+
|
73
|
+
def gem_version
|
74
|
+
if defined? RAILS_GEM_VERSION
|
75
|
+
RAILS_GEM_VERSION
|
76
|
+
elsif ENV.include?('RAILS_GEM_VERSION')
|
77
|
+
ENV['RAILS_GEM_VERSION']
|
78
|
+
else
|
79
|
+
parse_gem_version(read_environment_rb)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def load_rubygems
|
84
|
+
require 'rubygems'
|
85
|
+
min_version = '1.3.1'
|
86
|
+
unless rubygems_version >= min_version
|
87
|
+
$stderr.puts %Q(Rails requires RubyGems >= #{min_version} (you have #{rubygems_version}). Please `gem update --system` and try again.)
|
88
|
+
exit 1
|
89
|
+
end
|
90
|
+
|
91
|
+
rescue LoadError
|
92
|
+
$stderr.puts %Q(Rails requires RubyGems >= #{min_version}. Please install RubyGems and try again: http://rubygems.rubyforge.org)
|
93
|
+
exit 1
|
94
|
+
end
|
95
|
+
|
96
|
+
def parse_gem_version(text)
|
97
|
+
$1 if text =~ /^[^#]*RAILS_GEM_VERSION\s*=\s*["']([!~<>=]*\s*[\d.]+)["']/
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
def read_environment_rb
|
102
|
+
File.read("#{RAILS_ROOT}/config/environment.rb")
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# All that for this:
|
109
|
+
Rails.boot!
|
@@ -0,0 +1,15 @@
|
|
1
|
+
unless defined? RAILS_GEM_VERSION
|
2
|
+
RAILS_GEM_VERSION = ENV['UNICORN_RAILS_VERSION']
|
3
|
+
end
|
4
|
+
|
5
|
+
# Bootstrap the Rails environment, frameworks, and default configuration
|
6
|
+
require File.join(File.dirname(__FILE__), 'boot')
|
7
|
+
|
8
|
+
Rails::Initializer.run do |config|
|
9
|
+
config.frameworks -= [ :action_web_service, :action_mailer ]
|
10
|
+
config.action_controller.session_store = :active_record_store
|
11
|
+
config.action_controller.session = {
|
12
|
+
:session_key => "_unicorn_rails_test.#{rand}",
|
13
|
+
:secret => "#{rand}#{rand}#{rand}#{rand}",
|
14
|
+
}
|
15
|
+
end
|
File without changes
|
@@ -0,0 +1 @@
|
|
1
|
+
404 Not Found
|
@@ -0,0 +1 @@
|
|
1
|
+
500 Internal Server Error
|
@@ -7,6 +7,7 @@ require File.join(File.dirname(__FILE__), 'boot')
|
|
7
7
|
|
8
8
|
Rails::Initializer.run do |config|
|
9
9
|
config.frameworks -= [ :action_web_service, :action_mailer ]
|
10
|
+
config.action_controller.session_store = :active_record_store
|
10
11
|
config.action_controller.session = {
|
11
12
|
:session_key => "_unicorn_rails_test.#{rand}",
|
12
13
|
:secret => "#{rand}#{rand}#{rand}#{rand}",
|
@@ -6,7 +6,8 @@ end
|
|
6
6
|
require File.join(File.dirname(__FILE__), 'boot')
|
7
7
|
|
8
8
|
Rails::Initializer.run do |config|
|
9
|
-
config.frameworks -= [ :
|
9
|
+
config.frameworks -= [ :active_resource, :action_mailer ]
|
10
|
+
config.action_controller.session_store = :active_record_store
|
10
11
|
config.action_controller.session = {
|
11
12
|
:session_key => "_unicorn_rails_test.#{rand}",
|
12
13
|
:secret => "#{rand}#{rand}#{rand}#{rand}",
|
data/test/rails/test_rails.rb
CHANGED
@@ -74,6 +74,10 @@ logger Logger.new('#{COMMON_TMP.path}')
|
|
74
74
|
Dir.chdir("#@tmpdir/vendor/rails") do
|
75
75
|
system('git', 'reset', '-q', '--hard', "v#{UNICORN_RAILS_TEST_VERSION}")
|
76
76
|
end
|
77
|
+
|
78
|
+
assert(system('rake', 'db:sessions:create'))
|
79
|
+
assert(system('rake', 'db:migrate'))
|
80
|
+
|
77
81
|
@addr = ENV['UNICORN_TEST_ADDR'] || '127.0.0.1'
|
78
82
|
@port = unused_port(@addr)
|
79
83
|
@start_pid = $$
|
data/test/unit/test_upload.rb
CHANGED
@@ -74,6 +74,12 @@ class UploadTest < Test::Unit::TestCase
|
|
74
74
|
sock.close
|
75
75
|
assert path != path2
|
76
76
|
|
77
|
+
# make sure the next request comes in so the unlink got processed
|
78
|
+
sock = TCPSocket.new(@addr, @port)
|
79
|
+
sock.syswrite("GET ?lasdf\r\n\r\n\r\n\r\n")
|
80
|
+
sock.sysread(4096) rescue nil
|
81
|
+
sock.close
|
82
|
+
|
77
83
|
assert ! File.exist?(path)
|
78
84
|
end
|
79
85
|
|
data/unicorn.gemspec
CHANGED
@@ -2,17 +2,17 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = %q{unicorn}
|
5
|
-
s.version = "0.4.
|
5
|
+
s.version = "0.4.2"
|
6
6
|
|
7
7
|
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
8
8
|
s.authors = ["Eric Wong"]
|
9
|
-
s.date = %q{2009-04-
|
9
|
+
s.date = %q{2009-04-02}
|
10
10
|
s.description = %q{A small fast HTTP library and server for Rack applications.}
|
11
11
|
s.email = %q{normalperson@yhbt.net}
|
12
12
|
s.executables = ["unicorn", "unicorn_rails"]
|
13
13
|
s.extensions = ["ext/unicorn/http11/extconf.rb"]
|
14
14
|
s.extra_rdoc_files = ["CHANGELOG", "LICENSE", "README", "TODO", "bin/unicorn", "bin/unicorn_rails", "ext/unicorn/http11/ext_help.h", "ext/unicorn/http11/extconf.rb", "ext/unicorn/http11/http11.c", "ext/unicorn/http11/http11_parser.c", "ext/unicorn/http11/http11_parser.h", "ext/unicorn/http11/http11_parser.rl", "ext/unicorn/http11/http11_parser_common.rl", "lib/unicorn.rb", "lib/unicorn/app/exec_cgi.rb", "lib/unicorn/app/old_rails.rb", "lib/unicorn/app/old_rails/static.rb", "lib/unicorn/cgi_wrapper.rb", "lib/unicorn/configurator.rb", "lib/unicorn/const.rb", "lib/unicorn/http_request.rb", "lib/unicorn/http_response.rb", "lib/unicorn/launcher.rb", "lib/unicorn/socket.rb", "lib/unicorn/util.rb"]
|
15
|
-
s.files = [".document", ".gitignore", "CHANGELOG", "CONTRIBUTORS", "DESIGN", "GNUmakefile", "LICENSE", "Manifest", "README", "Rakefile", "SIGNALS", "TODO", "bin/unicorn", "bin/unicorn_rails", "ext/unicorn/http11/ext_help.h", "ext/unicorn/http11/extconf.rb", "ext/unicorn/http11/http11.c", "ext/unicorn/http11/http11_parser.c", "ext/unicorn/http11/http11_parser.h", "ext/unicorn/http11/http11_parser.rl", "ext/unicorn/http11/http11_parser_common.rl", "lib/unicorn.rb", "lib/unicorn/app/exec_cgi.rb", "lib/unicorn/app/old_rails.rb", "lib/unicorn/app/old_rails/static.rb", "lib/unicorn/cgi_wrapper.rb", "lib/unicorn/configurator.rb", "lib/unicorn/const.rb", "lib/unicorn/http_request.rb", "lib/unicorn/http_response.rb", "lib/unicorn/launcher.rb", "lib/unicorn/socket.rb", "lib/unicorn/util.rb", "setup.rb", "test/aggregate.rb", "test/benchmark/README", "test/benchmark/big_request.rb", "test/benchmark/dd.ru", "test/benchmark/request.rb", "test/benchmark/response.rb", "test/exec/README", "test/exec/test_exec.rb", "test/rails/app-1.2.3/.gitignore", "test/rails/app-1.2.3/app/controllers/application.rb", "test/rails/app-1.2.3/app/controllers/foo_controller.rb", "test/rails/app-1.2.3/app/helpers/application_helper.rb", "test/rails/app-1.2.3/config/boot.rb", "test/rails/app-1.2.3/config/database.yml", "test/rails/app-1.2.3/config/environment.rb", "test/rails/app-1.2.3/config/environments/development.rb", "test/rails/app-1.2.3/config/environments/production.rb", "test/rails/app-1.2.3/config/routes.rb", "test/rails/app-1.2.3/db/.gitignore", "test/rails/app-1.2.3/public/404.html", "test/rails/app-1.2.3/public/500.html", "test/rails/app-2.0.2/.gitignore", "test/rails/app-2.0.2/app/controllers/application.rb", "test/rails/app-2.0.2/app/controllers/foo_controller.rb", "test/rails/app-2.0.2/app/helpers/application_helper.rb", "test/rails/app-2.0.2/config/boot.rb", "test/rails/app-2.0.2/config/database.yml", "test/rails/app-2.0.2/config/environment.rb", "test/rails/app-2.0.2/config/environments/development.rb", "test/rails/app-2.0.2/config/environments/production.rb", "test/rails/app-2.0.2/config/routes.rb", "test/rails/app-2.0.2/db/.gitignore", "test/rails/app-2.0.2/public/404.html", "test/rails/app-2.0.2/public/500.html", "test/rails/app-2.2.2/.gitignore", "test/rails/app-2.2.2/app/controllers/application.rb", "test/rails/app-2.2.2/app/controllers/foo_controller.rb", "test/rails/app-2.2.2/app/helpers/application_helper.rb", "test/rails/app-2.2.2/config/boot.rb", "test/rails/app-2.2.2/config/database.yml", "test/rails/app-2.2.2/config/environment.rb", "test/rails/app-2.2.2/config/environments/development.rb", "test/rails/app-2.2.2/config/environments/production.rb", "test/rails/app-2.2.2/config/routes.rb", "test/rails/app-2.2.2/db/.gitignore", "test/rails/app-2.2.2/public/404.html", "test/rails/app-2.2.2/public/500.html", "test/rails/app-2.3.2.1/.gitignore", "test/rails/app-2.3.2.1/app/controllers/application_controller.rb", "test/rails/app-2.3.2.1/app/controllers/foo_controller.rb", "test/rails/app-2.3.2.1/app/helpers/application_helper.rb", "test/rails/app-2.3.2.1/config/boot.rb", "test/rails/app-2.3.2.1/config/database.yml", "test/rails/app-2.3.2.1/config/environment.rb", "test/rails/app-2.3.2.1/config/environments/development.rb", "test/rails/app-2.3.2.1/config/environments/production.rb", "test/rails/app-2.3.2.1/config/routes.rb", "test/rails/app-2.3.2.1/db/.gitignore", "test/rails/app-2.3.2.1/public/404.html", "test/rails/app-2.3.2.1/public/500.html", "test/rails/test_rails.rb", "test/test_helper.rb", "test/tools/trickletest.rb", "test/unit/test_configurator.rb", "test/unit/test_http_parser.rb", "test/unit/test_request.rb", "test/unit/test_response.rb", "test/unit/test_server.rb", "test/unit/test_socket_helper.rb", "test/unit/test_upload.rb", "unicorn.gemspec"]
|
15
|
+
s.files = [".document", ".gitignore", "CHANGELOG", "CONTRIBUTORS", "DESIGN", "GNUmakefile", "LICENSE", "Manifest", "PHILOSOPHY", "README", "Rakefile", "SIGNALS", "TODO", "bin/unicorn", "bin/unicorn_rails", "ext/unicorn/http11/ext_help.h", "ext/unicorn/http11/extconf.rb", "ext/unicorn/http11/http11.c", "ext/unicorn/http11/http11_parser.c", "ext/unicorn/http11/http11_parser.h", "ext/unicorn/http11/http11_parser.rl", "ext/unicorn/http11/http11_parser_common.rl", "lib/unicorn.rb", "lib/unicorn/app/exec_cgi.rb", "lib/unicorn/app/old_rails.rb", "lib/unicorn/app/old_rails/static.rb", "lib/unicorn/cgi_wrapper.rb", "lib/unicorn/configurator.rb", "lib/unicorn/const.rb", "lib/unicorn/http_request.rb", "lib/unicorn/http_response.rb", "lib/unicorn/launcher.rb", "lib/unicorn/socket.rb", "lib/unicorn/util.rb", "local.mk.sample", "setup.rb", "test/aggregate.rb", "test/benchmark/README", "test/benchmark/big_request.rb", "test/benchmark/dd.ru", "test/benchmark/request.rb", "test/benchmark/response.rb", "test/exec/README", "test/exec/test_exec.rb", "test/rails/app-1.2.3/.gitignore", "test/rails/app-1.2.3/Rakefile", "test/rails/app-1.2.3/app/controllers/application.rb", "test/rails/app-1.2.3/app/controllers/foo_controller.rb", "test/rails/app-1.2.3/app/helpers/application_helper.rb", "test/rails/app-1.2.3/config/boot.rb", "test/rails/app-1.2.3/config/database.yml", "test/rails/app-1.2.3/config/environment.rb", "test/rails/app-1.2.3/config/environments/development.rb", "test/rails/app-1.2.3/config/environments/production.rb", "test/rails/app-1.2.3/config/routes.rb", "test/rails/app-1.2.3/db/.gitignore", "test/rails/app-1.2.3/log/.gitignore", "test/rails/app-1.2.3/public/404.html", "test/rails/app-1.2.3/public/500.html", "test/rails/app-2.0.2/.gitignore", "test/rails/app-2.0.2/Rakefile", "test/rails/app-2.0.2/app/controllers/application.rb", "test/rails/app-2.0.2/app/controllers/foo_controller.rb", "test/rails/app-2.0.2/app/helpers/application_helper.rb", "test/rails/app-2.0.2/config/boot.rb", "test/rails/app-2.0.2/config/database.yml", "test/rails/app-2.0.2/config/environment.rb", "test/rails/app-2.0.2/config/environments/development.rb", "test/rails/app-2.0.2/config/environments/production.rb", "test/rails/app-2.0.2/config/routes.rb", "test/rails/app-2.0.2/db/.gitignore", "test/rails/app-2.0.2/log/.gitignore", "test/rails/app-2.0.2/public/404.html", "test/rails/app-2.0.2/public/500.html", "test/rails/app-2.1.2/.gitignore", "test/rails/app-2.1.2/Rakefile", "test/rails/app-2.1.2/app/controllers/application.rb", "test/rails/app-2.1.2/app/controllers/foo_controller.rb", "test/rails/app-2.1.2/app/helpers/application_helper.rb", "test/rails/app-2.1.2/config/boot.rb", "test/rails/app-2.1.2/config/database.yml", "test/rails/app-2.1.2/config/environment.rb", "test/rails/app-2.1.2/config/environments/development.rb", "test/rails/app-2.1.2/config/environments/production.rb", "test/rails/app-2.1.2/config/routes.rb", "test/rails/app-2.1.2/db/.gitignore", "test/rails/app-2.1.2/log/.gitignore", "test/rails/app-2.1.2/public/404.html", "test/rails/app-2.1.2/public/500.html", "test/rails/app-2.2.2/.gitignore", "test/rails/app-2.2.2/Rakefile", "test/rails/app-2.2.2/app/controllers/application.rb", "test/rails/app-2.2.2/app/controllers/foo_controller.rb", "test/rails/app-2.2.2/app/helpers/application_helper.rb", "test/rails/app-2.2.2/config/boot.rb", "test/rails/app-2.2.2/config/database.yml", "test/rails/app-2.2.2/config/environment.rb", "test/rails/app-2.2.2/config/environments/development.rb", "test/rails/app-2.2.2/config/environments/production.rb", "test/rails/app-2.2.2/config/routes.rb", "test/rails/app-2.2.2/db/.gitignore", "test/rails/app-2.2.2/log/.gitignore", "test/rails/app-2.2.2/public/404.html", "test/rails/app-2.2.2/public/500.html", "test/rails/app-2.3.2.1/.gitignore", "test/rails/app-2.3.2.1/Rakefile", "test/rails/app-2.3.2.1/app/controllers/application_controller.rb", "test/rails/app-2.3.2.1/app/controllers/foo_controller.rb", "test/rails/app-2.3.2.1/app/helpers/application_helper.rb", "test/rails/app-2.3.2.1/config/boot.rb", "test/rails/app-2.3.2.1/config/database.yml", "test/rails/app-2.3.2.1/config/environment.rb", "test/rails/app-2.3.2.1/config/environments/development.rb", "test/rails/app-2.3.2.1/config/environments/production.rb", "test/rails/app-2.3.2.1/config/routes.rb", "test/rails/app-2.3.2.1/db/.gitignore", "test/rails/app-2.3.2.1/log/.gitignore", "test/rails/app-2.3.2.1/public/404.html", "test/rails/app-2.3.2.1/public/500.html", "test/rails/test_rails.rb", "test/test_helper.rb", "test/tools/trickletest.rb", "test/unit/test_configurator.rb", "test/unit/test_http_parser.rb", "test/unit/test_request.rb", "test/unit/test_response.rb", "test/unit/test_server.rb", "test/unit/test_socket_helper.rb", "test/unit/test_upload.rb", "unicorn.gemspec"]
|
16
16
|
s.has_rdoc = true
|
17
17
|
s.homepage = %q{http://unicorn.bogomips.org}
|
18
18
|
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Unicorn", "--main", "README"]
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: unicorn
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Eric Wong
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-04-
|
12
|
+
date: 2009-04-02 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -55,6 +55,7 @@ files:
|
|
55
55
|
- GNUmakefile
|
56
56
|
- LICENSE
|
57
57
|
- Manifest
|
58
|
+
- PHILOSOPHY
|
58
59
|
- README
|
59
60
|
- Rakefile
|
60
61
|
- SIGNALS
|
@@ -80,6 +81,7 @@ files:
|
|
80
81
|
- lib/unicorn/launcher.rb
|
81
82
|
- lib/unicorn/socket.rb
|
82
83
|
- lib/unicorn/util.rb
|
84
|
+
- local.mk.sample
|
83
85
|
- setup.rb
|
84
86
|
- test/aggregate.rb
|
85
87
|
- test/benchmark/README
|
@@ -90,6 +92,7 @@ files:
|
|
90
92
|
- test/exec/README
|
91
93
|
- test/exec/test_exec.rb
|
92
94
|
- test/rails/app-1.2.3/.gitignore
|
95
|
+
- test/rails/app-1.2.3/Rakefile
|
93
96
|
- test/rails/app-1.2.3/app/controllers/application.rb
|
94
97
|
- test/rails/app-1.2.3/app/controllers/foo_controller.rb
|
95
98
|
- test/rails/app-1.2.3/app/helpers/application_helper.rb
|
@@ -100,9 +103,11 @@ files:
|
|
100
103
|
- test/rails/app-1.2.3/config/environments/production.rb
|
101
104
|
- test/rails/app-1.2.3/config/routes.rb
|
102
105
|
- test/rails/app-1.2.3/db/.gitignore
|
106
|
+
- test/rails/app-1.2.3/log/.gitignore
|
103
107
|
- test/rails/app-1.2.3/public/404.html
|
104
108
|
- test/rails/app-1.2.3/public/500.html
|
105
109
|
- test/rails/app-2.0.2/.gitignore
|
110
|
+
- test/rails/app-2.0.2/Rakefile
|
106
111
|
- test/rails/app-2.0.2/app/controllers/application.rb
|
107
112
|
- test/rails/app-2.0.2/app/controllers/foo_controller.rb
|
108
113
|
- test/rails/app-2.0.2/app/helpers/application_helper.rb
|
@@ -113,9 +118,26 @@ files:
|
|
113
118
|
- test/rails/app-2.0.2/config/environments/production.rb
|
114
119
|
- test/rails/app-2.0.2/config/routes.rb
|
115
120
|
- test/rails/app-2.0.2/db/.gitignore
|
121
|
+
- test/rails/app-2.0.2/log/.gitignore
|
116
122
|
- test/rails/app-2.0.2/public/404.html
|
117
123
|
- test/rails/app-2.0.2/public/500.html
|
124
|
+
- test/rails/app-2.1.2/.gitignore
|
125
|
+
- test/rails/app-2.1.2/Rakefile
|
126
|
+
- test/rails/app-2.1.2/app/controllers/application.rb
|
127
|
+
- test/rails/app-2.1.2/app/controllers/foo_controller.rb
|
128
|
+
- test/rails/app-2.1.2/app/helpers/application_helper.rb
|
129
|
+
- test/rails/app-2.1.2/config/boot.rb
|
130
|
+
- test/rails/app-2.1.2/config/database.yml
|
131
|
+
- test/rails/app-2.1.2/config/environment.rb
|
132
|
+
- test/rails/app-2.1.2/config/environments/development.rb
|
133
|
+
- test/rails/app-2.1.2/config/environments/production.rb
|
134
|
+
- test/rails/app-2.1.2/config/routes.rb
|
135
|
+
- test/rails/app-2.1.2/db/.gitignore
|
136
|
+
- test/rails/app-2.1.2/log/.gitignore
|
137
|
+
- test/rails/app-2.1.2/public/404.html
|
138
|
+
- test/rails/app-2.1.2/public/500.html
|
118
139
|
- test/rails/app-2.2.2/.gitignore
|
140
|
+
- test/rails/app-2.2.2/Rakefile
|
119
141
|
- test/rails/app-2.2.2/app/controllers/application.rb
|
120
142
|
- test/rails/app-2.2.2/app/controllers/foo_controller.rb
|
121
143
|
- test/rails/app-2.2.2/app/helpers/application_helper.rb
|
@@ -126,9 +148,11 @@ files:
|
|
126
148
|
- test/rails/app-2.2.2/config/environments/production.rb
|
127
149
|
- test/rails/app-2.2.2/config/routes.rb
|
128
150
|
- test/rails/app-2.2.2/db/.gitignore
|
151
|
+
- test/rails/app-2.2.2/log/.gitignore
|
129
152
|
- test/rails/app-2.2.2/public/404.html
|
130
153
|
- test/rails/app-2.2.2/public/500.html
|
131
154
|
- test/rails/app-2.3.2.1/.gitignore
|
155
|
+
- test/rails/app-2.3.2.1/Rakefile
|
132
156
|
- test/rails/app-2.3.2.1/app/controllers/application_controller.rb
|
133
157
|
- test/rails/app-2.3.2.1/app/controllers/foo_controller.rb
|
134
158
|
- test/rails/app-2.3.2.1/app/helpers/application_helper.rb
|
@@ -139,6 +163,7 @@ files:
|
|
139
163
|
- test/rails/app-2.3.2.1/config/environments/production.rb
|
140
164
|
- test/rails/app-2.3.2.1/config/routes.rb
|
141
165
|
- test/rails/app-2.3.2.1/db/.gitignore
|
166
|
+
- test/rails/app-2.3.2.1/log/.gitignore
|
142
167
|
- test/rails/app-2.3.2.1/public/404.html
|
143
168
|
- test/rails/app-2.3.2.1/public/500.html
|
144
169
|
- test/rails/test_rails.rb
|