rack 2.0.9.3 → 2.2.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 (191) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +675 -0
  3. data/CONTRIBUTING.md +136 -0
  4. data/{COPYING → MIT-LICENSE} +4 -2
  5. data/README.rdoc +152 -162
  6. data/Rakefile +37 -23
  7. data/{SPEC → SPEC.rdoc} +44 -15
  8. data/bin/rackup +1 -0
  9. data/example/lobster.ru +2 -0
  10. data/example/protectedlobster.rb +3 -1
  11. data/example/protectedlobster.ru +2 -0
  12. data/lib/rack/auth/abstract/handler.rb +3 -1
  13. data/lib/rack/auth/abstract/request.rb +1 -1
  14. data/lib/rack/auth/basic.rb +7 -4
  15. data/lib/rack/auth/digest/md5.rb +13 -11
  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 +5 -3
  19. data/lib/rack/body_proxy.rb +15 -14
  20. data/lib/rack/builder.rb +116 -23
  21. data/lib/rack/cascade.rb +28 -12
  22. data/lib/rack/chunked.rb +68 -20
  23. data/lib/rack/common_logger.rb +33 -28
  24. data/lib/rack/conditional_get.rb +20 -16
  25. data/lib/rack/config.rb +2 -0
  26. data/lib/rack/content_length.rb +8 -7
  27. data/lib/rack/content_type.rb +5 -4
  28. data/lib/rack/core_ext/regexp.rb +14 -0
  29. data/lib/rack/deflater.rb +59 -34
  30. data/lib/rack/directory.rb +84 -64
  31. data/lib/rack/etag.rb +5 -4
  32. data/lib/rack/events.rb +19 -20
  33. data/lib/rack/file.rb +4 -173
  34. data/lib/rack/files.rb +218 -0
  35. data/lib/rack/handler/cgi.rb +2 -3
  36. data/lib/rack/handler/fastcgi.rb +4 -4
  37. data/lib/rack/handler/lsws.rb +3 -3
  38. data/lib/rack/handler/scgi.rb +9 -8
  39. data/lib/rack/handler/thin.rb +17 -11
  40. data/lib/rack/handler/webrick.rb +15 -6
  41. data/lib/rack/handler.rb +7 -2
  42. data/lib/rack/head.rb +1 -1
  43. data/lib/rack/lint.rb +72 -26
  44. data/lib/rack/lobster.rb +10 -10
  45. data/lib/rack/lock.rb +2 -1
  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 +97 -20
  51. data/lib/rack/multipart/generator.rb +17 -13
  52. data/lib/rack/multipart/parser.rb +58 -73
  53. data/lib/rack/multipart/uploaded_file.rb +15 -7
  54. data/lib/rack/multipart.rb +7 -4
  55. data/lib/rack/null_logger.rb +2 -0
  56. data/lib/rack/query_parser.rb +53 -28
  57. data/lib/rack/recursive.rb +7 -5
  58. data/lib/rack/reloader.rb +8 -4
  59. data/lib/rack/request.rb +210 -61
  60. data/lib/rack/response.rb +127 -44
  61. data/lib/rack/rewindable_input.rb +4 -3
  62. data/lib/rack/runtime.rb +6 -4
  63. data/lib/rack/sendfile.rb +13 -9
  64. data/lib/rack/server.rb +95 -24
  65. data/lib/rack/session/abstract/id.rb +33 -21
  66. data/lib/rack/session/cookie.rb +12 -12
  67. data/lib/rack/session/memcache.rb +4 -93
  68. data/lib/rack/session/pool.rb +5 -3
  69. data/lib/rack/show_exceptions.rb +17 -13
  70. data/lib/rack/show_status.rb +5 -5
  71. data/lib/rack/static.rb +23 -11
  72. data/lib/rack/tempfile_reaper.rb +1 -1
  73. data/lib/rack/urlmap.rb +12 -6
  74. data/lib/rack/utils.rb +105 -130
  75. data/lib/rack/version.rb +29 -0
  76. data/lib/rack.rb +67 -73
  77. data/rack.gemspec +40 -28
  78. metadata +39 -182
  79. data/HISTORY.md +0 -520
  80. data/test/builder/an_underscore_app.rb +0 -5
  81. data/test/builder/anything.rb +0 -5
  82. data/test/builder/comment.ru +0 -4
  83. data/test/builder/end.ru +0 -5
  84. data/test/builder/line.ru +0 -1
  85. data/test/builder/options.ru +0 -2
  86. data/test/cgi/assets/folder/test.js +0 -1
  87. data/test/cgi/assets/fonts/font.eot +0 -1
  88. data/test/cgi/assets/images/image.png +0 -1
  89. data/test/cgi/assets/index.html +0 -1
  90. data/test/cgi/assets/javascripts/app.js +0 -1
  91. data/test/cgi/assets/stylesheets/app.css +0 -1
  92. data/test/cgi/lighttpd.conf +0 -26
  93. data/test/cgi/rackup_stub.rb +0 -6
  94. data/test/cgi/sample_rackup.ru +0 -5
  95. data/test/cgi/test +0 -9
  96. data/test/cgi/test+directory/test+file +0 -1
  97. data/test/cgi/test.fcgi +0 -9
  98. data/test/cgi/test.gz +0 -0
  99. data/test/cgi/test.ru +0 -5
  100. data/test/gemloader.rb +0 -10
  101. data/test/helper.rb +0 -34
  102. data/test/multipart/bad_robots +0 -259
  103. data/test/multipart/binary +0 -0
  104. data/test/multipart/content_type_and_no_filename +0 -6
  105. data/test/multipart/empty +0 -10
  106. data/test/multipart/fail_16384_nofile +0 -814
  107. data/test/multipart/file1.txt +0 -1
  108. data/test/multipart/filename_and_modification_param +0 -7
  109. data/test/multipart/filename_and_no_name +0 -6
  110. data/test/multipart/filename_with_encoded_words +0 -7
  111. data/test/multipart/filename_with_escaped_quotes +0 -6
  112. data/test/multipart/filename_with_escaped_quotes_and_modification_param +0 -7
  113. data/test/multipart/filename_with_null_byte +0 -7
  114. data/test/multipart/filename_with_percent_escaped_quotes +0 -6
  115. data/test/multipart/filename_with_single_quote +0 -7
  116. data/test/multipart/filename_with_unescaped_percentages +0 -6
  117. data/test/multipart/filename_with_unescaped_percentages2 +0 -6
  118. data/test/multipart/filename_with_unescaped_percentages3 +0 -6
  119. data/test/multipart/filename_with_unescaped_quotes +0 -6
  120. data/test/multipart/ie +0 -6
  121. data/test/multipart/invalid_character +0 -6
  122. data/test/multipart/mixed_files +0 -21
  123. data/test/multipart/nested +0 -10
  124. data/test/multipart/none +0 -9
  125. data/test/multipart/quoted +0 -15
  126. data/test/multipart/rack-logo.png +0 -0
  127. data/test/multipart/semicolon +0 -6
  128. data/test/multipart/text +0 -15
  129. data/test/multipart/three_files_three_fields +0 -31
  130. data/test/multipart/unity3d_wwwform +0 -11
  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 -89
  135. data/test/spec_auth_digest.rb +0 -260
  136. data/test/spec_body_proxy.rb +0 -85
  137. data/test/spec_builder.rb +0 -233
  138. data/test/spec_cascade.rb +0 -63
  139. data/test/spec_cgi.rb +0 -84
  140. data/test/spec_chunked.rb +0 -103
  141. data/test/spec_common_logger.rb +0 -107
  142. data/test/spec_conditional_get.rb +0 -103
  143. data/test/spec_config.rb +0 -23
  144. data/test/spec_content_length.rb +0 -86
  145. data/test/spec_content_type.rb +0 -46
  146. data/test/spec_deflater.rb +0 -375
  147. data/test/spec_directory.rb +0 -148
  148. data/test/spec_etag.rb +0 -108
  149. data/test/spec_events.rb +0 -133
  150. data/test/spec_fastcgi.rb +0 -85
  151. data/test/spec_file.rb +0 -264
  152. data/test/spec_handler.rb +0 -57
  153. data/test/spec_head.rb +0 -46
  154. data/test/spec_lint.rb +0 -520
  155. data/test/spec_lobster.rb +0 -59
  156. data/test/spec_lock.rb +0 -204
  157. data/test/spec_logger.rb +0 -24
  158. data/test/spec_media_type.rb +0 -42
  159. data/test/spec_method_override.rb +0 -110
  160. data/test/spec_mime.rb +0 -51
  161. data/test/spec_mock.rb +0 -359
  162. data/test/spec_multipart.rb +0 -721
  163. data/test/spec_null_logger.rb +0 -21
  164. data/test/spec_recursive.rb +0 -75
  165. data/test/spec_request.rb +0 -1423
  166. data/test/spec_response.rb +0 -528
  167. data/test/spec_rewindable_input.rb +0 -128
  168. data/test/spec_runtime.rb +0 -50
  169. data/test/spec_sendfile.rb +0 -125
  170. data/test/spec_server.rb +0 -193
  171. data/test/spec_session_abstract_id.rb +0 -31
  172. data/test/spec_session_abstract_session_hash.rb +0 -45
  173. data/test/spec_session_cookie.rb +0 -442
  174. data/test/spec_session_memcache.rb +0 -357
  175. data/test/spec_session_persisted_secure_secure_session_hash.rb +0 -73
  176. data/test/spec_session_pool.rb +0 -247
  177. data/test/spec_show_exceptions.rb +0 -93
  178. data/test/spec_show_status.rb +0 -104
  179. data/test/spec_static.rb +0 -184
  180. data/test/spec_tempfile_reaper.rb +0 -64
  181. data/test/spec_thin.rb +0 -96
  182. data/test/spec_urlmap.rb +0 -237
  183. data/test/spec_utils.rb +0 -742
  184. data/test/spec_version.rb +0 -11
  185. data/test/spec_webrick.rb +0 -206
  186. data/test/static/another/index.html +0 -1
  187. data/test/static/foo.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,6 +1,7 @@
