/******************************************************************************* * gMix open source project - https://svs.informatik.uni-hamburg.de/gmix/ * Copyright (C) 2014 SVS * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. *******************************************************************************/ /** * */ package userGeneratedContent.testbedPlugIns.layerPlugIns.layer5application.httpPush_v0_001.plugin; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.util.Arrays; import java.util.Hashtable; import java.util.List; import java.util.zip.DeflaterInputStream; import java.util.zip.DeflaterOutputStream; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; import org.apache.commons.compress.utils.IOUtils; import staticContent.framework.util.Util; import userGeneratedContent.testbedPlugIns.layerPlugIns.layer5application.httpPush_v0_001.dataObjects.Cache; import userGeneratedContent.testbedPlugIns.layerPlugIns.layer5application.httpPush_v0_001.dataObjects.Connection; import userGeneratedContent.testbedPlugIns.layerPlugIns.layer5application.httpPush_v0_001.dataObjects.HttpInfo; import userGeneratedContent.testbedPlugIns.layerPlugIns.layer5application.httpPush_v0_001.dataObjects.HttpPartType; import userGeneratedContent.testbedPlugIns.layerPlugIns.layer5application.httpPush_v0_001.helper.HtmlParser; import userGeneratedContent.testbedPlugIns.layerPlugIns.layer5application.httpPush_v0_001.helper.HttpParser; import userGeneratedContent.testbedPlugIns.layerPlugIns.layer5application.httpPush_v0_001.improvement.EntryImprovementInterface; import userGeneratedContent.testbedPlugIns.layerPlugIns.layer5application.httpPush_v0_001.improvement.ExitImprovementInterface; /** * @author bash * * This class implements the activeCache Improvement. * * The methods isAppBodyToMixImproveable() and isAppBodyFromMixImproveable() always returns false. There is no case where a HTTP Message * from a Webbrowser has a improveable Body. * * The method isWebBodyToMixImproveable() and isWebBodyFromMixImproveable() checks a header if a Body exists. * Returns true if they exists and false otherwise. * * The methods toMixBodyImprovement() and toWebBodyImprovement() should not called. It would return an unmodificated Body. The reason * is the same as above. * * The method toMixHeaderImprovement() checks if a body is listed in the cache by the identifier (URI). * If true the entryWriteOut variable is set to false. Then the method checks if the repsonse is completly stored in the cache. * If it is completely stored in the cache the method returns the response to the webbrowser. Otherwise it added the request * requestlist and set the entry status to requested. * If the request is not listed in the chache the request is forwarded to the mix and adds the request to the responselist. * * The method toWebHeaderImprovement() only adds the Identifier (URI) the to the responselist. * * The method fromWebHeaderImprovement() polls the identifier and adds it to the message. * * The method fromWebBodyImprovement() search a body via RegEx for suprequests and send them to the web. * It also stores the Identifier to the responselist * * The method fromMixHeaderImprovement() adds an entry to the cache an set the the status of complete to false. * The identifier for the entry is polled from the responselist. The the header is added to the cache * * The method fromMixBodyImprovement() search a body via RegEx for suprequests and adds the identifier to the responselist. * Then the methods adds the body part to the cache. If it ist the complete body or the last chunk by chunked encoding it set the status of the entry to complete * If the entry is complete it checks if the entry is already requested. If true the method returns the complete response. * * There are four private methods to compress and decompress a body compressed with GZIP or Deflate. * They are similar to the methods in the headerCompression-Plugin. * * */ public class ActiveCacheImprovement2 implements EntryImprovementInterface, ExitImprovementInterface { private boolean entryWriteOut; private boolean exitWriteOut; private String actualIdentifier; /** * */ public ActiveCacheImprovement2() { entryWriteOut = true; exitWriteOut = true; } /* * (non-Javadoc) * * @see * improvement.ExitImprovementInterface#isWebBodyImproveable(java.util.Hashtable * ) */ @Override public boolean isWebBodyToMixImproveable(Hashtable<String, String> headerTable, Connection connection) { String contentType = headerTable.get("content-type"); if (contentType.contains("text/html") || contentType.contains("text/css")) { return true; } else { return false; } } /* * (non-Javadoc) * * @see improvement.ExitImprovementInterface#toWebHeaderImprovement(byte[], * dataObjects.Connection) */ @Override public byte[] toWebHeaderImprovement(byte[] message, Connection connection) { String identifier = connection.getStatusHTTPFromMix().getHeader().get("uri"); connection.requestQueue.add(connection.requestId); connection.requestId++; connection.getIdentifierQueue().add(identifier); return message; } /* * (non-Javadoc) * * @see * improvement.ExitImprovementInterface#fromWebHeaderImprovement(byte[], * dataObjects.Connection) */ @Override public byte[] fromWebHeaderImprovement(byte[] message, Connection connection) { int requestId = connection.requestQueue.poll(); connection.actualRequestId = requestId; message = Util.concatArrays(Util.intToByteArray(requestId), message); return message; } /* * (non-Javadoc) * * @see improvement.ExitImprovementInterface#toWebBodyImprovement(byte[], * dataObjects.Connection) */ @Override public byte[] toWebBodyImprovement(byte[] message, Connection connection) { return message; } /* * (non-Javadoc) * * @see improvement.ExitImprovementInterface#fromWebBodyImprovement(byte[], * dataObjects.Connection) */ @Override public byte[] fromWebBodyImprovement(byte[] message, Connection connection) { int id = connection.actualRequestId; byte[] improvedMessage = message; Hashtable<String, String> header = connection.getStatusHTTPFromApp().getHeader(); // DEcompress message if (header.containsKey("content-encoding")) { String compressionType = header.get("content-encoding"); if (compressionType.equals("gzip")) { improvedMessage = decompressGzip(improvedMessage); } else if (compressionType.equals("deflate")) { improvedMessage = decompressDeflate(improvedMessage); } else { System.out.println("Unknown Compression"); return message; } } Hashtable<String, String> headerReq = connection.getStatusHTTPFromMix().getHeader(); // Determine contenttype String contentType = header.get("content-type"); List<String> subpages; if (contentType.contains("html")) { subpages = HtmlParser.getAllRessourcesHtml(headerReq.get("uri"), new String(improvedMessage)); } else if (contentType.contains("css")) { subpages = HtmlParser.getAllRessourcesCss(new String(improvedMessage)); } else { return message; } // Generate subrequests for (String ressource : subpages) { String saveUri = headerReq.get("uri"); headerReq.put("uri", ressource); // System.out.println(ressource); // TODO: Referer setzen String request = HttpParser.composeGetRequest(headerReq); connection.getIdentifierQueue().add(ressource); connection.addMethodToQueue("GET"); connection.requestQueue.add(id); connection.writeChunk(ByteBuffer.wrap(request.getBytes())); // TODO: // neue // connections // aufmachen? headerReq.put("uri", saveUri); } return message; } /* * (non-Javadoc) * * @see * improvement.EntryImprovementInterface#isAppBodyImproveable(java.util. * Hashtable) */ @Override public boolean isAppBodyFromMixImproveable(Hashtable<String, String> headerTable, Connection connection) { if (headerTable.contains("content-type")) { String contentType = headerTable.get("content-type"); if (contentType.contains("text/html") || contentType.contains("text/css")) { return true; } else { return true; } } return true; } /* * (non-Javadoc) * * @see * improvement.EntryImprovementInterface#fromMixHeaderImprovement(byte[], * dataObjects.Connection) */ @Override public byte[] fromMixHeaderImprovement(byte[] message, Connection connection) { int id = Util.byteArrayToInt(Arrays.copyOfRange(message, 0, 4)); message = Arrays.copyOfRange(message, 4, message.length); connection.actualRequestId = id; String identifier = connection.requestTable.get(id).poll(); // System.out.println("Request-Uri " + identifier); Cache cache = connection.getCache(); actualIdentifier = identifier; cache.addNewCacheEntry(identifier, message); entryWriteOut = false; return message; } /* * (non-Javadoc) * * @see improvement.EntryImprovementInterface#toMixHeaderImprovement(byte[], * dataObjects.Connection) */ @Override public byte[] toMixHeaderImprovement(byte[] message, Connection connection) { Hashtable<String, String> header = connection.getStatusHTTPFromApp().getHeader(); String identifier = header.get("uri"); Cache cache = connection.getCache(); if (cache.containsEntry(identifier)) { message = null; } else { int id = connection.requestId; connection.requestQueue.add(id); connection.requestId++; connection.addIdToTable(id, identifier); } cache.invokeEventsOfConnection(connection, identifier); return message; } /* * (non-Javadoc) * * @see improvement.EntryImprovementInterface#fromMixBodyImprovement(byte[], * dataObjects.Connection) */ @Override public byte[] fromMixBodyImprovement(byte[] message, Connection connection) { int id = connection.actualRequestId; byte[] improvedMessage = message; HttpInfo status = connection.getStatusHTTPFromMix(); Hashtable<String, String> header = status.getHeader(); String identifier = actualIdentifier; Cache cache = connection.getCache(); cache.appendMessageToEntry(identifier, message); // Check if entry complete HttpPartType type = status.getType(); // System.out.println("fromMixBodyImprovement "+new String(message)); if (new String(message).equals("0\r\n\r\n") || type == HttpPartType.Body) { cache.setMessageComplete(identifier, true); cache.invokeEvents(identifier); } if (header.containsKey("content-encoding")) { String compressionType = header.get("content-encoding"); if (compressionType.equals("gzip")) { improvedMessage = decompressGzip(improvedMessage); } else if (compressionType.equals("deflate")) { improvedMessage = decompressDeflate(improvedMessage); } else { System.out.println("Unknown Compression"); return message; } } if (header != null) { String contentType = header.get("content-type"); List<String> subpages; if (contentType == null) { System.out.println("Debug"); } if (contentType.contains("html")) { subpages = HtmlParser.getAllRessourcesHtml(connection.getStatusHTTPFromApp().getHeader().get("uri"), new String(improvedMessage)); } else if (contentType.contains("css")) { subpages = HtmlParser.getAllRessourcesCss(new String(improvedMessage)); } else { return message; } for (String ressource : subpages) { // System.out.println(ressource); // connection.getIdentifierQueue().add(ressource); connection.requestTable.get(id).add(ressource); connection.requestQueue.add(id); connection.addMethodToQueue("GET"); cache.addNewCacheEntry(ressource, null); cache.setWebRequest(ressource); } } entryWriteOut = false; return message; } /* * (non-Javadoc) * * @see improvement.EntryImprovementInterface#toMixBodyImprovement(byte[], * dataObjects.Connection) */ @Override public byte[] toMixBodyImprovement(byte[] message, Connection connection) { return null; } @Override public boolean isExitWriteOut() { return exitWriteOut; } @Override public boolean isEntryWriteOut() { return entryWriteOut; } // Helpermethods /** * Methos to compress a byte[] with Gzip * * @param content * @return gzipcompressed bytey[] */ private static byte[] compressGzip(byte[] content) { ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); try { GZIPOutputStream gzipOutputStream = new GZIPOutputStream(byteArrayOutputStream); gzipOutputStream.write(content); gzipOutputStream.close(); } catch (IOException e) { throw new RuntimeException(e); } System.out.printf("Compression ratio %f\n", (1.0f * content.length / byteArrayOutputStream.size())); return byteArrayOutputStream.toByteArray(); } /** * Method to decompress a byte[] * * @param contentBytes * compresed with Gzip * @return an uncompressed byte[] */ private static byte[] decompressGzip(byte[] contentBytes) { ByteArrayOutputStream out = new ByteArrayOutputStream(); try { IOUtils.copy(new GZIPInputStream(new ByteArrayInputStream(contentBytes)), out); } catch (IOException e) { throw new RuntimeException(e); } return out.toByteArray(); } /** * Methos to compress a byte[] with Deflate * * @param content * @return deflated byte[] */ private static byte[] compressDeflate(byte[] content) { ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); try { DeflaterOutputStream deflaterOutputStream = new DeflaterOutputStream(byteArrayOutputStream); deflaterOutputStream.write(content); deflaterOutputStream.close(); } catch (IOException e) { throw new RuntimeException(e); } System.out.printf("Compression ratio %f\n", (1.0f * content.length / byteArrayOutputStream.size())); return byteArrayOutputStream.toByteArray(); } /** * Method to decompress a byte[] * * @param contentBytes * compresed with Deflate * @return an uncompressed byte[] */ private static byte[] decompressDeflate(byte[] contentBytes) { ByteArrayOutputStream out = new ByteArrayOutputStream(); try { IOUtils.copy(new DeflaterInputStream(new ByteArrayInputStream(contentBytes)), out); } catch (IOException e) { throw new RuntimeException(e); } return out.toByteArray(); } @Override public boolean isWebBodyFromMixImproveable(Hashtable<String, String> headerTable, Connection connection) { return false; } @Override public boolean isAppBodyToMixImproveable(Hashtable<String, String> headerTable, Connection connection) { return false; } // private byte[] requestSubpage(byte[] request) { // byte[] returnValue; // Socket socket = null; // try { // socket = new Socket("localhost", 4007); // } catch (UnknownHostException e) { // // TODO Auto-generated catch block // e.printStackTrace(); // } catch (IOException e) { // // TODO Auto-generated catch block // e.printStackTrace(); // } // // try { // InputStream input = socket.getInputStream(); // // OutputStream output = socket.getOutputStream(); // // output.write(request); // output.flush(); // String answer = new String(); // returnValue. // String sign = input.read(); // while (sign != -1) { // answer += sign; // sign = (char) input.read(); // // } // System.out.println(answer); // socket.close(); // // } catch (IOException e) { // // TODO Auto-generated catch block // e.printStackTrace(); // } // // // // return returnValue; // } }