APNs Provider API HTTP/2 使用 php,curl 导致发送的多个推送通知出错

Posted

技术标签:

【中文标题】APNs Provider API HTTP/2 使用 php,curl 导致发送的多个推送通知出错【英文标题】:APNs Provider API HTTP/2 using php, curl causes error on multiple push notifications sent 【发布时间】:2016-08-05 07:34:26 【问题描述】:

我已经设置了一个运行 Ubuntu 15.10 x64 的 linux 服务器。我已经设置了 php/openssl/curl 一起使用 HTTP/2 发送。我正在测试的 PHP 脚本如下。基本上,我正在发送两条推送消息,它们都使用相同的 curl 句柄,以便按照 Apple 的建议保持连接打开。第一条消息通过并很好地显示在我的设备上,但是当它尝试发送第二条消息时,我在“SSL”之后收到错误“Unknown SSL protocol error in connection to api.development.push.apple.com:443”重用会话 ID”。有人对可能出现的问题有任何建议吗?有人可以试试这个脚本,如果他们遇到同样的事情,请告诉我?

以下是我服务器的版本打印输出:

PHP

PHP 7.0.5-2+deb.sury.org~wily+1 (cli) ( NTS )
Copyright (c) 1997-2016 The PHP Group
Zend Engine v3.0.0, Copyright (c) 1998-2016 Zend Technologies
with Zend OPcache v7.0.6-dev, Copyright (c) 1999-2016, by Zend Technologies

OpenSSL

OpenSSL 1.0.2d 9 Jul 2015

卷曲

curl 7.48.0 (x86_64-pc-linux-gnu) libcurl/7.48.0 OpenSSL/1.0.2d zlib/1.2.8 libidn/1.28 nghttp2/1.10.0-DEV librtmp/2.3
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtmp rtsp smb smbs smtp smtps telnet tftp
Features: IDN IPv6 Largefile NTLM NTLM_WB SSL libz TLS-SRP HTTP2 UnixSockets

PHP 代码:

<?php
$ch = curl_init();
$device_token   = 'TOKEN HERE';
$pem_file       = 'YOURFILE.pem';
$pem_secret     = 'PEM PASS';
$apns_topic     = 'com.YOURTOPIC';

//curl_setopt($ch, CURLOPT_SSLVERSION, 6);
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);
curl_setopt($ch, CURLOPT_HTTPHEADER, array("apns-topic: $apns_topic"));
curl_setopt($ch, CURLOPT_SSLCERT, $pem_file);
curl_setopt($ch, CURLOPT_SSLCERTPASSWD, $pem_secret);
curl_setopt($ch, CURLOPT_VERBOSE , true);

echo "Try 1 ================================================" . PHP_EOL;

//setup and send first push message
$url = "https://api.development.push.apple.com/3/device/$device_token";
curl_setopt($ch, CURLOPT_URL, "$url");
$sample_alert = '"aps":"alert":"hi #1","sound":"default"';
curl_setopt($ch, CURLOPT_POSTFIELDS, $sample_alert);

$response = curl_exec($ch);
$httpcode = curl_getinfo($ch);
//var_dump($response);
//var_dump($httpcode);

echo "Try 2 ================================================" . PHP_EOL;

//setup and send second push message
$url = "https://api.development.push.apple.com/3/device/$device_token";
curl_setopt($ch, CURLOPT_URL, "$url");
$sample_alert = '"aps":"alert":"hi #2","sound":"default"';
curl_setopt($ch, CURLOPT_POSTFIELDS, $sample_alert);

$response = curl_exec($ch);
$httpcode = curl_getinfo($ch);
//var_dump($response);
//var_dump($httpcode);

curl_close($ch);

使用 curl verbose 运行上述脚本的输出(个人项目替换为 XXXXX):

Try 1 ================================================
*   Trying 17.110.227.100...
* Connected to api.development.push.apple.com (17.110.227.100) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/certs/ca-certificates.crt
  CApath: none
