Skip to content

Commit

Permalink
0004338: Use HTTP/2 for encrypted HTTPS synchronization
Browse files Browse the repository at this point in the history
  • Loading branch information
erilong committed Apr 7, 2020
1 parent 00a181d commit 93512d9
Show file tree
Hide file tree
Showing 23 changed files with 828 additions and 142 deletions.
4 changes: 3 additions & 1 deletion symmetric-assemble/common.gradle
Expand Up @@ -254,7 +254,9 @@ subprojects { subproject ->
bouncyCastleVersion = '1.64'
animalSnifferVersion = '1.18'
jnaVersion = '5.5.0'
jettyVersion = '9.4.26.v20200117'
jettyVersion = '9.4.27.v20200227'
alpnBootVersion = '8.1.13.v20181017'
alpnApiVersion = '1.1.3.v20160715'
websocketVersion = '1.1'
env = System.getenv()
}
Expand Down
Expand Up @@ -129,8 +129,9 @@ public AbstractCommandLauncher(String app, String argSyntax, String messageKeyPr
this.messageKeyPrefix = messageKeyPrefix;
TypedProperties serverProperties = new TypedProperties(System.getProperties());
boolean allowSelfSignedCerts = serverProperties.is(ServerConstants.HTTPS_ALLOW_SELF_SIGNED_CERTS, true);
String allowServerNames = serverProperties.get(ServerConstants.HTTPS_ALLOW_SELF_SIGNED_CERTS, "all");
TransportManagerFactory.initHttps(allowServerNames, allowSelfSignedCerts);
String allowServerNames = serverProperties.get(ServerConstants.HTTPS_VERIFIED_SERVERS, "all");
boolean https2Enabled = serverProperties.is(ServerConstants.HTTPS2_ENABLE, true);
TransportManagerFactory.initHttps(allowServerNames, allowSelfSignedCerts, https2Enabled);
}

