rack 2.0.6 → 2.1.2

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 +69 -0
  3. data/{COPYING → MIT-LICENSE} +4 -2
  4. data/README.rdoc +77 -117
  5. data/Rakefile +25 -18
  6. data/SPEC +3 -4
  7. data/bin/rackup +1 -0
  8. data/example/lobster.ru +2 -0
  9. data/example/protectedlobster.rb +3 -1
  10. data/example/protectedlobster.ru +2 -0
  11. data/lib/rack.rb +63 -60
  12. data/lib/rack/auth/abstract/handler.rb +3 -1
  13. data/lib/rack/auth/abstract/request.rb +2 -0
  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 +4 -2
  18. data/lib/rack/auth/digest/request.rb +2 -0
  19. data/lib/rack/body_proxy.rb +3 -6
  20. data/lib/rack/builder.rb +39 -15
  21. data/lib/rack/cascade.rb +6 -5
  22. data/lib/rack/chunked.rb +29 -6
  23. data/lib/rack/common_logger.rb +9 -8
  24. data/lib/rack/conditional_get.rb +3 -1
  25. data/lib/rack/config.rb +2 -0
  26. data/lib/rack/content_length.rb +3 -1
  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 +32 -17
  30. data/lib/rack/directory.rb +17 -14
  31. data/lib/rack/etag.rb +3 -1
  32. data/lib/rack/events.rb +5 -3
  33. data/lib/rack/file.rb +4 -173
  34. data/lib/rack/files.rb +178 -0
  35. data/lib/rack/handler.rb +7 -2
  36. data/lib/rack/handler/cgi.rb +3 -1
  37. data/lib/rack/handler/fastcgi.rb +4 -2
  38. data/lib/rack/handler/lsws.rb +3 -1
  39. data/lib/rack/handler/scgi.rb +9 -6
  40. data/lib/rack/handler/thin.rb +3 -1
  41. data/lib/rack/handler/webrick.rb +4 -2
  42. data/lib/rack/head.rb +2 -0
  43. data/lib/rack/lint.rb +14 -11
  44. data/lib/rack/lobster.rb +7 -5
  45. data/lib/rack/lock.rb +2 -0
  46. data/lib/rack/logger.rb +2 -0
  47. data/lib/rack/media_type.rb +10 -5
  48. data/lib/rack/method_override.rb +4 -2
  49. data/lib/rack/mime.rb +9 -1
  50. data/lib/rack/mock.rb +74 -15
  51. data/lib/rack/multipart.rb +5 -3
  52. data/lib/rack/multipart/generator.rb +6 -7
  53. data/lib/rack/multipart/parser.rb +55 -52
  54. data/lib/rack/multipart/uploaded_file.rb +2 -0
  55. data/lib/rack/null_logger.rb +2 -0
  56. data/lib/rack/query_parser.rb +51 -25
  57. data/lib/rack/recursive.rb +7 -5
  58. data/lib/rack/reloader.rb +10 -4
  59. data/lib/rack/request.rb +80 -27
  60. data/lib/rack/response.rb +71 -31
  61. data/lib/rack/rewindable_input.rb +4 -2
  62. data/lib/rack/runtime.rb +4 -2
  63. data/lib/rack/sendfile.rb +15 -8
  64. data/lib/rack/server.rb +88 -18
  65. data/lib/rack/session/abstract/id.rb +96 -21
  66. data/lib/rack/session/cookie.rb +21 -11
  67. data/lib/rack/session/memcache.rb +4 -87
  68. data/lib/rack/session/pool.rb +17 -8
  69. data/lib/rack/show_exceptions.rb +15 -9
  70. data/lib/rack/show_status.rb +4 -2
  71. data/lib/rack/static.rb +15 -10
  72. data/lib/rack/tempfile_reaper.rb +2 -0
  73. data/lib/rack/urlmap.rb +11 -2
  74. data/lib/rack/utils.rb +55 -70
  75. data/rack.gemspec +17 -7
  76. metadata +30 -171
  77. data/HISTORY.md +0 -505
  78. data/test/builder/an_underscore_app.rb +0 -5
  79. data/test/builder/anything.rb +0 -5
  80. data/test/builder/comment.ru +0 -4
  81. data/test/builder/end.ru +0 -5
  82. data/test/builder/line.ru +0 -1
  83. data/test/builder/options.ru +0 -2
  84. data/test/cgi/assets/folder/test.js +0 -1
  85. data/test/cgi/assets/fonts/font.eot +0 -1
  86. data/test/cgi/assets/images/image.png +0 -1
  87. data/test/cgi/assets/index.html +0 -1
  88. data/test/cgi/assets/javascripts/app.js +0 -1
  89. data/test/cgi/assets/stylesheets/app.css +0 -1
  90. data/test/cgi/lighttpd.conf +0 -26
  91. data/test/cgi/rackup_stub.rb +0 -6
  92. data/test/cgi/sample_rackup.ru +0 -5
  93. data/test/cgi/test +0 -9
  94. data/test/cgi/test+directory/test+file +0 -1
  95. data/test/cgi/test.fcgi +0 -9
  96. data/test/cgi/test.gz +0 -0
  97. data/test/cgi/test.ru +0 -5
  98. data/test/gemloader.rb +0 -10
  99. data/test/helper.rb +0 -34
  100. data/test/multipart/bad_robots +0 -259
  101. data/test/multipart/binary +0 -0
  102. data/test/multipart/content_type_and_no_filename +0 -6
  103. data/test/multipart/empty +0 -10
  104. data/test/multipart/fail_16384_nofile +0 -814
  105. data/test/multipart/file1.txt +0 -1
  106. data/test/multipart/filename_and_modification_param +0 -7
  107. data/test/multipart/filename_and_no_name +0 -6
  108. data/test/multipart/filename_with_encoded_words +0 -7
  109. data/test/multipart/filename_with_escaped_quotes +0 -6
  110. data/test/multipart/filename_with_escaped_quotes_and_modification_param +0 -7
  111. data/test/multipart/filename_with_null_byte +0 -7
  112. data/test/multipart/filename_with_percent_escaped_quotes +0 -6
  113. data/test/multipart/filename_with_single_quote +0 -7
  114. data/test/multipart/filename_with_unescaped_percentages +0 -6
  115. data/test/multipart/filename_with_unescaped_percentages2 +0 -6
  116. data/test/multipart/filename_with_unescaped_percentages3 +0 -6
  117. data/test/multipart/filename_with_unescaped_quotes +0 -6
  118. data/test/multipart/ie +0 -6
  119. data/test/multipart/invalid_character +0 -6
  120. data/test/multipart/mixed_files +0 -21
  121. data/test/multipart/nested +0 -10
  122. data/test/multipart/none +0 -9
  123. data/test/multipart/quoted +0 -15
  124. data/test/multipart/rack-logo.png +0 -0
  125. data/test/multipart/semicolon +0 -6
  126. data/test/multipart/text +0 -15
  127. data/test/multipart/three_files_three_fields +0 -31
  128. data/test/multipart/unity3d_wwwform +0 -11
  129. data/test/multipart/webkit +0 -32
  130. data/test/rackup/config.ru +0 -31
  131. data/test/registering_handler/rack/handler/registering_myself.rb +0 -8
  132. data/test/spec_auth_basic.rb +0 -89
  133. data/test/spec_auth_digest.rb +0 -260
  134. data/test/spec_body_proxy.rb +0 -85
  135. data/test/spec_builder.rb +0 -233
  136. data/test/spec_cascade.rb +0 -63
  137. data/test/spec_cgi.rb +0 -84
  138. data/test/spec_chunked.rb +0 -103
  139. data/test/spec_common_logger.rb +0 -95
  140. data/test/spec_conditional_get.rb +0 -103
  141. data/test/spec_config.rb +0 -23
  142. data/test/spec_content_length.rb +0 -86
  143. data/test/spec_content_type.rb +0 -46
  144. data/test/spec_deflater.rb +0 -375
  145. data/test/spec_directory.rb +0 -148
  146. data/test/spec_etag.rb +0 -108
  147. data/test/spec_events.rb +0 -133
  148. data/test/spec_fastcgi.rb +0 -85
  149. data/test/spec_file.rb +0 -264
  150. data/test/spec_handler.rb +0 -57
  151. data/test/spec_head.rb +0 -46
  152. data/test/spec_lint.rb +0 -515
  153. data/test/spec_lobster.rb +0 -59
  154. data/test/spec_lock.rb +0 -204
  155. data/test/spec_logger.rb +0 -24
  156. data/test/spec_media_type.rb +0 -42
  157. data/test/spec_method_override.rb +0 -110
  158. data/test/spec_mime.rb +0 -51
  159. data/test/spec_mock.rb +0 -359
  160. data/test/spec_multipart.rb +0 -722
  161. data/test/spec_null_logger.rb +0 -21
  162. data/test/spec_recursive.rb +0 -75
  163. data/test/spec_request.rb +0 -1398
  164. data/test/spec_response.rb +0 -510
  165. data/test/spec_rewindable_input.rb +0 -128
  166. data/test/spec_runtime.rb +0 -50
  167. data/test/spec_sendfile.rb +0 -125
  168. data/test/spec_server.rb +0 -193
  169. data/test/spec_session_abstract_id.rb +0 -31
  170. data/test/spec_session_abstract_session_hash.rb +0 -45
  171. data/test/spec_session_cookie.rb +0 -442
  172. data/test/spec_session_memcache.rb +0 -320
  173. data/test/spec_session_pool.rb +0 -210
  174. data/test/spec_show_exceptions.rb +0 -93
  175. data/test/spec_show_status.rb +0 -104
  176. data/test/spec_static.rb +0 -184
  177. data/test/spec_tempfile_reaper.rb +0 -64
  178. data/test/spec_thin.rb +0 -96
  179. data/test/spec_urlmap.rb +0 -237
  180. data/test/spec_utils.rb +0 -742
  181. data/test/spec_version.rb +0 -11
  182. data/test/spec_webrick.rb +0 -206
  183. data/test/static/another/index.html +0 -1
  184. data/test/static/foo.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,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rack/utils'
