rpush 2.4.0-java → 2.6.0-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +27 -1
  3. data/README.md +18 -8
  4. data/lib/generators/rpush_migration_generator.rb +1 -0
  5. data/lib/generators/templates/rpush.rb +8 -2
  6. data/lib/generators/templates/rpush_2_0_0_updates.rb +1 -1
  7. data/lib/generators/templates/rpush_2_6_0_updates.rb +10 -0
  8. data/lib/rpush/cli.rb +63 -27
  9. data/lib/rpush/client/active_model.rb +3 -0
  10. data/lib/rpush/client/active_model/apns/notification.rb +1 -1
  11. data/lib/rpush/client/active_model/gcm/notification.rb +1 -0
  12. data/lib/rpush/client/active_model/wns/app.rb +23 -0
  13. data/lib/rpush/client/active_model/wns/notification.rb +28 -0
  14. data/lib/rpush/client/active_model/wpns/notification.rb +11 -6
  15. data/lib/rpush/client/active_record.rb +3 -0
  16. data/lib/rpush/client/active_record/notification.rb +1 -1
  17. data/lib/rpush/client/active_record/wns/app.rb +11 -0
  18. data/lib/rpush/client/active_record/wns/notification.rb +11 -0
  19. data/lib/rpush/client/mongoid.rb +3 -0
  20. data/lib/rpush/client/mongoid/apns/feedback.rb +3 -0
  21. data/lib/rpush/client/mongoid/notification.rb +7 -0
  22. data/lib/rpush/client/mongoid/wns/app.rb +14 -0
  23. data/lib/rpush/client/mongoid/wns/notification.rb +11 -0
  24. data/lib/rpush/client/redis.rb +3 -0
  25. data/lib/rpush/client/redis/notification.rb +1 -0
  26. data/lib/rpush/client/redis/wns/app.rb +14 -0
  27. data/lib/rpush/client/redis/wns/notification.rb +11 -0
  28. data/lib/rpush/configuration.rb +3 -7
  29. data/lib/rpush/daemon.rb +9 -0
  30. data/lib/rpush/daemon/apns/feedback_receiver.rb +5 -0
  31. data/lib/rpush/daemon/app_runner.rb +4 -5
  32. data/lib/rpush/daemon/dispatcher/apns_tcp.rb +47 -12
  33. data/lib/rpush/daemon/dispatcher_loop.rb +5 -0
  34. data/lib/rpush/daemon/feeder.rb +11 -0
  35. data/lib/rpush/daemon/gcm/delivery.rb +2 -2
  36. data/lib/rpush/daemon/interruptible_sleep.rb +8 -3
  37. data/lib/rpush/daemon/loggable.rb +4 -0
  38. data/lib/rpush/daemon/rpc.rb +9 -0
  39. data/lib/rpush/daemon/rpc/client.rb +27 -0
  40. data/lib/rpush/daemon/rpc/server.rb +82 -0
  41. data/lib/rpush/daemon/signal_handler.rb +7 -0
  42. data/lib/rpush/daemon/store/active_record.rb +17 -3
  43. data/lib/rpush/daemon/store/mongoid.rb +2 -2
  44. data/lib/rpush/daemon/store/redis.rb +2 -2
  45. data/lib/rpush/daemon/tcp_connection.rb +2 -2
  46. data/lib/rpush/daemon/wns.rb +9 -0
  47. data/lib/rpush/daemon/wns/delivery.rb +204 -0
  48. data/lib/rpush/embed.rb +15 -13
  49. data/lib/rpush/logger.rb +4 -0
  50. data/lib/rpush/plugin.rb +1 -1
  51. data/lib/rpush/push.rb +2 -11
  52. data/lib/rpush/reflection_collection.rb +15 -17
  53. data/lib/rpush/reflection_public_methods.rb +6 -4
  54. data/lib/rpush/version.rb +1 -1
  55. data/spec/functional/apns_spec.rb +1 -11
  56. data/spec/functional/cli_spec.rb +36 -0
  57. data/spec/functional_spec_helper.rb +11 -1
  58. data/spec/spec_helper.rb +4 -3
  59. data/spec/support/active_record_setup.rb +3 -2
  60. data/spec/unit/client/active_record/apns/notification_spec.rb +1 -1
  61. data/spec/unit/client/active_record/gcm/notification_spec.rb +5 -0
  62. data/spec/unit/configuration_spec.rb +0 -7
  63. data/spec/unit/daemon/adm/delivery_spec.rb +2 -2
  64. data/spec/unit/daemon/app_runner_spec.rb +2 -3
  65. data/spec/unit/daemon/gcm/delivery_spec.rb +1 -1
  66. data/spec/unit/daemon/tcp_connection_spec.rb +1 -1
  67. data/spec/unit/daemon/wns/delivery_spec.rb +171 -0
  68. data/spec/unit/daemon/wpns/delivery_spec.rb +1 -1
  69. data/spec/unit/daemon_spec.rb +2 -0
  70. data/spec/unit/embed_spec.rb +4 -11
  71. data/spec/unit/logger_spec.rb +2 -2
  72. data/spec/unit/push_spec.rb +0 -7
  73. data/spec/unit_spec_helper.rb +1 -1
  74. metadata +20 -2
