rapns 3.0.1-java → 3.1.0-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. data/CHANGELOG.md +10 -2
  2. data/README.md +26 -3
  3. data/bin/rapns +2 -7
  4. data/lib/generators/templates/add_gcm.rb +3 -1
  5. data/lib/generators/templates/rapns.rb +42 -11
  6. data/lib/rapns.rb +11 -1
  7. data/lib/rapns/apns/notification.rb +8 -2
  8. data/lib/rapns/app.rb +3 -3
  9. data/lib/rapns/configuration.rb +46 -13
  10. data/lib/rapns/daemon.rb +33 -22
  11. data/lib/rapns/daemon/apns/connection.rb +12 -9
  12. data/lib/rapns/daemon/apns/delivery_handler.rb +1 -1
  13. data/lib/rapns/daemon/apns/feedback_receiver.rb +6 -2
  14. data/lib/rapns/daemon/app_runner.rb +23 -7
  15. data/lib/rapns/daemon/delivery.rb +5 -1
  16. data/lib/rapns/daemon/delivery_handler.rb +4 -0
  17. data/lib/rapns/daemon/feeder.rb +26 -5
  18. data/lib/rapns/daemon/reflectable.rb +13 -0
  19. data/lib/rapns/embed.rb +28 -0
  20. data/lib/rapns/gcm/notification.rb +7 -2
  21. data/lib/rapns/gcm/payload_data_size_validator.rb +13 -0
  22. data/lib/rapns/gcm/registration_ids_count_validator.rb +13 -0
  23. data/lib/rapns/push.rb +12 -0
  24. data/lib/rapns/reflection.rb +44 -0
  25. data/lib/rapns/version.rb +1 -1
  26. data/spec/support/cert_with_password.pem +90 -0
  27. data/spec/support/cert_without_password.pem +59 -0
  28. data/spec/unit/apns/app_spec.rb +15 -1
  29. data/spec/unit/apns/notification_spec.rb +16 -1
  30. data/spec/unit/configuration_spec.rb +10 -1
  31. data/spec/unit/daemon/apns/connection_spec.rb +11 -2
  32. data/spec/unit/daemon/apns/delivery_handler_spec.rb +1 -1
  33. data/spec/unit/daemon/apns/delivery_spec.rb +10 -0
  34. data/spec/unit/daemon/apns/feedback_receiver_spec.rb +16 -7
  35. data/spec/unit/daemon/delivery_handler_shared.rb +8 -0
  36. data/spec/unit/daemon/feeder_spec.rb +37 -6
  37. data/spec/unit/daemon/gcm/delivery_spec.rb +33 -1
  38. data/spec/unit/daemon/reflectable_spec.rb +27 -0
  39. data/spec/unit/daemon_spec.rb +55 -9
  40. data/spec/unit/embed_spec.rb +44 -0
  41. data/spec/unit/gcm/notification_spec.rb +9 -3
  42. data/spec/unit/push_spec.rb +28 -0
  43. data/spec/unit/reflection_spec.rb +34 -0
  44. data/spec/unit_spec_helper.rb +4 -62
  45. metadata +22 -5
  46. data/lib/rapns/gcm/payload_size_validator.rb +0 -13
@@ -4,18 +4,20 @@ module Rapns
4
4
  class ConnectionError < StandardError; end
5
5
 
6
6
  class Connection
7
+ include Reflectable
8
+
7
9
  attr_accessor :last_write
8
10
 
9
11
  def self.idle_period
10
12
  30.minutes
11
13
  end
12
14
 
13
- def initialize(name, host, port, certificate, password)
14
- @name = name
15
+ def initialize(app, host, port)
16
+ @app = app
15
17
  @host = host
16
18
  @port = port
17
- @certificate = certificate
18
- @password = password
19
+ @certificate = app.certificate
20
+ @password = app.password
19
21
  written
20
22
  end
21
23
 
@@ -51,7 +53,8 @@ module Rapns
51
53
  retry_count += 1;
52
54
 
