EzDevInfo.com

PyAPNs

Python library for interacting with the Apple Push Notification service (APNs) apns 2.0.1 : Python Package Index a python library for interacting with the apple push notification service

PyAPNs sending push notification to more than one device token not working

I am using PyAPNs for sending iOS Push Notification. I also merged the fixes for following known issues

https://github.com/djacobs/PyAPNs/issues/13

Now, the code is working fine If I send notification to an individual device. But I have a list for device tokens and I have to send notification to all of them one by one. For this purpose I am simple looping over the single notification call like this:

def send_notifications(self, tokens, payload):
    for token in tokens:
        try :
            logging.info("Sending Notification to Token: %s" % (token))
            self.send_notification(token, payload)                
        except Exception, e:
            self._disconnect()
            logging.info("Exception: %s" % (str(e)))
            logging.info("Token: %s" % (token))

But the problem is that above code is not working. The device token which was working fine for individual push, is not working using above code. For example device token 45183e79de216ea05e3d6e83083476ebeb64caf733188bb77b0b1d268526c815 is working fine individually but failing in case of bulk send. For reference I am putting the apns file and partial server logs:

apns.py

# PyAPNs was developed by Simon Whitaker <simon@goosoftware.co.uk>
# Source available at https://github.com/simonwhitaker/PyAPNs
#
# PyAPNs is distributed under the terms of the MIT license.
#
# Copyright (c) 2011 Goo Software Ltd
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
# of the Software, and to permit persons to whom the Software is furnished to do
# so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

from binascii import a2b_hex, b2a_hex
from datetime import datetime, timedelta
from time import mktime
from socket import socket, AF_INET, SOCK_STREAM, timeout
from struct import pack, unpack

import select

try:
    from ssl import wrap_socket
    from ssl import SSLError, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE
except ImportError:
    from socket import ssl as wrap_socket

try:
    import json
except ImportError:
    import simplejson as json

from apnserrors import *

import logging
import StringIO

MAX_PAYLOAD_LENGTH = 256
TIMEOUT = 60
ERROR_RESPONSE_LENGTH = 6

class APNs(object):
    """A class representing an Apple Push Notification service connection"""

    def __init__(self, use_sandbox=False, cert_file=None, key_file=None, enhanced=True):
        """
        Set use_sandbox to True to use the sandbox (test) APNs servers.
        Default is False.
        """
        super(APNs, self).__init__()
        self.use_sandbox = use_sandbox
        self.cert_file = cert_file
        self.key_file = key_file
        self.enhanced = enhanced
        self._feedback_connection = None
        self._gateway_connection = None

    @staticmethod
    def unpacked_uchar_big_endian(byte):
        """
        Returns an unsigned char from a packed big-endian (network) byte
        """
        return unpack('>B', byte)[0]

    @staticmethod
    def packed_ushort_big_endian(num):
        """
        Returns an unsigned short in packed big-endian (network) form
        """
        return pack('>H', num)

    @staticmethod
    def unpacked_ushort_big_endian(bytes):
        """
        Returns an unsigned short from a packed big-endian (network) byte
        array
        """
        return unpack('>H', bytes)[0]

    @staticmethod
    def packed_uint_big_endian(num):
        """
        Returns an unsigned int in packed big-endian (network) form
        """
        return pack('>I', num)

    @staticmethod
    def unpacked_uint_big_endian(bytes):
        """
        Returns an unsigned int from a packed big-endian (network) byte array
        """
        return unpack('>I', bytes)[0]

    @property
    def feedback_server(self):
        if not self._feedback_connection:
            self._feedback_connection = FeedbackConnection(
                use_sandbox = self.use_sandbox,
                cert_file = self.cert_file,
                key_file = self.key_file
            )
        return self._feedback_connection

    @property
    def gateway_server(self):
        if not self._gateway_connection:
            self._gateway_connection = GatewayConnection(
                use_sandbox = self.use_sandbox,
                cert_file = self.cert_file,
                key_file = self.key_file,
                enhanced = self.enhanced
            )
        return self._gateway_connection


