rpush_extended 3.2.5

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 (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