@@ -27,5 +27,8 @@ require 'rpush/client/mongoid/gcm/app'
27
27
  require 'rpush/client/mongoid/wpns/notification'
28
28
  require 'rpush/client/mongoid/wpns/app'
29
29
 
30
+ require 'rpush/client/mongoid/wns/notification'
31
+ require 'rpush/client/mongoid/wns/app'
32
+
30
33
  require 'rpush/client/mongoid/adm/notification'
31
34
  require 'rpush/client/mongoid/adm/app'
@@ -13,6 +13,9 @@ module Rpush
13
13
  validates :device_token, presence: true
14
14
  validates :failed_at, presence: true
15
15
 
16
+ index device_token: 1
17
+ index app_id: 1
18
+
16
19
  validates_with Rpush::Client::ActiveModel::Apns::DeviceTokenFormatValidator
17
20
  end
18
21
  end
@@ -3,6 +3,7 @@ module Rpush
3
3
  module Mongoid
4
4
  class Notification
5
5
  include ::Mongoid::Document
6
+ include ::Mongoid::Timestamps
6
7
  include ::Mongoid::Autoinc
7
8
  include Rpush::MultiJsonHelper
8
9
  include Rpush::Client::ActiveModel::Notification
@@ -31,11 +32,17 @@ module Rpush
31
32
  field :priority, type: Integer
32
33
  field :url_args, type: Array
33
34
  field :category, type: String
35
+ field :content_available, type: Boolean, default: false
34
36
 
35
37
  field :integer_id, type: Integer
36
38
  increments :integer_id, model_name: name
37
39
  index integer_id: 1
38
40
 
41
+ index delivered: 1, failed: 1, deliver_after: 1, processing: 1
42
+ index delivered: 1, failed: 1
43
+ index device_token: 1
44
+ index app_id: 1
45
+
39
46
  belongs_to :app
40
47
  end
41
48
  end
@@ -0,0 +1,14 @@
1
+ module Rpush
2
+ module Client
3
+ module Mongoid
4
+ module Wns
5
+ class App < Rpush::Client::Mongoid::App
6
+ include Rpush::Client::ActiveModel::Wns::App
7
+
8
+ field :access_token, type: String
9
+ field :access_token_expiration, type: Time
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,11 @@
1
+ module Rpush
2
+ module Client
3
+ module Mongoid
4
+ module Wns
5
+ class Notification < Rpush::Client::Mongoid::Notification
6
+ include Rpush::Client::ActiveModel::Wns::Notification
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -30,6 +30,9 @@ require 'rpush/client/redis/adm/notification'
30
30
  require 'rpush/client/redis/wpns/app'
31
31
  require 'rpush/client/redis/wpns/notification'
32
32
 
33
+ require 'rpush/client/redis/wns/app'
34
+ require 'rpush/client/redis/wns/notification'
35
+
33
36
  Modis.configure do |config|
34
37
  config.namespace = :rpush
35
38
  end
@@ -42,6 +42,7 @@ module Rpush
42
42
  attribute :priority, :integer
43
43
  attribute :url_args, :array
44
44
  attribute :category, :string
45
+ attribute :content_available, :boolean, default: false
45
46
 
46
47
  def app
47
48
  return nil unless app_id
@@ -0,0 +1,14 @@
1
+ module Rpush
2
+ module Client
3
+ module Redis
4
+ module Wns
5
+ class App < Rpush::Client::Redis::App
6
+ include Rpush::Client::ActiveModel::Wns::App
7
+
8
+ attribute :access_token, :string
9
+ attribute :access_token_expiration, :timestamp
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,11 @@
1
+ module Rpush
2
+ module Client
3
+ module Redis
4
+ module Wns
5
+ class Notification < Rpush::Client::Redis::Notification
6
+ include Rpush::Client::ActiveModel::Wns::Notification
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -1,4 +1,5 @@
1
1
  require 'pathname'