53
55
  if retry_count == 1
54
- Rapns::Daemon.logger.error("[#{@name}] Lost connection to #{@host}:#{@port} (#{e.class.name}), reconnecting...")
56
+ Rapns::Daemon.logger.error("[#{@app.name}] Lost connection to #{@host}:#{@port} (#{e.class.name}), reconnecting...")
57
+ reflect(:apns_connection_lost, @app, e)
55
58
  end
56
59
 
57
60
  if retry_count <= 3
@@ -59,7 +62,7 @@ module Rapns
59
62
  sleep 1
60
63
  retry
61
64
  else
62
- raise ConnectionError, "#{@name} tried #{retry_count-1} times to reconnect but failed (#{e.class.name})."
65
+ raise ConnectionError, "#{@app.name} tried #{retry_count-1} times to reconnect but failed (#{e.class.name})."
63
66
  end
64
67
  end
65
68
  end
@@ -72,7 +75,7 @@ module Rapns
72
75
  protected
73
76
 
74
77
  def reconnect_idle
75
- Rapns::Daemon.logger.info("[#{@name}] Idle period exceeded, reconnecting...")
78
+ Rapns::Daemon.logger.info("[#{@app.name}] Idle period exceeded, reconnecting...")
76
79
  reconnect
77
80
  end
78
81
 
@@ -104,10 +107,10 @@ module Rapns
104
107
  ssl_socket = OpenSSL::SSL::SSLSocket.new(tcp_socket, @ssl_context)
105
108
  ssl_socket.sync = true
106
109
  ssl_socket.connect
107
- Rapns::Daemon.logger.info("[#{@name}] Connected to #{@host}:#{@port}")
110
+ Rapns::Daemon.logger.info("[#{@app.name}] Connected to #{@host}:#{@port}")
108
111
  [tcp_socket, ssl_socket]
109
112
  end
110
113
  end
111
114
  end
112
115
  end
113
- end
116
+ end
@@ -4,7 +4,7 @@ module Rapns
4
4
  class DeliveryHandler < Rapns::Daemon::DeliveryHandler
5
5
  def initialize(app, host, port)
6
6
  @app = app
7
- @connection = Connection.new(@app.name, host, port, @app.certificate, @app.password)
7
+ @connection = Connection.new(@app, host, port)
8
8
  @connection.connect
9
9
  end
10
10
 
@@ -2,6 +2,7 @@ module Rapns
2
2
  module Daemon
3
3
  module Apns
4
4
  class FeedbackReceiver
5
+ include Reflectable
5
6
  include InterruptibleSleep
6
7
  include DatabaseReconnectable
7
8
 
@@ -35,7 +36,7 @@ module Rapns
35
36
  def check_for_feedback
36
37
  connection = nil
37
38
  begin
38
- connection = Connection.new("FeedbackReceiver:#{@app.name}", @host, @port, @certificate, @password)
39
+ connection = Connection.new(@app, @host, @port)
39
40
  connection.connect
40
41
 
41
42
  while tuple = connection.read(FEEDBACK_TUPLE_BYTES)
@@ -59,8 +60,11 @@ module Rapns
59
60
  def create_feedback(failed_at, device_token)
60
61
  formatted_failed_at = failed_at.strftime("%Y-%m-%d %H:%M:%S UTC")
61
62
  with_database_reconnect_and_retry do
62
- Rapns::Daemon.logger.info("[FeedbackReceiver:#{@app.name}] Delivery failed at #{formatted_failed_at} for #{device_token}")
63
+ Rapns::Daemon.logger.info("[#{@app.name}] [FeedbackReceiver] Delivery failed at #{formatted_failed_at} for #{device_token}.")
63
64
  feedback = Rapns::Apns::Feedback.create!(:failed_at => failed_at, :device_token => device_token, :app => @app)
65
+ reflect(:apns_feedback, feedback)
66
+
67
+ # Deprecated.
64
68
  begin
65
69
  Rapns.config.apns_feedback_callback.call(feedback) if Rapns.config.apns_feedback_callback
