Skip to content

Commit

Permalink
Merge pull request #440 from relayrides/netty-4_1_9
Browse files Browse the repository at this point in the history
Update to Netty 4.1.9/netty-tcnative 2.0.0
  • Loading branch information
jchambers authored Apr 3, 2017
2 parents 9ba020c + 7d782a4 commit cd2c9af
Show file tree
Hide file tree
Showing 7 changed files with 23 additions and 110 deletions.
40 changes: 4 additions & 36 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,13 @@ If you use [Maven](http://maven.apache.org/), you can add Pushy to your project
</dependency>
```

It's very important to note that you will also need to have either `netty-tcnative` (1.1.33.Fork24 or newer) or `alpn-boot`, as discussed in the [system requirements](#system-requirements) section below.

If you don't use Maven (or something else that understands Maven dependencies, like Gradle), you can [download Pushy as a `.jar` file](https://github.com/relayrides/pushy/releases/download/pushy-0.9.2/pushy-0.9.2.jar) and add it to your project directly. You'll also need to make sure you have Pushy's runtime dependencies on your classpath. They are:

- [netty 4.1.6](http://netty.io/)
- [netty 4.1.9](http://netty.io/)
- [netty-tcnative-2.0.0.Final](http://netty.io/wiki/forked-tomcat-native.html)
- [gson 2.6](https://github.com/google/gson)
- [slf4j 1.7.6](http://www.slf4j.org/) (and possibly an SLF4J binding, as described in the [logging](#logging) section below)
- [alpn-api](http://www.eclipse.org/jetty/documentation/current/alpn-chapter.html) if you've opted to use a native SSL provider (`alpn-api` is included in `alpn-boot`); please see the [system requirements](#system-requirements) section for details)
- [alpn-api](http://www.eclipse.org/jetty/documentation/current/alpn-chapter.html)

Pushy itself requires Java 7 or newer to build and run.

Expand Down Expand Up @@ -148,38 +147,7 @@ When shutting down, clients will wait for all sent-but-not-acknowledged notifica

## System requirements

Pushy works with Java 7 and newer, but has some additional dependencies depending on the environment in which it is running.

The APNs protocol is built on top of the [HTTP/2 protocol](https://http2.github.io/). HTTP/2 is a relatively new protocol, and relies on some new developments that aren't yet wide-spread in the Java world. In particular:

1. HTTP/2 depends on [ALPN](https://tools.ietf.org/html/rfc7301), a TLS extension for protocol negotiation. No version of Java has native ALPN support at this time. The ALPN requirement may be met either by [using a native SSL provider](#using-a-native-ssl-provider) or by [using Jetty's ALPN implementation](#using-jettys-alpn-implementation) under Java 7 or 8.
2. The HTTP/2 specification requires the use of [ciphers](https://httpwg.github.io/specs/rfc7540.html#rfc.section.9.2.2) that weren't introduced in Java until Java 8. Using a native SSL provider is the best way to meet this requirement under Java 7. A native SSL provider isn't a requirement under Java 8, but may still yield performance gains.

Generally speaking, a native SSL provider is the best way to fulfill the system requirements imposed by HTTP/2 because installation is fairly straightforward, it works for Java 7 onward and generally offers better SSL performance than the JDK SSL provider.

### Using a native SSL provider

Using a native SSL provider (like [OpenSSL](https://www.openssl.org/), [BoringSSL](https://boringssl.googlesource.com/boringssl/), or [LibreSSL](http://www.libressl.org/)) via `netty-tcnative` fulfills the ALPN and cipher suite requirements imposed by HTTP/2 under all supported versions of Java. To use a native SSL provider, you'll need to add `netty-tcnative` as a dependency to your project. The `netty-tcnative` wiki provides [detailed instructions](http://netty.io/wiki/forked-tomcat-native.html), but in short, you'll need to add one additional platform-specific dependency to your project; we recommend using a statically-linked "uber jar" flavor for supported operating systems/CPU architectures (currently `linux-x86_64`, `osx-x86_64`, and `windows-x86_64`). This approach will meet all requirements imposed by HTTP/2 under Java 7 and 8.

To add the netty-tcnative uber-jar, you'll just need to add the following dependency (if you're using Maven):

```xml
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-tcnative-boringssl-static</artifactId>
<version>1.1.33.Fork24</version>
</dependency>
```

Otherwise, you may add the jar to your classpath by the means of your choice.

Please note that Pushy requires netty-tcnative 1.1.33.Fork24 or newer. Additionally, you'll need [`alpn-api`](http://mvnrepository.com/artifact/org.eclipse.jetty.alpn/alpn-api) as a `runtime` dependency for your project. If you're managing dependencies manually, you'll just need to make sure the latest version of `alpn-api` is available on your classpath.

### Using Jetty's ALPN implementation

As an alternative to a native SSL provider, Jetty's ALPN implementation. Please note that if you're not using Oracle JDK 8 or newer (or if you're using a JDK other than Oracle's), you'll need to meet the cipher suite requirement separately; you may do so either by using a native SSL provider (which also fulfills the ALPN requirement) or by using another cryptography provider (which is beyond the scope of this document).

Using Jetty's ALPN implementation is somewhat more complicated than using a native SSL provider. You'll need to choose a version of `alpn-boot` specific to the version (down to the update!) of the JDK you're using, and then add it to your *boot* class path (note that this is *not* the same as your regular classpath). [Detailed instructions](http://www.eclipse.org/jetty/documentation/current/alpn-chapter.html) are provided by Jetty. If you choose to use the `alpn-boot` approach instead of a native SSL provider, we strongly recommend using [`jetty-alpn-agent`](https://github.com/jetty-project/jetty-alpn-agent), which will automatically choose the correct version of `alpn-boot` for your JRE.
Pushy works with Java 7 and newer. By default, it depends on `netty-tcnative` and should work "out of the box" for most users. Users who can't (or choose not to) use `netty-tcnative` will need to take extra steps to [configure a JDK SSL provider](https://github.com/relayrides/pushy/wiki/Using-a-JDK-SSL-provider).

## Metrics

Expand Down
13 changes: 6 additions & 7 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@
<artifactId>netty-handler-proxy</artifactId>
<version>${netty.version}</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-tcnative-boringssl-static</artifactId>
<version>2.0.0.Final</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
Expand Down Expand Up @@ -64,12 +69,6 @@
<version>${slf4j.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-tcnative-boringssl-static</artifactId>
<version>1.1.33.Fork24</version>
<scope>test</scope>
</dependency>
</dependencies>
</dependencyManagement>

Expand All @@ -82,7 +81,7 @@
</licenses>

<properties>
<netty.version>4.1.6.Final</netty.version>
<netty.version>4.1.9.Final</netty.version>
<slf4j.version>1.7.21</slf4j.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
Expand Down
8 changes: 4 additions & 4 deletions pushy/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@
<groupId>io.netty</groupId>
<artifactId>netty-handler-proxy</artifactId>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-tcnative-boringssl-static</artifactId>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
Expand All @@ -42,10 +46,6 @@
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-tcnative-boringssl-static</artifactId>
</dependency>
</dependencies>

<build>
Expand Down
8 changes: 1 addition & 7 deletions pushy/script/generate-certs.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,10 @@ openssl req -new -keyout single-topic-client.key -nodes -newkey rsa:2048 -subj "
openssl req -new -keyout multi-topic-client.key -nodes -newkey rsa:2048 -subj "/CN=Apple Push Services: com.relayrides.pushy/UID=com.relayrides.pushy" | \
openssl x509 -extfile ./apns-extensions.cnf -extensions apns_multi_topic_client_extensions -req -CAkey ca.key -CA ca.pem -days 36500 -set_serial $RANDOM -sha512 -out multi-topic-client.pem

# We also want one bogus (self-signed) certificate to make sure the mock server is turning away untrusted clients
openssl req -new -keyout untrusted-client.key -nodes -newkey rsa:2048 -subj "/CN=Apple Push Services: com.relayrides.pushy/UID=com.relayrides.pushy" | \
openssl x509 -extfile ./apns-extensions.cnf -extensions apns_single_topic_client_extensions -req -days 36500 -set_serial $RANDOM -signkey untrusted-client.key -out untrusted-client.pem

# For simplicity, squish everything down into PKCS#12 keystores
openssl pkcs12 -export -in server.pem -inkey server.key -out server.p12 -password pass:pushy-test
openssl pkcs12 -export -in single-topic-client.pem -inkey single-topic-client.key -out single-topic-client.p12 -password pass:pushy-test
openssl pkcs12 -export -in multi-topic-client.pem -inkey multi-topic-client.key -out multi-topic-client.p12 -password pass:pushy-test
openssl pkcs12 -export -in untrusted-client.pem -inkey untrusted-client.key -out untrusted-client.p12 -password pass:pushy-test

# We'll also want one keystore with an unprotected key to make sure no-password constructors behave correctly
openssl pkcs12 -export -in single-topic-client.pem -inkey single-topic-client.key -out single-topic-client-unprotected.p12 -nodes -password pass:pushy-test
Expand All @@ -44,6 +39,5 @@ openssl pkcs12 -export -in ca.pem -nokeys -out no-keys.p12 -password pass:pushy-
openssl ecparam -name prime256v1 -genkey -noout | openssl pkcs8 -topk8 -nocrypt -out token-auth-private-key.p8

# Clean up intermediate files
rm ca.key multi-topic-client.key single-topic-client.key untrusted-client.key
rm ca.key multi-topic-client.key single-topic-client.key
rm multiple-keys.jks
rm untrusted-client.pem
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,6 @@ public class ApnsClientBuilder {
private InputStream trustedServerCertificateInputStream;
private X509Certificate[] trustedServerCertificates;

private SslProvider preferredSslProvider;

private EventLoopGroup eventLoopGroup;

private ApnsClientMetricsListener metricsListener;
Expand Down Expand Up @@ -239,22 +237,6 @@ public ApnsClientBuilder setTrustedServerCertificateChain(final X509Certificate.
return this;
}

/**
* Sets the SSL provider to be used by the client under construction. By default, the client will use a native SSL
* provider if available and fall back to the JDK provider otherwise.
*
* @param sslProvider the SSL provider to be used by the client under construction, or {@code null} to choose a
* provider automatically
*
* @return a reference to this builder
*
* @since 0.9
*/
public ApnsClientBuilder setSslProvider(final SslProvider sslProvider) {
this.preferredSslProvider = sslProvider;
return this;
}

/**
* <p>Sets the event loop group to be used by the client under construction. If not set (or if {@code null}), the
* client will create and manage its own event loop group.</p>
Expand Down Expand Up @@ -387,21 +369,17 @@ public ApnsClient build() throws SSLException {
{
final SslProvider sslProvider;

if (this.preferredSslProvider != null) {
sslProvider = this.preferredSslProvider;
} else {
if (OpenSsl.isAvailable()) {
if (OpenSsl.isAlpnSupported()) {
log.info("Native SSL provider is available and supports ALPN; will use native provider.");
sslProvider = SslProvider.OPENSSL;
} else {
log.info("Native SSL provider is available, but does not support ALPN; will use JDK SSL provider.");
sslProvider = SslProvider.JDK;
}
if (OpenSsl.isAvailable()) {
if (OpenSsl.isAlpnSupported()) {
log.info("Native SSL provider is available and supports ALPN; will use native provider.");
sslProvider = SslProvider.OPENSSL;
} else {
log.info("Native SSL provider not available; will use JDK SSL provider.");
log.info("Native SSL provider is available, but does not support ALPN; will use JDK SSL provider.");
sslProvider = SslProvider.JDK;
}
} else {
log.info("Native SSL provider not available; will use JDK SSL provider.");
sslProvider = SslProvider.JDK;
}

final SslContextBuilder sslContextBuilder = SslContextBuilder.forClient()
Expand Down
26 changes: 0 additions & 26 deletions pushy/src/test/java/com/relayrides/pushy/apns/ApnsClientTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
import com.relayrides.pushy.apns.util.SimpleApnsPushNotification;

import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.handler.ssl.SslProvider;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;

Expand All @@ -44,7 +43,6 @@ public class ApnsClientTest {

private static final String SINGLE_TOPIC_CLIENT_KEYSTORE_FILENAME = "/single-topic-client.p12";
private static final String MULTI_TOPIC_CLIENT_KEYSTORE_FILENAME = "/multi-topic-client.p12";
private static final String UNTRUSTED_CLIENT_KEYSTORE_FILENAME = "/untrusted-client.p12";

private static final String CA_CERTIFICATE_FILENAME = "/ca.pem";
private static final String SERVER_KEYSTORE = "/server.p12";
Expand All @@ -69,8 +67,6 @@ public class ApnsClientTest {
private ApnsClient tlsAuthenticationClient;
private ApnsClient tokenAuthenticationClient;

private SslProvider preferredSslProvider;

private static class TestMetricsListener implements ApnsClientMetricsListener {

private final List<Long> writeFailures = new ArrayList<>();
Expand Down Expand Up @@ -227,21 +223,17 @@ public void setUp() throws Exception {

this.server.start(PORT).await();

this.preferredSslProvider = "jdk".equals(System.getenv("PUSHY_SSL_PROVIDER")) ? SslProvider.JDK : null;

try (final InputStream p12InputStream = ApnsClientTest.class.getResourceAsStream(SINGLE_TOPIC_CLIENT_KEYSTORE_FILENAME)) {
this.tlsAuthenticationClient = new ApnsClientBuilder()
.setClientCredentials(p12InputStream, KEYSTORE_PASSWORD)
.setTrustedServerCertificateChain(CA_CERTIFICATE)
.setEventLoopGroup(EVENT_LOOP_GROUP)
.setSslProvider(this.preferredSslProvider)
.build();
}

this.tokenAuthenticationClient = new ApnsClientBuilder()
.setTrustedServerCertificateChain(CA_CERTIFICATE)
.setEventLoopGroup(EVENT_LOOP_GROUP)
.setSslProvider(this.preferredSslProvider)
.build();

this.tlsAuthenticationClient.connect(HOST, PORT).await();
Expand Down Expand Up @@ -366,24 +358,6 @@ public void testGetReconnectionFutureWhenNotConnected() throws Exception {
assertFalse(reconnectionFuture.isSuccess());
}

@Test
public void testConnectWithUntrustedCertificate() throws Exception {
final ApnsClient untrustedClient;

try (final InputStream p12InputStream = ApnsClientTest.class.getResourceAsStream(UNTRUSTED_CLIENT_KEYSTORE_FILENAME)) {
untrustedClient = new ApnsClientBuilder()
.setClientCredentials(p12InputStream, KEYSTORE_PASSWORD)
.setTrustedServerCertificateChain(CA_CERTIFICATE)
.setEventLoopGroup(EVENT_LOOP_GROUP)
.build();
}

final Future<Void> connectFuture = untrustedClient.connect(HOST, PORT).await();
assertFalse(connectFuture.isSuccess());

untrustedClient.disconnect().await();
}

@Test(expected = IllegalStateException.class)
public void testRegisterSigningKeyWithTlsAuthentication() throws Exception {
this.tlsAuthenticationClient.registerSigningKey((ECPrivateKey) KeyPairUtil.generateKeyPair().getPrivate(), "team-id", "key-id", "topic");
Expand Down
Binary file removed pushy/src/test/resources/untrusted-client.p12
Binary file not shown.

0 comments on commit cd2c9af

Please sign in to comment.