Skip to content

Instantly share code, notes, and snippets.

@farcaller
Created May 21, 2012 09:34
Show Gist options
  • Save farcaller/2761499 to your computer and use it in GitHub Desktop.
Save farcaller/2761499 to your computer and use it in GitHub Desktop.
require 'socket'
require 'openssl'
module Apns
class ApnsConnection
def initialize(host, port, cert, key)
@host, @port, @cert, @key = host, port, cert, key
end
def connect
ctx = OpenSSL::SSL::SSLContext.new()
ctx.cert = OpenSSL::X509::Certificate.new(File::read(@cert))
ctx.key = OpenSSL::PKey::RSA.new(File::read(@key))
begin
s = TCPSocket.new(@host, @port)
ssl = OpenSSL::SSL::SSLSocket.new(s, ctx)
ssl.connect # start SSL session
ssl.sync_close = true # close underlying socket on SSLSocket#close
@ssl = ssl
true
rescue Errno::ETIMEDOUT
nil
end
end
def push(notification)
delay = 2
begin
@ssl.write(notification.to_s)
rescue OpenSSL::SSL::SSLError, Errno::EPIPE
sleep(delay)
raise Timeout::Error unless connect
delay *= 2 and retry if delay < 60
raise Timeout::Error
end
end
def close_connection
@ssl.close
@ssl = nil
end
end
end
require 'json'
class Hash
MAX_PAYLOAD_LEN = 256
# Converts hash into JSON String.
# When payload is too long but can be chopped, tries to cut self.[:aps][:alert].
# If payload still don't fit Apple's restrictions, returns nil
#
# @return [String, nil] the object converted into JSON or nil.
def to_payload
# Payload too long
if (to_json.length > MAX_PAYLOAD_LEN)
alert = self[:aps][:alert]
self[:aps][:alert] = ''
# can be chopped?
if (to_json.length > MAX_PAYLOAD_LEN)
return nil
else # inefficient way, but payload may be full of unicode-escaped chars, so...
self[:aps][:alert] = alert
while (self.to_json.length > MAX_PAYLOAD_LEN)
self[:aps][:alert].chop!
end
end
end
to_json
end
# Invokes {Hash#to_payload} and returns it's length
# @return [Fixnum, nil] length of object converted into JSON or nil.
def payload_length
p = to_payload
p ? p.length : nil
end
end
module Apns
class Notification
def Notification.create(token, payload)
Notification.new token.kind_of?(String) ? token.delete(' ') : token.to_s(16) , payload.kind_of?(Hash) ? payload.to_payload : payload
end
def Notification.parse(bitstring)
command, tokenlen, token, payloadlen, payload = bitstring.unpack("CnH64na*")
Notification.new(token, payload)
end
def initialize(token, payload)
@token, @payload = token, payload
end
def to_s
[0, 32, @token, @payload.length, @payload ].pack("CnH*na*")
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment