org.opensearch.gradle.http.WaitForHttpResource Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of build-tools Show documentation
Show all versions of build-tools Show documentation
OpenSearch subproject :build-tools
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch 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.
*/
/*
* Modifications Copyright OpenSearch Contributors. See
* GitHub history for details.
*/
package org.opensearch.gradle.http;
import org.gradle.api.logging.Logger;
import org.gradle.api.logging.Logging;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.SecureRandom;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.util.Arrays;
import java.util.Base64;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
* A utility to wait for a specific HTTP resource to be available, optionally with customized TLS trusted CAs.
* This is logically similar to using the Ant Get task to retrieve a resource, but with the difference that it can
* access resources that do not use the JRE's default trusted CAs.
*/
public class WaitForHttpResource {
private static final Logger logger = Logging.getLogger(WaitForHttpResource.class);
private Set validResponseCodes = Collections.singleton(200);
private URL url;
private Set certificateAuthorities;
private File trustStoreFile;
private String trustStorePassword;
private String username;
private String password;
public WaitForHttpResource(String protocol, String host, int numberOfNodes) throws MalformedURLException {
this(new URL(protocol + "://" + host + "/_cluster/health?wait_for_nodes=>=" + numberOfNodes + "&wait_for_status=yellow"));
}
public WaitForHttpResource(URL url) {
this.url = url;
}
public void setValidResponseCodes(int... validResponseCodes) {
this.validResponseCodes = new HashSet<>(validResponseCodes.length);
for (int rc : validResponseCodes) {
this.validResponseCodes.add(rc);
}
}
public void setCertificateAuthorities(File... certificateAuthorities) {
this.certificateAuthorities = new HashSet<>(Arrays.asList(certificateAuthorities));
}
public void setTrustStoreFile(File trustStoreFile) {
this.trustStoreFile = trustStoreFile;
}
public void setTrustStorePassword(String trustStorePassword) {
this.trustStorePassword = trustStorePassword;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
public boolean wait(int durationInMs) throws GeneralSecurityException, InterruptedException, IOException {
final long waitUntil = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(durationInMs);
final long sleep = Long.max(durationInMs / 10, 100);
final SSLContext ssl;
final KeyStore trustStore = buildTrustStore();
if (trustStore != null) {
ssl = createSslContext(trustStore);
} else {
ssl = null;
}
IOException failure = null;
while (true) {
try {
checkResource(ssl);
return true;
} catch (IOException e) {
logger.debug("Failed to access resource [{}]", url, e);
failure = e;
}
if (System.nanoTime() < waitUntil) {
Thread.sleep(sleep);
} else {
throw failure;
}
}
}
protected void checkResource(SSLContext ssl) throws IOException {
final HttpURLConnection connection = buildConnection(ssl);
connection.connect();
final Integer response = connection.getResponseCode();
if (validResponseCodes.contains(response)) {
logger.info("Got successful response [{}] from URL [{}]", response, url);
return;
} else {
throw new IOException(response + " " + connection.getResponseMessage());
}
}
HttpURLConnection buildConnection(SSLContext ssl) throws IOException {
final HttpURLConnection connection = (HttpURLConnection) this.url.openConnection();
configureSslContext(connection, ssl);
configureBasicAuth(connection);
connection.setRequestMethod("GET");
return connection;
}
private void configureSslContext(HttpURLConnection connection, SSLContext ssl) {
if (ssl != null) {
if (connection instanceof HttpsURLConnection) {
((HttpsURLConnection) connection).setSSLSocketFactory(ssl.getSocketFactory());
} else {
throw new IllegalStateException("SSL trust has been configured, but [" + url + "] is not a 'https' URL");
}
}
}
private void configureBasicAuth(HttpURLConnection connection) {
if (username != null) {
if (password == null) {
throw new IllegalStateException("Basic Auth user [" + username + "] has been set, but no password has been configured");
}
connection.setRequestProperty(
"Authorization",
"Basic " + Base64.getEncoder().encodeToString((username + ":" + password).getBytes(StandardCharsets.UTF_8))
);
}
}
KeyStore buildTrustStore() throws GeneralSecurityException, IOException {
if (this.certificateAuthorities != null) {
if (trustStoreFile != null) {
throw new IllegalStateException("Cannot specify both truststore and CAs");
}
return buildTrustStoreFromCA();
} else if (trustStoreFile != null) {
return buildTrustStoreFromFile();
} else {
return null;
}
}
private KeyStore buildTrustStoreFromFile() throws GeneralSecurityException, IOException {
KeyStore keyStore = KeyStore.getInstance(trustStoreFile.getName().endsWith(".jks") ? "JKS" : "PKCS12");
try (InputStream input = new FileInputStream(trustStoreFile)) {
keyStore.load(input, trustStorePassword == null ? null : trustStorePassword.toCharArray());
}
return keyStore;
}
private KeyStore buildTrustStoreFromCA() throws GeneralSecurityException, IOException {
final KeyStore store = KeyStore.getInstance(KeyStore.getDefaultType());
store.load(null, null);
final CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
int counter = 0;
for (File ca : certificateAuthorities) {
try (InputStream input = new FileInputStream(ca)) {
for (Certificate certificate : certFactory.generateCertificates(input)) {
store.setCertificateEntry("cert-" + counter, certificate);
counter++;
}
}
}
return store;
}
private SSLContext createSslContext(KeyStore trustStore) throws GeneralSecurityException {
checkForTrustEntry(trustStore);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(trustStore);
SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
sslContext.init(new KeyManager[0], tmf.getTrustManagers(), new SecureRandom());
return sslContext;
}
private void checkForTrustEntry(KeyStore trustStore) throws KeyStoreException {
Enumeration enumeration = trustStore.aliases();
while (enumeration.hasMoreElements()) {
if (trustStore.isCertificateEntry(enumeration.nextElement())) {
// found trusted cert entry
return;
}
}
throw new IllegalStateException("Trust-store does not contain any trusted certificate entries");
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy