unicorn 0.9.2 → 0.90.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.
- data/.gitignore +1 -0
- data/CHANGELOG +3 -0
- data/GNUmakefile +7 -7
- data/Manifest +20 -23
- data/README +16 -13
- data/TODO +5 -3
- data/bin/unicorn +1 -1
- data/bin/unicorn_rails +1 -1
- data/ext/unicorn_http/c_util.h +107 -0
- data/ext/unicorn_http/common_field_optimization.h +110 -0
- data/ext/unicorn_http/ext_help.h +41 -5
- data/ext/unicorn_http/extconf.rb +3 -1
- data/ext/unicorn_http/global_variables.h +93 -0
- data/ext/unicorn_http/unicorn_http.c +2123 -326
- data/ext/unicorn_http/unicorn_http.rl +488 -87
- data/ext/unicorn_http/unicorn_http_common.rl +12 -1
- data/lib/unicorn.rb +0 -2
- data/lib/unicorn/app/exec_cgi.rb +0 -1
- data/lib/unicorn/app/inetd.rb +2 -0
- data/lib/unicorn/app/old_rails/static.rb +0 -2
- data/lib/unicorn/const.rb +1 -5
- data/lib/unicorn/http_request.rb +13 -29
- data/lib/unicorn/http_response.rb +1 -1
- data/lib/unicorn/tee_input.rb +48 -46
- data/lib/unicorn/util.rb +3 -3
- data/test/benchmark/README +0 -5
- data/test/exec/test_exec.rb +4 -0
- data/test/rails/{app-2.3.2.1 → app-2.3.3.1}/.gitignore +0 -0
- data/test/rails/{app-2.3.2.1 → app-2.3.3.1}/Rakefile +0 -0
- data/test/rails/{app-2.3.2.1 → app-2.3.3.1}/app/controllers/application_controller.rb +0 -0
- data/test/rails/{app-2.3.2.1 → app-2.3.3.1}/app/controllers/foo_controller.rb +0 -0
- data/test/rails/{app-2.3.2.1 → app-2.3.3.1}/app/helpers/application_helper.rb +0 -0
- data/test/rails/{app-2.3.2.1 → app-2.3.3.1}/config/boot.rb +0 -0
- data/test/rails/{app-2.3.2.1 → app-2.3.3.1}/config/database.yml +0 -0
- data/test/rails/{app-2.3.2.1 → app-2.3.3.1}/config/environment.rb +0 -0
- data/test/rails/{app-2.3.2.1 → app-2.3.3.1}/config/environments/development.rb +0 -0
- data/test/rails/{app-2.3.2.1 → app-2.3.3.1}/config/environments/production.rb +0 -0
- data/test/rails/{app-2.3.2.1 → app-2.3.3.1}/config/routes.rb +0 -0
- data/test/rails/{app-2.3.2.1 → app-2.3.3.1}/db/.gitignore +0 -0
- data/test/rails/{app-2.3.2.1 → app-2.3.3.1}/public/404.html +0 -0
- data/test/rails/{app-2.3.2.1 → app-2.3.3.1}/public/500.html +0 -0
- data/test/unit/test_http_parser.rb +112 -47
- data/test/unit/test_http_parser_ng.rb +284 -0
- data/test/unit/test_request.rb +25 -7
- data/test/unit/test_response.rb +11 -0
- data/test/unit/test_server.rb +7 -2
- data/test/unit/test_signals.rb +2 -0
- data/test/unit/test_tee_input.rb +118 -2
- data/test/unit/test_upload.rb +1 -1
- data/test/unit/test_util.rb +5 -0
- data/unicorn.gemspec +6 -6
- metadata +33 -37
- data/ext/unicorn_http/unicorn_http.h +0 -1289
- data/lib/unicorn/chunked_reader.rb +0 -77
- data/lib/unicorn/trailer_parser.rb +0 -52
- data/test/benchmark/big_request.rb +0 -44
- data/test/benchmark/request.rb +0 -56
- data/test/benchmark/response.rb +0 -30
- data/test/unit/test_chunked_reader.rb +0 -123
- data/test/unit/test_trailer_parser.rb +0 -52
@@ -0,0 +1,284 @@
|
|
1
|
+
# coding: binary
|
2
|
+
require 'test/test_helper'
|
3
|
+
require 'digest/md5'
|
4
|
+
|
5
|
+
include Unicorn
|
6
|
+
|
7
|
+
class HttpParserNgTest < Test::Unit::TestCase
|
8
|
+
|
9
|
+
def setup
|
10
|
+
@parser = HttpParser.new
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_identity_step_headers
|
14
|
+
req = {}
|
15
|
+
str = "PUT / HTTP/1.1\r\n"
|
16
|
+
assert ! @parser.headers(req, str)
|
17
|
+
str << "Content-Length: 123\r\n"
|
18
|
+
assert ! @parser.headers(req, str)
|
19
|
+
str << "\r\n"
|
20
|
+
assert_equal req.object_id, @parser.headers(req, str).object_id
|
21
|
+
assert_equal '123', req['CONTENT_LENGTH']
|
22
|
+
assert_equal 0, str.size
|
23
|
+
assert ! @parser.keepalive?
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_identity_oneshot_header
|
27
|
+
req = {}
|
28
|
+
str = "PUT / HTTP/1.1\r\nContent-Length: 123\r\n\r\n"
|
29
|
+
assert_equal req.object_id, @parser.headers(req, str).object_id
|
30
|
+
assert_equal '123', req['CONTENT_LENGTH']
|
31
|
+
assert_equal 0, str.size
|
32
|
+
assert ! @parser.keepalive?
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_identity_oneshot_header_with_body
|
36
|
+
body = ('a' * 123).freeze
|
37
|
+
req = {}
|
38
|
+
str = "PUT / HTTP/1.1\r\n" \
|
39
|
+
"Content-Length: #{body.length}\r\n" \
|
40
|
+
"\r\n#{body}"
|
41
|
+
assert_equal req.object_id, @parser.headers(req, str).object_id
|
42
|
+
assert_equal '123', req['CONTENT_LENGTH']
|
43
|
+
assert_equal 123, str.size
|
44
|
+
assert_equal body, str
|
45
|
+
tmp = ''
|
46
|
+
assert_nil @parser.filter_body(tmp, str)
|
47
|
+
assert_equal 0, str.size
|
48
|
+
assert_equal tmp, body
|
49
|
+
assert_equal "", @parser.filter_body(tmp, str)
|
50
|
+
assert ! @parser.keepalive?
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_identity_oneshot_header_with_body_partial
|
54
|
+
str = "PUT / HTTP/1.1\r\nContent-Length: 123\r\n\r\na"
|
55
|
+
assert_equal Hash, @parser.headers({}, str).class
|
56
|
+
assert_equal 1, str.size
|
57
|
+
assert_equal 'a', str
|
58
|
+
tmp = ''
|
59
|
+
assert_nil @parser.filter_body(tmp, str)
|
60
|
+
assert_equal "", str
|
61
|
+
assert_equal "a", tmp
|
62
|
+
str << ' ' * 122
|
63
|
+
rv = @parser.filter_body(tmp, str)
|
64
|
+
assert_equal 122, tmp.size
|
65
|
+
assert_nil rv
|
66
|
+
assert_equal "", str
|
67
|
+
assert_equal str.object_id, @parser.filter_body(tmp, str).object_id
|
68
|
+
assert ! @parser.keepalive?
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_identity_oneshot_header_with_body_slop
|
72
|
+
str = "PUT / HTTP/1.1\r\nContent-Length: 1\r\n\r\naG"
|
73
|
+
assert_equal Hash, @parser.headers({}, str).class
|
74
|
+
assert_equal 2, str.size
|
75
|
+
assert_equal 'aG', str
|
76
|
+
tmp = ''
|
77
|
+
assert_nil @parser.filter_body(tmp, str)
|
78
|
+
assert_equal "G", str
|
79
|
+
assert_equal "G", @parser.filter_body(tmp, str)
|
80
|
+
assert_equal 1, tmp.size
|
81
|
+
assert_equal "a", tmp
|
82
|
+
assert ! @parser.keepalive?
|
83
|
+
end
|
84
|
+
|
85
|
+
def test_chunked
|
86
|
+
str = "PUT / HTTP/1.1\r\ntransfer-Encoding: chunked\r\n\r\n"
|
87
|
+
req = {}
|
88
|
+
assert_equal req, @parser.headers(req, str)
|
89
|
+
assert_equal 0, str.size
|
90
|
+
tmp = ""
|
91
|
+
assert_nil @parser.filter_body(tmp, "6")
|
92
|
+
assert_equal 0, tmp.size
|
93
|
+
assert_nil @parser.filter_body(tmp, rv = "\r\n")
|
94
|
+
assert_equal 0, rv.size
|
95
|
+
assert_equal 0, tmp.size
|
96
|
+
tmp = ""
|
97
|
+
assert_nil @parser.filter_body(tmp, "..")
|
98
|
+
assert_equal "..", tmp
|
99
|
+
assert_nil @parser.filter_body(tmp, "abcd\r\n0\r\n")
|
100
|
+
assert_equal "abcd", tmp
|
101
|
+
rv = "PUT"
|
102
|
+
assert_equal rv.object_id, @parser.filter_body(tmp, rv).object_id
|
103
|
+
assert_equal "PUT", rv
|
104
|
+
assert ! @parser.keepalive?
|
105
|
+
end
|
106
|
+
|
107
|
+
def test_two_chunks
|
108
|
+
str = "PUT / HTTP/1.1\r\ntransfer-Encoding: chunked\r\n\r\n"
|
109
|
+
req = {}
|
110
|
+
assert_equal req, @parser.headers(req, str)
|
111
|
+
assert_equal 0, str.size
|
112
|
+
tmp = ""
|
113
|
+
assert_nil @parser.filter_body(tmp, "6")
|
114
|
+
assert_equal 0, tmp.size
|
115
|
+
assert_nil @parser.filter_body(tmp, rv = "\r\n")
|
116
|
+
assert_equal "", rv
|
117
|
+
assert_equal 0, tmp.size
|
118
|
+
tmp = ""
|
119
|
+
assert_nil @parser.filter_body(tmp, "..")
|
120
|
+
assert_equal 2, tmp.size
|
121
|
+
assert_equal "..", tmp
|
122
|
+
assert_nil @parser.filter_body(tmp, "abcd\r\n1")
|
123
|
+
assert_equal "abcd", tmp
|
124
|
+
assert_nil @parser.filter_body(tmp, "\r")
|
125
|
+
assert_equal "", tmp
|
126
|
+
assert_nil @parser.filter_body(tmp, "\n")
|
127
|
+
assert_equal "", tmp
|
128
|
+
assert_nil @parser.filter_body(tmp, "z")
|
129
|
+
assert_equal "z", tmp
|
130
|
+
assert_nil @parser.filter_body(tmp, "\r\n")
|
131
|
+
assert_nil @parser.filter_body(tmp, "0")
|
132
|
+
assert_nil @parser.filter_body(tmp, "\r")
|
133
|
+
rv = @parser.filter_body(tmp, buf = "\nGET")
|
134
|
+
assert_equal "GET", rv
|
135
|
+
assert_equal buf.object_id, rv.object_id
|
136
|
+
assert ! @parser.keepalive?
|
137
|
+
end
|
138
|
+
|
139
|
+
def test_big_chunk
|
140
|
+
str = "PUT / HTTP/1.1\r\ntransfer-Encoding: chunked\r\n\r\n" \
|
141
|
+
"4000\r\nabcd"
|
142
|
+
req = {}
|
143
|
+
assert_equal req, @parser.headers(req, str)
|
144
|
+
tmp = ''
|
145
|
+
assert_nil @parser.filter_body(tmp, str)
|
146
|
+
assert_equal '', str
|
147
|
+
str = ' ' * 16300
|
148
|
+
assert_nil @parser.filter_body(tmp, str)
|
149
|
+
assert_equal '', str
|
150
|
+
str = ' ' * 80
|
151
|
+
assert_nil @parser.filter_body(tmp, str)
|
152
|
+
assert_equal '', str
|
153
|
+
assert ! @parser.body_eof?
|
154
|
+
assert_equal "", @parser.filter_body(tmp, "\r\n0\r\n")
|
155
|
+
assert @parser.body_eof?
|
156
|
+
assert ! @parser.keepalive?
|
157
|
+
end
|
158
|
+
|
159
|
+
def test_two_chunks_oneshot
|
160
|
+
str = "PUT / HTTP/1.1\r\ntransfer-Encoding: chunked\r\n\r\n" \
|
161
|
+
"1\r\na\r\n2\r\n..\r\n0\r\n"
|
162
|
+
req = {}
|
163
|
+
assert_equal req, @parser.headers(req, str)
|
164
|
+
tmp = ''
|
165
|
+
assert_nil @parser.filter_body(tmp, str)
|
166
|
+
assert_equal 'a..', tmp
|
167
|
+
rv = @parser.filter_body(tmp, str)
|
168
|
+
assert_equal rv.object_id, str.object_id
|
169
|
+
assert ! @parser.keepalive?
|
170
|
+
end
|
171
|
+
|
172
|
+
def test_trailers
|
173
|
+
str = "PUT / HTTP/1.1\r\n" \
|
174
|
+
"Trailer: Content-MD5\r\n" \
|
175
|
+
"transfer-Encoding: chunked\r\n\r\n" \
|
176
|
+
"1\r\na\r\n2\r\n..\r\n0\r\n"
|
177
|
+
req = {}
|
178
|
+
assert_equal req, @parser.headers(req, str)
|
179
|
+
assert_equal 'Content-MD5', req['HTTP_TRAILER']
|
180
|
+
assert_nil req['HTTP_CONTENT_MD5']
|
181
|
+
tmp = ''
|
182
|
+
assert_nil @parser.filter_body(tmp, str)
|
183
|
+
assert_equal 'a..', tmp
|
184
|
+
md5_b64 = [ Digest::MD5.digest(tmp) ].pack('m').strip.freeze
|
185
|
+
rv = @parser.filter_body(tmp, str)
|
186
|
+
assert_equal rv.object_id, str.object_id
|
187
|
+
assert_equal '', str
|
188
|
+
md5_hdr = "Content-MD5: #{md5_b64}\r\n".freeze
|
189
|
+
str << md5_hdr
|
190
|
+
assert_nil @parser.trailers(req, str)
|
191
|
+
assert_equal md5_b64, req['HTTP_CONTENT_MD5']
|
192
|
+
assert_equal "CONTENT_MD5: #{md5_b64}\r\n", str
|
193
|
+
assert_nil @parser.trailers(req, str << "\r")
|
194
|
+
assert_equal req, @parser.trailers(req, str << "\nGET / ")
|
195
|
+
assert_equal "GET / ", str
|
196
|
+
assert ! @parser.keepalive?
|
197
|
+
end
|
198
|
+
|
199
|
+
def test_max_chunk
|
200
|
+
str = "PUT / HTTP/1.1\r\n" \
|
201
|
+
"transfer-Encoding: chunked\r\n\r\n" \
|
202
|
+
"#{HttpParser::CHUNK_MAX.to_s(16)}\r\na\r\n2\r\n..\r\n0\r\n"
|
203
|
+
req = {}
|
204
|
+
assert_equal req, @parser.headers(req, str)
|
205
|
+
assert_nil @parser.content_length
|
206
|
+
assert_nothing_raised { @parser.filter_body('', str) }
|
207
|
+
assert ! @parser.keepalive?
|
208
|
+
end
|
209
|
+
|
210
|
+
def test_max_body
|
211
|
+
n = HttpParser::LENGTH_MAX
|
212
|
+
str = "PUT / HTTP/1.1\r\nContent-Length: #{n}\r\n\r\n"
|
213
|
+
req = {}
|
214
|
+
assert_nothing_raised { @parser.headers(req, str) }
|
215
|
+
assert_equal n, req['CONTENT_LENGTH'].to_i
|
216
|
+
assert ! @parser.keepalive?
|
217
|
+
end
|
218
|
+
|
219
|
+
def test_overflow_chunk
|
220
|
+
n = HttpParser::CHUNK_MAX + 1
|
221
|
+
str = "PUT / HTTP/1.1\r\n" \
|
222
|
+
"transfer-Encoding: chunked\r\n\r\n" \
|
223
|
+
"#{n.to_s(16)}\r\na\r\n2\r\n..\r\n0\r\n"
|
224
|
+
req = {}
|
225
|
+
assert_equal req, @parser.headers(req, str)
|
226
|
+
assert_nil @parser.content_length
|
227
|
+
assert_raise(HttpParserError) { @parser.filter_body('', str) }
|
228
|
+
assert ! @parser.keepalive?
|
229
|
+
end
|
230
|
+
|
231
|
+
def test_overflow_content_length
|
232
|
+
n = HttpParser::LENGTH_MAX + 1
|
233
|
+
str = "PUT / HTTP/1.1\r\nContent-Length: #{n}\r\n\r\n"
|
234
|
+
assert_raise(HttpParserError) { @parser.headers({}, str) }
|
235
|
+
assert ! @parser.keepalive?
|
236
|
+
end
|
237
|
+
|
238
|
+
def test_bad_chunk
|
239
|
+
str = "PUT / HTTP/1.1\r\n" \
|
240
|
+
"transfer-Encoding: chunked\r\n\r\n" \
|
241
|
+
"#zzz\r\na\r\n2\r\n..\r\n0\r\n"
|
242
|
+
req = {}
|
243
|
+
assert_equal req, @parser.headers(req, str)
|
244
|
+
assert_nil @parser.content_length
|
245
|
+
assert_raise(HttpParserError) { @parser.filter_body('', str) }
|
246
|
+
assert ! @parser.keepalive?
|
247
|
+
end
|
248
|
+
|
249
|
+
def test_bad_content_length
|
250
|
+
str = "PUT / HTTP/1.1\r\nContent-Length: 7ff\r\n\r\n"
|
251
|
+
assert_raise(HttpParserError) { @parser.headers({}, str) }
|
252
|
+
assert ! @parser.keepalive?
|
253
|
+
end
|
254
|
+
|
255
|
+
def test_bad_trailers
|
256
|
+
str = "PUT / HTTP/1.1\r\n" \
|
257
|
+
"Trailer: Transfer-Encoding\r\n" \
|
258
|
+
"transfer-Encoding: chunked\r\n\r\n" \
|
259
|
+
"1\r\na\r\n2\r\n..\r\n0\r\n"
|
260
|
+
req = {}
|
261
|
+
assert_equal req, @parser.headers(req, str)
|
262
|
+
assert_equal 'Transfer-Encoding', req['HTTP_TRAILER']
|
263
|
+
tmp = ''
|
264
|
+
assert_nil @parser.filter_body(tmp, str)
|
265
|
+
assert_equal 'a..', tmp
|
266
|
+
assert_equal '', str
|
267
|
+
str << "Transfer-Encoding: identity\r\n\r\n"
|
268
|
+
assert_raise(HttpParserError) { @parser.trailers(req, str) }
|
269
|
+
assert ! @parser.keepalive?
|
270
|
+
end
|
271
|
+
|
272
|
+
def test_repeat_headers
|
273
|
+
str = "PUT / HTTP/1.1\r\n" \
|
274
|
+
"Trailer: Content-MD5\r\n" \
|
275
|
+
"Trailer: Content-SHA1\r\n" \
|
276
|
+
"transfer-Encoding: chunked\r\n\r\n" \
|
277
|
+
"1\r\na\r\n2\r\n..\r\n0\r\n"
|
278
|
+
req = {}
|
279
|
+
assert_equal req, @parser.headers(req, str)
|
280
|
+
assert_equal 'Content-MD5,Content-SHA1', req['HTTP_TRAILER']
|
281
|
+
assert ! @parser.keepalive?
|
282
|
+
end
|
283
|
+
|
284
|
+
end
|
data/test/unit/test_request.rb
CHANGED
@@ -2,13 +2,6 @@
|
|
2
2
|
# You can redistribute it and/or modify it under the same terms as Ruby.
|
3
3
|
|
4
4
|
require 'test/test_helper'
|
5
|
-
begin
|
6
|
-
require 'rack'
|
7
|
-
require 'rack/lint'
|
8
|
-
rescue LoadError
|
9
|
-
warn "Unable to load rack, skipping test"
|
10
|
-
exit 0
|
11
|
-
end
|
12
5
|
|
13
6
|
include Unicorn
|
14
7
|
|
@@ -120,6 +113,31 @@ class RequestTest < Test::Unit::TestCase
|
|
120
113
|
assert_nothing_raised { res = @lint.call(env) }
|
121
114
|
end
|
122
115
|
|
116
|
+
def test_no_content_stringio
|
117
|
+
client = MockRequest.new("GET / HTTP/1.1\r\nHost: foo\r\n\r\n")
|
118
|
+
res = env = nil
|
119
|
+
assert_nothing_raised { env = @request.read(client) }
|
120
|
+
assert_equal StringIO, env['rack.input'].class
|
121
|
+
end
|
122
|
+
|
123
|
+
def test_zero_content_stringio
|
124
|
+
client = MockRequest.new("PUT / HTTP/1.1\r\n" \
|
125
|
+
"Content-Length: 0\r\n" \
|
126
|
+
"Host: foo\r\n\r\n")
|
127
|
+
res = env = nil
|
128
|
+
assert_nothing_raised { env = @request.read(client) }
|
129
|
+
assert_equal StringIO, env['rack.input'].class
|
130
|
+
end
|
131
|
+
|
132
|
+
def test_real_content_not_stringio
|
133
|
+
client = MockRequest.new("PUT / HTTP/1.1\r\n" \
|
134
|
+
"Content-Length: 1\r\n" \
|
135
|
+
"Host: foo\r\n\r\n")
|
136
|
+
res = env = nil
|
137
|
+
assert_nothing_raised { env = @request.read(client) }
|
138
|
+
assert_equal Unicorn::TeeInput, env['rack.input'].class
|
139
|
+
end
|
140
|
+
|
123
141
|
def test_rack_lint_put
|
124
142
|
client = MockRequest.new(
|
125
143
|
"PUT / HTTP/1.1\r\n" \
|
data/test/unit/test_response.rb
CHANGED
@@ -94,4 +94,15 @@ class ResponseTest < Test::Unit::TestCase
|
|
94
94
|
assert_match(expect_body, out.string.split(/\r\n/).last)
|
95
95
|
end
|
96
96
|
|
97
|
+
def test_unknown_status_pass_through
|
98
|
+
out = StringIO.new
|
99
|
+
HttpResponse.write(out,["666 I AM THE BEAST", {}, [] ])
|
100
|
+
assert out.closed?
|
101
|
+
headers = out.string.split(/\r\n\r\n/).first.split(/\r\n/)
|
102
|
+
assert %r{\AHTTP/\d\.\d 666 I AM THE BEAST\z}.match(headers[0])
|
103
|
+
status = headers.grep(/\AStatus:/i).first
|
104
|
+
assert status
|
105
|
+
assert_equal "Status: 666 I AM THE BEAST", status
|
106
|
+
end
|
107
|
+
|
97
108
|
end
|
data/test/unit/test_server.rb
CHANGED
@@ -33,6 +33,8 @@ class WebServerTest < Test::Unit::TestCase
|
|
33
33
|
|
34
34
|
def teardown
|
35
35
|
redirect_test_io do
|
36
|
+
wait_workers_ready("test_stderr.#$$.log", 1)
|
37
|
+
File.truncate("test_stderr.#$$.log", 0)
|
36
38
|
@server.stop(true)
|
37
39
|
end
|
38
40
|
end
|
@@ -53,8 +55,10 @@ class WebServerTest < Test::Unit::TestCase
|
|
53
55
|
end
|
54
56
|
results = hit(["http://localhost:#@port/"])
|
55
57
|
worker_pid = results[0].to_i
|
58
|
+
assert worker_pid != 0
|
56
59
|
tmp.sysseek(0)
|
57
60
|
loader_pid = tmp.sysread(4096).to_i
|
61
|
+
assert loader_pid != 0
|
58
62
|
assert_equal worker_pid, loader_pid
|
59
63
|
teardown
|
60
64
|
|
@@ -65,6 +69,7 @@ class WebServerTest < Test::Unit::TestCase
|
|
65
69
|
end
|
66
70
|
results = hit(["http://localhost:#@port/"])
|
67
71
|
worker_pid = results[0].to_i
|
72
|
+
assert worker_pid != 0
|
68
73
|
tmp.sysseek(0)
|
69
74
|
loader_pid = tmp.sysread(4096).to_i
|
70
75
|
assert_equal $$, loader_pid
|
@@ -158,9 +163,9 @@ class WebServerTest < Test::Unit::TestCase
|
|
158
163
|
do_test(long, Unicorn::Const::CHUNK_SIZE * 2 -400)
|
159
164
|
end
|
160
165
|
|
161
|
-
def
|
166
|
+
def test_file_streamed_request_bad_body
|
162
167
|
body = "a" * (Unicorn::Const::MAX_BODY * 2)
|
163
|
-
long = "GET /test HTTP/1.1\r\nContent-
|
168
|
+
long = "GET /test HTTP/1.1\r\nContent-ength: #{body.length}\r\n\r\n" + body
|
164
169
|
assert_raises(EOFError,Errno::ECONNRESET,Errno::EPIPE,Errno::EINVAL,
|
165
170
|
Errno::EBADF) {
|
166
171
|
do_test(long, Unicorn::Const::CHUNK_SIZE * 2 -400)
|
data/test/unit/test_signals.rb
CHANGED
@@ -53,8 +53,10 @@ class SignalsTest < Test::Unit::TestCase
|
|
53
53
|
buf =~ /\bX-Pid: (\d+)\b/ or raise Exception
|
54
54
|
child = $1.to_i
|
55
55
|
wait_master_ready("test_stderr.#{pid}.log")
|
56
|
+
wait_workers_ready("test_stderr.#{pid}.log", 1)
|
56
57
|
Process.kill(:KILL, pid)
|
57
58
|
Process.waitpid(pid)
|
59
|
+
File.unlink("test_stderr.#{pid}.log", "test_stdout.#{pid}.log")
|
58
60
|
t0 = Time.now
|
59
61
|
end
|
60
62
|
assert child
|
data/test/unit/test_tee_input.rb
CHANGED
@@ -26,7 +26,8 @@ class TestTeeInput < Test::Unit::TestCase
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def test_gets_long
|
29
|
-
|
29
|
+
init_parser("hello", 5 + (4096 * 4 * 3) + "#$/foo#$/".size)
|
30
|
+
ti = Unicorn::TeeInput.new(@rd, @env, @parser, @buf)
|
30
31
|
status = line = nil
|
31
32
|
pid = fork {
|
32
33
|
@rd.close
|
@@ -46,7 +47,8 @@ class TestTeeInput < Test::Unit::TestCase
|
|
46
47
|
end
|
47
48
|
|
48
49
|
def test_gets_short
|
49
|
-
|
50
|
+
init_parser("hello", 5 + "#$/foo".size)
|
51
|
+
ti = Unicorn::TeeInput.new(@rd, @env, @parser, @buf)
|
50
52
|
status = line = nil
|
51
53
|
pid = fork {
|
52
54
|
@rd.close
|
@@ -63,4 +65,118 @@ class TestTeeInput < Test::Unit::TestCase
|
|
63
65
|
assert status.success?
|
64
66
|
end
|
65
67
|
|
68
|
+
def test_small_body
|
69
|
+
init_parser('hello')
|
70
|
+
ti = Unicorn::TeeInput.new(@rd, @env, @parser, @buf)
|
71
|
+
assert_equal 0, @parser.content_length
|
72
|
+
assert @parser.body_eof?
|
73
|
+
assert_equal StringIO, ti.instance_eval { @tmp.class }
|
74
|
+
assert_equal 0, ti.instance_eval { @tmp.pos }
|
75
|
+
assert_equal 5, ti.size
|
76
|
+
assert_equal 'hello', ti.read
|
77
|
+
assert_equal '', ti.read
|
78
|
+
assert_nil ti.read(4096)
|
79
|
+
end
|
80
|
+
|
81
|
+
def test_read_with_buffer
|
82
|
+
init_parser('hello')
|
83
|
+
ti = Unicorn::TeeInput.new(@rd, @env, @parser, @buf)
|
84
|
+
buf = ''
|
85
|
+
rv = ti.read(4, buf)
|
86
|
+
assert_equal 'hell', rv
|
87
|
+
assert_equal 'hell', buf
|
88
|
+
assert_equal rv.object_id, buf.object_id
|
89
|
+
assert_equal 'o', ti.read
|
90
|
+
assert_equal nil, ti.read(5, buf)
|
91
|
+
assert_equal 0, ti.rewind
|
92
|
+
assert_equal 'hello', ti.read(5, buf)
|
93
|
+
assert_equal 'hello', buf
|
94
|
+
end
|
95
|
+
|
96
|
+
def test_big_body
|
97
|
+
init_parser('.' * Unicorn::Const::MAX_BODY << 'a')
|
98
|
+
ti = Unicorn::TeeInput.new(@rd, @env, @parser, @buf)
|
99
|
+
assert_equal 0, @parser.content_length
|
100
|
+
assert @parser.body_eof?
|
101
|
+
assert_kind_of File, ti.instance_eval { @tmp }
|
102
|
+
assert_equal 0, ti.instance_eval { @tmp.pos }
|
103
|
+
assert_equal Unicorn::Const::MAX_BODY + 1, ti.size
|
104
|
+
end
|
105
|
+
|
106
|
+
def test_big_body_multi
|
107
|
+
init_parser('.', Unicorn::Const::MAX_BODY + 1)
|
108
|
+
ti = Unicorn::TeeInput.new(@rd, @env, @parser, @buf)
|
109
|
+
assert_equal Unicorn::Const::MAX_BODY, @parser.content_length
|
110
|
+
assert ! @parser.body_eof?
|
111
|
+
assert_kind_of File, ti.instance_eval { @tmp }
|
112
|
+
assert_equal 0, ti.instance_eval { @tmp.pos }
|
113
|
+
assert_equal 1, ti.instance_eval { tmp_size }
|
114
|
+
assert_equal Unicorn::Const::MAX_BODY + 1, ti.size
|
115
|
+
nr = Unicorn::Const::MAX_BODY / 4
|
116
|
+
pid = fork {
|
117
|
+
@rd.close
|
118
|
+
nr.times { @wr.write('....') }
|
119
|
+
@wr.close
|
120
|
+
}
|
121
|
+
@wr.close
|
122
|
+
assert_equal '.', ti.read(1)
|
123
|
+
assert_equal Unicorn::Const::MAX_BODY + 1, ti.size
|
124
|
+
nr.times {
|
125
|
+
assert_equal '....', ti.read(4)
|
126
|
+
assert_equal Unicorn::Const::MAX_BODY + 1, ti.size
|
127
|
+
}
|
128
|
+
assert_nil ti.read(1)
|
129
|
+
status = nil
|
130
|
+
assert_nothing_raised { pid, status = Process.waitpid2(pid) }
|
131
|
+
assert status.success?
|
132
|
+
end
|
133
|
+
|
134
|
+
def test_chunked
|
135
|
+
@parser = Unicorn::HttpParser.new
|
136
|
+
@buf = "POST / HTTP/1.1\r\n" \
|
137
|
+
"Host: localhost\r\n" \
|
138
|
+
"Transfer-Encoding: chunked\r\n" \
|
139
|
+
"\r\n"
|
140
|
+
assert_equal @env, @parser.headers(@env, @buf)
|
141
|
+
assert_equal "", @buf
|
142
|
+
|
143
|
+
pid = fork {
|
144
|
+
@rd.close
|
145
|
+
5.times { @wr.write("5\r\nabcde\r\n") }
|
146
|
+
@wr.write("0\r\n")
|
147
|
+
}
|
148
|
+
@wr.close
|
149
|
+
ti = Unicorn::TeeInput.new(@rd, @env, @parser, @buf)
|
150
|
+
assert_nil @parser.content_length
|
151
|
+
assert_nil ti.instance_eval { @size }
|
152
|
+
assert ! @parser.body_eof?
|
153
|
+
assert_equal 25, ti.size
|
154
|
+
assert @parser.body_eof?
|
155
|
+
assert_equal 25, ti.instance_eval { @size }
|
156
|
+
assert_equal 0, ti.instance_eval { @tmp.pos }
|
157
|
+
assert_nothing_raised { ti.rewind }
|
158
|
+
assert_equal 0, ti.instance_eval { @tmp.pos }
|
159
|
+
assert_equal 'abcdeabcdeabcdeabcde', ti.read(20)
|
160
|
+
assert_equal 20, ti.instance_eval { @tmp.pos }
|
161
|
+
assert_nothing_raised { ti.rewind }
|
162
|
+
assert_equal 0, ti.instance_eval { @tmp.pos }
|
163
|
+
assert_kind_of File, ti.instance_eval { @tmp }
|
164
|
+
status = nil
|
165
|
+
assert_nothing_raised { pid, status = Process.waitpid2(pid) }
|
166
|
+
assert status.success?
|
167
|
+
end
|
168
|
+
|
169
|
+
private
|
170
|
+
|
171
|
+
def init_parser(body, size = nil)
|
172
|
+
@parser = Unicorn::HttpParser.new
|
173
|
+
body = body.to_s.freeze
|
174
|
+
@buf = "POST / HTTP/1.1\r\n" \
|
175
|
+
"Host: localhost\r\n" \
|
176
|
+
"Content-Length: #{size || body.size}\r\n" \
|
177
|
+
"\r\n#{body}"
|
178
|
+
assert_equal @env, @parser.headers(@env, @buf)
|
179
|
+
assert_equal body, @buf
|
180
|
+
end
|
181
|
+
|
66
182
|
end
|