1
1
  # -*- encoding: binary -*-
2
+ # frozen_string_literal: true
3
+
2
4
  require 'tempfile'
3
- require 'rack/utils'
4
5
 
5
6
  module Rack
6
7
  # Class which can make any IO object rewindable, including non-rewindable ones. It does
@@ -40,7 +41,7 @@ module Rack
40
41
  end
41
42
 
42
43
  # Closes this RewindableInput object without closing the originally
43
- # wrapped IO oject. Cleans up any temporary resources that this RewindableInput
44
+ # wrapped IO object. Cleans up any temporary resources that this RewindableInput
44
45
  # has created.
45
46
  #
46
47
  # This method may be called multiple times. It does nothing on subsequent calls.
@@ -72,7 +73,7 @@ module Rack
72
73
  @unlinked = true
73
74
  end
74
75
 
75
- buffer = ""
76
+ buffer = "".dup
76
77
  while @io.read(1024 * 4, buffer)
77
78
  entire_buffer_written_out = false
78
79
  while !entire_buffer_written_out
data/lib/rack/runtime.rb CHANGED
@@ -1,4 +1,4 @@
1
- require 'rack/utils'
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Rack
4
4
  # Sets an "X-Runtime" response header, indicating the response
@@ -8,8 +8,8 @@ module Rack
8
8
  # time, or before all the other middlewares to include time for them,
