rack 3.0.8 → 3.1.6

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.
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 'erb'
9
10
 
10
11
  require_relative 'query_parser'
11
12
  require_relative 'mime'
@@ -85,15 +86,6 @@ module Rack
85
86
  self.default_query_parser = self.default_query_parser.new_depth_limit(v)
86
87
  end
87
88
 
88
- def self.key_space_limit
89
- 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)
90
- 65536
91
- end
92
-
93
- def self.key_space_limit=(v)
94
- warn("`Rack::Utils.key_space_limit=` is deprecated and no longer has an effect. It will be removed in Rack 3.1", uplevel: 1)
95
- end
96
-
97
89
  if defined?(Process::CLOCK_MONOTONIC)
98
90
  def clock_time
99
91
  Process.clock_gettime(Process::CLOCK_MONOTONIC)
@@ -143,8 +135,8 @@ module Rack
143
135
  end
144
136
 
145
137
  def q_values(q_value_header)
146
- q_value_header.to_s.split(/\s*,\s*/).map do |part|
147
- value, parameters = part.split(/\s*;\s*/, 2)
138
+ q_value_header.to_s.split(',').map do |part|
139
+ value, parameters = part.split(';', 2).map(&:strip)
148
140
  quality = 1.0
149
141
  if parameters && (md = /\Aq=([\d.]+)/.match(parameters))
150
142
  quality = md[1].to_f
@@ -157,9 +149,10 @@ module Rack
157
149
  return nil unless forwarded_header
158
150
  forwarded_header = forwarded_header.to_s.gsub("\n", ";")
159
151
 
160
- forwarded_header.split(/\s*;\s*/).each_with_object({}) do |field, values|
161
- field.split(/\s*,\s*/).each do |pair|
162
- return nil unless pair =~ /\A\s*(by|for|host|proto)\s*=\s*"?([^"]+)"?\s*\Z/i
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
163
156
  (values[$1.downcase.to_sym] ||= []) << $2
164
157
  end
165
158
  end
@@ -183,20 +176,16 @@ module Rack
183
176
  matches&.first
184
177
  end
185
178
 
186
- ESCAPE_HTML = {
187
- "&" => "&amp;",
188
- "<" => "&lt;",
189
- ">" => "&gt;",
190
- "'" => "&#x27;",
191
- '"' => "&quot;",
192
- "/" => "&#x2F;"
193
- }
194
-
195
- ESCAPE_HTML_PATTERN = Regexp.union(*ESCAPE_HTML.keys)
196
-
197
- # Escape ampersands, brackets and quotes to their HTML/XML entities.
198
- def escape_html(string)
199
- string.to_s.gsub(ESCAPE_HTML_PATTERN){|c| ESCAPE_HTML[c] }
179
+ # Introduced in ERB 4.0. ERB::Escape is an alias for ERB::Utils which
180
+ # doesn't get monkey-patched by rails
181
+ if defined?(ERB::Escape) && ERB::Escape.instance_method(:html_escape)
182
+ define_method(:escape_html, ERB::Escape.instance_method(:html_escape))
183
+ else
184
+ require 'cgi/escape'
185
+ # Escape ampersands, brackets and quotes to their HTML/XML entities.
186
+ def escape_html(string)
187
+ CGI.escapeHTML(string.to_s)
188
+ end
200
189
  end
201
190
 
202
191
  def select_best_encoding(available_encodings, accept_encoding)
@@ -251,21 +240,6 @@ module Rack
251
240
  end
252
241
  end
253
242
 
254
- def add_cookie_to_header(header, key, value)
255
- warn("add_cookie_to_header is deprecated and will be removed in Rack 3.1", uplevel: 1)
256
-
257
- case header
258
- when nil, ''
259
- return set_cookie_header(key, value)
260
- when String
261
- [header, set_cookie_header(key, value)]
262
- when Array
263
- header + [set_cookie_header(key, value)]
264
- else
265
- raise ArgumentError, "Unrecognized cookie header value. Expected String, Array, or nil, got #{header.inspect}"
266
- end
267
- end
268
-
269
243
  # :call-seq:
270
244
  # parse_cookies(env) -> hash
271
245
  #
@@ -279,6 +253,20 @@ module Rack
279
253
  parse_cookies_header env[HTTP_COOKIE]
280
254
  end
281
255
 
