rpush 4.2.0 → 7.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (154) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +307 -163
  3. data/README.md +105 -19
  4. data/lib/generators/templates/add_adm.rb +1 -1
  5. data/lib/generators/templates/add_alert_is_json_to_rapns_notifications.rb +2 -2
  6. data/lib/generators/templates/add_app_to_rapns.rb +2 -2
  7. data/lib/generators/templates/add_fail_after_to_rpush_notifications.rb +1 -1
  8. data/lib/generators/templates/add_gcm.rb +11 -25
  9. data/lib/generators/templates/add_rpush.rb +33 -83
  10. data/lib/generators/templates/add_wpns.rb +1 -1
  11. data/lib/generators/templates/create_rapns_apps.rb +1 -1
  12. data/lib/generators/templates/create_rapns_feedback.rb +3 -9
  13. data/lib/generators/templates/create_rapns_notifications.rb +3 -9
  14. data/lib/generators/templates/rename_rapns_to_rpush.rb +9 -33
  15. data/lib/generators/templates/rpush.rb +5 -4
  16. data/lib/generators/templates/rpush_2_0_0_updates.rb +10 -18
  17. data/lib/generators/templates/rpush_2_1_0_updates.rb +1 -1
  18. data/lib/generators/templates/rpush_2_6_0_updates.rb +1 -1
  19. data/lib/generators/templates/rpush_2_7_0_updates.rb +1 -1
  20. data/lib/generators/templates/rpush_3_0_0_updates.rb +1 -1
  21. data/lib/generators/templates/rpush_3_0_1_updates.rb +1 -1
  22. data/lib/generators/templates/rpush_3_1_0_add_pushy.rb +1 -1
  23. data/lib/generators/templates/rpush_3_1_1_updates.rb +1 -1
  24. data/lib/generators/templates/rpush_3_2_0_add_apns_p8.rb +1 -1
  25. data/lib/generators/templates/rpush_3_2_4_updates.rb +1 -1
  26. data/lib/generators/templates/rpush_3_3_0_updates.rb +1 -1
  27. data/lib/generators/templates/rpush_3_3_1_updates.rb +3 -3
  28. data/lib/generators/templates/rpush_4_1_0_updates.rb +1 -1
  29. data/lib/generators/templates/rpush_4_1_1_updates.rb +1 -1
  30. data/lib/generators/templates/rpush_4_2_0_updates.rb +1 -1
  31. data/lib/rpush/cli.rb +1 -1
  32. data/lib/rpush/client/active_model/adm/data_validator.rb +1 -1
  33. data/lib/rpush/client/active_model/apns/app.rb +1 -17
  34. data/lib/rpush/client/active_model/apns/device_token_format_validator.rb +2 -2
  35. data/lib/rpush/client/active_model/apns/notification.rb +13 -1
  36. data/lib/rpush/client/active_model/apns/notification_payload_size_validator.rb +15 -0
  37. data/lib/rpush/client/active_model/apns2/app.rb +7 -1
  38. data/lib/rpush/client/active_model/apns2/notification.rb +14 -0
  39. data/lib/rpush/client/active_model/certificate_private_key_validator.rb +19 -0
  40. data/lib/rpush/client/active_model/gcm/expiry_collapse_key_mutual_inclusion_validator.rb +1 -1
  41. data/lib/rpush/client/active_model/gcm/notification.rb +2 -2
  42. data/lib/rpush/client/active_model/payload_data_size_validator.rb +1 -1
  43. data/lib/rpush/client/active_model/registration_ids_count_validator.rb +1 -1
  44. data/lib/rpush/client/active_model/webpush/app.rb +41 -0
  45. data/lib/rpush/client/active_model/webpush/notification.rb +66 -0
  46. data/lib/rpush/client/active_model.rb +5 -1
  47. data/lib/rpush/client/active_record/apns/active_record_serializable_notification.rb +65 -0
  48. data/lib/rpush/client/active_record/apns/notification.rb +1 -57
  49. data/lib/rpush/client/active_record/apns2/notification.rb +4 -1
  50. data/lib/rpush/client/active_record/apnsp8/notification.rb +1 -0
  51. data/lib/rpush/client/active_record/webpush/app.rb +11 -0
  52. data/lib/rpush/client/active_record/webpush/notification.rb +12 -0
  53. data/lib/rpush/client/active_record.rb +4 -0
  54. data/lib/rpush/client/redis/apns2/notification.rb +1 -0
  55. data/lib/rpush/client/redis/apnsp8/notification.rb +2 -0
  56. data/lib/rpush/client/redis/pushy/notification.rb +0 -1
  57. data/lib/rpush/client/redis/webpush/app.rb +15 -0
  58. data/lib/rpush/client/redis/webpush/notification.rb +15 -0
  59. data/lib/rpush/client/redis.rb +3 -0
  60. data/lib/rpush/configuration.rb +3 -2
  61. data/lib/rpush/daemon/apns/feedback_receiver.rb +1 -1
  62. data/lib/rpush/daemon/apns2/delivery.rb +14 -2
  63. data/lib/rpush/daemon/apnsp8/delivery.rb +14 -3
  64. data/lib/rpush/daemon/app_runner.rb +1 -1
  65. data/lib/rpush/daemon/batch.rb +12 -5
  66. data/lib/rpush/daemon/delivery.rb +1 -2
  67. data/lib/rpush/daemon/store/active_record/reconnectable.rb +1 -1
  68. data/lib/rpush/daemon/store/active_record.rb +11 -7
  69. data/lib/rpush/daemon/store/redis.rb +6 -6
  70. data/lib/rpush/daemon/string_helpers.rb +1 -1
  71. data/lib/rpush/daemon/webpush/delivery.rb +114 -0
  72. data/lib/rpush/daemon/webpush.rb +10 -0
  73. data/lib/rpush/daemon.rb +4 -1
  74. data/lib/rpush/logger.rb +2 -1
  75. data/lib/rpush/version.rb +3 -3
  76. data/spec/functional/apns2_spec.rb +99 -2
  77. data/spec/functional/gcm_priority_spec.rb +40 -0
  78. data/spec/functional/retry_spec.rb +1 -1
  79. data/spec/functional/webpush_spec.rb +31 -0
  80. data/spec/spec_helper.rb +3 -1
  81. data/spec/support/active_record_setup.rb +4 -3
  82. data/spec/support/config/database.yml +4 -4
  83. data/spec/support/simplecov_helper.rb +2 -2
  84. data/spec/unit/client/active_record/adm/app_spec.rb +2 -54
  85. data/spec/unit/client/active_record/adm/notification_spec.rb +2 -39
  86. data/spec/unit/client/active_record/apns/app_spec.rb +3 -26
  87. data/spec/unit/client/active_record/apns/feedback_spec.rb +1 -5
  88. data/spec/unit/client/active_record/apns/notification_spec.rb +29 -293
  89. data/spec/unit/client/active_record/apns2/app_spec.rb +5 -0
  90. data/spec/unit/client/active_record/apns2/notification_spec.rb +65 -0
  91. data/spec/unit/client/active_record/apnsp8/notification_spec.rb +28 -0
  92. data/spec/unit/client/active_record/app_spec.rb +1 -26
  93. data/spec/unit/client/active_record/gcm/app_spec.rb +3 -1
  94. data/spec/unit/client/active_record/gcm/notification_spec.rb +6 -88
  95. data/spec/unit/client/active_record/notification_spec.rb +3 -11
  96. data/spec/unit/client/active_record/pushy/app_spec.rb +2 -13
  97. data/spec/unit/client/active_record/pushy/notification_spec.rb +2 -55
  98. data/spec/unit/client/active_record/shared/app.rb +14 -0
  99. data/spec/unit/{notification_shared.rb → client/active_record/shared/notification.rb} +12 -7
  100. data/spec/unit/client/active_record/webpush/app_spec.rb +6 -0
  101. data/spec/unit/client/active_record/webpush/notification_spec.rb +6 -0
  102. data/spec/unit/client/active_record/wns/badge_notification_spec.rb +1 -11
  103. data/spec/unit/client/active_record/wns/raw_notification_spec.rb +3 -12
  104. data/spec/unit/client/active_record/wpns/app_spec.rb +3 -1
  105. data/spec/unit/client/active_record/wpns/notification_spec.rb +2 -17
  106. data/spec/unit/client/redis/adm/app_spec.rb +5 -0
  107. data/spec/unit/client/redis/adm/notification_spec.rb +5 -0
  108. data/spec/unit/client/redis/apns/app_spec.rb +5 -0
  109. data/spec/unit/client/redis/apns/feedback_spec.rb +5 -0
  110. data/spec/unit/client/redis/apns/notification_spec.rb +50 -0
  111. data/spec/unit/client/redis/apns2/app_spec.rb +4 -0
  112. data/spec/unit/client/redis/apns2/notification_spec.rb +50 -0
  113. data/spec/unit/client/redis/apnsp8/notification_spec.rb +29 -0
  114. data/spec/unit/client/redis/app_spec.rb +5 -0
  115. data/spec/unit/client/redis/gcm/app_spec.rb +5 -0
  116. data/spec/unit/client/redis/gcm/notification_spec.rb +5 -0
  117. data/spec/unit/client/redis/notification_spec.rb +5 -0
  118. data/spec/unit/client/redis/pushy/app_spec.rb +5 -0
  119. data/spec/unit/client/redis/pushy/notification_spec.rb +5 -0
  120. data/spec/unit/client/redis/webpush/app_spec.rb +5 -0
  121. data/spec/unit/client/redis/webpush/notification_spec.rb +5 -0
  122. data/spec/unit/client/redis/wns/badge_notification_spec.rb +5 -0
  123. data/spec/unit/client/redis/wns/raw_notification_spec.rb +22 -0
  124. data/spec/unit/client/redis/wpns/app_spec.rb +5 -0
  125. data/spec/unit/client/redis/wpns/notification_spec.rb +5 -0
  126. data/spec/unit/client/shared/adm/app.rb +51 -0
  127. data/spec/unit/client/shared/adm/notification.rb +39 -0
  128. data/spec/unit/client/shared/apns/app.rb +29 -0
  129. data/spec/unit/client/shared/apns/feedback.rb +9 -0
  130. data/spec/unit/client/shared/apns/notification.rb +277 -0
  131. data/spec/unit/client/shared/app.rb +17 -0
  132. data/spec/unit/client/shared/gcm/app.rb +4 -0
  133. data/spec/unit/client/shared/gcm/notification.rb +77 -0
  134. data/spec/unit/client/shared/notification.rb +10 -0
  135. data/spec/unit/client/shared/pushy/app.rb +17 -0
  136. data/spec/unit/client/shared/pushy/notification.rb +55 -0
  137. data/spec/unit/client/shared/webpush/app.rb +33 -0
  138. data/spec/unit/client/shared/webpush/notification.rb +83 -0
  139. data/spec/unit/client/shared/wns/badge_notification.rb +15 -0
  140. data/spec/unit/client/shared/wns/raw_notification.rb +21 -0
  141. data/spec/unit/client/shared/wpns/app.rb +4 -0
  142. data/spec/unit/client/shared/wpns/notification.rb +18 -0
  143. data/spec/unit/daemon/apnsp8/delivery_spec.rb +53 -0
  144. data/spec/unit/daemon/batch_spec.rb +50 -2
  145. data/spec/unit/daemon/delivery_spec.rb +10 -0
  146. data/spec/unit/daemon/pushy/delivery_spec.rb +5 -3
  147. data/spec/unit/daemon/shared/store.rb +312 -0
  148. data/spec/unit/daemon/store/active_record/reconnectable_spec.rb +7 -7
  149. data/spec/unit/daemon/store/active_record_spec.rb +6 -287
  150. data/spec/unit/daemon/store/redis_spec.rb +2 -291
  151. data/spec/unit/daemon/webpush/delivery_spec.rb +144 -0
  152. data/spec/unit_spec_helper.rb +3 -0
  153. metadata +145 -18
  154. data/lib/rpush/client/active_model/apns/binary_notification_validator.rb +0 -16