9
9
  # too.
10
10
  class Runtime
11
- FORMAT_STRING = "%0.6f".freeze # :nodoc:
12
- HEADER_NAME = "X-Runtime".freeze # :nodoc:
11
+ FORMAT_STRING = "%0.6f" # :nodoc:
12
+ HEADER_NAME = "X-Runtime" # :nodoc:
13
13
 
14
14
  def initialize(app, name = nil)
15
15
  @app = app
@@ -20,9 +20,11 @@ module Rack
20
20
  def call(env)
21
21
  start_time = Utils.clock_time
22
22
  status, headers, body = @app.call(env)
23
+ headers = Utils::HeaderHash[headers]
24
+
23
25
  request_time = Utils.clock_time - start_time
24
26
 
25
- unless headers.has_key?(@header_name)
27
+ unless headers.key?(@header_name)
26
28
  headers[@header_name] = FORMAT_STRING % request_time
27
29
  end
28
30
 
data/lib/rack/sendfile.rb CHANGED
@@ -1,5 +1,4 @@
1
- require 'rack/file'
2
- require 'rack/body_proxy'
1
+ # frozen_string_literal: true
3
2
 
4
3
  module Rack
5
4
 
@@ -14,7 +13,7 @@ module Rack
14
13
  #
15
14
  # In order to take advantage of this middleware, the response body must
