rack 2.0.9.3 → 2.2.1
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 +681 -0
- data/CONTRIBUTING.md +136 -0
- data/{COPYING → MIT-LICENSE} +4 -2
- data/README.rdoc +152 -162
- 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 +33 -28
- 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 +5 -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 +17 -11
- 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 +58 -73
- data/lib/rack/multipart/uploaded_file.rb +15 -7
- data/lib/rack/multipart.rb +7 -4
- data/lib/rack/null_logger.rb +2 -0
- data/lib/rack/query_parser.rb +53 -28
- 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 +33 -21
- data/lib/rack/session/cookie.rb +12 -12
- data/lib/rack/session/memcache.rb +4 -93
- data/lib/rack/session/pool.rb +5 -3
- data/lib/rack/show_exceptions.rb +17 -13
- data/lib/rack/show_status.rb +5 -5
- 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 +105 -130
- data/lib/rack/version.rb +29 -0
- data/lib/rack.rb +67 -73
- data/rack.gemspec +40 -28
- metadata +39 -182
- 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/utils.rb
CHANGED
@@ -1,16 +1,21 @@
|
|
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
|
@@ -24,53 +29,39 @@ module Rack
|
|
24
29
|
# This helps prevent a rogue client from flooding a Request.
|
25
30
|
self.default_query_parser = QueryParser.make_default(65536, 100)
|
26
31
|
|
32
|
+
module_function
|
33
|
+
|
27
34
|
# URI escapes. (CGI style space to +)
|
28
35
|
def escape(s)
|
29
36
|
URI.encode_www_form_component(s)
|
30
37
|
end
|
31
|
-
module_function :escape
|
32
38
|
|
33
39
|
# Like URI escaping, but with %20 instead of +. Strictly speaking this is
|
34
40
|
# true URI escaping.
|
35
41
|
def escape_path(s)
|
36
42
|
::URI::DEFAULT_PARSER.escape s
|
37
43
|
end
|
38
|
-
module_function :escape_path
|
39
44
|
|
40
45
|
# Unescapes the **path** component of a URI. See Rack::Utils.unescape for
|
41
46
|
# unescaping query parameters or form components.
|
42
47
|
def unescape_path(s)
|
43
48
|
::URI::DEFAULT_PARSER.unescape s
|
44
49
|
end
|
45
|
-
module_function :unescape_path
|
46
|
-
|
47
50
|
|
48
51
|
# Unescapes a URI escaped string with +encoding+. +encoding+ will be the
|
49
52
|
# target encoding of the string returned, and it defaults to UTF-8
|
50
53
|
def unescape(s, encoding = Encoding::UTF_8)
|
51
54
|
URI.decode_www_form_component(s, encoding)
|
52
55
|
end
|
53
|
-
module_function :unescape
|
54
56
|
|
55
57
|
class << self
|
56
|
-
attr_accessor :
|
57
|
-
|
58
|
-
attr_accessor :multipart_file_limit
|
59
|
-
|
60
|
-
# multipart_part_limit is the original name of multipart_file_limit, but
|
61
|
-
# the limit only counts parts with filenames.
|
62
|
-
alias multipart_part_limit multipart_file_limit
|
63
|
-
alias multipart_part_limit= multipart_file_limit=
|
58
|
+
attr_accessor :multipart_part_limit
|
64
59
|
end
|
65
60
|
|
66
|
-
# The maximum number of
|
67
|
-
#
|
61
|
+
# The maximum number of parts a request can contain. Accepting too many part
|
62
|
+
# can lead to the server running out of file handles.
|
68
63
|
# Set to `0` for no limit.
|
69
|
-
self.
|
70
|
-
|
71
|
-
# The maximum total number of parts a request can contain. Accepting too
|
72
|
-
# many can lead to excessive memory use and parsing time.
|
73
|
-
self.multipart_total_part_limit = (ENV['RACK_MULTIPART_TOTAL_PART_LIMIT'] || 4096).to_i
|
64
|
+
self.multipart_part_limit = (ENV['RACK_MULTIPART_PART_LIMIT'] || 128).to_i
|
74
65
|
|
75
66
|
def self.param_depth_limit
|
76
67
|
default_query_parser.param_depth_limit
|
@@ -93,21 +84,20 @@ module Rack
|
|
93
84
|
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
94
85
|
end
|
95
86
|
else
|
87
|
+
# :nocov:
|
96
88
|
def clock_time
|
97
89
|
Time.now.to_f
|
98
90
|
end
|
91
|
+
# :nocov:
|
99
92
|
end
|
100
|
-
module_function :clock_time
|
101
93
|
|
102
94
|
def parse_query(qs, d = nil, &unescaper)
|
103
95
|
Rack::Utils.default_query_parser.parse_query(qs, d, &unescaper)
|
104
96
|
end
|
105
|
-
module_function :parse_query
|
106
97
|
|
107
98
|
def parse_nested_query(qs, d = nil)
|
108
99
|
Rack::Utils.default_query_parser.parse_nested_query(qs, d)
|
109
100
|
end
|
110
|
-
module_function :parse_nested_query
|
111
101
|
|
112
102
|
def build_query(params)
|
113
103
|
params.map { |k, v|
|
@@ -118,7 +108,6 @@ module Rack
|
|
118
108
|
end
|
119
109
|
}.join("&")
|
120
110
|
end
|
121
|
-
module_function :build_query
|
122
111
|
|
123
112
|
def build_nested_query(value, prefix = nil)
|
124
113
|
case value
|
@@ -129,7 +118,7 @@ module Rack
|
|
129
118
|
when Hash
|
130
119
|
value.map { |k, v|
|
131
120
|
build_nested_query(v, prefix ? "#{prefix}[#{escape(k)}]" : escape(k))
|
132
|
-
}.
|
121
|
+
}.delete_if(&:empty?).join('&')
|
133
122
|
when nil
|
134
123
|
prefix
|
135
124
|
else
|
@@ -137,20 +126,22 @@ module Rack
|
|
137
126
|
"#{prefix}=#{escape(value)}"
|
138
127
|
end
|
139
128
|
end
|
140
|
-
module_function :build_nested_query
|
141
129
|
|
142
130
|
def q_values(q_value_header)
|
143
131
|
q_value_header.to_s.split(/\s*,\s*/).map do |part|
|
144
132
|
value, parameters = part.split(/\s*;\s*/, 2)
|
145
133
|
quality = 1.0
|
146
|
-
if md = /\Aq=([\d.]+)/.match(parameters)
|
134
|
+
if parameters && (md = /\Aq=([\d.]+)/.match(parameters))
|
147
135
|
quality = md[1].to_f
|
148
136
|
end
|
149
137
|
[value, quality]
|
150
138
|
end
|
151
139
|
end
|
152
|
-
module_function :q_values
|
153
140
|
|
141
|
+
# Return best accept value to use, based on the algorithm
|
142
|
+
# in RFC 2616 Section 14. If there are multiple best
|
143
|
+
# matches (same specificity and quality), the value returned
|
144
|
+
# is arbitrary.
|
154
145
|
def best_q_match(q_value_header, available_mimes)
|
155
146
|
values = q_values(q_value_header)
|
156
147
|
|
@@ -163,7 +154,6 @@ module Rack
|
|
163
154
|
end.last
|
164
155
|
matches && matches.first
|
165
156
|
end
|
166
|
-
module_function :best_q_match
|
167
157
|
|
168
158
|
ESCAPE_HTML = {
|
169
159
|
"&" => "&",
|
@@ -180,51 +170,51 @@ module Rack
|
|
180
170
|
def escape_html(string)
|
181
171
|
string.to_s.gsub(ESCAPE_HTML_PATTERN){|c| ESCAPE_HTML[c] }
|
182
172
|
end
|
183
|
-
module_function :escape_html
|
184
173
|
|
185
174
|
def select_best_encoding(available_encodings, accept_encoding)
|
186
175
|
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
|
187
176
|
|
188
|
-
expanded_accept_encoding =
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
177
|
+
expanded_accept_encoding = []
|
178
|
+
|
179
|
+
accept_encoding.each do |m, q|
|
180
|
+
preference = available_encodings.index(m) || available_encodings.size
|
181
|
+
|
182
|
+
if m == "*"
|
183
|
+
(available_encodings - accept_encoding.map(&:first)).each do |m2|
|
184
|
+
expanded_accept_encoding << [m2, q, preference]
|
194
185
|
end
|
195
|
-
|
196
|
-
|
197
|
-
|
186
|
+
else
|
187
|
+
expanded_accept_encoding << [m, q, preference]
|
188
|
+
end
|
189
|
+
end
|
198
190
|
|
199
|
-
encoding_candidates = expanded_accept_encoding
|
191
|
+
encoding_candidates = expanded_accept_encoding
|
192
|
+
.sort_by { |_, q, p| [-q, p] }
|
193
|
+
.map!(&:first)
|
200
194
|
|
201
195
|
unless encoding_candidates.include?("identity")
|
202
196
|
encoding_candidates.push("identity")
|
203
197
|
end
|
204
198
|
|
205
|
-
expanded_accept_encoding.each
|
199
|
+
expanded_accept_encoding.each do |m, q|
|
206
200
|
encoding_candidates.delete(m) if q == 0.0
|
207
|
-
|
201
|
+
end
|
208
202
|
|
209
|
-
|
203
|
+
(encoding_candidates & available_encodings)[0]
|
210
204
|
end
|
211
|
-
module_function :select_best_encoding
|
212
205
|
|
213
206
|
def parse_cookies(env)
|
214
207
|
parse_cookies_header env[HTTP_COOKIE]
|
215
208
|
end
|
216
|
-
module_function :parse_cookies
|
217
209
|
|
218
210
|
def parse_cookies_header(header)
|
219
|
-
# According to RFC
|
220
|
-
#
|
221
|
-
#
|
222
|
-
#
|
223
|
-
|
224
|
-
cookies
|
225
|
-
cookies.each_with_object({}) { |(k,v), hash| hash[k] = Array === v ? v.first : v }
|
211
|
+
# According to RFC 6265:
|
212
|
+
# The syntax for cookie headers only supports semicolons
|
213
|
+
# User Agent -> Server ==
|
214
|
+
# Cookie: SID=31d4d96e407aad42; lang=en-US
|
215
|
+
cookies = parse_query(header, ';') { |s| unescape(s) rescue s }
|
216
|
+
cookies.each_with_object({}) { |(k, v), hash| hash[k] = Array === v ? v.first : v }
|
226
217
|
end
|
227
|
-
module_function :parse_cookies_header
|
228
218
|
|
229
219
|
def add_cookie_to_header(header, key, value)
|
230
220
|
case value
|
@@ -232,31 +222,7 @@ module Rack
|
|
232
222
|
domain = "; domain=#{value[:domain]}" if value[:domain]
|
233
223
|
path = "; path=#{value[:path]}" if value[:path]
|
234
224
|
max_age = "; max-age=#{value[:max_age]}" if value[:max_age]
|
235
|
-
|
236
|
-
# only are there contradicting RFCs and examples within RFC text, but
|
237
|
-
# there are also numerous conflicting names of fields and partially
|
238
|
-
# cross-applicable specifications.
|
239
|
-
#
|
240
|
-
# These are best described in RFC 2616 3.3.1. This RFC text also
|
241
|
-
# specifies that RFC 822 as updated by RFC 1123 is preferred. That is a
|
242
|
-
# fixed length format with space-date delimited fields.
|
243
|
-
#
|
244
|
-
# See also RFC 1123 section 5.2.14.
|
245
|
-
#
|
246
|
-
# RFC 6265 also specifies "sane-cookie-date" as RFC 1123 date, defined
|
247
|
-
# in RFC 2616 3.3.1. RFC 6265 also gives examples that clearly denote
|
248
|
-
# the space delimited format. These formats are compliant with RFC 2822.
|
249
|
-
#
|
250
|
-
# For reference, all involved RFCs are:
|
251
|
-
# RFC 822
|
252
|
-
# RFC 1123
|
253
|
-
# RFC 2109
|
254
|
-
# RFC 2616
|
255
|
-
# RFC 2822
|
256
|
-
# RFC 2965
|
257
|
-
# RFC 6265
|
258
|
-
expires = "; expires=" +
|
259
|
-
rfc2822(value[:expires].clone.gmtime) if value[:expires]
|
225
|
+
expires = "; expires=#{value[:expires].httpdate}" if value[:expires]
|
260
226
|
secure = "; secure" if value[:secure]
|
261
227
|
httponly = "; HttpOnly" if (value.key?(:httponly) ? value[:httponly] : value[:http_only])
|
262
228
|
same_site =
|
@@ -264,11 +230,11 @@ module Rack
|
|
264
230
|
when false, nil
|
265
231
|
nil
|
266
232
|
when :none, 'None', :None
|
267
|
-
'; SameSite=None'
|
233
|
+
'; SameSite=None'
|
268
234
|
when :lax, 'Lax', :Lax
|
269
|
-
'; SameSite=Lax'
|
235
|
+
'; SameSite=Lax'
|
270
236
|
when true, :strict, 'Strict', :Strict
|
271
|
-
'; SameSite=Strict'
|
237
|
+
'; SameSite=Strict'
|
272
238
|
else
|
273
239
|
raise ArgumentError, "Invalid SameSite value: #{value[:same_site].inspect}"
|
274
240
|
end
|
@@ -290,13 +256,11 @@ module Rack
|
|
290
256
|
raise ArgumentError, "Unrecognized cookie header value. Expected String, Array, or nil, got #{header.inspect}"
|
291
257
|
end
|
292
258
|
end
|
293
|
-
module_function :add_cookie_to_header
|
294
259
|
|
295
260
|
def set_cookie_header!(header, key, value)
|
296
261
|
header[SET_COOKIE] = add_cookie_to_header(header[SET_COOKIE], key, value)
|
297
262
|
nil
|
298
263
|
end
|
299
|
-
module_function :set_cookie_header!
|
300
264
|
|
301
265
|
def make_delete_cookie_header(header, key, value)
|
302
266
|
case header
|
@@ -308,25 +272,30 @@ module Rack
|
|
308
272
|
cookies = header
|
309
273
|
end
|
310
274
|
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
275
|
+
key = escape(key)
|
276
|
+
domain = value[:domain]
|
277
|
+
path = value[:path]
|
278
|
+
regexp = if domain
|
279
|
+
if path
|
280
|
+
/\A#{key}=.*(?:domain=#{domain}(?:;|$).*path=#{path}(?:;|$)|path=#{path}(?:;|$).*domain=#{domain}(?:;|$))/
|
281
|
+
else
|
282
|
+
/\A#{key}=.*domain=#{domain}(?:;|$)/
|
283
|
+
end
|
284
|
+
elsif path
|
285
|
+
/\A#{key}=.*path=#{path}(?:;|$)/
|
286
|
+
else
|
287
|
+
/\A#{key}=/
|
288
|
+
end
|
289
|
+
|
290
|
+
cookies.reject! { |cookie| regexp.match? cookie }
|
320
291
|
|
321
292
|
cookies.join("\n")
|
322
293
|
end
|
323
|
-
module_function :make_delete_cookie_header
|
324
294
|
|
325
295
|
def delete_cookie_header!(header, key, value = {})
|
326
296
|
header[SET_COOKIE] = add_remove_cookie_to_header(header[SET_COOKIE], key, value)
|
327
297
|
nil
|
328
298
|
end
|
329
|
-
module_function :delete_cookie_header!
|
330
299
|
|
331
300
|
# Adds a cookie that will *remove* a cookie from the client. Hence the
|
332
301
|
# strange method name.
|
@@ -334,17 +303,15 @@ module Rack
|
|
334
303
|
new_header = make_delete_cookie_header(header, key, value)
|
335
304
|
|
336
305
|
add_cookie_to_header(new_header, key,
|
337
|
-
{:
|
338
|
-
:
|
339
|
-
:
|
306
|
+
{ value: '', path: nil, domain: nil,
|
307
|
+
max_age: '0',
|
308
|
+
expires: Time.at(0) }.merge(value))
|
340
309
|
|
341
310
|
end
|
342
|
-
module_function :add_remove_cookie_to_header
|
343
311
|
|
344
312
|
def rfc2822(time)
|
345
313
|
time.rfc2822
|
346
314
|
end
|
347
|
-
module_function :rfc2822
|
348
315
|
|
349
316
|
# Modified version of stdlib time.rb Time#rfc2822 to use '%d-%b-%Y' instead
|
350
317
|
# of '% %b %Y'.
|
@@ -360,7 +327,6 @@ module Rack
|
|
360
327
|
mon = Time::RFC2822_MONTH_NAME[time.mon - 1]
|
361
328
|
time.strftime("#{wday}, %d-#{mon}-%Y %H:%M:%S GMT")
|
362
329
|
end
|
363
|
-
module_function :rfc2109
|
364
330
|
|
365
331
|
# Parses the "Range:" header, if present, into an array of Range objects.
|
366
332
|
# Returns nil if the header is missing or syntactically invalid.
|
@@ -369,37 +335,34 @@ module Rack
|
|
369
335
|
warn "`byte_ranges` is deprecated, please use `get_byte_ranges`" if $VERBOSE
|
370
336
|
get_byte_ranges env['HTTP_RANGE'], size
|
371
337
|
end
|
372
|
-
module_function :byte_ranges
|
373
338
|
|
374
339
|
def get_byte_ranges(http_range, size)
|
375
340
|
# See <http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35>
|
376
341
|
return nil unless http_range && http_range =~ /bytes=([^;]+)/
|
377
342
|
ranges = []
|
378
343
|
$1.split(/,\s*/).each do |range_spec|
|
379
|
-
return nil
|
380
|
-
|
381
|
-
r0
|
382
|
-
|
383
|
-
return nil if r1.nil?
|
344
|
+
return nil unless range_spec =~ /(\d*)-(\d*)/
|
345
|
+
r0, r1 = $1, $2
|
346
|
+
if r0.empty?
|
347
|
+
return nil if r1.empty?
|
384
348
|
# suffix-byte-range-spec, represents trailing suffix of file
|
385
349
|
r0 = size - r1.to_i
|
386
350
|
r0 = 0 if r0 < 0
|
387
351
|
r1 = size - 1
|
388
352
|
else
|
389
353
|
r0 = r0.to_i
|
390
|
-
if r1.
|
354
|
+
if r1.empty?
|
391
355
|
r1 = size - 1
|
392
356
|
else
|
393
357
|
r1 = r1.to_i
|
394
358
|
return nil if r1 < r0 # backwards range is syntactically invalid
|
395
|
-
r1 = size-1 if r1 >= size
|
359
|
+
r1 = size - 1 if r1 >= size
|
396
360
|
end
|
397
361
|
end
|
398
362
|
ranges << (r0..r1) if r0 <= r1
|
399
363
|
end
|
400
364
|
ranges
|
401
365
|
end
|
402
|
-
module_function :get_byte_ranges
|
403
366
|
|
404
367
|
# Constant time string comparison.
|
405
368
|
#
|
@@ -413,10 +376,9 @@ module Rack
|
|
413
376
|
l = a.unpack("C*")
|
414
377
|
|
415
378
|
r, i = 0, -1
|
416
|
-
b.each_byte { |v| r |= v ^ l[i+=1] }
|
379
|
+
b.each_byte { |v| r |= v ^ l[i += 1] }
|
417
380
|
r == 0
|
418
381
|
end
|
419
|
-
module_function :secure_compare
|
420
382
|
|
421
383
|
# Context allows the use of a compatible middleware at different points
|
422
384
|
# in a request handling stack. A compatible middleware must define
|
@@ -439,19 +401,25 @@ module Rack
|
|
439
401
|
self.class.new(@for, app)
|
440
402
|
end
|
441
403
|
|
442
|
-
def context(env, app
|
404
|
+
def context(env, app = @app)
|
443
405
|
recontext(app).call(env)
|
444
406
|
end
|
445
407
|
end
|
446
408
|
|
447
409
|
# A case-insensitive Hash that preserves the original case of a
|
448
410
|
# header when set.
|
449
|
-
|
450
|
-
|
451
|
-
|
411
|
+
#
|
412
|
+
# @api private
|
413
|
+
class HeaderHash < Hash # :nodoc:
|
414
|
+
def self.[](headers)
|
415
|
+
if headers.is_a?(HeaderHash) && !headers.frozen?
|
416
|
+
return headers
|
417
|
+
else
|
418
|
+
return self.new(headers)
|
419
|
+
end
|
452
420
|
end
|
453
421
|
|
454
|
-
def initialize(hash={})
|
422
|
+
def initialize(hash = {})
|
455
423
|
super()
|
456
424
|
@names = {}
|
457
425
|
hash.each { |k, v| self[k] = v }
|
@@ -463,6 +431,12 @@ module Rack
|
|
463
431
|
@names = other.names.dup
|
464
432
|
end
|
465
433
|
|
434
|
+
# on clear, we need to clear @names hash
|
435
|
+
def clear
|
436
|
+
super
|
437
|
+
@names.clear
|
438
|
+
end
|
439
|
+
|
466
440
|
def each
|
467
441
|
super do |k, v|
|
468
442
|
yield(k, v.respond_to?(:to_ary) ? v.to_ary.join("\n") : v)
|
@@ -471,7 +445,7 @@ module Rack
|
|
471
445
|
|
472
446
|
def to_hash
|
473
447
|
hash = {}
|
474
|
-
each { |k,v| hash[k] = v }
|
448
|
+
each { |k, v| hash[k] = v }
|
475
449
|
hash
|
476
450
|
end
|
477
451
|
|
@@ -524,13 +498,14 @@ module Rack
|
|
524
498
|
|
525
499
|
# Every standard HTTP code mapped to the appropriate message.
|
526
500
|
# Generated with:
|
527
|
-
#
|
528
|
-
#
|
529
|
-
#
|
501
|
+
# curl -s https://www.iana.org/assignments/http-status-codes/http-status-codes-1.csv | \
|
502
|
+
# ruby -ne 'm = /^(\d{3}),(?!Unassigned|\(Unused\))([^,]+)/.match($_) and \
|
503
|
+
# puts "#{m[1]} => \x27#{m[2].strip}\x27,"'
|
530
504
|
HTTP_STATUS_CODES = {
|
531
505
|
100 => 'Continue',
|
532
506
|
101 => 'Switching Protocols',
|
533
507
|
102 => 'Processing',
|
508
|
+
103 => 'Early Hints',
|
534
509
|
200 => 'OK',
|
535
510
|
201 => 'Created',
|
536
511
|
202 => 'Accepted',
|
@@ -547,6 +522,7 @@ module Rack
|
|
547
522
|
303 => 'See Other',
|
548
523
|
304 => 'Not Modified',
|
549
524
|
305 => 'Use Proxy',
|
525
|
+
306 => '(Unused)',
|
550
526
|
307 => 'Temporary Redirect',
|
551
527
|
308 => 'Permanent Redirect',
|
552
528
|
400 => 'Bad Request',
|
@@ -571,6 +547,7 @@ module Rack
|
|
571
547
|
422 => 'Unprocessable Entity',
|
572
548
|
423 => 'Locked',
|
573
549
|
424 => 'Failed Dependency',
|
550
|
+
425 => 'Too Early',
|
574
551
|
426 => 'Upgrade Required',
|
575
552
|
428 => 'Precondition Required',
|
576
553
|
429 => 'Too Many Requests',
|
@@ -585,12 +562,13 @@ module Rack
|
|
585
562
|
506 => 'Variant Also Negotiates',
|
586
563
|
507 => 'Insufficient Storage',
|
587
564
|
508 => 'Loop Detected',
|
565
|
+
509 => 'Bandwidth Limit Exceeded',
|
588
566
|
510 => 'Not Extended',
|
589
567
|
511 => 'Network Authentication Required'
|
590
568
|
}
|
591
569
|
|
592
570
|
# Responses with HTTP status codes that should not have an entity body
|
593
|
-
STATUS_WITH_NO_ENTITY_BODY =
|
571
|
+
STATUS_WITH_NO_ENTITY_BODY = Hash[((100..199).to_a << 204 << 304).product([true])]
|
594
572
|
|
595
573
|
SYMBOL_TO_STATUS_CODE = Hash[*HTTP_STATUS_CODES.map { |code, message|
|
596
574
|
[message.downcase.gsub(/\s|-|'/, '_').to_sym, code]
|
@@ -598,12 +576,11 @@ module Rack
|
|
598
576
|
|
599
577
|
def status_code(status)
|
600
578
|
if status.is_a?(Symbol)
|
601
|
-
SYMBOL_TO_STATUS_CODE
|
579
|
+
SYMBOL_TO_STATUS_CODE.fetch(status) { raise ArgumentError, "Unrecognized status code #{status.inspect}" }
|
602
580
|
else
|
603
581
|
status.to_i
|
604
582
|
end
|
605
583
|
end
|
606
|
-
module_function :status_code
|
607
584
|
|
608
585
|
PATH_SEPS = Regexp.union(*[::File::SEPARATOR, ::File::ALT_SEPARATOR].compact)
|
609
586
|
|
@@ -617,18 +594,16 @@ module Rack
|
|
617
594
|
part == '..' ? clean.pop : clean << part
|
618
595
|
end
|
619
596
|
|
620
|
-
|
621
|
-
|
622
|
-
|
597
|
+
clean_path = clean.join(::File::SEPARATOR)
|
598
|
+
clean_path.prepend("/") if parts.empty? || parts.first.empty?
|
599
|
+
clean_path
|
623
600
|
end
|
624
|
-
module_function :clean_path_info
|
625
601
|
|
626
|
-
NULL_BYTE = "\0"
|
602
|
+
NULL_BYTE = "\0"
|
627
603
|
|
628
604
|
def valid_path?(path)
|
629
605
|
path.valid_encoding? && !path.include?(NULL_BYTE)
|
630
606
|
end
|
631
|
-
module_function :valid_path?
|
632
607
|
|
633
608
|
end
|
634
609
|
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.1"
|
24
|
+
|
25
|
+
# Return the Rack release as a dotted string.
|
26
|
+
def self.release
|
27
|
+
RELEASE
|
28
|
+
end
|
29
|
+
end
|