rpush_extended 3.2.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (248) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +365 -0
  3. data/LICENSE +7 -0
  4. data/README.md +393 -0
  5. data/bin/rpush +4 -0
  6. data/lib/generators/rpush_config_generator.rb +7 -0
  7. data/lib/generators/rpush_migration_generator.rb +66 -0
  8. data/lib/generators/templates/add_adm.rb +23 -0
  9. data/lib/generators/templates/add_alert_is_json_to_rapns_notifications.rb +9 -0
  10. data/lib/generators/templates/add_app_to_rapns.rb +11 -0
  11. data/lib/generators/templates/add_fail_after_to_rpush_notifications.rb +9 -0
  12. data/lib/generators/templates/add_gcm.rb +117 -0
  13. data/lib/generators/templates/add_rpush.rb +402 -0
  14. data/lib/generators/templates/add_wpns.rb +16 -0
  15. data/lib/generators/templates/create_rapns_apps.rb +16 -0
  16. data/lib/generators/templates/create_rapns_feedback.rb +25 -0
  17. data/lib/generators/templates/create_rapns_notifications.rb +36 -0
  18. data/lib/generators/templates/rename_rapns_to_rpush.rb +87 -0
  19. data/lib/generators/templates/rpush.rb +135 -0
  20. data/lib/generators/templates/rpush_2_0_0_updates.rb +79 -0
  21. data/lib/generators/templates/rpush_2_1_0_updates.rb +11 -0
  22. data/lib/generators/templates/rpush_2_6_0_updates.rb +10 -0
  23. data/lib/generators/templates/rpush_2_7_0_updates.rb +12 -0
  24. data/lib/generators/templates/rpush_3_0_0_updates.rb +11 -0
  25. data/lib/generators/templates/rpush_3_0_1_updates.rb +13 -0
  26. data/lib/generators/templates/rpush_3_1_0_add_pushy.rb +9 -0
  27. data/lib/generators/templates/rpush_3_1_1_updates.rb +15 -0
  28. data/lib/generators/templates/rpush_3_2_0_add_apns_p8.rb +15 -0
  29. data/lib/generators/templates/rpush_3_2_4_updates.rb +9 -0
  30. data/lib/generators/templates/rpush_3_3_0_updates.rb +9 -0
  31. data/lib/generators/templates/rpush_3_3_1_updates.rb +11 -0
  32. data/lib/rpush/apns_feedback.rb +17 -0
  33. data/lib/rpush/cli.rb +213 -0
  34. data/lib/rpush/client/active_model/adm/app.rb +23 -0
  35. data/lib/rpush/client/active_model/adm/data_validator.rb +14 -0
  36. data/lib/rpush/client/active_model/adm/notification.rb +28 -0
  37. data/lib/rpush/client/active_model/apns/app.rb +37 -0
  38. data/lib/rpush/client/active_model/apns/binary_notification_validator.rb +16 -0
  39. data/lib/rpush/client/active_model/apns/device_token_format_validator.rb +14 -0
  40. data/lib/rpush/client/active_model/apns/notification.rb +104 -0
  41. data/lib/rpush/client/active_model/apns2/app.rb +15 -0
  42. data/lib/rpush/client/active_model/apns2/notification.rb +9 -0
  43. data/lib/rpush/client/active_model/apnsp8/app.rb +23 -0
  44. data/lib/rpush/client/active_model/apnsp8/notification.rb +9 -0
  45. data/lib/rpush/client/active_model/gcm/app.rb +19 -0
  46. data/lib/rpush/client/active_model/gcm/expiry_collapse_key_mutual_inclusion_validator.rb +14 -0
  47. data/lib/rpush/client/active_model/gcm/notification.rb +59 -0
  48. data/lib/rpush/client/active_model/notification.rb +22 -0
  49. data/lib/rpush/client/active_model/payload_data_size_validator.rb +13 -0
  50. data/lib/rpush/client/active_model/pushy/app.rb +20 -0
  51. data/lib/rpush/client/active_model/pushy/notification.rb +31 -0
  52. data/lib/rpush/client/active_model/pushy/time_to_live_validator.rb +14 -0
  53. data/lib/rpush/client/active_model/registration_ids_count_validator.rb +13 -0
  54. data/lib/rpush/client/active_model/wns/app.rb +23 -0
  55. data/lib/rpush/client/active_model/wns/notification.rb +32 -0
  56. data/lib/rpush/client/active_model/wpns/app.rb +13 -0
  57. data/lib/rpush/client/active_model/wpns/notification.rb +28 -0
  58. data/lib/rpush/client/active_model.rb +34 -0
  59. data/lib/rpush/client/active_record/adm/app.rb +11 -0
  60. data/lib/rpush/client/active_record/adm/notification.rb +11 -0
  61. data/lib/rpush/client/active_record/apns/app.rb +11 -0
  62. data/lib/rpush/client/active_record/apns/feedback.rb +18 -0
  63. data/lib/rpush/client/active_record/apns/notification.rb +40 -0
  64. data/lib/rpush/client/active_record/apns2/app.rb +11 -0
  65. data/lib/rpush/client/active_record/apns2/notification.rb +10 -0
  66. data/lib/rpush/client/active_record/apnsp8/app.rb +11 -0
  67. data/lib/rpush/client/active_record/apnsp8/notification.rb +10 -0
  68. data/lib/rpush/client/active_record/app.rb +13 -0
  69. data/lib/rpush/client/active_record/gcm/app.rb +11 -0
  70. data/lib/rpush/client/active_record/gcm/notification.rb +11 -0
  71. data/lib/rpush/client/active_record/notification.rb +42 -0
  72. data/lib/rpush/client/active_record/pushy/app.rb +11 -0
  73. data/lib/rpush/client/active_record/pushy/notification.rb +11 -0
  74. data/lib/rpush/client/active_record/wns/app.rb +11 -0
  75. data/lib/rpush/client/active_record/wns/badge_notification.rb +15 -0
  76. data/lib/rpush/client/active_record/wns/notification.rb +11 -0
  77. data/lib/rpush/client/active_record/wns/raw_notification.rb +13 -0
  78. data/lib/rpush/client/active_record/wpns/app.rb +11 -0
  79. data/lib/rpush/client/active_record/wpns/notification.rb +11 -0
  80. data/lib/rpush/client/active_record.rb +33 -0
  81. data/lib/rpush/client/redis/adm/app.rb +14 -0
  82. data/lib/rpush/client/redis/adm/notification.rb +11 -0
  83. data/lib/rpush/client/redis/apns/app.rb +11 -0
  84. data/lib/rpush/client/redis/apns/feedback.rb +20 -0
  85. data/lib/rpush/client/redis/apns/notification.rb +11 -0
  86. data/lib/rpush/client/redis/apns2/app.rb +11 -0
  87. data/lib/rpush/client/redis/apns2/notification.rb +11 -0
  88. data/lib/rpush/client/redis/apnsp8/app.rb +11 -0
  89. data/lib/rpush/client/redis/apnsp8/notification.rb +11 -0
  90. data/lib/rpush/client/redis/app.rb +29 -0
  91. data/lib/rpush/client/redis/gcm/app.rb +11 -0
  92. data/lib/rpush/client/redis/gcm/notification.rb +11 -0
  93. data/lib/rpush/client/redis/notification.rb +74 -0
  94. data/lib/rpush/client/redis/pushy/app.rb +16 -0
  95. data/lib/rpush/client/redis/pushy/notification.rb +18 -0
  96. data/lib/rpush/client/redis/wns/app.rb +14 -0
  97. data/lib/rpush/client/redis/wns/badge_notification.rb +15 -0
  98. data/lib/rpush/client/redis/wns/notification.rb +11 -0
  99. data/lib/rpush/client/redis/wns/raw_notification.rb +11 -0
  100. data/lib/rpush/client/redis/wpns/app.rb +11 -0
  101. data/lib/rpush/client/redis/wpns/notification.rb +11 -0
  102. data/lib/rpush/client/redis.rb +56 -0
  103. data/lib/rpush/configuration.rb +115 -0
  104. data/lib/rpush/daemon/adm/delivery.rb +226 -0
  105. data/lib/rpush/daemon/adm.rb +9 -0
  106. data/lib/rpush/daemon/apns/delivery.rb +43 -0
  107. data/lib/rpush/daemon/apns/feedback_receiver.rb +90 -0
  108. data/lib/rpush/daemon/apns.rb +17 -0
  109. data/lib/rpush/daemon/apns2/delivery.rb +127 -0
  110. data/lib/rpush/daemon/apns2.rb +10 -0
  111. data/lib/rpush/daemon/apnsp8/delivery.rb +166 -0
  112. data/lib/rpush/daemon/apnsp8/token.rb +43 -0
  113. data/lib/rpush/daemon/apnsp8.rb +10 -0
  114. data/lib/rpush/daemon/app_runner.rb +190 -0
  115. data/lib/rpush/daemon/batch.rb +138 -0
  116. data/lib/rpush/daemon/constants.rb +59 -0
  117. data/lib/rpush/daemon/delivery.rb +46 -0
  118. data/lib/rpush/daemon/delivery_error.rb +27 -0
  119. data/lib/rpush/daemon/dispatcher/apns_http2.rb +51 -0
  120. data/lib/rpush/daemon/dispatcher/apns_tcp.rb +152 -0
  121. data/lib/rpush/daemon/dispatcher/apnsp8_http2.rb +33 -0
  122. data/lib/rpush/daemon/dispatcher/http.rb +21 -0
  123. data/lib/rpush/daemon/dispatcher/tcp.rb +22 -0
  124. data/lib/rpush/daemon/dispatcher_loop.rb +73 -0
  125. data/lib/rpush/daemon/errors.rb +18 -0
  126. data/lib/rpush/daemon/feeder.rb +69 -0
  127. data/lib/rpush/daemon/gcm/delivery.rb +241 -0
  128. data/lib/rpush/daemon/gcm.rb +9 -0
  129. data/lib/rpush/daemon/interruptible_sleep.rb +24 -0
  130. data/lib/rpush/daemon/loggable.rb +33 -0
  131. data/lib/rpush/daemon/proc_title.rb +17 -0
  132. data/lib/rpush/daemon/pushy/delivery.rb +90 -0
  133. data/lib/rpush/daemon/pushy.rb +9 -0
  134. data/lib/rpush/daemon/queue_payload.rb +12 -0
  135. data/lib/rpush/daemon/retry_header_parser.rb +23 -0
  136. data/lib/rpush/daemon/retryable_error.rb +22 -0
  137. data/lib/rpush/daemon/ring_buffer.rb +16 -0
  138. data/lib/rpush/daemon/rpc/client.rb +27 -0
  139. data/lib/rpush/daemon/rpc/server.rb +82 -0
  140. data/lib/rpush/daemon/rpc.rb +9 -0
  141. data/lib/rpush/daemon/service_config_methods.rb +51 -0
  142. data/lib/rpush/daemon/signal_handler.rb +75 -0
  143. data/lib/rpush/daemon/store/active_record/reconnectable.rb +80 -0
  144. data/lib/rpush/daemon/store/active_record.rb +214 -0
  145. data/lib/rpush/daemon/store/interface.rb +20 -0
  146. data/lib/rpush/daemon/store/redis.rb +166 -0
  147. data/lib/rpush/daemon/string_helpers.rb +15 -0
  148. data/lib/rpush/daemon/synchronizer.rb +62 -0
  149. data/lib/rpush/daemon/tcp_connection.rb +190 -0
  150. data/lib/rpush/daemon/wns/badge_request.rb +32 -0
  151. data/lib/rpush/daemon/wns/delivery.rb +178 -0
  152. data/lib/rpush/daemon/wns/post_request.rb +33 -0
  153. data/lib/rpush/daemon/wns/raw_request.rb +22 -0
  154. data/lib/rpush/daemon/wns/toast_request.rb +54 -0
  155. data/lib/rpush/daemon/wns.rb +9 -0
  156. data/lib/rpush/daemon/wpns/delivery.rb +132 -0
  157. data/lib/rpush/daemon/wpns.rb +9 -0
  158. data/lib/rpush/daemon.rb +179 -0
  159. data/lib/rpush/deprecatable.rb +24 -0
  160. data/lib/rpush/deprecation.rb +26 -0
  161. data/lib/rpush/embed.rb +41 -0
  162. data/lib/rpush/logger.rb +92 -0
  163. data/lib/rpush/multi_json_helper.rb +16 -0
  164. data/lib/rpush/plugin.rb +44 -0
  165. data/lib/rpush/push.rb +11 -0
  166. data/lib/rpush/reflectable.rb +13 -0
  167. data/lib/rpush/reflection_collection.rb +44 -0
  168. data/lib/rpush/reflection_public_methods.rb +11 -0
  169. data/lib/rpush/version.rb +14 -0
  170. data/lib/rpush.rb +43 -0
  171. data/lib/tasks/quality.rake +35 -0
  172. data/lib/tasks/test.rake +69 -0
  173. data/spec/.rubocop.yml +4 -0
  174. data/spec/functional/adm_spec.rb +50 -0
  175. data/spec/functional/apns2_spec.rb +232 -0
  176. data/spec/functional/apns_spec.rb +162 -0
  177. data/spec/functional/cli_spec.rb +36 -0
  178. data/spec/functional/embed_spec.rb +49 -0
  179. data/spec/functional/gcm_spec.rb +46 -0
  180. data/spec/functional/new_app_spec.rb +44 -0
  181. data/spec/functional/pushy_spec.rb +22 -0
  182. data/spec/functional/retry_spec.rb +42 -0
  183. data/spec/functional/synchronization_spec.rb +97 -0
  184. data/spec/functional/wpns_spec.rb +71 -0
  185. data/spec/functional_spec_helper.rb +32 -0
  186. data/spec/spec_helper.rb +69 -0
  187. data/spec/support/active_record_setup.rb +73 -0
  188. data/spec/support/cert_with_password.pem +90 -0
  189. data/spec/support/cert_without_password.pem +59 -0
  190. data/spec/support/config/database.yml +44 -0
  191. data/spec/support/simplecov_helper.rb +24 -0
  192. data/spec/support/simplecov_quality_formatter.rb +12 -0
  193. data/spec/tmp/.gitkeep +0 -0
  194. data/spec/unit/apns_feedback_spec.rb +28 -0
  195. data/spec/unit/client/active_record/adm/app_spec.rb +58 -0
  196. data/spec/unit/client/active_record/adm/notification_spec.rb +43 -0
  197. data/spec/unit/client/active_record/apns/app_spec.rb +29 -0
  198. data/spec/unit/client/active_record/apns/feedback_spec.rb +9 -0
  199. data/spec/unit/client/active_record/apns/notification_spec.rb +324 -0
  200. data/spec/unit/client/active_record/app_spec.rb +30 -0
  201. data/spec/unit/client/active_record/gcm/app_spec.rb +4 -0
  202. data/spec/unit/client/active_record/gcm/notification_spec.rb +67 -0
  203. data/spec/unit/client/active_record/notification_spec.rb +21 -0
  204. data/spec/unit/client/active_record/pushy/app_spec.rb +17 -0
  205. data/spec/unit/client/active_record/pushy/notification_spec.rb +65 -0
  206. data/spec/unit/client/active_record/wns/badge_notification_spec.rb +15 -0
  207. data/spec/unit/client/active_record/wns/raw_notification_spec.rb +26 -0
  208. data/spec/unit/client/active_record/wpns/app_spec.rb +4 -0
  209. data/spec/unit/client/active_record/wpns/notification_spec.rb +21 -0
  210. data/spec/unit/configuration_spec.rb +46 -0
  211. data/spec/unit/daemon/adm/delivery_spec.rb +253 -0
  212. data/spec/unit/daemon/apns/certificate_expired_error_spec.rb +11 -0
  213. data/spec/unit/daemon/apns/delivery_spec.rb +108 -0
  214. data/spec/unit/daemon/apns/feedback_receiver_spec.rb +119 -0
  215. data/spec/unit/daemon/app_runner_spec.rb +188 -0
  216. data/spec/unit/daemon/batch_spec.rb +169 -0
  217. data/spec/unit/daemon/delivery_error_spec.rb +13 -0
  218. data/spec/unit/daemon/delivery_spec.rb +51 -0
  219. data/spec/unit/daemon/dispatcher/http_spec.rb +34 -0
  220. data/spec/unit/daemon/dispatcher/tcp_spec.rb +32 -0
  221. data/spec/unit/daemon/dispatcher_loop_spec.rb +53 -0
  222. data/spec/unit/daemon/feeder_spec.rb +96 -0
  223. data/spec/unit/daemon/gcm/delivery_spec.rb +387 -0
  224. data/spec/unit/daemon/proc_title_spec.rb +11 -0
  225. data/spec/unit/daemon/pushy/delivery_spec.rb +159 -0
  226. data/spec/unit/daemon/retryable_error_spec.rb +14 -0
  227. data/spec/unit/daemon/service_config_methods_spec.rb +36 -0
  228. data/spec/unit/daemon/signal_handler_spec.rb +99 -0
  229. data/spec/unit/daemon/store/active_record/reconnectable_spec.rb +165 -0
  230. data/spec/unit/daemon/store/active_record_spec.rb +357 -0
  231. data/spec/unit/daemon/store/redis_spec.rb +365 -0
  232. data/spec/unit/daemon/tcp_connection_spec.rb +292 -0
  233. data/spec/unit/daemon/wns/delivery_spec.rb +176 -0
  234. data/spec/unit/daemon/wns/post_request_spec.rb +117 -0
  235. data/spec/unit/daemon/wpns/delivery_spec.rb +167 -0
  236. data/spec/unit/daemon_spec.rb +138 -0
  237. data/spec/unit/deprecatable_spec.rb +32 -0
  238. data/spec/unit/deprecation_spec.rb +15 -0
  239. data/spec/unit/embed_spec.rb +47 -0
  240. data/spec/unit/logger_spec.rb +127 -0
  241. data/spec/unit/notification_shared.rb +53 -0
  242. data/spec/unit/plugin_spec.rb +36 -0
  243. data/spec/unit/push_spec.rb +34 -0
  244. data/spec/unit/reflectable_spec.rb +27 -0
  245. data/spec/unit/reflection_collection_spec.rb +26 -0
  246. data/spec/unit/rpush_spec.rb +8 -0
  247. data/spec/unit_spec_helper.rb +26 -0
  248. metadata +709 -0