@@ -0,0 +1,65 @@
1
+ module Rpush
2
+ module Client
3
+ module ActiveRecord
4
+ module Apns
5
+ module ActiveRecordSerializableNotification
6
+ def alert=(alert)
7
+ if alert.is_a?(Hash)
8
+ write_attribute(:alert, multi_json_dump(alert))
9
+ self.alert_is_json = true if has_attribute?(:alert_is_json)
10
+ else
11
+ write_attribute(:alert, alert)
12
+ self.alert_is_json = false if has_attribute?(:alert_is_json)
13
+ end
14
+ end
15
+
16
+ def alert
17
+ string_or_json = read_attribute(:alert)
18
+
19
+ if has_attribute?(:alert_is_json)
20
+ if alert_is_json?
21
+ multi_json_load(string_or_json)
22
+ else
23
+ string_or_json
24
+ end
25
+ else
26
+ begin
27
+ multi_json_load(string_or_json)
28
+ rescue StandardError
29
+ string_or_json
30
+ end
31
+ end
32
+ end
33
+
34
+ def sound=(sound)
35
+ if sound.is_a?(Hash)
36
+ write_attribute(:sound, multi_json_dump(sound))
37
+ self.sound_is_json = true if has_attribute?(:sound_is_json)
38
+ else
39
+ write_attribute(:sound, sound)
40
+ self.sound_is_json = false if has_attribute?(:sound_is_json)
41
+ end
42
+ end
43
+
44
+ def sound
45
+ string_or_json = read_attribute(:sound)
46
+
47
+ if has_attribute?(:sound_is_json)
48
+ if sound_is_json?
49
+ multi_json_load(string_or_json)
50
+ else
51
+ string_or_json
52
+ end
53
+ else
54
+ begin
55
+ multi_json_load(string_or_json)
56
+ rescue StandardError
57
+ string_or_json
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -3,64 +3,8 @@ module Rpush
3
3
  module ActiveRecord
