/******************************************************************************* * Copyright 2013-2015 alladin-IT GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package at.alladin.rmbt.client.v2.task; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; import java.security.DigestInputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Iterator; import java.util.List; import java.util.Map.Entry; import java.util.concurrent.Callable; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; import at.alladin.rmbt.client.QualityOfServiceTest; import at.alladin.rmbt.client.RMBTClient; import at.alladin.rmbt.client.v2.task.result.QoSTestResult; import at.alladin.rmbt.client.v2.task.result.QoSTestResultEnum; /** * * @author lb * */ public class HttpProxyTask extends AbstractQoSTask { private final String target; private final String range; private final long connectionTimeout; private final long downloadTimeout; public final static long DEFAULT_CONNECTION_TIMEOUT = 5000000000L; public final static long DEFAULT_DOWNLOAD_TIMEOUT = 10000000000L; public final static String PARAM_TARGET = "url"; public final static String PARAM_RANGE = "range"; public final static String PARAM_CONNECTION_TIMEOUT = "conn_timeout"; public final static String PARAM_DOWNLOAD_TIMEOUT = "download_timeout"; public final static String RESULT_STATUS = "http_result_status"; public final static String RESULT_DURATION = "http_result_duration"; public final static String RESULT_LENGTH = "http_result_length"; public final static String RESULT_HEADER = "http_result_header"; public final static String RESULT_RANGE = "http_objective_range"; public final static String RESULT_TARGET = "http_objective_url"; public final static String RESULT_HASH = "http_result_hash"; public final AtomicBoolean downloadCompleted = new AtomicBoolean(false); public final AtomicBoolean timeOutReached = new AtomicBoolean(false); public static class Md5Result { String md5; long contentLength = 0; long generatingTimeNs = 0; } /** * * @param taskDesc */ public HttpProxyTask(QualityOfServiceTest nnTest, TaskDesc taskDesc, int threadId) { super(nnTest, taskDesc, threadId, threadId); this.target = (String)taskDesc.getParams().get(PARAM_TARGET); this.range = (String)taskDesc.getParams().get(PARAM_RANGE); String value = (String) taskDesc.getParams().get(PARAM_CONNECTION_TIMEOUT); this.connectionTimeout = value != null ? Long.valueOf(value) : DEFAULT_CONNECTION_TIMEOUT; value = (String) taskDesc.getParams().get(PARAM_DOWNLOAD_TIMEOUT); this.downloadTimeout = value != null ? Long.valueOf(value) : DEFAULT_DOWNLOAD_TIMEOUT; } /* * (non-Javadoc) * @see java.util.concurrent.Callable#call() */ public QoSTestResult call() throws Exception { final QoSTestResult result = initQoSTestResult(QoSTestResultEnum.HTTP_PROXY); try { result.getResultMap().put(RESULT_RANGE, range); result.getResultMap().put(RESULT_TARGET, target); onStart(result); Future<QoSTestResult> httpTimeoutTask = RMBTClient.getCommonThreadPool().submit(new Callable<QoSTestResult>() { public QoSTestResult call() throws Exception { httpGet(result); return result; } }); final QoSTestResult testResult = httpTimeoutTask.get(downloadTimeout, TimeUnit.NANOSECONDS); return testResult; } catch (TimeoutException e) { e.printStackTrace(); result.getResultMap().put(RESULT_HASH, "TIMEOUT"); } catch (Exception e) { throw e; } finally { onEnd(result); } return result; } private QoSTestResult httpGet(final QoSTestResult result) throws Exception { final URL url = new URL(this.target); final HttpURLConnection httpGet; String hash = null; try { Thread timeoutThread = new Thread(new Runnable() { public void run() { try { System.out.println("HTTP PROXY TIMEOUT THREAD: " + downloadTimeout + " ms"); Thread.sleep((int)(downloadTimeout / 1000000)); if (!downloadCompleted.get()) { timeOutReached.set(true); System.out.println("HTTP PROXY TIMEOUT REACHED"); } } catch (InterruptedException e) { e.printStackTrace(); } } }); timeoutThread.start(); final long start = System.nanoTime(); httpGet = (HttpURLConnection) url.openConnection(); if (range != null && range.startsWith("bytes")) { httpGet.addRequestProperty("Range", range); } httpGet.setConnectTimeout((int) TimeUnit.MILLISECONDS.convert(connectionTimeout, TimeUnit.NANOSECONDS)); httpGet.setReadTimeout((int) TimeUnit.MILLISECONDS.convert(downloadTimeout, TimeUnit.NANOSECONDS)); httpGet.setInstanceFollowRedirects(false); Md5Result md5 = generateChecksum(httpGet.getInputStream()); downloadCompleted.set(true); hash = md5.md5; final long duration = System.nanoTime() - start; result.getResultMap().put(RESULT_DURATION, duration - md5.generatingTimeNs); result.getResultMap().put(RESULT_STATUS, httpGet.getResponseCode()); result.getResultMap().put(RESULT_LENGTH, md5.contentLength); final String headers; if (httpGet.getHeaderFields() != null) { final StringBuilder sb = new StringBuilder(); final Iterator<Entry<String, List<String>>> headerIterator = httpGet.getHeaderFields().entrySet().iterator(); while (headerIterator.hasNext()) { final Entry<String, List<String>> e = headerIterator.next(); if (e.getKey() != null && !e.getKey().equals("null")) { sb.append(e.getKey()); sb.append(": "); final List<String> values = e.getValue(); for (int i = 0; i < values.size(); i++) { sb.append(values.get(i)); if (i+1 < values.size()) { sb.append(","); } } sb.append("\n"); } } headers = sb.toString(); } else { headers = null; } result.getResultMap().put(RESULT_HEADER, headers); } catch (Exception e) { e.printStackTrace(); result.getResultMap().put(RESULT_STATUS, ""); result.getResultMap().put(RESULT_LENGTH, 0); result.getResultMap().put(RESULT_HEADER, ""); } finally { if (timeOutReached.get()) { result.getResultMap().put(RESULT_HASH, "TIMEOUT"); } else if (hash != null) { result.getResultMap().put(RESULT_HASH, hash); } else { result.getResultMap().put(RESULT_HASH, "ERROR"); } } return result; } /** * * @param is * @return */ public static String getStringFromInputStream(InputStream is) { BufferedReader br = null; StringBuilder sb = new StringBuilder(); String line; try { br = new BufferedReader(new InputStreamReader(is)); while ((line = br.readLine()) != null) { sb.append(line); sb.append("\n"); } } catch (IOException e) { e.printStackTrace(); } finally { if (br != null) { try { br.close(); } catch (IOException e) { e.printStackTrace(); } } } return sb.toString(); } public long writeFileFromInputStream(InputStream is, File outputFile) throws FileNotFoundException, IOException { return copyInputStreamToOutputStream(is, new FileOutputStream(outputFile)); } public long copyInputStreamToOutputStream(InputStream input, OutputStream output) throws IOException { byte[] buffer = new byte[4096]; long count = 0L; int n = 0; while (-1 != (n = input.read(buffer))) { if (timeOutReached.get()) { break; } output.write(buffer, 0, n); count += n; } downloadCompleted.set(true); output.close(); return count; } /** * * @param input * @return * @throws NoSuchAlgorithmException */ public static String generateChecksum(byte[] input) throws NoSuchAlgorithmException { MessageDigest md = MessageDigest.getInstance("MD5"); byte[] hash = md.digest(input); return generateChecksumFromDigest(hash); } /** * * @param file * @return * @throws NoSuchAlgorithmException * @throws IOException */ public static Md5Result generateChecksum(File file) throws NoSuchAlgorithmException, IOException { return generateChecksum(new FileInputStream(file)); } /** * * @param inputStream * @return * @throws NoSuchAlgorithmException * @throws IOException */ public static Md5Result generateChecksum(InputStream inputStream) throws NoSuchAlgorithmException, IOException { Md5Result md5 = new Md5Result(); MessageDigest md = MessageDigest.getInstance("MD5"); DigestInputStream dis = new DigestInputStream(inputStream, md); byte[] dataBytes = new byte[4096]; int nread = 0; while ((nread = dis.read(dataBytes)) != -1) { md5.contentLength += nread; }; dis.close(); long startNs = System.nanoTime(); md5.md5 = generateChecksumFromDigest(md.digest()); md5.generatingTimeNs = System.nanoTime() - startNs; return md5; } /** * * @param digest * @return */ public static String generateChecksumFromDigest(byte[] digest) { StringBuilder hexString = new StringBuilder(); for (int i = 0; i < digest.length; i++) { if ((0xff & digest[i]) < 0x10) { hexString.append("0" + Integer.toHexString((0xFF & digest[i]))); } else { hexString.append(Integer.toHexString(0xFF & digest[i])); } } return hexString.toString(); } /* * (non-Javadoc) * @see at.alladin.rmbt.client.v2.task.AbstractRmbtTask#initTask() */ @Override public void initTask() { // TODO Auto-generated method stub } /* * (non-Javadoc) * @see at.alladin.rmbt.client.v2.task.QoSTask#getTestType() */ public QoSTestResultEnum getTestType() { return QoSTestResultEnum.HTTP_PROXY; } /* * (non-Javadoc) * @see at.alladin.rmbt.client.v2.task.QoSTask#needsQoSControlConnection() */ public boolean needsQoSControlConnection() { return false; } }