
com.openshift.internal.client.httpclient.UrlConnectionHttpClient Maven / Gradle / Ivy
/*******************************************************************************
* Copyright (c) 2013 Red Hat, Inc.
* Distributed under license by Red Hat, Inc. All rights reserved.
* This program is made available under the terms of the
* Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
******************************************************************************/
package com.openshift.internal.client.httpclient;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.URLConnection;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Map;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.openshift.client.HttpMethod;
import com.openshift.client.IHttpClient;
import com.openshift.client.utils.Base64Coder;
import com.openshift.internal.client.utils.StreamUtils;
import com.openshift.internal.client.utils.StringUtils;
/**
* @author Andre Dietisheim
* @author Nicolas Spano
*/
public class UrlConnectionHttpClient implements IHttpClient {
private static final Logger LOGGER = LoggerFactory.getLogger(UrlConnectionHttpClient.class);
private static final String SYSPROP_OPENSHIFT_CONNECT_TIMEOUT = "com.openshift.httpclient.timeout";
private static final String SYSPROP_DEFAULT_CONNECT_TIMEOUT = "sun.net.client.defaultConnectTimeout";
private static final String SYSPROP_DEFAULT_READ_TIMEOUT = "sun.net.client.defaultReadTimeout";
private static final String USERAGENT_FOR_KEYAUTH = "OpenShift";
private String userAgent;
private boolean sslChecks;
private String username;
private String password;
private String authKey;
private String authIV;
private IMediaType requestMediaType;
private String acceptedMediaType;
private String acceptVersion;
public UrlConnectionHttpClient(String username, String password, String userAgent, boolean sslChecks,
IMediaType requestMediaType, String acceptedMediaType, String version) {
this(username, password, userAgent, sslChecks, requestMediaType, acceptedMediaType, version, null, null);
}
public UrlConnectionHttpClient(String username, String password, String userAgent, boolean sslChecks,
IMediaType requestMediaType, String acceptedMediaType, String version, String authKey, String authIV) {
this.username = username;
this.password = password;
this.userAgent = setupUserAgent(authKey, authIV, userAgent);
this.sslChecks = sslChecks;
this.requestMediaType = requestMediaType;
this.acceptedMediaType = acceptedMediaType;
this.authKey = authKey;
this.authIV = authIV;
this.acceptVersion = version;
}
/** TODO: unify with #setUserAgent **/
private String setupUserAgent(String authKey, String authIV, String userAgent) {
if (!StringUtils.isEmpty(authKey)) {
if (userAgent == null) {
userAgent = "OpenShift";
} else if (!userAgent.startsWith("OpenShift")) {
userAgent = "OpenShift-" + userAgent;
}
}
return userAgent;
}
public void setAcceptedMediaType(String acceptedMediaType) {
this.acceptedMediaType = acceptedMediaType;
}
public String getAcceptedMediaType() {
return acceptedMediaType;
}
public String get(URL url) throws HttpClientException, SocketTimeoutException {
return get(url, NO_TIMEOUT);
}
@Override
public String get(URL url, int timeout) throws HttpClientException, SocketTimeoutException {
HttpURLConnection connection = null;
try {
return write(null, HttpMethod.GET.toString(), url, timeout, requestMediaType);
} catch (SocketTimeoutException e) {
throw e;
/* TODO: cleanup exception handling */
} catch (IOException e) {
throw createException(e, connection);
} finally {
disconnect(connection);
}
}
public void setUserAgent(String userAgent) {
this.userAgent = userAgent;
}
public String getUserAgent() {
return userAgent;
}
public void setAcceptVersion(String version) {
this.acceptVersion = version;
}
public String getAcceptVersion() {
return acceptVersion;
}
public IMediaType getRequestMediaType() {
return requestMediaType;
}
public void setRequestMediaType(IMediaType requestMediaType) {
this.requestMediaType = requestMediaType;
}
public String put(Map parameters, URL url)
throws SocketTimeoutException, EncodingException, HttpClientException {
return put(requestMediaType.encodeParameters(parameters), url);
}
@Override
public String put(Map parameters, URL url, int timeout)
throws HttpClientException, SocketTimeoutException, EncodingException {
return put(parameters, url, timeout, requestMediaType);
}
@Override
public String put(Map parameters, URL url, int timeout, IMediaType mediaType) throws HttpClientException, SocketTimeoutException, EncodingException {
return write(mediaType.encodeParameters(parameters), HttpMethod.PUT.toString(), url, timeout, mediaType);
}
protected String put(String data, URL url) throws HttpClientException, SocketTimeoutException {
return write(data, HttpMethod.PUT.toString(), url, NO_TIMEOUT, requestMediaType);
}
public String post(Map parameters, URL url)
throws SocketTimeoutException, EncodingException, HttpClientException {
return post(requestMediaType.encodeParameters(parameters), url);
}
protected String post(String data, URL url) throws HttpClientException, SocketTimeoutException {
return write(data, HttpMethod.POST.toString(), url, NO_TIMEOUT, requestMediaType);
}
public String post(Map data, URL url, int timeout)
throws HttpClientException, SocketTimeoutException, EncodingException {
return post(data, url, timeout, requestMediaType);
}
@Override
public String post(Map parameters, URL url, int timeout, IMediaType mediaType) throws HttpClientException, SocketTimeoutException, EncodingException {
return write(mediaType.encodeParameters(parameters), HttpMethod.POST.toString(), url, timeout, mediaType);
}
public String delete(Map parameters, URL url)
throws HttpClientException, SocketTimeoutException, EncodingException {
return delete(requestMediaType.encodeParameters(parameters), url);
}
@Override
public String delete(Map parameters, URL url, int timeout) throws HttpClientException,
SocketTimeoutException,
EncodingException {
return delete(parameters, url, timeout, requestMediaType);
}
@Override
public String delete(Map parameters, URL url, int timeout, IMediaType mediaType) throws HttpClientException, SocketTimeoutException, EncodingException {
return write(mediaType.encodeParameters(parameters), HttpMethod.DELETE.toString(), url, timeout, mediaType);
}
public String delete(URL url)
throws HttpClientException, SocketTimeoutException, UnsupportedEncodingException {
return delete((String) null, url);
}
protected String delete(String data, URL url) throws HttpClientException, SocketTimeoutException {
return write(data, HttpMethod.DELETE.toString(), url, NO_TIMEOUT, requestMediaType);
}
protected String write(String data, String requestMethod, URL url, int timeout, IMediaType mediaType)
throws SocketTimeoutException, HttpClientException {
HttpURLConnection connection = null;
try {
connection = createConnection(username, password, authKey, authIV, userAgent, url, timeout, mediaType);
connection.setRequestMethod(requestMethod);
connection.setDoOutput(true);
if (data != null) {
LOGGER.trace("Sending \"{}\" to {}", data, url);
StreamUtils.writeTo(data.getBytes(), connection.getOutputStream());
}
return StreamUtils.readToString(connection.getInputStream());
} catch (SocketTimeoutException e) {
throw e;
} catch (IOException e) {
throw createException(e, connection);
} finally {
disconnect(connection);
}
}
private void disconnect(HttpURLConnection connection) {
if (connection != null) {
connection.disconnect();
}
}
private HttpClientException createException(IOException ioe, HttpURLConnection connection)
throws SocketTimeoutException {
try {
int responseCode = connection.getResponseCode();
String errorMessage = createErrorMessage(ioe, connection);
switch (responseCode) {
case STATUS_INTERNAL_SERVER_ERROR:
return new InternalServerErrorException(errorMessage, ioe);
case STATUS_BAD_REQUEST:
return new BadRequestException(errorMessage, ioe);
case STATUS_UNAUTHORIZED:
return new UnauthorizedException(errorMessage, ioe);
case STATUS_NOT_FOUND:
return new NotFoundException(errorMessage, ioe);
default:
return new HttpClientException(errorMessage, ioe);
}
} catch (SocketTimeoutException e) {
throw e;
} catch (IOException e) {
return new HttpClientException(e);
}
}
protected String createErrorMessage(IOException ioe, HttpURLConnection connection) throws IOException {
String errorMessage = StreamUtils.readToString(connection.getErrorStream());
if (!StringUtils.isEmpty(errorMessage)) {
return errorMessage;
}
StringBuilder builder = new StringBuilder("Connection to ")
.append(connection.getURL());
String reason = connection.getResponseMessage();
if (!StringUtils.isEmpty(reason)) {
builder.append(": ").append(reason);
}
return builder.toString();
}
private boolean isHttps(URL url) {
return "https".equals(url.getProtocol());
}
/**
* Sets a trust manager that will always trust.
*
* TODO: dont swallog exceptions and setup things so that they dont disturb
* other components.
*/
private void setPermissiveSSLSocketFactory(HttpsURLConnection connection) {
try {
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(
new KeyManager[0], new TrustManager[] { new PermissiveTrustManager() }, new SecureRandom());
SSLSocketFactory socketFactory = sslContext.getSocketFactory();
((HttpsURLConnection) connection).setSSLSocketFactory(socketFactory);
} catch (KeyManagementException e) {
// ignore
} catch (NoSuchAlgorithmException e) {
// ignore
}
}
protected HttpURLConnection createConnection(String username, String password, String userAgent, URL url)
throws IOException {
return createConnection(username, password, null, null, userAgent, url, NO_TIMEOUT, requestMediaType);
}
protected HttpURLConnection createConnection(String username, String password, String authKey, String authIV,
String userAgent, URL url, int timeout, IMediaType mediaType) throws IOException {
LOGGER.trace(
"creating connection to {} using username \"{}\" and password \"{}\"", new Object[] { url, username,
password });
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
setSSLChecks(url, connection);
setAuthorisation(username, password, authKey, authIV, connection);
connection.setUseCaches(false);
connection.setDoInput(true);
connection.setAllowUserInteraction(false);
setConnectTimeout(connection);
setReadTimeout(timeout, connection);
// wont work when switching http->https
// http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4620571
connection.setInstanceFollowRedirects(true);
setAcceptHeader(connection);
setUserAgent(connection);
connection.setRequestProperty(PROPERTY_CONTENT_TYPE, mediaType.getType());
return connection;
}
private void setUserAgent(HttpURLConnection connection) {
String userAgent = this.userAgent;
if (!StringUtils.isEmpty(authKey)) {
userAgent = USERAGENT_FOR_KEYAUTH;
}
if (userAgent != null) {
connection.setRequestProperty(PROPERTY_USER_AGENT, userAgent);
}
}
private void setAcceptHeader(HttpURLConnection connection) {
StringBuilder builder =
new StringBuilder(acceptedMediaType);
if (acceptVersion != null) {
builder.append(SEMICOLON).append(SPACE)
.append(VERSION).append(EQUALS).append(acceptVersion);
}
connection.setRequestProperty(PROPERTY_ACCEPT, builder.toString());
}
private void setAuthorisation(String username, String password, String authKey, String authIV,
HttpURLConnection connection) {
if (username == null || username.trim().length() == 0
|| password == null || password.trim().length() == 0) {
if (authKey != null && authIV != null) {
connection.setRequestProperty(PROPERTY_AUTHKEY, authKey);
connection.setRequestProperty(PROPERTY_AUTHIV, authIV);
}
} else {
String credentials = Base64Coder.encode(
new StringBuilder().append(username).append(COLON).append(password).toString().getBytes());
connection.setRequestProperty(PROPERTY_AUTHORIZATION,
new StringBuilder().append(AUTHORIZATION_BASIC).append(SPACE).append(credentials).toString());
}
}
private void setSSLChecks(URL url, HttpURLConnection connection) {
if (isHttps(url)
&& !sslChecks) {
HttpsURLConnection httpsConnection = (HttpsURLConnection) connection;
httpsConnection.setHostnameVerifier(new NoopHostnameVerifier());
setPermissiveSSLSocketFactory(httpsConnection);
}
}
private void setConnectTimeout(URLConnection connection) {
int timeout = getTimeout(
getSystemPropertyInteger(SYSPROP_OPENSHIFT_CONNECT_TIMEOUT),
getSystemPropertyInteger(SYSPROP_DEFAULT_CONNECT_TIMEOUT),
DEFAULT_CONNECT_TIMEOUT);
connection.setConnectTimeout(timeout);
}
private void setReadTimeout(int timeout, URLConnection connection) {
timeout = getTimeout(timeout, getSystemPropertyInteger(SYSPROP_DEFAULT_READ_TIMEOUT), DEFAULT_READ_TIMEOUT);
connection.setReadTimeout(timeout);
}
private int getTimeout(int timeout, int systemPropertyTimeout, int defaultTimeout) {
if (timeout == NO_TIMEOUT) {
timeout = systemPropertyTimeout;
if (timeout == NO_TIMEOUT) {
timeout = defaultTimeout;
}
}
return timeout;
}
private int getSystemPropertyInteger(String key) {
try {
return Integer.parseInt(System.getProperty(key));
} catch (NumberFormatException e) {
return -1;
}
}
private class PermissiveTrustManager implements X509TrustManager {
public X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkServerTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
}
public void checkClientTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
}
}
private class NoopHostnameVerifier implements HostnameVerifier {
public boolean verify(String hostname, SSLSession sslSession) {
return true;
}
}
}