rpush 1.0.0-java → 2.0.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 (201) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +15 -0
  3. data/README.md +37 -22
  4. data/bin/rpush +13 -4
  5. data/lib/generators/rpush_generator.rb +2 -0
  6. data/lib/generators/templates/add_adm.rb +5 -5
  7. data/lib/generators/templates/add_alert_is_json_to_rapns_notifications.rb +1 -1
  8. data/lib/generators/templates/add_app_to_rapns.rb +2 -2
  9. data/lib/generators/templates/add_fail_after_to_rpush_notifications.rb +1 -1
  10. data/lib/generators/templates/add_gcm.rb +32 -32
  11. data/lib/generators/templates/add_rpush.rb +67 -67
  12. data/lib/generators/templates/add_wpns.rb +2 -2
  13. data/lib/generators/templates/create_rapns_apps.rb +5 -5
  14. data/lib/generators/templates/create_rapns_feedback.rb +2 -2
  15. data/lib/generators/templates/create_rapns_notifications.rb +15 -15
  16. data/lib/generators/templates/rpush.rb +28 -7
  17. data/lib/generators/templates/rpush_2_0_0_updates.rb +42 -0
  18. data/lib/rpush/client/active_model/adm/app.rb +23 -0
  19. data/lib/rpush/client/active_model/adm/data_validator.rb +14 -0
  20. data/lib/rpush/client/active_model/adm/notification.rb +28 -0
  21. data/lib/rpush/client/active_model/apns/app.rb +37 -0
  22. data/lib/rpush/client/active_model/apns/binary_notification_validator.rb +16 -0
  23. data/lib/rpush/client/active_model/apns/device_token_format_validator.rb +14 -0
  24. data/lib/rpush/client/active_model/apns/notification.rb +90 -0
  25. data/lib/rpush/client/active_model/gcm/app.rb +19 -0
  26. data/lib/rpush/client/active_model/gcm/expiry_collapse_key_mutual_inclusion_validator.rb +14 -0
  27. data/lib/rpush/client/active_model/gcm/notification.rb +31 -0
  28. data/lib/rpush/client/active_model/notification.rb +26 -0
  29. data/lib/rpush/client/active_model/payload_data_size_validator.rb +13 -0
  30. data/lib/rpush/client/active_model/registration_ids_count_validator.rb +13 -0
  31. data/lib/rpush/client/active_model/wpns/app.rb +13 -0
  32. data/lib/rpush/client/active_model/wpns/notification.rb +17 -0
  33. data/lib/rpush/client/active_model.rb +21 -0
  34. data/lib/rpush/client/active_record/adm/app.rb +11 -0
  35. data/lib/rpush/client/active_record/adm/notification.rb +11 -0
  36. data/lib/rpush/client/active_record/apns/app.rb +11 -0
  37. data/lib/rpush/client/active_record/apns/feedback.rb +22 -0
  38. data/lib/rpush/client/active_record/apns/notification.rb +46 -0
  39. data/lib/rpush/client/active_record/app.rb +17 -0
  40. data/lib/rpush/client/active_record/gcm/app.rb +11 -0
  41. data/lib/rpush/client/active_record/gcm/notification.rb +11 -0
  42. data/lib/rpush/client/active_record/notification.rb +38 -0
  43. data/lib/rpush/client/active_record/wpns/app.rb +11 -0
  44. data/lib/rpush/client/active_record/wpns/notification.rb +11 -0
  45. data/lib/rpush/client/active_record.rb +19 -0
  46. data/lib/rpush/client/redis/adm/app.rb +14 -0
  47. data/lib/rpush/client/redis/adm/notification.rb +11 -0
  48. data/lib/rpush/client/redis/apns/app.rb +11 -0
  49. data/lib/rpush/client/redis/apns/feedback.rb +20 -0
  50. data/lib/rpush/client/redis/apns/notification.rb +11 -0
  51. data/lib/rpush/client/redis/app.rb +24 -0
  52. data/lib/rpush/client/redis/gcm/app.rb +11 -0
  53. data/lib/rpush/client/redis/gcm/notification.rb +11 -0
  54. data/lib/rpush/client/redis/notification.rb +68 -0
  55. data/lib/rpush/client/redis/wpns/app.rb +11 -0
  56. data/lib/rpush/client/redis/wpns/notification.rb +11 -0
  57. data/lib/rpush/client/redis.rb +35 -0
  58. data/lib/rpush/configuration.rb +27 -6
  59. data/lib/rpush/daemon/adm/delivery.rb +56 -55
  60. data/lib/rpush/daemon/apns/delivery.rb +20 -44
  61. data/lib/rpush/daemon/apns/feedback_receiver.rb +11 -8
  62. data/lib/rpush/daemon/apns.rb +6 -5
  63. data/lib/rpush/daemon/app_runner.rb +103 -99
  64. data/lib/rpush/daemon/batch.rb +54 -40
  65. data/lib/rpush/daemon/delivery.rb +13 -3
  66. data/lib/rpush/daemon/delivery_error.rb +10 -2
  67. data/lib/rpush/daemon/dispatcher/apns_tcp.rb +114 -0
  68. data/lib/rpush/daemon/dispatcher/http.rb +3 -3
  69. data/lib/rpush/daemon/dispatcher/tcp.rb +3 -3
  70. data/lib/rpush/daemon/dispatcher_loop.rb +37 -23
  71. data/lib/rpush/daemon/errors.rb +18 -0
  72. data/lib/rpush/daemon/feeder.rb +28 -39
  73. data/lib/rpush/daemon/gcm/delivery.rb +19 -20
  74. data/lib/rpush/daemon/interruptible_sleep.rb +26 -45
  75. data/lib/rpush/daemon/loggable.rb +2 -4
  76. data/lib/rpush/daemon/proc_title.rb +16 -0
  77. data/lib/rpush/daemon/queue_payload.rb +12 -0
  78. data/lib/rpush/daemon/reflectable.rb +3 -5
  79. data/lib/rpush/daemon/retry_header_parser.rb +6 -6
  80. data/lib/rpush/daemon/retryable_error.rb +2 -0
  81. data/lib/rpush/daemon/ring_buffer.rb +16 -0
  82. data/lib/rpush/daemon/service_config_methods.rb +23 -7
  83. data/lib/rpush/daemon/signal_handler.rb +56 -0
  84. data/lib/rpush/daemon/store/active_record/reconnectable.rb +21 -17
  85. data/lib/rpush/daemon/store/active_record.rb +71 -38
  86. data/lib/rpush/daemon/store/interface.rb +19 -0
  87. data/lib/rpush/daemon/store/redis.rb +149 -0
  88. data/lib/rpush/daemon/string_helpers.rb +15 -0
  89. data/lib/rpush/daemon/synchronizer.rb +60 -0
  90. data/lib/rpush/daemon/tcp_connection.rb +6 -11
  91. data/lib/rpush/daemon/wpns/delivery.rb +21 -30
  92. data/lib/rpush/daemon.rb +40 -60
  93. data/lib/rpush/deprecatable.rb +4 -3
  94. data/lib/rpush/deprecation.rb +7 -10
  95. data/lib/rpush/embed.rb +8 -3
  96. data/lib/rpush/logger.rb +11 -15
  97. data/lib/rpush/push.rb +1 -2
  98. data/lib/rpush/reflection.rb +8 -12
  99. data/lib/rpush/version.rb +1 -1
  100. data/lib/rpush.rb +5 -29
  101. data/lib/tasks/quality.rake +35 -0
  102. data/lib/tasks/test.rake +1 -5
  103. data/spec/.rubocop.yml +4 -0
  104. data/spec/functional/adm_spec.rb +3 -6
  105. data/spec/functional/apns_spec.rb +117 -24
  106. data/spec/functional/embed_spec.rb +20 -20
  107. data/spec/functional/gcm_spec.rb +4 -7
  108. data/spec/functional/new_app_spec.rb +59 -0
  109. data/spec/functional/retry_spec.rb +46 -0
  110. data/spec/functional/synchronization_spec.rb +68 -0
  111. data/spec/functional/wpns_spec.rb +3 -6
  112. data/spec/functional_spec_helper.rb +26 -0
  113. data/spec/integration/rpush_spec.rb +13 -0
  114. data/spec/integration/support/gcm_success_response.json +1 -0
  115. data/spec/spec_helper.rb +60 -0
  116. data/spec/support/active_record_setup.rb +48 -0
  117. data/{config → spec/support/config}/database.yml +0 -0
  118. data/spec/support/install.sh +43 -7
  119. data/spec/support/simplecov_helper.rb +9 -5
  120. data/spec/support/simplecov_quality_formatter.rb +10 -6
  121. data/spec/unit/apns_feedback_spec.rb +3 -3
  122. data/spec/unit/{adm → client/active_record/adm}/app_spec.rb +3 -3
  123. data/spec/unit/{adm → client/active_record/adm}/notification_spec.rb +5 -7
  124. data/spec/unit/client/active_record/apns/app_spec.rb +29 -0
  125. data/spec/unit/client/active_record/apns/feedback_spec.rb +9 -0
  126. data/spec/unit/client/active_record/apns/notification_spec.rb +231 -0
  127. data/spec/unit/client/active_record/app_spec.rb +30 -0
  128. data/spec/unit/client/active_record/gcm/app_spec.rb +4 -0
  129. data/spec/unit/{gcm → client/active_record/gcm}/notification_spec.rb +5 -7
  130. data/spec/unit/client/active_record/notification_spec.rb +21 -0
  131. data/spec/unit/client/active_record/wpns/app_spec.rb +4 -0
  132. data/spec/unit/client/active_record/wpns/notification_spec.rb +21 -0
  133. data/spec/unit/configuration_spec.rb +12 -5
  134. data/spec/unit/daemon/adm/delivery_spec.rb +66 -55
  135. data/spec/unit/daemon/apns/certificate_expired_error_spec.rb +3 -3
  136. data/spec/unit/daemon/apns/delivery_spec.rb +90 -83
  137. data/spec/unit/daemon/apns/feedback_receiver_spec.rb +22 -17
  138. data/spec/unit/daemon/app_runner_spec.rb +78 -186
  139. data/spec/unit/daemon/batch_spec.rb +52 -115
  140. data/spec/unit/daemon/delivery_spec.rb +15 -1
  141. data/spec/unit/daemon/dispatcher/http_spec.rb +3 -2
  142. data/spec/unit/daemon/dispatcher/tcp_spec.rb +10 -9
  143. data/spec/unit/daemon/dispatcher_loop_spec.rb +6 -24
  144. data/spec/unit/daemon/feeder_spec.rb +38 -39
  145. data/spec/unit/daemon/gcm/delivery_spec.rb +122 -101
  146. data/spec/unit/daemon/reflectable_spec.rb +2 -2
  147. data/spec/unit/daemon/retryable_error_spec.rb +1 -1
  148. data/spec/unit/daemon/service_config_methods_spec.rb +6 -3
  149. data/spec/unit/daemon/signal_handler_spec.rb +95 -0
  150. data/spec/unit/daemon/store/active_record/reconnectable_spec.rb +48 -27
  151. data/spec/unit/daemon/store/active_record_spec.rb +38 -47
  152. data/spec/unit/daemon/tcp_connection_spec.rb +22 -34
  153. data/spec/unit/daemon/wpns/delivery_spec.rb +58 -50
  154. data/spec/unit/daemon_spec.rb +48 -82
  155. data/spec/unit/embed_spec.rb +6 -4
  156. data/spec/unit/logger_spec.rb +35 -43
  157. data/spec/unit/notification_shared.rb +9 -79
  158. data/spec/unit/push_spec.rb +6 -10
  159. data/spec/unit/reflection_spec.rb +25 -25
  160. data/spec/unit/rpush_spec.rb +1 -2
  161. data/spec/unit_spec_helper.rb +33 -88
  162. metadata +126 -76
  163. data/lib/rpush/TODO +0 -3
  164. data/lib/rpush/adm/app.rb +0 -15
  165. data/lib/rpush/adm/data_validator.rb +0 -11
  166. data/lib/rpush/adm/notification.rb +0 -29
  167. data/lib/rpush/apns/app.rb +0 -29
  168. data/lib/rpush/apns/binary_notification_validator.rb +0 -12
  169. data/lib/rpush/apns/device_token_format_validator.rb +0 -12
  170. data/lib/rpush/apns/feedback.rb +0 -16
  171. data/lib/rpush/apns/notification.rb +0 -84
  172. data/lib/rpush/app.rb +0 -18
  173. data/lib/rpush/daemon/apns/certificate_expired_error.rb +0 -20
  174. data/lib/rpush/daemon/apns/disconnection_error.rb +0 -20
  175. data/lib/rpush/daemon/dispatcher_loop_collection.rb +0 -33
  176. data/lib/rpush/daemon/too_many_requests_error.rb +0 -20
  177. data/lib/rpush/gcm/app.rb +0 -11
  178. data/lib/rpush/gcm/expiry_collapse_key_mutual_inclusion_validator.rb +0 -11
  179. data/lib/rpush/gcm/notification.rb +0 -30
  180. data/lib/rpush/notification.rb +0 -69
  181. data/lib/rpush/notifier.rb +0 -52
  182. data/lib/rpush/payload_data_size_validator.rb +0 -10
  183. data/lib/rpush/railtie.rb +0 -11
  184. data/lib/rpush/registration_ids_count_validator.rb +0 -10
  185. data/lib/rpush/wpns/app.rb +0 -9
  186. data/lib/rpush/wpns/notification.rb +0 -26
  187. data/lib/tasks/cane.rake +0 -18
  188. data/lib/tasks/rpush.rake +0 -16
  189. data/spec/unit/apns/app_spec.rb +0 -29
  190. data/spec/unit/apns/feedback_spec.rb +0 -9
  191. data/spec/unit/apns/notification_spec.rb +0 -208
  192. data/spec/unit/app_spec.rb +0 -30
  193. data/spec/unit/daemon/apns/disconnection_error_spec.rb +0 -18
  194. data/spec/unit/daemon/dispatcher_loop_collection_spec.rb +0 -37
  195. data/spec/unit/daemon/interruptible_sleep_spec.rb +0 -68
  196. data/spec/unit/daemon/too_many_requests_error_spec.rb +0 -14
  197. data/spec/unit/gcm/app_spec.rb +0 -4
  198. data/spec/unit/notification_spec.rb +0 -15
  199. data/spec/unit/notifier_spec.rb +0 -49
  200. data/spec/unit/wpns/app_spec.rb +0 -4
  201. data/spec/unit/wpns/notification_spec.rb +0 -30
