rack 2.0.9.4 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of rack might be problematic. Click here for more details.

Files changed (188) hide show
  1. checksums.yaml +4 -4
  2. data/{HISTORY.md → CHANGELOG.md} +214 -164
  3. data/{COPYING → MIT-LICENSE} +4 -2
  4. data/README.rdoc +79 -133
  5. data/Rakefile +25 -18
  6. data/SPEC +9 -9
  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/auth/abstract/handler.rb +3 -1
  12. data/lib/rack/auth/abstract/request.rb +2 -0
  13. data/lib/rack/auth/basic.rb +4 -1
  14. data/lib/rack/auth/digest/md5.rb +9 -7
  15. data/lib/rack/auth/digest/nonce.rb +6 -3
  16. data/lib/rack/auth/digest/params.rb +4 -2
  17. data/lib/rack/auth/digest/request.rb +2 -0
  18. data/lib/rack/body_proxy.rb +3 -6
  19. data/lib/rack/builder.rb +38 -15
  20. data/lib/rack/cascade.rb +6 -5
  21. data/lib/rack/chunked.rb +29 -6
  22. data/lib/rack/common_logger.rb +9 -11
  23. data/lib/rack/conditional_get.rb +3 -1
  24. data/lib/rack/config.rb +2 -0
  25. data/lib/rack/content_length.rb +3 -1
  26. data/lib/rack/content_type.rb +3 -1
  27. data/lib/rack/core_ext/regexp.rb +14 -0
  28. data/lib/rack/deflater.rb +28 -17
  29. data/lib/rack/directory.rb +17 -14
  30. data/lib/rack/etag.rb +3 -1
  31. data/lib/rack/events.rb +5 -3
  32. data/lib/rack/file.rb +5 -173
  33. data/lib/rack/files.rb +178 -0
  34. data/lib/rack/handler/cgi.rb +3 -1
  35. data/lib/rack/handler/fastcgi.rb +4 -2
  36. data/lib/rack/handler/lsws.rb +3 -1
  37. data/lib/rack/handler/scgi.rb +9 -6
  38. data/lib/rack/handler/thin.rb +3 -1
  39. data/lib/rack/handler/webrick.rb +4 -2
  40. data/lib/rack/handler.rb +7 -2
  41. data/lib/rack/head.rb +2 -0
  42. data/lib/rack/lint.rb +15 -12
  43. data/lib/rack/lobster.rb +7 -5
  44. data/lib/rack/lock.rb +2 -0
  45. data/lib/rack/logger.rb +2 -0
  46. data/lib/rack/media_type.rb +10 -5
  47. data/lib/rack/method_override.rb +4 -2
  48. data/lib/rack/mime.rb +9 -1
  49. data/lib/rack/mock.rb +74 -15
  50. data/lib/rack/multipart/generator.rb +6 -7
  51. data/lib/rack/multipart/parser.rb +55 -62
  52. data/lib/rack/multipart/uploaded_file.rb +2 -0
  53. data/lib/rack/multipart.rb +6 -3
  54. data/lib/rack/null_logger.rb +2 -0
  55. data/lib/rack/query_parser.rb +51 -25
  56. data/lib/rack/recursive.rb +7 -5
  57. data/lib/rack/reloader.rb +10 -4
  58. data/lib/rack/request.rb +79 -26
  59. data/lib/rack/response.rb +71 -31
  60. data/lib/rack/rewindable_input.rb +4 -2
  61. data/lib/rack/runtime.rb +4 -2
  62. data/lib/rack/sendfile.rb +15 -8
  63. data/lib/rack/server.rb +88 -16
  64. data/lib/rack/session/abstract/id.rb +40 -22
  65. data/lib/rack/session/cookie.rb +10 -9
  66. data/lib/rack/session/memcache.rb +4 -93
  67. data/lib/rack/session/pool.rb +4 -2
  68. data/lib/rack/show_exceptions.rb +15 -9
  69. data/lib/rack/show_status.rb +4 -2
  70. data/lib/rack/static.rb +15 -10
  71. data/lib/rack/tempfile_reaper.rb +2 -0
  72. data/lib/rack/urlmap.rb +11 -2
  73. data/lib/rack/utils.rb +64 -93
  74. data/lib/rack.rb +63 -60
  75. data/rack.gemspec +17 -7
  76. metadata +33 -175
  77. data/test/builder/an_underscore_app.rb +0 -5
  78. data/test/builder/anything.rb +0 -5
  79. data/test/builder/comment.ru +0 -4
  80. data/test/builder/end.ru +0 -5
  81. data/test/builder/line.ru +0 -1
  82. data/test/builder/options.ru +0 -2
  83. data/test/cgi/assets/folder/test.js +0 -1
  84. data/test/cgi/assets/fonts/font.eot +0 -1
  85. data/test/cgi/assets/images/image.png +0 -1
  86. data/test/cgi/assets/index.html +0 -1
  87. data/test/cgi/assets/javascripts/app.js +0 -1
  88. data/test/cgi/assets/stylesheets/app.css +0 -1
  89. data/test/cgi/lighttpd.conf +0 -26
  90. data/test/cgi/rackup_stub.rb +0 -6
  91. data/test/cgi/sample_rackup.ru +0 -5
  92. data/test/cgi/test +0 -9
  93. data/test/cgi/test+directory/test+file +0 -1
  94. data/test/cgi/test.fcgi +0 -9
  95. data/test/cgi/test.gz +0 -0
  96. data/test/cgi/test.ru +0 -5
  97. data/test/gemloader.rb +0 -10
  98. data/test/helper.rb +0 -34
  99. data/test/multipart/bad_robots +0 -259
  100. data/test/multipart/binary +0 -0
  101. data/test/multipart/content_type_and_no_filename +0 -6
  102. data/test/multipart/empty +0 -10
  103. data/test/multipart/fail_16384_nofile +0 -814
  104. data/test/multipart/file1.txt +0 -1
  105. data/test/multipart/filename_and_modification_param +0 -7
  106. data/test/multipart/filename_and_no_name +0 -6
  107. data/test/multipart/filename_with_encoded_words +0 -7
  108. data/test/multipart/filename_with_escaped_quotes +0 -6
  109. data/test/multipart/filename_with_escaped_quotes_and_modification_param +0 -7
  110. data/test/multipart/filename_with_null_byte +0 -7
  111. data/test/multipart/filename_with_percent_escaped_quotes +0 -6
  112. data/test/multipart/filename_with_single_quote +0 -7
  113. data/test/multipart/filename_with_unescaped_percentages +0 -6
  114. data/test/multipart/filename_with_unescaped_percentages2 +0 -6
  115. data/test/multipart/filename_with_unescaped_percentages3 +0 -6
  116. data/test/multipart/filename_with_unescaped_quotes +0 -6
  117. data/test/multipart/ie +0 -6
  118. data/test/multipart/invalid_character +0 -6
  119. data/test/multipart/mixed_files +0 -21
  120. data/test/multipart/nested +0 -10
  121. data/test/multipart/none +0 -9
  122. data/test/multipart/quoted +0 -15
  123. data/test/multipart/rack-logo.png +0 -0
  124. data/test/multipart/semicolon +0 -6
  125. data/test/multipart/text +0 -15
  126. data/test/multipart/three_files_three_fields +0 -31
  127. data/test/multipart/unity3d_wwwform +0 -11
  128. data/test/multipart/webkit +0 -32
  129. data/test/rackup/config.ru +0 -31
  130. data/test/registering_handler/rack/handler/registering_myself.rb +0 -8
  131. data/test/spec_auth_basic.rb +0 -89
  132. data/test/spec_auth_digest.rb +0 -260
  133. data/test/spec_body_proxy.rb +0 -85
  134. data/test/spec_builder.rb +0 -233
  135. data/test/spec_cascade.rb +0 -63
  136. data/test/spec_cgi.rb +0 -84
  137. data/test/spec_chunked.rb +0 -103
  138. data/test/spec_common_logger.rb +0 -107
  139. data/test/spec_conditional_get.rb +0 -103
  140. data/test/spec_config.rb +0 -23
  141. data/test/spec_content_length.rb +0 -86
  142. data/test/spec_content_type.rb +0 -46
  143. data/test/spec_deflater.rb +0 -375
  144. data/test/spec_directory.rb +0 -148
  145. data/test/spec_etag.rb +0 -108
  146. data/test/spec_events.rb +0 -133
  147. data/test/spec_fastcgi.rb +0 -85
  148. data/test/spec_file.rb +0 -264
  149. data/test/spec_handler.rb +0 -57
  150. data/test/spec_head.rb +0 -46
  151. data/test/spec_lint.rb +0 -520
  152. data/test/spec_lobster.rb +0 -59
  153. data/test/spec_lock.rb +0 -204
  154. data/test/spec_logger.rb +0 -24
  155. data/test/spec_media_type.rb +0 -42
  156. data/test/spec_method_override.rb +0 -110
  157. data/test/spec_mime.rb +0 -51
  158. data/test/spec_mock.rb +0 -359
  159. data/test/spec_multipart.rb +0 -721
  160. data/test/spec_null_logger.rb +0 -21
  161. data/test/spec_recursive.rb +0 -75
  162. data/test/spec_request.rb +0 -1423
  163. data/test/spec_response.rb +0 -528
  164. data/test/spec_rewindable_input.rb +0 -128
  165. data/test/spec_runtime.rb +0 -50
  166. data/test/spec_sendfile.rb +0 -125
  167. data/test/spec_server.rb +0 -193
  168. data/test/spec_session_abstract_id.rb +0 -31
  169. data/test/spec_session_abstract_session_hash.rb +0 -45
  170. data/test/spec_session_cookie.rb +0 -442
  171. data/test/spec_session_memcache.rb +0 -357
  172. data/test/spec_session_persisted_secure_secure_session_hash.rb +0 -73
  173. data/test/spec_session_pool.rb +0 -247
  174. data/test/spec_show_exceptions.rb +0 -93
  175. data/test/spec_show_status.rb +0 -104
  176. data/test/spec_static.rb +0 -184
  177. data/test/spec_tempfile_reaper.rb +0 -64
  178. data/test/spec_thin.rb +0 -96
  179. data/test/spec_urlmap.rb +0 -237
  180. data/test/spec_utils.rb +0 -742
  181. data/test/spec_version.rb +0 -11
  182. data/test/spec_webrick.rb +0 -206
  183. data/test/static/another/index.html +0 -1
  184. data/test/static/foo.html +0 -1
  185. data/test/static/index.html +0 -1
  186. data/test/testrequest.rb +0 -78
  187. data/test/unregistered_handler/rack/handler/unregistered.rb +0 -7
  188. data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +0 -7
