rpush 8.0.0 → 9.0.0
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 +12 -6
- data/README.md +8 -65
- data/lib/generators/templates/rpush.rb +9 -16
- data/lib/rpush/client/active_model.rb +0 -4
- data/lib/rpush/client/active_record.rb +0 -3
- data/lib/rpush/client/redis.rb +0 -3
- data/lib/rpush/configuration.rb +2 -19
- data/lib/rpush/daemon/service_config_methods.rb +0 -2
- data/lib/rpush/daemon/store/active_record.rb +2 -14
- data/lib/rpush/daemon/store/interface.rb +2 -2
- data/lib/rpush/daemon/store/redis.rb +2 -11
- data/lib/rpush/daemon.rb +0 -10
- data/lib/rpush/reflection_collection.rb +1 -2
- data/lib/rpush/version.rb +1 -1
- data/lib/rpush.rb +0 -1
- data/spec/functional/cli_spec.rb +41 -15
- data/spec/functional/embed_spec.rb +57 -26
- data/spec/functional/retry_spec.rb +21 -4
- data/spec/functional/synchronization_spec.rb +1 -1
- data/spec/functional_spec_helper.rb +0 -6
- data/spec/spec_helper.rb +2 -0
- data/spec/unit/client/active_record/shared/app.rb +1 -1
- data/spec/unit/daemon/shared/store.rb +0 -42
- metadata +49 -55
- data/lib/rpush/apns_feedback.rb +0 -18
- data/lib/rpush/client/active_model/gcm/app.rb +0 -19
- data/lib/rpush/client/active_model/gcm/expiry_collapse_key_mutual_inclusion_validator.rb +0 -14
- data/lib/rpush/client/active_model/gcm/notification.rb +0 -62
- data/lib/rpush/client/active_record/gcm/app.rb +0 -11
- data/lib/rpush/client/active_record/gcm/notification.rb +0 -11
- data/lib/rpush/client/redis/gcm/app.rb +0 -11
- data/lib/rpush/client/redis/gcm/notification.rb +0 -11
- data/lib/rpush/daemon/apns/delivery.rb +0 -43
- data/lib/rpush/daemon/apns/feedback_receiver.rb +0 -91
- data/lib/rpush/daemon/apns.rb +0 -17
- data/lib/rpush/daemon/dispatcher/apns_tcp.rb +0 -152
- data/lib/rpush/daemon/dispatcher/tcp.rb +0 -22
- data/lib/rpush/daemon/gcm/delivery.rb +0 -241
- data/lib/rpush/daemon/gcm.rb +0 -9
- data/lib/rpush/daemon/tcp_connection.rb +0 -190
- data/spec/functional/apns_spec.rb +0 -162
- data/spec/functional/gcm_priority_spec.rb +0 -40
- data/spec/functional/gcm_spec.rb +0 -46
- data/spec/functional/new_app_spec.rb +0 -44
- data/spec/unit/apns_feedback_spec.rb +0 -39
- data/spec/unit/client/active_record/gcm/app_spec.rb +0 -6
- data/spec/unit/client/active_record/gcm/notification_spec.rb +0 -14
- data/spec/unit/client/redis/gcm/app_spec.rb +0 -5
- data/spec/unit/client/redis/gcm/notification_spec.rb +0 -5
- data/spec/unit/client/shared/gcm/app.rb +0 -4
- data/spec/unit/client/shared/gcm/notification.rb +0 -77
- data/spec/unit/daemon/apns/delivery_spec.rb +0 -108
- data/spec/unit/daemon/apns/feedback_receiver_spec.rb +0 -137
- data/spec/unit/daemon/dispatcher/tcp_spec.rb +0 -32
- data/spec/unit/daemon/gcm/delivery_spec.rb +0 -387
- data/spec/unit/daemon/tcp_connection_spec.rb +0 -293
@@ -1,43 +0,0 @@
|
|
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
|
@@ -1,91 +0,0 @@
|
|
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
|
-
return unless @app.feedback_enabled
|
28
|
-
Rpush.logger.info("[#{@app.name}] Starting feedback receiver... ", true)
|
29
|
-
|
30
|
-
@thread = Thread.new do
|
31
|
-
loop do
|
32
|
-
break if @stop
|
33
|
-
check_for_feedback
|
34
|
-
@interruptible_sleep.sleep(Rpush.config.apns.feedback_receiver.frequency)
|
35
|
-
end
|
36
|
-
|
37
|
-
Rpush::Daemon.store.release_connection
|
38
|
-
end
|
39
|
-
|
40
|
-
puts Rainbow('✔').green if Rpush.config.foreground && Rpush.config.foreground_logging
|
41
|
-
end
|
42
|
-
|
43
|
-
def stop
|
44
|
-
@stop = true
|
45
|
-
@interruptible_sleep.stop
|
46
|
-
@thread.join if @thread
|
47
|
-
rescue StandardError => e
|
48
|
-
log_error(e)
|
49
|
-
reflect(:error, e)
|
50
|
-
ensure
|
51
|
-
@thread = nil
|
52
|
-
end
|
53
|
-
|
54
|
-
def check_for_feedback
|
55
|
-
connection = nil
|
56
|
-
begin
|
57
|
-
connection = Rpush::Daemon::TcpConnection.new(@app, @host, @port)
|
58
|
-
connection.connect
|
59
|
-
tuple = connection.read(TUPLE_BYTES)
|
60
|
-
|
61
|
-
while tuple
|
62
|
-
timestamp, device_token = parse_tuple(tuple)
|
63
|
-
create_feedback(timestamp, device_token)
|
64
|
-
tuple = connection.read(TUPLE_BYTES)
|
65
|
-
end
|
66
|
-
rescue StandardError => e
|
67
|
-
log_error(e)
|
68
|
-
reflect(:error, e)
|
69
|
-
ensure
|
70
|
-
connection.close if connection
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
protected
|
75
|
-
|
76
|
-
def parse_tuple(tuple)
|
77
|
-
failed_at, _, device_token = tuple.unpack("N1n1H*")
|
78
|
-
[Time.at(failed_at).utc, device_token]
|
79
|
-
end
|
80
|
-
|
81
|
-
def create_feedback(failed_at, device_token)
|
82
|
-
formatted_failed_at = failed_at.strftime('%Y-%m-%d %H:%M:%S UTC')
|
83
|
-
log_info("[FeedbackReceiver] Delivery failed at #{formatted_failed_at} for #{device_token}.")
|
84
|
-
|
85
|
-
feedback = Rpush::Daemon.store.create_apns_feedback(failed_at, device_token, @app)
|
86
|
-
reflect(:apns_feedback, feedback)
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
91
|
-
end
|
data/lib/rpush/daemon/apns.rb
DELETED
@@ -1,17 +0,0 @@
|
|
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
|
@@ -1,152 +0,0 @@
|
|
1
|
-
module Rpush
|
2
|
-
module Daemon
|
3
|
-
module Dispatcher
|
4
|
-
class ApnsTcp < Rpush::Daemon::Dispatcher::Tcp
|
5
|
-
include Loggable
|
6
|
-
include Reflectable
|
7
|
-
|
8
|
-
SELECT_TIMEOUT = 10
|
9
|
-
ERROR_TUPLE_BYTES = 6
|
10
|
-
APNS_ERRORS = {
|
11
|
-
1 => 'Processing error',
|
12
|
-
2 => 'Missing device token',
|
13
|
-
3 => 'Missing topic',
|
14
|
-
4 => 'Missing payload',
|
15
|
-
5 => 'Missing token size',
|
16
|
-
6 => 'Missing topic size',
|
17
|
-
7 => 'Missing payload size',
|
18
|
-
8 => 'Invalid device token',
|
19
|
-
10 => 'APNs closed connection (possible maintenance)',
|
20
|
-
255 => 'None (unknown error)'
|
21
|
-
}
|
22
|
-
|
23
|
-
def initialize(*args)
|
24
|
-
super
|
25
|
-
@dispatch_mutex = Mutex.new
|
26
|
-
@stop_error_receiver = false
|
27
|
-
@connection.on_connect { start_error_receiver }
|
28
|
-
end
|
29
|
-
|
30
|
-
def dispatch(payload)
|
31
|
-
@dispatch_mutex.synchronize do
|
32
|
-
@delivery_class.new(@app, @connection, payload.batch).perform
|
33
|
-
record_batch(payload.batch)
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
def cleanup
|
38
|
-
if Rpush.config.push
|
39
|
-
# In push mode only a single batch is sent, followed by immediate shutdown.
|
40
|
-
# Allow the error receiver time to handle any errors.
|
41
|
-
@reconnect_disabled = true
|
42
|
-
sleep 1
|
43
|
-
end
|
44
|
-
|
45
|
-
@stop_error_receiver = true
|
46
|
-
super
|
47
|
-
@error_receiver_thread.join if @error_receiver_thread
|
48
|
-
rescue StandardError => e
|
49
|
-
log_error(e)
|
50
|
-
reflect(:error, e)
|
51
|
-
ensure
|
52
|
-
@error_receiver_thread = nil
|
53
|
-
end
|
54
|
-
|
55
|
-
private
|
56
|
-
|
57
|
-
def start_error_receiver
|
58
|
-
@error_receiver_thread = Thread.new do
|
59
|
-
check_for_error until @stop_error_receiver
|
60
|
-
Rpush::Daemon.store.release_connection
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
def delivered_buffer
|
65
|
-
@delivered_buffer ||= RingBuffer.new(Rpush.config.batch_size * 10)
|
66
|
-
end
|
67
|
-
|
68
|
-
def record_batch(batch)
|
69
|
-
batch.each_delivered do |notification|
|
70
|
-
delivered_buffer << notification.id
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
def check_for_error
|
75
|
-
begin
|
76
|
-
# On Linux, select returns nil from a dropped connection.
|
77
|
-
# On OS X, Errno::EBADF is raised following a Errno::EADDRNOTAVAIL from the write call.
|
78
|
-
return unless @connection.select(SELECT_TIMEOUT)
|
79
|
-
tuple = @connection.read(ERROR_TUPLE_BYTES)
|
80
|
-
rescue *TcpConnection::TCP_ERRORS
|
81
|
-
reconnect unless @stop_error_receiver
|
82
|
-
return
|
83
|
-
end
|
84
|
-
|
85
|
-
@dispatch_mutex.synchronize { handle_error_response(tuple) }
|
86
|
-
rescue StandardError => e
|
87
|
-
log_error(e)
|
88
|
-
end
|
89
|
-
|
90
|
-
def handle_error_response(tuple)
|
91
|
-
if tuple
|
92
|
-
_, code, notification_id = tuple.unpack('ccN')
|
93
|
-
handle_error(code, notification_id)
|
94
|
-
else
|
95
|
-
handle_disconnect
|
96
|
-
end
|
97
|
-
|
98
|
-
if Rpush.config.push
|
99
|
-
# Only attempt to handle a single error in Push mode.
|
100
|
-
@stop_error_receiver = true
|
101
|
-
return
|
102
|
-
end
|
103
|
-
|
104
|
-
reconnect
|
105
|
-
ensure
|
106
|
-
delivered_buffer.clear
|
107
|
-
end
|
108
|
-
|
109
|
-
def reconnect
|
110
|
-
return if @reconnect_disabled
|
111
|
-
log_error("Lost connection to #{@connection.host}:#{@connection.port}, reconnecting...")
|
112
|
-
@connection.reconnect_with_rescue
|
113
|
-
end
|
114
|
-
|
115
|
-
def handle_disconnect
|
116
|
-
log_error("The APNs disconnected before any notifications could be delivered. This usually indicates you are using an invalid certificate.") if delivered_buffer.size == 0
|
117
|
-
end
|
118
|
-
|
119
|
-
def handle_error(code, notification_id)
|
120
|
-
notification_id = Rpush::Daemon.store.translate_integer_notification_id(notification_id)
|
121
|
-
failed_pos = delivered_buffer.index(notification_id)
|
122
|
-
description = description_for_code(code)
|
123
|
-
log_error("Notification #{notification_id} failed with error: " + description)
|
124
|
-
Rpush::Daemon.store.mark_ids_failed([notification_id], code, description, Time.now)
|
125
|
-
reflect(:notification_id_failed, @app, notification_id, code, description)
|
126
|
-
|
127
|
-
if failed_pos
|
128
|
-
retry_ids = delivered_buffer[(failed_pos + 1)..-1]
|
129
|
-
retry_notification_ids(retry_ids, notification_id)
|
130
|
-
elsif delivered_buffer.size > 0
|
131
|
-
log_error("Delivery sequence unknown for notifications following #{notification_id}.")
|
132
|
-
end
|
133
|
-
end
|
134
|
-
|
135
|
-
def description_for_code(code)
|
136
|
-
APNS_ERRORS[code.to_i] ? "#{APNS_ERRORS[code.to_i]} (#{code})" : "Unknown error code #{code.inspect}. Possible Rpush bug?"
|
137
|
-
end
|
138
|
-
|
139
|
-
def retry_notification_ids(ids, notification_id)
|
140
|
-
return if ids.size == 0
|
141
|
-
|
142
|
-
now = Time.now
|
143
|
-
Rpush::Daemon.store.mark_ids_retryable(ids, now)
|
144
|
-
notifications_str = 'Notification'
|
145
|
-
notifications_str += 's' if ids.size > 1
|
146
|
-
log_warn("#{notifications_str} #{ids.join(', ')} will be retried due to the failure of notification #{notification_id}.")
|
147
|
-
ids.each { |id| reflect(:notification_id_will_retry, @app, id, now) }
|
148
|
-
end
|
149
|
-
end
|
150
|
-
end
|
151
|
-
end
|
152
|
-
end
|
@@ -1,22 +0,0 @@
|
|
1
|
-
module Rpush
|
2
|
-
module Daemon
|
3
|
-
module Dispatcher
|
4
|
-
class Tcp
|
5
|
-
def initialize(app, delivery_class, options = {})
|
6
|
-
@app = app
|
7
|
-
@delivery_class = delivery_class
|
8
|
-
@host, @port = options[:host].call(@app)
|
9
|
-
@connection = Rpush::Daemon::TcpConnection.new(@app, @host, @port)
|
10
|
-
end
|
11
|
-
|
12
|
-
def dispatch(payload)
|
13
|
-
@delivery_class.new(@app, @connection, payload.notification, payload.batch).perform
|
14
|
-
end
|
15
|
-
|
16
|
-
def cleanup
|
17
|
-
@connection.close if @connection
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
@@ -1,241 +0,0 @@
|
|
1
|
-
module Rpush
|
2
|
-
module Daemon
|
3
|
-
module Gcm
|
4
|
-
# https://firebase.google.com/docs/cloud-messaging/server
|
5
|
-
class Delivery < Rpush::Daemon::Delivery
|
6
|
-
include MultiJsonHelper
|
7
|
-
|
8
|
-
host = 'https://fcm.googleapis.com'
|
9
|
-
FCM_URI = URI.parse("#{host}/fcm/send")
|
10
|
-
UNAVAILABLE_STATES = %w(Unavailable BadGateway InternalServerError)
|
11
|
-
INVALID_REGISTRATION_ID_STATES = %w(InvalidRegistration MismatchSenderId NotRegistered InvalidPackageName)
|
12
|
-
|
13
|
-
def initialize(app, http, notification, batch)
|
14
|
-
@app = app
|
15
|
-
@http = http
|
16
|
-
@notification = notification
|
17
|
-
@batch = batch
|
18
|
-
end
|
19
|
-
|
20
|
-
def perform
|
21
|
-
handle_response(do_post)
|
22
|
-
rescue SocketError => error
|
23
|
-
mark_retryable(@notification, Time.now + 10.seconds, error)
|
24
|
-
raise
|
25
|
-
rescue StandardError => error
|
26
|
-
mark_failed(error)
|
27
|
-
raise
|
28
|
-
ensure
|
29
|
-
@batch.notification_processed
|
30
|
-
end
|
31
|
-
|
32
|
-
protected
|
33
|
-
|
34
|
-
def handle_response(response)
|
35
|
-
case response.code.to_i
|
36
|
-
when 200
|
37
|
-
ok(response)
|
38
|
-
when 400
|
39
|
-
bad_request
|
40
|
-
when 401
|
41
|
-
unauthorized
|
42
|
-
when 500
|
43
|
-
internal_server_error(response)
|
44
|
-
when 502
|
45
|
-
bad_gateway(response)
|
46
|
-
when 503
|
47
|
-
service_unavailable(response)
|
48
|
-
when 500..599
|
49
|
-
other_5xx_error(response)
|
50
|
-
else
|
51
|
-
fail Rpush::DeliveryError.new(response.code.to_i, @notification.id, Rpush::Daemon::HTTP_STATUS_CODES[response.code.to_i])
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
def ok(response)
|
56
|
-
results = process_response(response)
|
57
|
-
handle_successes(results.successes)
|
58
|
-
|
59
|
-
if results.failures.any?
|
60
|
-
handle_failures(results.failures, response)
|
61
|
-
else
|
62
|
-
mark_delivered
|
63
|
-
log_info("#{@notification.id} sent to #{@notification.registration_ids.join(', ')}")
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
def process_response(response)
|
68
|
-
body = multi_json_load(response.body)
|
69
|
-
results = Results.new(body['results'], @notification.registration_ids)
|
70
|
-
results.process(invalid: INVALID_REGISTRATION_ID_STATES, unavailable: UNAVAILABLE_STATES)
|
71
|
-
results
|
72
|
-
end
|
73
|
-
|
74
|
-
def handle_successes(successes)
|
75
|
-
successes.each do |result|
|
76
|
-
reflect(:gcm_delivered_to_recipient, @notification, result[:registration_id])
|
77
|
-
next unless result.key?(:canonical_id)
|
78
|
-
reflect(:gcm_canonical_id, result[:registration_id], result[:canonical_id])
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
def handle_failures(failures, response)
|
83
|
-
if failures[:unavailable].count == @notification.registration_ids.count
|
84
|
-
retry_delivery(@notification, response)
|
85
|
-
log_warn("All recipients unavailable. #{retry_message}")
|
86
|
-
else
|
87
|
-
if failures[:unavailable].any?
|
88
|
-
unavailable_idxs = failures[:unavailable].map { |result| result[:index] }
|
89
|
-
new_notification = create_new_notification(response, unavailable_idxs)
|
90
|
-
failures.description += " #{unavailable_idxs.join(', ')} will be retried as notification #{new_notification.id}."
|
91
|
-
end
|
92
|
-
handle_errors(failures)
|
93
|
-
fail Rpush::DeliveryError.new(nil, @notification.id, failures.description)
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
def handle_errors(failures)
|
98
|
-
failures.each do |result|
|
99
|
-
reflect(:gcm_failed_to_recipient, @notification, result[:error], result[:registration_id])
|
100
|
-
end
|
101
|
-
failures[:invalid].each do |result|
|
102
|
-
reflect(:gcm_invalid_registration_id, @app, result[:error], result[:registration_id])
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
def create_new_notification(response, unavailable_idxs)
|
107
|
-
attrs = { 'app_id' => @notification.app_id, 'collapse_key' => @notification.collapse_key, 'delay_while_idle' => @notification.delay_while_idle }
|
108
|
-
registration_ids = @notification.registration_ids.values_at(*unavailable_idxs)
|
109
|
-
Rpush::Daemon.store.create_gcm_notification(attrs, @notification.data,
|
110
|
-
registration_ids, deliver_after_header(response), @app)
|
111
|
-
end
|
112
|
-
|
113
|
-
def bad_request
|
114
|
-
fail Rpush::DeliveryError.new(400, @notification.id, 'GCM failed to parse the JSON request. Possibly an Rpush bug, please open an issue.')
|
115
|
-
end
|
116
|
-
|
117
|
-
def unauthorized
|
118
|
-
fail Rpush::DeliveryError.new(401, @notification.id, 'Unauthorized, check your App auth_key.')
|
119
|
-
end
|
120
|
-
|
121
|
-
def internal_server_error(response)
|
122
|
-
retry_delivery(@notification, response)
|
123
|
-
log_warn("GCM responded with an Internal Error. " + retry_message)
|
124
|
-
end
|
125
|
-
|
126
|
-
def bad_gateway(response)
|
127
|
-
retry_delivery(@notification, response)
|
128
|
-
log_warn("GCM responded with a Bad Gateway Error. " + retry_message)
|
129
|
-
end
|
130
|
-
|
131
|
-
def service_unavailable(response)
|
132
|
-
retry_delivery(@notification, response)
|
133
|
-
log_warn("GCM responded with an Service Unavailable Error. " + retry_message)
|
134
|
-
end
|
135
|
-
|
136
|
-
def other_5xx_error(response)
|
137
|
-
retry_delivery(@notification, response)
|
138
|
-
log_warn("GCM responded with a 5xx Error. " + retry_message)
|
139
|
-
end
|
140
|
-
|
141
|
-
def deliver_after_header(response)
|
142
|
-
Rpush::Daemon::RetryHeaderParser.parse(response.header['retry-after'])
|
143
|
-
end
|
144
|
-
|
145
|
-
def retry_delivery(notification, response)
|
146
|
-
time = deliver_after_header(response)
|
147
|
-
if time
|
148
|
-
mark_retryable(notification, time)
|
149
|
-
else
|
150
|
-
mark_retryable_exponential(notification)
|
151
|
-
end
|
152
|
-
end
|
153
|
-
|
154
|
-
def retry_message
|
155
|
-
"Notification #{@notification.id} will be retried after #{@notification.deliver_after.strftime('%Y-%m-%d %H:%M:%S')} (retry #{@notification.retries})."
|
156
|
-
end
|
157
|
-
|
158
|
-
def do_post
|
159
|
-
post = Net::HTTP::Post.new(FCM_URI.path, 'Content-Type' => 'application/json',
|
160
|
-
'Authorization' => "key=#{@app.auth_key}")
|
161
|
-
post.body = @notification.as_json.to_json
|
162
|
-
@http.request(FCM_URI, post)
|
163
|
-
end
|
164
|
-
end
|
165
|
-
|
166
|
-
class Results
|
167
|
-
attr_reader :successes, :failures
|
168
|
-
|
169
|
-
def initialize(results_data, registration_ids)
|
170
|
-
@results_data = results_data
|
171
|
-
@registration_ids = registration_ids
|
172
|
-
end
|
173
|
-
|
174
|
-
def process(failure_partitions = {}) # rubocop:disable Metrics/AbcSize
|
175
|
-
@successes = []
|
176
|
-
@failures = Failures.new
|
177
|
-
failure_partitions.each_key do |category|
|
178
|
-
failures[category] = []
|
179
|
-
end
|
180
|
-
|
181
|
-
@results_data.each_with_index do |result, index|
|
182
|
-
entry = {
|
183
|
-
registration_id: @registration_ids[index],
|
184
|
-
index: index
|
185
|
-
}
|
186
|
-
if result['message_id']
|
187
|
-
entry[:canonical_id] = result['registration_id'] if result['registration_id'].present?
|
188
|
-
successes << entry
|
189
|
-
elsif result['error']
|
190
|
-
entry[:error] = result['error']
|
191
|
-
failures << entry
|
192
|
-
failure_partitions.each do |category, error_states|
|
193
|
-
failures[category] << entry if error_states.include?(result['error'])
|
194
|
-
end
|
195
|
-
end
|
196
|
-
end
|
197
|
-
failures.all_failed = failures.count == @registration_ids.count
|
198
|
-
end
|
199
|
-
end
|
200
|
-
|
201
|
-
class Failures < Hash
|
202
|
-
include Enumerable
|
203
|
-
attr_writer :all_failed, :description
|
204
|
-
|
205
|
-
def initialize
|
206
|
-
super[:all] = []
|
207
|
-
end
|
208
|
-
|
209
|
-
def each
|
210
|
-
self[:all].each { |x| yield x }
|
211
|
-
end
|
212
|
-
|
213
|
-
def <<(item)
|
214
|
-
self[:all] << item
|
215
|
-
end
|
216
|
-
|
217
|
-
def description
|
218
|
-
@description ||= describe
|
219
|
-
end
|
220
|
-
|
221
|
-
def any?
|
222
|
-
self[:all].any?
|
223
|
-
end
|
224
|
-
|
225
|
-
private
|
226
|
-
|
227
|
-
def describe
|
228
|
-
if @all_failed
|
229
|
-
error_description = "Failed to deliver to all recipients."
|
230
|
-
else
|
231
|
-
index_list = map { |item| item[:index] }
|
232
|
-
error_description = "Failed to deliver to recipients #{index_list.join(', ')}."
|
233
|
-
end
|
234
|
-
|
235
|
-
error_list = map { |item| item[:error] }
|
236
|
-
error_description + " Errors: #{error_list.join(', ')}."
|
237
|
-
end
|
238
|
-
end
|
239
|
-
end
|
240
|
-
end
|
241
|
-
end
|