rpush 1.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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +99 -0
- data/LICENSE +7 -0
- data/README.md +189 -0
- data/bin/rpush +36 -0
- data/config/database.yml +44 -0
- data/lib/generators/rpush_generator.rb +44 -0
- data/lib/generators/templates/add_adm.rb +23 -0
- data/lib/generators/templates/add_alert_is_json_to_rapns_notifications.rb +9 -0
- data/lib/generators/templates/add_app_to_rapns.rb +11 -0
- data/lib/generators/templates/add_fail_after_to_rpush_notifications.rb +9 -0
- data/lib/generators/templates/add_gcm.rb +102 -0
- data/lib/generators/templates/add_rpush.rb +349 -0
- data/lib/generators/templates/add_wpns.rb +16 -0
- data/lib/generators/templates/create_rapns_apps.rb +16 -0
- data/lib/generators/templates/create_rapns_feedback.rb +18 -0
- data/lib/generators/templates/create_rapns_notifications.rb +29 -0
- data/lib/generators/templates/rename_rapns_to_rpush.rb +63 -0
- data/lib/generators/templates/rpush.rb +104 -0
- data/lib/rpush/TODO +3 -0
- data/lib/rpush/adm/app.rb +15 -0
- data/lib/rpush/adm/data_validator.rb +11 -0
- data/lib/rpush/adm/notification.rb +29 -0
- data/lib/rpush/apns/app.rb +29 -0
- data/lib/rpush/apns/binary_notification_validator.rb +12 -0
- data/lib/rpush/apns/device_token_format_validator.rb +12 -0
- data/lib/rpush/apns/feedback.rb +16 -0
- data/lib/rpush/apns/notification.rb +84 -0
- data/lib/rpush/apns_feedback.rb +13 -0
- data/lib/rpush/app.rb +18 -0
- data/lib/rpush/configuration.rb +75 -0
- data/lib/rpush/daemon/adm/delivery.rb +222 -0
- data/lib/rpush/daemon/adm.rb +9 -0
- data/lib/rpush/daemon/apns/certificate_expired_error.rb +20 -0
- data/lib/rpush/daemon/apns/delivery.rb +64 -0
- data/lib/rpush/daemon/apns/disconnection_error.rb +20 -0
- data/lib/rpush/daemon/apns/feedback_receiver.rb +79 -0
- data/lib/rpush/daemon/apns.rb +16 -0
- data/lib/rpush/daemon/app_runner.rb +187 -0
- data/lib/rpush/daemon/batch.rb +115 -0
- data/lib/rpush/daemon/constants.rb +59 -0
- data/lib/rpush/daemon/delivery.rb +28 -0
- data/lib/rpush/daemon/delivery_error.rb +19 -0
- data/lib/rpush/daemon/dispatcher/http.rb +21 -0
- data/lib/rpush/daemon/dispatcher/tcp.rb +30 -0
- data/lib/rpush/daemon/dispatcher_loop.rb +54 -0
- data/lib/rpush/daemon/dispatcher_loop_collection.rb +33 -0
- data/lib/rpush/daemon/feeder.rb +68 -0
- data/lib/rpush/daemon/gcm/delivery.rb +222 -0
- data/lib/rpush/daemon/gcm.rb +9 -0
- data/lib/rpush/daemon/interruptible_sleep.rb +61 -0
- data/lib/rpush/daemon/loggable.rb +31 -0
- data/lib/rpush/daemon/reflectable.rb +13 -0
- data/lib/rpush/daemon/retry_header_parser.rb +23 -0
- data/lib/rpush/daemon/retryable_error.rb +20 -0
- data/lib/rpush/daemon/service_config_methods.rb +33 -0
- data/lib/rpush/daemon/store/active_record/reconnectable.rb +68 -0
- data/lib/rpush/daemon/store/active_record.rb +154 -0
- data/lib/rpush/daemon/tcp_connection.rb +143 -0
- data/lib/rpush/daemon/too_many_requests_error.rb +20 -0
- data/lib/rpush/daemon/wpns/delivery.rb +132 -0
- data/lib/rpush/daemon/wpns.rb +9 -0
- data/lib/rpush/daemon.rb +140 -0
- data/lib/rpush/deprecatable.rb +23 -0
- data/lib/rpush/deprecation.rb +23 -0
- data/lib/rpush/embed.rb +28 -0
- data/lib/rpush/gcm/app.rb +11 -0
- data/lib/rpush/gcm/expiry_collapse_key_mutual_inclusion_validator.rb +11 -0
- data/lib/rpush/gcm/notification.rb +30 -0
- data/lib/rpush/logger.rb +63 -0
- data/lib/rpush/multi_json_helper.rb +16 -0
- data/lib/rpush/notification.rb +69 -0
- data/lib/rpush/notifier.rb +52 -0
- data/lib/rpush/payload_data_size_validator.rb +10 -0
- data/lib/rpush/push.rb +16 -0
- data/lib/rpush/railtie.rb +11 -0
- data/lib/rpush/reflection.rb +58 -0
- data/lib/rpush/registration_ids_count_validator.rb +10 -0
- data/lib/rpush/version.rb +3 -0
- data/lib/rpush/wpns/app.rb +9 -0
- data/lib/rpush/wpns/notification.rb +26 -0
- data/lib/rpush.rb +62 -0
- data/lib/tasks/cane.rake +18 -0
- data/lib/tasks/rpush.rake +16 -0
- data/lib/tasks/test.rake +38 -0
- data/spec/functional/adm_spec.rb +43 -0
- data/spec/functional/apns_spec.rb +58 -0
- data/spec/functional/embed_spec.rb +49 -0
- data/spec/functional/gcm_spec.rb +42 -0
- data/spec/functional/wpns_spec.rb +41 -0
- data/spec/support/cert_with_password.pem +90 -0
- data/spec/support/cert_without_password.pem +59 -0
- data/spec/support/install.sh +32 -0
- data/spec/support/simplecov_helper.rb +20 -0
- data/spec/support/simplecov_quality_formatter.rb +8 -0
- data/spec/tmp/.gitkeep +0 -0
- data/spec/unit/adm/app_spec.rb +58 -0
- data/spec/unit/adm/notification_spec.rb +45 -0
- data/spec/unit/apns/app_spec.rb +29 -0
- data/spec/unit/apns/feedback_spec.rb +9 -0
- data/spec/unit/apns/notification_spec.rb +208 -0
- data/spec/unit/apns_feedback_spec.rb +21 -0
- data/spec/unit/app_spec.rb +30 -0
- data/spec/unit/configuration_spec.rb +45 -0
- data/spec/unit/daemon/adm/delivery_spec.rb +243 -0
- data/spec/unit/daemon/apns/certificate_expired_error_spec.rb +11 -0
- data/spec/unit/daemon/apns/delivery_spec.rb +101 -0
- data/spec/unit/daemon/apns/disconnection_error_spec.rb +18 -0
- data/spec/unit/daemon/apns/feedback_receiver_spec.rb +117 -0
- data/spec/unit/daemon/app_runner_spec.rb +292 -0
- data/spec/unit/daemon/batch_spec.rb +232 -0
- data/spec/unit/daemon/delivery_error_spec.rb +13 -0
- data/spec/unit/daemon/delivery_spec.rb +38 -0
- data/spec/unit/daemon/dispatcher/http_spec.rb +33 -0
- data/spec/unit/daemon/dispatcher/tcp_spec.rb +38 -0
- data/spec/unit/daemon/dispatcher_loop_collection_spec.rb +37 -0
- data/spec/unit/daemon/dispatcher_loop_spec.rb +71 -0
- data/spec/unit/daemon/feeder_spec.rb +98 -0
- data/spec/unit/daemon/gcm/delivery_spec.rb +310 -0
- data/spec/unit/daemon/interruptible_sleep_spec.rb +68 -0
- data/spec/unit/daemon/reflectable_spec.rb +27 -0
- data/spec/unit/daemon/retryable_error_spec.rb +14 -0
- data/spec/unit/daemon/service_config_methods_spec.rb +33 -0
- data/spec/unit/daemon/store/active_record/reconnectable_spec.rb +114 -0
- data/spec/unit/daemon/store/active_record_spec.rb +357 -0
- data/spec/unit/daemon/tcp_connection_spec.rb +287 -0
- data/spec/unit/daemon/too_many_requests_error_spec.rb +14 -0
- data/spec/unit/daemon/wpns/delivery_spec.rb +159 -0
- data/spec/unit/daemon_spec.rb +159 -0
- data/spec/unit/deprecatable_spec.rb +32 -0
- data/spec/unit/deprecation_spec.rb +15 -0
- data/spec/unit/embed_spec.rb +50 -0
- data/spec/unit/gcm/app_spec.rb +4 -0
- data/spec/unit/gcm/notification_spec.rb +36 -0
- data/spec/unit/logger_spec.rb +127 -0
- data/spec/unit/notification_shared.rb +105 -0
- data/spec/unit/notification_spec.rb +15 -0
- data/spec/unit/notifier_spec.rb +49 -0
- data/spec/unit/push_spec.rb +43 -0
- data/spec/unit/reflection_spec.rb +30 -0
- data/spec/unit/rpush_spec.rb +9 -0
- data/spec/unit/wpns/app_spec.rb +4 -0
- data/spec/unit/wpns/notification_spec.rb +30 -0
- data/spec/unit_spec_helper.rb +101 -0
- metadata +304 -0
@@ -0,0 +1,16 @@
|
|
1
|
+
module Rpush
|
2
|
+
module Daemon
|
3
|
+
module Apns
|
4
|
+
extend ServiceConfigMethods
|
5
|
+
|
6
|
+
HOSTS = {
|
7
|
+
:production => ['gateway.push.apple.com', 2195],
|
8
|
+
:development => ['gateway.sandbox.push.apple.com', 2195], # deprecated
|
9
|
+
:sandbox => ['gateway.sandbox.push.apple.com', 2195]
|
10
|
+
}
|
11
|
+
|
12
|
+
dispatcher :tcp, :host => Proc.new { |app| HOSTS[app.environment.to_sym] }
|
13
|
+
loops Rpush::Daemon::Apns::FeedbackReceiver
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,187 @@
|
|
1
|
+
module Rpush
|
2
|
+
module Daemon
|
3
|
+
class AppRunner
|
4
|
+
extend Reflectable
|
5
|
+
include Reflectable
|
6
|
+
include Loggable
|
7
|
+
|
8
|
+
class << self
|
9
|
+
attr_reader :runners
|
10
|
+
end
|
11
|
+
|
12
|
+
@runners = {}
|
13
|
+
|
14
|
+
def self.enqueue(notifications)
|
15
|
+
notifications.group_by(&:app_id).each do |app_id, group|
|
16
|
+
batch = Batch.new(group)
|
17
|
+
if app = runners[app_id]
|
18
|
+
app.enqueue(batch)
|
19
|
+
else
|
20
|
+
Rpush.logger.error("No such app '#{app_id}' for notifications #{batch.describe}.")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.sync
|
26
|
+
apps = Rpush::App.all
|
27
|
+
apps.each { |app| sync_app(app) }
|
28
|
+
removed = runners.keys - apps.map(&:id)
|
29
|
+
removed.each { |app_id| runners.delete(app_id).stop }
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.sync_app(app)
|
33
|
+
if runners[app.id]
|
34
|
+
runners[app.id].sync(app)
|
35
|
+
else
|
36
|
+
runner = new(app)
|
37
|
+
begin
|
38
|
+
runner.start
|
39
|
+
runners[app.id] = runner
|
40
|
+
rescue StandardError => e
|
41
|
+
Rpush.logger.error("[#{app.name}] Exception raised during startup. Notifications will not be delivered for this app.")
|
42
|
+
Rpush.logger.error(e)
|
43
|
+
reflect(:error, e)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.stop
|
49
|
+
runners.values.map(&:stop)
|
50
|
+
runners.clear
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.debug
|
54
|
+
runners.values.map(&:debug)
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.idle
|
58
|
+
runners.values.select(&:idle?)
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.wait
|
62
|
+
sleep 0.1 while !runners.values.all?(&:idle?)
|
63
|
+
end
|
64
|
+
|
65
|
+
attr_reader :app
|
66
|
+
attr_accessor :batch
|
67
|
+
|
68
|
+
def initialize(app)
|
69
|
+
@app = app
|
70
|
+
@loops = []
|
71
|
+
end
|
72
|
+
|
73
|
+
def start
|
74
|
+
app.connections.times { dispatchers.push(new_dispatcher_loop) }
|
75
|
+
start_loops
|
76
|
+
log_info("Started, #{dispatchers_str}.")
|
77
|
+
end
|
78
|
+
|
79
|
+
def stop
|
80
|
+
dispatchers.stop
|
81
|
+
stop_loops
|
82
|
+
end
|
83
|
+
|
84
|
+
def enqueue(batch)
|
85
|
+
self.batch = batch
|
86
|
+
batch.notifications.each do |notification|
|
87
|
+
queue.push([notification, batch])
|
88
|
+
reflect(:notification_enqueued, notification)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def sync(app)
|
93
|
+
@app = app
|
94
|
+
diff = dispatchers.size - app.connections
|
95
|
+
return if diff == 0
|
96
|
+
if diff > 0
|
97
|
+
decrement_dispatchers(diff)
|
98
|
+
log_info("Stopped #{dispatchers_str(diff)}. #{dispatchers_str} running.")
|
99
|
+
else
|
100
|
+
increment_dispatchers(diff.abs)
|
101
|
+
log_info("Started #{dispatchers_str(diff)}. #{dispatchers_str} running.")
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def decrement_dispatchers(num)
|
106
|
+
num.times { dispatchers.pop }
|
107
|
+
end
|
108
|
+
|
109
|
+
def increment_dispatchers(num)
|
110
|
+
num.times { dispatchers.push(new_dispatcher_loop) }
|
111
|
+
end
|
112
|
+
|
113
|
+
def debug
|
114
|
+
Rpush.logger.info <<-EOS
|
115
|
+
|
116
|
+
#{@app.name}:
|
117
|
+
dispatchers: #{num_dispatchers}
|
118
|
+
queued: #{queue_size}
|
119
|
+
batch size: #{batch_size}
|
120
|
+
batch processed: #{batch_processed}
|
121
|
+
idle: #{idle?}
|
122
|
+
EOS
|
123
|
+
end
|
124
|
+
|
125
|
+
def idle?
|
126
|
+
batch ? batch.complete? : true
|
127
|
+
end
|
128
|
+
|
129
|
+
def queue_size
|
130
|
+
queue.size
|
131
|
+
end
|
132
|
+
|
133
|
+
def batch_size
|
134
|
+
batch ? batch.num_notifications : 0
|
135
|
+
end
|
136
|
+
|
137
|
+
def batch_processed
|
138
|
+
batch ? batch.num_processed : 0
|
139
|
+
end
|
140
|
+
|
141
|
+
def num_dispatchers
|
142
|
+
dispatchers.size
|
143
|
+
end
|
144
|
+
|
145
|
+
protected
|
146
|
+
|
147
|
+
def start_loops
|
148
|
+
service_module.loops.each do |loop_class|
|
149
|
+
instance = loop_class.new(@app)
|
150
|
+
instance.start
|
151
|
+
@loops << instance
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def stop_loops
|
156
|
+
@loops.map(&:stop)
|
157
|
+
@loops = []
|
158
|
+
end
|
159
|
+
|
160
|
+
def new_dispatcher_loop
|
161
|
+
dispatcher = service_module.new_dispatcher(@app)
|
162
|
+
dispatcher_loop = Rpush::Daemon::DispatcherLoop.new(queue, dispatcher)
|
163
|
+
dispatcher_loop.start
|
164
|
+
dispatcher_loop
|
165
|
+
end
|
166
|
+
|
167
|
+
def service_module
|
168
|
+
return @service_module if defined? @service_module
|
169
|
+
@service_module = "Rpush::Daemon::#{@app.service_name.camelize}".constantize
|
170
|
+
end
|
171
|
+
|
172
|
+
def queue
|
173
|
+
@queue ||= Queue.new
|
174
|
+
end
|
175
|
+
|
176
|
+
def dispatchers
|
177
|
+
@dispatchers ||= Rpush::Daemon::DispatcherLoopCollection.new
|
178
|
+
end
|
179
|
+
|
180
|
+
def dispatchers_str(count = app.connections)
|
181
|
+
count = count.abs
|
182
|
+
str = count == 1 ? 'dispatcher' : 'dispatchers'
|
183
|
+
"#{count} #{str}"
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
module Rpush
|
2
|
+
module Daemon
|
3
|
+
class Batch
|
4
|
+
include Reflectable
|
5
|
+
|
6
|
+
attr_reader :num_processed, :notifications,
|
7
|
+
:delivered, :failed, :retryable
|
8
|
+
|
9
|
+
def initialize(notifications)
|
10
|
+
@notifications = notifications
|
11
|
+
@num_processed = 0
|
12
|
+
@delivered = []
|
13
|
+
@failed = {}
|
14
|
+
@retryable = {}
|
15
|
+
@mutex = Mutex.new
|
16
|
+
end
|
17
|
+
|
18
|
+
def num_notifications
|
19
|
+
@notifications.size
|
20
|
+
end
|
21
|
+
|
22
|
+
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
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
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)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
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)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def notification_dispatched
|
56
|
+
@mutex.synchronize do
|
57
|
+
@num_processed += 1
|
58
|
+
complete if @num_processed >= @notifications.size
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def complete?
|
63
|
+
@complete == true
|
64
|
+
end
|
65
|
+
|
66
|
+
def describe
|
67
|
+
notifications.map(&:id).join(', ')
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def complete
|
73
|
+
[:complete_delivered, :complete_failed, :complete_retried].each do |method|
|
74
|
+
begin
|
75
|
+
send(method)
|
76
|
+
rescue StandardError => e
|
77
|
+
Rpush.logger.error(e)
|
78
|
+
reflect(:error, e)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
notifications.clear
|
83
|
+
@complete = true
|
84
|
+
end
|
85
|
+
|
86
|
+
def complete_delivered
|
87
|
+
Rpush::Daemon.store.mark_batch_delivered(delivered)
|
88
|
+
delivered.each do |notification|
|
89
|
+
reflect(:notification_delivered, notification)
|
90
|
+
end
|
91
|
+
delivered.clear
|
92
|
+
end
|
93
|
+
|
94
|
+
def complete_failed
|
95
|
+
failed.each do |(code, description), notifications|
|
96
|
+
Rpush::Daemon.store.mark_batch_failed(notifications, code, description)
|
97
|
+
notifications.each do |notification|
|
98
|
+
reflect(:notification_failed, notification)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
failed.clear
|
102
|
+
end
|
103
|
+
|
104
|
+
def complete_retried
|
105
|
+
retryable.each do |deliver_after, notifications|
|
106
|
+
Rpush::Daemon.store.mark_batch_retryable(notifications, deliver_after)
|
107
|
+
notifications.each do |notification|
|
108
|
+
reflect(:notification_will_retry, notification)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
retryable.clear
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Rpush
|
2
|
+
module Daemon
|
3
|
+
HTTP_STATUS_CODES = {
|
4
|
+
100 => 'Continue',
|
5
|
+
101 => 'Switching Protocols',
|
6
|
+
102 => 'Processing',
|
7
|
+
200 => 'OK',
|
8
|
+
201 => 'Created',
|
9
|
+
202 => 'Accepted',
|
10
|
+
203 => 'Non-Authoritative Information',
|
11
|
+
204 => 'No Content',
|
12
|
+
205 => 'Reset Content',
|
13
|
+
206 => 'Partial Content',
|
14
|
+
207 => 'Multi-Status',
|
15
|
+
226 => 'IM Used',
|
16
|
+
300 => 'Multiple Choices',
|
17
|
+
301 => 'Moved Permanently',
|
18
|
+
302 => 'Found',
|
19
|
+
303 => 'See Other',
|
20
|
+
304 => 'Not Modified',
|
21
|
+
305 => 'Use Proxy',
|
22
|
+
306 => 'Reserved',
|
23
|
+
307 => 'Temporary Redirect',
|
24
|
+
400 => 'Bad Request',
|
25
|
+
401 => 'Unauthorized',
|
26
|
+
402 => 'Payment Required',
|
27
|
+
403 => 'Forbidden',
|
28
|
+
404 => 'Not Found',
|
29
|
+
405 => 'Method Not Allowed',
|
30
|
+
406 => 'Not Acceptable',
|
31
|
+
407 => 'Proxy Authentication Required',
|
32
|
+
408 => 'Request Timeout',
|
33
|
+
409 => 'Conflict',
|
34
|
+
410 => 'Gone',
|
35
|
+
411 => 'Length Required',
|
36
|
+
412 => 'Precondition Failed',
|
37
|
+
413 => 'Request Entity Too Large',
|
38
|
+
414 => 'Request-URI Too Long',
|
39
|
+
415 => 'Unsupported Media Type',
|
40
|
+
416 => 'Requested Range Not Satisfiable',
|
41
|
+
417 => 'Expectation Failed',
|
42
|
+
418 => "I'm a Teapot",
|
43
|
+
422 => 'Unprocessable Entity',
|
44
|
+
423 => 'Locked',
|
45
|
+
424 => 'Failed Dependency',
|
46
|
+
426 => 'Upgrade Required',
|
47
|
+
429 => 'Too Many Requests',
|
48
|
+
500 => 'Internal Server Error',
|
49
|
+
501 => 'Not Implemented',
|
50
|
+
502 => 'Bad Gateway',
|
51
|
+
503 => 'Service Unavailable',
|
52
|
+
504 => 'Gateway Timeout',
|
53
|
+
505 => 'HTTP Version Not Supported',
|
54
|
+
506 => 'Variant Also Negotiates',
|
55
|
+
507 => 'Insufficient Storage',
|
56
|
+
510 => 'Not Extended'
|
57
|
+
}
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Rpush
|
2
|
+
module Daemon
|
3
|
+
class Delivery
|
4
|
+
include Reflectable
|
5
|
+
include Loggable
|
6
|
+
|
7
|
+
def mark_retryable(notification, deliver_after)
|
8
|
+
if notification.fail_after && notification.fail_after < Time.now
|
9
|
+
@batch.mark_failed(notification, nil, "Notification failed to be delivered before #{notification.fail_after.strftime("%Y-%m-%d %H:%M:%S")}.")
|
10
|
+
else
|
11
|
+
@batch.mark_retryable(notification, deliver_after)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def mark_retryable_exponential(notification)
|
16
|
+
mark_retryable(notification, Time.now + 2 ** (notification.retries + 1))
|
17
|
+
end
|
18
|
+
|
19
|
+
def mark_delivered
|
20
|
+
@batch.mark_delivered(@notification)
|
21
|
+
end
|
22
|
+
|
23
|
+
def mark_failed(code, description)
|
24
|
+
@batch.mark_failed(@notification, code, description)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Rpush
|
2
|
+
class DeliveryError < StandardError
|
3
|
+
attr_reader :code, :description
|
4
|
+
|
5
|
+
def initialize(code, notification_id, description)
|
6
|
+
@code = code
|
7
|
+
@notification_id = notification_id
|
8
|
+
@description = description
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_s
|
12
|
+
message
|
13
|
+
end
|
14
|
+
|
15
|
+
def message
|
16
|
+
"Unable to deliver notification #{@notification_id}, received error #{@code} (#{@description})"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Rpush
|
2
|
+
module Daemon
|
3
|
+
module Dispatcher
|
4
|
+
class Http
|
5
|
+
def initialize(app, delivery_class, options = {})
|
6
|
+
@app = app
|
7
|
+
@delivery_class = delivery_class
|
8
|
+
@http = Net::HTTP::Persistent.new('rpush')
|
9
|
+
end
|
10
|
+
|
11
|
+
def dispatch(notification, batch)
|
12
|
+
@delivery_class.new(@app, @http, notification, batch).perform
|
13
|
+
end
|
14
|
+
|
15
|
+
def cleanup
|
16
|
+
@http.shutdown
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Rpush
|
2
|
+
module Daemon
|
3
|
+
module Dispatcher
|
4
|
+
class Tcp
|
5
|
+
def initialize(app, delivery_class, options = {})
|
6
|
+
@app = app
|
7
|
+
@delivery_class = delivery_class
|
8
|
+
@host, @port = options[:host].call(@app)
|
9
|
+
end
|
10
|
+
|
11
|
+
def dispatch(notification, batch)
|
12
|
+
@delivery_class.new(@app, connection, notification, batch).perform
|
13
|
+
end
|
14
|
+
|
15
|
+
def cleanup
|
16
|
+
@connection.close if @connection
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def connection
|
22
|
+
return @connection if defined? @connection
|
23
|
+
connection = Rpush::Daemon::TcpConnection.new(@app, @host, @port)
|
24
|
+
connection.connect
|
25
|
+
@connection = connection
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Rpush
|
2
|
+
module Daemon
|
3
|
+
class DispatcherLoop
|
4
|
+
include Reflectable
|
5
|
+
|
6
|
+
WAKEUP = :wakeup
|
7
|
+
|
8
|
+
def initialize(queue, dispatcher)
|
9
|
+
@queue = queue
|
10
|
+
@dispatcher = dispatcher
|
11
|
+
end
|
12
|
+
|
13
|
+
def start
|
14
|
+
@thread = Thread.new do
|
15
|
+
loop do
|
16
|
+
dispatch
|
17
|
+
break if @stop
|
18
|
+
end
|
19
|
+
|
20
|
+
Rpush::Daemon.store.release_connection
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def stop
|
25
|
+
@stop = true
|
26
|
+
end
|
27
|
+
|
28
|
+
def wakeup
|
29
|
+
@queue.push(WAKEUP) if @thread
|
30
|
+
end
|
31
|
+
|
32
|
+
def wait
|
33
|
+
@thread.join if @thread
|
34
|
+
@dispatcher.cleanup
|
35
|
+
end
|
36
|
+
|
37
|
+
protected
|
38
|
+
|
39
|
+
def dispatch
|
40
|
+
notification, batch = @queue.pop
|
41
|
+
return if notification == WAKEUP
|
42
|
+
|
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
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Rpush
|
2
|
+
module Daemon
|
3
|
+
class DispatcherLoopCollection
|
4
|
+
attr_reader :loops
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@loops = []
|
8
|
+
end
|
9
|
+
|
10
|
+
def push(dispatcher_loop)
|
11
|
+
@loops << dispatcher_loop
|
12
|
+
end
|
13
|
+
|
14
|
+
def pop
|
15
|
+
dispatcher_loop = @loops.pop
|
16
|
+
dispatcher_loop.stop
|
17
|
+
dispatcher_loop.wakeup
|
18
|
+
@loops.map(&:wakeup)
|
19
|
+
dispatcher_loop.wait
|
20
|
+
end
|
21
|
+
|
22
|
+
def size
|
23
|
+
@loops.size
|
24
|
+
end
|
25
|
+
|
26
|
+
def stop
|
27
|
+
@loops.map(&:stop)
|
28
|
+
@loops.map(&:wakeup)
|
29
|
+
@loops.map(&:wait)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Rpush
|
2
|
+
module Daemon
|
3
|
+
class Feeder
|
4
|
+
extend Reflectable
|
5
|
+
|
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
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.stop
|
19
|
+
@stop = true
|
20
|
+
interrupt_sleep
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.interrupt_sleep
|
24
|
+
interruptible_sleeper.interrupt_sleep
|
25
|
+
end
|
26
|
+
|
27
|
+
protected
|
28
|
+
|
29
|
+
def self.feed_forever
|
30
|
+
loop do
|
31
|
+
enqueue_notifications
|
32
|
+
interruptible_sleeper.sleep(Rpush.config.push_poll)
|
33
|
+
break if stop?
|
34
|
+
end
|
35
|
+
|
36
|
+
Rpush::Daemon.store.release_connection
|
37
|
+
end
|
38
|
+
|
39
|
+
# :nocov:
|
40
|
+
def self.stop?
|
41
|
+
@stop
|
42
|
+
end
|
43
|
+
|
44
|
+
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
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.interruptible_sleeper
|
57
|
+
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
|
+
|
64
|
+
@interruptible_sleeper
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|