rack 1.6.11 → 2.1.4
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of rack might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +77 -0
- data/{COPYING → MIT-LICENSE} +4 -2
- data/README.rdoc +89 -139
- data/Rakefile +27 -28
- data/SPEC +6 -7
- data/bin/rackup +1 -0
- data/contrib/rack_logo.svg +164 -111
- data/example/lobster.ru +2 -0
- data/example/protectedlobster.rb +4 -2
- data/example/protectedlobster.ru +3 -1
- data/lib/rack/auth/abstract/handler.rb +3 -1
- data/lib/rack/auth/abstract/request.rb +7 -1
- data/lib/rack/auth/basic.rb +4 -1
- data/lib/rack/auth/digest/md5.rb +9 -7
- data/lib/rack/auth/digest/nonce.rb +6 -3
- data/lib/rack/auth/digest/params.rb +5 -4
- data/lib/rack/auth/digest/request.rb +3 -1
- data/lib/rack/body_proxy.rb +11 -9
- data/lib/rack/builder.rb +42 -18
- data/lib/rack/cascade.rb +6 -5
- data/lib/rack/chunked.rb +33 -10
- data/lib/rack/{commonlogger.rb → common_logger.rb} +11 -10
- data/lib/rack/{conditionalget.rb → conditional_get.rb} +3 -1
- data/lib/rack/config.rb +2 -0
- data/lib/rack/content_length.rb +5 -3
- data/lib/rack/content_type.rb +3 -1
- data/lib/rack/core_ext/regexp.rb +14 -0
- data/lib/rack/deflater.rb +33 -53
- data/lib/rack/directory.rb +75 -60
- data/lib/rack/etag.rb +8 -5
- data/lib/rack/events.rb +156 -0
- data/lib/rack/file.rb +4 -149
- data/lib/rack/files.rb +178 -0
- data/lib/rack/handler/cgi.rb +18 -17
- data/lib/rack/handler/fastcgi.rb +17 -16
- data/lib/rack/handler/lsws.rb +14 -12
- data/lib/rack/handler/scgi.rb +22 -19
- data/lib/rack/handler/thin.rb +6 -1
- data/lib/rack/handler/webrick.rb +28 -28
- data/lib/rack/handler.rb +9 -26
- data/lib/rack/head.rb +17 -17
- data/lib/rack/lint.rb +54 -51
- data/lib/rack/lobster.rb +8 -6
- data/lib/rack/lock.rb +17 -10
- data/lib/rack/logger.rb +4 -2
- data/lib/rack/media_type.rb +43 -0
- data/lib/rack/{methodoverride.rb → method_override.rb} +10 -8
- data/lib/rack/mime.rb +27 -6
- data/lib/rack/mock.rb +101 -60
- data/lib/rack/multipart/generator.rb +11 -12
- data/lib/rack/multipart/parser.rb +280 -161
- data/lib/rack/multipart/uploaded_file.rb +3 -2
- data/lib/rack/multipart.rb +39 -8
- data/lib/rack/{nulllogger.rb → null_logger.rb} +3 -1
- data/lib/rack/query_parser.rb +218 -0
- data/lib/rack/recursive.rb +11 -9
- data/lib/rack/reloader.rb +10 -4
- data/lib/rack/request.rb +447 -305
- data/lib/rack/response.rb +196 -83
- data/lib/rack/rewindable_input.rb +5 -14
- data/lib/rack/runtime.rb +12 -18
- data/lib/rack/sendfile.rb +19 -14
- data/lib/rack/server.rb +118 -41
- data/lib/rack/session/abstract/id.rb +215 -94
- data/lib/rack/session/cookie.rb +45 -28
- data/lib/rack/session/memcache.rb +4 -87
- data/lib/rack/session/pool.rb +25 -16
- data/lib/rack/show_exceptions.rb +392 -0
- data/lib/rack/{showstatus.rb → show_status.rb} +7 -5
- data/lib/rack/static.rb +41 -11
- data/lib/rack/tempfile_reaper.rb +4 -2
- data/lib/rack/urlmap.rb +25 -15
- data/lib/rack/utils.rb +186 -272
- data/lib/rack.rb +76 -24
- data/rack.gemspec +25 -14
- metadata +62 -182
- data/HISTORY.md +0 -375
- data/KNOWN-ISSUES +0 -44
- data/lib/rack/backports/uri/common_18.rb +0 -56
- data/lib/rack/backports/uri/common_192.rb +0 -52
- data/lib/rack/backports/uri/common_193.rb +0 -29
- data/lib/rack/handler/evented_mongrel.rb +0 -8
- data/lib/rack/handler/mongrel.rb +0 -106
- data/lib/rack/handler/swiftiplied_mongrel.rb +0 -8
- data/lib/rack/showexceptions.rb +0 -387
- data/lib/rack/utils/okjson.rb +0 -600
- data/test/builder/anything.rb +0 -5
- data/test/builder/comment.ru +0 -4
- data/test/builder/end.ru +0 -5
- data/test/builder/line.ru +0 -1
- data/test/builder/options.ru +0 -2
- data/test/cgi/assets/folder/test.js +0 -1
- data/test/cgi/assets/fonts/font.eot +0 -1
- data/test/cgi/assets/images/image.png +0 -1
- data/test/cgi/assets/index.html +0 -1
- data/test/cgi/assets/javascripts/app.js +0 -1
- data/test/cgi/assets/stylesheets/app.css +0 -1
- data/test/cgi/lighttpd.conf +0 -26
- data/test/cgi/rackup_stub.rb +0 -6
- data/test/cgi/sample_rackup.ru +0 -5
- data/test/cgi/test +0 -9
- data/test/cgi/test+directory/test+file +0 -1
- data/test/cgi/test.fcgi +0 -8
- data/test/cgi/test.ru +0 -5
- data/test/gemloader.rb +0 -10
- data/test/multipart/bad_robots +0 -259
- data/test/multipart/binary +0 -0
- data/test/multipart/content_type_and_no_filename +0 -6
- data/test/multipart/empty +0 -10
- data/test/multipart/fail_16384_nofile +0 -814
- data/test/multipart/file1.txt +0 -1
- data/test/multipart/filename_and_modification_param +0 -7
- data/test/multipart/filename_and_no_name +0 -6
- data/test/multipart/filename_with_escaped_quotes +0 -6
- data/test/multipart/filename_with_escaped_quotes_and_modification_param +0 -7
- data/test/multipart/filename_with_null_byte +0 -7
- data/test/multipart/filename_with_percent_escaped_quotes +0 -6
- data/test/multipart/filename_with_unescaped_percentages +0 -6
- data/test/multipart/filename_with_unescaped_percentages2 +0 -6
- data/test/multipart/filename_with_unescaped_percentages3 +0 -6
- data/test/multipart/filename_with_unescaped_quotes +0 -6
- data/test/multipart/ie +0 -6
- data/test/multipart/invalid_character +0 -6
- data/test/multipart/mixed_files +0 -21
- data/test/multipart/nested +0 -10
- data/test/multipart/none +0 -9
- data/test/multipart/semicolon +0 -6
- data/test/multipart/text +0 -15
- data/test/multipart/three_files_three_fields +0 -31
- data/test/multipart/webkit +0 -32
- data/test/rackup/config.ru +0 -31
- data/test/registering_handler/rack/handler/registering_myself.rb +0 -8
- data/test/spec_auth_basic.rb +0 -81
- data/test/spec_auth_digest.rb +0 -259
- data/test/spec_body_proxy.rb +0 -85
- data/test/spec_builder.rb +0 -223
- data/test/spec_cascade.rb +0 -61
- data/test/spec_cgi.rb +0 -102
- data/test/spec_chunked.rb +0 -101
- data/test/spec_commonlogger.rb +0 -93
- data/test/spec_conditionalget.rb +0 -102
- data/test/spec_config.rb +0 -22
- data/test/spec_content_length.rb +0 -85
- data/test/spec_content_type.rb +0 -45
- data/test/spec_deflater.rb +0 -339
- data/test/spec_directory.rb +0 -88
- data/test/spec_etag.rb +0 -107
- data/test/spec_fastcgi.rb +0 -107
- data/test/spec_file.rb +0 -221
- data/test/spec_handler.rb +0 -72
- data/test/spec_head.rb +0 -45
- data/test/spec_lint.rb +0 -550
- data/test/spec_lobster.rb +0 -58
- data/test/spec_lock.rb +0 -164
- data/test/spec_logger.rb +0 -23
- data/test/spec_methodoverride.rb +0 -111
- data/test/spec_mime.rb +0 -51
- data/test/spec_mock.rb +0 -297
- data/test/spec_mongrel.rb +0 -182
- data/test/spec_multipart.rb +0 -600
- data/test/spec_nulllogger.rb +0 -20
- data/test/spec_recursive.rb +0 -72
- data/test/spec_request.rb +0 -1232
- data/test/spec_response.rb +0 -407
- data/test/spec_rewindable_input.rb +0 -118
- data/test/spec_runtime.rb +0 -49
- data/test/spec_sendfile.rb +0 -130
- data/test/spec_server.rb +0 -167
- data/test/spec_session_abstract_id.rb +0 -53
- data/test/spec_session_cookie.rb +0 -410
- data/test/spec_session_memcache.rb +0 -321
- data/test/spec_session_pool.rb +0 -209
- data/test/spec_showexceptions.rb +0 -98
- data/test/spec_showstatus.rb +0 -103
- data/test/spec_static.rb +0 -145
- data/test/spec_tempfile_reaper.rb +0 -63
- data/test/spec_thin.rb +0 -91
- data/test/spec_urlmap.rb +0 -236
- data/test/spec_utils.rb +0 -647
- data/test/spec_version.rb +0 -17
- data/test/spec_webrick.rb +0 -184
- data/test/static/another/index.html +0 -1
- data/test/static/index.html +0 -1
- data/test/testrequest.rb +0 -78
- data/test/unregistered_handler/rack/handler/unregistered.rb +0 -7
- data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +0 -7
data/lib/rack/lint.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'rack/utils'
|
2
4
|
require 'forwardable'
|
3
5
|
|
@@ -15,8 +17,8 @@ module Rack
|
|
15
17
|
|
16
18
|
class LintError < RuntimeError; end
|
17
19
|
module Assertion
|
18
|
-
def assert(message
|
19
|
-
unless
|
20
|
+
def assert(message)
|
21
|
+
unless yield
|
20
22
|
raise LintError, message
|
21
23
|
end
|
22
24
|
end
|
@@ -33,7 +35,7 @@ module Rack
|
|
33
35
|
|
34
36
|
## A Rack application is a Ruby object (not a class) that
|
35
37
|
## responds to +call+.
|
36
|
-
def call(env=nil)
|
38
|
+
def call(env = nil)
|
37
39
|
dup._call(env)
|
38
40
|
end
|
39
41
|
|
@@ -42,8 +44,8 @@ module Rack
|
|
42
44
|
assert("No env given") { env }
|
43
45
|
check_env env
|
44
46
|
|
45
|
-
env[
|
46
|
-
env[
|
47
|
+
env[RACK_INPUT] = InputWrapper.new(env[RACK_INPUT])
|
48
|
+
env[RACK_ERRORS] = ErrorWrapper.new(env[RACK_ERRORS])
|
47
49
|
|
48
50
|
## and returns an Array of exactly three values:
|
49
51
|
status, headers, @body = @app.call(env)
|
@@ -57,7 +59,7 @@ module Rack
|
|
57
59
|
## and the *body*.
|
58
60
|
check_content_type status, headers
|
59
61
|
check_content_length status, headers
|
60
|
-
@head_request = env[REQUEST_METHOD] ==
|
62
|
+
@head_request = env[REQUEST_METHOD] == HEAD
|
61
63
|
[status, headers, self]
|
62
64
|
end
|
63
65
|
|
@@ -95,7 +97,7 @@ module Rack
|
|
95
97
|
## empty string, if the request URL targets
|
96
98
|
## the application root and does not have a
|
97
99
|
## trailing slash. This value may be
|
98
|
-
## percent-encoded when
|
100
|
+
## percent-encoded when originating from
|
99
101
|
## a URL.
|
100
102
|
|
101
103
|
## <tt>QUERY_STRING</tt>:: The portion of the request URL that
|
@@ -123,9 +125,8 @@ module Rack
|
|
123
125
|
## the presence or absence of the
|
124
126
|
## appropriate HTTP header in the
|
125
127
|
## request. See
|
126
|
-
##
|
127
|
-
##
|
128
|
-
## specific behavior.
|
128
|
+
## {RFC3875 section 4.1.18}[https://tools.ietf.org/html/rfc3875#section-4.1.18]
|
129
|
+
## for specific behavior.
|
129
130
|
|
130
131
|
## In addition to this, the Rack environment must include these
|
131
132
|
## Rack-specific variables:
|
@@ -177,7 +178,7 @@ module Rack
|
|
177
178
|
## <tt>rack.session</tt>:: A hash like interface for storing
|
178
179
|
## request session data.
|
179
180
|
## The store must implement:
|
180
|
-
if session = env[
|
181
|
+
if session = env[RACK_SESSION]
|
181
182
|
## store(key, value) (aliased as []=);
|
182
183
|
assert("session #{session.inspect} must respond to store and []=") {
|
183
184
|
session.respond_to?(:store) && session.respond_to?(:[]=)
|
@@ -201,7 +202,7 @@ module Rack
|
|
201
202
|
|
202
203
|
## <tt>rack.logger</tt>:: A common object interface for logging messages.
|
203
204
|
## The object must implement:
|
204
|
-
if logger = env[
|
205
|
+
if logger = env[RACK_LOGGER]
|
205
206
|
## info(message, &block)
|
206
207
|
assert("logger #{logger.inspect} must respond to info") {
|
207
208
|
logger.respond_to?(:info)
|
@@ -229,16 +230,16 @@ module Rack
|
|
229
230
|
end
|
230
231
|
|
231
232
|
## <tt>rack.multipart.buffer_size</tt>:: An Integer hint to the multipart parser as to what chunk size to use for reads and writes.
|
232
|
-
if bufsize = env[
|
233
|
+
if bufsize = env[RACK_MULTIPART_BUFFER_SIZE]
|
233
234
|
assert("rack.multipart.buffer_size must be an Integer > 0 if specified") {
|
234
235
|
bufsize.is_a?(Integer) && bufsize > 0
|
235
236
|
}
|
236
237
|
end
|
237
238
|
|
238
239
|
## <tt>rack.multipart.tempfile_factory</tt>:: An object responding to #call with two arguments, the filename and content_type given for the multipart form field, and returning an IO-like object that responds to #<< and optionally #rewind. This factory will be used to instantiate the tempfile for each multipart form file upload field, rather than the default class of Tempfile.
|
239
|
-
if tempfile_factory = env[
|
240
|
+
if tempfile_factory = env[RACK_MULTIPART_TEMPFILE_FACTORY]
|
240
241
|
assert("rack.multipart.tempfile_factory must respond to #call") { tempfile_factory.respond_to?(:call) }
|
241
|
-
env[
|
242
|
+
env[RACK_MULTIPART_TEMPFILE_FACTORY] = lambda do |filename, content_type|
|
242
243
|
io = tempfile_factory.call(filename, content_type)
|
243
244
|
assert("rack.multipart.tempfile_factory return value must respond to #<<") { io.respond_to?(:<<) }
|
244
245
|
io
|
@@ -263,7 +264,7 @@ module Rack
|
|
263
264
|
## <tt>HTTP_CONTENT_TYPE</tt> or <tt>HTTP_CONTENT_LENGTH</tt>
|
264
265
|
## (use the versions without <tt>HTTP_</tt>).
|
265
266
|
%w[HTTP_CONTENT_TYPE HTTP_CONTENT_LENGTH].each { |header|
|
266
|
-
assert("env contains #{header}, must use #{header[5
|
267
|
+
assert("env contains #{header}, must use #{header[5, -1]}") {
|
267
268
|
not env.include? header
|
268
269
|
}
|
269
270
|
}
|
@@ -279,37 +280,37 @@ module Rack
|
|
279
280
|
## There are the following restrictions:
|
280
281
|
|
281
282
|
## * <tt>rack.version</tt> must be an array of Integers.
|
282
|
-
assert("rack.version must be an Array, was #{env[
|
283
|
-
env[
|
283
|
+
assert("rack.version must be an Array, was #{env[RACK_VERSION].class}") {
|
284
|
+
env[RACK_VERSION].kind_of? Array
|
284
285
|
}
|
285
286
|
## * <tt>rack.url_scheme</tt> must either be +http+ or +https+.
|
286
|
-
assert("rack.url_scheme unknown: #{env[
|
287
|
-
%w[http https].include?
|
287
|
+
assert("rack.url_scheme unknown: #{env[RACK_URL_SCHEME].inspect}") {
|
288
|
+
%w[http https].include?(env[RACK_URL_SCHEME])
|
288
289
|
}
|
289
290
|
|
290
291
|
## * There must be a valid input stream in <tt>rack.input</tt>.
|
291
|
-
check_input env[
|
292
|
+
check_input env[RACK_INPUT]
|
292
293
|
## * There must be a valid error stream in <tt>rack.errors</tt>.
|
293
|
-
check_error env[
|
294
|
+
check_error env[RACK_ERRORS]
|
294
295
|
## * There may be a valid hijack stream in <tt>rack.hijack_io</tt>
|
295
296
|
check_hijack env
|
296
297
|
|
297
298
|
## * The <tt>REQUEST_METHOD</tt> must be a valid token.
|
298
299
|
assert("REQUEST_METHOD unknown: #{env[REQUEST_METHOD]}") {
|
299
|
-
env[
|
300
|
+
env[REQUEST_METHOD] =~ /\A[0-9A-Za-z!\#$%&'*+.^_`|~-]+\z/
|
300
301
|
}
|
301
302
|
|
302
303
|
## * The <tt>SCRIPT_NAME</tt>, if non-empty, must start with <tt>/</tt>
|
303
304
|
assert("SCRIPT_NAME must start with /") {
|
304
|
-
!env.include?(
|
305
|
-
env[
|
306
|
-
env[
|
305
|
+
!env.include?(SCRIPT_NAME) ||
|
306
|
+
env[SCRIPT_NAME] == "" ||
|
307
|
+
env[SCRIPT_NAME] =~ /\A\//
|
307
308
|
}
|
308
309
|
## * The <tt>PATH_INFO</tt>, if non-empty, must start with <tt>/</tt>
|
309
310
|
assert("PATH_INFO must start with /") {
|
310
|
-
!env.include?(
|
311
|
-
env[
|
312
|
-
env[
|
311
|
+
!env.include?(PATH_INFO) ||
|
312
|
+
env[PATH_INFO] == "" ||
|
313
|
+
env[PATH_INFO] =~ /\A\//
|
313
314
|
}
|
314
315
|
## * The <tt>CONTENT_LENGTH</tt>, if given, must consist of digits only.
|
315
316
|
assert("Invalid CONTENT_LENGTH: #{env["CONTENT_LENGTH"]}") {
|
@@ -320,11 +321,11 @@ module Rack
|
|
320
321
|
## set. <tt>PATH_INFO</tt> should be <tt>/</tt> if
|
321
322
|
## <tt>SCRIPT_NAME</tt> is empty.
|
322
323
|
assert("One of SCRIPT_NAME or PATH_INFO must be set (make PATH_INFO '/' if SCRIPT_NAME is empty)") {
|
323
|
-
env[
|
324
|
+
env[SCRIPT_NAME] || env[PATH_INFO]
|
324
325
|
}
|
325
326
|
## <tt>SCRIPT_NAME</tt> never should be <tt>/</tt>, but instead be empty.
|
326
327
|
assert("SCRIPT_NAME cannot be '/', make it '' and PATH_INFO '/'") {
|
327
|
-
env[
|
328
|
+
env[SCRIPT_NAME] != "/"
|
328
329
|
}
|
329
330
|
end
|
330
331
|
|
@@ -518,11 +519,11 @@ module Rack
|
|
518
519
|
#
|
519
520
|
## ==== Request (before status)
|
520
521
|
def check_hijack(env)
|
521
|
-
if env[
|
522
|
+
if env[RACK_IS_HIJACK]
|
522
523
|
## If rack.hijack? is true then rack.hijack must respond to #call.
|
523
|
-
original_hijack = env[
|
524
|
+
original_hijack = env[RACK_HIJACK]
|
524
525
|
assert("rack.hijack must respond to call") { original_hijack.respond_to?(:call) }
|
525
|
-
env[
|
526
|
+
env[RACK_HIJACK] = proc do
|
526
527
|
## rack.hijack must return the io that will also be assigned (or is
|
527
528
|
## already present, in rack.hijack_io.
|
528
529
|
io = original_hijack.call
|
@@ -548,16 +549,16 @@ module Rack
|
|
548
549
|
## hijack_io to provide additional features to users. The purpose of
|
549
550
|
## rack.hijack is for Rack to "get out of the way", as such, Rack only
|
550
551
|
## provides the minimum of specification and support.
|
551
|
-
env[
|
552
|
+
env[RACK_HIJACK_IO] = HijackWrapper.new(env[RACK_HIJACK_IO])
|
552
553
|
io
|
553
554
|
end
|
554
555
|
else
|
555
556
|
##
|
556
557
|
## If rack.hijack? is false, then rack.hijack should not be set.
|
557
|
-
assert("rack.hijack? is false, but rack.hijack is present") { env[
|
558
|
+
assert("rack.hijack? is false, but rack.hijack is present") { env[RACK_HIJACK].nil? }
|
558
559
|
##
|
559
560
|
## If rack.hijack? is false, then rack.hijack_io should not be set.
|
560
|
-
assert("rack.hijack? is false, but rack.hijack_io is present") { env[
|
561
|
+
assert("rack.hijack? is false, but rack.hijack_io is present") { env[RACK_HIJACK_IO].nil? }
|
561
562
|
end
|
562
563
|
end
|
563
564
|
|
@@ -587,12 +588,12 @@ module Rack
|
|
587
588
|
## Servers must ignore the <tt>body</tt> part of the response tuple when
|
588
589
|
## the <tt>rack.hijack</tt> response API is in use.
|
589
590
|
|
590
|
-
if env[
|
591
|
+
if env[RACK_IS_HIJACK] && headers[RACK_HIJACK]
|
591
592
|
assert('rack.hijack header must respond to #call') {
|
592
|
-
headers[
|
593
|
+
headers[RACK_HIJACK].respond_to? :call
|
593
594
|
}
|
594
|
-
original_hijack = headers[
|
595
|
-
headers[
|
595
|
+
original_hijack = headers[RACK_HIJACK]
|
596
|
+
headers[RACK_HIJACK] = proc do |io|
|
596
597
|
original_hijack.call HijackWrapper.new(io)
|
597
598
|
end
|
598
599
|
else
|
@@ -600,7 +601,7 @@ module Rack
|
|
600
601
|
## The special response header <tt>rack.hijack</tt> must only be set
|
601
602
|
## if the request env has <tt>rack.hijack?</tt> <tt>true</tt>.
|
602
603
|
assert('rack.hijack header must not be present if server does not support hijacking') {
|
603
|
-
headers[
|
604
|
+
headers[RACK_HIJACK].nil?
|
604
605
|
}
|
605
606
|
end
|
606
607
|
end
|
@@ -626,15 +627,17 @@ module Rack
|
|
626
627
|
assert("headers object should respond to #each, but doesn't (got #{header.class} as headers)") {
|
627
628
|
header.respond_to? :each
|
628
629
|
}
|
629
|
-
header.each { |key, value|
|
630
|
-
## Special headers starting "rack." are for communicating with the
|
631
|
-
## server, and must not be sent back to the client.
|
632
|
-
next if key =~ /^rack\..+$/
|
633
630
|
|
631
|
+
header.each { |key, value|
|
634
632
|
## The header keys must be Strings.
|
635
633
|
assert("header key must be a string, was #{key.class}") {
|
636
634
|
key.kind_of? String
|
637
635
|
}
|
636
|
+
|
637
|
+
## Special headers starting "rack." are for communicating with the
|
638
|
+
## server, and must not be sent back to the client.
|
639
|
+
next if key =~ /^rack\..+$/
|
640
|
+
|
638
641
|
## The header must not contain a +Status+ key.
|
639
642
|
assert("header must not contain Status") { key.downcase != "status" }
|
640
643
|
## The header must conform to RFC7230 token specification, i.e. cannot
|
@@ -659,10 +662,10 @@ module Rack
|
|
659
662
|
def check_content_type(status, headers)
|
660
663
|
headers.each { |key, value|
|
661
664
|
## There must not be a <tt>Content-Type</tt>, when the +Status+ is 1xx,
|
662
|
-
## 204
|
665
|
+
## 204 or 304.
|
663
666
|
if key.downcase == "content-type"
|
664
667
|
assert("Content-Type header found in #{status} response, not allowed") {
|
665
|
-
not Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.
|
668
|
+
not Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.key? status.to_i
|
666
669
|
}
|
667
670
|
return
|
668
671
|
end
|
@@ -674,9 +677,9 @@ module Rack
|
|
674
677
|
headers.each { |key, value|
|
675
678
|
if key.downcase == 'content-length'
|
676
679
|
## There must not be a <tt>Content-Length</tt> header when the
|
677
|
-
## +Status+ is 1xx, 204
|
680
|
+
## +Status+ is 1xx, 204 or 304.
|
678
681
|
assert("Content-Length header found in #{status} response, not allowed") {
|
679
|
-
not Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.
|
682
|
+
not Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.key? status.to_i
|
680
683
|
}
|
681
684
|
@content_length = value
|
682
685
|
end
|
@@ -710,7 +713,7 @@ module Rack
|
|
710
713
|
assert("Body yielded non-string value #{part.inspect}") {
|
711
714
|
part.kind_of? String
|
712
715
|
}
|
713
|
-
bytes +=
|
716
|
+
bytes += part.bytesize
|
714
717
|
yield part
|
715
718
|
}
|
716
719
|
verify_content_length(bytes)
|
data/lib/rack/lobster.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'zlib'
|
2
4
|
|
3
5
|
require 'rack/request'
|
@@ -25,8 +27,8 @@ module Rack
|
|
25
27
|
content = ["<title>Lobstericious!</title>",
|
26
28
|
"<pre>", lobster, "</pre>",
|
27
29
|
"<a href='#{href}'>flip!</a>"]
|
28
|
-
length = content.inject(0) { |a,e| a+e.size }.to_s
|
29
|
-
[200, {CONTENT_TYPE => "text/html", CONTENT_LENGTH => length}, content]
|
30
|
+
length = content.inject(0) { |a, e| a + e.size }.to_s
|
31
|
+
[200, { CONTENT_TYPE => "text/html", CONTENT_LENGTH => length }, content]
|
30
32
|
}
|
31
33
|
|
32
34
|
def call(env)
|
@@ -37,8 +39,8 @@ module Rack
|
|
37
39
|
gsub('\\', 'TEMP').
|
38
40
|
gsub('/', '\\').
|
39
41
|
gsub('TEMP', '/').
|
40
|
-
gsub('{','}').
|
41
|
-
gsub('(',')')
|
42
|
+
gsub('{', '}').
|
43
|
+
gsub('(', ')')
|
42
44
|
end.join("\n")
|
43
45
|
href = "?flip=right"
|
44
46
|
elsif req.GET["flip"] == "crash"
|
@@ -63,8 +65,8 @@ end
|
|
63
65
|
|
64
66
|
if $0 == __FILE__
|
65
67
|
require 'rack'
|
66
|
-
require 'rack/
|
68
|
+
require 'rack/show_exceptions'
|
67
69
|
Rack::Server.start(
|
68
|
-
:
|
70
|
+
app: Rack::ShowExceptions.new(Rack::Lint.new(Rack::Lobster.new)), Port: 9292
|
69
71
|
)
|
70
72
|
end
|
data/lib/rack/lock.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'thread'
|
2
4
|
require 'rack/body_proxy'
|
3
5
|
|
@@ -5,22 +7,27 @@ module Rack
|
|
5
7
|
# Rack::Lock locks every request inside a mutex, so that every request
|
6
8
|
# will effectively be executed synchronously.
|
7
9
|
class Lock
|
8
|
-
FLAG = 'rack.multithread'.freeze
|
9
|
-
|
10
10
|
def initialize(app, mutex = Mutex.new)
|
11
11
|
@app, @mutex = app, mutex
|
12
12
|
end
|
13
13
|
|
14
14
|
def call(env)
|
15
|
-
old, env[FLAG] = env[FLAG], false
|
16
15
|
@mutex.lock
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
16
|
+
@env = env
|
17
|
+
@old_rack_multithread = env[RACK_MULTITHREAD]
|
18
|
+
begin
|
19
|
+
response = @app.call(env.merge!(RACK_MULTITHREAD => false))
|
20
|
+
returned = response << BodyProxy.new(response.pop) { unlock }
|
21
|
+
ensure
|
22
|
+
unlock unless returned
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def unlock
|
29
|
+
@mutex.unlock
|
30
|
+
@env[RACK_MULTITHREAD] = @old_rack_multithread
|
24
31
|
end
|
25
32
|
end
|
26
33
|
end
|
data/lib/rack/logger.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'logger'
|
2
4
|
|
3
5
|
module Rack
|
@@ -8,10 +10,10 @@ module Rack
|
|
8
10
|
end
|
9
11
|
|
10
12
|
def call(env)
|
11
|
-
logger = ::Logger.new(env[
|
13
|
+
logger = ::Logger.new(env[RACK_ERRORS])
|
12
14
|
logger.level = @level
|
13
15
|
|
14
|
-
env[
|
16
|
+
env[RACK_LOGGER] = logger
|
15
17
|
@app.call(env)
|
16
18
|
end
|
17
19
|
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rack
|
4
|
+
# Rack::MediaType parse media type and parameters out of content_type string
|
5
|
+
|
6
|
+
class MediaType
|
7
|
+
SPLIT_PATTERN = %r{\s*[;,]\s*}
|
8
|
+
|
9
|
+
class << self
|
10
|
+
# The media type (type/subtype) portion of the CONTENT_TYPE header
|
11
|
+
# without any media type parameters. e.g., when CONTENT_TYPE is
|
12
|
+
# "text/plain;charset=utf-8", the media-type is "text/plain".
|
13
|
+
#
|
14
|
+
# For more information on the use of media types in HTTP, see:
|
15
|
+
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7
|
16
|
+
def type(content_type)
|
17
|
+
return nil unless content_type
|
18
|
+
content_type.split(SPLIT_PATTERN, 2).first.tap &:downcase!
|
19
|
+
end
|
20
|
+
|
21
|
+
# The media type parameters provided in CONTENT_TYPE as a Hash, or
|
22
|
+
# an empty Hash if no CONTENT_TYPE or media-type parameters were
|
23
|
+
# provided. e.g., when the CONTENT_TYPE is "text/plain;charset=utf-8",
|
24
|
+
# this method responds with the following Hash:
|
25
|
+
# { 'charset' => 'utf-8' }
|
26
|
+
def params(content_type)
|
27
|
+
return {} if content_type.nil?
|
28
|
+
|
29
|
+
content_type.split(SPLIT_PATTERN)[1..-1].each_with_object({}) do |s, hsh|
|
30
|
+
k, v = s.split('=', 2)
|
31
|
+
|
32
|
+
hsh[k.tap(&:downcase!)] = strip_doublequotes(v)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def strip_doublequotes(str)
|
39
|
+
(str.start_with?('"') && str.end_with?('"')) ? str[1..-2] : str
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -1,10 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Rack
|
2
4
|
class MethodOverride
|
3
|
-
HTTP_METHODS = %w
|
5
|
+
HTTP_METHODS = %w[GET HEAD PUT POST DELETE OPTIONS PATCH LINK UNLINK]
|
4
6
|
|
5
|
-
METHOD_OVERRIDE_PARAM_KEY = "_method"
|
6
|
-
HTTP_METHOD_OVERRIDE_HEADER = "HTTP_X_HTTP_METHOD_OVERRIDE"
|
7
|
-
ALLOWED_METHODS = [
|
7
|
+
METHOD_OVERRIDE_PARAM_KEY = "_method"
|
8
|
+
HTTP_METHOD_OVERRIDE_HEADER = "HTTP_X_HTTP_METHOD_OVERRIDE"
|
9
|
+
ALLOWED_METHODS = %w[POST]
|
8
10
|
|
9
11
|
def initialize(app)
|
10
12
|
@app = app
|
@@ -14,7 +16,7 @@ module Rack
|
|
14
16
|
if allowed_methods.include?(env[REQUEST_METHOD])
|
15
17
|
method = method_override(env)
|
16
18
|
if HTTP_METHODS.include?(method)
|
17
|
-
env[
|
19
|
+
env[RACK_METHODOVERRIDE_ORIGINAL_METHOD] = env[REQUEST_METHOD]
|
18
20
|
env[REQUEST_METHOD] = method
|
19
21
|
end
|
20
22
|
end
|
@@ -29,7 +31,7 @@ module Rack
|
|
29
31
|
begin
|
30
32
|
method.to_s.upcase
|
31
33
|
rescue ArgumentError
|
32
|
-
env[
|
34
|
+
env[RACK_ERRORS].puts "Invalid string for method"
|
33
35
|
end
|
34
36
|
end
|
35
37
|
|
@@ -42,9 +44,9 @@ module Rack
|
|
42
44
|
def method_override_param(req)
|
43
45
|
req.POST[METHOD_OVERRIDE_PARAM_KEY]
|
44
46
|
rescue Utils::InvalidParameterError, Utils::ParameterTypeError
|
45
|
-
req.
|
47
|
+
req.get_header(RACK_ERRORS).puts "Invalid or incomplete POST params"
|
46
48
|
rescue EOFError
|
47
|
-
req.
|
49
|
+
req.get_header(RACK_ERRORS).puts "Bad request content body"
|
48
50
|
end
|
49
51
|
end
|
50
52
|
end
|
data/lib/rack/mime.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Rack
|
2
4
|
module Mime
|
3
5
|
# Returns String with mime type if found, otherwise use +fallback+.
|
@@ -13,7 +15,7 @@ module Rack
|
|
13
15
|
# This is a shortcut for:
|
14
16
|
# Rack::Mime::MIME_TYPES.fetch('.foo', 'application/octet-stream')
|
15
17
|
|
16
|
-
def mime_type(ext, fallback='application/octet-stream')
|
18
|
+
def mime_type(ext, fallback = 'application/octet-stream')
|
17
19
|
MIME_TYPES.fetch(ext.to_s.downcase, fallback)
|
18
20
|
end
|
19
21
|
module_function :mime_type
|
@@ -45,11 +47,6 @@ module Rack
|
|
45
47
|
#
|
46
48
|
# N.B. On Ubuntu the mime.types file does not include the leading period, so
|
47
49
|
# users may need to modify the data before merging into the hash.
|
48
|
-
#
|
49
|
-
# To add the list mongrel provides, use:
|
50
|
-
#
|
51
|
-
# require 'mongrel/handlers'
|
52
|
-
# Rack::Mime::MIME_TYPES.merge!(Mongrel::DirHandler::MIME_TYPES)
|
53
50
|
|
54
51
|
MIME_TYPES = {
|
55
52
|
".123" => "application/vnd.lotus-1-2-3",
|
@@ -154,8 +151,11 @@ module Rack
|
|
154
151
|
".dmg" => "application/octet-stream",
|
155
152
|
".dna" => "application/vnd.dna",
|
156
153
|
".doc" => "application/msword",
|
154
|
+
".docm" => "application/vnd.ms-word.document.macroEnabled.12",
|
157
155
|
".docx" => "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
158
156
|
".dot" => "application/msword",
|
157
|
+
".dotm" => "application/vnd.ms-word.template.macroEnabled.12",
|
158
|
+
".dotx" => "application/vnd.openxmlformats-officedocument.wordprocessingml.template",
|
159
159
|
".dp" => "application/vnd.osgi.dp",
|
160
160
|
".dpg" => "application/vnd.dpgraph",
|
161
161
|
".dsc" => "text/prs.lines.tag",
|
@@ -308,6 +308,7 @@ module Rack
|
|
308
308
|
".lvp" => "audio/vnd.lucent.voice",
|
309
309
|
".lwp" => "application/vnd.lotus-wordpro",
|
310
310
|
".m3u" => "audio/x-mpegurl",
|
311
|
+
".m3u8" => "application/x-mpegurl",
|
311
312
|
".m4a" => "audio/mp4a-latm",
|
312
313
|
".m4v" => "video/mp4",
|
313
314
|
".ma" => "application/mathematica",
|
@@ -345,6 +346,7 @@ module Rack
|
|
345
346
|
".mp4s" => "application/mp4",
|
346
347
|
".mp4v" => "video/mp4",
|
347
348
|
".mpc" => "application/vnd.mophun.certificate",
|
349
|
+
".mpd" => "application/dash+xml",
|
348
350
|
".mpeg" => "video/mpeg",
|
349
351
|
".mpg" => "video/mpeg",
|
350
352
|
".mpga" => "audio/mpeg",
|
@@ -444,10 +446,19 @@ module Rack
|
|
444
446
|
".pnm" => "image/x-portable-anymap",
|
445
447
|
".pntg" => "image/x-macpaint",
|
446
448
|
".portpkg" => "application/vnd.macports.portpkg",
|
449
|
+
".pot" => "application/vnd.ms-powerpoint",
|
450
|
+
".potm" => "application/vnd.ms-powerpoint.template.macroEnabled.12",
|
451
|
+
".potx" => "application/vnd.openxmlformats-officedocument.presentationml.template",
|
452
|
+
".ppa" => "application/vnd.ms-powerpoint",
|
453
|
+
".ppam" => "application/vnd.ms-powerpoint.addin.macroEnabled.12",
|
447
454
|
".ppd" => "application/vnd.cups-ppd",
|
448
455
|
".ppm" => "image/x-portable-pixmap",
|
449
456
|
".pps" => "application/vnd.ms-powerpoint",
|
457
|
+
".ppsm" => "application/vnd.ms-powerpoint.slideshow.macroEnabled.12",
|
458
|
+
".ppsx" => "application/vnd.openxmlformats-officedocument.presentationml.slideshow",
|
450
459
|
".ppt" => "application/vnd.ms-powerpoint",
|
460
|
+
".pptm" => "application/vnd.ms-powerpoint.presentation.macroEnabled.12",
|
461
|
+
".pptx" => "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
451
462
|
".prc" => "application/vnd.palm",
|
452
463
|
".pre" => "application/vnd.lotus-freelance",
|
453
464
|
".prf" => "application/pics-rules",
|
@@ -535,6 +546,7 @@ module Rack
|
|
535
546
|
".spp" => "application/scvp-vp-response",
|
536
547
|
".spq" => "application/scvp-vp-request",
|
537
548
|
".src" => "application/x-wais-source",
|
549
|
+
".srt" => "text/srt",
|
538
550
|
".srx" => "application/sparql-results+xml",
|
539
551
|
".sse" => "application/vnd.kodak-descriptor",
|
540
552
|
".ssf" => "application/vnd.epson.ssf",
|
@@ -569,6 +581,7 @@ module Rack
|
|
569
581
|
".tr" => "text/troff",
|
570
582
|
".tra" => "application/vnd.trueapp",
|
571
583
|
".trm" => "application/x-msterminal",
|
584
|
+
".ts" => "video/mp2t",
|
572
585
|
".tsv" => "text/tab-separated-values",
|
573
586
|
".ttf" => "application/octet-stream",
|
574
587
|
".twd" => "application/vnd.simtech-mindmapper",
|
@@ -593,9 +606,11 @@ module Rack
|
|
593
606
|
".vrml" => "model/vrml",
|
594
607
|
".vsd" => "application/vnd.visio",
|
595
608
|
".vsf" => "application/vnd.vsf",
|
609
|
+
".vtt" => "text/vtt",
|
596
610
|
".vtu" => "model/vnd.vtu",
|
597
611
|
".vxml" => "application/voicexml+xml",
|
598
612
|
".war" => "application/java-archive",
|
613
|
+
".wasm" => "application/wasm",
|
599
614
|
".wav" => "audio/x-wav",
|
600
615
|
".wax" => "audio/x-ms-wax",
|
601
616
|
".wbmp" => "image/vnd.wap.wbmp",
|
@@ -638,8 +653,14 @@ module Rack
|
|
638
653
|
".xfdl" => "application/vnd.xfdl",
|
639
654
|
".xhtml" => "application/xhtml+xml",
|
640
655
|
".xif" => "image/vnd.xiff",
|
656
|
+
".xla" => "application/vnd.ms-excel",
|
657
|
+
".xlam" => "application/vnd.ms-excel.addin.macroEnabled.12",
|
641
658
|
".xls" => "application/vnd.ms-excel",
|
659
|
+
".xlsb" => "application/vnd.ms-excel.sheet.binary.macroEnabled.12",
|
642
660
|
".xlsx" => "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
661
|
+
".xlsm" => "application/vnd.ms-excel.sheet.macroEnabled.12",
|
662
|
+
".xlt" => "application/vnd.ms-excel",
|
663
|
+
".xltx" => "application/vnd.openxmlformats-officedocument.spreadsheetml.template",
|
643
664
|
".xml" => "application/xml",
|
644
665
|
".xo" => "application/vnd.olpc-sugar",
|
645
666
|
".xop" => "application/xop+xml",
|