rpush 1.0.0-java

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 (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