unicorn 0.4.1 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +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
|