data/lib/rack/static.rb CHANGED
@@ -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
data/lib/rack/urlmap.rb CHANGED
@@ -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
data/lib/rack/utils.rb CHANGED
@@ -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
@@ -53,24 +59,13 @@ module Rack
53
59
  module_function :unescape
54
60
 
55
61
  class << self
56
- attr_accessor :multipart_total_part_limit
57
-
58
- attr_accessor :multipart_file_limit
59
-
60
- # multipart_part_limit is the original name of multipart_file_limit, but
61
- # the limit only counts parts with filenames.
62
- alias multipart_part_limit multipart_file_limit
63
- alias multipart_part_limit= multipart_file_limit=
62
+ attr_accessor :multipart_part_limit
64
63
  end
65
64
 
66
- # The maximum number of file parts a request can contain. Accepting too
67
- # many parts can lead to the server running out of file handles.
65
+ # The maximum number of parts a request can contain. Accepting too many part
66
+ # can lead to the server running out of file handles.
68
67
  # Set to `0` for no limit.
69
- self.multipart_file_limit = (ENV['RACK_MULTIPART_PART_LIMIT'] || ENV['RACK_MULTIPART_FILE_LIMIT'] || 128).to_i
70
-
71
- # The maximum total number of parts a request can contain. Accepting too
72
- # many can lead to excessive memory use and parsing time.
73
- self.multipart_total_part_limit = (ENV['RACK_MULTIPART_TOTAL_PART_LIMIT'] || 4096).to_i
68
+ self.multipart_part_limit = (ENV['RACK_MULTIPART_PART_LIMIT'] || 128).to_i
74
69
 
