rack 2.0.1 → 2.2.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/CHANGELOG.md +795 -0
- data/CONTRIBUTING.md +136 -0
- data/{COPYING → MIT-LICENSE} +4 -2
- data/README.rdoc +188 -145
- data/Rakefile +37 -23
- data/{SPEC → SPEC.rdoc} +46 -17
- 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 +6 -4
- data/lib/rack/auth/digest/md5.rb +13 -11
- data/lib/rack/auth/digest/nonce.rb +5 -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 +37 -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 +60 -70
- data/lib/rack/directory.rb +84 -64
- data/lib/rack/etag.rb +8 -5
- 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 +19 -10
- data/lib/rack/handler.rb +7 -2
- data/lib/rack/head.rb +1 -1
- data/lib/rack/lint.rb +221 -186
- data/lib/rack/lobster.rb +10 -10
- data/lib/rack/lock.rb +14 -4
- data/lib/rack/logger.rb +2 -0
- data/lib/rack/media_type.rb +23 -8
- data/lib/rack/method_override.rb +13 -4
- data/lib/rack/mime.rb +9 -1
- data/lib/rack/mock.rb +135 -29
- data/lib/rack/multipart/generator.rb +17 -13
- data/lib/rack/multipart/parser.rb +85 -68
- data/lib/rack/multipart/uploaded_file.rb +15 -7
- data/lib/rack/multipart.rb +6 -5
- data/lib/rack/null_logger.rb +2 -0
- data/lib/rack/query_parser.rb +108 -36
- data/lib/rack/recursive.rb +7 -5
- data/lib/rack/reloader.rb +8 -4
- data/lib/rack/request.rb +232 -60
- 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 +14 -10
- data/lib/rack/server.rb +97 -25
- data/lib/rack/session/abstract/id.rb +113 -25
- data/lib/rack/session/cookie.rb +22 -14
- data/lib/rack/session/memcache.rb +4 -87
- data/lib/rack/session/pool.rb +24 -10
- data/lib/rack/show_exceptions.rb +22 -18
- data/lib/rack/show_status.rb +9 -9
- data/lib/rack/static.rb +25 -12
- data/lib/rack/tempfile_reaper.rb +1 -1
- data/lib/rack/urlmap.rb +13 -7
- data/lib/rack/utils.rb +135 -123
- data/lib/rack/version.rb +29 -0
- data/lib/rack.rb +67 -73
- data/rack.gemspec +40 -29
- metadata +25 -184
- 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_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 -365
- 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 -251
- 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 -194
- data/test/spec_logger.rb +0 -24
- data/test/spec_media_type.rb +0 -42
- data/test/spec_method_override.rb +0 -83
- data/test/spec_mime.rb +0 -51
- data/test/spec_mock.rb +0 -342
- data/test/spec_multipart.rb +0 -716
- data/test/spec_null_logger.rb +0 -21
- data/test/spec_recursive.rb +0 -75
- data/test/spec_request.rb +0 -1393
- 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 -28
- 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 -80
- 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 -208
- 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/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
@@ -1,5 +1,6 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'thread'
|
2
|
-
require 'rack/body_proxy'
|
3
4
|
|
4
5
|
module Rack
|
5
6
|
# Rack::Lock locks every request inside a mutex, so that every request
|
@@ -11,12 +12,21 @@ module Rack
|
|
11
12
|
|
12
13
|
def call(env)
|
13
14
|
@mutex.lock
|
15
|
+
@env = env
|
16
|
+
@old_rack_multithread = env[RACK_MULTITHREAD]
|
14
17
|
begin
|
15
|
-
response = @app.call(env.merge(RACK_MULTITHREAD => false))
|
16
|
-
returned = response << BodyProxy.new(response.pop) {
|
18
|
+
response = @app.call(env.merge!(RACK_MULTITHREAD => false))
|
19
|
+
returned = response << BodyProxy.new(response.pop) { unlock }
|
17
20
|
ensure
|
18
|
-
|
21
|
+
unlock unless returned
|
19
22
|
end
|
20
23
|
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def unlock
|
28
|
+
@mutex.unlock
|
29
|
+
@env[RACK_MULTITHREAD] = @old_rack_multithread
|
30
|
+
end
|
21
31
|
end
|
22
32
|
end
|
data/lib/rack/logger.rb
CHANGED
data/lib/rack/media_type.rb
CHANGED
@@ -1,8 +1,10 @@
|
|
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
|
|
4
6
|
class MediaType
|
5
|
-
SPLIT_PATTERN =
|
7
|
+
SPLIT_PATTERN = /[;,]/
|
6
8
|
|
7
9
|
class << self
|
8
10
|
# The media type (type/subtype) portion of the CONTENT_TYPE header
|
@@ -13,7 +15,11 @@ 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
|
18
|
+
if type = content_type.split(SPLIT_PATTERN, 2).first
|
19
|
+
type.rstrip!
|
20
|
+
type.downcase!
|
21
|
+
type
|
22
|
+
end
|
17
23
|
end
|
18
24
|
|
19
25
|
# The media type parameters provided in CONTENT_TYPE as a Hash, or
|
@@ -21,18 +27,27 @@ module Rack
|
|
21
27
|
# provided. e.g., when the CONTENT_TYPE is "text/plain;charset=utf-8",
|
22
28
|
# this method responds with the following Hash:
|
23
29
|
# { 'charset' => 'utf-8' }
|
30
|
+
#
|
31
|
+
# This will pass back parameters with empty strings in the hash if they
|
32
|
+
# lack a value (e.g., "text/plain;charset=" will return { 'charset' => '' },
|
33
|
+
# and "text/plain;charset" will return { 'charset' => '' }, similarly to
|
34
|
+
# the query params parser (barring the latter case, which returns nil instead)).
|
24
35
|
def params(content_type)
|
25
36
|
return {} if content_type.nil?
|
26
|
-
|
27
|
-
|
28
|
-
|
37
|
+
|
38
|
+
content_type.split(SPLIT_PATTERN)[1..-1].each_with_object({}) do |s, hsh|
|
39
|
+
s.strip!
|
40
|
+
k, v = s.split('=', 2)
|
41
|
+
k.downcase!
|
42
|
+
hsh[k] = strip_doublequotes(v)
|
43
|
+
end
|
29
44
|
end
|
30
45
|
|
31
46
|
private
|
32
47
|
|
33
|
-
|
34
|
-
|
35
|
-
|
48
|
+
def strip_doublequotes(str)
|
49
|
+
(str && str.start_with?('"') && str.end_with?('"')) ? str[1..-2] : str || ''
|
50
|
+
end
|
36
51
|
end
|
37
52
|
end
|
38
53
|
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)
|
@@ -26,7 +28,11 @@ module Rack
|
|
26
28
|
req = Request.new(env)
|
27
29
|
method = method_override_param(req) ||
|
28
30
|
env[HTTP_METHOD_OVERRIDE_HEADER]
|
29
|
-
|
31
|
+
begin
|
32
|
+
method.to_s.upcase
|
33
|
+
rescue ArgumentError
|
34
|
+
env[RACK_ERRORS].puts "Invalid string for method"
|
35
|
+
end
|
30
36
|
end
|
31
37
|
|
32
38
|
private
|
@@ -37,7 +43,10 @@ module Rack
|
|
37
43
|
|
38
44
|
def method_override_param(req)
|
39
45
|
req.POST[METHOD_OVERRIDE_PARAM_KEY]
|
40
|
-
rescue Utils::InvalidParameterError, Utils::ParameterTypeError
|
46
|
+
rescue Utils::InvalidParameterError, Utils::ParameterTypeError, QueryParser::ParamsTooDeepError
|
47
|
+
req.get_header(RACK_ERRORS).puts "Invalid or incomplete POST params"
|
48
|
+
rescue EOFError
|
49
|
+
req.get_header(RACK_ERRORS).puts "Bad request content body"
|
41
50
|
end
|
42
51
|
end
|
43
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
|
@@ -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,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'uri'
|
2
4
|
require 'stringio'
|
3
|
-
|
4
|
-
require 'rack/lint'
|
5
|
-
require 'rack/utils'
|
6
|
-
require 'rack/response'
|
5
|
+
require_relative '../rack'
|
7
6
|
|
8
7
|
module Rack
|
9
8
|
# Rack::MockRequest helps testing your Rack application without
|
@@ -53,16 +52,26 @@ module Rack
|
|
53
52
|
@app = app
|
54
53
|
end
|
55
54
|
|
56
|
-
|
57
|
-
def
|
58
|
-
|
59
|
-
def
|
60
|
-
|
61
|
-
def
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
55
|
+
# Make a GET request and return a MockResponse. See #request.
|
56
|
+
def get(uri, opts = {}) request(GET, uri, opts) end
|
57
|
+
# Make a POST request and return a MockResponse. See #request.
|
58
|
+
def post(uri, opts = {}) request(POST, uri, opts) end
|
59
|
+
# Make a PUT request and return a MockResponse. See #request.
|
60
|
+
def put(uri, opts = {}) request(PUT, uri, opts) end
|
61
|
+
# Make a PATCH request and return a MockResponse. See #request.
|
62
|
+
def patch(uri, opts = {}) request(PATCH, uri, opts) end
|
63
|
+
# Make a DELETE request and return a MockResponse. See #request.
|
64
|
+
def delete(uri, opts = {}) request(DELETE, uri, opts) end
|
65
|
+
# Make a HEAD request and return a MockResponse. See #request.
|
66
|
+
def head(uri, opts = {}) request(HEAD, uri, opts) end
|
67
|
+
# Make an OPTIONS request and return a MockResponse. See #request.
|
68
|
+
def options(uri, opts = {}) request(OPTIONS, uri, opts) end
|
69
|
+
|
70
|
+
# Make a request using the given request method for the given
|
71
|
+
# uri to the rack application and return a MockResponse.
|
72
|
+
# Options given are passed to MockRequest.env_for.
|
73
|
+
def request(method = GET, uri = "", opts = {})
|
74
|
+
env = self.class.env_for(uri, opts.merge(method: method))
|
66
75
|
|
67
76
|
if opts[:lint]
|
68
77
|
app = Rack::Lint.new(@app)
|
@@ -71,7 +80,7 @@ module Rack
|
|
71
80
|
end
|
72
81
|
|
73
82
|
errors = env[RACK_ERRORS]
|
74
|
-
status, headers, body
|
83
|
+
status, headers, body = app.call(env)
|
75
84
|
MockResponse.new(status, headers, body, errors)
|
76
85
|
ensure
|
77
86
|
body.close if body.respond_to?(:close)
|
@@ -85,19 +94,26 @@ module Rack
|
|
85
94
|
end
|
86
95
|
|
87
96
|
# Return the Rack environment used for a request to +uri+.
|
88
|
-
|
97
|
+
# All options that are strings are added to the returned environment.
|
98
|
+
# Options:
|
99
|
+
# :fatal :: Whether to raise an exception if request outputs to rack.errors
|
100
|
+
# :input :: The rack.input to set
|
101
|
+
# :method :: The HTTP request method to use
|
102
|
+
# :params :: The params to use
|
103
|
+
# :script_name :: The SCRIPT_NAME to set
|
104
|
+
def self.env_for(uri = "", opts = {})
|
89
105
|
uri = parse_uri_rfc2396(uri)
|
90
106
|
uri.path = "/#{uri.path}" unless uri.path[0] == ?/
|
91
107
|
|
92
108
|
env = DEFAULT_ENV.dup
|
93
109
|
|
94
|
-
env[REQUEST_METHOD] = opts[:method] ? opts[:method].to_s.upcase : GET
|
95
|
-
env[SERVER_NAME] = uri.host || "example.org"
|
96
|
-
env[SERVER_PORT] = uri.port ? uri.port.to_s : "80"
|
97
|
-
env[QUERY_STRING] = uri.query.to_s
|
98
|
-
env[PATH_INFO] = (!uri.path || uri.path.empty?) ? "/" : uri.path
|
99
|
-
env[RACK_URL_SCHEME] = uri.scheme || "http"
|
100
|
-
env[HTTPS] = env[RACK_URL_SCHEME] == "https" ? "on" : "off"
|
110
|
+
env[REQUEST_METHOD] = (opts[:method] ? opts[:method].to_s.upcase : GET).b
|
111
|
+
env[SERVER_NAME] = (uri.host || "example.org").b
|
112
|
+
env[SERVER_PORT] = (uri.port ? uri.port.to_s : "80").b
|
113
|
+
env[QUERY_STRING] = (uri.query.to_s).b
|
114
|
+
env[PATH_INFO] = ((!uri.path || uri.path.empty?) ? "/" : uri.path).b
|
115
|
+
env[RACK_URL_SCHEME] = (uri.scheme || "http").b
|
116
|
+
env[HTTPS] = (env[RACK_URL_SCHEME] == "https" ? "on" : "off").b
|
101
117
|
|
102
118
|
env[SCRIPT_NAME] = opts[:script_name] || ""
|
103
119
|
|
@@ -128,7 +144,7 @@ module Rack
|
|
128
144
|
end
|
129
145
|
end
|
130
146
|
|
131
|
-
empty_str = String.new
|
147
|
+
empty_str = String.new
|
132
148
|
opts[:input] ||= empty_str
|
133
149
|
if String === opts[:input]
|
134
150
|
rack_input = StringIO.new(opts[:input])
|
@@ -139,7 +155,7 @@ module Rack
|
|
139
155
|
rack_input.set_encoding(Encoding::BINARY)
|
140
156
|
env[RACK_INPUT] = rack_input
|
141
157
|
|
142
|
-
env["CONTENT_LENGTH"] ||= env[RACK_INPUT].
|
158
|
+
env["CONTENT_LENGTH"] ||= env[RACK_INPUT].size.to_s if env[RACK_INPUT].respond_to?(:size)
|
143
159
|
|
144
160
|
opts.each { |field, value|
|
145
161
|
env[field] = value if String === field
|
@@ -154,17 +170,54 @@ module Rack
|
|
154
170
|
# MockRequest.
|
155
171
|
|
156
172
|
class MockResponse < Rack::Response
|
173
|
+
begin
|
174
|
+
# Recent versions of the CGI gem may not provide `CGI::Cookie`.
|
175
|
+
require 'cgi/cookie'
|
176
|
+
Cookie = CGI::Cookie
|
177
|
+
rescue LoadError
|
178
|
+
class Cookie
|
179
|
+
attr_reader :name, :value, :path, :domain, :expires, :secure
|
180
|
+
|
181
|
+
def initialize(args)
|
182
|
+
@name = args["name"]
|
183
|
+
@value = args["value"]
|
184
|
+
@path = args["path"]
|
185
|
+
@domain = args["domain"]
|
186
|
+
@expires = args["expires"]
|
187
|
+
@secure = args["secure"]
|
188
|
+
end
|
189
|
+
|
190
|
+
def method_missing(method_name, *args, &block)
|
191
|
+
@value.send(method_name, *args, &block)
|
192
|
+
end
|
193
|
+
# :nocov:
|
194
|
+
ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
|
195
|
+
# :nocov:
|
196
|
+
|
197
|
+
def respond_to_missing?(method_name, include_all = false)
|
198
|
+
@value.respond_to?(method_name, include_all) || super
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
class << self
|
204
|
+
alias [] new
|
205
|
+
end
|
206
|
+
|
157
207
|
# Headers
|
158
|
-
attr_reader :original_headers
|
208
|
+
attr_reader :original_headers, :cookies
|
159
209
|
|
160
210
|
# Errors
|
161
211
|
attr_accessor :errors
|
162
212
|
|
163
|
-
def initialize(status, headers, body, errors=StringIO.new(""))
|
213
|
+
def initialize(status, headers, body, errors = StringIO.new(""))
|
164
214
|
@original_headers = headers
|
165
215
|
@errors = errors.string if errors.respond_to?(:string)
|
216
|
+
@cookies = parse_cookies_from_header
|
166
217
|
|
167
218
|
super(body, status, headers)
|
219
|
+
|
220
|
+
buffered_body!
|
168
221
|
end
|
169
222
|
|
170
223
|
def =~(other)
|
@@ -186,11 +239,64 @@ module Rack
|
|
186
239
|
# ...
|
187
240
|
# res.body.should == "foo!"
|
188
241
|
# end
|
189
|
-
|
242
|
+
buffer = String.new
|
243
|
+
|
244
|
+
super.each do |chunk|
|
245
|
+
buffer << chunk
|
246
|
+
end
|
247
|
+
|
248
|
+
return buffer
|
190
249
|
end
|
191
250
|
|
192
251
|
def empty?
|
193
|
-
[201, 204,
|
252
|
+
[201, 204, 304].include? status
|
253
|
+
end
|
254
|
+
|
255
|
+
def cookie(name)
|
256
|
+
cookies.fetch(name, nil)
|
257
|
+
end
|
258
|
+
|
259
|
+
private
|
260
|
+
|
261
|
+
def parse_cookies_from_header
|
262
|
+
cookies = Hash.new
|
263
|
+
if original_headers.has_key? 'Set-Cookie'
|
264
|
+
set_cookie_header = original_headers.fetch('Set-Cookie')
|
265
|
+
set_cookie_header.split("\n").each do |cookie|
|
266
|
+
cookie_name, cookie_filling = cookie.split('=', 2)
|
267
|
+
cookie_attributes = identify_cookie_attributes cookie_filling
|
268
|
+
parsed_cookie = Cookie.new(
|
269
|
+
'name' => cookie_name.strip,
|
270
|
+
'value' => cookie_attributes.fetch('value'),
|
271
|
+
'path' => cookie_attributes.fetch('path', nil),
|
272
|
+
'domain' => cookie_attributes.fetch('domain', nil),
|
273
|
+
'expires' => cookie_attributes.fetch('expires', nil),
|
274
|
+
'secure' => cookie_attributes.fetch('secure', false)
|
275
|
+
)
|
276
|
+
cookies.store(cookie_name, parsed_cookie)
|
277
|
+
end
|
278
|
+
end
|
279
|
+
cookies
|
194
280
|
end
|
281
|
+
|
282
|
+
def identify_cookie_attributes(cookie_filling)
|
283
|
+
cookie_bits = cookie_filling.split(';')
|
284
|
+
cookie_attributes = Hash.new
|
285
|
+
cookie_attributes.store('value', Array(cookie_bits[0].strip))
|
286
|
+
cookie_bits.each do |bit|
|
287
|
+
if bit.include? '='
|
288
|
+
cookie_attribute, attribute_value = bit.split('=')
|
289
|
+
cookie_attributes.store(cookie_attribute.strip, attribute_value.strip)
|
290
|
+
if cookie_attribute.include? 'max-age'
|
291
|
+
cookie_attributes.store('expires', Time.now + attribute_value.strip.to_i)
|
292
|
+
end
|
293
|
+
end
|
294
|
+
if bit.include? 'secure'
|
295
|
+
cookie_attributes.store('secure', true)
|
296
|
+
end
|
297
|
+
end
|
298
|
+
cookie_attributes
|
299
|
+
end
|
300
|
+
|
195
301
|
end
|
196
302
|
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
|