rack 2.0.9.3 → 2.2.1

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 (191) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +681 -0
  3. data/CONTRIBUTING.md +136 -0
  4. data/{COPYING → MIT-LICENSE} +4 -2
  5. data/README.rdoc +152 -162
  6. data/Rakefile +37 -23
  7. data/{SPEC → SPEC.rdoc} +35 -10
  8. data/bin/rackup +1 -0
  9. data/example/lobster.ru +2 -0
  10. data/example/protectedlobster.rb +3 -1
  11. data/example/protectedlobster.ru +2 -0
  12. data/lib/rack/auth/abstract/handler.rb +3 -1
  13. data/lib/rack/auth/abstract/request.rb +1 -1
  14. data/lib/rack/auth/basic.rb +7 -4
  15. data/lib/rack/auth/digest/md5.rb +13 -11
  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 +5 -3
  19. data/lib/rack/body_proxy.rb +15 -14
  20. data/lib/rack/builder.rb +116 -23
  21. data/lib/rack/cascade.rb +28 -12
  22. data/lib/rack/chunked.rb +68 -20
  23. data/lib/rack/common_logger.rb +33 -28
  24. data/lib/rack/conditional_get.rb +20 -16
  25. data/lib/rack/config.rb +2 -0
  26. data/lib/rack/content_length.rb +8 -7
  27. data/lib/rack/content_type.rb +5 -4
  28. data/lib/rack/core_ext/regexp.rb +14 -0
  29. data/lib/rack/deflater.rb +59 -34
  30. data/lib/rack/directory.rb +84 -64
  31. data/lib/rack/etag.rb +5 -4
  32. data/lib/rack/events.rb +19 -20
  33. data/lib/rack/file.rb +4 -173
  34. data/lib/rack/files.rb +218 -0
  35. data/lib/rack/handler/cgi.rb +2 -3
  36. data/lib/rack/handler/fastcgi.rb +4 -4
  37. data/lib/rack/handler/lsws.rb +3 -3
  38. data/lib/rack/handler/scgi.rb +9 -8
  39. data/lib/rack/handler/thin.rb +17 -11
  40. data/lib/rack/handler/webrick.rb +15 -6
  41. data/lib/rack/handler.rb +7 -2
  42. data/lib/rack/head.rb +1 -1
  43. data/lib/rack/lint.rb +72 -26
  44. data/lib/rack/lobster.rb +10 -10
  45. data/lib/rack/lock.rb +2 -1
  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 +97 -20
  51. data/lib/rack/multipart/generator.rb +17 -13
  52. data/lib/rack/multipart/parser.rb +58 -73
  53. data/lib/rack/multipart/uploaded_file.rb +15 -7
  54. data/lib/rack/multipart.rb +7 -4
  55. data/lib/rack/null_logger.rb +2 -0
  56. data/lib/rack/query_parser.rb +53 -28
  57. data/lib/rack/recursive.rb +7 -5
  58. data/lib/rack/reloader.rb +8 -4
  59. data/lib/rack/request.rb +220 -61
  60. data/lib/rack/response.rb +127 -44
  61. data/lib/rack/rewindable_input.rb +4 -3
  62. data/lib/rack/runtime.rb +6 -4
  63. data/lib/rack/sendfile.rb +13 -9
  64. data/lib/rack/server.rb +95 -24
  65. data/lib/rack/session/abstract/id.rb +33 -21
  66. data/lib/rack/session/cookie.rb +12 -12
  67. data/lib/rack/session/memcache.rb +4 -93
  68. data/lib/rack/session/pool.rb +5 -3
  69. data/lib/rack/show_exceptions.rb +17 -13
  70. data/lib/rack/show_status.rb +5 -5
  71. data/lib/rack/static.rb +23 -11
  72. data/lib/rack/tempfile_reaper.rb +1 -1
  73. data/lib/rack/urlmap.rb +12 -6
  74. data/lib/rack/utils.rb +105 -130
  75. data/lib/rack/version.rb +29 -0
  76. data/lib/rack.rb +67 -73
  77. data/rack.gemspec +40 -28
  78. metadata +39 -182
  79. data/HISTORY.md +0 -520
  80. data/test/builder/an_underscore_app.rb +0 -5
  81. data/test/builder/anything.rb +0 -5
  82. data/test/builder/comment.ru +0 -4
  83. data/test/builder/end.ru +0 -5
  84. data/test/builder/line.ru +0 -1
  85. data/test/builder/options.ru +0 -2
  86. data/test/cgi/assets/folder/test.js +0 -1
  87. data/test/cgi/assets/fonts/font.eot +0 -1
  88. data/test/cgi/assets/images/image.png +0 -1
  89. data/test/cgi/assets/index.html +0 -1
  90. data/test/cgi/assets/javascripts/app.js +0 -1
  91. data/test/cgi/assets/stylesheets/app.css +0 -1
  92. data/test/cgi/lighttpd.conf +0 -26
  93. data/test/cgi/rackup_stub.rb +0 -6
  94. data/test/cgi/sample_rackup.ru +0 -5
  95. data/test/cgi/test +0 -9
  96. data/test/cgi/test+directory/test+file +0 -1
  97. data/test/cgi/test.fcgi +0 -9
  98. data/test/cgi/test.gz +0 -0
  99. data/test/cgi/test.ru +0 -5
  100. data/test/gemloader.rb +0 -10
  101. data/test/helper.rb +0 -34
  102. data/test/multipart/bad_robots +0 -259
  103. data/test/multipart/binary +0 -0
  104. data/test/multipart/content_type_and_no_filename +0 -6
  105. data/test/multipart/empty +0 -10
  106. data/test/multipart/fail_16384_nofile +0 -814
  107. data/test/multipart/file1.txt +0 -1
  108. data/test/multipart/filename_and_modification_param +0 -7
  109. data/test/multipart/filename_and_no_name +0 -6
  110. data/test/multipart/filename_with_encoded_words +0 -7
  111. data/test/multipart/filename_with_escaped_quotes +0 -6
  112. data/test/multipart/filename_with_escaped_quotes_and_modification_param +0 -7
  113. data/test/multipart/filename_with_null_byte +0 -7
  114. data/test/multipart/filename_with_percent_escaped_quotes +0 -6
  115. data/test/multipart/filename_with_single_quote +0 -7
  116. data/test/multipart/filename_with_unescaped_percentages +0 -6
  117. data/test/multipart/filename_with_unescaped_percentages2 +0 -6
  118. data/test/multipart/filename_with_unescaped_percentages3 +0 -6
  119. data/test/multipart/filename_with_unescaped_quotes +0 -6
  120. data/test/multipart/ie +0 -6
  121. data/test/multipart/invalid_character +0 -6
  122. data/test/multipart/mixed_files +0 -21
  123. data/test/multipart/nested +0 -10
  124. data/test/multipart/none +0 -9
  125. data/test/multipart/quoted +0 -15
  126. data/test/multipart/rack-logo.png +0 -0
  127. data/test/multipart/semicolon +0 -6
  128. data/test/multipart/text +0 -15
  129. data/test/multipart/three_files_three_fields +0 -31
  130. data/test/multipart/unity3d_wwwform +0 -11
  131. data/test/multipart/webkit +0 -32
  132. data/test/rackup/config.ru +0 -31
  133. data/test/registering_handler/rack/handler/registering_myself.rb +0 -8
  134. data/test/spec_auth_basic.rb +0 -89
  135. data/test/spec_auth_digest.rb +0 -260
  136. data/test/spec_body_proxy.rb +0 -85
  137. data/test/spec_builder.rb +0 -233
  138. data/test/spec_cascade.rb +0 -63
  139. data/test/spec_cgi.rb +0 -84
  140. data/test/spec_chunked.rb +0 -103
  141. data/test/spec_common_logger.rb +0 -107
  142. data/test/spec_conditional_get.rb +0 -103
  143. data/test/spec_config.rb +0 -23
  144. data/test/spec_content_length.rb +0 -86
  145. data/test/spec_content_type.rb +0 -46
  146. data/test/spec_deflater.rb +0 -375
  147. data/test/spec_directory.rb +0 -148
  148. data/test/spec_etag.rb +0 -108
  149. data/test/spec_events.rb +0 -133
  150. data/test/spec_fastcgi.rb +0 -85
  151. data/test/spec_file.rb +0 -264
  152. data/test/spec_handler.rb +0 -57
  153. data/test/spec_head.rb +0 -46
  154. data/test/spec_lint.rb +0 -520
  155. data/test/spec_lobster.rb +0 -59
  156. data/test/spec_lock.rb +0 -204
  157. data/test/spec_logger.rb +0 -24
  158. data/test/spec_media_type.rb +0 -42
  159. data/test/spec_method_override.rb +0 -110
  160. data/test/spec_mime.rb +0 -51
  161. data/test/spec_mock.rb +0 -359
  162. data/test/spec_multipart.rb +0 -721
  163. data/test/spec_null_logger.rb +0 -21
  164. data/test/spec_recursive.rb +0 -75
  165. data/test/spec_request.rb +0 -1423
  166. data/test/spec_response.rb +0 -528
  167. data/test/spec_rewindable_input.rb +0 -128
  168. data/test/spec_runtime.rb +0 -50
  169. data/test/spec_sendfile.rb +0 -125
  170. data/test/spec_server.rb +0 -193
  171. data/test/spec_session_abstract_id.rb +0 -31
  172. data/test/spec_session_abstract_session_hash.rb +0 -45
  173. data/test/spec_session_cookie.rb +0 -442
  174. data/test/spec_session_memcache.rb +0 -357
  175. data/test/spec_session_persisted_secure_secure_session_hash.rb +0 -73
  176. data/test/spec_session_pool.rb +0 -247
  177. data/test/spec_show_exceptions.rb +0 -93
  178. data/test/spec_show_status.rb +0 -104
  179. data/test/spec_static.rb +0 -184
  180. data/test/spec_tempfile_reaper.rb +0 -64
  181. data/test/spec_thin.rb +0 -96
  182. data/test/spec_urlmap.rb +0 -237
  183. data/test/spec_utils.rb +0 -742
  184. data/test/spec_version.rb +0 -11
  185. data/test/spec_webrick.rb +0 -206
  186. data/test/static/another/index.html +0 -1
  187. data/test/static/foo.html +0 -1
  188. data/test/static/index.html +0 -1
  189. data/test/testrequest.rb +0 -78
  190. data/test/unregistered_handler/rack/handler/unregistered.rb +0 -7
  191. data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +0 -7
