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.
- checksums.yaml +4 -4
- data/COPYING +1 -1
- data/HISTORY.md +153 -8
- data/README.rdoc +35 -31
- data/Rakefile +6 -14
- data/SPEC +10 -11
- data/contrib/rack_logo.svg +164 -111
- data/example/protectedlobster.rb +1 -1
- data/example/protectedlobster.ru +1 -1
- data/lib/rack/auth/abstract/request.rb +5 -1
- data/lib/rack/auth/digest/params.rb +2 -3
- data/lib/rack/auth/digest/request.rb +1 -1
- data/lib/rack/body_proxy.rb +14 -9
- data/lib/rack/builder.rb +3 -3
- data/lib/rack/chunked.rb +5 -5
- data/lib/rack/{commonlogger.rb → common_logger.rb} +6 -3
- data/lib/rack/content_length.rb +2 -2
- data/lib/rack/deflater.rb +4 -39
- data/lib/rack/directory.rb +66 -54
- data/lib/rack/etag.rb +5 -4
- data/lib/rack/events.rb +154 -0
- data/lib/rack/file.rb +64 -40
- data/lib/rack/handler/cgi.rb +15 -16
- data/lib/rack/handler/fastcgi.rb +13 -14
- data/lib/rack/handler/lsws.rb +11 -11
- data/lib/rack/handler/scgi.rb +15 -15
- data/lib/rack/handler/thin.rb +3 -0
- data/lib/rack/handler/webrick.rb +24 -26
- data/lib/rack/handler.rb +3 -25
- data/lib/rack/head.rb +15 -17
- data/lib/rack/lint.rb +41 -41
- data/lib/rack/lobster.rb +1 -1
- data/lib/rack/lock.rb +15 -10
- data/lib/rack/logger.rb +2 -2
- data/lib/rack/media_type.rb +38 -0
- data/lib/rack/{methodoverride.rb → method_override.rb} +6 -6
- data/lib/rack/mime.rb +18 -5
- data/lib/rack/mock.rb +36 -54
- data/lib/rack/multipart/generator.rb +5 -5
- data/lib/rack/multipart/parser.rb +283 -157
- data/lib/rack/multipart/uploaded_file.rb +1 -2
- data/lib/rack/multipart.rb +36 -8
- data/lib/rack/{nulllogger.rb → null_logger.rb} +1 -1
- data/lib/rack/query_parser.rb +192 -0
- data/lib/rack/recursive.rb +8 -8
- data/lib/rack/request.rb +394 -305
- data/lib/rack/response.rb +130 -57
- data/lib/rack/rewindable_input.rb +1 -12
- data/lib/rack/runtime.rb +10 -18
- data/lib/rack/sendfile.rb +5 -7
- data/lib/rack/server.rb +30 -23
- data/lib/rack/session/abstract/id.rb +110 -75
- data/lib/rack/session/cookie.rb +24 -17
- data/lib/rack/session/memcache.rb +9 -9
- data/lib/rack/session/pool.rb +8 -8
- data/lib/rack/show_exceptions.rb +386 -0
- data/lib/rack/{showstatus.rb → show_status.rb} +3 -3
- data/lib/rack/static.rb +30 -5
- data/lib/rack/tempfile_reaper.rb +2 -2
- data/lib/rack/urlmap.rb +15 -14
- data/lib/rack/utils.rb +156 -217
- data/lib/rack.rb +70 -21
- data/rack.gemspec +10 -9
- data/test/builder/an_underscore_app.rb +5 -0
- data/test/builder/options.ru +1 -1
- data/test/cgi/test.fcgi +1 -0
- data/test/cgi/test.gz +0 -0
- data/test/helper.rb +34 -0
- data/test/multipart/filename_with_encoded_words +7 -0
- data/test/multipart/filename_with_escaped_quotes_and_modification_param +1 -1
- data/test/multipart/filename_with_single_quote +7 -0
- data/test/multipart/quoted +15 -0
- data/test/multipart/rack-logo.png +0 -0
- data/test/multipart/unity3d_wwwform +11 -0
- data/test/registering_handler/rack/handler/registering_myself.rb +1 -1
- data/test/spec_auth_basic.rb +27 -19
- data/test/spec_auth_digest.rb +47 -46
- data/test/spec_body_proxy.rb +27 -27
- data/test/spec_builder.rb +51 -41
- data/test/spec_cascade.rb +24 -22
- data/test/spec_cgi.rb +49 -67
- data/test/spec_chunked.rb +37 -35
- data/test/{spec_commonlogger.rb → spec_common_logger.rb} +35 -21
- data/test/{spec_conditionalget.rb → spec_conditional_get.rb} +29 -28
- data/test/spec_config.rb +3 -2
- data/test/spec_content_length.rb +18 -17
- data/test/spec_content_type.rb +13 -12
- data/test/spec_deflater.rb +85 -49
- data/test/spec_directory.rb +87 -27
- data/test/spec_etag.rb +32 -31
- data/test/spec_events.rb +133 -0
- data/test/spec_fastcgi.rb +50 -72
- data/test/spec_file.rb +120 -77
- data/test/spec_handler.rb +19 -34
- data/test/spec_head.rb +15 -14
- data/test/spec_lint.rb +169 -199
- data/test/spec_lobster.rb +24 -23
- data/test/spec_lock.rb +79 -39
- data/test/spec_logger.rb +4 -3
- data/test/spec_media_type.rb +42 -0
- data/test/{spec_methodoverride.rb → spec_method_override.rb} +34 -35
- data/test/spec_mime.rb +19 -19
- data/test/spec_mock.rb +206 -144
- data/test/spec_multipart.rb +329 -208
- data/test/{spec_nulllogger.rb → spec_null_logger.rb} +5 -4
- data/test/spec_recursive.rb +17 -14
- data/test/spec_request.rb +796 -605
- data/test/spec_response.rb +233 -112
- data/test/spec_rewindable_input.rb +50 -40
- data/test/spec_runtime.rb +11 -10
- data/test/spec_sendfile.rb +30 -35
- data/test/spec_server.rb +78 -52
- data/test/spec_session_abstract_id.rb +11 -33
- data/test/spec_session_abstract_session_hash.rb +45 -0
- data/test/spec_session_cookie.rb +99 -67
- data/test/spec_session_memcache.rb +67 -68
- data/test/spec_session_pool.rb +52 -51
- data/test/{spec_showexceptions.rb → spec_show_exceptions.rb} +23 -28
- data/test/{spec_showstatus.rb → spec_show_status.rb} +36 -35
- data/test/spec_static.rb +71 -32
- data/test/spec_tempfile_reaper.rb +11 -10
- data/test/spec_thin.rb +55 -50
- data/test/spec_urlmap.rb +79 -78
- data/test/spec_utils.rb +441 -346
- data/test/spec_version.rb +2 -8
- data/test/spec_webrick.rb +93 -71
- data/test/static/foo.html +1 -0
- data/test/testrequest.rb +1 -1
- data/test/unregistered_handler/rack/handler/unregistered.rb +1 -1
- data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +1 -1
- metadata +95 -74
- data/KNOWN-ISSUES +0 -44
- data/lib/rack/backports/uri/common_18.rb +0 -56
- data/lib/rack/backports/uri/common_192.rb +0 -52
- data/lib/rack/backports/uri/common_193.rb +0 -29
- data/lib/rack/handler/evented_mongrel.rb +0 -8
- data/lib/rack/handler/mongrel.rb +0 -106
- data/lib/rack/handler/swiftiplied_mongrel.rb +0 -8
- data/lib/rack/showexceptions.rb +0 -387
- data/lib/rack/utils/okjson.rb +0 -600
- data/test/spec_mongrel.rb +0 -182
- /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/
|
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
|
-
|
26
|
-
|
27
|
-
|
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
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
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
|
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
|
-
|
50
|
-
|
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 :
|
64
|
-
attr_accessor :param_depth_limit
|
65
|
-
attr_accessor :multipart_part_limit
|
66
|
-
end
|
56
|
+
attr_accessor :multipart_total_part_limit
|
67
57
|
|
68
|
-
|
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
|
-
|
73
|
-
|
74
|
-
|
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
|
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
|
-
|
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
|
-
|
97
|
-
|
98
|
-
|
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
|
-
|
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
|
-
|
120
|
-
|
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
|
-
|
123
|
-
|
83
|
+
def self.key_space_limit
|
84
|
+
default_query_parser.key_space_limit
|
85
|
+
end
|
124
86
|
|
125
|
-
|
126
|
-
|
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
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
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
|
-
|
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 :
|
105
|
+
module_function :parse_query
|
169
106
|
|
170
|
-
def
|
171
|
-
|
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 :
|
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
|
'"' => """,
|
237
174
|
"/" => "/"
|
238
175
|
}
|
239
|
-
|
240
|
-
|
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
|
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
|
285
|
-
path = "; path
|
286
|
-
max_age = "; 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
|
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
|
-
|
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
|
-
|
284
|
+
cookie
|
335
285
|
when String
|
336
|
-
|
286
|
+
[header, cookie].join("\n")
|
337
287
|
when Array
|
338
|
-
|
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
|
346
|
-
case header
|
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
|
306
|
+
cookies = header.split("\n")
|
351
307
|
when Array
|
352
|
-
cookies = header
|
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
|
-
|
366
|
-
|
367
|
-
|
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
|
-
#
|
377
|
-
#
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
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 :
|
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
|
420
|
-
|
421
|
-
|
422
|
-
|
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.
|
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 :
|
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
|
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[
|
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
|
-
|
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
|
-
|
566
|
-
|
567
|
-
|
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 <<
|
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
|