2
+ require 'ostruct'
2
3
 
3
4
  module Rpush
4
5
  class << self
@@ -16,7 +17,7 @@ module Rpush
16
17
  end
17
18
 
18
19
  CURRENT_ATTRS = [:push_poll, :embedded, :pid_file, :batch_size, :push, :client, :logger, :log_file, :foreground, :log_level, :plugin, :apns]
19
- DEPRECATED_ATTRS = [:feedback_poll]
20
+ DEPRECATED_ATTRS = []
20
21
  CONFIG_ATTRS = CURRENT_ATTRS + DEPRECATED_ATTRS
21
22
 
22
23
  class ConfigurationError < StandardError; end
@@ -97,11 +98,6 @@ module Rpush
97
98
  Modis.redis_options = options if client == :redis
98
99
  end
99
100
 
100
- def feedback_poll=(frequency)
101
- apns.feedback_receiver.frequency = frequency
102
- end
103
- deprecated(:feedback_poll=, '2.5.0', 'Please use apns.feedback_receiver.frequency= instead.')
104
-
105
101
  def initialize_client
106
102
  return if @client_initialized
107
103
  raise ConfigurationError, 'Rpush.config.client is not set.' unless client
@@ -110,7 +106,7 @@ module Rpush
110
106
  client_module = Rpush::Client.const_get(client.to_s.camelize)
111
107
  Rpush.send(:include, client_module) unless Rpush.ancestors.include?(client_module)
112
108
 
113
- [:Apns, :Gcm, :Wpns, :Adm].each do |service|
109
+ [:Apns, :Gcm, :Wpns, :Wns, :Adm].each do |service|
114
110
  Rpush.const_set(service, client_module.const_get(service)) unless Rpush.const_defined?(service)
115
111
  end
116
112
 
@@ -30,6 +30,10 @@ require 'rpush/daemon/ring_buffer'
30
30
  require 'rpush/daemon/signal_handler'
31
31
  require 'rpush/daemon/proc_title'
32
32
 
33
+ require 'rpush/daemon/rpc'
34
+ require 'rpush/daemon/rpc/server'
35
+ require 'rpush/daemon/rpc/client'
36
+
33
37
  require 'rpush/daemon/store/interface'
34
38
 
35
39
  require 'rpush/daemon/apns/delivery'
@@ -42,6 +46,9 @@ require 'rpush/daemon/gcm'
42
46
  require 'rpush/daemon/wpns/delivery'
43
47
  require 'rpush/daemon/wpns'
44
48
 
49
+ require 'rpush/daemon/wns/delivery'
50
+ require 'rpush/daemon/wns'
51
+
45
52
  require 'rpush/daemon/adm/delivery'
46
53
  require 'rpush/daemon/adm'
47
54
 
@@ -57,6 +64,7 @@ module Rpush
57
64
  SignalHandler.start
58
65
  common_init
59
66
  Synchronizer.sync
67
+ Rpc::Server.start
60
68
 
61
69
  # No further store connections will be made from this thread.
62
70
  store.release_connection
@@ -81,6 +89,7 @@ module Rpush
81
89
  Rpush.logger.info('Shutting down... ', true)
82
90
 
83
91
  shutdown_lock.synchronize do
92
+ Rpc::Server.stop
84
93
  Feeder.stop
85
94
  AppRunner.stop
86
95
  delete_pid_file
@@ -43,6 +43,11 @@ module Rpush
43
43
  @stop = true
44
44
  @interruptible_sleep.stop
45
45
  @thread.join if @thread
46
+ rescue StandardError => e
47
+ log_error(e)
48
+ reflect(:error, e)
49
+ ensure
50
+ @thread = nil
46
51
  end
47
52
 
48
53
  def check_for_feedback
@@ -84,8 +84,8 @@ module Rpush
84
84
  @runners[app.id].increment_dispatchers(num)
85
85
  end
86
86
 
87
- def self.debug
88
- @runners.values.map(&:debug)
87
+ def self.status
88
+ { app_runners: @runners.values.map(&:status) }
89
89
  end
90
90
 
91
91
  attr_reader :app
@@ -140,7 +140,7 @@ module Rpush
140
140
  num.times { @dispatcher_loops.push(new_dispatcher_loop) }
141
141
  end
142
142
 
143
- def debug
143
+ def status
144
144
  dispatcher_details = {}
145
145
 
146
146
  @dispatcher_loops.each_with_index do |dispatcher_loop, i|
