rapns 3.0.1-java → 3.1.0-java

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.rb +11 -1
  7. data/lib/rapns/apns/notification.rb +8 -2
  8. data/lib/rapns/app.rb +3 -3
  9. data/lib/rapns/configuration.rb +46 -13
  10. data/lib/rapns/daemon.rb +33 -22
  11. data/lib/rapns/daemon/apns/connection.rb +12 -9
  12. data/lib/rapns/daemon/apns/delivery_handler.rb +1 -1
  13. data/lib/rapns/daemon/apns/feedback_receiver.rb +6 -2
  14. data/lib/rapns/daemon/app_runner.rb +23 -7
  15. data/lib/rapns/daemon/delivery.rb +5 -1
  16. data/lib/rapns/daemon/delivery_handler.rb +4 -0
  17. data/lib/rapns/daemon/feeder.rb +26 -5
  18. data/lib/rapns/daemon/reflectable.rb +13 -0
  19. data/lib/rapns/embed.rb +28 -0
  20. data/lib/rapns/gcm/notification.rb +7 -2
  21. data/lib/rapns/gcm/payload_data_size_validator.rb +13 -0
  22. data/lib/rapns/gcm/registration_ids_count_validator.rb +13 -0
  23. data/lib/rapns/push.rb +12 -0
  24. data/lib/rapns/reflection.rb +44 -0
  25. data/lib/rapns/version.rb +1 -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
@@ -4,18 +4,20 @@ module Rapns
4
4
  class ConnectionError < StandardError; end
5
5
 
6
6
  class Connection
7
+ include Reflectable
8
+
7
9
  attr_accessor :last_write
8
10
 
9
11
  def self.idle_period
10
12
  30.minutes
11
13
  end
12
14
 
13
- def initialize(name, host, port, certificate, password)
14
- @name = name
15
+ def initialize(app, host, port)
16
+ @app = app
15
17
  @host = host
16
18
  @port = port
17
- @certificate = certificate
18
- @password = password
19
+ @certificate = app.certificate
20
+ @password = app.password
19
21
  written
20
22
  end
21
23
 
@@ -51,7 +53,8 @@ module Rapns
51
53
  retry_count += 1;
52
54
 
53
55
  if retry_count == 1
54
- Rapns::Daemon.logger.error("[#{@name}] Lost connection to #{@host}:#{@port} (#{e.class.name}), reconnecting...")
56
+ Rapns::Daemon.logger.error("[#{@app.name}] Lost connection to #{@host}:#{@port} (#{e.class.name}), reconnecting...")
57
+ reflect(:apns_connection_lost, @app, e)
55
58
  end
56
59
 
57
60
  if retry_count <= 3
@@ -59,7 +62,7 @@ module Rapns
59
62
  sleep 1
60
63
  retry
61
64
  else
62
- raise ConnectionError, "#{@name} tried #{retry_count-1} times to reconnect but failed (#{e.class.name})."
65
+ raise ConnectionError, "#{@app.name} tried #{retry_count-1} times to reconnect but failed (#{e.class.name})."
63
66
  end
64
67
  end
65
68
  end
@@ -72,7 +75,7 @@ module Rapns
72
75
  protected
73
76
 
74
77
  def reconnect_idle
75
- Rapns::Daemon.logger.info("[#{@name}] Idle period exceeded, reconnecting...")
78
+ Rapns::Daemon.logger.info("[#{@app.name}] Idle period exceeded, reconnecting...")
76
79
  reconnect
77
80
  end
78
81
 
@@ -104,10 +107,10 @@ module Rapns
104
107
  ssl_socket = OpenSSL::SSL::SSLSocket.new(tcp_socket, @ssl_context)
105
108
  ssl_socket.sync = true
106
109
  ssl_socket.connect
107
- Rapns::Daemon.logger.info("[#{@name}] Connected to #{@host}:#{@port}")
110
+ Rapns::Daemon.logger.info("[#{@app.name}] Connected to #{@host}:#{@port}")
108
111
  [tcp_socket, ssl_socket]
109
112
  end
110
113
  end
111
114
  end
112
115
  end
