rack 2.0.9 → 2.1.4

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.
Files changed (189) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +77 -0
  3. data/{COPYING → MIT-LICENSE} +4 -2
  4. data/README.rdoc +77 -117
  5. data/Rakefile +25 -18
  6. data/SPEC +3 -4
  7. data/bin/rackup +1 -0
  8. data/example/lobster.ru +2 -0
  9. data/example/protectedlobster.rb +3 -1
  10. data/example/protectedlobster.ru +2 -0
  11. data/lib/rack.rb +63 -60
  12. data/lib/rack/auth/abstract/handler.rb +3 -1
  13. data/lib/rack/auth/abstract/request.rb +2 -0
  14. data/lib/rack/auth/basic.rb +4 -1
  15. data/lib/rack/auth/digest/md5.rb +9 -7
  16. data/lib/rack/auth/digest/nonce.rb +6 -3
  17. data/lib/rack/auth/digest/params.rb +4 -2
  18. data/lib/rack/auth/digest/request.rb +2 -0
  19. data/lib/rack/body_proxy.rb +3 -6
  20. data/lib/rack/builder.rb +39 -15
  21. data/lib/rack/cascade.rb +6 -5
  22. data/lib/rack/chunked.rb +29 -6
  23. data/lib/rack/common_logger.rb +9 -8
  24. data/lib/rack/conditional_get.rb +3 -1
  25. data/lib/rack/config.rb +2 -0
  26. data/lib/rack/content_length.rb +3 -1
  27. data/lib/rack/content_type.rb +3 -1
  28. data/lib/rack/core_ext/regexp.rb +14 -0
  29. data/lib/rack/deflater.rb +32 -17
  30. data/lib/rack/directory.rb +19 -16
  31. data/lib/rack/etag.rb +3 -1
  32. data/lib/rack/events.rb +5 -3
  33. data/lib/rack/file.rb +4 -173
  34. data/lib/rack/files.rb +178 -0
  35. data/lib/rack/handler.rb +7 -2
  36. data/lib/rack/handler/cgi.rb +3 -1
  37. data/lib/rack/handler/fastcgi.rb +4 -2
  38. data/lib/rack/handler/lsws.rb +3 -1
  39. data/lib/rack/handler/scgi.rb +9 -6
  40. data/lib/rack/handler/thin.rb +3 -1
  41. data/lib/rack/handler/webrick.rb +4 -2
  42. data/lib/rack/head.rb +2 -0
  43. data/lib/rack/lint.rb +14 -11
  44. data/lib/rack/lobster.rb +7 -5
  45. data/lib/rack/lock.rb +2 -0
  46. data/lib/rack/logger.rb +2 -0
  47. data/lib/rack/media_type.rb +10 -5
  48. data/lib/rack/method_override.rb +4 -2
  49. data/lib/rack/mime.rb +9 -1
  50. data/lib/rack/mock.rb +74 -15
  51. data/lib/rack/multipart.rb +5 -3
  52. data/lib/rack/multipart/generator.rb +6 -7
  53. data/lib/rack/multipart/parser.rb +51 -45
  54. data/lib/rack/multipart/uploaded_file.rb +2 -0
  55. data/lib/rack/null_logger.rb +2 -0
  56. data/lib/rack/query_parser.rb +51 -25
  57. data/lib/rack/recursive.rb +7 -5
  58. data/lib/rack/reloader.rb +10 -4
  59. data/lib/rack/request.rb +79 -26
  60. data/lib/rack/response.rb +71 -31
  61. data/lib/rack/rewindable_input.rb +4 -2
  62. data/lib/rack/runtime.rb +4 -2
  63. data/lib/rack/sendfile.rb +15 -8
  64. data/lib/rack/server.rb +88 -18
  65. data/lib/rack/session/abstract/id.rb +30 -20
  66. data/lib/rack/session/cookie.rb +10 -9
  67. data/lib/rack/session/memcache.rb +4 -93
  68. data/lib/rack/session/pool.rb +4 -2
  69. data/lib/rack/show_exceptions.rb +15 -9
  70. data/lib/rack/show_status.rb +4 -2
  71. data/lib/rack/static.rb +15 -10
  72. data/lib/rack/tempfile_reaper.rb +2 -0
  73. data/lib/rack/urlmap.rb +11 -2
  74. data/lib/rack/utils.rb +59 -72
  75. data/rack.gemspec +17 -7
  76. metadata +33 -175
  77. data/HISTORY.md +0 -505
  78. data/test/builder/an_underscore_app.rb +0 -5
  79. data/test/builder/anything.rb +0 -5
  80. data/test/builder/comment.ru +0 -4
  81. data/test/builder/end.ru +0 -5
  82. data/test/builder/line.ru +0 -1
  83. data/test/builder/options.ru +0 -2
  84. data/test/cgi/assets/folder/test.js +0 -1
  85. data/test/cgi/assets/fonts/font.eot +0 -1
  86. data/test/cgi/assets/images/image.png +0 -1
  87. data/test/cgi/assets/index.html +0 -1
  88. data/test/cgi/assets/javascripts/app.js +0 -1
  89. data/test/cgi/assets/stylesheets/app.css +0 -1
  90. data/test/cgi/lighttpd.conf +0 -26
  91. data/test/cgi/rackup_stub.rb +0 -6
  92. data/test/cgi/sample_rackup.ru +0 -5
  93. data/test/cgi/test +0 -9
  94. data/test/cgi/test+directory/test+file +0 -1
  95. data/test/cgi/test.fcgi +0 -9
  96. data/test/cgi/test.gz +0 -0
  97. data/test/cgi/test.ru +0 -5
  98. data/test/gemloader.rb +0 -10
  99. data/test/helper.rb +0 -34
  100. data/test/multipart/bad_robots +0 -259
  101. data/test/multipart/binary +0 -0
  102. data/test/multipart/content_type_and_no_filename +0 -6
  103. data/test/multipart/empty +0 -10
  104. data/test/multipart/fail_16384_nofile +0 -814
  105. data/test/multipart/file1.txt +0 -1
  106. data/test/multipart/filename_and_modification_param +0 -7
  107. data/test/multipart/filename_and_no_name +0 -6
  108. data/test/multipart/filename_with_encoded_words +0 -7
  109. data/test/multipart/filename_with_escaped_quotes +0 -6
  110. data/test/multipart/filename_with_escaped_quotes_and_modification_param +0 -7
  111. data/test/multipart/filename_with_null_byte +0 -7
  112. data/test/multipart/filename_with_percent_escaped_quotes +0 -6
  113. data/test/multipart/filename_with_single_quote +0 -7
  114. data/test/multipart/filename_with_unescaped_percentages +0 -6
  115. data/test/multipart/filename_with_unescaped_percentages2 +0 -6
  116. data/test/multipart/filename_with_unescaped_percentages3 +0 -6
  117. data/test/multipart/filename_with_unescaped_quotes +0 -6
  118. data/test/multipart/ie +0 -6
  119. data/test/multipart/invalid_character +0 -6
  120. data/test/multipart/mixed_files +0 -21
  121. data/test/multipart/nested +0 -10
  122. data/test/multipart/none +0 -9
  123. data/test/multipart/quoted +0 -15
  124. data/test/multipart/rack-logo.png +0 -0
  125. data/test/multipart/semicolon +0 -6
  126. data/test/multipart/text +0 -15
  127. data/test/multipart/three_files_three_fields +0 -31
  128. data/test/multipart/unity3d_wwwform +0 -11
  129. data/test/multipart/webkit +0 -32
  130. data/test/rackup/config.ru +0 -31
  131. data/test/registering_handler/rack/handler/registering_myself.rb +0 -8
  132. data/test/spec_auth_basic.rb +0 -89
  133. data/test/spec_auth_digest.rb +0 -260
  134. data/test/spec_body_proxy.rb +0 -85
  135. data/test/spec_builder.rb +0 -233
  136. data/test/spec_cascade.rb +0 -63
  137. data/test/spec_cgi.rb +0 -84
  138. data/test/spec_chunked.rb +0 -103
  139. data/test/spec_common_logger.rb +0 -95
  140. data/test/spec_conditional_get.rb +0 -103
  141. data/test/spec_config.rb +0 -23
  142. data/test/spec_content_length.rb +0 -86
  143. data/test/spec_content_type.rb +0 -46
  144. data/test/spec_deflater.rb +0 -375
  145. data/test/spec_directory.rb +0 -148
  146. data/test/spec_etag.rb +0 -108
  147. data/test/spec_events.rb +0 -133
  148. data/test/spec_fastcgi.rb +0 -85
  149. data/test/spec_file.rb +0 -264
  150. data/test/spec_handler.rb +0 -57
  151. data/test/spec_head.rb +0 -46
  152. data/test/spec_lint.rb +0 -515
  153. data/test/spec_lobster.rb +0 -59
  154. data/test/spec_lock.rb +0 -204
  155. data/test/spec_logger.rb +0 -24
  156. data/test/spec_media_type.rb +0 -42
  157. data/test/spec_method_override.rb +0 -110
  158. data/test/spec_mime.rb +0 -51
  159. data/test/spec_mock.rb +0 -359
  160. data/test/spec_multipart.rb +0 -722
  161. data/test/spec_null_logger.rb +0 -21
  162. data/test/spec_recursive.rb +0 -75
  163. data/test/spec_request.rb +0 -1407
  164. data/test/spec_response.rb +0 -528
  165. data/test/spec_rewindable_input.rb +0 -128
  166. data/test/spec_runtime.rb +0 -50
  167. data/test/spec_sendfile.rb +0 -125
  168. data/test/spec_server.rb +0 -193
  169. data/test/spec_session_abstract_id.rb +0 -31
  170. data/test/spec_session_abstract_session_hash.rb +0 -45
  171. data/test/spec_session_cookie.rb +0 -442
  172. data/test/spec_session_memcache.rb +0 -357
  173. data/test/spec_session_persisted_secure_secure_session_hash.rb +0 -73
  174. data/test/spec_session_pool.rb +0 -247
  175. data/test/spec_show_exceptions.rb +0 -93
  176. data/test/spec_show_status.rb +0 -104
  177. data/test/spec_static.rb +0 -184
  178. data/test/spec_tempfile_reaper.rb +0 -64
  179. data/test/spec_thin.rb +0 -96
  180. data/test/spec_urlmap.rb +0 -237
  181. data/test/spec_utils.rb +0 -742
  182. data/test/spec_version.rb +0 -11
  183. data/test/spec_webrick.rb +0 -206
  184. data/test/static/another/index.html +0 -1
  185. data/test/static/foo.html +0 -1
  186. data/test/static/index.html +0 -1
  187. data/test/testrequest.rb +0 -78
  188. data/test/unregistered_handler/rack/handler/unregistered.rb +0 -7
  189. data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +0 -7
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # AUTHOR: blink <blinketje@gmail.com>; blink#ruby-lang@irc.freenode.net
2
4
  # THANKS:
