rapns 1.0.7 → 2.0.0rc1

Sign up to get free protection for your applications and to get access to all the features.
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