113
- end
116
+ end
@@ -4,7 +4,7 @@ module Rapns
4
4
  class DeliveryHandler < Rapns::Daemon::DeliveryHandler
5
5
  def initialize(app, host, port)
6
6
  @app = app
7
- @connection = Connection.new(@app.name, host, port, @app.certificate, @app.password)
7
+ @connection = Connection.new(@app, host, port)
8
8
  @connection.connect
9
9
  end
10
10
 
@@ -2,6 +2,7 @@ module Rapns
2
2
  module Daemon
3
3
  module Apns
4
4
  class FeedbackReceiver
5
+ include Reflectable
5
6
  include InterruptibleSleep
6
7
  include DatabaseReconnectable
7
8
 
@@ -35,7 +36,7 @@ module Rapns
35
36
  def check_for_feedback
36
37
  connection = nil
37
38
  begin
38
- connection = Connection.new("FeedbackReceiver:#{@app.name}", @host, @port, @certificate, @password)
39
+ connection = Connection.new(@app, @host, @port)
39
40
  connection.connect
40
41
 
41
42
  while tuple = connection.read(FEEDBACK_TUPLE_BYTES)
@@ -59,8 +60,11 @@ module Rapns
59
60
  def create_feedback(failed_at, device_token)
60
61
  formatted_failed_at = failed_at.strftime("%Y-%m-%d %H:%M:%S UTC")
61
62
  with_database_reconnect_and_retry do
62
- Rapns::Daemon.logger.info("[FeedbackReceiver:#{@app.name}] Delivery failed at #{formatted_failed_at} for #{device_token}")
63
+ Rapns::Daemon.logger.info("[#{@app.name}] [FeedbackReceiver] Delivery failed at #{formatted_failed_at} for #{device_token}.")
63
64
  feedback = Rapns::Apns::Feedback.create!(:failed_at => failed_at, :device_token => device_token, :app => @app)
65
+ reflect(:apns_feedback, feedback)
66
+
67
+ # Deprecated.
64
68
  begin
65
69
  Rapns.config.apns_feedback_callback.call(feedback) if Rapns.config.apns_feedback_callback
66
70
  rescue StandardError => e
@@ -2,7 +2,7 @@ module Rapns
2
2
  module Daemon
3
3
  class AppRunner
4
4
  class << self
5
- attr_reader :runners # TODO: Needed?
5
+ attr_reader :runners
6
6
  end
7
7
 
8
8
  @runners = {}
@@ -86,20 +86,28 @@ module Rapns
86
86
  diff = handlers.size - app.connections
87
87
  return if diff == 0
88
88
  if diff > 0
89
- diff.times { handlers.pop.stop }
90
- Rapns::Daemon.logger.info("[#{app.name}] Terminated #{handlers_str(diff)}. #{handlers_str} remaining.")
89
+ diff.times { decrement_handlers }
90
+ Rapns::Daemon.logger.info("[#{app.name}] Stopped #{handlers_str(diff)}. #{handlers_str} remaining.")
91
91
  else
92
- diff.abs.times { handlers << start_handler }
93
- Rapns::Daemon.logger.info("[#{app.name}] Added #{handlers_str(diff)}. #{handlers_str} remaining.")
92
+ diff.abs.times { increment_handlers }
93
+ Rapns::Daemon.logger.info("[#{app.name}] Started #{handlers_str(diff)}. #{handlers_str} remaining.")
94
94
  end
95
95
  end
96
96
 
97
+ def decrement_handlers
98
+ handlers.pop.stop
99
+ end
100
+
101
+ def increment_handlers
102
+ handlers << start_handler
103
+ end
104
+
97
105
  def debug
98
106
  Rapns::Daemon.logger.info <<-EOS
99
107
 
100
108
  #{@app.name}:
101
- handlers: #{handlers.size}
102
- queued: #{queue.size}
109
+ handlers: #{num_handlers}
110
+ queued: #{queue_size}
103
111
  idle: #{idle?}
104
112
  EOS
105
113
  end
@@ -108,6 +116,14 @@ module Rapns
108
116
  queue.notifications_processed?
109
117
  end
110
118
 
