immobiliare / ApnsPHP

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

Too many bytes written #88

Open Anarchid opened 9 years ago

Anarchid commented 9 years ago

I've managed to setup ApnsPHP and get it to work; messages are sent and received as expected.

However, there's this peculiar error:

      array (
        'identifier' => 1,
        'statusCode' => 999,
        'statusMessage' => 'Internal error (112 bytes written instead of 101 bytes)',
      ),

This goes for three times, after the third it gives up:

Wed, 22 Apr 2015 17:11:57 +0200 ApnsPHP[63774]: INFO: Trying ssl://gateway.sandbox.push.apple.com:2195...
Wed, 22 Apr 2015 17:11:58 +0200 ApnsPHP[63774]: INFO: Connected to ssl://gateway.sandbox.push.apple.com:2195.
Wed, 22 Apr 2015 17:11:58 +0200 ApnsPHP[63774]: INFO: Sending messages queue, run #4: 1 message(s) left in queue.
Wed, 22 Apr 2015 17:11:58 +0200 ApnsPHP[63774]: WARNING: Message ID 1 [custom identifier: Message-Badge-3] has 3 errors, removing from queue...

And yet, all three messages are received on the device.

What gives?

lucabrunox commented 9 years ago

Is this reproducible with a particular message or it's a sporadic error?

Anarchid commented 9 years ago

This was 100% reproducible for me, with every message i tried (though i only tried a few with some shared fields). The most recent looks like this:

ApnsPHP_Message::__set_state(array(
   '_bAutoAdjustLongPayload' => true,
   '_aDeviceTokens' => 
  array (
    0 => 'fffab12acadav12a1337',
  ),
   '_sText' => '21 unread messages',
   '_nBadge' => 21,
   '_sSound' => 'default',
   '_sCategory' => NULL,
   '_bContentAvailable' => NULL,
   '_aCustomProperties' => 
  array (
    'message' => '21 unread messages',
  ),
   '_nExpiryValue' => 30,
   '_mCustomIdentifier' => 'Message-Badge-3',
))

So far i've workarounded it by disabling the check here, but now it seems the message above no longer causes the error. :|

HunTer44x commented 9 years ago

I think

$nLen = mb_strlen($aMessage['BINARY_NOTIFICATION'], 'latin1');

should be here.

lucabrunox commented 9 years ago

I think that too, I wonder if @Anarchid could test that change...

ghispi commented 9 years ago

Documentation says it should be as defined by RFC 4627. So most likely:

$nLen = mb_strlen($aMessage['BINARY_NOTIFICATION'], 'UTF-8');

Excerpt from rfc:

3.  Encoding

   JSON text SHALL be encoded in Unicode.  The default encoding is
   UTF-8.

   Since the first two characters of a JSON text will always be ASCII
   characters [RFC0020], it is possible to determine whether an octet
   stream is UTF-8, UTF-16 (BE or LE), or UTF-32 (BE or LE) by looking
   at the pattern of nulls in the first four octets.

           00 00 00 xx  UTF-32BE
           00 xx 00 xx  UTF-16BE
           xx 00 00 00  UTF-32LE
           xx 00 xx 00  UTF-16LE
           xx xx xx xx  UTF-8
lucabrunox commented 9 years ago

@ghispi you want to send the raw binary, hence you want the length in bytes not the length in chars.

ghispi commented 9 years ago

True, got confused by the mb. So in such case simple strlen should work correctly. Not sure why mb with latin1 was suggested.

HunTer44x commented 9 years ago

Because many use _mbstring.funcoverload=2 in multi-lingual projects. You can find solution in the user contributed note in PHP Manual about mb_strlen.

ghispi commented 9 years ago

Ok, but I mean why changing currently used strlen to mb_strlen with such weird setup when strlen does exactly what we need. Documentation implicitly says:

Note:
strlen() returns the number of bytes rather than the number of characters in a string.
HunTer44x commented 9 years ago

If mbstring.func_overload=2, the strlen() is overloaded to mb_strlen() and return number of chars (not bytes).

ghispi commented 9 years ago

Ahhh, I never encountered this option but it makes sense. Sorry for the commotion and thanks for explaining.

In such case I'd propose something more flexible which will work if one doesn't have mbstring.

if ((int) ini_get('mbstring.func_overload') & 2) {
    $nLen = mb_strlen($aMessage['BINARY_NOTIFICATION'], 'latin1');
} else {
    $nLen = strlen($aMessage['BINARY_NOTIFICATION']);
}
ghispi commented 9 years ago

There is some bug in php (fixed for php7 alpha) which might cause some of the errors here.

Anarchid commented 7 years ago

The issue has disappeared for me for some (quite long) time and then recently resurfaced.

Since i could not simply upgrade to php 7, i've tried @ghispi's patch from https://github.com/immobiliare/ApnsPHP/issues/88#issuecomment-111251202 instead.

It seems to work wonderfully thus far.