rapns 0.2.2 → 0.2.3
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +8 -4
- data/lib/rapns/daemon/connection.rb +1 -1
- data/lib/rapns/daemon/delivery_handler.rb +30 -23
- data/lib/rapns/daemon/delivery_queue.rb +7 -10
- data/lib/rapns/daemon/feeder.rb +26 -9
- data/lib/rapns/daemon/logger.rb +5 -1
- data/lib/rapns/daemon.rb +3 -6
- data/lib/rapns/version.rb +1 -1
- data/spec/rapns/daemon/connection_spec.rb +45 -0
- data/spec/rapns/daemon/feeder_spec.rb +12 -0
- data/spec/rapns/daemon_spec.rb +0 -6
- data/spec/spec_helper.rb +1 -0
- metadata +2 -2
data/README.md
CHANGED
@@ -44,7 +44,7 @@ If you want to use rapns in environments other than development or production, y
|
|
44
44
|
### Options:
|
45
45
|
|
46
46
|
* `host` the APNs host to connect to, either `gateway.sandbox.push.apple.com` or `gateway.sandbox.push.apple.com`.
|
47
|
-
* `port` the APNs port. Currently 2195 for both hosts.
|
47
|
+
* `port` the APNs port. Currently `2195` for both hosts.
|
48
48
|
* `certificate` The path to your .pem certificate, `config/rapns` is automatically checked if a relative path is given.
|
49
49
|
* `certificate_password` (default: blank) the password you used when exporting your certificate, if any.
|
50
50
|
* `airbrake_notify` (default: true) Enables/disables error notifications via Airbrake.
|
@@ -59,7 +59,11 @@ If you want to use rapns in environments other than development or production, y
|
|
59
59
|
|
60
60
|
### Options
|
61
61
|
|
62
|
-
* `--foreground` will prevent rapns from forking into a daemon.
|
62
|
+
* `--foreground` will prevent rapns from forking into a daemon.
|
63
|
+
|
64
|
+
## Logging
|
65
|
+
|
66
|
+
rapns logs activity to `rapns.log` in your Rails log directory. This is also printed to STDOUT when running in the foreground. When running as a daemon rapns does not print to STDOUT or STDERR.
|
63
67
|
|
64
68
|
## Sending a Notification
|
65
69
|
|
@@ -74,7 +78,7 @@ If you want to use rapns in environments other than development or production, y
|
|
74
78
|
n.save!
|
75
79
|
|
76
80
|
* `sound` defaults to `1.aiff`. You can either set it to a custom .aiff file, or `nil` for no sound.
|
77
|
-
* `expiry` is the time in seconds the APNs will spend trying to deliver the notification to the device. The notification is discarded if it has not been delivered in this time. Default is 1 day.
|
81
|
+
* `expiry` is the time in seconds the APNs (not rapns) will spend trying to deliver the notification to the device. The notification is discarded if it has not been delivered in this time. Default is 1 day.
|
78
82
|
* `attributes_for_device` is the `NSDictionary` argument passed to your iOS app in either `didFinishLaunchingWithOptions` or `didReceiveRemoteNotification`.
|
79
83
|
* `deliver_after` is not required, but may be set if you'd like to delay delivery of the notification to a specific time in the future.
|
80
84
|
|
@@ -109,4 +113,4 @@ Fork as usual and go crazy!
|
|
109
113
|
|
110
114
|
When running specs, please note that the ActiveRecord adapter can be changed by setting the `ADAPTER` environment variable. For example: `ADAPTER=postgresql rake`.
|
111
115
|
|
112
|
-
Available adapters for testing are
|
116
|
+
Available adapters for testing are `mysql`, `mysql2` and `postgresql`.
|
@@ -39,7 +39,7 @@ module Rapns
|
|
39
39
|
@ssl_socket.flush
|
40
40
|
|
41
41
|
check_for_error
|
42
|
-
rescue Errno::EPIPE => e
|
42
|
+
rescue Errno::EPIPE, OpenSSL::SSL::SSLError => e
|
43
43
|
Rapns::Daemon.logger.error("[#{@name}] Lost connection to #{Rapns::Daemon.configuration.host}:#{Rapns::Daemon.configuration.port}, reconnecting...")
|
44
44
|
@tcp_socket, @ssl_socket = connect_socket
|
45
45
|
|
@@ -4,7 +4,7 @@ module Rapns
|
|
4
4
|
STOP = 0x666
|
5
5
|
|
6
6
|
def start
|
7
|
-
|
7
|
+
Thread.new do
|
8
8
|
loop do
|
9
9
|
break if @stop
|
10
10
|
handle_next_notification
|
@@ -18,32 +18,39 @@ module Rapns
|
|
18
18
|
|
19
19
|
protected
|
20
20
|
|
21
|
+
def deliver(notification)
|
22
|
+
Rapns::Daemon.connection_pool.claim_connection do |connection|
|
23
|
+
begin
|
24
|
+
connection.write(notification.to_binary)
|
25
|
+
|
26
|
+
notification.delivered = true
|
27
|
+
notification.delivered_at = Time.now
|
28
|
+
notification.save!(:validate => false)
|
29
|
+
|
30
|
+
Rapns::Daemon.logger.info("Notification #{notification.id} delivered to #{notification.device_token}")
|
31
|
+
rescue Rapns::DeliveryError => error
|
32
|
+
handle_delivery_error(notification, error)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def handle_delivery_error(notification, error)
|
38
|
+
Rapns::Daemon.logger.error(error)
|
39
|
+
|
40
|
+
notification.delivered = false
|
41
|
+
notification.delivered_at = nil
|
42
|
+
notification.failed = true
|
43
|
+
notification.failed_at = Time.now
|
44
|
+
notification.error_code = error.code
|
45
|
+
notification.error_description = error.description
|
46
|
+
notification.save!(:validate => false)
|
47
|
+
end
|
48
|
+
|
21
49
|
def handle_next_notification
|
22
50
|
notification = Rapns::Daemon.delivery_queue.pop
|
23
51
|
begin
|
24
52
|
return if notification == STOP
|
25
|
-
|
26
|
-
Rapns::Daemon.connection_pool.claim_connection do |connection|
|
27
|
-
begin
|
28
|
-
connection.write(notification.to_binary)
|
29
|
-
|
30
|
-
notification.delivered = true
|
31
|
-
notification.delivered_at = Time.now
|
32
|
-
notification.save!(:validate => false)
|
33
|
-
|
34
|
-
Rapns::Daemon.logger.info("Notification #{notification.id} delivered to #{notification.device_token}")
|
35
|
-
rescue Rapns::DeliveryError => error
|
36
|
-
Rapns::Daemon.logger.error(error)
|
37
|
-
|
38
|
-
notification.delivered = false
|
39
|
-
notification.delivered_at = nil
|
40
|
-
notification.failed = true
|
41
|
-
notification.failed_at = Time.now
|
42
|
-
notification.error_code = error.code
|
43
|
-
notification.error_description = error.description
|
44
|
-
notification.save!(:validate => false)
|
45
|
-
end
|
46
|
-
end
|
53
|
+
deliver(notification)
|
47
54
|
rescue StandardError => e
|
48
55
|
Rapns::Daemon.logger.error(e)
|
49
56
|
ensure
|
@@ -4,8 +4,8 @@ module Rapns
|
|
4
4
|
def initialize(num_handlers)
|
5
5
|
@num_handlers = num_handlers
|
6
6
|
@queue = Queue.new
|
7
|
-
@
|
8
|
-
@
|
7
|
+
@feeder = nil
|
8
|
+
@handler_mutex = Mutex.new
|
9
9
|
end
|
10
10
|
|
11
11
|
def push(obj)
|
@@ -17,7 +17,7 @@ module Rapns
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def handler_available
|
20
|
-
@
|
20
|
+
@handler_mutex.synchronize do
|
21
21
|
signal_feeder if handler_available?
|
22
22
|
end
|
23
23
|
end
|
@@ -28,19 +28,16 @@ module Rapns
|
|
28
28
|
|
29
29
|
def signal_feeder
|
30
30
|
begin
|
31
|
-
|
32
|
-
t.wakeup if t
|
31
|
+
@feeder.wakeup if @feeder
|
33
32
|
rescue ThreadError
|
34
33
|
retry
|
35
34
|
end
|
36
35
|
end
|
37
36
|
|
38
37
|
def wait_for_available_handler
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
Thread.stop
|
43
|
-
end
|
38
|
+
if !handler_available?
|
39
|
+
@feeder = Thread.current
|
40
|
+
@feeder.stop
|
44
41
|
end
|
45
42
|
end
|
46
43
|
end
|
data/lib/rapns/daemon/feeder.rb
CHANGED
@@ -7,13 +7,21 @@ ADAPTER_ERRORS = [PGError, Mysql::Error, Mysql2::Error]
|
|
7
7
|
module Rapns
|
8
8
|
module Daemon
|
9
9
|
class Feeder
|
10
|
-
def self.start
|
10
|
+
def self.start(foreground)
|
11
|
+
connect unless foreground
|
12
|
+
|
11
13
|
loop do
|
12
14
|
break if @stop
|
13
15
|
enqueue_notifications
|
14
16
|
end
|
15
17
|
end
|
16
18
|
|
19
|
+
def self.stop
|
20
|
+
@stop = true
|
21
|
+
end
|
22
|
+
|
23
|
+
protected
|
24
|
+
|
17
25
|
def self.enqueue_notifications
|
18
26
|
begin
|
19
27
|
Rapns::Notification.ready_for_delivery.each do |notification|
|
@@ -31,27 +39,36 @@ module Rapns
|
|
31
39
|
sleep Rapns::Daemon.configuration.poll
|
32
40
|
end
|
33
41
|
|
34
|
-
def self.stop
|
35
|
-
@stop = true
|
36
|
-
end
|
37
|
-
|
38
42
|
def self.reconnect
|
39
43
|
Rapns::Daemon.logger.warn('Lost connection to database, reconnecting...')
|
40
44
|
attempts = 0
|
41
45
|
loop do
|
42
46
|
begin
|
43
47
|
Rapns::Daemon.logger.warn("Attempt #{attempts += 1}")
|
44
|
-
|
45
|
-
|
46
|
-
Rapns::Notification.count
|
48
|
+
connect
|
49
|
+
check_is_connected
|
47
50
|
break
|
48
51
|
rescue *ADAPTER_ERRORS => e
|
49
52
|
Rapns::Daemon.logger.error(e, :airbrake_notify => false)
|
50
|
-
|
53
|
+
sleep_to_avoid_thrashing
|
51
54
|
end
|
52
55
|
end
|
53
56
|
Rapns::Daemon.logger.warn('Database reconnected')
|
54
57
|
end
|
58
|
+
|
59
|
+
def self.connect
|
60
|
+
ActiveRecord::Base.clear_all_connections!
|
61
|
+
ActiveRecord::Base.establish_connection
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.check_is_connected
|
65
|
+
# Simple asking the adapter for the connection state is not sufficient.
|
66
|
+
Rapns::Notification.count
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.sleep_to_avoid_thrashing
|
70
|
+
sleep 2
|
71
|
+
end
|
55
72
|
end
|
56
73
|
end
|
57
74
|
end
|
data/lib/rapns/daemon/logger.rb
CHANGED
@@ -13,7 +13,7 @@ module Rapns
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def error(msg, options = {})
|
16
|
-
airbrake_notify(msg) if
|
16
|
+
airbrake_notify(msg) if notify_via_airbrake?(msg, options)
|
17
17
|
log(:error, msg, 'ERROR')
|
18
18
|
end
|
19
19
|
|
@@ -44,6 +44,10 @@ module Rapns
|
|
44
44
|
HoptoadNotifier.notify(e)
|
45
45
|
end
|
46
46
|
end
|
47
|
+
|
48
|
+
def notify_via_airbrake?(msg, options)
|
49
|
+
msg.is_a?(Exception) && options[:airbrake_notify] != false
|
50
|
+
end
|
47
51
|
end
|
48
52
|
end
|
49
53
|
end
|
data/lib/rapns/daemon.rb
CHANGED
@@ -36,10 +36,7 @@ module Rapns
|
|
36
36
|
|
37
37
|
self.delivery_queue = DeliveryQueue.new(configuration.connections)
|
38
38
|
|
39
|
-
unless foreground?
|
40
|
-
daemonize
|
41
|
-
ActiveRecord::Base.establish_connection
|
42
|
-
end
|
39
|
+
daemonize unless foreground?
|
43
40
|
|
44
41
|
write_pid_file
|
45
42
|
|
@@ -49,8 +46,8 @@ module Rapns
|
|
49
46
|
self.connection_pool = ConnectionPool.new(configuration.connections)
|
50
47
|
connection_pool.populate
|
51
48
|
|
52
|
-
logger.info(
|
53
|
-
Feeder.start
|
49
|
+
logger.info('Ready')
|
50
|
+
Feeder.start(foreground?)
|
54
51
|
end
|
55
52
|
|
56
53
|
protected
|
data/lib/rapns/version.rb
CHANGED
@@ -160,6 +160,51 @@ describe Rapns::Daemon::Connection, "when the connection is lost" do
|
|
160
160
|
end
|
161
161
|
end
|
162
162
|
|
163
|
+
describe Rapns::Daemon::Connection, "when an SSL error occurs during write" do
|
164
|
+
before do
|
165
|
+
@connection = Rapns::Daemon::Connection.new("Connection 1")
|
166
|
+
@ssl_socket = mock("SSLSocket")
|
167
|
+
@connection.instance_variable_set("@ssl_socket", @ssl_socket)
|
168
|
+
@connection.stub(:connect_socket).and_return([mock("TCPSocket"), @ssl_socket])
|
169
|
+
@ssl_socket.stub(:write).and_raise(OpenSSL::SSL::SSLError)
|
170
|
+
@logger = mock("Logger", :error => nil)
|
171
|
+
Rapns::Daemon.stub(:logger).and_return(@logger)
|
172
|
+
@connection.stub(:sleep)
|
173
|
+
configuration = mock("Configuration", :host => "localhost", :port => 123)
|
174
|
+
Rapns::Daemon.stub(:configuration).and_return(configuration)
|
175
|
+
end
|
176
|
+
|
177
|
+
it "should log a error" do
|
178
|
+
Rapns::Daemon.logger.should_receive(:error).with("[Connection 1] Lost connection to localhost:123, reconnecting...")
|
179
|
+
begin
|
180
|
+
@connection.write(nil)
|
181
|
+
rescue Rapns::Daemon::ConnectionError
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
it "should retry to make a connection 3 times" do
|
186
|
+
@connection.should_receive(:connect_socket).exactly(3).times
|
187
|
+
begin
|
188
|
+
@connection.write(nil)
|
189
|
+
rescue Rapns::Daemon::ConnectionError
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
it "should raise a ConnectionError after 3 attempts at reconnecting" do
|
194
|
+
expect do
|
195
|
+
@connection.write(nil)
|
196
|
+
end.to raise_error(Rapns::Daemon::ConnectionError, "Connection 1 tried 3 times to reconnect but failed: #<OpenSSL::SSL::SSLError: OpenSSL::SSL::SSLError>")
|
197
|
+
end
|
198
|
+
|
199
|
+
it "should sleep 1 second before retrying the connection" do
|
200
|
+
@connection.should_receive(:sleep).with(1)
|
201
|
+
begin
|
202
|
+
@connection.write(nil)
|
203
|
+
rescue Rapns::Daemon::ConnectionError
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
163
208
|
describe Rapns::Daemon::Connection, "when sending a notification" do
|
164
209
|
before do
|
165
210
|
@connection = Rapns::Daemon::Connection.new("Connection 1")
|
@@ -11,6 +11,18 @@ describe Rapns::Daemon::Feeder do
|
|
11
11
|
Rapns::Daemon.stub(:configuration => mock("Configuration", :poll => 2))
|
12
12
|
end
|
13
13
|
|
14
|
+
it "should reconnect to the database when daemonized" do
|
15
|
+
Rapns::Daemon::Feeder.stub(:loop)
|
16
|
+
ActiveRecord::Base.should_receive(:establish_connection)
|
17
|
+
Rapns::Daemon::Feeder.start(false)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should not reconnect to the database when running in the foreground" do
|
21
|
+
Rapns::Daemon::Feeder.stub(:loop)
|
22
|
+
ActiveRecord::Base.should_not_receive(:establish_connection)
|
23
|
+
Rapns::Daemon::Feeder.start(true)
|
24
|
+
end
|
25
|
+
|
14
26
|
it "should enqueue an undelivered notification" do
|
15
27
|
@notification.update_attributes!(:delivered => false)
|
16
28
|
Rapns::Daemon.delivery_queue.should_receive(:push)
|
data/spec/rapns/daemon_spec.rb
CHANGED
@@ -87,12 +87,6 @@ describe Rapns::Daemon, "when starting" do
|
|
87
87
|
Rapns::Daemon.start("development", false)
|
88
88
|
end
|
89
89
|
|
90
|
-
it "should re-establish the connection to the database after being forked" do
|
91
|
-
ActiveRecord::Base.should_receive(:establish_connection)
|
92
|
-
Rapns::Daemon.stub(:daemonize)
|
93
|
-
Rapns::Daemon.start("development", false)
|
94
|
-
end
|
95
|
-
|
96
90
|
it "should not fork a child process if the foreground option is true" do
|
97
91
|
Rapns::Daemon.should_not_receive(:daemonize)
|
98
92
|
Rapns::Daemon.start("development", true)
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rapns
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2011-
|
12
|
+
date: 2011-11-04 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: Easy to use library for Apple's Push Notification Service with Rails
|
15
15
|
3
|