3
5
  # apeiros, for session id generation, expiry setup, and threadiness
@@ -26,9 +28,9 @@ module Rack
26
28
 
27
29
  class Pool < Abstract::PersistedSecure
28
30
  attr_reader :mutex, :pool
29
- DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge :drop => false
31
+ DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge drop: false
30
32
 
31
- def initialize(app, options={})
33
+ def initialize(app, options = {})
32
34
  super
33
35
  @pool = Hash.new
34
36
  @mutex = Mutex.new
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'ostruct'
2
4
  require 'erb'
3
5
  require 'rack/request'
@@ -55,7 +57,7 @@ module Rack
55
57
  private :accepts_html?
56
58
 
57
59
  def dump_exception(exception)
58
- string = "#{exception.class}: #{exception.message}\n"
60
+ string = "#{exception.class}: #{exception.message}\n".dup
59
61
  string << exception.backtrace.map { |l| "\t#{l}" }.join("\n")
60
62
  string
61
63
  end
@@ -77,13 +79,13 @@ module Rack
77
79
  frame.function = $4
78
80
 
79
81
  begin
80
- lineno = frame.lineno-1
82
+ lineno = frame.lineno - 1
81
83
  lines = ::File.readlines(frame.filename)
82
- frame.pre_context_lineno = [lineno-CONTEXT, 0].max
84
+ frame.pre_context_lineno = [lineno - CONTEXT, 0].max
83
85
  frame.pre_context = lines[frame.pre_context_lineno...lineno]
