sidekiq 6.1.1 → 6.5.7

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of sidekiq might be problematic. Click here for more details.

Files changed (118) hide show
  1. checksums.yaml +4 -4
  2. data/Changes.md +230 -3
  3. data/LICENSE +3 -3
  4. data/README.md +10 -6
  5. data/bin/sidekiq +3 -3
  6. data/bin/sidekiqload +70 -66
  7. data/bin/sidekiqmon +1 -1
  8. data/lib/generators/sidekiq/job_generator.rb +57 -0
  9. data/lib/generators/sidekiq/templates/{worker.rb.erb → job.rb.erb} +2 -2
  10. data/lib/generators/sidekiq/templates/{worker_spec.rb.erb → job_spec.rb.erb} +1 -1
  11. data/lib/generators/sidekiq/templates/{worker_test.rb.erb → job_test.rb.erb} +1 -1
  12. data/lib/sidekiq/api.rb +335 -146
  13. data/lib/sidekiq/cli.rb +74 -41
  14. data/lib/sidekiq/client.rb +48 -72
  15. data/lib/sidekiq/{util.rb → component.rb} +12 -14
  16. data/lib/sidekiq/delay.rb +3 -1
  17. data/lib/sidekiq/extensions/action_mailer.rb +3 -2
  18. data/lib/sidekiq/extensions/active_record.rb +1 -1
  19. data/lib/sidekiq/extensions/generic_proxy.rb +4 -2
  20. data/lib/sidekiq/fetch.rb +31 -20
  21. data/lib/sidekiq/job.rb +13 -0
  22. data/lib/sidekiq/job_logger.rb +16 -28
  23. data/lib/sidekiq/job_retry.rb +79 -59
  24. data/lib/sidekiq/job_util.rb +71 -0
  25. data/lib/sidekiq/launcher.rb +126 -65
  26. data/lib/sidekiq/logger.rb +11 -20
  27. data/lib/sidekiq/manager.rb +35 -34
  28. data/lib/sidekiq/metrics/deploy.rb +47 -0
  29. data/lib/sidekiq/metrics/query.rb +153 -0
  30. data/lib/sidekiq/metrics/shared.rb +94 -0
  31. data/lib/sidekiq/metrics/tracking.rb +134 -0
  32. data/lib/sidekiq/middleware/chain.rb +88 -42
  33. data/lib/sidekiq/middleware/current_attributes.rb +63 -0
  34. data/lib/sidekiq/middleware/i18n.rb +6 -4
  35. data/lib/sidekiq/middleware/modules.rb +21 -0
  36. data/lib/sidekiq/monitor.rb +1 -1
  37. data/lib/sidekiq/paginator.rb +8 -8
  38. data/lib/sidekiq/processor.rb +47 -41
  39. data/lib/sidekiq/rails.rb +22 -4
  40. data/lib/sidekiq/redis_client_adapter.rb +154 -0
  41. data/lib/sidekiq/redis_connection.rb +84 -55
  42. data/lib/sidekiq/ring_buffer.rb +29 -0
  43. data/lib/sidekiq/scheduled.rb +96 -32
  44. data/lib/sidekiq/testing/inline.rb +4 -4
  45. data/lib/sidekiq/testing.rb +38 -39
  46. data/lib/sidekiq/transaction_aware_client.rb +45 -0
  47. data/lib/sidekiq/version.rb +1 -1
  48. data/lib/sidekiq/web/action.rb +3 -3
  49. data/lib/sidekiq/web/application.rb +38 -16
  50. data/lib/sidekiq/web/csrf_protection.rb +32 -5
  51. data/lib/sidekiq/web/helpers.rb +60 -28
  52. data/lib/sidekiq/web/router.rb +4 -1
  53. data/lib/sidekiq/web.rb +38 -78
  54. data/lib/sidekiq/worker.rb +140 -14
  55. data/lib/sidekiq.rb +114 -31
  56. data/sidekiq.gemspec +12 -4
  57. data/web/assets/images/apple-touch-icon.png +0 -0
  58. data/web/assets/javascripts/application.js +113 -60
  59. data/web/assets/javascripts/chart.min.js +13 -0
  60. data/web/assets/javascripts/chartjs-plugin-annotation.min.js +7 -0
  61. data/web/assets/javascripts/dashboard.js +50 -67
  62. data/web/assets/javascripts/graph.js +16 -0
  63. data/web/assets/javascripts/metrics.js +262 -0
  64. data/web/assets/stylesheets/application-dark.css +61 -51
  65. data/web/assets/stylesheets/application-rtl.css +0 -4
  66. data/web/assets/stylesheets/application.css +84 -243
  67. data/web/locales/ar.yml +8 -2
  68. data/web/locales/el.yml +43 -19
  69. data/web/locales/en.yml +11 -1
  70. data/web/locales/es.yml +18 -2
  71. data/web/locales/fr.yml +8 -1
  72. data/web/locales/ja.yml +10 -0
  73. data/web/locales/lt.yml +1 -1
  74. data/web/locales/pt-br.yml +27 -9
  75. data/web/locales/ru.yml +4 -0
  76. data/web/locales/zh-cn.yml +36 -11
  77. data/web/locales/zh-tw.yml +32 -7
  78. data/web/views/_footer.erb +1 -1
  79. data/web/views/_job_info.erb +1 -1
  80. data/web/views/_nav.erb +1 -1
  81. data/web/views/_poll_link.erb +2 -5
  82. data/web/views/_summary.erb +7 -7
  83. data/web/views/busy.erb +52 -21
  84. data/web/views/dashboard.erb +23 -14
  85. data/web/views/dead.erb +1 -1
  86. data/web/views/layout.erb +2 -1
  87. data/web/views/metrics.erb +69 -0
  88. data/web/views/metrics_for_job.erb +87 -0
  89. data/web/views/morgue.erb +6 -6
  90. data/web/views/queue.erb +15 -11
  91. data/web/views/queues.erb +4 -4
  92. data/web/views/retries.erb +7 -7
  93. data/web/views/retry.erb +1 -1
  94. data/web/views/scheduled.erb +1 -1
  95. metadata +46 -39
  96. data/.circleci/config.yml +0 -71
  97. data/.github/contributing.md +0 -32
  98. data/.github/issue_template.md +0 -11
  99. data/.gitignore +0 -13
  100. data/.standard.yml +0 -20
  101. data/3.0-Upgrade.md +0 -70
  102. data/4.0-Upgrade.md +0 -53
  103. data/5.0-Upgrade.md +0 -56
  104. data/6.0-Upgrade.md +0 -72
  105. data/COMM-LICENSE +0 -97
  106. data/Ent-2.0-Upgrade.md +0 -37
  107. data/Ent-Changes.md +0 -275
  108. data/Gemfile +0 -24
  109. data/Gemfile.lock +0 -208
  110. data/Pro-2.0-Upgrade.md +0 -138
  111. data/Pro-3.0-Upgrade.md +0 -44
  112. data/Pro-4.0-Upgrade.md +0 -35
  113. data/Pro-5.0-Upgrade.md +0 -25
  114. data/Pro-Changes.md +0 -795
  115. data/Rakefile +0 -10
  116. data/code_of_conduct.md +0 -50
  117. data/lib/generators/sidekiq/worker_generator.rb +0 -57
  118. data/lib/sidekiq/exception_handler.rb +0 -27
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "securerandom"
4
+ require "sidekiq/client"
5
+
6
+ module Sidekiq
7
+ class TransactionAwareClient
8
+ def initialize(redis_pool)
9
+ @redis_client = Client.new(redis_pool)
10
+ end
11
+
12
+ def push(item)
13
+ # pre-allocate the JID so we can return it immediately and
14
+ # save it to the database as part of the transaction.
15
+ item["jid"] ||= SecureRandom.hex(12)
16
+ AfterCommitEverywhere.after_commit { @redis_client.push(item) }
17
+ item["jid"]
18
+ end
19
+
20
+ ##
21
+ # We don't provide transactionality for push_bulk because we don't want
22
+ # to hold potentially hundreds of thousands of job records in memory due to
23
+ # a long running enqueue process.
24
+ def push_bulk(items)
25
+ @redis_client.push_bulk(items)
26
+ end
27
+ end
28
+ end
29
+
30
+ ##
31
+ # Use `Sidekiq.transactional_push!` in your sidekiq.rb initializer
32
+ module Sidekiq
33
+ def self.transactional_push!
34
+ begin
35
+ require "after_commit_everywhere"
36
+ rescue LoadError
37
+ Sidekiq.logger.error("You need to add after_commit_everywhere to your Gemfile to use Sidekiq's transactional client")
38
+ raise
39
+ end
40
+
41
+ default_job_options["client_class"] = Sidekiq::TransactionAwareClient
42
+ Sidekiq::JobUtil::TRANSIENT_ATTRIBUTES << "client_class"
43
+ true
44
+ end
45
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sidekiq
4
- VERSION = "6.1.1"
4
+ VERSION = "6.5.7"
5
5
  end
