rack 1.4.7 → 2.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/CHANGELOG.md +77 -0
- data/{COPYING → MIT-LICENSE} +4 -2
- data/README.rdoc +122 -456
- data/Rakefile +32 -31
- data/SPEC +119 -29
- 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 +7 -5
- data/lib/rack/auth/abstract/request.rb +8 -6
- data/lib/rack/auth/basic.rb +5 -2
- data/lib/rack/auth/digest/md5.rb +10 -8
- 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 +4 -2
- data/lib/rack/body_proxy.rb +11 -9
- data/lib/rack/builder.rb +63 -20
- data/lib/rack/cascade.rb +10 -9
- data/lib/rack/chunked.rb +45 -11
- data/lib/rack/{commonlogger.rb → common_logger.rb} +24 -15
- data/lib/rack/{conditionalget.rb → conditional_get.rb} +20 -6
- data/lib/rack/config.rb +7 -0
- data/lib/rack/content_length.rb +12 -6
- data/lib/rack/content_type.rb +4 -2
- data/lib/rack/core_ext/regexp.rb +14 -0
- data/lib/rack/deflater.rb +73 -42
- data/lib/rack/directory.rb +77 -56
- data/lib/rack/etag.rb +25 -13
- data/lib/rack/events.rb +156 -0
- data/lib/rack/file.rb +4 -143
- data/lib/rack/files.rb +178 -0
- data/lib/rack/handler/cgi.rb +18 -17
- data/lib/rack/handler/fastcgi.rb +21 -17
- data/lib/rack/handler/lsws.rb +14 -12
- data/lib/rack/handler/scgi.rb +27 -21
- data/lib/rack/handler/thin.rb +19 -5
- data/lib/rack/handler/webrick.rb +66 -24
- data/lib/rack/handler.rb +29 -19
- data/lib/rack/head.rb +21 -14
- data/lib/rack/lint.rb +259 -65
- data/lib/rack/lobster.rb +17 -10
- data/lib/rack/lock.rb +19 -10
- data/lib/rack/logger.rb +4 -2
- data/lib/rack/media_type.rb +43 -0
- data/lib/rack/method_override.rb +52 -0
- data/lib/rack/mime.rb +43 -6
- data/lib/rack/mock.rb +109 -44
- data/lib/rack/multipart/generator.rb +11 -12
- data/lib/rack/multipart/parser.rb +302 -115
- data/lib/rack/multipart/uploaded_file.rb +4 -3
- data/lib/rack/multipart.rb +40 -9
- data/lib/rack/null_logger.rb +39 -0
- data/lib/rack/query_parser.rb +218 -0
- data/lib/rack/recursive.rb +14 -11
- data/lib/rack/reloader.rb +12 -5
- data/lib/rack/request.rb +484 -270
- data/lib/rack/response.rb +196 -77
- data/lib/rack/rewindable_input.rb +5 -14
- data/lib/rack/runtime.rb +13 -6
- data/lib/rack/sendfile.rb +44 -20
- data/lib/rack/server.rb +175 -61
- data/lib/rack/session/abstract/id.rb +276 -133
- data/lib/rack/session/cookie.rb +75 -40
- data/lib/rack/session/memcache.rb +4 -87
- data/lib/rack/session/pool.rb +24 -18
- data/lib/rack/show_exceptions.rb +392 -0
- data/lib/rack/{showstatus.rb → show_status.rb} +11 -9
- data/lib/rack/static.rb +65 -38
- data/lib/rack/tempfile_reaper.rb +24 -0
- data/lib/rack/urlmap.rb +40 -15
- data/lib/rack/utils.rb +316 -285
- data/lib/rack.rb +78 -23
- data/rack.gemspec +26 -19
- metadata +44 -209
- data/KNOWN-ISSUES +0 -30
- 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 -100
- data/lib/rack/handler/swiftiplied_mongrel.rb +0 -8
- data/lib/rack/methodoverride.rb +0 -33
- data/lib/rack/nulllogger.rb +0 -18
- data/lib/rack/showexceptions.rb +0 -378
- 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/lighttpd.errors +0 -1
- 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_with_escaped_quotes +0 -6
- data/test/multipart/filename_with_escaped_quotes_and_modification_param +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/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.rb +0 -57
- data/test/spec_auth_basic.rb +0 -81
- data/test/spec_auth_digest.rb +0 -259
- data/test/spec_body_proxy.rb +0 -69
- data/test/spec_builder.rb +0 -207
- data/test/spec_cascade.rb +0 -61
- data/test/spec_cgi.rb +0 -102
- data/test/spec_chunked.rb +0 -87
- data/test/spec_commonlogger.rb +0 -57
- data/test/spec_conditionalget.rb +0 -102
- data/test/spec_config.rb +0 -22
- data/test/spec_content_length.rb +0 -86
- data/test/spec_content_type.rb +0 -45
- data/test/spec_deflater.rb +0 -187
- data/test/spec_directory.rb +0 -88
- data/test/spec_etag.rb +0 -98
- data/test/spec_fastcgi.rb +0 -107
- data/test/spec_file.rb +0 -200
- data/test/spec_handler.rb +0 -59
- data/test/spec_head.rb +0 -48
- data/test/spec_lint.rb +0 -515
- data/test/spec_lobster.rb +0 -58
- data/test/spec_lock.rb +0 -167
- data/test/spec_logger.rb +0 -23
- data/test/spec_methodoverride.rb +0 -72
- data/test/spec_mock.rb +0 -269
- data/test/spec_mongrel.rb +0 -182
- data/test/spec_multipart.rb +0 -479
- data/test/spec_nulllogger.rb +0 -23
- data/test/spec_recursive.rb +0 -72
- data/test/spec_request.rb +0 -955
- data/test/spec_response.rb +0 -313
- data/test/spec_rewindable_input.rb +0 -118
- data/test/spec_runtime.rb +0 -49
- data/test/spec_sendfile.rb +0 -90
- data/test/spec_server.rb +0 -121
- data/test/spec_session_abstract_id.rb +0 -43
- data/test/spec_session_cookie.rb +0 -361
- data/test/spec_session_memcache.rb +0 -321
- data/test/spec_session_pool.rb +0 -209
- data/test/spec_showexceptions.rb +0 -92
- data/test/spec_showstatus.rb +0 -84
- data/test/spec_static.rb +0 -145
- data/test/spec_thin.rb +0 -86
- data/test/spec_urlmap.rb +0 -213
- data/test/spec_utils.rb +0 -554
- data/test/spec_webrick.rb +0 -143
- 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,102 +1,159 @@
|
|
|
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
|
|
10
|
+
require 'securerandom'
|
|
11
|
+
require 'digest/sha2'
|
|
12
12
|
|
|
13
13
|
module Rack
|
|
14
14
|
|
|
15
15
|
module Session
|
|
16
16
|
|
|
17
|
+
class SessionId
|
|
18
|
+
ID_VERSION = 2
|
|
19
|
+
|
|
20
|
+
attr_reader :public_id
|
|
21
|
+
|
|
22
|
+
def initialize(public_id)
|
|
23
|
+
@public_id = public_id
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def private_id
|
|
27
|
+
"#{ID_VERSION}::#{hash_sid(public_id)}"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
alias :cookie_value :public_id
|
|
31
|
+
alias :to_s :public_id
|
|
32
|
+
|
|
33
|
+
def empty?; false; end
|
|
34
|
+
def inspect; public_id.inspect; end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def hash_sid(sid)
|
|
39
|
+
Digest::SHA256.hexdigest(sid)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
17
43
|
module Abstract
|
|
18
|
-
|
|
19
|
-
|
|
44
|
+
# SessionHash is responsible to lazily load the session from store.
|
|
45
|
+
|
|
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)
|
|
20
58
|
|
|
21
|
-
|
|
59
|
+
include Enumerable
|
|
60
|
+
attr_writer :id
|
|
22
61
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
@session_id_loaded = false
|
|
28
|
-
merge!(default_options)
|
|
62
|
+
Unspecified = Object.new
|
|
63
|
+
|
|
64
|
+
def self.find(req)
|
|
65
|
+
req.get_header RACK_SESSION
|
|
29
66
|
end
|
|
30
67
|
|
|
31
|
-
def
|
|
32
|
-
|
|
33
|
-
super
|
|
68
|
+
def self.set(req, session)
|
|
69
|
+
req.set_header RACK_SESSION, session
|
|
34
70
|
end
|
|
35
71
|
|
|
36
|
-
|
|
72
|
+
def self.set_options(req, options)
|
|
73
|
+
req.set_header RACK_SESSION_OPTIONS, options.dup
|
|
74
|
+
end
|
|
37
75
|
|
|
38
|
-
def
|
|
39
|
-
|
|
76
|
+
def initialize(store, req)
|
|
77
|
+
@store = store
|
|
78
|
+
@req = req
|
|
79
|
+
@loaded = false
|
|
40
80
|
end
|
|
41
81
|
|
|
42
|
-
def
|
|
43
|
-
|
|
44
|
-
@
|
|
82
|
+
def id
|
|
83
|
+
return @id if @loaded or instance_variable_defined?(:@id)
|
|
84
|
+
@id = @store.send(:extract_session_id, @req)
|
|
45
85
|
end
|
|
46
|
-
end
|
|
47
86
|
|
|
48
|
-
|
|
87
|
+
def options
|
|
88
|
+
@req.session_options
|
|
89
|
+
end
|
|
49
90
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
@by = by
|
|
54
|
-
@env = env
|
|
55
|
-
@loaded = false
|
|
91
|
+
def each(&block)
|
|
92
|
+
load_for_read!
|
|
93
|
+
@data.each(&block)
|
|
56
94
|
end
|
|
57
95
|
|
|
58
96
|
def [](key)
|
|
59
97
|
load_for_read!
|
|
60
|
-
|
|
98
|
+
@data[key.to_s]
|
|
99
|
+
end
|
|
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
|
|
61
108
|
end
|
|
62
109
|
|
|
63
110
|
def has_key?(key)
|
|
64
111
|
load_for_read!
|
|
65
|
-
|
|
112
|
+
@data.has_key?(key.to_s)
|
|
66
113
|
end
|
|
67
114
|
alias :key? :has_key?
|
|
68
115
|
alias :include? :has_key?
|
|
69
116
|
|
|
70
117
|
def []=(key, value)
|
|
71
118
|
load_for_write!
|
|
72
|
-
|
|
119
|
+
@data[key.to_s] = value
|
|
73
120
|
end
|
|
121
|
+
alias :store :[]=
|
|
74
122
|
|
|
75
123
|
def clear
|
|
76
124
|
load_for_write!
|
|
77
|
-
|
|
125
|
+
@data.clear
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def destroy
|
|
129
|
+
clear
|
|
130
|
+
@id = @store.send(:delete_session, @req, id, options)
|
|
78
131
|
end
|
|
79
132
|
|
|
80
133
|
def to_hash
|
|
81
134
|
load_for_read!
|
|
82
|
-
|
|
83
|
-
h.delete_if { |k,v| v.nil? }
|
|
84
|
-
h
|
|
135
|
+
@data.dup
|
|
85
136
|
end
|
|
86
137
|
|
|
87
138
|
def update(hash)
|
|
88
139
|
load_for_write!
|
|
89
|
-
|
|
140
|
+
@data.update(stringify_keys(hash))
|
|
141
|
+
end
|
|
142
|
+
alias :merge! :update
|
|
143
|
+
|
|
144
|
+
def replace(hash)
|
|
145
|
+
load_for_write!
|
|
146
|
+
@data.replace(stringify_keys(hash))
|
|
90
147
|
end
|
|
91
148
|
|
|
92
149
|
def delete(key)
|
|
93
150
|
load_for_write!
|
|
94
|
-
|
|
151
|
+
@data.delete(key.to_s)
|
|
95
152
|
end
|
|
96
153
|
|
|
97
154
|
def inspect
|
|
98
155
|
if loaded?
|
|
99
|
-
|
|
156
|
+
@data.inspect
|
|
100
157
|
else
|
|
101
158
|
"#<#{self.class}:0x#{self.object_id.to_s(16)} not yet loaded>"
|
|
102
159
|
end
|
|
@@ -104,7 +161,8 @@ module Rack
|
|
|
104
161
|
|
|
105
162
|
def exists?
|
|
106
163
|
return @exists if instance_variable_defined?(:@exists)
|
|
107
|
-
@
|
|
164
|
+
@data = {}
|
|
165
|
+
@exists = @store.send(:session_exists?, @req)
|
|
108
166
|
end
|
|
109
167
|
|
|
110
168
|
def loaded?
|
|
@@ -113,12 +171,17 @@ module Rack
|
|
|
113
171
|
|
|
114
172
|
def empty?
|
|
115
173
|
load_for_read!
|
|
116
|
-
|
|
174
|
+
@data.empty?
|
|
117
175
|
end
|
|
118
176
|
|
|
119
|
-
def
|
|
120
|
-
|
|
121
|
-
|
|
177
|
+
def keys
|
|
178
|
+
load_for_read!
|
|
179
|
+
@data.keys
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def values
|
|
183
|
+
load_for_read!
|
|
184
|
+
@data.values
|
|
122
185
|
end
|
|
123
186
|
|
|
124
187
|
private
|
|
@@ -132,31 +195,26 @@ module Rack
|
|
|
132
195
|
end
|
|
133
196
|
|
|
134
197
|
def load!
|
|
135
|
-
id, session = @
|
|
136
|
-
@
|
|
137
|
-
replace(stringify_keys(session))
|
|
198
|
+
@id, session = @store.send(:load_session, @req)
|
|
199
|
+
@data = stringify_keys(session)
|
|
138
200
|
@loaded = true
|
|
139
201
|
end
|
|
140
202
|
|
|
141
203
|
def stringify_keys(other)
|
|
142
|
-
|
|
143
|
-
other.each do |key, value|
|
|
144
|
-
hash[key.to_s] = value
|
|
145
|
-
end
|
|
146
|
-
hash
|
|
204
|
+
other.to_hash.transform_keys(&:to_s)
|
|
147
205
|
end
|
|
148
206
|
end
|
|
149
207
|
|
|
150
208
|
# ID sets up a basic framework for implementing an id based sessioning
|
|
151
209
|
# service. Cookies sent to the client for maintaining sessions will only
|
|
152
|
-
# contain an id reference. Only #
|
|
210
|
+
# contain an id reference. Only #find_session and #write_session are
|
|
153
211
|
# required to be overwritten.
|
|
154
212
|
#
|
|
155
213
|
# All parameters are optional.
|
|
156
214
|
# * :key determines the name of the cookie, by default it is
|
|
157
215
|
# 'rack.session'
|
|
158
216
|
# * :path, :domain, :expire_after, :secure, and :httponly set the related
|
|
159
|
-
# cookie options as by Rack::Response#
|
|
217
|
+
# cookie options as by Rack::Response#set_cookie
|
|
160
218
|
# * :skip will not a set a cookie in the response nor update the session state
|
|
161
219
|
# * :defer will not set a cookie in the response but still update the session
|
|
162
220
|
# state if it is used with a backend
|
|
@@ -167,33 +225,33 @@ module Rack
|
|
|
167
225
|
# id will be.
|
|
168
226
|
#
|
|
169
227
|
# These options can be set on a per request basis, at the location of
|
|
170
|
-
# env['rack.session.options']
|
|
171
|
-
# found within the options hash at the key :id. It is
|
|
172
|
-
# 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.
|
|
173
231
|
#
|
|
174
232
|
# Is Rack::Utils::Context compatible.
|
|
175
233
|
#
|
|
176
234
|
# Not included by default; you must require 'rack/session/abstract/id'
|
|
177
235
|
# to use.
|
|
178
236
|
|
|
179
|
-
class
|
|
237
|
+
class Persisted
|
|
180
238
|
DEFAULT_OPTIONS = {
|
|
181
|
-
:
|
|
182
|
-
:
|
|
183
|
-
:
|
|
184
|
-
:
|
|
185
|
-
:
|
|
186
|
-
:
|
|
187
|
-
:
|
|
188
|
-
:
|
|
189
|
-
:
|
|
190
|
-
:
|
|
191
|
-
:
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
attr_reader :key, :default_options
|
|
195
|
-
|
|
196
|
-
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 = {})
|
|
197
255
|
@app = app
|
|
198
256
|
@default_options = self.class::DEFAULT_OPTIONS.merge(options)
|
|
199
257
|
@key = @default_options.delete(:key)
|
|
@@ -205,14 +263,21 @@ module Rack
|
|
|
205
263
|
context(env)
|
|
206
264
|
end
|
|
207
265
|
|
|
208
|
-
def context(env, app
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
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]
|
|
212
273
|
end
|
|
213
274
|
|
|
214
275
|
private
|
|
215
276
|
|
|
277
|
+
def make_request(env)
|
|
278
|
+
Rack::Request.new env
|
|
279
|
+
end
|
|
280
|
+
|
|
216
281
|
def initialize_sid
|
|
217
282
|
@sidbits = @default_options[:sidbits]
|
|
218
283
|
@sid_secure = @default_options[:secure_random]
|
|
@@ -225,7 +290,7 @@ module Rack
|
|
|
225
290
|
|
|
226
291
|
def generate_sid(secure = @sid_secure)
|
|
227
292
|
if secure
|
|
228
|
-
|
|
293
|
+
secure.hex(@sid_length)
|
|
229
294
|
else
|
|
230
295
|
"%0#{@sid_length}x" % Kernel.rand(2**@sidbits - 1)
|
|
231
296
|
end
|
|
@@ -236,58 +301,58 @@ module Rack
|
|
|
236
301
|
# Sets the lazy session at 'rack.session' and places options and session
|
|
237
302
|
# metadata into 'rack.session.options'.
|
|
238
303
|
|
|
239
|
-
def prepare_session(
|
|
240
|
-
session_was
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
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
|
|
244
310
|
end
|
|
245
311
|
|
|
246
312
|
# Extracts the session id from provided cookies and passes it and the
|
|
247
|
-
# environment to #
|
|
313
|
+
# environment to #find_session.
|
|
248
314
|
|
|
249
|
-
def load_session(
|
|
250
|
-
sid = current_session_id(
|
|
251
|
-
sid, session =
|
|
315
|
+
def load_session(req)
|
|
316
|
+
sid = current_session_id(req)
|
|
317
|
+
sid, session = find_session(req, sid)
|
|
252
318
|
[sid, session || {}]
|
|
253
319
|
end
|
|
254
320
|
|
|
255
321
|
# Extract session id from request object.
|
|
256
322
|
|
|
257
|
-
def extract_session_id(
|
|
258
|
-
request = Rack::Request.new(env)
|
|
323
|
+
def extract_session_id(request)
|
|
259
324
|
sid = request.cookies[@key]
|
|
260
325
|
sid ||= request.params[@key] unless @cookie_only
|
|
261
326
|
sid
|
|
262
327
|
end
|
|
263
328
|
|
|
264
|
-
# Returns the current session id from the
|
|
329
|
+
# Returns the current session id from the SessionHash.
|
|
265
330
|
|
|
266
|
-
def current_session_id(
|
|
267
|
-
|
|
331
|
+
def current_session_id(req)
|
|
332
|
+
req.get_header(RACK_SESSION).id
|
|
268
333
|
end
|
|
269
334
|
|
|
270
335
|
# Check if the session exists or not.
|
|
271
336
|
|
|
272
|
-
def session_exists?(
|
|
273
|
-
value = current_session_id(
|
|
337
|
+
def session_exists?(req)
|
|
338
|
+
value = current_session_id(req)
|
|
274
339
|
value && !value.empty?
|
|
275
340
|
end
|
|
276
341
|
|
|
277
|
-
# Session should be
|
|
342
|
+
# Session should be committed if it was loaded, any of specific options like :renew, :drop
|
|
278
343
|
# or :expire_after was given and the security permissions match. Skips if skip is given.
|
|
279
344
|
|
|
280
|
-
def commit_session?(
|
|
345
|
+
def commit_session?(req, session, options)
|
|
281
346
|
if options[:skip]
|
|
282
347
|
false
|
|
283
348
|
else
|
|
284
349
|
has_session = loaded_session?(session) || forced_session_update?(session, options)
|
|
285
|
-
has_session && security_matches?(
|
|
350
|
+
has_session && security_matches?(req, options)
|
|
286
351
|
end
|
|
287
352
|
end
|
|
288
353
|
|
|
289
354
|
def loaded_session?(session)
|
|
290
|
-
!session.is_a?(
|
|
355
|
+
!session.is_a?(session_class) || session.loaded?
|
|
291
356
|
end
|
|
292
357
|
|
|
293
358
|
def forced_session_update?(session, options)
|
|
@@ -295,81 +360,159 @@ module Rack
|
|
|
295
360
|
end
|
|
296
361
|
|
|
297
362
|
def force_options?(options)
|
|
298
|
-
options.values_at(:renew, :drop, :defer, :expire_after).any?
|
|
363
|
+
options.values_at(:max_age, :renew, :drop, :defer, :expire_after).any?
|
|
299
364
|
end
|
|
300
365
|
|
|
301
|
-
def security_matches?(
|
|
366
|
+
def security_matches?(request, options)
|
|
302
367
|
return true unless options[:secure]
|
|
303
|
-
request = Rack::Request.new(env)
|
|
304
368
|
request.ssl?
|
|
305
369
|
end
|
|
306
370
|
|
|
307
371
|
# Acquires the session from the environment and the session id from
|
|
308
|
-
# the session options and passes them to #
|
|
372
|
+
# the session options and passes them to #write_session. If successful
|
|
309
373
|
# and the :defer option is not true, a cookie will be added to the
|
|
310
374
|
# response with the session's id.
|
|
311
375
|
|
|
312
|
-
def commit_session(
|
|
313
|
-
session =
|
|
314
|
-
options =
|
|
376
|
+
def commit_session(req, res)
|
|
377
|
+
session = req.get_header RACK_SESSION
|
|
378
|
+
options = session.options
|
|
315
379
|
|
|
316
380
|
if options[:drop] || options[:renew]
|
|
317
|
-
session_id =
|
|
318
|
-
return
|
|
381
|
+
session_id = delete_session(req, session.id || generate_sid, options)
|
|
382
|
+
return unless session_id
|
|
319
383
|
end
|
|
320
384
|
|
|
321
|
-
return
|
|
385
|
+
return unless commit_session?(req, session, options)
|
|
322
386
|
|
|
323
387
|
session.send(:load!) unless loaded_session?(session)
|
|
324
|
-
|
|
325
|
-
|
|
388
|
+
session_id ||= session.id
|
|
389
|
+
session_data = session.to_hash.delete_if { |k, v| v.nil? }
|
|
326
390
|
|
|
327
|
-
if not data =
|
|
328
|
-
|
|
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.")
|
|
329
393
|
elsif options[:defer] and not options[:renew]
|
|
330
|
-
|
|
394
|
+
req.get_header(RACK_ERRORS).puts("Deferring cookie for #{session_id}") if $VERBOSE
|
|
331
395
|
else
|
|
332
396
|
cookie = Hash.new
|
|
333
|
-
cookie[:value] = data
|
|
397
|
+
cookie[:value] = cookie_value(data)
|
|
334
398
|
cookie[:expires] = Time.now + options[:expire_after] if options[:expire_after]
|
|
335
|
-
|
|
399
|
+
cookie[:expires] = Time.now + options[:max_age] if options[:max_age]
|
|
400
|
+
set_cookie(req, res, cookie.merge!(options))
|
|
336
401
|
end
|
|
402
|
+
end
|
|
403
|
+
public :commit_session
|
|
337
404
|
|
|
338
|
-
|
|
405
|
+
def cookie_value(data)
|
|
406
|
+
data
|
|
339
407
|
end
|
|
340
408
|
|
|
341
409
|
# Sets the cookie back to the client with session id. We skip the cookie
|
|
342
410
|
# setting if the value didn't change (sid is the same) or expires was given.
|
|
343
411
|
|
|
344
|
-
def set_cookie(
|
|
345
|
-
request = Rack::Request.new(env)
|
|
412
|
+
def set_cookie(request, res, cookie)
|
|
346
413
|
if request.cookies[@key] != cookie[:value] || cookie[:expires]
|
|
347
|
-
|
|
414
|
+
res.set_cookie_header =
|
|
415
|
+
Utils.add_cookie_to_header(res.set_cookie_header, @key, cookie)
|
|
416
|
+
end
|
|
417
|
+
end
|
|
418
|
+
|
|
419
|
+
# Allow subclasses to prepare_session for different Session classes
|
|
420
|
+
|
|
421
|
+
def session_class
|
|
422
|
+
SessionHash
|
|
423
|
+
end
|
|
424
|
+
|
|
425
|
+
# All thread safety and session retrieval procedures should occur here.
|
|
426
|
+
# Should return [session_id, session].
|
|
427
|
+
# If nil is provided as the session id, generation of a new valid id
|
|
428
|
+
# should occur within.
|
|
429
|
+
|
|
430
|
+
def find_session(env, sid)
|
|
431
|
+
raise '#find_session not implemented.'
|
|
432
|
+
end
|
|
433
|
+
|
|
434
|
+
# All thread safety and session storage procedures should occur here.
|
|
435
|
+
# Must return the session id if the session was saved successfully, or
|
|
436
|
+
# false if the session could not be saved.
|
|
437
|
+
|
|
438
|
+
def write_session(req, sid, session, options)
|
|
439
|
+
raise '#write_session not implemented.'
|
|
440
|
+
end
|
|
441
|
+
|
|
442
|
+
# All thread safety and session destroy procedures should occur here.
|
|
443
|
+
# Should return a new session id or nil if options[:drop]
|
|
444
|
+
|
|
445
|
+
def delete_session(req, sid, options)
|
|
446
|
+
raise '#delete_session not implemented'
|
|
447
|
+
end
|
|
448
|
+
end
|
|
449
|
+
|
|
450
|
+
class PersistedSecure < Persisted
|
|
451
|
+
class SecureSessionHash < SessionHash
|
|
452
|
+
def [](key)
|
|
453
|
+
if key == "session_id"
|
|
454
|
+
load_for_read!
|
|
455
|
+
id.public_id if id
|
|
456
|
+
else
|
|
457
|
+
super
|
|
458
|
+
end
|
|
459
|
+
end
|
|
460
|
+
end
|
|
461
|
+
|
|
462
|
+
def generate_sid(*)
|
|
463
|
+
public_id = super
|
|
464
|
+
|
|
465
|
+
SessionId.new(public_id)
|
|
466
|
+
end
|
|
467
|
+
|
|
468
|
+
def extract_session_id(*)
|
|
469
|
+
public_id = super
|
|
470
|
+
public_id && SessionId.new(public_id)
|
|
471
|
+
end
|
|
472
|
+
|
|
473
|
+
private
|
|
474
|
+
|
|
475
|
+
def session_class
|
|
476
|
+
SecureSessionHash
|
|
477
|
+
end
|
|
478
|
+
|
|
479
|
+
def cookie_value(data)
|
|
480
|
+
data.cookie_value
|
|
481
|
+
end
|
|
482
|
+
end
|
|
483
|
+
|
|
484
|
+
class ID < Persisted
|
|
485
|
+
def self.inherited(klass)
|
|
486
|
+
k = klass.ancestors.find { |kl| kl.respond_to?(:superclass) && kl.superclass == ID }
|
|
487
|
+
unless k.instance_variable_defined?(:"@_rack_warned")
|
|
488
|
+
warn "#{klass} is inheriting from #{ID}. Inheriting from #{ID} is deprecated, please inherit from #{Persisted} instead" if $VERBOSE
|
|
489
|
+
k.instance_variable_set(:"@_rack_warned", true)
|
|
348
490
|
end
|
|
491
|
+
super
|
|
349
492
|
end
|
|
350
493
|
|
|
351
|
-
# All thread safety and session
|
|
494
|
+
# All thread safety and session retrieval procedures should occur here.
|
|
352
495
|
# Should return [session_id, session].
|
|
353
496
|
# If nil is provided as the session id, generation of a new valid id
|
|
354
497
|
# should occur within.
|
|
355
498
|
|
|
356
|
-
def
|
|
357
|
-
|
|
499
|
+
def find_session(req, sid)
|
|
500
|
+
get_session req.env, sid
|
|
358
501
|
end
|
|
359
502
|
|
|
360
|
-
# All thread safety and session storage
|
|
361
|
-
#
|
|
362
|
-
#
|
|
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.
|
|
363
506
|
|
|
364
|
-
def
|
|
365
|
-
|
|
507
|
+
def write_session(req, sid, session, options)
|
|
508
|
+
set_session req.env, sid, session, options
|
|
366
509
|
end
|
|
367
510
|
|
|
368
|
-
# All thread safety and session destroy
|
|
511
|
+
# All thread safety and session destroy procedures should occur here.
|
|
369
512
|
# Should return a new session id or nil if options[:drop]
|
|
370
513
|
|
|
371
|
-
def
|
|
372
|
-
|
|
514
|
+
def delete_session(req, sid, options)
|
|
515
|
+
destroy_session req.env, sid, options
|
|
373
516
|
end
|
|
374
517
|
end
|
|
375
518
|
end
|