rpush 5.2.0 → 5.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +16 -0
  3. data/README.md +49 -4
  4. data/lib/rpush/client/active_model.rb +3 -0
  5. data/lib/rpush/client/active_model/adm/data_validator.rb +1 -1
  6. data/lib/rpush/client/active_model/apns/device_token_format_validator.rb +2 -2
  7. data/lib/rpush/client/active_model/apns/notification_payload_size_validator.rb +1 -1
  8. data/lib/rpush/client/active_model/gcm/expiry_collapse_key_mutual_inclusion_validator.rb +1 -1
  9. data/lib/rpush/client/active_model/payload_data_size_validator.rb +1 -1
  10. data/lib/rpush/client/active_model/registration_ids_count_validator.rb +1 -1
  11. data/lib/rpush/client/active_model/webpush/app.rb +41 -0
  12. data/lib/rpush/client/active_model/webpush/notification.rb +66 -0
  13. data/lib/rpush/client/active_record.rb +3 -0
  14. data/lib/rpush/client/active_record/apnsp8/notification.rb +1 -0
  15. data/lib/rpush/client/active_record/webpush/app.rb +11 -0
  16. data/lib/rpush/client/active_record/webpush/notification.rb +12 -0
  17. data/lib/rpush/client/redis.rb +3 -0
  18. data/lib/rpush/client/redis/apnsp8/notification.rb +2 -0
  19. data/lib/rpush/client/redis/webpush/app.rb +15 -0
  20. data/lib/rpush/client/redis/webpush/notification.rb +15 -0
  21. data/lib/rpush/configuration.rb +1 -1
  22. data/lib/rpush/daemon.rb +3 -0
  23. data/lib/rpush/daemon/apns2/delivery.rb +7 -1
  24. data/lib/rpush/daemon/webpush.rb +10 -0
  25. data/lib/rpush/daemon/webpush/delivery.rb +114 -0
  26. data/lib/rpush/version.rb +1 -1
  27. data/spec/functional/apns2_spec.rb +12 -2
  28. data/spec/functional/webpush_spec.rb +30 -0
  29. data/spec/spec_helper.rb +2 -0
  30. data/spec/unit/client/active_record/apnsp8/notification_spec.rb +28 -0
  31. data/spec/unit/client/active_record/webpush/app_spec.rb +6 -0
  32. data/spec/unit/client/active_record/webpush/notification_spec.rb +6 -0
  33. data/spec/unit/client/redis/apnsp8/notification_spec.rb +29 -0
  34. data/spec/unit/client/redis/webpush/app_spec.rb +5 -0
  35. data/spec/unit/client/redis/webpush/notification_spec.rb +5 -0
  36. data/spec/unit/client/shared/webpush/app.rb +33 -0
  37. data/spec/unit/client/shared/webpush/notification.rb +83 -0
  38. data/spec/unit/daemon/webpush/delivery_spec.rb +142 -0
  39. metadata +44 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6576c1456435dd596c992846892b840f204b4cf8956b13f0c06b9b336b6e0fd8
4
- data.tar.gz: 57ba3957011570e259585b3528f0bd344411f22db72bf03cea01bfe049107ac2
3
+ metadata.gz: 2da7543fed0193b0dcd20169efb5abd8170f15bc5043ef4ce2cb4d0e56380b4f
4
+ data.tar.gz: d7ba0f5b1dc97d17a7d23bf623d900b6da26b428ddcf4a8a93fce7641ab20f2d
5
5
  SHA512:
6
- metadata.gz: 1fcce116cbddf64aacbf00d2b7294056f3c502b59f108783b53a1c90807cd0816a68be2c43e543625d556ad7de484786edb2baad1dbdc2652cfc1c4f582539f9
7
- data.tar.gz: 0e468a1f43d8c422db967946fdc8d867bfc8bce6eaea511bff04e34108f77b75e9b0f903a3c008594482fe159ee9b820cc65be81008d60e2261b26f61c6ca274
6
+ metadata.gz: 745e0799a2e66d4813b99c4a93eb5d172dcbbe4c1317bd945b1addf177720f489bbdb0d83d918b9ab0ba4b98c35d186c0522d39740541097e16e90d0687487d8
7
+ data.tar.gz: e6d249ad9c7366cc54267e4eaa6f3d1bb3670dc6211380aa8bced26b33053cc0fda328f33b72a1d843cdd794e44e0793cd579e49745c60e46aa83155a5d0acca
@@ -1,5 +1,21 @@
1
1
  # Changelog
2
2
 
