geronimo-scm mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From xuhaih...@apache.org
Subject svn commit: r817571 - /geronimo/server/trunk/plugins/console/console-filter/src/main/java/org/apache/geronimo/console/filter/
Date Tue, 22 Sep 2009 09:27:36 GMT
Author: xuhaihong
Date: Tue Sep 22 09:27:35 2009
New Revision: 817571

URL: http://svn.apache.org/viewvc?rev=817571&view=rev
Log:
GERONIMO-4874 Improve the console filter performance (Patch from Jack Cai)

Added:
    geronimo/server/trunk/plugins/console/console-filter/src/main/java/org/apache/geronimo/console/filter/SubstitutePrintWriter.java
  (with props)
    geronimo/server/trunk/plugins/console/console-filter/src/main/java/org/apache/geronimo/console/filter/SubstituteResponseOutputStream.java
  (with props)
    geronimo/server/trunk/plugins/console/console-filter/src/main/java/org/apache/geronimo/console/filter/SubstituteResponseWrapper.java
  (with props)
    geronimo/server/trunk/plugins/console/console-filter/src/main/java/org/apache/geronimo/console/filter/SubstituteUtil.java
  (with props)
    geronimo/server/trunk/plugins/console/console-filter/src/main/java/org/apache/geronimo/console/filter/SubstituteWriter.java
  (with props)
Modified:
    geronimo/server/trunk/plugins/console/console-filter/src/main/java/org/apache/geronimo/console/filter/XSRFHandler.java
    geronimo/server/trunk/plugins/console/console-filter/src/main/java/org/apache/geronimo/console/filter/XSSXSRFFilter.java

Added: geronimo/server/trunk/plugins/console/console-filter/src/main/java/org/apache/geronimo/console/filter/SubstitutePrintWriter.java
URL: http://svn.apache.org/viewvc/geronimo/server/trunk/plugins/console/console-filter/src/main/java/org/apache/geronimo/console/filter/SubstitutePrintWriter.java?rev=817571&view=auto
==============================================================================
--- geronimo/server/trunk/plugins/console/console-filter/src/main/java/org/apache/geronimo/console/filter/SubstitutePrintWriter.java
(added)
+++ geronimo/server/trunk/plugins/console/console-filter/src/main/java/org/apache/geronimo/console/filter/SubstitutePrintWriter.java
Tue Sep 22 09:27:35 2009
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.geronimo.console.filter;
+
+import java.io.PrintWriter;
+
+/**
+ * A printer writer that will replace certain keyword (</body>) with a given
+ * string in the output.
+ * @version $Rev$, $Date$
+ */
+public class SubstitutePrintWriter extends PrintWriter {
+
+    private SubstituteWriter writer;
+
+    SubstitutePrintWriter(SubstituteWriter writer) {
+        super(writer);
+        this.writer = writer;
+    }
+
+    public void reset() {
+        writer.reset();
+    }
+}

