rapns_rails_2 3.4.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (113) hide show
  1. checksums.yaml +15 -0
  2. data/CHANGELOG.md +83 -0
  3. data/LICENSE +7 -0
  4. data/README.md +168 -0
  5. data/bin/rapns +37 -0
  6. data/config/database.yml +44 -0
  7. data/lib/generators/rapns_generator.rb +25 -0
  8. data/lib/generators/templates/add_alert_is_json_to_rapns_notifications.rb +9 -0
  9. data/lib/generators/templates/add_app_to_rapns.rb +11 -0
  10. data/lib/generators/templates/add_gcm.rb +95 -0
  11. data/lib/generators/templates/create_rapns_apps.rb +16 -0
  12. data/lib/generators/templates/create_rapns_feedback.rb +15 -0
  13. data/lib/generators/templates/create_rapns_notifications.rb +26 -0
  14. data/lib/generators/templates/rapns.rb +87 -0
  15. data/lib/rapns/TODO +3 -0
  16. data/lib/rapns/apns/app.rb +25 -0
  17. data/lib/rapns/apns/binary_notification_validator.rb +12 -0
  18. data/lib/rapns/apns/device_token_format_validator.rb +12 -0
  19. data/lib/rapns/apns/feedback.rb +16 -0
  20. data/lib/rapns/apns/notification.rb +91 -0
  21. data/lib/rapns/apns_feedback.rb +13 -0
  22. data/lib/rapns/app.rb +16 -0
  23. data/lib/rapns/configuration.rb +89 -0
  24. data/lib/rapns/daemon/apns/app_runner.rb +26 -0
  25. data/lib/rapns/daemon/apns/certificate_expired_error.rb +20 -0
  26. data/lib/rapns/daemon/apns/connection.rb +142 -0
  27. data/lib/rapns/daemon/apns/delivery.rb +64 -0
  28. data/lib/rapns/daemon/apns/delivery_handler.rb +35 -0
  29. data/lib/rapns/daemon/apns/disconnection_error.rb +20 -0
  30. data/lib/rapns/daemon/apns/feedback_receiver.rb +89 -0
  31. data/lib/rapns/daemon/app_runner.rb +179 -0
  32. data/lib/rapns/daemon/batch.rb +112 -0
  33. data/lib/rapns/daemon/delivery.rb +23 -0
  34. data/lib/rapns/daemon/delivery_error.rb +19 -0
  35. data/lib/rapns/daemon/delivery_handler.rb +52 -0
  36. data/lib/rapns/daemon/delivery_handler_collection.rb +33 -0
  37. data/lib/rapns/daemon/feeder.rb +65 -0
  38. data/lib/rapns/daemon/gcm/app_runner.rb +13 -0
  39. data/lib/rapns/daemon/gcm/delivery.rb +228 -0
  40. data/lib/rapns/daemon/gcm/delivery_handler.rb +20 -0
  41. data/lib/rapns/daemon/interruptible_sleep.rb +65 -0
  42. data/lib/rapns/daemon/reflectable.rb +13 -0
  43. data/lib/rapns/daemon/store/active_record/reconnectable.rb +66 -0
  44. data/lib/rapns/daemon/store/active_record.rb +128 -0
  45. data/lib/rapns/daemon.rb +129 -0
  46. data/lib/rapns/deprecatable.rb +23 -0
  47. data/lib/rapns/deprecation.rb +23 -0
  48. data/lib/rapns/embed.rb +28 -0
  49. data/lib/rapns/gcm/app.rb +7 -0
  50. data/lib/rapns/gcm/expiry_collapse_key_mutual_inclusion_validator.rb +11 -0
  51. data/lib/rapns/gcm/notification.rb +37 -0
  52. data/lib/rapns/gcm/payload_data_size_validator.rb +13 -0
  53. data/lib/rapns/gcm/registration_ids_count_validator.rb +13 -0
  54. data/lib/rapns/logger.rb +76 -0
  55. data/lib/rapns/multi_json_helper.rb +16 -0
  56. data/lib/rapns/notification.rb +62 -0
  57. data/lib/rapns/notifier.rb +35 -0
  58. data/lib/rapns/push.rb +17 -0
  59. data/lib/rapns/rails-2-compatibility.rb +34 -0
  60. data/lib/rapns/reflection.rb +44 -0
  61. data/lib/rapns/upgraded.rb +31 -0
  62. data/lib/rapns/version.rb +3 -0
  63. data/lib/rapns_rails_2.rb +67 -0
  64. data/lib/tasks/cane.rake +18 -0
  65. data/lib/tasks/test.rake +38 -0
  66. data/spec/support/cert_with_password.pem +90 -0
  67. data/spec/support/cert_without_password.pem +59 -0
  68. data/spec/support/simplecov_helper.rb +13 -0
  69. data/spec/support/simplecov_quality_formatter.rb +8 -0
  70. data/spec/tmp/.gitkeep +0 -0
  71. data/spec/unit/apns/app_spec.rb +29 -0
  72. data/spec/unit/apns/feedback_spec.rb +9 -0
  73. data/spec/unit/apns/notification_spec.rb +215 -0
  74. data/spec/unit/apns_feedback_spec.rb +21 -0
  75. data/spec/unit/app_spec.rb +16 -0
  76. data/spec/unit/configuration_spec.rb +55 -0
  77. data/spec/unit/daemon/apns/app_runner_spec.rb +45 -0
  78. data/spec/unit/daemon/apns/certificate_expired_error_spec.rb +11 -0
  79. data/spec/unit/daemon/apns/connection_spec.rb +287 -0
  80. data/spec/unit/daemon/apns/delivery_handler_spec.rb +59 -0
  81. data/spec/unit/daemon/apns/delivery_spec.rb +101 -0
  82. data/spec/unit/daemon/apns/disconnection_error_spec.rb +18 -0
  83. data/spec/unit/daemon/apns/feedback_receiver_spec.rb +134 -0
  84. data/spec/unit/daemon/app_runner_shared.rb +83 -0
  85. data/spec/unit/daemon/app_runner_spec.rb +170 -0
  86. data/spec/unit/daemon/batch_spec.rb +219 -0
  87. data/spec/unit/daemon/delivery_error_spec.rb +13 -0
  88. data/spec/unit/daemon/delivery_handler_collection_spec.rb +37 -0
  89. data/spec/unit/daemon/delivery_handler_shared.rb +45 -0
  90. data/spec/unit/daemon/feeder_spec.rb +81 -0
  91. data/spec/unit/daemon/gcm/app_runner_spec.rb +19 -0
  92. data/spec/unit/daemon/gcm/delivery_handler_spec.rb +44 -0
  93. data/spec/unit/daemon/gcm/delivery_spec.rb +289 -0
  94. data/spec/unit/daemon/interruptible_sleep_spec.rb +68 -0
  95. data/spec/unit/daemon/reflectable_spec.rb +27 -0
  96. data/spec/unit/daemon/store/active_record/reconnectable_spec.rb +114 -0
  97. data/spec/unit/daemon/store/active_record_spec.rb +281 -0
  98. data/spec/unit/daemon_spec.rb +157 -0
  99. data/spec/unit/deprecatable_spec.rb +32 -0
  100. data/spec/unit/deprecation_spec.rb +15 -0
  101. data/spec/unit/embed_spec.rb +50 -0
  102. data/spec/unit/gcm/app_spec.rb +4 -0
  103. data/spec/unit/gcm/notification_spec.rb +52 -0
  104. data/spec/unit/logger_spec.rb +180 -0
  105. data/spec/unit/notification_shared.rb +45 -0
  106. data/spec/unit/notification_spec.rb +4 -0
  107. data/spec/unit/notifier_spec.rb +32 -0
  108. data/spec/unit/push_spec.rb +44 -0
  109. data/spec/unit/rapns_spec.rb +9 -0
  110. data/spec/unit/reflection_spec.rb +30 -0
  111. data/spec/unit/upgraded_spec.rb +40 -0
  112. data/spec/unit_spec_helper.rb +137 -0
  113. metadata +232 -0