119
+ def queue_size
120
+ queue.size
121
+ end
122
+
123
+ def num_handlers
124
+ handlers.size
125
+ end
126
+
111
127
  protected
112
128
 
113
129
  def start_handler
@@ -2,6 +2,7 @@ module Rapns
2
2
  module Daemon
3
3
  class Delivery
4
4
  include DatabaseReconnectable
5
+ include Reflectable
5
6
 
6
7
  def self.perform(*args)
7
8
  new(*args).perform
@@ -13,6 +14,7 @@ module Rapns
13
14
  notification.deliver_after = deliver_after
14
15
  notification.save!(:validate => false)
15
16
  end
17
+ reflect(:notification_will_retry, notification)
16
18
  end
17
19
 
18
20
  def retry_exponentially(notification)
@@ -25,6 +27,7 @@ module Rapns
25
27
  @notification.delivered_at = Time.now
26
28
  @notification.save!(:validate => false)
27
29
  end
30
+ reflect(:notification_delivered, @notification)
28
31
  end
29
32
 
30
33
  def mark_failed(code, description)
@@ -37,7 +40,8 @@ module Rapns
37
40
  @notification.error_description = description
38
41
  @notification.save!(:validate => false)
39
42
  end
43
+ reflect(:notification_failed, @notification)
40
44
  end
41
45
  end
42
46
  end
43
- end
47
+ end
@@ -1,6 +1,8 @@
1
1
  module Rapns
2
2
  module Daemon
3
3
  class DeliveryHandler
4
+ include Reflectable
5
+
4
6
  attr_accessor :queue
5
7
 
6
8
  def start
@@ -35,8 +37,10 @@ module Rapns
35
37
 
36
38
  begin
37
39
  deliver(notification)
40
+ reflect(:notification_delivered, notification)
38
41
  rescue StandardError => e
39
42
  Rapns::Daemon.logger.error(e)
43
+ reflect(:error, e)
40
44
  ensure
41
45
  queue.notification_processed
42
46
  end
@@ -3,12 +3,17 @@ module Rapns
3
3
  class Feeder
4
4
  extend InterruptibleSleep
5
5
  extend DatabaseReconnectable
6
+ extend Reflectable
6
7
 
7
- def self.start(poll)
8
- loop do
8
+ def self.start
9
+ @stop = false
10
+
11
+ if Rapns.config.embedded
12
+ Thread.new { feed_forever }
13
+ elsif Rapns.config.push
9
14
  enqueue_notifications
10
- interruptible_sleep poll
11
- break if @stop
15
+ else
16
+ feed_forever
12
17
  end
13
18
  end
14
19
 
@@ -19,17 +24,33 @@ module Rapns
19
24
 
20
25
  protected
21
26
 
27
+ def self.feed_forever
28
+ loop do
29
+ enqueue_notifications
30
+ interruptible_sleep(Rapns.config.push_poll)
31
+ break if stop?
32
+ end
33
+ end
34
+
35
+ def self.stop?
36
+ @stop
37
+ end
38
+
22
39
  def self.enqueue_notifications
23
40
  begin
24
41
  with_database_reconnect_and_retry do
25
42
  batch_size = Rapns.config.batch_size
26
43
  idle = Rapns::Daemon::AppRunner.idle.map(&:app)
27
- Rapns::Notification.ready_for_delivery.for_apps(idle).limit(batch_size).each do |notification|
44
+ relation = Rapns::Notification.ready_for_delivery.for_apps(idle)
45
+ relation = relation.limit(batch_size) unless Rapns.config.push
46
+ relation.each do |notification|
28
47
  Rapns::Daemon::AppRunner.enqueue(notification)
48
+ reflect(:notification_enqueued, notification)
29
49
  end
30
50
  end
31
51
  rescue StandardError => e
32
52
  Rapns::Daemon.logger.error(e)
53
+ reflect(:error, e)
33
54
  end
34
55
  end
35
56
  end