@@ -3,8 +3,7 @@ module Rpush
3
3
  class Batch
4
4
  include Reflectable
5
5
 
6
- attr_reader :num_processed, :notifications,
7
- :delivered, :failed, :retryable
6
+ attr_reader :num_processed, :notifications, :delivered, :failed, :retryable
8
7
 
9
8
  def initialize(notifications)
10
9
  @notifications = notifications
@@ -15,61 +14,80 @@ module Rpush
15
14
  @mutex = Mutex.new
16
15
  end
17
16
 
18
- def num_notifications
19
- @notifications.size
17
+ def complete?
18
+ @complete == true
19
+ end
20
+
21
+ def each_notification(&blk)
22
+ @notifications.each(&blk)
23
+ end
24
+
25
+ def each_delivered(&blk)
26
+ @delivered.each(&blk)
20
27
  end
21
28
 
22
29
  def mark_retryable(notification, deliver_after)
23
- if Rpush.config.batch_storage_updates
24
- retryable[deliver_after] ||= []
25
- retryable[deliver_after] << notification
26
- Rpush::Daemon.store.mark_retryable(notification, deliver_after, :persist => false)
27
- else
28
- Rpush::Daemon.store.mark_retryable(notification, deliver_after)
29
- reflect(:notification_will_retry, notification)
30
+ @mutex.synchronize do
31
+ @retryable[deliver_after] ||= []
32
+ @retryable[deliver_after] << notification
30
33
  end