@@ -1,10 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # AUTHOR: blink <blinketje@gmail.com>; blink#ruby-lang@irc.freenode.net
2
4
  # bugrep: Andreas Zehnder
3
5
 
4
- require 'rack'
6
+ require_relative '../../../rack'
5
7
  require 'time'
6
- require 'rack/request'
7
- require 'rack/response'
8
8
  require 'securerandom'
9
9
  require 'digest/sha2'
10
10
 
@@ -84,7 +84,12 @@ module Rack
84
84
  @data[key.to_s]
85
85
  end
86
86
 
87
- def fetch(key, default=Unspecified, &block)
87
+ def dig(key, *keys)
88
+ load_for_read!
89
+ @data.dig(key.to_s, *keys)
90
+ end
91
+
92
+ def fetch(key, default = Unspecified, &block)
88
93
  load_for_read!
89
94
  if default == Unspecified
90
95
  @data.fetch(key.to_s, &block)
@@ -187,8 +192,9 @@ module Rack
187
192
  end
188
193
 
189
194
  def stringify_keys(other)
195
+ # Use transform_keys after dropping Ruby 2.4 support
190
196
  hash = {}
191
- other.each do |key, value|
197
+ other.to_hash.each do |key, value|
192
198
  hash[key.to_s] = value
193
199
  end
