rapns 1.0.7 → 2.0.0rc1

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.
Files changed (39) hide show
  1. data/CHANGELOG.md +7 -0
  2. data/LICENSE +7 -0
  3. data/README.md +58 -41
  4. data/bin/rapns +23 -5
  5. data/lib/generators/rapns_generator.rb +2 -4
  6. data/lib/generators/templates/add_app_to_rapns.rb +11 -0
  7. data/lib/generators/templates/create_rapns_apps.rb +15 -0
  8. data/lib/rapns/app.rb +9 -0
  9. data/lib/rapns/daemon/app_runner.rb +131 -0
  10. data/lib/rapns/daemon/connection.rb +5 -3
  11. data/lib/rapns/daemon/delivery_handler.rb +13 -15
  12. data/lib/rapns/daemon/delivery_handler_pool.rb +8 -10
  13. data/lib/rapns/daemon/delivery_queue.rb +36 -4
  14. data/lib/rapns/daemon/feedback_receiver.rb +19 -12
  15. data/lib/rapns/daemon/feeder.rb +8 -10
  16. data/lib/rapns/daemon/logger.rb +5 -3
  17. data/lib/rapns/daemon.rb +52 -38
  18. data/lib/rapns/notification.rb +16 -5
  19. data/lib/rapns/patches.rb +2 -2
  20. data/lib/rapns/version.rb +1 -1
  21. data/lib/rapns.rb +2 -1
  22. data/spec/rapns/daemon/app_runner_spec.rb +207 -0
  23. data/spec/rapns/daemon/connection_spec.rb +177 -236
  24. data/spec/rapns/daemon/delivery_handler_pool_spec.rb +10 -14
  25. data/spec/rapns/daemon/delivery_handler_spec.rb +92 -79
  26. data/spec/rapns/daemon/feedback_receiver_spec.rb +29 -23
  27. data/spec/rapns/daemon/feeder_spec.rb +40 -44
  28. data/spec/rapns/daemon/logger_spec.rb +21 -3
  29. data/spec/rapns/daemon_spec.rb +65 -125
  30. data/spec/rapns/notification_spec.rb +16 -0
  31. data/spec/spec_helper.rb +4 -1
  32. metadata +14 -15
  33. data/History.md +0 -5
  34. data/lib/generators/templates/rapns.yml +0 -31
  35. data/lib/rapns/daemon/certificate.rb +0 -27
  36. data/lib/rapns/daemon/configuration.rb +0 -98
  37. data/lib/rapns/daemon/pool.rb +0 -36
  38. data/spec/rapns/daemon/certificate_spec.rb +0 -22
  39. data/spec/rapns/daemon/configuration_spec.rb +0 -231
@@ -1,293 +1,234 @@
1
1
  require "spec_helper"
2
2
 
3
- describe Rapns::Daemon::Connection, "when setting up the SSL context" do
4
- before do
5
- @ssl_context = mock("SSLContext", :key= => nil, :cert= => nil)
6
- OpenSSL::SSL::SSLContext.should_receive(:new).and_return(@ssl_context)
7
- @rsa_key = mock("RSA public key")
8
- OpenSSL::PKey::RSA.stub(:new).and_return(@rsa_key)
9
- @certificate = mock("Certificate", :certificate => "certificate contents")
10
- Rapns::Daemon.stub(:certificate).and_return(@certificate)
11
- @x509_certificate = mock("X509 Certificate")
12
- OpenSSL::X509::Certificate.stub(:new).and_return(@x509_certificate)
13
- @connection = Rapns::Daemon::Connection.new('Connection 0', 'gateway.push.apple.com', 2195)
14
- @connection.stub(:connect_socket)
15
- @connection.stub(:setup_at_exit_hook)
16
- configuration = mock("Configuration", :certificate_password => "abc123")
17
- Rapns::Daemon.stub(:configuration).and_return(configuration)
18
- end
3
+ describe Rapns::Daemon::Connection do
4
+ let(:ssl_context) { stub(:key= => nil, :cert= => nil) }
5
+ let(:rsa_key) { stub }
6
+ let(:certificate) { stub }
7
+ let(:password) { stub }
8
+ let(:x509_certificate) { stub }
9
+ let(:host) { 'gateway.push.apple.com' }
10
+ let(:port) { '2195' }
11
+ let(:tcp_socket) { stub(:setsockopt => nil, :close => nil) }
12
+ let(:ssl_socket) { stub(:sync= => nil, :connect => nil, :close => nil, :write => nil, :flush => nil) }
13
+ let(:logger) { stub(:info => nil, :error => nil) }
14
+ let(:connection) { Rapns::Daemon::Connection.new('Connection 0', host, port, certificate, password) }
19
15
 
20
- it "should set the key on the context" do
21
- OpenSSL::PKey::RSA.should_receive(:new).with("certificate contents", "abc123").and_return(@rsa_key)
22
- @ssl_context.should_receive(:key=).with(@rsa_key)
23
- @connection.connect
24
- end
25
-
26
- it "should set the cert on the context" do
27
- OpenSSL::X509::Certificate.should_receive(:new).with("certificate contents").and_return(@x509_certificate)
28
- @ssl_context.should_receive(:cert=).with(@x509_certificate)
29
- @connection.connect
30
- end
31
- end
32
-
33
- describe Rapns::Daemon::Connection, "when connecting the socket" do
34
16
  before do
35
- @connection = Rapns::Daemon::Connection.new('Connection 0', 'gateway.push.apple.com', 2195)
36
- @connection.stub(:setup_at_exit_hook)
37
- @ssl_context = mock("SSLContext")
38
- @connection.stub(:setup_ssl_context).and_return(@ssl_context)
39
- @tcp_socket = mock("TCPSocket", :close => nil, :setsockopt => nil)
40
- TCPSocket.stub(:new).and_return(@tcp_socket)
41
- @ssl_socket = mock("SSLSocket", :sync= => nil, :connect => nil, :close => nil)
42
- OpenSSL::SSL::SSLSocket.stub(:new).and_return(@ssl_socket)
43
- @logger = mock("Logger", :info => nil)
44
- Rapns::Daemon.stub(:logger).and_return(@logger)
45
- end
46
-
47
- it "should create a TCP socket using the configured host and port" do
48
- TCPSocket.should_receive(:new).with('gateway.push.apple.com', 2195).and_return(@tcp_socket)
49
- @connection.connect
17
+ OpenSSL::SSL::SSLContext.stub(:new => ssl_context)
18
+ OpenSSL::PKey::RSA.stub(:new => rsa_key)
19
+ OpenSSL::X509::Certificate.stub(:new => x509_certificate)
20
+ TCPSocket.stub(:new => tcp_socket)
21
+ OpenSSL::SSL::SSLSocket.stub(:new => ssl_socket)
22
+ Rapns::Daemon.stub(:logger => logger)
50
23
  end
51
24
 
52
- it "should create a new SSL socket using the TCP socket and SSL context" do
53
- OpenSSL::SSL::SSLSocket.should_receive(:new).with(@tcp_socket, @ssl_context).and_return(@ssl_socket)
54
- @connection.connect
25
+ it "reads the number of bytes from the SSL socket" do
26
+ ssl_socket.should_receive(:read).with(123)
27
+ connection.connect
28
+ connection.read(123)
55
29
  end
56
30
 
57
- it "should set the sync option on the SSL socket" do
58
- @ssl_socket.should_receive(:sync=).with(true)
59
- @connection.connect
31
+ it "selects on the SSL socket until the given timeout" do
32
+ IO.should_receive(:select).with([ssl_socket], nil, nil, 10)
33
+ connection.connect
34
+ connection.select(10)
60
35
  end
61
36
 
62
- it "should connect the SSL socket" do
63
- @ssl_socket.should_receive(:connect)
64
- @connection.connect
65
- end
37
+ describe "when setting up the SSL context" do
38
+ it "sets the key on the context" do
39
+ OpenSSL::PKey::RSA.should_receive(:new).with(certificate, password).and_return(rsa_key)
40
+ ssl_context.should_receive(:key=).with(rsa_key)
41
+ connection.connect
42
+ end
66
43
 