4
4
  module Apns
5
5
  class Notification < Rpush::Client::ActiveRecord::Notification
6
- include Deprecatable
7
6
  include Rpush::Client::ActiveModel::Apns::Notification
8
-
9
- def alert=(alert)
10
- if alert.is_a?(Hash)
11
- write_attribute(:alert, multi_json_dump(alert))
12
- self.alert_is_json = true if has_attribute?(:alert_is_json)
13
- else
14
- write_attribute(:alert, alert)
15
- self.alert_is_json = false if has_attribute?(:alert_is_json)
16
- end
17
- end
18
-
19
- def alert
20
- string_or_json = read_attribute(:alert)
21
-
22
- if has_attribute?(:alert_is_json)
23
- if alert_is_json?
24
- multi_json_load(string_or_json)
25
- else
26
- string_or_json
27
- end
28
- else
29
- begin
30
- multi_json_load(string_or_json)
31
- rescue StandardError
32
- string_or_json
33
- end
34
- end
35
- end
36
-
37
- def sound=(sound)
38
- if sound.is_a?(Hash)
39
- write_attribute(:sound, multi_json_dump(sound))
40
- self.sound_is_json = true if has_attribute?(:sound_is_json)
41
- else
42
- write_attribute(:sound, sound)
43
- self.sound_is_json = false if has_attribute?(:sound_is_json)
44
- end
45
- end
46
-
47
- def sound
48
- string_or_json = read_attribute(:sound)
49
-
50
- if has_attribute?(:sound_is_json)
51
- if sound_is_json?
52
- multi_json_load(string_or_json)
53
- else
54
- string_or_json
55
- end
56
- else
57
- begin
58
- multi_json_load(string_or_json)
59
- rescue StandardError
60
- string_or_json
61
- end
62
- end
63
- end
7
+ include ActiveRecordSerializableNotification
64
8
  end
