Java tutorial
/* * Copyright (c) 2008-2011, Martijn Brinkers, Djigzo. * * This file is part of Djigzo email encryption. * * Djigzo is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3, 19 November 2007 as published by the Free Software * Foundation. * * Djigzo 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public * License along with Djigzo. If not, see <http://www.gnu.org/licenses/> * * Additional permission under GNU AGPL version 3 section 7 * * If you modify this Program, or any covered work, by linking or * combining it with aspectjrt.jar, aspectjweaver.jar, tyrex-1.0.3.jar, * freemarker.jar, dom4j.jar, mx4j-jmx.jar, mx4j-tools.jar, * spice-classman-1.0.jar, spice-loggerstore-0.5.jar, spice-salt-0.8.jar, * spice-xmlpolicy-1.0.jar, saaj-api-1.3.jar, saaj-impl-1.3.jar, * wsdl4j-1.6.1.jar (or modified versions of these libraries), * containing parts covered by the terms of Eclipse Public License, * tyrex license, freemarker license, dom4j license, mx4j license, * Spice Software License, Common Development and Distribution License * (CDDL), Common Public License (CPL) the licensors of this Program grant * you additional permission to convey the resulting work. */ package mitm.common.security.crl; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.security.NoSuchProviderException; import java.security.cert.CRL; import java.security.cert.CRLException; import java.security.cert.CertificateException; import java.security.cert.X509CRL; import java.util.Collection; import java.util.LinkedList; import mitm.common.net.NetUtils; import mitm.common.net.ProxyException; import mitm.common.net.ProxyInjector; import mitm.common.scheduler.HTTPMethodAbortTimeoutTask; import mitm.common.scheduler.InputStreamTimeoutTask; import mitm.common.scheduler.Task; import mitm.common.scheduler.TaskScheduler; import mitm.common.scheduler.ThreadInterruptTimeoutTask; import mitm.common.security.SecurityFactoryFactoryException; import mitm.common.util.Check; import mitm.common.util.ReadableOutputStreamBuffer; import mitm.common.util.SizeLimitedInputStream; import mitm.common.util.SizeUtils; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpException; import org.apache.commons.httpclient.HttpMethod; import org.apache.commons.httpclient.HttpStatus; import org.apache.commons.httpclient.methods.GetMethod; import org.apache.commons.httpclient.params.HttpConnectionManagerParams; import org.apache.commons.io.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class HTTPCRLDownloadHandler implements CRLDownloadHandler { private static final Logger logger = LoggerFactory.getLogger(HTTPCRLDownloadHandler.class); public static final String TIMEOUT_ERROR = "A timeout has occured while downloading CRL from: "; /* * The default size at which a downloaded CRL will be streamed to disk */ private final static int DEFAULT_MEM_THRESHOLD = SizeUtils.MB * 5; /* * The CRL download settings (like timeouts, max bytes etc.) */ private final CRLDownloadParameters downloadParameters; /* * Used to set the proxy for HttpClient */ private final ProxyInjector proxyInjector; /* * The size at which a downloaded CRL will be streamed to disk */ private int memThreshold = DEFAULT_MEM_THRESHOLD; public HTTPCRLDownloadHandler(CRLDownloadParameters downloadParameters) { this(downloadParameters, null); } public HTTPCRLDownloadHandler(CRLDownloadParameters downloadParameters, ProxyInjector proxyInjector) { this.downloadParameters = downloadParameters; this.proxyInjector = proxyInjector; } @Override public boolean canHandle(URI uri) { Check.notNull(uri, "uri"); String scheme = uri.getScheme(); return scheme != null && (scheme.equalsIgnoreCase("http") || scheme.equalsIgnoreCase("https")); } @Override public Collection<? extends CRL> downloadCRLs(URI uri) throws IOException, CRLException { /* * sanity check */ if (!canHandle(uri)) { throw new IllegalArgumentException("The uri cannot be handled by this handler."); } Collection<? extends CRL> crls = null; /* * watchdog to prevent runaway downloads */ TaskScheduler watchdog = new TaskScheduler(HTTPCRLDownloadHandler.class.getCanonicalName()); try { crls = downloadCRLs(uri, watchdog); } finally { /* * we must cancel the watchdogs */ watchdog.cancel(); } if (crls == null) { crls = new LinkedList<X509CRL>(); } return crls; } private Collection<? extends CRL> downloadCRLs(URI uri, TaskScheduler watchdog) throws IOException, HttpException, CRLException, FileNotFoundException { Collection<? extends CRL> crls = null; HttpClient httpClient = new HttpClient(); HttpConnectionManagerParams params = httpClient.getHttpConnectionManager().getParams(); params.setConnectionTimeout((int) downloadParameters.getConnectTimeout()); params.setSoTimeout((int) downloadParameters.getReadTimeout()); if (proxyInjector != null) { try { proxyInjector.setProxy(httpClient); } catch (ProxyException e) { throw new IOException(e); } } HttpMethod getMethod = new GetMethod(uri.toString()); getMethod.setFollowRedirects(true); getMethod.setRequestHeader("User-Agent", NetUtils.HTTP_USER_AGENT); /* * Add watchdog that will interrupt the thread on timeout. we want the abort to fire first so add 50% */ Task threadWatchdogTask = new ThreadInterruptTimeoutTask(Thread.currentThread(), watchdog.getName()); watchdog.addTask(threadWatchdogTask, (long) (downloadParameters.getTotalTimeout() * 1.5)); /* * Add watchdog that will abort the HTTPMethod on timeout. we want to close the input first so add 20% */ Task httpMethodAbortTimeoutTask = new HTTPMethodAbortTimeoutTask(getMethod, watchdog.getName()); watchdog.addTask(httpMethodAbortTimeoutTask, (long) (downloadParameters.getTotalTimeout() * 1.2)); try { logger.debug("Setting up a connection to: " + uri); int statusCode = 0; try { statusCode = httpClient.executeMethod(getMethod); } catch (IllegalArgumentException e) { /* * HttpClient can throw IllegalArgumentException when the host is not set */ throw new CRLException(e); } if (statusCode != HttpStatus.SC_OK) { throw new IOException("Error getting CRL. Message: " + getMethod.getStatusLine()); } InputStream urlStream = getMethod.getResponseBodyAsStream(); if (urlStream == null) { throw new IOException("Response body is null."); } /* * add a timeout watchdog on the input */ Task inputWatchdogTask = new InputStreamTimeoutTask(urlStream, watchdog.getName()); watchdog.addTask(inputWatchdogTask, downloadParameters.getTotalTimeout()); /* * we want to set a max on the number of bytes to download. We do not want * a rogue server to provide us with a 1 TB CRL. */ InputStream limitInputStream = new SizeLimitedInputStream(urlStream, downloadParameters.getMaxBytes()); ReadableOutputStreamBuffer output = new ReadableOutputStreamBuffer(memThreshold); try { IOUtils.copy(limitInputStream, output); if (threadWatchdogTask.hasRun() || httpMethodAbortTimeoutTask.hasRun() || inputWatchdogTask.hasRun()) { /* a timeout has occurred */ throw new IOException(TIMEOUT_ERROR + uri); } try { InputStream input = output.getInputStream(); try { crls = CRLUtils.readCRLs(input); } finally { IOUtils.closeQuietly(input); } if (crls == null || crls.size() == 0) { logger.debug("No CRLs found in the downloaded stream."); } } catch (CertificateException e) { throw new CRLException(e); } catch (NoSuchProviderException e) { throw new CRLException(e); } catch (SecurityFactoryFactoryException e) { throw new CRLException(e); } } finally { /* * we need to close ReadableOutputStreamBuffer to prevent temp file leak */ IOUtils.closeQuietly(output); } } finally { getMethod.releaseConnection(); } return crls; } public int getMemThreshold() { return memThreshold; } public void setMemThreshold(int memThreshold) { this.memThreshold = memThreshold; } }