2
4
 
3
5
  module Rack
@@ -8,8 +10,8 @@ module Rack
8
10
  # time, or before all the other middlewares to include time for them,
9
11
  # too.
10
12
  class Runtime
11
- FORMAT_STRING = "%0.6f".freeze # :nodoc:
12
- HEADER_NAME = "X-Runtime".freeze # :nodoc:
13
+ FORMAT_STRING = "%0.6f" # :nodoc:
14
+ HEADER_NAME = "X-Runtime" # :nodoc:
13
15
 
14
16
  def initialize(app, name = nil)
15
17
  @app = app
@@ -1,4 +1,6 @@
1
- require 'rack/file'
1
+ # frozen_string_literal: true
2
+
3
+ require 'rack/files'
2
4
  require 'rack/body_proxy'
3
5
 
4
6
  module Rack
@@ -14,7 +16,7 @@ module Rack
14
16
  #
15
17
  # In order to take advantage of this middleware, the response body must
16
18
  # respond to +to_path+ and the request must include an X-Sendfile-Type
17
- # header. Rack::File and other components implement +to_path+ so there's
19
+ # header. Rack::Files and other components implement +to_path+ so there's
18
20
  # rarely anything you need to do in your application. The X-Sendfile-Type
19
21
  # header is typically set in your web servers configuration. The following