84
86
  frame.context_line = lines[lineno].chomp
85
- frame.post_context_lineno = [lineno+CONTEXT, lines.size].min
86
- frame.post_context = lines[lineno+1..frame.post_context_lineno]
87
+ frame.post_context_lineno = [lineno + CONTEXT, lines.size].min
88
+ frame.post_context = lines[lineno + 1..frame.post_context_lineno]
87
89
  rescue
88
90
  end
89
91
 
@@ -93,7 +95,11 @@ module Rack
93
95
  end
94
96
  }.compact
95
97
 
96
- TEMPLATE.result(binding)
98
+ template.result(binding)
99
+ end
100
+
101
+ def template
102
+ TEMPLATE
97
103
  end
98
104
 
99
105
  def h(obj) # :nodoc:
@@ -107,8 +113,8 @@ module Rack
107
113
 
108
114
  # :stopdoc:
109
115
 
110
- # adapted from Django <djangoproject.com>
111
- # Copyright (c) 2005, the Lawrence Journal-World
116
+ # adapted from Django <www.djangoproject.com>
117
+ # Copyright (c) Django Software Foundation and individual contributors.
112
118
  # Used under the modified BSD license:
113
119
  # http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5
114
120
  TEMPLATE = ERB.new(<<-'HTML'.gsub(/^ /, ''))
