rapns 0.1.3 → 0.1.4
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +1 -0
- data/lib/generators/templates/rapns.yml +2 -1
- data/lib/rapns/daemon/configuration.rb +12 -1
- data/lib/rapns/daemon/connection.rb +2 -2
- data/lib/rapns/daemon/delivery_handler.rb +1 -1
- data/lib/rapns/daemon/delivery_queue.rb +4 -7
- data/lib/rapns/daemon/feeder.rb +1 -1
- data/lib/rapns/daemon.rb +32 -5
- data/lib/rapns/version.rb +1 -1
- data/lib/rapns.rb +4 -4
- data/spec/rapns/daemon/configuration_spec.rb +25 -3
- data/spec/rapns/daemon/connection_spec.rb +4 -4
- data/spec/rapns/daemon/delivery_handler_spec.rb +5 -0
- data/spec/rapns/daemon/feeder_spec.rb +2 -2
- data/spec/rapns/daemon_spec.rb +28 -1
- data/spec/spec_helper.rb +5 -0
- metadata +4 -4
data/README.md
CHANGED
@@ -50,6 +50,7 @@ If you want to use rapns in environments other than development or production, y
|
|
50
50
|
* `airbrake_notify` (default: true) Enables/disables error notifications via Airbrake.
|
51
51
|
* `poll` (default: 2) Frequency in seconds to check for new notifications to deliver.
|
52
52
|
* `connections` (default: 3) the number of connections to keep open to the APNs. Consider increasing this if you are sending a very large number of notifications.
|
53
|
+
* `pid_file` (default: blank) the file that rapns will write its process ID to. Paths are relative to your project's RAILS_ROOT unless an absolute path is given.
|
53
54
|
|
54
55
|
## Starting the rapns Daemon
|
55
56
|
|
@@ -5,7 +5,7 @@ module Rapns
|
|
5
5
|
|
6
6
|
module Daemon
|
7
7
|
class Configuration
|
8
|
-
attr_accessor :host, :port, :certificate, :certificate_password, :poll, :airbrake_notify, :connections
|
8
|
+
attr_accessor :host, :port, :certificate, :certificate_password, :poll, :airbrake_notify, :connections, :pid_file
|
9
9
|
alias_method :airbrake_notify?, :airbrake_notify
|
10
10
|
|
11
11
|
def initialize(environment, config_path)
|
@@ -24,6 +24,7 @@ module Rapns
|
|
24
24
|
set_variable(:certificate_password, config, :optional => true, :default => "")
|
25
25
|
set_variable(:poll, config, :optional => true, :default => 2)
|
26
26
|
set_variable(:connections, config, :optional => true, :default => 3)
|
27
|
+
set_variable(:pid_file, config, :optional => true, :default => "")
|
27
28
|
end
|
28
29
|
|
29
30
|
def certificate
|
@@ -34,6 +35,16 @@ module Rapns
|
|
34
35
|
end
|
35
36
|
end
|
36
37
|
|
38
|
+
def pid_file
|
39
|
+
return if @pid_file.blank?
|
40
|
+
|
41
|
+
if Pathname.new(@pid_file).absolute?
|
42
|
+
@pid_file
|
43
|
+
else
|
44
|
+
File.join(Rails.root, @pid_file)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
37
48
|
protected
|
38
49
|
|
39
50
|
def read_config
|
@@ -40,7 +40,7 @@ module Rapns
|
|
40
40
|
|
41
41
|
check_for_error
|
42
42
|
rescue Errno::EPIPE => e
|
43
|
-
Rapns::Daemon.logger.
|
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
|
|
46
46
|
retry_count += 1
|
@@ -70,7 +70,7 @@ module Rapns
|
|
70
70
|
end
|
71
71
|
|
72
72
|
begin
|
73
|
-
Rapns::Daemon.logger.
|
73
|
+
Rapns::Daemon.logger.error("[#{@name}] Error received, reconnecting...")
|
74
74
|
close
|
75
75
|
@tcp_socket, @ssl_socket = connect_socket
|
76
76
|
ensure
|
@@ -5,11 +5,9 @@ module Rapns
|
|
5
5
|
@queue = Queue.new
|
6
6
|
@waiting_threads = []
|
7
7
|
@mutex = Mutex.new
|
8
|
-
@counter = 0
|
9
8
|
end
|
10
9
|
|
11
10
|
def push(obj)
|
12
|
-
@mutex.synchronize { @counter += 1 }
|
13
11
|
@queue.push(obj)
|
14
12
|
end
|
15
13
|
|
@@ -17,11 +15,10 @@ module Rapns
|
|
17
15
|
@queue.pop
|
18
16
|
end
|
19
17
|
|
20
|
-
def
|
18
|
+
def signal_waiters_if_empty
|
21
19
|
@mutex.synchronize do
|
22
20
|
begin
|
23
|
-
@
|
24
|
-
if @counter <= 0
|
21
|
+
if @queue.size == 0
|
25
22
|
t = @waiting_threads.shift
|
26
23
|
t.wakeup if t
|
27
24
|
end
|
@@ -31,9 +28,9 @@ module Rapns
|
|
31
28
|
end
|
32
29
|
end
|
33
30
|
|
34
|
-
def
|
31
|
+
def wait_until_empty
|
35
32
|
Thread.exclusive do
|
36
|
-
if @
|
33
|
+
if @queue.size > 0
|
37
34
|
@waiting_threads << Thread.current
|
38
35
|
Thread.stop
|
39
36
|
end
|
data/lib/rapns/daemon/feeder.rb
CHANGED
data/lib/rapns/daemon.rb
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
require 'thread'
|
2
|
+
require 'socket'
|
3
|
+
require 'pathname'
|
4
|
+
|
1
5
|
require "rapns/daemon/configuration"
|
2
6
|
require "rapns/daemon/certificate"
|
3
7
|
require "rapns/daemon/delivery_error"
|
@@ -37,6 +41,8 @@ module Rapns
|
|
37
41
|
ActiveRecord::Base.establish_connection
|
38
42
|
end
|
39
43
|
|
44
|
+
write_pid_file
|
45
|
+
|
40
46
|
self.delivery_handler_pool = DeliveryHandlerPool.new(configuration.connections)
|
41
47
|
delivery_handler_pool.populate
|
42
48
|
|
@@ -49,19 +55,27 @@ module Rapns
|
|
49
55
|
protected
|
50
56
|
|
51
57
|
def self.setup_signal_hooks
|
52
|
-
@
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
58
|
+
@shutting_down = false
|
59
|
+
|
60
|
+
["SIGINT", "SIGTERM"].each do |signal|
|
61
|
+
Signal.trap(signal) do
|
62
|
+
handle_shutdown_signal
|
63
|
+
end
|
57
64
|
end
|
58
65
|
end
|
59
66
|
|
67
|
+
def self.handle_shutdown_signal
|
68
|
+
exit 1 if @shutting_down
|
69
|
+
@shutting_down = true
|
70
|
+
shutdown
|
71
|
+
end
|
72
|
+
|
60
73
|
def self.shutdown
|
61
74
|
puts "\nShutting down..."
|
62
75
|
Rapns::Daemon::Feeder.stop
|
63
76
|
Rapns::Daemon.delivery_handler_pool.drain if Rapns::Daemon.delivery_handler_pool
|
64
77
|
Rapns::Daemon.connection_pool.drain if Rapns::Daemon.connection_pool
|
78
|
+
delete_pid_file
|
65
79
|
end
|
66
80
|
|
67
81
|
def self.daemonize
|
@@ -76,5 +90,18 @@ module Rapns
|
|
76
90
|
STDOUT.reopen '/dev/null', 'a'
|
77
91
|
STDERR.reopen STDOUT
|
78
92
|
end
|
93
|
+
|
94
|
+
def self.write_pid_file
|
95
|
+
if !configuration.pid_file.blank?
|
96
|
+
File.open(configuration.pid_file, "w") do |f|
|
97
|
+
f.puts $$
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def self.delete_pid_file
|
103
|
+
pid_file = configuration.pid_file
|
104
|
+
File.delete(pid_file) if !pid_file.blank? && File.exists?(pid_file)
|
105
|
+
end
|
79
106
|
end
|
80
107
|
end
|
data/lib/rapns/version.rb
CHANGED
data/lib/rapns.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
require
|
1
|
+
require 'active_record'
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
3
|
+
require 'rapns/version'
|
4
|
+
require 'rapns/binary_notification_validator'
|
5
|
+
require 'rapns/notification'
|
@@ -5,7 +5,8 @@ describe Rapns::Daemon::Configuration do
|
|
5
5
|
end
|
6
6
|
|
7
7
|
before do
|
8
|
-
|
8
|
+
Rails.stub(:root).and_return("/rails_root")
|
9
|
+
@config = {"port" => 123, "host" => "localhost", "certificate" => "production.pem", "certificate_password" => "abc123", "airbrake_notify" => false, "poll" => 4, "connections" => 6, "pid_file" => "rapns.pid"}
|
9
10
|
end
|
10
11
|
|
11
12
|
it "should raise an error if the configuration file does not exist" do
|
@@ -107,7 +108,6 @@ describe Rapns::Daemon::Configuration do
|
|
107
108
|
end
|
108
109
|
|
109
110
|
it "should set the certificate, with absolute path" do
|
110
|
-
Rails.stub(:root).and_return("/rails_root")
|
111
111
|
configuration = Rapns::Daemon::Configuration.new("production", "/some/config.yml")
|
112
112
|
configuration.stub(:read_config).and_return({"production" => @config})
|
113
113
|
configuration.load
|
@@ -115,11 +115,33 @@ describe Rapns::Daemon::Configuration do
|
|
115
115
|
end
|
116
116
|
|
117
117
|
it "should keep the absolute path of the certificate if it has one" do
|
118
|
-
Rails.stub(:root).and_return("/rails_root")
|
119
118
|
@config["certificate"] = "/different_path/to/production.pem"
|
120
119
|
configuration = Rapns::Daemon::Configuration.new("production", "/some/config.yml")
|
121
120
|
configuration.stub(:read_config).and_return({"production" => @config})
|
122
121
|
configuration.load
|
123
122
|
configuration.certificate.should == "/different_path/to/production.pem"
|
124
123
|
end
|
124
|
+
|
125
|
+
it "should set the PID file path" do
|
126
|
+
configuration = Rapns::Daemon::Configuration.new("production", "/some/config.yml")
|
127
|
+
configuration.stub(:read_config).and_return({"production" => @config})
|
128
|
+
configuration.load
|
129
|
+
configuration.pid_file.should == "/rails_root/rapns.pid"
|
130
|
+
end
|
131
|
+
|
132
|
+
it "should keep the absolute path of the PID file if it has one" do
|
133
|
+
@config["pid_file"] = "/some/absolue/path/rapns.pid"
|
134
|
+
configuration = Rapns::Daemon::Configuration.new("production", "/some/config.yml")
|
135
|
+
configuration.stub(:read_config).and_return({"production" => @config})
|
136
|
+
configuration.load
|
137
|
+
configuration.pid_file.should == "/some/absolue/path/rapns.pid"
|
138
|
+
end
|
139
|
+
|
140
|
+
it "should return nil if no PID file was set" do
|
141
|
+
@config["pid_file"] = ""
|
142
|
+
configuration = Rapns::Daemon::Configuration.new("production", "/some/config.yml")
|
143
|
+
configuration.stub(:read_config).and_return({"production" => @config})
|
144
|
+
configuration.load
|
145
|
+
configuration.pid_file.should be_nil
|
146
|
+
end
|
125
147
|
end
|
@@ -122,15 +122,15 @@ describe Rapns::Daemon::Connection, "when the connection is lost" do
|
|
122
122
|
@connection.instance_variable_set("@ssl_socket", @ssl_socket)
|
123
123
|
@connection.stub(:connect_socket).and_return([mock("TCPSocket"), @ssl_socket])
|
124
124
|
@ssl_socket.stub(:write).and_raise(Errno::EPIPE)
|
125
|
-
@logger = mock("Logger", :
|
125
|
+
@logger = mock("Logger", :error => nil)
|
126
126
|
Rapns::Daemon.stub(:logger).and_return(@logger)
|
127
127
|
@connection.stub(:sleep)
|
128
128
|
configuration = mock("Configuration", :host => "localhost", :port => 123)
|
129
129
|
Rapns::Daemon.stub(:configuration).and_return(configuration)
|
130
130
|
end
|
131
131
|
|
132
|
-
it "should log a
|
133
|
-
Rapns::Daemon.logger.should_receive(:
|
132
|
+
it "should log a error" do
|
133
|
+
Rapns::Daemon.logger.should_receive(:error).with("[Connection 1] Lost connection to localhost:123, reconnecting...")
|
134
134
|
begin
|
135
135
|
@connection.write(nil)
|
136
136
|
rescue Rapns::Daemon::ConnectionError
|
@@ -248,7 +248,7 @@ describe Rapns::Daemon::Connection, "when receiving an error packet" do
|
|
248
248
|
end
|
249
249
|
|
250
250
|
it "should log that the connection is being reconnected" do
|
251
|
-
Rapns::Daemon.logger.should_receive(:
|
251
|
+
Rapns::Daemon.logger.should_receive(:error).with("[Connection 1] Error received, reconnecting...")
|
252
252
|
begin
|
253
253
|
@connection.write("msg with an error")
|
254
254
|
rescue Rapns::DeliveryError
|
@@ -59,6 +59,11 @@ describe Rapns::Daemon::DeliveryHandler do
|
|
59
59
|
@delivery_handler.send(:handle_next_notification)
|
60
60
|
end
|
61
61
|
|
62
|
+
it "should signal the feeder if the delivery queue is empty" do
|
63
|
+
Rapns::Daemon.delivery_queue.should_receive(:signal_waiters_if_empty)
|
64
|
+
@delivery_handler.send(:handle_next_notification)
|
65
|
+
end
|
66
|
+
|
62
67
|
describe "when delivery fails" do
|
63
68
|
before do
|
64
69
|
@error = Rapns::DeliveryError.new(4, "Missing payload", 1)
|
@@ -6,7 +6,7 @@ describe Rapns::Daemon::Feeder do
|
|
6
6
|
@notification = Rapns::Notification.create!(:device_token => "a" * 64)
|
7
7
|
@logger = mock("Logger", :info => nil, :error => nil)
|
8
8
|
Rapns::Daemon.stub(:logger).and_return(@logger)
|
9
|
-
@queue = mock(:push => nil, :
|
9
|
+
@queue = mock(:push => nil, :wait_until_empty => nil)
|
10
10
|
Rapns::Daemon.stub(:delivery_queue).and_return(@queue)
|
11
11
|
Rapns::Daemon.stub(:configuration => mock("Configuration", :poll => 2))
|
12
12
|
end
|
@@ -53,7 +53,7 @@ describe Rapns::Daemon::Feeder do
|
|
53
53
|
end
|
54
54
|
|
55
55
|
it "should wait for the delivery queue to be emptied" do
|
56
|
-
Rapns::Daemon.delivery_queue.should_receive(:
|
56
|
+
Rapns::Daemon.delivery_queue.should_receive(:wait_until_empty)
|
57
57
|
Rapns::Daemon::Feeder.enqueue_notifications
|
58
58
|
end
|
59
59
|
|
data/spec/rapns/daemon_spec.rb
CHANGED
@@ -8,7 +8,7 @@ describe Rapns::Daemon, "when starting" do
|
|
8
8
|
Rails.stub(:root).and_return("/rails_root")
|
9
9
|
|
10
10
|
@configuration = Rapns::Daemon::Configuration.new("development", "/rails_root/config/rapns/rapns.yml")
|
11
|
-
@configuration.stub(:read_config).and_return({"development" => {"port" => 123, "host" => "localhost", "certificate" => "development.pem", "certificate_password" => "abc123"}})
|
11
|
+
@configuration.stub(:read_config).and_return({"development" => {"port" => 123, "host" => "localhost", "certificate" => "development.pem", "certificate_password" => "abc123", "pid_file" => "rapns.pid"}})
|
12
12
|
Rapns::Daemon::Configuration.stub(:new).and_return(@configuration)
|
13
13
|
|
14
14
|
@certificate = Rapns::Daemon::Certificate.new("/rails_root/config/rapns/development.pem")
|
@@ -26,6 +26,7 @@ describe Rapns::Daemon, "when starting" do
|
|
26
26
|
Rapns::Daemon::Feeder.stub(:start)
|
27
27
|
Rapns::Daemon::Feeder.stub(:wait)
|
28
28
|
Rapns::Daemon.stub(:daemonize)
|
29
|
+
Rapns::Daemon.stub(:write_pid_file)
|
29
30
|
@logger = mock("Logger")
|
30
31
|
Rapns::Daemon::Logger.stub(:new).and_return(@logger)
|
31
32
|
end
|
@@ -92,6 +93,11 @@ describe Rapns::Daemon, "when starting" do
|
|
92
93
|
Rapns::Daemon.start("development", true)
|
93
94
|
end
|
94
95
|
|
96
|
+
it "should write the process ID to the PID file" do
|
97
|
+
Rapns::Daemon.should_receive(:write_pid_file)
|
98
|
+
Rapns::Daemon.start("development", {})
|
99
|
+
end
|
100
|
+
|
95
101
|
it "should start the feeder" do
|
96
102
|
Rapns::Daemon::Feeder.should_receive(:start)
|
97
103
|
Rapns::Daemon.start("development", true)
|
@@ -111,11 +117,14 @@ end
|
|
111
117
|
|
112
118
|
describe Rapns::Daemon, "when being shutdown" do
|
113
119
|
before do
|
120
|
+
Rails.stub(:root).and_return("/rails_root")
|
114
121
|
Rapns::Daemon::Feeder.stub(:stop)
|
115
122
|
@connection_pool = mock("ConnectionPool", :drain => nil)
|
116
123
|
Rapns::Daemon.stub(:connection_pool).and_return(@connection_pool)
|
117
124
|
@handler_pool = mock("DeliveryHandlerPool", :drain => nil)
|
118
125
|
Rapns::Daemon.stub(:delivery_handler_pool).and_return(@handler_pool)
|
126
|
+
@configuration = mock("Configuration", :pid_file => File.join(Rails.root, "rapns.pid"))
|
127
|
+
Rapns::Daemon.stub(:configuration).and_return(@configuration)
|
119
128
|
Rapns::Daemon.stub(:puts)
|
120
129
|
end
|
121
130
|
|
@@ -145,4 +154,22 @@ describe Rapns::Daemon, "when being shutdown" do
|
|
145
154
|
@handler_pool.should_not_receive(:drain)
|
146
155
|
Rapns::Daemon.send(:shutdown)
|
147
156
|
end
|
157
|
+
|
158
|
+
it "should remove the PID file if one was written" do
|
159
|
+
File.stub(:exists?).and_return(true)
|
160
|
+
File.should_receive(:delete).with("/rails_root/rapns.pid")
|
161
|
+
Rapns::Daemon.send(:shutdown)
|
162
|
+
end
|
163
|
+
|
164
|
+
it "should not attempt to remove the PID file if it does not exist" do
|
165
|
+
File.stub(:exists?).and_return(false)
|
166
|
+
File.should_not_receive(:delete)
|
167
|
+
Rapns::Daemon.send(:shutdown)
|
168
|
+
end
|
169
|
+
|
170
|
+
it "should not remove the PID file if one was not written" do
|
171
|
+
@configuration.stub(:pid_file).and_return(nil)
|
172
|
+
File.should_not_receive(:delete)
|
173
|
+
Rapns::Daemon.send(:shutdown)
|
174
|
+
end
|
148
175
|
end
|
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.1.
|
4
|
+
version: 0.1.4
|
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-10-08 00:00:00.000000000Z
|
13
13
|
dependencies: []
|
14
14
|
description: Easy to use library for Apple's Push Notification Service with Rails
|
15
15
|
3
|
@@ -53,7 +53,7 @@ files:
|
|
53
53
|
- spec/rapns/notification_spec.rb
|
54
54
|
- spec/spec_helper.rb
|
55
55
|
- bin/rapns
|
56
|
-
homepage:
|
56
|
+
homepage: https://github.com/ileitch/rapns
|
57
57
|
licenses: []
|
58
58
|
post_install_message:
|
59
59
|
rdoc_options: []
|
@@ -73,7 +73,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
73
73
|
version: '0'
|
74
74
|
requirements: []
|
75
75
|
rubyforge_project:
|
76
|
-
rubygems_version: 1.8.
|
76
|
+
rubygems_version: 1.8.10
|
77
77
|
signing_key:
|
78
78
|
specification_version: 3
|
79
79
|
summary: Easy to use library for Apple's Push Notification Service with Rails 3
|