34
+ Rpush::Daemon.store.mark_retryable(notification, deliver_after, persist: false)
31
35
  end
32
36
 
33
37
  def mark_delivered(notification)
34
- if Rpush.config.batch_storage_updates
35
- delivered << notification
36
- Rpush::Daemon.store.mark_delivered(notification, Time.now, :persist => false)
37
- else
38
- Rpush::Daemon.store.mark_delivered(notification, Time.now)
39
- reflect(:notification_delivered, notification)
38
+ @mutex.synchronize do
39
+ @delivered << notification
40
+ end
41
+ Rpush::Daemon.store.mark_delivered(notification, Time.now, persist: false)
42
+ end
43
+
44
+ def mark_all_delivered
45
+ @mutex.synchronize do
46
+ @delivered = @notifications
47
+ end
48
+ each_notification do |notification|
49
+ Rpush::Daemon.store.mark_delivered(notification, Time.now, persist: false)
40
50
  end
41
51
  end
42
52
 
43
53
  def mark_failed(notification, code, description)
44
- if Rpush.config.batch_storage_updates
45
- key = [code, description]
46
- failed[key] ||= []
47
- failed[key] << notification
48
- Rpush::Daemon.store.mark_failed(notification, code, description, Time.now, :persist => false)
49
- else
50
- Rpush::Daemon.store.mark_failed(notification, code, description, Time.now)
51
- reflect(:notification_failed, notification)
54
+ key = [code, description]
55
+ @mutex.synchronize do
56
+ @failed[key] ||= []
57
+ @failed[key] << notification
52
58
  end