65
9
  end
66
10
  end
@@ -2,7 +2,10 @@ module Rpush
2
2
  module Client
3
3
  module ActiveRecord
4
4
  module Apns2
5
- class Notification < Rpush::Client::ActiveRecord::Apns::Notification
5
+ class Notification < Rpush::Client::ActiveRecord::Notification
6
+ include Rpush::Client::ActiveModel::Apns::Notification
7
+ include Rpush::Client::ActiveModel::Apns2::Notification
8
+ include Rpush::Client::ActiveRecord::Apns::ActiveRecordSerializableNotification
6
9
  end
7
10
  end
8
11
  end
@@ -3,6 +3,7 @@ module Rpush
3
3
  module ActiveRecord
4
4
  module Apnsp8
5
5
  class Notification < Rpush::Client::ActiveRecord::Apns::Notification
6
+ include Rpush::Client::ActiveModel::Apns2::Notification
6
7
  end
7
8
  end
8
9
  end
@@ -0,0 +1,11 @@
1
+ module Rpush
2
+ module Client
3
+ module ActiveRecord
4
+ module Webpush
5
+ class App < Rpush::Client::ActiveRecord::App
6
+ include Rpush::Client::ActiveModel::Webpush::App
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,12 @@
1
+ module Rpush
2
+ module Client
3
+ module ActiveRecord
4
+ module Webpush
5
+ class Notification < Rpush::Client::ActiveRecord::Notification
6
+ include Rpush::Client::ActiveModel::Webpush::Notification
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
12
+
@@ -5,6 +5,7 @@ require 'rpush/client/active_model'
5
5
  require 'rpush/client/active_record/notification'
6
6
  require 'rpush/client/active_record/app'
7
7
 
8
+ require 'rpush/client/active_record/apns/active_record_serializable_notification'
8
9
  require 'rpush/client/active_record/apns/notification'
9
10
  require 'rpush/client/active_record/apns/feedback'
10
11
  require 'rpush/client/active_record/apns/app'
@@ -31,3 +32,6 @@ require 'rpush/client/active_record/adm/app'
31
32
 
32
33
  require 'rpush/client/active_record/pushy/notification'
33
34
  require 'rpush/client/active_record/pushy/app'
35
+
36
+ require 'rpush/client/active_record/webpush/notification'
37
+ require 'rpush/client/active_record/webpush/app'
@@ -3,6 +3,7 @@ module Rpush
3
3
  module Redis
4
4
  module Apns2
5
5
  class Notification < Rpush::Client::Redis::Notification
