rpush 1.0.0 → 2.0.0.beta1
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 +13 -0
- data/README.md +12 -14
- data/bin/rpush +11 -2
- data/lib/generators/rpush_generator.rb +2 -0
- data/lib/generators/templates/add_adm.rb +5 -5
- data/lib/generators/templates/add_alert_is_json_to_rapns_notifications.rb +1 -1
- 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 +32 -32
- data/lib/generators/templates/add_rpush.rb +67 -67
- data/lib/generators/templates/add_wpns.rb +2 -2
- data/lib/generators/templates/create_rapns_apps.rb +5 -5
- data/lib/generators/templates/create_rapns_feedback.rb +2 -2
- data/lib/generators/templates/create_rapns_notifications.rb +15 -15
- data/lib/generators/templates/rpush.rb +10 -7
- data/lib/generators/templates/rpush_2_0_0_updates.rb +23 -0
- data/lib/rpush.rb +4 -28
- data/lib/rpush/client/active_model.rb +21 -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 +90 -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 +31 -0
- data/lib/rpush/client/active_model/notification.rb +26 -0
- data/lib/rpush/client/active_model/payload_data_size_validator.rb +13 -0
- data/lib/rpush/client/active_model/registration_ids_count_validator.rb +13 -0
- data/lib/rpush/client/active_model/wpns/app.rb +13 -0
- data/lib/rpush/client/active_model/wpns/notification.rb +17 -0
- data/lib/rpush/client/active_record.rb +19 -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 +20 -0
- data/lib/rpush/client/active_record/apns/notification.rb +46 -0
- data/lib/rpush/client/active_record/app.rb +17 -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 +38 -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/redis.rb +35 -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/app.rb +22 -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 +68 -0
- data/lib/rpush/client/redis/wpns/app.rb +11 -0
- data/lib/rpush/client/redis/wpns/notification.rb +11 -0
- data/lib/rpush/configuration.rb +27 -6
- data/lib/rpush/daemon.rb +36 -56
- data/lib/rpush/daemon/adm/delivery.rb +50 -52
- data/lib/rpush/daemon/apns.rb +6 -5
- data/lib/rpush/daemon/apns/delivery.rb +20 -44
- data/lib/rpush/daemon/apns/feedback_receiver.rb +11 -8
- data/lib/rpush/daemon/app_runner.rb +67 -60
- data/lib/rpush/daemon/batch.rb +54 -40
- data/lib/rpush/daemon/delivery.rb +13 -3
- data/lib/rpush/daemon/delivery_error.rb +10 -2
- data/lib/rpush/daemon/dispatcher/apns_tcp.rb +106 -0
- data/lib/rpush/daemon/dispatcher/http.rb +3 -3
- data/lib/rpush/daemon/dispatcher/tcp.rb +3 -3
- data/lib/rpush/daemon/dispatcher_loop.rb +15 -6
- data/lib/rpush/daemon/errors.rb +18 -0
- data/lib/rpush/daemon/feeder.rb +28 -39
- data/lib/rpush/daemon/gcm/delivery.rb +19 -20
- data/lib/rpush/daemon/interruptible_sleep.rb +26 -45
- data/lib/rpush/daemon/loggable.rb +2 -4
- data/lib/rpush/daemon/proc_title.rb +16 -0
- data/lib/rpush/daemon/queue_payload.rb +12 -0
- data/lib/rpush/daemon/reflectable.rb +3 -5
- data/lib/rpush/daemon/retry_header_parser.rb +6 -6
- data/lib/rpush/daemon/ring_buffer.rb +16 -0
- data/lib/rpush/daemon/service_config_methods.rb +23 -7
- data/lib/rpush/daemon/signal_handler.rb +51 -0
- data/lib/rpush/daemon/store/active_record.rb +71 -38
- data/lib/rpush/daemon/store/active_record/reconnectable.rb +15 -15
- data/lib/rpush/daemon/store/interface.rb +19 -0
- data/lib/rpush/daemon/store/redis.rb +149 -0
- data/lib/rpush/daemon/tcp_connection.rb +6 -11
- data/lib/rpush/daemon/wpns/delivery.rb +21 -30
- data/lib/rpush/deprecatable.rb +4 -3
- data/lib/rpush/deprecation.rb +7 -10
- data/lib/rpush/embed.rb +7 -2
- data/lib/rpush/logger.rb +11 -15
- data/lib/rpush/push.rb +0 -1
- data/lib/rpush/reflection.rb +6 -12
- data/lib/rpush/version.rb +1 -1
- data/lib/tasks/quality.rake +34 -0
- data/spec/.rubocop.yml +4 -0
- data/spec/functional/adm_spec.rb +3 -6
- data/spec/functional/apns_spec.rb +118 -24
- data/spec/functional/embed_spec.rb +22 -20
- data/spec/functional/gcm_spec.rb +4 -7
- data/spec/functional/new_app_spec.rb +61 -0
- data/spec/functional/retry_spec.rb +46 -0
- data/spec/functional/wpns_spec.rb +3 -6
- data/spec/functional_spec_helper.rb +26 -0
- data/spec/integration/rpush_spec.rb +13 -0
- data/spec/integration/support/gcm_success_response.json +1 -0
- data/spec/spec_helper.rb +60 -0
- data/spec/support/active_record_setup.rb +48 -0
- data/{config → spec/support/config}/database.yml +0 -0
- data/spec/support/install.sh +43 -7
- data/spec/support/simplecov_helper.rb +9 -5
- data/spec/support/simplecov_quality_formatter.rb +10 -6
- data/spec/unit/apns_feedback_spec.rb +3 -3
- data/spec/unit/{adm → client/active_record/adm}/app_spec.rb +3 -3
- data/spec/unit/{adm → client/active_record/adm}/notification_spec.rb +5 -7
- 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 +231 -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/{gcm → client/active_record/gcm}/notification_spec.rb +5 -7
- data/spec/unit/client/active_record/notification_spec.rb +15 -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 +12 -5
- data/spec/unit/daemon/adm/delivery_spec.rb +57 -54
- data/spec/unit/daemon/apns/certificate_expired_error_spec.rb +3 -3
- data/spec/unit/daemon/apns/delivery_spec.rb +90 -83
- data/spec/unit/daemon/apns/feedback_receiver_spec.rb +24 -17
- data/spec/unit/daemon/app_runner_spec.rb +66 -123
- data/spec/unit/daemon/batch_spec.rb +52 -115
- data/spec/unit/daemon/delivery_spec.rb +15 -1
- data/spec/unit/daemon/dispatcher/http_spec.rb +3 -2
- data/spec/unit/daemon/dispatcher/tcp_spec.rb +10 -9
- data/spec/unit/daemon/dispatcher_loop_spec.rb +7 -12
- data/spec/unit/daemon/feeder_spec.rb +40 -39
- data/spec/unit/daemon/gcm/delivery_spec.rb +108 -89
- data/spec/unit/daemon/reflectable_spec.rb +2 -2
- data/spec/unit/daemon/retryable_error_spec.rb +1 -1
- data/spec/unit/daemon/service_config_methods_spec.rb +6 -3
- data/spec/unit/daemon/signal_handler_spec.rb +72 -0
- data/spec/unit/daemon/store/active_record/reconnectable_spec.rb +9 -9
- data/spec/unit/daemon/store/active_record_spec.rb +38 -47
- data/spec/unit/daemon/tcp_connection_spec.rb +22 -34
- data/spec/unit/daemon/too_many_requests_error_spec.rb +1 -1
- data/spec/unit/daemon/wpns/delivery_spec.rb +61 -50
- data/spec/unit/daemon_spec.rb +46 -81
- data/spec/unit/embed_spec.rb +4 -2
- data/spec/unit/logger_spec.rb +30 -40
- data/spec/unit/notification_shared.rb +9 -79
- data/spec/unit/push_spec.rb +3 -8
- data/spec/unit/reflection_spec.rb +25 -25
- data/spec/unit/rpush_spec.rb +1 -2
- data/spec/unit_spec_helper.rb +33 -88
- metadata +119 -67
- data/lib/rpush/TODO +0 -3
- data/lib/rpush/adm/app.rb +0 -15
- data/lib/rpush/adm/data_validator.rb +0 -11
- data/lib/rpush/adm/notification.rb +0 -29
- data/lib/rpush/apns/app.rb +0 -29
- data/lib/rpush/apns/binary_notification_validator.rb +0 -12
- data/lib/rpush/apns/device_token_format_validator.rb +0 -12
- data/lib/rpush/apns/feedback.rb +0 -16
- data/lib/rpush/apns/notification.rb +0 -84
- data/lib/rpush/app.rb +0 -18
- data/lib/rpush/daemon/apns/certificate_expired_error.rb +0 -20
- data/lib/rpush/daemon/apns/disconnection_error.rb +0 -20
- data/lib/rpush/gcm/app.rb +0 -11
- data/lib/rpush/gcm/expiry_collapse_key_mutual_inclusion_validator.rb +0 -11
- data/lib/rpush/gcm/notification.rb +0 -30
- data/lib/rpush/notification.rb +0 -69
- data/lib/rpush/notifier.rb +0 -52
- data/lib/rpush/payload_data_size_validator.rb +0 -10
- data/lib/rpush/railtie.rb +0 -11
- data/lib/rpush/registration_ids_count_validator.rb +0 -10
- data/lib/rpush/wpns/app.rb +0 -9
- data/lib/rpush/wpns/notification.rb +0 -26
- data/lib/tasks/cane.rake +0 -18
- data/lib/tasks/rpush.rake +0 -16
- data/spec/unit/apns/app_spec.rb +0 -29
- data/spec/unit/apns/feedback_spec.rb +0 -9
- data/spec/unit/apns/notification_spec.rb +0 -208
- data/spec/unit/app_spec.rb +0 -30
- data/spec/unit/daemon/apns/disconnection_error_spec.rb +0 -18
- data/spec/unit/daemon/interruptible_sleep_spec.rb +0 -68
- data/spec/unit/gcm/app_spec.rb +0 -4
- data/spec/unit/notification_spec.rb +0 -15
- data/spec/unit/notifier_spec.rb +0 -49
- data/spec/unit/wpns/app_spec.rb +0 -4
- data/spec/unit/wpns/notification_spec.rb +0 -30
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module Rpush
|
|
2
|
+
module Client
|
|
3
|
+
module Redis
|
|
4
|
+
module Apns
|
|
5
|
+
class Feedback
|
|
6
|
+
include Modis::Model
|
|
7
|
+
|
|
8
|
+
attribute :app_id, :integer
|
|
9
|
+
attribute :device_token, :string
|
|
10
|
+
attribute :failed_at, :timestamp
|
|
11
|
+
|
|
12
|
+
validates :device_token, presence: true
|
|
13
|
+
validates :failed_at, presence: true
|
|
14
|
+
|
|
15
|
+
validates_with Rpush::Client::ActiveModel::Apns::DeviceTokenFormatValidator
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module Rpush
|
|
2
|
+
module Client
|
|
3
|
+
module Redis
|
|
4
|
+
class App
|
|
5
|
+
include Modis::Model
|
|
6
|
+
self.namespace = 'apps'
|
|
7
|
+
|
|
8
|
+
attribute :name, :string
|
|
9
|
+
attribute :environment, :string
|
|
10
|
+
attribute :certificate, :string
|
|
11
|
+
attribute :password, :string
|
|
12
|
+
attribute :connections, :integer, default: 1
|
|
13
|
+
attribute :auth_key, :string
|
|
14
|
+
attribute :client_id, :string
|
|
15
|
+
attribute :client_secret, :string
|
|
16
|
+
|
|
17
|
+
validates :name, presence: true
|
|
18
|
+
validates_numericality_of :connections, greater_than: 0, only_integer: true
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
module Rpush
|
|
2
|
+
module Client
|
|
3
|
+
module Redis
|
|
4
|
+
class Notification
|
|
5
|
+
include Rpush::MultiJsonHelper
|
|
6
|
+
include Modis::Model
|
|
7
|
+
include Rpush::Client::ActiveModel::Notification
|
|
8
|
+
|
|
9
|
+
after_create :register_notification
|
|
10
|
+
|
|
11
|
+
self.namespace = 'notifications'
|
|
12
|
+
|
|
13
|
+
def self.absolute_pending_namespace
|
|
14
|
+
"#{absolute_namespace}:pending"
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.absolute_retryable_namespace
|
|
18
|
+
"#{absolute_namespace}:retryable"
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
attribute :badge, :integer
|
|
22
|
+
attribute :device_token, :string
|
|
23
|
+
attribute :sound, :string, default: 'default'
|
|
24
|
+
attribute :alert, :hash, strict: false
|
|
25
|
+
attribute :data, :hash
|
|
26
|
+
attribute :expiry, :integer, default: 1.day.to_i
|
|
27
|
+
attribute :delivered, :boolean
|
|
28
|
+
attribute :delivered_at, :timestamp
|
|
29
|
+
attribute :failed, :boolean
|
|
30
|
+
attribute :failed_at, :timestamp
|
|
31
|
+
attribute :fail_after, :timestamp
|
|
32
|
+
attribute :retries, :integer, default: 0
|
|
33
|
+
attribute :error_code, :integer
|
|
34
|
+
attribute :error_description, :string
|
|
35
|
+
attribute :deliver_after, :timestamp
|
|
36
|
+
attribute :alert_is_json, :boolean
|
|
37
|
+
attribute :app_id, :integer
|
|
38
|
+
attribute :collapse_key, :string
|
|
39
|
+
attribute :delay_while_idle, :boolean
|
|
40
|
+
attribute :registration_ids, :array
|
|
41
|
+
attribute :uri, :string
|
|
42
|
+
attribute :priority, :integer
|
|
43
|
+
|
|
44
|
+
def app
|
|
45
|
+
return nil unless app_id
|
|
46
|
+
@app ||= Rpush::Client::Redis::App.find(app_id)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def app=(app)
|
|
50
|
+
@app = app
|
|
51
|
+
if app
|
|
52
|
+
self.app_id = app.id
|
|
53
|
+
else
|
|
54
|
+
self.app_id = nil
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
private
|
|
59
|
+
|
|
60
|
+
def register_notification
|
|
61
|
+
Modis.with_connection do |redis|
|
|
62
|
+
redis.zadd(self.class.absolute_pending_namespace, id, id)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
data/lib/rpush/configuration.rb
CHANGED
|
@@ -4,12 +4,28 @@ module Rpush
|
|
|
4
4
|
end
|
|
5
5
|
|
|
6
6
|
def self.configure
|
|
7
|
-
|
|
7
|
+
if block_given?
|
|
8
|
+
yield config
|
|
9
|
+
initialize_client
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.initialize_client
|
|
14
|
+
return if @client_initialized
|
|
15
|
+
require "rpush/client/#{config.client}"
|
|
16
|
+
client_module = Rpush::Client.const_get(config.client.to_s.camelize)
|
|
17
|
+
Rpush.send(:include, client_module)
|
|
18
|
+
|
|
19
|
+
[:Apns, :Gcm, :Wpns, :Adm].each do |service|
|
|
20
|
+
Rpush.const_set(service, client_module.const_get(service))
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
@client_initialized = true
|
|
8
24
|
end
|
|
9
25
|
|
|
10
26
|
CONFIG_ATTRS = [:foreground, :push_poll, :feedback_poll, :embedded,
|
|
11
|
-
|
|
12
|
-
|
|
27
|
+
:check_for_errors, :pid_file, :batch_size, :push, :client, :logger,
|
|
28
|
+
:batch_storage_updates, :log_dir, :environment]
|
|
13
29
|
|
|
14
30
|
class ConfigurationWithoutDefaults < Struct.new(*CONFIG_ATTRS)
|
|
15
31
|
end
|
|
@@ -17,6 +33,11 @@ module Rpush
|
|
|
17
33
|
class Configuration < Struct.new(*CONFIG_ATTRS)
|
|
18
34
|
include Deprecatable
|
|
19
35
|
|
|
36
|
+
deprecated(:batch_storage_updates=, '2.1.0', 'Updates are now always batched by the storage backends.')
|
|
37
|
+
deprecated(:check_for_errors=, '2.1.0', 'APNs error detection is now performed asynchronously and does not require pauses.')
|
|
38
|
+
|
|
39
|
+
delegate :redis_options, :redis_options=, to: :Modis
|
|
40
|
+
|
|
20
41
|
def initialize
|
|
21
42
|
super
|
|
22
43
|
set_defaults
|
|
@@ -60,16 +81,16 @@ module Rpush
|
|
|
60
81
|
|
|
61
82
|
self.push_poll = 2
|
|
62
83
|
self.feedback_poll = 60
|
|
63
|
-
self.check_for_errors = true
|
|
64
84
|
self.batch_size = 100
|
|
65
85
|
self.pid_file = nil
|
|
66
|
-
self.
|
|
86
|
+
self.client = :active_record
|
|
67
87
|
self.logger = nil
|
|
68
|
-
self.
|
|
88
|
+
self.log_dir = Rails.root
|
|
69
89
|
|
|
70
90
|
# Internal options.
|
|
71
91
|
self.embedded = false
|
|
72
92
|
self.push = false
|
|
93
|
+
self.environment = Rails.env
|
|
73
94
|
end
|
|
74
95
|
end
|
|
75
96
|
end
|
data/lib/rpush/daemon.rb
CHANGED
|
@@ -2,9 +2,9 @@ require 'thread'
|
|
|
2
2
|
require 'socket'
|
|
3
3
|
require 'pathname'
|
|
4
4
|
require 'openssl'
|
|
5
|
-
|
|
6
5
|
require 'net/http/persistent'
|
|
7
6
|
|
|
7
|
+
require 'rpush/daemon/errors'
|
|
8
8
|
require 'rpush/daemon/constants'
|
|
9
9
|
require 'rpush/daemon/reflectable'
|
|
10
10
|
require 'rpush/daemon/loggable'
|
|
@@ -15,18 +15,23 @@ require 'rpush/daemon/too_many_requests_error'
|
|
|
15
15
|
require 'rpush/daemon/delivery'
|
|
16
16
|
require 'rpush/daemon/feeder'
|
|
17
17
|
require 'rpush/daemon/batch'
|
|
18
|
+
require 'rpush/daemon/queue_payload'
|
|
18
19
|
require 'rpush/daemon/app_runner'
|
|
19
20
|
require 'rpush/daemon/tcp_connection'
|
|
20
21
|
require 'rpush/daemon/dispatcher_loop'
|
|
21
22
|
require 'rpush/daemon/dispatcher_loop_collection'
|
|
22
23
|
require 'rpush/daemon/dispatcher/http'
|
|
23
24
|
require 'rpush/daemon/dispatcher/tcp'
|
|
25
|
+
require 'rpush/daemon/dispatcher/apns_tcp'
|
|
24
26
|
require 'rpush/daemon/service_config_methods'
|
|
25
27
|
require 'rpush/daemon/retry_header_parser'
|
|
28
|
+
require 'rpush/daemon/ring_buffer'
|
|
29
|
+
require 'rpush/daemon/signal_handler'
|
|
30
|
+
require 'rpush/daemon/proc_title'
|
|
31
|
+
|
|
32
|
+
require 'rpush/daemon/store/interface'
|
|
26
33
|
|
|
27
34
|
require 'rpush/daemon/apns/delivery'
|
|
28
|
-
require 'rpush/daemon/apns/disconnection_error'
|
|
29
|
-
require 'rpush/daemon/apns/certificate_expired_error'
|
|
30
35
|
require 'rpush/daemon/apns/feedback_receiver'
|
|
31
36
|
require 'rpush/daemon/apns'
|
|
32
37
|
|
|
@@ -46,69 +51,59 @@ module Rpush
|
|
|
46
51
|
end
|
|
47
52
|
|
|
48
53
|
def self.start
|
|
49
|
-
|
|
50
|
-
|
|
54
|
+
SignalHandler.start
|
|
55
|
+
Process.daemon if daemonize?
|
|
51
56
|
initialize_store
|
|
52
|
-
return unless store
|
|
53
|
-
|
|
54
|
-
if daemonize?
|
|
55
|
-
daemonize
|
|
56
|
-
store.after_daemonize
|
|
57
|
-
end
|
|
58
|
-
|
|
59
57
|
write_pid_file
|
|
60
58
|
AppRunner.sync
|
|
59
|
+
|
|
60
|
+
# No further store connections will be made from this thread.
|
|
61
|
+
store.release_connection
|
|
62
|
+
|
|
63
|
+
# Blocking call, returns after Feeder.stop is called from another thread.
|
|
61
64
|
Feeder.start
|
|
65
|
+
|
|
66
|
+
# Wait for shutdown to complete.
|
|
67
|
+
shutdown_lock.synchronize { true }
|
|
62
68
|
end
|
|
63
69
|
|
|
64
70
|
def self.shutdown(quiet = false)
|
|
65
71
|
puts "\nShutting down..." unless quiet
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
72
|
+
|
|
73
|
+
shutdown_lock.synchronize do
|
|
74
|
+
Feeder.stop
|
|
75
|
+
AppRunner.stop
|
|
76
|
+
delete_pid_file
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def self.shutdown_lock
|
|
81
|
+
return @shutdown_lock if @shutdown_lock
|
|
82
|
+
@shutdown_lock = Mutex.new
|
|
83
|
+
@shutdown_lock
|
|
69
84
|
end
|
|
70
85
|
|
|
71
86
|
def self.initialize_store
|
|
72
87
|
return if store
|
|
73
88
|
begin
|
|
74
|
-
name = Rpush.config.
|
|
89
|
+
name = Rpush.config.client.to_s
|
|
75
90
|
require "rpush/daemon/store/#{name}"
|
|
76
91
|
self.store = Rpush::Daemon::Store.const_get(name.camelcase).new
|
|
77
92
|
rescue StandardError, LoadError => e
|
|
78
|
-
Rpush.logger.error("Failed to load '#{Rpush.config.
|
|
93
|
+
Rpush.logger.error("Failed to load '#{Rpush.config.client}' storage backend.")
|
|
79
94
|
Rpush.logger.error(e)
|
|
95
|
+
exit 1
|
|
80
96
|
end
|
|
81
97
|
end
|
|
82
98
|
|
|
83
99
|
protected
|
|
84
100
|
|
|
85
101
|
def self.daemonize?
|
|
86
|
-
!(Rpush.config.foreground || Rpush.config.embedded || Rpush.jruby?)
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
def self.trap_signals?
|
|
90
|
-
!Rpush.config.embedded
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
def self.setup_signal_traps
|
|
94
|
-
@shutting_down = false
|
|
95
|
-
|
|
96
|
-
Signal.trap('SIGHUP') { AppRunner.sync }
|
|
97
|
-
Signal.trap('SIGUSR2') { AppRunner.debug }
|
|
98
|
-
|
|
99
|
-
['SIGINT', 'SIGTERM'].each do |signal|
|
|
100
|
-
Signal.trap(signal) { handle_shutdown_signal }
|
|
101
|
-
end
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
def self.handle_shutdown_signal
|
|
105
|
-
exit 1 if @shutting_down
|
|
106
|
-
@shutting_down = true
|
|
107
|
-
shutdown
|
|
102
|
+
!(Rpush.config.push || Rpush.config.foreground || Rpush.config.embedded || Rpush.jruby?)
|
|
108
103
|
end
|
|
109
104
|
|
|
110
105
|
def self.write_pid_file
|
|
111
|
-
|
|
106
|
+
unless Rpush.config.pid_file.blank?
|
|
112
107
|
begin
|
|
113
108
|
File.open(Rpush.config.pid_file, 'w') { |f| f.puts Process.pid }
|
|
114
109
|
rescue SystemCallError => e
|
|
@@ -119,22 +114,7 @@ module Rpush
|
|
|
119
114
|
|
|
120
115
|
def self.delete_pid_file
|
|
121
116
|
pid_file = Rpush.config.pid_file
|
|
122
|
-
File.delete(pid_file) if !pid_file.blank? && File.
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
# :nocov:
|
|
126
|
-
def self.daemonize
|
|
127
|
-
if RUBY_VERSION < "1.9"
|
|
128
|
-
exit if fork
|
|
129
|
-
Process.setsid
|
|
130
|
-
exit if fork
|
|
131
|
-
Dir.chdir "/"
|
|
132
|
-
STDIN.reopen "/dev/null"
|
|
133
|
-
STDOUT.reopen "/dev/null", "a"
|
|
134
|
-
STDERR.reopen "/dev/null", "a"
|
|
135
|
-
else
|
|
136
|
-
Process.daemon
|
|
137
|
-
end
|
|
117
|
+
File.delete(pid_file) if !pid_file.blank? && File.exist?(pid_file)
|
|
138
118
|
end
|
|
139
119
|
end
|
|
140
120
|
end
|
|
@@ -12,7 +12,7 @@ module Rpush
|
|
|
12
12
|
AMAZON_ADM_URL = 'https://api.amazon.com/messaging/registrations/%s/messages'
|
|
13
13
|
|
|
14
14
|
# Data used to request authorization tokens.
|
|
15
|
-
ACCESS_TOKEN_REQUEST_DATA = {"grant_type" => "client_credentials", "scope" => "messaging:push"}
|
|
15
|
+
ACCESS_TOKEN_REQUEST_DATA = { "grant_type" => "client_credentials", "scope" => "messaging:push" }
|
|
16
16
|
|
|
17
17
|
def initialize(app, http, notification, batch)
|
|
18
18
|
@app = app
|
|
@@ -24,29 +24,27 @@ module Rpush
|
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
def perform
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
@notification.error_description = describe_errors
|
|
37
|
-
Rpush::Daemon.store.update_notification(@notification)
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
mark_delivered
|
|
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)
|
|
41
36
|
end
|
|
42
|
-
|
|
43
|
-
handle_retryable(error)
|
|
44
|
-
rescue Rpush::TooManyRequestsError => error
|
|
45
|
-
handle_too_many_requests(error)
|
|
46
|
-
rescue Rpush::DeliveryError => error
|
|
47
|
-
mark_failed(error.code, error.description)
|
|
48
|
-
raise
|
|
37
|
+
mark_delivered
|
|
49
38
|
end
|
|
39
|
+
rescue Rpush::RetryableError => error
|
|
40
|
+
handle_retryable(error)
|
|
41
|
+
rescue Rpush::TooManyRequestsError => error
|
|
42
|
+
handle_too_many_requests(error)
|
|
43
|
+
rescue StandardError => error
|
|
44
|
+
mark_failed(error)
|
|
45
|
+
raise
|
|
46
|
+
ensure
|
|
47
|
+
@batch.notification_processed
|
|
50
48
|
end
|
|
51
49
|
|
|
52
50
|
protected
|
|
@@ -62,25 +60,25 @@ module Rpush
|
|
|
62
60
|
when 429
|
|
63
61
|
too_many_requests(response)
|
|
64
62
|
when 500
|
|
65
|
-
internal_server_error(
|
|
63
|
+
internal_server_error(current_registration_id)
|
|
66
64
|
when 503
|
|
67
65
|
service_unavailable(response)
|
|
68
66
|
else
|
|
69
|
-
|
|
67
|
+
fail Rpush::DeliveryError.new(response.code, @notification.id, Rpush::Daemon::HTTP_STATUS_CODES[response.code.to_i])
|
|
70
68
|
end
|
|
71
69
|
end
|
|
72
70
|
|
|
73
71
|
def ok(response, current_registration_id)
|
|
74
72
|
response_body = multi_json_load(response.body)
|
|
75
73
|
|
|
76
|
-
if
|
|
74
|
+
if response_body.key?('registrationID')
|
|
77
75
|
@sent_registration_ids << response_body['registrationID']
|
|
78
76
|
log_info("#{@notification.id} sent to #{response_body['registrationID']}")
|
|
79
77
|
end
|
|
80
78
|
|
|
81
|
-
if
|
|
82
|
-
|
|
83
|
-
|
|
79
|
+
return if current_registration_id == response_body['registrationID']
|
|
80
|
+
|
|
81
|
+
reflect(:adm_canonical_id, current_registration_id, response_body['registrationID'])
|
|
84
82
|
end
|
|
85
83
|
|
|
86
84
|
def handle_retryable(error)
|
|
@@ -88,7 +86,7 @@ module Rpush
|
|
|
88
86
|
when 401
|
|
89
87
|
# clear app access_token so a new one is fetched
|
|
90
88
|
@notification.app.access_token = nil
|
|
91
|
-
|
|
89
|
+
access_token
|
|
92
90
|
mark_retryable(@notification, Time.now) if @notification.app.access_token
|
|
93
91
|
when 503
|
|
94
92
|
retry_delivery(@notification, error.response)
|
|
@@ -97,7 +95,7 @@ module Rpush
|
|
|
97
95
|
end
|
|
98
96
|
|
|
99
97
|
def handle_too_many_requests(error)
|
|
100
|
-
if
|
|
98
|
+
if @sent_registration_ids.empty?
|
|
101
99
|
# none sent yet, just resend after the specified retry-after response.header
|
|
102
100
|
retry_delivery(@notification, error.response)
|
|
103
101
|
else
|
|
@@ -120,30 +118,30 @@ module Rpush
|
|
|
120
118
|
def bad_request(response, current_registration_id)
|
|
121
119
|
response_body = multi_json_load(response.body)
|
|
122
120
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
121
|
+
return unless response_body.key?('reason')
|
|
122
|
+
|
|
123
|
+
log_warn("bad_request: #{current_registration_id} (#{response_body['reason']})")
|
|
124
|
+
@failed_registration_ids[current_registration_id] = response_body['reason']
|
|
127
125
|
end
|
|
128
126
|
|
|
129
127
|
def unauthorized(response)
|
|
130
128
|
# Indicate a notification is retryable. Because ADM requires separate request for each push token, this will safely mark the entire notification to retry delivery.
|
|
131
|
-
|
|
129
|
+
fail Rpush::RetryableError.new(response.code.to_i, @notification.id, 'ADM responded with an Unauthorized Error.', response)
|
|
132
130
|
end
|
|
133
131
|
|
|
134
132
|
def too_many_requests(response)
|
|
135
133
|
# raise error so the current notification stops sending messages to remaining reg ids
|
|
136
|
-
|
|
134
|
+
fail Rpush::TooManyRequestsError.new(response.code.to_i, @notification.id, 'Exceeded maximum allowable rate of messages.', response)
|
|
137
135
|
end
|
|
138
136
|
|
|
139
|
-
def internal_server_error(
|
|
137
|
+
def internal_server_error(current_registration_id)
|
|
140
138
|
@failed_registration_ids[current_registration_id] = "Internal Server Error"
|
|
141
139
|
log_warn("internal_server_error: #{current_registration_id} (Internal Server Error)")
|
|
142
140
|
end
|
|
143
141
|
|
|
144
142
|
def service_unavailable(response)
|
|
145
143
|
# Indicate a notification is retryable. Because ADM requires separate request for each push token, this will safely mark the entire notification to retry delivery.
|
|
146
|
-
|
|
144
|
+
fail Rpush::RetryableError.new(response.code.to_i, @notification.id, 'ADM responded with an Service Unavailable Error.', response)
|
|
147
145
|
end
|
|
148
146
|
|
|
149
147
|
def create_new_notification(response, registration_ids)
|
|
@@ -156,7 +154,8 @@ module Rpush
|
|
|
156
154
|
end
|
|
157
155
|
|
|
158
156
|
def retry_delivery(notification, response)
|
|
159
|
-
|
|
157
|
+
time = deliver_after_header(response)
|
|
158
|
+
if time
|
|
160
159
|
mark_retryable(notification, time)
|
|
161
160
|
else
|
|
162
161
|
mark_retryable_exponential(notification)
|
|
@@ -178,23 +177,22 @@ module Rpush
|
|
|
178
177
|
end
|
|
179
178
|
|
|
180
179
|
def do_post(registration_id)
|
|
181
|
-
adm_uri = URI.parse(AMAZON_ADM_URL
|
|
182
|
-
post = Net::HTTP::Post.new(adm_uri.path,
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
})
|
|
180
|
+
adm_uri = URI.parse(format(AMAZON_ADM_URL, registration_id))
|
|
181
|
+
post = Net::HTTP::Post.new(adm_uri.path,
|
|
182
|
+
'Content-Type' => 'application/json',
|
|
183
|
+
'Accept' => 'application/json',
|
|
184
|
+
'x-amzn-type-version' => 'com.amazon.device.messaging.ADMMessage@1.0',
|
|
185
|
+
'x-amzn-accept-type' => 'com.amazon.device.messaging.ADMSendResult@1.0',
|
|
186
|
+
'Authorization' => "Bearer #{access_token}")
|
|
189
187
|
post.body = @notification.as_json.to_json
|
|
190
188
|
|
|
191
189
|
@http.request(adm_uri, post)
|
|
192
190
|
end
|
|
193
191
|
|
|
194
|
-
def
|
|
195
|
-
if
|
|
196
|
-
post = Net::HTTP::Post.new(AMAZON_TOKEN_URI.path,
|
|
197
|
-
post.set_form_data(ACCESS_TOKEN_REQUEST_DATA.merge(
|
|
192
|
+
def access_token
|
|
193
|
+
if @notification.app.access_token.nil? || @notification.app.access_token_expired?
|
|
194
|
+
post = Net::HTTP::Post.new(AMAZON_TOKEN_URI.path, 'Content-Type' => 'application/x-www-form-urlencoded')
|
|
195
|
+
post.set_form_data(ACCESS_TOKEN_REQUEST_DATA.merge('client_id' => @notification.app.client_id, 'client_secret' => @notification.app.client_secret))
|
|
198
196
|
|
|
199
197
|
handle_access_token(@http.request(AMAZON_TOKEN_URI, post))
|
|
200
198
|
end
|
|
@@ -203,7 +201,7 @@ module Rpush
|
|
|
203
201
|
end
|
|
204
202
|
|
|
205
203
|
def handle_access_token(response)
|
|
206
|
-
if
|
|
204
|
+
if response.code.to_i == 200
|
|
207
205
|
update_access_token(JSON.parse(response.body))
|
|
208
206
|
Rpush::Daemon.store.update_app(@notification.app)
|
|
209
207
|
log_info("ADM access token updated: token = #{@notification.app.access_token}, expires = #{@notification.app.access_token_expiration}")
|