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