rpush 1.0.0-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (145) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +99 -0
  3. data/LICENSE +7 -0
  4. data/README.md +189 -0
  5. data/bin/rpush +36 -0
  6. data/config/database.yml +44 -0
  7. data/lib/generators/rpush_generator.rb +44 -0
  8. data/lib/generators/templates/add_adm.rb +23 -0
  9. data/lib/generators/templates/add_alert_is_json_to_rapns_notifications.rb +9 -0
  10. data/lib/generators/templates/add_app_to_rapns.rb +11 -0
  11. data/lib/generators/templates/add_fail_after_to_rpush_notifications.rb +9 -0
  12. data/lib/generators/templates/add_gcm.rb +102 -0
  13. data/lib/generators/templates/add_rpush.rb +349 -0
  14. data/lib/generators/templates/add_wpns.rb +16 -0
  15. data/lib/generators/templates/create_rapns_apps.rb +16 -0
  16. data/lib/generators/templates/create_rapns_feedback.rb +18 -0
  17. data/lib/generators/templates/create_rapns_notifications.rb +29 -0
  18. data/lib/generators/templates/rename_rapns_to_rpush.rb +63 -0
  19. data/lib/generators/templates/rpush.rb +104 -0
  20. data/lib/rpush/TODO +3 -0
  21. data/lib/rpush/adm/app.rb +15 -0
  22. data/lib/rpush/adm/data_validator.rb +11 -0
  23. data/lib/rpush/adm/notification.rb +29 -0
  24. data/lib/rpush/apns/app.rb +29 -0
  25. data/lib/rpush/apns/binary_notification_validator.rb +12 -0
  26. data/lib/rpush/apns/device_token_format_validator.rb +12 -0
  27. data/lib/rpush/apns/feedback.rb +16 -0
  28. data/lib/rpush/apns/notification.rb +84 -0
  29. data/lib/rpush/apns_feedback.rb +13 -0
  30. data/lib/rpush/app.rb +18 -0
  31. data/lib/rpush/configuration.rb +75 -0
  32. data/lib/rpush/daemon/adm/delivery.rb +222 -0
  33. data/lib/rpush/daemon/adm.rb +9 -0
  34. data/lib/rpush/daemon/apns/certificate_expired_error.rb +20 -0
  35. data/lib/rpush/daemon/apns/delivery.rb +64 -0
  36. data/lib/rpush/daemon/apns/disconnection_error.rb +20 -0
  37. data/lib/rpush/daemon/apns/feedback_receiver.rb +79 -0
  38. data/lib/rpush/daemon/apns.rb +16 -0
  39. data/lib/rpush/daemon/app_runner.rb +187 -0
  40. data/lib/rpush/daemon/batch.rb +115 -0
  41. data/lib/rpush/daemon/constants.rb +59 -0
  42. data/lib/rpush/daemon/delivery.rb +28 -0
  43. data/lib/rpush/daemon/delivery_error.rb +19 -0
  44. data/lib/rpush/daemon/dispatcher/http.rb +21 -0
  45. data/lib/rpush/daemon/dispatcher/tcp.rb +30 -0
  46. data/lib/rpush/daemon/dispatcher_loop.rb +54 -0
  47. data/lib/rpush/daemon/dispatcher_loop_collection.rb +33 -0
  48. data/lib/rpush/daemon/feeder.rb +68 -0
  49. data/lib/rpush/daemon/gcm/delivery.rb +222 -0
  50. data/lib/rpush/daemon/gcm.rb +9 -0
  51. data/lib/rpush/daemon/interruptible_sleep.rb +61 -0
  52. data/lib/rpush/daemon/loggable.rb +31 -0
  53. data/lib/rpush/daemon/reflectable.rb +13 -0
  54. data/lib/rpush/daemon/retry_header_parser.rb +23 -0
  55. data/lib/rpush/daemon/retryable_error.rb +20 -0
  56. data/lib/rpush/daemon/service_config_methods.rb +33 -0
  57. data/lib/rpush/daemon/store/active_record/reconnectable.rb +68 -0
  58. data/lib/rpush/daemon/store/active_record.rb +154 -0
  59. data/lib/rpush/daemon/tcp_connection.rb +143 -0
  60. data/lib/rpush/daemon/too_many_requests_error.rb +20 -0
  61. data/lib/rpush/daemon/wpns/delivery.rb +132 -0
  62. data/lib/rpush/daemon/wpns.rb +9 -0
  63. data/lib/rpush/daemon.rb +140 -0
  64. data/lib/rpush/deprecatable.rb +23 -0
  65. data/lib/rpush/deprecation.rb +23 -0
  66. data/lib/rpush/embed.rb +28 -0
  67. data/lib/rpush/gcm/app.rb +11 -0
  68. data/lib/rpush/gcm/expiry_collapse_key_mutual_inclusion_validator.rb +11 -0
  69. data/lib/rpush/gcm/notification.rb +30 -0
  70. data/lib/rpush/logger.rb +63 -0
  71. data/lib/rpush/multi_json_helper.rb +16 -0
  72. data/lib/rpush/notification.rb +69 -0
  73. data/lib/rpush/notifier.rb +52 -0
  74. data/lib/rpush/payload_data_size_validator.rb +10 -0
  75. data/lib/rpush/push.rb +16 -0
  76. data/lib/rpush/railtie.rb +11 -0
  77. data/lib/rpush/reflection.rb +58 -0
  78. data/lib/rpush/registration_ids_count_validator.rb +10 -0
  79. data/lib/rpush/version.rb +3 -0
  80. data/lib/rpush/wpns/app.rb +9 -0
  81. data/lib/rpush/wpns/notification.rb +26 -0
  82. data/lib/rpush.rb +62 -0
  83. data/lib/tasks/cane.rake +18 -0
  84. data/lib/tasks/rpush.rake +16 -0
  85. data/lib/tasks/test.rake +38 -0
  86. data/spec/functional/adm_spec.rb +43 -0
  87. data/spec/functional/apns_spec.rb +58 -0
  88. data/spec/functional/embed_spec.rb +49 -0
  89. data/spec/functional/gcm_spec.rb +42 -0
  90. data/spec/functional/wpns_spec.rb +41 -0
  91. data/spec/support/cert_with_password.pem +90 -0
  92. data/spec/support/cert_without_password.pem +59 -0
  93. data/spec/support/install.sh +32 -0
  94. data/spec/support/simplecov_helper.rb +20 -0
  95. data/spec/support/simplecov_quality_formatter.rb +8 -0
  96. data/spec/tmp/.gitkeep +0 -0
  97. data/spec/unit/adm/app_spec.rb +58 -0
  98. data/spec/unit/adm/notification_spec.rb +45 -0
  99. data/spec/unit/apns/app_spec.rb +29 -0
  100. data/spec/unit/apns/feedback_spec.rb +9 -0
  101. data/spec/unit/apns/notification_spec.rb +208 -0
  102. data/spec/unit/apns_feedback_spec.rb +21 -0
  103. data/spec/unit/app_spec.rb +30 -0
  104. data/spec/unit/configuration_spec.rb +45 -0
  105. data/spec/unit/daemon/adm/delivery_spec.rb +243 -0
  106. data/spec/unit/daemon/apns/certificate_expired_error_spec.rb +11 -0
  107. data/spec/unit/daemon/apns/delivery_spec.rb +101 -0
  108. data/spec/unit/daemon/apns/disconnection_error_spec.rb +18 -0
  109. data/spec/unit/daemon/apns/feedback_receiver_spec.rb +117 -0
  110. data/spec/unit/daemon/app_runner_spec.rb +292 -0
  111. data/spec/unit/daemon/batch_spec.rb +232 -0
  112. data/spec/unit/daemon/delivery_error_spec.rb +13 -0
  113. data/spec/unit/daemon/delivery_spec.rb +38 -0
  114. data/spec/unit/daemon/dispatcher/http_spec.rb +33 -0
  115. data/spec/unit/daemon/dispatcher/tcp_spec.rb +38 -0
  116. data/spec/unit/daemon/dispatcher_loop_collection_spec.rb +37 -0
  117. data/spec/unit/daemon/dispatcher_loop_spec.rb +71 -0
  118. data/spec/unit/daemon/feeder_spec.rb +98 -0
  119. data/spec/unit/daemon/gcm/delivery_spec.rb +310 -0
  120. data/spec/unit/daemon/interruptible_sleep_spec.rb +68 -0
  121. data/spec/unit/daemon/reflectable_spec.rb +27 -0
  122. data/spec/unit/daemon/retryable_error_spec.rb +14 -0
  123. data/spec/unit/daemon/service_config_methods_spec.rb +33 -0
  124. data/spec/unit/daemon/store/active_record/reconnectable_spec.rb +114 -0
  125. data/spec/unit/daemon/store/active_record_spec.rb +357 -0
  126. data/spec/unit/daemon/tcp_connection_spec.rb +287 -0
  127. data/spec/unit/daemon/too_many_requests_error_spec.rb +14 -0
  128. data/spec/unit/daemon/wpns/delivery_spec.rb +159 -0
  129. data/spec/unit/daemon_spec.rb +159 -0
  130. data/spec/unit/deprecatable_spec.rb +32 -0
  131. data/spec/unit/deprecation_spec.rb +15 -0
  132. data/spec/unit/embed_spec.rb +50 -0
  133. data/spec/unit/gcm/app_spec.rb +4 -0
  134. data/spec/unit/gcm/notification_spec.rb +36 -0
  135. data/spec/unit/logger_spec.rb +127 -0
  136. data/spec/unit/notification_shared.rb +105 -0
  137. data/spec/unit/notification_spec.rb +15 -0
  138. data/spec/unit/notifier_spec.rb +49 -0
  139. data/spec/unit/push_spec.rb +43 -0
  140. data/spec/unit/reflection_spec.rb +30 -0
  141. data/spec/unit/rpush_spec.rb +9 -0
  142. data/spec/unit/wpns/app_spec.rb +4 -0
  143. data/spec/unit/wpns/notification_spec.rb +30 -0
  144. data/spec/unit_spec_helper.rb +101 -0
  145. metadata +304 -0
