boourns-unicorn 4.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.CHANGELOG.old +25 -0
- data/.document +29 -0
- data/.gitignore +24 -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 +267 -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 +83 -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 +69 -0
- data/lib/unicorn/worker.rb +88 -0
- data/local.mk.sample +59 -0
- data/script/isolate_for_tests +32 -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/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/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/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 +1041 -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 +99 -0
- data/unicorn.gemspec +44 -0
- metadata +333 -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 => nil,
|
|
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
|
+
unless val.nil?
|
|
66
|
+
if defined?(TCP_CORK) # Linux
|
|
67
|
+
sock.setsockopt(IPPROTO_TCP, TCP_CORK, val)
|
|
68
|
+
elsif defined?(TCP_NOPUSH) # TCP_NOPUSH is lightly tested (FreeBSD)
|
|
69
|
+
sock.setsockopt(IPPROTO_TCP, TCP_NOPUSH, val)
|
|
70
|
+
end
|
|
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
|