From: Kai Moritz Date: Sun, 25 Dec 2011 12:16:07 +0000 (+0100) Subject: GZIPServletOutputStream puffert, um Ausgabefehler zu verhindern X-Git-Url: http://juplo.de/gitweb/?a=commitdiff_plain;h=439e01c5f2e30bf7f271e3b8c0d6a2ca80ed12f4;p=percentcodec GZIPServletOutputStream puffert, um Ausgabefehler zu verhindern TODO: Eventuell reicht es auch, den Buffer gezielt zu leeren, anstatt hier stets eine neue Instanz zu erzeugen! Noch zu klären: sind die im folgenden vermerkten Fehler inzwischen korrigiert, so dass die Anmerkungen jetzt nach dem Zusammenlegen der Commits gelöscht werden können?!? ---------- Buggy Commit! Die in CountingServletOutputStream eingeführte Methode finish(), die wahrscheinlich von der finish()-Methode des Wrappers aus aufgerufen werden sollte, wird nur von der Unterklasse GZipServletOutputstream aufgerufen. Der im Wrapper entfernte Aufruf der Dekoration nach der Ausführung des Filters wird also durch diese Maßnahme gar nicht ersetzt! BUGGY: gzip-Header wird nicht mehr unterdrückt, wenn noch Ausgabe folgen könnte Die zur Sicherstellung der Kompression eingeführte Variable forceCompression wird nie ausgewertet. Eventuell ein Fehler, der erst nachträglich durch das Rebasen beim aufräumen produziert wurde. Wenn die Variable forceCompression korrekt in den Scope private gesetzt wäre, wäre der Fehler früher aufgefallen, da Netbeans ihn dann angemeckert hätte... --- diff --git a/cachecontrol/src/main/java/de/halbekunst/juplo/cachecontrol/AcceleratorFilter.java b/cachecontrol/src/main/java/de/halbekunst/juplo/cachecontrol/AcceleratorFilter.java index 9125554c..5fee7367 100644 --- a/cachecontrol/src/main/java/de/halbekunst/juplo/cachecontrol/AcceleratorFilter.java +++ b/cachecontrol/src/main/java/de/halbekunst/juplo/cachecontrol/AcceleratorFilter.java @@ -1,6 +1,8 @@ package de.halbekunst.juplo.cachecontrol; +import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.OutputStream; import java.io.PrintWriter; import java.text.ParseException; import java.text.SimpleDateFormat; @@ -71,8 +73,7 @@ public class AcceleratorFilter implements Filter { httpRequest.setAttribute(RESPONSE_WRAPPER, wrapper); cacheControl.init(wrapper); chain.doFilter(request, wrapper); - /** Dekoration auslösen, falls sie bisher nicht ausgelöst wurde... */ - cacheControl.decorate(httpRequest, httpResponse, wrapper); + wrapper.finish(); } @Override @@ -90,6 +91,7 @@ public class AcceleratorFilter implements Filter { private final HttpServletResponse response; boolean zipped; // CacheControll greift direkt auf dieses Flag zu! + boolean forceCompression = false; private CountingServletOutputStream out; private ServletOutputStream stream; @@ -133,7 +135,11 @@ public class AcceleratorFilter implements Filter { if (zipped) out = new GZIPServletOutputStream(); else - out = new WrappedServletOutputStream(); + out = new CountingServletOutputStream(); + } + + public void finish() throws IOException { + out.close(); } @@ -309,6 +315,7 @@ public class AcceleratorFilter implements Filter { @Override public void flushBuffer() throws IOException { + forceCompression = true; cacheControl.decorate(request, response, this); response.flushBuffer(); } @@ -317,6 +324,15 @@ public class AcceleratorFilter implements Filter { public void resetBuffer() { response.resetBuffer(); + try { + if (zipped) + out = new GZIPServletOutputStream(); + else + out = new CountingServletOutputStream(); + } + catch (IOException e) { + throw new IllegalStateException(e); + } stream = null; writer = null; } @@ -329,7 +345,7 @@ public class AcceleratorFilter implements Filter { if (zipped) out = new GZIPServletOutputStream(); else - out = new WrappedServletOutputStream(); + out = new CountingServletOutputStream(); } catch (IOException e) { throw new IllegalStateException(e); @@ -338,6 +354,7 @@ public class AcceleratorFilter implements Filter { writer = null; /** Cookies has been cleared! Reinitialize decorator... */ + forceCompression = false; cacheControl.init(this); } @@ -537,50 +554,31 @@ public class AcceleratorFilter implements Filter { } - abstract class CountingServletOutputStream extends ServletOutputStream { - - abstract void setBuffer(int size) throws IllegalStateException; - abstract boolean isZipped(); - } + class CountingServletOutputStream extends ServletOutputStream { + private OutputStream out; + int left; + boolean empty; - final class GZIPServletOutputStream extends CountingServletOutputStream { - - final static int MINMAL_BUFFER_SIZE = 128; - - private final GZIPOutputStream out; - private int buffer, left; - private boolean empty; - - - public GZIPServletOutputStream() throws IOException { - this.out = new GZIPOutputStream(response.getOutputStream()); - empty = true; - setBuffer(AcceleratorFilter.this.buffer); + CountingServletOutputStream() throws IOException { + out = response.getOutputStream(); left = buffer; + empty = true; } - @Override + void setBuffer(int size) throws IllegalStateException {} + boolean isZipped() { - return !empty; + return false; } - @Override - void setBuffer(int size) { - if (!empty) - throw new IllegalStateException("attemp to change buffer size after writing data to response!"); - - if (size > MINMAL_BUFFER_SIZE) { - buffer = size; - left = buffer; - } + void finish() throws IOException { + decorate(); } - - @Override - public void close() throws IOException { + void decorate() throws IOException { try { AcceleratorFilter.this.cacheControl.decorate(AccelerationWrapper.this.request, response, AccelerationWrapper.this); } @@ -588,79 +586,113 @@ public class AcceleratorFilter implements Filter { log.error("Error while guessing Cache-Header's", e); response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } + } + + @Override + public void close() throws IOException { + decorate(); out.close(); } @Override public void flush() throws IOException { - try { - AcceleratorFilter.this.cacheControl.decorate(AccelerationWrapper.this.request, response, AccelerationWrapper.this); - } - catch (Exception e) { - log.error("Error while guessing Cache-Header's", e); - response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); - } - + decorate(); out.flush(); } @Override public void write(int i) throws IOException { - if (left == 0) { - try { - AcceleratorFilter.this.cacheControl.decorate(AccelerationWrapper.this.request, response, AccelerationWrapper.this); - } - catch (Exception e) { - log.error("Error while guessing Cache-Header's", e); - response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); - } - } empty = false; + if (left == 0) + decorate(); left--; out.write(i); } } - final class WrappedServletOutputStream extends CountingServletOutputStream { + final class GZIPServletOutputStream extends CountingServletOutputStream { - private final ServletOutputStream out; - private boolean empty; + final static int MINIMAL_BUFFER_SIZE = 128; + private ByteArrayOutputStream buffer; + private OutputStream out; + private GZIPOutputStream zout; + private int bufferSize; + private boolean decorated = false; - public WrappedServletOutputStream() throws IOException { - this.out = response.getOutputStream(); - empty = true; + + public GZIPServletOutputStream() throws IOException { + setBuffer(AcceleratorFilter.this.buffer); } + @Override + void finish() throws IOException { + decorate(); + zout.finish(); + super.out.write(buffer.toByteArray()); + } + @Override boolean isZipped() { - return false; + if (decorated) + return true; + return !empty; } @Override - void setBuffer(int size) { + void setBuffer(int size) throws IllegalStateException { if (!empty) throw new IllegalStateException("attemp to change buffer size after writing data to response!"); + + if (size > MINIMAL_BUFFER_SIZE) { + left = size; + try { + bufferSize = size; + out = response.getOutputStream(); + buffer = new ByteArrayOutputStream(size); + zout = new GZIPOutputStream(buffer, size); + super.out = zout; + } + catch (IOException e) { + throw new IllegalStateException(e); + } + } } + @Override public void close() throws IOException { + decorate(); + zout.close(); + out.write(buffer.toByteArray()); out.close(); } @Override public void flush() throws IOException { + decorate(); + out.write(buffer.toByteArray()); out.flush(); } @Override public void write(int i) throws IOException { empty = false; - out.write(i); + if (left == 0) { + if (!decorated) { + decorate(); + decorated = true; + } + out.write(buffer.toByteArray()); + buffer.reset(); + left = bufferSize; + } + left--; + zout.write(i); } } }