59
+ Rpush::Daemon.store.mark_failed(notification, code, description, Time.now, persist: false)
53
60
  end
54
61
 
55
- def notification_dispatched
62
+ def mark_all_failed(code, message)
63
+ key = [code, message]
56
64
  @mutex.synchronize do
57
- @num_processed += 1
58
- complete if @num_processed >= @notifications.size
65
+ @failed[key] = @notifications
66
+ end
67
+ each_notification do |notification|
68
+ Rpush::Daemon.store.mark_failed(notification, code, message, Time.now, persist: false)
59
69
  end
60
70
  end
61
71
 
62
- def complete?
63
- @complete == true
72
+ def notification_processed
73
+ @mutex.synchronize do
74
+ @num_processed += 1
75
+ complete if @num_processed >= @notifications.size
76
+ end
64
77
  end
65
78
 
66
- def describe
67
- notifications.map(&:id).join(', ')
79
+ def all_processed
80
+ @mutex.synchronize do
81
+ @num_processed = @notifications.size
82
+ complete
83
+ end
68
84
  end
69
85
 
70
86
  private
71
87
 
72
88
  def complete
89
+ return if complete?
90
+
73
91
  [:complete_delivered, :complete_failed, :complete_retried].each do |method|
74
92
  begin
75
93
  send(method)
@@ -79,36 +97,32 @@ module Rpush
79
97
  end
80
98
  end
81
99
 
82
- notifications.clear
83
100
  @complete = true
84
101
  end
85
102
 
86
103
  def complete_delivered
87
- Rpush::Daemon.store.mark_batch_delivered(delivered)
88
- delivered.each do |notification|
104
+ Rpush::Daemon.store.mark_batch_delivered(@delivered)
105
+ @delivered.each do |notification|
89
106
  reflect(:notification_delivered, notification)
90
107
  end
91
- delivered.clear
92
108
  end
93
109
 
94
110
  def complete_failed