3
+ ## [v5.3.0](https://github.com/rpush/rpush/tree/v5.3.0) (2021-01-07)
4
+
5
+ [Full Changelog](https://github.com/rpush/rpush/compare/v5.2.0...v5.3.0)
6
+
7
+ **Implemented enhancements:**
8
+
9
+ - support for Webpush with VAPID [\#574](https://github.com/rpush/rpush/pull/574) ([jkraemer](https://github.com/jkraemer))
10
+
11
+ **Merged pull requests:**
12
+
13
+ - Bug fix: APNS P8 Notifications Are Marked as Invalid When the Payload Exceeds 2kb \(2048 bytes\) [\#583](https://github.com/rpush/rpush/pull/583) ([gregblake](https://github.com/gregblake))
14
+ - Fix more Rails 6.1 deprecation warnings [\#582](https://github.com/rpush/rpush/pull/582) ([jas14](https://github.com/jas14))
15
+ - Feature/apns2 default headers [\#579](https://github.com/rpush/rpush/pull/579) ([jkraemer](https://github.com/jkraemer))
16
+ - Fix APNS2 documentation in README [\#578](https://github.com/rpush/rpush/pull/578) ([jamestjw](https://github.com/jamestjw))
17
+ - Fixed typo with misspell [\#575](https://github.com/rpush/rpush/pull/575) ([hsbt](https://github.com/hsbt))
18
+
3
19
  ## [v5.2.0](https://github.com/rpush/rpush/tree/v5.2.0) (2020-10-08)
4
20
 
5
21
  [Full Changelog](https://github.com/rpush/rpush/compare/v5.1.0...v5.2.0)
data/README.md CHANGED
@@ -18,6 +18,7 @@ Rpush aims to be the *de facto* gem for sending push notifications in Ruby. Its
18
18
  * [**Amazon Device Messaging**](#amazon-device-messaging)
19
19
  * [**Windows Phone Push Notification Service**](#windows-phone-notification-service)
20
20
  * [**Pushy**](#pushy)
21
+ * [**Webpush**](#webpush)
21
22
 
22
23
  #### Feature Highlights
23
24
 
@@ -96,6 +97,7 @@ app.name = "ios_app"
96
97
  app.certificate = File.read("/path/to/sandbox.pem")
97
98
  app.environment = "development"
98
99
  app.password = "certificate password"
100
+ app.bundle_id = "BUNDLE ID" # the unique bundle id of the app, like com.example.appname
99
101
  app.connections = 1
100
102
  app.save!
101
103
  ```
@@ -106,9 +108,9 @@ n.app = Rpush::Apns2::App.find_by_name("ios_app")
106
108
  n.device_token = "..." # hex string
107
109
  n.alert = "hi mom!"
108
110
  n.data = {
109
- headers: { 'apns-topic': "BUNDLE ID", # the bundle id of the app, like com.example.appname
110
- foo: :bar }
111
- }
111
+ headers: { 'apns-topic': "BUNDLE ID" }, # the bundle id of the app, like com.example.appname. Not necessary if set on the app (see above)
112
+ foo: :bar
113
+ }
112
114
  n.save!
113
115
  ```
114
116
 
@@ -249,7 +251,7 @@ n.save!
249
251
 
250
252
  #### Windows Raw Push Notifications
251
253
 
252
- Note: The data is passed as `.to_json` so only this format is supported, altough raw notifications are meant to support any kind of data.
254
+ Note: The data is passed as `.to_json` so only this format is supported, although raw notifications are meant to support any kind of data.
253
255
  Current data structure enforces hashes and `.to_json` representation is natural presentation of it.
254
256
 
255
257
  ```ruby
@@ -297,6 +299,49 @@ n.save!
297
299
 
298
300
  For more documentation on [Pushy](https://pushy.me/docs).
299
301
 
302
+ #### Webpush
303
+
304
+ [Webpush](https://tools.ietf.org/html/draft-ietf-webpush-protocol-10) is a
305
+ protocol for delivering push messages to desktop browsers. It's supported by
306
+ all major browsers (except Safari, you have to use one of the Apns transports
307
+ for that).
308
+
309
+ Using [VAPID](https://tools.ietf.org/html/draft-ietf-webpush-vapid-01), there
310
+ is no need for the sender of push notifications to register upfront with push
311
+ services (as was the case with the now legacy Mozilla or Google desktop push
312
+ providers).
313
+
314
+ Instead, you generate a pair of keys and use the public key when subscribing
315
+ users in your web app. The keys are stored along with an email address (which,
316
+ according to the spec, can be used by push service providers to contact you in
317
+ case of problems) in the `certificates` field of the Rpush Application record:
318
+
319
+ ```ruby
320
+ vapid_keypair = Webpush.generate_key.to_hash
321
+ app = Rpush::Webpush::App.new
322
+ app.name = 'webpush'
323
+ app.certificate = vapid_keypair.merge(subject: 'user@example.org').to_json
324
+ app.connections = 1
325
+ app.save!
326
+ ```
327
+
328
+ The `subscription` object you obtain from a subscribed browser holds an
329
+ endpoint URL and cryptographic keys. When sending a notification, simply pass
330
+ the whole subscription as sole member of the `registration_ids` collection:
331
+
332
+ ```ruby
333
+ n = Rpush::Webpush::Notification.new
334
+ n.app = Rpush::App.find_by_name("webpush")
335
+ n.registration_ids = [subscription]
336
+ n.data = { message: "hi mom!" }
337
+ n.save!
338
+ ```
339
+
340
+ In order to send the same message to multiple devices, create one
341
+ `Notification` per device, as passing multiple subscriptions at once as
342
+ `registration_ids` is not supported.
343
+
344
+
300
345
  ### Running Rpush
301
346
 
302
347
  It is recommended to run Rpush as a separate process in most cases, though embedding and manual modes are provided for low-workload environments.
@@ -32,3 +32,6 @@ require 'rpush/client/active_model/wns/notification'
32
32
  require 'rpush/client/active_model/pushy/app'
33
33
  require 'rpush/client/active_model/pushy/notification'
34
34
  require 'rpush/client/active_model/pushy/time_to_live_validator'
35
+
36
+ require 'rpush/client/active_model/webpush/app'
37
+ require 'rpush/client/active_model/webpush/notification'
@@ -5,7 +5,7 @@ module Rpush
5
5
  class DataValidator < ::ActiveModel::Validator
6
6
  def validate(record)
7
7
  return unless record.collapse_key.nil? && record.data.nil?
8
- record.errors[:data] << 'must be set unless collapse_key is specified'
8
+ record.errors.add :data, 'must be set unless collapse_key is specified'
9
9
  end
10
10
  end
11
11
  end
@@ -4,8 +4,8 @@ module Rpush
4
4
  module Apns
5
5
  class DeviceTokenFormatValidator < ::ActiveModel::Validator
6
6
  def validate(record)
7
- return if record.device_token =~ /^[a-z0-9]\w+$/i
8
- record.errors[:device_token] << "is invalid"
7
+ return if record.device_token =~ /\A[a-z0-9]\w+\z/i
8
+ record.errors.add :device_token, "is invalid"
9
9
  end
10
10
  end
11
11
  end
@@ -6,7 +6,7 @@ module Rpush
6
6
  def validate(record)
7
7
  limit = record.class.max_payload_bytesize
8
8
  return unless record.payload.bytesize > limit
9
- record.errors[:base] << "APN notification cannot be larger than #{limit} bytes. Try condensing your alert and device attributes."
9
+ record.errors.add :base, "APN notification cannot be larger than #{limit} bytes. Try condensing your alert and device attributes."
10
10
  end
11
11
  end
12
12
  end
@@ -5,7 +5,7 @@ module Rpush
5
5
  class ExpiryCollapseKeyMutualInclusionValidator < ::ActiveModel::Validator
6
6
  def validate(record)
7
7
  return unless record.collapse_key && !record.expiry
8
- record.errors[:expiry] << 'must be set when using a collapse_key'
8
+ record.errors.add :expiry, 'must be set when using a collapse_key'
9
9
  end
10
10
  end
11
11
  end
@@ -5,7 +5,7 @@ module Rpush
5
5
  def validate(record)
6
6
  limit = options[:limit] || 1024
7
7
  return unless record.data && record.payload_data_size > limit
8
- record.errors[:base] << "Notification payload data cannot be larger than #{limit} bytes."
8
+ record.errors.add :base, "Notification payload data cannot be larger than #{limit} bytes."
9
9
  end
10
10
  end
11
11
  end
@@ -5,7 +5,7 @@ module Rpush
5
5
  def validate(record)
6
6
  limit = options[:limit] || 100
7
7
  return unless record.registration_ids && record.registration_ids.size > limit
8
- record.errors[:base] << "Number of registration_ids cannot be larger than #{limit}."
8
+ record.errors.add :base, "Number of registration_ids cannot be larger than #{limit}."
9
9
  end
10
10
  end
11
11
  end
@@ -0,0 +1,41 @@
1
+ module Rpush
2
+ module Client
3
+ module ActiveModel
4
+ module Webpush
5
+ module App
6
+
7
+ class VapidKeypairValidator < ::ActiveModel::Validator
8
+ def validate(record)
9
+ return if record.vapid_keypair.blank?
10
+ keypair = record.vapid
11
+ %i[ subject public_key private_key ].each do |key|
12
+ unless keypair.key?(key)
13
+ record.errors.add(:vapid_keypair, "must have a #{key} entry")
14
+ end
15
+ end
16
+ rescue
17
+ record.errors.add(:vapid_keypair, 'must be valid JSON')
18
+ end
19
+ end
20
+
21
+ def self.included(base)
22
+ base.class_eval do
23
+ alias_attribute :vapid_keypair, :certificate
24
+ validates :vapid_keypair, presence: true
25
+ validates_with VapidKeypairValidator
26
+ end
27
+ end
28
+
29
+ def service_name
30
+ 'webpush'
31
+ end
32
+
33
+ def vapid
34
+ @vapid ||= JSON.parse(vapid_keypair).symbolize_keys
35
+ end
36
+
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,66 @@
1
+ module Rpush
2
+ module Client
3
+ module ActiveModel
4
+ module Webpush
5
+ module Notification
6
+
7
+ class RegistrationValidator < ::ActiveModel::Validator
8
+ KEYS = %i[ endpoint keys ].freeze
9
+ def validate(record)
10
+ return if record.registration_ids.blank?
11
+ return if record.registration_ids.size > 1
12
+ reg = record.registration_ids.first
13
+ unless reg.is_a?(Hash) &&
14
+ reg.keys.sort == KEYS &&
15
+ reg[:endpoint].is_a?(String) &&
16
+ reg[:keys].is_a?(Hash)
17
+ record.errors.add(:base, 'Registration must have :endpoint (String) and :keys (Hash) keys')
18
+ end
19
+ end
20
+ end
21
+
22
+ def self.included(base)
23
+ base.instance_eval do
24
+ alias_attribute :time_to_live, :expiry
25
+
26
+ validates :registration_ids, presence: true
27
+ validates :data, presence: true
28
+ validates :time_to_live, numericality: { only_integer: true, greater_than: 0 }, allow_nil: true
29
+
30
+ validates_with Rpush::Client::ActiveModel::PayloadDataSizeValidator, limit: 4096
31
+ validates_with Rpush::Client::ActiveModel::RegistrationIdsCountValidator, limit: 1
32
+ validates_with RegistrationValidator
33
+ end
34
+ end
35
+
36
+ def data=(value)
37
+ value = value.stringify_keys if value.respond_to?(:stringify_keys)
38
+ super value
39
+ end
40
+
41
+ def subscription
42
+ @subscription ||= registration_ids.first.deep_symbolize_keys
43
+ end
44
+
45
+ def message
46
+ data['message'].presence if data
47
+ end
48
+
49
+ # https://webpush-wg.github.io/webpush-protocol/#urgency
50
+ def urgency
51
+ data['urgency'].presence if data
52
+ end
53
+
54
+ def as_json(_options = nil)
55
+ {
56
+ 'data' => data,
57
+ 'time_to_live' => time_to_live,
58
+ 'registration_ids' => registration_ids
59
+ }
60
+ end
61
+
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -32,3 +32,6 @@ require 'rpush/client/active_record/adm/app'
32
32
 
33
33
  require 'rpush/client/active_record/pushy/notification'
34
34
  require 'rpush/client/active_record/pushy/app'
35
+
36
+ require 'rpush/client/active_record/webpush/notification'
37
+ require 'rpush/client/active_record/webpush/app'
@@ -3,6 +3,7 @@ module Rpush
3
3
  module ActiveRecord
4
4
  module Apnsp8
5
5
  class Notification < Rpush::Client::ActiveRecord::Apns::Notification
6
+ include Rpush::Client::ActiveModel::Apns2::Notification
6
7
  end
7
8
  end
8
9
  end
@@ -0,0 +1,11 @@
1
+ module Rpush
2
+ module Client
3
+ module ActiveRecord
4
+ module Webpush
5
+ class App < Rpush::Client::ActiveRecord::App
6
+ include Rpush::Client::ActiveModel::Webpush::App
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,12 @@
1
+ module Rpush
2
+ module Client
3
+ module ActiveRecord
4
+ module Webpush
5
+ class Notification < Rpush::Client::ActiveRecord::Notification
6
+ include Rpush::Client::ActiveModel::Webpush::Notification
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
12
+
@@ -44,6 +44,9 @@ require 'rpush/client/redis/wns/badge_notification'
44
44
  require 'rpush/client/redis/pushy/app'
45
45
  require 'rpush/client/redis/pushy/notification'
46
46
 
47
+ require 'rpush/client/redis/webpush/app'
48
+ require 'rpush/client/redis/webpush/notification'
49
+
47
50
  Modis.configure do |config|
48
51
  config.namespace = :rpush
49
52
  end
@@ -3,6 +3,8 @@ module Rpush
3
3
  module Redis
4
4
  module Apnsp8
5
5
  class Notification < Rpush::Client::Redis::Notification
6
+ include Rpush::Client::ActiveModel::Apns::Notification
7
+ include Rpush::Client::ActiveModel::Apns2::Notification
6
8
  include Rpush::Client::ActiveModel::Apnsp8::Notification
7
9
  end
8
10
  end
@@ -0,0 +1,15 @@
1
+ module Rpush
2
+ module Client
3
+ module Redis
4
+ module Webpush
5
+ class App < Rpush::Client::Redis::App
6
+ include Rpush::Client::ActiveModel::Webpush::App
7
+
8
+ def vapid_keypair=(value)
9
+ self.certificate = value
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ module Rpush
2
+ module Client
3
+ module Redis
4
+ module Webpush
5
+ class Notification < Rpush::Client::Redis::Notification
6
+ include Rpush::Client::ActiveModel::Webpush::Notification
7
+
8
+ def time_to_live=(value)
9
+ self.expiry = value
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -106,7 +106,7 @@ module Rpush
106
106
  client_module = Rpush::Client.const_get(client.to_s.camelize)
107
107
  Rpush.send(:include, client_module) unless Rpush.ancestors.include?(client_module)
108
108
 
109
- [:Apns, :Gcm, :Wpns, :Wns, :Adm, :Pushy].each do |service|
109
+ [:Apns, :Gcm, :Wpns, :Wns, :Adm, :Pushy, :Webpush].each do |service|
110
110
  Rpush.const_set(service, client_module.const_get(service)) unless Rpush.const_defined?(service)
111
111
  end
112
112
 
@@ -68,6 +68,9 @@ require 'rpush/daemon/adm'
68
68
  require 'rpush/daemon/pushy'
69
69
  require 'rpush/daemon/pushy/delivery'
70
70
 
71
+ require 'rpush/daemon/webpush/delivery'
72
+ require 'rpush/daemon/webpush'
73
+
71
74
  module Rpush
72
75
  module Daemon
73
76
  class << self
@@ -107,7 +107,13 @@ module Rpush
107
107
  end
108
108
 
109
109
  def prepare_headers(notification)
110
- notification_data(notification)[HTTP2_HEADERS_KEY] || {}
110
+ headers = {}
111
+
112
+ headers['apns-expiration'] = '0'
113
+ headers['apns-priority'] = '10'
114
+ headers['apns-topic'] = @app.bundle_id
115
+
116
+ headers.merge notification_data(notification)[HTTP2_HEADERS_KEY] || {}
111
117
  end
112
118
 
113
119
  def notification_data(notification)
@@ -0,0 +1,10 @@
1
+ module Rpush
2
+ module Daemon
3
+ module Webpush
4
+ extend ServiceConfigMethods
5
+
6
+ dispatcher :http
7
+ end
8
+ end
9
+ end
10
+
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "webpush"
4
+
5
+ module Rpush
6
+ module Daemon
7
+ module Webpush
8
+
9
+ # Webpush::Request handles all the encryption / signing.
10
+ # We just override #perform to inject the http instance that is managed
11
+ # by Rpush.
12
+ #
13
+ class Request < ::Webpush::Request
14
+ def perform(http)
15
+ req = Net::HTTP::Post.new(uri.request_uri, headers)
16
+ req.body = body
17
+ http.request(uri, req)
18
+ end
19
+ end
20
+
21
+ class Delivery < Rpush::Daemon::Delivery
22
+
23
+ OK = [ 200, 201, 202 ].freeze
24
+ TEMPORARY_FAILURES = [ 429, 500, 502, 503, 504 ].freeze
25
+
26
+ def initialize(app, http, notification, batch)
27
+ @app = app
28
+ @http = http
29
+ @notification = notification
30
+ @batch = batch
31
+ end
32
+
33
+ def perform
34
+ response = send_request
35
+ process_response response
36
+ rescue SocketError, SystemCallError => error
37
+ mark_retryable(@notification, Time.now + 10.seconds, error)
38
+ raise
39
+ rescue StandardError => error
40
+ mark_failed(error)
41
+ raise
42
+ ensure
43
+ @batch.notification_processed
44
+ end
45
+
46
+ private
47
+
48
+ def send_request
49
+ # The initializer is inherited from Webpush::Request and looks like
50
+ # this:
51
+ #
52
+ # initialize(message: '', subscription:, vapid:, **options)
53
+ #
54
+ # where subscription is a hash of :endpoint and :keys, and vapid
55
+ # holds the vapid public and private keys and the :subject (which is
56
+ # an email address).
57
+ Request.new(
58
+ message: @notification.message,
59
+ subscription: @notification.subscription,
60
+ vapid: @app.vapid,
61
+ ttl: @notification.time_to_live,
62
+ urgency: @notification.urgency
63
+ ).perform(@http)
64
+ end
65
+
66
+ def process_response(response)
67
+ case response.code.to_i
68
+ when *OK
69
+ mark_delivered
70
+ when *TEMPORARY_FAILURES
71
+ retry_delivery(response)
72
+ else
73
+ fail_delivery(response)
74
+ end
75
+ end
76
+
77
+ def retry_delivery(response)
78
+ time = deliver_after_header(response)
79
+ if time
80
+ mark_retryable(@notification, time)
81
+ else
82
+ mark_retryable_exponential(@notification)
83
+ end
84
+ log_info("Webpush endpoint responded with a #{response.code} error. #{retry_message}")
85
+ end
86
+
87
+ def fail_delivery(response)
88
+ fail_message = fail_message(response)
89
+ log_error("#{@notification.id} failed: #{fail_message}")
90
+ fail Rpush::DeliveryError.new(response.code.to_i, @notification.id, fail_message)
91
+ end
92
+
93
+ def deliver_after_header(response)
94
+ Rpush::Daemon::RetryHeaderParser.parse(response.header['retry-after'])
95
+ end
96
+
97
+ def retry_message
98
+ deliver_after = @notification.deliver_after.strftime('%Y-%m-%d %H:%M:%S')
99
+ "Notification #{@notification.id} will be retried after #{deliver_after} (retry #{@notification.retries})."
100
+ end
101
+
102
+ def fail_message(response)
103
+ msg = Rpush::Daemon::HTTP_STATUS_CODES[response.code.to_i]
104
+ if explanation = response.body.to_s[0..200].presence
105
+ msg += ": #{explanation}"
106
+ end
107
+ msg
108
+ end
109
+
110
+ end
111
+ end
112
+ end
113
+ end
114
+
@@ -1,7 +1,7 @@
1
1
  module Rpush
2
2
  module VERSION
3
3
  MAJOR = 5
4
- MINOR = 2
4
+ MINOR = 3
5
5
  TINY = 0
6
6
  PRE = nil
7
7
 
@@ -42,6 +42,7 @@ describe 'APNs http2 adapter' do
42
42
  app.certificate = TEST_CERT
43
43
  app.name = 'test'
44
44
  app.environment = 'development'
45
+ app.bundle_id = 'com.example.app'
45
46
  app.save!
46
47
  app
47
48
  end
@@ -75,7 +76,12 @@ describe 'APNs http2 adapter' do
75
76
  :post,
76
77
  "/3/device/#{fake_device_token}",
77
78
  { body: "{\"aps\":{\"alert\":\"test\",\"sound\":\"default\",\"content-available\":1}}",
78
- headers: {} }
79
+ headers: {
80
+ 'apns-expiration' => '0',
81
+ 'apns-priority' => '10',
82
+ 'apns-topic' => 'com.example.app'
83
+ }
84
+ }
79
85
  )
80
86
  .and_return(fake_http2_request)
81
87
 
@@ -104,7 +110,11 @@ describe 'APNs http2 adapter' do
104
110
  "/3/device/#{fake_device_token}",
105
111
  { body: "{\"aps\":{\"alert\":\"test\",\"sound\":\"default\","\
106
112
  "\"content-available\":1},\"some_field\":\"some value\"}",
107
- headers: { 'apns-topic' => bundle_id }
113
+ headers: {
114
+ 'apns-topic' => bundle_id,
115
+ 'apns-expiration' => '0',
116
+ 'apns-priority' => '10'
117
+ }
108
118
  }
109
119
  ).and_return(fake_http2_request)
110
120
 
@@ -0,0 +1,30 @@
1
+ require 'functional_spec_helper'
2
+
3
+ describe 'Webpush' do
4
+ let(:code) { 201 }
5
+ let(:response) { instance_double('Net::HTTPResponse', code: code, body: '') }
6
+ let(:http) { instance_double('Net::HTTP::Persistent', request: response, shutdown: nil) }
7
+ let(:app) { Rpush::Webpush::App.create!(name: 'MyApp', vapid_keypair: VAPID_KEYPAIR) }
8
+
9
+ let(:device_reg) {
10
+ { endpoint: 'https://webpush-provider.example.org/push/some-id',
11
+ keys: {'auth' => 'DgN9EBia1o057BdhCOGURA', 'p256dh' => 'BAtxJ--7vHq9IVm8utUB3peJ4lpxRqk1rukCIkVJOomS83QkCnrQ4EyYQsSaCRgy_c8XPytgXxuyAvRJdnTPK4A'} }
12
+ }
13
+ let(:notification) { Rpush::Webpush::Notification.create!(app: app, registration_ids: [device_reg], data: { message: 'test' }) }
14
+
15
+ before do
16
+ allow(Net::HTTP::Persistent).to receive_messages(new: http)
17
+ end
18
+
19
+ it 'deliveres a notification successfully' do
20
+ expect { Rpush.push }.to change { notification.reload.delivered }.to(true)
21
+ end
22
+
23
+ context 'when delivery failed' do
24
+ let(:code) { 404 }
25
+ it 'marks a notification as failed' do
26
+ expect { Rpush.push }.to change { notification.reload.failed }.to(true)
27
+ end
28
+ end
29
+ end
30
+
@@ -46,6 +46,8 @@ path = File.join(File.dirname(__FILE__), 'support')
46
46
  TEST_CERT = File.read(File.join(path, 'cert_without_password.pem'))
47
47
  TEST_CERT_WITH_PASSWORD = File.read(File.join(path, 'cert_with_password.pem'))
48
48
 
49
+ VAPID_KEYPAIR = Webpush.generate_key.to_hash.merge(subject: 'rpush-test@example.org').to_json
50
+
49
51
  def after_example_cleanup
50
52
  Rpush.logger = nil
51
53
  Rpush::Daemon.store = nil
@@ -0,0 +1,28 @@
1
+ require "unit_spec_helper"
2
+
3
+ describe Rpush::Client::ActiveRecord::Apnsp8::Notification do
4
+ subject(:notification) { described_class.new }
5
+
6
+ it_behaves_like 'Rpush::Client::Apns::Notification'
7
+ it_behaves_like 'Rpush::Client::ActiveRecord::Notification'
8
+
9
+ it "should validate the length of the binary conversion of the notification", :aggregate_failures do
10
+ notification = described_class.new
11
+ notification.app = Rpush::Apnsp8::App.create(apn_key: "1",
12
+ apn_key_id: "2",
13
+ name: 'test',
14
+ environment: 'development',
15
+ team_id: "3",
16
+ bundle_id: "4")
17
+ notification.device_token = "a" * 108
18
+ notification.alert = ""
19
+
20
+ notification.alert << "a" until notification.payload.bytesize == 4096
21
+ expect(notification.valid?).to be_truthy
22
+ expect(notification.errors[:base]).to be_empty
23
+
24
+ notification.alert << "a"
25
+ expect(notification.valid?).to be_falsey
26
+ expect(notification.errors[:base].include?("APN notification cannot be larger than 4096 bytes. Try condensing your alert and device attributes.")).to be_truthy
27
+ end
28
+ end if active_record?
@@ -0,0 +1,6 @@
1
+ require 'unit_spec_helper'
2
+
3
+ describe Rpush::Client::ActiveRecord::Webpush::App do
4
+ it_behaves_like 'Rpush::Client::Webpush::App'
5
+ it_behaves_like 'Rpush::Client::ActiveRecord::App'
6
+ end if active_record?
@@ -0,0 +1,6 @@
1
+ require 'unit_spec_helper'
2
+
3
+ describe Rpush::Client::ActiveRecord::Webpush::Notification do
4
+ it_behaves_like 'Rpush::Client::Webpush::Notification'
5
+ it_behaves_like 'Rpush::Client::ActiveRecord::Notification'
6
+ end if active_record?
@@ -0,0 +1,29 @@
1
+ require "unit_spec_helper"
2
+
3
+ describe Rpush::Client::Redis::Apnsp8::Notification do
4
+ after do
5
+ Rpush::Apnsp8::App.all.select { |a| a.environment == "development" && a.apn_key == "1" }.each(&:destroy)
6
+ end
7
+
8
+ it_behaves_like "Rpush::Client::Apns::Notification"
9
+
10
+ it "should validate the length of the binary conversion of the notification", :aggregate_failures do
11
+ notification = described_class.new
12
+ notification.app = Rpush::Apnsp8::App.create(apn_key: "1",
13
+ apn_key_id: "2",
14
+ name: 'test',
15
+ environment: 'development',
16
+ team_id: "3",
17
+ bundle_id: "4")
18
+ notification.device_token = "a" * 108
19
+ notification.alert = ""
20
+
21
+ notification.alert << "a" until notification.payload.bytesize == 4096
22
+ expect(notification.valid?).to be_truthy
23
+ expect(notification.errors[:base]).to be_empty
24
+
25
+ notification.alert << "a"
26
+ expect(notification.valid?).to be_falsey
27
+ expect(notification.errors[:base].include?("APN notification cannot be larger than 4096 bytes. Try condensing your alert and device attributes.")).to be_truthy
28
+ end
29
+ end if redis?
@@ -0,0 +1,5 @@
1
+ require 'unit_spec_helper'
2
+
3
+ describe Rpush::Client::Redis::Webpush::App do
4
+ it_behaves_like 'Rpush::Client::Webpush::App'
5
+ end if redis?
@@ -0,0 +1,5 @@
1
+ require 'unit_spec_helper'
2
+
3
+ describe Rpush::Client::Redis::Webpush::Notification do
4
+ it_behaves_like 'Rpush::Client::Webpush::Notification'
5
+ end if redis?
@@ -0,0 +1,33 @@
1
+ require 'unit_spec_helper'
2
+
3
+ shared_examples 'Rpush::Client::Webpush::App' do
4
+ describe 'validates' do
5
+ subject { described_class.new }
6
+
7
+ it 'validates presence of name' do
8
+ is_expected.not_to be_valid
9
+ expect(subject.errors[:name]).to eq ["can't be blank"]
10
+ end
11
+
12
+ it 'validates presence of vapid_keypair' do
13
+ is_expected.not_to be_valid
14
+ expect(subject.errors[:vapid_keypair]).to eq ["can't be blank"]
15
+ end
16
+
17
+ it 'should require the vapid keypair to have subject, public and private key' do
18
+ subject.vapid_keypair = {}.to_json
19
+ is_expected.not_to be_valid
20
+ expect(subject.errors[:vapid_keypair].sort).to eq [
21
+ 'must have a private_key entry',
22
+ 'must have a public_key entry',
23
+ 'must have a subject entry',
24
+ ]
25
+ end
26
+
27
+ it 'should require valid json for the keypair' do
28
+ subject.vapid_keypair = 'invalid'
29
+ is_expected.not_to be_valid
30
+ expect(subject.errors[:vapid_keypair].sort).to eq [ 'must be valid JSON' ]
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,83 @@
1
+ require 'unit_spec_helper'
2
+
3
+ shared_examples 'Rpush::Client::Webpush::Notification' do
4
+ subject(:notification) { described_class.new }
5
+
6
+ describe 'notification attributes' do
7
+ describe 'data' do
8
+ subject { described_class.new(data: { message: 'test', urgency: 'normal' } ) }
9
+ it 'has a message' do
10
+ expect(subject.message).to eq "test"
11
+ end
12
+ it 'has an urgency' do
13
+ expect(subject.urgency).to eq "normal"
14
+ end
15
+ end
16
+
17
+ describe 'subscription' do
18
+ let(:subscription){ { endpoint: 'https://push.example.org/foo', keys: {'foo' => 'bar'}} }
19
+ subject { described_class.new(registration_ids: [subscription]) }
20
+
21
+ it "has a subscription" do
22
+ expect(subject.subscription).to eq({ endpoint: 'https://push.example.org/foo', keys: {foo: 'bar'} })
23
+ end
24
+ end
25
+ end
26
+
27
+
28
+ describe 'validates' do
29
+ let(:app) { Rpush::Webpush::App.create!(name: 'MyApp', vapid_keypair: VAPID_KEYPAIR) }
30
+
31
+ describe 'data' do
32
+ subject { described_class.new(app: app, registration_ids: [{endpoint: 'https://push.example.org/foo', keys: {'foo' => 'bar'}}]) }
33
+ it 'validates presence' do
34
+ is_expected.not_to be_valid
35
+ expect(subject.errors[:data]).to eq ["can't be blank"]
36
+ end
37
+
38
+ it "has a 'data' payload limit of 4096 bytes" do
39
+ subject.data = { message: 'a' * 4096 }
40
+ is_expected.not_to be_valid
41
+ expected_errors = ["Notification payload data cannot be larger than 4096 bytes."]
42
+ expect(subject.errors[:base]).to eq expected_errors
43
+ end
44
+ end
45
+
46
+ describe 'registration_ids' do
47
+ subject { described_class.new(app: app, data: { message: 'test' }) }
48
+ it 'validates presence' do
49
+ is_expected.not_to be_valid
50
+ expect(subject.errors[:registration_ids]).to eq ["can't be blank"]
51
+ end
52
+
53
+ it 'limits the number of registration ids to exactly 1' do
54
+ subject.registration_ids = [{endpoint: 'string', keys: { 'a' => 'hash' }}] * 2
55
+ is_expected.not_to be_valid
56
+ expected_errors = ["Number of registration_ids cannot be larger than 1."]
57
+ expect(subject.errors[:base]).to eq expected_errors
58
+ end
59
+
60
+ it 'validates the structure of the registration' do
61
+ subject.registration_ids = ['a']
62
+ is_expected.not_to be_valid
63
+ expect(subject.errors[:base]).to eq [
64
+ "Registration must have :endpoint (String) and :keys (Hash) keys"
65
+ ]
66
+
67
+ subject.registration_ids = [{endpoint: 'string', keys: { 'a' => 'hash' }}]
68
+ is_expected.to be_valid
69
+ end
70
+ end
71
+
72
+ describe 'time_to_live' do
73
+ subject { described_class.new(app: app, data: { message: 'test' }, registration_ids: [{endpoint: 'https://push.example.org/foo', keys: {'foo' => 'bar'}}]) }
74
+
75
+ it 'should be > 0' do
76
+ subject.time_to_live = -1
77
+ is_expected.not_to be_valid
78
+ expect(subject.errors[:time_to_live]).to eq ['must be greater than 0']
79
+ end
80
+ end
81
+
82
+ end
83
+ end
@@ -0,0 +1,142 @@
1
+ require 'unit_spec_helper'
2
+
3
+ describe Rpush::Daemon::Webpush::Delivery do
4
+ let(:app) { Rpush::Webpush::App.create!(name: 'MyApp', vapid_keypair: VAPID_KEYPAIR) }
5
+
6
+ # Push subscription information as received from a client browser when the
7
+ # user subscribed to push notifications.
8
+ let(:device_reg) {
9
+ { endpoint: 'https://webpush-provider.example.org/push/some-id',
10
+ keys: {'auth' => 'DgN9EBia1o057BdhCOGURA', 'p256dh' => 'BAtxJ--7vHq9IVm8utUB3peJ4lpxRqk1rukCIkVJOomS83QkCnrQ4EyYQsSaCRgy_c8XPytgXxuyAvRJdnTPK4A'} }
11
+ }
12
+
13
+ let(:data) { { message: 'some message' } }
14
+ let(:notification) { Rpush::Webpush::Notification.create!(app: app, registration_ids: [device_reg], data: data) }
15
+ let(:batch) { instance_double('Rpush::Daemon::Batch', notification_processed: nil) }
16
+ let(:response) { instance_double('Net::HTTPResponse', code: response_code, header: response_header, body: response_body) }
17
+ let(:response_code) { 201 }
18
+ let(:response_header) { {} }
19
+ let(:response_body) { nil }
20
+ let(:http) { instance_double('Net::HTTP::Persistent', request: response) }
21
+ let(:logger) { instance_double('Rpush::Logger', error: nil, info: nil, warn: nil, internal_logger: nil) }
22
+ let(:now) { Time.parse('2020-10-13 00:00:00 UTC') }
23
+
24
+ before do
25
+ allow(Rpush).to receive_messages(logger: logger)
26
+ allow(Time).to receive_messages(now: now)
27
+ end
28
+
29
+ subject(:delivery) { described_class.new(app, http, notification, batch) }
30
+
31
+ describe '#perform' do
32
+ shared_examples 'process notification' do
33
+ it 'invoke batch.notification_processed' do
34
+ subject.perform rescue nil
35
+ expect(batch).to have_received(:notification_processed)
36
+ end
37
+ end
38
+
39
+ context 'when response code is 201' do
40
+ before do
41
+ allow(batch).to receive(:mark_delivered)
42
+ Rpush::Daemon.store = Rpush::Daemon::Store.const_get(Rpush.config.client.to_s.camelcase).new
43
+ end
44
+
45
+ it 'marks the notification as delivered' do
46
+ delivery.perform
47
+ expect(batch).to have_received(:mark_delivered).with(notification)
48
+ end
49
+
50
+ it_behaves_like 'process notification'
51
+ end
52
+
53
+ shared_examples 'retry delivery' do |response_code:|
54
+ let(:response_code) { response_code }
55
+
56
+ shared_examples 'logs' do |deliver_after:|
57
+ let(:expected_log_message) do
58
+ "[MyApp] Webpush endpoint responded with a #{response_code} error. Notification #{notification.id} will be retried after #{deliver_after} (retry 1)."
59
+ end
60
+
61
+ it 'logs that the notification will be retried' do
62
+ delivery.perform
63
+ expect(logger).to have_received(:info).with(expected_log_message)
64
+ end
65
+ end
66
+
67
+ context 'when Retry-After header is present' do
68
+ let(:response_header) { { 'retry-after' => 10 } }
69
+
70
+ before do
71
+ allow(delivery).to receive(:mark_retryable) do
72
+ notification.deliver_after = now + 10.seconds
73
+ notification.retries += 1
74
+ end
75
+ end
76
+
77
+ it 'retry the notification' do
78
+ delivery.perform
79
+ expect(delivery).to have_received(:mark_retryable).with(notification, now + 10.seconds)
80
+ end
81
+
82
+ it_behaves_like 'logs', deliver_after: '2020-10-13 00:00:10'
83
+ it_behaves_like 'process notification'
84
+ end
85
+
86
+ context 'when Retry-After header is not present' do
87
+ before do
88
+ allow(delivery).to receive(:mark_retryable_exponential) do
89
+ notification.deliver_after = now + 2.seconds
90
+ notification.retries = 1
91
+ end
92
+ end
93
+
94
+ it 'retry the notification' do
95
+ delivery.perform
96
+ expect(delivery).to have_received(:mark_retryable_exponential).with(notification)
97
+ end
98
+
99
+ it_behaves_like 'logs', deliver_after: '2020-10-13 00:00:02'
100
+ it_behaves_like 'process notification'
101
+ end
102
+ end
103
+
104
+ it_behaves_like 'retry delivery', response_code: 429
105
+ it_behaves_like 'retry delivery', response_code: 500
106
+ it_behaves_like 'retry delivery', response_code: 502
107
+ it_behaves_like 'retry delivery', response_code: 503
108
+ it_behaves_like 'retry delivery', response_code: 504
109
+
110
+ context 'when delivery failed' do
111
+ let(:response_code) { 400 }
112
+ let(:fail_message) { 'that was a bad request' }
113
+ before do
114
+ allow(response).to receive(:body) { fail_message }
115
+ allow(batch).to receive(:mark_failed)
116
+ end
117
+
118
+ it 'marks the notification as failed' do
119
+ expect { delivery.perform }.to raise_error(Rpush::DeliveryError)
120
+ expected_message = "Unable to deliver notification #{notification.id}, " \
121
+ "received error 400 (Bad Request: #{fail_message})"
122
+ expect(batch).to have_received(:mark_failed).with(notification, 400, expected_message)
123
+ end
124
+
125
+ it_behaves_like 'process notification'
126
+ end
127
+
128
+ context 'when SocketError raised' do
129
+ before do
130
+ allow(http).to receive(:request) { raise SocketError }
131
+ allow(delivery).to receive(:mark_retryable)
132
+ end
133
+
134
+ it 'retry delivery after 10 seconds' do
135
+ expect { delivery.perform }.to raise_error(SocketError)
136
+ expect(delivery).to have_received(:mark_retryable).with(notification, now + 10.seconds, SocketError)
137
+ end
138
+
139
+ it_behaves_like 'process notification'
140
+ end
141
+ end
142
+ end
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.2.0
4
+ version: 5.3.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-10-08 00:00:00.000000000 Z
11
+ date: 2021-01-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: multi_json
@@ -134,6 +134,20 @@ dependencies:
134
134
  - - ">="
135
135
  - !ruby/object:Gem::Version
136
136
  version: '0'
137
+ - !ruby/object:Gem::Dependency
138
+ name: webpush
139
+ requirement: !ruby/object:Gem::Requirement
140
+ requirements:
141
+ - - "~>"
142
+ - !ruby/object:Gem::Version
143
+ version: '1.0'
144
+ type: :runtime
145
+ prerelease: false
146
+ version_requirements: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - "~>"
149
+ - !ruby/object:Gem::Version
150
+ version: '1.0'
137
151
  - !ruby/object:Gem::Dependency
138
152
  name: rake
139
153
  requirement: !ruby/object:Gem::Requirement
@@ -423,6 +437,8 @@ files:
423
437
  - lib/rpush/client/active_model/pushy/notification.rb
424
438
  - lib/rpush/client/active_model/pushy/time_to_live_validator.rb
425
439
  - lib/rpush/client/active_model/registration_ids_count_validator.rb
440
+ - lib/rpush/client/active_model/webpush/app.rb
441
+ - lib/rpush/client/active_model/webpush/notification.rb
426
442
  - lib/rpush/client/active_model/wns/app.rb
427
443
  - lib/rpush/client/active_model/wns/notification.rb
428
444
  - lib/rpush/client/active_model/wpns/app.rb
@@ -444,6 +460,8 @@ files:
444
460
  - lib/rpush/client/active_record/notification.rb
445
461
  - lib/rpush/client/active_record/pushy/app.rb
446
462
  - lib/rpush/client/active_record/pushy/notification.rb
463
+ - lib/rpush/client/active_record/webpush/app.rb
464
+ - lib/rpush/client/active_record/webpush/notification.rb
447
465
  - lib/rpush/client/active_record/wns/app.rb
448
466
  - lib/rpush/client/active_record/wns/badge_notification.rb
449
467
  - lib/rpush/client/active_record/wns/notification.rb
@@ -466,6 +484,8 @@ files:
466
484
  - lib/rpush/client/redis/notification.rb
467
485
  - lib/rpush/client/redis/pushy/app.rb
468
486
  - lib/rpush/client/redis/pushy/notification.rb
487
+ - lib/rpush/client/redis/webpush/app.rb
488
+ - lib/rpush/client/redis/webpush/notification.rb
469
489
  - lib/rpush/client/redis/wns/app.rb
470
490
  - lib/rpush/client/redis/wns/badge_notification.rb
471
491
  - lib/rpush/client/redis/wns/notification.rb
@@ -520,6 +540,8 @@ files:
520
540
  - lib/rpush/daemon/string_helpers.rb
521
541
  - lib/rpush/daemon/synchronizer.rb
522
542
  - lib/rpush/daemon/tcp_connection.rb
543
+ - lib/rpush/daemon/webpush.rb
544
+ - lib/rpush/daemon/webpush/delivery.rb
523
545
  - lib/rpush/daemon/wns.rb
524
546
  - lib/rpush/daemon/wns/badge_request.rb
525
547
  - lib/rpush/daemon/wns/delivery.rb
@@ -552,6 +574,7 @@ files:
552
574
  - spec/functional/pushy_spec.rb
553
575
  - spec/functional/retry_spec.rb
554
576
  - spec/functional/synchronization_spec.rb
577
+ - spec/functional/webpush_spec.rb
555
578
  - spec/functional/wpns_spec.rb
556
579
  - spec/functional_spec_helper.rb
557
580
  - spec/spec_helper.rb
@@ -570,6 +593,7 @@ files:
570
593
  - spec/unit/client/active_record/apns/notification_spec.rb
571
594
  - spec/unit/client/active_record/apns2/app_spec.rb
572
595
  - spec/unit/client/active_record/apns2/notification_spec.rb
596
+ - spec/unit/client/active_record/apnsp8/notification_spec.rb
573
597
  - spec/unit/client/active_record/app_spec.rb
574
598
  - spec/unit/client/active_record/gcm/app_spec.rb
575
599
  - spec/unit/client/active_record/gcm/notification_spec.rb
@@ -578,6 +602,8 @@ files:
578
602
  - spec/unit/client/active_record/pushy/notification_spec.rb
579
603
  - spec/unit/client/active_record/shared/app.rb
580
604
  - spec/unit/client/active_record/shared/notification.rb
605
+ - spec/unit/client/active_record/webpush/app_spec.rb
606
+ - spec/unit/client/active_record/webpush/notification_spec.rb
581
607
  - spec/unit/client/active_record/wns/badge_notification_spec.rb
582
608
  - spec/unit/client/active_record/wns/raw_notification_spec.rb
583
609
  - spec/unit/client/active_record/wpns/app_spec.rb
@@ -589,12 +615,15 @@ files:
589
615
  - spec/unit/client/redis/apns/notification_spec.rb
590
616
  - spec/unit/client/redis/apns2/app_spec.rb
591
617
  - spec/unit/client/redis/apns2/notification_spec.rb
618
+ - spec/unit/client/redis/apnsp8/notification_spec.rb
592
619
  - spec/unit/client/redis/app_spec.rb
593
620
  - spec/unit/client/redis/gcm/app_spec.rb
594
621
  - spec/unit/client/redis/gcm/notification_spec.rb
595
622
  - spec/unit/client/redis/notification_spec.rb
596
623
  - spec/unit/client/redis/pushy/app_spec.rb
597
624
  - spec/unit/client/redis/pushy/notification_spec.rb
625
+ - spec/unit/client/redis/webpush/app_spec.rb
626
+ - spec/unit/client/redis/webpush/notification_spec.rb
598
627
  - spec/unit/client/redis/wns/badge_notification_spec.rb
599
628
  - spec/unit/client/redis/wns/raw_notification_spec.rb
600
629
  - spec/unit/client/redis/wpns/app_spec.rb
@@ -610,6 +639,8 @@ files:
610
639
  - spec/unit/client/shared/notification.rb
611
640
  - spec/unit/client/shared/pushy/app.rb
612
641
  - spec/unit/client/shared/pushy/notification.rb
642
+ - spec/unit/client/shared/webpush/app.rb
643
+ - spec/unit/client/shared/webpush/notification.rb
613
644
  - spec/unit/client/shared/wns/badge_notification.rb
614
645
  - spec/unit/client/shared/wns/raw_notification.rb
615
646
  - spec/unit/client/shared/wpns/app.rb
@@ -638,6 +669,7 @@ files:
638
669
  - spec/unit/daemon/store/active_record_spec.rb
639
670
  - spec/unit/daemon/store/redis_spec.rb
640
671
  - spec/unit/daemon/tcp_connection_spec.rb
672
+ - spec/unit/daemon/webpush/delivery_spec.rb
641
673
  - spec/unit/daemon/wns/delivery_spec.rb
642
674
  - spec/unit/daemon/wns/post_request_spec.rb
643
675
  - spec/unit/daemon/wpns/delivery_spec.rb
@@ -695,6 +727,7 @@ test_files:
695
727
  - spec/functional/pushy_spec.rb
696
728
  - spec/functional/retry_spec.rb
697
729
  - spec/functional/synchronization_spec.rb
730
+ - spec/functional/webpush_spec.rb
698
731
  - spec/functional/wpns_spec.rb
699
732
  - spec/functional_spec_helper.rb
700
733
  - spec/spec_helper.rb
@@ -713,6 +746,7 @@ test_files:
713
746
  - spec/unit/client/active_record/apns/notification_spec.rb
714
747
  - spec/unit/client/active_record/apns2/app_spec.rb
715
748
  - spec/unit/client/active_record/apns2/notification_spec.rb
749
+ - spec/unit/client/active_record/apnsp8/notification_spec.rb
716
750
  - spec/unit/client/active_record/app_spec.rb
717
751
  - spec/unit/client/active_record/gcm/app_spec.rb
718
752
  - spec/unit/client/active_record/gcm/notification_spec.rb
@@ -721,6 +755,8 @@ test_files:
721
755
  - spec/unit/client/active_record/pushy/notification_spec.rb
722
756
  - spec/unit/client/active_record/shared/app.rb
723
757
  - spec/unit/client/active_record/shared/notification.rb
758
+ - spec/unit/client/active_record/webpush/app_spec.rb
759
+ - spec/unit/client/active_record/webpush/notification_spec.rb
724
760
  - spec/unit/client/active_record/wns/badge_notification_spec.rb
725
761
  - spec/unit/client/active_record/wns/raw_notification_spec.rb
726
762
  - spec/unit/client/active_record/wpns/app_spec.rb
@@ -732,12 +768,15 @@ test_files:
732
768
  - spec/unit/client/redis/apns/notification_spec.rb
733
769
  - spec/unit/client/redis/apns2/app_spec.rb
734
770
  - spec/unit/client/redis/apns2/notification_spec.rb
771
+ - spec/unit/client/redis/apnsp8/notification_spec.rb
735
772
  - spec/unit/client/redis/app_spec.rb
736
773
  - spec/unit/client/redis/gcm/app_spec.rb
737
774
  - spec/unit/client/redis/gcm/notification_spec.rb
738
775
  - spec/unit/client/redis/notification_spec.rb
739
776
  - spec/unit/client/redis/pushy/app_spec.rb
740
777
  - spec/unit/client/redis/pushy/notification_spec.rb
778
+ - spec/unit/client/redis/webpush/app_spec.rb
779
+ - spec/unit/client/redis/webpush/notification_spec.rb
741
780
  - spec/unit/client/redis/wns/badge_notification_spec.rb
742
781
  - spec/unit/client/redis/wns/raw_notification_spec.rb
743
782
  - spec/unit/client/redis/wpns/app_spec.rb
@@ -753,6 +792,8 @@ test_files:
753
792
  - spec/unit/client/shared/notification.rb
754
793
  - spec/unit/client/shared/pushy/app.rb
755
794
  - spec/unit/client/shared/pushy/notification.rb
795
+ - spec/unit/client/shared/webpush/app.rb
796
+ - spec/unit/client/shared/webpush/notification.rb
756
797
  - spec/unit/client/shared/wns/badge_notification.rb
757
798
  - spec/unit/client/shared/wns/raw_notification.rb
758
799
  - spec/unit/client/shared/wpns/app.rb
@@ -781,6 +822,7 @@ test_files:
781
822
  - spec/unit/daemon/store/active_record_spec.rb
782
823
  - spec/unit/daemon/store/redis_spec.rb
783
824
  - spec/unit/daemon/tcp_connection_spec.rb
825
+ - spec/unit/daemon/webpush/delivery_spec.rb
784
826
  - spec/unit/daemon/wns/delivery_spec.rb
785
827
  - spec/unit/daemon/wns/post_request_spec.rb
786
828
  - spec/unit/daemon/wpns/delivery_spec.rb