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.

Files changed (188) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +92 -0
  3. data/{COPYING → MIT-LICENSE} +4 -2
  4. data/README.rdoc +105 -141
  5. data/Rakefile +27 -28
  6. data/SPEC +6 -7
  7. data/bin/rackup +1 -0
  8. data/contrib/rack_logo.svg +164 -111
  9. data/example/lobster.ru +2 -0
  10. data/example/protectedlobster.rb +4 -2
  11. data/example/protectedlobster.ru +3 -1
  12. data/lib/rack/auth/abstract/handler.rb +3 -1
  13. data/lib/rack/auth/abstract/request.rb +7 -1
  14. data/lib/rack/auth/basic.rb +4 -1
  15. data/lib/rack/auth/digest/md5.rb +9 -7
  16. data/lib/rack/auth/digest/nonce.rb +6 -3
  17. data/lib/rack/auth/digest/params.rb +5 -4
  18. data/lib/rack/auth/digest/request.rb +3 -1
  19. data/lib/rack/body_proxy.rb +11 -9
  20. data/lib/rack/builder.rb +42 -18
  21. data/lib/rack/cascade.rb +6 -5
  22. data/lib/rack/chunked.rb +33 -10
  23. data/lib/rack/{commonlogger.rb → common_logger.rb} +14 -10
  24. data/lib/rack/{conditionalget.rb → conditional_get.rb} +3 -1
  25. data/lib/rack/config.rb +2 -0
  26. data/lib/rack/content_length.rb +5 -3
  27. data/lib/rack/content_type.rb +3 -1
  28. data/lib/rack/core_ext/regexp.rb +14 -0
  29. data/lib/rack/deflater.rb +33 -53
  30. data/lib/rack/directory.rb +75 -60
  31. data/lib/rack/etag.rb +8 -5
  32. data/lib/rack/events.rb +156 -0
  33. data/lib/rack/file.rb +4 -149
  34. data/lib/rack/files.rb +178 -0
  35. data/lib/rack/handler/cgi.rb +18 -17
  36. data/lib/rack/handler/fastcgi.rb +17 -16
  37. data/lib/rack/handler/lsws.rb +14 -12
  38. data/lib/rack/handler/scgi.rb +22 -19
  39. data/lib/rack/handler/thin.rb +6 -1
  40. data/lib/rack/handler/webrick.rb +28 -28
  41. data/lib/rack/handler.rb +9 -26
  42. data/lib/rack/head.rb +17 -17
  43. data/lib/rack/lint.rb +55 -52
  44. data/lib/rack/lobster.rb +8 -6
  45. data/lib/rack/lock.rb +17 -10
  46. data/lib/rack/logger.rb +4 -2
  47. data/lib/rack/media_type.rb +43 -0
  48. data/lib/rack/{methodoverride.rb → method_override.rb} +10 -8
  49. data/lib/rack/mime.rb +27 -6
  50. data/lib/rack/mock.rb +101 -60
  51. data/lib/rack/multipart/generator.rb +11 -12
  52. data/lib/rack/multipart/parser.rb +292 -161
  53. data/lib/rack/multipart/uploaded_file.rb +3 -2
  54. data/lib/rack/multipart.rb +38 -8
  55. data/lib/rack/{nulllogger.rb → null_logger.rb} +3 -1
  56. data/lib/rack/query_parser.rb +218 -0
  57. data/lib/rack/recursive.rb +11 -9
  58. data/lib/rack/reloader.rb +10 -4
  59. data/lib/rack/request.rb +447 -305
  60. data/lib/rack/response.rb +196 -83
  61. data/lib/rack/rewindable_input.rb +5 -14
  62. data/lib/rack/runtime.rb +12 -18
  63. data/lib/rack/sendfile.rb +19 -14
  64. data/lib/rack/server.rb +118 -41
  65. data/lib/rack/session/abstract/id.rb +139 -94
  66. data/lib/rack/session/cookie.rb +34 -26
  67. data/lib/rack/session/memcache.rb +4 -93
  68. data/lib/rack/session/pool.rb +12 -10
  69. data/lib/rack/show_exceptions.rb +392 -0
  70. data/lib/rack/{showstatus.rb → show_status.rb} +7 -5
  71. data/lib/rack/static.rb +41 -11
  72. data/lib/rack/tempfile_reaper.rb +4 -2
  73. data/lib/rack/urlmap.rb +25 -15
  74. data/lib/rack/utils.rb +203 -277
  75. data/lib/rack.rb +76 -24
  76. data/rack.gemspec +25 -14
  77. metadata +62 -183
  78. data/HISTORY.md +0 -375
  79. data/KNOWN-ISSUES +0 -44
  80. data/lib/rack/backports/uri/common_18.rb +0 -56
  81. data/lib/rack/backports/uri/common_192.rb +0 -52
  82. data/lib/rack/backports/uri/common_193.rb +0 -29
  83. data/lib/rack/handler/evented_mongrel.rb +0 -8
  84. data/lib/rack/handler/mongrel.rb +0 -106
  85. data/lib/rack/handler/swiftiplied_mongrel.rb +0 -8
  86. data/lib/rack/showexceptions.rb +0 -387
  87. data/lib/rack/utils/okjson.rb +0 -600
  88. data/test/builder/anything.rb +0 -5
  89. data/test/builder/comment.ru +0 -4
  90. data/test/builder/end.ru +0 -5
  91. data/test/builder/line.ru +0 -1
  92. data/test/builder/options.ru +0 -2
  93. data/test/cgi/assets/folder/test.js +0 -1
  94. data/test/cgi/assets/fonts/font.eot +0 -1
  95. data/test/cgi/assets/images/image.png +0 -1
  96. data/test/cgi/assets/index.html +0 -1
  97. data/test/cgi/assets/javascripts/app.js +0 -1
  98. data/test/cgi/assets/stylesheets/app.css +0 -1
  99. data/test/cgi/lighttpd.conf +0 -26
  100. data/test/cgi/rackup_stub.rb +0 -6
  101. data/test/cgi/sample_rackup.ru +0 -5
  102. data/test/cgi/test +0 -9
  103. data/test/cgi/test+directory/test+file +0 -1
  104. data/test/cgi/test.fcgi +0 -8
  105. data/test/cgi/test.ru +0 -5
  106. data/test/gemloader.rb +0 -10
  107. data/test/multipart/bad_robots +0 -259
  108. data/test/multipart/binary +0 -0
  109. data/test/multipart/content_type_and_no_filename +0 -6
  110. data/test/multipart/empty +0 -10
  111. data/test/multipart/fail_16384_nofile +0 -814
  112. data/test/multipart/file1.txt +0 -1
  113. data/test/multipart/filename_and_modification_param +0 -7
  114. data/test/multipart/filename_and_no_name +0 -6
  115. data/test/multipart/filename_with_escaped_quotes +0 -6
  116. data/test/multipart/filename_with_escaped_quotes_and_modification_param +0 -7
  117. data/test/multipart/filename_with_null_byte +0 -7
  118. data/test/multipart/filename_with_percent_escaped_quotes +0 -6
  119. data/test/multipart/filename_with_unescaped_percentages +0 -6
  120. data/test/multipart/filename_with_unescaped_percentages2 +0 -6
  121. data/test/multipart/filename_with_unescaped_percentages3 +0 -6
  122. data/test/multipart/filename_with_unescaped_quotes +0 -6
  123. data/test/multipart/ie +0 -6
  124. data/test/multipart/invalid_character +0 -6
  125. data/test/multipart/mixed_files +0 -21
  126. data/test/multipart/nested +0 -10
  127. data/test/multipart/none +0 -9
  128. data/test/multipart/semicolon +0 -6
  129. data/test/multipart/text +0 -15
  130. data/test/multipart/three_files_three_fields +0 -31
  131. data/test/multipart/webkit +0 -32
  132. data/test/rackup/config.ru +0 -31
  133. data/test/registering_handler/rack/handler/registering_myself.rb +0 -8
  134. data/test/spec_auth_basic.rb +0 -81
  135. data/test/spec_auth_digest.rb +0 -259
  136. data/test/spec_body_proxy.rb +0 -85
  137. data/test/spec_builder.rb +0 -223
  138. data/test/spec_cascade.rb +0 -61
  139. data/test/spec_cgi.rb +0 -102
  140. data/test/spec_chunked.rb +0 -101
  141. data/test/spec_commonlogger.rb +0 -93
  142. data/test/spec_conditionalget.rb +0 -102
  143. data/test/spec_config.rb +0 -22
  144. data/test/spec_content_length.rb +0 -85
  145. data/test/spec_content_type.rb +0 -45
  146. data/test/spec_deflater.rb +0 -339
  147. data/test/spec_directory.rb +0 -88
  148. data/test/spec_etag.rb +0 -107
  149. data/test/spec_fastcgi.rb +0 -107
  150. data/test/spec_file.rb +0 -221
  151. data/test/spec_handler.rb +0 -72
  152. data/test/spec_head.rb +0 -45
  153. data/test/spec_lint.rb +0 -550
  154. data/test/spec_lobster.rb +0 -58
  155. data/test/spec_lock.rb +0 -164
  156. data/test/spec_logger.rb +0 -23
  157. data/test/spec_methodoverride.rb +0 -111
  158. data/test/spec_mime.rb +0 -51
  159. data/test/spec_mock.rb +0 -297
  160. data/test/spec_mongrel.rb +0 -182
  161. data/test/spec_multipart.rb +0 -600
  162. data/test/spec_nulllogger.rb +0 -20
  163. data/test/spec_recursive.rb +0 -72
  164. data/test/spec_request.rb +0 -1232
  165. data/test/spec_response.rb +0 -407
  166. data/test/spec_rewindable_input.rb +0 -118
  167. data/test/spec_runtime.rb +0 -49
  168. data/test/spec_sendfile.rb +0 -130
  169. data/test/spec_server.rb +0 -167
  170. data/test/spec_session_abstract_id.rb +0 -53
  171. data/test/spec_session_cookie.rb +0 -410
  172. data/test/spec_session_memcache.rb +0 -358
  173. data/test/spec_session_persisted_secure_secure_session_hash.rb +0 -73
  174. data/test/spec_session_pool.rb +0 -246
  175. data/test/spec_showexceptions.rb +0 -98
  176. data/test/spec_showstatus.rb +0 -103
  177. data/test/spec_static.rb +0 -145
  178. data/test/spec_tempfile_reaper.rb +0 -63
  179. data/test/spec_thin.rb +0 -91
  180. data/test/spec_urlmap.rb +0 -236
  181. data/test/spec_utils.rb +0 -647
  182. data/test/spec_version.rb +0 -17
  183. data/test/spec_webrick.rb +0 -184
  184. data/test/static/another/index.html +0 -1
  185. data/test/static/index.html +0 -1
  186. data/test/testrequest.rb +0 -78
  187. data/test/unregistered_handler/rack/handler/unregistered.rb +0 -7
  188. 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