@@ -15,11 +15,11 @@ module Sidekiq
15
15
  end
16
16
 
17
17
  def halt(res)
18
- throw :halt, res
18
+ throw :halt, [res, {"content-type" => "text/plain"}, [res.to_s]]
19
19
  end
20
20
 
21
21
  def redirect(location)
22
- throw :halt, [302, {"Location" => "#{request.base_url}#{location}"}, []]
22
+ throw :halt, [302, {"location" => "#{request.base_url}#{location}"}, []]
23
23
  end
24
24
 
25
25
  def params
@@ -68,7 +68,7 @@ module Sidekiq
68
68
  end
69
69
 
70
70
  def json(payload)
71
- [200, {"Content-Type" => "application/json", "Cache-Control" => "no-cache"}, [Sidekiq.dump_json(payload)]]
71
+ [200, {"content-type" => "application/json", "cache-control" => "private, no-store"}, [Sidekiq.dump_json(payload)]]
72
72
  end
73
73
 
74
74
  def initialize(env, block)
@@ -4,7 +4,6 @@ module Sidekiq
4
4
  class WebApplication
5
5
  extend WebRouter
6
6
 
7
- CONTENT_LENGTH = "Content-Length"
8
7
  REDIS_KEYS = %w[redis_version uptime_in_days connected_clients used_memory_human used_memory_peak_human]
