entry : channelClassesByIoHandlerClass.entrySet()) {
+ try {
+ final Class extends IoHandler> ioHandlerClass =
+ Class.forName(entry.getKey()).asSubclass(IoHandler.class);
- try {
- return Class.forName(datagramChannelClassName).asSubclass(DatagramChannel.class);
- } catch (final ClassNotFoundException e) {
- throw new IllegalArgumentException(e);
+ if (ioEventLoopGroup.isIoType(ioHandlerClass)) {
+ return Class.forName(entry.getValue()).asSubclass(channelType);
+ }
+ } catch (final ClassNotFoundException e) {
+ continue;
+ }
}
+
+ throw new IllegalArgumentException("No suitable channel class found for event loop group");
}
}
diff --git a/pushy/src/main/java/com/eatthepath/pushy/apns/auth/ApnsKey.java b/pushy/src/main/java/com/eatthepath/pushy/apns/auth/ApnsKey.java
index 8b1f09905..20e4a5063 100644
--- a/pushy/src/main/java/com/eatthepath/pushy/apns/auth/ApnsKey.java
+++ b/pushy/src/main/java/com/eatthepath/pushy/apns/auth/ApnsKey.java
@@ -101,19 +101,4 @@ protected ECKey getKey() {
public ECParameterSpec getParams() {
return this.key.getParams();
}
-
- protected static byte[] decodeBase64EncodedString(final String base64EncodedString) {
- final ByteBuf base64EncodedByteBuf =
- Unpooled.wrappedBuffer(base64EncodedString.getBytes(StandardCharsets.US_ASCII));
-
- final ByteBuf decodedByteBuf = Base64.decode(base64EncodedByteBuf);
- final byte[] decodedBytes = new byte[decodedByteBuf.readableBytes()];
-
- decodedByteBuf.readBytes(decodedBytes);
-
- base64EncodedByteBuf.release();
- decodedByteBuf.release();
-
- return decodedBytes;
- }
}
diff --git a/pushy/src/main/java/com/eatthepath/pushy/apns/auth/ApnsSigningKey.java b/pushy/src/main/java/com/eatthepath/pushy/apns/auth/ApnsSigningKey.java
index 9beb1514b..790121199 100644
--- a/pushy/src/main/java/com/eatthepath/pushy/apns/auth/ApnsSigningKey.java
+++ b/pushy/src/main/java/com/eatthepath/pushy/apns/auth/ApnsSigningKey.java
@@ -31,6 +31,7 @@
import java.security.interfaces.ECPrivateKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
+import java.util.Base64;
/**
* A private key used to sign authentication tokens. Signing keys are associated with a developer team (in Apple's
@@ -154,7 +155,7 @@ public static ApnsSigningKey loadFromInputStream(final InputStream inputStream,
base64EncodedPrivateKey = privateKeyBuilder.toString();
}
- final byte[] keyBytes = decodeBase64EncodedString(base64EncodedPrivateKey);
+ final byte[] keyBytes = Base64.getDecoder().decode(base64EncodedPrivateKey);
final PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
final KeyFactory keyFactory = KeyFactory.getInstance("EC");
diff --git a/pushy/src/main/java/com/eatthepath/pushy/apns/auth/ApnsVerificationKey.java b/pushy/src/main/java/com/eatthepath/pushy/apns/auth/ApnsVerificationKey.java
index 2e69131e9..60a10b8a0 100644
--- a/pushy/src/main/java/com/eatthepath/pushy/apns/auth/ApnsVerificationKey.java
+++ b/pushy/src/main/java/com/eatthepath/pushy/apns/auth/ApnsVerificationKey.java
@@ -33,6 +33,7 @@
import java.security.spec.ECPoint;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
+import java.util.Base64;
/**
* A public key used to verify authentication tokens. Signing keys are associated with a developer team (in Apple's
@@ -161,7 +162,7 @@ public static ApnsVerificationKey loadFromInputStream(final InputStream inputStr
base64EncodedPublicKey = publicKeyBuilder.toString();
}
- final byte[] keyBytes = decodeBase64EncodedString(base64EncodedPublicKey);
+ final byte[] keyBytes = Base64.getDecoder().decode(base64EncodedPublicKey);
final X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
final KeyFactory keyFactory = KeyFactory.getInstance("EC");
diff --git a/pushy/src/main/java/com/eatthepath/pushy/apns/server/BaseHttp2Server.java b/pushy/src/main/java/com/eatthepath/pushy/apns/server/BaseHttp2Server.java
index e009aa5eb..916d3a3dc 100644
--- a/pushy/src/main/java/com/eatthepath/pushy/apns/server/BaseHttp2Server.java
+++ b/pushy/src/main/java/com/eatthepath/pushy/apns/server/BaseHttp2Server.java
@@ -26,7 +26,7 @@
import io.netty.channel.*;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
-import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.nio.NioIoHandler;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslHandler;
@@ -76,13 +76,13 @@ public void exceptionCaught(final ChannelHandlerContext context, final Throwable
this.bootstrap.group(eventLoopGroup);
this.shouldShutDownEventLoopGroup = false;
} else {
- this.bootstrap.group(new NioEventLoopGroup(1));
+ this.bootstrap.group(new MultiThreadIoEventLoopGroup(NioIoHandler.newFactory()));
this.shouldShutDownEventLoopGroup = true;
}
this.allChannels = new DefaultChannelGroup(this.bootstrap.config().group().next());
- this.bootstrap.channel(ServerChannelClassUtil.getServerSocketChannelClass(this.bootstrap.config().group()));
+ this.bootstrap.channel(ServerChannelClassUtil.getServerSocketChannelClass((IoEventLoopGroup) this.bootstrap.config().group()));
this.bootstrap.childHandler(new ChannelInitializer() {
@Override
diff --git a/pushy/src/main/java/com/eatthepath/pushy/apns/server/BaseHttp2ServerBuilder.java b/pushy/src/main/java/com/eatthepath/pushy/apns/server/BaseHttp2ServerBuilder.java
index 05eea2f18..9958d9ed9 100644
--- a/pushy/src/main/java/com/eatthepath/pushy/apns/server/BaseHttp2ServerBuilder.java
+++ b/pushy/src/main/java/com/eatthepath/pushy/apns/server/BaseHttp2ServerBuilder.java
@@ -22,7 +22,7 @@
package com.eatthepath.pushy.apns.server;
-import io.netty.channel.EventLoopGroup;
+import io.netty.channel.IoEventLoopGroup;
import io.netty.handler.codec.http2.Http2SecurityUtil;
import io.netty.handler.ssl.*;
import io.netty.util.ReferenceCounted;
@@ -53,7 +53,7 @@ abstract class BaseHttp2ServerBuilder {
protected InputStream trustedClientCertificateInputStream;
protected X509Certificate[] trustedClientCertificates;
- protected EventLoopGroup eventLoopGroup;
+ protected IoEventLoopGroup ioEventLoopGroup;
protected int maxConcurrentStreams = DEFAULT_MAX_CONCURRENT_STREAMS;
@@ -219,7 +219,7 @@ public BaseHttp2ServerBuilder setTrustedClientCertificateChain(final InputStr
*
* @return a reference to this builder
*/
- public BaseHttp2ServerBuilder setTrustedServerCertificateChain(final X509Certificate... certificates) {
+ public BaseHttp2ServerBuilder setTrustedClientCertificateChain(final X509Certificate... certificates) {
this.trustedClientCertificatePemFile = null;
this.trustedClientCertificateInputStream = null;
this.trustedClientCertificates = certificates;
@@ -231,15 +231,15 @@ public BaseHttp2ServerBuilder setTrustedServerCertificateChain(final X509Cert
* Sets the event loop group to be used by the server under construction. If not set (or if {@code null}), the
* server will create and manage its own event loop group.
*
- * @param eventLoopGroup the event loop group to use for this server, or {@code null} to let the server manage its
+ * @param ioEventLoopGroup the event loop group to use for this server, or {@code null} to let the server manage its
* own event loop group
*
* @return a reference to this builder
*
* @since 0.8
*/
- public BaseHttp2ServerBuilder setEventLoopGroup(final EventLoopGroup eventLoopGroup) {
- this.eventLoopGroup = eventLoopGroup;
+ public BaseHttp2ServerBuilder setIoEventLoopGroup(final IoEventLoopGroup ioEventLoopGroup) {
+ this.ioEventLoopGroup = ioEventLoopGroup;
return this;
}
diff --git a/pushy/src/main/java/com/eatthepath/pushy/apns/server/BenchmarkApnsServer.java b/pushy/src/main/java/com/eatthepath/pushy/apns/server/BenchmarkApnsServer.java
index 2b609b996..4d26d75b6 100644
--- a/pushy/src/main/java/com/eatthepath/pushy/apns/server/BenchmarkApnsServer.java
+++ b/pushy/src/main/java/com/eatthepath/pushy/apns/server/BenchmarkApnsServer.java
@@ -23,7 +23,7 @@
package com.eatthepath.pushy.apns.server;
import io.netty.channel.ChannelPipeline;
-import io.netty.channel.EventLoopGroup;
+import io.netty.channel.IoEventLoopGroup;
import io.netty.handler.codec.http2.Http2Settings;
import io.netty.handler.ssl.SslContext;
@@ -42,8 +42,8 @@ public class BenchmarkApnsServer extends BaseHttp2Server {
private final int maxConcurrentStreams;
- BenchmarkApnsServer(final SslContext sslContext, final EventLoopGroup eventLoopGroup, final int maxConcurrentStreams) {
- super(sslContext, eventLoopGroup);
+ BenchmarkApnsServer(final SslContext sslContext, final IoEventLoopGroup ioEventLoopGroup, final int maxConcurrentStreams) {
+ super(sslContext, ioEventLoopGroup);
this.maxConcurrentStreams = maxConcurrentStreams;
}
diff --git a/pushy/src/main/java/com/eatthepath/pushy/apns/server/BenchmarkApnsServerBuilder.java b/pushy/src/main/java/com/eatthepath/pushy/apns/server/BenchmarkApnsServerBuilder.java
index f506391f2..87eb8e51c 100644
--- a/pushy/src/main/java/com/eatthepath/pushy/apns/server/BenchmarkApnsServerBuilder.java
+++ b/pushy/src/main/java/com/eatthepath/pushy/apns/server/BenchmarkApnsServerBuilder.java
@@ -22,7 +22,7 @@
package com.eatthepath.pushy.apns.server;
-import io.netty.channel.EventLoopGroup;
+import io.netty.channel.IoEventLoopGroup;
import io.netty.handler.ssl.SslContext;
import javax.net.ssl.SSLException;
@@ -82,14 +82,14 @@ public BenchmarkApnsServerBuilder setTrustedClientCertificateChain(final InputSt
}
@Override
- public BenchmarkApnsServerBuilder setTrustedServerCertificateChain(final X509Certificate... certificates) {
- super.setTrustedServerCertificateChain(certificates);
+ public BenchmarkApnsServerBuilder setTrustedClientCertificateChain(final X509Certificate... certificates) {
+ super.setTrustedClientCertificateChain(certificates);
return this;
}
@Override
- public BenchmarkApnsServerBuilder setEventLoopGroup(final EventLoopGroup eventLoopGroup) {
- super.setEventLoopGroup(eventLoopGroup);
+ public BenchmarkApnsServerBuilder setIoEventLoopGroup(final IoEventLoopGroup ioEventLoopGroup) {
+ super.setIoEventLoopGroup(ioEventLoopGroup);
return this;
}
@@ -112,6 +112,6 @@ public BenchmarkApnsServer build() throws SSLException {
@Override
protected BenchmarkApnsServer constructServer(final SslContext sslContext) {
- return new BenchmarkApnsServer(sslContext, this.eventLoopGroup, this.maxConcurrentStreams);
+ return new BenchmarkApnsServer(sslContext, this.ioEventLoopGroup, this.maxConcurrentStreams);
}
}
diff --git a/pushy/src/main/java/com/eatthepath/pushy/apns/server/MockApnsServer.java b/pushy/src/main/java/com/eatthepath/pushy/apns/server/MockApnsServer.java
index f192a2cb5..f5ac70fb5 100644
--- a/pushy/src/main/java/com/eatthepath/pushy/apns/server/MockApnsServer.java
+++ b/pushy/src/main/java/com/eatthepath/pushy/apns/server/MockApnsServer.java
@@ -23,7 +23,7 @@
package com.eatthepath.pushy.apns.server;
import io.netty.channel.ChannelPipeline;
-import io.netty.channel.EventLoopGroup;
+import io.netty.channel.IoEventLoopGroup;
import io.netty.handler.codec.http2.Http2Settings;
import io.netty.handler.ssl.SslContext;
@@ -55,7 +55,7 @@ public class MockApnsServer extends BaseHttp2Server {
private final int maxConcurrentStreams;
private final boolean generateApnsUniqueId;
- MockApnsServer(final SslContext sslContext, final EventLoopGroup eventLoopGroup,
+ MockApnsServer(final SslContext sslContext, final IoEventLoopGroup eventLoopGroup,
final PushNotificationHandlerFactory handlerFactory, final MockApnsServerListener listener,
final int maxConcurrentStreams, boolean generateApnsUniqueId) {
diff --git a/pushy/src/main/java/com/eatthepath/pushy/apns/server/MockApnsServerBuilder.java b/pushy/src/main/java/com/eatthepath/pushy/apns/server/MockApnsServerBuilder.java
index ac24a2740..b794c99ff 100644
--- a/pushy/src/main/java/com/eatthepath/pushy/apns/server/MockApnsServerBuilder.java
+++ b/pushy/src/main/java/com/eatthepath/pushy/apns/server/MockApnsServerBuilder.java
@@ -22,7 +22,7 @@
package com.eatthepath.pushy.apns.server;
-import io.netty.channel.EventLoopGroup;
+import io.netty.channel.IoEventLoopGroup;
import io.netty.handler.ssl.SslContext;
import javax.net.ssl.SSLException;
@@ -87,14 +87,14 @@ public MockApnsServerBuilder setTrustedClientCertificateChain(final InputStream
}
@Override
- public MockApnsServerBuilder setTrustedServerCertificateChain(final X509Certificate... certificates) {
- super.setTrustedServerCertificateChain(certificates);
+ public MockApnsServerBuilder setTrustedClientCertificateChain(final X509Certificate... certificates) {
+ super.setTrustedClientCertificateChain(certificates);
return this;
}
@Override
- public MockApnsServerBuilder setEventLoopGroup(final EventLoopGroup eventLoopGroup) {
- super.setEventLoopGroup(eventLoopGroup);
+ public MockApnsServerBuilder setIoEventLoopGroup(final IoEventLoopGroup ioEventLoopGroup) {
+ super.setIoEventLoopGroup(ioEventLoopGroup);
return this;
}
@@ -156,6 +156,6 @@ protected MockApnsServer constructServer(final SslContext sslContext) {
throw new IllegalStateException("Must provide a push notification handler factory before building a mock server.");
}
- return new MockApnsServer(sslContext, this.eventLoopGroup, this.handlerFactory, this.listener, this.maxConcurrentStreams, generateApnsUniqueId);
+ return new MockApnsServer(sslContext, this.ioEventLoopGroup, this.handlerFactory, this.listener, this.maxConcurrentStreams, generateApnsUniqueId);
}
}
diff --git a/pushy/src/main/java/com/eatthepath/pushy/apns/server/ServerChannelClassUtil.java b/pushy/src/main/java/com/eatthepath/pushy/apns/server/ServerChannelClassUtil.java
index a43a1c2be..27e96ae74 100644
--- a/pushy/src/main/java/com/eatthepath/pushy/apns/server/ServerChannelClassUtil.java
+++ b/pushy/src/main/java/com/eatthepath/pushy/apns/server/ServerChannelClassUtil.java
@@ -22,8 +22,10 @@
package com.eatthepath.pushy.apns.server;
-import io.netty.channel.EventLoopGroup;
+import io.netty.channel.IoEventLoopGroup;
+import io.netty.channel.IoHandler;
import io.netty.channel.ServerChannel;
+import io.netty.channel.socket.ServerSocketChannel;
import java.util.HashMap;
import java.util.Map;
@@ -34,34 +36,40 @@ class ServerChannelClassUtil {
private static final Map SERVER_SOCKET_CHANNEL_CLASSES = new HashMap<>();
static {
- SERVER_SOCKET_CHANNEL_CLASSES.put("io.netty.channel.nio.NioEventLoopGroup", "io.netty.channel.socket.nio.NioServerSocketChannel");
- SERVER_SOCKET_CHANNEL_CLASSES.put("io.netty.channel.epoll.EpollEventLoopGroup", "io.netty.channel.epoll.EpollServerSocketChannel");
- SERVER_SOCKET_CHANNEL_CLASSES.put("io.netty.channel.kqueue.KQueueEventLoopGroup", "io.netty.channel.kqueue.KQueueServerSocketChannel");
+ SERVER_SOCKET_CHANNEL_CLASSES.put("io.netty.channel.nio.NioIoHandler", "io.netty.channel.socket.nio.NioServerSocketChannel");
+ SERVER_SOCKET_CHANNEL_CLASSES.put("io.netty.channel.uring.IoUringIoHandler", "io.netty.channel.uring.IoUringServerSocketChannel");
+ SERVER_SOCKET_CHANNEL_CLASSES.put("io.netty.channel.epoll.EpollIoHandler", "io.netty.channel.epoll.EpollServerSocketChannel");
+ SERVER_SOCKET_CHANNEL_CLASSES.put("io.netty.channel.kqueue.KQueueIoHandler", "io.netty.channel.kqueue.KQueueServerSocketChannel");
}
/**
* Returns a server socket channel class suitable for specified event loop group.
*
- * @param eventLoopGroup the event loop group for which to identify an appropriate socket channel class; must not
+ * @param ioEventLoopGroup the event loop group for which to identify an appropriate socket channel class; must not
* be {@code null}
*
* @return a server socket channel class suitable for use with the given event loop group
*
- * @throws IllegalArgumentException in case of null or unrecognized event loop group
+ * @throws IllegalArgumentException if no suitable server socket channel class could be found for the given event
+ * loop group
+ * @throws NullPointerException if the given {@code ioEventLoopGroup} was {@code null}
*/
- static Class extends ServerChannel> getServerSocketChannelClass(final EventLoopGroup eventLoopGroup) {
- Objects.requireNonNull(eventLoopGroup);
+ static Class extends ServerChannel> getServerSocketChannelClass(final IoEventLoopGroup ioEventLoopGroup) {
+ Objects.requireNonNull(ioEventLoopGroup);
- final String serverSocketChannelClassName = SERVER_SOCKET_CHANNEL_CLASSES.get(eventLoopGroup.getClass().getName());
+ for (final Map.Entry entry : SERVER_SOCKET_CHANNEL_CLASSES.entrySet()) {
+ try {
+ final Class extends IoHandler> ioHandlerClass =
+ Class.forName(entry.getKey()).asSubclass(IoHandler.class);
- if (serverSocketChannelClassName == null) {
- throw new IllegalArgumentException("No server socket channel class found for event loop group type: " + eventLoopGroup.getClass().getName());
+ if (ioEventLoopGroup.isIoType(ioHandlerClass)) {
+ return Class.forName(entry.getValue()).asSubclass(ServerSocketChannel.class);
+ }
+ } catch (final ClassNotFoundException e) {
+ continue;
+ }
}
- try {
- return Class.forName(serverSocketChannelClassName).asSubclass(ServerChannel.class);
- } catch (final ClassNotFoundException e) {
- throw new IllegalArgumentException(e);
- }
+ throw new IllegalArgumentException("No suitable channel class found for event loop group");
}
}
diff --git a/pushy/src/main/resources/.gitignore b/pushy/src/main/resources/.gitignore
deleted file mode 100644
index e69de29bb..000000000
diff --git a/pushy/src/test/java/com/eatthepath/ApnsTestCertificates.java b/pushy/src/test/java/com/eatthepath/ApnsTestCertificates.java
new file mode 100644
index 000000000..3bdff955f
--- /dev/null
+++ b/pushy/src/test/java/com/eatthepath/ApnsTestCertificates.java
@@ -0,0 +1,90 @@
+package com.eatthepath;
+
+import io.netty.pkitesting.CertificateBuilder;
+import io.netty.pkitesting.X509Bundle;
+
+import java.time.Duration;
+import java.time.Instant;
+import java.util.Base64;
+
+public class ApnsTestCertificates {
+
+ private final X509Bundle caBundle;
+ private final X509Bundle trustedServerCertificateBundle;
+ private final X509Bundle untrustedServerCertificateBundle;
+ private final X509Bundle singleTopicClientCertificateBundle;
+ private final X509Bundle multiTopicClientCertificateBundle;
+
+ // This is the value OpenSSL encodes for `ASN1:SEQUENCE:apns_topics`, where `apns_topics` is defined as:
+ //
+ // [ apns_topics ]
+ // aps_topics.0 = UTF8String:com.eatthepath.pushy
+ // aps_topics.1 = SEQWRAP,UTF8String:app
+ // aps_topics.2 = UTF8String:com.eatthepath.pushy.voip
+ // aps_topics.3 = SEQWRAP,UTF8String:voip
+ // aps_topics.4 = UTF8String:com.eatthepath.pushy.complication
+ // aps_topics.5 = SEQWRAP,UTF8String:complication
+ private static final byte[] MULTI_TOPIC_EXTENSION_VALUE = Base64.getDecoder()
+ .decode("BHUwcwwUY29tLmVhdHRoZXBhdGgucHVzaHkwBQwDYXBwDBljb20uZWF0dGhlcGF0aC5wdXNoeS52b2lwMAYMBHZvaXAMIWNvbS5lYXR0aGVwYXRoLnB1c2h5LmNvbXBsaWNhdGlvbjAODAxjb21wbGljYXRpb24=");
+
+ public ApnsTestCertificates() throws Exception {
+ final Instant now = Instant.now();
+
+ final CertificateBuilder rootCertificateBuilderTemplate = new CertificateBuilder()
+ .notBefore(now)
+ .notAfter(now.plus(Duration.ofHours(1)));
+
+ caBundle = rootCertificateBuilderTemplate.copy()
+ .subject("CN=PushyTestRoot")
+ .setKeyUsage(true, CertificateBuilder.KeyUsage.digitalSignature, CertificateBuilder.KeyUsage.keyCertSign)
+ .setIsCertificateAuthority(true)
+ .buildSelfSigned();
+
+ final CertificateBuilder serverCertificateBuilderTemplate = rootCertificateBuilderTemplate.copy()
+ .subject("CN=com.eatthepath.pushy")
+ .setKeyUsage(true, CertificateBuilder.KeyUsage.digitalSignature, CertificateBuilder.KeyUsage.keyEncipherment)
+ .addExtendedKeyUsage(CertificateBuilder.ExtendedKeyUsage.PKIX_KP_CLIENT_AUTH)
+ .addExtendedKeyUsage(CertificateBuilder.ExtendedKeyUsage.PKIX_KP_SERVER_AUTH)
+ .setIsCertificateAuthority(false);
+
+ trustedServerCertificateBundle = serverCertificateBuilderTemplate.copy()
+ .addSanDnsName("localhost")
+ .buildIssuedBy(caBundle);
+
+ untrustedServerCertificateBundle = serverCertificateBuilderTemplate.copy()
+ .buildIssuedBy(caBundle);
+
+ final CertificateBuilder clientCertificateBuilderTemplate = rootCertificateBuilderTemplate.copy()
+ .subject("CN=Apple Push Services: com.eatthepath.pushy, UID=com.eatthepath.pushy")
+ .setKeyUsage(true, CertificateBuilder.KeyUsage.digitalSignature)
+ .addExtendedKeyUsage(CertificateBuilder.ExtendedKeyUsage.PKIX_KP_CLIENT_AUTH)
+ .setIsCertificateAuthority(false);
+
+ singleTopicClientCertificateBundle = clientCertificateBuilderTemplate.copy()
+ .buildIssuedBy(caBundle);
+
+ multiTopicClientCertificateBundle = clientCertificateBuilderTemplate.copy()
+ .addExtensionOctetString("1.2.840.113635.100.6.3.6", false, MULTI_TOPIC_EXTENSION_VALUE)
+ .buildIssuedBy(caBundle);
+ }
+
+ public X509Bundle getCaBundle() {
+ return caBundle;
+ }
+
+ public X509Bundle getTrustedServerCertificateBundle() {
+ return trustedServerCertificateBundle;
+ }
+
+ public X509Bundle getUntrustedServerCertificateBundle() {
+ return untrustedServerCertificateBundle;
+ }
+
+ public X509Bundle getSingleTopicClientCertificateBundle() {
+ return singleTopicClientCertificateBundle;
+ }
+
+ public X509Bundle getMultiTopicClientCertificateBundle() {
+ return multiTopicClientCertificateBundle;
+ }
+}
diff --git a/pushy/src/test/java/com/eatthepath/pushy/apns/AbstractClientServerTest.java b/pushy/src/test/java/com/eatthepath/pushy/apns/AbstractClientServerTest.java
index 69e78639b..3af7875b5 100644
--- a/pushy/src/test/java/com/eatthepath/pushy/apns/AbstractClientServerTest.java
+++ b/pushy/src/test/java/com/eatthepath/pushy/apns/AbstractClientServerTest.java
@@ -22,12 +22,15 @@
package com.eatthepath.pushy.apns;
+import com.eatthepath.ApnsTestCertificates;
import com.eatthepath.pushy.apns.auth.ApnsSigningKey;
import com.eatthepath.pushy.apns.auth.ApnsVerificationKey;
import com.eatthepath.pushy.apns.auth.KeyPairUtil;
import com.eatthepath.pushy.apns.server.*;
import com.eatthepath.pushy.apns.util.ApnsPayloadBuilder;
-import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.IoEventLoopGroup;
+import io.netty.channel.MultiThreadIoEventLoopGroup;
+import io.netty.channel.nio.NioIoHandler;
import io.netty.util.concurrent.*;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
@@ -36,7 +39,6 @@
import javax.net.ssl.SSLException;
import java.io.IOException;
-import java.io.InputStream;
import java.security.KeyPair;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
@@ -47,16 +49,8 @@
public class AbstractClientServerTest {
protected static ApnsClientResources CLIENT_RESOURCES;
- protected static NioEventLoopGroup SERVER_EVENT_LOOP_GROUP;
-
- protected static final String CA_CERTIFICATE_FILENAME = "/ca.pem";
- protected static final String SERVER_CERTIFICATES_FILENAME = "/server-certs.pem";
- protected static final String SERVER_KEY_FILENAME = "/server-key.pem";
- protected static final String UNTRUSTED_HOSTNAME_SERVER_CERTIFICATES_FILENAME = "/server-certs-no-alt.pem";
- protected static final String UNTRUSTED_HOSTNAME_SERVER_KEY_FILENAME = "/server-key-no-alt.pem";
-
- protected static final String MULTI_TOPIC_CLIENT_KEYSTORE_FILENAME = "/multi-topic-client.p12";
- protected static final String KEYSTORE_PASSWORD = "pushy-test";
+ protected static IoEventLoopGroup SERVER_EVENT_LOOP_GROUP;
+ protected static ApnsTestCertificates TEST_CERTIFICATES;
protected static final String HOST = "localhost";
protected static final int PORT = 8443;
@@ -79,9 +73,11 @@ public class AbstractClientServerTest {
protected Map> topicsByVerificationKey;
@BeforeAll
- public static void setUpBeforeClass() {
- CLIENT_RESOURCES = new ApnsClientResources(new NioEventLoopGroup(2));
- SERVER_EVENT_LOOP_GROUP = new NioEventLoopGroup(2);
+ public static void setUpBeforeClass() throws Exception {
+ CLIENT_RESOURCES = new ApnsClientResources(new MultiThreadIoEventLoopGroup(2, NioIoHandler.newFactory()));
+ SERVER_EVENT_LOOP_GROUP = new MultiThreadIoEventLoopGroup(2, NioIoHandler.newFactory());
+
+ TEST_CERTIFICATES = new ApnsTestCertificates();
}
@BeforeEach
@@ -111,15 +107,14 @@ protected ApnsClient buildTlsAuthenticationClient() throws IOException {
}
protected ApnsClient buildTlsAuthenticationClient(final ApnsClientMetricsListener metricsListener) throws IOException {
- try (final InputStream p12InputStream = getClass().getResourceAsStream(MULTI_TOPIC_CLIENT_KEYSTORE_FILENAME)) {
- return new ApnsClientBuilder()
- .setApnsServer(HOST, PORT)
- .setClientCredentials(p12InputStream, KEYSTORE_PASSWORD)
- .setTrustedServerCertificateChain(getClass().getResourceAsStream(CA_CERTIFICATE_FILENAME))
- .setApnsClientResources(CLIENT_RESOURCES)
- .setMetricsListener(metricsListener)
- .build();
- }
+ return new ApnsClientBuilder()
+ .setApnsServer(HOST, PORT)
+ .setClientCredentials(TEST_CERTIFICATES.getMultiTopicClientCertificateBundle().getCertificate(),
+ TEST_CERTIFICATES.getMultiTopicClientCertificateBundle().getKeyPair().getPrivate())
+ .setTrustedServerCertificateChain(TEST_CERTIFICATES.getCaBundle().getCertificate())
+ .setApnsClientResources(CLIENT_RESOURCES)
+ .setMetricsListener(metricsListener)
+ .build();
}
protected ApnsClient buildTokenAuthenticationClient() throws SSLException {
@@ -129,7 +124,7 @@ protected ApnsClient buildTokenAuthenticationClient() throws SSLException {
protected ApnsClient buildTokenAuthenticationClient(final ApnsClientMetricsListener metricsListener) throws SSLException {
return new ApnsClientBuilder()
.setApnsServer(HOST, PORT)
- .setTrustedServerCertificateChain(getClass().getResourceAsStream(CA_CERTIFICATE_FILENAME))
+ .setTrustedServerCertificateChain(TEST_CERTIFICATES.getCaBundle().getCertificate())
.setSigningKey(this.signingKey)
.setApnsClientResources(CLIENT_RESOURCES)
.setMetricsListener(metricsListener)
@@ -150,9 +145,10 @@ protected MockApnsServer buildServer(final PushNotificationHandlerFactory handle
protected MockApnsServer buildServer(final PushNotificationHandlerFactory handlerFactory, final MockApnsServerListener listener, final boolean generateApnsUniqueId) throws SSLException {
return new MockApnsServerBuilder()
- .setServerCredentials(getClass().getResourceAsStream(SERVER_CERTIFICATES_FILENAME), getClass().getResourceAsStream(SERVER_KEY_FILENAME), null)
- .setTrustedClientCertificateChain(getClass().getResourceAsStream(CA_CERTIFICATE_FILENAME))
- .setEventLoopGroup(SERVER_EVENT_LOOP_GROUP)
+ .setServerCredentials(TEST_CERTIFICATES.getTrustedServerCertificateBundle().getCertificatePathWithRoot(),
+ TEST_CERTIFICATES.getTrustedServerCertificateBundle().getKeyPair().getPrivate())
+ .setTrustedClientCertificateChain(TEST_CERTIFICATES.getCaBundle().getCertificate())
+ .setIoEventLoopGroup(SERVER_EVENT_LOOP_GROUP)
.setHandlerFactory(handlerFactory)
.setListener(listener)
.generateApnsUniqueId(generateApnsUniqueId)
diff --git a/pushy/src/test/java/com/eatthepath/pushy/apns/ApnsClientBuilderTest.java b/pushy/src/test/java/com/eatthepath/pushy/apns/ApnsClientBuilderTest.java
index 1ba7faa68..d11604f68 100644
--- a/pushy/src/test/java/com/eatthepath/pushy/apns/ApnsClientBuilderTest.java
+++ b/pushy/src/test/java/com/eatthepath/pushy/apns/ApnsClientBuilderTest.java
@@ -22,33 +22,39 @@
package com.eatthepath.pushy.apns;
+import com.eatthepath.ApnsTestCertificates;
import com.eatthepath.pushy.apns.auth.ApnsSigningKey;
-import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.MultiThreadIoEventLoopGroup;
+import io.netty.channel.nio.NioIoHandler;
+import io.netty.pkitesting.X509Bundle;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.io.InputStream;
-import java.security.KeyStore.PrivateKeyEntry;
-import java.security.cert.X509Certificate;
-
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.security.InvalidKeyException;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.security.interfaces.ECPrivateKey;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class ApnsClientBuilderTest {
- private static final String SIGNING_KEY_FILENAME = "/token-auth-private-key.p8";
-
- private static final String SINGLE_TOPIC_CLIENT_KEYSTORE_FILENAME = "/single-topic-client.p12";
- private static final String SINGLE_TOPIC_CLIENT_KEYSTORE_UNPROTECTED_FILENAME = "/single-topic-client-unprotected.p12";
-
private static final String KEYSTORE_PASSWORD = "pushy-test";
+ private static ApnsTestCertificates TEST_CERTIFICATES;
private static ApnsClientResources CLIENT_RESOURCES;
@BeforeAll
- public static void setUpBeforeClass() {
- CLIENT_RESOURCES = new ApnsClientResources(new NioEventLoopGroup(1));
+ public static void setUpBeforeClass() throws Exception {
+ TEST_CERTIFICATES = new ApnsTestCertificates();
+ CLIENT_RESOURCES = new ApnsClientResources(new MultiThreadIoEventLoopGroup(NioIoHandler.newFactory()));
}
@AfterAll
@@ -58,105 +64,95 @@ public static void tearDownAfterClass() throws Exception {
@Test
void testBuildClientWithPasswordProtectedP12File() throws Exception {
- // We're happy here as long as nothing throws an exception
- final ApnsClient client = new ApnsClientBuilder()
+ final File keystoreFile = File.createTempFile("pushy-test", ".p12");
+ keystoreFile.deleteOnExit();
+
+ try (final OutputStream keystoreOutputStream = Files.newOutputStream(keystoreFile.toPath())) {
+ TEST_CERTIFICATES.getSingleTopicClientCertificateBundle().toKeyStore(KEYSTORE_PASSWORD.toCharArray())
+ .store(keystoreOutputStream, KEYSTORE_PASSWORD.toCharArray());
+ }
+
+ final ApnsClient client = assertDoesNotThrow(() -> new ApnsClientBuilder()
.setApnsServer(ApnsClientBuilder.PRODUCTION_APNS_HOST)
.setApnsClientResources(CLIENT_RESOURCES)
- .setClientCredentials(new File(this.getClass().getResource(SINGLE_TOPIC_CLIENT_KEYSTORE_FILENAME).toURI()), KEYSTORE_PASSWORD)
- .build();
+ .setClientCredentials(keystoreFile, KEYSTORE_PASSWORD)
+ .build());
client.close().get();
}
@Test
void testBuildClientWithPasswordProtectedP12InputStream() throws Exception {
- // We're happy here as long as nothing throws an exception
- try (final InputStream p12InputStream = this.getClass().getResourceAsStream(SINGLE_TOPIC_CLIENT_KEYSTORE_FILENAME)) {
- final ApnsClient client = new ApnsClientBuilder()
+ final File keystoreFile = File.createTempFile("pushy-test", ".p12");
+ keystoreFile.deleteOnExit();
+
+ try (final OutputStream keystoreOutputStream = Files.newOutputStream(keystoreFile.toPath())) {
+ TEST_CERTIFICATES.getSingleTopicClientCertificateBundle().toKeyStore(KEYSTORE_PASSWORD.toCharArray())
+ .store(keystoreOutputStream, KEYSTORE_PASSWORD.toCharArray());
+ }
+
+ try (final InputStream p12InputStream = Files.newInputStream(keystoreFile.toPath())) {
+ final ApnsClient client = assertDoesNotThrow(() -> new ApnsClientBuilder()
.setApnsServer(ApnsClientBuilder.PRODUCTION_APNS_HOST)
.setApnsClientResources(CLIENT_RESOURCES)
.setClientCredentials(p12InputStream, KEYSTORE_PASSWORD)
- .build();
+ .build());
client.close().get();
}
}
@Test
- void testBuildClientWithNullPassword() {
+ void testBuildClientWithNullPassword() throws Exception {
+ final File keystoreFile = File.createTempFile("pushy-test", ".p12");
+ keystoreFile.deleteOnExit();
+
+ try (final OutputStream keystoreOutputStream = Files.newOutputStream(keystoreFile.toPath())) {
+ TEST_CERTIFICATES.getSingleTopicClientCertificateBundle().toKeyStore(KEYSTORE_PASSWORD.toCharArray())
+ .store(keystoreOutputStream, KEYSTORE_PASSWORD.toCharArray());
+ }
+
assertThrows(NullPointerException.class, () -> new ApnsClientBuilder()
.setApnsClientResources(CLIENT_RESOURCES)
- .setClientCredentials(new File(this.getClass().getResource(SINGLE_TOPIC_CLIENT_KEYSTORE_FILENAME).toURI()), null)
+ .setClientCredentials(keystoreFile, null)
.build());
}
- @Test
- void testBuildClientWithCertificateAndPasswordProtectedKey() throws Exception {
- // We're happy here as long as nothing throws an exception
- try (final InputStream p12InputStream = this.getClass().getResourceAsStream(SINGLE_TOPIC_CLIENT_KEYSTORE_FILENAME)) {
- final PrivateKeyEntry privateKeyEntry =
- P12Util.getFirstPrivateKeyEntryFromP12InputStream(p12InputStream, KEYSTORE_PASSWORD);
-
- final ApnsClient client = new ApnsClientBuilder()
- .setApnsServer(ApnsClientBuilder.PRODUCTION_APNS_HOST)
- .setApnsClientResources(CLIENT_RESOURCES)
- .setClientCredentials((X509Certificate) privateKeyEntry.getCertificate(), privateKeyEntry.getPrivateKey(), KEYSTORE_PASSWORD)
- .build();
-
- client.close().get();
- }
- }
-
@Test
void testBuildClientWithCertificateAndUnprotectedKeyNoPassword() throws Exception {
- // We DO need a password to unlock the keystore, but the key itself should be unprotected
- try (final InputStream p12InputStream = this.getClass().getResourceAsStream(SINGLE_TOPIC_CLIENT_KEYSTORE_UNPROTECTED_FILENAME)) {
+ final X509Bundle clientCertificates = TEST_CERTIFICATES.getSingleTopicClientCertificateBundle();
- final PrivateKeyEntry privateKeyEntry =
- P12Util.getFirstPrivateKeyEntryFromP12InputStream(p12InputStream, KEYSTORE_PASSWORD);
+ final ApnsClient client = assertDoesNotThrow(() -> new ApnsClientBuilder()
+ .setApnsServer(ApnsClientBuilder.PRODUCTION_APNS_HOST)
+ .setApnsClientResources(CLIENT_RESOURCES)
+ .setClientCredentials(clientCertificates.getCertificate(), clientCertificates.getKeyPair().getPrivate())
+ .build());
- final ApnsClient client = new ApnsClientBuilder()
- .setApnsServer(ApnsClientBuilder.PRODUCTION_APNS_HOST)
- .setApnsClientResources(CLIENT_RESOURCES)
- .setClientCredentials((X509Certificate) privateKeyEntry.getCertificate(), privateKeyEntry.getPrivateKey())
- .build();
-
- client.close().get();
- }
+ client.close().get();
}
@Test
void testBuildClientWithCertificateAndUnprotectedKey() throws Exception {
- // We DO need a password to unlock the keystore, but the key itself should be unprotected
- try (final InputStream p12InputStream = this.getClass().getResourceAsStream(SINGLE_TOPIC_CLIENT_KEYSTORE_UNPROTECTED_FILENAME)) {
+ final X509Bundle clientCertificates = TEST_CERTIFICATES.getSingleTopicClientCertificateBundle();
- final PrivateKeyEntry privateKeyEntry =
- P12Util.getFirstPrivateKeyEntryFromP12InputStream(p12InputStream, KEYSTORE_PASSWORD);
+ final ApnsClient client = assertDoesNotThrow(() -> new ApnsClientBuilder()
+ .setApnsServer(ApnsClientBuilder.PRODUCTION_APNS_HOST)
+ .setApnsClientResources(CLIENT_RESOURCES)
+ .setClientCredentials(clientCertificates.getCertificate(), clientCertificates.getKeyPair().getPrivate(), null)
+ .build());
- final ApnsClient client = new ApnsClientBuilder()
- .setApnsServer(ApnsClientBuilder.PRODUCTION_APNS_HOST)
- .setApnsClientResources(CLIENT_RESOURCES)
- .setClientCredentials((X509Certificate) privateKeyEntry.getCertificate(), privateKeyEntry.getPrivateKey(), null)
- .build();
-
- client.close().get();
- }
+ client.close().get();
}
@Test
void testBuildWithSigningKey() throws Exception {
- try (final InputStream p8InputStream = this.getClass().getResourceAsStream(SIGNING_KEY_FILENAME)) {
- final ApnsSigningKey signingKey = ApnsSigningKey.loadFromInputStream(p8InputStream, "TEAM_ID", "KEY_ID");
-
- // We're happy here as long as nothing explodes
- final ApnsClient client = new ApnsClientBuilder()
- .setApnsServer(ApnsClientBuilder.PRODUCTION_APNS_HOST)
- .setApnsClientResources(CLIENT_RESOURCES)
- .setSigningKey(signingKey)
- .build();
+ final ApnsClient client = assertDoesNotThrow(() -> new ApnsClientBuilder()
+ .setApnsServer(ApnsClientBuilder.PRODUCTION_APNS_HOST)
+ .setApnsClientResources(CLIENT_RESOURCES)
+ .setSigningKey(generateSigningKey())
+ .build());
- client.close().get();
- }
+ client.close().get();
}
@Test
@@ -169,31 +165,31 @@ void testBuildWithoutClientCredentials() {
@Test
void testBuildWithClientCredentialsAndSigningCertificate() throws Exception {
- try (final InputStream p12InputStream = this.getClass().getResourceAsStream(SINGLE_TOPIC_CLIENT_KEYSTORE_UNPROTECTED_FILENAME)) {
-
- final PrivateKeyEntry privateKeyEntry =
- P12Util.getFirstPrivateKeyEntryFromP12InputStream(p12InputStream, KEYSTORE_PASSWORD);
-
- try (final InputStream p8InputStream = this.getClass().getResourceAsStream(SIGNING_KEY_FILENAME)) {
+ final X509Bundle clientCertificates = TEST_CERTIFICATES.getSingleTopicClientCertificateBundle();
- final ApnsSigningKey signingKey = ApnsSigningKey.loadFromInputStream(p8InputStream, "TEAM_ID", "KEY_ID");
-
- assertThrows(IllegalStateException.class, () ->
- new ApnsClientBuilder()
- .setApnsClientResources(CLIENT_RESOURCES)
- .setClientCredentials((X509Certificate) privateKeyEntry.getCertificate(), privateKeyEntry.getPrivateKey(), null)
- .setSigningKey(signingKey)
- .build());
- }
- }
+ assertThrows(IllegalStateException.class, () ->
+ new ApnsClientBuilder()
+ .setApnsClientResources(CLIENT_RESOURCES)
+ .setClientCredentials(clientCertificates.getCertificate(), clientCertificates.getKeyPair().getPrivate())
+ .setSigningKey(generateSigningKey())
+ .build());
}
@Test
void testBuildWithoutApnsServerAddress() {
+ final X509Bundle clientCertificates = TEST_CERTIFICATES.getSingleTopicClientCertificateBundle();
+
assertThrows(IllegalStateException.class, () ->
- new ApnsClientBuilder()
- .setApnsClientResources(CLIENT_RESOURCES)
- .setClientCredentials(new File(this.getClass().getResource(SINGLE_TOPIC_CLIENT_KEYSTORE_FILENAME).toURI()), KEYSTORE_PASSWORD)
- .build());
+ new ApnsClientBuilder()
+ .setApnsClientResources(CLIENT_RESOURCES)
+ .setClientCredentials(clientCertificates.getCertificate(), clientCertificates.getKeyPair().getPrivate())
+ .build());
+ }
+
+ private static ApnsSigningKey generateSigningKey() throws NoSuchAlgorithmException, InvalidKeyException {
+ final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC");
+ final KeyPair keyPair = keyPairGenerator.generateKeyPair();
+
+ return new ApnsSigningKey("KEY_ID", "TEAM_ID", (ECPrivateKey) keyPair.getPrivate());
}
}
diff --git a/pushy/src/test/java/com/eatthepath/pushy/apns/ApnsClientTest.java b/pushy/src/test/java/com/eatthepath/pushy/apns/ApnsClientTest.java
index 365d19399..8c8bd71d3 100644
--- a/pushy/src/test/java/com/eatthepath/pushy/apns/ApnsClientTest.java
+++ b/pushy/src/test/java/com/eatthepath/pushy/apns/ApnsClientTest.java
@@ -32,7 +32,6 @@
import org.junit.jupiter.params.provider.ValueSource;
import javax.net.ssl.SSLHandshakeException;
-import java.io.InputStream;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
@@ -228,13 +227,12 @@ void testSendNotificationToUntrustedServer(final boolean useTokenAuthentication)
.setApnsClientResources(CLIENT_RESOURCES)
.build();
} else {
- try (final InputStream p12InputStream = getClass().getResourceAsStream(MULTI_TOPIC_CLIENT_KEYSTORE_FILENAME)) {
- cautiousClient = new ApnsClientBuilder()
- .setApnsServer(HOST, PORT)
- .setClientCredentials(p12InputStream, KEYSTORE_PASSWORD)
- .setApnsClientResources(CLIENT_RESOURCES)
- .build();
- }
+ cautiousClient = new ApnsClientBuilder()
+ .setApnsServer(HOST, PORT)
+ .setClientCredentials(TEST_CERTIFICATES.getMultiTopicClientCertificateBundle().getCertificate(),
+ TEST_CERTIFICATES.getMultiTopicClientCertificateBundle().getKeyPair().getPrivate())
+ .setApnsClientResources(CLIENT_RESOURCES)
+ .build();
}
final MockApnsServer server = this.buildServer(new AcceptAllPushNotificationHandlerFactory());
@@ -268,9 +266,10 @@ void testSendNotificationToUntrustedServer(final boolean useTokenAuthentication)
@ValueSource(booleans = { true, false })
void testSendNotificationToServerWithUntrustedHostname(final boolean useTokenAuthentication) throws Exception {
final MockApnsServer serverWithUntrustedHostname = new MockApnsServerBuilder()
- .setServerCredentials(getClass().getResourceAsStream(UNTRUSTED_HOSTNAME_SERVER_CERTIFICATES_FILENAME), getClass().getResourceAsStream(UNTRUSTED_HOSTNAME_SERVER_KEY_FILENAME), null)
- .setTrustedClientCertificateChain(getClass().getResourceAsStream(CA_CERTIFICATE_FILENAME))
- .setEventLoopGroup(SERVER_EVENT_LOOP_GROUP)
+ .setServerCredentials(TEST_CERTIFICATES.getUntrustedServerCertificateBundle().getCertificatePathWithRoot(),
+ TEST_CERTIFICATES.getUntrustedServerCertificateBundle().getKeyPair().getPrivate())
+ .setTrustedClientCertificateChain(TEST_CERTIFICATES.getCaBundle().getCertificate())
+ .setIoEventLoopGroup(SERVER_EVENT_LOOP_GROUP)
.setHandlerFactory(new AcceptAllPushNotificationHandlerFactory())
.build();
@@ -304,15 +303,16 @@ void testSendNotificationToServerWithUntrustedHostname(final boolean useTokenAut
@Test
void testSendNotificationToServerWithUntrustedHostnameAndVerificationDisabled() throws Exception {
final MockApnsServer serverWithUntrustedHostname = new MockApnsServerBuilder()
- .setServerCredentials(getClass().getResourceAsStream(UNTRUSTED_HOSTNAME_SERVER_CERTIFICATES_FILENAME), getClass().getResourceAsStream(UNTRUSTED_HOSTNAME_SERVER_KEY_FILENAME), null)
- .setTrustedClientCertificateChain(getClass().getResourceAsStream(CA_CERTIFICATE_FILENAME))
- .setEventLoopGroup(SERVER_EVENT_LOOP_GROUP)
+ .setServerCredentials(TEST_CERTIFICATES.getUntrustedServerCertificateBundle().getCertificatePathWithRoot(),
+ TEST_CERTIFICATES.getUntrustedServerCertificateBundle().getKeyPair().getPrivate())
+ .setTrustedClientCertificateChain(TEST_CERTIFICATES.getCaBundle().getCertificate())
+ .setIoEventLoopGroup(SERVER_EVENT_LOOP_GROUP)
.setHandlerFactory(new AcceptAllPushNotificationHandlerFactory())
.build();
final ApnsClient client = new ApnsClientBuilder()
.setApnsServer(HOST, PORT)
- .setTrustedServerCertificateChain(getClass().getResourceAsStream(CA_CERTIFICATE_FILENAME))
+ .setTrustedServerCertificateChain(TEST_CERTIFICATES.getCaBundle().getCertificate())
.setSigningKey(this.signingKey)
.setApnsClientResources(CLIENT_RESOURCES)
.setHostnameVerificationEnabled(false)
diff --git a/pushy/src/test/java/com/eatthepath/pushy/apns/ClientChannelClassUtilTest.java b/pushy/src/test/java/com/eatthepath/pushy/apns/ClientChannelClassUtilTest.java
index 5f4830eb2..1bc667a95 100644
--- a/pushy/src/test/java/com/eatthepath/pushy/apns/ClientChannelClassUtilTest.java
+++ b/pushy/src/test/java/com/eatthepath/pushy/apns/ClientChannelClassUtilTest.java
@@ -22,107 +22,168 @@
package com.eatthepath.pushy.apns;
-import io.netty.channel.epoll.Epoll;
-import io.netty.channel.epoll.EpollDatagramChannel;
-import io.netty.channel.epoll.EpollEventLoopGroup;
-import io.netty.channel.epoll.EpollSocketChannel;
-import io.netty.channel.kqueue.KQueue;
-import io.netty.channel.kqueue.KQueueDatagramChannel;
-import io.netty.channel.kqueue.KQueueEventLoopGroup;
-import io.netty.channel.kqueue.KQueueSocketChannel;
+import io.netty.channel.IoEventLoopGroup;
+import io.netty.channel.MultiThreadIoEventLoopGroup;
+import io.netty.channel.epoll.*;
+import io.netty.channel.kqueue.*;
import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.nio.NioIoHandler;
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
-import org.junit.jupiter.api.Test;
+import io.netty.channel.uring.IoUring;
+import io.netty.channel.uring.IoUringDatagramChannel;
+import io.netty.channel.uring.IoUringIoHandler;
+import io.netty.channel.uring.IoUringSocketChannel;
+import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assumptions.assumeTrue;
public class ClientChannelClassUtilTest {
- @Test
- void testGetCoreSocketChannelClass() {
- final NioEventLoopGroup nioEventLoopGroup = new NioEventLoopGroup(1);
+ @Nested
+ class NioTransport {
- try {
- assertEquals(NioSocketChannel.class, ClientChannelClassUtil.getSocketChannelClass(nioEventLoopGroup));
- } finally {
- nioEventLoopGroup.shutdownGracefully();
- }
- }
+ private IoEventLoopGroup ioEventLoopGroup;
+ private NioEventLoopGroup legacyEventLoopGroup;
- @Test
- void testGetKqueueSocketChannelClass() {
- final String unavailabilityMessage =
- KQueue.unavailabilityCause() != null ? KQueue.unavailabilityCause().getMessage() : null;
+ @BeforeEach
+ void setUp() {
+ ioEventLoopGroup = new MultiThreadIoEventLoopGroup(NioIoHandler.newFactory());
+ legacyEventLoopGroup = new NioEventLoopGroup();
+ }
- assumeTrue(KQueue.isAvailable(), "KQueue not available: " + unavailabilityMessage);
+ @Test
+ void getSocketChannelClass() {
+ assertEquals(NioSocketChannel.class, ClientChannelClassUtil.getSocketChannelClass(ioEventLoopGroup));
+ assertEquals(NioSocketChannel.class, ClientChannelClassUtil.getSocketChannelClass(legacyEventLoopGroup));
+ }
- final KQueueEventLoopGroup kQueueEventLoopGroup = new KQueueEventLoopGroup(1);
+ @Test
+ void getDatagramClass() {
+ assertEquals(NioDatagramChannel.class, ClientChannelClassUtil.getDatagramChannelClass(ioEventLoopGroup));
+ assertEquals(NioDatagramChannel.class, ClientChannelClassUtil.getDatagramChannelClass(legacyEventLoopGroup));
+ }
- try {
- assertEquals(KQueueSocketChannel.class, ClientChannelClassUtil.getSocketChannelClass(kQueueEventLoopGroup));
- } finally {
- kQueueEventLoopGroup.shutdownGracefully();
+ @AfterEach
+ void tearDown() {
+ ioEventLoopGroup.shutdownGracefully();
+ legacyEventLoopGroup.shutdownGracefully();
}
}
- @Test
- void testGetEpollSocketChannelClass() {
- final String unavailabilityMessage =
+ @Nested
+ class EpollTransport {
+
+ private IoEventLoopGroup ioEventLoopGroup;
+ private EpollEventLoopGroup legacyEventLoopGroup;
+
+ @BeforeEach
+ void setUp() {
+ final String unavailabilityMessage =
Epoll.unavailabilityCause() != null ? Epoll.unavailabilityCause().getMessage() : null;
- assumeTrue(Epoll.isAvailable(), "Epoll not available: " + unavailabilityMessage);
+ assumeTrue(Epoll.isAvailable(), "Epoll not available: " + unavailabilityMessage);
- final EpollEventLoopGroup epollEventLoopGroup = new EpollEventLoopGroup(1);
+ ioEventLoopGroup = new MultiThreadIoEventLoopGroup(EpollIoHandler.newFactory());
+ legacyEventLoopGroup = new EpollEventLoopGroup();
+ }
- try {
- assertEquals(EpollSocketChannel.class, ClientChannelClassUtil.getSocketChannelClass(epollEventLoopGroup));
- } finally {
- epollEventLoopGroup.shutdownGracefully();
+ @Test
+ void getSocketChannelClass() {
+ assertEquals(EpollSocketChannel.class, ClientChannelClassUtil.getSocketChannelClass(ioEventLoopGroup));
+ assertEquals(EpollSocketChannel.class, ClientChannelClassUtil.getSocketChannelClass(legacyEventLoopGroup));
+ }
+
+ @Test
+ void getDatagramClass() {
+ assertEquals(EpollDatagramChannel.class, ClientChannelClassUtil.getDatagramChannelClass(ioEventLoopGroup));
+ assertEquals(EpollDatagramChannel.class, ClientChannelClassUtil.getDatagramChannelClass(legacyEventLoopGroup));
}
- }
- @Test
- void testGetCoreDatagramChannelClass() {
- final NioEventLoopGroup nioEventLoopGroup = new NioEventLoopGroup(1);
+ @AfterEach
+ void tearDown() {
+ if (ioEventLoopGroup != null) {
+ ioEventLoopGroup.shutdownGracefully();
+ }
- try {
- assertEquals(NioDatagramChannel.class, ClientChannelClassUtil.getDatagramChannelClass(nioEventLoopGroup));
- } finally {
- nioEventLoopGroup.shutdownGracefully();
+ if (legacyEventLoopGroup != null) {
+ legacyEventLoopGroup.shutdownGracefully();
+ }
}
}
- @Test
- void testGetKqueueDatagramChannelClass() {
- final String unavailabilityMessage =
- KQueue.unavailabilityCause() != null ? KQueue.unavailabilityCause().getMessage() : null;
+ @Nested
+ class IoUringTransport {
+
+ private IoEventLoopGroup ioEventLoopGroup;
+
+ @BeforeEach
+ void setUp() {
+ final String unavailabilityMessage =
+ IoUring.unavailabilityCause() != null ? IoUring.unavailabilityCause().getMessage() : null;
- assumeTrue(KQueue.isAvailable(), "KQueue not available: " + unavailabilityMessage);
+ assumeTrue(IoUring.isAvailable(), "io_uring not available: " + unavailabilityMessage);
- final KQueueEventLoopGroup kQueueEventLoopGroup = new KQueueEventLoopGroup(1);
+ ioEventLoopGroup = new MultiThreadIoEventLoopGroup(IoUringIoHandler.newFactory());
+ }
+
+ @Test
+ void getSocketChannelClass() {
+ assertEquals(IoUringSocketChannel.class, ClientChannelClassUtil.getSocketChannelClass(ioEventLoopGroup));
+ }
+
+ @Test
+ void getDatagramClass() {
+ assertEquals(IoUringDatagramChannel.class, ClientChannelClassUtil.getDatagramChannelClass(ioEventLoopGroup));
+ }
- try {
- assertEquals(KQueueDatagramChannel.class, ClientChannelClassUtil.getDatagramChannelClass(kQueueEventLoopGroup));
- } finally {
- kQueueEventLoopGroup.shutdownGracefully();
+ @AfterEach
+ void tearDown() {
+ if (ioEventLoopGroup != null) {
+ ioEventLoopGroup.shutdownGracefully();
+ }
}
}
- @Test
- void testGetEpollDatagramChannelClass() {
- final String unavailabilityMessage =
- Epoll.unavailabilityCause() != null ? Epoll.unavailabilityCause().getMessage() : null;
+ @Nested
+ class KQueueTransport {
- assumeTrue(Epoll.isAvailable(), "Epoll not available: " + unavailabilityMessage);
+ private IoEventLoopGroup ioEventLoopGroup;
+ private KQueueEventLoopGroup legacyEventLoopGroup;
+
+ @BeforeEach
+ void setUp() {
+ final String unavailabilityMessage =
+ KQueue.unavailabilityCause() != null ? KQueue.unavailabilityCause().getMessage() : null;
+
+ assumeTrue(KQueue.isAvailable(), "KQueue not available: " + unavailabilityMessage);
+
+ ioEventLoopGroup = new MultiThreadIoEventLoopGroup(KQueueIoHandler.newFactory());
+ legacyEventLoopGroup = new KQueueEventLoopGroup();
+ }
+
+ @Test
+ void getSocketChannelClass() {
+ assertEquals(KQueueSocketChannel.class, ClientChannelClassUtil.getSocketChannelClass(ioEventLoopGroup));
+ assertEquals(KQueueSocketChannel.class, ClientChannelClassUtil.getSocketChannelClass(legacyEventLoopGroup));
+ }
+
+ @Test
+ void getDatagramClass() {
+ assertEquals(KQueueDatagramChannel.class, ClientChannelClassUtil.getDatagramChannelClass(ioEventLoopGroup));
+ assertEquals(KQueueDatagramChannel.class, ClientChannelClassUtil.getDatagramChannelClass(legacyEventLoopGroup));
+ }
- final EpollEventLoopGroup epollEventLoopGroup = new EpollEventLoopGroup(1);
+ @AfterEach
+ void tearDown() {
+ if (ioEventLoopGroup != null) {
+ ioEventLoopGroup.shutdownGracefully();
+ }
- try {
- assertEquals(EpollDatagramChannel.class, ClientChannelClassUtil.getDatagramChannelClass(epollEventLoopGroup));
- } finally {
- epollEventLoopGroup.shutdownGracefully();
+ if (legacyEventLoopGroup != null) {
+ legacyEventLoopGroup.shutdownGracefully();
+ }
}
}
}
diff --git a/pushy/src/test/java/com/eatthepath/pushy/apns/P12UtilTest.java b/pushy/src/test/java/com/eatthepath/pushy/apns/P12UtilTest.java
index e9ce410b0..47ba72588 100644
--- a/pushy/src/test/java/com/eatthepath/pushy/apns/P12UtilTest.java
+++ b/pushy/src/test/java/com/eatthepath/pushy/apns/P12UtilTest.java
@@ -22,26 +22,32 @@
package com.eatthepath.pushy.apns;
+import io.netty.pkitesting.CertificateBuilder;
+import io.netty.pkitesting.X509Bundle;
import org.junit.jupiter.api.Test;
-import java.io.InputStream;
+import java.io.*;
+import java.nio.file.Files;
+import java.security.KeyStore;
import java.security.KeyStore.PrivateKeyEntry;
import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateException;
+import java.time.Duration;
+import java.time.Instant;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class P12UtilTest {
- private static final String SINGLE_TOPIC_CLIENT_KEYSTORE_FILENAME = "/single-topic-client.p12";
- private static final String MULTIPLE_KEY_KEYSTORE_FILENAME = "/multiple-keys.p12";
- private static final String NO_KEY_KEYSTORE_FILENAME = "/no-keys.p12";
-
private static final String KEYSTORE_PASSWORD = "pushy-test";
@Test
void testGetPrivateKeyEntryFromP12InputStream() throws Exception {
- try (final InputStream p12InputStream = P12UtilTest.class.getResourceAsStream(SINGLE_TOPIC_CLIENT_KEYSTORE_FILENAME)) {
+ final File keyStoreFile = writeTemporaryKeyStore(1);
+
+ try (final InputStream p12InputStream = Files.newInputStream(keyStoreFile.toPath())) {
final PrivateKeyEntry privateKeyEntry =
P12Util.getFirstPrivateKeyEntryFromP12InputStream(p12InputStream, KEYSTORE_PASSWORD);
@@ -51,7 +57,9 @@ void testGetPrivateKeyEntryFromP12InputStream() throws Exception {
@Test
void testGetPrivateKeyEntryFromP12InputStreamWithMultipleKeys() throws Exception {
- try (final InputStream p12InputStream = P12UtilTest.class.getResourceAsStream(MULTIPLE_KEY_KEYSTORE_FILENAME)) {
+ final File keyStoreFile = writeTemporaryKeyStore(4);
+
+ try (final InputStream p12InputStream = Files.newInputStream(keyStoreFile.toPath())) {
final PrivateKeyEntry privateKeyEntry =
P12Util.getFirstPrivateKeyEntryFromP12InputStream(p12InputStream, KEYSTORE_PASSWORD);
@@ -61,9 +69,53 @@ void testGetPrivateKeyEntryFromP12InputStreamWithMultipleKeys() throws Exception
@Test
void testGetPrivateKeyEntryFromP12InputStreamWithNoKeys() throws Exception {
- try (final InputStream p12InputStream = P12UtilTest.class.getResourceAsStream(NO_KEY_KEYSTORE_FILENAME)) {
+ final File keyStoreFile = writeTemporaryKeyStore(0);
+
+ try (final InputStream p12InputStream = Files.newInputStream(keyStoreFile.toPath())) {
assertThrows(KeyStoreException.class,
() -> P12Util.getFirstPrivateKeyEntryFromP12InputStream(p12InputStream, KEYSTORE_PASSWORD));
}
}
+
+ private static File writeTemporaryKeyStore(final int keyCount) throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException {
+ final KeyStore keyStore;
+
+ try {
+ keyStore = KeyStore.getInstance("PKCS12");
+ } catch (final KeyStoreException e) {
+ throw new AssertionError("Every implementation of the Java platform is required to support PKCS12");
+ }
+
+ // Keystores must be initialized before we can add any entries
+ keyStore.load(null);
+
+ for (int i = 0; i < keyCount; i++) {
+ final String alias = "test-key-" + i;
+
+ final X509Bundle x509Bundle;
+
+ try {
+ x509Bundle = new CertificateBuilder()
+ .notBefore(Instant.now())
+ .notAfter(Instant.now().plus(Duration.ofHours(1)))
+ .subject("CN=" + alias)
+ .setKeyUsage(true, CertificateBuilder.KeyUsage.digitalSignature, CertificateBuilder.KeyUsage.keyCertSign)
+ .setIsCertificateAuthority(true)
+ .buildSelfSigned();
+ } catch (final Exception e) {
+ throw new AssertionError("Failed to build in-memory, self-signed certificate", e);
+ }
+
+ keyStore.setKeyEntry(alias, x509Bundle.getKeyPair().getPrivate(), KEYSTORE_PASSWORD.toCharArray(), x509Bundle.getCertificatePath());
+ }
+
+ final File keystoreFile = File.createTempFile("pushy-test", ".p12");
+ keystoreFile.deleteOnExit();
+
+ try (final OutputStream outputStream = Files.newOutputStream(keystoreFile.toPath())) {
+ keyStore.store(outputStream, KEYSTORE_PASSWORD.toCharArray());
+ }
+
+ return keystoreFile;
+ }
}
diff --git a/pushy/src/test/java/com/eatthepath/pushy/apns/auth/ApnsKeyTest.java b/pushy/src/test/java/com/eatthepath/pushy/apns/auth/ApnsKeyTest.java
index a23614dc3..0856c79c8 100644
--- a/pushy/src/test/java/com/eatthepath/pushy/apns/auth/ApnsKeyTest.java
+++ b/pushy/src/test/java/com/eatthepath/pushy/apns/auth/ApnsKeyTest.java
@@ -23,21 +23,14 @@
package com.eatthepath.pushy.apns.auth;
import org.junit.jupiter.api.Test;
-import org.junit.jupiter.params.ParameterizedTest;
-import org.junit.jupiter.params.provider.Arguments;
-import org.junit.jupiter.params.provider.MethodSource;
import java.io.IOException;
-import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
-import java.util.stream.Stream;
-import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.params.provider.Arguments.arguments;
-public abstract class ApnsKeyTest {
+abstract class ApnsKeyTest {
protected abstract ApnsKey getApnsKey() throws NoSuchAlgorithmException, InvalidKeyException, IOException;
@@ -60,21 +53,4 @@ void testGetKey() throws Exception {
void testGetParams() throws Exception {
assertNotNull(this.getApnsKey().getParams());
}
-
- @ParameterizedTest
- @MethodSource("argumentsForDecodeBase64EncodedString")
- void testDecodeBase64EncodedString(final String base64EncodedString, final String decodedAsciiString) {
- assertEquals(decodedAsciiString, new String(ApnsKey.decodeBase64EncodedString(base64EncodedString), StandardCharsets.US_ASCII));
- }
-
- private static Stream argumentsForDecodeBase64EncodedString() {
- // Test vectors from https://tools.ietf.org/html/rfc4648#section-10
- return Stream.of(
- arguments("Zg==", "f"),
- arguments("Zm8=", "fo"),
- arguments("Zm9v", "foo"),
- arguments("Zm9vYg==", "foob"),
- arguments("Zm9vYmE=", "fooba"),
- arguments("Zm9vYmFy", "foobar"));
- }
}
diff --git a/pushy/src/test/java/com/eatthepath/pushy/apns/auth/ApnsSigningKeyTest.java b/pushy/src/test/java/com/eatthepath/pushy/apns/auth/ApnsSigningKeyTest.java
index b20a55aa9..1ee61de3b 100644
--- a/pushy/src/test/java/com/eatthepath/pushy/apns/auth/ApnsSigningKeyTest.java
+++ b/pushy/src/test/java/com/eatthepath/pushy/apns/auth/ApnsSigningKeyTest.java
@@ -22,14 +22,38 @@
package com.eatthepath.pushy.apns.auth;
+import org.junit.jupiter.api.Test;
+
+import java.io.ByteArrayInputStream;
import java.io.IOException;
+import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
+import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
+import java.security.interfaces.ECPrivateKey;
+import java.util.Base64;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
-public class ApnsSigningKeyTest extends ApnsKeyTest {
+class ApnsSigningKeyTest extends ApnsKeyTest {
@Override
- protected ApnsKey getApnsKey() throws NoSuchAlgorithmException, InvalidKeyException, IOException {
- return ApnsSigningKey.loadFromInputStream(this.getClass().getResourceAsStream("/token-auth-private-key.p8"), "Team ID", "Key ID");
+ protected ApnsKey getApnsKey() throws NoSuchAlgorithmException, InvalidKeyException {
+ return new ApnsSigningKey("Key ID", "Team ID",
+ (ECPrivateKey) KeyPairGenerator.getInstance("EC").generateKeyPair().getPrivate());
+ }
+
+ @Test
+ void loadFromInputStream() throws IOException, NoSuchAlgorithmException {
+ final ECPrivateKey privateKey =
+ (ECPrivateKey) KeyPairGenerator.getInstance("EC").generateKeyPair().getPrivate();
+
+ final String pkcs8EncodedKey = "-----BEGIN PRIVATE KEY-----\r\n" +
+ Base64.getMimeEncoder().encodeToString(privateKey.getEncoded()) +
+ "\r\n-----END PRIVATE KEY-----\r\n";
+
+ try (final ByteArrayInputStream keyInputStream = new ByteArrayInputStream(pkcs8EncodedKey.getBytes(StandardCharsets.UTF_8))) {
+ assertDoesNotThrow(() -> ApnsSigningKey.loadFromInputStream(keyInputStream, "Team ID", "Key ID"));
+ }
}
}
diff --git a/pushy/src/test/java/com/eatthepath/pushy/apns/auth/ApnsVerificationKeyTest.java b/pushy/src/test/java/com/eatthepath/pushy/apns/auth/ApnsVerificationKeyTest.java
index 819f32bc3..011a74adb 100644
--- a/pushy/src/test/java/com/eatthepath/pushy/apns/auth/ApnsVerificationKeyTest.java
+++ b/pushy/src/test/java/com/eatthepath/pushy/apns/auth/ApnsVerificationKeyTest.java
@@ -22,14 +22,38 @@
package com.eatthepath.pushy.apns.auth;
+import org.junit.jupiter.api.Test;
+
+import java.io.ByteArrayInputStream;
import java.io.IOException;
+import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
+import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
+import java.security.interfaces.ECPublicKey;
+import java.util.Base64;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
-public class ApnsVerificationKeyTest extends ApnsKeyTest {
+class ApnsVerificationKeyTest extends ApnsKeyTest {
@Override
- protected ApnsKey getApnsKey() throws NoSuchAlgorithmException, InvalidKeyException, IOException {
- return ApnsVerificationKey.loadFromInputStream(this.getClass().getResourceAsStream("/token-auth-public-key.p8"), "Team ID", "Key ID");
+ protected ApnsKey getApnsKey() throws NoSuchAlgorithmException, InvalidKeyException {
+ return new ApnsVerificationKey("Key ID", "Team ID",
+ (ECPublicKey) KeyPairGenerator.getInstance("EC").generateKeyPair().getPublic());
+ }
+
+ @Test
+ void loadFromInputStream() throws IOException, NoSuchAlgorithmException {
+ final ECPublicKey publicKey =
+ (ECPublicKey) KeyPairGenerator.getInstance("EC").generateKeyPair().getPublic();
+
+ final String pkcs8EncodedKey = "-----BEGIN PUBLIC KEY-----\r\n" +
+ Base64.getMimeEncoder().encodeToString(publicKey.getEncoded()) +
+ "\r\n-----END PUBLIC KEY-----\r\n";
+
+ try (final ByteArrayInputStream keyInputStream = new ByteArrayInputStream(pkcs8EncodedKey.getBytes(StandardCharsets.UTF_8))) {
+ assertDoesNotThrow(() -> ApnsVerificationKey.loadFromInputStream(keyInputStream, "Team ID", "Key ID"));
+ }
}
}
diff --git a/pushy/src/test/java/com/eatthepath/pushy/apns/server/MockApnsServerBuilderTest.java b/pushy/src/test/java/com/eatthepath/pushy/apns/server/MockApnsServerBuilderTest.java
index 99391f1bf..3aa09c77e 100644
--- a/pushy/src/test/java/com/eatthepath/pushy/apns/server/MockApnsServerBuilderTest.java
+++ b/pushy/src/test/java/com/eatthepath/pushy/apns/server/MockApnsServerBuilderTest.java
@@ -22,66 +22,74 @@
package com.eatthepath.pushy.apns.server;
+import com.eatthepath.ApnsTestCertificates;
+import io.netty.pkitesting.X509Bundle;
+import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
+import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
-import java.security.KeyStore;
-import java.security.cert.X509Certificate;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class MockApnsServerBuilderTest {
- private static final String SERVER_CERTIFICATE_FILENAME = "/server-certs.pem";
- private static final String SERVER_KEY_FILENAME = "/server-key.pem";
- private static final String SERVER_KEYSTORE_FILENAME = "/server.p12";
- private static final String SERVER_KEYSTORE_ALIAS = "1";
- private static final String SERVER_KEYSTORE_PASSWORD = "pushy-test";
+ private static X509Bundle CERTIFICATE_BUNDLE;
+
+ @BeforeAll
+ static void setUpBeforeAll() throws Exception {
+ CERTIFICATE_BUNDLE = new ApnsTestCertificates().getTrustedServerCertificateBundle();
+ }
@Test
void testSetServerCredentialsFileFileString() throws Exception {
- final File certificateFile = new File(this.getClass().getResource(SERVER_CERTIFICATE_FILENAME).toURI());
- final File keyFile = new File(this.getClass().getResource(SERVER_KEY_FILENAME).toURI());
+ final File certificateFile = File.createTempFile("pushy-test", ".pem");
+ certificateFile.deleteOnExit();
- // We're happy here as long as nothing explodes
- new MockApnsServerBuilder()
- .setServerCredentials(certificateFile, keyFile, null)
- .setHandlerFactory(new AcceptAllPushNotificationHandlerFactory())
- .build();
+ try (final OutputStream certificateOutputStream = Files.newOutputStream(certificateFile.toPath())) {
+ certificateOutputStream.write(CERTIFICATE_BUNDLE.getCertificatePathPEM().getBytes(StandardCharsets.UTF_8));
+ }
+
+ final File keyFile = File.createTempFile("pushy-test", ".p8");
+ keyFile.deleteOnExit();
+
+ try (final OutputStream keyOutputStream = Files.newOutputStream(keyFile.toPath())) {
+ keyOutputStream.write(CERTIFICATE_BUNDLE.getPrivateKeyPEM().getBytes(StandardCharsets.UTF_8));
+ }
+
+ assertDoesNotThrow(() -> new MockApnsServerBuilder()
+ .setServerCredentials(certificateFile, keyFile, null)
+ .setHandlerFactory(new AcceptAllPushNotificationHandlerFactory())
+ .build());
}
@Test
void testSetServerCredentialsInputStreamInputStreamString() throws Exception {
- try (final InputStream certificateInputStream = this.getClass().getResourceAsStream(SERVER_CERTIFICATE_FILENAME);
- final InputStream keyInputStream = this.getClass().getResourceAsStream(SERVER_KEY_FILENAME)) {
-
- // We're happy here as long as nothing explodes
- new MockApnsServerBuilder()
- .setServerCredentials(certificateInputStream, keyInputStream, null)
- .setHandlerFactory(new AcceptAllPushNotificationHandlerFactory())
- .build();
+
+ try (final InputStream certificateInputStream =
+ new ByteArrayInputStream(CERTIFICATE_BUNDLE.getCertificatePathPEM().getBytes(StandardCharsets.UTF_8));
+ final InputStream keyInputStream =
+ new ByteArrayInputStream(CERTIFICATE_BUNDLE.getPrivateKeyPEM().getBytes(StandardCharsets.UTF_8))) {
+
+ assertDoesNotThrow(() -> new MockApnsServerBuilder()
+ .setServerCredentials(certificateInputStream, keyInputStream, null)
+ .setHandlerFactory(new AcceptAllPushNotificationHandlerFactory())
+ .build());
}
}
@Test
- void testSetServerCredentialsX509CertificateArrayPrivateKeyString() throws Exception {
- try (final InputStream p12InputStream = this.getClass().getResourceAsStream(SERVER_KEYSTORE_FILENAME)) {
- final KeyStore keyStore = KeyStore.getInstance("PKCS12");
- keyStore.load(p12InputStream, SERVER_KEYSTORE_PASSWORD.toCharArray());
-
- final KeyStore.PasswordProtection passwordProtection =
- new KeyStore.PasswordProtection(SERVER_KEYSTORE_PASSWORD.toCharArray());
-
- final KeyStore.PrivateKeyEntry privateKeyEntry =
- (KeyStore.PrivateKeyEntry) keyStore.getEntry(SERVER_KEYSTORE_ALIAS, passwordProtection);
-
- // We're happy here as long as nothing explodes
- new MockApnsServerBuilder()
- .setServerCredentials(new X509Certificate[] { (X509Certificate) privateKeyEntry.getCertificate() }, privateKeyEntry.getPrivateKey())
- .setHandlerFactory(new AcceptAllPushNotificationHandlerFactory())
- .build();
- }
+ void testSetServerCredentialsX509CertificateArrayPrivateKeyString() {
+ assertDoesNotThrow(() -> new MockApnsServerBuilder()
+ .setServerCredentials(CERTIFICATE_BUNDLE.getCertificatePathWithRoot(),
+ CERTIFICATE_BUNDLE.getKeyPair().getPrivate())
+ .setHandlerFactory(new AcceptAllPushNotificationHandlerFactory())
+ .build());
}
@Test
@@ -92,19 +100,16 @@ void testBuildWithoutServerCredentials() {
}
@Test
- void testBuildWithoutHandlerFactory() throws Exception {
- final File certificateFile = new File(this.getClass().getResource(SERVER_CERTIFICATE_FILENAME).toURI());
- final File keyFile = new File(this.getClass().getResource(SERVER_KEY_FILENAME).toURI());
-
+ void testBuildWithoutHandlerFactory() {
assertThrows(IllegalStateException.class, () -> new MockApnsServerBuilder()
- .setServerCredentials(certificateFile, keyFile, null)
- .build());
+ .setServerCredentials(CERTIFICATE_BUNDLE.getCertificatePathWithRoot(),
+ CERTIFICATE_BUNDLE.getKeyPair().getPrivate())
+ .build());
}
@Test
void testSetMaxConcurrentStreams() {
- // We're happy here as long as nothing explodes
- new MockApnsServerBuilder().setMaxConcurrentStreams(1);
+ assertDoesNotThrow(() -> new MockApnsServerBuilder().setMaxConcurrentStreams(1));
}
@Test
diff --git a/pushy/src/test/java/com/eatthepath/pushy/apns/server/MockApnsServerTest.java b/pushy/src/test/java/com/eatthepath/pushy/apns/server/MockApnsServerTest.java
index 30bcf9ad7..99bc34203 100644
--- a/pushy/src/test/java/com/eatthepath/pushy/apns/server/MockApnsServerTest.java
+++ b/pushy/src/test/java/com/eatthepath/pushy/apns/server/MockApnsServerTest.java
@@ -24,7 +24,9 @@
import com.eatthepath.pushy.apns.*;
import com.eatthepath.pushy.apns.util.SimpleApnsPushNotification;
-import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.IoEventLoopGroup;
+import io.netty.channel.MultiThreadIoEventLoopGroup;
+import io.netty.channel.nio.NioIoHandler;
import org.junit.jupiter.api.Test;
import java.time.Instant;
@@ -105,22 +107,23 @@ void testShutdownBeforeStart() throws Exception {
@Test
void testShutdownWithProvidedEventLoopGroup() throws Exception {
- final NioEventLoopGroup eventLoopGroup = new NioEventLoopGroup(1);
+ final IoEventLoopGroup ioEventLoopGroup = new MultiThreadIoEventLoopGroup(NioIoHandler.newFactory());
try {
final MockApnsServer providedGroupServer = new MockApnsServerBuilder()
- .setServerCredentials(getClass().getResourceAsStream(SERVER_CERTIFICATES_FILENAME), getClass().getResourceAsStream(SERVER_KEY_FILENAME), null)
+ .setServerCredentials(TEST_CERTIFICATES.getTrustedServerCertificateBundle().getCertificatePathWithRoot(),
+ TEST_CERTIFICATES.getTrustedServerCertificateBundle().getKeyPair().getPrivate())
.setHandlerFactory(new AcceptAllPushNotificationHandlerFactory())
- .setEventLoopGroup(eventLoopGroup)
+ .setIoEventLoopGroup(ioEventLoopGroup)
.build();
assertDoesNotThrow(() -> providedGroupServer.start(PORT).get());
assertDoesNotThrow(() -> providedGroupServer.shutdown().get());
- assertFalse(eventLoopGroup.isShutdown());
+ assertFalse(ioEventLoopGroup.isShutdown());
} finally {
- eventLoopGroup.shutdownGracefully().await();
+ ioEventLoopGroup.shutdownGracefully().await();
}
}
@@ -136,14 +139,15 @@ void testRestartWithProvidedEventLoopGroup() throws Exception {
// TODO Remove this assumption when https://github.com/netty/netty/issues/8697 gets resolved
assumeTrue(javaVersion < 11);
- final NioEventLoopGroup eventLoopGroup = new NioEventLoopGroup(1);
+ final IoEventLoopGroup ioEventLoopGroup = new MultiThreadIoEventLoopGroup(NioIoHandler.newFactory());
try {
final MockApnsServer providedGroupServer = new MockApnsServerBuilder()
- .setServerCredentials(getClass().getResourceAsStream(SERVER_CERTIFICATES_FILENAME), getClass().getResourceAsStream(SERVER_KEY_FILENAME), null)
- .setHandlerFactory(new AcceptAllPushNotificationHandlerFactory())
- .setEventLoopGroup(eventLoopGroup)
- .build();
+ .setServerCredentials(TEST_CERTIFICATES.getTrustedServerCertificateBundle().getCertificatePathWithRoot(),
+ TEST_CERTIFICATES.getTrustedServerCertificateBundle().getKeyPair().getPrivate())
+ .setHandlerFactory(new AcceptAllPushNotificationHandlerFactory())
+ .setIoEventLoopGroup(ioEventLoopGroup)
+ .build();
assertDoesNotThrow(() -> providedGroupServer.start(PORT).get());
assertDoesNotThrow(() -> providedGroupServer.shutdown().get());
@@ -151,7 +155,7 @@ void testRestartWithProvidedEventLoopGroup() throws Exception {
assertDoesNotThrow(() -> providedGroupServer.start(PORT).get());
assertDoesNotThrow(() -> providedGroupServer.shutdown().get());
} finally {
- eventLoopGroup.shutdownGracefully().await();
+ ioEventLoopGroup.shutdownGracefully().await();
}
}
diff --git a/pushy/src/test/java/com/eatthepath/pushy/apns/server/ServerChannelClassUtilTest.java b/pushy/src/test/java/com/eatthepath/pushy/apns/server/ServerChannelClassUtilTest.java
index 6fe593f04..03830c9ab 100644
--- a/pushy/src/test/java/com/eatthepath/pushy/apns/server/ServerChannelClassUtilTest.java
+++ b/pushy/src/test/java/com/eatthepath/pushy/apns/server/ServerChannelClassUtilTest.java
@@ -22,61 +22,152 @@
package com.eatthepath.pushy.apns.server;
+import io.netty.channel.IoEventLoopGroup;
+import io.netty.channel.MultiThreadIoEventLoopGroup;
import io.netty.channel.epoll.Epoll;
import io.netty.channel.epoll.EpollEventLoopGroup;
+import io.netty.channel.epoll.EpollIoHandler;
import io.netty.channel.epoll.EpollServerSocketChannel;
import io.netty.channel.kqueue.KQueue;
import io.netty.channel.kqueue.KQueueEventLoopGroup;
+import io.netty.channel.kqueue.KQueueIoHandler;
import io.netty.channel.kqueue.KQueueServerSocketChannel;
import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.nio.NioIoHandler;
import io.netty.channel.socket.nio.NioServerSocketChannel;
+import io.netty.channel.uring.IoUring;
+import io.netty.channel.uring.IoUringIoHandler;
+import io.netty.channel.uring.IoUringServerSocketChannel;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
-import static org.junit.jupiter.api.Assertions.*;
+import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assumptions.assumeTrue;
public class ServerChannelClassUtilTest {
- @Test
- void testGetCoreSocketChannelClass() {
- final NioEventLoopGroup nioEventLoopGroup = new NioEventLoopGroup(1);
+ @Nested
+ class NioTransport {
- try {
- assertEquals(NioServerSocketChannel.class, ServerChannelClassUtil.getServerSocketChannelClass(nioEventLoopGroup));
- } finally {
- nioEventLoopGroup.shutdownGracefully();
+ private IoEventLoopGroup ioEventLoopGroup;
+ private NioEventLoopGroup legacyEventLoopGroup;
+
+ @BeforeEach
+ void setUp() {
+ ioEventLoopGroup = new MultiThreadIoEventLoopGroup(NioIoHandler.newFactory());
+ legacyEventLoopGroup = new NioEventLoopGroup();
+ }
+
+ @Test
+ void getServerSocketChannelClass() {
+ assertEquals(NioServerSocketChannel.class, ServerChannelClassUtil.getServerSocketChannelClass(ioEventLoopGroup));
+ assertEquals(NioServerSocketChannel.class, ServerChannelClassUtil.getServerSocketChannelClass(legacyEventLoopGroup));
+ }
+
+ @AfterEach
+ void tearDown() {
+ ioEventLoopGroup.shutdownGracefully();
+ legacyEventLoopGroup.shutdownGracefully();
}
}
- @Test
- void testGetKqueueSocketChannelClass() {
- final String unavailabilityMessage =
- KQueue.unavailabilityCause() != null ? KQueue.unavailabilityCause().getMessage() : null;
+ @Nested
+ class EpollTransport {
+
+ private IoEventLoopGroup ioEventLoopGroup;
+ private EpollEventLoopGroup legacyEventLoopGroup;
+
+ @BeforeEach
+ void setUp() {
+ final String unavailabilityMessage =
+ Epoll.unavailabilityCause() != null ? Epoll.unavailabilityCause().getMessage() : null;
+
+ assumeTrue(Epoll.isAvailable(), "Epoll not available: " + unavailabilityMessage);
+
+ ioEventLoopGroup = new MultiThreadIoEventLoopGroup(EpollIoHandler.newFactory());
+ legacyEventLoopGroup = new EpollEventLoopGroup();
+ }
- assumeTrue(KQueue.isAvailable(), "KQueue not available: " + unavailabilityMessage);
+ @Test
+ void getServerSocketChannelClass() {
+ assertEquals(EpollServerSocketChannel.class, ServerChannelClassUtil.getServerSocketChannelClass(ioEventLoopGroup));
+ assertEquals(EpollServerSocketChannel.class, ServerChannelClassUtil.getServerSocketChannelClass(legacyEventLoopGroup));
+ }
- final KQueueEventLoopGroup kQueueEventLoopGroup = new KQueueEventLoopGroup(1);
+ @AfterEach
+ void tearDown() {
+ if (ioEventLoopGroup != null) {
+ ioEventLoopGroup.shutdownGracefully();
+ }
- try {
- assertEquals(KQueueServerSocketChannel.class, ServerChannelClassUtil.getServerSocketChannelClass(kQueueEventLoopGroup));
- } finally {
- kQueueEventLoopGroup.shutdownGracefully();
+ if (legacyEventLoopGroup != null) {
+ legacyEventLoopGroup.shutdownGracefully();
+ }
}
}
- @Test
- void testGetEpollSocketChannelClass() {
- final String unavailabilityMessage =
- Epoll.unavailabilityCause() != null ? Epoll.unavailabilityCause().getMessage() : null;
+ @Nested
+ class IoUringTransport {
+
+ private IoEventLoopGroup ioEventLoopGroup;
+
+ @BeforeEach
+ void setUp() {
+ final String unavailabilityMessage =
+ IoUring.unavailabilityCause() != null ? IoUring.unavailabilityCause().getMessage() : null;
- assumeTrue(Epoll.isAvailable(), "Epoll not available: " + unavailabilityMessage);
+ assumeTrue(IoUring.isAvailable(), "io_uring not available: " + unavailabilityMessage);
+
+ ioEventLoopGroup = new MultiThreadIoEventLoopGroup(IoUringIoHandler.newFactory());
+ }
+
+ @Test
+ void getServerSocketChannelClass() {
+ assertEquals(IoUringServerSocketChannel.class, ServerChannelClassUtil.getServerSocketChannelClass(ioEventLoopGroup));
+ }
+
+ @AfterEach
+ void tearDown() {
+ if (ioEventLoopGroup != null) {
+ ioEventLoopGroup.shutdownGracefully();
+ }
+ }
+ }
+
+ @Nested
+ class KQueueTransport {
+
+ private IoEventLoopGroup ioEventLoopGroup;
+ private KQueueEventLoopGroup legacyEventLoopGroup;
+
+ @BeforeEach
+ void setUp() {
+ final String unavailabilityMessage =
+ KQueue.unavailabilityCause() != null ? KQueue.unavailabilityCause().getMessage() : null;
+
+ assumeTrue(KQueue.isAvailable(), "KQueue not available: " + unavailabilityMessage);
+
+ ioEventLoopGroup = new MultiThreadIoEventLoopGroup(KQueueIoHandler.newFactory());
+ legacyEventLoopGroup = new KQueueEventLoopGroup();
+ }
+
+ @Test
+ void getServerSocketChannelClass() {
+ assertEquals(KQueueServerSocketChannel.class, ServerChannelClassUtil.getServerSocketChannelClass(ioEventLoopGroup));
+ assertEquals(KQueueServerSocketChannel.class, ServerChannelClassUtil.getServerSocketChannelClass(legacyEventLoopGroup));
+ }
- final EpollEventLoopGroup epollEventLoopGroup = new EpollEventLoopGroup(1);
+ @AfterEach
+ void tearDown() {
+ if (ioEventLoopGroup != null) {
+ ioEventLoopGroup.shutdownGracefully();
+ }
- try {
- assertEquals(EpollServerSocketChannel.class, ServerChannelClassUtil.getServerSocketChannelClass(epollEventLoopGroup));
- } finally {
- epollEventLoopGroup.shutdownGracefully();
+ if (legacyEventLoopGroup != null) {
+ legacyEventLoopGroup.shutdownGracefully();
+ }
}
}
}
diff --git a/pushy/src/test/resources/.gitignore b/pushy/src/test/resources/.gitignore
deleted file mode 100644
index e69de29bb..000000000
diff --git a/pushy/src/test/resources/ca.pem b/pushy/src/test/resources/ca.pem
deleted file mode 100644
index b6aa12e40..000000000
--- a/pushy/src/test/resources/ca.pem
+++ /dev/null
@@ -1,19 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIDGDCCAgCgAwIBAgIJAJ+d4CU73eboMA0GCSqGSIb3DQEBDQUAMBgxFjAUBgNV
-BAMMDVB1c2h5VGVzdFJvb3QwIBcNMjEwNTMwMTc0MDUyWhgPMjEyMTA1MDYxNzQw
-NTJaMBgxFjAUBgNVBAMMDVB1c2h5VGVzdFJvb3QwggEiMA0GCSqGSIb3DQEBAQUA
-A4IBDwAwggEKAoIBAQDbxgGLLg0OMkBTiISlp+CFzldy+a4aV0iXQgZuyN0IiNgZ
-oYcW85NUt68GGMA74e6TMNKnTPfLtDIff7/GsrG4A44BYDmfmsHin793Z08gaBmv
-DjyhTyCFYSFW/fYH854Cc7aGL1EkB/YtZE+geIyhh8PBe2GjHv5JhGYamDsNplmN
-L5+tNO3Eh5RCkF6H1mLvYi6YLaPd0ebm6DMr8P2cI5jw7bGa+EudRHlT5cKfUC24
-yX1hVO78JFD9xktgCBzqLvAYOxQftrt8kydc+X5vrwcg2kL3cuAddCb65EMSnxyg
-RmqZZOcslfObbiTG5BxlhCU4dOhjbA3taeRIt7PZAgMBAAGjYzBhMB0GA1UdDgQW
-BBSEi2xZ6B06Ce5hbSF1TAVzNyFGPDAfBgNVHSMEGDAWgBSEi2xZ6B06Ce5hbSF1
-TAVzNyFGPDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG
-9w0BAQ0FAAOCAQEAL0zcVcy3K51SveofHYh156woBF0+JX7q+nslx8aRzdFCrJ/+
-gcFwPO3ntKA+k+Qw2iEgM5Tk3dUjywI05x8pFkeNIuIzCWjPYkw5xKwdYTMA9Uui
-0b7QDiAMz/Nu/YLgUMCSv3sI9E2v86dNx/EgdB1he2AVJk8Dhyf/ZIvxrOhAPyaJ
-59L2NA8ZyWYH57jUOjN39pTvKfVQ4U/W/2kt7PcuJS0jDcBxoZol2H2LRGWQbVxT
-rBwmmhBkqpmKAgm1+Kv3gyUSFOh+MR5TBzDm9bjzsLka5UxYzQS5/jknmKLZ98Y0
-2uMNWxdJ2Zt05oKoaNUeuUdwqLqrTYXgVWSnAg==
------END CERTIFICATE-----
diff --git a/pushy/src/test/resources/multi-topic-client.p12 b/pushy/src/test/resources/multi-topic-client.p12
deleted file mode 100644
index 3f95df783..000000000
Binary files a/pushy/src/test/resources/multi-topic-client.p12 and /dev/null differ
diff --git a/pushy/src/test/resources/multi-topic-client.pem b/pushy/src/test/resources/multi-topic-client.pem
deleted file mode 100644
index 05301d6f0..000000000
--- a/pushy/src/test/resources/multi-topic-client.pem
+++ /dev/null
@@ -1,22 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIDqTCCApGgAwIBAgIBATANBgkqhkiG9w0BAQ0FADAYMRYwFAYDVQQDDA1QdXNo
-eVRlc3RSb290MCAXDTIxMDUzMDE3NDA1M1oYDzIxMjEwNTA2MTc0MDUzWjBaMTIw
-MAYDVQQDDClBcHBsZSBQdXNoIFNlcnZpY2VzOiBjb20uZWF0dGhlcGF0aC5wdXNo
-eTEkMCIGCgmSJomT8ixkAQEMFGNvbS5lYXR0aGVwYXRoLnB1c2h5MIIBIjANBgkq
-hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA8bWSCyRhDo1YPhBSdGgQadrLvyfzZVWg
-jtL6oHVxqre4lM15woYCs34e7IYZ7o7XtOiI97gQgIWTfBvXbKcTg4FphrYrd+q8
-YXPbagakbM2IStuJep59DCcEx3mKw6rIFb+YgimQQ34l+qA2AgaEqQ0Qyywmgtcp
-wQShTfU2Sd2ELhROUnOyJyVeBqL+/sNkTtqt+zbU16GEVTi+X26h4UVFnoLHJgTc
-WRK8UALC+vu5TQpCCGWuCWBcbwdCdoCxQB3l1yLbfGsCzCnK7NBSMn0zM8WOpJqr
-5yz7dn8mR6/hTXSIjzZbKGPR+4265z8eVJVLCFrdvtEy+iUciH3pZQIDAQABo4G5
-MIG2MAkGA1UdEwQCMAAwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUF
-BwMCMIGDBgoqhkiG92NkBgMGBHUwcwwUY29tLmVhdHRoZXBhdGgucHVzaHkwBQwD
-YXBwDBljb20uZWF0dGhlcGF0aC5wdXNoeS52b2lwMAYMBHZvaXAMIWNvbS5lYXR0
-aGVwYXRoLnB1c2h5LmNvbXBsaWNhdGlvbjAODAxjb21wbGljYXRpb24wDQYJKoZI
-hvcNAQENBQADggEBAMk6+dc7eYt5xkC75dPwoh2y/ZZN4+i2YE8NfaQWLWjfIbl9
-2p/t7m0T8+J+3HsRSMf8Ywgl1KsqeNCtdWHQg+EJpB9r18LgMT12sGgJqrRdcw2R
-0gqTZw/q53erUcvnNVBeQFeLV5f3nz2kFgrktfel/+aWfzYoU9CqzZLFcdCRa+zp
-A5pD2zEzrs6lk2X1eq5aVHbJfoiBslUHitw9UNXIfPHCnoNsGuTj85ZFpvxBdNK8
-f/7mIR6qgJhMOOcGcNHZZG7+A1iZoiRYEyeywsC1+7unLqrhmRr0UzwMKOdgbSHd
-gn3vFW9onFYKPGq9Hg3Lvyz+8QzNPZTlw6o8aA8=
------END CERTIFICATE-----
diff --git a/pushy/src/test/resources/multiple-keys.p12 b/pushy/src/test/resources/multiple-keys.p12
deleted file mode 100644
index 795cb0602..000000000
Binary files a/pushy/src/test/resources/multiple-keys.p12 and /dev/null differ
diff --git a/pushy/src/test/resources/no-keys.p12 b/pushy/src/test/resources/no-keys.p12
deleted file mode 100644
index 651fc5315..000000000
Binary files a/pushy/src/test/resources/no-keys.p12 and /dev/null differ
diff --git a/pushy/src/test/resources/server-certs-no-alt.pem b/pushy/src/test/resources/server-certs-no-alt.pem
deleted file mode 100644
index 10596456f..000000000
--- a/pushy/src/test/resources/server-certs-no-alt.pem
+++ /dev/null
@@ -1,18 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIC8DCCAdigAwIBAgIBATANBgkqhkiG9w0BAQ0FADAYMRYwFAYDVQQDDA1QdXNo
-eVRlc3RSb290MCAXDTIxMDUzMDE3NDA1MloYDzIxMjEwNTA2MTc0MDUyWjAfMR0w
-GwYDVQQDDBRjb20uZWF0dGhlcGF0aC5wdXNoeTCCASIwDQYJKoZIhvcNAQEBBQAD
-ggEPADCCAQoCggEBAO1UVO09VFQW1ihZaEY3FV/Cm6npyuJo6oCBnVtlSUwCPIYo
-46lxSZXi3QrQcHHJ6XtZFZEwZgwzCCjMRthJEdjADwaG9on9TervvuDbc9s6ZDh7
-yHG0WqG9hhTjjnFGY25vrCno3Y2hcZVz6rtJKkvLAZOKUD2gbOJiZ0O1rrJks5ZP
-PxyHouh2jGBVxIXAJXlJcpa7xrrOvW+3TIzmuO03wtsIfv5sCn71RsLLJcYHQwZI
-O78ob1FcvY+lXhrC9qiT772RraqpfAnaXQ1GLaD/0rbc8l6KTD5ZzrydhMXwb2Gw
-c9SnxEQN0ogu3nQGLTd5JY7ALzKri3NsrhpxQS8CAwEAAaM8MDowCQYDVR0TBAIw
-ADAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMB
-MA0GCSqGSIb3DQEBDQUAA4IBAQBHt27vREjaYL0lKkfK6I1xhiLCUiZeZdbAu1O4
-L3Wvs0v/y+2xY37spL3v4+sJLTINcjNFuUS+L9Wnh7yCGltWtHvgVFuPGF19yrCp
-9288FHNba6NJvtY92wBGHZVyCBGeDET8BX4xv0q46Uu4j1RfArEEAjiaEfhwD3B6
-XkPD8vA+JYPLNSPMf8YYrd2eVUt/k2dzFyDxW/rf9AhKNqy7ThGIQlTevbAqk1sW
-JXGewZYwNWx/zgAIsRGkgrYZ99EYZmihxLTj/HtwdqE0urRsioItzYlGUp3FqcNU
-ap4t1Bw5PmtdOZzZr02lP0v1C6mYhjxiJb9qTxPQ4C2cR3+2
------END CERTIFICATE-----
diff --git a/pushy/src/test/resources/server-certs.pem b/pushy/src/test/resources/server-certs.pem
deleted file mode 100644
index 8b112ed15..000000000
--- a/pushy/src/test/resources/server-certs.pem
+++ /dev/null
@@ -1,19 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIDBjCCAe6gAwIBAgIBATANBgkqhkiG9w0BAQ0FADAYMRYwFAYDVQQDDA1QdXNo
-eVRlc3RSb290MCAXDTIxMDUzMDE3NDA1MloYDzIxMjEwNTA2MTc0MDUyWjAfMR0w
-GwYDVQQDDBRjb20uZWF0dGhlcGF0aC5wdXNoeTCCASIwDQYJKoZIhvcNAQEBBQAD
-ggEPADCCAQoCggEBALhgEuJJfNuPxJXoBOVB+ZE/msSgo5Dg6T80uqTQ0Y38d+Hg
-ERpXdO0eSm0M5INx//IVf25wRVH5ueHGBHXp7i2HrnZNgMXD3JMo9rrI0SJ78h91
-oe4+KRX+q2saIJJZ6arsDZUEKQHTLz+HEPODgtD5wzG6rV9P/dbgYF6OEpxDy99X
-/ZGwYH7p2T21XmRBYy0CWYIuJVYrHsLxuhagzw+BD3sCmz19dwzc/uPEawJaEvtu
-DRXvpN+Hih20bMn005U8xq4U7JuSUPvsDYYf8dI5SA6NqVanh1YH8ipVO6wyO/1W
-5QyB4+pV1r2FLHuFnlue3H1mtrKXuKuXTuMjzucCAwEAAaNSMFAwCQYDVR0TBAIw
-ADAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMB
-MBQGA1UdEQQNMAuCCWxvY2FsaG9zdDANBgkqhkiG9w0BAQ0FAAOCAQEAMcgC8aq8
-qpFPFWwN8Yp9P0dfn7G7zUAUy6qsVC63A+qCNpxsdcB87wawuiuxvSQbWS6XyNwm
-ZTIYjkyq9P+OtWOk99gQEyadkc25FRDqsYFC8TDfGWh11qi/VCGwvSZtat+j5hMU
-JG93tuGkuUiVFmpotZhE2x1hmS0WIvVzeaheCEWUNCUx5M7Ldt2yiBEQXGUL75Vm
-+a4DwMYw2e901gowdhuVSahDrX7nb4gz8Ixm4ljfU3fSFh5G4kncvZ7/Ikkw5dzx
-x1rnMXw90bqA7TkHmUSBjN2guFclxXg9KyRwq4Ps08moE/LCxPei76PgX82zJQl8
-B4L2nXfdSQzmAA==
------END CERTIFICATE-----
diff --git a/pushy/src/test/resources/server-key-no-alt.pem b/pushy/src/test/resources/server-key-no-alt.pem
deleted file mode 100644
index fa206f15b..000000000
--- a/pushy/src/test/resources/server-key-no-alt.pem
+++ /dev/null
@@ -1,28 +0,0 @@
------BEGIN PRIVATE KEY-----
-MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDtVFTtPVRUFtYo
-WWhGNxVfwpup6criaOqAgZ1bZUlMAjyGKOOpcUmV4t0K0HBxyel7WRWRMGYMMwgo
-zEbYSRHYwA8GhvaJ/U3q777g23PbOmQ4e8hxtFqhvYYU445xRmNub6wp6N2NoXGV
-c+q7SSpLywGTilA9oGziYmdDta6yZLOWTz8ch6LodoxgVcSFwCV5SXKWu8a6zr1v
-t0yM5rjtN8LbCH7+bAp+9UbCyyXGB0MGSDu/KG9RXL2PpV4awvaok++9ka2qqXwJ
-2l0NRi2g/9K23PJeikw+Wc68nYTF8G9hsHPUp8REDdKILt50Bi03eSWOwC8yq4tz
-bK4acUEvAgMBAAECggEAFyTNL2F2ssiTU9X8NDS08eSxd21kKpYeoC+Dn+ENt8rU
-CiU8pk505Zf9BEv1WzNcgHncf48ftHrZhdj946OkiOWZ0YIh0q0QByQgEh90eeGE
-2nk/v87ds74esDTMtEEv+xoKyP97c49V20Q1lNP6uu9uFOw9DPVzCNSdy12RTrYR
-LePuJhRsuTygiZPyMLst2jMjAs6M5687xm/AF6igKPU+onFX0/hzlnPV0pmVu3qj
-f2zz/plZeuRpI+Fnx3DwzH5n29SeIBGWQzC1CRu1LDBTkFz6Qc/16qLrclR8wY+6
-LHu6ZjzA8434y2GnamdcPXjSzkUTixZOXcz3CvimeQKBgQD/uvrFUxe4wAvbD7ya
-Tj7dVb9z23rOm/IpGKeIY/jBeGwwv9W40l9EwYEUaORoE4lobrqNiiQwUueASr94
-ny86viDKHdEs/P9pP9YClydxR+ogx8WBHOhx+cYlRvnVtJSZfGq/UqzKliuNfFEd
-ALcfE2eVOTD+utgUreFnk/z8JQKBgQDtlGLGNsSaBMrqv1Wt5RgmjnMnjRYe2Hok
-hBqZrwyoM5E5mCKKWvwWLVB0lj/5I2VCnZ+6pHrFA0Tj2gvDZqOL83RLYz9xY7Ec
-K9X1ht+3Q9W8KSbMl91jHc8P6eKwY0ErKsYhYdlcSRrveM0uM3fOicTihqxRWQdb
-2XqUCWkdwwKBgQDWJV2Zn9tdenRzHNpy3NMHxaZs/n34Rd6jS2H/dLf6Sz1OFVaD
-Tqc4jFHrJWsfPDz0lsThgayMSuBRLkboW2TRbCVJG27unW3EVRCBWtJMqkwE50Uc
-uXhs+RxUWvsbWfyWCvnY/QJ1IwuVj2TdRJwUCcvTyfCdXxlTN8hpVCOlgQKBgQDq
-Nyw9XsbpVCo7zQ8JlV3+vNaXukaBeEbJ8xZKRkGDHPthvTLoFRSKRHgZx/ofgh6U
-0tIibX6+9R8YReDs7SX0lbkjjR+BiJeVPz36hNHOWWi/zA39CwZtbXixppEd9WvM
-w6l6RX3EtimAxiX5EzJcgoOAEuaUd+GCUl++y5w+gwKBgQCDkskEgatuhstW9QtK
-9qQiWk1MCC8iBjOBiNRAuhw1DsYsp/vjKXeS2gMk9SZaPYyFqt/t6xbnT96NNpZL
-8BXZVO2oZdKbxCl5+EVyQuSd+dHeujFns9qoB5bBtu+S0ckEG9S1/cV2JASkbOmH
-F3ZfBaeqR47s3+rCqSu91gtInw==
------END PRIVATE KEY-----
diff --git a/pushy/src/test/resources/server-key.pem b/pushy/src/test/resources/server-key.pem
deleted file mode 100644
index 740b67ee7..000000000
--- a/pushy/src/test/resources/server-key.pem
+++ /dev/null
@@ -1,28 +0,0 @@
------BEGIN PRIVATE KEY-----
-MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC4YBLiSXzbj8SV
-6ATlQfmRP5rEoKOQ4Ok/NLqk0NGN/Hfh4BEaV3TtHkptDOSDcf/yFX9ucEVR+bnh
-xgR16e4th652TYDFw9yTKPa6yNEie/IfdaHuPikV/qtrGiCSWemq7A2VBCkB0y8/
-hxDzg4LQ+cMxuq1fT/3W4GBejhKcQ8vfV/2RsGB+6dk9tV5kQWMtAlmCLiVWKx7C
-8boWoM8PgQ97Aps9fXcM3P7jxGsCWhL7bg0V76Tfh4odtGzJ9NOVPMauFOybklD7
-7A2GH/HSOUgOjalWp4dWB/IqVTusMjv9VuUMgePqVda9hSx7hZ5bntx9Zrayl7ir
-l07jI87nAgMBAAECggEBAI7HH3iLDhx9HfA0Z64dxCT9y11fRKsJ8LZYn/zIFK9O
-houtV7E9bre9EEeYh1FfM6QFj9Q3LwdHSvISxRuG10H841aLuB/uB98SBtcocgOx
-VhOUpZx4GJsGxzo+VmDfLfuFpxLx0Muv/dPFRZQ+EEzCTa0x8dZwfJMs2JQAk1rc
-Npql/AY0EbpN/LgtzLxv+KlH+AUCHvsl7a9Y5M3VzWsvU0Pmn/Hbfk7SnkYLmuyA
-2urGVmIiI5fzFlxC2v39oLmijlaQEbQTetuUSOTCFehZfTb//SIKp8/rv7BsUfU5
-RMnRQ0VP03t1n1szA3OLswiE8WPlEymZ29lDkDcGIAkCgYEA9Oev4ERmkv+5Z27t
-o6/z6CtIx9rw5L9G+SaQOAbTlJzLgtuaxoA2PcLnjH9WzID7AyPe9W3uG367EdYc
-aTcb75y8GYKw9xdgAnMpNNFXMoU0UCQJaf4q78bfTGBPlA0+DGGzLgcMLDWvNZWT
-v+Q6TZRQqk9q/LaOYfCSAkeEGp0CgYEAwLpi7eui70lJuJpNaX2s0Oim3536Mwd2
-v5WzDB+yswu1CZ4hRJpa0d7R0FpcStieF7AL0gc/pc6WjjSH0hhyGwWtQKMabtRb
-y8MaHpa8WvLaCLnec+02RWVS+IDvpRS3JpF5oWOE6rtRn/8rLB9yi/OMbqfhROob
-8dhgRlCVhlMCgYEAoiLxMUyXjCJ5IJ0z/vZyR/bADHBKo2ZvGmwJds4uLWlQ4qV4
-5onjXyg2G7ICSQnrJL0O0vWgeduBBeH2lUHC4POnJEx91FhJW9XaJfmh/PRrGdOB
-2AZJbsz+8JWimaXaul/EPGi2Cl7QTG1mj9gNMWdLsDU742sJAJZAU/n835ECgYA1
-pq1LlkExY7GGFk5eg4HMVje+IHg6JGXGR6IkSd6xQR0QpFiWhHGr4t99pOn0XAEG
-jxd7TFFHkw8OX0lAD1YUd6wXRlBhcfRr8NAgm82rc9eGfleS5gIpp19Fln8f8Xha
-4Dx/1Ph3b9X1OE+IZOi6VP5O/6USTimhVZ7XdC9ryQKBgQCxCRPwzGGN8olgrAIF
-0VGsznrvoKE01mSKWrJ5J+FMxnD9YIWuvW9OZoO+tluDfezIInnBjY5iSNGNAhy8
-0rzdgTLT1RKsNUQB6Y9BHq3qkF+FxJg6/gbabKQdx7hrrxKiw/1LoEM8MK2XPuny
-NnbPOKIZgik5JR+uduVCM/5cqg==
------END PRIVATE KEY-----
diff --git a/pushy/src/test/resources/server.p12 b/pushy/src/test/resources/server.p12
deleted file mode 100644
index aa276f66a..000000000
Binary files a/pushy/src/test/resources/server.p12 and /dev/null differ
diff --git a/pushy/src/test/resources/single-topic-client-unprotected.p12 b/pushy/src/test/resources/single-topic-client-unprotected.p12
deleted file mode 100644
index b95de08b1..000000000
Binary files a/pushy/src/test/resources/single-topic-client-unprotected.p12 and /dev/null differ
diff --git a/pushy/src/test/resources/single-topic-client.p12 b/pushy/src/test/resources/single-topic-client.p12
deleted file mode 100644
index 61a352c38..000000000
Binary files a/pushy/src/test/resources/single-topic-client.p12 and /dev/null differ
diff --git a/pushy/src/test/resources/single-topic-client.pem b/pushy/src/test/resources/single-topic-client.pem
deleted file mode 100644
index 1796700f4..000000000
--- a/pushy/src/test/resources/single-topic-client.pem
+++ /dev/null
@@ -1,19 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIDITCCAgmgAwIBAgIBATANBgkqhkiG9w0BAQ0FADAYMRYwFAYDVQQDDA1QdXNo
-eVRlc3RSb290MCAXDTIxMDUzMDE3NDA1MloYDzIxMjEwNTA2MTc0MDUyWjBaMTIw
-MAYDVQQDDClBcHBsZSBQdXNoIFNlcnZpY2VzOiBjb20uZWF0dGhlcGF0aC5wdXNo
-eTEkMCIGCgmSJomT8ixkAQEMFGNvbS5lYXR0aGVwYXRoLnB1c2h5MIIBIjANBgkq
-hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqw4S2wls3RZvUP3ZD3SKwRYWF60a54DN
-oqfTl3aDL9lOSL/RMRUQS/4XwJN1nNwklVJteAtZbRbKOSf+DQHR4NsTwOWBYa42
-Nx6RIWVIdzYOWdhtOApoB2B5eilG/3jiOcZ2NkHmNQ2m47c84m4xWliaLJ9EULYJ
-n8qQqbMEK+vi+wLclkQuNsg1L7qCAjLf8brIxIX0RKGiDsbG/joWyO0hbzCxRlax
-JV0aOzHvLJF2VUZg5MMEOl1dTeb/EHIbUaI4h/zMAr4T/Ea3OxzEf7nrlKSY6yik
-oboX4D2Q780KYe2KqvAZ87QXXgJc4UyaCplAmbNmq/LFYxZFJHw9hwIDAQABozIw
-MDAJBgNVHRMEAjAAMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcD
-AjANBgkqhkiG9w0BAQ0FAAOCAQEAEwESey8ATqtJAYqCgGkBivL10RIG/8+BC36E
-cpRsxvzQaU2h8yGOyD6Y2TACHYHASDdGnMyNdeckue0vf8Ou3royYAe1nvDiG1ho
-dc8hSK2AgJwS7+BbxQfQgrwCDUOkPkKXtRfyww01fc3pYg6FO3cmNFagGjnw/Unw
-OjKUDyvZEadVzOEDhWzfMXrKOe8doxC4CyGLFsdUPbMVtO/KdsY1gzXal9IvS9P6
-eIjzGWLpeBz1WxM4PVZviwlC70yYZ/sbKw20tbiTNuloDjL5EePhgrkBbppIZ/Pg
-Fc1vlv26E+59Muqa1oCyWJxoCw3rB50ZNamPC20SjI1wv6NQAA==
------END CERTIFICATE-----
diff --git a/pushy/src/test/resources/token-auth-private-key.p8 b/pushy/src/test/resources/token-auth-private-key.p8
deleted file mode 100644
index 418d96906..000000000
--- a/pushy/src/test/resources/token-auth-private-key.p8
+++ /dev/null
@@ -1,5 +0,0 @@
------BEGIN PRIVATE KEY-----
-MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgupTFGBOZFMLlNJYr
-obN5w/Gpp4Un37JmkLIQ1eSVRM6hRANCAARWvXN7hpi86fvQUY7rE8IJ/C6bUSPs
-FwT2Z60gp37DKGeuTwMb61g4KflNiEbYDHFbBOAk9cTK3+yb/RoeSBz6
------END PRIVATE KEY-----
diff --git a/pushy/src/test/resources/token-auth-public-key.p8 b/pushy/src/test/resources/token-auth-public-key.p8
deleted file mode 100644
index a4f3a2dc8..000000000
--- a/pushy/src/test/resources/token-auth-public-key.p8
+++ /dev/null
@@ -1,4 +0,0 @@
------BEGIN PUBLIC KEY-----
-MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEVr1ze4aYvOn70FGO6xPCCfwum1Ej
-7BcE9metIKd+wyhnrk8DG+tYOCn5TYhG2AxxWwTgJPXEyt/sm/0aHkgc+g==
------END PUBLIC KEY-----