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.
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)