org.rhq.plugins.www.util.WWWUtils Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of rhq-apache-plugin Show documentation
Show all versions of rhq-apache-plugin Show documentation
a plugin for managing Apache web servers (1.3 and later)
/*
* RHQ Management Platform
* Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
package org.rhq.plugins.www.util;
import static org.rhq.core.domain.measurement.AvailabilityType.UP;
import static org.rhq.plugins.apache.AvailabilityResult.availabilityIsDown;
import static org.rhq.plugins.apache.AvailabilityResult.availabilityIsUp;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ConnectException;
import java.net.HttpURLConnection;
import java.net.ProtocolException;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.security.cert.X509Certificate;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.rhq.core.util.stream.StreamUtil;
import org.rhq.plugins.apache.AvailabilityResult;
import org.rhq.plugins.apache.AvailabilityResult.ErrorType;
/**
* Helper class that contains methods that send HTTP requests and evaluate results.
*
* @author Ian Springer
*/
public class WWWUtils {
private static final Log LOG = LogFactory.getLog(WWWUtils.class);
// prevent instantiation
private WWWUtils() {
}
/**
* Sends a HEAD request to the passed URL and returns true if the URL was reachable.
*
* @param httpURL a http or https URL to check
* @param timeout timeout, in milliseconds
*
* @return true if connecting to the URL succeeds, or false otherwise
*
* @deprecated as of RHQ 4.10. Use {@link #checkAvailability(java.net.URL, int)} instead.
*/
@Deprecated
public static boolean isAvailable(URL httpURL, int timeout) {
return checkAvailability(httpURL, timeout).getAvailabilityType() == UP;
}
/**
* Checks availability of the httpURL
.
*
* Will first try with HEAD request and fallback to GET.
*
* @param httpURL a http or https URL to check
* @param timeout timeout, in milliseconds
* @return an {@link AvailabilityResult}
*/
public static AvailabilityResult checkAvailability(URL httpURL, int timeout) {
if (timeout < 0) {
throw new IllegalArgumentException("Timeout cannot be negative.");
}
if (timeout == 0) {
if (LOG.isDebugEnabled()) {
LOG.debug("Pinging [" + httpURL + "] with no timeout...");
}
} else {
if (LOG.isDebugEnabled()) {
LOG.debug("Pinging [" + httpURL + "] with timeout of " + timeout + " milliseconds...");
}
}
HttpURLConnection connection;
try {
connection = (HttpURLConnection) httpURL.openConnection();
connection.setInstanceFollowRedirects(false);
} catch (IOException e) {
String errorMessage = "Failed to open URLConnection for [" + httpURL + "].";
LOG.error(errorMessage, e);
return availabilityIsDown(errorMessage, e);
}
try {
connection.setRequestMethod("HEAD");
} catch (ProtocolException ignore) {
try {
connection.setRequestMethod("GET");
} catch (ProtocolException e) {
String errorMessage = "Failed to set request method to HEAD or GET on URLConnection for [" + httpURL
+ "].";
LOG.error(errorMessage, e);
return availabilityIsDown(errorMessage, e);
}
}
connection.setConnectTimeout(timeout);
// Hold off on setting the read timeout until after we connect.
if (connection instanceof HttpsURLConnection) {
disableCertificateVerification((HttpsURLConnection) connection);
}
// First just connect to the HTTP server.
long connectStartTime = System.currentTimeMillis();
if (LOG.isDebugEnabled()) {
LOG.debug("Connecting to [" + httpURL + "]...");
}
try {
connection.connect();
} catch (IOException e) {
String errorMessage;
ErrorType errorType = ErrorType.UNKNOWN;
if (e instanceof ConnectException) {
// This most likely just means the server is down.
errorMessage = "Failed to connect to [" + httpURL + "].";
errorType = ErrorType.CANNOT_CONNECT;
} else if (e instanceof SocketTimeoutException) {
// This probably means the server is up but not properly accepting connection requests.
long connectDuration = System.currentTimeMillis() - connectStartTime;
errorMessage = "Attempt to connect to [" + httpURL + "] timed out after " + connectDuration
+ " milliseconds.";
errorType = ErrorType.CONNECTION_TIMEOUT;
} else {
errorMessage = "An error occurred while attempting to connect to [" + httpURL + "].";
}
if (LOG.isDebugEnabled()) {
LOG.warn(errorMessage, e);
}
return availabilityIsDown(errorType, errorMessage, e);
}
int connectDuration = (int) (System.currentTimeMillis() - connectStartTime);
if (LOG.isDebugEnabled()) {
LOG.debug("Connected to [" + httpURL + "] in " + connectDuration + " milliseconds.");
}
if ((timeout > 0) && (connectDuration >= timeout)) {
String errorMessage = "Attempt to ping [" + httpURL + "] timed out after " + connectDuration
+ " milliseconds.";
if (LOG.isDebugEnabled()) {
LOG.warn(errorMessage);
}
return availabilityIsDown(ErrorType.CONNECTION_TIMEOUT, errorMessage);
}
try {
int readTimeout = (timeout > 0) ? (timeout - connectDuration) : 0;
connection.setReadTimeout(readTimeout);
if (connection.getReadTimeout() != readTimeout) {
if (LOG.isDebugEnabled()) {
LOG.warn("Failed to set read timeout on URLConnection for [" + httpURL
+ "] - this most likely means we're running in a non-standard JRE.");
}
}
// Now actually send the request and read the response.
long readStartTime = System.currentTimeMillis();
if (LOG.isDebugEnabled()) {
LOG.debug("Sending " + connection.getRequestMethod() + " request to [" + httpURL + "]...");
}
try {
// Calling getResponseCode() will cause the request to be sent.
int responseCode = connection.getResponseCode();
if (LOG.isDebugEnabled()) {
if (responseCode == -1) {
LOG.warn("Ping request to [" + httpURL + "] returned an invalid response: "
+ getResponseBody(connection));
} else if (responseCode >= 500) {
LOG.warn("Ping request to [" + httpURL + "] returned a response with server error "
+ responseCode + " (" + connection.getResponseMessage() + "): "
+ getResponseBody(connection));
} else if (responseCode >= 400) {
LOG.warn("Ping request to [" + httpURL + "] returned a response with client error "
+ responseCode + " (" + connection.getResponseMessage() + ").");
}
}
} catch (IOException e) {
String errorMessage;
ErrorType errorType = ErrorType.UNKNOWN;
if (e instanceof SocketTimeoutException) {
long readDuration = System.currentTimeMillis() - readStartTime;
errorMessage = "Attempt to read response from " + connection.getRequestMethod() + " request to ["
+ httpURL + "] timed out after " + readDuration + " milliseconds.";
errorType = ErrorType.CONNECTION_TIMEOUT;
} else {
errorMessage = "An error occurred while attempting to read response from "
+ connection.getRequestMethod() + " to [" + httpURL + "].";
}
if (LOG.isDebugEnabled()) {
LOG.warn(errorMessage, e);
}
return availabilityIsDown(errorType, errorMessage, e);
}
long readDuration = System.currentTimeMillis() - readStartTime;
if (LOG.isDebugEnabled()) {
LOG.debug("Read response from " + connection.getRequestMethod() + " request to [" + httpURL + "] in "
+ readDuration + " milliseconds.");
}
} finally {
// We don't care about keeping the connection around. We're only going to be pinging each server once every
// minute.
connection.disconnect();
}
return availabilityIsUp();
}
/**
* Get the content of the 'Server' header.
*
* @param httpURL a http or https URL to get the header from
*
* @return the contents of the header or null if anything went wrong or the field was not present.
*/
public static String getServerHeader(URL httpURL) {
String ret;
try {
HttpURLConnection connection = (HttpURLConnection) httpURL.openConnection();
connection.setRequestMethod("HEAD");
connection.setConnectTimeout(3000);
connection.setReadTimeout(1000);
connection.connect();
// Get the response code to actually trigger sending the request.
connection.getResponseCode();
ret = connection.getHeaderField("Server");
} catch (IOException e) {
ret = null;
}
return ret;
}
private static TrustManager NO_OP_TRUST_MANAGER = new X509TrustManager() {
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
@Override
public void checkClientTrusted(X509Certificate[] certs, String authType) {
return;
}
@Override
public void checkServerTrusted(X509Certificate[] certs, String authType) {
return;
}
};
private static HostnameVerifier NO_OP_HOSTNAME_VERIFIER = new HostnameVerifier() {
@Override
public boolean verify(String s, SSLSession sslSession) {
return true;
}
};
// This method has been added in support of https://bugzilla.redhat.com/show_bug.cgi?id=690430.
private static void disableCertificateVerification(HttpsURLConnection connection) {
try {
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, new TrustManager[]{NO_OP_TRUST_MANAGER}, new java.security.SecureRandom());
connection.setSSLSocketFactory(sslContext.getSocketFactory());
connection.setHostnameVerifier(NO_OP_HOSTNAME_VERIFIER);
} catch (Exception e) {
logWarnWithStackTraceOnlyIfDebugEnabled("Failed to disable certificate and hostname validation on URLConnection for ["
+ connection.getURL() + "].", e);
}
}
private static void logWarnWithStackTraceOnlyIfDebugEnabled(String message, Exception e) {
if (LOG.isDebugEnabled()) {
LOG.warn(message, e);
} else {
LOG.warn(message + " (enable DEBUG logging to see stack trace): " + e);
}
}
private static String getResponseBody(HttpURLConnection connection) {
String responseBody;
try {
InputStream inputStream = (connection.getInputStream() != null) ? connection.getInputStream() :
connection.getErrorStream();
responseBody = (inputStream != null) ? StreamUtil.slurp(new InputStreamReader(inputStream)) : "";
} catch (IOException e) {
responseBody = "";
}
return responseBody;
}
}