X-Git-Url: https://juplo.de/gitweb/?p=percentcodec;a=blobdiff_plain;f=accelerator%2Fsrc%2Fmain%2Fjava%2Fde%2Fjuplo%2Faccelerator%2FAcceleratorFilter.java;fp=accelerator%2Fsrc%2Fmain%2Fjava%2Fde%2Fjuplo%2Faccelerator%2FAcceleratorFilter.java;h=0000000000000000000000000000000000000000;hp=7b1e3062b6aee51d5da7e5dd5e7d060d7f894714;hb=f95f687755d54c46975b15dbd0221e82a7458f79;hpb=4f07e33a7c246caa3e4cd7c939f75064e4af03b0 diff --git a/accelerator/src/main/java/de/juplo/accelerator/AcceleratorFilter.java b/accelerator/src/main/java/de/juplo/accelerator/AcceleratorFilter.java deleted file mode 100644 index 7b1e3062..00000000 --- a/accelerator/src/main/java/de/juplo/accelerator/AcceleratorFilter.java +++ /dev/null @@ -1,724 +0,0 @@ -package de.juplo.accelerator; - -import java.io.IOException; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.PrintWriter; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.Collections; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.Locale; -import java.util.Map; -import java.util.zip.GZIPOutputStream; -import javax.servlet.Filter; -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.ServletException; -import javax.servlet.ServletOutputStream; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowire; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Configurable; -import org.springframework.beans.factory.annotation.Qualifier; - - - -/** - * - * @author kai - */ -@Configurable(autowire=Autowire.BY_NAME) -public class AcceleratorFilter implements Filter { - private final static Logger log = LoggerFactory.getLogger(AcceleratorFilter.class); - - private final static Map EMPTY = Collections.unmodifiableMap(new HashMap()); - - public final static Integer DEFAULT_BUFFER_SIZE = 1024; - public final static String REQUEST_URI_ATTRIBUTE = "javax.servlet.include.request_uri"; - public final static String RESPONSE_WRAPPER = AcceleratorFilter.class.getName() + ".RESPONSE_WRAPPER"; - - - @Autowired CacheControl cacheControl; - @Autowired(required=false) @Qualifier("defaultBufferSize") Integer defaultBufferSize = DEFAULT_BUFFER_SIZE; - @Autowired String eTag; - @Autowired Boolean weak; - @Autowired Long lastModified; - @Autowired Integer cacheSeconds; - - - @Override - public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { - if (!(request instanceof HttpServletRequest)) { - log.error("AcceleratorFilter can only handle HTTP-requests"); - chain.doFilter(request, response); - return; - } - - HttpServletRequest httpRequest = (HttpServletRequest)request; - HttpServletResponse httpResponse = (HttpServletResponse)response; - - AccelerationWrapper wrapper; - - wrapper = (AccelerationWrapper)request.getAttribute(AcceleratorFilter.RESPONSE_WRAPPER); - if (wrapper != null) { - if (wrapper.getFilter() == this) { - /** Ignore multiple mappings of the same filter-instance */ - log.warn("Ignoring multiple mappings on same URL: {}", httpRequest.getRequestURI()); - chain.doFilter(request, response); - return; - } - else { - log.error("Only one instance of AcceleratorFilter must be mapped to any URL: {}", httpRequest.getRequestURI()); - httpResponse.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Only one instance of AcceleratorFilter must be mapped to any URL!"); - return; - } - } - - wrapper = new AccelerationWrapper(httpRequest, httpResponse); - httpRequest.setAttribute(RESPONSE_WRAPPER, wrapper); - cacheControl.init(wrapper); - try { - chain.doFilter(request, wrapper); - wrapper.finish(); - } - catch (NotModifiedException nm) { - log.trace("Not modified: {}", httpRequest.getRequestURI()); - } - } - - @Override - public void init(FilterConfig filterConfig) throws ServletException { - } - - @Override - public void destroy() { - } - - - class AccelerationWrapper implements HttpServletResponse, CacheMethodHandle { - - private final HttpServletRequest request; - private final HttpServletResponse response; - - private final AcceleratorServletOutputStream out; - - private ServletOutputStream stream; - private PrintWriter writer; - - private boolean guessing = true; - protected boolean zipped = false; // << CacheControll greift direkt auf diese Variable zu! - - private long now; - private int status; - private int cacheSeconds; - private boolean cacheSecondsSet = false; - private long lastModified, expires = 0l; - private String eTag; - private boolean weak; - private Map cacheParams; - - /** Für den AcceleratorOutputStream */ - private boolean committed = false; - private OutputStream os = null; - private int bufferSize; - private byte[] buffer; - private int pos = 0; - private int size = 0; - - - - AccelerationWrapper(HttpServletRequest request, HttpServletResponse response) throws IOException { - - this.request = request; - this.response = response; - - now = System.currentTimeMillis(); - status = HttpServletResponse.SC_OK; - cacheSeconds = AcceleratorFilter.this.cacheSeconds; - lastModified = AcceleratorFilter.this.lastModified; - eTag = AcceleratorFilter.this.eTag; - weak = AcceleratorFilter.this.weak; - cacheParams = new HashMap(); - - Enumeration values = request.getHeaders(Headers.HEADER_ACCEPT_ENCODING); - while (values.hasMoreElements()) { - String value = (String) values.nextElement(); - if (value.indexOf("gzip") != -1) { - zipped = true; - break; - } - } - - out = new AcceleratorServletOutputStream(); - } - - - private AcceleratorFilter getFilter() { - return AcceleratorFilter.this; - } - - private void finish() throws IOException { - flushBuffer(); - out.close(); - } - - @Override - public void setStatus(int sc) { - response.setStatus(sc); - status = sc; - } - - @Override - public void setStatus(int sc, String sm) { - response.setStatus(sc,sm); - status = sc; - } - - @Override - public void addDateHeader(String name, long value) { - - if (!guessing) { - response.addDateHeader(name, value); - return; - } - - if (Headers.HEADER_DATE.equalsIgnoreCase(name)) { - now = value; - calculateCacheSeconds(); - return; - } - - if (Headers.HEADER_EXPIRES.equalsIgnoreCase(name)) { - expires = value; - calculateCacheSeconds(); - return; - } - - if (Headers.HEADER_LAST_MODIFIED.equalsIgnoreCase(name)) { - lastModified = value; - return; - } - - /** Unknown header: pass throug! */ - response.addDateHeader(name, value); - } - - @Override - public void addHeader(String name, String value) { - - if (!guessing) { - response.addHeader(name, value); - return; - } - - if (value == null) - return; - analyzeHeader(name, value, false); - } - - @Override - public void addIntHeader(String name, int value) { - - if (!guessing) { - response.addIntHeader(name, value); - return; - } - - analyzeHeader(name, Integer.toString(value), false); - } - - @Override - public void setDateHeader(String name, long value) { - - if (!guessing) { - response.setDateHeader(name, value); - return; - } - - if (Headers.HEADER_DATE.equalsIgnoreCase(name)) { - now = value; - calculateCacheSeconds(); - return; - } - - if (Headers.HEADER_EXPIRES.equalsIgnoreCase(name)) { - expires = value; - calculateCacheSeconds(); - return; - } - - if (Headers.HEADER_LAST_MODIFIED.equalsIgnoreCase(name)) { - lastModified = value; - return; - } - - /** Unknown header: pass throug! */ - response.setDateHeader(name, value); - } - - @Override - public void setHeader(String name, String value) { - - if (!guessing) { - response.setHeader(name, value); - return; - } - - analyzeHeader(name, value, true); - } - - @Override - public void setIntHeader(String name, int value) { - - if (!guessing) { - response.setIntHeader(name, value); - return; - } - - analyzeHeader(name, Integer.toString(value), true); - } - - @Override - public ServletOutputStream getOutputStream() throws IOException { - - if (writer != null) - throw new IllegalStateException("ServletOutputStream and PrintWriter cannot be requested both!"); - - if (stream == null) { - stream = out; - } - - return out; - } - - @Override - public PrintWriter getWriter() throws IOException { - - if (stream != null) - throw new IllegalStateException("ServletOutputStream and PrintWriter cannot be requested both!"); - - if (writer == null) { - writer = new PrintWriter(new OutputStreamWriter(out, response.getCharacterEncoding())); - } - - return writer; - } - - @Override - public void setContentLength(int len) { - if (zipped) - log.info("Supressing explicit content-length {} for request {}, because content will be zipped!", len, request.getRequestURI()); - else - response.setContentLength(len); - } - - @Override - public int getBufferSize() { - return bufferSize; - } - - @Override - public void setBufferSize(int size) { - if (this.size > 0) - throw new IllegalStateException("cannot change buffer size, because content was already written!"); - bufferSize = size; - buffer = new byte[size]; - } - - @Override - public void resetBuffer() { - if (committed) - throw new IllegalStateException("cannot reset buffer, because response is already commited!"); - pos = 0; - stream = null; - writer = null; - } - - @Override - public void reset() { - if (committed) - throw new IllegalStateException("cannot reset response, because response is already commited!"); - /** - * Da committed==false gilt, wurde die Dekoration noch nicht angestßen - * und muss entsprechend auch nicht rückgängig gemacht werden! - */ - response.reset(); - pos = 0; - size = 0; - stream = null; - writer = null; - } - - @Override - public void flushBuffer() throws IOException { - if (writer != null) - writer.flush(); - else if (stream != null) - stream.flush(); - } - - @Override - public void addCookie(Cookie cookie) { - // TODO: Je nach Vary-Einstellung ETag anpassen? - response.addCookie(cookie); - } - - @Override - public boolean containsHeader(String name) { - return response.containsHeader(name); - } - - @Override - public String encodeURL(String url) { - return response.encodeURL(url); - } - - @Override - public String encodeRedirectURL(String url) { - return response.encodeRedirectURL(url); - } - - @Override - public String encodeUrl(String url) { - return response.encodeUrl(url); - } - - @Override - public String encodeRedirectUrl(String url) { - return response.encodeRedirectUrl(url); - } - - @Override - public void sendError(int sc, String msg) throws IOException { - response.sendError(sc,msg); - } - - @Override - public void sendError(int sc) throws IOException { - response.sendError(sc); - } - - @Override - public void sendRedirect(String location) throws IOException { - response.sendRedirect(location); - } - - @Override - public String getCharacterEncoding() { - return response.getCharacterEncoding(); - } - - @Override - public String getContentType() { - return response.getContentType(); - } - - @Override - public void setCharacterEncoding(String charset) { - // TODO: Je nach Vary-Einstellung ETag anpassen? - response.setCharacterEncoding(charset); - } - - @Override - public void setContentType(String type) { - // TODO: Je nach Vary-Einstellung ETag anpassen? - response.setContentType(type); - } - - @Override - public boolean isCommitted() { - return committed; - } - - @Override - public void setLocale(Locale loc) { - // TODO: Je nach Vary-Einstellung ETag anpassen? - response.setLocale(loc); - } - - @Override - public Locale getLocale() { - return getLocale(); - } - - - - @Override - public boolean isZipped() { - return zipped; - } - - @Override - public long getTimestamp() { - return now; - } - - @Override - public int accepts(HttpServletRequest request) { - return status; - } - - @Override - public int getCacheSeconds(HttpServletRequest request) { - return cacheSeconds; - } - - @Override - public long getLastModified(HttpServletRequest request) { - return lastModified; - } - - @Override - public String getETag(HttpServletRequest request) { - return eTag; - } - - @Override - public boolean isETagWeak() { - return weak; - } - - @Override - public void cacheControl(HttpServletRequest request, Map cacheControlMap) { - cacheControlMap.putAll(cacheParams); - } - - @Override - public Map getAdditionalHeaders(HttpServletRequest request) { - return EMPTY; - } - - public void supressGuessing() { - guessing = false; - } - - - private void analyzeHeader(String name, String value, boolean overwrite) { - if (name == null) - return; - name = name.trim(); - - if (name.equalsIgnoreCase(Headers.HEADER_DATE)) { - if (value == null) { - if (overwrite) { - now = System.currentTimeMillis(); - cacheSeconds = AcceleratorFilter.this.cacheSeconds; - } - return; - } - try { - SimpleDateFormat parser = new SimpleDateFormat(Headers.RFC_1123_DATE_FORMAT, Locale.US); - now = parser.parse(value).getTime(); - calculateCacheSeconds(); - } - catch (ParseException e) { - log.warn("ignoring date for header \"Date\" in invalid format: {}", value); - } - return; - } - - if (name.equalsIgnoreCase(Headers.HEADER_EXPIRES)) { - if (value == null) { - if (overwrite) { - expires = 0; - cacheSeconds = AcceleratorFilter.this.cacheSeconds; - } - return; - } - try { - SimpleDateFormat parser = new SimpleDateFormat(Headers.RFC_1123_DATE_FORMAT, Locale.US); - expires = parser.parse(value).getTime(); - calculateCacheSeconds(); - } - catch (ParseException e) { - log.warn("ignoring date for header \"Expires\" in invalid format: {}", value); - } - return; - } - - if (name.equalsIgnoreCase(Headers.HEADER_LAST_MODIFIED)) { - if (value == null) { - if (overwrite) - lastModified = AcceleratorFilter.this.lastModified; - return; - } - try { - SimpleDateFormat parser = new SimpleDateFormat(Headers.RFC_1123_DATE_FORMAT, Locale.US); - lastModified = parser.parse(value).getTime(); - } - catch (ParseException e) { - log.warn("ignoring date for header \"Last-Modified\" in invalid format: {}", value); - } - return; - } - - if (name.equalsIgnoreCase(Headers.HEADER_ETAG)) { - if (value == null) { - if (overwrite) { - eTag = AcceleratorFilter.this.eTag; - weak = AcceleratorFilter.this.weak; - } - return; - } - value = value.trim(); - int start = 0; - int end = value.length(); - if (value.startsWith("W/")) { - weak = true; - start = 2; - } - else { - weak = false; - } - if (value.charAt(start) == '"') - start++; - else - log.warn("Quote at the beginning ov ETag is missing: {}", value); - if (value.charAt(end -1) == '"') - end--; - else - log.warn("Quote at the end of ETag is missing: {}", value); - eTag = value.substring(start, end); - String filtered = eTag.replaceAll("[^\\x00-\\x21\\x23-\\x7F]+",""); - if (filtered.length() < eTag.length()) { - log.warn("filtering out illegal characters in ETag: \"{}\" -> \"{}\"", eTag, filtered); - eTag = filtered; - } - } - - if (name.equalsIgnoreCase(Headers.HEADER_CACHE_CONTROL)) { - if (overwrite) - cacheParams.clear(); - if (value == null) - return; - for (String param : value.split(",")) { - param = param.trim(); - int pos = param.indexOf("="); - if (pos < 0) { - cacheParams.put(param, null); - } - else { - String paramName = param.substring(0, pos).trim(); - if (paramName.equalsIgnoreCase("max-age")) { - try { - cacheSeconds = Integer.parseInt(param.substring(pos + 1)); - cacheSecondsSet = true; - } - catch (NumberFormatException e) { - log.warn("illegal value for Header \"Cache-Control\":", param); - } - } - else { - cacheParams.put(paramName, param.substring(pos + 1)); - } - } - } - return; - } - - if (name.equalsIgnoreCase(Headers.HEADER_PRAGMA)) { - if (value != null && value.trim().equalsIgnoreCase("no-cache")) - cacheSeconds = 0; - return; - } - - /** Pass header through, if no value from intrest was found */ - if (overwrite) - response.setHeader(name, value); - else - response.addHeader(name, value); - } - - private void calculateCacheSeconds() { - if (!cacheSecondsSet && expires >= now) { - cacheSeconds = (int)(expires/1000 - now/1000); - log.debug("calculating cache-seconds from DATE and EXPIRES: {}", cacheSeconds); - } - } - - - private class AcceleratorServletOutputStream extends ServletOutputStream { - - private final ServletOutputStream sos; - - - private AcceleratorServletOutputStream() throws IOException { - bufferSize = defaultBufferSize; - buffer = new byte[bufferSize]; - sos = AccelerationWrapper.this.response.getOutputStream(); - } - - - private OutputStream out() throws IOException { - if (os == null) - os = zipped ? new GZIPOutputStream(sos) : sos; - return os; - } - - @Override - public void write(int i) throws IOException { - if (pos == bufferSize) { - out().write(buffer); - committed = true; - /** Dekoration nur beim ersten Schreib-Schub anstoßen */ - if (pos == size) { - if (!cacheControl.decorate(request, response)) { - zipped = false; - os = null; - pos = 0; - throw new NotModifiedException(); - } - } - pos = 0; - } - buffer[pos++] = (byte) i; - size++; - } - - @Override - public void flush() throws IOException { - if (pos == 0) - return; - - committed = true; - /** Dekoration nur beim ersten Schreib-Schub anstoßen */ - if (pos == size) { - if (!cacheControl.decorate(request, response)) { - zipped = false; - os = null; - pos = 0; - throw new NotModifiedException(); - } - } - out().write(buffer, 0, pos); - out().flush(); - pos = 0; - } - - @Override - public void close() throws IOException { - if (size == 0) { - committed = true; - zipped = false; - if (!cacheControl.decorate(request, response)) - throw new NotModifiedException(); - sos.close(); - } - else { - flush(); - out().close(); - } - } - } - } -} - -class NotModifiedException extends IOException {} \ No newline at end of file