75
70
  def self.param_depth_limit
76
71
  default_query_parser.param_depth_limit
@@ -129,7 +124,7 @@ module Rack
129
124
  when Hash
130
125
  value.map { |k, v|
131
126
  build_nested_query(v, prefix ? "#{prefix}[#{escape(k)}]" : escape(k))
132
- }.reject(&:empty?).join('&')
127
+ }.delete_if(&:empty?).join('&')
133
128
  when nil
134
129
  prefix
135
130
  else
@@ -140,10 +135,10 @@ module Rack
140
135
  module_function :build_nested_query
141
136
 
142
137
  def q_values(q_value_header)
143
- q_value_header.to_s.split(',').map do |part|
144
- value, parameters = part.split(';', 2).map(&:strip)
138
+ q_value_header.to_s.split(/\s*,\s*/).map do |part|
139
+ value, parameters = part.split(/\s*;\s*/, 2)
145
140
  quality = 1.0
146
- if md = /\Aq=([\d.]+)/.match(parameters)
141
+ if parameters && (md = /\Aq=([\d.]+)/.match(parameters))
147
142
  quality = md[1].to_f
148
143
  end
149
144
  [value, quality]
@@ -186,27 +181,26 @@ module Rack
186
181
  # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
187
182
 
188
183
  expanded_accept_encoding =
189
- accept_encoding.map { |m, q|
184
+ accept_encoding.each_with_object([]) do |(m, q), list|
190
185
  if m == "*"
191
- (available_encodings - accept_encoding.map { |m2, _| m2 }).map { |m2| [m2, q] }
186
+ (available_encodings - accept_encoding.map(&:first))
187
+ .each { |m2| list << [m2, q] }
192
188
  else
193
- [[m, q]]
189
+ list << [m, q]
194
190
  end
195
- }.inject([]) { |mem, list|
196
- mem + list
197
- }
191
+ end
198
192
 
199
- encoding_candidates = expanded_accept_encoding.sort_by { |_, q| -q }.map { |m, _| m }
193
+ encoding_candidates = expanded_accept_encoding.sort_by { |_, q| -q }.map!(&:first)
200
194
 