95
- failed.each do |(code, description), notifications|
111
+ @failed.each do |(code, description), notifications|
96
112
  Rpush::Daemon.store.mark_batch_failed(notifications, code, description)
97
113
  notifications.each do |notification|
98
114
  reflect(:notification_failed, notification)
99
115
  end
100
116
  end
101
- failed.clear
102
117
  end
103
118
 
104
119
  def complete_retried
105
- retryable.each do |deliver_after, notifications|
120
+ @retryable.each do |deliver_after, notifications|
106
121
  Rpush::Daemon.store.mark_batch_retryable(notifications, deliver_after)
107
122
  notifications.each do |notification|
108
123
  reflect(:notification_will_retry, notification)
109
124
  end
110
125
  end
111
- retryable.clear
112
126
  end
113
127
  end
114
128
  end
@@ -13,15 +13,25 @@ module Rpush
13
13
  end
14
14
 
15
15
  def mark_retryable_exponential(notification)
16
- mark_retryable(notification, Time.now + 2 ** (notification.retries + 1))
16
+ mark_retryable(notification, Time.now + 2**(notification.retries + 1))
17
17
  end
18
18
 
19
19
  def mark_delivered
20
20
  @batch.mark_delivered(@notification)
21
21
  end
22
22
 
23
- def mark_failed(code, description)
24
- @batch.mark_failed(@notification, code, description)
23
+ def mark_batch_delivered
24
+ @batch.mark_all_delivered
25
+ end
26
+
27
+ def mark_failed(error)
28
+ code = error.respond_to?(:code) ? error.code : nil
29
+ @batch.mark_failed(@notification, code, error.to_s)
30
+ end
31
+
32
+ def mark_batch_failed(error)
33
+ code = error.respond_to?(:code) ? error.code : nil
34
+ @batch.mark_all_failed(code, error.to_s)
25
35
  end
26
36
  end
27
37
  end
@@ -1,6 +1,6 @@
1
1
  module Rpush
2
2
  class DeliveryError < StandardError
3
- attr_reader :code, :description
3
+ attr_reader :code, :notification_id
4
4
 
5
5
  def initialize(code, notification_id, description)
6
6
  @code = code
@@ -13,7 +13,15 @@ module Rpush
13
13
  end
14
14
 
15
15
  def message
16
- "Unable to deliver notification #{@notification_id}, received error #{@code} (#{@description})"
16
+ error_str = [@code, "(#{@description})"].compact.join(' ')
17
+ "Unable to deliver notification #{@notification_id}, received error #{error_str}"
18
+ end
19
+
20
+ def ==(other)
21
+ other.is_a?(DeliveryError) && \
22
+ other.code == code && \
23
+ other.notification_id == notification_id && \
24
+ other.to_s == to_s
17
25
  end
18
26
  end
19
27
  end
@@ -0,0 +1,114 @@
1
+ module Rpush
2
+ module Daemon
3
+ module Dispatcher
4
+ class ApnsTcp < Rpush::Daemon::Dispatcher::Tcp
5
+ include Loggable
6
+ include Reflectable
7
+
8
+ SELECT_TIMEOUT = 10
9
+ ERROR_TUPLE_BYTES = 6
10
+ APNS_ERRORS = {
11
+ 1 => 'Processing error',
12
+ 2 => 'Missing device token',
13
+ 3 => 'Missing topic',
14
+ 4 => 'Missing payload',
15
+ 5 => 'Missing token size',
16
+ 6 => 'Missing topic size',
17
+ 7 => 'Missing payload size',
18
+ 8 => 'Invalid token',
19
+ 255 => 'None (unknown error)'
20
+ }
21
+
22
+ def initialize(*args)
23
+ super
24
+ @dispatch_mutex = Mutex.new
25
+ @stop_error_receiver = false
26
+ start_error_receiver
27
+ end
28
+
29
+ def dispatch(payload)
30
+ @dispatch_mutex.synchronize do
31
+ @delivery_class.new(@app, connection, payload.batch).perform
32
+ record_batch(payload.batch)
33
+ end
34
+ end
35
+
36
+ def cleanup
37
+ @stop_error_receiver = true
38
+ super
39
+ @error_receiver_thread.join if @error_receiver_thread
40
+ end
41
+
42
+ private
43
+
44
+ def start_error_receiver
45
+ @error_receiver_thread = Thread.new do
46
+ check_for_error until @stop_error_receiver
47
+ Rpush::Daemon.store.release_connection
48
+ end
49
+ end
50
+
51
+ def delivered_buffer
52
+ @delivered_buffer ||= RingBuffer.new(Rpush.config.batch_size * 10)
53
+ end
54
+
55
+ def record_batch(batch)
56
+ batch.each_delivered do |notification|
57
+ delivered_buffer << notification.id
58
+ end
59
+ end
60
+
61
+ def check_for_error
62
+ begin
63
+ return unless connection.select(SELECT_TIMEOUT)
64
+ rescue Errno::EBADF
65
+ # Connection closed, daemon is shutting down.
66
+ return
67
+ end
68
+
69
+ tuple = connection.read(ERROR_TUPLE_BYTES)
70
+ @dispatch_mutex.synchronize { handle_error_response(tuple) }
71
+ end
72
+
73
+ def handle_error_response(tuple)
74
+ if tuple
75
+ _, code, notification_id = tuple.unpack('ccN')
76
+ handle_error(code, notification_id)
77
+ else
78
+ handle_disconnect
79
+ end
80
+
81
+ log_error('Error received, reconnecting...')
82
+ connection.reconnect
83
+ ensure
84
+ delivered_buffer.clear
85
+ end
86
+
87
+ def handle_disconnect
88
+ log_error('The APNs disconnected without returning an error. Marking all notifications delivered via this connection as failed.')
89
+ reason = 'The APNs disconnected without returning an error. This may indicate you are using an invalid certificate.'
90
+ Rpush::Daemon.store.mark_ids_failed(delivered_buffer, nil, reason, Time.now)
91
+ delivered_buffer.each { |id| reflect(:notification_id_failed, @app, id, nil, reason) }
92
+ end
93
+
94
+ def handle_error(code, notification_id)
95
+ failed_pos = delivered_buffer.index(notification_id)
96
+ description = APNS_ERRORS[code.to_i] || "Unknown error code #{code.inspect}. Possible Rpush bug?"
97
+ Rpush::Daemon.store.mark_ids_failed([notification_id], code, description, Time.now)
98
+ reflect(:notification_id_failed, @app, notification_id, code, description)
99
+
100
+ if failed_pos
101
+ retry_ids = delivered_buffer[(failed_pos + 1)..-1]
102
+ if retry_ids.size > 0
103
+ now = Time.now
104
+ Rpush::Daemon.store.mark_ids_retryable(retry_ids, now)
105
+ retry_ids.each { |id| reflect(:notification_id_will_retry, @app, id, now) }
106
+ end
107
+ elsif delivered_buffer.size > 0
108
+ log_error("Delivery sequence unknown for notifications following #{notification_id}.")
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
@@ -2,14 +2,14 @@ module Rpush
2
2
  module Daemon
