unicorn 0.9.2 → 0.90.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|