rpush 2.2.0-java → 2.3.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 +4 -4
- data/CHANGELOG.md +10 -0
- data/README.md +17 -9
- data/lib/generators/rpush_migration_generator.rb +1 -1
- data/lib/rpush.rb +4 -1
- data/lib/rpush/apns_feedback.rb +1 -1
- data/lib/rpush/cli.rb +24 -21
- data/lib/rpush/client/active_model/apns/app.rb +1 -1
- data/lib/rpush/client/active_model/apns/notification.rb +2 -2
- data/lib/rpush/client/active_model/wpns/notification.rb +7 -1
- data/lib/rpush/configuration.rb +32 -22
- data/lib/rpush/daemon.rb +18 -9
- data/lib/rpush/daemon/adm/delivery.rb +4 -1
- data/lib/rpush/daemon/apns/delivery.rb +3 -0
- data/lib/rpush/daemon/apns/feedback_receiver.rb +6 -2
- data/lib/rpush/daemon/app_runner.rb +11 -16
- data/lib/rpush/daemon/batch.rb +9 -0
- data/lib/rpush/daemon/delivery.rb +10 -2
- data/lib/rpush/daemon/delivery_error.rb +3 -3
- data/lib/rpush/daemon/dispatcher/apns_tcp.rb +11 -11
- data/lib/rpush/daemon/dispatcher/tcp.rb +2 -10
- data/lib/rpush/daemon/gcm/delivery.rb +13 -7
- data/lib/rpush/daemon/signal_handler.rb +5 -3
- data/lib/rpush/daemon/store/active_record.rb +14 -0
- data/lib/rpush/daemon/store/interface.rb +2 -1
- data/lib/rpush/daemon/store/redis.rb +3 -0
- data/lib/rpush/daemon/tcp_connection.rb +47 -17
- data/lib/rpush/daemon/wpns/delivery.rb +19 -10
- data/lib/rpush/logger.rb +30 -12
- data/lib/rpush/plugin.rb +44 -0
- data/lib/rpush/push.rb +1 -1
- data/lib/rpush/reflectable.rb +13 -0
- data/lib/rpush/{reflection.rb → reflection_collection.rb} +1 -9
- data/lib/rpush/reflection_public_methods.rb +9 -0
- data/lib/rpush/version.rb +1 -1
- data/lib/tasks/test.rake +6 -4
- data/spec/functional/adm_spec.rb +12 -0
- data/spec/functional/apns_spec.rb +61 -42
- data/spec/functional/gcm_spec.rb +9 -0
- data/spec/functional/retry_spec.rb +1 -1
- data/spec/functional/wpns_spec.rb +44 -11
- data/spec/spec_helper.rb +2 -0
- data/spec/unit/apns_feedback_spec.rb +2 -2
- data/spec/unit/client/active_record/apns/app_spec.rb +2 -2
- data/spec/unit/client/active_record/apns/notification_spec.rb +1 -2
- data/spec/unit/client/active_record/wpns/notification_spec.rb +9 -3
- data/spec/unit/configuration_spec.rb +1 -0
- data/spec/unit/daemon/adm/delivery_spec.rb +0 -1
- data/spec/unit/daemon/apns/feedback_receiver_spec.rb +0 -1
- data/spec/unit/daemon/app_runner_spec.rb +14 -9
- data/spec/unit/daemon/delivery_spec.rb +0 -1
- data/spec/unit/daemon/dispatcher/tcp_spec.rb +0 -7
- data/spec/unit/daemon/gcm/delivery_spec.rb +1 -1
- data/spec/unit/daemon/signal_handler_spec.rb +5 -1
- data/spec/unit/daemon/store/active_record_spec.rb +1 -1
- data/spec/unit/daemon/tcp_connection_spec.rb +18 -18
- data/spec/unit/daemon/wpns/delivery_spec.rb +1 -1
- data/spec/unit/daemon_spec.rb +10 -2
- data/spec/unit/logger_spec.rb +0 -1
- data/spec/unit/plugin_spec.rb +36 -0
- data/spec/unit/push_spec.rb +2 -2
- data/spec/unit/{daemon/reflectable_spec.rb → reflectable_spec.rb} +6 -6
- data/spec/unit/{reflection_spec.rb → reflection_collection_spec.rb} +4 -8
- metadata +34 -36
- data/lib/rpush/daemon/reflectable.rb +0 -11
- data/spec/integration/rpush_spec.rb +0 -13
- data/spec/integration/support/gcm_success_response.json +0 -1
- data/spec/support/install.sh +0 -68
@@ -17,6 +17,9 @@ module Rpush
|
|
17
17
|
|
18
18
|
def perform
|
19
19
|
handle_response(do_post)
|
20
|
+
rescue SocketError => error
|
21
|
+
mark_retryable(@notification, Time.now + 10.seconds, error)
|
22
|
+
raise
|
20
23
|
rescue StandardError => error
|
21
24
|
mark_failed(error)
|
22
25
|
raise
|
@@ -77,7 +80,7 @@ module Rpush
|
|
77
80
|
end
|
78
81
|
|
79
82
|
def retry_message
|
80
|
-
"Notification #{@notification.id} will be retried after #{@notification.deliver_after.strftime(
|
83
|
+
"Notification #{@notification.id} will be retried after #{@notification.deliver_after.strftime('%Y-%m-%d %H:%M:%S')} (retry #{@notification.retries})."
|
81
84
|
end
|
82
85
|
|
83
86
|
def retry_notification(reason)
|
@@ -106,16 +109,22 @@ module Rpush
|
|
106
109
|
end
|
107
110
|
|
108
111
|
def notification_to_xml
|
109
|
-
|
112
|
+
title = clean_param_string(@notification.data['title']) if @notification.data['title'].present?
|
113
|
+
body = clean_param_string(@notification.data['body']) if @notification.data['body'].present?
|
114
|
+
param = clean_param_string(@notification.data['param']) if @notification.data['param'].present?
|
115
|
+
"<?xml version=\"1.0\" encoding=\"utf-8\"?>
|
116
|
+
<wp:Notification xmlns:wp=\"WPNotification\">
|
117
|
+
<wp:Toast>
|
118
|
+
<wp:Text1>#{title}</wp:Text1>
|
119
|
+
<wp:Text2>#{body}</wp:Text2>
|
120
|
+
<wp:Param>#{param}</wp:Param>
|
121
|
+
</wp:Toast>
|
122
|
+
</wp:Notification>"
|
123
|
+
end
|
124
|
+
|
125
|
+
def clean_param_string(string)
|
126
|
+
string.gsub(/&/, "&").gsub(/</, "<") \
|
110
127
|
.gsub(/>/, ">").gsub(/'/, "'").gsub(/"/, """)
|
111
|
-
<<-EOF
|
112
|
-
<?xml version="1.0" encoding="utf-8"?>
|
113
|
-
<wp:Notification xmlns:wp="WPNotification">
|
114
|
-
<wp:Toast>
|
115
|
-
<wp:Text1>#{msg}</wp:Text1>
|
116
|
-
</wp:Toast>
|
117
|
-
</wp:Notification>
|
118
|
-
EOF
|
119
128
|
end
|
120
129
|
end
|
121
130
|
end
|
data/lib/rpush/logger.rb
CHANGED
@@ -1,12 +1,15 @@
|
|
1
1
|
module Rpush
|
2
2
|
class Logger
|
3
|
+
attr_reader :internal_logger
|
4
|
+
|
3
5
|
def initialize
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
6
|
+
if Rpush.config.logger
|
7
|
+
@internal_logger = Rpush.config.logger
|
8
|
+
else
|
9
|
+
@internal_logger = setup_logger(open_logfile)
|
10
|
+
end
|
8
11
|
rescue Errno::ENOENT, Errno::EPERM => e
|
9
|
-
@
|
12
|
+
@internal_logger = nil
|
10
13
|
error(e)
|
11
14
|
error('Logging disabled.')
|
12
15
|
end
|
@@ -23,16 +26,31 @@ module Rpush
|
|
23
26
|
log(:warn, msg, inline, 'WARNING', STDERR)
|
24
27
|
end
|
25
28
|
|
29
|
+
def reopen
|
30
|
+
if Rpush.config.logger
|
31
|
+
Rpush.config.logger.reopen if Rpush.config.logger.respond_to?(:reopen)
|
32
|
+
else
|
33
|
+
@internal_logger.close
|
34
|
+
@internal_logger = setup_logger(open_logfile)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
26
38
|
private
|
27
39
|
|
40
|
+
def open_logfile
|
41
|
+
FileUtils.mkdir_p(File.dirname(Rpush.config.log_file))
|
42
|
+
log = File.open(Rpush.config.log_file, 'a')
|
43
|
+
log.sync = true
|
44
|
+
log
|
45
|
+
end
|
46
|
+
|
28
47
|
def setup_logger(log)
|
29
|
-
if
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
@logger.auto_flushing = auto_flushing
|
48
|
+
if ActiveSupport.const_defined?('BufferedLogger')
|
49
|
+
logger = ActiveSupport::BufferedLogger.new(log, Rpush.config.log_level)
|
50
|
+
logger.auto_flushing = auto_flushing
|
51
|
+
logger
|
34
52
|
else
|
35
|
-
|
53
|
+
ActiveSupport::Logger.new(log, Rpush.config.log_level)
|
36
54
|
end
|
37
55
|
end
|
38
56
|
|
@@ -55,7 +73,7 @@ module Rpush
|
|
55
73
|
formatted_msg << msg
|
56
74
|
|
57
75
|
log_foreground(io, formatted_msg, inline)
|
58
|
-
@
|
76
|
+
@internal_logger.send(where, formatted_msg) if @internal_logger
|
59
77
|
end
|
60
78
|
|
61
79
|
def log_foreground(io, formatted_msg, inline)
|
data/lib/rpush/plugin.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
module Rpush
|
2
|
+
def self.plugin(name)
|
3
|
+
plugins[name] ||= Rpush::Plugin.new(name)
|
4
|
+
plugins[name]
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.plugins
|
8
|
+
@plugins ||= {}
|
9
|
+
end
|
10
|
+
|
11
|
+
class Plugin
|
12
|
+
attr_reader :name, :config, :init_block
|
13
|
+
attr_accessor :url, :description
|
14
|
+
|
15
|
+
def initialize(name)
|
16
|
+
@name = name
|
17
|
+
@url = nil
|
18
|
+
@description = nil
|
19
|
+
@config = OpenStruct.new
|
20
|
+
@reflection_collection = Rpush::ReflectionCollection.new
|
21
|
+
@init_block = -> {}
|
22
|
+
end
|
23
|
+
|
24
|
+
def reflect
|
25
|
+
yield(@reflection_collection)
|
26
|
+
return if Rpush.reflection_stack.include?(@reflection_collection)
|
27
|
+
Rpush.reflection_stack << @reflection_collection
|
28
|
+
end
|
29
|
+
|
30
|
+
def configure
|
31
|
+
yield(@config)
|
32
|
+
Rpush.config.plugin.send("#{@name}=", @config)
|
33
|
+
end
|
34
|
+
|
35
|
+
def init(&block) # rubocop:disable Style/TrivialAccessors
|
36
|
+
@init_block = block
|
37
|
+
end
|
38
|
+
|
39
|
+
def unload
|
40
|
+
Rpush.reflection_stack.delete(@reflection_collection)
|
41
|
+
Rpush.config.plugin.send("#{name}=", nil)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/lib/rpush/push.rb
CHANGED
@@ -0,0 +1,13 @@
|
|
1
|
+
module Rpush
|
2
|
+
module Reflectable
|
3
|
+
def reflect(name, *args)
|
4
|
+
Rpush.reflection_stack.each do |reflection_collection|
|
5
|
+
begin
|
6
|
+
reflection_collection.__dispatch(name, *args)
|
7
|
+
rescue StandardError => e
|
8
|
+
Rpush.logger.error(e)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -1,13 +1,5 @@
|
|
1
1
|
module Rpush
|
2
|
-
|
3
|
-
yield reflections if block_given?
|
4
|
-
end
|
5
|
-
|
6
|
-
def self.reflections
|
7
|
-
@reflections ||= Reflections.new
|
8
|
-
end
|
9
|
-
|
10
|
-
class Reflections
|
2
|
+
class ReflectionCollection
|
11
3
|
class NoSuchReflectionError < StandardError; end
|
12
4
|
|
13
5
|
REFLECTIONS = [
|
data/lib/rpush/version.rb
CHANGED
data/lib/tasks/test.rake
CHANGED
@@ -7,6 +7,7 @@ def cmd(str, clean_env = true)
|
|
7
7
|
retval
|
8
8
|
end
|
9
9
|
|
10
|
+
desc 'Build Rails app bundled with Rpush'
|
10
11
|
task :build_rails do
|
11
12
|
rpush_root = Dir.pwd
|
12
13
|
path = '/tmp/rpush/rails_test'
|
@@ -27,10 +28,10 @@ task :build_rails do
|
|
27
28
|
File.open('config/database.yml', 'w') do |fd|
|
28
29
|
fd.write(<<-YML)
|
29
30
|
development:
|
30
|
-
adapter: postgresql
|
31
|
-
database: rpush_rails_test
|
32
|
-
pool: 5
|
33
|
-
timeout: 5000
|
31
|
+
adapter: postgresql
|
32
|
+
database: rpush_rails_test
|
33
|
+
pool: 5
|
34
|
+
timeout: 5000
|
34
35
|
YML
|
35
36
|
end
|
36
37
|
ensure
|
@@ -40,6 +41,7 @@ timeout: 5000
|
|
40
41
|
puts "Built into #{path}"
|
41
42
|
end
|
42
43
|
|
44
|
+
desc 'Build blank app bundled with Rpush'
|
43
45
|
task :build_standalone do
|
44
46
|
rpush_root = Dir.pwd
|
45
47
|
path = '/tmp/rpush/standalone_test'
|
data/spec/functional/adm_spec.rb
CHANGED
@@ -5,6 +5,9 @@ describe 'ADM' do
|
|
5
5
|
let(:notification) { Rpush::Adm::Notification.new }
|
6
6
|
let(:response) { double(Net::HTTPResponse, code: 200) }
|
7
7
|
let(:http) { double(Net::HTTP::Persistent, request: response, shutdown: nil) }
|
8
|
+
let(:delivered_ids) { [] }
|
9
|
+
let(:failed_ids) { [] }
|
10
|
+
let(:retry_ids) { [] }
|
8
11
|
|
9
12
|
before do
|
10
13
|
app.name = 'test'
|
@@ -37,4 +40,13 @@ describe 'ADM' do
|
|
37
40
|
notification.reload
|
38
41
|
end.to_not change(notification, :delivered).to(true)
|
39
42
|
end
|
43
|
+
|
44
|
+
it 'retries notification that fail due to a SocketError' do
|
45
|
+
expect(http).to receive(:request).and_raise(SocketError.new)
|
46
|
+
expect(notification.deliver_after).to be_nil
|
47
|
+
expect do
|
48
|
+
Rpush.push
|
49
|
+
notification.reload
|
50
|
+
end.to change(notification, :deliver_after).to(kind_of(Time))
|
51
|
+
end
|
40
52
|
end
|
@@ -1,14 +1,16 @@
|
|
1
1
|
require 'functional_spec_helper'
|
2
2
|
|
3
3
|
describe 'APNs' do
|
4
|
-
let(:timeout) { 10 }
|
5
4
|
let(:app) { create_app }
|
6
|
-
let!(:notification) { create_notification }
|
7
5
|
let(:tcp_socket) { double(TCPSocket, setsockopt: nil, close: nil) }
|
8
6
|
let(:ssl_socket) { double(OpenSSL::SSL::SSLSocket, :sync= => nil, connect: nil, write: nil, flush: nil, read: nil, close: nil) }
|
9
7
|
let(:io_double) { double(select: nil) }
|
8
|
+
let(:delivered_ids) { [] }
|
9
|
+
let(:failed_ids) { [] }
|
10
|
+
let(:retry_ids) { [] }
|
10
11
|
|
11
12
|
before do
|
13
|
+
Rpush.config.push_poll = 0.5
|
12
14
|
stub_tcp_connection
|
13
15
|
end
|
14
16
|
|
@@ -36,31 +38,20 @@ describe 'APNs' do
|
|
36
38
|
stub_const('Rpush::Daemon::TcpConnection::IO', io_double)
|
37
39
|
end
|
38
40
|
|
41
|
+
def wait
|
42
|
+
sleep 0.1
|
43
|
+
end
|
44
|
+
|
39
45
|
def wait_for_notification_to_deliver(notification)
|
40
|
-
|
41
|
-
until notification.delivered
|
42
|
-
sleep 0.1
|
43
|
-
notification.reload
|
44
|
-
end
|
45
|
-
end
|
46
|
+
timeout { wait until delivered_ids.include?(notification.id) }
|
46
47
|
end
|
47
48
|
|
48
49
|
def wait_for_notification_to_fail(notification)
|
49
|
-
|
50
|
-
while notification.delivered
|
51
|
-
sleep 0.1
|
52
|
-
notification.reload
|
53
|
-
end
|
54
|
-
end
|
50
|
+
timeout { wait until failed_ids.include?(notification.id) }
|
55
51
|
end
|
56
52
|
|
57
53
|
def wait_for_notification_to_retry(notification)
|
58
|
-
|
59
|
-
until !notification.delivered && !notification.failed && !notification.deliver_after.nil?
|
60
|
-
sleep 0.1
|
61
|
-
notification.reload
|
62
|
-
end
|
63
|
-
end
|
54
|
+
timeout { wait until retry_ids.include?(notification.id) }
|
64
55
|
end
|
65
56
|
|
66
57
|
def fail_notification(notification)
|
@@ -79,7 +70,12 @@ describe 'APNs' do
|
|
79
70
|
end
|
80
71
|
end
|
81
72
|
|
73
|
+
def timeout(&blk)
|
74
|
+
Timeout.timeout(10, &blk)
|
75
|
+
end
|
76
|
+
|
82
77
|
it 'delivers a notification successfully' do
|
78
|
+
notification = create_notification
|
83
79
|
expect do
|
84
80
|
Rpush.push
|
85
81
|
notification.reload
|
@@ -87,6 +83,7 @@ describe 'APNs' do
|
|
87
83
|
end
|
88
84
|
|
89
85
|
it 'receives feedback' do
|
86
|
+
app
|
90
87
|
tuple = "N\xE3\x84\r\x00 \x83OxfU\xEB\x9F\x84aJ\x05\xAD}\x00\xAF1\xE5\xCF\xE9:\xC3\xEA\a\x8F\x1D\xA4M*N\xB0\xCE\x17"
|
91
88
|
allow(ssl_socket).to receive(:read).and_return(tuple, nil)
|
92
89
|
Rpush.apns_feedback
|
@@ -97,21 +94,57 @@ describe 'APNs' do
|
|
97
94
|
end
|
98
95
|
|
99
96
|
describe 'delivery failures' do
|
100
|
-
before
|
101
|
-
|
97
|
+
before do
|
98
|
+
Rpush.reflect do |on|
|
99
|
+
on.notification_delivered do |n|
|
100
|
+
delivered_ids << n.id
|
101
|
+
end
|
102
|
+
|
103
|
+
on.notification_id_failed do |_, n_id|
|
104
|
+
failed_ids << n_id
|
105
|
+
end
|
106
|
+
|
107
|
+
on.notification_id_will_retry do |_, n_id|
|
108
|
+
retry_ids << n_id
|
109
|
+
end
|
110
|
+
|
111
|
+
on.notification_will_retry do |n|
|
112
|
+
retry_ids << n.id
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
Rpush.embed
|
117
|
+
end
|
118
|
+
|
119
|
+
after do
|
120
|
+
Rpush.reflection_stack.clear
|
121
|
+
Rpush.reflection_stack.push(Rpush::ReflectionCollection.new)
|
122
|
+
|
123
|
+
timeout { Rpush.shutdown }
|
124
|
+
end
|
102
125
|
|
103
126
|
it 'fails to deliver a notification' do
|
127
|
+
notification = create_notification
|
104
128
|
wait_for_notification_to_deliver(notification)
|
105
129
|
fail_notification(notification)
|
106
130
|
wait_for_notification_to_fail(notification)
|
107
131
|
end
|
108
132
|
|
133
|
+
describe 'with a failed connection' do
|
134
|
+
it 'retries all notifications' do
|
135
|
+
Rpush::Daemon::TcpConnection.any_instance.stub(sleep: nil)
|
136
|
+
expect(ssl_socket).to receive(:write).at_least(1).times.and_raise(Errno::EPIPE)
|
137
|
+
notifications = 2.times.map { create_notification }
|
138
|
+
notifications.each { |n| wait_for_notification_to_retry(n) }
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
109
142
|
describe 'with multiple notifications' do
|
110
143
|
let(:notification1) { create_notification }
|
111
144
|
let(:notification2) { create_notification }
|
112
145
|
let(:notification3) { create_notification }
|
113
146
|
let(:notification4) { create_notification }
|
114
|
-
let
|
147
|
+
let(:notifications) { [notification1, notification2, notification3, notification4] }
|
115
148
|
|
116
149
|
it 'marks the correct notification as failed' do
|
117
150
|
notifications.each { |n| wait_for_notification_to_deliver(n) }
|
@@ -124,29 +157,15 @@ describe 'APNs' do
|
|
124
157
|
fail_notification(notification2)
|
125
158
|
wait_for_notification_to_fail(notification2)
|
126
159
|
|
160
|
+
expect(failed_ids).to_not include(notification1.id)
|
127
161
|
notification1.reload
|
128
162
|
notification1.delivered.should be_true
|
129
163
|
end
|
130
164
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
# notifications.each { |n| wait_for_notification_to_deliver(n) }
|
136
|
-
# fail_notification(notification2)
|
137
|
-
#
|
138
|
-
# [notification3, notification4].each do |n|
|
139
|
-
# wait_for_notification_to_retry(n)
|
140
|
-
# end
|
141
|
-
# end
|
142
|
-
|
143
|
-
describe 'without an error response' do
|
144
|
-
it 'marks all notifications as failed' do
|
145
|
-
notifications.each { |n| wait_for_notification_to_deliver(n) }
|
146
|
-
ssl_socket.stub(read: nil)
|
147
|
-
enable_io_select
|
148
|
-
notifications.each { |n| wait_for_notification_to_fail(n) }
|
149
|
-
end
|
165
|
+
it 'marks notifications following the failed one as retryable' do
|
166
|
+
notifications.each { |n| wait_for_notification_to_deliver(n) }
|
167
|
+
fail_notification(notification2)
|
168
|
+
[notification3, notification4].each { |n| wait_for_notification_to_retry(n) }
|
150
169
|
end
|
151
170
|
end
|
152
171
|
end
|
data/spec/functional/gcm_spec.rb
CHANGED
@@ -36,4 +36,13 @@ describe 'GCM' do
|
|
36
36
|
notification.reload
|
37
37
|
end.to_not change(notification, :delivered).to(true)
|
38
38
|
end
|
39
|
+
|
40
|
+
it 'retries notification that fail due to a SocketError' do
|
41
|
+
expect(http).to receive(:request).and_raise(SocketError.new)
|
42
|
+
expect(notification.deliver_after).to be_nil
|
43
|
+
expect do
|
44
|
+
Rpush.push
|
45
|
+
notification.reload
|
46
|
+
end.to change(notification, :deliver_after).to(kind_of(Time))
|
47
|
+
end
|
39
48
|
end
|