6
+ include Rpush::Client::ActiveModel::Apns::Notification
6
7
  include Rpush::Client::ActiveModel::Apns2::Notification
7
8
  end
8
9
  end
@@ -3,6 +3,8 @@ module Rpush
3
3
  module Redis
4
4
  module Apnsp8
5
5
  class Notification < Rpush::Client::Redis::Notification
6
+ include Rpush::Client::ActiveModel::Apns::Notification
7
+ include Rpush::Client::ActiveModel::Apns2::Notification
6
8
  include Rpush::Client::ActiveModel::Apnsp8::Notification
7
9
  end
8
10
  end
@@ -9,7 +9,6 @@ module Rpush
9
9
 
10
10
  def time_to_live=(value)
11
11
  self.expiry = value
12
- super
13
12
  end
14
13
  end
15
14
  end
@@ -0,0 +1,15 @@
1
+ module Rpush
2
+ module Client
3
+ module Redis
4
+ module Webpush
5
+ class App < Rpush::Client::Redis::App
6
+ include Rpush::Client::ActiveModel::Webpush::App
7
+
8
+ def vapid_keypair=(value)
9
+ self.certificate = value
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ module Rpush
2
+ module Client
3
+ module Redis
4
+ module Webpush
5
+ class Notification < Rpush::Client::Redis::Notification
6
+ include Rpush::Client::ActiveModel::Webpush::Notification
7
+
8
+ def time_to_live=(value)
9
+ self.expiry = value
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -44,6 +44,9 @@ require 'rpush/client/redis/wns/badge_notification'
44
44
  require 'rpush/client/redis/pushy/app'
45
45
  require 'rpush/client/redis/pushy/notification'
46
46
 
47
+ require 'rpush/client/redis/webpush/app'
48
+ require 'rpush/client/redis/webpush/notification'
49
+
47
50
  Modis.configure do |config|
48
51
  config.namespace = :rpush
49
52
  end
@@ -16,7 +16,7 @@ module Rpush
16
16
  end
17
17
  end
18
18
 
19
- CURRENT_ATTRS = [:push_poll, :embedded, :pid_file, :batch_size, :push, :client, :logger, :log_file, :foreground, :log_level, :plugin, :apns]
19
+ CURRENT_ATTRS = [:push_poll, :embedded, :pid_file, :batch_size, :push, :client, :logger, :log_file, :foreground, :foreground_logging, :log_level, :plugin, :apns]
20
20
  DEPRECATED_ATTRS = []
21
21
  CONFIG_ATTRS = CURRENT_ATTRS + DEPRECATED_ATTRS
22
22
 
@@ -53,6 +53,7 @@ module Rpush
53
53
  self.log_level = (defined?(Rails) && Rails.logger) ? Rails.logger.level : ::Logger::Severity::DEBUG
54
54
  self.plugin = OpenStruct.new
55
55
  self.foreground = false
56
+ self.foreground_logging = true
56
57
 
57
58
  self.apns = ApnsConfiguration.new
58
59
 
@@ -105,7 +106,7 @@ module Rpush
105
106
  client_module = Rpush::Client.const_get(client.to_s.camelize)
106
107
  Rpush.send(:include, client_module) unless Rpush.ancestors.include?(client_module)
107
108
 
108
- [:Apns, :Gcm, :Wpns, :Wns, :Adm, :Pushy].each do |service|
109
+ [:Apns, :Gcm, :Wpns, :Wns, :Adm, :Pushy, :Webpush].each do |service|
109
110
  Rpush.const_set(service, client_module.const_get(service)) unless Rpush.const_defined?(service)
110
111
  end
111
112
 
@@ -37,7 +37,7 @@ module Rpush
37
37
  Rpush::Daemon.store.release_connection
38
38
  end
39
39
 
40
- puts Rainbow('✔').green if Rpush.config.foreground
40
+ puts Rainbow('✔').green if Rpush.config.foreground && Rpush.config.foreground_logging
41
41
  end
42
42
 
43
43
  def stop
@@ -7,6 +7,7 @@ module Rpush
7
7
 
8
8
  class Delivery < Rpush::Daemon::Delivery
9
9
  RETRYABLE_CODES = [ 429, 500, 503 ]
10
+ CLIENT_JOIN_TIMEOUT = 60
10
11
 
11
12
  def initialize(app, http2_client, batch)
12
13
  @app = app
@@ -20,7 +21,11 @@ module Rpush
20
21
  end
21
22
 
22
23
  # Send all preprocessed requests at once
23
- @client.join
24
+ @client.join(timeout: CLIENT_JOIN_TIMEOUT)
25
+ rescue NetHttp2::AsyncRequestTimeout => error
26
+ mark_batch_retryable(Time.now + 10.seconds, error)
27
+ @client.close
28
+ raise
24
29
  rescue Errno::ECONNREFUSED, SocketError => error