@@ -0,0 +1,13 @@
1
+ module Rapns
2
+ module Daemon
3
+ module Reflectable
4
+ def reflect(name, *args)
5
+ begin
6
+ Rapns.reflections.__dispatch(name, *args)
7
+ rescue StandardError => e
8
+ Rapns::Daemon.logger.error(e)
9
+ end
10
+ end
11
+ end
12
+ end
13
+ 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
@@ -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
+ dwt8Z0+2alFCo/YDjyd2xDhVCpmmxn5BT2wVTZiCEJrlSIY99oQyQWDy/152X5ZE
42
+ dl3V014PtDjYyHeMh+V3Ws98hPxyTvymkQsDfQKhKHpg2IhEsubZi+crlKj2NQkm
43
+ i6+0t6v3sRLYbJnxbAKRa8rzLn2Q18vxflrWqO8WwDj+RuPevUBZEU5pceh3CyWu
44
+ qQ0MDcj1KSeM6SSJGnVw0Lk4p6HFzPU5xkgPO1lp5Abrm0G01F8xmS38sMjuMyfI
45
+ OZeuHfOX2VUTNPliRuAVa0SlioTIqsDFZCTTRLdjxNe8P1szJTXAhCn31TR2Z24m
46
+ iqEVDyxgR4M/qtR2QNkXdzAp7YlnrWlMp08vo+l7DyLYd+HOchN/VXk+3CU2B7w7
47
+ dUlqA8lwh/s5h7xiXZ35QR9PmF7Gih7Q2QrCpy3PhJt8V3/cSiNwg5wikBXP2Tep
48
+ 28X7qgTWBulmkp/R9DO3rUSR4Boc+UfvswI7/FQczcaQGJpedDY5f/7lJPoIKJL2
49
+ 5Ix9kr/inyUPnQZpNEmJmaKO0lyei6DawFozagT1XntYewzENFIYUqV6ZajLMuTe
50
+ VHkLUqK1M/yVgR2NCyKLFZHMAdTcYhdClSb0YvE++hevyWFxdD13TyWmHB9+UL3o
51
+ 29dWhBEA9nk/mVTGIFVmk6fF+QaWlKMVFxgdlYThTmk/1ZUyH0BqPWYE56Ux7Tmp
52
+ hP5wZvRzaF5fV/dlfFRXZ0S0LFK11ld4Oaps18OuCzYKNTr9alaFfChqFtddVBcf
53
+ HY1DEeCF4p59ptsTalTqrO4ieDFkf5da3ZAyC32X8pzaD9+pPwm8vBBFtamXp70V
54
+ jJt2K8jlS5S7KL5ZliBMrGGJZF+jMQh1SBRdFn/h+VFulaH+qDyIAvkwyeWV4V0t
55
+ rO4HroZalJIlraqXGPLyX7/QWTetqSvCUR8mZcckUIIHseeP6xeLFvxs7Y58ns2Y
56
+ Rw+B7UI253YEwUF9N5vBddqN7fCQlbrxpjMOMT/p+DZzuS8evayevjbYwIS8vssQ
57
+ hMjm4iTB4daAjMzWCKaBTFXQTRV4OfzXRYZaM3zeNYzTxakX+BPUX4R5Sf6VpDP3
58
+ vYyXpoIIG/n+6B7qXUMoQXprj/T5XzJQpQK6B+ubmFmuEjbWrCy/MdLGceV6pxxW
59
+ OW1xtCUDLjrd2UAFIfJRJLtevr1Fvin3xZYhvtrhwMPhk9JKOr/6ubLvL+5oturN
60
+ YzaIDRjE33XSEcOPCSPCymNaDStzpxKNbsM8POEne8qVSRK+D+9YDbmqbSLQR1vN
61
+ 07CTgbclTrvOUKZYL0nr9g99oFj/ldYPDrNzVd48MVmhvJZOuz1CApKZ4UcUO5EU
62
+ jfOqTtdFbZSbccOdGgQN39GmrQ/Ys0cj15VbymgNiOpk3dEMQli/iGBW9F+oBs4X
63
+ dRLvephnfOBRlB/4PVjrXuzLI1rQXhlGkEX4ik1HVQviti+7g2y+2IQvBu0C494n
64
+ g7PoAIoGQDPciCfBodxOWwg9dCXhmlcZZa3MDMEdFQ8dV5ZYdPBzbzqRhasbIZUR
65
+ K/b1qU6MUWoC7HfCXK8DUHZvvEi/uZT6zPQnukOPxf7jS6yMrmdFdT6v0qh3PYOt
66
+ LTjB1HMeDc2ku88y185yU8EFryV8B06WITHuhLZG1AqvS0KkD+vokcj5mXFFtPz0
67
+ FI5GcBQ/U7DF31BTRUhhOZV+MdCaRZMfhvQiEr+9axS04qO5okV6mvXknGJYN47m
68
+ 4s0Z2kgtnIpwMMlDxeuBGa2Qt/FL5i6JIJ8df265CnPBf9lPloTwsHohMPrnSH1y
69
+ VXaobhYJogSXj4A0WW/Kb4aW64mCKpGrm05ed/cBQ5TM/DQssPYxD1sibXBBR0Yq
70
+ iMa5JhOripEo7EceX8btsGUUDRNwDUZcaeeqJ5VEKnYW86LKpubMViIJeKoPzAX7
71
+ 4HsoR+g4G2nok1wntcGGszXk49iGuo59gDlnN6o6C3OY70L8AAN8DhxHQk3edzjV
72
+ ZIGm24y6Icz77qajgLGzhGHvZQ8f8910LNbyjGKrFKIA4m8PRvN/ZXjd2WWAB0Td
73
+ zbBGmYnonOQp7V9oD8bbUlofnSsav94QaeedI7W5is6cX01GPoHBnV85y9z44/+L
74
+ yDTt3ZIToMjq8gbWeEOoFI0sxf+uok5tDMnIFr4pAW0fitsRI0k/hUeUaGxuQnU1
75
+ zgLQia/+zWLAMgoaU+yGRvUW/SnBHR3EayNzKlLlVWK7cY4+TF0fYOYzebTsrfN0
76
+ w9KNjq3ahoofVcnj51euuvEpDXE2s9ZYsW7kYH475giYJxlJUNkq1nxqw5u1IZp3
77
+ /VmR7Vg/EzrN/vjvohn659fpYBBvPYcd0m+CFEzXdhJTBVY/AKK6BZTwiNUCoNPq
78
+ d0JsRdhrEmuVCk+LrEdkNFVXGOpCejsgRxHNVlnsO+V+imy+rrI/G1r7nNgA0QMp
79
+ R8sf26MpekASRQPmYmlP7Pq/kjIAdwfuEE4gNlec95/GoHnbHoHyyMHqxqudSJpA
80
+ mlbZs/uSiOU2uoPRRtVkZET82F7yz4zKLWNzYyAjCkVwXcHMOeZQMnh1SacR8YRM
81
+ Qqn/dd2TU5Xw3XBO/fplaznct9Svppx0e3XniLGkHN/rKN8Co6gH99GHOWQ/Mekx
82
+ MeMxxKbXQS2HGcPAkCGSa9PdD+/xuGWVdCOwPnLSRU+nLh+b3VoiLRPd+GE5MX6+
83
+ ClVANBp5Gi5Q21tDsGnIPJS8s6WCNa8jafBvAPi0J1w5eFfRIBXooFUfHMwXx+NF
84
+ udopf6S6OavheOLNstLUKlyn8tKgPcGide6Sl3fxHOULvGgLWM9IAM5ta0U6SkyX
85
+ mqW6pIVFc8OV73FEZXvUo3aztEhuB6wa7bC0xohbulDUHE56BfFt/OWLNhBDCkc6
86
+ RfbHBoPFv5oKz5zqcKw9tCx5pL33vLRhEsd11/0jDWJpPvp1pbHq3D75by7xmJt9
87
+ Vh+RRQgVfrF0Z6QD9hD/rBChmZLGEkmLNuLgP5DB14ebzdxL4NF1GdcIuXTWtv22
88
+ lA032Iv2YQQUA/2fvp2XzAmE9Uvma2cj50e65Ky9iJ5O6suDfBwc4UPYQ6gI8BxA
89
+ Hzd1Xl9Zs3f7b4eRKhu+V1kjloW4tfCurPme72cgvTtZacgUTRJmlcq1OtXxpt+V
90
+ -----END RSA PRIVATE KEY-----