66
70
  rescue StandardError => e
@@ -2,7 +2,7 @@ module Rapns
2
2
  module Daemon
3
3
  class AppRunner
4
4
  class << self
5
- attr_reader :runners # TODO: Needed?
5
+ attr_reader :runners
6
6
  end
7
7
 
8
8
  @runners = {}
@@ -86,20 +86,28 @@ module Rapns
86
86
  diff = handlers.size - app.connections
87
87
  return if diff == 0
88
88
  if diff > 0
89
- diff.times { handlers.pop.stop }
90
- Rapns::Daemon.logger.info("[#{app.name}] Terminated #{handlers_str(diff)}. #{handlers_str} remaining.")
89
+ diff.times { decrement_handlers }
90
+ Rapns::Daemon.logger.info("[#{app.name}] Stopped #{handlers_str(diff)}. #{handlers_str} remaining.")
91
91
  else
92
- diff.abs.times { handlers << start_handler }
93
- Rapns::Daemon.logger.info("[#{app.name}] Added #{handlers_str(diff)}. #{handlers_str} remaining.")
92
+ diff.abs.times { increment_handlers }
93
+ Rapns::Daemon.logger.info("[#{app.name}] Started #{handlers_str(diff)}. #{handlers_str} remaining.")
94
94
  end
95
95
  end
96
96
 
97
+ def decrement_handlers
98
+ handlers.pop.stop
99
+ end
100
+
101
+ def increment_handlers
102
+ handlers << start_handler
103
+ end
104
+
97
105
  def debug
98
106
  Rapns::Daemon.logger.info <<-EOS
99
107
 
100
108
  #{@app.name}:
101
- handlers: #{handlers.size}
102
- queued: #{queue.size}
109
+ handlers: #{num_handlers}
110
+ queued: #{queue_size}
103
111
  idle: #{idle?}
104
112
  EOS
105
113
  end
@@ -108,6 +116,14 @@ module Rapns
108
116
  queue.notifications_processed?
109
117
  end
110
118
 
119
+ def queue_size
120
+ queue.size
121
+ end
122
+
123
+ def num_handlers
124
+ handlers.size
125
+ end
126
+
111
127
  protected
112
128
 
113
129
  def start_handler
@@ -2,6 +2,7 @@ module Rapns
2
2
  module Daemon
3
3
  class Delivery
4
4
  include DatabaseReconnectable
5
+ include Reflectable
5
6
 
6
7
  def self.perform(*args)
7
8
  new(*args).perform
@@ -13,6 +14,7 @@ module Rapns
13
14
  notification.deliver_after = deliver_after
14
15
  notification.save!(:validate => false)
15
16
  end
17
+ reflect(:notification_will_retry, notification)
16
18
  end
17
19
 
18
20
  def retry_exponentially(notification)
@@ -25,6 +27,7 @@ module Rapns
25
27
  @notification.delivered_at = Time.now
26
28
  @notification.save!(:validate => false)
27
29
  end
30
+ reflect(:notification_delivered, @notification)
28
31
  end
29
32
 
30
33
  def mark_failed(code, description)
@@ -37,7 +40,8 @@ module Rapns
37
40
  @notification.error_description = description
38
41
  @notification.save!(:validate => false)
39
42
  end
43
+ reflect(:notification_failed, @notification)
40
44
  end
41
45
  end
42
46
  end
43
- end
47
+ end
@@ -1,6 +1,8 @@
1
1
  module Rapns
2
2
  module Daemon
3
3
  class DeliveryHandler
4
+ include Reflectable
5
+
4
6
  attr_accessor :queue
5
7
 
6
8
  def start
@@ -35,8 +37,10 @@ module Rapns
35
37
 
36
38
  begin
37
39
  deliver(notification)
40
+ reflect(:notification_delivered, notification)
38
41
  rescue StandardError => e
39
42
  Rapns::Daemon.logger.error(e)
43
+ reflect(:error, e)
40
44
  ensure
