sidekiq 6.1.1 → 6.5.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (118) hide show
  1. checksums.yaml +4 -4
  2. data/Changes.md +250 -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 +352 -156
  13. data/lib/sidekiq/cli.rb +86 -41
  14. data/lib/sidekiq/client.rb +49 -73
  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 +2 -2
  37. data/lib/sidekiq/paginator.rb +17 -9
  38. data/lib/sidekiq/processor.rb +47 -41
  39. data/lib/sidekiq/rails.rb +32 -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 +41 -16
  50. data/lib/sidekiq/web/csrf_protection.rb +32 -5
  51. data/lib/sidekiq/web/helpers.rb +52 -30
  52. data/lib/sidekiq/web/router.rb +4 -1
  53. data/lib/sidekiq/web.rb +38 -78
  54. data/lib/sidekiq/worker.rb +142 -16
  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 +114 -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 +57 -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 +52 -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.10"
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,16 +41,42 @@ 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
77
+ @count = (params["count"] || 100).to_i
78
+ (@current_page, @total_size, @workset) = page_items(workset, params["page"], @count)
79
+
55
80
  erb(:busy)
56
81
  end
57
82
 
@@ -76,15 +101,17 @@ module Sidekiq
76
101
  erb(:queues)
77
102
  end
78
103
 
104
+ QUEUE_NAME = /\A[a-z_:.\-0-9]+\z/i
105
+
79
106
  get "/queues/:name" do
80
107
  @name = route_params[:name]
81
108
 
82
- halt(404) unless @name
109
+ halt(404) if !@name || @name !~ QUEUE_NAME
83
110
 
84
111
  @count = (params["count"] || 25).to_i
85
112
  @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) }
113
+ (@current_page, @total_size, @jobs) = page("queue:#{@name}", params["page"], @count, reverse: params["direction"] == "asc")
114
+ @jobs = @jobs.map { |msg| Sidekiq::JobRecord.new(msg, @name) }
88
115
 
89
116
  erb(:queue)
90
117
  end
@@ -105,7 +132,7 @@ module Sidekiq
105
132
 
106
133
  post "/queues/:name/delete" do
107
134
  name = route_params[:name]
108
- Sidekiq::Job.new(params["key_val"], name).delete
135
+ Sidekiq::JobRecord.new(params["key_val"], name).delete
109
136
 
110
137
  redirect_with_query("#{root_path}queues/#{CGI.escape(name)}")
111
138
  end
@@ -288,37 +315,35 @@ module Sidekiq
288
315
 
289
316
  def call(env)
290
317
  action = self.class.match(env)
291
- return [404, {"Content-Type" => "text/plain", "X-Cascade" => "pass"}, ["Not Found"]] unless action
318
+ return [404, {"content-type" => "text/plain", "x-cascade" => "pass"}, ["Not Found"]] unless action
292
319
 
293
320
  app = @klass
294
- resp = catch(:halt) do # rubocop:disable Standard/SemanticBlocks
321
+ resp = catch(:halt) do
295
322
  self.class.run_befores(app, action)
296
323
  action.instance_exec env, &action.block
297
324
  ensure
298
325
  self.class.run_afters(app, action)
299
326
  end
300
327
 
301
- resp = case resp
328
+ case resp
302
329
  when Array
303
330
  # redirects go here
304
331
  resp
305
332
  else
306
333
  # rendered content goes here
307
334
  headers = {
308
- "Content-Type" => "text/html",
309
- "Cache-Control" => "no-cache",
310
- "Content-Language" => action.locale,
311
- "Content-Security-Policy" => CSP_HEADER
335
+ "content-type" => "text/html",
336
+ "cache-control" => "private, no-store",
337
+ "content-language" => action.locale,
338
+ "content-security-policy" => CSP_HEADER
312
339
  }
313
340
  # we'll let Rack calculate Content-Length for us.
314
341
  [200, headers, [resp]]
315
342
  end
316
-
317
- resp
318
343
  end
319
344
 
320
345
  def self.helpers(mod = nil, &block)