@@ -151,8 +151,7 @@ module Rpush
151
151
  }
152
152
  end
153
153
 
154
- runner_details = { dispatchers: dispatcher_details, queued: queue_size }
155
- log_info(JSON.pretty_generate(runner_details))
154
+ { app_name: @app.name, dispatchers: dispatcher_details, queued: queue_size }
156
155
  end
157
156
 
158
157
  def num_dispatcher_loops
@@ -16,6 +16,7 @@ module Rpush
16
16
  6 => 'Missing topic size',
17
17
  7 => 'Missing payload size',
18
18
  8 => 'Invalid token',
19
+ 10 => 'APNs closed connection (possible maintenance)',
19
20
  255 => 'None (unknown error)'
20
21
  }
21
22
 
@@ -34,9 +35,21 @@ module Rpush
34
35
  end
35
36
 
36
37
  def cleanup
38
+ if Rpush.config.push
39
+ # In push mode only a single batch is sent, followed my immediate shutdown.
40
+ # Allow the error receiver time to handle any errors.
41
+ @reconnect_disabled = true
42
+ sleep 1
43
+ end
44
+
37
45
  @stop_error_receiver = true
38
46
  super
39
47
  @error_receiver_thread.join if @error_receiver_thread
48
+ rescue StandardError => e
49
+ log_error(e)
50
+ reflect(:error, e)
51
+ ensure
52
+ @error_receiver_thread = nil
40
53
  end
41
54
 
42
55
  private
@@ -63,12 +76,12 @@ module Rpush
63
76
  # On Linux, select returns nil from a dropped connection.
64
77
  # On OS X, Errno::EBADF is raised following a Errno::EADDRNOTAVAIL from the write call.
65
78
  return unless @connection.select(SELECT_TIMEOUT)
66
- rescue SystemCallError, IOError
67
- # Connection closed.
79
+ tuple = @connection.read(ERROR_TUPLE_BYTES)
80
+ rescue *TcpConnection::TCP_ERRORS
81
+ reconnect unless @stop_error_receiver
68
82
  return
69
83
  end
70
84
 
71
- tuple = @connection.read(ERROR_TUPLE_BYTES)
72
85
  @dispatch_mutex.synchronize { handle_error_response(tuple) }
73
86
  rescue StandardError => e
74
87
  log_error(e)
@@ -82,12 +95,23 @@ module Rpush
82
95
  handle_disconnect
83
96
  end
84
97
 
85
- log_error("Lost connection to #{@connection.host}:#{@connection.port}, reconnecting...")
86
- @connection.reconnect_with_rescue
98
+ if Rpush.config.push
99
+ # Only attempt to handle a single error in Push mode.
100
+ @stop_error_receiver = true
101
+ return
102
+ end
103
+
104
+ reconnect
87
105
  ensure
88
106
  delivered_buffer.clear
89
107
  end
90
108
 
109
+ def reconnect
110
+ return if @reconnect_disabled
111
+ log_error("Lost connection to #{@connection.host}:#{@connection.port}, reconnecting...")
112
+ @connection.reconnect_with_rescue
113
+ end
114
+
91
115
  def handle_disconnect
92
116
  log_error("The APNs disconnected before any notifications could be delivered. This usually indicates you are using an invalid certificate.") if delivered_buffer.size == 0
93
117
  end
@@ -95,22 +119,33 @@ module Rpush
95
119
  def handle_error(code, notification_id)
96
120
  notification_id = Rpush::Daemon.store.translate_integer_notification_id(notification_id)
97
121
  failed_pos = delivered_buffer.index(notification_id)
98
- description = APNS_ERRORS[code.to_i] || "Unknown error code #{code.inspect}. Possible Rpush bug?"
99
- log_error(description + " (#{code})")
122
+ description = description_for_code(code)
123
+ log_error("Notification #{notification_id} failed with error: " + description)
100
124
  Rpush::Daemon.store.mark_ids_failed([notification_id], code, description, Time.now)
101
125
  reflect(:notification_id_failed, @app, notification_id, code, description)
102
126
 
103
127
  if failed_pos
104
128
  retry_ids = delivered_buffer[(failed_pos + 1)..-1]
105
- if retry_ids.size > 0
106
- now = Time.now
107
- Rpush::Daemon.store.mark_ids_retryable(retry_ids, now)
108
- retry_ids.each { |id| reflect(:notification_id_will_retry, @app, id, now) }
109
- end
129
+ retry_notification_ids(retry_ids, notification_id)
110
130
  elsif delivered_buffer.size > 0