3
3
  module Dispatcher
4
4
  class Http
5
- def initialize(app, delivery_class, options = {})
5
+ def initialize(app, delivery_class, _options = {})
6
6
  @app = app
7
7
  @delivery_class = delivery_class
8
8
  @http = Net::HTTP::Persistent.new('rpush')
9
9
  end
10
10
 
11
- def dispatch(notification, batch)
12
- @delivery_class.new(@app, @http, notification, batch).perform
11
+ def dispatch(payload)
12
+ @delivery_class.new(@app, @http, payload.notification, payload.batch).perform
13
13
  end
14
14
 
15
15
  def cleanup
@@ -8,15 +8,15 @@ module Rpush
8
8
  @host, @port = options[:host].call(@app)
9
9
  end
10
10
 
11
- def dispatch(notification, batch)
12
- @delivery_class.new(@app, connection, notification, batch).perform
11
+ def dispatch(payload)
12
+ @delivery_class.new(@app, connection, payload.notification, payload.batch).perform
13
13
  end
14
14
 
15
15
  def cleanup
16
16
  @connection.close if @connection
17
17
  end
18
18
 
19
- private
19
+ protected
20
20
 
21
21
  def connection
22
22
  return @connection if defined? @connection
@@ -2,19 +2,38 @@ module Rpush
2
2
  module Daemon
3
3
  class DispatcherLoop
4
4
  include Reflectable
5
+ include Loggable
5
6
 
6
- WAKEUP = :wakeup
7
+ attr_reader :started_at, :dispatch_count
8
+
9
+ STOP = :stop
7
10
 
8
11
  def initialize(queue, dispatcher)
9
12
  @queue = queue
10
13
  @dispatcher = dispatcher
14
+ @dispatch_count = 0
15
+ end
16
+
17
+ def thread_status
18
+ @thread ? @thread.status : 'not started'
11
19
  end
12
20
 
13
21
  def start
22
+ @started_at = Time.now
23
+
14
24
  @thread = Thread.new do
15
25
  loop do
16
- dispatch
17
- break if @stop
26
+ payload = @queue.pop
27
+ if stop_payload?(payload)
28
+ break if should_stop?(payload)
29
+
30
+ # Intended for another dispatcher loop.
31
+ @queue.push(payload)
32
+ Thread.pass
33
+ sleep 0.1
34
+ else
35
+ dispatch(payload)
36
+ end
18
37
  end
19
38
 
20
39
  Rpush::Daemon.store.release_connection
@@ -22,32 +41,27 @@ module Rpush
22
41
  end
23
42
 
24
43
  def stop
25
- @stop = true
26
- end
27
-
28
- def wakeup
29
- @queue.push(WAKEUP) if @thread
30
- end
31
-
32
- def wait
44
+ @queue.push([STOP, object_id]) if @thread
33
45
  @thread.join if @thread