@@ -0,0 +1,143 @@
1
+ module Rpush
2
+ module Daemon
3
+ class TcpConnectionError < StandardError; end
4
+
5
+ class TcpConnection
6
+ include Reflectable
7
+ include Loggable
8
+
9
+ attr_accessor :last_write
10
+
11
+ def self.idle_period
12
+ 30.minutes
13
+ end
14
+
15
+ def initialize(app, host, port)
16
+ @app = app
17
+ @host = host
18
+ @port = port
19
+ @certificate = app.certificate
20
+ @password = app.password
21
+ written
22
+ end
23
+
24
+ def connect
25
+ @ssl_context = setup_ssl_context
26
+ @tcp_socket, @ssl_socket = connect_socket
27
+ end
28
+
29
+ def close
30
+ begin
31
+ @ssl_socket.close if @ssl_socket
32
+ @tcp_socket.close if @tcp_socket
33
+ rescue IOError
34
+ end
35
+ end
36
+
37
+ def read(num_bytes)
38
+ @ssl_socket.read(num_bytes)
39
+ end
40
+
41
+ def select(timeout)
42
+ IO.select([@ssl_socket], nil, nil, timeout)
43
+ end
44
+
45
+ def write(data)
46
+ reconnect_idle if idle_period_exceeded?
47
+
48
+ retry_count = 0
49
+
50
+ begin
51
+ write_data(data)
52
+ rescue Errno::EPIPE, Errno::ETIMEDOUT, OpenSSL::SSL::SSLError, IOError => e
53
+ retry_count += 1;
54
+
55
+ if retry_count == 1
56
+ log_error("Lost connection to #{@host}:#{@port} (#{e.class.name}), reconnecting...")
57
+ reflect(:apns_connection_lost, @app, e) # deprecated
58
+ reflect(:tcp_connection_lost, @app, e)
59
+ end
60
+
61
+ if retry_count <= 3
62
+ reconnect
63
+ sleep 1
64
+ retry
65
+ else
66
+ raise TcpConnectionError, "#{@app.name} tried #{retry_count-1} times to reconnect but failed (#{e.class.name})."
67
+ end
68
+ end
69
+ end
70
+
71
+ def reconnect
72
+ close
73
+ @tcp_socket, @ssl_socket = connect_socket
74
+ end
75
+
76
+ protected
77
+
78
+ def reconnect_idle
79
+ log_info("Idle period exceeded, reconnecting...")
80
+ reconnect
81
+ end
82
+
83
+ def idle_period_exceeded?
84
+ Time.now - last_write > self.class.idle_period
85
+ end
86
+
87
+ def write_data(data)
88
+ @ssl_socket.write(data)
89
+ @ssl_socket.flush
90
+ written
91
+ end
92
+
93
+ def written
94
+ self.last_write = Time.now
95
+ end
96
+
97
+ def setup_ssl_context
98
+ ssl_context = OpenSSL::SSL::SSLContext.new
99
+ ssl_context.key = OpenSSL::PKey::RSA.new(@certificate, @password)
100
+ ssl_context.cert = OpenSSL::X509::Certificate.new(@certificate)
101
+ ssl_context
102
+ end
103
+
104
+ def connect_socket
105
+ check_certificate_expiration
106
+
107
+ tcp_socket = TCPSocket.new(@host, @port)
108
+ tcp_socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, 1)
109
+ tcp_socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
110
+ ssl_socket = OpenSSL::SSL::SSLSocket.new(tcp_socket, @ssl_context)
111
+ ssl_socket.sync = true
112
+ ssl_socket.connect
113
+ log_info("Connected to #{@host}:#{@port}")
114
+ [tcp_socket, ssl_socket]
115
+ end
116
+
117
+ def check_certificate_expiration
118
+ cert = @ssl_context.cert
119
+ if certificate_expired?
120
+ log_error(certificate_msg('expired'))
121
+ raise Rpush::Apns::CertificateExpiredError.new(@app, cert.not_after)
122
+ elsif certificate_expires_soon?
123
+ log_warn(certificate_msg('will expire'))
124
+ reflect(:apns_certificate_will_expire, @app, cert.not_after) # deprecated
125
+ reflect(:ssl_certificate_will_expire, @app, cert.not_after)
126
+ end
127
+ end
128
+
129
+ def certificate_msg(msg)
130
+ time = @ssl_context.cert.not_after.utc.strftime("%Y-%m-%d %H:%M:%S UTC")
131
+ "Certificate #{msg} at #{time}."
132
+ end
133
+
134
+ def certificate_expired?
135
+ @ssl_context.cert.not_after && @ssl_context.cert.not_after.utc < Time.now.utc
136
+ end
137
+
138
+ def certificate_expires_soon?
139
+ @ssl_context.cert.not_after && @ssl_context.cert.not_after.utc < (Time.now + 1.month).utc
140
+ end
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,20 @@
1
+ module Rpush
2
+ class TooManyRequestsError < StandardError
3
+ attr_reader :code, :description, :response
4
+
5
+ def initialize(code, notification_id, description, response)
6
+ @code = code
7
+ @notification_id = notification_id
8
+ @description = description
9
+ @response = response
10
+ end
11
+
12
+ def to_s
13
+ message
14
+ end
15
+
16
+ def message
17
+ "Too many requests for #{@notification_id}, received error #{@code} (#{@description}) - retry after #{@response.header['retry-after']}"
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,132 @@
1
+ module Rpush
2
+ module Daemon
3
+ module Wpns
4
+
5
+ # http://msdn.microsoft.com/en-us/library/windowsphone/develop/ff941100%28v=vs.105%29.aspx
6
+ class Delivery < Rpush::Daemon::Delivery
7
+
8
+ FAILURE_MESSAGES = {
9
+ 400 => 'Bad XML or malformed notification URI.',
10
+ 401 => 'Unauthorized to send a notification to this app.'
11
+ }
12
+
13
+ def initialize(app, http, notification, batch)
14
+ @app = app
15
+ @http = http
16
+ @notification = notification
17
+ @batch = batch
18
+ end
19
+
20
+ def perform
21
+ begin
22
+ handle_response(do_post)
23
+ rescue Rpush::DeliveryError => error
24
+ mark_failed(error.code, error.description)
25
+ raise
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def handle_response(response)
32
+ code = response.code.to_i
33
+ case code
34
+ when 200
35
+ ok(response)
36
+ when 406
37
+ not_acceptable(response)
38
+ when 412
39
+ precondition_failed(response)
40
+ when 503
41
+ service_unavailable(response)
42
+ else
43
+ handle_failure(code)
44
+ end
45
+ end
46
+
47
+ def handle_failure(code, msg=nil)
48
+ unless msg
49
+ msg = if FAILURE_MESSAGES.key?(code)
50
+ FAILURE_MESSAGES[code]
51
+ else
52
+ Rpush::Daemon::HTTP_STATUS_CODES[code]
53
+ end
54
+ end
55
+ raise Rpush::DeliveryError.new(code, @notification.id, msg)
56
+ end
57
+
58
+ def ok(response)
59
+ status = status_from_response(response)
60
+ case status[:notification]
61
+ when ["Received"]
62
+ mark_delivered
63
+ log_info("#{@notification.id} sent successfully")
64
+ when ["QueueFull"]
65
+ mark_retryable(@notification, Time.now + (60*10))
66
+ log_warn("#{@notification.id} cannot be sent. The Queue is full.")
67
+ when ["Suppressed"]
68
+ handle_failure(200, "Notification was received but suppressed by the service.")
69
+ end
70
+ end
71
+
72
+ def not_acceptable(response)
73
+ retry_notification("Per-day throttling limit reached.")
74
+ end
75
+
76
+ def precondition_failed(response)
77
+ retry_notification("Device unreachable.")
78
+ end
79
+
80
+ def service_unavailable(response)
81
+ mark_retryable_exponential(@notification)
82
+ log_warn("Service Unavailable. " + retry_message)
83
+ end
84
+
85
+ def retry_message
86
+ "Notification #{@notification.id} will be retried after #{@notification.deliver_after.strftime("%Y-%m-%d %H:%M:%S")} (retry #{@notification.retries})."
87
+ end
88
+
89
+ def retry_notification(reason)
90
+ deliver_after = Time.now + (60*60)
91
+ mark_retryable(@notification, deliver_after)
92
+ log_warn("#{reason} " + retry_message)
93
+ end
94
+
95
+ def do_post
96
+ body = notification_to_xml
97
+ header = {
98
+ "Content-Length" => body.length.to_s,
99
+ "Content-Type" => "text/xml",
100
+ "X-WindowsPhone-Target" => "toast",
101
+ "X-NotificationClass" => '2'
102
+ }
103
+ post = Net::HTTP::Post.new(URI.parse(@notification.uri).path, initheader=header)
104
+ post.body = body
105
+ @http.request(URI.parse(@notification.uri), post)
106
+ end
107
+
108
+ def status_from_response(response)
109
+ headers = response.to_hash
110
+ {
111
+ notification: headers["x-notificationstatus"],
112
+ notification_channel: headers["x-subscriptionstatus"],
113
+ device_connection: headers["x-deviceconnectionstatus"]
114
+ }
115
+ end
116
+
117
+ def notification_to_xml
118
+ msg = @notification.alert.gsub(/&/, "&amp;").gsub(/</, "&lt;") \
119
+ .gsub(/>/, "&gt;").gsub(/'/, "&apos;").gsub(/"/, "&quot;")
120
+ <<-EOF
121
+ <?xml version="1.0" encoding="utf-8"?>
122
+ <wp:Notification xmlns:wp="WPNotification">
123
+ <wp:Toast>
124
+ <wp:Text1>#{msg}</wp:Text1>
125
+ </wp:Toast>
126
+ </wp:Notification>
127
+ EOF
128
+ end
129
+ end
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,9 @@
1
+ module Rpush
2
+ module Daemon
3
+ module Wpns
4
+ extend ServiceConfigMethods
5
+
6
+ dispatcher :http
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,140 @@
1
+ require 'thread'
2
+ require 'socket'
3
+ require 'pathname'
4
+ require 'openssl'
5
+
6
+ require 'net/http/persistent'
7
+
8
+ require 'rpush/daemon/constants'
9
+ require 'rpush/daemon/reflectable'
10
+ require 'rpush/daemon/loggable'
11
+ require 'rpush/daemon/interruptible_sleep'
12
+ require 'rpush/daemon/delivery_error'
13
+ require 'rpush/daemon/retryable_error'
14
+ require 'rpush/daemon/too_many_requests_error'
15
+ require 'rpush/daemon/delivery'
16
+ require 'rpush/daemon/feeder'
17
+ require 'rpush/daemon/batch'
18
+ require 'rpush/daemon/app_runner'
19
+ require 'rpush/daemon/tcp_connection'
20
+ require 'rpush/daemon/dispatcher_loop'
21
+ require 'rpush/daemon/dispatcher_loop_collection'
22
+ require 'rpush/daemon/dispatcher/http'
23
+ require 'rpush/daemon/dispatcher/tcp'
24
+ require 'rpush/daemon/service_config_methods'
25
+ require 'rpush/daemon/retry_header_parser'
26
+
27
+ require 'rpush/daemon/apns/delivery'
28
+ require 'rpush/daemon/apns/disconnection_error'
29
+ require 'rpush/daemon/apns/certificate_expired_error'
30
+ require 'rpush/daemon/apns/feedback_receiver'
31
+ require 'rpush/daemon/apns'
32
+
33
+ require 'rpush/daemon/gcm/delivery'
34
+ require 'rpush/daemon/gcm'
35
+
36
+ require 'rpush/daemon/wpns/delivery'
37
+ require 'rpush/daemon/wpns'
38
+
39
+ require 'rpush/daemon/adm/delivery'
40
+ require 'rpush/daemon/adm'
41
+
42
+ module Rpush
43
+ module Daemon
44
+ class << self
45
+ attr_accessor :store
46
+ end
47
+
48
+ def self.start
49
+ setup_signal_traps if trap_signals?
50
+
51
+ initialize_store
52
+ return unless store
53
+
54
+ if daemonize?
55
+ daemonize
56
+ store.after_daemonize
57
+ end
58
+
59
+ write_pid_file
60
+ AppRunner.sync
61
+ Feeder.start
62
+ end
63
+
64
+ def self.shutdown(quiet = false)
65
+ puts "\nShutting down..." unless quiet
66
+ Feeder.stop
67
+ AppRunner.stop
68
+ delete_pid_file
69
+ end
70
+
71
+ def self.initialize_store
72
+ return if store
73
+ begin
74
+ name = Rpush.config.store.to_s
75
+ require "rpush/daemon/store/#{name}"
76
+ self.store = Rpush::Daemon::Store.const_get(name.camelcase).new
77
+ rescue StandardError, LoadError => e
78
+ Rpush.logger.error("Failed to load '#{Rpush.config.store}' storage backend.")
79
+ Rpush.logger.error(e)
80
+ end
81
+ end
82
+
83
+ protected
84
+
85
+ def self.daemonize?
86
+ !(Rpush.config.foreground || Rpush.config.embedded || Rpush.jruby?)
87
+ end
88
+
89
+ def self.trap_signals?
90
+ !Rpush.config.embedded
91
+ end
92
+
93
+ def self.setup_signal_traps
94
+ @shutting_down = false
95
+
96
+ Signal.trap('SIGHUP') { AppRunner.sync }
97
+ Signal.trap('SIGUSR2') { AppRunner.debug }
98
+
99
+ ['SIGINT', 'SIGTERM'].each do |signal|
100
+ Signal.trap(signal) { handle_shutdown_signal }
101
+ end
102
+ end
103
+
104
+ def self.handle_shutdown_signal
105
+ exit 1 if @shutting_down
106
+ @shutting_down = true
107
+ shutdown
108
+ end
109
+
110
+ def self.write_pid_file
111
+ if !Rpush.config.pid_file.blank?
112
+ begin
113
+ File.open(Rpush.config.pid_file, 'w') { |f| f.puts Process.pid }
114
+ rescue SystemCallError => e
115
+ Rpush.logger.error("Failed to write PID to '#{Rpush.config.pid_file}': #{e.inspect}")
116
+ end
117
+ end
118
+ end
119
+
120
+ def self.delete_pid_file
121
+ pid_file = Rpush.config.pid_file
122
+ File.delete(pid_file) if !pid_file.blank? && File.exists?(pid_file)
123
+ end
124
+
125
+ # :nocov:
126
+ def self.daemonize
127
+ if RUBY_VERSION < "1.9"
128
+ exit if fork
129
+ Process.setsid
130
+ exit if fork
131
+ Dir.chdir "/"
132
+ STDIN.reopen "/dev/null"
133
+ STDOUT.reopen "/dev/null", "a"
134
+ STDERR.reopen "/dev/null", "a"
135
+ else
136
+ Process.daemon
137
+ end
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,23 @@
1
+ module Rpush
2
+ module Deprecatable
3
+ def self.included(base)
4
+ base.extend ClassMethods
5
+ end
6
+
7
+ module ClassMethods
8
+ def deprecated(method_name, version, msg=nil)
9
+ instance_eval do
10
+ alias_method "#{method_name}_without_warning", method_name
11
+ end
12
+ warning = "#{method_name} is deprecated and will be removed from Rpush #{version}."
13
+ warning << " #{msg}" if msg
14
+ class_eval(<<-RUBY, __FILE__, __LINE__)
15
+ def #{method_name}(*args, &blk)
16
+ Rpush::Deprecation.warn(#{warning.inspect})
17
+ #{method_name}_without_warning(*args, &blk)
18
+ end
19
+ RUBY
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,23 @@
1
+ module Rpush
2
+ class Deprecation
3
+ def self.muted
4
+ begin
5
+ orig_val = Thread.current[:rpush_mute_deprecations]
6
+ Thread.current[:rpush_mute_deprecations] = true
7
+ yield
8
+ ensure
9
+ Thread.current[:rpush_mute_deprecations] = orig_val
10
+ end
11
+ end
12
+
13
+ def self.muted?
14
+ Thread.current[:rpush_mute_deprecations] == true
15
+ end
16
+
17
+ def self.warn(msg)
18
+ unless Rpush::Deprecation.muted?
19
+ STDERR.puts "DEPRECATION WARNING: #{msg}"
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,28 @@
1
+ module Rpush
2
+ def self.embed(options = {})
3
+ Rpush.require_for_daemon
4
+
5
+ config = Rpush::ConfigurationWithoutDefaults.new
6
+ options.each { |k, v| config.send("#{k}=", v) }
7
+ config.embedded = true
8
+ Rpush.config.update(config)
9
+ Rpush::Daemon.start
10
+
11
+ Kernel.at_exit { shutdown }
12
+ end
13
+
14
+ def self.shutdown
15
+ return unless Rpush.config.embedded
16
+ Rpush::Daemon.shutdown
17
+ end
18
+
19
+ def self.sync
20
+ return unless Rpush.config.embedded
21
+ Rpush::Daemon::AppRunner.sync
22
+ end
23
+
24
+ def self.debug
25
+ return unless Rpush.config.embedded
26
+ Rpush::Daemon::AppRunner.debug
27
+ end
28
+ end
@@ -0,0 +1,11 @@
1
+ module Rpush
2
+ module Gcm
3
+ class App < Rpush::App
4
+ validates :auth_key, :presence => true
5
+
6
+ def service_name
7
+ 'gcm'
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ module Rpush
2
+ module Gcm
3
+ class ExpiryCollapseKeyMutualInclusionValidator < ActiveModel::Validator
4
+ def validate(record)
5
+ if record.collapse_key && !record.expiry
6
+ record.errors[:expiry] << "must be set when using a collapse_key"
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,30 @@
1
+ module Rpush
2
+ module Gcm
3
+ class Notification < Rpush::Notification
4
+ validates :registration_ids, :presence => true
5
+
6
+ validates_with Rpush::PayloadDataSizeValidator, limit: 4096
7
+ validates_with Rpush::RegistrationIdsCountValidator, limit: 1000
8
+
9
+ validates_with Rpush::Gcm::ExpiryCollapseKeyMutualInclusionValidator
10
+
11
+ def as_json
12
+ json = {
13
+ 'registration_ids' => registration_ids,
14
+ 'delay_while_idle' => delay_while_idle,
15
+ 'data' => data
16
+ }
17
+
18
+ if collapse_key
19
+ json['collapse_key'] = collapse_key
20
+ end
21
+
22
+ if expiry
23
+ json['time_to_live'] = expiry
24
+ end
25
+
26
+ json
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,63 @@
1
+ module Rpush
2
+ class Logger
3
+ def initialize(options)
4
+ @options = options
5
+
6
+ begin
7
+ log_dir = File.join(Rails.root, 'log')
8
+ FileUtils.mkdir_p(log_dir)
9
+ log = File.open(File.join(log_dir, 'rpush.log'), 'a')
10
+ log.sync = true
11
+ setup_logger(log)
12
+ rescue Errno::ENOENT, Errno::EPERM => e
13
+ @logger = nil
14
+ error(e)
15
+ error('Logging disabled.')
16
+ end
17
+ end
18
+
19
+ def info(msg)
20
+ log(:info, msg)
21
+ end
22
+
23
+ def error(msg)
24
+ log(:error, msg, 'ERROR', STDERR)
25
+ end
26
+
27
+ def warn(msg)
28
+ log(:warn, msg, 'WARNING', STDERR)
29
+ end
30
+
31
+ private
32
+
33
+ def setup_logger(log)
34
+ if Rpush.config.logger
35
+ @logger = Rpush.config.logger
36
+ elsif ActiveSupport.const_defined?('BufferedLogger')
37
+ @logger = ActiveSupport::BufferedLogger.new(log, Rails.logger.level)
38
+ @logger.auto_flushing = Rails.logger.respond_to?(:auto_flushing) ? Rails.logger.auto_flushing : true
39
+ else
40
+ @logger = ActiveSupport::Logger.new(log, Rails.logger.level)
41
+ end
42
+ end
43
+
44
+ def log(where, msg, prefix = nil, io = STDOUT)
45
+ if msg.is_a?(Exception)
46
+ formatted_backtrace = msg.backtrace.join("\n")
47
+ msg = "#{msg.class.name}, #{msg.message}\n#{formatted_backtrace}"
48
+ end
49
+
50
+ formatted_msg = "[#{Time.now.to_s(:db)}] "
51
+ formatted_msg << "[#{prefix}] " if prefix
52
+ formatted_msg << msg
53
+
54
+ if io == STDERR
55
+ io.puts formatted_msg
56
+ elsif @options[:foreground]
57
+ io.puts formatted_msg
58
+ end
59
+
60
+ @logger.send(where, formatted_msg) if @logger
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,16 @@
1
+ module Rpush
2
+ module MultiJsonHelper
3
+ def multi_json_load(string, options = {})
4
+ # Calling load on multi_json less than v1.3.0 attempts to load a file from disk.
5
+ if Gem.loaded_specs['multi_json'].version >= Gem::Version.create('1.3.0')
6
+ MultiJson.load(string, options)
7
+ else
8
+ MultiJson.decode(string, options)
9
+ end
10
+ end
11
+
12
+ def multi_json_dump(string, options = {})
13
+ MultiJson.respond_to?(:dump) ? MultiJson.dump(string, options) : MultiJson.encode(string, options)
14
+ end
15
+ end
16
+ end