rpush 2.7.0 → 3.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +41 -0
- data/README.md +36 -15
- data/lib/generators/rpush_migration_generator.rb +1 -0
- data/lib/generators/templates/add_adm.rb +1 -1
- data/lib/generators/templates/add_alert_is_json_to_rapns_notifications.rb +1 -1
- data/lib/generators/templates/add_app_to_rapns.rb +1 -1
- data/lib/generators/templates/add_fail_after_to_rpush_notifications.rb +1 -1
- data/lib/generators/templates/add_gcm.rb +1 -1
- data/lib/generators/templates/add_rpush.rb +11 -11
- data/lib/generators/templates/add_wpns.rb +1 -1
- data/lib/generators/templates/create_rapns_apps.rb +1 -1
- data/lib/generators/templates/create_rapns_feedback.rb +1 -1
- data/lib/generators/templates/create_rapns_notifications.rb +1 -1
- data/lib/generators/templates/rename_rapns_to_rpush.rb +1 -1
- data/lib/generators/templates/rpush.rb +1 -1
- data/lib/generators/templates/rpush_2_0_0_updates.rb +1 -1
- data/lib/generators/templates/rpush_2_1_0_updates.rb +1 -1
- data/lib/generators/templates/rpush_2_6_0_updates.rb +1 -1
- data/lib/generators/templates/rpush_2_7_0_updates.rb +1 -1
- data/lib/generators/templates/rpush_3_0_0_updates.rb +11 -0
- data/lib/rpush.rb +2 -9
- data/lib/rpush/apns_feedback.rb +4 -0
- data/lib/rpush/cli.rb +2 -2
- data/lib/rpush/client/active_model.rb +3 -0
- data/lib/rpush/client/active_model/apns/notification.rb +11 -1
- 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_record.rb +3 -0
- data/lib/rpush/client/active_record/apns/feedback.rb +0 -4
- 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/app.rb +0 -4
- data/lib/rpush/client/active_record/notification.rb +0 -7
- data/lib/rpush/client/redis.rb +3 -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/notification.rb +1 -0
- data/lib/rpush/daemon.rb +5 -3
- data/lib/rpush/daemon/apns2.rb +10 -0
- data/lib/rpush/daemon/apns2/delivery.rb +127 -0
- data/lib/rpush/daemon/dispatcher/apns_http2.rb +50 -0
- data/lib/rpush/daemon/dispatcher/apns_tcp.rb +1 -1
- data/lib/rpush/daemon/dispatcher/http.rb +1 -1
- data/lib/rpush/daemon/gcm/delivery.rb +5 -5
- data/lib/rpush/daemon/service_config_methods.rb +4 -3
- data/lib/rpush/daemon/store/active_record/reconnectable.rb +11 -3
- data/lib/rpush/daemon/synchronizer.rb +14 -12
- data/lib/rpush/version.rb +12 -1
- data/spec/functional/apns2_spec.rb +232 -0
- data/spec/functional/apns_spec.rb +1 -2
- data/spec/functional/synchronization_spec.rb +29 -0
- data/spec/spec_helper.rb +0 -5
- data/spec/support/active_record_setup.rb +2 -1
- data/spec/unit/apns_feedback_spec.rb +9 -2
- data/spec/unit/client/active_record/apns/notification_spec.rb +34 -2
- data/spec/unit/daemon/store/active_record/reconnectable_spec.rb +30 -0
- data/spec/unit_spec_helper.rb +2 -21
- metadata +256 -29
- data/lib/rpush/client/mongoid.rb +0 -36
- data/lib/rpush/client/mongoid/adm/app.rb +0 -14
- data/lib/rpush/client/mongoid/adm/notification.rb +0 -11
- data/lib/rpush/client/mongoid/apns/app.rb +0 -11
- data/lib/rpush/client/mongoid/apns/feedback.rb +0 -24
- data/lib/rpush/client/mongoid/apns/notification.rb +0 -15
- data/lib/rpush/client/mongoid/app.rb +0 -23
- data/lib/rpush/client/mongoid/gcm/app.rb +0 -11
- data/lib/rpush/client/mongoid/gcm/notification.rb +0 -11
- data/lib/rpush/client/mongoid/notification.rb +0 -51
- data/lib/rpush/client/mongoid/wns/app.rb +0 -14
- data/lib/rpush/client/mongoid/wns/badge_notification.rb +0 -15
- data/lib/rpush/client/mongoid/wns/notification.rb +0 -11
- data/lib/rpush/client/mongoid/wns/raw_notification.rb +0 -11
- data/lib/rpush/client/mongoid/wpns/app.rb +0 -11
- data/lib/rpush/client/mongoid/wpns/notification.rb +0 -11
- data/lib/rpush/daemon/store/mongoid.rb +0 -157
- data/spec/support/config/mongoid.yml +0 -69
- data/spec/support/mongoid_setup.rb +0 -10
- data/spec/unit/daemon/store/mongoid_spec.rb +0 -339
@@ -9,6 +9,9 @@ require 'rpush/client/active_model/apns/device_token_format_validator'
|
|
9
9
|
require 'rpush/client/active_model/apns/app'
|
10
10
|
require 'rpush/client/active_model/apns/notification'
|
11
11
|
|
12
|
+
require 'rpush/client/active_model/apns2/app'
|
13
|
+
require 'rpush/client/active_model/apns2/notification'
|
14
|
+
|
12
15
|
require 'rpush/client/active_model/adm/data_validator'
|
13
16
|
require 'rpush/client/active_model/adm/app'
|
14
17
|
require 'rpush/client/active_model/adm/notification'
|
@@ -32,6 +32,12 @@ module Rpush
|
|
32
32
|
self.data = (data || {}).merge(MDM_KEY => magic)
|
33
33
|
end
|
34
34
|
|
35
|
+
MUTABLE_CONTENT_KEY = '__rpush_mutable_content__'
|
36
|
+
def mutable_content=(bool)
|
37
|
+
return unless bool
|
38
|
+
self.data = (data || {}).merge(MUTABLE_CONTENT_KEY => true)
|
39
|
+
end
|
40
|
+
|
35
41
|
CONTENT_AVAILABLE_KEY = '__rpush_content_available__'
|
36
42
|
def content_available=(bool)
|
37
43
|
return unless bool
|
@@ -51,12 +57,16 @@ module Rpush
|
|
51
57
|
json['aps']['category'] = category if category
|
52
58
|
json['aps']['url-args'] = url_args if url_args
|
53
59
|
|
60
|
+
if data && data[MUTABLE_CONTENT_KEY]
|
61
|
+
json['aps']['mutable-content'] = 1
|
62
|
+
end
|
63
|
+
|
54
64
|
if data && data[CONTENT_AVAILABLE_KEY]
|
55
65
|
json['aps']['content-available'] = 1
|
56
66
|
end
|
57
67
|
|
58
68
|
if data
|
59
|
-
non_aps_attributes = data.reject { |k, _| k == CONTENT_AVAILABLE_KEY }
|
69
|
+
non_aps_attributes = data.reject { |k, _| k == CONTENT_AVAILABLE_KEY || k == MUTABLE_CONTENT_KEY }
|
60
70
|
non_aps_attributes.each { |k, v| json[k.to_s] = v }
|
61
71
|
end
|
62
72
|
end
|
@@ -9,6 +9,9 @@ require 'rpush/client/active_record/apns/notification'
|
|
9
9
|
require 'rpush/client/active_record/apns/feedback'
|
10
10
|
require 'rpush/client/active_record/apns/app'
|
11
11
|
|
12
|
+
require 'rpush/client/active_record/apns2/notification'
|
13
|
+
require 'rpush/client/active_record/apns2/app'
|
14
|
+
|
12
15
|
require 'rpush/client/active_record/gcm/notification'
|
13
16
|
require 'rpush/client/active_record/gcm/app'
|
14
17
|
|
@@ -5,10 +5,6 @@ module Rpush
|
|
5
5
|
class Feedback < ::ActiveRecord::Base
|
6
6
|
self.table_name = 'rpush_feedback'
|
7
7
|
|
8
|
-
if Rpush.attr_accessible_available?
|
9
|
-
attr_accessible :device_token, :failed_at, :app_id
|
10
|
-
end
|
11
|
-
|
12
8
|
belongs_to :app, class_name: 'Rpush::Client::ActiveRecord::App'
|
13
9
|
|
14
10
|
validates :device_token, presence: true
|
@@ -4,10 +4,6 @@ module Rpush
|
|
4
4
|
class App < ::ActiveRecord::Base
|
5
5
|
self.table_name = 'rpush_apps'
|
6
6
|
|
7
|
-
if Rpush.attr_accessible_available?
|
8
|
-
attr_accessible :name, :environment, :certificate, :password, :connections, :auth_key, :client_id, :client_secret
|
9
|
-
end
|
10
|
-
|
11
7
|
has_many :notifications, class_name: 'Rpush::Client::ActiveRecord::Notification', dependent: :destroy
|
12
8
|
|
13
9
|
validates :name, presence: true, uniqueness: { scope: [:type, :environment] }
|
@@ -12,13 +12,6 @@ module Rpush
|
|
12
12
|
|
13
13
|
belongs_to :app, class_name: 'Rpush::Client::ActiveRecord::App'
|
14
14
|
|
15
|
-
if Rpush.attr_accessible_available?
|
16
|
-
attr_accessible :badge, :device_token, :sound, :alert, :data, :expiry, :delivered,
|
17
|
-
:delivered_at, :failed, :failed_at, :error_code, :error_description, :deliver_after,
|
18
|
-
:alert_is_json, :app, :app_id, :collapse_key, :delay_while_idle, :registration_ids,
|
19
|
-
:uri, :url_args, :category, :content_available, :notification
|
20
|
-
end
|
21
|
-
|
22
15
|
def data=(attrs)
|
23
16
|
return unless attrs
|
24
17
|
fail ArgumentError, 'must be a Hash' unless attrs.is_a?(Hash)
|
data/lib/rpush/client/redis.rb
CHANGED
@@ -21,6 +21,9 @@ require 'rpush/client/redis/apns/app'
|
|
21
21
|
require 'rpush/client/redis/apns/notification'
|
22
22
|
require 'rpush/client/redis/apns/feedback'
|
23
23
|
|
24
|
+
require 'rpush/client/redis/apns2/app'
|
25
|
+
require 'rpush/client/redis/apns2/notification'
|
26
|
+
|
24
27
|
require 'rpush/client/redis/gcm/app'
|
25
28
|
require 'rpush/client/redis/gcm/notification'
|
26
29
|
|
data/lib/rpush/daemon.rb
CHANGED
@@ -24,6 +24,7 @@ require 'rpush/daemon/dispatcher_loop'
|
|
24
24
|
require 'rpush/daemon/dispatcher/http'
|
25
25
|
require 'rpush/daemon/dispatcher/tcp'
|
26
26
|
require 'rpush/daemon/dispatcher/apns_tcp'
|
27
|
+
require 'rpush/daemon/dispatcher/apns_http2'
|
27
28
|
require 'rpush/daemon/service_config_methods'
|
28
29
|
require 'rpush/daemon/retry_header_parser'
|
29
30
|
require 'rpush/daemon/ring_buffer'
|
@@ -40,6 +41,9 @@ require 'rpush/daemon/apns/delivery'
|
|
40
41
|
require 'rpush/daemon/apns/feedback_receiver'
|
41
42
|
require 'rpush/daemon/apns'
|
42
43
|
|
44
|
+
require 'rpush/daemon/apns2/delivery'
|
45
|
+
require 'rpush/daemon/apns2'
|
46
|
+
|
43
47
|
require 'rpush/daemon/gcm/delivery'
|
44
48
|
require 'rpush/daemon/gcm'
|
45
49
|
|
@@ -102,9 +106,7 @@ module Rpush
|
|
102
106
|
end
|
103
107
|
|
104
108
|
def self.shutdown_lock
|
105
|
-
|
106
|
-
@shutdown_lock = Mutex.new
|
107
|
-
@shutdown_lock
|
109
|
+
@shutdown_lock ||= Mutex.new
|
108
110
|
end
|
109
111
|
|
110
112
|
def self.common_init
|
@@ -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
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Rpush
|
2
|
+
module Daemon
|
3
|
+
module Dispatcher
|
4
|
+
class ApnsHttp2
|
5
|
+
|
6
|
+
URLS = {
|
7
|
+
production: 'https://api.push.apple.com:443',
|
8
|
+
development: 'https://api.development.push.apple.com:443'
|
9
|
+
}
|
10
|
+
|
11
|
+
DEFAULT_TIMEOUT = 60
|
12
|
+
|
13
|
+
def initialize(app, delivery_class, _options = {})
|
14
|
+
@app = app
|
15
|
+
@delivery_class = delivery_class
|
16
|
+
|
17
|
+
url = URLS[app.environment.to_sym]
|
18
|
+
@client = NetHttp2::Client.new(url,
|
19
|
+
ssl_context: prepare_ssl_context,
|
20
|
+
connect_timeout: DEFAULT_TIMEOUT)
|
21
|
+
end
|
22
|
+
|
23
|
+
def dispatch(payload)
|
24
|
+
@delivery_class.new(@app, @client, payload.batch).perform
|
25
|
+
end
|
26
|
+
|
27
|
+
def cleanup
|
28
|
+
@client.close
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def prepare_ssl_context
|
34
|
+
@ssl_context ||= begin
|
35
|
+
ctx = OpenSSL::SSL::SSLContext.new
|
36
|
+
begin
|
37
|
+
p12 = OpenSSL::PKCS12.new(@app.certificate, @app.password)
|
38
|
+
ctx.key = p12.key
|
39
|
+
ctx.cert = p12.certificate
|
40
|
+
rescue OpenSSL::PKCS12::PKCS12Error
|
41
|
+
ctx.key = OpenSSL::PKey::RSA.new(@app.certificate, @app.password)
|
42
|
+
ctx.cert = OpenSSL::X509::Certificate.new(@app.certificate)
|
43
|
+
end
|
44
|
+
ctx
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -36,7 +36,7 @@ module Rpush
|
|
36
36
|
|
37
37
|
def cleanup
|
38
38
|
if Rpush.config.push
|
39
|
-
# In push mode only a single batch is sent, followed
|
39
|
+
# In push mode only a single batch is sent, followed by immediate shutdown.
|
40
40
|
# Allow the error receiver time to handle any errors.
|
41
41
|
@reconnect_disabled = true
|
42
42
|
sleep 1
|
@@ -1,12 +1,12 @@
|
|
1
1
|
module Rpush
|
2
2
|
module Daemon
|
3
3
|
module Gcm
|
4
|
-
#
|
4
|
+
# https://firebase.google.com/docs/cloud-messaging/server
|
5
5
|
class Delivery < Rpush::Daemon::Delivery
|
6
6
|
include MultiJsonHelper
|
7
7
|
|
8
|
-
host = 'https://
|
9
|
-
|
8
|
+
host = 'https://fcm.googleapis.com'
|
9
|
+
FCM_URI = URI.parse("#{host}/fcm/send")
|
10
10
|
UNAVAILABLE_STATES = %w(Unavailable InternalServerError)
|
11
11
|
INVALID_REGISTRATION_ID_STATES = %w(InvalidRegistration MismatchSenderId NotRegistered InvalidPackageName)
|
12
12
|
|
@@ -142,10 +142,10 @@ module Rpush
|
|
142
142
|
end
|
143
143
|
|
144
144
|
def do_post
|
145
|
-
post = Net::HTTP::Post.new(
|
145
|
+
post = Net::HTTP::Post.new(FCM_URI.path, 'Content-Type' => 'application/json',
|
146
146
|
'Authorization' => "key=#{@notification.app.auth_key}")
|
147
147
|
post.body = @notification.as_json.to_json
|
148
|
-
@http.request(
|
148
|
+
@http.request(FCM_URI, post)
|
149
149
|
end
|
150
150
|
end
|
151
151
|
|