lib/suj/pusher/apn_connection.rb in suj-pusher-0.2.3 vs lib/suj/pusher/apn_connection.rb in suj-pusher-0.2.5

- old
+ new

@@ -1,14 +1,17 @@ require "eventmachine" require "iobuffer" - require "base64" +require 'suj/pusher/monkey/vash' + module Suj module Pusher class APNConnection < EM::Connection include Suj::Pusher::Logger + @@last_id = 0 + ERRORS = { 0 => "No errors encountered", 1 => "Processing error", 2 => "Missing device token", 3 => "Missing topic", @@ -24,47 +27,72 @@ def initialize(pool, options = {}) super @disconnected = true @pool = pool @options = options + @processing_ids = Vash.new @cert_key = Digest::SHA1.hexdigest(@options[:cert]) @cert_file = File.join(Suj::Pusher.config.certs_path, @cert_key) @buffer = IO::Buffer.new + self.comm_inactivity_timeout = 60 # Close after 60 sec of inactivity + File.open(@cert_file, "w") do |f| f.write @options[:cert] end + @ssl_options = { private_key_file: @cert_file, cert_chain_file: @cert_file, verify_peer: false } end + def options + @options + end + def disconnected? @disconnected end def deliver(data) begin @notifications = [] data[:apn_ids].each do |apn_id| - @notifications << Suj::Pusher::ApnNotification.new(data.merge({token: apn_id})) + if ! @pool.valid_token?(@cert_key, apn_id) + warn "Skipping invalid #{apn_id} APN id" + next + end + notification = Suj::Pusher::ApnNotification.new(data.merge({token: apn_id, id: @@last_id})) + @processing_ids[@@last_id.to_s] = apn_id + @@last_id += 1 + @notifications << notification end - if ! disconnected? - info "APN delivering data" - send_data(@notifications.join) - info "APN push notification sent" - @notifications = nil - info "APN delivered data" - else - info "APN connection unavailable" + + @processing_ids.cleanup! + info "Processing list size #{@processing_ids.size}" + + if @notifications.empty? + warn "No valid tokens, skip message" + return end + + if disconnected? + warn "Connection not ready yet, queue notifications for later" + return + end + + info "APN delivering data" + send_data(@notifications.join) + @notifications = nil + info "APN delivered data" rescue Suj::Pusher::ApnNotification::PayloadTooLarge => e error "APN notification payload too large." debug @notifications.join.inspect rescue => ex error "APN notification error : #{ex}" + error ex.backtrace end end def post_init info "APN Connection init " @@ -83,25 +111,29 @@ while @buffer.size >= 6 res = @buffer.read(6) cmd, status, id = data.unpack("CCN") if cmd != 8 error "APN push response command differs from 8" + elsif status == 8 + token = @processing_ids[id.to_s] + @pool.invalidate_token(@cert_key, token) + warn "APN invalid (id: #{id}) token #{token}" elsif status != 0 error "APN push error received: #{ERRORS[status]} for id #{id}" end end end def connection_completed info "APN Connection established..." @disconnected = false - if ! @notifications.nil? - info "EST - APN delivering data" - send_data(@notifications.join) - info "APN push notification sent" - @notifications = nil - info "EST - APN delivered data" - end + + return if @notifications.nil? || @notifications.empty? + + info "EST - APN delivering data" + send_data(@notifications.join) + @notifications = nil + info "EST - APN delivered data" end def unbind info "APN Connection closed..." @disconnected = true