rack 2.2.8.1 → 3.1.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +301 -81
- data/CONTRIBUTING.md +63 -55
- data/MIT-LICENSE +1 -1
- data/README.md +328 -0
- data/SPEC.rdoc +204 -131
- data/lib/rack/auth/abstract/handler.rb +3 -1
- data/lib/rack/auth/abstract/request.rb +3 -1
- data/lib/rack/auth/basic.rb +1 -4
- data/lib/rack/bad_request.rb +8 -0
- data/lib/rack/body_proxy.rb +21 -3
- data/lib/rack/builder.rb +102 -69
- data/lib/rack/cascade.rb +2 -3
- data/lib/rack/common_logger.rb +23 -18
- data/lib/rack/conditional_get.rb +18 -15
- data/lib/rack/constants.rb +67 -0
- data/lib/rack/content_length.rb +12 -16
- data/lib/rack/content_type.rb +8 -5
- data/lib/rack/deflater.rb +40 -26
- data/lib/rack/directory.rb +9 -3
- data/lib/rack/etag.rb +14 -23
- data/lib/rack/events.rb +4 -0
- data/lib/rack/files.rb +15 -17
- data/lib/rack/head.rb +9 -8
- data/lib/rack/headers.rb +238 -0
- data/lib/rack/lint.rb +838 -644
- data/lib/rack/lock.rb +2 -5
- data/lib/rack/logger.rb +3 -0
- data/lib/rack/method_override.rb +5 -1
- data/lib/rack/mime.rb +14 -5
- data/lib/rack/mock.rb +1 -271
- data/lib/rack/mock_request.rb +171 -0
- data/lib/rack/mock_response.rb +124 -0
- data/lib/rack/multipart/generator.rb +7 -5
- data/lib/rack/multipart/parser.rb +215 -90
- data/lib/rack/multipart/uploaded_file.rb +4 -0
- data/lib/rack/multipart.rb +53 -40
- data/lib/rack/null_logger.rb +9 -0
- data/lib/rack/query_parser.rb +81 -102
- data/lib/rack/recursive.rb +2 -0
- data/lib/rack/reloader.rb +0 -2
- data/lib/rack/request.rb +250 -123
- data/lib/rack/response.rb +146 -66
- data/lib/rack/rewindable_input.rb +24 -5
- data/lib/rack/runtime.rb +7 -6
- data/lib/rack/sendfile.rb +30 -25
- data/lib/rack/show_exceptions.rb +21 -4
- data/lib/rack/show_status.rb +17 -7
- data/lib/rack/static.rb +8 -8
- data/lib/rack/tempfile_reaper.rb +15 -4
- data/lib/rack/urlmap.rb +3 -1
- data/lib/rack/utils.rb +232 -233
- data/lib/rack/version.rb +1 -9
- data/lib/rack.rb +13 -89
- metadata +15 -41
- data/README.rdoc +0 -320
- data/Rakefile +0 -130
- data/bin/rackup +0 -5
- data/contrib/rack.png +0 -0
- data/contrib/rack.svg +0 -150
- data/contrib/rack_logo.svg +0 -164
- data/contrib/rdoc.css +0 -412
- data/example/lobster.ru +0 -6
- data/example/protectedlobster.rb +0 -16
- data/example/protectedlobster.ru +0 -10
- data/lib/rack/auth/digest/md5.rb +0 -131
- data/lib/rack/auth/digest/nonce.rb +0 -54
- data/lib/rack/auth/digest/params.rb +0 -54
- data/lib/rack/auth/digest/request.rb +0 -43
- data/lib/rack/chunked.rb +0 -117
- data/lib/rack/core_ext/regexp.rb +0 -14
- data/lib/rack/file.rb +0 -7
- data/lib/rack/handler/cgi.rb +0 -59
- data/lib/rack/handler/fastcgi.rb +0 -100
- data/lib/rack/handler/lsws.rb +0 -61
- data/lib/rack/handler/scgi.rb +0 -71
- data/lib/rack/handler/thin.rb +0 -36
- data/lib/rack/handler/webrick.rb +0 -129
- data/lib/rack/handler.rb +0 -104
- data/lib/rack/lobster.rb +0 -70
- data/lib/rack/server.rb +0 -466
- data/lib/rack/session/abstract/id.rb +0 -523
- data/lib/rack/session/cookie.rb +0 -204
- data/lib/rack/session/memcache.rb +0 -10
- data/lib/rack/session/pool.rb +0 -85
- data/rack.gemspec +0 -46
data/lib/rack/lock.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
require_relative 'body_proxy'
|
4
4
|
|
5
5
|
module Rack
|
6
6
|
# Rack::Lock locks every request inside a mutex, so that every request
|
@@ -12,10 +12,8 @@ module Rack
|
|
12
12
|
|
13
13
|
def call(env)
|
14
14
|
@mutex.lock
|
15
|
-
@env = env
|
16
|
-
@old_rack_multithread = env[RACK_MULTITHREAD]
|
17
15
|
begin
|
18
|
-
response = @app.call(env
|
16
|
+
response = @app.call(env)
|
19
17
|
returned = response << BodyProxy.new(response.pop) { unlock }
|
20
18
|
ensure
|
21
19
|
unlock unless returned
|
@@ -26,7 +24,6 @@ module Rack
|
|
26
24
|
|
27
25
|
def unlock
|
28
26
|
@mutex.unlock
|
29
|
-
@env[RACK_MULTITHREAD] = @old_rack_multithread
|
30
27
|
end
|
31
28
|
end
|
32
29
|
end
|
data/lib/rack/logger.rb
CHANGED
data/lib/rack/method_override.rb
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'constants'
|
4
|
+
require_relative 'request'
|
5
|
+
require_relative 'utils'
|
6
|
+
|
3
7
|
module Rack
|
4
8
|
class MethodOverride
|
5
9
|
HTTP_METHODS = %w[GET HEAD PUT POST DELETE OPTIONS PATCH LINK UNLINK]
|
@@ -42,7 +46,7 @@ module Rack
|
|
42
46
|
end
|
43
47
|
|
44
48
|
def method_override_param(req)
|
45
|
-
req.POST[METHOD_OVERRIDE_PARAM_KEY]
|
49
|
+
req.POST[METHOD_OVERRIDE_PARAM_KEY] if req.form_data? || req.parseable_data?
|
46
50
|
rescue Utils::InvalidParameterError, Utils::ParameterTypeError, QueryParser::ParamsTooDeepError
|
47
51
|
req.get_header(RACK_ERRORS).puts "Invalid or incomplete POST params"
|
48
52
|
rescue EOFError
|
data/lib/rack/mime.rb
CHANGED
@@ -63,6 +63,7 @@ module Rack
|
|
63
63
|
".aif" => "audio/x-aiff",
|
64
64
|
".aiff" => "audio/x-aiff",
|
65
65
|
".ami" => "application/vnd.amiga.ami",
|
66
|
+
".apng" => "image/apng",
|
66
67
|
".appcache" => "text/cache-manifest",
|
67
68
|
".apr" => "application/vnd.lotus-approach",
|
68
69
|
".asc" => "application/pgp-signature",
|
@@ -77,6 +78,7 @@ module Rack
|
|
77
78
|
".atx" => "application/vnd.antix.game-component",
|
78
79
|
".au" => "audio/basic",
|
79
80
|
".avi" => "video/x-msvideo",
|
81
|
+
".avif" => "image/avif",
|
80
82
|
".bat" => "application/x-msdownload",
|
81
83
|
".bcpio" => "application/x-bcpio",
|
82
84
|
".bdm" => "application/vnd.syncml.dm+wbxml",
|
@@ -197,6 +199,7 @@ module Rack
|
|
197
199
|
".fe_launch" => "application/vnd.denovo.fcselayout-link",
|
198
200
|
".fg5" => "application/vnd.fujitsu.oasysgp",
|
199
201
|
".fli" => "video/x-fli",
|
202
|
+
".flif" => "image/flif",
|
200
203
|
".flo" => "application/vnd.micrografx.flo",
|
201
204
|
".flv" => "video/x-flv",
|
202
205
|
".flw" => "application/vnd.kde.kivio",
|
@@ -237,6 +240,10 @@ module Rack
|
|
237
240
|
".h264" => "video/h264",
|
238
241
|
".hbci" => "application/vnd.hbci",
|
239
242
|
".hdf" => "application/x-hdf",
|
243
|
+
".heic" => "image/heic",
|
244
|
+
".heics" => "image/heic-sequence",
|
245
|
+
".heif" => "image/heif",
|
246
|
+
".heifs" => "image/heif-sequence",
|
240
247
|
".hh" => "text/x-c",
|
241
248
|
".hlp" => "application/winhlp",
|
242
249
|
".hpgl" => "application/vnd.hp-hpgl",
|
@@ -283,7 +290,7 @@ module Rack
|
|
283
290
|
".jpg" => "image/jpeg",
|
284
291
|
".jpgv" => "video/jpeg",
|
285
292
|
".jpm" => "video/jpm",
|
286
|
-
".js" => "
|
293
|
+
".js" => "text/javascript",
|
287
294
|
".json" => "application/json",
|
288
295
|
".karbon" => "application/vnd.kde.karbon",
|
289
296
|
".kfo" => "application/vnd.kde.kformula",
|
@@ -331,6 +338,7 @@ module Rack
|
|
331
338
|
".mif" => "application/vnd.mif",
|
332
339
|
".mime" => "message/rfc822",
|
333
340
|
".mj2" => "video/mj2",
|
341
|
+
".mjs" => "text/javascript",
|
334
342
|
".mlp" => "application/vnd.dolby.mlp",
|
335
343
|
".mmd" => "application/vnd.chipnuts.karaoke-mmd",
|
336
344
|
".mmf" => "application/vnd.smaf",
|
@@ -402,7 +410,7 @@ module Rack
|
|
402
410
|
".ogx" => "application/ogg",
|
403
411
|
".org" => "application/vnd.lotus-organizer",
|
404
412
|
".otc" => "application/vnd.oasis.opendocument.chart-template",
|
405
|
-
".otf" => "
|
413
|
+
".otf" => "font/otf",
|
406
414
|
".otg" => "application/vnd.oasis.opendocument.graphics-template",
|
407
415
|
".oth" => "application/vnd.oasis.opendocument.text-web",
|
408
416
|
".oti" => "application/vnd.oasis.opendocument.image-template",
|
@@ -583,7 +591,7 @@ module Rack
|
|
583
591
|
".trm" => "application/x-msterminal",
|
584
592
|
".ts" => "video/mp2t",
|
585
593
|
".tsv" => "text/tab-separated-values",
|
586
|
-
".ttf" => "
|
594
|
+
".ttf" => "font/ttf",
|
587
595
|
".twd" => "application/vnd.simtech-mindmapper",
|
588
596
|
".txd" => "application/vnd.genomatix.tuxedo",
|
589
597
|
".txf" => "application/vnd.mobius.txf",
|
@@ -617,6 +625,7 @@ module Rack
|
|
617
625
|
".wbs" => "application/vnd.criticaltools.wbs+xml",
|
618
626
|
".wbxml" => "application/vnd.wap.wbxml",
|
619
627
|
".webm" => "video/webm",
|
628
|
+
".webp" => "image/webp",
|
620
629
|
".wm" => "video/x-ms-wm",
|
621
630
|
".wma" => "audio/x-ms-wma",
|
622
631
|
".wmd" => "application/x-ms-wmd",
|
@@ -628,8 +637,8 @@ module Rack
|
|
628
637
|
".wmv" => "video/x-ms-wmv",
|
629
638
|
".wmx" => "video/x-ms-wmx",
|
630
639
|
".wmz" => "application/x-ms-wmz",
|
631
|
-
".woff" => "
|
632
|
-
".woff2" => "
|
640
|
+
".woff" => "font/woff",
|
641
|
+
".woff2" => "font/woff2",
|
633
642
|
".wpd" => "application/vnd.wordperfect",
|
634
643
|
".wpl" => "application/vnd.ms-wpl",
|
635
644
|
".wps" => "application/vnd.ms-works",
|
data/lib/rack/mock.rb
CHANGED
@@ -1,273 +1,3 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
require 'stringio'
|
5
|
-
require_relative '../rack'
|
6
|
-
require 'cgi/cookie'
|
7
|
-
|
8
|
-
module Rack
|
9
|
-
# Rack::MockRequest helps testing your Rack application without
|
10
|
-
# actually using HTTP.
|
11
|
-
#
|
12
|
-
# After performing a request on a URL with get/post/put/patch/delete, it
|
13
|
-
# returns a MockResponse with useful helper methods for effective
|
14
|
-
# testing.
|
15
|
-
#
|
16
|
-
# You can pass a hash with additional configuration to the
|
17
|
-
# get/post/put/patch/delete.
|
18
|
-
# <tt>:input</tt>:: A String or IO-like to be used as rack.input.
|
19
|
-
# <tt>:fatal</tt>:: Raise a FatalWarning if the app writes to rack.errors.
|
20
|
-
# <tt>:lint</tt>:: If true, wrap the application in a Rack::Lint.
|
21
|
-
|
22
|
-
class MockRequest
|
23
|
-
class FatalWarning < RuntimeError
|
24
|
-
end
|
25
|
-
|
26
|
-
class FatalWarner
|
27
|
-
def puts(warning)
|
28
|
-
raise FatalWarning, warning
|
29
|
-
end
|
30
|
-
|
31
|
-
def write(warning)
|
32
|
-
raise FatalWarning, warning
|
33
|
-
end
|
34
|
-
|
35
|
-
def flush
|
36
|
-
end
|
37
|
-
|
38
|
-
def string
|
39
|
-
""
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
DEFAULT_ENV = {
|
44
|
-
RACK_VERSION => Rack::VERSION,
|
45
|
-
RACK_INPUT => StringIO.new,
|
46
|
-
RACK_ERRORS => StringIO.new,
|
47
|
-
RACK_MULTITHREAD => true,
|
48
|
-
RACK_MULTIPROCESS => true,
|
49
|
-
RACK_RUNONCE => false,
|
50
|
-
}.freeze
|
51
|
-
|
52
|
-
def initialize(app)
|
53
|
-
@app = app
|
54
|
-
end
|
55
|
-
|
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))
|
76
|
-
|
77
|
-
if opts[:lint]
|
78
|
-
app = Rack::Lint.new(@app)
|
79
|
-
else
|
80
|
-
app = @app
|
81
|
-
end
|
82
|
-
|
83
|
-
errors = env[RACK_ERRORS]
|
84
|
-
status, headers, body = app.call(env)
|
85
|
-
MockResponse.new(status, headers, body, errors)
|
86
|
-
ensure
|
87
|
-
body.close if body.respond_to?(:close)
|
88
|
-
end
|
89
|
-
|
90
|
-
# For historical reasons, we're pinning to RFC 2396.
|
91
|
-
# URI::Parser = URI::RFC2396_Parser
|
92
|
-
def self.parse_uri_rfc2396(uri)
|
93
|
-
@parser ||= URI::Parser.new
|
94
|
-
@parser.parse(uri)
|
95
|
-
end
|
96
|
-
|
97
|
-
# Return the Rack environment used for a request to +uri+.
|
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 = {})
|
106
|
-
uri = parse_uri_rfc2396(uri)
|
107
|
-
uri.path = "/#{uri.path}" unless uri.path[0] == ?/
|
108
|
-
|
109
|
-
env = DEFAULT_ENV.dup
|
110
|
-
|
111
|
-
env[REQUEST_METHOD] = (opts[:method] ? opts[:method].to_s.upcase : GET).b
|
112
|
-
env[SERVER_NAME] = (uri.host || "example.org").b
|
113
|
-
env[SERVER_PORT] = (uri.port ? uri.port.to_s : "80").b
|
114
|
-
env[QUERY_STRING] = (uri.query.to_s).b
|
115
|
-
env[PATH_INFO] = ((!uri.path || uri.path.empty?) ? "/" : uri.path).b
|
116
|
-
env[RACK_URL_SCHEME] = (uri.scheme || "http").b
|
117
|
-
env[HTTPS] = (env[RACK_URL_SCHEME] == "https" ? "on" : "off").b
|
118
|
-
|
119
|
-
env[SCRIPT_NAME] = opts[:script_name] || ""
|
120
|
-
|
121
|
-
if opts[:fatal]
|
122
|
-
env[RACK_ERRORS] = FatalWarner.new
|
123
|
-
else
|
124
|
-
env[RACK_ERRORS] = StringIO.new
|
125
|
-
end
|
126
|
-
|
127
|
-
if params = opts[:params]
|
128
|
-
if env[REQUEST_METHOD] == GET
|
129
|
-
params = Utils.parse_nested_query(params) if params.is_a?(String)
|
130
|
-
params.update(Utils.parse_nested_query(env[QUERY_STRING]))
|
131
|
-
env[QUERY_STRING] = Utils.build_nested_query(params)
|
132
|
-
elsif !opts.has_key?(:input)
|
133
|
-
opts["CONTENT_TYPE"] = "application/x-www-form-urlencoded"
|
134
|
-
if params.is_a?(Hash)
|
135
|
-
if data = Rack::Multipart.build_multipart(params)
|
136
|
-
opts[:input] = data
|
137
|
-
opts["CONTENT_LENGTH"] ||= data.length.to_s
|
138
|
-
opts["CONTENT_TYPE"] = "multipart/form-data; boundary=#{Rack::Multipart::MULTIPART_BOUNDARY}"
|
139
|
-
else
|
140
|
-
opts[:input] = Utils.build_nested_query(params)
|
141
|
-
end
|
142
|
-
else
|
143
|
-
opts[:input] = params
|
144
|
-
end
|
145
|
-
end
|
146
|
-
end
|
147
|
-
|
148
|
-
empty_str = String.new
|
149
|
-
opts[:input] ||= empty_str
|
150
|
-
if String === opts[:input]
|
151
|
-
rack_input = StringIO.new(opts[:input])
|
152
|
-
else
|
153
|
-
rack_input = opts[:input]
|
154
|
-
end
|
155
|
-
|
156
|
-
rack_input.set_encoding(Encoding::BINARY)
|
157
|
-
env[RACK_INPUT] = rack_input
|
158
|
-
|
159
|
-
env["CONTENT_LENGTH"] ||= env[RACK_INPUT].size.to_s if env[RACK_INPUT].respond_to?(:size)
|
160
|
-
|
161
|
-
opts.each { |field, value|
|
162
|
-
env[field] = value if String === field
|
163
|
-
}
|
164
|
-
|
165
|
-
env
|
166
|
-
end
|
167
|
-
end
|
168
|
-
|
169
|
-
# Rack::MockResponse provides useful helpers for testing your apps.
|
170
|
-
# Usually, you don't create the MockResponse on your own, but use
|
171
|
-
# MockRequest.
|
172
|
-
|
173
|
-
class MockResponse < Rack::Response
|
174
|
-
class << self
|
175
|
-
alias [] new
|
176
|
-
end
|
177
|
-
|
178
|
-
# Headers
|
179
|
-
attr_reader :original_headers, :cookies
|
180
|
-
|
181
|
-
# Errors
|
182
|
-
attr_accessor :errors
|
183
|
-
|
184
|
-
def initialize(status, headers, body, errors = StringIO.new(""))
|
185
|
-
@original_headers = headers
|
186
|
-
@errors = errors.string if errors.respond_to?(:string)
|
187
|
-
@cookies = parse_cookies_from_header
|
188
|
-
|
189
|
-
super(body, status, headers)
|
190
|
-
|
191
|
-
buffered_body!
|
192
|
-
end
|
193
|
-
|
194
|
-
def =~(other)
|
195
|
-
body =~ other
|
196
|
-
end
|
197
|
-
|
198
|
-
def match(other)
|
199
|
-
body.match other
|
200
|
-
end
|
201
|
-
|
202
|
-
def body
|
203
|
-
# FIXME: apparently users of MockResponse expect the return value of
|
204
|
-
# MockResponse#body to be a string. However, the real response object
|
205
|
-
# returns the body as a list.
|
206
|
-
#
|
207
|
-
# See spec_showstatus.rb:
|
208
|
-
#
|
209
|
-
# should "not replace existing messages" do
|
210
|
-
# ...
|
211
|
-
# res.body.should == "foo!"
|
212
|
-
# end
|
213
|
-
buffer = String.new
|
214
|
-
|
215
|
-
super.each do |chunk|
|
216
|
-
buffer << chunk
|
217
|
-
end
|
218
|
-
|
219
|
-
return buffer
|
220
|
-
end
|
221
|
-
|
222
|
-
def empty?
|
223
|
-
[201, 204, 304].include? status
|
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
|
-
|
272
|
-
end
|
273
|
-
end
|
3
|
+
require_relative 'mock_request'
|
@@ -0,0 +1,171 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'uri'
|
4
|
+
require 'stringio'
|
5
|
+
|
6
|
+
require_relative 'constants'
|
7
|
+
require_relative 'mock_response'
|
8
|
+
|
9
|
+
module Rack
|
10
|
+
# Rack::MockRequest helps testing your Rack application without
|
11
|
+
# actually using HTTP.
|
12
|
+
#
|
13
|
+
# After performing a request on a URL with get/post/put/patch/delete, it
|
14
|
+
# returns a MockResponse with useful helper methods for effective
|
15
|
+
# testing.
|
16
|
+
#
|
17
|
+
# You can pass a hash with additional configuration to the
|
18
|
+
# get/post/put/patch/delete.
|
19
|
+
# <tt>:input</tt>:: A String or IO-like to be used as rack.input.
|
20
|
+
# <tt>:fatal</tt>:: Raise a FatalWarning if the app writes to rack.errors.
|
21
|
+
# <tt>:lint</tt>:: If true, wrap the application in a Rack::Lint.
|
22
|
+
|
23
|
+
class MockRequest
|
24
|
+
class FatalWarning < RuntimeError
|
25
|
+
end
|
26
|
+
|
27
|
+
class FatalWarner
|
28
|
+
def puts(warning)
|
29
|
+
raise FatalWarning, warning
|
30
|
+
end
|
31
|
+
|
32
|
+
def write(warning)
|
33
|
+
raise FatalWarning, warning
|
34
|
+
end
|
35
|
+
|
36
|
+
def flush
|
37
|
+
end
|
38
|
+
|
39
|
+
def string
|
40
|
+
""
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def initialize(app)
|
45
|
+
@app = app
|
46
|
+
end
|
47
|
+
|
48
|
+
# Make a GET request and return a MockResponse. See #request.
|
49
|
+
def get(uri, opts = {}) request(GET, uri, opts) end
|
50
|
+
# Make a POST request and return a MockResponse. See #request.
|
51
|
+
def post(uri, opts = {}) request(POST, uri, opts) end
|
52
|
+
# Make a PUT request and return a MockResponse. See #request.
|
53
|
+
def put(uri, opts = {}) request(PUT, uri, opts) end
|
54
|
+
# Make a PATCH request and return a MockResponse. See #request.
|
55
|
+
def patch(uri, opts = {}) request(PATCH, uri, opts) end
|
56
|
+
# Make a DELETE request and return a MockResponse. See #request.
|
57
|
+
def delete(uri, opts = {}) request(DELETE, uri, opts) end
|
58
|
+
# Make a HEAD request and return a MockResponse. See #request.
|
59
|
+
def head(uri, opts = {}) request(HEAD, uri, opts) end
|
60
|
+
# Make an OPTIONS request and return a MockResponse. See #request.
|
61
|
+
def options(uri, opts = {}) request(OPTIONS, uri, opts) end
|
62
|
+
|
63
|
+
# Make a request using the given request method for the given
|
64
|
+
# uri to the rack application and return a MockResponse.
|
65
|
+
# Options given are passed to MockRequest.env_for.
|
66
|
+
def request(method = GET, uri = "", opts = {})
|
67
|
+
env = self.class.env_for(uri, opts.merge(method: method))
|
68
|
+
|
69
|
+
if opts[:lint]
|
70
|
+
app = Rack::Lint.new(@app)
|
71
|
+
else
|
72
|
+
app = @app
|
73
|
+
end
|
74
|
+
|
75
|
+
errors = env[RACK_ERRORS]
|
76
|
+
status, headers, body = app.call(env)
|
77
|
+
MockResponse.new(status, headers, body, errors)
|
78
|
+
ensure
|
79
|
+
body.close if body.respond_to?(:close)
|
80
|
+
end
|
81
|
+
|
82
|
+
# For historical reasons, we're pinning to RFC 2396.
|
83
|
+
# URI::Parser = URI::RFC2396_Parser
|
84
|
+
def self.parse_uri_rfc2396(uri)
|
85
|
+
@parser ||= URI::Parser.new
|
86
|
+
@parser.parse(uri)
|
87
|
+
end
|
88
|
+
|
89
|
+
# Return the Rack environment used for a request to +uri+.
|
90
|
+
# All options that are strings are added to the returned environment.
|
91
|
+
# Options:
|
92
|
+
# :fatal :: Whether to raise an exception if request outputs to rack.errors
|
93
|
+
# :input :: The rack.input to set
|
94
|
+
# :http_version :: The SERVER_PROTOCOL to set
|
95
|
+
# :method :: The HTTP request method to use
|
96
|
+
# :params :: The params to use
|
97
|
+
# :script_name :: The SCRIPT_NAME to set
|
98
|
+
def self.env_for(uri = "", opts = {})
|
99
|
+
uri = parse_uri_rfc2396(uri)
|
100
|
+
uri.path = "/#{uri.path}" unless uri.path[0] == ?/
|
101
|
+
|
102
|
+
env = {}
|
103
|
+
|
104
|
+
env[REQUEST_METHOD] = (opts[:method] ? opts[:method].to_s.upcase : GET).b
|
105
|
+
env[SERVER_NAME] = (uri.host || "example.org").b
|
106
|
+
env[SERVER_PORT] = (uri.port ? uri.port.to_s : "80").b
|
107
|
+
env[SERVER_PROTOCOL] = opts[:http_version] || 'HTTP/1.1'
|
108
|
+
env[QUERY_STRING] = (uri.query.to_s).b
|
109
|
+
env[PATH_INFO] = (uri.path).b
|
110
|
+
env[RACK_URL_SCHEME] = (uri.scheme || "http").b
|
111
|
+
env[HTTPS] = (env[RACK_URL_SCHEME] == "https" ? "on" : "off").b
|
112
|
+
|
113
|
+
env[SCRIPT_NAME] = opts[:script_name] || ""
|
114
|
+
|
115
|
+
if opts[:fatal]
|
116
|
+
env[RACK_ERRORS] = FatalWarner.new
|
117
|
+
else
|
118
|
+
env[RACK_ERRORS] = StringIO.new
|
119
|
+
end
|
120
|
+
|
121
|
+
if params = opts[:params]
|
122
|
+
if env[REQUEST_METHOD] == GET
|
123
|
+
params = Utils.parse_nested_query(params) if params.is_a?(String)
|
124
|
+
params.update(Utils.parse_nested_query(env[QUERY_STRING]))
|
125
|
+
env[QUERY_STRING] = Utils.build_nested_query(params)
|
126
|
+
elsif !opts.has_key?(:input)
|
127
|
+
opts["CONTENT_TYPE"] = "application/x-www-form-urlencoded"
|
128
|
+
if params.is_a?(Hash)
|
129
|
+
if data = Rack::Multipart.build_multipart(params)
|
130
|
+
opts[:input] = data
|
131
|
+
opts["CONTENT_LENGTH"] ||= data.length.to_s
|
132
|
+
opts["CONTENT_TYPE"] = "multipart/form-data; boundary=#{Rack::Multipart::MULTIPART_BOUNDARY}"
|
133
|
+
else
|
134
|
+
opts[:input] = Utils.build_nested_query(params)
|
135
|
+
end
|
136
|
+
else
|
137
|
+
opts[:input] = params
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
input = opts[:input]
|
143
|
+
if String === input
|
144
|
+
rack_input = StringIO.new(input)
|
145
|
+
rack_input.set_encoding(Encoding::BINARY)
|
146
|
+
else
|
147
|
+
if input.respond_to?(:encoding) && input.encoding != Encoding::BINARY
|
148
|
+
warn "input encoding not binary", uplevel: 1
|
149
|
+
if input.respond_to?(:set_encoding)
|
150
|
+
input.set_encoding(Encoding::BINARY)
|
151
|
+
else
|
152
|
+
raise ArgumentError, "could not coerce input to binary encoding"
|
153
|
+
end
|
154
|
+
end
|
155
|
+
rack_input = input
|
156
|
+
end
|
157
|
+
|
158
|
+
if rack_input
|
159
|
+
env[RACK_INPUT] = rack_input
|
160
|
+
|
161
|
+
env["CONTENT_LENGTH"] ||= env[RACK_INPUT].size.to_s if env[RACK_INPUT].respond_to?(:size)
|
162
|
+
end
|
163
|
+
|
164
|
+
opts.each { |field, value|
|
165
|
+
env[field] = value if String === field
|
166
|
+
}
|
167
|
+
|
168
|
+
env
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|