This is an automated email from the ASF dual-hosted git repository.
elserj pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-ratis-thirdparty.git
The following commit(s) were added to refs/heads/master by this push:
new 83c7710 RATIS-441. Add test for netty-tcnative into thirdparty
83c7710 is described below
commit 83c771075fce4724ed18c6d02644b88702d2414f
Author: Xiaoyu Yao <[email protected]>
AuthorDate: Wed Jan 2 18:14:49 2019 -0500
RATIS-441. Add test for netty-tcnative into thirdparty
Signed-off-by: Josh Elser <[email protected]>
---
pom.xml | 18 ++-
test/pom.xml | 156 +++++++++++++++++++++
.../apache/ratis/thirdparty/demo/GreeterImpl.java | 33 +++++
.../apache/ratis/thirdparty/demo/GrpcClient.java | 78 +++++++++++
.../apache/ratis/thirdparty/demo/GrpcServer.java | 71 ++++++++++
.../ratis/thirdparty/demo/GrpcSslClient.java | 90 ++++++++++++
.../ratis/thirdparty/demo/GrpcSslClientConfig.java | 62 ++++++++
.../ratis/thirdparty/demo/GrpcSslConfig.java | 56 ++++++++
.../ratis/thirdparty/demo/GrpcSslServer.java | 89 ++++++++++++
.../ratis/thirdparty/demo/GrpcSslServerConfig.java | 60 ++++++++
test/src/main/proto/hello.proto | 39 ++++++
.../apache/ratis/thirdparty/demo/GrpcSslTest.java | 80 +++++++++++
.../org/apache/ratis/thirdparty/demo/GrpcTest.java | 60 ++++++++
test/src/test/resources/log4j.properties | 23 +++
test/src/test/resources/ssl/ca.crt | 27 ++++
test/src/test/resources/ssl/ca.key | 54 +++++++
test/src/test/resources/ssl/client.crt | 27 ++++
test/src/test/resources/ssl/client.csr | 26 ++++
test/src/test/resources/ssl/client.key | 51 +++++++
test/src/test/resources/ssl/client.pem | 52 +++++++
test/src/test/resources/ssl/generate.sh | 50 +++++++
test/src/test/resources/ssl/server.crt | 27 ++++
test/src/test/resources/ssl/server.csr | 26 ++++
test/src/test/resources/ssl/server.key | 51 +++++++
test/src/test/resources/ssl/server.pem | 52 +++++++
25 files changed, 1356 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index f253522..8421dea 100644
--- a/pom.xml
+++ b/pom.xml
@@ -31,6 +31,7 @@
<modules>
<module>misc</module>
<module>hadoop</module>
+ <module>test</module>
</modules>
<licenses>
<license>
@@ -63,6 +64,7 @@
<maven.compiler.target>1.8</maven.compiler.target>
<java.min.version>${maven.compiler.source}</java.min.version>
<maven.min.version>3.3.9</maven.min.version>
+ <protobuf-maven-plugin.version>0.5.1</protobuf-maven-plugin.version>
<!--Version of protobuf to be shaded -->
<shaded.protobuf.version>3.5.0</shaded.protobuf.version>
@@ -228,11 +230,9 @@
<pomFileName>pom.xml</pomFileName>
</configuration>
</plugin>
-
<plugin>
<groupId>org.apache.rat</groupId>
<artifactId>apache-rat-plugin</artifactId>
- <version>${apache-rat-plugin.version}</version>
<!-- target/ should get picked up from Apache parent pom.. -->
<configuration>
<!-- Lets us define more exclusions in child-modules without
overriding these -->
@@ -246,6 +246,20 @@
<artifactId>maven-bundle-plugin</artifactId>
<version>${maven-bundle-plugin.version}</version>
</plugin>
+ <plugin>
+ <groupId>org.xolstice.maven.plugins</groupId>
+ <artifactId>protobuf-maven-plugin</artifactId>
+ <version>${protobuf-maven-plugin.version}</version>
+ <configuration>
+ <protocArtifact>
+
com.google.protobuf:protoc:${shaded.protobuf.version}:exe:${os.detected.classifier}
+ </protocArtifact>
+ <!-- Place these in a location that compiler-plugin is already
looking -->
+
<outputDirectory>${project.build.directory}/generated-sources</outputDirectory>
+ <!-- With multiple executions, this must be `false` otherwise we
wipe out the previous execution -->
+ <clearOutputDirectory>false</clearOutputDirectory>
+ </configuration>
+ </plugin>
</plugins>
</pluginManagement>
diff --git a/test/pom.xml b/test/pom.xml
new file mode 100644
index 0000000..a60e5c4
--- /dev/null
+++ b/test/pom.xml
@@ -0,0 +1,156 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License. See accompanying LICENSE file.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.apache.ratis</groupId>
+ <artifactId>ratis-thirdparty</artifactId>
+ <version>0.3.0-SNAPSHOT</version>
+ </parent>
+ <groupId>org.apache.ratis</groupId>
+ <artifactId>ratis-thirdparty-test</artifactId>
+ <name>Apache Ratis Thirdparty Test</name>
+ <packaging>jar</packaging>
+ <description>Miscellaneous tests for Apache Ratis Thirdparty
Jar</description>
+ <url>https://github.com/apache/incubator-ratis-thirdparty</url>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.ratis</groupId>
+ <artifactId>ratis-thirdparty-misc</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>4.11</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ <version>1.7.25</version>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-log4j12</artifactId>
+ <version>1.7.25</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-lang3</artifactId>
+ <version>3.8.1</version>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <extensions>
+ <extension>
+ <groupId>kr.motd.maven</groupId>
+ <artifactId>os-maven-plugin</artifactId>
+ <version>1.5.0.Final</version>
+ </extension>
+ </extensions>
+ <plugins>
+ <plugin>
+ <groupId>org.xolstice.maven.plugins</groupId>
+ <artifactId>protobuf-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>compile-protobuf</id>
+ <phase>generate-sources</phase>
+ <goals>
+ <goal>compile</goal>
+ </goals>
+ </execution>
+ <execution>
+ <id>compile-grpc</id>
+ <phase>generate-sources</phase>
+ <goals>
+ <goal>compile-custom</goal>
+ </goals>
+ <configuration>
+ <pluginId>grpc-java</pluginId>
+ <pluginArtifact>
+
io.grpc:protoc-gen-grpc-java:${shaded.grpc.version}:exe:${os.detected.classifier}
+ </pluginArtifact>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <!-- Modify the generated source to use our shaded protobuf -->
+ <plugin>
+ <groupId>com.google.code.maven-replacer-plugin</groupId>
+ <artifactId>replacer</artifactId>
+ <version>1.5.3</version>
+ <executions>
+ <execution>
+ <phase>process-sources</phase>
+ <goals>
+ <goal>replace</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <basedir>${project.build.directory}/generated-sources</basedir>
+ <includes>
+ <include>**/*.java</include>
+ </includes>
+ <replacements>
+ <replacement>
+ <token>([^\.])com.google</token>
+ <value>$1org.apache.ratis.thirdparty.com.google</value>
+ </replacement>
+ <replacement>
+ <token>([^\.])io.grpc</token>
+ <value>$1org.apache.ratis.thirdparty.io.grpc</value>
+ </replacement>
+ </replacements>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>8</source>
+ <target>8</target>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <systemPropertyVariables>
+
<log4j.configuration>file:///${project.basedir}/src/test/resources/log4j.properties</log4j.configuration>
+ </systemPropertyVariables>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.rat</groupId>
+ <artifactId>apache-rat-plugin</artifactId>
+ <!-- target/ should get picked up from Apache parent pom.. -->
+ <configuration>
+ <!-- Lets us define more exclusions in child-modules without
overriding these -->
+ <excludes combine.children="append">
+ <exclude>src/test/resources/ssl/*.crt</exclude>
+ <exclude>src/test/resources/ssl/*.csr</exclude>
+ <exclude>src/test/resources/ssl/*.key</exclude>
+ <exclude>src/test/resources/ssl/*.pem</exclude>
+ </excludes>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git
a/test/src/main/java/org/apache/ratis/thirdparty/demo/GreeterImpl.java
b/test/src/main/java/org/apache/ratis/thirdparty/demo/GreeterImpl.java
new file mode 100644
index 0000000..2474787
--- /dev/null
+++ b/test/src/main/java/org/apache/ratis/thirdparty/demo/GreeterImpl.java
@@ -0,0 +1,33 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ratis.thirdparty.demo;
+
+import org.apache.ratis.thirdparty.io.grpc.stub.StreamObserver;
+
+class GreeterImpl extends GreeterGrpc.GreeterImplBase {
+
+ @Override
+ public void hello(HelloRequest req, StreamObserver<HelloReply>
+ responseObserver) {
+ HelloReply reply = HelloReply.newBuilder()
+ .setMessage("Hello " + req.getName()).build();
+
+ responseObserver.onNext(reply);
+ responseObserver.onCompleted();
+ }
+}
\ No newline at end of file
diff --git
a/test/src/main/java/org/apache/ratis/thirdparty/demo/GrpcClient.java
b/test/src/main/java/org/apache/ratis/thirdparty/demo/GrpcClient.java
new file mode 100644
index 0000000..3ab28b5
--- /dev/null
+++ b/test/src/main/java/org/apache/ratis/thirdparty/demo/GrpcClient.java
@@ -0,0 +1,78 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ratis.thirdparty.demo;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.ratis.thirdparty.io.grpc.ManagedChannel;
+import org.apache.ratis.thirdparty.io.grpc.ManagedChannelBuilder;
+import org.apache.ratis.thirdparty.io.grpc.StatusRuntimeException;
+import java.util.concurrent.TimeUnit;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * gRPC Demo client with shaded ratis thirdparty jar.
+ */
+public class GrpcClient {
+ private static final Logger LOG = LoggerFactory.getLogger(
+ GrpcClient.class.getName());
+
+ private final ManagedChannel channel;
+ private final GreeterGrpc.GreeterBlockingStub blockingStub;
+
+ public GrpcClient(String host, int port) {
+ this(ManagedChannelBuilder.forAddress(host, port)
+ .usePlaintext()
+ .build());
+ }
+
+ GrpcClient(ManagedChannel channel) {
+ this.channel = channel;
+ blockingStub = GreeterGrpc.newBlockingStub(channel);
+ }
+
+ public void shutdown() throws InterruptedException {
+ channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
+ }
+
+ public String greet(String name) {
+ HelloRequest request = HelloRequest.newBuilder().setName(name).build();
+ HelloReply response;
+ try {
+ response = blockingStub.hello(request);
+ LOG.trace("Greeting: " + response.getMessage());
+ return response.getMessage();
+ } catch (StatusRuntimeException e) {
+ LOG.warn("RPC failed: {0}", e.getStatus());
+ return StringUtils.EMPTY;
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ GrpcClient client = new GrpcClient("localhost", 50001);
+ try {
+ String user = "testuser";
+ if (args.length > 0) {
+ user = args[0];
+ }
+ client.greet(user);
+ } finally {
+ client.shutdown();
+ }
+ }
+}
\ No newline at end of file
diff --git
a/test/src/main/java/org/apache/ratis/thirdparty/demo/GrpcServer.java
b/test/src/main/java/org/apache/ratis/thirdparty/demo/GrpcServer.java
new file mode 100644
index 0000000..882832e
--- /dev/null
+++ b/test/src/main/java/org/apache/ratis/thirdparty/demo/GrpcServer.java
@@ -0,0 +1,71 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ratis.thirdparty.demo;
+
+import org.apache.ratis.thirdparty.io.grpc.Server;
+import org.apache.ratis.thirdparty.io.grpc.ServerBuilder;
+import java.io.IOException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * gRPC Demo server with shaded ratis thirdparty jar.
+ */
+public class GrpcServer {
+ private static final Logger LOG =
+ LoggerFactory.getLogger(GrpcServer.class.getName());
+
+ private Server server;
+ private int port;
+
+ public GrpcServer(int port) {
+ this.port = port;
+ }
+
+ void start() throws IOException {
+ server = ServerBuilder.forPort(port)
+ .addService(new GreeterImpl())
+ .build()
+ .start();
+ LOG.info("GrpcServer started, listening on " + port);
+ Runtime.getRuntime().addShutdownHook(new Thread(() -> {
+ LOG.info("*** shutting down gRPC server since JVM is " +
+ "shutting down");
+ GrpcServer.this.stop();
+ LOG.info("*** gRPC demo server shut down complete");
+ }));
+ }
+
+ private void stop() {
+ if (server != null) {
+ server.shutdown();
+ }
+ }
+
+ void blockUntilShutdown() throws InterruptedException {
+ if (server != null) {
+ server.awaitTermination();
+ }
+ }
+
+ public static void main(String[] args) throws IOException,
InterruptedException {
+ final GrpcServer server = new GrpcServer(50001);
+ server.start();
+ server.blockUntilShutdown();
+ }
+}
\ No newline at end of file
diff --git
a/test/src/main/java/org/apache/ratis/thirdparty/demo/GrpcSslClient.java
b/test/src/main/java/org/apache/ratis/thirdparty/demo/GrpcSslClient.java
new file mode 100644
index 0000000..c2626d2
--- /dev/null
+++ b/test/src/main/java/org/apache/ratis/thirdparty/demo/GrpcSslClient.java
@@ -0,0 +1,90 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ratis.thirdparty.demo;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.ratis.thirdparty.io.grpc.ManagedChannel;
+import org.apache.ratis.thirdparty.io.grpc.ManagedChannelBuilder;
+import org.apache.ratis.thirdparty.io.grpc.StatusRuntimeException;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.ratis.thirdparty.io.grpc.netty.GrpcSslContexts;
+import org.apache.ratis.thirdparty.io.grpc.netty.NettyChannelBuilder;
+import org.apache.ratis.thirdparty.io.netty.handler.ssl.SslContextBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * gRPC Demo SSL client with shaded ratis thirdparty jar.
+ */
+public class GrpcSslClient {
+ private static final Logger LOG = LoggerFactory.getLogger(
+ GrpcSslClient.class.getName());
+
+ private final ManagedChannel channel;
+ private final GreeterGrpc.GreeterBlockingStub blockingStub;
+
+ public GrpcSslClient(String host, int port,
+ GrpcSslClientConfig conf) throws IOException {
+ channel = initChannel(host, port, conf);
+ blockingStub = GreeterGrpc.newBlockingStub(channel);
+ }
+
+ private ManagedChannel initChannel(String host, int port,
+ GrpcSslClientConfig conf) throws IOException {
+ NettyChannelBuilder channelBuilder =
+ NettyChannelBuilder.forAddress(host, port);
+ // Hacky way to work around hostname verify of the certificate
+ //channelBuilder.overrideAuthority(
+ // InetAddress.getLocalHost().getHostName()) ;
+ SslContextBuilder sslContextBuilder = GrpcSslContexts.forClient();
+ if (conf.getTrustCertCollection() != null) {
+ sslContextBuilder.trustManager(conf.getTrustCertCollection());
+ }
+ if (conf.isMutualAuthn()) {
+ sslContextBuilder.keyManager(conf.getCertChain(), conf.getPrivateKey());
+ }
+ if (conf.encryptionEnabled()) {
+ sslContextBuilder.ciphers(conf.getTlsCipherSuitesWithEncryption());
+ } else {
+ sslContextBuilder.ciphers(conf.getTlsCipherSuitesNoEncryption());
+ }
+ return channelBuilder.useTransportSecurity()
+ .sslContext(sslContextBuilder.build()).build();
+ }
+
+ public void shutdown() throws InterruptedException {
+ channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
+ }
+
+ public String greet(String name) {
+ HelloRequest request = HelloRequest.newBuilder().setName(name).build();
+ HelloReply response;
+ try {
+ response = blockingStub.hello(request);
+ LOG.trace("Greeting: " + response.getMessage());
+ return response.getMessage();
+ } catch (StatusRuntimeException e) {
+ LOG.warn("RPC failed: {0}", e.getStatus());
+ return StringUtils.EMPTY;
+ }
+ }
+}
diff --git
a/test/src/main/java/org/apache/ratis/thirdparty/demo/GrpcSslClientConfig.java
b/test/src/main/java/org/apache/ratis/thirdparty/demo/GrpcSslClientConfig.java
new file mode 100644
index 0000000..95a3096
--- /dev/null
+++
b/test/src/main/java/org/apache/ratis/thirdparty/demo/GrpcSslClientConfig.java
@@ -0,0 +1,62 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ratis.thirdparty.demo;
+
+import java.io.File;
+
+/**
+ * gRPC SSL client configurations.
+ */
+public class GrpcSslClientConfig extends GrpcSslConfig{
+ private File trustCertCollection;
+
+ // Only needed for mTLS
+ private Boolean mutualAuthn;
+ private File privateKey;
+ private File certChain;
+
+
+ public GrpcSslClientConfig(File privateKey,
+ File trustCertCollection,
+ File certChain,
+ Boolean mutualAuthn,
+ Boolean encryption) {
+ super(encryption);
+ this.privateKey = privateKey;
+ this.trustCertCollection = trustCertCollection;
+ this.certChain = certChain;
+ this.mutualAuthn = mutualAuthn;
+ }
+
+ public File getPrivateKey() {
+ return privateKey;
+ }
+
+ public File getTrustCertCollection() {
+ return trustCertCollection;
+ }
+
+ public File getCertChain() {
+ return certChain;
+ }
+
+ public Boolean isMutualAuthn() {
+ return mutualAuthn && privateKey!= null && certChain!= null;
+ }
+
+}
diff --git
a/test/src/main/java/org/apache/ratis/thirdparty/demo/GrpcSslConfig.java
b/test/src/main/java/org/apache/ratis/thirdparty/demo/GrpcSslConfig.java
new file mode 100644
index 0000000..68f9d13
--- /dev/null
+++ b/test/src/main/java/org/apache/ratis/thirdparty/demo/GrpcSslConfig.java
@@ -0,0 +1,56 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ratis.thirdparty.demo;
+
+import org.apache.ratis.thirdparty.com.google.common.collect.Lists;
+
+import java.util.List;
+
+public class GrpcSslConfig {
+ // TODO: allow configure cipher suites
+ private final List<String> tlsCipherSuitesWithEncryption =
Lists.newArrayList(
+ "TLS_RSA_WITH_AES_128_GCM_SHA256",
+ "TLS_RSA_WITH_AES_128_CBC_SHA",
+ "SSL_RSA_WITH_3DES_EDE_CBC_SHA");
+
+ // "RSA" in this case refers to the key exchange algorithm,
+ // "SHA" refers to the message digest algorithm to provide integrity
+ // "NULL" is the encryption algorithm, to disable encryption.
+ // TODO: suppot NULL cipher from tcnative
+ private final List<String> tlsCipherSuitesNoEncryption = Lists
+ .newArrayList("TLS_RSA_WITH_AES_128_GCM_SHA256");
+
+ private Boolean encryption;
+
+ protected GrpcSslConfig(Boolean encryption) {
+ this.encryption = encryption;
+ }
+
+ public Boolean encryptionEnabled() {
+ return encryption;
+ }
+
+ public List<String> getTlsCipherSuitesWithEncryption() {
+ return tlsCipherSuitesWithEncryption;
+ }
+
+ public List<String> getTlsCipherSuitesNoEncryption() {
+ return tlsCipherSuitesNoEncryption;
+ }
+
+}
diff --git
a/test/src/main/java/org/apache/ratis/thirdparty/demo/GrpcSslServer.java
b/test/src/main/java/org/apache/ratis/thirdparty/demo/GrpcSslServer.java
new file mode 100644
index 0000000..8d17d6b
--- /dev/null
+++ b/test/src/main/java/org/apache/ratis/thirdparty/demo/GrpcSslServer.java
@@ -0,0 +1,89 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ratis.thirdparty.demo;
+
+import org.apache.ratis.thirdparty.io.grpc.Server;
+import org.apache.ratis.thirdparty.io.grpc.ServerBuilder;
+import java.io.IOException;
+
+import org.apache.ratis.thirdparty.io.grpc.netty.GrpcSslContexts;
+import org.apache.ratis.thirdparty.io.grpc.netty.NettyServerBuilder;
+import org.apache.ratis.thirdparty.io.netty.handler.ssl.ClientAuth;
+import org.apache.ratis.thirdparty.io.netty.handler.ssl.SslContextBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static
org.apache.ratis.thirdparty.io.netty.handler.ssl.SslProvider.OPENSSL;
+
+/**
+ * gRPC Demo SSL server with shaded ratis thirdparty jar.
+ */
+public class GrpcSslServer {
+ private static final Logger LOG =
+ LoggerFactory.getLogger(GrpcServer.class.getName());
+
+ private Server server;
+ private int port;
+ private GrpcSslServerConfig conf;
+
+ public GrpcSslServer(int port, GrpcSslServerConfig conf) {
+ this.port = port;
+ this.conf = conf;
+ }
+
+ void start() throws IOException {
+ NettyServerBuilder nettyServerBuilder =
+ NettyServerBuilder.forPort(port).addService(new GreeterImpl());
+ SslContextBuilder sslContextBuilder = SslContextBuilder.forServer(
+ conf.getServerCertChain(), conf.getPrivateKey());
+ if (conf.isMutualAuthn()) {
+ sslContextBuilder.clientAuth(ClientAuth.REQUIRE);
+ sslContextBuilder.trustManager(conf.getClientCertChain());
+ }
+ sslContextBuilder =
+ GrpcSslContexts.configure(sslContextBuilder, OPENSSL);
+ if (conf.encryptionEnabled()) {
+ sslContextBuilder.ciphers(conf.getTlsCipherSuitesWithEncryption());
+ } else {
+ sslContextBuilder.ciphers(conf.getTlsCipherSuitesNoEncryption());
+ }
+
+ nettyServerBuilder.sslContext(sslContextBuilder.build());
+
+ server = nettyServerBuilder.build().start();
+ LOG.info("GrpcSslServer started, listening on " + port);
+ Runtime.getRuntime().addShutdownHook(new Thread(() -> {
+ LOG.info("*** shutting down GrpcSslServer since JVM is " +
+ "shutting down");
+ GrpcSslServer.this.stop();
+ LOG.info("*** GrpcSslServer shut down complete");
+ }));
+ }
+
+ private void stop() {
+ if (server != null) {
+ server.shutdown();
+ }
+ }
+
+ void blockUntilShutdown() throws InterruptedException {
+ if (server != null) {
+ server.awaitTermination();
+ }
+ }
+}
diff --git
a/test/src/main/java/org/apache/ratis/thirdparty/demo/GrpcSslServerConfig.java
b/test/src/main/java/org/apache/ratis/thirdparty/demo/GrpcSslServerConfig.java
new file mode 100644
index 0000000..ea512cb
--- /dev/null
+++
b/test/src/main/java/org/apache/ratis/thirdparty/demo/GrpcSslServerConfig.java
@@ -0,0 +1,60 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ratis.thirdparty.demo;
+
+import java.io.File;
+
+public class GrpcSslServerConfig extends GrpcSslConfig {
+ private File privateKey;
+ private File serverCertChain;
+ // Only needed for mTLS
+ private Boolean mutualAuthn;
+ private File clientCertChain;
+
+ private Boolean encryption;
+
+ public GrpcSslServerConfig(File privateKey,
+ File serverCertChain,
+ File clientCertChain,
+ Boolean mutualAuthn,
+ Boolean encryption) {
+ super(encryption);
+ this.privateKey = privateKey;
+ this.serverCertChain = serverCertChain;
+ this.clientCertChain = clientCertChain;
+ this.mutualAuthn = mutualAuthn;
+ }
+
+ public File getPrivateKey() {
+ return privateKey;
+ }
+
+ public File getServerCertChain() {
+ return serverCertChain;
+ }
+
+ public File getClientCertChain() {
+ return clientCertChain;
+ }
+
+ public Boolean isMutualAuthn() {
+ return mutualAuthn && clientCertChain!= null;
+ }
+
+
+}
diff --git a/test/src/main/proto/hello.proto b/test/src/main/proto/hello.proto
new file mode 100644
index 0000000..1fb1c34
--- /dev/null
+++ b/test/src/main/proto/hello.proto
@@ -0,0 +1,39 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+syntax = "proto3";
+
+option java_multiple_files = true;
+option java_package = "org.apache.ratis.thirdparty.demo";
+option java_outer_classname = "HelloProto";
+
+package org.apache.ratis.thirdparty.demo;
+
+service Greeter {
+ rpc Hello (HelloRequest) returns (HelloReply) {}
+}
+
+message HelloRequest {
+ string name = 1;
+}
+
+message HelloReply {
+ string message = 1;
+}
+
+
+
diff --git
a/test/src/test/java/org/apache/ratis/thirdparty/demo/GrpcSslTest.java
b/test/src/test/java/org/apache/ratis/thirdparty/demo/GrpcSslTest.java
new file mode 100644
index 0000000..dea7209
--- /dev/null
+++ b/test/src/test/java/org/apache/ratis/thirdparty/demo/GrpcSslTest.java
@@ -0,0 +1,80 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ratis.thirdparty.demo;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.IOException;
+
+public class GrpcSslTest {
+ private final static Logger LOG = LoggerFactory.getLogger(GrpcSslTest.class);
+
+ private int port = 50005;
+
+ private File getResource(String name) {
+ ClassLoader classLoader = getClass().getClassLoader();
+
+ File file = new File(classLoader.getResource(name).getFile());
+ LOG.info("Getting Resource: {}\n", file.getAbsolutePath());
+ return file;
+ }
+
+ @Test
+ public void testSslClientServer() throws InterruptedException, IOException {
+ Thread serverThread = new Thread(() -> {
+ GrpcSslServerConfig sslServerConf =
+ new GrpcSslServerConfig(
+ getResource("ssl/server.pem"),
+ getResource("ssl/server.crt"),
+ getResource("ssl/client.crt"),
+ true,
+ false);
+ GrpcSslServer server = new GrpcSslServer(port, sslServerConf);
+ try {
+ server.start();
+ server.blockUntilShutdown();
+ } catch (InterruptedException | IOException e) {
+ e.printStackTrace();
+ }
+ });
+ serverThread.start();
+
+ GrpcSslClientConfig sslClientConfig = new GrpcSslClientConfig(
+ getResource("ssl/client.pem"),
+ getResource("ssl/ca.crt"),
+ getResource("ssl/client.crt"),
+ true,
+ false);
+
+ GrpcSslClient client = new GrpcSslClient(
+ "localhost", port, sslClientConfig);
+ try {
+ /* Access a service running on the local machine on port 50051 */
+ String user = "testuser";
+ String response = client.greet(user);
+ LOG.info("Greet result: {}", response);
+ Assert.assertTrue(response.equals("Hello " + user));
+ } finally {
+ client.shutdown();
+ }
+ }
+}
diff --git a/test/src/test/java/org/apache/ratis/thirdparty/demo/GrpcTest.java
b/test/src/test/java/org/apache/ratis/thirdparty/demo/GrpcTest.java
new file mode 100644
index 0000000..ca609d0
--- /dev/null
+++ b/test/src/test/java/org/apache/ratis/thirdparty/demo/GrpcTest.java
@@ -0,0 +1,60 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ratis.thirdparty.demo;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+
+/**
+ * Unit test for demo gRPC client/server with ratis thirdparty.
+ */
+@RunWith(JUnit4.class)
+public class GrpcTest {
+
+ private final static Logger LOG = LoggerFactory.getLogger(GrpcTest.class);
+ @Test
+ public void testClientServer() throws InterruptedException {
+ Thread serverThread = new Thread(() -> {
+ GrpcServer server = new GrpcServer(50001);
+ try {
+ server.start();
+ server.blockUntilShutdown();
+ } catch (InterruptedException | IOException e) {
+ e.printStackTrace();
+ }
+ });
+ serverThread.start();
+
+ GrpcClient client = new GrpcClient("localhost", 50001);
+ try {
+ /* Access a service running on the local machine on port 50051 */
+ String user = "testuser";
+ String response = client.greet(user);
+ LOG.info("Greet result: {}", response);
+ Assert.assertTrue(response.equals("Hello " + user));
+ } finally {
+ client.shutdown();
+ }
+ }
+}
diff --git a/test/src/test/resources/log4j.properties
b/test/src/test/resources/log4j.properties
new file mode 100644
index 0000000..3bf1619
--- /dev/null
+++ b/test/src/test/resources/log4j.properties
@@ -0,0 +1,23 @@
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# log4j configuration used during build and unit tests
+
+log4j.rootLogger=INFO,stdout
+log4j.threshold=ALL
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ISO8601} [%t] %-5p %c{2}
(%F:%M(%L)) - %m%n
+
+log4j.logger.org.apache.hadoop.util.NativeCodeLoader=ERROR
+
+# for debugging low level Ozone operations, uncomment this line
+# log4j.logger.org.apache.hadoop.ozone=DEBUG
diff --git a/test/src/test/resources/ssl/ca.crt
b/test/src/test/resources/ssl/ca.crt
new file mode 100644
index 0000000..ec250cc
--- /dev/null
+++ b/test/src/test/resources/ssl/ca.crt
@@ -0,0 +1,27 @@
+-----BEGIN CERTIFICATE-----
+MIIEpDCCAowCCQDpcUU9gile6DANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDDAls
+b2NhbGhvc3QwHhcNMTgxMjAzMDU0NjA5WhcNMTkxMjAzMDU0NjA5WjAUMRIwEAYD
+VQQDDAlsb2NhbGhvc3QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDS
+m3g8uIHMMKR/BQ5yUzVhuNYnc59wu+sLO8ct4nm9iqrB0fMRbO7HU6CeL81OmdaT
+IJG6EmPBsEhR0+JyPfkwexSizJfrx7IZyadexo10+ErFEarcnCjpTvwUjYEaZxOc
+3xFD+o7E/zA4wU/oBZvrKsD2dHmRyQJhzrLYSxiKNqL9AeBGUaqf8PU10tXLY7FD
+M6jCh9s33FS1XJfhWAw7jVLTuzo03j/ROofovtfa3FACLfLNF6qbjyeenfRBpTvc
+QxjVx8M6SIiQqdN/Xe7LKm5oCfpOpd0Zsu2dSaHiBRt1sSFCfjAYvT/vS5BROXJd
+TINt31UJCFoyKocCAYD5RYwKwWKX5TT9B1kY3wEgZCRyFDd6EwzW85rRZZpF3gxr
+vqGgnEcDzY9NZQpT1YUapnIEjACfsjKyDLNnzsG+oJ4ECCNda+aiKKCDFezd5auO
+FfVWdR+wzFWJ3AH+I95qgUNmj5NTDo6+MBBDWzI8d0hcPItURABVMPuh3H5m5BZK
+LQ0JoOnabT1fKD/WhbZiCfpkv2BNKyNV31ekilxP3biKM/zlpyHDPlsrbeyaFcBt
+c3bjvcejto6Gd00ehkXW3GNFh1fBk8i9j1yVuMRhDp3Wb5QIJm8olBV94wu+LSYk
+gCCH9ZXQJPPK2bOh4EY0af6dwxJEgL2jTYttmLUvDQIDAQABMA0GCSqGSIb3DQEB
+CwUAA4ICAQARkiZnzRy5N/pxbtOdMKJnSyporjuHnx+LG2SNQ42lYwAIVLbWN0wH
+rtofN4zM4el9VSpfngjRAGHbSldB0jLiiS98MrGkUqR7JTEzTOpFrvXYE/3Jh42E
+h2N2JxDakzlbV8jAGAIXcGFzoujliD3sR/PjHa63UyrWx9YhQtv5EoHMR2YbFC9s
+biVuZyDFPLkG9b/Ydz423GHi6/3D6F5Y/fdCLWfXGQeh004FqYypDsyh0XHxHkoO
+UoeSkKAsF12p4eGlTm7o6vQSgv+SxUCZSOI+1PZbWqjCSPiQ3LSUvD1vPVSdqEpD
+zJJFkc+Sx/fvLjqOirLd6vZfRyDQaHwgiAxoxF0B/kR6Qm4ZGvxMn6uEjXELUiuC
+sVWa5joThrFocLLt+M6vdsOsr1BTbFryoRjG8CSmsQje2mxpFIYtcKnaHB5+mUXW
+hJhfZ9mZL4J1rW6uenY+V1f+1XA8RRIxG7LGxB4wz9ZbQ2TZToTlDr3hvP++BM2o
+QibCTOOJAAIBmGxs7HexNq17xO4IO/T1a4zL3TcJpdWoE4dmBD2atvwr3faS+sJp
+HPT3w2AJYe4Ib95J8IXtJfzF5fNkdzZqn8FgosjgJtqpTX5JumdkzYwOvKhWvi+U
+yeeAumDlqXSMe81bx2w6FoOkl4OvKrIApNWIrT02w9TF/aRF0g1wiw==
+-----END CERTIFICATE-----
diff --git a/test/src/test/resources/ssl/ca.key
b/test/src/test/resources/ssl/ca.key
new file mode 100644
index 0000000..5872437
--- /dev/null
+++ b/test/src/test/resources/ssl/ca.key
@@ -0,0 +1,54 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,90EB66EEF5FC0E1C
+
+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
+-----END RSA PRIVATE KEY-----
diff --git a/test/src/test/resources/ssl/client.crt
b/test/src/test/resources/ssl/client.crt
new file mode 100644
index 0000000..f70c287
--- /dev/null
+++ b/test/src/test/resources/ssl/client.crt
@@ -0,0 +1,27 @@
+-----BEGIN CERTIFICATE-----
+MIIEnDCCAoQCAQEwDQYJKoZIhvcNAQEFBQAwFDESMBAGA1UEAwwJbG9jYWxob3N0
+MB4XDTE4MTIwMzA1NDY0MloXDTE5MTIwMzA1NDY0MlowFDESMBAGA1UEAwwJbG9j
+YWxob3N0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAs5pIXTwdV0gM
+G0mBOg9jmcir8awN7F+A1dMnFi9WD9gbdPkePvLnm+fVoHDPONpCdtS4UrndNd6E
+Lu36n+FhLsacUvC3wpcoE+iGfX/gRFA90WQxcXdx7SjcIWQmBeyYSriVkeL+uDNz
+dq5vPwYaMel+Ve/VbLp0m7VDXzun7uEHO1XwrzdgLcTJCMrEDmyfo0Bz42D6gay4
+02I48uKCzJvjELDCXMoNU6eqGWGba53QScLXnS1TYTyqR9YDVT3FJs8alIYPaew4
+w4il7hdsgoHca73MgwNykxXOH6p4tNNdH1nUlb0+G6wzBJc/oNZD7ChAMR8n9pQb
+8umZuHopGrfak0QPyLoBwTRpWLvlebiT+81lED0lWhQzvwg/Y+MU2qmX+1PVLSc8
+YlfP+sj/b7796sLSMbMsQqPPcIQ4eIyxMuJrt+9zDbu/amLFiMEx78ve34AiHiqu
+ern7dgyK8VTh9zTNFyvdpZmuQTCwBLe2pWwjS8ZDH3of0O31h+JG69Ctl015TWig
+pIHPIKqwjH97HA/+BnQC5Uiq4FYlNtGFvTyJfmwbKFVfyKT7KO9dZJgO/Jrup/RD
+ptajmOkQvM6xi33li1m4ZPQBYuSclr5zk9XAVwYoaxw0hZj4CQ7+rvLfUzGRSzUy
+GKMJOy5IMhwelBJbF3B4Hh741nBwmzMCAwEAATANBgkqhkiG9w0BAQUFAAOCAgEA
+DFZfQQPS0R8LWCu1hEe9ythFVJe7iCSsoezxAg0Q15ejjsRLHzCUvg1VIvQT9b+p
+w8Jh4YVCrXoao0x124rRYgZ+gM2DDfWhWycLLcQB0dbrsxLf2lc6NZDcxR6i41c4
+9kmpjgiCptJ92b6nH9WJCpp9WZX0NFAcUCjo1PsinlgQYoO6prWBaKEzYT8JqOZB
+945OrKckmxHg9fjxG4U88LtMf6UsT6/tCEr2Byse62R9Rz/uat+zpPRWfGI7/2O8
+Wjch/tWxkShr9RxP8KiKyJMrjsOg4KmtBrGhfuavfxenwiUb8bnQUhcdpDdVIke/
+jA4VM7vBs/PA16wLOCb6Kn+GaT4+dR3ZJMWceNeTsoQluyibnM1dFo5DkuPF5YDG
+D5e4qg0DtW3uKc4TOu+YR0aQEOH3qu/1R0e9P0MssvavR62SkX29LGOqz/aFwRLR
++R2FmgBvWHbvMMNUnoWD4o4jR/LeiufyqlOrskpJymf5tsPpegCsuOTpktBFgX27
+5Dc2w7ST0LZv/lq+MDg7Y+r5zvHv3lslbcEAWPwBSiVdsereb4oJmx/XIT6zQR0x
+g2WDYL8lEKg3KuEeNV8NiYH3N6zp2QT0laOtzwdJjOZ7BcUHSF1jdf63evsKmi+d
+W1cpIyGaP6PtLsiw/+d3B5QYpsrTe9GG+omQqCAtrSE=
+-----END CERTIFICATE-----
diff --git a/test/src/test/resources/ssl/client.csr
b/test/src/test/resources/ssl/client.csr
new file mode 100644
index 0000000..a8578f0
--- /dev/null
+++ b/test/src/test/resources/ssl/client.csr
@@ -0,0 +1,26 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIIEWTCCAkECAQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MIICIjANBgkqhkiG9w0B
+AQEFAAOCAg8AMIICCgKCAgEAs5pIXTwdV0gMG0mBOg9jmcir8awN7F+A1dMnFi9W
+D9gbdPkePvLnm+fVoHDPONpCdtS4UrndNd6ELu36n+FhLsacUvC3wpcoE+iGfX/g
+RFA90WQxcXdx7SjcIWQmBeyYSriVkeL+uDNzdq5vPwYaMel+Ve/VbLp0m7VDXzun
+7uEHO1XwrzdgLcTJCMrEDmyfo0Bz42D6gay402I48uKCzJvjELDCXMoNU6eqGWGb
+a53QScLXnS1TYTyqR9YDVT3FJs8alIYPaew4w4il7hdsgoHca73MgwNykxXOH6p4
+tNNdH1nUlb0+G6wzBJc/oNZD7ChAMR8n9pQb8umZuHopGrfak0QPyLoBwTRpWLvl
+ebiT+81lED0lWhQzvwg/Y+MU2qmX+1PVLSc8YlfP+sj/b7796sLSMbMsQqPPcIQ4
+eIyxMuJrt+9zDbu/amLFiMEx78ve34AiHiquern7dgyK8VTh9zTNFyvdpZmuQTCw
+BLe2pWwjS8ZDH3of0O31h+JG69Ctl015TWigpIHPIKqwjH97HA/+BnQC5Uiq4FYl
+NtGFvTyJfmwbKFVfyKT7KO9dZJgO/Jrup/RDptajmOkQvM6xi33li1m4ZPQBYuSc
+lr5zk9XAVwYoaxw0hZj4CQ7+rvLfUzGRSzUyGKMJOy5IMhwelBJbF3B4Hh741nBw
+mzMCAwEAAaAAMA0GCSqGSIb3DQEBCwUAA4ICAQAx40dL6kO+40hrkCHJoC0q/B9b
+2MEUNycgXwLufuuZAgbCTW8u0sRKeH67qsSkgsvjAagH+WZA+C2kXn78RVVFycEb
+raqkppXIX0IeGEByOHre/7p1fHw2j3SUeGxVbArletj0x9BWJKLmKDCpaibvN1CL
+kk4kzq8NkQIAtG0x59e2/dpm0V43lKZxiizgFEHiFPVi5pKn2z+KXBKZCe+MHxGX
+Qcy7gKZyeYmZS3aDR7vFvPEJ5pSLaeKpCPFzw3nTp30XmUYf8N1N91f7JMASQAoP
+v22mOP68h6HrX8CWQZAt/khukpRs/Sl+jH6GdLuNNtWg+4OeIGz4peZZZzwOQTHj
+hfwyhoO4m7H5ot+rA+GQ74vkmvQ/6lRnmVijE9Meycvs+DmUsvcZBNTFRmukFM/n
+PowTH36ltCC+x0eu5aQ9k1b9mcly20gVar1NA/krjClIfzisiz8nR8711Fs3vvjS
+f4XDQf7XyAvENv+h6AwcwWMTBKVOQLifIaWC8yW++VrDCcFkgLAxq9o8kExboJ3r
+iexo72O0mRkH5hyR21VjEhZKfNXi+cLF2KmR2qp81+b7IdI8bgjt888xxAYm034Y
+qOvmBYaiMq8fs2TWyrv96uoAKSoBd5WVqaF28/irjnvHEn0pUX/cGB6fa6NMWNIw
+ahISxG4PjSVJndbAcA==
+-----END CERTIFICATE REQUEST-----
diff --git a/test/src/test/resources/ssl/client.key
b/test/src/test/resources/ssl/client.key
new file mode 100644
index 0000000..c293e10
--- /dev/null
+++ b/test/src/test/resources/ssl/client.key
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+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
+-----END RSA PRIVATE KEY-----
diff --git a/test/src/test/resources/ssl/client.pem
b/test/src/test/resources/ssl/client.pem
new file mode 100644
index 0000000..ec4c166
--- /dev/null
+++ b/test/src/test/resources/ssl/client.pem
@@ -0,0 +1,52 @@
+-----BEGIN PRIVATE KEY-----
+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
+LZwb88Kw2jnRtUmfGI8GwPeQwDsKdA==
+-----END PRIVATE KEY-----
diff --git a/test/src/test/resources/ssl/generate.sh
b/test/src/test/resources/ssl/generate.sh
new file mode 100755
index 0000000..45a5a53
--- /dev/null
+++ b/test/src/test/resources/ssl/generate.sh
@@ -0,0 +1,50 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Changes these CN's to match your hosts in your environment if needed.
+SERVER_CN=localhost
+CLIENT_CN=localhost # Used when doing mutual TLS
+
+echo Generate CA key:
+openssl genrsa -passout pass:1111 -des3 -out ca.key 4096
+echo Generate CA certificate:
+# Generates ca.crt which is the trustCertCollectionFile
+openssl req -passin pass:1111 -new -x509 -days 365 -key ca.key -out ca.crt
-subj "/CN=${SERVER_CN}"
+echo Generate server key:
+openssl genrsa -passout pass:1111 -des3 -out server.key 4096
+echo Generate server signing request:
+openssl req -passin pass:1111 -new -key server.key -out server.csr -subj
"/CN=${SERVER_CN}"
+echo Self-signed server certificate:
+# Generates server.crt which is the certChainFile for the server
+openssl x509 -req -passin pass:1111 -days 365 -in server.csr -CA ca.crt -CAkey
ca.key -set_serial 01 -out server.crt
+echo Remove passphrase from server key:
+openssl rsa -passin pass:1111 -in server.key -out server.key
+echo Generate client key
+openssl genrsa -passout pass:1111 -des3 -out client.key 4096
+echo Generate client signing request:
+openssl req -passin pass:1111 -new -key client.key -out client.csr -subj
"/CN=${CLIENT_CN}"
+echo Self-signed client certificate:
+# Generates client.crt which is the clientCertChainFile for the client (need
for mutual TLS only)
+openssl x509 -passin pass:1111 -req -days 365 -in client.csr -CA ca.crt -CAkey
ca.key -set_serial 01 -out client.crt
+echo Remove passphrase from client key:
+openssl rsa -passin pass:1111 -in client.key -out client.key
+echo Converting the private keys to X.509:
+# Generates client.pem which is the clientPrivateKeyFile for the Client
(needed for mutual TLS only)
+openssl pkcs8 -topk8 -nocrypt -in client.key -out client.pem
+# Generates server.pem which is the privateKeyFile for the Server
+openssl pkcs8 -topk8 -nocrypt -in server.key -out server.pem
+
+
diff --git a/test/src/test/resources/ssl/server.crt
b/test/src/test/resources/ssl/server.crt
new file mode 100644
index 0000000..6639d45
--- /dev/null
+++ b/test/src/test/resources/ssl/server.crt
@@ -0,0 +1,27 @@
+-----BEGIN CERTIFICATE-----
+MIIEnDCCAoQCAQEwDQYJKoZIhvcNAQEFBQAwFDESMBAGA1UEAwwJbG9jYWxob3N0
+MB4XDTE4MTIwMzA1NDYzOFoXDTE5MTIwMzA1NDYzOFowFDESMBAGA1UEAwwJbG9j
+YWxob3N0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA2k4udE4yTvUe
+vK053jfYbouwbdAgxozgYbX1FcKVs4VvT5BgS6dT7qi+7m3DInYLDAMksvjMYJmL
+ksUH7ANjWpCqc/0NKv5d6FKeK6CPIBtN5B9giENCSP3BYJGe4NZAkQQNxBLNzhFL
+qN/GELyzsPxPg2Tfn8OSPymP7/tzZxfrwa8fEenAEVIsnu0l6Y4zxKsODz5aZOLt
+rbKRBWejQeAuT8vIpZ1aCRDb/ysf9Ta8PeAv3o9urRQrwDYztJARnZh5nz8474zV
+gU5pqpsxRuRrFDWWui8MfFOIUthp2OE53SEWlVUpowI02PwzeWEpx8f9QtcgHyn0
+86+FvBWfvha1V8Pl9tqZbMjtplim8zdk7oOhu7FyPL+RT7M8O/fIS+mX1LbbFNms
+GM05FdYsWg+3DirdnmxHxOZLPLj50d6HWIUvdx+lozezSks85J3vbmnWxWkbxv2m
+rLq2CStoWDogXOarzu5xjCWguhxnHo8WAfVWuvMi/PrFI1qhhMcdQUwh00XmJrrq
+NKfMWvM52llAzJ/0CO5muVge8/tKgFRHUUj+VMsPpuGpLiIGwWdvABRH9hOCAwz9
+EuIWguXAqAHKPS2TGoJlDlPLAoM/fvQm0/a1eVHpGLUjGOqHSEuUIe4qqOd42oHB
+qO8dacBSevnBcHMvbsRYJe2So+mdEhMCAwEAATANBgkqhkiG9w0BAQUFAAOCAgEA
+YHKxSgioSbxY7ZKaopQ1DMY0H15n/J95XXeQjYJiU35ku/aUqHBHY/BPGDHO8Ogc
+4Y1e3ImyeOaj1G86ZR2tLHnkQLAEaF8eW/jNedC8G0ubLeplhJ8t3JD1QVFovAc6
+aXOXT4+PGVNumsC5UGa4H/eCW3K81SI7cacMPYD3zpHWt5+j0PcHzZVktAYpRjom
+hwM+osjge4aJc3VN1fIfpOI0z5O0Ci3iScTOR7A0nLY+/ZX+VsrApDHlOdFVCumS
+UFGVnvusnVQg1PFfQhz6tJd/ZNPZ5LuhAQLd/Wo1OEGAWpG4pwkxMYupmLafFm3Y
+CgpL6SO0vZexR8yEeAgtSWoQL3ISQXmVjRFXgKmd4V/XFkgkLqKweDk5scdSKo73
+8TFtXSU/tsTQpXttawDAVlhgwjs1qCej9Fd68mvenA1tu7Mc5N04nwQyBo3fc0Ga
+4+wsIPv2w+gMGt4t5KdBijiOB+8HqwWGxsa0HFONAMwr54Ev5OUZk2NqbVrmOIK1
+jWU0Uo1zIjkUrAfBlRCXRhyhN80my4D7CLz8kMkrbW9hM2CAafq7eiXMSdEvj2/O
+w//4xTm4i/RNrhk0dJwQv8/0nPGGIiT6ND3paKkD9d3J5SZEEy9i2ouG4reGEoP+
+tPhXDhIZGR0jlkLadkHxNReCD8bjH3Ph+ANkk0PmqhY=
+-----END CERTIFICATE-----
diff --git a/test/src/test/resources/ssl/server.csr
b/test/src/test/resources/ssl/server.csr
new file mode 100644
index 0000000..5e64e4e
--- /dev/null
+++ b/test/src/test/resources/ssl/server.csr
@@ -0,0 +1,26 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIIEWTCCAkECAQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MIICIjANBgkqhkiG9w0B
+AQEFAAOCAg8AMIICCgKCAgEA2k4udE4yTvUevK053jfYbouwbdAgxozgYbX1FcKV
+s4VvT5BgS6dT7qi+7m3DInYLDAMksvjMYJmLksUH7ANjWpCqc/0NKv5d6FKeK6CP
+IBtN5B9giENCSP3BYJGe4NZAkQQNxBLNzhFLqN/GELyzsPxPg2Tfn8OSPymP7/tz
+Zxfrwa8fEenAEVIsnu0l6Y4zxKsODz5aZOLtrbKRBWejQeAuT8vIpZ1aCRDb/ysf
+9Ta8PeAv3o9urRQrwDYztJARnZh5nz8474zVgU5pqpsxRuRrFDWWui8MfFOIUthp
+2OE53SEWlVUpowI02PwzeWEpx8f9QtcgHyn086+FvBWfvha1V8Pl9tqZbMjtplim
+8zdk7oOhu7FyPL+RT7M8O/fIS+mX1LbbFNmsGM05FdYsWg+3DirdnmxHxOZLPLj5
+0d6HWIUvdx+lozezSks85J3vbmnWxWkbxv2mrLq2CStoWDogXOarzu5xjCWguhxn
+Ho8WAfVWuvMi/PrFI1qhhMcdQUwh00XmJrrqNKfMWvM52llAzJ/0CO5muVge8/tK
+gFRHUUj+VMsPpuGpLiIGwWdvABRH9hOCAwz9EuIWguXAqAHKPS2TGoJlDlPLAoM/
+fvQm0/a1eVHpGLUjGOqHSEuUIe4qqOd42oHBqO8dacBSevnBcHMvbsRYJe2So+md
+EhMCAwEAAaAAMA0GCSqGSIb3DQEBCwUAA4ICAQBrXBBZuN2uPAd12Nrt7aWVxrJ2
+MYChpXXLdFnQutmt7Bp9cBlfnJC3EhhynFwbK3UXWURigPccy4S2L2LnURnMlNYE
+zyqKRsq4q8xvbsFKpSjw88b5chNmXYPpMSAuxw5imbEkODyvIoWzqf3UjlC5THK1
+6pXqYIGM0IyL/p8JlDndj3CrUv/oBmLy5TuJcHwl0Rjmgm/Nu4EEP+ExjaEDQnbM
+lqJhK8pZ0vrN+cWcAHkOBlBq5k394e3E1wPG82wHduwIaEsbnvnQj4Jn01G5nnMv
+0iiv6MbknFLGIdemkMOBgrGIb56PuG4kAFAo/ysUlzEVwLsy3kQ0tfneAovOUcZq
+Ay/vuz4w9VP2sSsJ9pisf/XEaFX5x5sfK/Ui3ft5y6BaXIhpJqQkAOHSt0u6V4zt
+2Jy1kmILirJZ/pOp/+wmyUq5KmJSq1gjq2+of3kAcxdcFBXX47VrWpzOUWhRJ/S0
+fL3woW+S7uTiLuCBzoEneYQQ9p559/R3SZPJymKtKilMT4KX2kbJzo8RW4+DwYPA
+hmQ/8i4WueuvIB0MxcWlLYTCl7mqom6jrdzJMZNYtl1sTkQX0+JI9X0dRtxJT02p
+lq/r/C8t1T1i90BQm7xqx7Adf+KEX8tKS2Y5wSll1s5Em7FXzdo8PqCC0ViVjE+U
+NlBdwperAfHAaCcB2Q==
+-----END CERTIFICATE REQUEST-----
diff --git a/test/src/test/resources/ssl/server.key
b/test/src/test/resources/ssl/server.key
new file mode 100644
index 0000000..d6331f6
--- /dev/null
+++ b/test/src/test/resources/ssl/server.key
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+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
+aO0pAoIBAQCoTw+2izCmlnrgA+Dk6+yMlhKyBH8q7y/pp0YMJ2ExYdx5tlLBUpzo
+NGsKU/v9NiFrjzy/N/1k11UjQRy950epJrG7rNybtEaZiMbCSvqP0wAgB4Waqai4
+1slyp0TWxt2c1T02IaUy3+HZyE8XuCzLP4kUkfN3YHH6atVqFg27NpBHlILsTTDx
+92ozHPIHZ1QutabZUbvmV9b8t6ildJ+vnyBJYookIE5moVbNrnHC5ZuE5Z0ZUv7F
+ps3R72eTha8yfsNXwMHbH2FiI6cucHbZWympastnKUBek8MvGC25i8vQR3pPXLol
+UggVvnzSg4bkkd5Toi24iL7yYY9/IE91AoIBAHjZa9KnWdW8wPhAvZX8VanYjWqQ
+mHi0vK8LBcQtaNq698jIzvAsQlJi4J+CVeNbYiynphqZCoOSZG4rzr3kAkPa4YBx
+Hpf9c1SkcqfVX0T5/i94vTQBSV/Ir2qxv9bNrKTmtzMT7/YT6H+07ZTiGBTPET+y
+1SEST2ujqOqZ7hXo/Ji3V2vqj6WgDLb5uD9+7NZP3WFJ6b6wn3bzjNQPgqU0H5Lb
+DqVhqdmeEpKpTa2xxxJG225UxZSlRsTaDTcqOtjTGPlLAjW887npebFa8jV9x85r
+ZVg3u2u4GhPkx+k/eUAJPouENP/x5aJweuJQzuckSy+mrtIE/ZpMWFzW7iw=
+-----END RSA PRIVATE KEY-----
diff --git a/test/src/test/resources/ssl/server.pem
b/test/src/test/resources/ssl/server.pem
new file mode 100644
index 0000000..293994a
--- /dev/null
+++ b/test/src/test/resources/ssl/server.pem
@@ -0,0 +1,52 @@
+-----BEGIN PRIVATE KEY-----
+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
+onB64lDO5yRLL6au0gT9mkxYXNbuLA==
+-----END PRIVATE KEY-----