20
22
  # sections attempt to document
@@ -53,7 +55,7 @@ module Rack
53
55
  # that it maps to. The middleware performs a simple substitution on the
54
56
  # resulting path.
55
57
  #
56
- # See Also: http://wiki.codemongers.com/NginxXSendfile
58
+ # See Also: https://www.nginx.com/resources/wiki/start/topics/examples/xsendfile
57
59
  #
58
60
  # === lighttpd
59
61
  #
@@ -99,7 +101,7 @@ module Rack
99
101
  # will be matched with case indifference.
100
102
 
101
103
  class Sendfile
102
- def initialize(app, variation=nil, mappings=[])
104
+ def initialize(app, variation = nil, mappings = [])
103
105
  @app = app
104
106
  @variation = variation
105
107
  @mappings = mappings.map do |internal, external|
@@ -115,7 +117,8 @@ module Rack
115
117
  path = ::File.expand_path(body.to_path)
116
118
  if url = map_accel_path(env, path)
117
119
  headers[CONTENT_LENGTH] = '0'
118
- headers[type] = url
120
+ # '?' must be percent-encoded because it is not query string but a part of path
121
+ headers[type] = ::Rack::Utils.escape_path(url).gsub('?', '%3F')
119
122
  obody = body
120
123
  body = Rack::BodyProxy.new([]) do
