rack 1.6.13 → 2.1.4.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/CHANGELOG.md +92 -0
- data/{COPYING → MIT-LICENSE} +4 -2
- data/README.rdoc +105 -141
- data/Rakefile +27 -28
- data/SPEC +6 -7
- data/bin/rackup +1 -0
- data/contrib/rack_logo.svg +164 -111
- data/example/lobster.ru +2 -0
- data/example/protectedlobster.rb +4 -2
- data/example/protectedlobster.ru +3 -1
- data/lib/rack/auth/abstract/handler.rb +3 -1
- data/lib/rack/auth/abstract/request.rb +7 -1
- data/lib/rack/auth/basic.rb +4 -1
- data/lib/rack/auth/digest/md5.rb +9 -7
- data/lib/rack/auth/digest/nonce.rb +6 -3
- data/lib/rack/auth/digest/params.rb +5 -4
- data/lib/rack/auth/digest/request.rb +3 -1
- data/lib/rack/body_proxy.rb +11 -9
- data/lib/rack/builder.rb +42 -18
- data/lib/rack/cascade.rb +6 -5
- data/lib/rack/chunked.rb +33 -10
- data/lib/rack/{commonlogger.rb → common_logger.rb} +14 -10
- data/lib/rack/{conditionalget.rb → conditional_get.rb} +3 -1
- data/lib/rack/config.rb +2 -0
- data/lib/rack/content_length.rb +5 -3
- data/lib/rack/content_type.rb +3 -1
- data/lib/rack/core_ext/regexp.rb +14 -0
- data/lib/rack/deflater.rb +33 -53
- data/lib/rack/directory.rb +75 -60
- data/lib/rack/etag.rb +8 -5
- data/lib/rack/events.rb +156 -0
- data/lib/rack/file.rb +4 -149
- data/lib/rack/files.rb +178 -0
- data/lib/rack/handler/cgi.rb +18 -17
- data/lib/rack/handler/fastcgi.rb +17 -16
- data/lib/rack/handler/lsws.rb +14 -12
- data/lib/rack/handler/scgi.rb +22 -19
- data/lib/rack/handler/thin.rb +6 -1
- data/lib/rack/handler/webrick.rb +28 -28
- data/lib/rack/handler.rb +9 -26
- data/lib/rack/head.rb +17 -17
- data/lib/rack/lint.rb +55 -52
- data/lib/rack/lobster.rb +8 -6
- data/lib/rack/lock.rb +17 -10
- data/lib/rack/logger.rb +4 -2
- data/lib/rack/media_type.rb +43 -0
- data/lib/rack/{methodoverride.rb → method_override.rb} +10 -8
- data/lib/rack/mime.rb +27 -6
- data/lib/rack/mock.rb +101 -60
- data/lib/rack/multipart/generator.rb +11 -12
- data/lib/rack/multipart/parser.rb +292 -161
- data/lib/rack/multipart/uploaded_file.rb +3 -2
- data/lib/rack/multipart.rb +38 -8
- data/lib/rack/{nulllogger.rb → null_logger.rb} +3 -1
- data/lib/rack/query_parser.rb +218 -0
- data/lib/rack/recursive.rb +11 -9
- data/lib/rack/reloader.rb +10 -4
- data/lib/rack/request.rb +447 -305
- data/lib/rack/response.rb +196 -83
- data/lib/rack/rewindable_input.rb +5 -14
- data/lib/rack/runtime.rb +12 -18
- data/lib/rack/sendfile.rb +19 -14
- data/lib/rack/server.rb +118 -41
- data/lib/rack/session/abstract/id.rb +139 -94
- data/lib/rack/session/cookie.rb +34 -26
- data/lib/rack/session/memcache.rb +4 -93
- data/lib/rack/session/pool.rb +12 -10
- data/lib/rack/show_exceptions.rb +392 -0
- data/lib/rack/{showstatus.rb → show_status.rb} +7 -5
- data/lib/rack/static.rb +41 -11
- data/lib/rack/tempfile_reaper.rb +4 -2
- data/lib/rack/urlmap.rb +25 -15
- data/lib/rack/utils.rb +203 -277
- data/lib/rack.rb +76 -24
- data/rack.gemspec +25 -14
- metadata +62 -183
- data/HISTORY.md +0 -375
- 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/builder/anything.rb +0 -5
- data/test/builder/comment.ru +0 -4
- data/test/builder/end.ru +0 -5
- data/test/builder/line.ru +0 -1
- data/test/builder/options.ru +0 -2
- data/test/cgi/assets/folder/test.js +0 -1
- data/test/cgi/assets/fonts/font.eot +0 -1
- data/test/cgi/assets/images/image.png +0 -1
- data/test/cgi/assets/index.html +0 -1
- data/test/cgi/assets/javascripts/app.js +0 -1
- data/test/cgi/assets/stylesheets/app.css +0 -1
- data/test/cgi/lighttpd.conf +0 -26
- data/test/cgi/rackup_stub.rb +0 -6
- data/test/cgi/sample_rackup.ru +0 -5
- data/test/cgi/test +0 -9
- data/test/cgi/test+directory/test+file +0 -1
- data/test/cgi/test.fcgi +0 -8
- data/test/cgi/test.ru +0 -5
- data/test/gemloader.rb +0 -10
- data/test/multipart/bad_robots +0 -259
- data/test/multipart/binary +0 -0
- data/test/multipart/content_type_and_no_filename +0 -6
- data/test/multipart/empty +0 -10
- data/test/multipart/fail_16384_nofile +0 -814
- data/test/multipart/file1.txt +0 -1
- data/test/multipart/filename_and_modification_param +0 -7
- data/test/multipart/filename_and_no_name +0 -6
- data/test/multipart/filename_with_escaped_quotes +0 -6
- data/test/multipart/filename_with_escaped_quotes_and_modification_param +0 -7
- data/test/multipart/filename_with_null_byte +0 -7
- data/test/multipart/filename_with_percent_escaped_quotes +0 -6
- data/test/multipart/filename_with_unescaped_percentages +0 -6
- data/test/multipart/filename_with_unescaped_percentages2 +0 -6
- data/test/multipart/filename_with_unescaped_percentages3 +0 -6
- data/test/multipart/filename_with_unescaped_quotes +0 -6
- data/test/multipart/ie +0 -6
- data/test/multipart/invalid_character +0 -6
- data/test/multipart/mixed_files +0 -21
- data/test/multipart/nested +0 -10
- data/test/multipart/none +0 -9
- data/test/multipart/semicolon +0 -6
- data/test/multipart/text +0 -15
- data/test/multipart/three_files_three_fields +0 -31
- data/test/multipart/webkit +0 -32
- data/test/rackup/config.ru +0 -31
- data/test/registering_handler/rack/handler/registering_myself.rb +0 -8
- data/test/spec_auth_basic.rb +0 -81
- data/test/spec_auth_digest.rb +0 -259
- data/test/spec_body_proxy.rb +0 -85
- data/test/spec_builder.rb +0 -223
- data/test/spec_cascade.rb +0 -61
- data/test/spec_cgi.rb +0 -102
- data/test/spec_chunked.rb +0 -101
- data/test/spec_commonlogger.rb +0 -93
- data/test/spec_conditionalget.rb +0 -102
- data/test/spec_config.rb +0 -22
- data/test/spec_content_length.rb +0 -85
- data/test/spec_content_type.rb +0 -45
- data/test/spec_deflater.rb +0 -339
- data/test/spec_directory.rb +0 -88
- data/test/spec_etag.rb +0 -107
- data/test/spec_fastcgi.rb +0 -107
- data/test/spec_file.rb +0 -221
- data/test/spec_handler.rb +0 -72
- data/test/spec_head.rb +0 -45
- data/test/spec_lint.rb +0 -550
- data/test/spec_lobster.rb +0 -58
- data/test/spec_lock.rb +0 -164
- data/test/spec_logger.rb +0 -23
- data/test/spec_methodoverride.rb +0 -111
- data/test/spec_mime.rb +0 -51
- data/test/spec_mock.rb +0 -297
- data/test/spec_mongrel.rb +0 -182
- data/test/spec_multipart.rb +0 -600
- data/test/spec_nulllogger.rb +0 -20
- data/test/spec_recursive.rb +0 -72
- data/test/spec_request.rb +0 -1232
- data/test/spec_response.rb +0 -407
- data/test/spec_rewindable_input.rb +0 -118
- data/test/spec_runtime.rb +0 -49
- data/test/spec_sendfile.rb +0 -130
- data/test/spec_server.rb +0 -167
- data/test/spec_session_abstract_id.rb +0 -53
- data/test/spec_session_cookie.rb +0 -410
- data/test/spec_session_memcache.rb +0 -358
- data/test/spec_session_persisted_secure_secure_session_hash.rb +0 -73
- data/test/spec_session_pool.rb +0 -246
- data/test/spec_showexceptions.rb +0 -98
- data/test/spec_showstatus.rb +0 -103
- data/test/spec_static.rb +0 -145
- data/test/spec_tempfile_reaper.rb +0 -63
- data/test/spec_thin.rb +0 -91
- data/test/spec_urlmap.rb +0 -236
- data/test/spec_utils.rb +0 -647
- data/test/spec_version.rb +0 -17
- data/test/spec_webrick.rb +0 -184
- data/test/static/another/index.html +0 -1
- data/test/static/index.html +0 -1
- data/test/testrequest.rb +0 -78
- data/test/unregistered_handler/rack/handler/unregistered.rb +0 -7
- data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +0 -7
@@ -1,15 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# AUTHOR: blink <blinketje@gmail.com>; blink#ruby-lang@irc.freenode.net
|
2
4
|
# bugrep: Andreas Zehnder
|
3
5
|
|
6
|
+
require 'rack'
|
4
7
|
require 'time'
|
5
8
|
require 'rack/request'
|
6
9
|
require 'rack/response'
|
7
|
-
|
8
|
-
|
9
|
-
rescue LoadError
|
10
|
-
# We just won't get securerandom
|
11
|
-
end
|
12
|
-
require "digest/sha2"
|
10
|
+
require 'securerandom'
|
11
|
+
require 'digest/sha2'
|
13
12
|
|
14
13
|
module Rack
|
15
14
|
|
@@ -42,40 +41,51 @@ module Rack
|
|
42
41
|
end
|
43
42
|
|
44
43
|
module Abstract
|
45
|
-
ENV_SESSION_KEY = 'rack.session'.freeze
|
46
|
-
ENV_SESSION_OPTIONS_KEY = 'rack.session.options'.freeze
|
47
|
-
|
48
44
|
# SessionHash is responsible to lazily load the session from store.
|
49
45
|
|
50
46
|
class SessionHash
|
47
|
+
using Module.new {
|
48
|
+
refine Hash do
|
49
|
+
def transform_keys(&block)
|
50
|
+
hash = {}
|
51
|
+
each do |key, value|
|
52
|
+
hash[block.call(key)] = value
|
53
|
+
end
|
54
|
+
hash
|
55
|
+
end
|
56
|
+
end
|
57
|
+
} unless {}.respond_to?(:transform_keys)
|
58
|
+
|
51
59
|
include Enumerable
|
52
60
|
attr_writer :id
|
53
61
|
|
54
|
-
|
55
|
-
|
62
|
+
Unspecified = Object.new
|
63
|
+
|
64
|
+
def self.find(req)
|
65
|
+
req.get_header RACK_SESSION
|
56
66
|
end
|
57
67
|
|
58
|
-
def self.set(
|
59
|
-
|
68
|
+
def self.set(req, session)
|
69
|
+
req.set_header RACK_SESSION, session
|
60
70
|
end
|
61
71
|
|
62
|
-
def self.set_options(
|
63
|
-
|
72
|
+
def self.set_options(req, options)
|
73
|
+
req.set_header RACK_SESSION_OPTIONS, options.dup
|
64
74
|
end
|
65
75
|
|
66
|
-
def initialize(store,
|
76
|
+
def initialize(store, req)
|
67
77
|
@store = store
|
68
|
-
@
|
78
|
+
@req = req
|
69
79
|
@loaded = false
|
70
80
|
end
|
71
81
|
|
72
82
|
def id
|
73
83
|
return @id if @loaded or instance_variable_defined?(:@id)
|
74
|
-
@id = @store.send(:extract_session_id, @
|
84
|
+
@id = @store.send(:extract_session_id, @req)
|
75
85
|
end
|
76
86
|
|
77
87
|
def options
|
78
|
-
@
|
88
|
+
@req.session_options
|
79
89
|
end
|
80
90
|
|
81
91
|
def each(&block)
|
@@ -87,7 +97,15 @@ module Rack
|
|
87
97
|
load_for_read!
|
88
98
|
@data[key.to_s]
|
89
99
|
end
|
90
|
-
|
100
|
+
|
101
|
+
def fetch(key, default = Unspecified, &block)
|
102
|
+
load_for_read!
|
103
|
+
if default == Unspecified
|
104
|
+
@data.fetch(key.to_s, &block)
|
105
|
+
else
|
106
|
+
@data.fetch(key.to_s, default, &block)
|
107
|
+
end
|
108
|
+
end
|
91
109
|
|
92
110
|
def has_key?(key)
|
93
111
|
load_for_read!
|
@@ -109,7 +127,7 @@ module Rack
|
|
109
127
|
|
110
128
|
def destroy
|
111
129
|
clear
|
112
|
-
@id = @store.send(:
|
130
|
+
@id = @store.send(:delete_session, @req, id, options)
|
113
131
|
end
|
114
132
|
|
115
133
|
def to_hash
|
@@ -144,7 +162,7 @@ module Rack
|
|
144
162
|
def exists?
|
145
163
|
return @exists if instance_variable_defined?(:@exists)
|
146
164
|
@data = {}
|
147
|
-
@exists = @store.send(:session_exists?, @
|
165
|
+
@exists = @store.send(:session_exists?, @req)
|
148
166
|
end
|
149
167
|
|
150
168
|
def loaded?
|
@@ -157,10 +175,12 @@ module Rack
|
|
157
175
|
end
|
158
176
|
|
159
177
|
def keys
|
178
|
+
load_for_read!
|
160
179
|
@data.keys
|
161
180
|
end
|
162
181
|
|
163
182
|
def values
|
183
|
+
load_for_read!
|
164
184
|
@data.values
|
165
185
|
end
|
166
186
|
|
@@ -175,30 +195,26 @@ module Rack
|
|
175
195
|
end
|
176
196
|
|
177
197
|
def load!
|
178
|
-
@id, session = @store.send(:load_session, @
|
198
|
+
@id, session = @store.send(:load_session, @req)
|
179
199
|
@data = stringify_keys(session)
|
180
200
|
@loaded = true
|
181
201
|
end
|
182
202
|
|
183
203
|
def stringify_keys(other)
|
184
|
-
|
185
|
-
other.each do |key, value|
|
186
|
-
hash[key.to_s] = value
|
187
|
-
end
|
188
|
-
hash
|
204
|
+
other.to_hash.transform_keys(&:to_s)
|
189
205
|
end
|
190
206
|
end
|
191
207
|
|
192
208
|
# ID sets up a basic framework for implementing an id based sessioning
|
193
209
|
# service. Cookies sent to the client for maintaining sessions will only
|
194
|
-
# contain an id reference. Only #
|
210
|
+
# contain an id reference. Only #find_session and #write_session are
|
195
211
|
# required to be overwritten.
|
196
212
|
#
|
197
213
|
# All parameters are optional.
|
198
214
|
# * :key determines the name of the cookie, by default it is
|
199
215
|
# 'rack.session'
|
200
216
|
# * :path, :domain, :expire_after, :secure, and :httponly set the related
|
201
|
-
# cookie options as by Rack::Response#
|
217
|
+
# cookie options as by Rack::Response#set_cookie
|
202
218
|
# * :skip will not a set a cookie in the response nor update the session state
|
203
219
|
# * :defer will not set a cookie in the response but still update the session
|
204
220
|
# state if it is used with a backend
|
@@ -209,9 +225,9 @@ module Rack
|
|
209
225
|
# id will be.
|
210
226
|
#
|
211
227
|
# These options can be set on a per request basis, at the location of
|
212
|
-
# env['rack.session.options']
|
213
|
-
# found within the options hash at the key :id. It is
|
214
|
-
# recommended to change its value.
|
228
|
+
# <tt>env['rack.session.options']</tt>. Additionally the id of the
|
229
|
+
# session can be found within the options hash at the key :id. It is
|
230
|
+
# highly not recommended to change its value.
|
215
231
|
#
|
216
232
|
# Is Rack::Utils::Context compatible.
|
217
233
|
#
|
@@ -220,22 +236,22 @@ module Rack
|
|
220
236
|
|
221
237
|
class Persisted
|
222
238
|
DEFAULT_OPTIONS = {
|
223
|
-
:
|
224
|
-
:
|
225
|
-
:
|
226
|
-
:
|
227
|
-
:
|
228
|
-
:
|
229
|
-
:
|
230
|
-
:
|
231
|
-
:
|
232
|
-
:
|
233
|
-
:
|
234
|
-
}
|
235
|
-
|
236
|
-
attr_reader :key, :default_options
|
237
|
-
|
238
|
-
def initialize(app, options={})
|
239
|
+
key: RACK_SESSION,
|
240
|
+
path: '/',
|
241
|
+
domain: nil,
|
242
|
+
expire_after: nil,
|
243
|
+
secure: false,
|
244
|
+
httponly: true,
|
245
|
+
defer: false,
|
246
|
+
renew: false,
|
247
|
+
sidbits: 128,
|
248
|
+
cookie_only: true,
|
249
|
+
secure_random: ::SecureRandom
|
250
|
+
}.freeze
|
251
|
+
|
252
|
+
attr_reader :key, :default_options, :sid_secure
|
253
|
+
|
254
|
+
def initialize(app, options = {})
|
239
255
|
@app = app
|
240
256
|
@default_options = self.class::DEFAULT_OPTIONS.merge(options)
|
241
257
|
@key = @default_options.delete(:key)
|
@@ -247,14 +263,21 @@ module Rack
|
|
247
263
|
context(env)
|
248
264
|
end
|
249
265
|
|
250
|
-
def context(env, app
|
251
|
-
|
252
|
-
|
253
|
-
|
266
|
+
def context(env, app = @app)
|
267
|
+
req = make_request env
|
268
|
+
prepare_session(req)
|
269
|
+
status, headers, body = app.call(req.env)
|
270
|
+
res = Rack::Response::Raw.new status, headers
|
271
|
+
commit_session(req, res)
|
272
|
+
[status, headers, body]
|
254
273
|
end
|
255
274
|
|
256
275
|
private
|
257
276
|
|
277
|
+
def make_request(env)
|
278
|
+
Rack::Request.new env
|
279
|
+
end
|
280
|
+
|
258
281
|
def initialize_sid
|
259
282
|
@sidbits = @default_options[:sidbits]
|
260
283
|
@sid_secure = @default_options[:secure_random]
|
@@ -278,26 +301,26 @@ module Rack
|
|
278
301
|
# Sets the lazy session at 'rack.session' and places options and session
|
279
302
|
# metadata into 'rack.session.options'.
|
280
303
|
|
281
|
-
def prepare_session(
|
282
|
-
session_was
|
283
|
-
|
284
|
-
|
285
|
-
|
304
|
+
def prepare_session(req)
|
305
|
+
session_was = req.get_header RACK_SESSION
|
306
|
+
session = session_class.new(self, req)
|
307
|
+
req.set_header RACK_SESSION, session
|
308
|
+
req.set_header RACK_SESSION_OPTIONS, @default_options.dup
|
309
|
+
session.merge! session_was if session_was
|
286
310
|
end
|
287
311
|
|
288
312
|
# Extracts the session id from provided cookies and passes it and the
|
289
|
-
# environment to #
|
313
|
+
# environment to #find_session.
|
290
314
|
|
291
|
-
def load_session(
|
292
|
-
sid = current_session_id(
|
293
|
-
sid, session =
|
315
|
+
def load_session(req)
|
316
|
+
sid = current_session_id(req)
|
317
|
+
sid, session = find_session(req, sid)
|
294
318
|
[sid, session || {}]
|
295
319
|
end
|
296
320
|
|
297
321
|
# Extract session id from request object.
|
298
322
|
|
299
|
-
def extract_session_id(
|
300
|
-
request = Rack::Request.new(env)
|
323
|
+
def extract_session_id(request)
|
301
324
|
sid = request.cookies[@key]
|
302
325
|
sid ||= request.params[@key] unless @cookie_only
|
303
326
|
sid
|
@@ -305,26 +328,26 @@ module Rack
|
|
305
328
|
|
306
329
|
# Returns the current session id from the SessionHash.
|
307
330
|
|
308
|
-
def current_session_id(
|
309
|
-
|
331
|
+
def current_session_id(req)
|
332
|
+
req.get_header(RACK_SESSION).id
|
310
333
|
end
|
311
334
|
|
312
335
|
# Check if the session exists or not.
|
313
336
|
|
314
|
-
def session_exists?(
|
315
|
-
value = current_session_id(
|
337
|
+
def session_exists?(req)
|
338
|
+
value = current_session_id(req)
|
316
339
|
value && !value.empty?
|
317
340
|
end
|
318
341
|
|
319
342
|
# Session should be committed if it was loaded, any of specific options like :renew, :drop
|
320
343
|
# or :expire_after was given and the security permissions match. Skips if skip is given.
|
321
344
|
|
322
|
-
def commit_session?(
|
345
|
+
def commit_session?(req, session, options)
|
323
346
|
if options[:skip]
|
324
347
|
false
|
325
348
|
else
|
326
349
|
has_session = loaded_session?(session) || forced_session_update?(session, options)
|
327
|
-
has_session && security_matches?(
|
350
|
+
has_session && security_matches?(req, options)
|
328
351
|
end
|
329
352
|
end
|
330
353
|
|
@@ -340,46 +363,44 @@ module Rack
|
|
340
363
|
options.values_at(:max_age, :renew, :drop, :defer, :expire_after).any?
|
341
364
|
end
|
342
365
|
|
343
|
-
def security_matches?(
|
366
|
+
def security_matches?(request, options)
|
344
367
|
return true unless options[:secure]
|
345
|
-
request = Rack::Request.new(env)
|
346
368
|
request.ssl?
|
347
369
|
end
|
348
370
|
|
349
371
|
# Acquires the session from the environment and the session id from
|
350
|
-
# the session options and passes them to #
|
372
|
+
# the session options and passes them to #write_session. If successful
|
351
373
|
# and the :defer option is not true, a cookie will be added to the
|
352
374
|
# response with the session's id.
|
353
375
|
|
354
|
-
def commit_session(
|
355
|
-
session =
|
376
|
+
def commit_session(req, res)
|
377
|
+
session = req.get_header RACK_SESSION
|
356
378
|
options = session.options
|
357
379
|
|
358
380
|
if options[:drop] || options[:renew]
|
359
|
-
session_id =
|
360
|
-
return
|
381
|
+
session_id = delete_session(req, session.id || generate_sid, options)
|
382
|
+
return unless session_id
|
361
383
|
end
|
362
384
|
|
363
|
-
return
|
385
|
+
return unless commit_session?(req, session, options)
|
364
386
|
|
365
387
|
session.send(:load!) unless loaded_session?(session)
|
366
388
|
session_id ||= session.id
|
367
|
-
session_data = session.to_hash.delete_if { |k,v| v.nil? }
|
389
|
+
session_data = session.to_hash.delete_if { |k, v| v.nil? }
|
368
390
|
|
369
|
-
if not data =
|
370
|
-
|
391
|
+
if not data = write_session(req, session_id, session_data, options)
|
392
|
+
req.get_header(RACK_ERRORS).puts("Warning! #{self.class.name} failed to save session. Content dropped.")
|
371
393
|
elsif options[:defer] and not options[:renew]
|
372
|
-
|
394
|
+
req.get_header(RACK_ERRORS).puts("Deferring cookie for #{session_id}") if $VERBOSE
|
373
395
|
else
|
374
396
|
cookie = Hash.new
|
375
397
|
cookie[:value] = cookie_value(data)
|
376
398
|
cookie[:expires] = Time.now + options[:expire_after] if options[:expire_after]
|
377
399
|
cookie[:expires] = Time.now + options[:max_age] if options[:max_age]
|
378
|
-
set_cookie(
|
400
|
+
set_cookie(req, res, cookie.merge!(options))
|
379
401
|
end
|
380
|
-
|
381
|
-
[status, headers, body]
|
382
402
|
end
|
403
|
+
public :commit_session
|
383
404
|
|
384
405
|
def cookie_value(data)
|
385
406
|
data
|
@@ -388,10 +409,10 @@ module Rack
|
|
388
409
|
# Sets the cookie back to the client with session id. We skip the cookie
|
389
410
|
# setting if the value didn't change (sid is the same) or expires was given.
|
390
411
|
|
391
|
-
def set_cookie(
|
392
|
-
request = Rack::Request.new(env)
|
412
|
+
def set_cookie(request, res, cookie)
|
393
413
|
if request.cookies[@key] != cookie[:value] || cookie[:expires]
|
394
|
-
|
414
|
+
res.set_cookie_header =
|
415
|
+
Utils.add_cookie_to_header(res.set_cookie_header, @key, cookie)
|
395
416
|
end
|
396
417
|
end
|
397
418
|
|
@@ -406,23 +427,23 @@ module Rack
|
|
406
427
|
# If nil is provided as the session id, generation of a new valid id
|
407
428
|
# should occur within.
|
408
429
|
|
409
|
-
def
|
410
|
-
raise '#
|
430
|
+
def find_session(env, sid)
|
431
|
+
raise '#find_session not implemented.'
|
411
432
|
end
|
412
433
|
|
413
434
|
# All thread safety and session storage procedures should occur here.
|
414
435
|
# Must return the session id if the session was saved successfully, or
|
415
436
|
# false if the session could not be saved.
|
416
437
|
|
417
|
-
def
|
418
|
-
raise '#
|
438
|
+
def write_session(req, sid, session, options)
|
439
|
+
raise '#write_session not implemented.'
|
419
440
|
end
|
420
441
|
|
421
442
|
# All thread safety and session destroy procedures should occur here.
|
422
443
|
# Should return a new session id or nil if options[:drop]
|
423
444
|
|
424
|
-
def
|
425
|
-
raise '#
|
445
|
+
def delete_session(req, sid, options)
|
446
|
+
raise '#delete_session not implemented'
|
426
447
|
end
|
427
448
|
end
|
428
449
|
|
@@ -469,6 +490,30 @@ module Rack
|
|
469
490
|
end
|
470
491
|
super
|
471
492
|
end
|
493
|
+
|
494
|
+
# All thread safety and session retrieval procedures should occur here.
|
495
|
+
# Should return [session_id, session].
|
496
|
+
# If nil is provided as the session id, generation of a new valid id
|
497
|
+
# should occur within.
|
498
|
+
|
499
|
+
def find_session(req, sid)
|
500
|
+
get_session req.env, sid
|
501
|
+
end
|
502
|
+
|
503
|
+
# All thread safety and session storage procedures should occur here.
|
504
|
+
# Must return the session id if the session was saved successfully, or
|
505
|
+
# false if the session could not be saved.
|
506
|
+
|
507
|
+
def write_session(req, sid, session, options)
|
508
|
+
set_session req.env, sid, session, options
|
509
|
+
end
|
510
|
+
|
511
|
+
# All thread safety and session destroy procedures should occur here.
|
512
|
+
# Should return a new session id or nil if options[:drop]
|
513
|
+
|
514
|
+
def delete_session(req, sid, options)
|
515
|
+
destroy_session req.env, sid, options
|
516
|
+
end
|
472
517
|
end
|
473
518
|
end
|
474
519
|
end
|
data/lib/rack/session/cookie.rb
CHANGED
@@ -1,8 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'openssl'
|
2
4
|
require 'zlib'
|
3
5
|
require 'rack/request'
|
4
6
|
require 'rack/response'
|
5
7
|
require 'rack/session/abstract/id'
|
8
|
+
require 'json'
|
9
|
+
require 'base64'
|
6
10
|
|
7
11
|
module Rack
|
8
12
|
|
@@ -48,11 +52,11 @@ module Rack
|
|
48
52
|
# Encode session cookies as Base64
|
49
53
|
class Base64
|
50
54
|
def encode(str)
|
51
|
-
|
55
|
+
::Base64.strict_encode64(str)
|
52
56
|
end
|
53
57
|
|
54
58
|
def decode(str)
|
55
|
-
|
59
|
+
::Base64.decode64(str)
|
56
60
|
end
|
57
61
|
|
58
62
|
# Encode session cookies as Marshaled Base64 data
|
@@ -71,23 +75,23 @@ module Rack
|
|
71
75
|
# valid JSON composite type, either a Hash or an Array.
|
72
76
|
class JSON < Base64
|
73
77
|
def encode(obj)
|
74
|
-
super(::
|
78
|
+
super(::JSON.dump(obj))
|
75
79
|
end
|
76
80
|
|
77
81
|
def decode(str)
|
78
82
|
return unless str
|
79
|
-
::
|
83
|
+
::JSON.parse(super(str)) rescue nil
|
80
84
|
end
|
81
85
|
end
|
82
86
|
|
83
87
|
class ZipJSON < Base64
|
84
88
|
def encode(obj)
|
85
|
-
super(Zlib::Deflate.deflate(::
|
89
|
+
super(Zlib::Deflate.deflate(::JSON.dump(obj)))
|
86
90
|
end
|
87
91
|
|
88
92
|
def decode(str)
|
89
93
|
return unless str
|
90
|
-
::
|
94
|
+
::JSON.parse(Zlib::Inflate.inflate(super(str)))
|
91
95
|
rescue
|
92
96
|
nil
|
93
97
|
end
|
@@ -102,9 +106,11 @@ module Rack
|
|
102
106
|
|
103
107
|
attr_reader :coder
|
104
108
|
|
105
|
-
def initialize(app, options={})
|
109
|
+
def initialize(app, options = {})
|
106
110
|
@secrets = options.values_at(:secret, :old_secret).compact
|
107
|
-
|
111
|
+
@hmac = options.fetch(:hmac, OpenSSL::Digest::SHA1)
|
112
|
+
|
113
|
+
warn <<-MSG unless secure?(options)
|
108
114
|
SECURITY WARNING: No secret option provided to Rack::Session::Cookie.
|
109
115
|
This poses a security threat. It is strongly recommended that you
|
110
116
|
provide a secret to prevent exploits that may be possible from crafted
|
@@ -113,39 +119,36 @@ module Rack
|
|
113
119
|
|
114
120
|
Called from: #{caller[0]}.
|
115
121
|
MSG
|
116
|
-
@coder
|
117
|
-
super(app, options.merge!(:
|
122
|
+
@coder = options[:coder] ||= Base64::Marshal.new
|
123
|
+
super(app, options.merge!(cookie_only: true))
|
118
124
|
end
|
119
125
|
|
120
126
|
private
|
121
127
|
|
122
|
-
def
|
123
|
-
data = unpacked_cookie_data(
|
128
|
+
def find_session(req, sid)
|
129
|
+
data = unpacked_cookie_data(req)
|
124
130
|
data = persistent_session_id!(data)
|
125
131
|
[data["session_id"], data]
|
126
132
|
end
|
127
133
|
|
128
|
-
def extract_session_id(
|
129
|
-
unpacked_cookie_data(
|
134
|
+
def extract_session_id(request)
|
135
|
+
unpacked_cookie_data(request)["session_id"]
|
130
136
|
end
|
131
137
|
|
132
|
-
def unpacked_cookie_data(
|
133
|
-
|
134
|
-
request = Rack::Request.new(env)
|
138
|
+
def unpacked_cookie_data(request)
|
139
|
+
request.fetch_header(RACK_SESSION_UNPACKED_COOKIE_DATA) do |k|
|
135
140
|
session_data = request.cookies[@key]
|
136
141
|
|
137
142
|
if @secrets.size > 0 && session_data
|
138
|
-
|
139
|
-
digest.reverse! if digest
|
140
|
-
session_data.reverse! if session_data
|
143
|
+
session_data, _, digest = session_data.rpartition('--')
|
141
144
|
session_data = nil unless digest_match?(session_data, digest)
|
142
145
|
end
|
143
146
|
|
144
|
-
coder.decode(session_data) || {}
|
147
|
+
request.set_header(k, coder.decode(session_data) || {})
|
145
148
|
end
|
146
149
|
end
|
147
150
|
|
148
|
-
def persistent_session_id!(data, sid=nil)
|
151
|
+
def persistent_session_id!(data, sid = nil)
|
149
152
|
data ||= {}
|
150
153
|
data["session_id"] ||= sid || generate_sid
|
151
154
|
data
|
@@ -160,7 +163,7 @@ module Rack
|
|
160
163
|
end
|
161
164
|
end
|
162
165
|
|
163
|
-
def
|
166
|
+
def write_session(req, session_id, session, options)
|
164
167
|
session = session.merge("session_id" => session_id)
|
165
168
|
session_data = coder.encode(session)
|
166
169
|
|
@@ -169,14 +172,14 @@ module Rack
|
|
169
172
|
end
|
170
173
|
|
171
174
|
if session_data.size > (4096 - @key.size)
|
172
|
-
|
175
|
+
req.get_header(RACK_ERRORS).puts("Warning! Rack::Session::Cookie data size exceeds 4K.")
|
173
176
|
nil
|
174
177
|
else
|
175
178
|
SessionId.new(session_id, session_data)
|
176
179
|
end
|
177
180
|
end
|
178
181
|
|
179
|
-
def
|
182
|
+
def delete_session(req, session_id, options)
|
180
183
|
# Nothing to do here, data is in the client
|
181
184
|
generate_sid unless options[:drop]
|
182
185
|
end
|
@@ -189,7 +192,12 @@ module Rack
|
|
189
192
|
end
|
190
193
|
|
191
194
|
def generate_hmac(data, secret)
|
192
|
-
OpenSSL::HMAC.hexdigest(
|
195
|
+
OpenSSL::HMAC.hexdigest(@hmac.new, secret, data)
|
196
|
+
end
|
197
|
+
|
198
|
+
def secure?(options)
|
199
|
+
@secrets.size >= 1 ||
|
200
|
+
(options[:coder] && options[:let_coder_handle_secure_encoding])
|
193
201
|
end
|
194
202
|
|
195
203
|
end
|