rack 2.0.6 → 2.2.7
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 +4 -4
- data/CHANGELOG.md +735 -0
- data/CONTRIBUTING.md +136 -0
- data/{COPYING → MIT-LICENSE} +4 -2
- data/README.rdoc +163 -145
- data/Rakefile +37 -23
- data/{SPEC → SPEC.rdoc} +29 -5
- 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 +5 -3
- 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 +74 -67
- 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 +59 -30
- data/lib/rack/recursive.rb +7 -5
- data/lib/rack/reloader.rb +8 -4
- data/lib/rack/request.rb +222 -63
- 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 +13 -7
- data/lib/rack/utils.rb +127 -119
- data/lib/rack/version.rb +29 -0
- data/lib/rack.rb +67 -73
- data/rack.gemspec +40 -28
- metadata +39 -181
- 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 -1398
- 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,42 +32,50 @@ 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
|
-
attr_accessor :
|
|
61
|
+
attr_accessor :multipart_total_part_limit
|
|
62
|
+
|
|
63
|
+
attr_accessor :multipart_file_limit
|
|
64
|
+
|
|
65
|
+
# multipart_part_limit is the original name of multipart_file_limit, but
|
|
66
|
+
# the limit only counts parts with filenames.
|
|
67
|
+
alias multipart_part_limit multipart_file_limit
|
|
68
|
+
alias multipart_part_limit= multipart_file_limit=
|
|
57
69
|
end
|
|
58
70
|
|
|
59
|
-
# The maximum number of parts a request can contain. Accepting too
|
|
60
|
-
# can lead to the server running out of file handles.
|
|
71
|
+
# The maximum number of file parts a request can contain. Accepting too
|
|
72
|
+
# many parts can lead to the server running out of file handles.
|
|
61
73
|
# Set to `0` for no limit.
|
|
62
|
-
self.
|
|
74
|
+
self.multipart_file_limit = (ENV['RACK_MULTIPART_PART_LIMIT'] || ENV['RACK_MULTIPART_FILE_LIMIT'] || 128).to_i
|
|
75
|
+
|
|
76
|
+
# The maximum total number of parts a request can contain. Accepting too
|
|
77
|
+
# many can lead to excessive memory use and parsing time.
|
|
78
|
+
self.multipart_total_part_limit = (ENV['RACK_MULTIPART_TOTAL_PART_LIMIT'] || 4096).to_i
|
|
63
79
|
|
|
64
80
|
def self.param_depth_limit
|
|
65
81
|
default_query_parser.param_depth_limit
|
|
@@ -82,21 +98,20 @@ module Rack
|
|
|
82
98
|
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
83
99
|
end
|
|
84
100
|
else
|
|
101
|
+
# :nocov:
|
|
85
102
|
def clock_time
|
|
86
103
|
Time.now.to_f
|
|
87
104
|
end
|
|
105
|
+
# :nocov:
|
|
88
106
|
end
|
|
89
|
-
module_function :clock_time
|
|
90
107
|
|
|
91
108
|
def parse_query(qs, d = nil, &unescaper)
|
|
92
109
|
Rack::Utils.default_query_parser.parse_query(qs, d, &unescaper)
|
|
93
110
|
end
|
|
94
|
-
module_function :parse_query
|
|
95
111
|
|
|
96
112
|
def parse_nested_query(qs, d = nil)
|
|
97
113
|
Rack::Utils.default_query_parser.parse_nested_query(qs, d)
|
|
98
114
|
end
|
|
99
|
-
module_function :parse_nested_query
|
|
100
115
|
|
|
101
116
|
def build_query(params)
|
|
102
117
|
params.map { |k, v|
|
|
@@ -107,7 +122,6 @@ module Rack
|
|
|
107
122
|
end
|
|
108
123
|
}.join("&")
|
|
109
124
|
end
|
|
110
|
-
module_function :build_query
|
|
111
125
|
|
|
112
126
|
def build_nested_query(value, prefix = nil)
|
|
113
127
|
case value
|
|
@@ -118,7 +132,7 @@ module Rack
|
|
|
118
132
|
when Hash
|
|
119
133
|
value.map { |k, v|
|
|
120
134
|
build_nested_query(v, prefix ? "#{prefix}[#{escape(k)}]" : escape(k))
|
|
121
|
-
}.
|
|
135
|
+
}.delete_if(&:empty?).join('&')
|
|
122
136
|
when nil
|
|
123
137
|
prefix
|
|
124
138
|
else
|
|
@@ -126,20 +140,22 @@ module Rack
|
|
|
126
140
|
"#{prefix}=#{escape(value)}"
|
|
127
141
|
end
|
|
128
142
|
end
|
|
129
|
-
module_function :build_nested_query
|
|
130
143
|
|
|
131
144
|
def q_values(q_value_header)
|
|
132
145
|
q_value_header.to_s.split(/\s*,\s*/).map do |part|
|
|
133
146
|
value, parameters = part.split(/\s*;\s*/, 2)
|
|
134
147
|
quality = 1.0
|
|
135
|
-
if md = /\Aq=([\d.]+)/.match(parameters)
|
|
148
|
+
if parameters && (md = /\Aq=([\d.]+)/.match(parameters))
|
|
136
149
|
quality = md[1].to_f
|
|
137
150
|
end
|
|
138
151
|
[value, quality]
|
|
139
152
|
end
|
|
140
153
|
end
|
|
141
|
-
module_function :q_values
|
|
142
154
|
|
|
155
|
+
# Return best accept value to use, based on the algorithm
|
|
156
|
+
# in RFC 2616 Section 14. If there are multiple best
|
|
157
|
+
# matches (same specificity and quality), the value returned
|
|
158
|
+
# is arbitrary.
|
|
143
159
|
def best_q_match(q_value_header, available_mimes)
|
|
144
160
|
values = q_values(q_value_header)
|
|
145
161
|
|
|
@@ -152,7 +168,6 @@ module Rack
|
|
|
152
168
|
end.last
|
|
153
169
|
matches && matches.first
|
|
154
170
|
end
|
|
155
|
-
module_function :best_q_match
|
|
156
171
|
|
|
157
172
|
ESCAPE_HTML = {
|
|
158
173
|
"&" => "&",
|
|
@@ -169,51 +184,55 @@ module Rack
|
|
|
169
184
|
def escape_html(string)
|
|
170
185
|
string.to_s.gsub(ESCAPE_HTML_PATTERN){|c| ESCAPE_HTML[c] }
|
|
171
186
|
end
|
|
172
|
-
module_function :escape_html
|
|
173
187
|
|
|
174
188
|
def select_best_encoding(available_encodings, accept_encoding)
|
|
175
189
|
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
|
|
176
190
|
|
|
177
|
-
expanded_accept_encoding =
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
191
|
+
expanded_accept_encoding = []
|
|
192
|
+
|
|
193
|
+
accept_encoding.each do |m, q|
|
|
194
|
+
preference = available_encodings.index(m) || available_encodings.size
|
|
195
|
+
|
|
196
|
+
if m == "*"
|
|
197
|
+
(available_encodings - accept_encoding.map(&:first)).each do |m2|
|
|
198
|
+
expanded_accept_encoding << [m2, q, preference]
|
|
183
199
|
end
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
200
|
+
else
|
|
201
|
+
expanded_accept_encoding << [m, q, preference]
|
|
202
|
+
end
|
|
203
|
+
end
|
|
187
204
|
|
|
188
|
-
encoding_candidates = expanded_accept_encoding
|
|
205
|
+
encoding_candidates = expanded_accept_encoding
|
|
206
|
+
.sort_by { |_, q, p| [-q, p] }
|
|
207
|
+
.map!(&:first)
|
|
189
208
|
|
|
190
209
|
unless encoding_candidates.include?("identity")
|
|
191
210
|
encoding_candidates.push("identity")
|
|
192
211
|
end
|
|
193
212
|
|
|
194
|
-
expanded_accept_encoding.each
|
|
213
|
+
expanded_accept_encoding.each do |m, q|
|
|
195
214
|
encoding_candidates.delete(m) if q == 0.0
|
|
196
|
-
|
|
215
|
+
end
|
|
197
216
|
|
|
198
|
-
|
|
217
|
+
(encoding_candidates & available_encodings)[0]
|
|
199
218
|
end
|
|
200
|
-
module_function :select_best_encoding
|
|
201
219
|
|
|
202
220
|
def parse_cookies(env)
|
|
203
221
|
parse_cookies_header env[HTTP_COOKIE]
|
|
204
222
|
end
|
|
205
|
-
module_function :parse_cookies
|
|
206
223
|
|
|
207
224
|
def parse_cookies_header(header)
|
|
208
|
-
# According to RFC
|
|
209
|
-
#
|
|
210
|
-
#
|
|
211
|
-
#
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
225
|
+
# According to RFC 6265:
|
|
226
|
+
# The syntax for cookie headers only supports semicolons
|
|
227
|
+
# User Agent -> Server ==
|
|
228
|
+
# Cookie: SID=31d4d96e407aad42; lang=en-US
|
|
229
|
+
return {} unless header
|
|
230
|
+
header.split(/[;] */n).each_with_object({}) do |cookie, cookies|
|
|
231
|
+
next if cookie.empty?
|
|
232
|
+
key, value = cookie.split('=', 2)
|
|
233
|
+
cookies[key] = (unescape(value) rescue value) unless cookies.key?(key)
|
|
234
|
+
end
|
|
215
235
|
end
|
|
216
|
-
module_function :parse_cookies_header
|
|
217
236
|
|
|
218
237
|
def add_cookie_to_header(header, key, value)
|
|
219
238
|
case value
|
|
@@ -221,41 +240,19 @@ module Rack
|
|
|
221
240
|
domain = "; domain=#{value[:domain]}" if value[:domain]
|
|
222
241
|
path = "; path=#{value[:path]}" if value[:path]
|
|
223
242
|
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]
|
|
243
|
+
expires = "; expires=#{value[:expires].httpdate}" if value[:expires]
|
|
249
244
|
secure = "; secure" if value[:secure]
|
|
250
245
|
httponly = "; HttpOnly" if (value.key?(:httponly) ? value[:httponly] : value[:http_only])
|
|
251
246
|
same_site =
|
|
252
247
|
case value[:same_site]
|
|
253
248
|
when false, nil
|
|
254
249
|
nil
|
|
250
|
+
when :none, 'None', :None
|
|
251
|
+
'; SameSite=None'
|
|
255
252
|
when :lax, 'Lax', :Lax
|
|
256
|
-
'; SameSite=Lax'
|
|
253
|
+
'; SameSite=Lax'
|
|
257
254
|
when true, :strict, 'Strict', :Strict
|
|
258
|
-
'; SameSite=Strict'
|
|
255
|
+
'; SameSite=Strict'
|
|
259
256
|
else
|
|
260
257
|
raise ArgumentError, "Invalid SameSite value: #{value[:same_site].inspect}"
|
|
261
258
|
end
|
|
@@ -277,13 +274,11 @@ module Rack
|
|
|
277
274
|
raise ArgumentError, "Unrecognized cookie header value. Expected String, Array, or nil, got #{header.inspect}"
|
|
278
275
|
end
|
|
279
276
|
end
|
|
280
|
-
module_function :add_cookie_to_header
|
|
281
277
|
|
|
282
278
|
def set_cookie_header!(header, key, value)
|
|
283
279
|
header[SET_COOKIE] = add_cookie_to_header(header[SET_COOKIE], key, value)
|
|
284
280
|
nil
|
|
285
281
|
end
|
|
286
|
-
module_function :set_cookie_header!
|
|
287
282
|
|
|
288
283
|
def make_delete_cookie_header(header, key, value)
|
|
289
284
|
case header
|
|
@@ -295,25 +290,30 @@ module Rack
|
|
|
295
290
|
cookies = header
|
|
296
291
|
end
|
|
297
292
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
293
|
+
key = escape(key)
|
|
294
|
+
domain = value[:domain]
|
|
295
|
+
path = value[:path]
|
|
296
|
+
regexp = if domain
|
|
297
|
+
if path
|
|
298
|
+
/\A#{key}=.*(?:domain=#{domain}(?:;|$).*path=#{path}(?:;|$)|path=#{path}(?:;|$).*domain=#{domain}(?:;|$))/
|
|
299
|
+
else
|
|
300
|
+
/\A#{key}=.*domain=#{domain}(?:;|$)/
|
|
301
|
+
end
|
|
302
|
+
elsif path
|
|
303
|
+
/\A#{key}=.*path=#{path}(?:;|$)/
|
|
304
|
+
else
|
|
305
|
+
/\A#{key}=/
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
cookies.reject! { |cookie| regexp.match? cookie }
|
|
307
309
|
|
|
308
310
|
cookies.join("\n")
|
|
309
311
|
end
|
|
310
|
-
module_function :make_delete_cookie_header
|
|
311
312
|
|
|
312
313
|
def delete_cookie_header!(header, key, value = {})
|
|
313
314
|
header[SET_COOKIE] = add_remove_cookie_to_header(header[SET_COOKIE], key, value)
|
|
314
315
|
nil
|
|
315
316
|
end
|
|
316
|
-
module_function :delete_cookie_header!
|
|
317
317
|
|
|
318
318
|
# Adds a cookie that will *remove* a cookie from the client. Hence the
|
|
319
319
|
# strange method name.
|
|
@@ -321,17 +321,15 @@ module Rack
|
|
|
321
321
|
new_header = make_delete_cookie_header(header, key, value)
|
|
322
322
|
|
|
323
323
|
add_cookie_to_header(new_header, key,
|
|
324
|
-
{:
|
|
325
|
-
:
|
|
326
|
-
:
|
|
324
|
+
{ value: '', path: nil, domain: nil,
|
|
325
|
+
max_age: '0',
|
|
326
|
+
expires: Time.at(0) }.merge(value))
|
|
327
327
|
|
|
328
328
|
end
|
|
329
|
-
module_function :add_remove_cookie_to_header
|
|
330
329
|
|
|
331
330
|
def rfc2822(time)
|
|
332
331
|
time.rfc2822
|
|
333
332
|
end
|
|
334
|
-
module_function :rfc2822
|
|
335
333
|
|
|
336
334
|
# Modified version of stdlib time.rb Time#rfc2822 to use '%d-%b-%Y' instead
|
|
337
335
|
# of '% %b %Y'.
|
|
@@ -343,11 +341,10 @@ module Rack
|
|
|
343
341
|
# weekday and month.
|
|
344
342
|
#
|
|
345
343
|
def rfc2109(time)
|
|
346
|
-
wday =
|
|
347
|
-
mon =
|
|
344
|
+
wday = RFC2822_DAY_NAME[time.wday]
|
|
345
|
+
mon = RFC2822_MONTH_NAME[time.mon - 1]
|
|
348
346
|
time.strftime("#{wday}, %d-#{mon}-%Y %H:%M:%S GMT")
|
|
349
347
|
end
|
|
350
|
-
module_function :rfc2109
|
|
351
348
|
|
|
352
349
|
# Parses the "Range:" header, if present, into an array of Range objects.
|
|
353
350
|
# Returns nil if the header is missing or syntactically invalid.
|
|
@@ -356,36 +353,35 @@ module Rack
|
|
|
356
353
|
warn "`byte_ranges` is deprecated, please use `get_byte_ranges`" if $VERBOSE
|
|
357
354
|
get_byte_ranges env['HTTP_RANGE'], size
|
|
358
355
|
end
|
|
359
|
-
module_function :byte_ranges
|
|
360
356
|
|
|
361
357
|
def get_byte_ranges(http_range, size)
|
|
362
358
|
# See <http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35>
|
|
363
359
|
return nil unless http_range && http_range =~ /bytes=([^;]+)/
|
|
364
360
|
ranges = []
|
|
365
361
|
$1.split(/,\s*/).each do |range_spec|
|
|
366
|
-
return nil
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
362
|
+
return nil unless range_spec.include?('-')
|
|
363
|
+
range = range_spec.split('-')
|
|
364
|
+
r0, r1 = range[0], range[1]
|
|
365
|
+
if r0.nil? || r0.empty?
|
|
366
|
+
return nil if r1.nil?
|
|
370
367
|
# suffix-byte-range-spec, represents trailing suffix of file
|
|
371
368
|
r0 = size - r1.to_i
|
|
372
369
|
r0 = 0 if r0 < 0
|
|
373
370
|
r1 = size - 1
|
|
374
371
|
else
|
|
375
372
|
r0 = r0.to_i
|
|
376
|
-
if r1.
|
|
373
|
+
if r1.nil?
|
|
377
374
|
r1 = size - 1
|
|
378
375
|
else
|
|
379
376
|
r1 = r1.to_i
|
|
380
377
|
return nil if r1 < r0 # backwards range is syntactically invalid
|
|
381
|
-
r1 = size-1 if r1 >= size
|
|
378
|
+
r1 = size - 1 if r1 >= size
|
|
382
379
|
end
|
|
383
380
|
end
|
|
384
381
|
ranges << (r0..r1) if r0 <= r1
|
|
385
382
|
end
|
|
386
383
|
ranges
|
|
387
384
|
end
|
|
388
|
-
module_function :get_byte_ranges
|
|
389
385
|
|
|
390
386
|
# Constant time string comparison.
|
|
391
387
|
#
|
|
@@ -399,10 +395,9 @@ module Rack
|
|
|
399
395
|
l = a.unpack("C*")
|
|
400
396
|
|
|
401
397
|
r, i = 0, -1
|
|
402
|
-
b.each_byte { |v| r |= v ^ l[i+=1] }
|
|
398
|
+
b.each_byte { |v| r |= v ^ l[i += 1] }
|
|
403
399
|
r == 0
|
|
404
400
|
end
|
|
405
|
-
module_function :secure_compare
|
|
406
401
|
|
|
407
402
|
# Context allows the use of a compatible middleware at different points
|
|
408
403
|
# in a request handling stack. A compatible middleware must define
|
|
@@ -425,19 +420,25 @@ module Rack
|
|
|
425
420
|
self.class.new(@for, app)
|
|
426
421
|
end
|
|
427
422
|
|
|
428
|
-
def context(env, app
|
|
423
|
+
def context(env, app = @app)
|
|
429
424
|
recontext(app).call(env)
|
|
430
425
|
end
|
|
431
426
|
end
|
|
432
427
|
|
|
433
428
|
# A case-insensitive Hash that preserves the original case of a
|
|
434
429
|
# header when set.
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
430
|
+
#
|
|
431
|
+
# @api private
|
|
432
|
+
class HeaderHash < Hash # :nodoc:
|
|
433
|
+
def self.[](headers)
|
|
434
|
+
if headers.is_a?(HeaderHash) && !headers.frozen?
|
|
435
|
+
return headers
|
|
436
|
+
else
|
|
437
|
+
return self.new(headers)
|
|
438
|
+
end
|
|
438
439
|
end
|
|
439
440
|
|
|
440
|
-
def initialize(hash={})
|
|
441
|
+
def initialize(hash = {})
|
|
441
442
|
super()
|
|
442
443
|
@names = {}
|
|
443
444
|
hash.each { |k, v| self[k] = v }
|
|
@@ -449,6 +450,12 @@ module Rack
|
|
|
449
450
|
@names = other.names.dup
|
|
450
451
|
end
|
|
451
452
|
|
|
453
|
+
# on clear, we need to clear @names hash
|
|
454
|
+
def clear
|
|
455
|
+
super
|
|
456
|
+
@names.clear
|
|
457
|
+
end
|
|
458
|
+
|
|
452
459
|
def each
|
|
453
460
|
super do |k, v|
|
|
454
461
|
yield(k, v.respond_to?(:to_ary) ? v.to_ary.join("\n") : v)
|
|
@@ -457,7 +464,7 @@ module Rack
|
|
|
457
464
|
|
|
458
465
|
def to_hash
|
|
459
466
|
hash = {}
|
|
460
|
-
each { |k,v| hash[k] = v }
|
|
467
|
+
each { |k, v| hash[k] = v }
|
|
461
468
|
hash
|
|
462
469
|
end
|
|
463
470
|
|
|
@@ -510,13 +517,14 @@ module Rack
|
|
|
510
517
|
|
|
511
518
|
# Every standard HTTP code mapped to the appropriate message.
|
|
512
519
|
# Generated with:
|
|
513
|
-
#
|
|
514
|
-
#
|
|
515
|
-
#
|
|
520
|
+
# curl -s https://www.iana.org/assignments/http-status-codes/http-status-codes-1.csv | \
|
|
521
|
+
# ruby -ne 'm = /^(\d{3}),(?!Unassigned|\(Unused\))([^,]+)/.match($_) and \
|
|
522
|
+
# puts "#{m[1]} => \x27#{m[2].strip}\x27,"'
|
|
516
523
|
HTTP_STATUS_CODES = {
|
|
517
524
|
100 => 'Continue',
|
|
518
525
|
101 => 'Switching Protocols',
|
|
519
526
|
102 => 'Processing',
|
|
527
|
+
103 => 'Early Hints',
|
|
520
528
|
200 => 'OK',
|
|
521
529
|
201 => 'Created',
|
|
522
530
|
202 => 'Accepted',
|
|
@@ -533,6 +541,7 @@ module Rack
|
|
|
533
541
|
303 => 'See Other',
|
|
534
542
|
304 => 'Not Modified',
|
|
535
543
|
305 => 'Use Proxy',
|
|
544
|
+
306 => '(Unused)',
|
|
536
545
|
307 => 'Temporary Redirect',
|
|
537
546
|
308 => 'Permanent Redirect',
|
|
538
547
|
400 => 'Bad Request',
|
|
@@ -557,6 +566,7 @@ module Rack
|
|
|
557
566
|
422 => 'Unprocessable Entity',
|
|
558
567
|
423 => 'Locked',
|
|
559
568
|
424 => 'Failed Dependency',
|
|
569
|
+
425 => 'Too Early',
|
|
560
570
|
426 => 'Upgrade Required',
|
|
561
571
|
428 => 'Precondition Required',
|
|
562
572
|
429 => 'Too Many Requests',
|
|
@@ -571,12 +581,13 @@ module Rack
|
|
|
571
581
|
506 => 'Variant Also Negotiates',
|
|
572
582
|
507 => 'Insufficient Storage',
|
|
573
583
|
508 => 'Loop Detected',
|
|
584
|
+
509 => 'Bandwidth Limit Exceeded',
|
|
574
585
|
510 => 'Not Extended',
|
|
575
586
|
511 => 'Network Authentication Required'
|
|
576
587
|
}
|
|
577
588
|
|
|
578
589
|
# Responses with HTTP status codes that should not have an entity body
|
|
579
|
-
STATUS_WITH_NO_ENTITY_BODY =
|
|
590
|
+
STATUS_WITH_NO_ENTITY_BODY = Hash[((100..199).to_a << 204 << 304).product([true])]
|
|
580
591
|
|
|
581
592
|
SYMBOL_TO_STATUS_CODE = Hash[*HTTP_STATUS_CODES.map { |code, message|
|
|
582
593
|
[message.downcase.gsub(/\s|-|'/, '_').to_sym, code]
|
|
@@ -584,12 +595,11 @@ module Rack
|
|
|
584
595
|
|
|
585
596
|
def status_code(status)
|
|
586
597
|
if status.is_a?(Symbol)
|
|
587
|
-
SYMBOL_TO_STATUS_CODE
|
|
598
|
+
SYMBOL_TO_STATUS_CODE.fetch(status) { raise ArgumentError, "Unrecognized status code #{status.inspect}" }
|
|
588
599
|
else
|
|
589
600
|
status.to_i
|
|
590
601
|
end
|
|
591
602
|
end
|
|
592
|
-
module_function :status_code
|
|
593
603
|
|
|
594
604
|
PATH_SEPS = Regexp.union(*[::File::SEPARATOR, ::File::ALT_SEPARATOR].compact)
|
|
595
605
|
|
|
@@ -603,18 +613,16 @@ module Rack
|
|
|
603
613
|
part == '..' ? clean.pop : clean << part
|
|
604
614
|
end
|
|
605
615
|
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
616
|
+
clean_path = clean.join(::File::SEPARATOR)
|
|
617
|
+
clean_path.prepend("/") if parts.empty? || parts.first.empty?
|
|
618
|
+
clean_path
|
|
609
619
|
end
|
|
610
|
-
module_function :clean_path_info
|
|
611
620
|
|
|
612
|
-
NULL_BYTE = "\0"
|
|
621
|
+
NULL_BYTE = "\0"
|
|
613
622
|
|
|
614
623
|
def valid_path?(path)
|
|
615
624
|
path.valid_encoding? && !path.include?(NULL_BYTE)
|
|
616
625
|
end
|
|
617
|
-
module_function :valid_path?
|
|
618
626
|
|
|
619
627
|
end
|
|
620
628
|
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.7"
|
|
24
|
+
|
|
25
|
+
# Return the Rack release as a dotted string.
|
|
26
|
+
def self.release
|
|
27
|
+
RELEASE
|
|
28
|
+
end
|
|
29
|
+
end
|