9
8
  CSP_HEADER = [
10
9
  "default-src 'self' https: http:",
@@ -42,15 +41,38 @@ module Sidekiq
42
41
  # nothing, backwards compatibility
43
42
  end
44
43
 
44
+ head "/" do
45
+ # HEAD / is the cheapest heartbeat possible,
46
+ # it hits Redis to ensure connectivity
47
+ Sidekiq.redis { |c| c.llen("queue:default") }
48
+ ""
49
+ end
50
+
45
51
  get "/" do
46
52
  @redis_info = redis_info.select { |k, v| REDIS_KEYS.include? k }
47
- stats_history = Sidekiq::Stats::History.new((params["days"] || 30).to_i)
53
+ days = (params["days"] || 30).to_i
54
+ return halt(401) if days < 1 || days > 180
55
+
56
+ stats_history = Sidekiq::Stats::History.new(days)
48
57
  @processed_history = stats_history.processed
49
58
  @failed_history = stats_history.failed
50
59
 
51
60
  erb(:dashboard)
52
61
  end
53
62
 
63
+ get "/metrics" do
64
+ q = Sidekiq::Metrics::Query.new
65
+ @query_result = q.top_jobs
66
+ erb(:metrics)
67
+ end
68
+
69
+ get "/metrics/:name" do
70
+ @name = route_params[:name]
71
+ q = Sidekiq::Metrics::Query.new
72
+ @query_result = q.for_job(@name)
73
+ erb(:metrics_for_job)
74
+ end
75
+
54
76
  get "/busy" do
55
77
  erb(:busy)
56
78
  end
@@ -76,15 +98,17 @@ module Sidekiq
76
98
  erb(:queues)
77
99
  end
78
100
 
101
+ QUEUE_NAME = /\A[a-z_:.\-0-9]+\z/i
102
+
79
103
  get "/queues/:name" do
80
104
  @name = route_params[:name]
81
105
 
82
- halt(404) unless @name
106
+ halt(404) if !@name || @name !~ QUEUE_NAME
83
107
 
84
108
  @count = (params["count"] || 25).to_i
85
109
  @queue = Sidekiq::Queue.new(@name)
86
- (@current_page, @total_size, @messages) = page("queue:#{@name}", params["page"], @count, reverse: params["direction"] == "asc")
87
- @messages = @messages.map { |msg| Sidekiq::Job.new(msg, @name) }
110
+ (@current_page, @total_size, @jobs) = page("queue:#{@name}", params["page"], @count, reverse: params["direction"] == "asc")
111
+ @jobs = @jobs.map { |msg| Sidekiq::JobRecord.new(msg, @name) }
88
112
 
89
113
  erb(:queue)
90
114
  end
@@ -105,7 +129,7 @@ module Sidekiq
105
129
 
106
130
  post "/queues/:name/delete" do
107
131
  name = route_params[:name]
108
- Sidekiq::Job.new(params["key_val"], name).delete
132
+ Sidekiq::JobRecord.new(params["key_val"], name).delete
109
133
 
110
134
  redirect_with_query("#{root_path}queues/#{CGI.escape(name)}")
111
135
  end
@@ -288,37 +312,35 @@ module Sidekiq
288
312
 
289
313
  def call(env)
290
314
  action = self.class.match(env)
291
- return [404, {"Content-Type" => "text/plain", "X-Cascade" => "pass"}, ["Not Found"]] unless action
315
+ return [404, {"content-type" => "text/plain", "x-cascade" => "pass"}, ["Not Found"]] unless action
292
316
 
293
317
  app = @klass
294
- resp = catch(:halt) do # rubocop:disable Standard/SemanticBlocks
318
+ resp = catch(:halt) do
295
319
  self.class.run_befores(app, action)
296
320
  action.instance_exec env, &action.block
297
321
  ensure
298
322
  self.class.run_afters(app, action)
299
323
  end
300
324
 
301
- resp = case resp
325
+ case resp
302
326
  when Array
303
327
  # redirects go here
304
328
  resp
305
329
  else
306
330
  # rendered content goes here
307
331
  headers = {
308
- "Content-Type" => "text/html",
309
- "Cache-Control" => "no-cache",
310
- "Content-Language" => action.locale,
311
- "Content-Security-Policy" => CSP_HEADER
332
+ "content-type" => "text/html",
333
+ "cache-control" => "private, no-store",
334
+ "content-language" => action.locale,
335
+ "content-security-policy" => CSP_HEADER
312
336
  }
313
337
  # we'll let Rack calculate Content-Length for us.
314
338
  [200, headers, [resp]]
315
339
  end
316
-
317
- resp
318
340
  end
319
341
 
320
342
  def self.helpers(mod = nil, &block)
321
- if block_given?
343
+ if block
322
344
  WebAction.class_eval(&block)
323
345
  else
324
346
  WebAction.send(:include, mod)
@@ -66,13 +66,37 @@ module Sidekiq
66
66
  end
67
67
 
68
68
  def session(env)
69
- env["rack.session"] || fail("you need to set up a session middleware *before* #{self.class}")
69
+ env["rack.session"] || fail(<<~EOM)
70
+ Sidekiq::Web needs a valid Rack session for CSRF protection. If this is a Rails app,
71
+ make sure you mount Sidekiq::Web *inside* your application routes:
72
+
73
+
74
+ Rails.application.routes.draw do
75
+ mount Sidekiq::Web => "/sidekiq"
76
+ ....
77
+ end
78
+
79
+
80
+ If this is a Rails app in API mode, you need to enable sessions.
81
+
82
+ https://guides.rubyonrails.org/api_app.html#using-session-middlewares
83
+
84
+ If this is a bare Rack app, use a session middleware before Sidekiq::Web:
85
+
86
+ # first, use IRB to create a shared secret key for sessions and commit it
87
+ require 'securerandom'; File.open(".session.key", "w") {|f| f.write(SecureRandom.hex(32)) }
88
+
89
+ # now use the secret with a session cookie middleware
90
+ use Rack::Session::Cookie, secret: File.read(".session.key"), same_site: true, max_age: 86400
91
+ run Sidekiq::Web
92
+
93
+ EOM
70
94
  end
71
95
 
72
96
  def accept?(env)
73
97
  return true if safe?(env)
74
98
 
75
- giventoken = Rack::Request.new(env).params["authenticity_token"]
99
+ giventoken = ::Rack::Request.new(env).params["authenticity_token"]
76
100
  valid_token?(env, giventoken)
77
101
  end
78
102
 
@@ -92,6 +116,9 @@ module Sidekiq
92
116
  sess = session(env)
93
117
  localtoken = sess[:csrf]
94
118
 
119
+ # Checks that Rack::Session::Cookie actualy contains the csrf toekn
120
+ return false if localtoken.nil?
121
+
95
122
  # Rotate the session token after every use
96
123
  sess[:csrf] = SecureRandom.base64(TOKEN_LENGTH)
97
124
 
@@ -116,7 +143,7 @@ module Sidekiq
116
143
  one_time_pad = SecureRandom.random_bytes(token.length)
117
144
  encrypted_token = xor_byte_strings(one_time_pad, token)
118
145
  masked_token = one_time_pad + encrypted_token
119
- Base64.strict_encode64(masked_token)
146
+ Base64.urlsafe_encode64(masked_token)
120
147
  end
121
148
 
122
149
  # Essentially the inverse of +mask_token+.
@@ -138,11 +165,11 @@ module Sidekiq
138
165
  end
139
166
 
140
167
  def compare_with_real_token(token, local)
141
- Rack::Utils.secure_compare(token.to_s, decode_token(local).to_s)
168
+ ::Rack::Utils.secure_compare(token.to_s, decode_token(local).to_s)
142
169
  end
143
170
 
144
171
  def decode_token(token)
145
- Base64.strict_decode64(token)
172
+ Base64.urlsafe_decode64(token)
146
173
  end
147
174
 
148
175
  def xor_byte_strings(s1, s2)
@@ -10,18 +10,25 @@ module Sidekiq
10
10
  module WebHelpers
11
11
  def strings(lang)
12
12
  @strings ||= {}
13
- @strings[lang] ||= begin
14
- # Allow sidekiq-web extensions to add locale paths
15
- # so extensions can be localized
16
- settings.locales.each_with_object({}) do |path, global|
17
- find_locale_files(lang).each do |file|
18
- strs = YAML.load(File.open(file))
19
- global.merge!(strs[lang])
20
- end
13
+
14
+ # Allow sidekiq-web extensions to add locale paths
15
+ # so extensions can be localized
16
+ @strings[lang] ||= settings.locales.each_with_object({}) do |path, global|
17
+ find_locale_files(lang).each do |file|
18
+ strs = YAML.safe_load(File.open(file))
19
+ global.merge!(strs[lang])
21
20
  end
22
21
  end
23
22
  end
24
23
 
24
+ def singularize(str, count)
25
+ if count == 1 && str.respond_to?(:singularize) # rails
26
+ str.singularize
27
+ else
28
+ str
29
+ end
30
+ end
31
+
25
32
  def clear_caches
26
33
  @strings = nil
27
34
  @locale_files = nil
@@ -63,17 +70,6 @@ module Sidekiq
63
70
  @head_html.join if defined?(@head_html)
64
71
  end
65
72
 
66
- def poll_path
67
- if current_path != "" && params["poll"]
68
- path = root_path + current_path
69
- query_string = to_query_string(params.slice(*params.keys - %w[page poll]))
70
- path += "?#{query_string}" unless query_string.empty?
71
- path
72
- else
73
- ""
74
- end
75
- end
76
-
77
73
  def text_direction
78
74
  get_locale["TextDirection"] || "ltr"
79
75
  end
@@ -118,7 +114,7 @@ module Sidekiq
118
114
  # within is used by Sidekiq Pro
119
115
  def display_tags(job, within = nil)
120
116
  job.tags.map { |tag|
121
- "<span class='jobtag label label-info'>#{::Rack::Utils.escape_html(tag)}</span>"
117
+ "<span class='label label-info jobtag'>#{::Rack::Utils.escape_html(tag)}</span>"
122
118
  }.join(" ")
123
119
  end
124
120
 
@@ -144,22 +140,44 @@ module Sidekiq
144
140
  params[:direction] == "asc" ? "&uarr;" : "&darr;"
145
141
  end
146
142
 
147
- def workers
148
- @workers ||= Sidekiq::Workers.new
143
+ def workset
144
+ @work ||= Sidekiq::WorkSet.new
149
145
  end
150
146
 
151
147
  def processes
152
148
  @processes ||= Sidekiq::ProcessSet.new
153
149
  end
154
150
 
151
+ # Sorts processes by hostname following the natural sort order so that
152
+ # 'worker.1' < 'worker.2' < 'worker.10' < 'worker.20'
153
+ # '2.1.1.1' < '192.168.0.2' < '192.168.0.10'
154
+ def sorted_processes
155
+ @sorted_processes ||= begin
156
+ return processes unless processes.all? { |p| p["hostname"] }
157
+
158
+ split_characters = /[._-]+/
159
+
160
+ padding = processes.flat_map { |p| p["hostname"].split(split_characters) }.map(&:size).max
161
+
162
+ processes.to_a.sort_by do |process|
163
+ process["hostname"].split(split_characters).map do |substring|
164
+ # Left-pad the substring with '0' if it starts with a number or 'a'
165
+ # otherwise, so that '25' < 192' < 'a' ('025' < '192' < 'aaa')
166
+ padding_char = substring[0].match?(/\d/) ? "0" : "a"
167
+
168
+ substring.rjust(padding, padding_char)
169
+ end
170
+ end
171
+ end
172
+ end
173
+
155
174
  def stats
156
175
  @stats ||= Sidekiq::Stats.new
157
176
  end
158
177
 
159
178
  def redis_connection
160
179
  Sidekiq.redis do |conn|
161
- c = conn.connection
162
- "redis://#{c[:location]}/#{c[:db]}"
180
+ conn.connection[:id]
163
181
  end
164
182
  end
165
183
 
@@ -180,7 +198,7 @@ module Sidekiq
180
198
  end
181
199
 
182
200
  def current_status
183
- workers.size == 0 ? "idle" : "active"
201
+ workset.size == 0 ? "idle" : "active"
184
202
  end
185
203
 
186
204
  def relative_time(time)
@@ -197,7 +215,7 @@ module Sidekiq
197
215
  [score.to_f, jid]
198
216
  end
199
217
 
200
- SAFE_QPARAMS = %w[page poll direction]
218
+ SAFE_QPARAMS = %w[page direction]
201
219
 
202
220
  # Merge options with current params, filter safe params, and stringify to query string
203
221
  def qparams(options)
@@ -247,7 +265,7 @@ module Sidekiq
247
265
  queue class args retry_count retried_at failed_at
248
266
  jid error_message error_class backtrace
249
267
  error_backtrace enqueued_at retry wrapped
250
- created_at tags
268
+ created_at tags display_class
251
269
  ])