protected static void initFromServerProperties() {
Expand Down
3 changes: 3 additions & 0 deletions symmetric-core/build.gradle
Expand Up @@ -9,6 +9,9 @@ apply from: symAssembleDir + '/common.gradle'
compile "com.google.code.gson:gson:$gsonVersion"
compile "org.springframework:spring-core:$springVersion"

compileOnly "org.eclipse.jetty:jetty-alpn-conscrypt-server:$jettyVersion"
compile "com.squareup.okhttp3:okhttp:4.4.1"

compileOnly ("nl.cad:tps-parse:1.0.15-SNAPSHOT") {
exclude group: 'commons-lang', module: 'commons-lang'
}
Expand Down
Expand Up @@ -33,6 +33,8 @@ public class ServerConstants {
public final static String HTTPS_ENABLE = "https.enable";
public final static String HTTPS_PORT = "https.port";

public final static String HTTPS2_ENABLE = "https2.enable";

public final static String HTTPS_VERIFIED_SERVERS = "https.verified.server.names";
public final static String HTTPS_ALLOW_SELF_SIGNED_CERTS = "https.allow.self.signed.certs";

Expand Down
Expand Up @@ -23,7 +23,6 @@
import java.io.BufferedReader;
import java.io.EOFException;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
Expand Down Expand Up @@ -51,6 +50,7 @@
import org.jumpmind.symmetric.service.IService;
import org.jumpmind.symmetric.transport.IOutgoingWithResponseTransport;
import org.jumpmind.symmetric.transport.ITransportManager;
import org.jumpmind.symmetric.web.WebConstants;
import org.jumpmind.util.AppUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -253,15 +253,16 @@ protected void sendAck(Node remote, Node local, NodeSecurity localSecurity,
int statusCode = -1;
int numberOfStatusSendRetries = parameterService.getInt(ParameterConstants.DATA_LOADER_NUM_OF_ACK_RETRIES);

for (int i = 0; i < numberOfStatusSendRetries && statusCode != HttpURLConnection.HTTP_OK; i++) {
for (int i = 0; i < numberOfStatusSendRetries && statusCode != WebConstants.SC_OK; i++) {
try {
statusCode = transportManager.sendAcknowledgement(remote, list, local,
localSecurity.getNodePassword(), parameterService.getRegistrationUrl());
exception = null;
} catch (Exception e) {
e.printStackTrace();
exception = e;
}
if (statusCode != HttpURLConnection.HTTP_OK) {
if (statusCode != WebConstants.SC_OK) {
String message = String.format("Ack was not sent successfully on try number %s of %s.", i+1, numberOfStatusSendRetries);
if (statusCode > 0) {
message += String.format(" statusCode=%s", statusCode);
Expand Down
Expand Up @@ -23,7 +23,6 @@
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.util.ArrayList;
Expand All @@ -39,6 +38,7 @@
import org.jumpmind.symmetric.service.IBandwidthService;
import org.jumpmind.symmetric.transport.BandwidthTestResults;
import org.jumpmind.symmetric.transport.IOutgoingWithResponseTransport;
import org.jumpmind.symmetric.transport.http.Http2Connection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -78,31 +78,20 @@ public double getDownloadKbpsFor(String syncUrl, long sampleSize, long maxTestDu
protected BandwidthTestResults getDownloadResultsFor(String syncUrl, long sampleSize,
long maxTestDuration) throws IOException {
byte[] buffer = new byte[1024];
InputStream is = null;
try {
BandwidthTestResults bw = new BandwidthTestResults();
URL u = new URL(String.format("%s/bandwidth?direction=pull&sampleSize=%s", syncUrl, sampleSize));
bw.start();
HttpURLConnection conn = (HttpURLConnection) u.openConnection();

conn.connect();
is = conn.getInputStream();
int r;
while (-1 != (r = is.read(buffer)) && bw.getElapsed() <= maxTestDuration) {
bw.transmitted(r);
}
is.close();
bw.stop();
log.info("{} was calculated to have a download bandwidth of {} kbps", syncUrl, bw.getKbps());
return bw;
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
BandwidthTestResults bw = new BandwidthTestResults();
URL u = new URL(String.format("%s/bandwidth?direction=pull&sampleSize=%s", syncUrl, sampleSize));
bw.start();
try (Http2Connection conn = new Http2Connection(u)) {
try (InputStream is = conn.getInputStream()) {
int r;
while (-1 != (r = is.read(buffer)) && bw.getElapsed() <= maxTestDuration) {
bw.transmitted(r);
}
}
}
bw.stop();
log.info("{} was calculated to have a download bandwidth of {} kbps", syncUrl, bw.getKbps());
return bw;
}

public double getUploadKbpsFor(Node remoteNode, Node localNode, long sampleSize, long maxTestDuration) throws IOException {
Expand Down
Expand Up @@ -25,7 +25,6 @@
import java.io.IOException;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.HttpURLConnection;
import java.net.UnknownHostException;
import java.sql.Types;
import java.util.Collection;
Expand Down Expand Up @@ -68,6 +67,7 @@
import org.jumpmind.symmetric.transport.ConnectionRejectedException;
import org.jumpmind.symmetric.transport.ITransportManager;
import org.jumpmind.symmetric.transport.ServiceUnavailableException;
import org.jumpmind.symmetric.web.WebConstants;
import org.jumpmind.util.AppUtils;
import org.jumpmind.util.RandomTimeSlot;

Expand Down Expand Up @@ -738,7 +738,7 @@ public void requestNodeCopy() {

try {
log.info("Detected that node '{}' should be copied to a new node id. Attempting to contact server to accomplish this", copyFrom.getNodeId());
copied = transportManager.sendCopyRequest(copyFrom) == HttpURLConnection.HTTP_OK;
copied = transportManager.sendCopyRequest(copyFrom) == WebConstants.SC_OK;
if (copied) {
nodeService.deleteIdentity();
}
Expand Down
Expand Up @@ -33,6 +33,7 @@
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.apache.commons.lang.ClassUtils;
import org.apache.commons.lang.StringUtils;
Expand All @@ -43,6 +44,8 @@
import org.jumpmind.symmetric.common.ParameterConstants;
import org.jumpmind.symmetric.common.ServerConstants;
import org.jumpmind.symmetric.transport.file.FileTransportManager;
import org.jumpmind.symmetric.transport.http.ConscryptHelper;
import org.jumpmind.symmetric.transport.http.Http2Connection;
import org.jumpmind.symmetric.transport.http.HttpTransportManager;
import org.jumpmind.symmetric.transport.http.SelfSignedX509TrustManager;
import org.jumpmind.symmetric.transport.internal.InternalTransportManager;
Expand All @@ -56,10 +59,10 @@ public TransportManagerFactory(ISymmetricEngine symmetricEngine) {
}

public static void initHttps(final String httpSslVerifiedServerNames,
boolean allowSelfSignedCerts) {
boolean allowSelfSignedCerts, boolean enableHttps2) {
try {
if (!StringUtils.isBlank(httpSslVerifiedServerNames)) {
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
HostnameVerifier hostnameVerifier = new HostnameVerifier() {
public boolean verify(String s, SSLSession sslsession) {
boolean verified = false;
if (!StringUtils.isBlank(httpSslVerifiedServerNames)) {
Expand All @@ -78,11 +81,15 @@ public boolean verify(String s, SSLSession sslsession) {
}
return verified;
}
});
};
HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier);
if (enableHttps2) {
Http2Connection.setHostnameVerifier(hostnameVerifier);
}
}

if (allowSelfSignedCerts) {
HttpsURLConnection.setDefaultSSLSocketFactory(createSelfSignedSocketFactory());
initSelfSignedSocketFactory(enableHttps2);
}

} catch (GeneralSecurityException ex) {
Expand All @@ -103,7 +110,8 @@ public ITransportManager create(String transport) {
// Allow self signed certs based on the parameter value.
boolean allowSelfSignedCerts = symmetricEngine.getParameterService().is(
ServerConstants.HTTPS_ALLOW_SELF_SIGNED_CERTS, false);
initHttps(httpSslVerifiedServerNames, allowSelfSignedCerts);
boolean https2Enabled = symmetricEngine.getParameterService().is(ServerConstants.HTTPS2_ENABLE, true);
initHttps(httpSslVerifiedServerNames, allowSelfSignedCerts, https2Enabled);
return createHttpTransportManager(symmetricEngine);
} else if (Constants.PROTOCOL_FILE.equalsIgnoreCase(transport)) {
return new FileTransportManager(symmetricEngine);
Expand Down Expand Up @@ -148,16 +156,20 @@ protected HttpTransportManager createHttpTransportManager(ISymmetricEngine symme
* @throws KeyManagementException
* @throws KeyStoreException
*/
private static SSLSocketFactory createSelfSignedSocketFactory()
private static void initSelfSignedSocketFactory(boolean enableHttps2)
throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException {
SSLSocketFactory factory = null;

if (enableHttps2) {
new ConscryptHelper().checkProviderInstalled();
}
X509TrustManager trustManager = new SelfSignedX509TrustManager(null);
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, new TrustManager[] { new SelfSignedX509TrustManager(null) },
new SecureRandom());
factory = context.getSocketFactory();

return factory;
context.init(null, new TrustManager[] { trustManager }, new SecureRandom());
SSLSocketFactory sslSocketFactory = context.getSocketFactory();

HttpsURLConnection.setDefaultSSLSocketFactory(sslSocketFactory);
Http2Connection.setSslSocketFactory(sslSocketFactory);
Http2Connection.setTrustManager(trustManager);
}

}
Expand Up @@ -23,7 +23,6 @@
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.util.List;
import java.util.Map;

Expand All @@ -37,6 +36,7 @@
import org.jumpmind.symmetric.transport.IOutgoingWithResponseTransport;
import org.jumpmind.symmetric.transport.ITransportManager;
import org.jumpmind.symmetric.transport.http.HttpTransportManager;
import org.jumpmind.symmetric.web.WebConstants;
import org.jumpmind.util.FormatUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -55,7 +55,7 @@ public FileTransportManager(ISymmetricEngine engine) {
@Override
public int sendAcknowledgement(Node remote, List<IncomingBatch> list, Node local, String securityToken, String registrationUrl)
throws IOException {
return HttpURLConnection.HTTP_OK;
return WebConstants.SC_OK;
}

@Override
Expand Down
@@ -0,0 +1,44 @@
/**
* Licensed to JumpMind Inc under one or more contributor
* license agreements. See the NOTICE file distributed
* with this work for additional information regarding
* copyright ownership. JumpMind Inc licenses this file
* to you under the GNU General Public License, version 3.0 (GPLv3)
* (the "License"); you may not use this file except in compliance
* with the License.
*
* You should have received a copy of the GNU General Public License,
* version 3.0 (GPLv3) along with this library; if not, see
* <http://www.gnu.org/licenses/>.
*
* 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.jumpmind.symmetric.transport.http;

import java.security.Provider;
import java.security.Security;

import org.conscrypt.Conscrypt;

public class ConscryptHelper {

protected final static String PROVIDER_NAME = "Conscrypt";

public void checkProviderInstalled() {
if (Security.getProvider(PROVIDER_NAME) == null) {
Security.insertProviderAt(Conscrypt.newProvider(), 1);
} else {
Provider[] providers = Security.getProviders();
if (providers.length > 0 && !providers[0].getName().equals(PROVIDER_NAME)) {
Security.removeProvider(PROVIDER_NAME);
Security.insertProviderAt(Conscrypt.newProvider(), 1);
}
}
}

}

0 comments on commit 93512d9

Please sign in to comment.