rpush 1.0.0-java → 2.0.0-java

Sign up to get free protection for your applications and to get access to all the features.
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