immobiliare / ApnsPHP

ApnsPHP: Apple Push Notification & Feedback Provider
BSD 3-Clause "New" or "Revised" License
1.44k stars 452 forks source link

Invalid tokens cause random "Connection reset by peer", which prevents from finishing a sending batch #34

Closed albertboada closed 9 years ago

albertboada commented 11 years ago

Been testing the library for quite a while and, although it's definitely the best one out there for PHP, I'm almost getting insane with some random behaviour I've noticed mixing valid and invalid tokens.

After writing all push notifications to the socket (both valid and invalid tokens mixed up), we just wait through select_stream() for any error feedback. When the error is available (which it is, since $nChangedStreams is 1), we call _updateQueue(), which calls _readErrorMessage(), which does a fread() to the socket.

Although $nChangedStreams is indeed always 1 whenever it has to be, _readErrorMessage's fread() fails once in a while, throwing away the following warning (both in UNIX and Windows environments):

Warning: fread(): SSL: Connection reset by peer in path_to_project/ApnsPHP/Push.php on line 302 

Something is getting in the way between the select_stream() check, and the following fread(), but I'm not sure anymore which end is to blame here.

As mentioned, this only happens sometimes, not always, not never. When it doesn't, everything goes well and all valid notifications end up being sent, with its proper retries, as expected. On the other hand, when it fails, the queue is emptied, since the library considers there's no error if fread() returns nothing, and so retries are lost and execution ends (having lost the remaining pending notifications).

Am I the only one to notice this behaviour? Or is it a common issue? Advice is appreciated.

Thanks for your time.

ghost commented 11 years ago

I grepped my logs for the last month and can't find this error. But I'm making reconnect every 5k pushes.

albertboada commented 11 years ago

Do you usually send any invalid token? It happens when mixing valid with invalid tokens (e.g. 1/3 invalid tokens per batch).

Besides, you might no be seeing any error because fread() is error-supressed via the @ operator in the original code (which does not mean it isn't happening). I'm seeing it because I removed it just to make sure something odd was really happening.

ghost commented 11 years ago

Sometimes of course, but not so much as you. And I didn't change value of socket write interval ( https://github.com/duccio/ApnsPHP/blob/d352b53c5dc14279fc3928379d28fcf50986d27c/ApnsPHP/Abstract.php#L47 )

albertboada commented 11 years ago

Since I don't think one can assume the library is working properly just by looking at production logs and just believe everything went well, I'm proposing everyone test the ApnsPHP library (master) this way:

$tokens = array(
    '[invalid token goes here]' // invalid token
    '[valid token goes here]'   // valid token 
);

require_once 'ApnsPHP/Autoload.php';

$push = new ApnsPHP_Push(
    ApnsPHP_Abstract::ENVIRONMENT_SANDBOX,
    '[your sandbox app certificate here]'
);

$push->setRootCertificationAuthority('[CA certifica here]');

$push->connect();

for ($i = 1; $i <= 20; $i++) {
    foreach ($tokens as $token) {
        $msg_id = sprintf('Message %s to %s', $i, substr($token, 0, 10));
        $message = new ApnsPHP_Message($token);
        $message->setCustomIdentifier($msg_id);
        $message->setText('Test'.' '.$i.' '.date('H:i:s').' '.uniqid());
        $message->setSound();
        $push->add($message);
    }
}

$push->send();

$push->disconnect();

$aErrorQueue = $push->getErrors();
if (!empty($aErrorQueue)) {
    var_dump($aErrorQueue);
}

This script basically tries to send 40 push notifications, 20 invalid, 20 valid, alternately: [1] invalid, [2] valid, [3] invalid, [4] valid, ..., [40] valid

After running the script, check if all 20 valid notifications have been sent (and received). Check also if all 20 invalid notifications have been identified through the console logs, and the execution has retried the sending from the correct points. Repeat the test some times.

After doing these tests, what I observe is that the script more than occasionally finishes sooner than it should, leaving some notifications unsent. There is no pattern in this behaviour, the execution finishes randomly at different points in each execution (i.e. having sent 7, 15, all, or even zero notifications).

As I explained, all this is because, at some random point, _readErrorMessage()'s @fread() (https://github.com/duccio/ApnsPHP/blob/master/ApnsPHP/Push.php#L295) is failing with the SSL: Connection reset by peer error. This translates into an empty fread() result, which the library interprets as no errors (although we know there were errors!!) and finishes the execution (leaving pushes unsent that should had been retried).

I'm no expert in socket writing and reading, but either (1) the library is not handling Apple disconnections properly, or (2) Apple has changed the way they proceed, or (3) I am doing something very wrongly.

Finally, I know sending invalid tokens should not happen, but understand that before using this library for production I had to fully test it in order to make sure it would ensure 100% correct sending and feedback, which, at least in my case, it doesn't until this is solved :(

Let me know your test results! :)

