■iOS: アプリにPHPからPushを送信(HTTP/2版)
※「旧式のバイナリインターフェイス」が使える間は、この手順は省略できる
が、早い段階で検証しておくことを推奨
APNs Provider API(HTTP/2)をPHPで試してみる - Qiita
https://qiita.com/itosho/items/2402df4de85b360d5bd9
■pemファイルの作成
「iOS: アプリにPHPからPushを送信」と同じ手順で作成する
■環境の構築
PHP curlでHTTP/2リクエストを実行するための設定 on CentOS 7 | 稲葉サーバーデザイン
https://inaba-serverdesign.jp/blog/20171011/php_curl_http2_centos7.html
PHP+curlでHTTP/2リクエストを送信できる環境が必要
上記ページが参考になりそうだが、Amazon Linux 2 なら特別な更新作業なしにPHP+curlでHTTP/2リクエストを送信できた
以下は2020年1月7日時点に構築した Amazon Linux 2。PHPはExtrasリポジトリからインストールした
# cat /etc/system-release
Amazon Linux release 2 (Karoo)
# openssl version
OpenSSL 1.0.2k-fips 26 Jan 2017
# php --version
PHP 7.3.11 (cli) (built: Oct 31 2019 19:16:47) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.3.11, Copyright (c) 1998-2018 Zend Technologies
# curl --version
curl 7.61.1 (x86_64-koji-linux-gnu) libcurl/7.61.1 OpenSSL/1.0.2k zlib/1.2.7 libidn2/2.3.0 libssh2/1.4.3 nghttp2/1.39.2
Release-Date: 2018-09-05
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtsp scp sftp smb smbs smtp smtps telnet tftp
Features: AsynchDNS IDN IPv6 Largefile GSS-API Kerberos SPNEGO NTLM NTLM_WB SSL libz HTTP2 UnixSockets HTTPS-proxy Metalink
# php -r 'phpinfo();' | grep SSL
SSL => Yes
MULTI_SSL => No
SSL Version => OpenSSL/1.0.2k
core SSL => supported
extended SSL => supported
OpenSSL support => enabled
OpenSSL Library Version => OpenSSL 1.0.2k-fips 26 Jan 2017
OpenSSL Header Version => OpenSSL 1.0.2k 26 Jan 2017
Native OpenSSL support => enabled
# curl -vso /dev/null --http2
https://www.google.co.jp/
* Trying 216.58.197.131...
* TCP_NODELAY set
* Connected to www.google.co.jp (216.58.197.131) 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/pki/tls/certs/ca-bundle.crt
CApath: none
* TLSv1.2 (OUT), TLS header, Certificate Status (22):
} [5 bytes data]
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
} [512 bytes data]
* TLSv1.2 (IN), TLS handshake, Server hello (2):
{ [96 bytes data]
* TLSv1.2 (IN), TLS handshake, Certificate (11):
{ [3491 bytes data]
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
{ [148 bytes data]
* TLSv1.2 (IN), TLS handshake, Server finished (14):
{ [4 bytes data]
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
} [70 bytes data]
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
} [1 bytes data]
* TLSv1.2 (OUT), TLS handshake, Finished (20):
} [16 bytes data]
* TLSv1.2 (IN), TLS change cipher, Change cipher spec (1):
{ [1 bytes data]
* TLSv1.2 (IN), TLS handshake, Finished (20):
{ [16 bytes data]
* SSL connection using TLSv1.2 / ECDHE-ECDSA-AES128-GCM-SHA256
* ALPN, server accepted to use h2
* Server certificate:
* subject: C=US; ST=California; L=Mountain View; O=Google LLC; CN=*.google.com
* start date: Dec 3 14:48:49 2019 GMT
* expire date: Feb 25 14:48:49 2020 GMT
* subjectAltName: host "www.google.co.jp" matched cert's "*.google.co.jp"
* issuer: C=US; O=Google Trust Services; CN=GTS CA 1O1
* SSL certificate verify ok.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
} [5 bytes data]
* Using Stream ID: 1 (easy handle 0x6fdb70)
} [5 bytes data]
> GET / HTTP/2
> Host: www.google.co.jp
> User-Agent: curl/7.61.1
> Accept: */*
>
{ [5 bytes data]
* Connection state changed (MAX_CONCURRENT_STREAMS == 100)!
} [5 bytes data]
< HTTP/2 200
< date: Tue, 07 Jan 2020 02:42:04 GMT
< expires: -1
< cache-control: private, max-age=0
< content-type: text/html; charset=Shift_JIS
< p3p: CP="This is not a P3P policy! See g.co/p3phelp for more info."
< server: gws
< x-xss-protection: 0
< x-frame-options: SAMEORIGIN
< set-cookie: 1P_JAR=2020-01-07-02; expires=Thu, 06-Feb-2020 02:42:04 GMT; path=/; domain=.google.co.jp; Secure
< set-cookie: NID=195=J90uYLvbutUBMyCjx5B-cfvCaSQbmbTbItStMl_8Md-t_k6qRCq18qweObs-AYSOHrUVkw0u3xS-4y6DC_1kC4eOgd5JC94rgpdM9qm-62BiEZecOeYCMXqqZbdvZgdIEUpvAMBKM_gWQK2X_p5YFwtyCoHVl0M9B-wegtle3hc; expires=Wed, 08-Jul-2020 02:42:04 GMT; path=/; domain=.google.co.jp; HttpOnly
< alt-svc: quic=":443"; ma=2592000; v="46,43",h3-Q050=":443"; ma=2592000,h3-Q049=":443"; ma=2592000,h3-Q048=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000
< accept-ranges: none
< vary: Accept-Encoding
<
{ [5 bytes data]
* Connection #0 to host www.google.co.jp left intact
■テスト用PHPプログラム
※ファイルの文字コードは UTF-8N にする
$ vi curl_test.php
<?php
if (!defined('CURL_HTTP_VERSION_2_0')) {
define('CURL_HTTP_VERSION_2_0', CURL_HTTP_VERSION_1_1 + 1);
}
$url = 'https://www.google.co.jp/';
$opts = [
CURLOPT_VERBOSE => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_2_0,
CURLOPT_SSL_VERIFYHOST => false,
CURLOPT_SSL_VERIFYPEER => false
];
$ch = curl_init($url);
curl_setopt_array($ch, $opts);
curl_exec($ch);
curl_close($ch);
以下で実行できる
Googleのページデータを取得できれば成功
# php curl_test.php
* Trying 172.217.25.227...
* TCP_NODELAY set
* Connected to www.google.co.jp (172.217.25.227) 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/pki/tls/certs/ca-bundle.crt
CApath: none
* SSL connection using TLSv1.2 / ECDHE-ECDSA-AES128-GCM-SHA256
* ALPN, server accepted to use h2
* Server certificate:
* subject: C=US; ST=California; L=Mountain View; O=Google LLC; CN=*.google.com
* start date: Dec 10 08:42:43 2019 GMT
* expire date: Mar 3 08:42:43 2020 GMT
* issuer: C=US; O=Google Trust Services; CN=GTS CA 1O1
* SSL certificate verify ok.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x55866798eba0)
> GET / HTTP/2
Host: www.google.co.jp
Accept: */*
* Connection state changed (MAX_CONCURRENT_STREAMS == 100)!
< HTTP/2 200
< date: Tue, 07 Jan 2020 02:54:02 GMT
< expires: -1
< cache-control: private, max-age=0
< content-type: text/html; charset=Shift_JIS
< p3p: CP="This is not a P3P policy! See g.co/p3phelp for more info."
< server: gws
< x-xss-protection: 0
< x-frame-options: SAMEORIGIN
< set-cookie: 1P_JAR=2020-01-07-02; expires=Thu, 06-Feb-2020 02:54:02 GMT; path=/; domain=.google.co.jp; Secure
< set-cookie: NID=195=F6rGPJWX6DSZJFKvW036m6NKEqLkasi-SLWmkjF6rwGRm9Ks9S2Ix3xVj1Kplj1_C1Bg0gc8LOsjuFJcR64Orm-tR7h0pIBuncfPxFEZsqP-pysrYrU_ah2C24Tty8ng2cyDMIBiPbOBBlluAPryLSYiJbOTMX8DoCgjwpRMUzU; expires=Wed, 08-Jul-2020 02:54:02 GMT; path=/; domain=.google.co.jp; HttpOnly
< alt-svc: quic=":443"; ma=2592000; v="46,43",h3-Q050=":443"; ma=2592000,h3-Q049=":443"; ma=2592000,h3-Q048=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000
< accept-ranges: none
< vary: Accept-Encoding
<
<!doctype html><html itemscope="" itemtype="http://schema.org/WebPage" lang="ja"><head>
〜〜中略
</body></html>* Connection #0 to host www.google.co.jp left intact
■curlでプッシュを送信
以下のコマンドでプッシュを送信できる(curlで送信するならPHPは関係ない)
$ curl -v -d '{"aps":{"alert":"[送信メッセージ]"}}' -H "[アプリのID]" --http2 --cert [pemファイル]
https://api.development.push.apple.com/3/device/[デバイストークン]
具体的には以下のようになる
(server_certificates_bundle_sandbox.pem は同じ階層に配置してあるものとする
p12 ファイルではなく pem ファイルが必要なので注意)
$ curl -v -d '{"aps":{"alert":"Hello!"}}' -H "net.refirio.pushtest1" --http2 --cert server_certificates_bundle_sandbox.pem
https://api.development.push.apple.com/3/device/f7b8b8ade018f18e8b0dd9e34cfd1764575941243e21d9b47a6d...
* Trying 17.188.165.219...
* TCP_NODELAY set
* Connected to api.development.push.apple.com (17.188.165.219) 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/pki/tls/certs/ca-bundle.crt
CApath: none
* TLSv1.2 (OUT), TLS header, Certificate Status (22):
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Request CERT (13):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Certificate (11):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS handshake, CERT verify (15):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
* 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: Apr 17 17:37:51 2019 GMT
* expire date: May 16 17:37:51 2021 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)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x2046b70)
> POST /3/device/f7b8b8ade018f18e8b0dd9e34cfd1764575941243e21d9b47a6d3a3ad62d277b HTTP/2
> Host: api.development.push.apple.com
> User-Agent: curl/7.61.1
> Accept: */*
> Content-Length: 26
> Content-Type: application/x-www-form-urlencoded
>
* Connection state changed (MAX_CONCURRENT_STREAMS == 1000)!
* We are completely uploaded and fine
< HTTP/2 200
< apns-id: DE5A62A4-C4FE-E576-8245-03404F50A790
<
* Connection #0 to host api.development.push.apple.com left intact
■PHPでプッシュを送信
※ファイルの文字コードは UTF-8N にする
apns_test.php
<?php
if (defined('CURL_HTTP_VERSION_2_0')) {
$ch = curl_init('https://api.development.push.apple.com/3/device/f7b8b8ade018f18e8b0dd9e34cfd1764575941243e21d9b47a6d3a3ad62d277b');
curl_setopt($ch, CURLOPT_POSTFIELDS, '{"aps":{"alert":"Hello!"}}');
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['apns-topic: net.refirio.pushtest1']);
curl_setopt($ch, CURLOPT_SSLCERT, 'server_certificates_bundle_sandbox.pem');
//curl_setopt($ch, CURLOPT_SSLCERTPASSWD, 'your pem secret'); // pemファイルにパスワードを設定している場合は必要
$response = curl_exec($ch);
$httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
var_dump($response);
var_dump($httpcode);
}
以下で実行できる
(server_certificates_bundle_sandbox.pem は同じ階層に配置してあるものとする)
$ php apns_test.php
bool(true)
int(200)
送信内容に問題があると、JSON形式でエラー内容を返してくれる
以下はデバイストークンを間違えた場合の例
$ php apns_test.php
{"reason":"BadDeviceToken"}bool(true)
int(400)