Sending notifications via Apple’s new HTTP/2 API (using Jetty 9.3.6)

HTTP/2 is still very much new to Java, and as such, there are just two libraries who support it – Jetty (from 9.3), and Netty (in alpha). If you’re going the Jetty way (as I have), you’ll need to add their ALPN library to your boot classpath.

Note: Jetty 9.3.x requires the use of Java 8.

A full library for this is available here, on GitHub.

Here’s a quick example:


package com.judepereira.jetty.apns.http2;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.util.StringContentProvider;
import org.eclipse.jetty.http2.client.HTTP2Client;
import org.eclipse.jetty.http2.client.http.HttpClientTransportOverHTTP2;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import java.io.FileInputStream;
import java.security.KeyStore;
public class Main {
public static void main(String[] args) throws Exception {
HTTP2Client http2Client = new HTTP2Client();
http2Client.start();
KeyStore ks = KeyStore.getInstance("PKCS12");
// Ensure that the password is the same as the one used later in setKeyStorePassword()
ks.load(new FileInputStream("MyProductionOrDevelopmentCertificate.p12"), "".toCharArray());
SslContextFactory ssl = new SslContextFactory(true);
ssl.setKeyStore(ks);
ssl.setKeyStorePassword("");
HttpClient client = new HttpClient(new HttpClientTransportOverHTTP2(http2Client), ssl);
client.start();
// Change the API endpoint to api.development.push.apple.com if you're using a development certificate
Request req = client.POST("https://api.push.apple.com")
// Update your :path "/3/device/<your token>"
.path("/3/device/b2482deaf55521b2ccd755d5817a39784cc0044e24s3523a4708c2fa08983bdf")
.content(new StringContentProvider("{ \"aps\" : { \"alert\" : \"Hello\" } }"));
ContentResponse response = req.send();
System.out.println("response code: " + response.getStatus());
// The response body is empty for successful requests
System.out.println("response body: " + response.getContentAsString());
}
}

view raw

Main.java

hosted with ❤ by GitHub


Comments

14 responses to “Sending notifications via Apple’s new HTTP/2 API (using Jetty 9.3.6)”

  1. Thanks for the example above.

    I’m trying to get it running and everything seems to be correct but when I execute the request.send() method it just hangs.
    I never get a response nor do I get a error…have let it run for up to 15 – 20 minutes and it doesn’t even timeout.

    Any ideas?
    Thanks,
    Jeff

    1. Jeff, looks like you’ve missed out on the ALPN library in your boot classpath. I’ve faced this issue a few times in the beginning.

      1. Hey Jude,
        I have it on there (or at least I think it correctly) and I’m getting the following on the server output:

        ALPN protocols [h2, h2-17, h2-16, h2-15, h2-14] for 146edad1[SSLEngine[hostname=api.development.push.apple.com port=443] SSL_NULL_WITH_NULL_NULL

        then hangs there.

        1. Strange, could you try with Jetty 9.3.7? 9.3.6 had a connection related bug, which was fixed in 9.3.7 (the bug was that Jetty wouldn’t reuse it’s internal HTTP/2 streams, and would crash after 500 requests).

          1. ah..yes…I’ll give that a shot.

            Thanks!

            1. Hi Jeff! Have you found out the reason that causing hang?
              I have searching for it for two days with no luck.
              Also, I have noticed that when I`m trying to send HTTP/2 request to APNS with another java lib (okHttp), i`m facing the same issue.
              Maybe there are some firewall setting or something like that?

              1. The ALPN version is tied with the JRE version. I’ve faced the same thing in the beginning. What version of Java and the ALPN library are you using?

                Also, you’ve got to put it in the boot class path.

                1. Yes, in VM options of my app launch i got:
                  -Xbootclasspath/p:”D:\\apln-boot-8.0.0.0.jar”

                  dependencies from gradle
                  compile ‘org.eclipse.jetty.http2:http2-http-client-transport:9.3.7.v20160115’
                  compile ‘org.eclipse.jetty:jetty-http:9.3.7.v20160115’
                  compile ‘org.eclipse.jetty.http2:http2-client:9.3.7.v20160115’
                  compile ‘org.eclipse.jetty:jetty-alpn-client:9.3.7.v20160115’
                  compile ‘org.eclipse.jetty.alpn:alpn-api:1.1.2.v20150522’

                  while running, I got log:
                  [main] INFO org.eclipse.jetty.util.ssl.SslContextFactory – x509=X509@685cb137(name surname,h=[],w=[]) for SslContextFactory@6a41eaa2(null,null).

                  does SslContextFactory@6a41eaa2(null,null) shows that something is wrong?

                  1. I get null, null too.
                    I’ve never tried it on Windows + Gradle + Jetty, can’t help you there.

                    1. I have installed jdk 1.8 ver 71, and now I get “next step error”

                      [HttpClient@1681595665-17] WARN org.eclipse.jetty.http2.HTTP2Session – NOT IMPLEMENTED max header list size to 8000

                    2. That warning is expected, the message should go through though :)

          2. I Have the same issue even with 9.3.7 version.

  2. Thanks a lot for the sample.
    Have you managed to make the connection through an authenticated proxy (like squid)?

    I’m trying with the following code:

    HttpProxy httpProxy = new HttpProxy(“localhost”, 3128);
    ProxyConfiguration proxyConfig = client.getProxyConfiguration();
    proxyConfig.getProxies().add(httpProxy);

    But the ALPN negotiation fails with a NullPointerException.

    1. I haven’t tried it with a proxy as such.