111
131
  log_error("Delivery sequence unknown for notifications following #{notification_id}.")
112
132
  end
113
133
  end
134
+
135
+ def description_for_code(code)
136
+ APNS_ERRORS[code.to_i] ? "#{APNS_ERRORS[code.to_i]} (#{code})" : "Unknown error code #{code.inspect}. Possible Rpush bug?"
137
+ end
138
+
139
+ def retry_notification_ids(ids, notification_id)
140
+ return if ids.size == 0
141
+
142
+ now = Time.now
143
+ Rpush::Daemon.store.mark_ids_retryable(ids, now)
144
+ notifications_str = 'Notification'
145
+ notifications_str += 's' if ids.size > 1
146
+ log_warn("#{notifications_str} #{ids.join(', ')} will be retried due to the failure of notification #{notification_id}.")
147
+ ids.each { |id| reflect(:notification_id_will_retry, @app, id, now) }
148
+ end
114
149
  end
115
150
  end
116
151
  end
@@ -44,6 +44,11 @@ module Rpush
44
44
  @queue.push([STOP, object_id]) if @thread
45
45
  @thread.join if @thread
46
46
  @dispatcher.cleanup
47
+ rescue StandardError => e
48
+ log_error(e)
49
+ reflect(:error, e)
50
+ ensure
51
+ @thread = nil
47
52
  end
48
53
 
49
54
  private
@@ -2,6 +2,7 @@ module Rpush
2
2
  module Daemon
3
3
  class Feeder
4
4
  extend Reflectable
5
+ extend Loggable
5
6
 
6
7
  def self.start(push_mode = false)
7
8
  self.should_stop = false
@@ -12,12 +13,22 @@ module Rpush
12
13
  end
13
14
 
14
15
  @thread.join
16
+ rescue StandardError => e
17
+ log_error(e)
18
+ reflect(:error, e)
19
+ ensure
20
+ @thread = nil
15
21
  end
16
22
 
17
23
  def self.stop
18
24
  self.should_stop = true
19
25
  interruptible_sleeper.stop
20
26
  @thread.join if @thread
27
+ rescue StandardError => e
28
+ log_error(e)
29
+ reflect(:error, e)
30
+ ensure
31
+ @thread = nil
21
32
  end
22
33
 
23
34
  def self.wakeup
@@ -5,7 +5,7 @@ module Rpush
5
5
  class Delivery < Rpush::Daemon::Delivery
6
6
  include MultiJsonHelper
7
7
 
8
- host = 'https://android.googleapis.com'
8
+ host = 'https://gcm-http.googleapis.com'
9
9
  GCM_URI = URI.parse("#{host}/gcm/send")
10
10
  UNAVAILABLE_STATES = %w(Unavailable InternalServerError)
11
11
  INVALID_REGISTRATION_ID_STATES = %w(InvalidRegistration MismatchSenderId NotRegistered InvalidPackageName)
@@ -44,7 +44,7 @@ module Rpush
44
44
  when 503
45
45
  service_unavailable(response)
46
46
  else
47
- fail Rpush::DeliveryError.new(response.code, @notification.id, Rpush::Daemon::HTTP_STATUS_CODES[response.code.to_i])
47
+ fail Rpush::DeliveryError.new(response.code.to_i, @notification.id, Rpush::Daemon::HTTP_STATUS_CODES[response.code.to_i])
48
48
  end
49
49
  end
50
50
 
@@ -1,17 +1,22 @@
1
- require 'monitor'
2
-
3
1
  module Rpush
4
2
  module Daemon
5
3
  class InterruptibleSleep
6
4
  def sleep(duration)
7
5
  @thread = Thread.new { Kernel.sleep duration }
8
6
  Thread.pass
9
- @thread.join
7
+
8
+ begin
9
+ @thread.join
10
+ rescue StandardError
11
+ @thread = nil
12
+ end
10
13
  end
11
14
 
12
15
  def stop
13
16
  @thread.kill if @thread
14
17
  rescue StandardError # rubocop:disable Lint/HandleExceptions
18
+ ensure
19
+ @thread = nil
15
20
  end
16
21
  end
17
22
  end
@@ -1,6 +1,10 @@
1
1
  module Rpush
2
2
  module Daemon
3
3
  module Loggable
4
+ def log_debug(msg)
5
+ Rpush.logger.debug(app_prefix(msg))
6
+ end
7
+
4
8
  def log_info(msg)
5
9
  Rpush.logger.info(app_prefix(msg))
6
10
  end