252
270
 
253
271
  def retry_extra_items(retry_job)
@@ -258,7 +276,21 @@ module Sidekiq
258
276
  end
259
277
  end
260
278
 
279
+ def format_memory(rss_kb)
280
+ return "0" if rss_kb.nil? || rss_kb == 0
281
+
282
+ if rss_kb < 100_000
283
+ "#{number_with_delimiter(rss_kb)} KB"
284
+ elsif rss_kb < 10_000_000
285
+ "#{number_with_delimiter((rss_kb / 1024.0).to_i)} MB"
286
+ else
287
+ "#{number_with_delimiter((rss_kb / (1024.0 * 1024.0)).round(1))} GB"
288
+ end
289
+ end
290
+
261
291
  def number_with_delimiter(number)
292
+ return "" if number.nil?
293
+
262
294
  begin
263
295
  Float(number)
264
296
  rescue ArgumentError, TypeError
@@ -292,7 +324,7 @@ module Sidekiq
292
324
  end
293
325
 
294
326
  def environment_title_prefix
295
- environment = Sidekiq.options[:environment] || ENV["APP_ENV"] || ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
327
+ environment = Sidekiq[:environment] || ENV["APP_ENV"] || ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
296
328
 
297
329
  "[#{environment.upcase}] " unless environment == "production"