194
200
  hash
@@ -197,8 +203,8 @@ module Rack
197
203
 
198
204
  # ID sets up a basic framework for implementing an id based sessioning
199
205
  # service. Cookies sent to the client for maintaining sessions will only
200
- # contain an id reference. Only #find_session and #write_session are
201
- # required to be overwritten.
206
+ # contain an id reference. Only #find_session, #write_session and
207
+ # #delete_session are required to be overwritten.
202
208
  #
203
209
  # All parameters are optional.
204
210
  # * :key determines the name of the cookie, by default it is
@@ -226,22 +232,22 @@ module Rack
226
232
 
227
233
  class Persisted
228
234
  DEFAULT_OPTIONS = {
229
- :key => RACK_SESSION,
230
- :path => '/',
231
- :domain => nil,
232
- :expire_after => nil,
233
- :secure => false,
234
- :httponly => true,
235
- :defer => false,
236
- :renew => false,
237
- :sidbits => 128,
238
- :cookie_only => true,
239
- :secure_random => ::SecureRandom
235
+ key: RACK_SESSION,
236
+ path: '/',
237
+ domain: nil,
238
+ expire_after: nil,
239
+ secure: false,
240
+ httponly: true,
241
+ defer: false,
242
+ renew: false,
243
+ sidbits: 128,
244
+ cookie_only: true,
245
+ secure_random: ::SecureRandom
240
246
  }.freeze
241
247
 
242
248
  attr_reader :key, :default_options, :sid_secure
243
249
 
244
- def initialize(app, options={})
250
+ def initialize(app, options = {})
245
251
  @app = app
246
252
  @default_options = self.class::DEFAULT_OPTIONS.merge(options)
247
253
  @key = @default_options.delete(:key)
@@ -253,7 +259,7 @@ module Rack
253
259
  context(env)
254
260
  end
255
261
 
256
- def context(env, app=@app)
262
+ def context(env, app = @app)
257
263
  req = make_request env
258
264
  prepare_session(req)
259
265
  status, headers, body = app.call(req.env)
@@ -376,7 +382,7 @@ module Rack
376
382
 
377
383
  session.send(:load!) unless loaded_session?(session)
378
384
  session_id ||= session.id
379
- session_data = session.to_hash.delete_if { |k,v| v.nil? }
385
+ session_data = session.to_hash.delete_if { |k, v| v.nil? }
380
386
 
381
387
  if not data = write_session(req, session_id, session_data, options)
382
388
  req.get_header(RACK_ERRORS).puts("Warning! #{self.class.name} failed to save session. Content dropped.")
@@ -387,6 +393,12 @@ module Rack
387
393
  cookie[:value] = cookie_value(data)
388
394
  cookie[:expires] = Time.now + options[:expire_after] if options[:expire_after]
389
395
  cookie[:expires] = Time.now + options[:max_age] if options[:max_age]
396
+
397
+ if @same_site.respond_to? :call
398
+ cookie[:same_site] = @same_site.call(req, res)
399
+ else
400
+ cookie[:same_site] = @same_site
401
+ end
390
402
  set_cookie(req, res, cookie.merge!(options))
391
403
  end
392
404
  end
@@ -1,9 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'openssl'
2
4
  require 'zlib'
3
- require 'rack/request'
4
- require 'rack/response'
5
- require 'rack/session/abstract/id'
5
+ require_relative 'abstract/id'
6
6
  require 'json'
7
+ require 'base64'
7
8
 
8
9
  module Rack
9
10
 
@@ -49,11 +50,11 @@ module Rack
49
50
  # Encode session cookies as Base64
50
51
  class Base64
51
52
  def encode(str)
52
- [str].pack('m')
53
+ ::Base64.strict_encode64(str)
53
54
  end
54
55
 
55
56
  def decode(str)
56
- str.unpack('m').first
57
+ ::Base64.decode64(str)
57
58
  end
58
59
 
59
60
  # Encode session cookies as Marshaled Base64 data
@@ -103,7 +104,7 @@ module Rack
103
104
 
104
105
  attr_reader :coder
105
106
 
106
- def initialize(app, options={})
107
+ def initialize(app, options = {})
107
108
  @secrets = options.values_at(:secret, :old_secret).compact
108
109
  @hmac = options.fetch(:hmac, OpenSSL::Digest::SHA1)
109
110
 
@@ -116,8 +117,9 @@ module Rack
116
117
 
117
118
  Called from: #{caller[0]}.
118
119
  MSG
119
- @coder = options[:coder] ||= Base64::Marshal.new
120
- super(app, options.merge!(:cookie_only => true))
120
+ @coder = options[:coder] ||= Base64::Marshal.new
121
+ @same_site = options.delete :same_site
122
+ super(app, options.merge!(cookie_only: true))
121
123
  end
122
124
 
123
125
  private
@@ -137,9 +139,7 @@ module Rack
137
139
  session_data = request.cookies[@key]
138
140
 
139
141
  if @secrets.size > 0 && session_data
140
- digest, session_data = session_data.reverse.split("--", 2)
141
- digest.reverse! if digest
142
- session_data.reverse! if session_data
142
+ session_data, _, digest = session_data.rpartition('--')
143
143
  session_data = nil unless digest_match?(session_data, digest)
144
144
  end
145
145
 
@@ -147,7 +147,7 @@ module Rack
147
147
  end
148
148
  end
149
149
 
150
- def persistent_session_id!(data, sid=nil)
150
+ def persistent_session_id!(data, sid = nil)
151
151
  data ||= {}
152
152
  data["session_id"] ||= sid || generate_sid
153
153
  data
@@ -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,9 +1,11 @@
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
4
6
  # sergio, threadiness and bugreps
5
7
 
6
- require 'rack/session/abstract/id'
8
+ require_relative 'abstract/id'
7
9
  require 'thread'
8
10
 
9
11
  module Rack
@@ -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,7 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'ostruct'
2
4
  require 'erb'
3
- require 'rack/request'
4
- require 'rack/utils'
5
5
 
6
6
  module Rack
7
7
  # Rack::ShowExceptions catches all exceptions raised from the app it
@@ -55,7 +55,7 @@ module Rack
55
55
  private :accepts_html?
56
56
 
57
57
  def dump_exception(exception)
58
- string = "#{exception.class}: #{exception.message}\n"
58
+ string = "#{exception.class}: #{exception.message}\n".dup
59
59
  string << exception.backtrace.map { |l| "\t#{l}" }.join("\n")
60
60
  string
61
61
  end
@@ -77,13 +77,13 @@ module Rack
77
77
  frame.function = $4
78
78
 
79
79
  begin
80
- lineno = frame.lineno-1
80
+ lineno = frame.lineno - 1
81
81
  lines = ::File.readlines(frame.filename)
82
- frame.pre_context_lineno = [lineno-CONTEXT, 0].max
82
+ frame.pre_context_lineno = [lineno - CONTEXT, 0].max
83
83
  frame.pre_context = lines[frame.pre_context_lineno...lineno]
84
84
  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]
85
+ frame.post_context_lineno = [lineno + CONTEXT, lines.size].min
86
+ frame.post_context = lines[lineno + 1..frame.post_context_lineno]
87
87
  rescue
88
88
  end
89
89
 
@@ -93,7 +93,11 @@ module Rack
93
93
  end
94
94
  }.compact
95
95
 
96
- TEMPLATE.result(binding)
96
+ template.result(binding)
97
+ end
98
+
99
+ def template
100
+ TEMPLATE
97
101
  end
98
102
 
99
103
  def h(obj) # :nodoc:
@@ -107,8 +111,8 @@ module Rack
107
111
 
108
112
  # :stopdoc:
109
113
 
110
- # adapted from Django <djangoproject.com>
111
- # Copyright (c) 2005, the Lawrence Journal-World
114
+ # adapted from Django <www.djangoproject.com>
115
+ # Copyright (c) Django Software Foundation and individual contributors.
112
116
  # Used under the modified BSD license:
113
117
  # http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5
114
118
  TEMPLATE = ERB.new(<<-'HTML'.gsub(/^ /, ''))
@@ -307,7 +311,7 @@ module Rack
307
311
  <% end %>
308
312
 
309
313
  <h3 id="post-info">POST</h3>
310
- <% if req.POST and not req.POST.empty? %>
314
+ <% if ((req.POST and not req.POST.empty?) rescue (no_post_data = "Invalid POST data"; nil)) %>
311
315
  <table class="req">
312
316
  <thead>
313
317
  <tr>
@@ -325,7 +329,7 @@ module Rack
325
329
  </tbody>
326
330
  </table>
327
331
  <% else %>