@@ -0,0 +1,24 @@
1
+ module Rpush
2
+ module Daemon
3
+ class InterruptibleSleep
4
+ def sleep(duration)
5
+ @thread = Thread.new { Kernel.sleep duration }
6
+ Thread.pass
7
+
8
+ begin
9
+ @thread.join
10
+ rescue StandardError # rubocop:disable Lint/HandleExceptions
11
+ ensure
12
+ @thread = nil
13
+ end
14
+ end
15
+
16
+ def stop
17
+ @thread.kill if @thread
18
+ rescue StandardError # rubocop:disable Lint/HandleExceptions
19
+ ensure
20
+ @thread = nil
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,33 @@
1
+ module Rpush
2
+ module Daemon
3
+ module Loggable
4
+ def log_debug(msg)
5
+ Rpush.logger.debug(app_prefix(msg))
6
+ end
7
+
8
+ def log_info(msg)
9
+ Rpush.logger.info(app_prefix(msg))
10
+ end
11
+
12
+ def log_warn(msg)
13
+ Rpush.logger.warn(app_prefix(msg))
14
+ end
15
+
16
+ def log_error(e)
17
+ if e.is_a?(Exception)
18
+ Rpush.logger.error(e)
19
+ else
20
+ Rpush.logger.error(app_prefix(e))
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def app_prefix(msg)
27
+ app = instance_variable_get('@app')
28
+ msg = "[#{app.name}] #{msg}" if app
29
+ msg
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,17 @@
1
+ module Rpush
2
+ module Daemon
3
+ class ProcTitle
4
+ def self.update
5
+ return if Rpush.config.embedded || Rpush.config.push
6
+ Process.respond_to?(:setproctitle) ? Process.setproctitle(proc_title) : $0 = proc_title
7
+ end
8
+
9
+ def self.proc_title
10
+ total_dispatchers = AppRunner.total_dispatchers
11
+ dispatchers_str = total_dispatchers == 1 ? 'dispatcher' : 'dispatchers'
12
+ total_queued = AppRunner.total_queued
13
+ format("rpush | %d queued | %d %s", total_queued, total_dispatchers, dispatchers_str)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,90 @@
1
+ module Rpush
2
+ module Daemon
3
+ module Pushy
4
+ class Delivery < Rpush::Daemon::Delivery
5
+ include MultiJsonHelper
6
+
7
+ attr_reader :http, :notification, :batch, :pushy_uri
8
+
9
+ def initialize(app, http, notification, batch)
10
+ @http = http
11
+ @notification = notification
12
+ @batch = batch
13
+ @pushy_uri = URI.parse("https://api.pushy.me/push?api_key=#{app.api_key}")
14
+ end
15
+
16
+ def perform
17
+ response = send_request
18
+ process_response(response)
19
+ rescue SocketError => error
20
+ mark_retryable(notification, Time.now + 10.seconds, error)
21
+ raise
22
+ rescue StandardError => error
23
+ mark_failed(error)
24
+ raise
25
+ ensure
26
+ batch.notification_processed
27
+ end
28
+
29
+ private
30
+
31
+ def send_request
32
+ post = Net::HTTP::Post.new(pushy_uri)
33
+ post.content_type = 'application/json'
34
+ post.body = notification.to_json
35
+ http.request(pushy_uri, post)
36
+ end
37
+
38
+ def process_response(response)
39
+ case response.code.to_i
40
+ when 200
41
+ process_delivery(response)
42
+ when 429, 500, 502, 503, 504
43
+ retry_delivery(response)
44
+ else
45
+ fail_delivery(response)
46
+ end
47
+ end
48
+
49
+ def process_delivery(response)
50
+ mark_delivered
51
+ body = multi_json_load(response.body)
52
+ external_device_id = body['id']
53
+ notification.external_device_id = external_device_id
54
+ Rpush::Daemon.store.update_notification(notification)
55
+ log_info("#{notification.id} received an external id=#{external_device_id}")
56
+ end
57
+
58
+ def retry_delivery(response)
59
+ time = deliver_after_header(response)
60
+ if time
61
+ mark_retryable(notification, time)
62
+ else
63
+ mark_retryable_exponential(notification)
64
+ end
65
+ log_warn("Pushy responded with a #{response.code} error. #{retry_message}")
66
+ end
67
+
68
+ def deliver_after_header(response)
69
+ Rpush::Daemon::RetryHeaderParser.parse(response.header['retry-after'])
70
+ end
71
+
72
+ def retry_message
73
+ deliver_after = notification.deliver_after.strftime('%Y-%m-%d %H:%M:%S')
74
+ "Notification #{notification.id} will be retried after #{deliver_after} (retry #{notification.retries})."
75
+ end
76
+
77
+ def fail_delivery(response)
78
+ fail_message = fail_message(response)
79
+ log_error("#{notification.id} failed: #{fail_message}")
80
+ fail Rpush::DeliveryError.new(response.code.to_i, notification.id, fail_message)
81
+ end
82
+
83
+ def fail_message(response)
84
+ body = multi_json_load(response.body)
85
+ body['error'] || Rpush::Daemon::HTTP_STATUS_CODES[response.code.to_i]
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,9 @@
1
+ module Rpush
2
+ module Daemon
3
+ module Pushy
4
+ extend ServiceConfigMethods
5
+
6
+ dispatcher :http
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,12 @@
1
+ module Rpush
2
+ module Daemon
3
+ class QueuePayload
4
+ attr_reader :batch, :notification
5
+
6
+ def initialize(batch, notification = nil)
7
+ @batch = batch
8
+ @notification = notification
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,23 @@
1
+ module Rpush
2
+ module Daemon
3
+ class RetryHeaderParser
4
+ def self.parse(header)
5
+ new(header).parse
6
+ end
7
+
8
+ def initialize(header)
9
+ @header = header
10
+ end
11
+
12
+ def parse
13
+ return unless @header
14
+
15
+ if @header.to_s =~ /^[0-9]+$/
16
+ Time.now + @header.to_i
17
+ else
18
+ Time.httpdate(@header)
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,22 @@
1
+ module Rpush
2
+ class RetryableError < StandardError
3
+ attr_reader :code, :description, :response
4
+
5
+ def initialize(code, notification_id, description, response)
6
+ @code = code
7
+ @notification_id = notification_id
8
+ @description = description
9
+ @response = response
10
+ end
11
+
12
+ def to_s
13
+ message
14
+ end
15
+
16
+ def message
17
+ "Retryable error for #{@notification_id}, received error #{@code} (#{@description}) - retry after #{@response.header['retry-after']}"
18
+ end
19
+ end
20
+
21
+ class RateLimitError < RetryableError; end
22
+ end
@@ -0,0 +1,16 @@
1
+ module Rpush
2
+ module Daemon
3
+ class RingBuffer < Array
4
+ def initialize(max_size)
5
+ @max_size = max_size
6
+ end
7
+
8
+ def <<(obj)
9
+ shift if size >= @max_size
10
+ super
11
+ end
12
+
13
+ alias_method :push, :<<
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,27 @@
1
+ module Rpush
2
+ module Daemon
3
+ module Rpc
4
+ class Client
5
+ def initialize(pid)
6
+ @socket = UNIXSocket.open(Rpc.socket_path(pid))
7
+ end
8
+
9
+ def status
10
+ call(:status)
11
+ end
12
+
13
+ def close
14
+ @socket.close
15
+ rescue StandardError # rubocop:disable Lint/HandleExceptions
16
+ end
17
+
18
+ private
19
+
20
+ def call(cmd, args = {})
21
+ @socket.puts(JSON.dump([cmd, args]))
22
+ JSON.parse(@socket.gets)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,82 @@
1
+ require 'socket'
2
+ require 'singleton'
3
+
4
+ module Rpush
5
+ module Daemon
6
+ module Rpc
7
+ class Server
8
+ include Singleton
9
+ include Loggable
10
+ include Reflectable
11
+
12
+ def self.start
13
+ instance.start
14
+ end
15
+
16
+ def self.stop
17
+ instance.stop
18
+ end
19
+
20
+ def start
21
+ @stop = false
22
+
23
+ @thread = Thread.new(UNIXServer.open(Rpc.socket_path)) do |server|
24
+ begin
25
+ loop do
26
+ socket = server.accept
27
+ break if @stop
28
+ read_loop(socket)
29
+ end
30
+
31
+ server.close
32
+ rescue StandardError => e
33
+ log_error(e)
34
+ ensure
35
+ File.unlink(Rpc.socket_path) if File.exist?(Rpc.socket_path)
36
+ end
37
+ end
38
+ end
39
+
40
+ def stop
41
+ @stop = true
42
+ UNIXSocket.new(Rpc.socket_path)
43
+ @thread.join if @thread
44
+ rescue StandardError => e
45
+ log_error(e)
46
+ end
47
+
48
+ private
49
+
50
+ def read_loop(socket)
51
+ loop do
52
+ line = socket.gets
53
+ break unless line
54
+
55
+ begin
56
+ cmd, args = JSON.load(line)
57
+ log_debug("[rpc:server] #{cmd.to_sym.inspect}, args: #{args.inspect}")
58
+ response = process(cmd, args)
59
+ socket.puts(JSON.dump(response))
60
+ rescue StandardError => e
61
+ log_error(e)
62
+ reflect(:error, e)
63
+ end
64
+ end
65
+
66
+ socket.close
67
+ end
68
+
69
+ def process(cmd, args) # rubocop:disable Lint/UnusedMethodArgument
70
+ case cmd
71
+ when 'status'
72
+ status
73
+ end
74
+ end
75
+
76
+ def status
77
+ Rpush::Daemon::AppRunner.status
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,9 @@
1
+ module Rpush
2
+ module Daemon
3
+ module Rpc
4
+ def self.socket_path(pid = Process.pid)
5
+ "/tmp/rpush.#{pid}.sock"
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,51 @@
1
+ module Rpush
2
+ module Daemon
3
+ module ServiceConfigMethods
4
+ DISPATCHERS = {
5
+ http: Rpush::Daemon::Dispatcher::Http,
6
+ tcp: Rpush::Daemon::Dispatcher::Tcp,
7
+ apns_tcp: Rpush::Daemon::Dispatcher::ApnsTcp,
8
+ apns_http2: Rpush::Daemon::Dispatcher::ApnsHttp2,
9
+ apnsp8_http2: Rpush::Daemon::Dispatcher::Apnsp8Http2
10
+ }
11
+
12
+ def batch_deliveries(value = nil)
13
+ return batch_deliveries? if value.nil?
14
+ @batch_deliveries = value
15
+ end
16
+
17
+ def batch_deliveries?
18
+ @batch_deliveries == true
19
+ end
20
+
21
+ def dispatcher(name = nil, options = {})
22
+ @dispatcher_name = name
23
+ @dispatcher_options = options
24
+ end
25
+
26
+ def dispatcher_class
27
+ DISPATCHERS[@dispatcher_name] || (fail NotImplementedError)
28
+ end
29
+
30
+ def delivery_class
31
+ const_get('Delivery')
32
+ end
33
+
34
+ def new_dispatcher(app)
35
+ dispatcher_class.new(app, delivery_class, @dispatcher_options)
36
+ end
37
+
38
+ def loops(classes, options = {})
39
+ classes = Array[*classes]
40
+ @loops = classes.map { |cls| [cls, options] }
41
+ end
42
+
43
+ def loop_instances(app)
44
+ (@loops || []).map do |cls, options|
45
+ next unless options.key?(:if) ? options[:if].call : true
46
+ cls.new(app)
47
+ end.compact
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,75 @@
1
+ module Rpush
2
+ module Daemon
3
+ class SignalHandler
4
+ extend Loggable
5
+
6
+ class << self
7
+ attr_reader :thread
8
+ end
9
+
10
+ def self.start
11
+ return unless trap_signals?
12
+
13
+ read_io, @write_io = IO.pipe
14
+ start_handler(read_io)
15
+ %w(INT TERM HUP USR2).each do |signal|
16
+ Signal.trap(signal) { @write_io.puts(signal) }
17
+ end
18
+ end
19
+
20
+ def self.stop
21
+ @write_io.puts('break') if @write_io
22
+ @thread.join if @thread
23
+ rescue StandardError => e
24
+ log_error(e)
25
+ reflect(:error, e)
26
+ ensure
27
+ @thread = nil
28
+ end
29
+
30
+ def self.start_handler(read_io)
31
+ @thread = Thread.new do
32
+ while readable_io = IO.select([read_io]) # rubocop:disable AssignmentInCondition
33
+ signal = readable_io.first[0].gets.strip
34
+
35
+ begin
36
+ case signal
37
+ when 'HUP'
38
+ handle_hup
39
+ when 'USR2'
40
+ handle_usr2
41
+ when 'INT', 'TERM'
42
+ Thread.new { Rpush::Daemon.shutdown }
43
+ break
44
+ when 'break'
45
+ break
46
+ else
47
+ Rpush.logger.error("Unhandled signal: #{signal}")
48
+ end
49
+ rescue StandardError => e
50
+ Rpush.logger.error("Error raised when handling signal '#{signal}'")
51
+ Rpush.logger.error(e)
52
+ end
53
+ end
54
+ end
55
+ end
56
+
57
+ def self.handle_hup
58
+ Rpush.logger.reopen
59
+ Rpush.logger.info('Received HUP signal.')
60
+ Rpush::Daemon.store.reopen_log
61
+ Synchronizer.sync
62
+ Feeder.wakeup
63
+ end
64
+
65
+ def self.handle_usr2
66
+ Rpush.logger.info('Received USR2 signal.')
67
+ AppRunner.debug
68
+ end
69
+
70
+ def self.trap_signals?
71
+ !Rpush.config.embedded
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,80 @@
1
+ class PGError < StandardError; end unless defined?(PGError)
2
+ module PG
3
+ class Error < StandardError; end unless defined?(::PG::Error)
4
+ end
5
+ class Mysql; class Error < StandardError; end; end unless defined?(Mysql)
6
+ module Mysql2; class Error < StandardError; end; end unless defined?(Mysql2)
7
+ module ActiveRecord
8
+ class JDBCError < StandardError; end unless defined?(::ActiveRecord::JDBCError)
9
+ end
10
+
11
+ # :nocov:
12
+ unless defined?(::SQLite3::Exception)
13
+ module SQLite3
14
+ class Exception < StandardError; end
15
+ end
16
+ end
17
+
18
+ module Rpush
19
+ module Daemon
20
+ module Store
21
+ class ActiveRecord
22
+ module Reconnectable
23
+ ADAPTER_ERRORS = [
24
+ ::ActiveRecord::ConnectionNotEstablished,
25
+ ::ActiveRecord::ConnectionTimeoutError,
26
+ ::ActiveRecord::JDBCError,
27
+ ::ActiveRecord::StatementInvalid,
28
+ Mysql::Error,
29
+ Mysql2::Error,
30
+ PG::Error,
31
+ PGError,
32
+ SQLite3::Exception
33
+ ]
34
+
35
+ def with_database_reconnect_and_retry
36
+ ::ActiveRecord::Base.connection_pool.with_connection do
37
+ yield
38
+ end
39
+ rescue *ADAPTER_ERRORS => e
40
+ Rpush.logger.error(e)
41
+ sleep_to_avoid_thrashing
42
+ database_connection_lost
43
+ retry
44
+ end
45
+
46
+ def database_connection_lost
47
+ Rpush.logger.warn("Lost connection to database, reconnecting...")
48
+ attempts = 0
49
+ loop do
50
+ begin
51
+ Rpush.logger.warn("Attempt #{attempts += 1}")
52
+ reconnect_database
53
+ check_database_is_connected
54
+ break
55
+ rescue *ADAPTER_ERRORS => e
56
+ Rpush.logger.error(e)
57
+ sleep_to_avoid_thrashing
58
+ end
59
+ end
60
+ Rpush.logger.warn("Database reconnected")
61
+ end
62
+
63
+ def reconnect_database
64
+ ::ActiveRecord::Base.clear_all_connections!
65
+ ::ActiveRecord::Base.establish_connection
66
+ end
67
+
68
+ def check_database_is_connected
69
+ # Simply asking the adapter for the connection state is not sufficient.
70
+ Rpush::Client::ActiveRecord::Notification.count
71
+ end
72
+
73
+ def sleep_to_avoid_thrashing
74
+ sleep 2
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end