rack 1.6.13 → 2.0.9.3

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 (142) hide show
  1. checksums.yaml +4 -4
  2. data/COPYING +1 -1
  3. data/HISTORY.md +153 -8
  4. data/README.rdoc +35 -31
  5. data/Rakefile +6 -14
  6. data/SPEC +10 -11
  7. data/contrib/rack_logo.svg +164 -111
  8. data/example/protectedlobster.rb +1 -1
  9. data/example/protectedlobster.ru +1 -1
  10. data/lib/rack/auth/abstract/request.rb +5 -1
  11. data/lib/rack/auth/digest/params.rb +2 -3
  12. data/lib/rack/auth/digest/request.rb +1 -1
  13. data/lib/rack/body_proxy.rb +14 -9
  14. data/lib/rack/builder.rb +3 -3
  15. data/lib/rack/chunked.rb +5 -5
  16. data/lib/rack/{commonlogger.rb → common_logger.rb} +6 -3
  17. data/lib/rack/content_length.rb +2 -2
  18. data/lib/rack/deflater.rb +4 -39
  19. data/lib/rack/directory.rb +66 -54
  20. data/lib/rack/etag.rb +5 -4
  21. data/lib/rack/events.rb +154 -0
  22. data/lib/rack/file.rb +64 -40
  23. data/lib/rack/handler/cgi.rb +15 -16
  24. data/lib/rack/handler/fastcgi.rb +13 -14
  25. data/lib/rack/handler/lsws.rb +11 -11
  26. data/lib/rack/handler/scgi.rb +15 -15
  27. data/lib/rack/handler/thin.rb +3 -0
  28. data/lib/rack/handler/webrick.rb +24 -26
  29. data/lib/rack/handler.rb +3 -25
  30. data/lib/rack/head.rb +15 -17
  31. data/lib/rack/lint.rb +41 -41
  32. data/lib/rack/lobster.rb +1 -1
  33. data/lib/rack/lock.rb +15 -10
  34. data/lib/rack/logger.rb +2 -2
  35. data/lib/rack/media_type.rb +38 -0
  36. data/lib/rack/{methodoverride.rb → method_override.rb} +6 -6
  37. data/lib/rack/mime.rb +18 -5
  38. data/lib/rack/mock.rb +36 -54
  39. data/lib/rack/multipart/generator.rb +5 -5
  40. data/lib/rack/multipart/parser.rb +283 -157
  41. data/lib/rack/multipart/uploaded_file.rb +1 -2
  42. data/lib/rack/multipart.rb +36 -8
  43. data/lib/rack/{nulllogger.rb → null_logger.rb} +1 -1
  44. data/lib/rack/query_parser.rb +192 -0
  45. data/lib/rack/recursive.rb +8 -8
  46. data/lib/rack/request.rb +394 -305
  47. data/lib/rack/response.rb +130 -57
  48. data/lib/rack/rewindable_input.rb +1 -12
  49. data/lib/rack/runtime.rb +10 -18
  50. data/lib/rack/sendfile.rb +5 -7
  51. data/lib/rack/server.rb +30 -23
  52. data/lib/rack/session/abstract/id.rb +110 -75
  53. data/lib/rack/session/cookie.rb +24 -17
  54. data/lib/rack/session/memcache.rb +9 -9
  55. data/lib/rack/session/pool.rb +8 -8
  56. data/lib/rack/show_exceptions.rb +386 -0
  57. data/lib/rack/{showstatus.rb → show_status.rb} +3 -3
  58. data/lib/rack/static.rb +30 -5
  59. data/lib/rack/tempfile_reaper.rb +2 -2
  60. data/lib/rack/urlmap.rb +15 -14
  61. data/lib/rack/utils.rb +156 -217
  62. data/lib/rack.rb +70 -21
  63. data/rack.gemspec +10 -9
  64. data/test/builder/an_underscore_app.rb +5 -0
  65. data/test/builder/options.ru +1 -1
  66. data/test/cgi/test.fcgi +1 -0
  67. data/test/cgi/test.gz +0 -0
  68. data/test/helper.rb +34 -0
  69. data/test/multipart/filename_with_encoded_words +7 -0
  70. data/test/multipart/filename_with_escaped_quotes_and_modification_param +1 -1
  71. data/test/multipart/filename_with_single_quote +7 -0
  72. data/test/multipart/quoted +15 -0
  73. data/test/multipart/rack-logo.png +0 -0
  74. data/test/multipart/unity3d_wwwform +11 -0
  75. data/test/registering_handler/rack/handler/registering_myself.rb +1 -1
  76. data/test/spec_auth_basic.rb +27 -19
  77. data/test/spec_auth_digest.rb +47 -46
  78. data/test/spec_body_proxy.rb +27 -27
  79. data/test/spec_builder.rb +51 -41
  80. data/test/spec_cascade.rb +24 -22
  81. data/test/spec_cgi.rb +49 -67
  82. data/test/spec_chunked.rb +37 -35
  83. data/test/{spec_commonlogger.rb → spec_common_logger.rb} +35 -21
  84. data/test/{spec_conditionalget.rb → spec_conditional_get.rb} +29 -28
  85. data/test/spec_config.rb +3 -2
  86. data/test/spec_content_length.rb +18 -17
  87. data/test/spec_content_type.rb +13 -12
  88. data/test/spec_deflater.rb +85 -49
  89. data/test/spec_directory.rb +87 -27
  90. data/test/spec_etag.rb +32 -31
  91. data/test/spec_events.rb +133 -0
  92. data/test/spec_fastcgi.rb +50 -72
  93. data/test/spec_file.rb +120 -77
  94. data/test/spec_handler.rb +19 -34
  95. data/test/spec_head.rb +15 -14
  96. data/test/spec_lint.rb +169 -199
  97. data/test/spec_lobster.rb +24 -23
  98. data/test/spec_lock.rb +79 -39
  99. data/test/spec_logger.rb +4 -3
  100. data/test/spec_media_type.rb +42 -0
  101. data/test/{spec_methodoverride.rb → spec_method_override.rb} +34 -35
  102. data/test/spec_mime.rb +19 -19
  103. data/test/spec_mock.rb +206 -144
  104. data/test/spec_multipart.rb +329 -208
  105. data/test/{spec_nulllogger.rb → spec_null_logger.rb} +5 -4
  106. data/test/spec_recursive.rb +17 -14
  107. data/test/spec_request.rb +796 -605
  108. data/test/spec_response.rb +233 -112
  109. data/test/spec_rewindable_input.rb +50 -40
  110. data/test/spec_runtime.rb +11 -10
  111. data/test/spec_sendfile.rb +30 -35
  112. data/test/spec_server.rb +78 -52
  113. data/test/spec_session_abstract_id.rb +11 -33
  114. data/test/spec_session_abstract_session_hash.rb +45 -0
  115. data/test/spec_session_cookie.rb +99 -67
  116. data/test/spec_session_memcache.rb +67 -68
  117. data/test/spec_session_pool.rb +52 -51
  118. data/test/{spec_showexceptions.rb → spec_show_exceptions.rb} +23 -28
  119. data/test/{spec_showstatus.rb → spec_show_status.rb} +36 -35
  120. data/test/spec_static.rb +71 -32
  121. data/test/spec_tempfile_reaper.rb +11 -10
  122. data/test/spec_thin.rb +55 -50
  123. data/test/spec_urlmap.rb +79 -78
  124. data/test/spec_utils.rb +441 -346
  125. data/test/spec_version.rb +2 -8
  126. data/test/spec_webrick.rb +93 -71
  127. data/test/static/foo.html +1 -0
  128. data/test/testrequest.rb +1 -1
  129. data/test/unregistered_handler/rack/handler/unregistered.rb +1 -1
  130. data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +1 -1
  131. metadata +95 -74
  132. data/KNOWN-ISSUES +0 -44
  133. data/lib/rack/backports/uri/common_18.rb +0 -56
  134. data/lib/rack/backports/uri/common_192.rb +0 -52
  135. data/lib/rack/backports/uri/common_193.rb +0 -29
  136. data/lib/rack/handler/evented_mongrel.rb +0 -8
  137. data/lib/rack/handler/mongrel.rb +0 -106
  138. data/lib/rack/handler/swiftiplied_mongrel.rb +0 -8
  139. data/lib/rack/showexceptions.rb +0 -387
  140. data/lib/rack/utils/okjson.rb +0 -600
  141. data/test/spec_mongrel.rb +0 -182
  142. /data/lib/rack/{conditionalget.rb → conditional_get.rb} +0 -0
