Skip to content

Instantly share code, notes, and snippets.

@paukul
Created June 21, 2012 16:11
Show Gist options
  • Save paukul/2966768 to your computer and use it in GitHub Desktop.
Save paukul/2966768 to your computer and use it in GitHub Desktop.
eventmachine based apns push service implementation
require 'eventmachine'
module PushService::Gateways::Apns
class EMGateway
include PushService::Logging
class NotConnected < PushService::Error; end
def initialize
@error_callback = nil
end
def push(*notifications)
connect unless @connected
notifications = notifications.flatten
logger.debug "Sending notifications #{notifications.map(&:id).inspect}"
apn_messages = notifications.map { |notification| notification.to_enhanced_binary_message }.compact
@connection.send_data apn_messages
end
def disconnect
@connection.close_connection_after_writing
ensure
@connection = nil
@connected = false
end
def on_error(&blk)
@error_callback = blk
end
def error(response)
@error_callback.call(response) if @error_callback
end
private
def connect
logger.debug("Gateway connection not yet established. connecting...")
@connection = EM.connect PushService.host, 2195, GatewayHandler, self
logger.debug("Gateway connected")
@connected = true
end
end
BadResponse = Struct.new(:error, :command, :status, :identifier)
class GatewayHandler < EM::Connection
include PushService::Logging
ERROR_CODES = [:no_error, :processing_error, :missing_device_token,
:missing_topic, :missing_payload, :invalid_token_size,
:invalid_topic_size, :invalid_payload_size, :invalid_token]
def initialize(gateway)
@gateway = gateway
end
def post_init
logger.info("establishing ssh connection with certificate #{PushService.certificate}")
start_tls(:cert_chain_file => PushService.certificate, :private_key_file => PushService.certificate, :verify_peer => false)
end
def receive_data(data)
# TODO: write a buffer and extract responses on unbind
command, status, identifier = data.unpack("ccN")
logger.debug "Received response:"
# logger.debug "Command : #{command}"
# logger.debug "Status : #{status}"
# logger.debug "Identifier : #{identifier}"
# logger.debug "Error : #{error_for(status)}"
response = BadResponse.new(error_for(status), command, status, identifier)
@gateway.error(response)
end
def unbind
logger.info "Terminated connection"
@gateway.disconnect
end
def error_for(code)
ERROR_CODES[code] || "Unknown errno #{code}"
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment