rpush 5.1.0 → 5.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1bd7e063c214b8ab031ca63892ec39f425813c6a1dd9f54f09cfdc5b0e6291b7
4
- data.tar.gz: '080a33b636882719f1bf2284035a5fe9d22d8073123808a9d1e3acee84622588'
3
+ metadata.gz: 6576c1456435dd596c992846892b840f204b4cf8956b13f0c06b9b336b6e0fd8
4
+ data.tar.gz: 57ba3957011570e259585b3528f0bd344411f22db72bf03cea01bfe049107ac2
5
5
  SHA512:
6
- metadata.gz: e68cf92207ac195e120063301ae6ec23217fbaf31ea46757a9a28163dcc584763e08f26e4e1cf11978bf10a232b9eabd71ef205c4817543d8065e32fc3bf0418
7
- data.tar.gz: 5bdb35aa1a78ff6f6590072581deb06e29ad51c2255b603c2ed35c985e65fdbb8b524fca80a011d0acae8038dc67579e7cb7d822f24d41ce36da81fb615b3f24
6
+ metadata.gz: 1fcce116cbddf64aacbf00d2b7294056f3c502b59f108783b53a1c90807cd0816a68be2c43e543625d556ad7de484786edb2baad1dbdc2652cfc1c4f582539f9
7
+ data.tar.gz: 0e468a1f43d8c422db967946fdc8d867bfc8bce6eaea511bff04e34108f77b75e9b0f903a3c008594482fe159ee9b820cc65be81008d60e2261b26f61c6ca274
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## [v5.2.0](https://github.com/rpush/rpush/tree/v5.2.0) (2020-10-08)
4
+
5
+ [Full Changelog](https://github.com/rpush/rpush/compare/v5.1.0...v5.2.0)
6
+
7
+ **Merged pull requests:**
8
+
9
+ - Allow opting out of foreground stdout logging [\#571](https://github.com/rpush/rpush/pull/571) ([benlangfeld](https://github.com/benlangfeld))
10
+ - Do not retry notifications which already have been delivered/failed [\#567](https://github.com/rpush/rpush/pull/567) ([AlexTatarnikov](https://github.com/AlexTatarnikov))
11
+ - Improve APNs documentation. [\#553](https://github.com/rpush/rpush/pull/553) ([timdiggins](https://github.com/timdiggins))
12
+
3
13
  ## [v5.1.0](https://github.com/rpush/rpush/tree/v5.1.0) (2020-09-25)
4
14
 
5
15
  [Full Changelog](https://github.com/rpush/rpush/compare/v5.0.0...v5.1.0)
@@ -481,6 +491,4 @@ Bug fixes:
481
491
  - Removed rpush.yml in favour of command line options.
482
492
  - Started the changelog!
483
493
 
484
- \* _This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)_
485
-
486
- \* _This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)_
494
+ \* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)*
data/README.md CHANGED
@@ -52,56 +52,97 @@ $ bundle exec rpush init
52
52
 
53
53
  #### Apple Push Notification Service
54
54
 
55
+ There is a choice of two modes (and one legacy mode) using certificates or using tokens:
55
56
 
56
- If this is your first time using the APNs, you will need to generate SSL certificates. See [Generating Certificates](https://github.com/rpush/rpush/wiki/Generating-Certificates) for instructions.
57
+ * `Rpush::Apns2` This requires an annually renewable certificate. see https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/establishing_a_certificate-based_connection_to_apns
58
+ * `Rpush::Apnsp8` This uses encrypted tokens and requires an encryption key id and encryption key (provide as a p8 file). (see https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/establishing_a_token-based_connection_to_apns)
59
+ * `Rpush::Apns` There is also the original APNS (the original version using certificates with a binary underlying protocol over TCP directly rather than over Http/2).
60
+ Apple have [announced](https://developer.apple.com/news/?id=11042019a) that this is not supported after November 2020.
61
+
62
+ If this is your first time using the APNs, you will need to generate either SSL certificates (for Apns2 or Apns) or an Encryption Key (p8) and an Encryption Key ID (for Apnsp8). See [Generating Certificates](https://github.com/rpush/rpush/wiki/Generating-Certificates) for instructions.
63
+
64
+ ##### Apnsp8
65
+
66
+ To use the p8 APNs Api:
57
67
 
58
68
  ```ruby
59
- app = Rpush::Apns::App.new
69
+ app = Rpush::Apnsp8::App.new
60
70
  app.name = "ios_app"
61
- app.certificate = File.read("/path/to/sandbox.pem")
71
+ app.apn_key = File.read("/path/to/sandbox.p8")
62
72
  app.environment = "development" # APNs environment.
63
- app.password = "certificate password"
73
+ app.apn_key_id = "APN KEY ID" # This is the Encryption Key ID provided by apple
74
+ app.team_id = "TEAM ID" # the team id - e.g. ABCDE12345
75
+ app.bundle_id = "BUNDLE ID" # the unique bundle id of the app, like com.example.appname
64
76
  app.connections = 1
65
77
  app.save!
66
78
  ```
67
79
 
68
80
  ```ruby
69
81
  n = Rpush::Apns::Notification.new
70
- n.app = Rpush::Apns::App.find_by_name("ios_app")
82
+ n.app = Rpush::Apnsp8::App.find_by_name("ios_app")
71
83
  n.device_token = "..." # hex string
72
84
  n.alert = "hi mom!"
73
85
  n.data = { foo: :bar }
74
86
  n.save!
75
87
  ```
76
88
 
77
- The `url_args` attribute is available for Safari Push Notifications.
89
+ ##### Apns2
78
90
 
79
- You should also implement the [ssl_certificate_will_expire](https://github.com/rpush/rpush/wiki/Reflection-API) reflection to monitor when your certificate is due to expire.
91
+ (NB this uses the same protocol as Apnsp8, but authenticates with a certificate rather than tokens)
92
+
93
+ ```ruby
94
+ app = Rpush::Apns2::App.new
95
+ app.name = "ios_app"
96
+ app.certificate = File.read("/path/to/sandbox.pem")
97
+ app.environment = "development"
98
+ app.password = "certificate password"
99
+ app.connections = 1
100
+ app.save!
101
+ ```
80
102
 
81
- To use the newer APNs Api replace `Rpush::Apns::App` with `Rpush::Apns2::App`.
103
+ ```ruby
104
+ n = Rpush::Apns2::Notification.new
105
+ n.app = Rpush::Apns2::App.find_by_name("ios_app")
106
+ n.device_token = "..." # hex string
107
+ n.alert = "hi mom!"
108
+ n.data = {
109
+ headers: { 'apns-topic': "BUNDLE ID", # the bundle id of the app, like com.example.appname
110
+ foo: :bar }
111
+ }
112
+ n.save!
113
+ ```
82
114
 
83
- To use the p8 APNs Api replace `Rpush::Apns::App` with `Rpush::Apnsp8::App`.
115
+ You should also implement the [ssl_certificate_will_expire](https://github.com/rpush/rpush/wiki/Reflection-API) reflection to monitor when your certificate is due to expire.
116
+
117
+ ##### Apns (legacy protocol)
84
118
 
85
119
  ```ruby
86
- app = Rpush::Apnsp8::App.new
120
+ app = Rpush::Apns::App.new
87
121
  app.name = "ios_app"
88
- app.apn_key = File.read("/path/to/sandbox.p8")
122
+ app.certificate = File.read("/path/to/sandbox.pem")
89
123
  app.environment = "development" # APNs environment.
90
- app.apn_key_id = "APN KEY ID"
91
- app.team_id = "TEAM ID"
92
- app.bundle_id = "BUNDLE ID"
124
+ app.password = "certificate password"
93
125
  app.connections = 1
94
126
  app.save!
95
127
  ```
96
128
 
97
129
  ```ruby
98
130
  n = Rpush::Apns::Notification.new
99
- n.app = Rpush::Apnsp8::App.find_by_name("ios_app")
131
+ n.app = Rpush::Apns::App.find_by_name("ios_app")
100
132
  n.device_token = "..." # hex string
101
133
  n.alert = "hi mom!"
102
134
  n.data = { foo: :bar }
103
135
  n.save!
104
136
  ```
137
+
138
+ ##### Safari Push Notifications
139
+
140
+ Using one of the notifications methods above, the `url_args` attribute is available for Safari Push Notifications.
141
+
142
+ ##### Environment
143
+
144
+ The app `environment` for any Apns* option is "development" for XCode installs, and "production" for app store and TestFlight. Note that for Apns2 you can now use one (production + sandbox) certificate (you don't need a separate "sandbox" or development certificate), but if you do generate a development/sandbox certificate it can only be used for "development". With Apnsp8 tokens, you can target either "development" or "production" environments.
145
+
105
146
  #### Firebase Cloud Messaging
106
147
 
107
148
  FCM and GCM are – as of writing – compatible with each other. See also [this comment](https://github.com/rpush/rpush/issues/284#issuecomment-228330206) for further references.
@@ -26,6 +26,10 @@ Rpush.configure do |config|
26
26
  # Define a custom logger.
27
27
  # config.logger = MyLogger.new
28
28
 
29
+ # By default in foreground mode logs are directed both to the logger and to stdout.
30
+ # If the logger goes to stdout, you can disable foreground logging to avoid duplication.
31
+ # config.foreground_logging = false
32
+
29
33
  # config.apns.feedback_receiver.enabled = true
30
34
  # config.apns.feedback_receiver.frequency = 60
31
35
 
@@ -16,7 +16,7 @@ module Rpush
16
16
  end
17
17
  end
18
18
 
19
- CURRENT_ATTRS = [:push_poll, :embedded, :pid_file, :batch_size, :push, :client, :logger, :log_file, :foreground, :log_level, :plugin, :apns]
19
+ CURRENT_ATTRS = [:push_poll, :embedded, :pid_file, :batch_size, :push, :client, :logger, :log_file, :foreground, :foreground_logging, :log_level, :plugin, :apns]
20
20
  DEPRECATED_ATTRS = []
21
21
  CONFIG_ATTRS = CURRENT_ATTRS + DEPRECATED_ATTRS
22
22
 
@@ -53,6 +53,7 @@ module Rpush
53
53
  self.log_level = (defined?(Rails) && Rails.logger) ? Rails.logger.level : ::Logger::Severity::DEBUG
54
54
  self.plugin = OpenStruct.new
55
55
  self.foreground = false
56
+ self.foreground_logging = true
56
57
 
57
58
  self.apns = ApnsConfiguration.new
58
59
 
@@ -109,7 +109,7 @@ module Rpush
109
109
  Feeder.stop
110
110
  AppRunner.stop
111
111
  delete_pid_file
112
- puts Rainbow('✔').red if Rpush.config.foreground
112
+ puts Rainbow('✔').red if Rpush.config.foreground && Rpush.config.foreground_logging
113
113
  end
114
114
  end
115
115
 
@@ -37,7 +37,7 @@ module Rpush
37
37
  Rpush::Daemon.store.release_connection
38
38
  end
39
39
 
40
- puts Rainbow('✔').green if Rpush.config.foreground
40
+ puts Rainbow('✔').green if Rpush.config.foreground && Rpush.config.foreground_logging
41
41
  end
42
42
 
43
43
  def stop
@@ -29,7 +29,7 @@ module Rpush
29
29
  Rpush.logger.info("[#{app.name}] Starting #{pluralize(app.connections, 'dispatcher')}... ", true)
30
30
  runner = @runners[app.id] = new(app)
31
31
  runner.start_dispatchers
32
- puts Rainbow('✔').green if Rpush.config.foreground
32
+ puts Rainbow('✔').green if Rpush.config.foreground && Rpush.config.foreground_logging
33
33
  runner.start_loops
34
34
  rescue StandardError => e
35
35
  @runners.delete(app.id)
@@ -2,6 +2,7 @@ module Rpush
2
2
  module Daemon
3
3
  class Batch
4
4
  include Reflectable
5
+ include Loggable
5
6
 
6
7
  attr_reader :num_processed, :notifications, :delivered, :failed, :retryable
7
8
 
@@ -31,16 +32,21 @@ module Rpush
31
32
  @retryable[deliver_after] ||= []
32
33
  @retryable[deliver_after] << notification
33
34
  end
35
+
34
36
  Rpush::Daemon.store.mark_retryable(notification, deliver_after, persist: false)
35
37
  end
36
38
 
37
- def mark_all_retryable(deliver_after)
38
- @mutex.synchronize do
39
- @retryable[deliver_after] = @notifications
40
- end
39
+ def mark_all_retryable(deliver_after, error)
40
+ retryable_count = 0
41
+
41
42
  each_notification do |notification|
42
- Rpush::Daemon.store.mark_retryable(notification, deliver_after, persist: false)
43
+ next if notification.delivered || notification.failed
44
+
45
+ retryable_count += 1
46
+ mark_retryable(notification, deliver_after)
43
47
  end
48
+
49
+ log_warn("Will retry #{retryable_count} of #{@notifications.size} notifications after #{deliver_after.strftime('%Y-%m-%d %H:%M:%S')} due to error (#{error.class.name}, #{error.message})")
44
50
  end
45
51
 
46
52
  def mark_delivered(notification)
@@ -54,6 +60,7 @@ module Rpush
54
60
  @mutex.synchronize do
55
61
  @delivered = @notifications
56
62
  end
63
+
57
64
  each_notification do |notification|
58
65
  Rpush::Daemon.store.mark_delivered(notification, Time.now, persist: false)
59
66
  end
@@ -20,8 +20,7 @@ module Rpush
20
20
  end
21
21
 
22
22
  def mark_batch_retryable(deliver_after, error)
23
- log_warn("Will retry #{@batch.notifications.size} notifications after #{deliver_after.strftime('%Y-%m-%d %H:%M:%S')} due to error (#{error.class.name}, #{error.message})")
24
- @batch.mark_all_retryable(deliver_after)
23
+ @batch.mark_all_retryable(deliver_after, error)
25
24
  end
26
25
 
27
26
  def mark_delivered
@@ -79,6 +79,7 @@ module Rpush
79
79
  end
80
80
 
81
81
  def log_foreground(io, formatted_msg, inline)
82
+ return unless Rpush.config.foreground_logging
82
83
  return unless io == STDERR || Rpush.config.foreground
83
84
 
84
85
  if inline
@@ -1,7 +1,7 @@
1
1
  module Rpush
2
2
  module VERSION
3
3
  MAJOR = 5
4
- MINOR = 1
4
+ MINOR = 2
5
5
  TINY = 0
6
6
  PRE = nil
7
7
 
@@ -177,6 +177,13 @@ describe 'APNs http2 adapter' do
177
177
  end
178
178
 
179
179
  context 'when there is SocketError' do
180
+ let(:fake_http_resp_headers) {
181
+ {
182
+ ":status" => "500",
183
+ "apns-id"=>"C6D65840-5E3F-785A-4D91-B97D305C12F6"
184
+ }
185
+ }
186
+
180
187
  before(:each) do
181
188
  expect(fake_client).to receive(:call_async) { raise(SocketError) }
182
189
  end
@@ -201,6 +208,24 @@ describe 'APNs http2 adapter' do
201
208
  notification = create_notification
202
209
  Rpush.push
203
210
  end
211
+
212
+ context 'when specific notification was delivered before request failed' do
213
+ let(:fake_http_resp_headers) {
214
+ {
215
+ ":status" => "200",
216
+ "apns-id"=>"C6D65840-5E3F-785A-4D91-B97D305C12F6"
217
+ }
218
+ }
219
+
220
+ it 'fails but will not retry this notification' do
221
+ notification = create_notification
222
+ expect do
223
+ Rpush.push
224
+ notification.reload
225
+ end.to change(notification, :retries).by(0)
226
+ .and change(notification, :delivered).to(true)
227
+ end
228
+ end
204
229
  end
205
230
 
206
231
  context 'when any StandardError occurs' do
@@ -230,8 +255,17 @@ describe 'APNs http2 adapter' do
230
255
  end
231
256
 
232
257
  context 'when waiting for requests to complete times out' do
258
+ let(:on_close) do
259
+ proc { |&block| @thread = Thread.new { sleep(0.01) } }
260
+ end
261
+
233
262
  before(:each) do
234
- expect(fake_client).to receive(:join) { raise(NetHttp2::AsyncRequestTimeout) }
263
+ @thread = nil
264
+
265
+ expect(fake_http2_request).
266
+ to receive(:on).with(:close), &on_close
267
+
268
+ expect(fake_client).to receive(:join) { @thread.join; raise(NetHttp2::AsyncRequestTimeout) }
235
269
  end
236
270
 
237
271
  it 'closes the client' do
@@ -263,6 +297,21 @@ describe 'APNs http2 adapter' do
263
297
  notification.reload
264
298
  end.to change(notification, :retries)
265
299
  end
300
+
301
+ context 'when specific notification was delivered before another async call failed' do
302
+ let(:on_close) do
303
+ proc { |&block| @thread = Thread.new { sleep(0.01); block.call } }
304
+ end
305
+
306
+ it 'fails but retries delivery several times' do
307
+ notification = create_notification
308
+ expect do
309
+ Rpush.push
310
+ notification.reload
311
+ end.to change(notification, :retries).by(0)
312
+ .and change(notification, :delivered).to(true)
313
+ end
314
+ end
266
315
  end
267
316
  end
268
317
  end
@@ -1,8 +1,8 @@
1
1
  require 'unit_spec_helper'
2
2
 
3
3
  describe Rpush::Daemon::Batch do
4
- let(:notification1) { double(:notification1, id: 1) }
5
- let(:notification2) { double(:notification2, id: 2) }
4
+ let(:notification1) { double(:notification1, id: 1, delivered: false, failed: false) }
5
+ let(:notification2) { double(:notification2, id: 2, delivered: false, failed: false) }
6
6
  let(:batch) { Rpush::Daemon::Batch.new([notification1, notification2]) }
7
7
  let(:store) { double.as_null_object }
8
8
  let(:time) { Time.now }
@@ -50,6 +50,54 @@ describe Rpush::Daemon::Batch do
50
50
  end
51
51
  end
52
52
 
53
+ describe 'mark_all_retryable' do
54
+ let(:error) { StandardError.new('Exception') }
55
+
56
+ it 'marks all notifications as retryable without persisting' do
57
+ expect(store).to receive(:mark_retryable).ordered.with(notification1, time, persist: false)
58
+ expect(store).to receive(:mark_retryable).ordered.with(notification2, time, persist: false)
59
+
60
+ batch.mark_all_retryable(time, error)
61
+ end
62
+
63
+ it 'defers persisting' do
64
+ batch.mark_all_retryable(time, error)
65
+ expect(batch.retryable).to eq(time => [notification1, notification2])
66
+ end
67
+
68
+ context 'when one of the notifications delivered' do
69
+ let(:notification2) { double(:notification2, id: 2, delivered: true, failed: false) }
70
+
71
+ it 'marks all only pending notification as retryable without persisting' do
72
+ expect(store).to receive(:mark_retryable).ordered.with(notification1, time, persist: false)
73
+ expect(store).not_to receive(:mark_retryable).ordered.with(notification2, time, persist: false)
74
+
75
+ batch.mark_all_retryable(time, error)
76
+ end
77
+
78
+ it 'defers persisting' do
79
+ batch.mark_all_retryable(time, error)
80
+ expect(batch.retryable).to eq(time => [notification1])
81
+ end
82
+ end
83
+
84
+ context 'when one of the notifications failed' do
85
+ let(:notification2) { double(:notification2, id: 2, delivered: false, failed: true) }
86
+
87
+ it 'marks all only pending notification as retryable without persisting' do
88
+ expect(store).to receive(:mark_retryable).ordered.with(notification1, time, persist: false)
89
+ expect(store).not_to receive(:mark_retryable).ordered.with(notification2, time, persist: false)
90
+
91
+ batch.mark_all_retryable(time, error)
92
+ end
93
+
94
+ it 'defers persisting' do
95
+ batch.mark_all_retryable(time, error)
96
+ expect(batch.retryable).to eq(time => [notification1])
97
+ end
98
+ end
99
+ end
100
+
53
101
  describe 'mark_failed' do
54
102
  it 'marks the notification as failed without persisting' do
55
103
  expect(store).to receive(:mark_failed).with(notification1, 1, 'an error', time, persist: false)
@@ -41,6 +41,16 @@ describe Rpush::Daemon::Delivery do
41
41
  end
42
42
  end
43
43
 
44
+ describe 'mark_batch_retryable' do
45
+ let(:batch) { double(Rpush::Daemon::Batch) }
46
+ let(:error) { StandardError.new('Exception') }
47
+
48
+ it 'marks all notifications as retryable' do
49
+ expect(batch).to receive(:mark_all_retryable)
50
+ delivery.mark_batch_retryable(Time.now + 1.hour, error)
51
+ end
52
+ end
53
+
44
54
  describe 'mark_batch_failed' do
45
55
  it 'marks all notifications as delivered' do
46
56
  error = Rpush::DeliveryError.new(1, 42, 'an error')
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rpush
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.1.0
4
+ version: 5.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ian Leitch
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-09-25 00:00:00.000000000 Z
11
+ date: 2020-10-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: multi_json