25
30
  mark_batch_retryable(Time.now + 10.seconds, error)
26
31
  raise
@@ -102,7 +107,14 @@ module Rpush
102
107
  end
103
108
 
104
109
  def prepare_headers(notification)
105
- notification_data(notification)[HTTP2_HEADERS_KEY] || {}
110
+ headers = {}
111
+
112
+ headers['apns-expiration'] = '0'
113
+ headers['apns-priority'] = '10'
114
+ headers['apns-topic'] = @app.bundle_id
115
+ headers['apns-push-type'] = 'background' if notification.content_available?
116
+
117
+ headers.merge notification_data(notification)[HTTP2_HEADERS_KEY] || {}
106
118
  end
107
119
 
108
120
  def notification_data(notification)
@@ -7,6 +7,8 @@ module Rpush
7
7
 
8
8
  class Delivery < Rpush::Daemon::Delivery
9
9
  RETRYABLE_CODES = [ 429, 500, 503 ]
10
+ CLIENT_JOIN_TIMEOUT = 60
11
+ DEFAULT_MAX_CONCURRENT_STREAMS = 100
10
12
 
11
13
  def initialize(app, http2_client, token_provider, batch)
12
14
  @app = app
@@ -22,7 +24,11 @@ module Rpush
22
24
  end
23
25
 
24
26
  # Send all preprocessed requests at once
25
- @client.join
27
+ @client.join(timeout: CLIENT_JOIN_TIMEOUT)
28
+ rescue NetHttp2::AsyncRequestTimeout => error
29
+ mark_batch_retryable(Time.now + 10.seconds, error)
30
+ @client.close
31
+ raise
26
32
  rescue Errno::ECONNREFUSED, SocketError, HTTP2::Error::StreamLimitExceeded => error
27
33
  # TODO restart connection when StreamLimitExceeded
28
34
  mark_batch_retryable(Time.now + 10.seconds, error)
@@ -80,7 +86,11 @@ module Rpush
80
86
  def remote_max_concurrent_streams
81
87
  # 0x7fffffff is the default value from http-2 gem (2^31)
82
88
  if @client.remote_settings[:settings_max_concurrent_streams] == 0x7fffffff
83
- 0
89
+ # Ideally we'd fall back to `#local_settings` here, but `NetHttp2::Client`
90
+ # doesn't expose that attr from the `HTTP2::Client` it wraps. Instead, we
91
+ # chose a hard-coded value matching the default local setting from the
92
+ # `HTTP2::Client` class
93
+ DEFAULT_MAX_CONCURRENT_STREAMS
84
94
  else
85
95
  @client.remote_settings[:settings_max_concurrent_streams]
86
96
  end
@@ -133,12 +143,13 @@ module Rpush
133
143
  jwt_token = @token_provider.token
134
144
 
135
145
  headers = {}
136
-
146
+
137
147
  headers['content-type'] = 'application/json'
138
148
  headers['apns-expiration'] = '0'
139
149
  headers['apns-priority'] = '10'
140
150
  headers['apns-topic'] = @app.bundle_id
141
151
  headers['authorization'] = "bearer #{jwt_token}"
152
+ headers['apns-push-type'] = 'background' if notification.content_available?
142
153
 
143
154
  headers.merge notification_data(notification)[HTTP2_HEADERS_KEY] || {}
144
155
  end
@@ -29,7 +29,7 @@ module Rpush
29
29
  Rpush.logger.info("[#{app.name}] Starting #{pluralize(app.connections, 'dispatcher')}... ", true)
30
30
  runner = @runners[app.id] = new(app)
31
31
  runner.start_dispatchers
32
- puts Rainbow('✔').green if Rpush.config.foreground
32
+ puts Rainbow('✔').green if Rpush.config.foreground && Rpush.config.foreground_logging
33
33
  runner.start_loops
34
34
  rescue StandardError => e
35
35
  @runners.delete(app.id)
@@ -2,6 +2,7 @@ module Rpush
2
2
  module Daemon
3
3
  class Batch
4
4
  include Reflectable
5
+ include Loggable
5
6
 
6
7
  attr_reader :num_processed, :notifications, :delivered, :failed, :retryable
7
8
 
@@ -31,16 +32,21 @@ module Rpush
31
32
  @retryable[deliver_after] ||= []
32
33
  @retryable[deliver_after] << notification
33
34
  end
35
+
34
36
  Rpush::Daemon.store.mark_retryable(notification, deliver_after, persist: false)
35
37
  end
