Polling Apple Push Notification feedback service with Node.js
June 3, 2012 4 Comments
Apple assumes that your app polls the Push Notification Feedback Service to get information about App uninstalls so you can stop sending notifications to those devices. Service returns list of tokens with uninstall timestamps.
This is how you can do it with Node.js. First, check TLS Example and how to create SSL certs to understand basics of how to make secure SSL connection with Node.js.
First simple utility to convert binary data to hex
function bintohex(buf) { var hexbuf = new Buffer(buf.length * 2); function nibble(b) { if (b <= 0x09) return 0x30 + b; return 0x41 + (b - 10); } for(var i=0; i < buf.length; i++) { hexbuf[i*2] = nibble(buf[i] >> 4); hexbuf[i*2+1] = nibble(buf[i] & 0x0F); } return hexbuf.toString('ascii', 0, hexbuf.length); }
Then define function that will be called with push device tokens
function processTokens( pdtokens ) { // send or process tokens here pdtokens.forEach( function( pd ){ console.log('TOKEN ' + pd.token ' INVALIDATED AT ' + new Date( pd.timestamp ) ); }); }
Then polling function that does it all, note that it assumes function connectAPN that needs simply create SSL connection to ‘feedback.push.apple.com’ port 2196 with your apps client cert and private key.
function pollAPNFeedback() { console.log("Connecting APN feedback service"); connectAPN(function( apnc ) { var bufferlist = []; apnc.on('data', function(data) { // APN feedback starts sending data immediately on successful connect bufferlist.push(data) }); apnc.on('end', function() { console.log("APN Connection closed"); // tokens are parsed to object var pdtokens = [] // concatenate all asynchronously collected buffers, and parse the PDU's from them var parsebuf; var totall = 0; for(var i=0; i < bufferlist.length; i++) { totall += bufferlist[i].length; } parsebuf = new Buffer(totall); var offset = 0; for(var i=0; i < bufferlist.length; i++) { bufferlist[i].copy(parsebuf, offset, 0); offset += bufferlist[i].length; } var count = 0; for(var k = 0; k < parsebuf.length; ) { count++; // parse timestamp var ts = ((parsebuf[k] << 24) + (parsebuf[k+1] << 16) + (parsebuf[k+2] << 8) + parsebuf[k+3]) >>> 0; k += 4; var l = ((parsebuf[k] << 8) + parsebuf[k]) >>> 0; k += 2; var pdtokenbuf = new Buffer(l); parsebuf.copy(pdtokenbuf, 0, k, k + l); k += l; var hextoken = bintohex(pdtokenbuf); pdtokens.push({timestamp: ts, token:hextoken}); // process tokens in 100 item batches if( count >= 100 ) { processTokens( pdtokens ); count = 0; pdtokens = []; } } processTokens(pdtokens); setTimeout(pollAPNFeedback, 1000*60*60*4); }); } pollAPNFeedback();
The function read raw binary data from connection and after Feedback services closes the socket (this happens immediately after it has sent the data) it parses tokens and calls processing function to handle them. Function connects to server every 4 hours.
Note that feedback service host and port is different for sandbox (testing environment) that also needs its own SSL certificates.