class APNsConnection(object):
    """
    A generic connection class for communicating with the APNs
    """
    def __init__(self, cert_file=None, key_file=None, enhanced=True):
        super(APNsConnection, self).__init__()
        self.cert_file = cert_file
        self.key_file = key_file
        self.enhanced = enhanced
        self._socket = None
        self._ssl = None

    def __del__(self):
        self._disconnect();

    def _connect(self):
        # Establish an SSL connection
        self._socket = socket(AF_INET, SOCK_STREAM)
        self._socket.connect((self.server, self.port))

        if self.enhanced:
            self._ssl = wrap_socket(self._socket, StringIO.StringIO(self.key_file), StringIO.StringIO(self.cert_file),
                                    do_handshake_on_connect=False)
            self._ssl.setblocking(0)
            while True:
                try:
                    self._ssl.do_handshake()
                    break
                except SSLError, err:
                    if SSL_ERROR_WANT_READ == err.args[0]:
                        select.select([self._ssl], [], [])
                    elif SSL_ERROR_WANT_WRITE == err.args[0]:
                        select.select([], [self._ssl], [])
                    else:
                        raise
        else:
            self._ssl = wrap_socket(self._socket, StringIO.StringIO(self.key_file), StringIO.StringIO(self.cert_file))

    def _disconnect(self):
        if self._socket:
            self._socket.close()
            self._ssl = None

    def _connection(self):
        if not self._ssl:
            self._connect()
        return self._ssl

    def read(self, n=None):
        return self._connection().recv(n)

    def recvall(self, n):
        data = ""
        while True:
            more = self._connection().recv(n - len(data))
            data += more
            if len(data) >= n:
                break
            rlist, _, _ = select.select([self._connection()], [], [], TIMEOUT)
            if not rlist:
                raise timeout

        return data

    def write(self, string):
        if self.enhanced: # nonblocking socket
            rlist, _, _ = select.select([self._connection()], [], [], 0)

            if rlist: # there's error response from APNs
                buff = self.recvall(ERROR_RESPONSE_LENGTH)
                if len(buff) != ERROR_RESPONSE_LENGTH:
                    return None

                command = APNs.unpacked_uchar_big_endian(buff[0])

                if 8 != command:
                    self._disconnect()
                    raise UnknownError(0)

                status = APNs.unpacked_uchar_big_endian(buff[1])
                identifier = APNs.unpacked_uint_big_endian(buff[2:6])

                self._disconnect()

                raise { 1: ProcessingError,
                        2: MissingDeviceTokenError,
                        3: MissingTopicError,
                        4: MissingPayloadError,
                        5: InvalidTokenSizeError,
                        6: InvalidTopicSizeError,
                        7: InvalidPayloadSizeError,
                        8: InvalidTokenError }.get(status, UnknownError)(identifier)

            _, wlist, _ = select.select([], [self._connection()], [], TIMEOUT)
            if wlist:
                return self._connection().sendall(string)
            else:
                self._disconnect()
                raise timeout

        else: # not-enhanced format using blocking socket
            return self._connection().sendall(string)

class PayloadAlert(object):
    def __init__(self, body, action_loc_key=None, loc_key=None,
                 loc_args=None, launch_image=None):
        super(PayloadAlert, self).__init__()
        self.body = body
        self.action_loc_key = action_loc_key
        self.loc_key = loc_key
        self.loc_args = loc_args
        self.launch_image = launch_image

    def dict(self):
        d = { 'body': self.body }
        if self.action_loc_key:
            d['action-loc-key'] = self.action_loc_key
        if self.loc_key:
            d['loc-key'] = self.loc_key
        if self.loc_args:
            d['loc-args'] = self.loc_args
        if self.launch_image:
            d['launch-image'] = self.launch_image
        return d