298
330
  end
@@ -15,6 +15,10 @@ module Sidekiq
15
15
  REQUEST_METHOD = "REQUEST_METHOD"
16
16
  PATH_INFO = "PATH_INFO"
17
17
 
18
+ def head(path, &block)
19
+ route(HEAD, path, &block)
20
+ end
21
+
18
22
  def get(path, &block)
19
23
  route(GET, path, &block)
20
24
  end
@@ -39,7 +43,6 @@ module Sidekiq
39
43
  @routes ||= {GET => [], POST => [], PUT => [], PATCH => [], DELETE => [], HEAD => []}
40
44
 
41
45
  @routes[method] << WebRoute.new(method, path, block)
42
- @routes[HEAD] << WebRoute.new(method, path, block) if method == GET
43
46
  end
44
47
 
45
48
  def match(env)
data/lib/sidekiq/web.rb CHANGED
@@ -13,10 +13,8 @@ require "sidekiq/web/application"
13
13
  require "sidekiq/web/csrf_protection"
14
14
 
15
15
  require "rack/content_length"
16
-
17
16
  require "rack/builder"
18
- require "rack/file"
19
- require "rack/session/cookie"
17
+ require "rack/static"
20
18
 
21
19
  module Sidekiq
22
20
  class Web
@@ -35,19 +33,15 @@ module Sidekiq
35
33
  "Dead" => "morgue"