256
+ # A valid cookie key according to RFC2616.
257
+ # 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: ( ) < > @ , ; : \ " / [ ] ? = { }.
258
+ VALID_COOKIE_KEY = /\A[!#$%&'*+\-\.\^_`|~0-9a-zA-Z]+\z/.freeze
259
+ private_constant :VALID_COOKIE_KEY
260
+
261
+ private def escape_cookie_key(key)
262
+ if key =~ VALID_COOKIE_KEY
263
+ key
264
+ else
265
+ 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
266
+ escape(key)
267
+ end
268
+ end
269
+
282
270
  # :call-seq:
283
271
  # set_cookie_header(key, value) -> encoded string
284
272
  #
@@ -305,7 +293,7 @@ module Rack
305
293
  def set_cookie_header(key, value)
306
294
  case value
307
295
  when Hash
308
- key = escape(key) unless value[:escape_key] == false
296
+ key = escape_cookie_key(key) unless value[:escape_key] == false
309
297
  domain = "; domain=#{value[:domain]}" if value[:domain]
310
298
  path = "; path=#{value[:path]}" if value[:path]
311
299
  max_age = "; max-age=#{value[:max_age]}" if value[:max_age]
@@ -317,23 +305,24 @@ module Rack
317
305
  when false, nil
318
306
  nil
319
307
  when :none, 'None', :None
320
- '; SameSite=None'
308
+ '; samesite=none'
321
309
  when :lax, 'Lax', :Lax
322
- '; SameSite=Lax'
310
+ '; samesite=lax'
323
311
  when true, :strict, 'Strict', :Strict
324
- '; SameSite=Strict'
312
+ '; samesite=strict'
325
313
  else
326
- raise ArgumentError, "Invalid SameSite value: #{value[:same_site].inspect}"
314
+ raise ArgumentError, "Invalid :same_site value: #{value[:same_site].inspect}"
327
315
  end
316
+ partitioned = "; partitioned" if value[:partitioned]
328
317
  value = value[:value]
329
318
  else
330
- key = escape(key)
319
+ key = escape_cookie_key(key)
331
320
  end
332
321
 
333
322
  value = [value] unless Array === value
334
323
 
335
324
  return "#{key}=#{value.map { |v| escape v }.join('&')}#{domain}" \
336
- "#{path}#{max_age}#{expires}#{secure}#{httponly}#{same_site}"
325
+ "#{path}#{max_age}#{expires}#{secure}#{httponly}#{same_site}#{partitioned}"
337
326
  end
338
327
 
339
328
  # :call-seq:
@@ -374,24 +363,12 @@ module Rack
374
363
  set_cookie_header(key, value.merge(max_age: '0', expires: Time.at(0), value: ''))
375
364
  end
376
365
 
377
- def make_delete_cookie_header(header, key, value)
378
- warn("make_delete_cookie_header is deprecated and will be removed in Rack 3.1, use delete_set_cookie_header! instead", uplevel: 1)
379
-
380
- delete_set_cookie_header!(header, key, value)
381
- end
382
-
383
366
  def delete_cookie_header!(headers, key, value = {})
384
367
  headers[SET_COOKIE] = delete_set_cookie_header!(headers[SET_COOKIE], key, value)
385
368
 
386
369
  return nil
387
370
  end
388
371
 
389
- def add_remove_cookie_to_header(header, key, value = {})
390
- warn("add_remove_cookie_to_header is deprecated and will be removed in Rack 3.1, use delete_set_cookie_header! instead", uplevel: 1)
391
-
392
- delete_set_cookie_header!(header, key, value)
393
- end
394
-
395
372
  # :call-seq:
396
373
  # delete_set_cookie_header!(header, key, value = {}) -> header value
397
374
  #
@@ -434,6 +411,8 @@ module Rack
434
411
 
435
412
  def get_byte_ranges(http_range, size)
436
413
  # See <http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35>
414
+ # Ignore Range when file size is 0 to avoid a 416 error.
415
+ return nil if size.zero?
437
416
  return nil unless http_range && http_range =~ /bytes=([^;]+)/
438
417
  ranges = []
439
418
  $1.split(/,\s*/).each do |range_spec|
@@ -458,6 +437,9 @@ module Rack
458
437
  end
459
438
  ranges << (r0..r1) if r0 <= r1
460
439
  end
440
+
441
+ return [] if ranges.map(&:size).sum > size
442
+
461
443
  ranges
462
444
  end
463
445
 
@@ -513,39 +495,12 @@ module Rack
513
495
  end
514
496
  end
515
497
 
516
- # A wrapper around Headers
517
- # header when set.
518
- #
519
- # @api private
520
- class HeaderHash < Hash # :nodoc:
521
- def self.[](headers)
522
- warn "Rack::Utils::HeaderHash is deprecated and will be removed in Rack 3.1, switch to Rack::Headers", uplevel: 1
523
- if headers.is_a?(Headers) && !headers.frozen?
524
- return headers
525
- end
526
-
527
- new_headers = Headers.new
528
- headers.each{|k,v| new_headers[k] = v}
529
- new_headers
530
- end
531
-
532
- def self.new(hash = {})
533
- warn "Rack::Utils::HeaderHash is deprecated and will be removed in Rack 3.1, switch to Rack::Headers", uplevel: 1
534
- headers = Headers.new
535
- hash.each{|k,v| headers[k] = v}
536
- headers
537
- end
538
-
539
- def self.allocate
540
- raise TypeError, "cannot allocate HeaderHash"
541
- end
542
- end
543
-
544
498
  # Every standard HTTP code mapped to the appropriate message.
545
499
  # Generated with:
546
- # curl -s https://www.iana.org/assignments/http-status-codes/http-status-codes-1.csv | \
547
- # ruby -ne 'm = /^(\d{3}),(?!Unassigned|\(Unused\))([^,]+)/.match($_) and \
548
- # puts "#{m[1]} => \x27#{m[2].strip}\x27,"'
500
+ # curl -s https://www.iana.org/assignments/http-status-codes/http-status-codes-1.csv \
501
+ # | ruby -rcsv -e "puts CSV.parse(STDIN, headers: true) \
502
+ # .reject {|v| v['Description'] == 'Unassigned' or v['Description'].include? '(' } \
503
+ # .map {|v| %Q/#{v['Value']} => '#{v['Description']}'/ }.join(','+?\n)"
549
504
  HTTP_STATUS_CODES = {
550
505
  100 => 'Continue',
551
506
  101 => 'Switching Protocols',
@@ -567,7 +522,6 @@ module Rack
567
522
  303 => 'See Other',
568
523
  304 => 'Not Modified',
569
524
  305 => 'Use Proxy',
570
- 306 => '(Unused)',
571
525
  307 => 'Temporary Redirect',
572
526
  308 => 'Permanent Redirect',
573
527
  400 => 'Bad Request',
@@ -583,13 +537,13 @@ module Rack
583
537
  410 => 'Gone',
584
538
  411 => 'Length Required',
585
539
  412 => 'Precondition Failed',
586
- 413 => 'Payload Too Large',
540
+ 413 => 'Content Too Large',
587
541
  414 => 'URI Too Long',
588
542
  415 => 'Unsupported Media Type',
589
543
  416 => 'Range Not Satisfiable',
590
544
  417 => 'Expectation Failed',
591
545
  421 => 'Misdirected Request',
592
- 422 => 'Unprocessable Entity',
546
+ 422 => 'Unprocessable Content',
593
547
  423 => 'Locked',
594
548
  424 => 'Failed Dependency',
595
549
  425 => 'Too Early',
@@ -597,7 +551,7 @@ module Rack
597
551
  428 => 'Precondition Required',
598
552
  429 => 'Too Many Requests',
599
553
  431 => 'Request Header Fields Too Large',
600
- 451 => 'Unavailable for Legal Reasons',
554
+ 451 => 'Unavailable For Legal Reasons',
601
555
  500 => 'Internal Server Error',
602
556
  501 => 'Not Implemented',
603
557
  502 => 'Bad Gateway',
@@ -607,8 +561,6 @@ module Rack
607
561
  506 => 'Variant Also Negotiates',
608
562
  507 => 'Insufficient Storage',
609
563
  508 => 'Loop Detected',
610
- 509 => 'Bandwidth Limit Exceeded',
611
- 510 => 'Not Extended',
612
564
  511 => 'Network Authentication Required'
613
565
  }
614
566
 
@@ -616,12 +568,36 @@ module Rack
616
568
  STATUS_WITH_NO_ENTITY_BODY = Hash[((100..199).to_a << 204 << 304).product([true])]
617
569
 
618
570
  SYMBOL_TO_STATUS_CODE = Hash[*HTTP_STATUS_CODES.map { |code, message|
619
- [message.downcase.gsub(/\s|-|'/, '_').to_sym, code]
571
+ [message.downcase.gsub(/\s|-/, '_').to_sym, code]
620
572
  }.flatten]
621
573
 
574
+ OBSOLETE_SYMBOLS_TO_STATUS_CODES = {
575
+ payload_too_large: 413,
576
+ unprocessable_entity: 422,
577
+ bandwidth_limit_exceeded: 509,
578
+ not_extended: 510
579
+ }.freeze
580
+ private_constant :OBSOLETE_SYMBOLS_TO_STATUS_CODES
581
+
582
+ OBSOLETE_SYMBOL_MAPPINGS = {
583
+ payload_too_large: :content_too_large,
584
+ unprocessable_entity: :unprocessable_content
585
+ }.freeze
586
+ private_constant :OBSOLETE_SYMBOL_MAPPINGS
587
+
622
588
  def status_code(status)
623
589
  if status.is_a?(Symbol)
624
- SYMBOL_TO_STATUS_CODE.fetch(status) { raise ArgumentError, "Unrecognized status code #{status.inspect}" }
590
+ SYMBOL_TO_STATUS_CODE.fetch(status) do
591
+ fallback_code = OBSOLETE_SYMBOLS_TO_STATUS_CODES.fetch(status) { raise ArgumentError, "Unrecognized status code #{status.inspect}" }
592
+ message = "Status code #{status.inspect} is deprecated and will be removed in a future version of Rack."
593
+ if canonical_symbol = OBSOLETE_SYMBOL_MAPPINGS[status]
594
+ # message = "#{message} Please use #{canonical_symbol.inspect} instead."
595
+ # For now, let's not emit any warning when there is a mapping.
596
+ else
597
+ warn message, uplevel: 3
598
+ end
599
+ fallback_code
600
+ end
625
601
  else
626
602
  status.to_i
627
603
  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
- # The Rack protocol version number implemented.
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.8"
15
+ RELEASE = "3.1.6"
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 :Builder, "rack/builder"
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,32 +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"
44
45
  autoload :QueryParser, "rack/query_parser"
45
46
  autoload :Recursive, "rack/recursive"
46
47
  autoload :Reloader, "rack/reloader"
48
+ autoload :Request, "rack/request"
49
+ autoload :Response, "rack/response"
47
50
  autoload :RewindableInput, "rack/rewindable_input"
48
51
  autoload :Runtime, "rack/runtime"
49
52
  autoload :Sendfile, "rack/sendfile"
50
- autoload :Server, "rack/server"
51
53
  autoload :ShowExceptions, "rack/show_exceptions"
52
54
  autoload :ShowStatus, "rack/show_status"
53
55
  autoload :Static, "rack/static"
54
56
  autoload :TempfileReaper, "rack/tempfile_reaper"
55
57
  autoload :URLMap, "rack/urlmap"
56
58
  autoload :Utils, "rack/utils"
57
- autoload :Multipart, "rack/multipart"
58
-
59
- autoload :MockRequest, "rack/mock_request"
60
- autoload :MockResponse, "rack/mock_response"
61
-
62
- autoload :Request, "rack/request"
63
- autoload :Response, "rack/response"
64
59
 
65
60
  module Auth
66
61
  autoload :Basic, "rack/auth/basic"
67
- autoload :AbstractRequest, "rack/auth/abstract/request"
68
62
  autoload :AbstractHandler, "rack/auth/abstract/handler"
69
- autoload :Digest, "rack/auth/digest"
63
+ autoload :AbstractRequest, "rack/auth/abstract/request"
70
64
  end
71
65
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.8
4
+ version: 3.1.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Leah Neukirchen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-06-14 00:00:00.000000000 Z
11
+ date: 2024-07-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest
@@ -89,15 +89,10 @@ files:
89
89
  - lib/rack/auth/abstract/handler.rb
90
90
  - lib/rack/auth/abstract/request.rb
91
91
  - lib/rack/auth/basic.rb
92
- - lib/rack/auth/digest.rb
93
- - lib/rack/auth/digest/md5.rb
94
- - lib/rack/auth/digest/nonce.rb
95
- - lib/rack/auth/digest/params.rb
96
- - lib/rack/auth/digest/request.rb
92
+ - lib/rack/bad_request.rb
97
93
  - lib/rack/body_proxy.rb
98
94
  - lib/rack/builder.rb
99
95
  - lib/rack/cascade.rb
100
- - lib/rack/chunked.rb
101
96
  - lib/rack/common_logger.rb
102
97
  - lib/rack/conditional_get.rb
103
98
  - lib/rack/config.rb
@@ -108,7 +103,6 @@ files:
108
103
  - lib/rack/directory.rb
109
104
  - lib/rack/etag.rb
110
105
  - lib/rack/events.rb
111
- - lib/rack/file.rb
112
106
  - lib/rack/files.rb
113
107
  - lib/rack/head.rb
114
108
  - lib/rack/headers.rb
@@ -164,7 +158,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
164
158
  - !ruby/object:Gem::Version
165
159
  version: '0'
166
160
  requirements: []
167
- rubygems_version: 3.4.7
161
+ rubygems_version: 3.5.11
168
162
  signing_key:
169
163
  specification_version: 4
170
164
  summary: A modular Ruby webserver interface.
@@ -1 +0,0 @@
1
- require_relative '../digest'
@@ -1 +0,0 @@
1
- require_relative '../digest'
@@ -1 +0,0 @@
1
- require_relative '../digest'
@@ -1 +0,0 @@
1
- require_relative '../digest'