41
45
  queue.notification_processed
42
46
  end
@@ -3,12 +3,17 @@ module Rapns
3
3
  class Feeder
4
4
  extend InterruptibleSleep
5
5
  extend DatabaseReconnectable
6
+ extend Reflectable
6
7
 
7
- def self.start(poll)
8
- loop do
8
+ def self.start
9
+ @stop = false
10
+
11
+ if Rapns.config.embedded
12
+ Thread.new { feed_forever }
13
+ elsif Rapns.config.push
9
14
  enqueue_notifications
10
- interruptible_sleep poll
11
- break if @stop
15
+ else
16
+ feed_forever
12
17
  end
13
18
  end
14
19
 
@@ -19,17 +24,33 @@ module Rapns
19
24
 
20
25
  protected
21
26
 
27
+ def self.feed_forever
28
+ loop do
29
+ enqueue_notifications
30
+ interruptible_sleep(Rapns.config.push_poll)
31
+ break if stop?
32
+ end
33
+ end
34
+
35
+ def self.stop?
36
+ @stop
37
+ end
38
+
22
39
  def self.enqueue_notifications
23
40
  begin
24
41
  with_database_reconnect_and_retry do
25
42
  batch_size = Rapns.config.batch_size
26
43
  idle = Rapns::Daemon::AppRunner.idle.map(&:app)
27
- Rapns::Notification.ready_for_delivery.for_apps(idle).limit(batch_size).each do |notification|
44
+ relation = Rapns::Notification.ready_for_delivery.for_apps(idle)
45
+ relation = relation.limit(batch_size) unless Rapns.config.push
46
+ relation.each do |notification|
28
47
  Rapns::Daemon::AppRunner.enqueue(notification)
48
+ reflect(:notification_enqueued, notification)
29
49
  end
30
50
  end
31
51
  rescue StandardError => e
32
52
  Rapns::Daemon.logger.error(e)
53
+ reflect(:error, e)
33
54
  end
34
55
  end
35
56
  end
@@ -0,0 +1,13 @@
1
+ module Rapns
2
+ module Daemon
3
+ module Reflectable
4
+ def reflect(name, *args)
5
+ begin
6
+ Rapns.reflections.__dispatch(name, *args)
7
+ rescue StandardError => e
8
+ Rapns::Daemon.logger.error(e)
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,28 @@
1
+ module Rapns
2
+ def self.embed(options = {})
3
+ Rapns.require_for_daemon
4
+
5
+ config = Rapns::ConfigurationWithoutDefaults.new
6
+ options.each { |k, v| config.send("#{k}=", v) }
7
+ config.embedded = true
8
+ Rapns.config.update(config)
9
+ Rapns::Daemon.start
10
+
11
+ Kernel.at_exit { shutdown }
12
+ end
13
+
14
+ def self.shutdown
15
+ return unless Rapns.config.embedded
16
+ Rapns::Daemon.shutdown
17
+ end
18
+
19
+ def self.sync
20
+ return unless Rapns.config.embedded
21
+ Rapns::Daemon::AppRunner.sync
22
+ end
23
+
24
+ def self.debug
25
+ return unless Rapns.config.embedded
26
+ Rapns::Daemon::AppRunner.debug
27
+ end
28
+ end
@@ -3,7 +3,8 @@ module Rapns
3
3
  class Notification < Rapns::Notification
4
4
  validates :registration_ids, :presence => true
5
5
  validates_with Rapns::Gcm::ExpiryCollapseKeyMutualInclusionValidator
6
- validates_with Rapns::Gcm::PayloadSizeValidator
6
+ validates_with Rapns::Gcm::PayloadDataSizeValidator
7
+ validates_with Rapns::Gcm::RegistrationIdsCountValidator
7
8
 
8
9
  def registration_ids=(ids)
9
10
  ids = [ids] if ids && !ids.is_a?(Array)
@@ -26,6 +27,10 @@ module Rapns
26
27
 
27
28
  json
