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.
Files changed (37) hide show
  1. data/CHANGELOG +1 -0
  2. data/GNUmakefile +7 -3
  3. data/Manifest +25 -0
  4. data/PHILOSOPHY +139 -0
  5. data/bin/unicorn +17 -23
  6. data/bin/unicorn_rails +19 -30
  7. data/lib/unicorn.rb +32 -12
  8. data/lib/unicorn/const.rb +1 -1
  9. data/lib/unicorn/socket.rb +0 -5
  10. data/local.mk.sample +43 -0
  11. data/test/exec/test_exec.rb +95 -0
  12. data/test/rails/app-1.2.3/Rakefile +7 -0
  13. data/test/rails/app-1.2.3/config/environment.rb +1 -0
  14. data/test/rails/app-2.0.2/Rakefile +7 -0
  15. data/test/rails/app-2.0.2/config/environment.rb +1 -0
  16. data/test/rails/app-2.1.2/.gitignore +2 -0
  17. data/test/rails/app-2.1.2/Rakefile +7 -0
  18. data/test/rails/app-2.1.2/app/controllers/application.rb +2 -0
  19. data/test/rails/app-2.1.2/app/controllers/foo_controller.rb +34 -0
  20. data/test/rails/app-2.1.2/app/helpers/application_helper.rb +2 -0
  21. data/test/rails/app-2.1.2/config/boot.rb +109 -0
  22. data/test/rails/app-2.1.2/config/database.yml +12 -0
  23. data/test/rails/app-2.1.2/config/environment.rb +15 -0
  24. data/test/rails/app-2.1.2/config/environments/development.rb +5 -0
  25. data/test/rails/app-2.1.2/config/environments/production.rb +3 -0
  26. data/test/rails/app-2.1.2/config/routes.rb +4 -0
  27. data/test/rails/app-2.1.2/db/.gitignore +0 -0
  28. data/test/rails/app-2.1.2/public/404.html +1 -0
  29. data/test/rails/app-2.1.2/public/500.html +1 -0
  30. data/test/rails/app-2.2.2/Rakefile +7 -0
  31. data/test/rails/app-2.2.2/config/environment.rb +1 -0
  32. data/test/rails/app-2.3.2.1/Rakefile +7 -0
  33. data/test/rails/app-2.3.2.1/config/environment.rb +2 -1
  34. data/test/rails/test_rails.rb +4 -0
  35. data/test/unit/test_upload.rb +6 -0
  36. data/unicorn.gemspec +3 -3
  37. 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
- git diff --quiet $<
89
+ $(RM) -r .install-tmp
90
+ mkdir .install-tmp
91
+ cp -p $^ .install-tmp
90
92
  $(ruby) setup.rb all
91
- git checkout $<
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
- # require Rack as late as possible in case $LOAD_PATH is modified
121
- # in config.ru or command-line
122
- require 'rack'
123
-
124
- inner_app = case config
125
- when /\.ru$/
126
- raw = File.open(config, "rb") { |fp| fp.sysread(fp.stat.size) }
127
- lambda { || eval("Rack::Builder.new {(#{raw}\n)}.to_app", nil, config) }
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
- end
134
-
135
- app = case env
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.call
139
+ run inner_app
143
140
  end.to_app
144
- end
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.call
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
- lambda do ||
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
- if old_rails
150
- require 'rack'
151
- require 'unicorn/app/old_rails'
152
- Unicorn::App::OldRails.new
153
- else
154
- ActionController::Dispatcher.new
155
- end
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
- lambda { || eval("Rack::Builder.new {(#{raw}\n)}.to_app", nil, config) }
153
+ eval("Rack::Builder.new {(#{raw}\n)}.to_app", nil, config)
160
154
  else
161
- lambda do ||
162
- require config
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 = inner_app.call
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
- $stderr.reopen(File.open(@stderr_path, "a")) if @stderr_path
97
- $stdout.reopen(File.open(@stdout_path, "a")) if @stdout_path
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
- $0 = "unicorn master"
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
- $0 = "unicorn master"
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
- ENV['UNICORN_FD'] = @listeners.map { |sock| sock.fileno }.join(',')
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
- $0 = "unicorn master (old)"
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
- $0 = "unicorn worker[#{worker.nr}]"
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| set_cloexec(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
@@ -58,7 +58,7 @@ module Unicorn
58
58
  REQUEST_URI='REQUEST_URI'.freeze
59
59
  REQUEST_PATH='REQUEST_PATH'.freeze
60
60
 
61
- UNICORN_VERSION="0.4.1".freeze
61
+ UNICORN_VERSION="0.4.2".freeze
62
62
 
63
63
  UNICORN_TMP_BASE="unicorn".freeze
64
64
 
@@ -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
@@ -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
@@ -0,0 +1,7 @@
1
+ require(File.join(File.dirname(__FILE__), 'config', 'boot'))
2
+
3
+ require 'rake'
4
+ require 'rake/testtask'
5
+ require 'rake/rdoctask'
6
+
7
+ require 'tasks/rails'
@@ -7,4 +7,5 @@ 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
  end
@@ -0,0 +1,7 @@
1
+ require(File.join(File.dirname(__FILE__), 'config', 'boot'))
2
+
3
+ require 'rake'
4
+ require 'rake/testtask'
5
+ require 'rake/rdoctask'
6
+
7
+ require 'tasks/rails'
@@ -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,2 @@
1
+ /tmp
2
+ /vendor
@@ -0,0 +1,7 @@
1
+ require(File.join(File.dirname(__FILE__), 'config', 'boot'))
2
+
3
+ require 'rake'
4
+ require 'rake/testtask'
5
+ require 'rake/rdoctask'
6
+
7
+ require 'tasks/rails'
@@ -0,0 +1,2 @@
1
+ class ApplicationController < ActionController::Base
2
+ end
@@ -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,2 @@
1
+ module ApplicationHelper
2
+ 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,12 @@
1
+ development:
2
+ adapter: sqlite3
3
+ database: db/development.sqlite3
4
+ timeout: 5000
5
+ test:
6
+ adapter: sqlite3
7
+ database: db/test.sqlite3
8
+ timeout: 5000
9
+ production:
10
+ adapter: sqlite3
11
+ database: db/production.sqlite3
12
+ timeout: 5000
@@ -0,0 +1,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
@@ -0,0 +1,5 @@
1
+ config.cache_classes = false
2
+ config.whiny_nils = true
3
+ config.action_controller.consider_all_requests_local = true
4
+ config.action_controller.perform_caching = false
5
+ config.action_view.debug_rjs = true
@@ -0,0 +1,3 @@
1
+ config.cache_classes = true
2
+ config.action_controller.consider_all_requests_local = false
3
+ config.action_controller.perform_caching = true
@@ -0,0 +1,4 @@
1
+ ActionController::Routing::Routes.draw do |map|
2
+ map.connect ':controller/:action/:id.:format'
3
+ map.connect ':controller/:action/:id'
4
+ end
File without changes
@@ -0,0 +1 @@
1
+ 404 Not Found
@@ -0,0 +1 @@
1
+ 500 Internal Server Error
@@ -0,0 +1,7 @@
1
+ require(File.join(File.dirname(__FILE__), 'config', 'boot'))
2
+
3
+ require 'rake'
4
+ require 'rake/testtask'
5
+ require 'rake/rdoctask'
6
+
7
+ require 'tasks/rails'
@@ -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,7 @@
1
+ require(File.join(File.dirname(__FILE__), 'config', 'boot'))
2
+
3
+ require 'rake'
4
+ require 'rake/testtask'
5
+ require 'rake/rdoctask'
6
+
7
+ require 'tasks/rails'
@@ -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 -= [ :active_record, :active_resource, :action_mailer ]
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}",
@@ -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 = $$
@@ -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.1"
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-01}
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.1
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-01 00:00:00 -07:00
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