data/lib/rack/utils.rb CHANGED
@@ -1,35 +1,28 @@
1
1
  # -*- encoding: binary -*-
2
+ require 'uri'
2
3
  require 'fileutils'
3
4
  require 'set'
4
5
  require 'tempfile'
5
- require 'rack/multipart'
6
+ require 'rack/query_parser'
6
7
  require 'time'
7
8
 
8
- major, minor, patch = RUBY_VERSION.split('.').map { |v| v.to_i }
9
-
10
- if major == 1 && minor < 9
11
- require 'rack/backports/uri/common_18'
12
- elsif major == 1 && minor == 9 && patch == 2 && RUBY_PATCHLEVEL <= 328 && RUBY_ENGINE != 'jruby'
13
- require 'rack/backports/uri/common_192'
14
- elsif major == 1 && minor == 9 && patch == 3 && RUBY_PATCHLEVEL < 125
15
- require 'rack/backports/uri/common_193'
16
- else
17
- require 'uri/common'
18
- end
19
-
20
9
  module Rack
21
10
  # Rack::Utils contains a grab-bag of useful methods for writing web
22
11
  # applications adopted from all kinds of Ruby libraries.
23
12
 
24
13
  module Utils
25
- # ParameterTypeError is the error that is raised when incoming structural
26
- # parameters (parsed by parse_nested_query) contain conflicting types.
27
- class ParameterTypeError < TypeError; end
14
+ ParameterTypeError = QueryParser::ParameterTypeError
15
+ InvalidParameterError = QueryParser::InvalidParameterError
16
+ DEFAULT_SEP = QueryParser::DEFAULT_SEP
17
+ COMMON_SEP = QueryParser::COMMON_SEP
18
+ KeySpaceConstrainedParams = QueryParser::Params
28
19
 