36
34
  }
37
35
 
36
+ if ENV["SIDEKIQ_METRICS_BETA"] == "1"
37
+ DEFAULT_TABS["Metrics"] = "metrics"
38
+ end
39
+
38
40
  class << self
39
41
  def settings
40
42
  self
41
43
  end
42
44
 
43
- def middlewares
44
- @middlewares ||= []
45
- end
46
-
47
- def use(*middleware_args, &block)
48
- middlewares << [middleware_args, block]
49
- end
50
-
51
45
  def default_tabs
52
46
  DEFAULT_TABS
53
47
  end
@@ -73,32 +67,45 @@ module Sidekiq
73
67
  opts.each { |key| set(key, false) }
74
68
  end
75
69
 
76
- # Helper for the Sinatra syntax: Sidekiq::Web.set(:session_secret, Rails.application.secrets...)
70
+ def middlewares
71
+ @middlewares ||= []
72
+ end
73
+
74
+ def use(*args, &block)
75
+ middlewares << [args, block]
76
+ end
77
+
77
78
  def set(attribute, value)
78
79
  send(:"#{attribute}=", value)
79
80
  end
80
81
 
81
- attr_accessor :app_url, :session_secret, :redis_pool, :sessions
82
+ def sessions=(val)
83
+ puts "WARNING: Sidekiq::Web.sessions= is no longer relevant and will be removed in Sidekiq 7.0. #{caller(1..1).first}"
84
+ end
85
+
86
+ def session_secret=(val)
87
+ puts "WARNING: Sidekiq::Web.session_secret= is no longer relevant and will be removed in Sidekiq 7.0. #{caller(1..1).first}"
88
+ end
89
+
90
+ attr_accessor :app_url, :redis_pool
82
91
  attr_writer :locales, :views
