4

I am trying to write a servlet filter in Quarkus that modifies the response body. Basically, I have followed the steps on following article to make sure I am not missing anything: https://medium.com/@sportans300/fiddling-with-httpresponses-in-java-2a269cd5a474

So, filter method looks like this:

public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
        throws IOException, ServletException {

    if (request instanceof HttpServletRequestImpl) {
        String url = ((HttpServletRequestImpl) request).getRequestURL().toString();
        HttpServletResponse httpResponse = (HttpServletResponse) response;

        if (url.endsWith("/index.html")) {

            ServletResponseWrapper wrappedResp = new ServletResponseWrapper((HttpServletResponse) response);

            filterChain.doFilter(request, wrappedResp);

            String respBody = wrappedResp.toString();

            if (wrappedResp.getContentType().contains("text/html")) {
                String msg = "<html><head></head><body>hello world</body></html>";
                response.setContentLength(msg.length());
                response.getWriter().write(msg);
            }
            else {
                System.out.println("-- Just return original resonse");
                response.getWriter().append(respBody);
            }
            return;
        }
    }

    filterChain.doFilter(request, response);
}

And the ResponseWrapper like this:

public class ServletResponseWrapper extends HttpServletResponseWrapper {

    private final ByteArrayOutputStream capture;
    private ServletOutputStream output;
    private PrintWriter writer;

    public ServletResponseWrapper(HttpServletResponse response) throws IOException {
        super(response);
        capture = new ByteArrayOutputStream(response.getBufferSize());
    }

    @Override
    public ServletOutputStream getOutputStream() {
        if (writer != null) {
            throw new IllegalStateException("getWriter() has already been called on this response.");
        }

        if (output == null) {
            output = new ServletOutputStream() {
                @Override
                public void write(int b) throws IOException {
                    capture.write(b);
                }

                @Override
                public void write(byte b[]) throws IOException {
                    capture.write(b);
                }

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

                @Override
                public void flush() throws IOException {
                    capture.flush();
                }

                @Override
                public void close() throws IOException {
                    capture.close();
                }

                @Override
                public boolean isReady() {
                    return false;
                }

                @Override
                public void setWriteListener(WriteListener arg0) {
                }
            };
        }

        return output;
    }

    @Override
    public PrintWriter getWriter() throws IOException {
        if (output != null) {
            throw new IllegalStateException("getOutputStream() has already been called on this response.");
        }

        if (writer == null) {
            writer = new PrintWriter(new OutputStreamWriter(capture, getCharacterEncoding()));
        }

        return writer;
    }

    public void close() throws IOException {
        if (writer != null) {
            writer.close();
        }
        if (output != null) {
            output.close();
        }
    }

    @Override
    public void flushBuffer() throws IOException {
        System.out.println("-- flush buffer");
        if (writer != null) {
            writer.flush();
        } else if (output != null) {
            output.flush();
        }
    }

    public byte[] getResponseData() throws IOException {
        if (writer != null) {
            writer.close();
        } else if (output != null) {
            output.close();
        }
        return capture.toByteArray();
    }

    @Override
    public String toString() {
        try {
            return new String(getResponseData());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

What is happening is that its seems like the response is already committed and I am not able to change the contentLength and the body anymore. Undertow seems to have already started to send the headers during filterChain.doFilter(request, wrappedResp). The browser receives Content-Length set to the length of the original response and a body is not received at all (page keeps loading).

UPDATE: I tested the exact same webfilter on a Wildfly 19 and OpenLiberty; the filter is working as expected. So, the issue is specific to Quarkus it seems.

UPDATE 2: Seems to be an issue in quarkus:dev only. I have opened up an issue: https://github.com/quarkusio/quarkus/issues/8546

ilja
  • 351
  • 2
  • 14
38leinad
  • 196
  • 2
  • 11

0 Answers0