201
195
  unless encoding_candidates.include?("identity")
202
196
  encoding_candidates.push("identity")
203
197
  end
204
198
 
205
- expanded_accept_encoding.each { |m, q|
199
+ expanded_accept_encoding.each do |m, q|
206
200
  encoding_candidates.delete(m) if q == 0.0
207
- }
201
+ end
208
202
 
209
- return (encoding_candidates & available_encodings)[0]
203
+ (encoding_candidates & available_encodings)[0]
210
204
  end
211
205
  module_function :select_best_encoding
212
206
 
@@ -222,7 +216,7 @@ module Rack
222
216
  # precede those with less specific. Ordering with respect to other
223
217
  # attributes (e.g., Domain) is unspecified.
224
218
  cookies = parse_query(header, ';,') { |s| unescape(s) rescue s }
225
- cookies.each_with_object({}) { |(k,v), hash| hash[k] = Array === v ? v.first : v }
219
+ cookies.each_with_object({}) { |(k, v), hash| hash[k] = Array === v ? v.first : v }
226
220
  end
227
221
  module_function :parse_cookies_header
228
222
 
@@ -232,31 +226,7 @@ module Rack
232
226
  domain = "; domain=#{value[:domain]}" if value[:domain]
233
227
  path = "; path=#{value[:path]}" if value[:path]
234
228
  max_age = "; max-age=#{value[:max_age]}" if value[:max_age]
235
- # There is an RFC mess in the area of date formatting for Cookies. Not
236
- # only are there contradicting RFCs and examples within RFC text, but
237
- # there are also numerous conflicting names of fields and partially
238
- # cross-applicable specifications.
239
- #
240
- # These are best described in RFC 2616 3.3.1. This RFC text also
241
- # specifies that RFC 822 as updated by RFC 1123 is preferred. That is a
242
- # fixed length format with space-date delimited fields.
243
- #
244
- # See also RFC 1123 section 5.2.14.
245
- #
246
- # RFC 6265 also specifies "sane-cookie-date" as RFC 1123 date, defined
247
- # in RFC 2616 3.3.1. RFC 6265 also gives examples that clearly denote
248
- # the space delimited format. These formats are compliant with RFC 2822.
249
- #
250
- # For reference, all involved RFCs are:
251
- # RFC 822
252
- # RFC 1123
253
- # RFC 2109
254
- # RFC 2616
255
- # RFC 2822
256
- # RFC 2965
257
- # RFC 6265
258
- expires = "; expires=" +
259
- rfc2822(value[:expires].clone.gmtime) if value[:expires]
229
+ expires = "; expires=#{value[:expires].httpdate}" if value[:expires]
260
230
  secure = "; secure" if value[:secure]
261
231
  httponly = "; HttpOnly" if (value.key?(:httponly) ? value[:httponly] : value[:http_only])
262
232
  same_site =
@@ -264,11 +234,11 @@ module Rack
264
234
  when false, nil
265
235
  nil
266
236
  when :none, 'None', :None
267
- '; SameSite=None'.freeze
237
+ '; SameSite=None'
268
238
  when :lax, 'Lax', :Lax
269
- '; SameSite=Lax'.freeze
239
+ '; SameSite=Lax'
270
240
  when true, :strict, 'Strict', :Strict
271
- '; SameSite=Strict'.freeze
241
+ '; SameSite=Strict'
272
242
  else
273
243
  raise ArgumentError, "Invalid SameSite value: #{value[:same_site].inspect}"
274
244
  end
@@ -308,15 +278,15 @@ module Rack
308
278
  cookies = header
309
279
  end
310
280
 
311
- cookies.reject! { |cookie|
312
- if value[:domain]
313
- cookie =~ /\A#{escape(key)}=.*domain=#{value[:domain]}/
314
- elsif value[:path]
315
- cookie =~ /\A#{escape(key)}=.*path=#{value[:path]}/
316
- else
317
- cookie =~ /\A#{escape(key)}=/
318
- end
319
- }
281
+ regexp = if value[:domain]
282
+ /\A#{escape(key)}=.*domain=#{value[:domain]}/
283
+ elsif value[:path]
284
+ /\A#{escape(key)}=.*path=#{value[:path]}/
285
+ else
286
+ /\A#{escape(key)}=/
287
+ end
288
+
289
+ cookies.reject! { |cookie| regexp.match? cookie }
320
290
 
321
291
  cookies.join("\n")
322
292
  end
@@ -334,9 +304,9 @@ module Rack
334
304
  new_header = make_delete_cookie_header(header, key, value)
335
305
 