- begin
8
- require 'securerandom'
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
- def self.find(env)
55
- env[ENV_SESSION_KEY]
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(env, session)
59
- env[ENV_SESSION_KEY] = session
68
+ def self.set(req, session)
69
+ req.set_header RACK_SESSION, session
60
70
  end
61
71
 
62
- def self.set_options(env, options)
63
- env[ENV_SESSION_OPTIONS_KEY] = options.dup
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, env)
76
+ def initialize(store, req)
67
77
  @store = store
68
- @env = env
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, @env)
84
+ @id = @store.send(:extract_session_id, @req)
75
85
  end
76
86
 
77
87
  def options
78
- @env[ENV_SESSION_OPTIONS_KEY]
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
- alias :fetch :[]
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(:destroy_session, @env, id, options)
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?, @env)
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, @env)
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
- hash = {}
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 #get_session and #set_session are
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#add_cookie
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']. Additionally the id of the session can be
213
- # found within the options hash at the key :id. It is highly not
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
- :key => 'rack.session',
224
- :path => '/',
225
- :domain => nil,
226
- :expire_after => nil,
227
- :secure => false,
228
- :httponly => true,
229
- :defer => false,
230
- :renew => false,
231
- :sidbits => 128,
232
- :cookie_only => true,
233
- :secure_random => (::SecureRandom rescue false)
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=@app)
251
- prepare_session(env)
252
- status, headers, body = app.call(env)
253
- commit_session(env, status, headers, body)
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(env)
282
- session_was = env[ENV_SESSION_KEY]
283
- env[ENV_SESSION_KEY] = session_class.new(self, env)
284
- env[ENV_SESSION_OPTIONS_KEY] = @default_options.dup
285
- env[ENV_SESSION_KEY].merge! session_was if session_was
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 #get_session.
313
+ # environment to #find_session.
290
314
 
