rpush 8.0.0 → 9.0.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -6
- data/README.md +8 -65
- data/lib/generators/templates/rpush.rb +9 -16
- data/lib/rpush/client/active_model.rb +0 -4
- data/lib/rpush/client/active_record.rb +0 -3
- data/lib/rpush/client/redis.rb +0 -3
- data/lib/rpush/configuration.rb +2 -19
- data/lib/rpush/daemon/service_config_methods.rb +0 -2
- data/lib/rpush/daemon/store/active_record.rb +2 -14
- data/lib/rpush/daemon/store/interface.rb +2 -2
- data/lib/rpush/daemon/store/redis.rb +2 -11
- data/lib/rpush/daemon.rb +0 -10
- data/lib/rpush/reflection_collection.rb +1 -2
- data/lib/rpush/version.rb +1 -1
- data/lib/rpush.rb +0 -1
- data/spec/functional/cli_spec.rb +41 -15
- data/spec/functional/embed_spec.rb +57 -26
- data/spec/functional/retry_spec.rb +21 -4
- data/spec/functional/synchronization_spec.rb +1 -1
- data/spec/functional_spec_helper.rb +0 -6
- data/spec/spec_helper.rb +2 -0
- data/spec/unit/client/active_record/shared/app.rb +1 -1
- data/spec/unit/daemon/shared/store.rb +0 -42
- metadata +49 -55
- data/lib/rpush/apns_feedback.rb +0 -18
- data/lib/rpush/client/active_model/gcm/app.rb +0 -19
- data/lib/rpush/client/active_model/gcm/expiry_collapse_key_mutual_inclusion_validator.rb +0 -14
- data/lib/rpush/client/active_model/gcm/notification.rb +0 -62
- data/lib/rpush/client/active_record/gcm/app.rb +0 -11
- data/lib/rpush/client/active_record/gcm/notification.rb +0 -11
- data/lib/rpush/client/redis/gcm/app.rb +0 -11
- data/lib/rpush/client/redis/gcm/notification.rb +0 -11
- data/lib/rpush/daemon/apns/delivery.rb +0 -43
- data/lib/rpush/daemon/apns/feedback_receiver.rb +0 -91
- data/lib/rpush/daemon/apns.rb +0 -17
- data/lib/rpush/daemon/dispatcher/apns_tcp.rb +0 -152
- data/lib/rpush/daemon/dispatcher/tcp.rb +0 -22
- data/lib/rpush/daemon/gcm/delivery.rb +0 -241
- data/lib/rpush/daemon/gcm.rb +0 -9
- data/lib/rpush/daemon/tcp_connection.rb +0 -190
- data/spec/functional/apns_spec.rb +0 -162
- data/spec/functional/gcm_priority_spec.rb +0 -40
- data/spec/functional/gcm_spec.rb +0 -46
- data/spec/functional/new_app_spec.rb +0 -44
- data/spec/unit/apns_feedback_spec.rb +0 -39
- data/spec/unit/client/active_record/gcm/app_spec.rb +0 -6
- data/spec/unit/client/active_record/gcm/notification_spec.rb +0 -14
- data/spec/unit/client/redis/gcm/app_spec.rb +0 -5
- data/spec/unit/client/redis/gcm/notification_spec.rb +0 -5
- data/spec/unit/client/shared/gcm/app.rb +0 -4
- data/spec/unit/client/shared/gcm/notification.rb +0 -77
- data/spec/unit/daemon/apns/delivery_spec.rb +0 -108
- data/spec/unit/daemon/apns/feedback_receiver_spec.rb +0 -137
- data/spec/unit/daemon/dispatcher/tcp_spec.rb +0 -32
- data/spec/unit/daemon/gcm/delivery_spec.rb +0 -387
- data/spec/unit/daemon/tcp_connection_spec.rb +0 -293
@@ -1,293 +0,0 @@
|
|
1
|
-
require "unit_spec_helper"
|
2
|
-
|
3
|
-
describe Rpush::Daemon::TcpConnection do
|
4
|
-
let(:rsa_key) { double }
|
5
|
-
let(:certificate) { double }
|
6
|
-
let(:password) { double }
|
7
|
-
let(:x509_certificate) { OpenSSL::X509::Certificate.new(TEST_CERT) }
|
8
|
-
let(:ssl_context) { double(:key= => nil, :cert= => nil, cert: x509_certificate) }
|
9
|
-
let(:host) { 'gateway.push.apple.com' }
|
10
|
-
let(:port) { '2195' }
|
11
|
-
let(:tcp_socket) { double(setsockopt: nil, close: nil) }
|
12
|
-
let(:ssl_socket) { double(:sync= => nil, connect: nil, close: nil, write: nil, flush: nil) }
|
13
|
-
let(:logger) { double(info: nil, error: nil, warn: nil) }
|
14
|
-
let(:app) { double(name: 'Connection 0', certificate: certificate, password: password) }
|
15
|
-
let(:connection) { Rpush::Daemon::TcpConnection.new(app, host, port) }
|
16
|
-
|
17
|
-
before do
|
18
|
-
allow(x509_certificate).to receive(:not_after).and_return(Time.now + 1.year)
|
19
|
-
allow(OpenSSL::SSL::SSLContext).to receive_messages(new: ssl_context)
|
20
|
-
allow(OpenSSL::PKey::RSA).to receive_messages(new: rsa_key)
|
21
|
-
allow(OpenSSL::X509::Certificate).to receive_messages(new: x509_certificate)
|
22
|
-
allow(TCPSocket).to receive_messages(new: tcp_socket)
|
23
|
-
allow(OpenSSL::SSL::SSLSocket).to receive_messages(new: ssl_socket)
|
24
|
-
allow(Rpush).to receive_messages(logger: logger)
|
25
|
-
allow(connection).to receive(:reflect)
|
26
|
-
end
|
27
|
-
|
28
|
-
it "reads the number of bytes from the SSL socket" do
|
29
|
-
expect(ssl_socket).to receive(:read).with(123)
|
30
|
-
connection.connect
|
31
|
-
connection.read(123)
|
32
|
-
end
|
33
|
-
|
34
|
-
it "selects on the SSL socket until the given timeout" do
|
35
|
-
expect(IO).to receive(:select).with([ssl_socket], nil, nil, 10)
|
36
|
-
connection.connect
|
37
|
-
connection.select(10)
|
38
|
-
end
|
39
|
-
|
40
|
-
describe "when setting up the SSL context" do
|
41
|
-
it "sets the key on the context" do
|
42
|
-
expect(OpenSSL::PKey::RSA).to receive(:new).with(certificate, password).and_return(rsa_key)
|
43
|
-
expect(ssl_context).to receive(:key=).with(rsa_key)
|
44
|
-
connection.connect
|
45
|
-
end
|
46
|
-
|
47
|
-
it "sets the cert on the context" do
|
48
|
-
expect(OpenSSL::X509::Certificate).to receive(:new).with(certificate).and_return(x509_certificate)
|
49
|
-
expect(ssl_context).to receive(:cert=).with(x509_certificate)
|
50
|
-
connection.connect
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
describe "when connecting the socket" do
|
55
|
-
it "creates a TCP socket using the configured host and port" do
|
56
|
-
expect(TCPSocket).to receive(:new).with(host, port).and_return(tcp_socket)
|
57
|
-
connection.connect
|
58
|
-
end
|
59
|
-
|
60
|
-
it "creates a new SSL socket using the TCP socket and SSL context" do
|
61
|
-
expect(OpenSSL::SSL::SSLSocket).to receive(:new).with(tcp_socket, ssl_context).and_return(ssl_socket)
|
62
|
-
connection.connect
|
63
|
-
end
|
64
|
-
|
65
|
-
it "sets the sync option on the SSL socket" do
|
66
|
-
expect(ssl_socket).to receive(:sync=).with(true)
|
67
|
-
connection.connect
|
68
|
-
end
|
69
|
-
|
70
|
-
it "connects the SSL socket" do
|
71
|
-
expect(ssl_socket).to receive(:connect)
|
72
|
-
connection.connect
|
73
|
-
end
|
74
|
-
|
75
|
-
it "sets the socket option TCP_NODELAY" do
|
76
|
-
expect(tcp_socket).to receive(:setsockopt).with(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, true)
|
77
|
-
connection.connect
|
78
|
-
end
|
79
|
-
|
80
|
-
it "sets the socket option SO_KEEPALIVE" do
|
81
|
-
expect(tcp_socket).to receive(:setsockopt).with(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, true)
|
82
|
-
connection.connect
|
83
|
-
end
|
84
|
-
|
85
|
-
describe 'certificate expiry' do
|
86
|
-
it 'reflects if the certificate will expire soon' do
|
87
|
-
cert = x509_certificate
|
88
|
-
expect(connection).to receive(:reflect).with(:ssl_certificate_will_expire, app, cert.not_after)
|
89
|
-
Timecop.freeze(cert.not_after - 3.days) { connection.connect }
|
90
|
-
end
|
91
|
-
|
92
|
-
it 'logs that the certificate will expire soon' do
|
93
|
-
cert = x509_certificate
|
94
|
-
expect(logger).to receive(:warn).with("[#{app.name}] Certificate will expire at #{cert.not_after.utc}.")
|
95
|
-
Timecop.freeze(cert.not_after - 3.days) { connection.connect }
|
96
|
-
end
|
97
|
-
|
98
|
-
it 'does not reflect if the certificate will not expire soon' do
|
99
|
-
cert = x509_certificate
|
100
|
-
expect(connection).not_to receive(:reflect).with(:ssl_certificate_will_expire, app, kind_of(Time))
|
101
|
-
Timecop.freeze(cert.not_after - 2.months) { connection.connect }
|
102
|
-
end
|
103
|
-
|
104
|
-
it 'logs that the certificate has expired' do
|
105
|
-
cert = x509_certificate
|
106
|
-
expect(logger).to receive(:error).with("[#{app.name}] Certificate expired at #{cert.not_after.utc}.")
|
107
|
-
Timecop.freeze(cert.not_after + 1.day) { connection.connect rescue Rpush::CertificateExpiredError }
|
108
|
-
end
|
109
|
-
|
110
|
-
it 'raises an error if the certificate has expired' do
|
111
|
-
cert = x509_certificate
|
112
|
-
Timecop.freeze(cert.not_after + 1.day) do
|
113
|
-
expect { connection.connect }.to raise_error(Rpush::CertificateExpiredError)
|
114
|
-
end
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
describe 'certificate revocation' do
|
119
|
-
let(:cert_revoked_error) { OpenSSL::SSL::SSLError.new('certificate revoked') }
|
120
|
-
before do
|
121
|
-
allow(ssl_socket).to receive(:connect).and_raise(cert_revoked_error)
|
122
|
-
end
|
123
|
-
|
124
|
-
it 'reflects that the certificate has been revoked' do
|
125
|
-
expect(connection).to receive(:reflect).with(:ssl_certificate_revoked, app, cert_revoked_error)
|
126
|
-
expect { connection.connect }.to raise_error(Rpush::Daemon::TcpConnectionError, 'OpenSSL::SSL::SSLError, certificate revoked')
|
127
|
-
end
|
128
|
-
|
129
|
-
it 'logs that the certificate has been revoked' do
|
130
|
-
expect(logger).to receive(:error).with('[Connection 0] Certificate has been revoked.')
|
131
|
-
expect { connection.connect }.to raise_error(Rpush::Daemon::TcpConnectionError, 'OpenSSL::SSL::SSLError, certificate revoked')
|
132
|
-
end
|
133
|
-
end
|
134
|
-
end
|
135
|
-
|
136
|
-
describe "when shuting down the connection" do
|
137
|
-
it "closes the TCP socket" do
|
138
|
-
connection.connect
|
139
|
-
expect(tcp_socket).to receive(:close)
|
140
|
-
connection.close
|
141
|
-
end
|
142
|
-
|
143
|
-
it "does not attempt to close the TCP socket if it is not connected" do
|
144
|
-
connection.connect
|
145
|
-
expect(tcp_socket).not_to receive(:close)
|
146
|
-
connection.instance_variable_set("@tcp_socket", nil)
|
147
|
-
connection.close
|
148
|
-
end
|
149
|
-
|
150
|
-
it "closes the SSL socket" do
|
151
|
-
connection.connect
|
152
|
-
expect(ssl_socket).to receive(:close)
|
153
|
-
connection.close
|
154
|
-
end
|
155
|
-
|
156
|
-
it "does not attempt to close the SSL socket if it is not connected" do
|
157
|
-
connection.connect
|
158
|
-
expect(ssl_socket).not_to receive(:close)
|
159
|
-
connection.instance_variable_set("@ssl_socket", nil)
|
160
|
-
connection.close
|
161
|
-
end
|
162
|
-
|
163
|
-
it "ignores IOError when the socket is already closed" do
|
164
|
-
allow(tcp_socket).to receive(:close).and_raise(IOError)
|
165
|
-
connection.connect
|
166
|
-
connection.close
|
167
|
-
end
|
168
|
-
end
|
169
|
-
|
170
|
-
shared_examples_for "when the write fails" do
|
171
|
-
before do
|
172
|
-
allow(connection).to receive(:sleep)
|
173
|
-
connection.connect
|
174
|
-
allow(ssl_socket).to receive(:write).and_raise(error)
|
175
|
-
end
|
176
|
-
|
177
|
-
it 'reflects the connection has been lost' do
|
178
|
-
expect(connection).to receive(:reflect).with(:tcp_connection_lost, app, kind_of(error.class))
|
179
|
-
expect { connection.write(nil) }.to raise_error(Rpush::Daemon::TcpConnectionError)
|
180
|
-
end
|
181
|
-
|
182
|
-
it "logs that the connection has been lost once only" do
|
183
|
-
expect(logger).to receive(:error).with("[Connection 0] Lost connection to gateway.push.apple.com:2195 (#{error.class.name}, #{error.message}), reconnecting...").once
|
184
|
-
expect { connection.write(nil) }.to raise_error(Rpush::Daemon::TcpConnectionError)
|
185
|
-
end
|
186
|
-
|
187
|
-
it "retries to make a connection 3 times" do
|
188
|
-
expect(connection).to receive(:reconnect).exactly(3).times
|
189
|
-
expect { connection.write(nil) }.to raise_error(Rpush::Daemon::TcpConnectionError)
|
190
|
-
end
|
191
|
-
|
192
|
-
it "raises a TcpConnectionError after 3 attempts at reconnecting" do
|
193
|
-
expect do
|
194
|
-
connection.write(nil)
|
195
|
-
end.to raise_error(Rpush::Daemon::TcpConnectionError, "Connection 0 tried 3 times to reconnect but failed (#{error.class.name}, #{error.message}).")
|
196
|
-
end
|
197
|
-
|
198
|
-
it "sleeps 1 second before retrying the connection" do
|
199
|
-
expect(connection).to receive(:sleep).with(1)
|
200
|
-
expect { connection.write(nil) }.to raise_error(Rpush::Daemon::TcpConnectionError)
|
201
|
-
end
|
202
|
-
end
|
203
|
-
|
204
|
-
describe "when write raises an Errno::EPIPE" do
|
205
|
-
it_should_behave_like "when the write fails"
|
206
|
-
|
207
|
-
def error
|
208
|
-
Errno::EPIPE.new('an message')
|
209
|
-
end
|
210
|
-
end
|
211
|
-
|
212
|
-
describe "when write raises an Errno::ETIMEDOUT" do
|
213
|
-
it_should_behave_like "when the write fails"
|
214
|
-
|
215
|
-
def error
|
216
|
-
Errno::ETIMEDOUT.new('an message')
|
217
|
-
end
|
218
|
-
end
|
219
|
-
|
220
|
-
describe "when write raises an OpenSSL::SSL::SSLError" do
|
221
|
-
it_should_behave_like "when the write fails"
|
222
|
-
|
223
|
-
def error
|
224
|
-
OpenSSL::SSL::SSLError.new('an message')
|
225
|
-
end
|
226
|
-
end
|
227
|
-
|
228
|
-
describe "when write raises an IOError" do
|
229
|
-
it_should_behave_like "when the write fails"
|
230
|
-
|
231
|
-
def error
|
232
|
-
IOError.new('an message')
|
233
|
-
end
|
234
|
-
end
|
235
|
-
|
236
|
-
describe "when reconnecting" do
|
237
|
-
before { connection.connect }
|
238
|
-
|
239
|
-
it 'closes the socket' do
|
240
|
-
expect(connection).to receive(:close)
|
241
|
-
connection.send(:reconnect)
|
242
|
-
end
|
243
|
-
|
244
|
-
it 'connects the socket' do
|
245
|
-
expect(connection).to receive(:connect_socket)
|
246
|
-
connection.send(:reconnect)
|
247
|
-
end
|
248
|
-
end
|
249
|
-
|
250
|
-
describe "when sending a notification" do
|
251
|
-
before { connection.connect }
|
252
|
-
|
253
|
-
it "writes the data to the SSL socket" do
|
254
|
-
expect(ssl_socket).to receive(:write).with("blah")
|
255
|
-
connection.write("blah")
|
256
|
-
end
|
257
|
-
|
258
|
-
it "flushes the SSL socket" do
|
259
|
-
expect(ssl_socket).to receive(:flush)
|
260
|
-
connection.write("blah")
|
261
|
-
end
|
262
|
-
end
|
263
|
-
|
264
|
-
describe 'idle period' do
|
265
|
-
before { connection.connect }
|
266
|
-
|
267
|
-
it 'reconnects if the connection has been idle for more than the defined period' do
|
268
|
-
allow(Rpush::Daemon::TcpConnection).to receive_messages(idle_period: 60)
|
269
|
-
allow(Time).to receive_messages(now: Time.now + 61)
|
270
|
-
expect(connection).to receive(:reconnect)
|
271
|
-
connection.write('blah')
|
272
|
-
end
|
273
|
-
|
274
|
-
it 'resets the last touch time' do
|
275
|
-
now = Time.now
|
276
|
-
allow(Time).to receive_messages(now: now)
|
277
|
-
connection.write('blah')
|
278
|
-
expect(connection.last_touch).to eq now
|
279
|
-
end
|
280
|
-
|
281
|
-
it 'does not reconnect if the connection has not been idle for more than the defined period' do
|
282
|
-
expect(connection).not_to receive(:reconnect)
|
283
|
-
connection.write('blah')
|
284
|
-
end
|
285
|
-
|
286
|
-
it 'logs the the connection is idle' do
|
287
|
-
allow(Rpush::Daemon::TcpConnection).to receive_messages(idle_period: 60)
|
288
|
-
allow(Time).to receive_messages(now: Time.now + 61)
|
289
|
-
expect(Rpush.logger).to receive(:info).with('[Connection 0] Idle period exceeded, reconnecting...')
|
290
|
-
connection.write('blah')
|
291
|
-
end
|
292
|
-
end
|
293
|
-
end
|