rapns 3.0.1 → 3.1.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.
- data/CHANGELOG.md +10 -2
- data/README.md +26 -3
- data/bin/rapns +2 -7
- data/lib/generators/templates/add_gcm.rb +3 -1
- data/lib/generators/templates/rapns.rb +42 -11
- data/lib/rapns/apns/notification.rb +8 -2
- data/lib/rapns/app.rb +3 -3
- data/lib/rapns/configuration.rb +46 -13
- data/lib/rapns/daemon/apns/connection.rb +12 -9
- data/lib/rapns/daemon/apns/delivery_handler.rb +1 -1
- data/lib/rapns/daemon/apns/feedback_receiver.rb +6 -2
- data/lib/rapns/daemon/app_runner.rb +23 -7
- data/lib/rapns/daemon/delivery.rb +5 -1
- data/lib/rapns/daemon/delivery_handler.rb +4 -0
- data/lib/rapns/daemon/feeder.rb +26 -5
- data/lib/rapns/daemon/reflectable.rb +13 -0
- data/lib/rapns/daemon.rb +33 -22
- data/lib/rapns/embed.rb +28 -0
- data/lib/rapns/gcm/notification.rb +7 -2
- data/lib/rapns/gcm/payload_data_size_validator.rb +13 -0
- data/lib/rapns/gcm/registration_ids_count_validator.rb +13 -0
- data/lib/rapns/push.rb +12 -0
- data/lib/rapns/reflection.rb +44 -0
- data/lib/rapns/version.rb +1 -1
- data/lib/rapns.rb +11 -1
- data/spec/support/cert_with_password.pem +90 -0
- data/spec/support/cert_without_password.pem +59 -0
- data/spec/unit/apns/app_spec.rb +15 -1
- data/spec/unit/apns/notification_spec.rb +16 -1
- data/spec/unit/configuration_spec.rb +10 -1
- data/spec/unit/daemon/apns/connection_spec.rb +11 -2
- data/spec/unit/daemon/apns/delivery_handler_spec.rb +1 -1
- data/spec/unit/daemon/apns/delivery_spec.rb +10 -0
- data/spec/unit/daemon/apns/feedback_receiver_spec.rb +16 -7
- data/spec/unit/daemon/delivery_handler_shared.rb +8 -0
- data/spec/unit/daemon/feeder_spec.rb +37 -6
- data/spec/unit/daemon/gcm/delivery_spec.rb +33 -1
- data/spec/unit/daemon/reflectable_spec.rb +27 -0
- data/spec/unit/daemon_spec.rb +55 -9
- data/spec/unit/embed_spec.rb +44 -0
- data/spec/unit/gcm/notification_spec.rb +9 -3
- data/spec/unit/push_spec.rb +28 -0
- data/spec/unit/reflection_spec.rb +34 -0
- data/spec/unit_spec_helper.rb +4 -62
- metadata +22 -5
- data/lib/rapns/gcm/payload_size_validator.rb +0 -13
data/lib/rapns/daemon.rb
CHANGED
@@ -5,6 +5,7 @@ require 'openssl'
|
|
5
5
|
|
6
6
|
require 'net/http/persistent'
|
7
7
|
|
8
|
+
require 'rapns/daemon/reflectable'
|
8
9
|
require 'rapns/daemon/interruptible_sleep'
|
9
10
|
require 'rapns/daemon/delivery_error'
|
10
11
|
require 'rapns/daemon/database_reconnectable'
|
@@ -37,9 +38,10 @@ module Rapns
|
|
37
38
|
def self.start
|
38
39
|
self.logger = Logger.new(:foreground => Rapns.config.foreground,
|
39
40
|
:airbrake_notify => Rapns.config.airbrake_notify)
|
40
|
-
setup_signal_hooks
|
41
41
|
|
42
|
-
|
42
|
+
setup_signal_traps if trap_signals?
|
43
|
+
|
44
|
+
if daemonize?
|
43
45
|
daemonize
|
44
46
|
reconnect_database
|
45
47
|
end
|
@@ -47,11 +49,22 @@ module Rapns
|
|
47
49
|
write_pid_file
|
48
50
|
ensure_upgraded
|
49
51
|
AppRunner.sync
|
50
|
-
Feeder.start
|
52
|
+
Feeder.start
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.shutdown(quiet = false)
|
56
|
+
puts "\nShutting down..." unless quiet
|
57
|
+
Feeder.stop
|
58
|
+
AppRunner.stop
|
59
|
+
delete_pid_file
|
51
60
|
end
|
52
61
|
|
53
62
|
protected
|
54
63
|
|
64
|
+
def self.daemonize?
|
65
|
+
!(Rapns.config.foreground || Rapns.config.embedded || Rapns.config.push || defined?(JRUBY_VERSION))
|
66
|
+
end
|
67
|
+
|
55
68
|
def self.ensure_upgraded
|
56
69
|
count = 0
|
57
70
|
|
@@ -64,7 +77,7 @@ module Rapns
|
|
64
77
|
puts "Please run 'rails g rapns' to generate the new migrations and create your app."
|
65
78
|
puts "See https://github.com/ileitch/rapns for further instructions."
|
66
79
|
puts
|
67
|
-
exit 1
|
80
|
+
exit 1 unless Rapns.config.embedded || Rapns.config.push
|
68
81
|
end
|
69
82
|
|
70
83
|
if count == 0
|
@@ -80,7 +93,11 @@ Remove config/rapns/rapns.yml to avoid this warning.
|
|
80
93
|
end
|
81
94
|
end
|
82
95
|
|
83
|
-
def self.
|
96
|
+
def self.trap_signals?
|
97
|
+
!(Rapns.config.embedded || Rapns.config.push)
|
98
|
+
end
|
99
|
+
|
100
|
+
def self.setup_signal_traps
|
84
101
|
@shutting_down = false
|
85
102
|
|
86
103
|
Signal.trap('SIGHUP') { AppRunner.sync }
|
@@ -97,13 +114,6 @@ Remove config/rapns/rapns.yml to avoid this warning.
|
|
97
114
|
shutdown
|
98
115
|
end
|
99
116
|
|
100
|
-
def self.shutdown
|
101
|
-
puts "\nShutting down..."
|
102
|
-
Feeder.stop
|
103
|
-
AppRunner.stop
|
104
|
-
delete_pid_file
|
105
|
-
end
|
106
|
-
|
107
117
|
def self.write_pid_file
|
108
118
|
if !Rapns.config.pid_file.blank?
|
109
119
|
begin
|
@@ -121,16 +131,17 @@ Remove config/rapns/rapns.yml to avoid this warning.
|
|
121
131
|
|
122
132
|
# :nocov:
|
123
133
|
def self.daemonize
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
+
if RUBY_VERSION < "1.9"
|
135
|
+
exit if fork
|
136
|
+
Process.setsid
|
137
|
+
exit if fork
|
138
|
+
Dir.chdir "/"
|
139
|
+
STDIN.reopen "/dev/null"
|
140
|
+
STDOUT.reopen "/dev/null", "a"
|
141
|
+
STDERR.reopen "/dev/null", "a"
|
142
|
+
else
|
143
|
+
Process.daemon
|
144
|
+
end
|
134
145
|
end
|
135
146
|
end
|
136
147
|
end
|
data/lib/rapns/embed.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
module Rapns
|
2
|
+
def self.embed(options = {})
|
3
|
+
Rapns.require_for_daemon
|
4
|
+
|
5
|
+
config = Rapns::ConfigurationWithoutDefaults.new
|
6
|
+
options.each { |k, v| config.send("#{k}=", v) }
|
7
|
+
config.embedded = true
|
8
|
+
Rapns.config.update(config)
|
9
|
+
Rapns::Daemon.start
|
10
|
+
|
11
|
+
Kernel.at_exit { shutdown }
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.shutdown
|
15
|
+
return unless Rapns.config.embedded
|
16
|
+
Rapns::Daemon.shutdown
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.sync
|
20
|
+
return unless Rapns.config.embedded
|
21
|
+
Rapns::Daemon::AppRunner.sync
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.debug
|
25
|
+
return unless Rapns.config.embedded
|
26
|
+
Rapns::Daemon::AppRunner.debug
|
27
|
+
end
|
28
|
+
end
|
@@ -3,7 +3,8 @@ module Rapns
|
|
3
3
|
class Notification < Rapns::Notification
|
4
4
|
validates :registration_ids, :presence => true
|
5
5
|
validates_with Rapns::Gcm::ExpiryCollapseKeyMutualInclusionValidator
|
6
|
-
validates_with Rapns::Gcm::
|
6
|
+
validates_with Rapns::Gcm::PayloadDataSizeValidator
|
7
|
+
validates_with Rapns::Gcm::RegistrationIdsCountValidator
|
7
8
|
|
8
9
|
def registration_ids=(ids)
|
9
10
|
ids = [ids] if ids && !ids.is_a?(Array)
|
@@ -26,6 +27,10 @@ module Rapns
|
|
26
27
|
|
27
28
|
json
|
28
29
|
end
|
30
|
+
|
31
|
+
def payload_data_size
|
32
|
+
multi_json_dump(as_json['data']).bytesize
|
33
|
+
end
|
29
34
|
end
|
30
35
|
end
|
31
|
-
end
|
36
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Rapns
|
2
|
+
module Gcm
|
3
|
+
class PayloadDataSizeValidator < ActiveModel::Validator
|
4
|
+
LIMIT = 4096
|
5
|
+
|
6
|
+
def validate(record)
|
7
|
+
if record.payload_data_size > LIMIT
|
8
|
+
record.errors[:base] << "GCM notification payload data cannot be larger than #{LIMIT} bytes."
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Rapns
|
2
|
+
module Gcm
|
3
|
+
class RegistrationIdsCountValidator < ActiveModel::Validator
|
4
|
+
LIMIT = 1000
|
5
|
+
|
6
|
+
def validate(record)
|
7
|
+
if record.registration_ids && record.registration_ids.size > LIMIT
|
8
|
+
record.errors[:base] << "GCM notification num of registration_ids cannot be larger than #{LIMIT}."
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
data/lib/rapns/push.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
module Rapns
|
2
|
+
def self.push(options = {})
|
3
|
+
Rapns.require_for_daemon
|
4
|
+
|
5
|
+
config = Rapns::ConfigurationWithoutDefaults.new
|
6
|
+
options.each { |k, v| config.send("#{k}=", v) }
|
7
|
+
config.push = true
|
8
|
+
Rapns.config.update(config)
|
9
|
+
Rapns::Daemon.start
|
10
|
+
Rapns::Daemon.shutdown(true)
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Rapns
|
2
|
+
def self.reflect
|
3
|
+
yield reflections if block_given?
|
4
|
+
end
|
5
|
+
|
6
|
+
def self.reflections
|
7
|
+
@reflections ||= Reflections.new
|
8
|
+
end
|
9
|
+
|
10
|
+
class Reflections
|
11
|
+
class NoSuchReflectionError < StandardError; end
|
12
|
+
|
13
|
+
REFLECTIONS = [
|
14
|
+
:apns_feedback, :notification_enqueued, :notification_delivered,
|
15
|
+
:notification_failed, :notification_will_retry, :apns_connection_lost,
|
16
|
+
:error
|
17
|
+
]
|
18
|
+
|
19
|
+
REFLECTIONS.each do |reflection|
|
20
|
+
class_eval(<<-RUBY, __FILE__, __LINE__)
|
21
|
+
def #{reflection}(*args, &blk)
|
22
|
+
raise "block required" unless block_given?
|
23
|
+
reflections[:#{reflection}] = blk
|
24
|
+
end
|
25
|
+
RUBY
|
26
|
+
end
|
27
|
+
|
28
|
+
def __dispatch(reflection, *args)
|
29
|
+
unless REFLECTIONS.include?(reflection.to_sym)
|
30
|
+
raise NoSuchReflectionError, reflection
|
31
|
+
end
|
32
|
+
|
33
|
+
if reflections[reflection]
|
34
|
+
reflections[reflection].call(*args)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def reflections
|
41
|
+
@reflections ||= {}
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/lib/rapns/version.rb
CHANGED
data/lib/rapns.rb
CHANGED
@@ -8,6 +8,9 @@ require 'rapns/multi_json_helper'
|
|
8
8
|
require 'rapns/notification'
|
9
9
|
require 'rapns/app'
|
10
10
|
require 'rapns/configuration'
|
11
|
+
require 'rapns/reflection'
|
12
|
+
require 'rapns/embed'
|
13
|
+
require 'rapns/push'
|
11
14
|
|
12
15
|
require 'rapns/apns/binary_notification_validator'
|
13
16
|
require 'rapns/apns/device_token_format_validator'
|
@@ -17,7 +20,14 @@ require 'rapns/apns/feedback'
|
|
17
20
|
require 'rapns/apns/app'
|
18
21
|
|
19
22
|
require 'rapns/gcm/expiry_collapse_key_mutual_inclusion_validator'
|
20
|
-
require 'rapns/gcm/
|
23
|
+
require 'rapns/gcm/payload_data_size_validator'
|
24
|
+
require 'rapns/gcm/registration_ids_count_validator'
|
21
25
|
require 'rapns/gcm/notification'
|
22
26
|
require 'rapns/gcm/app'
|
23
27
|
|
28
|
+
module Rapns
|
29
|
+
def self.require_for_daemon
|
30
|
+
require 'rapns/daemon'
|
31
|
+
require 'rapns/patches'
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
Bag Attributes
|
2
|
+
localKeyID: A4 1A DB 3E 3E 45 D9 C7 51 1E E6 DC 4E BC 29 19 E4 22 95 35
|
3
|
+
subject=/C=AU/ST=Some-State/O=Internet Widgits Pty Ltd
|
4
|
+
issuer=/C=UK/ST=Some-State/O=Internet Widgits Pty Ltd
|
5
|
+
-----BEGIN CERTIFICATE-----
|
6
|
+
MIIE/jCCAuYCAQEwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCVUsxEzARBgNV
|
7
|
+
BAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0
|
8
|
+
ZDAeFw0xMjEyMjgxMzU4NTdaFw0xNDEyMjgxMzU4NTdaMEUxCzAJBgNVBAYTAkFV
|
9
|
+
MRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRz
|
10
|
+
IFB0eSBMdGQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDYCSfYP0P7
|
11
|
+
F+JbxUHYsl/9Plr2bFZGMBygqs5RWpw1X977p4FqcNtnRmGENQ/I5vE3KkAKCAlS
|
12
|
+
gmus7CjB9/FVzoJq65cT3wBkcqAAzPgIQuI0UjP+rLktV84eUup7Pt6f/Bmt/fBj
|
13
|
+
h3fvf80nBQcdK2ztu1xtTs7EsRgyudUDDEIUZw9ddK21RbCMYE0PFY0QapNVvevs
|
14
|
+
8lDVUJX60bZqVqzhvlTzmEaBF+E66J/DhHuhcWZV588hVKjHMNED9Aq4PxRy78vI
|
15
|
+
54v1buX2Y9C6dHfIzfu7zz0Zv5NAY1BZKpaglVGKArOrX+K6O7Bq+DwckTm1AbUf
|
16
|
+
pQqi10ghveOtVrm4GXJ4OYW8ohdYKMFjzwhgTTr/NQ+EVlcZ+8AOPVPPJk0HgPu/
|
17
|
+
eMNiLytvcSnB09OzIUAzcxOof2a1zfxn7aPzBTEC6kkoDJC/BDG8wxySUz0zRyyM
|
18
|
+
jUN2+J1mBJZX1h1sM/4ycAzsX1EYm2II5GGJaFngiV45Qv7wTC7W31kvih7FsdUX
|
19
|
+
rEYMkevB5AFPUtIvzevLUObLbPW4yWvU2CMcKLZdIaPTvtPMba91t1YOdufpPRDd
|
20
|
+
HTmT42h0aUWDSrcWDAuZPPPqEIQNjRVCudtHtobZDeaUfvbx05CvypyuNFaPonuQ
|
21
|
+
l5h0LpZWPetvfsrOlMxPm2kfVcxm8mWjzQIDAQABMA0GCSqGSIb3DQEBBQUAA4IC
|
22
|
+
AQBiwdPfLU0kjbU1hM6grUHduLqOPDqLT5gefGRrL5fDqb86G08+Wz+Tnn1YmH+7
|
23
|
+
rfiQhCrdi4zxRv6ZEaKZeEm/q1UMttuUdXuzd25BEEtav4R0POdR+H1q3trlS0ol
|
24
|
+
EcWlAbRgVaT2tTyKGAW54fH8vZPqS6IP+mXIzfaOFECPEgAO8BL8t7hBDpkL4ASO
|
25
|
+
HbU7ktYamsr6PAei3kXAnJ1thXyQqrhelwLrQJyM8RVOJYUgVbl6Fpdtgu7YQF5G
|
26
|
+
kxTAfshSmDCQkf+tbUEs44rZ6BZ2TWnbXjQGkRntHcMCP/1rjsPPdX3oeZ7P3jMQ
|
27
|
+
XER3drdm1mOiSdDUKbem9GzQ9Dx7WkwKNLYAZ+IWzVRACzGxnkxXyxOEFIgesDgg
|
28
|
+
RIhczN+eLIR8iwcUxEVKFmmsbEIve3Uh1/NE6xGudbfZDNfhyOhiNYIBnQqVkk8l
|
29
|
+
c3gw2UDR3lXTayiiXhK2l1etsyxtYncT3pgDsCe72RODrGKbASt3FzfBbalzN0GG
|
30
|
+
9tiPSNtGqCch9q4eHfViUh9s3+8n4bknAYcwzQ96+gMEn8PUVtDBv9F8Kxffn/Jt
|
31
|
+
XMWKX76nTVuAuCLkXxrKwc01lq8SeuvCH650xsv0LBvxj9h6vR34vHGrj0C3sH2E
|
32
|
+
VQpelKNv8IEwkSiQcwDtU8H0jaPJNqmYlkrxasSrSeg6PA==
|
33
|
+
-----END CERTIFICATE-----
|
34
|
+
Bag Attributes
|
35
|
+
localKeyID: A4 1A DB 3E 3E 45 D9 C7 51 1E E6 DC 4E BC 29 19 E4 22 95 35
|
36
|
+
Key Attributes: <No Attributes>
|
37
|
+
-----BEGIN RSA PRIVATE KEY-----
|
38
|
+
Proc-Type: 4,ENCRYPTED
|
39
|
+
DEK-Info: DES-EDE3-CBC,874F2C982467BF08
|
40
|
+
|
41
|
+
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
|
90
|
+
-----END RSA PRIVATE KEY-----
|
@@ -0,0 +1,59 @@
|
|
1
|
+
Bag Attributes
|
2
|
+
friendlyName: test certificate
|
3
|
+
localKeyID: 00 93 8F E4 A3 C3 75 64 3D 7E EA 14 0B 0A EA DD 15 85 8A D5
|
4
|
+
subject=/CN=test certificate/O=Example/OU=Example/ST=QLD/C=AU/L=Example/emailAddress=user@example.com
|
5
|
+
issuer=/CN=test certificate/O=Example/OU=Example/ST=QLD/C=AU/L=Example/emailAddress=user@example.com
|
6
|
+
-----BEGIN CERTIFICATE-----
|
7
|
+
MIID5jCCAs6gAwIBAgIBATALBgkqhkiG9w0BAQswgY0xGTAXBgNVBAMMEHRlc3Qg
|
8
|
+
Y2VydGlmaWNhdGUxEDAOBgNVBAoMB0V4YW1wbGUxEDAOBgNVBAsMB0V4YW1wbGUx
|
9
|
+
DDAKBgNVBAgMA1FMRDELMAkGA1UEBhMCQVUxEDAOBgNVBAcMB0V4YW1wbGUxHzAd
|
10
|
+
BgkqhkiG9w0BCQEWEHVzZXJAZXhhbXBsZS5jb20wHhcNMTIwOTA5MDMxODMyWhcN
|
11
|
+
MjIwOTA3MDMxODMyWjCBjTEZMBcGA1UEAwwQdGVzdCBjZXJ0aWZpY2F0ZTEQMA4G
|
12
|
+
A1UECgwHRXhhbXBsZTEQMA4GA1UECwwHRXhhbXBsZTEMMAoGA1UECAwDUUxEMQsw
|
13
|
+
CQYDVQQGEwJBVTEQMA4GA1UEBwwHRXhhbXBsZTEfMB0GCSqGSIb3DQEJARYQdXNl
|
14
|
+
ckBleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKF+
|
15
|
+
UDsN1sLen8g+97PNTiWju9+wkSv+H5rQlvb6YFLPx11YvqpK8ms6kFU1OmWeLfmh
|
16
|
+
cpsT+bZtKupC7aGPoSG3RXzzf/YUMgs/ZSXA0idZHA6tkReAEzIX6jL5otfPWbaP
|
17
|
+
luCTUoVMeP4u9ywk628zlqh9IQHC1Agl0R1xGCpULDk8kn1gPyEisl38wI5aDbzy
|
18
|
+
6lYQGNUKOqt1xfVjtIFe/jyY/v0sxFjIJlRLcAFBuJx4sRV+PwRBkusOQtYwcwpI
|
19
|
+
loMxJj+GQe66ueATW81aC4iOU66DAFFEuGzwIwm3bOilimGGQbGb92F339RfmSOo
|
20
|
+
TPAvVhsakI3mzESb4lkCAwEAAaNRME8wDgYDVR0PAQH/BAQDAgeAMCAGA1UdJQEB
|
21
|
+
/wQWMBQGCCsGAQUFBwMCBggrBgEFBQcDATAbBgNVHREEFDASgRB1c2VyQGV4YW1w
|
22
|
+
bGUuY29tMA0GCSqGSIb3DQEBCwUAA4IBAQA5UbNR+83ZdI2DiaB4dRmy0V5RDAqJ
|
23
|
+
k9+QskcTV4gBTjsOBS46Dw1tI6iTrfTyjYJdnyH0Y2Y2YVWBnvtON41UCZak+4ed
|
24
|
+
/IqyzU0dtfZ+frWa0RY4reyl80TwqnzyJfni0nDo4zGGvz70cxyaz2u1BWqwLjqb
|
25
|
+
dh8Dxvt+aHW2MQi0iGKh/HNbgwVanR4+ubNwziK9sR1Rnq9MkHWtwBw16SXQG6ao
|
26
|
+
SZKASWNaH8VL08Zz0E98cwd137UJkPsldCwJ8kHR5OzkcjPdXvnGD3d64yy2TC1Z
|
27
|
+
Gy1Aazt98wPcTYBytlhK8Rvzg9OoY9QmsdpmWxz1ZCXECJNqCa3IKsqO
|
28
|
+
-----END CERTIFICATE-----
|
29
|
+
Bag Attributes
|
30
|
+
friendlyName: test certificate
|
31
|
+
localKeyID: 00 93 8F E4 A3 C3 75 64 3D 7E EA 14 0B 0A EA DD 15 85 8A D5
|
32
|
+
Key Attributes: <No Attributes>
|
33
|
+
-----BEGIN RSA PRIVATE KEY-----
|
34
|
+
MIIEpQIBAAKCAQEAoX5QOw3Wwt6fyD73s81OJaO737CRK/4fmtCW9vpgUs/HXVi+
|
35
|
+
qkryazqQVTU6ZZ4t+aFymxP5tm0q6kLtoY+hIbdFfPN/9hQyCz9lJcDSJ1kcDq2R
|
36
|
+
F4ATMhfqMvmi189Zto+W4JNShUx4/i73LCTrbzOWqH0hAcLUCCXRHXEYKlQsOTyS
|
37
|
+
fWA/ISKyXfzAjloNvPLqVhAY1Qo6q3XF9WO0gV7+PJj+/SzEWMgmVEtwAUG4nHix
|
38
|
+
FX4/BEGS6w5C1jBzCkiWgzEmP4ZB7rq54BNbzVoLiI5TroMAUUS4bPAjCbds6KWK
|
39
|
+
YYZBsZv3YXff1F+ZI6hM8C9WGxqQjebMRJviWQIDAQABAoIBAQCTiLIDQUFSBdAz
|
40
|
+
QFNLD+S0vkCEuunlJuP4q1c/ir006l1YChsluBJ/o6D4NwiCjV+zDquEwVsALftm
|
41
|
+
yH4PewfZpXT2Ef508T5GyEO/mchj6iSXxDkpHvhqay6qIyWBwwxSnBtaTzy0Soi+
|
42
|
+
rmlhCtmLXbXld2sQEM1kJChGnWtWPtvSyrn+mapNPZviGRtgRNK+YsrAti1nUext
|
43
|
+
2syO5mTdHf1D8GR7I98OaX6odREuSocEV9PzfapWZx2GK5tvRiS1skiug5ciieTd
|
44
|
+
Am5/C+bb31h4drFslihLb5BRGO5SFQJvMJL2Sx1f19BCC4XikS01P4/zZbxQNq79
|
45
|
+
kxEQuDGBAoGBANP4pIYZ5xshCkx7cTYqmxzWLClGKE2S7Oa8N89mtOwfmqT9AFun
|
46
|
+
t9Us9Ukbi8BaKlKhGpQ1HlLf/KVcpyW0x2qLou6AyIWYH+/5VaR3graNgUnzpK9f
|
47
|
+
1F5HoaNHbhlAoebqhzhASFlJI2aqUdQjdOv73z+s9szJU4gpILNwGDFnAoGBAMMJ
|
48
|
+
j+vIxtG9J2jldyoXzpg5mbMXSj9u/wFLBVdjXWyOoiqVMMBto53RnoqAom7Ifr9D
|
49
|
+
49LxRAT1Q3l4vs/YnM3ziMsIg2vQK1EbrLsY9OnD/kvPaLXOlNIOdfLM8UeVWZMc
|
50
|
+
I4LPbbZrhv/7CC8RjbRhMoWWdGYPvxmvD6V4ZDY/AoGBALoI6OxA45Htx4okdNHj
|
51
|
+
RstiNNPsnQaoQn6nBhxiubraafEPkzbd1fukP4pwQJELEUX/2sHkdL6rkqLW1GPF
|
52
|
+
a5dZAiBsqpCFWNJWdBGqSfBJ9QSgbxLz+gDcwUH6OOi0zuNJRm/aCyVBiW5bYQHc
|
53
|
+
NIvAPMk31ksZDtTbs7WIVdNVAoGBALZ1+KWNxKqs+fSBT5UahpUUtfy8miJz9a7A
|
54
|
+
/3M8q0cGvSF3Rw+OwpW/aEGMi+l2OlU27ykFuyukRAac9m296RwnbF79TO2M5ylO
|
55
|
+
6a5zb5ROXlWP6RbE96b4DlIidssQJqegmHwlEC+rsrVBpOtb0aThlYEyOxzMOGyP
|
56
|
+
wOR9l8rDAoGADZ4TUHFM6VrvPlUZBkGbqiyXH9IM/y9JWk+22JQCEGnM6RFZemSs
|
57
|
+
jxWqQiPAdJtb3xKryJSCMtFPH9azedoCrSgaMflJ1QgoXgpiKZyoEXWraVUggh/0
|
58
|
+
CEavgZcTZ6SvMuayqJdGGB+zb1V8XwXMtCjApR/kTm47DjxO4DmpOPs=
|
59
|
+
-----END RSA PRIVATE KEY-----
|
data/spec/unit/apns/app_spec.rb
CHANGED
@@ -7,9 +7,23 @@ describe Rapns::App do
|
|
7
7
|
app.errors[:certificate].should == ['Certificate value must contain a certificate and a private key.']
|
8
8
|
end
|
9
9
|
|
10
|
-
it 'validates a
|
10
|
+
it 'validates a certificate without a password' do
|
11
11
|
app = Rapns::Apns::App.new :key => 'test', :environment => 'development', :certificate => TEST_CERT
|
12
12
|
app.valid?
|
13
13
|
app.errors[:certificate].should == []
|
14
14
|
end
|
15
|
+
|
16
|
+
it 'validates a certificate with a password' do
|
17
|
+
app = Rapns::Apns::App.new :key => 'test', :environment => 'development',
|
18
|
+
:certificate => TEST_CERT_WITH_PASSWORD, :password => 'fubar'
|
19
|
+
app.valid?
|
20
|
+
app.errors[:certificate].should == []
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'validates a certificate with an incorrect password' do
|
24
|
+
app = Rapns::Apns::App.new :key => 'test', :environment => 'development',
|
25
|
+
:certificate => TEST_CERT_WITH_PASSWORD, :password => 'incorrect'
|
26
|
+
app.valid?
|
27
|
+
app.errors[:certificate].should == ["Certificate value must contain a certificate and a private key."]
|
28
|
+
end
|
15
29
|
end
|
@@ -128,10 +128,25 @@ describe Rapns::Apns::Notification, 'content-available' do
|
|
128
128
|
notification.as_json['aps'].key?('content-available').should be_false
|
129
129
|
end
|
130
130
|
|
131
|
-
it 'does not include
|
131
|
+
it 'does not include content-available as a non-aps attribute' do
|
132
132
|
notification.content_available = true
|
133
133
|
notification.as_json.key?('content-available').should be_false
|
134
134
|
end
|
135
|
+
|
136
|
+
it 'does not overwrite existing attributes for the device' do
|
137
|
+
notification.data = {:hi => :mom}
|
138
|
+
notification.content_available = true
|
139
|
+
notification.as_json['aps']['content-available'].should == 1
|
140
|
+
notification.as_json['hi'].should == 'mom'
|
141
|
+
end
|
142
|
+
|
143
|
+
it 'does not overwrite the content-available flag when setting attributes for the device' do
|
144
|
+
notification.content_available = true
|
145
|
+
notification.data = {:hi => :mom}
|
146
|
+
notification.as_json['aps']['content-available'].should == 1
|
147
|
+
notification.as_json['hi'].should == 'mom'
|
148
|
+
end
|
149
|
+
|
135
150
|
end
|
136
151
|
|
137
152
|
describe Rapns::Apns::Notification, "to_binary" do
|
@@ -15,7 +15,9 @@ describe Rapns::Configuration do
|
|
15
15
|
|
16
16
|
it 'configures a feedback callback' do
|
17
17
|
b = Proc.new {}
|
18
|
-
|
18
|
+
Rapns::Deprecation.silenced do
|
19
|
+
config.on_apns_feedback(&b)
|
20
|
+
end
|
19
21
|
config.apns_feedback_callback.should == b
|
20
22
|
end
|
21
23
|
|
@@ -35,4 +37,11 @@ describe Rapns::Configuration do
|
|
35
37
|
config.pid_file = '/tmp/rapns.pid'
|
36
38
|
config.pid_file.should == '/tmp/rapns.pid'
|
37
39
|
end
|
40
|
+
|
41
|
+
it 'does not allow foreground to be set to false if the platform is JRuby' do
|
42
|
+
config.foreground = true
|
43
|
+
stub_const('Rapns::Configuration::JRUBY_VERSION', '1.7.1')
|
44
|
+
config.foreground = false
|
45
|
+
config.foreground.should be_true
|
46
|
+
end
|
38
47
|
end
|
@@ -11,7 +11,8 @@ describe Rapns::Daemon::Apns::Connection do
|
|
11
11
|
let(:tcp_socket) { stub(:setsockopt => nil, :close => nil) }
|
12
12
|
let(:ssl_socket) { stub(:sync= => nil, :connect => nil, :close => nil, :write => nil, :flush => nil) }
|
13
13
|
let(:logger) { stub(:info => nil, :error => nil) }
|
14
|
-
let(:
|
14
|
+
let(:app) { stub(:name => 'Connection 0', :certificate => certificate, :password => password)}
|
15
|
+
let(:connection) { Rapns::Daemon::Apns::Connection.new(app, host, port) }
|
15
16
|
|
16
17
|
before do
|
17
18
|
OpenSSL::SSL::SSLContext.stub(:new => ssl_context)
|
@@ -121,6 +122,14 @@ describe Rapns::Daemon::Apns::Connection do
|
|
121
122
|
ssl_socket.stub(:write).and_raise(error_type)
|
122
123
|
end
|
123
124
|
|
125
|
+
it 'reflects the connection has been lost' do
|
126
|
+
connection.should_receive(:reflect).with(:apns_connection_lost, app, kind_of(error_type))
|
127
|
+
begin
|
128
|
+
connection.write(nil)
|
129
|
+
rescue Rapns::Daemon::Apns::ConnectionError
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
124
133
|
it "logs that the connection has been lost once only" do
|
125
134
|
logger.should_receive(:error).with("[Connection 0] Lost connection to gateway.push.apple.com:2195 (#{error_type.name}), reconnecting...").once
|
126
135
|
begin
|
@@ -231,4 +240,4 @@ describe Rapns::Daemon::Apns::Connection do
|
|
231
240
|
connection.write('blah')
|
232
241
|
end
|
233
242
|
end
|
234
|
-
end
|
243
|
+
end
|
@@ -23,7 +23,7 @@ describe Rapns::Daemon::Apns::DeliveryHandler do
|
|
23
23
|
end
|
24
24
|
|
25
25
|
it "instantiates a new connection" do
|
26
|
-
Rapns::Daemon::Apns::Connection.should_receive(:new).with(app
|
26
|
+
Rapns::Daemon::Apns::Connection.should_receive(:new).with(app, host, port)
|
27
27
|
Rapns::Daemon::Apns::DeliveryHandler.new(app, host, port)
|
28
28
|
end
|
29
29
|
|
@@ -37,6 +37,11 @@ describe Rapns::Daemon::Apns::Delivery do
|
|
37
37
|
perform
|
38
38
|
end
|
39
39
|
|
40
|
+
it 'reflects the notification was delivered' do
|
41
|
+
delivery.should_receive(:reflect).with(:notification_delivered, notification)
|
42
|
+
perform
|
43
|
+
end
|
44
|
+
|
40
45
|
it "sets the time the notification was delivered" do
|
41
46
|
now = Time.now
|
42
47
|
Time.stub(:now).and_return(now)
|
@@ -83,6 +88,11 @@ describe Rapns::Daemon::Apns::Delivery do
|
|
83
88
|
perform
|
84
89
|
end
|
85
90
|
|
91
|
+
it 'reflects the notification delivery failed' do
|
92
|
+
delivery.should_receive(:reflect).with(:notification_failed, notification)
|
93
|
+
perform
|
94
|
+
end
|
95
|
+
|
86
96
|
it "sets the notification failed_at timestamp" do
|
87
97
|
now = Time.now
|
88
98
|
Time.stub(:now).and_return(now)
|
@@ -10,12 +10,13 @@ describe Rapns::Daemon::Apns::FeedbackReceiver, 'check_for_feedback' do
|
|
10
10
|
let(:connection) { stub(:connect => nil, :read => nil, :close => nil) }
|
11
11
|
let(:logger) { stub(:error => nil, :info => nil) }
|
12
12
|
let(:receiver) { Rapns::Daemon::Apns::FeedbackReceiver.new(app, host, port, poll) }
|
13
|
+
let(:feedback) { stub }
|
13
14
|
|
14
15
|
before do
|
15
16
|
receiver.stub(:interruptible_sleep)
|
16
17
|
Rapns::Daemon.logger = logger
|
17
18
|
Rapns::Daemon::Apns::Connection.stub(:new => connection)
|
18
|
-
Rapns::Apns::Feedback.stub(:create!)
|
19
|
+
Rapns::Apns::Feedback.stub(:create! => feedback)
|
19
20
|
receiver.instance_variable_set("@stop", false)
|
20
21
|
end
|
21
22
|
|
@@ -31,7 +32,7 @@ describe Rapns::Daemon::Apns::FeedbackReceiver, 'check_for_feedback' do
|
|
31
32
|
end
|
32
33
|
|
33
34
|
it 'instantiates a new connection' do
|
34
|
-
Rapns::Daemon::Apns::Connection.should_receive(:new).with(
|
35
|
+
Rapns::Daemon::Apns::Connection.should_receive(:new).with(app, host, port)
|
35
36
|
receiver.check_for_feedback
|
36
37
|
end
|
37
38
|
|
@@ -52,7 +53,7 @@ describe Rapns::Daemon::Apns::FeedbackReceiver, 'check_for_feedback' do
|
|
52
53
|
|
53
54
|
it 'logs the feedback' do
|
54
55
|
stub_connection_read_with_tuple
|
55
|
-
Rapns::Daemon.logger.should_receive(:info).with("[
|
56
|
+
Rapns::Daemon.logger.should_receive(:info).with("[my_app] [FeedbackReceiver] Delivery failed at 2011-12-10 16:08:45 UTC for 834f786655eb9f84614a05ad7d00af31e5cfe93ac3ea078f1da44d2a4eb0ce17.")
|
56
57
|
receiver.check_for_feedback
|
57
58
|
end
|
58
59
|
|
@@ -90,11 +91,15 @@ describe Rapns::Daemon::Apns::FeedbackReceiver, 'check_for_feedback' do
|
|
90
91
|
receiver.stop
|
91
92
|
end
|
92
93
|
|
94
|
+
it 'reflects feedback was received' do
|
95
|
+
stub_connection_read_with_tuple
|
96
|
+
receiver.should_receive(:reflect).with(:apns_feedback, feedback)
|
97
|
+
receiver.check_for_feedback
|
98
|
+
end
|
99
|
+
|
93
100
|
it 'calls the apns_feedback_callback when feedback is received and the callback is set' do
|
94
101
|
stub_connection_read_with_tuple
|
95
102
|
Rapns.config.apns_feedback_callback = Proc.new {}
|
96
|
-
feedback = Object.new
|
97
|
-
Rapns::Apns::Feedback.stub(:create! => feedback)
|
98
103
|
Rapns.config.apns_feedback_callback.should_receive(:call).with(feedback)
|
99
104
|
receiver.check_for_feedback
|
100
105
|
end
|
@@ -103,7 +108,9 @@ describe Rapns::Daemon::Apns::FeedbackReceiver, 'check_for_feedback' do
|
|
103
108
|
error = StandardError.new('bork!')
|
104
109
|
stub_connection_read_with_tuple
|
105
110
|
callback = Proc.new { raise error }
|
106
|
-
Rapns.
|
111
|
+
Rapns::Deprecation.silenced do
|
112
|
+
Rapns.config.on_apns_feedback &callback
|
113
|
+
end
|
107
114
|
expect { receiver.check_for_feedback }.not_to raise_error
|
108
115
|
end
|
109
116
|
|
@@ -112,7 +119,9 @@ describe Rapns::Daemon::Apns::FeedbackReceiver, 'check_for_feedback' do
|
|
112
119
|
stub_connection_read_with_tuple
|
113
120
|
callback = Proc.new { raise error }
|
114
121
|
Rapns::Daemon.logger.should_receive(:error).with(error)
|
115
|
-
Rapns.
|
122
|
+
Rapns::Deprecation.silenced do
|
123
|
+
Rapns.config.on_apns_feedback &callback
|
124
|
+
end
|
116
125
|
receiver.check_for_feedback
|
117
126
|
end
|
118
127
|
end
|
@@ -8,6 +8,14 @@ shared_examples_for 'an DeliveryHandler subclass' do
|
|
8
8
|
delivery_handler.send(:handle_next_notification)
|
9
9
|
end
|
10
10
|
|
11
|
+
it 'reflects an exception' do
|
12
|
+
Rapns::Daemon.stub(:logger => stub(:error => nil))
|
13
|
+
error = StandardError.new
|
14
|
+
delivery_handler.stub(:deliver).and_raise(error)
|
15
|
+
delivery_handler.should_receive(:reflect).with(:error, error)
|
16
|
+
delivery_handler.send(:handle_next_notification)
|
17
|
+
end
|
18
|
+
|
11
19
|
it "instructs the queue to wakeup the thread when told to stop" do
|
12
20
|
thread = stub(:join => nil)
|
13
21
|
Thread.stub(:new => thread)
|