336
306
  add_cookie_to_header(new_header, key,
337
- {:value => '', :path => nil, :domain => nil,
338
- :max_age => '0',
339
- :expires => Time.at(0) }.merge(value))
307
+ { value: '', path: nil, domain: nil,
308
+ max_age: '0',
309
+ expires: Time.at(0) }.merge(value))
340
310
 
341
311
  end
342
312
  module_function :add_remove_cookie_to_header
@@ -376,23 +346,22 @@ module Rack
376
346
  return nil unless http_range && http_range =~ /bytes=([^;]+)/
377
347
  ranges = []
378
348
  $1.split(/,\s*/).each do |range_spec|
379
- return nil unless range_spec.include?('-')
380
- range = range_spec.split('-')
381
- r0, r1 = range[0], range[1]
382
- if r0.nil? || r0.empty?
383
- return nil if r1.nil?
349
+ return nil unless range_spec =~ /(\d*)-(\d*)/
350
+ r0, r1 = $1, $2
351
+ if r0.empty?
352
+ return nil if r1.empty?
384
353
  # suffix-byte-range-spec, represents trailing suffix of file
385
354
  r0 = size - r1.to_i
386
355
  r0 = 0 if r0 < 0
387
356
  r1 = size - 1
388
357
  else
389
358
  r0 = r0.to_i
390
- if r1.nil?
359
+ if r1.empty?
391
360
  r1 = size - 1
392
361
  else
393
362
  r1 = r1.to_i
394
363
  return nil if r1 < r0 # backwards range is syntactically invalid
395
- r1 = size-1 if r1 >= size
364
+ r1 = size - 1 if r1 >= size
396
365
  end
397
366
  end
398
367
  ranges << (r0..r1) if r0 <= r1
@@ -413,7 +382,7 @@ module Rack
413
382
  l = a.unpack("C*")
414
383
 
415
384
  r, i = 0, -1
416
- b.each_byte { |v| r |= v ^ l[i+=1] }
385
+ b.each_byte { |v| r |= v ^ l[i += 1] }
417
386
  r == 0
418
387
  end
419
388
  module_function :secure_compare
@@ -439,19 +408,17 @@ module Rack
439
408
  self.class.new(@for, app)
440
409
  end
441
410
 
442
- def context(env, app=@app)
411
+ def context(env, app = @app)
443
412
  recontext(app).call(env)
444
413
  end
445
414
  end
446
415
 
447
416
  # A case-insensitive Hash that preserves the original case of a
448
417
  # header when set.
449
- class HeaderHash < Hash
450
- def self.new(hash={})
451
- HeaderHash === hash ? hash : super(hash)
452
- end
453
-
454
- def initialize(hash={})
418
+ #
419
+ # @api private
420
+ class HeaderHash < Hash # :nodoc:
421
+ def initialize(hash = {})
455
422
  super()
456
423
  @names = {}
457
424
  hash.each { |k, v| self[k] = v }
@@ -471,7 +438,7 @@ module Rack
471
438
 
472
439
  def to_hash
473
440
  hash = {}
474
- each { |k,v| hash[k] = v }
441
+ each { |k, v| hash[k] = v }
475
442
  hash
476
443
  end
477
444
 
@@ -524,13 +491,14 @@ module Rack
524
491
 
525
492
  # Every standard HTTP code mapped to the appropriate message.
526
493
  # Generated with:
527
- # curl -s https://www.iana.org/assignments/http-status-codes/http-status-codes-1.csv | \
528
- # ruby -ne 'm = /^(\d{3}),(?!Unassigned|\(Unused\))([^,]+)/.match($_) and \
529
- # puts "#{m[1]} => \x27#{m[2].strip}\x27,"'
494
+ # curl -s https://www.iana.org/assignments/http-status-codes/http-status-codes-1.csv | \
495
+ # ruby -ne 'm = /^(\d{3}),(?!Unassigned|\(Unused\))([^,]+)/.match($_) and \
496
+ # puts "#{m[1]} => \x27#{m[2].strip}\x27,"'
530
497
  HTTP_STATUS_CODES = {
531
498
  100 => 'Continue',
532
499
  101 => 'Switching Protocols',
533
500
  102 => 'Processing',
501
+ 103 => 'Early Hints',
534
502
  200 => 'OK',
535
503
  201 => 'Created',
536
504
  202 => 'Accepted',
@@ -547,6 +515,7 @@ module Rack
547
515
  303 => 'See Other',
548
516
  304 => 'Not Modified',
549
517
  305 => 'Use Proxy',
518
+ 306 => '(Unused)',
550
519
  307 => 'Temporary Redirect',
551
520
  308 => 'Permanent Redirect',
552
521
  400 => 'Bad Request',
@@ -571,6 +540,7 @@ module Rack
571
540
  422 => 'Unprocessable Entity',
572
541
  423 => 'Locked',
573
542
  424 => 'Failed Dependency',
543
+ 425 => 'Too Early',
574
544
  426 => 'Upgrade Required',
575
545
  428 => 'Precondition Required',
576
546
  429 => 'Too Many Requests',
@@ -585,12 +555,13 @@ module Rack
585
555
  506 => 'Variant Also Negotiates',
586
556
  507 => 'Insufficient Storage',
587
557
  508 => 'Loop Detected',
558
+ 509 => 'Bandwidth Limit Exceeded',
588
559
  510 => 'Not Extended',
589
560
  511 => 'Network Authentication Required'
590
561
  }
