Package com.openshift.internal.client.httpclient

Source Code of com.openshift.internal.client.httpclient.UrlConnectionHttpClient$CallbackTrustManager

/*******************************************************************************
* 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.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.ProtocolException;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.URLConnection;
import java.net.UnknownHostException;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.text.MessageFormat;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
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.client.utils.SSLUtils;
import com.openshift.internal.client.httpclient.request.IMediaType;
import com.openshift.internal.client.httpclient.request.Parameter;
import com.openshift.internal.client.httpclient.request.ParameterValueMap;
import com.openshift.internal.client.utils.StreamUtils;
import com.openshift.internal.client.utils.StringUtils;

/**
* @author Andre Dietisheim
* @author Nicolas Spano
* @author Corey Daley
*/
public class UrlConnectionHttpClient implements IHttpClient {

  private static final Logger LOGGER = LoggerFactory.getLogger(UrlConnectionHttpClient.class);

  protected String userAgent;
  protected String username;
  protected String password;
  protected String authKey;
  protected String authIV;
  protected String acceptedMediaType;
  protected String acceptedVersion;
  protected ISSLCertificateCallback sslAuthorizationCallback;
  protected Integer configTimeout;
  private String excludedSSLCipherRegex;

  public UrlConnectionHttpClient(
      String username, String password, String userAgent, String acceptedMediaType, String version) {
    this(username, password, userAgent, acceptedMediaType, version, null, null);
  }

  public UrlConnectionHttpClient(
      String username, String password, String userAgent, String acceptedMediaType, String version, String authKey, String authIV) {
    this(username, password, userAgent, acceptedMediaType, version, authKey, authIV, null,null, null);
  }

  public UrlConnectionHttpClient(String username, String password, String userAgent, String acceptedMediaType,
      String version, String authKey, String authIV, ISSLCertificateCallback callback, Integer configTimeout, String excludedSSLCipherRegex) {
    this.username = username;
    this.password = password;
    this.userAgent = userAgent;
    this.acceptedMediaType = acceptedMediaType;
    this.acceptedVersion = version;
    this.authKey = authKey;
    this.authIV = authIV;
    this.sslAuthorizationCallback = callback;
    this.configTimeout = configTimeout;
    this.excludedSSLCipherRegex = excludedSSLCipherRegex;
  }

  @Override
  public String get(URL url, int timeout) throws HttpClientException, SocketTimeoutException {
    return request(HttpMethod.GET, url, null, timeout);
  }

  @Override
  public String head(URL url, int timeout) throws HttpClientException, SocketTimeoutException {
    return request(HttpMethod.HEAD, url, null, timeout);
  }

  @Override
  public String put(URL url, IMediaType mediaType, int timeout, Parameter... parameters)
      throws HttpClientException, SocketTimeoutException, EncodingException {
    return request(HttpMethod.PUT, url, mediaType, timeout, parameters);
  }

  @Override
  public String post(URL url, IMediaType mediaType, int timeout, Parameter... parameters)
      throws HttpClientException, SocketTimeoutException, EncodingException {
    return request(HttpMethod.POST, url, mediaType, timeout, parameters);
  }

  @Override
  public String patch(URL url, IMediaType mediaType, int timeout, Parameter... parameters)
      throws HttpClientException, SocketTimeoutException, EncodingException {
    return request(HttpMethod.PATCH, url, mediaType, timeout, parameters);
  }

  @Override
  public String delete(URL url, IMediaType mediaType, int timeout, Parameter... parameters)
      throws HttpClientException, SocketTimeoutException, EncodingException {
    return request(HttpMethod.DELETE, url, mediaType, timeout, parameters);
  }

  @Override
  public String delete(URL url, int timeout)
      throws HttpClientException, SocketTimeoutException, EncodingException {
    return delete(url, null, timeout);
  }

  protected String request(HttpMethod httpMethod, URL url, IMediaType requestMediaType, int timeout,
      Parameter... parameters)
      throws SocketTimeoutException, HttpClientException {
    return request(httpMethod, url, requestMediaType, timeout, new ParameterValueMap(parameters));
  }

  protected String request(HttpMethod httpMethod, URL url, IMediaType requestMediaType, int timeout,
      ParameterValueMap parameters)
      throws SocketTimeoutException, HttpClientException {
    HttpURLConnection connection = null;
    try {
      connection = createConnection(
          url, username, password, authKey, authIV, userAgent, acceptedVersion, acceptedMediaType, sslAuthorizationCallback, timeout);
      // PATCH not yet supported by JVM
      setRequestMethod(httpMethod, connection);
      if (!parameters.isEmpty()) {
        connection.setDoOutput(true);
        setRequestMediaType(requestMediaType, connection);
        requestMediaType.writeTo(parameters, connection.getOutputStream());
      }
      return StreamUtils.readToString(connection.getInputStream(), StreamUtils.UTF_8);
    } catch (SocketTimeoutException e) {
      throw e;
    } catch (IOException e) {
      throw createException(e, connection);
    } finally {
      disconnect(connection);
    }
  }

  private void setRequestMethod(HttpMethod httpMethod, HttpURLConnection connection) throws ProtocolException {
    if (httpMethod == HttpMethod.PATCH) {
      httpMethod = HttpMethod.POST;
      connection.setRequestProperty("X-Http-Method-Override", "PATCH");
    }
    connection.setRequestMethod(httpMethod.toString());
  }
 
  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());
  }

  protected HttpURLConnection createConnection(URL url, String username, String password, String authKey,
      String authIV, String userAgent, String acceptedVersion, String acceptedMediaType, ISSLCertificateCallback callback, int timeout)
      throws IOException {
    LOGGER.trace(
        "creating connection to {} using username \"{}\" and password \"{}\"",
        new Object[] { url, username, password });
    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
    if (isHttps(url)) {
      HttpsURLConnection httpsConnection = (HttpsURLConnection) connection;
      SSLContext sslContext = setSSLCallback(sslAuthorizationCallback, url, httpsConnection);
      setFilteredCiphers(excludedSSLCipherRegex, sslContext, httpsConnection);
    }
    setAuthorisation(username, password, authKey, authIV, connection);
    connection.setUseCaches(false);
    connection.setDoInput(true);
    connection.setAllowUserInteraction(false);
    setConnectTimeout(NO_TIMEOUT, connection);
    setReadTimeout(timeout, connection);
    // wont work when switching http->https
    // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4620571
    connection.setInstanceFollowRedirects(true);
    setUserAgent(userAgent, authKey, connection);
    setAcceptHeader(acceptedVersion, acceptedMediaType, connection);

    return connection;
  }

  private void setUserAgent(String userAgent, String authKey, HttpURLConnection connection) {
    if (!StringUtils.isEmpty(userAgent)) {
      connection.setRequestProperty(PROPERTY_USER_AGENT, userAgent);
    }
  }

  private void setAcceptHeader(String acceptedVersion, String acceptedMediaType, HttpURLConnection connection) {
    if (StringUtils.isEmpty(acceptedMediaType)) {
      throw new HttpClientException(MessageFormat.format(
          "Accepted media type (ex. {0}) is not defined", MEDIATYPE_APPLICATION_JSON));
    }

    StringBuilder builder = new StringBuilder(acceptedMediaType);
    if (acceptedVersion != null) {
      builder.append(SEMICOLON).append(SPACE)
          .append(VERSION).append(EQUALS).append(acceptedVersion);
    }

    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 SSLContext setSSLCallback(ISSLCertificateCallback sslAuthorizationCallback, URL url, HttpsURLConnection connection) {
    X509TrustManager trustManager = null;
    if (sslAuthorizationCallback != null) {
      connection.setHostnameVerifier(new CallbackHostnameVerifier());
      trustManager = createCallbackTrustManager(sslAuthorizationCallback, connection);
    }

    try {
      SSLContext sslContext = SSLUtils.getSSLContext(trustManager);
      connection.setSSLSocketFactory(sslContext.getSocketFactory());
      return sslContext;
    } catch (GeneralSecurityException e) {
      LOGGER.warn("Could not install trust manager callback", e);;
      return null;
    }
  }

  /**
   * Returns the callback trustmanager or <code>null</code> if it could not be created.
   *
   * @see ISSLCertificateCallback
   */
  private X509TrustManager createCallbackTrustManager(ISSLCertificateCallback sslAuthorizationCallback,HttpsURLConnection connection) {
    X509TrustManager trustManager = null;
    try {
      trustManager = getCurrentTrustManager();
      if (trustManager == null) {
        LOGGER.warn("Could not install trust manager callback, no trustmanager was found.", trustManager);
      } else {
        trustManager = new CallbackTrustManager(trustManager, sslAuthorizationCallback);
      }
    } catch (GeneralSecurityException e) {
      LOGGER.warn("Could not install trust manager callback.", e);;
    }
    return trustManager;
  }
   
  /**
   * Sets a ssl socket factory that sets a filtered list of ciphers based on
   * the #excludedSSLCipherRegex to the given connection.
   *
   * @param sslContext
   *
   * @param sslContext
   *            the ssl context that shall be used
   * @param url
   *            the url we are connecting to
   * @param connection
   *            the connection that the cipher filter shall be applied to
   */
  protected SSLContext setFilteredCiphers(String excludedSSLCipherRegex, SSLContext sslContext, HttpsURLConnection connection) {
    if (excludedSSLCipherRegex != null) {
      connection.setSSLSocketFactory(
          new EnabledCiphersSSLSocketFactory(
              SSLUtils.filterCiphers(
                  excludedSSLCipherRegex, getSupportedCiphers(sslContext)), sslContext
                  .getSocketFactory()));
    }
    return sslContext;
  }

  protected String[] getSupportedCiphers(SSLContext sslContext) {
    return sslContext.getSupportedSSLParameters().getCipherSuites();
  }

  private void setConnectTimeout(int timeout, URLConnection connection) {
    if (getTimeout(timeout) != NO_TIMEOUT) {
      connection.setConnectTimeout(getTimeout(timeout));
    }
  }

  private void setReadTimeout(int timeout, URLConnection connection) {
    if (getTimeout(timeout) != NO_TIMEOUT) {
      connection.setReadTimeout(getTimeout(timeout));
    }
  }

  private int getTimeout(int timeout) {
      if (timeout == NO_TIMEOUT) {
        if (configTimeout != null) {
          timeout = this.configTimeout;
        }
      }
    return timeout;
  }

  private void setRequestMediaType(IMediaType mediaType, HttpURLConnection connection) {
    if (mediaType == null
        || StringUtils.isEmpty(mediaType.getType())) {
      throw new HttpClientException(
          MessageFormat.format("Request media type (ex. {0}) is not defined",
              MEDIATYPE_APPLICATION_FORMURLENCODED));
    }
    connection.setRequestProperty(PROPERTY_CONTENT_TYPE, mediaType.getType())
  }
 
  private X509TrustManager getCurrentTrustManager() throws NoSuchAlgorithmException, KeyStoreException {
    TrustManagerFactory trustManagerFactory =
        TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
    trustManagerFactory.init((KeyStore) null);

    X509TrustManager x509TrustManager = null;
    for (TrustManager trustManager : trustManagerFactory.getTrustManagers()) {
      if (trustManager instanceof X509TrustManager) {
        x509TrustManager = (X509TrustManager) trustManager;
        break;
      }
    }
    return x509TrustManager;
  }
 
  @Override
  public void setUserAgent(String userAgent) {
    this.userAgent = userAgent;
  }

  @Override
  public void setAcceptVersion(String version) {
    this.acceptedVersion = version;
  }

  @Override
  public void setAcceptedMediaType(String acceptedMediaType) {
    this.acceptedMediaType = acceptedMediaType;
  }
 
  public class CallbackTrustManager implements X509TrustManager {

    private X509TrustManager trustManager;
    private ISSLCertificateCallback callback;

    private CallbackTrustManager(X509TrustManager currentTrustManager, ISSLCertificateCallback callback)
        throws NoSuchAlgorithmException, KeyStoreException {
      this.trustManager = currentTrustManager;
      this.callback = callback;
    }
   
    public X509Certificate[] getAcceptedIssuers() {
      return trustManager.getAcceptedIssuers();
    }

    public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
      try {
        trustManager.checkServerTrusted(chain, authType);
      } catch (CertificateException e) {
        if (!callback.allowCertificate(chain)) {
          throw e;
        }
      }
    }

    public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
      trustManager.checkServerTrusted(chain, authType);
    }
  }

  private class CallbackHostnameVerifier implements HostnameVerifier {

    @Override
    public boolean verify(String hostname, SSLSession session) {
      return sslAuthorizationCallback.allowHostname(hostname, session);
    }
  }
 
  /**
   * SSL socket factory that wraps a given socket factory and sets given ciphers
   * to the socket that the wrapped factory creates.
   *
   * @see http://stackoverflow.com/questions/6851461/java-why-does-ssl-handshake-give-could-not-generate-dh-keypair-exception/16686994#16686994
   */
  private static class EnabledCiphersSSLSocketFactory extends SSLSocketFactory {
   
    private String[] enabledCiphers;
    private SSLSocketFactory socketFactory;

    EnabledCiphersSSLSocketFactory(String[] enabledCiphers, SSLSocketFactory socketFactory) {
      this.enabledCiphers = enabledCiphers;
      this.socketFactory = socketFactory;
    }

    @Override
    public Socket createSocket(InetAddress host, int port, InetAddress localHost, int localPort) throws IOException {
      return setEnabledCiphers((SSLSocket) socketFactory.createSocket(host, port, localHost, localPort));
    }
   
    @Override
    public Socket createSocket(String host, int port, InetAddress localHost, int localPort)
        throws IOException, UnknownHostException {
      return setEnabledCiphers((SSLSocket) socketFactory.createSocket(host, port, localHost, localPort));
    }
   
    @Override
    public Socket createSocket(InetAddress host, int port) throws IOException {
      return setEnabledCiphers((SSLSocket) socketFactory.createSocket(host, port));
    }
   
    @Override
    public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
      return setEnabledCiphers((SSLSocket) socketFactory.createSocket(host, port));
    }
   
    @Override
    public String[] getSupportedCipherSuites() {
      if (enabledCiphers == null) {
        return socketFactory.getSupportedCipherSuites();
      } else {
        return enabledCiphers;
      }
    }
   
    @Override
    public String[] getDefaultCipherSuites() {
      return socketFactory.getDefaultCipherSuites();
    }
   
    @Override
    public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException {
       return setEnabledCiphers((SSLSocket) socketFactory.createSocket(socket, host, port, autoClose));
    }
   
    private SSLSocket setEnabledCiphers(SSLSocket socket) {
      if (enabledCiphers == null) {
        return socket;
      }
      socket.setEnabledCipherSuites(enabledCiphers);
      return socket;
    }
  }
 
}
TOP

Related Classes of com.openshift.internal.client.httpclient.UrlConnectionHttpClient$CallbackTrustManager

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.