291
- def load_session(env)
292
- sid = current_session_id(env)
293
- sid, session = get_session(env, sid)
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(env)
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(env)
309
- env[ENV_SESSION_KEY].id
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?(env)
315
- value = current_session_id(env)
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?(env, session, options)
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?(env, options)
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?(env, options)
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 #set_session. If successful
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(env, status, headers, body)
355
- session = env[ENV_SESSION_KEY]
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 = destroy_session(env, session.id || generate_sid, options)
360
- return [status, headers, body] unless session_id
381
+ session_id = delete_session(req, session.id || generate_sid, options)
382
+ return unless session_id
361
383
  end
362
384
 
363
- return [status, headers, body] unless commit_session?(env, session, options)
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 = set_session(env, session_id, session_data, options)
370
- env["rack.errors"].puts("Warning! #{self.class.name} failed to save session. Content dropped.")
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
- env["rack.errors"].puts("Deferring cookie for #{session_id.public_id}") if $VERBOSE
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(env, headers, cookie.merge!(options))
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(env, headers, 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
- Utils.set_cookie_header!(headers, @key, cookie)
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 get_session(env, sid)
410
- raise '#get_session not implemented.'
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 set_session(env, sid, session, options)
418
- raise '#set_session not implemented.'
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 destroy_session(env, sid, options)
425
- raise '#destroy_session not implemented'
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
@@ -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
- [str].pack('m')
55
+ ::Base64.strict_encode64(str)
52
56
  end
53
57
 
54
58
  def decode(str)
55
- str.unpack('m').first
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(::Rack::Utils::OkJson.encode(obj))
78
+ super(::JSON.dump(obj))
75
79
  end
76
80
 
77
81
  def decode(str)
78
82
  return unless str
79
- ::Rack::Utils::OkJson.decode(super(str)) rescue nil
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(::Rack::Utils::OkJson.encode(obj)))
89
+ super(Zlib::Deflate.deflate(::JSON.dump(obj)))
86
90
  end
87
91
 
88
92
  def decode(str)
89
93
  return unless str
90
- ::Rack::Utils::OkJson.decode(Zlib::Inflate.inflate(super(str)))
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
- warn <<-MSG unless @secrets.size >= 1
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 = options[:coder] ||= Base64::Marshal.new
117
- super(app, options.merge!(:cookie_only => true))
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 get_session(env, sid)
123
- data = unpacked_cookie_data(env)
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(env)
129
- unpacked_cookie_data(env)["session_id"]
134
+ def extract_session_id(request)
135
+ unpacked_cookie_data(request)["session_id"]
130
136
  end
131
137
 
132
- def unpacked_cookie_data(env)
133
- env["rack.session.unpacked_cookie_data"] ||= begin
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
- digest, session_data = session_data.reverse.split("--", 2)
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 set_session(env, session_id, session, options)
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
- env["rack.errors"].puts("Warning! Rack::Session::Cookie data size exceeds 4K.")
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 destroy_session(env, session_id, options)
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(OpenSSL::Digest::SHA1.new, secret, data)
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