Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WebFilter is not able to modify response when running with quarkus:dev #8546

Closed
38leinaD opened this issue Apr 13, 2020 · 0 comments · Fixed by #8618
Closed

WebFilter is not able to modify response when running with quarkus:dev #8546

38leinaD opened this issue Apr 13, 2020 · 0 comments · Fixed by #8618
Labels
kind/bug Something isn't working
Milestone

Comments

@38leinaD
Copy link
Contributor

Describe the bug
I have written a @webfilter (using HttpServletResponseWrapper) that wants to modify/rewrite a response (see code at end of issue description). The implementation works fine when quarkus is running "normally"; it is not working when running via "quarkus:dev".
It is not possible to modify the the contentLength, contentType or body. The browser receives the original headers but no body; so, just waits to load the body endlessly. It seems the response is already commited/sent to the client.

Expected behavior
The Filter should be able to modify the headers and body.
E.g. for index.html return body "hello world" and content-type "text/plain".

Actual behavior
Headers sent to the client are the ones of the orginal response as set by
filterChain.doFilter(request, wrappedResp);
It see the response is already commited and has started to be sent to the client.

To Reproduce
Steps to reproduce the behavior:

  1. Set up a simple quarkus project containing a dependency to quarkus-undertow
  2. Add the attached code.
  3. Run with mvn compile quarkus:dev
  4. Try to open localhost:8080/index.html

Instead of showing the index.html, "hello world" should be printed as text/plain.

Configuration

# empty

Screenshots
image

Environment (please complete the following information):

  • Output of uname -a or ver: Linux hedgehog 4.9.0-12-amd64 Switch to the Maven distributed copy of the SubstrateVM annotations #1 SMP Debian 4.9.210-1 (2020-01-20) x86_64 GNU/Linux
  • Output of java -version: openjdk version "13" 2019-09-17
    OpenJDK Runtime Environment (build 13+33)
    OpenJDK 64-Bit Server VM (build 13+33, mixed mode, sharing)
  • GraalVM version (if different from Java):
  • Quarkus version or git rev: 1.3.2.Final
  • Build tool (ie. output of mvnw --version or gradlew --version): Apache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f)
    Maven home: /home/daniel/.m2/wrapper/dists/apache-maven-3.6.3-bin/1iopthnavndlasol9gbrbg6bf2/apache-maven-3.6.3
    Java version: 13, vendor: Oracle Corporation, runtime: /home/daniel/.sdkman/candidates/java/13.0.0-open
    Default locale: en_US, platform encoding: UTF-8
    OS name: "linux", version: "4.9.0-12-amd64", arch: "amd64", family: "unix"

Additional context
The Filter implementation:

package org.acme;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
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.WriteListener;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

import io.undertow.servlet.spec.HttpServletRequestImpl;

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);
        }
    }
}

@WebFilter("/*")
public class WebResourceFilter implements Filter {
    public void init(FilterConfig filterConfig) throws ServletException {
    }

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

        System.out.println("Filtering " + request);

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

            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")) {
                    System.out.println("-- committed? " + response.isCommitted());

 
                    String msg = "hello world";
                    response.setContentType("text/plain");
                    response.setContentLength(msg.length());
                    response.getWriter().write(msg);
                }
                else {
                    System.out.println("-- Just return original resonse");
                    response.setContentType(wrappedResp.getContentType());
                    response.setContentLength(respBody.length());
                    response.getWriter().append(respBody);
                }
                return;
            }
        }
        filterChain.doFilter(request, response);
    }
    
    public void destroy() {
    }
}
@38leinaD 38leinaD added the kind/bug Something isn't working label Apr 13, 2020
stuartwdouglas added a commit to stuartwdouglas/quarkus that referenced this issue Apr 16, 2020
@gsmet gsmet added this to the 1.4.0.Final milestone Apr 20, 2020
gsmet pushed a commit to gsmet/quarkus that referenced this issue Apr 20, 2020
gsmet pushed a commit to gsmet/quarkus that referenced this issue May 18, 2020
gsmet pushed a commit to gsmet/quarkus that referenced this issue May 18, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind/bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants