unicorn 4.9.0 → 5.0.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.
- checksums.yaml +4 -4
- data/.gitattributes +5 -0
- data/.manifest +3 -10
- data/Application_Timeouts +3 -3
- data/DESIGN +2 -4
- data/Documentation/unicorn.1.txt +8 -5
- data/Documentation/unicorn_rails.1.txt +2 -2
- data/FAQ +17 -8
- data/GIT-VERSION-FILE +1 -1
- data/GIT-VERSION-GEN +1 -1
- data/GNUmakefile +6 -1
- data/ISSUES +20 -28
- data/KNOWN_ISSUES +9 -9
- data/LATEST +28 -29
- data/Links +14 -17
- data/NEWS +159 -0
- data/PHILOSOPHY +0 -6
- data/README +22 -17
- data/SIGNALS +1 -1
- data/Sandbox +4 -4
- data/TUNING +11 -8
- data/bin/unicorn +1 -1
- data/bin/unicorn_rails +1 -1
- data/examples/nginx.conf +10 -11
- data/examples/unicorn.conf.rb +1 -4
- data/examples/unicorn.socket +11 -0
- data/examples/unicorn@.service +26 -0
- data/ext/unicorn_http/extconf.rb +1 -0
- data/ext/unicorn_http/httpdate.c +1 -1
- data/ext/unicorn_http/unicorn_http.c +267 -334
- data/ext/unicorn_http/unicorn_http.rl +89 -156
- data/lib/unicorn/configurator.rb +17 -31
- data/lib/unicorn/const.rb +2 -25
- data/lib/unicorn/http_request.rb +22 -33
- data/lib/unicorn/http_response.rb +13 -31
- data/lib/unicorn/http_server.rb +129 -122
- data/lib/unicorn/socket_helper.rb +36 -72
- data/lib/unicorn/stream_input.rb +3 -3
- data/lib/unicorn/tmpio.rb +0 -5
- data/lib/unicorn/util.rb +2 -1
- data/lib/unicorn/version.rb +1 -1
- data/lib/unicorn/worker.rb +3 -15
- data/lib/unicorn.rb +10 -18
- data/man/man1/unicorn.1 +7 -5
- data/man/man1/unicorn_rails.1 +2 -2
- data/t/hijack.ru +2 -1
- data/t/t0200-rack-hijack.sh +5 -2
- data/test/exec/test_exec.rb +52 -0
- data/test/test_helper.rb +3 -2
- data/test/unit/test_http_parser_ng.rb +16 -114
- data/test/unit/test_response.rb +28 -16
- data/test/unit/test_socket_helper.rb +1 -1
- data/unicorn.gemspec +10 -1
- metadata +13 -23
- data/examples/git.ru +0 -13
- data/lib/unicorn/app/exec_cgi.rb +0 -154
- data/lib/unicorn/app/inetd.rb +0 -109
- data/lib/unicorn/ssl_client.rb +0 -11
- data/lib/unicorn/ssl_configurator.rb +0 -104
- data/lib/unicorn/ssl_server.rb +0 -42
- data/t/t0016-trust-x-forwarded-false.sh +0 -30
- data/t/t0017-trust-x-forwarded-true.sh +0 -30
- data/test/unit/test_http_parser_xftrust.rb +0 -38
- data/test/unit/test_sni_hostnames.rb +0 -47
data/test/unit/test_response.rb
CHANGED
|
@@ -33,12 +33,20 @@ class ResponseTest < Test::Unit::TestCase
|
|
|
33
33
|
assert out.length > 0, "output didn't have data"
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
+
# ref: <CAO47=rJa=zRcLn_Xm4v2cHPr6c0UswaFC_omYFEH+baSxHOWKQ@mail.gmail.com>
|
|
37
|
+
def test_response_header_broken_nil
|
|
38
|
+
out = StringIO.new
|
|
39
|
+
http_response_write(out, 200, {"Nil" => nil}, %w(hysterical raisin))
|
|
40
|
+
assert ! out.closed?
|
|
41
|
+
|
|
42
|
+
assert_match %r{^Nil: \r\n}sm, out.string, 'nil accepted'
|
|
43
|
+
end
|
|
44
|
+
|
|
36
45
|
def test_response_string_status
|
|
37
46
|
out = StringIO.new
|
|
38
47
|
http_response_write(out,'200', {}, [])
|
|
39
48
|
assert ! out.closed?
|
|
40
49
|
assert out.length > 0, "output didn't have data"
|
|
41
|
-
assert_equal 1, out.string.split(/\r\n/).grep(/^Status: 200 OK/).size
|
|
42
50
|
end
|
|
43
51
|
|
|
44
52
|
def test_response_200
|
|
@@ -71,18 +79,6 @@ class ResponseTest < Test::Unit::TestCase
|
|
|
71
79
|
out = StringIO.new
|
|
72
80
|
http_response_write(out,200, {"X-Whatever" => "stuff"}, [])
|
|
73
81
|
assert ! out.closed?
|
|
74
|
-
assert_equal 1, out.string.split(/\r\n/).grep(/^Status: 200 OK/i).size
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
def test_body_closed
|
|
78
|
-
expect_body = %w(1 2 3 4).join("\n")
|
|
79
|
-
body = StringIO.new(expect_body)
|
|
80
|
-
body.rewind
|
|
81
|
-
out = StringIO.new
|
|
82
|
-
http_response_write(out,200, {}, body)
|
|
83
|
-
assert ! out.closed?
|
|
84
|
-
assert body.closed?
|
|
85
|
-
assert_match(expect_body, out.string.split(/\r\n/).last)
|
|
86
82
|
end
|
|
87
83
|
|
|
88
84
|
def test_unknown_status_pass_through
|
|
@@ -91,9 +87,25 @@ class ResponseTest < Test::Unit::TestCase
|
|
|
91
87
|
assert ! out.closed?
|
|
92
88
|
headers = out.string.split(/\r\n\r\n/).first.split(/\r\n/)
|
|
93
89
|
assert %r{\AHTTP/\d\.\d 666 I AM THE BEAST\z}.match(headers[0])
|
|
94
|
-
status = headers.grep(/\AStatus:/i).first
|
|
95
|
-
assert status
|
|
96
|
-
assert_equal "Status: 666 I AM THE BEAST", status
|
|
97
90
|
end
|
|
98
91
|
|
|
92
|
+
def test_modified_rack_http_status_codes_late
|
|
93
|
+
r, w = IO.pipe
|
|
94
|
+
pid = fork do
|
|
95
|
+
r.close
|
|
96
|
+
# Users may want to globally override the status text associated
|
|
97
|
+
# with an HTTP status code in their app.
|
|
98
|
+
Rack::Utils::HTTP_STATUS_CODES[200] = "HI"
|
|
99
|
+
http_response_write(w, 200, {}, [])
|
|
100
|
+
w.close
|
|
101
|
+
end
|
|
102
|
+
w.close
|
|
103
|
+
assert_equal "HTTP/1.1 200 HI\r\n", r.gets
|
|
104
|
+
r.read # just drain the pipe
|
|
105
|
+
pid, status = Process.waitpid2(pid)
|
|
106
|
+
assert status.success?, status.inspect
|
|
107
|
+
ensure
|
|
108
|
+
r.close
|
|
109
|
+
w.close unless w.closed?
|
|
110
|
+
end
|
|
99
111
|
end
|
|
@@ -189,7 +189,7 @@ class TestSocketHelper < Test::Unit::TestCase
|
|
|
189
189
|
port = unused_port @test_addr
|
|
190
190
|
name = "#@test_addr:#{port}"
|
|
191
191
|
sock = bind_listen(name, :reuseport => true)
|
|
192
|
-
cur = sock.getsockopt(
|
|
192
|
+
cur = sock.getsockopt(:SOL_SOCKET, :SO_REUSEPORT).int
|
|
193
193
|
assert_operator cur, :>, 0
|
|
194
194
|
rescue Errno::ENOPROTOOPT
|
|
195
195
|
# kernel does not support SO_REUSEPORT (older Linux)
|
data/unicorn.gemspec
CHANGED
|
@@ -26,6 +26,11 @@ Gem::Specification.new do |s|
|
|
|
26
26
|
s.homepage = Olddoc.config['rdoc_url']
|
|
27
27
|
s.test_files = test_files
|
|
28
28
|
|
|
29
|
+
# technically we need ">= 1.9.3", too, but avoid the array here since
|
|
30
|
+
# old rubygems versions (1.8.23.2 at least) do not support multiple
|
|
31
|
+
# version requirements here.
|
|
32
|
+
s.required_ruby_version = '< 3.0'
|
|
33
|
+
|
|
29
34
|
# for people that are absolutely stuck on Rails 2.3.2 and can't
|
|
30
35
|
# up/downgrade to any other version, the Rack dependency may be
|
|
31
36
|
# commented out. Nevertheless, upgrading to Rails 2.3.4 or later is
|
|
@@ -37,5 +42,9 @@ Gem::Specification.new do |s|
|
|
|
37
42
|
s.add_development_dependency('test-unit', '~> 3.0')
|
|
38
43
|
s.add_development_dependency('olddoc', '~> 1.0')
|
|
39
44
|
|
|
40
|
-
|
|
45
|
+
# Note: To avoid ambiguity, we intentionally avoid the SPDX-compatible
|
|
46
|
+
# 'Ruby' here since Ruby 1.9.3 switched to BSD-2-Clause, but we
|
|
47
|
+
# inherited our license from Mongrel when Ruby was at 1.8.
|
|
48
|
+
# We cannot automatically switch licenses when Ruby changes.
|
|
49
|
+
s.licenses = ['GPL-2.0+', 'Ruby-1.8']
|
|
41
50
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: unicorn
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 5.0.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
|
-
-
|
|
7
|
+
- unicorn hackers
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2015-
|
|
11
|
+
date: 2015-11-17 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rack
|
|
@@ -81,11 +81,11 @@ dependencies:
|
|
|
81
81
|
- !ruby/object:Gem::Version
|
|
82
82
|
version: '1.0'
|
|
83
83
|
description: |-
|
|
84
|
-
|
|
84
|
+
unicorn is an HTTP server for Rack applications designed to only serve
|
|
85
85
|
fast clients on low-latency, high-bandwidth connections and take
|
|
86
86
|
advantage of features in Unix/Unix-like kernels. Slow clients should
|
|
87
87
|
only be served by placing a reverse proxy capable of fully buffering
|
|
88
|
-
both the the request and response in between
|
|
88
|
+
both the the request and response in between unicorn and slow clients.
|
|
89
89
|
email: unicorn-public@bogomips.org
|
|
90
90
|
executables:
|
|
91
91
|
- unicorn
|
|
@@ -124,6 +124,7 @@ extra_rdoc_files:
|
|
|
124
124
|
files:
|
|
125
125
|
- ".CHANGELOG.old"
|
|
126
126
|
- ".document"
|
|
127
|
+
- ".gitattributes"
|
|
127
128
|
- ".gitignore"
|
|
128
129
|
- ".mailmap"
|
|
129
130
|
- ".manifest"
|
|
@@ -160,13 +161,14 @@ files:
|
|
|
160
161
|
- bin/unicorn_rails
|
|
161
162
|
- examples/big_app_gc.rb
|
|
162
163
|
- examples/echo.ru
|
|
163
|
-
- examples/git.ru
|
|
164
164
|
- examples/init.sh
|
|
165
165
|
- examples/logger_mp_safe.rb
|
|
166
166
|
- examples/logrotate.conf
|
|
167
167
|
- examples/nginx.conf
|
|
168
168
|
- examples/unicorn.conf.minimal.rb
|
|
169
169
|
- examples/unicorn.conf.rb
|
|
170
|
+
- examples/unicorn.socket
|
|
171
|
+
- examples/unicorn@.service
|
|
170
172
|
- ext/unicorn_http/CFLAGS
|
|
171
173
|
- ext/unicorn_http/c_util.h
|
|
172
174
|
- ext/unicorn_http/common_field_optimization.h
|
|
@@ -178,8 +180,6 @@ files:
|
|
|
178
180
|
- ext/unicorn_http/unicorn_http.rl
|
|
179
181
|
- ext/unicorn_http/unicorn_http_common.rl
|
|
180
182
|
- lib/unicorn.rb
|
|
181
|
-
- lib/unicorn/app/exec_cgi.rb
|
|
182
|
-
- lib/unicorn/app/inetd.rb
|
|
183
183
|
- lib/unicorn/app/old_rails.rb
|
|
184
184
|
- lib/unicorn/app/old_rails/static.rb
|
|
185
185
|
- lib/unicorn/cgi_wrapper.rb
|
|
@@ -192,9 +192,6 @@ files:
|
|
|
192
192
|
- lib/unicorn/oob_gc.rb
|
|
193
193
|
- lib/unicorn/preread_input.rb
|
|
194
194
|
- lib/unicorn/socket_helper.rb
|
|
195
|
-
- lib/unicorn/ssl_client.rb
|
|
196
|
-
- lib/unicorn/ssl_configurator.rb
|
|
197
|
-
- lib/unicorn/ssl_server.rb
|
|
198
195
|
- lib/unicorn/stream_input.rb
|
|
199
196
|
- lib/unicorn/tee_input.rb
|
|
200
197
|
- lib/unicorn/tmpio.rb
|
|
@@ -245,8 +242,6 @@ files:
|
|
|
245
242
|
- t/t0014-rewindable-input-true.sh
|
|
246
243
|
- t/t0014.ru
|
|
247
244
|
- t/t0015-configurator-internals.sh
|
|
248
|
-
- t/t0016-trust-x-forwarded-false.sh
|
|
249
|
-
- t/t0017-trust-x-forwarded-true.sh
|
|
250
245
|
- t/t0018-write-on-close.sh
|
|
251
246
|
- t/t0019-max_header_len.sh
|
|
252
247
|
- t/t0020-at_exit-handler.sh
|
|
@@ -273,12 +268,10 @@ files:
|
|
|
273
268
|
- test/unit/test_droplet.rb
|
|
274
269
|
- test/unit/test_http_parser.rb
|
|
275
270
|
- test/unit/test_http_parser_ng.rb
|
|
276
|
-
- test/unit/test_http_parser_xftrust.rb
|
|
277
271
|
- test/unit/test_request.rb
|
|
278
272
|
- test/unit/test_response.rb
|
|
279
273
|
- test/unit/test_server.rb
|
|
280
274
|
- test/unit/test_signals.rb
|
|
281
|
-
- test/unit/test_sni_hostnames.rb
|
|
282
275
|
- test/unit/test_socket_helper.rb
|
|
283
276
|
- test/unit/test_stream_input.rb
|
|
284
277
|
- test/unit/test_tee_input.rb
|
|
@@ -289,8 +282,8 @@ files:
|
|
|
289
282
|
- unicorn_rails_1
|
|
290
283
|
homepage: http://unicorn.bogomips.org/
|
|
291
284
|
licenses:
|
|
292
|
-
-
|
|
293
|
-
- Ruby
|
|
285
|
+
- GPL-2.0+
|
|
286
|
+
- Ruby-1.8
|
|
294
287
|
metadata: {}
|
|
295
288
|
post_install_message:
|
|
296
289
|
rdoc_options: []
|
|
@@ -298,9 +291,9 @@ require_paths:
|
|
|
298
291
|
- lib
|
|
299
292
|
required_ruby_version: !ruby/object:Gem::Requirement
|
|
300
293
|
requirements:
|
|
301
|
-
- - "
|
|
294
|
+
- - "<"
|
|
302
295
|
- !ruby/object:Gem::Version
|
|
303
|
-
version: '0'
|
|
296
|
+
version: '3.0'
|
|
304
297
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
305
298
|
requirements:
|
|
306
299
|
- - ">="
|
|
@@ -308,7 +301,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
308
301
|
version: '0'
|
|
309
302
|
requirements: []
|
|
310
303
|
rubyforge_project:
|
|
311
|
-
rubygems_version: 2.
|
|
304
|
+
rubygems_version: 2.5.0
|
|
312
305
|
signing_key:
|
|
313
306
|
specification_version: 4
|
|
314
307
|
summary: Rack HTTP server for fast clients and Unix
|
|
@@ -316,9 +309,6 @@ test_files:
|
|
|
316
309
|
- test/unit/test_configurator.rb
|
|
317
310
|
- test/unit/test_http_parser.rb
|
|
318
311
|
- test/unit/test_http_parser_ng.rb
|
|
319
|
-
- test/unit/test_http_parser_xftrust.rb
|
|
320
312
|
- test/unit/test_request.rb
|
|
321
|
-
- test/unit/test_response.rb
|
|
322
313
|
- test/unit/test_server.rb
|
|
323
|
-
- test/unit/test_sni_hostnames.rb
|
|
324
314
|
- test/unit/test_util.rb
|
data/examples/git.ru
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
#\-E none
|
|
2
|
-
|
|
3
|
-
# See http://thread.gmane.org/gmane.comp.web.curl.general/10473/raw on
|
|
4
|
-
# how to setup git for this. A better version of the above patch was
|
|
5
|
-
# accepted and committed on June 15, 2009, so you can pull the latest
|
|
6
|
-
# curl CVS snapshot to try this out.
|
|
7
|
-
require 'unicorn/app/inetd'
|
|
8
|
-
|
|
9
|
-
use Rack::Lint
|
|
10
|
-
use Rack::Chunked # important!
|
|
11
|
-
run Unicorn::App::Inetd.new(
|
|
12
|
-
*%w(git daemon --verbose --inetd --export-all --base-path=/home/ew/unicorn)
|
|
13
|
-
)
|
data/lib/unicorn/app/exec_cgi.rb
DELETED
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
# -*- encoding: binary -*-
|
|
2
|
-
# :enddoc:
|
|
3
|
-
require 'unicorn'
|
|
4
|
-
|
|
5
|
-
module Unicorn::App
|
|
6
|
-
|
|
7
|
-
# This class is highly experimental (even more so than the rest of Unicorn)
|
|
8
|
-
# and has never run anything other than cgit.
|
|
9
|
-
class ExecCgi < Struct.new(:args)
|
|
10
|
-
|
|
11
|
-
CHUNK_SIZE = 16384
|
|
12
|
-
PASS_VARS = %w(
|
|
13
|
-
CONTENT_LENGTH
|
|
14
|
-
CONTENT_TYPE
|
|
15
|
-
GATEWAY_INTERFACE
|
|
16
|
-
AUTH_TYPE
|
|
17
|
-
PATH_INFO
|
|
18
|
-
PATH_TRANSLATED
|
|
19
|
-
QUERY_STRING
|
|
20
|
-
REMOTE_ADDR
|
|
21
|
-
REMOTE_HOST
|
|
22
|
-
REMOTE_IDENT
|
|
23
|
-
REMOTE_USER
|
|
24
|
-
REQUEST_METHOD
|
|
25
|
-
SERVER_NAME
|
|
26
|
-
SERVER_PORT
|
|
27
|
-
SERVER_PROTOCOL
|
|
28
|
-
SERVER_SOFTWARE
|
|
29
|
-
).map { |x| x.freeze } # frozen strings are faster for Hash assignments
|
|
30
|
-
|
|
31
|
-
class Body < Unicorn::TmpIO
|
|
32
|
-
def body_offset=(n)
|
|
33
|
-
sysseek(@body_offset = n)
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def each
|
|
37
|
-
sysseek @body_offset
|
|
38
|
-
# don't use a preallocated buffer for sysread since we can't
|
|
39
|
-
# guarantee an actual socket is consuming the yielded string
|
|
40
|
-
# (or if somebody is pushing to an array for eventual concatenation
|
|
41
|
-
begin
|
|
42
|
-
yield sysread(CHUNK_SIZE)
|
|
43
|
-
rescue EOFError
|
|
44
|
-
break
|
|
45
|
-
end while true
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
# Intializes the app, example of usage in a config.ru
|
|
50
|
-
# map "/cgit" do
|
|
51
|
-
# run Unicorn::App::ExecCgi.new("/path/to/cgit.cgi")
|
|
52
|
-
# end
|
|
53
|
-
def initialize(*args)
|
|
54
|
-
self.args = args
|
|
55
|
-
first = args[0] or
|
|
56
|
-
raise ArgumentError, "need path to executable"
|
|
57
|
-
first[0] == ?/ or args[0] = ::File.expand_path(first)
|
|
58
|
-
File.executable?(args[0]) or
|
|
59
|
-
raise ArgumentError, "#{args[0]} is not executable"
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
# Calls the app
|
|
63
|
-
def call(env)
|
|
64
|
-
out, err = Body.new, Unicorn::TmpIO.new
|
|
65
|
-
inp = force_file_input(env)
|
|
66
|
-
pid = fork { run_child(inp, out, err, env) }
|
|
67
|
-
inp.close
|
|
68
|
-
pid, status = Process.waitpid2(pid)
|
|
69
|
-
write_errors(env, err, status) if err.stat.size > 0
|
|
70
|
-
err.close
|
|
71
|
-
|
|
72
|
-
return parse_output!(out) if status.success?
|
|
73
|
-
out.close
|
|
74
|
-
[ 500, { 'Content-Length' => '0', 'Content-Type' => 'text/plain' }, [] ]
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
private
|
|
78
|
-
|
|
79
|
-
def run_child(inp, out, err, env)
|
|
80
|
-
PASS_VARS.each do |key|
|
|
81
|
-
val = env[key] or next
|
|
82
|
-
ENV[key] = val
|
|
83
|
-
end
|
|
84
|
-
ENV['SCRIPT_NAME'] = args[0]
|
|
85
|
-
ENV['GATEWAY_INTERFACE'] = 'CGI/1.1'
|
|
86
|
-
env.keys.grep(/^HTTP_/) { |key| ENV[key] = env[key] }
|
|
87
|
-
|
|
88
|
-
$stdin.reopen(inp)
|
|
89
|
-
$stdout.reopen(out)
|
|
90
|
-
$stderr.reopen(err)
|
|
91
|
-
exec(*args)
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
# Extracts headers from CGI out, will change the offset of out.
|
|
95
|
-
# This returns a standard Rack-compatible return value:
|
|
96
|
-
# [ 200, HeadersHash, body ]
|
|
97
|
-
def parse_output!(out)
|
|
98
|
-
size = out.stat.size
|
|
99
|
-
out.sysseek(0)
|
|
100
|
-
head = out.sysread(CHUNK_SIZE)
|
|
101
|
-
offset = 2
|
|
102
|
-
head, body = head.split(/\n\n/, 2)
|
|
103
|
-
if body.nil?
|
|
104
|
-
head, body = head.split(/\r\n\r\n/, 2)
|
|
105
|
-
offset = 4
|
|
106
|
-
end
|
|
107
|
-
offset += head.length
|
|
108
|
-
out.body_offset = offset
|
|
109
|
-
size -= offset
|
|
110
|
-
prev = nil
|
|
111
|
-
headers = Rack::Utils::HeaderHash.new
|
|
112
|
-
head.split(/\r?\n/).each do |line|
|
|
113
|
-
case line
|
|
114
|
-
when /^([A-Za-z0-9-]+):\s*(.*)$/ then headers[prev = $1] = $2
|
|
115
|
-
when /^[ \t]/ then headers[prev] << "\n#{line}" if prev
|
|
116
|
-
end
|
|
117
|
-
end
|
|
118
|
-
status = headers.delete("Status") || 200
|
|
119
|
-
headers['Content-Length'] = size.to_s
|
|
120
|
-
[ status, headers, out ]
|
|
121
|
-
end
|
|
122
|
-
|
|
123
|
-
# ensures rack.input is a file handle that we can redirect stdin to
|
|
124
|
-
def force_file_input(env)
|
|
125
|
-
inp = env['rack.input']
|
|
126
|
-
# inp could be a StringIO or StringIO-like object
|
|
127
|
-
if inp.respond_to?(:size) && inp.size == 0
|
|
128
|
-
::File.open('/dev/null', 'rb')
|
|
129
|
-
else
|
|
130
|
-
tmp = Unicorn::TmpIO.new
|
|
131
|
-
|
|
132
|
-
buf = inp.read(CHUNK_SIZE)
|
|
133
|
-
begin
|
|
134
|
-
tmp.syswrite(buf)
|
|
135
|
-
end while inp.read(CHUNK_SIZE, buf)
|
|
136
|
-
tmp.sysseek(0)
|
|
137
|
-
tmp
|
|
138
|
-
end
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
# rack.errors this may not be an IO object, so we couldn't
|
|
142
|
-
# just redirect the CGI executable to that earlier.
|
|
143
|
-
def write_errors(env, err, status)
|
|
144
|
-
err.seek(0)
|
|
145
|
-
dst = env['rack.errors']
|
|
146
|
-
pid = status.pid
|
|
147
|
-
dst.write("#{pid}: #{args.inspect} status=#{status} stderr:\n")
|
|
148
|
-
err.each_line { |line| dst.write("#{pid}: #{line}") }
|
|
149
|
-
dst.flush
|
|
150
|
-
end
|
|
151
|
-
|
|
152
|
-
end
|
|
153
|
-
|
|
154
|
-
end
|
data/lib/unicorn/app/inetd.rb
DELETED
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
# -*- encoding: binary -*-
|
|
2
|
-
# :enddoc:
|
|
3
|
-
# Copyright (c) 2009 Eric Wong
|
|
4
|
-
# You can redistribute it and/or modify it under the same terms as Ruby 1.8 or
|
|
5
|
-
# the GPLv2+ (GPLv3+ preferred)
|
|
6
|
-
|
|
7
|
-
# this class *must* be used with Rack::Chunked
|
|
8
|
-
module Unicorn::App
|
|
9
|
-
class Inetd < Struct.new(:cmd)
|
|
10
|
-
|
|
11
|
-
class CatBody < Struct.new(:errors, :err_rd, :out_rd, :pid_map)
|
|
12
|
-
def initialize(env, cmd)
|
|
13
|
-
self.errors = env['rack.errors']
|
|
14
|
-
in_rd, in_wr = IO.pipe
|
|
15
|
-
self.err_rd, err_wr = IO.pipe
|
|
16
|
-
self.out_rd, out_wr = IO.pipe
|
|
17
|
-
|
|
18
|
-
cmd_pid = fork {
|
|
19
|
-
inp, out, err = (0..2).map { |i| IO.new(i) }
|
|
20
|
-
inp.reopen(in_rd)
|
|
21
|
-
out.reopen(out_wr)
|
|
22
|
-
err.reopen(err_wr)
|
|
23
|
-
[ in_rd, in_wr, err_rd, err_wr, out_rd, out_wr ].each { |i| i.close }
|
|
24
|
-
exec(*cmd)
|
|
25
|
-
}
|
|
26
|
-
[ in_rd, err_wr, out_wr ].each { |io| io.close }
|
|
27
|
-
[ in_wr, err_rd, out_rd ].each { |io| io.binmode }
|
|
28
|
-
in_wr.sync = true
|
|
29
|
-
|
|
30
|
-
# Unfortunately, input here must be processed inside a seperate
|
|
31
|
-
# thread/process using blocking I/O since env['rack.input'] is not
|
|
32
|
-
# IO.select-able and attempting to make it so would trip Rack::Lint
|
|
33
|
-
inp_pid = fork {
|
|
34
|
-
input = env['rack.input']
|
|
35
|
-
[ err_rd, out_rd ].each { |io| io.close }
|
|
36
|
-
|
|
37
|
-
# this is dependent on input.read having readpartial semantics:
|
|
38
|
-
buf = input.read(16384)
|
|
39
|
-
begin
|
|
40
|
-
in_wr.write(buf)
|
|
41
|
-
end while input.read(16384, buf)
|
|
42
|
-
}
|
|
43
|
-
in_wr.close
|
|
44
|
-
self.pid_map = {
|
|
45
|
-
inp_pid => 'input streamer',
|
|
46
|
-
cmd_pid => cmd.inspect,
|
|
47
|
-
}
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
def each
|
|
51
|
-
begin
|
|
52
|
-
rd, = IO.select([err_rd, out_rd])
|
|
53
|
-
rd && rd.first or next
|
|
54
|
-
|
|
55
|
-
if rd.include?(err_rd)
|
|
56
|
-
begin
|
|
57
|
-
errors.write(err_rd.read_nonblock(16384))
|
|
58
|
-
rescue Errno::EINTR
|
|
59
|
-
rescue Errno::EAGAIN
|
|
60
|
-
break
|
|
61
|
-
end while true
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
rd.include?(out_rd) or next
|
|
65
|
-
|
|
66
|
-
begin
|
|
67
|
-
yield out_rd.read_nonblock(16384)
|
|
68
|
-
rescue Errno::EINTR
|
|
69
|
-
rescue Errno::EAGAIN
|
|
70
|
-
break
|
|
71
|
-
end while true
|
|
72
|
-
rescue EOFError,Errno::EPIPE,Errno::EBADF,Errno::EINVAL
|
|
73
|
-
break
|
|
74
|
-
end while true
|
|
75
|
-
|
|
76
|
-
self
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
def close
|
|
80
|
-
pid_map.each { |pid, str|
|
|
81
|
-
begin
|
|
82
|
-
pid, status = Process.waitpid2(pid)
|
|
83
|
-
status.success? or
|
|
84
|
-
errors.write("#{str}: #{status.inspect} (PID:#{pid})\n")
|
|
85
|
-
rescue Errno::ECHILD
|
|
86
|
-
errors.write("Failed to reap #{str} (PID:#{pid})\n")
|
|
87
|
-
end
|
|
88
|
-
}
|
|
89
|
-
out_rd.close
|
|
90
|
-
err_rd.close
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
def initialize(*cmd)
|
|
96
|
-
self.cmd = cmd
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
def call(env)
|
|
100
|
-
/\A100-continue\z/i =~ env[Unicorn::Const::HTTP_EXPECT] and
|
|
101
|
-
return [ 100, {} , [] ]
|
|
102
|
-
|
|
103
|
-
[ 200, { 'Content-Type' => 'application/octet-stream' },
|
|
104
|
-
CatBody.new(env, cmd) ]
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
end
|
data/lib/unicorn/ssl_client.rb
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
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
|
|
@@ -1,104 +0,0 @@
|
|
|
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 unicorn-public@bogomips.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
|
data/lib/unicorn/ssl_server.rb
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
# -*- encoding: binary -*-
|
|
2
|
-
# :stopdoc:
|
|
3
|
-
# this module is meant to be included in Unicorn::HttpServer
|
|
4
|
-
# It is an implementation detail and NOT meant for users.
|
|
5
|
-
module Unicorn::SSLServer
|
|
6
|
-
attr_accessor :ssl_engine
|
|
7
|
-
|
|
8
|
-
def ssl_enable!
|
|
9
|
-
sni_hostnames = rack_sni_hostnames(@app)
|
|
10
|
-
seen = {} # we map a single SSLContext to multiple listeners
|
|
11
|
-
listener_ctx = {}
|
|
12
|
-
@listener_opts.each do |address, address_opts|
|
|
13
|
-
ssl_opts = address_opts[:ssl_opts] or next
|
|
14
|
-
listener_ctx[address] = seen[ssl_opts.object_id] ||= begin
|
|
15
|
-
unless sni_hostnames.empty?
|
|
16
|
-
ssl_opts = ssl_opts.dup
|
|
17
|
-
ssl_opts[:sni_hostnames] = sni_hostnames
|
|
18
|
-
end
|
|
19
|
-
ctx = Flipper.ssl_context(ssl_opts)
|
|
20
|
-
# FIXME: make configurable
|
|
21
|
-
ctx.session_cache_mode = OpenSSL::SSL::SSLContext::SESSION_CACHE_OFF
|
|
22
|
-
ctx
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
Unicorn::HttpServer::LISTENERS.each do |listener|
|
|
26
|
-
ctx = listener_ctx[sock_name(listener)] or next
|
|
27
|
-
listener.extend(Kgio::SSLServer)
|
|
28
|
-
listener.ssl_ctx = ctx
|
|
29
|
-
listener.kgio_ssl_class = Unicorn::SSLClient
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
# ugh, this depends on Rack internals...
|
|
34
|
-
def rack_sni_hostnames(rack_app) # :nodoc:
|
|
35
|
-
hostnames = {}
|
|
36
|
-
if Rack::URLMap === rack_app
|
|
37
|
-
mapping = rack_app.instance_variable_get(:@mapping)
|
|
38
|
-
mapping.each { |hostname,_,_,_| hostnames[hostname] = true }
|
|
39
|
-
end
|
|
40
|
-
hostnames.keys
|
|
41
|
-
end
|
|
42
|
-
end
|