328
- <p>No POST data.</p>
332
+ <p><%= no_post_data || "No POST data" %>.</p>
329
333
  <% end %>
330
334
 
331
335
 
@@ -363,7 +367,7 @@ module Rack
363
367
  <% env.sort_by { |k, v| k.to_s }.each { |key, val| %>
364
368
  <tr>
365
369
  <td><%=h key %></td>
366
- <td class="code"><div><%=h val %></div></td>
370
+ <td class="code"><div><%=h val.inspect %></div></td>
367
371
  </tr>
368
372
  <% } %>
369
373
  </tbody>
@@ -1,6 +1,6 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'erb'
2
- require 'rack/request'
3
- require 'rack/utils'
4
4
 
5
5
  module Rack
6
6
  # Rack::ShowStatus catches all empty responses and replaces them
@@ -18,7 +18,7 @@ module Rack
18
18
 
19
19
  def call(env)
20
20
  status, headers, body = @app.call(env)
21
- headers = Utils::HeaderHash.new(headers)
21
+ headers = Utils::HeaderHash[headers]
22
22
  empty = headers[CONTENT_LENGTH].to_i <= 0
23
23
 
24
24
  # client or server error, or explicit message
@@ -52,8 +52,8 @@ module Rack
52
52
 
53
53
  # :stopdoc:
54
54
 
55
- # adapted from Django <djangoproject.com>
56
- # Copyright (c) 2005, the Lawrence Journal-World
55
+ # adapted from Django <www.djangoproject.com>
56
+ # Copyright (c) Django Software Foundation and individual contributors.
57
57
  # Used under the modified BSD license:
58
58
  # http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5
59
59
  TEMPLATE = <<'HTML'
data/lib/rack/static.rb CHANGED
@@ -1,11 +1,10 @@
1
- require "rack/file"
2
- require "rack/utils"
1
+ # frozen_string_literal: true
3
2
 
4
3
  module Rack
5
4
 
6
5
  # The Rack::Static middleware intercepts requests for static files
7
6
  # (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
7
+ # route mappings passed in the options, and serves them using a Rack::Files
9
8
  # object. This allows a Rack stack to serve both static and dynamic content.
10
9
  #
11
10
  # Examples:
@@ -15,6 +14,11 @@ module Rack
15
14
  #
16
15
  # use Rack::Static, :urls => ["/media"]
17
16
  #
17
+ # Same as previous, but instead of returning 404 for missing files under
18
+ # /media, call the next middleware:
19
+ #
20
+ # use Rack::Static, :urls => ["/media"], :cascade => true
21
+ #
18
22
  # Serve all requests beginning with /css or /images from the folder "public"
19
23
  # in the current directory (ie public/css/* and public/images/*):
20
24
  #
@@ -82,24 +86,26 @@ module Rack
82
86
  # ]
83
87
  #
84
88
  class Static
89
+ (require_relative 'core_ext/regexp'; using ::Rack::RegexpExtensions) if RUBY_VERSION < '2.4'
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]
90
95
  @gzip = options[:gzip]
96
+ @cascade = options[:cascade]
91
97
  root = options[:root] || Dir.pwd
92
98
 
93
99
  # HTTP Headers
94
100
  @header_rules = options[:header_rules] || []
95
101
  # 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]
102
+ @header_rules.unshift([:all, { CACHE_CONTROL => options[:cache_control] }]) if options[:cache_control]
97
103
 
98
- @file_server = Rack::File.new(root)
104
+ @file_server = Rack::Files.new(root)
99
105
  end
100
106
 
101
107
  def add_index_root?(path)
102
- @index && path =~ /\/$/
108
+ @index && route_file(path) && path.end_with?('/')
103
109
  end
104
110
 
105
111
  def overwrite_file_path(path)
@@ -120,7 +126,7 @@ module Rack
120
126
  if can_serve(path)
121
127
  if overwrite_file_path(path)
122
128
  env[PATH_INFO] = (add_index_root?(path) ? path + @index : @urls[path])