albertboada commented 10 years ago

No one, really?

ghost commented 10 years ago

Did you set socket write interval to zero? ( https://github.com/duccio/ApnsPHP/blob/d352b53c5dc14279fc3928379d28fcf50986d27c/ApnsPHP/Abstract.php#L47

Finally, I know sending invalid tokens should not happen, but understand that before using this library for production

If you have 20 tokens and 19 first are incorrect then it will send like this:

And this library reconnect every ~200-300 push messages, but I don't undertand the trouble, really.

albertboada commented 10 years ago

Yes, I tweaked the write interval with no luck, and I know how the sending process IS SUPPOSED TO BE. But the thing is that it is not always like that. The sending process sometimes ends unexpectedly, at least in my case.

There is no point in discussing an issue based on someone's beliefs. Have you tried the test I proposed?. If you try it, we can comment on the results and then you can call me crazy if you want. If you don't try it, there is no point we waste any more time here.

ghost commented 10 years ago

Of course I tried, but i haven't see any errors.

albertboada commented 10 years ago

Would you mind attaching the output of a few executions of that test script, then? That would probably help me a lot. Thanks!

rbattles commented 9 years ago

albertboada, I am seeing the same activity in my app as well. We regularly send out push notifications in large batches and this library is unreliable unless you do not batch them. Have you found a solution or a better library?

albertboada commented 9 years ago

Hey @rbattles,

sadly, I never went to the bottom with all this, and I say sadly because this is the best php apns library I found, since it has queue and retry system built in which others don't have.

The behaviour reported here is observed when mixing invalid and valid tokens in the batch. This may happen in your test env., but, hopefully, it should not on your real env., since all the push tokens you have collected there are for sure not from the apns sandbox.

I actually put some effort in here, but no useful feedback was given. I could not get any further on my own either, altho I tried.

I stick to this lib anyway.

rbattles commented 9 years ago

Hi @albertboada, thank you for your reply. Not sure why this was labeled invalid. This issue does exist. I will keep exploring my options and will post back if I make headway. Might just resort to threading and sending them out one by one. I appreciate the work that has gone into this library, but this problem is quite severe for apps that feature push notifications for reliable communication.

junghyun-kim commented 9 years ago

Any updates on this issue?

lucabrunox commented 9 years ago

I'm not able to reproduce this issue using the code in https://github.com/immobiliare/ApnsPHP/issues/34#issuecomment-20441441 . I correctly see 20 messages being sent, and 20 errors at the end. I don't see any connection reset by peer error in the middle, so that's probably why it works for me.

Is it possible for you to understand what's causing the connection reset?

junghyun-kim commented 9 years ago

Frankly speaking, I'm using another language - ruby. And I am facing the same problem. 'Connection reset by peer' after sending messages including some invalid tokens. But it is not always. I tested with 1000 messages with 10~20% of invalid tokens. And sometimes I can see ECONNRESET (connection reset by peer).

I'm sorry to ask here. I think this issue is not a problem of this library. I found same problem at node-apn. (https://github.com/argon/node-apn/issues/137)