rapns 2.0.5 → 3.0.0.beta.1
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.
- data/lib/generators/rapns_generator.rb +1 -0
- data/lib/generators/templates/add_gcm.rb +86 -0
- data/lib/generators/templates/create_rapns_notifications.rb +1 -1
- data/lib/rapns/apns/app.rb +8 -0
- data/lib/rapns/apns/binary_notification_validator.rb +12 -0
- data/lib/rapns/apns/device_token_format_validator.rb +12 -0
- data/lib/rapns/apns/feedback.rb +14 -0
- data/lib/rapns/apns/notification.rb +84 -0
- data/lib/rapns/app.rb +5 -6
- data/lib/rapns/{config.rb → configuration.rb} +5 -5
- data/lib/rapns/daemon/apns/app_runner.rb +36 -0
- data/lib/rapns/daemon/apns/connection.rb +113 -0
- data/lib/rapns/daemon/apns/delivery.rb +63 -0
- data/lib/rapns/daemon/apns/delivery_handler.rb +21 -0
- data/lib/rapns/daemon/apns/disconnection_error.rb +20 -0
- data/lib/rapns/daemon/apns/feedback_receiver.rb +74 -0
- data/lib/rapns/daemon/app_runner.rb +76 -77
- data/lib/rapns/daemon/database_reconnectable.rb +3 -3
- data/lib/rapns/daemon/delivery.rb +43 -0
- data/lib/rapns/daemon/delivery_error.rb +6 -2
- data/lib/rapns/daemon/delivery_handler.rb +13 -79
- data/lib/rapns/daemon/delivery_queue_18.rb +2 -2
- data/lib/rapns/daemon/delivery_queue_19.rb +3 -3
- data/lib/rapns/daemon/feeder.rb +5 -5
- data/lib/rapns/daemon/gcm/app_runner.rb +13 -0
- data/lib/rapns/daemon/gcm/delivery.rb +206 -0
- data/lib/rapns/daemon/gcm/delivery_handler.rb +20 -0
- data/lib/rapns/daemon.rb +31 -20
- data/lib/rapns/gcm/app.rb +7 -0
- data/lib/rapns/gcm/expiry_collapse_key_mutual_inclusion_validator.rb +11 -0
- data/lib/rapns/gcm/notification.rb +31 -0
- data/lib/rapns/gcm/payload_size_validator.rb +13 -0
- data/lib/rapns/multi_json_helper.rb +16 -0
- data/lib/rapns/notification.rb +28 -95
- data/lib/rapns/version.rb +1 -1
- data/lib/rapns.rb +14 -4
- data/lib/tasks/cane.rake +19 -0
- data/lib/tasks/test.rake +34 -0
- data/spec/acceptance/gcm_upgrade_spec.rb +34 -0
- data/spec/acceptance_spec_helper.rb +85 -0
- data/spec/support/simplecov_helper.rb +13 -0
- data/spec/support/simplecov_quality_formatter.rb +8 -0
- data/spec/unit/apns/app_spec.rb +15 -0
- data/spec/unit/apns/feedback_spec.rb +12 -0
- data/spec/{rapns → unit/apns}/notification_spec.rb +44 -72
- data/spec/unit/app_spec.rb +18 -0
- data/spec/unit/daemon/apns/app_runner_spec.rb +37 -0
- data/spec/{rapns/daemon → unit/daemon/apns}/connection_spec.rb +9 -9
- data/spec/unit/daemon/apns/delivery_handler_spec.rb +48 -0
- data/spec/unit/daemon/apns/delivery_spec.rb +154 -0
- data/spec/{rapns/daemon → unit/daemon/apns}/feedback_receiver_spec.rb +14 -14
- data/spec/unit/daemon/app_runner_shared.rb +66 -0
- data/spec/unit/daemon/app_runner_spec.rb +78 -0
- data/spec/{rapns → unit}/daemon/database_reconnectable_spec.rb +4 -5
- data/spec/{rapns → unit}/daemon/delivery_error_spec.rb +2 -2
- data/spec/unit/daemon/delivery_handler_shared.rb +19 -0
- data/spec/{rapns → unit}/daemon/delivery_queue_spec.rb +1 -1
- data/spec/{rapns → unit}/daemon/feeder_spec.rb +33 -33
- data/spec/unit/daemon/gcm/app_runner_spec.rb +15 -0
- data/spec/unit/daemon/gcm/delivery_handler_spec.rb +36 -0
- data/spec/unit/daemon/gcm/delivery_spec.rb +236 -0
- data/spec/{rapns → unit}/daemon/interruptible_sleep_spec.rb +1 -1
- data/spec/{rapns → unit}/daemon/logger_spec.rb +1 -1
- data/spec/{rapns → unit}/daemon_spec.rb +1 -1
- data/spec/unit/gcm/app_spec.rb +5 -0
- data/spec/unit/gcm/notification_spec.rb +55 -0
- data/spec/unit/notification_shared.rb +38 -0
- data/spec/unit/notification_spec.rb +6 -0
- data/spec/{rapns/app_spec.rb → unit_spec_helper.rb} +76 -16
- metadata +107 -45
- data/lib/rapns/binary_notification_validator.rb +0 -10
- data/lib/rapns/daemon/connection.rb +0 -114
- data/lib/rapns/daemon/delivery_handler_pool.rb +0 -18
- data/lib/rapns/daemon/disconnection_error.rb +0 -14
- data/lib/rapns/daemon/feedback_receiver.rb +0 -82
- data/lib/rapns/device_token_format_validator.rb +0 -10
- data/lib/rapns/feedback.rb +0 -12
- data/spec/rapns/daemon/app_runner_spec.rb +0 -193
- data/spec/rapns/daemon/delivery_handler_pool_spec.rb +0 -17
- data/spec/rapns/daemon/delivery_handler_spec.rb +0 -206
- data/spec/rapns/feedback_spec.rb +0 -12
- data/spec/spec_helper.rb +0 -78
@@ -1,10 +0,0 @@
|
|
1
|
-
module Rapns
|
2
|
-
class BinaryNotificationValidator < ActiveModel::Validator
|
3
|
-
|
4
|
-
def validate(record)
|
5
|
-
if record.payload_size > 256
|
6
|
-
record.errors[:base] << "APN notification cannot be larger than 256 bytes. Try condensing your alert and device attributes."
|
7
|
-
end
|
8
|
-
end
|
9
|
-
end
|
10
|
-
end
|
@@ -1,114 +0,0 @@
|
|
1
|
-
module Rapns
|
2
|
-
module Daemon
|
3
|
-
class ConnectionError < StandardError; end
|
4
|
-
|
5
|
-
class Connection
|
6
|
-
attr_accessor :last_write
|
7
|
-
|
8
|
-
def self.idle_period
|
9
|
-
30.minutes
|
10
|
-
end
|
11
|
-
|
12
|
-
def initialize(name, host, port, certificate, password)
|
13
|
-
@name = name
|
14
|
-
@host = host
|
15
|
-
@port = port
|
16
|
-
@certificate = certificate
|
17
|
-
@password = password
|
18
|
-
written
|
19
|
-
end
|
20
|
-
|
21
|
-
def connect
|
22
|
-
@ssl_context = setup_ssl_context
|
23
|
-
@tcp_socket, @ssl_socket = connect_socket
|
24
|
-
end
|
25
|
-
|
26
|
-
def close
|
27
|
-
begin
|
28
|
-
@ssl_socket.close if @ssl_socket
|
29
|
-
@tcp_socket.close if @tcp_socket
|
30
|
-
rescue IOError
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
def read(num_bytes)
|
35
|
-
@ssl_socket.read(num_bytes)
|
36
|
-
end
|
37
|
-
|
38
|
-
def select(timeout)
|
39
|
-
IO.select([@ssl_socket], nil, nil, timeout)
|
40
|
-
end
|
41
|
-
|
42
|
-
def write(data)
|
43
|
-
reconnect_idle if idle_period_exceeded?
|
44
|
-
|
45
|
-
retry_count = 0
|
46
|
-
|
47
|
-
begin
|
48
|
-
write_data(data)
|
49
|
-
rescue Errno::EPIPE, Errno::ETIMEDOUT, OpenSSL::SSL::SSLError => e
|
50
|
-
retry_count += 1;
|
51
|
-
|
52
|
-
if retry_count == 1
|
53
|
-
Rapns::Daemon.logger.error("[#{@name}] Lost connection to #{@host}:#{@port} (#{e.class.name}), reconnecting...")
|
54
|
-
end
|
55
|
-
|
56
|
-
if retry_count <= 3
|
57
|
-
reconnect
|
58
|
-
sleep 1
|
59
|
-
retry
|
60
|
-
else
|
61
|
-
raise ConnectionError, "#{@name} tried #{retry_count-1} times to reconnect but failed (#{e.class.name})."
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
def reconnect
|
67
|
-
close
|
68
|
-
@tcp_socket, @ssl_socket = connect_socket
|
69
|
-
end
|
70
|
-
|
71
|
-
protected
|
72
|
-
|
73
|
-
def reconnect_idle
|
74
|
-
Rapns::Daemon.logger.info("[#{@name}] Idle period exceeded, reconnecting...")
|
75
|
-
reconnect
|
76
|
-
end
|
77
|
-
|
78
|
-
def idle_period_exceeded?
|
79
|
-
Time.now - last_write > self.class.idle_period
|
80
|
-
end
|
81
|
-
|
82
|
-
def write_data(data)
|
83
|
-
@ssl_socket.write(data)
|
84
|
-
@ssl_socket.flush
|
85
|
-
written
|
86
|
-
end
|
87
|
-
|
88
|
-
def written
|
89
|
-
self.last_write = Time.now
|
90
|
-
end
|
91
|
-
|
92
|
-
def setup_ssl_context
|
93
|
-
ssl_context = OpenSSL::SSL::SSLContext.new
|
94
|
-
ssl_context.key = OpenSSL::PKey::RSA.new(@certificate, @password)
|
95
|
-
ssl_context.cert = OpenSSL::X509::Certificate.new(@certificate)
|
96
|
-
ssl_context
|
97
|
-
end
|
98
|
-
|
99
|
-
def connect_socket
|
100
|
-
tcp_socket = TCPSocket.new(@host, @port)
|
101
|
-
tcp_socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, 1)
|
102
|
-
tcp_socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
103
|
-
ssl_socket = OpenSSL::SSL::SSLSocket.new(tcp_socket, @ssl_context)
|
104
|
-
ssl_socket.sync = true
|
105
|
-
ssl_socket.connect
|
106
|
-
Rapns::Daemon.logger.info("[#{@name}] Connected to #{@host}:#{@port}")
|
107
|
-
[tcp_socket, ssl_socket]
|
108
|
-
rescue StandardError => e
|
109
|
-
Rapns::Daemon.logger.error("[#{@name}] Error connecting to #{@host}:#{@port} : #{e}")
|
110
|
-
raise
|
111
|
-
end
|
112
|
-
end
|
113
|
-
end
|
114
|
-
end
|
@@ -1,18 +0,0 @@
|
|
1
|
-
module Rapns
|
2
|
-
module Daemon
|
3
|
-
class DeliveryHandlerPool
|
4
|
-
def initialize
|
5
|
-
@handlers = []
|
6
|
-
end
|
7
|
-
|
8
|
-
def <<(handler)
|
9
|
-
@handlers << handler
|
10
|
-
handler.start
|
11
|
-
end
|
12
|
-
|
13
|
-
def drain
|
14
|
-
@handlers.pop.stop while !@handlers.empty?
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
@@ -1,14 +0,0 @@
|
|
1
|
-
module Rapns
|
2
|
-
class DisconnectionError < StandardError
|
3
|
-
attr_reader :code, :description
|
4
|
-
|
5
|
-
def initialize
|
6
|
-
@code = nil
|
7
|
-
@description = "APNs disconnected without returning an error."
|
8
|
-
end
|
9
|
-
|
10
|
-
def message
|
11
|
-
"The APNs disconnected without returning an error. This may indicate you are using an invalid certificate for the host."
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
@@ -1,82 +0,0 @@
|
|
1
|
-
module Rapns
|
2
|
-
module Daemon
|
3
|
-
class FeedbackReceiver
|
4
|
-
include InterruptibleSleep
|
5
|
-
include DatabaseReconnectable
|
6
|
-
|
7
|
-
FEEDBACK_TUPLE_BYTES = 38
|
8
|
-
|
9
|
-
def initialize(name, host, port, poll, certificate, password)
|
10
|
-
@name = name
|
11
|
-
@host = host
|
12
|
-
@port = port
|
13
|
-
@poll = poll
|
14
|
-
@certificate = certificate
|
15
|
-
@password = password
|
16
|
-
end
|
17
|
-
|
18
|
-
def start
|
19
|
-
@thread = Thread.new do
|
20
|
-
loop do
|
21
|
-
begin
|
22
|
-
break if @stop
|
23
|
-
check_for_feedback
|
24
|
-
rescue OpenSSL::SSL::SSLError
|
25
|
-
# stop the thread if there is an SSL error. Other errors might be recoverable,
|
26
|
-
# and retrying later might make sense (for example, a network outage)
|
27
|
-
@stop = true
|
28
|
-
break
|
29
|
-
rescue
|
30
|
-
# error will be logged in check_for_feedback
|
31
|
-
end
|
32
|
-
interruptible_sleep @poll
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
def stop
|
38
|
-
@stop = true
|
39
|
-
interrupt_sleep
|
40
|
-
@thread.join if @thread
|
41
|
-
end
|
42
|
-
|
43
|
-
def check_for_feedback
|
44
|
-
connection = nil
|
45
|
-
begin
|
46
|
-
connection = Connection.new("FeedbackReceiver:#{@name}", @host, @port, @certificate, @password)
|
47
|
-
connection.connect
|
48
|
-
|
49
|
-
while tuple = connection.read(FEEDBACK_TUPLE_BYTES)
|
50
|
-
timestamp, device_token = parse_tuple(tuple)
|
51
|
-
create_feedback(timestamp, device_token)
|
52
|
-
end
|
53
|
-
rescue StandardError => e
|
54
|
-
Rapns::Daemon.logger.error(e)
|
55
|
-
raise
|
56
|
-
ensure
|
57
|
-
connection.close if connection
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
protected
|
62
|
-
|
63
|
-
def parse_tuple(tuple)
|
64
|
-
failed_at, _, device_token = tuple.unpack("N1n1H*")
|
65
|
-
[Time.at(failed_at).utc, device_token]
|
66
|
-
end
|
67
|
-
|
68
|
-
def create_feedback(failed_at, device_token)
|
69
|
-
formatted_failed_at = failed_at.strftime("%Y-%m-%d %H:%M:%S UTC")
|
70
|
-
with_database_reconnect_and_retry do
|
71
|
-
Rapns::Daemon.logger.info("[FeedbackReceiver:#{@name}] Delivery failed at #{formatted_failed_at} for #{device_token}")
|
72
|
-
feedback = Rapns::Feedback.create!(:failed_at => failed_at, :device_token => device_token, :app => @name)
|
73
|
-
begin
|
74
|
-
Rapns.configuration.feedback_callback.call(feedback) if Rapns.configuration.feedback_callback
|
75
|
-
rescue Exception => e
|
76
|
-
Rapns::Daemon.logger.error(e)
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
data/lib/rapns/feedback.rb
DELETED
@@ -1,12 +0,0 @@
|
|
1
|
-
module Rapns
|
2
|
-
class Feedback < ActiveRecord::Base
|
3
|
-
self.table_name = 'rapns_feedback'
|
4
|
-
|
5
|
-
attr_accessible :device_token, :failed_at, :app
|
6
|
-
|
7
|
-
validates :device_token, :presence => true
|
8
|
-
validates :failed_at, :presence => true
|
9
|
-
|
10
|
-
validates_with Rapns::DeviceTokenFormatValidator
|
11
|
-
end
|
12
|
-
end
|
@@ -1,193 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe Rapns::Daemon::AppRunner do
|
4
|
-
let(:app) { stub(:key => 'app', :certificate => 'cert', :password => '', :connections => 1) }
|
5
|
-
let(:queue) { stub(:notifications_processed? => true, :push => nil) }
|
6
|
-
let(:receiver) { stub(:start => nil, :stop => nil) }
|
7
|
-
let(:handler) { stub(:start => nil, :stop => nil) }
|
8
|
-
let(:push_config) { stub(:host => 'gateway.push.apple.com', :port => 2195) }
|
9
|
-
let(:feedback_config) { stub(:host => 'feedback.push.apple.com', :port => 2196, :poll => 60) }
|
10
|
-
let(:runner) { Rapns::Daemon::AppRunner.new(app, push_config.host, push_config.port,
|
11
|
-
feedback_config.host, feedback_config.port, feedback_config.poll) }
|
12
|
-
|
13
|
-
before do
|
14
|
-
Rapns::Daemon::DeliveryQueue.stub(:new => queue)
|
15
|
-
Rapns::Daemon::FeedbackReceiver.stub(:new => receiver)
|
16
|
-
Rapns::Daemon::DeliveryHandler.stub(:new => handler)
|
17
|
-
end
|
18
|
-
|
19
|
-
after { Rapns::Daemon::AppRunner.all.clear }
|
20
|
-
|
21
|
-
describe 'start' do
|
22
|
-
it 'starts a feedback receiver' do
|
23
|
-
Rapns::Daemon::FeedbackReceiver.should_receive(:new).with(app.key, feedback_config.host, feedback_config.port, feedback_config.poll, app.certificate, app.password)
|
24
|
-
receiver.should_receive(:start)
|
25
|
-
runner.start
|
26
|
-
end
|
27
|
-
|
28
|
-
it 'starts a delivery handler for each connection' do
|
29
|
-
Rapns::Daemon::DeliveryHandler.should_receive(:new).with(queue, app.key, push_config.host,
|
30
|
-
push_config.port, app.certificate, app.password)
|
31
|
-
handler.should_receive(:start)
|
32
|
-
runner.start
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
describe 'deliver' do
|
37
|
-
let(:notification) { stub }
|
38
|
-
|
39
|
-
it 'enqueues the notification' do
|
40
|
-
queue.should_receive(:push).with(notification)
|
41
|
-
runner.deliver(notification)
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
describe 'stop' do
|
46
|
-
before { runner.start }
|
47
|
-
|
48
|
-
it 'stops the delivery handlers' do
|
49
|
-
handler.should_receive(:stop)
|
50
|
-
runner.stop
|
51
|
-
end
|
52
|
-
|
53
|
-
it 'stops the feedback receiver' do
|
54
|
-
receiver.should_receive(:stop)
|
55
|
-
runner.stop
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
describe 'ready?' do
|
60
|
-
it 'is ready if all notifications have been processed' do
|
61
|
-
queue.stub(:notifications_processed? => true)
|
62
|
-
runner.ready?.should be_true
|
63
|
-
end
|
64
|
-
|
65
|
-
it 'is not ready if not all notifications have been processed' do
|
66
|
-
queue.stub(:notifications_processed? => false)
|
67
|
-
runner.ready?.should be_false
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
describe 'sync' do
|
72
|
-
let(:new_app) { stub(:key => 'app', :certificate => 'cert', :password => '', :connections => 1) }
|
73
|
-
before { runner.start }
|
74
|
-
|
75
|
-
it 'reduces the number of handlers if needed' do
|
76
|
-
handler.should_receive(:stop)
|
77
|
-
new_app.stub(:connections => app.connections - 1)
|
78
|
-
runner.sync(new_app)
|
79
|
-
end
|
80
|
-
|
81
|
-
it 'increases the number of handlers if needed' do
|
82
|
-
new_handler = stub
|
83
|
-
Rapns::Daemon::DeliveryHandler.should_receive(:new).and_return(new_handler)
|
84
|
-
new_handler.should_receive(:start)
|
85
|
-
new_app.stub(:connections => app.connections + 1)
|
86
|
-
runner.sync(new_app)
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
describe Rapns::Daemon::AppRunner, 'stop' do
|
92
|
-
let(:runner) { stub }
|
93
|
-
before { Rapns::Daemon::AppRunner.all['app'] = runner }
|
94
|
-
after { Rapns::Daemon::AppRunner.all.clear }
|
95
|
-
|
96
|
-
it 'stops all runners' do
|
97
|
-
runner.should_receive(:stop)
|
98
|
-
Rapns::Daemon::AppRunner.stop
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
describe Rapns::Daemon::AppRunner, 'deliver' do
|
103
|
-
let(:runner) { stub }
|
104
|
-
let(:notification) { stub(:app => 'app') }
|
105
|
-
let(:logger) { stub(:error => nil) }
|
106
|
-
|
107
|
-
before do
|
108
|
-
Rapns::Daemon.stub(:logger => logger)
|
109
|
-
Rapns::Daemon::AppRunner.all['app'] = runner
|
110
|
-
end
|
111
|
-
|
112
|
-
after { Rapns::Daemon::AppRunner.all.clear }
|
113
|
-
|
114
|
-
it 'delivers the notification' do
|
115
|
-
runner.should_receive(:deliver).with(notification)
|
116
|
-
Rapns::Daemon::AppRunner.deliver(notification)
|
117
|
-
end
|
118
|
-
|
119
|
-
it 'logs an error if there is no runner to deliver the notification' do
|
120
|
-
notification.stub(:app => 'unknonw', :id => 123)
|
121
|
-
logger.should_receive(:error).with("No such app '#{notification.app}' for notification #{notification.id}.")
|
122
|
-
Rapns::Daemon::AppRunner.deliver(notification)
|
123
|
-
end
|
124
|
-
end
|
125
|
-
|
126
|
-
describe Rapns::Daemon::AppRunner, 'ready' do
|
127
|
-
let(:runner1) { stub(:ready? => true) }
|
128
|
-
let(:runner2) { stub(:ready? => false) }
|
129
|
-
|
130
|
-
before do
|
131
|
-
Rapns::Daemon::AppRunner.all['app1'] = runner1
|
132
|
-
Rapns::Daemon::AppRunner.all['app2'] = runner2
|
133
|
-
end
|
134
|
-
|
135
|
-
after { Rapns::Daemon::AppRunner.all.clear }
|
136
|
-
|
137
|
-
it 'returns apps that are ready for more notifications' do
|
138
|
-
Rapns::Daemon::AppRunner.ready.should == ['app1']
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
|
-
describe Rapns::Daemon::AppRunner, 'sync' do
|
143
|
-
let(:app) { stub(:key => 'app', :environment => 'development') }
|
144
|
-
let(:new_app) { stub(:key => 'new_app', :environment => 'development') }
|
145
|
-
let(:runner) { stub(:sync => nil, :stop => nil) }
|
146
|
-
let(:new_runner) { stub }
|
147
|
-
let(:logger) { stub(:error => nil) }
|
148
|
-
let(:config) { stub(:feedback_poll => 60) }
|
149
|
-
|
150
|
-
before do
|
151
|
-
Rapns::Daemon.stub(:config => config, :logger => logger)
|
152
|
-
Rapns::Daemon::AppRunner.all['app'] = runner
|
153
|
-
Rapns::App.stub(:all => [app])
|
154
|
-
end
|
155
|
-
|
156
|
-
after { Rapns::Daemon::AppRunner.all.clear }
|
157
|
-
|
158
|
-
it 'loads all apps' do
|
159
|
-
Rapns::App.should_receive(:all)
|
160
|
-
Rapns::Daemon::AppRunner.sync
|
161
|
-
end
|
162
|
-
|
163
|
-
it 'instructs existing runners to sync' do
|
164
|
-
runner.should_receive(:sync).with(app)
|
165
|
-
Rapns::Daemon::AppRunner.sync
|
166
|
-
end
|
167
|
-
|
168
|
-
it 'starts a runner for a new app with a production certificate' do
|
169
|
-
new_app.stub(:environment => 'production')
|
170
|
-
Rapns::App.stub(:all => [new_app])
|
171
|
-
new_runner = stub
|
172
|
-
Rapns::Daemon::AppRunner.should_receive(:new).with(new_app, 'gateway.push.apple.com', 2195,
|
173
|
-
'feedback.push.apple.com', 2196, config.feedback_poll).and_return(new_runner)
|
174
|
-
new_runner.should_receive(:start)
|
175
|
-
Rapns::Daemon::AppRunner.sync
|
176
|
-
end
|
177
|
-
|
178
|
-
it 'starts a runner for a new app with a development certificate' do
|
179
|
-
new_app.stub(:environment => 'development')
|
180
|
-
Rapns::App.stub(:all => [new_app])
|
181
|
-
new_runner = stub
|
182
|
-
Rapns::Daemon::AppRunner.should_receive(:new).with(new_app, 'gateway.sandbox.push.apple.com', 2195,
|
183
|
-
'feedback.sandbox.push.apple.com', 2196, config.feedback_poll).and_return(new_runner)
|
184
|
-
new_runner.should_receive(:start)
|
185
|
-
Rapns::Daemon::AppRunner.sync
|
186
|
-
end
|
187
|
-
|
188
|
-
it 'deletes old apps' do
|
189
|
-
Rapns::App.stub(:all => [])
|
190
|
-
runner.should_receive(:stop)
|
191
|
-
Rapns::Daemon::AppRunner.sync
|
192
|
-
end
|
193
|
-
end
|
@@ -1,17 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
|
3
|
-
describe Rapns::Daemon::DeliveryHandlerPool do
|
4
|
-
let(:pool) { Rapns::Daemon::DeliveryHandlerPool.new }
|
5
|
-
let(:handler) { stub(:start => nil, :stop => nil) }
|
6
|
-
|
7
|
-
it 'starts the handler when added to the pool' do
|
8
|
-
handler.should_receive(:start)
|
9
|
-
pool << handler
|
10
|
-
end
|
11
|
-
|
12
|
-
it 'stops each handler when drained' do
|
13
|
-
pool << handler
|
14
|
-
handler.should_receive(:stop)
|
15
|
-
pool.drain
|
16
|
-
end
|
17
|
-
end
|
@@ -1,206 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
|
3
|
-
describe Rapns::Daemon::DeliveryHandler do
|
4
|
-
let(:queue) { Rapns::Daemon::DeliveryQueue.new }
|
5
|
-
let(:name) { 'my_app:0' }
|
6
|
-
let(:host) { 'localhost' }
|
7
|
-
let(:port) { 2195 }
|
8
|
-
let(:certificate) { stub }
|
9
|
-
let(:password) { stub }
|
10
|
-
let(:delivery_handler) { Rapns::Daemon::DeliveryHandler.new(queue, name, host, port, certificate, password) }
|
11
|
-
let(:connection) { stub(:select => false, :write => nil, :reconnect => nil, :close => nil, :connect => nil) }
|
12
|
-
let(:logger) { stub(:error => nil, :info => nil) }
|
13
|
-
let(:notification) { stub.as_null_object }
|
14
|
-
let(:config) { stub(:check_for_errors => true) }
|
15
|
-
let(:delivery_queues) { [] }
|
16
|
-
|
17
|
-
before do
|
18
|
-
Rapns::Daemon::Connection.stub(:new => connection)
|
19
|
-
Rapns::Daemon.stub(:delivery_queues => delivery_queues, :logger => logger, :config => config)
|
20
|
-
queue.push(notification)
|
21
|
-
end
|
22
|
-
|
23
|
-
it "instantiates a new connection" do
|
24
|
-
Rapns::Daemon::Connection.should_receive(:new).with("DeliveryHandler:#{name}", host, port, certificate, password)
|
25
|
-
delivery_handler
|
26
|
-
end
|
27
|
-
|
28
|
-
it "connects the socket when started" do
|
29
|
-
connection.should_receive(:connect)
|
30
|
-
delivery_handler.start
|
31
|
-
delivery_handler.stop
|
32
|
-
end
|
33
|
-
|
34
|
-
it "instructs the queue to wakeup the thread when told to stop" do
|
35
|
-
thread = stub
|
36
|
-
Thread.stub(:new => thread)
|
37
|
-
queue.should_receive(:wakeup).with(thread)
|
38
|
-
delivery_handler.start
|
39
|
-
delivery_handler.stop
|
40
|
-
end
|
41
|
-
|
42
|
-
it "sends the binary version of the notification" do
|
43
|
-
notification.stub(:to_binary => "hi mom")
|
44
|
-
connection.should_receive(:write).with("hi mom")
|
45
|
-
delivery_handler.send(:handle_next_notification)
|
46
|
-
end
|
47
|
-
|
48
|
-
it "logs the notification delivery" do
|
49
|
-
notification.stub(:id => 666, :device_token => 'abc123')
|
50
|
-
logger.should_receive(:info).with("[DeliveryHandler:my_app:0] 666 sent to abc123")
|
51
|
-
delivery_handler.send(:handle_next_notification)
|
52
|
-
end
|
53
|
-
|
54
|
-
it "marks the notification as delivered" do
|
55
|
-
notification.should_receive(:delivered=).with(true)
|
56
|
-
delivery_handler.send(:handle_next_notification)
|
57
|
-
end
|
58
|
-
|
59
|
-
it "sets the time the notification was delivered" do
|
60
|
-
now = Time.now
|
61
|
-
Time.stub(:now).and_return(now)
|
62
|
-
notification.should_receive(:delivered_at=).with(now)
|
63
|
-
delivery_handler.send(:handle_next_notification)
|
64
|
-
end
|
65
|
-
|
66
|
-
it "does not trigger validations when saving the notification" do
|
67
|
-
notification.should_receive(:save!).with(:validate => false)
|
68
|
-
delivery_handler.send(:handle_next_notification)
|
69
|
-
end
|
70
|
-
|
71
|
-
it "updates notification with the ability to reconnect the database" do
|
72
|
-
delivery_handler.should_receive(:with_database_reconnect_and_retry)
|
73
|
-
delivery_handler.send(:handle_next_notification)
|
74
|
-
end
|
75
|
-
|
76
|
-
it "logs if an error is raised when updating the notification" do
|
77
|
-
e = StandardError.new("bork!")
|
78
|
-
notification.stub(:save!).and_raise(e)
|
79
|
-
Rapns::Daemon.logger.should_receive(:error).with(e)
|
80
|
-
delivery_handler.send(:handle_next_notification)
|
81
|
-
end
|
82
|
-
|
83
|
-
it "notifies the delivery queue the notification has been processed" do
|
84
|
-
queue.should_receive(:notification_processed)
|
85
|
-
delivery_handler.send(:handle_next_notification)
|
86
|
-
end
|
87
|
-
|
88
|
-
it 'does not check for errors if check_for_errors config option is false' do
|
89
|
-
config.stub(:check_for_errors => false)
|
90
|
-
delivery_handler.should_not_receive(:check_for_error)
|
91
|
-
delivery_handler.send(:handle_next_notification)
|
92
|
-
end
|
93
|
-
|
94
|
-
describe "when being stopped" do
|
95
|
-
before { queue.pop }
|
96
|
-
|
97
|
-
it "closes the connection when a DeliveryQueue::WakeupError is raised" do
|
98
|
-
connection.should_receive(:close)
|
99
|
-
queue.stub(:pop).and_raise(Rapns::Daemon::DeliveryQueue::WakeupError)
|
100
|
-
delivery_handler.send(:handle_next_notification)
|
101
|
-
end
|
102
|
-
|
103
|
-
it "does not attempt to deliver a notification when a DeliveryQueue::::WakeupError is raised" do
|
104
|
-
queue.stub(:pop).and_raise(Rapns::Daemon::DeliveryQueue::WakeupError)
|
105
|
-
delivery_handler.should_not_receive(:deliver)
|
106
|
-
delivery_handler.send(:handle_next_notification)
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
describe "when delivery fails" do
|
111
|
-
before { connection.stub(:select => true, :read => [8, 4, 69].pack("ccN")) }
|
112
|
-
|
113
|
-
it "updates notification with the ability to reconnect the database" do
|
114
|
-
delivery_handler.should_receive(:with_database_reconnect_and_retry)
|
115
|
-
delivery_handler.send(:handle_next_notification)
|
116
|
-
end
|
117
|
-
|
118
|
-
it "sets the notification as not delivered" do
|
119
|
-
notification.should_receive(:delivered=).with(false)
|
120
|
-
delivery_handler.send(:handle_next_notification)
|
121
|
-
end
|
122
|
-
|
123
|
-
it "sets the notification delivered_at timestamp to nil" do
|
124
|
-
notification.should_receive(:delivered_at=).with(nil)
|
125
|
-
delivery_handler.send(:handle_next_notification)
|
126
|
-
end
|
127
|
-
|
128
|
-
it "sets the notification as failed" do
|
129
|
-
notification.should_receive(:failed=).with(true)
|
130
|
-
delivery_handler.send(:handle_next_notification)
|
131
|
-
end
|
132
|
-
|
133
|
-
it "sets the notification failed_at timestamp" do
|
134
|
-
now = Time.now
|
135
|
-
Time.stub(:now).and_return(now)
|
136
|
-
notification.should_receive(:failed_at=).with(now)
|
137
|
-
delivery_handler.send(:handle_next_notification)
|
138
|
-
end
|
139
|
-
|
140
|
-
it "sets the notification error code" do
|
141
|
-
notification.should_receive(:error_code=).with(4)
|
142
|
-
delivery_handler.send(:handle_next_notification)
|
143
|
-
end
|
144
|
-
|
145
|
-
it "logs the delivery error" do
|
146
|
-
error = Rapns::DeliveryError.new(4, 12, "Missing payload")
|
147
|
-
Rapns::DeliveryError.stub(:new => error)
|
148
|
-
logger.should_receive(:error).with(error)
|
149
|
-
delivery_handler.send(:handle_next_notification)
|
150
|
-
end
|
151
|
-
|
152
|
-
it "sets the notification error description" do
|
153
|
-
notification.should_receive(:error_description=).with("Missing payload")
|
154
|
-
delivery_handler.send(:handle_next_notification)
|
155
|
-
end
|
156
|
-
|
157
|
-
it "skips validation when saving the notification" do
|
158
|
-
notification.should_receive(:save!).with(:validate => false)
|
159
|
-
delivery_handler.send(:handle_next_notification)
|
160
|
-
end
|
161
|
-
|
162
|
-
it "reads 6 bytes from the socket" do
|
163
|
-
connection.should_receive(:read).with(6).and_return(nil)
|
164
|
-
delivery_handler.send(:handle_next_notification)
|
165
|
-
end
|
166
|
-
|
167
|
-
it "does not attempt to read from the socket if the socket was not selected for reading after the timeout" do
|
168
|
-
connection.stub(:select => nil)
|
169
|
-
connection.should_not_receive(:read)
|
170
|
-
delivery_handler.send(:handle_next_notification)
|
171
|
-
end
|
172
|
-
|
173
|
-
it "reconnects the socket" do
|
174
|
-
connection.should_receive(:reconnect)
|
175
|
-
delivery_handler.send(:handle_next_notification)
|
176
|
-
end
|
177
|
-
|
178
|
-
it "logs that the connection is being reconnected" do
|
179
|
-
Rapns::Daemon.logger.should_receive(:error).with("[DeliveryHandler:my_app:0] Error received, reconnecting...")
|
180
|
-
delivery_handler.send(:handle_next_notification)
|
181
|
-
end
|
182
|
-
|
183
|
-
context "when the APNs disconnects without returning an error" do
|
184
|
-
before do
|
185
|
-
connection.stub(:read => nil)
|
186
|
-
end
|
187
|
-
|
188
|
-
it 'raises a DisconnectError error if the connection is closed without an error being returned' do
|
189
|
-
error = Rapns::DisconnectionError.new
|
190
|
-
Rapns::DisconnectionError.should_receive(:new).and_return(error)
|
191
|
-
Rapns::Daemon.logger.should_receive(:error).with(error)
|
192
|
-
delivery_handler.send(:handle_next_notification)
|
193
|
-
end
|
194
|
-
|
195
|
-
it 'does not set the error code on the notification' do
|
196
|
-
notification.should_receive(:error_code=).with(nil)
|
197
|
-
delivery_handler.send(:handle_next_notification)
|
198
|
-
end
|
199
|
-
|
200
|
-
it 'sets the error descriptipon on the notification' do
|
201
|
-
notification.should_receive(:error_description=).with("APNs disconnected without returning an error.")
|
202
|
-
delivery_handler.send(:handle_next_notification)
|
203
|
-
end
|
204
|
-
end
|
205
|
-
end
|
206
|
-
end
|
data/spec/rapns/feedback_spec.rb
DELETED
@@ -1,12 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
|
3
|
-
describe Rapns::Feedback do
|
4
|
-
it { should validate_presence_of(:device_token) }
|
5
|
-
it { should validate_presence_of(:failed_at) }
|
6
|
-
|
7
|
-
it "should validate the format of the device_token" do
|
8
|
-
feedback = Rapns::Feedback.new(:device_token => "{$%^&*()}")
|
9
|
-
feedback.valid?.should be_false
|
10
|
-
feedback.errors[:device_token].include?("is invalid").should be_true
|
11
|
-
end
|
12
|
-
end
|