29
- # InvalidParameterError is the error that is raised when incoming structural
30
- # parameters (parsed by parse_nested_query) contain invalid format or byte
31
- # sequence.
32
- class InvalidParameterError < ArgumentError; end
20
+ class << self
21
+ attr_accessor :default_query_parser
22
+ end
23
+ # The default number of bytes to allow parameter keys to take up.
24
+ # This helps prevent a rogue client from flooding a Request.
25
+ self.default_query_parser = QueryParser.make_default(65536, 100)
33
26
 
34
27
  # URI escapes. (CGI style space to +)
35
28
  def escape(s)
@@ -40,137 +33,81 @@ module Rack
40
33
  # Like URI escaping, but with %20 instead of +. Strictly speaking this is
41
34
  # true URI escaping.
42
35
  def escape_path(s)
43
- escape(s).gsub('+', '%20')
36
+ ::URI::DEFAULT_PARSER.escape s
44
37
  end
45
38
  module_function :escape_path
46
39
 
40
+ # Unescapes the **path** component of a URI. See Rack::Utils.unescape for
41
+ # unescaping query parameters or form components.
42
+ def unescape_path(s)
43
+ ::URI::DEFAULT_PARSER.unescape s
44
+ end
45
+ module_function :unescape_path
46
+
47
+
47
48
  # Unescapes a URI escaped string with +encoding+. +encoding+ will be the
48
49
  # target encoding of the string returned, and it defaults to UTF-8
49
- if defined?(::Encoding)
50
- def unescape(s, encoding = Encoding::UTF_8)
51
- URI.decode_www_form_component(s, encoding)
52
- end
53
- else
54
- def unescape(s, encoding = nil)
55
- URI.decode_www_form_component(s, encoding)
56
- end
50
+ def unescape(s, encoding = Encoding::UTF_8)
51
+ URI.decode_www_form_component(s, encoding)
57
52
  end
