rack 2.0.9 → 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} +220 -155
  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 +38 -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 +28 -17
  30. data/lib/rack/directory.rb +17 -14
  31. data/lib/rack/etag.rb +3 -1
  32. data/lib/rack/events.rb +5 -3
  33. data/lib/rack/file.rb +5 -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 -16
  65. data/lib/rack/session/abstract/id.rb +40 -22
  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 +54 -71
  75. data/rack.gemspec +17 -7
  76. metadata +30 -172
  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 -95
  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 -515
  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 -722
  160. data/test/spec_null_logger.rb +0 -21
  161. data/test/spec_recursive.rb +0 -75
  162. data/test/spec_request.rb +0 -1407
  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
@@ -1,99 +1,10 @@
1
- # AUTHOR: blink <blinketje@gmail.com>; blink#ruby-lang@irc.freenode.net
1
+ # frozen_string_literal: true
2
2
 
3
- require 'rack/session/abstract/id'
4
- require 'memcache'
3
+ require 'rack/session/dalli'
5
4
 
6
5
  module Rack
7
6
  module Session
8
- # Rack::Session::Memcache provides simple cookie based session management.
9
- # Session data is stored in memcached. The corresponding session key is
10
- # maintained in the cookie.
11
- # You may treat Session::Memcache as you would Session::Pool with the
12
- # following caveats.
13
- #
14
- # * Setting :expire_after to 0 would note to the Memcache server to hang
15
- # onto the session data until it would drop it according to it's own
16
- # specifications. However, the cookie sent to the client would expire
17
- # immediately.
18
- #
19
- # Note that memcache does drop data before it may be listed to expire. For
20
- # a full description of behaviour, please see memcache's documentation.
21
-
22
- class Memcache < Abstract::PersistedSecure
23
- attr_reader :mutex, :pool
24
-
25
- DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge \
26
- :namespace => 'rack:session',
27
- :memcache_server => 'localhost:11211'
28
-
29
- def initialize(app, options={})
30
- super
31
-
32
- @mutex = Mutex.new
33
- mserv = @default_options[:memcache_server]
34
- mopts = @default_options.reject{|k,v| !MemCache::DEFAULT_OPTIONS.include? k }
35
-
36
- @pool = options[:cache] || MemCache.new(mserv, mopts)
37
- unless @pool.active? and @pool.servers.any?(&:alive?)
38
- raise 'No memcache servers'
39
- end
40
- end
41
-
42
- def generate_sid
43
- loop do
44
- sid = super
45
- break sid unless @pool.get(sid.private_id, true)
46
- end
47
- end
48
-
49
- def find_session(req, sid)
50
- with_lock(req) do
51
- unless sid and session = get_session_with_fallback(sid)
52
- sid, session = generate_sid, {}
53
- unless /^STORED/ =~ @pool.add(sid.private_id, session)
54
- raise "Session collision on '#{sid.inspect}'"
55
- end
56
- end
57
- [sid, session]
58
- end
59
- end
60
-
61
- def write_session(req, session_id, new_session, options)
62
- expiry = options[:expire_after]
63
- expiry = expiry.nil? ? 0 : expiry + 1
64
-
65
- with_lock(req) do
66
- @pool.set session_id.private_id, new_session, expiry
67
- session_id
68
- end
69
- end
70
-
71
- def delete_session(req, session_id, options)
72
- with_lock(req) do
73
- @pool.delete(session_id.public_id)
74
- @pool.delete(session_id.private_id)
75
- generate_sid unless options[:drop]
76
- end
77
- end
78
-
79
- def with_lock(req)
80
- @mutex.lock if req.multithread?
81
- yield
82
- rescue MemCache::MemCacheError, Errno::ECONNREFUSED
83
- if $VERBOSE
84
- warn "#{self} is unable to find memcached server."
85
- warn $!.inspect
86
- end
87
- raise
88
- ensure
89
- @mutex.unlock if @mutex.locked?
90
- end
91
-
92
- private
93
-
94
- def get_session_with_fallback(sid)
95
- @pool.get(sid.private_id) || @pool.get(sid.public_id)
96
- end
97
- end
7
+ warn "Rack::Session::Memcache is deprecated, please use Rack::Session::Dalli from 'dalli' gem instead."
8
+ Memcache = Dalli
98
9
  end