83
92
  end
84
93
 
85
94
  def self.inherited(child)
86
95
  child.app_url = app_url
87
- child.session_secret = session_secret
88
96
  child.redis_pool = redis_pool
89
- child.sessions = sessions
90
97
  end
91
98
 
92
99
  def settings
93
100
  self.class.settings
94
101
  end
95
102
 
96
- def use(*middleware_args, &block)
97
- middlewares << [middleware_args, block]
103
+ def middlewares
104
+ @middlewares ||= self.class.middlewares
98
105
  end
99
106
 
100
- def middlewares
101
- @middlewares ||= Web.middlewares.dup
107
+ def use(*args, &block)
108
+ middlewares << [args, block]
102
109
  end
103
110
 
104
111
  def call(env)
@@ -126,18 +133,8 @@ module Sidekiq
126
133
  send(:"#{attribute}=", value)
127
134
  end
128
135
 
129
- # Default values
130
- set :sessions, true
131
-
132
- attr_writer :sessions
133
-
134
- def sessions
135
- unless instance_variable_defined?("@sessions")
136
- @sessions = self.class.sessions
137
- @sessions = @sessions.to_hash.dup if @sessions.respond_to?(:to_hash)
138
- end
139
-
140
- @sessions
136
+ def sessions=(val)
137
+ puts "Sidekiq::Web#sessions= is no longer relevant and will be removed in Sidekiq 7.0. #{caller[2..2].first}"
141
138
  end