28
29
  end
30
+
31
+ def payload_data_size
32
+ multi_json_dump(as_json['data']).bytesize
33
+ end
29
34
  end
30
35
  end
31
- end
36
+ end
@@ -0,0 +1,13 @@
1
+ module Rapns
2
+ module Gcm
3
+ class PayloadDataSizeValidator < ActiveModel::Validator
4
+ LIMIT = 4096
5
+
6
+ def validate(record)
7
+ if record.payload_data_size > LIMIT
8
+ record.errors[:base] << "GCM notification payload data cannot be larger than #{LIMIT} bytes."
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ module Rapns
2
+ module Gcm
3
+ class RegistrationIdsCountValidator < ActiveModel::Validator
4
+ LIMIT = 1000
5
+
6
+ def validate(record)
7
+ if record.registration_ids && record.registration_ids.size > LIMIT
8
+ record.errors[:base] << "GCM notification num of registration_ids cannot be larger than #{LIMIT}."
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
data/lib/rapns/push.rb ADDED
@@ -0,0 +1,12 @@
1
+ module Rapns
2
+ def self.push(options = {})
3
+ Rapns.require_for_daemon
4
+
5
+ config = Rapns::ConfigurationWithoutDefaults.new
6
+ options.each { |k, v| config.send("#{k}=", v) }
7
+ config.push = true
8
+ Rapns.config.update(config)
9
+ Rapns::Daemon.start
10
+ Rapns::Daemon.shutdown(true)
11
+ end
12
+ end
@@ -0,0 +1,44 @@
1
+ module Rapns
2
+ def self.reflect
3
+ yield reflections if block_given?
4
+ end
5
+
6
+ def self.reflections
7
+ @reflections ||= Reflections.new
8
+ end
9
+
10
+ class Reflections
11
+ class NoSuchReflectionError < StandardError; end
12
+
13
+ REFLECTIONS = [
14
+ :apns_feedback, :notification_enqueued, :notification_delivered,
15
+ :notification_failed, :notification_will_retry, :apns_connection_lost,
16
+ :error
17
+ ]
18
+
19
+ REFLECTIONS.each do |reflection|
20
+ class_eval(<<-RUBY, __FILE__, __LINE__)
21
+ def #{reflection}(*args, &blk)
22
+ raise "block required" unless block_given?
23
+ reflections[:#{reflection}] = blk
24
+ end
25
+ RUBY
26
+ end
27
+
28
+ def __dispatch(reflection, *args)
29
+ unless REFLECTIONS.include?(reflection.to_sym)
30
+ raise NoSuchReflectionError, reflection
31
+ end
32
+
33
+ if reflections[reflection]
34
+ reflections[reflection].call(*args)
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ def reflections
41
+ @reflections ||= {}
42
+ end
43
+ end
44
+ end
data/lib/rapns/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Rapns
2
- VERSION = '3.0.1'
2
+ VERSION = '3.1.0'
3
3
  end
@@ -0,0 +1,90 @@
1
+ Bag Attributes
2
+ localKeyID: A4 1A DB 3E 3E 45 D9 C7 51 1E E6 DC 4E BC 29 19 E4 22 95 35
3
+ subject=/C=AU/ST=Some-State/O=Internet Widgits Pty Ltd
4
+ issuer=/C=UK/ST=Some-State/O=Internet Widgits Pty Ltd
5
+ -----BEGIN CERTIFICATE-----
6
+ MIIE/jCCAuYCAQEwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCVUsxEzARBgNV
7
+ BAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0
8
+ ZDAeFw0xMjEyMjgxMzU4NTdaFw0xNDEyMjgxMzU4NTdaMEUxCzAJBgNVBAYTAkFV
9
+ MRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRz
10
+ IFB0eSBMdGQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDYCSfYP0P7
11
+ F+JbxUHYsl/9Plr2bFZGMBygqs5RWpw1X977p4FqcNtnRmGENQ/I5vE3KkAKCAlS
12
+ gmus7CjB9/FVzoJq65cT3wBkcqAAzPgIQuI0UjP+rLktV84eUup7Pt6f/Bmt/fBj
13
+ h3fvf80nBQcdK2ztu1xtTs7EsRgyudUDDEIUZw9ddK21RbCMYE0PFY0QapNVvevs
14
+ 8lDVUJX60bZqVqzhvlTzmEaBF+E66J/DhHuhcWZV588hVKjHMNED9Aq4PxRy78vI
15
+ 54v1buX2Y9C6dHfIzfu7zz0Zv5NAY1BZKpaglVGKArOrX+K6O7Bq+DwckTm1AbUf
16
+ pQqi10ghveOtVrm4GXJ4OYW8ohdYKMFjzwhgTTr/NQ+EVlcZ+8AOPVPPJk0HgPu/
17
+ eMNiLytvcSnB09OzIUAzcxOof2a1zfxn7aPzBTEC6kkoDJC/BDG8wxySUz0zRyyM
18
+ jUN2+J1mBJZX1h1sM/4ycAzsX1EYm2II5GGJaFngiV45Qv7wTC7W31kvih7FsdUX
19
+ rEYMkevB5AFPUtIvzevLUObLbPW4yWvU2CMcKLZdIaPTvtPMba91t1YOdufpPRDd
20
+ HTmT42h0aUWDSrcWDAuZPPPqEIQNjRVCudtHtobZDeaUfvbx05CvypyuNFaPonuQ
21
+ l5h0LpZWPetvfsrOlMxPm2kfVcxm8mWjzQIDAQABMA0GCSqGSIb3DQEBBQUAA4IC
22
+ AQBiwdPfLU0kjbU1hM6grUHduLqOPDqLT5gefGRrL5fDqb86G08+Wz+Tnn1YmH+7
23
+ rfiQhCrdi4zxRv6ZEaKZeEm/q1UMttuUdXuzd25BEEtav4R0POdR+H1q3trlS0ol
24
+ EcWlAbRgVaT2tTyKGAW54fH8vZPqS6IP+mXIzfaOFECPEgAO8BL8t7hBDpkL4ASO
25
+ HbU7ktYamsr6PAei3kXAnJ1thXyQqrhelwLrQJyM8RVOJYUgVbl6Fpdtgu7YQF5G
26
+ kxTAfshSmDCQkf+tbUEs44rZ6BZ2TWnbXjQGkRntHcMCP/1rjsPPdX3oeZ7P3jMQ
27
+ XER3drdm1mOiSdDUKbem9GzQ9Dx7WkwKNLYAZ+IWzVRACzGxnkxXyxOEFIgesDgg
28
+ RIhczN+eLIR8iwcUxEVKFmmsbEIve3Uh1/NE6xGudbfZDNfhyOhiNYIBnQqVkk8l
29
+ c3gw2UDR3lXTayiiXhK2l1etsyxtYncT3pgDsCe72RODrGKbASt3FzfBbalzN0GG
30
+ 9tiPSNtGqCch9q4eHfViUh9s3+8n4bknAYcwzQ96+gMEn8PUVtDBv9F8Kxffn/Jt
31
+ XMWKX76nTVuAuCLkXxrKwc01lq8SeuvCH650xsv0LBvxj9h6vR34vHGrj0C3sH2E
32
+ VQpelKNv8IEwkSiQcwDtU8H0jaPJNqmYlkrxasSrSeg6PA==
33
+ -----END CERTIFICATE-----
34
+ Bag Attributes
35
+ localKeyID: A4 1A DB 3E 3E 45 D9 C7 51 1E E6 DC 4E BC 29 19 E4 22 95 35
36
+ Key Attributes: <No Attributes>
37
+ -----BEGIN RSA PRIVATE KEY-----
38
+ Proc-Type: 4,ENCRYPTED
39
+ DEK-Info: DES-EDE3-CBC,874F2C982467BF08
40
+
41
+ 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
90
+ -----END RSA PRIVATE KEY-----