apn_sender 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +39 -2
- data/VERSION +1 -1
- data/apn_sender.gemspec +4 -2
- data/lib/apn/connection/base.rb +97 -0
- data/lib/apn/feedback.rb +92 -0
- data/lib/apn/notification.rb +21 -9
- data/lib/apn/notification_job.rb +5 -4
- data/lib/apn/sender.rb +28 -85
- data/lib/apn.rb +3 -1
- metadata +4 -2
data/README.rdoc
CHANGED
@@ -9,7 +9,7 @@ The apn_sender gem includes a background daemon which processes background messa
|
|
9
9
|
|
10
10
|
== Usage
|
11
11
|
|
12
|
-
=== Queueing Messages From Your Application
|
12
|
+
=== 1. Queueing Messages From Your Application
|
13
13
|
|
14
14
|
To queue a message for sending through Apple's Push Notification service from your Rails application:
|
15
15
|
|
@@ -22,7 +22,7 @@ where +token+ is the unique identifier of the iPhone to receive the notification
|
|
22
22
|
# :sound #=> The sound file to play on receipt, or true to play the default sound installed with your app
|
23
23
|
# :custom #=> Hash of application-specific custom data to send along with the notification
|
24
24
|
|
25
|
-
===
|
25
|
+
=== 2. Sending Queued Messages
|
26
26
|
|
27
27
|
Put your <code>apn_development.pem</code> and <code>apn_production.pem</code> certificates from Apple in your <code>RAILS_ROOT/config/certs</code> directory.
|
28
28
|
|
@@ -40,6 +40,43 @@ For production, you're probably better off running a dedicated daemon and settin
|
|
40
40
|
|
41
41
|
Note the --environment must be explicitly set (separately from your <code>RAILS_ENV</code>) to production in order to send messages via the production APN servers. Any other environment sends messages through Apple's sandbox servers at <code>gateway.sandbox.push.apple.com</code>.
|
42
42
|
|
43
|
+
=== 3. Checking Apple's Feedback Service
|
44
|
+
|
45
|
+
Since push notifications are a fire-and-forget sorta deal, where you get no indication if your message was received (or if the specified recipient even exists), Apple needed to come up with some other way to ensure their network isn't clogged with thousands of bogus messages (e.g. from developers sending messages to phones where their application <em>used</em> to be installed, but where the user has since removed it). Hence, the Feedback Service.
|
46
|
+
|
47
|
+
It's actually really simple - you connect to them periodically and they give you a big dump of tokens you shouldn't send to anymore. The gem wraps this up nicely -- just call:
|
48
|
+
|
49
|
+
# APN::Feedback accepts the same optional :environment and :cert_path options as APN::Sender
|
50
|
+
feedback = APN::Feedback.new()
|
51
|
+
|
52
|
+
tokens = feedback.tokens # => Array of device tokens
|
53
|
+
tokens.each do |token|
|
54
|
+
# ... custom logic here to stop you app from
|
55
|
+
# sending further notifications to this token
|
56
|
+
end
|
57
|
+
|
58
|
+
If you're interested in knowing exactly <em>when</em> Apple determined each token was expired (which can be useful in determining if the application re-registered with your service since it first appeared in the expired queue):
|
59
|
+
|
60
|
+
items = feedback.data # => Array of APN::FeedbackItem elements
|
61
|
+
items.each do |item|
|
62
|
+
item.token
|
63
|
+
item.timestamp
|
64
|
+
# ... custom logic here
|
65
|
+
end
|
66
|
+
|
67
|
+
The Feedback Service works as a big queue, and when you connect it pops off all its data and sends it over the wire. This means that connecting a second time will return an empty array. For ease of use, a call to either +tokens+ or +data+ will connect once and cache the data, so if you call either one again it'll continue to use its cached version rather than connecting to Apple a second time to retrieve an empty array.
|
68
|
+
|
69
|
+
Forcing a reconnect is as easy as calling either method with the single parameter +true+, but be sure you've already used the existing data because you'll never get it back.
|
70
|
+
|
71
|
+
|
72
|
+
==== Warning: No really, check Apple's Feedback Service occasionally
|
73
|
+
|
74
|
+
If you're sending notifications, you should definitely call one of the <code>receive</code> methods periodically, as Apple's policies require it and they apparently monitors providers for compliance. I'd definitely recommend throwing together a quick rake task to take care of this for you (the {whenever library}[http://github.com/javan/whenever] provides a nice wrapper around scheduling tasks to run at certain times (for systems with cron enabled)).
|
75
|
+
|
76
|
+
|
77
|
+
|
78
|
+
|
79
|
+
|
43
80
|
=== Keeping Your Workers Working
|
44
81
|
|
45
82
|
There's also an included sample <code>apn_sender.monitrc</code> file in the <code>contrib/</code> folder to help monit handle server restarts and unexpected disasters.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.4
|
data/apn_sender.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{apn_sender}
|
8
|
-
s.version = "0.0.
|
8
|
+
s.version = "0.0.4"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Kali Donovan"]
|
12
|
-
s.date = %q{2010-
|
12
|
+
s.date = %q{2010-05-02}
|
13
13
|
s.description = %q{Resque-based background worker to send Apple Push Notifications over a persistent TCP socket. Includes Resque tweaks to allow persistent sockets between jobs, helper methods for enqueueing APN notifications, and a background daemon to send them.}
|
14
14
|
s.email = %q{kali.donovan@gmail.com}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -29,6 +29,8 @@ Gem::Specification.new do |s|
|
|
29
29
|
"generators/templates/script",
|
30
30
|
"init.rb",
|
31
31
|
"lib/apn.rb",
|
32
|
+
"lib/apn/connection/base.rb",
|
33
|
+
"lib/apn/feedback.rb",
|
32
34
|
"lib/apn/notification.rb",
|
33
35
|
"lib/apn/notification_job.rb",
|
34
36
|
"lib/apn/queue_manager.rb",
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'socket'
|
2
|
+
require 'openssl'
|
3
|
+
require 'resque'
|
4
|
+
|
5
|
+
module APN
|
6
|
+
module Connection
|
7
|
+
module Base
|
8
|
+
attr_accessor :opts, :logger
|
9
|
+
|
10
|
+
def initialize(opts = {})
|
11
|
+
@opts = opts
|
12
|
+
|
13
|
+
setup_logger
|
14
|
+
apn_log(:info, "APN::Sender initializing. Establishing connections first...") if @opts[:verbose]
|
15
|
+
setup_paths
|
16
|
+
|
17
|
+
super( APN::QUEUE_NAME ) if self.class.ancestors.include?(Resque::Worker)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Lazy-connect the socket once we try to access it in some way
|
21
|
+
def socket
|
22
|
+
setup_connection unless @socket
|
23
|
+
return @socket
|
24
|
+
end
|
25
|
+
|
26
|
+
protected
|
27
|
+
|
28
|
+
def setup_logger
|
29
|
+
@logger = if defined?(Merb::Logger)
|
30
|
+
Merb.logger
|
31
|
+
elsif defined?(RAILS_DEFAULT_LOGGER)
|
32
|
+
RAILS_DEFAULT_LOGGER
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def apn_log(level, message)
|
37
|
+
return false unless self.logger
|
38
|
+
self.logger.send(level, "#{Time.now}: #{message}")
|
39
|
+
end
|
40
|
+
|
41
|
+
def apn_production?
|
42
|
+
@opts[:environment] && @opts[:environment] != '' && :production == @opts[:environment].to_sym
|
43
|
+
end
|
44
|
+
|
45
|
+
# Get a fix on the .pem certificate we'll be using for SSL
|
46
|
+
def setup_paths
|
47
|
+
# Set option defaults
|
48
|
+
@opts[:cert_path] ||= File.join(File.expand_path(RAILS_ROOT), "config", "certs") if defined?(RAILS_ROOT)
|
49
|
+
@opts[:environment] ||= RAILS_ENV if defined?(RAILS_ENV)
|
50
|
+
|
51
|
+
raise "Missing certificate path. Please specify :cert_path when initializing class." unless @opts[:cert_path]
|
52
|
+
cert_name = apn_production? ? "apn_production.pem" : "apn_development.pem"
|
53
|
+
cert_path = File.join(@opts[:cert_path], cert_name)
|
54
|
+
|
55
|
+
@apn_cert = File.exists?(cert_path) ? File.read(cert_path) : nil
|
56
|
+
raise "Missing apple push notification certificate in #{cert_path}" unless @apn_cert
|
57
|
+
end
|
58
|
+
|
59
|
+
# Open socket to Apple's servers
|
60
|
+
def setup_connection
|
61
|
+
raise "Missing apple push notification certificate" unless @apn_cert
|
62
|
+
return true if @socket && @socket_tcp
|
63
|
+
raise "Trying to open half-open connection" if @socket || @socket_tcp
|
64
|
+
|
65
|
+
ctx = OpenSSL::SSL::SSLContext.new
|
66
|
+
ctx.cert = OpenSSL::X509::Certificate.new(@apn_cert)
|
67
|
+
ctx.key = OpenSSL::PKey::RSA.new(@apn_cert)
|
68
|
+
|
69
|
+
@socket_tcp = TCPSocket.new(apn_host, apn_port)
|
70
|
+
@socket = OpenSSL::SSL::SSLSocket.new(@socket_tcp, ctx)
|
71
|
+
@socket.sync = true
|
72
|
+
@socket.connect
|
73
|
+
rescue SocketError => error
|
74
|
+
apn_log(:error, "Error with connection to #{apn_host}: #{error}")
|
75
|
+
raise "Error with connection to #{apn_host}: #{error}"
|
76
|
+
end
|
77
|
+
|
78
|
+
# Close open sockets
|
79
|
+
def teardown_connection
|
80
|
+
apn_log(:info, "Closing connections...") if @opts[:verbose]
|
81
|
+
|
82
|
+
begin
|
83
|
+
@socket.close if @socket
|
84
|
+
rescue Exception => e
|
85
|
+
apn_log(:error, "Error closing SSL Socket: #{e}")
|
86
|
+
end
|
87
|
+
|
88
|
+
begin
|
89
|
+
@socket_tcp.close if @socket_tcp
|
90
|
+
rescue Exception => e
|
91
|
+
apn_log(:error, "Error closing TCP Socket: #{e}")
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
data/lib/apn/feedback.rb
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/connection/base')
|
2
|
+
|
3
|
+
module APN
|
4
|
+
# Encapsulates data returned from the {APN Feedback Service}[http://developer.apple.com/iphone/library/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CommunicatingWIthAPS/CommunicatingWIthAPS.html#//apple_ref/doc/uid/TP40008194-CH101-SW3].
|
5
|
+
# Possesses +timestamp+ and +token+ attributes.
|
6
|
+
class FeedbackItem
|
7
|
+
attr_accessor :timestamp, :token
|
8
|
+
|
9
|
+
def initialize(time, token)
|
10
|
+
@timestamp = time
|
11
|
+
@token = token
|
12
|
+
end
|
13
|
+
|
14
|
+
# For convenience, return the token on to_s
|
15
|
+
def to_s
|
16
|
+
token
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# When supplied with the certificate path and the desired environment, connects to the {APN Feedback Service}[http://developer.apple.com/iphone/library/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CommunicatingWIthAPS/CommunicatingWIthAPS.html#//apple_ref/doc/uid/TP40008194-CH101-SW3]
|
21
|
+
# and returns any response as an array of APN::FeedbackItem elements.
|
22
|
+
#
|
23
|
+
# See README for usage and details.
|
24
|
+
class Feedback
|
25
|
+
include APN::Connection::Base
|
26
|
+
|
27
|
+
# Returns array of APN::FeedbackItem elements read from Apple. Connects to Apple once and caches the
|
28
|
+
# data, continues to returns cached data unless called with <code>data(true)</code>, which clears the
|
29
|
+
# existing feedback array. Note that once you force resetting the cache you loose all previous feedback,
|
30
|
+
# so be sure you've already processed it.
|
31
|
+
def data(force = nil)
|
32
|
+
@feedback = nil if force
|
33
|
+
@feedback ||= receive
|
34
|
+
end
|
35
|
+
|
36
|
+
# Wrapper around +data+ returning just an array of token strings.
|
37
|
+
def tokens(force = nil)
|
38
|
+
data(force).map(&:token)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Prettify to return meaningful status information when printed. Can't add these directly to connection/base, because Resque depends on decoding to_s
|
42
|
+
def inspect
|
43
|
+
"#<#{self.class.name}: #{to_s}>"
|
44
|
+
end
|
45
|
+
|
46
|
+
# Prettify to return meaningful status information when printed. Can't add these directly to connection/base, because Resque depends on decoding to_s
|
47
|
+
def to_s
|
48
|
+
"#{@socket ? 'Connected' : 'Connection not currently established'} to #{apn_host} on #{apn_port}"
|
49
|
+
end
|
50
|
+
|
51
|
+
protected
|
52
|
+
|
53
|
+
# Connects to Apple's Feedback Service and checks if there's anything there for us.
|
54
|
+
# Returns an array of APN::FeedbackItem pairs
|
55
|
+
def receive
|
56
|
+
feedback = []
|
57
|
+
|
58
|
+
# Hi Apple
|
59
|
+
setup_connection
|
60
|
+
|
61
|
+
# Unpacking code borrowed from http://github.com/jpoz/APNS/blob/master/lib/apns/core.rb
|
62
|
+
while line = socket.gets # Read lines from the socket
|
63
|
+
line.strip!
|
64
|
+
f = line.unpack('N1n1H140')
|
65
|
+
feedback << APN::FeedbackItem.new(Time.at(f[0]), f[2])
|
66
|
+
end
|
67
|
+
|
68
|
+
# Bye Apple
|
69
|
+
teardown_connection
|
70
|
+
|
71
|
+
return feedback
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
def apn_host
|
76
|
+
@apn_host ||= apn_production? ? "feedback.push.apple.com" : "feedback.sandbox.push.apple.com"
|
77
|
+
end
|
78
|
+
|
79
|
+
def apn_port
|
80
|
+
2196
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
|
88
|
+
__END__
|
89
|
+
# Testing from irb
|
90
|
+
irb -r 'lib/apn/feedback'
|
91
|
+
|
92
|
+
a=APN::Feedback.new(:cert_path => '/Users/kali/Code/insurrection/certs/', :environment => :production)
|
data/lib/apn/notification.rb
CHANGED
@@ -18,28 +18,40 @@ module APN
|
|
18
18
|
# APN::Notification.new(token, {:alert => 'Some Alert'})
|
19
19
|
#
|
20
20
|
class Notification
|
21
|
-
attr_accessor :
|
21
|
+
attr_accessor :options, :token
|
22
22
|
def initialize(token, opts)
|
23
23
|
@options = hash_as_symbols(opts.is_a?(Hash) ? opts : {:alert => opts})
|
24
|
-
@
|
25
|
-
|
26
|
-
|
27
|
-
raise "The maximum size allowed for a notification payload is 256 bytes." if @message.size.to_i > 256
|
24
|
+
@token = token
|
25
|
+
|
26
|
+
raise "The maximum size allowed for a notification payload is 256 bytes." if packaged_notification.size.to_i > 256
|
28
27
|
end
|
29
28
|
|
30
29
|
def to_s
|
31
|
-
|
30
|
+
packaged_notification
|
32
31
|
end
|
33
32
|
|
33
|
+
# Ensures at least one of <code>%w(alert badge sound)</code> is present
|
34
34
|
def valid?
|
35
35
|
return true if %w(alert badge sound).any?{|key| options.keys.include?(key.to_sym) }
|
36
36
|
false
|
37
37
|
end
|
38
38
|
|
39
39
|
protected
|
40
|
-
|
40
|
+
|
41
|
+
# Completed encoded notification, ready to send down the wire to Apple
|
42
|
+
def packaged_notification
|
43
|
+
pt = packaged_token
|
44
|
+
pm = packaged_message
|
45
|
+
[0, 0, 32, pt, 0, pm.size, pm].pack("ccca*cca*")
|
46
|
+
end
|
47
|
+
|
48
|
+
# Device token, compressed and hex-ified
|
49
|
+
def packaged_token
|
50
|
+
[@token.gsub(/[\s|<|>]/,'')].pack('H*')
|
51
|
+
end
|
52
|
+
|
41
53
|
# Convert the supplied options into the JSON needed for Apple's push notification servers
|
42
|
-
def
|
54
|
+
def packaged_message
|
43
55
|
hsh = {'aps' => {}}
|
44
56
|
hsh['aps']['alert'] = @options[:alert].to_s if @options[:alert]
|
45
57
|
hsh['aps']['badge'] = @options[:badge].to_i if @options[:badge]
|
@@ -48,7 +60,7 @@ module APN
|
|
48
60
|
hsh.merge!(@options[:custom]) if @options[:custom]
|
49
61
|
hsh.to_json
|
50
62
|
end
|
51
|
-
|
63
|
+
|
52
64
|
# Symbolize keys, using ActiveSupport if available
|
53
65
|
def hash_as_symbols(hash)
|
54
66
|
if hash.respond_to?(:symbolize_keys)
|
data/lib/apn/notification_job.rb
CHANGED
@@ -5,18 +5,19 @@ module APN
|
|
5
5
|
class NotificationJob
|
6
6
|
# Behind the scenes, this is the name of our Resque queue
|
7
7
|
@queue = APN::QUEUE_NAME
|
8
|
-
|
8
|
+
|
9
9
|
# Build a notification from arguments and send to Apple
|
10
10
|
def self.perform(token, opts)
|
11
11
|
msg = APN::Notification.new(token, opts)
|
12
12
|
raise "Invalid notification options: #{opts.inspect}" unless msg.valid?
|
13
|
+
|
13
14
|
worker.send_to_apple( msg )
|
14
15
|
end
|
15
|
-
|
16
|
-
|
16
|
+
|
17
|
+
|
17
18
|
# Only execute this job in specialized APN::Sender workers, since
|
18
19
|
# standard Resque workers don't maintain the persistent TCP connection.
|
19
20
|
extend Resque::Plugins::AccessWorkerFromJob
|
20
21
|
self.required_worker_class = 'APN::Sender'
|
21
22
|
end
|
22
|
-
end
|
23
|
+
end
|
data/lib/apn/sender.rb
CHANGED
@@ -1,7 +1,3 @@
|
|
1
|
-
require 'socket'
|
2
|
-
require 'openssl'
|
3
|
-
require 'resque'
|
4
|
-
|
5
1
|
module APN
|
6
2
|
# Subclass of Resque::Worker which initializes a single TCP socket on creation to communicate with Apple's Push Notification servers.
|
7
3
|
# Shares this socket with each child process forked off by Resque to complete a job. Socket is closed in the before_unregister_worker
|
@@ -14,94 +10,38 @@ module APN
|
|
14
10
|
# Rails context, will default to RAILS_ENV and RAILS_ROOT/config/certs. :environment will default to development.
|
15
11
|
# APN::Sender expects two files to exist in the specified <code>:cert_path</code> directory:
|
16
12
|
# <code>apn_production.pem</code> and <code>apn_development.pem</code>.
|
13
|
+
#
|
14
|
+
# If a socket error is encountered, will teardown the connection and retry again twice before admitting defeat.
|
17
15
|
class Sender < ::Resque::Worker
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
class << self
|
22
|
-
attr_accessor :logger
|
23
|
-
end
|
24
|
-
|
25
|
-
self.logger = if defined?(Merb::Logger)
|
26
|
-
Merb.logger
|
27
|
-
elsif defined?(RAILS_DEFAULT_LOGGER)
|
28
|
-
RAILS_DEFAULT_LOGGER
|
29
|
-
end
|
30
|
-
|
31
|
-
def initialize(opts = {})
|
32
|
-
@opts = opts
|
33
|
-
|
34
|
-
# Set option defaults
|
35
|
-
@opts[:cert_path] ||= File.join(File.expand_path(RAILS_ROOT), "config", "certs") if defined?(RAILS_ROOT)
|
36
|
-
@opts[:environment] ||= RAILS_ENV if defined?(RAILS_ENV)
|
37
|
-
|
38
|
-
logger.info "APN::Sender initializing. Establishing connections first..." if @opts[:verbose]
|
39
|
-
setup_paths
|
40
|
-
setup_connection
|
41
|
-
|
42
|
-
super( APN::QUEUE_NAME )
|
43
|
-
end
|
44
|
-
|
16
|
+
include APN::Connection::Base
|
17
|
+
TIMES_TO_RETRY_SOCKET_ERROR = 2
|
18
|
+
|
45
19
|
# Send a raw string over the socket to Apple's servers (presumably already formatted by APN::Notification)
|
46
|
-
def send_to_apple(
|
47
|
-
|
20
|
+
def send_to_apple( notification, attempt = 0 )
|
21
|
+
if attempt > TIMES_TO_RETRY_SOCKET_ERROR
|
22
|
+
raise "Error with connection to #{apn_host} (retried #{TIMES_TO_RETRY_SOCKET_ERROR} times): #{error}"
|
23
|
+
end
|
24
|
+
|
25
|
+
self.socket.write( notification.to_s )
|
48
26
|
rescue SocketError => error
|
49
|
-
|
50
|
-
|
27
|
+
apn_log(:error, "Error with connection to #{apn_host} (attempt #{attempt}): #{error}")
|
28
|
+
|
29
|
+
# Try reestablishing the connection
|
30
|
+
teardown_connection
|
31
|
+
setup_connection
|
32
|
+
send_to_apple(notification, attempt + 1)
|
51
33
|
end
|
52
|
-
|
34
|
+
|
53
35
|
protected
|
54
36
|
|
55
|
-
def
|
56
|
-
@
|
37
|
+
def apn_host
|
38
|
+
@apn_host ||= apn_production? ? "gateway.push.apple.com" : "gateway.sandbox.push.apple.com"
|
57
39
|
end
|
58
40
|
|
59
|
-
|
60
|
-
|
61
|
-
raise "Missing certificate path. Please specify :cert_path when initializing class." unless @opts[:cert_path]
|
62
|
-
@apn_host = apn_production? ? "gateway.push.apple.com" : "gateway.sandbox.push.apple.com"
|
63
|
-
cert_name = apn_production? ? "apn_production.pem" : "apn_development.pem"
|
64
|
-
cert_path = File.join(@opts[:cert_path], cert_name)
|
65
|
-
|
66
|
-
@apn_cert = File.exists?(cert_path) ? File.read(cert_path) : nil
|
67
|
-
raise "Missing apple push notification certificate in #{cert_path}" unless @apn_cert
|
41
|
+
def apn_port
|
42
|
+
2195
|
68
43
|
end
|
69
44
|
|
70
|
-
# Open socket to Apple's servers
|
71
|
-
def setup_connection
|
72
|
-
raise "Missing apple push notification certificate" unless @apn_cert
|
73
|
-
raise "Trying to open already-open socket" if @socket || @socket_tcp
|
74
|
-
|
75
|
-
ctx = OpenSSL::SSL::SSLContext.new
|
76
|
-
ctx.cert = OpenSSL::X509::Certificate.new(@apn_cert)
|
77
|
-
ctx.key = OpenSSL::PKey::RSA.new(@apn_cert)
|
78
|
-
|
79
|
-
@socket_tcp = TCPSocket.new(@apn_host, APN_PORT)
|
80
|
-
@socket = OpenSSL::SSL::SSLSocket.new(@socket_tcp, ctx)
|
81
|
-
@socket.sync = true
|
82
|
-
@socket.connect
|
83
|
-
rescue SocketError => error
|
84
|
-
logger.error("Error with connection to #{@apn_host}: #{error}")
|
85
|
-
raise "Error with connection to #{@apn_host}: #{error}"
|
86
|
-
end
|
87
|
-
|
88
|
-
# Close open sockets
|
89
|
-
def teardown_connection
|
90
|
-
logger.info "Closing connections..." if @opts[:verbose]
|
91
|
-
|
92
|
-
begin
|
93
|
-
@socket.close if @socket
|
94
|
-
rescue Exception => e
|
95
|
-
logger.error("Error closing SSL Socket: #{e}")
|
96
|
-
end
|
97
|
-
|
98
|
-
begin
|
99
|
-
@socket_tcp.close if @socket_tcp
|
100
|
-
rescue Exception => e
|
101
|
-
logger.error("Error closing TCP Socket: #{e}")
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
45
|
end
|
106
46
|
|
107
47
|
end
|
@@ -112,9 +52,14 @@ __END__
|
|
112
52
|
# irb -r 'lib/apple_push_notification'
|
113
53
|
|
114
54
|
## To enqueue test job
|
115
|
-
|
55
|
+
k = 'ceecdc18 ef17b2d0 745475e0 0a6cd5bf 54534184 ac2649eb 40873c81 ae76dbe8'
|
56
|
+
c = '0f58e3e2 77237b8f f8213851 c835dee0 376b7a31 9e0484f7 06fe3035 7c5dda2f'
|
57
|
+
Resque.enqueue APN::NotificationJob, k, {:alert => 'Resque Test'}
|
116
58
|
APN.notify 'ceecdc18 ef17b2d0 745475e0 0a6cd5bf 54534184 ac2649eb 40873c81 ae76dbe8', 'Resque Test'
|
117
59
|
|
60
|
+
# If you need to really force quit some screwed up workers
|
61
|
+
Resque.workers.map{|w| Resque.redis.srem(:workers, w)}
|
62
|
+
|
118
63
|
## To run worker from rake task
|
119
64
|
CERT_PATH=/Users/kali/Code/insurrection/certs/ ENVIRONMENT=production rake apn:work
|
120
65
|
|
@@ -128,6 +73,4 @@ worker.work(5)
|
|
128
73
|
## To run worker as daemon - NOT YET TESTED
|
129
74
|
APN::SenderDaemon.new(ARGV).daemonize
|
130
75
|
|
131
|
-
## TESTING - check the broken pipe errors
|
132
76
|
|
133
|
-
## TESTING - then check implementation of resque-web
|
data/lib/apn.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: apn_sender
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kali Donovan
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2010-
|
12
|
+
date: 2010-05-02 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -54,6 +54,8 @@ files:
|
|
54
54
|
- generators/templates/script
|
55
55
|
- init.rb
|
56
56
|
- lib/apn.rb
|
57
|
+
- lib/apn/connection/base.rb
|
58
|
+
- lib/apn/feedback.rb
|
57
59
|
- lib/apn/notification.rb
|
58
60
|
- lib/apn/notification_job.rb
|
59
61
|
- lib/apn/queue_manager.rb
|