16
15
  # 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
16
+ # header. Rack::Files and other components implement +to_path+ so there's
18
17
  # rarely anything you need to do in your application. The X-Sendfile-Type
19
18
  # header is typically set in your web servers configuration. The following
20
19
  # sections attempt to document
@@ -53,7 +52,7 @@ module Rack
53
52
  # that it maps to. The middleware performs a simple substitution on the
54
53
  # resulting path.
55
54
  #
56
- # See Also: http://wiki.codemongers.com/NginxXSendfile
55
+ # See Also: https://www.nginx.com/resources/wiki/start/topics/examples/xsendfile
57
56
  #
58
57
  # === lighttpd
59
58
  #
@@ -99,7 +98,7 @@ module Rack
99
98
  # will be matched with case indifference.
100
99
 
101
100
  class Sendfile
102
- def initialize(app, variation=nil, mappings=[])
101
+ def initialize(app, variation = nil, mappings = [])
103
102
  @app = app
104
103
  @variation = variation
105
104
  @mappings = mappings.map do |internal, external|
@@ -115,7 +114,8 @@ module Rack
115
114
  path = ::File.expand_path(body.to_path)
116
115
  if url = map_accel_path(env, path)
117
116
  headers[CONTENT_LENGTH] = '0'
118
- headers[type] = url
117
+ # '?' must be percent-encoded because it is not query string but a part of path
118
+ headers[type] = ::Rack::Utils.escape_path(url).gsub('?', '%3F')
119
119
  obody = body
120
120
  body = Rack::BodyProxy.new([]) do
121
121
  obody.close if obody.respond_to?(:close)
@@ -147,11 +147,15 @@ module Rack
147
147
  end
148
148
 
149
149
  def map_accel_path(env, path)
150
- if mapping = @mappings.find { |internal,_| internal =~ path }
150
+ if mapping = @mappings.find { |internal, _| internal =~ path }
151
151
  path.sub(*mapping)
152
152
  elsif mapping = env['HTTP_X_ACCEL_MAPPING']