99
10
  end
@@ -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
 
@@ -211,7 +216,7 @@ module Rack
211
216
  # precede those with less specific. Ordering with respect to other
212
217
  # attributes (e.g., Domain) is unspecified.
213
218
  cookies = parse_query(header, ';,') { |s| unescape(s) rescue s }
214
- 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 }
215
220
  end
216
221
  module_function :parse_cookies_header
217
222
 
@@ -221,31 +226,7 @@ module Rack
221
226
  domain = "; domain=#{value[:domain]}" if value[:domain]
222
227
  path = "; path=#{value[:path]}" if value[:path]
223
228
  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]
229
+ expires = "; expires=#{value[:expires].httpdate}" if value[:expires]
249
230
  secure = "; secure" if value[:secure]
250
231
  httponly = "; HttpOnly" if (value.key?(:httponly) ? value[:httponly] : value[:http_only])
251
232
  same_site =
@@ -253,11 +234,11 @@ module Rack
253
234
  when false, nil
254
235
  nil
255
236
  when :none, 'None', :None
256
- '; SameSite=None'.freeze
237
+ '; SameSite=None'
257
238
  when :lax, 'Lax', :Lax
258
- '; SameSite=Lax'.freeze
239
+ '; SameSite=Lax'
259
240
  when true, :strict, 'Strict', :Strict
260
- '; SameSite=Strict'.freeze
241
+ '; SameSite=Strict'
261
242
  else
262
243
  raise ArgumentError, "Invalid SameSite value: #{value[:same_site].inspect}"
263
244
  end
@@ -297,15 +278,15 @@ module Rack
297
278
  cookies = header
298
279
  end
299
280
 
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
- }
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 }
309
290
 
310
291
  cookies.join("\n")
311
292
  end
@@ -323,9 +304,9 @@ module Rack
323
304
  new_header = make_delete_cookie_header(header, key, value)
324
305
 
325
306
  add_cookie_to_header(new_header, key,
326
- {:value => '', :path => nil, :domain => nil,
327
- :max_age => '0',
328
- :expires => Time.at(0) }.merge(value))
307
+ { value: '', path: nil, domain: nil,
308
+ max_age: '0',
309
+ expires: Time.at(0) }.merge(value))
329
310
 
330
311
  end
331
312
  module_function :add_remove_cookie_to_header
@@ -366,7 +347,7 @@ module Rack
366
347
  ranges = []
367
348
  $1.split(/,\s*/).each do |range_spec|
368
349
  return nil unless range_spec =~ /(\d*)-(\d*)/
369
- r0,r1 = $1, $2
350
+ r0, r1 = $1, $2
370
351
  if r0.empty?
371
352
  return nil if r1.empty?
372
353
  # suffix-byte-range-spec, represents trailing suffix of file
@@ -380,7 +361,7 @@ module Rack
380
361
  else
381
362
  r1 = r1.to_i
382
363
  return nil if r1 < r0 # backwards range is syntactically invalid
383
- r1 = size-1 if r1 >= size
364
+ r1 = size - 1 if r1 >= size
384
365
  end
385
366
  end
386
367
  ranges << (r0..r1) if r0 <= r1
@@ -401,7 +382,7 @@ module Rack
401
382
  l = a.unpack("C*")
402
383
 
403
384
  r, i = 0, -1
404
- b.each_byte { |v| r |= v ^ l[i+=1] }
385
+ b.each_byte { |v| r |= v ^ l[i += 1] }
405
386
  r == 0
406
387
  end