@@ -363,7 +369,7 @@ module Rack
363
369
  <% env.sort_by { |k, v| k.to_s }.each { |key, val| %>
364
370
  <tr>
365
371
  <td><%=h key %></td>
366
- <td class="code"><div><%=h val %></div></td>
372
+ <td class="code"><div><%=h val.inspect %></div></td>
367
373
  </tr>
368
374
  <% } %>
369
375
  </tbody>
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'erb'
2
4
  require 'rack/request'
3
5
  require 'rack/utils'
@@ -52,8 +54,8 @@ module Rack
52
54
 
53
55
  # :stopdoc:
54
56
 
55
- # adapted from Django <djangoproject.com>
56
- # Copyright (c) 2005, the Lawrence Journal-World
57
+ # adapted from Django <www.djangoproject.com>
58
+ # Copyright (c) Django Software Foundation and individual contributors.
57
59
  # Used under the modified BSD license:
58
60
  # http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5
59
61
  TEMPLATE = <<'HTML'
@@ -1,11 +1,15 @@
1
- require "rack/file"
1
+ # frozen_string_literal: true
2
+
3
+ require "rack/files"
2
4
  require "rack/utils"
3
5
 
6
+ require_relative 'core_ext/regexp'
7
+
4
8
  module Rack
5
9
 
6
10
  # The Rack::Static middleware intercepts requests for static files
7
11
  # (javascript files, images, stylesheets, etc) based on the url prefixes or
8
- # route mappings passed in the options, and serves them using a Rack::File
12
+ # route mappings passed in the options, and serves them using a Rack::Files
9
13
  # object. This allows a Rack stack to serve both static and dynamic content.
10
14
  #
11
15
  # Examples:
@@ -82,8 +86,9 @@ module Rack
82
86
  # ]
83
87
  #
84
88
  class Static
89
+ using ::Rack::RegexpExtensions
85
90
 
86
- def initialize(app, options={})
91
+ def initialize(app, options = {})
87
92
  @app = app
88
93
  @urls = options[:urls] || ["/favicon.ico"]
89
94
  @index = options[:index]
@@ -93,13 +98,13 @@ module Rack
93
98
  # HTTP Headers
94
99
  @header_rules = options[:header_rules] || []
95
100
  # Allow for legacy :cache_control option while prioritizing global header_rules setting
96
- @header_rules.unshift([:all, {CACHE_CONTROL => options[:cache_control]}]) if options[:cache_control]
101
+ @header_rules.unshift([:all, { CACHE_CONTROL => options[:cache_control] }]) if options[:cache_control]
97
102
 
98
- @file_server = Rack::File.new(root)
103
+ @file_server = Rack::Files.new(root)
99
104
  end