* SSL connection using TLSv1.2 / XXXXXXXXXXXXXXXXXXXXXXXXXXX
* ALPN, server accepted to use h2
* Server certificate:
*  subject: CN=api.development.push.apple.com; OU=management:idms.group.533599; O=Apple Inc.; ST=California; C=US
*  start date: Jun 19 01:49:43 2015 GMT
*  expire date: Jul 18 01:49:43 2017 GMT
*  subjectAltName: host "api.development.push.apple.com" matched cert's "api.development.push.apple.com"
*  issuer: CN=Apple IST CA 2 - G1; OU=Certification Authority; O=Apple Inc.; C=US
*  SSL certificate verify ok.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* TCP_NODELAY set
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x555e84417f80)
> POST /3/device/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
HTTP/1.1
Host: api.development.push.apple.com
Accept: */*
apns-topic: com.XXXXXXX.XXXXXXXXXXXXXXXXXX
Content-Length: 43
Content-Type: application/x-www-form-urlencoded

* Connection state changed (MAX_CONCURRENT_STREAMS updated)!
* We are completely uploaded and fine
< HTTP/2.0 200
< apns-id:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
<
* Connection #0 to host api.development.push.apple.com left intact
Try 2 ================================================
* Found bundle for host api.development.push.apple.com: 0x555e8442afb0 [can multiplex]
* Hostname api.development.push.apple.com was found in DNS cache
*   Trying 17.110.227.100...
* Connected to api.development.push.apple.com (17.110.227.100) port 443 (#1)
* ALPN, offering h2
* ALPN, offering http/1.1
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH
* successfully set certificate verify locations:
*   CAfile: /etc/ssl/certs/ca-certificates.crt
  CApath: none
* SSL re-using session ID
* Unknown SSL protocol error in connection to api.development.push.apple.com:443
* Closing connection 1

【问题讨论】:

已解决:对于任何想知道如何解决此问题的人,我不得不降级到 7.47.1 - 请参阅下面已接受答案中的评论主题 【参考方案1】:

我尝试了您的代码,它可以在我的机器上正常运行。尽管如此,我还是看到了我们的详细日志之间的一些差异。 Try 1 的 log 是一样的,但是 Try 2 的 log 有一些不同,这是我的:

...
Try 2 ================================================
* Found bundle for host api.development.push.apple.com: 0x7fe1b380e730 [can multiplex]
* Re-using existing connection! (#0) with host api.development.push.apple.com
* Connected to api.development.push.apple.com (17.172.238.203) port 443 (#0)
* Using Stream ID: 3 (easy handle 0x7fe1b305da00)
> POST /3/device/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX HTTP/1.1
Host: api.development.push.apple.com
Accept: */*
apns-topic: it.XXX.XXXXX
Content-Length: 43
Content-Type: application/x-www-form-urlencoded

* We are completely uploaded and fine
< HTTP/2.0 200
< apns-id:XXXXXXXXXXXXXXXXX
< 
* Connection #0 to host api.development.push.apple.com left intact

我在您的日志中没有看到“重用现有连接!”的文字。 ...

编辑

解决方案似乎是将 curl 降级到 7.47.1

【讨论】:

感谢您为我测试!我将研究“重用现有连接”问题。我的服务器上可能有一些错误配置,但看到其他人尝试相同的代码并获得不同的输出确实很有帮助!谢谢! 您是否尝试降级到 curl 7.47.1? 接下来我会试一试。 好吧,经过数小时的研究,降级到 7.47.1 解决了这个问题。感谢您的帮助!!我会接受这个答案。对于任何有兴趣的人,我使用下面的说明并将版本号替换为 7.47.1 serversforhackers.com/video/curl-with-http2-support 好吧,你也帮了我(我暂时不会升级 curl)。我编辑答案以使解决方案更清晰【参考方案2】:

有同样的问题。在挖掘代码时在 github 包上找到了这个解决方案,对我有用。 https://github.com/nfilin/apns-http2.

将此添加到 curl 的选项中,然后重试。

curl_setopt($ch, CURLOPT_SSLKEY, $pem_file);
curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM');

【讨论】:

【参考方案3】:

正如公认的答案所说,它通过降级到 cur-7.47.1 来工作...但前提是所有发送的消息都返回 HTTP 200 代码。

例如,如果第二条消息包含无效的负载或设备令牌(并且如预期的那样,收到了 HTTP 4xx 响应),则在 第三条 消息中获取“连接中的未知 SSL 协议错误到 api.development.push.apple.com:443"。

我猜预期的行为是在第二条消息上收到 4xx,但不会中断连接。

如果没有收到 200,则不符合标准的解决方法是断开连接并重新连接。但我想经过多次断开/重新连接循环后,我们会被苹果服务器禁止。

【讨论】:

你好,你知道用于生产推送的 api,而不是开发吗?谢谢

以上是关于APNs Provider API HTTP/2 使用 php,curl 导致发送的多个推送通知出错的主要内容,如果未能解决你的问题,请参考以下文章

APNS Provider API 和 Postman

将 APNS 二进制协议升级到增强的基于 HTTP/2 的 API

iOS推送原理

基于 HTTP/2 的全新 APNs 协议

如何在调用 API 时向 APNS 发送推送证书?

OpenSSL 标头版本!= 影响 APNS 的 HTTP/2 的 OpenSSL 库版本