153
- internal, external = mapping.split('=', 2).map(&:strip)
154
- path.sub(/^#{internal}/i, external)
153
+ mapping.split(',').map(&:strip).each do |m|
154
+ internal, external = m.split('=', 2).map(&:strip)
155
+ new_path = path.sub(/^#{internal}/i, external)
156
+ return new_path unless path == new_path
157
+ end
158
+ path
155
159
  end
156
160
  end
157
161
  end
data/lib/rack/server.rb CHANGED
@@ -1,10 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'optparse'
2
4
  require 'fileutils'
3
5
 
4
-
5
6
  module Rack
6
7
 
7
8
  class Server
9
+ (require_relative 'core_ext/regexp'; using ::Rack::RegexpExtensions) if RUBY_VERSION < '2.4'
8
10
 
9
11
  class Options
10
12
  def parse!(args)
@@ -21,10 +23,6 @@ module Rack
21
23
  lineno += 1
22
24
  }
23
25
 
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
26
  opts.on("-d", "--debug", "set debugging flags (set $DEBUG to true)") {
29
27
  options[:debug] = true
30
28
  }
@@ -42,12 +40,16 @@ module Rack
42
40
 
43
41
  opts.on("-r", "--require LIBRARY",
44
42
  "require the library, before executing your script") { |library|
45
- options[:require] = library
43
+ (options[:require] ||= []) << library
46
44
  }
47
45
 
48
46
  opts.separator ""
49
47
  opts.separator "Rack options:"
50
- opts.on("-s", "--server SERVER", "serve using SERVER (thin/puma/webrick/mongrel)") { |s|
48
+ opts.on("-b", "--builder BUILDER_LINE", "evaluate a BUILDER_LINE of code as a builder script") { |line|
49
+ options[:builder] = line
50
+ }
51
+
52
+ opts.on("-s", "--server SERVER", "serve using SERVER (thin/puma/webrick)") { |s|
51
53
  options[:server] = s
52
54
  }
53
55
 
@@ -77,6 +79,24 @@ module Rack
77
79
  options[:pid] = ::File.expand_path(f)
78
80
  }
79
81
 
82
+ opts.separator ""
83
+ opts.separator "Profiling options:"
84
+
85
+ opts.on("--heap HEAPFILE", "Build the application, then dump the heap to HEAPFILE") do |e|
86
+ options[:heapfile] = e
87
+ end
88
+
89
+ opts.on("--profile PROFILE", "Dump CPU or Memory profile to PROFILE (defaults to a tempfile)") do |e|
90
+ options[:profile_file] = e
91
+ end
92
+
93
+ opts.on("--profile-mode MODE", "Profile mode (cpu|wall|object)") do |e|
94
+ { cpu: true, wall: true, object: true }.fetch(e.to_sym) do
95
+ raise OptionParser::InvalidOption, "unknown profile mode: #{e}"
96
+ end
97
+ options[:profile_mode] = e.to_sym
98
+ end
99
+
80
100
  opts.separator ""
81
101
  opts.separator "Common options:"
82
102
 
@@ -114,14 +134,14 @@ module Rack
114
134
 
115
135
  has_options = false
116
136
  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.
137
+ next if /^(Host|Port)[^a-zA-Z]/.match?(name.to_s) # ignore handler's host and port options, we do our own.
118
138
  info << " -O %-21s %s" % [name, description]
119
139
  has_options = true
120
140
  end
121
141
  return "" if !has_options
122
142
  end
123
143
  info.join("\n")
124
- rescue NameError
144
+ rescue NameError, LoadError
125
145
  return "Warning: Could not find handler specified (#{options[:server] || 'default'}) to determine handler-specific options"
126
146
  end
127
147
  end
@@ -152,7 +172,9 @@ module Rack
152
172
 
153
173
  # Options may include:
154
174
  # * :app
155
- # a rack application to run (overrides :config)
175
+ # a rack application to run (overrides :config and :builder)
176
+ # * :builder
177
+ # a string to evaluate a Rack::Builder from
156
178
  # * :config
157
179
  # a rackup configuration file path to load (.ru)
158
180
  # * :environment
@@ -182,6 +204,14 @@ module Rack
182
204
  # add given paths to $LOAD_PATH
183
205
  # * :require
184
206
  # require the given libraries
207
+ #
208
+ # Additional options for profiling app initialization include:
209
+ # * :heapfile
210
+ # location for ObjectSpace.dump_all to write the output to
211
+ # * :profile_file
212
+ # location for CPU/Memory (StackProf) profile output (defaults to a tempfile)
213
+ # * :profile_mode
214
+ # StackProf profile mode (cpu|wall|object)
185
215
  def initialize(options = nil)
186
216
  @ignore_options = []
187
217
 
@@ -206,12 +236,12 @@ module Rack
206
236
  default_host = environment == 'development' ? 'localhost' : '0.0.0.0'
207
237
 
208
238
  {
209
- :environment => environment,
210
- :pid => nil,
211
- :Port => 9292,
212
- :Host => default_host,
213
- :AccessLog => [],
214
- :config => "config.ru"
239
+ environment: environment,
240
+ pid: nil,
241
+ Port: 9292,
242
+ Host: default_host,
243
+ AccessLog: [],
244
+ config: "config.ru"
215
245
  }
216
246
  end
217
247
 
@@ -222,21 +252,19 @@ module Rack
222
252
  class << self
223
253
  def logging_middleware
224
254
  lambda { |server|
225
- server.server.name =~ /CGI/ || server.options[:quiet] ? nil : [Rack::CommonLogger, $stderr]
255
+ /CGI/.match?(server.server.name) || server.options[:quiet] ? nil : [Rack::CommonLogger, $stderr]
226
256
  }
227
257
  end
228
258
 
229
259
  def default_middleware_by_environment
230
- m = Hash.new {|h,k| h[k] = []}
260
+ m = Hash.new {|h, k| h[k] = []}
231
261
  m["deployment"] = [
232
262
  [Rack::ContentLength],
233
- [Rack::Chunked],
234
263
  logging_middleware,
235
264
  [Rack::TempfileReaper]
236
265
  ]
237
266
  m["development"] = [
238
267
  [Rack::ContentLength],
239
- [Rack::Chunked],
240
268
  logging_middleware,
241
269
  [Rack::ShowExceptions],
242
270
  [Rack::Lint],
@@ -255,7 +283,7 @@ module Rack
255
283
  self.class.middleware
256
284
  end
257
285
 
258
- def start &blk
286
+ def start(&block)
259
287
  if options[:warn]
260
288
  $-w = true
261
289
  end
@@ -264,7 +292,7 @@ module Rack
264
292
  $LOAD_PATH.unshift(*includes)
265
293
  end
266
294
 
267
- if library = options[:require]
295
+ Array(options[:require]).each do |library|
268
296
  require library
269
297
  end
270
298
 
@@ -280,7 +308,9 @@ module Rack
280
308
 
281
309
  # Touch the wrapped app, so that the config.ru is loaded before
282
310
  # daemonization (i.e. before chdir, etc).
283
- wrapped_app
311
+ handle_profiling(options[:heapfile], options[:profile_mode], options[:profile_file]) do
312
+ wrapped_app
313
+ end
284
314
 
285
315
  daemonize_app if options[:daemonize]
286
316
 
@@ -294,7 +324,7 @@ module Rack
294
324
  end
295
325
  end
296
326
 
297
- server.run wrapped_app, options, &blk
327
+ server.run(wrapped_app, **options, &block)
298
328
  end
299
329
 
300
330
  def server
@@ -321,6 +351,44 @@ module Rack
321
351
  app
322
352
  end
323
353
 
354
+ def handle_profiling(heapfile, profile_mode, filename)
355
+ if heapfile
356
+ require "objspace"
357
+ ObjectSpace.trace_object_allocations_start
358
+ yield
359
+ GC.start
360
+ ::File.open(heapfile, "w") { |f| ObjectSpace.dump_all(output: f) }
361
+ exit
362
+ end
363
+
364
+ if profile_mode
365
+ require "stackprof"
366
+ require "tempfile"
367
+
368
+ make_profile_name(filename) do |filename|
369
+ ::File.open(filename, "w") do |f|
370
+ StackProf.run(mode: profile_mode, out: f) do
371
+ yield
372
+ end
373
+ puts "Profile written to: #{filename}"
374
+ end
375
+ end
376
+ exit
377
+ end
378
+
379
+ yield
380
+ end
381
+
382
+ def make_profile_name(filename)
383
+ if filename
384
+ yield filename
385
+ else
386
+ ::Dir::Tmpname.create("profile.dump") do |tmpname, _, _|
387
+ yield tmpname
388
+ end
389
+ end
390
+ end
391
+
324
392
  def build_app_from_string
325
393
  Rack::Builder.new_from_string(self.options[:builder])
326
394
  end
@@ -355,7 +423,10 @@ module Rack
355
423
  end
356
424
 
357
425
  def daemonize_app
426
+ # Cannot be covered as it forks
427
+ # :nocov:
358
428
  Process.daemon
429
+ # :nocov:
359
430
  end
360
431
 
361
432
  def write_pid
@@ -1,10 +1,10 @@
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
 
4
- require 'rack'
6
+ require_relative '../../../rack'
5
7
  require 'time'
6
- require 'rack/request'
7
- require 'rack/response'
8
8
  require 'securerandom'
9
9
  require 'digest/sha2'
10
10
 
@@ -84,7 +84,12 @@ module Rack
84
84
  @data[key.to_s]
85
85
  end
86
86
 
87
- def fetch(key, default=Unspecified, &block)
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)
88
93
  load_for_read!
89
94
  if default == Unspecified
90
95
  @data.fetch(key.to_s, &block)
@@ -187,8 +192,9 @@ module Rack
187
192
  end
188
193
 
189
194
  def stringify_keys(other)
195
+ # Use transform_keys after dropping Ruby 2.4 support
190
196
  hash = {}
191
- other.each do |key, value|
197
+ other.to_hash.each do |key, value|
192
198
  hash[key.to_s] = value
193
199
  end
194
200
  hash
@@ -197,8 +203,8 @@ module Rack
197
203
 
198
204
  # ID sets up a basic framework for implementing an id based sessioning
199
205
  # 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.
206
+ # contain an id reference. Only #find_session, #write_session and
207
+ # #delete_session are required to be overwritten.
202
208
  #
203
209
  # All parameters are optional.
204
210
  # * :key determines the name of the cookie, by default it is
@@ -226,22 +232,22 @@ module Rack
226
232
 
227
233
  class Persisted
228
234
  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
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
240
246
  }.freeze
