unicorn 4.9.0 → 5.0.0
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/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-GEN +1 -1
- data/GNUmakefile +6 -1
- data/ISSUES +20 -28
- data/KNOWN_ISSUES +9 -9
- data/Links +14 -17
- 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/ext/unicorn_http/extconf.rb +1 -0
- data/ext/unicorn_http/httpdate.c +1 -1
- data/ext/unicorn_http/unicorn_http.rl +89 -156
- data/lib/unicorn.rb +10 -18
- 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 +14 -32
- 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/worker.rb +3 -15
- 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 +19 -16
- data/test/unit/test_socket_helper.rb +1 -1
- data/unicorn.gemspec +10 -1
- metadata +10 -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
@@ -38,7 +38,6 @@ class ResponseTest < Test::Unit::TestCase
|
|
38
38
|
http_response_write(out,'200', {}, [])
|
39
39
|
assert ! out.closed?
|
40
40
|
assert out.length > 0, "output didn't have data"
|
41
|
-
assert_equal 1, out.string.split(/\r\n/).grep(/^Status: 200 OK/).size
|
42
41
|
end
|
43
42
|
|
44
43
|
def test_response_200
|
@@ -71,18 +70,6 @@ class ResponseTest < Test::Unit::TestCase
|
|
71
70
|
out = StringIO.new
|
72
71
|
http_response_write(out,200, {"X-Whatever" => "stuff"}, [])
|
73
72
|
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
73
|
end
|
87
74
|
|
88
75
|
def test_unknown_status_pass_through
|
@@ -91,9 +78,25 @@ class ResponseTest < Test::Unit::TestCase
|
|
91
78
|
assert ! out.closed?
|
92
79
|
headers = out.string.split(/\r\n\r\n/).first.split(/\r\n/)
|
93
80
|
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
81
|
end
|
98
82
|
|
83
|
+
def test_modified_rack_http_status_codes_late
|
84
|
+
r, w = IO.pipe
|
85
|
+
pid = fork do
|
86
|
+
r.close
|
87
|
+
# Users may want to globally override the status text associated
|
88
|
+
# with an HTTP status code in their app.
|
89
|
+
Rack::Utils::HTTP_STATUS_CODES[200] = "HI"
|
90
|
+
http_response_write(w, 200, {}, [])
|
91
|
+
w.close
|
92
|
+
end
|
93
|
+
w.close
|
94
|
+
assert_equal "HTTP/1.1 200 HI\r\n", r.gets
|
95
|
+
r.read # just drain the pipe
|
96
|
+
pid, status = Process.waitpid2(pid)
|
97
|
+
assert status.success?, status.inspect
|
98
|
+
ensure
|
99
|
+
r.close
|
100
|
+
w.close unless w.closed?
|
101
|
+
end
|
99
102
|
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.0
|
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-01 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
|
@@ -160,7 +160,6 @@ files:
|
|
160
160
|
- bin/unicorn_rails
|
161
161
|
- examples/big_app_gc.rb
|
162
162
|
- examples/echo.ru
|
163
|
-
- examples/git.ru
|
164
163
|
- examples/init.sh
|
165
164
|
- examples/logger_mp_safe.rb
|
166
165
|
- examples/logrotate.conf
|
@@ -178,8 +177,6 @@ files:
|
|
178
177
|
- ext/unicorn_http/unicorn_http.rl
|
179
178
|
- ext/unicorn_http/unicorn_http_common.rl
|
180
179
|
- lib/unicorn.rb
|
181
|
-
- lib/unicorn/app/exec_cgi.rb
|
182
|
-
- lib/unicorn/app/inetd.rb
|
183
180
|
- lib/unicorn/app/old_rails.rb
|
184
181
|
- lib/unicorn/app/old_rails/static.rb
|
185
182
|
- lib/unicorn/cgi_wrapper.rb
|
@@ -192,9 +189,6 @@ files:
|
|
192
189
|
- lib/unicorn/oob_gc.rb
|
193
190
|
- lib/unicorn/preread_input.rb
|
194
191
|
- lib/unicorn/socket_helper.rb
|
195
|
-
- lib/unicorn/ssl_client.rb
|
196
|
-
- lib/unicorn/ssl_configurator.rb
|
197
|
-
- lib/unicorn/ssl_server.rb
|
198
192
|
- lib/unicorn/stream_input.rb
|
199
193
|
- lib/unicorn/tee_input.rb
|
200
194
|
- lib/unicorn/tmpio.rb
|
@@ -245,8 +239,6 @@ files:
|
|
245
239
|
- t/t0014-rewindable-input-true.sh
|
246
240
|
- t/t0014.ru
|
247
241
|
- t/t0015-configurator-internals.sh
|
248
|
-
- t/t0016-trust-x-forwarded-false.sh
|
249
|
-
- t/t0017-trust-x-forwarded-true.sh
|
250
242
|
- t/t0018-write-on-close.sh
|
251
243
|
- t/t0019-max_header_len.sh
|
252
244
|
- t/t0020-at_exit-handler.sh
|
@@ -273,12 +265,10 @@ files:
|
|
273
265
|
- test/unit/test_droplet.rb
|
274
266
|
- test/unit/test_http_parser.rb
|
275
267
|
- test/unit/test_http_parser_ng.rb
|
276
|
-
- test/unit/test_http_parser_xftrust.rb
|
277
268
|
- test/unit/test_request.rb
|
278
269
|
- test/unit/test_response.rb
|
279
270
|
- test/unit/test_server.rb
|
280
271
|
- test/unit/test_signals.rb
|
281
|
-
- test/unit/test_sni_hostnames.rb
|
282
272
|
- test/unit/test_socket_helper.rb
|
283
273
|
- test/unit/test_stream_input.rb
|
284
274
|
- test/unit/test_tee_input.rb
|
@@ -289,8 +279,8 @@ files:
|
|
289
279
|
- unicorn_rails_1
|
290
280
|
homepage: http://unicorn.bogomips.org/
|
291
281
|
licenses:
|
292
|
-
-
|
293
|
-
- Ruby
|
282
|
+
- GPL-2.0+
|
283
|
+
- Ruby-1.8
|
294
284
|
metadata: {}
|
295
285
|
post_install_message:
|
296
286
|
rdoc_options: []
|
@@ -298,9 +288,9 @@ require_paths:
|
|
298
288
|
- lib
|
299
289
|
required_ruby_version: !ruby/object:Gem::Requirement
|
300
290
|
requirements:
|
301
|
-
- - "
|
291
|
+
- - "<"
|
302
292
|
- !ruby/object:Gem::Version
|
303
|
-
version: '0'
|
293
|
+
version: '3.0'
|
304
294
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
305
295
|
requirements:
|
306
296
|
- - ">="
|
@@ -308,7 +298,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
308
298
|
version: '0'
|
309
299
|
requirements: []
|
310
300
|
rubyforge_project:
|
311
|
-
rubygems_version: 2.
|
301
|
+
rubygems_version: 2.5.0
|
312
302
|
signing_key:
|
313
303
|
specification_version: 4
|
314
304
|
summary: Rack HTTP server for fast clients and Unix
|
@@ -316,9 +306,6 @@ test_files:
|
|
316
306
|
- test/unit/test_configurator.rb
|
317
307
|
- test/unit/test_http_parser.rb
|
318
308
|
- test/unit/test_http_parser_ng.rb
|
319
|
-
- test/unit/test_http_parser_xftrust.rb
|
320
309
|
- test/unit/test_request.rb
|
321
|
-
- test/unit/test_response.rb
|
322
310
|
- test/unit/test_server.rb
|
323
|
-
- test/unit/test_sni_hostnames.rb
|
324
311
|
- 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
|