Catch-all сервлет-фильтр, который должен захватывать ВСЕ HTML-контент для манипуляций, работает только периодически

Мне нужен фильтр сервлетов, который будет захватывать все входные данные, затем обрабатывать их, вставляя специальный токен в каждую форму. Представьте, что фильтр привязан ко всем запросам (например,url-pattern=*). У меня есть код для захвата контента, но это не похоже наRequestWrapper достаточно надежен, чтобы захватить весь ввод. Некоторые входные данные возвращают ноль байтов, и тогда я не могу «передать» этот контент обратно пользователю. Например, мы все еще используем Struts 1.3.10, и любой код Struts не «правильно» захватывает, мы получаем нулевой байтовый контент. Я полагаю, что это из-за того, как Struts работает вперед. Если в запросе участвует форвард, мне интересно, сработает ли приведенный ниже код захвата. Вот весь код, есть ли у вас подход, который будет захватывать любой тип контента, предназначенный для потоковой передачи пользователю.

<filter>
   <filter-name>Filter</filter-name>
   <filter-class>mybrokenCaptureHtml.TokenFilter</filter-class>
</filter> 
<filter-mapping>
    <filter-name>Filter</filter-name>
    <url-pattern>/*</url-pattern>
 </filter-mapping>
package mybrokenCaptureHtml;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintWriter;

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.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

public class TokenFilter implements Filter {    
    @Override
    public void destroy() {
    }

    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;               
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        try {                                                                                       
            final MyResponseWrapper responseWrapper = new MyResponseWrapper((HttpServletResponse) response);
            chain.doFilter(request, responseWrapper);                       

            // **HERE DEPENDING ON THE SERVLET OR APPLICATION CODE (STRUTS, WICKET), the response returns an empty string //
            // Especiall struts, is there something in their forwards that would cause an error?
            final byte [] bytes = responseWrapper.toByteArray(); 
                    // For some applications that hit this filter
                    // ZERO BYTE DATA is returned, this is bad, but SOME
                    // CODE, the data is captured.
            final String origHtml = new String(bytes);

            final String newHtml = origHtml.replaceAll("(?i)</(\\s)*form(\\s)*>", "<input type=\"hidden\" name=\"zval\" value=\"fromSiteZ123\"/></form>");          
            response.getOutputStream().write(newHtml.getBytes());

        } catch(final Exception e) {            
            e.printStackTrace();
        }
        return;
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {        
    }

    static class MyResponseWrapper extends HttpServletResponseWrapper {    
        private final MyPrintWriter pw = new MyPrintWriter();               
        public byte [] toByteArray() {            
            return pw.toByteArray();        
        }
        public MyResponseWrapper(HttpServletResponse response) {
            super(response);       
        }

        @Override
        public PrintWriter getWriter() {
            return pw.getWriter();
        }
        @Override
        public ServletOutputStream getOutputStream() {
            return pw.getStream();
        }       
        private static class MyPrintWriter {
            private ByteArrayOutputStream baos = new ByteArrayOutputStream();
            private PrintWriter pw = new PrintWriter(baos);
            private ServletOutputStream sos = new MyServletStream(baos);
            public PrintWriter getWriter() {
                return pw;
            }
            public ServletOutputStream getStream() {
                return sos;
            }
            byte[] toByteArray() {
                return baos.toByteArray();
            }
        }    
        private static class MyServletStream extends ServletOutputStream {
            ByteArrayOutputStream baos;
            MyServletStream(final ByteArrayOutputStream baos) {
                this.baos = baos;
            }
            @Override
            public void write(final int param) throws IOException {
                baos.write(param);
            }
        }
    }

}

Вот как может выглядеть пример приложения Struts, для некоторых приложений (не для Struts) мы можем захватывать контент. Но для приложений, подобных приведенному ниже, нулевой байт возвращается для содержимого HTML, но он должен быть.

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/c.tld" prefix="c" %>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>
<%@ taglib uri="/WEB-INF/struts-tiles.tld" prefix="tiles" %>
<%@ taglib uri="/WEB-INF/struts-nested.tld" prefix="nested"%>
<html:html>
<head>
<title><bean:message key="myApp.customization.title" /></title>
<LINK rel="stylesheet" type="text/css" href="../theme/styles.css">
</head>
<body>
<html:form styleId="customizemyAppForm" method="post" action="/customizemyApp.do?step=submit">
<html:submit onclick="javascript:finish(this.form);" styleClass="input_small">&nbsp;&nbsp;<bean:message key="myApp.customization.submit" />&nbsp;</html:submit> 
<input type="button" styleClass="input_small" width="80" style="WIDTH:80px" name="<bean:message key="myApp.customization.cancel" />" value="<bean:message key="myApp.customization.cancel" />" onclick="javascript:cancel();">

</html:form>
</body>
</html:html>

Я подозреваю, чтоMyResponseWrapper а такжеMyPrintWriter недостаточно надежны для захвата всех типов контента.

Пример сервлета, который будет работать (a):

response.getOutputStream().write(str.getBytes());

Пример сервлета, который не будет работать (b):

response.getWriter().println("<html>data</html>");

примерa получил бы захват, примерb не буду.

Вот улучшенный класс-обертка, большинство приложений будут работать, но СЕЙЧАС некоторые из приложений Struts, только НЕКОТОРЫЙ ответ отправляется в браузер.

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

public class ByteArrayResponseWrapper extends HttpServletResponseWrapper {
    private PrintWriter output = null;
    private ServletOutputStream outStream = null;
    private static final String NL = System.getProperty("line.separator");

    public ByteArrayResponseWrapper(final HttpServletResponse response) {
        super(response);
    }

    public String getDocument() {        
        InputStream in = null;
        try {            
            in = this.getInputStream();            
            if (in != null) {             
                return getDocument(in);
            }           
        } catch(final Exception ee) {
            // ee.print;StackTrace();
        } finally {                  
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    //e.prin;tStackTrace();
                }
            }
        }
        return "";    
    }

    protected String getDocument(final InputStream in) {
        final StringBuffer buf = new StringBuffer();
        BufferedReader br = null;
        try {
            String line = "";
            br = new BufferedReader(new InputStreamReader(getInputStream(), this.getCharacterEncoding()));            
            while ((line = br.readLine()) != null) {
                buf.append(line).append(NL);                
            }
        } catch(final IOException e) {
            //e.print;StackTrace();
        } finally {
            try {
                if (br != null) {
                    br.close();
                }
            } catch (IOException ex) {             
            }
        }
        return buf.toString();
    }

    @Override
    public PrintWriter getWriter() throws IOException {
        if (output == null) {
            output = new PrintWriter(new OutputStreamWriter(getOutputStream(), this.getCharacterEncoding()));
        }
        return output;
    }

    @Override
    public ServletOutputStream getOutputStream() throws IOException {
        if (outStream == null) {
            outStream = new BufferingServletOutputStream();
        }
        return outStream;
    }

    public InputStream getInputStream() throws IOException {
        final BufferingServletOutputStream out = (BufferingServletOutputStream) getOutputStream();        
        return new ByteArrayInputStream(out.getBuffer().toByteArray());
    }

    /**
     * Implementation of ServletOutputStream that handles the in-memory
     * buffering of the response content
     */
    public static class BufferingServletOutputStream extends ServletOutputStream {
        ByteArrayOutputStream out = null;

        public BufferingServletOutputStream() {
            this.out = new ByteArrayOutputStream();
        }

        public ByteArrayOutputStream getBuffer() {
            return out;
        }

        public void write(int b) throws IOException {
            out.write(b);
        }

        public void write(byte[] b) throws IOException {
            out.write(b);
        }

        public void write(byte[] b, int off, int len) throws IOException {
            out.write(b, off, len);
        }
        @Override
        public void close() throws IOException {
            out.close();
            super.close();
        }
        @Override
        public void flush() throws IOException {
            out.flush();
            super.flush();
        }
    }
}

Я нашел возможное решение, вgetInputStream метод, похоже, если я вызову «закрыть» на всех объектах, например,outStream.flush() а такжеoutStream.close() а потомout.flush() а такжеout.close()... похоже, что последние байты записаны правильно. это не интуитивно понятно, но похоже, что это работает.

Ответы на вопрос(1)

Ваш ответ на вопрос