67
- it "should set the socket option TCP_NODELAY" do
68
- @tcp_socket.should_receive(:setsockopt).with(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
69
- @connection.connect
44
+ it "sets the cert on the context" do
45
+ OpenSSL::X509::Certificate.should_receive(:new).with(certificate).and_return(x509_certificate)
46
+ ssl_context.should_receive(:cert=).with(x509_certificate)
47
+ connection.connect
48
+ end
70
49
  end
71
50
 
72
- it "should set the socket option SO_KEEPALIVE" do
73
- @tcp_socket.should_receive(:setsockopt).with(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, 1)
74
- @connection.connect
75
- end
76
- end
51
+ describe "when connecting the socket" do
52
+ it "creates a TCP socket using the configured host and port" do
53
+ TCPSocket.should_receive(:new).with(host, port).and_return(tcp_socket)
54
+ connection.connect
55
+ end
77
56
 
78
- describe Rapns::Daemon::Connection, "when shuting down the connection" do
79
- before do
80
- @connection = Rapns::Daemon::Connection.new('Connection 0', 'gateway.push.apple.com', 2195)
81
- @connection.stub(:setup_ssl_context)
82
- @ssl_socket = mock("SSLSocket", :close => nil)
83
- @tcp_socket = mock("TCPSocket", :close => nil)
84
- @connection.stub(:connect_socket).and_return([@tcp_socket, @ssl_socket])
85
- end
57
+ it "creates a new SSL socket using the TCP socket and SSL context" do
58
+ OpenSSL::SSL::SSLSocket.should_receive(:new).with(tcp_socket, ssl_context).and_return(ssl_socket)
59
+ connection.connect
60
+ end
86
61
 
87
- it "should close the TCP socket" do
88
- @connection.connect
89
- @tcp_socket.should_receive(:close)
90
- @connection.close
91
- end
62
+ it "sets the sync option on the SSL socket" do
63
+ ssl_socket.should_receive(:sync=).with(true)
64
+ connection.connect
65
+ end
92
66
 
93
- it "should attempt to close the TCP socket if it does not exist" do
94
- @connection.connect
95
- @tcp_socket.should_not_receive(:close)
96
- @connection.instance_variable_set("@tcp_socket", nil)
97
- @connection.close
98
- end
67
+ it "connects the SSL socket" do
68
+ ssl_socket.should_receive(:connect)
69
+ connection.connect
70
+ end
99
71
 
100
- it "should close the SSL socket" do
101
- @connection.connect
102
- @ssl_socket.should_receive(:close)
103
- @connection.close
104
- end
72
+ it "sets the socket option TCP_NODELAY" do
73
+ tcp_socket.should_receive(:setsockopt).with(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
74
+ connection.connect
75
+ end
105
76
 
106
- it "should attempt to close the SSL socket if it does not exist" do
107
- @connection.connect
108
- @ssl_socket.should_not_receive(:close)
109
- @connection.instance_variable_set("@ssl_socket", nil)
110
- @connection.close
77
+ it "sets the socket option SO_KEEPALIVE" do
78
+ tcp_socket.should_receive(:setsockopt).with(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, 1)
79
+ connection.connect
80
+ end
111
81
  end
112
82
 
113
- it "should ignore IOError when the socket is already closed" do
114
- @tcp_socket.stub(:close).and_raise(IOError)
115
- @connection.connect
116
- expect {@connection.close }.should_not raise_error(IOError)
117
- end
118
- end
83
+ describe "when shuting down the connection" do
84
+ it "closes the TCP socket" do
85
+ connection.connect
86
+ tcp_socket.should_receive(:close)
87
+ connection.close
88
+ end
119
89
 
120
- describe Rapns::Daemon::Connection, "read" do
121
- before do
122
- @connection = Rapns::Daemon::Connection.new('Connection 0', 'gateway.push.apple.com', 2195)
123
- @connection.stub(:setup_ssl_context)
124
- @ssl_socket = mock("SSLSocket", :close => nil)
125
- @tcp_socket = mock("TCPSocket", :close => nil)
126
- @connection.stub(:connect_socket).and_return([@tcp_socket, @ssl_socket])
127
- end
90
+ it "does not attempt to close the TCP socket if it is not connected" do
91
+ connection.connect
92
+ tcp_socket.should_not_receive(:close)
93
+ connection.instance_variable_set("@tcp_socket", nil)
94
+ connection.close
95
+ end
128
96
 
129
- it "reads the number of bytes from the SSL socket" do
130
- @ssl_socket.should_receive(:read).with(123)
131
- @connection.connect
132
- @connection.read(123)
133
- end
134
- end
97
+ it "closes the SSL socket" do
98
+ connection.connect
99
+ ssl_socket.should_receive(:close)
100
+ connection.close
101
+ end
135
102
 
136
- describe Rapns::Daemon::Connection, "select" do
137
- before do
138
- @connection = Rapns::Daemon::Connection.new('Connection 0', 'gateway.push.apple.com', 2195)
139
- @connection.stub(:setup_ssl_context)
140
- @ssl_socket = mock("SSLSocket", :close => nil)
141
- @tcp_socket = mock("TCPSocket", :close => nil)
142
- @connection.stub(:connect_socket).and_return([@tcp_socket, @ssl_socket])
143
- end
103
+ it "does not attempt to close the SSL socket if it is not connected" do
104
+ connection.connect
105
+ ssl_socket.should_not_receive(:close)
106
+ connection.instance_variable_set("@ssl_socket", nil)
107
+ connection.close
108
+ end
144
109
 
145
- it "selects on the SSL socket until the given timeout" do
146
- IO.should_receive(:select).with([@ssl_socket], nil, nil, 10)
147
- @connection.connect
148
- @connection.select(10)
110
+ it "ignores IOError when the socket is already closed" do
111
+ tcp_socket.stub(:close).and_raise(IOError)
112
+ connection.connect
113
+ expect { connection.close }.should_not raise_error(IOError)
114
+ end
149
115
  end
150
- end
151
116
 
152
- shared_examples_for "when the write fails" do
153
- before do
154
- @connection = Rapns::Daemon::Connection.new('Connection 0', 'gateway.push.apple.com', 2195)
155
- @logger = mock("Logger", :error => nil)
156
- Rapns::Daemon.stub(:logger).and_return(@logger)
157
- @connection.stub(:reconnect)
158
- @connection.stub(:write_data).and_raise(error_type)
159
- @connection.stub(:sleep)
160
- end
117
+ shared_examples_for "when the write fails" do
118
+ before do
119
+ connection.stub(:sleep)
120
+ connection.connect
121
+ ssl_socket.stub(:write).and_raise(error_type)
122
+ end
161
123
 
162
- it "should log that the connection has been lost once only" do
163
- Rapns::Daemon.logger.should_receive(:error).with("[Connection 0] Lost connection to gateway.push.apple.com:2195 (#{error_type.name}), reconnecting...").once
164
- begin
165
- @connection.write(nil)
166
- rescue Rapns::Daemon::ConnectionError
124
+ it "logs that the connection has been lost once only" do
125
+ logger.should_receive(:error).with("[Connection 0] Lost connection to gateway.push.apple.com:2195 (#{error_type.name}), reconnecting...").once
126
+ begin
127
+ connection.write(nil)
128
+ rescue Rapns::Daemon::ConnectionError
129
+ end
167
130
  end
168
- end
169
131
 
170
- it "should retry to make a connection 3 times" do
171
- @connection.should_receive(:reconnect).exactly(3).times
172
- begin
173
- @connection.write(nil)
174
- rescue Rapns::Daemon::ConnectionError
132
+ it "retries to make a connection 3 times" do
133
+ connection.should_receive(:reconnect).exactly(3).times
134
+ begin
135
+ connection.write(nil)
136
+ rescue Rapns::Daemon::ConnectionError
137
+ end
175
138
  end
176
- end
177
139
 
178
- it "should raise a ConnectionError after 3 attempts at reconnecting" do
179
- expect do
180
- @connection.write(nil)
181
- end.to raise_error(Rapns::Daemon::ConnectionError, "Connection 0 tried 3 times to reconnect but failed (#{error_type.name}).")
182
- end
140
+ it "raises a ConnectionError after 3 attempts at reconnecting" do
141
+ expect do
142
+ connection.write(nil)
143
+ end.to raise_error(Rapns::Daemon::ConnectionError, "Connection 0 tried 3 times to reconnect but failed (#{error_type.name}).")
144
+ end
183
145
 
184
- it "should sleep 1 second before retrying the connection" do
185
- @connection.should_receive(:sleep).with(1)
186
- begin
187
- @connection.write(nil)
188
- rescue Rapns::Daemon::ConnectionError
146
+ it "sleeps 1 second before retrying the connection" do
147
+ connection.should_receive(:sleep).with(1)
148
+ begin
149
+ connection.write(nil)
150
+ rescue Rapns::Daemon::ConnectionError
151
+ end
189
152
  end
190
153
  end
191
- end
192
154
 
193
- describe Rapns::Daemon::Connection, "when write raises an Errno::EPIPE" do
194
- it_should_behave_like "when the write fails"
155
+ describe "when write raises an Errno::EPIPE" do
156
+ it_should_behave_like "when the write fails"
195
157
 
196
- def error_type
197
- Errno::EPIPE
158
+ def error_type
159
+ Errno::EPIPE
160
+ end
198
161
  end
199
- end
200
162
 
201
- describe Rapns::Daemon::Connection, "when write raises an Errno::ETIMEDOUT" do
202
- it_should_behave_like "when the write fails"
163
+ describe "when write raises an Errno::ETIMEDOUT" do
164
+ it_should_behave_like "when the write fails"
203
165
 
204
- def error_type
205
- Errno::ETIMEDOUT
166
+ def error_type
167
+ Errno::ETIMEDOUT
168
+ end
206
169
  end
207
- end
208
170
 
209
- describe Rapns::Daemon::Connection, "when write raises an OpenSSL::SSL::SSLError" do
210
- it_should_behave_like "when the write fails"
171
+ describe "when write raises an OpenSSL::SSL::SSLError" do
172
+ it_should_behave_like "when the write fails"
211
173
 
212
- def error_type
213
- OpenSSL::SSL::SSLError
214
- end
215
- end
216
-
217
- describe Rapns::Daemon::Connection, "when reconnecting" do
218
- before do
219
- @connection = Rapns::Daemon::Connection.new('Connection 0', 'gateway.push.apple.com', 2195)
220
- @connection.stub(:close)
221
- @connection.stub(:connect_socket)
174
+ def error_type
175
+ OpenSSL::SSL::SSLError
176
+ end
222
177
  end
223
178
 
224
- it 'closes the socket' do
225
- @connection.should_receive(:close)
226
- @connection.send(:reconnect)
227
- end
179
+ describe "when reconnecting" do
180
+ it 'closes the socket' do
181
+ connection.should_receive(:close)
182
+ connection.send(:reconnect)
183
+ end
228
184
 
229
- it 'connects the socket' do
230
- @connection.should_receive(:connect_socket)
231
- @connection.send(:reconnect)
185
+ it 'connects the socket' do
186
+ connection.should_receive(:connect_socket)
187
+ connection.send(:reconnect)
188
+ end
232
189
  end
233
- end
234
190
 
235
- describe Rapns::Daemon::Connection, "when sending a notification" do
236
- before do
237
- @connection = Rapns::Daemon::Connection.new('Connection 0', 'gateway.push.apple.com', 2195)
238
- @ssl_socket = mock("SSLSocket", :write => nil, :flush => nil, :close => nil)
239
- @tcp_socket = mock("TCPSocket", :close => nil)
240
- @connection.stub(:setup_ssl_context)
241
- @connection.stub(:connect_socket).and_return([@tcp_socket, @ssl_socket])
242
- @connection.connect
243
- end
191
+ describe "when sending a notification" do
192
+ before { connection.connect }
244
193
 
245
- it "should write the data to the SSL socket" do
246
- @ssl_socket.should_receive(:write).with("blah")
247
- @connection.write("blah")
248
- end
194
+ it "writes the data to the SSL socket" do
195
+ ssl_socket.should_receive(:write).with("blah")
196
+ connection.write("blah")
197
+ end
249
198
 
250
- it "should flush the SSL socket" do
251
- @ssl_socket.should_receive(:flush)
252
- @connection.write("blah")
199
+ it "flushes the SSL socket" do
200
+ ssl_socket.should_receive(:flush)
201
+ connection.write("blah")
202
+ end
253
203
  end
254
- end
255
204
 
256
- describe Rapns::Daemon::Connection, 'idle period' do
257
- before do
258
- @connection = Rapns::Daemon::Connection.new('Connection 0', 'gateway.push.apple.com', 2195)
259
- @ssl_socket = mock("SSLSocket", :write => nil, :flush => nil, :close => nil)
260
- @tcp_socket = mock("TCPSocket", :close => nil)
261
- @connection.stub(:setup_ssl_context)
262
- @connection.stub(:connect_socket => [@tcp_socket, @ssl_socket])
263
- @logger = mock("Logger", :info => nil)
264
- Rapns::Daemon.stub(:logger).and_return(@logger)
265
- @connection.connect
266
- end
205
+ describe 'idle period' do
206
+ before { connection.connect }
267
207
 
268
- it 'reconnects if the connection has been idle for more than the defined period' do
269
- Rapns::Daemon::Connection.stub(:idle_period => 0.1)
270
- sleep 0.2
271
- @connection.should_receive(:reconnect)
272
- @connection.write('blah')
273
- end
208
+ it 'reconnects if the connection has been idle for more than the defined period' do
209
+ Rapns::Daemon::Connection.stub(:idle_period => 0.1)
210
+ sleep 0.2
211
+ connection.should_receive(:reconnect)
212
+ connection.write('blah')
213
+ end
274
214
 
275
- it 'resets the last write time' do
276
- now = Time.now
277
- Time.stub(:now => now)
278
- @connection.write('blah')
279
- @connection.last_write.should == now
280
- end
215
+ it 'resets the last write time' do
216
+ now = Time.now
217
+ Time.stub(:now => now)
218
+ connection.write('blah')
219
+ connection.last_write.should == now
220
+ end
281
221
 
282
- it 'does not reconnect if the connection has not been idle for more than the defined period' do
283
- @connection.should_not_receive(:reconnect)
284
- @connection.write('blah')
285
- end
222
+ it 'does not reconnect if the connection has not been idle for more than the defined period' do
223
+ connection.should_not_receive(:reconnect)
224
+ connection.write('blah')
225
+ end
286
226
 
287
- it 'logs the the connection is idle' do
288
- Rapns::Daemon::Connection.stub(:idle_period => 0.1)
289
- sleep 0.2
290
- Rapns::Daemon.logger.should_receive(:info).with('[Connection 0] Idle period exceeded, reconnecting...')
291
- @connection.write('blah')
227
+ it 'logs the the connection is idle' do
228
+ Rapns::Daemon::Connection.stub(:idle_period => 0.1)
229
+ sleep 0.2
230
+ Rapns::Daemon.logger.should_receive(:info).with('[Connection 0] Idle period exceeded, reconnecting...')
231
+ connection.write('blah')
232
+ end
292
233
  end
293
234
  end
@@ -1,21 +1,17 @@
1
1
  require "spec_helper"
2
2
 
3
3
  describe Rapns::Daemon::DeliveryHandlerPool do
4
- before do
5
- @handler = mock("DeliveryHandler", :start => nil)
6
- Rapns::Daemon::DeliveryHandler.stub(:new).and_return(@handler)
7
- @pool = Rapns::Daemon::DeliveryHandlerPool.new(3)
8
- Rapns::Daemon.stub(:delivery_queue).and_return(mock("Delivery queue", :push => nil))
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
9
10
  end
10
11
 
11
- it "should populate the pool" do
12
- Rapns::Daemon::DeliveryHandler.should_receive(:new).exactly(3).times
13
- @pool.populate
14
- end
15
-
16
- it "waits for each handle to stop" do
17
- @pool.populate
18
- @handler.should_receive(:stop).exactly(3).times
19
- @pool.drain
12
+ it 'stops each handler when drained' do
13
+ pool << handler
14
+ handler.should_receive(:stop)
15
+ pool.drain
20
16
  end
21
17
  end