rack 2.0.9.3 → 3.0.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.

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