rapns 3.0.1 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/CHANGELOG.md +10 -2
  2. data/README.md +26 -3
  3. data/bin/rapns +2 -7
  4. data/lib/generators/templates/add_gcm.rb +3 -1
  5. data/lib/generators/templates/rapns.rb +42 -11
  6. data/lib/rapns/apns/notification.rb +8 -2
  7. data/lib/rapns/app.rb +3 -3
  8. data/lib/rapns/configuration.rb +46 -13
  9. data/lib/rapns/daemon/apns/connection.rb +12 -9
  10. data/lib/rapns/daemon/apns/delivery_handler.rb +1 -1
  11. data/lib/rapns/daemon/apns/feedback_receiver.rb +6 -2
  12. data/lib/rapns/daemon/app_runner.rb +23 -7
  13. data/lib/rapns/daemon/delivery.rb +5 -1
  14. data/lib/rapns/daemon/delivery_handler.rb +4 -0
  15. data/lib/rapns/daemon/feeder.rb +26 -5
  16. data/lib/rapns/daemon/reflectable.rb +13 -0
  17. data/lib/rapns/daemon.rb +33 -22
  18. data/lib/rapns/embed.rb +28 -0
  19. data/lib/rapns/gcm/notification.rb +7 -2
  20. data/lib/rapns/gcm/payload_data_size_validator.rb +13 -0
  21. data/lib/rapns/gcm/registration_ids_count_validator.rb +13 -0
  22. data/lib/rapns/push.rb +12 -0
  23. data/lib/rapns/reflection.rb +44 -0
  24. data/lib/rapns/version.rb +1 -1
  25. data/lib/rapns.rb +11 -1
  26. data/spec/support/cert_with_password.pem +90 -0
  27. data/spec/support/cert_without_password.pem +59 -0
  28. data/spec/unit/apns/app_spec.rb +15 -1
  29. data/spec/unit/apns/notification_spec.rb +16 -1
  30. data/spec/unit/configuration_spec.rb +10 -1
  31. data/spec/unit/daemon/apns/connection_spec.rb +11 -2
  32. data/spec/unit/daemon/apns/delivery_handler_spec.rb +1 -1
  33. data/spec/unit/daemon/apns/delivery_spec.rb +10 -0
  34. data/spec/unit/daemon/apns/feedback_receiver_spec.rb +16 -7
  35. data/spec/unit/daemon/delivery_handler_shared.rb +8 -0
  36. data/spec/unit/daemon/feeder_spec.rb +37 -6
  37. data/spec/unit/daemon/gcm/delivery_spec.rb +33 -1
  38. data/spec/unit/daemon/reflectable_spec.rb +27 -0
  39. data/spec/unit/daemon_spec.rb +55 -9
  40. data/spec/unit/embed_spec.rb +44 -0
  41. data/spec/unit/gcm/notification_spec.rb +9 -3
  42. data/spec/unit/push_spec.rb +28 -0
  43. data/spec/unit/reflection_spec.rb +34 -0
  44. data/spec/unit_spec_helper.rb +4 -62
  45. metadata +22 -5
  46. 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
- unless Rapns.config.foreground
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(Rapns.config.push_poll)
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.setup_signal_hooks
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
- exit if pid = fork
125
- Process.setsid
126
- exit if pid = fork
127
-
128
- Dir.chdir '/'
129
- File.umask 0000
130
-
131
- STDIN.reopen '/dev/null'
132
- STDOUT.reopen '/dev/null', 'a'
133
- STDERR.reopen STDOUT
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
@@ -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::PayloadSizeValidator
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
@@ -1,3 +1,3 @@
1
1
  module Rapns
2
- VERSION = '3.0.1'
2
+ VERSION = '3.1.0'
3
3
  end
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/payload_size_validator'
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-----
@@ -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 real certificate' do
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 convert-available as a non-aps attribute' do
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
- config.on_apns_feedback(&b)
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(:connection) { Rapns::Daemon::Apns::Connection.new('Connection 0', host, port, certificate, password) }
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.name, host, port, certificate, password)
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("FeedbackReceiver:#{app.name}", host, port, certificate, password)
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("[FeedbackReceiver:my_app] Delivery failed at 2011-12-10 16:08:45 UTC for 834f786655eb9f84614a05ad7d00af31e5cfe93ac3ea078f1da44d2a4eb0ce17")
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.config.on_apns_feedback &callback
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.config.on_apns_feedback &callback
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)