unicorn 4.5.0 → 4.6.0pre1
Sign up to get free protection for your applications and to get access to all the features.
- data/GIT-VERSION-GEN +1 -1
- data/bin/unicorn +5 -0
- data/lib/unicorn.rb +2 -0
- data/lib/unicorn/http_request.rb +29 -0
- data/lib/unicorn/http_response.rb +32 -8
- data/lib/unicorn/http_server.rb +6 -2
- data/script/isolate_for_tests +3 -3
- data/t/hijack.ru +42 -0
- data/t/t0005-working_directory_app.rb.sh +4 -1
- data/t/t0200-rack-hijack.sh +27 -0
- data/test/exec/test_exec.rb +1 -0
- metadata +7 -5
data/GIT-VERSION-GEN
CHANGED
data/bin/unicorn
CHANGED
@@ -58,6 +58,11 @@ op = OptionParser.new("", 24, ' ') do |opts|
|
|
58
58
|
ENV["RACK_ENV"] = e
|
59
59
|
end
|
60
60
|
|
61
|
+
opts.on("-N", "--no-default-middleware",
|
62
|
+
"do not load middleware implied by RACK_ENV") do |e|
|
63
|
+
rackup_opts[:no_default_middleware] = true
|
64
|
+
end
|
65
|
+
|
61
66
|
opts.on("-D", "--daemonize", "run daemonized in the background") do |d|
|
62
67
|
rackup_opts[:daemonize] = !!d
|
63
68
|
end
|
data/lib/unicorn.rb
CHANGED
@@ -49,6 +49,8 @@ module Unicorn
|
|
49
49
|
|
50
50
|
pp({ :inner_app => inner_app }) if $DEBUG
|
51
51
|
|
52
|
+
return inner_app if op[:no_default_middleware]
|
53
|
+
|
52
54
|
# return value, matches rackup defaults based on env
|
53
55
|
# Unicorn does not support persistent connections, but Rainbows!
|
54
56
|
# and Zbatery both do. Users accustomed to the Rack::Server default
|
data/lib/unicorn/http_request.rb
CHANGED
@@ -91,6 +91,35 @@ class Unicorn::HttpParser
|
|
91
91
|
|
92
92
|
e[RACK_INPUT] = 0 == content_length ?
|
93
93
|
NULL_IO : @@input_class.new(socket, self)
|
94
|
+
hijack_setup(e, socket)
|
94
95
|
e.merge!(DEFAULTS)
|
95
96
|
end
|
97
|
+
|
98
|
+
# Rack 1.5.0 (protocol version 1.2) adds hijack request support
|
99
|
+
if ((Rack::VERSION[0] << 8) | Rack::VERSION[1]) >= 0x0102
|
100
|
+
DEFAULTS["rack.hijack?"] = true
|
101
|
+
|
102
|
+
# FIXME: asking for clarification about this in
|
103
|
+
# http://mid.gmane.org/20130122100802.GA28585@dcvr.yhbt.net
|
104
|
+
DEFAULTS["rack.version"] = [1, 2]
|
105
|
+
|
106
|
+
RACK_HIJACK = "rack.hijack".freeze
|
107
|
+
RACK_HIJACK_IO = "rack.hijack_io".freeze
|
108
|
+
|
109
|
+
def hijacked?
|
110
|
+
env.include?(RACK_HIJACK_IO)
|
111
|
+
end
|
112
|
+
|
113
|
+
def hijack_setup(e, socket)
|
114
|
+
e[RACK_HIJACK] = proc { e[RACK_HIJACK_IO] ||= socket }
|
115
|
+
end
|
116
|
+
else
|
117
|
+
# old Rack, do nothing.
|
118
|
+
def hijack_setup(e, _)
|
119
|
+
end
|
120
|
+
|
121
|
+
def hijacked?
|
122
|
+
false
|
123
|
+
end
|
124
|
+
end
|
96
125
|
end
|
@@ -25,6 +25,7 @@ module Unicorn::HttpResponse
|
|
25
25
|
def http_response_write(socket, status, headers, body,
|
26
26
|
response_start_sent=false)
|
27
27
|
status = CODES[status.to_i] || status
|
28
|
+
hijack = nil
|
28
29
|
|
29
30
|
http_response_start = response_start_sent ? '' : 'HTTP/1.1 '
|
30
31
|
if headers
|
@@ -33,19 +34,42 @@ module Unicorn::HttpResponse
|
|
33
34
|
"Status: #{status}\r\n" \
|
34
35
|
"Connection: close\r\n"
|
35
36
|
headers.each do |key, value|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
37
|
+
case key
|
38
|
+
when %r{\A(?:Date\z|Connection\z)}i
|
39
|
+
next
|
40
|
+
when "rack.hijack"
|
41
|
+
# this was an illegal key in Rack < 1.5, so it should be
|
42
|
+
# OK to silently discard it for those older versions
|
43
|
+
hijack = hijack_prepare(value)
|
40
44
|
else
|
41
|
-
|
45
|
+
if value =~ /\n/
|
46
|
+
# avoiding blank, key-only cookies with /\n+/
|
47
|
+
buf << value.split(/\n+/).map! { |v| "#{key}: #{v}\r\n" }.join
|
48
|
+
else
|
49
|
+
buf << "#{key}: #{value}\r\n"
|
50
|
+
end
|
42
51
|
end
|
43
52
|
end
|
44
53
|
socket.write(buf << CRLF)
|
45
54
|
end
|
46
55
|
|
47
|
-
|
48
|
-
|
49
|
-
|
56
|
+
if hijack
|
57
|
+
body = nil # ensure we do not close body
|
58
|
+
hijack.call(socket)
|
59
|
+
else
|
60
|
+
body.each { |chunk| socket.write(chunk) }
|
61
|
+
end
|
62
|
+
ensure
|
63
|
+
body.respond_to?(:close) and body.close
|
64
|
+
end
|
65
|
+
|
66
|
+
# Rack 1.5.0 (protocol version 1.2) adds response hijacking support
|
67
|
+
if ((Rack::VERSION[0] << 8) | Rack::VERSION[1]) >= 0x0102
|
68
|
+
def hijack_prepare(value)
|
69
|
+
value
|
70
|
+
end
|
71
|
+
else
|
72
|
+
def hijack_prepare(_)
|
73
|
+
end
|
50
74
|
end
|
51
75
|
end
|
data/lib/unicorn/http_server.rb
CHANGED
@@ -550,17 +550,21 @@ class Unicorn::HttpServer
|
|
550
550
|
# in 3 easy steps: read request, call app, write app response
|
551
551
|
def process_client(client)
|
552
552
|
status, headers, body = @app.call(env = @request.read(client))
|
553
|
+
return if @request.hijacked?
|
553
554
|
|
554
555
|
if 100 == status.to_i
|
555
556
|
client.write(expect_100_response)
|
556
557
|
env.delete(Unicorn::Const::HTTP_EXPECT)
|
557
558
|
status, headers, body = @app.call(env)
|
559
|
+
return if @request.hijacked?
|
558
560
|
end
|
559
561
|
@request.headers? or headers = nil
|
560
562
|
http_response_write(client, status, headers, body,
|
561
563
|
@request.response_start_sent)
|
562
|
-
client.
|
563
|
-
|
564
|
+
unless client.closed? # rack.hijack may've close this for us
|
565
|
+
client.shutdown # in case of fork() in Rack app
|
566
|
+
client.close # flush and uncork socket immediately, no keepalive
|
567
|
+
end
|
564
568
|
rescue => e
|
565
569
|
handle_error(client, e)
|
566
570
|
end
|
data/script/isolate_for_tests
CHANGED
@@ -16,10 +16,10 @@ opts = {
|
|
16
16
|
|
17
17
|
pid = fork do
|
18
18
|
Isolate.now!(opts) do
|
19
|
-
gem 'raindrops', '0.
|
19
|
+
gem 'raindrops', '0.10.0'
|
20
20
|
gem 'kgio-monkey', '0.4.0'
|
21
|
-
gem 'kgio', '2.
|
22
|
-
gem 'rack', '1.
|
21
|
+
gem 'kgio', '2.8.0'
|
22
|
+
gem 'rack', '1.5.1'
|
23
23
|
end
|
24
24
|
end
|
25
25
|
_, status = Process.waitpid2(pid)
|
data/t/hijack.ru
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
use Rack::Lint
|
2
|
+
use Rack::ContentLength
|
3
|
+
use Rack::ContentType, "text/plain"
|
4
|
+
class DieIfUsed
|
5
|
+
def each
|
6
|
+
abort "body.each called after response hijack\n"
|
7
|
+
end
|
8
|
+
|
9
|
+
def close
|
10
|
+
abort "body.close called after response hijack\n"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
run lambda { |env|
|
14
|
+
case env["PATH_INFO"]
|
15
|
+
when "/hijack_req"
|
16
|
+
if env["rack.hijack?"]
|
17
|
+
io = env["rack.hijack"].call
|
18
|
+
if io.respond_to?(:read_nonblock) &&
|
19
|
+
env["rack.hijack_io"].respond_to?(:read_nonblock)
|
20
|
+
|
21
|
+
# exercise both, since we Rack::Lint may use different objects
|
22
|
+
env["rack.hijack_io"].write("HTTP/1.0 200 OK\r\n\r\n")
|
23
|
+
io.write("request.hijacked")
|
24
|
+
io.close
|
25
|
+
return [ 500, {}, DieIfUsed.new ]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
[ 500, {}, [ "hijack BAD\n" ] ]
|
29
|
+
when "/hijack_res"
|
30
|
+
r = "response.hijacked"
|
31
|
+
[ 200,
|
32
|
+
{
|
33
|
+
"Content-Length" => r.bytesize.to_s,
|
34
|
+
"rack.hijack" => proc do |io|
|
35
|
+
io.write(r)
|
36
|
+
io.close
|
37
|
+
end
|
38
|
+
},
|
39
|
+
DieIfUsed.new
|
40
|
+
]
|
41
|
+
end
|
42
|
+
}
|
@@ -11,7 +11,10 @@ t_begin "setup and start" && {
|
|
11
11
|
cat > $t_pfx.app/fooapp.rb <<\EOF
|
12
12
|
class Fooapp
|
13
13
|
def self.call(env)
|
14
|
-
|
14
|
+
# Rack::Lint in 1.5.0 requires headers to be a hash
|
15
|
+
h = [%w(Content-Type text/plain), %w(Content-Length 2)]
|
16
|
+
h = Rack::Utils::HeaderHash.new(h)
|
17
|
+
[ 200, h, %w(HI) ]
|
15
18
|
end
|
16
19
|
end
|
17
20
|
EOF
|
@@ -0,0 +1,27 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
. ./test-lib.sh
|
3
|
+
t_plan 5 "rack.hijack tests (Rack 1.5+ (Rack::VERSION >= [ 1,2]))"
|
4
|
+
|
5
|
+
t_begin "setup and start" && {
|
6
|
+
unicorn_setup
|
7
|
+
unicorn -D -c $unicorn_config hijack.ru
|
8
|
+
unicorn_wait_start
|
9
|
+
}
|
10
|
+
|
11
|
+
t_begin "check request hijack" && {
|
12
|
+
test "xrequest.hijacked" = x"$(curl -sSfv http://$listen/hijack_req)"
|
13
|
+
}
|
14
|
+
|
15
|
+
t_begin "check response hijack" && {
|
16
|
+
test "xresponse.hijacked" = x"$(curl -sSfv http://$listen/hijack_res)"
|
17
|
+
}
|
18
|
+
|
19
|
+
t_begin "killing succeeds" && {
|
20
|
+
kill $unicorn_pid
|
21
|
+
}
|
22
|
+
|
23
|
+
t_begin "check stderr" && {
|
24
|
+
check_stderr
|
25
|
+
}
|
26
|
+
|
27
|
+
t_done
|
data/test/exec/test_exec.rb
CHANGED
@@ -323,6 +323,7 @@ EOF
|
|
323
323
|
# mobile phone or netbook on a slow connection :)
|
324
324
|
assert lines.size <= 24, "help height fits in an ANSI terminal window"
|
325
325
|
lines.each do |line|
|
326
|
+
line.chomp!
|
326
327
|
assert line.size <= 80, "help width fits in an ANSI terminal window"
|
327
328
|
end
|
328
329
|
end
|
metadata
CHANGED
@@ -2,15 +2,15 @@
|
|
2
2
|
name: !binary |-
|
3
3
|
dW5pY29ybg==
|
4
4
|
version: !ruby/object:Gem::Version
|
5
|
-
version: 4.
|
6
|
-
prerelease:
|
5
|
+
version: 4.6.0pre1
|
6
|
+
prerelease: 5
|
7
7
|
platform: ruby
|
8
8
|
authors:
|
9
9
|
- Unicorn hackers
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2013-01-29 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: !binary |-
|
@@ -255,6 +255,7 @@ files:
|
|
255
255
|
- t/detach.ru
|
256
256
|
- t/env.ru
|
257
257
|
- t/heartbeat-timeout.ru
|
258
|
+
- t/hijack.ru
|
258
259
|
- t/listener_names.ru
|
259
260
|
- t/my-tap-lib.sh
|
260
261
|
- t/oob_gc.ru
|
@@ -295,6 +296,7 @@ files:
|
|
295
296
|
- t/t0100-rack-input-tests.sh
|
296
297
|
- t/t0116-client_body_buffer_size.sh
|
297
298
|
- t/t0116.ru
|
299
|
+
- t/t0200-rack-hijack.sh
|
298
300
|
- t/t0600-https-server-basic.sh
|
299
301
|
- t/t9000-preread-input.sh
|
300
302
|
- t/t9001-oob_gc.sh
|
@@ -349,9 +351,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
349
351
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
350
352
|
none: false
|
351
353
|
requirements:
|
352
|
-
- - ! '
|
354
|
+
- - ! '>'
|
353
355
|
- !ruby/object:Gem::Version
|
354
|
-
version:
|
356
|
+
version: 1.3.1
|
355
357
|
requirements: []
|
356
358
|
rubyforge_project: !binary |-
|
357
359
|
bW9uZ3JlbA==
|