rack 1.3.10 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of rack might be problematic. Click here for more details.
- data/COPYING +1 -1
- data/KNOWN-ISSUES +0 -9
- data/README.rdoc +4 -118
- data/Rakefile +15 -0
- data/SPEC +3 -5
- data/lib/rack.rb +0 -12
- data/lib/rack/auth/abstract/request.rb +1 -5
- data/lib/rack/auth/basic.rb +1 -1
- data/lib/rack/auth/digest/nonce.rb +1 -1
- data/lib/rack/backports/uri/common_18.rb +28 -14
- data/lib/rack/backports/uri/common_192.rb +17 -14
- data/lib/rack/body_proxy.rb +0 -10
- data/lib/rack/builder.rb +26 -18
- data/lib/rack/cascade.rb +1 -12
- data/lib/rack/chunked.rb +2 -0
- data/lib/rack/content_type.rb +7 -1
- data/lib/rack/deflater.rb +1 -5
- data/lib/rack/directory.rb +5 -1
- data/lib/rack/file.rb +26 -9
- data/lib/rack/handler.rb +2 -2
- data/lib/rack/head.rb +0 -1
- data/lib/rack/lint.rb +3 -5
- data/lib/rack/methodoverride.rb +10 -4
- data/lib/rack/mime.rb +606 -171
- data/lib/rack/mock.rb +2 -1
- data/lib/rack/multipart.rb +2 -2
- data/lib/rack/multipart/parser.rb +3 -10
- data/lib/rack/reloader.rb +1 -1
- data/lib/rack/request.rb +45 -13
- data/lib/rack/response.rb +15 -14
- data/lib/rack/sendfile.rb +8 -6
- data/lib/rack/server.rb +4 -30
- data/lib/rack/session/abstract/id.rb +25 -6
- data/lib/rack/session/cookie.rb +12 -16
- data/lib/rack/static.rb +21 -8
- data/lib/rack/urlmap.rb +28 -13
- data/lib/rack/utils.rb +22 -28
- data/rack.gemspec +5 -5
- data/test/builder/end.ru +2 -0
- data/test/cgi/lighttpd.conf +1 -0
- data/test/cgi/sample_rackup.ru +1 -1
- data/test/cgi/test+directory/test+file +1 -0
- data/test/cgi/test.ru +1 -1
- data/test/gemloader.rb +6 -2
- data/test/spec_auth_basic.rb +4 -9
- data/test/spec_auth_digest.rb +3 -16
- data/test/spec_body_proxy.rb +0 -4
- data/test/spec_builder.rb +63 -20
- data/test/spec_cascade.rb +10 -13
- data/test/spec_cgi.rb +1 -1
- data/test/spec_chunked.rb +39 -12
- data/test/spec_commonlogger.rb +4 -3
- data/test/spec_conditionalget.rb +16 -12
- data/test/spec_content_length.rb +1 -1
- data/test/spec_content_type.rb +6 -0
- data/test/spec_deflater.rb +2 -2
- data/test/spec_directory.rb +12 -0
- data/test/spec_fastcgi.rb +1 -1
- data/test/spec_file.rb +58 -8
- data/test/spec_head.rb +6 -18
- data/test/spec_lint.rb +2 -2
- data/test/spec_methodoverride.rb +15 -0
- data/test/spec_mock.rb +6 -2
- data/test/spec_mongrel.rb +8 -8
- data/test/spec_multipart.rb +10 -63
- data/test/spec_request.rb +94 -21
- data/test/spec_response.rb +22 -24
- data/test/spec_sendfile.rb +3 -0
- data/test/spec_server.rb +2 -49
- data/test/spec_session_cookie.rb +58 -22
- data/test/spec_session_memcache.rb +31 -1
- data/test/spec_session_pool.rb +10 -4
- data/test/spec_static.rb +8 -0
- data/test/spec_thin.rb +2 -2
- data/test/spec_utils.rb +38 -35
- data/test/spec_webrick.rb +5 -3
- data/test/static/index.html +1 -0
- metadata +13 -18
- data/contrib/rack.png +0 -0
- data/contrib/rack.svg +0 -150
- data/lib/rack/backports/uri/common_193.rb +0 -29
- data/test/builder/line.ru +0 -1
- data/test/spec_auth.rb +0 -57
data/lib/rack/mock.rb
CHANGED
@@ -57,6 +57,7 @@ module Rack
|
|
57
57
|
def post(uri, opts={}) request("POST", uri, opts) end
|
58
58
|
def put(uri, opts={}) request("PUT", uri, opts) end
|
59
59
|
def delete(uri, opts={}) request("DELETE", uri, opts) end
|
60
|
+
def head(uri, opts={}) request("HEAD", uri, opts) end
|
60
61
|
|
61
62
|
def request(method="GET", uri="", opts={})
|
62
63
|
env = self.class.env_for(uri, opts.merge(:method => method))
|
@@ -182,7 +183,7 @@ module Rack
|
|
182
183
|
end
|
183
184
|
|
184
185
|
def empty?
|
185
|
-
[201, 204, 304].include? status
|
186
|
+
[201, 204, 205, 304].include? status
|
186
187
|
end
|
187
188
|
end
|
188
189
|
end
|
data/lib/rack/multipart.rb
CHANGED
@@ -12,7 +12,7 @@ module Rack
|
|
12
12
|
MULTIPART = %r|\Amultipart/.*boundary=\"?([^\";,]+)\"?|n
|
13
13
|
TOKEN = /[^\s()<>,;:\\"\/\[\]?=]+/
|
14
14
|
CONDISP = /Content-Disposition:\s*#{TOKEN}\s*/i
|
15
|
-
DISPPARM = /;\s*(#{TOKEN})=("(?:\\"|[^"])*"|#{TOKEN})
|
15
|
+
DISPPARM = /;\s*(#{TOKEN})=("(?:\\"|[^"])*"|#{TOKEN})*/
|
16
16
|
RFC2183 = /^#{CONDISP}(#{DISPPARM})+$/i
|
17
17
|
BROKEN_QUOTED = /^#{CONDISP}.*;\sfilename="(.*?)"(?:\s*$|\s*;\s*#{TOKEN}=)/i
|
18
18
|
BROKEN_UNQUOTED = /^#{CONDISP}.*;\sfilename=(#{TOKEN})/i
|
@@ -31,4 +31,4 @@ module Rack
|
|
31
31
|
end
|
32
32
|
|
33
33
|
end
|
34
|
-
end
|
34
|
+
end
|
@@ -78,16 +78,9 @@ module Rack
|
|
78
78
|
|
79
79
|
def fast_forward_to_first_boundary
|
80
80
|
loop do
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
while @buf.gsub!(/\A([^\n]*\n)/, '')
|
86
|
-
read_buffer = $1
|
87
|
-
return if read_buffer == full_boundary
|
88
|
-
end
|
89
|
-
|
90
|
-
raise EOFError, "bad content body" if Utils.bytesize(@buf) >= BUFSIZE
|
81
|
+
read_buffer = @io.gets
|
82
|
+
break if read_buffer == full_boundary
|
83
|
+
raise EOFError, "bad content body" if read_buffer.nil?
|
91
84
|
end
|
92
85
|
end
|
93
86
|
|
data/lib/rack/reloader.rb
CHANGED
data/lib/rack/request.rb
CHANGED
@@ -72,6 +72,8 @@ module Rack
|
|
72
72
|
'https'
|
73
73
|
elsif @env['HTTP_X_FORWARDED_SSL'] == 'on'
|
74
74
|
'https'
|
75
|
+
elsif @env['HTTP_X_FORWARDED_SCHEME']
|
76
|
+
@env['HTTP_X_FORWARDED_SCHEME']
|
75
77
|
elsif @env['HTTP_X_FORWARDED_PROTO']
|
76
78
|
@env['HTTP_X_FORWARDED_PROTO'].split(',')[0]
|
77
79
|
else
|
@@ -113,15 +115,32 @@ module Rack
|
|
113
115
|
def script_name=(s); @env["SCRIPT_NAME"] = s.to_s end
|
114
116
|
def path_info=(s); @env["PATH_INFO"] = s.to_s end
|
115
117
|
|
118
|
+
|
119
|
+
# Checks the HTTP request method (or verb) to see if it was of type DELETE
|
116
120
|
def delete?; request_method == "DELETE" end
|
121
|
+
|
122
|
+
# Checks the HTTP request method (or verb) to see if it was of type GET
|
117
123
|
def get?; request_method == "GET" end
|
124
|
+
|
125
|
+
# Checks the HTTP request method (or verb) to see if it was of type HEAD
|
118
126
|
def head?; request_method == "HEAD" end
|
127
|
+
|
128
|
+
# Checks the HTTP request method (or verb) to see if it was of type OPTIONS
|
119
129
|
def options?; request_method == "OPTIONS" end
|
130
|
+
|
131
|
+
# Checks the HTTP request method (or verb) to see if it was of type PATCH
|
120
132
|
def patch?; request_method == "PATCH" end
|
133
|
+
|
134
|
+
# Checks the HTTP request method (or verb) to see if it was of type POST
|
121
135
|
def post?; request_method == "POST" end
|
136
|
+
|
137
|
+
# Checks the HTTP request method (or verb) to see if it was of type PUT
|
122
138
|
def put?; request_method == "PUT" end
|
139
|
+
|
140
|
+
# Checks the HTTP request method (or verb) to see if it was of type TRACE
|
123
141
|
def trace?; request_method == "TRACE" end
|
124
142
|
|
143
|
+
|
125
144
|
# The set of form-data media-types. Requests that do not indicate
|
126
145
|
# one of the media types presents in this list will not be eligible
|
127
146
|
# for form-data / param parsing.
|
@@ -233,8 +252,8 @@ module Rack
|
|
233
252
|
hash = @env["rack.request.cookie_hash"] ||= {}
|
234
253
|
string = @env["HTTP_COOKIE"]
|
235
254
|
|
236
|
-
hash.clear unless string
|
237
255
|
return hash if string == @env["rack.request.cookie_string"]
|
256
|
+
hash.clear
|
238
257
|
|
239
258
|
# According to RFC 2109:
|
240
259
|
# If multiple cookies satisfy the criteria above, they are ordered in
|
@@ -245,7 +264,8 @@ module Rack
|
|
245
264
|
@env["rack.request.cookie_string"] = string
|
246
265
|
hash
|
247
266
|
rescue => error
|
248
|
-
|
267
|
+
error.message.replace "cannot parse Cookie header: #{error.message}"
|
268
|
+
raise
|
249
269
|
end
|
250
270
|
|
251
271
|
def xhr?
|
@@ -278,23 +298,35 @@ module Rack
|
|
278
298
|
end
|
279
299
|
|
280
300
|
def accept_encoding
|
281
|
-
@env["HTTP_ACCEPT_ENCODING"].to_s.split(
|
282
|
-
|
283
|
-
|
284
|
-
if
|
285
|
-
|
286
|
-
else
|
287
|
-
raise "Invalid value for Accept-Encoding: #{part.inspect}"
|
301
|
+
@env["HTTP_ACCEPT_ENCODING"].to_s.split(/\s*,\s*/).map do |part|
|
302
|
+
encoding, parameters = part.split(/\s*;\s*/, 2)
|
303
|
+
quality = 1.0
|
304
|
+
if parameters and /\Aq=([\d.]+)/ =~ parameters
|
305
|
+
quality = $1.to_f
|
288
306
|
end
|
307
|
+
[encoding, quality]
|
289
308
|
end
|
290
309
|
end
|
291
310
|
|
311
|
+
def trusted_proxy?(ip)
|
312
|
+
ip =~ /^127\.0\.0\.1$|^(10|172\.(1[6-9]|2[0-9]|30|31)|192\.168)\.|^::1$|^fd[0-9a-f]{2}:.+|^localhost$/i
|
313
|
+
end
|
314
|
+
|
292
315
|
def ip
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
316
|
+
remote_addrs = @env['REMOTE_ADDR'] ? @env['REMOTE_ADDR'].split(/[,\s]+/) : []
|
317
|
+
remote_addrs.reject! { |addr| trusted_proxy?(addr) }
|
318
|
+
|
319
|
+
return remote_addrs.first if remote_addrs.any?
|
320
|
+
|
321
|
+
forwarded_ips = @env['HTTP_X_FORWARDED_FOR'] ? @env['HTTP_X_FORWARDED_FOR'].strip.split(/[,\s]+/) : []
|
322
|
+
|
323
|
+
if client_ip = @env['HTTP_CLIENT_IP']
|
324
|
+
# If forwarded_ips doesn't include the client_ip, it might be an
|
325
|
+
# ip spoofing attempt, so we ignore HTTP_CLIENT_IP
|
326
|
+
return client_ip if forwarded_ips.include?(client_ip)
|
297
327
|
end
|
328
|
+
|
329
|
+
return forwarded_ips.reject { |ip| trusted_proxy?(ip) }.last || @env["REMOTE_ADDR"]
|
298
330
|
end
|
299
331
|
|
300
332
|
protected
|
data/lib/rack/response.rb
CHANGED
@@ -19,7 +19,7 @@ module Rack
|
|
19
19
|
class Response
|
20
20
|
attr_accessor :length
|
21
21
|
|
22
|
-
def initialize(body=[], status=200, header={}
|
22
|
+
def initialize(body=[], status=200, header={})
|
23
23
|
@status = status.to_i
|
24
24
|
@header = Utils::HeaderHash.new("Content-Type" => "text/html").
|
25
25
|
merge(header)
|
@@ -71,13 +71,12 @@ module Rack
|
|
71
71
|
def finish(&block)
|
72
72
|
@block = block
|
73
73
|
|
74
|
-
if [204, 304].include?(status.to_i)
|
74
|
+
if [204, 205, 304].include?(status.to_i)
|
75
75
|
header.delete "Content-Type"
|
76
76
|
header.delete "Content-Length"
|
77
|
-
close
|
78
77
|
[status.to_i, header, []]
|
79
78
|
else
|
80
|
-
[status.to_i, header,
|
79
|
+
[status.to_i, header, self]
|
81
80
|
end
|
82
81
|
end
|
83
82
|
alias to_a finish # For *response
|
@@ -113,19 +112,21 @@ module Rack
|
|
113
112
|
alias headers header
|
114
113
|
|
115
114
|
module Helpers
|
116
|
-
def invalid?;
|
115
|
+
def invalid?; status < 100 || status >= 600; end
|
117
116
|
|
118
|
-
def informational?;
|
119
|
-
def successful?;
|
120
|
-
def redirection?;
|
121
|
-
def client_error?;
|
122
|
-
def server_error?;
|
117
|
+
def informational?; status >= 100 && status < 200; end
|
118
|
+
def successful?; status >= 200 && status < 300; end
|
119
|
+
def redirection?; status >= 300 && status < 400; end
|
120
|
+
def client_error?; status >= 400 && status < 500; end
|
121
|
+
def server_error?; status >= 500 && status < 600; end
|
123
122
|
|
124
|
-
def ok?;
|
125
|
-
def
|
126
|
-
def
|
123
|
+
def ok?; status == 200; end
|
124
|
+
def bad_request?; status == 400; end
|
125
|
+
def forbidden?; status == 403; end
|
126
|
+
def not_found?; status == 404; end
|
127
|
+
def unprocessable?; status == 422; end
|
127
128
|
|
128
|
-
def redirect?; [301, 302, 303, 307].include?
|
129
|
+
def redirect?; [301, 302, 303, 307].include? status; end
|
129
130
|
|
130
131
|
# Headers
|
131
132
|
attr_reader :headers, :original_headers
|
data/lib/rack/sendfile.rb
CHANGED
@@ -46,10 +46,11 @@ module Rack
|
|
46
46
|
# proxy_pass http://127.0.0.1:8080/;
|
47
47
|
# }
|
48
48
|
#
|
49
|
-
# Note that the X-Sendfile-Type header must be set exactly as shown above.
|
50
|
-
# X-Accel-Mapping header should specify the
|
51
|
-
# equals sign (=), followed name of the
|
52
|
-
# to. The middleware performs a simple substitution on the
|
49
|
+
# Note that the X-Sendfile-Type header must be set exactly as shown above.
|
50
|
+
# The X-Accel-Mapping header should specify the location on the file system,
|
51
|
+
# followed by an equals sign (=), followed name of the private URL pattern
|
52
|
+
# that it maps to. The middleware performs a simple substitution on the
|
53
|
+
# resulting path.
|
53
54
|
#
|
54
55
|
# See Also: http://wiki.codemongers.com/NginxXSendfile
|
55
56
|
#
|
@@ -104,10 +105,11 @@ module Rack
|
|
104
105
|
when 'X-Accel-Redirect'
|
105
106
|
path = F.expand_path(body.to_path)
|
106
107
|
if url = map_accel_path(env, path)
|
108
|
+
headers['Content-Length'] = '0'
|
107
109
|
headers[type] = url
|
108
110
|
body = []
|
109
111
|
else
|
110
|
-
env['rack.errors']
|
112
|
+
env['rack.errors'].puts "X-Accel-Mapping header missing"
|
111
113
|
end
|
112
114
|
when 'X-Sendfile', 'X-Lighttpd-Send-File'
|
113
115
|
path = F.expand_path(body.to_path)
|
@@ -116,7 +118,7 @@ module Rack
|
|
116
118
|
body = []
|
117
119
|
when '', nil
|
118
120
|
else
|
119
|
-
env['rack.errors']
|
121
|
+
env['rack.errors'].puts "Unknown x-sendfile variation: '#{variation}'.\n"
|
120
122
|
end
|
121
123
|
end
|
122
124
|
[status, headers, body]
|
data/lib/rack/server.rb
CHANGED
@@ -26,7 +26,7 @@ module Rack
|
|
26
26
|
|
27
27
|
opts.on("-I", "--include PATH",
|
28
28
|
"specify $LOAD_PATH (may be used more than once)") { |path|
|
29
|
-
|
29
|
+
options[:include] = path.split(":")
|
30
30
|
}
|
31
31
|
|
32
32
|
opts.on("-r", "--require LIBRARY",
|
@@ -226,7 +226,7 @@ module Rack
|
|
226
226
|
self.class.middleware
|
227
227
|
end
|
228
228
|
|
229
|
-
def start
|
229
|
+
def start &blk
|
230
230
|
if options[:warn]
|
231
231
|
$-w = true
|
232
232
|
end
|
@@ -247,14 +247,11 @@ module Rack
|
|
247
247
|
pp app
|
248
248
|
end
|
249
249
|
|
250
|
-
check_pid! if options[:pid]
|
251
|
-
|
252
250
|
# Touch the wrapped app, so that the config.ru is loaded before
|
253
251
|
# daemonization (i.e. before chdir, etc).
|
254
252
|
wrapped_app
|
255
253
|
|
256
254
|
daemonize_app if options[:daemonize]
|
257
|
-
|
258
255
|
write_pid if options[:pid]
|
259
256
|
|
260
257
|
trap(:INT) do
|
@@ -265,7 +262,7 @@ module Rack
|
|
265
262
|
end
|
266
263
|
end
|
267
264
|
|
268
|
-
server.run wrapped_app, options
|
265
|
+
server.run wrapped_app, options, &blk
|
269
266
|
end
|
270
267
|
|
271
268
|
def server
|
@@ -277,7 +274,7 @@ module Rack
|
|
277
274
|
options = default_options
|
278
275
|
|
279
276
|
# Don't evaluate CGI ISINDEX parameters.
|
280
|
-
# http://
|
277
|
+
# http://hoohoo.ncsa.uiuc.edu/cgi/cl.html
|
281
278
|
args.clear if ENV.include?("REQUEST_METHOD")
|
282
279
|
|
283
280
|
options.merge! opt_parser.parse!(args)
|
@@ -322,28 +319,5 @@ module Rack
|
|
322
319
|
::File.open(options[:pid], 'w'){ |f| f.write("#{Process.pid}") }
|
323
320
|
at_exit { ::File.delete(options[:pid]) if ::File.exist?(options[:pid]) }
|
324
321
|
end
|
325
|
-
|
326
|
-
def check_pid!
|
327
|
-
case pidfile_process_status
|
328
|
-
when :running, :not_owned
|
329
|
-
$stderr.puts "A server is already running. Check #{options[:pid]}."
|
330
|
-
exit(1)
|
331
|
-
when :dead
|
332
|
-
::File.delete(options[:pid])
|
333
|
-
end
|
334
|
-
end
|
335
|
-
|
336
|
-
def pidfile_process_status
|
337
|
-
return :exited unless ::File.exist?(options[:pid])
|
338
|
-
|
339
|
-
pid = ::File.read(options[:pid]).to_i
|
340
|
-
Process.kill(0, pid)
|
341
|
-
:running
|
342
|
-
rescue Errno::ESRCH
|
343
|
-
:dead
|
344
|
-
rescue Errno::EPERM
|
345
|
-
:not_owned
|
346
|
-
end
|
347
|
-
|
348
322
|
end
|
349
323
|
end
|
@@ -95,8 +95,11 @@ module Rack
|
|
95
95
|
end
|
96
96
|
|
97
97
|
def inspect
|
98
|
-
|
99
|
-
|
98
|
+
if loaded?
|
99
|
+
super
|
100
|
+
else
|
101
|
+
"#<#{self.class}:0x#{self.object_id.to_s(16)} not yet loaded>"
|
102
|
+
end
|
100
103
|
end
|
101
104
|
|
102
105
|
def exists?
|
@@ -108,6 +111,11 @@ module Rack
|
|
108
111
|
@loaded
|
109
112
|
end
|
110
113
|
|
114
|
+
def empty?
|
115
|
+
load_for_read!
|
116
|
+
super
|
117
|
+
end
|
118
|
+
|
111
119
|
private
|
112
120
|
|
113
121
|
def load_for_read!
|
@@ -144,7 +152,9 @@ module Rack
|
|
144
152
|
# 'rack.session'
|
145
153
|
# * :path, :domain, :expire_after, :secure, and :httponly set the related
|
146
154
|
# cookie options as by Rack::Response#add_cookie
|
147
|
-
# * :
|
155
|
+
# * :skip will not a set a cookie in the response nor update the session state
|
156
|
+
# * :defer will not set a cookie in the response but still update the session
|
157
|
+
# state if it is used with a backend
|
148
158
|
# * :renew (implementation dependent) will prompt the generation of a new
|
149
159
|
# session id, and migration of data to be referenced at the new id. If
|
150
160
|
# :defer is set, it will be overridden and the cookie will be set.
|
@@ -260,21 +270,30 @@ module Rack
|
|
260
270
|
end
|
261
271
|
|
262
272
|
# Session should be commited if it was loaded, any of specific options like :renew, :drop
|
263
|
-
# or :expire_after was given and the security permissions match.
|
273
|
+
# or :expire_after was given and the security permissions match. Skips if skip is given.
|
264
274
|
|
265
275
|
def commit_session?(env, session, options)
|
266
|
-
|
276
|
+
if options[:skip]
|
277
|
+
false
|
278
|
+
else
|
279
|
+
has_session = loaded_session?(session) || forced_session_update?(session, options)
|
280
|
+
has_session && security_matches?(env, options)
|
281
|
+
end
|
267
282
|
end
|
268
283
|
|
269
284
|
def loaded_session?(session)
|
270
285
|
!session.is_a?(SessionHash) || session.loaded?
|
271
286
|
end
|
272
287
|
|
288
|
+
def forced_session_update?(session, options)
|
289
|
+
force_options?(options) && session && !session.empty?
|
290
|
+
end
|
291
|
+
|
273
292
|
def force_options?(options)
|
274
293
|
options.values_at(:renew, :drop, :defer, :expire_after).any?
|
275
294
|
end
|
276
295
|
|
277
|
-
def
|
296
|
+
def security_matches?(env, options)
|
278
297
|
return true unless options[:secure]
|
279
298
|
request = Rack::Request.new(env)
|
280
299
|
request.ssl?
|
data/lib/rack/session/cookie.rb
CHANGED
@@ -14,6 +14,7 @@ module Rack
|
|
14
14
|
# Both methods must take a string and return a string.
|
15
15
|
#
|
16
16
|
# When the secret key is set, cookie data is checked for data integrity.
|
17
|
+
# The old secret key is also accepted and allows graceful secret rotation.
|
17
18
|
#
|
18
19
|
# Example:
|
19
20
|
#
|
@@ -21,14 +22,15 @@ module Rack
|
|
21
22
|
# :domain => 'foo.com',
|
22
23
|
# :path => '/',
|
23
24
|
# :expire_after => 2592000,
|
24
|
-
# :secret => 'change_me'
|
25
|
+
# :secret => 'change_me',
|
26
|
+
# :old_secret => 'also_change_me'
|
25
27
|
#
|
26
28
|
# All parameters are optional.
|
27
29
|
#
|
28
30
|
# Example of a cookie with no encoding:
|
29
31
|
#
|
30
32
|
# Rack::Session::Cookie.new(application, {
|
31
|
-
# :coder =>
|
33
|
+
# :coder => Rack::Session::Cookie::Identity.new
|
32
34
|
# })
|
33
35
|
#
|
34
36
|
# Example of a cookie with custom encoding:
|
@@ -80,15 +82,7 @@ module Rack
|
|
80
82
|
|
81
83
|
def initialize(app, options={})
|
82
84
|
@secret = options[:secret]
|
83
|
-
|
84
|
-
SECURITY WARNING: No secret option provided to Rack::Session::Cookie.
|
85
|
-
This poses a security threat. It is strongly recommended that you
|
86
|
-
provide a secret to prevent exploits that may be possible from crafted
|
87
|
-
cookies. This will not be supported in future versions of Rack, and
|
88
|
-
future versions will even invalidate your existing user cookies.
|
89
|
-
|
90
|
-
Called from: #{caller[0]}.
|
91
|
-
MSG
|
85
|
+
@old_secret = options[:old_secret]
|
92
86
|
@coder = options[:coder] ||= Base64::Marshal.new
|
93
87
|
super(app, options.merge!(:cookie_only => true))
|
94
88
|
end
|
@@ -110,9 +104,11 @@ module Rack
|
|
110
104
|
request = Rack::Request.new(env)
|
111
105
|
session_data = request.cookies[@key]
|
112
106
|
|
113
|
-
if @secret && session_data
|
107
|
+
if (@secret || @old_secret) && session_data
|
114
108
|
session_data, digest = session_data.split("--")
|
115
|
-
session_data
|
109
|
+
if (digest != generate_hmac(session_data, @secret)) && (digest != generate_hmac(session_data, @old_secret))
|
110
|
+
session_data = nil
|
111
|
+
end
|
116
112
|
end
|
117
113
|
|
118
114
|
coder.decode(session_data) || {}
|
@@ -136,7 +132,7 @@ module Rack
|
|
136
132
|
session_data = coder.encode(session)
|
137
133
|
|
138
134
|
if @secret
|
139
|
-
session_data = "#{session_data}--#{generate_hmac(session_data)}"
|
135
|
+
session_data = "#{session_data}--#{generate_hmac(session_data, @secret)}"
|
140
136
|
end
|
141
137
|
|
142
138
|
if session_data.size > (4096 - @key.size)
|
@@ -152,8 +148,8 @@ module Rack
|
|
152
148
|
generate_sid unless options[:drop]
|
153
149
|
end
|
154
150
|
|
155
|
-
def generate_hmac(data)
|
156
|
-
OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA1.new,
|
151
|
+
def generate_hmac(data, secret)
|
152
|
+
OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA1.new, secret, data)
|
157
153
|
end
|
158
154
|
|
159
155
|
end
|