rack 2.0.7 → 2.2.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 +5 -5
- data/CHANGELOG.md +708 -0
- data/CONTRIBUTING.md +136 -0
- data/{COPYING → MIT-LICENSE} +4 -2
- data/README.rdoc +152 -148
- data/Rakefile +37 -23
- data/{SPEC → SPEC.rdoc} +35 -10
- data/bin/rackup +1 -0
- data/example/lobster.ru +2 -0
- data/example/protectedlobster.rb +3 -1
- data/example/protectedlobster.ru +2 -0
- data/lib/rack/auth/abstract/handler.rb +3 -1
- data/lib/rack/auth/abstract/request.rb +1 -1
- data/lib/rack/auth/basic.rb +7 -4
- data/lib/rack/auth/digest/md5.rb +13 -11
- data/lib/rack/auth/digest/nonce.rb +6 -3
- data/lib/rack/auth/digest/params.rb +4 -2
- data/lib/rack/auth/digest/request.rb +5 -3
- data/lib/rack/body_proxy.rb +15 -14
- data/lib/rack/builder.rb +116 -23
- data/lib/rack/cascade.rb +28 -12
- data/lib/rack/chunked.rb +68 -20
- data/lib/rack/common_logger.rb +36 -25
- data/lib/rack/conditional_get.rb +20 -16
- data/lib/rack/config.rb +2 -0
- data/lib/rack/content_length.rb +8 -7
- data/lib/rack/content_type.rb +5 -4
- data/lib/rack/core_ext/regexp.rb +14 -0
- data/lib/rack/deflater.rb +59 -34
- data/lib/rack/directory.rb +84 -64
- data/lib/rack/etag.rb +7 -4
- data/lib/rack/events.rb +19 -20
- data/lib/rack/file.rb +4 -173
- data/lib/rack/files.rb +218 -0
- data/lib/rack/handler/cgi.rb +2 -3
- data/lib/rack/handler/fastcgi.rb +4 -4
- data/lib/rack/handler/lsws.rb +3 -3
- data/lib/rack/handler/scgi.rb +9 -8
- data/lib/rack/handler/thin.rb +3 -3
- data/lib/rack/handler/webrick.rb +15 -6
- data/lib/rack/handler.rb +7 -2
- data/lib/rack/head.rb +1 -1
- data/lib/rack/lint.rb +72 -26
- data/lib/rack/lobster.rb +10 -10
- data/lib/rack/lock.rb +2 -1
- data/lib/rack/logger.rb +2 -0
- data/lib/rack/media_type.rb +10 -5
- data/lib/rack/method_override.rb +4 -2
- data/lib/rack/mime.rb +9 -1
- data/lib/rack/mock.rb +97 -20
- data/lib/rack/multipart/generator.rb +17 -13
- data/lib/rack/multipart/parser.rb +56 -57
- data/lib/rack/multipart/uploaded_file.rb +15 -7
- data/lib/rack/multipart.rb +5 -4
- data/lib/rack/null_logger.rb +2 -0
- data/lib/rack/query_parser.rb +59 -30
- data/lib/rack/recursive.rb +7 -5
- data/lib/rack/reloader.rb +8 -4
- data/lib/rack/request.rb +220 -61
- data/lib/rack/response.rb +127 -44
- data/lib/rack/rewindable_input.rb +4 -3
- data/lib/rack/runtime.rb +6 -4
- data/lib/rack/sendfile.rb +13 -9
- data/lib/rack/server.rb +95 -24
- data/lib/rack/session/abstract/id.rb +100 -22
- data/lib/rack/session/cookie.rb +22 -14
- data/lib/rack/session/memcache.rb +4 -87
- data/lib/rack/session/pool.rb +18 -9
- data/lib/rack/show_exceptions.rb +21 -17
- data/lib/rack/show_status.rb +9 -9
- data/lib/rack/static.rb +23 -11
- data/lib/rack/tempfile_reaper.rb +1 -1
- data/lib/rack/urlmap.rb +12 -6
- data/lib/rack/utils.rb +107 -111
- data/lib/rack/version.rb +29 -0
- data/lib/rack.rb +67 -73
- data/rack.gemspec +40 -28
- metadata +36 -178
- data/HISTORY.md +0 -505
- data/test/builder/an_underscore_app.rb +0 -5
- 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 -9
- data/test/cgi/test.gz +0 -0
- data/test/cgi/test.ru +0 -5
- data/test/gemloader.rb +0 -10
- data/test/helper.rb +0 -34
- 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_encoded_words +0 -7
- 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_single_quote +0 -7
- 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/quoted +0 -15
- data/test/multipart/rack-logo.png +0 -0
- data/test/multipart/semicolon +0 -6
- data/test/multipart/text +0 -15
- data/test/multipart/three_files_three_fields +0 -31
- data/test/multipart/unity3d_wwwform +0 -11
- 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 -89
- data/test/spec_auth_digest.rb +0 -260
- data/test/spec_body_proxy.rb +0 -85
- data/test/spec_builder.rb +0 -233
- data/test/spec_cascade.rb +0 -63
- data/test/spec_cgi.rb +0 -84
- data/test/spec_chunked.rb +0 -103
- data/test/spec_common_logger.rb +0 -95
- data/test/spec_conditional_get.rb +0 -103
- data/test/spec_config.rb +0 -23
- data/test/spec_content_length.rb +0 -86
- data/test/spec_content_type.rb +0 -46
- data/test/spec_deflater.rb +0 -375
- data/test/spec_directory.rb +0 -148
- data/test/spec_etag.rb +0 -108
- data/test/spec_events.rb +0 -133
- data/test/spec_fastcgi.rb +0 -85
- data/test/spec_file.rb +0 -264
- data/test/spec_handler.rb +0 -57
- data/test/spec_head.rb +0 -46
- data/test/spec_lint.rb +0 -515
- data/test/spec_lobster.rb +0 -59
- data/test/spec_lock.rb +0 -204
- data/test/spec_logger.rb +0 -24
- data/test/spec_media_type.rb +0 -42
- data/test/spec_method_override.rb +0 -110
- data/test/spec_mime.rb +0 -51
- data/test/spec_mock.rb +0 -359
- data/test/spec_multipart.rb +0 -722
- data/test/spec_null_logger.rb +0 -21
- data/test/spec_recursive.rb +0 -75
- data/test/spec_request.rb +0 -1407
- data/test/spec_response.rb +0 -510
- data/test/spec_rewindable_input.rb +0 -128
- data/test/spec_runtime.rb +0 -50
- data/test/spec_sendfile.rb +0 -125
- data/test/spec_server.rb +0 -193
- data/test/spec_session_abstract_id.rb +0 -31
- data/test/spec_session_abstract_session_hash.rb +0 -45
- data/test/spec_session_cookie.rb +0 -442
- data/test/spec_session_memcache.rb +0 -320
- data/test/spec_session_pool.rb +0 -210
- data/test/spec_show_exceptions.rb +0 -93
- data/test/spec_show_status.rb +0 -104
- data/test/spec_static.rb +0 -184
- data/test/spec_tempfile_reaper.rb +0 -64
- data/test/spec_thin.rb +0 -96
- data/test/spec_urlmap.rb +0 -237
- data/test/spec_utils.rb +0 -742
- data/test/spec_version.rb +0 -11
- data/test/spec_webrick.rb +0 -206
- data/test/static/another/index.html +0 -1
- data/test/static/foo.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,4 +1,5 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'forwardable'
|
3
4
|
|
4
5
|
module Rack
|
@@ -33,7 +34,7 @@ module Rack
|
|
33
34
|
|
34
35
|
## A Rack application is a Ruby object (not a class) that
|
35
36
|
## responds to +call+.
|
36
|
-
def call(env=nil)
|
37
|
+
def call(env = nil)
|
37
38
|
dup._call(env)
|
38
39
|
end
|
39
40
|
|
@@ -46,13 +47,24 @@ module Rack
|
|
46
47
|
env[RACK_ERRORS] = ErrorWrapper.new(env[RACK_ERRORS])
|
47
48
|
|
48
49
|
## and returns an Array of exactly three values:
|
49
|
-
|
50
|
+
ary = @app.call(env)
|
51
|
+
assert("response is not an Array, but #{ary.class}") {
|
52
|
+
ary.kind_of? Array
|
53
|
+
}
|
54
|
+
assert("response array has #{ary.size} elements instead of 3") {
|
55
|
+
ary.size == 3
|
56
|
+
}
|
57
|
+
|
58
|
+
status, headers, @body = ary
|
50
59
|
## The *status*,
|
51
60
|
check_status status
|
52
61
|
## the *headers*,
|
53
62
|
check_headers headers
|
54
63
|
|
55
|
-
check_hijack_response headers, env
|
64
|
+
hijack_proc = check_hijack_response headers, env
|
65
|
+
if hijack_proc && headers.is_a?(Hash)
|
66
|
+
headers[RACK_HIJACK] = hijack_proc
|
67
|
+
end
|
56
68
|
|
57
69
|
## and the *body*.
|
58
70
|
check_content_type status, headers
|
@@ -63,12 +75,15 @@ module Rack
|
|
63
75
|
|
64
76
|
## == The Environment
|
65
77
|
def check_env(env)
|
66
|
-
## The environment must be an instance of Hash that includes
|
78
|
+
## The environment must be an unfrozen instance of Hash that includes
|
67
79
|
## CGI-like headers. The application is free to modify the
|
68
80
|
## environment.
|
69
81
|
assert("env #{env.inspect} is not a Hash, but #{env.class}") {
|
70
82
|
env.kind_of? Hash
|
71
83
|
}
|
84
|
+
assert("env should not be frozen, but is") {
|
85
|
+
!env.frozen?
|
86
|
+
}
|
72
87
|
|
73
88
|
##
|
74
89
|
## The environment is required to include these variables
|
@@ -102,17 +117,19 @@ module Rack
|
|
102
117
|
## follows the <tt>?</tt>, if any. May be
|
103
118
|
## empty, but is always required!
|
104
119
|
|
105
|
-
## <tt>SERVER_NAME</tt
|
106
|
-
## When combined with <tt>SCRIPT_NAME</tt> and
|
120
|
+
## <tt>SERVER_NAME</tt>:: When combined with <tt>SCRIPT_NAME</tt> and
|
107
121
|
## <tt>PATH_INFO</tt>, these variables can be
|
108
122
|
## used to complete the URL. Note, however,
|
109
123
|
## that <tt>HTTP_HOST</tt>, if present,
|
110
124
|
## should be used in preference to
|
111
125
|
## <tt>SERVER_NAME</tt> for reconstructing
|
112
126
|
## the request URL.
|
113
|
-
## <tt>SERVER_NAME</tt>
|
114
|
-
##
|
115
|
-
|
127
|
+
## <tt>SERVER_NAME</tt> can never be an empty
|
128
|
+
## string, and so is always required.
|
129
|
+
|
130
|
+
## <tt>SERVER_PORT</tt>:: An optional +Integer+ which is the port the
|
131
|
+
## server is running on. Should be specified if
|
132
|
+
## the server is running on a non-standard port.
|
116
133
|
|
117
134
|
## <tt>HTTP_</tt> Variables:: Variables corresponding to the
|
118
135
|
## client-supplied HTTP request
|
@@ -123,9 +140,8 @@ module Rack
|
|
123
140
|
## the presence or absence of the
|
124
141
|
## appropriate HTTP header in the
|
125
142
|
## request. See
|
126
|
-
##
|
127
|
-
##
|
128
|
-
## specific behavior.
|
143
|
+
## {RFC3875 section 4.1.18}[https://tools.ietf.org/html/rfc3875#section-4.1.18]
|
144
|
+
## for specific behavior.
|
129
145
|
|
130
146
|
## In addition to this, the Rack environment must include these
|
131
147
|
## Rack-specific variables:
|
@@ -197,6 +213,11 @@ module Rack
|
|
197
213
|
assert("session #{session.inspect} must respond to clear") {
|
198
214
|
session.respond_to?(:clear)
|
199
215
|
}
|
216
|
+
|
217
|
+
## to_hash (returning unfrozen Hash instance);
|
218
|
+
assert("session #{session.inspect} must respond to to_hash and return unfrozen Hash instance") {
|
219
|
+
session.respond_to?(:to_hash) && session.to_hash.kind_of?(Hash) && !session.to_hash.frozen?
|
220
|
+
}
|
200
221
|
end
|
201
222
|
|
202
223
|
## <tt>rack.logger</tt>:: A common object interface for logging messages.
|
@@ -252,28 +273,49 @@ module Rack
|
|
252
273
|
## accepted specifications and must not be used otherwise.
|
253
274
|
##
|
254
275
|
|
255
|
-
%w[REQUEST_METHOD SERVER_NAME
|
256
|
-
QUERY_STRING
|
276
|
+
%w[REQUEST_METHOD SERVER_NAME QUERY_STRING
|
257
277
|
rack.version rack.input rack.errors
|
258
278
|
rack.multithread rack.multiprocess rack.run_once].each { |header|
|
259
279
|
assert("env missing required key #{header}") { env.include? header }
|
260
280
|
}
|
261
281
|
|
282
|
+
## The <tt>SERVER_PORT</tt> must be an Integer if set.
|
283
|
+
assert("env[SERVER_PORT] is not an Integer") do
|
284
|
+
server_port = env["SERVER_PORT"]
|
285
|
+
server_port.nil? || (Integer(server_port) rescue false)
|
286
|
+
end
|
287
|
+
|
288
|
+
## The <tt>SERVER_NAME</tt> must be a valid authority as defined by RFC7540.
|
289
|
+
assert("#{env[SERVER_NAME]} must be a valid authority") do
|
290
|
+
URI.parse("http://#{env[SERVER_NAME]}/") rescue false
|
291
|
+
end
|
292
|
+
|
293
|
+
## The <tt>HTTP_HOST</tt> must be a valid authority as defined by RFC7540.
|
294
|
+
assert("#{env[HTTP_HOST]} must be a valid authority") do
|
295
|
+
URI.parse("http://#{env[HTTP_HOST]}/") rescue false
|
296
|
+
end
|
297
|
+
|
262
298
|
## The environment must not contain the keys
|
263
299
|
## <tt>HTTP_CONTENT_TYPE</tt> or <tt>HTTP_CONTENT_LENGTH</tt>
|
264
300
|
## (use the versions without <tt>HTTP_</tt>).
|
265
301
|
%w[HTTP_CONTENT_TYPE HTTP_CONTENT_LENGTH].each { |header|
|
266
|
-
assert("env contains #{header}, must use #{header[5
|
302
|
+
assert("env contains #{header}, must use #{header[5, -1]}") {
|
267
303
|
not env.include? header
|
268
304
|
}
|
269
305
|
}
|
270
306
|
|
271
307
|
## The CGI keys (named without a period) must have String values.
|
308
|
+
## If the string values for CGI keys contain non-ASCII characters,
|
309
|
+
## they should use ASCII-8BIT encoding.
|
272
310
|
env.each { |key, value|
|
273
311
|
next if key.include? "." # Skip extensions
|
274
312
|
assert("env variable #{key} has non-string value #{value.inspect}") {
|
275
313
|
value.kind_of? String
|
276
314
|
}
|
315
|
+
next if value.encoding == Encoding::ASCII_8BIT
|
316
|
+
assert("env variable #{key} has value containing non-ASCII characters and has non-ASCII-8BIT encoding #{value.inspect} encoding: #{value.encoding}") {
|
317
|
+
value.b !~ /[\x80-\xff]/n
|
318
|
+
}
|
277
319
|
}
|
278
320
|
|
279
321
|
## There are the following restrictions:
|
@@ -295,7 +337,7 @@ module Rack
|
|
295
337
|
check_hijack env
|
296
338
|
|
297
339
|
## * The <tt>REQUEST_METHOD</tt> must be a valid token.
|
298
|
-
assert("REQUEST_METHOD unknown: #{env[REQUEST_METHOD]}") {
|
340
|
+
assert("REQUEST_METHOD unknown: #{env[REQUEST_METHOD].dump}") {
|
299
341
|
env[REQUEST_METHOD] =~ /\A[0-9A-Za-z!\#$%&'*+.^_`|~-]+\z/
|
300
342
|
}
|
301
343
|
|
@@ -336,7 +378,7 @@ module Rack
|
|
336
378
|
## When applicable, its external encoding must be "ASCII-8BIT" and it
|
337
379
|
## must be opened in binary mode, for Ruby 1.9 compatibility.
|
338
380
|
assert("rack.input #{input} does not have ASCII-8BIT as its external encoding") {
|
339
|
-
input.external_encoding
|
381
|
+
input.external_encoding == Encoding::ASCII_8BIT
|
340
382
|
} if input.respond_to?(:external_encoding)
|
341
383
|
assert("rack.input #{input} is not opened in binary mode") {
|
342
384
|
input.binmode?
|
@@ -568,7 +610,7 @@ module Rack
|
|
568
610
|
|
569
611
|
# this check uses headers like a hash, but the spec only requires
|
570
612
|
# headers respond to #each
|
571
|
-
headers = Rack::Utils::HeaderHash
|
613
|
+
headers = Rack::Utils::HeaderHash[headers]
|
572
614
|
|
573
615
|
## In order to do this, an application may set the special header
|
574
616
|
## <tt>rack.hijack</tt> to an object that responds to <tt>call</tt>
|
@@ -592,7 +634,7 @@ module Rack
|
|
592
634
|
headers[RACK_HIJACK].respond_to? :call
|
593
635
|
}
|
594
636
|
original_hijack = headers[RACK_HIJACK]
|
595
|
-
|
637
|
+
proc do |io|
|
596
638
|
original_hijack.call HijackWrapper.new(io)
|
597
639
|
end
|
598
640
|
else
|
@@ -602,6 +644,8 @@ module Rack
|
|
602
644
|
assert('rack.hijack header must not be present if server does not support hijacking') {
|
603
645
|
headers[RACK_HIJACK].nil?
|
604
646
|
}
|
647
|
+
|
648
|
+
nil
|
605
649
|
end
|
606
650
|
end
|
607
651
|
## ==== Conventions
|
@@ -626,15 +670,17 @@ module Rack
|
|
626
670
|
assert("headers object should respond to #each, but doesn't (got #{header.class} as headers)") {
|
627
671
|
header.respond_to? :each
|
628
672
|
}
|
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
673
|
|
674
|
+
header.each { |key, value|
|
634
675
|
## The header keys must be Strings.
|
635
676
|
assert("header key must be a string, was #{key.class}") {
|
636
677
|
key.kind_of? String
|
637
678
|
}
|
679
|
+
|
680
|
+
## Special headers starting "rack." are for communicating with the
|
681
|
+
## server, and must not be sent back to the client.
|
682
|
+
next if key =~ /^rack\..+$/
|
683
|
+
|
638
684
|
## The header must not contain a +Status+ key.
|
639
685
|
assert("header must not contain Status") { key.downcase != "status" }
|
640
686
|
## The header must conform to RFC7230 token specification, i.e. cannot
|
@@ -662,7 +708,7 @@ module Rack
|
|
662
708
|
## 204 or 304.
|
663
709
|
if key.downcase == "content-type"
|
664
710
|
assert("Content-Type header found in #{status} response, not allowed") {
|
665
|
-
not Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.
|
711
|
+
not Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.key? status.to_i
|
666
712
|
}
|
667
713
|
return
|
668
714
|
end
|
@@ -676,7 +722,7 @@ module Rack
|
|
676
722
|
## There must not be a <tt>Content-Length</tt> header when the
|
677
723
|
## +Status+ is 1xx, 204 or 304.
|
678
724
|
assert("Content-Length header found in #{status} response, not allowed") {
|
679
|
-
not Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.
|
725
|
+
not Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.key? status.to_i
|
680
726
|
}
|
681
727
|
@content_length = value
|
682
728
|
end
|
data/lib/rack/lobster.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require '
|
4
|
-
require 'rack/response'
|
3
|
+
require 'zlib'
|
5
4
|
|
6
5
|
module Rack
|
7
6
|
# Paste has a Pony, Rack has a Lobster!
|
@@ -25,8 +24,8 @@ module Rack
|
|
25
24
|
content = ["<title>Lobstericious!</title>",
|
26
25
|
"<pre>", lobster, "</pre>",
|
27
26
|
"<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]
|
27
|
+
length = content.inject(0) { |a, e| a + e.size }.to_s
|
28
|
+
[200, { CONTENT_TYPE => "text/html", CONTENT_LENGTH => length }, content]
|
30
29
|
}
|
31
30
|
|
32
31
|
def call(env)
|
@@ -37,8 +36,8 @@ module Rack
|
|
37
36
|
gsub('\\', 'TEMP').
|
38
37
|
gsub('/', '\\').
|
39
38
|
gsub('TEMP', '/').
|
40
|
-
gsub('{','}').
|
41
|
-
gsub('(',')')
|
39
|
+
gsub('{', '}').
|
40
|
+
gsub('(', ')')
|
42
41
|
end.join("\n")
|
43
42
|
href = "?flip=right"
|
44
43
|
elsif req.GET["flip"] == "crash"
|
@@ -62,9 +61,10 @@ module Rack
|
|
62
61
|
end
|
63
62
|
|
64
63
|
if $0 == __FILE__
|
65
|
-
|
66
|
-
|
64
|
+
# :nocov:
|
65
|
+
require_relative '../rack'
|
67
66
|
Rack::Server.start(
|
68
|
-
:
|
67
|
+
app: Rack::ShowExceptions.new(Rack::Lint.new(Rack::Lobster.new)), Port: 9292
|
69
68
|
)
|
69
|
+
# :nocov:
|
70
70
|
end
|
data/lib/rack/lock.rb
CHANGED
data/lib/rack/logger.rb
CHANGED
data/lib/rack/media_type.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Rack
|
2
4
|
# Rack::MediaType parse media type and parameters out of content_type string
|
3
5
|
|
@@ -13,7 +15,7 @@ module Rack
|
|
13
15
|
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7
|
14
16
|
def type(content_type)
|
15
17
|
return nil unless content_type
|
16
|
-
content_type.split(SPLIT_PATTERN, 2).first.downcase
|
18
|
+
content_type.split(SPLIT_PATTERN, 2).first.tap &:downcase!
|
17
19
|
end
|
18
20
|
|
19
21
|
# The media type parameters provided in CONTENT_TYPE as a Hash, or
|
@@ -23,15 +25,18 @@ module Rack
|
|
23
25
|
# { 'charset' => 'utf-8' }
|
24
26
|
def params(content_type)
|
25
27
|
return {} if content_type.nil?
|
26
|
-
|
27
|
-
|
28
|
-
|
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
|
29
34
|
end
|
30
35
|
|
31
36
|
private
|
32
37
|
|
33
38
|
def strip_doublequotes(str)
|
34
|
-
(str
|
39
|
+
(str.start_with?('"') && str.end_with?('"')) ? str[1..-2] : str
|
35
40
|
end
|
36
41
|
end
|
37
42
|
end
|
data/lib/rack/method_override.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Rack
|
2
4
|
class MethodOverride
|
3
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
|
+
METHOD_OVERRIDE_PARAM_KEY = "_method"
|
8
|
+
HTTP_METHOD_OVERRIDE_HEADER = "HTTP_X_HTTP_METHOD_OVERRIDE"
|
7
9
|
ALLOWED_METHODS = %w[POST]
|
8
10
|
|
9
11
|
def initialize(app)
|
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
|
@@ -306,6 +308,7 @@ module Rack
|
|
306
308
|
".lvp" => "audio/vnd.lucent.voice",
|
307
309
|
".lwp" => "application/vnd.lotus-wordpro",
|
308
310
|
".m3u" => "audio/x-mpegurl",
|
311
|
+
".m3u8" => "application/x-mpegurl",
|
309
312
|
".m4a" => "audio/mp4a-latm",
|
310
313
|
".m4v" => "video/mp4",
|
311
314
|
".ma" => "application/mathematica",
|
@@ -343,6 +346,7 @@ module Rack
|
|
343
346
|
".mp4s" => "application/mp4",
|
344
347
|
".mp4v" => "video/mp4",
|
345
348
|
".mpc" => "application/vnd.mophun.certificate",
|
349
|
+
".mpd" => "application/dash+xml",
|
346
350
|
".mpeg" => "video/mpeg",
|
347
351
|
".mpg" => "video/mpeg",
|
348
352
|
".mpga" => "audio/mpeg",
|
@@ -542,6 +546,7 @@ module Rack
|
|
542
546
|
".spp" => "application/scvp-vp-response",
|
543
547
|
".spq" => "application/scvp-vp-request",
|
544
548
|
".src" => "application/x-wais-source",
|
549
|
+
".srt" => "text/srt",
|
545
550
|
".srx" => "application/sparql-results+xml",
|
546
551
|
".sse" => "application/vnd.kodak-descriptor",
|
547
552
|
".ssf" => "application/vnd.epson.ssf",
|
@@ -576,6 +581,7 @@ module Rack
|
|
576
581
|
".tr" => "text/troff",
|
577
582
|
".tra" => "application/vnd.trueapp",
|
578
583
|
".trm" => "application/x-msterminal",
|
584
|
+
".ts" => "video/mp2t",
|
579
585
|
".tsv" => "text/tab-separated-values",
|
580
586
|
".ttf" => "application/octet-stream",
|
581
587
|
".twd" => "application/vnd.simtech-mindmapper",
|
@@ -600,9 +606,11 @@ module Rack
|
|
600
606
|
".vrml" => "model/vrml",
|
601
607
|
".vsd" => "application/vnd.visio",
|
602
608
|
".vsf" => "application/vnd.vsf",
|
609
|
+
".vtt" => "text/vtt",
|
603
610
|
".vtu" => "model/vnd.vtu",
|
604
611
|
".vxml" => "application/voicexml+xml",
|
605
612
|
".war" => "application/java-archive",
|
613
|
+
".wasm" => "application/wasm",
|
606
614
|
".wav" => "audio/x-wav",
|
607
615
|
".wax" => "audio/x-ms-wax",
|
608
616
|
".wbmp" => "image/vnd.wap.wbmp",
|
data/lib/rack/mock.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'uri'
|
2
4
|
require 'stringio'
|
3
|
-
|
4
|
-
require '
|
5
|
-
require 'rack/utils'
|
6
|
-
require 'rack/response'
|
5
|
+
require_relative '../rack'
|
6
|
+
require 'cgi/cookie'
|
7
7
|
|
8
8
|
module Rack
|
9
9
|
# Rack::MockRequest helps testing your Rack application without
|
@@ -53,16 +53,26 @@ module Rack
|
|
53
53
|
@app = app
|
54
54
|
end
|
55
55
|
|
56
|
-
|
57
|
-
def
|
58
|
-
|
59
|
-
def
|
60
|
-
|
61
|
-
def
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
56
|
+
# Make a GET request and return a MockResponse. See #request.
|
57
|
+
def get(uri, opts = {}) request(GET, uri, opts) end
|
58
|
+
# Make a POST request and return a MockResponse. See #request.
|
59
|
+
def post(uri, opts = {}) request(POST, uri, opts) end
|
60
|
+
# Make a PUT request and return a MockResponse. See #request.
|
61
|
+
def put(uri, opts = {}) request(PUT, uri, opts) end
|
62
|
+
# Make a PATCH request and return a MockResponse. See #request.
|
63
|
+
def patch(uri, opts = {}) request(PATCH, uri, opts) end
|
64
|
+
# Make a DELETE request and return a MockResponse. See #request.
|
65
|
+
def delete(uri, opts = {}) request(DELETE, uri, opts) end
|
66
|
+
# Make a HEAD request and return a MockResponse. See #request.
|
67
|
+
def head(uri, opts = {}) request(HEAD, uri, opts) end
|
68
|
+
# Make an OPTIONS request and return a MockResponse. See #request.
|
69
|
+
def options(uri, opts = {}) request(OPTIONS, uri, opts) end
|
70
|
+
|
71
|
+
# Make a request using the given request method for the given
|
72
|
+
# uri to the rack application and return a MockResponse.
|
73
|
+
# Options given are passed to MockRequest.env_for.
|
74
|
+
def request(method = GET, uri = "", opts = {})
|
75
|
+
env = self.class.env_for(uri, opts.merge(method: method))
|
66
76
|
|
67
77
|
if opts[:lint]
|
68
78
|
app = Rack::Lint.new(@app)
|
@@ -71,7 +81,7 @@ module Rack
|
|
71
81
|
end
|
72
82
|
|
73
83
|
errors = env[RACK_ERRORS]
|
74
|
-
status, headers, body
|
84
|
+
status, headers, body = app.call(env)
|
75
85
|
MockResponse.new(status, headers, body, errors)
|
76
86
|
ensure
|
77
87
|
body.close if body.respond_to?(:close)
|
@@ -85,7 +95,14 @@ module Rack
|
|
85
95
|
end
|
86
96
|
|
87
97
|
# Return the Rack environment used for a request to +uri+.
|
88
|
-
|
98
|
+
# All options that are strings are added to the returned environment.
|
99
|
+
# Options:
|
100
|
+
# :fatal :: Whether to raise an exception if request outputs to rack.errors
|
101
|
+
# :input :: The rack.input to set
|
102
|
+
# :method :: The HTTP request method to use
|
103
|
+
# :params :: The params to use
|
104
|
+
# :script_name :: The SCRIPT_NAME to set
|
105
|
+
def self.env_for(uri = "", opts = {})
|
89
106
|
uri = parse_uri_rfc2396(uri)
|
90
107
|
uri.path = "/#{uri.path}" unless uri.path[0] == ?/
|
91
108
|
|
@@ -139,7 +156,7 @@ module Rack
|
|
139
156
|
rack_input.set_encoding(Encoding::BINARY)
|
140
157
|
env[RACK_INPUT] = rack_input
|
141
158
|
|
142
|
-
env["CONTENT_LENGTH"] ||= env[RACK_INPUT].
|
159
|
+
env["CONTENT_LENGTH"] ||= env[RACK_INPUT].size.to_s if env[RACK_INPUT].respond_to?(:size)
|
143
160
|
|
144
161
|
opts.each { |field, value|
|
145
162
|
env[field] = value if String === field
|
@@ -154,17 +171,24 @@ module Rack
|
|
154
171
|
# MockRequest.
|
155
172
|
|
156
173
|
class MockResponse < Rack::Response
|
174
|
+
class << self
|
175
|
+
alias [] new
|
176
|
+
end
|
177
|
+
|
157
178
|
# Headers
|
158
|
-
attr_reader :original_headers
|
179
|
+
attr_reader :original_headers, :cookies
|
159
180
|
|
160
181
|
# Errors
|
161
182
|
attr_accessor :errors
|
162
183
|
|
163
|
-
def initialize(status, headers, body, errors=StringIO.new(""))
|
184
|
+
def initialize(status, headers, body, errors = StringIO.new(""))
|
164
185
|
@original_headers = headers
|
165
186
|
@errors = errors.string if errors.respond_to?(:string)
|
187
|
+
@cookies = parse_cookies_from_header
|
166
188
|
|
167
189
|
super(body, status, headers)
|
190
|
+
|
191
|
+
buffered_body!
|
168
192
|
end
|
169
193
|
|
170
194
|
def =~(other)
|
@@ -186,11 +210,64 @@ module Rack
|
|
186
210
|
# ...
|
187
211
|
# res.body.should == "foo!"
|
188
212
|
# end
|
189
|
-
|
213
|
+
buffer = String.new
|
214
|
+
|
215
|
+
super.each do |chunk|
|
216
|
+
buffer << chunk
|
217
|
+
end
|
218
|
+
|
219
|
+
return buffer
|
190
220
|
end
|
191
221
|
|
192
222
|
def empty?
|
193
223
|
[201, 204, 304].include? status
|
194
224
|
end
|
225
|
+
|
226
|
+
def cookie(name)
|
227
|
+
cookies.fetch(name, nil)
|
228
|
+
end
|
229
|
+
|
230
|
+
private
|
231
|
+
|
232
|
+
def parse_cookies_from_header
|
233
|
+
cookies = Hash.new
|
234
|
+
if original_headers.has_key? 'Set-Cookie'
|
235
|
+
set_cookie_header = original_headers.fetch('Set-Cookie')
|
236
|
+
set_cookie_header.split("\n").each do |cookie|
|
237
|
+
cookie_name, cookie_filling = cookie.split('=', 2)
|
238
|
+
cookie_attributes = identify_cookie_attributes cookie_filling
|
239
|
+
parsed_cookie = CGI::Cookie.new(
|
240
|
+
'name' => cookie_name.strip,
|
241
|
+
'value' => cookie_attributes.fetch('value'),
|
242
|
+
'path' => cookie_attributes.fetch('path', nil),
|
243
|
+
'domain' => cookie_attributes.fetch('domain', nil),
|
244
|
+
'expires' => cookie_attributes.fetch('expires', nil),
|
245
|
+
'secure' => cookie_attributes.fetch('secure', false)
|
246
|
+
)
|
247
|
+
cookies.store(cookie_name, parsed_cookie)
|
248
|
+
end
|
249
|
+
end
|
250
|
+
cookies
|
251
|
+
end
|
252
|
+
|
253
|
+
def identify_cookie_attributes(cookie_filling)
|
254
|
+
cookie_bits = cookie_filling.split(';')
|
255
|
+
cookie_attributes = Hash.new
|
256
|
+
cookie_attributes.store('value', cookie_bits[0].strip)
|
257
|
+
cookie_bits.each do |bit|
|
258
|
+
if bit.include? '='
|
259
|
+
cookie_attribute, attribute_value = bit.split('=')
|
260
|
+
cookie_attributes.store(cookie_attribute.strip, attribute_value.strip)
|
261
|
+
if cookie_attribute.include? 'max-age'
|
262
|
+
cookie_attributes.store('expires', Time.now + attribute_value.strip.to_i)
|
263
|
+
end
|
264
|
+
end
|
265
|
+
if bit.include? 'secure'
|
266
|
+
cookie_attributes.store('secure', true)
|
267
|
+
end
|
268
|
+
end
|
269
|
+
cookie_attributes
|
270
|
+
end
|
271
|
+
|
195
272
|
end
|
196
273
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Rack
|
2
4
|
module Multipart
|
3
5
|
class Generator
|
@@ -15,9 +17,13 @@ module Rack
|
|
15
17
|
|
16
18
|
flattened_params.map do |name, file|
|
17
19
|
if file.respond_to?(:original_filename)
|
18
|
-
|
19
|
-
|
20
|
-
|
20
|
+
if file.path
|
21
|
+
::File.open(file.path, 'rb') do |f|
|
22
|
+
f.set_encoding(Encoding::BINARY)
|
23
|
+
content_for_tempfile(f, file, name)
|
24
|
+
end
|
25
|
+
else
|
26
|
+
content_for_tempfile(file, file, name)
|
21
27
|
end
|
22
28
|
else
|
23
29
|
content_for_other(file, name)
|
@@ -27,21 +33,18 @@ module Rack
|
|
27
33
|
|
28
34
|
private
|
29
35
|
def multipart?
|
30
|
-
multipart = false
|
31
|
-
|
32
36
|
query = lambda { |value|
|
33
37
|
case value
|
34
38
|
when Array
|
35
|
-
value.
|
39
|
+
value.any?(&query)
|
36
40
|
when Hash
|
37
|
-
value.values.
|
41
|
+
value.values.any?(&query)
|
38
42
|
when Rack::Multipart::UploadedFile
|
39
|
-
|
43
|
+
true
|
40
44
|
end
|
41
45
|
}
|
42
|
-
@params.values.each(&query)
|
43
46
|
|
44
|
-
|
47
|
+
@params.values.any?(&query)
|
45
48
|
end
|
46
49
|
|
47
50
|
def flattened_params
|
@@ -70,12 +73,13 @@ module Rack
|
|
70
73
|
end
|
71
74
|
|
72
75
|
def content_for_tempfile(io, file, name)
|
76
|
+
length = ::File.stat(file.path).size if file.path
|
77
|
+
filename = "; filename=\"#{Utils.escape(file.original_filename)}\"" if file.original_filename
|
73
78
|
<<-EOF
|
74
79
|
--#{MULTIPART_BOUNDARY}\r
|
75
|
-
Content-Disposition: form-data; name="#{name}"
|
80
|
+
Content-Disposition: form-data; name="#{name}"#{filename}\r
|
76
81
|
Content-Type: #{file.content_type}\r
|
77
|
-
Content-Length: #{
|
78
|
-
\r
|
82
|
+
#{"Content-Length: #{length}\r\n" if length}\r
|
79
83
|
#{io.read}\r
|
80
84
|
EOF
|
81
85
|
end
|