36
38
 
37
- def mark_all_retryable(deliver_after)
38
- @mutex.synchronize do
39
- @retryable[deliver_after] = @notifications
40
- end
39
+ def mark_all_retryable(deliver_after, error)
40
+ retryable_count = 0
41
+
41
42
  each_notification do |notification|
42
- Rpush::Daemon.store.mark_retryable(notification, deliver_after, persist: false)
43
+ next if notification.delivered || notification.failed
44
+
45
+ retryable_count += 1
46
+ mark_retryable(notification, deliver_after)
43
47
  end
48
+
49
+ log_warn("Will retry #{retryable_count} of #{@notifications.size} notifications after #{deliver_after.strftime('%Y-%m-%d %H:%M:%S')} due to error (#{error.class.name}, #{error.message})")
44
50
  end
45
51
 
46
52
  def mark_delivered(notification)
@@ -54,6 +60,7 @@ module Rpush
54
60
  @mutex.synchronize do
55
61
  @delivered = @notifications
56
62
  end
63
+
57
64
  each_notification do |notification|
58
65
  Rpush::Daemon.store.mark_delivered(notification, Time.now, persist: false)
59
66
  end
@@ -20,8 +20,7 @@ module Rpush
20
20
  end
21
21
 
22
22
  def mark_batch_retryable(deliver_after, error)
23
- log_warn("Will retry #{@batch.notifications.size} notifications after #{deliver_after.strftime('%Y-%m-%d %H:%M:%S')} due to error (#{error.class.name}, #{error.message})")
24
- @batch.mark_all_retryable(deliver_after)
23
+ @batch.mark_all_retryable(deliver_after, error)
25
24
  end
26
25
 
27
26
  def mark_delivered
@@ -67,7 +67,7 @@ module Rpush
67
67
 
68
68
  def check_database_is_connected
69
69
  # Simply asking the adapter for the connection state is not sufficient.
70
- Rpush::Client::ActiveRecord::Notification.count
70
+ Rpush::Client::ActiveRecord::Notification.exists?
71
71
  end
72
72
 
73
73
  def sleep_to_avoid_thrashing
@@ -181,6 +181,17 @@ module Rpush
181
181
  id
182
182
  end
183
183
 
184
+ def adapter_name
185
+ env = (defined?(Rails) && Rails.env) ? Rails.env : 'development'
186
+ if ::ActiveRecord::VERSION::MAJOR > 6
187
+ ::ActiveRecord::Base.configurations.configs_for(env_name: env).first.configuration_hash[:adapter]
188
+ else
189
+ config = ::ActiveRecord::Base.configurations[env]
190
+ return '' unless config
191
+ Hash[config.map { |k, v| [k.to_sym, v] }][:adapter]
192
+ end
193
+ end
194
+
184
195
  private
185
196
 
186
197
  def create_gcm_like_notification(notification, attrs, data, registration_ids, deliver_after, app) # rubocop:disable Metrics/ParameterLists
@@ -199,13 +210,6 @@ module Rpush
199
210
  relation = Rpush::Client::ActiveRecord::Notification.where('processing = ? AND delivered = ? AND failed = ? AND (deliver_after IS NULL OR deliver_after < ?)', false, false, false, Time.now)
200
211
  relation.order('deliver_after ASC, created_at ASC')
201
212
  end
202
-
203
- def adapter_name
204
- env = (defined?(Rails) && Rails.env) ? Rails.env : 'development'
205
- config = ::ActiveRecord::Base.configurations[env]
206
- return '' unless config
207
- Hash[config.map { |k, v| [k.to_sym, v] }][:adapter]
208
- end
209
213
  end
210
214
  end
211
215
  end
@@ -152,10 +152,10 @@ module Rpush
152
152
  retryable_ns = Rpush::Client::Redis::Notification.absolute_retryable_namespace
153
153
 
154
154
  Modis.with_connection do |redis|
155
- retryable_results = redis.multi do
155
+ retryable_results = redis.multi do |transaction|
156
156
  now = Time.now.to_i
157
- redis.zrangebyscore(retryable_ns, 0, now)
158
- redis.zremrangebyscore(retryable_ns, 0, now)
157
+ transaction.zrangebyscore(retryable_ns, 0, now)
158
+ transaction.zremrangebyscore(retryable_ns, 0, now)
159
159
  end
160
160
 
161
161
  retryable_results.first
@@ -167,9 +167,9 @@ module Rpush
167
167
  pending_ns = Rpush::Client::Redis::Notification.absolute_pending_namespace
168
168
 
169
169
  Modis.with_connection do |redis|