321
- if block_given?
346
+ if block
322
347
  WebAction.class_eval(&block)
323
348
  else
324
349
  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
 
@@ -141,25 +137,37 @@ module Sidekiq
141
137
  end
142
138
 
143
139
  def sort_direction_label
144
- params[:direction] == "asc" ? "&uarr;" : "&darr;"
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
152
+ def sorted_processes
153
+ @sorted_processes ||= begin
154
+ return processes unless processes.all? { |p| p["hostname"] }
155
+
156
+ processes.to_a.sort_by do |process|
157
+ # Kudos to `shurikk` on StackOverflow
158
+ # https://stackoverflow.com/a/15170063/575547
159
+ process["hostname"].split(/(\d+)/).map { |a| /\d+/.match?(a) ? a.to_i : a }
160
+ end
161
+ end
162
+ end
163
+
155
164
  def stats
156
165
  @stats ||= Sidekiq::Stats.new
157
166
  end
158
167
 
159
168
  def redis_connection
160
169
  Sidekiq.redis do |conn|
161
- c = conn.connection
162
- "redis://#{c[:location]}/#{c[:db]}"
170
+ conn.connection[:id]
163
171
  end
164
172
  end
165
173
 
@@ -180,7 +188,7 @@ module Sidekiq
180
188
  end
181
189
 
182
190
  def current_status
183
- workers.size == 0 ? "idle" : "active"
191
+ (workset.size == 0) ? "idle" : "active"
184
192
  end
185
193
 
186
194
  def relative_time(time)
@@ -197,7 +205,7 @@ module Sidekiq
197
205
  [score.to_f, jid]
198
206
  end
199
207
 
200
- SAFE_QPARAMS = %w[page poll direction]
208
+ SAFE_QPARAMS = %w[page direction]
201
209
 
202
210
  # Merge options with current params, filter safe params, and stringify to query string
203
211
  def qparams(options)
@@ -213,7 +221,7 @@ module Sidekiq
213
221
  end
214
222
 
215
223
  def truncate(text, truncate_after_chars = 2000)
216
- truncate_after_chars && text.size > truncate_after_chars ? "#{text[0..truncate_after_chars]}..." : text
224
+ (truncate_after_chars && text.size > truncate_after_chars) ? "#{text[0..truncate_after_chars]}..." : text
217
225
  end
218
226
 
219
227
  def display_args(args, truncate_after_chars = 2000)
@@ -247,7 +255,7 @@ module Sidekiq
247
255
  queue class args retry_count retried_at failed_at
248
256
  jid error_message error_class backtrace
249
257
  error_backtrace enqueued_at retry wrapped
250
- created_at tags
258
+ created_at tags display_class
251
259
  ])
252
260
 
253
261
  def retry_extra_items(retry_job)
@@ -258,7 +266,21 @@ module Sidekiq
258
266
  end
259
267
  end
260
268
 
269
+ def format_memory(rss_kb)
270
+ return "0" if rss_kb.nil? || rss_kb == 0
271
+
272
+ if rss_kb < 100_000
273
+ "#{number_with_delimiter(rss_kb)} KB"
274
+ elsif rss_kb < 10_000_000
275
+ "#{number_with_delimiter((rss_kb / 1024.0).to_i)} MB"
276
+ else
277
+ "#{number_with_delimiter((rss_kb / (1024.0 * 1024.0)).round(1))} GB"
278
+ end
279
+ end
280
+
261
281
  def number_with_delimiter(number)
282
+ return "" if number.nil?
283
+
262
284
  begin
263
285
  Float(number)
264
286
  rescue ArgumentError, TypeError
@@ -292,7 +314,7 @@ module Sidekiq
292
314
  end
293
315
 
294
316
  def environment_title_prefix
295
- environment = Sidekiq.options[:environment] || ENV["APP_ENV"] || ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
317
+ environment = Sidekiq[:environment] || ENV["APP_ENV"] || ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
296
318
 
297
319
  "[#{environment.upcase}] " unless environment == "production"
298
320
  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