407
388
  module_function :secure_compare
@@ -427,19 +408,17 @@ module Rack
427
408
  self.class.new(@for, app)
428
409
  end
429
410
 
430
- def context(env, app=@app)
411
+ def context(env, app = @app)
431
412
  recontext(app).call(env)
432
413
  end
433
414
  end
434
415
 
435
416
  # A case-insensitive Hash that preserves the original case of a
436
417
  # 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={})
418
+ #
419
+ # @api private
420
+ class HeaderHash < Hash # :nodoc:
421
+ def initialize(hash = {})
443
422
  super()
444
423
  @names = {}
445
424
  hash.each { |k, v| self[k] = v }
@@ -459,7 +438,7 @@ module Rack
459
438
 
460
439
  def to_hash
461
440
  hash = {}
462
- each { |k,v| hash[k] = v }
441
+ each { |k, v| hash[k] = v }
463
442
  hash
464
443
  end
465
444
 
@@ -512,13 +491,14 @@ module Rack
512
491
 
513
492
  # Every standard HTTP code mapped to the appropriate message.
514
493
  # 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,"'
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,"'
518
497
  HTTP_STATUS_CODES = {
519
498
  100 => 'Continue',
520
499
  101 => 'Switching Protocols',
521
500
  102 => 'Processing',
501
+ 103 => 'Early Hints',
522
502
  200 => 'OK',
523
503
  201 => 'Created',
524
504
  202 => 'Accepted',
@@ -535,6 +515,7 @@ module Rack
535
515
  303 => 'See Other',
536
516
  304 => 'Not Modified',
537
517
  305 => 'Use Proxy',
518
+ 306 => '(Unused)',
538
519
  307 => 'Temporary Redirect',
539
520
  308 => 'Permanent Redirect',
540
521
  400 => 'Bad Request',
@@ -559,6 +540,7 @@ module Rack
559
540
  422 => 'Unprocessable Entity',
560
541
  423 => 'Locked',
561
542
  424 => 'Failed Dependency',
543
+ 425 => 'Too Early',
562
544
  426 => 'Upgrade Required',
563
545
  428 => 'Precondition Required',
564
546
  429 => 'Too Many Requests',
@@ -573,12 +555,13 @@ module Rack
573
555
  506 => 'Variant Also Negotiates',
574
556
  507 => 'Insufficient Storage',
575
557
  508 => 'Loop Detected',
558
+ 509 => 'Bandwidth Limit Exceeded',
576
559
  510 => 'Not Extended',
577
560
  511 => 'Network Authentication Required'
578
561
  }
579
562
 
580
563
  # 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)
564
+ STATUS_WITH_NO_ENTITY_BODY = Hash[((100..199).to_a << 204 << 304).product([true])]
582
565
 
583
566
  SYMBOL_TO_STATUS_CODE = Hash[*HTTP_STATUS_CODES.map { |code, message|
584
567
  [message.downcase.gsub(/\s|-|'/, '_').to_sym, code]
@@ -586,7 +569,7 @@ module Rack
586
569
 
587
570
  def status_code(status)
588
571
  if status.is_a?(Symbol)
589
- SYMBOL_TO_STATUS_CODE[status] || 500
572
+ SYMBOL_TO_STATUS_CODE.fetch(status) { raise ArgumentError, "Unrecognized status code #{status.inspect}" }
590
573
  else
591
574
  status.to_i
592
575
  end
@@ -607,11 +590,11 @@ module Rack
607
590
 
608
591
  clean.unshift '/' if parts.empty? || parts.first.empty?
609
592
 
610
- ::File.join(*clean)
593
+ ::File.join clean
611
594
  end
612
595
  module_function :clean_path_info
613
596
 
614
- NULL_BYTE = "\0".freeze
597
+ NULL_BYTE = "\0"
615
598
 
616
599
  def valid_path?(path)
617
600
  path.valid_encoding? && !path.include?(NULL_BYTE)