@@ -0,0 +1,179 @@
1
+ module Rapns
2
+ module Daemon
3
+ class AppRunner
4
+ extend Reflectable
5
+ include Reflectable
6
+
7
+ class << self
8
+ attr_reader :runners
9
+ end
10
+
11
+ @runners = {}
12
+
13
+ def self.enqueue(notifications)
14
+ notifications.group_by(&:app_id).each do |app_id, group|
15
+ batch = Batch.new(group)
16
+ if app = runners[app_id]
17
+ app.enqueue(batch)
18
+ else
19
+ Rapns.logger.error("No such app '#{app_id}' for notifications #{batch.describe}.")
20
+ end
21
+ end
22
+ end
23
+
24
+ def self.sync
25
+ apps = Rapns::App.all
26
+ apps.each { |app| sync_app(app) }
27
+ removed = runners.keys - apps.map(&:id)
28
+ removed.each { |app_id| runners.delete(app_id).stop }
29
+ end
30
+
31
+ def self.sync_app(app)
32
+ if runners[app.id]
33
+ runners[app.id].sync(app)
34
+ else
35
+ runner = new_runner(app)
36
+ begin
37
+ runner.start
38
+ runners[app.id] = runner
39
+ rescue StandardError => e
40
+ Rapns.logger.error("[#{app.name}] Exception raised during startup. Notifications will not be delivered for this app.")
41
+ Rapns.logger.error(e)
42
+ reflect(:error, e)
43
+ end
44
+ end
45
+ end
46
+
47
+ def self.new_runner(app)
48
+ type = app.class.parent.name.demodulize
49
+ "Rapns::Daemon::#{type}::AppRunner".constantize.new(app)
50
+ end
51
+
52
+ def self.stop
53
+ runners.values.map(&:stop)
54
+ runners.clear
55
+ end
56
+
57
+ def self.debug
58
+ runners.values.map(&:debug)
59
+ end
60
+
61
+ def self.idle
62
+ runners.values.select(&:idle?)
63
+ end
64
+
65
+ def self.wait
66
+ sleep 0.1 while !runners.values.all?(&:idle?)
67
+ end
68
+
69
+ attr_reader :app
70
+ attr_accessor :batch
71
+
72
+ def initialize(app)
73
+ @app = app
74
+ end
75
+
76
+ def before_start; end
77
+ def after_start; end
78
+ def before_stop; end
79
+ def after_stop; end
80
+
81
+ def start
82
+ before_start
83
+ app.connections.times { handlers.push(start_handler) }
84
+ after_start
85
+ Rapns.logger.info("[#{app.name}] Started, #{handlers_str}.")
86
+ end
87
+
88
+ def stop
89
+ before_stop
90
+ handlers.stop
91
+ after_stop
92
+ end
93
+
94
+ def enqueue(batch)
95
+ self.batch = batch
96
+ batch.notifications.each do |notification|
97
+ queue.push([notification, batch])
98
+ reflect(:notification_enqueued, notification)
99
+ end
100
+ end
101
+
102
+ def sync(app)
103
+ @app = app
104
+ diff = handlers.size - app.connections
105
+ return if diff == 0
106
+ if diff > 0
107
+ decrement_handlers(diff)
108
+ Rapns.logger.info("[#{app.name}] Stopped #{handlers_str(diff)}. #{handlers_str} running.")
109
+ else
110
+ increment_handlers(diff.abs)
111
+ Rapns.logger.info("[#{app.name}] Started #{handlers_str(diff)}. #{handlers_str} running.")
112
+ end
113
+ end
114
+
115
+ def decrement_handlers(num)
116
+ num.times { handlers.pop }
117
+ end
118
+
119
+ def increment_handlers(num)
120
+ num.times { handlers.push(start_handler) }
121
+ end
122
+
123
+ def debug
124
+ Rapns.logger.info <<-EOS
125
+
126
+ #{@app.name}:
127
+ handlers: #{num_handlers}
128
+ queued: #{queue_size}
129
+ batch size: #{batch_size}
130
+ batch processed: #{batch_processed}
131
+ idle: #{idle?}
132
+ EOS
133
+ end
134
+
135
+ def idle?
136
+ batch ? batch.complete? : true
137
+ end
138
+
139
+ def queue_size
140
+ queue.size
141
+ end
142
+
143
+ def batch_size
144
+ batch ? batch.num_notifications : 0
145
+ end
146
+
147
+ def batch_processed
148
+ batch ? batch.num_processed : 0
149
+ end
150
+
151
+ def num_handlers
152
+ handlers.size
153
+ end
154
+
155
+ protected
156
+
157
+ def start_handler
158
+ handler = new_delivery_handler
159
+ handler.queue = queue
160
+ handler.start
161
+ handler
162
+ end
163
+
164
+ def queue
165
+ @queue ||= Queue.new
166
+ end
167
+
168
+ def handlers
169
+ @handlers ||= Rapns::Daemon::DeliveryHandlerCollection.new
170
+ end
171
+
172
+ def handlers_str(count = app.connections)
173
+ count = count.abs
174
+ str = count == 1 ? 'handler' : 'handlers'
175
+ "#{count} #{str}"
176
+ end
177
+ end
178
+ end
179
+ end
@@ -0,0 +1,112 @@
1
+ module Rapns
2
+ module Daemon
3
+ class Batch
4
+ include Reflectable
5
+
6
+ attr_reader :num_processed, :notifications,
7
+ :delivered, :failed, :retryable
8
+
9
+ def initialize(notifications)
10
+ @notifications = notifications
11
+ @num_processed = 0
12
+ @delivered = []
13
+ @failed = {}
14
+ @retryable = {}
15
+ @mutex = Mutex.new
16
+ end
17
+
18
+ def num_notifications
19
+ @notifications.size
20
+ end
21
+
22
+ def mark_retryable(notification, deliver_after)
23
+ if Rapns.config.batch_storage_updates
24
+ retryable[deliver_after] ||= []
25
+ retryable[deliver_after] << notification
26
+ else
27
+ Rapns::Daemon.store.mark_retryable(notification, deliver_after)
28
+ reflect(:notification_will_retry, notification)
29
+ end
30
+ end
31
+
32
+ def mark_delivered(notification)
33
+ if Rapns.config.batch_storage_updates
34
+ delivered << notification
35
+ else
36
+ Rapns::Daemon.store.mark_delivered(notification)
37
+ reflect(:notification_delivered, notification)
38
+ end
39
+ end
40
+
41
+ def mark_failed(notification, code, description)
42
+ if Rapns.config.batch_storage_updates
43
+ key = [code, description]
44
+ failed[key] ||= []
45
+ failed[key] << notification
46
+ else
47
+ Rapns::Daemon.store.mark_failed(notification, code, description)
48
+ reflect(:notification_failed, notification)
49
+ end
50
+ end
51
+
52
+ def notification_processed
53
+ @mutex.synchronize do
54
+ @num_processed += 1
55
+ complete if @num_processed >= @notifications.size
56
+ end
57
+ end
58
+
59
+ def complete?
60
+ @complete == true
61
+ end
62
+
63
+ def describe
64
+ notifications.map(&:id).join(', ')
65
+ end
66
+
67
+ private
68
+
69
+ def complete
70
+ [:complete_delivered, :complete_failed, :complete_retried].each do |method|
71
+ begin
72
+ send(method)
73
+ rescue StandardError => e
74
+ Rapns.logger.error(e)
75
+ reflect(:error, e)
76
+ end
77
+ end
78
+
79
+ notifications.clear
80
+ @complete = true
81
+ end
82
+
83
+ def complete_delivered
84
+ Rapns::Daemon.store.mark_batch_delivered(delivered)
85
+ delivered.each do |notification|
86
+ reflect(:notification_delivered, notification)
87
+ end
88
+ delivered.clear
89
+ end
90
+
91
+ def complete_failed
92
+ failed.each do |(code, description), notifications|
93
+ Rapns::Daemon.store.mark_batch_failed(notifications, code, description)
94
+ notifications.each do |notification|
95
+ reflect(:notification_failed, notification)
96
+ end
97
+ end
98
+ failed.clear
99
+ end
100
+
101
+ def complete_retried
102
+ retryable.each do |deliver_after, notifications|
103
+ Rapns::Daemon.store.mark_batch_retryable(notifications, deliver_after)
104
+ notifications.each do |notification|
105
+ reflect(:notification_will_retry, notification)
106
+ end
107
+ end
108
+ retryable.clear
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,23 @@
1
+ module Rapns
2
+ module Daemon
3
+ class Delivery
4
+ include Reflectable
5
+
6
+ def mark_retryable(notification, deliver_after)
7
+ @batch.mark_retryable(notification, deliver_after)
8
+ end
9
+
10
+ def mark_retryable_exponential(notification)
11
+ mark_retryable(notification, Time.now + 2 ** (notification.retries + 1))
12
+ end
13
+
14
+ def mark_delivered
15
+ @batch.mark_delivered(@notification)
16
+ end
17
+
18
+ def mark_failed(code, description)
19
+ @batch.mark_failed(@notification, code, description)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,19 @@
1
+ module Rapns
2
+ class DeliveryError < StandardError
3
+ attr_reader :code, :description
4
+
5
+ def initialize(code, notification_id, description)
6
+ @code = code
7
+ @notification_id = notification_id
8
+ @description = description
9
+ end
10
+
11
+ def to_s
12
+ message
13
+ end
14
+
15
+ def message
16
+ "Unable to deliver notification #{@notification_id}, received error #{@code} (#{@description})"
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,52 @@
1
+ module Rapns
2
+ module Daemon
3
+ class DeliveryHandler
4
+ include Reflectable
5
+
6
+ WAKEUP = :wakeup
7
+
8
+ attr_accessor :queue
9
+
10
+ def start
11
+ @thread = Thread.new do
12
+ loop do
13
+ handle_next_notification
14
+ break if @stop
15
+ end
16
+ end
17
+ end
18
+
19
+ def stop
20
+ @stop = true
21
+ end
22
+
23
+ def wakeup
24
+ queue.push(WAKEUP) if @thread
25
+ end
26
+
27
+ def wait
28
+ @thread.join if @thread
29
+ stopped
30
+ end
31
+
32
+ protected
33
+
34
+ def stopped
35
+ end
36
+
37
+ def handle_next_notification
38
+ notification, batch = queue.pop
39
+ return if notification == WAKEUP
40
+
41
+ begin
42
+ deliver(notification, batch)
43
+ rescue StandardError => e
44
+ Rapns.logger.error(e)
45
+ reflect(:error, e)
46
+ ensure
47
+ batch.notification_processed
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,33 @@
1
+ module Rapns
2
+ module Daemon
3
+ class DeliveryHandlerCollection
4
+ attr_reader :handlers
5
+
6
+ def initialize
7
+ @handlers = []
8
+ end
9
+
10
+ def push(handler)
11
+ @handlers << handler
12
+ end
13
+
14
+ def pop
15
+ handler = @handlers.pop
16
+ handler.stop
17
+ handler.wakeup
18
+ @handlers.map(&:wakeup)
19
+ handler.wait
20
+ end
21
+
22
+ def size
23
+ @handlers.size
24
+ end
25
+
26
+ def stop
27
+ @handlers.map(&:stop)
28
+ @handlers.map(&:wakeup)
29
+ @handlers.map(&:wait)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,65 @@
1
+ module Rapns
2
+ module Daemon
3
+ class Feeder
4
+ extend Reflectable
5
+
6
+ def self.start
7
+ @stop = false
8
+
9
+ if Rapns.config.embedded
10
+ Thread.new { feed_forever }
11
+ elsif Rapns.config.push
12
+ enqueue_notifications
13
+ else
14
+ feed_forever
15
+ end
16
+ end
17
+
18
+ def self.stop
19
+ @stop = true
20
+ interrupt_sleep
21
+ end
22
+
23
+ def self.interrupt_sleep
24
+ interruptible_sleeper.interrupt_sleep
25
+ end
26
+
27
+ protected
28
+
29
+ def self.feed_forever
30
+ loop do
31
+ enqueue_notifications
32
+ interruptible_sleeper.sleep(Rapns.config.push_poll)
33
+ break if stop?
34
+ end
35
+ end
36
+
37
+ def self.stop?
38
+ @stop
39
+ end
40
+
41
+ def self.enqueue_notifications
42
+ begin
43
+ idle = Rapns::Daemon::AppRunner.idle.map(&:app)
44
+ return if idle.empty?
45
+ notifications = Rapns::Daemon.store.deliverable_notifications(idle)
46
+ Rapns::Daemon::AppRunner.enqueue(notifications)
47
+ rescue StandardError => e
48
+ Rapns.logger.error(e)
49
+ reflect(:error, e)
50
+ end
51
+ end
52
+
53
+ def self.interruptible_sleeper
54
+ unless @interruptible_sleeper
55
+ @interruptible_sleeper = InterruptibleSleep.new
56
+ if Rapns.config.udp_wake_host && Rapns.config.udp_wake_port
57
+ @interruptible_sleeper.enable_wake_on_udp Rapns.config.udp_wake_host, Rapns.config.udp_wake_port
58
+ end
59
+ end
60
+ @interruptible_sleeper
61
+ end
62
+
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,13 @@
1
+ module Rapns
2
+ module Daemon
3
+ module Gcm
4
+ class AppRunner < Rapns::Daemon::AppRunner
5
+ protected
6
+
7
+ def new_delivery_handler
8
+ DeliveryHandler.new(app)
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end