123
- elsif @gzip && env['HTTP_ACCEPT_ENCODING'] =~ /\bgzip\b/
129
+ elsif @gzip && env['HTTP_ACCEPT_ENCODING'] && /\bgzip\b/.match?(env['HTTP_ACCEPT_ENCODING'])
124
130
  path = env[PATH_INFO]
125
131
  env[PATH_INFO] += '.gz'
126
132
  response = @file_server.call(env)
@@ -128,6 +134,8 @@ module Rack
128
134
 
129
135
  if response[0] == 404
130
136
  response = nil
137
+ elsif response[0] == 304
138
+ # Do nothing, leave headers as is
131
139
  else
132
140
  if mime_type = Mime.mime_type(::File.extname(path), 'text/plain')
133
141
  response[1][CONTENT_TYPE] = mime_type
@@ -139,6 +147,10 @@ module Rack
139
147
  path = env[PATH_INFO]
140
148
  response ||= @file_server.call(env)
141
149
 
150
+ if @cascade && response[0] == 404
151
+ return @app.call(env)
152
+ end
153
+
142
154
  headers = response[1]
143
155
  applicable_rules(path).each do |rule, new_headers|
144
156
  new_headers.each { |field, content| headers[field] = content }
@@ -157,14 +169,14 @@ module Rack
157
169
  when :all
158
170
  true
159
171
  when :fonts
160
- path =~ /\.(?:ttf|otf|eot|woff2|woff|svg)\z/
172
+ /\.(?:ttf|otf|eot|woff2|woff|svg)\z/.match?(path)
161
173
  when String
162
174
  path = ::Rack::Utils.unescape(path)
163
175
  path.start_with?(rule) || path.start_with?('/' + rule)
164
176
  when Array
165
- path =~ /\.(#{rule.join('|')})\z/
177
+ /\.(#{rule.join('|')})\z/.match?(path)
166
178
  when Regexp
167
- path =~ rule
179
+ rule.match?(path)
168
180
  else
169
181
  false
170
182
  end
@@ -1,4 +1,4 @@
1
- require 'rack/body_proxy'
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Rack
4
4
 
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
@@ -12,17 +16,16 @@ module Rack
12
16
  # first, since they are most specific.
13
17
 
14
18
  class URLMap
15
- NEGATIVE_INFINITY = -1.0 / 0.0
16
- INFINITY = 1.0 / 0.0
17
-
18
19
  def initialize(map = {})
19
20
  remap(map)
20
21
  end
21
22
 
22
23
  def remap(map)
24
+ @known_hosts = Set[]
23
25
  @mapping = map.map { |location, app|
24
26
  if location =~ %r{\Ahttps?://(.*?)(/.*)}
25
27
  host, location = $1, $2
28
+ @known_hosts << host
26
29
  else
27
30
  host = nil
28
31
  end
@@ -36,7 +39,7 @@ module Rack
36
39
 
37
40
  [host, location, match, app]
38
41
  }.sort_by do |(host, location, _, _)|
39
- [host ? -host.size : INFINITY, -location.size]
42
+ [host ? -host.size : Float::INFINITY, -location.size]
40
43
  end
41
44
  end
42
45
 
@@ -50,10 +53,13 @@ module Rack
50
53
  is_same_server = casecmp?(http_host, server_name) ||
51
54
  casecmp?(http_host, "#{server_name}:#{server_port}")
52
55
 
56
+ is_host_known = @known_hosts.include? http_host
57
+
53
58
  @mapping.each do |host, location, match, app|
54
59
  unless casecmp?(http_host, host) \
55
60
  || casecmp?(server_name, host) \
56
- || (!host && is_same_server)
61
+ || (!host && is_same_server) \
62
+ || (!host && !is_host_known) # If we don't have a matching host, default to the first without a specified host
57
63
  next
58
64
  end
59
65
 
@@ -68,7 +74,7 @@ module Rack
68
74
  return app.call(env)
69
75
  end
70
76
 
71
- [404, {CONTENT_TYPE => "text/plain", "X-Cascade" => "pass"}, ["Not Found: #{path}"]]
77
+ [404, { CONTENT_TYPE => "text/plain", "X-Cascade" => "pass" }, ["Not Found: #{path}"]]
72
78
 
73
79
  ensure
74
80
  env[PATH_INFO] = path