142
139
 
143
140
  def self.register(extension)
@@ -146,57 +143,20 @@ module Sidekiq
146
143
 
147
144
  private
148
145
 
149
- def using?(middleware)
150
- middlewares.any? do |(m, _)|
151
- m.is_a?(Array) && (m[0] == middleware || m[0].is_a?(middleware))
152
- end
153
- end
154
-
155
- def build_sessions
156
- middlewares = self.middlewares
157
-
158
- s = sessions
159
-
160
- # turn on CSRF protection if sessions are enabled and this is not the test env
161
- if s && !using?(CsrfProtection) && ENV["RACK_ENV"] != "test"
162
- middlewares.unshift [[CsrfProtection], nil]
163
- end
164
-
165
- if s && !using?(::Rack::Session::Cookie)
166
- unless (secret = Web.session_secret)
167
- require "securerandom"
168
- secret = SecureRandom.hex(64)
169
- end
170
-
171
- options = {secret: secret}
172
- options = options.merge(s.to_hash) if s.respond_to? :to_hash
173
-
174
- middlewares.unshift [[::Rack::Session::Cookie, options], nil]
175
- end
176
-
177
- # Since Sidekiq::WebApplication no longer calculates its own
178
- # Content-Length response header, we must ensure that the Rack middleware
179
- # that does this is loaded
180
- unless using? ::Rack::ContentLength
181
- middlewares.unshift [[::Rack::ContentLength], nil]
182
- end
183
- end
184
-
185
146
  def build
186
- build_sessions
187
-
188
- middlewares = self.middlewares
189
147
  klass = self.class
148
+ m = middlewares
190
149
 
191
- ::Rack::Builder.new do
192
- %w[stylesheets javascripts images].each do |asset_dir|
193
- map "/#{asset_dir}" do
194
- run ::Rack::File.new("#{ASSETS}/#{asset_dir}", {"Cache-Control" => "public, max-age=86400"})
195
- end
196
- end
197
-
198
- middlewares.each { |middleware, block| use(*middleware, &block) }
150
+ rules = []
151
+ rules = [[:all, {"cache-control" => "public, max-age=86400"}]] unless ENV["SIDEKIQ_WEB_TESTING"]
199
152
 
153
+ ::Rack::Builder.new do
154
+ use Rack::Static, urls: ["/stylesheets", "/images", "/javascripts"],
155
+ root: ASSETS,
156
+ cascade: true,
157
+ header_rules: rules
158
+ m.each { |middleware, block| use(*middleware, &block) }
159
+ use Sidekiq::Web::CsrfProtection unless $TESTING
200
160
  run WebApplication.new(klass)
201
161
  end
202
162
  end