241
247
 
242
248
  attr_reader :key, :default_options, :sid_secure
243
249
 
244
- def initialize(app, options={})
250
+ def initialize(app, options = {})
245
251
  @app = app
246
252
  @default_options = self.class::DEFAULT_OPTIONS.merge(options)
247
253
  @key = @default_options.delete(:key)
@@ -253,7 +259,7 @@ module Rack
253
259
  context(env)
254
260
  end
255
261
 
256
- def context(env, app=@app)
262
+ def context(env, app = @app)
257
263
  req = make_request env
258
264
  prepare_session(req)
259
265
  status, headers, body = app.call(req.env)
@@ -376,7 +382,7 @@ module Rack
376
382
 
377
383
  session.send(:load!) unless loaded_session?(session)
378
384
  session_id ||= session.id
379
- session_data = session.to_hash.delete_if { |k,v| v.nil? }
385
+ session_data = session.to_hash.delete_if { |k, v| v.nil? }
380
386
 
381
387
  if not data = write_session(req, session_id, session_data, options)
382
388
  req.get_header(RACK_ERRORS).puts("Warning! #{self.class.name} failed to save session. Content dropped.")
@@ -387,6 +393,12 @@ module Rack
387
393
  cookie[:value] = cookie_value(data)
388
394
  cookie[:expires] = Time.now + options[:expire_after] if options[:expire_after]