34
46
  @dispatcher.cleanup
35
47
  end
36
48
 
37
- protected
49
+ private
50
+
51
+ def stop_payload?(payload)
52
+ payload.is_a?(Array) && payload.first == STOP
53
+ end
38
54
 
39
- def dispatch
40
- notification, batch = @queue.pop
41
- return if notification == WAKEUP
55
+ def should_stop?(payload)
56
+ payload.last == object_id
57
+ end
42
58
 
43
- begin
44
- @dispatcher.dispatch(notification, batch)
45
- rescue StandardError => e
46
- Rpush.logger.error(e)
47
- reflect(:error, e)
48
- ensure
49
- batch.notification_dispatched
50
- end
59
+ def dispatch(payload)
60
+ @dispatch_count += 1
61
+ @dispatcher.dispatch(payload)
62
+ rescue StandardError => e
63
+ log_error(e)
64
+ reflect(:error, e)
51
65
  end
52
66
  end
53
67
  end
@@ -0,0 +1,18 @@
1
+ module Rpush
2
+ class CertificateExpiredError < StandardError
3
+ attr_reader :app, :time
4
+
5
+ def initialize(app, time)
6
+ @app = app
7
+ @time = time
8
+ end
9
+
10
+ def to_s
11
+ message
12
+ end
13
+
14
+ def message
15
+ "#{app.name} certificate expired at #{time}."
16
+ end
17
+ end
18
+ end
@@ -4,63 +4,52 @@ module Rpush
4
4
  extend Reflectable
5
5
 
6
6
  def self.start
7
- @stop = false
8
-
9
- if Rpush.config.embedded
10
- Thread.new { feed_forever }
11
- elsif Rpush.config.push
12
- enqueue_notifications
13
- else
14
- feed_forever
15
- end
7
+ self.should_stop = false
8
+ Rpush.config.push ? enqueue_notifications : feed_forever
16
9
  end
17
10
 
18
11
  def self.stop
19
- @stop = true
20
- interrupt_sleep
12
+ self.should_stop = true
13
+ interruptible_sleeper.stop
14
+ @thread.join if @thread
21
15
  end
22
16
 
23
- def self.interrupt_sleep
24
- interruptible_sleeper.interrupt_sleep
17
+ def self.wakeup
18
+ interruptible_sleeper.wakeup
25
19
  end
26
20
 
27
- protected
21
+ class << self
22
+ attr_accessor :should_stop
23
+ end
28
24
 
29
25
  def self.feed_forever
30
- loop do
31
- enqueue_notifications
32
- interruptible_sleeper.sleep(Rpush.config.push_poll)
33
- break if stop?
26
+ @thread = Thread.new do
27
+ loop do
28
+ enqueue_notifications
29
+ interruptible_sleeper.sleep
30
+ break if should_stop
31
+ end
32
+
33
+ Rpush::Daemon.store.release_connection
34
34
  end
35
35
 
36
- Rpush::Daemon.store.release_connection
37
- end
38
-
39
- # :nocov:
40
- def self.stop?
41
- @stop
36
+ @thread.join
42
37
  end
43
38
 
44
39
  def self.enqueue_notifications
45
- begin
46
- idle = Rpush::Daemon::AppRunner.idle.map(&:app)
47
- return if idle.empty?
48
- notifications = Rpush::Daemon.store.deliverable_notifications(idle)
49
- Rpush::Daemon::AppRunner.enqueue(notifications)
50
- rescue StandardError => e
51
- Rpush.logger.error(e)
52
- reflect(:error, e)
53
- end
40
+ batch_size = Rpush.config.batch_size - Rpush::Daemon::AppRunner.total_queued
41
+ return if batch_size <= 0
42
+ notifications = Rpush::Daemon.store.deliverable_notifications(batch_size)
43
+ Rpush::Daemon::AppRunner.enqueue(notifications)
44
+ rescue StandardError => e
45
+ Rpush.logger.error(e)
46
+ reflect(:error, e)
54
47
  end
55
48
 
56
49
  def self.interruptible_sleeper
57
50
  return @interruptible_sleeper if @interruptible_sleeper
58
-
59
- @interruptible_sleeper = InterruptibleSleep.new
60
- if Rpush.config.wakeup
61
- @interruptible_sleeper.enable_wake_on_udp Rpush.config.wakeup[:bind], Rpush.config.wakeup[:port]
62
- end
63
-
51
+ @interruptible_sleeper = InterruptibleSleep.new(Rpush.config.push_poll)
52
+ @interruptible_sleeper.start
64
53
  @interruptible_sleeper
65
54
  end
66
55
  end
@@ -1,15 +1,14 @@
1
1
  module Rpush
2
2
  module Daemon
3
3
  module Gcm
4
-
5
4
  # http://developer.android.com/guide/google/gcm/gcm.html#response
6
5
  class Delivery < Rpush::Daemon::Delivery
7
6
  include MultiJsonHelper
8
7
 