170
- pending_results = redis.multi do
171
- redis.zrange(pending_ns, 0, limit)
172
- redis.zremrangebyrank(pending_ns, 0, limit)
170
+ pending_results = redis.multi do |transaction|
171
+ transaction.zrange(pending_ns, 0, limit)
172
+ transaction.zremrangebyrank(pending_ns, 0, limit)
173
173
  end
174
174
 
175
175
  pending_results.first
@@ -2,7 +2,7 @@ module Rpush
2
2
  module Daemon
3
3
  module StringHelpers
4
4
  def pluralize(count, singular, plural = nil)
5
- if count == 1 || count =~ /^1(\.0+)?$/
5
+ if count == 1
6
6
  word = singular
7
7
  else
8
8
  word = plural || singular.pluralize
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "webpush"
4
+
5
+ module Rpush
6
+ module Daemon
7
+ module Webpush
8
+
9
+ # Webpush::Request handles all the encryption / signing.
10
+ # We just override #perform to inject the http instance that is managed
11
+ # by Rpush.
12
+ #
13
+ class Request < ::Webpush::Request
14
+ def perform(http)
15
+ req = Net::HTTP::Post.new(uri.request_uri, headers)
16
+ req.body = body
17
+ http.request(uri, req)
18
+ end
19
+ end
20
+
21
+ class Delivery < Rpush::Daemon::Delivery
22
+
23
+ OK = [ 200, 201, 202 ].freeze
24
+ TEMPORARY_FAILURES = [ 429, 500, 502, 503, 504 ].freeze
25
+
26
+ def initialize(app, http, notification, batch)
27
+ @app = app
28
+ @http = http
29
+ @notification = notification
30
+ @batch = batch
31
+ end
32
+
33
+ def perform
34
+ response = send_request
35
+ process_response response
36
+ rescue SocketError, SystemCallError => error
37
+ mark_retryable(@notification, Time.now + 10.seconds, error)
38
+ raise
39
+ rescue StandardError => error
40
+ mark_failed(error)
41
+ raise
42
+ ensure
43
+ @batch.notification_processed
44
+ end
45
+
46
+ private
47
+
48
+ def send_request
49
+ # The initializer is inherited from Webpush::Request and looks like
50
+ # this:
51
+ #
52
+ # initialize(message: '', subscription:, vapid:, **options)
53
+ #
54
+ # where subscription is a hash of :endpoint and :keys, and vapid
55
+ # holds the vapid public and private keys and the :subject (which is
56
+ # an email address).
57
+ Request.new(
58
+ message: @notification.message,
59
+ subscription: @notification.subscription,
60
+ vapid: @app.vapid,
61
+ ttl: @notification.time_to_live,
62
+ urgency: @notification.urgency
63
+ ).perform(@http)
64
+ end
65
+
66
+ def process_response(response)
67
+ case response.code.to_i
68
+ when *OK
69
+ mark_delivered
70
+ when *TEMPORARY_FAILURES
71
+ retry_delivery(response)
72
+ else
73
+ fail_delivery(response)
74
+ end
75
+ end
76
+
77
+ def retry_delivery(response)
78
+ time = deliver_after_header(response)
79
+ if time
80
+ mark_retryable(@notification, time)
81
+ else
82
+ mark_retryable_exponential(@notification)
83
+ end
84
+ log_info("Webpush endpoint responded with a #{response.code} error. #{retry_message}")
85
+ end
86
+
87
+ def fail_delivery(response)
88
+ fail_message = fail_message(response)
89
+ log_error("#{@notification.id} failed: #{fail_message}")
90
+ fail Rpush::DeliveryError.new(response.code.to_i, @notification.id, fail_message)
91
+ end
92
+
93
+ def deliver_after_header(response)
94
+ Rpush::Daemon::RetryHeaderParser.parse(response.header['retry-after'])
95
+ end
96
+
97
+ def retry_message
98
+ deliver_after = @notification.deliver_after.strftime('%Y-%m-%d %H:%M:%S')
99
+ "Notification #{@notification.id} will be retried after #{deliver_after} (retry #{@notification.retries})."
100
+ end
101
+
102
+ def fail_message(response)
103
+ msg = Rpush::Daemon::HTTP_STATUS_CODES[response.code.to_i]
104
+ if explanation = response.body.to_s[0..200].presence
105
+ msg += ": #{explanation}"
106
+ end
107
+ msg
108
+ end
109
+
110
+ end
111
+ end
112
+ end
113
+ end
114
+
@@ -0,0 +1,10 @@
1
+ module Rpush
2
+ module Daemon
3
+ module Webpush
4
+ extend ServiceConfigMethods
5
+
6
+ dispatcher :http
7
+ end
8
+ end
9
+ end
10
+