389
395
  cookie[:expires] = Time.now + options[:max_age] if options[:max_age]
396
+
397
+ if @same_site.respond_to? :call
398
+ cookie[:same_site] = @same_site.call(req, res)
399
+ else
400
+ cookie[:same_site] = @same_site
401
+ end
390
402
  set_cookie(req, res, cookie.merge!(options))
391
403
  end
392
404
  end
@@ -1,9 +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
6
  require 'json'
7
+ require 'base64'
7
8
 
8
9
  module Rack
9
10
 
@@ -49,11 +50,11 @@ module Rack
49
50
  # Encode session cookies as Base64
50
51
  class Base64
51
52
  def encode(str)
52
- [str].pack('m')
53
+ ::Base64.strict_encode64(str)
53
54
  end
54
55
 
55
56
  def decode(str)
56
- str.unpack('m').first
57
+ ::Base64.decode64(str)
57
58
  end
58
59
 
59
60
  # Encode session cookies as Marshaled Base64 data
@@ -103,7 +104,7 @@ module Rack
103
104
 
104
105
  attr_reader :coder
105
106
 
106
- def initialize(app, options={})
107
+ def initialize(app, options = {})
107
108
  @secrets = options.values_at(:secret, :old_secret).compact
108
109
  @hmac = options.fetch(:hmac, OpenSSL::Digest::SHA1)
109
110
 
@@ -116,8 +117,9 @@ module Rack
116
117
 
117
118
  Called from: #{caller[0]}.
118
119
  MSG
119
- @coder = options[:coder] ||= Base64::Marshal.new
120
- super(app, options.merge!(:cookie_only => true))
120
+ @coder = options[:coder] ||= Base64::Marshal.new
121
+ @same_site = options.delete :same_site
122
+ super(app, options.merge!(cookie_only: true))
121
123
  end
122
124
 
123
125
  private
@@ -137,9 +139,7 @@ module Rack
137
139
  session_data = request.cookies[@key]
138
140
 
139
141
  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
142
+ session_data, _, digest = session_data.rpartition('--')
143
143
  session_data = nil unless digest_match?(session_data, digest)
144
144
  end
145
145
 
@@ -147,7 +147,7 @@ module Rack
147
147
  end
148
148
  end
149
149
 
150
- def persistent_session_id!(data, sid=nil)
150
+ def persistent_session_id!(data, sid = nil)
151
151
  data ||= {}
152
152
  data["session_id"] ||= sid || generate_sid
153
153
  data
@@ -1,99 +1,10 @@
1
- # AUTHOR: blink <blinketje@gmail.com>; blink#ruby-lang@irc.freenode.net
1
+ # frozen_string_literal: true
2
2
 
