rack 1.1.6 → 1.6.9
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 +7 -0
- data/COPYING +1 -1
- data/HISTORY.md +375 -0
- data/KNOWN-ISSUES +23 -0
- data/README.rdoc +312 -0
- data/Rakefile +124 -0
- data/SPEC +125 -32
- data/contrib/rack.png +0 -0
- data/contrib/rack.svg +150 -0
- data/contrib/rack_logo.svg +1 -1
- data/contrib/rdoc.css +412 -0
- data/example/protectedlobster.rb +1 -1
- data/lib/rack/auth/abstract/handler.rb +4 -4
- data/lib/rack/auth/abstract/request.rb +7 -5
- data/lib/rack/auth/basic.rb +1 -1
- data/lib/rack/auth/digest/md5.rb +7 -3
- data/lib/rack/auth/digest/nonce.rb +1 -1
- data/lib/rack/auth/digest/params.rb +7 -9
- data/lib/rack/auth/digest/request.rb +10 -9
- data/lib/rack/backports/uri/common_18.rb +56 -0
- data/lib/rack/backports/uri/common_192.rb +52 -0
- data/lib/rack/backports/uri/common_193.rb +29 -0
- data/lib/rack/body_proxy.rb +39 -0
- data/lib/rack/builder.rb +106 -22
- data/lib/rack/cascade.rb +17 -6
- data/lib/rack/chunked.rb +44 -24
- data/lib/rack/commonlogger.rb +36 -13
- data/lib/rack/conditionalget.rb +49 -17
- data/lib/rack/config.rb +5 -0
- data/lib/rack/content_length.rb +14 -6
- data/lib/rack/content_type.rb +7 -1
- data/lib/rack/deflater.rb +73 -15
- data/lib/rack/directory.rb +18 -8
- data/lib/rack/etag.rb +59 -9
- data/lib/rack/file.rb +106 -44
- data/lib/rack/handler/cgi.rb +11 -11
- data/lib/rack/handler/fastcgi.rb +18 -6
- data/lib/rack/handler/lsws.rb +2 -4
- data/lib/rack/handler/mongrel.rb +22 -6
- data/lib/rack/handler/scgi.rb +16 -8
- data/lib/rack/handler/thin.rb +19 -4
- data/lib/rack/handler/webrick.rb +72 -19
- data/lib/rack/handler.rb +47 -14
- data/lib/rack/head.rb +10 -2
- data/lib/rack/lint.rb +260 -75
- data/lib/rack/lobster.rb +13 -8
- data/lib/rack/lock.rb +13 -3
- data/lib/rack/logger.rb +0 -2
- data/lib/rack/methodoverride.rb +27 -8
- data/lib/rack/mime.rb +625 -167
- data/lib/rack/mock.rb +78 -53
- data/lib/rack/multipart/generator.rb +93 -0
- data/lib/rack/multipart/parser.rb +253 -0
- data/lib/rack/multipart/uploaded_file.rb +34 -0
- data/lib/rack/multipart.rb +34 -0
- data/lib/rack/nulllogger.rb +21 -2
- data/lib/rack/recursive.rb +10 -5
- data/lib/rack/reloader.rb +3 -2
- data/lib/rack/request.rb +201 -74
- data/lib/rack/response.rb +41 -28
- data/lib/rack/rewindable_input.rb +15 -11
- data/lib/rack/runtime.rb +16 -3
- data/lib/rack/sendfile.rb +47 -29
- data/lib/rack/server.rb +223 -47
- data/lib/rack/session/abstract/id.rb +289 -30
- data/lib/rack/session/cookie.rb +133 -44
- data/lib/rack/session/memcache.rb +30 -56
- data/lib/rack/session/pool.rb +19 -43
- data/lib/rack/showexceptions.rb +53 -15
- data/lib/rack/showstatus.rb +14 -7
- data/lib/rack/static.rb +124 -12
- data/lib/rack/tempfile_reaper.rb +22 -0
- data/lib/rack/urlmap.rb +49 -15
- data/lib/rack/utils/okjson.rb +600 -0
- data/lib/rack/utils.rb +363 -361
- data/lib/rack.rb +17 -23
- data/rack.gemspec +11 -20
- data/test/builder/anything.rb +5 -0
- data/test/builder/comment.ru +4 -0
- data/test/builder/end.ru +5 -0
- data/test/builder/line.ru +1 -0
- data/test/builder/options.ru +2 -0
- data/test/cgi/assets/folder/test.js +1 -0
- data/test/cgi/assets/fonts/font.eot +1 -0
- data/test/cgi/assets/images/image.png +1 -0
- data/test/cgi/assets/index.html +1 -0
- data/test/cgi/assets/javascripts/app.js +1 -0
- data/test/cgi/assets/stylesheets/app.css +1 -0
- data/test/cgi/lighttpd.conf +26 -0
- data/test/cgi/rackup_stub.rb +6 -0
- data/test/cgi/sample_rackup.ru +5 -0
- data/test/cgi/test +9 -0
- data/test/cgi/test+directory/test+file +1 -0
- data/test/cgi/test.fcgi +8 -0
- data/test/cgi/test.ru +5 -0
- data/test/gemloader.rb +10 -0
- data/test/multipart/bad_robots +259 -0
- data/test/multipart/binary +0 -0
- data/test/multipart/content_type_and_no_filename +6 -0
- data/test/multipart/empty +10 -0
- data/test/multipart/fail_16384_nofile +814 -0
- data/test/multipart/file1.txt +1 -0
- data/test/multipart/filename_and_modification_param +7 -0
- data/test/multipart/filename_and_no_name +6 -0
- data/test/multipart/filename_with_escaped_quotes +6 -0
- data/test/multipart/filename_with_escaped_quotes_and_modification_param +7 -0
- data/test/multipart/filename_with_null_byte +7 -0
- data/test/multipart/filename_with_percent_escaped_quotes +6 -0
- data/test/multipart/filename_with_unescaped_percentages +6 -0
- data/test/multipart/filename_with_unescaped_percentages2 +6 -0
- data/test/multipart/filename_with_unescaped_percentages3 +6 -0
- data/test/multipart/filename_with_unescaped_quotes +6 -0
- data/test/multipart/ie +6 -0
- data/test/multipart/invalid_character +6 -0
- data/test/multipart/mixed_files +21 -0
- data/test/multipart/nested +10 -0
- data/test/multipart/none +9 -0
- data/test/multipart/semicolon +6 -0
- data/test/multipart/text +15 -0
- data/test/multipart/three_files_three_fields +31 -0
- data/test/multipart/webkit +32 -0
- data/test/rackup/config.ru +31 -0
- data/test/registering_handler/rack/handler/registering_myself.rb +8 -0
- data/test/{spec_rack_auth_basic.rb → spec_auth_basic.rb} +23 -15
- data/test/{spec_rack_auth_digest.rb → spec_auth_digest.rb} +56 -29
- data/test/spec_body_proxy.rb +85 -0
- data/test/spec_builder.rb +223 -0
- data/test/{spec_rack_cascade.rb → spec_cascade.rb} +28 -15
- data/test/{spec_rack_cgi.rb → spec_cgi.rb} +44 -31
- data/test/spec_chunked.rb +101 -0
- data/test/spec_commonlogger.rb +93 -0
- data/test/spec_conditionalget.rb +102 -0
- data/test/{spec_rack_config.rb → spec_config.rb} +6 -8
- data/test/spec_content_length.rb +85 -0
- data/test/spec_content_type.rb +45 -0
- data/test/spec_deflater.rb +339 -0
- data/test/{spec_rack_directory.rb → spec_directory.rb} +37 -10
- data/test/spec_etag.rb +107 -0
- data/test/{spec_rack_fastcgi.rb → spec_fastcgi.rb} +47 -29
- data/test/spec_file.rb +221 -0
- data/test/spec_handler.rb +72 -0
- data/test/spec_head.rb +45 -0
- data/test/{spec_rack_lint.rb → spec_lint.rb} +82 -60
- data/test/spec_lobster.rb +58 -0
- data/test/spec_lock.rb +164 -0
- data/test/spec_logger.rb +23 -0
- data/test/spec_methodoverride.rb +95 -0
- data/test/spec_mime.rb +51 -0
- data/test/{spec_rack_mock.rb → spec_mock.rb} +92 -38
- data/test/{spec_rack_mongrel.rb → spec_mongrel.rb} +46 -53
- data/test/spec_multipart.rb +600 -0
- data/test/spec_nulllogger.rb +20 -0
- data/test/spec_recursive.rb +72 -0
- data/test/spec_request.rb +1227 -0
- data/test/spec_response.rb +407 -0
- data/test/spec_rewindable_input.rb +118 -0
- data/test/spec_runtime.rb +49 -0
- data/test/spec_sendfile.rb +130 -0
- data/test/spec_server.rb +167 -0
- data/test/spec_session_abstract_id.rb +53 -0
- data/test/spec_session_cookie.rb +410 -0
- data/test/{spec_rack_session_memcache.rb → spec_session_memcache.rb} +119 -71
- data/test/{spec_rack_session_pool.rb → spec_session_pool.rb} +106 -69
- data/test/spec_showexceptions.rb +85 -0
- data/test/spec_showstatus.rb +103 -0
- data/test/spec_static.rb +145 -0
- data/test/spec_tempfile_reaper.rb +63 -0
- data/test/{spec_rack_thin.rb → spec_thin.rb} +35 -35
- data/test/{spec_rack_urlmap.rb → spec_urlmap.rb} +40 -19
- data/test/spec_utils.rb +647 -0
- data/test/spec_version.rb +17 -0
- data/test/spec_webrick.rb +184 -0
- data/test/static/another/index.html +1 -0
- data/test/static/index.html +1 -0
- data/test/testrequest.rb +78 -0
- data/test/unregistered_handler/rack/handler/unregistered.rb +7 -0
- data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +7 -0
- metadata +220 -239
- data/RDOX +0 -0
- data/README +0 -592
- data/lib/rack/adapter/camping.rb +0 -22
- data/test/spec_auth.rb +0 -57
- data/test/spec_rack_builder.rb +0 -84
- data/test/spec_rack_camping.rb +0 -55
- data/test/spec_rack_chunked.rb +0 -62
- data/test/spec_rack_commonlogger.rb +0 -61
- data/test/spec_rack_conditionalget.rb +0 -41
- data/test/spec_rack_content_length.rb +0 -43
- data/test/spec_rack_content_type.rb +0 -30
- data/test/spec_rack_deflater.rb +0 -127
- data/test/spec_rack_etag.rb +0 -17
- data/test/spec_rack_file.rb +0 -75
- data/test/spec_rack_handler.rb +0 -43
- data/test/spec_rack_head.rb +0 -30
- data/test/spec_rack_lobster.rb +0 -45
- data/test/spec_rack_lock.rb +0 -38
- data/test/spec_rack_logger.rb +0 -21
- data/test/spec_rack_methodoverride.rb +0 -60
- data/test/spec_rack_nulllogger.rb +0 -13
- data/test/spec_rack_recursive.rb +0 -77
- data/test/spec_rack_request.rb +0 -594
- data/test/spec_rack_response.rb +0 -221
- data/test/spec_rack_rewindable_input.rb +0 -118
- data/test/spec_rack_runtime.rb +0 -35
- data/test/spec_rack_sendfile.rb +0 -86
- data/test/spec_rack_session_cookie.rb +0 -92
- data/test/spec_rack_showexceptions.rb +0 -21
- data/test/spec_rack_showstatus.rb +0 -72
- data/test/spec_rack_static.rb +0 -37
- data/test/spec_rack_utils.rb +0 -557
- data/test/spec_rack_webrick.rb +0 -130
- data/test/spec_rackup.rb +0 -164
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
require 'test/spec'
|
|
2
1
|
require 'stringio'
|
|
3
|
-
|
|
2
|
+
require 'tempfile'
|
|
4
3
|
require 'rack/lint'
|
|
5
4
|
require 'rack/mock'
|
|
6
5
|
|
|
7
|
-
|
|
6
|
+
describe Rack::Lint do
|
|
8
7
|
def env(*args)
|
|
9
8
|
Rack::MockRequest.env_for("/", *args)
|
|
10
9
|
end
|
|
11
10
|
|
|
12
|
-
|
|
11
|
+
should "pass valid request" do
|
|
13
12
|
lambda {
|
|
14
13
|
Rack::Lint.new(lambda { |env|
|
|
15
14
|
[200, {"Content-type" => "test/plain", "Content-length" => "3"}, ["foo"]]
|
|
@@ -17,12 +16,12 @@ context "Rack::Lint" do
|
|
|
17
16
|
}.should.not.raise
|
|
18
17
|
end
|
|
19
18
|
|
|
20
|
-
|
|
19
|
+
should "notice fatal errors" do
|
|
21
20
|
lambda { Rack::Lint.new(nil).call }.should.raise(Rack::Lint::LintError).
|
|
22
21
|
message.should.match(/No env given/)
|
|
23
22
|
end
|
|
24
23
|
|
|
25
|
-
|
|
24
|
+
should "notice environment errors" do
|
|
26
25
|
lambda { Rack::Lint.new(nil).call 5 }.should.raise(Rack::Lint::LintError).
|
|
27
26
|
message.should.match(/not a Hash/)
|
|
28
27
|
|
|
@@ -76,6 +75,23 @@ context "Rack::Lint" do
|
|
|
76
75
|
}.should.raise(Rack::Lint::LintError).
|
|
77
76
|
message.should.equal("logger [] must respond to info")
|
|
78
77
|
|
|
78
|
+
lambda {
|
|
79
|
+
Rack::Lint.new(nil).call(env("rack.multipart.buffer_size" => 0))
|
|
80
|
+
}.should.raise(Rack::Lint::LintError).
|
|
81
|
+
message.should.equal("rack.multipart.buffer_size must be an Integer > 0 if specified")
|
|
82
|
+
|
|
83
|
+
lambda {
|
|
84
|
+
Rack::Lint.new(nil).call(env("rack.multipart.tempfile_factory" => Tempfile))
|
|
85
|
+
}.should.raise(Rack::Lint::LintError).
|
|
86
|
+
message.should.equal("rack.multipart.tempfile_factory must respond to #call")
|
|
87
|
+
|
|
88
|
+
lambda {
|
|
89
|
+
Rack::Lint.new(lambda { |env|
|
|
90
|
+
env['rack.multipart.tempfile_factory'].call("testfile", "text/plain")
|
|
91
|
+
}).call(env("rack.multipart.tempfile_factory" => lambda { |filename, content_type| Object.new }))
|
|
92
|
+
}.should.raise(Rack::Lint::LintError).
|
|
93
|
+
message.should.equal("rack.multipart.tempfile_factory return value must respond to #<<")
|
|
94
|
+
|
|
79
95
|
lambda {
|
|
80
96
|
Rack::Lint.new(nil).call(env("REQUEST_METHOD" => "FUCKUP?"))
|
|
81
97
|
}.should.raise(Rack::Lint::LintError).
|
|
@@ -110,7 +126,7 @@ context "Rack::Lint" do
|
|
|
110
126
|
message.should.match(/cannot be .* make it ''/)
|
|
111
127
|
end
|
|
112
128
|
|
|
113
|
-
|
|
129
|
+
should "notice input errors" do
|
|
114
130
|
lambda {
|
|
115
131
|
Rack::Lint.new(nil).call(env("rack.input" => ""))
|
|
116
132
|
}.should.raise(Rack::Lint::LintError).
|
|
@@ -139,14 +155,14 @@ context "Rack::Lint" do
|
|
|
139
155
|
message.should.match(/does not have ASCII-8BIT as its external encoding/)
|
|
140
156
|
end
|
|
141
157
|
|
|
142
|
-
|
|
158
|
+
should "notice error errors" do
|
|
143
159
|
lambda {
|
|
144
160
|
Rack::Lint.new(nil).call(env("rack.errors" => ""))
|
|
145
161
|
}.should.raise(Rack::Lint::LintError).
|
|
146
162
|
message.should.match(/does not respond to #puts/)
|
|
147
163
|
end
|
|
148
164
|
|
|
149
|
-
|
|
165
|
+
should "notice status errors" do
|
|
150
166
|
lambda {
|
|
151
167
|
Rack::Lint.new(lambda { |env|
|
|
152
168
|
["cc", {}, ""]
|
|
@@ -162,7 +178,7 @@ context "Rack::Lint" do
|
|
|
162
178
|
message.should.match(/must be >=100 seen as integer/)
|
|
163
179
|
end
|
|
164
180
|
|
|
165
|
-
|
|
181
|
+
should "notice header errors" do
|
|
166
182
|
lambda {
|
|
167
183
|
Rack::Lint.new(lambda { |env|
|
|
168
184
|
[200, Object.new, []]
|
|
@@ -184,26 +200,36 @@ context "Rack::Lint" do
|
|
|
184
200
|
}.should.raise(Rack::Lint::LintError).
|
|
185
201
|
message.should.match(/must not contain Status/)
|
|
186
202
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
203
|
+
# From RFC 7230:<F24><F25>
|
|
204
|
+
# Most HTTP header field values are defined using common syntax
|
|
205
|
+
# components (token, quoted-string, and comment) separated by
|
|
206
|
+
# whitespace or specific delimiting characters. Delimiters are chosen
|
|
207
|
+
# from the set of US-ASCII visual characters not allowed in a token
|
|
208
|
+
# (DQUOTE and "(),/:;<=>?@[\]{}").
|
|
209
|
+
#
|
|
210
|
+
# token = 1*tchar
|
|
211
|
+
#
|
|
212
|
+
# tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*"
|
|
213
|
+
# / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~"
|
|
214
|
+
# / DIGIT / ALPHA
|
|
215
|
+
# ; any VCHAR, except delimiters
|
|
216
|
+
invalid_headers = 0.upto(31).map(&:chr) + %W<( ) , / : ; < = > ? @ [ \\ ] { } \x7F>
|
|
217
|
+
invalid_headers.each do |invalid_header|
|
|
218
|
+
lambda {
|
|
219
|
+
Rack::Lint.new(lambda { |env|
|
|
220
|
+
[200, {invalid_header => "text/plain"}, []]
|
|
221
|
+
}).call(env({}))
|
|
222
|
+
}.should.raise(Rack::Lint::LintError, "on invalid header: #{invalid_header}").
|
|
223
|
+
message.should.equal("invalid header name: #{invalid_header}")
|
|
224
|
+
end
|
|
225
|
+
valid_headers = 0.upto(127).map(&:chr) - invalid_headers
|
|
226
|
+
valid_headers.each do |valid_header|
|
|
227
|
+
lambda {
|
|
228
|
+
Rack::Lint.new(lambda { |env|
|
|
229
|
+
[200, {valid_header => "text/plain"}, []]
|
|
230
|
+
}).call(env({}))
|
|
231
|
+
}.should.not.raise(Rack::Lint::LintError, "on valid header: #{valid_header}")
|
|
232
|
+
end
|
|
207
233
|
|
|
208
234
|
lambda {
|
|
209
235
|
Rack::Lint.new(lambda { |env|
|
|
@@ -233,17 +259,24 @@ context "Rack::Lint" do
|
|
|
233
259
|
[200, {"Foo-Bar" => "one\ntwo\nthree", "Content-Length" => "0", "Content-Type" => "text/plain" }, []]
|
|
234
260
|
}).call(env({}))
|
|
235
261
|
}.should.not.raise(Rack::Lint::LintError)
|
|
236
|
-
end
|
|
237
262
|
|
|
238
|
-
|
|
263
|
+
# non-Hash header responses should be allowed
|
|
239
264
|
lambda {
|
|
240
265
|
Rack::Lint.new(lambda { |env|
|
|
241
|
-
[200,
|
|
266
|
+
[200, [%w(Content-Type text/plain), %w(Content-Length 0)], []]
|
|
242
267
|
}).call(env({}))
|
|
243
|
-
}.should.raise(
|
|
244
|
-
|
|
268
|
+
}.should.not.raise(TypeError)
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
should "notice content-type errors" do
|
|
272
|
+
# lambda {
|
|
273
|
+
# Rack::Lint.new(lambda { |env|
|
|
274
|
+
# [200, {"Content-length" => "0"}, []]
|
|
275
|
+
# }).call(env({}))
|
|
276
|
+
# }.should.raise(Rack::Lint::LintError).
|
|
277
|
+
# message.should.match(/No Content-Type/)
|
|
245
278
|
|
|
246
|
-
[100, 101, 204, 304].each do |status|
|
|
279
|
+
[100, 101, 204, 205, 304].each do |status|
|
|
247
280
|
lambda {
|
|
248
281
|
Rack::Lint.new(lambda { |env|
|
|
249
282
|
[status, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
|
|
@@ -253,8 +286,8 @@ context "Rack::Lint" do
|
|
|
253
286
|
end
|
|
254
287
|
end
|
|
255
288
|
|
|
256
|
-
|
|
257
|
-
[100, 101, 204, 304].each do |status|
|
|
289
|
+
should "notice content-length errors" do
|
|
290
|
+
[100, 101, 204, 205, 304].each do |status|
|
|
258
291
|
lambda {
|
|
259
292
|
Rack::Lint.new(lambda { |env|
|
|
260
293
|
[status, {"Content-length" => "0"}, []]
|
|
@@ -266,22 +299,22 @@ context "Rack::Lint" do
|
|
|
266
299
|
lambda {
|
|
267
300
|
Rack::Lint.new(lambda { |env|
|
|
268
301
|
[200, {"Content-type" => "text/plain", "Content-Length" => "1"}, []]
|
|
269
|
-
}).call(env({}))
|
|
302
|
+
}).call(env({}))[2].each { }
|
|
270
303
|
}.should.raise(Rack::Lint::LintError).
|
|
271
304
|
message.should.match(/Content-Length header was 1, but should be 0/)
|
|
272
305
|
end
|
|
273
306
|
|
|
274
|
-
|
|
307
|
+
should "notice body errors" do
|
|
275
308
|
lambda {
|
|
276
|
-
|
|
309
|
+
body = Rack::Lint.new(lambda { |env|
|
|
277
310
|
[200, {"Content-type" => "text/plain","Content-length" => "3"}, [1,2,3]]
|
|
278
|
-
}).call(env({}))
|
|
311
|
+
}).call(env({}))[2]
|
|
279
312
|
body.each { |part| }
|
|
280
313
|
}.should.raise(Rack::Lint::LintError).
|
|
281
314
|
message.should.match(/yielded non-string/)
|
|
282
315
|
end
|
|
283
316
|
|
|
284
|
-
|
|
317
|
+
should "notice input handling errors" do
|
|
285
318
|
lambda {
|
|
286
319
|
Rack::Lint.new(lambda { |env|
|
|
287
320
|
env["rack.input"].gets("\r\n")
|
|
@@ -425,7 +458,7 @@ context "Rack::Lint" do
|
|
|
425
458
|
message.should.match(/close must not be called/)
|
|
426
459
|
end
|
|
427
460
|
|
|
428
|
-
|
|
461
|
+
should "notice error handling errors" do
|
|
429
462
|
lambda {
|
|
430
463
|
Rack::Lint.new(lambda { |env|
|
|
431
464
|
env["rack.errors"].write(42)
|
|
@@ -443,7 +476,7 @@ context "Rack::Lint" do
|
|
|
443
476
|
message.should.match(/close must not be called/)
|
|
444
477
|
end
|
|
445
478
|
|
|
446
|
-
|
|
479
|
+
should "notice HEAD errors" do
|
|
447
480
|
lambda {
|
|
448
481
|
Rack::Lint.new(lambda { |env|
|
|
449
482
|
[200, {"Content-type" => "test/plain", "Content-length" => "3"}, []]
|
|
@@ -453,12 +486,12 @@ context "Rack::Lint" do
|
|
|
453
486
|
lambda {
|
|
454
487
|
Rack::Lint.new(lambda { |env|
|
|
455
488
|
[200, {"Content-type" => "test/plain", "Content-length" => "3"}, ["foo"]]
|
|
456
|
-
}).call(env({"REQUEST_METHOD" => "HEAD"}))
|
|
489
|
+
}).call(env({"REQUEST_METHOD" => "HEAD"}))[2].each { }
|
|
457
490
|
}.should.raise(Rack::Lint::LintError).
|
|
458
491
|
message.should.match(/body was given for HEAD/)
|
|
459
492
|
end
|
|
460
493
|
|
|
461
|
-
|
|
494
|
+
should "pass valid read calls" do
|
|
462
495
|
hello_str = "hello world"
|
|
463
496
|
hello_str.force_encoding("ASCII-8BIT") if hello_str.respond_to? :force_encoding
|
|
464
497
|
lambda {
|
|
@@ -505,19 +538,8 @@ context "Rack::Lint" do
|
|
|
505
538
|
end
|
|
506
539
|
end
|
|
507
540
|
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
class IOMock
|
|
511
|
-
def size
|
|
512
|
-
101
|
|
513
|
-
end
|
|
514
|
-
end
|
|
515
|
-
|
|
516
|
-
wrapper = Rack::Lint::InputWrapper.new(IOMock.new)
|
|
517
|
-
wrapper.size.should == 101
|
|
518
|
-
end
|
|
519
|
-
|
|
520
|
-
specify "delegates :rewind to underlying IO object" do
|
|
541
|
+
describe "Rack::Lint::InputWrapper" do
|
|
542
|
+
should "delegate :rewind to underlying IO object" do
|
|
521
543
|
io = StringIO.new("123")
|
|
522
544
|
wrapper = Rack::Lint::InputWrapper.new(io)
|
|
523
545
|
wrapper.read.should.equal "123"
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
require 'rack/lobster'
|
|
2
|
+
require 'rack/lint'
|
|
3
|
+
require 'rack/mock'
|
|
4
|
+
|
|
5
|
+
module LobsterHelpers
|
|
6
|
+
def lobster
|
|
7
|
+
Rack::MockRequest.new Rack::Lint.new(Rack::Lobster.new)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def lambda_lobster
|
|
11
|
+
Rack::MockRequest.new Rack::Lint.new(Rack::Lobster::LambdaLobster)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
describe Rack::Lobster::LambdaLobster do
|
|
16
|
+
extend LobsterHelpers
|
|
17
|
+
|
|
18
|
+
should "be a single lambda" do
|
|
19
|
+
Rack::Lobster::LambdaLobster.should.be.kind_of Proc
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
should "look like a lobster" do
|
|
23
|
+
res = lambda_lobster.get("/")
|
|
24
|
+
res.should.be.ok
|
|
25
|
+
res.body.should.include "(,(,,(,,,("
|
|
26
|
+
res.body.should.include "?flip"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
should "be flippable" do
|
|
30
|
+
res = lambda_lobster.get("/?flip")
|
|
31
|
+
res.should.be.ok
|
|
32
|
+
res.body.should.include "(,,,(,,(,("
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
describe Rack::Lobster do
|
|
37
|
+
extend LobsterHelpers
|
|
38
|
+
|
|
39
|
+
should "look like a lobster" do
|
|
40
|
+
res = lobster.get("/")
|
|
41
|
+
res.should.be.ok
|
|
42
|
+
res.body.should.include "(,(,,(,,,("
|
|
43
|
+
res.body.should.include "?flip"
|
|
44
|
+
res.body.should.include "crash"
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
should "be flippable" do
|
|
48
|
+
res = lobster.get("/?flip=left")
|
|
49
|
+
res.should.be.ok
|
|
50
|
+
res.body.should.include "),,,),,),)"
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
should "provide crashing for testing purposes" do
|
|
54
|
+
lambda {
|
|
55
|
+
lobster.get("/?flip=crash")
|
|
56
|
+
}.should.raise
|
|
57
|
+
end
|
|
58
|
+
end
|
data/test/spec_lock.rb
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
require 'rack/lint'
|
|
2
|
+
require 'rack/lock'
|
|
3
|
+
require 'rack/mock'
|
|
4
|
+
|
|
5
|
+
class Lock
|
|
6
|
+
attr_reader :synchronized
|
|
7
|
+
|
|
8
|
+
def initialize
|
|
9
|
+
@synchronized = false
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def synchronize
|
|
13
|
+
@synchronized = true
|
|
14
|
+
yield
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def lock
|
|
18
|
+
@synchronized = true
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def unlock
|
|
22
|
+
@synchronized = false
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
module LockHelpers
|
|
27
|
+
def lock_app(app, lock = Lock.new)
|
|
28
|
+
app = if lock
|
|
29
|
+
Rack::Lock.new app, lock
|
|
30
|
+
else
|
|
31
|
+
Rack::Lock.new app
|
|
32
|
+
end
|
|
33
|
+
Rack::Lint.new app
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
describe Rack::Lock do
|
|
38
|
+
extend LockHelpers
|
|
39
|
+
|
|
40
|
+
describe 'Proxy' do
|
|
41
|
+
extend LockHelpers
|
|
42
|
+
|
|
43
|
+
should 'delegate each' do
|
|
44
|
+
env = Rack::MockRequest.env_for("/")
|
|
45
|
+
response = Class.new {
|
|
46
|
+
attr_accessor :close_called
|
|
47
|
+
def initialize; @close_called = false; end
|
|
48
|
+
def each; %w{ hi mom }.each { |x| yield x }; end
|
|
49
|
+
}.new
|
|
50
|
+
|
|
51
|
+
app = lock_app(lambda { |inner_env| [200, {"Content-Type" => "text/plain"}, response] })
|
|
52
|
+
response = app.call(env)[2]
|
|
53
|
+
list = []
|
|
54
|
+
response.each { |x| list << x }
|
|
55
|
+
list.should.equal %w{ hi mom }
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
should 'delegate to_path' do
|
|
59
|
+
lock = Lock.new
|
|
60
|
+
env = Rack::MockRequest.env_for("/")
|
|
61
|
+
|
|
62
|
+
res = ['Hello World']
|
|
63
|
+
def res.to_path ; "/tmp/hello.txt" ; end
|
|
64
|
+
|
|
65
|
+
app = Rack::Lock.new(lambda { |inner_env| [200, {"Content-Type" => "text/plain"}, res] }, lock)
|
|
66
|
+
body = app.call(env)[2]
|
|
67
|
+
|
|
68
|
+
body.should.respond_to :to_path
|
|
69
|
+
body.to_path.should.equal "/tmp/hello.txt"
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
should 'not delegate to_path if body does not implement it' do
|
|
73
|
+
env = Rack::MockRequest.env_for("/")
|
|
74
|
+
|
|
75
|
+
res = ['Hello World']
|
|
76
|
+
|
|
77
|
+
app = lock_app(lambda { |inner_env| [200, {"Content-Type" => "text/plain"}, res] })
|
|
78
|
+
body = app.call(env)[2]
|
|
79
|
+
|
|
80
|
+
body.should.not.respond_to :to_path
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
should 'call super on close' do
|
|
85
|
+
env = Rack::MockRequest.env_for("/")
|
|
86
|
+
response = Class.new {
|
|
87
|
+
attr_accessor :close_called
|
|
88
|
+
def initialize; @close_called = false; end
|
|
89
|
+
def close; @close_called = true; end
|
|
90
|
+
}.new
|
|
91
|
+
|
|
92
|
+
app = lock_app(lambda { |inner_env| [200, {"Content-Type" => "text/plain"}, response] })
|
|
93
|
+
app.call(env)
|
|
94
|
+
response.close_called.should.equal false
|
|
95
|
+
response.close
|
|
96
|
+
response.close_called.should.equal true
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
should "not unlock until body is closed" do
|
|
100
|
+
lock = Lock.new
|
|
101
|
+
env = Rack::MockRequest.env_for("/")
|
|
102
|
+
response = Object.new
|
|
103
|
+
app = lock_app(lambda { |inner_env| [200, {"Content-Type" => "text/plain"}, response] }, lock)
|
|
104
|
+
lock.synchronized.should.equal false
|
|
105
|
+
response = app.call(env)[2]
|
|
106
|
+
lock.synchronized.should.equal true
|
|
107
|
+
response.close
|
|
108
|
+
lock.synchronized.should.equal false
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
should "return value from app" do
|
|
112
|
+
env = Rack::MockRequest.env_for("/")
|
|
113
|
+
body = [200, {"Content-Type" => "text/plain"}, %w{ hi mom }]
|
|
114
|
+
app = lock_app(lambda { |inner_env| body })
|
|
115
|
+
|
|
116
|
+
res = app.call(env)
|
|
117
|
+
res[0].should.equal body[0]
|
|
118
|
+
res[1].should.equal body[1]
|
|
119
|
+
res[2].to_enum.to_a.should.equal ["hi", "mom"]
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
should "call synchronize on lock" do
|
|
123
|
+
lock = Lock.new
|
|
124
|
+
env = Rack::MockRequest.env_for("/")
|
|
125
|
+
app = lock_app(lambda { |inner_env| [200, {"Content-Type" => "text/plain"}, %w{ a b c }] }, lock)
|
|
126
|
+
lock.synchronized.should.equal false
|
|
127
|
+
app.call(env)
|
|
128
|
+
lock.synchronized.should.equal true
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
should "unlock if the app raises" do
|
|
132
|
+
lock = Lock.new
|
|
133
|
+
env = Rack::MockRequest.env_for("/")
|
|
134
|
+
app = lock_app(lambda { raise Exception }, lock)
|
|
135
|
+
lambda { app.call(env) }.should.raise(Exception)
|
|
136
|
+
lock.synchronized.should.equal false
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
should "unlock if the app throws" do
|
|
140
|
+
lock = Lock.new
|
|
141
|
+
env = Rack::MockRequest.env_for("/")
|
|
142
|
+
app = lock_app(lambda {|_| throw :bacon }, lock)
|
|
143
|
+
lambda { app.call(env) }.should.throw(:bacon)
|
|
144
|
+
lock.synchronized.should.equal false
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
should "set multithread flag to false" do
|
|
148
|
+
app = lock_app(lambda { |env|
|
|
149
|
+
env['rack.multithread'].should.equal false
|
|
150
|
+
[200, {"Content-Type" => "text/plain"}, %w{ a b c }]
|
|
151
|
+
}, false)
|
|
152
|
+
app.call(Rack::MockRequest.env_for("/"))
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
should "reset original multithread flag when exiting lock" do
|
|
156
|
+
app = Class.new(Rack::Lock) {
|
|
157
|
+
def call(env)
|
|
158
|
+
env['rack.multithread'].should.equal true
|
|
159
|
+
super
|
|
160
|
+
end
|
|
161
|
+
}.new(lambda { |env| [200, {"Content-Type" => "text/plain"}, %w{ a b c }] })
|
|
162
|
+
Rack::Lint.new(app).call(Rack::MockRequest.env_for("/"))
|
|
163
|
+
end
|
|
164
|
+
end
|
data/test/spec_logger.rb
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
require 'stringio'
|
|
2
|
+
require 'rack/lint'
|
|
3
|
+
require 'rack/logger'
|
|
4
|
+
require 'rack/mock'
|
|
5
|
+
|
|
6
|
+
describe Rack::Logger do
|
|
7
|
+
app = lambda { |env|
|
|
8
|
+
log = env['rack.logger']
|
|
9
|
+
log.debug("Created logger")
|
|
10
|
+
log.info("Program started")
|
|
11
|
+
log.warn("Nothing to do!")
|
|
12
|
+
|
|
13
|
+
[200, {'Content-Type' => 'text/plain'}, ["Hello, World!"]]
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
should "conform to Rack::Lint" do
|
|
17
|
+
errors = StringIO.new
|
|
18
|
+
a = Rack::Lint.new(Rack::Logger.new(app))
|
|
19
|
+
Rack::MockRequest.new(a).get('/', 'rack.errors' => errors)
|
|
20
|
+
errors.string.should.match(/INFO -- : Program started/)
|
|
21
|
+
errors.string.should.match(/WARN -- : Nothing to do/)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
require 'stringio'
|
|
2
|
+
require 'rack/methodoverride'
|
|
3
|
+
require 'rack/mock'
|
|
4
|
+
|
|
5
|
+
describe Rack::MethodOverride do
|
|
6
|
+
def app
|
|
7
|
+
Rack::Lint.new(Rack::MethodOverride.new(lambda {|e|
|
|
8
|
+
[200, {"Content-Type" => "text/plain"}, []]
|
|
9
|
+
}))
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
should "not affect GET requests" do
|
|
13
|
+
env = Rack::MockRequest.env_for("/?_method=delete", :method => "GET")
|
|
14
|
+
app.call env
|
|
15
|
+
|
|
16
|
+
env["REQUEST_METHOD"].should.equal "GET"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
should "modify REQUEST_METHOD for POST requests when _method parameter is set" do
|
|
20
|
+
env = Rack::MockRequest.env_for("/", :method => "POST", :input => "_method=put")
|
|
21
|
+
app.call env
|
|
22
|
+
|
|
23
|
+
env["REQUEST_METHOD"].should.equal "PUT"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
should "modify REQUEST_METHOD for POST requests when X-HTTP-Method-Override is set" do
|
|
27
|
+
env = Rack::MockRequest.env_for("/",
|
|
28
|
+
:method => "POST",
|
|
29
|
+
"HTTP_X_HTTP_METHOD_OVERRIDE" => "PATCH"
|
|
30
|
+
)
|
|
31
|
+
app.call env
|
|
32
|
+
|
|
33
|
+
env["REQUEST_METHOD"].should.equal "PATCH"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
should "not modify REQUEST_METHOD if the method is unknown" do
|
|
37
|
+
env = Rack::MockRequest.env_for("/", :method => "POST", :input => "_method=foo")
|
|
38
|
+
app.call env
|
|
39
|
+
|
|
40
|
+
env["REQUEST_METHOD"].should.equal "POST"
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
should "not modify REQUEST_METHOD when _method is nil" do
|
|
44
|
+
env = Rack::MockRequest.env_for("/", :method => "POST", :input => "foo=bar")
|
|
45
|
+
app.call env
|
|
46
|
+
|
|
47
|
+
env["REQUEST_METHOD"].should.equal "POST"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
should "store the original REQUEST_METHOD prior to overriding" do
|
|
51
|
+
env = Rack::MockRequest.env_for("/",
|
|
52
|
+
:method => "POST",
|
|
53
|
+
:input => "_method=options")
|
|
54
|
+
app.call env
|
|
55
|
+
|
|
56
|
+
env["rack.methodoverride.original_method"].should.equal "POST"
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
should "not modify REQUEST_METHOD when given invalid multipart form data" do
|
|
60
|
+
input = <<EOF
|
|
61
|
+
--AaB03x\r
|
|
62
|
+
content-disposition: form-data; name="huge"; filename="huge"\r
|
|
63
|
+
EOF
|
|
64
|
+
env = Rack::MockRequest.env_for("/",
|
|
65
|
+
"CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
|
|
66
|
+
"CONTENT_LENGTH" => input.size.to_s,
|
|
67
|
+
:method => "POST", :input => input)
|
|
68
|
+
app.call env
|
|
69
|
+
|
|
70
|
+
env["REQUEST_METHOD"].should.equal "POST"
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
should "write error to RACK_ERRORS when given invalid multipart form data" do
|
|
74
|
+
input = <<EOF
|
|
75
|
+
--AaB03x\r
|
|
76
|
+
content-disposition: form-data; name="huge"; filename="huge"\r
|
|
77
|
+
EOF
|
|
78
|
+
env = Rack::MockRequest.env_for("/",
|
|
79
|
+
"CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
|
|
80
|
+
"CONTENT_LENGTH" => input.size.to_s,
|
|
81
|
+
"rack.errors" => StringIO.new,
|
|
82
|
+
:method => "POST", :input => input)
|
|
83
|
+
Rack::MethodOverride.new(proc { [200, {"Content-Type" => "text/plain"}, []] }).call env
|
|
84
|
+
|
|
85
|
+
env["rack.errors"].rewind
|
|
86
|
+
env["rack.errors"].read.should =~ /Bad request content body/
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
should "not modify REQUEST_METHOD for POST requests when the params are unparseable" do
|
|
90
|
+
env = Rack::MockRequest.env_for("/", :method => "POST", :input => "(%bad-params%)")
|
|
91
|
+
app.call env
|
|
92
|
+
|
|
93
|
+
env["REQUEST_METHOD"].should.equal "POST"
|
|
94
|
+
end
|
|
95
|
+
end
|
data/test/spec_mime.rb
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
require 'rack/mime'
|
|
2
|
+
|
|
3
|
+
describe Rack::Mime do
|
|
4
|
+
|
|
5
|
+
it "should return the fallback mime-type for files with no extension" do
|
|
6
|
+
fallback = 'image/jpg'
|
|
7
|
+
Rack::Mime.mime_type(File.extname('no_ext'), fallback).should.equal fallback
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
it "should always return 'application/octet-stream' for unknown file extensions" do
|
|
11
|
+
unknown_ext = File.extname('unknown_ext.abcdefg')
|
|
12
|
+
Rack::Mime.mime_type(unknown_ext).should.equal 'application/octet-stream'
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it "should return the mime-type for a given extension" do
|
|
16
|
+
# sanity check. it would be infeasible test every single mime-type.
|
|
17
|
+
Rack::Mime.mime_type(File.extname('image.jpg')).should.equal 'image/jpeg'
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "should support null fallbacks" do
|
|
21
|
+
Rack::Mime.mime_type('.nothing', nil).should.equal nil
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it "should match exact mimes" do
|
|
25
|
+
Rack::Mime.match?('text/html', 'text/html').should.equal true
|
|
26
|
+
Rack::Mime.match?('text/html', 'text/meme').should.equal false
|
|
27
|
+
Rack::Mime.match?('text', 'text').should.equal true
|
|
28
|
+
Rack::Mime.match?('text', 'binary').should.equal false
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it "should match class wildcard mimes" do
|
|
32
|
+
Rack::Mime.match?('text/html', 'text/*').should.equal true
|
|
33
|
+
Rack::Mime.match?('text/plain', 'text/*').should.equal true
|
|
34
|
+
Rack::Mime.match?('application/json', 'text/*').should.equal false
|
|
35
|
+
Rack::Mime.match?('text/html', 'text').should.equal true
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it "should match full wildcards" do
|
|
39
|
+
Rack::Mime.match?('text/html', '*').should.equal true
|
|
40
|
+
Rack::Mime.match?('text/plain', '*').should.equal true
|
|
41
|
+
Rack::Mime.match?('text/html', '*/*').should.equal true
|
|
42
|
+
Rack::Mime.match?('text/plain', '*/*').should.equal true
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it "should match type wildcard mimes" do
|
|
46
|
+
Rack::Mime.match?('text/html', '*/html').should.equal true
|
|
47
|
+
Rack::Mime.match?('text/plain', '*/plain').should.equal true
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
end
|
|
51
|
+
|