Propchange: geronimo/server/trunk/plugins/console/console-filter/src/main/java/org/apache/geronimo/console/filter/SubstitutePrintWriter.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/server/trunk/plugins/console/console-filter/src/main/java/org/apache/geronimo/console/filter/SubstitutePrintWriter.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/server/trunk/plugins/console/console-filter/src/main/java/org/apache/geronimo/console/filter/SubstitutePrintWriter.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: geronimo/server/trunk/plugins/console/console-filter/src/main/java/org/apache/geronimo/console/filter/SubstituteResponseOutputStream.java
URL: http://svn.apache.org/viewvc/geronimo/server/trunk/plugins/console/console-filter/src/main/java/org/apache/geronimo/console/filter/SubstituteResponseOutputStream.java?rev=817571&view=auto
==============================================================================
--- geronimo/server/trunk/plugins/console/console-filter/src/main/java/org/apache/geronimo/console/filter/SubstituteResponseOutputStream.java
(added)
+++ geronimo/server/trunk/plugins/console/console-filter/src/main/java/org/apache/geronimo/console/filter/SubstituteResponseOutputStream.java
Tue Sep 22 09:27:35 2009
@@ -0,0 +1,270 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.geronimo.console.filter;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.CoderResult;
+import java.nio.charset.CodingErrorAction;
+import java.nio.charset.MalformedInputException;
+
+import javax.servlet.ServletOutputStream;
+
+/**
+ * A output stream that will replace certain keyword (</body>) with a
+ * given string in the output.
+ * @version $Rev$, $Date$
+ */
+public class SubstituteResponseOutputStream extends ServletOutputStream {
+
+    private static final int BUFFER_SIZE = 4086;
+
+    private static final int PROCESS_THRESHOLD = 128;
+
+    // Console use UTF-8 encoding. In UTF-8, max length for one character is 4.
+    private static final int MALFORMED_INPUT_MAX_LENGTH = 4;
+
+    private String substitute = null;
+
+    private String outputCharset = null;
+
+    private OutputStream stream = null;
+
+    private CharsetDecoder cd = null;
+
+    private ByteBuffer bb = ByteBuffer.allocate(BUFFER_SIZE);
+
+    private CharBuffer cb = CharBuffer.allocate(BUFFER_SIZE);
+
+    private boolean found = false;
+
+    /**
+     * Construct a output stream which will scan the input and do substitution
+     * for the keyword "</body>".
+     * 
+     * @param substitute
+     *            The text that will replace "</body>"
+     * @param outputCharset
+     *            The charset that will be used to encode the output
+     * @param os
+     *            The output stream
+     */
+    public SubstituteResponseOutputStream(String substitute, String outputCharset, OutputStream
os) {
+        if (substitute == null || outputCharset == null || os == null) {
+            throw new NullPointerException();
+        }
+        this.substitute = substitute;
+        this.outputCharset = outputCharset;
+        this.stream = os;
+        Charset cs = Charset.forName(outputCharset);
+        cd = cs.newDecoder().onMalformedInput(CodingErrorAction.REPORT).onUnmappableCharacter(CodingErrorAction.REPLACE);
+    }
+
+    /*
+     * Decode the bytes in the byte buffer and append to the char buffer.
+     * 
+     * The byte & char buffer should be ready for "put" before entering this
+     * method. They are still ready for "put" after exiting this method.
+     * 
+     * @param flushBuffer Whether to flush out all the contents in the byte
+     * buffer
+     * 
+     * @throws IOException
+     */
+    private void decodeBuffer(boolean flushBuffer) throws IOException {
+        if (!flushBuffer && bb.position() < PROCESS_THRESHOLD) {
+            return;
+        }
+        bb.flip();
+        cd.reset();
+        if (bb.hasRemaining()) {
+            CoderResult cr = cd.decode(bb, cb, true);
+            cd.flush(cb);
+            if (cr.isMalformed()) {
+                // Move the tail bytes to the head
+                int tailLength = bb.remaining();
+                if (tailLength >= MALFORMED_INPUT_MAX_LENGTH) {
+                    // We only expect the bytes of one character to be broken
+                    throw new MalformedInputException(tailLength);
+                }
+            }
+            bb.compact();
+        } else {
+            bb.clear();
+        }
+    }
+
+    /*
+     * Process the bytes in the buffer. This involves: 1. Decode the bytes and
+     * put into the char buffer; 2. Scan the characters in the buffer and do
+     * replacement.
+     * 
+     * The byte & char buffer should be ready for "put" before entering this
+     * method. They are still ready for "put" after exiting this method.
+     * 
+     * @param flushBuffer Whether to flush out all the contents in the buffer
+     * @param endOfInput Whether this is the end of input
+     * 
+     * @throws IOException
+     */
+    private void processBuffer(boolean flushBuffer, boolean endOfInput) throws IOException
{
+        decodeBuffer(flushBuffer || endOfInput);
+        if (!endOfInput && !flushBuffer && cb.position() < PROCESS_THRESHOLD)
{
+            return;
+        }
+        cb.flip();
+        found = SubstituteUtil.processSubstitute(cb, substitute, endOfInput, outputCharset,
stream);
+        // Write the tail bytes in the byte buffer if required
+        if (bb.position() > 0 && (found || endOfInput)) {
+            bb.flip();
+            while (bb.hasRemaining()) {
+                stream.write(bb.get());
+            }
+            bb.clear();
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.io.OutputStream#write(int)
+     */
+    @Override
+    public void write(int b) throws IOException {
+        if (found) {
+            stream.write(b);
+        } else {
+            bb.put((byte) b);
+            processBuffer(false, false);
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.io.OutputStream#write(int)
+     */
+    @Override
+    public void write(byte[] content, int offset, int length) throws IOException {
+        if (found) {
+            stream.write(content, offset, length);
+            return;
+        }
+        int boundary = offset + length;
+        int batchOffset = offset;
+        int batchLength;
+        while (batchOffset < boundary) {
+            if (found) {
+                stream.write(content, batchOffset, boundary - batchOffset);
+                break;
+            } else {
+                if (boundary - batchOffset < bb.remaining()) {
+                    batchLength = boundary - batchOffset;
+                } else {
+                    batchLength = bb.remaining();
+                }
+                bb.put(content, batchOffset, batchLength);
+                processBuffer(false, false);
+                batchOffset = batchOffset + batchLength;
+            }
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.io.OutputStream#print(String)
+     */
+    @Override
+    public void print(String s) throws IOException {
+        if (found) {
+            super.print(s);
+            return;
+        }
+        if (bb.position() > 0) {
+            // Process the buffered bytes
+            decodeBuffer(true);
+            if (bb.position() > 0) {
+                // There are still malformed input remaining, even though we
+                // are going to write a string. This should not happen.
+                throw new MalformedInputException(bb.position());
+            }
+        }
+        // Process the string without encoding/decoding overhead
+        char[] content = s.toCharArray();
+        int offset = 0;
+        int length;
+        while (offset < content.length) {
+            if (found) {
+                super.print(s.substring(offset));
+                break;
+            } else {
+                if (content.length - offset < cb.remaining()) {
+                    length = content.length - offset;
+                } else {
+                    length = cb.remaining();
+                }
+                cb.put(content, offset, length);
+                processBuffer(false, false);
+                offset = offset + length;
+            }
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.io.OutputStream#close()
+     */
+    @Override
+    public void close() throws IOException {
+        if (!found) {
+            processBuffer(true, true);
+        }
+        stream.flush();
+        stream.close();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.io.OutputStream#flush()
+     */
+    @Override
+    public void flush() throws IOException {
+        if (!found) {
+            // Try to write out as much as possible, but some text might be
+            // still buffered in order to handle broken keyword
+            processBuffer(true, false);
+        }
+        stream.flush();
+    }
+
+    /**
+     * Reset this output stream. Clear the buffers.
+     */
+    public void reset() {
+        bb.clear();
+        cb.clear();
+        found = false;
+    }
+}

Propchange: geronimo/server/trunk/plugins/console/console-filter/src/main/java/org/apache/geronimo/console/filter/SubstituteResponseOutputStream.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/server/trunk/plugins/console/console-filter/src/main/java/org/apache/geronimo/console/filter/SubstituteResponseOutputStream.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/server/trunk/plugins/console/console-filter/src/main/java/org/apache/geronimo/console/filter/SubstituteResponseOutputStream.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: geronimo/server/trunk/plugins/console/console-filter/src/main/java/org/apache/geronimo/console/filter/SubstituteResponseWrapper.java
URL: http://svn.apache.org/viewvc/geronimo/server/trunk/plugins/console/console-filter/src/main/java/org/apache/geronimo/console/filter/SubstituteResponseWrapper.java?rev=817571&view=auto
==============================================================================
--- geronimo/server/trunk/plugins/console/console-filter/src/main/java/org/apache/geronimo/console/filter/SubstituteResponseWrapper.java
(added)
+++ geronimo/server/trunk/plugins/console/console-filter/src/main/java/org/apache/geronimo/console/filter/SubstituteResponseWrapper.java
Tue Sep 22 09:27:35 2009
@@ -0,0 +1,130 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.geronimo.console.filter;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletResponseWrapper;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A response wrapper that will replace certain keyword (</body>) with a given
+ * string in the output.
+ * @version $Rev$, $Date$
+ */
+public class SubstituteResponseWrapper extends HttpServletResponseWrapper {
+    private static final Logger log = LoggerFactory.getLogger(SubstituteResponseWrapper.class);
+
+    private SubstituteResponseOutputStream stream = null;
+    private SubstitutePrintWriter writer = null;
+    private String substitute = null;
+
+    public SubstituteResponseWrapper(HttpServletResponse response,
+            String substitute) {
+        super(response);
+        this.substitute = substitute;
+    }
+
+    private boolean substituteRequired() {
+        String cType = getContentType();
+        if (cType != null) {
+            // only update the content if it is HTML
+            if (cType.toLowerCase().indexOf("html") != -1) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public void flushBuffer() throws IOException {
+        if (substituteRequired()) {
+            if (writer != null) {
+                writer.flush();
+            } else if (stream != null) {
+                stream.flush();
+            }
+        } else {
+            super.flushBuffer();
+        }
+    }
+
+    @Override
+    public ServletOutputStream getOutputStream() throws IOException {
+        log.debug("Getting a stream...");
+        if (!substituteRequired()) {
+            return super.getOutputStream();
+        } else if (writer != null) {
+            throw new IllegalStateException(
+                    "getWriter() has already been called on this response.");
+        } else if (stream == null) {
+            stream = new SubstituteResponseOutputStream(substitute,
+                    getCharacterEncoding(), super.getOutputStream());
+        }
+        return stream;
+    }
+
+    @Override
+    public PrintWriter getWriter() throws IOException {
+        log.debug("Getting a writer...");
+        if (!substituteRequired()) {
+            return super.getWriter();
+        } else if (stream != null) {
+            throw new IllegalStateException(
+                    "getStream() has already been called on this response.");
+        } else if (writer == null) {
+            writer = new SubstitutePrintWriter(new SubstituteWriter(
+                    substitute, getCharacterEncoding(), super
+                            .getOutputStream()));
+        }
+        return writer;
+
+    }
+
+    @Override
+    public void reset() {
+        log.debug("Resetting...");
+        super.reset();
+        // If no exception from the wrapped response, let's reset too
+        if (substituteRequired()) {
+            if (stream != null) {
+                stream.reset();
+            } else if (writer != null) {
+                writer.reset();
+            }
+        }
+    }
+
+    @Override
+    public void resetBuffer() {
+        log.debug("Resetting buffer...");
+        super.resetBuffer();
+        if (substituteRequired()) {
+            // If no exception from the wrapped response, let's reset too
+            if (stream != null) {
+                stream.reset();
+            } else if (writer != null) {
+                writer.reset();
+            }
+        }
+    }
+}

Propchange: geronimo/server/trunk/plugins/console/console-filter/src/main/java/org/apache/geronimo/console/filter/SubstituteResponseWrapper.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/server/trunk/plugins/console/console-filter/src/main/java/org/apache/geronimo/console/filter/SubstituteResponseWrapper.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/server/trunk/plugins/console/console-filter/src/main/java/org/apache/geronimo/console/filter/SubstituteResponseWrapper.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: geronimo/server/trunk/plugins/console/console-filter/src/main/java/org/apache/geronimo/console/filter/SubstituteUtil.java
URL: http://svn.apache.org/viewvc/geronimo/server/trunk/plugins/console/console-filter/src/main/java/org/apache/geronimo/console/filter/SubstituteUtil.java?rev=817571&view=auto
==============================================================================
--- geronimo/server/trunk/plugins/console/console-filter/src/main/java/org/apache/geronimo/console/filter/SubstituteUtil.java
(added)
+++ geronimo/server/trunk/plugins/console/console-filter/src/main/java/org/apache/geronimo/console/filter/SubstituteUtil.java
Tue Sep 22 09:27:35 2009
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.geronimo.console.filter;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.CharBuffer;
+import java.util.regex.Pattern;
+
+/**
+ * @version $Rev$, $Date$
+ */
+public class SubstituteUtil {
+
+    private final static String KEYWORD = "</body>";
+
+    private final static Pattern SEARCH_PATTERN = Pattern.compile("(?i)" + KEYWORD);
+
+    /**
+     * Scan the characters in the buffer and do replacement.
+     * 
+     * The char buffer should be ready for "get" before entering this method,
+     * and will change to be ready for "put" after exiting this method.
+     * 
+     * @param cb
+     *            The char buffer to be scanned
+     * @param replacement
+     *            The replacement string that will be used to replace found
+     *            occurrences of the keyword
+     * @param endOfInput
+     *            Whether this is the last batch to scan
+     * @param outputCharset
+     *            The charset to encode the output
+     * @param os
+     *            The output stream to receive the output
+     * @return true if the keyword is found, otherwise false
+     * @throws IOException
+     */
+    public static boolean processSubstitute(CharBuffer cb, String replacement, boolean endOfInput,
String outputCharset, OutputStream os) throws IOException {
+        int remaining = cb.remaining();
+        if (remaining == 0) {
+            cb.clear();
+            return false;
+        }
+        String result = SEARCH_PATTERN.matcher(cb).replaceFirst(replacement);
+        cb.clear();
+        // For simplicity, we assume that the length of the replacement is
+        // different from the keyword, which is definitely true in Geronimo
+        // admin console's case
+        if (result.length() != remaining) {
+            // Replacement happened, we've got a match
+            os.write(result.getBytes(outputCharset));
+            return true;
+        } else if (endOfInput) {
+            // End of input, write everything out
+            os.write(result.getBytes(outputCharset));
+            return false;
+        } else {
+            // Push back the last N chars so that we don't break a keyword
+            char tail = result.charAt(result.length() - 1);
+            if (tail != ' ' && tail != '\n' && tail != '\r') {
+                int textTailLength = Math.min(result.length(), KEYWORD.length() - 1);
+                int textTailOffset = result.length() - textTailLength;
+                cb.put(result.substring(textTailOffset));
+                if (textTailOffset > 0) {
+                    os.write(result.substring(0, textTailOffset).getBytes(outputCharset));
+                }
+            } else {
+                os.write(result.getBytes(outputCharset));
+            }
+            return false;
+        }
+    }
+}

Propchange: geronimo/server/trunk/plugins/console/console-filter/src/main/java/org/apache/geronimo/console/filter/SubstituteUtil.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/server/trunk/plugins/console/console-filter/src/main/java/org/apache/geronimo/console/filter/SubstituteUtil.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/server/trunk/plugins/console/console-filter/src/main/java/org/apache/geronimo/console/filter/SubstituteUtil.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: geronimo/server/trunk/plugins/console/console-filter/src/main/java/org/apache/geronimo/console/filter/SubstituteWriter.java
URL: http://svn.apache.org/viewvc/geronimo/server/trunk/plugins/console/console-filter/src/main/java/org/apache/geronimo/console/filter/SubstituteWriter.java?rev=817571&view=auto
==============================================================================
--- geronimo/server/trunk/plugins/console/console-filter/src/main/java/org/apache/geronimo/console/filter/SubstituteWriter.java
(added)
+++ geronimo/server/trunk/plugins/console/console-filter/src/main/java/org/apache/geronimo/console/filter/SubstituteWriter.java
Tue Sep 22 09:27:35 2009
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.geronimo.console.filter;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Writer;
+import java.nio.CharBuffer;
+
+/**
+ * A writer that will replace certain keyword (&lt;/body&gt;) with a given string
in
+ * the output.
+ * @version $Rev$, $Date$
+ */
+public class SubstituteWriter extends Writer {
+
+    // Default internal buffer size
+    private static final int BUFFER_SIZE = 4086;
+
+    // Threshold for processing buffered content
+    private static final int PROCESS_THRESHOLD = 128;
+
+    private String substitute = null;
+
+    private String outputCharset = null;
+
+    private OutputStream stream = null;
+
+    private CharBuffer cb = CharBuffer.allocate(BUFFER_SIZE);
+
+    private boolean found = false;
+
+    public SubstituteWriter(String substitute, String outputCharset, OutputStream os) {
+        if (substitute == null || outputCharset == null || os == null) {
+            throw new NullPointerException();
+        }
+        this.substitute = substitute;
+        this.outputCharset = outputCharset;
+        this.stream = os;
+    }
+
+    @Override
+    public void close() throws IOException {
+        synchronized (lock) {
+            if (!found) {
+                cb.flip();
+                found = SubstituteUtil.processSubstitute(cb, substitute, true, outputCharset,
stream);
+            }
+            stream.close();
+        }
+    }
+
+    @Override
+    public void flush() throws IOException {
+        synchronized (lock) {
+            if (!found) {
+                // Try to write out as much as possible, but some text might be
+                // still buffered in order to handle broken keyword
+                cb.flip();
+                found = SubstituteUtil.processSubstitute(cb, substitute, false, outputCharset,
stream);
+            }
+            stream.flush();
+        }
+    }
+
+    @Override
+    public void write(char[] content, int offset, int length) throws IOException {
+        synchronized (lock) {
+            // Skip replacement if already found one occurrence
+            if (found) {
+                String s = new String(content, offset, length);
+                stream.write(s.getBytes(outputCharset));
+                return;
+            }
+            // Process the content in batches
+            int boundary = offset + length;
+            int batchOffset = offset;
+            int batchLength;
+            while (batchOffset < boundary) {
+                if (found) {
+                    stream.write(new String(content, batchOffset, boundary - batchOffset).getBytes(outputCharset));
+                    break;
+                } else {
+                    if (boundary - batchOffset < cb.remaining()) {
+                        batchLength = boundary - batchOffset;
+                    } else {
+                        batchLength = cb.remaining();
+                    }
+                    cb.put(content, batchOffset, batchLength);
+                    if (cb.position() > PROCESS_THRESHOLD) {
+                        cb.flip();
+                        found = SubstituteUtil.processSubstitute(cb, substitute, false, outputCharset,
stream);
+                    }
+                    batchOffset = batchOffset + batchLength;
+                }
+            }
+        }
+    }
+
+    public void reset() {
+        synchronized (lock) {
+            cb.clear();
+            found = false;
+        }
+    }
+}

Propchange: geronimo/server/trunk/plugins/console/console-filter/src/main/java/org/apache/geronimo/console/filter/SubstituteWriter.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/server/trunk/plugins/console/console-filter/src/main/java/org/apache/geronimo/console/filter/SubstituteWriter.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/server/trunk/plugins/console/console-filter/src/main/java/org/apache/geronimo/console/filter/SubstituteWriter.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: geronimo/server/trunk/plugins/console/console-filter/src/main/java/org/apache/geronimo/console/filter/XSRFHandler.java
URL: http://svn.apache.org/viewvc/geronimo/server/trunk/plugins/console/console-filter/src/main/java/org/apache/geronimo/console/filter/XSRFHandler.java?rev=817571&r1=817570&r2=817571&view=diff
==============================================================================
--- geronimo/server/trunk/plugins/console/console-filter/src/main/java/org/apache/geronimo/console/filter/XSRFHandler.java
(original)
+++ geronimo/server/trunk/plugins/console/console-filter/src/main/java/org/apache/geronimo/console/filter/XSRFHandler.java
Tue Sep 22 09:27:35 2009
@@ -25,7 +25,6 @@
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Random;
-import java.util.regex.Pattern;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpSession;
@@ -49,8 +48,6 @@
     private static final String XSRF_UNIQUEID = "formId";
     private static final String XSRF_JS_FILENAME = "/XSRF.js";
     private final static String XSRF_JS_UNIQUEID = "<%XSRF_UNIQUEID%>";
-    private final static String SEARCH_PATTERN = "(?i)</body>";
-    private static final Pattern regexPattern = Pattern.compile(SEARCH_PATTERN);
 
     private Map<String, String> sessionMap = Collections.synchronizedMap(new HashMap<String,
String>());
     private String xsrfJS;
@@ -192,53 +189,26 @@
     }
 
     //----- Response handler routines -----
-
     /**
-     * Main response handler, which appends our XSRF JavaScript with the
-     * unique session token to any HTML response content that includes a
-     * form tag.
+     * Get XSRF JavaScript containing the unique session token.
+     * 
      * @param hreq
-     * @param hres
      */
-    public void updateResponse(HttpServletRequest hreq, FilterResponseWrapper hres) throws
IOException {
+    public String getReplacement(HttpServletRequest hreq) throws IOException {
         // get the JavaScript file we're going to append to it
-        String updatedXsrfJS;
         String uniqueId = getSession(hreq);
         if (xsrfJS == null) {
             log.error("No JavaScript to append to the response!");
-        }
-        else if (uniqueId == null) {
-            // this should only happen for user logout or session timeout, so ignore
+            return null;
+        } else if (uniqueId == null) {
+            // this should only happen for user logout or session timeout, so
+            // ignore
             log.debug("HttpSession is null!");
+            return null;
+        } else {
+            // update the JavaScript with the uniqueId for this session
+            return xsrfJS.replace(XSRF_JS_UNIQUEID, uniqueId);
         }
-        else {
-            String cType = hres.getContentType();
-            if (cType != null) {
-                // only update the content if it is HTML
-                if (cType.toLowerCase().indexOf("html") != -1) {
-                    // get the response content
-                    String content = new String(hres.getOutput(), "UTF-8");
-                    // update the JavaScript with the uniqueId for this session
-                    updatedXsrfJS = xsrfJS.replace(XSRF_JS_UNIQUEID, uniqueId);
-                    // update the response to contain the JS fragment
-                    content = regexPattern.matcher(content).replaceAll(updatedXsrfJS);
-                    log.info("Updated HTML content with XSRF JavaScript for requestURI="
+ hreq.getRequestURI());
-                    //log.debug("Updated content =" + content);
-                    // update the ResponseOutputStream content
-                    hres.setOutput(content);                                    
-                }
-                else {
-                    // we don't want to try updating non-HTML content with our JavaScript
-                    log.debug("Not updating requestURI=" + hreq.getRequestURI() + " due to
ContentType = " + cType);
-                }
-            }
-            else {
-                // no ContentType provided, so ignore this content
-                log.debug("Not updating requestURI=" +  hreq.getRequestURI() + " due to NO
ContentType");
-            }
-        }
-        // write out our updated HttpServletResponse
-        hres.writeOutput();
     }
 
     /**

Modified: geronimo/server/trunk/plugins/console/console-filter/src/main/java/org/apache/geronimo/console/filter/XSSXSRFFilter.java
URL: http://svn.apache.org/viewvc/geronimo/server/trunk/plugins/console/console-filter/src/main/java/org/apache/geronimo/console/filter/XSSXSRFFilter.java?rev=817571&r1=817570&r2=817571&view=diff
==============================================================================
--- geronimo/server/trunk/plugins/console/console-filter/src/main/java/org/apache/geronimo/console/filter/XSSXSRFFilter.java
(original)
+++ geronimo/server/trunk/plugins/console/console-filter/src/main/java/org/apache/geronimo/console/filter/XSSXSRFFilter.java
Tue Sep 22 09:27:35 2009
@@ -120,14 +120,14 @@
             }
             //-----------------------------------------------
             // Call other filters and eventually the Servlet
+            // Update the response with our XSRF FORM protection code
             //-----------------------------------------------
-            FilterResponseWrapper whres = new FilterResponseWrapper((HttpServletResponse)response);
+            String replacement = xsrf.getReplacement(hreq);
+            ServletResponse whres = response;
+            if (replacement != null ) {
+                whres = new SubstituteResponseWrapper((HttpServletResponse)response, replacement);
+            }
             chain.doFilter(hreq, whres);
-
-            //-------------------------------------------------------------------
-            // Update and commit the response with our XSRF FORM protection code
-            //-------------------------------------------------------------------
-            xsrf.updateResponse(hreq, whres);
         }
         else {
             log.debug("Request not HttpServletRequest and/or Response not HttpServletResponse");



Mime
View raw message