3
- require 'rack/session/abstract/id'
4
- require 'memcache'
3
+ require 'rack/session/dalli'
5
4
 
6
5
  module Rack
7
6
  module Session
8
- # Rack::Session::Memcache provides simple cookie based session management.
9
- # Session data is stored in memcached. The corresponding session key is
10
- # maintained in the cookie.
11
- # You may treat Session::Memcache as you would Session::Pool with the
12
- # following caveats.
13
- #
14
- # * Setting :expire_after to 0 would note to the Memcache server to hang
15
- # onto the session data until it would drop it according to it's own
16
- # specifications. However, the cookie sent to the client would expire
17
- # immediately.
18
- #
19
- # Note that memcache does drop data before it may be listed to expire. For
20
- # a full description of behaviour, please see memcache's documentation.
21
-
22
- class Memcache < Abstract::PersistedSecure
23
- attr_reader :mutex, :pool
24
-
25
- DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge \
26
- :namespace => 'rack:session',
27
- :memcache_server => 'localhost:11211'
28
-
29
- def initialize(app, options={})
30
- super
31
-
32
- @mutex = Mutex.new
33
- mserv = @default_options[:memcache_server]
34
- mopts = @default_options.reject{|k,v| !MemCache::DEFAULT_OPTIONS.include? k }
35
-
36
- @pool = options[:cache] || MemCache.new(mserv, mopts)
37
- unless @pool.active? and @pool.servers.any?(&:alive?)
38
- raise 'No memcache servers'
39
- end
40
- end
41
-
42
- def generate_sid
43
- loop do
44
- sid = super
45
- break sid unless @pool.get(sid.private_id, true)
46
- end
47
- end
48
-
49
- def find_session(req, sid)
50
- with_lock(req) do
51
- unless sid and session = get_session_with_fallback(sid)
52
- sid, session = generate_sid, {}
53
- unless /^STORED/ =~ @pool.add(sid.private_id, session)
54
- raise "Session collision on '#{sid.inspect}'"
55
- end
56
- end
57
- [sid, session]
58
- end
59
- end
60
-
61
- def write_session(req, session_id, new_session, options)
62
- expiry = options[:expire_after]
63
- expiry = expiry.nil? ? 0 : expiry + 1
64
-
65
- with_lock(req) do
66
- @pool.set session_id.private_id, new_session, expiry
67
- session_id
68
- end
69
- end
70
-
71
- def delete_session(req, session_id, options)
72
- with_lock(req) do
73
- @pool.delete(session_id.public_id)
74
- @pool.delete(session_id.private_id)
75
- generate_sid unless options[:drop]
76
- end
77
- end
78
-
79
- def with_lock(req)
80
- @mutex.lock if req.multithread?
81
- yield
82
- rescue MemCache::MemCacheError, Errno::ECONNREFUSED
83
- if $VERBOSE
84
- warn "#{self} is unable to find memcached server."
85
- warn $!.inspect
86
- end
87
- raise
88
- ensure
89
- @mutex.unlock if @mutex.locked?
90
- end
91
-
92
- private
93
-
94
- def get_session_with_fallback(sid)
95
- @pool.get(sid.private_id) || @pool.get(sid.public_id)
96
- end
97
- end
7
+ warn "Rack::Session::Memcache is deprecated, please use Rack::Session::Dalli from 'dalli' gem instead."
8
+ Memcache = Dalli
98
9
  end
99
10
  end
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # AUTHOR: blink <blinketje@gmail.com>; blink#ruby-lang@irc.freenode.net
2
4
  # THANKS:
3
5
  # apeiros, for session id generation, expiry setup, and threadiness
4
6
  # sergio, threadiness and bugreps
5
7
 
6
- require 'rack/session/abstract/id'
8
+ require_relative 'abstract/id'
7
9
  require 'thread'
8
10
 
9
11
  module Rack
@@ -26,9 +28,9 @@ module Rack
26
28
 
27
29
  class Pool < Abstract::PersistedSecure
28
30
  attr_reader :mutex, :pool
29
- DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge :drop => false
31
+ DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge drop: false
30
32
 
31
- def initialize(app, options={})
33
+ def initialize(app, options = {})
32
34
  super
33
35
  @pool = Hash.new
34
36
  @mutex = Mutex.new