unicorn 0.93.1 → 0.93.2
Sign up to get free protection for your applications and to get access to all the features.
- data/GIT-VERSION-GEN +1 -1
- data/GNUmakefile +1 -0
- data/LICENSE +3 -3
- data/README +1 -1
- data/Rakefile +1 -1
- data/TODO +0 -2
- data/lib/unicorn.rb +21 -0
- data/lib/unicorn/const.rb +1 -1
- data/lib/unicorn/tee_input.rb +41 -2
- data/test/unit/test_tee_input.rb +46 -0
- data/unicorn.gemspec +1 -1
- metadata +3 -3
data/GIT-VERSION-GEN
CHANGED
data/GNUmakefile
CHANGED
@@ -169,6 +169,7 @@ atom = <link rel="alternate" title="Atom feed" href="$(1)" \
|
|
169
169
|
doc: .document $(ext)/unicorn_http.c NEWS ChangeLog
|
170
170
|
for i in $(man1_bins); do > $$i; done
|
171
171
|
rdoc -Na -t "$(shell sed -ne '1s/^= //p' README)"
|
172
|
+
install -m644 COPYING doc/COPYING
|
172
173
|
install -m644 $(shell grep '^[A-Z]' .document) doc/
|
173
174
|
$(MAKE) -C Documentation install-html install-man
|
174
175
|
install -m644 $(man1_paths) doc/
|
data/LICENSE
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
Unicorn is copyrighted free software by
|
2
|
-
|
3
|
-
and/or modify it under either the terms of the
|
1
|
+
Unicorn is copyrighted free software by all contributors, see logs in
|
2
|
+
revision control for names and email addresses of all of them. You can
|
3
|
+
redistribute it and/or modify it under either the terms of the
|
4
4
|
{GPL2}[http://www.gnu.org/licenses/gpl-2.0.txt] (see link:COPYING) or
|
5
5
|
the conditions below:
|
6
6
|
|
data/README
CHANGED
@@ -59,7 +59,7 @@ both the the request and response in between Unicorn and slow clients.
|
|
59
59
|
|
60
60
|
== License
|
61
61
|
|
62
|
-
Unicorn is copyright 2009
|
62
|
+
Unicorn is copyright 2009 by all contributors (see logs in git).
|
63
63
|
It is based on Mongrel and carries the same license.
|
64
64
|
|
65
65
|
Mongrel is copyright 2007 Zed A. Shaw and contributors. It is licensed
|
data/Rakefile
CHANGED
data/TODO
CHANGED
data/lib/unicorn.rb
CHANGED
@@ -53,6 +53,27 @@ module Unicorn
|
|
53
53
|
|
54
54
|
# We populate this at startup so we can figure out how to reexecute
|
55
55
|
# and upgrade the currently running instance of Unicorn
|
56
|
+
# This Hash is considered a stable interface and changing its contents
|
57
|
+
# will allow you to switch between different installations of Unicorn
|
58
|
+
# or even different installations of the same applications without
|
59
|
+
# downtime. Keys of this constant Hash are described as follows:
|
60
|
+
#
|
61
|
+
# * 0 - the path to the unicorn/unicorn_rails executable
|
62
|
+
# * :argv - a deep copy of the ARGV array the executable originally saw
|
63
|
+
# * :cwd - the working directory of the application, this is where
|
64
|
+
# you originally started Unicorn.
|
65
|
+
#
|
66
|
+
# The following example may be used in your Unicorn config file to
|
67
|
+
# change your working directory during a config reload (HUP) without
|
68
|
+
# upgrading or restarting:
|
69
|
+
#
|
70
|
+
# Dir.chdir(Unicorn::HttpServer::START_CTX[:cwd] = path)
|
71
|
+
#
|
72
|
+
# To change your unicorn executable to a different path without downtime,
|
73
|
+
# you can set the following in your Unicorn config file, HUP and then
|
74
|
+
# continue with the traditional USR2 + QUIT upgrade steps:
|
75
|
+
#
|
76
|
+
# Unicorn::HttpServer::START_CTX[0] = "/home/bofh/1.9.2/bin/unicorn"
|
56
77
|
START_CTX = {
|
57
78
|
:argv => ARGV.map { |arg| arg.dup },
|
58
79
|
# don't rely on Dir.pwd here since it's not symlink-aware, and
|
data/lib/unicorn/const.rb
CHANGED
@@ -7,7 +7,7 @@ module Unicorn
|
|
7
7
|
# gave about a 3% to 10% performance improvement over using the strings directly.
|
8
8
|
# Symbols did not really improve things much compared to constants.
|
9
9
|
module Const
|
10
|
-
UNICORN_VERSION="0.93.
|
10
|
+
UNICORN_VERSION="0.93.2"
|
11
11
|
|
12
12
|
DEFAULT_HOST = "0.0.0.0" # default TCP listen host address
|
13
13
|
DEFAULT_PORT = 8080 # default TCP listen port
|
data/lib/unicorn/tee_input.rb
CHANGED
@@ -41,6 +41,26 @@ module Unicorn
|
|
41
41
|
@size = tmp_size
|
42
42
|
end
|
43
43
|
|
44
|
+
# call-seq:
|
45
|
+
# ios = env['rack.input']
|
46
|
+
# ios.read([length [, buffer ]]) => string, buffer, or nil
|
47
|
+
#
|
48
|
+
# Reads at most length bytes from the I/O stream, or to the end of
|
49
|
+
# file if length is omitted or is nil. length must be a non-negative
|
50
|
+
# integer or nil. If the optional buffer argument is present, it
|
51
|
+
# must reference a String, which will receive the data.
|
52
|
+
#
|
53
|
+
# At end of file, it returns nil or "" depend on length.
|
54
|
+
# ios.read() and ios.read(nil) returns "".
|
55
|
+
# ios.read(length [, buffer]) returns nil.
|
56
|
+
#
|
57
|
+
# If the Content-Length of the HTTP request is known (as is the common
|
58
|
+
# case for POST requests), then ios.read(length [, buffer]) will block
|
59
|
+
# until the specified length is read (or it is the last chunk).
|
60
|
+
# Otherwise, for uncommon "Transfer-Encoding: chunked" requests,
|
61
|
+
# ios.read(length [, buffer]) will return immediately if there is
|
62
|
+
# any data and only block when nothing is available (providing
|
63
|
+
# IO#readpartial semantics).
|
44
64
|
def read(*args)
|
45
65
|
socket or return @tmp.read(*args)
|
46
66
|
|
@@ -55,9 +75,9 @@ module Unicorn
|
|
55
75
|
rv = args.shift || @buf2.dup
|
56
76
|
diff = tmp_size - @tmp.pos
|
57
77
|
if 0 == diff
|
58
|
-
tee(length, rv)
|
78
|
+
ensure_length(tee(length, rv), length)
|
59
79
|
else
|
60
|
-
@tmp.read(diff > length ? length : diff, rv)
|
80
|
+
ensure_length(@tmp.read(diff > length ? length : diff, rv), length)
|
61
81
|
end
|
62
82
|
end
|
63
83
|
end
|
@@ -130,5 +150,24 @@ module Unicorn
|
|
130
150
|
StringIO === @tmp ? @tmp.size : @tmp.stat.size
|
131
151
|
end
|
132
152
|
|
153
|
+
# tee()s into +buf+ until it is of +length+ bytes (or until
|
154
|
+
# we've reached the Content-Length of the request body).
|
155
|
+
# Returns +buf+ (the exact object, not a duplicate)
|
156
|
+
# To continue supporting applications that need near-real-time
|
157
|
+
# streaming input bodies, this is a no-op for
|
158
|
+
# "Transfer-Encoding: chunked" requests.
|
159
|
+
def ensure_length(buf, length)
|
160
|
+
# @size is nil for chunked bodies, so we can't ensure length for those
|
161
|
+
# since they could be streaming bidirectionally and we don't want to
|
162
|
+
# block the caller in that case.
|
163
|
+
return buf if buf.nil? || @size.nil?
|
164
|
+
|
165
|
+
while buf.size < length && @size != @tmp.pos
|
166
|
+
buf << tee(length - buf.size, @buf2)
|
167
|
+
end
|
168
|
+
|
169
|
+
buf
|
170
|
+
end
|
171
|
+
|
133
172
|
end
|
134
173
|
end
|
data/test/unit/test_tee_input.rb
CHANGED
@@ -104,6 +104,22 @@ class TestTeeInput < Test::Unit::TestCase
|
|
104
104
|
assert_equal Unicorn::Const::MAX_BODY + 1, ti.size
|
105
105
|
end
|
106
106
|
|
107
|
+
def test_read_in_full_if_content_length
|
108
|
+
a, b = 300, 3
|
109
|
+
init_parser('.' * b, 300)
|
110
|
+
assert_equal 300, @parser.content_length
|
111
|
+
ti = Unicorn::TeeInput.new(@rd, @env, @parser, @buf)
|
112
|
+
pid = fork {
|
113
|
+
@wr.write('.' * 197)
|
114
|
+
sleep 1 # still a *potential* race here that would make the test moot...
|
115
|
+
@wr.write('.' * 100)
|
116
|
+
}
|
117
|
+
assert_equal a, ti.read(a).size
|
118
|
+
_, status = Process.waitpid2(pid)
|
119
|
+
assert status.success?
|
120
|
+
@wr.close
|
121
|
+
end
|
122
|
+
|
107
123
|
def test_big_body_multi
|
108
124
|
init_parser('.', Unicorn::Const::MAX_BODY + 1)
|
109
125
|
ti = Unicorn::TeeInput.new(@rd, @env, @parser, @buf)
|
@@ -167,6 +183,36 @@ class TestTeeInput < Test::Unit::TestCase
|
|
167
183
|
assert status.success?
|
168
184
|
end
|
169
185
|
|
186
|
+
def test_chunked_ping_pong
|
187
|
+
@parser = Unicorn::HttpParser.new
|
188
|
+
@buf = "POST / HTTP/1.1\r\n" \
|
189
|
+
"Host: localhost\r\n" \
|
190
|
+
"Transfer-Encoding: chunked\r\n" \
|
191
|
+
"\r\n"
|
192
|
+
assert_equal @env, @parser.headers(@env, @buf)
|
193
|
+
assert_equal "", @buf
|
194
|
+
chunks = %w(aa bbb cccc dddd eeee)
|
195
|
+
rd, wr = IO.pipe
|
196
|
+
|
197
|
+
pid = fork {
|
198
|
+
chunks.each do |chunk|
|
199
|
+
rd.read(1) == "." and
|
200
|
+
@wr.write("#{'%x' % [ chunk.size]}\r\n#{chunk}\r\n")
|
201
|
+
end
|
202
|
+
@wr.write("0\r\n")
|
203
|
+
}
|
204
|
+
ti = Unicorn::TeeInput.new(@rd, @env, @parser, @buf)
|
205
|
+
assert_nil @parser.content_length
|
206
|
+
assert_nil ti.instance_eval { @size }
|
207
|
+
assert ! @parser.body_eof?
|
208
|
+
chunks.each do |chunk|
|
209
|
+
wr.write('.')
|
210
|
+
assert_equal chunk, ti.read(16384)
|
211
|
+
end
|
212
|
+
_, status = Process.waitpid2(pid)
|
213
|
+
assert status.success?
|
214
|
+
end
|
215
|
+
|
170
216
|
private
|
171
217
|
|
172
218
|
def init_parser(body, size = nil)
|
data/unicorn.gemspec
CHANGED
@@ -14,7 +14,7 @@ Gem::Specification.new do |s|
|
|
14
14
|
s.name = %q{unicorn}
|
15
15
|
s.version = ENV["VERSION"]
|
16
16
|
|
17
|
-
s.authors = ["
|
17
|
+
s.authors = ["Unicorn developers"]
|
18
18
|
s.date = Time.now.utc.strftime('%Y-%m-%d')
|
19
19
|
s.description = File.read("README").split(/\n\n/)[1]
|
20
20
|
s.email = %q{mongrel-unicorn@rubyforge.org}
|
metadata
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: unicorn
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.93.
|
4
|
+
version: 0.93.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
-
|
7
|
+
- Unicorn developers
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-10-
|
12
|
+
date: 2009-10-07 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|