rpush_extended 3.2.5
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 +7 -0
- data/CHANGELOG.md +365 -0
- data/LICENSE +7 -0
- data/README.md +393 -0
- data/bin/rpush +4 -0
- data/lib/generators/rpush_config_generator.rb +7 -0
- data/lib/generators/rpush_migration_generator.rb +66 -0
- data/lib/generators/templates/add_adm.rb +23 -0
- data/lib/generators/templates/add_alert_is_json_to_rapns_notifications.rb +9 -0
- data/lib/generators/templates/add_app_to_rapns.rb +11 -0
- data/lib/generators/templates/add_fail_after_to_rpush_notifications.rb +9 -0
- data/lib/generators/templates/add_gcm.rb +117 -0
- data/lib/generators/templates/add_rpush.rb +402 -0
- data/lib/generators/templates/add_wpns.rb +16 -0
- data/lib/generators/templates/create_rapns_apps.rb +16 -0
- data/lib/generators/templates/create_rapns_feedback.rb +25 -0
- data/lib/generators/templates/create_rapns_notifications.rb +36 -0
- data/lib/generators/templates/rename_rapns_to_rpush.rb +87 -0
- data/lib/generators/templates/rpush.rb +135 -0
- data/lib/generators/templates/rpush_2_0_0_updates.rb +79 -0
- data/lib/generators/templates/rpush_2_1_0_updates.rb +11 -0
- data/lib/generators/templates/rpush_2_6_0_updates.rb +10 -0
- data/lib/generators/templates/rpush_2_7_0_updates.rb +12 -0
- data/lib/generators/templates/rpush_3_0_0_updates.rb +11 -0
- data/lib/generators/templates/rpush_3_0_1_updates.rb +13 -0
- data/lib/generators/templates/rpush_3_1_0_add_pushy.rb +9 -0
- data/lib/generators/templates/rpush_3_1_1_updates.rb +15 -0
- data/lib/generators/templates/rpush_3_2_0_add_apns_p8.rb +15 -0
- data/lib/generators/templates/rpush_3_2_4_updates.rb +9 -0
- data/lib/generators/templates/rpush_3_3_0_updates.rb +9 -0
- data/lib/generators/templates/rpush_3_3_1_updates.rb +11 -0
- data/lib/rpush/apns_feedback.rb +17 -0
- data/lib/rpush/cli.rb +213 -0
- data/lib/rpush/client/active_model/adm/app.rb +23 -0
- data/lib/rpush/client/active_model/adm/data_validator.rb +14 -0
- data/lib/rpush/client/active_model/adm/notification.rb +28 -0
- data/lib/rpush/client/active_model/apns/app.rb +37 -0
- data/lib/rpush/client/active_model/apns/binary_notification_validator.rb +16 -0
- data/lib/rpush/client/active_model/apns/device_token_format_validator.rb +14 -0
- data/lib/rpush/client/active_model/apns/notification.rb +104 -0
- data/lib/rpush/client/active_model/apns2/app.rb +15 -0
- data/lib/rpush/client/active_model/apns2/notification.rb +9 -0
- data/lib/rpush/client/active_model/apnsp8/app.rb +23 -0
- data/lib/rpush/client/active_model/apnsp8/notification.rb +9 -0
- data/lib/rpush/client/active_model/gcm/app.rb +19 -0
- data/lib/rpush/client/active_model/gcm/expiry_collapse_key_mutual_inclusion_validator.rb +14 -0
- data/lib/rpush/client/active_model/gcm/notification.rb +59 -0
- data/lib/rpush/client/active_model/notification.rb +22 -0
- data/lib/rpush/client/active_model/payload_data_size_validator.rb +13 -0
- data/lib/rpush/client/active_model/pushy/app.rb +20 -0
- data/lib/rpush/client/active_model/pushy/notification.rb +31 -0
- data/lib/rpush/client/active_model/pushy/time_to_live_validator.rb +14 -0
- data/lib/rpush/client/active_model/registration_ids_count_validator.rb +13 -0
- data/lib/rpush/client/active_model/wns/app.rb +23 -0
- data/lib/rpush/client/active_model/wns/notification.rb +32 -0
- data/lib/rpush/client/active_model/wpns/app.rb +13 -0
- data/lib/rpush/client/active_model/wpns/notification.rb +28 -0
- data/lib/rpush/client/active_model.rb +34 -0
- data/lib/rpush/client/active_record/adm/app.rb +11 -0
- data/lib/rpush/client/active_record/adm/notification.rb +11 -0
- data/lib/rpush/client/active_record/apns/app.rb +11 -0
- data/lib/rpush/client/active_record/apns/feedback.rb +18 -0
- data/lib/rpush/client/active_record/apns/notification.rb +40 -0
- data/lib/rpush/client/active_record/apns2/app.rb +11 -0
- data/lib/rpush/client/active_record/apns2/notification.rb +10 -0
- data/lib/rpush/client/active_record/apnsp8/app.rb +11 -0
- data/lib/rpush/client/active_record/apnsp8/notification.rb +10 -0
- data/lib/rpush/client/active_record/app.rb +13 -0
- data/lib/rpush/client/active_record/gcm/app.rb +11 -0
- data/lib/rpush/client/active_record/gcm/notification.rb +11 -0
- data/lib/rpush/client/active_record/notification.rb +42 -0
- data/lib/rpush/client/active_record/pushy/app.rb +11 -0
- data/lib/rpush/client/active_record/pushy/notification.rb +11 -0
- data/lib/rpush/client/active_record/wns/app.rb +11 -0
- data/lib/rpush/client/active_record/wns/badge_notification.rb +15 -0
- data/lib/rpush/client/active_record/wns/notification.rb +11 -0
- data/lib/rpush/client/active_record/wns/raw_notification.rb +13 -0
- data/lib/rpush/client/active_record/wpns/app.rb +11 -0
- data/lib/rpush/client/active_record/wpns/notification.rb +11 -0
- data/lib/rpush/client/active_record.rb +33 -0
- data/lib/rpush/client/redis/adm/app.rb +14 -0
- data/lib/rpush/client/redis/adm/notification.rb +11 -0
- data/lib/rpush/client/redis/apns/app.rb +11 -0
- data/lib/rpush/client/redis/apns/feedback.rb +20 -0
- data/lib/rpush/client/redis/apns/notification.rb +11 -0
- data/lib/rpush/client/redis/apns2/app.rb +11 -0
- data/lib/rpush/client/redis/apns2/notification.rb +11 -0
- data/lib/rpush/client/redis/apnsp8/app.rb +11 -0
- data/lib/rpush/client/redis/apnsp8/notification.rb +11 -0
- data/lib/rpush/client/redis/app.rb +29 -0
- data/lib/rpush/client/redis/gcm/app.rb +11 -0
- data/lib/rpush/client/redis/gcm/notification.rb +11 -0
- data/lib/rpush/client/redis/notification.rb +74 -0
- data/lib/rpush/client/redis/pushy/app.rb +16 -0
- data/lib/rpush/client/redis/pushy/notification.rb +18 -0
- data/lib/rpush/client/redis/wns/app.rb +14 -0
- data/lib/rpush/client/redis/wns/badge_notification.rb +15 -0
- data/lib/rpush/client/redis/wns/notification.rb +11 -0
- data/lib/rpush/client/redis/wns/raw_notification.rb +11 -0
- data/lib/rpush/client/redis/wpns/app.rb +11 -0
- data/lib/rpush/client/redis/wpns/notification.rb +11 -0
- data/lib/rpush/client/redis.rb +56 -0
- data/lib/rpush/configuration.rb +115 -0
- data/lib/rpush/daemon/adm/delivery.rb +226 -0
- data/lib/rpush/daemon/adm.rb +9 -0
- data/lib/rpush/daemon/apns/delivery.rb +43 -0
- data/lib/rpush/daemon/apns/feedback_receiver.rb +90 -0
- data/lib/rpush/daemon/apns.rb +17 -0
- data/lib/rpush/daemon/apns2/delivery.rb +127 -0
- data/lib/rpush/daemon/apns2.rb +10 -0
- data/lib/rpush/daemon/apnsp8/delivery.rb +166 -0
- data/lib/rpush/daemon/apnsp8/token.rb +43 -0
- data/lib/rpush/daemon/apnsp8.rb +10 -0
- data/lib/rpush/daemon/app_runner.rb +190 -0
- data/lib/rpush/daemon/batch.rb +138 -0
- data/lib/rpush/daemon/constants.rb +59 -0
- data/lib/rpush/daemon/delivery.rb +46 -0
- data/lib/rpush/daemon/delivery_error.rb +27 -0
- data/lib/rpush/daemon/dispatcher/apns_http2.rb +51 -0
- data/lib/rpush/daemon/dispatcher/apns_tcp.rb +152 -0
- data/lib/rpush/daemon/dispatcher/apnsp8_http2.rb +33 -0
- data/lib/rpush/daemon/dispatcher/http.rb +21 -0
- data/lib/rpush/daemon/dispatcher/tcp.rb +22 -0
- data/lib/rpush/daemon/dispatcher_loop.rb +73 -0
- data/lib/rpush/daemon/errors.rb +18 -0
- data/lib/rpush/daemon/feeder.rb +69 -0
- data/lib/rpush/daemon/gcm/delivery.rb +241 -0
- data/lib/rpush/daemon/gcm.rb +9 -0
- data/lib/rpush/daemon/interruptible_sleep.rb +24 -0
- data/lib/rpush/daemon/loggable.rb +33 -0
- data/lib/rpush/daemon/proc_title.rb +17 -0
- data/lib/rpush/daemon/pushy/delivery.rb +90 -0
- data/lib/rpush/daemon/pushy.rb +9 -0
- data/lib/rpush/daemon/queue_payload.rb +12 -0
- data/lib/rpush/daemon/retry_header_parser.rb +23 -0
- data/lib/rpush/daemon/retryable_error.rb +22 -0
- data/lib/rpush/daemon/ring_buffer.rb +16 -0
- data/lib/rpush/daemon/rpc/client.rb +27 -0
- data/lib/rpush/daemon/rpc/server.rb +82 -0
- data/lib/rpush/daemon/rpc.rb +9 -0
- data/lib/rpush/daemon/service_config_methods.rb +51 -0
- data/lib/rpush/daemon/signal_handler.rb +75 -0
- data/lib/rpush/daemon/store/active_record/reconnectable.rb +80 -0
- data/lib/rpush/daemon/store/active_record.rb +214 -0
- data/lib/rpush/daemon/store/interface.rb +20 -0
- data/lib/rpush/daemon/store/redis.rb +166 -0
- data/lib/rpush/daemon/string_helpers.rb +15 -0
- data/lib/rpush/daemon/synchronizer.rb +62 -0
- data/lib/rpush/daemon/tcp_connection.rb +190 -0
- data/lib/rpush/daemon/wns/badge_request.rb +32 -0
- data/lib/rpush/daemon/wns/delivery.rb +178 -0
- data/lib/rpush/daemon/wns/post_request.rb +33 -0
- data/lib/rpush/daemon/wns/raw_request.rb +22 -0
- data/lib/rpush/daemon/wns/toast_request.rb +54 -0
- data/lib/rpush/daemon/wns.rb +9 -0
- data/lib/rpush/daemon/wpns/delivery.rb +132 -0
- data/lib/rpush/daemon/wpns.rb +9 -0
- data/lib/rpush/daemon.rb +179 -0
- data/lib/rpush/deprecatable.rb +24 -0
- data/lib/rpush/deprecation.rb +26 -0
- data/lib/rpush/embed.rb +41 -0
- data/lib/rpush/logger.rb +92 -0
- data/lib/rpush/multi_json_helper.rb +16 -0
- data/lib/rpush/plugin.rb +44 -0
- data/lib/rpush/push.rb +11 -0
- data/lib/rpush/reflectable.rb +13 -0
- data/lib/rpush/reflection_collection.rb +44 -0
- data/lib/rpush/reflection_public_methods.rb +11 -0
- data/lib/rpush/version.rb +14 -0
- data/lib/rpush.rb +43 -0
- data/lib/tasks/quality.rake +35 -0
- data/lib/tasks/test.rake +69 -0
- data/spec/.rubocop.yml +4 -0
- data/spec/functional/adm_spec.rb +50 -0
- data/spec/functional/apns2_spec.rb +232 -0
- data/spec/functional/apns_spec.rb +162 -0
- data/spec/functional/cli_spec.rb +36 -0
- data/spec/functional/embed_spec.rb +49 -0
- data/spec/functional/gcm_spec.rb +46 -0
- data/spec/functional/new_app_spec.rb +44 -0
- data/spec/functional/pushy_spec.rb +22 -0
- data/spec/functional/retry_spec.rb +42 -0
- data/spec/functional/synchronization_spec.rb +97 -0
- data/spec/functional/wpns_spec.rb +71 -0
- data/spec/functional_spec_helper.rb +32 -0
- data/spec/spec_helper.rb +69 -0
- data/spec/support/active_record_setup.rb +73 -0
- data/spec/support/cert_with_password.pem +90 -0
- data/spec/support/cert_without_password.pem +59 -0
- data/spec/support/config/database.yml +44 -0
- data/spec/support/simplecov_helper.rb +24 -0
- data/spec/support/simplecov_quality_formatter.rb +12 -0
- data/spec/tmp/.gitkeep +0 -0
- data/spec/unit/apns_feedback_spec.rb +28 -0
- data/spec/unit/client/active_record/adm/app_spec.rb +58 -0
- data/spec/unit/client/active_record/adm/notification_spec.rb +43 -0
- data/spec/unit/client/active_record/apns/app_spec.rb +29 -0
- data/spec/unit/client/active_record/apns/feedback_spec.rb +9 -0
- data/spec/unit/client/active_record/apns/notification_spec.rb +324 -0
- data/spec/unit/client/active_record/app_spec.rb +30 -0
- data/spec/unit/client/active_record/gcm/app_spec.rb +4 -0
- data/spec/unit/client/active_record/gcm/notification_spec.rb +67 -0
- data/spec/unit/client/active_record/notification_spec.rb +21 -0
- data/spec/unit/client/active_record/pushy/app_spec.rb +17 -0
- data/spec/unit/client/active_record/pushy/notification_spec.rb +65 -0
- data/spec/unit/client/active_record/wns/badge_notification_spec.rb +15 -0
- data/spec/unit/client/active_record/wns/raw_notification_spec.rb +26 -0
- data/spec/unit/client/active_record/wpns/app_spec.rb +4 -0
- data/spec/unit/client/active_record/wpns/notification_spec.rb +21 -0
- data/spec/unit/configuration_spec.rb +46 -0
- data/spec/unit/daemon/adm/delivery_spec.rb +253 -0
- data/spec/unit/daemon/apns/certificate_expired_error_spec.rb +11 -0
- data/spec/unit/daemon/apns/delivery_spec.rb +108 -0
- data/spec/unit/daemon/apns/feedback_receiver_spec.rb +119 -0
- data/spec/unit/daemon/app_runner_spec.rb +188 -0
- data/spec/unit/daemon/batch_spec.rb +169 -0
- data/spec/unit/daemon/delivery_error_spec.rb +13 -0
- data/spec/unit/daemon/delivery_spec.rb +51 -0
- data/spec/unit/daemon/dispatcher/http_spec.rb +34 -0
- data/spec/unit/daemon/dispatcher/tcp_spec.rb +32 -0
- data/spec/unit/daemon/dispatcher_loop_spec.rb +53 -0
- data/spec/unit/daemon/feeder_spec.rb +96 -0
- data/spec/unit/daemon/gcm/delivery_spec.rb +387 -0
- data/spec/unit/daemon/proc_title_spec.rb +11 -0
- data/spec/unit/daemon/pushy/delivery_spec.rb +159 -0
- data/spec/unit/daemon/retryable_error_spec.rb +14 -0
- data/spec/unit/daemon/service_config_methods_spec.rb +36 -0
- data/spec/unit/daemon/signal_handler_spec.rb +99 -0
- data/spec/unit/daemon/store/active_record/reconnectable_spec.rb +165 -0
- data/spec/unit/daemon/store/active_record_spec.rb +357 -0
- data/spec/unit/daemon/store/redis_spec.rb +365 -0
- data/spec/unit/daemon/tcp_connection_spec.rb +292 -0
- data/spec/unit/daemon/wns/delivery_spec.rb +176 -0
- data/spec/unit/daemon/wns/post_request_spec.rb +117 -0
- data/spec/unit/daemon/wpns/delivery_spec.rb +167 -0
- data/spec/unit/daemon_spec.rb +138 -0
- data/spec/unit/deprecatable_spec.rb +32 -0
- data/spec/unit/deprecation_spec.rb +15 -0
- data/spec/unit/embed_spec.rb +47 -0
- data/spec/unit/logger_spec.rb +127 -0
- data/spec/unit/notification_shared.rb +53 -0
- data/spec/unit/plugin_spec.rb +36 -0
- data/spec/unit/push_spec.rb +34 -0
- data/spec/unit/reflectable_spec.rb +27 -0
- data/spec/unit/reflection_collection_spec.rb +26 -0
- data/spec/unit/rpush_spec.rb +8 -0
- data/spec/unit_spec_helper.rb +26 -0
- metadata +709 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
|
|
2
|
+
# :nocov:
|
|
3
|
+
begin
|
|
4
|
+
require 'modis'
|
|
5
|
+
require 'redis'
|
|
6
|
+
rescue LoadError
|
|
7
|
+
puts
|
|
8
|
+
str = "* Please add 'rpush-redis' to your Gemfile to use the Redis client. *"
|
|
9
|
+
puts "*" * str.size
|
|
10
|
+
puts str
|
|
11
|
+
puts "*" * str.size
|
|
12
|
+
puts
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
require 'rpush/client/active_model'
|
|
16
|
+
|
|
17
|
+
require 'rpush/client/redis/app'
|
|
18
|
+
require 'rpush/client/redis/notification'
|
|
19
|
+
|
|
20
|
+
require 'rpush/client/redis/apns/app'
|
|
21
|
+
require 'rpush/client/redis/apns/notification'
|
|
22
|
+
require 'rpush/client/redis/apns/feedback'
|
|
23
|
+
|
|
24
|
+
require 'rpush/client/redis/apns2/app'
|
|
25
|
+
require 'rpush/client/redis/apns2/notification'
|
|
26
|
+
|
|
27
|
+
require 'rpush/client/redis/apnsp8/app'
|
|
28
|
+
require 'rpush/client/redis/apnsp8/notification'
|
|
29
|
+
|
|
30
|
+
require 'rpush/client/redis/gcm/app'
|
|
31
|
+
require 'rpush/client/redis/gcm/notification'
|
|
32
|
+
|
|
33
|
+
require 'rpush/client/redis/adm/app'
|
|
34
|
+
require 'rpush/client/redis/adm/notification'
|
|
35
|
+
|
|
36
|
+
require 'rpush/client/redis/wpns/app'
|
|
37
|
+
require 'rpush/client/redis/wpns/notification'
|
|
38
|
+
|
|
39
|
+
require 'rpush/client/redis/wns/app'
|
|
40
|
+
require 'rpush/client/redis/wns/notification'
|
|
41
|
+
require 'rpush/client/redis/wns/raw_notification'
|
|
42
|
+
require 'rpush/client/redis/wns/badge_notification'
|
|
43
|
+
|
|
44
|
+
require 'rpush/client/redis/pushy/app'
|
|
45
|
+
require 'rpush/client/redis/pushy/notification'
|
|
46
|
+
|
|
47
|
+
Modis.configure do |config|
|
|
48
|
+
config.namespace = :rpush
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Prevent diverging Redis namespaces for subclasses as introduced by Modis 1.4.2
|
|
52
|
+
Rpush::Client::Redis::Notification.subclasses.each do |notification_class|
|
|
53
|
+
notification_class.class_eval do
|
|
54
|
+
self.namespace = Rpush::Client::Redis::Notification.namespace
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
require 'pathname'
|
|
2
|
+
require 'ostruct'
|
|
3
|
+
|
|
4
|
+
module Rpush
|
|
5
|
+
class << self
|
|
6
|
+
attr_writer :config
|
|
7
|
+
|
|
8
|
+
def config
|
|
9
|
+
@config ||= Rpush::Configuration.new
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def configure
|
|
13
|
+
return unless block_given?
|
|
14
|
+
yield config
|
|
15
|
+
config.initialize_client
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
CURRENT_ATTRS = [:push_poll, :embedded, :pid_file, :batch_size, :push, :client, :logger, :log_file, :foreground, :log_level, :plugin, :apns]
|
|
20
|
+
DEPRECATED_ATTRS = []
|
|
21
|
+
CONFIG_ATTRS = CURRENT_ATTRS + DEPRECATED_ATTRS
|
|
22
|
+
|
|
23
|
+
class ConfigurationError < StandardError; end
|
|
24
|
+
|
|
25
|
+
class ApnsFeedbackReceiverConfiguration < Struct.new(:frequency, :enabled) # rubocop:disable Style/StructInheritance
|
|
26
|
+
def initialize
|
|
27
|
+
super
|
|
28
|
+
self.enabled = true
|
|
29
|
+
self.frequency = 60
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
class ApnsConfiguration < Struct.new(:feedback_receiver) # rubocop:disable Style/StructInheritance
|
|
34
|
+
def initialize
|
|
35
|
+
super
|
|
36
|
+
self.feedback_receiver = ApnsFeedbackReceiverConfiguration.new
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
class Configuration < Struct.new(*CONFIG_ATTRS) # rubocop:disable Style/StructInheritance
|
|
41
|
+
include Deprecatable
|
|
42
|
+
|
|
43
|
+
delegate :redis_options, to: '::Modis'
|
|
44
|
+
|
|
45
|
+
def initialize
|
|
46
|
+
super
|
|
47
|
+
|
|
48
|
+
self.push_poll = 2
|
|
49
|
+
self.batch_size = 100
|
|
50
|
+
self.logger = nil
|
|
51
|
+
self.log_file = 'log/rpush.log'
|
|
52
|
+
self.pid_file = 'tmp/rpush.pid'
|
|
53
|
+
self.log_level = (defined?(Rails) && Rails.logger) ? Rails.logger.level : ::Logger::Severity::DEBUG
|
|
54
|
+
self.plugin = OpenStruct.new
|
|
55
|
+
self.foreground = false
|
|
56
|
+
|
|
57
|
+
self.apns = ApnsConfiguration.new
|
|
58
|
+
|
|
59
|
+
# Internal options.
|
|
60
|
+
self.embedded = false
|
|
61
|
+
self.push = false
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def update(other)
|
|
65
|
+
CONFIG_ATTRS.each do |attr|
|
|
66
|
+
other_value = other.send(attr)
|
|
67
|
+
send("#{attr}=", other_value) unless other_value.nil?
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def pid_file=(path)
|
|
72
|
+
if path && !Pathname.new(path).absolute?
|
|
73
|
+
super(File.join(Rpush.root, path))
|
|
74
|
+
else
|
|
75
|
+
super
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def log_file=(path)
|
|
80
|
+
if path && !Pathname.new(path).absolute?
|
|
81
|
+
super(File.join(Rpush.root, path))
|
|
82
|
+
else
|
|
83
|
+
super
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def logger=(logger)
|
|
88
|
+
super(logger)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def client=(client)
|
|
92
|
+
super
|
|
93
|
+
initialize_client
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def redis_options=(options)
|
|
97
|
+
Modis.redis_options = options if client == :redis
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def initialize_client
|
|
101
|
+
return if @client_initialized
|
|
102
|
+
raise ConfigurationError, 'Rpush.config.client is not set.' unless client
|
|
103
|
+
require "rpush/client/#{client}"
|
|
104
|
+
|
|
105
|
+
client_module = Rpush::Client.const_get(client.to_s.camelize)
|
|
106
|
+
Rpush.send(:include, client_module) unless Rpush.ancestors.include?(client_module)
|
|
107
|
+
|
|
108
|
+
[:Apns, :Gcm, :Wpns, :Wns, :Adm, :Pushy].each do |service|
|
|
109
|
+
Rpush.const_set(service, client_module.const_get(service)) unless Rpush.const_defined?(service)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
@client_initialized = true
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
module Rpush
|
|
2
|
+
module Daemon
|
|
3
|
+
module Adm
|
|
4
|
+
# https://developer.amazon.com/sdk/adm/sending-message.html
|
|
5
|
+
class Delivery < Rpush::Daemon::Delivery
|
|
6
|
+
include MultiJsonHelper
|
|
7
|
+
|
|
8
|
+
# Oauth2.0 token endpoint. This endpoint is used to request authorization tokens.
|
|
9
|
+
AMAZON_TOKEN_URI = URI.parse('https://api.amazon.com/auth/O2/token')
|
|
10
|
+
|
|
11
|
+
# ADM services endpoint. This endpoint is used to perform ADM requests.
|
|
12
|
+
AMAZON_ADM_URL = 'https://api.amazon.com/messaging/registrations/%s/messages'
|
|
13
|
+
|
|
14
|
+
# Data used to request authorization tokens.
|
|
15
|
+
ACCESS_TOKEN_REQUEST_DATA = { "grant_type" => "client_credentials", "scope" => "messaging:push" }
|
|
16
|
+
|
|
17
|
+
def initialize(app, http, notification, batch)
|
|
18
|
+
@app = app
|
|
19
|
+
@http = http
|
|
20
|
+
@notification = notification
|
|
21
|
+
@batch = batch
|
|
22
|
+
@sent_registration_ids = []
|
|
23
|
+
@failed_registration_ids = {}
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def perform
|
|
27
|
+
@notification.registration_ids.each do |registration_id|
|
|
28
|
+
handle_response(do_post(registration_id), registration_id)
|
|
29
|
+
end
|
|
30
|
+
if @sent_registration_ids.empty?
|
|
31
|
+
fail Rpush::DeliveryError.new(nil, @notification.id, describe_errors)
|
|
32
|
+
else
|
|
33
|
+
unless @failed_registration_ids.empty?
|
|
34
|
+
@notification.error_description = describe_errors
|
|
35
|
+
Rpush::Daemon.store.update_notification(@notification)
|
|
36
|
+
end
|
|
37
|
+
mark_delivered
|
|
38
|
+
end
|
|
39
|
+
rescue Rpush::RateLimitError => error
|
|
40
|
+
handle_rate_limited(error)
|
|
41
|
+
rescue Rpush::RetryableError => error
|
|
42
|
+
handle_retryable(error)
|
|
43
|
+
rescue SocketError => error
|
|
44
|
+
mark_retryable(@notification, Time.now + 10.seconds, error)
|
|
45
|
+
raise
|
|
46
|
+
rescue StandardError => error
|
|
47
|
+
mark_failed(error)
|
|
48
|
+
raise
|
|
49
|
+
ensure
|
|
50
|
+
@batch.notification_processed
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
protected
|
|
54
|
+
|
|
55
|
+
def handle_response(response, current_registration_id)
|
|
56
|
+
case response.code.to_i
|
|
57
|
+
when 200
|
|
58
|
+
ok(response, current_registration_id)
|
|
59
|
+
when 400
|
|
60
|
+
bad_request(response, current_registration_id)
|
|
61
|
+
when 401
|
|
62
|
+
unauthorized(response)
|
|
63
|
+
when 429
|
|
64
|
+
rate_limited(response)
|
|
65
|
+
when 500
|
|
66
|
+
internal_server_error(current_registration_id)
|
|
67
|
+
when 503
|
|
68
|
+
service_unavailable(response)
|
|
69
|
+
else
|
|
70
|
+
fail Rpush::DeliveryError.new(response.code, @notification.id, Rpush::Daemon::HTTP_STATUS_CODES[response.code.to_i])
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def ok(response, current_registration_id)
|
|
75
|
+
response_body = multi_json_load(response.body)
|
|
76
|
+
|
|
77
|
+
if response_body.key?('registrationID')
|
|
78
|
+
@sent_registration_ids << response_body['registrationID']
|
|
79
|
+
log_info("#{@notification.id} sent to #{response_body['registrationID']}")
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
return if current_registration_id == response_body['registrationID']
|
|
83
|
+
|
|
84
|
+
reflect(:adm_canonical_id, current_registration_id, response_body['registrationID'])
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def handle_retryable(error)
|
|
88
|
+
case error.code
|
|
89
|
+
when 401
|
|
90
|
+
# clear app access_token so a new one is fetched
|
|
91
|
+
@notification.app.access_token = nil
|
|
92
|
+
access_token
|
|
93
|
+
mark_retryable(@notification, Time.now) if @notification.app.access_token
|
|
94
|
+
when 503
|
|
95
|
+
retry_delivery(@notification, error.response)
|
|
96
|
+
log_warn("ADM responded with an Service Unavailable Error. " + retry_message)
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def handle_rate_limited(error)
|
|
101
|
+
if @sent_registration_ids.empty?
|
|
102
|
+
# none sent yet, just resend after the specified retry-after response.header
|
|
103
|
+
retry_delivery(@notification, error.response)
|
|
104
|
+
else
|
|
105
|
+
# save unsent registration ids
|
|
106
|
+
unsent_registration_ids = @notification.registration_ids.select { |reg_id| !@sent_registration_ids.include?(reg_id) }
|
|
107
|
+
|
|
108
|
+
# update the current notification so it only contains the sent reg ids
|
|
109
|
+
@notification.registration_ids.reject! { |reg_id| !@sent_registration_ids.include?(reg_id) }
|
|
110
|
+
|
|
111
|
+
Rpush::Daemon.store.update_notification(@notification)
|
|
112
|
+
|
|
113
|
+
# create a new notification with the remaining unsent reg ids
|
|
114
|
+
create_new_notification(error.response, unsent_registration_ids)
|
|
115
|
+
|
|
116
|
+
# mark the current notification as sent
|
|
117
|
+
mark_delivered
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def bad_request(response, current_registration_id)
|
|
122
|
+
response_body = multi_json_load(response.body)
|
|
123
|
+
|
|
124
|
+
return unless response_body.key?('reason')
|
|
125
|
+
|
|
126
|
+
reason = response_body['reason']
|
|
127
|
+
log_warn("bad_request: #{current_registration_id} (#{reason})")
|
|
128
|
+
@failed_registration_ids[current_registration_id] = reason
|
|
129
|
+
|
|
130
|
+
reflect(:adm_failed_to_recipient, @notification, current_registration_id, reason)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def unauthorized(response)
|
|
134
|
+
# Indicate a notification is retryable. Because ADM requires separate request for each push token, this will safely mark the entire notification to retry delivery.
|
|
135
|
+
fail Rpush::RetryableError.new(response.code.to_i, @notification.id, 'ADM responded with an Unauthorized Error.', response)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def rate_limited(response)
|
|
139
|
+
# raise error so the current notification stops sending messages to remaining reg ids
|
|
140
|
+
fail Rpush::RateLimitError.new(response.code.to_i, @notification.id, 'Exceeded maximum allowable rate of messages.', response)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def internal_server_error(current_registration_id)
|
|
144
|
+
@failed_registration_ids[current_registration_id] = "Internal Server Error"
|
|
145
|
+
log_warn("internal_server_error: #{current_registration_id} (Internal Server Error)")
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def service_unavailable(response)
|
|
149
|
+
# Indicate a notification is retryable. Because ADM requires separate request for each push token, this will safely mark the entire notification to retry delivery.
|
|
150
|
+
fail Rpush::RetryableError.new(response.code.to_i, @notification.id, 'ADM responded with an Service Unavailable Error.', response)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def create_new_notification(response, registration_ids)
|
|
154
|
+
attrs = { 'app_id' => @notification.app_id, 'collapse_key' => @notification.collapse_key, 'delay_while_idle' => @notification.delay_while_idle }
|
|
155
|
+
Rpush::Daemon.store.create_adm_notification(attrs, @notification.data, registration_ids, deliver_after_header(response), @notification.app)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def deliver_after_header(response)
|
|
159
|
+
Rpush::Daemon::RetryHeaderParser.parse(response.header['retry-after'])
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def retry_delivery(notification, response)
|
|
163
|
+
time = deliver_after_header(response)
|
|
164
|
+
if time
|
|
165
|
+
mark_retryable(notification, time)
|
|
166
|
+
else
|
|
167
|
+
mark_retryable_exponential(notification)
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def describe_errors
|
|
172
|
+
if @failed_registration_ids.size == @notification.registration_ids.size
|
|
173
|
+
"Failed to deliver to all recipients."
|
|
174
|
+
else
|
|
175
|
+
error_msgs = []
|
|
176
|
+
@failed_registration_ids.each_pair { |regId, reason| error_msgs.push("#{regId}: #{reason}") }
|
|
177
|
+
"Failed to deliver to recipients: \n#{error_msgs.join("\n")}"
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def retry_message
|
|
182
|
+
"Notification #{@notification.id} will be retired after #{@notification.deliver_after.strftime('%Y-%m-%d %H:%M:%S')} (retry #{@notification.retries})."
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def do_post(registration_id)
|
|
186
|
+
adm_uri = URI.parse(format(AMAZON_ADM_URL, registration_id))
|
|
187
|
+
post = Net::HTTP::Post.new(adm_uri.path,
|
|
188
|
+
'Content-Type' => 'application/json',
|
|
189
|
+
'Accept' => 'application/json',
|
|
190
|
+
'x-amzn-type-version' => 'com.amazon.device.messaging.ADMMessage@1.0',
|
|
191
|
+
'x-amzn-accept-type' => 'com.amazon.device.messaging.ADMSendResult@1.0',
|
|
192
|
+
'Authorization' => "Bearer #{access_token}")
|
|
193
|
+
post.body = @notification.as_json.to_json
|
|
194
|
+
|
|
195
|
+
@http.request(adm_uri, post)
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def access_token
|
|
199
|
+
if @notification.app.access_token.nil? || @notification.app.access_token_expired?
|
|
200
|
+
post = Net::HTTP::Post.new(AMAZON_TOKEN_URI.path, 'Content-Type' => 'application/x-www-form-urlencoded')
|
|
201
|
+
post.set_form_data(ACCESS_TOKEN_REQUEST_DATA.merge('client_id' => @notification.app.client_id, 'client_secret' => @notification.app.client_secret))
|
|
202
|
+
|
|
203
|
+
handle_access_token(@http.request(AMAZON_TOKEN_URI, post))
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
@notification.app.access_token
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def handle_access_token(response)
|
|
210
|
+
if response.code.to_i == 200
|
|
211
|
+
update_access_token(JSON.parse(response.body))
|
|
212
|
+
Rpush::Daemon.store.update_app(@notification.app)
|
|
213
|
+
log_info("ADM access token updated: token = #{@notification.app.access_token}, expires = #{@notification.app.access_token_expiration}")
|
|
214
|
+
else
|
|
215
|
+
log_warn("Could not retrieve access token from ADM: #{response.body}")
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
def update_access_token(data)
|
|
220
|
+
@notification.app.access_token = data['access_token']
|
|
221
|
+
@notification.app.access_token_expiration = Time.now + data['expires_in'].to_i
|
|
222
|
+
end
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
module Rpush
|
|
2
|
+
module Daemon
|
|
3
|
+
module Apns
|
|
4
|
+
class Delivery < Rpush::Daemon::Delivery
|
|
5
|
+
def initialize(app, connection, batch)
|
|
6
|
+
@app = app
|
|
7
|
+
@connection = connection
|
|
8
|
+
@batch = batch
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def perform
|
|
12
|
+
@connection.write(batch_to_binary)
|
|
13
|
+
mark_batch_delivered
|
|
14
|
+
describe_deliveries
|
|
15
|
+
rescue Rpush::Daemon::TcpConnectionError => error
|
|
16
|
+
mark_batch_retryable(Time.now + 10.seconds, error)
|
|
17
|
+
raise
|
|
18
|
+
rescue StandardError => error
|
|
19
|
+
mark_batch_failed(error)
|
|
20
|
+
raise
|
|
21
|
+
ensure
|
|
22
|
+
@batch.all_processed
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
protected
|
|
26
|
+
|
|
27
|
+
def batch_to_binary
|
|
28
|
+
payload = ""
|
|
29
|
+
@batch.each_notification do |notification|
|
|
30
|
+
payload << notification.to_binary
|
|
31
|
+
end
|
|
32
|
+
payload
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def describe_deliveries
|
|
36
|
+
@batch.each_notification do |notification|
|
|
37
|
+
log_info("#{notification.id} sent to #{notification.device_token}")
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
|
|
3
|
+
module Rpush
|
|
4
|
+
module Daemon
|
|
5
|
+
module Apns
|
|
6
|
+
class FeedbackReceiver
|
|
7
|
+
include Reflectable
|
|
8
|
+
include Loggable
|
|
9
|
+
|
|
10
|
+
TUPLE_BYTES = 38
|
|
11
|
+
HOSTS = {
|
|
12
|
+
production: ['feedback.push.apple.com', 2196],
|
|
13
|
+
development: ['feedback.sandbox.push.apple.com', 2196], # deprecated
|
|
14
|
+
sandbox: ['feedback.sandbox.push.apple.com', 2196]
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
def initialize(app)
|
|
18
|
+
@app = app
|
|
19
|
+
@host, @port = HOSTS[@app.environment.to_sym]
|
|
20
|
+
@certificate = app.certificate
|
|
21
|
+
@password = app.password
|
|
22
|
+
@interruptible_sleep = InterruptibleSleep.new
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def start
|
|
26
|
+
return if Rpush.config.push
|
|
27
|
+
Rpush.logger.info("[#{@app.name}] Starting feedback receiver... ", true)
|
|
28
|
+
|
|
29
|
+
@thread = Thread.new do
|
|
30
|
+
loop do
|
|
31
|
+
break if @stop
|
|
32
|
+
check_for_feedback
|
|
33
|
+
@interruptible_sleep.sleep(Rpush.config.apns.feedback_receiver.frequency)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
Rpush::Daemon.store.release_connection
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
puts ANSI.green { '✔' } if Rpush.config.foreground
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def stop
|
|
43
|
+
@stop = true
|
|
44
|
+
@interruptible_sleep.stop
|
|
45
|
+
@thread.join if @thread
|
|
46
|
+
rescue StandardError => e
|
|
47
|
+
log_error(e)
|
|
48
|
+
reflect(:error, e)
|
|
49
|
+
ensure
|
|
50
|
+
@thread = nil
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def check_for_feedback
|
|
54
|
+
connection = nil
|
|
55
|
+
begin
|
|
56
|
+
connection = Rpush::Daemon::TcpConnection.new(@app, @host, @port)
|
|
57
|
+
connection.connect
|
|
58
|
+
tuple = connection.read(TUPLE_BYTES)
|
|
59
|
+
|
|
60
|
+
while tuple
|
|
61
|
+
timestamp, device_token = parse_tuple(tuple)
|
|
62
|
+
create_feedback(timestamp, device_token)
|
|
63
|
+
tuple = connection.read(TUPLE_BYTES)
|
|
64
|
+
end
|
|
65
|
+
rescue StandardError => e
|
|
66
|
+
log_error(e)
|
|
67
|
+
reflect(:error, e)
|
|
68
|
+
ensure
|
|
69
|
+
connection.close if connection
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
protected
|
|
74
|
+
|
|
75
|
+
def parse_tuple(tuple)
|
|
76
|
+
failed_at, _, device_token = tuple.unpack("N1n1H*")
|
|
77
|
+
[Time.at(failed_at).utc, device_token]
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def create_feedback(failed_at, device_token)
|
|
81
|
+
formatted_failed_at = failed_at.strftime('%Y-%m-%d %H:%M:%S UTC')
|
|
82
|
+
log_info("[FeedbackReceiver] Delivery failed at #{formatted_failed_at} for #{device_token}.")
|
|
83
|
+
|
|
84
|
+
feedback = Rpush::Daemon.store.create_apns_feedback(failed_at, device_token, @app)
|
|
85
|
+
reflect(:apns_feedback, feedback)
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module Rpush
|
|
2
|
+
module Daemon
|
|
3
|
+
module Apns
|
|
4
|
+
extend ServiceConfigMethods
|
|
5
|
+
|
|
6
|
+
HOSTS = {
|
|
7
|
+
production: ['gateway.push.apple.com', 2195],
|
|
8
|
+
development: ['gateway.sandbox.push.apple.com', 2195], # deprecated
|
|
9
|
+
sandbox: ['gateway.sandbox.push.apple.com', 2195]
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
batch_deliveries true
|
|
13
|
+
dispatcher :apns_tcp, host: proc { |app| HOSTS[app.environment.to_sym] }
|
|
14
|
+
loops Rpush::Daemon::Apns::FeedbackReceiver, if: -> { Rpush.config.apns.feedback_receiver.enabled && !Rpush.config.push }
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
module Rpush
|
|
2
|
+
module Daemon
|
|
3
|
+
module Apns2
|
|
4
|
+
# https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CommunicatingwithAPNs.html
|
|
5
|
+
|
|
6
|
+
HTTP2_HEADERS_KEY = 'headers'
|
|
7
|
+
|
|
8
|
+
class Delivery < Rpush::Daemon::Delivery
|
|
9
|
+
RETRYABLE_CODES = [ 429, 500, 503 ]
|
|
10
|
+
|
|
11
|
+
def initialize(app, http2_client, batch)
|
|
12
|
+
@app = app
|
|
13
|
+
@client = http2_client
|
|
14
|
+
@batch = batch
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def perform
|
|
18
|
+
@client.on(:error) { |err| mark_batch_retryable(Time.now + 10.seconds, err) }
|
|
19
|
+
|
|
20
|
+
@batch.each_notification do |notification|
|
|
21
|
+
prepare_async_post(notification)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Send all preprocessed requests at once
|
|
25
|
+
@client.join
|
|
26
|
+
rescue Errno::ECONNREFUSED, SocketError => error
|
|
27
|
+
mark_batch_retryable(Time.now + 10.seconds, error)
|
|
28
|
+
raise
|
|
29
|
+
rescue StandardError => error
|
|
30
|
+
mark_batch_failed(error)
|
|
31
|
+
raise
|
|
32
|
+
ensure
|
|
33
|
+
@batch.all_processed
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
protected
|
|
37
|
+
######################################################################
|
|
38
|
+
|
|
39
|
+
def prepare_async_post(notification)
|
|
40
|
+
response = {}
|
|
41
|
+
|
|
42
|
+
request = build_request(notification)
|
|
43
|
+
http_request = @client.prepare_request(:post, request[:path],
|
|
44
|
+
body: request[:body],
|
|
45
|
+
headers: request[:headers]
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
http_request.on(:headers) do |hdrs|
|
|
49
|
+
response[:code] = hdrs[':status'].to_i
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
http_request.on(:body_chunk) do |body_chunk|
|
|
53
|
+
next unless body_chunk.present?
|
|
54
|
+
|
|
55
|
+
response[:failure_reason] = JSON.parse(body_chunk)['reason']
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
http_request.on(:close) { handle_response(notification, response) }
|
|
59
|
+
|
|
60
|
+
@client.call_async(http_request)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def handle_response(notification, response)
|
|
64
|
+
code = response[:code]
|
|
65
|
+
case code
|
|
66
|
+
when 200
|
|
67
|
+
ok(notification)
|
|
68
|
+
when *RETRYABLE_CODES
|
|
69
|
+
service_unavailable(notification, response)
|
|
70
|
+
else
|
|
71
|
+
reflect(:notification_id_failed,
|
|
72
|
+
@app,
|
|
73
|
+
notification.id, code,
|
|
74
|
+
response[:failure_reason])
|
|
75
|
+
@batch.mark_failed(notification, response[:code], response[:failure_reason])
|
|
76
|
+
failed_message_to_log(notification, response)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def ok(notification)
|
|
81
|
+
log_info("#{notification.id} sent to #{notification.device_token}")
|
|
82
|
+
@batch.mark_delivered(notification)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def service_unavailable(notification, response)
|
|
86
|
+
@batch.mark_retryable(notification, Time.now + 10.seconds)
|
|
87
|
+
# Logs should go last as soon as we need to initialize
|
|
88
|
+
# retry time to display it in log
|
|
89
|
+
failed_message_to_log(notification, response)
|
|
90
|
+
retry_message_to_log(notification)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def build_request(notification)
|
|
94
|
+
{
|
|
95
|
+
path: "/3/device/#{notification.device_token}",
|
|
96
|
+
headers: prepare_headers(notification),
|
|
97
|
+
body: prepare_body(notification)
|
|
98
|
+
}
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def prepare_body(notification)
|
|
102
|
+
hash = notification.as_json.except(HTTP2_HEADERS_KEY)
|
|
103
|
+
JSON.dump(hash).force_encoding(Encoding::BINARY)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def prepare_headers(notification)
|
|
107
|
+
notification_data(notification)[HTTP2_HEADERS_KEY] || {}
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def notification_data(notification)
|
|
111
|
+
notification.data || {}
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def retry_message_to_log(notification)
|
|
115
|
+
log_warn("Notification #{notification.id} will be retried after "\
|
|
116
|
+
"#{notification.deliver_after.strftime('%Y-%m-%d %H:%M:%S')} "\
|
|
117
|
+
"(retry #{notification.retries}).")
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def failed_message_to_log(notification, response)
|
|
121
|
+
log_error("Notification #{notification.id} failed, "\
|
|
122
|
+
"#{response[:code]}/#{response[:failure_reason]}")
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|