58
53
  module_function :unescape
59
54
 
60
- DEFAULT_SEP = /[&;] */n
61
-
62
55
  class << self
63
- attr_accessor :key_space_limit
64
- attr_accessor :param_depth_limit
65
- attr_accessor :multipart_part_limit
66
- end
56
+ attr_accessor :multipart_total_part_limit
67
57
 
68
- # The default number of bytes to allow parameter keys to take up.
69
- # This helps prevent a rogue client from flooding a Request.
70
- self.key_space_limit = 65536
58
+ attr_accessor :multipart_file_limit
71
59
 
72
- # Default depth at which the parameter parser will raise an exception for
73
- # being too deep. This helps prevent SystemStackErrors
74
- self.param_depth_limit = 100
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=
64
+ end
75
65
 
76
- # The maximum number of parts a request can contain. Accepting too many part
77
- # can lead to the server running out of file handles.
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.
78
68
  # Set to `0` for no limit.
79
- # FIXME: RACK_MULTIPART_LIMIT was introduced by mistake and it will be removed in 1.7.0
80
- self.multipart_part_limit = (ENV['RACK_MULTIPART_PART_LIMIT'] || ENV['RACK_MULTIPART_LIMIT'] || 128).to_i
81
-
82
- # Stolen from Mongrel, with some small modifications:
83
- # Parses a query string by breaking it up at the '&'
84
- # and ';' characters. You can also use this to parse
85
- # cookies by changing the characters used in the second
86
- # parameter (which defaults to '&;').
87
- def parse_query(qs, d = nil, &unescaper)
88
- unescaper ||= method(:unescape)
89
-
90
- params = KeySpaceConstrainedParams.new
91
-
92
- (qs || '').split(d ? /[#{d}] */n : DEFAULT_SEP).each do |p|
93
- next if p.empty?
94
- k, v = p.split('=', 2).map(&unescaper)
69
+ self.multipart_file_limit = (ENV['RACK_MULTIPART_PART_LIMIT'] || ENV['RACK_MULTIPART_FILE_LIMIT'] || 128).to_i
95
70
 
96
- if cur = params[k]
97
- if cur.class == Array
98
- params[k] << v
99
- else
100
- params[k] = [cur, v]
101
- end
102
- else
103
- params[k] = v
104
- end
105
- end
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
106
74
 
107
- return params.to_params_hash
75
+ def self.param_depth_limit
76
+ default_query_parser.param_depth_limit
108
77
  end
109
- module_function :parse_query
110
-
111
- # parse_nested_query expands a query string into structural types. Supported
112
- # types are Arrays, Hashes and basic value types. It is possible to supply
113
- # query strings with parameters of conflicting types, in this case a
114
- # ParameterTypeError is raised. Users are encouraged to return a 400 in this
115
- # case.
116
- def parse_nested_query(qs, d = nil)
117
- params = KeySpaceConstrainedParams.new
118
78
 
119
- (qs || '').split(d ? /[#{d}] */n : DEFAULT_SEP).each do |p|
120
- k, v = p.split('=', 2).map { |s| unescape(s) }
79
+ def self.param_depth_limit=(v)
80
+ self.default_query_parser = self.default_query_parser.new_depth_limit(v)
81
+ end
121
82
 
122
- normalize_params(params, k, v)
123
- end
83
+ def self.key_space_limit
84
+ default_query_parser.key_space_limit
85
+ end
124
86
 
125
- return params.to_params_hash
126
- rescue ArgumentError => e
127
- raise InvalidParameterError, e.message
87
+ def self.key_space_limit=(v)
88
+ self.default_query_parser = self.default_query_parser.new_space_limit(v)
128
89
  end
129
- module_function :parse_nested_query
130
90
 
131
- # normalize_params recursively expands parameters into structural types. If
132
- # the structural types represented by two different parameter names are in
133
- # conflict, a ParameterTypeError is raised.
134
- def normalize_params(params, name, v = nil, depth = Utils.param_depth_limit)
135
- raise RangeError if depth <= 0
136
-
137
- name =~ %r(\A[\[\]]*([^\[\]]+)\]*)
138
- k = $1 || ''
139
- after = $' || ''
140
-
141
- return if k.empty?
142
-
143
- if after == ""
144
- params[k] = v
145
- elsif after == "["
146
- params[name] = v
147
- elsif after == "[]"
148
- params[k] ||= []
149
- raise ParameterTypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
150
- params[k] << v
151
- elsif after =~ %r(^\[\]\[([^\[\]]+)\]$) || after =~ %r(^\[\](.+)$)
152
- child_key = $1
153
- params[k] ||= []
154
- raise ParameterTypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
155
- if params_hash_type?(params[k].last) && !params[k].last.key?(child_key)
156
- normalize_params(params[k].last, child_key, v, depth - 1)
157
- else
158
- params[k] << normalize_params(params.class.new, child_key, v, depth - 1)
159
- end
160
- else
161
- params[k] ||= params.class.new
162
- raise ParameterTypeError, "expected Hash (got #{params[k].class.name}) for param `#{k}'" unless params_hash_type?(params[k])
163
- params[k] = normalize_params(params[k], after, v, depth - 1)
91
+ if defined?(Process::CLOCK_MONOTONIC)
92
+ def clock_time
93
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
94
+ end
95
+ else
96
+ def clock_time
97
+ Time.now.to_f
164
98
  end
99
+ end
100
+ module_function :clock_time
165
101
 
166
- return params
102
+ def parse_query(qs, d = nil, &unescaper)
103
+ Rack::Utils.default_query_parser.parse_query(qs, d, &unescaper)
167
104
  end
168
- module_function :normalize_params
105
+ module_function :parse_query
169
106
 
170
- def params_hash_type?(obj)
171
- obj.kind_of?(KeySpaceConstrainedParams) || obj.kind_of?(Hash)
107
+ def parse_nested_query(qs, d = nil)
108
+ Rack::Utils.default_query_parser.parse_nested_query(qs, d)
172
109
  end
173
- module_function :params_hash_type?
110
+ module_function :parse_nested_query
174
111
 
175
112
  def build_query(params)
176
113
  params.map { |k, v|
@@ -236,13 +173,8 @@ module Rack
236
173
  '"' => "&quot;",
237
174
  "/" => "&#x2F;"
238
175
  }
239
- if //.respond_to?(:encoding)
240
- ESCAPE_HTML_PATTERN = Regexp.union(*ESCAPE_HTML.keys)
241
- else
242
- # On 1.8, there is a kcode = 'u' bug that allows for XSS otherwise
243
- # TODO doesn't apply to jruby, so a better condition above might be preferable?
244
- ESCAPE_HTML_PATTERN = /#{Regexp.union(*ESCAPE_HTML.keys)}/n
245
- end
176
+
177
+ ESCAPE_HTML_PATTERN = Regexp.union(*ESCAPE_HTML.keys)
246
178
 
247
179
  # Escape ampersands, brackets and quotes to their HTML/XML entities.
248
180
  def escape_html(string)
@@ -278,12 +210,28 @@ module Rack
278
210
  end
279
211
  module_function :select_best_encoding
280
212
 
281
- def set_cookie_header!(header, key, value)
213
+ def parse_cookies(env)
214
+ parse_cookies_header env[HTTP_COOKIE]
215
+ end
216
+ module_function :parse_cookies
217
+
218
+ def parse_cookies_header(header)
219
+ # According to RFC 2109:
220
+ # If multiple cookies satisfy the criteria above, they are ordered in
221
+ # the Cookie header such that those with more specific Path attributes
222
+ # precede those with less specific. Ordering with respect to other
223
+ # attributes (e.g., Domain) is unspecified.
224
+ cookies = parse_query(header, ';,') { |s| unescape(s) rescue s }
225
+ cookies.each_with_object({}) { |(k,v), hash| hash[k] = Array === v ? v.first : v }
226
+ end
227
+ module_function :parse_cookies_header
228
+
229
+ def add_cookie_to_header(header, key, value)
282
230
  case value
283
231
  when Hash
284
- domain = "; domain=" + value[:domain] if value[:domain]
285
- path = "; path=" + value[:path] if value[:path]
286
- max_age = "; max-age=" + value[:max_age].to_s if value[:max_age]
232
+ domain = "; domain=#{value[:domain]}" if value[:domain]
233
+ path = "; path=#{value[:path]}" if value[:path]
234
+ max_age = "; max-age=#{value[:max_age]}" if value[:max_age]
287
235
  # There is an RFC mess in the area of date formatting for Cookies. Not
288
236
  # only are there contradicting RFCs and examples within RFC text, but
289
237
  # there are also numerous conflicting names of fields and partially
@@ -291,7 +239,7 @@ module Rack
291
239
  #
292
240
  # These are best described in RFC 2616 3.3.1. This RFC text also
293
241
  # specifies that RFC 822 as updated by RFC 1123 is preferred. That is a
294
- # fixed length format with space-date delimeted fields.
242
+ # fixed length format with space-date delimited fields.
295
243
  #
296
244
  # See also RFC 1123 section 5.2.14.
297
245
  #
@@ -315,6 +263,8 @@ module Rack
315
263
  case value[:same_site]
316
264
  when false, nil
317
265
  nil
266
+ when :none, 'None', :None
267
+ '; SameSite=None'.freeze
318
268
  when :lax, 'Lax', :Lax
319
269
  '; SameSite=Lax'.freeze
320
270
  when true, :strict, 'Strict', :Strict
@@ -325,31 +275,37 @@ module Rack
325
275
  value = value[:value]
326
276
  end
327
277
  value = [value] unless Array === value
328
- cookie = escape(key) + "=" +
329
- value.map { |v| escape v }.join("&") +
330
- "#{domain}#{path}#{max_age}#{expires}#{secure}#{httponly}#{same_site}"
331
278
 
332
- case header["Set-Cookie"]
279
+ cookie = "#{escape(key)}=#{value.map { |v| escape v }.join('&')}#{domain}" \
280
+ "#{path}#{max_age}#{expires}#{secure}#{httponly}#{same_site}"
281
+
282
+ case header
333
283
  when nil, ''
334
- header["Set-Cookie"] = cookie
284
+ cookie
335
285
  when String
336
- header["Set-Cookie"] = [header["Set-Cookie"], cookie].join("\n")
286
+ [header, cookie].join("\n")
337
287
  when Array
338
- header["Set-Cookie"] = (header["Set-Cookie"] + [cookie]).join("\n")
288
+ (header + [cookie]).join("\n")
289
+ else
290
+ raise ArgumentError, "Unrecognized cookie header value. Expected String, Array, or nil, got #{header.inspect}"
339
291
  end
292
+ end
293
+ module_function :add_cookie_to_header
340
294
 
295
+ def set_cookie_header!(header, key, value)
296
+ header[SET_COOKIE] = add_cookie_to_header(header[SET_COOKIE], key, value)
341
297
  nil
342
298
  end
343
299
  module_function :set_cookie_header!
344
300
 
345
- def delete_cookie_header!(header, key, value = {})
346
- case header["Set-Cookie"]
301
+ def make_delete_cookie_header(header, key, value)
302
+ case header
347
303
  when nil, ''
348
304
  cookies = []
349
305
  when String
350
- cookies = header["Set-Cookie"].split("\n")
306
+ cookies = header.split("\n")
351
307
  when Array
352
- cookies = header["Set-Cookie"]
308
+ cookies = header
353
309
  end
354
310
 
355
311
  cookies.reject! { |cookie|
@@ -362,29 +318,28 @@ module Rack
362
318
  end
363
319
  }
364
320
 
365
- header["Set-Cookie"] = cookies.join("\n")
366
-
367
- set_cookie_header!(header, key,
368
- {:value => '', :path => nil, :domain => nil,
369
- :max_age => '0',
370
- :expires => Time.at(0) }.merge(value))
321
+ cookies.join("\n")
322
+ end
323
+ module_function :make_delete_cookie_header
371
324
 
325
+ def delete_cookie_header!(header, key, value = {})
326
+ header[SET_COOKIE] = add_remove_cookie_to_header(header[SET_COOKIE], key, value)
372
327
  nil
373
328
  end
374
329
  module_function :delete_cookie_header!
375
330
 
376
- # Return the bytesize of String; uses String#size under Ruby 1.8 and
377
- # String#bytesize under 1.9.
378
- if ''.respond_to?(:bytesize)
379
- def bytesize(string)
380
- string.bytesize
381
- end
382
- else
383
- def bytesize(string)
384
- string.size
385
- end
331
+ # Adds a cookie that will *remove* a cookie from the client. Hence the
332
+ # strange method name.
333
+ def add_remove_cookie_to_header(header, key, value = {})
334
+ new_header = make_delete_cookie_header(header, key, value)
335
+
336
+ add_cookie_to_header(new_header, key,
337
+ {:value => '', :path => nil, :domain => nil,
338
+ :max_age => '0',
339
+ :expires => Time.at(0) }.merge(value))
340
+
386
341
  end
387
- module_function :bytesize
342
+ module_function :add_remove_cookie_to_header
388
343
 
389
344
  def rfc2822(time)
390
345
  time.rfc2822
@@ -411,22 +366,28 @@ module Rack
411
366
  # Returns nil if the header is missing or syntactically invalid.
412
367
  # Returns an empty array if none of the ranges are satisfiable.
413
368
  def byte_ranges(env, size)
369
+ warn "`byte_ranges` is deprecated, please use `get_byte_ranges`" if $VERBOSE
370
+ get_byte_ranges env['HTTP_RANGE'], size
371
+ end
372
+ module_function :byte_ranges
373
+
374
+ def get_byte_ranges(http_range, size)
414
375
  # See <http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35>
415
- http_range = env['HTTP_RANGE']
416
376
  return nil unless http_range && http_range =~ /bytes=([^;]+)/
417
377
  ranges = []
418
378
  $1.split(/,\s*/).each do |range_spec|
419
- return nil unless range_spec =~ /(\d*)-(\d*)/
420
- r0,r1 = $1, $2
421
- if r0.empty?
422
- return nil if r1.empty?
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?
423
384
  # suffix-byte-range-spec, represents trailing suffix of file
424
385
  r0 = size - r1.to_i
425
386
  r0 = 0 if r0 < 0
426
387
  r1 = size - 1
427
388
  else
428
389
  r0 = r0.to_i
429
- if r1.empty?
390
+ if r1.nil?
430
391
  r1 = size - 1
431
392
  else
432
393
  r1 = r1.to_i
@@ -438,7 +399,7 @@ module Rack
438
399
  end
439
400
  ranges
440
401
  end
441
- module_function :byte_ranges
402
+ module_function :get_byte_ranges
442
403
 
443
404
  # Constant time string comparison.
444
405
  #
@@ -447,7 +408,7 @@ module Rack
447
408
  # on variable length plaintext strings because it could leak length info
448
409
  # via timing attacks.
449
410
  def secure_compare(a, b)
450
- return false unless bytesize(a) == bytesize(b)
411
+ return false unless a.bytesize == b.bytesize
451
412
 
452
413
  l = a.unpack("C*")
453
414
 
@@ -496,6 +457,12 @@ module Rack
496
457
  hash.each { |k, v| self[k] = v }
497
458
  end
498
459
 
460
+ # on dup/clone, we need to duplicate @names hash
461
+ def initialize_copy(other)
462
+ super
463
+ @names = other.names.dup
464
+ end
465
+
499
466
  def each
500
467
  super do |k, v|
501
468
  yield(k, v.respond_to?(:to_ary) ? v.to_ary.join("\n") : v)
@@ -513,21 +480,20 @@ module Rack
513
480
  end
514
481
 
515
482
  def []=(k, v)
516
- canonical = k.downcase
483
+ canonical = k.downcase.freeze
517
484
  delete k if @names[canonical] && @names[canonical] != k # .delete is expensive, don't invoke it unless necessary
518
- @names[k] = @names[canonical] = k
485
+ @names[canonical] = k
519
486
  super k, v
520
487
  end
521
488
 
522
489
  def delete(k)
523
490
  canonical = k.downcase
524
491
  result = super @names.delete(canonical)
525
- @names.delete_if { |name,| name.downcase == canonical }
526
492
  result
527
493
  end
528
494
 
529
495
  def include?(k)
530
- @names.include?(k) || @names.include?(k.downcase)
496
+ super || @names.include?(k.downcase)
531
497
  end
532
498
 
533
499
  alias_method :has_key?, :include?
@@ -549,45 +515,11 @@ module Rack
549
515
  other.each { |k, v| self[k] = v }
550
516
  self
551
517
  end
552
- end
553
-
554
- class KeySpaceConstrainedParams
555
- def initialize(limit = Utils.key_space_limit)
556
- @limit = limit
557
- @size = 0
558
- @params = {}
559
- end
560
-
561
- def [](key)
562
- @params[key]
563
- end
564
518
 
565
- def []=(key, value)
566
- @size += key.size if key && !@params.key?(key)
567
- raise RangeError, 'exceeded available parameter key space' if @size > @limit
568
- @params[key] = value
569
- end
570
-
571
- def key?(key)
572
- @params.key?(key)
573
- end
574
-
575
- def to_params_hash
576
- hash = @params
577
- hash.keys.each do |key|
578
- value = hash[key]
579
- if value.kind_of?(self.class)
580
- if value.object_id == self.object_id
581
- hash[key] = hash
582
- else
583
- hash[key] = value.to_params_hash
584
- end
585
- elsif value.kind_of?(Array)
586
- value.map! {|x| x.kind_of?(self.class) ? x.to_params_hash : x}
587
- end
519
+ protected
520
+ def names
521
+ @names
588
522
  end
589
- hash
590
- end
591
523
  end
592
524
 
593
525
  # Every standard HTTP code mapped to the appropriate message.
@@ -635,6 +567,7 @@ module Rack
635
567
  415 => 'Unsupported Media Type',
636
568
  416 => 'Range Not Satisfiable',
637
569
  417 => 'Expectation Failed',
570
+ 421 => 'Misdirected Request',
638
571
  422 => 'Unprocessable Entity',
639
572
  423 => 'Locked',
640
573
  424 => 'Failed Dependency',
@@ -642,6 +575,7 @@ module Rack
642
575
  428 => 'Precondition Required',
643
576
  429 => 'Too Many Requests',
644
577
  431 => 'Request Header Fields Too Large',
578
+ 451 => 'Unavailable for Legal Reasons',
645
579
  500 => 'Internal Server Error',
646
580
  501 => 'Not Implemented',
647
581
  502 => 'Bad Gateway',
@@ -656,7 +590,7 @@ module Rack
656
590
  }
657
591
 
658
592
  # Responses with HTTP status codes that should not have an entity body
659
- STATUS_WITH_NO_ENTITY_BODY = Set.new((100..199).to_a << 204 << 205 << 304)
593
+ STATUS_WITH_NO_ENTITY_BODY = Set.new((100..199).to_a << 204 << 304)
660
594
 
661
595
  SYMBOL_TO_STATUS_CODE = Hash[*HTTP_STATUS_CODES.map { |code, message|
662
596
  [message.downcase.gsub(/\s|-|'/, '_').to_sym, code]
@@ -671,8 +605,6 @@ module Rack
671
605
  end
672
606
  module_function :status_code
673
607
 
674
- Multipart = Rack::Multipart
675
-
676
608
  PATH_SEPS = Regexp.union(*[::File::SEPARATOR, ::File::ALT_SEPARATOR].compact)
677
609
 
678
610
  def clean_path_info(path_info)
@@ -691,5 +623,12 @@ module Rack
691
623
  end
692
624
  module_function :clean_path_info
693
625
 
626
+ NULL_BYTE = "\0".freeze
627
+
628
+ def valid_path?(path)
629
+ path.valid_encoding? && !path.include?(NULL_BYTE)
630
+ end
631
+ module_function :valid_path?
632
+
694
633
  end
695
634
  end