121
124
  obody.close if obody.respond_to?(:close)
@@ -147,11 +150,15 @@ module Rack
147
150
  end
148
151
 
149
152
  def map_accel_path(env, path)
150
- if mapping = @mappings.find { |internal,_| internal =~ path }
153
+ if mapping = @mappings.find { |internal, _| internal =~ path }
151
154
  path.sub(*mapping)
152
155
  elsif mapping = env['HTTP_X_ACCEL_MAPPING']
153
- internal, external = mapping.split('=', 2).map(&:strip)
154
- path.sub(/^#{internal}/i, external)
156
+ mapping.split(',').map(&:strip).each do |m|
157
+ internal, external = m.split('=', 2).map(&:strip)
158
+ new_path = path.sub(/^#{internal}/i, external)
159
+ return new_path unless path == new_path
160
+ end
161
+ path
155
162
  end
156
163
  end
157
164
  end
@@ -1,10 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'optparse'
2
4
  require 'fileutils'
3
5
 
6
+ require_relative 'core_ext/regexp'
4
7
 
5
8
  module Rack
6
9
 
7
10
  class Server
11
+ using ::Rack::RegexpExtensions
8
12
 
9
13
  class Options
10
14
  def parse!(args)
@@ -21,10 +25,6 @@ module Rack
21
25
  lineno += 1
22
26
  }
23
27
 
24
- opts.on("-b", "--builder BUILDER_LINE", "evaluate a BUILDER_LINE of code as a builder script") { |line|
25
- options[:builder] = line
26
- }
27
-
28
28
  opts.on("-d", "--debug", "set debugging flags (set $DEBUG to true)") {
29
29
  options[:debug] = true
30
30
  }
@@ -47,7 +47,11 @@ module Rack
47
47
 
48
48
  opts.separator ""
49
49
  opts.separator "Rack options:"
50
- opts.on("-s", "--server SERVER", "serve using SERVER (thin/puma/webrick/mongrel)") { |s|
50
+ opts.on("-b", "--builder BUILDER_LINE", "evaluate a BUILDER_LINE of code as a builder script") { |line|
51
+ options[:builder] = line
52
+ }
53
+
54
+ opts.on("-s", "--server SERVER", "serve using SERVER (thin/puma/webrick)") { |s|
51
55
  options[:server] = s
52
56
  }
53
57
 
@@ -77,6 +81,24 @@ module Rack
77
81
  options[:pid] = ::File.expand_path(f)
78
82
  }
79
83
 
84
+ opts.separator ""
85
+ opts.separator "Profiling options:"
86
+
87
+ opts.on("--heap HEAPFILE", "Build the application, then dump the heap to HEAPFILE") do |e|
88
+ options[:heapfile] = e
89
+ end
90
+
91
+ opts.on("--profile PROFILE", "Dump CPU or Memory profile to PROFILE (defaults to a tempfile)") do |e|
92
+ options[:profile_file] = e
93
+ end
94
+
95
+ opts.on("--profile-mode MODE", "Profile mode (cpu|wall|object)") do |e|
96
+ { cpu: true, wall: true, object: true }.fetch(e.to_sym) do
97
+ raise OptionParser::InvalidOption, "unknown profile mode: #{e}"
98
+ end
99
+ options[:profile_mode] = e.to_sym
100
+ end
101
+
80
102
  opts.separator ""
81
103
  opts.separator "Common options:"
82
104
 
@@ -114,7 +136,7 @@ module Rack
114
136
 
115
137
  has_options = false
116
138
  server.valid_options.each do |name, description|
117
- next if name.to_s.match(/^(Host|Port)[^a-zA-Z]/) # ignore handler's host and port options, we do our own.
139
+ next if /^(Host|Port)[^a-zA-Z]/.match?(name.to_s) # ignore handler's host and port options, we do our own.
118
140
  info << " -O %-21s %s" % [name, description]
119
141
  has_options = true
120
142
  end
@@ -152,7 +174,9 @@ module Rack
152
174
 
153
175
  # Options may include:
154
176
  # * :app
155
- # a rack application to run (overrides :config)
177
+ # a rack application to run (overrides :config and :builder)
178
+ # * :builder
179
+ # a string to evaluate a Rack::Builder from
156
180
  # * :config
157
181
  # a rackup configuration file path to load (.ru)
158
182
  # * :environment
@@ -182,6 +206,14 @@ module Rack
182
206
  # add given paths to $LOAD_PATH
183
207
  # * :require
184
208
  # require the given libraries
209
+ #
210
+ # Additional options for profiling app initialization include:
211
+ # * :heapfile
212
+ # location for ObjectSpace.dump_all to write the output to
213
+ # * :profile_file
214
+ # location for CPU/Memory (StackProf) profile output (defaults to a tempfile)
215
+ # * :profile_mode
216
+ # StackProf profile mode (cpu|wall|object)
185
217
  def initialize(options = nil)
186
218
  @ignore_options = []
187
219
 
@@ -206,12 +238,12 @@ module Rack
206
238
  default_host = environment == 'development' ? 'localhost' : '0.0.0.0'
207
239
 
208
240
  {
209
- :environment => environment,
210
- :pid => nil,
211
- :Port => 9292,
212
- :Host => default_host,
213
- :AccessLog => [],
214
- :config => "config.ru"
241
+ environment: environment,
242
+ pid: nil,
243
+ Port: 9292,
244
+ Host: default_host,
245
+ AccessLog: [],
246
+ config: "config.ru"
215
247
  }
216
248
  end
217
249
 
@@ -222,21 +254,19 @@ module Rack
222
254
  class << self
223
255
  def logging_middleware
224
256
  lambda { |server|
225
- server.server.name =~ /CGI/ || server.options[:quiet] ? nil : [Rack::CommonLogger, $stderr]
257
+ /CGI/.match?(server.server.name) || server.options[:quiet] ? nil : [Rack::CommonLogger, $stderr]
226
258
  }
227
259
  end
228
260
 
229
261
  def default_middleware_by_environment
230
- m = Hash.new {|h,k| h[k] = []}
262
+ m = Hash.new {|h, k| h[k] = []}
231
263
  m["deployment"] = [
232
264
  [Rack::ContentLength],
233
- [Rack::Chunked],
234
265
  logging_middleware,
235
266
  [Rack::TempfileReaper]
236
267
  ]
237
268
  m["development"] = [
238
269
  [Rack::ContentLength],
239
- [Rack::Chunked],
240
270
  logging_middleware,
241
271
  [Rack::ShowExceptions],
242
272
  [Rack::Lint],
@@ -280,7 +310,9 @@ module Rack
280
310
 
281
311
  # Touch the wrapped app, so that the config.ru is loaded before
282
312
  # daemonization (i.e. before chdir, etc).
283
- wrapped_app
313
+ handle_profiling(options[:heapfile], options[:profile_mode], options[:profile_file]) do
314
+ wrapped_app
315
+ end
284
316
 
285
317
  daemonize_app if options[:daemonize]
286
318
 
@@ -321,6 +353,44 @@ module Rack
321
353
  app
322
354
  end
323
355
 
356
+ def handle_profiling(heapfile, profile_mode, filename)
357
+ if heapfile
358
+ require "objspace"
359
+ ObjectSpace.trace_object_allocations_start
360
+ yield
361
+ GC.start
362
+ ::File.open(heapfile, "w") { |f| ObjectSpace.dump_all(output: f) }
363
+ exit
364
+ end
365
+
366
+ if profile_mode
367
+ require "stackprof"
368
+ require "tempfile"
369
+
370
+ make_profile_name(filename) do |filename|
371
+ ::File.open(filename, "w") do |f|
372
+ StackProf.run(mode: profile_mode, out: f) do
373
+ yield
374
+ end
375
+ puts "Profile written to: #{filename}"
376
+ end
377
+ end
378
+ exit
379
+ end
380
+
381
+ yield
382
+ end
383
+
384
+ def make_profile_name(filename)
385
+ if filename
386
+ yield filename
387
+ else
388
+ ::Dir::Tmpname.create("profile.dump") do |tmpname, _, _|
389
+ yield tmpname
390
+ end
391
+ end
392
+ end
393
+
324
394
  def build_app_from_string
325
395
  Rack::Builder.new_from_string(self.options[:builder])
326
396
  end
@@ -1,3 +1,5 @@
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,15 +8,54 @@ require 'time'
6
8
  require 'rack/request'
7
9
  require 'rack/response'
8
10
  require 'securerandom'
11
+ require 'digest/sha2'
9
12
 
10
13
  module Rack
11
14
 
12
15
  module Session
13
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
+
14
43
  module Abstract
15
44
  # SessionHash is responsible to lazily load the session from store.
16
45
 
17
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
+
18
59
  include Enumerable
19
60
  attr_writer :id
20
61
 
@@ -57,7 +98,7 @@ module Rack
57
98
  @data[key.to_s]
58
99
  end
59
100
 
60
- def fetch(key, default=Unspecified, &block)
101
+ def fetch(key, default = Unspecified, &block)
61
102
  load_for_read!
62
103
  if default == Unspecified
63
104
  @data.fetch(key.to_s, &block)
@@ -160,11 +201,7 @@ module Rack
160
201
  end
161
202
 
162
203
  def stringify_keys(other)
163
- hash = {}
164
- other.each do |key, value|
165
- hash[key.to_s] = value
166
- end
167
- hash
204
+ other.to_hash.transform_keys(&:to_s)
168
205
  end
169
206
  end
170
207
 
@@ -199,22 +236,22 @@ module Rack
199
236
 
200
237
  class Persisted
201
238
  DEFAULT_OPTIONS = {
202
- :key => RACK_SESSION,
203
- :path => '/',
204
- :domain => nil,
205
- :expire_after => nil,
206
- :secure => false,
207
- :httponly => true,
208
- :defer => false,
209
- :renew => false,
210
- :sidbits => 128,
211
- :cookie_only => true,
212
- :secure_random => ::SecureRandom
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
213
250
  }.freeze
214
251
 
215
252
  attr_reader :key, :default_options, :sid_secure
216
253
 
217
- def initialize(app, options={})
254
+ def initialize(app, options = {})
218
255
  @app = app
219
256
  @default_options = self.class::DEFAULT_OPTIONS.merge(options)
220
257
  @key = @default_options.delete(:key)
@@ -226,7 +263,7 @@ module Rack
226
263
  context(env)
227
264
  end
228
265
 
229
- def context(env, app=@app)
266
+ def context(env, app = @app)
230
267
  req = make_request env
231
268
  prepare_session(req)
232
269
  status, headers, body = app.call(req.env)
@@ -349,7 +386,7 @@ module Rack
349
386
 
350
387
  session.send(:load!) unless loaded_session?(session)
351
388
  session_id ||= session.id
352
- session_data = session.to_hash.delete_if { |k,v| v.nil? }
389
+ session_data = session.to_hash.delete_if { |k, v| v.nil? }
353
390
 
354
391
  if not data = write_session(req, session_id, session_data, options)
355
392
  req.get_header(RACK_ERRORS).puts("Warning! #{self.class.name} failed to save session. Content dropped.")
@@ -357,7 +394,7 @@ module Rack
357
394
  req.get_header(RACK_ERRORS).puts("Deferring cookie for #{session_id}") if $VERBOSE
358
395
  else
359
396
  cookie = Hash.new
360
- cookie[:value] = data
397
+ cookie[:value] = cookie_value(data)
361
398
  cookie[:expires] = Time.now + options[:expire_after] if options[:expire_after]
362
399
  cookie[:expires] = Time.now + options[:max_age] if options[:max_age]
363
400
  set_cookie(req, res, cookie.merge!(options))
@@ -365,6 +402,10 @@ module Rack
365
402
  end
366
403
  public :commit_session
367
404
 
405
+ def cookie_value(data)
406
+ data
407
+ end
408
+
368
409
  # Sets the cookie back to the client with session id. We skip the cookie
369
410
  # setting if the value didn't change (sid is the same) or expires was given.
370
411
 
@@ -406,6 +447,40 @@ module Rack
406
447
  end
407
448
  end
408
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
+
409
484
  class ID < Persisted
410
485
  def self.inherited(klass)
411
486
  k = klass.ancestors.find { |kl| kl.respond_to?(:superclass) && kl.superclass == ID }