591
562
 
592
563
  # Responses with HTTP status codes that should not have an entity body
593
- STATUS_WITH_NO_ENTITY_BODY = Set.new((100..199).to_a << 204 << 304)
564
+ STATUS_WITH_NO_ENTITY_BODY = Hash[((100..199).to_a << 204 << 304).product([true])]
594
565
 
595
566
  SYMBOL_TO_STATUS_CODE = Hash[*HTTP_STATUS_CODES.map { |code, message|
596
567
  [message.downcase.gsub(/\s|-|'/, '_').to_sym, code]
@@ -598,7 +569,7 @@ module Rack
598
569
 
599
570
  def status_code(status)
600
571
  if status.is_a?(Symbol)
601
- SYMBOL_TO_STATUS_CODE[status] || 500
572
+ SYMBOL_TO_STATUS_CODE.fetch(status) { raise ArgumentError, "Unrecognized status code #{status.inspect}" }
602
573
  else
603
574
  status.to_i
604
575
  end
@@ -619,11 +590,11 @@ module Rack
619
590
 
620
591
  clean.unshift '/' if parts.empty? || parts.first.empty?
621
592
 
622
- ::File.join(*clean)
593
+ ::File.join clean
623
594
  end
624
595
  module_function :clean_path_info
625
596
 
626
- NULL_BYTE = "\0".freeze
597
+ NULL_BYTE = "\0"
627
598
 
628
599
  def valid_path?(path)
629
600
  path.valid_encoding? && !path.include?(NULL_BYTE)
data/lib/rack.rb CHANGED
@@ -1,7 +1,9 @@
1
- # Copyright (C) 2007, 2008, 2009, 2010 Christian Neukirchen <purl.org/net/chneukirchen>
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (C) 2007-2019 Leah Neukirchen <http://leahneukirchen.org/infopage.html>
2
4
  #
3
5
  # Rack is freely distributable under the terms of an MIT-style license.
4
- # See COPYING or http://www.opensource.org/licenses/mit-license.php.
6
+ # See MIT-LICENSE or https://opensource.org/licenses/MIT.
5
7
 
6
8
  # The Rack main module, serving as a namespace for all core Rack
7
9
  # modules and classes.
@@ -11,80 +13,80 @@
11
13
 
12
14
  module Rack
13
15
  # The Rack protocol version number implemented.
14
- VERSION = [1,3]
16
+ VERSION = [1, 3]
15
17
 
16
18
  # Return the Rack protocol version as a dotted string.
17
19
  def self.version
18
20
  VERSION.join(".")
19
21
  end
20
22
 
21
- RELEASE = "2.0.9.4"
23
+ RELEASE = "2.1.0"
22
24
 
23
25
  # Return the Rack release as a dotted string.
24
26
  def self.release
25
27
  RELEASE
26
28
  end
27
29
 
28
- HTTP_HOST = 'HTTP_HOST'.freeze
29
- HTTP_VERSION = 'HTTP_VERSION'.freeze
30
- HTTPS = 'HTTPS'.freeze
31
- PATH_INFO = 'PATH_INFO'.freeze
32
- REQUEST_METHOD = 'REQUEST_METHOD'.freeze
33
- REQUEST_PATH = 'REQUEST_PATH'.freeze
34
- SCRIPT_NAME = 'SCRIPT_NAME'.freeze
35
- QUERY_STRING = 'QUERY_STRING'.freeze
36
- SERVER_PROTOCOL = 'SERVER_PROTOCOL'.freeze
37
- SERVER_NAME = 'SERVER_NAME'.freeze
38
- SERVER_ADDR = 'SERVER_ADDR'.freeze
39
- SERVER_PORT = 'SERVER_PORT'.freeze
40
- CACHE_CONTROL = 'Cache-Control'.freeze
41
- CONTENT_LENGTH = 'Content-Length'.freeze
42
- CONTENT_TYPE = 'Content-Type'.freeze
43
- SET_COOKIE = 'Set-Cookie'.freeze
44
- TRANSFER_ENCODING = 'Transfer-Encoding'.freeze
45
- HTTP_COOKIE = 'HTTP_COOKIE'.freeze
46
- ETAG = 'ETag'.freeze
30
+ HTTP_HOST = 'HTTP_HOST'
31
+ HTTP_VERSION = 'HTTP_VERSION'
32
+ HTTPS = 'HTTPS'
33
+ PATH_INFO = 'PATH_INFO'
34
+ REQUEST_METHOD = 'REQUEST_METHOD'
35
+ REQUEST_PATH = 'REQUEST_PATH'
36
+ SCRIPT_NAME = 'SCRIPT_NAME'
37
+ QUERY_STRING = 'QUERY_STRING'
38
+ SERVER_PROTOCOL = 'SERVER_PROTOCOL'
39
+ SERVER_NAME = 'SERVER_NAME'
40
+ SERVER_ADDR = 'SERVER_ADDR'
41
+ SERVER_PORT = 'SERVER_PORT'
42
+ CACHE_CONTROL = 'Cache-Control'
43
+ CONTENT_LENGTH = 'Content-Length'
44
+ CONTENT_TYPE = 'Content-Type'
45
+ SET_COOKIE = 'Set-Cookie'
46
+ TRANSFER_ENCODING = 'Transfer-Encoding'
47
+ HTTP_COOKIE = 'HTTP_COOKIE'
48
+ ETAG = 'ETag'
47
49
 
48
50
  # HTTP method verbs
49
- GET = 'GET'.freeze
50
- POST = 'POST'.freeze
51
- PUT = 'PUT'.freeze
52
- PATCH = 'PATCH'.freeze
53
- DELETE = 'DELETE'.freeze
54
- HEAD = 'HEAD'.freeze
55
- OPTIONS = 'OPTIONS'.freeze
56
- LINK = 'LINK'.freeze
57
- UNLINK = 'UNLINK'.freeze
58
- TRACE = 'TRACE'.freeze
51
+ GET = 'GET'
52
+ POST = 'POST'
53
+ PUT = 'PUT'
54
+ PATCH = 'PATCH'
55
+ DELETE = 'DELETE'
56
+ HEAD = 'HEAD'
57
+ OPTIONS = 'OPTIONS'
58
+ LINK = 'LINK'
59
+ UNLINK = 'UNLINK'
60
+ TRACE = 'TRACE'
59
61
 
60
62
  # Rack environment variables
61
- RACK_VERSION = 'rack.version'.freeze
62
- RACK_TEMPFILES = 'rack.tempfiles'.freeze
63
- RACK_ERRORS = 'rack.errors'.freeze
64
- RACK_LOGGER = 'rack.logger'.freeze
65
- RACK_INPUT = 'rack.input'.freeze
66
- RACK_SESSION = 'rack.session'.freeze
67
- RACK_SESSION_OPTIONS = 'rack.session.options'.freeze
68
- RACK_SHOWSTATUS_DETAIL = 'rack.showstatus.detail'.freeze
69
- RACK_MULTITHREAD = 'rack.multithread'.freeze
70
- RACK_MULTIPROCESS = 'rack.multiprocess'.freeze
71
- RACK_RUNONCE = 'rack.run_once'.freeze
72
- RACK_URL_SCHEME = 'rack.url_scheme'.freeze
73
- RACK_HIJACK = 'rack.hijack'.freeze
74
- RACK_IS_HIJACK = 'rack.hijack?'.freeze
75
- RACK_HIJACK_IO = 'rack.hijack_io'.freeze
76
- RACK_RECURSIVE_INCLUDE = 'rack.recursive.include'.freeze
77
- RACK_MULTIPART_BUFFER_SIZE = 'rack.multipart.buffer_size'.freeze
78
- RACK_MULTIPART_TEMPFILE_FACTORY = 'rack.multipart.tempfile_factory'.freeze
79
- RACK_REQUEST_FORM_INPUT = 'rack.request.form_input'.freeze
80
- RACK_REQUEST_FORM_HASH = 'rack.request.form_hash'.freeze
81
- RACK_REQUEST_FORM_VARS = 'rack.request.form_vars'.freeze
82
- RACK_REQUEST_COOKIE_HASH = 'rack.request.cookie_hash'.freeze
83
- RACK_REQUEST_COOKIE_STRING = 'rack.request.cookie_string'.freeze
84
- RACK_REQUEST_QUERY_HASH = 'rack.request.query_hash'.freeze
85
- RACK_REQUEST_QUERY_STRING = 'rack.request.query_string'.freeze
86
- RACK_METHODOVERRIDE_ORIGINAL_METHOD = 'rack.methodoverride.original_method'.freeze
87
- RACK_SESSION_UNPACKED_COOKIE_DATA = 'rack.session.unpacked_cookie_data'.freeze
63
+ RACK_VERSION = 'rack.version'
64
+ RACK_TEMPFILES = 'rack.tempfiles'
65
+ RACK_ERRORS = 'rack.errors'
66
+ RACK_LOGGER = 'rack.logger'
67
+ RACK_INPUT = 'rack.input'
68
+ RACK_SESSION = 'rack.session'
69
+ RACK_SESSION_OPTIONS = 'rack.session.options'
70
+ RACK_SHOWSTATUS_DETAIL = 'rack.showstatus.detail'
71
+ RACK_MULTITHREAD = 'rack.multithread'
72
+ RACK_MULTIPROCESS = 'rack.multiprocess'
73
+ RACK_RUNONCE = 'rack.run_once'
74
+ RACK_URL_SCHEME = 'rack.url_scheme'
75
+ RACK_HIJACK = 'rack.hijack'
76
+ RACK_IS_HIJACK = 'rack.hijack?'
77
+ RACK_HIJACK_IO = 'rack.hijack_io'
78
+ RACK_RECURSIVE_INCLUDE = 'rack.recursive.include'
79
+ RACK_MULTIPART_BUFFER_SIZE = 'rack.multipart.buffer_size'
80
+ RACK_MULTIPART_TEMPFILE_FACTORY = 'rack.multipart.tempfile_factory'
81
+ RACK_REQUEST_FORM_INPUT = 'rack.request.form_input'
82
+ RACK_REQUEST_FORM_HASH = 'rack.request.form_hash'
83
+ RACK_REQUEST_FORM_VARS = 'rack.request.form_vars'
84
+ RACK_REQUEST_COOKIE_HASH = 'rack.request.cookie_hash'
85
+ RACK_REQUEST_COOKIE_STRING = 'rack.request.cookie_string'
86
+ RACK_REQUEST_QUERY_HASH = 'rack.request.query_hash'
87
+ RACK_REQUEST_QUERY_STRING = 'rack.request.query_string'
88
+ RACK_METHODOVERRIDE_ORIGINAL_METHOD = 'rack.methodoverride.original_method'
89
+ RACK_SESSION_UNPACKED_COOKIE_DATA = 'rack.session.unpacked_cookie_data'
88
90
 
89
91
  autoload :Builder, "rack/builder"
90
92
  autoload :BodyProxy, "rack/body_proxy"
@@ -97,6 +99,7 @@ module Rack
97
99
  autoload :ContentType, "rack/content_type"
98
100
  autoload :ETag, "rack/etag"
99
101
  autoload :File, "rack/file"
102
+ autoload :Files, "rack/files"
100
103
  autoload :Deflater, "rack/deflater"
101
104
  autoload :Directory, "rack/directory"
102
105
  autoload :ForwardRequest, "rack/recursive"
data/rack.gemspec CHANGED
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  Gem::Specification.new do |s|
2
- s.name = "rack"
4
+ s.name = "rack"
3
5
  s.version = File.read('lib/rack.rb')[/RELEASE += +([\"\'])([\d][\w\.]+)\1/, 2]
4
6
  s.platform = Gem::Platform::RUBY
5
7
  s.summary = "a modular Ruby webserver interface"
@@ -15,20 +17,28 @@ middleware) into a single method call.
15
17
  Also see https://rack.github.io/.
16
18
  EOF
17
19
 
18
- s.files = Dir['{bin/*,contrib/*,example/*,lib/**/*,test/**/*}'] +
19
- %w(COPYING rack.gemspec Rakefile README.rdoc SPEC)
20
+ s.files = Dir['{bin/*,contrib/*,example/*,lib/**/*}'] +
21
+ %w(MIT-LICENSE rack.gemspec Rakefile README.rdoc SPEC)
20
22
  s.bindir = 'bin'
21
- s.executables << 'rackup'
22
- s.require_path = 'lib'
23
- s.extra_rdoc_files = ['README.rdoc', 'HISTORY.md']
24
- s.test_files = Dir['test/spec_*.rb']
23
+ s.executables << 'rackup'
24
+ s.require_path = 'lib'
25
+ s.extra_rdoc_files = ['README.rdoc', 'CHANGELOG.md']
25
26
 
26
27
  s.author = 'Leah Neukirchen'
27
28
  s.email = 'leah@vuxu.org'
28
29
  s.homepage = 'https://rack.github.io/'
29
30
  s.required_ruby_version = '>= 2.2.2'
31
+ s.metadata = {
32
+ "bug_tracker_uri" => "https://github.com/rack/rack/issues",
33
+ "changelog_uri" => "https://github.com/rack/rack/blob/master/CHANGELOG.md",
34
+ "documentation_uri" => "https://rubydoc.info/github/rack/rack",
35
+ "homepage_uri" => "https://rack.github.io",
36
+ "mailing_list_uri" => "https://groups.google.com/forum/#!forum/rack-devel",
37
+ "source_code_uri" => "https://github.com/rack/rack"
38
+ }
30
39
 
31
40
  s.add_development_dependency 'minitest', "~> 5.0"
32
41
  s.add_development_dependency 'minitest-sprint'
42
+ s.add_development_dependency 'minitest-global_expectations'
33
43
  s.add_development_dependency 'rake'
34
44
  end