100
105
 
101
106
  def add_index_root?(path)
102
- @index && path =~ /\/$/
107
+ @index && route_file(path) && path.end_with?('/')
103
108
  end
104
109
 
105
110
  def overwrite_file_path(path)
@@ -120,7 +125,7 @@ module Rack
120
125
  if can_serve(path)
121
126
  if overwrite_file_path(path)
122
127
  env[PATH_INFO] = (add_index_root?(path) ? path + @index : @urls[path])
123
- elsif @gzip && env['HTTP_ACCEPT_ENCODING'] =~ /\bgzip\b/
128
+ elsif @gzip && env['HTTP_ACCEPT_ENCODING'] && /\bgzip\b/.match?(env['HTTP_ACCEPT_ENCODING'])
124
129
  path = env[PATH_INFO]
125
130
  env[PATH_INFO] += '.gz'
126
131
  response = @file_server.call(env)
@@ -157,14 +162,14 @@ module Rack
157
162
  when :all
158
163
  true
159
164
  when :fonts
160
- path =~ /\.(?:ttf|otf|eot|woff2|woff|svg)\z/
165
+ /\.(?:ttf|otf|eot|woff2|woff|svg)\z/.match?(path)
161
166
  when String
162
167
  path = ::Rack::Utils.unescape(path)
163
168
  path.start_with?(rule) || path.start_with?('/' + rule)
164
169
  when Array
