rack 2.0.9.3 → 2.1.4.3
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 +92 -0
- data/{COPYING → MIT-LICENSE} +4 -2
- data/README.rdoc +76 -116
- data/Rakefile +25 -18
- data/SPEC +9 -9
- 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 +2 -0
- 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 +4 -2
- data/lib/rack/auth/digest/request.rb +2 -0
- data/lib/rack/body_proxy.rb +3 -6
- data/lib/rack/builder.rb +39 -15
- data/lib/rack/cascade.rb +6 -5
- data/lib/rack/chunked.rb +29 -6
- data/lib/rack/common_logger.rb +9 -8
- data/lib/rack/conditional_get.rb +3 -1
- data/lib/rack/config.rb +2 -0
- data/lib/rack/content_length.rb +3 -1
- data/lib/rack/content_type.rb +3 -1
- data/lib/rack/core_ext/regexp.rb +14 -0
- data/lib/rack/deflater.rb +32 -17
- data/lib/rack/directory.rb +19 -16
- data/lib/rack/etag.rb +3 -1
- data/lib/rack/events.rb +5 -3
- data/lib/rack/file.rb +4 -173
- data/lib/rack/files.rb +178 -0
- data/lib/rack/handler/cgi.rb +3 -1
- data/lib/rack/handler/fastcgi.rb +4 -2
- data/lib/rack/handler/lsws.rb +3 -1
- data/lib/rack/handler/scgi.rb +9 -6
- data/lib/rack/handler/thin.rb +3 -1
- data/lib/rack/handler/webrick.rb +4 -2
- data/lib/rack/handler.rb +7 -2
- data/lib/rack/head.rb +2 -0
- data/lib/rack/lint.rb +14 -11
- data/lib/rack/lobster.rb +7 -5
- data/lib/rack/lock.rb +2 -0
- 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 +74 -15
- data/lib/rack/multipart/generator.rb +6 -7
- data/lib/rack/multipart/parser.rb +50 -45
- data/lib/rack/multipart/uploaded_file.rb +2 -0
- data/lib/rack/multipart.rb +3 -1
- data/lib/rack/null_logger.rb +2 -0
- data/lib/rack/query_parser.rb +51 -25
- data/lib/rack/recursive.rb +7 -5
- data/lib/rack/reloader.rb +10 -4
- data/lib/rack/request.rb +79 -26
- data/lib/rack/response.rb +71 -31
- data/lib/rack/rewindable_input.rb +4 -2
- data/lib/rack/runtime.rb +4 -2
- data/lib/rack/sendfile.rb +15 -8
- data/lib/rack/server.rb +88 -18
- data/lib/rack/session/abstract/id.rb +30 -20
- data/lib/rack/session/cookie.rb +10 -9
- data/lib/rack/session/memcache.rb +4 -93
- data/lib/rack/session/pool.rb +4 -2
- data/lib/rack/show_exceptions.rb +15 -9
- data/lib/rack/show_status.rb +4 -2
- data/lib/rack/static.rb +15 -10
- data/lib/rack/tempfile_reaper.rb +2 -0
- data/lib/rack/urlmap.rb +11 -2
- data/lib/rack/utils.rb +58 -71
- data/lib/rack.rb +63 -60
- data/rack.gemspec +17 -7
- metadata +28 -170
- data/HISTORY.md +0 -520
- 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 -107
- 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 -520
- 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 -721
- data/test/spec_null_logger.rb +0 -21
- data/test/spec_recursive.rb +0 -75
- data/test/spec_request.rb +0 -1423
- data/test/spec_response.rb +0 -528
- 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 -357
- data/test/spec_session_persisted_secure_secure_session_hash.rb +0 -73
- data/test/spec_session_pool.rb +0 -247
- 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,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'rack/utils'
|
2
4
|
require 'forwardable'
|
3
5
|
|
@@ -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
|
|
@@ -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:
|
@@ -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
|
}
|
@@ -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
|
@@ -662,7 +665,7 @@ module Rack
|
|
662
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
|
@@ -676,7 +679,7 @@ module Rack
|
|
676
679
|
## There must not be a <tt>Content-Length</tt> header when the
|
677
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
|
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"
|
@@ -65,6 +67,6 @@ if $0 == __FILE__
|
|
65
67
|
require 'rack'
|
66
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
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,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'uri'
|
2
4
|
require 'stringio'
|
3
5
|
require 'rack'
|
4
6
|
require 'rack/lint'
|
5
7
|
require 'rack/utils'
|
6
8
|
require 'rack/response'
|
9
|
+
require 'cgi/cookie'
|
7
10
|
|
8
11
|
module Rack
|
9
12
|
# Rack::MockRequest helps testing your Rack application without
|
@@ -53,16 +56,16 @@ module Rack
|
|
53
56
|
@app = app
|
54
57
|
end
|
55
58
|
|
56
|
-
def get(uri, opts={}) request(GET, uri, opts) end
|
57
|
-
def post(uri, opts={}) request(POST, uri, opts) end
|
58
|
-
def put(uri, opts={}) request(PUT, uri, opts) end
|
59
|
-
def patch(uri, opts={}) request(PATCH, uri, opts) end
|
60
|
-
def delete(uri, opts={}) request(DELETE, uri, opts) end
|
61
|
-
def head(uri, opts={}) request(HEAD, uri, opts) end
|
62
|
-
def options(uri, opts={}) request(OPTIONS, uri, opts) end
|
59
|
+
def get(uri, opts = {}) request(GET, uri, opts) end
|
60
|
+
def post(uri, opts = {}) request(POST, uri, opts) end
|
61
|
+
def put(uri, opts = {}) request(PUT, uri, opts) end
|
62
|
+
def patch(uri, opts = {}) request(PATCH, uri, opts) end
|
63
|
+
def delete(uri, opts = {}) request(DELETE, uri, opts) end
|
64
|
+
def head(uri, opts = {}) request(HEAD, uri, opts) end
|
65
|
+
def options(uri, opts = {}) request(OPTIONS, uri, opts) end
|
63
66
|
|
64
|
-
def request(method=GET, uri="", opts={})
|
65
|
-
env = self.class.env_for(uri, opts.merge(:
|
67
|
+
def request(method = GET, uri = "", opts = {})
|
68
|
+
env = self.class.env_for(uri, opts.merge(method: method))
|
66
69
|
|
67
70
|
if opts[:lint]
|
68
71
|
app = Rack::Lint.new(@app)
|
@@ -71,7 +74,7 @@ module Rack
|
|
71
74
|
end
|
72
75
|
|
73
76
|
errors = env[RACK_ERRORS]
|
74
|
-
status, headers, body
|
77
|
+
status, headers, body = app.call(env)
|
75
78
|
MockResponse.new(status, headers, body, errors)
|
76
79
|
ensure
|
77
80
|
body.close if body.respond_to?(:close)
|
@@ -85,7 +88,7 @@ module Rack
|
|
85
88
|
end
|
86
89
|
|
87
90
|
# Return the Rack environment used for a request to +uri+.
|
88
|
-
def self.env_for(uri="", opts={})
|
91
|
+
def self.env_for(uri = "", opts = {})
|
89
92
|
uri = parse_uri_rfc2396(uri)
|
90
93
|
uri.path = "/#{uri.path}" unless uri.path[0] == ?/
|
91
94
|
|
@@ -139,7 +142,7 @@ module Rack
|
|
139
142
|
rack_input.set_encoding(Encoding::BINARY)
|
140
143
|
env[RACK_INPUT] = rack_input
|
141
144
|
|
142
|
-
env["CONTENT_LENGTH"] ||= env[RACK_INPUT].
|
145
|
+
env["CONTENT_LENGTH"] ||= env[RACK_INPUT].size.to_s if env[RACK_INPUT].respond_to?(:size)
|
143
146
|
|
144
147
|
opts.each { |field, value|
|
145
148
|
env[field] = value if String === field
|
@@ -155,16 +158,19 @@ module Rack
|
|
155
158
|
|
156
159
|
class MockResponse < Rack::Response
|
157
160
|
# Headers
|
158
|
-
attr_reader :original_headers
|
161
|
+
attr_reader :original_headers, :cookies
|
159
162
|
|
160
163
|
# Errors
|
161
164
|
attr_accessor :errors
|
162
165
|
|
163
|
-
def initialize(status, headers, body, errors=StringIO.new(""))
|
166
|
+
def initialize(status, headers, body, errors = StringIO.new(""))
|
164
167
|
@original_headers = headers
|
165
168
|
@errors = errors.string if errors.respond_to?(:string)
|
169
|
+
@cookies = parse_cookies_from_header
|
166
170
|
|
167
171
|
super(body, status, headers)
|
172
|
+
|
173
|
+
buffered_body!
|
168
174
|
end
|
169
175
|
|
170
176
|
def =~(other)
|
@@ -186,11 +192,64 @@ module Rack
|
|
186
192
|
# ...
|
187
193
|
# res.body.should == "foo!"
|
188
194
|
# end
|
189
|
-
|
195
|
+
buffer = String.new
|
196
|
+
|
197
|
+
super.each do |chunk|
|
198
|
+
buffer << chunk
|
199
|
+
end
|
200
|
+
|
201
|
+
return buffer
|
190
202
|
end
|
191
203
|
|
192
204
|
def empty?
|
193
205
|
[201, 204, 304].include? status
|
194
206
|
end
|
207
|
+
|
208
|
+
def cookie(name)
|
209
|
+
cookies.fetch(name, nil)
|
210
|
+
end
|
211
|
+
|
212
|
+
private
|
213
|
+
|
214
|
+
def parse_cookies_from_header
|
215
|
+
cookies = Hash.new
|
216
|
+
if original_headers.has_key? 'Set-Cookie'
|
217
|
+
set_cookie_header = original_headers.fetch('Set-Cookie')
|
218
|
+
set_cookie_header.split("\n").each do |cookie|
|
219
|
+
cookie_name, cookie_filling = cookie.split('=', 2)
|
220
|
+
cookie_attributes = identify_cookie_attributes cookie_filling
|
221
|
+
parsed_cookie = CGI::Cookie.new(
|
222
|
+
'name' => cookie_name.strip,
|
223
|
+
'value' => cookie_attributes.fetch('value'),
|
224
|
+
'path' => cookie_attributes.fetch('path', nil),
|
225
|
+
'domain' => cookie_attributes.fetch('domain', nil),
|
226
|
+
'expires' => cookie_attributes.fetch('expires', nil),
|
227
|
+
'secure' => cookie_attributes.fetch('secure', false)
|
228
|
+
)
|
229
|
+
cookies.store(cookie_name, parsed_cookie)
|
230
|
+
end
|
231
|
+
end
|
232
|
+
cookies
|
233
|
+
end
|
234
|
+
|
235
|
+
def identify_cookie_attributes(cookie_filling)
|
236
|
+
cookie_bits = cookie_filling.split(';')
|
237
|
+
cookie_attributes = Hash.new
|
238
|
+
cookie_attributes.store('value', cookie_bits[0].strip)
|
239
|
+
cookie_bits.each do |bit|
|
240
|
+
if bit.include? '='
|
241
|
+
cookie_attribute, attribute_value = bit.split('=')
|
242
|
+
cookie_attributes.store(cookie_attribute.strip, attribute_value.strip)
|
243
|
+
if cookie_attribute.include? 'max-age'
|
244
|
+
cookie_attributes.store('expires', Time.now + attribute_value.strip.to_i)
|
245
|
+
end
|
246
|
+
end
|
247
|
+
if bit.include? 'secure'
|
248
|
+
cookie_attributes.store('secure', true)
|
249
|
+
end
|
250
|
+
end
|
251
|
+
cookie_attributes
|
252
|
+
end
|
253
|
+
|
195
254
|
end
|
196
255
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Rack
|
2
4
|
module Multipart
|
3
5
|
class Generator
|
@@ -27,21 +29,18 @@ module Rack
|
|
27
29
|
|
28
30
|
private
|
29
31
|
def multipart?
|
30
|
-
multipart = false
|
31
|
-
|
32
32
|
query = lambda { |value|
|
33
33
|
case value
|
34
34
|
when Array
|
35
|
-
value.
|
35
|
+
value.any?(&query)
|
36
36
|
when Hash
|
37
|
-
value.values.
|
37
|
+
value.values.any?(&query)
|
38
38
|
when Rack::Multipart::UploadedFile
|
39
|
-
|
39
|
+
true
|
40
40
|
end
|
41
41
|
}
|
42
|
-
@params.values.each(&query)
|
43
42
|
|
44
|
-
|
43
|
+
@params.values.any?(&query)
|
45
44
|
end
|
46
45
|
|
47
46
|
def flattened_params
|
@@ -1,4 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'rack/utils'
|
4
|
+
require 'strscan'
|
5
|
+
require 'rack/core_ext/regexp'
|
2
6
|
|
3
7
|
module Rack
|
4
8
|
module Multipart
|
@@ -6,12 +10,16 @@ module Rack
|
|
6
10
|
class MultipartTotalPartLimitError < StandardError; end
|
7
11
|
|
8
12
|
class Parser
|
9
|
-
|
13
|
+
using ::Rack::RegexpExtensions
|
14
|
+
|
15
|
+
BUFSIZE = 1_048_576
|
10
16
|
TEXT_PLAIN = "text/plain"
|
11
17
|
TEMPFILE_FACTORY = lambda { |filename, content_type|
|
12
|
-
Tempfile.new(["RackMultipart", ::File.extname(filename.gsub("\0"
|
18
|
+
Tempfile.new(["RackMultipart", ::File.extname(filename.gsub("\0", '%00'))])
|
13
19
|
}
|
14
20
|
|
21
|
+
BOUNDARY_REGEX = /\A([^\n]*(?:\n|\Z))/
|
22
|
+
|
15
23
|
class BoundedIO # :nodoc:
|
16
24
|
def initialize(io, content_length)
|
17
25
|
@io = io
|
@@ -19,15 +27,15 @@ module Rack
|
|
19
27
|
@cursor = 0
|
20
28
|
end
|
21
29
|
|
22
|
-
def read(size)
|
30
|
+
def read(size, outbuf = nil)
|
23
31
|
return if @cursor >= @content_length
|
24
32
|
|
25
33
|
left = @content_length - @cursor
|
26
34
|
|
27
35
|
str = if left < size
|
28
|
-
@io.read left
|
36
|
+
@io.read left, outbuf
|
29
37
|
else
|
30
|
-
@io.read size
|
38
|
+
@io.read size, outbuf
|
31
39
|
end
|
32
40
|
|
33
41
|
if str
|
@@ -62,13 +70,14 @@ module Rack
|
|
62
70
|
return EMPTY unless boundary
|
63
71
|
|
64
72
|
io = BoundedIO.new(io, content_length) if content_length
|
73
|
+
outbuf = String.new
|
65
74
|
|
66
75
|
parser = new(boundary, tmpfile, bufsize, qp)
|
67
|
-
parser.on_read io.read(bufsize)
|
76
|
+
parser.on_read io.read(bufsize, outbuf)
|
68
77
|
|
69
78
|
loop do
|
70
79
|
break if parser.state == :DONE
|
71
|
-
parser.on_read io.read(bufsize)
|
80
|
+
parser.on_read io.read(bufsize, outbuf)
|
72
81
|
end
|
73
82
|
|
74
83
|
io.rewind
|
@@ -91,14 +100,14 @@ module Rack
|
|
91
100
|
# those which give the lone filename.
|
92
101
|
fn = filename.split(/[\/\\]/).last
|
93
102
|
|
94
|
-
data = {:
|
95
|
-
:
|
103
|
+
data = { filename: fn, type: content_type,
|
104
|
+
name: name, tempfile: body, head: head }
|
96
105
|
elsif !filename && content_type && body.is_a?(IO)
|
97
106
|
body.rewind
|
98
107
|
|
99
108
|
# Generic multipart cases, not coming from a form
|
100
|
-
data = {:
|
101
|
-
:
|
109
|
+
data = { type: content_type,
|
110
|
+
name: name, tempfile: body, head: head }
|
102
111
|
end
|
103
112
|
|
104
113
|
yield data
|
@@ -175,25 +184,26 @@ module Rack
|
|
175
184
|
attr_reader :state
|
176
185
|
|
177
186
|
def initialize(boundary, tempfile, bufsize, query_parser)
|
178
|
-
@buf = String.new
|
179
|
-
|
180
187
|
@query_parser = query_parser
|
181
188
|
@params = query_parser.make_params
|
182
189
|
@boundary = "--#{boundary}"
|
183
190
|
@bufsize = bufsize
|
184
191
|
|
185
|
-
@rx = /(?:#{EOL})?#{Regexp.quote(@boundary)}(#{EOL}|--)/n
|
186
|
-
@rx_max_size = EOL.size + @boundary.bytesize + [EOL.size, '--'.size].max
|
187
192
|
@full_boundary = @boundary
|
188
193
|
@end_boundary = @boundary + '--'
|
189
194
|
@state = :FAST_FORWARD
|
190
195
|
@mime_index = 0
|
191
196
|
@collector = Collector.new tempfile
|
197
|
+
|
198
|
+
@sbuf = StringScanner.new("".dup)
|
199
|
+
@body_regex = /(?:#{EOL})?#{Regexp.quote(@boundary)}(?:#{EOL}|--)/m
|
200
|
+
@rx_max_size = EOL.size + @boundary.bytesize + [EOL.size, '--'.size].max
|
201
|
+
@head_regex = /(.*?#{EOL})#{EOL}/m
|
192
202
|
end
|
193
203
|
|
194
204
|
def on_read content
|
195
205
|
handle_empty_content!(content)
|
196
|
-
@
|
206
|
+
@sbuf.concat content
|
197
207
|
run_parser
|
198
208
|
end
|
199
209
|
|
@@ -204,7 +214,6 @@ module Rack
|
|
204
214
|
@query_parser.normalize_params(@params, part.name, data, @query_parser.param_depth_limit)
|
205
215
|
end
|
206
216
|
end
|
207
|
-
|
208
217
|
MultipartInfo.new @params.to_params_hash, @collector.find_all(&:file?).map(&:body)
|
209
218
|
end
|
210
219
|
|
@@ -231,7 +240,7 @@ module Rack
|
|
231
240
|
if consume_boundary
|
232
241
|
@state = :MIME_HEAD
|
233
242
|
else
|
234
|
-
raise EOFError, "bad content body" if @
|
243
|
+
raise EOFError, "bad content body" if @sbuf.rest_size >= @bufsize
|
235
244
|
:want_read
|
236
245
|
end
|
237
246
|
end
|
@@ -239,19 +248,16 @@ module Rack
|
|
239
248
|
def handle_consume_token
|
240
249
|
tok = consume_boundary
|
241
250
|
# break if we're at the end of a buffer, but not if it is the end of a field
|
242
|
-
if tok == :END_BOUNDARY || (@
|
243
|
-
|
251
|
+
@state = if tok == :END_BOUNDARY || (@sbuf.eos? && tok != :BOUNDARY)
|
252
|
+
:DONE
|
244
253
|
else
|
245
|
-
|
254
|
+
:MIME_HEAD
|
246
255
|
end
|
247
256
|
end
|
248
257
|
|
249
258
|
def handle_mime_head
|
250
|
-
if @
|
251
|
-
|
252
|
-
head = @buf.slice!(0, i+2) # First \r\n
|
253
|
-
@buf.slice!(0, 2) # Second \r\n
|
254
|
-
|
259
|
+
if @sbuf.scan_until(@head_regex)
|
260
|
+
head = @sbuf[1]
|
255
261
|
content_type = head[MULTIPART_CONTENT_TYPE, 1]
|
256
262
|
if name = head[MULTIPART_CONTENT_DISPOSITION, 1]
|
257
263
|
name = Rack::Auth::Digest::Params::dequote(name)
|
@@ -262,7 +268,7 @@ module Rack
|
|
262
268
|
filename = get_filename(head)
|
263
269
|
|
264
270
|
if name.nil? || name.empty?
|
265
|
-
name = filename || "#{content_type || TEXT_PLAIN}[]"
|
271
|
+
name = filename || "#{content_type || TEXT_PLAIN}[]".dup
|
266
272
|
end
|
267
273
|
|
268
274
|
@collector.on_mime_head @mime_index, head, filename, content_type, name
|
@@ -273,16 +279,19 @@ module Rack
|
|
273
279
|
end
|
274
280
|
|
275
281
|
def handle_mime_body
|
276
|
-
if
|
277
|
-
#
|
278
|
-
@collector.on_mime_body @mime_index,
|
279
|
-
@
|
282
|
+
if (body_with_boundary = @sbuf.check_until(@body_regex)) # check but do not advance the pointer yet
|
283
|
+
body = body_with_boundary.sub(/#{@body_regex}\z/m, '') # remove the boundary from the string
|
284
|
+
@collector.on_mime_body @mime_index, body
|
285
|
+
@sbuf.pos += body.length + 2 # skip \r\n after the content
|
280
286
|
@state = :CONSUME_TOKEN
|
281
287
|
@mime_index += 1
|
282
288
|
else
|
283
|
-
# Save
|
284
|
-
if @rx_max_size < @
|
285
|
-
|
289
|
+
# Save what we have so far
|
290
|
+
if @rx_max_size < @sbuf.rest_size
|
291
|
+
delta = @sbuf.rest_size - @rx_max_size
|
292
|
+
@collector.on_mime_body @mime_index, @sbuf.peek(delta)
|
293
|
+
@sbuf.pos += delta
|
294
|
+
@sbuf.string = @sbuf.rest
|
286
295
|
end
|
287
296
|
:want_read
|
288
297
|
end
|
@@ -290,16 +299,13 @@ module Rack
|
|
290
299
|
|
291
300
|
def full_boundary; @full_boundary; end
|
292
301
|
|
293
|
-
def rx; @rx; end
|
294
|
-
|
295
302
|
def consume_boundary
|
296
|
-
while @
|
297
|
-
read_buffer = $1
|
303
|
+
while read_buffer = @sbuf.scan_until(BOUNDARY_REGEX)
|
298
304
|
case read_buffer.strip
|
299
305
|
when full_boundary then return :BOUNDARY
|
300
306
|
when @end_boundary then return :END_BOUNDARY
|
301
307
|
end
|
302
|
-
return if @
|
308
|
+
return if @sbuf.eos?
|
303
309
|
end
|
304
310
|
end
|
305
311
|
|
@@ -321,8 +327,8 @@ module Rack
|
|
321
327
|
|
322
328
|
return unless filename
|
323
329
|
|
324
|
-
if filename.scan(/%.?.?/).all? { |s|
|
325
|
-
filename = Utils.
|
330
|
+
if filename.scan(/%.?.?/).all? { |s| /%[0-9a-fA-F]{2}/.match?(s) }
|
331
|
+
filename = Utils.unescape_path(filename)
|
326
332
|
end
|
327
333
|
|
328
334
|
filename.scrub!
|
@@ -338,7 +344,7 @@ module Rack
|
|
338
344
|
filename
|
339
345
|
end
|
340
346
|
|
341
|
-
CHARSET
|
347
|
+
CHARSET = "charset"
|
342
348
|
|
343
349
|
def tag_multipart_encoding(filename, content_type, name, body)
|
344
350
|
name = name.to_s
|
@@ -355,10 +361,10 @@ module Rack
|
|
355
361
|
if TEXT_PLAIN == type_subtype
|
356
362
|
rest = list.drop 1
|
357
363
|
rest.each do |param|
|
358
|
-
k,v = param.split('=', 2)
|
364
|
+
k, v = param.split('=', 2)
|
359
365
|
k.strip!
|
360
366
|
v.strip!
|
361
|
-
v = v[1..-2] if v
|
367
|
+
v = v[1..-2] if v.start_with?('"') && v.end_with?('"')
|
362
368
|
encoding = Encoding.find v if k == CHARSET
|
363
369
|
end
|
364
370
|
end
|
@@ -368,7 +374,6 @@ module Rack
|
|
368
374
|
body.force_encoding(encoding)
|
369
375
|
end
|
370
376
|
|
371
|
-
|
372
377
|
def handle_empty_content!(content)
|
373
378
|
if content.nil? || content.empty?
|
374
379
|
raise EOFError
|