rack 3.0.0 → 3.1.0
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 +125 -2
- data/CONTRIBUTING.md +11 -9
- data/README.md +55 -20
- data/SPEC.rdoc +38 -13
- data/lib/rack/auth/basic.rb +1 -4
- data/lib/rack/bad_request.rb +8 -0
- data/lib/rack/body_proxy.rb +18 -2
- data/lib/rack/builder.rb +55 -40
- data/lib/rack/cascade.rb +0 -3
- data/lib/rack/constants.rb +5 -2
- data/lib/rack/content_length.rb +0 -1
- data/lib/rack/headers.rb +98 -14
- data/lib/rack/lint.rb +126 -38
- data/lib/rack/logger.rb +2 -1
- data/lib/rack/media_type.rb +9 -4
- data/lib/rack/method_override.rb +2 -2
- data/lib/rack/mime.rb +6 -5
- data/lib/rack/mock_request.rb +19 -14
- data/lib/rack/mock_response.rb +12 -14
- data/lib/rack/multipart/parser.rb +147 -66
- data/lib/rack/multipart.rb +34 -1
- data/lib/rack/query_parser.rb +19 -72
- data/lib/rack/request.rb +65 -38
- data/lib/rack/response.rb +27 -22
- data/lib/rack/sendfile.rb +1 -1
- data/lib/rack/show_exceptions.rb +6 -2
- data/lib/rack/urlmap.rb +1 -1
- data/lib/rack/utils.rb +91 -114
- data/lib/rack/version.rb +1 -14
- data/lib/rack.rb +11 -16
- metadata +4 -10
- data/lib/rack/auth/digest/md5.rb +0 -1
- data/lib/rack/auth/digest/nonce.rb +0 -1
- data/lib/rack/auth/digest/params.rb +0 -1
- data/lib/rack/auth/digest/request.rb +0 -1
- data/lib/rack/auth/digest.rb +0 -256
- data/lib/rack/chunked.rb +0 -120
- data/lib/rack/file.rb +0 -9
data/lib/rack/response.rb
CHANGED
|
@@ -25,25 +25,17 @@ module Rack
|
|
|
25
25
|
self.new(body, status, headers)
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
-
CHUNKED = 'chunked'
|
|
29
28
|
STATUS_WITH_NO_ENTITY_BODY = Utils::STATUS_WITH_NO_ENTITY_BODY
|
|
30
29
|
|
|
31
30
|
attr_accessor :length, :status, :body
|
|
32
31
|
attr_reader :headers
|
|
33
32
|
|
|
34
|
-
# Deprecated, use headers instead.
|
|
35
|
-
def header
|
|
36
|
-
warn 'Rack::Response#header is deprecated and will be removed in Rack 3.1', uplevel: 1
|
|
37
|
-
|
|
38
|
-
headers
|
|
39
|
-
end
|
|
40
|
-
|
|
41
33
|
# Initialize the response object with the specified +body+, +status+
|
|
42
34
|
# and +headers+.
|
|
43
35
|
#
|
|
44
36
|
# If the +body+ is +nil+, construct an empty response object with internal
|
|
45
37
|
# buffering.
|
|
46
|
-
#
|
|
38
|
+
#
|
|
47
39
|
# If the +body+ responds to +to_str+, assume it's a string-like object and
|
|
48
40
|
# construct a buffered response object containing using that string as the
|
|
49
41
|
# initial contents of the buffer.
|
|
@@ -62,7 +54,7 @@ module Rack
|
|
|
62
54
|
@status = status.to_i
|
|
63
55
|
|
|
64
56
|
unless headers.is_a?(Hash)
|
|
65
|
-
|
|
57
|
+
raise ArgumentError, "Headers must be a Hash!"
|
|
66
58
|
end
|
|
67
59
|
|
|
68
60
|
@headers = Headers.new
|
|
@@ -97,21 +89,26 @@ module Rack
|
|
|
97
89
|
self.status = status
|
|
98
90
|
self.location = target
|
|
99
91
|
end
|
|
100
|
-
|
|
101
|
-
def
|
|
102
|
-
|
|
92
|
+
|
|
93
|
+
def no_entity_body?
|
|
94
|
+
# The response body is an enumerable body and it is not allowed to have an entity body.
|
|
95
|
+
@body.respond_to?(:each) && STATUS_WITH_NO_ENTITY_BODY[@status]
|
|
103
96
|
end
|
|
104
97
|
|
|
105
98
|
# Generate a response array consistent with the requirements of the SPEC.
|
|
106
99
|
# @return [Array] a 3-tuple suitable of `[status, headers, body]`
|
|
107
100
|
# which is suitable to be returned from the middleware `#call(env)` method.
|
|
108
101
|
def finish(&block)
|
|
109
|
-
if
|
|
102
|
+
if no_entity_body?
|
|
110
103
|
delete_header CONTENT_TYPE
|
|
111
104
|
delete_header CONTENT_LENGTH
|
|
112
105
|
close
|
|
113
106
|
return [@status, @headers, []]
|
|
114
107
|
else
|
|
108
|
+
if @length && @length > 0
|
|
109
|
+
set_header CONTENT_LENGTH, @length.to_s
|
|
110
|
+
end
|
|
111
|
+
|
|
115
112
|
if block_given?
|
|
116
113
|
@block = block
|
|
117
114
|
return [@status, @headers, self]
|
|
@@ -315,27 +312,37 @@ module Rack
|
|
|
315
312
|
|
|
316
313
|
protected
|
|
317
314
|
|
|
315
|
+
# Convert the body of this response into an internally buffered Array if possible.
|
|
316
|
+
#
|
|
317
|
+
# `@buffered` is a ternary value which indicates whether the body is buffered. It can be:
|
|
318
|
+
# * `nil` - The body has not been buffered yet.
|
|
319
|
+
# * `true` - The body is buffered as an Array instance.
|
|
320
|
+
# * `false` - The body is not buffered and cannot be buffered.
|
|
321
|
+
#
|
|
322
|
+
# @return [Boolean] whether the body is buffered as an Array instance.
|
|
318
323
|
def buffered_body!
|
|
319
324
|
if @buffered.nil?
|
|
320
325
|
if @body.is_a?(Array)
|
|
321
326
|
# The user supplied body was an array:
|
|
322
327
|
@body = @body.compact
|
|
323
|
-
@body.
|
|
324
|
-
|
|
325
|
-
end
|
|
328
|
+
@length = @body.sum{|part| part.bytesize}
|
|
329
|
+
@buffered = true
|
|
326
330
|
elsif @body.respond_to?(:each)
|
|
327
331
|
# Turn the user supplied body into a buffered array:
|
|
328
332
|
body = @body
|
|
329
333
|
@body = Array.new
|
|
334
|
+
@length = 0
|
|
330
335
|
|
|
331
336
|
body.each do |part|
|
|
332
337
|
@writer.call(part.to_s)
|
|
333
338
|
end
|
|
334
339
|
|
|
335
340
|
body.close if body.respond_to?(:close)
|
|
336
|
-
|
|
341
|
+
|
|
342
|
+
# We have converted the body into an Array:
|
|
337
343
|
@buffered = true
|
|
338
344
|
else
|
|
345
|
+
# We don't know how to buffer the user-supplied body:
|
|
339
346
|
@buffered = false
|
|
340
347
|
end
|
|
341
348
|
end
|
|
@@ -344,12 +351,10 @@ module Rack
|
|
|
344
351
|
end
|
|
345
352
|
|
|
346
353
|
def append(chunk)
|
|
354
|
+
chunk = chunk.dup unless chunk.frozen?
|
|
347
355
|
@body << chunk
|
|
348
356
|
|
|
349
|
-
|
|
350
|
-
@length += chunk.bytesize
|
|
351
|
-
set_header(CONTENT_LENGTH, @length.to_s)
|
|
352
|
-
end
|
|
357
|
+
@length += chunk.bytesize
|
|
353
358
|
|
|
354
359
|
return chunk
|
|
355
360
|
end
|
data/lib/rack/sendfile.rb
CHANGED
data/lib/rack/show_exceptions.rb
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'ostruct'
|
|
4
3
|
require 'erb'
|
|
5
4
|
|
|
6
5
|
require_relative 'constants'
|
|
@@ -19,6 +18,11 @@ module Rack
|
|
|
19
18
|
class ShowExceptions
|
|
20
19
|
CONTEXT = 7
|
|
21
20
|
|
|
21
|
+
Frame = Struct.new(:filename, :lineno, :function,
|
|
22
|
+
:pre_context_lineno, :pre_context,
|
|
23
|
+
:context_line, :post_context_lineno,
|
|
24
|
+
:post_context)
|
|
25
|
+
|
|
22
26
|
def initialize(app)
|
|
23
27
|
@app = app
|
|
24
28
|
end
|
|
@@ -79,7 +83,7 @@ module Rack
|
|
|
79
83
|
# This double assignment is to prevent an "unused variable" warning.
|
|
80
84
|
# Yes, it is dumb, but I don't like Ruby yelling at me.
|
|
81
85
|
frames = frames = exception.backtrace.map { |line|
|
|
82
|
-
frame =
|
|
86
|
+
frame = Frame.new
|
|
83
87
|
if line =~ /(.*?):(\d+)(:in `(.*)')?/
|
|
84
88
|
frame.filename = $1
|
|
85
89
|
frame.lineno = $2.to_i
|
data/lib/rack/urlmap.rb
CHANGED
|
@@ -37,7 +37,7 @@ module Rack
|
|
|
37
37
|
end
|
|
38
38
|
|
|
39
39
|
location = location.chomp('/')
|
|
40
|
-
match = Regexp.new("^#{Regexp.quote(location).gsub('/', '/+')}(.*)",
|
|
40
|
+
match = Regexp.new("^#{Regexp.quote(location).gsub('/', '/+')}(.*)", Regexp::NOENCODING)
|
|
41
41
|
|
|
42
42
|
[host, location, match, app]
|
|
43
43
|
}.sort_by do |(host, location, _, _)|
|
data/lib/rack/utils.rb
CHANGED
|
@@ -6,6 +6,7 @@ require 'fileutils'
|
|
|
6
6
|
require 'set'
|
|
7
7
|
require 'tempfile'
|
|
8
8
|
require 'time'
|
|
9
|
+
require 'cgi/escape'
|
|
9
10
|
|
|
10
11
|
require_relative 'query_parser'
|
|
11
12
|
require_relative 'mime'
|
|
@@ -58,13 +59,24 @@ module Rack
|
|
|
58
59
|
end
|
|
59
60
|
|
|
60
61
|
class << self
|
|
61
|
-
attr_accessor :
|
|
62
|
+
attr_accessor :multipart_total_part_limit
|
|
63
|
+
|
|
64
|
+
attr_accessor :multipart_file_limit
|
|
65
|
+
|
|
66
|
+
# multipart_part_limit is the original name of multipart_file_limit, but
|
|
67
|
+
# the limit only counts parts with filenames.
|
|
68
|
+
alias multipart_part_limit multipart_file_limit
|
|
69
|
+
alias multipart_part_limit= multipart_file_limit=
|
|
62
70
|
end
|
|
63
71
|
|
|
64
|
-
# The maximum number of parts a request can contain. Accepting too
|
|
65
|
-
# can lead to the server running out of file handles.
|
|
72
|
+
# The maximum number of file parts a request can contain. Accepting too
|
|
73
|
+
# many parts can lead to the server running out of file handles.
|
|
66
74
|
# Set to `0` for no limit.
|
|
67
|
-
self.
|
|
75
|
+
self.multipart_file_limit = (ENV['RACK_MULTIPART_PART_LIMIT'] || ENV['RACK_MULTIPART_FILE_LIMIT'] || 128).to_i
|
|
76
|
+
|
|
77
|
+
# The maximum total number of parts a request can contain. Accepting too
|
|
78
|
+
# many can lead to excessive memory use and parsing time.
|
|
79
|
+
self.multipart_total_part_limit = (ENV['RACK_MULTIPART_TOTAL_PART_LIMIT'] || 4096).to_i
|
|
68
80
|
|
|
69
81
|
def self.param_depth_limit
|
|
70
82
|
default_query_parser.param_depth_limit
|
|
@@ -74,15 +86,6 @@ module Rack
|
|
|
74
86
|
self.default_query_parser = self.default_query_parser.new_depth_limit(v)
|
|
75
87
|
end
|
|
76
88
|
|
|
77
|
-
def self.key_space_limit
|
|
78
|
-
warn("`Rack::Utils.key_space_limit` is deprecated as this value no longer has an effect. It will be removed in Rack 3.1", uplevel: 1)
|
|
79
|
-
65536
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
def self.key_space_limit=(v)
|
|
83
|
-
warn("`Rack::Utils.key_space_limit=` is deprecated and no longer has an effect. It will be removed in Rack 3.1", uplevel: 1)
|
|
84
|
-
end
|
|
85
|
-
|
|
86
89
|
if defined?(Process::CLOCK_MONOTONIC)
|
|
87
90
|
def clock_time
|
|
88
91
|
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
@@ -121,19 +124,19 @@ module Rack
|
|
|
121
124
|
}.join("&")
|
|
122
125
|
when Hash
|
|
123
126
|
value.map { |k, v|
|
|
124
|
-
build_nested_query(v, prefix ? "#{prefix}[#{
|
|
127
|
+
build_nested_query(v, prefix ? "#{prefix}[#{k}]" : k)
|
|
125
128
|
}.delete_if(&:empty?).join('&')
|
|
126
129
|
when nil
|
|
127
|
-
prefix
|
|
130
|
+
escape(prefix)
|
|
128
131
|
else
|
|
129
132
|
raise ArgumentError, "value must be a Hash" if prefix.nil?
|
|
130
|
-
"#{prefix}=#{escape(value)}"
|
|
133
|
+
"#{escape(prefix)}=#{escape(value)}"
|
|
131
134
|
end
|
|
132
135
|
end
|
|
133
136
|
|
|
134
137
|
def q_values(q_value_header)
|
|
135
|
-
q_value_header.to_s.split(
|
|
136
|
-
value, parameters = part.split(
|
|
138
|
+
q_value_header.to_s.split(',').map do |part|
|
|
139
|
+
value, parameters = part.split(';', 2).map(&:strip)
|
|
137
140
|
quality = 1.0
|
|
138
141
|
if parameters && (md = /\Aq=([\d.]+)/.match(parameters))
|
|
139
142
|
quality = md[1].to_f
|
|
@@ -146,9 +149,10 @@ module Rack
|
|
|
146
149
|
return nil unless forwarded_header
|
|
147
150
|
forwarded_header = forwarded_header.to_s.gsub("\n", ";")
|
|
148
151
|
|
|
149
|
-
forwarded_header.split(
|
|
150
|
-
field.split(
|
|
151
|
-
|
|
152
|
+
forwarded_header.split(';').each_with_object({}) do |field, values|
|
|
153
|
+
field.split(',').each do |pair|
|
|
154
|
+
pair = pair.split('=').map(&:strip).join('=')
|
|
155
|
+
return nil unless pair =~ /\A(by|for|host|proto)="?([^"]+)"?\Z/i
|
|
152
156
|
(values[$1.downcase.to_sym] ||= []) << $2
|
|
153
157
|
end
|
|
154
158
|
end
|
|
@@ -172,21 +176,8 @@ module Rack
|
|
|
172
176
|
matches&.first
|
|
173
177
|
end
|
|
174
178
|
|
|
175
|
-
ESCAPE_HTML = {
|
|
176
|
-
"&" => "&",
|
|
177
|
-
"<" => "<",
|
|
178
|
-
">" => ">",
|
|
179
|
-
"'" => "'",
|
|
180
|
-
'"' => """,
|
|
181
|
-
"/" => "/"
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
ESCAPE_HTML_PATTERN = Regexp.union(*ESCAPE_HTML.keys)
|
|
185
|
-
|
|
186
179
|
# Escape ampersands, brackets and quotes to their HTML/XML entities.
|
|
187
|
-
|
|
188
|
-
string.to_s.gsub(ESCAPE_HTML_PATTERN){|c| ESCAPE_HTML[c] }
|
|
189
|
-
end
|
|
180
|
+
define_method(:escape_html, CGI.method(:escapeHTML))
|
|
190
181
|
|
|
191
182
|
def select_best_encoding(available_encodings, accept_encoding)
|
|
192
183
|
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
|
|
@@ -240,21 +231,6 @@ module Rack
|
|
|
240
231
|
end
|
|
241
232
|
end
|
|
242
233
|
|
|
243
|
-
def add_cookie_to_header(header, key, value)
|
|
244
|
-
warn("add_cookie_to_header is deprecated and will be removed in Rack 3.1", uplevel: 1)
|
|
245
|
-
|
|
246
|
-
case header
|
|
247
|
-
when nil, ''
|
|
248
|
-
return set_cookie_header(key, value)
|
|
249
|
-
when String
|
|
250
|
-
[header, set_cookie_header(key, value)]
|
|
251
|
-
when Array
|
|
252
|
-
header + [set_cookie_header(key, value)]
|
|
253
|
-
else
|
|
254
|
-
raise ArgumentError, "Unrecognized cookie header value. Expected String, Array, or nil, got #{header.inspect}"
|
|
255
|
-
end
|
|
256
|
-
end
|
|
257
|
-
|
|
258
234
|
# :call-seq:
|
|
259
235
|
# parse_cookies(env) -> hash
|
|
260
236
|
#
|
|
@@ -268,6 +244,20 @@ module Rack
|
|
|
268
244
|
parse_cookies_header env[HTTP_COOKIE]
|
|
269
245
|
end
|
|
270
246
|
|
|
247
|
+
# A valid cookie key according to RFC2616.
|
|
248
|
+
# A <cookie-name> can be any US-ASCII characters, except control characters, spaces, or tabs. It also must not contain a separator character like the following: ( ) < > @ , ; : \ " / [ ] ? = { }.
|
|
249
|
+
VALID_COOKIE_KEY = /\A[!#$%&'*+\-\.\^_`|~0-9a-zA-Z]+\z/.freeze
|
|
250
|
+
private_constant :VALID_COOKIE_KEY
|
|
251
|
+
|
|
252
|
+
private def escape_cookie_key(key)
|
|
253
|
+
if key =~ VALID_COOKIE_KEY
|
|
254
|
+
key
|
|
255
|
+
else
|
|
256
|
+
warn "Cookie key #{key.inspect} is not valid according to RFC2616; it will be escaped. This behaviour is deprecated and will be removed in a future version of Rack.", uplevel: 2
|
|
257
|
+
escape(key)
|
|
258
|
+
end
|
|
259
|
+
end
|
|
260
|
+
|
|
271
261
|
# :call-seq:
|
|
272
262
|
# set_cookie_header(key, value) -> encoded string
|
|
273
263
|
#
|
|
@@ -278,7 +268,7 @@ module Rack
|
|
|
278
268
|
# If the cookie +value+ is an instance of +Hash+, it considers the following
|
|
279
269
|
# cookie attribute keys: +domain+, +max_age+, +expires+ (must be instance
|
|
280
270
|
# of +Time+), +secure+, +http_only+, +same_site+ and +value+. For more
|
|
281
|
-
# details about the interpretation of these fields, consult
|
|
271
|
+
# details about the interpretation of these fields, consult
|
|
282
272
|
# [RFC6265 Section 5.2](https://datatracker.ietf.org/doc/html/rfc6265#section-5.2).
|
|
283
273
|
#
|
|
284
274
|
# An extra cookie attribute +escape_key+ can be provided to control whether
|
|
@@ -294,7 +284,7 @@ module Rack
|
|
|
294
284
|
def set_cookie_header(key, value)
|
|
295
285
|
case value
|
|
296
286
|
when Hash
|
|
297
|
-
key =
|
|
287
|
+
key = escape_cookie_key(key) unless value[:escape_key] == false
|
|
298
288
|
domain = "; domain=#{value[:domain]}" if value[:domain]
|
|
299
289
|
path = "; path=#{value[:path]}" if value[:path]
|
|
300
290
|
max_age = "; max-age=#{value[:max_age]}" if value[:max_age]
|
|
@@ -306,23 +296,24 @@ module Rack
|
|
|
306
296
|
when false, nil
|
|
307
297
|
nil
|
|
308
298
|
when :none, 'None', :None
|
|
309
|
-
';
|
|
299
|
+
'; samesite=none'
|
|
310
300
|
when :lax, 'Lax', :Lax
|
|
311
|
-
';
|
|
301
|
+
'; samesite=lax'
|
|
312
302
|
when true, :strict, 'Strict', :Strict
|
|
313
|
-
';
|
|
303
|
+
'; samesite=strict'
|
|
314
304
|
else
|
|
315
|
-
raise ArgumentError, "Invalid
|
|
305
|
+
raise ArgumentError, "Invalid :same_site value: #{value[:same_site].inspect}"
|
|
316
306
|
end
|
|
307
|
+
partitioned = "; partitioned" if value[:partitioned]
|
|
317
308
|
value = value[:value]
|
|
318
309
|
else
|
|
319
|
-
key =
|
|
310
|
+
key = escape_cookie_key(key)
|
|
320
311
|
end
|
|
321
312
|
|
|
322
313
|
value = [value] unless Array === value
|
|
323
314
|
|
|
324
315
|
return "#{key}=#{value.map { |v| escape v }.join('&')}#{domain}" \
|
|
325
|
-
"#{path}#{max_age}#{expires}#{secure}#{httponly}#{same_site}"
|
|
316
|
+
"#{path}#{max_age}#{expires}#{secure}#{httponly}#{same_site}#{partitioned}"
|
|
326
317
|
end
|
|
327
318
|
|
|
328
319
|
# :call-seq:
|
|
@@ -363,24 +354,12 @@ module Rack
|
|
|
363
354
|
set_cookie_header(key, value.merge(max_age: '0', expires: Time.at(0), value: ''))
|
|
364
355
|
end
|
|
365
356
|
|
|
366
|
-
def make_delete_cookie_header(header, key, value)
|
|
367
|
-
warn("make_delete_cookie_header is deprecated and will be removed in Rack 3.1, use delete_set_cookie_header! instead", uplevel: 1)
|
|
368
|
-
|
|
369
|
-
delete_set_cookie_header!(header, key, value)
|
|
370
|
-
end
|
|
371
|
-
|
|
372
357
|
def delete_cookie_header!(headers, key, value = {})
|
|
373
358
|
headers[SET_COOKIE] = delete_set_cookie_header!(headers[SET_COOKIE], key, value)
|
|
374
359
|
|
|
375
360
|
return nil
|
|
376
361
|
end
|
|
377
362
|
|
|
378
|
-
def add_remove_cookie_to_header(header, key, value = {})
|
|
379
|
-
warn("add_remove_cookie_to_header is deprecated and will be removed in Rack 3.1, use delete_set_cookie_header! instead", uplevel: 1)
|
|
380
|
-
|
|
381
|
-
delete_set_cookie_header!(header, key, value)
|
|
382
|
-
end
|
|
383
|
-
|
|
384
363
|
# :call-seq:
|
|
385
364
|
# delete_set_cookie_header!(header, key, value = {}) -> header value
|
|
386
365
|
#
|
|
@@ -423,20 +402,23 @@ module Rack
|
|
|
423
402
|
|
|
424
403
|
def get_byte_ranges(http_range, size)
|
|
425
404
|
# See <http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35>
|
|
405
|
+
# Ignore Range when file size is 0 to avoid a 416 error.
|
|
406
|
+
return nil if size.zero?
|
|
426
407
|
return nil unless http_range && http_range =~ /bytes=([^;]+)/
|
|
427
408
|
ranges = []
|
|
428
409
|
$1.split(/,\s*/).each do |range_spec|
|
|
429
|
-
return nil
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
410
|
+
return nil unless range_spec.include?('-')
|
|
411
|
+
range = range_spec.split('-')
|
|
412
|
+
r0, r1 = range[0], range[1]
|
|
413
|
+
if r0.nil? || r0.empty?
|
|
414
|
+
return nil if r1.nil?
|
|
433
415
|
# suffix-byte-range-spec, represents trailing suffix of file
|
|
434
416
|
r0 = size - r1.to_i
|
|
435
417
|
r0 = 0 if r0 < 0
|
|
436
418
|
r1 = size - 1
|
|
437
419
|
else
|
|
438
420
|
r0 = r0.to_i
|
|
439
|
-
if r1.
|
|
421
|
+
if r1.nil?
|
|
440
422
|
r1 = size - 1
|
|
441
423
|
else
|
|
442
424
|
r1 = r1.to_i
|
|
@@ -446,6 +428,9 @@ module Rack
|
|
|
446
428
|
end
|
|
447
429
|
ranges << (r0..r1) if r0 <= r1
|
|
448
430
|
end
|
|
431
|
+
|
|
432
|
+
return [] if ranges.map(&:size).sum > size
|
|
433
|
+
|
|
449
434
|
ranges
|
|
450
435
|
end
|
|
451
436
|
|
|
@@ -501,39 +486,12 @@ module Rack
|
|
|
501
486
|
end
|
|
502
487
|
end
|
|
503
488
|
|
|
504
|
-
# A wrapper around Headers
|
|
505
|
-
# header when set.
|
|
506
|
-
#
|
|
507
|
-
# @api private
|
|
508
|
-
class HeaderHash < Hash # :nodoc:
|
|
509
|
-
def self.[](headers)
|
|
510
|
-
warn "Rack::Utils::HeaderHash is deprecated and will be removed in Rack 3.1, switch to Rack::Headers", uplevel: 1
|
|
511
|
-
if headers.is_a?(Headers) && !headers.frozen?
|
|
512
|
-
return headers
|
|
513
|
-
end
|
|
514
|
-
|
|
515
|
-
new_headers = Headers.new
|
|
516
|
-
headers.each{|k,v| new_headers[k] = v}
|
|
517
|
-
new_headers
|
|
518
|
-
end
|
|
519
|
-
|
|
520
|
-
def self.new(hash = {})
|
|
521
|
-
warn "Rack::Utils::HeaderHash is deprecated and will be removed in Rack 3.1, switch to Rack::Headers", uplevel: 1
|
|
522
|
-
headers = Headers.new
|
|
523
|
-
hash.each{|k,v| headers[k] = v}
|
|
524
|
-
headers
|
|
525
|
-
end
|
|
526
|
-
|
|
527
|
-
def self.allocate
|
|
528
|
-
raise TypeError, "cannot allocate HeaderHash"
|
|
529
|
-
end
|
|
530
|
-
end
|
|
531
|
-
|
|
532
489
|
# Every standard HTTP code mapped to the appropriate message.
|
|
533
490
|
# Generated with:
|
|
534
|
-
# curl -s https://www.iana.org/assignments/http-status-codes/http-status-codes-1.csv
|
|
535
|
-
# ruby -
|
|
536
|
-
#
|
|
491
|
+
# curl -s https://www.iana.org/assignments/http-status-codes/http-status-codes-1.csv \
|
|
492
|
+
# | ruby -rcsv -e "puts CSV.parse(STDIN, headers: true) \
|
|
493
|
+
# .reject {|v| v['Description'] == 'Unassigned' or v['Description'].include? '(' } \
|
|
494
|
+
# .map {|v| %Q/#{v['Value']} => '#{v['Description']}'/ }.join(','+?\n)"
|
|
537
495
|
HTTP_STATUS_CODES = {
|
|
538
496
|
100 => 'Continue',
|
|
539
497
|
101 => 'Switching Protocols',
|
|
@@ -555,7 +513,6 @@ module Rack
|
|
|
555
513
|
303 => 'See Other',
|
|
556
514
|
304 => 'Not Modified',
|
|
557
515
|
305 => 'Use Proxy',
|
|
558
|
-
306 => '(Unused)',
|
|
559
516
|
307 => 'Temporary Redirect',
|
|
560
517
|
308 => 'Permanent Redirect',
|
|
561
518
|
400 => 'Bad Request',
|
|
@@ -571,13 +528,13 @@ module Rack
|
|
|
571
528
|
410 => 'Gone',
|
|
572
529
|
411 => 'Length Required',
|
|
573
530
|
412 => 'Precondition Failed',
|
|
574
|
-
413 => '
|
|
531
|
+
413 => 'Content Too Large',
|
|
575
532
|
414 => 'URI Too Long',
|
|
576
533
|
415 => 'Unsupported Media Type',
|
|
577
534
|
416 => 'Range Not Satisfiable',
|
|
578
535
|
417 => 'Expectation Failed',
|
|
579
536
|
421 => 'Misdirected Request',
|
|
580
|
-
422 => 'Unprocessable
|
|
537
|
+
422 => 'Unprocessable Content',
|
|
581
538
|
423 => 'Locked',
|
|
582
539
|
424 => 'Failed Dependency',
|
|
583
540
|
425 => 'Too Early',
|
|
@@ -585,7 +542,7 @@ module Rack
|
|
|
585
542
|
428 => 'Precondition Required',
|
|
586
543
|
429 => 'Too Many Requests',
|
|
587
544
|
431 => 'Request Header Fields Too Large',
|
|
588
|
-
451 => 'Unavailable
|
|
545
|
+
451 => 'Unavailable For Legal Reasons',
|
|
589
546
|
500 => 'Internal Server Error',
|
|
590
547
|
501 => 'Not Implemented',
|
|
591
548
|
502 => 'Bad Gateway',
|
|
@@ -595,8 +552,6 @@ module Rack
|
|
|
595
552
|
506 => 'Variant Also Negotiates',
|
|
596
553
|
507 => 'Insufficient Storage',
|
|
597
554
|
508 => 'Loop Detected',
|
|
598
|
-
509 => 'Bandwidth Limit Exceeded',
|
|
599
|
-
510 => 'Not Extended',
|
|
600
555
|
511 => 'Network Authentication Required'
|
|
601
556
|
}
|
|
602
557
|
|
|
@@ -604,12 +559,34 @@ module Rack
|
|
|
604
559
|
STATUS_WITH_NO_ENTITY_BODY = Hash[((100..199).to_a << 204 << 304).product([true])]
|
|
605
560
|
|
|
606
561
|
SYMBOL_TO_STATUS_CODE = Hash[*HTTP_STATUS_CODES.map { |code, message|
|
|
607
|
-
[message.downcase.gsub(/\s
|
|
562
|
+
[message.downcase.gsub(/\s|-/, '_').to_sym, code]
|
|
608
563
|
}.flatten]
|
|
609
564
|
|
|
565
|
+
OBSOLETE_SYMBOLS_TO_STATUS_CODES = {
|
|
566
|
+
payload_too_large: 413,
|
|
567
|
+
unprocessable_entity: 422,
|
|
568
|
+
bandwidth_limit_exceeded: 509,
|
|
569
|
+
not_extended: 510
|
|
570
|
+
}.freeze
|
|
571
|
+
private_constant :OBSOLETE_SYMBOLS_TO_STATUS_CODES
|
|
572
|
+
|
|
573
|
+
OBSOLETE_SYMBOL_MAPPINGS = {
|
|
574
|
+
payload_too_large: :content_too_large,
|
|
575
|
+
unprocessable_entity: :unprocessable_content
|
|
576
|
+
}.freeze
|
|
577
|
+
private_constant :OBSOLETE_SYMBOL_MAPPINGS
|
|
578
|
+
|
|
610
579
|
def status_code(status)
|
|
611
580
|
if status.is_a?(Symbol)
|
|
612
|
-
SYMBOL_TO_STATUS_CODE.fetch(status)
|
|
581
|
+
SYMBOL_TO_STATUS_CODE.fetch(status) do
|
|
582
|
+
fallback_code = OBSOLETE_SYMBOLS_TO_STATUS_CODES.fetch(status) { raise ArgumentError, "Unrecognized status code #{status.inspect}" }
|
|
583
|
+
message = "Status code #{status.inspect} is deprecated and will be removed in a future version of Rack."
|
|
584
|
+
if canonical_symbol = OBSOLETE_SYMBOL_MAPPINGS[status]
|
|
585
|
+
message = "#{message} Please use #{canonical_symbol.inspect} instead."
|
|
586
|
+
end
|
|
587
|
+
warn message, uplevel: 1
|
|
588
|
+
fallback_code
|
|
589
|
+
end
|
|
613
590
|
else
|
|
614
591
|
status.to_i
|
|
615
592
|
end
|
data/lib/rack/version.rb
CHANGED
|
@@ -12,20 +12,7 @@
|
|
|
12
12
|
# so it should be enough just to <tt>require 'rack'</tt> in your code.
|
|
13
13
|
|
|
14
14
|
module Rack
|
|
15
|
-
|
|
16
|
-
VERSION = [1, 3].freeze
|
|
17
|
-
deprecate_constant :VERSION
|
|
18
|
-
|
|
19
|
-
VERSION_STRING = "1.3".freeze
|
|
20
|
-
deprecate_constant :VERSION_STRING
|
|
21
|
-
|
|
22
|
-
# The Rack protocol version number implemented.
|
|
23
|
-
def self.version
|
|
24
|
-
warn "Rack.version is deprecated and will be removed in Rack 3.1!", uplevel: 1
|
|
25
|
-
VERSION
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
RELEASE = "3.0.0"
|
|
15
|
+
RELEASE = "3.1.0"
|
|
29
16
|
|
|
30
17
|
# Return the Rack release as a dotted string.
|
|
31
18
|
def self.release
|
data/lib/rack.rb
CHANGED
|
@@ -15,23 +15,21 @@ require_relative 'rack/version'
|
|
|
15
15
|
require_relative 'rack/constants'
|
|
16
16
|
|
|
17
17
|
module Rack
|
|
18
|
-
autoload :
|
|
18
|
+
autoload :BadRequest, "rack/bad_request"
|
|
19
19
|
autoload :BodyProxy, "rack/body_proxy"
|
|
20
|
+
autoload :Builder, "rack/builder"
|
|
20
21
|
autoload :Cascade, "rack/cascade"
|
|
21
|
-
autoload :Chunked, "rack/chunked"
|
|
22
22
|
autoload :CommonLogger, "rack/common_logger"
|
|
23
23
|
autoload :ConditionalGet, "rack/conditional_get"
|
|
24
24
|
autoload :Config, "rack/config"
|
|
25
25
|
autoload :ContentLength, "rack/content_length"
|
|
26
26
|
autoload :ContentType, "rack/content_type"
|
|
27
|
+
autoload :Deflater, "rack/deflater"
|
|
28
|
+
autoload :Directory, "rack/directory"
|
|
27
29
|
autoload :ETag, "rack/etag"
|
|
28
30
|
autoload :Events, "rack/events"
|
|
29
|
-
autoload :File, "rack/file"
|
|
30
31
|
autoload :Files, "rack/files"
|
|
31
|
-
autoload :Deflater, "rack/deflater"
|
|
32
|
-
autoload :Directory, "rack/directory"
|
|
33
32
|
autoload :ForwardRequest, "rack/recursive"
|
|
34
|
-
autoload :Handler, "rack/handler"
|
|
35
33
|
autoload :Head, "rack/head"
|
|
36
34
|
autoload :Headers, "rack/headers"
|
|
37
35
|
autoload :Lint, "rack/lint"
|
|
@@ -40,31 +38,28 @@ module Rack
|
|
|
40
38
|
autoload :MediaType, "rack/media_type"
|
|
41
39
|
autoload :MethodOverride, "rack/method_override"
|
|
42
40
|
autoload :Mime, "rack/mime"
|
|
41
|
+
autoload :MockRequest, "rack/mock_request"
|
|
42
|
+
autoload :MockResponse, "rack/mock_response"
|
|
43
|
+
autoload :Multipart, "rack/multipart"
|
|
43
44
|
autoload :NullLogger, "rack/null_logger"
|
|
45
|
+
autoload :QueryParser, "rack/query_parser"
|
|
44
46
|
autoload :Recursive, "rack/recursive"
|
|
45
47
|
autoload :Reloader, "rack/reloader"
|
|
48
|
+
autoload :Request, "rack/request"
|
|
49
|
+
autoload :Response, "rack/response"
|
|
46
50
|
autoload :RewindableInput, "rack/rewindable_input"
|
|
47
51
|
autoload :Runtime, "rack/runtime"
|
|
48
52
|
autoload :Sendfile, "rack/sendfile"
|
|
49
|
-
autoload :Server, "rack/server"
|
|
50
53
|
autoload :ShowExceptions, "rack/show_exceptions"
|
|
51
54
|
autoload :ShowStatus, "rack/show_status"
|
|
52
55
|
autoload :Static, "rack/static"
|
|
53
56
|
autoload :TempfileReaper, "rack/tempfile_reaper"
|
|
54
57
|
autoload :URLMap, "rack/urlmap"
|
|
55
58
|
autoload :Utils, "rack/utils"
|
|
56
|
-
autoload :Multipart, "rack/multipart"
|
|
57
|
-
|
|
58
|
-
autoload :MockRequest, "rack/mock_request"
|
|
59
|
-
autoload :MockResponse, "rack/mock_response"
|
|
60
|
-
|
|
61
|
-
autoload :Request, "rack/request"
|
|
62
|
-
autoload :Response, "rack/response"
|
|
63
59
|
|
|
64
60
|
module Auth
|
|
65
61
|
autoload :Basic, "rack/auth/basic"
|
|
66
|
-
autoload :AbstractRequest, "rack/auth/abstract/request"
|
|
67
62
|
autoload :AbstractHandler, "rack/auth/abstract/handler"
|
|
68
|
-
autoload :
|
|
63
|
+
autoload :AbstractRequest, "rack/auth/abstract/request"
|
|
69
64
|
end
|
|
70
65
|
end
|