165
- path =~ /\.(#{rule.join('|')})\z/
170
+ /\.(#{rule.join('|')})\z/.match?(path)
166
171
  when Regexp
167
- path =~ rule
172
+ rule.match?(path)
168
173
  else
169
174
  false
170
175
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rack/body_proxy'
2
4
 
3
5
  module Rack
@@ -1,3 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'set'
4
+
1
5
  module Rack
2
6
  # Rack::URLMap takes a hash mapping urls or paths to apps, and
3
7
  # dispatches accordingly. Support for HTTP/1.1 host names exists if
@@ -20,9 +24,11 @@ module Rack
20
24
  end
21
25
 
22
26
  def remap(map)
27
+ @known_hosts = Set[]
23
28
  @mapping = map.map { |location, app|
24
29
  if location =~ %r{\Ahttps?://(.*?)(/.*)}
25
30
  host, location = $1, $2
31
+ @known_hosts << host
26
32
  else
27
33
  host = nil
28
34
  end
@@ -50,10 +56,13 @@ module Rack
50
56
  is_same_server = casecmp?(http_host, server_name) ||
51
57
  casecmp?(http_host, "#{server_name}:#{server_port}")
52
58
 
59
+ is_host_known = @known_hosts.include? http_host
60
+
53
61
  @mapping.each do |host, location, match, app|
54
62
  unless casecmp?(http_host, host) \
55
63
  || casecmp?(server_name, host) \
56
- || (!host && is_same_server)
64
+ || (!host && is_same_server) \
65
+ || (!host && !is_host_known) # If we don't have a matching host, default to the first without a specified host
57
66
  next
58
67
  end
59
68
 
@@ -68,7 +77,7 @@ module Rack
68
77
  return app.call(env)
69
78
  end
70
79
 
71
- [404, {CONTENT_TYPE => "text/plain", "X-Cascade" => "pass"}, ["Not Found: #{path}"]]
80
+ [404, { CONTENT_TYPE => "text/plain", "X-Cascade" => "pass" }, ["Not Found: #{path}"]]
72
81
 
73
82
  ensure
74
83
  env[PATH_INFO] = path
@@ -1,4 +1,6 @@
1
1
  # -*- encoding: binary -*-
2
+ # frozen_string_literal: true
3
+
2
4
  require 'uri'
3
5
  require 'fileutils'
4
6
  require 'set'
@@ -6,11 +8,15 @@ require 'tempfile'
6
8
  require 'rack/query_parser'
7
9
  require 'time'
8
10
 
11
+ require_relative 'core_ext/regexp'
12
+
9
13
  module Rack
10
14
  # Rack::Utils contains a grab-bag of useful methods for writing web
11
15
  # applications adopted from all kinds of Ruby libraries.
12
16
 
13
17
  module Utils
18
+ using ::Rack::RegexpExtensions
19
+
14
20
  ParameterTypeError = QueryParser::ParameterTypeError
15
21
  InvalidParameterError = QueryParser::InvalidParameterError
16
22
  DEFAULT_SEP = QueryParser::DEFAULT_SEP
@@ -118,7 +124,7 @@ module Rack
118
124
  when Hash
119
125
  value.map { |k, v|
120
126
  build_nested_query(v, prefix ? "#{prefix}[#{escape(k)}]" : escape(k))
121
- }.reject(&:empty?).join('&')
127
+ }.delete_if(&:empty?).join('&')
122
128
  when nil
123
129
  prefix
124
130
  else
@@ -132,7 +138,7 @@ module Rack
132
138
  q_value_header.to_s.split(/\s*,\s*/).map do |part|
133
139
  value, parameters = part.split(/\s*;\s*/, 2)
134
140
  quality = 1.0
135
- if md = /\Aq=([\d.]+)/.match(parameters)
141
+ if parameters && (md = /\Aq=([\d.]+)/.match(parameters))
136
142
  quality = md[1].to_f
137
143
  end
138
144
  [value, quality]
@@ -175,27 +181,26 @@ module Rack
175
181
  # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
176
182
 
177
183
  expanded_accept_encoding =
178
- accept_encoding.map { |m, q|
184
+ accept_encoding.each_with_object([]) do |(m, q), list|
179
185
  if m == "*"
180
- (available_encodings - accept_encoding.map { |m2, _| m2 }).map { |m2| [m2, q] }
186
+ (available_encodings - accept_encoding.map(&:first))
187
+ .each { |m2| list << [m2, q] }
181
188
  else
182
- [[m, q]]
189
+ list << [m, q]
183
190
  end
184
- }.inject([]) { |mem, list|
185
- mem + list
186
- }
191
+ end
187
192
 
188
- encoding_candidates = expanded_accept_encoding.sort_by { |_, q| -q }.map { |m, _| m }
193
+ encoding_candidates = expanded_accept_encoding.sort_by { |_, q| -q }.map!(&:first)
189
194
 
190
195
  unless encoding_candidates.include?("identity")
191
196
  encoding_candidates.push("identity")
192
197
  end
193
198
 
194
- expanded_accept_encoding.each { |m, q|
199
+ expanded_accept_encoding.each do |m, q|
195
200
  encoding_candidates.delete(m) if q == 0.0
196
- }
201
+ end
197
202
 
198
- return (encoding_candidates & available_encodings)[0]
203
+ (encoding_candidates & available_encodings)[0]
199
204
  end
200
205
  module_function :select_best_encoding
201
206
 
@@ -210,8 +215,12 @@ module Rack
210
215
  # the Cookie header such that those with more specific Path attributes
211
216
  # precede those with less specific. Ordering with respect to other
212
217
  # attributes (e.g., Domain) is unspecified.
213
- cookies = parse_query(header, ';,') { |s| unescape(s) rescue s }
214
- cookies.each_with_object({}) { |(k,v), hash| hash[k] = Array === v ? v.first : v }
218
+ return {} unless header
219
+ header.split(/[;,] */n).each_with_object({}) do |cookie, cookies|
220
+ next if cookie.empty?
221
+ key, value = cookie.split('=', 2)
222
+ cookies[key] = (unescape(value) rescue value) unless cookies.key?(key)
223
+ end
215
224
  end
216
225
  module_function :parse_cookies_header
217
226
 
@@ -221,31 +230,7 @@ module Rack
221
230
  domain = "; domain=#{value[:domain]}" if value[:domain]
222
231
  path = "; path=#{value[:path]}" if value[:path]
223
232
  max_age = "; max-age=#{value[:max_age]}" if value[:max_age]
224
- # There is an RFC mess in the area of date formatting for Cookies. Not
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]
233
+ expires = "; expires=#{value[:expires].httpdate}" if value[:expires]
249
234
  secure = "; secure" if value[:secure]
250
235
  httponly = "; HttpOnly" if (value.key?(:httponly) ? value[:httponly] : value[:http_only])
251
236
  same_site =
@@ -253,11 +238,11 @@ module Rack
253
238
  when false, nil
254
239
  nil
255
240
  when :none, 'None', :None
256
- '; SameSite=None'.freeze
241
+ '; SameSite=None'
257
242
  when :lax, 'Lax', :Lax
258
- '; SameSite=Lax'.freeze
243
+ '; SameSite=Lax'
259
244
  when true, :strict, 'Strict', :Strict
260
- '; SameSite=Strict'.freeze
245
+ '; SameSite=Strict'
261
246
  else
262
247
  raise ArgumentError, "Invalid SameSite value: #{value[:same_site].inspect}"
263
248
  end
@@ -297,15 +282,15 @@ module Rack
297
282
  cookies = header
298
283
  end
299
284
 
300
- cookies.reject! { |cookie|
301
- if value[:domain]
302
- cookie =~ /\A#{escape(key)}=.*domain=#{value[:domain]}/
303
- elsif value[:path]
304
- cookie =~ /\A#{escape(key)}=.*path=#{value[:path]}/
305
- else
306
- cookie =~ /\A#{escape(key)}=/
307
- end
308
- }
285
+ regexp = if value[:domain]
286
+ /\A#{escape(key)}=.*domain=#{value[:domain]}/
287
+ elsif value[:path]
288
+ /\A#{escape(key)}=.*path=#{value[:path]}/
289
+ else
290
+ /\A#{escape(key)}=/
291
+ end
292
+
293
+ cookies.reject! { |cookie| regexp.match? cookie }
309
294
 
310
295
  cookies.join("\n")
311
296
  end
@@ -323,9 +308,9 @@ module Rack
323
308
  new_header = make_delete_cookie_header(header, key, value)
324
309
 
325
310
  add_cookie_to_header(new_header, key,
326
- {:value => '', :path => nil, :domain => nil,
327
- :max_age => '0',
328
- :expires => Time.at(0) }.merge(value))
311
+ { value: '', path: nil, domain: nil,
312
+ max_age: '0',
313
+ expires: Time.at(0) }.merge(value))
329
314
 
330
315
  end
331
316
  module_function :add_remove_cookie_to_header
@@ -366,7 +351,7 @@ module Rack
366
351
  ranges = []
367
352
  $1.split(/,\s*/).each do |range_spec|
368
353
  return nil unless range_spec =~ /(\d*)-(\d*)/
369
- r0,r1 = $1, $2
354
+ r0, r1 = $1, $2
370
355
  if r0.empty?
371
356
  return nil if r1.empty?
372
357
  # suffix-byte-range-spec, represents trailing suffix of file
@@ -380,7 +365,7 @@ module Rack
380
365
  else
381
366
  r1 = r1.to_i
382
367
  return nil if r1 < r0 # backwards range is syntactically invalid
383
- r1 = size-1 if r1 >= size
368
+ r1 = size - 1 if r1 >= size
384
369
  end
385
370
  end
386
371
  ranges << (r0..r1) if r0 <= r1
@@ -401,7 +386,7 @@ module Rack
401
386
  l = a.unpack("C*")
402
387
 
403
388
  r, i = 0, -1
404
- b.each_byte { |v| r |= v ^ l[i+=1] }
389
+ b.each_byte { |v| r |= v ^ l[i += 1] }
405
390
  r == 0
406
391
  end
407
392
  module_function :secure_compare
@@ -427,19 +412,17 @@ module Rack
427
412
  self.class.new(@for, app)
428
413
  end
429
414
 
430
- def context(env, app=@app)
415
+ def context(env, app = @app)
431
416
  recontext(app).call(env)
432
417
  end
433
418
  end
434
419
 
435
420
  # A case-insensitive Hash that preserves the original case of a
436
421
  # header when set.
437
- class HeaderHash < Hash
438
- def self.new(hash={})
439
- HeaderHash === hash ? hash : super(hash)
440
- end
441
-
442
- def initialize(hash={})
422
+ #
423
+ # @api private
424
+ class HeaderHash < Hash # :nodoc:
425
+ def initialize(hash = {})
443
426
  super()
444
427
  @names = {}
445
428
  hash.each { |k, v| self[k] = v }
@@ -459,7 +442,7 @@ module Rack
459
442
 
460
443
  def to_hash
461
444
  hash = {}
462
- each { |k,v| hash[k] = v }
445
+ each { |k, v| hash[k] = v }
463
446
  hash
464
447
  end
465
448
 
@@ -512,13 +495,14 @@ module Rack
512
495
 
513
496
  # Every standard HTTP code mapped to the appropriate message.
514
497
  # Generated with:
515
- # curl -s https://www.iana.org/assignments/http-status-codes/http-status-codes-1.csv | \
516
- # ruby -ne 'm = /^(\d{3}),(?!Unassigned|\(Unused\))([^,]+)/.match($_) and \
517
- # puts "#{m[1]} => \x27#{m[2].strip}\x27,"'
498
+ # curl -s https://www.iana.org/assignments/http-status-codes/http-status-codes-1.csv | \
499
+ # ruby -ne 'm = /^(\d{3}),(?!Unassigned|\(Unused\))([^,]+)/.match($_) and \
500
+ # puts "#{m[1]} => \x27#{m[2].strip}\x27,"'
518
501
  HTTP_STATUS_CODES = {
519
502
  100 => 'Continue',
520
503
  101 => 'Switching Protocols',
521
504
  102 => 'Processing',
505
+ 103 => 'Early Hints',
522
506
  200 => 'OK',
523
507
  201 => 'Created',
524
508
  202 => 'Accepted',
@@ -535,6 +519,7 @@ module Rack
535
519
  303 => 'See Other',
536
520
  304 => 'Not Modified',
537
521
  305 => 'Use Proxy',
522
+ 306 => '(Unused)',
538
523
  307 => 'Temporary Redirect',
539
524
  308 => 'Permanent Redirect',
540
525
  400 => 'Bad Request',
@@ -559,6 +544,7 @@ module Rack
559
544
  422 => 'Unprocessable Entity',
560
545
  423 => 'Locked',
561
546
  424 => 'Failed Dependency',
547
+ 425 => 'Too Early',
562
548
  426 => 'Upgrade Required',
563
549
  428 => 'Precondition Required',
564
550
  429 => 'Too Many Requests',
@@ -573,12 +559,13 @@ module Rack
573
559
  506 => 'Variant Also Negotiates',
574
560
  507 => 'Insufficient Storage',
575
561
  508 => 'Loop Detected',
562
+ 509 => 'Bandwidth Limit Exceeded',
576
563
  510 => 'Not Extended',
577
564
  511 => 'Network Authentication Required'
578
565
  }
579
566
 
580
567
  # Responses with HTTP status codes that should not have an entity body
581
- STATUS_WITH_NO_ENTITY_BODY = Set.new((100..199).to_a << 204 << 304)
568
+ STATUS_WITH_NO_ENTITY_BODY = Hash[((100..199).to_a << 204 << 304).product([true])]
582
569
 
583
570
  SYMBOL_TO_STATUS_CODE = Hash[*HTTP_STATUS_CODES.map { |code, message|
584
571
  [message.downcase.gsub(/\s|-|'/, '_').to_sym, code]
@@ -586,7 +573,7 @@ module Rack
586
573
 
587
574
  def status_code(status)
588
575
  if status.is_a?(Symbol)
589
- SYMBOL_TO_STATUS_CODE[status] || 500
576
+ SYMBOL_TO_STATUS_CODE.fetch(status) { raise ArgumentError, "Unrecognized status code #{status.inspect}" }
590
577
  else
591
578
  status.to_i
592
579
  end
@@ -607,11 +594,11 @@ module Rack
607
594
 
608
595
  clean.unshift '/' if parts.empty? || parts.first.empty?
609
596
 
610
- ::File.join(*clean)
597
+ ::File.join clean
611
598
  end
612
599
  module_function :clean_path_info
613
600
 
614
- NULL_BYTE = "\0".freeze
601
+ NULL_BYTE = "\0"
615
602
 
616
603
  def valid_path?(path)
617
604
  path.valid_encoding? && !path.include?(NULL_BYTE)