9
8
  host = ENV["RPUSH_GCM_HOST"] || "https://android.googleapis.com"
10
9
  GCM_URI = URI.parse("#{host}/gcm/send")
11
- UNAVAILABLE_STATES = ['Unavailable', 'InternalServerError']
12
- INVALID_REGISTRATION_ID_STATES = ['InvalidRegistration', 'MismatchSenderId', 'NotRegistered', 'InvalidPackageName']
10
+ UNAVAILABLE_STATES = %w(Unavailable InternalServerError)
11
+ INVALID_REGISTRATION_ID_STATES = %w(InvalidRegistration MismatchSenderId NotRegistered InvalidPackageName)
13
12
 
14
13
  def initialize(app, http, notification, batch)
15
14
  @app = app
@@ -19,12 +18,12 @@ module Rpush
19
18
  end
20
19
 
21
20
  def perform
22
- begin
23
- handle_response(do_post)
24
- rescue Rpush::DeliveryError => error
25
- mark_failed(error.code, error.description)
26
- raise
27
- end
21
+ handle_response(do_post)
22
+ rescue StandardError => error
23
+ mark_failed(error)
24
+ raise
25
+ ensure
26
+ @batch.notification_processed
28
27
  end
29
28
 
30
29
  protected
@@ -42,7 +41,7 @@ module Rpush
42
41
  when 503
43
42
  service_unavailable(response)
44
43
  else
45
- raise Rpush::DeliveryError.new(response.code, @notification.id, Rpush::Daemon::HTTP_STATUS_CODES[response.code.to_i])
44
+ fail Rpush::DeliveryError.new(response.code, @notification.id, Rpush::Daemon::HTTP_STATUS_CODES[response.code.to_i])
46
45
  end
47
46
  end
48
47
 
@@ -69,9 +68,8 @@ module Rpush
69
68
  def handle_successes(successes)
70
69
  successes.each do |result|
71
70
  reflect(:gcm_delivered_to_recipient, @notification, result[:registration_id])
72
- if result.has_key?(:canonical_id)
73
- reflect(:gcm_canonical_id, result[:registration_id], result[:canonical_id])
74
- end
71
+ next unless result.key?(:canonical_id)
72
+ reflect(:gcm_canonical_id, result[:registration_id], result[:canonical_id])
75
73
  end
76
74
  end
77
75
 
@@ -86,7 +84,7 @@ module Rpush
86
84
  failures.description += " #{unavailable_idxs.join(', ')} will be retried as notification #{new_notification.id}."
87
85
  end
88
86
  handle_errors(failures)
89
- raise Rpush::DeliveryError.new(nil, @notification.id, failures.description)
87
+ fail Rpush::DeliveryError.new(nil, @notification.id, failures.description)
90
88
  end
91
89
  end
92
90
 
@@ -103,15 +101,15 @@ module Rpush
103
101
  attrs = @notification.attributes.slice('app_id', 'collapse_key', 'delay_while_idle')
104
102
  registration_ids = @notification.registration_ids.values_at(*unavailable_idxs)
105
103
  Rpush::Daemon.store.create_gcm_notification(attrs, @notification.data,
106
- registration_ids, deliver_after_header(response), @notification.app)
104
+ registration_ids, deliver_after_header(response), @notification.app)
107
105
  end
108
106
 
109
107
  def bad_request
110
- raise Rpush::DeliveryError.new(400, @notification.id, 'GCM failed to parse the JSON request. Possibly an Rpush bug, please open an issue.')
108
+ fail Rpush::DeliveryError.new(400, @notification.id, 'GCM failed to parse the JSON request. Possibly an Rpush bug, please open an issue.')
111
109
  end
112
110
 
113
111
  def unauthorized
114
- raise Rpush::DeliveryError.new(401, @notification.id, 'Unauthorized, check your App auth_key.')
112
+ fail Rpush::DeliveryError.new(401, @notification.id, 'Unauthorized, check your App auth_key.')
115
113
  end
116
114
 
117
115
  def internal_server_error(response)
@@ -129,7 +127,8 @@ module Rpush
129
127
  end
130
128
 
131
129
  def retry_delivery(notification, response)
132
- if time = deliver_after_header(response)
130
+ time = deliver_after_header(response)
131
+ if time
133
132
  mark_retryable(notification, time)
134
133
  else
135
134
  mark_retryable_exponential(notification)
@@ -141,8 +140,8 @@ module Rpush
141
140
  end
142
141
 
143
142
  def do_post
144
- post = Net::HTTP::Post.new(GCM_URI.path, initheader = {'Content-Type' => 'application/json',
145
- 'Authorization' => "key=#{@notification.app.auth_key}"})
143
+ post = Net::HTTP::Post.new(GCM_URI.path, 'Content-Type' => 'application/json',
144
+ 'Authorization' => "key=#{@notification.app.auth_key}")
146
145
  post.body = @notification.as_json.to_json
147
146
  @http.request(GCM_URI, post)
148
147
  end