/* * Copyright(c) 2005 Center for E-Commerce Infrastructure Development, The * University of Hong Kong (HKU). All Rights Reserved. * * This software is licensed under the GNU GENERAL PUBLIC LICENSE Version 2.0 [1] * * [1] http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt */ package hk.hku.cecid.piazza.commons.net; import com.sun.org.apache.xml.internal.security.utils.Base64; import hk.hku.cecid.piazza.commons.Sys; import hk.hku.cecid.piazza.commons.io.IOHandler; import hk.hku.cecid.piazza.commons.module.Component; import java.io.InputStream; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.util.Iterator; import java.util.Map; import java.util.Vector; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.KeyManager; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; /** * HttpConnector is a connector for making HTTP/S connections to an URL. * * @author Hugo Y. K. Lam * */ public class HttpConnector { private URL url; private Vector keyManagers = new Vector(); private Vector trustManagers = new Vector(); private HostnameVerifier hostnameVerifier; private Map headers; private String username; private String password; /** * Creates a new instance of HttpConnector. * * @param destUrl the destination URL, either in String or URL format. * @throws MalformedURLException if the URL is malformed. */ public HttpConnector(Object destUrl) throws MalformedURLException { if (destUrl instanceof URL) { url = (URL)destUrl; } else { url = new URL(destUrl.toString()); } setDefaultManagers(); } /** * Sets the default key manager and trust manager. This method will look up * module components of key manager (id: ssl-key-manager) and trust manager * (id: ssl-trust-manager) in the system main module. */ private void setDefaultManagers() { Component keyman = Sys.main.getComponent("ssl-key-manager"); Component trustman = Sys.main.getComponent("ssl-trust-manager"); if (keyman != null && keyman instanceof KeyManager) { addKeyManager((KeyManager)keyman); } if (trustman != null && trustman instanceof TrustManager) { addTrustManager((TrustManager)trustman); } } /** * Sets a host name verifier for SSL connection. * * @param hostnameVerifier the host name verifier. */ public void setHostnameVerifier(HostnameVerifier hostnameVerifier) { this.hostnameVerifier = hostnameVerifier; } /** * Gets the host verifier for SSL connection. * * @return the host name verifier. */ public HostnameVerifier getHostnameVerifier() { return hostnameVerifier == null? HttpsURLConnection.getDefaultHostnameVerifier() : hostnameVerifier; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } /** * Adds a key manager for SSL connection. * * @param km the key manager. */ public void addKeyManager(KeyManager km) { if (km != null) { keyManagers.addElement(km); } } /** * Adds a trust manager for SSL connection. * * @param tm the trust manager. */ public void addTrustManager(TrustManager tm) { if (tm != null) { trustManagers.addElement(tm); } } /** * Gets the SSL socket factory which is used in SSL connection. * * @return the SSL socket factory. * @throws ConnectionException if unable to create SSL socket factory. */ public SSLSocketFactory getSSLSocketFactory() throws ConnectionException { try { if (keyManagers.size() > 0 || trustManagers.size() > 0) { SSLContext context = SSLContext.getInstance("SSL"); context.init((KeyManager[]) keyManagers.toArray(new KeyManager[] {}), (TrustManager[]) trustManagers.toArray(new TrustManager[] {}), null); return context.getSocketFactory(); } else { return HttpsURLConnection.getDefaultSSLSocketFactory(); } } catch (Exception e) { throw new ConnectionException("Unable to create SSL socket factory", e); } } /** * Sets the HTTP request headers. * * @param headers the HTTP headers. */ public void setRequestHeaders(Map headers) { this.headers = headers; } /** * Gets the HTTP request headers. * * @return the HTTP headers, or null. */ public Map getRequestHeaders() { return headers; } /** * Creates a new HTTP connection based on this connector's properties. * * @return a new HTTP connection. * @throws ConnectionException if unable to create a new HTTP connection. */ public HttpURLConnection createConnection() throws ConnectionException { try { HttpURLConnection connection = (HttpURLConnection) url.openConnection(); if(username != null && password != null) { String encodedPassword = username + ":" + password; String encoded = Base64.encode(encodedPassword.getBytes()); connection.setRequestProperty("Authorization", "Basic "+encoded); } connection.setUseCaches(false); if (connection instanceof HttpsURLConnection) { HttpsURLConnection httpsConnection = (HttpsURLConnection) connection; httpsConnection.setHostnameVerifier(getHostnameVerifier()); httpsConnection.setSSLSocketFactory(getSSLSocketFactory()); } if (headers != null) { Iterator headerKeys = headers.keySet().iterator(); while (headerKeys.hasNext()) { String headerKey = headerKeys.next().toString(); Object headerValue = headers.get(headerKey); if (headerValue != null) { connection.addRequestProperty(headerKey, headerValue.toString()); } } } return connection; } catch (Exception e) { throw new ConnectionException("Unable to create HTTP connection", e); } } /** * Sends an HTTP/S request using the given HTTP connection. * * @param request the HTTP request content or null for a simple get request. * @return an input stream for reading the reply from the host. * @throws ConnectionException if failed in sending the HTTP request or * creating a new connection. */ public InputStream send(InputStream request) throws ConnectionException { return send(request, createConnection()); } /** * Sends an HTTP/S request using the given HTTP connection. * * @param request the HTTP request content or null for a simple get request. * @param connection the HTTP connection for sending the request. * @return an input stream for reading the reply from the host. * @throws ConnectionException if failed in sending the HTTP request. */ public InputStream send(InputStream request, HttpURLConnection connection) throws ConnectionException { OutputStream outstream = null; try { if (request != null) { connection.setRequestMethod("POST"); connection.setDoOutput(true); outstream = connection.getOutputStream(); IOHandler.pipe(request, outstream); } else { connection.setRequestMethod("GET"); } connection.connect(); return connection.getInputStream(); } catch (Exception e) { throw new ConnectionException("Unable to send HTTP request", e); } finally { try { if (outstream != null) { outstream.close(); } } catch (Exception e) { } } } }