unicorn 0.4.1 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
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