rpush 4.2.0 → 7.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +307 -163
- data/README.md +105 -19
- data/lib/generators/templates/add_adm.rb +1 -1
- data/lib/generators/templates/add_alert_is_json_to_rapns_notifications.rb +2 -2
- data/lib/generators/templates/add_app_to_rapns.rb +2 -2
- data/lib/generators/templates/add_fail_after_to_rpush_notifications.rb +1 -1
- data/lib/generators/templates/add_gcm.rb +11 -25
- data/lib/generators/templates/add_rpush.rb +33 -83
- data/lib/generators/templates/add_wpns.rb +1 -1
- data/lib/generators/templates/create_rapns_apps.rb +1 -1
- data/lib/generators/templates/create_rapns_feedback.rb +3 -9
- data/lib/generators/templates/create_rapns_notifications.rb +3 -9
- data/lib/generators/templates/rename_rapns_to_rpush.rb +9 -33
- data/lib/generators/templates/rpush.rb +5 -4
- data/lib/generators/templates/rpush_2_0_0_updates.rb +10 -18
- data/lib/generators/templates/rpush_2_1_0_updates.rb +1 -1
- data/lib/generators/templates/rpush_2_6_0_updates.rb +1 -1
- data/lib/generators/templates/rpush_2_7_0_updates.rb +1 -1
- data/lib/generators/templates/rpush_3_0_0_updates.rb +1 -1
- data/lib/generators/templates/rpush_3_0_1_updates.rb +1 -1
- data/lib/generators/templates/rpush_3_1_0_add_pushy.rb +1 -1
- data/lib/generators/templates/rpush_3_1_1_updates.rb +1 -1
- data/lib/generators/templates/rpush_3_2_0_add_apns_p8.rb +1 -1
- data/lib/generators/templates/rpush_3_2_4_updates.rb +1 -1
- data/lib/generators/templates/rpush_3_3_0_updates.rb +1 -1
- data/lib/generators/templates/rpush_3_3_1_updates.rb +3 -3
- data/lib/generators/templates/rpush_4_1_0_updates.rb +1 -1
- data/lib/generators/templates/rpush_4_1_1_updates.rb +1 -1
- data/lib/generators/templates/rpush_4_2_0_updates.rb +1 -1
- data/lib/rpush/cli.rb +1 -1
- data/lib/rpush/client/active_model/adm/data_validator.rb +1 -1
- data/lib/rpush/client/active_model/apns/app.rb +1 -17
- data/lib/rpush/client/active_model/apns/device_token_format_validator.rb +2 -2
- data/lib/rpush/client/active_model/apns/notification.rb +13 -1
- data/lib/rpush/client/active_model/apns/notification_payload_size_validator.rb +15 -0
- data/lib/rpush/client/active_model/apns2/app.rb +7 -1
- data/lib/rpush/client/active_model/apns2/notification.rb +14 -0
- data/lib/rpush/client/active_model/certificate_private_key_validator.rb +19 -0
- data/lib/rpush/client/active_model/gcm/expiry_collapse_key_mutual_inclusion_validator.rb +1 -1
- data/lib/rpush/client/active_model/gcm/notification.rb +2 -2
- data/lib/rpush/client/active_model/payload_data_size_validator.rb +1 -1
- data/lib/rpush/client/active_model/registration_ids_count_validator.rb +1 -1
- data/lib/rpush/client/active_model/webpush/app.rb +41 -0
- data/lib/rpush/client/active_model/webpush/notification.rb +66 -0
- data/lib/rpush/client/active_model.rb +5 -1
- data/lib/rpush/client/active_record/apns/active_record_serializable_notification.rb +65 -0
- data/lib/rpush/client/active_record/apns/notification.rb +1 -57
- data/lib/rpush/client/active_record/apns2/notification.rb +4 -1
- data/lib/rpush/client/active_record/apnsp8/notification.rb +1 -0
- data/lib/rpush/client/active_record/webpush/app.rb +11 -0
- data/lib/rpush/client/active_record/webpush/notification.rb +12 -0
- data/lib/rpush/client/active_record.rb +4 -0
- data/lib/rpush/client/redis/apns2/notification.rb +1 -0
- data/lib/rpush/client/redis/apnsp8/notification.rb +2 -0
- data/lib/rpush/client/redis/pushy/notification.rb +0 -1
- data/lib/rpush/client/redis/webpush/app.rb +15 -0
- data/lib/rpush/client/redis/webpush/notification.rb +15 -0
- data/lib/rpush/client/redis.rb +3 -0
- data/lib/rpush/configuration.rb +3 -2
- data/lib/rpush/daemon/apns/feedback_receiver.rb +1 -1
- data/lib/rpush/daemon/apns2/delivery.rb +14 -2
- data/lib/rpush/daemon/apnsp8/delivery.rb +14 -3
- data/lib/rpush/daemon/app_runner.rb +1 -1
- data/lib/rpush/daemon/batch.rb +12 -5
- data/lib/rpush/daemon/delivery.rb +1 -2
- data/lib/rpush/daemon/store/active_record/reconnectable.rb +1 -1
- data/lib/rpush/daemon/store/active_record.rb +11 -7
- data/lib/rpush/daemon/store/redis.rb +6 -6
- data/lib/rpush/daemon/string_helpers.rb +1 -1
- data/lib/rpush/daemon/webpush/delivery.rb +114 -0
- data/lib/rpush/daemon/webpush.rb +10 -0
- data/lib/rpush/daemon.rb +4 -1
- data/lib/rpush/logger.rb +2 -1
- data/lib/rpush/version.rb +3 -3
- data/spec/functional/apns2_spec.rb +99 -2
- data/spec/functional/gcm_priority_spec.rb +40 -0
- data/spec/functional/retry_spec.rb +1 -1
- data/spec/functional/webpush_spec.rb +31 -0
- data/spec/spec_helper.rb +3 -1
- data/spec/support/active_record_setup.rb +4 -3
- data/spec/support/config/database.yml +4 -4
- data/spec/support/simplecov_helper.rb +2 -2
- data/spec/unit/client/active_record/adm/app_spec.rb +2 -54
- data/spec/unit/client/active_record/adm/notification_spec.rb +2 -39
- data/spec/unit/client/active_record/apns/app_spec.rb +3 -26
- data/spec/unit/client/active_record/apns/feedback_spec.rb +1 -5
- data/spec/unit/client/active_record/apns/notification_spec.rb +29 -293
- data/spec/unit/client/active_record/apns2/app_spec.rb +5 -0
- data/spec/unit/client/active_record/apns2/notification_spec.rb +65 -0
- data/spec/unit/client/active_record/apnsp8/notification_spec.rb +28 -0
- data/spec/unit/client/active_record/app_spec.rb +1 -26
- data/spec/unit/client/active_record/gcm/app_spec.rb +3 -1
- data/spec/unit/client/active_record/gcm/notification_spec.rb +6 -88
- data/spec/unit/client/active_record/notification_spec.rb +3 -11
- data/spec/unit/client/active_record/pushy/app_spec.rb +2 -13
- data/spec/unit/client/active_record/pushy/notification_spec.rb +2 -55
- data/spec/unit/client/active_record/shared/app.rb +14 -0
- data/spec/unit/{notification_shared.rb → client/active_record/shared/notification.rb} +12 -7
- data/spec/unit/client/active_record/webpush/app_spec.rb +6 -0
- data/spec/unit/client/active_record/webpush/notification_spec.rb +6 -0
- data/spec/unit/client/active_record/wns/badge_notification_spec.rb +1 -11
- data/spec/unit/client/active_record/wns/raw_notification_spec.rb +3 -12
- data/spec/unit/client/active_record/wpns/app_spec.rb +3 -1
- data/spec/unit/client/active_record/wpns/notification_spec.rb +2 -17
- data/spec/unit/client/redis/adm/app_spec.rb +5 -0
- data/spec/unit/client/redis/adm/notification_spec.rb +5 -0
- data/spec/unit/client/redis/apns/app_spec.rb +5 -0
- data/spec/unit/client/redis/apns/feedback_spec.rb +5 -0
- data/spec/unit/client/redis/apns/notification_spec.rb +50 -0
- data/spec/unit/client/redis/apns2/app_spec.rb +4 -0
- data/spec/unit/client/redis/apns2/notification_spec.rb +50 -0
- data/spec/unit/client/redis/apnsp8/notification_spec.rb +29 -0
- data/spec/unit/client/redis/app_spec.rb +5 -0
- data/spec/unit/client/redis/gcm/app_spec.rb +5 -0
- data/spec/unit/client/redis/gcm/notification_spec.rb +5 -0
- data/spec/unit/client/redis/notification_spec.rb +5 -0
- data/spec/unit/client/redis/pushy/app_spec.rb +5 -0
- data/spec/unit/client/redis/pushy/notification_spec.rb +5 -0
- data/spec/unit/client/redis/webpush/app_spec.rb +5 -0
- data/spec/unit/client/redis/webpush/notification_spec.rb +5 -0
- data/spec/unit/client/redis/wns/badge_notification_spec.rb +5 -0
- data/spec/unit/client/redis/wns/raw_notification_spec.rb +22 -0
- data/spec/unit/client/redis/wpns/app_spec.rb +5 -0
- data/spec/unit/client/redis/wpns/notification_spec.rb +5 -0
- data/spec/unit/client/shared/adm/app.rb +51 -0
- data/spec/unit/client/shared/adm/notification.rb +39 -0
- data/spec/unit/client/shared/apns/app.rb +29 -0
- data/spec/unit/client/shared/apns/feedback.rb +9 -0
- data/spec/unit/client/shared/apns/notification.rb +277 -0
- data/spec/unit/client/shared/app.rb +17 -0
- data/spec/unit/client/shared/gcm/app.rb +4 -0
- data/spec/unit/client/shared/gcm/notification.rb +77 -0
- data/spec/unit/client/shared/notification.rb +10 -0
- data/spec/unit/client/shared/pushy/app.rb +17 -0
- data/spec/unit/client/shared/pushy/notification.rb +55 -0
- data/spec/unit/client/shared/webpush/app.rb +33 -0
- data/spec/unit/client/shared/webpush/notification.rb +83 -0
- data/spec/unit/client/shared/wns/badge_notification.rb +15 -0
- data/spec/unit/client/shared/wns/raw_notification.rb +21 -0
- data/spec/unit/client/shared/wpns/app.rb +4 -0
- data/spec/unit/client/shared/wpns/notification.rb +18 -0
- data/spec/unit/daemon/apnsp8/delivery_spec.rb +53 -0
- data/spec/unit/daemon/batch_spec.rb +50 -2
- data/spec/unit/daemon/delivery_spec.rb +10 -0
- data/spec/unit/daemon/pushy/delivery_spec.rb +5 -3
- data/spec/unit/daemon/shared/store.rb +312 -0
- data/spec/unit/daemon/store/active_record/reconnectable_spec.rb +7 -7
- data/spec/unit/daemon/store/active_record_spec.rb +6 -287
- data/spec/unit/daemon/store/redis_spec.rb +2 -291
- data/spec/unit/daemon/webpush/delivery_spec.rb +144 -0
- data/spec/unit_spec_helper.rb +3 -0
- metadata +145 -18
- 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::
|
|
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
|
|
@@ -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,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
|
|
@@ -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
|
data/lib/rpush/client/redis.rb
CHANGED
|
@@ -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
|
data/lib/rpush/configuration.rb
CHANGED
|
@@ -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
|
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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)
|
data/lib/rpush/daemon/batch.rb
CHANGED
|
@@ -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
|
-
|
|
39
|
-
|
|
40
|
-
end
|
|
39
|
+
def mark_all_retryable(deliver_after, error)
|
|
40
|
+
retryable_count = 0
|
|
41
|
+
|
|
41
42
|
each_notification do |notification|
|
|
42
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
158
|
-
|
|
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
|
-
|
|
172
|
-
|
|
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
|
|
@@ -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
|
+
|