rack 1.6.13 → 2.0.9
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 +138 -8
- data/README.rdoc +18 -28
- data/Rakefile +6 -14
- data/SPEC +3 -3
- 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} +3 -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 +40 -40
- 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 +270 -157
- data/lib/rack/multipart/uploaded_file.rb +1 -2
- data/lib/rack/multipart.rb +35 -6
- 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 +138 -211
- 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_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} +23 -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 +164 -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 +322 -200
- data/test/{spec_nulllogger.rb → spec_null_logger.rb} +5 -4
- data/test/spec_recursive.rb +17 -14
- data/test/spec_request.rb +780 -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 +57 -36
- 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,70 @@ 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 :key_space_limit
|
64
|
-
attr_accessor :param_depth_limit
|
65
56
|
attr_accessor :multipart_part_limit
|
66
57
|
end
|
67
58
|
|
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
|
71
|
-
|
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
|
75
|
-
|
76
59
|
# The maximum number of parts a request can contain. Accepting too many part
|
77
60
|
# can lead to the server running out of file handles.
|
78
61
|
# 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)
|
62
|
+
self.multipart_part_limit = (ENV['RACK_MULTIPART_PART_LIMIT'] || 128).to_i
|
95
63
|
|
96
|
-
|
97
|
-
|
98
|
-
params[k] << v
|
99
|
-
else
|
100
|
-
params[k] = [cur, v]
|
101
|
-
end
|
102
|
-
else
|
103
|
-
params[k] = v
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
return params.to_params_hash
|
64
|
+
def self.param_depth_limit
|
65
|
+
default_query_parser.param_depth_limit
|
108
66
|
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
67
|
|
119
|
-
|
120
|
-
|
68
|
+
def self.param_depth_limit=(v)
|
69
|
+
self.default_query_parser = self.default_query_parser.new_depth_limit(v)
|
70
|
+
end
|
121
71
|
|
122
|
-
|
123
|
-
|
72
|
+
def self.key_space_limit
|
73
|
+
default_query_parser.key_space_limit
|
74
|
+
end
|
124
75
|
|
125
|
-
|
126
|
-
|
127
|
-
raise InvalidParameterError, e.message
|
76
|
+
def self.key_space_limit=(v)
|
77
|
+
self.default_query_parser = self.default_query_parser.new_space_limit(v)
|
128
78
|
end
|
129
|
-
module_function :parse_nested_query
|
130
79
|
|
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)
|
80
|
+
if defined?(Process::CLOCK_MONOTONIC)
|
81
|
+
def clock_time
|
82
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
83
|
+
end
|
84
|
+
else
|
85
|
+
def clock_time
|
86
|
+
Time.now.to_f
|
164
87
|
end
|
88
|
+
end
|
89
|
+
module_function :clock_time
|
165
90
|
|
166
|
-
|
91
|
+
def parse_query(qs, d = nil, &unescaper)
|
92
|
+
Rack::Utils.default_query_parser.parse_query(qs, d, &unescaper)
|
167
93
|
end
|
168
|
-
module_function :
|
94
|
+
module_function :parse_query
|
169
95
|
|
170
|
-
def
|
171
|
-
|
96
|
+
def parse_nested_query(qs, d = nil)
|
97
|
+
Rack::Utils.default_query_parser.parse_nested_query(qs, d)
|
172
98
|
end
|
173
|
-
module_function :
|
99
|
+
module_function :parse_nested_query
|
174
100
|
|
175
101
|
def build_query(params)
|
176
102
|
params.map { |k, v|
|
@@ -236,13 +162,8 @@ module Rack
|
|
236
162
|
'"' => """,
|
237
163
|
"/" => "/"
|
238
164
|
}
|
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
|
165
|
+
|
166
|
+
ESCAPE_HTML_PATTERN = Regexp.union(*ESCAPE_HTML.keys)
|
246
167
|
|
247
168
|
# Escape ampersands, brackets and quotes to their HTML/XML entities.
|
248
169
|
def escape_html(string)
|
@@ -278,12 +199,28 @@ module Rack
|
|
278
199
|
end
|
279
200
|
module_function :select_best_encoding
|
280
201
|
|
281
|
-
def
|
202
|
+
def parse_cookies(env)
|
203
|
+
parse_cookies_header env[HTTP_COOKIE]
|
204
|
+
end
|
205
|
+
module_function :parse_cookies
|
206
|
+
|
207
|
+
def parse_cookies_header(header)
|
208
|
+
# According to RFC 2109:
|
209
|
+
# If multiple cookies satisfy the criteria above, they are ordered in
|
210
|
+
# the Cookie header such that those with more specific Path attributes
|
211
|
+
# precede those with less specific. Ordering with respect to other
|
212
|
+
# attributes (e.g., Domain) is unspecified.
|
213
|
+
cookies = parse_query(header, ';,') { |s| unescape(s) rescue s }
|
214
|
+
cookies.each_with_object({}) { |(k,v), hash| hash[k] = Array === v ? v.first : v }
|
215
|
+
end
|
216
|
+
module_function :parse_cookies_header
|
217
|
+
|
218
|
+
def add_cookie_to_header(header, key, value)
|
282
219
|
case value
|
283
220
|
when Hash
|
284
|
-
domain = "; domain
|
285
|
-
path = "; path
|
286
|
-
max_age = "; max-age
|
221
|
+
domain = "; domain=#{value[:domain]}" if value[:domain]
|
222
|
+
path = "; path=#{value[:path]}" if value[:path]
|
223
|
+
max_age = "; max-age=#{value[:max_age]}" if value[:max_age]
|
287
224
|
# There is an RFC mess in the area of date formatting for Cookies. Not
|
288
225
|
# only are there contradicting RFCs and examples within RFC text, but
|
289
226
|
# there are also numerous conflicting names of fields and partially
|
@@ -291,7 +228,7 @@ module Rack
|
|
291
228
|
#
|
292
229
|
# These are best described in RFC 2616 3.3.1. This RFC text also
|
293
230
|
# specifies that RFC 822 as updated by RFC 1123 is preferred. That is a
|
294
|
-
# fixed length format with space-date
|
231
|
+
# fixed length format with space-date delimited fields.
|
295
232
|
#
|
296
233
|
# See also RFC 1123 section 5.2.14.
|
297
234
|
#
|
@@ -315,6 +252,8 @@ module Rack
|
|
315
252
|
case value[:same_site]
|
316
253
|
when false, nil
|
317
254
|
nil
|
255
|
+
when :none, 'None', :None
|
256
|
+
'; SameSite=None'.freeze
|
318
257
|
when :lax, 'Lax', :Lax
|
319
258
|
'; SameSite=Lax'.freeze
|
320
259
|
when true, :strict, 'Strict', :Strict
|
@@ -325,31 +264,37 @@ module Rack
|
|
325
264
|
value = value[:value]
|
326
265
|
end
|
327
266
|
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
267
|
|
332
|
-
|
268
|
+
cookie = "#{escape(key)}=#{value.map { |v| escape v }.join('&')}#{domain}" \
|
269
|
+
"#{path}#{max_age}#{expires}#{secure}#{httponly}#{same_site}"
|
270
|
+
|
271
|
+
case header
|
333
272
|
when nil, ''
|
334
|
-
|
273
|
+
cookie
|
335
274
|
when String
|
336
|
-
|
275
|
+
[header, cookie].join("\n")
|
337
276
|
when Array
|
338
|
-
|
277
|
+
(header + [cookie]).join("\n")
|
278
|
+
else
|
279
|
+
raise ArgumentError, "Unrecognized cookie header value. Expected String, Array, or nil, got #{header.inspect}"
|
339
280
|
end
|
281
|
+
end
|
282
|
+
module_function :add_cookie_to_header
|
340
283
|
|
284
|
+
def set_cookie_header!(header, key, value)
|
285
|
+
header[SET_COOKIE] = add_cookie_to_header(header[SET_COOKIE], key, value)
|
341
286
|
nil
|
342
287
|
end
|
343
288
|
module_function :set_cookie_header!
|
344
289
|
|
345
|
-
def
|
346
|
-
case header
|
290
|
+
def make_delete_cookie_header(header, key, value)
|
291
|
+
case header
|
347
292
|
when nil, ''
|
348
293
|
cookies = []
|
349
294
|
when String
|
350
|
-
cookies = header
|
295
|
+
cookies = header.split("\n")
|
351
296
|
when Array
|
352
|
-
cookies = header
|
297
|
+
cookies = header
|
353
298
|
end
|
354
299
|
|
355
300
|
cookies.reject! { |cookie|
|
@@ -362,29 +307,28 @@ module Rack
|
|
362
307
|
end
|
363
308
|
}
|
364
309
|
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
{:value => '', :path => nil, :domain => nil,
|
369
|
-
:max_age => '0',
|
370
|
-
:expires => Time.at(0) }.merge(value))
|
310
|
+
cookies.join("\n")
|
311
|
+
end
|
312
|
+
module_function :make_delete_cookie_header
|
371
313
|
|
314
|
+
def delete_cookie_header!(header, key, value = {})
|
315
|
+
header[SET_COOKIE] = add_remove_cookie_to_header(header[SET_COOKIE], key, value)
|
372
316
|
nil
|
373
317
|
end
|
374
318
|
module_function :delete_cookie_header!
|
375
319
|
|
376
|
-
#
|
377
|
-
#
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
320
|
+
# Adds a cookie that will *remove* a cookie from the client. Hence the
|
321
|
+
# strange method name.
|
322
|
+
def add_remove_cookie_to_header(header, key, value = {})
|
323
|
+
new_header = make_delete_cookie_header(header, key, value)
|
324
|
+
|
325
|
+
add_cookie_to_header(new_header, key,
|
326
|
+
{:value => '', :path => nil, :domain => nil,
|
327
|
+
:max_age => '0',
|
328
|
+
:expires => Time.at(0) }.merge(value))
|
329
|
+
|
386
330
|
end
|
387
|
-
module_function :
|
331
|
+
module_function :add_remove_cookie_to_header
|
388
332
|
|
389
333
|
def rfc2822(time)
|
390
334
|
time.rfc2822
|
@@ -411,8 +355,13 @@ module Rack
|
|
411
355
|
# Returns nil if the header is missing or syntactically invalid.
|
412
356
|
# Returns an empty array if none of the ranges are satisfiable.
|
413
357
|
def byte_ranges(env, size)
|
358
|
+
warn "`byte_ranges` is deprecated, please use `get_byte_ranges`" if $VERBOSE
|
359
|
+
get_byte_ranges env['HTTP_RANGE'], size
|
360
|
+
end
|
361
|
+
module_function :byte_ranges
|
362
|
+
|
363
|
+
def get_byte_ranges(http_range, size)
|
414
364
|
# See <http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35>
|
415
|
-
http_range = env['HTTP_RANGE']
|
416
365
|
return nil unless http_range && http_range =~ /bytes=([^;]+)/
|
417
366
|
ranges = []
|
418
367
|
$1.split(/,\s*/).each do |range_spec|
|
@@ -438,7 +387,7 @@ module Rack
|
|
438
387
|
end
|
439
388
|
ranges
|
440
389
|
end
|
441
|
-
module_function :
|
390
|
+
module_function :get_byte_ranges
|
442
391
|
|
443
392
|
# Constant time string comparison.
|
444
393
|
#
|
@@ -447,7 +396,7 @@ module Rack
|
|
447
396
|
# on variable length plaintext strings because it could leak length info
|
448
397
|
# via timing attacks.
|
449
398
|
def secure_compare(a, b)
|
450
|
-
return false unless bytesize
|
399
|
+
return false unless a.bytesize == b.bytesize
|
451
400
|
|
452
401
|
l = a.unpack("C*")
|
453
402
|
|
@@ -496,6 +445,12 @@ module Rack
|
|
496
445
|
hash.each { |k, v| self[k] = v }
|
497
446
|
end
|
498
447
|
|
448
|
+
# on dup/clone, we need to duplicate @names hash
|
449
|
+
def initialize_copy(other)
|
450
|
+
super
|
451
|
+
@names = other.names.dup
|
452
|
+
end
|
453
|
+
|
499
454
|
def each
|
500
455
|
super do |k, v|
|
501
456
|
yield(k, v.respond_to?(:to_ary) ? v.to_ary.join("\n") : v)
|
@@ -513,21 +468,20 @@ module Rack
|
|
513
468
|
end
|
514
469
|
|
515
470
|
def []=(k, v)
|
516
|
-
canonical = k.downcase
|
471
|
+
canonical = k.downcase.freeze
|
517
472
|
delete k if @names[canonical] && @names[canonical] != k # .delete is expensive, don't invoke it unless necessary
|
518
|
-
@names[
|
473
|
+
@names[canonical] = k
|
519
474
|
super k, v
|
520
475
|
end
|
521
476
|
|
522
477
|
def delete(k)
|
523
478
|
canonical = k.downcase
|
524
479
|
result = super @names.delete(canonical)
|
525
|
-
@names.delete_if { |name,| name.downcase == canonical }
|
526
480
|
result
|
527
481
|
end
|
528
482
|
|
529
483
|
def include?(k)
|
530
|
-
|
484
|
+
super || @names.include?(k.downcase)
|
531
485
|
end
|
532
486
|
|
533
487
|
alias_method :has_key?, :include?
|
@@ -549,45 +503,11 @@ module Rack
|
|
549
503
|
other.each { |k, v| self[k] = v }
|
550
504
|
self
|
551
505
|
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
506
|
|
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
|
507
|
+
protected
|
508
|
+
def names
|
509
|
+
@names
|
588
510
|
end
|
589
|
-
hash
|
590
|
-
end
|
591
511
|
end
|
592
512
|
|
593
513
|
# Every standard HTTP code mapped to the appropriate message.
|
@@ -635,6 +555,7 @@ module Rack
|
|
635
555
|
415 => 'Unsupported Media Type',
|
636
556
|
416 => 'Range Not Satisfiable',
|
637
557
|
417 => 'Expectation Failed',
|
558
|
+
421 => 'Misdirected Request',
|
638
559
|
422 => 'Unprocessable Entity',
|
639
560
|
423 => 'Locked',
|
640
561
|
424 => 'Failed Dependency',
|
@@ -642,6 +563,7 @@ module Rack
|
|
642
563
|
428 => 'Precondition Required',
|
643
564
|
429 => 'Too Many Requests',
|
644
565
|
431 => 'Request Header Fields Too Large',
|
566
|
+
451 => 'Unavailable for Legal Reasons',
|
645
567
|
500 => 'Internal Server Error',
|
646
568
|
501 => 'Not Implemented',
|
647
569
|
502 => 'Bad Gateway',
|
@@ -656,7 +578,7 @@ module Rack
|
|
656
578
|
}
|
657
579
|
|
658
580
|
# 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 <<
|
581
|
+
STATUS_WITH_NO_ENTITY_BODY = Set.new((100..199).to_a << 204 << 304)
|
660
582
|
|
661
583
|
SYMBOL_TO_STATUS_CODE = Hash[*HTTP_STATUS_CODES.map { |code, message|
|
662
584
|
[message.downcase.gsub(/\s|-|'/, '_').to_sym, code]
|
@@ -671,8 +593,6 @@ module Rack
|
|
671
593
|
end
|
672
594
|
module_function :status_code
|
673
595
|
|
674
|
-
Multipart = Rack::Multipart
|
675
|
-
|
676
596
|
PATH_SEPS = Regexp.union(*[::File::SEPARATOR, ::File::ALT_SEPARATOR].compact)
|
677
597
|
|
678
598
|
def clean_path_info(path_info)
|
@@ -691,5 +611,12 @@ module Rack
|
|
691
611
|
end
|
692
612
|
module_function :clean_path_info
|
693
613
|
|
614
|
+
NULL_BYTE = "\0".freeze
|
615
|
+
|
616
|
+
def valid_path?(path)
|
617
|
+
path.valid_encoding? && !path.include?(NULL_BYTE)
|
618
|
+
end
|
619
|
+
module_function :valid_path?
|
620
|
+
|
694
621
|
end
|
695
622
|
end
|
data/lib/rack.rb
CHANGED
@@ -7,7 +7,7 @@
|
|
7
7
|
# modules and classes.
|
8
8
|
#
|
9
9
|
# All modules meant for use in your application are <tt>autoload</tt>ed here,
|
10
|
-
# so it should be enough just to <tt>require rack
|
10
|
+
# so it should be enough just to <tt>require 'rack'</tt> in your code.
|
11
11
|
|
12
12
|
module Rack
|
13
13
|
# The Rack protocol version number implemented.
|
@@ -18,27 +18,80 @@ module Rack
|
|
18
18
|
VERSION.join(".")
|
19
19
|
end
|
20
20
|
|
21
|
+
RELEASE = "2.0.9"
|
22
|
+
|
21
23
|
# Return the Rack release as a dotted string.
|
22
24
|
def self.release
|
23
|
-
|
25
|
+
RELEASE
|
24
26
|
end
|
25
|
-
PATH_INFO = 'PATH_INFO'.freeze
|
26
|
-
REQUEST_METHOD = 'REQUEST_METHOD'.freeze
|
27
|
-
SCRIPT_NAME = 'SCRIPT_NAME'.freeze
|
28
|
-
QUERY_STRING = 'QUERY_STRING'.freeze
|
29
|
-
CACHE_CONTROL = 'Cache-Control'.freeze
|
30
|
-
CONTENT_LENGTH = 'Content-Length'.freeze
|
31
|
-
CONTENT_TYPE = 'Content-Type'.freeze
|
32
27
|
|
33
|
-
|
34
|
-
|
28
|
+
HTTP_HOST = 'HTTP_HOST'.freeze
|
29
|
+
HTTP_VERSION = 'HTTP_VERSION'.freeze
|
30
|
+
HTTPS = 'HTTPS'.freeze
|
31
|
+
PATH_INFO = 'PATH_INFO'.freeze
|
32
|
+
REQUEST_METHOD = 'REQUEST_METHOD'.freeze
|
33
|
+
REQUEST_PATH = 'REQUEST_PATH'.freeze
|
34
|
+
SCRIPT_NAME = 'SCRIPT_NAME'.freeze
|
35
|
+
QUERY_STRING = 'QUERY_STRING'.freeze
|
36
|
+
SERVER_PROTOCOL = 'SERVER_PROTOCOL'.freeze
|
37
|
+
SERVER_NAME = 'SERVER_NAME'.freeze
|
38
|
+
SERVER_ADDR = 'SERVER_ADDR'.freeze
|
39
|
+
SERVER_PORT = 'SERVER_PORT'.freeze
|
40
|
+
CACHE_CONTROL = 'Cache-Control'.freeze
|
41
|
+
CONTENT_LENGTH = 'Content-Length'.freeze
|
42
|
+
CONTENT_TYPE = 'Content-Type'.freeze
|
43
|
+
SET_COOKIE = 'Set-Cookie'.freeze
|
44
|
+
TRANSFER_ENCODING = 'Transfer-Encoding'.freeze
|
45
|
+
HTTP_COOKIE = 'HTTP_COOKIE'.freeze
|
46
|
+
ETAG = 'ETag'.freeze
|
47
|
+
|
48
|
+
# HTTP method verbs
|
49
|
+
GET = 'GET'.freeze
|
50
|
+
POST = 'POST'.freeze
|
51
|
+
PUT = 'PUT'.freeze
|
52
|
+
PATCH = 'PATCH'.freeze
|
53
|
+
DELETE = 'DELETE'.freeze
|
54
|
+
HEAD = 'HEAD'.freeze
|
55
|
+
OPTIONS = 'OPTIONS'.freeze
|
56
|
+
LINK = 'LINK'.freeze
|
57
|
+
UNLINK = 'UNLINK'.freeze
|
58
|
+
TRACE = 'TRACE'.freeze
|
59
|
+
|
60
|
+
# Rack environment variables
|
61
|
+
RACK_VERSION = 'rack.version'.freeze
|
62
|
+
RACK_TEMPFILES = 'rack.tempfiles'.freeze
|
63
|
+
RACK_ERRORS = 'rack.errors'.freeze
|
64
|
+
RACK_LOGGER = 'rack.logger'.freeze
|
65
|
+
RACK_INPUT = 'rack.input'.freeze
|
66
|
+
RACK_SESSION = 'rack.session'.freeze
|
67
|
+
RACK_SESSION_OPTIONS = 'rack.session.options'.freeze
|
68
|
+
RACK_SHOWSTATUS_DETAIL = 'rack.showstatus.detail'.freeze
|
69
|
+
RACK_MULTITHREAD = 'rack.multithread'.freeze
|
70
|
+
RACK_MULTIPROCESS = 'rack.multiprocess'.freeze
|
71
|
+
RACK_RUNONCE = 'rack.run_once'.freeze
|
72
|
+
RACK_URL_SCHEME = 'rack.url_scheme'.freeze
|
73
|
+
RACK_HIJACK = 'rack.hijack'.freeze
|
74
|
+
RACK_IS_HIJACK = 'rack.hijack?'.freeze
|
75
|
+
RACK_HIJACK_IO = 'rack.hijack_io'.freeze
|
76
|
+
RACK_RECURSIVE_INCLUDE = 'rack.recursive.include'.freeze
|
77
|
+
RACK_MULTIPART_BUFFER_SIZE = 'rack.multipart.buffer_size'.freeze
|
78
|
+
RACK_MULTIPART_TEMPFILE_FACTORY = 'rack.multipart.tempfile_factory'.freeze
|
79
|
+
RACK_REQUEST_FORM_INPUT = 'rack.request.form_input'.freeze
|
80
|
+
RACK_REQUEST_FORM_HASH = 'rack.request.form_hash'.freeze
|
81
|
+
RACK_REQUEST_FORM_VARS = 'rack.request.form_vars'.freeze
|
82
|
+
RACK_REQUEST_COOKIE_HASH = 'rack.request.cookie_hash'.freeze
|
83
|
+
RACK_REQUEST_COOKIE_STRING = 'rack.request.cookie_string'.freeze
|
84
|
+
RACK_REQUEST_QUERY_HASH = 'rack.request.query_hash'.freeze
|
85
|
+
RACK_REQUEST_QUERY_STRING = 'rack.request.query_string'.freeze
|
86
|
+
RACK_METHODOVERRIDE_ORIGINAL_METHOD = 'rack.methodoverride.original_method'.freeze
|
87
|
+
RACK_SESSION_UNPACKED_COOKIE_DATA = 'rack.session.unpacked_cookie_data'.freeze
|
35
88
|
|
36
89
|
autoload :Builder, "rack/builder"
|
37
90
|
autoload :BodyProxy, "rack/body_proxy"
|
38
91
|
autoload :Cascade, "rack/cascade"
|
39
92
|
autoload :Chunked, "rack/chunked"
|
40
|
-
autoload :CommonLogger, "rack/
|
41
|
-
autoload :ConditionalGet, "rack/
|
93
|
+
autoload :CommonLogger, "rack/common_logger"
|
94
|
+
autoload :ConditionalGet, "rack/conditional_get"
|
42
95
|
autoload :Config, "rack/config"
|
43
96
|
autoload :ContentLength, "rack/content_length"
|
44
97
|
autoload :ContentType, "rack/content_type"
|
@@ -52,16 +105,16 @@ module Rack
|
|
52
105
|
autoload :Lint, "rack/lint"
|
53
106
|
autoload :Lock, "rack/lock"
|
54
107
|
autoload :Logger, "rack/logger"
|
55
|
-
autoload :MethodOverride, "rack/
|
108
|
+
autoload :MethodOverride, "rack/method_override"
|
56
109
|
autoload :Mime, "rack/mime"
|
57
|
-
autoload :NullLogger, "rack/
|
110
|
+
autoload :NullLogger, "rack/null_logger"
|
58
111
|
autoload :Recursive, "rack/recursive"
|
59
112
|
autoload :Reloader, "rack/reloader"
|
60
113
|
autoload :Runtime, "rack/runtime"
|
61
114
|
autoload :Sendfile, "rack/sendfile"
|
62
115
|
autoload :Server, "rack/server"
|
63
|
-
autoload :ShowExceptions, "rack/
|
64
|
-
autoload :ShowStatus, "rack/
|
116
|
+
autoload :ShowExceptions, "rack/show_exceptions"
|
117
|
+
autoload :ShowStatus, "rack/show_status"
|
65
118
|
autoload :Static, "rack/static"
|
66
119
|
autoload :TempfileReaper, "rack/tempfile_reaper"
|
67
120
|
autoload :URLMap, "rack/urlmap"
|
@@ -91,8 +144,4 @@ module Rack
|
|
91
144
|
autoload :Pool, "rack/session/pool"
|
92
145
|
autoload :Memcache, "rack/session/memcache"
|
93
146
|
end
|
94
|
-
|
95
|
-
module Utils
|
96
|
-
autoload :OkJson, "rack/utils/okjson"
|
97
|
-
end
|
98
147
|
end
|