unicorn-lb-patch 4.3.1.11.g21b8.dirty
Sign up to get free protection for your applications and to get access to all the features.
- data/.CHANGELOG.old +25 -0
- data/.document +29 -0
- data/.gitignore +25 -0
- data/.mailmap +26 -0
- data/.wrongdoc.yml +10 -0
- data/Application_Timeouts +77 -0
- data/CONTRIBUTORS +35 -0
- data/COPYING +674 -0
- data/DESIGN +97 -0
- data/Documentation/.gitignore +5 -0
- data/Documentation/GNUmakefile +30 -0
- data/Documentation/unicorn.1.txt +174 -0
- data/Documentation/unicorn_rails.1.txt +175 -0
- data/FAQ +53 -0
- data/GIT-VERSION-GEN +40 -0
- data/GNUmakefile +294 -0
- data/HACKING +134 -0
- data/ISSUES +36 -0
- data/KNOWN_ISSUES +79 -0
- data/LICENSE +64 -0
- data/Links +56 -0
- data/PHILOSOPHY +145 -0
- data/README +149 -0
- data/Rakefile +97 -0
- data/SIGNALS +114 -0
- data/Sandbox +96 -0
- data/TODO +5 -0
- data/TUNING +98 -0
- data/bin/unicorn +121 -0
- data/bin/unicorn_rails +209 -0
- data/examples/big_app_gc.rb +2 -0
- data/examples/echo.ru +27 -0
- data/examples/git.ru +13 -0
- data/examples/init.sh +74 -0
- data/examples/logger_mp_safe.rb +25 -0
- data/examples/logrotate.conf +29 -0
- data/examples/nginx.conf +156 -0
- data/examples/unicorn.conf.minimal.rb +13 -0
- data/examples/unicorn.conf.rb +94 -0
- data/ext/unicorn_http/CFLAGS +13 -0
- data/ext/unicorn_http/c_util.h +124 -0
- data/ext/unicorn_http/common_field_optimization.h +111 -0
- data/ext/unicorn_http/ext_help.h +86 -0
- data/ext/unicorn_http/extconf.rb +10 -0
- data/ext/unicorn_http/global_variables.h +97 -0
- data/ext/unicorn_http/httpdate.c +82 -0
- data/ext/unicorn_http/unicorn_http.rl +1036 -0
- data/ext/unicorn_http/unicorn_http_common.rl +76 -0
- data/lib/unicorn.rb +107 -0
- data/lib/unicorn/app/exec_cgi.rb +154 -0
- data/lib/unicorn/app/inetd.rb +109 -0
- data/lib/unicorn/app/old_rails.rb +35 -0
- data/lib/unicorn/app/old_rails/static.rb +59 -0
- data/lib/unicorn/cgi_wrapper.rb +147 -0
- data/lib/unicorn/configurator.rb +630 -0
- data/lib/unicorn/const.rb +40 -0
- data/lib/unicorn/http_request.rb +77 -0
- data/lib/unicorn/http_response.rb +45 -0
- data/lib/unicorn/http_server.rb +755 -0
- data/lib/unicorn/launcher.rb +62 -0
- data/lib/unicorn/oob_gc.rb +71 -0
- data/lib/unicorn/preread_input.rb +33 -0
- data/lib/unicorn/socket_helper.rb +208 -0
- data/lib/unicorn/ssl_client.rb +11 -0
- data/lib/unicorn/ssl_configurator.rb +104 -0
- data/lib/unicorn/ssl_server.rb +42 -0
- data/lib/unicorn/stream_input.rb +149 -0
- data/lib/unicorn/tee_input.rb +126 -0
- data/lib/unicorn/tmpio.rb +29 -0
- data/lib/unicorn/util.rb +68 -0
- data/lib/unicorn/worker.rb +88 -0
- data/local.mk.sample +59 -0
- data/script/isolate_for_tests +50 -0
- data/setup.rb +1586 -0
- data/t/.gitignore +5 -0
- data/t/GNUmakefile +82 -0
- data/t/README +42 -0
- data/t/bin/content-md5-put +36 -0
- data/t/bin/sha1sum.rb +17 -0
- data/t/bin/unused_listen +40 -0
- data/t/bin/utee +12 -0
- data/t/broken-app.ru +12 -0
- data/t/detach.ru +11 -0
- data/t/env.ru +3 -0
- data/t/heartbeat-timeout.ru +12 -0
- data/t/listener_names.ru +4 -0
- data/t/my-tap-lib.sh +201 -0
- data/t/oob_gc.ru +21 -0
- data/t/oob_gc_path.ru +21 -0
- data/t/pid.ru +3 -0
- data/t/preread_input.ru +17 -0
- data/t/rack-input-tests.ru +21 -0
- data/t/rails3-app/.gitignore +4 -0
- data/t/rails3-app/Gemfile +26 -0
- data/t/rails3-app/Rakefile +10 -0
- data/t/rails3-app/app/controllers/application_controller.rb +4 -0
- data/t/rails3-app/app/helpers/application_helper.rb +2 -0
- data/t/rails3-app/app/views/layouts/application.html.erb +14 -0
- data/t/rails3-app/config.ru +4 -0
- data/t/rails3-app/config/application.rb +46 -0
- data/t/rails3-app/config/boot.rb +6 -0
- data/t/rails3-app/config/database.yml +22 -0
- data/t/rails3-app/config/environment.rb +5 -0
- data/t/rails3-app/config/environments/development.rb +19 -0
- data/t/rails3-app/config/environments/production.rb +42 -0
- data/t/rails3-app/config/environments/test.rb +32 -0
- data/t/rails3-app/config/initializers/backtrace_silencers.rb +7 -0
- data/t/rails3-app/config/initializers/inflections.rb +10 -0
- data/t/rails3-app/config/initializers/mime_types.rb +5 -0
- data/t/rails3-app/config/initializers/secret_token.rb +7 -0
- data/t/rails3-app/config/initializers/session_store.rb +8 -0
- data/t/rails3-app/config/locales/en.yml +5 -0
- data/t/rails3-app/config/routes.rb +58 -0
- data/t/rails3-app/db/seeds.rb +7 -0
- data/t/rails3-app/doc/README_FOR_APP +2 -0
- data/t/rails3-app/lib/tasks/.gitkeep +0 -0
- data/t/rails3-app/public/404.html +1 -0
- data/t/rails3-app/public/500.html +1 -0
- data/t/rails3-app/public/x.txt +1 -0
- data/t/rails3-app/script/rails +9 -0
- data/t/rails3-app/test/performance/browsing_test.rb +9 -0
- data/t/rails3-app/test/test_helper.rb +13 -0
- data/t/rails3-app/vendor/plugins/.gitkeep +0 -0
- data/t/sslgen.sh +71 -0
- data/t/t0000-http-basic.sh +50 -0
- data/t/t0001-reload-bad-config.sh +53 -0
- data/t/t0002-config-conflict.sh +49 -0
- data/t/t0002-parser-error.sh +94 -0
- data/t/t0003-working_directory.sh +51 -0
- data/t/t0004-heartbeat-timeout.sh +69 -0
- data/t/t0004-working_directory_broken.sh +24 -0
- data/t/t0005-working_directory_app.rb.sh +37 -0
- data/t/t0006-reopen-logs.sh +83 -0
- data/t/t0006.ru +13 -0
- data/t/t0007-working_directory_no_embed_cli.sh +44 -0
- data/t/t0008-back_out_of_upgrade.sh +110 -0
- data/t/t0009-broken-app.sh +56 -0
- data/t/t0009-winch_ttin.sh +59 -0
- data/t/t0010-reap-logging.sh +55 -0
- data/t/t0011-active-unix-socket.sh +79 -0
- data/t/t0012-reload-empty-config.sh +85 -0
- data/t/t0013-rewindable-input-false.sh +24 -0
- data/t/t0013.ru +12 -0
- data/t/t0014-rewindable-input-true.sh +24 -0
- data/t/t0014.ru +12 -0
- data/t/t0015-configurator-internals.sh +25 -0
- data/t/t0016-trust-x-forwarded-false.sh +30 -0
- data/t/t0017-trust-x-forwarded-true.sh +30 -0
- data/t/t0018-write-on-close.sh +23 -0
- data/t/t0019-max_header_len.sh +49 -0
- data/t/t0020-at_exit-handler.sh +49 -0
- data/t/t0021-process_detach.sh +29 -0
- data/t/t0022-listener-names-preload_app.sh +32 -0
- data/t/t0100-rack-input-tests.sh +124 -0
- data/t/t0116-client_body_buffer_size.sh +80 -0
- data/t/t0116.ru +16 -0
- data/t/t0300-rails3-basic.sh +28 -0
- data/t/t0301-rails3-missing-config-ru.sh +33 -0
- data/t/t0302-rails3-alt-working_directory.sh +32 -0
- data/t/t0303-rails3-alt-working_directory_config.ru.sh +56 -0
- data/t/t0304-rails3-alt-working_directory_no_embed_cli.sh +52 -0
- data/t/t0600-https-server-basic.sh +48 -0
- data/t/t9000-preread-input.sh +48 -0
- data/t/t9001-oob_gc.sh +47 -0
- data/t/t9002-oob_gc-path.sh +75 -0
- data/t/test-lib.sh +113 -0
- data/t/test-rails3.sh +27 -0
- data/t/write-on-close.ru +11 -0
- data/test/aggregate.rb +15 -0
- data/test/benchmark/README +50 -0
- data/test/benchmark/dd.ru +18 -0
- data/test/benchmark/stack.ru +8 -0
- data/test/exec/README +5 -0
- data/test/exec/test_exec.rb +1055 -0
- data/test/rails/app-1.2.3/.gitignore +2 -0
- data/test/rails/app-1.2.3/Rakefile +7 -0
- data/test/rails/app-1.2.3/app/controllers/application.rb +6 -0
- data/test/rails/app-1.2.3/app/controllers/foo_controller.rb +36 -0
- data/test/rails/app-1.2.3/app/helpers/application_helper.rb +4 -0
- data/test/rails/app-1.2.3/config/boot.rb +11 -0
- data/test/rails/app-1.2.3/config/database.yml +12 -0
- data/test/rails/app-1.2.3/config/environment.rb +13 -0
- data/test/rails/app-1.2.3/config/environments/development.rb +9 -0
- data/test/rails/app-1.2.3/config/environments/production.rb +5 -0
- data/test/rails/app-1.2.3/config/routes.rb +6 -0
- data/test/rails/app-1.2.3/db/.gitignore +0 -0
- data/test/rails/app-1.2.3/public/404.html +1 -0
- data/test/rails/app-1.2.3/public/500.html +1 -0
- data/test/rails/app-2.0.2/.gitignore +2 -0
- data/test/rails/app-2.0.2/Rakefile +7 -0
- data/test/rails/app-2.0.2/app/controllers/application.rb +4 -0
- data/test/rails/app-2.0.2/app/controllers/foo_controller.rb +36 -0
- data/test/rails/app-2.0.2/app/helpers/application_helper.rb +4 -0
- data/test/rails/app-2.0.2/config/boot.rb +11 -0
- data/test/rails/app-2.0.2/config/database.yml +12 -0
- data/test/rails/app-2.0.2/config/environment.rb +17 -0
- data/test/rails/app-2.0.2/config/environments/development.rb +8 -0
- data/test/rails/app-2.0.2/config/environments/production.rb +5 -0
- data/test/rails/app-2.0.2/config/routes.rb +6 -0
- data/test/rails/app-2.0.2/db/.gitignore +0 -0
- data/test/rails/app-2.0.2/public/404.html +1 -0
- data/test/rails/app-2.0.2/public/500.html +1 -0
- data/test/rails/app-2.1.2/.gitignore +2 -0
- data/test/rails/app-2.1.2/Rakefile +7 -0
- data/test/rails/app-2.1.2/app/controllers/application.rb +4 -0
- data/test/rails/app-2.1.2/app/controllers/foo_controller.rb +36 -0
- data/test/rails/app-2.1.2/app/helpers/application_helper.rb +4 -0
- data/test/rails/app-2.1.2/config/boot.rb +111 -0
- data/test/rails/app-2.1.2/config/database.yml +12 -0
- data/test/rails/app-2.1.2/config/environment.rb +17 -0
- data/test/rails/app-2.1.2/config/environments/development.rb +7 -0
- data/test/rails/app-2.1.2/config/environments/production.rb +5 -0
- data/test/rails/app-2.1.2/config/routes.rb +6 -0
- data/test/rails/app-2.1.2/db/.gitignore +0 -0
- data/test/rails/app-2.1.2/public/404.html +1 -0
- data/test/rails/app-2.1.2/public/500.html +1 -0
- data/test/rails/app-2.2.2/.gitignore +2 -0
- data/test/rails/app-2.2.2/Rakefile +7 -0
- data/test/rails/app-2.2.2/app/controllers/application.rb +4 -0
- data/test/rails/app-2.2.2/app/controllers/foo_controller.rb +36 -0
- data/test/rails/app-2.2.2/app/helpers/application_helper.rb +4 -0
- data/test/rails/app-2.2.2/config/boot.rb +111 -0
- data/test/rails/app-2.2.2/config/database.yml +12 -0
- data/test/rails/app-2.2.2/config/environment.rb +17 -0
- data/test/rails/app-2.2.2/config/environments/development.rb +7 -0
- data/test/rails/app-2.2.2/config/environments/production.rb +5 -0
- data/test/rails/app-2.2.2/config/routes.rb +6 -0
- data/test/rails/app-2.2.2/db/.gitignore +0 -0
- data/test/rails/app-2.2.2/public/404.html +1 -0
- data/test/rails/app-2.2.2/public/500.html +1 -0
- data/test/rails/test_rails.rb +287 -0
- data/test/test_helper.rb +300 -0
- data/test/unit/test_configurator.rb +158 -0
- data/test/unit/test_droplet.rb +28 -0
- data/test/unit/test_http_parser.rb +860 -0
- data/test/unit/test_http_parser_ng.rb +716 -0
- data/test/unit/test_http_parser_xftrust.rb +38 -0
- data/test/unit/test_request.rb +197 -0
- data/test/unit/test_response.rb +99 -0
- data/test/unit/test_server.rb +289 -0
- data/test/unit/test_signals.rb +207 -0
- data/test/unit/test_sni_hostnames.rb +47 -0
- data/test/unit/test_socket_helper.rb +192 -0
- data/test/unit/test_stream_input.rb +204 -0
- data/test/unit/test_tee_input.rb +296 -0
- data/test/unit/test_upload.rb +306 -0
- data/test/unit/test_util.rb +100 -0
- data/unicorn-lb-patch.gemspec +27 -0
- metadata +561 -0
@@ -0,0 +1,62 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
|
3
|
+
# :enddoc:
|
4
|
+
$stdout.sync = $stderr.sync = true
|
5
|
+
$stdin.binmode
|
6
|
+
$stdout.binmode
|
7
|
+
$stderr.binmode
|
8
|
+
|
9
|
+
require 'unicorn'
|
10
|
+
|
11
|
+
module Unicorn::Launcher
|
12
|
+
|
13
|
+
# We don't do a lot of standard daemonization stuff:
|
14
|
+
# * umask is whatever was set by the parent process at startup
|
15
|
+
# and can be set in config.ru and config_file, so making it
|
16
|
+
# 0000 and potentially exposing sensitive log data can be bad
|
17
|
+
# policy.
|
18
|
+
# * don't bother to chdir("/") here since unicorn is designed to
|
19
|
+
# run inside APP_ROOT. Unicorn will also re-chdir() to
|
20
|
+
# the directory it was started in when being re-executed
|
21
|
+
# to pickup code changes if the original deployment directory
|
22
|
+
# is a symlink or otherwise got replaced.
|
23
|
+
def self.daemonize!(options)
|
24
|
+
cfg = Unicorn::Configurator
|
25
|
+
$stdin.reopen("/dev/null")
|
26
|
+
|
27
|
+
# We only start a new process group if we're not being reexecuted
|
28
|
+
# and inheriting file descriptors from our parent
|
29
|
+
unless ENV['UNICORN_FD']
|
30
|
+
# grandparent - reads pipe, exits when master is ready
|
31
|
+
# \_ parent - exits immediately ASAP
|
32
|
+
# \_ unicorn master - writes to pipe when ready
|
33
|
+
|
34
|
+
rd, wr = IO.pipe
|
35
|
+
grandparent = $$
|
36
|
+
if fork
|
37
|
+
wr.close # grandparent does not write
|
38
|
+
else
|
39
|
+
rd.close # unicorn master does not read
|
40
|
+
Process.setsid
|
41
|
+
exit if fork # parent dies now
|
42
|
+
end
|
43
|
+
|
44
|
+
if grandparent == $$
|
45
|
+
# this will block until HttpServer#join runs (or it dies)
|
46
|
+
master_pid = (rd.readpartial(16) rescue nil).to_i
|
47
|
+
unless master_pid > 1
|
48
|
+
warn "master failed to start, check stderr log for details"
|
49
|
+
exit!(1)
|
50
|
+
end
|
51
|
+
exit 0
|
52
|
+
else # unicorn master process
|
53
|
+
options[:ready_pipe] = wr
|
54
|
+
end
|
55
|
+
end
|
56
|
+
# $stderr/$stderr can/will be redirected separately in the Unicorn config
|
57
|
+
cfg::DEFAULTS[:stderr_path] ||= "/dev/null"
|
58
|
+
cfg::DEFAULTS[:stdout_path] ||= "/dev/null"
|
59
|
+
cfg::RACKUP[:daemonized] = true
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
|
3
|
+
# Runs GC after requests, after closing the client socket and
|
4
|
+
# before attempting to accept more connections.
|
5
|
+
#
|
6
|
+
# This shouldn't hurt overall performance as long as the server cluster
|
7
|
+
# is at <50% CPU capacity, and improves the performance of most memory
|
8
|
+
# intensive requests. This serves to improve _client-visible_
|
9
|
+
# performance (possibly at the cost of overall performance).
|
10
|
+
#
|
11
|
+
# Increasing the number of +worker_processes+ may be necessary to
|
12
|
+
# improve average client response times because some of your workers
|
13
|
+
# will be busy doing GC and unable to service clients. Think of
|
14
|
+
# using more workers with this module as a poor man's concurrent GC.
|
15
|
+
#
|
16
|
+
# We'll call GC after each request is been written out to the socket, so
|
17
|
+
# the client never sees the extra GC hit it.
|
18
|
+
#
|
19
|
+
# This middleware is _only_ effective for applications that use a lot
|
20
|
+
# of memory, and will hurt simpler apps/endpoints that can process
|
21
|
+
# multiple requests before incurring GC.
|
22
|
+
#
|
23
|
+
# This middleware is only designed to work with unicorn, as it harms
|
24
|
+
# performance with keepalive-enabled servers.
|
25
|
+
#
|
26
|
+
# Example (in config.ru):
|
27
|
+
#
|
28
|
+
# require 'unicorn/oob_gc'
|
29
|
+
#
|
30
|
+
# # GC ever two requests that hit /expensive/foo or /more_expensive/foo
|
31
|
+
# # in your app. By default, this will GC once every 5 requests
|
32
|
+
# # for all endpoints in your app
|
33
|
+
# use Unicorn::OobGC, 2, %r{\A/(?:expensive/foo|more_expensive/foo)}
|
34
|
+
#
|
35
|
+
# Feedback from users of early implementations of this module:
|
36
|
+
# * http://comments.gmane.org/gmane.comp.lang.ruby.unicorn.general/486
|
37
|
+
# * http://article.gmane.org/gmane.comp.lang.ruby.unicorn.general/596
|
38
|
+
module Unicorn::OobGC
|
39
|
+
|
40
|
+
# this pretends to be Rack middleware because it used to be
|
41
|
+
# But we need to hook into unicorn internals so we need to close
|
42
|
+
# the socket before clearing the request env.
|
43
|
+
#
|
44
|
+
# +interval+ is the number of requests matching the +path+ regular
|
45
|
+
# expression before invoking GC.
|
46
|
+
def self.new(app, interval = 5, path = %r{\A/})
|
47
|
+
@@nr = interval
|
48
|
+
self.const_set :OOBGC_PATH, path
|
49
|
+
self.const_set :OOBGC_INTERVAL, interval
|
50
|
+
ObjectSpace.each_object(Unicorn::HttpServer) do |s|
|
51
|
+
s.extend(self)
|
52
|
+
self.const_set :OOBGC_ENV, s.instance_variable_get(:@request).env
|
53
|
+
end
|
54
|
+
app # pretend to be Rack middleware since it was in the past
|
55
|
+
end
|
56
|
+
|
57
|
+
#:stopdoc:
|
58
|
+
PATH_INFO = "PATH_INFO"
|
59
|
+
def process_client(client)
|
60
|
+
super(client) # Unicorn::HttpServer#process_client
|
61
|
+
if OOBGC_PATH =~ OOBGC_ENV[PATH_INFO] && ((@@nr -= 1) <= 0)
|
62
|
+
@@nr = OOBGC_INTERVAL
|
63
|
+
OOBGC_ENV.clear
|
64
|
+
disabled = GC.enable
|
65
|
+
GC.start
|
66
|
+
GC.disable if disabled
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# :startdoc:
|
71
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
|
3
|
+
module Unicorn
|
4
|
+
# This middleware is used to ensure input is buffered to memory
|
5
|
+
# or disk (depending on size) before the application is dispatched
|
6
|
+
# by entirely consuming it (from TeeInput) beforehand.
|
7
|
+
#
|
8
|
+
# Usage (in config.ru):
|
9
|
+
#
|
10
|
+
# require 'unicorn/preread_input'
|
11
|
+
# if defined?(Unicorn)
|
12
|
+
# use Unicorn::PrereadInput
|
13
|
+
# end
|
14
|
+
# run YourApp.new
|
15
|
+
class PrereadInput
|
16
|
+
|
17
|
+
# :stopdoc:
|
18
|
+
def initialize(app)
|
19
|
+
@app = app
|
20
|
+
end
|
21
|
+
|
22
|
+
def call(env)
|
23
|
+
buf = ""
|
24
|
+
input = env["rack.input"]
|
25
|
+
if input.respond_to?(:rewind)
|
26
|
+
true while input.read(16384, buf)
|
27
|
+
input.rewind
|
28
|
+
end
|
29
|
+
@app.call(env)
|
30
|
+
end
|
31
|
+
# :startdoc:
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,208 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
# :enddoc:
|
3
|
+
require 'socket'
|
4
|
+
|
5
|
+
module Unicorn
|
6
|
+
module SocketHelper
|
7
|
+
# :stopdoc:
|
8
|
+
include Socket::Constants
|
9
|
+
|
10
|
+
# prevents IO objects in here from being GC-ed
|
11
|
+
IO_PURGATORY = []
|
12
|
+
|
13
|
+
# internal interface, only used by Rainbows!/Zbatery
|
14
|
+
DEFAULTS = {
|
15
|
+
# The semantics for TCP_DEFER_ACCEPT changed in Linux 2.6.32+
|
16
|
+
# with commit d1b99ba41d6c5aa1ed2fc634323449dd656899e9
|
17
|
+
# This change shouldn't affect Unicorn users behind nginx (a
|
18
|
+
# value of 1 remains an optimization), but Rainbows! users may
|
19
|
+
# want to use a higher value on Linux 2.6.32+ to protect against
|
20
|
+
# denial-of-service attacks
|
21
|
+
:tcp_defer_accept => 1,
|
22
|
+
|
23
|
+
# FreeBSD, we need to override this to 'dataready' if we
|
24
|
+
# eventually get HTTPS support
|
25
|
+
:accept_filter => 'httpready',
|
26
|
+
|
27
|
+
# same default value as Mongrel
|
28
|
+
:backlog => 1024,
|
29
|
+
|
30
|
+
# favor latency over bandwidth savings
|
31
|
+
:tcp_nopush => false,
|
32
|
+
:tcp_nodelay => true,
|
33
|
+
}
|
34
|
+
#:startdoc:
|
35
|
+
|
36
|
+
# configure platform-specific options (only tested on Linux 2.6 so far)
|
37
|
+
case RUBY_PLATFORM
|
38
|
+
when /linux/
|
39
|
+
# from /usr/include/linux/tcp.h
|
40
|
+
TCP_DEFER_ACCEPT = 9 unless defined?(TCP_DEFER_ACCEPT)
|
41
|
+
|
42
|
+
# do not send out partial frames (Linux)
|
43
|
+
TCP_CORK = 3 unless defined?(TCP_CORK)
|
44
|
+
when /freebsd/
|
45
|
+
# do not send out partial frames (FreeBSD)
|
46
|
+
TCP_NOPUSH = 4 unless defined?(TCP_NOPUSH)
|
47
|
+
|
48
|
+
def accf_arg(af_name)
|
49
|
+
[ af_name, nil ].pack('a16a240')
|
50
|
+
end if defined?(SO_ACCEPTFILTER)
|
51
|
+
end
|
52
|
+
|
53
|
+
def set_tcp_sockopt(sock, opt)
|
54
|
+
# just in case, even LANs can break sometimes. Linux sysadmins
|
55
|
+
# can lower net.ipv4.tcp_keepalive_* sysctl knobs to very low values.
|
56
|
+
sock.setsockopt(SOL_SOCKET, SO_KEEPALIVE, 1) if defined?(SO_KEEPALIVE)
|
57
|
+
|
58
|
+
if defined?(TCP_NODELAY)
|
59
|
+
val = opt[:tcp_nodelay]
|
60
|
+
val = DEFAULTS[:tcp_nodelay] if nil == val
|
61
|
+
sock.setsockopt(IPPROTO_TCP, TCP_NODELAY, val ? 1 : 0)
|
62
|
+
end
|
63
|
+
|
64
|
+
val = opt[:tcp_nopush]
|
65
|
+
val = DEFAULTS[:tcp_nopush] if nil == val
|
66
|
+
val = val ? 1 : 0
|
67
|
+
if defined?(TCP_CORK) # Linux
|
68
|
+
sock.setsockopt(IPPROTO_TCP, TCP_CORK, val)
|
69
|
+
elsif defined?(TCP_NOPUSH) # TCP_NOPUSH is untested (FreeBSD)
|
70
|
+
sock.setsockopt(IPPROTO_TCP, TCP_NOPUSH, val)
|
71
|
+
end
|
72
|
+
|
73
|
+
# No good reason to ever have deferred accepts off
|
74
|
+
# (except maybe benchmarking)
|
75
|
+
if defined?(TCP_DEFER_ACCEPT)
|
76
|
+
# this differs from nginx, since nginx doesn't allow us to
|
77
|
+
# configure the the timeout...
|
78
|
+
seconds = opt[:tcp_defer_accept]
|
79
|
+
seconds = DEFAULTS[:tcp_defer_accept] if [true,nil].include?(seconds)
|
80
|
+
seconds = 0 unless seconds # nil/false means disable this
|
81
|
+
sock.setsockopt(SOL_TCP, TCP_DEFER_ACCEPT, seconds)
|
82
|
+
elsif respond_to?(:accf_arg)
|
83
|
+
name = opt[:accept_filter]
|
84
|
+
name = DEFAULTS[:accept_filter] if nil == name
|
85
|
+
begin
|
86
|
+
sock.setsockopt(SOL_SOCKET, SO_ACCEPTFILTER, accf_arg(name))
|
87
|
+
rescue => e
|
88
|
+
logger.error("#{sock_name(sock)} " \
|
89
|
+
"failed to set accept_filter=#{name} (#{e.inspect})")
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def set_server_sockopt(sock, opt)
|
95
|
+
opt = DEFAULTS.merge(opt || {})
|
96
|
+
|
97
|
+
TCPSocket === sock and set_tcp_sockopt(sock, opt)
|
98
|
+
|
99
|
+
if opt[:rcvbuf] || opt[:sndbuf]
|
100
|
+
log_buffer_sizes(sock, "before: ")
|
101
|
+
sock.setsockopt(SOL_SOCKET, SO_RCVBUF, opt[:rcvbuf]) if opt[:rcvbuf]
|
102
|
+
sock.setsockopt(SOL_SOCKET, SO_SNDBUF, opt[:sndbuf]) if opt[:sndbuf]
|
103
|
+
log_buffer_sizes(sock, " after: ")
|
104
|
+
end
|
105
|
+
sock.listen(opt[:backlog])
|
106
|
+
rescue => e
|
107
|
+
Unicorn.log_error(logger, "#{sock_name(sock)} #{opt.inspect}", e)
|
108
|
+
end
|
109
|
+
|
110
|
+
def log_buffer_sizes(sock, pfx = '')
|
111
|
+
rcvbuf = sock.getsockopt(SOL_SOCKET, SO_RCVBUF).unpack('i')
|
112
|
+
sndbuf = sock.getsockopt(SOL_SOCKET, SO_SNDBUF).unpack('i')
|
113
|
+
logger.info "#{pfx}#{sock_name(sock)} rcvbuf=#{rcvbuf} sndbuf=#{sndbuf}"
|
114
|
+
end
|
115
|
+
|
116
|
+
# creates a new server, socket. address may be a HOST:PORT or
|
117
|
+
# an absolute path to a UNIX socket. address can even be a Socket
|
118
|
+
# object in which case it is immediately returned
|
119
|
+
def bind_listen(address = '0.0.0.0:8080', opt = {})
|
120
|
+
return address unless String === address
|
121
|
+
|
122
|
+
sock = if address[0] == ?/
|
123
|
+
if File.exist?(address)
|
124
|
+
if File.socket?(address)
|
125
|
+
begin
|
126
|
+
UNIXSocket.new(address).close
|
127
|
+
# fall through, try to bind(2) and fail with EADDRINUSE
|
128
|
+
# (or succeed from a small race condition we can't sanely avoid).
|
129
|
+
rescue Errno::ECONNREFUSED
|
130
|
+
logger.info "unlinking existing socket=#{address}"
|
131
|
+
File.unlink(address)
|
132
|
+
end
|
133
|
+
else
|
134
|
+
raise ArgumentError,
|
135
|
+
"socket=#{address} specified but it is not a socket!"
|
136
|
+
end
|
137
|
+
end
|
138
|
+
old_umask = File.umask(opt[:umask] || 0)
|
139
|
+
begin
|
140
|
+
Kgio::UNIXServer.new(address)
|
141
|
+
ensure
|
142
|
+
File.umask(old_umask)
|
143
|
+
end
|
144
|
+
elsif /\A\[([a-fA-F0-9:]+)\]:(\d+)\z/ =~ address
|
145
|
+
new_ipv6_server($1, $2.to_i, opt)
|
146
|
+
elsif /\A(\d+\.\d+\.\d+\.\d+):(\d+)\z/ =~ address
|
147
|
+
Kgio::TCPServer.new($1, $2.to_i)
|
148
|
+
else
|
149
|
+
raise ArgumentError, "Don't know how to bind: #{address}"
|
150
|
+
end
|
151
|
+
set_server_sockopt(sock, opt)
|
152
|
+
sock
|
153
|
+
end
|
154
|
+
|
155
|
+
def new_ipv6_server(addr, port, opt)
|
156
|
+
opt.key?(:ipv6only) or return Kgio::TCPServer.new(addr, port)
|
157
|
+
defined?(IPV6_V6ONLY) or
|
158
|
+
abort "Socket::IPV6_V6ONLY not defined, upgrade Ruby and/or your OS"
|
159
|
+
sock = Socket.new(AF_INET6, SOCK_STREAM, 0)
|
160
|
+
sock.setsockopt(IPPROTO_IPV6, IPV6_V6ONLY, opt[:ipv6only] ? 1 : 0)
|
161
|
+
sock.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
|
162
|
+
sock.bind(Socket.pack_sockaddr_in(port, addr))
|
163
|
+
IO_PURGATORY << sock
|
164
|
+
Kgio::TCPServer.for_fd(sock.fileno)
|
165
|
+
end
|
166
|
+
|
167
|
+
# returns rfc2732-style (e.g. "[::1]:666") addresses for IPv6
|
168
|
+
def tcp_name(sock)
|
169
|
+
port, addr = Socket.unpack_sockaddr_in(sock.getsockname)
|
170
|
+
/:/ =~ addr ? "[#{addr}]:#{port}" : "#{addr}:#{port}"
|
171
|
+
end
|
172
|
+
module_function :tcp_name
|
173
|
+
|
174
|
+
# Returns the configuration name of a socket as a string. sock may
|
175
|
+
# be a string value, in which case it is returned as-is
|
176
|
+
# Warning: TCP sockets may not always return the name given to it.
|
177
|
+
def sock_name(sock)
|
178
|
+
case sock
|
179
|
+
when String then sock
|
180
|
+
when UNIXServer
|
181
|
+
Socket.unpack_sockaddr_un(sock.getsockname)
|
182
|
+
when TCPServer
|
183
|
+
tcp_name(sock)
|
184
|
+
when Socket
|
185
|
+
begin
|
186
|
+
tcp_name(sock)
|
187
|
+
rescue ArgumentError
|
188
|
+
Socket.unpack_sockaddr_un(sock.getsockname)
|
189
|
+
end
|
190
|
+
else
|
191
|
+
raise ArgumentError, "Unhandled class #{sock.class}: #{sock.inspect}"
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
module_function :sock_name
|
196
|
+
|
197
|
+
# casts a given Socket to be a TCPServer or UNIXServer
|
198
|
+
def server_cast(sock)
|
199
|
+
begin
|
200
|
+
Socket.unpack_sockaddr_in(sock.getsockname)
|
201
|
+
Kgio::TCPServer.for_fd(sock.fileno)
|
202
|
+
rescue ArgumentError
|
203
|
+
Kgio::UNIXServer.for_fd(sock.fileno)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
end # module SocketHelper
|
208
|
+
end # module Unicorn
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
# :stopdoc:
|
3
|
+
class Unicorn::SSLClient < Kgio::SSL
|
4
|
+
alias write kgio_write
|
5
|
+
alias close kgio_close
|
6
|
+
|
7
|
+
# this is no-op for now, to be fixed in kgio-monkey if people care
|
8
|
+
# about SSL support...
|
9
|
+
def shutdown(how = nil)
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
# :stopdoc:
|
3
|
+
# This module is included in Unicorn::Configurator
|
4
|
+
# :startdoc:
|
5
|
+
#
|
6
|
+
module Unicorn::SSLConfigurator
|
7
|
+
def ssl(&block)
|
8
|
+
ssl_require!
|
9
|
+
before = @set[:listeners].dup
|
10
|
+
opts = @set[:ssl_opts] = {}
|
11
|
+
yield
|
12
|
+
(@set[:listeners] - before).each do |address|
|
13
|
+
(@set[:listener_opts][address] ||= {})[:ssl_opts] = opts
|
14
|
+
end
|
15
|
+
ensure
|
16
|
+
@set.delete(:ssl_opts)
|
17
|
+
end
|
18
|
+
|
19
|
+
def ssl_certificate(file)
|
20
|
+
ssl_set(:ssl_certificate, file)
|
21
|
+
end
|
22
|
+
|
23
|
+
def ssl_certificate_key(file)
|
24
|
+
ssl_set(:ssl_certificate_key, file)
|
25
|
+
end
|
26
|
+
|
27
|
+
def ssl_client_certificate(file)
|
28
|
+
ssl_set(:ssl_client_certificate, file)
|
29
|
+
end
|
30
|
+
|
31
|
+
def ssl_dhparam(file)
|
32
|
+
ssl_set(:ssl_dhparam, file)
|
33
|
+
end
|
34
|
+
|
35
|
+
def ssl_ciphers(openssl_cipherlist_spec)
|
36
|
+
ssl_set(:ssl_ciphers, openssl_cipherlist_spec)
|
37
|
+
end
|
38
|
+
|
39
|
+
def ssl_crl(file)
|
40
|
+
ssl_set(:ssl_crl, file)
|
41
|
+
end
|
42
|
+
|
43
|
+
def ssl_prefer_server_ciphers(bool)
|
44
|
+
ssl_set(:ssl_prefer_server_ciphers, check_bool(bool))
|
45
|
+
end
|
46
|
+
|
47
|
+
def ssl_protocols(list)
|
48
|
+
ssl_set(:ssl_protocols, list)
|
49
|
+
end
|
50
|
+
|
51
|
+
def ssl_verify_client(on_off_optional)
|
52
|
+
ssl_set(:ssl_verify_client, on_off_optional)
|
53
|
+
end
|
54
|
+
|
55
|
+
def ssl_session_timeout(seconds)
|
56
|
+
ssl_set(:ssl_session_timeout, seconds)
|
57
|
+
end
|
58
|
+
|
59
|
+
def ssl_verify_depth(depth)
|
60
|
+
ssl_set(:ssl_verify_depth, depth)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Allows specifying an engine for OpenSSL to use. We have not been
|
64
|
+
# able to successfully test this feature due to a lack of hardware,
|
65
|
+
# Reports of success or patches to mongrel-unicorn@rubyforge.org is
|
66
|
+
# greatly appreciated.
|
67
|
+
def ssl_engine(engine)
|
68
|
+
ssl_warn_global(:ssl_engine)
|
69
|
+
ssl_require!
|
70
|
+
OpenSSL::Engine.load
|
71
|
+
OpenSSL::Engine.by_id(engine)
|
72
|
+
@set[:ssl_engine] = engine
|
73
|
+
end
|
74
|
+
|
75
|
+
def ssl_compression(bool)
|
76
|
+
# OpenSSL uses the SSL_OP_NO_COMPRESSION flag, Flipper follows suit
|
77
|
+
# with :ssl_no_compression, but we negate it to avoid exposing double
|
78
|
+
# negatives to the user.
|
79
|
+
ssl_set(:ssl_no_compression, check_bool(:ssl_compression, ! bool))
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def ssl_warn_global(func) # :nodoc:
|
85
|
+
Hash === @set[:ssl_opts] or return
|
86
|
+
warn("`#{func}' affects all SSL contexts in this process, " \
|
87
|
+
"not just this block")
|
88
|
+
end
|
89
|
+
|
90
|
+
def ssl_set(key, value) # :nodoc:
|
91
|
+
cur = @set[:ssl_opts]
|
92
|
+
Hash === cur or
|
93
|
+
raise ArgumentError, "#{key} must be called inside an `ssl' block"
|
94
|
+
cur[key] = value
|
95
|
+
end
|
96
|
+
|
97
|
+
def ssl_require! # :nodoc:
|
98
|
+
require "flipper"
|
99
|
+
require "unicorn/ssl_client"
|
100
|
+
rescue LoadError
|
101
|
+
warn "install 'kgio-monkey' for SSL support"
|
102
|
+
raise
|
103
|
+
end
|
104
|
+
end
|