It's a late night. I just spent 10 hours in google/stackoverflow search and experiments. And seems I hate Apple Push Notifications. I'm totally frustrated and will appreciate any help.
Thank you.
The problem:
The PHP code for sending Apple Push Notifications, which successfully worked two weeks ago stopped to work now and throws following errors:
PHP Warning: stream_socket_client(): Failed to enable crypto in /home/...
PHP Warning: stream_socket_client(): unable to connect to ssl://gateway.push.apple.com:2195 (Unknown error) in /home/...
It stopped to work on two separate servers, which are using separate scripts for APNs sending.
Environment:
Servers: CentOS 6.5 with PHP 5.4.32 and Ubuntu 14.04.3 with PHP 5.5.9
APN: In production mode
Certificates: tested with 700+ push notifications.
One of the servers is using https://github.com/immobiliare/ApnsPHP, other - https://github.com/antongorodezkiy/wp-apn, on localhost I tested simple file without using any third party code.
Investigation:
For all cases below I used the same active device token and the same production PEM certificate.
php
However, even this simple code doesn't work on both servers and localhost and return the same error as above:
$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', '/absolute/path/to/apn_prod.pem');
// Open a connection to the APNS server
$fp = stream_socket_client(
'ssl://gateway.push.apple.com:2195', $err,
$errstr, 60, STREAM_CLIENT_CONNECT|STREAM_CLIENT_PERSISTENT, $ctx);
I also tried to play with stream_context_set_option()
options, include entrust_2048_ca.cer
, etc, and even some options from this article. Although provided code worked without any modifications before August 2015.
openssl
Connection worked with openssl (link):
openssl s_client -connect gateway.push.apple.com:2195 -cert /absolute/path/to/apn_prod.pem -debug -showcerts -CAfile /absolute/path/to/server-ca-cert.pem
And got with CONNECTED(00000003)
and Verify return code: 0 ( ok )
.
telnet
Connection worked with telnet:
-sh-4.1$ telnet gateway.push.apple.com 2195
Trying 17.172.233.150...
Connected to gateway.push.apple.com.
pecl apn
It didn't send push notification. I just tried to use adaptation of sample code, but got the error Invalid token
. The token is active and same token I used everywhere and for Houston and Ruby too.
houston
It worked with Houston
apn push "0346a53f...231d9d6abe11" -c /absolute/path/to/apn_prod.pem -m "Hello from the command line!" -e "production"
ruby
I'm not a Ruby programmer (yet at least), but after success with Houston, I found and adapted Ruby code without Houston dependency.
And it worked:
#!/usr/bin/env ruby
require 'openssl'
require 'socket'
require 'json'
token = "0346a53f...231d9d6abe11"
cert = File.read("/absolute/path/to/apn_prod.pem")
ctx = OpenSSL::SSL::SSLContext.new
ctx.key = OpenSSL::PKey::RSA.new(cert, '') #set passphrase here, if any
ctx.cert = OpenSSL::X509::Certificate.new(cert)
sock = TCPSocket.new('gateway.push.apple.com', 2195) #development gateway
ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
ssl.connect
payload = {"aps" => {"alert" => "Oh hai!", "badge" => 1, "sound" => 'default'}}
json = payload.to_json()
token = [token.delete(' ')].pack('H*') #something like 2c0cad 01d1465 346786a9 3a07613f2 b03f0b94b6 8dde3993 d9017224 ad068d36
apnsMessage = "\0\0 #{token}\0#{json.length.chr}#{json}"
ssl.write(apnsMessage)
ssl.close
sock.close
puts "End"
Questions:
- What's wrong with PHP? Is there some bug related to this issue? (I didn't find bug report though)
- Any ideas how to solve this issue?
- Any ideas what could be the difference in PHP and Ruby cases (I assume that Python or Perl could work fine too)? I even tried to read PHP sources, but without success to understand how
stream_socket_client()
implemented.