rpush 1.0.0-java → 2.0.0-java
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 +15 -0
- data/README.md +37 -22
- data/bin/rpush +13 -4
- 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 +28 -7
- data/lib/generators/templates/rpush_2_0_0_updates.rb +42 -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_model.rb +21 -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 +22 -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/active_record.rb +19 -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 +24 -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/client/redis.rb +35 -0
- data/lib/rpush/configuration.rb +27 -6
- data/lib/rpush/daemon/adm/delivery.rb +56 -55
- data/lib/rpush/daemon/apns/delivery.rb +20 -44
- data/lib/rpush/daemon/apns/feedback_receiver.rb +11 -8
- data/lib/rpush/daemon/apns.rb +6 -5
- data/lib/rpush/daemon/app_runner.rb +103 -99
- 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 +114 -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 +37 -23
- 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/retryable_error.rb +2 -0
- 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 +56 -0
- data/lib/rpush/daemon/store/active_record/reconnectable.rb +21 -17
- data/lib/rpush/daemon/store/active_record.rb +71 -38
- data/lib/rpush/daemon/store/interface.rb +19 -0
- data/lib/rpush/daemon/store/redis.rb +149 -0
- data/lib/rpush/daemon/string_helpers.rb +15 -0
- data/lib/rpush/daemon/synchronizer.rb +60 -0
- data/lib/rpush/daemon/tcp_connection.rb +6 -11
- data/lib/rpush/daemon/wpns/delivery.rb +21 -30
- data/lib/rpush/daemon.rb +40 -60
- data/lib/rpush/deprecatable.rb +4 -3
- data/lib/rpush/deprecation.rb +7 -10
- data/lib/rpush/embed.rb +8 -3
- data/lib/rpush/logger.rb +11 -15
- data/lib/rpush/push.rb +1 -2
- data/lib/rpush/reflection.rb +8 -12
- data/lib/rpush/version.rb +1 -1
- data/lib/rpush.rb +5 -29
- data/lib/tasks/quality.rake +35 -0
- data/lib/tasks/test.rake +1 -5
- data/spec/.rubocop.yml +4 -0
- data/spec/functional/adm_spec.rb +3 -6
- data/spec/functional/apns_spec.rb +117 -24
- data/spec/functional/embed_spec.rb +20 -20
- data/spec/functional/gcm_spec.rb +4 -7
- data/spec/functional/new_app_spec.rb +59 -0
- data/spec/functional/retry_spec.rb +46 -0
- data/spec/functional/synchronization_spec.rb +68 -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 +21 -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 +66 -55
- 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 +22 -17
- data/spec/unit/daemon/app_runner_spec.rb +78 -186
- 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 +6 -24
- data/spec/unit/daemon/feeder_spec.rb +38 -39
- data/spec/unit/daemon/gcm/delivery_spec.rb +122 -101
- 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 +95 -0
- data/spec/unit/daemon/store/active_record/reconnectable_spec.rb +48 -27
- 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/wpns/delivery_spec.rb +58 -50
- data/spec/unit/daemon_spec.rb +48 -82
- data/spec/unit/embed_spec.rb +6 -4
- data/spec/unit/logger_spec.rb +35 -43
- data/spec/unit/notification_shared.rb +9 -79
- data/spec/unit/push_spec.rb +6 -10
- 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 +126 -76
- 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/daemon/dispatcher_loop_collection.rb +0 -33
- data/lib/rpush/daemon/too_many_requests_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/dispatcher_loop_collection_spec.rb +0 -37
- data/spec/unit/daemon/interruptible_sleep_spec.rb +0 -68
- data/spec/unit/daemon/too_many_requests_error_spec.rb +0 -14
- 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
@@ -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::RateLimitError => error
|
40
|
+
handle_rate_limited(error)
|
41
|
+
rescue Rpush::RetryableError => error
|
42
|
+
handle_retryable(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
|
@@ -60,27 +58,27 @@ module Rpush
|
|
60
58
|
when 401
|
61
59
|
unauthorized(response)
|
62
60
|
when 429
|
63
|
-
|
61
|
+
rate_limited(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)
|
@@ -96,8 +94,8 @@ module Rpush
|
|
96
94
|
end
|
97
95
|
end
|
98
96
|
|
99
|
-
def
|
100
|
-
if
|
97
|
+
def handle_rate_limited(error)
|
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,33 @@ 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
|
+
reason = response_body['reason']
|
124
|
+
log_warn("bad_request: #{current_registration_id} (#{reason})")
|
125
|
+
@failed_registration_ids[current_registration_id] = reason
|
126
|
+
|
127
|
+
reflect(:adm_failed_to_recipient, @notification, current_registration_id, reason)
|
127
128
|
end
|
128
129
|
|
129
130
|
def unauthorized(response)
|
130
131
|
# 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
|
-
|
132
|
+
fail Rpush::RetryableError.new(response.code.to_i, @notification.id, 'ADM responded with an Unauthorized Error.', response)
|
132
133
|
end
|
133
134
|
|
134
|
-
def
|
135
|
+
def rate_limited(response)
|
135
136
|
# raise error so the current notification stops sending messages to remaining reg ids
|
136
|
-
|
137
|
+
fail Rpush::RateLimitError.new(response.code.to_i, @notification.id, 'Exceeded maximum allowable rate of messages.', response)
|
137
138
|
end
|
138
139
|
|
139
|
-
def internal_server_error(
|
140
|
+
def internal_server_error(current_registration_id)
|
140
141
|
@failed_registration_ids[current_registration_id] = "Internal Server Error"
|
141
142
|
log_warn("internal_server_error: #{current_registration_id} (Internal Server Error)")
|
142
143
|
end
|
143
144
|
|
144
145
|
def service_unavailable(response)
|
145
146
|
# 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
|
-
|
147
|
+
fail Rpush::RetryableError.new(response.code.to_i, @notification.id, 'ADM responded with an Service Unavailable Error.', response)
|
147
148
|
end
|
148
149
|
|
149
150
|
def create_new_notification(response, registration_ids)
|
@@ -156,7 +157,8 @@ module Rpush
|
|
156
157
|
end
|
157
158
|
|
158
159
|
def retry_delivery(notification, response)
|
159
|
-
|
160
|
+
time = deliver_after_header(response)
|
161
|
+
if time
|
160
162
|
mark_retryable(notification, time)
|
161
163
|
else
|
162
164
|
mark_retryable_exponential(notification)
|
@@ -178,23 +180,22 @@ module Rpush
|
|
178
180
|
end
|
179
181
|
|
180
182
|
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
|
-
})
|
183
|
+
adm_uri = URI.parse(format(AMAZON_ADM_URL, registration_id))
|
184
|
+
post = Net::HTTP::Post.new(adm_uri.path,
|
185
|
+
'Content-Type' => 'application/json',
|
186
|
+
'Accept' => 'application/json',
|
187
|
+
'x-amzn-type-version' => 'com.amazon.device.messaging.ADMMessage@1.0',
|
188
|
+
'x-amzn-accept-type' => 'com.amazon.device.messaging.ADMSendResult@1.0',
|
189
|
+
'Authorization' => "Bearer #{access_token}")
|
189
190
|
post.body = @notification.as_json.to_json
|
190
191
|
|
191
192
|
@http.request(adm_uri, post)
|
192
193
|
end
|
193
194
|
|
194
|
-
def
|
195
|
-
if
|
196
|
-
post = Net::HTTP::Post.new(AMAZON_TOKEN_URI.path,
|
197
|
-
post.set_form_data(ACCESS_TOKEN_REQUEST_DATA.merge(
|
195
|
+
def access_token
|
196
|
+
if @notification.app.access_token.nil? || @notification.app.access_token_expired?
|
197
|
+
post = Net::HTTP::Post.new(AMAZON_TOKEN_URI.path, 'Content-Type' => 'application/x-www-form-urlencoded')
|
198
|
+
post.set_form_data(ACCESS_TOKEN_REQUEST_DATA.merge('client_id' => @notification.app.client_id, 'client_secret' => @notification.app.client_secret))
|
198
199
|
|
199
200
|
handle_access_token(@http.request(AMAZON_TOKEN_URI, post))
|
200
201
|
end
|
@@ -203,7 +204,7 @@ module Rpush
|
|
203
204
|
end
|
204
205
|
|
205
206
|
def handle_access_token(response)
|
206
|
-
if
|
207
|
+
if response.code.to_i == 200
|
207
208
|
update_access_token(JSON.parse(response.body))
|
208
209
|
Rpush::Daemon.store.update_app(@notification.app)
|
209
210
|
log_info("ADM access token updated: token = #{@notification.app.access_token}, expires = #{@notification.app.access_token_expiration}")
|
@@ -2,60 +2,36 @@ module Rpush
|
|
2
2
|
module Daemon
|
3
3
|
module Apns
|
4
4
|
class Delivery < Rpush::Daemon::Delivery
|
5
|
-
|
6
|
-
ERROR_TUPLE_BYTES = 6
|
7
|
-
APN_ERRORS = {
|
8
|
-
1 => "Processing error",
|
9
|
-
2 => "Missing device token",
|
10
|
-
3 => "Missing topic",
|
11
|
-
4 => "Missing payload",
|
12
|
-
5 => "Missing token size",
|
13
|
-
6 => "Missing topic size",
|
14
|
-
7 => "Missing payload size",
|
15
|
-
8 => "Invalid token",
|
16
|
-
255 => "None (unknown error)"
|
17
|
-
}
|
18
|
-
|
19
|
-
def initialize(app, conneciton, notification, batch)
|
5
|
+
def initialize(app, connection, batch)
|
20
6
|
@app = app
|
21
|
-
@connection =
|
22
|
-
@notification = notification
|
7
|
+
@connection = connection
|
23
8
|
@batch = batch
|
24
9
|
end
|
25
10
|
|
26
11
|
def perform
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
end
|
12
|
+
@connection.write(batch_to_binary)
|
13
|
+
mark_batch_delivered
|
14
|
+
describe_deliveries
|
15
|
+
rescue StandardError => error
|
16
|
+
mark_batch_failed(error)
|
17
|
+
raise
|
18
|
+
ensure
|
19
|
+
@batch.all_processed
|
36
20
|
end
|
37
21
|
|
38
22
|
protected
|
39
23
|
|
40
|
-
def
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
description = APN_ERRORS[code.to_i] || "Unknown error. Possible Rpush bug?"
|
48
|
-
error = Rpush::DeliveryError.new(code, notification_id, description)
|
49
|
-
else
|
50
|
-
error = Rpush::Apns::DisconnectionError.new
|
51
|
-
end
|
24
|
+
def batch_to_binary
|
25
|
+
payload = ""
|
26
|
+
@batch.each_notification do |notification|
|
27
|
+
payload << notification.to_binary
|
28
|
+
end
|
29
|
+
payload
|
30
|
+
end
|
52
31
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
ensure
|
57
|
-
raise error if error
|
58
|
-
end
|
32
|
+
def describe_deliveries
|
33
|
+
@batch.each_notification do |notification|
|
34
|
+
log_info("#{notification.id} sent to #{notification.device_token}")
|
59
35
|
end
|
60
36
|
end
|
61
37
|
end
|
@@ -7,28 +7,29 @@ module Rpush
|
|
7
7
|
|
8
8
|
TUPLE_BYTES = 38
|
9
9
|
HOSTS = {
|
10
|
-
:
|
11
|
-
:
|
12
|
-
:
|
10
|
+
production: ['feedback.push.apple.com', 2196],
|
11
|
+
development: ['feedback.sandbox.push.apple.com', 2196], # deprecated
|
12
|
+
sandbox: ['feedback.sandbox.push.apple.com', 2196]
|
13
13
|
}
|
14
14
|
|
15
15
|
def initialize(app)
|
16
16
|
@app = app
|
17
17
|
@host, @port = HOSTS[@app.environment.to_sym]
|
18
|
-
@poll = Rpush.config.feedback_poll
|
19
18
|
@certificate = app.certificate
|
20
19
|
@password = app.password
|
21
|
-
@interruptible_sleep = InterruptibleSleep.new
|
20
|
+
@interruptible_sleep = InterruptibleSleep.new(Rpush.config.feedback_poll)
|
22
21
|
end
|
23
22
|
|
24
23
|
def start
|
25
24
|
return if Rpush.config.push
|
25
|
+
log_info("APNs Feedback Receiver started.")
|
26
|
+
@interruptible_sleep.start
|
26
27
|
|
27
28
|
@thread = Thread.new do
|
28
29
|
loop do
|
29
30
|
break if @stop
|
30
31
|
check_for_feedback
|
31
|
-
@interruptible_sleep.sleep
|
32
|
+
@interruptible_sleep.sleep
|
32
33
|
end
|
33
34
|
|
34
35
|
Rpush::Daemon.store.release_connection
|
@@ -37,7 +38,7 @@ module Rpush
|
|
37
38
|
|
38
39
|
def stop
|
39
40
|
@stop = true
|
40
|
-
@interruptible_sleep.
|
41
|
+
@interruptible_sleep.stop
|
41
42
|
@thread.join if @thread
|
42
43
|
end
|
43
44
|
|
@@ -46,10 +47,12 @@ module Rpush
|
|
46
47
|
begin
|
47
48
|
connection = Rpush::Daemon::TcpConnection.new(@app, @host, @port)
|
48
49
|
connection.connect
|
50
|
+
tuple = connection.read(TUPLE_BYTES)
|
49
51
|
|
50
|
-
while tuple
|
52
|
+
while tuple
|
51
53
|
timestamp, device_token = parse_tuple(tuple)
|
52
54
|
create_feedback(timestamp, device_token)
|
55
|
+
tuple = connection.read(TUPLE_BYTES)
|
53
56
|
end
|
54
57
|
rescue StandardError => e
|
55
58
|
log_error(e)
|
data/lib/rpush/daemon/apns.rb
CHANGED
@@ -4,13 +4,14 @@ module Rpush
|
|
4
4
|
extend ServiceConfigMethods
|
5
5
|
|
6
6
|
HOSTS = {
|
7
|
-
:
|
8
|
-
:
|
9
|
-
:
|
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
10
|
}
|
11
11
|
|
12
|
-
|
13
|
-
|
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.push }
|
14
15
|
end
|
15
16
|
end
|
16
17
|
end
|
@@ -4,152 +4,161 @@ module Rpush
|
|
4
4
|
extend Reflectable
|
5
5
|
include Reflectable
|
6
6
|
include Loggable
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
end
|
7
|
+
extend Loggable
|
8
|
+
include StringHelpers
|
9
|
+
extend StringHelpers
|
11
10
|
|
12
11
|
@runners = {}
|
13
12
|
|
14
13
|
def self.enqueue(notifications)
|
15
14
|
notifications.group_by(&:app_id).each do |app_id, group|
|
16
|
-
|
17
|
-
if
|
18
|
-
app.enqueue(batch)
|
19
|
-
else
|
20
|
-
Rpush.logger.error("No such app '#{app_id}' for notifications #{batch.describe}.")
|
21
|
-
end
|
15
|
+
start_app_with_id(app_id) unless @runners[app_id]
|
16
|
+
@runners[app_id].enqueue(group) if @runners[app_id]
|
22
17
|
end
|
18
|
+
|
19
|
+
ProcTitle.update
|
23
20
|
end
|
24
21
|
|
25
|
-
def self.
|
26
|
-
|
27
|
-
apps.each { |app| sync_app(app) }
|
28
|
-
removed = runners.keys - apps.map(&:id)
|
29
|
-
removed.each { |app_id| runners.delete(app_id).stop }
|
22
|
+
def self.start_app_with_id(app_id)
|
23
|
+
start_app(Rpush::Daemon.store.app(app_id))
|
30
24
|
end
|
31
25
|
|
32
|
-
def self.
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
26
|
+
def self.start_app(app)
|
27
|
+
@runners[app.id] = new(app)
|
28
|
+
@runners[app.id].start
|
29
|
+
log_info("[#{app.name}] Started, #{pluralize(app.connections, 'dispatcher')}.")
|
30
|
+
rescue StandardError => e
|
31
|
+
@runners.delete(app.id)
|
32
|
+
Rpush.logger.error("[#{app.name}] Exception raised during startup. Notifications will not be delivered for this app.")
|
33
|
+
Rpush.logger.error(e)
|
34
|
+
reflect(:error, e)
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.stop_app(app_id)
|
38
|
+
runner = @runners.delete(app_id)
|
39
|
+
if runner
|
40
|
+
runner.stop
|
41
|
+
log_info("[#{runner.app.name}] Stopped.")
|
45
42
|
end
|
46
43
|
end
|
47
44
|
|
45
|
+
def self.app_with_id(app_id)
|
46
|
+
@runners[app_id].app
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.app_running?(app)
|
50
|
+
@runners.key?(app.id)
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.app_ids
|
54
|
+
@runners.keys
|
55
|
+
end
|
56
|
+
|
48
57
|
def self.stop
|
49
|
-
runners.values.map(&:stop)
|
50
|
-
runners.clear
|
58
|
+
@runners.values.map(&:stop)
|
59
|
+
@runners.clear
|
51
60
|
end
|
52
61
|
|
53
|
-
def self.
|
54
|
-
runners.values.
|
62
|
+
def self.total_dispatchers
|
63
|
+
@runners.values.sum(&:num_dispatcher_loops)
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.total_queued
|
67
|
+
@runners.values.sum(&:queue_size)
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.num_dispatchers_for_app(app)
|
71
|
+
runner = @runners[app.id]
|
72
|
+
runner ? runner.num_dispatcher_loops : 0
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.decrement_dispatchers(app, num)
|
76
|
+
@runners[app.id].decrement_dispatchers(num)
|
55
77
|
end
|
56
78
|
|
57
|
-
def self.
|
58
|
-
runners.
|
79
|
+
def self.increment_dispatchers(app, num)
|
80
|
+
@runners[app.id].increment_dispatchers(num)
|
59
81
|
end
|
60
82
|
|
61
|
-
def self.
|
62
|
-
|
83
|
+
def self.debug
|
84
|
+
@runners.values.map(&:debug)
|
63
85
|
end
|
64
86
|
|
65
87
|
attr_reader :app
|
66
|
-
attr_accessor :batch
|
67
88
|
|
68
89
|
def initialize(app)
|
69
90
|
@app = app
|
70
91
|
@loops = []
|
92
|
+
@dispatcher_loops = []
|
71
93
|
end
|
72
94
|
|
73
95
|
def start
|
74
|
-
app.connections.times {
|
96
|
+
app.connections.times { @dispatcher_loops.push(new_dispatcher_loop) }
|
75
97
|
start_loops
|
76
|
-
log_info("Started, #{dispatchers_str}.")
|
77
98
|
end
|
78
99
|
|
79
100
|
def stop
|
80
|
-
|
101
|
+
wait_until_idle
|
102
|
+
stop_dispatcher_loops
|
81
103
|
stop_loops
|
82
104
|
end
|
83
105
|
|
84
|
-
def
|
85
|
-
|
86
|
-
batch.notifications.each do |notification|
|
87
|
-
queue.push([notification, batch])
|
88
|
-
reflect(:notification_enqueued, notification)
|
89
|
-
end
|
106
|
+
def wait_until_idle
|
107
|
+
sleep 0.5 while queue.size > 0
|
90
108
|
end
|
91
109
|
|
92
|
-
def
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
110
|
+
def enqueue(notifications)
|
111
|
+
if service.batch_deliveries?
|
112
|
+
batch_size = (notifications.size / num_dispatcher_loops.to_f).ceil
|
113
|
+
notifications.in_groups_of(batch_size, false).each do |batch_notifications|
|
114
|
+
batch = Batch.new(batch_notifications)
|
115
|
+
queue.push(QueuePayload.new(batch))
|
116
|
+
end
|
99
117
|
else
|
100
|
-
|
101
|
-
|
118
|
+
batch = Batch.new(notifications)
|
119
|
+
notifications.each do |notification|
|
120
|
+
queue.push(QueuePayload.new(batch, notification))
|
121
|
+
reflect(:notification_enqueued, notification)
|
122
|
+
end
|
102
123
|
end
|
103
124
|
end
|
104
125
|
|
105
126
|
def decrement_dispatchers(num)
|
106
|
-
num.times {
|
127
|
+
num.times { @dispatcher_loops.pop.stop }
|
107
128
|
end
|
108
129
|
|
109
130
|
def increment_dispatchers(num)
|
110
|
-
num.times {
|
131
|
+
num.times { @dispatcher_loops.push(new_dispatcher_loop) }
|
111
132
|
end
|
112
133
|
|
113
134
|
def debug
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
end
|
135
|
+
dispatcher_details = {}
|
136
|
+
|
137
|
+
@dispatcher_loops.each_with_index do |dispatcher_loop, i|
|
138
|
+
dispatcher_details[i] = {
|
139
|
+
started_at: dispatcher_loop.started_at.iso8601,
|
140
|
+
dispatched: dispatcher_loop.dispatch_count,
|
141
|
+
thread_status: dispatcher_loop.thread_status
|
142
|
+
}
|
143
|
+
end
|
124
144
|
|
125
|
-
|
126
|
-
|
145
|
+
runner_details = { dispatchers: dispatcher_details, queued: queue_size }
|
146
|
+
log_info(JSON.pretty_generate(runner_details))
|
127
147
|
end
|
128
148
|
|
129
149
|
def queue_size
|
130
150
|
queue.size
|
131
151
|
end
|
132
152
|
|
133
|
-
def
|
134
|
-
|
135
|
-
end
|
136
|
-
|
137
|
-
def batch_processed
|
138
|
-
batch ? batch.num_processed : 0
|
139
|
-
end
|
140
|
-
|
141
|
-
def num_dispatchers
|
142
|
-
dispatchers.size
|
153
|
+
def num_dispatcher_loops
|
154
|
+
@dispatcher_loops.size
|
143
155
|
end
|
144
156
|
|
145
|
-
|
157
|
+
private
|
146
158
|
|
147
159
|
def start_loops
|
148
|
-
|
149
|
-
|
150
|
-
instance.start
|
151
|
-
@loops << instance
|
152
|
-
end
|
160
|
+
@loops = service.loop_instances(@app)
|
161
|
+
@loops.map(&:start)
|
153
162
|
end
|
154
163
|
|
155
164
|
def stop_loops
|
@@ -157,31 +166,26 @@ module Rpush
|
|
157
166
|
@loops = []
|
158
167
|
end
|
159
168
|
|
169
|
+
def stop_dispatcher_loops
|
170
|
+
@dispatcher_loops.map(&:stop)
|
171
|
+
@dispatcher_loops.clear
|
172
|
+
end
|
173
|
+
|
160
174
|
def new_dispatcher_loop
|
161
|
-
dispatcher =
|
175
|
+
dispatcher = service.new_dispatcher(@app)
|
162
176
|
dispatcher_loop = Rpush::Daemon::DispatcherLoop.new(queue, dispatcher)
|
163
177
|
dispatcher_loop.start
|
164
178
|
dispatcher_loop
|
165
179
|
end
|
166
180
|
|
167
|
-
def
|
168
|
-
return @
|
169
|
-
@
|
181
|
+
def service
|
182
|
+
return @service if defined? @service
|
183
|
+
@service = "Rpush::Daemon::#{@app.service_name.camelize}".constantize
|
170
184
|
end
|
171
185
|
|
172
186
|
def queue
|
173
187
|
@queue ||= Queue.new
|
174
188
|
end
|
175
|
-
|
176
|
-
def dispatchers
|
177
|
-
@dispatchers ||= Rpush::Daemon::DispatcherLoopCollection.new
|
178
|
-
end
|
179
|
-
|
180
|
-
def dispatchers_str(count = app.connections)
|
181
|
-
count = count.abs
|
182
|
-
str = count == 1 ? 'dispatcher' : 'dispatchers'
|
183
|
-
"#{count} #{str}"
|
184
|
-
end
|
185
189
|
end
|
186
190
|
end
|
187
191
|
end
|