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/utils.rb
CHANGED
@@ -1,22 +1,30 @@
|
|
1
1
|
# -*- encoding: binary -*-
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
2
4
|
require 'uri'
|
3
5
|
require 'fileutils'
|
4
6
|
require 'set'
|
5
7
|
require 'tempfile'
|
6
|
-
require 'rack/query_parser'
|
7
8
|
require 'time'
|
8
9
|
|
10
|
+
require_relative 'query_parser'
|
11
|
+
|
9
12
|
module Rack
|
10
13
|
# Rack::Utils contains a grab-bag of useful methods for writing web
|
11
14
|
# applications adopted from all kinds of Ruby libraries.
|
12
15
|
|
13
16
|
module Utils
|
17
|
+
(require_relative 'core_ext/regexp'; using ::Rack::RegexpExtensions) if RUBY_VERSION < '2.4'
|
18
|
+
|
14
19
|
ParameterTypeError = QueryParser::ParameterTypeError
|
15
20
|
InvalidParameterError = QueryParser::InvalidParameterError
|
16
21
|
DEFAULT_SEP = QueryParser::DEFAULT_SEP
|
17
22
|
COMMON_SEP = QueryParser::COMMON_SEP
|
18
23
|
KeySpaceConstrainedParams = QueryParser::Params
|
19
24
|
|
25
|
+
RFC2822_DAY_NAME = [ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ]
|
26
|
+
RFC2822_MONTH_NAME = [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ]
|
27
|
+
|
20
28
|
class << self
|
21
29
|
attr_accessor :default_query_parser
|
22
30
|
end
|
@@ -24,33 +32,30 @@ module Rack
|
|
24
32
|
# This helps prevent a rogue client from flooding a Request.
|
25
33
|
self.default_query_parser = QueryParser.make_default(65536, 100)
|
26
34
|
|
35
|
+
module_function
|
36
|
+
|
27
37
|
# URI escapes. (CGI style space to +)
|
28
38
|
def escape(s)
|
29
39
|
URI.encode_www_form_component(s)
|
30
40
|
end
|
31
|
-
module_function :escape
|
32
41
|
|
33
42
|
# Like URI escaping, but with %20 instead of +. Strictly speaking this is
|
34
43
|
# true URI escaping.
|
35
44
|
def escape_path(s)
|
36
45
|
::URI::DEFAULT_PARSER.escape s
|
37
46
|
end
|
38
|
-
module_function :escape_path
|
39
47
|
|
40
48
|
# Unescapes the **path** component of a URI. See Rack::Utils.unescape for
|
41
49
|
# unescaping query parameters or form components.
|
42
50
|
def unescape_path(s)
|
43
51
|
::URI::DEFAULT_PARSER.unescape s
|
44
52
|
end
|
45
|
-
module_function :unescape_path
|
46
|
-
|
47
53
|
|
48
54
|
# Unescapes a URI escaped string with +encoding+. +encoding+ will be the
|
49
55
|
# target encoding of the string returned, and it defaults to UTF-8
|
50
56
|
def unescape(s, encoding = Encoding::UTF_8)
|
51
57
|
URI.decode_www_form_component(s, encoding)
|
52
58
|
end
|
53
|
-
module_function :unescape
|
54
59
|
|
55
60
|
class << self
|
56
61
|
attr_accessor :multipart_part_limit
|
@@ -82,21 +87,20 @@ module Rack
|
|
82
87
|
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
83
88
|
end
|
84
89
|
else
|
90
|
+
# :nocov:
|
85
91
|
def clock_time
|
86
92
|
Time.now.to_f
|
87
93
|
end
|
94
|
+
# :nocov:
|
88
95
|
end
|
89
|
-
module_function :clock_time
|
90
96
|
|
91
97
|
def parse_query(qs, d = nil, &unescaper)
|
92
98
|
Rack::Utils.default_query_parser.parse_query(qs, d, &unescaper)
|
93
99
|
end
|
94
|
-
module_function :parse_query
|
95
100
|
|
96
101
|
def parse_nested_query(qs, d = nil)
|
97
102
|
Rack::Utils.default_query_parser.parse_nested_query(qs, d)
|
98
103
|
end
|
99
|
-
module_function :parse_nested_query
|
100
104
|
|
101
105
|
def build_query(params)
|
102
106
|
params.map { |k, v|
|
@@ -107,7 +111,6 @@ module Rack
|
|
107
111
|
end
|
108
112
|
}.join("&")
|
109
113
|
end
|
110
|
-
module_function :build_query
|
111
114
|
|
112
115
|
def build_nested_query(value, prefix = nil)
|
113
116
|
case value
|
@@ -118,7 +121,7 @@ module Rack
|
|
118
121
|
when Hash
|
119
122
|
value.map { |k, v|
|
120
123
|
build_nested_query(v, prefix ? "#{prefix}[#{escape(k)}]" : escape(k))
|
121
|
-
}.
|
124
|
+
}.delete_if(&:empty?).join('&')
|
122
125
|
when nil
|
123
126
|
prefix
|
124
127
|
else
|
@@ -126,20 +129,22 @@ module Rack
|
|
126
129
|
"#{prefix}=#{escape(value)}"
|
127
130
|
end
|
128
131
|
end
|
129
|
-
module_function :build_nested_query
|
130
132
|
|
131
133
|
def q_values(q_value_header)
|
132
134
|
q_value_header.to_s.split(/\s*,\s*/).map do |part|
|
133
135
|
value, parameters = part.split(/\s*;\s*/, 2)
|
134
136
|
quality = 1.0
|
135
|
-
if md = /\Aq=([\d.]+)/.match(parameters)
|
137
|
+
if parameters && (md = /\Aq=([\d.]+)/.match(parameters))
|
136
138
|
quality = md[1].to_f
|
137
139
|
end
|
138
140
|
[value, quality]
|
139
141
|
end
|
140
142
|
end
|
141
|
-
module_function :q_values
|
142
143
|
|
144
|
+
# Return best accept value to use, based on the algorithm
|
145
|
+
# in RFC 2616 Section 14. If there are multiple best
|
146
|
+
# matches (same specificity and quality), the value returned
|
147
|
+
# is arbitrary.
|
143
148
|
def best_q_match(q_value_header, available_mimes)
|
144
149
|
values = q_values(q_value_header)
|
145
150
|
|
@@ -152,7 +157,6 @@ module Rack
|
|
152
157
|
end.last
|
153
158
|
matches && matches.first
|
154
159
|
end
|
155
|
-
module_function :best_q_match
|
156
160
|
|
157
161
|
ESCAPE_HTML = {
|
158
162
|
"&" => "&",
|
@@ -169,51 +173,55 @@ module Rack
|
|
169
173
|
def escape_html(string)
|
170
174
|
string.to_s.gsub(ESCAPE_HTML_PATTERN){|c| ESCAPE_HTML[c] }
|
171
175
|
end
|
172
|
-
module_function :escape_html
|
173
176
|
|
174
177
|
def select_best_encoding(available_encodings, accept_encoding)
|
175
178
|
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
|
176
179
|
|
177
|
-
expanded_accept_encoding =
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
180
|
+
expanded_accept_encoding = []
|
181
|
+
|
182
|
+
accept_encoding.each do |m, q|
|
183
|
+
preference = available_encodings.index(m) || available_encodings.size
|
184
|
+
|
185
|
+
if m == "*"
|
186
|
+
(available_encodings - accept_encoding.map(&:first)).each do |m2|
|
187
|
+
expanded_accept_encoding << [m2, q, preference]
|
183
188
|
end
|
184
|
-
|
185
|
-
|
186
|
-
|
189
|
+
else
|
190
|
+
expanded_accept_encoding << [m, q, preference]
|
191
|
+
end
|
192
|
+
end
|
187
193
|
|
188
|
-
encoding_candidates = expanded_accept_encoding
|
194
|
+
encoding_candidates = expanded_accept_encoding
|
195
|
+
.sort_by { |_, q, p| [-q, p] }
|
196
|
+
.map!(&:first)
|
189
197
|
|
190
198
|
unless encoding_candidates.include?("identity")
|
191
199
|
encoding_candidates.push("identity")
|
192
200
|
end
|
193
201
|
|
194
|
-
expanded_accept_encoding.each
|
202
|
+
expanded_accept_encoding.each do |m, q|
|
195
203
|
encoding_candidates.delete(m) if q == 0.0
|
196
|
-
|
204
|
+
end
|
197
205
|
|
198
|
-
|
206
|
+
(encoding_candidates & available_encodings)[0]
|
199
207
|
end
|
200
|
-
module_function :select_best_encoding
|
201
208
|
|
202
209
|
def parse_cookies(env)
|
203
210
|
parse_cookies_header env[HTTP_COOKIE]
|
204
211
|
end
|
205
|
-
module_function :parse_cookies
|
206
212
|
|
207
213
|
def parse_cookies_header(header)
|
208
|
-
# According to RFC
|
209
|
-
#
|
210
|
-
#
|
211
|
-
#
|
212
|
-
|
213
|
-
|
214
|
-
|
214
|
+
# According to RFC 6265:
|
215
|
+
# The syntax for cookie headers only supports semicolons
|
216
|
+
# User Agent -> Server ==
|
217
|
+
# Cookie: SID=31d4d96e407aad42; lang=en-US
|
218
|
+
return {} unless header
|
219
|
+
header.split(/[;] */n).each_with_object({}) do |cookie, cookies|
|
220
|
+
next if cookie.empty?
|
221
|
+
key, value = cookie.split('=', 2)
|
222
|
+
cookies[key] = (unescape(value) rescue value) unless cookies.key?(key)
|
223
|
+
end
|
215
224
|
end
|
216
|
-
module_function :parse_cookies_header
|
217
225
|
|
218
226
|
def add_cookie_to_header(header, key, value)
|
219
227
|
case value
|
@@ -221,41 +229,19 @@ module Rack
|
|
221
229
|
domain = "; domain=#{value[:domain]}" if value[:domain]
|
222
230
|
path = "; path=#{value[:path]}" if value[:path]
|
223
231
|
max_age = "; max-age=#{value[:max_age]}" if value[:max_age]
|
224
|
-
|
225
|
-
# only are there contradicting RFCs and examples within RFC text, but
|
226
|
-
# there are also numerous conflicting names of fields and partially
|
227
|
-
# cross-applicable specifications.
|
228
|
-
#
|
229
|
-
# These are best described in RFC 2616 3.3.1. This RFC text also
|
230
|
-
# specifies that RFC 822 as updated by RFC 1123 is preferred. That is a
|
231
|
-
# fixed length format with space-date delimited fields.
|
232
|
-
#
|
233
|
-
# See also RFC 1123 section 5.2.14.
|
234
|
-
#
|
235
|
-
# RFC 6265 also specifies "sane-cookie-date" as RFC 1123 date, defined
|
236
|
-
# in RFC 2616 3.3.1. RFC 6265 also gives examples that clearly denote
|
237
|
-
# the space delimited format. These formats are compliant with RFC 2822.
|
238
|
-
#
|
239
|
-
# For reference, all involved RFCs are:
|
240
|
-
# RFC 822
|
241
|
-
# RFC 1123
|
242
|
-
# RFC 2109
|
243
|
-
# RFC 2616
|
244
|
-
# RFC 2822
|
245
|
-
# RFC 2965
|
246
|
-
# RFC 6265
|
247
|
-
expires = "; expires=" +
|
248
|
-
rfc2822(value[:expires].clone.gmtime) if value[:expires]
|
232
|
+
expires = "; expires=#{value[:expires].httpdate}" if value[:expires]
|
249
233
|
secure = "; secure" if value[:secure]
|
250
234
|
httponly = "; HttpOnly" if (value.key?(:httponly) ? value[:httponly] : value[:http_only])
|
251
235
|
same_site =
|
252
236
|
case value[:same_site]
|
253
237
|
when false, nil
|
254
238
|
nil
|
239
|
+
when :none, 'None', :None
|
240
|
+
'; SameSite=None'
|
255
241
|
when :lax, 'Lax', :Lax
|
256
|
-
'; SameSite=Lax'
|
242
|
+
'; SameSite=Lax'
|
257
243
|
when true, :strict, 'Strict', :Strict
|
258
|
-
'; SameSite=Strict'
|
244
|
+
'; SameSite=Strict'
|
259
245
|
else
|
260
246
|
raise ArgumentError, "Invalid SameSite value: #{value[:same_site].inspect}"
|
261
247
|
end
|
@@ -277,13 +263,11 @@ module Rack
|
|
277
263
|
raise ArgumentError, "Unrecognized cookie header value. Expected String, Array, or nil, got #{header.inspect}"
|
278
264
|
end
|
279
265
|
end
|
280
|
-
module_function :add_cookie_to_header
|
281
266
|
|
282
267
|
def set_cookie_header!(header, key, value)
|
283
268
|
header[SET_COOKIE] = add_cookie_to_header(header[SET_COOKIE], key, value)
|
284
269
|
nil
|
285
270
|
end
|
286
|
-
module_function :set_cookie_header!
|
287
271
|
|
288
272
|
def make_delete_cookie_header(header, key, value)
|
289
273
|
case header
|
@@ -295,25 +279,30 @@ module Rack
|
|
295
279
|
cookies = header
|
296
280
|
end
|
297
281
|
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
282
|
+
key = escape(key)
|
283
|
+
domain = value[:domain]
|
284
|
+
path = value[:path]
|
285
|
+
regexp = if domain
|
286
|
+
if path
|
287
|
+
/\A#{key}=.*(?:domain=#{domain}(?:;|$).*path=#{path}(?:;|$)|path=#{path}(?:;|$).*domain=#{domain}(?:;|$))/
|
288
|
+
else
|
289
|
+
/\A#{key}=.*domain=#{domain}(?:;|$)/
|
290
|
+
end
|
291
|
+
elsif path
|
292
|
+
/\A#{key}=.*path=#{path}(?:;|$)/
|
293
|
+
else
|
294
|
+
/\A#{key}=/
|
295
|
+
end
|
296
|
+
|
297
|
+
cookies.reject! { |cookie| regexp.match? cookie }
|
307
298
|
|
308
299
|
cookies.join("\n")
|
309
300
|
end
|
310
|
-
module_function :make_delete_cookie_header
|
311
301
|
|
312
302
|
def delete_cookie_header!(header, key, value = {})
|
313
303
|
header[SET_COOKIE] = add_remove_cookie_to_header(header[SET_COOKIE], key, value)
|
314
304
|
nil
|
315
305
|
end
|
316
|
-
module_function :delete_cookie_header!
|
317
306
|
|
318
307
|
# Adds a cookie that will *remove* a cookie from the client. Hence the
|
319
308
|
# strange method name.
|
@@ -321,17 +310,15 @@ module Rack
|
|
321
310
|
new_header = make_delete_cookie_header(header, key, value)
|
322
311
|
|
323
312
|
add_cookie_to_header(new_header, key,
|
324
|
-
{:
|
325
|
-
:
|
326
|
-
:
|
313
|
+
{ value: '', path: nil, domain: nil,
|
314
|
+
max_age: '0',
|
315
|
+
expires: Time.at(0) }.merge(value))
|
327
316
|
|
328
317
|
end
|
329
|
-
module_function :add_remove_cookie_to_header
|
330
318
|
|
331
319
|
def rfc2822(time)
|
332
320
|
time.rfc2822
|
333
321
|
end
|
334
|
-
module_function :rfc2822
|
335
322
|
|
336
323
|
# Modified version of stdlib time.rb Time#rfc2822 to use '%d-%b-%Y' instead
|
337
324
|
# of '% %b %Y'.
|
@@ -343,11 +330,10 @@ module Rack
|
|
343
330
|
# weekday and month.
|
344
331
|
#
|
345
332
|
def rfc2109(time)
|
346
|
-
wday =
|
347
|
-
mon =
|
333
|
+
wday = RFC2822_DAY_NAME[time.wday]
|
334
|
+
mon = RFC2822_MONTH_NAME[time.mon - 1]
|
348
335
|
time.strftime("#{wday}, %d-#{mon}-%Y %H:%M:%S GMT")
|
349
336
|
end
|
350
|
-
module_function :rfc2109
|
351
337
|
|
352
338
|
# Parses the "Range:" header, if present, into an array of Range objects.
|
353
339
|
# Returns nil if the header is missing or syntactically invalid.
|
@@ -356,7 +342,6 @@ module Rack
|
|
356
342
|
warn "`byte_ranges` is deprecated, please use `get_byte_ranges`" if $VERBOSE
|
357
343
|
get_byte_ranges env['HTTP_RANGE'], size
|
358
344
|
end
|
359
|
-
module_function :byte_ranges
|
360
345
|
|
361
346
|
def get_byte_ranges(http_range, size)
|
362
347
|
# See <http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35>
|
@@ -364,7 +349,7 @@ module Rack
|
|
364
349
|
ranges = []
|
365
350
|
$1.split(/,\s*/).each do |range_spec|
|
366
351
|
return nil unless range_spec =~ /(\d*)-(\d*)/
|
367
|
-
r0,r1 = $1, $2
|
352
|
+
r0, r1 = $1, $2
|
368
353
|
if r0.empty?
|
369
354
|
return nil if r1.empty?
|
370
355
|
# suffix-byte-range-spec, represents trailing suffix of file
|
@@ -378,14 +363,13 @@ module Rack
|
|
378
363
|
else
|
379
364
|
r1 = r1.to_i
|
380
365
|
return nil if r1 < r0 # backwards range is syntactically invalid
|
381
|
-
r1 = size-1 if r1 >= size
|
366
|
+
r1 = size - 1 if r1 >= size
|
382
367
|
end
|
383
368
|
end
|
384
369
|
ranges << (r0..r1) if r0 <= r1
|
385
370
|
end
|
386
371
|
ranges
|
387
372
|
end
|
388
|
-
module_function :get_byte_ranges
|
389
373
|
|
390
374
|
# Constant time string comparison.
|
391
375
|
#
|
@@ -399,10 +383,9 @@ module Rack
|
|
399
383
|
l = a.unpack("C*")
|
400
384
|
|
401
385
|
r, i = 0, -1
|
402
|
-
b.each_byte { |v| r |= v ^ l[i+=1] }
|
386
|
+
b.each_byte { |v| r |= v ^ l[i += 1] }
|
403
387
|
r == 0
|
404
388
|
end
|
405
|
-
module_function :secure_compare
|
406
389
|
|
407
390
|
# Context allows the use of a compatible middleware at different points
|
408
391
|
# in a request handling stack. A compatible middleware must define
|
@@ -425,19 +408,25 @@ module Rack
|
|
425
408
|
self.class.new(@for, app)
|
426
409
|
end
|
427
410
|
|
428
|
-
def context(env, app
|
411
|
+
def context(env, app = @app)
|
429
412
|
recontext(app).call(env)
|
430
413
|
end
|
431
414
|
end
|
432
415
|
|
433
416
|
# A case-insensitive Hash that preserves the original case of a
|
434
417
|
# header when set.
|
435
|
-
|
436
|
-
|
437
|
-
|
418
|
+
#
|
419
|
+
# @api private
|
420
|
+
class HeaderHash < Hash # :nodoc:
|
421
|
+
def self.[](headers)
|
422
|
+
if headers.is_a?(HeaderHash) && !headers.frozen?
|
423
|
+
return headers
|
424
|
+
else
|
425
|
+
return self.new(headers)
|
426
|
+
end
|
438
427
|
end
|
439
428
|
|
440
|
-
def initialize(hash={})
|
429
|
+
def initialize(hash = {})
|
441
430
|
super()
|
442
431
|
@names = {}
|
443
432
|
hash.each { |k, v| self[k] = v }
|
@@ -449,6 +438,12 @@ module Rack
|
|
449
438
|
@names = other.names.dup
|
450
439
|
end
|
451
440
|
|
441
|
+
# on clear, we need to clear @names hash
|
442
|
+
def clear
|
443
|
+
super
|
444
|
+
@names.clear
|
445
|
+
end
|
446
|
+
|
452
447
|
def each
|
453
448
|
super do |k, v|
|
454
449
|
yield(k, v.respond_to?(:to_ary) ? v.to_ary.join("\n") : v)
|
@@ -457,7 +452,7 @@ module Rack
|
|
457
452
|
|
458
453
|
def to_hash
|
459
454
|
hash = {}
|
460
|
-
each { |k,v| hash[k] = v }
|
455
|
+
each { |k, v| hash[k] = v }
|
461
456
|
hash
|
462
457
|
end
|
463
458
|
|
@@ -510,13 +505,14 @@ module Rack
|
|
510
505
|
|
511
506
|
# Every standard HTTP code mapped to the appropriate message.
|
512
507
|
# Generated with:
|
513
|
-
#
|
514
|
-
#
|
515
|
-
#
|
508
|
+
# curl -s https://www.iana.org/assignments/http-status-codes/http-status-codes-1.csv | \
|
509
|
+
# ruby -ne 'm = /^(\d{3}),(?!Unassigned|\(Unused\))([^,]+)/.match($_) and \
|
510
|
+
# puts "#{m[1]} => \x27#{m[2].strip}\x27,"'
|
516
511
|
HTTP_STATUS_CODES = {
|
517
512
|
100 => 'Continue',
|
518
513
|
101 => 'Switching Protocols',
|
519
514
|
102 => 'Processing',
|
515
|
+
103 => 'Early Hints',
|
520
516
|
200 => 'OK',
|
521
517
|
201 => 'Created',
|
522
518
|
202 => 'Accepted',
|
@@ -533,6 +529,7 @@ module Rack
|
|
533
529
|
303 => 'See Other',
|
534
530
|
304 => 'Not Modified',
|
535
531
|
305 => 'Use Proxy',
|
532
|
+
306 => '(Unused)',
|
536
533
|
307 => 'Temporary Redirect',
|
537
534
|
308 => 'Permanent Redirect',
|
538
535
|
400 => 'Bad Request',
|
@@ -557,6 +554,7 @@ module Rack
|
|
557
554
|
422 => 'Unprocessable Entity',
|
558
555
|
423 => 'Locked',
|
559
556
|
424 => 'Failed Dependency',
|
557
|
+
425 => 'Too Early',
|
560
558
|
426 => 'Upgrade Required',
|
561
559
|
428 => 'Precondition Required',
|
562
560
|
429 => 'Too Many Requests',
|
@@ -571,12 +569,13 @@ module Rack
|
|
571
569
|
506 => 'Variant Also Negotiates',
|
572
570
|
507 => 'Insufficient Storage',
|
573
571
|
508 => 'Loop Detected',
|
572
|
+
509 => 'Bandwidth Limit Exceeded',
|
574
573
|
510 => 'Not Extended',
|
575
574
|
511 => 'Network Authentication Required'
|
576
575
|
}
|
577
576
|
|
578
577
|
# Responses with HTTP status codes that should not have an entity body
|
579
|
-
STATUS_WITH_NO_ENTITY_BODY =
|
578
|
+
STATUS_WITH_NO_ENTITY_BODY = Hash[((100..199).to_a << 204 << 304).product([true])]
|
580
579
|
|
581
580
|
SYMBOL_TO_STATUS_CODE = Hash[*HTTP_STATUS_CODES.map { |code, message|
|
582
581
|
[message.downcase.gsub(/\s|-|'/, '_').to_sym, code]
|
@@ -584,12 +583,11 @@ module Rack
|
|
584
583
|
|
585
584
|
def status_code(status)
|
586
585
|
if status.is_a?(Symbol)
|
587
|
-
SYMBOL_TO_STATUS_CODE
|
586
|
+
SYMBOL_TO_STATUS_CODE.fetch(status) { raise ArgumentError, "Unrecognized status code #{status.inspect}" }
|
588
587
|
else
|
589
588
|
status.to_i
|
590
589
|
end
|
591
590
|
end
|
592
|
-
module_function :status_code
|
593
591
|
|
594
592
|
PATH_SEPS = Regexp.union(*[::File::SEPARATOR, ::File::ALT_SEPARATOR].compact)
|
595
593
|
|
@@ -603,18 +601,16 @@ module Rack
|
|
603
601
|
part == '..' ? clean.pop : clean << part
|
604
602
|
end
|
605
603
|
|
606
|
-
|
607
|
-
|
608
|
-
|
604
|
+
clean_path = clean.join(::File::SEPARATOR)
|
605
|
+
clean_path.prepend("/") if parts.empty? || parts.first.empty?
|
606
|
+
clean_path
|
609
607
|
end
|
610
|
-
module_function :clean_path_info
|
611
608
|
|
612
|
-
NULL_BYTE = "\0"
|
609
|
+
NULL_BYTE = "\0"
|
613
610
|
|
614
611
|
def valid_path?(path)
|
615
612
|
path.valid_encoding? && !path.include?(NULL_BYTE)
|
616
613
|
end
|
617
|
-
module_function :valid_path?
|
618
614
|
|
619
615
|
end
|
620
616
|
end
|
data/lib/rack/version.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright (C) 2007-2019 Leah Neukirchen <http://leahneukirchen.org/infopage.html>
|
4
|
+
#
|
5
|
+
# Rack is freely distributable under the terms of an MIT-style license.
|
6
|
+
# See MIT-LICENSE or https://opensource.org/licenses/MIT.
|
7
|
+
|
8
|
+
# The Rack main module, serving as a namespace for all core Rack
|
9
|
+
# modules and classes.
|
10
|
+
#
|
11
|
+
# All modules meant for use in your application are <tt>autoload</tt>ed here,
|
12
|
+
# so it should be enough just to <tt>require 'rack'</tt> in your code.
|
13
|
+
|
14
|
+
module Rack
|
15
|
+
# The Rack protocol version number implemented.
|
16
|
+
VERSION = [1, 3]
|
17
|
+
|
18
|
+
# Return the Rack protocol version as a dotted string.
|
19
|
+
def self.version
|
20
|
+
VERSION.join(".")
|
21
|
+
end
|
22
|
+
|
23
|
+
RELEASE = "2.2.4"
|
24
|
+
|
25
|
+
# Return the Rack release as a dotted string.
|
26
|
+
def self.release
|
27
|
+
RELEASE
|
28
|
+
end
|
29
|
+
end
|