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