class Payload(object):
    """A class representing an APNs message payload"""
    def __init__(self, alert=None, badge=None, sound=None, custom={}):
        super(Payload, self).__init__()
        self.alert = alert
        self.badge = badge
        self.sound = sound
        self.custom = custom
        self._check_size()

    def dict(self):
        """Returns the payload as a regular Python dictionary"""
        d = {}
        if self.alert:
            # Alert can be either a string or a PayloadAlert
            # object
            if isinstance(self.alert, PayloadAlert):
                d['alert'] = self.alert.dict()
            else:
                d['alert'] = self.alert
        if self.sound:
            d['sound'] = self.sound
        if self.badge is not None:
            d['badge'] = int(self.badge)

        d = { 'aps': d }
        d.update(self.custom)
        return d

    def json(self):
        return json.dumps(self.dict(), separators=(',',':'), ensure_ascii=False).encode('utf-8')

    def _check_size(self):
        if len(self.json()) > MAX_PAYLOAD_LENGTH:
            raise PayloadTooLargeError()

    def __repr__(self):
        attrs = ("alert", "badge", "sound", "custom")
        args = ", ".join(["%s=%r" % (n, getattr(self, n)) for n in attrs])
        return "%s(%s)" % (self.__class__.__name__, args)


class FeedbackConnection(APNsConnection):
    """
    A class representing a connection to the APNs Feedback server
    """
    def __init__(self, use_sandbox=False, **kwargs):
        super(FeedbackConnection, self).__init__(**kwargs)
        self.server = (
            'feedback.push.apple.com',
            'feedback.sandbox.push.apple.com')[use_sandbox]
        self.port = 2196

    def _chunks(self):
        BUF_SIZE = 4096
        while 1:
            data = self.read(BUF_SIZE)
            yield data
            if not data:
                break

    def items(self):
        """
        A generator that yields (token_hex, fail_time) pairs retrieved from
        the APNs feedback server
        """
        buff = ''
        for chunk in self._chunks():
            buff += chunk

            # Quit if there's no more data to read
            if not buff:
                break

            # Sanity check: after a socket read we should always have at least
            # 6 bytes in the buffer
            if len(buff) < 6:
                break

            while len(buff) > 6:
                token_length = APNs.unpacked_ushort_big_endian(buff[4:6])
                bytes_to_read = 6 + token_length
                if len(buff) >= bytes_to_read:
                    fail_time_unix = APNs.unpacked_uint_big_endian(buff[0:4])
                    fail_time = datetime.utcfromtimestamp(fail_time_unix)
                    token = b2a_hex(buff[6:bytes_to_read])

                    yield (token, fail_time)

                    # Remove data for current token from buffer
                    buff = buff[bytes_to_read:]
                else:
                    # break out of inner while loop - i.e. go and fetch
                    # some more data and append to buffer
                    break

class GatewayConnection(APNsConnection):
    """
    A class that represents a connection to the APNs gateway server
    """
    def __init__(self, use_sandbox=False, **kwargs):
        super(GatewayConnection, self).__init__(**kwargs)
        self.server = (
            'gateway.push.apple.com',
            'gateway.sandbox.push.apple.com')[use_sandbox]
        self.port = 2195

    def _get_notification(self, token_hex, payload):
        """
        Takes a token as a hex string and a payload as a Python dict and sends
        the notification
        """
        token_bin = a2b_hex(token_hex)
        token_length_bin = APNs.packed_ushort_big_endian(len(token_bin))
        payload_json = payload.json()
        payload_length_bin = APNs.packed_ushort_big_endian(len(payload_json))

        notification = ('\0' + token_length_bin + token_bin
                        + payload_length_bin + payload_json)

        return notification

    def _get_enhanced_notification(self, token_hex, payload, identifier, expiry):
        """
        Takes a token as a hex string and a payload as a Python dict and sends
        the notification in the enhanced format
        """
        token_bin = a2b_hex(token_hex)
        token_length_bin = APNs.packed_ushort_big_endian(len(token_bin))
        payload_json = payload.json()
        payload_length_bin = APNs.packed_ushort_big_endian(len(payload_json))
        identifier_bin = APNs.packed_uint_big_endian(identifier)
        expiry_bin = APNs.packed_uint_big_endian(int(mktime(expiry.timetuple())))

        notification = ('\1' + identifier_bin + expiry_bin + token_length_bin + token_bin
                        + payload_length_bin + payload_json)

        return notification

    def send_notification(self, token_hex, payload, identifier=None, expiry=None):
        if self.enhanced:
            if not expiry: # by default, undelivered notification expires after 30 seconds
                expiry = datetime.utcnow() + timedelta(30)
            if not identifier:
                identifier = 0

            logging.info("self.write(self._get_enhanced_notification())")    
            self.write(self._get_enhanced_notification(token_hex, payload, identifier,
                                                       expiry))
        else:
            logging.info("self.write(self._get_notification(token_hex, payload))")
            self.write(self._get_notification(token_hex, payload))

    def send_notifications(self, tokens, payload):
        for token in tokens:
            try :
                logging.info("Sending Notification to Token: %s" % (token))
                self.send_notification(token, payload)                
            except Exception, e:
                self._disconnect()
                logging.info("Exception: %s" % (str(e)))
                logging.info("Token: %s" % (token))

Server Logs:

    Sending Notification to Token: 99f65209a76ed41ce50c73198d72048f94085dd2a2dde0245110dccccda86fd0
    I 2014-05-20 05:18:24.029
    self.write(self._get_enhanced_notification())
    I 2014-05-20 05:18:24.437
    Sending Notification to Token: 2230c2421e3b83cd6b16a69c6ba528230b11d29183b0bfb73b159816237b17ce
    I 2014-05-20 05:18:24.437
    self.write(self._get_enhanced_notification())
    I 2014-05-20 05:18:24.442
.
.
.
    Sending Notification to Token: 6bd80feb5158a8f92537955c93acd1661242c007dcffebe55e77bb38cafef0ba
    I 2014-05-20 05:18:24.986
    self.write(self._get_enhanced_notification())
    I 2014-05-20 05:18:24.991
    Sending Notification to Token: 2230c2421e3b83cd6b16a69c6ba528230b11d29183b0bfb73b159816237b17ce
    I 2014-05-20 05:18:24.991
    self.write(self._get_enhanced_notification())
    I 2014-05-20 05:18:24.996
    Sending Notification to Token: 6bd80feb5158a8f92537955c93acd1661242c007dcffebe55e77bb38cafef0ba
    I 2014-05-20 05:18:24.996
    self.write(self._get_enhanced_notification())
    I 2014-05-20 05:18:25.004
    Sending Notification to Token: 1bacfcb6b80868493b236ec6131bed11918c935752734701b89b060045e6b006
    I 2014-05-20 05:18:25.004
    self.write(self._get_enhanced_notification())
    I 2014-05-20 05:18:25.021
    Sending Notification to Token: 35bd8dda849e30a85b12b2a0e274b9507db7c7f365aa5a27f3fbda316052246e
    I 2014-05-20 05:18:25.021
    self.write(self._get_enhanced_notification())
    I 2014-05-20 05:18:25.054
    Sending Notification to Token: 6bd80feb5158a8f92537955c93acd1661242c007dcffebe55e77bb38cafef0ba
    I 2014-05-20 05:18:25.054
    self.write(self._get_enhanced_notification())
    I 2014-05-20 05:18:25.059
    Sending Notification to Token: 6bd80feb5158a8f92537955c93acd1661242c007dcffebe55e77bb38cafef0ba
    I 2014-05-20 05:18:25.059
    self.write(self._get_enhanced_notification())
    I 2014-05-20 05:18:25.064
    Sending Notification to Token: 1bacfcb6b80868493b236ec6131bed11918c935752734701b89b060045e6b006
    I 2014-05-20 05:18:25.064
    self.write(self._get_enhanced_notification())
    I 2014-05-20 05:18:25.068
    Sending Notification to Token: 6bd80feb5158a8f92537955c93acd1661242c007dcffebe55e77bb38cafef0ba
    I 2014-05-20 05:18:25.069
    self.write(self._get_enhanced_notification())
    I 2014-05-20 05:18:25.073
    Sending Notification to Token: d25a34a1fd031abf3fbfb5916af415206048fb6343586b91b96d0506eb28cb54
    I 2014-05-20 05:18:25.073
    self.write(self._get_enhanced_notification())
    I 2014-05-20 05:18:25.078

.
.
.
    Sending Notification to Token: 45183e79de216ea05e3d6e83083476ebeb64caf733188bb77b0b1d268526c815
    I 2014-05-20 05:18:30.145
    self.write(self._get_enhanced_notification())
    I 2014-05-20 05:18:30.152
    Sending Notification to Token: b57b2d96a4b4db552137bcea4fd58f3ce53393fbe7c828b617306df2922dbfd3
    I 2014-05-20 05:18:30.152
    self.write(self._get_enhanced_notification())
    I 2014-05-20 05:18:30.159
    Sending Notification to Token: 82acbf3dc5da893d2f4d551df10c129c8c192efe335cc608d291dc922e947615
    I 2014-05-20 05:18:30.159
    self.write(self._get_enhanced_notification())
    I 2014-05-20 05:18:30.166

    feedback token_hex: 0cf58d47f435f170473b63e1852b637c11935b6e38d41321fe98911eaf898301
    I 2014-05-20 05:18:31.754
    feedback token_hex: 0d344046d62f808c30bc5670cbb7dc478cca0a9798830d22f8f6ed27c76923c6
    I 2014-05-20 05:18:31.754
    feedback token_hex: 2230c2421e3b83cd6b16a69c6ba528230b11d29183b0bfb73b159816237b17ce
    I 2014-05-20 05:18:31.754
    feedback token_hex: 349c54d18bb1ee014dc84f7b7b60c4a2eef1b9d3cf51c12daab93261d5e09e7c
    I 2014-05-20 05:18:31.754
    feedback token_hex: 3980924c6cd4e752f2a02b8d28f7ce11d7a3eba5f41628166733cda4e621bfcf
    I 2014-05-20 05:18:31.755
    feedback token_hex: 6bd80feb5158a8f92537955c93acd1661242c007dcffebe55e77bb38cafef0ba
    I 2014-05-20 05:18:31.755
    feedback token_hex: b96e27adab644f0a18e8f4dfe19786aab82b69e1ef46c580b887e6779964c55f
    I 2014-05-20 05:18:31.755
    feedback token_hex: e5ee1848342d2e4789cfa07baae3ac754785d78ccb50dc5b5f10044053843115
    I 2014-05-20 05:18:31.755
    feedback token_hex: f339e53e44efa03996dffc24b5c9419609018fd8dd5d1953230a4bd8c5cabc78
    I 2014-05-20 05:18:31.760
    feedback fail_count: 9

Source: (StackOverflow)

PyAPNs and the need to Sleep between Sends

I am using PyAPNs to send notifications to iOS devices. I am often sending groups of notifications at once. If any of the tokens is bad for any reason, the process will stop. As a result I am using the enhanced setup and the following method:

apns.gateway_server.register_response_listener

I use this to track which token was the problem and then I pick up from there sending the rest. The issue is that when sending the only way to trap these errors is to use a sleep timer between token sends. For example:

for x in self.retryAPNList:
   apns.gateway_server.send_notification(x, payload, identifier = token)
   time.sleep(0.5)

If I don't use a sleep timer no errors are caught and thus my entire APN list is not sent to as the process stops when there is a bad token. However, this sleep timer is somewhat arbitrary. Sometimes the .5 seconds is enough while other times I have had to set it to 1. In no case has it worked without some sleep delay being added. Doing this slows down web calls and it feels less than bullet proof to enter random sleep times.

Any suggestions for how this can work without a delay between APN calls or is there a best practice for the delay needed?

Adding more code due to the request made below. Here are 3 methods inside of a class that I use to control this:

class PushAdmin(webapp2.RequestHandler):

    retryAPNList=[]
    channelID=""
    channelName = ""
    userName=""

    apns = APNs(use_sandbox=True,cert_file="mycert.pem", key_file="mykey.pem", enhanced=True)

    def devChannelPush(self,channel,name,sendAlerts):
        ucs = UsedChannelStore()
        pus = PushUpdateStore()
        channelName = ""
        refreshApnList = pus.getAPN(channel)
        if sendAlerts:
            alertApnList,channelName = ucs.getAPN(channel)
            if not alertApnList: alertApnList=[]
            if not refreshApnList: refreshApnList=[]
            pushApnList = list(set(alertApnList+refreshApnList))
        elif refreshApnList:
            pushApnList = refreshApnList
        else:
            pushApnList = []

        self.retryAPNList = pushApnList
        self.channelID = channel
        self.channelName = channelName
        self.userName = name
        self.retryAPNPush()

    def retryAPNPush(self):
        token = -1
        payload = Payload(alert="A message from " +self.userName+ " posted to "+self.channelName, sound="default", badge=1, custom={"channel":self.channelID})

        if len(self.retryAPNList)>0:
            token +=1
            for x in self.retryAPNList:
                    self.apns.gateway_server.send_notification(x, payload, identifier = token)
                    time.sleep(0.5)

Below is the calling class (abbreviate to reduce non-related items):

class ChannelStore(ndb.Model):
   def writeMessage(self,ID,name,message,imageKey,fileKey):
        notify = PushAdmin()

        notify.devChannelPush(ID,name,True)

Below is the slight change I made to the placement of the sleep timer that seems to have resolved the issue. I am, however, still concerned for whether the time given will be the right amount in all circumstances.

def retryAPNPush(self):
        identifier = 1
        token = -1
        payload = Payload(alert="A message from " +self.userName+ " posted to "+self.channelName, sound="default", badge=1, custom={"channel":self.channelID})

        if len(self.retryAPNList)>0:
            token +=1
            for x in self.retryAPNList:
                self.apns.gateway_server.send_notification(x, payload, identifier = token)
            time.sleep(0.5)

Resolution:

As noted in the comments at bottom, the resolution to this problem was to move the following statement to the module level outside the class. By doing this there is no need for any sleep statements.

apns = APNs(use_sandbox=True,cert_file="mycert.pem", key_file="mykey.pem", enhanced=True)

Source: (StackOverflow)

Advertisements

ssl.SSLError when sending Push message using PyAPNS

I've been trying to access the APNS in order to send push notifications to an iOS app I develop. I have created a development and production certificate requests and signed them on the Apple dev center:

My certificates

enter image description here

For each certificate (production and development), I have three files:

  1. aps_development.cer: A.cer` file I have downloaded from Apple after signing the certificate request.
  2. dev.pem: A .pem public key file from the OSX keychain.
  3. dev.p12: A .p12 private key file from the OSX keychain, with password (and dev_nopass.p12: A .p12 private key file from the OSX keychain, with password).

What I've tried

  1. Cloned and installed PyAPNs form github (The PyPi version seems a bit old and causes import errors.).
  2. Adapted the example code to:

from apns import APNs, Frame, Payload

p12 = '/path/to/dev.p12'
p12_nopass = '/path/to/dev_nopass.p12'
pem = '/path/to/dev.pem'
cer = '/path/to/aps_development.cer'

apns = APNs(use_sandbox=True,
            cert_file=pem,
            key_file=p12_nopass)

# Send a notification
# Dummy token, but it does not cause the error (SSL fails before token check)
token_hex = 'b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b87'
payload = Payload(alert="Hello World!", sound="default", badge=1)
apns.gateway_server.send_notification(token_hex, payload)

Result:

Traceback (most recent call last):
  File "apple_push_driver.py", line 18, in <module>
    apns.gateway_server.send_notification(token_hex, payload)
  File "/path_to/site-packages/apns.py", line 381, in send_notification
    self.write(self._get_notification(token_hex, payload))
  File "/path_to/apns.py", line 174, in write
    return self._connection().write(string)
  File "/path_to/apns.py", line 167, in _connection
    self._connect()
  File "/path_to/apns.py", line 151, in _connect
    self._ssl = wrap_socket(self._socket, self.key_file, self.cert_file)
  File "/path_to/ssl.py", line 387, in wrap_socket
    ciphers=ciphers)
  File "/path_to/ssl.py", line 141, in __init__
    ciphers)
ssl.SSLError: [Errno 336265225] _ssl.c:351: error:140B0009:SSL routines:SSL_CTX_use_PrivateKey_file:PEM lib

I tried both p12 and p12_nopass, production and dev, and got the same results.

Any idea why I can't make SSL connection in order to send a Push notification via PyAPNs?


Source: (StackOverflow)

ios apns not sending consistently to devices

Good day people!

I started having this problem last month when the registered devices on my app stopped receiving push notifications

At first I realized that it was because Apple dropped SSL v3 support so I performed a git pull of the pyapns update.

Push messages started sending properly again, but are not being received. The push server logs are returning few malformed payload errors, but the occurrence of a successfully sent notification is very low, and sometimes being sent in batches.

After rebuilding the push certificate, push messages started being sent on time, and successfully, but this only lasted for a few hours.

As of now, a direct push notification test request to my server is not even being delivered to test devices properly, which was not the case a few hours back.

This is my setup:

  1. All push messages are queued via Resque.

  2. Actual server pushing the payload is 'pyapns.'

  3. Push server OS is Ubuntu 14.04.1 LTS.

  4. Upon checking logs on my service, Resque, and pyapns, I was very sure that I am successfully sending the payload to the Apple push servers. There are no malformed payload responses from APNS, but still, no messages.

  5. Push certificate is recently updated and is being used by server running pyapns.

  6. A quick peek at the server shows that a copy of Entrust CA Root Certificate is installed (as per requirement for TLS connections, specified by Apple).

Can anyone help me in this problem? Thank you!


Source: (StackOverflow)

When APNS tokens fail, is it a must to repeat the remaining push notifications?

I'm sending 1-10 notifications at a time, sometimes one of the tokens fail, I'm guessing they fail when a user deletes the app (or other similar reasons), do I need to retry the remaining tokens/notifications for that batch? (I receive a "failed token" warning later on from PyAPNS)

The tokens are all valid tokens, it's stated that malformed tokens require a retry of the remaining push notifications, however I'm wondering whether I need to retry too with non-malformed tokens, it would be pretty stupid If I need to retry in the above scenario, people delete apps all the time, I'm guessing a high percentage of batches would require a retry

(There are many questions related to this issue, however I wasn't able to find a definite answer, from my trials I'm guessing I already have my answer, It seems the notifications aren't delivered in the above scenario)


Source: (StackOverflow)

How to get the custom payload from pyapns using phonegap push plugin?

we are using PYAPNS to send push notifications from an Django application to iOS devices running an phonegap application.

The server code looks like this:

def sendNotificationAPNS(title, message, targets, eventid):
    apns = APNs(use_sandbox=False, cert_file='/path/to/push_cert.pem', key_file='/path/to/push_cert.pem')

        # Send a notification
        for token_hex in targets:
            payload = Payload(alert=message, sound="default", badge=1, custom={'eventid':str(eventid)})
            apns.gateway_server.send_notification(token_hex, payload)
            print('Notification to APNS send!')

        return true

On the mobile application the code from Phonegap Push Plugin looks like this:

// handle APNS notifications for iOS
function onNotificationAPN(e) {
    if (e.alert) {
        console.log('APNS Notifiation recieved: ' +  e.alert);
        // showing an alert also requires the org.apache.cordova.dialogs plugin
        navigator.notification.alert(e.alert);
    }

    if (e.sound) {
        // playing a sound also requires the org.apache.cordova.media plugin
        var snd = new Media(e.sound);
        snd.play();
    }

    if (e.badge) {
        pushNotification.setApplicationIconBadgeNumber(successHandler, e.badge);
    }
    //eventid
    if(e.eventid) {
        console.log('APNS eventid: ' + e.eventid);
    }

    //custom
    if(e.custom) {
        console.log('APNS eventid: ' + e.custom.eventid);
    }
}

The problem is: I don't get anything for e.custom or e.eventid?! What do I have to change to access the custom payload?

Thanks!


Source: (StackOverflow)

PyAPNs version 1.1.2 doesn’t have enhanced keyword?

I’m sure this is obvious, but I’m missing it. I’ve installed PyAPNs via pip:

# pip install apns

Then when I try to use the “enhanced” flag in APNs, it’s not there.

# python
Python 2.7.6 (default, Nov 11 2013, 18:34:29) 
[GCC 4.4.7 20120313 (Red Hat 4.4.7-3)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from apns import APNs
>>> server = APNs(use_sandbox=True, cert_file=“/mydir/apns-dev-cert.pem", key_file=“/mydir/apns-dev-key.pem", enhanced=True)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: __init__() got an unexpected keyword argument 'enhanced'
>>> 

And sure enough the 1.1.2 version that pip installed doesn’t have that keyword. But I thought this was the latest released version in the PyAPNs repository https://github.com/djacobs/PyAPNs.

I want to use the ‘enhanced’ keyword, for error checking. Any ideas?


Source: (StackOverflow)