Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should the connection to APNs be closed after voip push notification is sent?

I am using a simplepush.php script to send voip pushes from user to user. My app can potentially make many of these push requests depending on how many users it acquires. Every example of simplepush.php that I found seems to explicitly close the connection at the end - here is my script (see last line):

$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', 'voip.pem');
stream_context_set_option($ctx, 'ssl', 'passphrase', $passphrase);

// Open a connection to the APNS server
$fp = stream_socket_client($apnsUrl, $err,
    $errstr, 60, STREAM_CLIENT_CONNECT|STREAM_CLIENT_PERSISTENT, $ctx);

if (!$fp)
    exit("Failed to connect: $err $errstr" . PHP_EOL);

echo 'Connected to APNS' . PHP_EOL;

// Create the payload body
$body['aps'] = array(
    'alert' => $message,
    'sound' => 'default'
    );

$body['info'] = array(
  'roomname' => $roomName,
  'uuid' => $uuid
  );

// Encode the payload as JSON
$payload = json_encode($body);

// Build the binary notification
$msg = chr(0) . pack('n', 32) . pack('H*', $deviceToken) . pack('n', strlen($payload)) . $payload;

// Send it to the server
$result = fwrite($fp, $msg, strlen($msg));

if (!$result)
    echo 'Message not delivered' . PHP_EOL;
else
    echo 'Message successfully delivered' . PHP_EOL;

// Close the connection to the server
fclose($fp);

Please note: I am using the legacy APNs binary interface to send notifications instead of an HTTP/2 request because all the simplepush scripts used it. I am not well versed in PHP, but it seems like the script is closing the connection at the end of every call: fclose($fp);

But according to Apple I should leave the connection open:

Best Practices for Managing Connections Keep your connections with APNs open across multiple notifications; do not repeatedly open and close connections. APNs treats rapid connection and disconnection as a denial-of-service attack. https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CommunicatingwithAPNs.html#//apple_ref/doc/uid/TP40008194-CH11-SW6

But since I'm using the legacy binary interface, should I actually be closing the connection after every call? Or am I misunderstanding the fclose($fp); function here? Any clarity on the appropriate way to handle the connection when using this binary would be much appreciated!

like image 985
Alan Scarpa Avatar asked Mar 16 '18 19:03

Alan Scarpa


People also ask

What is VoIP push notification in iOS?

In iOS 8 Apple introduced PushKit, and VoIP pushes - a new type of push notifications. On top of the standard push functionality, a VoIP push allows the app to execute code before displaying the notification to the user.

What is VoIP push notification?

VoIP notifications are background messages that don't generate alerts or sounds. These notifications are used to wake up apps and pass across information about incoming calls. With VoIP, mobile apps let users send and receive calls on their devices, using the app interface rather than the default phone interface.

Do push notifications use Internet?

Web push notifications are notifications that can be sent to a user via desktop web and mobile web.

Do you need to be logged in to receive push notifications?

A push notification is a message that pops up on a mobile device, such as a sports score, an invitation to a flash sale or a coupon for downloading. App publishers can send them at any time, since users don't have to be in the app or using their devices to receive them.


1 Answers

Explanation

As far as I can tell, the recommendation not to close the connection for each notification comes from an area of bulk notification delivery, where many users are delivered the same notification.

Apart from possibly being interpreted as an attack, closing and reopening all the time is very inefficient and would cause huge delivery delays. Just writing the binary message on the stream is very much faster than opening and closing for each notification. See this example:

// Open a connection to the apns server (this code is the same as a few lines below, so if changed here, also change there)
$stream_context = stream_context_create();
stream_context_set_option($stream_context, 'ssl', 'local_cert', 'voip.pem');
stream_context_set_option($stream_context, 'ssl', 'passphrase', 'secret_pass');
$apns = stream_socket_client($url, $err, $errstr, 60, STREAM_CLIENT_CONNECT|STREAM_CLIENT_PERSISTENT, $stream_context);

// Return if connection is impossible
if (!$apns) { return; }

// Create payload body
$body['aps'] = array(
    'alert' => $title,
    'badge' => 1
);

// Encode the payload as json
$payload = json_encode($body);

// Iterate through token array
foreach($tokens as $token) {    
    $user_id = 2 // Random id ;)

    // Build binary notification
    $msg = pack("C", 1);
    $msg .= pack("N", $user_id);
    $msg .= pack("N", $notification_expiration_date);
    $msg .= pack("n", 32);
    $msg .= pack('H*', $token);
    $msg .= pack("n", strlen($payload));
    $msg .= $payload;

    // Send to the server
    $fwrite_result = fwrite($apns, $msg, strlen($msg));
}

fclose($apns);

See how it opens one connection, writes for each token in the array, and closes afterwards, instead of opening, writing and closing each time.

Having considered a bulk push application as an example, it's now your design decision whether the connection should be kept. If you send out notification just every hour, it would be suitable to open and close for each notification in my opinion. However, if you have a throughput of multiple notification per minute you will need to keep the connection open.

Suggestion

One approach you could take is looking for any new notifications to be delivered after finishing the existing queue. If there are none, you could wait for another few minutes, then check again and close the connection if still nothing new is there, if there are some, keep it open and send the new notifications using the same connection.

This would be in-line with Apple's recommendations:

You should leave a connection open unless you know it will be idle for an extended period of time—for example, if you only send notifications to your users once a day, it is acceptable practice to use a new connection each day.

Warning

One important thing to consider when using one single connection is error handling: Invalid tokens (production / sandbox mode confused) can cause the connection to close without you noticing but there are other posts discussing that further.

like image 186
fredpi Avatar answered Oct 05 '22 16:10

fredpi